diff options
Diffstat (limited to 'common.c')
-rw-r--r-- | common.c | 240 |
1 files changed, 0 insertions, 240 deletions
@@ -41,12 +41,6 @@ return false; \ BLOCK_END -// A few other debugging shorthands -#define LOG_FUNC_FAILURE(name, desc) \ - print_debug ("%s: %s: %s", __func__, (name), (desc)) -#define LOG_LIBC_FAILURE(name) \ - print_debug ("%s: %s: %s", __func__, (name), strerror (errno)) - // --- To be moved to liberty -------------------------------------------------- static void @@ -90,240 +84,6 @@ log_message_syslog (void *user_data, const char *quote, const char *fmt, syslog (prio, "%s%s", quote, buf); } -// --- Connector --------------------------------------------------------------- - -// This is a helper that tries to establish a connection with any address on -// a given list. Sadly it also introduces a bit of a callback hell. - -struct connector_target -{ - LIST_HEADER (struct connector_target) - - char *hostname; ///< Target hostname or address - char *service; ///< Target service name or port - - struct addrinfo *results; ///< Resolved target - struct addrinfo *iter; ///< Current endpoint -}; - -static struct connector_target * -connector_target_new (void) -{ - struct connector_target *self = xmalloc (sizeof *self); - return self; -} - -static void -connector_target_destroy (struct connector_target *self) -{ - free (self->hostname); - free (self->service); - freeaddrinfo (self->results); - free (self); -} - -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct connector -{ - int socket; ///< Socket FD for the connection - struct poller_fd connected_event; ///< We've connected or failed - struct connector_target *targets; ///< Targets - struct connector_target *targets_t; ///< Tail of targets - - void *user_data; ///< User data for callbacks - - // You may destroy the connector object in these two main callbacks: - - /// Connection has been successfully established - void (*on_connected) (void *user_data, int socket); - /// Failed to establish a connection to either target - void (*on_failure) (void *user_data); - - // Optional: - - /// Connecting to a new address - void (*on_connecting) (void *user_data, const char *address); - /// Connecting to the last address has failed - void (*on_error) (void *user_data, const char *error); -}; - -static void -connector_notify_connecting (struct connector *self, - struct connector_target *target, struct addrinfo *gai_iter) -{ - if (!self->on_connecting) - return; - - const char *real_host = target->hostname; - - // We don't really need this, so we can let it quietly fail - char buf[NI_MAXHOST]; - int err = getnameinfo (gai_iter->ai_addr, gai_iter->ai_addrlen, - buf, sizeof buf, NULL, 0, NI_NUMERICHOST); - if (err) - LOG_FUNC_FAILURE ("getnameinfo", gai_strerror (err)); - else - real_host = buf; - - char *address = format_host_port_pair (real_host, target->service); - self->on_connecting (self->user_data, address); - free (address); -} - -static void -connector_notify_error (struct connector *self, const char *error) -{ - if (self->on_error) - self->on_error (self->user_data, error); -} - -static void -connector_prepare_next (struct connector *self) -{ - struct connector_target *target = self->targets; - if (!(target->iter = target->iter->ai_next)) - { - LIST_UNLINK_WITH_TAIL (self->targets, self->targets_t, target); - connector_target_destroy (target); - } -} - -static void -connector_step (struct connector *self) -{ - struct connector_target *target = self->targets; - if (!target) - { - // Total failure, none of the targets has succeeded - self->on_failure (self->user_data); - return; - } - - struct addrinfo *gai_iter = target->iter; - hard_assert (gai_iter != NULL); - - connector_notify_connecting (self, target, gai_iter); - - int fd = socket (gai_iter->ai_family, - gai_iter->ai_socktype, gai_iter->ai_protocol); - if (fd == -1) - { - connector_notify_error (self, strerror (errno)); - - connector_prepare_next (self); - connector_step (self); - return; - } - - set_cloexec (fd); - set_blocking (fd, false); - - int yes = 1; - soft_assert (setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE, - &yes, sizeof yes) != -1); - - if (!connect (fd, gai_iter->ai_addr, gai_iter->ai_addrlen)) - { - set_blocking (fd, true); - self->on_connected (self->user_data, fd); - return; - } - if (errno != EINPROGRESS) - { - connector_notify_error (self, strerror (errno)); - xclose (fd); - - connector_prepare_next (self); - connector_step (self); - return; - } - - self->connected_event.fd = self->socket = fd; - poller_fd_set (&self->connected_event, POLLOUT); - - connector_prepare_next (self); -} - -static void -connector_on_ready (const struct pollfd *pfd, struct connector *self) -{ - // See http://cr.yp.to/docs/connect.html if this doesn't work. - // The second connect() method doesn't work with DragonflyBSD. - - int error = 0; - socklen_t error_len = sizeof error; - hard_assert (!getsockopt (pfd->fd, - SOL_SOCKET, SO_ERROR, &error, &error_len)); - - if (error) - { - connector_notify_error (self, strerror (error)); - - poller_fd_reset (&self->connected_event); - xclose (self->socket); - self->socket = -1; - - connector_step (self); - } - else - { - poller_fd_reset (&self->connected_event); - self->socket = -1; - - set_blocking (pfd->fd, true); - self->on_connected (self->user_data, pfd->fd); - } -} - -static void -connector_init (struct connector *self, struct poller *poller) -{ - memset (self, 0, sizeof *self); - self->socket = -1; - poller_fd_init (&self->connected_event, poller, self->socket); - self->connected_event.user_data = self; - self->connected_event.dispatcher = (poller_fd_fn) connector_on_ready; -} - -static void -connector_free (struct connector *self) -{ - poller_fd_reset (&self->connected_event); - if (self->socket != -1) - xclose (self->socket); - - LIST_FOR_EACH (struct connector_target, iter, self->targets) - connector_target_destroy (iter); -} - -static bool -connector_add_target (struct connector *self, - const char *hostname, const char *service, struct error **e) -{ - struct addrinfo hints, *results; - memset (&hints, 0, sizeof hints); - hints.ai_socktype = SOCK_STREAM; - - // TODO: even this should be done asynchronously, most likely in - // a thread pool, similarly to how libuv does it - int err = getaddrinfo (hostname, service, &hints, &results); - if (err) - { - error_set (e, "%s: %s", "getaddrinfo", gai_strerror (err)); - return false; - } - - struct connector_target *target = connector_target_new (); - target->hostname = xstrdup (hostname); - target->service = xstrdup (service); - target->results = results; - target->iter = target->results; - - LIST_APPEND_WITH_TAIL (self->targets, self->targets_t, target); - return true; -} - // --- SOCKS 5/4a -------------------------------------------------------------- // Asynchronous SOCKS connector. Adds more stuff on top of the regular one. |