Mercurial > dropbear
comparison common-channel.c @ 1168:509cf5df51c6
Avoid queueing into circbuffer when the channel is about to close
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Thu, 19 Nov 2015 23:52:52 +0800 |
parents | b0f351edf370 |
children | b370b4b172d0 |
comparison
equal
deleted
inserted
replaced
1167:1397a677cb5c | 1168:509cf5df51c6 |
---|---|
40 static void send_msg_channel_open_failure(unsigned int remotechan, int reason, | 40 static void send_msg_channel_open_failure(unsigned int remotechan, int reason, |
41 const char *text, const char *lang); | 41 const char *text, const char *lang); |
42 static void send_msg_channel_open_confirmation(struct Channel* channel, | 42 static void send_msg_channel_open_confirmation(struct Channel* channel, |
43 unsigned int recvwindow, | 43 unsigned int recvwindow, |
44 unsigned int recvmaxpacket); | 44 unsigned int recvmaxpacket); |
45 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf, | 45 static int writechannel(struct Channel* channel, int fd, circbuffer *cbuf, |
46 const unsigned char *moredata, unsigned int *morelen); | 46 const unsigned char *moredata, unsigned int *morelen); |
47 static void send_msg_channel_window_adjust(struct Channel *channel, | 47 static void send_msg_channel_window_adjust(struct Channel *channel, |
48 unsigned int incr); | 48 unsigned int incr); |
49 static void send_msg_channel_data(struct Channel *channel, int isextended); | 49 static void send_msg_channel_data(struct Channel *channel, int isextended); |
50 static void send_msg_channel_eof(struct Channel *channel); | 50 static void send_msg_channel_eof(struct Channel *channel); |
98 } | 98 } |
99 m_free(ses.channels); | 99 m_free(ses.channels); |
100 TRACE(("leave chancleanup")) | 100 TRACE(("leave chancleanup")) |
101 } | 101 } |
102 | 102 |
103 static void | |
104 chan_initwritebuf(struct Channel *channel) | |
105 { | |
106 dropbear_assert(channel->writebuf->size == 0 && channel->recvwindow == 0); | |
107 cbuf_free(channel->writebuf); | |
108 channel->writebuf = cbuf_new(opts.recv_window); | |
109 channel->recvwindow = opts.recv_window; | |
110 } | |
111 | |
112 /* Create a new channel entry, send a reply confirm or failure */ | 103 /* Create a new channel entry, send a reply confirm or failure */ |
113 /* If remotechan, transwindow and transmaxpacket are not know (for a new | 104 /* If remotechan, transwindow and transmaxpacket are not know (for a new |
114 * outgoing connection, with them to be filled on confirmation), they should | 105 * outgoing connection, with them to be filled on confirmation), they should |
115 * all be set to 0 */ | 106 * all be set to 0 */ |
116 static struct Channel* newchannel(unsigned int remotechan, | 107 static struct Channel* newchannel(unsigned int remotechan, |
165 newchan->readfd = FD_UNINIT; | 156 newchan->readfd = FD_UNINIT; |
166 newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ | 157 newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ |
167 newchan->await_open = 0; | 158 newchan->await_open = 0; |
168 newchan->flushing = 0; | 159 newchan->flushing = 0; |
169 | 160 |
170 newchan->writebuf = cbuf_new(0); /* resized later by chan_initwritebuf */ | 161 newchan->writebuf = cbuf_new(opts.recv_window); |
171 newchan->recvwindow = 0; | 162 newchan->recvwindow = opts.recv_window; |
172 | 163 |
173 newchan->extrabuf = NULL; /* The user code can set it up */ | 164 newchan->extrabuf = NULL; /* The user code can set it up */ |
174 newchan->recvdonelen = 0; | 165 newchan->recvdonelen = 0; |
175 newchan->recvmaxpacket = RECV_MAX_CHANNEL_DATA_LEN; | 166 newchan->recvmaxpacket = RECV_MAX_CHANNEL_DATA_LEN; |
176 | 167 |
377 | 368 |
378 if (result == DROPBEAR_SUCCESS) | 369 if (result == DROPBEAR_SUCCESS) |
379 { | 370 { |
380 channel->readfd = channel->writefd = sock; | 371 channel->readfd = channel->writefd = sock; |
381 channel->conn_pending = NULL; | 372 channel->conn_pending = NULL; |
382 chan_initwritebuf(channel); | |
383 send_msg_channel_open_confirmation(channel, channel->recvwindow, | 373 send_msg_channel_open_confirmation(channel, channel->recvwindow, |
384 channel->recvmaxpacket); | 374 channel->recvmaxpacket); |
385 TRACE(("leave channel_connect_done: success")) | 375 TRACE(("leave channel_connect_done: success")) |
386 } | 376 } |
387 else | 377 else |
434 | 424 |
435 TRACE(("leave send_msg_channel_eof")) | 425 TRACE(("leave send_msg_channel_eof")) |
436 } | 426 } |
437 | 427 |
438 #ifndef HAVE_WRITEV | 428 #ifndef HAVE_WRITEV |
439 static void writechannel_fallback(struct Channel* channel, int fd, circbuffer *cbuf, | 429 static int writechannel_fallback(struct Channel* channel, int fd, circbuffer *cbuf, |
440 const unsigned char *UNUSED(moredata), unsigned int *morelen) { | 430 const unsigned char *UNUSED(moredata), unsigned int *morelen) { |
441 | 431 |
442 unsigned char *circ_p1, *circ_p2; | 432 unsigned char *circ_p1, *circ_p2; |
443 unsigned int circ_len1, circ_len2; | 433 unsigned int circ_len1, circ_len2; |
444 ssize_t written; | 434 ssize_t written; |
453 written = write(fd, circ_p1, circ_len1); | 443 written = write(fd, circ_p1, circ_len1); |
454 if (written < 0) { | 444 if (written < 0) { |
455 if (errno != EINTR && errno != EAGAIN) { | 445 if (errno != EINTR && errno != EAGAIN) { |
456 TRACE(("channel IO write error fd %d %s", fd, strerror(errno))) | 446 TRACE(("channel IO write error fd %d %s", fd, strerror(errno))) |
457 close_chan_fd(channel, fd, SHUT_WR); | 447 close_chan_fd(channel, fd, SHUT_WR); |
458 } | 448 return DROPBEAR_FAILURE; |
459 } else { | 449 } |
460 cbuf_incrread(cbuf, written); | 450 } |
461 channel->recvdonelen += written; | 451 cbuf_incrread(cbuf, written); |
462 } | 452 channel->recvdonelen += written; |
453 return DROPBEAR_SUCCESS; | |
463 } | 454 } |
464 #endif /* !HAVE_WRITEV */ | 455 #endif /* !HAVE_WRITEV */ |
465 | 456 |
466 #ifdef HAVE_WRITEV | 457 #ifdef HAVE_WRITEV |
467 static void writechannel_writev(struct Channel* channel, int fd, circbuffer *cbuf, | 458 static int writechannel_writev(struct Channel* channel, int fd, circbuffer *cbuf, |
468 const unsigned char *moredata, unsigned int *morelen) { | 459 const unsigned char *moredata, unsigned int *morelen) { |
469 | 460 |
470 struct iovec iov[3]; | 461 struct iovec iov[3]; |
471 unsigned char *circ_p1, *circ_p2; | 462 unsigned char *circ_p1, *circ_p2; |
472 unsigned int circ_len1, circ_len2; | 463 unsigned int circ_len1, circ_len2; |
473 int io_count = 0; | 464 int io_count = 0; |
474 | 465 int cbuf_written; |
475 ssize_t written; | 466 ssize_t written; |
476 | 467 |
477 cbuf_readptrs(cbuf, &circ_p1, &circ_len1, &circ_p2, &circ_len2); | 468 cbuf_readptrs(cbuf, &circ_p1, &circ_len1, &circ_p2, &circ_len2); |
478 | 469 |
479 if (circ_len1 > 0) { | 470 if (circ_len1 > 0) { |
501 if (io_count == 0) { | 492 if (io_count == 0) { |
502 /* writechannel may sometimes be called twice in a main loop iteration. | 493 /* writechannel may sometimes be called twice in a main loop iteration. |
503 From common_recv_msg_channel_data() then channelio(). | 494 From common_recv_msg_channel_data() then channelio(). |
504 The second call may not have any data to write, so we just return. */ | 495 The second call may not have any data to write, so we just return. */ |
505 TRACE(("leave writechannel, no data")) | 496 TRACE(("leave writechannel, no data")) |
506 return; | 497 return DROPBEAR_SUCCESS; |
507 } | 498 } |
508 | 499 |
509 if (morelen) { | 500 if (morelen) { |
510 /* Default return value, none consumed */ | 501 /* Default return value, none consumed */ |
511 *morelen = 0; | 502 *morelen = 0; |
515 | 506 |
516 if (written < 0) { | 507 if (written < 0) { |
517 if (errno != EINTR && errno != EAGAIN) { | 508 if (errno != EINTR && errno != EAGAIN) { |
518 TRACE(("channel IO write error fd %d %s", fd, strerror(errno))) | 509 TRACE(("channel IO write error fd %d %s", fd, strerror(errno))) |
519 close_chan_fd(channel, fd, SHUT_WR); | 510 close_chan_fd(channel, fd, SHUT_WR); |
520 } | 511 return DROPBEAR_FAILURE; |
521 } else { | 512 } |
522 int cbuf_written = MIN(circ_len1+circ_len2, (unsigned int)written); | 513 } |
523 cbuf_incrread(cbuf, cbuf_written); | 514 |
524 if (morelen) { | 515 cbuf_written = MIN(circ_len1+circ_len2, (unsigned int)written); |
525 *morelen = written - cbuf_written; | 516 cbuf_incrread(cbuf, cbuf_written); |
526 } | 517 if (morelen) { |
527 channel->recvdonelen += written; | 518 *morelen = written - cbuf_written; |
528 } | 519 } |
529 | 520 channel->recvdonelen += written; |
521 return DROPBEAR_SUCCESS; | |
530 } | 522 } |
531 #endif /* HAVE_WRITEV */ | 523 #endif /* HAVE_WRITEV */ |
532 | 524 |
533 /* Called to write data out to the local side of the channel. | 525 /* Called to write data out to the local side of the channel. |
534 Writes the circular buffer contents and also the "moredata" buffer | 526 Writes the circular buffer contents and also the "moredata" buffer |
535 if not null. Will ignore EAGAIN */ | 527 if not null. Will ignore EAGAIN. |
536 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf, | 528 Returns DROPBEAR_FAILURE if writing to fd had an error and the channel is being closed, DROPBEAR_SUCCESS otherwise */ |
529 static int writechannel(struct Channel* channel, int fd, circbuffer *cbuf, | |
537 const unsigned char *moredata, unsigned int *morelen) { | 530 const unsigned char *moredata, unsigned int *morelen) { |
531 int ret = DROPBEAR_SUCCESS; | |
538 TRACE(("enter writechannel fd %d", fd)) | 532 TRACE(("enter writechannel fd %d", fd)) |
539 #ifdef HAVE_WRITEV | 533 #ifdef HAVE_WRITEV |
540 writechannel_writev(channel, fd, cbuf, moredata, morelen); | 534 ret = writechannel_writev(channel, fd, cbuf, moredata, morelen); |
541 #else | 535 #else |
542 writechannel_fallback(channel, fd, cbuf, moredata, morelen); | 536 ret = writechannel_fallback(channel, fd, cbuf, moredata, morelen); |
543 #endif | 537 #endif |
544 | 538 |
545 /* Window adjust handling */ | 539 /* Window adjust handling */ |
546 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { | 540 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { |
547 send_msg_channel_window_adjust(channel, channel->recvdonelen); | 541 send_msg_channel_window_adjust(channel, channel->recvdonelen); |
553 dropbear_assert(channel->recvwindow <= cbuf_getavail(channel->writebuf)); | 547 dropbear_assert(channel->recvwindow <= cbuf_getavail(channel->writebuf)); |
554 dropbear_assert(channel->extrabuf == NULL || | 548 dropbear_assert(channel->extrabuf == NULL || |
555 channel->recvwindow <= cbuf_getavail(channel->extrabuf)); | 549 channel->recvwindow <= cbuf_getavail(channel->extrabuf)); |
556 | 550 |
557 TRACE(("leave writechannel")) | 551 TRACE(("leave writechannel")) |
552 return ret; | |
558 } | 553 } |
559 | 554 |
560 | 555 |
561 /* Set the file descriptors for the main select in session.c | 556 /* Set the file descriptors for the main select in session.c |
562 * This avoid channels which don't have any window available, are closed, etc*/ | 557 * This avoid channels which don't have any window available, are closed, etc*/ |
827 unsigned int datalen; | 822 unsigned int datalen; |
828 unsigned int maxdata; | 823 unsigned int maxdata; |
829 unsigned int buflen; | 824 unsigned int buflen; |
830 unsigned int len; | 825 unsigned int len; |
831 unsigned int consumed; | 826 unsigned int consumed; |
827 int res; | |
832 | 828 |
833 TRACE(("enter recv_msg_channel_data")) | 829 TRACE(("enter recv_msg_channel_data")) |
834 | 830 |
835 if (channel->recv_eof) { | 831 if (channel->recv_eof) { |
836 dropbear_exit("Received data after eof"); | 832 dropbear_exit("Received data after eof"); |
859 channel->recvwindow -= datalen; | 855 channel->recvwindow -= datalen; |
860 dropbear_assert(channel->recvwindow <= opts.recv_window); | 856 dropbear_assert(channel->recvwindow <= opts.recv_window); |
861 | 857 |
862 /* Attempt to write the data immediately without having to put it in the circular buffer */ | 858 /* Attempt to write the data immediately without having to put it in the circular buffer */ |
863 consumed = datalen; | 859 consumed = datalen; |
864 writechannel(channel, fd, cbuf, buf_getptr(ses.payload, datalen), &consumed); | 860 res = writechannel(channel, fd, cbuf, buf_getptr(ses.payload, datalen), &consumed); |
865 | 861 |
866 datalen -= consumed; | 862 datalen -= consumed; |
867 buf_incrpos(ses.payload, consumed); | 863 buf_incrpos(ses.payload, consumed); |
868 | 864 |
869 | 865 |
870 /* We may have to run throught twice, if the buffer wraps around. Can't | 866 /* We may have to run throught twice, if the buffer wraps around. Can't |
871 * just "leave it for next time" like with writechannel, since this | 867 * just "leave it for next time" like with writechannel, since this |
872 * is payload data */ | 868 * is payload data. |
873 len = datalen; | 869 * If the writechannel() failed then remaining data is discarded */ |
874 while (len > 0) { | 870 if (res == DROPBEAR_SUCCESS) { |
875 buflen = cbuf_writelen(cbuf); | 871 len = datalen; |
876 buflen = MIN(buflen, len); | 872 while (len > 0) { |
877 | 873 buflen = cbuf_writelen(cbuf); |
878 memcpy(cbuf_writeptr(cbuf, buflen), | 874 buflen = MIN(buflen, len); |
879 buf_getptr(ses.payload, buflen), buflen); | 875 |
880 cbuf_incrwrite(cbuf, buflen); | 876 memcpy(cbuf_writeptr(cbuf, buflen), |
881 buf_incrpos(ses.payload, buflen); | 877 buf_getptr(ses.payload, buflen), buflen); |
882 len -= buflen; | 878 cbuf_incrwrite(cbuf, buflen); |
879 buf_incrpos(ses.payload, buflen); | |
880 len -= buflen; | |
881 } | |
883 } | 882 } |
884 | 883 |
885 TRACE(("leave recv_msg_channel_data")) | 884 TRACE(("leave recv_msg_channel_data")) |
886 } | 885 } |
887 | 886 |
991 | 990 |
992 if (channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) { | 991 if (channel->prio == DROPBEAR_CHANNEL_PRIO_EARLY) { |
993 channel->prio = DROPBEAR_CHANNEL_PRIO_BULK; | 992 channel->prio = DROPBEAR_CHANNEL_PRIO_BULK; |
994 } | 993 } |
995 | 994 |
996 chan_initwritebuf(channel); | |
997 | |
998 /* success */ | 995 /* success */ |
999 send_msg_channel_open_confirmation(channel, channel->recvwindow, | 996 send_msg_channel_open_confirmation(channel, channel->recvwindow, |
1000 channel->recvmaxpacket); | 997 channel->recvmaxpacket); |
1001 goto cleanup; | 998 goto cleanup; |
1002 | 999 |
1135 return DROPBEAR_FAILURE; | 1132 return DROPBEAR_FAILURE; |
1136 } | 1133 } |
1137 | 1134 |
1138 /* Outbound opened channels don't make use of in-progress connections, | 1135 /* Outbound opened channels don't make use of in-progress connections, |
1139 * we can set it up straight away */ | 1136 * we can set it up straight away */ |
1140 chan_initwritebuf(chan); | |
1141 | 1137 |
1142 /* set fd non-blocking */ | 1138 /* set fd non-blocking */ |
1143 setnonblocking(fd); | 1139 setnonblocking(fd); |
1144 | 1140 |
1145 chan->writefd = chan->readfd = fd; | 1141 chan->writefd = chan->readfd = fd; |