On Tue, Mar 15, 2011 at 01:06:21AM -0300, Christiano F. Haesbaert wrote:
> 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     
>  }

Hi,

Any feedback on this ?

-- 
Christiano Farina HAESBAERT
Do NOT send me html mail.

Reply via email to