Whoops, I made some last time changes which broke big-endian systems, spotted by Mattew.
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 20 Mar 2011 01:32:16 -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 20 Mar 2011 01:32:16 -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 20 Mar 2011 01:32:16 -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 20 Mar 2011 01:32:17 -0000 @@ -52,6 +52,7 @@ */ #include <sys/types.h> +#include <sys/socket.h> #include <sys/param.h> #include <netinet/in.h> #include <arpa/inet.h> @@ -65,6 +66,7 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <poll.h> #include "thread_private.h" @@ -400,4 +402,134 @@ 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 ALL_MDNS_DEVICES "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); + inet_aton(ALL_MDNS_DEVICES, &msin.sin_addr); + + 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 ALL_MDNS_DEVICES +#undef MDNS_PORT }