Named upstreams

This commit is contained in:
Willem Toorop 2017-11-15 13:01:23 +08:00
parent b86f149523
commit b4083ddb6f
9 changed files with 252 additions and 74 deletions

View File

@ -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"

View File

@ -37,26 +37,6 @@
#include "config.h"
#include "anchor.h"
#ifndef USE_WINSOCK
#include <arpa/inet.h>
#include <sys/time.h>
#include <netdb.h>
#include <pwd.h>
#else
#include <winsock2.h>
#include <iphlpapi.h>
typedef unsigned short in_port_t;
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include <shlobj.h>
#endif
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
@ -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 */

View File

@ -37,9 +37,29 @@
#ifndef _GETDNS_CONTEXT_H_
#define _GETDNS_CONTEXT_H_
#include "config.h"
#ifndef USE_WINSOCK
#include <arpa/inet.h>
#include <sys/time.h>
#include <netdb.h>
#include <pwd.h>
#else
#include <winsock2.h>
#include <iphlpapi.h>
typedef unsigned short in_port_t;
#include <openssl/x509.h>
#include <openssl/pem.h>
#include <openssl/bio.h>
#include <stdio.h>
#include <windows.h>
#include <wincrypt.h>
#include <shlobj.h>
#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_ */

View File

@ -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 */
/*

View File

@ -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,

View File

@ -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;

View File

@ -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:

View File

@ -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;

View File

@ -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;