/** * * \file general.c * @brief getdns_general and related support functions * * The getdns_general function is called by most of the other public entry * points to the library. Private support functions are also included in this * file where they are directly logically related to the getdns_general implementation. */ /* * Copyright (c) 2013, NLnet Labs, Verisign, 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 names of the copyright holders 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 "config.h" #include "gldns/wire2str.h" #include "context.h" #include "types-internal.h" #include "util-internal.h" #include "dnssec.h" #include "stub.h" /* cancel, cleanup and send timeout to callback */ static void ub_resolve_timeout(void *arg) { getdns_dns_req *dns_req = (getdns_dns_req *) arg; (void) getdns_context_request_timed_out(dns_req); } void priv_getdns_call_user_callback(getdns_dns_req *dns_req, struct getdns_dict *response) { struct getdns_context *context = dns_req->context; getdns_transaction_t trans_id = dns_req->trans_id; getdns_callback_t cb = dns_req->user_callback; void *user_arg = dns_req->user_pointer; /* clean up */ getdns_context_clear_outbound_request(dns_req); dns_req_free(dns_req); cb(context, (response ? GETDNS_CALLBACK_COMPLETE : GETDNS_CALLBACK_ERROR), response, user_arg, trans_id); } /* cleanup and send an error to the user callback */ static void handle_network_request_error(getdns_network_req * netreq, int err) { priv_getdns_call_user_callback(netreq->owner, NULL); } void priv_getdns_check_dns_req_complete(getdns_dns_req *dns_req) { getdns_network_req **netreq_p, *netreq; int results_found = 0; for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++) if (netreq->state != NET_REQ_FINISHED && netreq->state != NET_REQ_CANCELED) return; else if (netreq->response_len > 0) results_found = 1; if (dns_req->internal_cb) dns_req->internal_cb(dns_req); else if (! results_found) priv_getdns_call_user_callback(dns_req, NULL); else if (dns_req->dnssec_return_validation_chain #ifdef STUB_NATIVE_DNSSEC || (dns_req->context->resolution_type == GETDNS_RESOLUTION_STUB && (dns_req->dnssec_return_status || dns_req->dnssec_return_only_secure)) #endif ) priv_getdns_get_validation_chain(dns_req); else priv_getdns_call_user_callback( dns_req, create_getdns_response(dns_req)); } static void ub_resolve_callback(void* arg, int err, struct ub_result* ub_res) { getdns_network_req *netreq = (getdns_network_req *) arg; getdns_dns_req *dns_req = netreq->owner; netreq->state = NET_REQ_FINISHED; if (err != 0) { handle_network_request_error(netreq, err); return; } /* parse */ if (getdns_apply_network_result(netreq, ub_res)) { ub_resolve_free(ub_res); handle_network_request_error(netreq, err); return; } ub_resolve_free(ub_res); priv_getdns_check_dns_req_complete(dns_req); } /* ub_resolve_callback */ static getdns_return_t submit_network_request(getdns_network_req *netreq) { getdns_return_t r; getdns_dns_req *dns_req = netreq->owner; char name[1024]; if (dns_req->context->resolution_type == GETDNS_RESOLUTION_RECURSING /* TODO: Until DNSSEC with the new async stub resolver is finished, * use unbound when we need DNSSEC. */ #ifndef STUB_NATIVE_DNSSEC || dns_req->dnssec_return_status || dns_req->dnssec_return_only_secure || dns_req->dnssec_return_validation_chain #endif ) { /* schedule the timeout */ if (! dns_req->timeout.timeout_cb) { dns_req->timeout.userarg = dns_req; dns_req->timeout.read_cb = NULL; dns_req->timeout.write_cb = NULL; dns_req->timeout.timeout_cb = ub_resolve_timeout; dns_req->timeout.ev = NULL; if ((r = dns_req->loop->vmt->schedule(dns_req->loop, -1, dns_req->context->timeout, &dns_req->timeout))) return r; } (void) gldns_wire2str_dname_buf(dns_req->name, dns_req->name_len, name, sizeof(name)); return ub_resolve_async(dns_req->context->unbound_ctx, name, netreq->request_type, netreq->request_class, netreq, ub_resolve_callback, &(netreq->unbound_id)) ? GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD; } /* Submit with stub resolver */ return priv_getdns_submit_stub_request(netreq); } static getdns_return_t getdns_general_ns(getdns_context *context, getdns_eventloop *loop, const char *name, uint16_t request_type, getdns_dict *extensions, void *userarg, getdns_dns_req **dnsreq_p, getdns_callback_t callbackfn, internal_cb_t internal_cb, int usenamespaces) { getdns_return_t r = GETDNS_RETURN_GOOD; getdns_network_req *netreq, **netreq_p; getdns_dns_req *req; getdns_dict *localnames_response; size_t i; if (!context || !name || (!callbackfn && !internal_cb)) return GETDNS_RETURN_INVALID_PARAMETER; if ((r = priv_getdns_validate_dname(name))) return r; if (extensions && (r = priv_getdns_validate_extensions(extensions))) return r; /* Set up the context assuming we won't use the specified namespaces. This is (currently) identical to setting up a pure DNS namespace */ if ((r = getdns_context_prepare_for_resolution(context, 0))) return r; /* create the request */ if (!(req = dns_req_new(context, loop, name, request_type, extensions))) return GETDNS_RETURN_MEMORY_ERROR; req->user_pointer = userarg; req->user_callback = callbackfn; req->internal_cb = internal_cb; if (dnsreq_p) *dnsreq_p = req; getdns_context_track_outbound_request(req); if (!usenamespaces) /* issue all network requests */ for ( netreq_p = req->netreqs ; !r && (netreq = *netreq_p) ; netreq_p++) r = submit_network_request(netreq); else for (i = 0; i < context->namespace_count; i++) { if (context->namespaces[i] == GETDNS_NAMESPACE_LOCALNAMES) { if (!(r = getdns_context_local_namespace_resolve( req, &localnames_response))) { priv_getdns_call_user_callback ( req, localnames_response); break; } } else if (context->namespaces[i] == GETDNS_NAMESPACE_DNS) { /* TODO: We will get a good return code here even if the name is not found (NXDOMAIN). We should consider if this means we go onto the next namespace instead of returning */ r = GETDNS_RETURN_GOOD; for ( netreq_p = req->netreqs ; !r && (netreq = *netreq_p) ; netreq_p++) r = submit_network_request(netreq); break; } else r = GETDNS_RETURN_BAD_CONTEXT; } if (r != 0) { /* clean up the request */ getdns_context_clear_outbound_request(req); dns_req_free(req); return r; } return GETDNS_RETURN_GOOD; } /* getdns_general_ns */ getdns_return_t priv_getdns_general_loop(getdns_context *context, getdns_eventloop *loop, const char *name, uint16_t request_type, getdns_dict *extensions, void *userarg, getdns_dns_req **dnsreq_p, getdns_callback_t callback, internal_cb_t internal_cb) { return getdns_general_ns(context, loop, name, request_type, extensions, userarg, dnsreq_p, callback, internal_cb, 0); } /* getdns_general_loop */ getdns_return_t priv_getdns_address_loop(getdns_context *context, getdns_eventloop *loop, const char *name, getdns_dict *extensions, void *userarg, getdns_transaction_t *transaction_id, getdns_callback_t callback) { getdns_dict *my_extensions = extensions; getdns_return_t r; uint32_t value; getdns_dns_req *dnsreq = NULL; if (!my_extensions) { if (!(my_extensions=getdns_dict_create_with_context(context))) return GETDNS_RETURN_MEMORY_ERROR; } else if ( getdns_dict_get_int(my_extensions, "return_both_v4_and_v6", &value) && (r = getdns_dict_copy(extensions, &my_extensions))) return r; if (my_extensions != extensions && (r = getdns_dict_set_int( my_extensions, "return_both_v4_and_v6", GETDNS_EXTENSION_TRUE))) return r; r = getdns_general_ns(context, loop, name, GETDNS_RRTYPE_AAAA, my_extensions, userarg, &dnsreq, callback, NULL, 1); if (dnsreq && transaction_id) *transaction_id = dnsreq->trans_id; if (my_extensions != extensions) getdns_dict_destroy(my_extensions); return r; } /* getdns_address_loop */ getdns_return_t priv_getdns_hostname_loop(getdns_context *context, getdns_eventloop *loop, getdns_dict *address, getdns_dict *extensions, void *userarg, getdns_transaction_t *transaction_id, getdns_callback_t callback) { struct getdns_bindata *address_data; struct getdns_bindata *address_type; uint16_t req_type; char name[1024]; getdns_return_t retval; getdns_dns_req *dnsreq = NULL; if ((retval = getdns_dict_get_bindata(address, "address_data", &address_data)) != GETDNS_RETURN_GOOD) return retval; if ((retval = getdns_dict_get_bindata(address, "address_type", &address_type)) != GETDNS_RETURN_GOOD) return retval; if ((strncmp(GETDNS_STR_IPV4, (char *) address_type->data, ( strlen(GETDNS_STR_IPV4) < address_type->size ? strlen(GETDNS_STR_IPV4) : address_type->size )) == 0 && address_data->size == 4) || (strncmp(GETDNS_STR_IPV6, (char *) address_type->data, ( strlen(GETDNS_STR_IPV6) < address_type->size ? strlen(GETDNS_STR_IPV6) : address_type->size )) == 0 && address_data->size == 16)) req_type = GETDNS_RRTYPE_PTR; else return GETDNS_RETURN_INVALID_PARAMETER; switch (address_data->size) { case 4: (void)snprintf(name, sizeof(name), "%hhu.%hhu.%hhu.%hhu.in-addr.arpa.", ((uint8_t *)address_data->data)[3], ((uint8_t *)address_data->data)[2], ((uint8_t *)address_data->data)[1], ((uint8_t *)address_data->data)[0]); break; case 16: (void)snprintf(name, sizeof(name), "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx." "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx." "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx." "%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.%hhx.ip6.arpa.", (uint8_t)(((uint8_t *)address_data->data)[15] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[15] >> 4), (uint8_t)(((uint8_t *)address_data->data)[14] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[14] >> 4), (uint8_t)(((uint8_t *)address_data->data)[13] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[13] >> 4), (uint8_t)(((uint8_t *)address_data->data)[12] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[12] >> 4), (uint8_t)(((uint8_t *)address_data->data)[11] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[11] >> 4), (uint8_t)(((uint8_t *)address_data->data)[10] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[10] >> 4), (uint8_t)(((uint8_t *)address_data->data)[9] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[9] >> 4), (uint8_t)(((uint8_t *)address_data->data)[8] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[8] >> 4), (uint8_t)(((uint8_t *)address_data->data)[7] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[7] >> 4), (uint8_t)(((uint8_t *)address_data->data)[6] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[6] >> 4), (uint8_t)(((uint8_t *)address_data->data)[5] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[5] >> 4), (uint8_t)(((uint8_t *)address_data->data)[4] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[4] >> 4), (uint8_t)(((uint8_t *)address_data->data)[3] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[3] >> 4), (uint8_t)(((uint8_t *)address_data->data)[2] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[2] >> 4), (uint8_t)(((uint8_t *)address_data->data)[1] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[1] >> 4), (uint8_t)(((uint8_t *)address_data->data)[0] & 0x0F), (uint8_t)(((uint8_t *)address_data->data)[0] >> 4)); break; default: return GETDNS_RETURN_INVALID_PARAMETER; } retval = priv_getdns_general_loop(context, loop, name, req_type, extensions, userarg, &dnsreq, callback, NULL); if (dnsreq && transaction_id) *transaction_id = dnsreq->trans_id; return retval; } /* getdns_hostname_loop */ getdns_return_t priv_getdns_service_loop(getdns_context *context, getdns_eventloop *loop, const char *name, getdns_dict *extensions, void *userarg, getdns_transaction_t * transaction_id, getdns_callback_t callback) { getdns_return_t r; getdns_dns_req *dnsreq = NULL; r = getdns_general_ns(context, loop, name, GETDNS_RRTYPE_SRV, extensions, userarg, &dnsreq, callback, NULL, 1); if (dnsreq && transaction_id) *transaction_id = dnsreq->trans_id; return r; } /* getdns_service_loop */ /** * getdns_general */ getdns_return_t getdns_general(getdns_context *context, const char *name, uint16_t request_type, getdns_dict *extensions, void *userarg, getdns_transaction_t * transaction_id, getdns_callback_t callback) { getdns_return_t r; getdns_dns_req *dnsreq = NULL; if (!context) return GETDNS_RETURN_INVALID_PARAMETER; r = priv_getdns_general_loop(context, context->extension, name, request_type, extensions, userarg, &dnsreq, callback, NULL); if (dnsreq && transaction_id) *transaction_id = dnsreq->trans_id; return r; } /* getdns_general */ /* * getdns_address * */ getdns_return_t getdns_address(getdns_context *context, const char *name, getdns_dict *extensions, void *userarg, getdns_transaction_t *transaction_id, getdns_callback_t callback) { if (!context) return GETDNS_RETURN_INVALID_PARAMETER; return priv_getdns_address_loop(context, context->extension, name, extensions, userarg, transaction_id, callback); } /* getdns_address */ /* * getdns_hostname * */ getdns_return_t getdns_hostname(getdns_context *context, getdns_dict *address, getdns_dict *extensions, void *userarg, getdns_transaction_t *transaction_id, getdns_callback_t callback) { if (!context) return GETDNS_RETURN_INVALID_PARAMETER; return priv_getdns_hostname_loop(context, context->extension, address, extensions, userarg, transaction_id, callback); } /* getdns_hostname */ /* * getdns_service * */ getdns_return_t getdns_service(getdns_context *context, const char *name, getdns_dict *extensions, void *userarg, getdns_transaction_t *transaction_id, getdns_callback_t callback) { if (!context) return GETDNS_RETURN_INVALID_PARAMETER; return priv_getdns_service_loop(context, context->extension, name, extensions, userarg, transaction_id, callback); } /* getdns_service */ /* getdns_general.c */