From 405c629d5257456588879db59f20e04b900b838a Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Sat, 3 Jun 2017 23:41:38 +0100 Subject: [PATCH] resolv: add Ringaard resolver We can't yet build it and it shows up some gaps in our headers too such as the fact we need to provide a send() and recv() wrapper. --- Library/libs/resolv.c | 1341 +++++++++++++++++++++++++++++++++++++++++ Library/libs/resolv.h | 211 +++++++ 2 files changed, 1552 insertions(+) create mode 100644 Library/libs/resolv.c create mode 100644 Library/libs/resolv.h diff --git a/Library/libs/resolv.c b/Library/libs/resolv.c new file mode 100644 index 00000000..99b149de --- /dev/null +++ b/Library/libs/resolv.c @@ -0,0 +1,1341 @@ +// +// resolv.c +// +// DNS resolver +// +// Copyright (C) 2002 Michael Ringgaard. All rights reserved. +// Portions Copyright (C) 1996-2002 Internet Software Consortium. +// Portions Copyright (C) 1996-2001 Nominum, Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// + +#define _BSD_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "resolv.h" + +/* FIXME */ +#define EMSGSIZE EINVAL + + + +struct res_state res; + +static int res_nsend(struct res_state *statp, const char *buf, int buflen, + char *answer, int anslen); + +static int res_nquery(struct res_state *statp, const char *dname, + int class, int type, unsigned char *answer, + int anslen); + +static int res_nsearch(struct res_state *statp, const char *name, + int class, int type, unsigned char *answer, + int anslen); + +static int res_nquerydomain(struct res_state *statp, const char *name, + const char *domain, int class, int type, + unsigned char *answer, int anslen); + + +static int res_nmkquery(struct res_state *statp, int op, const char *dname, + int class, int type, + const unsigned char *data, int datalen, + unsigned char *newrr, + unsigned char *buf, int buflen); + +// +// special +// + +static int special(int ch) +{ + switch (ch) { + case 0x22: // '"' + case 0x2E: // '.' + case 0x3B: // ';' + case 0x5C: // '\\' + // Special modifiers in zone files + case 0x40: // '@' + case 0x24: // '$' + return 1; + default: + return 0; + } +} + +// +// printable +// + +static int printable(int ch) +{ + return (ch > 0x20 && ch < 0x7f); +} + +// +// dn_find +// +// Search for the counted-label name in an array of compressed names. +// Returns offset from msg if found, or error. +// +// dnptrs is the pointer to the first name on the list, +// not the pointer to the start of the message. +// + +static int dn_find(unsigned char *domain, unsigned char *msg, + unsigned char **dnptrs, unsigned char **lastdnptr) +{ + unsigned char *dn, *cp, *sp; + unsigned char **cpp; + unsigned int n; + + for (cpp = dnptrs; cpp < lastdnptr; cpp++) { + sp = *cpp; + + // Terminate search on: + // - root label + // - compression pointer + // - unusable offset + while (*sp != 0 && (*sp & NS_CMPRSFLGS) == 0 + && (sp - msg) < 0x4000) { + dn = domain; + cp = sp; + while ((n = *cp++) != 0) { + // Check for indirection + switch (n & NS_CMPRSFLGS) { + case 0: // normal case, n == len + if (n != *dn++) + goto next; + for (; n > 0; n--) + if (tolower(*dn++) != + tolower(*cp++)) + goto next; + + // Is next root for both ? + if (*dn == '\0' && *cp == '\0') + return (sp - msg); + if (*dn) + continue; + goto next; + + case NS_CMPRSFLGS: // indirection + cp = msg + + (((n & 0x3f) << 8) | *cp); + break; + + default: // illegal type + errno = EMSGSIZE; + return -1; + } + } + next: + sp += *sp + 1; + } + } + + errno = ENOENT; + return -1; +} + +// +// ns_name_ntop +// +// Convert an encoded domain name to printable ascii as per RFC1035. +// Returns number of bytes written to buffer, or error. +// The root is returned as "." +// All other domains are returned in non absolute form +// + +int ns_name_ntop(const unsigned char *src, char *dst, int dstsiz) +{ + const unsigned char *cp; + unsigned char *dn, *eom; + unsigned char c; + unsigned int n; + + cp = src; + dn = dst; + eom = dst + dstsiz; + + while ((n = *cp++) != 0) { + if ((n & NS_CMPRSFLGS) != 0) { + errno = EMSGSIZE; + return -1; + } + + if (dn != dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return -1; + } + *dn++ = '.'; + } + + if (dn + n >= eom) { + errno = EMSGSIZE; + return -1; + } + for (; n > 0; n--) { + c = *cp++; + if (special(c)) { + if (dn + 1 >= eom) { + errno = EMSGSIZE; + return -1; + } + *dn++ = '\\'; + *dn++ = (char) c; + } else if (!printable(c)) { + if (dn + 3 >= eom) { + errno = EMSGSIZE; + return -1; + } + *dn++ = '\\'; + *dn++ = c / 100 + '0'; + *dn++ = (c % 100) / 10 + '0'; + *dn++ = c % 10 + '0'; + } else { + if (dn >= eom) { + errno = EMSGSIZE; + return -1; + } + *dn++ = (char) c; + } + } + } + + if (dn == dst) { + if (dn >= eom) { + errno = EMSGSIZE; + return -1; + } + *dn++ = '.'; + } + + if (dn >= eom) { + errno = EMSGSIZE; + return -1; + } + *dn++ = '\0'; + + return dn - dst; +} + +// +// ns_name_pton +// +// Convert a ascii string into an encoded domain name as per RFC1035. +// +// Return: +// <0 if it fails +// 1 if string was fully qualified +// 0 if string was not fully qualified +// +// Enforces label and domain length limits. +// + +int ns_name_pton(const char *src, unsigned char *dst, int dstsiz) +{ + unsigned char *label, *bp, *eom; + int c, n, escaped; + + escaped = 0; + bp = dst; + eom = dst + dstsiz; + label = bp++; + + while ((c = *src++) != 0) { + if (escaped) { + if (c >= '0' && c <= '9') { + n = (c - '0') * 100; + if ((c = *src++) == 0 || c < '0' + || c > '9') { + errno = EINVAL; + return -1; + } + n += (c - '0') * 10; + if ((c = *src++) == 0 || c < '0' + || c > '9') { + errno = EINVAL; + return -1; + } + n += (c - '0'); + if (n > 255) { + errno = EINVAL; + return -1; + } + c = n; + } + escaped = 0; + } else if (c == '\\') { + escaped = 1; + continue; + } else if (c == '.') { + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { + errno = EMSGSIZE; + return -1; + } + if (label >= eom) { + errno = EMSGSIZE; + return -1; + } + *label = c; + + // Fully qualified ? + if (*src == '\0') { + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return -1; + } + *bp++ = '\0'; + } + if ((bp - dst) > NS_MAXCDNAME) { + errno = EMSGSIZE; + return -1; + } + return 1; + } + + if (c == 0 || *src == '.') { + errno = EMSGSIZE; + return -1; + } + label = bp++; + continue; + } + + if (bp >= eom) { + errno = EMSGSIZE; + return -1; + } + *bp++ = (unsigned char) c; + } + + c = (bp - label - 1); + if ((c & NS_CMPRSFLGS) != 0) { + errno = EMSGSIZE; + return -1; + } + if (label >= eom) { + errno = EMSGSIZE; + return -1; + } + *label = c; + if (c != 0) { + if (bp >= eom) { + errno = EMSGSIZE; + return -1; + } + *bp++ = 0; + } + if ((bp - dst) > NS_MAXCDNAME) { + errno = EMSGSIZE; + return -1; + } + + return 0; +} + +// +// ns_name_pack +// +// Pack domain name 'src' into 'dst'. +// Returns size of the compressed name, or error. +// +// dnptrs' is an array of pointers to previous compressed names. +// dnptrs[0] is a pointer to the beginning of the message. The array +// ends with NULL. +// 'lastdnptr' is a pointer to the end of the array pointed to +// by 'dnptrs'. +// +// The list of pointers in dnptrs is updated for labels inserted into +// the message as we compress the name. If 'dnptr' is NULL, we don't +// try to compress names. If 'lastdnptr' is NULL, we don't update the +// list. +// + +static int ns_name_pack(const unsigned char *src, unsigned char *dst, + int dstsiz, unsigned char **dnptrs, + unsigned char **lastdnptr) +{ + unsigned char *dstp; + unsigned char **cpp, **lpp, *eob, *msg; + unsigned char *srcp; + int n, l, first = 1; + + srcp = (unsigned char *) src; + dstp = dst; + eob = dstp + dstsiz; + lpp = cpp = NULL; + if (dnptrs != NULL) { + if ((msg = *dnptrs++) != NULL) { + for (cpp = dnptrs; *cpp != NULL; cpp++); + lpp = cpp; + } + } else { + msg = NULL; + } + + // Make sure the domain we are about to add is legal + l = 0; + do { + n = *srcp; + if ((n & NS_CMPRSFLGS) != 0) { + errno = EMSGSIZE; + return -1; + } + l += n + 1; + if (l > NS_MAXCDNAME) { + errno = EMSGSIZE; + return -1; + } + srcp += n + 1; + } while (n != 0); + + // From here on we need to reset compression pointer array on error + srcp = (unsigned char *) src; + do { + // Look to see if we can use pointers + n = *srcp; + if (n != 0 && msg != NULL) { + l = dn_find(srcp, msg, dnptrs, lpp); + if (l >= 0) { + if (dstp + 1 >= eob) + goto cleanup; + *dstp++ = (l >> 8) | NS_CMPRSFLGS; + *dstp++ = l % 256; + return dstp - dst; + } + // Not found, save it + if (lastdnptr != NULL && cpp < lastdnptr - 1 + && (dstp - msg) < 0x4000 && first) { + *cpp++ = dstp; + *cpp = NULL; + first = 0; + } + } + // Copy label to buffer + if (n & NS_CMPRSFLGS) + goto cleanup; + if (dstp + 1 + n >= eob) + goto cleanup; + memcpy(dstp, srcp, n + 1); + srcp += n + 1; + dstp += n + 1; + } while (n != 0); + + if (dstp > eob) { + cleanup: + if (msg != NULL) + *lpp = NULL; + errno = EMSGSIZE; + return -1; + } + + return dstp - dst; +} + +// +// ns_name_unpack +// +// Unpack a domain name from a message, source may be compressed. +// Return error if it fails, or consumed octets if it succeeds. +// + +static int ns_name_unpack(const unsigned char *msg, + const unsigned char *eom, + const unsigned char *src, unsigned char *dst, + int dstsiz) +{ + const unsigned char *srcp, *dstlim; + unsigned char *dstp; + int n, len, checked; + + len = -1; + checked = 0; + dstp = dst; + srcp = src; + dstlim = dst + dstsiz; + if (srcp < msg || srcp >= eom) { + errno = EMSGSIZE; + return -1; + } + // Fetch next label in domain name + while ((n = *srcp++) != 0) { + // Check for indirection + switch (n & NS_CMPRSFLGS) { + case 0: + // Limit checks + if (dstp + n + 1 >= dstlim || srcp + n >= eom) { + errno = EMSGSIZE; + return -1; + } + checked += n + 1; + *dstp++ = n; + memcpy(dstp, srcp, n); + dstp += n; + srcp += n; + break; + + case NS_CMPRSFLGS: + if (srcp >= eom) { + errno = EMSGSIZE; + return -1; + } + if (len < 0) + len = srcp - src + 1; + srcp = msg + (((n & 0x3F) << 8) | (*srcp & 0xFF)); + if (srcp < msg || srcp >= eom) { + errno = EMSGSIZE; + return -1; + } + checked += 2; + + // Check for loops in the compressed name; + // if we've looked at the whole message, there must be a loop. + if (checked >= eom - msg) { + errno = EMSGSIZE; + return -1; + } + break; + + default: + errno = EMSGSIZE; + return -1; + } + } + + *dstp = '\0'; + if (len < 0) + len = srcp - src; + return len; +} + +// +// res_randomid +// + +static unsigned int res_randomid(void) +{ + struct timeval now; + + gettimeofday(&now, NULL); + return (now.tv_sec ^ now.tv_usec ^ getpid()) & 0xFFFF; +} + +// +// res_init +// + +int res_init(void) +{ + char *addr; + + memset(&res, 0, sizeof(struct res_state)); + res.options = RES_DEFAULT; + res.retry = RES_DFLRETRY; + res.retrans = RES_TIMEOUT; + res.id = res_randomid(); + res.ndots = 1; + res.nscount = 0; + + /* FIXME: parse file */ +#if 0 + if (peb->primary_dns.s_addr != INADDR_ANY) { + res.nsaddr_list[res.nscount].sin_addr.s_addr = + peb->primary_dns.s_addr; + res.nsaddr_list[res.nscount].sin_family = AF_INET; + res.nsaddr_list[res.nscount].sin_port = + htons(NS_DEFAULTPORT); + res.nscount++; + } + + if (peb->secondary_dns.s_addr != INADDR_ANY) { + res.nsaddr_list[res.nscount].sin_addr.s_addr = + peb->secondary_dns.s_addr; + res.nsaddr_list[res.nscount].sin_family = AF_INET; + res.nsaddr_list[res.nscount].sin_port = + htons(NS_DEFAULTPORT); + res.nscount++; + } + + addr = get_property(osconfig(), "dns", "primary", NULL); + if (addr != NULL) { + res.nsaddr_list[res.nscount].sin_addr.s_addr = + inet_addr(addr); + res.nsaddr_list[res.nscount].sin_family = AF_INET; + res.nsaddr_list[res.nscount].sin_port = + htons(NS_DEFAULTPORT); + res.nscount++; + } + + addr = get_property(osconfig(), "dns", "secondary", NULL); + if (addr != NULL) { + res.nsaddr_list[res.nscount].sin_addr.s_addr = + inet_addr(addr); + res.nsaddr_list[res.nscount].sin_family = AF_INET; + res.nsaddr_list[res.nscount].sin_port = + htons(NS_DEFAULTPORT); + if (res.nsaddr_list[res.nscount].sin_addr.s_addr != + INADDR_NONE) + res.nscount++; + } + + if (res.nscount == 0) { + res.nsaddr_list[res.nscount].sin_addr.s_addr = + INADDR_LOOPBACK; + res.nsaddr_list[res.nscount].sin_family = AF_INET; + res.nsaddr_list[res.nscount].sin_port = + htons(NS_DEFAULTPORT); + if (res.nsaddr_list[res.nscount].sin_addr.s_addr != + INADDR_NONE) + res.nscount++; + } + + strcpy(res.defdname, peb->default_domain); + if (!*res.defdname) { + strcpy(res.defdname, + get_property(osconfig(), "dns", "domain", + "local.domain")); + } +#endif + + res.dnsrch[0] = res.defdname; + return 0; +} + +// +// send_vc +// + +static int send_vc(struct res_state *statp, + const unsigned char *buf, int buflen, + unsigned char *answer, int anslen, int *terrno, int ns) +{ + const struct dns_hdr *hp = (const struct dns_hdr *) buf; + struct dns_hdr *anhp = (struct dns_hdr *) answer; + struct sockaddr_in *nsap = &statp->nsaddr_list[ns]; + int resplen, n; + unsigned short len; + unsigned char *cp; + struct iovec iov[2]; + int s; + int rc; + + // Connect to name server + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + *terrno = s; + return -1; + } + + rc = connect(s, (struct sockaddr *) nsap, sizeof *nsap); + if (rc < 0) { + *terrno = rc; + close(s); + return 0; + } + + /* FIXME: we don't have writev() */ + // Send length & message + len = htons((unsigned short) buflen); + iov[0].iov_base = &len; + iov[0].iov_len = sizeof(unsigned short); + iov[1].iov_base = (char *) buf; + iov[1].iov_len = buflen; + + rc = writev(s, iov, 2); + if (rc != sizeof(unsigned short) + buflen) { + *terrno = rc; + close(s); + return 0; + } + // Receive length & response + cp = answer; + len = sizeof(unsigned short); + while ((n = recv(s, cp, len, 0)) > 0) { + cp += n; + if ((len -= n) <= 0) + break; + } + + if (n <= 0) { + *terrno = n; + close(s); + return 0; + } + + resplen = ntohs(*(unsigned short *) answer); + if (resplen > anslen) { + len = anslen; + } else { + len = resplen; + } + + if (len < NS_HFIXEDSZ) { + // Undersized message + *terrno = -EMSGSIZE; + close(s); + return 0; + } + + cp = answer; + while (len != 0 && (n = recv(s, cp, len, 0)) > 0) { + cp += n; + len -= n; + } + + if (n <= 0) { + *terrno = n; + close(s); + return 0; + } + // All is well, or the error is fatal. + // Signal that the next nameserver ought not be tried. + + close(s); + return resplen; +} + +// +// send_dg +// + +static int send_dg(struct res_state *statp, + const unsigned char *buf, int buflen, + unsigned char *answer, int anslen, + int *terrno, int ns, int *v_circuit, int *gotsomewhere) +{ + const struct dns_hdr *hp = (const struct dns_hdr *) buf; + struct dns_hdr *anhp = (struct dns_hdr *) answer; + const struct sockaddr_in *nsap = &statp->nsaddr_list[ns]; + struct sockaddr_in from; + //struct sockaddr_in local; + int fromlen, resplen, timeout, s; + + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + *terrno = s; + return -1; + } + //local.sin_family = AF_INET; + //local.sin_port = htons(1024); + //local.sin_addr.s_addr = INADDR_ANY; + + if (connect(s, (struct sockaddr *) nsap, sizeof *nsap) < 0) { + close(s); + return 0; + } + + if (send(s, (char *) buf, buflen, 0) != buflen) { + close(s); + return 0; + } + // Wait for reply. + timeout = (statp->retrans << ns); + if (ns > 0) + timeout /= statp->nscount; + if (timeout <= 0) + timeout = 1; + timeout = timeout * 1000; + setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(int)); + + wait: + fromlen = sizeof(struct sockaddr_in); + resplen = + recvfrom(s, (char *) answer, anslen, 0, + (struct sockaddr *) &from, &fromlen); + + if (resplen == 0) { + *gotsomewhere = 1; + close(s); + return 0; + } + + if (resplen < 0) { + if (errno == ETIMEOUT) { + *gotsomewhere = 1; + close(s); + return 0; + } + + close(s); + return 0; + } + + *gotsomewhere = 1; + + if (resplen < NS_HFIXEDSZ) { + // Undersized message + *terrno = -EMSGSIZE; + close(s); + return 0; + } + + if (hp->id != anhp->id) { + // Response from old query, ignore it. + goto wait; + } + + if (!(statp->options & RES_IGNTC) && anhp->tc) { + // To get the rest of answer, TCP with same server. + *v_circuit = 1; + close(s); + return 1; + } + // All is well, or the error is fatal. + // Signal that the next nameserver ought not be tried. + close(s); + return resplen; +} + +// +// res_nsend +// + +static int res_nsend(struct res_state *statp, const char *buf, int buflen, + char *answer, int anslen) +{ + int gotsomewhere, try, v_circuit, resplen, ns, n, terrno; + + if (statp->nscount == 0) { + errno = ESRCH; + return -1; + } + if (anslen < NS_HFIXEDSZ) { + errno = EINVAL; + return -1; + } + + v_circuit = (statp->options & RES_USEVC) || buflen > NS_PACKETSZ; + gotsomewhere = 0; + terrno = -ETIMEOUT; + + // FIXME: add dns cache lookup here + + // Some resolvers want to even out the load on their nameservers. + if ((statp->options & RES_ROTATE) != 0) { + struct sockaddr_in ina; + int lastns = statp->nscount - 1; + + ina = statp->nsaddr_list[0]; + for (ns = 0; ns < lastns; ns++) { + statp->nsaddr_list[ns] = + statp->nsaddr_list[ns + 1]; + } + statp->nsaddr_list[lastns] = ina; + } + // Send request, RETRY times, or until successful. + for (try = 0; try < statp->retry; try++) { + for (ns = 0; ns < statp->nscount; ns++) { + struct sockaddr_in *nsap = &statp->nsaddr_list[ns]; + + same_ns: + if (v_circuit) { + // Use VC; at most one attempt per server. + try = statp->retry; + n = send_vc(statp, buf, buflen, answer, + anslen, &terrno, ns); + if (n < 0) { + errno = -terrno; + return -1; + } + if (n == 0) + break; + resplen = n; + } else { + // Use datagrams. + n = send_dg(statp, buf, buflen, answer, + anslen, &terrno, ns, + &v_circuit, &gotsomewhere); + if (n < 0) { + errno = -terrno; + return -1; + } + if (n == 0) + break; + if (v_circuit) + goto same_ns; + resplen = n; + } + + return resplen; + } + } + + if (!v_circuit) { + if (!gotsomewhere) { + errno = ECONNREFUSED; // No nameservers found + return -1; + } else { + errno = ETIMEOUT; // No answer obtained + return -1; + } + } else { + errno = -terrno; + return -1; + } +} + +// +// res_send +// + +int res_send(const char *buf, int buflen, char *answer, int anslen) +{ + return res_nsend(&res, buf, buflen, answer, anslen); +} + +// +// res_nquery +// + +static int res_nquery(struct res_state *statp, const char *dname, + int class, int type, unsigned char *answer, + int anslen) +{ + unsigned char buf[QUERYBUF_SIZE]; + struct dns_hdr *hp = (struct dns_hdr *) answer; + int n; + + hp->rcode = 0; + + n = res_nmkquery(statp, DNS_OP_QUERY, dname, class, type, NULL, 0, + NULL, buf, sizeof(buf)); + if (n <= 0) + return n; + + n = res_nsend(statp, buf, n, answer, anslen); + if (n < 0) + return n; + + if (hp->rcode != 0 || ntohs(hp->ancount) == 0) { + switch (hp->rcode) { + case DNS_ERR_NXDOMAIN: + errno = EHOSTNOTFOUND; + return -1; + + case DNS_ERR_SERVFAIL: + errno = ETRYAGAIN; + return -1; + + case DNS_ERR_NOERROR: + errno = ENODATA; + return -1; + + case DNS_ERR_FORMERR: + case DNS_ERR_NOTIMPL: + case DNS_ERR_REFUSED: + default: + errno = ENORECOVERY; + return -1; + } + } + + return n; +} + +// +// res_query +// + +int res_query(const char *dname, int class, int type, + unsigned char *answer, int anslen) +{ + return res_nquery(&res, dname, class, type, answer, anslen); +} + +// +// res_nsearch +// + +static int res_nsearch(struct res_state *statp, const char *name, + int class, int type, unsigned char *answer, + int anslen) +{ + const char *cp; + char **domain; + struct dns_hdr *hp = (struct dns_hdr *) answer; + int dots; + int trailing_dot, rc, saved_rc; + int got_nodata = 0, got_servfail = 0, root_on_list = 0; + int tried_as_is = 0; + + dots = 0; + for (cp = name; *cp != '\0'; cp++) + dots += (*cp == '.'); + trailing_dot = 0; + if (cp > name && *--cp == '.') + trailing_dot++; + + // If there are enough dots in the name, let's just give it a + // try 'as is'. The threshold can be set with the "ndots" option. + // Also, query 'as is', if there is a trailing dot in the name. + + saved_rc = 0; + if (dots >= statp->ndots || trailing_dot) { + rc = res_nquerydomain(statp, name, NULL, class, type, + answer, anslen); + if (rc > 0 || trailing_dot) + return rc; + saved_rc = rc; + tried_as_is++; + } + // We do at least one level of search if + // - there is no dot and RES_DEFNAME is set, or + // - there is at least one dot, there is no trailing dot, + // and RES_DNSRCH is set. + + if ((!dots && (statp->options & RES_DEFNAMES) != 0) || + (dots && !trailing_dot + && (statp->options & RES_DNSRCH) != 0)) { + int done = 0; + + for (domain = statp->dnsrch; *domain && !done; domain++) { + + if (domain[0][0] == '\0' + || (domain[0][0] == '.' + && domain[0][1] == '\0')) { + root_on_list++; + } + + rc = res_nquerydomain(statp, name, + (const char *) *domain, + class, type, answer, anslen); + if (rc > 0) + return rc; + + if (errno == ECONNREFUSED) + return -1; + + switch (errno) { + case ETIMEOUT: + got_nodata++; + // FALLTHROUGH + + case EHOSTUNREACH: + case ENETUNREACH: + // Keep trying + break; + + case EAGAIN: + if (hp->rcode == DNS_ERR_SERVFAIL) { + // Try next search element, if any + got_servfail++; + break; + } + // FALLTHROUGH + + default: + // Anything else implies that we're done + done++; + } + + // If we got here for some reason other than DNSRCH, + // we only wanted one iteration of the loop, so stop. + if ((statp->options & RES_DNSRCH) == 0) + done++; + } + } + // If the name has any dots at all, and no earlier 'as-is' query + // for the name, and "." is not on the search list, then try an as-is + // query now. + if (statp->ndots && !(tried_as_is || root_on_list)) { + rc = res_nquerydomain(statp, name, NULL, class, type, + answer, anslen); + if (rc > 0) + return rc; + } + // If we got here, we didn't satisfy the search. + // If we did an initial full query, return that query's return code + // (note that we wouldn't be here if that query had succeeded). + // else if we ever got a nodata, send that back as the reason. + // else send back meaningless error code, that being the one from + // the last DNSRCH we did. + if (saved_rc != 0) { + return saved_rc; + } else if (got_nodata) { + errno = ETIMEOUT; + return -1; + } else if (got_servfail) { + errno = EAGAIN; + return -1; + } else { + errno = EIO; + return -1; // ??? + } +} + +// +// res_search +// + +int res_search(const char *name, int class, int type, + unsigned char *answer, int anslen) +{ + return res_nsearch(&res, name, class, type, answer, anslen); +} + +// +// res_nquerydomain +// +// Perform a call on res_query on the concatenation of name and domain, +// removing a trailing dot from name if domain is NULL. +// + +static int res_nquerydomain(struct res_state *statp, const char *name, + const char *domain, int class, int type, + unsigned char *answer, int anslen) +{ + char nbuf[NS_MAXDNAME]; + const char *longname = nbuf; + int n, d; + + if (domain == NULL) { + // Check for trailing '.'; copy without '.' if present. + n = strlen(name); + if (n >= NS_MAXDNAME) { + errno = EMSGSIZE; + return -1; + } + + n--; + if (n >= 0 && name[n] == '.') { + strncpy(nbuf, name, n); + nbuf[n] = '\0'; + } else { + longname = name; + } + } else { + n = strlen(name); + d = strlen(domain); + if (n + d + 1 >= NS_MAXDNAME) { + errno = EMSGSIZE; + return -1; + } + sprintf(nbuf, "%s.%s", name, domain); + } + + return res_nquery(statp, longname, class, type, answer, anslen); +} + +// +// res_querydomain +// + +int res_querydomain(const char *name, const char *domain, int class, + int type, unsigned char *answer, int anslen) +{ + return res_nquerydomain(&res, name, domain, class, type, answer, + anslen); +} + +// +// res_nmkquery +// + +static int res_nmkquery(struct res_state *statp, int op, const char *dname, + int class, int type, + const unsigned char *data, int datalen, + unsigned char *newrr, + unsigned char *buf, int buflen) +{ + struct dns_hdr *hp; + unsigned char *cp; + int n; + unsigned char *dnptrs[20], **dpp, **lastdnptr; + + // Initialize header fields. + if (buf == NULL || buflen < NS_HFIXEDSZ) { + errno = EINVAL; + return -1; + } + + memset(buf, 0, NS_HFIXEDSZ); + hp = (struct dns_hdr *) buf; + hp->id = htons(++statp->id); + hp->opcode = op; + hp->rd = (statp->options & RES_RECURSE) != 0; + hp->rcode = 0; + cp = ((unsigned char *) buf) + NS_HFIXEDSZ; + buflen -= NS_HFIXEDSZ; + dpp = dnptrs; + *dpp++ = (unsigned char *) buf; + *dpp++ = NULL; + lastdnptr = dnptrs + sizeof dnptrs / sizeof dnptrs[0]; + + // Perform opcode specific processing + switch (op) { + case DNS_OP_QUERY: + case DNS_OP_NOTIFY: + if ((buflen -= NS_QFIXEDSZ) < 0) { + errno = EMSGSIZE; + return -1; + } + + if ((n = + dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0) { + errno = EMSGSIZE; + return -1; + } + + cp += n; + buflen -= n; + + *(unsigned short *) cp = htons((unsigned short) type); + cp += sizeof(unsigned short); + + *(unsigned short *) cp = htons((unsigned short) class); + cp += sizeof(unsigned short); + + hp->qdcount = htons(1); + if (op == DNS_OP_QUERY || data == NULL) + break; + + // Make an additional record for completion domain + buflen -= NS_RRFIXEDSZ; + n = dn_comp((const char *) data, cp, buflen, dnptrs, + lastdnptr); + if (n < 0) { + errno = EMSGSIZE; + return -1; + } + cp += n; + buflen -= n; + + *(unsigned short *) cp = htons(DNS_TYPE_NULL); + cp += sizeof(unsigned short); + + *(unsigned short *) cp = htons((unsigned short) class); + cp += sizeof(unsigned short); + + *(unsigned long *) cp = 0; + cp += sizeof(unsigned long); + + *(unsigned short *) cp = 0; + cp += sizeof(unsigned short); + + hp->arcount = htons(1); + break; + + case DNS_OP_IQUERY: + // Initialize answer section + if (buflen < 1 + NS_RRFIXEDSZ + datalen) { + errno = EMSGSIZE; + return -1; + } + *cp++ = '\0'; // No domain name + + *(unsigned short *) cp = htons((unsigned short) type); + cp += sizeof(unsigned short); + + *(unsigned short *) cp = htons((unsigned short) class); + cp += sizeof(unsigned short); + + *(unsigned long *) cp = 0; + cp += sizeof(unsigned long); + + *(unsigned short *) cp = htons((unsigned short) datalen); + cp += sizeof(unsigned short); + + if (datalen) { + memcpy(cp, data, datalen); + cp += datalen; + } + + hp->ancount = htons(1); + break; + + default: + errno = ENOSYS; + return -1; + } + + return cp - buf; +} + +// +// res_mkquery +// + +int res_mkquery(int op, const char *dname, int class, int type, char *data, + int datalen, unsigned char *newrr, char *buf, int buflen) +{ + return res_nmkquery(&res, op, dname, class, type, data, datalen, + newrr, buf, buflen); +} + +// +// dn_comp +// + +int dn_comp(const char *src, unsigned char *dst, int dstsiz, + unsigned char **dnptrs, unsigned char **lastdnptr) +{ + unsigned char tmp[NS_MAXCDNAME]; + int rc; + + rc = ns_name_pton(src, tmp, sizeof tmp); + if (rc < 0) + return rc; + + return ns_name_pack(tmp, dst, dstsiz, dnptrs, lastdnptr); +} + +// +// dn_expand +// + +int dn_expand(const unsigned char *msg, const unsigned char *eom, + const unsigned char *src, char *dst, int dstsiz) +{ + unsigned char tmp[NS_MAXCDNAME]; + int n; + int rc; + + n = ns_name_unpack(msg, eom, src, tmp, sizeof tmp); + if (n < 0) + return n; + + rc = ns_name_ntop(tmp, dst, dstsiz); + if (rc < 0) + return rc; + + if (n > 0 && dst[0] == '.') + dst[0] = '\0'; + return n; +} diff --git a/Library/libs/resolv.h b/Library/libs/resolv.h new file mode 100644 index 00000000..c8d25559 --- /dev/null +++ b/Library/libs/resolv.h @@ -0,0 +1,211 @@ +// +// resolv.h +// +// DNS resolver +// +// Copyright (C) 2002 Michael Ringgaard. All rights reserved. +// Portions Copyright (C) 1996-2002 Internet Software Consortium. +// Portions Copyright (C) 1996-2001 Nominum, Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// 3. Neither the name of the project nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +// OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +// SUCH DAMAGE. +// + +#ifndef RESOLV_H +#define RESOLV_H + +#define NS_PACKETSZ 512 // Maximum packet size +#define NS_MAXCDNAME 255 // Maximum compressed domain name +#define NS_MAXDNAME 1025 // Maximum domain name +#define NS_CMPRSFLGS 0xc0 // Flag bits indicating name compression +#define NS_HFIXEDSZ 12 // #/bytes of fixed data in header +#define NS_QFIXEDSZ 4 // #/bytes of fixed data in query +#define NS_RRFIXEDSZ 10 // #/bytes of fixed data in r record +#define NS_DEFAULTPORT 53 // For both TCP and UDP + +#define MAXHOSTNAMELEN 256 + +#define QUERYBUF_SIZE 1024 // Size of query buffer + +// +// DNS opcodes +// + +#define DNS_OP_QUERY 0 // Standard query +#define DNS_OP_IQUERY 1 // Inverse query (deprecated/unsupported) +#define DNS_OP_STATUS 2 // Name server status query (unsupported) +#define DNS_OP_NOTIFY 4 // Zone change notification +#define DNS_OP_UPDATE 5 // Zone update message + +// +// DNS response codes +// + +#define DNS_ERR_NOERROR 0 // No error occurred +#define DNS_ERR_FORMERR 1 // Format error +#define DNS_ERR_SERVFAIL 2 // Server failure +#define DNS_ERR_NXDOMAIN 3 // Name error +#define DNS_ERR_NOTIMPL 4 // Unimplemented +#define DNS_ERR_REFUSED 5 // Operation refused +#define DNS_ERR_YXDOMAIN 6 // Name exists +#define DNS_ERR_YXRRSET 7 // RRset exists +#define DNS_ERR_NXRRSET 8 // RRset does not exist +#define DNS_ERR_NOTAUTH 9 // Not authoritative for zone +#define DNS_ERR_NOTZONE 10 // Zone of record different from zone section + +// +// DNS resource record types +// + +#define DNS_TYPE_INVALID 0 // Cookie +#define DNS_TYPE_A 1 // Host address +#define DNS_TYPE_NS 2 // Authoritative server +#define DNS_TYPE_MD 3 // Mail destination +#define DNS_TYPE_MF 4 // Mail forwarder +#define DNS_TYPE_CNAME 5 // Canonical name +#define DNS_TYPE_SOA 6 // Start of authority zone +#define DNS_TYPE_MB 7 // Mailbox domain name +#define DNS_TYPE_MG 8 // Mail group member +#define DNS_TYPE_MR 9 // Mail rename name +#define DNS_TYPE_NULL 10 // Null resource record +#define DNS_TYPE_WKS 11 // Well known service +#define DNS_TYPE_PTR 12 // Domain name pointer +#define DNS_TYPE_HINFO 13 // Host information +#define DNS_TYPE_MINFO 14 // Mailbox information +#define DNS_TYPE_MX 15 // Mail routing information +#define DNS_TYPE_TXT 16 // Text strings +#define DNS_TYPE_RP 17 // Responsible person +#define DNS_TYPE_AFSDB 18 // AFS cell database +#define DNS_TYPE_X25 19 // X_25 calling address +#define DNS_TYPE_ISDN 20 // ISDN calling address +#define DNS_TYPE_RT 21 // Router +#define DNS_TYPE_NSAP 22 // NSAP address +#define DNS_TYPE_NSAP_PTR 23 // Reverse NSAP lookup (deprecated) +#define DNS_TYPE_SIG 24 // Security signature +#define DNS_TYPE_KEY 25 // Security key +#define DNS_TYPE_PX 26 // X.400 mail mapping +#define DNS_TYPE_GPOS 27 // Geographical position (withdrawn) +#define DNS_TYPE_AAAA 28 // Ip6 Address +#define DNS_TYPE_LOC 29 // Location Information +#define DNS_TYPE_NXT 30 // Next domain (security) +#define DNS_TYPE_EID 31 // Endpoint identifier +#define DNS_TYPE_NIMLOC 32 // Nimrod Locator +#define DNS_TYPE_SRV 33 // Server Selection +#define DNS_TYPE_ATMA 34 // ATM Address +#define DNS_TYPE_NAPTR 35 // Naming Authority PoinTeR +#define DNS_TYPE_KX 36 // Key Exchange +#define DNS_TYPE_CERT 37 // Certification record +#define DNS_TYPE_A6 38 // IPv6 address (deprecates AAAA) +#define DNS_TYPE_DNAME 39 // Non-terminal DNAME (for IPv6) +#define DNS_TYPE_SINK 40 // Kitchen sink (experimentatl) +#define DNS_TYPE_OPT 41 // EDNS0 option (meta-RR) + +#define DNS_TYPE_TSIG 250 // Transaction signature +#define DNS_TYPE_IXFR 251 // Incremental zone transfer +#define DNS_TYPE_AXFR 252 // Transfer zone of authority +#define DNS_TYPE_MAILB 253 // Transfer mailbox records +#define DNS_TYPE_MAILA 254 // Transfer mail agent records +#define DNS_TYPE_ANY 255 // Wildcard match + +// +// DNS classes +// + +#define DNS_CLASS_INVALID 0 // Cookie +#define DNS_CLASS_IN 1 // Internet +#define DNS_CLASS_2 2 // Unallocated/unsupported +#define DNS_CLASS_CHAOS 3 // MIT Chaos-net +#define DNS_CLASS_HS 4 // MIT Hesiod + +#define DNS_CLASS_NONE 254 // For prereq. sections in update request +#define DNS_CLASS_ANY 255 // Wildcard match + +// +// DNS message header +// + +/* FIXME: we need to ifdef this for the CPU bitendian packing */ + +struct dns_hdr { + unsigned short id; // Query identification number + + // Fields in third byte + unsigned char rd : 1; // Recursion desired + unsigned char tc : 1; // Truncated message + unsigned char aa : 1; // Authoritive answer + unsigned char opcode : 4; // Purpose of message + unsigned char vqr : 1; // Response flag + + // Fields in fourth byte + unsigned char rcode : 4; // Response code + unsigned char cd: 1; // Checking disabled by resolver + unsigned char ad: 1; // Authentic data from named + unsigned char unused : 1; // Unused bits (MBZ as of 4.9.3a3) + unsigned char ra : 1; // Recursion available + + // Remaining bytes + unsigned short qdcount; // Number of question entries + unsigned short ancount; // Number of answer entries + unsigned short nscount; // Number of authority entries + unsigned short arcount; // Number of resource entries +}; + +// +// DNS resolver state +// + +#define MAXDNSRCH 6 // Max # domains in search path +#define MAXNS 3 // Max # name servers we'll track +#define RES_TIMEOUT 5 // Min. seconds between retries +#define RES_DFLRETRY 2 // Default #/tries + +struct res_state { + unsigned long options; // Option flags - see below + int retry; // Number of times to retransmit + int retrans; // Retransmition time interval + int nscount; // Number of name servers + struct sockaddr_in nsaddr_list[MAXNS]; // Address of name server + unsigned short id; // Current message id + int ndots; // Threshold for initial abs. query + char *dnsrch[MAXDNSRCH + 1]; // Components of domain to search + char defdname[256]; // Default domain +}; + +// +// Resolver options +// + +#define RES_USEVC 0x00000008 // Use virtual circuit +#define RES_IGNTC 0x00000020 // Ignore trucation errors +#define RES_RECURSE 0x00000040 // Recursion desired +#define RES_DEFNAMES 0x00000080 // Use default domain name +#define RES_DNSRCH 0x00000200 // Search up local domain tree +#define RES_ROTATE 0x00004000 // Rotate ns list after each query + +#define RES_DEFAULT (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH) + +int res_init(void); + +#endif -- 2.34.1