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