Mercurial > dropbear
comparison common-channel.c @ 107:d3eb1fa8484e
Nasty.
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Tue, 24 Aug 2004 18:12:18 +0000 |
parents | b0316ce64e4b |
children | 10f4d3319780 |
comparison
equal
deleted
inserted
replaced
106:e13f8a712a1c | 107:d3eb1fa8484e |
---|---|
1 /* | 1 /* |
2 * Dropbear - a SSH2 server | 2 * Dropbear SSH |
3 * | 3 * |
4 * Copyright (c) 2002,2003 Matt Johnston | 4 * Copyright (c) 2002-2004 Matt Johnston |
5 * All rights reserved. | 5 * All rights reserved. |
6 * | 6 * |
7 * Permission is hereby granted, free of charge, to any person obtaining a copy | 7 * Permission is hereby granted, free of charge, to any person obtaining a copy |
8 * of this software and associated documentation files (the "Software"), to deal | 8 * of this software and associated documentation files (the "Software"), to deal |
9 * in the Software without restriction, including without limitation the rights | 9 * in the Software without restriction, including without limitation the rights |
27 #include "includes.h" | 27 #include "includes.h" |
28 #include "session.h" | 28 #include "session.h" |
29 #include "packet.h" | 29 #include "packet.h" |
30 #include "ssh.h" | 30 #include "ssh.h" |
31 #include "buffer.h" | 31 #include "buffer.h" |
32 #include "circbuffer.h" | |
32 #include "dbutil.h" | 33 #include "dbutil.h" |
33 #include "channel.h" | 34 #include "channel.h" |
34 #include "ssh.h" | 35 #include "ssh.h" |
35 #include "listener.h" | 36 #include "listener.h" |
36 | 37 |
145 newchan->infd = FD_UNINIT; | 146 newchan->infd = FD_UNINIT; |
146 newchan->outfd = FD_UNINIT; | 147 newchan->outfd = FD_UNINIT; |
147 newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ | 148 newchan->errfd = FD_CLOSED; /* this isn't always set to start with */ |
148 newchan->initconn = 0; | 149 newchan->initconn = 0; |
149 | 150 |
150 newchan->writebuf = buf_new(RECV_MAXWINDOW); | 151 newchan->writebuf = cbuf_new(RECV_MAXWINDOW); |
152 newchan->extrabuf = NULL; /* The user code can set it up */ | |
151 newchan->recvwindow = RECV_MAXWINDOW; | 153 newchan->recvwindow = RECV_MAXWINDOW; |
152 newchan->recvmaxpacket = RECV_MAXPACKET; | 154 newchan->recvmaxpacket = RECV_MAXPACKET; |
153 | 155 |
154 ses.channels[i] = newchan; | 156 ses.channels[i] = newchan; |
155 ses.chancount++; | 157 ses.chancount++; |
158 | 160 |
159 return newchan; | 161 return newchan; |
160 } | 162 } |
161 | 163 |
162 /* Get the channel structure corresponding to a channel number */ | 164 /* Get the channel structure corresponding to a channel number */ |
163 static struct Channel* getchannel(unsigned int chan) { | 165 struct Channel* getchannel(unsigned int chan) { |
164 if (chan >= ses.chansize || ses.channels[chan] == NULL) { | 166 if (chan >= ses.chansize || ses.channels[chan] == NULL) { |
165 return NULL; | 167 return NULL; |
166 } | 168 } |
167 return ses.channels[chan]; | 169 return ses.channels[chan]; |
168 } | 170 } |
343 channel->senteof = 1; | 345 channel->senteof = 1; |
344 | 346 |
345 TRACE(("leave send_msg_channel_eof")); | 347 TRACE(("leave send_msg_channel_eof")); |
346 } | 348 } |
347 | 349 |
348 /* Called to write data out to the server side of a channel (eg a shell or a | 350 /* Called to write data out to the local side of the channel. |
349 * program. | |
350 * Only called when we know we can write to a channel, writes as much as | 351 * Only called when we know we can write to a channel, writes as much as |
351 * possible */ | 352 * possible */ |
352 static void writechannel(struct Channel* channel) { | 353 static void writechannel(struct Channel* channel) { |
353 | 354 |
354 int len, maxlen; | 355 int len, maxlen; |
355 buffer *buf; | 356 circbuffer *cbuf; |
356 | 357 |
357 TRACE(("enter writechannel")); | 358 TRACE(("enter writechannel")); |
358 | 359 |
359 buf = channel->writebuf; | 360 cbuf = channel->writebuf; |
360 maxlen = buf->len - buf->pos; | 361 maxlen = cbuf_readlen(cbuf); |
361 | 362 |
362 len = write(channel->infd, buf_getptr(buf, maxlen), maxlen); | 363 TRACE(("maxlen = %d", maxlen)); |
364 | |
365 /* Write the data out */ | |
366 len = write(channel->infd, cbuf_readptr(cbuf, maxlen), maxlen); | |
363 if (len <= 0) { | 367 if (len <= 0) { |
364 if (len < 0 && errno != EINTR) { | 368 if (len < 0 && errno != EINTR) { |
365 /* no more to write */ | 369 /* no more to write */ |
366 closeinfd(channel); | 370 closeinfd(channel); |
367 } | 371 } |
368 TRACE(("leave writechannel: len <= 0")); | 372 TRACE(("leave writechannel: len <= 0")); |
369 return; | 373 return; |
370 } | 374 } |
371 | 375 |
372 if (len == maxlen) { | 376 TRACE(("len = %d", len)); |
373 buf_setpos(buf, 0); | 377 cbuf_incrread(cbuf, len); |
374 buf_setlen(buf, 0); | 378 |
375 | 379 if (len == maxlen && channel->recveof) { |
376 if (channel->recveof) { | 380 /* Check if we're closing up */ |
377 /* we're closing up */ | 381 closeinfd(channel); |
378 closeinfd(channel); | 382 return; |
379 return; | 383 TRACE(("leave writechannel: recveof set")); |
380 TRACE(("leave writechannel: recveof set")); | 384 |
381 } | 385 } |
382 | 386 |
383 /* extend the window if we're at the end*/ | 387 /* Window adjust handling */ |
384 /* TODO - this is inefficient */ | 388 if (channel->recvwindow < (RECV_MAXWINDOW - RECV_WINDOWEXTEND)) { |
385 send_msg_channel_window_adjust(channel, buf->size | 389 /* Set it back to max window */ |
386 - channel->recvwindow); | 390 send_msg_channel_window_adjust(channel, RECV_MAXWINDOW - |
387 channel->recvwindow = buf->size; | 391 channel->recvwindow); |
388 } else { | 392 channel->recvwindow = RECV_MAXWINDOW; |
389 buf_incrpos(buf, len); | 393 } |
390 } | 394 |
395 | |
391 TRACE(("leave writechannel")); | 396 TRACE(("leave writechannel")); |
392 } | 397 } |
393 | 398 |
394 /* Set the file descriptors for the main select in session.c | 399 /* Set the file descriptors for the main select in session.c |
395 * This avoid channels which don't have any window available, are closed, etc*/ | 400 * This avoid channels which don't have any window available, are closed, etc*/ |
403 channel = ses.channels[i]; | 408 channel = ses.channels[i]; |
404 if (channel == NULL) { | 409 if (channel == NULL) { |
405 continue; | 410 continue; |
406 } | 411 } |
407 | 412 |
408 /* stdout and stderr */ | 413 /* Stuff to put over the wire */ |
409 if (channel->transwindow > 0) { | 414 if (channel->transwindow > 0) { |
410 | 415 |
411 /* stdout */ | |
412 if (channel->outfd >= 0) { | 416 if (channel->outfd >= 0) { |
413 /* there's space to read more from the program */ | |
414 FD_SET(channel->outfd, readfd); | 417 FD_SET(channel->outfd, readfd); |
415 } | 418 } |
416 /* stderr */ | 419 |
417 if (channel->errfd >= 0) { | 420 if (channel->extrabuf == NULL && channel->errfd >= 0) { |
418 FD_SET(channel->errfd, readfd); | 421 FD_SET(channel->errfd, readfd); |
419 } | 422 } |
420 } | 423 } |
421 | 424 |
425 /* For checking FD status (ie closure etc) - we don't actually | |
426 * read data from infd */ | |
422 if (channel->infd >= 0 && channel->infd != channel->outfd) { | 427 if (channel->infd >= 0 && channel->infd != channel->outfd) { |
423 FD_SET(channel->infd, readfd); | 428 FD_SET(channel->infd, readfd); |
424 } | 429 } |
425 | 430 |
426 /* stdin */ | 431 /* Stuff from the wire, to local program/shell/user etc */ |
427 if (channel->infd >= 0 && | 432 if ((channel->infd >= 0 && cbuf_getused(channel->writebuf) > 0 ) |
428 (channel->writebuf->pos < channel->writebuf->len || | 433 || channel->initconn) { |
429 channel->initconn)) { | 434 |
430 /* there's space to write more to the program */ | 435 FD_SET(channel->infd, writefd); |
431 FD_SET(channel->infd, writefd); | 436 } |
432 } | 437 |
438 /* | |
439 if (channel->extrabuf != NULL && channel->errfd >= 0 | |
440 && cbuf_getavail(channel->extrabuf) > 0 ) { | |
441 FD_SET(channel->errfd, writefd); | |
442 } | |
443 */ | |
433 | 444 |
434 } /* foreach channel */ | 445 } /* foreach channel */ |
435 | 446 |
436 #ifdef USING_LISTENERS | 447 #ifdef USING_LISTENERS |
437 set_listener_fds(readfd); | 448 set_listener_fds(readfd); |
455 if (channel == NULL) { | 466 if (channel == NULL) { |
456 dropbear_exit("EOF for unknown channel"); | 467 dropbear_exit("EOF for unknown channel"); |
457 } | 468 } |
458 | 469 |
459 channel->recveof = 1; | 470 channel->recveof = 1; |
460 if (channel->writebuf->len == 0) { | 471 if (cbuf_getused(channel->writebuf) == 0) { |
461 closeinfd(channel); | 472 closeinfd(channel); |
462 } | 473 } |
463 | 474 |
464 TRACE(("leave recv_msg_channel_eof")); | 475 TRACE(("leave recv_msg_channel_eof")); |
465 } | 476 } |
497 static void removechannel(struct Channel * channel) { | 508 static void removechannel(struct Channel * channel) { |
498 | 509 |
499 TRACE(("enter removechannel")); | 510 TRACE(("enter removechannel")); |
500 TRACE(("channel index is %d", channel->index)); | 511 TRACE(("channel index is %d", channel->index)); |
501 | 512 |
502 buf_free(channel->writebuf); | 513 cbuf_free(channel->writebuf); |
503 channel->writebuf = NULL; | 514 channel->writebuf = NULL; |
515 | |
516 if (channel->extrabuf) { | |
517 cbuf_free(channel->extrabuf); | |
518 channel->extrabuf = NULL; | |
519 } | |
520 | |
504 | 521 |
505 /* close the FDs in case they haven't been done | 522 /* close the FDs in case they haven't been done |
506 * yet (ie they were shutdown etc */ | 523 * yet (ie they were shutdown etc */ |
507 close(channel->infd); | 524 close(channel->infd); |
508 close(channel->outfd); | 525 close(channel->outfd); |
621 | 638 |
622 encrypt_packet(); | 639 encrypt_packet(); |
623 TRACE(("leave send_msg_channel_data")); | 640 TRACE(("leave send_msg_channel_data")); |
624 } | 641 } |
625 | 642 |
626 | 643 /* We receive channel data */ |
627 /* when we receive channel data, put it in a buffer for writing to the program/ | |
628 * shell etc */ | |
629 void recv_msg_channel_data() { | 644 void recv_msg_channel_data() { |
630 | 645 |
631 unsigned int chan; | 646 unsigned int chan; |
632 struct Channel * channel; | 647 struct Channel *channel; |
633 unsigned int datalen; | 648 |
634 unsigned int pos; | |
635 unsigned int maxdata; | |
636 | |
637 TRACE(("enter recv_msg_channel_data")); | |
638 | |
639 chan = buf_getint(ses.payload); | 649 chan = buf_getint(ses.payload); |
640 channel = getchannel(chan); | 650 channel = getchannel(chan); |
651 | |
641 if (channel == NULL) { | 652 if (channel == NULL) { |
642 dropbear_exit("Unknown channel"); | 653 dropbear_exit("Unknown channel"); |
643 } | 654 } |
644 | 655 |
656 common_recv_msg_channel_data(channel, channel->infd, channel->writebuf); | |
657 } | |
658 | |
659 /* Shared for data and stderr data - when we receive data, put it in a buffer | |
660 * for writing to the local file descriptor */ | |
661 void common_recv_msg_channel_data(struct Channel *channel, int fd, | |
662 circbuffer * cbuf) { | |
663 | |
664 unsigned int datalen; | |
665 unsigned int maxdata; | |
666 unsigned int buflen; | |
667 unsigned int len; | |
668 | |
669 TRACE(("enter recv_msg_channel_data")); | |
670 | |
645 if (channel->recveof) { | 671 if (channel->recveof) { |
646 dropbear_exit("received data after eof"); | 672 dropbear_exit("received data after eof"); |
647 } | 673 } |
648 | 674 |
649 if (channel->infd < 0) { | 675 if (fd < 0) { |
650 dropbear_exit("received data with bad infd"); | 676 dropbear_exit("received data with bad infd"); |
651 } | 677 } |
652 | 678 |
653 datalen = buf_getint(ses.payload); | 679 datalen = buf_getint(ses.payload); |
680 | |
681 TRACE(("datalen = %d", datalen)); | |
654 | 682 |
655 /* if the client is going to send us more data than we've allocated, then | 683 /* if the client is going to send us more data than we've allocated, then |
656 * it has ignored the windowsize, so we "MAY ignore all extra data" */ | 684 * it has ignored the windowsize, so we "MAY ignore all extra data" */ |
657 maxdata = channel->writebuf->size - channel->writebuf->pos; | 685 maxdata = cbuf_getavail(cbuf); |
686 TRACE(("maxdata = %d", maxdata)); | |
658 if (datalen > maxdata) { | 687 if (datalen > maxdata) { |
659 TRACE(("Warning: recv_msg_channel_data: extra data past window")); | 688 TRACE(("Warning: recv_msg_channel_data: extra data past window")); |
660 datalen = maxdata; | 689 datalen = maxdata; |
661 } | 690 } |
662 | 691 |
663 /* write to the buffer - we always append to the end of the buffer */ | 692 |
664 pos = channel->writebuf->pos; | 693 /* We may have to run throught twice, if the buffer wraps around. Can't |
665 buf_setpos(channel->writebuf, channel->writebuf->len); | 694 * just "leave it for next time" like with writechannel, since this |
666 memcpy(buf_getwriteptr(channel->writebuf, datalen), | 695 * is payload data */ |
667 buf_getptr(ses.payload, datalen), datalen); | 696 len = datalen; |
668 buf_incrwritepos(channel->writebuf, datalen); | 697 while (len > 0) { |
669 buf_setpos(channel->writebuf, pos); /* revert pos */ | 698 buflen = cbuf_writelen(cbuf); |
699 TRACE(("buflen = %d", buflen)); | |
700 buflen = MIN(buflen, len); | |
701 TRACE(("buflenmin = %d", buflen)); | |
702 | |
703 memcpy(cbuf_writeptr(cbuf, buflen), | |
704 buf_getptr(ses.payload, buflen), buflen); | |
705 cbuf_incrwrite(cbuf, buflen); | |
706 len -= buflen; | |
707 TRACE(("len = %d", buflen)); | |
708 } | |
670 | 709 |
671 channel->recvwindow -= datalen; | 710 channel->recvwindow -= datalen; |
672 | |
673 /* matt - this might be for later */ | |
674 /* if (channel->recvwindow < RECV_MINWINDOW) { | |
675 send_msg_channel_window_adjust(channel, | |
676 RECV_MAXWINDOW - channel->recvwindow); | |
677 channel->recvwindow = RECV_MAXWINDOW; | |
678 }*/ | |
679 | 711 |
680 TRACE(("leave recv_msg_channel_data")); | 712 TRACE(("leave recv_msg_channel_data")); |
681 } | 713 } |
682 | 714 |
683 /* Increment the outgoing data window for a channel - the remote end limits | 715 /* Increment the outgoing data window for a channel - the remote end limits |