xref: /openbsd-src/lib/libc/asr/gethostnamadr_async.c (revision 0348accf476e5b502fcf72b70b1f5c2bf9cd5ef6)
1 /*	$OpenBSD: gethostnamadr_async.c,v 1.4 2012/07/10 15:58:00 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 #include <sys/types.h>
18 #include <sys/socket.h>
19 
20 #include <netinet/in.h>
21 #include <arpa/inet.h>
22 #include <arpa/nameser.h>
23 
24 #include <err.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 
30 #include "asr.h"
31 #include "asr_private.h"
32 
33 
34 #define MAXALIASES	16
35 #define MAXADDRS	16
36 
37 ssize_t addr_as_fqdn(const char *, int, char *, size_t);
38 
39 static int gethostnamadr_async_run(struct async *, struct async_res *);
40 static struct hostent *hostent_alloc(int);
41 static int hostent_set_cname(struct hostent *, const char *, int);
42 static int hostent_add_alias(struct hostent *, const char *, int);
43 static int hostent_add_addr(struct hostent *, const void *, int);
44 static struct hostent *hostent_file_match(FILE *, int, int, const char *, int);
45 static struct hostent *hostent_from_packet(int, int, char *, size_t);
46 
47 struct async *
48 gethostbyname_async(const char *name, struct asr *asr)
49 {
50 	return gethostbyname2_async(name, AF_INET, asr);
51 }
52 
53 struct async *
54 gethostbyname2_async(const char *name, int af, struct asr *asr)
55 {
56 	struct asr_ctx	*ac;
57 	struct async	*as;
58 
59 	/* the original segfaults */
60 	if (name == NULL) {
61 		errno = EINVAL;
62 		return (NULL);
63 	}
64 
65 	ac = asr_use_resolver(asr);
66 	if ((as = async_new(ac, ASR_GETHOSTBYNAME)) == NULL)
67 		goto abort; /* errno set */
68 	as->as_run = gethostnamadr_async_run;
69 
70 	as->as.hostnamadr.family = af;
71 	if (af == AF_INET)
72 		as->as.hostnamadr.addrlen = INADDRSZ;
73 	else if (af == AF_INET6)
74 		as->as.hostnamadr.addrlen = IN6ADDRSZ;
75 	as->as.hostnamadr.name = strdup(name);
76 	if (as->as.hostnamadr.name == NULL)
77 		goto abort; /* errno set */
78 
79 	asr_ctx_unref(ac);
80 	return (as);
81 
82     abort:
83 	if (as)
84 		async_free(as);
85 	asr_ctx_unref(ac);
86 	return (NULL);
87 }
88 
89 struct async *
90 gethostbyaddr_async(const void *addr, socklen_t len, int af, struct asr *asr)
91 {
92 	struct asr_ctx	*ac;
93 	struct async	*as;
94 
95 	ac = asr_use_resolver(asr);
96 	as = gethostbyaddr_async_ctx(addr, len, af, ac);
97 	asr_ctx_unref(ac);
98 
99 	return (as);
100 }
101 
102 struct async *
103 gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af,
104     struct asr_ctx *ac)
105 {
106 	struct async	*as;
107 
108 	if ((as = async_new(ac, ASR_GETHOSTBYADDR)) == NULL)
109 		goto abort; /* errno set */
110 	as->as_run = gethostnamadr_async_run;
111 
112 	as->as.hostnamadr.family = af;
113 	as->as.hostnamadr.addrlen = len;
114 	if (len > 0)
115 		memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len);
116 
117 	return (as);
118 
119     abort:
120 	if (as)
121 		async_free(as);
122 	return (NULL);
123 }
124 
125 static int
126 gethostnamadr_async_run(struct async *as, struct async_res *ar)
127 {
128 	int	i, n, r, type;
129 	FILE	*f;
130 	char	dname[MAXDNAME], *data;
131 
132     next:
133 	switch(as->as_state) {
134 
135 	case ASR_STATE_INIT:
136 
137 		if (as->as.hostnamadr.family != AF_INET &&
138 		    as->as.hostnamadr.family != AF_INET6) {
139 			ar->ar_h_errno = NETDB_INTERNAL;
140 			ar->ar_errno = EAFNOSUPPORT;
141 			async_set_state(as, ASR_STATE_HALT);
142 			break;
143 		}
144 
145 		if ((as->as.hostnamadr.family == AF_INET &&
146 		     as->as.hostnamadr.addrlen != INADDRSZ) ||
147 		    (as->as.hostnamadr.family == AF_INET6 &&
148 		     as->as.hostnamadr.addrlen != IN6ADDRSZ)) {
149 			ar->ar_h_errno = NETDB_INTERNAL;
150 			ar->ar_errno = EINVAL;
151 			async_set_state(as, ASR_STATE_HALT);
152 			break;
153 		}
154 
155 		if (as->as_type == ASR_GETHOSTBYNAME)
156 			async_set_state(as, ASR_STATE_NEXT_DOMAIN);
157 		else
158 			async_set_state(as, ASR_STATE_NEXT_DB);
159 		break;
160 
161 	case ASR_STATE_NEXT_DOMAIN:
162 
163 		r = asr_iter_domain(as, as->as.hostnamadr.name, dname, sizeof(dname));
164 		if (r == -1) {
165 			async_set_state(as, ASR_STATE_NOT_FOUND);
166 			break;
167 		}
168 
169 		if (as->as.hostnamadr.dname)
170 			free(as->as.hostnamadr.dname);
171 		if ((as->as.hostnamadr.dname = strdup(dname)) == NULL) {
172 			ar->ar_h_errno = NETDB_INTERNAL;
173 			ar->ar_errno = errno;
174 			async_set_state(as, ASR_STATE_HALT);
175 		}
176 
177 		as->as_db_idx = 0;
178 		async_set_state(as, ASR_STATE_NEXT_DB);
179 		break;
180 
181 	case ASR_STATE_NEXT_DB:
182 
183 		if (asr_iter_db(as) == -1) {
184 			if (as->as_type == ASR_GETHOSTBYNAME)
185 				async_set_state(as, ASR_STATE_NEXT_DOMAIN);
186 			else
187 				async_set_state(as, ASR_STATE_NOT_FOUND);
188 			break;
189 		}
190 
191 		switch(AS_DB(as)) {
192 
193 		case ASR_DB_DNS:
194 
195 			/* Create a subquery to do the DNS lookup */
196 
197 			if (as->as_type == ASR_GETHOSTBYNAME) {
198 				type = (as->as.hostnamadr.family == AF_INET) ?
199 				    T_A : T_AAAA;
200 				as->as.hostnamadr.subq = res_query_async_ctx(
201 				    as->as.hostnamadr.dname,
202 				    C_IN, type, NULL, 0, as->as_ctx);
203 			} else {
204 				addr_as_fqdn(as->as.hostnamadr.addr,
205 				    as->as.hostnamadr.family,
206 				    dname, sizeof(dname));
207 				as->as.hostnamadr.subq = res_query_async_ctx(
208 				    dname, C_IN, T_PTR, NULL, 0, as->as_ctx);
209 			}
210 
211 			if (as->as.hostnamadr.subq == NULL) {
212 				ar->ar_errno = errno;
213 				ar->ar_h_errno = NETDB_INTERNAL;
214 				async_set_state(as, ASR_STATE_HALT);
215 				break;
216 			}
217 
218 			async_set_state(as, ASR_STATE_SUBQUERY);
219 			break;
220 
221 		case ASR_DB_FILE:
222 
223 			/* Try to find a match in the host file */
224 
225 			if ((f = fopen(as->as_ctx->ac_hostfile, "r")) == NULL)
226 				break;
227 
228 			if (as->as_type == ASR_GETHOSTBYNAME)
229 				data = as->as.hostnamadr.dname;
230 			else
231 				data = as->as.hostnamadr.addr;
232 
233 			ar->ar_hostent = hostent_file_match(f, as->as_type,
234 			    as->as.hostnamadr.family, data,
235 			    as->as.hostnamadr.addrlen);
236 
237 			fclose(f);
238 
239 			if (ar->ar_hostent == NULL) {
240 				if (errno) {
241 					ar->ar_errno = errno;
242 					ar->ar_h_errno = NETDB_INTERNAL;
243 					async_set_state(as, ASR_STATE_HALT);
244 				}
245 				/* otherwise not found */
246 				break;
247 			}
248 
249 			ar->ar_h_errno = NETDB_SUCCESS;
250 			async_set_state(as, ASR_STATE_HALT);
251 			break;
252 		}
253 		break;
254 
255 	case ASR_STATE_SUBQUERY:
256 
257 		/* Run the DNS subquery. */
258 
259 		if ((r = async_run(as->as.hostnamadr.subq, ar)) == ASYNC_COND)
260 			return (ASYNC_COND);
261 
262 		/* Done. */
263 		as->as.hostnamadr.subq = NULL;
264 
265 		if (ar->ar_datalen == -1) {
266 			async_set_state(as, ASR_STATE_NEXT_DB);
267 			break;
268 		}
269 
270 		/* If we got a packet but no anwser, use the next DB. */
271 		if (ar->ar_count == 0) {
272 			free(ar->ar_data);
273 			async_set_state(as, ASR_STATE_NEXT_DB);
274 			break;
275 		}
276 
277 		/* Read the hostent from the packet. */
278 
279 		ar->ar_hostent = hostent_from_packet(as->as_type,
280 		    as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen);
281 		free(ar->ar_data);
282 
283 		if (ar->ar_hostent == NULL) {
284 			ar->ar_errno = errno;
285 			ar->ar_h_errno = NETDB_INTERNAL;
286 			async_set_state(as, ASR_STATE_HALT);
287 			break;
288 		}
289 
290 		if (as->as_type == ASR_GETHOSTBYADDR) {
291 			if (hostent_add_addr(ar->ar_hostent,
292 			    as->as.hostnamadr.addr,
293 			    as->as.hostnamadr.addrlen) == -1) {
294 				freehostent(ar->ar_hostent);
295 				ar->ar_errno = errno;
296 				ar->ar_h_errno = NETDB_INTERNAL;
297 				async_set_state(as, ASR_STATE_HALT);
298 				break;
299 			}
300 		}
301 
302 		/*
303 		 * No address found in the dns packet. The blocking version
304 		 * reports this as an error.
305 		 */
306 		if (as->as_type == ASR_GETHOSTBYNAME &&
307 		    ar->ar_hostent->h_addr_list[0] == NULL) {
308 			freehostent(ar->ar_hostent);
309 			async_set_state(as, ASR_STATE_NEXT_DB);
310 			break;
311 		}
312 
313 		ar->ar_h_errno = NETDB_SUCCESS;
314 		async_set_state(as, ASR_STATE_HALT);
315 		break;
316 
317 	case ASR_STATE_NOT_FOUND:
318 		ar->ar_errno = 0;
319 		ar->ar_h_errno = HOST_NOT_FOUND;
320 		async_set_state(as, ASR_STATE_HALT);
321 		break;
322 
323 	case ASR_STATE_HALT:
324 		if (ar->ar_h_errno)
325 			ar->ar_hostent = NULL;
326 		else
327 			ar->ar_errno = 0;
328 		return (ASYNC_DONE);
329 
330 	default:
331 		ar->ar_errno = EOPNOTSUPP;
332 		ar->ar_h_errno = NETDB_INTERNAL;
333 		ar->ar_gai_errno = EAI_SYSTEM;
334 		async_set_state(as, ASR_STATE_HALT);
335                 break;
336 	}
337 	goto next;
338 }
339 
340 /*
341  * Lookup the first matching entry in the hostfile, either by address or by
342  * name depending on reqtype, and build a hostent from the line.
343  */
344 static struct hostent *
345 hostent_file_match(FILE *f, int reqtype, int family, const char *data, int datalen)
346 {
347 	char	*tokens[MAXTOKEN], addr[16];
348 	struct	 hostent *h;
349 	int	 n, i;
350 
351 	for(;;) {
352 		n = asr_parse_namedb_line(f, tokens, MAXTOKEN);
353 		if (n == -1) {
354 			errno = 0; /* ignore errors reading the file */
355 			return (NULL);
356 		}
357 
358 		if (reqtype == ASR_GETHOSTBYNAME) {
359 			for (i = 1; i < n; i++) {
360 				if (strcasecmp(data, tokens[i]))
361 					continue;
362 				if (inet_pton(family, tokens[0], addr) == 1)
363 					goto found;
364 			}
365 		} else {
366 			if (inet_pton(family, tokens[0], addr) == 1)
367 				if (memcmp(addr, data, datalen) == 0)
368 					goto found;
369 		}
370 	}
371 
372 found:
373 	if ((h = hostent_alloc(family)) == NULL)
374 		return (NULL);
375 	if (hostent_set_cname(h, tokens[1], 0) == -1)
376 		goto fail;
377 	for (i = 2; i < n; i ++)
378 		if (hostent_add_alias(h, tokens[i], 0) == -1)
379 			goto fail;
380 	if (hostent_add_addr(h, addr, h->h_length) == -1)
381 		goto fail;
382 	return (h);
383 fail:
384 	freehostent(h);
385 	return (NULL);
386 }
387 
388 /*
389  * Fill the hostent from the given DNS packet.
390  */
391 static struct hostent *
392 hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen)
393 {
394 	struct hostent	*h;
395 	struct packed	 p;
396 	struct header	 hdr;
397 	struct query	 q;
398 	struct rr	 rr;
399 
400 	if ((h = hostent_alloc(family)) == NULL)
401 		return (NULL);
402 
403 	packed_init(&p, pkt, pktlen);
404 	unpack_header(&p, &hdr);
405 	for(; hdr.qdcount; hdr.qdcount--)
406 		unpack_query(&p, &q);
407 	for(; hdr.ancount; hdr.ancount--) {
408 		unpack_rr(&p, &rr);
409 		if (rr.rr_class != C_IN)
410 			continue;
411 		switch (rr.rr_type) {
412 
413 		case T_CNAME:
414 			if (reqtype == ASR_GETHOSTBYNAME) {
415 				if (hostent_add_alias(h, rr.rr_dname, 1) == -1)
416 					goto fail;
417 			} else {
418 				if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
419 					goto fail;
420 			}
421 			break;
422 
423 		case T_PTR:
424 			if (reqtype != ASR_GETHOSTBYADDR)
425 				break;
426 			if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1)
427 				goto fail;
428 			/* XXX See if we need MULTI_PTRS_ARE_ALIASES */
429 			break;
430 
431 		case T_A:
432 			if (reqtype != ASR_GETHOSTBYNAME)
433 				break;
434 			if (family != AF_INET)
435 				break;
436 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
437 				goto fail;
438 			if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1)
439 				goto fail;
440 			break;
441 
442 		case T_AAAA:
443 			if (reqtype != ASR_GETHOSTBYNAME)
444 				break;
445 			if (family != AF_INET6)
446 				break;
447 			if (hostent_set_cname(h, rr.rr_dname, 1) == -1)
448 				goto fail;
449 			if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1)
450 				goto fail;
451 			break;
452 		}
453 	}
454 
455 	return (h);
456 fail:
457 	freehostent(h);
458 	return (NULL);
459 }
460 
461 static struct hostent *
462 hostent_alloc(int family)
463 {
464 	struct hostent	*h;
465 
466 	h = calloc(1, sizeof *h);
467 	if (h == NULL)
468 		return (NULL);
469 
470 	h->h_aliases = calloc(MAXALIASES, sizeof *h->h_aliases);
471 	h->h_addr_list = calloc(MAXADDRS, sizeof *h->h_addr_list);
472 	if (h->h_aliases == NULL || h->h_addr_list == NULL) {
473 		freehostent(h);
474 		return (NULL);
475 	}
476 	h->h_addrtype = family;
477 	h->h_length = (family == AF_INET) ? 4 : 16;
478 
479 	return (h);
480 }
481 
482 static int
483 hostent_set_cname(struct hostent *h, const char *name, int isdname)
484 {
485 	char	buf[MAXDNAME];
486 
487 	if (h->h_name)
488 		return (0);
489 
490 	if (isdname) {
491 		asr_strdname(name, buf, sizeof buf);
492 		buf[strlen(buf) - 1] = '\0';
493 		h->h_name = strdup(buf);
494 	} else {
495 		h->h_name = strdup(name);
496 	}
497 	if (h->h_name == NULL)
498 		return (-1);
499 
500 	return (0);
501 }
502 
503 static int
504 hostent_add_alias(struct hostent *h, const char *name, int isdname)
505 {
506 	char	buf[MAXDNAME];
507 	size_t	i;
508 
509 	for (i = 0; i < MAXALIASES; i++)
510 		if (h->h_aliases[i] == NULL)
511 			break;
512 	if (i == MAXALIASES)
513 		return (0);
514 
515 	if (isdname) {
516 		asr_strdname(name, buf, sizeof buf);
517 		buf[strlen(buf)-1] = '\0';
518 		h->h_aliases[i] = strdup(buf);
519 	} else {
520 		h->h_aliases[i] = strdup(name);
521 	}
522 	if (h->h_aliases[i] == NULL)
523 		return (-1);
524 
525 	return (0);
526 }
527 
528 static int
529 hostent_add_addr(struct hostent *h, const void *addr, int size)
530 {
531 	int	i;
532 
533 	for (i = 0; i < MAXADDRS; i++)
534 		if (h->h_addr_list[i] == NULL)
535 			break;
536 	if (i == MAXADDRS)
537 		return (0);
538 
539 	h->h_addr_list[i] = malloc(size);
540 	if (h->h_addr_list[i] == NULL)
541 		return (-1);
542 	memmove(h->h_addr_list[i], addr, size);
543 
544 	return (0);
545 }
546 
547 void
548 freehostent(struct hostent *h)
549 {
550 	char **c;
551 
552 	free(h->h_name);
553 	for (c = h->h_aliases; *c; c++)
554 		free(*c);
555 	free(h->h_aliases);
556 	for (c = h->h_addr_list; *c; c++)
557 		free(*c);
558 	free(h->h_addr_list);
559 	free(h);
560 }
561 
562 
563 ssize_t
564 addr_as_fqdn(const char *addr, int family, char *dst, size_t max)
565 {
566 	const struct in6_addr	*in6_addr;
567 	in_addr_t		 in_addr;
568 
569 	switch (family) {
570 	case AF_INET:
571 		in_addr = ntohl(*((const in_addr_t *)addr));
572 		snprintf(dst, max,
573 		    "%d.%d.%d.%d.in-addr.arpa.",
574 		    in_addr & 0xff,
575 		    (in_addr >> 8) & 0xff,
576 		    (in_addr >> 16) & 0xff,
577 		    (in_addr >> 24) & 0xff);
578 		break;
579 	case AF_INET6:
580 		in6_addr = (const struct in6_addr *)addr;
581 		snprintf(dst, max,
582 		    "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
583 		    "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x."
584 		    "ip6.arpa.",
585 		    in6_addr->s6_addr[15] & 0xf,
586 		    (in6_addr->s6_addr[15] >> 4) & 0xf,
587 		    in6_addr->s6_addr[14] & 0xf,
588 		    (in6_addr->s6_addr[14] >> 4) & 0xf,
589 		    in6_addr->s6_addr[13] & 0xf,
590 		    (in6_addr->s6_addr[13] >> 4) & 0xf,
591 		    in6_addr->s6_addr[12] & 0xf,
592 		    (in6_addr->s6_addr[12] >> 4) & 0xf,
593 		    in6_addr->s6_addr[11] & 0xf,
594 		    (in6_addr->s6_addr[11] >> 4) & 0xf,
595 		    in6_addr->s6_addr[10] & 0xf,
596 		    (in6_addr->s6_addr[10] >> 4) & 0xf,
597 		    in6_addr->s6_addr[9] & 0xf,
598 		    (in6_addr->s6_addr[9] >> 4) & 0xf,
599 		    in6_addr->s6_addr[8] & 0xf,
600 		    (in6_addr->s6_addr[8] >> 4) & 0xf,
601 		    in6_addr->s6_addr[7] & 0xf,
602 		    (in6_addr->s6_addr[7] >> 4) & 0xf,
603 		    in6_addr->s6_addr[6] & 0xf,
604 		    (in6_addr->s6_addr[6] >> 4) & 0xf,
605 		    in6_addr->s6_addr[5] & 0xf,
606 		    (in6_addr->s6_addr[5] >> 4) & 0xf,
607 		    in6_addr->s6_addr[4] & 0xf,
608 		    (in6_addr->s6_addr[4] >> 4) & 0xf,
609 		    in6_addr->s6_addr[3] & 0xf,
610 		    (in6_addr->s6_addr[3] >> 4) & 0xf,
611 		    in6_addr->s6_addr[2] & 0xf,
612 		    (in6_addr->s6_addr[2] >> 4) & 0xf,
613 		    in6_addr->s6_addr[1] & 0xf,
614 		    (in6_addr->s6_addr[1] >> 4) & 0xf,
615 		    in6_addr->s6_addr[0] & 0xf,
616 		    (in6_addr->s6_addr[0] >> 4) & 0xf);
617 		break;
618 	default:
619 		return (-1);
620 	}
621 	return (0);
622 }
623