]> andersk Git - openssh.git/blobdiff - channels.c
- stevesk@cvs.openbsd.org 2006/02/20 16:36:15
[openssh.git] / channels.c
index 9607717cc15dc44b59b37697816423414d11d77b..7161b9413f4c964905feab798bd22b8ac47a5b3c 100644 (file)
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.227 2005/10/14 02:29:37 stevesk Exp $");
+RCSID("$OpenBSD: channels.c,v 1.235 2006/02/20 16:36:14 stevesk Exp $");
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <termios.h>
 
 #include "ssh.h"
 #include "ssh1.h"
@@ -58,8 +64,6 @@ RCSID("$OpenBSD: channels.c,v 1.227 2005/10/14 02:29:37 stevesk Exp $");
 
 /* -- channel core */
 
-#define CHAN_RBUF      16*1024
-
 /*
  * Pointer to an array containing all allocated channels.  The array is
  * dynamically extended as needed.
@@ -142,22 +146,50 @@ static void port_open_helper(Channel *c, char *rtype);
 /* -- channel core */
 
 Channel *
-channel_lookup(int id)
+channel_by_id(int id)
 {
        Channel *c;
 
        if (id < 0 || (u_int)id >= channels_alloc) {
-               logit("channel_lookup: %d: bad id", id);
+               logit("channel_by_id: %d: bad id", id);
                return NULL;
        }
        c = channels[id];
        if (c == NULL) {
-               logit("channel_lookup: %d: bad id: channel free", id);
+               logit("channel_by_id: %d: bad id: channel free", id);
                return NULL;
        }
        return c;
 }
 
+/*
+ * Returns the channel if it is allowed to receive protocol messages.
+ * Private channels, like listening sockets, may not receive messages.
+ */
+Channel *
+channel_lookup(int id)
+{
+       Channel *c;
+
+       if ((c = channel_by_id(id)) == NULL)
+               return (NULL);
+
+       switch(c->type) {
+       case SSH_CHANNEL_X11_OPEN:
+       case SSH_CHANNEL_LARVAL:
+       case SSH_CHANNEL_CONNECTING:
+       case SSH_CHANNEL_DYNAMIC:
+       case SSH_CHANNEL_OPENING:
+       case SSH_CHANNEL_OPEN:
+       case SSH_CHANNEL_INPUT_DRAINING:
+       case SSH_CHANNEL_OUTPUT_DRAINING:
+               return (c);
+               break;
+       }
+       logit("Non-public channel %d, type %d.", id, c->type);
+       return (NULL);
+}
+
 /*
  * Register filedescriptors for a channel, used when allocating a channel or
  * when the channel consumer/producer is ready, e.g. shell exec'd
@@ -273,6 +305,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
        c->confirm = NULL;
        c->confirm_ctx = NULL;
        c->input_filter = NULL;
+       c->output_filter = NULL;
        debug("channel %d: new [%s]", found, remote_name);
        return c;
 }
@@ -631,7 +664,7 @@ channel_register_confirm(int id, channel_callback_fn *fn, void *ctx)
 void
 channel_register_cleanup(int id, channel_callback_fn *fn, int do_close)
 {
-       Channel *c = channel_lookup(id);
+       Channel *c = channel_by_id(id);
 
        if (c == NULL) {
                logit("channel_register_cleanup: %d: bad id", id);
@@ -643,7 +676,7 @@ channel_register_cleanup(int id, channel_callback_fn *fn, int do_close)
 void
 channel_cancel_cleanup(int id)
 {
-       Channel *c = channel_lookup(id);
+       Channel *c = channel_by_id(id);
 
        if (c == NULL) {
                logit("channel_cancel_cleanup: %d: bad id", id);
@@ -653,7 +686,8 @@ channel_cancel_cleanup(int id)
        c->detach_close = 0;
 }
 void
-channel_register_filter(int id, channel_filter_fn *fn)
+channel_register_filter(int id, channel_infilter_fn *ifn,
+    channel_outfilter_fn *ofn)
 {
        Channel *c = channel_lookup(id);
 
@@ -661,7 +695,8 @@ channel_register_filter(int id, channel_filter_fn *fn)
                logit("channel_register_filter: %d: bad id", id);
                return;
        }
-       c->input_filter = fn;
+       c->input_filter = ifn;
+       c->output_filter = ofn;
 }
 
 void
@@ -1414,6 +1449,8 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
                                debug2("channel %d: filter stops", c->self);
                                chan_read_failed(c);
                        }
+               } else if (c->datagram) {
+                       buffer_put_string(&c->input, buf, len);
                } else {
                        buffer_append(&c->input, buf, len);
                }
@@ -1424,7 +1461,7 @@ static int
 channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
 {
        struct termios tio;
-       u_char *data;
+       u_char *data = NULL, *buf;
        u_int dlen;
        int len;
 
@@ -1432,14 +1469,45 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
        if (c->wfd != -1 &&
            FD_ISSET(c->wfd, writeset) &&
            buffer_len(&c->output) > 0) {
-               data = buffer_ptr(&c->output);
-               dlen = buffer_len(&c->output);
+               if (c->output_filter != NULL) {
+                       if ((buf = c->output_filter(c, &data, &dlen)) == NULL) {
+                               debug2("channel %d: filter stops", c->self);
+                               if (c->type != SSH_CHANNEL_OPEN)
+                                       chan_mark_dead(c);
+                               else
+                                       chan_write_failed(c);
+                               return -1;
+                       }
+               } else if (c->datagram) {
+                       buf = data = buffer_get_string(&c->output, &dlen);
+               } else {
+                       buf = data = buffer_ptr(&c->output);
+                       dlen = buffer_len(&c->output);
+               }
+
+               if (c->datagram) {
+                       /* ignore truncated writes, datagrams might get lost */
+                       c->local_consumed += dlen + 4;
+                       len = write(c->wfd, buf, dlen);
+                       xfree(data);
+                       if (len < 0 && (errno == EINTR || errno == EAGAIN))
+                               return 1;
+                       if (len <= 0) {
+                               if (c->type != SSH_CHANNEL_OPEN)
+                                       chan_mark_dead(c);
+                               else
+                                       chan_write_failed(c);
+                               return -1;
+                       }
+                       return 1;
+               }
 #ifdef _AIX
                /* XXX: Later AIX versions can't push as much data to tty */
                if (compat20 && c->wfd_isatty)
                        dlen = MIN(dlen, 8*1024);
 #endif
-               len = write(c->wfd, data, dlen);
+
+               len = write(c->wfd, buf, dlen);
                if (len < 0 && (errno == EINTR || errno == EAGAIN))
                        return 1;
                if (len <= 0) {
@@ -1456,14 +1524,14 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
                        }
                        return -1;
                }
-               if (compat20 && c->isatty && dlen >= 1 && data[0] != '\r') {
+               if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') {
                        if (tcgetattr(c->wfd, &tio) == 0 &&
                            !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) {
                                /*
                                 * Simulate echo to reduce the impact of
                                 * traffic analysis. We need to match the
                                 * size of a SSH2_MSG_CHANNEL_DATA message
-                                * (4 byte channel id + data)
+                                * (4 byte channel id + buf)
                                 */
                                packet_send_ignore(4 + len);
                                packet_send();
@@ -1792,6 +1860,22 @@ channel_output_poll(void)
                if ((c->istate == CHAN_INPUT_OPEN ||
                    c->istate == CHAN_INPUT_WAIT_DRAIN) &&
                    (len = buffer_len(&c->input)) > 0) {
+                       if (c->datagram) {
+                               if (len > 0) {
+                                       u_char *data;
+                                       u_int dlen;
+
+                                       data = buffer_get_string(&c->input,
+                                           &dlen);
+                                       packet_start(SSH2_MSG_CHANNEL_DATA);
+                                       packet_put_int(c->remote_id);
+                                       packet_put_string(data, dlen);
+                                       packet_send();
+                                       c->remote_window -= dlen + 4;
+                                       xfree(data);
+                               }
+                               continue;
+                       }
                        /*
                         * Send some data for the other side over the secure
                         * connection.
@@ -1914,7 +1998,10 @@ channel_input_data(int type, u_int32_t seq, void *ctxt)
                c->local_window -= data_len;
        }
        packet_check_eom();
-       buffer_append(&c->output, data, data_len);
+       if (c->datagram)
+               buffer_put_string(&c->output, data, data_len);
+       else
+               buffer_append(&c->output, data, data_len);
        xfree(data);
 }
 
@@ -2145,9 +2232,8 @@ channel_input_window_adjust(int type, u_int32_t seq, void *ctxt)
        id = packet_get_int();
        c = channel_lookup(id);
 
-       if (c == NULL || c->type != SSH_CHANNEL_OPEN) {
-               logit("Received window adjust for "
-                   "non-open channel %d.", id);
+       if (c == NULL) {
+               logit("Received window adjust for non-open channel %d.", id);
                return;
        }
        adjust = packet_get_int();
@@ -2960,7 +3046,7 @@ deny_input_open(int type, u_int32_t seq, void *ctxt)
                error("deny_input_open: type %d", type);
                break;
        }
-       error("Warning: this is probably a break in attempt by a malicious server.");
+       error("Warning: this is probably a break-in attempt by a malicious server.");
        packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
        packet_put_int(rchan);
        packet_send();
This page took 0.044698 seconds and 4 git commands to generate.