1/* $OpenBSD: getrrsetbyname.c,v 1.11 2007/10/11 18:36:41 jakob Exp $ */
2
3/*
4 * Copyright (c) 2001 Jakob Schlyter. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/*
30 * Portions Copyright (c) 1999-2001 Internet Software Consortium.
31 *
32 * Permission to use, copy, modify, and distribute this software for any
33 * purpose with or without fee is hereby granted, provided that the above
34 * copyright notice and this permission notice appear in all copies.
35 *
36 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
37 * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
38 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
39 * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
40 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
41 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
42 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
43 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
44 */
45
46/* OPENBSD ORIGINAL: lib/libc/net/getrrsetbyname.c */
47
48#include "includes.h"
49
50#if !defined (HAVE_GETRRSETBYNAME) && !defined (HAVE_LDNS)
51
52#include <stdlib.h>
53#include <string.h>
54
55#include <netinet/in.h>
56#include <arpa/inet.h>
57
58#include "getrrsetbyname.h"
59
60#if defined(HAVE_DECL_H_ERRNO) && !HAVE_DECL_H_ERRNO
61extern int h_errno;
62#endif
63
64/* We don't need multithread support here */
65#ifdef _THREAD_PRIVATE
66# undef _THREAD_PRIVATE
67#endif
68#define _THREAD_PRIVATE(a,b,c) (c)
69
70#ifndef HAVE__RES_EXTERN
71struct __res_state _res;
72#endif
73
74/* Necessary functions and macros */
75
76/*
77 * Inline versions of get/put short/long.  Pointer is advanced.
78 *
79 * These macros demonstrate the property of C whereby it can be
80 * portable or it can be elegant but rarely both.
81 */
82
83#ifndef INT32SZ
84# define INT32SZ	4
85#endif
86#ifndef INT16SZ
87# define INT16SZ	2
88#endif
89
90#ifndef GETSHORT
91#define GETSHORT(s, cp) { \
92	register u_char *t_cp = (u_char *)(cp); \
93	(s) = ((u_int16_t)t_cp[0] << 8) \
94	    | ((u_int16_t)t_cp[1]) \
95	    ; \
96	(cp) += INT16SZ; \
97}
98#endif
99
100#ifndef GETLONG
101#define GETLONG(l, cp) { \
102	register u_char *t_cp = (u_char *)(cp); \
103	(l) = ((u_int32_t)t_cp[0] << 24) \
104	    | ((u_int32_t)t_cp[1] << 16) \
105	    | ((u_int32_t)t_cp[2] << 8) \
106	    | ((u_int32_t)t_cp[3]) \
107	    ; \
108	(cp) += INT32SZ; \
109}
110#endif
111
112/*
113 * Routines to insert/extract short/long's.
114 */
115
116#ifndef HAVE__GETSHORT
117static u_int16_t
118_getshort(msgp)
119	register const u_char *msgp;
120{
121	register u_int16_t u;
122
123	GETSHORT(u, msgp);
124	return (u);
125}
126#elif defined(HAVE_DECL__GETSHORT) && (HAVE_DECL__GETSHORT == 0)
127u_int16_t _getshort(register const u_char *);
128#endif
129
130#ifndef HAVE__GETLONG
131static u_int32_t
132_getlong(msgp)
133	register const u_char *msgp;
134{
135	register u_int32_t u;
136
137	GETLONG(u, msgp);
138	return (u);
139}
140#elif defined(HAVE_DECL__GETLONG) && (HAVE_DECL__GETLONG == 0)
141u_int32_t _getlong(register const u_char *);
142#endif
143
144/* ************** */
145
146#define ANSWER_BUFFER_SIZE 0xffff
147
148struct dns_query {
149	char			*name;
150	u_int16_t		type;
151	u_int16_t		class;
152	struct dns_query	*next;
153};
154
155struct dns_rr {
156	char			*name;
157	u_int16_t		type;
158	u_int16_t		class;
159	u_int16_t		ttl;
160	u_int16_t		size;
161	void			*rdata;
162	struct dns_rr		*next;
163};
164
165/* <rdar://problem/6345666> Remove dependency on BIND_8_COMPAT
166 * Mimicing the used fields of the HEADER structure from the compat library.
167 *
168 * Note, ad is 1 bit in the original structure and 8 here.
169 */
170struct dns_header {
171	u_int8_t		ad;
172	u_int16_t		qdcount;
173	u_int16_t		ancount;
174	u_int16_t		nscount;
175	u_int16_t		arcount;
176};
177
178struct dns_response {
179	struct dns_header   	header;
180	struct dns_query	*query;
181	struct dns_rr		*answer;
182	struct dns_rr		*authority;
183	struct dns_rr		*additional;
184};
185
186static struct dns_response *parse_dns_response(const u_char *, int);
187static struct dns_query *parse_dns_qsection(const u_char *, int,
188    const u_char **, int);
189static struct dns_rr *parse_dns_rrsection(const u_char *, int, const u_char **,
190    int);
191
192static void free_dns_query(struct dns_query *);
193static void free_dns_rr(struct dns_rr *);
194static void free_dns_response(struct dns_response *);
195
196static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t);
197
198int
199getrrsetbyname(const char *hostname, unsigned int rdclass,
200    unsigned int rdtype, unsigned int flags,
201    struct rrsetinfo **res)
202{
203	struct __res_state *_resp = _THREAD_PRIVATE(_res, _res, &_res);
204	int result;
205	struct rrsetinfo *rrset = NULL;
206	struct dns_response *response = NULL;
207	struct dns_rr *rr;
208	struct rdatainfo *rdata;
209	int length;
210	unsigned int index_ans, index_sig;
211	u_char answer[ANSWER_BUFFER_SIZE];
212
213	/* check for invalid class and type */
214	if (rdclass > 0xffff || rdtype > 0xffff) {
215		result = ERRSET_INVAL;
216		goto fail;
217	}
218
219	/* don't allow queries of class or type ANY */
220	if (rdclass == 0xff || rdtype == 0xff) {
221		result = ERRSET_INVAL;
222		goto fail;
223	}
224
225	/* don't allow flags yet, unimplemented */
226	if (flags) {
227		result = ERRSET_INVAL;
228		goto fail;
229	}
230
231	/* initialize resolver */
232	if ((_resp->options & RES_INIT) == 0 && res_init() == -1) {
233		result = ERRSET_FAIL;
234		goto fail;
235	}
236
237#ifdef DEBUG
238	_resp->options |= RES_DEBUG;
239#endif /* DEBUG */
240
241#ifdef RES_USE_DNSSEC
242	/* turn on DNSSEC if EDNS0 is configured */
243	if (_resp->options & RES_USE_EDNS0)
244		_resp->options |= RES_USE_DNSSEC;
245#endif /* RES_USE_DNSEC */
246
247	/* make query */
248	length = res_query(hostname, (signed int) rdclass, (signed int) rdtype,
249	    answer, sizeof(answer));
250	if (length < 0) {
251		switch(h_errno) {
252		case HOST_NOT_FOUND:
253			result = ERRSET_NONAME;
254			goto fail;
255		case NO_DATA:
256			result = ERRSET_NODATA;
257			goto fail;
258		default:
259			result = ERRSET_FAIL;
260			goto fail;
261		}
262	}
263
264	/* parse result */
265	response = parse_dns_response(answer, length);
266	if (response == NULL) {
267		result = ERRSET_FAIL;
268		goto fail;
269	}
270
271	if (response->header.qdcount != 1) {
272		result = ERRSET_FAIL;
273		goto fail;
274	}
275
276	/* initialize rrset */
277	rrset = calloc(1, sizeof(struct rrsetinfo));
278	if (rrset == NULL) {
279		result = ERRSET_NOMEMORY;
280		goto fail;
281	}
282	rrset->rri_rdclass = response->query->class;
283	rrset->rri_rdtype = response->query->type;
284	rrset->rri_ttl = response->answer->ttl;
285	rrset->rri_nrdatas = response->header.ancount;
286
287#ifdef HAVE_HEADER_AD
288	/* check for authenticated data */
289	if (response->header.ad == 1)
290		rrset->rri_flags |= RRSET_VALIDATED;
291#endif
292
293	/* copy name from answer section */
294	rrset->rri_name = strdup(response->answer->name);
295	if (rrset->rri_name == NULL) {
296		result = 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		result = ERRSET_NOMEMORY;
311		goto fail;
312	}
313
314	/* allocate memory for signatures */
315	if (rrset->rri_nsigs > 0) {
316		rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo));
317		if (rrset->rri_sigs == NULL) {
318			result = ERRSET_NOMEMORY;
319			goto fail;
320		}
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				result = ERRSET_NOMEMORY;
343				goto fail;
344			}
345			memcpy(rdata->rdi_data, rr->rdata, rr->size);
346		}
347	}
348	free_dns_response(response);
349
350	*res = rrset;
351	return (ERRSET_SUCCESS);
352
353fail:
354	if (rrset != NULL)
355		freerrset(rrset);
356	if (response != NULL)
357		free_dns_response(response);
358	return (result);
359}
360
361void
362freerrset(struct rrsetinfo *rrset)
363{
364	u_int16_t i;
365
366	if (rrset == NULL)
367		return;
368
369	if (rrset->rri_rdatas) {
370		for (i = 0; i < rrset->rri_nrdatas; i++) {
371			if (rrset->rri_rdatas[i].rdi_data == NULL)
372				break;
373			free(rrset->rri_rdatas[i].rdi_data);
374		}
375		free(rrset->rri_rdatas);
376	}
377
378	if (rrset->rri_sigs) {
379		for (i = 0; i < rrset->rri_nsigs; i++) {
380			if (rrset->rri_sigs[i].rdi_data == NULL)
381				break;
382			free(rrset->rri_sigs[i].rdi_data);
383		}
384		free(rrset->rri_sigs);
385	}
386
387	if (rrset->rri_name)
388		free(rrset->rri_name);
389	free(rrset);
390}
391
392/*
393 * DNS response parsing routines
394 */
395static struct dns_response *
396parse_dns_response(const u_char *answer, int size)
397{
398	struct dns_response *resp;
399	const u_char *cp;
400
401	/* allocate memory for the response */
402	resp = calloc(1, sizeof(*resp));
403	if (resp == NULL)
404		return (NULL);
405
406	/* initialize current pointer */
407	cp = answer;
408
409	/* copy header */
410	memcpy(&resp->header, cp, HFIXEDSZ);
411	cp += HFIXEDSZ;
412
413	/* fix header byte order */
414	resp->header.qdcount = ntohs(resp->header.qdcount);
415	resp->header.ancount = ntohs(resp->header.ancount);
416	resp->header.nscount = ntohs(resp->header.nscount);
417	resp->header.arcount = ntohs(resp->header.arcount);
418
419	/* there must be at least one query */
420	if (resp->header.qdcount < 1) {
421		free_dns_response(resp);
422		return (NULL);
423	}
424
425	/* parse query section */
426	resp->query = parse_dns_qsection(answer, size, &cp,
427	    resp->header.qdcount);
428	if (resp->header.qdcount && resp->query == NULL) {
429		free_dns_response(resp);
430		return (NULL);
431	}
432
433	/* parse answer section */
434	resp->answer = parse_dns_rrsection(answer, size, &cp,
435	    resp->header.ancount);
436	if (resp->header.ancount && resp->answer == NULL) {
437		free_dns_response(resp);
438		return (NULL);
439	}
440
441	/* parse authority section */
442	resp->authority = parse_dns_rrsection(answer, size, &cp,
443	    resp->header.nscount);
444	if (resp->header.nscount && resp->authority == NULL) {
445		free_dns_response(resp);
446		return (NULL);
447	}
448
449	/* parse additional section */
450	resp->additional = parse_dns_rrsection(answer, size, &cp,
451	    resp->header.arcount);
452	if (resp->header.arcount && resp->additional == NULL) {
453		free_dns_response(resp);
454		return (NULL);
455	}
456
457	return (resp);
458}
459
460static struct dns_query *
461parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count)
462{
463	struct dns_query *head, *curr, *prev;
464	int i, length;
465	char name[NS_MAXDNAME];
466
467	for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
468
469		/* allocate and initialize struct */
470		curr = calloc(1, sizeof(struct dns_query));
471		if (curr == NULL) {
472			free_dns_query(head);
473			return (NULL);
474		}
475		if (head == NULL)
476			head = curr;
477		if (prev != NULL)
478			prev->next = curr;
479
480		/* name */
481		length = dn_expand(answer, answer + size, *cp, name,
482		    sizeof(name));
483		if (length < 0) {
484			free_dns_query(head);
485			return (NULL);
486		}
487		curr->name = strdup(name);
488		if (curr->name == NULL) {
489			free_dns_query(head);
490			return (NULL);
491		}
492		*cp += length;
493
494		/* type */
495		curr->type = _getshort(*cp);
496		*cp += INT16SZ;
497
498		/* class */
499		curr->class = _getshort(*cp);
500		*cp += INT16SZ;
501	}
502
503	return (head);
504}
505
506static struct dns_rr *
507parse_dns_rrsection(const u_char *answer, int size, const u_char **cp,
508    int count)
509{
510	struct dns_rr *head, *curr, *prev;
511	int i, length;
512	char name[NS_MAXDNAME];
513
514	for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) {
515
516		/* allocate and initialize struct */
517		curr = calloc(1, sizeof(struct dns_rr));
518		if (curr == NULL) {
519			free_dns_rr(head);
520			return (NULL);
521		}
522		if (head == NULL)
523			head = curr;
524		if (prev != NULL)
525			prev->next = curr;
526
527		/* name */
528		length = dn_expand(answer, answer + size, *cp, name,
529		    sizeof(name));
530		if (length < 0) {
531			free_dns_rr(head);
532			return (NULL);
533		}
534		curr->name = strdup(name);
535		if (curr->name == NULL) {
536			free_dns_rr(head);
537			return (NULL);
538		}
539		*cp += length;
540
541		/* type */
542		curr->type = _getshort(*cp);
543		*cp += INT16SZ;
544
545		/* class */
546		curr->class = _getshort(*cp);
547		*cp += INT16SZ;
548
549		/* ttl */
550		curr->ttl = _getlong(*cp);
551		*cp += INT32SZ;
552
553		/* rdata size */
554		curr->size = _getshort(*cp);
555		*cp += INT16SZ;
556
557		/* rdata itself */
558		curr->rdata = malloc(curr->size);
559		if (curr->rdata == NULL) {
560			free_dns_rr(head);
561			return (NULL);
562		}
563		memcpy(curr->rdata, *cp, curr->size);
564		*cp += curr->size;
565	}
566
567	return (head);
568}
569
570static void
571free_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
582static void
583free_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
596static void
597free_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
609static int
610count_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
623#endif /*  !defined (HAVE_GETRRSETBYNAME) && !defined (HAVE_LDNS) */
624