X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/fce397492fcee903dcdb8062249ecbeaddfe745d..13de3560de281b3c8b9c4ae7aac023d049b89644:/channels.c diff --git a/channels.c b/channels.c index 14405bd4..68d85438 100644 --- a/channels.c +++ b/channels.c @@ -39,7 +39,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: channels.c,v 1.199 2003/12/02 17:01:14 markus Exp $"); +RCSID("$OpenBSD: channels.c,v 1.206 2004/06/18 11:11:54 djm Exp $"); #include "ssh.h" #include "ssh1.h" @@ -172,6 +172,7 @@ channel_register_fds(Channel *c, int rfd, int wfd, int efd, c->rfd = rfd; c->wfd = wfd; c->sock = (rfd == wfd) ? rfd : -1; + c->ctl_fd = -1; /* XXX: set elsewhere */ c->efd = efd; c->extended_usage = extusage; @@ -263,6 +264,7 @@ channel_new(char *ctype, int type, int rfd, int wfd, int efd, c->single_connection = 0; c->detach_user = NULL; c->confirm = NULL; + c->confirm_ctx = NULL; c->input_filter = NULL; debug("channel %d: new [%s]", found, remote_name); return c; @@ -304,10 +306,11 @@ channel_close_fd(int *fdp) static void channel_close_fds(Channel *c) { - debug3("channel %d: close_fds r %d w %d e %d", - c->self, c->rfd, c->wfd, c->efd); + debug3("channel %d: close_fds r %d w %d e %d c %d", + c->self, c->rfd, c->wfd, c->efd, c->ctl_fd); channel_close_fd(&c->sock); + channel_close_fd(&c->ctl_fd); channel_close_fd(&c->rfd); channel_close_fd(&c->wfd); channel_close_fd(&c->efd); @@ -333,6 +336,8 @@ channel_free(Channel *c) if (c->sock != -1) shutdown(c->sock, SHUT_RDWR); + if (c->ctl_fd != -1) + shutdown(c->ctl_fd, SHUT_RDWR); channel_close_fds(c); buffer_free(&c->input); buffer_free(&c->output); @@ -482,7 +487,7 @@ channel_find_open(void) for (i = 0; i < channels_alloc; i++) { c = channels[i]; - if (c == NULL) + if (c == NULL || c->remote_id < 0) continue; switch (c->type) { case SSH_CHANNEL_CLOSED: @@ -550,12 +555,13 @@ channel_open_message(void) case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: - snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n", + snprintf(buf, sizeof buf, + " #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d cfd %d)\r\n", c->self, c->remote_name, c->type, c->remote_id, c->istate, buffer_len(&c->input), c->ostate, buffer_len(&c->output), - c->rfd, c->wfd); + c->rfd, c->wfd, c->ctl_fd); buffer_append(&buffer, buf, strlen(buf)); continue; default: @@ -596,14 +602,14 @@ channel_request_start(int id, char *service, int wantconfirm) logit("channel_request_start: %d: unknown channel id", id); return; } - debug2("channel %d: request %s", id, service) ; + debug2("channel %d: request %s confirm %d", id, service, wantconfirm); packet_start(SSH2_MSG_CHANNEL_REQUEST); packet_put_int(c->remote_id); packet_put_cstring(service); packet_put_char(wantconfirm); } void -channel_register_confirm(int id, channel_callback_fn *fn) +channel_register_confirm(int id, channel_callback_fn *fn, void *ctx) { Channel *c = channel_lookup(id); @@ -612,6 +618,7 @@ channel_register_confirm(int id, channel_callback_fn *fn) return; } c->confirm = fn; + c->confirm_ctx = ctx; } void channel_register_cleanup(int id, channel_callback_fn *fn) @@ -729,6 +736,10 @@ channel_pre_open(Channel *c, fd_set * readset, fd_set * writeset) buffer_len(&c->extended) < c->remote_window) FD_SET(c->efd, readset); } + /* XXX: What about efd? races? */ + if (compat20 && c->ctl_fd != -1 && + c->istate == CHAN_INPUT_OPEN && c->ostate == CHAN_OUTPUT_OPEN) + FD_SET(c->ctl_fd, readset); } static void @@ -1031,7 +1042,7 @@ channel_decode_socks5(Channel *c, fd_set * readset, fd_set * writeset) buffer_get(&c->input, (char *)&dest_port, 2); dest_addr[addrlen] = '\0'; if (s5_req.atyp == SSH_SOCKS5_DOMAIN) - strlcpy(c->path, dest_addr, sizeof(c->path)); + strlcpy(c->path, (char *)dest_addr, sizeof(c->path)); else if (inet_ntop(af, dest_addr, c->path, sizeof(c->path)) == NULL) return -1; c->host_port = ntohs(dest_port); @@ -1482,6 +1493,33 @@ 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) +{ + char buf[16]; + int len; + + /* 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)) + return 1; + if (len <= 0) { + debug2("channel %d: ctl read<=0", c->self); + if (c->type != SSH_CHANNEL_OPEN) { + debug2("channel %d: not open", c->self); + chan_mark_dead(c); + return -1; + } else { + chan_read_failed(c); + chan_write_failed(c); + } + return -1; + } else + fatal("%s: unexpected data on ctl fd", __func__); + } + return 1; +} +static int channel_check_window(Channel *c) { if (c->type == SSH_CHANNEL_OPEN && @@ -1511,6 +1549,7 @@ channel_post_open(Channel *c, fd_set * readset, fd_set * writeset) if (!compat20) return; channel_handle_efd(c, readset, writeset); + channel_handle_ctl(c, readset, writeset); channel_check_window(c); } @@ -1817,13 +1856,25 @@ channel_input_data(int type, u_int32_t seq, void *ctxt) c->type != SSH_CHANNEL_X11_OPEN) return; - /* same for protocol 1.5 if output end is no longer open */ - if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) - return; - /* Get the data. */ data = packet_get_string(&data_len); + /* + * Ignore data for protocol > 1.3 if output end is no longer open. + * For protocol 2 the sending side is reducing its window as it sends + * data, so we must 'fake' consumption of the data in order to ensure + * that window updates are sent back. Otherwise the connection might + * deadlock. + */ + if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { + if (compat20) { + c->local_window -= data_len; + c->local_consumed += data_len; + } + xfree(data); + return; + } + if (compat20) { if (data_len > c->local_maxpacket) { logit("channel %d: rcvd big packet %d, maxpack %d", @@ -1999,7 +2050,7 @@ channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) c->remote_maxpacket = packet_get_int(); if (c->confirm) { debug2("callback start"); - c->confirm(c->self, NULL); + c->confirm(c->self, c->confirm_ctx); debug2("callback done"); } debug2("channel %d: open confirm rwindow %u rmax %u", c->self, @@ -2216,6 +2267,26 @@ channel_setup_fwd_listener(int type, const char *listen_addr, u_short listen_por return success; } +int +channel_cancel_rport_listener(const char *host, u_short port) +{ + int i, found = 0; + + for(i = 0; i < channels_alloc; i++) { + 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) { + debug2("%s: close clannel %d", __func__, i); + channel_free(c); + found = 1; + } + } + + return (found); +} + /* protocol local port fwd, used by ssh (and sshd in v1) */ int channel_setup_local_fwd_listener(u_short listen_port, @@ -2292,6 +2363,42 @@ channel_request_remote_forwarding(u_short listen_port, } } +/* + * Request cancellation of remote forwarding of connection host:port from + * local side. + */ + +void +channel_request_rforward_cancel(u_short port) +{ + int i; + const char *address_to_bind = "0.0.0.0"; + + if (!compat20) + return; + + for (i = 0; i < num_permitted_opens; i++) { + if (permitted_opens[i].host_to_connect != NULL && + permitted_opens[i].listen_port == port) + break; + } + if (i >= num_permitted_opens) { + debug("%s: requested forward not found", __func__); + return; + } + packet_start(SSH2_MSG_GLOBAL_REQUEST); + packet_put_cstring("cancel-tcpip-forward"); + packet_put_char(0); + packet_put_cstring(address_to_bind); + packet_put_int(port); + packet_send(); + + permitted_opens[i].listen_port = 0; + permitted_opens[i].port_to_connect = 0; + free(permitted_opens[i].host_to_connect); + permitted_opens[i].host_to_connect = NULL; +} + /* * This is called after receiving CHANNEL_FORWARDING_REQUEST. This initates * listening for the port, and sends back a success reply (or disconnect @@ -2361,7 +2468,8 @@ channel_clear_permitted_opens(void) int i; for (i = 0; i < num_permitted_opens; i++) - xfree(permitted_opens[i].host_to_connect); + if (permitted_opens[i].host_to_connect != NULL) + xfree(permitted_opens[i].host_to_connect); num_permitted_opens = 0; } @@ -2401,8 +2509,8 @@ connect_to(const char *host, u_short port) verbose("socket: %.100s", strerror(errno)); continue; } - if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) - fatal("connect_to: F_SETFL: %s", strerror(errno)); + if (set_nonblock(sock) == -1) + fatal("%s: set_nonblock(%d)", __func__, sock); if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0 && errno != EINPROGRESS) { error("connect_to %.100s port %s: %.100s", ntop, strport, @@ -2429,7 +2537,8 @@ channel_connect_by_listen_address(u_short listen_port) int i; for (i = 0; i < num_permitted_opens; i++) - if (permitted_opens[i].listen_port == listen_port) + if (permitted_opens[i].host_to_connect != NULL && + permitted_opens[i].listen_port == listen_port) return connect_to( permitted_opens[i].host_to_connect, permitted_opens[i].port_to_connect); @@ -2447,7 +2556,8 @@ channel_connect_to(const char *host, u_short port) permit = all_opens_permitted; if (!permit) { for (i = 0; i < num_permitted_opens; i++) - if (permitted_opens[i].port_to_connect == port && + if (permitted_opens[i].host_to_connect != NULL && + permitted_opens[i].port_to_connect == port && strcmp(permitted_opens[i].host_to_connect, host) == 0) permit = 1; @@ -2460,6 +2570,27 @@ channel_connect_to(const char *host, u_short port) return connect_to(host, port); } +void +channel_send_window_changes(void) +{ + int i; + struct winsize ws; + + for (i = 0; i < channels_alloc; i++) { + if (channels[i] == NULL || + channels[i]->type != SSH_CHANNEL_OPEN) + continue; + 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_send(); + } +} + /* -- X11 forwarding */ /* @@ -2499,6 +2630,7 @@ x11_create_display_inet(int x11_display_offset, int x11_use_localhost, if (sock < 0) { if ((errno != EINVAL) && (errno != EAFNOSUPPORT)) { error("socket: %.100s", strerror(errno)); + freeaddrinfo(aitop); return -1; } else { debug("x11_create_display_inet: Socket family %d not supported",