xref: /openbsd/lib/libc/asr/getrrsetbyname_async.c (revision 8ddfd431)
1 /*	$OpenBSD: getrrsetbyname_async.c,v 1.14 2024/05/07 23:40:53 djm 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 *
getrrsetbyname_async(const char * hostname,unsigned int rdclass,unsigned int rdtype,unsigned int flags,void * asr)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
getrrsetbyname_async_run(struct asr_query * as,struct asr_result * ar)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.14 2024/05/07 23:40:53 djm 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
get_response(struct asr_result * ar,const char * pkt,int pktlen)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 			if (rr->size != 0) {
338 				rdata->rdi_data = malloc(rr->size);
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 	}
347 	free_dns_response(response);
348 
349 	ar->ar_rrsetinfo = rrset;
350 	ar->ar_rrset_errno = ERRSET_SUCCESS;
351 	return;
352 
353 fail:
354 	if (rrset != NULL)
355 		freerrset(rrset);
356 	if (response != NULL)
357 		free_dns_response(response);
358 }
359 
360 /*
361  * DNS response parsing routines
362  */
363 static struct dns_response *
parse_dns_response(const u_char * answer,int size)364 parse_dns_response(const u_char *answer, int size)
365 {
366 	struct dns_response *resp;
367 	const u_char *cp;
368 
369 	if (size <= HFIXEDSZ)
370 		return (NULL);
371 
372 	/* allocate memory for the response */
373 	resp = calloc(1, sizeof(*resp));
374 	if (resp == NULL)
375 		return (NULL);
376 
377 	/* initialize current pointer */
378 	cp = answer;
379 
380 	/* copy header */
381 	memcpy(&resp->header, cp, HFIXEDSZ);
382 	cp += HFIXEDSZ;
383 
384 	/* fix header byte order */
385 	resp->header.qdcount = ntohs(resp->header.qdcount);
386 	resp->header.ancount = ntohs(resp->header.ancount);
387 	resp->header.nscount = ntohs(resp->header.nscount);
388 	resp->header.arcount = ntohs(resp->header.arcount);
389 
390 	/* there must be at least one query */
391 	if (resp->header.qdcount < 1) {
392 		free_dns_response(resp);
393 		return (NULL);
394 	}
395 
396 	/* parse query section */
397 	resp->query = parse_dns_qsection(answer, size, &cp,
398 	    resp->header.qdcount);
399 	if (resp->header.qdcount && resp->query == NULL) {
400 		free_dns_response(resp);
401 		return (NULL);
402 	}
403 
404 	/* parse answer section */
405 	resp->answer = parse_dns_rrsection(answer, size, &cp,
406 	    resp->header.ancount);
407 	if (resp->header.ancount && resp->answer == NULL) {
408 		free_dns_response(resp);
409 		return (NULL);
410 	}
411 
412 	/* parse authority section */
413 	resp->authority = parse_dns_rrsection(answer, size, &cp,
414 	    resp->header.nscount);
415 	if (resp->header.nscount && resp->authority == NULL) {
416 		free_dns_response(resp);
417 		return (NULL);
418 	}
419 
420 	/* parse additional section */
421 	resp->additional = parse_dns_rrsection(answer, size, &cp,
422 	    resp->header.arcount);
423 	if (resp->header.arcount && resp->additional == NULL) {
424 		free_dns_response(resp);
425 		return (NULL);
426 	}
427 
428 	return (resp);
429 }
430 
431 static struct dns_query *
parse_dns_qsection(const u_char * answer,int size,const u_char ** cp,int count)432 parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count)
433 {
434 	struct dns_query *head, *curr, *prev;
435 	int i, length;
436 	char name[MAXDNAME];
437 
438 #define NEED(need) \
439 	do { \
440 		if (*cp + need > answer + size) \
441 			goto fail; \
442 	} while (0)
443 
444 	for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
445 		if (*cp >= answer + size) {
446  fail:
447 			free_dns_query(head);
448 			return (NULL);
449 		}
450 		/* allocate and initialize struct */
451 		curr = calloc(1, sizeof(struct dns_query));
452 		if (curr == NULL)
453 			goto fail;
454 		if (head == NULL)
455 			head = curr;
456 		if (prev != NULL)
457 			prev->next = curr;
458 
459 		/* name */
460 		length = dn_expand(answer, answer + size, *cp, name,
461 		    sizeof(name));
462 		if (length < 0) {
463 			free_dns_query(head);
464 			return (NULL);
465 		}
466 		curr->name = strdup(name);
467 		if (curr->name == NULL) {
468 			free_dns_query(head);
469 			return (NULL);
470 		}
471 		NEED(length);
472 		*cp += length;
473 
474 		/* type */
475 		NEED(INT16SZ);
476 		curr->type = _getshort(*cp);
477 		*cp += INT16SZ;
478 
479 		/* class */
480 		NEED(INT16SZ);
481 		curr->class = _getshort(*cp);
482 		*cp += INT16SZ;
483 	}
484 #undef NEED
485 
486 	return (head);
487 }
488 
489 static struct dns_rr *
parse_dns_rrsection(const u_char * answer,int size,const u_char ** cp,int count)490 parse_dns_rrsection(const u_char *answer, int size, const u_char **cp,
491     int count)
492 {
493 	struct dns_rr *head, *curr, *prev;
494 	int i, length;
495 	char name[MAXDNAME];
496 
497 #define NEED(need) \
498 	do { \
499 		if (*cp + need > answer + size) \
500 			goto fail; \
501 	} while (0)
502 
503 	for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
504 		if (*cp >= answer + size) {
505  fail:
506 			free_dns_rr(head);
507 			return (NULL);
508 		}
509 
510 		/* allocate and initialize struct */
511 		curr = calloc(1, sizeof(struct dns_rr));
512 		if (curr == NULL)
513 			goto fail;
514 		if (head == NULL)
515 			head = curr;
516 		if (prev != NULL)
517 			prev->next = curr;
518 
519 		/* name */
520 		length = dn_expand(answer, answer + size, *cp, name,
521 		    sizeof(name));
522 		if (length < 0) {
523 			free_dns_rr(head);
524 			return (NULL);
525 		}
526 		curr->name = strdup(name);
527 		if (curr->name == NULL) {
528 			free_dns_rr(head);
529 			return (NULL);
530 		}
531 		NEED(length);
532 		*cp += length;
533 
534 		/* type */
535 		NEED(INT16SZ);
536 		curr->type = _getshort(*cp);
537 		*cp += INT16SZ;
538 
539 		/* class */
540 		NEED(INT16SZ);
541 		curr->class = _getshort(*cp);
542 		*cp += INT16SZ;
543 
544 		/* ttl */
545 		NEED(INT32SZ);
546 		curr->ttl = _getlong(*cp);
547 		*cp += INT32SZ;
548 
549 		/* rdata size */
550 		NEED(INT16SZ);
551 		curr->size = _getshort(*cp);
552 		*cp += INT16SZ;
553 
554 		/* rdata itself */
555 		NEED(curr->size);
556 		if (curr->size != 0) {
557 			if ((curr->rdata = malloc(curr->size)) == NULL) {
558 				free_dns_rr(head);
559 				return (NULL);
560 			}
561 			memcpy(curr->rdata, *cp, curr->size);
562 		}
563 		*cp += curr->size;
564 	}
565 #undef NEED
566 
567 	return (head);
568 }
569 
570 static void
free_dns_query(struct dns_query * p)571 free_dns_query(struct dns_query *p)
572 {
573 	if (p == NULL)
574 		return;
575 
576 	if (p->name)
577 		free(p->name);
578 	free_dns_query(p->next);
579 	free(p);
580 }
581 
582 static void
free_dns_rr(struct dns_rr * p)583 free_dns_rr(struct dns_rr *p)
584 {
585 	if (p == NULL)
586 		return;
587 
588 	if (p->name)
589 		free(p->name);
590 	if (p->rdata)
591 		free(p->rdata);
592 	free_dns_rr(p->next);
593 	free(p);
594 }
595 
596 static void
free_dns_response(struct dns_response * p)597 free_dns_response(struct dns_response *p)
598 {
599 	if (p == NULL)
600 		return;
601 
602 	free_dns_query(p->query);
603 	free_dns_rr(p->answer);
604 	free_dns_rr(p->authority);
605 	free_dns_rr(p->additional);
606 	free(p);
607 }
608 
609 static int
count_dns_rr(struct dns_rr * p,u_int16_t class,u_int16_t type)610 count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type)
611 {
612 	int n = 0;
613 
614 	while (p) {
615 		if (p->class == class && p->type == type)
616 			n++;
617 		p = p->next;
618 	}
619 
620 	return (n);
621 }
622