1239844Sdes/* $OpenBSD: getrrsetbyname.c,v 1.10 2005/03/30 02:58:28 tedu Exp $ */
2239844Sdes
3239844Sdes/*
4239844Sdes * Copyright (c) 2007 Simon Vallet / Genoscope <svallet@genoscope.cns.fr>
5239844Sdes *
6239844Sdes * Redistribution and use in source and binary forms, with or without
7239844Sdes * modification, are permitted provided that the following conditions
8239844Sdes * are met:
9239844Sdes *
10239844Sdes * 1. Redistributions of source code must retain the above copyright
11239844Sdes *    notice, this list of conditions and the following disclaimer.
12239844Sdes *
13239844Sdes * 2. Redistributions in binary form must reproduce the above copyright
14239844Sdes *    notice, this list of conditions and the following disclaimer in the
15239844Sdes *    documentation and/or other materials provided with the distribution.
16239844Sdes *
17239844Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18239844Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19239844Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20239844Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21239844Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22239844Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23239844Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24239844Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25239844Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26239844Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27239844Sdes */
28239844Sdes
29239844Sdes/*
30239844Sdes * Portions Copyright (c) 1999-2001 Internet Software Consortium.
31239844Sdes *
32239844Sdes * Permission to use, copy, modify, and distribute this software for any
33239844Sdes * purpose with or without fee is hereby granted, provided that the above
34239844Sdes * copyright notice and this permission notice appear in all copies.
35239844Sdes *
36239844Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
37239844Sdes * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
38239844Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
39239844Sdes * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
40239844Sdes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
41239844Sdes * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
42239844Sdes * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
43239844Sdes * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
44239844Sdes */
45239844Sdes
46239844Sdes#include "includes.h"
47239844Sdes
48239844Sdes#if !defined (HAVE_GETRRSETBYNAME) && defined (HAVE_LDNS)
49239844Sdes
50239844Sdes#include <stdlib.h>
51239844Sdes#include <string.h>
52239844Sdes
53239844Sdes#include <ldns/ldns.h>
54239844Sdes
55239844Sdes#include "getrrsetbyname.h"
56239844Sdes#include "log.h"
57239844Sdes#include "xmalloc.h"
58239844Sdes
59239844Sdes#define malloc(x)	(xmalloc(x))
60239844Sdes#define calloc(x, y)	(xcalloc((x),(y)))
61239844Sdes
62239844Sdesint
63239844Sdesgetrrsetbyname(const char *hostname, unsigned int rdclass,
64239844Sdes	       unsigned int rdtype, unsigned int flags,
65239844Sdes	       struct rrsetinfo **res)
66239844Sdes{
67239844Sdes	int result;
68239844Sdes	unsigned int i, j, index_ans, index_sig;
69239844Sdes	struct rrsetinfo *rrset = NULL;
70239844Sdes	struct rdatainfo *rdata;
71239844Sdes	size_t len;
72295367Sdes	ldns_resolver *ldns_res = NULL;
73239844Sdes	ldns_rdf *domain = NULL;
74239844Sdes	ldns_pkt *pkt = NULL;
75239844Sdes	ldns_rr_list *rrsigs = NULL, *rrdata = NULL;
76239844Sdes	ldns_status err;
77239844Sdes	ldns_rr *rr;
78239844Sdes
79239844Sdes	/* check for invalid class and type */
80239844Sdes	if (rdclass > 0xffff || rdtype > 0xffff) {
81239844Sdes		result = ERRSET_INVAL;
82239844Sdes		goto fail;
83239844Sdes	}
84239844Sdes
85239844Sdes	/* don't allow queries of class or type ANY */
86239844Sdes	if (rdclass == 0xff || rdtype == 0xff) {
87239844Sdes		result = ERRSET_INVAL;
88239844Sdes		goto fail;
89239844Sdes	}
90239844Sdes
91239844Sdes	/* don't allow flags yet, unimplemented */
92239844Sdes	if (flags) {
93239844Sdes		result = ERRSET_INVAL;
94239844Sdes		goto fail;
95239844Sdes	}
96239844Sdes
97239844Sdes	/* Initialize resolver from resolv.conf */
98239844Sdes	domain = ldns_dname_new_frm_str(hostname);
99239844Sdes	if ((err = ldns_resolver_new_frm_file(&ldns_res, NULL)) != \
100239844Sdes	    LDNS_STATUS_OK) {
101239844Sdes		result = ERRSET_FAIL;
102239844Sdes		goto fail;
103239844Sdes	}
104239844Sdes
105239844Sdes#ifdef LDNS_DEBUG
106239844Sdes	ldns_resolver_set_debug(ldns_res, true);
107239844Sdes#endif /* LDNS_DEBUG */
108239844Sdes
109239844Sdes	ldns_resolver_set_dnssec(ldns_res, true); /* Use DNSSEC */
110239844Sdes
111239844Sdes	/* make query */
112239844Sdes	pkt = ldns_resolver_query(ldns_res, domain, rdtype, rdclass, LDNS_RD);
113239844Sdes
114239844Sdes	/*** TODO: finer errcodes -- see original **/
115239844Sdes	if (!pkt || ldns_pkt_ancount(pkt) < 1) {
116239844Sdes		result = ERRSET_FAIL;
117239844Sdes		goto fail;
118239844Sdes	}
119239844Sdes
120239844Sdes	/* initialize rrset */
121239844Sdes	rrset = calloc(1, sizeof(struct rrsetinfo));
122239844Sdes	if (rrset == NULL) {
123239844Sdes		result = ERRSET_NOMEMORY;
124239844Sdes		goto fail;
125239844Sdes	}
126239844Sdes
127239844Sdes	rrdata = ldns_pkt_rr_list_by_type(pkt, rdtype, LDNS_SECTION_ANSWER);
128239844Sdes	rrset->rri_nrdatas = ldns_rr_list_rr_count(rrdata);
129239844Sdes	if (!rrset->rri_nrdatas) {
130239844Sdes		result = ERRSET_NODATA;
131239844Sdes		goto fail;
132239844Sdes	}
133239844Sdes
134239844Sdes	/* copy name from answer section */
135239844Sdes	len = ldns_rdf_size(ldns_rr_owner(ldns_rr_list_rr(rrdata, 0)));
136239844Sdes	if ((rrset->rri_name = malloc(len)) == NULL) {
137239844Sdes		result = ERRSET_NOMEMORY;
138239844Sdes		goto fail;
139239844Sdes	}
140239844Sdes	memcpy(rrset->rri_name,
141239844Sdes	    ldns_rdf_data(ldns_rr_owner(ldns_rr_list_rr(rrdata, 0))), len);
142239844Sdes
143239844Sdes	rrset->rri_rdclass = ldns_rr_get_class(ldns_rr_list_rr(rrdata, 0));
144239844Sdes	rrset->rri_rdtype = ldns_rr_get_type(ldns_rr_list_rr(rrdata, 0));
145239844Sdes	rrset->rri_ttl = ldns_rr_ttl(ldns_rr_list_rr(rrdata, 0));
146239844Sdes
147239844Sdes	debug2("ldns: got %u answers from DNS", rrset->rri_nrdatas);
148239844Sdes
149239844Sdes	/* Check for authenticated data */
150239844Sdes	if (ldns_pkt_ad(pkt)) {
151239844Sdes		rrset->rri_flags |= RRSET_VALIDATED;
152239844Sdes	} else { /* AD is not set, try autonomous validation */
153239844Sdes		ldns_rr_list * trusted_keys = ldns_rr_list_new();
154239844Sdes
155239844Sdes		debug2("ldns: trying to validate RRset");
156239844Sdes		/* Get eventual sigs */
157239844Sdes		rrsigs = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_RRSIG,
158239844Sdes		    LDNS_SECTION_ANSWER);
159239844Sdes
160239844Sdes		rrset->rri_nsigs = ldns_rr_list_rr_count(rrsigs);
161239844Sdes		debug2("ldns: got %u signature(s) (RRTYPE %u) from DNS",
162239844Sdes		       rrset->rri_nsigs, LDNS_RR_TYPE_RRSIG);
163239844Sdes
164239844Sdes		if ((err = ldns_verify_trusted(ldns_res, rrdata, rrsigs,
165239844Sdes		     trusted_keys)) == LDNS_STATUS_OK) {
166239844Sdes			rrset->rri_flags |= RRSET_VALIDATED;
167239844Sdes			debug2("ldns: RRset is signed with a valid key");
168239844Sdes		} else {
169239844Sdes			debug2("ldns: RRset validation failed: %s",
170239844Sdes			    ldns_get_errorstr_by_id(err));
171239844Sdes		}
172239844Sdes
173239844Sdes		ldns_rr_list_deep_free(trusted_keys);
174239844Sdes	}
175239844Sdes
176239844Sdes	/* allocate memory for answers */
177239844Sdes	rrset->rri_rdatas = calloc(rrset->rri_nrdatas,
178239844Sdes	   sizeof(struct rdatainfo));
179239844Sdes
180239844Sdes	if (rrset->rri_rdatas == NULL) {
181239844Sdes		result = ERRSET_NOMEMORY;
182239844Sdes		goto fail;
183239844Sdes	}
184239844Sdes
185239844Sdes	/* allocate memory for signatures */
186239844Sdes	if (rrset->rri_nsigs > 0) {
187239844Sdes		rrset->rri_sigs = calloc(rrset->rri_nsigs,
188239844Sdes		    sizeof(struct rdatainfo));
189239844Sdes
190239844Sdes		if (rrset->rri_sigs == NULL) {
191239844Sdes			result = ERRSET_NOMEMORY;
192239844Sdes			goto fail;
193239844Sdes		}
194239844Sdes	}
195239844Sdes
196239844Sdes	/* copy answers & signatures */
197239844Sdes	for (i=0, index_ans=0, index_sig=0; i< pkt->_header->_ancount; i++) {
198239844Sdes		rdata = NULL;
199239844Sdes		rr = ldns_rr_list_rr(ldns_pkt_answer(pkt), i);
200239844Sdes
201239844Sdes		if (ldns_rr_get_class(rr) == rrset->rri_rdclass &&
202239844Sdes		    ldns_rr_get_type(rr) == rrset->rri_rdtype) {
203239844Sdes			rdata = &rrset->rri_rdatas[index_ans++];
204239844Sdes		}
205239844Sdes
206239844Sdes		if (rr->_rr_class == rrset->rri_rdclass &&
207239849Sdes		    rr->_rr_type == LDNS_RR_TYPE_RRSIG &&
208239849Sdes		    rrset->rri_sigs) {
209239844Sdes			rdata = &rrset->rri_sigs[index_sig++];
210239844Sdes		}
211239844Sdes
212239844Sdes		if (rdata) {
213239844Sdes			size_t rdata_offset = 0;
214239844Sdes
215239844Sdes			rdata->rdi_length = 0;
216239844Sdes			for (j=0; j< rr->_rd_count; j++) {
217239844Sdes				rdata->rdi_length +=
218239844Sdes				    ldns_rdf_size(ldns_rr_rdf(rr, j));
219239844Sdes			}
220239844Sdes
221239844Sdes			rdata->rdi_data = malloc(rdata->rdi_length);
222239844Sdes			if (rdata->rdi_data == NULL) {
223239844Sdes				result = ERRSET_NOMEMORY;
224239844Sdes				goto fail;
225239844Sdes			}
226239844Sdes
227239844Sdes			/* Re-create the raw DNS RDATA */
228239844Sdes			for (j=0; j< rr->_rd_count; j++) {
229239844Sdes				len = ldns_rdf_size(ldns_rr_rdf(rr, j));
230239844Sdes				memcpy(rdata->rdi_data + rdata_offset,
231239844Sdes				       ldns_rdf_data(ldns_rr_rdf(rr, j)), len);
232239844Sdes				rdata_offset += len;
233239844Sdes			}
234239844Sdes		}
235239844Sdes	}
236239844Sdes
237239844Sdes	*res = rrset;
238239844Sdes	result = ERRSET_SUCCESS;
239239844Sdes
240239844Sdesfail:
241239844Sdes	/* freerrset(rrset); */
242239844Sdes	ldns_rdf_deep_free(domain);
243239844Sdes	ldns_pkt_free(pkt);
244239844Sdes	ldns_rr_list_deep_free(rrsigs);
245239844Sdes	ldns_rr_list_deep_free(rrdata);
246239844Sdes	ldns_resolver_deep_free(ldns_res);
247239844Sdes
248239844Sdes	return result;
249239844Sdes}
250239844Sdes
251239844Sdes
252239844Sdesvoid
253239844Sdesfreerrset(struct rrsetinfo *rrset)
254239844Sdes{
255239844Sdes	u_int16_t i;
256239844Sdes
257239844Sdes	if (rrset == NULL)
258239844Sdes		return;
259239844Sdes
260239844Sdes	if (rrset->rri_rdatas) {
261239844Sdes		for (i = 0; i < rrset->rri_nrdatas; i++) {
262239844Sdes			if (rrset->rri_rdatas[i].rdi_data == NULL)
263239844Sdes				break;
264239844Sdes			free(rrset->rri_rdatas[i].rdi_data);
265239844Sdes		}
266239844Sdes		free(rrset->rri_rdatas);
267239844Sdes	}
268239844Sdes
269239844Sdes	if (rrset->rri_sigs) {
270239844Sdes		for (i = 0; i < rrset->rri_nsigs; i++) {
271239844Sdes			if (rrset->rri_sigs[i].rdi_data == NULL)
272239844Sdes				break;
273239844Sdes			free(rrset->rri_sigs[i].rdi_data);
274239844Sdes		}
275239844Sdes		free(rrset->rri_sigs);
276239844Sdes	}
277239844Sdes
278239844Sdes	if (rrset->rri_name)
279239844Sdes		free(rrset->rri_name);
280239844Sdes	free(rrset);
281239844Sdes}
282239844Sdes
283239844Sdes
284239844Sdes#endif /* !defined (HAVE_GETRRSETBYNAME) && defined (HAVE_LDNS) */
285