Mercurial > dropbear
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 } |