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;