From 0797b60bfb8891be7182682a55e54c4bb65b1f8e Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Mon, 3 Feb 2014 23:34:55 +0100 Subject: [PATCH] Compartmentalize priv_getdns_get_validation_chain --- src/Makefile.in | 2 +- src/general.c | 228 +-------------------------------- src/general.h | 2 + src/validation-chain.c | 283 +++++++++++++++++++++++++++++++++++++++++ src/validation-chain.h | 48 +++++++ 5 files changed, 340 insertions(+), 223 deletions(-) create mode 100644 src/validation-chain.c create mode 100644 src/validation-chain.h diff --git a/src/Makefile.in b/src/Makefile.in index c03cfa09..0b9e55ab 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -33,7 +33,7 @@ CFLAGS=@CFLAGS@ -Wall -I$(srcdir)/ -I/usr/local/include -std=c99 LDFLAGS=@LDFLAGS@ @LIBS@ GETDNS_OBJ=sync.lo context.lo list.lo dict.lo convert.lo general.lo \ hostname.lo service.lo request-internal.lo validate_dnssec.lo \ - util-internal.lo getdns_error.lo rr-dict.lo + util-internal.lo getdns_error.lo rr-dict.lo validation-chain.lo .SUFFIXES: .c .o .a .lo .h diff --git a/src/general.c b/src/general.c index 7cd0b53d..64aa3493 100644 --- a/src/general.c +++ b/src/general.c @@ -50,6 +50,7 @@ #include "context.h" #include "types-internal.h" #include "util-internal.h" +#include "validation-chain.h" #include /* stuff to make it compile pedantically */ @@ -131,7 +132,7 @@ ub_local_resolve_timeout(evutil_socket_t fd, short what, void *arg) free(cb_data); } -static void call_user_callback(getdns_dns_req *dns_req, +void priv_getdns_call_user_callback(getdns_dns_req *dns_req, struct getdns_dict *response) { struct getdns_context *context = dns_req->context; @@ -152,225 +153,7 @@ static void call_user_callback(getdns_dns_req *dns_req, static void handle_network_request_error(getdns_network_req * netreq, int err) { - call_user_callback(netreq->owner, NULL); -} - -struct validation_chain { - ldns_rbtree_t root; - struct mem_funcs mf; - getdns_dns_req *dns_req; - size_t todo; -}; -struct chain_response { - int err; - ldns_rr_list *result; - int sec; - char *bogus; - struct validation_chain *chain; - int unbound_id; -}; -struct chain_link { - ldns_rbnode_t node; - struct chain_response DNSKEY; - struct chain_response DS; -}; - -static void submit_link(struct validation_chain *chain, char *name); -static void callback_on_complete_chain(struct validation_chain *chain); -static void -ub_supporting_callback(void *arg, int err, void *result, int packet_len, - int sec, char *bogus) -{ - struct chain_response *response = (struct chain_response *) arg; - ldns_status r; - ldns_pkt *p; - ldns_rr_list *answer; - ldns_rr_list *keys; - size_t i; - - response->err = err; - response->sec = sec; - response->bogus = bogus; - - if (result == NULL) - goto done; - - r = ldns_wire2pkt(&p, (uint8_t *)result, (size_t)packet_len); - if (r != LDNS_STATUS_OK) { - if (err == 0) - response->err = r; - goto done; - } - - keys = ldns_rr_list_new(); - answer = ldns_pkt_answer(p); - for (i = 0; i < ldns_rr_list_rr_count(answer); i++) { - ldns_rr *rr = ldns_rr_list_rr(answer, i); - - if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_DNSKEY || - ldns_rr_get_type(rr) == LDNS_RR_TYPE_DS) { - - (void) ldns_rr_list_push_rr(keys, ldns_rr_clone(rr)); - continue; - } - if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_RRSIG) - continue; - - if (ldns_read_uint16(ldns_rdf_data(ldns_rr_rdf(rr, 0))) == - LDNS_RR_TYPE_DS) - submit_link(response->chain, - ldns_rdf2str(ldns_rr_rdf(rr, 7))); - - else if (ldns_read_uint16(ldns_rdf_data(ldns_rr_rdf(rr, 0))) != - LDNS_RR_TYPE_DNSKEY) - continue; - - (void) ldns_rr_list_push_rr(keys, ldns_rr_clone(rr)); - } - if (ldns_rr_list_rr_count(keys)) - response->result = keys; - else - ldns_rr_list_free(keys); - - ldns_pkt_free(p); - -done: if (response->err == 0 && response->result == NULL) - response->err = -1; - - callback_on_complete_chain(response->chain); -} - -static void submit_link(struct validation_chain *chain, char *name) -{ - int r; - struct chain_link *link = (struct chain_link *) - ldns_rbtree_search((ldns_rbtree_t *)&(chain->root), name); - - if (link) { - free(name); - return; - } - link = GETDNS_MALLOC(chain->mf, struct chain_link); - link->node.key = name; - - link->DNSKEY.err = 0; - link->DNSKEY.result = NULL; - link->DNSKEY.sec = 0; - link->DNSKEY.bogus = NULL; - link->DNSKEY.chain = chain; - link->DNSKEY.unbound_id = -1; - - link->DS.err = 0; - link->DS.result = NULL; - link->DS.sec = 0; - link->DS.bogus = NULL; - link->DS.chain = chain; - link->DS.unbound_id = -1; - - ldns_rbtree_insert(&(chain->root), (ldns_rbnode_t *)link); - - chain->todo++; - r = ub_resolve_event(chain->dns_req->unbound, - name, LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN, &link->DNSKEY, - ub_supporting_callback, &link->DNSKEY.unbound_id); - if (r != 0) - link->DNSKEY.err = r; - - if (name[0] != '.' || name[1] != '\0') { - r = ub_resolve_event(chain->dns_req->unbound, - name, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN, &link->DS, - ub_supporting_callback, &link->DS.unbound_id); - if (r != 0) - link->DS.err = r; - } - chain->todo--; -} - -void destroy_chain_link(ldns_rbnode_t * node, void *arg) -{ - struct chain_link *link = (struct chain_link*) node; - struct validation_chain *chain = (struct validation_chain*) arg; - - free((void *)link->node.key); - ldns_rr_list_deep_free(link->DNSKEY.result); - ldns_rr_list_deep_free(link->DS.result); - GETDNS_FREE(chain->mf, link); -} - -static void destroy_chain(struct getdns_context *context, - struct validation_chain *chain) -{ - ldns_traverse_postorder(&(chain->root), - destroy_chain_link, chain); - GETDNS_FREE(chain->mf, chain); -} - -static void callback_on_complete_chain(struct validation_chain *chain) -{ - struct getdns_context *context = chain->dns_req->context; - struct getdns_dict *response; - struct chain_link *link; - size_t todo = chain->todo; - ldns_rr_list *keys; - struct getdns_list *getdns_keys; - - LDNS_RBTREE_FOR(link, struct chain_link *, - (ldns_rbtree_t *)&(chain->root)) { - if (link->DNSKEY.result == NULL && link->DNSKEY.err == 0) - todo++; - if (link->DS.result == NULL && link->DS.err == 0 && - (((const char *)link->node.key)[0] != '.' || - ((const char *)link->node.key)[1] != '\0' )) - todo++; - } - if (todo == 0) { - getdns_dns_req *dns_req = chain->dns_req; - response = create_getdns_response(chain->dns_req); - - keys = ldns_rr_list_new(); - LDNS_RBTREE_FOR(link, struct chain_link *, - (ldns_rbtree_t *)&(chain->root)) { - (void) ldns_rr_list_cat(keys, link->DNSKEY.result); - (void) ldns_rr_list_cat(keys, link->DS.result); - } - getdns_keys = create_list_from_rr_list(context, keys); - (void) getdns_dict_set_list(response, "validation_chain", - getdns_keys); - getdns_list_destroy(getdns_keys); - ldns_rr_list_free(keys); - destroy_chain(context, chain); - call_user_callback(dns_req, response); - } -} - -/* Do some additional requests to fetch the complete validation chain */ -static void get_validation_chain(getdns_dns_req *dns_req) -{ - getdns_network_req *netreq = dns_req->first_req; - struct validation_chain *chain = GETDNS_MALLOC(dns_req->context->mf, - struct validation_chain); - - ldns_rbtree_init(&(chain->root), - (int (*)(const void *, const void *)) strcmp); - chain->mf.mf_arg = dns_req->context->mf.mf_arg; - chain->mf.mf.ext.malloc = dns_req->context->mf.mf.ext.malloc; - chain->mf.mf.ext.realloc = dns_req->context->mf.mf.ext.realloc; - chain->mf.mf.ext.free = dns_req->context->mf.mf.ext.free; - chain->dns_req = dns_req; - chain->todo = 0; - - while (netreq) { - size_t i; - ldns_rr_list *answer = ldns_pkt_answer(netreq->result); - for (i = 0; i < ldns_rr_list_rr_count(answer); i++) { - ldns_rr *rr = ldns_rr_list_rr(answer, i); - if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_RRSIG) - submit_link(chain, - ldns_rdf2str(ldns_rr_rdf(rr, 7))); - } - netreq = netreq->next; - } - callback_on_complete_chain(chain); + priv_getdns_call_user_callback(netreq->owner, NULL); } /* cleanup and send the response to the user callback */ @@ -383,9 +166,10 @@ handle_dns_request_complete(getdns_dns_req * dns_req) if (r == GETDNS_RETURN_GOOD && ret_chain_ext == GETDNS_EXTENSION_TRUE) - get_validation_chain(dns_req); + priv_getdns_get_validation_chain(dns_req); else - call_user_callback(dns_req, create_getdns_response(dns_req)); + priv_getdns_call_user_callback( + dns_req, create_getdns_response(dns_req)); } static int diff --git a/src/general.h b/src/general.h index 3885e37b..5e2dc674 100644 --- a/src/general.h +++ b/src/general.h @@ -53,4 +53,6 @@ getdns_general_ub(struct ub_ctx *unbound, void *userarg, getdns_transaction_t * transaction_id, getdns_callback_t callbackfn); +void priv_getdns_call_user_callback(getdns_dns_req *, struct getdns_dict *); + #endif diff --git a/src/validation-chain.c b/src/validation-chain.c new file mode 100644 index 00000000..84b506f2 --- /dev/null +++ b/src/validation-chain.c @@ -0,0 +1,283 @@ +/** + * + * /brief priv_getdns_get_validation_chain function + * + * The priv_getdns_get_validation_chain function is called after an answer + * has been fetched when the dnssec_return_validation_chain extension is set. + * It fetches DNSKEYs, DSes and their signatures for all RRSIGs found in the + * answer. + */ + +/* + * Copyright (c) 2013, Versign, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 Verisign, Inc. 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. + */ + +#include +#include +#include +#include +#include "context.h" +#include "util-internal.h" +#include "types-internal.h" + +void priv_getdns_call_user_callback(getdns_dns_req *, struct getdns_dict *); + +struct validation_chain { + ldns_rbtree_t root; + struct mem_funcs mf; + getdns_dns_req *dns_req; + size_t lock; +}; + +struct chain_response { + int err; + ldns_rr_list *result; + int sec; + char *bogus; + struct validation_chain *chain; + int unbound_id; +}; + +struct chain_link { + ldns_rbnode_t node; + struct chain_response DNSKEY; + struct chain_response DS; +}; + +static void launch_chain_link_lookup(struct validation_chain *chain, char *name); +static void destroy_chain(struct validation_chain *chain); + +static void callback_on_complete_chain(struct validation_chain *chain) +{ + struct getdns_context *context = chain->dns_req->context; + struct getdns_dict *response; + struct chain_link *link; + size_t ongoing = chain->lock; + ldns_rr_list *keys; + struct getdns_list *getdns_keys; + + LDNS_RBTREE_FOR(link, struct chain_link *, + (ldns_rbtree_t *)&(chain->root)) { + if (link->DNSKEY.result == NULL && link->DNSKEY.err == 0) + ongoing++; + if (link->DS.result == NULL && link->DS.err == 0 && + (((const char *)link->node.key)[0] != '.' || + ((const char *)link->node.key)[1] != '\0' )) + ongoing++; + } + if (ongoing == 0) { + getdns_dns_req *dns_req = chain->dns_req; + response = create_getdns_response(chain->dns_req); + + keys = ldns_rr_list_new(); + LDNS_RBTREE_FOR(link, struct chain_link *, + (ldns_rbtree_t *)&(chain->root)) { + (void) ldns_rr_list_cat(keys, link->DNSKEY.result); + (void) ldns_rr_list_cat(keys, link->DS.result); + } + getdns_keys = create_list_from_rr_list(context, keys); + (void) getdns_dict_set_list(response, "validation_chain", + getdns_keys); + getdns_list_destroy(getdns_keys); + ldns_rr_list_free(keys); + destroy_chain(chain); + priv_getdns_call_user_callback(dns_req, response); + } +} + + +static void +ub_chain_response_callback(void *arg, int err, void *result, int packet_len, + int sec, char *bogus) +{ + struct chain_response *response = (struct chain_response *) arg; + ldns_status r; + ldns_pkt *p; + ldns_rr_list *answer; + ldns_rr_list *keys; + size_t i; + + response->err = err; + response->sec = sec; + response->bogus = bogus; + + if (result == NULL) + goto done; + + r = ldns_wire2pkt(&p, (uint8_t *)result, (size_t)packet_len); + if (r != LDNS_STATUS_OK) { + if (err == 0) + response->err = r; + goto done; + } + + keys = ldns_rr_list_new(); + answer = ldns_pkt_answer(p); + for (i = 0; i < ldns_rr_list_rr_count(answer); i++) { + ldns_rr *rr = ldns_rr_list_rr(answer, i); + + if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_DNSKEY || + ldns_rr_get_type(rr) == LDNS_RR_TYPE_DS) { + + (void) ldns_rr_list_push_rr(keys, ldns_rr_clone(rr)); + continue; + } + if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_RRSIG) + continue; + + if (ldns_read_uint16(ldns_rdf_data(ldns_rr_rdf(rr, 0))) == + LDNS_RR_TYPE_DS) + launch_chain_link_lookup(response->chain, + ldns_rdf2str(ldns_rr_rdf(rr, 7))); + + else if (ldns_read_uint16(ldns_rdf_data(ldns_rr_rdf(rr, 0))) != + LDNS_RR_TYPE_DNSKEY) + continue; + + (void) ldns_rr_list_push_rr(keys, ldns_rr_clone(rr)); + } + if (ldns_rr_list_rr_count(keys)) + response->result = keys; + else + ldns_rr_list_free(keys); + + ldns_pkt_free(p); + +done: if (response->err == 0 && response->result == NULL) + response->err = -1; + + callback_on_complete_chain(response->chain); +} + +static void chain_response_init( + struct validation_chain *chain, struct chain_response *response) +{ + response->err = 0; + response->result = NULL; + response->sec = 0; + response->bogus = NULL; + response->chain = chain; + response->unbound_id = -1; +} + +static void launch_chain_link_lookup(struct validation_chain *chain, char *name) +{ + int r; + struct chain_link *link = (struct chain_link *) + ldns_rbtree_search((ldns_rbtree_t *)&(chain->root), name); + + if (link) { + free(name); + return; + } + link = GETDNS_MALLOC(chain->mf, struct chain_link); + link->node.key = name; + + chain_response_init(chain, &link->DNSKEY); + chain_response_init(chain, &link->DS); + + ldns_rbtree_insert(&(chain->root), (ldns_rbnode_t *)link); + + chain->lock++; + r = ub_resolve_event(chain->dns_req->unbound, + name, LDNS_RR_TYPE_DNSKEY, LDNS_RR_CLASS_IN, &link->DNSKEY, + ub_chain_response_callback, &link->DNSKEY.unbound_id); + if (r != 0) + link->DNSKEY.err = r; + + if (name[0] != '.' || name[1] != '\0') { + r = ub_resolve_event(chain->dns_req->unbound, + name, LDNS_RR_TYPE_DS, LDNS_RR_CLASS_IN, &link->DS, + ub_chain_response_callback, &link->DS.unbound_id); + if (r != 0) + link->DS.err = r; + } + chain->lock--; +} + +static struct validation_chain *create_chain(getdns_dns_req *dns_req) +{ + struct validation_chain *chain = GETDNS_MALLOC( + dns_req->context->mf, struct validation_chain); + + if (! chain) + return NULL; + + ldns_rbtree_init(&(chain->root), + (int (*)(const void *, const void *)) strcmp); + chain->mf.mf_arg = dns_req->context->mf.mf_arg; + chain->mf.mf.ext.malloc = dns_req->context->mf.mf.ext.malloc; + chain->mf.mf.ext.realloc = dns_req->context->mf.mf.ext.realloc; + chain->mf.mf.ext.free = dns_req->context->mf.mf.ext.free; + chain->dns_req = dns_req; + chain->lock = 0; + return chain; +} + +static void destroy_chain_link(ldns_rbnode_t * node, void *arg) +{ + struct chain_link *link = (struct chain_link*) node; + struct validation_chain *chain = (struct validation_chain*) arg; + + free((void *)link->node.key); + ldns_rr_list_deep_free(link->DNSKEY.result); + ldns_rr_list_deep_free(link->DS.result); + GETDNS_FREE(chain->mf, link); +} + +static void destroy_chain(struct validation_chain *chain) +{ + ldns_traverse_postorder(&(chain->root), + destroy_chain_link, chain); + GETDNS_FREE(chain->mf, chain); +} + +/* Do some additional requests to fetch the complete validation chain */ +void priv_getdns_get_validation_chain(getdns_dns_req *dns_req) +{ + getdns_network_req *netreq = dns_req->first_req; + struct validation_chain *chain = create_chain(dns_req); + + if (! chain) { + priv_getdns_call_user_callback( + dns_req, create_getdns_response(chain->dns_req)); + return; + } + while (netreq) { + size_t i; + ldns_rr_list *answer = ldns_pkt_answer(netreq->result); + for (i = 0; i < ldns_rr_list_rr_count(answer); i++) { + ldns_rr *rr = ldns_rr_list_rr(answer, i); + if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_RRSIG) + launch_chain_link_lookup(chain, + ldns_rdf2str(ldns_rr_rdf(rr, 7))); + } + netreq = netreq->next; + } + callback_on_complete_chain(chain); +} + +/* validation-chain.c */ diff --git a/src/validation-chain.h b/src/validation-chain.h new file mode 100644 index 00000000..eee75fd3 --- /dev/null +++ b/src/validation-chain.h @@ -0,0 +1,48 @@ +/** + * + * /brief priv_getdns_get_validation_chain function + * + * The priv_getdns_get_validation_chain function is called after an answer + * has been fetched when the dnssec_return_validation_chain extension is set. + * It fetches DNSKEYs, DSes and their signatures for all RRSIGs found in the + * answer. + */ + +/* + * Copyright (c) 2013, Versign, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 Verisign, Inc. 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. + */ + +#ifndef VALIDATION_CHAIN_H_ +#define VALIDATION_CHAIN_H_ + +#include "types-internal.h" + +/* Do some additional requests to fetch the complete validation chain */ +void priv_getdns_get_validation_chain(getdns_dns_req *dns_req); + +#endif + +/* validation-chain.h */