diff options
Diffstat (limited to 'channels.c')
-rw-r--r-- | channels.c | 388 |
1 files changed, 227 insertions, 161 deletions
@@ -16,7 +16,7 @@ */ #include "includes.h" -RCSID("$Id: channels.c,v 1.14 1999/12/27 12:54:55 damien Exp $"); +RCSID("$Id: channels.c,v 1.15 2000/01/14 04:45:48 damien Exp $"); #include "ssh.h" #include "packet.h" @@ -374,7 +374,8 @@ void channel_after_select(fd_set * readset, fd_set * writeset) { struct sockaddr addr; - int addrlen, newsock, i, newch, len; + int newsock, i, newch, len; + socklen_t addrlen; Channel *ch; char buf[16384], *remote_hostname; @@ -412,7 +413,7 @@ channel_after_select(fd_set * readset, fd_set * writeset) * forwarded TCP/IP port. */ if (FD_ISSET(ch->sock, readset)) { - debug("Connection to port %d forwarding to %.100s:%d requested.", + debug("Connection to port %d forwarding to %.100s port %d requested.", ch->listening_port, ch->path, ch->host_port); addrlen = sizeof(addr); newsock = accept(ch->sock, &addr, &addrlen); @@ -421,7 +422,7 @@ channel_after_select(fd_set * readset, fd_set * writeset) break; } remote_hostname = get_remote_hostname(newsock); - snprintf(buf, sizeof buf, "listen port %d:%.100s:%d, connect from %.200s:%d", + snprintf(buf, sizeof buf, "listen port %d for %.100s port %d, connect from %.200s port %d", ch->listening_port, ch->path, ch->host_port, remote_hostname, get_peer_port(newsock)); xfree(remote_hostname); @@ -532,10 +533,19 @@ channel_output_poll() for (i = 0; i < channels_alloc; i++) { ch = &channels[i]; + /* We are only interested in channels that can have buffered incoming data. */ - if (ch->type != SSH_CHANNEL_OPEN && - ch->type != SSH_CHANNEL_INPUT_DRAINING) - continue; + if (compat13) { + if (ch->type != SSH_CHANNEL_OPEN && + ch->type != SSH_CHANNEL_INPUT_DRAINING) + continue; + } else { + if (ch->type != SSH_CHANNEL_OPEN) + continue; + if (ch->istate != CHAN_INPUT_OPEN && + ch->istate != CHAN_INPUT_WAIT_DRAIN) + continue; + } /* Get the amount of buffered data for this channel. */ len = buffer_len(&ch->input); @@ -575,25 +585,33 @@ channel_output_poll() void channel_input_data(int payload_len) { - int channel; + int id; char *data; unsigned int data_len; + Channel *ch; /* Get the channel number and verify it. */ - channel = packet_get_int(); - if (channel < 0 || channel >= channels_alloc || - channels[channel].type == SSH_CHANNEL_FREE) - packet_disconnect("Received data for nonexistent channel %d.", channel); + id = packet_get_int(); + if (id < 0 || id >= channels_alloc) + packet_disconnect("Received data for nonexistent channel %d.", id); + ch = &channels[id]; + + if (ch->type == SSH_CHANNEL_FREE) + packet_disconnect("Received data for free channel %d.", ch->self); /* Ignore any data for non-open channels (might happen on close) */ - if (channels[channel].type != SSH_CHANNEL_OPEN && - channels[channel].type != SSH_CHANNEL_X11_OPEN) + if (ch->type != SSH_CHANNEL_OPEN && + ch->type != SSH_CHANNEL_X11_OPEN) + return; + + /* same for protocol 1.5 if output end is no longer open */ + if (!compat13 && ch->ostate != CHAN_OUTPUT_OPEN) return; /* Get the data. */ data = packet_get_string(&data_len); packet_integrity_check(payload_len, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA); - buffer_append(&channels[channel].output, data, data_len); + buffer_append(&ch->output, data, data_len); xfree(data); } @@ -610,23 +628,11 @@ channel_not_very_much_buffered_data() for (i = 0; i < channels_alloc; i++) { ch = &channels[i]; - switch (ch->type) { - case SSH_CHANNEL_X11_LISTENER: - case SSH_CHANNEL_PORT_LISTENER: - case SSH_CHANNEL_AUTH_SOCKET: - continue; - case SSH_CHANNEL_OPEN: + if (ch->type == SSH_CHANNEL_OPEN) { if (buffer_len(&ch->input) > packet_get_maxsize()) return 0; if (buffer_len(&ch->output) > packet_get_maxsize()) return 0; - continue; - case SSH_CHANNEL_INPUT_DRAINING: - case SSH_CHANNEL_OUTPUT_DRAINING: - case SSH_CHANNEL_X11_OPEN: - case SSH_CHANNEL_FREE: - default: - continue; } } return 1; @@ -853,9 +859,11 @@ channel_open_message() 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 o%d)\r\n", - c->self, c->remote_name, - c->type, c->remote_id, c->istate, c->ostate); + snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%d/%d o%d/%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)); buffer_append(&buffer, buf, strlen(buf)); continue; default: @@ -878,50 +886,76 @@ void channel_request_local_forwarding(u_short port, const char *host, u_short host_port, int gateway_ports) { - int ch, sock, on = 1; - struct sockaddr_in sin; + int success, ch, sock, on = 1; + struct addrinfo hints, *ai, *aitop; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; struct linger linger; if (strlen(host) > sizeof(channels[0].path) - 1) packet_disconnect("Forward host name too long."); - /* Create a port to listen for the host. */ - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) - packet_disconnect("socket: %.100s", strerror(errno)); - - /* Initialize socket address. */ - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - if (gateway_ports == 1) - sin.sin_addr.s_addr = htonl(INADDR_ANY); - else - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - sin.sin_port = htons(port); - /* - * Set socket options. We would like the socket to disappear as soon - * as it has been closed for whatever reason. + * getaddrinfo returns a loopback address if the hostname is + * set to NULL and hints.ai_flags is not AI_PASSIVE */ - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); - linger.l_onoff = 1; - linger.l_linger = 5; - setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger)); - - /* Bind the socket to the address. */ - if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) - packet_disconnect("bind: %.100s", strerror(errno)); - - /* Start listening for connections on the socket. */ - if (listen(sock, 5) < 0) - packet_disconnect("listen: %.100s", strerror(errno)); - - /* Allocate a channel number for the socket. */ - ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, - xstrdup("port listener")); - strlcpy(channels[ch].path, host, sizeof(channels[ch].path)); - channels[ch].host_port = host_port; - channels[ch].listening_port = port; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_flags = gateway_ports ? AI_PASSIVE : 0; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", port); + if (getaddrinfo(NULL, strport, &hints, &aitop) != 0) + packet_disconnect("getaddrinfo: fatal error"); + + success = 0; + for (ai = aitop; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), + strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("channel_request_local_forwarding: getnameinfo failed"); + continue; + } + /* Create a port to listen for the host. */ + sock = socket(ai->ai_family, SOCK_STREAM, 0); + if (sock < 0) { + /* this is no error since kernel may not support ipv6 */ + verbose("socket: %.100s", strerror(errno)); + continue; + } + /* + * Set socket options. We would like the socket to disappear + * as soon as it has been closed for whatever reason. + */ + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); + linger.l_onoff = 1; + linger.l_linger = 5; + setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *)&linger, sizeof(linger)); + 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) { + /* address can be in use ipv6 address is already bound */ + verbose("bind: %.100s", strerror(errno)); + close(sock); + continue; + } + /* Start listening for connections on the socket. */ + if (listen(sock, 5) < 0) { + error("listen: %.100s", strerror(errno)); + close(sock); + continue; + } + /* Allocate a channel number for the socket. */ + ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock, + xstrdup("port listener")); + strlcpy(channels[ch].path, host, sizeof(channels[ch].path)); + channels[ch].host_port = host_port; + channels[ch].listening_port = port; + success = 1; + } + if (success == 0) + packet_disconnect("cannot listen port: %d", port); + freeaddrinfo(aitop); } /* @@ -1000,12 +1034,13 @@ channel_input_port_forward_request(int is_root) void channel_input_port_open(int payload_len) { - int remote_channel, sock, newch, i; + int remote_channel, sock = 0, newch, i; u_short host_port; - struct sockaddr_in sin; char *host, *originator_string; - struct hostent *hp; int host_len, originator_len; + struct addrinfo hints, *ai, *aitop; + char ntop[NI_MAXHOST], strport[NI_MAXSERV]; + int gaierr; /* Get remote channel number. */ remote_channel = packet_get_int(); @@ -1047,41 +1082,47 @@ channel_input_port_open(int payload_len) packet_send(); } } - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = inet_addr(host); - if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) { - /* It was a valid numeric host address. */ - sin.sin_family = AF_INET; - } else { - /* Look up the host address from the name servers. */ - hp = gethostbyname(host); - if (!hp) { - error("%.100s: unknown host.", host); - goto fail; + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", host_port); + if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { + error("%.100s: unknown host (%s)", host, gai_strerror(gaierr)); + goto fail; + } + + for (ai = aitop; ai; ai = ai->ai_next) { + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), + strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { + error("channel_input_port_open: getnameinfo failed"); + continue; } - if (!hp->h_addr_list[0]) { - error("%.100s: host has no IP address.", host); - goto fail; + /* Create the socket. */ + sock = socket(ai->ai_family, SOCK_STREAM, 0); + if (sock < 0) { + error("socket: %.100s", strerror(errno)); + continue; } - sin.sin_family = hp->h_addrtype; - memcpy(&sin.sin_addr, hp->h_addr_list[0], - sizeof(sin.sin_addr)); - } - sin.sin_port = htons(host_port); + /* Connect to the host/port. */ + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + error("connect %.100s port %s: %.100s", ntop, strport, + strerror(errno)); + close(sock); + continue; /* fail -- try next */ + } + break; /* success */ - /* Create the socket. */ - sock = socket(sin.sin_family, SOCK_STREAM, 0); - if (sock < 0) { - error("socket: %.100s", strerror(errno)); - goto fail; } - /* Connect to the host/port. */ - if (connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { - error("connect %.100s:%d: %.100s", host, host_port, - strerror(errno)); - close(sock); + freeaddrinfo(aitop); + + if (!ai) { + error("connect %.100s port %d: failed.", host, host_port); goto fail; } + /* Successful connection. */ /* Allocate a channel for this connection. */ @@ -1115,48 +1156,73 @@ fail: * occurs. */ +#define NUM_SOCKS 10 + char * x11_create_display_inet(int screen_number, int x11_display_offset) { int display_number, sock; u_short port; - struct sockaddr_in sin; - char buf[512]; + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; + char display[512]; char hostname[MAXHOSTNAMELEN]; for (display_number = x11_display_offset; display_number < MAX_DISPLAYS; display_number++) { port = 6000 + display_number; - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = htonl(INADDR_ANY); - sin.sin_port = htons(port); - - sock = socket(AF_INET, SOCK_STREAM, 0); - if (sock < 0) { - error("socket: %.100s", strerror(errno)); + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_flags = AI_PASSIVE; /* XXX loopback only ? */ + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", port); + if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { + error("getaddrinfo: %.100s", gai_strerror(gaierr)); return NULL; } - if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { - debug("bind port %d: %.100s", port, strerror(errno)); - shutdown(sock, SHUT_RDWR); - close(sock); - continue; + 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, SOCK_STREAM, 0); + if (sock < 0) { + error("socket: %.100s", strerror(errno)); + return NULL; + } + if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + debug("bind port %d: %.100s", port, strerror(errno)); + shutdown(sock, SHUT_RDWR); + close(sock); + for (n = 0; n < num_socks; n++) { + shutdown(socks[n], SHUT_RDWR); + close(socks[n]); + } + num_socks = 0; + break; + } + socks[num_socks++] = sock; + if (num_socks == NUM_SOCKS) + break; } - break; + if (num_socks > 0) + break; } if (display_number >= MAX_DISPLAYS) { error("Failed to allocate internet-domain X11 display socket."); return NULL; } /* Start listening for connections on the socket. */ - if (listen(sock, 5) < 0) { - error("listen: %.100s", strerror(errno)); - shutdown(sock, SHUT_RDWR); - close(sock); - return NULL; + for (n = 0; n < num_socks; n++) { + sock = socks[n]; + if (listen(sock, 5) < 0) { + error("listen: %.100s", strerror(errno)); + shutdown(sock, SHUT_RDWR); + close(sock); + return NULL; + } } + /* Set up a suitable value for the DISPLAY variable. */ if (gethostname(hostname, sizeof(hostname)) < 0) @@ -1192,21 +1258,24 @@ x11_create_display_inet(int screen_number, int x11_display_offset) memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr)); /* Set DISPLAY to <ip address>:screen.display */ - snprintf(buf, sizeof(buf), "%.50s:%d.%d", inet_ntoa(my_addr), + snprintf(display, sizeof(display), "%.50s:%d.%d", inet_ntoa(my_addr), display_number, screen_number); } #else /* IPADDR_IN_DISPLAY */ /* Just set DISPLAY to hostname:screen.display */ - snprintf(buf, sizeof buf, "%.400s:%d.%d", hostname, + snprintf(display, sizeof display, "%.400s:%d.%d", hostname, display_number, screen_number); #endif /* IPADDR_IN_DISPLAY */ - /* Allocate a channel for the socket. */ - (void) channel_allocate(SSH_CHANNEL_X11_LISTENER, sock, - xstrdup("X11 inet listener")); + /* Allocate a channel for each socket. */ + for (n = 0; n < num_socks; n++) { + sock = socks[n]; + (void) channel_allocate(SSH_CHANNEL_X11_LISTENER, sock, + xstrdup("X11 inet listener")); + } /* Return a suitable value for the DISPLAY environment variable. */ - return xstrdup(buf); + return xstrdup(display); } #ifndef X_UNIX_PATH @@ -1252,12 +1321,13 @@ connect_local_xsocket(unsigned int dnr) void x11_input_open(int payload_len) { - int remote_channel, display_number, sock, newch; + int remote_channel, display_number, sock = 0, newch; const char *display; - struct sockaddr_in sin; char buf[1024], *cp, *remote_host; - struct hostent *hp; int remote_len; + struct addrinfo hints, *ai, *aitop; + char strport[NI_MAXSERV]; + int gaierr; /* Get remote channel number. */ remote_channel = packet_get_int(); @@ -1323,42 +1393,38 @@ x11_input_open(int payload_len) display); goto fail; } - /* Try to parse the host name as a numeric IP address. */ - memset(&sin, 0, sizeof(sin)); - sin.sin_addr.s_addr = inet_addr(buf); - if ((sin.sin_addr.s_addr & 0xffffffff) != 0xffffffff) { - /* It was a valid numeric host address. */ - sin.sin_family = AF_INET; - } else { - /* Not a numeric IP address. */ - /* Look up the host address from the name servers. */ - hp = gethostbyname(buf); - if (!hp) { - error("%.100s: unknown host.", buf); - goto fail; - } - if (!hp->h_addr_list[0]) { - error("%.100s: host has no IP address.", buf); - goto fail; - } - sin.sin_family = hp->h_addrtype; - memcpy(&sin.sin_addr, hp->h_addr_list[0], - sizeof(sin.sin_addr)); - } - /* Set port number. */ - sin.sin_port = htons(6000 + display_number); - /* Create a socket. */ - sock = socket(sin.sin_family, SOCK_STREAM, 0); - if (sock < 0) { - error("socket: %.100s", strerror(errno)); + /* Look up the host address */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = IPv4or6; + hints.ai_socktype = SOCK_STREAM; + snprintf(strport, sizeof strport, "%d", 6000 + display_number); + if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { + error("%.100s: unknown host. (%s)", buf, gai_strerror(gaierr)); goto fail; } + for (ai = aitop; ai; ai = ai->ai_next) { + /* Create a socket. */ + sock = socket(ai->ai_family, SOCK_STREAM, 0); + if (sock < 0) { + debug("socket: %.100s", strerror(errno)); + continue; + } /* Connect it to the display. */ - if (connect(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) { - error("connect %.100s:%d: %.100s", buf, 6000 + display_number, - strerror(errno)); + if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { + debug("connect %.100s port %d: %.100s", buf, 6000 + display_number, + strerror(errno)); close(sock); + continue; + } + /* Success */ + break; + + } /* (ai = aitop, ai; ai = ai->ai_next) */ + freeaddrinfo(aitop); + if (!ai) { + error("connect %.100s port %d: %.100s", buf, 6000 + display_number, + strerror(errno)); goto fail; } success: |