Support IPv6 addresses with "hosts allow" and "hosts deny". Patch from
authorDavid Dykstra <dwd@samba.org>
Thu, 9 Jan 2003 21:14:10 +0000 (21:14 +0000)
committerDavid Dykstra <dwd@samba.org>
Thu, 9 Jan 2003 21:14:10 +0000 (21:14 +0000)
Hideaki Yoshifuji.

NEWS
access.c
acconfig.h
clientname.c
configure.in
rsyncd.conf.yo

diff --git a/NEWS b/NEWS
index 98788e0..f02b35d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,32 +2,35 @@ rsync changes since last release
 
   ENHANCEMENTS:
 
-    * The --delete-after option now implies --delete. (Wayne Davison)
+    * The --delete-after option now implies --delete.  (Wayne Davison)
 
-    * The --suffix option can now be used with --backup-dir. (Michael
+    * The --suffix option can now be used with --backup-dir.  (Michael
       Zimmerman)
 
     * Combining "::" syntax with the -rsh/-e option now uses the
       specified remote-shell as a transport to talk to a (newly-spawned)
       server-daemon.  This allows someone to use daemon features, such
-      as modules, over a secure protocol, such as ssh. (JD Paul)
+      as modules, over a secure protocol, such as ssh.  (JD Paul)
 
     * The rsync:// syntax for daemon connections is now accepted in the
       destination field.
 
     * If the file name given to --include-from or --exclude-from is "-",
-      rsync will read from standard input. (J.W. Schultz)
+      rsync will read from standard input.  (J.W. Schultz)
 
     * New option --link-dest which is like --compare-dest except that
       unchanged files are hard-linked in to the destination directory.
       (J.W. Schultz)
 
     * Don't report an error if an excluded file disappears during an
-      rsync run. (Eugene Chupriyanov and Bo Kersey)
+      rsync run.  (Eugene Chupriyanov and Bo Kersey)
 
     * Added .svn to --cvs-exclude list to support subversion.  (Jon
       Middleton)
 
+    * Properly support IPv6 addresses in the rsyncd.conf "hosts allow"
+      and "hosts deny" fields.  (Hideaki Yoshifuji)
+
   BUG FIXES:
   
     * Fix "forward name lookup failed" errors on AIX 4.3.3.  (John
index ff35748..aec633a 100644 (file)
--- a/access.c
+++ b/access.c
@@ -30,54 +30,165 @@ static int match_hostname(char *host, char *tok)
        return (fnmatch(tok, host, 0) == 0);
 }
 
+static int match_binary(char *b1, char *b2, char *mask, int addrlen)
+{
+       int i;
+
+       for (i=0; i<addrlen; i++) {
+               if ((b1[i]^b2[i])&mask[i]) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+static void make_mask(char *mask, int plen, int addrlen) {
+       int w, b;
+
+       w = plen >> 3;
+       b = plen & 0x7;
+
+       if (w)
+               memset(mask, 0xff, w);
+       mask[w] = 0xff & (0xff<<(8-b));
+       if (w+1 < addrlen)
+               memset(mask+w+1, 0, addrlen-w-1);
+
+       return;
+}
 
 static int match_address(char *addr, char *tok)
 {
        char *p;
-       unsigned long a, t, mask = (unsigned long)~0;
+       struct addrinfo hints, *resa, *rest;
+       int gai;
+       int ret = 0;
+       int addrlen = 0;
+#ifdef HAVE_STRTOL
+       long int bits;
+#else
+       int bits;
+#endif
+       char mask[16];
+       char *a = NULL, *t = NULL;
 
        if (!addr || !*addr) return 0;
 
-       if (!isdigit(* (unsigned char *) tok)) return 0;
-
        p = strchr(tok,'/');
        if (p) *p = 0;
 
-       a = inet_addr(addr);
-       t = inet_addr(tok);
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_flags = AI_NUMERICHOST;
 
-       if (p) {
-               *p = '/';
-       }
+       gai = getaddrinfo(addr, NULL, &hints, &resa);
+       if (gai) return 0;
 
-       if (t == INADDR_NONE) {
+       gai = getaddrinfo(tok, NULL, &hints, &rest);
+       if (p)
+               *p++ = '/';
+       if (gai) {
                rprintf(FERROR,"malformed address %s\n", tok);
+               freeaddrinfo(resa);
                return 0;
        }
 
-       a = ntohl(a);
-       t = ntohl(t);
+       if (rest->ai_family != resa->ai_family) {
+               ret = 0;
+               goto out;
+       }
+
+       switch(resa->ai_family) {
+       case PF_INET:
+               a = (char *)&((struct sockaddr_in *)resa->ai_addr)->sin_addr;
+               t = (char *)&((struct sockaddr_in *)rest->ai_addr)->sin_addr;
+               addrlen = 4;
+
+               break;
+
+#ifdef INET6
+       case PF_INET6:
+           {
+               struct sockaddr_in6 *sin6a, *sin6t;
+
+               sin6a = (struct sockaddr_in6 *)resa->ai_addr;
+               sin6t = (struct sockaddr_in6 *)rest->ai_addr;
+
+               a = (char *)&sin6a->sin6_addr;
+               t = (char *)&sin6t->sin6_addr;
 
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+               if (sin6t->sin6_scope_id &&
+                   sin6a->sin6_scope_id != sin6t->sin6_scope_id) {
+                       ret = 0;
+                       goto out;
+               }
+#endif
+
+               a = (char *)&sin6a->sin6_addr;
+               t = (char *)&sin6t->sin6_addr;
+               addrlen = 16;
+
+               break;
+           }
+#endif
+       default:
+           rprintf(FERROR,"unknown family %u\n", rest->ai_family);
+           ret = 0;
+           goto out;
+       }
+
+       bits = -1;
        if (p) {
-               if (strchr(p+1,'.')) {
-                       mask = inet_addr(p+1);
-                       if (mask == INADDR_NONE) {
+               if (inet_pton(resa->ai_addr->sa_family, p, mask) <= 0) {
+#ifdef HAVE_STRTOL
+                       char *ep = NULL;
+#else
+                       unsigned char *pp;
+#endif
+
+#ifdef HAVE_STRTOL
+                       bits = strtol(p, &ep, 10);
+                       if (!*p || *ep) {
                                rprintf(FERROR,"malformed mask in %s\n", tok);
-                               return 0;
+                               ret = 0;
+                               goto out;
+                       }
+#else
+                       for (pp = (unsigned char *)p; *pp; pp++) {
+                               if (!isascii(*pp) || !isdigit(*pp)) {
+                                       rprintf(FERROR,"malformed mask in %s\n", tok);
+                                       ret = 0;
+                                       goto out;
+                               }
                        }
-                       mask = ntohl(mask);
-               } else {
-                       int bits = atoi(p+1);
-                       if (bits == 0) return 1;
-                       if (bits <= 0 || bits > 32) {
+                       bits = atoi(p);
+#endif
+                       if (bits == 0) {
+                               ret = 1;
+                               goto out;
+                       }
+                       if (bits < 0 || bits > (addrlen << 3)) {
                                rprintf(FERROR,"malformed mask in %s\n", tok);
-                               return 0;
+                               ret = 0;
+                               goto out;
                        }
-                       mask &= (mask << (32-bits));
                }
+       } else {
+               bits = 128;
        }
 
-       return ((a&mask) == (t&mask));
+       if (bits >= 0)
+               make_mask(mask, bits, addrlen);
+
+       ret = match_binary(a, t, mask, addrlen);
+
+out:
+       freeaddrinfo(resa);
+       freeaddrinfo(rest);
+       return ret;
 }
 
 static int access_match(char *list, char *addr, char *host)
index 4b9d12a..defbc1b 100644 (file)
@@ -7,4 +7,5 @@
 #undef HAVE_GETTIMEOFDAY_TZ
 #undef ENABLE_IPV6
 #undef HAVE_SOCKADDR_LEN
+#undef HAVE_SOCKADDR_IN6_SCOPE_ID
 #undef HAVE_SOCKETPAIR
index 95dadd5..6c86c12 100644 (file)
@@ -169,6 +169,8 @@ void client_sockaddr(int fd,
                     struct sockaddr_storage *ss,
                     socklen_t *ss_len)
 {
+       memset(ss, 0, sizeof(*ss));
+
        if (getpeername(fd, (struct sockaddr *) ss, ss_len)) {
                /* FIXME: Can we really not continue? */
                rprintf(FERROR, RSYNC_NAME ": getpeername on fd%d failed: %s\n",
@@ -272,9 +274,23 @@ int compare_addrinfo_sockaddr(const struct addrinfo *ai,
 
                sin1 = (const struct sockaddr_in6 *) ss;
                sin2 = (const struct sockaddr_in6 *) ai->ai_addr;
-               
-               return memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
-                             sizeof sin1->sin6_addr);
+
+               if (ai->ai_addrlen < sizeof(struct sockaddr_in6)) {
+                       rprintf(FERROR,
+                               "%s: too short sockaddr_in6; length=%d\n",
+                               fn, ai->ai_addrlen);
+                       return 1;
+               }
+
+               if (memcmp(&sin1->sin6_addr, &sin2->sin6_addr,
+                          sizeof sin1->sin6_addr))
+                       return 1;
+
+#ifdef HAVE_SOCKADDR_IN6_SCOPE_ID
+               if (sin1->sin6_scope_id != sin2->sin6_scope_id)
+                       return 1;
+#endif
+               return 0;
        }
 #endif /* INET6 */
        else {
index c7c6581..d2c61a8 100644 (file)
@@ -359,6 +359,15 @@ AC_TRY_COMPILE([#include <sys/types.h>
                [Define if you have strct sockaddr_storage.] ),
        AC_MSG_RESULT(no))
 
+AC_CHECK_MEMBER([struct sockaddr_in6.sin6_scope_id],
+               [ AC_DEFINE(HAVE_SOCKADDR_IN6_SCOPE_ID) ],
+               [],
+               [
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+])
+
 # if we can't find strcasecmp, look in -lresolv (for Unixware at least)
 #
 AC_CHECK_FUNCS(strcasecmp)
@@ -375,7 +384,7 @@ AC_FUNC_UTIME_NULL
 AC_CHECK_FUNCS(waitpid wait4 getcwd strdup strerror chown chmod mknod mkfifo)
 AC_CHECK_FUNCS(fchmod fstat strchr readlink link utime utimes strftime)
 AC_CHECK_FUNCS(memmove lchown vsnprintf snprintf asprintf setsid glob strpbrk)
-AC_CHECK_FUNCS(strlcat strlcpy mtrace mallinfo setgroups)
+AC_CHECK_FUNCS(strlcat strlcpy strtol mtrace mallinfo setgroups)
 
 AC_CACHE_CHECK([for working socketpair],rsync_cv_HAVE_SOCKETPAIR,[
 AC_TRY_RUN([
index 697534a..a11c6a2 100644 (file)
@@ -256,16 +256,19 @@ connection is rejected.
 Each pattern can be in one of five forms:
 
 itemize(
-  it() a dotted decimal IP address. In this case the incoming machines
-  IP address must match exactly.
-
-  it() a address/mask in the form a.b.c.d/n were n is the number of
-  one bits in in the netmask. All IP addresses which match the masked
-  IP address will be allowed in.
-
-  it() a address/mask in the form a.b.c.d/e.f.g.h where e.f.g.h is a
-  netmask in dotted decimal notation. All IP addresses which match the masked
-  IP address will be allowed in.
+  it() a dotted decimal IP address of the form a.b.c.d for IPv4 and 
+  a.b.c.d.e.f for IPv6. In this case the incoming machine's IP address
+  must match exactly.
+
+  it() a address/mask in the form ipaddr/n where ipaddr is the IP
+  address in dotted decimal notation and n is the number of one bits in
+  the netmask.  All IP addresses which match the masked IP address will
+  be allowed in.
+
+  it() a address/mask in the form ipaddr/maskaddr where ipaddr is the
+  IP address in dotted decimal notation and maskaddr is the netmask in
+  dotted decimal notation.  All IP addresses which match the masked IP
+  address will be allowed in.
 
   it() a hostname. The hostname as determined by a reverse lookup will
   be matched (case insensitive) against the pattern. Only an exact