#include "port_before.h"
#include "namespace.h"
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <arpa/nameser.h>
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include "un-namespace.h"
#include "port_after.h"
#include <resolv.h>
#include "res_config.h"
#include "res_private.h"
static void res_readconf(res_state, const char *, int);
static void res_setdefdname(res_state, const char *, const char *);
static void res_setdnsrch(res_state, const char *, const char *);
static int res_setnsaddr(res_state, int, const char *, const char *);
static void res_setoptions(res_state, const char *, const char *);
#ifdef RESOLVSORT
static int res_setsortlist(res_state, int, const char *, const char *);
#endif
#define res_space(ch) \
isspace((unsigned char)(ch))
#define res_comment(ch) \
((ch) == '#' || (ch) == ';')
#define res_match(str, end, word) \
(strncmp((str), (word), (end) - (str)) == 0 && \
(word)[(end) - (str)] == '\0')
int
res_ninit(res_state statp)
{
return (__res_vinit(statp, 0));
}
int
__res_vinit(res_state statp, int preinit)
{
char buf[NI_MAXHOST];
union res_sockaddr_union u[] = {
{ .sin = {
.sin_family = AF_INET,
#ifdef HAVE_SA_LEN
.sin_len = sizeof(struct sockaddr_in),
#endif
.sin_port = htons(NAMESERVER_PORT),
.sin_addr = { htonl(INADDR_LOOPBACK) },
} },
{ .sin6 = {
.sin6_family = AF_INET6,
#ifdef HAVE_SA_LEN
.sin6_len = sizeof(struct sockaddr_in6),
#endif
.sin6_port = htons(NAMESERVER_PORT),
.sin6_addr = IN6ADDR_LOOPBACK_INIT,
} },
};
char *cp, **pp;
int dots;
int maxns = MAXNS;
RES_SET_H_ERRNO(statp, 0);
if (statp->_u._ext.ext != NULL)
res_ndestroy(statp);
if (!preinit) {
statp->retrans = RES_TIMEOUT;
statp->retry = RES_DFLRETRY;
statp->options = RES_DEFAULT;
}
statp->id = res_nrandomid(statp);
statp->nscount = 0;
statp->ndots = 1;
statp->pfcode = 0;
statp->_vcsock = -1;
statp->_flags = 0;
statp->qhook = NULL;
statp->rhook = NULL;
statp->_u._ext.nscount = 0;
statp->_u._ext.ext = malloc(sizeof(*statp->_u._ext.ext));
if (statp->_u._ext.ext != NULL) {
memset(statp->_u._ext.ext, 0, sizeof(*statp->_u._ext.ext));
statp->_u._ext.ext->nsaddrs[0].sin = statp->nsaddr;
strcpy(statp->_u._ext.ext->nsuffix, "ip6.arpa");
strcpy(statp->_u._ext.ext->nsuffix2, "ip6.int");
statp->_u._ext.ext->reload_period = 2;
} else {
RES_SET_H_ERRNO(statp, NETDB_INTERNAL);
maxns = 0;
}
#ifdef RESOLVSORT
statp->nsort = 0;
#endif
res_setservers(statp, u, nitems(u));
res_readconf(statp, _PATH_RESCONF, maxns);
if ((cp = secure_getenv("LOCALDOMAIN")) != NULL) {
while (res_space(*cp))
cp++;
res_setdnsrch(statp, cp, strchr(cp, '\0'));
}
if (statp->defdname[0] == 0 &&
gethostname(buf, sizeof(statp->defdname) - 1) == 0 &&
(cp = strchr(buf, '.')) != NULL)
strcpy(statp->defdname, cp + 1);
if (statp->dnsrch[0] == NULL) {
pp = statp->dnsrch;
*pp++ = statp->defdname;
*pp = NULL;
dots = 0;
for (cp = statp->defdname; *cp; cp++)
dots += (*cp == '.');
cp = statp->defdname;
while (pp < statp->dnsrch + MAXDFLSRCH) {
if (dots < LOCALDOMAINPARTS)
break;
cp = strchr(cp, '.') + 1;
*pp++ = cp;
dots--;
}
*pp = NULL;
#ifdef DEBUG
if (statp->options & RES_DEBUG) {
printf(";; res_init()... default dnsrch list:\n");
for (pp = statp->dnsrch; *pp; pp++)
printf(";;\t%s\n", *pp);
printf(";;\t..END..\n");
}
#endif
}
if (issetugid())
statp->options |= RES_NOALIASES;
else if ((cp = getenv("RES_OPTIONS")) != NULL)
res_setoptions(statp, cp, "env");
statp->options |= RES_INIT;
return (statp->res_h_errno);
}
static void
res_readconf(res_state statp, const char *resconf, int maxns)
{
struct stat sb;
struct timespec now;
FILE *fp;
char *line = NULL, *eol, *eow;
char *cp;
ssize_t len;
size_t sz = 0;
int nserv = 0;
#ifdef RESOLVSORT
int nsort = 0;
#endif
if ((fp = fopen(resconf, "re")) == NULL)
return;
if (statp->_u._ext.ext != NULL) {
if (_fstat(fileno(fp), &sb) == 0) {
statp->_u._ext.ext->conf_mtim = sb.st_mtim;
if (clock_gettime(CLOCK_MONOTONIC_FAST, &now) == 0) {
statp->_u._ext.ext->conf_stat = now.tv_sec;
}
}
}
while ((len = getline(&line, &sz, fp)) != -1) {
eol = line;
while (eol < line + len && !res_comment(*eol))
eol++;
while (eol > line && res_space(eol[-1]))
eol--;
*eol = '\0';
if (eol == line)
continue;
eow = line;
while (eow < eol && !res_space(*eow))
eow++;
if (res_match(line, eow, "domain")) {
cp = eow;
while (cp < eol && res_space(*cp))
cp++;
res_setdefdname(statp, cp, eol);
continue;
}
if (res_match(line, eow, "search")) {
cp = eow;
while (cp < eol && res_space(*cp))
cp++;
res_setdnsrch(statp, cp, eol);
continue;
}
if (res_match(line, eow, "nameserver")) {
cp = eow;
while (cp < eol && res_space(*cp))
cp++;
if (cp == eol)
continue;
if (nserv < maxns) {
if (res_setnsaddr(statp, nserv, cp, eol))
nserv++;
#ifdef DEBUG
} else {
printf(";; nameserver overflow: %.*s\n",
(int)(eol - cp), cp);
#endif
}
continue;
}
#ifdef RESOLVSORT
if (res_match(line, eow, "sortlist")) {
while (nsort < MAXRESOLVSORT) {
cp = eow;
while (cp < eol && res_space(*cp))
cp++;
if (cp == eol)
break;
eow = cp;
while (eow < eol && !res_space(*eow))
eow++;
if (res_setsortlist(statp, nsort, cp, eow))
nsort++;
}
#ifdef DEBUG
while (cp < eol && res_space(*cp))
cp++;
if (cp < eol) {
printf(";; sortlist overflow: %.*s\n",
(int)(eol - cp), cp);
}
#endif
continue;
}
#endif
if (res_match(line, eow, "options")) {
cp = eow;
while (res_space(*cp))
cp++;
res_setoptions(statp, cp, "conf");
continue;
}
}
free(line);
if (nserv > 0)
statp->nscount = nserv;
#ifdef RESOLVSORT
statp->nsort = nsort;
#endif
(void)fclose(fp);
}
static void
res_setdefdname(res_state statp, const char *str, const char *end)
{
const char *sp;
sp = str;
while (sp < end && !res_space(*sp))
sp++;
if (sp == str || sp - str >= sizeof(statp->defdname)) {
statp->defdname[0] = '\0';
statp->dnsrch[0] = NULL;
return;
}
memcpy(statp->defdname, str, sp - str);
statp->defdname[sp - str] = '\0';
statp->dnsrch[0] = NULL;
#ifdef DEBUG
printf(";; domain %s\n", statp->defdname);
#endif
}
static void
res_setdnsrch(res_state statp, const char *str, const char *end)
{
char *sp, *wp, *np;
int n;
while (end > str && res_space(end[-1]))
end--;
if (end == str || end - str >= sizeof(statp->defdname)) {
statp->defdname[0] = '\0';
statp->dnsrch[0] = NULL;
return;
}
memcpy(statp->defdname, str, end - str);
statp->defdname[end - str] = '\0';
for (wp = statp->defdname, n = 0;
*wp != '\0' && n < MAXDNSRCH;
wp = np) {
sp = wp;
while (*sp != '\0' && !res_space(*sp))
sp++;
np = sp;
while (*np != '\0' && res_space(*np))
np++;
if (sp > wp) {
statp->dnsrch[n] = wp;
*sp = '\0';
#ifdef DEBUG
if (n == 0)
printf(";; domain %s\n;; search", statp->defdname);
printf(" %s", statp->dnsrch[n]);
#endif
n++;
}
}
#ifdef DEBUG
printf("\n");
#endif
statp->dnsrch[n] = NULL;
}
static int
res_setnsaddr(res_state statp, int i, const char *str, const char *end)
{
char addr[48], port[8];
_Static_assert(sizeof(addr) > INET6_ADDRSTRLEN,
"address buffer too small for AF_INET6");
_Static_assert(sizeof(addr) > INET_ADDRSTRLEN,
"address buffer too small for AF_INET");
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_DGRAM,
.ai_flags = AI_NUMERICHOST,
};
struct addrinfo *ai;
int ret = 0;
snprintf(port, sizeof(port), "%u", NAMESERVER_PORT);
if (end - str >= sizeof(addr)) {
#ifdef DEBUG
printf(";; nameserver too long: %.*s\n",
(int)(end - str), str);
#endif
return (0);
}
memcpy(addr, str, end - str);
addr[end - str] = '\0';
if (getaddrinfo(addr, port, &hints, &ai) != 0) {
#ifdef DEBUG
printf(";; nameserver invalid: %s\n", addr);
#endif
return (0);
}
if (ai->ai_addrlen <= sizeof(statp->_u._ext.ext->nsaddrs[0])) {
if (statp->_u._ext.ext != NULL) {
memcpy(&statp->_u._ext.ext->nsaddrs[i],
ai->ai_addr, ai->ai_addrlen);
ret = 1;
}
if (ai->ai_addrlen <= sizeof(statp->nsaddr_list[i])) {
memcpy(&statp->nsaddr_list[i],
ai->ai_addr, ai->ai_addrlen);
ret = 1;
} else {
statp->nsaddr_list[i].sin_family = 0;
}
#ifdef DEBUG
printf(";; nameserver %s\n", addr);
#endif
} else {
#ifdef DEBUG
printf(";; sockaddr too big: %s\n", addr);
#endif
}
freeaddrinfo(ai);
return (ret);
}
#ifdef RESOLVSORT
static int
res_setsortlist(res_state statp, int i, const char *str, const char *end)
{
struct __res_state_ext *ext = statp->_u._ext.ext;
char addr[48], mask[48], *dst, *numend;
_Static_assert(sizeof(addr) > INET6_ADDRSTRLEN,
"address buffer too small for AF_INET6");
_Static_assert(sizeof(addr) > INET_ADDRSTRLEN,
"address buffer too small for AF_INET");
_Static_assert(sizeof(mask) > INET6_ADDRSTRLEN,
"mask buffer too small for AF_INET6");
_Static_assert(sizeof(mask) > INET_ADDRSTRLEN,
"mask buffer too small for AF_INET");
struct in_addr a;
struct in6_addr a6;
uint8_t *u;
long num;
dst = addr;
while (str < end && !res_space(*str) && *str != '/') {
*dst++ = *str++;
if (dst == addr + sizeof(addr))
return (0);
}
*dst = '\0';
dst = mask;
if (str < end) {
if (*str++ != '/')
return (0);
if (end - str >= sizeof(mask))
return (0);
memcpy(mask, str, end - str);
dst += end - str;
}
*dst = '\0';
if (inet_pton(AF_INET, addr, &a) == 1) {
statp->sort_list[i].addr = a;
if (strchr(mask, '.') &&
inet_pton(AF_INET, mask, &a) == 1) {
statp->sort_list[i].mask = a.s_addr;
} else if (*mask != '\0') {
if ((num = strtol(mask, &numend, 10)) < 0 ||
num > 32 || *numend != '\0')
return (0);
statp->sort_list[i].mask =
num == 0 ? 0 :
htonl(0xffffffffU << (32 - num));
} else if (IN_CLASSA(ntohl(a.s_addr))) {
statp->sort_list[i].mask =
htonl(IN_CLASSA_NET);
} else if (IN_CLASSB(ntohl(a.s_addr))) {
statp->sort_list[i].mask =
htonl(IN_CLASSB_NET);
} else if (IN_CLASSC(ntohl(a.s_addr))) {
statp->sort_list[i].mask =
htonl(IN_CLASSC_NET);
} else {
statp->sort_list[i].mask =
htonl(IN_NETMASK_DEFAULT);
}
ext->sort_list[i].af = AF_INET;
ext->sort_list[i].addr.ina =
statp->sort_list[i].addr;
ext->sort_list[i].mask.ina.s_addr =
statp->sort_list[i].mask;
#ifdef DEBUG
inet_ntop(AF_INET, &statp->sort_list[i].addr,
addr, sizeof(addr));
a.s_addr = statp->sort_list[i].mask;
inet_ntop(AF_INET, &a, mask, sizeof(mask));
printf(";; sortlist %s/%s\n", addr, mask);
#endif
return (1);
}
if (inet_pton(AF_INET6, addr, &a6) == 1) {
statp->sort_list[i].addr.s_addr = INADDR_NONE;
statp->sort_list[i].mask = INADDR_NONE;
ext->sort_list[i].af = AF_INET6;
ext->sort_list[i].addr.in6a = a6;
if (*mask != '\0') {
if ((num = strtol(mask, &numend, 10)) < 0 ||
num > 128 || *numend != '\0')
return (0);
} else {
num = 64;
}
#ifdef DEBUG
inet_ntop(AF_INET6, &a6, addr, sizeof(addr));
printf(";; sortlist %s/%ld\n", addr, num);
#endif
a6 = (struct in6_addr)IN6ADDR_ANY_INIT;
for (u = a6.s6_addr8; num > 0; num -= 8)
*u++ = num > 8 ? 0xffU : 0xffU << (8 - num);
ext->sort_list[i].mask.in6a = a6;
return (1);
}
return (0);
}
#endif
static void
res_setoptions(res_state statp, const char *options, const char *source)
{
const char *cp = options, *sp, *ep;
char *numend;
long num;
struct __res_state_ext *ext = statp->_u._ext.ext;
#ifdef DEBUG
if (statp->options & RES_DEBUG) {
printf(";; res_setoptions(\"%s\", \"%s\")...\n",
options, source);
}
#endif
for (;;) {
while (res_space(*cp))
cp++;
sp = cp;
while (*sp != '\0' && !res_space(*sp) && *sp != ':')
sp++;
ep = sp;
while (*ep != '\0' && !res_space(*ep))
ep++;
if (ep == cp)
break;
if (res_match(cp, sp, "ndots") && sp < ep) {
num = strtol(sp + 1, &numend, 10);
if (numend == ep && num >= 0 && num <= RES_MAXNDOTS) {
statp->ndots = num;
#ifdef DEBUG
if (statp->options & RES_DEBUG)
printf(";;\tndots=%d\n", statp->ndots);
#endif
}
} else if (res_match(cp, sp, "timeout") && sp < ep) {
num = strtol(sp + 1, &numend, 10);
if (numend == ep && num > 0 && num <= RES_MAXRETRANS) {
statp->retrans = num;
#ifdef DEBUG
if (statp->options & RES_DEBUG)
printf(";;\ttimeout=%d\n",
statp->retrans);
#endif
}
} else if (res_match(cp, sp, "attempts") && sp < ep) {
num = strtol(sp + 1, &numend, 10);
if (numend == ep && num > 0 && num <= RES_MAXRETRY) {
statp->retry = num;
#ifdef DEBUG
if (statp->options & RES_DEBUG)
printf(";;\tattempts=%d\n",
statp->retry);
#endif
}
} else if (res_match(cp, ep, "debug")) {
#ifdef DEBUG
if (!(statp->options & RES_DEBUG)) {
printf(";; res_setoptions(\"%s\", \"%s\")..\n",
options, source);
}
#endif
statp->options |= RES_DEBUG;
#ifdef DEBUG
printf(";;\tdebug\n");
#endif
} else if (res_match(cp, ep, "no-debug")) {
statp->options &= ~RES_DEBUG;
} else if (res_match(cp, ep, "no-rotate")) {
statp->options &= ~RES_ROTATE;
} else if (res_match(cp, ep, "no-tld-query") ||
res_match(cp, ep, "no_tld_query")) {
statp->options |= RES_NOTLDQUERY;
} else if (res_match(cp, ep, "inet6")) {
statp->options |= RES_USE_INET6;
} else if (res_match(cp, ep, "insecure1")) {
statp->options |= RES_INSECURE1;
} else if (res_match(cp, ep, "insecure2")) {
statp->options |= RES_INSECURE2;
} else if (res_match(cp, ep, "blast")) {
statp->options |= RES_BLAST;
} else if (res_match(cp, ep, "rotate")) {
statp->options |= RES_ROTATE;
} else if (res_match(cp, ep, "usevc")) {
statp->options |= RES_USEVC;
} else if (res_match(cp, ep, "no-check-names")) {
statp->options |= RES_NOCHECKNAME;
} else if (res_match(cp, sp, "reload-period") && sp < ep) {
num = strtol(sp + 1, &numend, 10);
if (numend == ep && num > 0 && num <= USHRT_MAX) {
if (ext != NULL)
ext->reload_period = (u_short)num;
#ifdef DEBUG
if (statp->options & RES_DEBUG)
printf(";;\treload-period %hu\n",
(u_short)num);
#endif
}
#ifdef RES_USE_EDNS0
} else if (res_match(cp, ep, "edns0")) {
statp->options |= RES_USE_EDNS0;
#endif
#ifndef _LIBC
} else if (res_match(cp, ep, "dname")) {
statp->options |= RES_USE_DNAME;
} else if (res_match(cp, sp, "nibble") && sp < ep) {
sp++;
if (ext != NULL && ep - sp < sizeof(ext->nsuffix)) {
memcpy(ext->nsuffix, sp, ep - sp);
ext->nsuffix[ep - sp] = '\0';
}
} else if (res_match(cp, sp, "nibble2") && sp < ep) {
sp++;
if (ext != NULL && ep - sp < sizeof(ext->nsuffix2)) {
memcpy(ext->nsuffix2, sp, ep - sp);
ext->nsuffix2[ep - sp] = '\0';
}
} else if (res_match(cp, sp, "v6revmode") && sp < ep) {
if (res_match(sp + 1, ep, "single"))
statp->options |= RES_NO_NIBBLE2;
else if (res_match(sp + 1, ep, "both"))
statp->options &= ~RES_NO_NIBBLE2;
#ifdef DEBUG
else
printf(";;\t%.*s: unrecognized value: %.*s\n",
(int)(sp - cp), cp,
(int)(ep - sp - 1), sp + 1);
#endif
#endif
} else {
#ifdef DEBUG
printf(";;\tunrecognized option: %.*s\n",
(int)(ep - cp), cp);
#endif
}
cp = ep;
}
}
void
freebsd15_res_rndinit(res_state statp)
{
(void)statp;
}
__sym_compat(__res_rndinit, freebsd15_res_rndinit, FBSD_1.4);
u_int
res_nrandomid(res_state statp)
{
(void) statp;
return ((u_int)(arc4random() & 0xffff));
}
void
res_nclose(res_state statp)
{
int ns;
if (statp->_vcsock >= 0) {
(void) _close(statp->_vcsock);
statp->_vcsock = -1;
statp->_flags &= ~(RES_F_VC | RES_F_CONN);
}
for (ns = 0; ns < statp->_u._ext.nscount; ns++) {
if (statp->_u._ext.nssocks[ns] != -1) {
(void) _close(statp->_u._ext.nssocks[ns]);
statp->_u._ext.nssocks[ns] = -1;
}
}
}
void
res_ndestroy(res_state statp)
{
res_nclose(statp);
if (statp->_u._ext.ext != NULL) {
free(statp->_u._ext.ext);
statp->_u._ext.ext = NULL;
}
statp->options &= ~RES_INIT;
}
void
res_setservers(res_state statp, const union res_sockaddr_union *set, int cnt)
{
int i, nserv;
size_t size;
res_nclose(statp);
statp->_u._ext.nscount = 0;
nserv = 0;
for (i = 0; i < cnt && nserv < MAXNS; i++) {
switch (set->sin.sin_family) {
case AF_INET:
size = sizeof(set->sin);
if (statp->_u._ext.ext)
memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
&set->sin, size);
if (size <= sizeof(statp->nsaddr_list[nserv]))
memcpy(&statp->nsaddr_list[nserv],
&set->sin, size);
else
statp->nsaddr_list[nserv].sin_family = 0;
nserv++;
break;
case AF_INET6:
size = sizeof(set->sin6);
if (statp->_u._ext.ext)
memcpy(&statp->_u._ext.ext->nsaddrs[nserv],
&set->sin6, size);
if (size <= sizeof(statp->nsaddr_list[nserv]))
memcpy(&statp->nsaddr_list[nserv],
&set->sin6, size);
else
statp->nsaddr_list[nserv].sin_family = 0;
nserv++;
break;
default:
break;
}
set++;
}
statp->nscount = nserv;
}
int
res_getservers(res_state statp, union res_sockaddr_union *set, int cnt)
{
int i;
size_t size;
u_int16_t family;
for (i = 0; i < statp->nscount && i < cnt; i++) {
if (statp->_u._ext.ext)
family = statp->_u._ext.ext->nsaddrs[i].sin.sin_family;
else
family = statp->nsaddr_list[i].sin_family;
switch (family) {
case AF_INET:
size = sizeof(set->sin);
if (statp->_u._ext.ext)
memcpy(&set->sin,
&statp->_u._ext.ext->nsaddrs[i],
size);
else
memcpy(&set->sin, &statp->nsaddr_list[i],
size);
break;
case AF_INET6:
size = sizeof(set->sin6);
if (statp->_u._ext.ext)
memcpy(&set->sin6,
&statp->_u._ext.ext->nsaddrs[i],
size);
else
memcpy(&set->sin6, &statp->nsaddr_list[i],
size);
break;
default:
set->sin.sin_family = 0;
break;
}
set++;
}
return (statp->nscount);
}