diff options
Diffstat (limited to 'src')
-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; +} |