--- resolv/res_hconf.c +++ resolv/res_hconf.c 2004/09/21 12:56:54 @@ -59,6 +59,7 @@ #define ENV_TRIM_ADD "RESOLV_ADD_TRIM_DOMAINS" #define ENV_MULTI "RESOLV_MULTI" #define ENV_REORDER "RESOLV_REORDER" +#define ENV_MDNS "RESOLV_MDNS" static const char *arg_service_list (const char *, int, const char *, unsigned int); @@ -81,7 +82,8 @@ {"multi", arg_bool, HCONF_FLAG_MULTI}, {"nospoof", arg_bool, HCONF_FLAG_SPOOF}, {"spoofalert", arg_bool, HCONF_FLAG_SPOOFALERT}, - {"reorder", arg_bool, HCONF_FLAG_REORDER} + {"reorder", arg_bool, HCONF_FLAG_REORDER}, + {"mdns", arg_bool, HCONF_FLAG_MDNS} }; /* Structure containing the state. */ @@ -431,6 +433,9 @@ memset (&_res_hconf, '\0', sizeof (_res_hconf)); + /* Default for mdns is "on". */ + _res_hconf.flags |= HCONF_FLAG_MDNS; + hconf_name = getenv (ENV_HOSTCONF); if (hconf_name == NULL) hconf_name = _PATH_HOSTCONF; @@ -483,6 +488,10 @@ arg_trimdomain_list (ENV_TRIM_OVERR, 1, envval, 0); } + envval = getenv (ENV_MDNS); + if (envval) + arg_bool (ENV_MDNS, 1, envval, HCONF_FLAG_MDNS); + _res_hconf.initialized = 1; } --- resolv/res_hconf.h +++ resolv/res_hconf.h 2004/09/21 12:56:54 @@ -44,6 +44,7 @@ # define HCONF_FLAG_SPOOFALERT (1 << 2) /* syslog warning of spoofed */ # define HCONF_FLAG_REORDER (1 << 3) /* list best address first */ # define HCONF_FLAG_MULTI (1 << 4) /* see comments for gethtbyname() */ +# define HCONF_FLAG_MDNS (1 << 5) /* Disable MDNS support */ }; extern struct hconf _res_hconf; --- resolv/res_query.c +++ resolv/res_query.c 2004/09/21 12:57:59 @@ -82,6 +82,8 @@ #include #include +#include "res_hconf.h" + /* Options. Leave them on. */ /* #undef DEBUG */ @@ -278,6 +280,13 @@ *domain && !done; domain++) { + if ((_res_hconf.flags & HCONF_FLAG_MDNS) != 0) { + /* don't add "local" domain if query contains a dot */ + if (dots && (!__strcasecmp(*domain, "local") || + !__strcasecmp(*domain, "local."))) + continue; + } + if (domain[0][0] == '\0' || (domain[0][0] == '.' && domain[0][1] == '\0')) root_on_list++; --- resolv/res_send.c +++ resolv/res_send.c 2004/09/21 12:58:14 @@ -86,6 +86,9 @@ #include #include #include +#if defined(_LIBC) && defined(linux) +#include +#endif #include #include @@ -96,6 +99,8 @@ #include #include +#include "res_hconf.h" + #if PACKETSZ > 65536 #define MAXPACKET PACKETSZ #else @@ -188,6 +193,9 @@ static int send_dg(res_state, const u_char *, int, u_char **, int *, int *, int, int *, int *, u_char **); +static int send_dg_mdns(res_state, const u_char *, int, + u_char **, int *, int *, struct sockaddr_in6 *, + int *, int *, u_char **); #ifdef DEBUG static void Aerror(const res_state, FILE *, const char *, int, const struct sockaddr *); @@ -366,6 +374,35 @@ u_char *ans, int anssiz, u_char **ansp) { int gotsomewhere, terrno, try, v_circuit, resplen, ns, n; + int usemdns; + HEADER *qhp = (HEADER *) buf; + + usemdns = 0; + if ((_res_hconf.flags & HCONF_FLAG_MDNS) != 0 && + qhp->qr == 0 && qhp->opcode == QUERY && qhp->qdcount == htons(1)) { + /* got one simple query */ + const u_char *bp, *be; + be = buf + buflen; + for (bp = buf + NS_HFIXEDSZ; bp < be; ) + if ((*bp & NS_CMPRSFLGS) != 0) + break; + else if (*bp) { + if (*bp == 5 && !strncasecmp(bp, "\005local\000", 7)) { + usemdns = 1; + break; + } + if (*bp == 3 && !strncasecmp(bp, "\003254\003169\007in-addr\004arpa\000", 22)) { + usemdns = 1; + break; + } + if (*bp == 1 && !strncasecmp(bp, "\0010\0018\001e\001f\003ip6\004arpa\000", 18)) { + usemdns = 2; + break; + } + bp += *bp + 1; + } else + break; + } if (statp->nscount == 0) { __set_errno (ESRCH); @@ -524,13 +561,28 @@ */ for (try = 0; try < statp->retry; try++) { #ifdef _LIBC - for (ns = 0; ns < MAXNS; ns++) + for (ns = 0; ns < (usemdns ? 1 : MAXNS); ns++) #else for (ns = 0; ns < statp->nscount; ns++) #endif { #ifdef _LIBC struct sockaddr_in6 *nsap = EXT(statp).nsaddrs[ns]; + if (usemdns == 1) { + static struct sockaddr_in mdns4; + mdns4.sin_family = AF_INET; + mdns4.sin_port = htons(5353); + mdns4.sin_addr.s_addr = htonl(0xe00000fb); + nsap = (struct sockaddr_in6 *)&mdns4; + } + if (usemdns == 2) { + static struct sockaddr_in6 mdns6; + mdns6.sin6_family = AF_INET6; + mdns6.sin6_port = htons(5353); + mdns6.sin6_addr.s6_addr32[0] = htonl(0xff020000); + mdns6.sin6_addr.s6_addr32[3] = htonl(0x000000fb); + nsap = &mdns6; + } if (nsap == NULL) goto next_ns; @@ -602,8 +654,11 @@ resplen = n; } else { /* Use datagrams. */ - n = send_dg(statp, buf, buflen, &ans, &anssiz, &terrno, - ns, &v_circuit, &gotsomewhere, ansp); + if (usemdns) + n = send_dg_mdns(statp, buf, buflen, &ans, &anssiz, &terrno, nsap, &v_circuit, &gotsomewhere, ansp); + else + n = send_dg(statp, buf, buflen, &ans, &anssiz, &terrno, + ns, &v_circuit, &gotsomewhere, ansp); if (n < 0) return (-1); if (n == 0) @@ -675,8 +730,15 @@ if (!v_circuit) { if (!gotsomewhere) __set_errno (ECONNREFUSED); /* no nameservers found */ - else + else if (!usemdns) { __set_errno (ETIMEDOUT); /* no answer obtained */ + } else { + /* treat timeout as host not found */ + HEADER *anhp = (HEADER *) ans; + memset(ans, 0, HFIXEDSZ); + anhp->rcode = NXDOMAIN; + return HFIXEDSZ; + } } else __set_errno (terrno); return (-1); @@ -1140,6 +1202,255 @@ return (resplen); } +static int +send_dg_mdns(res_state statp, + const u_char *buf, int buflen, u_char **ansp, int *anssizp, + int *terrno, struct sockaddr_in6 *nsap, int *v_circuit, int *gotsomewhere, u_char **anscp) +{ + const HEADER *hp = (HEADER *) buf; + u_char *ans = *ansp; + int anssiz = *anssizp; + HEADER *anhp = (HEADER *) ans; + struct timespec now, timeout, finish; + struct pollfd pfd[32]; + int ptimeout; + int fromlen, resplen, seconds, n, s; + int on = 1; + struct msghdr mhdr; + struct iovec iov; + u_char cmsgbuf[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + int ttl; + struct ifconf ifconf; + struct ifreq ifreq[64]; + int ifreqn; + int i, j; + int ifidx[32], ifidxn; + struct ip_mreqn mreqn; + + s = socket(nsap->sin6_family == AF_INET ? PF_INET : PF_INET6, SOCK_DGRAM, 0); + if (s < 0) { + *terrno = errno; + Perror(statp, stderr, "socket(dg)", errno); + return (-1); + } + ifconf.ifc_len = sizeof(ifreq); + ifconf.ifc_req = ifreq; + ifidxn = 0; + if (ioctl(s, SIOCGIFCONF, &ifconf) == 0) { + ifreqn = ifconf.ifc_len / sizeof(*ifreq); + for (i = 0 ; i < ifreqn; i++) { + if (ioctl(s, SIOCGIFFLAGS, ifreq + i)) + continue; + if (!(ifreq[i].ifr_flags & IFF_MULTICAST)) + continue; + if (ioctl(s, SIOCGIFINDEX, ifreq + i)) + continue; + for (j = 0; j < ifidxn; j++) + if (ifidx[j] == ifreq[i].ifr_ifindex) + break; + if (j < ifidxn) + continue; + ifidx[ifidxn++] = ifreq[i].ifr_ifindex; + if (ifidxn == sizeof(ifidx)/sizeof(*ifidx)) + break; + } + } + j = 0; + for (i = 0; i < (ifidxn ? ifidxn : 1); i++) { + if (i) { + s = socket(nsap->sin6_family == AF_INET ? PF_INET : PF_INET6, SOCK_DGRAM, 0); + if (!s) + continue; + } + if (setsockopt(s, SOL_IP, IP_RECVTTL, &on, sizeof(on))) { + *terrno = errno; + Perror(statp, stderr, "IP_RECVTTL(dg)", errno); + close(s); + continue; + } + if (ifidxn) { + memset(&mreqn, 0, sizeof(mreqn)); + mreqn.imr_ifindex = ifidx[i]; + if (setsockopt(s, SOL_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn))) { + *terrno = errno; + Perror(statp, stderr, "IP_MULTICAST_IF", errno); + close(s); + continue; + } + } + if (sendto(s, (char*)buf, buflen, 0, + (struct sockaddr *)nsap, sizeof *nsap) != buflen) { + Aerror(statp, stderr, "sendto", errno, *(struct sockaddr_in *)nsap); + close(s); + continue; + } + pfd[j].fd = s; + pfd[j].events = POLLIN; + j++; + } + /* + * Wait for reply. + */ + seconds = statp->retrans; + if (seconds <= 0) + seconds = 1; + evNowTime(&now); + evConsTime(&timeout, seconds, 0); + evAddTime(&finish, &now, &timeout); + wait: + if (j == 0) { + return (0); + } + + /* Convert struct timespec in milliseconds. */ + ptimeout = timeout.tv_sec * 1000 + timeout.tv_nsec / 1000000; + n = __poll (pfd, j, ptimeout); + if (n == 0) { + Dprint(statp->options & RES_DEBUG, (stdout, ";; timeout\n")); + *gotsomewhere = 1; + for (i = 0; i < j; i++) + close(pfd[i].fd); + return (0); + } + if (n < 0) { + if (errno == EINTR) { + evNowTime(&now); + if (evCmpTime(finish, now) > 0) { + evSubTime(&timeout, &finish, &now); + goto wait; + } + } + Perror(statp, stderr, "select", errno); + for (i = 0; i < j; i++) + close(pfd[i].fd); + res_nclose(statp); + return (0); + } + for (i = 0; i < j - 1; i++) + if (pfd[j].revents == POLLIN) + break; + s = pfd[i].fd; + __set_errno (0); + fromlen = sizeof(struct sockaddr_in6); + if (anssiz < MAXPACKET + && anscp + && (ioctl (s, FIONREAD, &resplen) < 0 + || anssiz < resplen)) { + ans = malloc (MAXPACKET); + if (ans == NULL) + ans = *ansp; + else { + anssiz = MAXPACKET; + *anssizp = MAXPACKET; + *ansp = ans; + *anscp = ans; + anhp = (HEADER *) ans; + } + } + iov.iov_base = ans; + iov.iov_len = anssiz; + mhdr.msg_name = 0; + mhdr.msg_namelen = 0; + mhdr.msg_iov = &iov; + mhdr.msg_iovlen = 1; + mhdr.msg_control = cmsgbuf; + mhdr.msg_controllen = sizeof(cmsgbuf); + mhdr.msg_flags = 0; + resplen = recvmsg(s, &mhdr, 0); + if (resplen <= 0) { + if (errno == EAGAIN) + goto wait; + Perror(statp, stderr, "recvfrom", errno); +wait2: + close(s); + if (i < j) + memmove(pfd + i, pfd + i + 1, sizeof(*pfd) * (j - i)); + j--; + goto wait; + } + cmsg = CMSG_FIRSTHDR(&mhdr); + for (cmsg = CMSG_FIRSTHDR(&mhdr); cmsg; CMSG_NXTHDR(&mhdr, cmsg)) + if (cmsg->cmsg_level == SOL_IP && cmsg->cmsg_type == IP_TTL) + break; + if (!cmsg) { + Dprint(statp->options & RES_DEBUG, + (stdout, ";; no TTL found\n")); + goto wait2; + } + ttl = *(int *)CMSG_DATA(cmsg); + if (ttl != 255) { + Dprint(statp->options & RES_DEBUG, + (stdout, ";; answer with bad TTL: %d \n", ttl)); + goto wait; + } + *gotsomewhere = 1; + if (resplen < HFIXEDSZ) { + /* + * Undersized message. + */ + Dprint(statp->options & RES_DEBUG, + (stdout, ";; undersized: %d\n", + resplen)); + *terrno = EMSGSIZE; + goto wait; + } + if (hp->id != anhp->id) { + /* + * response from old query, ignore it. + * XXX - potential security hazard could + * be detected here. + */ + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, ";; old answer:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + goto wait; + } + if (!(statp->options & RES_INSECURE2) && + !res_queriesmatch(buf, buf + buflen, + ans, ans + anssiz)) { + /* + * response contains wrong query? ignore it. + * XXX - potential security hazard could + * be detected here. + */ + DprintQ((statp->options & RES_DEBUG) || + (statp->pfcode & RES_PRF_REPLY), + (stdout, ";; wrong query name:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + goto wait; + } + if (anhp->rcode == SERVFAIL || + anhp->rcode == NOTIMP || + anhp->rcode == REFUSED) { + DprintQ(statp->options & RES_DEBUG, + (stdout, "server rejected query:\n"), + ans, (resplen > anssiz) ? anssiz : resplen); + goto wait; + } + for (i = 0; i < j; i++) + close(pfd[i].fd); +#if 0 + if (!(statp->options & RES_IGNTC) && anhp->tc) { + /* + * To get the rest of answer, + * use TCP with same server. + */ + Dprint(statp->options & RES_DEBUG, + (stdout, ";; truncated answer\n")); + *v_circuit = 1; + res_nclose(statp); + return (1); + } +#endif + /* + * All is well, or the error is fatal. Signal that the + * next nameserver ought not be tried. + */ + return (resplen); +} + #ifdef DEBUG static void Aerror(const res_state statp, FILE *file, const char *string, int error,