mirror of
https://github.com/tmate-io/tmate.git
synced 2025-02-02 19:39:32 +01:00
Support for multiple IP on DNS
This commit is contained in:
parent
2e3661a0f6
commit
36bfa71b78
@ -182,7 +182,7 @@ dist_tmate_SOURCES = \
|
||||
tmate-encoder.c \
|
||||
tmate-decoder.c \
|
||||
tmate-msg.c \
|
||||
tmate.c \
|
||||
tmate-session.c \
|
||||
tmux.c \
|
||||
tty-acs.c \
|
||||
tty-keys.c \
|
||||
|
@ -1,7 +1,7 @@
|
||||
# $Id$
|
||||
|
||||
# Miscellaneous autofoo bullshit.
|
||||
AC_INIT(tmate, 1.8.4)
|
||||
AC_INIT(tmate, 1.8.6)
|
||||
|
||||
AC_CONFIG_AUX_DIR(etc)
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
|
2
server.c
2
server.c
@ -187,7 +187,7 @@ server_start(int lockfd, char *lockfile)
|
||||
}
|
||||
}
|
||||
|
||||
tmate_client_start();
|
||||
tmate_session_start();
|
||||
|
||||
cmdq_continue(cfg_cmd_q);
|
||||
|
||||
|
@ -73,3 +73,17 @@ void tmate_print_trace(void)
|
||||
|
||||
free (strings);
|
||||
}
|
||||
|
||||
|
||||
static void handle_sigsegv(int sig)
|
||||
{
|
||||
/* TODO send stack trace to server */
|
||||
tmate_info("CRASH, printing stack trace");
|
||||
tmate_print_trace();
|
||||
tmate_fatal("CRASHED");
|
||||
}
|
||||
|
||||
void tmate_catch_sigsegv(void)
|
||||
{
|
||||
signal(SIGSEGV, handle_sigsegv);
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "tmate.h"
|
||||
|
||||
#define DEFAULT_ENCODER (&tmate_session.encoder)
|
||||
|
||||
static int msgpack_write(void *data, const char *buf, unsigned int len)
|
||||
{
|
||||
struct tmate_encoder *encoder = data;
|
||||
@ -26,13 +28,14 @@ void tmate_encoder_init(struct tmate_encoder *encoder)
|
||||
msgpack_pack_raw_body(pk, str, __strlen); \
|
||||
} while(0)
|
||||
|
||||
#define pack(what, ...) msgpack_pack_##what(&tmate_encoder->pk, __VA_ARGS__)
|
||||
#define pack(what, ...) msgpack_pack_##what(&DEFAULT_ENCODER->pk, __VA_ARGS__)
|
||||
|
||||
void tmate_write_header(void)
|
||||
{
|
||||
pack(array, 2);
|
||||
pack(array, 3);
|
||||
pack(int, TMATE_HEADER);
|
||||
pack(int, TMATE_PROTOCOL_VERSION);
|
||||
pack(string, VERSION);
|
||||
}
|
||||
|
||||
void tmate_sync_layout(void)
|
||||
|
107
tmate-session.c
Normal file
107
tmate-session.c
Normal file
@ -0,0 +1,107 @@
|
||||
#include <event2/dns.h>
|
||||
#include <event2/util.h>
|
||||
#include <event2/event.h>
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "tmate.h"
|
||||
|
||||
#define TMATE_DNS_RETRY_TIMEOUT 10
|
||||
|
||||
struct tmate_session tmate_session;
|
||||
|
||||
static struct evdns_base *ev_dnsbase;
|
||||
static struct event ev_dns_retry;
|
||||
static void lookup_and_connect(void);
|
||||
|
||||
static void on_dns_retry(evutil_socket_t fd, short what, void *arg)
|
||||
{
|
||||
lookup_and_connect();
|
||||
}
|
||||
|
||||
static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr)
|
||||
{
|
||||
struct tmate_ssh_client *client;
|
||||
struct evutil_addrinfo *ai;
|
||||
struct timeval tv;
|
||||
|
||||
if (errcode) {
|
||||
tmate_status_message("%s lookup failure. Retrying in %d seconds (%s)",
|
||||
TMATE_HOST, TMATE_DNS_RETRY_TIMEOUT,
|
||||
evutil_gai_strerror(errcode));
|
||||
|
||||
tv.tv_sec = TMATE_DNS_RETRY_TIMEOUT;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
evtimer_assign(&ev_dns_retry, ev_base, on_dns_retry, NULL);
|
||||
evtimer_add(&ev_dns_retry, &tv);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
tmate_status_message("Connecting to %s...", TMATE_HOST);
|
||||
|
||||
for (ai = addr; ai; ai = ai->ai_next) {
|
||||
char buf[128];
|
||||
const char *ip = NULL;
|
||||
if (ai->ai_family == AF_INET) {
|
||||
struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
|
||||
ip = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128);
|
||||
} else if (ai->ai_family == AF_INET6) {
|
||||
struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
|
||||
ip = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128);
|
||||
}
|
||||
|
||||
tmate_debug("Trying server %s", ip);
|
||||
|
||||
/*
|
||||
* Note: We don't deal with the client list. Clients manage it
|
||||
* and free client structs when necessary.
|
||||
*/
|
||||
(void)tmate_ssh_client_alloc(&tmate_session, ip);
|
||||
}
|
||||
|
||||
evutil_freeaddrinfo(addr);
|
||||
|
||||
evdns_base_free(ev_dnsbase, 0);
|
||||
ev_dnsbase = NULL;
|
||||
}
|
||||
|
||||
static void lookup_and_connect(void)
|
||||
{
|
||||
struct evutil_addrinfo hints;
|
||||
|
||||
if (!ev_dnsbase)
|
||||
ev_dnsbase = evdns_base_new(ev_base, 1);
|
||||
if (!ev_dnsbase)
|
||||
tmate_fatal("Cannot initialize the DNS lookup service");
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_flags = 0;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
tmate_status_message("Looking up %s...", TMATE_HOST);
|
||||
(void)evdns_getaddrinfo(ev_dnsbase, TMATE_HOST, NULL,
|
||||
&hints, dns_cb, NULL);
|
||||
}
|
||||
|
||||
void tmate_session_start(void)
|
||||
{
|
||||
tmate_catch_sigsegv();
|
||||
|
||||
TAILQ_INIT(&tmate_session.clients);
|
||||
tmate_encoder_init(&tmate_session.encoder);
|
||||
tmate_decoder_init(&tmate_session.decoder);
|
||||
|
||||
lookup_and_connect();
|
||||
|
||||
/* The header will be written as soon as the first client connects */
|
||||
tmate_write_header();
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/callbacks.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <stdio.h>
|
||||
#include <event.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "tmate.h"
|
||||
|
||||
@ -19,14 +18,10 @@ static void printflike2 reconnect_session(struct tmate_ssh_client *client,
|
||||
static void log_function(ssh_session session, int priority,
|
||||
const char *message, void *userdata)
|
||||
{
|
||||
tmate_debug("[%d] %s", priority, message);
|
||||
struct tmate_ssh_client *client = userdata;
|
||||
tmate_debug("[%s] [%d] %s", client->server_ip, priority, message);
|
||||
}
|
||||
|
||||
static struct ssh_callbacks_struct ssh_session_callbacks = {
|
||||
.log_function = log_function
|
||||
};
|
||||
|
||||
|
||||
static void register_session_fd_event(struct tmate_ssh_client *client)
|
||||
{
|
||||
if (!event_initialized(&client->ev_ssh)) {
|
||||
@ -42,20 +37,24 @@ static void register_session_fd_event(struct tmate_ssh_client *client)
|
||||
|
||||
static void register_input_stream_event(struct tmate_ssh_client *client)
|
||||
{
|
||||
if (!event_initialized(&client->encoder->ev_readable)) {
|
||||
event_assign(&client->encoder->ev_readable, ev_base, -1,
|
||||
struct tmate_encoder *encoder = &client->tmate_session->encoder;
|
||||
|
||||
if (!event_initialized(&encoder->ev_readable)) {
|
||||
event_assign(&encoder->ev_readable, ev_base, -1,
|
||||
EV_READ | EV_PERSIST, __flush_input_stream, client);
|
||||
event_add(&client->encoder->ev_readable, NULL);
|
||||
event_add(&encoder->ev_readable, NULL);
|
||||
client->has_encoder = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void consume_channel(struct tmate_ssh_client *client)
|
||||
{
|
||||
struct tmate_decoder *decoder = &client->tmate_session->decoder;
|
||||
char *buf;
|
||||
ssize_t len;
|
||||
|
||||
for (;;) {
|
||||
tmate_decoder_get_buffer(client->decoder, &buf, &len);
|
||||
tmate_decoder_get_buffer(decoder, &buf, &len);
|
||||
len = ssh_channel_read_nonblocking(client->channel, buf, len, 0);
|
||||
if (len < 0) {
|
||||
reconnect_session(client, "Error reading from channel: %s",
|
||||
@ -66,7 +65,21 @@ static void consume_channel(struct tmate_ssh_client *client)
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
tmate_decoder_commit(client->decoder, len);
|
||||
tmate_decoder_commit(decoder, len);
|
||||
}
|
||||
}
|
||||
|
||||
static void connection_complete(struct tmate_ssh_client *connected_client)
|
||||
{
|
||||
struct tmate_session *session = connected_client->tmate_session;
|
||||
struct tmate_ssh_client *client, *tmp_client;
|
||||
|
||||
TAILQ_FOREACH_SAFE(client, &session->clients, node, tmp_client) {
|
||||
if (client == connected_client)
|
||||
continue;
|
||||
|
||||
assert(!client->has_encoder);
|
||||
tmate_ssh_client_free(client);
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +106,7 @@ static void on_session_event(struct tmate_ssh_client *client)
|
||||
return;
|
||||
}
|
||||
|
||||
ssh_set_callbacks(session, &ssh_session_callbacks);
|
||||
ssh_set_callbacks(session, &client->ssh_callbacks);
|
||||
|
||||
client->channel = channel = ssh_channel_new(session);
|
||||
if (!channel) {
|
||||
@ -102,13 +115,12 @@ static void on_session_event(struct tmate_ssh_client *client)
|
||||
}
|
||||
|
||||
ssh_set_blocking(session, 0);
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, TMATE_HOST);
|
||||
ssh_options_set(session, SSH_OPTIONS_HOST, client->server_ip);
|
||||
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||
ssh_options_set(session, SSH_OPTIONS_PORT, &port);
|
||||
ssh_options_set(session, SSH_OPTIONS_USER, "tmate");
|
||||
ssh_options_set(session, SSH_OPTIONS_COMPRESSION, "yes");
|
||||
|
||||
tmate_status_message("Connecting to %s...", TMATE_HOST);
|
||||
client->state = SSH_CONNECT;
|
||||
/* fall through */
|
||||
|
||||
@ -123,14 +135,14 @@ static void on_session_event(struct tmate_ssh_client *client)
|
||||
return;
|
||||
case SSH_OK:
|
||||
register_session_fd_event(client);
|
||||
tmate_debug("Connected");
|
||||
tmate_debug("Establishing connection to %s", client->server_ip);
|
||||
client->state = SSH_AUTH_SERVER;
|
||||
/* fall through */
|
||||
}
|
||||
|
||||
case SSH_AUTH_SERVER:
|
||||
if ((hash_len = ssh_get_pubkey_hash(session, &hash)) < 0) {
|
||||
disconnect_session(client, "Cannnot authenticate server");
|
||||
disconnect_session(client, "Cannot authenticate server");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -165,11 +177,20 @@ static void on_session_event(struct tmate_ssh_client *client)
|
||||
free(hash_str);
|
||||
|
||||
if (!match) {
|
||||
disconnect_session(client, "Cannnot authenticate server");
|
||||
disconnect_session(client, "Cannot authenticate server");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* At this point, we abort other connection attempts to the
|
||||
* other tmate servers, since we have reached the fastest one.
|
||||
* We need to do it before we ask the user its passphrase,
|
||||
* otherwise the speed test would be biased.
|
||||
*/
|
||||
tmate_debug("Connected to %s", client->server_ip);
|
||||
connection_complete(client);
|
||||
client->state = SSH_AUTH_CLIENT;
|
||||
|
||||
/* fall through */
|
||||
|
||||
case SSH_AUTH_CLIENT:
|
||||
@ -179,7 +200,8 @@ static void on_session_event(struct tmate_ssh_client *client)
|
||||
case SSH_AUTH_PARTIAL:
|
||||
case SSH_AUTH_INFO:
|
||||
case SSH_AUTH_DENIED:
|
||||
disconnect_session(client, "Access denied. Check your SSH keys.");
|
||||
disconnect_session(client, "Access denied. Check your SSH keys "
|
||||
"(passphrases are not supported yet).");
|
||||
return;
|
||||
case SSH_AUTH_ERROR:
|
||||
reconnect_session(client, "Auth error: %s",
|
||||
@ -236,7 +258,8 @@ static void on_session_event(struct tmate_ssh_client *client)
|
||||
|
||||
static void flush_input_stream(struct tmate_ssh_client *client)
|
||||
{
|
||||
struct evbuffer *evb = client->encoder->buffer;
|
||||
struct tmate_encoder *encoder = &client->tmate_session->encoder;
|
||||
struct evbuffer *evb = encoder->buffer;
|
||||
ssize_t len, written;
|
||||
char *buf;
|
||||
|
||||
@ -274,16 +297,23 @@ static void __on_session_event(evutil_socket_t fd, short what, void *arg)
|
||||
static void __disconnect_session(struct tmate_ssh_client *client,
|
||||
const char *fmt, va_list va)
|
||||
{
|
||||
__tmate_status_message(fmt, va);
|
||||
struct tmate_encoder *encoder;
|
||||
|
||||
if (fmt)
|
||||
__tmate_status_message(fmt, va);
|
||||
else
|
||||
tmate_debug("Disconnecting %s", client->server_ip);
|
||||
|
||||
if (event_initialized(&client->ev_ssh)) {
|
||||
event_del(&client->ev_ssh);
|
||||
client->ev_ssh.ev_flags = 0;
|
||||
}
|
||||
|
||||
if (event_initialized(&client->encoder->ev_readable)) {
|
||||
event_del(&client->encoder->ev_readable);
|
||||
client->encoder->ev_readable.ev_flags = 0;
|
||||
if (client->has_encoder) {
|
||||
encoder = &client->tmate_session->encoder;
|
||||
event_del(&encoder->ev_readable);
|
||||
encoder->ev_readable.ev_flags = 0;
|
||||
client->has_encoder = 0;
|
||||
}
|
||||
|
||||
if (client->session) {
|
||||
@ -337,21 +367,38 @@ static void printflike2 reconnect_session(struct tmate_ssh_client *client,
|
||||
#endif
|
||||
}
|
||||
|
||||
void tmate_ssh_client_init(struct tmate_ssh_client *client,
|
||||
struct tmate_encoder *encoder,
|
||||
struct tmate_decoder *decoder)
|
||||
{
|
||||
ssh_callbacks_init(&ssh_session_callbacks);
|
||||
|
||||
struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session,
|
||||
const char *server_ip)
|
||||
{
|
||||
struct tmate_ssh_client *client;
|
||||
client = xmalloc(sizeof(*client));
|
||||
|
||||
ssh_callbacks_init(&client->ssh_callbacks);
|
||||
client->ssh_callbacks.log_function = log_function;
|
||||
client->ssh_callbacks.userdata = client;
|
||||
|
||||
client->tmate_session = session;
|
||||
TAILQ_INSERT_TAIL(&session->clients, client, node);
|
||||
|
||||
client->server_ip = xstrdup(server_ip);
|
||||
client->state = SSH_NONE;
|
||||
client->session = NULL;
|
||||
client->channel = NULL;
|
||||
|
||||
client->encoder = encoder;
|
||||
client->decoder = decoder;
|
||||
client->has_encoder = 0;
|
||||
|
||||
evtimer_assign(&client->ev_ssh_reconnect, ev_base,
|
||||
on_reconnect_timer, client);
|
||||
|
||||
connect_session(client);
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void tmate_ssh_client_free(struct tmate_ssh_client *client)
|
||||
{
|
||||
disconnect_session(client, NULL);
|
||||
TAILQ_REMOVE(&client->tmate_session->clients, client, node);
|
||||
free(client->server_ip);
|
||||
free(client);
|
||||
}
|
||||
|
18
tmate.c
18
tmate.c
@ -1,18 +0,0 @@
|
||||
#include "tmate.h"
|
||||
|
||||
struct tmate_encoder *tmate_encoder;
|
||||
|
||||
static struct tmate_ssh_client client;
|
||||
static struct tmate_encoder encoder;
|
||||
static struct tmate_decoder decoder;
|
||||
|
||||
void tmate_client_start(void)
|
||||
{
|
||||
tmate_encoder_init(&encoder);
|
||||
tmate_decoder_init(&decoder);
|
||||
tmate_encoder = &encoder;
|
||||
|
||||
tmate_ssh_client_init(&client, &encoder, &decoder);
|
||||
|
||||
tmate_write_header();
|
||||
}
|
57
tmate.h
57
tmate.h
@ -4,6 +4,7 @@
|
||||
#include <sys/types.h>
|
||||
#include <msgpack.h>
|
||||
#include <libssh/libssh.h>
|
||||
#include <libssh/callbacks.h>
|
||||
#include <event.h>
|
||||
|
||||
#include "tmux.h"
|
||||
@ -17,7 +18,7 @@
|
||||
|
||||
#define TMATE_MAX_MESSAGE_SIZE (16*1024)
|
||||
|
||||
#define TMATE_PROTOCOL_VERSION 2
|
||||
#define TMATE_PROTOCOL_VERSION 3
|
||||
|
||||
enum tmate_commands {
|
||||
TMATE_HEADER,
|
||||
@ -72,7 +73,7 @@ extern void tmate_decoder_commit(struct tmate_decoder *decoder, size_t len);
|
||||
/* tmate-ssh-client.c */
|
||||
|
||||
#ifdef DEVENV
|
||||
#define TMATE_HOST "127.0.0.1"
|
||||
#define TMATE_HOST "localhost"
|
||||
#define TMATE_PORT 2200
|
||||
#else
|
||||
#define TMATE_HOST "master.tmate.io"
|
||||
@ -94,28 +95,60 @@ enum tmate_ssh_client_state_types {
|
||||
};
|
||||
|
||||
struct tmate_ssh_client {
|
||||
/* XXX The "session" word is used for three things:
|
||||
* - the ssh session
|
||||
* - the tmate sesssion
|
||||
* - the tmux session
|
||||
* A tmux session is associated 1:1 with a tmate session.
|
||||
* An ssh session belongs to a tmate session, and a tmate session
|
||||
* has one ssh session, except during bootstrapping where
|
||||
* there is one ssh session per tmate server, and the first one wins.
|
||||
*/
|
||||
struct tmate_session *tmate_session;
|
||||
TAILQ_ENTRY(tmate_ssh_client) node;
|
||||
|
||||
char *server_ip;
|
||||
|
||||
int has_encoder;
|
||||
int state;
|
||||
|
||||
/*
|
||||
* ssh_callbacks is allocated because the libssh API sucks (userdata
|
||||
* has to be in the struct itself).
|
||||
*/
|
||||
struct ssh_callbacks_struct ssh_callbacks;
|
||||
ssh_session session;
|
||||
ssh_channel channel;
|
||||
|
||||
struct tmate_encoder *encoder;
|
||||
struct tmate_decoder *decoder;
|
||||
|
||||
struct event ev_ssh;
|
||||
struct event ev_ssh_reconnect;
|
||||
};
|
||||
TAILQ_HEAD(tmate_ssh_clients, tmate_ssh_client);
|
||||
|
||||
extern void tmate_ssh_client_init(struct tmate_ssh_client *client,
|
||||
struct tmate_encoder *encoder,
|
||||
struct tmate_decoder *decoder);
|
||||
extern struct tmate_ssh_client *tmate_ssh_client_alloc(struct tmate_session *session,
|
||||
const char *server_ip);
|
||||
extern void tmate_ssh_client_free(struct tmate_ssh_client *client);
|
||||
|
||||
/* tmate.c */
|
||||
/* tmate-session.c */
|
||||
|
||||
extern struct tmate_encoder *tmate_encoder;
|
||||
extern void tmate_client_start(void);
|
||||
struct tmate_session {
|
||||
struct tmate_encoder encoder;
|
||||
struct tmate_decoder decoder;
|
||||
|
||||
/*
|
||||
* This list contains one connection per IP. The first connected
|
||||
* client wins, and saved in *client. When we have a winner, the
|
||||
* losers are disconnected and killed.
|
||||
*/
|
||||
struct tmate_ssh_clients clients;
|
||||
};
|
||||
|
||||
extern struct tmate_session tmate_session;
|
||||
extern void tmate_session_start(void);
|
||||
|
||||
/* tmate-debug.c */
|
||||
extern void tmate_print_trace (void);
|
||||
extern void tmate_print_trace(void);
|
||||
extern void tmate_catch_sigsegv(void);
|
||||
|
||||
/* tmate-msg.c */
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user