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;