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