void session_loop(void(*loophandler)()) { fd_set readfd, writefd; struct timeval timeout; int val; /* main loop, select()s for all sockets in use */ for(;;) { const int writequeue_has_space = (ses.writequeue_len <= 2*TRANS_MAX_PAYLOAD_LEN); timeout.tv_sec = select_timeout(); timeout.tv_usec = 0; FD_ZERO(&writefd); FD_ZERO(&readfd); dropbear_assert(ses.payload == NULL); /* We get woken up when signal handlers write to this pipe. SIGCHLD in svr-chansession is the only one currently. */ FD_SET(ses.signal_pipe[0], &readfd); ses.channel_signal_pending = 0; /* set up for channels which can be read/written */ setchannelfds(&readfd, &writefd, writequeue_has_space); /* Pending connections to test */ set_connect_fds(&writefd); /* We delay reading from the input socket during initial setup until after we have written out our initial KEXINIT packet (empty writequeue). This means our initial packet can be in-flight while we're doing a blocking read for the remote ident. We also avoid reading from the socket if the writequeue is full, that avoids replies backing up */ if (ses.sock_in != -1 && (ses.remoteident || isempty(&ses.writequeue)) && writequeue_has_space) { FD_SET(ses.sock_in, &readfd); } /* Ordering is important, this test must occur after any other function might have queued packets (such as connection handlers) */ if (ses.sock_out != -1 && !isempty(&ses.writequeue)) { FD_SET(ses.sock_out, &writefd); } val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); if (exitflag) { dropbear_exit("Terminated by signal"); } if (val < 0 && errno != EINTR) { dropbear_exit("Error in select"); } if (val <= 0) { /* If we were interrupted or the select timed out, we still * want to iterate over channels etc for reading, to handle * server processes exiting etc. * We don't want to read/write FDs. */ FD_ZERO(&writefd); FD_ZERO(&readfd); } /* We'll just empty out the pipe if required. We don't do any thing with the data, since the pipe's purpose is purely to wake up the select() above. */ if (FD_ISSET(ses.signal_pipe[0], &readfd)) { char x; TRACE(("signal pipe set")) while (read(ses.signal_pipe[0], &x, 1) > 0) {} ses.channel_signal_pending = 1; } /* check for auth timeout, rekeying required etc */ checktimeouts(); /* process session socket's incoming data */ if (ses.sock_in != -1) { if (FD_ISSET(ses.sock_in, &readfd)) { if (!ses.remoteident) { /* blocking read of the version string */ read_session_identification(); } else { read_packet(); } } /* Process the decrypted packet. After this, the read buffer * will be ready for a new packet */ if (ses.payload != NULL) { process_packet(); } } /* if required, flush out any queued reply packets that were being held up during a KEX */ maybe_flush_reply_queue(); handle_connect_fds(&writefd); /* process pipes etc for the channels, ses.dataallowed == 0 * during rekeying ) */ channelio(&readfd, &writefd); /* process session socket's outgoing data */ if (ses.sock_out != -1) { if (!isempty(&ses.writequeue)) { write_packet(); } } if (loophandler) { loophandler(); } } /* for(;;) */
void child_session(int sock, runopts *opts, int childpipe, struct sockaddr *remoteaddr) { fd_set readfd, writefd; struct timeval timeout; int val; crypto_init(); session_init(sock, opts, childpipe, remoteaddr); /* exchange identification, version etc */ session_identification(); seedrandom(); /* start off with key exchange */ send_msg_kexinit(); FD_ZERO(&readfd); FD_ZERO(&writefd); /* main loop, select()s for all sockets in use */ for(;;) { timeout.tv_sec = SELECT_TIMEOUT; timeout.tv_usec = 0; FD_ZERO(&writefd); FD_ZERO(&readfd); assert(ses.payload == NULL); if (ses.sock != -1) { FD_SET(ses.sock, &readfd); if (!isempty(&ses.writequeue)) { FD_SET(ses.sock, &writefd); } } /* set up for channels which require reading/writing */ if (ses.dataallowed) { setchannelfds(&readfd, &writefd); } val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); if (exitflag) { dropbear_exit("Terminated by signal"); } if (val < 0) { if (errno == EINTR) { continue; } else { dropbear_exit("Error in select"); } } /* check for auth timeout, rekeying required etc */ checktimeouts(); if (val == 0) { /* timeout */ TRACE(("select timeout")); continue; } /* process session socket's incoming/outgoing data */ if (ses.sock != -1) { if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) { write_packet(); } if (FD_ISSET(ses.sock, &readfd)) { read_packet(); } /* Process the decrypted packet. After this, the read buffer * will be ready for a new packet */ if (ses.payload != NULL) { process_packet(); } } /* process pipes etc for the channels, ses.dataallowed == 0 * during rekeying ) */ if (ses.dataallowed) { channelio(&readfd, &writefd); } } /* for(;;) */ }
void session_loop(void(*loophandler)()) { fd_set readfd, writefd; struct timeval timeout; int val; /* main loop, select()s for all sockets in use */ for(;;) { timeout.tv_sec = select_timeout(); timeout.tv_usec = 0; FD_ZERO(&writefd); FD_ZERO(&readfd); dropbear_assert(ses.payload == NULL); if (ses.sock_in != -1) { FD_SET(ses.sock_in, &readfd); } if (ses.sock_out != -1 && !isempty(&ses.writequeue)) { FD_SET(ses.sock_out, &writefd); } /* We get woken up when signal handlers write to this pipe. SIGCHLD in svr-chansession is the only one currently. */ FD_SET(ses.signal_pipe[0], &readfd); /* set up for channels which require reading/writing */ if (ses.dataallowed) { setchannelfds(&readfd, &writefd); } val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout); if (exitflag) { dropbear_exit("Terminated by signal"); } if (val < 0 && errno != EINTR) { dropbear_exit("Error in select"); } if (val <= 0) { /* If we were interrupted or the select timed out, we still * want to iterate over channels etc for reading, to handle * server processes exiting etc. * We don't want to read/write FDs. */ FD_ZERO(&writefd); FD_ZERO(&readfd); } /* We'll just empty out the pipe if required. We don't do any thing with the data, since the pipe's purpose is purely to wake up the select() above. */ if (FD_ISSET(ses.signal_pipe[0], &readfd)) { char x; while (read(ses.signal_pipe[0], &x, 1) > 0) {} } /* check for auth timeout, rekeying required etc */ checktimeouts(); /* process session socket's incoming/outgoing data */ if (ses.sock_out != -1) { if (FD_ISSET(ses.sock_out, &writefd) && !isempty(&ses.writequeue)) { write_packet(); } } if (ses.sock_in != -1) { if (FD_ISSET(ses.sock_in, &readfd)) { read_packet(); } /* Process the decrypted packet. After this, the read buffer * will be ready for a new packet */ if (ses.payload != NULL) { process_packet(); } } /* if required, flush out any queued reply packets that were being held up during a KEX */ maybe_flush_reply_queue(); /* process pipes etc for the channels, ses.dataallowed == 0 * during rekeying ) */ if (ses.dataallowed) { channelio(&readfd, &writefd); } if (loophandler) { loophandler(); } } /* for(;;) */ /* Not reached */ }