work in progress for async connect

--HG--
branch : fastopen
This commit is contained in:
Matt Johnston 2015-02-18 00:05:27 +08:00
parent 28f61c8b3a
commit 8795d733ec
5 changed files with 246 additions and 93 deletions

View File

@ -632,7 +632,7 @@ fi
AC_PROG_GCC_TRADITIONAL AC_PROG_GCC_TRADITIONAL
AC_FUNC_MEMCMP AC_FUNC_MEMCMP
AC_FUNC_SELECT_ARGTYPES 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)) AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME))

299
dbutil.c
View File

@ -439,95 +439,6 @@ static void set_piggyback_ack(int sock) {
} }
#endif #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 /* 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, * 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. * it will be run after the child has fork()ed, and is passed exec_data.
@ -1069,3 +980,213 @@ time_t monotonic_now() {
return time(NULL); 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"))
}

View File

@ -76,7 +76,7 @@ void getaddrstring(struct sockaddr_storage* addr,
void set_sock_nodelay(int sock); void set_sock_nodelay(int sock);
void set_sock_priority(int sock, enum dropbear_prio prio); void set_sock_priority(int sock, enum dropbear_prio prio);
#ifdef __linux__ #if defined(__linux__) && HAVE_SENDMSG
#define DROPBEAR_TCP_FAST_OPEN #define DROPBEAR_TCP_FAST_OPEN
void set_listen_fast_open(int sock); void set_listen_fast_open(int sock);
#endif #endif
@ -89,7 +89,6 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell);
#ifdef ENABLE_CONNECT_UNIX #ifdef ENABLE_CONNECT_UNIX
int connect_unix(const char* addr); int connect_unix(const char* addr);
#endif #endif
int connect_remote(const char* remotehost, const char* remoteport, char ** errstring);
int buf_readfile(buffer* buf, const char* filename); int buf_readfile(buffer* buf, const char* filename);
int buf_getline(buffer * line, FILE * authfile); int buf_getline(buffer * line, FILE * authfile);
@ -118,4 +117,16 @@ time_t monotonic_now();
char * expand_tilde(const char *inpath); 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_ */ #endif /* _DBUTIL_H_ */

View File

@ -52,10 +52,29 @@ static buffer* buf_decompress(buffer* buf, unsigned int len);
static void buf_compress(buffer * dest, buffer * src, unsigned int len); static void buf_compress(buffer * dest, buffer * src, unsigned int len);
#endif #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 */ /* non-blocking function writing out a current encrypted packet */
void write_packet() { void write_packet() {
int len, written; ssize_t written;
int len;
buffer * writebuf = NULL; buffer * writebuf = NULL;
unsigned packet_type; unsigned packet_type;
#ifdef HAVE_WRITEV #ifdef HAVE_WRITEV

View File

@ -145,6 +145,8 @@ struct sshsession {
int signal_pipe[2]; /* stores endpoints of a self-pipe used for int signal_pipe[2]; /* stores endpoints of a self-pipe used for
race-free signal handling */ race-free signal handling */
m_list conn_pending;
/* time of the last packet send/receive, for keepalive. Not real-world clock */ /* time of the last packet send/receive, for keepalive. Not real-world clock */
time_t last_packet_time_keepalive_sent; time_t last_packet_time_keepalive_sent;
time_t last_packet_time_keepalive_recv; time_t last_packet_time_keepalive_recv;