Exemplo n.º 1
0
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(;;) */
}
Exemplo n.º 2
0
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(;;) */
Exemplo n.º 3
0
/* returns the length including null-terminating zero on success,
 * or -1 on failure */
static int ident_readln(int fd, char* buf, int count) {
	
	char in;
	int pos = 0;
	int num = 0;
	fd_set fds;
	struct timeval timeout;

	TRACE(("enter ident_readln"));

	if (count < 1) {
		return -1;
	}

	FD_ZERO(&fds);

	/* select since it's a non-blocking fd */
	
	/* leave space to null-terminate */
	while (pos < count-1) {

		FD_SET(fd, &fds);

		timeout.tv_sec = 1;
		timeout.tv_usec = 0;
		if (select(fd+1, &fds, NULL, NULL, &timeout) < 0) {
			if (errno == EINTR) {
				continue;
			}
			TRACE(("leave ident_readln: select error"));
			return -1;
		}

		checktimeouts();
		
		/* Have to go one byte at a time, since we don't want to read past
		 * the end, and have to somehow shove bytes back into the normal
		 * packet reader */
		if (FD_ISSET(fd, &fds)) {
			num = read(fd, &in, 1);
			/* a "\n" is a newline, "\r" we want to read in and keep going
			 * so that it won't be read as part of the next line */
			if (num < 0) {
				/* error */
				if (errno == EINTR) {
					continue; /* not a real error */
				}
				TRACE(("leave ident_readln: read error"));
				return -1;
			}
			if (num == 0) {
				/* EOF */
				TRACE(("leave ident_readln: EOF"));
				return -1;
			}
			if (in == '\n') {
				/* end of ident string */
				break;
			}
			/* we don't want to include '\r's */
			if (in != '\r') {
				buf[pos] = in;
				pos++;
			}
		}
	}

	buf[pos] = '\0';
	TRACE(("leave ident_readln: return %d", pos+1));
	return pos+1;
}
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 */
}