X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/e07e21adfdc4b24c1e6dfbb844ab32c1cd9712ae..d03186af8de9b660564ccc1804d2a9c2e93b2e58:/channels.c diff --git a/channels.c b/channels.c index 1e57951d..94939239 100644 --- a/channels.c +++ b/channels.c @@ -1,4 +1,4 @@ -/* $OpenBSD: channels.c,v 1.276 2008/05/09 04:55:56 djm Exp $ */ +/* $OpenBSD: channels.c,v 1.299 2009/11/11 21:37:03 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -162,6 +163,9 @@ static u_int x11_fake_data_len; /* AF_UNSPEC or AF_INET or AF_INET6 */ static int IPv4or6 = AF_UNSPEC; +/* Set the routing domain a.k.a. VRF */ +static int channel_rdomain = -1; + /* helper */ static void port_open_helper(Channel *c, char *rtype); @@ -221,14 +225,19 @@ channel_lookup(int id) */ static void channel_register_fds(Channel *c, int rfd, int wfd, int efd, - int extusage, int nonblock) + int extusage, int nonblock, int is_tty) { /* Update the maximum file descriptor value. */ channel_max_fd = MAX(channel_max_fd, rfd); channel_max_fd = MAX(channel_max_fd, wfd); channel_max_fd = MAX(channel_max_fd, efd); - /* XXX set close-on-exec -markus */ + if (rfd != -1) + fcntl(rfd, F_SETFD, FD_CLOEXEC); + if (wfd != -1 && wfd != rfd) + fcntl(wfd, F_SETFD, FD_CLOEXEC); + if (efd != -1 && efd != rfd && efd != wfd) + fcntl(efd, F_SETFD, FD_CLOEXEC); c->rfd = rfd; c->wfd = wfd; @@ -237,18 +246,9 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, c->efd = efd; c->extended_usage = extusage; - /* XXX ugly hack: nonblock is only set by the server */ - if (nonblock && isatty(c->rfd)) { + if ((c->isatty = is_tty) != 0) debug2("channel %d: rfd %d isatty", c->self, c->rfd); - c->isatty = 1; - if (!isatty(c->wfd)) { - error("channel %d: wfd %d is not a tty?", - c->self, c->wfd); - } - } else { - c->isatty = 0; - } - c->wfd_isatty = isatty(c->wfd); + c->wfd_isatty = is_tty || isatty(c->wfd); /* enable nonblocking mode */ if (nonblock) { @@ -305,10 +305,11 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, buffer_init(&c->input); buffer_init(&c->output); buffer_init(&c->extended); + c->path = NULL; c->ostate = CHAN_OUTPUT_OPEN; c->istate = CHAN_INPUT_OPEN; c->flags = 0; - channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); + channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); c->self = found; c->type = type; c->ctype = ctype; @@ -328,6 +329,9 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, c->open_confirm_ctx = NULL; c->input_filter = NULL; c->output_filter = NULL; + c->filter_ctx = NULL; + c->filter_cleanup = NULL; + c->delayed = 1; /* prevent call to channel_post handler */ TAILQ_INIT(&c->status_confirms); debug("channel %d: new [%s]", found, remote_name); return c; @@ -409,6 +413,10 @@ channel_free(Channel *c) xfree(c->remote_name); c->remote_name = NULL; } + if (c->path) { + xfree(c->path); + c->path = NULL; + } while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { if (cc->abandon_cb != NULL) cc->abandon_cb(c, cc->ctx); @@ -416,6 +424,8 @@ channel_free(Channel *c) bzero(cc, sizeof(*cc)); xfree(cc); } + if (c->filter_cleanup != NULL && c->filter_ctx != NULL) + c->filter_cleanup(c->self, c->filter_ctx); channels[c->self] = NULL; xfree(c); } @@ -696,7 +706,7 @@ channel_register_open_confirm(int id, channel_callback_fn *fn, void *ctx) Channel *c = channel_lookup(id); if (c == NULL) { - logit("channel_register_open_comfirm: %d: bad id", id); + logit("channel_register_open_confirm: %d: bad id", id); return; } c->open_confirm = fn; @@ -731,7 +741,7 @@ channel_cancel_cleanup(int id) void channel_register_filter(int id, channel_infilter_fn *ifn, - channel_outfilter_fn *ofn) + channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) { Channel *c = channel_lookup(id); @@ -741,17 +751,19 @@ channel_register_filter(int id, channel_infilter_fn *ifn, } c->input_filter = ifn; c->output_filter = ofn; + c->filter_ctx = ctx; + c->filter_cleanup = cfn; } void channel_set_fds(int id, int rfd, int wfd, int efd, - int extusage, int nonblock, u_int window_max) + int extusage, int nonblock, int is_tty, u_int window_max) { Channel *c = channel_lookup(id); if (c == NULL || c->type != SSH_CHANNEL_LARVAL) fatal("channel_activate for non-larval channel %d.", id); - channel_register_fds(c, rfd, wfd, efd, extusage, nonblock); + channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); c->type = SSH_CHANNEL_OPEN; c->local_window = c->local_window_max = window_max; packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); @@ -819,7 +831,8 @@ channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) } } /** XXX check close conditions, too */ - if (compat20 && c->efd != -1) { + if (compat20 && c->efd != -1 && + !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { if (c->extended_usage == CHAN_EXTENDED_WRITE && buffer_len(&c->extended) > 0) FD_SET(c->efd, writeset); @@ -982,7 +995,7 @@ static int channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) { char *p, *host; - u_int len, have, i, found; + u_int len, have, i, found, need; char username[256]; struct { u_int8_t version; @@ -998,10 +1011,20 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) if (have < len) return 0; p = buffer_ptr(&c->input); + + need = 1; + /* SOCKS4A uses an invalid IP address 0.0.0.x */ + if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { + debug2("channel %d: socks4a request", c->self); + /* ... and needs an extra string (the hostname) */ + need = 2; + } + /* Check for terminating NUL on the string(s) */ for (found = 0, i = len; i < have; i++) { if (p[i] == '\0') { - found = 1; - break; + found++; + if (found == need) + break; } if (i > 1024) { /* the peer is probably sending garbage */ @@ -1010,7 +1033,7 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) return -1; } } - if (!found) + if (found < need) return 0; buffer_get(&c->input, (char *)&s4_req.version, 1); buffer_get(&c->input, (char *)&s4_req.command, 1); @@ -1020,23 +1043,46 @@ channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) p = buffer_ptr(&c->input); len = strlen(p); debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); + len++; /* trailing '\0' */ if (len > have) fatal("channel %d: decode socks4: len %d > have %d", c->self, len, have); strlcpy(username, p, sizeof(username)); buffer_consume(&c->input, len); - buffer_consume(&c->input, 1); /* trailing '\0' */ - host = inet_ntoa(s4_req.dest_addr); - strlcpy(c->path, host, sizeof(c->path)); + if (c->path != NULL) { + xfree(c->path); + c->path = NULL; + } + if (need == 1) { /* SOCKS4: one string */ + host = inet_ntoa(s4_req.dest_addr); + c->path = xstrdup(host); + } else { /* SOCKS4A: two strings */ + have = buffer_len(&c->input); + p = buffer_ptr(&c->input); + len = strlen(p); + debug2("channel %d: decode socks4a: host %s/%d", + c->self, p, len); + len++; /* trailing '\0' */ + if (len > have) + fatal("channel %d: decode socks4a: len %d > have %d", + c->self, len, have); + if (len > NI_MAXHOST) { + error("channel %d: hostname \"%.100s\" too long", + c->self, p); + return -1; + } + c->path = xstrdup(p); + buffer_consume(&c->input, len); + } c->host_port = ntohs(s4_req.dest_port); debug2("channel %d: dynamic request: socks4 host %s port %u command %u", - c->self, host, c->host_port, s4_req.command); + c->self, c->path, c->host_port, s4_req.command); if (s4_req.command != 1) { - debug("channel %d: cannot handle: socks4 cn %d", - c->self, s4_req.command); + debug("channel %d: cannot handle: %s cn %d", + c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); return -1; } s4_rsp.version = 0; /* vn: 0 for reply */ @@ -1067,7 +1113,7 @@ channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) u_int8_t atyp; } s5_req, s5_rsp; u_int16_t dest_port; - u_char *p, dest_addr[255+1]; + u_char *p, dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; u_int have, need, i, found, nmethods, addrlen, af; debug2("channel %d: decode socks5", c->self); @@ -1140,10 +1186,22 @@ channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) buffer_get(&c->input, (char *)&dest_addr, addrlen); buffer_get(&c->input, (char *)&dest_port, 2); dest_addr[addrlen] = '\0'; - if (s5_req.atyp == SSH_SOCKS5_DOMAIN) - strlcpy(c->path, (char *)dest_addr, sizeof(c->path)); - else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) - return -1; + if (c->path != NULL) { + xfree(c->path); + c->path = NULL; + } + if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { + if (addrlen >= NI_MAXHOST) { + error("channel %d: dynamic request: socks5 hostname " + "\"%.100s\" too long", c->self, dest_addr); + return -1; + } + c->path = xstrdup(dest_addr); + } else { + if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) + return -1; + c->path = xstrdup(ntop); + } c->host_port = ntohs(dest_port); debug2("channel %d: dynamic request: socks5 host %s port %u command %u", @@ -1171,7 +1229,6 @@ channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) int ret; have = buffer_len(&c->input); - c->delayed = 0; debug2("channel %d: pre_dynamic: have %d", c->self, have); /* buffer_dump(&c->input); */ /* check if the fixed size part of the packet is in buffer. */ @@ -1212,7 +1269,7 @@ static void channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) { Channel *nc; - struct sockaddr addr; + struct sockaddr_storage addr; int newsock; socklen_t addrlen; char buf[16384], *remote_ipaddr; @@ -1221,7 +1278,7 @@ channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) if (FD_ISSET(c->sock, readset)) { debug("X11 connection requested."); addrlen = sizeof(addr); - newsock = accept(c->sock, &addr, &addrlen); + newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (c->single_connection) { debug2("single_connection: closing X11 listener."); channel_close_fd(&c->sock); @@ -1338,7 +1395,7 @@ static void channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) { Channel *nc; - struct sockaddr addr; + struct sockaddr_storage addr; int newsock, nextstate; socklen_t addrlen; char *rtype; @@ -1362,7 +1419,7 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) } addrlen = sizeof(addr); - newsock = accept(c->sock, &addr, &addrlen); + newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (newsock < 0) { error("accept: %.100s", strerror(errno)); return; @@ -1372,18 +1429,11 @@ channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) c->local_window_max, c->local_maxpacket, 0, rtype, 1); nc->listening_port = c->listening_port; nc->host_port = c->host_port; - strlcpy(nc->path, c->path, sizeof(nc->path)); + if (c->path != NULL) + nc->path = xstrdup(c->path); - if (nextstate == SSH_CHANNEL_DYNAMIC) { - /* - * do not call the channel_post handler until - * this flag has been reset by a pre-handler. - * otherwise the FD_ISSET calls might overflow - */ - nc->delayed = 1; - } else { + if (nextstate != SSH_CHANNEL_DYNAMIC) port_open_helper(nc, rtype); - } } } @@ -1397,12 +1447,12 @@ channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) { Channel *nc; int newsock; - struct sockaddr addr; + struct sockaddr_storage addr; socklen_t addrlen; if (FD_ISSET(c->sock, readset)) { addrlen = sizeof(addr); - newsock = accept(c->sock, &addr, &addrlen); + newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (newsock < 0) { error("accept from auth socket: %.100s", strerror(errno)); return; @@ -1496,7 +1546,8 @@ channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { errno = 0; len = read(c->rfd, buf, sizeof(buf)); - if (len < 0 && (errno == EINTR || (errno == EAGAIN && !force))) + if (len < 0 && (errno == EINTR || + ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) return 1; #ifndef PTY_ZEROREAD if (len <= 0) { @@ -1567,7 +1618,8 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) c->local_consumed += dlen + 4; len = write(c->wfd, buf, dlen); xfree(data); - if (len < 0 && (errno == EINTR || errno == EAGAIN)) + if (len < 0 && (errno == EINTR || errno == EAGAIN || + errno == EWOULDBLOCK)) return 1; if (len <= 0) { if (c->type != SSH_CHANNEL_OPEN) @@ -1585,7 +1637,8 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) #endif len = write(c->wfd, buf, dlen); - if (len < 0 && (errno == EINTR || errno == EAGAIN)) + if (len < 0 && + (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) { if (c->type != SSH_CHANNEL_OPEN) { @@ -1601,6 +1654,7 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) } return -1; } +#ifndef BROKEN_TCGETATTR_ICANON if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { if (tcgetattr(c->wfd, &tio) == 0 && !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { @@ -1614,6 +1668,7 @@ channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) packet_send(); } } +#endif buffer_consume(&c->output, len); if (compat20 && len > 0) { c->local_consumed += len; @@ -1637,7 +1692,8 @@ channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) buffer_len(&c->extended)); debug2("channel %d: written %d to efd %d", c->self, len, c->efd); - if (len < 0 && (errno == EINTR || errno == EAGAIN)) + if (len < 0 && (errno == EINTR || errno == EAGAIN || + errno == EWOULDBLOCK)) return 1; if (len <= 0) { debug2("channel %d: closing write-efd %d", @@ -1652,8 +1708,8 @@ channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) len = read(c->efd, buf, sizeof(buf)); debug2("channel %d: read %d from efd %d", c->self, len, c->efd); - if (len < 0 && (errno == EINTR || - (errno == EAGAIN && !c->detach_close))) + if (len < 0 && (errno == EINTR || ((errno == EAGAIN || + errno == EWOULDBLOCK) && !c->detach_close))) return 1; if (len <= 0) { debug2("channel %d: closing read-efd %d", @@ -1677,7 +1733,8 @@ channel_handle_ctl(Channel *c, fd_set *readset, fd_set *writeset) /* Monitor control fd to detect if the slave client exits */ if (c->ctl_fd != -1 && FD_ISSET(c->ctl_fd, readset)) { len = read(c->ctl_fd, buf, sizeof(buf)); - if (len < 0 && (errno == EINTR || errno == EAGAIN)) + if (len < 0 && + (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) { debug2("channel %d: ctl read<=0", c->self); @@ -1721,8 +1778,6 @@ channel_check_window(Channel *c) static void channel_post_open(Channel *c, fd_set *readset, fd_set *writeset) { - if (c->delayed) - return; channel_handle_rfd(c, readset, writeset); channel_handle_wfd(c, readset, writeset); if (!compat20) @@ -1854,17 +1909,23 @@ static void channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset) { static int did_init = 0; - u_int i; + u_int i, oalloc; Channel *c; if (!did_init) { channel_handler_init(); did_init = 1; } - for (i = 0; i < channels_alloc; i++) { + for (i = 0, oalloc = channels_alloc; i < oalloc; i++) { c = channels[i]; if (c == NULL) continue; + if (c->delayed) { + if (ftab == channel_pre) + c->delayed = 0; + else + continue; + } if (ftab[c->type] != NULL) (*ftab[c->type])(c, readset, writeset); channel_garbage_collect(c); @@ -2308,8 +2369,8 @@ channel_input_open_failure(int type, u_int32_t seq, void *ctxt) xfree(lang); } packet_check_eom(); - /* Free the channel. This will also close the socket. */ - channel_free(c); + /* Schedule the channel for cleanup/deletion. */ + chan_mark_dead(c); } /* ARGSUSED */ @@ -2374,18 +2435,18 @@ channel_input_status_confirm(int type, u_int32_t seq, void *ctxt) { Channel *c; struct channel_confirm *cc; - int remote_id; + int id; /* Reset keepalive timeout */ - keep_alive_timeouts = 0; + packet_set_alive_timeouts(0); - remote_id = packet_get_int(); + id = packet_get_int(); packet_check_eom(); - debug2("channel_input_confirm: type %d id %d", type, remote_id); + debug2("channel_input_status_confirm: type %d id %d", type, id); - if ((c = channel_lookup(remote_id)) == NULL) { - logit("channel_input_success_failure: %d: unknown", remote_id); + if ((c = channel_lookup(id)) == NULL) { + logit("channel_input_status_confirm: %d: unknown", id); return; } ; @@ -2405,8 +2466,15 @@ channel_set_af(int af) IPv4or6 = af; } +void +channel_set_rdomain(int rdomain) +{ + channel_rdomain = rdomain; +} + static int -channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_port, +channel_setup_fwd_listener(int type, const char *listen_addr, + u_short listen_port, int *allocated_listen_port, const char *host_to_connect, u_short port_to_connect, int gateway_ports) { Channel *c; @@ -2414,6 +2482,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por struct addrinfo hints, *ai, *aitop; const char *host, *addr; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + in_port_t *lport_p; host = (type == SSH_CHANNEL_RPORT_LISTENER) ? listen_addr : host_to_connect; @@ -2423,7 +2492,7 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por error("No forward host name."); return 0; } - if (strlen(host) > SSH_CHANNEL_PATH_LEN - 1) { + if (strlen(host) >= NI_MAXHOST) { error("Forward host name too long."); return 0; } @@ -2482,17 +2551,37 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por } return 0; } - + if (allocated_listen_port != NULL) + *allocated_listen_port = 0; for (ai = aitop; ai; ai = ai->ai_next) { - if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + switch (ai->ai_family) { + case AF_INET: + lport_p = &((struct sockaddr_in *)ai->ai_addr)-> + sin_port; + break; + case AF_INET6: + lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> + sin6_port; + break; + default: continue; + } + /* + * If allocating a port for -R forwards, then use the + * same port for all address families. + */ + if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && + allocated_listen_port != NULL && *allocated_listen_port > 0) + *lport_p = htons(*allocated_listen_port); + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("channel_setup_fwd_listener: getnameinfo failed"); continue; } /* Create a port to listen for the host. */ - sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + sock = socket_rdomain(ai->ai_family, ai->ai_socktype, + ai->ai_protocol, channel_rdomain); if (sock < 0) { /* this is no error since kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); @@ -2500,8 +2589,11 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por } channel_set_reuseaddr(sock); + if (ai->ai_family == AF_INET6) + sock_set_v6only(sock); - debug("Local forwarding listening on %s port %s.", ntop, strport); + debug("Local forwarding listening on %s port %s.", + ntop, strport); /* Bind the socket to the address. */ if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { @@ -2520,11 +2612,24 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por close(sock); continue; } + + /* + * listen_port == 0 requests a dynamically allocated port - + * record what we got. + */ + if (type == SSH_CHANNEL_RPORT_LISTENER && listen_port == 0 && + allocated_listen_port != NULL && + *allocated_listen_port == 0) { + *allocated_listen_port = get_sock_port(sock, 1); + debug("Allocated listen port %d", + *allocated_listen_port); + } + /* Allocate a channel number for the socket. */ c = channel_new("port listener", type, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "port listener", 1); - strlcpy(c->path, host, sizeof(c->path)); + c->path = xstrdup(host); c->host_port = port_to_connect; c->listening_port = listen_port; success = 1; @@ -2546,8 +2651,7 @@ channel_cancel_rport_listener(const char *host, u_short port) Channel *c = channels[i]; if (c != NULL && c->type == SSH_CHANNEL_RPORT_LISTENER && - strncmp(c->path, host, sizeof(c->path)) == 0 && - c->listening_port == port) { + strcmp(c->path, host) == 0 && c->listening_port == port) { debug2("%s: close channel %d", __func__, i); channel_free(c); found = 1; @@ -2563,17 +2667,18 @@ channel_setup_local_fwd_listener(const char *listen_host, u_short listen_port, const char *host_to_connect, u_short port_to_connect, int gateway_ports) { return channel_setup_fwd_listener(SSH_CHANNEL_PORT_LISTENER, - listen_host, listen_port, host_to_connect, port_to_connect, + listen_host, listen_port, NULL, host_to_connect, port_to_connect, gateway_ports); } /* protocol v2 remote port fwd, used by sshd */ int channel_setup_remote_fwd_listener(const char *listen_address, - u_short listen_port, int gateway_ports) + u_short listen_port, int *allocated_listen_port, int gateway_ports) { return channel_setup_fwd_listener(SSH_CHANNEL_RPORT_LISTENER, - listen_address, listen_port, NULL, 0, gateway_ports); + listen_address, listen_port, allocated_listen_port, + NULL, 0, gateway_ports); } /* @@ -2783,6 +2888,23 @@ channel_clear_adm_permitted_opens(void) num_adm_permitted_opens = 0; } +void +channel_print_adm_permitted_opens(void) +{ + int i; + + printf("permitopen"); + if (num_adm_permitted_opens == 0) { + printf(" any\n"); + return; + } + for (i = 0; i < num_adm_permitted_opens; i++) + if (permitted_adm_opens[i].host_to_connect != NULL) + printf(" %s:%d", permitted_adm_opens[i].host_to_connect, + permitted_adm_opens[i].port_to_connect); + printf("\n"); +} + /* Try to start non-blocking connect to next host in cctx list */ static int connect_next(struct channel_connect *cctx) @@ -2800,8 +2922,9 @@ connect_next(struct channel_connect *cctx) error("connect_next: getnameinfo failed"); continue; } - if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, - cctx->ai->ai_protocol)) == -1) { + if ((sock = socket_rdomain(cctx->ai->ai_family, + cctx->ai->ai_socktype, cctx->ai->ai_protocol, + channel_rdomain)) == -1) { if (cctx->ai->ai_next == NULL) error("socket: %.100s", strerror(errno)); else @@ -2851,6 +2974,7 @@ connect_to(const char *host, u_short port, char *ctype, char *rname) struct channel_connect cctx; Channel *c; + memset(&cctx, 0, sizeof(cctx)); memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_socktype = SOCK_STREAM; @@ -2986,8 +3110,8 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; - sock = socket(ai->ai_family, ai->ai_socktype, - ai->ai_protocol); + sock = socket_rdomain(ai->ai_family, ai->ai_socktype, + ai->ai_protocol, channel_rdomain); if (sock < 0) { if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { error("socket: %.100s", strerror(errno)); @@ -2999,14 +3123,10 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, continue; } } -#ifdef IPV6_V6ONLY - if (ai->ai_family == AF_INET6) { - int on = 1; - if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) < 0) - error("setsockopt IPV6_V6ONLY: %.100s", strerror(errno)); - } -#endif - channel_set_reuseaddr(sock); + if (ai->ai_family == AF_INET6) + sock_set_v6only(sock); + if (x11_use_localhost) + channel_set_reuseaddr(sock); if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { debug2("bind port %d: %.100s", port, strerror(errno)); close(sock); @@ -3018,17 +3138,8 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, break; } socks[num_socks++] = sock; -#ifndef DONT_TRY_OTHER_AF if (num_socks == NUM_SOCKS) break; -#else - if (x11_use_localhost) { - if (num_socks == NUM_SOCKS) - break; - } else { - break; - } -#endif } freeaddrinfo(aitop); if (num_socks > 0) @@ -3067,7 +3178,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, } static int -connect_local_xsocket(u_int dnr) +connect_local_xsocket_path(const char *pathname) { int sock; struct sockaddr_un addr; @@ -3077,7 +3188,7 @@ connect_local_xsocket(u_int dnr) error("socket: %.100s", strerror(errno)); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof addr.sun_path, _PATH_UNIX_X, dnr); + strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) return sock; close(sock); @@ -3085,6 +3196,14 @@ connect_local_xsocket(u_int dnr) return -1; } +static int +connect_local_xsocket(u_int dnr) +{ + char buf[1024]; + snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); + return connect_local_xsocket_path(buf); +} + int x11_connect_display(void) { @@ -3106,6 +3225,17 @@ x11_connect_display(void) * connection to the real X server. */ + /* Check if the display is from launchd. */ +#ifdef __APPLE__ + if (strncmp(display, "/tmp/launch", 11) == 0) { + sock = connect_local_xsocket_path(display); + if (sock < 0) + return -1; + + /* OK, we now have a connection to the display. */ + return sock; + } +#endif /* * Check if it is a unix domain socket. Unix domain displays are in * one of the following formats: unix:d[.s], :d[.s], ::d[.s] @@ -3156,7 +3286,8 @@ x11_connect_display(void) } for (ai = aitop; ai; ai = ai->ai_next) { /* Create a socket. */ - sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + sock = socket_rdomain(ai->ai_family, ai->ai_socktype, + ai->ai_protocol, channel_rdomain); if (sock < 0) { debug2("socket: %.100s", strerror(errno)); continue;