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