=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/channels.c,v retrieving revision 1.34 retrieving revision 1.35 diff -u -r1.34 -r1.35 --- src/usr.bin/ssh/channels.c 1999/12/27 09:48:38 1.34 +++ src/usr.bin/ssh/channels.c 2000/01/04 00:07:58 1.35 @@ -16,7 +16,7 @@ */ #include "includes.h" -RCSID("$Id: channels.c,v 1.34 1999/12/27 09:48:38 markus Exp $"); +RCSID("$Id: channels.c,v 1.35 2000/01/04 00:07:58 markus Exp $"); #include "ssh.h" #include "packet.h" @@ -374,7 +374,8 @@ 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 @@ * 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 @@ 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); @@ -878,50 +879,76 @@ 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)); + 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"); - /* Bind the socket to the address. */ - if (bind(sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) - packet_disconnect("bind: %.100s", strerror(errno)); + 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); - /* 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; + /* 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 +1027,13 @@ 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 +1075,47 @@ 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)); + /* 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 */ + } - sin.sin_port = htons(host_port); + freeaddrinfo(aitop); - /* Create the socket. */ - sock = socket(sin.sin_family, SOCK_STREAM, 0); - if (sock < 0) { - error("socket: %.100s", strerror(errno)); + if (!ai) { + error("connect %.100s port %d: failed.", host, host_port); 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); - goto fail; - } + /* Successful connection. */ /* Allocate a channel for this connection. */ @@ -1115,60 +1149,88 @@ * 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 = 0 /*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) fatal("gethostname: %.100s", strerror(errno)); - snprintf(buf, sizeof buf, "%.400s:%d.%d", hostname, + snprintf(display, sizeof display, "%.400s:%d.%d", hostname, display_number, screen_number); - /* 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 @@ -1214,12 +1276,13 @@ 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(); @@ -1285,42 +1348,38 @@ 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: