- Merged OpenBSD IPv6 patch:
   - [sshd.c sshd.8 sshconnect.c ssh.h ssh.c servconf.h servconf.c scp.1]
     [scp.c packet.h packet.c login.c log.c canohost.c channels.c]
     [hostfile.c sshd_config]
     ipv6 support: mostly gethostbyname->getaddrinfo/getnameinfo, new
     features: sshd allows multiple ListenAddress and Port options. note
     that libwrap is not IPv6-ready. (based on patches from
     fujiwara@rcac.tdi.co.jp)
   - [ssh.c canohost.c]
     more hints (hints.ai_socktype=SOCK_STREAM) for getaddrinfo,
     from itojun@
   - [channels.c]
     listen on _all_ interfaces for X11-Fwd (hints.ai_flags = AI_PASSIVE)
   - [packet.h]
     allow auth-kerberos for IPv4 only
   - [scp.1 sshd.8 servconf.h scp.c]
     document -4, -6, and 'ssh -L 2022/::1/22'
   - [ssh.c]
     'ssh @host' is illegal (null user name), from
     karsten@gedankenpolizei.de
   - [sshconnect.c]
     better error message
   - [sshd.c]
     allow auth-kerberos for IPv4 only
 - Big IPv6 merge:
   - Cleanup overrun in sockaddr copying on RHL 6.1
   - Replacements for getaddrinfo, getnameinfo, etc based on versions
     from patch from KIKUCHI Takahiro <kick@kyoto.wide.ad.jp>
   - Replacement for missing structures on systems that lack IPv6
   - record_login needed to know about AF_INET6 addresses
   - Borrowed more code from OpenBSD: rresvport_af and requisites
diff --git a/sshconnect.c b/sshconnect.c
index e19392a..fb6af67 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -8,7 +8,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: sshconnect.c,v 1.20 2000/01/03 12:41:05 damien Exp $");
+RCSID("$Id: sshconnect.c,v 1.21 2000/01/14 04:45:52 damien Exp $");
 
 #ifdef HAVE_OPENSSL
 #include <openssl/bn.h>
@@ -35,6 +35,7 @@
 unsigned char session_id[16];
 
 extern Options options;
+extern char *__progname;
 
 /*
  * Connect to the given ssh server using a proxy command.
@@ -48,10 +49,10 @@
 	char *command_string;
 	int pin[2], pout[2];
 	int pid;
-	char portstring[100];
+	char strport[NI_MAXSERV];
 
 	/* Convert the port number into a string. */
-	snprintf(portstring, sizeof portstring, "%hu", port);
+	snprintf(strport, sizeof strport, "%hu", port);
 
 	/* Build the final command string in the buffer by making the
 	   appropriate substitutions to the given proxy command. */
@@ -68,7 +69,7 @@
 			continue;
 		}
 		if (cp[0] == '%' && cp[1] == 'p') {
-			buffer_append(&command, portstring, strlen(portstring));
+			buffer_append(&command, strport, strlen(strport));
 			cp++;
 			continue;
 		}
@@ -140,7 +141,7 @@
  * Creates a (possibly privileged) socket for use as the ssh connection.
  */
 int
-ssh_create_socket(uid_t original_real_uid, int privileged)
+ssh_create_socket(uid_t original_real_uid, int privileged, int family)
 {
 	int sock;
 
@@ -150,10 +151,9 @@
 	 */
 	if (privileged) {
 		int p = IPPORT_RESERVED - 1;
-
-		sock = rresvport(&p);
+		sock = rresvport_af(&p, family);
 		if (sock < 0)
-			fatal("rresvport: %.100s", strerror(errno));
+			fatal("rresvport: af=%d %.100s", family, strerror(errno));
 		debug("Allocated local port %d.", p);
 	} else {
 		/*
@@ -161,17 +161,18 @@
 		 * the user's uid to create the socket.
 		 */
 		temporarily_use_uid(original_real_uid);
-		sock = socket(AF_INET, SOCK_STREAM, 0);
+		sock = socket(family, SOCK_STREAM, 0);
 		if (sock < 0)
-			fatal("socket: %.100s", strerror(errno));
+			error("socket: %.100s", strerror(errno));
 		restore_uid();
 	}
 	return sock;
 }
 
 /*
- * Opens a TCP/IP connection to the remote server on the given host.  If
- * port is 0, the default port will be used.  If anonymous is zero,
+ * Opens a TCP/IP connection to the remote server on the given host.
+ * The address of the remote host will be returned in hostaddr.
+ * If port is 0, the default port will be used.  If anonymous is zero,
  * a privileged port will be allocated to make the connection.
  * This requires super-user privileges if anonymous is false.
  * Connection_attempts specifies the maximum number of tries (one per
@@ -180,15 +181,16 @@
  * the daemon.
  */
 int
-ssh_connect(const char *host, struct sockaddr_in * hostaddr,
+ssh_connect(const char *host, struct sockaddr_storage * hostaddr,
 	    u_short port, int connection_attempts,
 	    int anonymous, uid_t original_real_uid,
 	    const char *proxy_command)
 {
-	int sock = -1, attempt, i;
-	int on = 1;
+	int sock = -1, attempt;
 	struct servent *sp;
-	struct hostent *hp;
+	struct addrinfo hints, *ai, *aitop;
+	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+	int gaierr;
 	struct linger linger;
 
 	debug("ssh_connect: getuid %d geteuid %d anon %d",
@@ -208,8 +210,13 @@
 
 	/* No proxy command. */
 
-	/* No host lookup made yet. */
-	hp = NULL;
+	memset(&hints, 0, sizeof(hints));
+	hints.ai_family = IPv4or6;
+	hints.ai_socktype = SOCK_STREAM;
+	snprintf(strport, sizeof strport, "%d", port);
+	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0)
+		fatal("%s: %.100s: %s", __progname, host,
+		    gai_strerror(gaierr));
 
 	/*
 	 * Try to connect several times.  On some machines, the first time
@@ -220,82 +227,40 @@
 		if (attempt > 0)
 			debug("Trying again...");
 
-		/* Try to parse the host name as a numeric inet address. */
-		memset(hostaddr, 0, sizeof(hostaddr));
-		hostaddr->sin_family = AF_INET;
-		hostaddr->sin_port = htons(port);
-		hostaddr->sin_addr.s_addr = inet_addr(host);
-		if ((hostaddr->sin_addr.s_addr & 0xffffffff) != 0xffffffff) {
-			/* Valid numeric IP address */
-			debug("Connecting to %.100s port %d.",
-			      inet_ntoa(hostaddr->sin_addr), port);
+		/* Loop through addresses for this host, and try each one in
+ 		   sequence until the connection succeeds. */
+		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("ssh_connect: getnameinfo failed");
+				continue;
+			}
+			debug("Connecting to %.200s [%.100s] port %s.",
+				host, ntop, strport);
 
-			/* Create a socket. */
-			sock = ssh_create_socket(original_real_uid,
-					  !anonymous && geteuid() == 0 &&
-						 port < IPPORT_RESERVED);
+			/* Create a socket for connecting. */
+			sock = ssh_create_socket(original_real_uid, 
+			    !anonymous && geteuid() == 0 && port < IPPORT_RESERVED,
+			    ai->ai_family);
+			if (sock < 0)
+				continue;
 
-			/*
-			 * Connect to the host.  We use the user's uid in the
-			 * hope that it will help with the problems of
-			 * tcp_wrappers showing the remote uid as root.
+			/* Connect to the host.  We use the user's uid in the
+			 * hope that it will help with tcp_wrappers showing
+			 * the remote uid as root.
 			 */
 			temporarily_use_uid(original_real_uid);
-			if (connect(sock, (struct sockaddr *) hostaddr, sizeof(*hostaddr))
-			    >= 0) {
-				/* Successful connect. */
+			if (connect(sock, ai->ai_addr, ai->ai_addrlen) >= 0) {
+				/* Successful connection. */
+				memcpy(hostaddr, ai->ai_addr, sizeof(*(ai->ai_addr)));
 				restore_uid();
 				break;
-			}
-			debug("connect: %.100s", strerror(errno));
-			restore_uid();
-
-			/* Destroy the failed socket. */
-			shutdown(sock, SHUT_RDWR);
-			close(sock);
-		} else {
-			/* Not a valid numeric inet address. */
-			/* Map host name to an address. */
-			if (!hp)
-				hp = gethostbyname(host);
-			if (!hp)
-				fatal("Bad host name: %.100s", host);
-			if (!hp->h_addr_list[0])
-				fatal("Host does not have an IP address: %.100s", host);
-
-			/* Loop through addresses for this host, and try
-			   each one in sequence until the connection
-			   succeeds. */
-			for (i = 0; hp->h_addr_list[i]; i++) {
-				/* Set the address to connect to. */
-				hostaddr->sin_family = hp->h_addrtype;
-				memcpy(&hostaddr->sin_addr, hp->h_addr_list[i],
-				       sizeof(hostaddr->sin_addr));
-
-				debug("Connecting to %.200s [%.100s] port %d.",
-				      host, inet_ntoa(hostaddr->sin_addr), port);
-
-				/* Create a socket for connecting. */
-				sock = ssh_create_socket(original_real_uid,
-					  !anonymous && geteuid() == 0 &&
-						 port < IPPORT_RESERVED);
-
-				/*
-				 * Connect to the host.  We use the user's
-				 * uid in the hope that it will help with
-				 * tcp_wrappers showing the remote uid as
-				 * root.
-				 */
-				temporarily_use_uid(original_real_uid);
-				if (connect(sock, (struct sockaddr *) hostaddr,
-					    sizeof(*hostaddr)) >= 0) {
-					/* Successful connection. */
-					restore_uid();
-					break;
-				}
+			} else {
 				debug("connect: %.100s", strerror(errno));
 				restore_uid();
-
 				/*
 				 * Close the failed socket; there appear to
 				 * be some problems when reusing a socket for
@@ -305,13 +270,16 @@
 				shutdown(sock, SHUT_RDWR);
 				close(sock);
 			}
-			if (hp->h_addr_list[i])
-				break;	/* Successful connection. */
 		}
+		if (ai)
+			break;	/* Successful connection. */
 
 		/* Sleep a moment before retrying. */
 		sleep(1);
 	}
+
+	freeaddrinfo(aitop);
+
 	/* Return failure if we didn't get a successful connection. */
 	if (attempt >= connection_attempts)
 		return 0;
@@ -323,7 +291,6 @@
 	 * as it has been closed for whatever reason.
 	 */
 	/* setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&on, sizeof(on)); */
-	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *) &on, sizeof(on));
 	linger.l_onoff = 1;
 	linger.l_linger = 5;
 	setsockopt(sock, SOL_SOCKET, SO_LINGER, (void *) &linger, sizeof(linger));
@@ -1095,17 +1062,43 @@
  */
 
 void
-check_host_key(char *host,
-    struct sockaddr_in *hostaddr,
-    RSA *host_key)
+check_host_key(char *host, struct sockaddr *hostaddr, RSA *host_key)
 {
 	RSA *file_key;
 	char *ip = NULL;
 	char hostline[1000], *hostp;
 	HostStatus host_status;
 	HostStatus ip_status;
-	int host_ip_differ = 0;
-	int local = (ntohl(hostaddr->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
+	int local = 0, host_ip_differ = 0;
+	int sa_len;
+	char ntop[NI_MAXHOST];
+
+	/*
+	 * Force accepting of the host key for loopback/localhost. The
+	 * problem is that if the home directory is NFS-mounted to multiple
+	 * machines, localhost will refer to a different machine in each of
+	 * them, and the user will get bogus HOST_CHANGED warnings.  This
+	 * essentially disables host authentication for localhost; however,
+	 * this is probably not a real problem.
+	 */
+	switch (hostaddr->sa_family) {
+	case AF_INET:
+		local = (ntohl(((struct sockaddr_in *)hostaddr)->sin_addr.s_addr) >> 24) == IN_LOOPBACKNET;
+		sa_len = sizeof(struct sockaddr_in);
+		break;
+	case AF_INET6:
+		local = IN6_IS_ADDR_LOOPBACK(&(((struct sockaddr_in6 *)hostaddr)->sin6_addr));
+		sa_len = sizeof(struct sockaddr_in6);
+		break;
+	default:
+		local = 0;
+		sa_len = sizeof(struct sockaddr_storage);
+		break;
+	}
+	if (local) {
+		debug("Forcing accepting of host key for loopback/localhost.");
+		return;
+	}
 
 	/*
 	 * Turn off check_host_ip for proxy connects, since
@@ -1114,8 +1107,12 @@
 	if (options.proxy_command != NULL && options.check_host_ip)
 		options.check_host_ip = 0;
 
-	if (options.check_host_ip)
-		ip = xstrdup(inet_ntoa(hostaddr->sin_addr));
+	if (options.check_host_ip) {
+		if (getnameinfo(hostaddr, sa_len, ntop, sizeof(ntop),
+		    NULL, 0, NI_NUMERICHOST) != 0)
+			fatal("check_host_key: getnameinfo failed");
+		ip = xstrdup(ntop);
+	}
 
 	/*
 	 * Store the host key from the known host file in here so that we can
@@ -1137,18 +1134,6 @@
 						host_key->e, host_key->n,
 					       file_key->e, file_key->n);
 	/*
-	 * Force accepting of the host key for localhost and 127.0.0.1. The
-	 * problem is that if the home directory is NFS-mounted to multiple
-	 * machines, localhost will refer to a different machine in each of
-	 * them, and the user will get bogus HOST_CHANGED warnings.  This
-	 * essentially disables host authentication for localhost; however,
-	 * this is probably not a real problem.
-	 */
-	if (local) {
-		debug("Forcing accepting of host key for localhost.");
-		host_status = HOST_OK;
-	}
-	/*
 	 * Also perform check for the ip address, skip the check if we are
 	 * localhost or the hostname was an ip address to begin with
 	 */
@@ -1301,7 +1286,7 @@
 ssh_login(int host_key_valid,
 	  RSA *own_host_key,
 	  const char *orighost,
-	  struct sockaddr_in *hostaddr,
+	  struct sockaddr *hostaddr,
 	  uid_t original_real_uid)
 {
 	int i, type;