summaryrefslogtreecommitdiffstats
path: root/tmate-session.c
blob: 02469c5912fc0098f9920123d1c67ba67e19c61a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#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_info("Looking up %s...", TMATE_HOST);
	(void)evdns_getaddrinfo(ev_dnsbase, TMATE_HOST, NULL,
				&hints, dns_cb, NULL);
}

void tmate_session_init(void)
{
	tmate_catch_sigsegv();

	tmate_encoder_init(&tmate_session.encoder);
	tmate_decoder_init(&tmate_session.decoder);

	TAILQ_INIT(&tmate_session.clients);

	tmate_session.need_passphrase = 0;
	tmate_session.passphrase = NULL;

	/* The header will be written as soon as the first client connects */
	tmate_write_header();
}

void tmate_session_start(void)
{
	/* We split init and start because:
	 * - We need to process the tmux config file during the connection as
	 *   we are setting up the tmate identity.
	 * - While we are parsing the config file, we need to be able to
	 *   serialize it, and so we need a worker encoder.
	 */
	lookup_and_connect();
}