int ssh_socket_get_poll_flags(ssh_socket s) { int r = 0; if (s->poll_in != NULL && (ssh_poll_get_events (s->poll_in) & POLLIN) > 0) { r |= SSH_READ_PENDING; } if (s->poll_out != NULL && (ssh_poll_get_events (s->poll_out) & POLLOUT) > 0) { r |= SSH_WRITE_PENDING; } return r; }
/** \internal * \brief writes len bytes from buffer to socket */ static int ssh_socket_unbuffered_write(ssh_socket s, const void *buffer, uint32_t len) { int w = -1; if (s->data_except) { return -1; } if (s->fd_is_socket) w = send(s->fd_out,buffer, len, 0); else w = write(s->fd_out, buffer, len); #ifdef _WIN32 s->last_errno = WSAGetLastError(); #else s->last_errno = errno; #endif s->write_wontblock = 0; /* Reactive the POLLOUT detector in the poll multiplexer system */ if(s->poll_out){ SSH_LOG(SSH_LOG_PACKET, "Enabling POLLOUT for socket"); ssh_poll_set_events(s->poll_out,ssh_poll_get_events(s->poll_out) | POLLOUT); } if (w < 0) { s->data_except = 1; } return w; }
/** \internal * \brief writes len bytes from buffer to socket */ static ssize_t ssh_socket_unbuffered_write(ssh_socket s, const void *buffer, uint32_t len) { ssize_t w = -1; int flags = 0; #ifdef MSG_NOSIGNAL flags |= MSG_NOSIGNAL; #endif if (s->data_except) { return -1; } if (s->fd_is_socket) { w = send(s->fd, buffer, len, flags); } else { w = write(s->fd, buffer, len); } #ifdef _WIN32 s->last_errno = WSAGetLastError(); #else s->last_errno = errno; #endif s->write_wontblock = 0; /* Reactive the POLLOUT detector in the poll multiplexer system */ if (s->poll_handle) { SSH_LOG(SSH_LOG_PACKET, "Enabling POLLOUT for socket"); ssh_poll_set_events(s->poll_handle,ssh_poll_get_events(s->poll_handle) | POLLOUT); } if (w < 0) { s->data_except = 1; } return w; }
/** * @brief Remove events from a poll object. Non-existent are ignored. * The events will also be propagated to an associated poll context. * * @param p Pointer to an already allocated poll object. * @param events Poll events. */ void ssh_poll_remove_events(ssh_poll_handle p, short events) { ssh_poll_set_events(p, ssh_poll_get_events(p) & ~events); }
/** * @brief Add extra events to a poll object. Duplicates are ignored. * The events will also be propagated to an associated poll context. * * @param p Pointer to an already allocated poll object. * @param events Poll events. */ void ssh_poll_add_events(ssh_poll_handle p, short events) { ssh_poll_set_events(p, ssh_poll_get_events(p) | events); }
/** * @brief SSH poll callback. This callback will be used when an event * caught on the socket. * * @param p Poll object this callback belongs to. * @param fd The raw socket. * @param revents The current poll events on the socket. * @param userdata Userdata to be passed to the callback function, * in this case the socket object. * * @return 0 on success, < 0 when the poll object has been removed * from its poll context. */ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s){ ssh_socket s=(ssh_socket )v_s; char buffer[4096]; int r; int err=0; socklen_t errlen=sizeof(err); /* Do not do anything if this socket was already closed */ if(!ssh_socket_is_open(s)){ return -1; } if(revents & POLLERR){ /* Check if we are in a connecting state */ if(s->state==SSH_SOCKET_CONNECTING){ s->state=SSH_SOCKET_ERROR; getsockopt(fd,SOL_SOCKET,SO_ERROR,(void *)&err,&errlen); s->last_errno=err; ssh_socket_close(s); if(s->callbacks && s->callbacks->connected) s->callbacks->connected(SSH_SOCKET_CONNECTED_ERROR,err, s->callbacks->userdata); return -1; } /* Then we are in a more standard kind of error */ /* force a read to get an explanation */ revents |= POLLIN; } if(revents & POLLIN){ s->read_wontblock=1; r=ssh_socket_unbuffered_read(s,buffer,sizeof(buffer)); if(r<0){ if(p != NULL) ssh_poll_set_events(p,ssh_poll_get_events(p) & ~POLLIN); if(s->callbacks && s->callbacks->exception){ s->callbacks->exception( SSH_SOCKET_EXCEPTION_ERROR, s->last_errno,s->callbacks->userdata); } } if(r==0){ ssh_poll_set_events(p,ssh_poll_get_events(p) & ~POLLIN); if(s->callbacks && s->callbacks->exception){ s->callbacks->exception( SSH_SOCKET_EXCEPTION_EOF, 0,s->callbacks->userdata); } } if(r>0){ /* Bufferize the data and then call the callback */ buffer_add_data(s->in_buffer,buffer,r); if(s->callbacks && s->callbacks->data){ r= s->callbacks->data(buffer_get_rest(s->in_buffer), buffer_get_rest_len(s->in_buffer), s->callbacks->userdata); buffer_pass_bytes(s->in_buffer,r); } } } #ifdef _WIN32 if(revents & POLLOUT || revents & POLLWRNORM){ #else if(revents & POLLOUT){ #endif /* First, POLLOUT is a sign we may be connected */ if(s->state == SSH_SOCKET_CONNECTING){ 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_in(s)); if(s->callbacks && s->callbacks->connected) s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,0,s->callbacks->userdata); return 0; } /* So, we can write data */ s->write_wontblock=1; ssh_poll_remove_events(p,POLLOUT); /* If buffered data is pending, write it */ if(buffer_get_rest_len(s->out_buffer) > 0){ ssh_socket_nonblocking_flush(s); } else if(s->callbacks && s->callbacks->controlflow){ /* Otherwise advertise the upper level that write can be done */ s->callbacks->controlflow(SSH_SOCKET_FLOW_WRITEWONTBLOCK,s->callbacks->userdata); } /* TODO: Find a way to put back POLLOUT when buffering occurs */ } /* Return -1 if one of the poll handlers disappeared */ return (s->poll_in == NULL || s->poll_out == NULL) ? -1 : 0; } /** @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 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_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 * \brief Deletes a socket object */ void ssh_socket_free(ssh_socket s){ if (s == NULL) { return; } ssh_socket_close(s); ssh_buffer_free(s->in_buffer); ssh_buffer_free(s->out_buffer); 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); fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd == SSH_INVALID_SOCKET) { ssh_set_error(s->session, SSH_FATAL, "Error from socket(AF_UNIX, SOCK_STREAM, 0): %s", strerror(errno)); return -1; } if (fcntl(fd, F_SETFD, 1) == -1) { ssh_set_error(s->session, SSH_FATAL, "Error from fcntl(fd, F_SETFD, 1): %s", strerror(errno)); close(fd); return -1; } if (connect(fd, (struct sockaddr *) &sunaddr, sizeof(sunaddr)) < 0) { ssh_set_error(s->session, SSH_FATAL, "Error from connect(): %s", strerror(errno)); close(fd); return -1; } ssh_socket_set_fd(s,fd); return 0; } #endif /** \internal * \brief closes a socket */ void ssh_socket_close(ssh_socket s){ if (ssh_socket_is_open(s)) { #ifdef _WIN32 closesocket(s->fd_in); /* fd_in = fd_out under win32 */ s->last_errno = WSAGetLastError(); #else 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_in = s->fd_out = SSH_INVALID_SOCKET; } 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; } #ifndef _WIN32 /* kill proxycommand child process */ if (s->proxycommand_pid > 0){ if (kill(s->proxycommand_pid, SIGTERM) == 0){ s->proxycommand_pid = 0; } } #endif } /** * @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_in = s->fd_out = fd; if(s->poll_in) ssh_poll_set_fd(s->poll_in,fd); }