xref: /openbsd-src/lib/libc/asr/getaddrinfo_async.c (revision d216d6b1290c53cb63da2778d039467b2c88efcf)
1 /*	$OpenBSD: getaddrinfo_async.c,v 1.26 2014/03/26 18:13:15 eric Exp $	*/
2 /*
3  * Copyright (c) 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/uio.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <arpa/nameser.h>
24 #ifdef YP
25 #include <rpc/rpc.h>
26 #include <rpcsvc/yp.h>
27 #include <rpcsvc/ypclnt.h>
28 #include "ypinternal.h"
29 #endif
30 #include <netdb.h>
31 
32 #include <asr.h>
33 #include <err.h>
34 #include <errno.h>
35 #include <resolv.h> /* for res_hnok */
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 
40 #include "asr_private.h"
41 
42 struct match {
43 	int family;
44 	int socktype;
45 	int protocol;
46 };
47 
48 static int getaddrinfo_async_run(struct asr_query *, struct asr_result *);
49 static int get_port(const char *, const char *, int);
50 static int iter_family(struct asr_query *, int);
51 static int iter_domain(struct asr_query *, const char *, char *, size_t);
52 static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *);
53 static int addrinfo_from_file(struct asr_query *, int,  FILE *);
54 static int addrinfo_from_pkt(struct asr_query *, char *, size_t);
55 #ifdef YP
56 static int addrinfo_from_yp(struct asr_query *, int, char *);
57 #endif
58 
59 static const struct match matches[] = {
60 	{ PF_INET,	SOCK_DGRAM,	IPPROTO_UDP	},
61 	{ PF_INET,	SOCK_STREAM,	IPPROTO_TCP	},
62 	{ PF_INET,	SOCK_RAW,	0		},
63 	{ PF_INET6,	SOCK_DGRAM,	IPPROTO_UDP	},
64 	{ PF_INET6,	SOCK_STREAM,	IPPROTO_TCP	},
65 	{ PF_INET6,	SOCK_RAW,	0		},
66 	{ -1,		0,		0,		},
67 };
68 
69 #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC)
70 #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0)
71 /* Do not match SOCK_RAW unless explicitely specified */
72 #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \
73 				matches[(b)].socktype != SOCK_RAW))
74 
75 enum {
76 	DOM_INIT,
77 	DOM_DOMAIN,
78 	DOM_DONE
79 };
80 
81 struct asr_query *
82 getaddrinfo_async(const char *hostname, const char *servname,
83 	const struct addrinfo *hints, void *asr)
84 {
85 	struct asr_ctx		*ac;
86 	struct asr_query	*as;
87 	char			 alias[MAXDNAME];
88 
89 	ac = asr_use_resolver(asr);
90 	if ((as = asr_async_new(ac, ASR_GETADDRINFO)) == NULL)
91 		goto abort; /* errno set */
92 	as->as_run = getaddrinfo_async_run;
93 
94 	if (hostname) {
95 		if (asr_hostalias(ac, hostname, alias, sizeof(alias)))
96 			hostname = alias;
97 		if ((as->as.ai.hostname = strdup(hostname)) == NULL)
98 			goto abort; /* errno set */
99 	}
100 	if (servname && (as->as.ai.servname = strdup(servname)) == NULL)
101 		goto abort; /* errno set */
102 	if (hints)
103 		memmove(&as->as.ai.hints, hints, sizeof *hints);
104 	else {
105 		memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints);
106 		as->as.ai.hints.ai_family = PF_UNSPEC;
107 	}
108 
109 	asr_ctx_unref(ac);
110 	return (as);
111     abort:
112 	if (as)
113 		asr_async_free(as);
114 	asr_ctx_unref(ac);
115 	return (NULL);
116 }
117 
118 static int
119 getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar)
120 {
121 #ifdef YP
122 	static char	*domain = NULL;
123 	char		*res;
124 	int		 len;
125 	char		 *name;
126 #endif
127 	char		 fqdn[MAXDNAME];
128 	const char	*str;
129 	struct addrinfo	*ai;
130 	int		 i, family, r;
131 	FILE		*f;
132 	union {
133 		struct sockaddr		sa;
134 		struct sockaddr_in	sain;
135 		struct sockaddr_in6	sain6;
136 	} sa;
137 
138     next:
139 	switch (as->as_state) {
140 
141 	case ASR_STATE_INIT:
142 
143 		/*
144 		 * First, make sure the parameters are valid.
145 		 */
146 
147 		as->as_count = 0;
148 
149 		if (as->as.ai.hostname == NULL &&
150 		    as->as.ai.servname == NULL) {
151 			ar->ar_gai_errno = EAI_NONAME;
152 			async_set_state(as, ASR_STATE_HALT);
153 			break;
154 		}
155 
156 		ai = &as->as.ai.hints;
157 
158 		if (ai->ai_addrlen ||
159 		    ai->ai_canonname ||
160 		    ai->ai_addr ||
161 		    ai->ai_next) {
162 			ar->ar_gai_errno = EAI_BADHINTS;
163 			async_set_state(as, ASR_STATE_HALT);
164 			break;
165 		}
166 
167 		if (ai->ai_flags & ~AI_MASK ||
168 		    (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) {
169 			ar->ar_gai_errno = EAI_BADFLAGS;
170 			async_set_state(as, ASR_STATE_HALT);
171 			break;
172 		}
173 
174 		if (ai->ai_family != PF_UNSPEC &&
175 		    ai->ai_family != PF_INET &&
176 		    ai->ai_family != PF_INET6) {
177 			ar->ar_gai_errno = EAI_FAMILY;
178 			async_set_state(as, ASR_STATE_HALT);
179 			break;
180 		}
181 
182 		if (ai->ai_socktype &&
183 		    ai->ai_socktype != SOCK_DGRAM  &&
184 		    ai->ai_socktype != SOCK_STREAM &&
185 		    ai->ai_socktype != SOCK_RAW) {
186 			ar->ar_gai_errno = EAI_SOCKTYPE;
187 			async_set_state(as, ASR_STATE_HALT);
188 			break;
189 		}
190 
191 		if (ai->ai_socktype == SOCK_RAW &&
192 		    get_port(as->as.ai.servname, NULL, 1) != 0) {
193 			ar->ar_gai_errno = EAI_SERVICE;
194 			async_set_state(as, ASR_STATE_HALT);
195 			break;
196 		}
197 
198 		/* Make sure there is at least a valid combination */
199 		for (i = 0; matches[i].family != -1; i++)
200 			if (MATCH_FAMILY(ai->ai_family, i) &&
201 			    MATCH_SOCKTYPE(ai->ai_socktype, i) &&
202 			    MATCH_PROTO(ai->ai_protocol, i))
203 				break;
204 		if (matches[i].family == -1) {
205 			ar->ar_gai_errno = EAI_BADHINTS;
206 			async_set_state(as, ASR_STATE_HALT);
207 			break;
208 		}
209 
210 		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP)
211 			as->as.ai.port_udp = get_port(as->as.ai.servname, "udp",
212 			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
213 		if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP)
214 			as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp",
215 			    as->as.ai.hints.ai_flags & AI_NUMERICSERV);
216 		if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 ||
217 		    (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) ||
218 		    (ai->ai_protocol && (as->as.ai.port_udp == -1 ||
219 					 as->as.ai.port_tcp == -1))) {
220 			ar->ar_gai_errno = EAI_SERVICE;
221 			async_set_state(as, ASR_STATE_HALT);
222 			break;
223 		}
224 
225 		ar->ar_gai_errno = 0;
226 
227 		/* If hostname is NULL, use local address */
228 		if (as->as.ai.hostname == NULL) {
229 			for (family = iter_family(as, 1);
230 			    family != -1;
231 			    family = iter_family(as, 0)) {
232 				/*
233 				 * We could use statically built sockaddrs for
234 				 * those, rather than parsing over and over.
235 				 */
236 				if (family == PF_INET)
237 					str = (ai->ai_flags & AI_PASSIVE) ? \
238 						"0.0.0.0" : "127.0.0.1";
239 				else /* PF_INET6 */
240 					str = (ai->ai_flags & AI_PASSIVE) ? \
241 						"::" : "::1";
242 				 /* This can't fail */
243 				asr_sockaddr_from_str(&sa.sa, family, str);
244 				if ((r = addrinfo_add(as, &sa.sa, NULL))) {
245 					ar->ar_gai_errno = r;
246 					break;
247 				}
248 			}
249 			if (ar->ar_gai_errno == 0 && as->as_count == 0) {
250 				ar->ar_gai_errno = EAI_NODATA;
251 			}
252 			async_set_state(as, ASR_STATE_HALT);
253 			break;
254 		}
255 
256 		/* Try numeric addresses first */
257 		for (family = iter_family(as, 1);
258 		    family != -1;
259 		    family = iter_family(as, 0)) {
260 
261 			if (asr_sockaddr_from_str(&sa.sa, family,
262 			    as->as.ai.hostname) == -1)
263 				continue;
264 
265 			if ((r = addrinfo_add(as, &sa.sa, NULL)))
266 				ar->ar_gai_errno = r;
267 			break;
268 		}
269 		if (ar->ar_gai_errno || as->as_count) {
270 			async_set_state(as, ASR_STATE_HALT);
271 			break;
272 		}
273 
274 		if (ai->ai_flags & AI_NUMERICHOST) {
275 			ar->ar_gai_errno = EAI_NONAME;
276 			async_set_state(as, ASR_STATE_HALT);
277 			break;
278 		}
279 
280 		async_set_state(as, ASR_STATE_NEXT_DB);
281 		break;
282 
283 	case ASR_STATE_NEXT_DB:
284 		if (asr_iter_db(as) == -1) {
285 			async_set_state(as, ASR_STATE_NOT_FOUND);
286 			break;
287 		}
288 		as->as_family_idx = 0;
289 		async_set_state(as, ASR_STATE_SAME_DB);
290 		break;
291 
292 	case ASR_STATE_NEXT_FAMILY:
293 		as->as_family_idx += 1;
294 		if (as->as.ai.hints.ai_family != AF_UNSPEC ||
295 		    AS_FAMILY(as) == -1) {
296 			/* The family was specified, or we have tried all
297 			 * families with this DB.
298 			 */
299 			if (as->as_count) {
300 				ar->ar_gai_errno = 0;
301 				async_set_state(as, ASR_STATE_HALT);
302 			} else
303 				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
304 			break;
305 		}
306 		async_set_state(as, ASR_STATE_SAME_DB);
307 		break;
308 
309 	case ASR_STATE_NEXT_DOMAIN:
310 		/* domain search is only for dns */
311 		if (AS_DB(as) != ASR_DB_DNS) {
312 			async_set_state(as, ASR_STATE_NEXT_DB);
313 			break;
314 		}
315 		as->as_family_idx = 0;
316 
317 		free(as->as.ai.fqdn);
318 		as->as.ai.fqdn = NULL;
319 		r = iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn));
320 		if (r == -1) {
321 			async_set_state(as, ASR_STATE_NEXT_DB);
322 			break;
323 		}
324 		if (r == 0) {
325 			ar->ar_gai_errno = EAI_FAIL;
326 			async_set_state(as, ASR_STATE_HALT);
327 			break;
328 		}
329 		as->as.ai.fqdn = strdup(fqdn);
330 		if (as->as.ai.fqdn == NULL) {
331 			ar->ar_gai_errno = EAI_MEMORY;
332 			async_set_state(as, ASR_STATE_HALT);
333 		}
334 
335 		async_set_state(as, ASR_STATE_SAME_DB);
336 		break;
337 
338 	case ASR_STATE_SAME_DB:
339 		/* query the current DB again */
340 		switch (AS_DB(as)) {
341 		case ASR_DB_DNS:
342 			if (as->as.ai.fqdn == NULL) {
343 				/* First try, initialize domain iteration */
344 				as->as_dom_flags = 0;
345 				as->as_dom_step = DOM_INIT;
346 				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
347 				break;
348 			}
349 
350 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
351 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
352 
353 			as->as.ai.subq = res_query_async_ctx(as->as.ai.fqdn,
354 			    C_IN, (family == AF_INET6) ? T_AAAA : T_A,
355 			    as->as_ctx);
356 
357 			if (as->as.ai.subq == NULL) {
358 				if (errno == ENOMEM)
359 					ar->ar_gai_errno = EAI_MEMORY;
360 				else
361 					ar->ar_gai_errno = EAI_FAIL;
362 				async_set_state(as, ASR_STATE_HALT);
363 				break;
364 			}
365 			async_set_state(as, ASR_STATE_SUBQUERY);
366 			break;
367 
368 		case ASR_DB_FILE:
369 			f = fopen(as->as_ctx->ac_hostfile, "r");
370 			if (f == NULL) {
371 				async_set_state(as, ASR_STATE_NEXT_DB);
372 				break;
373 			}
374 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
375 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
376 
377 			r = addrinfo_from_file(as, family, f);
378 			if (r == -1) {
379 				if (errno == ENOMEM)
380 					ar->ar_gai_errno = EAI_MEMORY;
381 				else
382 					ar->ar_gai_errno = EAI_FAIL;
383 				async_set_state(as, ASR_STATE_HALT);
384 			} else
385 				async_set_state(as, ASR_STATE_NEXT_FAMILY);
386 			fclose(f);
387 			break;
388 
389 #ifdef YP
390 		case ASR_DB_YP:
391 			if (!domain && _yp_check(&domain) == 0) {
392 				async_set_state(as, ASR_STATE_NEXT_DB);
393 				break;
394 			}
395 			family = (as->as.ai.hints.ai_family == AF_UNSPEC) ?
396 			    AS_FAMILY(as) : as->as.ai.hints.ai_family;
397 
398 			name = as->as.ai.hostname;
399 
400 			/* XXX
401 			 * ipnodes.byname could also contain IPv4 address
402 			 */
403 			r = yp_match(domain, (family == AF_INET6) ?
404 			    "ipnodes.byname" : "hosts.byname",
405 			    name, strlen(name), &res, &len);
406 			if (r == 0) {
407 				r = addrinfo_from_yp(as, family, res);
408 				free(res);
409 				if (r == -1) {
410 					if (errno == ENOMEM)
411 						ar->ar_gai_errno = EAI_MEMORY;
412 					else
413 						ar->ar_gai_errno = EAI_FAIL;
414 					async_set_state(as, ASR_STATE_HALT);
415 					break;
416 				}
417 			}
418 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
419 			break;
420 #endif
421 		default:
422 			async_set_state(as, ASR_STATE_NEXT_DB);
423 		}
424 		break;
425 
426 	case ASR_STATE_SUBQUERY:
427 		if ((r = asr_run(as->as.ai.subq, ar)) == ASYNC_COND)
428 			return (ASYNC_COND);
429 
430 		as->as.ai.subq = NULL;
431 
432 		if (ar->ar_datalen == -1) {
433 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
434 			break;
435 		}
436 
437 		r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen);
438 		if (r == -1) {
439 			if (errno == ENOMEM)
440 				ar->ar_gai_errno = EAI_MEMORY;
441 			else
442 				ar->ar_gai_errno = EAI_FAIL;
443 			async_set_state(as, ASR_STATE_HALT);
444 		} else
445 			async_set_state(as, ASR_STATE_NEXT_FAMILY);
446 		free(ar->ar_data);
447 		break;
448 
449 	case ASR_STATE_NOT_FOUND:
450 		/* No result found. Maybe we can try again. */
451 		if (as->as.ai.flags & ASYNC_AGAIN)
452 			ar->ar_gai_errno = EAI_AGAIN;
453 		else
454 			ar->ar_gai_errno = EAI_NODATA;
455 		async_set_state(as, ASR_STATE_HALT);
456 		break;
457 
458 	case ASR_STATE_HALT:
459 		if (ar->ar_gai_errno == 0) {
460 			ar->ar_count = as->as_count;
461 			ar->ar_addrinfo = as->as.ai.aifirst;
462 			as->as.ai.aifirst = NULL;
463 		} else {
464 			ar->ar_count = 0;
465 			ar->ar_addrinfo = NULL;
466 		}
467 		return (ASYNC_DONE);
468 
469 	default:
470 		ar->ar_errno = EOPNOTSUPP;
471 		ar->ar_gai_errno = EAI_SYSTEM;
472 		async_set_state(as, ASR_STATE_HALT);
473 		break;
474 	}
475 	goto next;
476 }
477 
478 /*
479  * Retreive the port number for the service name "servname" and
480  * the protocol "proto".
481  */
482 static int
483 get_port(const char *servname, const char *proto, int numonly)
484 {
485 	struct servent		se;
486 	struct servent_data	sed;
487 	int			port, r;
488 	const char		*e;
489 
490 	if (servname == NULL)
491 		return (0);
492 
493 	e = NULL;
494 	port = strtonum(servname, 0, USHRT_MAX, &e);
495 	if (e == NULL)
496 		return (port);
497 	if (errno == ERANGE)
498 		return (-2); /* invalid */
499 	if (numonly)
500 		return (-2);
501 
502 	memset(&sed, 0, sizeof(sed));
503 	r = getservbyname_r(servname, proto, &se, &sed);
504 	port = ntohs(se.s_port);
505 	endservent_r(&sed);
506 
507 	if (r == -1)
508 		return (-1); /* not found */
509 
510 	return (port);
511 }
512 
513 /*
514  * Iterate over the address families that are to be queried. Use the
515  * list on the async context, unless a specific family was given in hints.
516  */
517 static int
518 iter_family(struct asr_query *as, int first)
519 {
520 	if (first) {
521 		as->as_family_idx = 0;
522 		if (as->as.ai.hints.ai_family != PF_UNSPEC)
523 			return as->as.ai.hints.ai_family;
524 		return AS_FAMILY(as);
525 	}
526 
527 	if (as->as.ai.hints.ai_family != PF_UNSPEC)
528 		return (-1);
529 
530 	as->as_family_idx++;
531 
532 	return AS_FAMILY(as);
533 }
534 
535 /*
536  * Concatenate a name and a domain name. The result has no trailing dot.
537  * Return the resulting string length, or 0 in case of error.
538  */
539 static size_t
540 domcat(const char *name, const char *domain, char *buf, size_t buflen)
541 {
542 	size_t	r;
543 
544 	r = asr_make_fqdn(name, domain, buf, buflen);
545 	if (r == 0)
546 		return (0);
547 	buf[r - 1] = '\0';
548 
549 	return (r - 1);
550 }
551 
552 /*
553  * Implement the search domain strategy.
554  *
555  * XXX duplicate from res_search_async
556  *
557  * This function works as a generator that constructs complete domains in
558  * buffer "buf" of size "len" for the given host name "name", according to the
559  * search rules defined by the resolving context.  It is supposed to be called
560  * multiple times (with the same name) to generate the next possible domain
561  * name, if any.
562  *
563  * It returns -1 if all possibilities have been exhausted, 0 if there was an
564  * error generating the next name, or the resulting name length.
565  */
566 static int
567 iter_domain(struct asr_query *as, const char *name, char * buf, size_t len)
568 {
569 	const char	*c;
570 	int		 dots;
571 
572 	switch (as->as_dom_step) {
573 
574 	case DOM_INIT:
575 		/* First call */
576 
577 		/*
578 		 * If "name" is an FQDN, that's the only result and we
579 		 * don't try anything else.
580 		 */
581 		if (strlen(name) && name[strlen(name) - 1] ==  '.') {
582 			DPRINT("asr: iter_domain(\"%s\") fqdn\n", name);
583 			as->as_dom_flags |= ASYNC_DOM_FQDN;
584 			as->as_dom_step = DOM_DONE;
585 			return (domcat(name, NULL, buf, len));
586 		}
587 
588 		/*
589 		 * Otherwise, we iterate through the specified search domains.
590 		 */
591 		as->as_dom_step = DOM_DOMAIN;
592 		as->as_dom_idx = 0;
593 
594 		/*
595 		 * If "name" as enough dots, use it as-is first, as indicated
596 		 * in resolv.conf(5).
597 		 */
598 		dots = 0;
599 		for (c = name; *c; c++)
600 			dots += (*c == '.');
601 		if (dots >= as->as_ctx->ac_ndots) {
602 			DPRINT("asr: iter_domain(\"%s\") ndots\n", name);
603 			as->as_dom_flags |= ASYNC_DOM_NDOTS;
604 			if (strlcpy(buf, name, len) >= len)
605 				return (0);
606 			return (strlen(buf));
607 		}
608 		/* Otherwise, starts using the search domains */
609 		/* FALLTHROUGH */
610 
611 	case DOM_DOMAIN:
612 		if (as->as_dom_idx < as->as_ctx->ac_domcount) {
613 			DPRINT("asr: iter_domain(\"%s\") domain \"%s\"\n",
614 			    name, as->as_ctx->ac_dom[as->as_dom_idx]);
615 			as->as_dom_flags |= ASYNC_DOM_DOMAIN;
616 			return (domcat(name,
617 			    as->as_ctx->ac_dom[as->as_dom_idx++], buf, len));
618 		}
619 
620 		/* No more domain to try. */
621 
622 		as->as_dom_step = DOM_DONE;
623 
624 		/*
625 		 * If the name was not tried as an absolute name before,
626 		 * do it now.
627 		 */
628 		if (!(as->as_dom_flags & ASYNC_DOM_NDOTS)) {
629 			DPRINT("asr: iter_domain(\"%s\") as is\n", name);
630 			as->as_dom_flags |= ASYNC_DOM_ASIS;
631 			if (strlcpy(buf, name, len) >= len)
632 				return (0);
633 			return (strlen(buf));
634 		}
635 		/* Otherwise, we are done. */
636 
637 	case DOM_DONE:
638 	default:
639 		DPRINT("asr: iter_domain(\"%s\") done\n", name);
640 		return (-1);
641 	}
642 }
643 
644 /*
645  * Use the sockaddr at "sa" to extend the result list on the "as" context,
646  * with the specified canonical name "cname". This function adds one
647  * entry per protocol/socktype match.
648  */
649 static int
650 addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname)
651 {
652 	struct addrinfo		*ai;
653 	int			 i, port, proto;
654 
655 	for (i = 0; matches[i].family != -1; i++) {
656 		if (matches[i].family != sa->sa_family ||
657 		    !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) ||
658 		    !MATCH_PROTO(as->as.ai.hints.ai_protocol, i))
659 			continue;
660 
661 		proto = as->as.ai.hints.ai_protocol;
662 		if (!proto)
663 			proto = matches[i].protocol;
664 
665 		if (proto == IPPROTO_TCP)
666 			port = as->as.ai.port_tcp;
667 		else if (proto == IPPROTO_UDP)
668 			port = as->as.ai.port_udp;
669 		else
670 			port = 0;
671 
672 		/* servname specified, but not defined for this protocol */
673 		if (port == -1)
674 			continue;
675 
676 		ai = calloc(1, sizeof(*ai) + sa->sa_len);
677 		if (ai == NULL)
678 			return (EAI_MEMORY);
679 		ai->ai_family = sa->sa_family;
680 		ai->ai_socktype = matches[i].socktype;
681 		ai->ai_protocol = proto;
682 		ai->ai_flags = as->as.ai.hints.ai_flags;
683 		ai->ai_addrlen = sa->sa_len;
684 		ai->ai_addr = (void *)(ai + 1);
685 		if (cname &&
686 		    as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) {
687 			if ((ai->ai_canonname = strdup(cname)) == NULL) {
688 				free(ai);
689 				return (EAI_MEMORY);
690 			}
691 		}
692 		memmove(ai->ai_addr, sa, sa->sa_len);
693 		if (sa->sa_family == PF_INET)
694 			((struct sockaddr_in *)ai->ai_addr)->sin_port =
695 			    htons(port);
696 		else if (sa->sa_family == PF_INET6)
697 			((struct sockaddr_in6 *)ai->ai_addr)->sin6_port =
698 			    htons(port);
699 
700 		if (as->as.ai.aifirst == NULL)
701 			as->as.ai.aifirst = ai;
702 		if (as->as.ai.ailast)
703 			as->as.ai.ailast->ai_next = ai;
704 		as->as.ai.ailast = ai;
705 		as->as_count += 1;
706 	}
707 
708 	return (0);
709 }
710 
711 static int
712 addrinfo_from_file(struct asr_query *as, int family, FILE *f)
713 {
714 	char		*tokens[MAXTOKEN], *c;
715 	int		 n, i;
716 	union {
717 		struct sockaddr		sa;
718 		struct sockaddr_in	sain;
719 		struct sockaddr_in6	sain6;
720 	} u;
721 
722 	for (;;) {
723 		n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
724 		if (n == -1)
725 			break; /* ignore errors reading the file */
726 
727 		for (i = 1; i < n; i++) {
728 			if (strcasecmp(as->as.ai.hostname, tokens[i]))
729 				continue;
730 			if (asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
731 				continue;
732 			break;
733 		}
734 		if (i == n)
735 			continue;
736 
737 		if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
738 			c = tokens[1];
739 		else
740 			c = NULL;
741 
742 		if (addrinfo_add(as, &u.sa, c))
743 			return (-1); /* errno set */
744 	}
745 	return (0);
746 }
747 
748 static int
749 addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen)
750 {
751 	struct asr_unpack	 p;
752 	struct asr_dns_header	 h;
753 	struct asr_dns_query	 q;
754 	struct asr_dns_rr	 rr;
755 	int			 i;
756 	union {
757 		struct sockaddr		sa;
758 		struct sockaddr_in	sain;
759 		struct sockaddr_in6	sain6;
760 	} u;
761 	char		 buf[MAXDNAME], *c;
762 
763 	asr_unpack_init(&p, pkt, pktlen);
764 	asr_unpack_header(&p, &h);
765 	for (; h.qdcount; h.qdcount--)
766 		asr_unpack_query(&p, &q);
767 
768 	for (i = 0; i < h.ancount; i++) {
769 		asr_unpack_rr(&p, &rr);
770 		if (rr.rr_type != q.q_type ||
771 		    rr.rr_class != q.q_class)
772 			continue;
773 
774 		memset(&u, 0, sizeof u);
775 		if (rr.rr_type == T_A) {
776 			u.sain.sin_len = sizeof u.sain;
777 			u.sain.sin_family = AF_INET;
778 			u.sain.sin_addr = rr.rr.in_a.addr;
779 			u.sain.sin_port = 0;
780 		} else if (rr.rr_type == T_AAAA) {
781 			u.sain6.sin6_len = sizeof u.sain6;
782 			u.sain6.sin6_family = AF_INET6;
783 			u.sain6.sin6_addr = rr.rr.in_aaaa.addr6;
784 			u.sain6.sin6_port = 0;
785 		} else
786 			continue;
787 
788 		if (as->as.ai.hints.ai_flags & AI_CANONNAME) {
789 			asr_strdname(rr.rr_dname, buf, sizeof buf);
790 			buf[strlen(buf) - 1] = '\0';
791 			c = res_hnok(buf) ? buf : NULL;
792 		} else if (as->as.ai.hints.ai_flags & AI_FQDN)
793 			c = as->as.ai.fqdn;
794 		else
795 			c = NULL;
796 
797 		if (addrinfo_add(as, &u.sa, c))
798 			return (-1); /* errno set */
799 	}
800 	return (0);
801 }
802 
803 #ifdef YP
804 static int
805 strsplit(char *line, char **tokens, int ntokens)
806 {
807 	int	ntok;
808 	char	*cp, **tp;
809 
810 	for (cp = line, tp = tokens, ntok = 0;
811 	    ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; )
812 		if (**tp != '\0') {
813 			tp++;
814 			ntok++;
815 		}
816 
817 	return (ntok);
818 }
819 
820 static int
821 addrinfo_from_yp(struct asr_query *as, int family, char *line)
822 {
823 	char		*next, *tokens[MAXTOKEN], *c;
824 	int		 ntok;
825 	union {
826 		struct sockaddr		sa;
827 		struct sockaddr_in	sain;
828 		struct sockaddr_in6	sain6;
829 	} u;
830 
831 	for (next = line; line; line = next) {
832 		if ((next = strchr(line, '\n'))) {
833 			*next = '\0';
834 			next += 1;
835 		}
836 		ntok = strsplit(line, tokens, MAXTOKEN);
837 		if (ntok < 2)
838 			continue;
839 
840 		if (asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1)
841 			continue;
842 
843 		if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN))
844 			c = tokens[1];
845 		else
846 			c = NULL;
847 
848 		if (addrinfo_add(as, &u.sa, c))
849 			return (-1); /* errno set */
850 	}
851 	return (0);
852 }
853 #endif
854