|  | /* $OpenBSD: getrrsetbyname.c,v 1.10 2005/03/30 02:58:28 tedu Exp $ */ | 
|  |  | 
|  | /* | 
|  | * Copyright (c) 2007 Simon Vallet / Genoscope <svallet@genoscope.cns.fr> | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
|  | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
|  | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
|  | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
|  | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
|  | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
|  | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | /* | 
|  | * Portions Copyright (c) 1999-2001 Internet Software Consortium. | 
|  | * | 
|  | * Permission to use, copy, modify, and distribute this software for any | 
|  | * purpose with or without fee is hereby granted, provided that the above | 
|  | * copyright notice and this permission notice appear in all copies. | 
|  | * | 
|  | * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM | 
|  | * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL | 
|  | * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, | 
|  | * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING | 
|  | * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, | 
|  | * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION | 
|  | * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
|  | */ | 
|  |  | 
|  | #include "includes.h" | 
|  |  | 
|  | #if !defined (HAVE_GETRRSETBYNAME) && defined (HAVE_LDNS) | 
|  |  | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  |  | 
|  | #include <ldns/ldns.h> | 
|  |  | 
|  | #include "getrrsetbyname.h" | 
|  | #include "log.h" | 
|  | #include "xmalloc.h" | 
|  |  | 
|  | #define malloc(x)	(xmalloc(x)) | 
|  | #define calloc(x, y)	(xcalloc((x),(y))) | 
|  |  | 
|  | int | 
|  | getrrsetbyname(const char *hostname, unsigned int rdclass, | 
|  | unsigned int rdtype, unsigned int flags, | 
|  | struct rrsetinfo **res) | 
|  | { | 
|  | int result; | 
|  | unsigned int i, j, index_ans, index_sig; | 
|  | struct rrsetinfo *rrset = NULL; | 
|  | struct rdatainfo *rdata; | 
|  | size_t len; | 
|  | ldns_resolver *ldns_res; | 
|  | ldns_rdf *domain = NULL; | 
|  | ldns_pkt *pkt = NULL; | 
|  | ldns_rr_list *rrsigs = NULL, *rrdata = NULL; | 
|  | ldns_status err; | 
|  | ldns_rr *rr; | 
|  |  | 
|  | /* check for invalid class and type */ | 
|  | if (rdclass > 0xffff || rdtype > 0xffff) { | 
|  | result = ERRSET_INVAL; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | /* don't allow queries of class or type ANY */ | 
|  | if (rdclass == 0xff || rdtype == 0xff) { | 
|  | result = ERRSET_INVAL; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | /* don't allow flags yet, unimplemented */ | 
|  | if (flags) { | 
|  | result = ERRSET_INVAL; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | /* Initialize resolver from resolv.conf */ | 
|  | domain = ldns_dname_new_frm_str(hostname); | 
|  | if ((err = ldns_resolver_new_frm_file(&ldns_res, NULL)) != \ | 
|  | LDNS_STATUS_OK) { | 
|  | result = ERRSET_FAIL; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | #ifdef LDNS_DEBUG | 
|  | ldns_resolver_set_debug(ldns_res, true); | 
|  | #endif /* LDNS_DEBUG */ | 
|  |  | 
|  | ldns_resolver_set_dnssec(ldns_res, true); /* Use DNSSEC */ | 
|  |  | 
|  | /* make query */ | 
|  | pkt = ldns_resolver_query(ldns_res, domain, rdtype, rdclass, LDNS_RD); | 
|  |  | 
|  | /*** TODO: finer errcodes -- see original **/ | 
|  | if (!pkt || ldns_pkt_ancount(pkt) < 1) { | 
|  | result = ERRSET_FAIL; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | /* initialize rrset */ | 
|  | rrset = calloc(1, sizeof(struct rrsetinfo)); | 
|  | if (rrset == NULL) { | 
|  | result = ERRSET_NOMEMORY; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | rrdata = ldns_pkt_rr_list_by_type(pkt, rdtype, LDNS_SECTION_ANSWER); | 
|  | rrset->rri_nrdatas = ldns_rr_list_rr_count(rrdata); | 
|  | if (!rrset->rri_nrdatas) { | 
|  | result = ERRSET_NODATA; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | /* copy name from answer section */ | 
|  | len = ldns_rdf_size(ldns_rr_owner(ldns_rr_list_rr(rrdata, 0))); | 
|  | if ((rrset->rri_name = malloc(len)) == NULL) { | 
|  | result = ERRSET_NOMEMORY; | 
|  | goto fail; | 
|  | } | 
|  | memcpy(rrset->rri_name, | 
|  | ldns_rdf_data(ldns_rr_owner(ldns_rr_list_rr(rrdata, 0))), len); | 
|  |  | 
|  | rrset->rri_rdclass = ldns_rr_get_class(ldns_rr_list_rr(rrdata, 0)); | 
|  | rrset->rri_rdtype = ldns_rr_get_type(ldns_rr_list_rr(rrdata, 0)); | 
|  | rrset->rri_ttl = ldns_rr_ttl(ldns_rr_list_rr(rrdata, 0)); | 
|  |  | 
|  | debug2("ldns: got %u answers from DNS", rrset->rri_nrdatas); | 
|  |  | 
|  | /* Check for authenticated data */ | 
|  | if (ldns_pkt_ad(pkt)) { | 
|  | rrset->rri_flags |= RRSET_VALIDATED; | 
|  | } else { /* AD is not set, try autonomous validation */ | 
|  | ldns_rr_list * trusted_keys = ldns_rr_list_new(); | 
|  |  | 
|  | debug2("ldns: trying to validate RRset"); | 
|  | /* Get eventual sigs */ | 
|  | rrsigs = ldns_pkt_rr_list_by_type(pkt, LDNS_RR_TYPE_RRSIG, | 
|  | LDNS_SECTION_ANSWER); | 
|  |  | 
|  | rrset->rri_nsigs = ldns_rr_list_rr_count(rrsigs); | 
|  | debug2("ldns: got %u signature(s) (RRTYPE %u) from DNS", | 
|  | rrset->rri_nsigs, LDNS_RR_TYPE_RRSIG); | 
|  |  | 
|  | if ((err = ldns_verify_trusted(ldns_res, rrdata, rrsigs, | 
|  | trusted_keys)) == LDNS_STATUS_OK) { | 
|  | rrset->rri_flags |= RRSET_VALIDATED; | 
|  | debug2("ldns: RRset is signed with a valid key"); | 
|  | } else { | 
|  | debug2("ldns: RRset validation failed: %s", | 
|  | ldns_get_errorstr_by_id(err)); | 
|  | } | 
|  |  | 
|  | ldns_rr_list_deep_free(trusted_keys); | 
|  | } | 
|  |  | 
|  | /* allocate memory for answers */ | 
|  | rrset->rri_rdatas = calloc(rrset->rri_nrdatas, | 
|  | sizeof(struct rdatainfo)); | 
|  |  | 
|  | if (rrset->rri_rdatas == NULL) { | 
|  | result = ERRSET_NOMEMORY; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | /* allocate memory for signatures */ | 
|  | if (rrset->rri_nsigs > 0) { | 
|  | rrset->rri_sigs = calloc(rrset->rri_nsigs, | 
|  | sizeof(struct rdatainfo)); | 
|  |  | 
|  | if (rrset->rri_sigs == NULL) { | 
|  | result = ERRSET_NOMEMORY; | 
|  | goto fail; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* copy answers & signatures */ | 
|  | for (i=0, index_ans=0, index_sig=0; i< pkt->_header->_ancount; i++) { | 
|  | rdata = NULL; | 
|  | rr = ldns_rr_list_rr(ldns_pkt_answer(pkt), i); | 
|  |  | 
|  | if (ldns_rr_get_class(rr) == rrset->rri_rdclass && | 
|  | ldns_rr_get_type(rr) == rrset->rri_rdtype) { | 
|  | rdata = &rrset->rri_rdatas[index_ans++]; | 
|  | } | 
|  |  | 
|  | if (rr->_rr_class == rrset->rri_rdclass && | 
|  | rr->_rr_type == LDNS_RR_TYPE_RRSIG && | 
|  | rrset->rri_sigs) { | 
|  | rdata = &rrset->rri_sigs[index_sig++]; | 
|  | } | 
|  |  | 
|  | if (rdata) { | 
|  | size_t rdata_offset = 0; | 
|  |  | 
|  | rdata->rdi_length = 0; | 
|  | for (j=0; j< rr->_rd_count; j++) { | 
|  | rdata->rdi_length += | 
|  | ldns_rdf_size(ldns_rr_rdf(rr, j)); | 
|  | } | 
|  |  | 
|  | rdata->rdi_data = malloc(rdata->rdi_length); | 
|  | if (rdata->rdi_data == NULL) { | 
|  | result = ERRSET_NOMEMORY; | 
|  | goto fail; | 
|  | } | 
|  |  | 
|  | /* Re-create the raw DNS RDATA */ | 
|  | for (j=0; j< rr->_rd_count; j++) { | 
|  | len = ldns_rdf_size(ldns_rr_rdf(rr, j)); | 
|  | memcpy(rdata->rdi_data + rdata_offset, | 
|  | ldns_rdf_data(ldns_rr_rdf(rr, j)), len); | 
|  | rdata_offset += len; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | *res = rrset; | 
|  | result = ERRSET_SUCCESS; | 
|  |  | 
|  | fail: | 
|  | /* freerrset(rrset); */ | 
|  | ldns_rdf_deep_free(domain); | 
|  | ldns_pkt_free(pkt); | 
|  | ldns_rr_list_deep_free(rrsigs); | 
|  | ldns_rr_list_deep_free(rrdata); | 
|  | ldns_resolver_deep_free(ldns_res); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  |  | 
|  | void | 
|  | freerrset(struct rrsetinfo *rrset) | 
|  | { | 
|  | u_int16_t i; | 
|  |  | 
|  | if (rrset == NULL) | 
|  | return; | 
|  |  | 
|  | if (rrset->rri_rdatas) { | 
|  | for (i = 0; i < rrset->rri_nrdatas; i++) { | 
|  | if (rrset->rri_rdatas[i].rdi_data == NULL) | 
|  | break; | 
|  | free(rrset->rri_rdatas[i].rdi_data); | 
|  | } | 
|  | free(rrset->rri_rdatas); | 
|  | } | 
|  |  | 
|  | if (rrset->rri_sigs) { | 
|  | for (i = 0; i < rrset->rri_nsigs; i++) { | 
|  | if (rrset->rri_sigs[i].rdi_data == NULL) | 
|  | break; | 
|  | free(rrset->rri_sigs[i].rdi_data); | 
|  | } | 
|  | free(rrset->rri_sigs); | 
|  | } | 
|  |  | 
|  | if (rrset->rri_name) | 
|  | free(rrset->rri_name); | 
|  | free(rrset); | 
|  | } | 
|  |  | 
|  |  | 
|  | #endif /* !defined (HAVE_GETRRSETBYNAME) && defined (HAVE_LDNS) */ |