Mercurial > dropbear
diff dbutil.c @ 1024:aac0095dc3b4 fastopen
work in progress for async connect
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Wed, 18 Feb 2015 00:05:27 +0800 |
parents | a00303a7d247 |
children | 02baa0b334e8 |
line wrap: on
line diff
--- a/dbutil.c Sun Feb 15 22:34:05 2015 +0800 +++ b/dbutil.c Wed Feb 18 00:05:27 2015 +0800 @@ -439,95 +439,6 @@ } #endif - -/* Connect via TCP to a host. Connection will try ipv4 or ipv6, will - * return immediately if nonblocking is set. On failure, if errstring - * wasn't null, it will be a newly malloced error message */ - -/* TODO: maxfd */ -int connect_remote(const char* remotehost, const char* remoteport, char ** errstring) { - - struct addrinfo *res0 = NULL, *res = NULL, hints; - int sock; - int err; - - TRACE(("enter connect_remote")) - - if (errstring != NULL) { - *errstring = NULL; - } - - memset(&hints, 0, sizeof(hints)); - hints.ai_socktype = SOCK_STREAM; - hints.ai_family = PF_UNSPEC; - - err = getaddrinfo(remotehost, remoteport, &hints, &res0); - if (err) { - if (errstring != NULL && *errstring == NULL) { - int len; - len = 100 + strlen(gai_strerror(err)); - *errstring = (char*)m_malloc(len); - snprintf(*errstring, len, "Error resolving '%s' port '%s'. %s", - remotehost, remoteport, gai_strerror(err)); - } - TRACE(("Error resolving: %s", gai_strerror(err))) - return -1; - } - - sock = -1; - err = EADDRNOTAVAIL; - for (res = res0; res; res = res->ai_next) { - - sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); - if (sock < 0) { - err = errno; - continue; - } - - setnonblocking(sock); - -#if defined(__linux__) && defined(TCP_DEFER_ACCEPT) - set_piggyback_ack(sock); -#endif - - if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { - if (errno == EINPROGRESS) { - TRACE(("Connect in progress")) - break; - } else { - err = errno; - close(sock); - sock = -1; - continue; - } - } - - break; /* Success */ - } - - if (sock < 0 && !(errno == EINPROGRESS)) { - /* Failed */ - if (errstring != NULL && *errstring == NULL) { - int len; - len = 20 + strlen(strerror(err)); - *errstring = (char*)m_malloc(len); - snprintf(*errstring, len, "Error connecting: %s", strerror(err)); - } - TRACE(("Error connecting: %s", strerror(err))) - } else { - /* Success */ - set_sock_nodelay(sock); - } - - freeaddrinfo(res0); - if (sock > 0 && errstring != NULL && *errstring != NULL) { - m_free(*errstring); - } - - TRACE(("leave connect_remote: sock %d\n", sock)) - return sock; -} - /* Sets up a pipe for a, returning three non-blocking file descriptors * and the pid. exec_fn is the function that will actually execute the child process, * it will be run after the child has fork()ed, and is passed exec_data. @@ -1069,3 +980,213 @@ return time(NULL); } + +struct dropbear_progress_connection +{ + struct addrinfo *res; + struct addrinfo *res_iter; + + char *remotehost, *remoteport; /* For error reporting */ + + connect_callback cb; + void *cb_data; + + struct Queue *writequeue; /* A queue of encrypted packets to send with TCP fastopen, + or NULL. */ + + int sock; +}; + +/* Deallocate a progress connection. Removes from the pending list if iter!=NULL. +Does not close sockets */ +static void remove_connect(struct dropbear_progress_connection *c, m_list_elem *iter) { + if (c->res) { + freeaddrinfo(c->res); + } + m_free(c->remotehost); + m_free(c->remoteport); + m_free(c); + + if (iter) { + list_remove(iter); + } +} + +static int connect_try_next(struct dropbear_progress_connection *c) { + int err = EADDRNOTAVAIL; + struct addrinfo *r; + + if (!c->res_iter) { + return DROPBEAR_FAILURE; + } + + for (r = c->res_iter; r; r = r->ai_next) + { + assert(c->sock == -1); + + c->sock = socket(c->res_iter->ai_family, c->res_iter->ai_socktype, c->res_iter->ai_protocol); + if (c->sock < 0) { + err = errno; + continue; + } + + setnonblocking(c->sock); + +#if defined(__linux__) && defined(TCP_DEFER_ACCEPT) + set_piggyback_ack(sock); +#endif + + if (connect(c->sock, r->ai_addr, r->ai_addrlen) < 0) { + if (errno == EINPROGRESS) { + TRACE(("Connect in progress")) + break; + } else { + err = errno; + close(c->sock); + c->sock = -1; + continue; + } + } + + break; /* Success. Treated the same as EINPROGRESS */ + } + + if (r) { + c->res_iter = r->ai_next; + } else { + c->res_iter = NULL; + } + + if (c->sock >= 0 || (errno == EINPROGRESS)) { + /* Success */ + set_sock_nodelay(c->sock); + return DROPBEAR_SUCCESS; + } else { + /* XXX - returning error message through */ +#if 0 + /* Failed */ + if (errstring != NULL && *errstring == NULL) { + int len; + len = 20 + strlen(strerror(err)); + *errstring = (char*)m_malloc(len); + snprintf(*errstring, len, "Error connecting: %s", strerror(err)); + } + TRACE(("Error connecting: %s", strerror(err))) +#endif + return DROPBEAR_FAILURE; + } +} + +/* Connect via TCP to a host. Connection will try ipv4 or ipv6, will + * return immediately if nonblocking is set. On failure, if errstring + * wasn't null, it will be a newly malloced error message */ + +/* TODO: maxfd */ +struct dropbear_progress_connection *connect_remote(const char* remotehost, const char* remoteport, + connect_callback cb, void* cb_data) +{ + struct dropbear_progress_connection *c = NULL; + int err; + struct addrinfo hints; + + c = m_malloc(sizeof(*c)); + c->remotehost = m_strdup(remotehost); + c->remoteport = m_strdup(remoteport); + c->sock = -1; + c->cb = cb; + c->cb_data = cb_data; + + memset(&hints, 0, sizeof(hints)); + hints.ai_socktype = SOCK_STREAM; + hints.ai_family = PF_UNSPEC; + + err = getaddrinfo(remotehost, remoteport, &hints, &c->res); + if (err) { + int len; + char *errstring; + len = 100 + strlen(gai_strerror(err)); + errstring = (char*)m_malloc(len); + snprintf(errstring, len, "Error resolving '%s' port '%s'. %s", + remotehost, remoteport, gai_strerror(err)); + c->cb(DROPBEAR_FAILURE, -1, c->cb_data, errstring); + m_free(errstring); + TRACE(("Error resolving: %s", gai_strerror(err))) + remove_connect(c, NULL); + return NULL; + } + + c->res_iter = c->res; + + if (connect_try_next(c) == DROPBEAR_FAILURE) { + /* Should not happen - getaddrinfo() should return failure if there are no addresses */ + c->cb(DROPBEAR_FAILURE, -1, c->cb_data, "No address to try"); + TRACE(("leave handle_connect_fds - failed")) + remove_connect(c, NULL); + return NULL; + } + + list_append(&ses.conn_pending, c); + + return c; +} + + +void set_connect_fds(fd_set *writefd) { + m_list_elem *iter; + TRACE(("enter handle_connect_fds")) + for (iter = ses.conn_pending.first; iter; iter = iter->next) { + struct dropbear_progress_connection *c = iter->item; + if (c->sock >= 0) { + FD_SET(c->sock, writefd); + } + else + { + + } + } +} + +void handle_connect_fds(fd_set *writefd) { + m_list_elem *iter; + TRACE(("enter handle_connect_fds")) + for (iter = ses.conn_pending.first; iter; iter = iter->next) { + int val; + socklen_t vallen = sizeof(val); + struct dropbear_progress_connection *c = iter->item; + + if (!FD_ISSET(c->sock, writefd)) { + continue; + } + + TRACE(("handling %s port %s socket %d", c->remotehost, c->remoteport, c->sock)); + + if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &val, &vallen) != 0) { + TRACE(("handle_connect_fds getsockopt(%d) SO_ERROR failed: %s", c->sock, strerror(errno))) + } else if (val != 0) { + /* Connect failed */ + TRACE(("connect to %s port %s failed.", c->remotehost, c->remoteport)) + m_close(c->sock); + c->sock = -1; + + if (connect_try_next(c) == DROPBEAR_FAILURE) { + c->cb(DROPBEAR_FAILURE, -1, c->cb_data, strerror(val)); + TRACE(("leave handle_connect_fds - failed")) + remove_connect(c, iter); + /* Must return here - remove_connect() invalidates iter */ + return; + } else { + /* new connection try was successfuly started, will be finished by a + later call to handle_connect_fds() */ + TRACE(("leave handle_connect_fds - new try")) + continue; + } + } + /* New connection has been established */ + c->cb(DROPBEAR_SUCCESS, c->sock, c->cb_data, ""); + remove_connect(c, iter); + TRACE(("leave handle_connect_fds - success")) + /* Must return here - remove_connect() invalidates iter */ + return; + } + TRACE(("leave handle_connect_fds - end iter")) +}