diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/network/__dns.c | 282 | ||||
| -rw-r--r-- | src/network/dns_parse.c | 31 | ||||
| -rw-r--r-- | src/network/getnameinfo.c | 59 | ||||
| -rw-r--r-- | src/network/lookup_name.c | 82 | 
4 files changed, 145 insertions, 309 deletions
| diff --git a/src/network/__dns.c b/src/network/__dns.c deleted file mode 100644 index 97d8031c..00000000 --- a/src/network/__dns.c +++ /dev/null @@ -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; -} diff --git a/src/network/dns_parse.c b/src/network/dns_parse.c new file mode 100644 index 00000000..aa0d39f3 --- /dev/null +++ b/src/network/dns_parse.c @@ -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; +} diff --git a/src/network/getnameinfo.c b/src/network/getnameinfo.c index 33f89a38..dfcf6eda 100644 --- a/src/network/getnameinfo.c +++ b/src/network/getnameinfo.c @@ -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);  		} diff --git a/src/network/lookup_name.c b/src/network/lookup_name.c index e1b583ee..83c0fc27 100644 --- a/src/network/lookup_name.c +++ b/src/network/lookup_name.c @@ -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;  } -static int name_from_dns(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family) +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)  { -	unsigned char reply[1024] = { 0 }, *p = reply;  	char tmp[256]; -	int i, cnt = 0; - -	/* Perform one or more DNS queries for host */ -	int result = __dns_query(reply, name, family, 0); -	if (result < 0) return result; +	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; +} -	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; +static int name_from_dns(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family) +{ +	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 }; + +	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) | 
