xref: /openbsd/lib/libc/asr/getrrsetbyname_async.c (revision a6445c1d)
1 /*	$OpenBSD: getrrsetbyname_async.c,v 1.7 2014/03/26 18:13:15 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 
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 <err.h>
27 #include <errno.h>
28 #include <resolv.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 
33 #include "asr_private.h"
34 
35 static int getrrsetbyname_async_run(struct asr_query *, struct asr_result *);
36 static void get_response(struct asr_result *, const char *, int);
37 
38 struct asr_query *
39 getrrsetbyname_async(const char *hostname, unsigned int rdclass,
40     unsigned int rdtype, unsigned int flags, void *asr)
41 {
42 	struct asr_ctx	 *ac;
43 	struct asr_query *as;
44 
45 	ac = asr_use_resolver(asr);
46 	if ((as = asr_async_new(ac, ASR_GETRRSETBYNAME)) == NULL)
47 		goto abort; /* errno set */
48 	as->as_run = getrrsetbyname_async_run;
49 
50 	as->as.rrset.flags = flags;
51 	as->as.rrset.class = rdclass;
52 	as->as.rrset.type = rdtype;
53 	as->as.rrset.name = strdup(hostname);
54 	if (as->as.rrset.name == NULL)
55 		goto abort; /* errno set */
56 
57 	asr_ctx_unref(ac);
58 	return (as);
59     abort:
60 	if (as)
61 		asr_async_free(as);
62 
63 	asr_ctx_unref(ac);
64 	return (NULL);
65 }
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.rrset.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.rrset.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.rrset.subq, ar)) == ASYNC_COND)
114 			return (ASYNC_COND);
115 
116 		as->as.rrset.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 orignal implementation. */
172 
173 /* $OpenBSD: getrrsetbyname_async.c,v 1.7 2014/03/26 18:13:15 eric 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 	/* allocate memory for the response */
369 	resp = calloc(1, sizeof(*resp));
370 	if (resp == NULL)
371 		return (NULL);
372 
373 	/* initialize current pointer */
374 	cp = answer;
375 
376 	/* copy header */
377 	memcpy(&resp->header, cp, HFIXEDSZ);
378 	cp += HFIXEDSZ;
379 
380 	/* fix header byte order */
381 	resp->header.qdcount = ntohs(resp->header.qdcount);
382 	resp->header.ancount = ntohs(resp->header.ancount);
383 	resp->header.nscount = ntohs(resp->header.nscount);
384 	resp->header.arcount = ntohs(resp->header.arcount);
385 
386 	/* there must be at least one query */
387 	if (resp->header.qdcount < 1) {
388 		free_dns_response(resp);
389 		return (NULL);
390 	}
391 
392 	/* parse query section */
393 	resp->query = parse_dns_qsection(answer, size, &cp,
394 	    resp->header.qdcount);
395 	if (resp->header.qdcount && resp->query == NULL) {
396 		free_dns_response(resp);
397 		return (NULL);
398 	}
399 
400 	/* parse answer section */
401 	resp->answer = parse_dns_rrsection(answer, size, &cp,
402 	    resp->header.ancount);
403 	if (resp->header.ancount && resp->answer == NULL) {
404 		free_dns_response(resp);
405 		return (NULL);
406 	}
407 
408 	/* parse authority section */
409 	resp->authority = parse_dns_rrsection(answer, size, &cp,
410 	    resp->header.nscount);
411 	if (resp->header.nscount && resp->authority == NULL) {
412 		free_dns_response(resp);
413 		return (NULL);
414 	}
415 
416 	/* parse additional section */
417 	resp->additional = parse_dns_rrsection(answer, size, &cp,
418 	    resp->header.arcount);
419 	if (resp->header.arcount && resp->additional == NULL) {
420 		free_dns_response(resp);
421 		return (NULL);
422 	}
423 
424 	return (resp);
425 }
426 
427 static struct dns_query *
428 parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count)
429 {
430 	struct dns_query *head, *curr, *prev;
431 	int i, length;
432 	char name[MAXDNAME];
433 
434 	for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
435 
436 		/* allocate and initialize struct */
437 		curr = calloc(1, sizeof(struct dns_query));
438 		if (curr == NULL) {
439 			free_dns_query(head);
440 			return (NULL);
441 		}
442 		if (head == NULL)
443 			head = curr;
444 		if (prev != NULL)
445 			prev->next = curr;
446 
447 		/* name */
448 		length = dn_expand(answer, answer + size, *cp, name,
449 		    sizeof(name));
450 		if (length < 0) {
451 			free_dns_query(head);
452 			return (NULL);
453 		}
454 		curr->name = strdup(name);
455 		if (curr->name == NULL) {
456 			free_dns_query(head);
457 			return (NULL);
458 		}
459 		*cp += length;
460 
461 		/* type */
462 		curr->type = _getshort(*cp);
463 		*cp += INT16SZ;
464 
465 		/* class */
466 		curr->class = _getshort(*cp);
467 		*cp += INT16SZ;
468 	}
469 
470 	return (head);
471 }
472 
473 static struct dns_rr *
474 parse_dns_rrsection(const u_char *answer, int size, const u_char **cp,
475     int count)
476 {
477 	struct dns_rr *head, *curr, *prev;
478 	int i, length;
479 	char name[MAXDNAME];
480 
481 	for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
482 
483 		/* allocate and initialize struct */
484 		curr = calloc(1, sizeof(struct dns_rr));
485 		if (curr == NULL) {
486 			free_dns_rr(head);
487 			return (NULL);
488 		}
489 		if (head == NULL)
490 			head = curr;
491 		if (prev != NULL)
492 			prev->next = curr;
493 
494 		/* name */
495 		length = dn_expand(answer, answer + size, *cp, name,
496 		    sizeof(name));
497 		if (length < 0) {
498 			free_dns_rr(head);
499 			return (NULL);
500 		}
501 		curr->name = strdup(name);
502 		if (curr->name == NULL) {
503 			free_dns_rr(head);
504 			return (NULL);
505 		}
506 		*cp += length;
507 
508 		/* type */
509 		curr->type = _getshort(*cp);
510 		*cp += INT16SZ;
511 
512 		/* class */
513 		curr->class = _getshort(*cp);
514 		*cp += INT16SZ;
515 
516 		/* ttl */
517 		curr->ttl = _getlong(*cp);
518 		*cp += INT32SZ;
519 
520 		/* rdata size */
521 		curr->size = _getshort(*cp);
522 		*cp += INT16SZ;
523 
524 		/* rdata itself */
525 		curr->rdata = malloc(curr->size);
526 		if (curr->rdata == NULL) {
527 			free_dns_rr(head);
528 			return (NULL);
529 		}
530 		memcpy(curr->rdata, *cp, curr->size);
531 		*cp += curr->size;
532 	}
533 
534 	return (head);
535 }
536 
537 static void
538 free_dns_query(struct dns_query *p)
539 {
540 	if (p == NULL)
541 		return;
542 
543 	if (p->name)
544 		free(p->name);
545 	free_dns_query(p->next);
546 	free(p);
547 }
548 
549 static void
550 free_dns_rr(struct dns_rr *p)
551 {
552 	if (p == NULL)
553 		return;
554 
555 	if (p->name)
556 		free(p->name);
557 	if (p->rdata)
558 		free(p->rdata);
559 	free_dns_rr(p->next);
560 	free(p);
561 }
562 
563 static void
564 free_dns_response(struct dns_response *p)
565 {
566 	if (p == NULL)
567 		return;
568 
569 	free_dns_query(p->query);
570 	free_dns_rr(p->answer);
571 	free_dns_rr(p->authority);
572 	free_dns_rr(p->additional);
573 	free(p);
574 }
575 
576 static int
577 count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type)
578 {
579 	int n = 0;
580 
581 	while (p) {
582 		if (p->class == class && p->type == type)
583 			n++;
584 		p = p->next;
585 	}
586 
587 	return (n);
588 }
589