From b4083ddb6fb4c82739f704693c755a9ad43cc58e Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 15 Nov 2017 13:01:23 +0800 Subject: [PATCH] Named upstreams --- src/anchor.c | 32 ++------- src/context.c | 84 ++++++++++++++--------- src/context.h | 30 ++++++++- src/general.c | 20 +++--- src/general.h | 4 +- src/request-internal.c | 1 + src/stub.c | 148 ++++++++++++++++++++++++++++++++++++++++- src/sync.c | 2 +- src/types-internal.h | 5 ++ 9 files changed, 252 insertions(+), 74 deletions(-) diff --git a/src/anchor.c b/src/anchor.c index 006e5da4..a374ab78 100644 --- a/src/anchor.c +++ b/src/anchor.c @@ -1517,6 +1517,7 @@ void _getdns_start_fetching_ta(getdns_context *context, getdns_eventloop *loop) char tas_hostname[256]; const char *verify_CA; const char *verify_email; + getdns_context *sys_ctxt; if ((r = _getdns_get_tas_url_hostname(context, tas_hostname, NULL))) { DEBUG_ANCHOR("ERROR %s(): Could not get_tas_url_hostname" @@ -1557,32 +1558,7 @@ void _getdns_start_fetching_ta(getdns_context *context, getdns_eventloop *loop) DEBUG_ANCHOR("%s on the %ssynchronous loop\n", __FUNC__, loop == &context->sync_eventloop.loop ? "" : "a"); - while (!context->sys_ctxt) { /* Used as breakable if. Never repeats. */ - if ((r = getdns_context_create_with_extended_memory_functions( - &context->sys_ctxt, 1, context->mf.mf_arg, - context->mf.mf.ext.malloc, context->mf.mf.ext.realloc, - context->mf.mf.ext.free))) - DEBUG_ANCHOR("Could not create system context: %s\n" - , getdns_get_errorstr_by_id(r)); - - else if ((r = getdns_context_set_eventloop( - context->sys_ctxt, loop))) - DEBUG_ANCHOR("Could not configure %ssynchronous loop " - "with system context: %s\n" - , ( loop == &context->sync_eventloop.loop - ? "" : "a" ) - , getdns_get_errorstr_by_id(r)); - - else if ((r = getdns_context_set_resolution_type( - context->sys_ctxt, GETDNS_RESOLUTION_STUB))) - DEBUG_ANCHOR("Could not configure system context for " - "stub resolver: %s\n" - , getdns_get_errorstr_by_id(r)); - else - break; - - getdns_context_destroy(context->sys_ctxt); - context->sys_ctxt = NULL; + if (!(sys_ctxt = _getdns_context_get_sys_ctxt(context, loop))) { DEBUG_ANCHOR("Fatal error fetching trust anchor: " "missing system context\n"); context->trust_anchors_source = GETDNS_TASRC_FAILED; @@ -1592,7 +1568,7 @@ void _getdns_start_fetching_ta(getdns_context *context, getdns_eventloop *loop) scheduled = 0; #if 1 context->a.state = TAS_LOOKUP_ADDRESSES; - if ((r = _getdns_general_loop(context->sys_ctxt, loop, + if ((r = _getdns_general_loop(sys_ctxt, loop, tas_hostname, GETDNS_RRTYPE_A, NULL, context, &context->a.req, NULL, _tas_hostname_lookup_cb))) { DEBUG_ANCHOR("Error scheduling A lookup for %s: %s\n" @@ -1603,7 +1579,7 @@ void _getdns_start_fetching_ta(getdns_context *context, getdns_eventloop *loop) #if 1 context->aaaa.state = TAS_LOOKUP_ADDRESSES; - if ((r = _getdns_general_loop(context->sys_ctxt, loop, + if ((r = _getdns_general_loop(sys_ctxt, loop, tas_hostname, GETDNS_RRTYPE_AAAA, NULL, context, &context->aaaa.req, NULL, _tas_hostname_lookup_cb))) { DEBUG_ANCHOR("Error scheduling AAAA lookup for %s: %s\n" diff --git a/src/context.c b/src/context.c index 37f7baaf..fff0ea94 100644 --- a/src/context.c +++ b/src/context.c @@ -37,26 +37,6 @@ #include "config.h" #include "anchor.h" -#ifndef USE_WINSOCK -#include -#include -#include -#include -#else -#include -#include -typedef unsigned short in_port_t; - -#include -#include -#include - -#include -#include -#include -#include -#endif - #include #include #include @@ -952,7 +932,7 @@ static getdns_tsig_algo _getdns_get_tsig_algo(getdns_bindata *algo) return GETDNS_NO_TSIG; } -static void +void upstream_init(getdns_upstream *upstream, getdns_upstreams *parent, struct addrinfo *ai) { @@ -964,6 +944,8 @@ upstream_init(getdns_upstream *upstream, } else upstream->addr_len = 0; + upstream->addr_notify = NULL; + /* How is this upstream doing on connections? */ upstream->conn_completed = 0; upstream->conn_shutdowns = 0; @@ -989,7 +971,6 @@ upstream_init(getdns_upstream *upstream, upstream->fd = -1; upstream->tls_obj = NULL; upstream->tls_session = NULL; - upstream->transport = GETDNS_TRANSPORT_TCP; upstream->tls_hs_state = GETDNS_HS_NONE; upstream->tls_auth_name[0] = '\0'; upstream->tls_auth_state = GETDNS_AUTH_NONE; @@ -2763,7 +2744,7 @@ getdns_context_set_upstream_recursive_servers(getdns_context *context, size_t count = 0; size_t i; getdns_upstreams *upstreams; - char addrstr[1024], portstr[1024], *eos; + char addrstr[1024], portstr[11], *eos; struct addrinfo hints; RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); @@ -2827,6 +2808,9 @@ getdns_context_set_upstream_recursive_servers(getdns_context *context, if ((r = getdns_dict_get_bindata( dict, "name", &name))) goto error; + else + assert(name); + } else if (address_data->size == 4) addr.ss_family = AF_INET; else if (address_data->size == 16) @@ -2935,20 +2919,25 @@ getdns_context_set_upstream_recursive_servers(getdns_context *context, /* Loop to create upstreams as needed*/ for (j = 0; j < GETDNS_UPSTREAM_TRANSPORTS; j++) { - uint32_t port; struct addrinfo *ai; - port = getdns_port_array[j]; - if (port == GETDNS_PORT_ZERO) + + upstream = &upstreams->upstreams[upstreams->count]; + upstream->port = getdns_port_array[j]; + if (upstream->port == GETDNS_PORT_ZERO) continue; + upstream->addr.ss_family = addr.ss_family; if (getdns_upstream_transports[j] != GETDNS_TRANSPORT_TLS) { if (dict) - (void) getdns_dict_get_int(dict, "port", &port); + (void) getdns_dict_get_int( + dict, "port", &upstream->port); } else { if (dict) - (void) getdns_dict_get_int(dict, "tls_port", &port); + (void) getdns_dict_get_int( + dict, "tls_port", &upstream->port); } - (void) snprintf(portstr, 1024, "%d", (int)port); + (void) snprintf( + portstr, sizeof(portstr), "%d", (int)upstream->port); if (!name) { if (getaddrinfo(addrstr, portstr, &hints, &ai)) @@ -2962,8 +2951,6 @@ getdns_context_set_upstream_recursive_servers(getdns_context *context, * already exist (in case user has specified TLS port explicitly and * to prevent duplicates) */ - upstream = &upstreams->upstreams[upstreams->count]; - upstream->addr.ss_family = addr.ss_family; (void)strncpy(upstream->addr_str, addrstr , sizeof(upstream->addr_str)); upstream_init(upstream, upstreams, ai); @@ -5024,5 +5011,40 @@ getdns_context_set_appdata_dir( return GETDNS_RETURN_GOOD; } +getdns_context *_getdns_context_get_sys_ctxt( + getdns_context *context, getdns_eventloop *loop) +{ + getdns_return_t r; + + if (context->sys_ctxt) + return context->sys_ctxt; + + if ((r = getdns_context_create_with_extended_memory_functions( + &context->sys_ctxt, 1, context->mf.mf_arg, + context->mf.mf.ext.malloc, context->mf.mf.ext.realloc, + context->mf.mf.ext.free))) + DEBUG_ANCHOR("Could not create system context: %s\n" + , getdns_get_errorstr_by_id(r)); + + else if ((r = getdns_context_set_eventloop( + context->sys_ctxt, loop))) + DEBUG_ANCHOR("Could not configure %ssynchronous loop " + "with system context: %s\n" + , ( loop == &context->sync_eventloop.loop + ? "" : "a" ) + , getdns_get_errorstr_by_id(r)); + + else if ((r = getdns_context_set_resolution_type( + context->sys_ctxt, GETDNS_RESOLUTION_STUB))) + DEBUG_ANCHOR("Could not configure system context for " + "stub resolver: %s\n" + , getdns_get_errorstr_by_id(r)); + else + return context->sys_ctxt; + + getdns_context_destroy(context->sys_ctxt); + context->sys_ctxt = NULL; + return NULL; +} /* context.c */ diff --git a/src/context.h b/src/context.h index b8b67a44..a5045590 100644 --- a/src/context.h +++ b/src/context.h @@ -37,9 +37,29 @@ #ifndef _GETDNS_CONTEXT_H_ #define _GETDNS_CONTEXT_H_ +#include "config.h" +#ifndef USE_WINSOCK +#include +#include +#include +#include +#else +#include +#include +typedef unsigned short in_port_t; + +#include +#include +#include + +#include +#include +#include +#include +#endif + #include "getdns/getdns.h" #include "getdns/getdns_extra.h" -#include "config.h" #include "types-internal.h" #include "extension/default_eventloop.h" #include "util/rbtree.h" @@ -141,6 +161,8 @@ typedef struct getdns_upstream { socklen_t addr_len; struct sockaddr_storage addr; char addr_str[1024]; + getdns_network_req *addr_notify; + uint32_t port; /** * How is this upstream doing over UDP? @@ -557,4 +579,10 @@ int _getdns_context_write_priv_file(getdns_context *context, int _getdns_context_can_write_appdata(getdns_context *context); +getdns_context *_getdns_context_get_sys_ctxt( + getdns_context *context, getdns_eventloop *loop); + +void upstream_init(getdns_upstream *upstream, + getdns_upstreams *parent, struct addrinfo *ai); + #endif /* _GETDNS_CONTEXT_H_ */ diff --git a/src/general.c b/src/general.c index b263c0a9..350188c3 100644 --- a/src/general.c +++ b/src/general.c @@ -717,12 +717,12 @@ _getdns_general_loop(getdns_context *context, getdns_eventloop *loop, getdns_return_t _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_network_req **netreq_p, getdns_callback_t callback, + internal_cb_t internal_cb) { getdns_dict *my_extensions = extensions; getdns_return_t r; uint32_t value; - getdns_network_req *netreq = NULL; if (!my_extensions) { if (!(my_extensions=getdns_dict_create_with_context(context))) @@ -738,9 +738,7 @@ _getdns_address_loop(getdns_context *context, getdns_eventloop *loop, r = getdns_general_ns(context, loop, name, GETDNS_RRTYPE_AAAA, my_extensions, - userarg, &netreq, callback, NULL, 1); - if (netreq && transaction_id) - *transaction_id = netreq->owner->trans_id; + userarg, netreq_p, callback, internal_cb, 1); if (my_extensions != extensions) getdns_dict_destroy(my_extensions); @@ -882,10 +880,16 @@ getdns_address(getdns_context *context, const char *name, getdns_dict *extensions, void *userarg, getdns_transaction_t *transaction_id, getdns_callback_t callbackfn) { + getdns_return_t r; + getdns_network_req *netreq = NULL; + if (!context) return GETDNS_RETURN_INVALID_PARAMETER; - return _getdns_address_loop(context, context->extension, - name, extensions, userarg, - transaction_id, callbackfn); + r = _getdns_address_loop(context, context->extension, + name, extensions, userarg, &netreq, callbackfn, NULL); + if (netreq && transaction_id) + *transaction_id = netreq->owner->trans_id; + return r; + } /* getdns_address */ /* diff --git a/src/general.h b/src/general.h index e0860c78..c4a236b3 100644 --- a/src/general.h +++ b/src/general.h @@ -70,8 +70,8 @@ _getdns_general_loop(getdns_context *context, getdns_eventloop *loop, getdns_return_t _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 callbackfn); + void *userarg, getdns_network_req **netreq_p, + getdns_callback_t callbackfn, internal_cb_t internal_cb); getdns_return_t _getdns_hostname_loop(getdns_context *context, getdns_eventloop *loop, diff --git a/src/request-internal.c b/src/request-internal.c index c9f27db0..0958239f 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -135,6 +135,7 @@ netreq_reset(getdns_network_req *net_req) net_req->query_id_registered = NULL; net_req->node.key = NULL; } + net_req->addr_notify = NULL; net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE; net_req->tsig_status = GETDNS_DNSSEC_INDETERMINATE; net_req->response_len = 0; diff --git a/src/stub.c b/src/stub.c index 10745928..b07e34e4 100644 --- a/src/stub.c +++ b/src/stub.c @@ -61,6 +61,7 @@ * level triggered). See also lines containing WSA TODO below... */ #define STUB_TRY_AGAIN_LATER -24 /* EMFILE, i.e. Out of OS resources */ +#define STUB_RESOLVE_UPSTREAM_NAME -89 /* EDESTADDRREQ, i.e. lookup name */ #define STUB_NO_AUTH -8 /* Existing TLS connection is not authenticated */ #define STUB_CONN_GONE -7 /* Connection has failed, clear queue*/ #define STUB_TCP_RETRY -6 @@ -1750,7 +1751,7 @@ upstream_usable(getdns_upstream *upstream, int backoff_ok) upstream->keepalive_shutdown == 0) return 1; /* Otherwise, allow upstreams that are backed off to be used because that - is better that having no upstream at all. */ + is better than having no upstream at all. */ if (backoff_ok == 1 && upstream->conn_state == GETDNS_CONN_BACKOFF) return 1; @@ -1929,7 +1930,7 @@ upstream_select(getdns_network_req *netreq) if (!upstreams->count) return NULL; - /* First UPD/TCP upstream is always at i=0 and then start of each upstream block*/ + /* First UDP/TCP upstream is always at i=0 and then start of each upstream block*/ /* TODO: Have direct access to sets of upstreams for different transports*/ for (i = 0; i < upstreams->count; i+=GETDNS_UPSTREAM_TRANSPORTS) if (upstreams->upstreams[i].to_retry <= 0) @@ -2020,7 +2021,8 @@ upstream_find_for_transport(getdns_network_req *netreq, no socket is available, in which case that is an error.*/ if (transport == GETDNS_TRANSPORT_UDP) { upstream = upstream_select(netreq); - *fd = upstream_connect(upstream, transport, netreq->owner); + if (upstream->addr_len > 0) /* not an upstream by name */ + *fd = upstream_connect(upstream, transport, netreq->owner); return upstream; } else { @@ -2032,6 +2034,8 @@ upstream_find_for_transport(getdns_network_req *netreq, upstream = upstream_select_stateful(netreq, transport); if (!upstream) return NULL; + if (upstream->addr_len == 0) /* an upstream by name */ + return upstream; *fd = upstream_connect(upstream, transport, netreq->owner); if (i >= upstream->upstreams->count) return NULL; @@ -2059,6 +2063,12 @@ upstream_find_for_netreq(getdns_network_req *netreq) if (!upstream) continue; + if (upstream->addr_len == 0) { /* upstream by name */ + netreq->transport_current = i; + netreq->upstream = upstream; + netreq->keepalive_sent = 0; + return STUB_RESOLVE_UPSTREAM_NAME; + } if (fd == -1) { if (_getdns_resource_depletion()) return STUB_TRY_AGAIN_LATER; @@ -2191,12 +2201,120 @@ upstream_schedule_netreq(getdns_upstream *upstream, getdns_network_req *netreq) } } +static int upstream_address_found( + getdns_upstream *upstream, getdns_network_req *netreq) +{ + _getdns_rrset_spc rrset_spc; + _getdns_rrset *rrset; + _getdns_rrtype_iter rr_spc, *rr; + struct addrinfo hints; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_flags = AI_NUMERICHOST; /* No reverse name lookups */ + + if (!(rrset = _getdns_rrset_answer( + &rrset_spc, netreq->response, netreq->response_len))) + ; /* FORMERR */ + + else if (rrset->rr_type != netreq->request_type) + ; /* Can this happen? */ + + else if (!(rr = _getdns_rrtype_iter_init(&rr_spc, rrset))) + ; /* NOERROR */ + + else for(; rr; rr = _getdns_rrtype_iter_next(rr)) { + struct sockaddr_storage addr; + char addrstr[1024], portstr[11]; + struct addrinfo *ai; + + if (rr->rr_i.nxt - (rr->rr_i.rr_type + 10) != + ( netreq->request_type == GETDNS_RRTYPE_A ? 4 + : netreq->request_type == GETDNS_RRTYPE_AAAA ? 16 : -1)) + continue; + + addr.ss_family = netreq->request_type == GETDNS_RRTYPE_A + ? AF_INET : AF_INET6; + if (inet_ntop(addr.ss_family, rr->rr_i.rr_type + 10, + addrstr, sizeof(addrstr)) == NULL) + continue; + + (void) snprintf( portstr, sizeof(portstr) + , "%d", (int)upstream->port); + + if (getaddrinfo(addrstr, portstr, &hints, &ai)) + continue; + + (void)strncpy( upstream->addr_str, addrstr + , sizeof(upstream->addr_str)); + upstream->addr_len = ai->ai_addrlen; + (void) memcpy(&upstream->addr, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo(ai); + return 1; + } + DEBUG_STUB("Some error!\n"); + return 0; /* Try the other netreq */ +} + +static void upstream_name_cb(getdns_dns_req *dnsreq) +{ + getdns_upstream *upstream = (getdns_upstream *)dnsreq->user_pointer; + uint64_t now_ms = 0; + getdns_network_req *addr_notify; + + DEBUG_STUB("YAY: %d, %d\n", (int)dnsreq->netreqs[0]->response_len + , (int)dnsreq->netreqs[1]->response_len); + DEBUG_STUB("upstream: %p, upstream->addr_notify: %p\n", + (void *)upstream, (void *)upstream->addr_notify); + + if (dnsreq->netreqs[0]->response_len && + upstream_address_found(upstream, dnsreq->netreqs[0])) + ; /* pass */ + + else if (dnsreq->netreqs[1]->response_len && + upstream_address_found(upstream, dnsreq->netreqs[1])) + ; /* pass */ + + else { + /* Fail this upstream */ + if (upstream->transport == GETDNS_TRANSPORT_UDP) { + if (upstream->addr_notify) + stub_next_upstream(upstream->addr_notify); + } else + upstream->conn_setup_failed++; + + addr_notify = upstream->addr_notify; + upstream->addr_notify = NULL; + while (addr_notify) { + DEBUG_STUB("Error netreq: %p", (void *)addr_notify); + _getdns_netreq_change_state( + addr_notify, NET_REQ_ERRORED); + _getdns_check_dns_req_complete( + addr_notify->owner); + addr_notify = addr_notify->addr_notify; + } + return; + } + /* Upstream has now changed to an address upstream, + * so reschedule queries. + */ + addr_notify = upstream->addr_notify; + upstream->addr_notify = NULL; + while (addr_notify) { + DEBUG_STUB("Resubmit netreq: %p", (void *)addr_notify); + _getdns_submit_stub_request(addr_notify, &now_ms); + addr_notify = addr_notify->addr_notify; + } +} + getdns_return_t _getdns_submit_stub_request(getdns_network_req *netreq, uint64_t *now_ms) { int fd = -1; getdns_dns_req *dnsreq; getdns_context *context; + getdns_return_t r; + getdns_context *sys_ctxt; DEBUG_STUB("%s %-35s: MSG: %p TYPE: %d\n", STUB_DEBUG_ENTRY, __FUNC__, (void*)netreq, netreq->request_type); @@ -2217,6 +2335,30 @@ _getdns_submit_stub_request(getdns_network_req *netreq, uint64_t *now_ms) &context->pending_netreqs, &netreq->node)) return GETDNS_RETURN_GOOD; return GETDNS_RETURN_NO_UPSTREAM_AVAILABLE; + + } else if (fd == STUB_RESOLVE_UPSTREAM_NAME) { + assert( netreq->upstream->addr_len == 0); + assert(*netreq->upstream->addr_str != 0); + + _getdns_netreq_change_state(netreq, NET_REQ_NOT_SENT); + if (netreq->addr_notify) { + netreq->addr_notify = netreq->upstream->addr_notify; + netreq->upstream->addr_notify = netreq; + return GETDNS_RETURN_GOOD; + } else + netreq->upstream->addr_notify = netreq; + + if (!(sys_ctxt = _getdns_context_get_sys_ctxt(context, dnsreq->loop))) + /* TODO: retry something else? */ + return GETDNS_RETURN_MEMORY_ERROR; + + DEBUG_STUB("Scheduling address lookup for: %s\n", netreq->upstream->addr_str); + if ((r =_getdns_address_loop(sys_ctxt, dnsreq->loop, + netreq->upstream->addr_str, NULL, netreq->upstream, + NULL, NULL, upstream_name_cb))) + return r; + else + return GETDNS_RETURN_GOOD; } switch(netreq->transports[netreq->transport_current]) { case GETDNS_TRANSPORT_UDP: diff --git a/src/sync.c b/src/sync.c index debf904b..6a92d54b 100644 --- a/src/sync.c +++ b/src/sync.c @@ -199,7 +199,7 @@ getdns_address_sync(getdns_context *context, const char *name, return r; if ((r = _getdns_address_loop(context, &context->sync_eventloop.loop, - name, extensions, &data, NULL, getdns_sync_cb))) { + name, extensions, &data, NULL, getdns_sync_cb, NULL))) { getdns_sync_data_cleanup(&data); return r; diff --git a/src/types-internal.h b/src/types-internal.h index 05589f4a..9cbdf711 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -237,6 +237,11 @@ typedef struct getdns_network_req /* Network requests scheduled to write after me */ struct getdns_network_req *write_queue_tail; + /* Network requests subscribed to be notified after me, + * when an address lookup for a "name" upstream completed. + */ + struct getdns_network_req *addr_notify; + /* Some fields to record info for return_call_reporting */ uint64_t debug_start_time; uint64_t debug_end_time;