Mercurial > dropbear
comparison common-channel.c @ 375:8d149b812669 channel-fix
- Add some extra tracing.
- Be clearer about errfd be used for read versus write with
ERRFD_IS_READ and ERRFD_IS_WRITE macros
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Tue, 05 Dec 2006 14:42:03 +0000 |
parents | 49fcc9875045 |
children | 4f2dbd1c3685 |
comparison
equal
deleted
inserted
replaced
374:87ae4565679d | 375:8d149b812669 |
---|---|
54 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); |
55 | 55 |
56 #define FD_UNINIT (-2) | 56 #define FD_UNINIT (-2) |
57 #define FD_CLOSED (-1) | 57 #define FD_CLOSED (-1) |
58 | 58 |
59 #define ERRFD_IS_READ(channel) ((channel)->extrabuf == NULL) | |
60 #define ERRFD_IS_WRITE(channel) (!ERRFD_IS_READ(channel)) | |
61 | |
59 /* Initialise all the channels */ | 62 /* Initialise all the channels */ |
60 void chaninitialise(const struct ChanType *chantypes[]) { | 63 void chaninitialise(const struct ChanType *chantypes[]) { |
61 | 64 |
62 /* may as well create space for a single channel */ | 65 /* may as well create space for a single channel */ |
63 ses.channels = (struct Channel**)m_malloc(sizeof(struct Channel*)); | 66 ses.channels = (struct Channel**)m_malloc(sizeof(struct Channel*)); |
202 if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) { | 205 if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) { |
203 send_msg_channel_data(channel, 0); | 206 send_msg_channel_data(channel, 0); |
204 } | 207 } |
205 | 208 |
206 /* read stderr data and send it over the wire */ | 209 /* read stderr data and send it over the wire */ |
207 if (channel->extrabuf == NULL && | 210 if (ERRFD_IS_READ(channel) && |
208 channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) { | 211 channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) { |
209 send_msg_channel_data(channel, 1); | 212 send_msg_channel_data(channel, 1); |
210 } | 213 } |
211 | 214 |
212 /* write to program/pipe stdin */ | 215 /* write to program/pipe stdin */ |
219 } | 222 } |
220 writechannel(channel, channel->writefd, channel->writebuf); | 223 writechannel(channel, channel->writefd, channel->writebuf); |
221 } | 224 } |
222 | 225 |
223 /* stderr for client mode */ | 226 /* stderr for client mode */ |
224 if (channel->extrabuf != NULL | 227 if (ERRFD_IS_WRITE(channel) |
225 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) { | 228 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) { |
226 writechannel(channel, channel->errfd, channel->extrabuf); | 229 writechannel(channel, channel->errfd, channel->extrabuf); |
227 } | 230 } |
228 | 231 |
229 /* handle any channel closing etc */ | 232 /* handle any channel closing etc */ |
243 static unsigned int write_pending(struct Channel * channel) { | 246 static unsigned int write_pending(struct Channel * channel) { |
244 | 247 |
245 if (channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0) { | 248 if (channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0) { |
246 return 1; | 249 return 1; |
247 } else if (channel->errfd >= 0 && channel->extrabuf && | 250 } else if (channel->errfd >= 0 && channel->extrabuf && |
248 cbuf_getused(channel->writebuf) > 0) { | 251 cbuf_getused(channel->extrabuf) > 0) { |
249 return 1; | 252 return 1; |
250 } | 253 } |
251 return 0; | 254 return 0; |
252 } | 255 } |
253 | 256 |
256 static void check_close(struct Channel *channel) { | 259 static void check_close(struct Channel *channel) { |
257 | 260 |
258 TRACE(("check_close: writefd %d, readfd %d, errfd %d, sent_close %d, recv_close %d", | 261 TRACE(("check_close: writefd %d, readfd %d, errfd %d, sent_close %d, recv_close %d", |
259 channel->writefd, channel->readfd, | 262 channel->writefd, channel->readfd, |
260 channel->errfd, channel->sent_close, channel->recv_close)) | 263 channel->errfd, channel->sent_close, channel->recv_close)) |
261 TRACE(("writebuf size %d extrabuf ptr 0x%x extrabuf size %d", | 264 TRACE(("writebuf size %d extrabuf size %d", |
262 cbuf_getused(channel->writebuf), | 265 cbuf_getused(channel->writebuf), |
263 channel->writebuf, | 266 channel->extrabuf ? cbuf_getused(channel->extrabuf) : 0)) |
264 channel->writebuf ? 0 : cbuf_getused(channel->extrabuf))) | |
265 | |
266 /* A bit of a hack for closing up server session channels */ | |
267 if (channel->writefd >= 0 | |
268 && channel->type->check_close | |
269 && channel->type->check_close(channel)) { | |
270 TRACE(("channel->type->check_close got hit")) | |
271 close_chan_fd(channel, channel->writefd, SHUT_WR); | |
272 } | |
273 | 267 |
274 if (channel->recv_close && !write_pending(channel)) { | 268 if (channel->recv_close && !write_pending(channel)) { |
275 if (! channel->sent_close) { | 269 if (! channel->sent_close) { |
276 TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")) | 270 TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")) |
277 send_msg_channel_close(channel); | 271 send_msg_channel_close(channel); |
282 | 276 |
283 if (channel->recv_eof && !write_pending(channel)) { | 277 if (channel->recv_eof && !write_pending(channel)) { |
284 close_chan_fd(channel, channel->writefd, SHUT_WR); | 278 close_chan_fd(channel, channel->writefd, SHUT_WR); |
285 } | 279 } |
286 | 280 |
281 /* If we're not going to send any more data, send EOF */ | |
287 if (!channel->sent_eof | 282 if (!channel->sent_eof |
288 && channel->readfd == FD_CLOSED | 283 && channel->readfd == FD_CLOSED |
289 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { | 284 && (ERRFD_IS_WRITE(channel) || channel->errfd == FD_CLOSED)) { |
290 send_msg_channel_eof(channel); | 285 send_msg_channel_eof(channel); |
291 } | 286 } |
292 | 287 |
288 /* And if we can't receive any more data from them either, close up */ | |
293 if (!channel->sent_close | 289 if (!channel->sent_close |
294 && channel->writefd == FD_CLOSED | 290 && channel->writefd == FD_CLOSED |
295 && channel->readfd == FD_CLOSED | 291 && channel->readfd == FD_CLOSED |
296 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { | 292 && channel->errfd == FD_CLOSED) { |
297 send_msg_channel_close(channel); | 293 send_msg_channel_close(channel); |
298 } | 294 } |
299 | 295 |
300 } | 296 } |
301 | 297 |
369 * possible */ | 365 * possible */ |
370 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) { | 366 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) { |
371 | 367 |
372 int len, maxlen; | 368 int len, maxlen; |
373 | 369 |
374 TRACE(("enter writechannel")) | 370 TRACE(("enter writechannel fd %d", fd)) |
375 | 371 |
376 maxlen = cbuf_readlen(cbuf); | 372 maxlen = cbuf_readlen(cbuf); |
377 | 373 |
378 /* Write the data out */ | 374 /* Write the data out */ |
379 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); | 375 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); |
380 if (len <= 0) { | 376 if (len <= 0) { |
377 TRACE(("errno %d len %d", errno, len)) | |
381 if (len < 0 && errno != EINTR) { | 378 if (len < 0 && errno != EINTR) { |
382 close_chan_fd(channel, fd, SHUT_WR); | 379 close_chan_fd(channel, fd, SHUT_WR); |
383 } | 380 } |
384 TRACE(("leave writechannel: len <= 0")) | 381 TRACE(("leave writechannel: len <= 0")) |
385 return; | 382 return; |
386 } | 383 } |
384 TRACE(("writechannel wrote %d", len)) | |
387 | 385 |
388 cbuf_incrread(cbuf, len); | 386 cbuf_incrread(cbuf, len); |
389 channel->recvdonelen += len; | 387 channel->recvdonelen += len; |
390 | 388 |
391 /* Window adjust handling */ | 389 /* Window adjust handling */ |
423 | 421 |
424 if (channel->readfd >= 0) { | 422 if (channel->readfd >= 0) { |
425 FD_SET(channel->readfd, readfds); | 423 FD_SET(channel->readfd, readfds); |
426 } | 424 } |
427 | 425 |
428 if (channel->extrabuf == NULL && channel->errfd >= 0) { | 426 if (ERRFD_IS_READ(channel) && channel->errfd >= 0) { |
429 FD_SET(channel->errfd, readfds); | 427 FD_SET(channel->errfd, readfds); |
430 } | 428 } |
431 } | 429 } |
432 | 430 |
433 /* Stuff from the wire */ | 431 /* Stuff from the wire */ |
434 if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 ) | 432 if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 ) |
435 || channel->initconn) { | 433 || channel->initconn) { |
436 FD_SET(channel->writefd, writefds); | 434 FD_SET(channel->writefd, writefds); |
437 } | 435 } |
438 | 436 |
439 if (channel->extrabuf != NULL && channel->errfd >= 0 | 437 if (ERRFD_IS_WRITE(channel) != NULL && channel->errfd >= 0 |
440 && cbuf_getused(channel->extrabuf) > 0 ) { | 438 && cbuf_getused(channel->extrabuf) > 0 ) { |
441 FD_SET(channel->errfd, writefds); | 439 FD_SET(channel->errfd, writefds); |
442 } | 440 } |
443 | 441 |
444 } /* foreach channel */ | 442 } /* foreach channel */ |
499 } | 497 } |
500 | 498 |
501 | 499 |
502 /* close the FDs in case they haven't been done | 500 /* close the FDs in case they haven't been done |
503 * yet (they might have been shutdown etc) */ | 501 * yet (they might have been shutdown etc) */ |
502 TRACE(("CLOSE writefd %d", channel->writefd)) | |
504 close(channel->writefd); | 503 close(channel->writefd); |
504 TRACE(("CLOSE readfd %d", channel->readfd)) | |
505 close(channel->readfd); | 505 close(channel->readfd); |
506 TRACE(("CLOSE errfd %d", channel->errfd)) | |
506 close(channel->errfd); | 507 close(channel->errfd); |
507 | 508 |
508 channel->typedata = NULL; | 509 channel->typedata = NULL; |
509 | 510 |
510 delete_channel(channel); | 511 delete_channel(channel); |
560 if (isextended) { | 561 if (isextended) { |
561 fd = channel->errfd; | 562 fd = channel->errfd; |
562 } else { | 563 } else { |
563 fd = channel->readfd; | 564 fd = channel->readfd; |
564 } | 565 } |
566 TRACE(("enter send_msg_channel_data isextended %d fd %d", isextended, fd)) | |
565 dropbear_assert(fd >= 0); | 567 dropbear_assert(fd >= 0); |
566 | 568 |
567 maxlen = MIN(channel->transwindow, channel->transmaxpacket); | 569 maxlen = MIN(channel->transwindow, channel->transmaxpacket); |
568 /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and | 570 /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and |
569 * exttype if is extended */ | 571 * exttype if is extended */ |
590 if (len <= 0) { | 592 if (len <= 0) { |
591 if (len == 0 || errno != EINTR) { | 593 if (len == 0 || errno != EINTR) { |
592 close_chan_fd(channel, fd, SHUT_RD); | 594 close_chan_fd(channel, fd, SHUT_RD); |
593 } | 595 } |
594 ses.writepayload->len = ses.writepayload->pos = 0; | 596 ses.writepayload->len = ses.writepayload->pos = 0; |
595 TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", | 597 TRACE(("leave send_msg_channel_data: len %d read err or EOF for fd %d", |
596 channel->index)); | 598 len, channel->index)); |
597 return; | 599 return; |
598 } | 600 } |
599 buf_incrwritepos(ses.writepayload, len); | 601 buf_incrwritepos(ses.writepayload, len); |
600 /* ... real size here */ | 602 /* ... real size here */ |
601 buf_setpos(ses.writepayload, size_pos); | 603 buf_setpos(ses.writepayload, size_pos); |
738 } | 740 } |
739 | 741 |
740 /* Get the channel type. Client and server style invokation will set up a | 742 /* Get the channel type. Client and server style invokation will set up a |
741 * different list for ses.chantypes at startup. We just iterate through | 743 * different list for ses.chantypes at startup. We just iterate through |
742 * this list and find the matching name */ | 744 * this list and find the matching name */ |
743 /* XXX fugly */ | |
744 for (cp = &ses.chantypes[0], chantype = (*cp); | 745 for (cp = &ses.chantypes[0], chantype = (*cp); |
745 chantype != NULL; | 746 chantype != NULL; |
746 cp++, chantype = (*cp)) { | 747 cp++, chantype = (*cp)) { |
747 if (strcmp(type, chantype->name) == 0) { | 748 if (strcmp(type, chantype->name) == 0) { |
748 break; | 749 break; |
860 static void close_chan_fd(struct Channel *channel, int fd, int how) { | 861 static void close_chan_fd(struct Channel *channel, int fd, int how) { |
861 | 862 |
862 int closein = 0, closeout = 0; | 863 int closein = 0, closeout = 0; |
863 | 864 |
864 if (channel->type->sepfds) { | 865 if (channel->type->sepfds) { |
865 TRACE(("shutdown((%d), %d)", fd, how)) | 866 TRACE(("SHUTDOWN(%d, %d)", fd, how)) |
866 shutdown(fd, how); | 867 shutdown(fd, how); |
867 if (how == 0) { | 868 if (how == 0) { |
868 closeout = 1; | 869 closeout = 1; |
869 } else { | 870 } else { |
870 closein = 1; | 871 closein = 1; |
871 } | 872 } |
872 } else { | 873 } else { |
874 TRACE(("CLOSE some fd %d", fd)) | |
873 close(fd); | 875 close(fd); |
874 closein = closeout = 1; | 876 closein = closeout = 1; |
875 } | 877 } |
876 | 878 |
877 if (closeout && fd == channel->readfd) { | 879 if (closeout && (fd == channel->readfd)) { |
878 channel->readfd = FD_CLOSED; | 880 channel->readfd = FD_CLOSED; |
879 } | 881 } |
880 if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) { | 882 if (closeout && ERRFD_IS_READ(channel) && (fd == channel->errfd)) { |
881 channel->errfd = FD_CLOSED; | 883 channel->errfd = FD_CLOSED; |
882 } | 884 } |
883 | 885 |
884 if (closein && fd == channel->writefd) { | 886 if (closein && fd == channel->writefd) { |
885 channel->writefd = FD_CLOSED; | 887 channel->writefd = FD_CLOSED; |
886 } | 888 } |
887 if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) { | 889 if (closein && ERRFD_IS_WRITE(channel) && (fd == channel->errfd)) { |
888 channel->errfd = FD_CLOSED; | 890 channel->errfd = FD_CLOSED; |
889 } | 891 } |
890 | 892 |
891 /* if we called shutdown on it and all references are gone, then we | 893 /* if we called shutdown on it and all references are gone, then we |
892 * need to close() it to stop it lingering */ | 894 * need to close() it to stop it lingering */ |
893 if (channel->type->sepfds && channel->readfd == FD_CLOSED | 895 if (channel->type->sepfds && channel->readfd == FD_CLOSED |
894 && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) { | 896 && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) { |
897 TRACE(("CLOSE (finally) of %d", fd)) | |
895 close(fd); | 898 close(fd); |
896 } | 899 } |
897 } | 900 } |
898 | 901 |
899 | 902 |