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 }