switch standard resolver functions to use the new dns backend

this is the third phase of the "resolver overhaul" project.

this commit removes all of the old dns code, and switches the
__lookup_name backend (used by getaddrinfo, etc.) and the getnameinfo
function to use the newly implemented __res_mkquery and __res_msend
interfaces. for parsing the results, a new callback-based __dns_parse
function, based on __dns_get_rr from the old dns code, is used.
This commit is contained in:
Rich Felker 2014-06-02 04:47:45 -04:00
parent a2a328bd89
commit 3330198060
4 changed files with 145 additions and 309 deletions

View File

@ -1,282 +0,0 @@
#include <stdint.h>
#include <netdb.h>
#include <stdio.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <sys/socket.h>
#include <poll.h>
#include <netinet/in.h>
#include <time.h>
#include <ctype.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include "__dns.h"
#include "stdio_impl.h"
#define TIMEOUT 5
#define RETRY 1000
#define PACKET_MAX 512
#define PTR_MAX (64 + sizeof ".in-addr.arpa")
static void cleanup(void *p)
{
close((intptr_t)p);
}
int __dns_doqueries(unsigned char *dest, const char *name, int *rr, int rrcnt)
{
time_t t0 = time(0);
int fd;
FILE *f, _f;
unsigned char _buf[256];
char line[64], *s, *z;
union {
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} sa = {0}, ns[3] = {{0}};
socklen_t sl = sizeof sa.sin;
int nns = 0;
int family = AF_INET;
unsigned char q[280] = "", *r = dest;
int ql;
int rlen;
int got = 0, failed = 0;
int errcode = EAI_AGAIN;
int i, j;
struct timespec ts;
struct pollfd pfd;
int id;
int cs;
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
/* Construct query template - RR and ID will be filled later */
if (strlen(name)-1 >= 254U) return EAI_NONAME;
q[2] = q[5] = 1;
strcpy((char *)q+13, name);
for (i=13; q[i]; i=j+1) {
for (j=i; q[j] && q[j] != '.'; j++);
if (j-i-1u > 62u) return EAI_NONAME;
q[i-1] = j-i;
}
q[i+3] = 1;
ql = i+4;
/* Make a reasonably unpredictable id */
clock_gettime(CLOCK_REALTIME, &ts);
id = ts.tv_nsec + ts.tv_nsec/65536UL & 0xffff;
/* Get nameservers from resolv.conf, fallback to localhost */
f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf);
if (f) for (nns=0; nns<3 && fgets(line, sizeof line, f); ) {
if (strncmp(line, "nameserver", 10) || !isspace(line[10]))
continue;
for (s=line+11; isspace(*s); s++);
for (z=s; *z && !isspace(*z); z++);
*z=0;
if (__ipparse(ns+nns, AF_UNSPEC, s) < 0) continue;
ns[nns].sin.sin_port = htons(53);
if (ns[nns++].sin.sin_family == AF_INET6) {
family = AF_INET6;
sl = sizeof sa.sin6;
}
}
if (f) __fclose_ca(f);
if (!nns) {
ns[0].sin.sin_family = family = AF_INET;
ns[0].sin.sin_port = htons(53);
ns[0].sin.sin_addr.s_addr = htonl(0x7f000001);
nns=1;
sl = sizeof sa.sin;
}
/* Get local address and open/bind a socket */
sa.sin.sin_family = family;
fd = socket(family, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
/* Handle case where system lacks IPv6 support */
if (fd < 0 && errno == EAFNOSUPPORT) {
if (family != AF_INET6) return EAI_SYSTEM;
fd = socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
family = AF_INET;
}
if (fd < 0) return EAI_SYSTEM;
/* Convert any IPv4 addresses in a mixed environment to v4-mapped */
if (family == AF_INET6) {
setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &(int){0}, sizeof 0);
for (i=0; i<nns; i++) {
if (ns[i].sin.sin_family != AF_INET) continue;
memcpy(ns[i].sin6.sin6_addr.s6_addr+12,
&ns[i].sin.sin_addr, 4);
memcpy(ns[i].sin6.sin6_addr.s6_addr,
"\0\0\0\0\0\0\0\0\0\0\xff\xff", 12);
ns[i].sin6.sin6_family = AF_INET6;
ns[i].sin6.sin6_flowinfo = 0;
ns[i].sin6.sin6_scope_id = 0;
}
}
pthread_cleanup_push(cleanup, (void *)(intptr_t)fd);
pthread_setcancelstate(cs, 0);
if (bind(fd, (void *)&sa, sl) < 0) {
errcode = EAI_SYSTEM;
goto out;
}
pfd.fd = fd;
pfd.events = POLLIN;
/* Loop until we timeout; break early on success */
for (; time(0)-t0 < TIMEOUT; ) {
/* Query all configured namservers in parallel */
for (i=0; i<rrcnt; i++) if (rr[i]) for (j=0; j<nns; j++) {
q[0] = id+i >> 8;
q[1] = id+i;
q[ql-3] = rr[i];
sendto(fd, q, ql, MSG_NOSIGNAL, (void *)&ns[j], sl);
}
/* Wait for a response, or until time to retry */
if (poll(&pfd, 1, RETRY) <= 0) continue;
/* Process any and all replies */
while (got+failed < rrcnt && (rlen = recvfrom(fd, r, 512, 0,
(void *)&sa, (socklen_t[1]){sl})) >= 2)
{
/* Ignore replies from addresses we didn't send to */
for (i=0; i<nns; i++) if (!memcmp(ns+i, &sa, sl)) break;
if (i==nns) continue;
/* Compute index of the query from id */
i = r[0]*256+r[1] - id & 0xffff;
if ((unsigned)i >= rrcnt || !rr[i]) continue;
/* Interpret the result code */
switch (r[3] & 15) {
case 0:
got++;
break;
case 3:
if (1) errcode = EAI_NONAME; else
default:
errcode = EAI_FAIL;
failed++;
}
/* Mark this record as answered */
rr[i] = 0;
r += 512;
}
/* Check to see if we have answers to all queries */
if (got+failed == rrcnt) break;
}
out:
pthread_cleanup_pop(1);
/* Return the number of results, or an error code if none */
if (got) return got;
return errcode;
}
static void mkptr4(char *s, const unsigned char *ip)
{
sprintf(s, "%d.%d.%d.%d.in-addr.arpa",
ip[3], ip[2], ip[1], ip[0]);
}
static void mkptr6(char *s, const unsigned char *ip)
{
static const char xdigits[] = "0123456789abcdef";
int i;
for (i=15; i>=0; i--) {
*s++ = xdigits[ip[i]&15]; *s++ = '.';
*s++ = xdigits[ip[i]>>4]; *s++ = '.';
}
strcpy(s, "ip6.arpa");
}
int __dns_query(unsigned char *r, const void *a, int family, int ptr)
{
char buf[PTR_MAX];
int rr[2], rrcnt = 1;
if (ptr) {
if (family == AF_INET6) mkptr6(buf, a);
else mkptr4(buf, a);
rr[0] = RR_PTR;
a = buf;
} else if (family == AF_INET6) {
rr[0] = RR_AAAA;
} else {
rr[0] = RR_A;
if (family != AF_INET) rr[rrcnt++] = RR_AAAA;
}
return __dns_doqueries(r, a, rr, rrcnt);
}
int __dn_expand(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
int __dns_get_rr(void *dest, size_t stride, size_t maxlen, size_t limit, const unsigned char *r, int rr, int dec)
{
int qdcount, ancount;
const unsigned char *p;
char tmp[256];
int found = 0;
int len;
if ((r[3]&15)) return 0;
p = r+12;
qdcount = r[4]*256 + r[5];
ancount = r[6]*256 + r[7];
if (qdcount+ancount > 64) return -1;
while (qdcount--) {
while (p-r < 512 && *p-1U < 127) p++;
if (*p>193 || (*p==193 && p[1]>254) || p>r+506)
return -1;
p += 5 + !!*p;
}
while (ancount--) {
while (p-r < 512 && *p-1U < 127) p++;
if (*p>193 || (*p==193 && p[1]>254) || p>r+506)
return -1;
p += 1 + !!*p;
len = p[8]*256 + p[9];
if (p+len > r+512) return -1;
if (p[1]==rr && len <= maxlen) {
if (dec && __dn_expand(r, r+512, p+10, tmp, sizeof tmp)<0)
return -1;
if (dest && limit) {
if (dec) strcpy(dest, tmp);
else memcpy(dest, p+10, len);
dest = (char *)dest + stride;
limit--;
}
found++;
}
p += 10 + len;
}
return found;
}
int __dns_count_addrs(const unsigned char *r, int cnt)
{
int found=0, res, i;
static const int p[2][2] = { { 4, RR_A }, { 16, RR_AAAA } };
while (cnt--) {
for (i=0; i<2; i++) {
res = __dns_get_rr(0, 0, p[i][0], -1, r, p[i][1], 0);
if (res < 0) return res;
found += res;
}
r += 512;
}
return found;
}

31
src/network/dns_parse.c Normal file
View File

@ -0,0 +1,31 @@
#include <string.h>
int __dns_parse(const unsigned char *r, int rlen, int (*callback)(void *, int, const void *, int, const void *), void *ctx)
{
int qdcount, ancount;
const unsigned char *p;
int len;
if ((r[3]&15)) return 0;
p = r+12;
qdcount = r[4]*256 + r[5];
ancount = r[6]*256 + r[7];
if (qdcount+ancount > 64) return -1;
while (qdcount--) {
while (p-r < rlen && *p-1U < 127) p++;
if (*p>193 || (*p==193 && p[1]>254) || p>r+506)
return -1;
p += 5 + !!*p;
}
while (ancount--) {
while (p-r < rlen && *p-1U < 127) p++;
if (*p>193 || (*p==193 && p[1]>254) || p>r+506)
return -1;
p += 1 + !!*p;
len = p[8]*256 + p[9];
if (p+len > r+rlen) return -1;
if (callback(ctx, p[1], p+10, len, r) < 0) return -1;
p += 10 + len;
}
return 0;
}

View File

@ -5,15 +5,50 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "__dns.h"
int __dns_parse(const unsigned char *, int, int (*)(void *, int, const void *, int, const void *), void *);
int __dn_expand(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
int __res_mkquery(int, const char *, int, int, const unsigned char *, int, const unsigned char*, unsigned char *, int);
int __res_send(const unsigned char *, int, unsigned char *, int);
#define PTR_MAX (64 + sizeof ".in-addr.arpa")
#define RR_PTR 12
static void mkptr4(char *s, const unsigned char *ip)
{
sprintf(s, "%d.%d.%d.%d.in-addr.arpa",
ip[3], ip[2], ip[1], ip[0]);
}
static void mkptr6(char *s, const unsigned char *ip)
{
static const char xdigits[] = "0123456789abcdef";
int i;
for (i=15; i>=0; i--) {
*s++ = xdigits[ip[i]&15]; *s++ = '.';
*s++ = xdigits[ip[i]>>4]; *s++ = '.';
}
strcpy(s, "ip6.arpa");
}
static int dns_parse_callback(void *c, int rr, const void *data, int len, const void *packet)
{
char tmp[256];
if (rr != RR_PTR) return 0;
if (__dn_expand(packet, (const unsigned char *)packet + 512,
data, tmp, sizeof tmp) > 0)
strcpy(c, tmp);
return 0;
}
int getnameinfo(const struct sockaddr *restrict sa, socklen_t sl,
char *restrict node, socklen_t nodelen,
char *restrict serv, socklen_t servlen,
int flags)
{
char ptr[PTR_MAX];
char buf[256];
unsigned char reply[512];
int af = sa->sa_family;
unsigned char *a;
@ -21,20 +56,32 @@ int getnameinfo(const struct sockaddr *restrict sa, socklen_t sl,
case AF_INET:
a = (void *)&((struct sockaddr_in *)sa)->sin_addr;
if (sl != sizeof(struct sockaddr_in)) return EAI_FAMILY;
mkptr4(ptr, a);
break;
case AF_INET6:
a = (void *)&((struct sockaddr_in6 *)sa)->sin6_addr;
if (sl != sizeof(struct sockaddr_in6)) return EAI_FAMILY;
if (memcmp(a, "\0\0\0\0\0\0\0\0\0\0\xff\xff", 12))
mkptr6(ptr, a);
else
mkptr4(ptr, a+12);
break;
default:
return EAI_FAMILY;
}
if (node && nodelen) {
if ((flags & NI_NUMERICHOST)
|| __dns_query(reply, a, af, 1) <= 0
|| __dns_get_rr(buf, 0, 256, 1, reply, RR_PTR, 1) <= 0)
{
buf[0] = 0;
if (!(flags & NI_NUMERICHOST)) {
unsigned char query[18+PTR_MAX], reply[512];
int qlen = __res_mkquery(0, ptr, 1, RR_PTR,
0, 0, 0, query, sizeof query);
int rlen = __res_send(query, qlen, reply, sizeof reply);
buf[0] = 0;
if (rlen > 0)
__dns_parse(reply, rlen, dns_parse_callback, buf);
}
if (!*buf) {
if (flags & NI_NAMEREQD) return EAI_NONAME;
inet_ntop(af, a, buf, sizeof buf);
}

View File

@ -9,7 +9,6 @@
#include "lookup.h"
#include "stdio_impl.h"
#include "syscall.h"
#include "__dns.h"
static int is_valid_hostname(const char *host)
{
@ -86,30 +85,71 @@ static int name_from_hosts(struct address buf[static MAXADDRS], char canon[stati
return cnt;
}
struct dpc_ctx {
struct address *addrs;
char *canon;
int cnt;
};
int __dns_parse(const unsigned char *, int, int (*)(void *, int, const void *, int, const void *), void *);
int __dn_expand(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
int __res_mkquery(int, const char *, int, int, const unsigned char *, int, const unsigned char*, unsigned char *, int);
int __res_msend(int, const unsigned char *const *, const int *, unsigned char *const *, int *, int);
#define RR_A 1
#define RR_CNAME 5
#define RR_AAAA 28
static int dns_parse_callback(void *c, int rr, const void *data, int len, const void *packet)
{
char tmp[256];
struct dpc_ctx *ctx = c;
switch (rr) {
case RR_A:
if (len != 4) return -1;
ctx->addrs[ctx->cnt].family = AF_INET;
memcpy(ctx->addrs[ctx->cnt++].addr, data, 4);
break;
case RR_AAAA:
if (len != 16) return -1;
ctx->addrs[ctx->cnt].family = AF_INET6;
memcpy(ctx->addrs[ctx->cnt++].addr, data, 16);
break;
case RR_CNAME:
if (__dn_expand(packet, (const unsigned char *)packet + 512,
data, tmp, sizeof tmp) > 0 && is_valid_hostname(tmp))
strcpy(ctx->canon, tmp);
break;
}
return 0;
}
static int name_from_dns(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family)
{
unsigned char reply[1024] = { 0 }, *p = reply;
char tmp[256];
int i, cnt = 0;
unsigned char qbuf[2][280], abuf[2][512];
const unsigned char *qp[2] = { qbuf[0], qbuf[1] };
unsigned char *ap[2] = { abuf[0], abuf[1] };
int qlens[2], alens[2];
int i, nq = 0;
struct dpc_ctx ctx = { .addrs = buf, .canon = canon };
/* Perform one or more DNS queries for host */
int result = __dns_query(reply, name, family, 0);
if (result < 0) return result;
for (i=0; i<result; i++) {
if (family != AF_INET6) {
int j = __dns_get_rr(&buf[cnt].addr, sizeof *buf, 4, MAXADDRS-cnt, p, RR_A, 0);
while (j--) buf[cnt++].family = AF_INET;
}
if (family != AF_INET) {
int j = __dns_get_rr(&buf[cnt].addr, sizeof *buf, 16, MAXADDRS-cnt, p, RR_AAAA, 0);
while (j--) buf[cnt++].family = AF_INET6;
}
p += 512;
if (family != AF_INET6) {
qlens[nq] = __res_mkquery(0, name, 1, RR_A, 0, 0, 0,
qbuf[nq], sizeof *qbuf);
nq++;
}
__dns_get_rr(tmp, 0, 256, 1, reply, RR_CNAME, 1);
if (is_valid_hostname(tmp)) strcpy(canon, tmp);
return cnt;
if (family != AF_INET) {
qlens[nq] = __res_mkquery(0, name, 1, RR_AAAA, 0, 0, 0,
qbuf[nq], sizeof *qbuf);
nq++;
}
if (__res_msend(nq, qp, qlens, ap, alens, sizeof *abuf) < 0) return EAI_SYSTEM;
for (i=0; i<nq; i++)
__dns_parse(abuf[i], alens[i], dns_parse_callback, &ctx);
return ctx.cnt;
}
int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, int flags)