comparison common-session.c @ 478:d4f32c3443ac dbclient-netcat-alike

propagate from branch 'au.asn.ucc.matt.dropbear' (head f21045c791002d81fc6b8dde6537ea481e513eb2) to branch 'au.asn.ucc.matt.dropbear.dbclient-netcat-alike' (head d1f69334581dc4c35f9ca16aa5355074c9dd315d)
author Matt Johnston <matt@ucc.asn.au>
date Sun, 14 Sep 2008 06:47:51 +0000
parents 7e43f5e473b9
children e3db1f7a2e43
comparison
equal deleted inserted replaced
296:6b41e2cbf071 478:d4f32c3443ac
32 #include "ssh.h" 32 #include "ssh.h"
33 #include "random.h" 33 #include "random.h"
34 #include "kex.h" 34 #include "kex.h"
35 #include "channel.h" 35 #include "channel.h"
36 #include "atomicio.h" 36 #include "atomicio.h"
37 #include "runopts.h"
37 38
38 static void checktimeouts(); 39 static void checktimeouts();
40 static long select_timeout();
39 static int ident_readln(int fd, char* buf, int count); 41 static int ident_readln(int fd, char* buf, int count);
40 42
41 struct sshsession ses; /* GLOBAL */ 43 struct sshsession ses; /* GLOBAL */
42 44
43 /* need to know if the session struct has been initialised, this way isn't the 45 /* need to know if the session struct has been initialised, this way isn't the
57 ses.remotehost = remotehost; 59 ses.remotehost = remotehost;
58 60
59 ses.sock = sock; 61 ses.sock = sock;
60 ses.maxfd = sock; 62 ses.maxfd = sock;
61 63
62 ses.connecttimeout = 0; 64 ses.connect_time = 0;
65 ses.last_packet_time = 0;
66
67 if (pipe(ses.signal_pipe) < 0) {
68 dropbear_exit("signal pipe failed");
69 }
70 setnonblocking(ses.signal_pipe[0]);
71 setnonblocking(ses.signal_pipe[1]);
63 72
64 kexfirstinitialise(); /* initialise the kex state */ 73 kexfirstinitialise(); /* initialise the kex state */
65 74
66 ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN); 75 ses.writepayload = buf_new(TRANS_MAX_PAYLOAD_LEN);
67 ses.transseq = 0; 76 ses.transseq = 0;
68 77
69 ses.readbuf = NULL; 78 ses.readbuf = NULL;
70 ses.decryptreadbuf = NULL; 79 ses.decryptreadbuf = NULL;
71 ses.payload = NULL; 80 ses.payload = NULL;
72 ses.recvseq = 0; 81 ses.recvseq = 0;
73 82
74 initqueue(&ses.writequeue); 83 initqueue(&ses.writequeue);
75 84
76 ses.requirenext = SSH_MSG_KEXINIT; 85 ses.requirenext = SSH_MSG_KEXINIT;
77 ses.dataallowed = 0; /* don't send data yet, we'll wait until after kex */ 86 ses.dataallowed = 1; /* we can send data until we actually
87 send the SSH_MSG_KEXINIT */
78 ses.ignorenext = 0; 88 ses.ignorenext = 0;
79 ses.lastpacket = 0; 89 ses.lastpacket = 0;
90 ses.reply_queue_head = NULL;
91 ses.reply_queue_tail = NULL;
80 92
81 /* set all the algos to none */ 93 /* set all the algos to none */
82 ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context)); 94 ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context));
83 ses.newkeys = NULL; 95 ses.newkeys = NULL;
84 ses.keys->recv_algo_crypt = &dropbear_nocipher; 96 ses.keys->recv_algo_crypt = &dropbear_nocipher;
106 118
107 ses.chantypes = NULL; 119 ses.chantypes = NULL;
108 120
109 ses.allowprivport = 0; 121 ses.allowprivport = 0;
110 122
111
112 TRACE(("leave session_init")) 123 TRACE(("leave session_init"))
113 } 124 }
114 125
115 void session_loop(void(*loophandler)()) { 126 void session_loop(void(*loophandler)()) {
116 127
119 int val; 130 int val;
120 131
121 /* main loop, select()s for all sockets in use */ 132 /* main loop, select()s for all sockets in use */
122 for(;;) { 133 for(;;) {
123 134
124 timeout.tv_sec = SELECT_TIMEOUT; 135 timeout.tv_sec = select_timeout();
125 timeout.tv_usec = 0; 136 timeout.tv_usec = 0;
126 FD_ZERO(&writefd); 137 FD_ZERO(&writefd);
127 FD_ZERO(&readfd); 138 FD_ZERO(&readfd);
128 dropbear_assert(ses.payload == NULL); 139 dropbear_assert(ses.payload == NULL);
129 if (ses.sock != -1) { 140 if (ses.sock != -1) {
130 FD_SET(ses.sock, &readfd); 141 FD_SET(ses.sock, &readfd);
131 if (!isempty(&ses.writequeue)) { 142 if (!isempty(&ses.writequeue)) {
132 FD_SET(ses.sock, &writefd); 143 FD_SET(ses.sock, &writefd);
133 } 144 }
134 } 145 }
146
147 /* We get woken up when signal handlers write to this pipe.
148 SIGCHLD in svr-chansession is the only one currently. */
149 FD_SET(ses.signal_pipe[0], &readfd);
135 150
136 /* set up for channels which require reading/writing */ 151 /* set up for channels which require reading/writing */
137 if (ses.dataallowed) { 152 if (ses.dataallowed) {
138 setchannelfds(&readfd, &writefd); 153 setchannelfds(&readfd, &writefd);
139 } 154 }
141 156
142 if (exitflag) { 157 if (exitflag) {
143 dropbear_exit("Terminated by signal"); 158 dropbear_exit("Terminated by signal");
144 } 159 }
145 160
146 if (val < 0) { 161 if (val < 0 && errno != EINTR) {
147 if (errno == EINTR) { 162 dropbear_exit("Error in select");
148 /* This must happen even if we've been interrupted, so that 163 }
149 * changed signal-handler vars can take effect etc */ 164
150 if (loophandler) { 165 if (val <= 0) {
151 loophandler(); 166 /* If we were interrupted or the select timed out, we still
152 } 167 * want to iterate over channels etc for reading, to handle
153 continue; 168 * server processes exiting etc.
154 } else { 169 * We don't want to read/write FDs. */
155 dropbear_exit("Error in select"); 170 FD_ZERO(&writefd);
156 } 171 FD_ZERO(&readfd);
172 }
173
174 /* We'll just empty out the pipe if required. We don't do
175 any thing with the data, since the pipe's purpose is purely to
176 wake up the select() above. */
177 if (FD_ISSET(ses.signal_pipe[0], &readfd)) {
178 char x;
179 while (read(ses.signal_pipe[0], &x, 1) > 0) {}
157 } 180 }
158 181
159 /* check for auth timeout, rekeying required etc */ 182 /* check for auth timeout, rekeying required etc */
160 checktimeouts(); 183 checktimeouts();
161
162 if (val == 0) {
163 /* timeout */
164 TRACE(("select timeout"))
165 continue;
166 }
167 184
168 /* process session socket's incoming/outgoing data */ 185 /* process session socket's incoming/outgoing data */
169 if (ses.sock != -1) { 186 if (ses.sock != -1) {
170 if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) { 187 if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
171 write_packet(); 188 write_packet();
179 * will be ready for a new packet */ 196 * will be ready for a new packet */
180 if (ses.payload != NULL) { 197 if (ses.payload != NULL) {
181 process_packet(); 198 process_packet();
182 } 199 }
183 } 200 }
201
202 /* if required, flush out any queued reply packets that
203 were being held up during a KEX */
204 maybe_flush_reply_queue();
184 205
185 /* process pipes etc for the channels, ses.dataallowed == 0 206 /* process pipes etc for the channels, ses.dataallowed == 0
186 * during rekeying ) */ 207 * during rekeying ) */
187 if (ses.dataallowed) { 208 if (ses.dataallowed) {
188 channelio(&readfd, &writefd); 209 channelio(&readfd, &writefd);
227 int i; 248 int i;
228 249
229 /* write our version string, this blocks */ 250 /* write our version string, this blocks */
230 if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n", 251 if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n",
231 strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) { 252 strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) {
232 dropbear_exit("Error writing ident string"); 253 ses.remoteclosed();
233 } 254 }
234 255
235 /* If they send more than 50 lines, something is wrong */ 256 /* If they send more than 50 lines, something is wrong */
236 for (i = 0; i < 50; i++) { 257 for (i = 0; i < 50; i++) {
237 len = ident_readln(ses.sock, linebuf, sizeof(linebuf)); 258 len = ident_readln(ses.sock, linebuf, sizeof(linebuf));
248 } 269 }
249 } 270 }
250 271
251 if (!done) { 272 if (!done) {
252 TRACE(("err: %s for '%s'\n", strerror(errno), linebuf)) 273 TRACE(("err: %s for '%s'\n", strerror(errno), linebuf))
253 dropbear_exit("Failed to get remote version"); 274 ses.remoteclosed();
254 } else { 275 } else {
255 /* linebuf is already null terminated */ 276 /* linebuf is already null terminated */
256 ses.remoteident = m_malloc(len); 277 ses.remoteident = m_malloc(len);
257 memcpy(ses.remoteident, linebuf, len); 278 memcpy(ses.remoteident, linebuf, len);
258 } 279 }
339 buf[pos] = '\0'; 360 buf[pos] = '\0';
340 TRACE(("leave ident_readln: return %d", pos+1)) 361 TRACE(("leave ident_readln: return %d", pos+1))
341 return pos+1; 362 return pos+1;
342 } 363 }
343 364
365 void send_msg_ignore() {
366 CHECKCLEARTOWRITE();
367 buf_putbyte(ses.writepayload, SSH_MSG_IGNORE);
368 buf_putstring(ses.writepayload, "", 0);
369 encrypt_packet();
370 }
371
344 /* Check all timeouts which are required. Currently these are the time for 372 /* Check all timeouts which are required. Currently these are the time for
345 * user authentication, and the automatic rekeying. */ 373 * user authentication, and the automatic rekeying. */
346 static void checktimeouts() { 374 static void checktimeouts() {
347 375
348 struct timeval tv; 376 time_t now;
349 long secs; 377
350 378 now = time(NULL);
351 if (gettimeofday(&tv, 0) < 0) { 379
352 dropbear_exit("Error getting time"); 380 if (ses.connect_time != 0 && now - ses.connect_time >= AUTH_TIMEOUT) {
353 }
354
355 secs = tv.tv_sec;
356
357 if (ses.connecttimeout != 0 && secs > ses.connecttimeout) {
358 dropbear_close("Timeout before auth"); 381 dropbear_close("Timeout before auth");
359 } 382 }
360 383
361 /* we can't rekey if we haven't done remote ident exchange yet */ 384 /* we can't rekey if we haven't done remote ident exchange yet */
362 if (ses.remoteident == NULL) { 385 if (ses.remoteident == NULL) {
363 return; 386 return;
364 } 387 }
365 388
366 if (!ses.kexstate.sentkexinit 389 if (!ses.kexstate.sentkexinit
367 && (secs - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT 390 && (now - ses.kexstate.lastkextime >= KEX_REKEY_TIMEOUT
368 || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)){ 391 || ses.kexstate.datarecv+ses.kexstate.datatrans >= KEX_REKEY_DATA)) {
369 TRACE(("rekeying after timeout or max data reached")) 392 TRACE(("rekeying after timeout or max data reached"))
370 send_msg_kexinit(); 393 send_msg_kexinit();
371 } 394 }
372 } 395
373 396 if (opts.keepalive_secs > 0
397 && now - ses.last_packet_time >= opts.keepalive_secs) {
398 send_msg_ignore();
399 }
400 }
401
402 static long select_timeout() {
403 /* determine the minimum timeout that might be required, so
404 as to avoid waking when unneccessary */
405 long ret = LONG_MAX;
406 if (KEX_REKEY_TIMEOUT > 0)
407 ret = MIN(KEX_REKEY_TIMEOUT, ret);
408 if (AUTH_TIMEOUT > 0)
409 ret = MIN(AUTH_TIMEOUT, ret);
410 if (opts.keepalive_secs > 0)
411 ret = MIN(opts.keepalive_secs, ret);
412 return ret;
413 }