static void tcp_acceptor(struct Listener *listener, int sock) { int fd; struct sockaddr_storage addr; socklen_t len; char ipstring[NI_MAXHOST], portstring[NI_MAXSERV]; struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata); len = sizeof(addr); fd = accept(sock, (struct sockaddr*)&addr, &len); if (fd < 0) { return; } if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring), portstring, sizeof(portstring), NI_NUMERICHOST | NI_NUMERICSERV) != 0) { m_close(fd); return; } if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) { char* addr = NULL; unsigned int port = 0; if (tcpinfo->tcp_type == direct) { /* "direct-tcpip" */ /* host to connect, port to connect */ addr = tcpinfo->sendaddr; port = tcpinfo->sendport; } else { dropbear_assert(tcpinfo->tcp_type == forwarded); /* "forwarded-tcpip" */ /* address that was connected, port that was connected */ addr = tcpinfo->request_listenaddr; port = tcpinfo->listenport; } if (addr == NULL) { addr = "localhost"; } buf_putstring(ses.writepayload, addr, strlen(addr)); buf_putint(ses.writepayload, port); /* originator ip */ buf_putstring(ses.writepayload, ipstring, strlen(ipstring)); /* originator port */ buf_putint(ses.writepayload, atol(portstring)); encrypt_packet(); } else { /* XXX debug? */ close(fd); } }
/* non-blocking function writing out a current encrypted packet */ void write_packet() { int len, written; buffer * writebuf = NULL; time_t now; unsigned packet_type; int all_ignore = 1; #ifdef HAVE_WRITEV struct iovec *iov = NULL; int i; struct Link *l; #endif TRACE2(("enter write_packet")) dropbear_assert(!isempty(&ses.writequeue)); #ifdef HAVE_WRITEV iov = m_malloc(sizeof(*iov) * ses.writequeue.count); for (l = ses.writequeue.head, i = 0; l; l = l->link, i++) { writebuf = (buffer*)l->item; packet_type = writebuf->data[writebuf->len-1]; len = writebuf->len - 1 - writebuf->pos; dropbear_assert(len > 0); all_ignore &= (packet_type == SSH_MSG_IGNORE); TRACE2(("write_packet writev #%d type %d len %d/%d", i, packet_type, len, writebuf->len-1)) iov[i].iov_base = buf_getptr(writebuf, len); iov[i].iov_len = len; } written = writev(ses.sock_out, iov, ses.writequeue.count); if (written < 0) { if (errno == EINTR) { m_free(iov); TRACE2(("leave writepacket: EINTR")) return; } else {
/* for our purposes we only need positive (or 0) numbers, so will * fail if we get negative numbers */ void buf_putmpint(buffer* buf, mp_int * mp) { unsigned int len, pad = 0; TRACE2(("enter buf_putmpint")) dropbear_assert(mp != NULL); if (SIGN(mp) == MP_NEG) { dropbear_exit("negative bignum"); } /* zero check */ if (USED(mp) == 1 && DIGIT(mp, 0) == 0) { len = 0; } else { /* SSH spec requires padding for mpints with the MSB set, this code * implements it */ len = mp_count_bits(mp); /* if the top bit of MSB is set, we need to pad */ pad = (len%8 == 0) ? 1 : 0; len = len / 8 + 1; /* don't worry about rounding, we need it for padding anyway when len%8 == 0 */ } /* store the length */ buf_putint(buf, len); /* store the actual value */ if (len > 0) { if (pad) { buf_putbyte(buf, 0x00); } if (mp_to_unsigned_bin(mp, buf_getwriteptr(buf, len-pad)) != MP_OKAY) { dropbear_exit("mpint error"); } buf_incrwritepos(buf, len-pad); } TRACE2(("leave buf_putmpint")) }
void* dequeue(struct Queue* queue) { void* ret; struct Link* oldhead; dropbear_assert(!isempty(queue)); ret = queue->head->item; oldhead = queue->head; if (oldhead->link != NULL) { queue->head = oldhead->link; } else { queue->head = NULL; queue->tail = NULL; TRACE(("empty queue dequeing")) } m_free(oldhead); queue->count--; return ret; }
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(;;) */
static void connect_try_next(struct dropbear_progress_connection *c) { struct addrinfo *r; int res = 0; int fastopen = 0; #ifdef DROPBEAR_CLIENT_TCP_FAST_OPEN struct msghdr message; #endif for (r = c->res_iter; r; r = r->ai_next) { dropbear_assert(c->sock == -1); c->sock = socket(r->ai_family, r->ai_socktype, r->ai_protocol); if (c->sock < 0) { continue; } ses.maxfd = MAX(ses.maxfd, c->sock); set_sock_nodelay(c->sock); setnonblocking(c->sock); #ifdef DROPBEAR_CLIENT_TCP_FAST_OPEN fastopen = (c->writequeue != NULL); if (fastopen) { memset(&message, 0x0, sizeof(message)); message.msg_name = r->ai_addr; message.msg_namelen = r->ai_addrlen; /* 6 is arbitrary, enough to hold initial packets */ unsigned int iovlen = 6; /* Linux msg_iovlen is a size_t */ struct iovec iov[6]; packet_queue_to_iovec(c->writequeue, iov, &iovlen); message.msg_iov = iov; message.msg_iovlen = iovlen; res = sendmsg(c->sock, &message, MSG_FASTOPEN); /* Returns EINPROGRESS if FASTOPEN wasn't available */ if (res < 0) { if (errno != EINPROGRESS) { m_free(c->errstring); c->errstring = m_strdup(strerror(errno)); /* Not entirely sure which kind of errors are normal - 2.6.32 seems to return EPIPE for any (nonblocking?) sendmsg(). just fall back */ TRACE(("sendmsg tcp_fastopen failed, falling back. %s", strerror(errno))); /* No kernel MSG_FASTOPEN support. Fall back below */ fastopen = 0; /* Set to NULL to avoid trying again */ c->writequeue = NULL; } } else { packet_queue_consume(c->writequeue, res); } } #endif /* Normal connect(), used as fallback for TCP fastopen too */ if (!fastopen) { res = connect(c->sock, r->ai_addr, r->ai_addrlen); } if (res < 0 && errno != EINPROGRESS) { /* failure */ m_free(c->errstring); c->errstring = m_strdup(strerror(errno)); close(c->sock); c->sock = -1; continue; } else { /* new connection was successful, wait for it to complete */ break; } } if (r) { c->res_iter = r->ai_next; } else { c->res_iter = NULL; } }
void *examine(struct Queue* queue) { dropbear_assert(!isempty(queue)); return queue->head->item; }
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 */ }