Author: ume
Date: Sun Mar 29 17:55:11 2009
New Revision: 190525
URL: http://svn.freebsd.org/changeset/base/190525

Log:
  Query DNS only once per an address family.
  
  Obtained from:        KAME
  MFC after:    2 weeks

Modified:
  head/lib/libc/net/getaddrinfo.c

Modified: head/lib/libc/net/getaddrinfo.c
==============================================================================
--- head/lib/libc/net/getaddrinfo.c     Sun Mar 29 17:45:48 2009        
(r190524)
+++ head/lib/libc/net/getaddrinfo.c     Sun Mar 29 17:55:11 2009        
(r190525)
@@ -102,7 +102,6 @@ __FBSDID("$FreeBSD$");
 # define FAITH
 #endif
 
-#define SUCCESS 0
 #define ANY 0
 #define YES 1
 #define NO  0
@@ -179,11 +178,6 @@ static const struct explore explore[] = 
        { PF_INET, SOCK_STREAM, IPPROTO_SCTP, "sctp", 0x03 },
        { PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, "sctp", 0x07 },
        { PF_INET, SOCK_RAW, ANY, NULL, 0x05 },
-       { PF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP, "udp", 0x07 },
-       { PF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, "tcp", 0x07 },
-       { PF_UNSPEC, SOCK_STREAM, IPPROTO_SCTP, "sctp", 0x03 },
-       { PF_UNSPEC, SOCK_SEQPACKET, IPPROTO_SCTP, "sctp", 0x07 },
-       { PF_UNSPEC, SOCK_RAW, ANY, NULL, 0x05 },
        { -1, 0, 0, NULL, 0 },
 };
 
@@ -233,6 +227,8 @@ typedef union {
 } querybuf;
 
 static int str2number(const char *, int *);
+static int explore_copy(const struct addrinfo *, const struct addrinfo *,
+       struct addrinfo **);
 static int explore_null(const struct addrinfo *,
        const char *, struct addrinfo **);
 static int explore_numeric(const struct addrinfo *, const char *,
@@ -243,6 +239,7 @@ static int get_canonname(const struct ad
        struct addrinfo *, const char *);
 static struct addrinfo *get_ai(const struct addrinfo *,
        const struct afd *, const char *);
+static struct addrinfo *copy_ai(const struct addrinfo *);
 static int get_portmatch(const struct addrinfo *, const char *);
 static int get_port(struct addrinfo *, const char *, int);
 static const struct afd *find_afd(int);
@@ -371,12 +368,23 @@ getaddrinfo(const char *hostname, const 
        struct addrinfo sentinel;
        struct addrinfo *cur;
        int error = 0;
-       struct addrinfo ai;
-       struct addrinfo ai0;
+       struct addrinfo ai, ai0, *afai;
        struct addrinfo *pai;
+       const struct afd *afd;
        const struct explore *ex;
+       struct addrinfo *afailist[sizeof(afdl)/sizeof(afdl[0])];
+       struct addrinfo *afai_unspec;
+       int found;
        int numeric = 0;
 
+       /* ensure we return NULL on errors */
+       *res = NULL;
+
+       memset(&ai, 0, sizeof(ai));
+
+       memset(afailist, 0, sizeof(afailist));
+       afai_unspec = NULL;
+
        memset(&sentinel, 0, sizeof(sentinel));
        cur = &sentinel;
        pai = &ai;
@@ -416,15 +424,18 @@ getaddrinfo(const char *hostname, const 
                 */
                if (pai->ai_socktype != ANY && pai->ai_protocol != ANY) {
                        for (ex = explore; ex->e_af >= 0; ex++) {
-                               if (pai->ai_family != ex->e_af)
+                               if (!MATCH_FAMILY(pai->ai_family, ex->e_af,
+                                   WILD_AF(ex)))
                                        continue;
-                               if (ex->e_socktype == ANY)
+                               if (!MATCH(pai->ai_socktype, ex->e_socktype,
+                                   WILD_SOCKTYPE(ex)))
                                        continue;
-                               if (ex->e_protocol == ANY)
+                               if (!MATCH(pai->ai_protocol, ex->e_protocol,
+                                   WILD_PROTOCOL(ex)))
                                        continue;
-                               if (pai->ai_socktype == ex->e_socktype &&
-                                   pai->ai_protocol == ex->e_protocol)
-                                       break;
+
+                               /* matched */
+                               break;
                        }
 
                        if (ex->e_af < 0)
@@ -460,49 +471,48 @@ getaddrinfo(const char *hostname, const 
 
        ai0 = *pai;
 
-       /* NULL hostname, or numeric hostname */
-       for (ex = explore; ex->e_af >= 0; ex++) {
+       /*
+        * NULL hostname, or numeric hostname.
+        * If numeric representation of AF1 can be interpreted as FQDN
+        * representation of AF2, we need to think again about the code below.
+        */
+       found = 0;
+       for (afd = afdl; afd->a_af; afd++) {
                *pai = ai0;
 
-               /* PF_UNSPEC entries are prepared for DNS queries only */
-               if (ex->e_af == PF_UNSPEC)
-                       continue;
-
-               if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
-                       continue;
-               if (!MATCH(pai->ai_socktype, ex->e_socktype, WILD_SOCKTYPE(ex)))
-                       continue;
-               if (!MATCH(pai->ai_protocol, ex->e_protocol, WILD_PROTOCOL(ex)))
+               if (!MATCH_FAMILY(pai->ai_family, afd->a_af, 1))
                        continue;
 
                if (pai->ai_family == PF_UNSPEC)
-                       pai->ai_family = ex->e_af;
-               if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
-                       pai->ai_socktype = ex->e_socktype;
-               if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
-                       pai->ai_protocol = ex->e_protocol;
+                       pai->ai_family = afd->a_af;
 
-               if (hostname == NULL)
-                       error = explore_null(pai, servname, &cur->ai_next);
-               else
+               if (hostname == NULL) {
+                       error = explore_null(pai, servname,
+                           &afailist[afd - afdl]);
+
+                       /*
+                        * Errors from explore_null should be unexpected and
+                        * be caught to avoid returning an incomplete result.
+                        */
+                       if (error != 0)
+                               goto bad;
+               } else {
                        error = explore_numeric_scope(pai, hostname, servname,
-                           &cur->ai_next);
+                           &afailist[afd - afdl]);
 
-               if (error)
-                       goto free;
+                       /*
+                        * explore_numeric_scope returns an error for address
+                        * families that do not match that of hostname.
+                        * Thus we should not catch the error at this moment. 
+                        */
+               }
 
-               while (cur && cur->ai_next)
-                       cur = cur->ai_next;
+               if (!error && afailist[afd - afdl])
+                       found++;
        }
-
-       /*
-        * XXX
-        * If numreic representation of AF1 can be interpreted as FQDN
-        * representation of AF2, we need to think again about the code below.
-        */
-       if (sentinel.ai_next) {
+       if (found) {
                numeric = 1;
-               goto good;
+               goto globcopy;
        }
 
        if (hostname == NULL)
@@ -515,42 +525,55 @@ getaddrinfo(const char *hostname, const 
 
        /*
         * hostname as alphabetical name.
-        * we would like to prefer AF_INET6 than AF_INET, so we'll make a
-        * outer loop by AFs.
         */
+       *pai = ai0;
+       error = explore_fqdn(pai, hostname, servname, &afai_unspec);
+
+globcopy:
        for (ex = explore; ex->e_af >= 0; ex++) {
                *pai = ai0;
 
-               /* require exact match for family field */
-               if (pai->ai_family != ex->e_af)
+               if (!MATCH_FAMILY(pai->ai_family, ex->e_af, WILD_AF(ex)))
                        continue;
-
                if (!MATCH(pai->ai_socktype, ex->e_socktype,
-                               WILD_SOCKTYPE(ex))) {
+                   WILD_SOCKTYPE(ex)))
                        continue;
-               }
                if (!MATCH(pai->ai_protocol, ex->e_protocol,
-                               WILD_PROTOCOL(ex))) {
+                   WILD_PROTOCOL(ex)))
                        continue;
-               }
 
+               if (pai->ai_family == PF_UNSPEC)
+                       pai->ai_family = ex->e_af;
                if (pai->ai_socktype == ANY && ex->e_socktype != ANY)
                        pai->ai_socktype = ex->e_socktype;
                if (pai->ai_protocol == ANY && ex->e_protocol != ANY)
                        pai->ai_protocol = ex->e_protocol;
 
-               error = explore_fqdn(pai, hostname, servname,
-                       &cur->ai_next);
+               /*
+                * if the servname does not match socktype/protocol, ignore it.
+                */
+               if (get_portmatch(pai, servname) != 0)
+                       continue;
+
+               if (afai_unspec)
+                       afai = afai_unspec;
+               else {
+                       if ((afd = find_afd(pai->ai_family)) == NULL)
+                               continue;
+                       /* XXX assumes that afd points inside afdl[] */
+                       afai = afailist[afd - afdl];
+               }
+               if (!afai)
+                       continue;
+
+               error = explore_copy(pai, afai, &cur->ai_next);
+               if (error != 0)
+                       goto bad;
 
                while (cur && cur->ai_next)
                        cur = cur->ai_next;
        }
 
-       /* XXX inhibit errors if we have the result */
-       if (sentinel.ai_next)
-               error = 0;
-
-good:
        /*
         * ensure we return either:
         * - error == 0, non-NULL *res
@@ -586,16 +609,22 @@ good:
                                }
                        }
                        *res = sentinel.ai_next;
-                       return SUCCESS;
                } else
                        error = EAI_FAIL;
        }
-free:
+
 bad:
-       if (sentinel.ai_next)
-               freeaddrinfo(sentinel.ai_next);
-       *res = NULL;
-       return error;
+       if (afai_unspec)
+               freeaddrinfo(afai_unspec);
+       for (afd = afdl; afd->a_af; afd++) {
+               if (afailist[afd - afdl])
+                       freeaddrinfo(afailist[afd - afdl]);
+       }
+       if (!*res)
+               if (sentinel.ai_next)
+                       freeaddrinfo(sentinel.ai_next);
+
+       return (error);
 }
 
 static int
@@ -1047,6 +1076,41 @@ gai_addr2scopetype(struct sockaddr *sa)
        }
 }
 
+static int
+explore_copy(const struct addrinfo *pai, const struct addrinfo *src0,
+    struct addrinfo **res)
+{
+       int error;
+       struct addrinfo sentinel, *cur;
+       const struct addrinfo *src;
+
+       error = 0;
+       sentinel.ai_next = NULL;
+       cur = &sentinel;
+
+       for (src = src0; src != NULL; src = src->ai_next) {
+               if (src->ai_family != pai->ai_family)
+                       continue;
+
+               cur->ai_next = copy_ai(src);
+               if (!cur->ai_next) {
+                       error = EAI_MEMORY;
+                       goto fail;
+               }
+
+               cur->ai_next->ai_socktype = pai->ai_socktype;
+               cur->ai_next->ai_protocol = pai->ai_protocol;
+               cur = cur->ai_next;
+       }
+
+       *res = sentinel.ai_next;
+       return 0;
+
+fail:
+       freeaddrinfo(sentinel.ai_next);
+       return error;
+}
+
 /*
  * hostname == NULL.
  * passive socket -> anyaddr (0.0.0.0 or ::)
@@ -1075,12 +1139,6 @@ explore_null(const struct addrinfo *pai,
        } else
                _close(s);
 
-       /*
-        * if the servname does not match socktype/protocol, ignore it.
-        */
-       if (get_portmatch(pai, servname) != 0)
-               return 0;
-
        afd = find_afd(pai->ai_family);
        if (afd == NULL)
                return 0;
@@ -1117,12 +1175,6 @@ explore_numeric(const struct addrinfo *p
        *res = NULL;
        ai = NULL;
 
-       /*
-        * if the servname does not match socktype/protocol, ignore it.
-        */
-       if (get_portmatch(pai, servname) != 0)
-               return 0;
-
        afd = find_afd(pai->ai_family);
        if (afd == NULL)
                return 0;
@@ -1189,12 +1241,6 @@ explore_numeric_scope(const struct addri
        char *cp, *hostname2 = NULL, *scope, *addr;
        struct sockaddr_in6 *sin6;
 
-       /*
-        * if the servname does not match socktype/protocol, ignore it.
-        */
-       if (get_portmatch(pai, servname) != 0)
-               return 0;
-
        afd = find_afd(pai->ai_family);
        if (afd == NULL)
                return 0;
@@ -1227,6 +1273,8 @@ explore_numeric_scope(const struct addri
                        sin6 = (struct sockaddr_in6 *)(void *)cur->ai_addr;
                        if (ip6_str2scopeid(scope, sin6, &scopeid) == -1) {
                                free(hostname2);
+                               freeaddrinfo(*res);
+                               *res = NULL;
                                return(EAI_NONAME); /* XXX: is return OK? */
                        }
                        sin6->sin6_scope_id = scopeid;
@@ -1235,6 +1283,10 @@ explore_numeric_scope(const struct addri
 
        free(hostname2);
 
+       if (error && *res) {
+               freeaddrinfo(*res);
+               *res = NULL;
+       }
        return error;
 #endif
 }
@@ -1318,6 +1370,38 @@ get_ai(const struct addrinfo *pai, const
        return ai;
 }
 
+/* XXX need to malloc() the same way we do from other functions! */
+static struct addrinfo *
+copy_ai(const struct addrinfo *pai)
+{
+       struct addrinfo *ai;
+       size_t l;
+
+       l = sizeof(*ai) + pai->ai_addrlen;
+       if ((ai = (struct addrinfo *)malloc(l)) == NULL)
+               return NULL;
+       memset(ai, 0, l);
+       memcpy(ai, pai, sizeof(*ai));
+       ai->ai_addr = (struct sockaddr *)(void *)(ai + 1);
+       memcpy(ai->ai_addr, pai->ai_addr, pai->ai_addrlen);
+
+       if (pai->ai_canonname) {
+               l = strlen(pai->ai_canonname) + 1;
+               if ((ai->ai_canonname = malloc(l)) == NULL) {
+                       free(ai);
+                       return NULL;
+               }
+               strlcpy(ai->ai_canonname, pai->ai_canonname, l);
+       } else {
+               /* just to make sure */
+               ai->ai_canonname = NULL;
+       }
+
+       ai->ai_next = NULL;
+
+       return ai;
+}
+
 static int
 get_portmatch(const struct addrinfo *ai, const char *servname)
 {
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to