Mercurial > dropbear
comparison common-channel.c @ 1069:2fa71c3b2827 pam
merge pam branch up to date
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Mon, 16 Mar 2015 21:34:05 +0800 |
parents | c71df09bc610 |
children | 696205e3dc99 |
comparison
equal
deleted
inserted
replaced
1068:9a6395ddb1b6 | 1069:2fa71c3b2827 |
---|---|
33 #include "dbutil.h" | 33 #include "dbutil.h" |
34 #include "channel.h" | 34 #include "channel.h" |
35 #include "ssh.h" | 35 #include "ssh.h" |
36 #include "listener.h" | 36 #include "listener.h" |
37 #include "runopts.h" | 37 #include "runopts.h" |
38 #include "netio.h" | |
38 | 39 |
39 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, |
40 const unsigned char *text, const unsigned char *lang); | 41 const unsigned char *text, const unsigned char *lang); |
41 static void send_msg_channel_open_confirmation(struct Channel* channel, | 42 static void send_msg_channel_open_confirmation(struct Channel* channel, |
42 unsigned int recvwindow, | 43 unsigned int recvwindow, |
43 unsigned int recvmaxpacket); | 44 unsigned int recvmaxpacket); |
44 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf); | 45 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf, |
46 const unsigned char *moredata, unsigned int *morelen); | |
45 static void send_msg_channel_window_adjust(struct Channel *channel, | 47 static void send_msg_channel_window_adjust(struct Channel *channel, |
46 unsigned int incr); | 48 unsigned int incr); |
47 static void send_msg_channel_data(struct Channel *channel, int isextended); | 49 static void send_msg_channel_data(struct Channel *channel, int isextended); |
48 static void send_msg_channel_eof(struct Channel *channel); | 50 static void send_msg_channel_eof(struct Channel *channel); |
49 static void send_msg_channel_close(struct Channel *channel); | 51 static void send_msg_channel_close(struct Channel *channel); |
50 static void remove_channel(struct Channel *channel); | 52 static void remove_channel(struct Channel *channel); |
51 static void check_in_progress(struct Channel *channel); | |
52 static unsigned int write_pending(struct Channel * channel); | 53 static unsigned int write_pending(struct Channel * channel); |
53 static void check_close(struct Channel *channel); | 54 static void check_close(struct Channel *channel); |
54 static void close_chan_fd(struct Channel *channel, int fd, int how); | 55 static void close_chan_fd(struct Channel *channel, int fd, int how); |
55 | 56 |
56 #define FD_UNINIT (-2) | 57 #define FD_UNINIT (-2) |
161 | 162 |
162 newchan->typedata = NULL; | 163 newchan->typedata = NULL; |
163 newchan->writefd = FD_UNINIT; | 164 newchan->writefd = FD_UNINIT; |
164 newchan->readfd = FD_UNINIT; | 165 newchan->readfd = FD_UNINIT; |
165 newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ | 166 newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ |
166 newchan->initconn = 0; | |
167 newchan->await_open = 0; | 167 newchan->await_open = 0; |
168 newchan->flushing = 0; | 168 newchan->flushing = 0; |
169 | 169 |
170 newchan->writebuf = cbuf_new(0); /* resized later by chan_initwritebuf */ | 170 newchan->writebuf = cbuf_new(0); /* resized later by chan_initwritebuf */ |
171 newchan->recvwindow = 0; | 171 newchan->recvwindow = 0; |
240 do_check_close = 1; | 240 do_check_close = 1; |
241 } | 241 } |
242 | 242 |
243 /* write to program/pipe stdin */ | 243 /* write to program/pipe stdin */ |
244 if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) { | 244 if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) { |
245 if (channel->initconn) { | 245 writechannel(channel, channel->writefd, channel->writebuf, NULL, NULL); |
246 /* XXX should this go somewhere cleaner? */ | |
247 check_in_progress(channel); | |
248 continue; /* Important not to use the channel after | |
249 check_in_progress(), as it may be NULL */ | |
250 } | |
251 writechannel(channel, channel->writefd, channel->writebuf); | |
252 do_check_close = 1; | 246 do_check_close = 1; |
253 } | 247 } |
254 | 248 |
255 /* stderr for client mode */ | 249 /* stderr for client mode */ |
256 if (ERRFD_IS_WRITE(channel) | 250 if (ERRFD_IS_WRITE(channel) |
257 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) { | 251 && channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) { |
258 writechannel(channel, channel->errfd, channel->extrabuf); | 252 writechannel(channel, channel->errfd, channel->extrabuf, NULL, NULL); |
259 do_check_close = 1; | 253 do_check_close = 1; |
260 } | 254 } |
261 | 255 |
262 if (ses.channel_signal_pending) { | 256 if (ses.channel_signal_pending) { |
263 /* SIGCHLD can change channel state for server sessions */ | 257 /* SIGCHLD can change channel state for server sessions */ |
372 | 366 |
373 /* Check whether a deferred (EINPROGRESS) connect() was successful, and | 367 /* Check whether a deferred (EINPROGRESS) connect() was successful, and |
374 * if so, set up the channel properly. Otherwise, the channel is cleaned up, so | 368 * if so, set up the channel properly. Otherwise, the channel is cleaned up, so |
375 * it is important that the channel reference isn't used after a call to this | 369 * it is important that the channel reference isn't used after a call to this |
376 * function */ | 370 * function */ |
377 static void check_in_progress(struct Channel *channel) { | 371 void channel_connect_done(int result, int sock, void* user_data, const char* UNUSED(errstring)) { |
378 | 372 |
379 int val; | 373 struct Channel *channel = user_data; |
380 socklen_t vallen = sizeof(val); | 374 |
381 | 375 TRACE(("enter channel_connect_done")) |
382 TRACE(("enter check_in_progress")) | 376 |
383 | 377 if (result == DROPBEAR_SUCCESS) |
384 if (getsockopt(channel->writefd, SOL_SOCKET, SO_ERROR, &val, &vallen) | 378 { |
385 || val != 0) { | 379 channel->readfd = channel->writefd = sock; |
386 send_msg_channel_open_failure(channel->remotechan, | 380 channel->conn_pending = NULL; |
387 SSH_OPEN_CONNECT_FAILED, "", ""); | |
388 close(channel->writefd); | |
389 remove_channel(channel); | |
390 TRACE(("leave check_in_progress: fail")) | |
391 } else { | |
392 chan_initwritebuf(channel); | 381 chan_initwritebuf(channel); |
393 send_msg_channel_open_confirmation(channel, channel->recvwindow, | 382 send_msg_channel_open_confirmation(channel, channel->recvwindow, |
394 channel->recvmaxpacket); | 383 channel->recvmaxpacket); |
395 channel->readfd = channel->writefd; | 384 TRACE(("leave channel_connect_done: success")) |
396 channel->initconn = 0; | 385 } |
397 TRACE(("leave check_in_progress: success")) | 386 else |
387 { | |
388 send_msg_channel_open_failure(channel->remotechan, | |
389 SSH_OPEN_CONNECT_FAILED, "", ""); | |
390 remove_channel(channel); | |
391 TRACE(("leave check_in_progress: fail")) | |
398 } | 392 } |
399 } | 393 } |
400 | 394 |
401 | 395 |
402 /* Send the close message and set the channel as closed */ | 396 /* Send the close message and set the channel as closed */ |
403 static void send_msg_channel_close(struct Channel *channel) { | 397 static void send_msg_channel_close(struct Channel *channel) { |
404 | 398 |
405 TRACE(("enter send_msg_channel_close %p", channel)) | 399 TRACE(("enter send_msg_channel_close %p", (void*)channel)) |
406 if (channel->type->closehandler | 400 if (channel->type->closehandler |
407 && !channel->close_handler_done) { | 401 && !channel->close_handler_done) { |
408 channel->type->closehandler(channel); | 402 channel->type->closehandler(channel); |
409 channel->close_handler_done = 1; | 403 channel->close_handler_done = 1; |
410 } | 404 } |
439 | 433 |
440 TRACE(("leave send_msg_channel_eof")) | 434 TRACE(("leave send_msg_channel_eof")) |
441 } | 435 } |
442 | 436 |
443 /* Called to write data out to the local side of the channel. | 437 /* Called to write data out to the local side of the channel. |
444 * Only called when we know we can write to a channel, writes as much as | 438 Writes the circular buffer contents and also the "moredata" buffer |
445 * possible */ | 439 if not null. Will ignore EAGAIN */ |
446 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf) { | 440 static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf, |
447 | 441 const unsigned char *moredata, unsigned int *morelen) { |
448 int len, maxlen; | 442 |
443 struct iovec iov[3]; | |
444 unsigned char *circ_p1, *circ_p2; | |
445 unsigned int circ_len1, circ_len2; | |
446 int io_count = 0; | |
447 | |
448 int written; | |
449 | 449 |
450 TRACE(("enter writechannel fd %d", fd)) | 450 TRACE(("enter writechannel fd %d", fd)) |
451 | |
452 cbuf_readptrs(cbuf, &circ_p1, &circ_len1, &circ_p2, &circ_len2); | |
453 | |
454 if (circ_len1 > 0) { | |
455 TRACE(("circ1 %d", circ_len1)) | |
456 iov[io_count].iov_base = circ_p1; | |
457 iov[io_count].iov_len = circ_len1; | |
458 io_count++; | |
459 } | |
460 | |
461 if (circ_len2 > 0) { | |
462 TRACE(("circ2 %d", circ_len2)) | |
463 iov[io_count].iov_base = circ_p2; | |
464 iov[io_count].iov_len = circ_len2; | |
465 io_count++; | |
466 } | |
467 | |
468 if (morelen) { | |
469 assert(moredata); | |
470 TRACE(("more %d", *morelen)) | |
471 iov[io_count].iov_base = (void*)moredata; | |
472 iov[io_count].iov_len = *morelen; | |
473 io_count++; | |
474 } | |
475 | |
476 if (morelen) { | |
477 /* Default return value, none consumed */ | |
478 *morelen = 0; | |
479 } | |
480 | |
481 written = writev(fd, iov, io_count); | |
482 | |
483 if (written < 0) { | |
484 if (errno != EINTR && errno != EAGAIN) { | |
485 TRACE(("errno %d len %d", errno, len)) | |
486 close_chan_fd(channel, fd, SHUT_WR); | |
487 } | |
488 } else { | |
489 int cbuf_written = MIN(circ_len1+circ_len2, (unsigned int)written); | |
490 cbuf_incrread(cbuf, cbuf_written); | |
491 if (morelen) { | |
492 *morelen = written - cbuf_written; | |
493 } | |
494 channel->recvdonelen += written; | |
495 } | |
496 | |
497 #if 0 | |
451 | 498 |
452 maxlen = cbuf_readlen(cbuf); | 499 maxlen = cbuf_readlen(cbuf); |
453 | 500 |
454 /* Write the data out */ | 501 /* Write the data out */ |
455 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); | 502 len = write(fd, cbuf_readptr(cbuf, maxlen), maxlen); |
463 } | 510 } |
464 TRACE(("writechannel wrote %d", len)) | 511 TRACE(("writechannel wrote %d", len)) |
465 | 512 |
466 cbuf_incrread(cbuf, len); | 513 cbuf_incrread(cbuf, len); |
467 channel->recvdonelen += len; | 514 channel->recvdonelen += len; |
515 #endif | |
468 | 516 |
469 /* Window adjust handling */ | 517 /* Window adjust handling */ |
470 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { | 518 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { |
471 /* Set it back to max window */ | |
472 send_msg_channel_window_adjust(channel, channel->recvdonelen); | 519 send_msg_channel_window_adjust(channel, channel->recvdonelen); |
473 channel->recvwindow += channel->recvdonelen; | 520 channel->recvwindow += channel->recvdonelen; |
474 channel->recvdonelen = 0; | 521 channel->recvdonelen = 0; |
475 } | 522 } |
476 | 523 |
512 FD_SET(channel->errfd, readfds); | 559 FD_SET(channel->errfd, readfds); |
513 } | 560 } |
514 } | 561 } |
515 | 562 |
516 /* Stuff from the wire */ | 563 /* Stuff from the wire */ |
517 if (channel->initconn | 564 if (channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0) { |
518 ||(channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0)) { | |
519 FD_SET(channel->writefd, writefds); | 565 FD_SET(channel->writefd, writefds); |
520 } | 566 } |
521 | 567 |
522 if (ERRFD_IS_WRITE(channel) && channel->errfd >= 0 | 568 if (ERRFD_IS_WRITE(channel) && channel->errfd >= 0 |
523 && cbuf_getused(channel->extrabuf) > 0) { | 569 && cbuf_getused(channel->extrabuf) > 0) { |
584 | 630 |
585 if (IS_DROPBEAR_SERVER || (channel->writefd != STDOUT_FILENO)) { | 631 if (IS_DROPBEAR_SERVER || (channel->writefd != STDOUT_FILENO)) { |
586 /* close the FDs in case they haven't been done | 632 /* close the FDs in case they haven't been done |
587 * yet (they might have been shutdown etc) */ | 633 * yet (they might have been shutdown etc) */ |
588 TRACE(("CLOSE writefd %d", channel->writefd)) | 634 TRACE(("CLOSE writefd %d", channel->writefd)) |
589 close(channel->writefd); | 635 m_close(channel->writefd); |
590 TRACE(("CLOSE readfd %d", channel->readfd)) | 636 TRACE(("CLOSE readfd %d", channel->readfd)) |
591 close(channel->readfd); | 637 m_close(channel->readfd); |
592 TRACE(("CLOSE errfd %d", channel->errfd)) | 638 TRACE(("CLOSE errfd %d", channel->errfd)) |
593 close(channel->errfd); | 639 m_close(channel->errfd); |
594 } | 640 } |
595 | 641 |
596 if (!channel->close_handler_done | 642 if (!channel->close_handler_done |
597 && channel->type->closehandler) { | 643 && channel->type->closehandler) { |
598 channel->type->closehandler(channel); | 644 channel->type->closehandler(channel); |
599 channel->close_handler_done = 1; | 645 channel->close_handler_done = 1; |
600 } | 646 } |
601 | 647 |
648 if (channel->conn_pending) { | |
649 cancel_connect(channel->conn_pending); | |
650 } | |
651 | |
602 ses.channels[channel->index] = NULL; | 652 ses.channels[channel->index] = NULL; |
603 m_free(channel); | 653 m_free(channel); |
604 ses.chancount--; | 654 ses.chancount--; |
605 | 655 |
606 update_channel_prio(); | 656 update_channel_prio(); |
614 | 664 |
615 struct Channel *channel; | 665 struct Channel *channel; |
616 | 666 |
617 channel = getchannel(); | 667 channel = getchannel(); |
618 | 668 |
619 TRACE(("enter recv_msg_channel_request %p", channel)) | 669 TRACE(("enter recv_msg_channel_request %p", (void*)channel)) |
620 | 670 |
621 if (channel->sent_close) { | 671 if (channel->sent_close) { |
622 TRACE(("leave recv_msg_channel_request: already closed channel")) | 672 TRACE(("leave recv_msg_channel_request: already closed channel")) |
623 return; | 673 return; |
624 } | 674 } |
747 | 797 |
748 unsigned int datalen; | 798 unsigned int datalen; |
749 unsigned int maxdata; | 799 unsigned int maxdata; |
750 unsigned int buflen; | 800 unsigned int buflen; |
751 unsigned int len; | 801 unsigned int len; |
802 unsigned int consumed; | |
752 | 803 |
753 TRACE(("enter recv_msg_channel_data")) | 804 TRACE(("enter recv_msg_channel_data")) |
754 | 805 |
755 if (channel->recv_eof) { | 806 if (channel->recv_eof) { |
756 dropbear_exit("Received data after eof"); | 807 dropbear_exit("Received data after eof"); |
772 * lead to corrupted file transfers etc (chunks missed etc). It's better to | 823 * lead to corrupted file transfers etc (chunks missed etc). It's better to |
773 * just die horribly */ | 824 * just die horribly */ |
774 if (datalen > maxdata) { | 825 if (datalen > maxdata) { |
775 dropbear_exit("Oversized packet"); | 826 dropbear_exit("Oversized packet"); |
776 } | 827 } |
828 | |
829 dropbear_assert(channel->recvwindow >= datalen); | |
830 channel->recvwindow -= datalen; | |
831 dropbear_assert(channel->recvwindow <= opts.recv_window); | |
832 | |
833 consumed = datalen; | |
834 writechannel(channel, fd, cbuf, buf_getptr(ses.payload, datalen), &consumed); | |
835 | |
836 datalen -= consumed; | |
837 buf_incrpos(ses.payload, consumed); | |
838 | |
777 | 839 |
778 /* We may have to run throught twice, if the buffer wraps around. Can't | 840 /* We may have to run throught twice, if the buffer wraps around. Can't |
779 * just "leave it for next time" like with writechannel, since this | 841 * just "leave it for next time" like with writechannel, since this |
780 * is payload data */ | 842 * is payload data */ |
781 len = datalen; | 843 len = datalen; |
787 buf_getptr(ses.payload, buflen), buflen); | 849 buf_getptr(ses.payload, buflen), buflen); |
788 cbuf_incrwrite(cbuf, buflen); | 850 cbuf_incrwrite(cbuf, buflen); |
789 buf_incrpos(ses.payload, buflen); | 851 buf_incrpos(ses.payload, buflen); |
790 len -= buflen; | 852 len -= buflen; |
791 } | 853 } |
792 | |
793 dropbear_assert(channel->recvwindow >= datalen); | |
794 channel->recvwindow -= datalen; | |
795 dropbear_assert(channel->recvwindow <= opts.recv_window); | |
796 | 854 |
797 TRACE(("leave recv_msg_channel_data")) | 855 TRACE(("leave recv_msg_channel_data")) |
798 } | 856 } |
799 | 857 |
800 /* Increment the outgoing data window for a channel - the remote end limits | 858 /* Increment the outgoing data window for a channel - the remote end limits |
999 } else { | 1057 } else { |
1000 closein = 1; | 1058 closein = 1; |
1001 } | 1059 } |
1002 } else { | 1060 } else { |
1003 TRACE(("CLOSE some fd %d", fd)) | 1061 TRACE(("CLOSE some fd %d", fd)) |
1004 close(fd); | 1062 m_close(fd); |
1005 closein = closeout = 1; | 1063 closein = closeout = 1; |
1006 } | 1064 } |
1007 | 1065 |
1008 if (closeout && (fd == channel->readfd)) { | 1066 if (closeout && (fd == channel->readfd)) { |
1009 channel->readfd = FD_CLOSED; | 1067 channel->readfd = FD_CLOSED; |
1022 /* if we called shutdown on it and all references are gone, then we | 1080 /* if we called shutdown on it and all references are gone, then we |
1023 * need to close() it to stop it lingering */ | 1081 * need to close() it to stop it lingering */ |
1024 if (channel->type->sepfds && channel->readfd == FD_CLOSED | 1082 if (channel->type->sepfds && channel->readfd == FD_CLOSED |
1025 && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) { | 1083 && channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) { |
1026 TRACE(("CLOSE (finally) of %d", fd)) | 1084 TRACE(("CLOSE (finally) of %d", fd)) |
1027 close(fd); | 1085 m_close(fd); |
1028 } | 1086 } |
1029 } | 1087 } |
1030 | 1088 |
1031 | 1089 |
1032 #if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT) | 1090 #if defined(USING_LISTENERS) || defined(DROPBEAR_CLIENT) |
1139 buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE); | 1197 buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE); |
1140 encrypt_packet(); | 1198 encrypt_packet(); |
1141 } | 1199 } |
1142 | 1200 |
1143 struct Channel* get_any_ready_channel() { | 1201 struct Channel* get_any_ready_channel() { |
1202 size_t i; | |
1144 if (ses.chancount == 0) { | 1203 if (ses.chancount == 0) { |
1145 return NULL; | 1204 return NULL; |
1146 } | 1205 } |
1147 size_t i; | |
1148 for (i = 0; i < ses.chansize; i++) { | 1206 for (i = 0; i < ses.chansize; i++) { |
1149 struct Channel *chan = ses.channels[i]; | 1207 struct Channel *chan = ses.channels[i]; |
1150 if (chan | 1208 if (chan |
1151 && !(chan->sent_eof || chan->recv_eof) | 1209 && !(chan->sent_eof || chan->recv_eof) |
1152 && !(chan->await_open || chan->initconn)) { | 1210 && !(chan->await_open)) { |
1153 return chan; | 1211 return chan; |
1154 } | 1212 } |
1155 } | 1213 } |
1156 return NULL; | 1214 return NULL; |
1157 } | 1215 } |