xref: /openbsd-src/lib/libc/asr/getrrsetbyname_async.c (revision 43f5d16760c0fe0d818d0bb9fb0f26e3782fdc80)
1 /*	$OpenBSD: getrrsetbyname_async.c,v 1.13 2023/03/15 22:12:00 millert 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 <netinet/in.h>
22 #include <arpa/nameser.h>
23 #include <netdb.h>
24 
25 #include <asr.h>
26 #include <errno.h>
27 #include <resolv.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 
32 #include "asr_private.h"
33 
34 static int getrrsetbyname_async_run(struct asr_query *, struct asr_result *);
35 static void get_response(struct asr_result *, const char *, int);
36 
37 struct asr_query *
38 getrrsetbyname_async(const char *hostname, unsigned int rdclass,
39     unsigned int rdtype, unsigned int flags, void *asr)
40 {
41 	struct asr_ctx	 *ac;
42 	struct asr_query *as;
43 
44 	ac = _asr_use_resolver(asr);
45 	if ((as = _asr_async_new(ac, ASR_GETRRSETBYNAME)) == NULL)
46 		goto abort; /* errno set */
47 	as->as_run = getrrsetbyname_async_run;
48 
49 	as->as.rrset.flags = flags;
50 	as->as.rrset.class = rdclass;
51 	as->as.rrset.type = rdtype;
52 	as->as.rrset.name = strdup(hostname);
53 	if (as->as.rrset.name == NULL)
54 		goto abort; /* errno set */
55 
56 	_asr_ctx_unref(ac);
57 	return (as);
58     abort:
59 	if (as)
60 		_asr_async_free(as);
61 
62 	_asr_ctx_unref(ac);
63 	return (NULL);
64 }
65 DEF_WEAK(getrrsetbyname_async);
66 
67 static int
68 getrrsetbyname_async_run(struct asr_query *as, struct asr_result *ar)
69 {
70     next:
71 	switch (as->as_state) {
72 
73 	case ASR_STATE_INIT:
74 
75 		/* Check for invalid class and type. */
76 		if (as->as.rrset.class > 0xffff || as->as.rrset.type > 0xffff) {
77 			ar->ar_rrset_errno = ERRSET_INVAL;
78 			async_set_state(as, ASR_STATE_HALT);
79 			break;
80 		}
81 
82 		/* Do not allow queries of class or type ANY. */
83 		if (as->as.rrset.class == 0xff || as->as.rrset.type == 0xff) {
84 			ar->ar_rrset_errno = ERRSET_INVAL;
85 			async_set_state(as, ASR_STATE_HALT);
86 			break;
87 		}
88 
89 		/* Do not allow flags yet, unimplemented. */
90 		if (as->as.rrset.flags) {
91 			ar->ar_rrset_errno = ERRSET_INVAL;
92 			async_set_state(as, ASR_STATE_HALT);
93 			break;
94 		}
95 
96 		/* Create a delegate the lookup to a subquery. */
97 		as->as_subq = _res_query_async_ctx(
98 		    as->as.rrset.name,
99 		    as->as.rrset.class,
100 		    as->as.rrset.type,
101 		    as->as_ctx);
102 		if (as->as_subq == NULL) {
103 			ar->ar_rrset_errno = ERRSET_FAIL;
104 			async_set_state(as, ASR_STATE_HALT);
105 			break;
106 		}
107 
108 		async_set_state(as, ASR_STATE_SUBQUERY);
109 		break;
110 
111 	case ASR_STATE_SUBQUERY:
112 
113 		if ((asr_run(as->as_subq, ar)) == ASYNC_COND)
114 			return (ASYNC_COND);
115 
116 		as->as_subq = NULL;
117 
118 		/* No packet received.*/
119 		if (ar->ar_datalen == -1) {
120 			switch (ar->ar_h_errno) {
121 			case HOST_NOT_FOUND:
122 				ar->ar_rrset_errno = ERRSET_NONAME;
123 				break;
124 			case NO_DATA:
125 				ar->ar_rrset_errno = ERRSET_NODATA;
126 				break;
127 			default:
128 				ar->ar_rrset_errno = ERRSET_FAIL;
129 				break;
130 			}
131 			async_set_state(as, ASR_STATE_HALT);
132 			break;
133 		}
134 
135 		/* Got a packet but no answer. */
136 		if (ar->ar_count == 0) {
137 			free(ar->ar_data);
138 			switch (ar->ar_rcode) {
139 			case NXDOMAIN:
140 				ar->ar_rrset_errno = ERRSET_NONAME;
141 				break;
142 			case NOERROR:
143 				ar->ar_rrset_errno = ERRSET_NODATA;
144 				break;
145 			default:
146 				ar->ar_rrset_errno = ERRSET_FAIL;
147 				break;
148 			}
149 			async_set_state(as, ASR_STATE_HALT);
150 			break;
151 		}
152 
153 		get_response(ar, ar->ar_data, ar->ar_datalen);
154 		free(ar->ar_data);
155 		async_set_state(as, ASR_STATE_HALT);
156 		break;
157 
158 	case ASR_STATE_HALT:
159 		if (ar->ar_rrset_errno)
160 			ar->ar_rrsetinfo = NULL;
161 		return (ASYNC_DONE);
162 
163 	default:
164 		ar->ar_rrset_errno = ERRSET_FAIL;
165 		async_set_state(as, ASR_STATE_HALT);
166 		break;
167 	}
168 	goto next;
169 }
170 
171 /* The rest of this file is taken from the original implementation. */
172 
173 /* $OpenBSD: getrrsetbyname_async.c,v 1.13 2023/03/15 22:12:00 millert Exp $ */
174 
175 /*
176  * Copyright (c) 2001 Jakob Schlyter. All rights reserved.
177  *
178  * Redistribution and use in source and binary forms, with or without
179  * modification, are permitted provided that the following conditions
180  * are met:
181  *
182  * 1. Redistributions of source code must retain the above copyright
183  *    notice, this list of conditions and the following disclaimer.
184  *
185  * 2. Redistributions in binary form must reproduce the above copyright
186  *    notice, this list of conditions and the following disclaimer in the
187  *    documentation and/or other materials provided with the distribution.
188  *
189  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
190  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
191  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
192  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
193  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
194  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
195  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
196  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
197  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
198  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
199  */
200 
201 /*
202  * Portions Copyright (c) 1999-2001 Internet Software Consortium.
203  *
204  * Permission to use, copy, modify, and distribute this software for any
205  * purpose with or without fee is hereby granted, provided that the above
206  * copyright notice and this permission notice appear in all copies.
207  *
208  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
209  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
210  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
211  * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
212  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
213  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
214  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
215  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
216  */
217 
218 #define MAXPACKET 1024*64
219 
220 struct dns_query {
221 	char			*name;
222 	u_int16_t		type;
223 	u_int16_t		class;
224 	struct dns_query	*next;
225 };
226 
227 struct dns_rr {
228 	char			*name;
229 	u_int16_t		type;
230 	u_int16_t		class;
231 	u_int16_t		ttl;
232 	u_int16_t		size;
233 	void			*rdata;
234 	struct dns_rr		*next;
235 };
236 
237 struct dns_response {
238 	HEADER			header;
239 	struct dns_query	*query;
240 	struct dns_rr		*answer;
241 	struct dns_rr		*authority;
242 	struct dns_rr		*additional;
243 };
244 
245 static struct dns_response *parse_dns_response(const u_char *, int);
246 static struct dns_query *parse_dns_qsection(const u_char *, int,
247     const u_char **, int);
248 static struct dns_rr *parse_dns_rrsection(const u_char *, int, const u_char **,
249     int);
250 
251 static void free_dns_query(struct dns_query *);
252 static void free_dns_rr(struct dns_rr *);
253 static void free_dns_response(struct dns_response *);
254 
255 static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t);
256 
257 static void
258 get_response(struct asr_result *ar, const char *pkt, int pktlen)
259 {
260 	struct rrsetinfo *rrset = NULL;
261 	struct dns_response *response = NULL;
262 	struct dns_rr *rr;
263 	struct rdatainfo *rdata;
264 	unsigned int index_ans, index_sig;
265 
266 	/* parse result */
267 	response = parse_dns_response(pkt, pktlen);
268 	if (response == NULL) {
269 		ar->ar_rrset_errno = ERRSET_FAIL;
270 		goto fail;
271 	}
272 
273 	if (response->header.qdcount != 1) {
274 		ar->ar_rrset_errno = ERRSET_FAIL;
275 		goto fail;
276 	}
277 
278 	/* initialize rrset */
279 	rrset = calloc(1, sizeof(struct rrsetinfo));
280 	if (rrset == NULL) {
281 		ar->ar_rrset_errno = ERRSET_NOMEMORY;
282 		goto fail;
283 	}
284 	rrset->rri_rdclass = response->query->class;
285 	rrset->rri_rdtype = response->query->type;
286 	rrset->rri_ttl = response->answer->ttl;
287 	rrset->rri_nrdatas = response->header.ancount;
288 
289 	/* check for authenticated data */
290 	if (response->header.ad == 1)
291 		rrset->rri_flags |= RRSET_VALIDATED;
292 
293 	/* copy name from answer section */
294 	rrset->rri_name = strdup(response->answer->name);
295 	if (rrset->rri_name == NULL) {
296 		ar->ar_rrset_errno = ERRSET_NOMEMORY;
297 		goto fail;
298 	}
299 
300 	/* count answers */
301 	rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass,
302 	    rrset->rri_rdtype);
303 	rrset->rri_nsigs = count_dns_rr(response->answer, rrset->rri_rdclass,
304 	    T_RRSIG);
305 
306 	/* allocate memory for answers */
307 	rrset->rri_rdatas = calloc(rrset->rri_nrdatas,
308 	    sizeof(struct rdatainfo));
309 	if (rrset->rri_rdatas == NULL) {
310 		ar->ar_rrset_errno = ERRSET_NOMEMORY;
311 		goto fail;
312 	}
313 
314 	/* allocate memory for signatures */
315 	rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo));
316 	if (rrset->rri_sigs == NULL) {
317 		ar->ar_rrset_errno = ERRSET_NOMEMORY;
318 		goto fail;
319 	}
320 
321 	/* copy answers & signatures */
322 	for (rr = response->answer, index_ans = 0, index_sig = 0;
323 	    rr; rr = rr->next) {
324 
325 		rdata = NULL;
326 
327 		if (rr->class == rrset->rri_rdclass &&
328 		    rr->type  == rrset->rri_rdtype)
329 			rdata = &rrset->rri_rdatas[index_ans++];
330 
331 		if (rr->class == rrset->rri_rdclass &&
332 		    rr->type  == T_RRSIG)
333 			rdata = &rrset->rri_sigs[index_sig++];
334 
335 		if (rdata) {
336 			rdata->rdi_length = rr->size;
337 			rdata->rdi_data   = malloc(rr->size);
338 
339 			if (rdata->rdi_data == NULL) {
340 				ar->ar_rrset_errno = ERRSET_NOMEMORY;
341 				goto fail;
342 			}
343 			memcpy(rdata->rdi_data, rr->rdata, rr->size);
344 		}
345 	}
346 	free_dns_response(response);
347 
348 	ar->ar_rrsetinfo = rrset;
349 	ar->ar_rrset_errno = ERRSET_SUCCESS;
350 	return;
351 
352 fail:
353 	if (rrset != NULL)
354 		freerrset(rrset);
355 	if (response != NULL)
356 		free_dns_response(response);
357 }
358 
359 /*
360  * DNS response parsing routines
361  */
362 static struct dns_response *
363 parse_dns_response(const u_char *answer, int size)
364 {
365 	struct dns_response *resp;
366 	const u_char *cp;
367 
368 	if (size <= HFIXEDSZ)
369 		return (NULL);
370 
371 	/* allocate memory for the response */
372 	resp = calloc(1, sizeof(*resp));
373 	if (resp == NULL)
374 		return (NULL);
375 
376 	/* initialize current pointer */
377 	cp = answer;
378 
379 	/* copy header */
380 	memcpy(&resp->header, cp, HFIXEDSZ);
381 	cp += HFIXEDSZ;
382 
383 	/* fix header byte order */
384 	resp->header.qdcount = ntohs(resp->header.qdcount);
385 	resp->header.ancount = ntohs(resp->header.ancount);
386 	resp->header.nscount = ntohs(resp->header.nscount);
387 	resp->header.arcount = ntohs(resp->header.arcount);
388 
389 	/* there must be at least one query */
390 	if (resp->header.qdcount < 1) {
391 		free_dns_response(resp);
392 		return (NULL);
393 	}
394 
395 	/* parse query section */
396 	resp->query = parse_dns_qsection(answer, size, &cp,
397 	    resp->header.qdcount);
398 	if (resp->header.qdcount && resp->query == NULL) {
399 		free_dns_response(resp);
400 		return (NULL);
401 	}
402 
403 	/* parse answer section */
404 	resp->answer = parse_dns_rrsection(answer, size, &cp,
405 	    resp->header.ancount);
406 	if (resp->header.ancount && resp->answer == NULL) {
407 		free_dns_response(resp);
408 		return (NULL);
409 	}
410 
411 	/* parse authority section */
412 	resp->authority = parse_dns_rrsection(answer, size, &cp,
413 	    resp->header.nscount);
414 	if (resp->header.nscount && resp->authority == NULL) {
415 		free_dns_response(resp);
416 		return (NULL);
417 	}
418 
419 	/* parse additional section */
420 	resp->additional = parse_dns_rrsection(answer, size, &cp,
421 	    resp->header.arcount);
422 	if (resp->header.arcount && resp->additional == NULL) {
423 		free_dns_response(resp);
424 		return (NULL);
425 	}
426 
427 	return (resp);
428 }
429 
430 static struct dns_query *
431 parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count)
432 {
433 	struct dns_query *head, *curr, *prev;
434 	int i, length;
435 	char name[MAXDNAME];
436 
437 #define NEED(need) \
438 	do { \
439 		if (*cp + need > answer + size) \
440 			goto fail; \
441 	} while (0)
442 
443 	for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
444 		if (*cp >= answer + size) {
445  fail:
446 			free_dns_query(head);
447 			return (NULL);
448 		}
449 		/* allocate and initialize struct */
450 		curr = calloc(1, sizeof(struct dns_query));
451 		if (curr == NULL)
452 			goto fail;
453 		if (head == NULL)
454 			head = curr;
455 		if (prev != NULL)
456 			prev->next = curr;
457 
458 		/* name */
459 		length = dn_expand(answer, answer + size, *cp, name,
460 		    sizeof(name));
461 		if (length < 0) {
462 			free_dns_query(head);
463 			return (NULL);
464 		}
465 		curr->name = strdup(name);
466 		if (curr->name == NULL) {
467 			free_dns_query(head);
468 			return (NULL);
469 		}
470 		NEED(length);
471 		*cp += length;
472 
473 		/* type */
474 		NEED(INT16SZ);
475 		curr->type = _getshort(*cp);
476 		*cp += INT16SZ;
477 
478 		/* class */
479 		NEED(INT16SZ);
480 		curr->class = _getshort(*cp);
481 		*cp += INT16SZ;
482 	}
483 #undef NEED
484 
485 	return (head);
486 }
487 
488 static struct dns_rr *
489 parse_dns_rrsection(const u_char *answer, int size, const u_char **cp,
490     int count)
491 {
492 	struct dns_rr *head, *curr, *prev;
493 	int i, length;
494 	char name[MAXDNAME];
495 
496 #define NEED(need) \
497 	do { \
498 		if (*cp + need > answer + size) \
499 			goto fail; \
500 	} while (0)
501 
502 	for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
503 		if (*cp >= answer + size) {
504  fail:
505 			free_dns_rr(head);
506 			return (NULL);
507 		}
508 
509 		/* allocate and initialize struct */
510 		curr = calloc(1, sizeof(struct dns_rr));
511 		if (curr == NULL)
512 			goto fail;
513 		if (head == NULL)
514 			head = curr;
515 		if (prev != NULL)
516 			prev->next = curr;
517 
518 		/* name */
519 		length = dn_expand(answer, answer + size, *cp, name,
520 		    sizeof(name));
521 		if (length < 0) {
522 			free_dns_rr(head);
523 			return (NULL);
524 		}
525 		curr->name = strdup(name);
526 		if (curr->name == NULL) {
527 			free_dns_rr(head);
528 			return (NULL);
529 		}
530 		NEED(length);
531 		*cp += length;
532 
533 		/* type */
534 		NEED(INT16SZ);
535 		curr->type = _getshort(*cp);
536 		*cp += INT16SZ;
537 
538 		/* class */
539 		NEED(INT16SZ);
540 		curr->class = _getshort(*cp);
541 		*cp += INT16SZ;
542 
543 		/* ttl */
544 		NEED(INT32SZ);
545 		curr->ttl = _getlong(*cp);
546 		*cp += INT32SZ;
547 
548 		/* rdata size */
549 		NEED(INT16SZ);
550 		curr->size = _getshort(*cp);
551 		*cp += INT16SZ;
552 
553 		/* rdata itself */
554 		NEED(curr->size);
555 		curr->rdata = malloc(curr->size);
556 		if (curr->rdata == NULL) {
557 			free_dns_rr(head);
558 			return (NULL);
559 		}
560 		memcpy(curr->rdata, *cp, curr->size);
561 		*cp += curr->size;
562 	}
563 #undef NEED
564 
565 	return (head);
566 }
567 
568 static void
569 free_dns_query(struct dns_query *p)
570 {
571 	if (p == NULL)
572 		return;
573 
574 	if (p->name)
575 		free(p->name);
576 	free_dns_query(p->next);
577 	free(p);
578 }
579 
580 static void
581 free_dns_rr(struct dns_rr *p)
582 {
583 	if (p == NULL)
584 		return;
585 
586 	if (p->name)
587 		free(p->name);
588 	if (p->rdata)
589 		free(p->rdata);
590 	free_dns_rr(p->next);
591 	free(p);
592 }
593 
594 static void
595 free_dns_response(struct dns_response *p)
596 {
597 	if (p == NULL)
598 		return;
599 
600 	free_dns_query(p->query);
601 	free_dns_rr(p->answer);
602 	free_dns_rr(p->authority);
603 	free_dns_rr(p->additional);
604 	free(p);
605 }
606 
607 static int
608 count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type)
609 {
610 	int n = 0;
611 
612 	while (p) {
613 		if (p->class == class && p->type == type)
614 			n++;
615 		p = p->next;
616 	}
617 
618 	return (n);
619 }
620