- 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/ssh.c b/ssh.c
index f9e7722..962aa2d 100644
--- a/ssh.c
+++ b/ssh.c
@@ -11,7 +11,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: ssh.c,v 1.15 1999/12/28 23:17:09 damien Exp $");
+RCSID("$Id: ssh.c,v 1.16 2000/01/14 04:45:51 damien Exp $");
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -27,6 +27,10 @@
 const char *__progname = "ssh";
 #endif /* HAVE___PROGNAME */
 
+/* Flag indicating whether IPv4 or IPv6.  This can be set on the command line.
+   Default value is AF_UNSPEC means both IPv4 and IPv6. */
+int IPv4or6 = AF_UNSPEC;
+
 /* Flag indicating whether debug mode is on.  This can be set on the command line. */
 int debug_flag = 0;
 
@@ -59,7 +63,7 @@
 char *host;
 
 /* socket address the host resolves to */
-struct sockaddr_in hostaddr;
+struct sockaddr_storage hostaddr;
 
 /*
  * Flag to indicate that we have received a window change signal which has
@@ -114,6 +118,8 @@
 	fprintf(stderr, "              forward them to the other side by connecting to host:port.\n");
 	fprintf(stderr, "  -C          Enable compression.\n");
 	fprintf(stderr, "  -g          Allow remote hosts to connect to forwarded ports.\n");
+	fprintf(stderr, "  -4          Use IPv4 only.\n");
+	fprintf(stderr, "  -6          Use IPv6 only.\n");
 	fprintf(stderr, "  -o 'option' Process the option as if it was read from a configuration file.\n");
 	exit(1);
 }
@@ -227,6 +233,8 @@
 			if (host)
 				break;
 			if ((cp = strchr(av[optind], '@'))) {
+			        if(cp == av[optind])
+				        usage();
 				options.user = av[optind];
 				*cp = '\0';
 				host = ++cp;
@@ -250,6 +258,14 @@
 			optarg = NULL;
 		}
 		switch (opt) {
+		case '4':
+			IPv4or6 = AF_INET;
+			break;
+
+		case '6':
+			IPv4or6 = AF_INET6;
+			break;
+
 		case 'n':
 			stdin_null_flag = 1;
 			break;
@@ -351,8 +367,10 @@
 			break;
 
 		case 'R':
-			if (sscanf(optarg, "%hu:%255[^:]:%hu", &fwd_port, buf,
-				   &fwd_host_port) != 3) {
+			if (sscanf(optarg, "%hu/%255[^/]/%hu", &fwd_port, buf,
+			    &fwd_host_port) != 3 &&
+			    sscanf(optarg, "%hu:%255[^:]:%hu", &fwd_port, buf,
+			    &fwd_host_port) != 3) {
 				fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg);
 				usage();
 				/* NOTREACHED */
@@ -361,8 +379,10 @@
 			break;
 
 		case 'L':
-			if (sscanf(optarg, "%hu:%255[^:]:%hu", &fwd_port, buf,
-				   &fwd_host_port) != 3) {
+			if (sscanf(optarg, "%hu/%255[^/]/%hu", &fwd_port, buf,
+			    &fwd_host_port) != 3 &&
+			    sscanf(optarg, "%hu:%255[^:]:%hu", &fwd_port, buf,
+			    &fwd_host_port) != 3) {
 				fprintf(stderr, "Bad forwarding specification '%s'.\n", optarg);
 				usage();
 				/* NOTREACHED */
@@ -473,14 +493,18 @@
 
 	/* Find canonic host name. */
 	if (strchr(host, '.') == 0) {
-		struct hostent *hp = gethostbyname(host);
-		if (hp != 0) {
-			if (strchr(hp->h_name, '.') != 0)
-				host = xstrdup(hp->h_name);
-			else if (hp->h_aliases != 0
-				 && hp->h_aliases[0] != 0
-				 && strchr(hp->h_aliases[0], '.') != 0)
-				host = xstrdup(hp->h_aliases[0]);
+		struct addrinfo hints;
+		struct addrinfo *ai = NULL;
+		int errgai;
+		memset(&hints, 0, sizeof(hints));
+		hints.ai_family = AF_UNSPEC;
+		hints.ai_flags = AI_CANONNAME;
+		hints.ai_socktype = SOCK_STREAM;
+		errgai = getaddrinfo(host, NULL, &hints, &ai);
+		if (errgai == 0) {
+			if (ai->ai_canonname != NULL)
+				host = xstrdup(ai->ai_canonname);
+			freeaddrinfo(ai);
 		}
 	}
 	/* Disable rhosts authentication if not running as root. */
@@ -587,7 +611,7 @@
 
 	/* Log into the remote system.  This never returns if the login fails. */
 	ssh_login(host_private_key_loaded, host_private_key,
-		  host, &hostaddr, original_real_uid);
+		  host, (struct sockaddr *)&hostaddr, original_real_uid);
 
 	/* We no longer need the host private key.  Clear it now. */
 	if (host_private_key_loaded)