mirror of https://github.com/getdnsapi/getdns.git
5310 lines
159 KiB
C
5310 lines
159 KiB
C
/**
|
|
*
|
|
* \file context.c
|
|
* @brief getdns context management functions
|
|
*
|
|
* Declarations taken from the getdns API description pseudo 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 "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 <stdio.h>
|
|
#include <windows.h>
|
|
#include <wincrypt.h>
|
|
#include <shlobj.h>
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <stdarg.h>
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
#include <pthread.h>
|
|
#endif
|
|
#include <stdbool.h>
|
|
|
|
#ifdef HAVE_LIBUNBOUND
|
|
#include <unbound.h>
|
|
#endif
|
|
#include "debug.h"
|
|
#include "gldns/str2wire.h"
|
|
#include "gldns/wire2str.h"
|
|
#include "context.h"
|
|
#include "types-internal.h"
|
|
#include "util-internal.h"
|
|
#include "platform.h"
|
|
#include "dnssec.h"
|
|
#include "stub.h"
|
|
#include "list.h"
|
|
#include "dict.h"
|
|
#include "pubkey-pinning.h"
|
|
#include "const-info.h"
|
|
#include "tls.h"
|
|
|
|
#define GETDNS_PORT_ZERO 0
|
|
#define GETDNS_PORT_DNS 53
|
|
#define GETDNS_PORT_DNS_OVER_TLS 853
|
|
#define GETDNS_STR_PORT_ZERO "0"
|
|
#define GETDNS_STR_PORT_DNS "53"
|
|
#define GETDNS_STR_PORT_DNS_OVER_TLS "853"
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
static pthread_mutex_t ssl_init_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
#endif
|
|
static bool ssl_init=false;
|
|
|
|
#ifdef HAVE_MDNS_SUPPORT
|
|
/*
|
|
* Forward declaration of MDNS context init and destroy function.
|
|
* We do this here instead of including mdns.h, in order to
|
|
* minimize dependencies.
|
|
*/
|
|
void _getdns_mdns_context_init(struct getdns_context *context);
|
|
void _getdns_mdns_context_destroy(struct getdns_context *context);
|
|
#endif
|
|
|
|
void *plain_mem_funcs_user_arg = MF_PLAIN;
|
|
|
|
typedef struct host_name_addrs {
|
|
_getdns_rbnode_t node;
|
|
getdns_list *ipv4addrs;
|
|
getdns_list *ipv6addrs;
|
|
uint8_t host_name[];
|
|
} host_name_addrs;
|
|
|
|
|
|
/* If changing these lists also remember to
|
|
change the value of GETDNS_UPSTREAM_TRANSPORTS */
|
|
static getdns_transport_list_t
|
|
getdns_upstream_transports[GETDNS_UPSTREAM_TRANSPORTS] = {
|
|
GETDNS_TRANSPORT_TCP,
|
|
GETDNS_TRANSPORT_TLS,
|
|
};
|
|
|
|
static in_port_t
|
|
getdns_port_array[GETDNS_UPSTREAM_TRANSPORTS] = {
|
|
GETDNS_PORT_DNS,
|
|
GETDNS_PORT_DNS_OVER_TLS
|
|
};
|
|
|
|
static char*
|
|
getdns_port_str_array[] = {
|
|
GETDNS_STR_PORT_DNS,
|
|
GETDNS_STR_PORT_DNS_OVER_TLS
|
|
};
|
|
|
|
static const uint8_t no_suffixes[] = { 1, 0 };
|
|
|
|
/* Private functions */
|
|
static getdns_return_t create_default_namespaces(struct getdns_context *context);
|
|
static getdns_return_t create_default_dns_transports(struct getdns_context *context);
|
|
static int transaction_id_cmp(const void *, const void *);
|
|
static void dispatch_updated(struct getdns_context *, uint16_t);
|
|
static void cancel_outstanding_requests(getdns_context*);
|
|
|
|
/* unbound helpers */
|
|
#ifdef HAVE_LIBUNBOUND
|
|
static getdns_return_t rebuild_ub_ctx(struct getdns_context* context);
|
|
static void set_ub_string_opt(struct getdns_context *, char *, char *);
|
|
static void set_ub_number_opt(struct getdns_context *, char *, uint16_t);
|
|
static getdns_return_t set_ub_dns_transport(struct getdns_context*);
|
|
static void set_ub_limit_outstanding_queries(struct getdns_context*,
|
|
uint16_t);
|
|
static void set_ub_dnssec_allowed_skew(struct getdns_context*, uint32_t);
|
|
#endif
|
|
|
|
/* Stuff to make it compile pedantically */
|
|
#define RETURN_IF_NULL(ptr, code) if(ptr == NULL) return code;
|
|
|
|
static char *
|
|
_getdns_strdup2(const struct mem_funcs *mfs, const getdns_bindata *s)
|
|
{
|
|
char *r;
|
|
if (!s || !(r = GETDNS_XMALLOC(*mfs, char, s->size + 1)))
|
|
return NULL;
|
|
else {
|
|
r[s->size] = '\0';
|
|
return memcpy(r, s, s->size);
|
|
}
|
|
}
|
|
|
|
static uint8_t*
|
|
upstream_addr(getdns_upstream *upstream)
|
|
{
|
|
return upstream->addr.ss_family == AF_INET
|
|
? (void *)&((struct sockaddr_in*)&upstream->addr)->sin_addr
|
|
: (void *)&((struct sockaddr_in6*)&upstream->addr)->sin6_addr;
|
|
}
|
|
|
|
static in_port_t
|
|
upstream_port(getdns_upstream *upstream)
|
|
{
|
|
return ntohs(upstream->addr.ss_family == AF_INET
|
|
? ((struct sockaddr_in *)&upstream->addr)->sin_port
|
|
: ((struct sockaddr_in6*)&upstream->addr)->sin6_port);
|
|
}
|
|
|
|
static void destroy_local_host(_getdns_rbnode_t * node, void *arg)
|
|
{
|
|
getdns_context *context = (getdns_context *)arg;
|
|
host_name_addrs *hnas = (host_name_addrs *)node;
|
|
getdns_list_destroy(hnas->ipv4addrs);
|
|
getdns_list_destroy(hnas->ipv6addrs);
|
|
GETDNS_FREE(context->my_mf, hnas);
|
|
}
|
|
|
|
/**
|
|
* Helper to get default lookup namespaces.
|
|
* TODO: Determine from OS
|
|
*/
|
|
static getdns_return_t
|
|
create_default_namespaces(struct getdns_context *context)
|
|
{
|
|
context->namespaces = GETDNS_XMALLOC(context->my_mf, getdns_namespace_t, 2);
|
|
if(context->namespaces == NULL)
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
context->namespaces[0] = GETDNS_NAMESPACE_LOCALNAMES;
|
|
context->namespaces[1] = GETDNS_NAMESPACE_DNS;
|
|
context->namespace_count = 2;
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
/**
|
|
* Helper to get default transports.
|
|
*/
|
|
static getdns_return_t
|
|
create_default_dns_transports(struct getdns_context *context)
|
|
{
|
|
context->dns_transports = GETDNS_XMALLOC(context->my_mf, getdns_transport_list_t, 2);
|
|
if(context->dns_transports == NULL)
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
context->dns_transports[0] = GETDNS_TRANSPORT_UDP;
|
|
context->dns_transports[1] = GETDNS_TRANSPORT_TCP;
|
|
context->dns_transport_count = 2;
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
static inline void canonicalize_dname(uint8_t *dname)
|
|
{
|
|
uint8_t *next_label;
|
|
|
|
while (*dname && !(*dname & 0xC0)) {
|
|
next_label = dname + *dname + 1;
|
|
dname += 1;
|
|
while (dname < next_label) {
|
|
*dname = (uint8_t)tolower((unsigned char)*dname);
|
|
dname++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
canonical_dname_compare(register const uint8_t *d1, register const uint8_t *d2)
|
|
{
|
|
register uint8_t lab1, lab2;
|
|
|
|
assert(d1 && d2);
|
|
|
|
lab1 = *d1++;
|
|
lab2 = *d2++;
|
|
while (lab1 != 0 || lab2 != 0) {
|
|
/* compare label length */
|
|
/* if one dname ends, it has labellength 0 */
|
|
if (lab1 != lab2) {
|
|
if (lab1 < lab2)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
while (lab1--) {
|
|
/* compare bytes first for speed */
|
|
if (*d1 != *d2) {
|
|
if (*d1 < *d2)
|
|
return -1;
|
|
return 1;
|
|
}
|
|
d1++;
|
|
d2++;
|
|
}
|
|
/* next pair of labels. */
|
|
lab1 = *d1++;
|
|
lab2 = *d2++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
local_host_cmp(const void *id1, const void *id2)
|
|
{
|
|
return canonical_dname_compare(id1, id2);
|
|
}
|
|
|
|
/** return 0 on success */
|
|
static int
|
|
add_local_host(getdns_context *context, getdns_dict *address, const char *str)
|
|
{
|
|
uint8_t host_name[256];
|
|
size_t host_name_len = sizeof(host_name);
|
|
host_name_addrs *hnas;
|
|
getdns_bindata *address_type;
|
|
int hnas_found = 0;
|
|
getdns_list **addrs;
|
|
|
|
if (gldns_str2wire_dname_buf(str, host_name, &host_name_len))
|
|
return -1;
|
|
|
|
canonicalize_dname(host_name);
|
|
|
|
if (!(hnas = (host_name_addrs *)_getdns_rbtree_search(
|
|
&context->local_hosts, host_name))) {
|
|
|
|
if (!(hnas = (host_name_addrs *)GETDNS_XMALLOC(context->mf,
|
|
uint8_t, sizeof(host_name_addrs) + host_name_len)))
|
|
return -1;
|
|
|
|
hnas->ipv4addrs = NULL;
|
|
hnas->ipv6addrs = NULL;
|
|
(void)memcpy(hnas->host_name, host_name, host_name_len);
|
|
hnas->node.key = &hnas->host_name;
|
|
|
|
} else
|
|
hnas_found = 1;
|
|
|
|
if (getdns_dict_get_bindata(address, "address_type", &address_type) ||
|
|
|
|
address_type->size < 4 ||
|
|
|
|
!(addrs = address_type->data[3] == '4'? &hnas->ipv4addrs
|
|
: address_type->data[3] == '6'? &hnas->ipv4addrs : NULL)) {
|
|
|
|
if (!hnas_found) GETDNS_FREE(context->mf, hnas);
|
|
return -1;
|
|
}
|
|
if (!*addrs && !(*addrs = getdns_list_create_with_context(context))) {
|
|
if (!hnas_found) GETDNS_FREE(context->mf, hnas);
|
|
return -1;
|
|
}
|
|
if (_getdns_list_append_this_dict(*addrs, address)) {
|
|
if (!hnas_found) {
|
|
getdns_list_destroy(*addrs);
|
|
GETDNS_FREE(context->mf, hnas);
|
|
}
|
|
return -1;
|
|
|
|
} else if (!hnas_found)
|
|
(void)_getdns_rbtree_insert(&context->local_hosts, &hnas->node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static getdns_dict *
|
|
sockaddr_dict(getdns_context *context, struct sockaddr *sa)
|
|
{
|
|
getdns_dict *address = getdns_dict_create_with_context(context);
|
|
char addrstr[1024], *b;
|
|
getdns_bindata bindata;
|
|
uint16_t port;
|
|
|
|
if (!address)
|
|
return NULL;
|
|
|
|
switch (sa->sa_family) {
|
|
case AF_INET:
|
|
if (getdns_dict_util_set_string(address,"address_type","IPv4"))
|
|
break;
|
|
|
|
bindata.size = 4;
|
|
bindata.data = (void *)&((struct sockaddr_in*)sa)->sin_addr;
|
|
if ((getdns_dict_set_bindata(address,"address_data",&bindata)))
|
|
break;
|
|
|
|
port = ntohs(((struct sockaddr_in *)sa)->sin_port);
|
|
if (port != GETDNS_PORT_ZERO && port != GETDNS_PORT_DNS &&
|
|
getdns_dict_set_int(address, "port", (uint32_t)port))
|
|
break;
|
|
|
|
return address;
|
|
|
|
case AF_INET6:
|
|
if (getdns_dict_util_set_string(address,"address_type","IPv6"))
|
|
break;
|
|
|
|
bindata.size = 16;
|
|
bindata.data = (void *)&((struct sockaddr_in6*)sa)->sin6_addr;
|
|
if ((getdns_dict_set_bindata(address,"address_data",&bindata)))
|
|
break;
|
|
|
|
port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
|
|
if (port != GETDNS_PORT_ZERO && port != GETDNS_PORT_DNS &&
|
|
getdns_dict_set_int(address, "port", (uint32_t)port))
|
|
break;
|
|
|
|
/* Try to get scope_id too */
|
|
if (getnameinfo(sa, sizeof(struct sockaddr_in6),
|
|
addrstr, sizeof(addrstr), NULL, 0, NI_NUMERICHOST))
|
|
break;
|
|
if ((b = strchr(addrstr, '%')) &&
|
|
getdns_dict_util_set_string(address, "scope_id", b+1))
|
|
break;
|
|
|
|
return address;
|
|
default:
|
|
/* Unknown protocol */
|
|
break;
|
|
}
|
|
getdns_dict_destroy(address);
|
|
return NULL;
|
|
}
|
|
|
|
static getdns_dict *
|
|
str_addr_dict(getdns_context *context, const char *str)
|
|
{
|
|
static struct addrinfo hints = { .ai_family = AF_UNSPEC
|
|
, .ai_flags = AI_NUMERICHOST };
|
|
struct addrinfo *ai;
|
|
getdns_dict *address;
|
|
|
|
if (getaddrinfo(str, NULL, &hints, &ai))
|
|
return NULL;
|
|
if (!ai)
|
|
return NULL;
|
|
|
|
address = sockaddr_dict(context, ai->ai_addr);
|
|
freeaddrinfo(ai);
|
|
|
|
return address;
|
|
}
|
|
|
|
/**
|
|
* check a file for changes since the last check
|
|
* and refresh the current data if changes are detected
|
|
* @param context pointer to a previously created context to be used for this call
|
|
* @param fchg file to check
|
|
* @returns changes as OR'd list of GETDNS_FCHG_* values
|
|
* @returns GETDNS_FCHG_NONE if no changes
|
|
* @returns GETDNS_FCHG_ERRORS if problems (see fchg->errors for details)
|
|
*/
|
|
static int
|
|
_getdns_filechg_check(struct filechg *fchg)
|
|
{
|
|
struct stat finfo;
|
|
|
|
if(fchg == NULL)
|
|
return 0;
|
|
|
|
fchg->errors = GETDNS_FCHG_NOERROR;
|
|
fchg->changes = GETDNS_FCHG_NOCHANGES;
|
|
|
|
if(stat(fchg->fn, &finfo) != 0) {
|
|
fchg->errors = errno;
|
|
return GETDNS_FCHG_ERRORS;
|
|
}
|
|
|
|
/* we want to consider a file that previously returned error for stat() as a
|
|
change */
|
|
|
|
if(fchg->prevstat.st_mtime != finfo.st_mtime)
|
|
fchg->changes |= GETDNS_FCHG_MTIME;
|
|
if(fchg->prevstat.st_ctime != finfo.st_ctime)
|
|
fchg->changes |= GETDNS_FCHG_CTIME;
|
|
|
|
fchg->prevstat = finfo;
|
|
return fchg->changes;
|
|
} /* filechg */
|
|
|
|
getdns_return_t
|
|
getdns_context_set_hosts(getdns_context *context, const char *hosts)
|
|
{
|
|
/* enough space in buf for longest allowed domain name */
|
|
char buf[1024];
|
|
char *pos = buf, prev_c, *start_of_word = NULL;
|
|
FILE *in;
|
|
int start_of_line = 1;
|
|
getdns_dict *address = NULL;
|
|
|
|
if (!context || !hosts)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (!(in = fopen(hosts, "r")))
|
|
return GETDNS_RETURN_IO_ERROR;
|
|
|
|
(void)strlcpy(context->fchg_hosts.fn, hosts, _GETDNS_PATH_MAX);
|
|
(void) memset(&context->fchg_hosts.prevstat, 0, sizeof(struct stat));
|
|
context->fchg_hosts.changes = GETDNS_FCHG_NOCHANGES;
|
|
context->fchg_hosts.errors = GETDNS_FCHG_NOERROR;
|
|
(void) _getdns_filechg_check(&context->fchg_hosts);
|
|
_getdns_traverse_postorder(&context->local_hosts,
|
|
destroy_local_host, context);
|
|
_getdns_rbtree_init(&context->local_hosts, local_host_cmp);
|
|
|
|
while (fgets(pos, (int)(sizeof(buf) - (pos - buf)), in)) {
|
|
pos = buf;
|
|
/* Break out of for to read more */
|
|
for (;;) {
|
|
/* Skip whitespace */
|
|
while (*pos == ' ' || *pos == '\f'
|
|
|| *pos == '\t' || *pos == '\v')
|
|
pos++;
|
|
|
|
if (*pos == '\0') { /* End of read data */
|
|
pos = buf;
|
|
goto read_more;
|
|
|
|
} else if (*pos == '#' || *pos == '\r' || *pos == '\n')
|
|
/* Comments or end of line */
|
|
break; /* skip to next line */
|
|
|
|
assert(*pos && !isspace(*pos));
|
|
|
|
start_of_word = pos;
|
|
|
|
/* Search for end of word */
|
|
while (*pos && !isspace(*pos))
|
|
pos++;
|
|
|
|
/* '\0' before whitespace, so either the word did not
|
|
* fit, or we are at the end of the file.
|
|
*/
|
|
if (*pos == '\0') {
|
|
if (start_of_word == buf) /* word too big */
|
|
break; /* skip to next line */
|
|
|
|
/* Move word to fit in buffer */
|
|
memmove(buf,start_of_word,pos - start_of_word);
|
|
pos = buf + (pos - start_of_word);
|
|
start_of_word = buf;
|
|
*pos = '\0';
|
|
goto read_more;
|
|
}
|
|
assert(isspace(*pos));
|
|
prev_c = *pos;
|
|
*pos = '\0';
|
|
if (start_of_line) {
|
|
start_of_line = 0;
|
|
if (address)
|
|
getdns_dict_destroy(address);
|
|
if (!(address =
|
|
str_addr_dict(context, start_of_word)))
|
|
/* Unparseable address */
|
|
break; /* skip to next line */
|
|
} else if (!add_local_host(context, address, start_of_word))
|
|
address = NULL;
|
|
|
|
start_of_word = NULL;
|
|
*pos = prev_c;
|
|
/* process next word in buf */
|
|
}
|
|
/* skip to next line */
|
|
while (*pos != '\r' && *pos != '\n')
|
|
if (*pos)
|
|
pos++;
|
|
else if (!fgets((pos = buf), sizeof(buf), in))
|
|
break; /* We're done */
|
|
start_of_line = 1;
|
|
if (address) {
|
|
getdns_dict_destroy(address);
|
|
address = NULL;
|
|
}
|
|
pos = buf;
|
|
read_more: ;
|
|
}
|
|
fclose(in);
|
|
if (address) {
|
|
/* One last name for this address? */
|
|
if (start_of_word && !start_of_line)
|
|
if (!add_local_host(context, address, start_of_word))
|
|
address = NULL;
|
|
getdns_dict_destroy(address);
|
|
}
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_hosts(getdns_context *context, const char **hosts)
|
|
{
|
|
if (!context || !hosts)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
*hosts = *context->fchg_hosts.fn ? context->fchg_hosts.fn : NULL;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
|
|
static getdns_upstreams *
|
|
upstreams_create(getdns_context *context, size_t size)
|
|
{
|
|
getdns_upstreams *r = (void *) GETDNS_XMALLOC(context->mf, char,
|
|
sizeof(getdns_upstreams) +
|
|
sizeof(getdns_upstream) * size);
|
|
r->mf = context->mf;
|
|
r->referenced = 1;
|
|
r->count = 0;
|
|
r->current_udp = 0;
|
|
r->current_stateful = 0;
|
|
r->max_backoff_value = context->max_backoff_value;
|
|
r->tls_backoff_time = context->tls_backoff_time;
|
|
r->tls_connection_retries = context->tls_connection_retries;
|
|
r->log = context->log;
|
|
return r;
|
|
}
|
|
|
|
|
|
void
|
|
_getdns_upstreams_dereference(getdns_upstreams *upstreams)
|
|
{
|
|
getdns_upstream *upstream;
|
|
getdns_dns_req *dnsreq;
|
|
|
|
if (!upstreams || --upstreams->referenced > 0)
|
|
return;
|
|
|
|
for ( upstream = upstreams->upstreams
|
|
; upstreams->count
|
|
; upstreams->count--, upstream++ ) {
|
|
|
|
sha256_pin_t *pin = upstream->tls_pubkey_pinset;
|
|
if (upstream->loop && ( upstream->event.read_cb
|
|
|| upstream->event.write_cb
|
|
|| upstream->event.timeout_cb) ) {
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
|
upstream->event.read_cb = NULL;
|
|
upstream->event.write_cb = NULL;
|
|
upstream->event.timeout_cb = NULL;
|
|
}
|
|
if (upstream->loop && upstream->finished_event.timeout_cb) {
|
|
GETDNS_CLEAR_EVENT(upstream->loop,
|
|
&upstream->finished_event);
|
|
upstream->finished_event.timeout_cb = NULL;
|
|
}
|
|
while (upstream->finished_dnsreqs) {
|
|
dnsreq = upstream->finished_dnsreqs;
|
|
upstream->finished_dnsreqs = dnsreq->finished_next;
|
|
if (!dnsreq->internal_cb) { /* Not part of chain */
|
|
debug_req("Destroy ", *dnsreq->netreqs);
|
|
_getdns_context_cancel_request(dnsreq);
|
|
}
|
|
}
|
|
if (upstream->tls_session != NULL)
|
|
_getdns_tls_session_free(&upstreams->mf, upstream->tls_session);
|
|
|
|
if (upstream->tls_obj != NULL) {
|
|
_getdns_tls_connection_shutdown(upstream->tls_obj);
|
|
_getdns_tls_connection_free(&upstreams->mf, upstream->tls_obj);
|
|
}
|
|
if (upstream->fd != -1)
|
|
{
|
|
_getdns_closesocket(upstream->fd);
|
|
}
|
|
if (upstream->tcp.read_buf)
|
|
GETDNS_FREE(upstreams->mf, upstream->tcp.read_buf);
|
|
while (pin) {
|
|
sha256_pin_t *nextpin = pin->next;
|
|
GETDNS_FREE(upstreams->mf, pin);
|
|
pin = nextpin;
|
|
}
|
|
upstream->tls_pubkey_pinset = NULL;
|
|
if (upstream->tls_cipher_list)
|
|
GETDNS_FREE(upstreams->mf, upstream->tls_cipher_list);
|
|
if (upstream->tls_curves_list)
|
|
GETDNS_FREE(upstreams->mf, upstream->tls_curves_list);
|
|
}
|
|
GETDNS_FREE(upstreams->mf, upstreams);
|
|
}
|
|
|
|
void _getdns_upstream_log(getdns_upstream *upstream, uint64_t system,
|
|
getdns_loglevel_type level, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (!upstream || !upstream->upstreams || !upstream->upstreams->log.func
|
|
|| !(upstream->upstreams->log.system & system)
|
|
|| level > upstream->upstreams->log.level)
|
|
return;
|
|
|
|
va_start(args, fmt);
|
|
upstream->upstreams->log.func(
|
|
upstream->upstreams->log.userarg, system, level, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
static void
|
|
upstream_backoff(getdns_upstream *upstream) {
|
|
upstream->conn_state = GETDNS_CONN_BACKOFF;
|
|
/* Increase the backoff interval incrementally up to the tls_backoff_time*/
|
|
if (upstream->conn_backoff_interval < upstream->upstreams->tls_backoff_time) {
|
|
if (upstream->conn_backoff_interval < (UINT16_MAX-1)/2)
|
|
upstream->conn_backoff_interval *= 2;
|
|
else
|
|
upstream->conn_backoff_interval = upstream->upstreams->tls_backoff_time;
|
|
}
|
|
if (upstream->conn_backoff_interval < upstream->upstreams->tls_backoff_time)
|
|
upstream->conn_retry_time = time(NULL) + upstream->conn_backoff_interval;
|
|
else
|
|
upstream->conn_retry_time = time(NULL) + upstream->upstreams->tls_backoff_time;
|
|
upstream->total_responses = 0;
|
|
upstream->total_timeouts = 0;
|
|
upstream->conn_completed = 0;
|
|
upstream->conn_setup_failed = 0;
|
|
upstream->conn_shutdowns = 0;
|
|
upstream->conn_backoffs++;
|
|
_getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_NOTICE,
|
|
"%-40s : Upstream : !Backing off %s on this upstream - Will retry again in %ds at %s",
|
|
upstream->addr_str,
|
|
upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP",
|
|
upstream->conn_backoff_interval,
|
|
asctime(gmtime(&upstream->conn_retry_time)));
|
|
}
|
|
|
|
static void
|
|
_getdns_upstream_reset(getdns_upstream *upstream)
|
|
{
|
|
/* Back off connections that never got up service at all (probably no
|
|
TCP service or incompatible TLS version/cipher).
|
|
Leave choice between working upstreams to the stub.
|
|
This back-off should be time based for TLS according to RFC7858. For now,
|
|
use the same basis if we simply can't get TCP service either.*/
|
|
/* [TLS1]TODO: This arbitrary logic at the moment - review and improve!*/
|
|
|
|
/*This is the configured number of retries to attempt*/
|
|
uint16_t conn_retries = upstream->upstreams->tls_connection_retries;
|
|
|
|
if (upstream->conn_setup_failed >= conn_retries
|
|
|
|
|| ((int)upstream->conn_shutdowns >= conn_retries*GETDNS_TRANSPORT_FAIL_MULT
|
|
&& upstream->total_responses == 0)
|
|
|
|
|| (upstream->conn_completed >= conn_retries &&
|
|
upstream->total_responses == 0 &&
|
|
upstream->total_timeouts > GETDNS_TRANSPORT_FAIL_MULT)) {
|
|
|
|
upstream_backoff(upstream);
|
|
}
|
|
|
|
/* If we didn't backoff it would be nice to reset the conn_backoff_interval
|
|
if the upstream is working well again otherwise it would get stuck at the
|
|
tls_backoff_time forever... How about */
|
|
if (upstream->conn_state != GETDNS_CONN_BACKOFF &&
|
|
upstream->responses_received > 1)
|
|
upstream->conn_backoff_interval = 1;
|
|
|
|
// Reset per connection counters
|
|
upstream->queries_sent = 0;
|
|
upstream->responses_received = 0;
|
|
upstream->responses_timeouts = 0;
|
|
upstream->keepalive_timeout = 0;
|
|
upstream->keepalive_shutdown = 0;
|
|
|
|
/* Now TLS stuff*/
|
|
upstream->tls_auth_state = GETDNS_AUTH_NONE;
|
|
if (upstream->event.ev && upstream->loop) {
|
|
upstream->loop->vmt->clear(
|
|
upstream->loop, &upstream->event);
|
|
}
|
|
if (upstream->tls_obj != NULL) {
|
|
_getdns_tls_connection_shutdown(upstream->tls_obj);
|
|
_getdns_tls_connection_free(&upstream->upstreams->mf, upstream->tls_obj);
|
|
upstream->tls_obj = NULL;
|
|
}
|
|
if (upstream->fd != -1) {
|
|
_getdns_closesocket(upstream->fd);
|
|
upstream->fd = -1;
|
|
}
|
|
/* Set connection ready for use again*/
|
|
if (upstream->conn_state != GETDNS_CONN_BACKOFF)
|
|
upstream->conn_state = GETDNS_CONN_CLOSED;
|
|
}
|
|
|
|
void
|
|
_getdns_upstream_shutdown(getdns_upstream *upstream)
|
|
{
|
|
/* Update total stats for the upstream.*/
|
|
upstream->total_responses+=upstream->responses_received;
|
|
upstream->total_timeouts+=upstream->responses_timeouts;
|
|
/* Need the last auth state when using session resumption*/
|
|
upstream->last_tls_auth_state = upstream->tls_auth_state;
|
|
/* Keep track of the best auth state this upstream has had*/
|
|
if (upstream->tls_auth_state > upstream->best_tls_auth_state)
|
|
upstream->best_tls_auth_state = upstream->tls_auth_state;
|
|
_getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG,
|
|
"%-40s : Conn closed: %s - Resps=%6d, Timeouts =%6d, Curr_auth =%7s, Keepalive(ms)=%6d\n",
|
|
upstream->addr_str,
|
|
(upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"),
|
|
(int)upstream->responses_received, (int)upstream->responses_timeouts,
|
|
_getdns_auth_str(upstream->tls_auth_state), (int)upstream->keepalive_timeout);
|
|
_getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO,
|
|
"%-40s : Upstream : %s - Resps=%6d, Timeouts =%6d, Best_auth =%7s\n",
|
|
upstream->addr_str,
|
|
(upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"),
|
|
(int)upstream->total_responses, (int)upstream->total_timeouts,
|
|
_getdns_auth_str(upstream->best_tls_auth_state));
|
|
_getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_INFO,
|
|
"%-40s : Upstream : %s - Conns=%6d, Conn_fails=%6d, Conn_shuts=%7d, Backoffs =%6d\n",
|
|
upstream->addr_str,
|
|
(upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"),
|
|
(int)upstream->conn_completed, (int)upstream->conn_setup_failed,
|
|
(int)upstream->conn_shutdowns, (int)upstream->conn_backoffs);
|
|
|
|
_getdns_upstream_reset(upstream);
|
|
}
|
|
|
|
static int
|
|
tls_is_in_transports_list(getdns_context *context)
|
|
{
|
|
size_t i;
|
|
|
|
for (i = 0; i< context->dns_transport_count;i++) {
|
|
if (context->dns_transports[i] == GETDNS_TRANSPORT_TLS)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
tls_only_is_in_transports_list(getdns_context *context) {
|
|
if (context->dns_transport_count != 1)
|
|
return 0;
|
|
if (context->dns_transports[0] == GETDNS_TRANSPORT_TLS)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int
|
|
net_req_query_id_cmp(const void *id1, const void *id2)
|
|
{
|
|
/*
|
|
* old code was:
|
|
* return (intptr_t)id1 - (intptr_t)id2;
|
|
*but this is incorrect on 64 bit architectures.
|
|
*/
|
|
int ret = 0;
|
|
|
|
if (id1 != id2)
|
|
{
|
|
ret = ((intptr_t)id1 < (intptr_t)id2) ? -1 : 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static getdns_tsig_info const tsig_info[] = {
|
|
{ GETDNS_NO_TSIG, NULL, 0, NULL, 0, 0, 0 }
|
|
, { GETDNS_HMAC_MD5 , "hmac-md5.sig-alg.reg.int", 24
|
|
, (uint8_t *)"\x08hmac-md5\x07sig-alg\x03reg\x03int", 26, 10, 16 }
|
|
, { GETDNS_NO_TSIG, NULL, 0, NULL, 0, 0, 0 }
|
|
, { GETDNS_HMAC_SHA1 , "hmac-sha1" , 9
|
|
, (uint8_t *)"\x09hmac-sha1" , 11, 10, 20 }
|
|
, { GETDNS_HMAC_SHA224, "hmac-sha224", 11
|
|
, (uint8_t *)"\x0bhmac-sha224", 13, 14, 28 }
|
|
, { GETDNS_HMAC_SHA256, "hmac-sha256", 11
|
|
, (uint8_t *)"\x0bhmac-sha256", 13, 16, 32 }
|
|
, { GETDNS_HMAC_SHA384, "hmac-sha384", 11
|
|
, (uint8_t *)"\x0bhmac-sha384", 13, 24, 48 }
|
|
, { GETDNS_HMAC_SHA512, "hmac-sha512", 11
|
|
, (uint8_t *)"\x0bhmac-sha512", 13, 32, 64 }
|
|
, { GETDNS_HMAC_MD5 , "hmac-md5" , 8
|
|
, (uint8_t *)"\x08hmac-md5" , 10, 10, 16 }
|
|
};
|
|
static size_t const n_tsig_infos =
|
|
sizeof(tsig_info) / sizeof(getdns_tsig_info);
|
|
|
|
static getdns_tsig_info const * const last_tsig_info =
|
|
tsig_info + (sizeof(tsig_info) / sizeof(getdns_tsig_info));
|
|
|
|
const getdns_tsig_info *_getdns_get_tsig_info(getdns_tsig_algo tsig_alg)
|
|
{
|
|
return ((unsigned) tsig_alg > n_tsig_infos - 1)
|
|
|| tsig_info[tsig_alg].alg == GETDNS_NO_TSIG ? NULL
|
|
: &tsig_info[tsig_alg];
|
|
}
|
|
|
|
static getdns_tsig_algo _getdns_get_tsig_algo(getdns_bindata *algo)
|
|
{
|
|
const getdns_tsig_info *i;
|
|
|
|
if (!algo || algo->size == 0)
|
|
return GETDNS_NO_TSIG;
|
|
|
|
if (algo->data[algo->size-1] != 0) {
|
|
/* Unterminated string */
|
|
for (i = tsig_info; i < last_tsig_info; i++)
|
|
if ((algo->size == i->strlen_name ||
|
|
(algo->size - 1 == i->strlen_name &&
|
|
algo->data[algo->size - 1] == '.'
|
|
)
|
|
)&&
|
|
strncasecmp((const char *)algo->data, i->name,
|
|
i->strlen_name) == 0)
|
|
return i->alg;
|
|
|
|
} else if (!_getdns_bindata_is_dname(algo)) {
|
|
/* Terminated string */
|
|
for (i = tsig_info; i < last_tsig_info; i++)
|
|
if (algo->size - 1 == i->strlen_name &&
|
|
strncasecmp((const char *)algo->data, i->name,
|
|
i->strlen_name) == 0)
|
|
return i->alg;
|
|
|
|
} else {
|
|
/* fqdn, canonical_dname_compare is now safe to use! */
|
|
for (i = tsig_info; i < last_tsig_info; i++)
|
|
if (canonical_dname_compare(algo->data, i->dname) == 0)
|
|
return i->alg;
|
|
}
|
|
return GETDNS_NO_TSIG;
|
|
}
|
|
|
|
static void
|
|
upstream_init(getdns_upstream *upstream,
|
|
getdns_upstreams *parent, struct addrinfo *ai)
|
|
{
|
|
upstream->upstreams = parent;
|
|
|
|
upstream->addr_len = ai->ai_addrlen;
|
|
(void) memcpy(&upstream->addr, ai->ai_addr, ai->ai_addrlen);
|
|
inet_ntop(upstream->addr.ss_family, upstream_addr(upstream),
|
|
upstream->addr_str, INET6_ADDRSTRLEN);
|
|
|
|
/* How is this upstream doing on connections? */
|
|
upstream->conn_completed = 0;
|
|
upstream->conn_shutdowns = 0;
|
|
upstream->conn_setup_failed = 0;
|
|
upstream->conn_retry_time = 0;
|
|
upstream->conn_backoff_interval = 1;
|
|
upstream->conn_backoffs = 0;
|
|
upstream->total_responses = 0;
|
|
upstream->total_timeouts = 0;
|
|
upstream->conn_state = GETDNS_CONN_CLOSED;
|
|
upstream->queries_sent = 0;
|
|
upstream->responses_received = 0;
|
|
upstream->responses_timeouts = 0;
|
|
upstream->keepalive_shutdown = 0;
|
|
upstream->keepalive_timeout = 0;
|
|
upstream->server_keepalive_received = 0;
|
|
/* How is this upstream doing on UDP? */
|
|
upstream->to_retry = 1;
|
|
upstream->back_off = 1;
|
|
upstream->udp_responses = 0;
|
|
upstream->udp_timeouts = 0;
|
|
|
|
/* For sharing a socket to this upstream with TCP */
|
|
upstream->fd = -1;
|
|
upstream->tls_obj = NULL;
|
|
upstream->tls_session = NULL;
|
|
upstream->tls_cipher_list = NULL;
|
|
upstream->tls_curves_list = 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;
|
|
upstream->last_tls_auth_state = GETDNS_AUTH_NONE;
|
|
upstream->best_tls_auth_state = GETDNS_AUTH_NONE;
|
|
upstream->tls_pubkey_pinset = NULL;
|
|
upstream->loop = NULL;
|
|
(void) getdns_eventloop_event_init(
|
|
&upstream->event, upstream, NULL, NULL, NULL);
|
|
(void) memset(&upstream->tcp, 0, sizeof(upstream->tcp));
|
|
|
|
upstream->write_queue = NULL;
|
|
upstream->write_queue_last = NULL;
|
|
|
|
upstream->finished_dnsreqs = NULL;
|
|
(void) getdns_eventloop_event_init(
|
|
&upstream->finished_event, upstream, NULL, NULL, NULL);
|
|
|
|
upstream->has_client_cookie = 0;
|
|
upstream->has_prev_client_cookie = 0;
|
|
upstream->has_server_cookie = 0;
|
|
|
|
upstream->tsig_alg = GETDNS_NO_TSIG;
|
|
upstream->tsig_dname_len = 0;
|
|
upstream->tsig_size = 0;
|
|
|
|
/* Tracking of network requests on this socket */
|
|
_getdns_rbtree_init(&upstream->netreq_by_query_id,
|
|
net_req_query_id_cmp);
|
|
}
|
|
|
|
#ifdef USE_WINSOCK
|
|
|
|
/*
|
|
Read the Windows search suffix and add to context
|
|
There may or may not be domains and suffixes so do not error if missing
|
|
*/
|
|
static int get_dns_suffix_windows(getdns_list *suffix, char* domain)
|
|
{
|
|
char *parse, *token, prev_ch;
|
|
char lszValue[255] = "";
|
|
char lszDomain[255] = "";
|
|
HKEY hKey;
|
|
LONG returnStatus;
|
|
DWORD dwType=REG_SZ;
|
|
DWORD dwSize=255;
|
|
returnStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
"SYSTEM\\CurrentControlSet\\Services\\TcpIp\\Parameters",
|
|
0, KEY_READ, &hKey);
|
|
if (returnStatus != ERROR_SUCCESS)
|
|
{
|
|
/* try windows 9x/me */
|
|
returnStatus = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
"SYSTEM\\CurrentControlSet\\Services\\VxD\\MSTCP",
|
|
0, KEY_READ, &hKey);
|
|
|
|
}
|
|
if (returnStatus == ERROR_SUCCESS)
|
|
{
|
|
returnStatus = RegQueryValueEx(hKey,
|
|
TEXT("SearchList"), 0, &dwType,(LPBYTE)&lszValue, &dwSize);
|
|
if (returnStatus == ERROR_SUCCESS)
|
|
{
|
|
if ((strlen(lszValue)) > 0) {
|
|
parse = lszValue;
|
|
do {
|
|
parse += strspn(parse, ",");
|
|
token = parse + strcspn(parse, ",");
|
|
prev_ch = *token;
|
|
*token = 0;
|
|
_getdns_list_append_string(suffix, parse);
|
|
|
|
*token = prev_ch;
|
|
parse = token;
|
|
} while (*parse);
|
|
}
|
|
|
|
}
|
|
|
|
dwSize = 255;
|
|
returnStatus = RegQueryValueEx(hKey,
|
|
TEXT("Domain"), 0, &dwType,(LPBYTE)&lszDomain, &dwSize);
|
|
if (returnStatus == ERROR_SUCCESS)
|
|
{
|
|
strcpy_s(domain, dwSize, lszDomain);
|
|
}
|
|
|
|
RegCloseKey(hKey);
|
|
} else {
|
|
return 0; /* no DNS keys or suffixes */
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
static getdns_return_t
|
|
set_os_defaults_windows(getdns_context *context)
|
|
{
|
|
char domain[1024] = "";
|
|
size_t upstreams_limit = 10;
|
|
struct addrinfo hints;
|
|
struct addrinfo *result;
|
|
getdns_list *suffix;
|
|
getdns_upstream *upstream;
|
|
size_t length;
|
|
int s;
|
|
uint32_t info_err = 0;
|
|
|
|
if (!(context->upstreams = upstreams_create(context, upstreams_limit)))
|
|
return GETDNS_RETURN_MEMORY_ERROR;
|
|
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
|
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
|
hints.ai_socktype = 0; /* Datagram socket */
|
|
hints.ai_flags = AI_NUMERICHOST; /* No reverse name lookups */
|
|
hints.ai_protocol = 0; /* Any protocol */
|
|
hints.ai_canonname = NULL;
|
|
hints.ai_addr = NULL;
|
|
hints.ai_next = NULL;
|
|
|
|
FIXED_INFO *info;
|
|
ULONG buflen = sizeof(*info);
|
|
IP_ADDR_STRING *ptr = 0;
|
|
|
|
info = (FIXED_INFO *)malloc(sizeof(FIXED_INFO));
|
|
if (info == NULL)
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
if ((info_err = GetNetworkParams(info, &buflen)) == ERROR_BUFFER_OVERFLOW) {
|
|
free(info);
|
|
info = (FIXED_INFO *)malloc(buflen);
|
|
if (info == NULL)
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
info_err = GetNetworkParams(info, &buflen);
|
|
}
|
|
|
|
if (info_err == NO_ERROR) {
|
|
ptr = &info->DnsServerList;
|
|
*domain = 0;
|
|
while (ptr) {
|
|
size_t i;
|
|
for (i = 0; i < GETDNS_UPSTREAM_TRANSPORTS; i++) {
|
|
char *port_str = getdns_port_str_array[i];
|
|
if ((s = getaddrinfo(ptr->IpAddress.String, port_str, &hints, &result)))
|
|
continue;
|
|
if (!result)
|
|
continue;
|
|
|
|
upstream = &context->upstreams->
|
|
upstreams[context->upstreams->count++];
|
|
upstream_init(upstream, context->upstreams, result);
|
|
upstream->transport = getdns_upstream_transports[i];
|
|
freeaddrinfo(result);
|
|
}
|
|
ptr = ptr->Next;
|
|
}
|
|
}
|
|
|
|
if (info != NULL)
|
|
free(info);
|
|
|
|
suffix = getdns_list_create_with_context(context);
|
|
|
|
if (get_dns_suffix_windows(suffix, domain)) {
|
|
(void) getdns_list_get_length(suffix, &length);
|
|
if (*domain != 0) {
|
|
_getdns_list_append_string(suffix, domain);
|
|
}
|
|
(void) getdns_list_get_length(suffix, &length);
|
|
if (length > 0) {
|
|
(void )getdns_context_set_suffix(context, suffix);
|
|
}
|
|
}
|
|
getdns_list_destroy(suffix);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* set_os_defaults_windows */
|
|
|
|
#else
|
|
|
|
getdns_return_t
|
|
getdns_context_set_resolvconf(getdns_context *context, const char *resolvconf)
|
|
{
|
|
FILE *in;
|
|
char line[1024], domain[1024];
|
|
char *parse, *token, prev_ch;
|
|
size_t upstream_count, length;
|
|
struct addrinfo hints;
|
|
struct addrinfo *result;
|
|
getdns_upstream *upstream;
|
|
getdns_list *suffix;
|
|
int s;
|
|
|
|
if (!context || !resolvconf)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
|
|
(void) strlcpy( context->fchg_resolvconf.fn, resolvconf, _GETDNS_PATH_MAX);
|
|
(void) memset(&context->fchg_resolvconf.prevstat, 0, sizeof(struct stat));
|
|
context->fchg_resolvconf.changes = GETDNS_FCHG_NOCHANGES;
|
|
context->fchg_resolvconf.errors = GETDNS_FCHG_NOERROR;
|
|
(void) _getdns_filechg_check(&context->fchg_resolvconf);
|
|
|
|
if (!(in = fopen(context->fchg_resolvconf.fn, "r")))
|
|
return GETDNS_RETURN_IO_ERROR;
|
|
|
|
upstream_count = 0;
|
|
while (fgets(line, (int)sizeof(line), in))
|
|
if (strncmp(line, "nameserver", 10) == 0)
|
|
upstream_count++;
|
|
fclose(in);
|
|
|
|
suffix = getdns_list_create_with_context(context);
|
|
if (context->upstreams) {
|
|
_getdns_upstreams_dereference(context->upstreams);
|
|
context->upstreams = NULL;
|
|
}
|
|
if (!(context->upstreams = upstreams_create(
|
|
context, upstream_count * GETDNS_UPSTREAM_TRANSPORTS))) {
|
|
return GETDNS_RETURN_MEMORY_ERROR;
|
|
}
|
|
if (!(in = fopen(context->fchg_resolvconf.fn, "r")))
|
|
return GETDNS_RETURN_IO_ERROR;
|
|
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
|
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
|
hints.ai_socktype = 0; /* Datagram socket */
|
|
hints.ai_flags = AI_NUMERICHOST; /* No reverse name lookups */
|
|
hints.ai_protocol = 0; /* Any protocol */
|
|
hints.ai_canonname = NULL;
|
|
hints.ai_addr = NULL;
|
|
hints.ai_next = NULL;
|
|
|
|
*domain = 0;
|
|
while (fgets(line, (int)sizeof(line), in)) {
|
|
size_t i;
|
|
|
|
line[sizeof(line)-1] = 0;
|
|
/* parse = line + strspn(line, " \t"); */ /* No leading whitespace */
|
|
parse = line;
|
|
|
|
if (strncmp(parse, "domain", 6) == 0) {
|
|
parse += 6;
|
|
parse += strspn(parse, " \t");
|
|
if (*parse == 0 || *parse == '#') continue;
|
|
token = parse + strcspn(parse, " \t\r\n");
|
|
*token = 0;
|
|
|
|
(void) strlcpy(domain, parse, sizeof(domain));
|
|
continue;
|
|
}
|
|
if (strncmp(parse, "search", 6) == 0) {
|
|
parse += 6;
|
|
do {
|
|
parse += strspn(parse, " \t");
|
|
if (*parse == '#' || *parse == '\n') break;
|
|
token = parse + strcspn(parse, " \t\r\n");
|
|
prev_ch = *token;
|
|
*token = 0;
|
|
|
|
_getdns_list_append_string(suffix, parse);
|
|
|
|
*token = prev_ch;
|
|
parse = token;
|
|
} while (*parse);
|
|
continue;
|
|
}
|
|
if (strncmp(parse, "nameserver", 10) != 0)
|
|
continue;
|
|
|
|
parse += 10;
|
|
parse += strspn(parse, " \t");
|
|
if (*parse == 0 || *parse == '#') continue;
|
|
token = parse + strcspn(parse, " \t\r\n");
|
|
*token = 0;
|
|
|
|
for (i = 0; i < GETDNS_UPSTREAM_TRANSPORTS; i++) {
|
|
char *port_str = getdns_port_str_array[i];
|
|
if ((s = getaddrinfo(parse, port_str, &hints, &result)))
|
|
continue;
|
|
if (!result)
|
|
continue;
|
|
|
|
upstream = &context->upstreams->
|
|
upstreams[context->upstreams->count++];
|
|
upstream_init(upstream, context->upstreams, result);
|
|
upstream->transport = getdns_upstream_transports[i];
|
|
freeaddrinfo(result);
|
|
}
|
|
}
|
|
fclose(in);
|
|
|
|
(void) getdns_list_get_length(suffix, &length);
|
|
if (length == 0 && *domain != 0)
|
|
_getdns_list_append_string(suffix, domain);
|
|
(void )getdns_context_set_suffix(context, suffix);
|
|
getdns_list_destroy(suffix);
|
|
|
|
dispatch_updated(context,
|
|
GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* set_os_defaults */
|
|
#endif
|
|
|
|
getdns_return_t
|
|
getdns_context_get_resolvconf(getdns_context *context, const char **resolvconf)
|
|
{
|
|
if (!context || !resolvconf)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
*resolvconf = *context->fchg_resolvconf.fn
|
|
? context->fchg_resolvconf.fn : NULL;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
|
|
/* compare of transaction ids in DESCENDING order
|
|
so that 0 comes last
|
|
*/
|
|
static int
|
|
transaction_id_cmp(const void *id1, const void *id2)
|
|
{
|
|
if (id1 == NULL && id2 == NULL) {
|
|
return 0;
|
|
} else if (id1 == NULL && id2 != NULL) {
|
|
return 1;
|
|
} else if (id1 != NULL && id2 == NULL) {
|
|
return -1;
|
|
} else {
|
|
getdns_transaction_t t1 =
|
|
*((const getdns_transaction_t *) id1);
|
|
getdns_transaction_t t2 =
|
|
*((const getdns_transaction_t *) id2);
|
|
if (t1 == t2) {
|
|
return 0;
|
|
} else if (t1 > t2) {
|
|
return -1;
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
NULL_update_callback(
|
|
getdns_context *context, getdns_context_code_t code, void *userarg)
|
|
{ (void)context; (void)code; (void)userarg; }
|
|
|
|
static int
|
|
netreq_expiry_cmp(const void *id1, const void *id2)
|
|
{
|
|
getdns_network_req *req1 = (getdns_network_req *)id1;
|
|
getdns_network_req *req2 = (getdns_network_req *)id2;
|
|
|
|
return req1->owner->expires < req2->owner->expires ? -1 :
|
|
req1->owner->expires > req2->owner->expires ? 1 :
|
|
req1 < req2 ? -1 :
|
|
req1 > req2 ? 1 : 0;
|
|
}
|
|
|
|
void _getdns_check_expired_pending_netreqs(
|
|
getdns_context *context, uint64_t *now_ms);
|
|
static void _getdns_check_expired_pending_netreqs_cb(void *arg)
|
|
{
|
|
uint64_t now_ms = 0;
|
|
_getdns_check_expired_pending_netreqs((getdns_context *)arg, &now_ms);
|
|
}
|
|
|
|
static char const * const _getdns_default_trust_anchors_url =
|
|
"http://data.iana.org/root-anchors/root-anchors.xml";
|
|
|
|
/* The ICANN CA fetched at 24 Sep 2010. Valid to 2028 */
|
|
static char const * const _getdns_default_trust_anchors_verify_CA =
|
|
"-----BEGIN CERTIFICATE-----\n"
|
|
"MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n"
|
|
"TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n"
|
|
"BAMTDUlDQU5OIFJvb3QgQ0ExCzAJBgNVBAYTAlVTMB4XDTA5MTIyMzA0MTkxMloX\n"
|
|
"DTI5MTIxODA0MTkxMlowXTEOMAwGA1UEChMFSUNBTk4xJjAkBgNVBAsTHUlDQU5O\n"
|
|
"IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRYwFAYDVQQDEw1JQ0FOTiBSb290IENB\n"
|
|
"MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKDb\n"
|
|
"cLhPNNqc1NB+u+oVvOnJESofYS9qub0/PXagmgr37pNublVThIzyLPGCJ8gPms9S\n"
|
|
"G1TaKNIsMI7d+5IgMy3WyPEOECGIcfqEIktdR1YWfJufXcMReZwU4v/AdKzdOdfg\n"
|
|
"ONiwc6r70duEr1IiqPbVm5T05l1e6D+HkAvHGnf1LtOPGs4CHQdpIUcy2kauAEy2\n"
|
|
"paKcOcHASvbTHK7TbbvHGPB+7faAztABLoneErruEcumetcNfPMIjXKdv1V1E3C7\n"
|
|
"MSJKy+jAqqQJqjZoQGB0necZgUMiUv7JK1IPQRM2CXJllcyJrm9WFxY0c1KjBO29\n"
|
|
"iIKK69fcglKcBuFShUECAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
|
|
"Af8EBAMCAf4wHQYDVR0OBBYEFLpS6UmDJIZSL8eZzfyNa2kITcBQMA0GCSqGSIb3\n"
|
|
"DQEBCwUAA4IBAQAP8emCogqHny2UYFqywEuhLys7R9UKmYY4suzGO4nkbgfPFMfH\n"
|
|
"6M+Zj6owwxlwueZt1j/IaCayoKU3QsrYYoDRolpILh+FPwx7wseUEV8ZKpWsoDoD\n"
|
|
"2JFbLg2cfB8u/OlE4RYmcxxFSmXBg0yQ8/IoQt/bxOcEEhhiQ168H2yE5rxJMt9h\n"
|
|
"15nu5JBSewrCkYqYYmaxyOC3WrVGfHZxVI7MpIFcGdvSb2a1uyuua8l0BKgk3ujF\n"
|
|
"0/wsHNeP22qNyVO+XVBzrM8fk8BSUFuiT/6tZTYXRtEt5aKQZgXbKU5dUF3jT9qg\n"
|
|
"j/Br5BZw3X/zd325TvnswzMC1+ljLzHnQGGk\n"
|
|
"-----END CERTIFICATE-----\n";
|
|
|
|
static char const * const _getdns_default_trust_anchors_verify_email =
|
|
"dnssec@iana.org";
|
|
|
|
/*
|
|
* getdns_context_create
|
|
*
|
|
* Call this to initialize the context that is used in other getdns calls.
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_create_with_extended_memory_functions(
|
|
getdns_context **context,
|
|
int set_from_os,
|
|
void *userarg,
|
|
void *(*malloc)(void *userarg, size_t),
|
|
void *(*realloc)(void *userarg, void *, size_t),
|
|
void (*free)(void *userarg, void *)
|
|
)
|
|
{
|
|
getdns_return_t r;
|
|
struct getdns_context *result = NULL;
|
|
mf_union mf;
|
|
gldns_buffer gbuf;
|
|
#ifdef USE_WINSOCK
|
|
WORD wVersionRequested;
|
|
#endif
|
|
|
|
if (!context || !malloc || !realloc || !free)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
/** default init **/
|
|
mf.ext.malloc = malloc;
|
|
result = userarg == MF_PLAIN
|
|
? (*mf.pln.malloc)( sizeof(struct getdns_context))
|
|
: (*mf.ext.malloc)(userarg, sizeof(struct getdns_context));
|
|
|
|
if (!result)
|
|
return GETDNS_RETURN_MEMORY_ERROR;
|
|
|
|
#ifdef USE_WINSOCK
|
|
/* We need to run WSAStartup() to be able to use getaddrinfo() */
|
|
wVersionRequested = MAKEWORD(2, 2);
|
|
if (WSAStartup(wVersionRequested, &result->wsaData)) {
|
|
r = GETDNS_RETURN_GENERIC_ERROR;
|
|
goto error;
|
|
}
|
|
#endif
|
|
result->processing = 0;
|
|
result->destroying = 0;
|
|
result->my_mf.mf_arg = userarg;
|
|
result->my_mf.mf.ext.malloc = malloc;
|
|
result->my_mf.mf.ext.realloc = realloc;
|
|
result->my_mf.mf.ext.free = free;
|
|
|
|
result->update_callback = NULL;
|
|
result->update_callback2 = NULL_update_callback;
|
|
result->update_userarg = NULL;
|
|
|
|
result->log.func = NULL;
|
|
result->log.userarg = NULL;
|
|
result->log.system = 0;
|
|
result->log.level = GETDNS_LOG_ERR;
|
|
|
|
result->mf.mf_arg = userarg;
|
|
result->mf.mf.ext.malloc = malloc;
|
|
result->mf.mf.ext.realloc = realloc;
|
|
result->mf.mf.ext.free = free;
|
|
|
|
result->resolution_type_set = 0;
|
|
|
|
_getdns_rbtree_init(&result->outbound_requests, transaction_id_cmp);
|
|
_getdns_rbtree_init(&result->local_hosts, local_host_cmp);
|
|
_getdns_rbtree_init(&result->pending_netreqs, netreq_expiry_cmp);
|
|
result->first_pending_netreq = NULL;
|
|
result->netreqs_in_flight = 0;
|
|
result->pending_timeout_event.userarg = result;
|
|
result->pending_timeout_event.read_cb = NULL;
|
|
result->pending_timeout_event.write_cb = NULL;
|
|
result->pending_timeout_event.timeout_cb =
|
|
_getdns_check_expired_pending_netreqs_cb;
|
|
result->pending_timeout_event.ev = NULL;
|
|
|
|
result->server = NULL;
|
|
|
|
#ifdef HAVE_LIBUNBOUND
|
|
result->resolution_type = GETDNS_RESOLUTION_RECURSING;
|
|
#else
|
|
result->resolution_type = GETDNS_RESOLUTION_STUB;
|
|
#endif
|
|
if ((r = create_default_namespaces(result)))
|
|
goto error;
|
|
|
|
result->timeout = 5000;
|
|
result->idle_timeout = 0;
|
|
result->follow_redirects = GETDNS_REDIRECTS_FOLLOW;
|
|
result->dns_root_servers = NULL;
|
|
#if defined(HAVE_LIBUNBOUND) && !defined(HAVE_UB_CTX_SET_STUB)
|
|
result->root_servers_fn[0] = 0;
|
|
#endif
|
|
result->append_name = GETDNS_APPEND_NAME_TO_SINGLE_LABEL_FIRST;
|
|
result->suffixes = no_suffixes;
|
|
result->suffixes_len = sizeof(no_suffixes);
|
|
|
|
result->trust_anchors_source = GETDNS_TASRC_NONE;
|
|
result->can_write_appdata = PROP_UNKNOWN;
|
|
result->trust_anchors_url = NULL;
|
|
result->trust_anchors_verify_email = NULL;
|
|
result->trust_anchors_verify_CA = NULL;
|
|
result->appdata_dir = NULL;
|
|
result->tls_ca_path = NULL;
|
|
result->tls_ca_file = NULL;
|
|
result->tls_cipher_list = NULL;
|
|
result->tls_curves_list = NULL;
|
|
|
|
(void) memset(&result->root_ksk, 0, sizeof(result->root_ksk));
|
|
|
|
(void) memset(&result->a, 0, sizeof(result->a));
|
|
(void) memset(&result->aaaa, 0, sizeof(result->aaaa));
|
|
result->a.fd = -1;
|
|
result->aaaa.fd = -1;
|
|
|
|
gldns_buffer_init_vfixed_frm_data(&gbuf, result->trust_anchors_spc
|
|
, sizeof(result->trust_anchors_spc));
|
|
|
|
if (!_getdns_parse_ta_file(NULL, &gbuf)) {
|
|
result->trust_anchors = NULL;
|
|
result->trust_anchors_len = 0;
|
|
|
|
} else if ((result->trust_anchors_len = gldns_buffer_position(&gbuf))
|
|
> sizeof(result->trust_anchors_spc)) {
|
|
|
|
if ((result->trust_anchors = GETDNS_XMALLOC(
|
|
result->mf, uint8_t, result->trust_anchors_len))) {
|
|
|
|
gldns_buffer_init_frm_data(&gbuf
|
|
, result->trust_anchors
|
|
, result->trust_anchors_len);
|
|
if (!_getdns_parse_ta_file(NULL, &gbuf)) {
|
|
GETDNS_FREE(result->mf, result->trust_anchors);
|
|
result->trust_anchors = NULL;
|
|
result->trust_anchors_len = 0;
|
|
} else
|
|
result->trust_anchors_source = GETDNS_TASRC_ZONE;
|
|
}
|
|
} else {
|
|
result->trust_anchors = result->trust_anchors_spc;
|
|
result->trust_anchors_source = GETDNS_TASRC_ZONE;
|
|
}
|
|
|
|
result->upstreams = NULL;
|
|
|
|
result->edns_extended_rcode = 0;
|
|
result->edns_version = 0;
|
|
result->edns_do_bit = 0;
|
|
result->edns_client_subnet_private = 0;
|
|
result->tls_query_padding_blocksize = 1; /* default is to pad queries sensibly */
|
|
result->tls_ctx = NULL;
|
|
|
|
result->extension = &result->default_eventloop.loop;
|
|
_getdns_default_eventloop_init(&result->mf, &result->default_eventloop);
|
|
_getdns_default_eventloop_init(&result->mf, &result->sync_eventloop);
|
|
|
|
/* request extension defaults
|
|
*/
|
|
result->header = NULL;
|
|
result->add_opt_parameters = NULL;
|
|
result->add_warning_for_bad_dns = 0;
|
|
result->dnssec_return_all_statuses = 0;
|
|
result->dnssec_return_full_validation_chain = 0;
|
|
result->dnssec_return_only_secure = 0;
|
|
result->dnssec_return_status = 0;
|
|
result->dnssec_return_validation_chain = 0;
|
|
#ifdef DNSSEC_ROADBLOCK_AVOIDANCE
|
|
result->dnssec_roadblock_avoidance = 0;
|
|
#endif
|
|
result->edns_cookies = 0;
|
|
result->return_api_information = 0;
|
|
result->return_both_v4_and_v6 = 0;
|
|
result->return_call_reporting = 0;
|
|
result->specify_class = GETDNS_RRCLASS_IN;
|
|
|
|
result->sys_ctxt = NULL;
|
|
result->ta_notify = NULL;
|
|
|
|
/* state data used to detect changes to the system config files
|
|
*/
|
|
(void)memset(&result->fchg_resolvconf, 0, sizeof(struct filechg));
|
|
(void)memset(&result->fchg_hosts , 0, sizeof(struct filechg));
|
|
|
|
result->dnssec_allowed_skew = 0;
|
|
result->edns_maximum_udp_payload_size = -1;
|
|
if ((r = create_default_dns_transports(result)))
|
|
goto error;
|
|
result->tls_auth = GETDNS_AUTHENTICATION_NONE;
|
|
result->tls_auth_min = GETDNS_AUTHENTICATION_NONE;
|
|
result->round_robin_upstreams = 0;
|
|
result->tls_backoff_time = 3600;
|
|
result->tls_connection_retries = 2;
|
|
result->limit_outstanding_queries = 0;
|
|
result->max_backoff_value = UDP_MAX_BACKOFF;
|
|
|
|
/* unbound context is initialized here */
|
|
/* Unbound needs SSL to be init'ed this early when TLS is used. However we
|
|
* don't know that till later so we will have to do this every time. */
|
|
|
|
#ifdef HAVE_PTHREAD
|
|
pthread_mutex_lock(&ssl_init_lock);
|
|
#else
|
|
/* XXX implement Windows-style lock here */
|
|
#endif
|
|
/* Only initialise SSL once and ideally in a thread-safe manner */
|
|
if (ssl_init == false) {
|
|
_getdns_tls_init();
|
|
ssl_init = true;
|
|
}
|
|
#ifdef HAVE_PTHREAD
|
|
pthread_mutex_unlock(&ssl_init_lock);
|
|
#else
|
|
/* XXX implement Windows-style unlock here */
|
|
#endif
|
|
#ifdef HAVE_LIBUNBOUND
|
|
result->unbound_ctx = NULL;
|
|
if ((r = rebuild_ub_ctx(result)))
|
|
goto error;
|
|
#endif
|
|
|
|
|
|
#ifdef HAVE_MDNS_SUPPORT
|
|
_getdns_mdns_context_init(result);
|
|
#endif
|
|
|
|
// resolv.conf does not exist on Windows, handle differently
|
|
#ifndef USE_WINSOCK
|
|
if ((set_from_os & 1)) {
|
|
(void) getdns_context_set_resolvconf(result, GETDNS_FN_RESOLVCONF);
|
|
(void) getdns_context_set_hosts(result, GETDNS_FN_HOSTS);
|
|
}
|
|
#else
|
|
if ((set_from_os & 1) && (r = set_os_defaults_windows(result)))
|
|
goto error;
|
|
#endif
|
|
|
|
*context = result;
|
|
return GETDNS_RETURN_GOOD;
|
|
error:
|
|
getdns_context_destroy(result);
|
|
return r;
|
|
} /* getdns_context_create_with_extended_memory_functions */
|
|
|
|
/*
|
|
* getdns_context_create
|
|
*
|
|
* Call this to initialize the context that is used in other getdns calls.
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_create_with_memory_functions(struct getdns_context ** context,
|
|
int set_from_os,
|
|
void *(*malloc)(size_t),
|
|
void *(*realloc)(void *, size_t),
|
|
void (*free)(void *)
|
|
)
|
|
{
|
|
mf_union mf;
|
|
mf.pln.malloc = malloc;
|
|
mf.pln.realloc = realloc;
|
|
mf.pln.free = free;
|
|
return getdns_context_create_with_extended_memory_functions(
|
|
context, set_from_os, MF_PLAIN,
|
|
mf.ext.malloc, mf.ext.realloc, mf.ext.free);
|
|
} /* getdns_context_create */
|
|
|
|
/*
|
|
* getdns_context_create
|
|
*
|
|
* Call this to initialize the context that is used in other getdns calls.
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_create(struct getdns_context ** context, int set_from_os)
|
|
{
|
|
return getdns_context_create_with_memory_functions(context,
|
|
set_from_os, malloc, realloc, free);
|
|
} /* getdns_context_create */
|
|
|
|
/*
|
|
* getdns_context_destroy
|
|
*
|
|
* Call this to dispose of resources associated with a context once you
|
|
* are done with it.
|
|
*/
|
|
void
|
|
getdns_context_destroy(struct getdns_context *context)
|
|
{
|
|
if (context == NULL)
|
|
return;
|
|
|
|
/* If being destroyed during getdns callback, fail via assert */
|
|
assert(context->processing == 0);
|
|
if (context->destroying)
|
|
return;
|
|
|
|
context->destroying = 1;
|
|
|
|
if (context->sys_ctxt)
|
|
getdns_context_destroy(context->sys_ctxt);
|
|
|
|
/* cancel all outstanding requests */
|
|
cancel_outstanding_requests(context);
|
|
|
|
/* Destroy listening addresses */
|
|
(void) getdns_context_set_listen_addresses(context, NULL, NULL, NULL);
|
|
|
|
/* This needs to be done before cleaning the extension, because there
|
|
* might be an idle_timeout schedules, which will not get unscheduled
|
|
* with cancel_outstanding_requests.
|
|
*/
|
|
_getdns_upstreams_dereference(context->upstreams);
|
|
|
|
context->sync_eventloop.loop.vmt->cleanup(&context->sync_eventloop.loop);
|
|
context->extension->vmt->cleanup(context->extension);
|
|
#ifdef HAVE_LIBUNBOUND
|
|
if (context->unbound_ctx)
|
|
ub_ctx_delete(context->unbound_ctx);
|
|
#endif
|
|
|
|
#ifdef HAVE_MDNS_SUPPORT
|
|
/*
|
|
* Release all ressource allocated for MDNS.
|
|
*/
|
|
_getdns_mdns_context_destroy(context);
|
|
#endif
|
|
|
|
if (context->namespaces)
|
|
GETDNS_FREE(context->my_mf, context->namespaces);
|
|
|
|
if (context->dns_transports)
|
|
GETDNS_FREE(context->my_mf, context->dns_transports);
|
|
|
|
if (context->tls_ctx)
|
|
_getdns_tls_context_free(&context->my_mf, context->tls_ctx);
|
|
|
|
getdns_list_destroy(context->dns_root_servers);
|
|
|
|
#if defined(HAVE_LIBUNBOUND) && !defined(HAVE_UB_CTX_SET_STUB)
|
|
if (context->root_servers_fn[0])
|
|
unlink(context->root_servers_fn);
|
|
#endif
|
|
|
|
if (context->suffixes && context->suffixes != no_suffixes)
|
|
GETDNS_FREE(context->mf, (void *)context->suffixes);
|
|
|
|
if (context->trust_anchors &&
|
|
context->trust_anchors != context->trust_anchors_spc)
|
|
GETDNS_FREE(context->mf, context->trust_anchors);
|
|
|
|
_getdns_traverse_postorder(&context->local_hosts,
|
|
destroy_local_host, context);
|
|
|
|
getdns_dict_destroy(context->header);
|
|
getdns_dict_destroy(context->add_opt_parameters);
|
|
|
|
if (context->trust_anchors_url)
|
|
GETDNS_FREE(context->mf, context->trust_anchors_url);
|
|
if (context->trust_anchors_verify_CA)
|
|
GETDNS_FREE( context->mf
|
|
, context->trust_anchors_verify_CA);
|
|
if (context->trust_anchors_verify_email)
|
|
GETDNS_FREE( context->mf
|
|
, context->trust_anchors_verify_email);
|
|
if (context->appdata_dir)
|
|
GETDNS_FREE(context->mf, context->appdata_dir);
|
|
if (context->tls_ca_path)
|
|
GETDNS_FREE(context->mf, context->tls_ca_path);
|
|
if (context->tls_ca_file)
|
|
GETDNS_FREE(context->mf, context->tls_ca_file);
|
|
if (context->tls_cipher_list)
|
|
GETDNS_FREE(context->mf, context->tls_cipher_list);
|
|
if (context->tls_curves_list)
|
|
GETDNS_FREE(context->mf, context->tls_curves_list);
|
|
|
|
#ifdef USE_WINSOCK
|
|
WSACleanup();
|
|
#endif
|
|
GETDNS_FREE(context->my_mf, context);
|
|
} /* getdns_context_destroy */
|
|
|
|
/*
|
|
* getdns_context_set_context_update_callback
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_context_update_callback(struct getdns_context *context,
|
|
void (*value) (struct getdns_context *context,
|
|
getdns_context_code_t changed_item))
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
context->update_callback = value;
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_context_update_callback */
|
|
|
|
getdns_return_t
|
|
getdns_context_set_update_callback(getdns_context *context, void *userarg,
|
|
void (*cb)(getdns_context *, getdns_context_code_t, void *))
|
|
{
|
|
if (!context) return GETDNS_RETURN_INVALID_PARAMETER;
|
|
context->update_userarg = userarg;
|
|
context->update_callback2 = cb ? cb : NULL_update_callback;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_update_callback(getdns_context *context, void **userarg,
|
|
void (**cb)(getdns_context *, getdns_context_code_t, void *))
|
|
{
|
|
if (!context || !userarg || !cb)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
*userarg = context->update_userarg;
|
|
*cb = context->update_callback2;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_logfunc(getdns_context *context, void *userarg,
|
|
uint64_t system, getdns_loglevel_type level, getdns_logfunc_type log)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
context->log.func = log;
|
|
context->log.userarg = userarg;
|
|
context->log.system = system;
|
|
context->log.level = level;
|
|
if (context->upstreams) {
|
|
context->upstreams->log = context->log;
|
|
}
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
void _getdns_context_log(getdns_context *context, uint64_t system,
|
|
getdns_loglevel_type level, const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
|
|
if (!context || !context->log.func || !(context->log.system & system)
|
|
|| level > context->log.level)
|
|
return;
|
|
|
|
va_start(args, fmt);
|
|
context->log.func(context->log.userarg, system, level, fmt, args);
|
|
va_end(args);
|
|
}
|
|
|
|
#ifdef HAVE_LIBUNBOUND
|
|
/*
|
|
* Helpers to set options on the unbound ctx
|
|
*/
|
|
static void
|
|
set_ub_string_opt(struct getdns_context *ctx, char *opt, char *value)
|
|
{
|
|
if (ctx->unbound_ctx)
|
|
ub_ctx_set_option(ctx->unbound_ctx, opt, value);
|
|
}
|
|
|
|
static void
|
|
set_ub_number_opt(struct getdns_context *ctx, char *opt, uint16_t value)
|
|
{
|
|
char buffer[64];
|
|
snprintf(buffer, 64, "%hu", value);
|
|
set_ub_string_opt(ctx, opt, buffer);
|
|
}
|
|
|
|
static void
|
|
getdns_context_request_count_changed(getdns_context *context)
|
|
{
|
|
size_t prev_count;
|
|
|
|
if (context->ub_event_scheduling) {
|
|
return;
|
|
}
|
|
context->ub_event_scheduling++;
|
|
do {
|
|
prev_count = context->outbound_requests.count;
|
|
DEBUG_SCHED("getdns_context_request_count_changed(%d)\n",
|
|
(int) context->outbound_requests.count);
|
|
if (context->outbound_requests.count && ! context->ub_event.ev){
|
|
DEBUG_SCHED("gc_request_count_changed "
|
|
"-> ub schedule(el_ev = %p, el_ev->ev = %p)\n",
|
|
(void *)&context->ub_event, (void *)context->ub_event.ev);
|
|
#ifndef USE_WINSOCK
|
|
#ifdef HAVE_UNBOUND_EVENT_API
|
|
if (!_getdns_ub_loop_enabled(&context->ub_loop))
|
|
#endif
|
|
context->extension->vmt->schedule(
|
|
context->extension,
|
|
ub_fd(context->unbound_ctx),
|
|
TIMEOUT_FOREVER, &context->ub_event);
|
|
#endif
|
|
} else if (! context->outbound_requests.count &&
|
|
context->ub_event.ev) {
|
|
DEBUG_SCHED("gc_request_count_changed "
|
|
"-> ub clear(el_ev = %p, el_ev->ev = %p)\n",
|
|
(void *)&context->ub_event, (void *)context->ub_event.ev);
|
|
|
|
#ifndef USE_WINSOCK
|
|
#ifdef HAVE_UNBOUND_EVENT_API
|
|
if (!_getdns_ub_loop_enabled(&context->ub_loop))
|
|
#endif
|
|
context->extension->vmt->clear(
|
|
context->extension, &context->ub_event);
|
|
#endif
|
|
}
|
|
} while (prev_count != context->outbound_requests.count);
|
|
context->ub_event_scheduling--;
|
|
}
|
|
|
|
void
|
|
_getdns_context_ub_read_cb(void *userarg)
|
|
{
|
|
getdns_context *context = (getdns_context *)userarg;
|
|
|
|
/* getdns_context_process_async, but without reinvoking an eventloop
|
|
* (with context->extension->vmt->run*), because we are already
|
|
* called from a running eventloop.
|
|
*/
|
|
if (ub_poll(context->unbound_ctx))
|
|
(void) ub_process(context->unbound_ctx);
|
|
|
|
/* No need to handle timeouts. They are handled by the extension. */
|
|
|
|
getdns_context_request_count_changed(context);
|
|
}
|
|
|
|
static getdns_return_t
|
|
rebuild_ub_ctx(struct getdns_context* context) {
|
|
if (context->unbound_ctx != NULL) {
|
|
/* cancel all requests and delete */
|
|
cancel_outstanding_requests(context);
|
|
ub_ctx_delete(context->unbound_ctx);
|
|
context->unbound_ctx = NULL;
|
|
}
|
|
/* setup */
|
|
#ifdef HAVE_UNBOUND_EVENT_API
|
|
_getdns_ub_loop_init(&context->ub_loop, &context->mf, context->extension);
|
|
if (_getdns_ub_loop_enabled(&context->ub_loop)) {
|
|
context->unbound_ctx = ub_ctx_create_ub_event(&context->ub_loop.super);
|
|
} else {
|
|
#endif
|
|
context->unbound_ctx = ub_ctx_create();
|
|
(void) ub_ctx_async(context->unbound_ctx, 1);
|
|
#ifdef HAVE_UNBOUND_EVENT_API
|
|
}
|
|
#endif
|
|
context->unbound_ta_set = 0;
|
|
if (!context->unbound_ctx)
|
|
return GETDNS_RETURN_MEMORY_ERROR;
|
|
|
|
#ifdef HAVE_UNBOUND_EVENT_API
|
|
ub_ctx_set_option(context->unbound_ctx,
|
|
"target-fetch-policy:", "0 0 0 0 0");
|
|
#endif
|
|
set_ub_dnssec_allowed_skew(context,
|
|
context->dnssec_allowed_skew);
|
|
set_ub_number_opt(context, "edns-buffer-size:",
|
|
context->edns_maximum_udp_payload_size);
|
|
set_ub_dns_transport(context);
|
|
|
|
context->ub_event.userarg = context;
|
|
context->ub_event.read_cb = _getdns_context_ub_read_cb;
|
|
context->ub_event.write_cb = NULL;
|
|
context->ub_event.timeout_cb = NULL;
|
|
context->ub_event.ev = NULL;
|
|
context->ub_event_scheduling = 0;
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
#else
|
|
#define set_ub_string_opt(ctx, opt, value) do {} while (0)
|
|
#define set_ub_number_opt(ctx, opt, value) do {} while (0)
|
|
#define getdns_context_request_count_changed(context) do {} while (0)
|
|
#endif
|
|
|
|
/**
|
|
* Helper to dispatch the updated callback
|
|
*/
|
|
static void
|
|
dispatch_updated(struct getdns_context *context, uint16_t item)
|
|
{
|
|
if (context->update_callback2 != NULL_update_callback)
|
|
context->update_callback2(
|
|
context, item, context->update_userarg);
|
|
|
|
if (context->update_callback) {
|
|
context->update_callback(context, item);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* getdns_context_set_resolution_type
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_resolution_type(struct getdns_context *context,
|
|
getdns_resolution_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
if (value != GETDNS_RESOLUTION_STUB && value != GETDNS_RESOLUTION_RECURSING) {
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
#ifndef HAVE_LIBUNBOUND
|
|
if (value == GETDNS_RESOLUTION_RECURSING)
|
|
return GETDNS_RETURN_NOT_IMPLEMENTED;
|
|
#endif
|
|
#ifndef STUB_NATIVE_DNSSEC
|
|
if (context->resolution_type_set != 0) {
|
|
/* already setup */
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
#endif
|
|
context->resolution_type = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_RESOLUTION_TYPE);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_resolution_type */
|
|
|
|
/*
|
|
* getdns_context_set_namespaces
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_namespaces(getdns_context *context,
|
|
size_t namespace_count, getdns_namespace_t *namespaces)
|
|
{
|
|
size_t i;
|
|
getdns_return_t r = GETDNS_RETURN_GOOD;
|
|
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (namespace_count == 0 || namespaces == NULL)
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
|
|
for (i = 0; i < namespace_count; i++) {
|
|
if (namespaces[i] == GETDNS_NAMESPACE_NETBIOS ||
|
|
#ifndef HAVE_MDNS_SUPPORT
|
|
namespaces[i] == GETDNS_NAMESPACE_MDNS ||
|
|
#endif
|
|
namespaces[i] == GETDNS_NAMESPACE_NIS)
|
|
r = GETDNS_RETURN_NOT_IMPLEMENTED;
|
|
|
|
else if (namespaces[i] != GETDNS_NAMESPACE_DNS &&
|
|
#ifdef HAVE_MDNS_SUPPORT
|
|
namespaces[i] != GETDNS_NAMESPACE_MDNS &&
|
|
#endif
|
|
namespaces[i] != GETDNS_NAMESPACE_LOCALNAMES )
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
GETDNS_FREE(context->my_mf, context->namespaces);
|
|
|
|
/** duplicate **/
|
|
context->namespaces = GETDNS_XMALLOC(
|
|
context->my_mf, getdns_namespace_t, namespace_count);
|
|
(void) memcpy(context->namespaces, namespaces,
|
|
namespace_count * sizeof(getdns_namespace_t));
|
|
context->namespace_count = namespace_count;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_NAMESPACES);
|
|
|
|
return r;
|
|
} /* getdns_context_set_namespaces */
|
|
|
|
static getdns_return_t
|
|
getdns_set_base_dns_transports(
|
|
getdns_context *context, size_t transport_count, getdns_transport_list_t *transports)
|
|
{
|
|
size_t i;
|
|
getdns_transport_list_t *new_transports;
|
|
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
if (transport_count == 0 || !transports)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
/* Check for valid transports and that they are used only once*/
|
|
int u=0,t=0,l=0;
|
|
for(i=0; i<transport_count; i++)
|
|
{
|
|
switch (transports[i]) {
|
|
case GETDNS_TRANSPORT_UDP: u++; break;
|
|
case GETDNS_TRANSPORT_TCP: t++; break;
|
|
case GETDNS_TRANSPORT_TLS: l++; break;
|
|
default: return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
}
|
|
if ( u>1 || t>1 || l>1)
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
|
|
if (!(new_transports = GETDNS_XMALLOC(context->my_mf,
|
|
getdns_transport_list_t, transport_count)))
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
|
|
if (context->dns_transports)
|
|
GETDNS_FREE(context->my_mf, context->dns_transports);
|
|
|
|
/** duplicate **/
|
|
context->dns_transports = new_transports;
|
|
memcpy(context->dns_transports, transports,
|
|
transport_count * sizeof(getdns_transport_list_t));
|
|
context->dns_transport_count = transport_count;
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
static getdns_return_t
|
|
set_ub_dns_transport(struct getdns_context* context) {
|
|
int fallback;
|
|
size_t i;
|
|
|
|
/* These mappings are not exact because Unbound is configured differently,
|
|
so just map as close as possible. Not all options can be supported.*/
|
|
switch (context->dns_transports[0]) {
|
|
case GETDNS_TRANSPORT_UDP:
|
|
set_ub_string_opt(context, "do-udp:", "yes");
|
|
if (context->dns_transport_count > 1
|
|
&& context->dns_transports[1] == GETDNS_TRANSPORT_TCP)
|
|
set_ub_string_opt(context, "do-tcp:", "yes");
|
|
else
|
|
set_ub_string_opt(context, "do-tcp:", "no");
|
|
break;
|
|
case GETDNS_TRANSPORT_TCP:
|
|
set_ub_string_opt(context, "do-udp:", "no");
|
|
set_ub_string_opt(context, "do-tcp:", "yes");
|
|
break;
|
|
case GETDNS_TRANSPORT_TLS:
|
|
set_ub_string_opt(context, "do-udp:", "no");
|
|
set_ub_string_opt(context, "do-tcp:", "yes");
|
|
/* Find out if there is a fallback available. */
|
|
fallback = 0;
|
|
for (i = 1; i < context->dns_transport_count; i++) {
|
|
if (context->dns_transports[i] == GETDNS_TRANSPORT_TCP) {
|
|
fallback = 1;
|
|
break;
|
|
}
|
|
else if (context->dns_transports[i] == GETDNS_TRANSPORT_UDP) {
|
|
set_ub_string_opt(context, "do-udp:", "yes");
|
|
set_ub_string_opt(context, "do-tcp:", "no");
|
|
fallback = 1;
|
|
break;
|
|
}
|
|
}
|
|
if (fallback == 0)
|
|
/* Use TLS if it is the only thing.*/
|
|
set_ub_string_opt(context, "ssl-upstream:", "yes");
|
|
break;
|
|
default:
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
/*
|
|
* getdns_context_set_dns_transport
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_dns_transport(
|
|
getdns_context *context, getdns_transport_t value)
|
|
{
|
|
size_t count = 2;
|
|
getdns_transport_list_t *new_transports;
|
|
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
|
|
if (value == GETDNS_TRANSPORT_UDP_ONLY ||
|
|
value == GETDNS_TRANSPORT_TCP_ONLY ||
|
|
value == GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN ||
|
|
value == GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN)
|
|
count = 1;
|
|
|
|
if (!(new_transports = GETDNS_XMALLOC(
|
|
context->my_mf, getdns_transport_list_t, count)))
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
|
|
if (context->dns_transports)
|
|
GETDNS_FREE(context->my_mf, context->dns_transports);
|
|
|
|
context->dns_transport_count = count;
|
|
context->dns_transports = new_transports;
|
|
|
|
switch ((int)value) {
|
|
case GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP:
|
|
context->dns_transports[0] = GETDNS_TRANSPORT_UDP;
|
|
context->dns_transports[1] = GETDNS_TRANSPORT_TCP;
|
|
break;
|
|
case GETDNS_TRANSPORT_UDP_ONLY:
|
|
context->dns_transports[0] = GETDNS_TRANSPORT_UDP;
|
|
break;
|
|
case GETDNS_TRANSPORT_TCP_ONLY:
|
|
case GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN:
|
|
context->dns_transports[0] = GETDNS_TRANSPORT_TCP;
|
|
break;
|
|
case GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN:
|
|
context->dns_transports[0] = GETDNS_TRANSPORT_TLS;
|
|
break;
|
|
case GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN:
|
|
context->dns_transports[0] = GETDNS_TRANSPORT_TLS;
|
|
context->dns_transports[1] = GETDNS_TRANSPORT_TCP;
|
|
break;
|
|
default:
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
/* Note that the call below does not have any effect in unbound after the
|
|
* ctx is finalised so for recursive mode or stub + dnssec only the first
|
|
* transport specified on the first query is used.
|
|
* However the method returns success as otherwise the transport could not
|
|
* be reset for stub mode.
|
|
* Also, not all transport options supported in libunbound yet */
|
|
if (set_ub_dns_transport(context) != GETDNS_RETURN_GOOD) {
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_DNS_TRANSPORT);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
/*
|
|
* getdns_context_set_dns_transport_list
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_dns_transport_list(getdns_context *context,
|
|
size_t transport_count, getdns_transport_list_t *transports)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
if (getdns_set_base_dns_transports(context, transport_count, transports) != GETDNS_RETURN_GOOD)
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
/* Note that the call below does not have any effect in unbound after the
|
|
* ctx is finalised so for recursive mode or stub + dnssec only the first
|
|
* transport specified on the first query is used.
|
|
* However the method returns success as otherwise the transport could not
|
|
* be reset for stub mode.
|
|
* Also, not all transport options supported in libunbound yet */
|
|
if (set_ub_dns_transport(context) != GETDNS_RETURN_GOOD) {
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_DNS_TRANSPORT);
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_dns_transport_list */
|
|
|
|
/*
|
|
* getdns_context_set_tls_authentication
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_tls_authentication(getdns_context *context,
|
|
getdns_tls_authentication_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
if (value != GETDNS_AUTHENTICATION_NONE &&
|
|
value != GETDNS_AUTHENTICATION_REQUIRED) {
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
context->tls_auth = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_TLS_AUTHENTICATION);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_tls_authentication_list */
|
|
|
|
/*
|
|
* getdns_context_set_round_robin_upstreams
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_round_robin_upstreams(getdns_context *context, uint8_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
/* only allow 0 or 1 */
|
|
if (value != 0 && value != 1) {
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
|
|
context->round_robin_upstreams = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_ROUND_ROBIN_UPSTREAMS);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_round_robin_upstreams */
|
|
|
|
/**
|
|
* Set the maximum number of messages that can be sent to other upstreams
|
|
* before the upstream which has previously timed out will be tried again.
|
|
* @see getdns_context_get_max_backoff_value
|
|
* @param[in] context The context to configure
|
|
* @param[in[ value Number of messages sent to other upstreams before
|
|
* retrying the upstream which had timed out.
|
|
* @return GETDNS_RETURN_GOOD on success
|
|
* @return GETDNS_RETURN_INVALID_PARAMETER if context is null.
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_max_backoff_value(getdns_context *context, uint16_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
|
|
context->max_backoff_value = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_MAX_BACKOFF_VALUE);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_max_backoff_value */
|
|
|
|
/*
|
|
* getdns_context_set_tls_backoff_time
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_tls_backoff_time(getdns_context *context, uint16_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
/* Value is in seconds. Should we have a lower limit? 1 second?*/
|
|
context->tls_backoff_time = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_TLS_BACKOFF_TIME);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_tls_backoff_time */
|
|
|
|
/*
|
|
* getdns_context_set_tls_connection_retries
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_tls_connection_retries(getdns_context *context, uint16_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
/* Should we put a sensible upper limit on this? 10?*/
|
|
// if (value > 10) {
|
|
// return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
// }
|
|
|
|
context->tls_connection_retries = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_TLS_CONNECTION_RETRIES);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_tls_connection retries */
|
|
|
|
#ifdef HAVE_LIBUNBOUND
|
|
static void
|
|
set_ub_limit_outstanding_queries(getdns_context* context, uint16_t value) {
|
|
/* num-queries-per-thread */
|
|
set_ub_number_opt(context, "num-queries-per-thread:", value);
|
|
}
|
|
#endif
|
|
/*
|
|
* getdns_context_set_limit_outstanding_queries
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_limit_outstanding_queries(struct getdns_context *context,
|
|
uint16_t limit)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
#ifdef HAVE_LIBUNBOUND
|
|
set_ub_limit_outstanding_queries(context, limit);
|
|
#endif
|
|
if (limit != context->limit_outstanding_queries) {
|
|
context->limit_outstanding_queries = limit;
|
|
dispatch_updated(context,
|
|
GETDNS_CONTEXT_CODE_LIMIT_OUTSTANDING_QUERIES);
|
|
}
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_limit_outstanding_queries */
|
|
|
|
/*
|
|
* getdns_context_set_timeout
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_timeout(struct getdns_context *context, uint64_t timeout)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
|
|
if (timeout == 0) {
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
}
|
|
|
|
context->timeout = timeout;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_TIMEOUT);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_timeout */
|
|
|
|
/*
|
|
* getdns_context_set_idle_timeout
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_idle_timeout(getdns_context *context, uint64_t timeout)
|
|
{
|
|
size_t i;
|
|
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
/* Shuold we enforce maximum based on edns-tcp-keepalive spec? */
|
|
/* 0 should be allowed as that is the default.*/
|
|
|
|
context->idle_timeout = timeout;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_IDLE_TIMEOUT);
|
|
|
|
if (timeout)
|
|
return GETDNS_RETURN_GOOD;
|
|
|
|
/* If timeout == 0, call scheduled idle timeout events */
|
|
for (i = 0; i < context->upstreams->count; i++) {
|
|
getdns_upstream *upstream =
|
|
&context->upstreams->upstreams[i];
|
|
|
|
if (!upstream->event.ev ||
|
|
!upstream->event.timeout_cb ||
|
|
upstream->event.read_cb ||
|
|
upstream->event.write_cb)
|
|
continue;
|
|
|
|
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
|
upstream->event.timeout_cb(upstream->event.userarg);
|
|
}
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_timeout */
|
|
|
|
|
|
/*
|
|
* getdns_context_set_follow_redirects
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_follow_redirects(
|
|
getdns_context *context, getdns_redirects_t value)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (value != GETDNS_REDIRECTS_FOLLOW &&
|
|
value != GETDNS_REDIRECTS_DO_NOT_FOLLOW)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
context->follow_redirects = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_FOLLOW_REDIRECTS);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_follow_redirects */
|
|
|
|
/*
|
|
* getdns_context_set_dns_root_servers
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_dns_root_servers(
|
|
getdns_context *context, getdns_list *addresses)
|
|
{
|
|
#ifdef HAVE_LIBUNBOUND
|
|
# ifndef HAVE_UB_CTX_SET_STUB
|
|
char tmpfn[FILENAME_MAX] = P_tmpdir "/getdns-root-dns-servers-XXXXXX";
|
|
FILE *fh;
|
|
int fd;
|
|
size_t dst_len;
|
|
# endif
|
|
size_t i;
|
|
getdns_dict *rr_dict;
|
|
getdns_return_t r = GETDNS_RETURN_GOOD;
|
|
getdns_bindata *addr_bd;
|
|
char dst[2048];
|
|
#endif
|
|
getdns_list *newlist;
|
|
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (!addresses) {
|
|
#ifdef HAVE_LIBUNBOUND
|
|
if (ub_ctx_set_option(
|
|
context->unbound_ctx, "root-hints:", ""))
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
#endif
|
|
if (context->dns_root_servers)
|
|
getdns_list_destroy(context->dns_root_servers);
|
|
context->dns_root_servers = NULL;
|
|
|
|
#if defined(HAVE_LIBUNBOUND) && !defined(HAVE_UB_CTX_SET_STUB)
|
|
if (context->root_servers_fn[0])
|
|
unlink(context->root_servers_fn);
|
|
context->root_servers_fn[0] = 0;
|
|
#endif
|
|
dispatch_updated(
|
|
context, GETDNS_CONTEXT_CODE_DNS_ROOT_SERVERS);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
#ifdef HAVE_LIBUNBOUND
|
|
# ifdef HAVE_UB_CTX_SET_STUB
|
|
for (i=0; !r; i++) {
|
|
if (!(r = getdns_list_get_bindata(addresses, i, &addr_bd)))
|
|
/* success! */
|
|
;
|
|
|
|
else if ((r = getdns_list_get_dict(addresses, i, &rr_dict)))
|
|
/* Not a bindata, not a dict? ERROR! */
|
|
break;
|
|
|
|
else if (getdns_dict_get_bindata(
|
|
rr_dict, "address_data", &addr_bd) &&
|
|
getdns_dict_get_bindata(
|
|
rr_dict, "/rdata/ipv4_address", &addr_bd) &&
|
|
getdns_dict_get_bindata(
|
|
rr_dict, "/rdata/ipv6_address", &addr_bd))
|
|
|
|
/* Not a parsable address,
|
|
* pass because we allow root.hint's files as input
|
|
*/
|
|
continue;
|
|
|
|
if (addr_bd->size == 16 &&
|
|
inet_ntop(AF_INET6, addr_bd->data, dst, sizeof(dst))) {
|
|
|
|
if (ub_ctx_set_stub(context->unbound_ctx,".",dst,1)) {
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
|
|
} else if (addr_bd->size == 4 &&
|
|
inet_ntop(AF_INET, addr_bd->data, dst, sizeof(dst))) {
|
|
if (ub_ctx_set_stub(context->unbound_ctx,".",dst,1)) {
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
}
|
|
}
|
|
# else
|
|
if ((fd = mkstemp(tmpfn)) < 0)
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
|
|
if (!(fh = fdopen(fd, "w")))
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
for (i=0; !r; i++) {
|
|
dst_len = sizeof(dst);
|
|
if (!(r = getdns_list_get_bindata(addresses, i, &addr_bd)))
|
|
/* success! */
|
|
;
|
|
|
|
else if ((r = getdns_list_get_dict(addresses, i, &rr_dict)))
|
|
/* Not a bindata, not a dict? ERROR! */
|
|
break;
|
|
|
|
else if (!getdns_rr_dict2str_buf(rr_dict, dst, &dst_len)) {
|
|
|
|
fprintf(fh, "%s", dst);
|
|
continue;
|
|
|
|
} else if (getdns_dict_get_bindata(
|
|
rr_dict, "address_data", &addr_bd) &&
|
|
getdns_dict_get_bindata(
|
|
rr_dict, "/rdata/ipv4_address", &addr_bd) &&
|
|
getdns_dict_get_bindata(
|
|
rr_dict, "/rdata/ipv6_address", &addr_bd))
|
|
|
|
/* Not a parsable address,
|
|
* pass because we allow root.hint's files as input
|
|
*/
|
|
continue;
|
|
|
|
if (addr_bd->size == 16 &&
|
|
inet_ntop(AF_INET6, addr_bd->data, dst, sizeof(dst)))
|
|
|
|
fprintf(fh,". NS %"PRIsz".root-servers.getdnsapi.net.\n"
|
|
"%"PRIsz".root-servers.getdnsapi.net. AAAA %s\n",
|
|
i, i, dst);
|
|
|
|
else if (addr_bd->size == 4 &&
|
|
inet_ntop(AF_INET, addr_bd->data, dst, sizeof(dst)))
|
|
|
|
fprintf(fh,". NS %"PRIsz".root-servers.getdnsapi.net.\n"
|
|
"%"PRIsz".root-servers.getdnsapi.net. A %s\n",
|
|
i, i, dst);
|
|
}
|
|
fclose(fh);
|
|
if (ub_ctx_set_option(
|
|
context->unbound_ctx, "root-hints:", tmpfn)) {
|
|
unlink(tmpfn);
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
# endif
|
|
#endif
|
|
if (_getdns_list_copy(addresses, &newlist)) {
|
|
#if defined(HAVE_LIBUNBOUND) && !defined(HAVE_UB_CTX_SET_STUB)
|
|
unlink(tmpfn);
|
|
#endif
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
if (context->dns_root_servers)
|
|
getdns_list_destroy(context->dns_root_servers);
|
|
context->dns_root_servers = newlist;
|
|
|
|
#if defined(HAVE_LIBUNBOUND) && !defined(HAVE_UB_CTX_SET_STUB)
|
|
if (context->root_servers_fn[0])
|
|
unlink(context->root_servers_fn);
|
|
(void) memcpy(context->root_servers_fn, tmpfn, strlen(tmpfn));
|
|
#endif
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_DNS_ROOT_SERVERS);
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_dns_root_servers */
|
|
|
|
/*
|
|
* getdns_context_set_append_name
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_append_name(
|
|
getdns_context *context, getdns_append_name_t value)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
switch ((int)value) {
|
|
case GETDNS_APPEND_NAME_ALWAYS:
|
|
case GETDNS_APPEND_NAME_ONLY_TO_SINGLE_LABEL_AFTER_FAILURE:
|
|
case GETDNS_APPEND_NAME_ONLY_TO_MULTIPLE_LABEL_NAME_AFTER_FAILURE:
|
|
case GETDNS_APPEND_NAME_NEVER:
|
|
case GETDNS_APPEND_NAME_TO_SINGLE_LABEL_FIRST:
|
|
context->append_name = value;
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_APPEND_NAME);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
} /* getdns_context_set_append_name */
|
|
|
|
/*
|
|
* getdns_context_set_suffix
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_suffix(getdns_context *context, getdns_list *value)
|
|
{
|
|
getdns_return_t r;
|
|
size_t i;
|
|
gldns_buffer gbuf;
|
|
uint8_t buf_spc[1024], *suffixes = NULL;
|
|
size_t suffixes_len = 0;
|
|
uint8_t dname[256];
|
|
size_t dname_len;
|
|
char name_spc[1025], *name;
|
|
getdns_bindata *bindata;
|
|
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (value == NULL) {
|
|
if (context->suffixes && context->suffixes != no_suffixes)
|
|
GETDNS_FREE(context->mf, (void *)context->suffixes);
|
|
|
|
context->suffixes = no_suffixes;
|
|
context->suffixes_len = sizeof(no_suffixes);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
gldns_buffer_init_vfixed_frm_data(&gbuf, buf_spc, sizeof(buf_spc));
|
|
for (;;) {
|
|
for ( i = 0
|
|
; !(r = getdns_list_get_bindata(value, i, &bindata))
|
|
; i++) {
|
|
|
|
if (bindata->size == 0 || bindata->size >= sizeof(name_spc))
|
|
continue;
|
|
|
|
if (bindata->data[bindata->size-1] != 0) {
|
|
/* Unterminated string */
|
|
(void) memcpy(name_spc, bindata->data, bindata->size);
|
|
name_spc[bindata->size] = 0;
|
|
name = name_spc;
|
|
} else
|
|
/* Terminated string */
|
|
name = (char *)bindata->data;
|
|
|
|
dname_len = sizeof(dname);
|
|
if (gldns_str2wire_dname_buf(name, dname, &dname_len))
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
gldns_buffer_write_u8(&gbuf, (uint8_t) dname_len);
|
|
gldns_buffer_write(&gbuf, dname, dname_len);
|
|
}
|
|
if (r == GETDNS_RETURN_NO_SUCH_LIST_ITEM)
|
|
r = GETDNS_RETURN_GOOD;
|
|
else
|
|
break;
|
|
|
|
gldns_buffer_write_u8(&gbuf, 1);
|
|
gldns_buffer_write_u8(&gbuf, 0);
|
|
|
|
if (gldns_buffer_begin(&gbuf) != buf_spc)
|
|
break;
|
|
|
|
suffixes_len = gldns_buffer_position(&gbuf);
|
|
if (!(suffixes = GETDNS_XMALLOC(
|
|
context->mf, uint8_t, suffixes_len))) {
|
|
r = GETDNS_RETURN_MEMORY_ERROR;
|
|
break;
|
|
}
|
|
if (suffixes_len <= gldns_buffer_limit(&gbuf)) {
|
|
(void) memcpy (suffixes, buf_spc, suffixes_len);
|
|
break;
|
|
}
|
|
gldns_buffer_init_frm_data(&gbuf, suffixes, suffixes_len);
|
|
}
|
|
if (r) {
|
|
if (gldns_buffer_begin(&gbuf) != buf_spc)
|
|
GETDNS_FREE(context->mf, suffixes);
|
|
return r;
|
|
}
|
|
if (context->suffixes && context->suffixes != no_suffixes)
|
|
GETDNS_FREE(context->mf, (void *)context->suffixes);
|
|
|
|
context->suffixes = suffixes;
|
|
context->suffixes_len = suffixes_len;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_SUFFIX);
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_suffix */
|
|
|
|
/*
|
|
* getdns_context_set_dnssec_trust_anchors
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_dnssec_trust_anchors(
|
|
getdns_context *context, getdns_list *value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
|
|
if (context->trust_anchors &&
|
|
context->trust_anchors != context->trust_anchors_spc)
|
|
GETDNS_FREE(context->mf, context->trust_anchors);
|
|
|
|
if (value) {
|
|
context->trust_anchors_len = sizeof(context->trust_anchors_spc);
|
|
context->trust_anchors = _getdns_list2wire(value,
|
|
context->trust_anchors_spc, &context->trust_anchors_len,
|
|
&context->mf);
|
|
context->trust_anchors_source = GETDNS_TASRC_APP;
|
|
} else {
|
|
context->trust_anchors = NULL;
|
|
context->trust_anchors_len = 0;
|
|
context->trust_anchors_source = GETDNS_TASRC_NONE;
|
|
}
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_DNSSEC_TRUST_ANCHORS);
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_dnssec_trust_anchors */
|
|
|
|
#ifdef HAVE_LIBUNBOUND
|
|
static void
|
|
set_ub_dnssec_allowed_skew(struct getdns_context* context, uint32_t value) {
|
|
set_ub_number_opt(context, "val-sig-skew-min:", value);
|
|
set_ub_number_opt(context, "val-sig-skew-max:", value);
|
|
}
|
|
#endif
|
|
/*
|
|
* getdns_context_set_dnssec_allowed_skew
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_dnssec_allowed_skew(struct getdns_context *context,
|
|
uint32_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
#ifdef HAVE_LIBUNBOUND
|
|
set_ub_dnssec_allowed_skew(context, value);
|
|
#endif
|
|
if (value != context->dnssec_allowed_skew) {
|
|
context->dnssec_allowed_skew = value;
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_DNSSEC_ALLOWED_SKEW);
|
|
}
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_dnssec_allowed_skew */
|
|
|
|
/*
|
|
* getdns_context_set_upstream_recursive_servers
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
|
|
struct getdns_list *upstream_list)
|
|
{
|
|
getdns_return_t r;
|
|
size_t count = 0;
|
|
size_t i;
|
|
getdns_upstreams *upstreams;
|
|
char addrstr[1024], portstr[1024], *eos;
|
|
struct addrinfo hints;
|
|
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
|
|
if ( !upstream_list
|
|
|| (r = getdns_list_get_length(upstream_list, &count))
|
|
|| count == 0) {
|
|
_getdns_upstreams_dereference(context->upstreams);
|
|
context->upstreams = NULL;
|
|
dispatch_updated(context,
|
|
GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS);
|
|
}
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
|
hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
|
hints.ai_socktype = 0; /* Datagram socket */
|
|
hints.ai_flags = AI_NUMERICHOST; /* No reverse name lookups */
|
|
hints.ai_protocol = 0; /* Any protocol */
|
|
hints.ai_canonname = NULL;
|
|
hints.ai_addr = NULL;
|
|
hints.ai_next = NULL;
|
|
|
|
upstreams = upstreams_create(
|
|
context, count * GETDNS_UPSTREAM_TRANSPORTS);
|
|
for (i = 0; i < count; i++) {
|
|
getdns_dict *dict;
|
|
getdns_bindata *address_type;
|
|
getdns_bindata *address_data;
|
|
getdns_bindata *tls_auth_name;
|
|
struct sockaddr_storage addr;
|
|
|
|
getdns_bindata *scope_id;
|
|
getdns_upstream *upstream;
|
|
|
|
getdns_bindata *tsig_alg_name, *tsig_name, *tsig_key;
|
|
getdns_tsig_algo tsig_alg;
|
|
char tsig_name_str[1024];
|
|
uint8_t tsig_dname_spc[256], *tsig_dname;
|
|
size_t tsig_dname_len;
|
|
|
|
size_t j;
|
|
|
|
if ((r = getdns_list_get_dict(upstream_list, i, &dict))) {
|
|
dict = NULL;
|
|
|
|
if ((r = getdns_list_get_bindata(
|
|
upstream_list, i, &address_data)))
|
|
goto error;
|
|
|
|
if (address_data->size == 4)
|
|
addr.ss_family = AF_INET;
|
|
else if (address_data->size == 16)
|
|
addr.ss_family = AF_INET6;
|
|
else goto invalid_parameter;
|
|
|
|
} else if ((r = getdns_dict_get_bindata(
|
|
dict, "address_type",&address_type))) {
|
|
/* Just address_data is also okay */
|
|
if ((r = getdns_dict_get_bindata(
|
|
dict, "address_data", &address_data)))
|
|
goto error;
|
|
|
|
if (address_data->size == 4)
|
|
addr.ss_family = AF_INET;
|
|
else if (address_data->size == 16)
|
|
addr.ss_family = AF_INET6;
|
|
else goto invalid_parameter;
|
|
|
|
} else {
|
|
if (address_type->size < 4)
|
|
goto invalid_parameter;
|
|
else if (!strncmp((char*)address_type->data,"IPv4",4))
|
|
addr.ss_family = AF_INET;
|
|
else if (!strncmp((char*)address_type->data,"IPv6",4))
|
|
addr.ss_family = AF_INET6;
|
|
else goto invalid_parameter;
|
|
|
|
if ((r = getdns_dict_get_bindata(
|
|
dict, "address_data", &address_data)))
|
|
goto error;
|
|
if ((addr.ss_family == AF_INET &&
|
|
address_data->size != 4) ||
|
|
(addr.ss_family == AF_INET6 &&
|
|
address_data->size != 16))
|
|
goto invalid_parameter;
|
|
}
|
|
if (inet_ntop(addr.ss_family, address_data->data,
|
|
addrstr, 1024) == NULL)
|
|
goto invalid_parameter;
|
|
|
|
if (dict && getdns_dict_get_bindata(dict,"scope_id",&scope_id)
|
|
== GETDNS_RETURN_GOOD) {
|
|
if (strlen(addrstr) + scope_id->size > 1022)
|
|
goto invalid_parameter;
|
|
eos = &addrstr[strlen(addrstr)];
|
|
*eos++ = '%';
|
|
(void) memcpy(eos, scope_id->data, scope_id->size);
|
|
eos[scope_id->size] = 0;
|
|
}
|
|
|
|
tsig_alg_name = tsig_name = tsig_key = NULL;
|
|
tsig_dname = NULL;
|
|
tsig_dname_len = 0;
|
|
|
|
if (dict && getdns_dict_get_bindata(dict,
|
|
"tsig_algorithm", &tsig_alg_name) == GETDNS_RETURN_GOOD)
|
|
tsig_alg = _getdns_get_tsig_algo(tsig_alg_name);
|
|
else
|
|
tsig_alg = GETDNS_HMAC_MD5;
|
|
|
|
if (!dict)
|
|
tsig_alg = GETDNS_NO_TSIG; /* No name, no TSIG */
|
|
|
|
else if (getdns_dict_get_bindata(
|
|
dict, "tsig_name", &tsig_name))
|
|
tsig_alg = GETDNS_NO_TSIG; /* No name, no TSIG */
|
|
|
|
else if (tsig_name->size == 0)
|
|
tsig_alg = GETDNS_NO_TSIG;
|
|
|
|
else if (tsig_name->data[tsig_name->size - 1] != 0) {
|
|
/* Unterminated string */
|
|
if (tsig_name->size >= sizeof(tsig_name_str) - 1)
|
|
tsig_alg = GETDNS_NO_TSIG;
|
|
else {
|
|
(void) memcpy(tsig_name_str, tsig_name->data
|
|
, tsig_name->size);
|
|
tsig_name_str[tsig_name->size] = 0;
|
|
|
|
tsig_dname_len = sizeof(tsig_dname_spc);
|
|
if (gldns_str2wire_dname_buf(tsig_name_str,
|
|
tsig_dname_spc, &tsig_dname_len))
|
|
tsig_alg = GETDNS_NO_TSIG;
|
|
else
|
|
tsig_dname = tsig_dname_spc;
|
|
}
|
|
} else if (!_getdns_bindata_is_dname(tsig_name)) {
|
|
/* Terminated string */
|
|
tsig_dname_len = sizeof(tsig_dname_spc);
|
|
if (gldns_str2wire_dname_buf(tsig_name_str,
|
|
tsig_dname_spc, &tsig_dname_len))
|
|
tsig_alg = GETDNS_NO_TSIG;
|
|
else
|
|
tsig_dname = tsig_dname_spc;
|
|
|
|
} else if (tsig_name->size > sizeof(tsig_dname_spc))
|
|
tsig_alg = GETDNS_NO_TSIG;
|
|
|
|
else {
|
|
/* fqdn */
|
|
tsig_dname = memcpy(tsig_dname_spc, tsig_name->data
|
|
, tsig_name->size);
|
|
tsig_dname_len = tsig_name->size;
|
|
}
|
|
if (dict && getdns_dict_get_bindata(
|
|
dict, "tsig_secret", &tsig_key))
|
|
tsig_alg = GETDNS_NO_TSIG; /* No key, no TSIG */
|
|
|
|
/* Don't check TSIG length contraints here.
|
|
* Let the upstream decide what is secure enough.
|
|
*/
|
|
|
|
/* 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)
|
|
continue;
|
|
|
|
if (getdns_upstream_transports[j] != GETDNS_TRANSPORT_TLS) {
|
|
if (dict)
|
|
(void) getdns_dict_get_int(dict, "port", &port);
|
|
} else {
|
|
if (dict)
|
|
(void) getdns_dict_get_int(dict, "tls_port", &port);
|
|
}
|
|
(void) snprintf(portstr, 1024, "%d", (int)port);
|
|
|
|
if (getaddrinfo(addrstr, portstr, &hints, &ai))
|
|
goto invalid_parameter;
|
|
if (!ai)
|
|
continue;
|
|
|
|
/* TODO[TLS]: Should probably check that the upstream doesn't
|
|
* 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;
|
|
upstream_init(upstream, upstreams, ai);
|
|
upstream->transport = getdns_upstream_transports[j];
|
|
if (dict && getdns_upstream_transports[j] == GETDNS_TRANSPORT_TLS) {
|
|
getdns_list *pubkey_pinset = NULL;
|
|
getdns_bindata *tls_cipher_list = NULL;
|
|
getdns_bindata *tls_curves_list = NULL;
|
|
|
|
if ((r = getdns_dict_get_bindata(
|
|
dict, "tls_auth_name", &tls_auth_name)) == GETDNS_RETURN_GOOD) {
|
|
|
|
if (tls_auth_name->size >= sizeof(upstream->tls_auth_name)) {
|
|
/* tls_auth_name's are
|
|
* domain names in presentation
|
|
* format and, taking escaping
|
|
* into account, should not
|
|
* be larger than 1024 bytes.
|
|
*/
|
|
freeaddrinfo(ai);
|
|
goto invalid_parameter;
|
|
}
|
|
memcpy(upstream->tls_auth_name,
|
|
(char *)tls_auth_name->data,
|
|
tls_auth_name->size);
|
|
upstream->tls_auth_name
|
|
[tls_auth_name->size] = '\0';
|
|
}
|
|
if ((r = getdns_dict_get_list(dict, "tls_pubkey_pinset",
|
|
&pubkey_pinset)) == GETDNS_RETURN_GOOD) {
|
|
/* TODO: what if the user supplies tls_pubkey_pinset with
|
|
* something other than a list? */
|
|
r = _getdns_get_pubkey_pinset_from_list(pubkey_pinset,
|
|
&(upstreams->mf),
|
|
&(upstream->tls_pubkey_pinset));
|
|
if (r != GETDNS_RETURN_GOOD) {
|
|
freeaddrinfo(ai);
|
|
goto invalid_parameter;
|
|
}
|
|
}
|
|
(void) getdns_dict_get_bindata(
|
|
dict, "tls_cipher_list", &tls_cipher_list);
|
|
upstream->tls_cipher_list = tls_cipher_list
|
|
? _getdns_strdup2(&upstreams->mf
|
|
, tls_cipher_list)
|
|
: NULL;
|
|
(void) getdns_dict_get_bindata(
|
|
dict, "tls_curves_list", &tls_curves_list);
|
|
if (tls_curves_list) {
|
|
#if HAVE_TLS_CONN_CURVES_LIST
|
|
upstream->tls_curves_list =
|
|
_getdns_strdup2(&upstreams->mf
|
|
, tls_curves_list);
|
|
#else
|
|
freeaddrinfo(ai);
|
|
goto not_implemented;
|
|
#endif
|
|
} else
|
|
upstream->tls_curves_list = NULL;
|
|
}
|
|
if ((upstream->tsig_alg = tsig_alg)) {
|
|
if (tsig_name) {
|
|
(void) memcpy(upstream->tsig_dname,
|
|
tsig_dname, tsig_dname_len);
|
|
upstream->tsig_dname_len =
|
|
tsig_dname_len;
|
|
} else
|
|
upstream->tsig_dname_len = 0;
|
|
|
|
if (tsig_key) {
|
|
(void) memcpy(upstream->tsig_key,
|
|
tsig_key->data, tsig_key->size);
|
|
upstream->tsig_size = tsig_key->size;
|
|
} else
|
|
upstream->tsig_size = 0;
|
|
} else {
|
|
upstream->tsig_dname_len = 0;
|
|
upstream->tsig_size = 0;
|
|
}
|
|
upstreams->count++;
|
|
freeaddrinfo(ai);
|
|
}
|
|
}
|
|
_getdns_upstreams_dereference(context->upstreams);
|
|
context->upstreams = upstreams;
|
|
dispatch_updated(context,
|
|
GETDNS_CONTEXT_CODE_UPSTREAM_RECURSIVE_SERVERS);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
|
|
invalid_parameter:
|
|
_getdns_upstreams_dereference(upstreams);
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
error:
|
|
_getdns_upstreams_dereference(upstreams);
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
#if !HAVE_TLS_CONN_CURVES_LIST
|
|
not_implemented:
|
|
_getdns_upstreams_dereference(upstreams);
|
|
return GETDNS_RETURN_NOT_IMPLEMENTED;
|
|
#endif
|
|
} /* getdns_context_set_upstream_recursive_servers */
|
|
|
|
|
|
/*
|
|
* getdns_context_unset_edns_maximum_udp_payload_size
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_unset_edns_maximum_udp_payload_size(getdns_context *context)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
#ifdef HAVE_LIBUNBOUND
|
|
set_ub_number_opt(context, "edns-buffer-size:", 4096);
|
|
#endif
|
|
if (context->edns_maximum_udp_payload_size != -1) {
|
|
context->edns_maximum_udp_payload_size = -1;
|
|
dispatch_updated(context,
|
|
GETDNS_CONTEXT_CODE_EDNS_MAXIMUM_UDP_PAYLOAD_SIZE);
|
|
}
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_edns_maximum_udp_payload_size */
|
|
|
|
/*
|
|
* getdns_context_set_edns_maximum_udp_payload_size
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_edns_maximum_udp_payload_size(struct getdns_context *context,
|
|
uint16_t value)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
#ifdef HAVE_LIBUNBOUND
|
|
set_ub_number_opt(context, "edns-buffer-size:", value);
|
|
#endif
|
|
if (value != context->edns_maximum_udp_payload_size) {
|
|
context->edns_maximum_udp_payload_size = value;
|
|
dispatch_updated(context,
|
|
GETDNS_CONTEXT_CODE_EDNS_MAXIMUM_UDP_PAYLOAD_SIZE);
|
|
}
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_edns_maximum_udp_payload_size */
|
|
|
|
/*
|
|
* getdns_context_set_edns_extended_rcode
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_edns_extended_rcode(struct getdns_context *context, uint8_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
context->edns_extended_rcode = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_EDNS_EXTENDED_RCODE);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_edns_extended_rcode */
|
|
|
|
/*
|
|
* getdns_context_set_edns_version
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_edns_version(struct getdns_context *context, uint8_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
context->edns_version = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_EDNS_VERSION);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_edns_version */
|
|
|
|
/*
|
|
* getdns_context_set_edns_do_bit
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_edns_do_bit(struct getdns_context *context, uint8_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
/* only allow 1 */
|
|
if (value != 0 && value != 1) {
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
|
|
context->edns_do_bit = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_EDNS_DO_BIT);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_edns_do_bit */
|
|
|
|
/*
|
|
* getdns_context_set_edns_client_subnet_private
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_edns_client_subnet_private(struct getdns_context *context, uint8_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
/* only allow 1 */
|
|
if (value != 0 && value != 1) {
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
|
|
context->edns_client_subnet_private = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_edns_client_subnet_private */
|
|
|
|
/*
|
|
* getdns_context_set_tls_query_padding_blocksize
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_tls_query_padding_blocksize(struct getdns_context *context, uint16_t value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
/* only allow values between 0 and MAXIMUM_UPSTREAM_OPTION_SPACE - 4
|
|
(4 is for the overhead of the option itself) */
|
|
if (value > MAXIMUM_UPSTREAM_OPTION_SPACE - 4) {
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
|
|
context->tls_query_padding_blocksize = value;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_TLS_QUERY_PADDING_BLOCKSIZE);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_tls_query_padding_blocksize */
|
|
/*
|
|
* getdns_context_set_extended_memory_functions
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_extended_memory_functions(
|
|
struct getdns_context *context,
|
|
void *userarg,
|
|
void *(*malloc) (void *userarg, size_t),
|
|
void *(*realloc) (void *userarg, void *, size_t),
|
|
void (*free) (void *userarg, void *)
|
|
)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
if (!malloc || !realloc || !free)
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
|
|
context->mf.mf_arg = userarg;
|
|
context->mf.mf.ext.malloc = malloc;
|
|
context->mf.mf.ext.realloc = realloc;
|
|
context->mf.mf.ext.free = free;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_MEMORY_FUNCTIONS);
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_context_set_extended_memory_functions*/
|
|
|
|
|
|
/*
|
|
* getdns_context_set_memory_functions
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_set_memory_functions(struct getdns_context *context,
|
|
void *(*malloc) (size_t),
|
|
void *(*realloc) (void *, size_t),
|
|
void (*free) (void *)
|
|
)
|
|
{
|
|
mf_union mf;
|
|
mf.pln.malloc = malloc;
|
|
mf.pln.realloc = realloc;
|
|
mf.pln.free = free;
|
|
return getdns_context_set_extended_memory_functions(
|
|
context, MF_PLAIN, mf.ext.malloc, mf.ext.realloc, mf.ext.free);
|
|
} /* getdns_context_set_memory_functions*/
|
|
|
|
void
|
|
_getdns_context_track_outbound_request(getdns_dns_req *dnsreq)
|
|
{
|
|
/* Called only by getdns_general_ns() after successful allocation */
|
|
assert(dnsreq);
|
|
|
|
dnsreq->node.key = &(dnsreq->trans_id);
|
|
if (_getdns_rbtree_insert(
|
|
&dnsreq->context->outbound_requests, &dnsreq->node))
|
|
getdns_context_request_count_changed(dnsreq->context);
|
|
}
|
|
|
|
void
|
|
_getdns_context_clear_outbound_request(getdns_dns_req *dnsreq)
|
|
{
|
|
if (!dnsreq) return;
|
|
|
|
if (dnsreq->loop && dnsreq->loop->vmt && dnsreq->timeout.timeout_cb) {
|
|
dnsreq->loop->vmt->clear(dnsreq->loop, &dnsreq->timeout);
|
|
dnsreq->timeout.timeout_cb = NULL;
|
|
}
|
|
/* delete the node from the tree */
|
|
if (_getdns_rbtree_delete(
|
|
&dnsreq->context->outbound_requests, &dnsreq->trans_id))
|
|
getdns_context_request_count_changed(dnsreq->context);
|
|
|
|
if (dnsreq->chain)
|
|
_getdns_cancel_validation_chain(dnsreq);
|
|
}
|
|
|
|
void
|
|
_getdns_context_cancel_request(getdns_dns_req *dnsreq)
|
|
{
|
|
getdns_network_req *netreq, **netreq_p;
|
|
|
|
DEBUG_SCHED("%s(%p)\n", __FUNC__, (void *)dnsreq);
|
|
if (!dnsreq) return;
|
|
|
|
_getdns_context_clear_outbound_request(dnsreq);
|
|
|
|
/* cancel network requests */
|
|
for (netreq_p = dnsreq->netreqs; (netreq = *netreq_p); netreq_p++)
|
|
#ifdef HAVE_LIBUNBOUND
|
|
if (netreq->unbound_id != -1) {
|
|
ub_cancel(dnsreq->context->unbound_ctx,
|
|
netreq->unbound_id);
|
|
netreq->unbound_id = -1;
|
|
} else
|
|
#endif
|
|
_getdns_cancel_stub_request(netreq);
|
|
|
|
/* clean up */
|
|
_getdns_dns_req_free(dnsreq);
|
|
}
|
|
|
|
/*
|
|
* getdns_cancel_callback
|
|
*
|
|
*/
|
|
getdns_return_t
|
|
getdns_cancel_callback(getdns_context *context,
|
|
getdns_transaction_t transaction_id)
|
|
{
|
|
getdns_dns_req *dnsreq;
|
|
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
/* delete the node from the tree */
|
|
if (!(dnsreq = (getdns_dns_req *)_getdns_rbtree_delete(
|
|
&context->outbound_requests, &transaction_id)))
|
|
return GETDNS_RETURN_UNKNOWN_TRANSACTION;
|
|
|
|
getdns_context_request_count_changed(context);
|
|
|
|
debug_req("CB Cancel ", *dnsreq->netreqs);
|
|
if (dnsreq->user_callback) {
|
|
dnsreq->context->processing = 1;
|
|
dnsreq->user_callback(dnsreq->context, GETDNS_CALLBACK_CANCEL,
|
|
NULL, dnsreq->user_pointer, dnsreq->trans_id);
|
|
dnsreq->context->processing = 0;
|
|
}
|
|
if (!dnsreq->internal_cb) { /* Not part of chain */
|
|
debug_req("Destroy ", *dnsreq->netreqs);
|
|
_getdns_context_cancel_request(dnsreq);
|
|
}
|
|
return GETDNS_RETURN_GOOD;
|
|
} /* getdns_cancel_callback */
|
|
|
|
void
|
|
_getdns_context_request_timed_out(getdns_dns_req *dnsreq)
|
|
{
|
|
DEBUG_SCHED("%s(%p)\n", __FUNC__, (void *)dnsreq);
|
|
|
|
debug_req("CB Timeout ", *dnsreq->netreqs);
|
|
if (dnsreq->user_callback) {
|
|
dnsreq->context->processing = 1;
|
|
dnsreq->user_callback(dnsreq->context, GETDNS_CALLBACK_TIMEOUT,
|
|
_getdns_create_getdns_response(dnsreq),
|
|
dnsreq->user_pointer, dnsreq->trans_id);
|
|
dnsreq->context->processing = 0;
|
|
}
|
|
_getdns_context_cancel_request(dnsreq);
|
|
}
|
|
|
|
static void
|
|
accumulate_outstanding_transactions(_getdns_rbnode_t *node, void* arg)
|
|
{
|
|
*(*(getdns_transaction_t**)arg)++ = ((getdns_dns_req*)node)->trans_id;
|
|
}
|
|
|
|
static void
|
|
cancel_outstanding_requests(getdns_context* context)
|
|
{
|
|
getdns_transaction_t *trans_ids, *tids_a, *tids_i;
|
|
|
|
if (context->outbound_requests.count == 0)
|
|
return;
|
|
|
|
tids_i = tids_a = trans_ids = GETDNS_XMALLOC(context->my_mf,
|
|
getdns_transaction_t, context->outbound_requests.count);
|
|
|
|
_getdns_traverse_postorder(&context->outbound_requests,
|
|
accumulate_outstanding_transactions, &tids_a);
|
|
|
|
while (tids_i < tids_a) {
|
|
|
|
/* We have to cancel by transaction_id because we do not know
|
|
* what happens when the user_callback is called. It might
|
|
* delete getdns_dns_req's that were scheduled to be canceled.
|
|
* The extra lookup with transaction_id makes sure we do not
|
|
* access freed memory.
|
|
*/
|
|
(void) getdns_cancel_callback(context, *tids_i++);
|
|
}
|
|
GETDNS_FREE(context->my_mf, trans_ids);
|
|
}
|
|
|
|
#ifndef STUB_NATIVE_DNSSEC
|
|
|
|
static uint32_t *
|
|
upstream_scope_id(getdns_upstream *upstream)
|
|
{
|
|
return upstream->addr.ss_family == AF_INET ? NULL
|
|
: (upstream_addr(upstream)[0] == 0xFE &&
|
|
(upstream_addr(upstream)[1] & 0xC0) == 0x80 ?
|
|
&((struct sockaddr_in6*)&upstream->addr)->sin6_scope_id : NULL);
|
|
}
|
|
|
|
static void
|
|
upstream_ntop_buf(getdns_upstream *upstream, char *buf, size_t len)
|
|
{
|
|
/* Also possible but prints scope_id by name (nor parsed by unbound)
|
|
*
|
|
* getnameinfo((struct sockaddr *)&upstream->addr, upstream->addr_len,
|
|
* buf, len, NULL, 0, NI_NUMERICHOST)
|
|
*/
|
|
(void) inet_ntop(upstream->addr.ss_family, upstream_addr(upstream),
|
|
buf, len);
|
|
if (upstream_scope_id(upstream))
|
|
(void) snprintf(buf + strlen(buf), len - strlen(buf),
|
|
"%%%d", (int)*upstream_scope_id(upstream));
|
|
else if (upstream_port(upstream) != GETDNS_PORT_DNS && upstream_port(upstream) != GETDNS_PORT_ZERO)
|
|
(void) snprintf(buf + strlen(buf), len - strlen(buf),
|
|
"@%d", (int)upstream_port(upstream));
|
|
}
|
|
|
|
static getdns_return_t
|
|
ub_setup_stub(struct ub_ctx *ctx, getdns_context *context)
|
|
{
|
|
getdns_return_t r = GETDNS_RETURN_GOOD;
|
|
size_t i;
|
|
getdns_upstream *upstream;
|
|
char addr[1024];
|
|
getdns_upstreams *upstreams = context->upstreams;
|
|
|
|
(void) ub_ctx_set_fwd(ctx, NULL);
|
|
for (i = 0; i < upstreams->count; i++) {
|
|
upstream = &upstreams->upstreams[i];
|
|
/*[TLS]: Use only the TLS subset of upstreams when TLS is the only thing
|
|
* used. All other cases must currently fallback to TCP for libunbound.*/
|
|
if (context->dns_transports[0] == GETDNS_TRANSPORT_TLS &&
|
|
context->dns_transport_count ==1 &&
|
|
upstream->transport != GETDNS_TRANSPORT_TLS)
|
|
continue;
|
|
upstream_ntop_buf(upstream, addr, 1024);
|
|
ub_ctx_set_fwd(ctx, addr);
|
|
}
|
|
|
|
/* Allow lookups of:
|
|
*/
|
|
/* - localhost */
|
|
(void)ub_ctx_zone_remove(ctx, "localhost.");
|
|
|
|
/* - reverse IPv4 loopback */
|
|
(void)ub_ctx_zone_remove(ctx, "127.in-addr.arpa.");
|
|
|
|
/* - reverse IPv6 loopback */
|
|
(void)ub_ctx_zone_remove(ctx, "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0."
|
|
"0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.");
|
|
|
|
/* - reverse RFC1918 local use zones */
|
|
(void)ub_ctx_zone_remove(ctx, "10.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "16.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "17.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "18.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "19.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "20.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "21.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "22.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "23.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "24.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "25.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "26.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "27.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "28.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "29.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "30.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "31.172.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "168.192.in-addr.arpa.");
|
|
|
|
/* - reverse RFC3330 IP4 this, link-local, testnet and broadcast */
|
|
(void)ub_ctx_zone_remove(ctx, "0.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "254.169.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "2.0.192.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "100.51.198.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "113.0.203.in-addr.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "255.255.255.255.in-addr.arpa.");
|
|
|
|
/* - reverse RFC4291 IP6 unspecified */
|
|
(void)ub_ctx_zone_remove(ctx, "0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0."
|
|
"0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa.");
|
|
|
|
/* - reverse RFC4193 IPv6 Locally Assigned Local Addresses */
|
|
(void)ub_ctx_zone_remove(ctx, "D.F.ip6.arpa.");
|
|
|
|
/* - reverse RFC4291 IPv6 Link Local Addresses */
|
|
(void)ub_ctx_zone_remove(ctx, "8.E.F.ip6.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "9.E.F.ip6.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "A.E.F.ip6.arpa.");
|
|
(void)ub_ctx_zone_remove(ctx, "B.E.F.ip6.arpa.");
|
|
|
|
/* - reverse IPv6 Example Prefix */
|
|
(void)ub_ctx_zone_remove(ctx, "8.B.D.0.1.0.0.2.ip6.arpa.");
|
|
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
|
|
#ifdef HAVE_LIBUNBOUND
|
|
static getdns_return_t
|
|
ub_setup_recursing(struct ub_ctx *ctx, getdns_context *context)
|
|
{
|
|
_getdns_rr_iter rr_spc, *rr;
|
|
char ta_str[8192];
|
|
|
|
(void) ub_ctx_set_fwd(ctx, NULL);
|
|
if (!context->unbound_ta_set && context->trust_anchors) {
|
|
for ( rr = _getdns_rr_iter_init( &rr_spc
|
|
, context->trust_anchors
|
|
, context->trust_anchors_len)
|
|
; rr ; rr = _getdns_rr_iter_next(rr) ) {
|
|
|
|
(void) gldns_wire2str_rr_buf((UNCONST_UINT8_p)rr->pos,
|
|
rr->nxt - rr->pos, ta_str, sizeof(ta_str));
|
|
(void) ub_ctx_add_ta(ctx, ta_str);
|
|
}
|
|
context->unbound_ta_set = 1;
|
|
}
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
#endif
|
|
|
|
static getdns_return_t
|
|
_getdns_ns_dns_setup(struct getdns_context *context)
|
|
{
|
|
assert(context);
|
|
|
|
switch (context->resolution_type) {
|
|
case GETDNS_RESOLUTION_STUB:
|
|
if (!context->upstreams || !context->upstreams->count)
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
#ifdef STUB_NATIVE_DNSSEC
|
|
# ifdef DNSSEC_ROADBLOCK_AVOIDANCE
|
|
# ifdef HAVE_LIBUNBOUND
|
|
return ub_setup_recursing(context->unbound_ctx, context);
|
|
# else
|
|
/* Return NOT_IMPLEMENTED on query with an
|
|
* roadblock avoidance extension.
|
|
*/
|
|
return GETDNS_RETURN_GOOD;
|
|
# endif
|
|
# else
|
|
return GETDNS_RETURN_GOOD;
|
|
# endif
|
|
#else
|
|
return ub_setup_stub(context->unbound_ctx, context);
|
|
#endif
|
|
|
|
case GETDNS_RESOLUTION_RECURSING:
|
|
#ifdef HAVE_LIBUNBOUND
|
|
return ub_setup_recursing(context->unbound_ctx, context);
|
|
#else
|
|
return GETDNS_RETURN_NOT_IMPLEMENTED;
|
|
#endif
|
|
}
|
|
return GETDNS_RETURN_BAD_CONTEXT;
|
|
}
|
|
|
|
getdns_return_t
|
|
_getdns_context_prepare_for_resolution(getdns_context *context)
|
|
{
|
|
getdns_return_t r;
|
|
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
if (context->destroying)
|
|
return GETDNS_RETURN_BAD_CONTEXT;
|
|
|
|
/* Transport can in theory be set per query in stub mode */
|
|
if (context->resolution_type == GETDNS_RESOLUTION_STUB &&
|
|
tls_is_in_transports_list(context) == 1) {
|
|
/* Check minimum require authentication level*/
|
|
if (tls_only_is_in_transports_list(context) == 1 &&
|
|
context->tls_auth == GETDNS_AUTHENTICATION_REQUIRED) {
|
|
context->tls_auth_min = GETDNS_AUTHENTICATION_REQUIRED;
|
|
/* TODO: If no auth data provided for any upstream, fail here */
|
|
}
|
|
else {
|
|
context->tls_auth_min = GETDNS_AUTHENTICATION_NONE;
|
|
}
|
|
|
|
if (context->tls_ctx == NULL) {
|
|
context->tls_ctx = _getdns_tls_context_new(&context->my_mf);
|
|
if (context->tls_ctx == NULL)
|
|
return GETDNS_RETURN_BAD_CONTEXT;
|
|
|
|
r = _getdns_tls_context_set_min_proto_1_2(context->tls_ctx);
|
|
if (r && r != GETDNS_RETURN_NOT_IMPLEMENTED) {
|
|
_getdns_tls_context_free(&context->my_mf, context->tls_ctx);
|
|
context->tls_ctx = NULL;
|
|
return GETDNS_RETURN_BAD_CONTEXT;
|
|
}
|
|
/* Be strict and only use the cipher suites recommended in RFC7525
|
|
Unless we later fallback to opportunistic. */
|
|
if (_getdns_tls_context_set_cipher_list(context->tls_ctx, context->tls_cipher_list))
|
|
return GETDNS_RETURN_BAD_CONTEXT;
|
|
|
|
if (context->tls_curves_list &&
|
|
_getdns_tls_context_set_curves_list(context->tls_ctx, context->tls_curves_list))
|
|
return GETDNS_RETURN_BAD_CONTEXT;
|
|
|
|
|
|
/* For strict authentication, we must have local root certs available
|
|
Set up is done only when the tls_ctx is created (per getdns_context)*/
|
|
if (_getdns_tls_context_set_ca(context->tls_ctx, context->tls_ca_file, context->tls_ca_path)) {
|
|
if (context->tls_auth_min == GETDNS_AUTHENTICATION_REQUIRED)
|
|
return GETDNS_RETURN_BAD_CONTEXT;
|
|
}
|
|
_getdns_tls_context_pinset_init(context->tls_ctx);
|
|
}
|
|
}
|
|
|
|
/* Block use of TLS ONLY in recursive mode as it won't work */
|
|
/* Note: If TLS is used in recursive mode this will try TLS on port
|
|
* 53 so it is blocked here. */
|
|
if (context->resolution_type == GETDNS_RESOLUTION_RECURSING &&
|
|
tls_only_is_in_transports_list(context) == 1)
|
|
return GETDNS_RETURN_BAD_CONTEXT;
|
|
|
|
if (context->resolution_type_set == context->resolution_type)
|
|
/* already set and no config changes
|
|
* have caused this to be bad.
|
|
*/
|
|
return GETDNS_RETURN_GOOD;
|
|
|
|
/* TODO: respect namespace order (unbound always uses local first if cfg
|
|
* the spec calls for us to treat the namespace list as ordered
|
|
* so we need to respect that order
|
|
*/
|
|
|
|
|
|
r = _getdns_ns_dns_setup(context);
|
|
if (r == GETDNS_RETURN_GOOD)
|
|
context->resolution_type_set = context->resolution_type;
|
|
return r;
|
|
} /* _getdns_context_prepare_for_resolution */
|
|
|
|
static char *
|
|
_getdns_strdup(const struct mem_funcs *mfs, const char *s)
|
|
{
|
|
size_t sz;
|
|
char *r;
|
|
if (!s || !(r = GETDNS_XMALLOC(*mfs, char, (sz = strlen(s) + 1))))
|
|
return NULL;
|
|
else
|
|
return memcpy(r, s, sz);
|
|
}
|
|
|
|
static uint8_t _getdns_bindata_nodata[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
struct getdns_bindata *
|
|
_getdns_bindata_copy(struct mem_funcs *mfs, size_t size, const uint8_t *data)
|
|
{
|
|
/* Don't know why, but nodata allows
|
|
* empty bindatas with the python bindings
|
|
*/
|
|
struct getdns_bindata *dst;
|
|
|
|
if (!(dst = GETDNS_MALLOC(*mfs, struct getdns_bindata)))
|
|
return NULL;
|
|
|
|
if ((dst->size = size)) {
|
|
dst->data = GETDNS_XMALLOC(*mfs, uint8_t, size);
|
|
if (!dst->data) {
|
|
GETDNS_FREE(*mfs, dst);
|
|
return NULL;
|
|
}
|
|
(void) memcpy(dst->data, data, size);
|
|
} else {
|
|
dst->data = _getdns_bindata_nodata;
|
|
}
|
|
return dst;
|
|
}
|
|
|
|
void
|
|
_getdns_bindata_destroy(struct mem_funcs *mfs,
|
|
struct getdns_bindata *bindata)
|
|
{
|
|
if (!bindata)
|
|
return;
|
|
|
|
if (bindata->data && bindata->data != _getdns_bindata_nodata)
|
|
GETDNS_FREE(*mfs, bindata->data);
|
|
GETDNS_FREE(*mfs, bindata);
|
|
}
|
|
|
|
/* TODO: Remove next_timeout argument from getdns_context_get_num_pending_requests
|
|
*/
|
|
uint32_t
|
|
getdns_context_get_num_pending_requests(getdns_context* context,
|
|
struct timeval* next_timeout)
|
|
{
|
|
(void)next_timeout;
|
|
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (context->outbound_requests.count)
|
|
context->extension->vmt->run_once(context->extension, 0);
|
|
|
|
return context->outbound_requests.count;
|
|
}
|
|
|
|
/* process async reqs */
|
|
getdns_return_t
|
|
getdns_context_process_async(getdns_context *context)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
context->extension->vmt->run_once(context->extension, 0);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
void
|
|
getdns_context_run(getdns_context *context)
|
|
{
|
|
context->extension->vmt->run(context->extension);
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_detach_eventloop(struct getdns_context* context)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
|
|
/* When called from within a callback, do not execute pending
|
|
* context destroys.
|
|
* The (other) callback handler will handle it.
|
|
*
|
|
* ( because callbacks occur in cancel_outstanding_requests,
|
|
* and they may destroy the context )
|
|
*/
|
|
/* cancel all outstanding requests */
|
|
cancel_outstanding_requests(context);
|
|
context->extension->vmt->cleanup(context->extension);
|
|
context->extension = &context->default_eventloop.loop;
|
|
_getdns_default_eventloop_init(&context->mf, &context->default_eventloop);
|
|
#ifdef HAVE_UNBOUND_EVENT_API
|
|
if (_getdns_ub_loop_enabled(&context->ub_loop))
|
|
context->ub_loop.extension = context->extension;
|
|
#endif
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_eventloop(getdns_context* context, getdns_eventloop* loop)
|
|
{
|
|
if (!context || !loop)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (context->extension) {
|
|
cancel_outstanding_requests(context);
|
|
context->extension->vmt->cleanup(context->extension);
|
|
}
|
|
context->extension = loop;
|
|
#ifdef HAVE_UNBOUND_EVENT_API
|
|
if (_getdns_ub_loop_enabled(&context->ub_loop))
|
|
context->ub_loop.extension = loop;
|
|
#endif
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_eventloop(getdns_context *context, getdns_eventloop **loop)
|
|
{
|
|
if (!context || !loop)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (!context->extension)
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
else
|
|
*loop = context->extension;
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
static size_t _getdns_get_appdata(getdns_context *context, char *path);
|
|
static getdns_dict*
|
|
_get_context_settings(getdns_context* context)
|
|
{
|
|
getdns_dict *result = getdns_dict_create_with_context(context);
|
|
getdns_list *list;
|
|
size_t i;
|
|
const char *str_value;
|
|
char appdata_dir[_GETDNS_PATH_MAX] = "";
|
|
|
|
if (!result)
|
|
return NULL;
|
|
|
|
/* int fields */
|
|
/* the timeouts are stored as uint64, but the value maximum used in
|
|
practice is 6553500ms, so we just trim the value to be on the safe side. */
|
|
if ( getdns_dict_set_int(result, "timeout",
|
|
(context->timeout > 0xFFFFFFFFull) ? 0xFFFFFFFF: (uint32_t) context->timeout)
|
|
|| getdns_dict_set_int(result, "idle_timeout",
|
|
(context->idle_timeout > 0xFFFFFFFFull) ? 0xFFFFFFFF : (uint32_t) context->idle_timeout)
|
|
|| getdns_dict_set_int(result, "limit_outstanding_queries",
|
|
context->limit_outstanding_queries)
|
|
|| getdns_dict_set_int(result, "dnssec_allowed_skew",
|
|
context->dnssec_allowed_skew)
|
|
|| getdns_dict_set_int(result, "follow_redirects",
|
|
context->follow_redirects)
|
|
|| ( context->edns_maximum_udp_payload_size != -1
|
|
&& getdns_dict_set_int(result, "edns_maximum_udp_payload_size",
|
|
context->edns_maximum_udp_payload_size))
|
|
|| getdns_dict_set_int(result, "edns_client_subnet_private",
|
|
context->edns_client_subnet_private)
|
|
|| getdns_dict_set_int(result, "edns_extended_rcode",
|
|
context->edns_extended_rcode)
|
|
|| getdns_dict_set_int(result, "edns_version",
|
|
context->edns_version)
|
|
|| getdns_dict_set_int(result, "edns_do_bit",
|
|
context->edns_do_bit)
|
|
|| getdns_dict_set_int(result, "append_name",
|
|
context->append_name)
|
|
|| getdns_dict_set_int(result, "tls_authentication",
|
|
context->tls_auth)
|
|
|| getdns_dict_set_int(result, "round_robin_upstreams",
|
|
context->round_robin_upstreams)
|
|
|| getdns_dict_set_int(result, "max_backoff_value",
|
|
context->max_backoff_value)
|
|
|| getdns_dict_set_int(result, "tls_backoff_time",
|
|
context->tls_backoff_time)
|
|
|| getdns_dict_set_int(result, "tls_connection_retries",
|
|
context->tls_connection_retries)
|
|
|| getdns_dict_set_int(result, "tls_query_padding_blocksize",
|
|
context->tls_query_padding_blocksize)
|
|
|| getdns_dict_set_int(result, "resolution_type",
|
|
context->resolution_type)
|
|
)
|
|
goto error;
|
|
|
|
/* list fields */
|
|
if (getdns_context_get_suffix(context, &list))
|
|
goto error;
|
|
|
|
if (_getdns_dict_set_this_list(result, "suffix", list)) {
|
|
getdns_list_destroy(list);
|
|
goto error;
|
|
}
|
|
if (getdns_context_get_upstream_recursive_servers(context, &list))
|
|
goto error;
|
|
|
|
if (_getdns_dict_set_this_list(
|
|
result, "upstream_recursive_servers", list)) {
|
|
getdns_list_destroy(list);
|
|
goto error;
|
|
}
|
|
if (getdns_context_get_dnssec_trust_anchors(context, &list))
|
|
; /* pass */
|
|
|
|
else if (list && _getdns_dict_set_this_list(
|
|
result, "dnssec_trust_anchors", list)) {
|
|
getdns_list_destroy(list);
|
|
goto error;
|
|
}
|
|
if (context->dns_transport_count > 0) {
|
|
/* create a namespace list */
|
|
if (!(list = getdns_list_create_with_context(context)))
|
|
goto error;
|
|
|
|
for (i = 0; i < context->dns_transport_count; ++i) {
|
|
if (getdns_list_set_int(list, i,
|
|
context->dns_transports[i])) {
|
|
getdns_list_destroy(list);
|
|
goto error;
|
|
}
|
|
}
|
|
if (_getdns_dict_set_this_list(
|
|
result, "dns_transport_list", list)) {
|
|
getdns_list_destroy(list);
|
|
goto error;
|
|
}
|
|
}
|
|
if (context->namespace_count > 0) {
|
|
/* create a namespace list */
|
|
if (!(list = getdns_list_create_with_context(context)))
|
|
goto error;
|
|
|
|
for (i = 0; i < context->namespace_count; ++i) {
|
|
if (getdns_list_set_int(list, i,
|
|
context->namespaces[i])) {
|
|
getdns_list_destroy(list);
|
|
goto error;
|
|
}
|
|
}
|
|
if (_getdns_dict_set_this_list(result, "namespaces", list)) {
|
|
getdns_list_destroy(list);
|
|
return NULL;
|
|
}
|
|
}
|
|
(void) _getdns_get_appdata(context, appdata_dir);
|
|
(void) getdns_dict_util_set_string(result, "appdata_dir", appdata_dir);
|
|
if (!getdns_context_get_trust_anchors_url(context, &str_value) && str_value)
|
|
(void) getdns_dict_util_set_string(result, "trust_anchors_url", str_value);
|
|
if (!getdns_context_get_trust_anchors_verify_CA(context, &str_value) && str_value)
|
|
(void) getdns_dict_util_set_string(result, "trust_anchors_verify_CA", str_value);
|
|
if (!getdns_context_get_trust_anchors_verify_email(context, &str_value) && str_value)
|
|
(void) getdns_dict_util_set_string(result, "trust_anchors_verify_email", str_value);
|
|
if (!getdns_context_get_resolvconf(context, &str_value) && str_value)
|
|
(void) getdns_dict_util_set_string(result, "resolvconf", str_value);
|
|
if (!getdns_context_get_hosts(context, &str_value) && str_value)
|
|
(void) getdns_dict_util_set_string(result, "hosts", str_value);
|
|
if (!getdns_context_get_tls_ca_path(context, &str_value) && str_value)
|
|
(void) getdns_dict_util_set_string(result, "tls_ca_path", str_value);
|
|
if (!getdns_context_get_tls_ca_file(context, &str_value) && str_value)
|
|
(void) getdns_dict_util_set_string(result, "tls_ca_file", str_value);
|
|
if (!getdns_context_get_tls_cipher_list(context, &str_value) && str_value)
|
|
(void) getdns_dict_util_set_string(result, "tls_cipher_list", str_value);
|
|
if (!getdns_context_get_tls_curves_list(context, &str_value) && str_value)
|
|
(void) getdns_dict_util_set_string(result, "tls_curves_list", str_value);
|
|
|
|
/* Default settings for extensions */
|
|
(void)getdns_dict_set_int(
|
|
result, "add_warning_for_bad_dns",
|
|
context->add_warning_for_bad_dns ? GETDNS_EXTENSION_TRUE
|
|
: GETDNS_EXTENSION_FALSE);
|
|
(void)getdns_dict_set_int(
|
|
result, "dnssec_return_all_statuses",
|
|
context->dnssec_return_all_statuses ? GETDNS_EXTENSION_TRUE
|
|
: GETDNS_EXTENSION_FALSE);
|
|
(void)getdns_dict_set_int(
|
|
result, "dnssec_return_full_validation_chain",
|
|
context->dnssec_return_full_validation_chain ? GETDNS_EXTENSION_TRUE
|
|
: GETDNS_EXTENSION_FALSE);
|
|
(void)getdns_dict_set_int(
|
|
result, "dnssec_return_only_secure",
|
|
context->dnssec_return_only_secure ? GETDNS_EXTENSION_TRUE
|
|
: GETDNS_EXTENSION_FALSE);
|
|
(void)getdns_dict_set_int(
|
|
result, "dnssec_return_status",
|
|
context->dnssec_return_status ? GETDNS_EXTENSION_TRUE
|
|
: GETDNS_EXTENSION_FALSE);
|
|
(void)getdns_dict_set_int(
|
|
result, "dnssec_return_validation_chain",
|
|
context->dnssec_return_validation_chain ? GETDNS_EXTENSION_TRUE
|
|
: GETDNS_EXTENSION_FALSE);
|
|
|
|
#if defined(DNSSEC_ROADBLOCK_AVOIDANCE) && defined(HAVE_LIBUNBOUND)
|
|
(void)getdns_dict_set_int(
|
|
result, "dnssec_roadblock_avoidance",
|
|
context->dnssec_roadblock_avoidance ? GETDNS_EXTENSION_TRUE
|
|
: GETDNS_EXTENSION_FALSE);
|
|
#endif
|
|
#ifdef EDNS_COOKIES
|
|
(void)getdns_dict_set_int(
|
|
result, "edns_cookies",
|
|
context->edns_cookies ? GETDNS_EXTENSION_TRUE
|
|
: GETDNS_EXTENSION_FALSE);
|
|
#endif
|
|
(void)getdns_dict_set_int(
|
|
result, "return_both_v4_and_v6",
|
|
context->return_both_v4_and_v6 ? GETDNS_EXTENSION_TRUE
|
|
: GETDNS_EXTENSION_FALSE);
|
|
(void)getdns_dict_set_int(
|
|
result, "return_call_reporting",
|
|
context->return_call_reporting ? GETDNS_EXTENSION_TRUE
|
|
: GETDNS_EXTENSION_FALSE);
|
|
(void)getdns_dict_set_int(result, "specify_class",
|
|
(uint32_t)context->specify_class);
|
|
|
|
if (context->add_opt_parameters)
|
|
(void)getdns_dict_set_dict(
|
|
result, "add_opt_parameters", context->add_opt_parameters);
|
|
|
|
if (context->header)
|
|
(void)getdns_dict_set_dict(
|
|
result, "header", context->add_opt_parameters);
|
|
|
|
return result;
|
|
error:
|
|
getdns_dict_destroy(result);
|
|
return NULL;
|
|
}
|
|
|
|
getdns_dict*
|
|
getdns_context_get_api_information(getdns_context* context)
|
|
{
|
|
getdns_dict* result;
|
|
getdns_dict* settings;
|
|
|
|
if ((result = getdns_dict_create_with_context(context))
|
|
|
|
&& ! getdns_dict_util_set_string(
|
|
result, "version_string", GETDNS_VERSION)
|
|
|
|
&& ! getdns_dict_set_int(
|
|
result, "version_number", getdns_get_version_number())
|
|
|
|
&& ! getdns_dict_util_set_string(
|
|
result, "api_version_string", getdns_get_api_version())
|
|
|
|
&& ! getdns_dict_set_int(
|
|
result, "api_version_number", getdns_get_api_version_number())
|
|
|
|
&& ! getdns_dict_util_set_string(
|
|
result, "implementation_string", PACKAGE_URL)
|
|
|
|
&& ! getdns_dict_util_set_string(
|
|
result, "compilation_comment", GETDNS_COMPILATION_COMMENT)
|
|
|
|
&& ! getdns_dict_util_set_string(
|
|
result, "default_trust_anchor_location", TRUST_ANCHOR_FILE)
|
|
|
|
&& ! getdns_dict_util_set_string(
|
|
result, "default_resolvconf_location", GETDNS_FN_RESOLVCONF)
|
|
|
|
&& ! getdns_dict_util_set_string(
|
|
result, "default_hosts_location", GETDNS_FN_HOSTS)
|
|
|
|
&& ! _getdns_tls_get_api_information(result)
|
|
|
|
&& ! getdns_dict_set_int(
|
|
result, "resolution_type", context->resolution_type)
|
|
|
|
&& (settings = _get_context_settings(context))) {
|
|
|
|
if (!_getdns_dict_set_this_dict(result,"all_context",settings))
|
|
return result;
|
|
|
|
getdns_dict_destroy(settings);
|
|
}
|
|
getdns_dict_destroy(result);
|
|
return NULL;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_return_dnssec_status(getdns_context* context, int enabled) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
if (enabled != GETDNS_EXTENSION_TRUE &&
|
|
enabled != GETDNS_EXTENSION_FALSE) {
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
}
|
|
context->dnssec_return_status = enabled == GETDNS_EXTENSION_TRUE;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_use_threads(getdns_context* context, int use_threads) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
int r = 0;
|
|
if (context->resolution_type_set != 0) {
|
|
/* already setup */
|
|
return GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
#ifdef HAVE_LIBUNBOUND
|
|
if (use_threads)
|
|
r = ub_ctx_async(context->unbound_ctx, 1);
|
|
else
|
|
r = ub_ctx_async(context->unbound_ctx, 0);
|
|
#else
|
|
(void)use_threads;
|
|
#endif
|
|
return r == 0 ? GETDNS_RETURN_GOOD : GETDNS_RETURN_CONTEXT_UPDATE_FAIL;
|
|
}
|
|
|
|
getdns_return_t
|
|
_getdns_context_local_namespace_resolve(
|
|
getdns_dns_req *dnsreq, getdns_dict **response)
|
|
{
|
|
getdns_context *context = dnsreq->context;
|
|
host_name_addrs *hnas;
|
|
uint8_t lookup[256];
|
|
getdns_list empty_list = { 0, 0, NULL, { NULL, {{ NULL, NULL, NULL }}}};
|
|
getdns_bindata bindata;
|
|
getdns_list *jaa;
|
|
size_t i;
|
|
getdns_dict *addr;
|
|
int ipv4 = dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A ||
|
|
(dnsreq->netreqs[1] &&
|
|
dnsreq->netreqs[1]->request_type == GETDNS_RRTYPE_A);
|
|
int ipv6 = dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_AAAA ||
|
|
(dnsreq->netreqs[1] &&
|
|
dnsreq->netreqs[1]->request_type == GETDNS_RRTYPE_AAAA);
|
|
|
|
if (!ipv4 && !ipv6)
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
/*Do the lookup*/
|
|
(void)memcpy(lookup, dnsreq->name, dnsreq->name_len);
|
|
canonicalize_dname(lookup);
|
|
|
|
if (!(hnas = (host_name_addrs *)
|
|
_getdns_rbtree_search(&context->local_hosts, lookup)))
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
if (!hnas->ipv4addrs && (!ipv6 || !hnas->ipv6addrs))
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
if (!hnas->ipv6addrs && (!ipv4 || !hnas->ipv4addrs))
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
if (!(*response = getdns_dict_create_with_context(context)))
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
bindata.size = dnsreq->name_len;
|
|
bindata.data = dnsreq->name;
|
|
if (getdns_dict_set_bindata(*response, "canonical_name", &bindata))
|
|
goto error;
|
|
|
|
empty_list.mf = context->mf;
|
|
if (getdns_dict_set_list(*response, "replies_full", &empty_list))
|
|
goto error;
|
|
|
|
if (getdns_dict_set_list(*response, "replies_tree", &empty_list))
|
|
goto error;
|
|
|
|
if (getdns_dict_set_int(*response, "status", GETDNS_RESPSTATUS_GOOD))
|
|
goto error;
|
|
|
|
if (!ipv4 || !hnas->ipv4addrs) {
|
|
if (getdns_dict_set_list(*response,
|
|
"just_address_answers", hnas->ipv6addrs))
|
|
goto error;
|
|
return GETDNS_RETURN_GOOD;
|
|
} else if (!ipv6 || !hnas->ipv6addrs) {
|
|
if (getdns_dict_set_list(*response,
|
|
"just_address_answers", hnas->ipv4addrs))
|
|
goto error;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
if (!(jaa = getdns_list_create_with_context(context)))
|
|
goto error;
|
|
|
|
for (i = 0; !getdns_list_get_dict(hnas->ipv4addrs, i, &addr); i++)
|
|
if (_getdns_list_append_dict(jaa, addr))
|
|
break;
|
|
for (i = 0; !getdns_list_get_dict(hnas->ipv6addrs, i, &addr); i++)
|
|
if (_getdns_list_append_dict(jaa, addr))
|
|
break;
|
|
if (!_getdns_dict_set_this_list(*response, "just_address_answers", jaa))
|
|
return GETDNS_RETURN_GOOD;
|
|
else
|
|
getdns_list_destroy(jaa);
|
|
error:
|
|
getdns_dict_destroy(*response);
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
}
|
|
|
|
struct mem_funcs *
|
|
priv_getdns_context_mf(getdns_context *context)
|
|
{
|
|
return &context->mf;
|
|
}
|
|
|
|
/** begin getters **/
|
|
getdns_return_t
|
|
getdns_context_get_resolution_type(getdns_context *context,
|
|
getdns_resolution_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->resolution_type;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_namespaces(getdns_context *context,
|
|
size_t* namespace_count, getdns_namespace_t **namespaces) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(namespace_count, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(namespaces, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*namespace_count = context->namespace_count;
|
|
if (!context->namespace_count) {
|
|
*namespaces = NULL;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
// use normal malloc here so users can do normal free
|
|
*namespaces = malloc(context->namespace_count * sizeof(getdns_namespace_t));
|
|
memcpy(*namespaces, context->namespaces,
|
|
context->namespace_count * sizeof(getdns_namespace_t));
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_dns_transport(getdns_context *context,
|
|
getdns_transport_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
int count = context->dns_transport_count;
|
|
getdns_transport_list_t *transports = context->dns_transports;
|
|
if (!count)
|
|
return GETDNS_RETURN_WRONG_TYPE_REQUESTED;
|
|
|
|
/* Best effort mapping for backwards compatibility*/
|
|
if (transports[0] == GETDNS_TRANSPORT_UDP) {
|
|
if (count == 1)
|
|
*value = GETDNS_TRANSPORT_UDP_ONLY;
|
|
else if (count == 2 && transports[1] == GETDNS_TRANSPORT_TCP)
|
|
*value = GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP;
|
|
else
|
|
return GETDNS_RETURN_WRONG_TYPE_REQUESTED;
|
|
}
|
|
if (transports[0] == GETDNS_TRANSPORT_TCP) {
|
|
if (count == 1)
|
|
*value = GETDNS_TRANSPORT_TCP_ONLY_KEEP_CONNECTIONS_OPEN;
|
|
}
|
|
if (transports[0] == GETDNS_TRANSPORT_TLS) {
|
|
if (count == 1)
|
|
*value = GETDNS_TRANSPORT_TLS_ONLY_KEEP_CONNECTIONS_OPEN;
|
|
else if (count == 2 && transports[1] == GETDNS_TRANSPORT_TCP)
|
|
*value = GETDNS_TRANSPORT_TLS_FIRST_AND_FALL_BACK_TO_TCP_KEEP_CONNECTIONS_OPEN;
|
|
else
|
|
return GETDNS_RETURN_WRONG_TYPE_REQUESTED;
|
|
}
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_dns_transport_list(getdns_context *context,
|
|
size_t* transport_count, getdns_transport_list_t **transports) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(transport_count, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(transports, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*transport_count = context->dns_transport_count;
|
|
if (!context->dns_transport_count) {
|
|
*transports = NULL;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
// use normal malloc here so users can do normal free
|
|
*transports = malloc(context->dns_transport_count * sizeof(getdns_transport_list_t));
|
|
memcpy(*transports, context->dns_transports,
|
|
context->dns_transport_count * sizeof(getdns_transport_list_t));
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_tls_authentication(getdns_context *context,
|
|
getdns_tls_authentication_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->tls_auth;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_round_robin_upstreams(getdns_context *context,
|
|
uint8_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->round_robin_upstreams;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
/**
|
|
* Get the maximum number of messages that can be sent to other upstreams
|
|
* before the upstream which has previously timed out will be tried again.
|
|
* @see getdns_context_set_max_backoff_value
|
|
* @param[in] context The context from which to get the setting
|
|
* @param[out] value Number of messages sent to other upstreams before
|
|
* retrying the upstream which had timed out.
|
|
* @return GETDNS_RETURN_GOOD on success
|
|
* @return GETDNS_RETURN_INVALID_PARAMETER if context is null.
|
|
*/
|
|
getdns_return_t
|
|
getdns_context_get_max_backoff_value(getdns_context *context,
|
|
uint16_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->max_backoff_value;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_tls_backoff_time(getdns_context *context,
|
|
uint16_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->tls_backoff_time;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_tls_connection_retries(getdns_context *context,
|
|
uint16_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->tls_connection_retries;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_limit_outstanding_queries(getdns_context *context,
|
|
uint16_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->limit_outstanding_queries;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_timeout(getdns_context *context, uint64_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->timeout;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_idle_timeout(getdns_context *context, uint64_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->idle_timeout;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_follow_redirects(
|
|
getdns_context *context, getdns_redirects_t* value)
|
|
{
|
|
if (!context || !value)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
*value = context->follow_redirects;
|
|
return GETDNS_RETURN_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_dns_root_servers(getdns_context *context,
|
|
getdns_list **value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = NULL;
|
|
if (context->dns_root_servers)
|
|
return _getdns_list_copy(context->dns_root_servers, value);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_append_name(getdns_context *context,
|
|
getdns_append_name_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->append_name;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_suffix(getdns_context *context, getdns_list **value)
|
|
{
|
|
size_t dname_len;
|
|
const uint8_t *dname;
|
|
char name[1024];
|
|
getdns_return_t r = GETDNS_RETURN_GOOD;
|
|
getdns_list *list;
|
|
|
|
if (!context || !value)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (!(list = getdns_list_create_with_context(context)))
|
|
return GETDNS_RETURN_MEMORY_ERROR;
|
|
|
|
assert(context->suffixes);
|
|
dname_len = context->suffixes[0];
|
|
dname = context->suffixes + 1;
|
|
while (dname_len && *dname) {
|
|
if (! gldns_wire2str_dname_buf((UNCONST_UINT8_p)
|
|
dname, dname_len, name, sizeof(name))) {
|
|
r = GETDNS_RETURN_GENERIC_ERROR;
|
|
break;
|
|
}
|
|
if ((r = _getdns_list_append_string(list, name)))
|
|
break;
|
|
dname += dname_len;
|
|
dname_len = *dname++;
|
|
}
|
|
if (r)
|
|
getdns_list_destroy(list);
|
|
else
|
|
*value = list;
|
|
|
|
return r;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_dnssec_trust_anchors(
|
|
getdns_context *context, getdns_list **value)
|
|
{
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
|
|
if (context->trust_anchors) {
|
|
if ((*value = getdns_list_create_with_context(context)))
|
|
_getdns_wire2list( context->trust_anchors
|
|
, context->trust_anchors_len
|
|
, *value);
|
|
else
|
|
return GETDNS_RETURN_MEMORY_ERROR;
|
|
} else
|
|
*value = NULL;
|
|
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_dnssec_allowed_skew(getdns_context *context,
|
|
uint32_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->dnssec_allowed_skew;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_upstream_recursive_servers(getdns_context *context,
|
|
getdns_list **upstreams_r)
|
|
{
|
|
size_t i;
|
|
getdns_list *upstreams;
|
|
getdns_return_t r;
|
|
|
|
if (!context || !upstreams_r)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (!(upstreams = getdns_list_create_with_context(context)))
|
|
return GETDNS_RETURN_MEMORY_ERROR;
|
|
|
|
if (!context->upstreams || context->upstreams->count == 0) {
|
|
*upstreams_r = upstreams;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
r = GETDNS_RETURN_GOOD;
|
|
i = 0;
|
|
while (!r && i < context->upstreams->count) {
|
|
size_t j;
|
|
getdns_dict *d;
|
|
getdns_upstream *upstream = &context->upstreams->upstreams[i];
|
|
getdns_bindata bindata;
|
|
const getdns_tsig_info *tsig_info;
|
|
|
|
if (!(d =
|
|
sockaddr_dict(context, (struct sockaddr*)&upstream->addr))) {
|
|
r = GETDNS_RETURN_MEMORY_ERROR;
|
|
break;
|
|
}
|
|
if (upstream->tsig_alg) {
|
|
tsig_info = _getdns_get_tsig_info(upstream->tsig_alg);
|
|
|
|
if ((r = _getdns_dict_set_const_bindata(
|
|
d, "tsig_algorithm",
|
|
tsig_info->dname_len, tsig_info->dname)))
|
|
break;
|
|
|
|
if (upstream->tsig_dname_len) {
|
|
bindata.data = upstream->tsig_dname;
|
|
bindata.size = upstream->tsig_dname_len;
|
|
if ((r = getdns_dict_set_bindata(
|
|
d, "tsig_name", &bindata)))
|
|
break;
|
|
}
|
|
if (upstream->tsig_size) {
|
|
bindata.data = upstream->tsig_key;
|
|
bindata.size = upstream->tsig_size;
|
|
if ((r = getdns_dict_set_bindata(
|
|
d, "tsig_secret", &bindata)))
|
|
break;
|
|
}
|
|
}
|
|
for ( j = 1, i++
|
|
; j < GETDNS_UPSTREAM_TRANSPORTS &&
|
|
i < context->upstreams->count
|
|
; j++, i++) {
|
|
|
|
upstream = &context->upstreams->upstreams[i];
|
|
|
|
if (upstream->transport == GETDNS_TRANSPORT_UDP &&
|
|
upstream_port(upstream) != getdns_port_array[j] &&
|
|
(r = getdns_dict_set_int(d, "port",
|
|
(uint32_t)upstream_port(upstream))))
|
|
break;
|
|
|
|
if (upstream->transport == GETDNS_TRANSPORT_TLS) {
|
|
if (upstream_port(upstream) != getdns_port_array[j] &&
|
|
(r = getdns_dict_set_int(d, "tls_port",
|
|
(uint32_t) upstream_port(upstream))))
|
|
break;
|
|
if (upstream->tls_auth_name[0] != '\0' &&
|
|
(r = getdns_dict_util_set_string(d,
|
|
"tls_auth_name",
|
|
upstream->tls_auth_name)))
|
|
break;
|
|
if (upstream->tls_pubkey_pinset) {
|
|
getdns_list *pins = NULL;
|
|
if ((_getdns_get_pubkey_pinset_list(context,
|
|
upstream->tls_pubkey_pinset,
|
|
&pins) == GETDNS_RETURN_GOOD) &&
|
|
(r = _getdns_dict_set_this_list(d, "tls_pubkey_pinset", pins))) {
|
|
getdns_list_destroy(pins);
|
|
break;
|
|
}
|
|
}
|
|
if (upstream->tls_cipher_list) {
|
|
(void) getdns_dict_util_set_string(
|
|
d, "tls_cipher_list",
|
|
upstream->tls_cipher_list);
|
|
}
|
|
if (upstream->tls_curves_list) {
|
|
(void) getdns_dict_util_set_string(
|
|
d, "tls_curves_list",
|
|
upstream->tls_curves_list);
|
|
}
|
|
}
|
|
}
|
|
if (!r)
|
|
if (!(r = _getdns_list_append_this_dict(upstreams, d)))
|
|
d = NULL;
|
|
getdns_dict_destroy(d);
|
|
}
|
|
if (r)
|
|
getdns_list_destroy(upstreams);
|
|
else
|
|
*upstreams_r = upstreams;
|
|
return r;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_edns_maximum_udp_payload_size(getdns_context *context,
|
|
uint16_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->edns_maximum_udp_payload_size == -1 ? 0
|
|
: context->edns_maximum_udp_payload_size;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_edns_extended_rcode(getdns_context *context,
|
|
uint8_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->edns_extended_rcode;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_edns_version(getdns_context *context, uint8_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->edns_version;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_edns_do_bit(getdns_context *context, uint8_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->edns_do_bit;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_edns_client_subnet_private(getdns_context *context, uint8_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->edns_client_subnet_private;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_tls_query_padding_blocksize(getdns_context *context, uint16_t* value) {
|
|
RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER);
|
|
RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER);
|
|
*value = context->tls_query_padding_blocksize;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
static int _streq(const getdns_bindata *name, const char *str)
|
|
{
|
|
if (strlen(str) != name->size)
|
|
return 0;
|
|
else return strncmp((const char *)name->data, str, name->size) == 0;
|
|
}
|
|
|
|
static getdns_return_t _get_list_or_read_file(const getdns_dict *config_dict,
|
|
const char *setting, getdns_list **r_list, int *destroy_list)
|
|
{
|
|
getdns_bindata *fn_bd;
|
|
char fn[FILENAME_MAX];
|
|
FILE *fh;
|
|
getdns_return_t r;
|
|
|
|
assert(r_list);
|
|
assert(destroy_list);
|
|
|
|
*destroy_list = 0;
|
|
if (!(r = getdns_dict_get_list(config_dict, setting, r_list)))
|
|
return GETDNS_RETURN_GOOD;
|
|
|
|
else if ((r = getdns_dict_get_bindata(config_dict, setting, &fn_bd)))
|
|
return r;
|
|
|
|
else if (fn_bd->size >= FILENAME_MAX)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
(void)memcpy(fn, fn_bd->data, fn_bd->size);
|
|
fn[fn_bd->size] = 0;
|
|
|
|
if (!(fh = fopen(fn, "r")))
|
|
return GETDNS_RETURN_GENERIC_ERROR;
|
|
|
|
if (!(r = getdns_fp2rr_list(fh, r_list, NULL, 3600)))
|
|
*destroy_list = 1;
|
|
|
|
fclose(fh);
|
|
return r;
|
|
}
|
|
|
|
#define CONTEXT_SETTING_INT(X) \
|
|
} else if (_streq(setting, #X)) { \
|
|
if (!(r = getdns_dict_get_int(config_dict, #X , &n))) \
|
|
r = getdns_context_set_ ## X (context, n);
|
|
|
|
#define CONTEXT_SETTING_LIST(X) \
|
|
} else if (_streq(setting, #X)) { \
|
|
if (!(r = getdns_dict_get_list(config_dict, #X , &list))) \
|
|
r = getdns_context_set_ ## X (context, list);
|
|
|
|
#define CONTEXT_SETTING_LIST_OR_ZONEFILE(X) \
|
|
} else if (_streq(setting, #X)) { \
|
|
if (!(r = _get_list_or_read_file( \
|
|
config_dict, #X , &list, &destroy_list))) \
|
|
r = getdns_context_set_ ## X(context, list); \
|
|
if (destroy_list) getdns_list_destroy(list);
|
|
|
|
#define CONTEXT_SETTING_ARRAY(X, T) \
|
|
} else if (_streq(setting, #X )) { \
|
|
if (!(r = getdns_dict_get_list(config_dict, #X , &list)) && \
|
|
!(r = getdns_list_get_length(list, &count))) { \
|
|
for (i=0; i<count && i<(sizeof(X)/sizeof(*X)); i++) { \
|
|
if ((r = getdns_list_get_int(list, i, &n))) \
|
|
break; \
|
|
X[i] = (getdns_ ## T ## _t)n; \
|
|
} \
|
|
r = getdns_context_set_ ##X (context, i, X); \
|
|
}
|
|
|
|
#define EXTENSION_SETTING_BOOL(X) \
|
|
} else if (_streq(setting, #X )) { \
|
|
if (!(r = getdns_dict_get_int(config_dict, #X , &n))) { \
|
|
if (n == GETDNS_EXTENSION_TRUE) context->X = 1; \
|
|
else if (n == GETDNS_EXTENSION_FALSE) context->X = 0; \
|
|
else r = GETDNS_RETURN_INVALID_PARAMETER; \
|
|
}
|
|
|
|
#define CONTEXT_SETTING_STRING(X) \
|
|
} else if (_streq(setting, #X )) { \
|
|
if (!(r = getdns_dict_get_bindata(config_dict, #X , &bd))) { \
|
|
if (bd->size < sizeof(str_buf)) { \
|
|
(void) memcpy(str_buf, (char *)bd->data, bd->size); \
|
|
str_buf[bd->size] = '\0'; \
|
|
r = getdns_context_set_ ## X( \
|
|
context, str_buf); \
|
|
} else if ((tmp_str = _getdns_strdup2(&context->mf, bd))) { \
|
|
r = getdns_context_set_ ## X( \
|
|
context, tmp_str); \
|
|
GETDNS_FREE(context->mf, tmp_str); \
|
|
} else \
|
|
r = GETDNS_RETURN_MEMORY_ERROR; \
|
|
}
|
|
|
|
static getdns_return_t
|
|
_getdns_context_config_setting(getdns_context *context,
|
|
const getdns_dict *config_dict, const getdns_bindata *setting)
|
|
{
|
|
getdns_return_t r = GETDNS_RETURN_GOOD;
|
|
getdns_dict *dict;
|
|
getdns_list *list;
|
|
getdns_namespace_t namespaces[100];
|
|
getdns_transport_list_t dns_transport_list[100];
|
|
size_t count, i;
|
|
uint32_t n;
|
|
getdns_bindata *bd;
|
|
int destroy_list = 0;
|
|
char str_buf[1024], *tmp_str;
|
|
|
|
if (_streq(setting, "all_context")) {
|
|
if (!(r = getdns_dict_get_dict(config_dict, "all_context", &dict)))
|
|
r = getdns_context_config(context, dict);
|
|
|
|
CONTEXT_SETTING_INT(resolution_type)
|
|
CONTEXT_SETTING_ARRAY(namespaces, namespace)
|
|
CONTEXT_SETTING_INT(dns_transport)
|
|
CONTEXT_SETTING_ARRAY(dns_transport_list, transport_list)
|
|
CONTEXT_SETTING_INT(idle_timeout)
|
|
CONTEXT_SETTING_INT(limit_outstanding_queries)
|
|
CONTEXT_SETTING_INT(timeout)
|
|
CONTEXT_SETTING_INT(follow_redirects)
|
|
CONTEXT_SETTING_LIST_OR_ZONEFILE(dns_root_servers)
|
|
CONTEXT_SETTING_INT(append_name)
|
|
CONTEXT_SETTING_LIST(suffix)
|
|
CONTEXT_SETTING_LIST_OR_ZONEFILE(dnssec_trust_anchors)
|
|
CONTEXT_SETTING_INT(dnssec_allowed_skew)
|
|
CONTEXT_SETTING_LIST(upstream_recursive_servers)
|
|
CONTEXT_SETTING_INT(edns_maximum_udp_payload_size)
|
|
CONTEXT_SETTING_INT(edns_extended_rcode)
|
|
CONTEXT_SETTING_INT(edns_version)
|
|
CONTEXT_SETTING_INT(edns_do_bit)
|
|
|
|
/***************************************/
|
|
/**** ****/
|
|
/**** Unofficial context settings ****/
|
|
/**** ****/
|
|
/***************************************/
|
|
|
|
CONTEXT_SETTING_INT(edns_client_subnet_private)
|
|
CONTEXT_SETTING_INT(tls_authentication)
|
|
CONTEXT_SETTING_INT(round_robin_upstreams)
|
|
CONTEXT_SETTING_INT(tls_backoff_time)
|
|
CONTEXT_SETTING_INT(tls_connection_retries)
|
|
CONTEXT_SETTING_INT(tls_query_padding_blocksize)
|
|
CONTEXT_SETTING_STRING(trust_anchors_url)
|
|
CONTEXT_SETTING_STRING(trust_anchors_verify_CA)
|
|
CONTEXT_SETTING_STRING(trust_anchors_verify_email)
|
|
CONTEXT_SETTING_STRING(appdata_dir)
|
|
#ifndef USE_WINSOCK
|
|
CONTEXT_SETTING_STRING(resolvconf)
|
|
#endif
|
|
CONTEXT_SETTING_STRING(hosts)
|
|
CONTEXT_SETTING_STRING(tls_ca_path)
|
|
CONTEXT_SETTING_STRING(tls_ca_file)
|
|
CONTEXT_SETTING_STRING(tls_cipher_list)
|
|
CONTEXT_SETTING_STRING(tls_curves_list)
|
|
|
|
/**************************************/
|
|
/**** ****/
|
|
/**** Default extensions setting ****/
|
|
/**** ****/
|
|
/**************************************/
|
|
EXTENSION_SETTING_BOOL(add_warning_for_bad_dns)
|
|
EXTENSION_SETTING_BOOL(dnssec_return_all_statuses)
|
|
EXTENSION_SETTING_BOOL(dnssec_return_full_validation_chain)
|
|
EXTENSION_SETTING_BOOL(dnssec_return_only_secure)
|
|
EXTENSION_SETTING_BOOL(dnssec_return_status)
|
|
EXTENSION_SETTING_BOOL(dnssec_return_validation_chain)
|
|
#if defined(DNSSEC_ROADBLOCK_AVOIDANCE) && defined(HAVE_LIBUNBOUND)
|
|
EXTENSION_SETTING_BOOL(dnssec_roadblock_avoidance)
|
|
#endif
|
|
#ifdef EDNS_COOKIES
|
|
EXTENSION_SETTING_BOOL(edns_cookies)
|
|
#endif
|
|
EXTENSION_SETTING_BOOL(return_api_information)
|
|
EXTENSION_SETTING_BOOL(return_both_v4_and_v6)
|
|
EXTENSION_SETTING_BOOL(return_call_reporting)
|
|
|
|
} else if (_streq(setting, "add_opt_parameters")) {
|
|
if (!(r = getdns_dict_get_dict(config_dict, "add_opt_parameters" , &dict))) {
|
|
if (context->add_opt_parameters)
|
|
getdns_dict_destroy(context->add_opt_parameters);
|
|
context->add_opt_parameters = NULL;
|
|
r = _getdns_dict_copy(dict, &context->add_opt_parameters);
|
|
}
|
|
|
|
} else if (_streq(setting, "header")) {
|
|
if (!(r = getdns_dict_get_dict(config_dict, "header" , &dict))) {
|
|
if (context->header)
|
|
getdns_dict_destroy(context->header);
|
|
if (!(context->header =
|
|
getdns_dict_create_with_context(context)))
|
|
r = GETDNS_RETURN_MEMORY_ERROR;
|
|
else r = getdns_dict_set_dict(
|
|
context->header, "header", dict);
|
|
}
|
|
|
|
} else if (_streq(setting, "specify_class")) {
|
|
if (!(r = getdns_dict_get_int(
|
|
config_dict, "specify_class" , &n)))
|
|
context->specify_class = (uint16_t)n;
|
|
|
|
|
|
/************************************/
|
|
/**** ****/
|
|
/**** Ignored context settings ****/
|
|
/**** ****/
|
|
/************************************/
|
|
} else if (!_streq(setting, "implementation_string")
|
|
&& !_streq(setting, "version_string")
|
|
&& !_streq(setting, "version_number")
|
|
&& !_streq(setting, "api_version_string")
|
|
&& !_streq(setting, "api_version_number")
|
|
&& !_streq(setting, "trust_anchor_file")
|
|
&& !_streq(setting, "default_trust_anchor_location")
|
|
&& !_streq(setting, "default_resolvconf_location")
|
|
&& !_streq(setting, "default_hosts_location")
|
|
&& !_streq(setting, "compilation_comment")
|
|
&& !_streq(setting, "openssl_build_version_number")
|
|
&& !_streq(setting, "openssl_version_number")
|
|
&& !_streq(setting, "openssl_version_string")
|
|
&& !_streq(setting, "openssl_cflags")
|
|
&& !_streq(setting, "openssl_built_on")
|
|
&& !_streq(setting, "openssl_platform")
|
|
&& !_streq(setting, "openssl_dir")
|
|
&& !_streq(setting, "openssl_engines_dir")
|
|
) {
|
|
r = GETDNS_RETURN_NOT_IMPLEMENTED;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_config(getdns_context *context, const getdns_dict *config_dict)
|
|
{
|
|
getdns_list *settings;
|
|
getdns_return_t r;
|
|
getdns_bindata *setting;
|
|
size_t i;
|
|
|
|
if ((r = getdns_dict_get_names(config_dict, &settings)))
|
|
return r;
|
|
|
|
for (i = 0; !(r = getdns_list_get_bindata(settings,i,&setting)); i++) {
|
|
if ((r = _getdns_context_config_setting(
|
|
context, config_dict, setting)))
|
|
break;
|
|
}
|
|
if (r == GETDNS_RETURN_NO_SUCH_LIST_ITEM)
|
|
r = GETDNS_RETURN_GOOD;
|
|
|
|
getdns_list_destroy(settings);
|
|
return r;
|
|
}
|
|
|
|
static size_t _getdns_get_appdata(getdns_context *context, char *path)
|
|
{
|
|
size_t len = 0;
|
|
|
|
#ifdef USE_WINSOCK
|
|
# define SLASHTOK '\\'
|
|
# define APPDATA_SUBDIR "getdns"
|
|
|
|
if (context->appdata_dir) {
|
|
(void) strcpy(path, context->appdata_dir);
|
|
len = strlen(path);
|
|
|
|
} else if (! SUCCEEDED(SHGetFolderPath(NULL,
|
|
CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, path)))
|
|
DEBUG_ANCHOR("ERROR %s(): Could not get %%AppData%% directory\n"
|
|
, __FUNC__);
|
|
|
|
else if ((len = strlen(path)) + sizeof(APPDATA_SUBDIR) + 2 >= _GETDNS_PATH_MAX)
|
|
DEBUG_ANCHOR("ERROR %s(): Home path too long for appdata\n"
|
|
, __FUNC__);
|
|
#else
|
|
# define SLASHTOK '/'
|
|
# define APPDATA_SUBDIR ".getdns"
|
|
struct passwd *p = getpwuid(getuid());
|
|
char *home = NULL;
|
|
|
|
if (context->appdata_dir) {
|
|
(void) strcpy(path, context->appdata_dir);
|
|
len = strlen(path);
|
|
|
|
} else if (!(home = p ? p->pw_dir : getenv("HOME")))
|
|
DEBUG_ANCHOR("ERROR %s(): Could not get home directory\n"
|
|
, __FUNC__);
|
|
|
|
else if ((len = strlen(home)) + sizeof(APPDATA_SUBDIR) + 2 >= _GETDNS_PATH_MAX)
|
|
DEBUG_ANCHOR("ERROR %s(): Home path too long for appdata\n"
|
|
, __FUNC__);
|
|
|
|
else if (!strcpy(path, home))
|
|
; /* strcpy returns path always */
|
|
#endif
|
|
else {
|
|
if (len == 0 || ( path[len - 1] != '/'
|
|
&& path[len - 1] != '\\')) {
|
|
path[len++] = SLASHTOK;
|
|
path[len ] = '\0';
|
|
}
|
|
(void) strcpy(path + len, APPDATA_SUBDIR);
|
|
len += sizeof(APPDATA_SUBDIR) - 1;
|
|
}
|
|
if (len) {
|
|
if (path[len - 1] == '/' || path[len - 1] == '\\') {
|
|
path[--len] = '\0';
|
|
}
|
|
if (0 >
|
|
#ifdef USE_WINSOCK
|
|
mkdir(path)
|
|
#else
|
|
mkdir(path, 0755)
|
|
#endif
|
|
&& errno != EEXIST)
|
|
DEBUG_ANCHOR("ERROR %s(): Could not mkdir %s: %s\n"
|
|
, __FUNC__, path, strerror(errno));
|
|
else {
|
|
path[len++] = SLASHTOK;
|
|
path[len ] = '\0';
|
|
return len;
|
|
}
|
|
}
|
|
path[0] = '\0';
|
|
return 0;
|
|
}
|
|
|
|
FILE *_getdns_context_get_priv_fp(getdns_context *context, const char *fn)
|
|
{
|
|
char path[_GETDNS_PATH_MAX];
|
|
FILE *f = NULL;
|
|
size_t len = _getdns_get_appdata(context, path);
|
|
|
|
(void) context;
|
|
/*
|
|
* Commented out to enable fallback to current directory
|
|
*
|
|
* if (!(len = _getdns_get_appdata(context, path)))
|
|
* DEBUG_ANCHOR("ERROR %s(): Could nog get application data path\n"
|
|
* , __FUNC__);
|
|
*
|
|
* else
|
|
*/
|
|
if (len + strlen(fn) >= sizeof(path))
|
|
DEBUG_ANCHOR("ERROR %s(): Application data too long\n", __FUNC__);
|
|
|
|
else if (!strcpy(path + len, fn))
|
|
; /* strcpy returns path + len always */
|
|
|
|
else if (!(f = fopen(path, "r")))
|
|
DEBUG_ANCHOR("ERROR %s(): Opening \"%s\": %s\n"
|
|
, __FUNC__, path, strerror(errno));
|
|
|
|
return f;
|
|
}
|
|
|
|
uint8_t *_getdns_context_get_priv_file(getdns_context *context,
|
|
const char *fn, uint8_t *buf, size_t buf_len, size_t *file_sz)
|
|
{
|
|
FILE *f = NULL;
|
|
|
|
if (!(f = _getdns_context_get_priv_fp(context, fn)))
|
|
; /* pass */
|
|
|
|
else if ((*file_sz = fread(buf, 1, buf_len, f)) < (buf_len - 1) && feof(f)) {
|
|
buf[*file_sz] = 0;
|
|
(void) fclose(f);
|
|
return buf;
|
|
}
|
|
else if (fseek(f, 0, SEEK_END) < 0)
|
|
DEBUG_ANCHOR("ERROR %s(): Determining size of \"%s\": %s\n"
|
|
, __FUNC__, fn, strerror(errno));
|
|
|
|
else if (!(buf = GETDNS_XMALLOC(
|
|
context->mf, uint8_t, (buf_len = ftell(f) + 1))))
|
|
DEBUG_ANCHOR("ERROR %s(): Allocating %d memory for \"%s\"\n"
|
|
, __FUNC__, (int)buf_len, fn);
|
|
|
|
else {
|
|
rewind(f);
|
|
if ((*file_sz = fread(buf, 1, buf_len, f)) >= buf_len || !feof(f)) {
|
|
GETDNS_FREE(context->mf, buf);
|
|
DEBUG_ANCHOR("ERROR %s(): Reading \"%s\": %s\n"
|
|
, __FUNC__, fn, strerror(errno));
|
|
}
|
|
else {
|
|
buf[*file_sz] = 0;
|
|
(void) fclose(f);
|
|
return buf;
|
|
}
|
|
}
|
|
if (f)
|
|
(void) fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int _getdns_context_write_priv_file(getdns_context *context,
|
|
const char *fn, getdns_bindata *content)
|
|
{
|
|
char path[_GETDNS_PATH_MAX], tmpfn[_GETDNS_PATH_MAX];
|
|
int fd = -1;
|
|
FILE *f = NULL;
|
|
size_t len = _getdns_get_appdata(context, path);
|
|
|
|
/*
|
|
* Commented out to enable fallback to current directory
|
|
*
|
|
* if (!(len = _getdns_get_appdata(context, path)))
|
|
* DEBUG_ANCHOR("ERROR %s(): Could nog get application data path\n"
|
|
* , __FUNC__);
|
|
*
|
|
* else
|
|
*/
|
|
if (len + 6 >= sizeof(tmpfn)
|
|
|| len + strlen(fn) >= sizeof(path))
|
|
DEBUG_ANCHOR("ERROR %s(): Application data too long\n", __FUNC__);
|
|
|
|
|
|
else if (snprintf(tmpfn, sizeof(tmpfn), "%sXXXXXX", path) < 0)
|
|
DEBUG_ANCHOR("ERROR %s(): Creating temporary filename template: \"%s\"\n"
|
|
, __FUNC__, tmpfn);
|
|
|
|
else if (!strcpy(path + len, fn))
|
|
; /* strcpy returns path + len always */
|
|
|
|
else if ((fd = mkstemp(tmpfn)) < 0)
|
|
DEBUG_ANCHOR("ERROR %s(): Creating temporary file \"%s\": %s\n"
|
|
, __FUNC__, tmpfn, strerror(errno));
|
|
|
|
else if (!(f = fdopen(fd, "w")))
|
|
DEBUG_ANCHOR("ERROR %s(): Opening temporary file: %s\n"
|
|
, __FUNC__, strerror(errno));
|
|
|
|
else if (fwrite(content->data, 1, content->size, f) < content->size)
|
|
DEBUG_ANCHOR("ERROR %s(): Writing temporary file: %s\n"
|
|
, __FUNC__, strerror(errno));
|
|
|
|
else if (fclose(f) < 0)
|
|
DEBUG_ANCHOR("ERROR %s(): Closing temporary file: %s\n"
|
|
, __FUNC__, strerror(errno));
|
|
|
|
else if (rename(tmpfn, path) < 0)
|
|
DEBUG_ANCHOR("ERROR %s(): Renaming temporary file: %s\n"
|
|
, __FUNC__, strerror(errno));
|
|
else {
|
|
context->can_write_appdata = PROP_ABLE;
|
|
return 1;
|
|
}
|
|
if (f)
|
|
(void) fclose(f);
|
|
|
|
else if (fd >= 0)
|
|
(void) close(fd);
|
|
|
|
context->can_write_appdata = PROP_UNABLE;
|
|
return 0;
|
|
}
|
|
|
|
int _getdns_context_can_write_appdata(getdns_context *context)
|
|
{
|
|
char test_fn[30], path[_GETDNS_PATH_MAX];
|
|
size_t len;
|
|
getdns_bindata test_content = { 4, (void *)"TEST" };
|
|
|
|
if (context->can_write_appdata == PROP_ABLE)
|
|
return 1;
|
|
|
|
else if (context->can_write_appdata == PROP_UNABLE)
|
|
return 0;
|
|
|
|
(void) snprintf( test_fn, sizeof(test_fn)
|
|
, "write-test-%d.tmp", arc4random());
|
|
|
|
if (!_getdns_context_write_priv_file(context, test_fn, &test_content))
|
|
return 0;
|
|
|
|
len = _getdns_get_appdata(context, path);
|
|
/*
|
|
* Commented out to enable fallback to current directory
|
|
*
|
|
*
|
|
* if (!(len = _getdns_get_appdata(context, path)))
|
|
* DEBUG_ANCHOR("ERROR %s(): Could not get application data path\n"
|
|
* , __FUNC__);
|
|
*
|
|
* else
|
|
*/
|
|
if (len + strlen(test_fn) >= sizeof(path))
|
|
DEBUG_ANCHOR("ERROR %s(): Application data too long\n", __FUNC__);
|
|
|
|
else if (!strcpy(path + len, test_fn))
|
|
; /* strcpy returns path + len always */
|
|
|
|
else if (unlink(path) < 0)
|
|
DEBUG_ANCHOR("ERROR %s(): Unlinking write test file \"%s\": %s\n"
|
|
, __FUNC__, path, strerror(errno));
|
|
|
|
return 1;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_trust_anchors_url(
|
|
getdns_context *context, const char *url)
|
|
{
|
|
const char *path;
|
|
size_t path_len;
|
|
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (url) {
|
|
if (! ((url[0] == 'h' || url[0] == 'H')
|
|
&& (url[1] == 't' || url[1] == 'T')
|
|
&& (url[2] == 't' || url[2] == 'T')
|
|
&& (url[3] == 'p' || url[3] == 'P')
|
|
&& url[4] == ':' && url[5] == '/' && url[6] == '/'
|
|
&& (path = strchr(url + 7, '/'))))
|
|
return GETDNS_RETURN_NOT_IMPLEMENTED;
|
|
|
|
path_len = strlen(path);
|
|
if (! ( path_len >= 5
|
|
&& path[path_len - 4] == '.'
|
|
&& ( path[path_len - 3] == 'x'
|
|
|| path[path_len - 3] == 'X')
|
|
&& ( path[path_len - 2] == 'm'
|
|
|| path[path_len - 2] == 'M')
|
|
&& ( path[path_len - 1] == 'l'
|
|
|| path[path_len - 1] == 'L')))
|
|
return GETDNS_RETURN_NOT_IMPLEMENTED;
|
|
}
|
|
if (context->trust_anchors_url)
|
|
GETDNS_FREE(context->mf, context->trust_anchors_url);
|
|
context->trust_anchors_url = _getdns_strdup(&context->mf, url);
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_TRUST_ANCHORS_URL);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_trust_anchors_url(
|
|
getdns_context *context, const char **url)
|
|
{
|
|
if (!context || !url)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
*url = context && context->trust_anchors_url
|
|
? context->trust_anchors_url
|
|
: _getdns_default_trust_anchors_url;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_trust_anchors_verify_CA(
|
|
getdns_context *context, const char *verify_CA)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (context->trust_anchors_verify_CA)
|
|
GETDNS_FREE(context->mf, context->trust_anchors_verify_CA);
|
|
context->trust_anchors_verify_CA =
|
|
_getdns_strdup(&context->mf, verify_CA);
|
|
|
|
dispatch_updated( context
|
|
, GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_CA);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_trust_anchors_verify_CA(
|
|
getdns_context *context, const char **verify_CA)
|
|
{
|
|
if (!verify_CA)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
*verify_CA = context && context->trust_anchors_verify_CA
|
|
? context->trust_anchors_verify_CA
|
|
: _getdns_default_trust_anchors_verify_CA;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_trust_anchors_verify_email(
|
|
getdns_context *context, const char *verify_email)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (context->trust_anchors_verify_email)
|
|
GETDNS_FREE(context->mf, context->trust_anchors_verify_email);
|
|
context->trust_anchors_verify_email =
|
|
_getdns_strdup(&context->mf, verify_email);
|
|
|
|
dispatch_updated( context
|
|
, GETDNS_CONTEXT_CODE_TRUST_ANCHORS_VERIFY_EMAIL);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_trust_anchors_verify_email(
|
|
getdns_context *context, const char **verify_email)
|
|
{
|
|
if (!verify_email)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
*verify_email = context && context->trust_anchors_verify_email
|
|
? context->trust_anchors_verify_email
|
|
: _getdns_default_trust_anchors_verify_email;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_appdata_dir(
|
|
getdns_context *context, const char *appdata_dir)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
if (context->appdata_dir)
|
|
GETDNS_FREE(context->mf, context->appdata_dir);
|
|
context->appdata_dir = _getdns_strdup(&context->mf, appdata_dir);
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_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));
|
|
#ifndef USE_WINSOCK
|
|
else if (*context->fchg_resolvconf.fn &&
|
|
(r = getdns_context_set_resolvconf(
|
|
context->sys_ctxt, context->fchg_resolvconf.fn)))
|
|
DEBUG_ANCHOR("Could initialize system context with resolvconf "
|
|
"\"%s\": %s\n", context->fchg_resolvconf.fn
|
|
, getdns_get_errorstr_by_id(r));
|
|
#endif
|
|
else if (*context->fchg_hosts.fn &&
|
|
(r = getdns_context_set_hosts(
|
|
context->sys_ctxt, context->fchg_hosts.fn)))
|
|
DEBUG_ANCHOR("Could initialize system context with hosts "
|
|
"\"%s\": %s\n", context->fchg_resolvconf.fn
|
|
, 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;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_tls_ca_path(getdns_context *context, const char *tls_ca_path)
|
|
{
|
|
if (!context || !tls_ca_path)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
if (context->tls_ca_path)
|
|
GETDNS_FREE(context->mf, context->tls_ca_path);
|
|
context->tls_ca_path = _getdns_strdup(&context->mf, tls_ca_path);
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_TLS_CA_PATH);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_tls_ca_path(getdns_context *context, const char **tls_ca_path)
|
|
{
|
|
if (!context || !tls_ca_path)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
*tls_ca_path = context->tls_ca_path;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_tls_ca_file(getdns_context *context, const char *tls_ca_file)
|
|
{
|
|
if (!context || !tls_ca_file)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
if (context->tls_ca_file)
|
|
GETDNS_FREE(context->mf, context->tls_ca_file);
|
|
context->tls_ca_file = _getdns_strdup(&context->mf, tls_ca_file);
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_TLS_CA_FILE);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_tls_ca_file(getdns_context *context, const char **tls_ca_file)
|
|
{
|
|
if (!context || !tls_ca_file)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
*tls_ca_file = context->tls_ca_file;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_tls_cipher_list(
|
|
getdns_context *context, const char *tls_cipher_list)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
if (context->tls_cipher_list)
|
|
GETDNS_FREE(context->mf, context->tls_cipher_list);
|
|
context->tls_cipher_list = tls_cipher_list
|
|
? _getdns_strdup(&context->mf, tls_cipher_list)
|
|
: NULL;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_TLS_CIPHER_LIST);
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_tls_cipher_list(
|
|
getdns_context *context, const char **tls_cipher_list)
|
|
{
|
|
if (!context || !tls_cipher_list)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
|
|
*tls_cipher_list = context->tls_cipher_list
|
|
? context->tls_cipher_list
|
|
: _getdns_tls_context_default_cipher_list;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_set_tls_curves_list(
|
|
getdns_context *context, const char *tls_curves_list)
|
|
{
|
|
if (!context)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
#if HAVE_TLS_CTX_CURVES_LIST
|
|
if (context->tls_curves_list)
|
|
GETDNS_FREE(context->mf, context->tls_curves_list);
|
|
context->tls_curves_list = tls_curves_list
|
|
? _getdns_strdup(&context->mf, tls_curves_list)
|
|
: NULL;
|
|
|
|
dispatch_updated(context, GETDNS_CONTEXT_CODE_TLS_CIPHER_LIST);
|
|
return GETDNS_RETURN_GOOD;
|
|
#else
|
|
(void)tls_curves_list;
|
|
return GETDNS_RETURN_NOT_IMPLEMENTED;
|
|
#endif
|
|
}
|
|
|
|
getdns_return_t
|
|
getdns_context_get_tls_curves_list(
|
|
getdns_context *context, const char **tls_curves_list)
|
|
{
|
|
if (!context || !tls_curves_list)
|
|
return GETDNS_RETURN_INVALID_PARAMETER;
|
|
*tls_curves_list = context->tls_curves_list;
|
|
return GETDNS_RETURN_GOOD;
|
|
}
|
|
|
|
/* context.c */
|