diff options
| -rw-r--r-- | src/network/lookup.h | 11 | ||||
| -rw-r--r-- | src/network/res_msend.c | 82 | ||||
| -rw-r--r-- | src/network/resolvconf.c | 93 | 
3 files changed, 126 insertions, 60 deletions
| diff --git a/src/network/lookup.h b/src/network/lookup.h index 69419115..0468edbc 100644 --- a/src/network/lookup.h +++ b/src/network/lookup.h @@ -2,6 +2,7 @@  #define LOOKUP_H  #include <stdint.h> +#include <stddef.h>  struct address {  	int family; @@ -15,6 +16,14 @@ struct service {  	unsigned char proto, socktype;  }; +#define MAXNS 3 + +struct resolvconf { +	struct address ns[MAXNS]; +	unsigned nns, attempts, ndots; +	unsigned timeout; +}; +  /* The limit of 48 results is a non-sharp bound on the number of addresses   * that can fit in one 512-byte DNS packet full of v4 results and a second   * packet full of v6 results. Due to headers, the actual limit is lower. */ @@ -25,4 +34,6 @@ int __lookup_serv(struct service buf[static MAXSERVS], const char *name, int pro  int __lookup_name(struct address buf[static MAXADDRS], char canon[static 256], const char *name, int family, int flags);  int __lookup_ipliteral(struct address buf[static 1], const char *name, int family); +int __get_resolv_conf(struct resolvconf *, char *, size_t); +  #endif diff --git a/src/network/res_msend.c b/src/network/res_msend.c index d0e8e481..0ee914d4 100644 --- a/src/network/res_msend.c +++ b/src/network/res_msend.c @@ -31,14 +31,11 @@ int __res_msend(int nqueries, const unsigned char *const *queries,  	const int *qlens, unsigned char *const *answers, int *alens, int asize)  {  	int fd; -	FILE *f, _f; -	unsigned char _buf[256]; -	char line[64], *s, *z; -	int timeout = 5000, attempts = 2, retry_interval, servfail_retry; +	int timeout, attempts, retry_interval, servfail_retry;  	union {  		struct sockaddr_in sin;  		struct sockaddr_in6 sin6; -	} sa = {0}, ns[3] = {{0}}; +	} sa = {0}, ns[MAXNS] = {{0}};  	socklen_t sl = sizeof sa.sin;  	int nns = 0;  	int family = AF_INET; @@ -48,65 +45,30 @@ int __res_msend(int nqueries, const unsigned char *const *queries,  	int cs;  	struct pollfd pfd;  	unsigned long t0, t1, t2; -	struct address iplit; +	struct resolvconf conf;  	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs); -	/* Get nameservers from resolv.conf, fallback to localhost */ -	f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf); -	if (!f) switch (errno) { -	case ENOENT: -	case ENOTDIR: -	case EACCES: -		goto no_resolv_conf; -	default: -		return -1; -	} -	for (nns=0; nns<3 && fgets(line, sizeof line, f); ) { -		if (!strncmp(line, "options", 7) && isspace(line[7])) { -			unsigned long x; -			char *p, *z; -			p = strstr(line, "timeout:"); -			if (p && isdigit(p[8])) { -				p += 8; -				x = strtoul(p, &z, 10); -				if (z != p) timeout = x < 30 ? x*1000 : 30000; -			} -			p = strstr(line, "attempts:"); -			if (p && isdigit(p[9])) { -				p += 9; -				x = strtoul(p, &z, 10); -				if (z != p) attempts = x < 10 ? x : 10; -				if (!attempts) attempts = 1; -			} +	/* Get nameservers & timeout/retry settings from resolv.conf */ +	if (__get_resolv_conf(&conf, 0, 0) < 0) return -1; + +	timeout = 1000*conf.timeout; +	attempts = conf.attempts; + +	nns = conf.nns; +	for (nns=0; nns<conf.nns; nns++) { +		struct address *iplit = &conf.ns[nns]; +		if (iplit->family == AF_INET) { +			memcpy(&ns[nns].sin.sin_addr, iplit->addr, 4); +			ns[nns].sin.sin_port = htons(53); +			ns[nns].sin.sin_family = AF_INET; +		} else { +			sl = sizeof sa.sin6; +			memcpy(&ns[nns].sin6.sin6_addr, iplit->addr, 16); +			ns[nns].sin6.sin6_port = htons(53); +			ns[nns].sin6.sin6_scope_id = iplit->scopeid; +			ns[nns].sin6.sin6_family = family = AF_INET6;  		} -		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 (__lookup_ipliteral(&iplit, s, AF_UNSPEC)>0) { -			if (iplit.family == AF_INET) { -				memcpy(&ns[nns].sin.sin_addr, iplit.addr, 4); -				ns[nns].sin.sin_port = htons(53); -				ns[nns++].sin.sin_family = AF_INET; -			} else { -				sl = sizeof sa.sin6; -				memcpy(&ns[nns].sin6.sin6_addr, iplit.addr, 16); -				ns[nns].sin6.sin6_port = htons(53); -				ns[nns].sin6.sin6_scope_id = iplit.scopeid; -				ns[nns++].sin6.sin6_family = family = AF_INET6; -			} -		} -	} -	__fclose_ca(f); -no_resolv_conf: -	if (!nns) { -		ns[0].sin.sin_family = AF_INET; -		ns[0].sin.sin_port = htons(53); -		ns[0].sin.sin_addr.s_addr = htonl(0x7f000001); -		nns=1;  	}  	/* Get local address and open/bind a socket */ diff --git a/src/network/resolvconf.c b/src/network/resolvconf.c new file mode 100644 index 00000000..0743a88f --- /dev/null +++ b/src/network/resolvconf.c @@ -0,0 +1,93 @@ +#include "lookup.h" +#include "stdio_impl.h" +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <netinet/in.h> + +int __get_resolv_conf(struct resolvconf *conf, char *search, size_t search_sz) +{ +	char line[256]; +	unsigned char _buf[256]; +	FILE *f, _f; +	int nns; + +	conf->ndots = 1; +	conf->timeout = 5; +	conf->attempts = 2; +	if (search) *search = 0; + +	f = __fopen_rb_ca("/etc/resolv.conf", &_f, _buf, sizeof _buf); +	if (!f) switch (errno) { +	case ENOENT: +	case ENOTDIR: +	case EACCES: +		goto no_resolv_conf; +	default: +		return -1; +	} + +	while (fgets(line, sizeof line, f)) { +		char *p, *z; +		if (!strchr(line, '\n') && !feof(f)) { +			/* Ignore lines that get truncated rather than +			 * potentially misinterpreting them. */ +			int c; +			do c = getc(f); +			while (c != '\n' && c != EOF); +			continue; +		} +		if (!strncmp(line, "options", 7) && isspace(line[7])) { +			p = strstr(line, "ndots:"); +			if (p && isdigit(p[6])) { +				p += 6; +				unsigned long x = strtoul(p, &z, 10); +				if (z != p) conf->ndots = x > 15 ? 15 : x; +			} +			p = strstr(line, "attempts:"); +			if (p && isdigit(p[6])) { +				p += 6; +				unsigned long x = strtoul(p, &z, 10); +				if (z != p) conf->attempts = x > 10 ? 10 : x; +			} +			p = strstr(line, "timeout:"); +			if (p && (isdigit(p[8]) || p[8]=='.')) { +				p += 8; +				unsigned long x = strtoul(p, &z, 10); +				if (z != p) conf->timeout = x > 60 ? 60 : x; +			} +			continue; +		} +		if (!strncmp(line, "nameserver", 10) && isspace(line[10])) { +			if (nns >= MAXNS) continue; +			for (p=line+11; isspace(*p); p++); +			for (z=p; *z && !isspace(*z); z++); +			*z=0; +			if (__lookup_ipliteral(conf->ns+nns, p, AF_UNSPEC) > 0) +				nns++; +			continue; +		} + +		if (!search) continue; +		if (strncmp(line, "domain", 6) || strncmp(line, "search", 6) +		    || !isspace(line[6])) +			continue; +		for (p=line+7; isspace(*p); p++); +		size_t l = strlen(p); +		/* This can never happen anyway with chosen buffer sizes. */ +		if (l >= search_sz) continue; +		memcpy(search, p, l+1); +	} + +	__fclose_ca(f); + +no_resolv_conf: +	if (!nns) { +		__lookup_ipliteral(conf->ns, "127.0.0.1", AF_UNSPEC); +		nns = 1; +	} + +	conf->nns = nns; + +	return 0; +} | 
