Exemplo n.º 1
0
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);
	}
}
Exemplo n.º 2
0
/* 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 {
Exemplo n.º 3
0
/* 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"))
}
Exemplo n.º 4
0
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;
}
Exemplo n.º 5
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.º 6
0
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;
	}
}
Exemplo n.º 7
0
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 */
}