tmate-session.c 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. #include <event2/dns.h>
  2. #include <event2/util.h>
  3. #include <event2/event.h>
  4. #include <sys/socket.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <assert.h>
  9. #include "tmate.h"
  10. #define TMATE_DNS_RETRY_TIMEOUT 2
  11. #define TMATE_RECONNECT_RETRY_TIMEOUT 2
  12. struct tmate_session tmate_session;
  13. static void lookup_and_connect(void);
  14. static void on_dns_retry(__unused evutil_socket_t fd, __unused short what,
  15. __unused void *arg)
  16. {
  17. lookup_and_connect();
  18. }
  19. static void dns_cb(int errcode, struct evutil_addrinfo *addr, void *ptr)
  20. {
  21. struct evutil_addrinfo *ai;
  22. struct timeval tv;
  23. const char *host = ptr;
  24. if (errcode) {
  25. tmate_status_message("%s lookup failure. Retrying in %d seconds (%s)",
  26. host, TMATE_DNS_RETRY_TIMEOUT,
  27. evutil_gai_strerror(errcode));
  28. tv.tv_sec = TMATE_DNS_RETRY_TIMEOUT;
  29. tv.tv_usec = 0;
  30. evtimer_assign(&tmate_session.ev_dns_retry, tmate_session.ev_base,
  31. on_dns_retry, NULL);
  32. evtimer_add(&tmate_session.ev_dns_retry, &tv);
  33. return;
  34. }
  35. tmate_status_message("Connecting to %s...", host);
  36. for (ai = addr; ai; ai = ai->ai_next) {
  37. char buf[128];
  38. const char *ip = NULL;
  39. if (ai->ai_family == AF_INET) {
  40. struct sockaddr_in *sin = (struct sockaddr_in *)ai->ai_addr;
  41. ip = evutil_inet_ntop(AF_INET, &sin->sin_addr, buf, 128);
  42. } else if (ai->ai_family == AF_INET6) {
  43. struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ai->ai_addr;
  44. ip = evutil_inet_ntop(AF_INET6, &sin6->sin6_addr, buf, 128);
  45. }
  46. tmate_debug("Trying server %s", ip);
  47. /*
  48. * Note: We don't deal with the client list. Clients manage it
  49. * and free client structs when necessary.
  50. */
  51. (void)tmate_ssh_client_alloc(&tmate_session, ip);
  52. }
  53. evutil_freeaddrinfo(addr);
  54. /*
  55. * XXX For some reason, freeing the DNS resolver makes MacOSX flip out...
  56. * not sure what's going on...
  57. * evdns_base_free(tmate_session.ev_dnsbase, 0);
  58. * tmate_session.ev_dnsbase = NULL;
  59. */
  60. }
  61. static void lookup_and_connect(void)
  62. {
  63. struct evutil_addrinfo hints;
  64. const char *tmate_server_host;
  65. if (!tmate_session.ev_dnsbase)
  66. tmate_session.ev_dnsbase = evdns_base_new(tmate_session.ev_base, 1);
  67. if (!tmate_session.ev_dnsbase)
  68. tmate_fatal("Cannot initialize the DNS lookup service");
  69. memset(&hints, 0, sizeof(hints));
  70. hints.ai_family = AF_UNSPEC;
  71. hints.ai_flags = EVUTIL_AI_ADDRCONFIG;
  72. hints.ai_socktype = SOCK_STREAM;
  73. hints.ai_protocol = IPPROTO_TCP;
  74. tmate_server_host = options_get_string(global_options,
  75. "tmate-server-host");
  76. tmate_info("Looking up %s...", tmate_server_host);
  77. (void)evdns_getaddrinfo(tmate_session.ev_dnsbase, tmate_server_host, NULL,
  78. &hints, dns_cb, (void *)tmate_server_host);
  79. }
  80. static void __tmate_session_init(struct tmate_session *session,
  81. struct event_base *base)
  82. {
  83. memset(session, 0, sizeof(*session));
  84. session->ev_base = base;
  85. /*
  86. * Early initialization of encoder because we need to parse
  87. * config files to get the server configs, but while we are parsing
  88. * config files, we need to buffer bind commands and all for the
  89. * slave.
  90. * Decoder is setup later.
  91. */
  92. tmate_encoder_init(&session->encoder, NULL, &tmate_session);
  93. session->min_sx = -1;
  94. session->min_sy = -1;
  95. TAILQ_INIT(&session->clients);
  96. }
  97. void tmate_session_init(struct event_base *base)
  98. {
  99. __tmate_session_init(&tmate_session, base);
  100. tmate_write_header();
  101. }
  102. void tmate_session_start(void)
  103. {
  104. /*
  105. * We split init and start because:
  106. * - We need to process the tmux config file during the connection as
  107. * we are setting up the tmate identity.
  108. * - While we are parsing the config file, we need to be able to
  109. * serialize it, and so we need a worker encoder.
  110. */
  111. lookup_and_connect();
  112. }
  113. static void on_reconnect_retry(__unused evutil_socket_t fd, __unused short what, void *arg)
  114. {
  115. struct tmate_session *session = arg;
  116. if (session->last_server_ip) {
  117. /*
  118. * We have a previous server ip. Let's try that again first,
  119. * but then connect to any server if it fails again.
  120. */
  121. (void)tmate_ssh_client_alloc(&tmate_session, session->last_server_ip);
  122. free(session->last_server_ip);
  123. session->last_server_ip = NULL;
  124. } else {
  125. lookup_and_connect();
  126. }
  127. }
  128. void tmate_reconnect_session(struct tmate_session *session, const char *message)
  129. {
  130. /*
  131. * We no longer have an SSH connection. Time to reconnect.
  132. * We'll reuse some of the session information if we can,
  133. * and we'll try to reconnect to the same server if possible,
  134. * to avoid an SSH connection string change.
  135. */
  136. struct timeval tv = { .tv_sec = TMATE_RECONNECT_RETRY_TIMEOUT, .tv_usec = 0 };
  137. evtimer_assign(&session->ev_connection_retry, session->ev_base,
  138. on_reconnect_retry, session);
  139. evtimer_add(&session->ev_connection_retry, &tv);
  140. if (message)
  141. tmate_status_message("Reconnecting... (%s)", message);
  142. else
  143. tmate_status_message("Reconnecting...");
  144. /*
  145. * This says that we'll need to send a snapshot of the current state.
  146. * Until we have persisted logs...
  147. */
  148. session->reconnected = true;
  149. }