]> andersk Git - openssh.git/blobdiff - channels.c
- deraadt@cvs.openbsd.org 2006/03/20 18:17:20
[openssh.git] / channels.c
index 4bd9af8e67e9f1c7467bb7559964d1258e036c1d..6cb88ad44f6ceaa4794a9d553b8667cad65ae4a5 100644 (file)
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.213 2005/03/10 22:01:05 deraadt Exp $");
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <termios.h>
 
 #include "ssh.h"
 #include "ssh1.h"
@@ -109,6 +114,9 @@ static int all_opens_permitted = 0;
 /* Maximum number of fake X11 displays to try. */
 #define MAX_DISPLAYS  1000
 
+/* Saved X11 local (client) display. */
+static char *x11_saved_display = NULL;
+
 /* Saved X11 authentication protocol name. */
 static char *x11_saved_proto = NULL;
 
@@ -137,22 +145,49 @@ 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);
+       }
+       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
@@ -264,9 +299,11 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd,
        c->force_drain = 0;
        c->single_connection = 0;
        c->detach_user = NULL;
+       c->detach_close = 0;
        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;
 }
@@ -623,29 +660,32 @@ channel_register_confirm(int id, channel_callback_fn *fn, void *ctx)
        c->confirm_ctx = ctx;
 }
 void
-channel_register_cleanup(int id, channel_callback_fn *fn)
+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);
                return;
        }
        c->detach_user = fn;
+       c->detach_close = 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);
                return;
        }
        c->detach_user = NULL;
+       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);
 
@@ -653,7 +693,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
@@ -681,25 +722,25 @@ channel_set_fds(int id, int rfd, int wfd, int efd,
  * 'channel_post*': perform any appropriate operations for channels which
  * have events pending.
  */
-typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset);
+typedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset);
 chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
 chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
 
 static void
-channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset)
 {
        FD_SET(c->sock, readset);
 }
 
 static void
-channel_pre_connecting(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset)
 {
        debug3("channel %d: waiting for connection", c->self);
        FD_SET(c->sock, writeset);
 }
 
 static void
-channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset)
 {
        if (buffer_len(&c->input) < packet_get_maxsize())
                FD_SET(c->sock, readset);
@@ -708,10 +749,13 @@ channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset)
 }
 
 static void
-channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset)
 {
        u_int limit = compat20 ? c->remote_window : packet_get_maxsize();
 
+       /* check buffer limits */
+       limit = MIN(limit, (BUFFER_MAX_LEN - BUFFER_MAX_CHUNK - CHAN_RBUF));
+
        if (c->istate == CHAN_INPUT_OPEN &&
            limit > 0 &&
            buffer_len(&c->input) < limit)
@@ -722,8 +766,8 @@ channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)
                        FD_SET(c->wfd, writeset);
                } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
                        if (CHANNEL_EFD_OUTPUT_ACTIVE(c))
-                              debug2("channel %d: obuf_empty delayed efd %d/(%d)",
-                                  c->self, c->efd, buffer_len(&c->extended));
+                               debug2("channel %d: obuf_empty delayed efd %d/(%d)",
+                                   c->self, c->efd, buffer_len(&c->extended));
                        else
                                chan_obuf_empty(c);
                }
@@ -745,7 +789,7 @@ channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset)
 }
 
 static void
-channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset)
 {
        if (buffer_len(&c->input) == 0) {
                packet_start(SSH_MSG_CHANNEL_CLOSE);
@@ -757,7 +801,7 @@ channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset)
 }
 
 static void
-channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset)
 {
        if (buffer_len(&c->output) == 0)
                chan_mark_dead(c);
@@ -833,7 +877,7 @@ x11_open_helper(Buffer *b)
 }
 
 static void
-channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset)
 {
        int ret = x11_open_helper(&c->output);
 
@@ -859,7 +903,7 @@ channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
 }
 
 static void
-channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset)
 {
        int ret = x11_open_helper(&c->output);
 
@@ -886,10 +930,10 @@ channel_pre_x11_open(Channel *c, fd_set * readset, fd_set * writeset)
 
 /* try to decode a socks4 header */
 static int
-channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset)
+channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset)
 {
        char *p, *host;
-       int len, have, i, found;
+       u_int len, have, i, found;
        char username[256];
        struct {
                u_int8_t version;
@@ -964,7 +1008,7 @@ channel_decode_socks4(Channel *c, fd_set * readset, fd_set * writeset)
 #define SSH_SOCKS5_SUCCESS     0x00
 
 static int
-channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset)
+channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset)
 {
        struct {
                u_int8_t version;
@@ -974,7 +1018,7 @@ channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset)
        } s5_req, s5_rsp;
        u_int16_t dest_port;
        u_char *p, dest_addr[255+1];
-       int i, have, found, nmethods, addrlen, af;
+       u_int have, i, found, nmethods, addrlen, af;
 
        debug2("channel %d: decode socks5", c->self);
        p = buffer_ptr(&c->input);
@@ -1067,10 +1111,11 @@ channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset)
 
 /* dynamic port forwarding */
 static void
-channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
+channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset)
 {
        u_char *p;
-       int have, ret;
+       u_int have;
+       int ret;
 
        have = buffer_len(&c->input);
        c->delayed = 0;
@@ -1110,7 +1155,7 @@ channel_pre_dynamic(Channel *c, fd_set * readset, fd_set * writeset)
 
 /* This is our fake X11 server socket. */
 static void
-channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset)
 {
        Channel *nc;
        struct sockaddr addr;
@@ -1173,7 +1218,7 @@ port_open_helper(Channel *c, char *rtype)
        int direct;
        char buf[1024];
        char *remote_ipaddr = get_peer_ipaddr(c->sock);
-       u_short remote_port = get_peer_port(c->sock);
+       int remote_port = get_peer_port(c->sock);
 
        direct = (strcmp(rtype, "direct-tcpip") == 0);
 
@@ -1203,7 +1248,7 @@ port_open_helper(Channel *c, char *rtype)
                }
                /* originator host and port */
                packet_put_cstring(remote_ipaddr);
-               packet_put_int(remote_port);
+               packet_put_int((u_int)remote_port);
                packet_send();
        } else {
                packet_start(SSH_MSG_PORT_OPEN);
@@ -1218,11 +1263,24 @@ port_open_helper(Channel *c, char *rtype)
        xfree(remote_ipaddr);
 }
 
+static void
+channel_set_reuseaddr(int fd)
+{
+       int on = 1;
+
+       /*
+        * Set socket options.
+        * Allow local port reuse in TIME_WAIT.
+        */
+       if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
+               error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno));
+}
+
 /*
  * This socket is listening for connections to a forwarded TCP/IP port.
  */
 static void
-channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset)
 {
        Channel *nc;
        struct sockaddr addr;
@@ -1279,7 +1337,7 @@ channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
  * clients.
  */
 static void
-channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset)
 {
        Channel *nc;
        int newsock;
@@ -1312,7 +1370,7 @@ channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset)
 }
 
 static void
-channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset)
 {
        int err = 0;
        socklen_t sz = sizeof(err);
@@ -1358,9 +1416,9 @@ channel_post_connecting(Channel *c, fd_set * readset, fd_set * writeset)
 }
 
 static int
-channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
+channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset)
 {
-       char buf[16*1024];
+       char buf[CHAN_RBUF];
        int len;
 
        if (c->rfd != -1 &&
@@ -1389,6 +1447,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);
                }
@@ -1396,10 +1456,10 @@ channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
        return 1;
 }
 static int
-channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
+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;
 
@@ -1407,14 +1467,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) {
@@ -1431,14 +1522,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();
@@ -1452,9 +1543,9 @@ channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
        return 1;
 }
 static int
-channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset)
+channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset)
 {
-       char buf[16*1024];
+       char buf[CHAN_RBUF];
        int len;
 
 /** XXX handle drain efd, too */
@@ -1495,7 +1586,7 @@ channel_handle_efd(Channel *c, fd_set * readset, fd_set * writeset)
        return 1;
 }
 static int
-channel_handle_ctl(Channel *c, fd_set * readset, fd_set * writeset)
+channel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset)
 {
        char buf[16];
        int len;
@@ -1542,7 +1633,7 @@ channel_check_window(Channel *c)
 }
 
 static void
-channel_post_open(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_open(Channel *c, fd_set *readset, fd_set *writeset)
 {
        if (c->delayed)
                return;
@@ -1556,7 +1647,7 @@ channel_post_open(Channel *c, fd_set * readset, fd_set * writeset)
 }
 
 static void
-channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
+channel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset)
 {
        int len;
 
@@ -1657,7 +1748,7 @@ channel_garbage_collect(Channel *c)
        if (c == NULL)
                return;
        if (c->detach_user != NULL) {
-               if (!chan_is_dead(c, 0))
+               if (!chan_is_dead(c, c->detach_close))
                        return;
                debug2("channel %d: gc: notify user", c->self);
                c->detach_user(c->self, NULL);
@@ -1673,7 +1764,7 @@ channel_garbage_collect(Channel *c)
 }
 
 static void
-channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset)
+channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset)
 {
        static int did_init = 0;
        u_int i;
@@ -1725,7 +1816,7 @@ channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp,
  * events pending.
  */
 void
-channel_after_select(fd_set * readset, fd_set * writeset)
+channel_after_select(fd_set *readset, fd_set *writeset)
 {
        channel_handler(channel_post, readset, writeset);
 }
@@ -1767,6 +1858,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.
@@ -1804,8 +1911,8 @@ channel_output_poll(void)
                         * hack for extended data: delay EOF if EFD still in use.
                         */
                        if (CHANNEL_EFD_INPUT_ACTIVE(c))
-                              debug2("channel %d: ibuf_empty delayed efd %d/(%d)",
-                                  c->self, c->efd, buffer_len(&c->extended));
+                               debug2("channel %d: ibuf_empty delayed efd %d/(%d)",
+                                   c->self, c->efd, buffer_len(&c->extended));
                        else
                                chan_ibuf_empty(c);
                }
@@ -1889,7 +1996,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);
 }
 
@@ -2120,9 +2230,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();
@@ -2179,7 +2288,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por
     const char *host_to_connect, u_short port_to_connect, int gateway_ports)
 {
        Channel *c;
-       int sock, r, success = 0, on = 1, wildcard = 0, is_client;
+       int sock, r, success = 0, wildcard = 0, is_client;
        struct addrinfo hints, *ai, *aitop;
        const char *host, *addr;
        char ntop[NI_MAXHOST], strport[NI_MAXSERV];
@@ -2190,11 +2299,11 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por
 
        if (host == NULL) {
                error("No forward host name.");
-               return success;
+               return 0;
        }
        if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) {
                error("Forward host name too long.");
-               return success;
+               return 0;
        }
 
        /*
@@ -2245,12 +2354,10 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por
                        packet_disconnect("getaddrinfo: fatal error: %s",
                            gai_strerror(r));
                } else {
-                       verbose("channel_setup_fwd_listener: "
-                           "getaddrinfo(%.64s): %s", addr, gai_strerror(r));
-                       packet_send_debug("channel_setup_fwd_listener: "
+                       error("channel_setup_fwd_listener: "
                            "getaddrinfo(%.64s): %s", addr, gai_strerror(r));
                }
-               aitop = NULL;
+               return 0;
        }
 
        for (ai = aitop; ai; ai = ai->ai_next) {
@@ -2268,13 +2375,8 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por
                        verbose("socket: %.100s", strerror(errno));
                        continue;
                }
-               /*
-                * Set socket options.
-                * Allow local port reuse in TIME_WAIT.
-                */
-               if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on,
-                   sizeof(on)) == -1)
-                       error("setsockopt SO_REUSEADDR: %s", strerror(errno));
+
+               channel_set_reuseaddr(sock);
 
                debug("Local forwarding listening on %s port %s.", ntop, strport);
 
@@ -2446,7 +2548,7 @@ channel_request_rforward_cancel(const char *host, u_short port)
 
        permitted_opens[i].listen_port = 0;
        permitted_opens[i].port_to_connect = 0;
-       free(permitted_opens[i].host_to_connect);
+       xfree(permitted_opens[i].host_to_connect);
        permitted_opens[i].host_to_connect = NULL;
 }
 
@@ -2635,10 +2737,10 @@ channel_send_window_changes(void)
                if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0)
                        continue;
                channel_request_start(i, "window-change", 0);
-               packet_put_int(ws.ws_col);
-               packet_put_int(ws.ws_row);
-               packet_put_int(ws.ws_xpixel);
-               packet_put_int(ws.ws_ypixel);
+               packet_put_int((u_int)ws.ws_col);
+               packet_put_int((u_int)ws.ws_row);
+               packet_put_int((u_int)ws.ws_xpixel);
+               packet_put_int((u_int)ws.ws_ypixel);
                packet_send();
        }
 }
@@ -2652,7 +2754,7 @@ channel_send_window_changes(void)
  */
 int
 x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
-    int single_connection, u_int *display_numberp)
+    int single_connection, u_int *display_numberp, int **chanids)
 {
        Channel *nc = NULL;
        int display_number, sock;
@@ -2661,6 +2763,9 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
        char strport[NI_MAXSERV];
        int gaierr, n, num_socks = 0, socks[NUM_SOCKS];
 
+       if (chanids == NULL)
+               return -1;
+
        for (display_number = x11_display_offset;
            display_number < MAX_DISPLAYS;
            display_number++) {
@@ -2697,6 +2802,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
                                        error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno));
                        }
 #endif
+                       channel_set_reuseaddr(sock);
                        if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
                                debug2("bind port %d: %.100s", port, strerror(errno));
                                close(sock);
@@ -2742,6 +2848,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
        }
 
        /* Allocate a channel for each socket. */
+       *chanids = xmalloc(sizeof(**chanids) * (num_socks + 1));
        for (n = 0; n < num_socks; n++) {
                sock = socks[n];
                nc = channel_new("x11 listener",
@@ -2749,7 +2856,9 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost,
                    CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT,
                    0, "X11 inet listener", 1);
                nc->single_connection = single_connection;
+               (*chanids)[n] = nc->self;
        }
+       (*chanids)[n] = -1;
 
        /* Return the display number for the DISPLAY environment variable. */
        *display_numberp = display_number;
@@ -2935,7 +3044,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();
@@ -2947,19 +3056,27 @@ deny_input_open(int type, u_int32_t seq, void *ctxt)
  * This should be called in the client only.
  */
 void
-x11_request_forwarding_with_spoofing(int client_session_id,
+x11_request_forwarding_with_spoofing(int client_session_id, const char *disp,
     const char *proto, const char *data)
 {
        u_int data_len = (u_int) strlen(data) / 2;
-       u_int i, value, len;
+       u_int i, value;
        char *new_data;
        int screen_number;
        const char *cp;
        u_int32_t rnd = 0;
 
-       cp = getenv("DISPLAY");
-       if (cp)
-               cp = strchr(cp, ':');
+       if (x11_saved_display == NULL)
+               x11_saved_display = xstrdup(disp);
+       else if (strcmp(disp, x11_saved_display) != 0) {
+               error("x11_request_forwarding_with_spoofing: different "
+                   "$DISPLAY already forwarded");
+               return;
+       }
+
+       cp = disp;
+       if (disp)
+               cp = strchr(disp, ':');
        if (cp)
                cp = strchr(cp, '.');
        if (cp)
@@ -2967,33 +3084,31 @@ x11_request_forwarding_with_spoofing(int client_session_id,
        else
                screen_number = 0;
 
-       /* Save protocol name. */
-       x11_saved_proto = xstrdup(proto);
-
-       /*
-        * Extract real authentication data and generate fake data of the
-        * same length.
-        */
-       x11_saved_data = xmalloc(data_len);
-       x11_fake_data = xmalloc(data_len);
-       for (i = 0; i < data_len; i++) {
-               if (sscanf(data + 2 * i, "%2x", &value) != 1)
-                       fatal("x11_request_forwarding: bad authentication data: %.100s", data);
-               if (i % 4 == 0)
-                       rnd = arc4random();
-               x11_saved_data[i] = value;
-               x11_fake_data[i] = rnd & 0xff;
-               rnd >>= 8;
-       }
-       x11_saved_data_len = data_len;
-       x11_fake_data_len = data_len;
+       if (x11_saved_proto == NULL) {
+               /* Save protocol name. */
+               x11_saved_proto = xstrdup(proto);
+               /*
+                * Extract real authentication data and generate fake data
+                * of the same length.
+                */
+               x11_saved_data = xmalloc(data_len);
+               x11_fake_data = xmalloc(data_len);
+               for (i = 0; i < data_len; i++) {
+                       if (sscanf(data + 2 * i, "%2x", &value) != 1)
+                               fatal("x11_request_forwarding: bad "
+                                   "authentication data: %.100s", data);
+                       if (i % 4 == 0)
+                               rnd = arc4random();
+                       x11_saved_data[i] = value;
+                       x11_fake_data[i] = rnd & 0xff;
+                       rnd >>= 8;
+               }
+               x11_saved_data_len = data_len;
+               x11_fake_data_len = data_len;
+       }
 
        /* Convert the fake data into hex. */
-       len = 2 * data_len + 1;
-       new_data = xmalloc(len);
-       for (i = 0; i < data_len; i++)
-               snprintf(new_data + 2 * i, len - 2 * i,
-                   "%02x", (u_char) x11_fake_data[i]);
+       new_data = tohex(x11_fake_data, data_len);
 
        /* Send the request packet. */
        if (compat20) {
This page took 0.085559 seconds and 4 git commands to generate.