1*ce6743a3Snaddy /* $OpenBSD: asr.c,v 1.68 2022/01/20 14:18:10 naddy Exp $ */
2b44da627Seric /*
3b44da627Seric * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org>
4b44da627Seric *
5b44da627Seric * Permission to use, copy, modify, and distribute this software for any
6b44da627Seric * purpose with or without fee is hereby granted, provided that the above
7b44da627Seric * copyright notice and this permission notice appear in all copies.
8b44da627Seric *
9b44da627Seric * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10b44da627Seric * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11b44da627Seric * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12b44da627Seric * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13b44da627Seric * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14b44da627Seric * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15b44da627Seric * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16b44da627Seric */
1780f48568Seric
18b44da627Seric #include <sys/types.h>
19d216d6b1Seric #include <sys/socket.h>
20b44da627Seric #include <sys/stat.h>
21b44da627Seric #include <netinet/in.h>
22b44da627Seric #include <arpa/inet.h>
23b44da627Seric #include <arpa/nameser.h>
24d216d6b1Seric #include <netdb.h>
25b44da627Seric
26d216d6b1Seric #include <asr.h>
27b44da627Seric #include <errno.h>
28b44da627Seric #include <fcntl.h>
29b44da627Seric #include <resolv.h>
30b44da627Seric #include <poll.h>
31b44da627Seric #include <stdio.h>
32b44da627Seric #include <stdlib.h>
33b44da627Seric #include <string.h>
34b44da627Seric #include <unistd.h>
35aea60beeSderaadt #include <limits.h>
36b44da627Seric
37b44da627Seric #include "asr_private.h"
384671d0a5Seric
3912bff097Seric #include "thread_private.h"
40b44da627Seric
415d09e3abSeric #define DEFAULT_CONF "lookup file\n"
42b44da627Seric #define DEFAULT_LOOKUP "lookup bind file"
43b44da627Seric
44b44da627Seric #define RELOAD_DELAY 15 /* seconds */
45b44da627Seric
46b44da627Seric static void asr_check_reload(struct asr *);
47b44da627Seric static struct asr_ctx *asr_ctx_create(void);
48b44da627Seric static void asr_ctx_ref(struct asr_ctx *);
49b44da627Seric static void asr_ctx_free(struct asr_ctx *);
50b44da627Seric static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *);
51b44da627Seric static int asr_ctx_from_file(struct asr_ctx *, const char *);
52b44da627Seric static int asr_ctx_from_string(struct asr_ctx *, const char *);
5381550ec8Seric static int asr_ctx_parse(struct asr_ctx *, const char *);
54b44da627Seric static int asr_parse_nameserver(struct sockaddr *, const char *);
55b44da627Seric static int asr_ndots(const char *);
56c3a0261fSeric static void pass0(char **, int, struct asr_ctx *);
5795463336Seric static int strsplit(char *, char **, int);
584671d0a5Seric static void asr_ctx_envopts(struct asr_ctx *);
5912bff097Seric static void *__THREAD_NAME(_asr);
604671d0a5Seric
6112bff097Seric static struct asr *_asr = NULL;
62b44da627Seric
63b44da627Seric /* Allocate and configure an async "resolver". */
64cbd0a9faSderaadt static void *
_asr_resolver(void)65cbd0a9faSderaadt _asr_resolver(void)
66b44da627Seric {
67b44da627Seric static int init = 0;
68b44da627Seric struct asr *asr;
69b44da627Seric
70b44da627Seric if (init == 0) {
7146ab4803Seric #ifdef DEBUG
72b44da627Seric if (getenv("ASR_DEBUG"))
7355f55055Seric _asr_debug = stderr;
7446ab4803Seric #endif
75b44da627Seric init = 1;
76b44da627Seric }
7746ab4803Seric
78b44da627Seric if ((asr = calloc(1, sizeof(*asr))) == NULL)
79b44da627Seric goto fail;
80b44da627Seric
81b44da627Seric asr_check_reload(asr);
82b44da627Seric if (asr->a_ctx == NULL) {
83b44da627Seric if ((asr->a_ctx = asr_ctx_create()) == NULL)
84b44da627Seric goto fail;
85b44da627Seric if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1)
86b44da627Seric goto fail;
87b44da627Seric asr_ctx_envopts(asr->a_ctx);
88b44da627Seric }
89b44da627Seric
90b44da627Seric #ifdef DEBUG
9155f55055Seric _asr_dump_config(_asr_debug, asr);
92b44da627Seric #endif
93b44da627Seric return (asr);
94b44da627Seric
95b44da627Seric fail:
96b44da627Seric if (asr) {
97b44da627Seric if (asr->a_ctx)
98b44da627Seric asr_ctx_free(asr->a_ctx);
99b44da627Seric free(asr);
100b44da627Seric }
101b44da627Seric
102b44da627Seric return (NULL);
103b44da627Seric }
104b44da627Seric
105b44da627Seric /*
106b44da627Seric * Free the "asr" async resolver (or the thread-local resolver if NULL).
107b44da627Seric * Drop the reference to the current context.
108b44da627Seric */
109b44da627Seric void
_asr_resolver_done(void * arg)110253ef892Sderaadt _asr_resolver_done(void *arg)
111b44da627Seric {
112af4d66b3Sotto struct asr_ctx *ac = arg;
113af4d66b3Sotto struct asr *asr;
11412bff097Seric struct asr **priv;
11512bff097Seric
116af4d66b3Sotto if (ac) {
117af4d66b3Sotto _asr_ctx_unref(ac);
118af4d66b3Sotto return;
119af4d66b3Sotto } else {
120a7244e9fSotto priv = _THREAD_PRIVATE_DT(_asr, _asr, NULL, &_asr);
12112bff097Seric if (*priv == NULL)
122b44da627Seric return;
12312bff097Seric asr = *priv;
12412bff097Seric *priv = NULL;
125b44da627Seric }
126b44da627Seric
127253ef892Sderaadt _asr_ctx_unref(asr->a_ctx);
128b44da627Seric free(asr);
129b44da627Seric }
130b44da627Seric
131a7244e9fSotto static void
_asr_resolver_done_tp(void * arg)132a7244e9fSotto _asr_resolver_done_tp(void *arg)
133a7244e9fSotto {
134a7244e9fSotto struct asr **priv = arg;
135a7244e9fSotto struct asr *asr;
136a7244e9fSotto
137a7244e9fSotto if (*priv == NULL)
138a7244e9fSotto return;
139a7244e9fSotto asr = *priv;
140a7244e9fSotto
141a7244e9fSotto _asr_ctx_unref(asr->a_ctx);
142a7244e9fSotto free(asr);
143a7244e9fSotto free(priv);
144a7244e9fSotto }
145a7244e9fSotto
146af4d66b3Sotto void *
asr_resolver_from_string(const char * str)147af4d66b3Sotto asr_resolver_from_string(const char *str)
148af4d66b3Sotto {
149af4d66b3Sotto struct asr_ctx *ac;
150af4d66b3Sotto
151af4d66b3Sotto if ((ac = asr_ctx_create()) == NULL)
152af4d66b3Sotto return NULL;
153af4d66b3Sotto
154af4d66b3Sotto if (asr_ctx_from_string(ac, str) == -1) {
155af4d66b3Sotto asr_ctx_free(ac);
156af4d66b3Sotto return NULL;
157af4d66b3Sotto }
158af4d66b3Sotto
159af4d66b3Sotto return ac;
160af4d66b3Sotto }
161af4d66b3Sotto DEF_WEAK(asr_resolver_from_string);
162af4d66b3Sotto
163af4d66b3Sotto void
asr_resolver_free(void * arg)164af4d66b3Sotto asr_resolver_free(void *arg)
165af4d66b3Sotto {
166af4d66b3Sotto _asr_ctx_unref(arg);
167af4d66b3Sotto }
168af4d66b3Sotto DEF_WEAK(asr_resolver_free);
169af4d66b3Sotto
170b44da627Seric /*
171b44da627Seric * Cancel an async query.
172b44da627Seric */
173b44da627Seric void
asr_abort(struct asr_query * as)1745be03f8fSeric asr_abort(struct asr_query *as)
175b44da627Seric {
176253ef892Sderaadt _asr_async_free(as);
177b44da627Seric }
178b44da627Seric
179b44da627Seric /*
180b44da627Seric * Resume the "as" async query resolution. Return one of ASYNC_COND,
1815be03f8fSeric * or ASYNC_DONE and put query-specific return values in the user-allocated
1825be03f8fSeric * memory at "ar".
183b44da627Seric */
184b44da627Seric int
asr_run(struct asr_query * as,struct asr_result * ar)1855be03f8fSeric asr_run(struct asr_query *as, struct asr_result *ar)
186b44da627Seric {
187055465aeSeric int r, saved_errno = errno;
188b44da627Seric
18930d3f3d9Sderaadt memset(ar, 0, sizeof(*ar));
19030d3f3d9Sderaadt
1915be03f8fSeric DPRINT("asr: asr_run(%p, %p) %s ctx=[%p]\n", as, ar,
19255f55055Seric _asr_querystr(as->as_type), as->as_ctx);
193b44da627Seric r = as->as_run(as, ar);
194b44da627Seric
19555f55055Seric DPRINT("asr: asr_run(%p, %p) -> %s", as, ar, _asr_transitionstr(r));
196d4cf23afSeric #ifdef DEBUG
197b44da627Seric if (r == ASYNC_COND)
198d4cf23afSeric #endif
199d4cf23afSeric DPRINT(" fd=%i timeout=%i", ar->ar_fd, ar->ar_timeout);
20046ab4803Seric DPRINT("\n");
201b44da627Seric if (r == ASYNC_DONE)
202253ef892Sderaadt _asr_async_free(as);
203b44da627Seric
204055465aeSeric errno = saved_errno;
205055465aeSeric
206b44da627Seric return (r);
207b44da627Seric }
2085826fd8cSguenther DEF_WEAK(asr_run);
209b44da627Seric
210e23664d1Seric static int
poll_intrsafe(struct pollfd * fds,nfds_t nfds,int timeout)211e23664d1Seric poll_intrsafe(struct pollfd *fds, nfds_t nfds, int timeout)
212e23664d1Seric {
213e23664d1Seric struct timespec pollstart, pollend, elapsed;
214e23664d1Seric int r;
215e23664d1Seric
216d82e6535Spirofti if (WRAP(clock_gettime)(CLOCK_MONOTONIC, &pollstart))
217e23664d1Seric return -1;
218e23664d1Seric
219e23664d1Seric while ((r = poll(fds, 1, timeout)) == -1 && errno == EINTR) {
220d82e6535Spirofti if (WRAP(clock_gettime)(CLOCK_MONOTONIC, &pollend))
221e23664d1Seric return -1;
222e23664d1Seric timespecsub(&pollend, &pollstart, &elapsed);
223e23664d1Seric timeout -= elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000;
224e23664d1Seric if (timeout < 1)
225e23664d1Seric return 0;
226e23664d1Seric }
227e23664d1Seric
228e23664d1Seric return r;
229e23664d1Seric }
230e23664d1Seric
231610287b6Sjca /*
232610287b6Sjca * Same as asr_run, but run in a loop that handles the fd conditions result.
233610287b6Sjca */
234b44da627Seric int
asr_run_sync(struct asr_query * as,struct asr_result * ar)2355be03f8fSeric asr_run_sync(struct asr_query *as, struct asr_result *ar)
236b44da627Seric {
237b44da627Seric struct pollfd fds[1];
238e23664d1Seric int r, saved_errno = errno;
239b44da627Seric
2405be03f8fSeric while ((r = asr_run(as, ar)) == ASYNC_COND) {
241b44da627Seric fds[0].fd = ar->ar_fd;
2425be03f8fSeric fds[0].events = (ar->ar_cond == ASR_WANT_READ) ? POLLIN:POLLOUT;
243eb76e4bcSjmatthew
244e23664d1Seric if (poll_intrsafe(fds, 1, ar->ar_timeout) == -1) {
245e23664d1Seric memset(ar, 0, sizeof(*ar));
246e23664d1Seric ar->ar_errno = errno;
247e23664d1Seric ar->ar_h_errno = NETDB_INTERNAL;
248e23664d1Seric ar->ar_gai_errno = EAI_SYSTEM;
249e23664d1Seric ar->ar_rrset_errno = NETDB_INTERNAL;
250e23664d1Seric _asr_async_free(as);
251e23664d1Seric errno = saved_errno;
252e23664d1Seric return ASYNC_DONE;
253eb76e4bcSjmatthew }
254eb76e4bcSjmatthew
255055465aeSeric /*
2565be03f8fSeric * Otherwise, just ignore the error and let asr_run()
257055465aeSeric * catch the failure.
258055465aeSeric */
259b44da627Seric }
260b44da627Seric
261055465aeSeric errno = saved_errno;
262055465aeSeric
263b44da627Seric return (r);
264b44da627Seric }
2655826fd8cSguenther DEF_WEAK(asr_run_sync);
266b44da627Seric
267b44da627Seric /*
268b44da627Seric * Create a new async request of the given "type" on the async context "ac".
269610287b6Sjca * Take a reference on it so it does not get deleted while the async query
270b44da627Seric * is running.
271b44da627Seric */
2725be03f8fSeric struct asr_query *
_asr_async_new(struct asr_ctx * ac,int type)273253ef892Sderaadt _asr_async_new(struct asr_ctx *ac, int type)
274b44da627Seric {
2755be03f8fSeric struct asr_query *as;
27646ab4803Seric
2775bd9e5c2Seric DPRINT("asr: asr_async_new(ctx=%p) type=%i refcount=%i\n", ac, type,
2782ccfb57cSotto ac ? ac->ac_refcount : 0);
2792ccfb57cSotto if (ac == NULL || (as = calloc(1, sizeof(*as))) == NULL)
280b44da627Seric return (NULL);
281b44da627Seric
282b44da627Seric ac->ac_refcount += 1;
283b44da627Seric as->as_ctx = ac;
284b44da627Seric as->as_fd = -1;
285b44da627Seric as->as_type = type;
286b44da627Seric as->as_state = ASR_STATE_INIT;
287b44da627Seric
288b44da627Seric return (as);
289b44da627Seric }
290b44da627Seric
291b44da627Seric /*
292b44da627Seric * Free an async query and unref the associated context.
293b44da627Seric */
294b44da627Seric void
_asr_async_free(struct asr_query * as)295253ef892Sderaadt _asr_async_free(struct asr_query *as)
296b44da627Seric {
2975bd9e5c2Seric DPRINT("asr: asr_async_free(%p)\n", as);
298f6f51dadSeric
299f6f51dadSeric if (as->as_subq)
300f6f51dadSeric _asr_async_free(as->as_subq);
301f6f51dadSeric
302b44da627Seric switch (as->as_type) {
303b44da627Seric case ASR_SEND:
304b44da627Seric if (as->as_fd != -1)
305b44da627Seric close(as->as_fd);
306abe78e02Sjca if (as->as.dns.obuf && !(as->as_flags & ASYNC_EXTOBUF))
307b44da627Seric free(as->as.dns.obuf);
308c5221d45Seric if (as->as.dns.ibuf)
309b44da627Seric free(as->as.dns.ibuf);
310b44da627Seric if (as->as.dns.dname)
311b44da627Seric free(as->as.dns.dname);
312b44da627Seric break;
313b44da627Seric
314b44da627Seric case ASR_SEARCH:
315b44da627Seric if (as->as.search.name)
316b44da627Seric free(as->as.search.name);
317b44da627Seric break;
318b44da627Seric
319b44da627Seric case ASR_GETRRSETBYNAME:
320b44da627Seric if (as->as.rrset.name)
321b44da627Seric free(as->as.rrset.name);
322b44da627Seric break;
323b44da627Seric
324b44da627Seric case ASR_GETHOSTBYNAME:
325b44da627Seric case ASR_GETHOSTBYADDR:
326b44da627Seric if (as->as.hostnamadr.name)
327b44da627Seric free(as->as.hostnamadr.name);
328b44da627Seric break;
329b44da627Seric
330b44da627Seric case ASR_GETADDRINFO:
331b44da627Seric if (as->as.ai.aifirst)
332b44da627Seric freeaddrinfo(as->as.ai.aifirst);
333b44da627Seric if (as->as.ai.hostname)
334b44da627Seric free(as->as.ai.hostname);
335b44da627Seric if (as->as.ai.servname)
336b44da627Seric free(as->as.ai.servname);
337c5c8c49bSeric if (as->as.ai.fqdn)
338c5c8c49bSeric free(as->as.ai.fqdn);
339b44da627Seric break;
340b44da627Seric
341b44da627Seric case ASR_GETNAMEINFO:
342b44da627Seric break;
343b44da627Seric }
344b44da627Seric
345253ef892Sderaadt _asr_ctx_unref(as->as_ctx);
346b44da627Seric free(as);
347b44da627Seric }
348b44da627Seric
349b44da627Seric /*
350b44da627Seric * Get a context from the given resolver. This takes a new reference to
351c43131adSkrw * the returned context, which *must* be explicitly dropped when done
352b44da627Seric * using this context.
353b44da627Seric */
354b44da627Seric struct asr_ctx *
_asr_use_resolver(void * arg)355253ef892Sderaadt _asr_use_resolver(void *arg)
356b44da627Seric {
357af4d66b3Sotto struct asr_ctx *ac = arg;
358af4d66b3Sotto struct asr *asr;
35912bff097Seric struct asr **priv;
360b44da627Seric
361af4d66b3Sotto if (ac) {
362af4d66b3Sotto asr_ctx_ref(ac);
363af4d66b3Sotto return ac;
364af4d66b3Sotto }
365af4d66b3Sotto else {
36646ab4803Seric DPRINT("using thread-local resolver\n");
367a7244e9fSotto priv = _THREAD_PRIVATE_DT(_asr, _asr, _asr_resolver_done_tp,
368a7244e9fSotto &_asr);
36912bff097Seric if (*priv == NULL) {
37046ab4803Seric DPRINT("setting up thread-local resolver\n");
371cbd0a9faSderaadt *priv = _asr_resolver();
37212bff097Seric }
37312bff097Seric asr = *priv;
374b44da627Seric }
3752ccfb57cSotto if (asr != NULL) {
376b44da627Seric asr_check_reload(asr);
377b44da627Seric asr_ctx_ref(asr->a_ctx);
378b44da627Seric return (asr->a_ctx);
379b44da627Seric }
3802ccfb57cSotto return (NULL);
3812ccfb57cSotto }
382b44da627Seric
383b44da627Seric static void
asr_ctx_ref(struct asr_ctx * ac)384b44da627Seric asr_ctx_ref(struct asr_ctx *ac)
385b44da627Seric {
38646ab4803Seric DPRINT("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", ac, ac->ac_refcount);
387b44da627Seric ac->ac_refcount += 1;
388b44da627Seric }
389b44da627Seric
390b44da627Seric /*
391b44da627Seric * Drop a reference to an async context, freeing it if the reference
392b44da627Seric * count drops to 0.
393b44da627Seric */
394b44da627Seric void
_asr_ctx_unref(struct asr_ctx * ac)395253ef892Sderaadt _asr_ctx_unref(struct asr_ctx *ac)
396b44da627Seric {
3972ccfb57cSotto DPRINT("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", ac,
3982ccfb57cSotto ac ? ac->ac_refcount : 0);
3992ccfb57cSotto if (ac == NULL)
4002ccfb57cSotto return;
401b44da627Seric if (--ac->ac_refcount)
402b44da627Seric return;
403b44da627Seric
404b44da627Seric asr_ctx_free(ac);
405b44da627Seric }
406b44da627Seric
407b44da627Seric static void
asr_ctx_free(struct asr_ctx * ac)408b44da627Seric asr_ctx_free(struct asr_ctx *ac)
409b44da627Seric {
410b44da627Seric int i;
411b44da627Seric
412b44da627Seric if (ac->ac_domain)
413b44da627Seric free(ac->ac_domain);
4141ed934d0Seric for (i = 0; i < ASR_MAXNS; i++)
415b44da627Seric free(ac->ac_ns[i]);
4161ed934d0Seric for (i = 0; i < ASR_MAXDOM; i++)
417b44da627Seric free(ac->ac_dom[i]);
418b44da627Seric
419b44da627Seric free(ac);
420b44da627Seric }
421b44da627Seric
422b44da627Seric /*
423b44da627Seric * Reload the configuration file if it has changed on disk.
424b44da627Seric */
425b44da627Seric static void
asr_check_reload(struct asr * asr)426b44da627Seric asr_check_reload(struct asr *asr)
427b44da627Seric {
428b44da627Seric struct asr_ctx *ac;
4294671d0a5Seric struct stat st;
43099491d36Sderaadt struct timespec ts;
4315aaab171Seric pid_t pid;
432b44da627Seric
4335aaab171Seric pid = getpid();
4345aaab171Seric if (pid != asr->a_pid) {
4355aaab171Seric asr->a_pid = pid;
4365aaab171Seric asr->a_rtime = 0;
4375aaab171Seric }
4385aaab171Seric
439d82e6535Spirofti if (WRAP(clock_gettime)(CLOCK_MONOTONIC, &ts) == -1)
440b44da627Seric return;
441b44da627Seric
44299491d36Sderaadt if ((ts.tv_sec - asr->a_rtime) < RELOAD_DELAY && asr->a_rtime != 0)
443b44da627Seric return;
44499491d36Sderaadt asr->a_rtime = ts.tv_sec;
445b44da627Seric
44683d312d6Seric DPRINT("asr: checking for update of \"%s\"\n", _PATH_RESCONF);
44783d312d6Seric if (stat(_PATH_RESCONF, &st) == -1 ||
448b44da627Seric asr->a_mtime == st.st_mtime ||
449b44da627Seric (ac = asr_ctx_create()) == NULL)
450b44da627Seric return;
451b44da627Seric asr->a_mtime = st.st_mtime;
452b44da627Seric
45346ab4803Seric DPRINT("asr: reloading config file\n");
45483d312d6Seric if (asr_ctx_from_file(ac, _PATH_RESCONF) == -1) {
455b44da627Seric asr_ctx_free(ac);
456b44da627Seric return;
457b44da627Seric }
458b44da627Seric
459b44da627Seric asr_ctx_envopts(ac);
460b44da627Seric if (asr->a_ctx)
461253ef892Sderaadt _asr_ctx_unref(asr->a_ctx);
462b44da627Seric asr->a_ctx = ac;
463b44da627Seric }
464b44da627Seric
465b44da627Seric /*
466b44da627Seric * Construct a fully-qualified domain name for the given name and domain.
467b44da627Seric * If "name" ends with a '.' it is considered as a FQDN by itself.
468b44da627Seric * Otherwise, the domain, which must be a FQDN, is appended to "name" (it
469b44da627Seric * may have a leading dot which would be ignored). If the domain is null,
470b44da627Seric * then "." is used. Return the length of the constructed FQDN or (0) on
471b44da627Seric * error.
472b44da627Seric */
473b44da627Seric size_t
_asr_make_fqdn(const char * name,const char * domain,char * buf,size_t buflen)474253ef892Sderaadt _asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen)
475b44da627Seric {
476b44da627Seric size_t len;
477b44da627Seric
478b44da627Seric if (domain == NULL)
479b44da627Seric domain = ".";
480b44da627Seric else if ((len = strlen(domain)) == 0)
481b44da627Seric return (0);
482b44da627Seric else if (domain[len -1] != '.')
483b44da627Seric return (0);
484b44da627Seric
485b44da627Seric len = strlen(name);
486b44da627Seric if (len == 0) {
4876dde8a29Seric if (strlcpy(buf, domain, buflen) >= buflen)
4886dde8a29Seric return (0);
489b44da627Seric } else if (name[len - 1] != '.') {
490b44da627Seric if (domain[0] == '.')
491b44da627Seric domain += 1;
4926dde8a29Seric if (strlcpy(buf, name, buflen) >= buflen ||
4936dde8a29Seric strlcat(buf, ".", buflen) >= buflen ||
4946dde8a29Seric strlcat(buf, domain, buflen) >= buflen)
4956dde8a29Seric return (0);
496b44da627Seric } else {
4976dde8a29Seric if (strlcpy(buf, name, buflen) >= buflen)
4986dde8a29Seric return (0);
499b44da627Seric }
500b44da627Seric
501b44da627Seric return (strlen(buf));
502b44da627Seric }
503b44da627Seric
504b44da627Seric /*
505b44da627Seric * Count the dots in a string.
506b44da627Seric */
507b44da627Seric static int
asr_ndots(const char * s)508b44da627Seric asr_ndots(const char *s)
509b44da627Seric {
510b44da627Seric int n;
511b44da627Seric
512b44da627Seric for (n = 0; *s; s++)
513b44da627Seric if (*s == '.')
514b44da627Seric n += 1;
515b44da627Seric
516b44da627Seric return (n);
517b44da627Seric }
518b44da627Seric
519b44da627Seric /*
520b44da627Seric * Allocate a new empty context.
521b44da627Seric */
522b44da627Seric static struct asr_ctx *
asr_ctx_create(void)523b44da627Seric asr_ctx_create(void)
524b44da627Seric {
525b44da627Seric struct asr_ctx *ac;
526b44da627Seric
527b44da627Seric if ((ac = calloc(1, sizeof(*ac))) == NULL)
528b44da627Seric return (NULL);
529b44da627Seric
530b44da627Seric ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
531b44da627Seric ac->ac_refcount = 1;
532b44da627Seric ac->ac_ndots = 1;
533b44da627Seric ac->ac_family[0] = AF_INET;
534b44da627Seric ac->ac_family[1] = AF_INET6;
535b44da627Seric ac->ac_family[2] = -1;
536b44da627Seric
537b44da627Seric ac->ac_nscount = 0;
5387234c6e9Seric ac->ac_nstimeout = 5;
5397234c6e9Seric ac->ac_nsretries = 4;
540b44da627Seric
541b44da627Seric return (ac);
542b44da627Seric }
543b44da627Seric
544656b8d51Sderaadt struct asr_ctx *
_asr_no_resolver(void)545656b8d51Sderaadt _asr_no_resolver(void)
546656b8d51Sderaadt {
547656b8d51Sderaadt return asr_ctx_create();
548656b8d51Sderaadt }
549656b8d51Sderaadt
550b44da627Seric /*
551b44da627Seric * Add a search domain to the async context.
552b44da627Seric */
553b44da627Seric static int
asr_ctx_add_searchdomain(struct asr_ctx * ac,const char * domain)554b44da627Seric asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain)
555b44da627Seric {
556b44da627Seric char buf[MAXDNAME];
557b44da627Seric
558b44da627Seric if (ac->ac_domcount == ASR_MAXDOM)
559b44da627Seric return (-1);
560b44da627Seric
561253ef892Sderaadt if (_asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0)
562b44da627Seric return (-1);
563b44da627Seric
564b44da627Seric if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL)
565b44da627Seric return (0);
566b44da627Seric
567b44da627Seric ac->ac_domcount += 1;
568b44da627Seric
569b44da627Seric return (1);
570b44da627Seric }
571b44da627Seric
57295463336Seric static int
strsplit(char * line,char ** tokens,int ntokens)57395463336Seric strsplit(char *line, char **tokens, int ntokens)
57495463336Seric {
57595463336Seric int ntok;
57695463336Seric char *cp, **tp;
57795463336Seric
57895463336Seric for (cp = line, tp = tokens, ntok = 0;
57995463336Seric ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
58095463336Seric if (**tp != '\0') {
58195463336Seric tp++;
58295463336Seric ntok++;
58395463336Seric }
58495463336Seric
58595463336Seric return (ntok);
58695463336Seric }
58795463336Seric
588b44da627Seric /*
589b44da627Seric * Pass on a split config line.
590b44da627Seric */
591c3a0261fSeric static void
pass0(char ** tok,int n,struct asr_ctx * ac)592b44da627Seric pass0(char **tok, int n, struct asr_ctx *ac)
593b44da627Seric {
594b44da627Seric int i, j, d;
595b44da627Seric const char *e;
596b44da627Seric struct sockaddr_storage ss;
597b44da627Seric
598b44da627Seric if (!strcmp(tok[0], "nameserver")) {
599b44da627Seric if (ac->ac_nscount == ASR_MAXNS)
600c3a0261fSeric return;
601b44da627Seric if (n != 2)
602c3a0261fSeric return;
603b44da627Seric if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1]))
604c3a0261fSeric return;
605b44da627Seric if ((ac->ac_ns[ac->ac_nscount] = calloc(1, ss.ss_len)) == NULL)
606c3a0261fSeric return;
607b44da627Seric memmove(ac->ac_ns[ac->ac_nscount], &ss, ss.ss_len);
608b44da627Seric ac->ac_nscount += 1;
609b44da627Seric
610b44da627Seric } else if (!strcmp(tok[0], "domain")) {
611b44da627Seric if (n != 2)
612c3a0261fSeric return;
613b44da627Seric if (ac->ac_domain)
614c3a0261fSeric return;
615b44da627Seric ac->ac_domain = strdup(tok[1]);
616b44da627Seric
617b44da627Seric } else if (!strcmp(tok[0], "lookup")) {
618b44da627Seric /* ensure that each lookup is only given once */
619b44da627Seric for (i = 1; i < n; i++)
620b44da627Seric for (j = i + 1; j < n; j++)
621b44da627Seric if (!strcmp(tok[i], tok[j]))
622c3a0261fSeric return;
623b44da627Seric ac->ac_dbcount = 0;
624ddc6d4c5Seric for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) {
62559f710d8Sderaadt if (!strcmp(tok[i], "yp")) {
62659f710d8Sderaadt /* silently deprecated */
62759f710d8Sderaadt } else if (!strcmp(tok[i], "bind"))
628ddc6d4c5Seric ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS;
629ddc6d4c5Seric else if (!strcmp(tok[i], "file"))
630ddc6d4c5Seric ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE;
631b44da627Seric }
632b44da627Seric } else if (!strcmp(tok[0], "search")) {
633b44da627Seric /* resolv.conf says the last line wins */
634365b8ebfSeric for (i = 0; i < ASR_MAXDOM; i++) {
635b44da627Seric free(ac->ac_dom[i]);
636365b8ebfSeric ac->ac_dom[i] = NULL;
637365b8ebfSeric }
638b44da627Seric ac->ac_domcount = 0;
639b44da627Seric for (i = 1; i < n; i++)
640b44da627Seric asr_ctx_add_searchdomain(ac, tok[i]);
641b44da627Seric
642b44da627Seric } else if (!strcmp(tok[0], "family")) {
643b44da627Seric if (n == 1 || n > 3)
644c3a0261fSeric return;
645b44da627Seric for (i = 1; i < n; i++)
646b44da627Seric if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6"))
647c3a0261fSeric return;
648b44da627Seric for (i = 1; i < n; i++)
649b44da627Seric ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \
650b44da627Seric AF_INET6 : AF_INET;
651b44da627Seric ac->ac_family[i - 1] = -1;
652b44da627Seric
653b44da627Seric } else if (!strcmp(tok[0], "options")) {
654b44da627Seric for (i = 1; i < n; i++) {
655b44da627Seric if (!strcmp(tok[i], "tcp"))
656b44da627Seric ac->ac_options |= RES_USEVC;
6572aa4cd21Sjca else if (!strcmp(tok[i], "edns0"))
6582aa4cd21Sjca ac->ac_options |= RES_USE_EDNS0;
659b44da627Seric else if ((!strncmp(tok[i], "ndots:", 6))) {
660b44da627Seric e = NULL;
661b44da627Seric d = strtonum(tok[i] + 6, 1, 16, &e);
662b44da627Seric if (e == NULL)
663b44da627Seric ac->ac_ndots = d;
664931108e9Sjca } else if (!strcmp(tok[i], "trust-ad"))
665931108e9Sjca ac->ac_options |= RES_TRUSTAD;
666b44da627Seric }
667b44da627Seric }
668b44da627Seric }
669b44da627Seric
670b44da627Seric /*
671b44da627Seric * Setup an async context with the config specified in the string "str".
672b44da627Seric */
673b44da627Seric static int
asr_ctx_from_string(struct asr_ctx * ac,const char * str)674b44da627Seric asr_ctx_from_string(struct asr_ctx *ac, const char *str)
675b44da627Seric {
676931108e9Sjca struct sockaddr_in6 *sin6;
677931108e9Sjca struct sockaddr_in *sin;
678931108e9Sjca int i, trustad;
679b44da627Seric char buf[512], *ch;
680b44da627Seric
68181550ec8Seric asr_ctx_parse(ac, str);
682b44da627Seric
683b44da627Seric if (ac->ac_dbcount == 0) {
684b44da627Seric /* No lookup directive */
68581550ec8Seric asr_ctx_parse(ac, DEFAULT_LOOKUP);
686b44da627Seric }
687b44da627Seric
688b44da627Seric if (ac->ac_nscount == 0)
68981550ec8Seric asr_ctx_parse(ac, "nameserver 127.0.0.1");
690b44da627Seric
691b44da627Seric if (ac->ac_domain == NULL)
692b44da627Seric if (gethostname(buf, sizeof buf) == 0) {
693b44da627Seric ch = strchr(buf, '.');
694b44da627Seric if (ch)
695b44da627Seric ac->ac_domain = strdup(ch + 1);
696b44da627Seric else /* Assume root. see resolv.conf(5) */
697b44da627Seric ac->ac_domain = strdup("");
698b44da627Seric }
699b44da627Seric
700b44da627Seric /* If no search domain was specified, use the local subdomains */
701b44da627Seric if (ac->ac_domcount == 0)
702b44da627Seric for (ch = ac->ac_domain; ch; ) {
703b44da627Seric asr_ctx_add_searchdomain(ac, ch);
704b44da627Seric ch = strchr(ch, '.');
705b44da627Seric if (ch && asr_ndots(++ch) == 0)
706b44da627Seric break;
707b44da627Seric }
708b44da627Seric
709931108e9Sjca trustad = 1;
710931108e9Sjca for (i = 0; i < ac->ac_nscount && trustad; i++) {
711931108e9Sjca switch (ac->ac_ns[i]->sa_family) {
712931108e9Sjca case AF_INET:
713931108e9Sjca sin = (struct sockaddr_in *)ac->ac_ns[i];
714931108e9Sjca if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
715931108e9Sjca trustad = 0;
716931108e9Sjca break;
717931108e9Sjca case AF_INET6:
718931108e9Sjca sin6 = (struct sockaddr_in6 *)ac->ac_ns[i];
719931108e9Sjca if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
720931108e9Sjca trustad = 0;
721931108e9Sjca break;
722931108e9Sjca default:
723931108e9Sjca trustad = 0;
724931108e9Sjca break;
725931108e9Sjca }
726931108e9Sjca }
727931108e9Sjca if (trustad)
728931108e9Sjca ac->ac_options |= RES_TRUSTAD;
729931108e9Sjca
730b44da627Seric return (0);
731b44da627Seric }
732b44da627Seric
733b44da627Seric /*
734b44da627Seric * Setup the "ac" async context from the file at location "path".
735b44da627Seric */
736b44da627Seric static int
asr_ctx_from_file(struct asr_ctx * ac,const char * path)737b44da627Seric asr_ctx_from_file(struct asr_ctx *ac, const char *path)
738b44da627Seric {
739b44da627Seric FILE *cf;
740b44da627Seric char buf[4096];
741b44da627Seric ssize_t r;
742b44da627Seric
743241db059Sguenther cf = fopen(path, "re");
744b44da627Seric if (cf == NULL)
745b44da627Seric return (-1);
746b44da627Seric
747b44da627Seric r = fread(buf, 1, sizeof buf - 1, cf);
748b44da627Seric if (feof(cf) == 0) {
74946ab4803Seric DPRINT("asr: config file too long: \"%s\"\n", path);
750b44da627Seric r = -1;
751b44da627Seric }
752b44da627Seric fclose(cf);
753b44da627Seric if (r == -1)
754b44da627Seric return (-1);
755b44da627Seric buf[r] = '\0';
756b44da627Seric
757b44da627Seric return asr_ctx_from_string(ac, buf);
758b44da627Seric }
759b44da627Seric
760b44da627Seric /*
761c3a0261fSeric * Parse lines in the configuration string. For each one, split it into
762c3a0261fSeric * tokens and pass them to "pass0" for processing.
763b44da627Seric */
764b44da627Seric static int
asr_ctx_parse(struct asr_ctx * ac,const char * str)76581550ec8Seric asr_ctx_parse(struct asr_ctx *ac, const char *str)
766b44da627Seric {
767b44da627Seric size_t len;
768b44da627Seric const char *line;
769b44da627Seric char buf[1024];
77095463336Seric char *tok[10];
771b44da627Seric int ntok;
772b44da627Seric
773b44da627Seric line = str;
774b44da627Seric while (*line) {
775b44da627Seric len = strcspn(line, "\n\0");
776b44da627Seric if (len < sizeof buf) {
777b44da627Seric memmove(buf, line, len);
778b44da627Seric buf[len] = '\0';
779b44da627Seric } else
780b44da627Seric buf[0] = '\0';
781b44da627Seric line += len;
782b44da627Seric if (*line == '\n')
783b44da627Seric line++;
784b44da627Seric buf[strcspn(buf, ";#")] = '\0';
78595463336Seric if ((ntok = strsplit(buf, tok, 10)) == 0)
786b44da627Seric continue;
787b44da627Seric
788c3a0261fSeric pass0(tok, ntok, ac);
789b44da627Seric }
790b44da627Seric
791b44da627Seric return (0);
792b44da627Seric }
793b44da627Seric
794b44da627Seric /*
795b44da627Seric * Check for environment variables altering the configuration as described
796e5ef2ce9Ssthen * in resolv.conf(5). Although not documented there, this feature is disabled
797b44da627Seric * for setuid/setgid programs.
798b44da627Seric */
799b44da627Seric static void
asr_ctx_envopts(struct asr_ctx * ac)800b44da627Seric asr_ctx_envopts(struct asr_ctx *ac)
801b44da627Seric {
802b44da627Seric char buf[4096], *e;
803b44da627Seric size_t s;
804b44da627Seric
805b44da627Seric if (issetugid()) {
806b44da627Seric ac->ac_options |= RES_NOALIASES;
807b44da627Seric return;
808b44da627Seric }
809b44da627Seric
810b44da627Seric if ((e = getenv("RES_OPTIONS")) != NULL) {
811b44da627Seric strlcpy(buf, "options ", sizeof buf);
812b44da627Seric strlcat(buf, e, sizeof buf);
813b44da627Seric s = strlcat(buf, "\n", sizeof buf);
814b44da627Seric if (s < sizeof buf)
81581550ec8Seric asr_ctx_parse(ac, buf);
816b44da627Seric }
817b44da627Seric
818b44da627Seric if ((e = getenv("LOCALDOMAIN")) != NULL) {
819b44da627Seric strlcpy(buf, "search ", sizeof buf);
820b44da627Seric strlcat(buf, e, sizeof buf);
821b44da627Seric s = strlcat(buf, "\n", sizeof buf);
822b44da627Seric if (s < sizeof buf)
82381550ec8Seric asr_ctx_parse(ac, buf);
824b44da627Seric }
825b44da627Seric }
826b44da627Seric
827b44da627Seric /*
828b44da627Seric * Parse a resolv.conf(5) nameserver string into a sockaddr.
829b44da627Seric */
830b44da627Seric static int
asr_parse_nameserver(struct sockaddr * sa,const char * s)831b44da627Seric asr_parse_nameserver(struct sockaddr *sa, const char *s)
832b44da627Seric {
833b44da627Seric in_port_t portno = 53;
834b44da627Seric
835253ef892Sderaadt if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1)
836b44da627Seric return (-1);
837b44da627Seric
838b44da627Seric if (sa->sa_family == PF_INET)
839b44da627Seric ((struct sockaddr_in *)sa)->sin_port = htons(portno);
840b44da627Seric else if (sa->sa_family == PF_INET6)
841b44da627Seric ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno);
842b44da627Seric
843b44da627Seric return (0);
844b44da627Seric }
845b44da627Seric
846b44da627Seric /*
847b44da627Seric * Turn a (uncompressed) DNS domain name into a regular nul-terminated string
848b44da627Seric * where labels are separated by dots. The result is put into the "buf" buffer,
849b44da627Seric * truncated if it exceeds "max" chars. The function returns "buf".
850b44da627Seric */
851b44da627Seric char *
_asr_strdname(const char * _dname,char * buf,size_t max)852253ef892Sderaadt _asr_strdname(const char *_dname, char *buf, size_t max)
853b44da627Seric {
854b44da627Seric const unsigned char *dname = _dname;
855b44da627Seric char *res;
856*ce6743a3Snaddy size_t left, count;
857b44da627Seric
858b44da627Seric if (_dname[0] == 0) {
859b44da627Seric strlcpy(buf, ".", max);
860b44da627Seric return buf;
861b44da627Seric }
862b44da627Seric
863b44da627Seric res = buf;
864b44da627Seric left = max - 1;
865*ce6743a3Snaddy while (dname[0] && left) {
866b44da627Seric count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
867b44da627Seric memmove(buf, dname + 1, count);
868b44da627Seric dname += dname[0] + 1;
869b44da627Seric left -= count;
870b44da627Seric buf += count;
871b44da627Seric if (left) {
872b44da627Seric left -= 1;
873b44da627Seric *buf++ = '.';
874b44da627Seric }
875b44da627Seric }
876b44da627Seric buf[0] = 0;
877b44da627Seric
878b44da627Seric return (res);
879b44da627Seric }
880b44da627Seric
881b44da627Seric /*
882b44da627Seric * Read and split the next line from the given namedb file.
883b44da627Seric * Return -1 on error, or put the result in the "tokens" array of
884b44da627Seric * size "ntoken" and returns the number of token on the line.
885b44da627Seric */
886b44da627Seric int
_asr_parse_namedb_line(FILE * file,char ** tokens,int ntoken,char * lbuf,size_t sz)887253ef892Sderaadt _asr_parse_namedb_line(FILE *file, char **tokens, int ntoken, char *lbuf, size_t sz)
888b44da627Seric {
889b44da627Seric size_t len;
89095463336Seric char *buf;
891b44da627Seric int ntok;
892b44da627Seric
893b44da627Seric again:
894b44da627Seric if ((buf = fgetln(file, &len)) == NULL)
895b44da627Seric return (-1);
896b44da627Seric
897f108579bSeric if (len >= sz)
898f108579bSeric goto again;
899f108579bSeric
900b44da627Seric if (buf[len - 1] == '\n')
901b44da627Seric len--;
902f108579bSeric else {
903f108579bSeric memcpy(lbuf, buf, len);
904f108579bSeric buf = lbuf;
905f108579bSeric }
906b44da627Seric
907b44da627Seric buf[len] = '\0';
908b44da627Seric buf[strcspn(buf, "#")] = '\0';
90995463336Seric if ((ntok = strsplit(buf, tokens, ntoken)) == 0)
910b44da627Seric goto again;
911b44da627Seric
912b44da627Seric return (ntok);
913b44da627Seric }
914b44da627Seric
915b44da627Seric /*
916b44da627Seric * Update the async context so that it uses the next configured DB.
917b44da627Seric * Return 0 on success, or -1 if no more DBs is available.
918b44da627Seric */
919b44da627Seric int
_asr_iter_db(struct asr_query * as)920253ef892Sderaadt _asr_iter_db(struct asr_query *as)
921b44da627Seric {
922b44da627Seric if (as->as_db_idx >= as->as_ctx->ac_dbcount) {
92346ab4803Seric DPRINT("asr_iter_db: done\n");
924b44da627Seric return (-1);
925b44da627Seric }
926b44da627Seric
927b44da627Seric as->as_db_idx += 1;
92846ab4803Seric DPRINT("asr_iter_db: %i\n", as->as_db_idx);
92946ab4803Seric
930b44da627Seric return (0);
931b44da627Seric }
932