GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: lib/libc/asr/asr.c Lines: 205 363 56.5 %
Date: 2017-11-07 Branches: 101 285 35.4 %

Line Branch Exec Source
1
/*	$OpenBSD: asr.c,v 1.57 2017/02/27 10:44:46 jca Exp $	*/
2
/*
3
 * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
#include <sys/types.h>
19
#include <sys/socket.h>
20
#include <sys/stat.h>
21
#include <netinet/in.h>
22
#include <arpa/inet.h>
23
#include <arpa/nameser.h>
24
#include <netdb.h>
25
26
#include <asr.h>
27
#include <errno.h>
28
#include <fcntl.h>
29
#include <resolv.h>
30
#include <poll.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <unistd.h>
35
#include <limits.h>
36
37
#include "asr_private.h"
38
39
#include "thread_private.h"
40
41
#define DEFAULT_CONF		"lookup file\n"
42
#define DEFAULT_LOOKUP		"lookup bind file"
43
44
#define RELOAD_DELAY		15 /* seconds */
45
46
static void asr_check_reload(struct asr *);
47
static struct asr_ctx *asr_ctx_create(void);
48
static void asr_ctx_ref(struct asr_ctx *);
49
static void asr_ctx_free(struct asr_ctx *);
50
static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *);
51
static int asr_ctx_from_file(struct asr_ctx *, const char *);
52
static int asr_ctx_from_string(struct asr_ctx *, const char *);
53
static int asr_ctx_parse(struct asr_ctx *, const char *);
54
static int asr_parse_nameserver(struct sockaddr *, const char *);
55
static int asr_ndots(const char *);
56
static void pass0(char **, int, struct asr_ctx *);
57
static int strsplit(char *, char **, int);
58
static void asr_ctx_envopts(struct asr_ctx *);
59
static void *__THREAD_NAME(_asr);
60
61
static struct asr *_asr = NULL;
62
63
/* Allocate and configure an async "resolver". */
64
static void *
65
_asr_resolver(void)
66
{
67
	static int	 init = 0;
68
	struct asr	*asr;
69
70
24
	if (init == 0) {
71
#ifdef DEBUG
72
		if (getenv("ASR_DEBUG"))
73
			_asr_debug = stderr;
74
#endif
75
12
		init = 1;
76
12
	}
77
78
12
	if ((asr = calloc(1, sizeof(*asr))) == NULL)
79
		goto fail;
80
81
12
	asr_check_reload(asr);
82
12
	if (asr->a_ctx == NULL) {
83
		if ((asr->a_ctx = asr_ctx_create()) == NULL)
84
			goto fail;
85
		if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1)
86
			goto fail;
87
		asr_ctx_envopts(asr->a_ctx);
88
	}
89
90
#ifdef DEBUG
91
	_asr_dump_config(_asr_debug, asr);
92
#endif
93
12
	return (asr);
94
95
    fail:
96
	if (asr) {
97
		if (asr->a_ctx)
98
			asr_ctx_free(asr->a_ctx);
99
		free(asr);
100
	}
101
102
	return (NULL);
103
12
}
104
105
/*
106
 * Free the "asr" async resolver (or the thread-local resolver if NULL).
107
 * Drop the reference to the current context.
108
 */
109
void
110
_asr_resolver_done(void *arg)
111
{
112
	struct asr *asr = arg;
113
	struct asr **priv;
114
115
	if (asr == NULL) {
116
		priv = _THREAD_PRIVATE(_asr, _asr, &_asr);
117
		if (*priv == NULL)
118
			return;
119
		asr = *priv;
120
		*priv = NULL;
121
	}
122
123
	_asr_ctx_unref(asr->a_ctx);
124
	free(asr);
125
}
126
127
/*
128
 * Cancel an async query.
129
 */
130
void
131
asr_abort(struct asr_query *as)
132
{
133
	_asr_async_free(as);
134
}
135
136
/*
137
 * Resume the "as" async query resolution.  Return one of ASYNC_COND,
138
 * or ASYNC_DONE and put query-specific return values in the user-allocated
139
 * memory at "ar".
140
 */
141
int
142
asr_run(struct asr_query *as, struct asr_result *ar)
143
{
144
68
	int	r, saved_errno = errno;
145
146
	DPRINT("asr: asr_run(%p, %p) %s ctx=[%p]\n", as, ar,
147
	    _asr_querystr(as->as_type), as->as_ctx);
148
34
	r = as->as_run(as, ar);
149
150
	DPRINT("asr: asr_run(%p, %p) -> %s", as, ar, _asr_transitionstr(r));
151
#ifdef DEBUG
152
	if (r == ASYNC_COND)
153
#endif
154
		DPRINT(" fd=%i timeout=%i", ar->ar_fd, ar->ar_timeout);
155
	DPRINT("\n");
156
34
	if (r == ASYNC_DONE)
157
34
		_asr_async_free(as);
158
159
34
	errno = saved_errno;
160
161
34
	return (r);
162
}
163
DEF_WEAK(asr_run);
164
165
/*
166
 * Same as above, but run in a loop that handles the fd conditions result.
167
 */
168
int
169
asr_run_sync(struct asr_query *as, struct asr_result *ar)
170
{
171
68
	struct pollfd	 fds[1];
172
34
	struct timespec	 pollstart, pollend, elapsed;
173
34
	int		 timeout, r, p, saved_errno = errno;
174
175
68
	while ((r = asr_run(as, ar)) == ASYNC_COND) {
176
		fds[0].fd = ar->ar_fd;
177
		fds[0].events = (ar->ar_cond == ASR_WANT_READ) ? POLLIN:POLLOUT;
178
179
		timeout = ar->ar_timeout;
180
	again:
181
		if (clock_gettime(CLOCK_MONOTONIC, &pollstart))
182
			break;
183
		p = poll(fds, 1, timeout);
184
		if (p == -1 && errno == EINTR) {
185
			if (clock_gettime(CLOCK_MONOTONIC, &pollend))
186
				break;
187
188
			timespecsub(&pollend, &pollstart, &elapsed);
189
			timeout -= (elapsed.tv_sec * 1000) +
190
			    (elapsed.tv_nsec / 1000000);
191
			if (timeout < 1)
192
				break;
193
			goto again;
194
		}
195
196
		/*
197
		 * Otherwise, just ignore the error and let asr_run()
198
		 * catch the failure.
199
		 */
200
	}
201
202
34
	errno = saved_errno;
203
204
34
	return (r);
205
34
}
206
DEF_WEAK(asr_run_sync);
207
208
/*
209
 * Create a new async request of the given "type" on the async context "ac".
210
 * Take a reference on it so it does not gets deleted while the async query
211
 * is running.
212
 */
213
struct asr_query *
214
_asr_async_new(struct asr_ctx *ac, int type)
215
{
216
	struct asr_query	*as;
217
218
	DPRINT("asr: asr_async_new(ctx=%p) type=%i refcount=%i\n", ac, type,
219
	    ac ? ac->ac_refcount : 0);
220

102
	if (ac == NULL || (as = calloc(1, sizeof(*as))) == NULL)
221
		return (NULL);
222
223
34
	ac->ac_refcount += 1;
224
34
	as->as_ctx = ac;
225
34
	as->as_fd = -1;
226
34
	as->as_type = type;
227
34
	as->as_state = ASR_STATE_INIT;
228
229
34
	return (as);
230
34
}
231
232
/*
233
 * Free an async query and unref the associated context.
234
 */
235
void
236
_asr_async_free(struct asr_query *as)
237
{
238
	DPRINT("asr: asr_async_free(%p)\n", as);
239
240
68
	if (as->as_subq)
241
		_asr_async_free(as->as_subq);
242
243


68
	switch (as->as_type) {
244
	case ASR_SEND:
245
		if (as->as_fd != -1)
246
			close(as->as_fd);
247
		if (as->as.dns.obuf && !(as->as_flags & ASYNC_EXTOBUF))
248
			free(as->as.dns.obuf);
249
		if (as->as.dns.ibuf)
250
			free(as->as.dns.ibuf);
251
		if (as->as.dns.dname)
252
			free(as->as.dns.dname);
253
		break;
254
255
	case ASR_SEARCH:
256
		if (as->as.search.name)
257
			free(as->as.search.name);
258
		break;
259
260
	case ASR_GETRRSETBYNAME:
261
		if (as->as.rrset.name)
262
			free(as->as.rrset.name);
263
		break;
264
265
	case ASR_GETHOSTBYNAME:
266
	case ASR_GETHOSTBYADDR:
267
		if (as->as.hostnamadr.name)
268
			free(as->as.hostnamadr.name);
269
		break;
270
271
	case ASR_GETNETBYNAME:
272
	case ASR_GETNETBYADDR:
273
		if (as->as.netnamadr.name)
274
			free(as->as.netnamadr.name);
275
		break;
276
277
	case ASR_GETADDRINFO:
278
34
		if (as->as.ai.aifirst)
279
			freeaddrinfo(as->as.ai.aifirst);
280
34
		if (as->as.ai.hostname)
281
34
			free(as->as.ai.hostname);
282
34
		if (as->as.ai.servname)
283
34
			free(as->as.ai.servname);
284
34
		if (as->as.ai.fqdn)
285
			free(as->as.ai.fqdn);
286
		break;
287
288
	case ASR_GETNAMEINFO:
289
		break;
290
	}
291
292
34
	_asr_ctx_unref(as->as_ctx);
293
34
	free(as);
294
34
}
295
296
/*
297
 * Get a context from the given resolver. This takes a new reference to
298
 * the returned context, which *must* be explicitely dropped when done
299
 * using this context.
300
 */
301
struct asr_ctx *
302
_asr_use_resolver(void *arg)
303
{
304
48
	struct asr *asr = arg;
305
	struct asr **priv;
306
307
24
	if (asr == NULL) {
308
		DPRINT("using thread-local resolver\n");
309
48
		priv = _THREAD_PRIVATE(_asr, _asr, &_asr);
310
24
		if (*priv == NULL) {
311
			DPRINT("setting up thread-local resolver\n");
312
12
			*priv = _asr_resolver();
313
12
		}
314
24
		asr = *priv;
315
24
	}
316
24
	if (asr != NULL) {
317
24
		asr_check_reload(asr);
318
24
		asr_ctx_ref(asr->a_ctx);
319
24
		return (asr->a_ctx);
320
	}
321
	return (NULL);
322
24
}
323
324
static void
325
asr_ctx_ref(struct asr_ctx *ac)
326
{
327
	DPRINT("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", ac, ac->ac_refcount);
328
48
	ac->ac_refcount += 1;
329
24
}
330
331
/*
332
 * Drop a reference to an async context, freeing it if the reference
333
 * count drops to 0.
334
 */
335
void
336
_asr_ctx_unref(struct asr_ctx *ac)
337
{
338
	DPRINT("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", ac,
339
	    ac ? ac->ac_refcount : 0);
340
160
	if (ac == NULL)
341
		return;
342
80
	if (--ac->ac_refcount)
343
		return;
344
345
22
	asr_ctx_free(ac);
346
102
}
347
348
static void
349
asr_ctx_free(struct asr_ctx *ac)
350
{
351
	int i;
352
353
44
	if (ac->ac_domain)
354
		free(ac->ac_domain);
355
264
	for (i = 0; i < ASR_MAXNS; i++)
356
110
		free(ac->ac_ns[i]);
357
484
	for (i = 0; i < ASR_MAXDOM; i++)
358
220
		free(ac->ac_dom[i]);
359
360
22
	free(ac);
361
22
}
362
363
/*
364
 * Reload the configuration file if it has changed on disk.
365
 */
366
static void
367
asr_check_reload(struct asr *asr)
368
{
369
	struct asr_ctx	*ac;
370
72
	struct stat	 st;
371
36
	struct timespec	 ts;
372
	pid_t		 pid;
373
374
36
	pid = getpid();
375
36
	if (pid != asr->a_pid) {
376
12
		asr->a_pid = pid;
377
12
		asr->a_rtime = 0;
378
12
	}
379
380
36
	if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1)
381
		return;
382
383

60
	if ((ts.tv_sec - asr->a_rtime) < RELOAD_DELAY && asr->a_rtime != 0)
384
24
		return;
385
12
	asr->a_rtime = ts.tv_sec;
386
387
	DPRINT("asr: checking for update of \"%s\"\n", _PATH_RESCONF);
388

24
	if (stat(_PATH_RESCONF, &st) == -1 ||
389
12
	    asr->a_mtime == st.st_mtime ||
390
12
	    (ac = asr_ctx_create()) == NULL)
391
		return;
392
12
	asr->a_mtime = st.st_mtime;
393
394
	DPRINT("asr: reloading config file\n");
395
12
	if (asr_ctx_from_file(ac, _PATH_RESCONF) == -1) {
396
		asr_ctx_free(ac);
397
		return;
398
	}
399
400
12
	asr_ctx_envopts(ac);
401
12
	if (asr->a_ctx)
402
		_asr_ctx_unref(asr->a_ctx);
403
12
	asr->a_ctx = ac;
404
48
}
405
406
/*
407
 * Construct a fully-qualified domain name for the given name and domain.
408
 * If "name" ends with a '.' it is considered as a FQDN by itself.
409
 * Otherwise, the domain, which must be a FQDN, is appended to "name" (it
410
 * may have a leading dot which would be ignored). If the domain is null,
411
 * then "." is used. Return the length of the constructed FQDN or (0) on
412
 * error.
413
 */
414
size_t
415
_asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen)
416
{
417
	size_t	len;
418
419
24
	if (domain == NULL)
420
12
		domain = ".";
421
	else if ((len = strlen(domain)) == 0)
422
		return (0);
423
	else if (domain[len -1] != '.')
424
		return (0);
425
426
12
	len = strlen(name);
427
12
	if (len == 0) {
428
		if (strlcpy(buf, domain, buflen) >= buflen)
429
			return (0);
430
12
	} else if (name[len - 1] !=  '.') {
431
12
		if (domain[0] == '.')
432
12
			domain += 1;
433

24
		if (strlcpy(buf, name, buflen) >= buflen ||
434
12
		    strlcat(buf, ".", buflen) >= buflen ||
435
12
		    strlcat(buf, domain, buflen) >= buflen)
436
			return (0);
437
	} else {
438
		if (strlcpy(buf, name, buflen) >= buflen)
439
			return (0);
440
	}
441
442
12
	return (strlen(buf));
443
12
}
444
445
/*
446
 * Count the dots in a string.
447
 */
448
static int
449
asr_ndots(const char *s)
450
{
451
	int n;
452
453
	for (n = 0; *s; s++)
454
		if (*s == '.')
455
			n += 1;
456
457
	return (n);
458
}
459
460
/*
461
 * Allocate a new empty context.
462
 */
463
static struct asr_ctx *
464
asr_ctx_create(void)
465
{
466
	struct asr_ctx	*ac;
467
468
68
	if ((ac = calloc(1, sizeof(*ac))) == NULL)
469
		return (NULL);
470
471
34
	ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH;
472
34
	ac->ac_refcount = 1;
473
34
	ac->ac_ndots = 1;
474
34
	ac->ac_family[0] = AF_INET;
475
34
	ac->ac_family[1] = AF_INET6;
476
34
	ac->ac_family[2] = -1;
477
478
34
	ac->ac_nscount = 0;
479
34
	ac->ac_nstimeout = 5;
480
34
	ac->ac_nsretries = 4;
481
482
34
	return (ac);
483
34
}
484
485
struct asr_ctx *
486
_asr_no_resolver(void)
487
{
488
44
	return asr_ctx_create();
489
}
490
491
/*
492
 * Add a search domain to the async context.
493
 */
494
static int
495
asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain)
496
{
497
24
	char buf[MAXDNAME];
498
499
12
	if (ac->ac_domcount == ASR_MAXDOM)
500
		return (-1);
501
502
12
	if (_asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0)
503
		return (-1);
504
505
12
	if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL)
506
		return (0);
507
508
12
	ac->ac_domcount += 1;
509
510
12
	return (1);
511
12
}
512
513
static int
514
strsplit(char *line, char **tokens, int ntokens)
515
{
516
	int	ntok;
517
144
	char	*cp, **tp;
518
519
288
	for (cp = line, tp = tokens, ntok = 0;
520
432
	    ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
521
144
		if (**tp != '\0') {
522
132
			tp++;
523
132
			ntok++;
524
132
		}
525
526
72
	return (ntok);
527
72
}
528
529
/*
530
 * Pass on a split config line.
531
 */
532
static void
533
pass0(char **tok, int n, struct asr_ctx *ac)
534
{
535
	int		 i, j, d;
536
120
	const char	*e;
537
60
	struct sockaddr_storage	ss;
538
539
60
	if (!strcmp(tok[0], "nameserver")) {
540
36
		if (ac->ac_nscount == ASR_MAXNS)
541
			return;
542
36
		if (n != 2)
543
			return;
544
36
		if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1]))
545
			return;
546
36
		if ((ac->ac_ns[ac->ac_nscount] = calloc(1, ss.ss_len)) == NULL)
547
			return;
548
36
		memmove(ac->ac_ns[ac->ac_nscount], &ss, ss.ss_len);
549
36
		ac->ac_nscount += 1;
550
551
60
	} else if (!strcmp(tok[0], "domain")) {
552
		if (n != 2)
553
			return;
554
		if (ac->ac_domain)
555
			return;
556
		ac->ac_domain = strdup(tok[1]);
557
558
24
	} else if (!strcmp(tok[0], "lookup")) {
559
		/* ensure that each lookup is only given once */
560
72
		for (i = 1; i < n; i++)
561
72
			for (j = i + 1; j < n; j++)
562
12
				if (!strcmp(tok[i], tok[j]))
563
					return;
564
12
		ac->ac_dbcount = 0;
565

96
		for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) {
566
24
			if (!strcmp(tok[i], "yp")) {
567
				/* silently deprecated */
568
24
			} else if (!strcmp(tok[i], "bind"))
569
12
				ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS;
570
12
			else if (!strcmp(tok[i], "file"))
571
12
				ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE;
572
		}
573
12
	} else if (!strcmp(tok[0], "search")) {
574
		/* resolv.conf says the last line wins */
575
264
		for (i = 0; i < ASR_MAXDOM; i++) {
576
120
			free(ac->ac_dom[i]);
577
120
			ac->ac_dom[i] = NULL;
578
		}
579
12
		ac->ac_domcount = 0;
580
48
		for (i = 1; i < n; i++)
581
12
			asr_ctx_add_searchdomain(ac, tok[i]);
582
583
	} else if (!strcmp(tok[0], "family")) {
584
		if (n == 1 || n > 3)
585
			return;
586
		for (i = 1; i < n; i++)
587
			if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6"))
588
				return;
589
		for (i = 1; i < n; i++)
590
			ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \
591
			    AF_INET6 : AF_INET;
592
		ac->ac_family[i - 1] = -1;
593
594
	} else if (!strcmp(tok[0], "options")) {
595
		for (i = 1; i < n; i++) {
596
			if (!strcmp(tok[i], "tcp"))
597
				ac->ac_options |= RES_USEVC;
598
			else if (!strcmp(tok[i], "edns0"))
599
				ac->ac_options |= RES_USE_EDNS0;
600
			else if ((!strncmp(tok[i], "ndots:", 6))) {
601
				e = NULL;
602
				d = strtonum(tok[i] + 6, 1, 16, &e);
603
				if (e == NULL)
604
					ac->ac_ndots = d;
605
			}
606
		}
607
	}
608
120
}
609
610
/*
611
 * Setup an async context with the config specified in the string "str".
612
 */
613
static int
614
asr_ctx_from_string(struct asr_ctx *ac, const char *str)
615
{
616
24
	char		 buf[512], *ch;
617
618
12
	asr_ctx_parse(ac, str);
619
620
12
	if (ac->ac_dbcount == 0) {
621
		/* No lookup directive */
622
		asr_ctx_parse(ac, DEFAULT_LOOKUP);
623
	}
624
625
12
	if (ac->ac_nscount == 0)
626
		asr_ctx_parse(ac, "nameserver 127.0.0.1");
627
628
12
	if (ac->ac_domain == NULL)
629
12
		if (gethostname(buf, sizeof buf) == 0) {
630
12
			ch = strchr(buf, '.');
631
12
			if (ch)
632
12
				ac->ac_domain = strdup(ch + 1);
633
			else /* Assume root. see resolv.conf(5) */
634
				ac->ac_domain = strdup("");
635
12
		}
636
637
	/* If no search domain was specified, use the local subdomains */
638
12
	if (ac->ac_domcount == 0)
639
		for (ch = ac->ac_domain; ch; ) {
640
			asr_ctx_add_searchdomain(ac, ch);
641
			ch = strchr(ch, '.');
642
			if (ch && asr_ndots(++ch) == 0)
643
				break;
644
		}
645
646
12
	return (0);
647
12
}
648
649
/*
650
 * Setup the "ac" async context from the file at location "path".
651
 */
652
static int
653
asr_ctx_from_file(struct asr_ctx *ac, const char *path)
654
{
655
	FILE	*cf;
656
24
	char	 buf[4096];
657
	ssize_t	 r;
658
659
12
	cf = fopen(path, "re");
660
12
	if (cf == NULL)
661
		return (-1);
662
663
12
	r = fread(buf, 1, sizeof buf - 1, cf);
664

36
	if (feof(cf) == 0) {
665
		DPRINT("asr: config file too long: \"%s\"\n", path);
666
		r = -1;
667
	}
668
12
	fclose(cf);
669
12
	if (r == -1)
670
		return (-1);
671
12
	buf[r] = '\0';
672
673
12
	return asr_ctx_from_string(ac, buf);
674
12
}
675
676
/*
677
 * Parse lines in the configuration string. For each one, split it into
678
 * tokens and pass them to "pass0" for processing.
679
 */
680
static int
681
asr_ctx_parse(struct asr_ctx *ac, const char *str)
682
{
683
	size_t		 len;
684
	const char	*line;
685
24
	char		 buf[1024];
686
12
	char		*tok[10];
687
	int		 ntok;
688
689
	line = str;
690
156
	while (*line) {
691
72
		len = strcspn(line, "\n\0");
692
72
		if (len < sizeof buf) {
693
72
			memmove(buf, line, len);
694
72
			buf[len] = '\0';
695
72
		} else
696
			buf[0] = '\0';
697
72
		line += len;
698
72
		if (*line == '\n')
699
72
			line++;
700
72
		buf[strcspn(buf, ";#")] = '\0';
701
72
		if ((ntok = strsplit(buf, tok, 10)) == 0)
702
			continue;
703
704
60
		pass0(tok, ntok, ac);
705
	}
706
707
12
	return (0);
708
12
}
709
710
/*
711
 * Check for environment variables altering the configuration as described
712
 * in resolv.conf(5).  Although not documented there, this feature is disabled
713
 * for setuid/setgid programs.
714
 */
715
static void
716
asr_ctx_envopts(struct asr_ctx *ac)
717
{
718
24
	char	buf[4096], *e;
719
	size_t	s;
720
721
12
	if (issetugid()) {
722
		ac->ac_options |= RES_NOALIASES;
723
		return;
724
	}
725
726
12
	if ((e = getenv("RES_OPTIONS")) != NULL) {
727
		strlcpy(buf, "options ", sizeof buf);
728
		strlcat(buf, e, sizeof buf);
729
		s = strlcat(buf, "\n", sizeof buf);
730
		if (s < sizeof buf)
731
			asr_ctx_parse(ac, buf);
732
	}
733
734
12
	if ((e = getenv("LOCALDOMAIN")) != NULL) {
735
		strlcpy(buf, "search ", sizeof buf);
736
		strlcat(buf, e, sizeof buf);
737
		s = strlcat(buf, "\n", sizeof buf);
738
		if (s < sizeof buf)
739
			asr_ctx_parse(ac, buf);
740
	}
741
24
}
742
743
/*
744
 * Parse a resolv.conf(5) nameserver string into a sockaddr.
745
 */
746
static int
747
asr_parse_nameserver(struct sockaddr *sa, const char *s)
748
{
749
	in_port_t	 portno = 53;
750
751
72
	if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1)
752
		return (-1);
753
754
36
	if (sa->sa_family == PF_INET)
755
36
		((struct sockaddr_in *)sa)->sin_port = htons(portno);
756
	else if (sa->sa_family == PF_INET6)
757
		((struct sockaddr_in6 *)sa)->sin6_port = htons(portno);
758
759
36
	return (0);
760
36
}
761
762
/*
763
 * Turn a (uncompressed) DNS domain name into a regular nul-terminated string
764
 * where labels are separated by dots. The result is put into the "buf" buffer,
765
 * truncated if it exceeds "max" chars. The function returns "buf".
766
 */
767
char *
768
_asr_strdname(const char *_dname, char *buf, size_t max)
769
{
770
	const unsigned char *dname = _dname;
771
	char	*res;
772
	size_t	 left, n, count;
773
774
	if (_dname[0] == 0) {
775
		strlcpy(buf, ".", max);
776
		return buf;
777
	}
778
779
	res = buf;
780
	left = max - 1;
781
	for (n = 0; dname[0] && left; n += dname[0]) {
782
		count = (dname[0] < (left - 1)) ? dname[0] : (left - 1);
783
		memmove(buf, dname + 1, count);
784
		dname += dname[0] + 1;
785
		left -= count;
786
		buf += count;
787
		if (left) {
788
			left -= 1;
789
			*buf++ = '.';
790
		}
791
	}
792
	buf[0] = 0;
793
794
	return (res);
795
}
796
797
/*
798
 * Read and split the next line from the given namedb file.
799
 * Return -1 on error, or put the result in the "tokens" array of
800
 * size "ntoken" and returns the number of token on the line.
801
 */
802
int
803
_asr_parse_namedb_line(FILE *file, char **tokens, int ntoken, char *lbuf, size_t sz)
804
{
805
	size_t	  len;
806
	char	 *buf;
807
	int	  ntok;
808
809
    again:
810
	if ((buf = fgetln(file, &len)) == NULL)
811
		return (-1);
812
813
	if (len >= sz)
814
		goto again;
815
816
	if (buf[len - 1] == '\n')
817
		len--;
818
	else {
819
		memcpy(lbuf, buf, len);
820
		buf = lbuf;
821
	}
822
823
	buf[len] = '\0';
824
	buf[strcspn(buf, "#")] = '\0';
825
	if ((ntok = strsplit(buf, tokens, ntoken)) == 0)
826
		goto again;
827
828
	return (ntok);
829
}
830
831
/*
832
 * Update the async context so that it uses the next configured DB.
833
 * Return 0 on success, or -1 if no more DBs is available.
834
 */
835
int
836
_asr_iter_db(struct asr_query *as)
837
{
838
	if (as->as_db_idx >= as->as_ctx->ac_dbcount) {
839
		DPRINT("asr_iter_db: done\n");
840
		return (-1);
841
	}
842
843
	as->as_db_idx += 1;
844
	DPRINT("asr_iter_db: %i\n", as->as_db_idx);
845
846
	return (0);
847
}