diff options
author | Aris Adamantiadis <aris@0xbadc0de.be> | 2010-05-09 00:48:10 +0200 |
---|---|---|
committer | Aris Adamantiadis <aris@0xbadc0de.be> | 2010-05-09 00:48:10 +0200 |
commit | f31a14b7932ef4cc165ddd8f1f1a5b23eb21beb3 (patch) | |
tree | 2b4149ee9bbd8c264e63de9653d026f29d028a61 /libssh | |
parent | 7886326aa8a4ac2242c743bfe38f4266b0d24551 (diff) | |
download | libssh-f31a14b7932ef4cc165ddd8f1f1a5b23eb21beb3.tar.gz libssh-f31a14b7932ef4cc165ddd8f1f1a5b23eb21beb3.tar.xz libssh-f31a14b7932ef4cc165ddd8f1f1a5b23eb21beb3.zip |
ssh_socket support for 2 fd + Proxyhost command
Diffstat (limited to 'libssh')
-rw-r--r-- | libssh/agent.c | 2 | ||||
-rw-r--r-- | libssh/config.c | 10 | ||||
-rw-r--r-- | libssh/options.c | 8 | ||||
-rw-r--r-- | libssh/pcap.c | 2 | ||||
-rw-r--r-- | libssh/poll.c | 14 | ||||
-rw-r--r-- | libssh/session.c | 14 | ||||
-rw-r--r-- | libssh/socket.c | 269 |
7 files changed, 237 insertions, 82 deletions
diff --git a/libssh/agent.c b/libssh/agent.c index 4bcdcbbc..ac9fe190 100644 --- a/libssh/agent.c +++ b/libssh/agent.c @@ -86,7 +86,7 @@ static size_t atomicio(ssh_socket s, void *buf, size_t n, int do_read) { size_t pos = 0; ssize_t res; struct pollfd pfd; - int fd = ssh_socket_get_fd(s); + int fd = ssh_socket_get_fd_in(s); pfd.fd = fd; pfd.events = do_read ? POLLIN : POLLOUT; diff --git a/libssh/config.c b/libssh/config.c index 375ed715..6c00c130 100644 --- a/libssh/config.c +++ b/libssh/config.c @@ -41,7 +41,8 @@ enum ssh_config_opcode_e { SOC_TIMEOUT, SOC_PROTOCOL, SOC_HOSTKEYCHECK, - SOC_KNOWNHOSTS + SOC_KNOWNHOSTS, + SOC_PROXYCOMMAND }; struct ssh_config_keyword_table_s { @@ -61,6 +62,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = { { "protocol", SOC_PROTOCOL }, { "stricthostkeychecking", SOC_HOSTKEYCHECK }, { "userknownhostsfile", SOC_KNOWNHOSTS }, + { "proxycommand", SOC_PROXYCOMMAND }, { NULL, SOC_UNSUPPORTED } }; @@ -294,6 +296,12 @@ static int ssh_config_parse_line(ssh_session session, const char *line, ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, p); } break; + case SOC_PROXYCOMMAND: + p = ssh_config_get_str(&s, NULL); + if (p && *parsing) { + ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, p); + } + break; case SOC_UNSUPPORTED: fprintf(stderr, "Unsupported option: %s, line: %d\n", keyword, count); break; diff --git a/libssh/options.c b/libssh/options.c index 2b3276a2..df953dd1 100644 --- a/libssh/options.c +++ b/libssh/options.c @@ -624,6 +624,14 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, session->StrictHostKeyChecking = *(int*)value; } break; + case SSH_OPTIONS_PROXYCOMMAND: + if (value == NULL) { + ssh_set_error_invalid(session, __FUNCTION__); + return -1; + } else { + session->ProxyCommand = strdup(value); + } + break; default: ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type); return -1; diff --git a/libssh/pcap.c b/libssh/pcap.c index 58a71a4f..a36faa6b 100644 --- a/libssh/pcap.c +++ b/libssh/pcap.c @@ -247,7 +247,7 @@ static int ssh_pcap_context_connect(ssh_pcap_context ctx){ return SSH_ERROR; if(session->socket==NULL) return SSH_ERROR; - fd=ssh_socket_get_fd(session->socket); + fd=ssh_socket_get_fd_in(session->socket); /* TODO: adapt for windows */ if(fd<0) return SSH_ERROR; diff --git a/libssh/poll.c b/libssh/poll.c index ea59266d..56f940ab 100644 --- a/libssh/poll.c +++ b/libssh/poll.c @@ -463,10 +463,18 @@ int ssh_poll_ctx_add(ssh_poll_ctx ctx, ssh_poll_handle p) { * @return 0 on success, < 0 on error */ int ssh_poll_ctx_add_socket (ssh_poll_ctx ctx, ssh_socket s) { - ssh_poll_handle p=ssh_socket_get_poll_handle(s); - if(p==NULL) + ssh_poll_handle p_in, p_out; + int ret; + p_in=ssh_socket_get_poll_handle_in(s); + if(p_in==NULL) return -1; - return ssh_poll_ctx_add(ctx,p); + ret = ssh_poll_ctx_add(ctx,p_in); + if(ret != 0) + return ret; + p_out=ssh_socket_get_poll_handle_out(s); + if(p_in != p_out) + ret = ssh_poll_ctx_add(ctx,p_out); + return ret; } diff --git a/libssh/session.c b/libssh/session.c index 1fe1f432..56061378 100644 --- a/libssh/session.c +++ b/libssh/session.c @@ -228,6 +228,7 @@ void ssh_free(ssh_session session) { SAFE_FREE(session->host); SAFE_FREE(session->sshdir); SAFE_FREE(session->knownhosts); + SAFE_FREE(session->ProxyCommand); for (i = 0; i < 10; i++) { if (session->wanted_methods[i]) { @@ -292,7 +293,7 @@ socket_t ssh_get_fd(ssh_session session) { return -1; } - return ssh_socket_get_fd(session->socket); + return ssh_socket_get_fd_in(session->socket); } /** @@ -353,16 +354,19 @@ void ssh_set_fd_except(ssh_session session) { * @return SSH_OK on success, SSH_ERROR otherwise. */ int ssh_handle_packets(ssh_session session, int timeout) { - ssh_poll_handle spoll; + ssh_poll_handle spoll_in,spoll_out; ssh_poll_ctx ctx; if(session==NULL || session->socket==NULL) return SSH_ERROR; enter_function(); - spoll=ssh_socket_get_poll_handle(session->socket); - ctx=ssh_poll_get_ctx(spoll); + spoll_in=ssh_socket_get_poll_handle_in(session->socket); + spoll_out=ssh_socket_get_poll_handle_out(session->socket); + ctx=ssh_poll_get_ctx(spoll_in); if(ctx==NULL){ ctx=ssh_get_global_poll_ctx(session); - ssh_poll_ctx_add(ctx,spoll); + ssh_poll_ctx_add(ctx,spoll_in); + if(spoll_in != spoll_out) + ssh_poll_ctx_add(ctx,spoll_out); } ssh_poll_ctx_dopoll(ctx,timeout); leave_function(); diff --git a/libssh/socket.c b/libssh/socket.c index 3e80b89f..af32c399 100644 --- a/libssh/socket.c +++ b/libssh/socket.c @@ -40,6 +40,9 @@ #include "libssh/poll.h" #include "libssh/session.h" +#ifndef _WIN32 +extern const char **environ; +#endif /** * @internal * @@ -61,7 +64,8 @@ enum ssh_socket_states_e { }; struct ssh_socket_struct { - socket_t fd; + socket_t fd_in; + socket_t fd_out; int last_errno; int data_to_read; /* reading now on socket will not block */ @@ -72,7 +76,8 @@ struct ssh_socket_struct { ssh_buffer in_buffer; ssh_session session; ssh_socket_callbacks callbacks; - ssh_poll_handle poll; + ssh_poll_handle poll_in; + ssh_poll_handle poll_out; }; static int ssh_socket_unbuffered_read(ssh_socket s, void *buffer, uint32_t len); @@ -105,7 +110,8 @@ ssh_socket ssh_socket_new(ssh_session session) { if (s == NULL) { return NULL; } - s->fd = -1; + s->fd_in = -1; + s->fd_out= -1; s->last_errno = -1; s->session = session; s->in_buffer = buffer_new(); @@ -122,7 +128,7 @@ ssh_socket ssh_socket_new(ssh_session session) { s->data_to_read = 0; s->data_to_write = 0; s->data_except = 0; - s->poll=NULL; + s->poll_in=s->poll_out=NULL; s->state=SSH_SOCKET_NONE; return s; } @@ -149,16 +155,9 @@ int ssh_socket_pollcallback(ssh_poll_handle p, socket_t fd, int revents, void *v /* Check if we are in a connecting state */ if(s->state==SSH_SOCKET_CONNECTING){ s->state=SSH_SOCKET_ERROR; - ssh_poll_free(p); - s->poll=p=NULL; getsockopt(fd,SOL_SOCKET,SO_ERROR,(void *)&err,&errlen); s->last_errno=err; -#ifdef _WIN32 - closesocket(fd); -#else - close(fd); -#endif - s->fd=-1; + ssh_socket_close(s); if(s->callbacks && s->callbacks->connected) s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR,err, s->callbacks->userdata); @@ -209,7 +208,7 @@ int ssh_socket_pollcallback(ssh_poll_handle p, socket_t fd, int revents, void *v ssh_log(s->session,SSH_LOG_PACKET,"Received POLLOUT in connecting state"); s->state = SSH_SOCKET_CONNECTED; ssh_poll_set_events(p,POLLOUT | POLLIN | POLLERR); - ssh_sock_set_blocking(ssh_socket_get_fd(s)); + ssh_sock_set_blocking(ssh_socket_get_fd_in(s)); if(s->callbacks && s->callbacks->connected) s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata); return 0; @@ -232,21 +231,32 @@ int ssh_socket_pollcallback(ssh_poll_handle p, socket_t fd, int revents, void *v return 0; } -void ssh_socket_register_pollcallback(ssh_socket s, ssh_poll_handle p){ - ssh_poll_set_callback(p,ssh_socket_pollcallback,s); - s->poll=p; +/** @internal + * @brief returns the input poll handle corresponding to the socket, + * creates it if it does not exist. + * @returns allocated and initialized ssh_poll_handle object + */ +ssh_poll_handle ssh_socket_get_poll_handle_in(ssh_socket s){ + if(s->poll_in) + return s->poll_in; + s->poll_in=ssh_poll_new(s->fd_in,0,ssh_socket_pollcallback,s); + if(s->fd_in == s->fd_out && s->poll_out == NULL) + s->poll_out=s->poll_in; + return s->poll_in; } /** @internal - * @brief returns the poll handle corresponding to the socket, + * @brief returns the output poll handle corresponding to the socket, * creates it if it does not exist. * @returns allocated and initialized ssh_poll_handle object */ -ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s){ - if(s->poll) - return s->poll; - s->poll=ssh_poll_new(s->fd,0,ssh_socket_pollcallback,s); - return s->poll; +ssh_poll_handle ssh_socket_get_poll_handle_out(ssh_socket s){ + if(s->poll_out) + return s->poll_out; + s->poll_out=ssh_poll_new(s->fd_out,0,ssh_socket_pollcallback,s); + if(s->fd_in == s->fd_out && s->poll_in == NULL) + s->poll_in=s->poll_out; + return s->poll_out; } /** \internal @@ -259,36 +269,32 @@ void ssh_socket_free(ssh_socket s){ ssh_socket_close(s); buffer_free(s->in_buffer); buffer_free(s->out_buffer); - if(s->poll) - ssh_poll_free(s->poll); SAFE_FREE(s); } #ifndef _WIN32 int ssh_socket_unix(ssh_socket s, const char *path) { struct sockaddr_un sunaddr; - + socket_t fd; sunaddr.sun_family = AF_UNIX; snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", path); - s->fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (s->fd < 0) { + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd < 0) { return -1; } - if (fcntl(s->fd, F_SETFD, 1) == -1) { - close(s->fd); - s->fd = -1; + if (fcntl(fd, F_SETFD, 1) == -1) { + close(fd); return -1; } - if (connect(s->fd, (struct sockaddr *) &sunaddr, + if (connect(fd, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) { - close(s->fd); - s->fd = -1; + close(fd); return -1; } - + ssh_socket_set_fd(s,fd); return 0; } #endif @@ -299,37 +305,81 @@ int ssh_socket_unix(ssh_socket s, const char *path) { void ssh_socket_close(ssh_socket s){ if (ssh_socket_is_open(s)) { #ifdef _WIN32 - closesocket(s->fd); + closesocket(s->fd_in); + /* fd_in = fd_out under win32 */ s->last_errno = WSAGetLastError(); #else - close(s->fd); + close(s->fd_in); + if(s->fd_out != s->fd_in && s->fd_out != -1) + close(s->fd_out); s->last_errno = errno; #endif - s->fd=-1; + s->fd_in = s->fd_out = -1; + } + if(s->poll_in != NULL){ + if(s->poll_out == s->poll_in) + s->poll_out = NULL; + ssh_poll_free(s->poll_in); + s->poll_in=NULL; + } + if(s->poll_out != NULL){ + ssh_poll_free(s->poll_out); + s->poll_out=NULL; } } -/** \internal - * \brief sets the file descriptor of the socket +/** + * @internal + * @brief sets the file descriptor of the socket. + * @param[out] s ssh_socket to update + * @param[in] fd file descriptor to set + * @warning this function updates boths the input and output + * file descriptors */ void ssh_socket_set_fd(ssh_socket s, socket_t fd) { - s->fd = fd; - if(s->poll) - ssh_poll_set_fd(s->poll,fd); + s->fd_in = s->fd_out = fd; + if(s->poll_in) + ssh_poll_set_fd(s->poll_in,fd); +} + +/** + * @internal + * @brief sets the input file descriptor of the socket. + * @param[out] s ssh_socket to update + * @param[in] fd file descriptor to set + */ +void ssh_socket_set_fd_in(ssh_socket s, socket_t fd) { + s->fd_in = fd; + if(s->poll_in) + ssh_poll_set_fd(s->poll_in,fd); +} + +/** + * @internal + * @brief sets the output file descriptor of the socket. + * @param[out] s ssh_socket to update + * @param[in] fd file descriptor to set + */ +void ssh_socket_set_fd_out(ssh_socket s, socket_t fd) { + s->fd_out = fd; + if(s->poll_out) + ssh_poll_set_fd(s->poll_out,fd); } + + /** \internal - * \brief returns the file descriptor of the socket + * \brief returns the input file descriptor of the socket */ -socket_t ssh_socket_get_fd(ssh_socket s) { - return s->fd; +socket_t ssh_socket_get_fd_in(ssh_socket s) { + return s->fd_in; } /** \internal * \brief returns nonzero if the socket is open */ int ssh_socket_is_open(ssh_socket s) { - return s->fd != -1; + return s->fd_in != -1; } /** \internal @@ -342,7 +392,7 @@ static int ssh_socket_unbuffered_read(ssh_socket s, void *buffer, uint32_t len) return -1; } - rc = recv(s->fd,buffer, len, 0); + rc = recv(s->fd_in,buffer, len, 0); #ifdef _WIN32 s->last_errno = WSAGetLastError(); #else @@ -368,7 +418,7 @@ static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer, return -1; } - w = send(s->fd,buffer, len, 0); + w = send(s->fd_out,buffer, len, 0); #ifdef _WIN32 s->last_errno = WSAGetLastError(); #else @@ -376,8 +426,8 @@ static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer, #endif s->data_to_write = 0; /* Reactive the POLLOUT detector in the poll multiplexer system */ - if(s->poll){ - ssh_poll_set_events(s->poll,ssh_poll_get_events(s->poll) | POLLOUT); + if(s->poll_out){ + ssh_poll_set_events(s->poll_out,ssh_poll_get_events(s->poll_out) | POLLOUT); } if (w < 0) { s->data_except = 1; @@ -390,10 +440,10 @@ static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer, * \brief returns nonzero if the current socket is in the fd_set */ int ssh_socket_fd_isset(ssh_socket s, fd_set *set) { - if(s->fd == -1) { + if(s->fd_in == -1) { return 0; } - return FD_ISSET(s->fd,set); + return FD_ISSET(s->fd_in,set) || FD_ISSET(s->fd_out,set); } /** \internal @@ -401,11 +451,15 @@ int ssh_socket_fd_isset(ssh_socket s, fd_set *set) { */ void ssh_socket_fd_set(ssh_socket s, fd_set *set, int *fd_max) { - if (s->fd == -1) + if (s->fd_in == -1) return; - FD_SET(s->fd,set); - if (s->fd >= *fd_max) { - *fd_max = s->fd + 1; + FD_SET(s->fd_in,set); + FD_SET(s->fd_out,set); + if (s->fd_in >= *fd_max) { + *fd_max = s->fd_in + 1; + } + if (s->fd_out >= *fd_max) { + *fd_max = s->fd_out + 1; } } @@ -602,11 +656,14 @@ int ssh_socket_wait_for_data(ssh_socket s, ssh_session session, uint32_t len) { } /* ssh_socket_poll */ +/** @brief polls the socket for activity + * @bug this function should disappear in favor of the new polling mechanism + */ int ssh_socket_poll(ssh_socket s, int *writeable, int *except) { ssh_session session = s->session; - ssh_pollfd_t fd[1]; + ssh_pollfd_t fd[2]; int rc = -1; - + int n_fd; enter_function(); if (!ssh_socket_is_open(s)) { @@ -614,19 +671,29 @@ int ssh_socket_poll(ssh_socket s, int *writeable, int *except) { *writeable = 0; return 0; } - - fd->fd = s->fd; - fd->events = 0; + if(s->fd_in == s->fd_out){ + n_fd=1; + } else { + n_fd=2; + } + fd[0].fd = s->fd_in; + fd[0].events = 0; if (!s->data_to_read) { - fd->events |= POLLIN; + fd[0].events |= POLLIN; } + + if(n_fd == 2){ + fd[1].fd=s->fd_out; + fd[1].events = 0; + } + if (!s->data_to_write) { - fd->events |= POLLOUT; + fd[n_fd - 1].events |= POLLOUT; } /* Make the call, and listen for errors */ - rc = ssh_poll(fd, 1, 0); + rc = ssh_poll(fd, n_fd, 0); if (rc < 0) { ssh_set_error(session, SSH_FATAL, "poll(): %s", strerror(errno)); leave_function(); @@ -634,13 +701,13 @@ int ssh_socket_poll(ssh_socket s, int *writeable, int *except) { } if (!s->data_to_read) { - s->data_to_read = fd->revents & POLLIN; + s->data_to_read = fd[0].revents & POLLIN; } if (!s->data_to_write) { - s->data_to_write = fd->revents & POLLOUT; + s->data_to_write = fd[n_fd - 1].revents & POLLOUT; } if (!s->data_except) { - s->data_except = fd->revents & POLLERR; + s->data_except = fd[0].revents & POLLERR; } *except = s->data_except; @@ -689,9 +756,9 @@ int ssh_socket_nonblocking_flush(ssh_socket s) { } /* Is there some data pending? */ - if (buffer_get_rest_len(s->out_buffer) > 0 && s->poll) { + if (buffer_get_rest_len(s->out_buffer) > 0 && s->poll_out) { /* force the poll system to catch pollout events */ - ssh_poll_set_events(s->poll, ssh_poll_get_events(s->poll) |POLLOUT); + ssh_poll_set_events(s->poll_out, ssh_poll_get_events(s->poll_out) |POLLOUT); leave_function(); return SSH_AGAIN; } @@ -811,14 +878,74 @@ int ssh_socket_connect(ssh_socket s, const char *host, int port, const char *bin ssh_socket_set_fd(s,fd); s->state=SSH_SOCKET_CONNECTING; /* POLLOUT is the event to wait for in a nonblocking connect */ - ssh_poll_set_events(ssh_socket_get_poll_handle(s),POLLOUT); + ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLOUT); #ifdef _WIN32 - ssh_poll_add_events(ssh_socket_get_poll_handle(s),POLLWRNORM); + ssh_poll_add_events(ssh_socket_get_poll_handle_in(s),POLLWRNORM); #endif leave_function(); return SSH_OK; } +#ifndef _WIN32 +/** + * @internal + * @brief executes a command and redirect input and outputs + * @param command command to execute + * @param in input file descriptor + * @param out output file descriptor + */ +void ssh_execute_command(const char *command, socket_t in, socket_t out){ + const char *args[]={"/bin/sh","-c",command,NULL}; + /* redirect in and out to stdin, stdout and stderr */ + dup2(in, 0); + dup2(out,1); + dup2(out,2); + close(in); + close(out); + execve(args[0],(char * const *)args,(char * const *)environ); + exit(1); +} + +/** + * @internal + * @brief Open a socket on a ProxyCommand + * This call will always be nonblocking. + * @param s socket to connect. + * @param command Command to execute. + * @returns SSH_OK socket is being connected. + * @returns SSH_ERROR error while executing the command. + */ + +int ssh_socket_connect_proxycommand(ssh_socket s, const char *command){ + socket_t in_pipe[2]; + socket_t out_pipe[2]; + int pid; + ssh_session session=s->session; + enter_function(); + if(s->state != SSH_SOCKET_NONE) + return SSH_ERROR; + pipe(in_pipe); + pipe(out_pipe); + pid = fork(); + if(pid == 0){ + ssh_execute_command(command,out_pipe[0],in_pipe[1]); + } + close(in_pipe[1]); + close(out_pipe[0]); + ssh_log(session,SSH_LOG_PROTOCOL,"ProxyCommand connection pipe: [%d,%d]",in_pipe[0],out_pipe[1]); + ssh_socket_set_fd_in(s,in_pipe[0]); + ssh_socket_set_fd_out(s,out_pipe[1]); + s->state=SSH_SOCKET_CONNECTED; + /* POLLOUT is the event to wait for in a nonblocking connect */ + ssh_poll_set_events(ssh_socket_get_poll_handle_in(s),POLLIN | POLLERR); + ssh_poll_set_events(ssh_socket_get_poll_handle_out(s),POLLOUT); + if(s->callbacks && s->callbacks->connected) + s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata); + leave_function(); + return SSH_OK; +} + +#endif /* _WIN32 */ /* @} */ /* vim: set ts=4 sw=4 et cindent: */ |