/* {{{ libssh2_packet_requirev * Loops libssh2_packet_read() until one of a list of packet types requested is available * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout * packet_types is a null terminated list of packet_type numbers */ int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len) { if (libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0) == 0) { /* One of the packets listed was available in the packet brigade */ return 0; } while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) { int ret = libssh2_packet_read(session, 1); if (ret < 0) { return -1; } if (ret == 0) { continue; } if (strchr(packet_types, ret)) { /* Be lazy, let packet_ask pull it out of the brigade */ return libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0); } } /* Only reached if the socket died */ return -1; }
/* {{{ libssh2_packet_require * Loops libssh2_packet_read() until the packet requested is available * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout */ int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len) { if (libssh2_packet_ask_ex(session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0) == 0) { /* A packet was available in the packet brigade */ return 0; } #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet of type %d becomes available", (int)packet_type); #endif while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { int ret = libssh2_packet_read(session, 1); if (ret < 0) { return -1; } if (ret == 0) continue; if (packet_type == ret) { /* Be lazy, let packet_ask pull it out of the brigade */ return libssh2_packet_ask_ex(session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0); } } /* Only reached if the socket died */ return -1; }
/* {{{ libssh2_packet_ask * Scan the brigade for a matching packet type, optionally poll the socket for * a packet first */ int libssh2_packet_ask_ex(LIBSSH2_SESSION * session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket) { LIBSSH2_PACKET *packet = session->packets.head; if (poll_socket) { /* * XXX CHECK *** * When "poll_socket" is "1" libhss2_packet_read() can return * PACKET_EAGAIN. I am not sure what should happen, but internally * there is only one location that might do so, libssh2_packet_askv_ex() */ libssh2pack_t rc = libssh2_packet_read(session); if ((rc < 0) && !packet) { return rc; } } _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Looking for packet of type: %d", (int) packet_type); while (packet) { if (packet->data[0] == packet_type && (packet->data_len >= (match_ofs + match_len)) && (!match_buf || (memcmp (packet-> data + match_ofs, match_buf, match_len) == 0))) { *data = packet->data; *data_len = packet->data_len; if (packet->prev) { packet->prev->next = packet->next; } else { session->packets.head = packet->next; } if (packet->next) { packet->next->prev = packet->prev; } else { session->packets.tail = packet->prev; } LIBSSH2_FREE(session, packet); return 0; } packet = packet->next; } return -1; }
/* {{{ libssh2_packet_require * Loops libssh2_packet_read() until the packet requested is available * SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout * * Returns negative on error * Returns 0 when it has taken care of the requested packet. */ int libssh2_packet_require_ex(LIBSSH2_SESSION * session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, packet_require_state_t * state) { if (state->start == 0) { if (libssh2_packet_ask_ex (session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0) == 0) { /* A packet was available in the packet brigade */ return 0; } state->start = time(NULL); _libssh2_debug(session, LIBSSH2_DBG_TRANS, "May block until packet of type %d becomes available", (int) packet_type); } while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { libssh2pack_t ret = libssh2_packet_read(session); if (ret == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if ((ret == 0) && (!session->socket_block)) { /* If we are in non-blocking and there is no data, return that */ return PACKET_EAGAIN; } else if (ret < 0) { state->start = 0; /* an error which is not just because of blocking */ return ret; } else if (ret == packet_type) { /* Be lazy, let packet_ask pull it out of the brigade */ ret = libssh2_packet_ask_ex(session, packet_type, data, data_len, match_ofs, match_buf, match_len, 0); state->start = 0; return ret; } else if (ret == 0) { /* nothing available, wait until data arrives or we time out */ long left = LIBSSH2_READ_TIMEOUT - (time(NULL) - state->start); if ((left <= 0) || (libssh2_waitsocket(session, left) <= 0)) { state->start = 0; return PACKET_TIMEOUT; } } } /* Only reached if the socket died */ return -1; }
/* {{{ libssh2_packet_burn * Loops libssh2_packet_read() until any packet is available and promptly * discards it * Used during KEX exchange to discard badly guessed KEX_INIT packets */ int libssh2_packet_burn(LIBSSH2_SESSION * session, libssh2_nonblocking_states * state) { unsigned char *data; unsigned long data_len; unsigned char all_packets[255]; int i; int ret; if (*state == libssh2_NB_state_idle) { for(i = 1; i < 256; i++) { all_packets[i - 1] = i; } if (libssh2_packet_askv_ex (session, all_packets, &data, &data_len, 0, NULL, 0, 0) == 0) { i = data[0]; /* A packet was available in the packet brigade, burn it */ LIBSSH2_FREE(session, data); return i; } _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet becomes available to burn"); *state = libssh2_NB_state_created; } while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { if ((ret = libssh2_packet_read(session)) == PACKET_EAGAIN) { return PACKET_EAGAIN; } else if (ret < 0) { *state = libssh2_NB_state_idle; return ret; } else if (ret == 0) { /* FIXME: this might busyloop */ continue; } /* Be lazy, let packet_ask pull it out of the brigade */ if (0 == libssh2_packet_ask_ex(session, ret, &data, &data_len, 0, NULL, 0, 0)) { /* Smoke 'em if you got 'em */ LIBSSH2_FREE(session, data); *state = libssh2_NB_state_idle; return ret; } } /* Only reached if the socket died */ return -1; }
int libssh2_packet_requirev_ex(LIBSSH2_SESSION * session, const unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, packet_requirev_state_t * state) { if (libssh2_packet_askv_ex (session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0) == 0) { /* One of the packets listed was available in the packet brigade */ state->start = 0; return 0; } if (state->start == 0) { state->start = time(NULL); } while (session->socket_state != LIBSSH2_SOCKET_DISCONNECTED) { int ret = libssh2_packet_read(session); if ((ret < 0) && (ret != PACKET_EAGAIN)) { state->start = 0; return ret; } if (ret <= 0) { long left = LIBSSH2_READ_TIMEOUT - (time(NULL) - state->start); if ((left <= 0) || (libssh2_waitsocket(session, left) <= 0)) { state->start = 0; return PACKET_TIMEOUT; } else if (ret == PACKET_EAGAIN) { return PACKET_EAGAIN; } } if (strchr((char *) packet_types, ret)) { /* Be lazy, let packet_ask pull it out of the brigade */ return libssh2_packet_askv_ex(session, packet_types, data, data_len, match_ofs, match_buf, match_len, 0); } } /* Only reached if the socket died */ state->start = 0; return -1; }
/* {{{ libssh2_packet_ask * Scan the brigade for a matching packet type, optionally poll the socket for a packet first */ int libssh2_packet_ask_ex(LIBSSH2_SESSION *session, unsigned char packet_type, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len, int poll_socket) { LIBSSH2_PACKET *packet = session->packets.head; if (poll_socket) { if (libssh2_packet_read(session, 0) < 0) { return -1; } } #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Looking for packet of type: %d", (int)packet_type); #endif while (packet) { if (packet->data[0] == packet_type && (packet->data_len >= (match_ofs + match_len)) && (!match_buf || (memcmp(packet->data + match_ofs, match_buf, match_len) == 0))) { *data = packet->data; *data_len = packet->data_len; if (packet->prev) { packet->prev->next = packet->next; } else { session->packets.head = packet->next; } if (packet->next) { packet->next->prev = packet->prev; } else { session->packets.tail = packet->prev; } LIBSSH2_FREE(session, packet); return 0; } packet = packet->next; } return -1; }
/* {{{ libssh2_packet_burn * Loops libssh2_packet_read() until any packet is available and promptly discards it * Used during KEX exchange to discard badly guessed KEX_INIT packets */ int libssh2_packet_burn(LIBSSH2_SESSION *session) { unsigned char *data; unsigned long data_len; char all_packets[255]; int i; for(i = 1; i < 256; i++) all_packets[i - 1] = i; if (libssh2_packet_askv_ex(session, all_packets, &data, &data_len, 0, NULL, 0, 0) == 0) { i = data[0]; /* A packet was available in the packet brigade, burn it */ LIBSSH2_FREE(session, data); return i; } #ifdef LIBSSH2_DEBUG_TRANSPORT _libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet becomes available to burn"); #endif while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) { int ret = libssh2_packet_read(session, 1); if (ret < 0) { return -1; } if (ret == 0) continue; /* Be lazy, let packet_ask pull it out of the brigade */ if (0 == libssh2_packet_ask_ex(session, ret, &data, &data_len, 0, NULL, 0, 0)) { /* Smoke 'em if you got 'em */ LIBSSH2_FREE(session, data); return ret; } } /* Only reached if the socket died */ return -1; }
/* {{{ libssh2_poll * Poll sockets, channels, and listeners for activity */ LIBSSH2_API int libssh2_poll(LIBSSH2_POLLFD * fds, unsigned int nfds, long timeout) { long timeout_remaining; unsigned int i, active_fds; #ifdef HAVE_POLL LIBSSH2_SESSION *session = NULL; #ifdef HAVE_ALLOCA struct pollfd *sockets = alloca(sizeof(struct pollfd) * nfds); #else struct pollfd sockets[256]; if (nfds > 256) /* systems without alloca use a fixed-size array, this can be fixed if we really want to, at least if the compiler is a C99 capable one */ return -1; #endif /* Setup sockets for polling */ for(i = 0; i < nfds; i++) { fds[i].revents = 0; switch (fds[i].type) { case LIBSSH2_POLLFD_SOCKET: sockets[i].fd = fds[i].fd.socket; sockets[i].events = fds[i].events; sockets[i].revents = 0; break; case LIBSSH2_POLLFD_CHANNEL: sockets[i].fd = fds[i].fd.channel->session->socket_fd; sockets[i].events = POLLIN; sockets[i].revents = 0; if (!session) session = fds[i].fd.channel->session; break; case LIBSSH2_POLLFD_LISTENER: sockets[i].fd = fds[i].fd.listener->session->socket_fd; sockets[i].events = POLLIN; sockets[i].revents = 0; if (!session) session = fds[i].fd.listener->session; break; default: if (session) libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, "Invalid descriptor passed to libssh2_poll()", 0); return -1; } } #elif defined(HAVE_SELECT) LIBSSH2_SESSION *session = NULL; int maxfd = 0; fd_set rfds, wfds; struct timeval tv; FD_ZERO(&rfds); FD_ZERO(&wfds); for(i = 0; i < nfds; i++) { fds[i].revents = 0; switch (fds[i].type) { case LIBSSH2_POLLFD_SOCKET: if (fds[i].events & LIBSSH2_POLLFD_POLLIN) { FD_SET(fds[i].fd.socket, &rfds); if (fds[i].fd.socket > maxfd) maxfd = fds[i].fd.socket; } if (fds[i].events & LIBSSH2_POLLFD_POLLOUT) { FD_SET(fds[i].fd.socket, &wfds); if (fds[i].fd.socket > maxfd) maxfd = fds[i].fd.socket; } break; case LIBSSH2_POLLFD_CHANNEL: FD_SET(fds[i].fd.channel->session->socket_fd, &rfds); if (fds[i].fd.channel->session->socket_fd > maxfd) maxfd = fds[i].fd.channel->session->socket_fd; if (!session) session = fds[i].fd.channel->session; break; case LIBSSH2_POLLFD_LISTENER: FD_SET(fds[i].fd.listener->session->socket_fd, &rfds); if (fds[i].fd.listener->session->socket_fd > maxfd) maxfd = fds[i].fd.listener->session->socket_fd; if (!session) session = fds[i].fd.listener->session; break; default: if (session) libssh2_error(session, LIBSSH2_ERROR_INVALID_POLL_TYPE, "Invalid descriptor passed to libssh2_poll()", 0); return -1; } } #else /* No select() or poll() * no sockets sturcture to setup */ timeout = 0; #endif /* HAVE_POLL or HAVE_SELECT */ timeout_remaining = timeout; do { #if defined(HAVE_POLL) || defined(HAVE_SELECT) int sysret; #endif active_fds = 0; for(i = 0; i < nfds; i++) { if (fds[i].events != fds[i].revents) { switch (fds[i].type) { case LIBSSH2_POLLFD_CHANNEL: if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && /* Want to be ready for read */ ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { /* Not yet known to be ready for read */ fds[i].revents |= libssh2_poll_channel_read(fds[i].fd.channel, 0) ? LIBSSH2_POLLFD_POLLIN : 0; } if ((fds[i].events & LIBSSH2_POLLFD_POLLEXT) && /* Want to be ready for extended read */ ((fds[i].revents & LIBSSH2_POLLFD_POLLEXT) == 0)) { /* Not yet known to be ready for extended read */ fds[i].revents |= libssh2_poll_channel_read(fds[i].fd.channel, 1) ? LIBSSH2_POLLFD_POLLEXT : 0; } if ((fds[i].events & LIBSSH2_POLLFD_POLLOUT) && /* Want to be ready for write */ ((fds[i].revents & LIBSSH2_POLLFD_POLLOUT) == 0)) { /* Not yet known to be ready for write */ fds[i].revents |= libssh2_poll_channel_write(fds[i].fd. channel) ? LIBSSH2_POLLFD_POLLOUT : 0; } if (fds[i].fd.channel->remote.close || fds[i].fd.channel->local.close) { fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED; } if (fds[i].fd.channel->session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) { fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED; } break; case LIBSSH2_POLLFD_LISTENER: if ((fds[i].events & LIBSSH2_POLLFD_POLLIN) && /* Want a connection */ ((fds[i].revents & LIBSSH2_POLLFD_POLLIN) == 0)) { /* No connections known of yet */ fds[i].revents |= libssh2_poll_listener_queued(fds[i].fd. listener) ? LIBSSH2_POLLFD_POLLIN : 0; } if (fds[i].fd.listener->session->socket_state == LIBSSH2_SOCKET_DISCONNECTED) { fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED; } break; } } if (fds[i].revents) { active_fds++; } } if (active_fds) { /* Don't block on the sockets if we have channels/listeners which are ready */ timeout_remaining = 0; } #ifdef HAVE_POLL #ifdef HAVE_GETTIMEOFDAY { struct timeval tv_begin, tv_end; gettimeofday((struct timeval *) &tv_begin, NULL); sysret = poll(sockets, nfds, timeout_remaining); gettimeofday((struct timeval *) &tv_end, NULL); timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000; timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000; } #else /* If the platform doesn't support gettimeofday, * then just make the call non-blocking and walk away */ sysret = poll(sockets, nfds, timeout_remaining); timeout_remaining = 0; #endif /* HAVE_GETTIMEOFDAY */ if (sysret > 0) { for(i = 0; i < nfds; i++) { switch (fds[i].type) { case LIBSSH2_POLLFD_SOCKET: fds[i].revents = sockets[i].revents; sockets[i].revents = 0; /* In case we loop again, be nice */ if (fds[i].revents) { active_fds++; } break; case LIBSSH2_POLLFD_CHANNEL: if (sockets[i].events & POLLIN) { /* Spin session until no data available */ while (libssh2_packet_read(fds[i].fd.channel->session) > 0); } if (sockets[i].revents & POLLHUP) { fds[i].revents |= LIBSSH2_POLLFD_CHANNEL_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED; } sockets[i].revents = 0; break; case LIBSSH2_POLLFD_LISTENER: if (sockets[i].events & POLLIN) { /* Spin session until no data available */ while (libssh2_packet_read(fds[i].fd.listener->session) > 0); } if (sockets[i].revents & POLLHUP) { fds[i].revents |= LIBSSH2_POLLFD_LISTENER_CLOSED | LIBSSH2_POLLFD_SESSION_CLOSED; } sockets[i].revents = 0; break; } } } #elif defined(HAVE_SELECT) tv.tv_sec = timeout_remaining / 1000; tv.tv_usec = (timeout_remaining % 1000) * 1000; #ifdef HAVE_GETTIMEOFDAY { struct timeval tv_begin, tv_end; gettimeofday((struct timeval *) &tv_begin, NULL); sysret = select(maxfd, &rfds, &wfds, NULL, &tv); gettimeofday((struct timeval *) &tv_end, NULL); timeout_remaining -= (tv_end.tv_sec - tv_begin.tv_sec) * 1000; timeout_remaining -= (tv_end.tv_usec - tv_begin.tv_usec) / 1000; } #else /* If the platform doesn't support gettimeofday, * then just make the call non-blocking and walk away */ sysret = select(maxfd, &rfds, &wfds, NULL, &tv); timeout_remaining = 0; #endif if (sysret > 0) { for(i = 0; i < nfds; i++) { switch (fds[i].type) { case LIBSSH2_POLLFD_SOCKET: if (FD_ISSET(fds[i].fd.socket, &rfds)) { fds[i].revents |= LIBSSH2_POLLFD_POLLIN; } if (FD_ISSET(fds[i].fd.socket, &wfds)) { fds[i].revents |= LIBSSH2_POLLFD_POLLOUT; } if (fds[i].revents) { active_fds++; } break; case LIBSSH2_POLLFD_CHANNEL: if (FD_ISSET(fds[i].fd.channel->session->socket_fd, &rfds)) { /* Spin session until no data available */ while (libssh2_packet_read(fds[i].fd.channel->session) > 0); } break; case LIBSSH2_POLLFD_LISTENER: if (FD_ISSET (fds[i].fd.listener->session->socket_fd, &rfds)) { /* Spin session until no data available */ while (libssh2_packet_read(fds[i].fd.listener->session) > 0); } break; } } } #endif /* else no select() or poll() -- timeout (and by extension timeout_remaining) will be equal to 0 */ } while ((timeout_remaining > 0) && !active_fds); return active_fds; }