Mercurial > dropbear
comparison common-channel.c @ 362:1c7bf9cec6c8 channel-fix
Rearranged some more bits, marked some areas that need work.
* send_msg_channel_data() no longer allocates a separate buffer
* getchannel() handles unknown channels so callers don't have to
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Mon, 02 Oct 2006 16:34:06 +0000 |
parents | 78518751cb82 |
children | 6ba2894ec8d5 |
comparison
equal
deleted
inserted
replaced
359:78518751cb82 | 362:1c7bf9cec6c8 |
---|---|
162 | 162 |
163 return newchan; | 163 return newchan; |
164 } | 164 } |
165 | 165 |
166 /* Returns the channel structure corresponding to the channel in the current | 166 /* Returns the channel structure corresponding to the channel in the current |
167 * data packet (ses.payload must be positioned appropriately) */ | 167 * data packet (ses.payload must be positioned appropriately). |
168 struct Channel* getchannel() { | 168 * A valid channel is always returns, it will fail fatally with an unknown |
169 * channel */ | |
170 static struct Channel* getchannel_msg(const char* kind) { | |
169 | 171 |
170 unsigned int chan; | 172 unsigned int chan; |
171 | 173 |
172 chan = buf_getint(ses.payload); | 174 chan = buf_getint(ses.payload); |
173 if (chan >= ses.chansize || ses.channels[chan] == NULL) { | 175 if (chan >= ses.chansize || ses.channels[chan] == NULL) { |
174 return NULL; | 176 if (kind) { |
177 dropbear_exit("%s for unknown channel %d", kind, chan); | |
178 } else { | |
179 dropbear_exit("Unknown channel %d", chan); | |
180 } | |
175 } | 181 } |
176 return ses.channels[chan]; | 182 return ses.channels[chan]; |
183 } | |
184 | |
185 struct Channel* getchannel() { | |
186 return getchannel_msg(NULL); | |
177 } | 187 } |
178 | 188 |
179 /* Iterate through the channels, performing IO if available */ | 189 /* Iterate through the channels, performing IO if available */ |
180 void channelio(fd_set *readfds, fd_set *writefds) { | 190 void channelio(fd_set *readfds, fd_set *writefds) { |
181 | 191 |
182 struct Channel *channel; | 192 struct Channel *channel; |
183 unsigned int i; | 193 unsigned int i; |
184 int ret; | |
185 | 194 |
186 /* iterate through all the possible channels */ | 195 /* iterate through all the possible channels */ |
187 for (i = 0; i < ses.chansize; i++) { | 196 for (i = 0; i < ses.chansize; i++) { |
188 | 197 |
189 channel = ses.channels[i]; | 198 channel = ses.channels[i]; |
216 #endif | 225 #endif |
217 | 226 |
218 /* write to program/pipe stdin */ | 227 /* write to program/pipe stdin */ |
219 if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) { | 228 if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) { |
220 if (channel->initconn) { | 229 if (channel->initconn) { |
230 /* XXX could this go somewhere cleaner? */ | |
221 check_in_progress(channel); | 231 check_in_progress(channel); |
222 continue; /* Important not to use the channel after | 232 continue; /* Important not to use the channel after |
223 check_in_progress(), as it may be NULL */ | 233 check_in_progress(), as it may be NULL */ |
224 } | 234 } |
225 writechannel(channel, channel->writefd, channel->writebuf); | 235 writechannel(channel, channel->writefd, channel->writebuf); |
252 TRACE(("writebuf size %d extrabuf ptr 0x%x extrabuf size %d", | 262 TRACE(("writebuf size %d extrabuf ptr 0x%x extrabuf size %d", |
253 cbuf_getused(channel->writebuf), | 263 cbuf_getused(channel->writebuf), |
254 channel->writebuf, | 264 channel->writebuf, |
255 channel->writebuf ? 0 : cbuf_getused(channel->extrabuf))) | 265 channel->writebuf ? 0 : cbuf_getused(channel->extrabuf))) |
256 | 266 |
267 /* XXX not good, doesn't flush out */ | |
268 if (channel->recv_close) { | |
269 if (! channel->sent_close) { | |
270 TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")) | |
271 send_msg_channel_close(channel); | |
272 } | |
273 remove_channel(channel); | |
274 return; | |
275 } | |
276 | |
257 /* server chansession channels are special, since readfd mightn't | 277 /* server chansession channels are special, since readfd mightn't |
258 * close in the case of "sleep 4 & echo blah" until the sleep is up */ | 278 * close in the case of "sleep 4 & echo blah" until the sleep is up */ |
259 if (channel->type->check_close) { | 279 if (channel->type->check_close) { |
260 if (channel->type->check_close(channel)) { | 280 if (channel->type->check_close(channel)) { |
261 close_write_fd(channel); | 281 close_write_fd(channel); |
273 if (!channel->sent_close | 293 if (!channel->sent_close |
274 && channel->writefd == FD_CLOSED | 294 && channel->writefd == FD_CLOSED |
275 && channel->readfd == FD_CLOSED | 295 && channel->readfd == FD_CLOSED |
276 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { | 296 && (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) { |
277 send_msg_channel_close(channel); | 297 send_msg_channel_close(channel); |
298 } | |
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); | |
278 } | 306 } |
279 | 307 |
280 /* When either party wishes to terminate the channel, it sends | 308 /* When either party wishes to terminate the channel, it sends |
281 * SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST | 309 * SSH_MSG_CHANNEL_CLOSE. Upon receiving this message, a party MUST |
282 * send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this | 310 * send back a SSH_MSG_CHANNEL_CLOSE unless it has already sent this |
285 * the party may then reuse the channel number. A party MAY send | 313 * the party may then reuse the channel number. A party MAY send |
286 * SSH_MSG_CHANNEL_CLOSE without having sent or received | 314 * SSH_MSG_CHANNEL_CLOSE without having sent or received |
287 * SSH_MSG_CHANNEL_EOF. | 315 * SSH_MSG_CHANNEL_EOF. |
288 * (from draft-ietf-secsh-connect) | 316 * (from draft-ietf-secsh-connect) |
289 */ | 317 */ |
290 if (channel->recv_close) { | |
291 if (! channel->sent_close) { | |
292 TRACE(("Sending MSG_CHANNEL_CLOSE in response to same.")) | |
293 send_msg_channel_close(channel); | |
294 } | |
295 remove_channel(channel); | |
296 } | |
297 } | 318 } |
298 | 319 |
299 | 320 |
300 /* Check whether a deferred (EINPROGRESS) connect() was successful, and | 321 /* Check whether a deferred (EINPROGRESS) connect() was successful, and |
301 * if so, set up the channel properly. Otherwise, the channel is cleaned up, so | 322 * if so, set up the channel properly. Otherwise, the channel is cleaned up, so |
323 TRACE(("leave check_in_progress: success")) | 344 TRACE(("leave check_in_progress: success")) |
324 } | 345 } |
325 } | 346 } |
326 | 347 |
327 | 348 |
328 | |
329 /* Send the close message and set the channel as closed */ | 349 /* Send the close message and set the channel as closed */ |
330 static void send_msg_channel_close(struct Channel *channel) { | 350 static void send_msg_channel_close(struct Channel *channel) { |
331 | 351 |
332 TRACE(("enter send_msg_channel_close")) | 352 TRACE(("enter send_msg_channel_close")) |
333 if (channel->type->closehandler) { | 353 if (channel->type->closehandler) { |
339 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE); | 359 buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_CLOSE); |
340 buf_putint(ses.writepayload, channel->remotechan); | 360 buf_putint(ses.writepayload, channel->remotechan); |
341 | 361 |
342 encrypt_packet(); | 362 encrypt_packet(); |
343 | 363 |
364 /* XXX is setting sent_eof required? */ | |
344 channel->sent_eof = 1; | 365 channel->sent_eof = 1; |
345 channel->sent_close = 1; | 366 channel->sent_close = 1; |
346 TRACE(("leave send_msg_channel_close")) | 367 TRACE(("leave send_msg_channel_close")) |
347 } | 368 } |
348 | 369 |
386 } | 407 } |
387 | 408 |
388 cbuf_incrread(cbuf, len); | 409 cbuf_incrread(cbuf, len); |
389 channel->recvdonelen += len; | 410 channel->recvdonelen += len; |
390 | 411 |
391 if (fd == channel->writefd && cbuf_getused(cbuf) == 0 && channel->recv_eof) { | |
392 /* Check if we're closing up */ | |
393 close_write_fd(channel); | |
394 TRACE(("leave writechannel: recv_eof set")) | |
395 return; | |
396 } | |
397 | |
398 /* Window adjust handling */ | 412 /* Window adjust handling */ |
399 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { | 413 if (channel->recvdonelen >= RECV_WINDOWEXTEND) { |
400 /* Set it back to max window */ | 414 /* Set it back to max window */ |
401 send_msg_channel_window_adjust(channel, channel->recvdonelen); | 415 send_msg_channel_window_adjust(channel, channel->recvdonelen); |
402 channel->recvwindow += channel->recvdonelen; | 416 channel->recvwindow += channel->recvdonelen; |
405 | 419 |
406 dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW); | 420 dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW); |
407 dropbear_assert(channel->recvwindow <= cbuf_getavail(channel->writebuf)); | 421 dropbear_assert(channel->recvwindow <= cbuf_getavail(channel->writebuf)); |
408 dropbear_assert(channel->extrabuf == NULL || | 422 dropbear_assert(channel->extrabuf == NULL || |
409 channel->recvwindow <= cbuf_getavail(channel->extrabuf)); | 423 channel->recvwindow <= cbuf_getavail(channel->extrabuf)); |
410 | |
411 | 424 |
412 TRACE(("leave writechannel")) | 425 TRACE(("leave writechannel")) |
413 } | 426 } |
414 | 427 |
415 /* Set the file descriptors for the main select in session.c | 428 /* Set the file descriptors for the main select in session.c |
464 | 477 |
465 struct Channel * channel; | 478 struct Channel * channel; |
466 | 479 |
467 TRACE(("enter recv_msg_channel_eof")) | 480 TRACE(("enter recv_msg_channel_eof")) |
468 | 481 |
469 channel = getchannel(); | 482 channel = getchannel_msg("EOF"); |
470 if (channel == NULL) { | |
471 dropbear_exit("EOF for unknown channel"); | |
472 } | |
473 | 483 |
474 channel->recv_eof = 1; | 484 channel->recv_eof = 1; |
475 if (cbuf_getused(channel->writebuf) == 0 | 485 |
476 && (channel->extrabuf == NULL | 486 check_close(channel); |
477 || cbuf_getused(channel->extrabuf) == 0)) { | |
478 close_write_fd(channel); | |
479 } | |
480 | |
481 TRACE(("leave recv_msg_channel_eof")) | 487 TRACE(("leave recv_msg_channel_eof")) |
482 } | 488 } |
483 | 489 |
484 | 490 |
485 /* Handle channel closure(), respond in kind and close the channels */ | 491 /* Handle channel closure(), respond in kind and close the channels */ |
487 | 493 |
488 struct Channel * channel; | 494 struct Channel * channel; |
489 | 495 |
490 TRACE(("enter recv_msg_channel_close")) | 496 TRACE(("enter recv_msg_channel_close")) |
491 | 497 |
492 channel = getchannel(); | 498 channel = getchannel_msg("Close"); |
493 if (channel == NULL) { | 499 |
494 /* disconnect ? */ | 500 /* XXX eof required? */ |
495 dropbear_exit("Close for unknown channel"); | |
496 } | |
497 | |
498 channel->recv_eof = 1; | 501 channel->recv_eof = 1; |
499 channel->recv_close = 1; | 502 channel->recv_close = 1; |
500 | 503 |
501 if (channel->sent_close) { | 504 check_close(channel); |
502 remove_channel(channel); | |
503 } | |
504 | |
505 TRACE(("leave recv_msg_channel_close")) | 505 TRACE(("leave recv_msg_channel_close")) |
506 } | 506 } |
507 | 507 |
508 /* Remove a channel entry, this is only executed after both sides have sent | 508 /* Remove a channel entry, this is only executed after both sides have sent |
509 * channel close */ | 509 * channel close */ |
510 static void remove_channel(struct Channel * channel) { | 510 static void remove_channel(struct Channel * channel) { |
511 | 511 |
512 TRACE(("enter remove_channel")) | 512 TRACE(("enter remove_channel")) |
513 TRACE(("channel index is %d", channel->index)) | 513 TRACE(("channel index is %d", channel->index)) |
514 | 514 |
515 /* XXX shuold we assert for sent_closed and recv_closed? | |
516 * but we also cleanup manually, maybe we need a flag. */ | |
517 | |
515 cbuf_free(channel->writebuf); | 518 cbuf_free(channel->writebuf); |
516 channel->writebuf = NULL; | 519 channel->writebuf = NULL; |
517 | 520 |
518 if (channel->extrabuf) { | 521 if (channel->extrabuf) { |
519 cbuf_free(channel->extrabuf); | 522 cbuf_free(channel->extrabuf); |
520 channel->extrabuf = NULL; | 523 channel->extrabuf = NULL; |
521 } | 524 } |
522 | 525 |
523 | 526 |
524 /* close the FDs in case they haven't been done | 527 /* close the FDs in case they haven't been done |
525 * yet (ie they were shutdown etc */ | 528 * yet (they might have been shutdown etc) */ |
526 close(channel->writefd); | 529 close(channel->writefd); |
527 close(channel->readfd); | 530 close(channel->readfd); |
528 close(channel->errfd); | 531 close(channel->errfd); |
529 | 532 |
530 channel->typedata = NULL; | 533 channel->typedata = NULL; |
551 struct Channel *channel; | 554 struct Channel *channel; |
552 | 555 |
553 TRACE(("enter recv_msg_channel_request")) | 556 TRACE(("enter recv_msg_channel_request")) |
554 | 557 |
555 channel = getchannel(); | 558 channel = getchannel(); |
556 if (channel == NULL) { | |
557 /* disconnect ? */ | |
558 dropbear_exit("Unknown channel"); | |
559 } | |
560 | 559 |
561 if (channel->type->reqhandler) { | 560 if (channel->type->reqhandler) { |
562 channel->type->reqhandler(channel); | 561 channel->type->reqhandler(channel); |
563 } else { | 562 } else { |
564 send_msg_channel_failure(channel); | 563 send_msg_channel_failure(channel); |
574 * if it is extended data. if it is extended, then the type is in | 573 * if it is extended data. if it is extended, then the type is in |
575 * exttype */ | 574 * exttype */ |
576 static void send_msg_channel_data(struct Channel *channel, int isextended, | 575 static void send_msg_channel_data(struct Channel *channel, int isextended, |
577 unsigned int exttype) { | 576 unsigned int exttype) { |
578 | 577 |
579 buffer *buf; | |
580 int len; | 578 int len; |
581 unsigned int maxlen; | 579 size_t maxlen, size_pos; |
582 int fd; | 580 int fd; |
583 | 581 |
584 /* TRACE(("enter send_msg_channel_data")) | 582 /* TRACE(("enter send_msg_channel_data")) |
585 TRACE(("extended = %d type = %d", isextended, exttype))*/ | 583 TRACE(("extended = %d type = %d", isextended, exttype))*/ |
586 | 584 |
598 maxlen = MIN(channel->transwindow, channel->transmaxpacket); | 596 maxlen = MIN(channel->transwindow, channel->transmaxpacket); |
599 /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and | 597 /* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and |
600 * exttype if is extended */ | 598 * exttype if is extended */ |
601 maxlen = MIN(maxlen, | 599 maxlen = MIN(maxlen, |
602 ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0)); | 600 ses.writepayload->size - 1 - 4 - 4 - (isextended ? 4 : 0)); |
601 TRACE(("maxlen %d", maxlen)) | |
603 if (maxlen == 0) { | 602 if (maxlen == 0) { |
604 TRACE(("leave send_msg_channel_data: no window")) | 603 TRACE(("leave send_msg_channel_data: no window")) |
605 return; /* the data will get written later */ | 604 return; |
606 } | 605 } |
606 | |
607 buf_putbyte(ses.writepayload, | |
608 isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA); | |
609 buf_putint(ses.writepayload, channel->remotechan); | |
610 if (isextended) { | |
611 buf_putint(ses.writepayload, exttype); | |
612 } | |
613 /* a dummy size first ...*/ | |
614 size_pos = ses.writepayload->pos; | |
615 buf_putint(ses.writepayload, 0); | |
607 | 616 |
608 /* read the data */ | 617 /* read the data */ |
609 TRACE(("maxlen %d", maxlen)) | 618 len = read(fd, buf_getwriteptr(ses.writepayload, maxlen), maxlen); |
610 buf = buf_new(maxlen); | |
611 TRACE(("buf pos %d data %x", buf->pos, buf->data)) | |
612 len = read(fd, buf_getwriteptr(buf, maxlen), maxlen); | |
613 if (len <= 0) { | 619 if (len <= 0) { |
614 /* on error/eof, send eof */ | |
615 if (len == 0 || errno != EINTR) { | 620 if (len == 0 || errno != EINTR) { |
616 close_read_fd(channel, fd); | 621 close_read_fd(channel, fd); |
617 } | 622 } |
618 buf_free(buf); | 623 ses.writepayload->len = ses.writepayload->pos = 0; |
619 buf = NULL; | |
620 TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", | 624 TRACE(("leave send_msg_channel_data: read err or EOF for fd %d", |
621 channel->index)); | 625 channel->index)); |
622 return; | 626 return; |
623 } | 627 } |
624 buf_incrlen(buf, len); | 628 buf_incrwritepos(ses.writepayload, len); |
625 | 629 /* ... real size here */ |
626 buf_putbyte(ses.writepayload, | 630 buf_setpos(ses.writepayload, size_pos); |
627 isextended ? SSH_MSG_CHANNEL_EXTENDED_DATA : SSH_MSG_CHANNEL_DATA); | 631 buf_putint(ses.writepayload, len); |
628 buf_putint(ses.writepayload, channel->remotechan); | |
629 | |
630 if (isextended) { | |
631 buf_putint(ses.writepayload, exttype); | |
632 } | |
633 | |
634 buf_putstring(ses.writepayload, buf_getptr(buf, len), len); | |
635 buf_free(buf); | |
636 buf = NULL; | |
637 | 632 |
638 channel->transwindow -= len; | 633 channel->transwindow -= len; |
639 | 634 |
640 encrypt_packet(); | 635 encrypt_packet(); |
641 TRACE(("leave send_msg_channel_data")) | 636 TRACE(("leave send_msg_channel_data")) |
645 void recv_msg_channel_data() { | 640 void recv_msg_channel_data() { |
646 | 641 |
647 struct Channel *channel; | 642 struct Channel *channel; |
648 | 643 |
649 channel = getchannel(); | 644 channel = getchannel(); |
650 if (channel == NULL) { | |
651 dropbear_exit("Unknown channel"); | |
652 } | |
653 | 645 |
654 common_recv_msg_channel_data(channel, channel->writefd, channel->writebuf); | 646 common_recv_msg_channel_data(channel, channel->writefd, channel->writebuf); |
655 } | 647 } |
656 | 648 |
657 /* Shared for data and stderr data - when we receive data, put it in a buffer | 649 /* Shared for data and stderr data - when we receive data, put it in a buffer |
724 | 716 |
725 struct Channel * channel; | 717 struct Channel * channel; |
726 unsigned int incr; | 718 unsigned int incr; |
727 | 719 |
728 channel = getchannel(); | 720 channel = getchannel(); |
729 if (channel == NULL) { | |
730 dropbear_exit("Unknown channel"); | |
731 } | |
732 | 721 |
733 incr = buf_getint(ses.payload); | 722 incr = buf_getint(ses.payload); |
734 TRACE(("received window increment %d", incr)) | 723 TRACE(("received window increment %d", incr)) |
735 incr = MIN(incr, MAX_TRANS_WIN_INCR); | 724 incr = MIN(incr, MAX_TRANS_WIN_INCR); |
736 | 725 |
784 } | 773 } |
785 | 774 |
786 /* Get the channel type. Client and server style invokation will set up a | 775 /* Get the channel type. Client and server style invokation will set up a |
787 * different list for ses.chantypes at startup. We just iterate through | 776 * different list for ses.chantypes at startup. We just iterate through |
788 * this list and find the matching name */ | 777 * this list and find the matching name */ |
778 /* XXX fugly */ | |
789 for (cp = &ses.chantypes[0], chantype = (*cp); | 779 for (cp = &ses.chantypes[0], chantype = (*cp); |
790 chantype != NULL; | 780 chantype != NULL; |
791 cp++, chantype = (*cp)) { | 781 cp++, chantype = (*cp)) { |
792 if (strcmp(type, chantype->name) == 0) { | 782 if (strcmp(type, chantype->name) == 0) { |
793 break; | 783 break; |
809 goto failure; | 799 goto failure; |
810 } | 800 } |
811 | 801 |
812 if (channel->type->inithandler) { | 802 if (channel->type->inithandler) { |
813 ret = channel->type->inithandler(channel); | 803 ret = channel->type->inithandler(channel); |
804 if (ret == SSH_OPEN_IN_PROGRESS) { | |
805 /* We'll send the confirmation later */ | |
806 goto cleanup; | |
807 } | |
814 if (ret > 0) { | 808 if (ret > 0) { |
815 if (ret == SSH_OPEN_IN_PROGRESS) { | |
816 /* We'll send the confirmation later */ | |
817 goto cleanup; | |
818 } | |
819 errtype = ret; | 809 errtype = ret; |
820 delete_channel(channel); | 810 delete_channel(channel); |
821 TRACE(("inithandler returned failure %d", ret)) | 811 TRACE(("inithandler returned failure %d", ret)) |
822 goto failure; | 812 goto failure; |
823 } | 813 } |
947 int ret; | 937 int ret; |
948 | 938 |
949 TRACE(("enter recv_msg_channel_open_confirmation")) | 939 TRACE(("enter recv_msg_channel_open_confirmation")) |
950 | 940 |
951 channel = getchannel(); | 941 channel = getchannel(); |
952 if (channel == NULL) { | |
953 dropbear_exit("Unknown channel"); | |
954 } | |
955 | 942 |
956 if (!channel->await_open) { | 943 if (!channel->await_open) { |
957 dropbear_exit("unexpected channel reply"); | 944 dropbear_exit("unexpected channel reply"); |
958 } | 945 } |
959 channel->await_open = 0; | 946 channel->await_open = 0; |
982 void recv_msg_channel_open_failure() { | 969 void recv_msg_channel_open_failure() { |
983 | 970 |
984 struct Channel * channel; | 971 struct Channel * channel; |
985 | 972 |
986 channel = getchannel(); | 973 channel = getchannel(); |
987 if (channel == NULL) { | |
988 dropbear_exit("Unknown channel"); | |
989 } | |
990 | 974 |
991 if (!channel->await_open) { | 975 if (!channel->await_open) { |
992 dropbear_exit("unexpected channel reply"); | 976 dropbear_exit("unexpected channel reply"); |
993 } | 977 } |
994 channel->await_open = 0; | 978 channel->await_open = 0; |