Goto sanos source index
//
// 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.
//
#include <os.h>
#include <string.h>
#include <inifile.h>
#include "resolv.h"
int sprintf(char *buf, const char *fmt, ...);
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);
//
// mklower
//
static __inline int mklower(int ch) {
if (ch >= 0x41 && ch <= 0x5A) return ch + 0x20;
return ch;
}
//
// 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 (mklower(*dn++) != mklower(*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() {
struct timeval now;
gettimeofday(&now, NULL);
return (now.tv_sec ^ now.tv_usec ^ gettid()) & 0xFFFF;
}
//
// res_init
//
int res_init() {
struct peb *peb = getpeb();
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;
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"));
}
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;
}
// 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;
}