# HG changeset patch # User Matt Johnston # Date 1424189127 -28800 # Node ID aac0095dc3b454341b3d14b258a4b307de1a238b # Parent a00303a7d2473ce6d36fc34b04296d170fbd58c3 work in progress for async connect diff -r a00303a7d247 -r aac0095dc3b4 configure.ac --- a/configure.ac Sun Feb 15 22:34:05 2015 +0800 +++ b/configure.ac Wed Feb 18 00:05:27 2015 +0800 @@ -632,7 +632,7 @@ AC_PROG_GCC_TRADITIONAL AC_FUNC_MEMCMP AC_FUNC_SELECT_ARGTYPES -AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork writev]) +AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork writev sendmsg]) AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME)) diff -r a00303a7d247 -r aac0095dc3b4 dbutil.c --- 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")) +} diff -r a00303a7d247 -r aac0095dc3b4 dbutil.h --- a/dbutil.h Sun Feb 15 22:34:05 2015 +0800 +++ b/dbutil.h Wed Feb 18 00:05:27 2015 +0800 @@ -76,7 +76,7 @@ void set_sock_nodelay(int sock); void set_sock_priority(int sock, enum dropbear_prio prio); -#ifdef __linux__ +#if defined(__linux__) && HAVE_SENDMSG #define DROPBEAR_TCP_FAST_OPEN void set_listen_fast_open(int sock); #endif @@ -89,7 +89,6 @@ #ifdef ENABLE_CONNECT_UNIX int connect_unix(const char* addr); #endif -int connect_remote(const char* remotehost, const char* remoteport, char ** errstring); int buf_readfile(buffer* buf, const char* filename); int buf_getline(buffer * line, FILE * authfile); @@ -118,4 +117,16 @@ char * expand_tilde(const char *inpath); +struct dropbear_progress_connection; + +/* result is DROPBEAR_SUCCESS or DROPBEAR_FAILURE. +errstring is only set on DROPBEAR_FAILURE, returns failure message for the last attempted socket */ +typedef void(*connect_callback)(int result, int sock, void* data, const char* errstring); + +struct dropbear_progress_connection * connect_remote (const char* remotehost, const char* remoteport, + connect_callback cb, void *cb_data); + +void set_connect_fds(fd_set *writefd); +void handle_connect_fds(fd_set *writefd); + #endif /* _DBUTIL_H_ */ diff -r a00303a7d247 -r aac0095dc3b4 packet.c --- a/packet.c Sun Feb 15 22:34:05 2015 +0800 +++ b/packet.c Wed Feb 18 00:05:27 2015 +0800 @@ -52,10 +52,29 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len); #endif +struct iovec * dropbear_queue_to_iovec(struct Queue *queue) { + + struct iovec *iov = NULL; + struct Link *l; + int iov_max_count; + + #ifndef IOV_MAX + #define IOV_MAX UIO_MAXIOV + #endif + +#error incomplete + +} + +void dropbear_queue_consume(struct Queue *queue, ssize_t written) { + +} + /* non-blocking function writing out a current encrypted packet */ void write_packet() { - int len, written; + ssize_t written; + int len; buffer * writebuf = NULL; unsigned packet_type; #ifdef HAVE_WRITEV diff -r a00303a7d247 -r aac0095dc3b4 session.h --- a/session.h Sun Feb 15 22:34:05 2015 +0800 +++ b/session.h Wed Feb 18 00:05:27 2015 +0800 @@ -144,6 +144,8 @@ int signal_pipe[2]; /* stores endpoints of a self-pipe used for race-free signal handling */ + + m_list conn_pending; /* time of the last packet send/receive, for keepalive. Not real-world clock */ time_t last_packet_time_keepalive_sent;