summaryrefslogtreecommitdiffhomepage
path: root/common-session.c
diff options
context:
space:
mode:
authorMatt Johnston <matt@ucc.asn.au>2008-09-14 06:47:51 +0000
committerMatt Johnston <matt@ucc.asn.au>2008-09-14 06:47:51 +0000
commit1c72a35ddb79eede31657a450b8ba35aed24c79e (patch)
treee6f6ae8bac919e2696678d7a680dce410a1ea91a /common-session.c
parentcdbe853595d1ba06be4127d86c60a9bc2e9e3545 (diff)
parent460bf4382257a262fda862f66d6fe97c749f5bb7 (diff)
propagate from branch 'au.asn.ucc.matt.dropbear' (head f21045c791002d81fc6b8dde6537ea481e513eb2)
to branch 'au.asn.ucc.matt.dropbear.dbclient-netcat-alike' (head d1f69334581dc4c35f9ca16aa5355074c9dd315d) --HG-- branch : dbclient-netcat-alike extra : convert_revision : 22bbe895accc3995b48f07b556e45d546ff1ce5d
Diffstat (limited to 'common-session.c')
-rw-r--r--common-session.c108
1 files changed, 74 insertions, 34 deletions
diff --git a/common-session.c b/common-session.c
index 4c15391..79313f2 100644
--- a/common-session.c
+++ b/common-session.c
@@ -34,8 +34,10 @@
#include "kex.h"
#include "channel.h"
#include "atomicio.h"
+#include "runopts.h"
static void checktimeouts();
+static long select_timeout();
static int ident_readln(int fd, char* buf, int count);
struct sshsession ses; /* GLOBAL */
@@ -59,11 +61,18 @@ void common_session_init(int sock, char* remotehost) {
ses.sock = sock;
ses.maxfd = sock;
- ses.connecttimeout = 0;
+ ses.connect_time = 0;
+ ses.last_packet_time = 0;
+
+ if (pipe(ses.signal_pipe) < 0) {
+ dropbear_exit("signal pipe failed");
+ }
+ setnonblocking(ses.signal_pipe[0]);
+ setnonblocking(ses.signal_pipe[1]);
kexfirstinitialise(); /* initialise the kex state */
- ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN);
+ ses.writepayload = buf_new(TRANS_MAX_PAYLOAD_LEN);
ses.transseq = 0;
ses.readbuf = NULL;
@@ -74,9 +83,12 @@ void common_session_init(int sock, char* remotehost) {
initqueue(&ses.writequeue);
ses.requirenext = SSH_MSG_KEXINIT;
- ses.dataallowed = 0; /* don't send data yet, we'll wait until after kex */
+ ses.dataallowed = 1; /* we can send data until we actually
+ send the SSH_MSG_KEXINIT */
ses.ignorenext = 0;
ses.lastpacket = 0;
+ ses.reply_queue_head = NULL;
+ ses.reply_queue_tail = NULL;
/* set all the algos to none */
ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context));
@@ -108,7 +120,6 @@ void common_session_init(int sock, char* remotehost) {
ses.allowprivport = 0;
-
TRACE(("leave session_init"))
}
@@ -121,7 +132,7 @@ void session_loop(void(*loophandler)()) {
/* main loop, select()s for all sockets in use */
for(;;) {
- timeout.tv_sec = SELECT_TIMEOUT;
+ timeout.tv_sec = select_timeout();
timeout.tv_usec = 0;
FD_ZERO(&writefd);
FD_ZERO(&readfd);
@@ -132,6 +143,10 @@ void session_loop(void(*loophandler)()) {
FD_SET(ses.sock, &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) {
@@ -143,28 +158,30 @@ void session_loop(void(*loophandler)()) {
dropbear_exit("Terminated by signal");
}
- if (val < 0) {
- if (errno == EINTR) {
- /* This must happen even if we've been interrupted, so that
- * changed signal-handler vars can take effect etc */
- if (loophandler) {
- loophandler();
- }
- continue;
- } else {
- dropbear_exit("Error in select");
- }
+ if (val < 0 && errno != EINTR) {
+ dropbear_exit("Error in select");
}
- /* check for auth timeout, rekeying required etc */
- checktimeouts();
+ 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);
+ }
- if (val == 0) {
- /* timeout */
- TRACE(("select timeout"))
- continue;
+ /* 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 != -1) {
if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
@@ -181,6 +198,10 @@ void session_loop(void(*loophandler)()) {
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 ) */
@@ -229,7 +250,7 @@ void session_identification() {
/* write our version string, this blocks */
if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n",
strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) {
- dropbear_exit("Error writing ident string");
+ ses.remoteclosed();
}
/* If they send more than 50 lines, something is wrong */
@@ -250,7 +271,7 @@ void session_identification() {
if (!done) {
TRACE(("err: %s for '%s'\n", strerror(errno), linebuf))
- dropbear_exit("Failed to get remote version");
+ ses.remoteclosed();
} else {
/* linebuf is already null terminated */
ses.remoteident = m_malloc(len);
@@ -341,20 +362,22 @@ static int ident_readln(int fd, char* buf, int count) {
return pos+1;
}
+void send_msg_ignore() {
+ CHECKCLEARTOWRITE();
+ buf_putbyte(ses.writepayload, SSH_MSG_IGNORE);
+ buf_putstring(ses.writepayload, "", 0);
+ encrypt_packet();
+}
+
/* Check all timeouts which are required. Currently these are the time for
* user authentication, and the automatic rekeying. */
static void checktimeouts() {
- struct timeval tv;
- long secs;
+ time_t now;
- if (gettimeofday(&tv, 0) < 0) {
- dropbear_exit("Error getting time");
- }
-
- secs = tv.tv_sec;
+ now = time(NULL);
- if (ses.connecttimeout != 0 && secs > ses.connecttimeout) {
+ if (ses.connect_time != 0 && now - ses.connect_time >= AUTH_TIMEOUT) {
dropbear_close("Timeout before auth");
}
@@ -364,10 +387,27 @@ static void checktimeouts() {
}
if (!ses.kexstate.sentkexinit
- && (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
- || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){
+ && (now - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
+ || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)) {
TRACE(("rekeying after timeout or max data reached"))
send_msg_kexinit();
}
+
+ if (opts.keepalive_secs > 0
+ && now - ses.last_packet_time >= opts.keepalive_secs) {
+ send_msg_ignore();
+ }
}
+static long select_timeout() {
+ /* determine the minimum timeout that might be required, so
+ as to avoid waking when unneccessary */
+ long ret = LONG_MAX;
+ if (KEX_REKEY_TIMEOUT > 0)
+ ret = MIN(KEX_REKEY_TIMEOUT, ret);
+ if (AUTH_TIMEOUT > 0)
+ ret = MIN(AUTH_TIMEOUT, ret);
+ if (opts.keepalive_secs > 0)
+ ret = MIN(opts.keepalive_secs, ret);
+ return ret;
+}