From 3b5a92f238c45b39fbdc0c88a28a6bd113a9385b Mon Sep 17 00:00:00 2001 From: KingToolbox Date: Sun, 18 Oct 2020 17:16:42 +0800 Subject: [PATCH] Add support for external socket in libssh. --- src/libssh/include/libssh/callbacks.h | 11 +++++ src/libssh/include/libssh/libssh.h | 4 ++ src/libssh/include/libssh/session.h | 3 ++ src/libssh/include/libssh/socket.h | 1 + src/libssh/src/client.c | 17 ++++++-- src/libssh/src/options.c | 60 ++++++++++++++++++++++++++- src/libssh/src/session.c | 1 + src/libssh/src/socket.c | 12 +++++- 8 files changed, 102 insertions(+), 7 deletions(-) diff --git a/src/libssh/include/libssh/callbacks.h b/src/libssh/include/libssh/callbacks.h index 15e8801..ecedf96 100644 --- a/src/libssh/include/libssh/callbacks.h +++ b/src/libssh/include/libssh/callbacks.h @@ -400,6 +400,17 @@ struct ssh_socket_callbacks_struct { }; typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks; +struct ssh_socket_external_callbacks_struct { + /** + * User-provided data. User is free to set anything he wants here + */ + void *userdata; + /** This function will be called each time data need send. + */ + ssh_callback_data send; +}; +typedef struct ssh_socket_external_callbacks_struct *ssh_socket_external_callbacks; + #define SSH_SOCKET_FLOW_WRITEWILLBLOCK 1 #define SSH_SOCKET_FLOW_WRITEWONTBLOCK 2 diff --git a/src/libssh/include/libssh/libssh.h b/src/libssh/include/libssh/libssh.h index 5b9ad50..add3163 100644 --- a/src/libssh/include/libssh/libssh.h +++ b/src/libssh/include/libssh/libssh.h @@ -362,6 +362,9 @@ enum ssh_options_e { SSH_OPTIONS_HOST, SSH_OPTIONS_PORT, SSH_OPTIONS_PORT_STR, + SSH_OPTIONS_PROXY_HOST, + SSH_OPTIONS_PROXY_PORT, + SSH_OPTIONS_PROXY_PORT_STR, SSH_OPTIONS_FD, SSH_OPTIONS_USER, SSH_OPTIONS_SSH_DIR, @@ -400,6 +403,7 @@ enum ssh_options_e { SSH_OPTIONS_PROCESS_CONFIG, SSH_OPTIONS_REKEY_DATA, SSH_OPTIONS_REKEY_TIME, + SSH_OPTIONS_EXTERNAL_CALLBACKS }; enum { diff --git a/src/libssh/include/libssh/session.h b/src/libssh/include/libssh/session.h index 2225615..b3d8f4f 100644 --- a/src/libssh/include/libssh/session.h +++ b/src/libssh/include/libssh/session.h @@ -200,6 +200,7 @@ struct ssh_session_struct { struct ssh_packet_callbacks_struct default_packet_callbacks; struct ssh_list *packet_callbacks; struct ssh_socket_callbacks_struct socket_callbacks; + struct ssh_socket_external_callbacks_struct socket_external_callbacks; ssh_poll_ctx default_poll_ctx; /* options */ #ifdef WITH_PCAP @@ -209,6 +210,7 @@ struct ssh_session_struct { struct ssh_list *identity; char *username; char *host; + char *proxy_host; char *bindaddr; /* bind the client to an ip addr */ char *sshdir; char *knownhosts; @@ -220,6 +222,7 @@ struct ssh_session_struct { unsigned long timeout; /* seconds */ unsigned long timeout_usec; unsigned int port; + unsigned int proxy_port; socket_t fd; int StrictHostKeyChecking; char compressionlevel; diff --git a/src/libssh/include/libssh/socket.h b/src/libssh/include/libssh/socket.h index 5e345c6..5f18e74 100644 --- a/src/libssh/include/libssh/socket.h +++ b/src/libssh/include/libssh/socket.h @@ -60,6 +60,7 @@ int ssh_socket_set_nonblocking(socket_t fd); int ssh_socket_set_blocking(socket_t fd); void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks); +void ssh_socket_set_external_callbacks(ssh_socket s, ssh_socket_external_callbacks external_callbacks); int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s); struct ssh_poll_handle_struct * ssh_socket_get_poll_handle(ssh_socket s); diff --git a/src/libssh/src/client.c b/src/libssh/src/client.c index 4610b78..9fd1a44 100644 --- a/src/libssh/src/client.c +++ b/src/libssh/src/client.c @@ -571,6 +571,8 @@ int ssh_connect(ssh_session session) session->socket_callbacks.exception = ssh_socket_exception_callback; session->socket_callbacks.userdata = session; + ssh_socket_set_external_callbacks(session->socket, &session->socket_external_callbacks); + if (session->opts.fd != SSH_INVALID_SOCKET) { session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED; ssh_socket_set_fd(session->socket, session->opts.fd); @@ -581,10 +583,17 @@ int ssh_connect(ssh_session session) session->opts.ProxyCommand); #endif } else { - ret = ssh_socket_connect(session->socket, - session->opts.host, - session->opts.port > 0 ? session->opts.port : 22, - session->opts.bindaddr); + if (session->opts.proxy_host != NULL) { + ret = ssh_socket_connect(session->socket, + session->opts.proxy_host, + session->opts.proxy_port > 0 ? session->opts.proxy_port : 22, + session->opts.bindaddr); + } else { + ret = ssh_socket_connect(session->socket, + session->opts.host, + session->opts.port > 0 ? session->opts.port : 22, + session->opts.bindaddr); + } } if (ret == SSH_ERROR) { return SSH_ERROR; diff --git a/src/libssh/src/options.c b/src/libssh/src/options.c index b5f951a..ffa97d7 100644 --- a/src/libssh/src/options.c +++ b/src/libssh/src/options.c @@ -520,7 +520,44 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, } } break; + case SSH_OPTIONS_PROXY_HOST: + v = value; + if (v == NULL || v[0] == '\0') { + ssh_set_error_invalid(session); + return -1; + } else { + q = strdup(value); + if (q == NULL) { + ssh_set_error_oom(session); + return -1; + } + p = strchr(q, '@'); + + SAFE_FREE(session->opts.proxy_host); + + if (p) { + *p = '\0'; + session->opts.proxy_host = strdup(p + 1); + if (session->opts.proxy_host == NULL) { + SAFE_FREE(q); + ssh_set_error_oom(session); + return -1; + } + + SAFE_FREE(session->opts.username); + session->opts.username = strdup(q); + SAFE_FREE(q); + if (session->opts.username == NULL) { + ssh_set_error_oom(session); + return -1; + } + } else { + session->opts.proxy_host = q; + } + } + break; case SSH_OPTIONS_PORT: + case SSH_OPTIONS_PROXY_PORT: if (value == NULL) { ssh_set_error_invalid(session); return -1; @@ -531,10 +568,15 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, return -1; } - session->opts.port = *x & 0xffffU; + if (type == SSH_OPTIONS_PORT) { + session->opts.port = *x & 0xffffU; + } else if (type == SSH_OPTIONS_PROXY_PORT) { + session->opts.proxy_port = *x & 0xffffU; + } } break; case SSH_OPTIONS_PORT_STR: + case SSH_OPTIONS_PROXY_PORT_STR: v = value; if (v == NULL || v[0] == '\0') { ssh_set_error_invalid(session); @@ -555,7 +597,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, return -1; } - session->opts.port = i & 0xffffU; + if (type == SSH_OPTIONS_PORT_STR) { + session->opts.port = i & 0xffffU; + } else if (type == SSH_OPTIONS_PROXY_PORT_STR) { + session->opts.proxy_port = i & 0xffffU; + } } break; case SSH_OPTIONS_FD: @@ -1029,6 +1075,16 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, session->opts.rekey_time = (*x) * 1000; } break; + case SSH_OPTIONS_EXTERNAL_CALLBACKS: + if (value == NULL) { + session->socket_external_callbacks.send = NULL; + session->socket_external_callbacks.userdata = NULL; + } else { + ssh_socket_external_callbacks external_callbacks = (ssh_socket_external_callbacks) value; + session->socket_external_callbacks.send = external_callbacks->send; + session->socket_external_callbacks.userdata = external_callbacks->userdata; + } + break; default: ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type); return -1; diff --git a/src/libssh/src/session.c b/src/libssh/src/session.c index 05f81f2..4244251 100644 --- a/src/libssh/src/session.c +++ b/src/libssh/src/session.c @@ -300,6 +300,7 @@ void ssh_free(ssh_session session) SAFE_FREE(session->opts.custombanner); SAFE_FREE(session->opts.username); SAFE_FREE(session->opts.host); + SAFE_FREE(session->opts.proxy_host); SAFE_FREE(session->opts.sshdir); SAFE_FREE(session->opts.knownhosts); SAFE_FREE(session->opts.global_knownhosts); diff --git a/src/libssh/src/socket.c b/src/libssh/src/socket.c index 2fef8e7..b56a865 100644 --- a/src/libssh/src/socket.c +++ b/src/libssh/src/socket.c @@ -88,6 +88,7 @@ struct ssh_socket_struct { ssh_buffer in_buffer; ssh_session session; ssh_socket_callbacks callbacks; + ssh_socket_external_callbacks external_callbacks; ssh_poll_handle poll_handle; #ifndef _WIN32 pid_t proxy_pid; @@ -214,6 +215,11 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks) s->callbacks = callbacks; } +void ssh_socket_set_external_callbacks(ssh_socket s, ssh_socket_external_callbacks external_callbacks) +{ + s->external_callbacks = external_callbacks; +} + /** * @brief SSH poll callback. This callback will be used when an event * caught on the socket. @@ -577,7 +583,11 @@ static ssize_t ssh_socket_unbuffered_write(ssh_socket s, } if (s->fd_is_socket) { - w = send(s->fd, buffer, len, flags); + if (s->external_callbacks->send != NULL) { + w = s->external_callbacks->send(buffer, len, s->external_callbacks->userdata); + } else { + w = send(s->fd, buffer, len, flags); + } } else { w = write(s->fd, buffer, len); }