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