]> andersk Git - openssh.git/commitdiff
- Merged OpenBSD IPv6 patch:
authordamien <damien>
Fri, 14 Jan 2000 04:45:46 +0000 (04:45 +0000)
committerdamien <damien>
Fri, 14 Jan 2000 04:45:46 +0000 (04:45 +0000)
   - [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

38 files changed:
ChangeLog
Makefile.in
TODO
acconfig.h
bsd-bindresvport.c [new file with mode: 0644]
bsd-bindresvport.h [new file with mode: 0644]
bsd-rresvport.c [new file with mode: 0644]
bsd-rresvport.h [new file with mode: 0644]
canohost.c
channels.c
configure.in
defines.h
fake-gai-errnos.h [new file with mode: 0644]
fake-getaddrinfo.c [new file with mode: 0644]
fake-getaddrinfo.h [new file with mode: 0644]
fake-getnameinfo.c [new file with mode: 0644]
fake-getnameinfo.h [new file with mode: 0644]
fake-socket.h [new file with mode: 0644]
fixpaths
hostfile.c
includes.h
log.c
login.c
nchan.c
nchan.ms
packet.c
packet.h
scp.1.in
scp.c
servconf.c
servconf.h
ssh.1.in
ssh.c
ssh.h
sshconnect.c
sshd.8.in
sshd.c
sshd_config.in

index e96dfb803003477f3081330225fe0ba43c5f2f45..701bf403f60c4c3f7dbd83f90181a16836f75c82 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,36 @@
+20000114
+ - 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
+
 20000110
  - Fixes to auth-skey to enable it to use the standard OpenSSL libraries
 
index 23f0058228f356e16d859b0979aaf6fbf2b9386e..786cd43686f3e8613e4a78465effc9c05d71b004 100644 (file)
@@ -19,7 +19,6 @@ CC=@CC@
 PATHS=-DETCDIR=\"$(sysconfdir)\" -DSSH_PROGRAM=\"$(SSH_PROGRAM)\" -DSSH_ASKPASS_DEFAULT=\"$(ASKPASS_PROGRAM)\"
 CFLAGS=@CFLAGS@ $(PATHS) @DEFS@
 EXTRA_TARGETS=@GNOME_ASKPASS@
-TARGETS=libssh.a ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS)
 LIBS=@LIBS@
 AR=@AR@
 RANLIB=@RANLIB@
@@ -30,34 +29,19 @@ LDFLAGS=-L. @LDFLAGS@
 GNOME_CFLAGS=`gnome-config --cflags gnome gnomeui`
 GNOME_LIBS=`gnome-config --libs gnome gnomeui`
 
-OBJS= atomicio.o authfd.o authfile.o auth-krb4.o auth-passwd.o auth-pam.o \
-  auth-rhosts.o auth-rh-rsa.o auth-rsa.o auth-skey.o bsd-daemon.o \
-  bsd-login.o bsd-misc.o bsd-mktemp.o bsd-snprintf.o bsd-strlcat.o \
-  bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o \
-  clientloop.o compress.o crc32.o deattack.o hostfile.o \
-  log-client.o login.o log-server.o match.o md5crypt.o mpaux.o \
-  packet.o pty.o radix.o readconf.o readpass.o rsa.o servconf.o \
-  serverloop.o sshconnect.o tildexpand.o ttymodes.o uidswap.o \
-  xmalloc.o 
-
-LIBOBJS= atomicio.o authfd.o authfile.o bsd-daemon.o bsd-misc.o \
-  bsd-mktemp.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o \
-  buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o \
-  deattack.o fingerprint.o hostfile.o log.o match.o mpaux.o nchan.o \
-  packet.o radix.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o \
-  xmalloc.o 
+TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS)
+
+LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hostfile.o log.o match.o mpaux.o nchan.o packet.o radix.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o 
 
 SSHOBJS= ssh.o sshconnect.o log-client.o readconf.o clientloop.o
 
-SSHDOBJS= sshd.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o \
-  auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o \
-  serverloop.o bsd-login.o md5crypt.o
+SSHDOBJS= sshd.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o bsd-login.o md5crypt.o
 
 MANPAGES=scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8
 
 CONFIGFILES=sshd_config ssh_config
 
-all: $(OBJS) $(TARGETS) $(MANPAGES) $(CONFIGFILES)
+all: $(TARGETS) $(MANPAGES) $(CONFIGFILES)
 
 $(OBJS): config.h
 
@@ -67,31 +51,27 @@ libssh.a: $(LIBOBJS)
        $(AR) rv $@ $(LIBOBJS)
        $(RANLIB) $@
 
-ssh: $(SSHOBJS) libssh.a
+ssh: libssh.a $(SSHOBJS)
        $(CC) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh $(LIBS)
 
-sshd:  $(SSHDOBJS) libssh.a
+sshd: libssh.a $(SSHDOBJS)
        $(CC) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh $(LIBS)
 
-scp:   scp.o libssh.a
+scp: libssh.a scp.o
        $(CC) -o $@ scp.o $(LDFLAGS) -lssh $(LIBS) 
 
-ssh-add: ssh-add.o log-client.o libssh.a
+ssh-add: libssh.a ssh-add.o log-client.o
        $(CC) -o $@ ssh-add.o log-client.o $(LDFLAGS) -lssh $(LIBS) 
 
-ssh-agent: ssh-agent.o log-client.o libssh.a
+ssh-agent: libssh.a ssh-agent.o log-client.o
        $(CC) -o $@ ssh-agent.o log-client.o $(LDFLAGS) -lssh $(LIBS) 
 
-ssh-keygen: ssh-keygen.o log-client.o libssh.a
+ssh-keygen: libssh.a ssh-keygen.o log-client.o
        $(CC) -o $@ ssh-keygen.o log-client.o $(LDFLAGS) -lssh $(LIBS) 
 
 gnome-ssh-askpass: gnome-ssh-askpass.c
        $(CC) $(CFLAGS) $(GNOME_CFLAGS) -o $@ gnome-ssh-askpass.c $(GNOME_LIBS)
 
-clean:
-       rm -f *.o $(TARGETS) config.status config.cache config.log core \
-               *.1 *.8 sshd_config ssh_config
-
 scp.1: scp.1.in
        $(PERL) $(FIXPATHS) -Dsysconfdir=${sysconfdir} $(srcdir)/scp.1.in
 
@@ -116,6 +96,18 @@ sshd_config: sshd_config.in
 ssh_config: ssh_config.in
        $(PERL) $(FIXPATHS) -Dsysconfdir=${sysconfdir} ssh_config.in
 
+clean:
+       rm -f *.o *.a $(TARGETS) config.status config.cache config.log 
+       rm -f core *.1 *.8 sshd_config ssh_config
+
+distclean: clean
+       rm -f Makefile config.h core *~
+
+mrproper: distclean
+
+veryclean: distclean
+       rm -f configure config.h.in
+
 install: $(TARGETS)
        $(INSTALL) -d $(bindir)
        $(INSTALL) -d $(sbindir)
@@ -180,11 +172,3 @@ uninstall:
        -rm -f ${ASKPASS_PROGRAM}
        -rmdir $(libexecdir)/ssh ;
 
-distclean: clean
-       rm -f Makefile config.h core *~
-
-mrproper: distclean
-
-veryclean: distclean
-       rm -f configure config.h.in
-
diff --git a/TODO b/TODO
index e3249fd155b430ae04b64157de9d652345af4569..0eccc9be40c16721c6ac2d04f0aa278cb8569fa1 100644 (file)
--- a/TODO
+++ b/TODO
@@ -7,6 +7,8 @@
 - Replace the horror in acconfig.h which tries to comphensate for the 
   lack of u_intXX_t types. There must be a better way.
 
+- Move all compatability cruft (bsd-*, fake-*) into subordinate library
+
 - Hanging on logout:
 
 localhost$ ssh remotehost
index e61bfd33ac6fd2b99304f9e51cdc8474c1126c98..52aef76a6e387306ae30a34d7c1103ff316a2c46 100644 (file)
  * message at run-time. */
 #undef RSAREF
 
-/* Define is utmp.h has a ut_host field */
+/* struct utmp and struct utmpx fields */
 #undef HAVE_HOST_IN_UTMP
-
-/* Define is utmpx.h has a ut_host field */
 #undef HAVE_HOST_IN_UTMPX
-
-/* Define is utmp.h has a ut_addr field */
 #undef HAVE_ADDR_IN_UTMP
-
-/* Define is utmpx.h has a ut_addr field */
 #undef HAVE_ADDR_IN_UTMPX
-
-/* Define is utmpx.h has a syslen field */
+#undef HAVE_ADDR_V6_IN_UTMP
+#undef HAVE_ADDR_V6_IN_UTMPX
 #undef HAVE_SYSLEN_IN_UTMPX
-
-/* Define is utmp.h has a ut_pid field */
 #undef HAVE_PID_IN_UTMP
-
-/* Define is utmp.h has a ut_type field */
 #undef HAVE_TYPE_IN_UTMP
-
-/* Define is utmp.h has a ut_tv field */
 #undef HAVE_TV_IN_UTMP
-
-/* Define is utmp.h has a ut_id field */
 #undef HAVE_ID_IN_UTMP
 
 /* Define if you want to use utmpx */
 #undef HAVE_UINTXX_T
 #undef HAVE_SOCKLEN_T
 #undef HAVE_SIZE_T
+#undef HAVE_STRUCT_SOCKADDR_STORAGE
+#undef HAVE_STRUCT_ADDRINFO
+#undef HAVE_STRUCT_IN6_ADDR
+#undef HAVE_STRUCT_SOCKADDR_IN6
+
+/* Fields in struct sockaddr_storage */
+#undef HAVE_SS_FAMILY_IN_SS
+#undef HAVE___SS_FAMILY_IN_SS
 
 /* Define if you have /dev/ptmx */
 #undef HAVE_DEV_PTMX
diff --git a/bsd-bindresvport.c b/bsd-bindresvport.c
new file mode 100644 (file)
index 0000000..3ea37ee
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part.  Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ * 
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ * 
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ * 
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ * 
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ * 
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California  94043
+ */
+
+#include "config.h"
+
+#ifndef HAVE_BINRESVPORT_AF
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: bindresvport.c,v 1.11 1999/12/17 19:22:08 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * Copyright (c) 1987 by Sun Microsystems, Inc.
+ *
+ * Portions Copyright(C) 1996, Jason Downs.  All rights reserved.
+ */
+
+#include "includes.h"
+
+#define STARTPORT 600
+#define ENDPORT (IPPORT_RESERVED - 1)
+#define NPORTS (ENDPORT - STARTPORT + 1)
+
+#if 0
+/*
+ * Bind a socket to a privileged IP port
+ */
+int
+bindresvport(sd, sin)
+       int sd;
+       struct sockaddr_in *sin;
+{
+       return bindresvport_af(sd, (struct sockaddr *)sin, AF_INET);
+}
+#endif 
+
+/*
+ * Bind a socket to a privileged IP port
+ */
+int
+bindresvport_af(sd, sa, af)
+       int sd;
+       struct sockaddr *sa;
+       int af;
+{
+       int error;
+       struct sockaddr_storage myaddr;
+       struct sockaddr_in *sin;
+       struct sockaddr_in6 *sin6;
+       u_int16_t *portp;
+       int salen;
+       int i;
+
+       if (sa == NULL) {
+               memset(&myaddr, 0, sizeof(myaddr));
+               sa = (struct sockaddr *)&myaddr;
+       }
+
+       if (af == AF_INET) {
+               sin = (struct sockaddr_in *)sa;
+               salen = sizeof(struct sockaddr_in);
+               portp = &sin->sin_port;
+       } else if (af == AF_INET6) {
+               sin6 = (struct sockaddr_in6 *)sa;
+               salen = sizeof(struct sockaddr_in6);
+               portp = &sin6->sin6_port;
+       } else {
+               errno = EPFNOSUPPORT;
+               return (-1);
+       }
+       sa->sa_family = af;
+
+       if (*portp == 0)
+               *portp = (getpid() % NPORTS) + STARTPORT;
+
+       for(i = 0; i < NPORTS; i++) {
+               error = bind(sd, sa, salen);
+               
+               if ((error == 0) || ((error < 0) && (errno != EADDRINUSE)))
+                       break;
+                       
+               (*portp)++;
+               if (*portp < ENDPORT)
+                       *portp = STARTPORT;
+       }
+
+       return (error);
+}
+
+#endif /* HAVE_BINRESVPORT_AF */
diff --git a/bsd-bindresvport.h b/bsd-bindresvport.h
new file mode 100644 (file)
index 0000000..df084e3
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _BSD_BINRESVPORT_H
+#define _BSD_BINRESVPORT_H
+
+#include "config.h"
+
+#ifndef HAVE_BINRESVPORT_AF
+int bindresvport_af(int sd, struct sockaddr *sa, int af);
+#endif /* !HAVE_BINRESVPORT_AF */
+
+#endif /* _BSD_BINRESVPORT_H */
diff --git a/bsd-rresvport.c b/bsd-rresvport.c
new file mode 100644 (file)
index 0000000..c29165f
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 1995, 1996, 1998 Theo de Raadt.  All rights reserved.
+ * Copyright (c) 1983, 1993, 1994
+ *     The Regents of the University of California.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ *     This product includes software developed by Theo de Raadt.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#ifndef HAVE_RRESVPORT_AF
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char *rcsid = "$OpenBSD: rresvport.c,v 1.4 1999/12/17 20:48:03 deraadt Exp $";
+#endif /* LIBC_SCCS and not lint */
+
+#include "includes.h"
+
+#if 0
+int
+rresvport(alport)
+       int *alport;
+{
+       return rresvport_af(alport, AF_INET);
+}
+#endif
+
+int
+rresvport_af(alport, af)
+       int *alport;
+       int af;
+{
+       struct sockaddr_storage ss;
+       struct sockaddr *sa;
+       u_int16_t *portp;
+       int s;
+       int sa_len;
+
+       bzero(&ss, sizeof ss);
+       sa = (struct sockaddr *)&ss;
+
+       switch (af) {
+       case AF_INET:
+               sa_len = sizeof(struct sockaddr_in);
+               portp = &((struct sockaddr_in *)sa)->sin_port;
+               break;
+       case AF_INET6:
+               sa_len = sizeof(struct sockaddr_in6);
+               portp = &((struct sockaddr_in6 *)sa)->sin6_port;
+               break;
+       default:
+               errno = EPFNOSUPPORT;
+               return (-1);
+       }
+       sa->sa_family = af;
+       
+       s = socket(af, SOCK_STREAM, 0);
+       if (s < 0)
+               return (-1);
+
+       *portp = htons(*alport);
+       if (*alport < IPPORT_RESERVED - 1) {
+               if (bind(s, sa, sa_len) >= 0)
+                       return (s);
+               if (errno != EADDRINUSE) {
+                       (void)close(s);
+                       return (-1);
+               }
+       }
+
+       *portp = 0;
+       if (bindresvport_af(s, sa, af) == -1) {
+               (void)close(s);
+               return (-1);
+       }
+       *alport = ntohs(*portp);
+       return (s);
+}
+
+#endif /* HAVE_RRESVPORT_AF */
diff --git a/bsd-rresvport.h b/bsd-rresvport.h
new file mode 100644 (file)
index 0000000..69d6bbd
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _BSD_RRESVPORT_H
+#define _BSD_RRESVPORT_H
+
+#include "config.h"
+
+#ifndef HAVE_RRESVPORT_AF
+int rresvport_af(int *alport, int af);
+#endif /* !HAVE_RRESVPORT_AF */
+
+#endif /* _BSD_RRESVPORT_H */
index 76b5ed2fe5b79ebcecaf395fa369703e2ea339da..4e9c6a9236ea29ff89a394d6b0c93445040f169c 100644 (file)
@@ -28,10 +28,12 @@ RCSID("$Id$");
 char *
 get_remote_hostname(int socket)
 {
-       struct sockaddr_in from;
-       int fromlen, i;
-       struct hostent *hp;
+       struct sockaddr_storage from;
+       int i;
+       socklen_t fromlen;
+       struct addrinfo hints, *ai, *aitop;
        char name[MAXHOSTNAMELEN];
+       char ntop[NI_MAXHOST], ntop2[NI_MAXHOST];
 
        /* Get IP address of client. */
        fromlen = sizeof(from);
@@ -40,20 +42,15 @@ get_remote_hostname(int socket)
                debug("getpeername failed: %.100s", strerror(errno));
                fatal_cleanup();
        }
-       /* Map the IP address to a host name. */
-       hp = gethostbyaddr((char *) &from.sin_addr, sizeof(struct in_addr),
-                          from.sin_family);
-       if (hp) {
-               /* Got host name, find canonic host name. */
-               if (strchr(hp->h_name, '.') != 0)
-                       strlcpy(name, hp->h_name, sizeof(name));
-               else if (hp->h_aliases != 0
-                        && hp->h_aliases[0] != 0
-                        && strchr(hp->h_aliases[0], '.') != 0)
-                       strlcpy(name, hp->h_aliases[0], sizeof(name));
-               else
-                       strlcpy(name, hp->h_name, sizeof(name));
+       if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
+            NULL, 0, NI_NUMERICHOST) != 0)
+               fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed");
 
+       /* Map the IP address to a host name. */
+       if (getnameinfo((struct sockaddr *)&from, fromlen, name, sizeof(name),
+            NULL, 0, NI_NAMEREQD) == 0) {
+               /* Got host name. */
+               name[sizeof(name) - 1] = '\0';
                /*
                 * Convert it to all lowercase (which is expected by the rest
                 * of this software).
@@ -71,32 +68,34 @@ get_remote_hostname(int socket)
                 * fooled if the intruder has access to the name server of
                 * the domain).
                 */
-               hp = gethostbyname(name);
-               if (!hp) {
-                       log("reverse mapping checking gethostbyname for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name);
-                       strlcpy(name, inet_ntoa(from.sin_addr), sizeof name);
+               memset(&hints, 0, sizeof(hints));
+               hints.ai_family = from.ss_family;
+               hints.ai_socktype = SOCK_STREAM;
+               if (getaddrinfo(name, NULL, &hints, &aitop) != 0) {
+                       log("reverse mapping checking getaddrinfo for %.700s failed - POSSIBLE BREAKIN ATTEMPT!", name);
+                       strlcpy(name, ntop, sizeof name);
                        goto check_ip_options;
                }
                /* Look for the address from the list of addresses. */
-               for (i = 0; hp->h_addr_list[i]; i++)
-                       if (memcmp(hp->h_addr_list[i], &from.sin_addr, sizeof(from.sin_addr))
-                           == 0)
-                               break;
-               /*
-                * If we reached the end of the list, the address was not
-                * there.
-                */
-               if (!hp->h_addr_list[i]) {
+               for (ai = aitop; ai; ai = ai->ai_next) {
+                       if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop2,
+                           sizeof(ntop2), NULL, 0, NI_NUMERICHOST) == 0 &&
+                           (strcmp(ntop, ntop2) == 0))
+                                       break;
+               }
+               freeaddrinfo(aitop);
+               /* If we reached the end of the list, the address was not there. */
+               if (!ai) {
                        /* Address not found for the host name. */
                        log("Address %.100s maps to %.600s, but this does not map back to the address - POSSIBLE BREAKIN ATTEMPT!",
-                           inet_ntoa(from.sin_addr), name);
-                       strlcpy(name, inet_ntoa(from.sin_addr), sizeof name);
+                           ntop, name);
+                       strlcpy(name, ntop, sizeof name);
                        goto check_ip_options;
                }
                /* Address was found for the host name.  We accept the host name. */
        } else {
                /* Host name not found.  Use ascii representation of the address. */
-               strlcpy(name, inet_ntoa(from.sin_addr), sizeof name);
+               strlcpy(name, ntop, sizeof name);
                log("Could not reverse map address %.100s.", name);
        }
 
@@ -113,10 +112,12 @@ check_ip_options:
         * rest of the interaction and could still bypass security.  So we
         * exit here if we detect any IP options.
         */
-       {
+       /* IP options -- IPv4 only */
+       if (from.ss_family == AF_INET) {
                unsigned char options[200], *ucp;
                char text[1024], *cp;
-               int option_size, ipproto;
+               socklen_t option_size;
+               int ipproto;
                struct protoent *ip;
 
                if ((ip = getprotobyname("ip")) != NULL)
@@ -125,47 +126,21 @@ check_ip_options:
                        ipproto = IPPROTO_IP;
                option_size = sizeof(options);
                if (getsockopt(0, ipproto, IP_OPTIONS, (char *) options,
-                              &option_size) >= 0 && option_size != 0) {
+                   &option_size) >= 0 && option_size != 0) {
                        cp = text;
                        /* Note: "text" buffer must be at least 3x as big as options. */
                        for (ucp = options; option_size > 0; ucp++, option_size--, cp += 3)
                                sprintf(cp, " %2.2x", *ucp);
                        log("Connection from %.100s with IP options:%.800s",
-                           inet_ntoa(from.sin_addr), text);
+                           ntop, text);
                        packet_disconnect("Connection from %.100s with IP options:%.800s",
-                                         inet_ntoa(from.sin_addr), text);
+                                         ntop, text);
                }
        }
 
        return xstrdup(name);
 }
 
-static char *canonical_host_name = NULL;
-static char *canonical_host_ip = NULL;
-
-/* Returns 1 if remote host is connected via socket, 0 if not. */
-
-int
-peer_connection_is_on_socket()
-{
-       struct sockaddr_in from;
-       int fromlen;
-       int in = packet_get_connection_in();
-       int out = packet_get_connection_out();
-
-       /* filedescriptors in and out are the same, so it's a socket */
-       if (in == out)
-               return 1;
-       fromlen = sizeof(from);
-       memset(&from, 0, sizeof(from));
-       if (getpeername(in, (struct sockaddr *) & from, &fromlen) < 0)
-               return 0;
-       if (from.sin_family != AF_INET)
-               return 0;
-
-       return 1;
-}
-
 /*
  * Return the canonical name of the host in the other side of the current
  * connection.  The host name is cached, so it is efficient to call this
@@ -175,12 +150,14 @@ peer_connection_is_on_socket()
 const char *
 get_canonical_hostname()
 {
+       static char *canonical_host_name = NULL;
+
        /* Check if we have previously retrieved this same name. */
        if (canonical_host_name != NULL)
                return canonical_host_name;
 
        /* Get the real hostname if socket; otherwise return UNKNOWN. */
-       if (peer_connection_is_on_socket())
+       if (packet_connection_is_on_socket())
                canonical_host_name = get_remote_hostname(packet_get_connection_in());
        else
                canonical_host_name = xstrdup("UNKNOWN");
@@ -190,21 +167,24 @@ get_canonical_hostname()
 
 /*
  * Returns the IP-address of the remote host as a string.  The returned
- * string need not be freed.
+ * string must not be freed.
  */
 
 const char *
 get_remote_ipaddr()
 {
-       struct sockaddr_in from;
-       int fromlen, socket;
+       static char *canonical_host_ip = NULL;
+       struct sockaddr_storage from;
+       socklen_t fromlen;
+       int socket;
+       char ntop[NI_MAXHOST];
 
        /* Check whether we have chached the name. */
        if (canonical_host_ip != NULL)
                return canonical_host_ip;
 
        /* If not a socket, return UNKNOWN. */
-       if (!peer_connection_is_on_socket()) {
+       if (!packet_connection_is_on_socket()) {
                canonical_host_ip = xstrdup("UNKNOWN");
                return canonical_host_ip;
        }
@@ -219,48 +199,76 @@ get_remote_ipaddr()
                fatal_cleanup();
        }
        /* Get the IP address in ascii. */
-       canonical_host_ip = xstrdup(inet_ntoa(from.sin_addr));
+       if (getnameinfo((struct sockaddr *)&from, fromlen, ntop, sizeof(ntop),
+            NULL, 0, NI_NUMERICHOST) != 0)
+               fatal("get_remote_hostname: getnameinfo NI_NUMERICHOST failed");
+
+       canonical_host_ip = xstrdup(ntop);
 
        /* Return ip address string. */
        return canonical_host_ip;
 }
 
-/* Returns the port of the peer of the socket. */
+/* Returns the local/remote port for the socket. */
 
-int 
-get_peer_port(int sock)
+int
+get_sock_port(int sock, int local)
 {
-       struct sockaddr_in from;
-       int fromlen;
+       struct sockaddr_storage from;
+       socklen_t fromlen;
+       char strport[NI_MAXSERV];
 
        /* Get IP address of client. */
        fromlen = sizeof(from);
        memset(&from, 0, sizeof(from));
-       if (getpeername(sock, (struct sockaddr *) & from, &fromlen) < 0) {
-               debug("getpeername failed: %.100s", strerror(errno));
-               fatal_cleanup();
+       if (local) {
+               if (getsockname(sock, (struct sockaddr *)&from, &fromlen) < 0) {
+                       error("getsockname failed: %.100s", strerror(errno));
+                       return 0;
+               }
+       } else {
+               if (getpeername(sock, (struct sockaddr *) & from, &fromlen) < 0) {
+                       debug("getpeername failed: %.100s", strerror(errno));
+                       fatal_cleanup();
+               }
        }
        /* Return port number. */
-       return ntohs(from.sin_port);
+       if (getnameinfo((struct sockaddr *)&from, fromlen, NULL, 0,
+            strport, sizeof(strport), NI_NUMERICSERV) != 0)
+               fatal("get_sock_port: getnameinfo NI_NUMERICSERV failed");
+       return atoi(strport);
 }
 
-/* Returns the port number of the remote host.  */
+/* Returns remote/local port number for the current connection. */
 
 int 
-get_remote_port()
+get_port(int local)
 {
-       int socket;
-
        /*
         * If the connection is not a socket, return 65535.  This is
         * intentionally chosen to be an unprivileged port number.
         */
-       if (!peer_connection_is_on_socket())
+       if (!packet_connection_is_on_socket())
                return 65535;
 
-       /* Get client socket. */
-       socket = packet_get_connection_in();
+       /* Get socket and return the port number. */
+       return get_sock_port(packet_get_connection_in(), local);
+}
+
+int 
+get_peer_port(int sock)
+{
+       return get_sock_port(sock, 0);
+}
 
-       /* Get and return the peer port number. */
-       return get_peer_port(socket);
+int 
+get_remote_port()
+{
+       return get_port(0);
+}
+
+int
+get_local_port()
+{
+       return get_port(1);
 }
index e96d670535a2b95924f8a99b01a914d1b6201cbd..30d3b1081e795a8e6b423b443046eacb295e6e29 100644 (file)
@@ -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:
index a6654022b238bd0c544acf595e62198f46a7dfe0..ab175d4097ddb91b9ee816c2cf94b33ef5c43d05 100644 (file)
@@ -128,7 +128,7 @@ dnl Checks for header files.
 AC_CHECK_HEADERS(bstring.h endian.h lastlog.h login.h maillock.h netdb.h netgroup.h paths.h poll.h pty.h shadow.h security/pam_appl.h sys/bsdtty.h sys/cdefs.h sys/poll.h sys/select.h sys/stropts.h sys/time.h sys/ttcompat.h util.h utmp.h utmpx.h)
 
 dnl Checks for library functions.
-AC_CHECK_FUNCS(arc4random getpagesize _getpty innetgr md5_crypt mkdtemp openpty setenv seteuid setlogin setproctitle setreuid snprintf strlcat strlcpy updwtmpx vsnprintf)
+AC_CHECK_FUNCS(arc4random bindresvport_af freeaddrinfo gai_strerror getaddrinfo getpagesize getnameinfo _getpty innetgr md5_crypt mkdtemp openpty  rresvport_af setenv seteuid setlogin setproctitle setreuid snprintf strlcat strlcpy updwtmpx vsnprintf)
 
 AC_CHECK_FUNC(login, 
        [AC_DEFINE(HAVE_LOGIN)],
@@ -169,7 +169,7 @@ AC_TRY_COMPILE(
        [AC_MSG_RESULT(no)]
 ) 
 
-AC_MSG_CHECKING([For uintXX_t types])
+AC_MSG_CHECKING([for uintXX_t types])
 AC_TRY_COMPILE(
        [#include <sys/types.h>], 
        [uint16_t c; uint32_t d; c = 1235; d = 1235;], 
@@ -180,7 +180,7 @@ AC_TRY_COMPILE(
        [AC_MSG_RESULT(no)]
 ) 
 
-AC_MSG_CHECKING([For socklen_t])
+AC_MSG_CHECKING([for socklen_t])
 AC_TRY_COMPILE(
        [
                #include <sys/types.h>
@@ -194,7 +194,7 @@ AC_TRY_COMPILE(
        [AC_MSG_RESULT(no)]
 )
 
-AC_MSG_CHECKING([For size_t])
+AC_MSG_CHECKING([for size_t])
 AC_TRY_COMPILE(
        [#include <sys/types.h>],
        [size_t foo; foo = 1235;],
@@ -205,6 +205,53 @@ AC_TRY_COMPILE(
        [AC_MSG_RESULT(no)]
 )
 
+AC_MSG_CHECKING([for struct sockaddr_storage])
+AC_TRY_COMPILE(
+       [#include <sys/socket.h>],
+       [struct sockaddr_storage s;],
+       [
+               AC_DEFINE(HAVE_STRUCT_SOCKADDR_STORAGE)
+               AC_MSG_RESULT(yes)
+       ],
+       [AC_MSG_RESULT(no)]
+)
+
+AC_MSG_CHECKING([for struct sockaddr_in6])
+AC_TRY_COMPILE(
+       [#include <netinet/in.h>],
+       [struct sockaddr_in6 s; s.sin6_family = 0;],
+       [
+               AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6)
+               AC_MSG_RESULT(yes)
+       ],
+       [AC_MSG_RESULT(no)]
+)
+
+AC_MSG_CHECKING([for struct in6_addr])
+AC_TRY_COMPILE(
+       [#include <netinet/in.h>],
+       [struct in6_addr s; s.s6_addr[0] = 0;],
+       [
+               AC_DEFINE(HAVE_STRUCT_IN6_ADDR)
+               AC_MSG_RESULT(yes)
+       ],
+       [AC_MSG_RESULT(no)]
+)
+
+AC_MSG_CHECKING([for struct addrinfo])
+AC_TRY_COMPILE(
+       [
+               #include <sys/socket.h>
+               #include <netdb.h>
+       ],
+       [struct addrinfo s; s.ai_flags = AI_PASSIVE;],
+       [
+               AC_DEFINE(HAVE_STRUCT_ADDRINFO)
+               AC_MSG_RESULT(yes)
+       ],
+       [AC_MSG_RESULT(no)]
+)
+
 AC_ARG_WITH(pam,
        [  --without-pam           Disable PAM support ],
        [
@@ -330,7 +377,38 @@ AC_EGREP_HEADER(ut_addr, utmp.h,
 )
 AC_MSG_CHECKING([whether utmpx.h has ut_addr field])
 AC_EGREP_HEADER(ut_addr, utmpx.h, 
-       [AC_DEFINE(HAVE_ADDR_IN_UTMP) AC_MSG_RESULT(yes); ], 
+       [AC_DEFINE(HAVE_ADDR_IN_UTMPX) AC_MSG_RESULT(yes); ], 
+       [AC_MSG_RESULT(no)]
+)
+AC_MSG_CHECKING([whether utmp.h has ut_addr_v6 field])
+AC_EGREP_HEADER(ut_addr_v6, utmp.h, 
+       [AC_DEFINE(HAVE_ADDR_V6_IN_UTMP) AC_MSG_RESULT(yes); ], 
+       [AC_MSG_RESULT(no)]
+)
+AC_MSG_CHECKING([whether utmpx.h has ut_addr_v6 field])
+AC_EGREP_HEADER(ut_addr_v6, utmpx.h, 
+       [AC_DEFINE(HAVE_ADDR_V6_IN_UTMPX) AC_MSG_RESULT(yes); ], 
+       [AC_MSG_RESULT(no)]
+)
+
+AC_MSG_CHECKING([whether struct sockaddr_storage has ss_family field])
+AC_TRY_COMPILE(
+       [#include <sys/socket.h>],
+       [struct sockaddr_storage s; s.ss_family = 1;],
+       [
+               AC_DEFINE(HAVE_SS_FAMILY_IN_SS)
+               AC_MSG_RESULT(yes)
+       ],
+       [AC_MSG_RESULT(no)]
+)
+AC_MSG_CHECKING([whether struct sockaddr_storage has __ss_family field])
+AC_TRY_COMPILE(
+       [#include <sys/socket.h>],
+       [struct sockaddr_storage s; s.__ss_family = 1;],
+       [
+               AC_DEFINE(HAVE___SS_FAMILY_IN_SS)
+               AC_MSG_RESULT(yes)
+       ],
        [AC_MSG_RESULT(no)]
 )
 
index 40c1a3eadffd1f224152b725364d2725f51e4353..d2f61e26991bb48c42266f1f24462be3704025af 100644 (file)
--- a/defines.h
+++ b/defines.h
@@ -112,6 +112,10 @@ typedef unsigned int size_t;
 # define HAVE_SIZE_T
 #endif /* HAVE_SIZE_T */
 
+#if !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE___SS_FAMILY_IN_SS)
+# define ss_family __ss_family
+#endif /* !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE_SA_FAMILY_IN_SS) */
+
 /* Paths */
 
 /* If _PATH_LASTLOG is not defined by system headers, set it to the */
diff --git a/fake-gai-errnos.h b/fake-gai-errnos.h
new file mode 100644 (file)
index 0000000..27f6089
--- /dev/null
@@ -0,0 +1,12 @@
+/*
+ * fake library for ssh
+ *
+ * This file is included in getaddrinfo.c and getnameinfo.c.
+ * See getaddrinfo.c and getnameinfo.c.
+ */
+
+/* for old netdb.h */
+#ifndef EAI_NODATA
+#define EAI_NODATA     1
+#define EAI_MEMORY     2
+#endif
diff --git a/fake-getaddrinfo.c b/fake-getaddrinfo.c
new file mode 100644 (file)
index 0000000..b918798
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * fake library for ssh
+ *
+ * This file includes getaddrinfo(), freeaddrinfo() and gai_strerror().
+ * These funtions are defined in rfc2133.
+ *
+ * But these functions are not implemented correctly. The minimum subset
+ * is implemented for ssh use only. For exapmle, this routine assumes
+ * that ai_family is AF_INET. Don't use it for another purpose.
+ * 
+ * In the case not using 'configure --enable-ipv6', this getaddrinfo.c
+ * will be used if you have broken getaddrinfo or no getaddrinfo.
+ */
+
+#include "includes.h"
+#include "ssh.h"
+
+#ifndef HAVE_GAI_STRERROR
+char *
+gai_strerror(ecode)
+int ecode;
+{
+  switch (ecode) {
+  case EAI_NODATA:
+    return "no address associated with hostname.";
+  case EAI_MEMORY:
+    return "memory allocation failure.";
+  default:
+    return "unknown error.";
+  }
+}    
+#endif /* !HAVE_GAI_STRERROR */
+
+#ifndef HAVE_FREEADDRINFO
+void
+freeaddrinfo(ai)
+struct addrinfo *ai;
+{
+  struct addrinfo *next;
+  
+  do {
+    next = ai->ai_next;
+    free(ai);
+  } while (ai = next);
+}
+#endif /* !HAVE_FREEADDRINFO */
+
+#ifndef HAVE_GETADDRINFO
+static struct addrinfo *
+malloc_ai(port, addr)
+int port;
+u_long addr;
+{
+  struct addrinfo *ai;
+
+  if (ai = (struct addrinfo *)malloc(sizeof(struct addrinfo) +
+                                    sizeof(struct sockaddr_in))) {
+    memset(ai, 0, sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
+    ai->ai_addr = (struct sockaddr *)(ai + 1);
+    /* XXX -- ssh doesn't use sa_len */
+    ai->ai_addrlen = sizeof(struct sockaddr_in);
+    ai->ai_addr->sa_family = ai->ai_family = AF_INET;
+    ((struct sockaddr_in *)(ai)->ai_addr)->sin_port = port;
+    ((struct sockaddr_in *)(ai)->ai_addr)->sin_addr.s_addr = addr;
+    return ai;
+  } else {
+    return NULL;
+  }
+}
+
+int
+getaddrinfo(hostname, servname, hints, res)
+const char *hostname, *servname;
+const struct addrinfo *hints;
+struct addrinfo **res;
+{
+  struct addrinfo *cur, *prev = NULL;
+  struct hostent *hp;
+  int i, port;
+  
+  if (servname)
+    port = htons(atoi(servname));
+  else
+    port = 0;
+  if (hints && hints->ai_flags & AI_PASSIVE)
+    if (*res = malloc_ai(port, htonl(0x00000000)))
+      return 0;
+    else
+      return EAI_MEMORY;
+  if (!hostname)
+    if (*res = malloc_ai(port, htonl(0x7f000001)))
+      return 0;
+    else
+      return EAI_MEMORY;
+  if (inet_addr(hostname) != -1)
+    if (*res = malloc_ai(port, inet_addr(hostname)))
+      return 0;
+    else
+      return EAI_MEMORY;
+  if ((hp = gethostbyname(hostname)) &&
+      hp->h_name && hp->h_name[0] && hp->h_addr_list[0]) {
+    for (i = 0; hp->h_addr_list[i]; i++)
+      if (cur = malloc_ai(port,
+                         ((struct in_addr *)hp->h_addr_list[i])->s_addr)) {
+       if (prev)
+         prev->ai_next = cur;
+       else
+         *res = cur;
+       prev = cur;
+      } else {
+       if (*res)
+         freeaddrinfo(*res);
+       return EAI_MEMORY;
+      }
+    return 0;
+  }
+  return EAI_NODATA;
+}
+#endif /* !HAVE_GETADDRINFO */
diff --git a/fake-getaddrinfo.h b/fake-getaddrinfo.h
new file mode 100644 (file)
index 0000000..de1748f
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef _FAKE_GETADDRINFO_H
+#define _FAKE_GETADDRINFO_H
+
+#include "config.h"
+
+#include "fake-gai-errnos.h"
+
+#ifndef AI_PASSIVE
+# define AI_PASSIVE        1
+#endif
+
+#ifndef NI_NUMERICHOST
+# define NI_NUMERICHOST    2
+# define NI_NAMEREQD       4
+# define NI_NUMERICSERV    8
+#endif
+
+#ifndef HAVE_STRUCT_ADDRINFO
+struct addrinfo {
+       int     ai_flags;       /* AI_PASSIVE, AI_CANONNAME */
+       int     ai_family;      /* PF_xxx */
+       int     ai_socktype;    /* SOCK_xxx */
+       int     ai_protocol;    /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+       size_t  ai_addrlen;     /* length of ai_addr */
+       char    *ai_canonname;  /* canonical name for hostname */
+       struct sockaddr *ai_addr;       /* binary address */
+       struct addrinfo *ai_next;       /* next structure in linked list */
+}
+#endif /* !HAVE_STRUCT_ADDRINFO */
+
+#ifndef HAVE_GETADDRINFO
+int getaddrinfo(const char *hostname, const char *servname, 
+                const struct addrinfo *hints, struct addrinfo **res);
+#endif /* !HAVE_GETADDRINFO */
+
+#ifndef HAVE_GAI_STRERROR
+char *gai_strerror(int ecode);
+#endif /* !HAVE_GAI_STRERROR */
+
+#ifndef HAVE_FREEADDRINFO
+void freeaddrinfo(struct addrinfo *ai);
+#endif /* !HAVE_FREEADDRINFO */
+
+#endif /* _FAKE_GETADDRINFO_H */
diff --git a/fake-getnameinfo.c b/fake-getnameinfo.c
new file mode 100644 (file)
index 0000000..bf1184e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * fake library for ssh
+ *
+ * This file includes getnameinfo().
+ * These funtions are defined in rfc2133.
+ *
+ * But these functions are not implemented correctly. The minimum subset
+ * is implemented for ssh use only. For exapmle, this routine assumes
+ * that ai_family is AF_INET. Don't use it for another purpose.
+ * 
+ * In the case not using 'configure --enable-ipv6', this getnameinfo.c
+ * will be used if you have broken getnameinfo or no getnameinfo.
+ */
+
+#include "includes.h"
+#include "ssh.h"
+
+#ifndef HAVE_GETNAMEINFO
+int
+getnameinfo(sa, salen, host, hostlen, serv, servlen, flags)
+const struct sockaddr *sa;
+size_t salen;
+char *host;
+size_t hostlen;
+char *serv;
+size_t servlen;
+int flags;
+{
+  struct sockaddr_in *sin = (struct sockaddr_in *)sa;
+  struct hostent *hp;
+  char tmpserv[16];
+  
+  if (serv) {
+    sprintf(tmpserv, "%d", ntohs(sin->sin_port));
+    if (strlen(tmpserv) > servlen)
+      return EAI_MEMORY;
+    else
+      strcpy(serv, tmpserv);
+  }
+  if (host)
+    if (flags & NI_NUMERICHOST)
+      if (strlen(inet_ntoa(sin->sin_addr)) > hostlen)
+       return EAI_MEMORY;
+      else {
+       strcpy(host, inet_ntoa(sin->sin_addr));
+       return 0;
+      }
+    else
+      if (hp = gethostbyaddr((char *)&sin->sin_addr, sizeof(struct in_addr),
+                            AF_INET))
+       if (strlen(hp->h_name) > hostlen)
+         return EAI_MEMORY;
+       else {
+         strcpy(host, hp->h_name);
+         return 0;
+       }
+      else
+       return EAI_NODATA;
+  return 0;
+}
+#endif /* !HAVE_GETNAMEINFO */
diff --git a/fake-getnameinfo.h b/fake-getnameinfo.h
new file mode 100644 (file)
index 0000000..ecf0df2
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _FAKE_GETNAMEINFO_H
+#define _FAKE_GETNAMEINFO_H
+
+#include "config.h"
+#ifndef HAVE_GETNAMEINFO
+int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, 
+                size_t hostlen, char *serv, size_t servlen, int flags);
+#endif /* !HAVE_GETNAMEINFO */
+
+#ifndef NI_MAXSERV
+# define NI_MAXSERV 32
+#endif /* !NI_MAXSERV */
+#ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+#endif /* !NI_MAXHOST */
+
+#endif /* _FAKE_GETNAMEINFO_H */
diff --git a/fake-socket.h b/fake-socket.h
new file mode 100644 (file)
index 0000000..e11ad44
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef _FAKE_SOCKET_H
+#define _FAKE_SOCKET_H
+
+#include "config.h"
+#include "sys/types.h"
+
+#ifndef HAVE_STRUCT_SOCKADDR_STORAGE
+#define        _SS_MAXSIZE     128     /* Implementation specific max size */
+#define        _SS_ALIGNSIZE   (sizeof(int))
+#define        _SS_PAD1SIZE    (_SS_ALIGNSIZE - sizeof(u_short))
+#define        _SS_PAD2SIZE    (_SS_MAXSIZE - (sizeof(u_short) + \
+                                       _SS_PAD1SIZE + _SS_ALIGNSIZE))
+
+struct sockaddr_storage {
+  u_short      ss_family;
+  char         __ss_pad1[_SS_PAD1SIZE];
+  int                  __ss_align;
+  char         __ss_pad2[_SS_PAD2SIZE];
+};
+#endif /* !HAVE_STRUCT_SOCKADDR_STORAGE */
+
+#ifndef IN6_IS_ADDR_LOOPBACK
+#define IN6_IS_ADDR_LOOPBACK(a) \
+       (((u_int32_t *) (a))[0] == 0 && ((u_int32_t *) (a))[1] == 0 && \
+        ((u_int32_t *) (a))[2] == 0 && ((u_int32_t *) (a))[3] == htonl (1))
+#endif /* !IN6_IS_ADDR_LOOPBACK */
+
+#ifndef HAVE_STRUCT_IN6_ADDR
+struct in6_addr {
+       u_int8_t                s6_addr[16];
+};
+#endif /* !HAVE_STRUCT_IN6_ADDR */
+
+#ifndef HAVE_STRUCT_SOCKADDR_IN6
+struct sockaddr_in6 {
+   unsigned short sin6_family;
+       u_int16_t sin6_port;
+       u_int32_t sin6_flowinfo;
+       struct in6_addr sin6_addr;
+};
+#endif /* !HAVE_STRUCT_SOCKADDR_IN6 */
+
+#ifndef AF_INET6
+/* Define it to something that should never appear */
+#define AF_INET6 AF_MAX
+#endif
+
+#endif /* !_FAKE_SOCKET_H */
+
index 6a2a3a0b3954d860860667c236073fad0ac5d31b..8a6740649e5fa6045375d6f3bd65573232dc1a8e 100755 (executable)
--- a/fixpaths
+++ b/fixpaths
@@ -31,8 +31,6 @@ for $f (@ARGV) {
   $f =~ /(.*\/)*(.*)$/;
   $of = $2; $of =~ s/.in$//;
 
-  print("Making substitutions for $of\n");
-
   open(IN, "<$f")          || die ("$0: input file $f missing!\n");
   if (open(OUT, ">$of")) {
     while (<IN>) {
index 7060a899ef4e18734d9148de9466b282ad13df17..831ac592fe9703877840ae9185e4557605988081 100644 (file)
@@ -14,7 +14,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: hostfile.c,v 1.10 1999/12/02 20:18:59 markus Exp $");
+RCSID("$OpenBSD: hostfile.c,v 1.11 2000/01/04 00:07:59 markus Exp $");
 
 #include "packet.h"
 #include "ssh.h"
index bc7db419a747514f76dfa56344975800fde12d7d..b4af0c22018aa6b2650d0ff1fe652e688798d5de 100644 (file)
@@ -88,6 +88,10 @@ static /**/const char *const rcsid[] = { (char *)rcsid, "\100(#)" msg }
 #endif
 
 #include "version.h"
+
+/* BSD function replacements */
+#include "bsd-bindresvport.h"
+#include "bsd-rresvport.h"
 #include "bsd-misc.h"
 #include "bsd-strlcpy.h"
 #include "bsd-strlcat.h"
@@ -96,6 +100,11 @@ static /**/const char *const rcsid[] = { (char *)rcsid, "\100(#)" msg }
 #include "bsd-daemon.h"
 #include "bsd-login.h"
 
+/* rfc2553 socket API replacements */
+#include "fake-getaddrinfo.h"
+#include "fake-getnameinfo.h"
+#include "fake-socket.h"
+
 /* Define this to be the path of the xauth program. */
 #ifndef XAUTH_PATH
 #define XAUTH_PATH "/usr/X11R6/bin/xauth"
diff --git a/log.c b/log.c
index 837a670de1c63f08057eb115cd36d2d4950bfbbd..03038b2fbe41dd3539bf85830657957e7ced64f5 100644 (file)
--- a/log.c
+++ b/log.c
@@ -3,7 +3,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: log.c,v 1.6 1999/11/24 19:53:47 markus Exp $");
+RCSID("$OpenBSD: log.c,v 1.7 2000/01/04 00:07:59 markus Exp $");
 
 #include "ssh.h"
 #include "xmalloc.h"
diff --git a/login.c b/login.c
index f0cc61d1ac08d43662b70d18aca9f9c761b86cce..633cc32bb4fa2bc8890929964d4c38b85d1a5c7e 100644 (file)
--- a/login.c
+++ b/login.c
@@ -137,7 +137,7 @@ get_last_login_time(uid_t uid, const char *logname,
 
 void 
 record_login(int pid, const char *ttyname, const char *user, uid_t uid,
-            const char *host, struct sockaddr_in * addr)
+            const char *host, struct sockaddr * addr)
 {
 #if defined(_PATH_LASTLOG) && !defined(DISABLE_LASTLOG)
        struct lastlog ll;
@@ -173,7 +173,22 @@ record_login(int pid, const char *ttyname, const char *user, uid_t uid,
        strncpy(u.ut_host, host, sizeof(u.ut_host));
 #endif
 #if defined(HAVE_ADDR_IN_UTMP)
-       u.ut_addr = addr->sin_addr.s_addr;
+       switch (addr->sa_family) {
+               case AF_INET: {
+                       struct sockaddr_in *in = (struct sockaddr_in*)addr;
+                       memcpy(&(u.ut_addr), &(in->sin_addr), sizeof(&(in->sin_addr)));
+                       break;
+               }
+#if defined(HAVE_ADDR_V6_IN_UTMP)
+               case AF_INET6: {
+                       struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr;
+                       memcpy(u.ut_addr_v6, &(in6->sin6_addr), sizeof(&(in6->sin6_addr)));
+                       break;
+               }
+#endif
+               default:
+                       break;
+       }
 #endif
 
 #if defined(HAVE_UTMPX_H) && defined(USE_UTMPX)
@@ -192,9 +207,24 @@ record_login(int pid, const char *ttyname, const char *user, uid_t uid,
        strncpy(utx.ut_host, host, sizeof(utx.ut_host));
 #  endif /* HAVE_SYSLEN_IN_UTMPX */
 # endif
-# if defined(HAVE_ADDR_IN_UTMPX)
-       utx.ut_addr = addr->sin_addr.s_addr;
-# endif
+#if defined(HAVE_ADDR_IN_UTMPX)
+       switch (addr->sa_family) {
+               case AF_INET: {
+                       struct sockaddr_in *in = (struct sockaddr_in*)addr;
+                       memcpy(&(utx.ut_addr), &(in->sin_addr), sizeof(&(in->sin_addr)));
+                       break;
+               }
+#if defined(HAVE_ADDR_V6_IN_UTMPX)
+               case AF_INET6: {
+                       struct sockaddr_in6 *in6 = (struct sockaddr_in6*)addr;
+                       memcpy(utx.ut_addr_v6, &(in6->sin6_addr), sizeof(&(in6->sin6_addr)));
+                       break;
+               }
+#endif
+               default:
+                       break;
+       }
+#endif
 #endif /* defined(HAVE_UTMPX_H) && defined(USE_UTMPX) */
 
 /*#if defined(HAVE_UTMPX_H) && defined(USE_UTMPX) && !defined(HAVE_LOGIN)*/
diff --git a/nchan.c b/nchan.c
index b89b73e551b3b8487ea0b3d88cb40b8b2e64448e..8021dd6cb1cb4f295037d8143fba83d736c74049 100644 (file)
--- a/nchan.c
+++ b/nchan.c
@@ -41,7 +41,7 @@ static void chan_send_ieof(Channel *c);
 static void chan_send_oclose(Channel *c);
 static void chan_shutdown_write(Channel *c);
 static void chan_shutdown_read(Channel *c);
-static void chan_delele_if_full_closed(Channel *c);
+static void chan_delete_if_full_closed(Channel *c);
 
 /*
  * EVENTS update channel input/output states execute ACTIONS
@@ -55,19 +55,25 @@ chan_rcvd_oclose(Channel *c)
        case CHAN_INPUT_WAIT_OCLOSE:
                debug("channel %d: INPUT_WAIT_OCLOSE -> INPUT_CLOSED [rcvd OCLOSE]", c->self);
                c->istate = CHAN_INPUT_CLOSED;
-               chan_delele_if_full_closed(c);
                break;
        case CHAN_INPUT_OPEN:
                debug("channel %d: INPUT_OPEN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self);
                chan_shutdown_read(c);
                chan_send_ieof(c);
                c->istate = CHAN_INPUT_CLOSED;
-               chan_delele_if_full_closed(c);
+               break;
+       case CHAN_INPUT_WAIT_DRAIN:
+               /* both local read_failed and remote write_failed  */
+               log("channel %d: INPUT_WAIT_DRAIN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self);
+               debug("channel %d: INPUT_WAIT_DRAIN -> INPUT_CLOSED [rvcd OCLOSE, send IEOF]", c->self);
+               chan_send_ieof(c);
+               c->istate = CHAN_INPUT_CLOSED;
                break;
        default:
                error("protocol error: chan_rcvd_oclose %d for istate %d", c->self, c->istate);
-               break;
+               return;
        }
+       chan_delete_if_full_closed(c);
 }
 void
 chan_read_failed(Channel *c)
@@ -115,7 +121,7 @@ chan_rcvd_ieof(Channel *c)
        case CHAN_OUTPUT_WAIT_IEOF:
                debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self);
                c->ostate = CHAN_OUTPUT_CLOSED;
-               chan_delele_if_full_closed(c);
+               chan_delete_if_full_closed(c);
                break;
        default:
                error("protocol error: chan_rcvd_ieof %d for ostate %d", c->self, c->ostate);
@@ -135,7 +141,7 @@ chan_write_failed(Channel *c)
                debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self);
                chan_send_oclose(c);
                c->ostate = CHAN_OUTPUT_CLOSED;
-               chan_delele_if_full_closed(c);
+               chan_delete_if_full_closed(c);
                break;
        default:
                error("internal error: chan_write_failed %d for ostate %d", c->self, c->ostate);
@@ -154,7 +160,7 @@ chan_obuf_empty(Channel *c)
                debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self);
                chan_send_oclose(c);
                c->ostate = CHAN_OUTPUT_CLOSED;
-               chan_delele_if_full_closed(c);
+               chan_delete_if_full_closed(c);
                break;
        default:
                error("internal error: chan_obuf_empty %d for ostate %d", c->self, c->ostate);
@@ -213,14 +219,14 @@ chan_shutdown_read(Channel *c)
 {
        debug("channel %d: shutdown_read", c->self);
        if (shutdown(c->sock, SHUT_RD) < 0)
-               error("chan_shutdown_read failed for #%d/fd%d: %.100s",
-                     c->self, c->sock, strerror(errno));
+               error("chan_shutdown_read failed for #%d/fd%d [i%d o%d]: %.100s",
+                     c->self, c->sock, c->istate, c->ostate, strerror(errno));
 }
 static void
-chan_delele_if_full_closed(Channel *c)
+chan_delete_if_full_closed(Channel *c)
 {
        if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) {
-               debug("channel %d: closing", c->self);
+               debug("channel %d: full closed", c->self);
                channel_free(c->self);
        }
 }
index 7b6c1617e4700b8b8f7044b48b29ff2424dc2777..eb49cd3b4fadd52e2021ae8eae476f8703f374c0 100644 (file)
--- a/nchan.ms
+++ b/nchan.ms
@@ -52,6 +52,9 @@ arrow  from S3.n to S4.s
 box invis "rcvd OCLOSE/" "-" with .w at last arrow.c
 ellipse wid .9*ellipsewid ht .9*ellipseht at S4
 arrow "start" "" from S1.w+(-0.5,0) to S1.w
+arrow from S2.ne to S4.sw
+box invis "rcvd OCLOSE/     " with .e at last arrow.c
+box invis " send IEOF" with .w at last arrow.c
 .PE
 .SH
 Channel Output State Diagram
@@ -76,7 +79,7 @@ arrow "start" "" from S1.w+(-0.5,0) to S1.w
 Notes
 .PP
 The input buffer is filled with data from the socket
-(the socket represents the local comsumer/producer of the
+(the socket represents the local consumer/producer of the
 forwarded channel).
 The data is then sent over the INPUT-end (transmit-end) of the channel to the
 remote peer.
@@ -85,7 +88,7 @@ saved in the output buffer and written to the socket.
 .PP
 If the local protocol instance has forwarded all data on the
 INPUT-end of the channel, it sends an IEOF message to the peer.
-If the peer receives the IEOF and has comsumed all
+If the peer receives the IEOF and has consumed all
 data he replies with an OCLOSE.
 When the local instance receives the OCLOSE
 he considers the INPUT-half of the channel closed.
@@ -94,6 +97,6 @@ The peer has his OUTOUT-half closed.
 A channel can be deallocated by a protocol instance
 if both the INPUT- and the OUTOUT-half on his
 side of the channel are closed.
-Note that when an instance is unable to comsume the
+Note that when an instance is unable to consume the
 received data, he is permitted to send an OCLOSE
 before the matching IEOF is received.
index c8acbd087f2fa5be5eb069ef7aefb74be1dbc373..1b9f59475cbbfc9d09c64d808365e44e9477bec6 100644 (file)
--- a/packet.c
+++ b/packet.c
@@ -104,6 +104,48 @@ packet_set_connection(int fd_in, int fd_out)
        fatal_add_cleanup((void (*) (void *)) packet_close, NULL);
 }
 
+/* Returns 1 if remote host is connected via socket, 0 if not. */
+
+int
+packet_connection_is_on_socket()
+{
+       struct sockaddr_storage from, to;
+       socklen_t fromlen, tolen;
+
+       /* filedescriptors in and out are the same, so it's a socket */
+       if (connection_in == connection_out)
+               return 1;
+       fromlen = sizeof(from);
+       memset(&from, 0, sizeof(from));
+       if (getpeername(connection_in, (struct sockaddr *) & from, &fromlen) < 0)
+               return 0;
+       tolen = sizeof(to);
+       memset(&to, 0, sizeof(to));
+       if (getsockname(connection_out, (struct sockaddr *)&to, &tolen) < 0)
+               return 0;
+       if (fromlen != tolen || memcmp(&from, &to, fromlen) != 0)
+               return 0;
+       if (from.ss_family != AF_INET && from.ss_family != AF_INET6)
+               return 0;
+       return 1;
+}
+
+/* returns 1 if connection is via ipv4 */
+
+int
+packet_connection_is_ipv4()
+{
+       struct sockaddr_storage to;
+       socklen_t tolen;
+
+       memset(&to, 0, sizeof(to));
+       if (getsockname(connection_out, (struct sockaddr *)&to, &tolen) < 0)
+               return 0;
+       if (to.ss_family != AF_INET)
+               return 0;
+       return 1;
+}
+
 /* Sets the connection into non-blocking mode. */
 
 void
@@ -735,19 +777,20 @@ packet_set_interactive(int interactive, int keepalives)
        /* Record that we are in interactive mode. */
        interactive_mode = interactive;
 
-       /*
-        * Only set socket options if using a socket (as indicated by the
-        * descriptors being the same).
-        */
-       if (connection_in != connection_out)
+       /* Only set socket options if using a socket.  */
+       if (!packet_connection_is_on_socket())
                return;
-
        if (keepalives) {
                /* Set keepalives if requested. */
                if (setsockopt(connection_in, SOL_SOCKET, SO_KEEPALIVE, (void *) &on,
-                              sizeof(on)) < 0)
+                   sizeof(on)) < 0)
                        error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno));
        }
+       /*
+        * IPTOS_LOWDELAY, TCP_NODELAY and IPTOS_THROUGHPUT are IPv4 only
+        */
+       if (!packet_connection_is_ipv4())
+               return;
        if (interactive) {
                /*
                 * Set IP options for an interactive connection.  Use
@@ -755,10 +798,10 @@ packet_set_interactive(int interactive, int keepalives)
                 */
                int lowdelay = IPTOS_LOWDELAY;
                if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *) &lowdelay,
-                              sizeof(lowdelay)) < 0)
+                   sizeof(lowdelay)) < 0)
                        error("setsockopt IPTOS_LOWDELAY: %.100s", strerror(errno));
                if (setsockopt(connection_in, IPPROTO_TCP, TCP_NODELAY, (void *) &on,
-                              sizeof(on)) < 0)
+                   sizeof(on)) < 0)
                        error("setsockopt TCP_NODELAY: %.100s", strerror(errno));
        } else {
                /*
@@ -767,7 +810,7 @@ packet_set_interactive(int interactive, int keepalives)
                 */
                int throughput = IPTOS_THROUGHPUT;
                if (setsockopt(connection_in, IPPROTO_IP, IP_TOS, (void *) &throughput,
-                              sizeof(throughput)) < 0)
+                   sizeof(throughput)) < 0)
                        error("setsockopt IPTOS_THROUGHPUT: %.100s", strerror(errno));
        }
 }
index 1b81e4357716d01e13ddc409977c45b05bd0746c..7befab9e84b651a72f67d76d6d8e80f9ad3a8a47 100644 (file)
--- a/packet.h
+++ b/packet.h
@@ -198,4 +198,8 @@ do { \
   } \
 } while (0)
 
+/* remote host is connected via a socket/ipv4 */
+int    packet_connection_is_on_socket(void);
+int    packet_connection_is_ipv4(void);
+
 #endif                         /* PACKET_H */
index d820766482415b55dad00f6aec0ac6ea75967a65..91ff893556925c7769a4a1699d6cef50d70fc15d 100644 (file)
--- a/scp.1.in
+++ b/scp.1.in
@@ -19,7 +19,7 @@
 .Nd secure copy (remote file copy program)
 .Sh SYNOPSIS
 .Nm scp
-.Op Fl pqrvC
+.Op Fl pqrvC46
 .Op Fl P Ar port
 .Op Fl c Ar cipher
 .Op Fl i Ar identity_file
@@ -93,6 +93,14 @@ because
 .Fl p
 is already reserved for preserving the times and modes of the file in
 .Xr rcp 1 .
+.It Fl 4
+Forces
+.Nm
+to use IPv4 addresses only.
+.It Fl 6
+Forces
+.Nm
+to use IPv6 addresses only.
 .Sh AUTHORS
 Timo Rinne <tri@iki.fi> and Tatu Ylonen <ylo@cs.hut.fi>
 .Sh HISTORY
diff --git a/scp.c b/scp.c
index 1051d0d38de77c85f6bf2bc2238f2e36d1bbcae5..300d10d0c56070cd6bbc2c0a65c3381940ec32dd 100644 (file)
--- a/scp.c
+++ b/scp.c
@@ -74,6 +74,12 @@ off_t totalbytes = 0;
 /* Name of current file being transferred. */
 char *curfile;
 
+/* This is set to non-zero if IPv4 is desired. */
+int IPv4 = 0;
+
+/* This is set to non-zero if IPv6 is desired. */
+int IPv6 = 0;
+
 /* This is set to non-zero to enable verbose mode. */
 int verbose_mode = 0;
 
@@ -145,6 +151,11 @@ do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
                args[i++] = SSH_PROGRAM;
                args[i++] = "-x";
                args[i++] = "-oFallBackToRsh no";
+               if (IPv4)
+                       args[i++] = "-4";
+               if (IPv6)
+                       args[i++] = "-6";
+               args[i++] = "-oFallBackToRsh no";
                if (verbose_mode)
                        args[i++] = "-v";
                if (compress_flag)
@@ -242,9 +253,15 @@ main(argc, argv)
        extern int optind;
 
        fflag = tflag = 0;
-       while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q")) != EOF)
+       while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46")) != EOF)
                switch (ch) {
                /* User-visible flags. */
+               case '4':
+                       IPv4 = 1;
+                       break;
+               case '6':
+                       IPv6 = 1;
+                       break;
                case 'p':
                        pflag = 1;
                        break;
@@ -334,6 +351,17 @@ main(argc, argv)
        exit(errs != 0);
 }
 
+char *
+cleanhostname(host)
+       char *host;
+{
+       if (*host == '[' && host[strlen(host) - 1] == ']') {
+               host[strlen(host) - 1] = '\0';
+               return (host + 1);
+       } else
+               return host;
+}
+
 void
 toremote(targ, argc, argv)
        char *targ, *argv[];
@@ -372,6 +400,7 @@ toremote(targ, argc, argv)
                        bp = xmalloc(len);
                        if (host) {
                                *host++ = 0;
+                               host = cleanhostname(host);
                                suser = argv[i];
                                if (*suser == '\0')
                                        suser = pwd->pw_name;
@@ -383,13 +412,15 @@ toremote(targ, argc, argv)
                                               suser, host, cmd, src,
                                               tuser ? tuser : "", tuser ? "@" : "",
                                               thost, targ);
-                       } else
+                       } else {
+                               host = cleanhostname(argv[i]);
                                (void) sprintf(bp,
                                               "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'",
                                               SSH_PROGRAM, verbose_mode ? " -v" : "",
-                                              argv[i], cmd, src,
+                                              host, cmd, src,
                                               tuser ? tuser : "", tuser ? "@" : "",
                                               thost, targ);
+                       }
                        if (verbose_mode)
                                fprintf(stderr, "Executing: %s\n", bp);
                        (void) system(bp);
@@ -399,7 +430,7 @@ toremote(targ, argc, argv)
                                len = strlen(targ) + CMDNEEDS + 20;
                                bp = xmalloc(len);
                                (void) sprintf(bp, "%s -t %s", cmd, targ);
-                               host = thost;
+                               host = cleanhostname(thost);
                                if (do_cmd(host, tuser,
                                           bp, &remin, &remout) < 0)
                                        exit(1);
@@ -449,6 +480,7 @@ tolocal(argc, argv)
                        else if (!okname(suser))
                                continue;
                }
+               host = cleanhostname(host);
                len = strlen(src) + CMDNEEDS + 20;
                bp = xmalloc(len);
                (void) sprintf(bp, "%s -f %s", cmd, src);
@@ -913,7 +945,7 @@ void
 usage()
 {
        (void) fprintf(stderr,
-                      "usage: scp [-pqrvC] [-P port] [-c cipher] [-i identity] f1 f2; or:\n       scp [options] f1 ... fn directory\n");
+                      "usage: scp [-pqrvC46] [-P port] [-c cipher] [-i identity] f1 f2; or:\n       scp [options] f1 ... fn directory\n");
        exit(1);
 }
 
@@ -983,11 +1015,19 @@ char *
 colon(cp)
        char *cp;
 {
+       int flag = 0;
+
        if (*cp == ':')         /* Leading colon is part of file name. */
                return (0);
+       if (*cp == '[')
+               flag = 1;
 
        for (; *cp; ++cp) {
-               if (*cp == ':')
+               if (*cp == '@' && *(cp+1) == '[')
+                       flag = 1;
+               if (*cp == ']' && *(cp+1) == ':' && flag)
+                       return (cp+1);
+               if (*cp == ':' && !flag)
                        return (cp);
                if (*cp == '/')
                        return (0);
index 71f9cac5b2eba0a3be50aa45c7b824b0af73629e..98146b181d6b43fbba1cdbf81e213fd385616fac 100644 (file)
@@ -18,14 +18,18 @@ RCSID("$Id$");
 #include "servconf.h"
 #include "xmalloc.h"
 
+/* add listen address */
+void add_listen_addr(ServerOptions *options, char *addr);
+
 /* Initializes the server options to their default values. */
 
 void 
 initialize_server_options(ServerOptions *options)
 {
        memset(options, 0, sizeof(*options));
-       options->port = -1;
-       options->listen_addr.s_addr = htonl(INADDR_ANY);
+       options->num_ports = 0;
+       options->ports_from_cmdline = 0;
+       options->listen_addrs = NULL;
        options->host_key_file = NULL;
        options->server_key_bits = -1;
        options->login_grace_time = -1;
@@ -68,16 +72,10 @@ initialize_server_options(ServerOptions *options)
 void 
 fill_default_server_options(ServerOptions *options)
 {
-       if (options->port == -1) {
-               struct servent *sp;
-
-               sp = getservbyname(SSH_SERVICE_NAME, "tcp");
-               if (sp)
-                       options->port = ntohs(sp->s_port);
-               else
-                       options->port = SSH_DEFAULT_PORT;
-               endservent();
-       }
+       if (options->num_ports == 0)
+               options->ports[options->num_ports++] = SSH_DEFAULT_PORT;
+       if (options->listen_addrs == NULL)
+               add_listen_addr(options, NULL);
        if (options->host_key_file == NULL)
                options->host_key_file = HOST_KEY_FILE;
        if (options->server_key_bits == -1)
@@ -232,6 +230,37 @@ parse_token(const char *cp, const char *filename,
        return sBadOption;
 }
 
+/*
+ * add listen address
+ */
+void 
+add_listen_addr(ServerOptions *options, char *addr)
+{
+       extern int IPv4or6;
+       struct addrinfo hints, *ai, *aitop;
+       char strport[NI_MAXSERV];
+       int gaierr;
+       int i;
+
+       if (options->num_ports == 0)
+               options->ports[options->num_ports++] = SSH_DEFAULT_PORT;
+       for (i = 0; i < options->num_ports; i++) {
+               memset(&hints, 0, sizeof(hints));
+               hints.ai_family = IPv4or6;
+               hints.ai_socktype = SOCK_STREAM;
+               hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0;
+               snprintf(strport, sizeof strport, "%d", options->ports[i]);
+               if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0)
+                       fatal("bad addr or host: %s (%s)\n",
+                           addr ? addr : "<NULL>",
+                           gai_strerror(gaierr));
+               for (ai = aitop; ai->ai_next; ai = ai->ai_next)
+                       ;
+               ai->ai_next = options->listen_addrs;
+               options->listen_addrs = aitop;
+       }
+}
+
 /* Reads the server configuration file. */
 
 void 
@@ -262,7 +291,24 @@ read_server_config(ServerOptions *options, const char *filename)
                        bad_options++;
                        continue;
                case sPort:
-                       intptr = &options->port;
+                       /* ignore ports from configfile if cmdline specifies ports */
+                       if (options->ports_from_cmdline)
+                               continue;
+                       if (options->listen_addrs != NULL)
+                               fatal("%s line %d: ports must be specified before "
+                                   "ListenAdress.\n", filename, linenum);
+                       if (options->num_ports >= MAX_PORTS)
+                               fatal("%s line %d: too many ports.\n",
+                                   filename, linenum);
+                       cp = strtok(NULL, WHITESPACE);
+                       if (!cp)
+                               fatal("%s line %d: missing port number.\n",
+                                   filename, linenum);
+                       options->ports[options->num_ports++] = atoi(cp);
+                       break;
+
+               case sServerKeyBits:
+                       intptr = &options->server_key_bits;
 parse_int:
                        cp = strtok(NULL, WHITESPACE);
                        if (!cp) {
@@ -275,10 +321,6 @@ parse_int:
                                *intptr = value;
                        break;
 
-               case sServerKeyBits:
-                       intptr = &options->server_key_bits;
-                       goto parse_int;
-
                case sLoginGraceTime:
                        intptr = &options->login_grace_time;
                        goto parse_int;
@@ -289,12 +331,10 @@ parse_int:
 
                case sListenAddress:
                        cp = strtok(NULL, WHITESPACE);
-                       if (!cp) {
-                               fprintf(stderr, "%s line %d: missing inet addr.\n",
-                                       filename, linenum);
-                               exit(1);
-                       }
-                       options->listen_addr.s_addr = inet_addr(cp);
+                       if (!cp)
+                               fatal("%s line %d: missing inet addr.\n",
+                                   filename, linenum);
+                       add_listen_addr(options, cp);
                        break;
 
                case sHostKeyFile:
index aea4497393c7d44c924a93d7d42466b497193466..26e6ea3f3134d6474d52fa08fe071992fb423715 100644 (file)
 #ifndef SERVCONF_H
 #define SERVCONF_H
 
+#define MAX_PORTS              256     /* Max # ports. */
+
 #define MAX_ALLOW_USERS                256     /* Max # users on allow list. */
 #define MAX_DENY_USERS         256     /* Max # users on deny list. */
 #define MAX_ALLOW_GROUPS       256     /* Max # groups on allow list. */
 #define MAX_DENY_GROUPS                256     /* Max # groups on deny list. */
 
 typedef struct {
-       int     port;           /* Port number to listen on. */
-       struct in_addr listen_addr;     /* Address on which the server
-                                        * listens. */
+       unsigned int num_ports;
+       unsigned int ports_from_cmdline;
+       u_short ports[MAX_PORTS];       /* Port number to listen on. */
+       char   *listen_addr;            /* Address on which the server listens. */
+       struct addrinfo *listen_addrs;  /* Addresses on which the server listens. */
        char   *host_key_file;  /* File containing host key. */
        int     server_key_bits;/* Size of the server key. */
        int     login_grace_time;       /* Disconnect if no auth in this time
index fe2bd62d631cb7ae4fc28f720056c05a1139e6af..f7f10ea7c5ede8b53c89abd9a3c9545296bcf90c 100644 (file)
--- a/ssh.1.in
+++ b/ssh.1.in
@@ -24,7 +24,7 @@
 .Op Ar command
 .Pp
 .Nm ssh
-.Op Fl afgknqtvxCPX
+.Op Fl afgknqtvxCPX46
 .Op Fl c Ar blowfish | 3des
 .Op Fl e Ar escape_char
 .Op Fl i Ar identity_file
@@ -396,9 +396,13 @@ by allocating a socket to listen to
 on the local side, and whenever a connection is made to this port, the
 connection is forwarded over the secure channel, and a connection is
 made to
-.Ar host:hostport
+.Ar host
+port
+.Ar hostport
 from the remote machine.  Port forwardings can also be specified in the
 configuration file.  Only root can forward privileged ports.
+IPv6 addresses can be specified with an alternative syntax:
+.Ar port/host/hostport
 .It Fl R Ar port:host:hostport
 Specifies that the given port on the remote (server) host is to be
 forwarded to the given host and port on the local side.  This works
@@ -407,10 +411,20 @@ by allocating a socket to listen to
 on the remote side, and whenever a connection is made to this port, the
 connection is forwarded over the secure channel, and a connection is
 made to
-.Ar host:hostport
+.Ar host
+port
+.Ar hostport
 from the local machine.  Port forwardings can also be specified in the
 configuration file.  Privileged ports can be forwarded only when
 logging in as root on the remote machine.
+.It Fl 4
+Forces
+.Nm
+to use IPv4 addresses only.
+.It Fl 6
+Forces
+.Nm
+to use IPv6 addresses only.
 .El
 .Sh CONFIGURATION FILES
 .Nm
diff --git a/ssh.c b/ssh.c
index ff7c29c83a67053bc7941effc5a2e1d119ec2217..154f31a125778c544cf923680bbfab7d56cd656e 100644 (file)
--- a/ssh.c
+++ b/ssh.c
@@ -27,6 +27,10 @@ extern char *__progname;
 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 @@ Options options;
 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 @@ usage()
        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 @@ main(int ac, char **av)
                        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 @@ main(int ac, char **av)
                        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 @@ main(int ac, char **av)
                        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 @@ main(int ac, char **av)
                        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 @@ main(int ac, char **av)
 
        /* 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 @@ main(int ac, char **av)
 
        /* 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)
diff --git a/ssh.h b/ssh.h
index 7d1cd4b0e6b241db2ed2140a45825de6af1e0167..d03fec91b5111cf0f3aabaa24698f5aef69aa9f8 100644 (file)
--- a/ssh.h
+++ b/ssh.h
@@ -21,6 +21,8 @@
 #include <netinet/in.h> /* For struct sockaddr_in */
 #include <pwd.h> /* For struct pw */
 #include <stdarg.h> /* For va_list */
+#include <sys/socket.h> /* For struct sockaddr_storage */
+#include "fake-socket.h" /* For struct sockaddr_storage */
 #ifdef HAVE_SYS_SELECT_H
 # include <sys/select.h>
 #endif
@@ -284,7 +286,7 @@ get_last_login_time(uid_t uid, const char *logname,
  */
 void 
 record_login(int pid, const char *ttyname, const char *user, uid_t uid,
-    const char *host, struct sockaddr_in * addr);
+    const char *host, struct sockaddr *addr);
 
 /*
  * Records that the user has logged out.  This does many thigs normally done
@@ -304,7 +306,7 @@ void    record_logout(int pid, const char *ttyname);
  * packet_set_connection for the connection.
  */
 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);
@@ -320,7 +322,7 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr,
 
 void 
 ssh_login(int host_key_valid, RSA * host_key, const char *host,
-    struct sockaddr_in * hostaddr, uid_t original_real_uid);
+    struct sockaddr * hostaddr, uid_t original_real_uid);
 
 /*------------ Definitions for various authentication methods. -------*/
 
@@ -380,8 +382,10 @@ const char *get_remote_ipaddr(void);
 /* Returns the port number of the peer of the socket. */
 int     get_peer_port(int sock);
 
-/* Returns the port number of the remote host. */
+/* Returns the port number of the remote/local host. */
 int     get_remote_port(void);
+int    get_local_port(void);
+
 
 /*
  * Tries to match the host name (which must be in all lowercase) against the
@@ -741,6 +745,9 @@ char   *skey_fake_keyinfo(char *username);
 int    auth_skey_password(struct passwd * pw, const char *password);
 #endif                         /* SKEY */
 
+/* AF_UNSPEC or AF_INET or AF_INET6 */
+extern int IPv4or6;
+
 #ifdef USE_PAM
 #include "auth-pam.h"
 #endif /* USE_PAM */
index 98b42f077fdcc1f07df1f0af67652a6a0ddd2fe0..56cc93f2838c81b4a1dd5ac87193ea39de5c8544 100644 (file)
@@ -35,6 +35,7 @@ RCSID("$Id$");
 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 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid,
        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 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid,
                        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 @@ ssh_proxy_connect(const char *host, u_short port, uid_t original_real_uid,
  * 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 @@ ssh_create_socket(uid_t original_real_uid, int privileged)
         */
        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 @@ ssh_create_socket(uid_t original_real_uid, int privileged)
                 * 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 @@ ssh_create_socket(uid_t original_real_uid, int privileged)
  * 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 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr,
 
        /* 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 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr,
                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);
-
-                       /* Create a socket. */
-                       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 the problems of
-                        * tcp_wrappers showing the remote uid as root.
+               /* 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 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 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 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr,
                                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 @@ ssh_connect(const char *host, struct sockaddr_in * hostaddr,
         * 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 @@ read_yes_or_no(const char *prompt, int defval)
  */
 
 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 @@ check_host_key(char *host,
        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
@@ -1136,18 +1133,6 @@ check_host_key(char *host,
                host_status = check_host_in_hostfile(options.system_hostfile, host,
                                                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 @@ void
 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;
index d4585af34bf8ab44f03030879bfe48a5b1581472..ecbd848e53072043181495801dbff3e4532f8061 100644 (file)
--- a/sshd.8.in
+++ b/sshd.8.in
@@ -19,7 +19,7 @@
 .Nd secure shell daemon
 .Sh SYNOPSIS
 .Nm sshd
-.Op Fl diqQ
+.Op Fl diqQ46
 .Op Fl b Ar bits
 .Op Fl f Ar config_file
 .Op Fl g Ar login_grace_time
@@ -173,6 +173,14 @@ When this options is specified
 assumes the client has sent the given version string
 and skips the
 Protocol Version Identification Exchange.
+.It Fl 4
+Forces
+.Nm
+to use IPv4 addresses only.
+.It Fl 6
+Forces
+.Nm
+to use IPv6 addresses only.
 .El
 .Sh CONFIGURATION FILE
 .Nm
@@ -320,6 +328,10 @@ Specifies what local address
 .Nm
 should listen on.
 The default is to listen to all local addresses.
+Multiple options of this type are permitted.
+Additionally, the
+.Cm Ports
+options must precede this option.
 .It Cm LoginGraceTime
 The server disconnects after this time if the user has not
 successfully logged in.  If the value is 0, there is no time limit.
@@ -365,6 +377,7 @@ normally not allowed).
 Specifies the port number that
 .Nm
 listens on.  The default is 22.
+Multiple options of this type are permitted.
 .It Cm PrintMotd
 Specifies whether
 .Nm
diff --git a/sshd.c b/sshd.c
index 685fd27f425e29d442ec6d16c45148a1e5341610..0eaffebd4d51e28525420a9a6f44388ff32d7ced 100644 (file)
--- a/sshd.c
+++ b/sshd.c
@@ -45,6 +45,12 @@ ServerOptions options;
 /* Name of the server configuration file. */
 char *config_file_name = SERVER_CONFIG_FILE;
 
+/* 
+ * 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;
+
 /*
  * Debug mode flag.  This can be set on the command line.  If debug
  * mode is enabled, extra debugging output will be sent to the system
@@ -66,10 +72,12 @@ char *av0;
 char **saved_argv;
 
 /*
- * This is set to the socket that the server is listening; this is used in
- * the SIGHUP signal handler.
+ * The sockets that the server is listening; this is used in the SIGHUP
+ * signal handler.
  */
-int listen_sock;
+#define        MAX_LISTEN_SOCKS        16
+int listen_socks[MAX_LISTEN_SOCKS];
+int num_listen_socks = 0;
 
 /*
  * the client's version string, passed by sshd2 in compat mode. if != NULL,
@@ -135,6 +143,18 @@ void do_child(const char *command, struct passwd * pw, const char *term,
              const char *display, const char *auth_proto,
              const char *auth_data, const char *ttyname);
 
+/*
+ * Close all listening sockets
+ */
+void
+close_listen_socks(void)
+{
+       int i;
+       for (i = 0; i < num_listen_socks; i++)
+               close(listen_socks[i]);
+       num_listen_socks = -1;
+}
+
 /*
  * Signal handler for SIGHUP.  Sshd execs itself when it receives SIGHUP;
  * the effect is to reread the configuration file (and to regenerate
@@ -155,7 +175,7 @@ void
 sighup_restart()
 {
        log("Received SIGHUP; restarting.");
-       close(listen_sock);
+       close_listen_socks();
        execv(saved_argv[0], saved_argv);
        log("RESTART FAILED: av0='%s', error: %s.", av0, strerror(errno));
        exit(1);
@@ -170,7 +190,7 @@ void
 sigterm_handler(int sig)
 {
        log("Received signal %d; terminating.", sig);
-       close(listen_sock);
+       close_listen_socks();
        exit(255);
 }
 
@@ -277,11 +297,12 @@ main(int ac, char **av)
 {
        extern char *optarg;
        extern int optind;
-       int opt, aux, sock_in, sock_out, newsock, i, pid, on = 1;
+       int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, pid, on = 1;
+       socklen_t fromlen;
        int remote_major, remote_minor;
        int silentrsa = 0;
-       struct pollfd fds;
-       struct sockaddr_in sin;
+       fd_set *fdset;
+       struct sockaddr_storage from;
        char buf[100];                  /* Must not be larger than remote_version. */
        char remote_version[100];       /* Must be at least as big as buf. */
        const char *remote_ip;
@@ -289,6 +310,9 @@ main(int ac, char **av)
        char *comment;
        FILE *f;
        struct linger linger;
+       struct addrinfo *ai;
+       char ntop[NI_MAXHOST], strport[NI_MAXSERV];
+       int listen_sock, maxfd;
 
        /* Save argv[0]. */
        saved_argv = av;
@@ -301,8 +325,14 @@ main(int ac, char **av)
        initialize_server_options(&options);
 
        /* Parse command-line arguments. */
-       while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ")) != EOF) {
+       while ((opt = getopt(ac, av, "f:p:b:k:h:g:V:diqQ46")) != EOF) {
                switch (opt) {
+               case '4':
+                       IPv4or6 = AF_INET;
+                       break;
+               case '6':
+                       IPv4or6 = AF_INET6;
+                       break;
                case 'f':
                        config_file_name = optarg;
                        break;
@@ -323,7 +353,10 @@ main(int ac, char **av)
                        options.server_key_bits = atoi(optarg);
                        break;
                case 'p':
-                       options.port = atoi(optarg);
+                       options.ports_from_cmdline = 1;
+                       if (options.num_ports >= MAX_PORTS)
+                               fatal("too many ports.\n");
+                       options.ports[options.num_ports++] = atoi(optarg);
                        break;
                case 'g':
                        options.login_grace_time = atoi(optarg);
@@ -356,11 +389,22 @@ main(int ac, char **av)
                        fprintf(stderr, "  -g seconds Grace period for authentication (default: 300)\n");
                        fprintf(stderr, "  -b bits    Size of server RSA key (default: 768 bits)\n");
                        fprintf(stderr, "  -h file    File from which to read host key (default: %s)\n",
-                               HOST_KEY_FILE);
+                           HOST_KEY_FILE);
+                       fprintf(stderr, "  -4         Use IPv4 only\n");
+                       fprintf(stderr, "  -6         Use IPv6 only\n");
                        exit(1);
                }
        }
 
+       /*
+        * Force logging to stderr until we have loaded the private host
+        * key (unless started from inetd)
+        */
+       log_init(av0,
+           options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level,
+           options.log_facility == -1 ? SYSLOG_FACILITY_AUTH : options.log_facility,
+           !inetd_flag);
+
        /* check if RSA support exists */
        if (rsa_alive() == 0) {
                if (silentrsa == 0)
@@ -380,18 +424,11 @@ main(int ac, char **av)
                fprintf(stderr, "Bad server key size.\n");
                exit(1);
        }
-       if (options.port < 1 || options.port > 65535) {
-               fprintf(stderr, "Bad port number.\n");
-               exit(1);
-       }
        /* Check that there are no remaining arguments. */
        if (optind < ac) {
                fprintf(stderr, "Extra argument %s.\n", av[optind]);
                exit(1);
        }
-       /* Force logging to stderr while loading the private host key
-          unless started from inetd */
-       log_init(av0, options.log_level, options.log_facility, !inetd_flag);
 
        debug("sshd version %.100s", SSH_VERSION);
 
@@ -480,32 +517,66 @@ main(int ac, char **av)
                arc4random_stir();
                log("RSA key generation complete.");
        } else {
-               /* Create socket for listening. */
-               listen_sock = socket(AF_INET, SOCK_STREAM, 0);
-               if (listen_sock < 0)
-                       fatal("socket: %.100s", strerror(errno));
-
-               /* Set socket options.  We try to make the port reusable
-                  and have it close as fast as possible without waiting
-                  in unnecessary wait states on close. */
-               setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, (void *) &on,
-                          sizeof(on));
-               linger.l_onoff = 1;
-               linger.l_linger = 5;
-               setsockopt(listen_sock, SOL_SOCKET, SO_LINGER, (void *) &linger,
-                          sizeof(linger));
-
-               memset(&sin, 0, sizeof(sin));
-               sin.sin_family = AF_INET;
-               sin.sin_addr = options.listen_addr;
-               sin.sin_port = htons(options.port);
-
-               if (bind(listen_sock, (struct sockaddr *) & sin, sizeof(sin)) < 0) {
-                       error("bind: %.100s", strerror(errno));
-                       shutdown(listen_sock, SHUT_RDWR);
-                       close(listen_sock);
-                       fatal("Bind to port %d failed.", options.port);
+               for (ai = options.listen_addrs; ai; ai = ai->ai_next) {
+                       if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
+                               continue;
+                       if (num_listen_socks >= MAX_LISTEN_SOCKS)
+                               fatal("Too many listen sockets. "
+                                   "Enlarge MAX_LISTEN_SOCKS");
+                       if (getnameinfo(ai->ai_addr, ai->ai_addrlen,
+                           ntop, sizeof(ntop), strport, sizeof(strport),
+                           NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
+                               error("getnameinfo failed");
+                               continue;
+                       }
+                       /* Create socket for listening. */
+                       listen_sock = socket(ai->ai_family, SOCK_STREAM, 0);
+                       if (listen_sock < 0) {
+                               /* kernel may not support ipv6 */
+                               verbose("socket: %.100s", strerror(errno));
+                               continue;
+                       }
+                       if (fcntl(listen_sock, F_SETFL, O_NONBLOCK) < 0) {
+                               error("listen_sock O_NONBLOCK: %s", strerror(errno));
+                               close(listen_sock);
+                               continue;
+                       }
+                       /*
+                        * Set socket options.  We try to make the port
+                        * reusable and have it close as fast as possible
+                        * without waiting in unnecessary wait states on
+                        * close.
+                        */
+                       setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR,
+                           (void *) &on, sizeof(on));
+                       linger.l_onoff = 1;
+                       linger.l_linger = 5;
+                       setsockopt(listen_sock, SOL_SOCKET, SO_LINGER,
+                           (void *) &linger, sizeof(linger));
+
+                       debug("Bind to port %s on %s.", strport, ntop);
+
+                       /* Bind the socket to the desired port. */
+                       if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) {
+                               error("Bind to port %s on %s failed: %.200s.",
+                                   strport, ntop, strerror(errno));
+                               close(listen_sock);
+                               continue;
+                       }
+                       listen_socks[num_listen_socks] = listen_sock;
+                       num_listen_socks++;
+
+                       /* Start listening on the port. */
+                       log("Server listening on %s port %s.", ntop, strport);
+                       if (listen(listen_sock, 5) < 0)
+                               fatal("listen: %.100s", strerror(errno));
+
                }
+               freeaddrinfo(options.listen_addrs);
+
+               if (!num_listen_socks)
+                       fatal("Cannot bind any address.");
+
                if (!debug_flag) {
                        /*
                         * Record our pid in /etc/sshd_pid to make it easier
@@ -521,10 +592,6 @@ main(int ac, char **av)
                        }
                }
 
-               log("Server listening on port %d.", options.port);
-               if (listen(listen_sock, 5) < 0)
-                       fatal("listen: %.100s", strerror(errno));
-
                public_key = RSA_new();
                sensitive_data.private_key = RSA_new();
 
@@ -546,6 +613,14 @@ main(int ac, char **av)
                /* Arrange SIGCHLD to be caught. */
                signal(SIGCHLD, main_sigchld_handler);
 
+               /* setup fd set for listen */
+               maxfd = 0;
+               for (i = 0; i < num_listen_socks; i++)
+                       if (listen_socks[i] > maxfd)
+                               maxfd = listen_socks[i];
+               fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask);         
+               fdset = (fd_set *)xmalloc(fdsetsz);                                  
+
                /*
                 * Stay listening for connections until the system crashes or
                 * the daemon is killed with a signal.
@@ -553,26 +628,28 @@ main(int ac, char **av)
                for (;;) {
                        if (received_sighup)
                                sighup_restart();
-                       /* Wait in poll until there is a connection. */
-                       memset(&fds, 0, sizeof(fds));
-                       fds.fd = listen_sock;
-                       fds.events = POLLIN;
-                       if (poll(&fds, 1, -1) == -1) {
-                               if (errno == EINTR)
-                                       continue;
-                               fatal("poll: %.100s", strerror(errno));
-                               /*NOTREACHED*/
-                       }
-                       if (fds.revents == 0)
+                       /* Wait in select until there is a connection. */
+                       memset(fdset, 0, fdsetsz);
+                       for (i = 0; i < num_listen_socks; i++)
+                               FD_SET(listen_socks[i], fdset);
+                       if (select(maxfd + 1, fdset, NULL, NULL, NULL) < 0) {
+                               if (errno != EINTR)
+                                       error("select: %.100s", strerror(errno));
                                continue;
-                       aux = sizeof(sin);
-                       newsock = accept(listen_sock, (struct sockaddr *) & sin, &aux);
-                       if (received_sighup)
-                               sighup_restart();
-                       if (newsock < 0) {
-                               if (errno == EINTR)
+                       }
+                       for (i = 0; i < num_listen_socks; i++) {
+                               if (!FD_ISSET(listen_socks[i], fdset))
                                        continue;
-                               error("accept: %.100s", strerror(errno));
+                       fromlen = sizeof(from);
+                       newsock = accept(listen_socks[i], (struct sockaddr *)&from,
+                           &fromlen);
+                       if (newsock < 0) {
+                               if (errno != EINTR && errno != EWOULDBLOCK)
+                                       error("accept: %.100s", strerror(errno));
+                               continue;
+                       }
+                       if (fcntl(newsock, F_SETFL, 0) < 0) {
+                               error("newsock del O_NONBLOCK: %s", strerror(errno));
                                continue;
                        }
                        /*
@@ -586,7 +663,7 @@ main(int ac, char **av)
                                 * connection without forking.
                                 */
                                debug("Server will not fork when running in debugging mode.");
-                               close(listen_sock);
+                               close_listen_socks();
                                sock_in = newsock;
                                sock_out = newsock;
                                pid = getpid();
@@ -603,7 +680,7 @@ main(int ac, char **av)
                                         * accepted socket.  Reinitialize logging (since our pid has
                                         * changed).  We break out of the loop to handle the connection.
                                         */
-                                       close(listen_sock);
+                                       close_listen_socks();
                                        sock_in = newsock;
                                        sock_out = newsock;
                                        log_init(av0, options.log_level, options.log_facility, log_stderr);
@@ -624,6 +701,10 @@ main(int ac, char **av)
 
                        /* Close the new socket (the child is now taking care of it). */
                        close(newsock);
+                       } /* for (i = 0; i < num_listen_socks; i++) */
+                       /* child process check (or debug mode) */
+                       if (num_listen_socks < 0)
+                               break;
                }
        }
 
@@ -662,6 +743,7 @@ main(int ac, char **av)
 
        /* Check whether logins are denied from this host. */
 #ifdef LIBWRAP
+       /* XXX LIBWRAP noes not know about IPv6 */
        {
                struct request_info req;
 
@@ -673,12 +755,11 @@ main(int ac, char **av)
                        close(sock_out);
                        refuse(&req);
                }
-               verbose("Connection from %.500s port %d", eval_client(&req), remote_port);
+/*XXX IPv6 verbose("Connection from %.500s port %d", eval_client(&req), remote_port); */
        }
-#else
+#endif /* LIBWRAP */
        /* Log the connection. */
        verbose("Connection from %.500s port %d", remote_ip, remote_port);
-#endif /* LIBWRAP */
 
        /*
         * We don\'t want to listen forever unless the other side
@@ -700,12 +781,12 @@ main(int ac, char **av)
                snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
                         PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
                if (atomicio(write, sock_out, buf, strlen(buf)) != strlen(buf))
-                       fatal("Could not write ident string to %s.", get_remote_ipaddr());
+                       fatal("Could not write ident string to %s.", remote_ip);
 
                /* Read other side\'s version identification. */
                for (i = 0; i < sizeof(buf) - 1; i++) {
                        if (read(sock_in, &buf[i], 1) != 1)
-                               fatal("Did not receive ident string from %s.", get_remote_ipaddr());
+                               fatal("Did not receive ident string from %s.", remote_ip);
                        if (buf[i] == '\r') {
                                buf[i] = '\n';
                                buf[i + 1] = 0;
@@ -732,7 +813,7 @@ main(int ac, char **av)
                close(sock_in);
                close(sock_out);
                fatal("Bad protocol version identification '%.100s' from %s",
-                     buf, get_remote_ipaddr());
+                     buf, remote_ip);
        }
        debug("Client protocol version %d.%d; client software version %.100s",
              remote_major, remote_minor, remote_version);
@@ -743,8 +824,7 @@ main(int ac, char **av)
                close(sock_in);
                close(sock_out);
                fatal("Protocol major versions differ for %s: %d vs. %d",
-                     get_remote_ipaddr(),
-                     PROTOCOL_MAJOR, remote_major);
+                     remote_ip, PROTOCOL_MAJOR, remote_major);
        }
        /* Check that the client has sufficiently high software version. */
        if (remote_major == 1 && remote_minor < 3)
@@ -769,6 +849,14 @@ main(int ac, char **av)
                options.rhosts_authentication = 0;
                options.rhosts_rsa_authentication = 0;
        }
+#ifdef KRB4
+       if (!packet_connection_is_ipv4() &&
+           options.kerberos_authentication) {
+               debug("Kerberos Authentication disabled, only available for IPv4.");
+               options.kerberos_authentication = 0;
+       }
+#endif /* KRB4 */
+
        packet_set_nonblocking();
 
        /* Handle the connection. */
@@ -1935,8 +2023,8 @@ do_exec_pty(const char *command, int ptyfd, int ttyfd,
        char line[256];
        struct stat st;
        int quiet_login;
-       struct sockaddr_in from;
-       int fromlen;
+       struct sockaddr_storage from;
+       socklen_t fromlen;
        struct pty_cleanup_context cleanup_context;
 
        /* Get remote host name. */
@@ -1997,7 +2085,7 @@ do_exec_pty(const char *command, int ptyfd, int ttyfd,
                }
                /* Record that there was a login on that terminal. */
                record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname,
-                            &from);
+                            (struct sockaddr *)&from);
 
                /* Check if .hushlogin exists. */
                snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir);
@@ -2318,7 +2406,7 @@ do_child(const char *command, struct passwd * pw, const char *term,
        }
 
        snprintf(buf, sizeof buf, "%.50s %d %d",
-                get_remote_ipaddr(), get_remote_port(), options.port);
+                get_remote_ipaddr(), get_remote_port(), get_local_port());
        child_set_env(&env, &envsize, "SSH_CLIENT", buf);
 
        if (ttyname)
@@ -2385,7 +2473,6 @@ do_child(const char *command, struct passwd * pw, const char *term,
         * descriptors left by system functions.  They will be closed later.
         */
        endpwent();
-       endhostent();
 
        /*
         * Close any extra open file descriptors so that we don\'t have them
index 63e3d982943c8255c4547c0d49e65493ff102387..cb2c56e05a3ef2fc57560d2cf192115cac7701b5 100644 (file)
@@ -2,6 +2,7 @@
 
 Port 22
 ListenAddress 0.0.0.0
+#ListenAddress ::
 HostKey @sysconfdir@/ssh_host_key
 ServerKeyBits 768
 LoginGraceTime 600
This page took 0.260456 seconds and 5 git commands to generate.