Hi,

The following adds legacy MDNS lookups to libc.

It adds the keyword 'mdns' to 'lookup' in /etc/resolv.conf, only names in the
.local (MDNS domain) are looked up.

A legacy lookup is how MDNS calls a simple unicast lookup sent to the mcast addr
224.0.0.251, the MDNS responder is responsible for identifying the legacy query
and answering it via unicast, like a normal unicast domain server would do.

In order to *answer* the queries you need a proper MDNS daemon running, OpenMDNS
is now in ports and works fine. You could also run avahi or bonjour, but please
try OpenMDNS instead :-). http://www.haesbaert.org/openmdns

This approach is different from linux+avahi and netbsd+bonjour, in which they
route the queries to the daemon in order to use standard MDNS queries.

I've been using this in i386 and sparc64 for about a week, support for both
getaddrinfo(3) and gethostbyname(3), AF_INET and AF_INET6, no support for
reverse lookups yet.

I'm still unfamiliar with the libc resolv code, so if I'm doing something wrong
please correct me, I've tried to keep the code apart from the rest of the
resolver. 

Index: net/getaddrinfo.c
===================================================================
RCS file: /cvs/src/lib/libc/net/getaddrinfo.c,v
retrieving revision 1.71
diff -d -u -p -w -r1.71 getaddrinfo.c
--- net/getaddrinfo.c   18 Nov 2009 07:43:22 -0000      1.71
+++ net/getaddrinfo.c   15 Mar 2011 00:56:20 -0000
@@ -235,6 +235,8 @@ static int res_searchN(const char *, str
 static int res_querydomainN(const char *, const char *, struct res_target *);
 static struct addrinfo *_dns_getaddrinfo(const char *, const struct addrinfo *,
        const struct __res_state *);
+static struct addrinfo *_mcast_getaddrinfo(const char *,
+    const struct addrinfo *, const struct __res_state *);
 
 
 /* XXX macros that make external reference is BAD. */
@@ -523,6 +525,9 @@ explore_fqdn(const struct addrinfo *pai,
                case 'f':
                        result = _files_getaddrinfo(hostname, pai);
                        break;
+               case 'm':
+                       result = _mcast_getaddrinfo(hostname, pai, _resp);
+                       break;
                }
        }
        _THREAD_PRIVATE_MUTEX_UNLOCK(_explore_mutex);
@@ -1209,6 +1214,99 @@ _dns_getaddrinfo(const char *name, const
                free(buf);
                free(buf2);
                return NULL;
+       }
+       ai = getanswer(buf, q.n, q.name, q.qtype, pai);
+       if (ai) {
+               cur->ai_next = ai;
+               while (cur && cur->ai_next)
+                       cur = cur->ai_next;
+       }
+       if (q.next) {
+               ai = getanswer(buf2, q2.n, q2.name, q2.qtype, pai);
+               if (ai)
+                       cur->ai_next = ai;
+       }
+       free(buf);
+       free(buf2);
+       return sentinel.ai_next;
+}
+
+static struct addrinfo *
+_mcast_getaddrinfo(const char *name, const struct addrinfo *pai,
+    const struct __res_state *_resp)
+{
+       struct addrinfo *ai;
+       querybuf *buf, *buf2;
+       struct addrinfo sentinel, *cur;
+       struct res_target q, q2;
+
+       memset(&q, 0, sizeof(q));
+       memset(&q2, 0, sizeof(q2));
+       memset(&sentinel, 0, sizeof(sentinel));
+       cur = &sentinel;
+
+       buf = malloc(sizeof(*buf));
+       if (buf == NULL) {
+               h_errno = NETDB_INTERNAL;
+               return NULL;
+       }
+       buf2 = malloc(sizeof(*buf2));
+       if (buf2 == NULL) {
+               free(buf);
+               h_errno = NETDB_INTERNAL;
+               return NULL;
+       }
+
+       switch (pai->ai_family) {
+       case AF_UNSPEC:
+               /* respect user supplied order */
+               q.qclass = C_IN;
+               q.qtype = (_resp->family[0] == AF_INET6) ? T_AAAA : T_A;
+               q.answer = buf->buf;
+               q.anslen = sizeof(buf->buf);
+               q.next = &q2;
+
+               if (_resp->family[1] == -1) {
+                       /* stop here if only one family was given */
+                       q.next = NULL;
+                       break;
+               }
+
+               q2.qclass = C_IN;
+               q2.qtype = (_resp->family[1] == AF_INET6) ? T_AAAA : T_A;
+               q2.answer = buf2->buf;
+               q2.anslen = sizeof(buf2->buf);
+               break;
+       case AF_INET:
+               q.qclass = C_IN;
+               q.qtype = T_A;
+               q.answer = buf->buf;
+               q.anslen = sizeof(buf->buf);
+               break;
+       case AF_INET6:
+               q.qclass = C_IN;
+               q.qtype = T_AAAA;
+               q.answer = buf->buf;
+               q.anslen = sizeof(buf->buf);
+               break;
+       default:
+               free(buf);
+               free(buf2);
+               return NULL;
+       }
+       if ((q.n = res_search_mcast(name,
+           q.qclass, q.qtype, q.answer, q.anslen)) < 0) {
+               free(buf);
+               free(buf2);
+               return (NULL);
+       }
+       if (q.next != NULL) {
+               if ((q2.n = res_search_mcast(name,
+                   q2.qclass, q2.qtype, q2.answer, q2.anslen)) < 0) {
+                       free(buf);
+                       free(buf2);
+                       return (NULL);
+               }
        }
        ai = getanswer(buf, q.n, q.name, q.qtype, pai);
        if (ai) {
Index: net/gethostnamadr.c
===================================================================
RCS file: /cvs/src/lib/libc/net/gethostnamadr.c,v
retrieving revision 1.73
diff -d -u -p -w -r1.73 gethostnamadr.c
--- net/gethostnamadr.c 18 Nov 2009 07:43:22 -0000      1.73
+++ net/gethostnamadr.c 15 Mar 2011 00:56:20 -0000
@@ -606,6 +606,7 @@ gethostbyname2(const char *name, int af)
 
        hp = (struct hostent *)NULL;
        for (i = 0; i < MAXDNSLUS && hp == NULL && lookups[i]; i++) {
+               size_t nlen;
                switch (lookups[i]) {
 #ifdef YP
                case 'y':
@@ -615,9 +616,10 @@ gethostbyname2(const char *name, int af)
                        break;
 #endif
                case 'b':
-                       buf = malloc(sizeof(*buf));
-                       if (buf == NULL)
+                       if ((buf = malloc(sizeof(*buf))) == NULL) {
+                               h_errno = NETDB_INTERNAL;
                                break;
+                       }
                        if ((n = res_search(name, C_IN, type, buf->buf,
                            sizeof(buf->buf))) < 0) {
                                free(buf);
@@ -633,6 +635,23 @@ gethostbyname2(const char *name, int af)
                case 'f':
                        hp = _gethtbyname2(name, af);
                        break;
+               case 'm':
+                       if ((buf = malloc(sizeof(*buf))) == NULL) {
+                               h_errno = NETDB_INTERNAL;
+                               break;
+                       }
+                       if ((n = res_search_mcast(name, C_IN, type, buf->buf,
+                           sizeof(buf->buf))) < 0) {
+                               free(buf);
+#ifdef DEBUG
+                               if (_resp->options & RES_DEBUG)
+                                       printf("res_search_mcast failed\n");
+#endif
+                               break;
+                       }
+                       hp = getanswer(buf, n, name, type);
+                       free(buf);
+                       break;   
                }
        }
        /* XXX h_errno not correct in all cases... */
Index: net/res_init.c
===================================================================
RCS file: /cvs/src/lib/libc/net/res_init.c,v
retrieving revision 1.40
diff -d -u -p -w -r1.40 res_init.c
--- net/res_init.c      5 Jun 2009 09:52:26 -0000       1.40
+++ net/res_init.c      15 Mar 2011 00:56:20 -0000
@@ -361,14 +361,16 @@ _res_init(int usercall)
                                    break;
                            if ((*cp == '\0') || (*cp == '\n')) {
                                    if (sp) {
-                                           if (*sp=='y' || *sp=='b' || 
*sp=='f')
+                                           if (*sp=='y' || *sp=='b' ||
+                                               *sp=='f' || *sp=='m')
                                                    _resp->lookups[n++] = *sp;
                                            sp = NULL;
                                    }
                                    break;
                            } else if ((*cp == ' ') || (*cp == '\t') || (*cp == 
',')) {
                                    if (sp) {
-                                           if (*sp=='y' || *sp=='b' || 
*sp=='f')
+                                           if (*sp=='y' || *sp=='b' ||
+                                               *sp=='f' || *sp=='m')
                                                    _resp->lookups[n++] = *sp;
                                            sp = NULL;
                                    }
Index: net/res_query.c
===================================================================
RCS file: /cvs/src/lib/libc/net/res_query.c,v
retrieving revision 1.26
diff -d -u -p -w -r1.26 res_query.c
--- net/res_query.c     29 Jun 2010 21:08:54 -0000      1.26
+++ net/res_query.c     15 Mar 2011 00:56:21 -0000
@@ -65,6 +65,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <poll.h>
 
 #include "thread_private.h"
 
@@ -400,4 +401,133 @@ hostalias(const char *name)
        }
        fclose(fp);
        return (NULL);
+}
+
+int
+res_search_mcast(const char *name,
+    int class,         /* domain name */
+    int type,          /* class and type of query */
+    u_char *answer,    /* buffer to put answer */
+    int anslen)                /* size of answer */
+{
+#define MDNS_ADDRT 0xFB0000E0  /* 224.0.0.251 */
+#define MDNS_PORT 5353 
+       union {
+               HEADER hdr;
+               u_char buf[MAXPACKET];
+       } buf;
+       struct __res_state *_resp = _THREAD_PRIVATE(_res, _res, &_res);
+       struct sockaddr_in msin, from;
+       socklen_t msinlen, fromlen;
+       size_t resplen, nlen;
+       struct pollfd pfd;
+       int timeout, try;
+       int msock;
+       int n;
+       HEADER *hp = &buf.hdr;   
+       HEADER *anhp = (HEADER *) answer;
+
+       /*
+        * Only names in the .local domain should be looked up in the mdns
+        * network.
+        */
+       nlen = strlen(name);
+       if (nlen < 7 || (strcmp(&name[nlen - 6], ".local") != 0))
+               return (0);
+       
+       if (_res_init(0) == -1) {
+               h_errno = NETDB_INTERNAL;
+               return (-1);
+       }
+       h_errno = HOST_NOT_FOUND;
+       
+       n = res_mkquery(QUERY, name, class, type, NULL, 0, NULL,
+           buf.buf, sizeof(buf.buf));
+       if (n <= 0) {
+               h_errno = NO_RECOVERY;
+#ifdef DEBUG
+               if (_resp->options & RES_DEBUG)
+                       printf(";; res_query: mkquery failed\n");
+#endif
+               return (n);
+       }
+
+       bzero(&msin, sizeof(msin));
+       bzero(&from, sizeof(from));
+       msinlen = fromlen = sizeof(struct sockaddr_in);
+       /* Initialize msin to send to mcast */
+       msin.sin_family = AF_INET;
+       msin.sin_port = htons(MDNS_PORT);
+       msin.sin_addr.s_addr = MDNS_ADDRT;
+       
+       if ((msock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+               h_errno = TRY_AGAIN;
+               return (-1);
+       }
+       if (sendto(msock, buf.buf, n, 0,
+           (struct sockaddr *)&msin, msinlen) != n) {
+               h_errno = TRY_AGAIN;
+               close(msock);
+               return (-1);
+       }
+       /*
+        * Wait for reply
+        */
+       try = 3;
+       timeout = 3000;
+wait:  
+       if (try-- == 0) {
+               close(msock);
+               h_errno = HOST_NOT_FOUND;
+               return (-1);
+       }
+       pfd.fd = msock;
+       pfd.events = POLLIN;
+       n = poll(&pfd, 1, timeout);
+       if (n < 0) {
+               if (errno == EINTR)
+                       goto wait;
+               h_errno = TRY_AGAIN;
+               close(msock);
+               return (-1);
+       }
+       if (n == 0) {
+               /*
+                * timeout
+                */
+               h_errno = HOST_NOT_FOUND;
+               close(msock);
+               return (0);
+       }
+       /* Got an answer */
+       h_errno = NETDB_SUCCESS;
+       resplen = recvfrom(msock, answer, anslen, 0,
+           (struct sockaddr *)&from, &fromlen);
+       if (resplen <= 0) {
+               h_errno = TRY_AGAIN;
+               close(msock);
+               return (-1);
+       }
+       /* Make sure this is the answer for our question */
+       if (hp->id != anhp->id) {
+               /*
+                * response from old query, ignore it.
+                * XXX - potential security hazard could
+                *       be detected here.
+                */
+               goto wait;
+       }
+       if (!res_queriesmatch(buf.buf, buf.buf + sizeof(buf.buf),
+           answer, answer + anslen)) {
+               /*
+                * response contains wrong query? ignore it.
+                * XXX - potential security hazard could
+                *       be detected here.
+                */
+               goto wait;
+       }
+
+       return (resplen);
+#undef MDNS_ADDRT
+#undef MDNS_PORT       
 }

Reply via email to