Mercurial > dropbear
comparison common-channel.c @ 363:6ba2894ec8d5 channel-fix
Rearranged (and hopefully simplified) channel close/eof handling
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Sat, 07 Oct 2006 17:48:55 +0000 |
parents | 1c7bf9cec6c8 |
children | 90cb290836de |
comparison
equal
deleted
inserted
replaced
362:1c7bf9cec6c8 | 363:6ba2894ec8d5 |
---|---|
41 unsigned int recvwindow, | 41 unsigned int recvwindow, |
42 unsigned int recvmaxpacket); | 42 unsigned int recvmaxpacket); |
43 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf); | 43 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf); |
44 static void send_msg_channel_window_adjust(struct Channel *channel, | 44 static void send_msg_channel_window_adjust(struct Channel *channel, |
45 unsigned int incr); | 45 unsigned int incr); |
46 static void send_msg_channel_data(struct Channel *channel, int isextended, | 46 static void send_msg_channel_data(struct Channel *channel, int isextended); |
47 unsigned int exttype); | |
48 static void send_msg_channel_eof(struct Channel *channel); | 47 static void send_msg_channel_eof(struct Channel *channel); |
49 static void send_msg_channel_close(struct Channel *channel); | 48 static void send_msg_channel_close(struct Channel *channel); |
50 static void remove_channel(struct Channel *channel); | 49 static void remove_channel(struct Channel *channel); |
51 static void delete_channel(struct Channel *channel); | 50 static void delete_channel(struct Channel *channel); |
52 static void check_in_progress(struct Channel *channel); | 51 static void check_in_progress(struct Channel *channel); |
52 static unsigned int write_pending(struct Channel * channel); | |
53 static void check_close(struct Channel *channel); | 53 static void check_close(struct Channel *channel); |
54 | |
55 static void close_write_fd(struct Channel * channel); | |
56 static void close_read_fd(struct Channel * channel, int fd); | |
57 static void close_chan_fd(struct Channel *channel, int fd, int how); | 54 static void close_chan_fd(struct Channel *channel, int fd, int how); |
58 | 55 |
59 #define FD_UNINIT (-2) | 56 #define FD_UNINIT (-2) |
60 #define FD_CLOSED (-1) | 57 #define FD_CLOSED (-1) |
61 | 58 |
190 void channelio(fd_set *readfds, fd_set *writefds) { | 187 void channelio(fd_set *readfds, fd_set *writefds) { |
191 | 188 |
192 struct Channel *channel; | 189 struct Channel *channel; |
193 unsigned int i; | 190 unsigned int i; |
194 | 191 |
195 /* iterate through all the possible channels */ | 192 /* foreach channel */ |
196 for (i = 0; i < ses.chansize; i++) { | 193 for (i = 0; i < ses.chansize; i++) { |
197 | 194 |
198 channel = ses.channels[i]; | 195 channel = ses.channels[i]; |
199 if (channel == NULL) { | 196 if (channel == NULL) { |
200 /* only process in-use channels */ | 197 /* only process in-use channels */ |
201 continue; | 198 continue; |
202 } | 199 } |
203 | 200 |
204 /* read data and send it over the wire */ | 201 /* read data and send it over the wire */ |
205 if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) { | 202 if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) { |
206 send_msg_channel_data(channel, 0, 0); | 203 send_msg_channel_data(channel, 0); |
207 } | 204 } |
208 | 205 |
209 /* read stderr data and send it over the wire */ | 206 /* read stderr data and send it over the wire */ |
210 if (channel->extrabuf == NULL && | 207 if (channel->extrabuf == NULL && |
211 channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) { | 208 channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) { |
212 send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR); | 209 send_msg_channel_data(channel, 1); |
213 } | 210 } |
214 | |
215 #if 0 | |
216 /* XXX where is this required? */ | |
217 if (channel->initconn) { | |
218 /* Handling for "in progress" connection - this is needed | |
219 * to avoid spinning 100% CPU when we connect to a server | |
220 * which doesn't send anything (tcpfwding) */ | |
221 check_in_progress(channel); | |
222 continue; /* Important not to use the channel after | |
223 check_in_progress(), as it may be NULL */ | |
224 } | |
225 #endif | |
226 | 211 |
227 /* write to program/pipe stdin */ | 212 /* write to program/pipe stdin */ |
228 if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) { | 213 if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) { |
229 if (channel->initconn) { | 214 if (channel->initconn) { |
230 /* XXX could this go somewhere cleaner? */ | 215 /* XXX should this go somewhere cleaner? */ |
231 check_in_progress(channel); | 216 check_in_progress(channel); |
232 continue; /* Important not to use the channel after | 217 continue; /* Important not to use the channel after |
233 check_in_progress(), as it may be NULL */ | 218 check_in_progress(), as it may be NULL */ |
234 } | 219 } |
235 writechannel(channel, channel->writefd, channel->writebuf); | 220 writechannel(channel, channel->writefd, channel->writebuf); |
239 if (channel->extrabuf != NULL | 224 if (channel->extrabuf != NULL |
240 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) { | 225 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) { |
241 writechannel(channel, channel->errfd, channel->extrabuf); | 226 writechannel(channel, channel->errfd, channel->extrabuf); |
242 } | 227 } |
243 | 228 |
244 /* now handle any of the channel-closing type stuff */ | 229 /* handle any channel closing etc */ |
245 check_close(channel); | 230 check_close(channel); |
246 | 231 |
247 } /* foreach channel */ | 232 } |
248 | 233 |
249 /* Listeners such as TCP, X11, agent-auth */ | 234 /* Listeners such as TCP, X11, agent-auth */ |
250 #ifdef USING_LISTENERS | 235 #ifdef USING_LISTENERS |
251 handle_listeners(readfds); | 236 handle_listeners(readfds); |
252 #endif | 237 #endif |
238 } | |
239 | |
240 | |
241 /* Returns true if there is data remaining to be written to stdin or | |
242 * stderr of a channel's endpoint. */ | |
243 static unsigned int write_pending(struct Channel * channel) { | |
244 | |
245 if (channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0) { | |
246 return 1; | |
247 } else if (channel->errfd >= 0 && channel->extrabuf && | |
248 cbuf_getused(channel->writebuf) > 0) { | |
249 return 1; | |
250 } | |
251 return 0; | |
253 } | 252 } |
254 | 253 |
255 | 254 |
256 /* EOF/close handling */ | 255 /* EOF/close handling */ |
257 static void check_close(struct Channel *channel) { | 256 static void check_close(struct Channel *channel) { |
262 TRACE(("writebuf size %d extrabuf ptr 0x%x extrabuf size %d", | 261 TRACE(("writebuf size %d extrabuf ptr 0x%x extrabuf size %d", |
263 cbuf_getused(channel->writebuf), | 262 cbuf_getused(channel->writebuf), |
264 channel->writebuf, | 263 channel->writebuf, |
265 channel->writebuf ? 0 : cbuf_getused(channel->extrabuf))) | 264 channel->writebuf ? 0 : cbuf_getused(channel->extrabuf))) |
266 | 265 |
267 /* XXX not good, doesn't flush out */ | 266 if (!channel->sent_close |
268 if (channel->recv_close) { | 267 && channel->writefd == FD_CLOSED |
268 && (channel->errfd == FD_CLOSED || channel->extrabuf == NULL)) { | |
269 send_msg_channel_close(channel); | |
270 } | |
271 | |
272 if (channel->recv_close && !write_pending(channel)) { | |
269 if (! channel->sent_close) { | 273 if (! channel->sent_close) { |
270 TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")) | 274 TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")) |
271 send_msg_channel_close(channel); | 275 send_msg_channel_close(channel); |
272 } | 276 } |
273 remove_channel(channel); | 277 remove_channel(channel); |
274 return; | 278 return; |
275 } | 279 } |
276 | 280 |
277 /* server chansession channels are special, since readfd mightn't | 281 #if 0 |
278 * close in the case of "sleep 4 & echo blah" until the sleep is up */ | 282 // The only use of check_close is "return channel->writefd == -1;" for a server |
283 // chansession. Should be able to handle that with just the general | |
284 // socket close handling...? | |
279 if (channel->type->check_close) { | 285 if (channel->type->check_close) { |
280 if (channel->type->check_close(channel)) { | 286 if (channel->type->check_close(channel)) { |
281 close_write_fd(channel); | 287 close_write_fd(channel); |
282 close_read_fd(channel, channel->readfd); | 288 close_read_fd(channel, channel->readfd); |
283 close_read_fd(channel, channel->errfd); | 289 close_read_fd(channel, channel->errfd); |
284 } | 290 } |
285 } | 291 } |
292 #endif | |
286 | 293 |
287 if (!channel->sent_eof | 294 if (!channel->sent_eof |
288 && channel->readfd == FD_CLOSED | 295 && channel->readfd == FD_CLOSED |
289 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { | 296 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { |
290 send_msg_channel_eof(channel); | 297 send_msg_channel_eof(channel); |
294 && channel->writefd == FD_CLOSED | 301 && channel->writefd == FD_CLOSED |
295 && channel->readfd == FD_CLOSED | 302 && channel->readfd == FD_CLOSED |
296 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { | 303 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { |
297 send_msg_channel_close(channel); | 304 send_msg_channel_close(channel); |
298 } | 305 } |
299 | |
300 /* XXX blah */ | |
301 if (channel->recv_eof && | |
302 (cbuf_getused(channel->writebuf) == 0 | |
303 && (channel->extrabuf == NULL | |
304 || cbuf_getused(channel->extrabuf) == 0))) { | |
305 close_write_fd(channel); | |
306 } | |
307 | |
308 /* When either party wishes to terminate the channel, it sends | |
309 * SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST | |
310 * send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this | |
311 * message for the channel. The channel is considered closed for a | |
312 * party when it has both sent and received SSH_MSG_CHANNEL_CLOSE, and | |
313 * the party may then reuse the channel number. A party MAY send | |
314 * SSH_MSG_CHANNEL_CLOSE without having sent or received | |
315 * SSH_MSG_CHANNEL_EOF. | |
316 * (from draft-ietf-secsh-connect) | |
317 */ | |
318 } | 306 } |
319 | 307 |
320 | 308 |
321 /* Check whether a deferred (EINPROGRESS) connect() was successful, and | 309 /* Check whether a deferred (EINPROGRESS) connect() was successful, and |
322 * if so, set up the channel properly. Otherwise, the channel is cleaned up, so | 310 * if so, set up the channel properly. Otherwise, the channel is cleaned up, so |
396 | 384 |
397 /* Write the data out */ | 385 /* Write the data out */ |
398 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); | 386 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); |
399 if (len <= 0) { | 387 if (len <= 0) { |
400 if (len < 0 && errno != EINTR) { | 388 if (len < 0 && errno != EINTR) { |
401 /* no more to write - we close it even if the fd was stderr, since | 389 close_chan_fd(channel, fd, SHUT_WR); |
402 * that's a nasty failure too */ | |
403 close_write_fd(channel); | |
404 } | 390 } |
405 TRACE(("leave writechannel: len <= 0")) | 391 TRACE(("leave writechannel: len <= 0")) |
406 return; | 392 return; |
407 } | 393 } |
408 | 394 |
409 cbuf_incrread(cbuf, len); | 395 cbuf_incrread(cbuf, len); |
410 channel->recvdonelen += len; | 396 channel->recvdonelen += len; |
397 | |
398 /* We're closing out */ | |
399 if (channel->recv_eof && cbuf_getused(cbuf) == 0) { | |
400 TRACE(("leave writechannel")) | |
401 close_chan_fd(channel, fd, SHUT_WR); | |
402 return; | |
403 } | |
411 | 404 |
412 /* Window adjust handling */ | 405 /* Window adjust handling */ |
413 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { | 406 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { |
414 /* Set it back to max window */ | 407 /* Set it back to max window */ |
415 send_msg_channel_window_adjust(channel, channel->recvdonelen); | 408 send_msg_channel_window_adjust(channel, channel->recvdonelen); |
570 /* Reads data from the server's program/shell/etc, and puts it in a | 563 /* Reads data from the server's program/shell/etc, and puts it in a |
571 * channel_data packet to send. | 564 * channel_data packet to send. |
572 * chan is the remote channel, isextended is 0 if it is normal data, 1 | 565 * chan is the remote channel, isextended is 0 if it is normal data, 1 |
573 * if it is extended data. if it is extended, then the type is in | 566 * if it is extended data. if it is extended, then the type is in |
574 * exttype */ | 567 * exttype */ |
575 static void send_msg_channel_data(struct Channel *channel, int isextended, | 568 static void send_msg_channel_data(struct Channel *channel, int isextended) { |
576 unsigned int exttype) { | |
577 | 569 |
578 int len; | 570 int len; |
579 size_t maxlen, size_pos; | 571 size_t maxlen, size_pos; |
580 int fd; | 572 int fd; |
581 | |
582 /* TRACE(("enter send_msg_channel_data")) | |
583 TRACE(("extended = %d type = %d", isextended, exttype))*/ | |
584 | 573 |
585 CHECKCLEARTOWRITE(); | 574 CHECKCLEARTOWRITE(); |
586 | 575 |
587 dropbear_assert(!channel->sent_close); | 576 dropbear_assert(!channel->sent_close); |
588 | 577 |
606 | 595 |
607 buf_putbyte(ses.writepayload, | 596 buf_putbyte(ses.writepayload, |
608 isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA); | 597 isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA); |
609 buf_putint(ses.writepayload, channel->remotechan); | 598 buf_putint(ses.writepayload, channel->remotechan); |
610 if (isextended) { | 599 if (isextended) { |
611 buf_putint(ses.writepayload, exttype); | 600 buf_putint(ses.writepayload, SSH_EXTENDED_DATA_STDERR); |
612 } | 601 } |
613 /* a dummy size first ...*/ | 602 /* a dummy size first ...*/ |
614 size_pos = ses.writepayload->pos; | 603 size_pos = ses.writepayload->pos; |
615 buf_putint(ses.writepayload, 0); | 604 buf_putint(ses.writepayload, 0); |
616 | 605 |
617 /* read the data */ | 606 /* read the data */ |
618 len = read(fd, buf_getwriteptr(ses.writepayload, maxlen), maxlen); | 607 len = read(fd, buf_getwriteptr(ses.writepayload, maxlen), maxlen); |
619 if (len <= 0) { | 608 if (len <= 0) { |
620 if (len == 0 || errno != EINTR) { | 609 if (len == 0 || errno != EINTR) { |
621 close_read_fd(channel, fd); | 610 close_chan_fd(channel, fd, SHUT_RD); |
622 } | 611 } |
623 ses.writepayload->len = ses.writepayload->pos = 0; | 612 ses.writepayload->len = ses.writepayload->pos = 0; |
624 TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", | 613 TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", |
625 channel->index)); | 614 channel->index)); |
626 return; | 615 return; |
889 | 878 |
890 encrypt_packet(); | 879 encrypt_packet(); |
891 TRACE(("leave send_msg_channel_open_confirmation")) | 880 TRACE(("leave send_msg_channel_open_confirmation")) |
892 } | 881 } |
893 | 882 |
883 /* close a fd, how is SHUT_RD or SHUT_WR */ | |
884 static void close_chan_fd(struct Channel *channel, int fd, int how) { | |
885 | |
886 int closein = 0, closeout = 0; | |
887 | |
888 if (channel->type->sepfds) { | |
889 TRACE(("shutdown((%d), %d)", fd, how)) | |
890 shutdown(fd, how); | |
891 if (how == 0) { | |
892 closeout = 1; | |
893 } else { | |
894 closein = 1; | |
895 } | |
896 } else { | |
897 close(fd); | |
898 closein = closeout = 1; | |
899 } | |
900 | |
901 if (closeout && fd == channel->readfd) { | |
902 channel->readfd = FD_CLOSED; | |
903 } | |
904 if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) { | |
905 channel->errfd = FD_CLOSED; | |
906 } | |
907 | |
908 if (closein && fd == channel->writefd) { | |
909 channel->writefd = FD_CLOSED; | |
910 } | |
911 if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) { | |
912 channel->errfd = FD_CLOSED; | |
913 } | |
914 | |
915 /* if we called shutdown on it and all references are gone, then we | |
916 * need to close() it to stop it lingering */ | |
917 if (channel->type->sepfds && channel->readfd == FD_CLOSED | |
918 && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) { | |
919 close(fd); | |
920 } | |
921 } | |
922 | |
923 | |
894 #if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT) | 924 #if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT) |
895 /* Create a new channel, and start the open request. This is intended | 925 /* Create a new channel, and start the open request. This is intended |
896 * for X11, agent, tcp forwarding, and should be filled with channel-specific | 926 * for X11, agent, tcp forwarding, and should be filled with channel-specific |
897 * options, with the calling function calling encrypt_packet() after | 927 * options, with the calling function calling encrypt_packet() after |
898 * completion. It is mandatory for the caller to encrypt_packet() if | 928 * completion. It is mandatory for the caller to encrypt_packet() if |
978 channel->await_open = 0; | 1008 channel->await_open = 0; |
979 | 1009 |
980 remove_channel(channel); | 1010 remove_channel(channel); |
981 } | 1011 } |
982 #endif /* USING_LISTENERS */ | 1012 #endif /* USING_LISTENERS */ |
983 | |
984 /* close a stdout/stderr fd */ | |
985 static void close_read_fd(struct Channel * channel, int fd) { | |
986 | |
987 /* don't close it if it is the same as writefd, | |
988 * unless writefd is already set -1 */ | |
989 TRACE(("enter close_read_fd")) | |
990 close_chan_fd(channel, fd, 0); | |
991 TRACE(("leave close_read_fd")) | |
992 } | |
993 | |
994 /* close a stdin fd */ | |
995 static void close_write_fd(struct Channel * channel) { | |
996 | |
997 TRACE(("enter close_write_fd")) | |
998 close_chan_fd(channel, channel->writefd, 1); | |
999 TRACE(("leave close_write_fd")) | |
1000 } | |
1001 | |
1002 /* close a fd, how is 0 for stdout/stderr, 1 for stdin */ | |
1003 static void close_chan_fd(struct Channel *channel, int fd, int how) { | |
1004 | |
1005 int closein = 0, closeout = 0; | |
1006 | |
1007 if (channel->type->sepfds) { | |
1008 TRACE(("shutdown((%d), %d)", fd, how)) | |
1009 shutdown(fd, how); | |
1010 if (how == 0) { | |
1011 closeout = 1; | |
1012 } else { | |
1013 closein = 1; | |
1014 } | |
1015 } else { | |
1016 close(fd); | |
1017 closein = closeout = 1; | |
1018 } | |
1019 | |
1020 if (closeout && fd == channel->readfd) { | |
1021 channel->readfd = FD_CLOSED; | |
1022 } | |
1023 if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) { | |
1024 channel->errfd = FD_CLOSED; | |
1025 } | |
1026 | |
1027 if (closein && fd == channel->writefd) { | |
1028 channel->writefd = FD_CLOSED; | |
1029 } | |
1030 if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) { | |
1031 channel->errfd = FD_CLOSED; | |
1032 } | |
1033 | |
1034 /* if we called shutdown on it and all references are gone, then we | |
1035 * need to close() it to stop it lingering */ | |
1036 if (channel->type->sepfds && channel->readfd == FD_CLOSED | |
1037 && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) { | |
1038 close(fd); | |
1039 } | |
1040 } |