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