Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/lib/libc/net PR/32373, PR/25827: Add SRV lookup in getaddrin...



details:   https://anonhg.NetBSD.org/src/rev/dcc34113d5f2
branches:  trunk
changeset: 786615:dcc34113d5f2
user:      christos <christos%NetBSD.org@localhost>
date:      Fri May 03 19:31:13 2013 +0000

description:
PR/32373, PR/25827: Add SRV lookup in getaddrinfo(3)
Per DNS-SD (RFC 2782), but only enabled if AI_SRV is set.

diffstat:

 lib/libc/net/getaddrinfo.c |  516 ++++++++++++++++++++++++++++++++++++--------
 1 files changed, 420 insertions(+), 96 deletions(-)

diffs (truncated from 663 to 300 lines):

diff -r d85213e15006 -r dcc34113d5f2 lib/libc/net/getaddrinfo.c
--- a/lib/libc/net/getaddrinfo.c        Fri May 03 19:24:52 2013 +0000
+++ b/lib/libc/net/getaddrinfo.c        Fri May 03 19:31:13 2013 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: getaddrinfo.c,v 1.102 2013/05/03 19:24:52 christos Exp $ */
+/*     $NetBSD: getaddrinfo.c,v 1.103 2013/05/03 19:31:13 christos Exp $       */
 /*     $KAME: getaddrinfo.c,v 1.29 2000/08/31 17:26:57 itojun Exp $    */
 
 /*
@@ -55,7 +55,7 @@
 
 #include <sys/cdefs.h>
 #if defined(LIBC_SCCS) && !defined(lint)
-__RCSID("$NetBSD: getaddrinfo.c,v 1.102 2013/05/03 19:24:52 christos Exp $");
+__RCSID("$NetBSD: getaddrinfo.c,v 1.103 2013/05/03 19:31:13 christos Exp $");
 #endif /* LIBC_SCCS and not lint */
 
 #include "namespace.h"
@@ -191,6 +191,13 @@
        int n;                  /* result length */
 };
 
+struct srvinfo {
+       struct srvinfo *next;
+       char name[MAXDNAME];
+       int port, pri, weight;
+};
+
+static int gai_srvok(const char *);
 static int str2number(const char *);
 static int explore_fqdn(const struct addrinfo *, const char *,
     const char *, struct addrinfo **, struct servent_data *);
@@ -217,6 +224,12 @@
 static struct addrinfo *getanswer(const querybuf *, int, const char *, int,
     const struct addrinfo *);
 static void aisort(struct addrinfo *s, res_state res);
+static struct addrinfo * _dns_query(struct res_target *,
+    const struct addrinfo *, res_state, int);
+static struct addrinfo * _dns_srv_lookup(const char *, const char *,
+    const struct addrinfo *);
+static struct addrinfo * _dns_host_lookup(const char *,
+    const struct addrinfo *);
 static int _dns_getaddrinfo(void *, void *, va_list);
 static void _sethtent(FILE **);
 static void _endhtent(FILE **);
@@ -319,6 +332,58 @@
        } while (ai);
 }
 
+/*
+ * We don't want localization to affect us
+ */
+#define PERIOD '.'
+#define hyphenchar(c) ((c) == '-')
+#define periodchar(c) ((c) == PERIOD)
+#define underschar(c) ((c) == '_')
+#define alphachar(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
+#define digitchar(c) ((c) >= '0' && (c) <= '9')
+
+#define firstchar(c)  (alphachar(c) || digitchar(c) || underschar(c))
+#define lastchar(c)   (alphachar(c) || digitchar(c))
+#define middlechar(c) (lastchar(c) || hyphenchar(c))
+
+static int
+gai_srvok(const char *dn)
+{
+       int nch, pch, ch;
+
+       for (pch = PERIOD, nch = ch = *dn++; ch != '\0'; pch = ch, ch = nch) {
+               if (periodchar(ch))
+                       continue;
+               if (periodchar(pch)) {
+                       if (!firstchar(ch))
+                               return 0;
+               } else if (periodchar(nch) || nch == '\0') {
+                       if (!lastchar(ch))
+                               return 0;
+               } else if (!middlechar(ch))
+                       return 0;
+       }
+       return 1;
+}
+
+static in_port_t *
+getport(struct addrinfo *ai) {
+       static in_port_t p;
+
+       switch (ai->ai_family) {
+       case AF_INET:
+               return &((struct sockaddr_in *)(void *)ai->ai_addr)->sin_port;
+#ifdef INET6
+       case AF_INET6:
+               return &((struct sockaddr_in6 *)(void *)ai->ai_addr)->sin6_port;
+#endif
+       default:
+               p = 0;
+               /* XXX: abort()? */
+               return &p;
+       }
+}
+
 static int
 str2number(const char *p)
 {
@@ -589,7 +654,7 @@
                return 0;
 
        switch (nsdispatch(&result, dtab, NSDB_HOSTS, "getaddrinfo",
-                       default_dns_files, hostname, pai)) {
+           default_dns_files, hostname, pai, servname)) {
        case NS_TRYAGAIN:
                error = EAI_AGAIN;
                goto free;
@@ -602,6 +667,9 @@
        case NS_SUCCESS:
                error = 0;
                for (cur = result; cur; cur = cur->ai_next) {
+                       /* Check for already filled port. */
+                       if (*getport(cur))
+                               continue;
                        GET_PORT(cur, servname, svd);
                        /* canonname should be filled already */
                }
@@ -990,21 +1058,8 @@
                port = sp->s_port;
        }
 
-       if (!matchonly) {
-               switch (ai->ai_family) {
-               case AF_INET:
-                       ((struct sockaddr_in *)(void *)
-                           ai->ai_addr)->sin_port = port;
-                       break;
-#ifdef INET6
-               case AF_INET6:
-                       ((struct sockaddr_in6 *)(void *)
-                           ai->ai_addr)->sin6_port = port;
-                       break;
-#endif
-               }
-       }
-
+       if (!matchonly)
+               *getport(__UNCONST(ai)) = port;
        return 0;
 }
 
@@ -1107,7 +1162,7 @@
     const struct addrinfo *pai)
 {
        struct addrinfo sentinel, *cur;
-       struct addrinfo ai;
+       struct addrinfo ai, *aip;
        const struct afd *afd;
        char *canonname;
        const HEADER *hp;
@@ -1120,6 +1175,8 @@
        char tbuf[MAXDNAME];
        int (*name_ok) (const char *);
        char hostbuf[8*1024];
+       int port, pri, weight;
+       struct srvinfo *srvlist, *srv, *csrv;
 
        _DIAGASSERT(answer != NULL);
        _DIAGASSERT(qname != NULL);
@@ -1136,6 +1193,9 @@
        case T_ANY:     /*use T_ANY only for T_A/T_AAAA lookup*/
                name_ok = res_hnok;
                break;
+       case T_SRV:
+               name_ok = gai_srvok;
+               break;
        default:
                return NULL;    /* XXX should be abort(); */
        }
@@ -1175,6 +1235,7 @@
        }
        haveanswer = 0;
        had_error = 0;
+       srvlist = NULL;
        while (ancount-- > 0 && cp < eom && !had_error) {
                n = dn_expand(answer->buf, eom, cp, bp, (int)(ep - bp));
                if ((n < 0) || !(*name_ok)(bp)) {
@@ -1277,17 +1338,116 @@
                                cur = cur->ai_next;
                        cp += n;
                        break;
+               case T_SRV:
+                       /* Add to SRV list. Insertion sort on priority. */
+                       pri = _getshort(cp);
+                       cp += INT16SZ;
+                       weight = _getshort(cp);
+                       cp += INT16SZ;
+                       port = _getshort(cp);
+                       cp += INT16SZ;
+                       n = dn_expand(answer->buf, eom, cp, tbuf,
+                           (int)sizeof(tbuf));
+                       if ((n < 0) || !res_hnok(tbuf)) {
+                               had_error++;
+                               continue;
+                       }
+                       cp += n;
+                       if (strlen(tbuf) + 1 >= MAXDNAME) {
+                               had_error++;
+                               continue;
+                       }
+                       srv = malloc(sizeof(*srv));
+                       if (!srv) {
+                               had_error++;
+                               continue;
+                       }
+                       strlcpy(srv->name, tbuf, sizeof(srv->name));
+                       srv->pri = pri;
+                       srv->weight = weight;
+                       srv->port = port;
+                       /* Weight 0 is sorted before other weights. */
+                       if (!srvlist
+                           || srv->pri < srvlist->pri
+                           || (srv->pri == srvlist->pri &&
+                           (!srv->weight || srvlist->weight))) {
+                               srv->next = srvlist;
+                               srvlist = srv;
+                       } else {
+                               for (csrv = srvlist;
+                                   csrv->next && csrv->next->pri <= srv->pri;
+                                   csrv = csrv->next) {
+                                       if (csrv->next->pri == srv->pri
+                                           && (!srv->weight ||
+                                           csrv->next->weight))
+                                               break;
+                               }
+                               srv->next = csrv->next;
+                               csrv->next = srv;
+                       }
+                       continue; /* Don't add to haveanswer yet. */
                default:
                        abort();
                }
                if (!had_error)
                        haveanswer++;
        }
+
+       if (srvlist) {
+               res_state res;
+               /*
+                * Check for explicit rejection.
+                */
+               if (!srvlist->next && !srvlist->name[0]) {
+                       free(srvlist);
+                       h_errno = HOST_NOT_FOUND;
+                       return NULL;
+               }
+               res = __res_get_state();
+               if (res == NULL) {
+                       h_errno = NETDB_INTERNAL;
+                       return NULL;
+               }
+
+               while (srvlist) {
+                       struct res_target q, q2;
+
+                       srv = srvlist;
+                       srvlist = srvlist->next;
+
+                       /*
+                        * Since res_* doesn't give the additional
+                        * section, we always look up.
+                        */
+                       memset(&q, 0, sizeof(q));
+                       memset(&q2, 0, sizeof(q2));
+
+                       q.name = srv->name;
+                       q.qclass = C_IN;
+                       q.qtype = T_AAAA;
+                       q.next = &q2;
+                       q2.name = srv->name;
+                       q2.qclass = C_IN;
+                       q2.qtype = T_A;
+
+                       aip = _dns_query(&q, pai, res, 0);
+
+                       if (aip != NULL) {
+                               cur->ai_next = aip;
+                               while (cur && cur->ai_next) {
+                                       cur = cur->ai_next;
+                                       *getport(cur) = htons(srv->port);
+                                       haveanswer++;
+                               }
+                       }
+                       free(srv);
+               }
+               __res_put_state(res);
+       }
        if (haveanswer) {
-               if (!canonname)
-                       (void)get_canonname(pai, sentinel.ai_next, qname);
-               else
-                       (void)get_canonname(pai, sentinel.ai_next, canonname);
+               if (!sentinel.ai_next->ai_canonname)
+                      (void)get_canonname(pai, sentinel.ai_next,



Home | Main Index | Thread Index | Old Index