mirror of
https://github.com/clearml/dropbear
synced 2025-04-22 23:24:52 +00:00
work in progress for async connect
--HG-- branch : fastopen
This commit is contained in:
parent
28f61c8b3a
commit
8795d733ec
@ -632,7 +632,7 @@ fi
|
||||
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))
|
||||
|
||||
|
299
dbutil.c
299
dbutil.c
@ -439,95 +439,6 @@ static void set_piggyback_ack(int sock) {
|
||||
}
|
||||
#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 @@ time_t monotonic_now() {
|
||||
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"))
|
||||
}
|
||||
|
15
dbutil.h
15
dbutil.h
@ -76,7 +76,7 @@ void getaddrstring(struct sockaddr_storage* addr,
|
||||
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 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell);
|
||||
#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 @@ time_t monotonic_now();
|
||||
|
||||
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_ */
|
||||
|
21
packet.c
21
packet.c
@ -52,10 +52,29 @@ static buffer* buf_decompress(buffer* buf, unsigned int len);
|
||||
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
|
||||
|
@ -145,6 +145,8 @@ struct sshsession {
|
||||
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;
|
||||
time_t last_packet_time_keepalive_recv;
|
||||
|
Loading…
Reference in New Issue
Block a user