getdns/src/context.h

568 lines
18 KiB
C

/**
*
* \file context.h
* @brief getdns context management functions
*
* Originally 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.
*/
#ifndef _GETDNS_CONTEXT_H_
#define _GETDNS_CONTEXT_H_
#include "getdns/getdns.h"
#include "getdns/getdns_extra.h"
#include "config.h"
#include "types-internal.h"
#include "extension/default_eventloop.h"
#include "util/rbtree.h"
#include "ub_loop.h"
#include "server.h"
#ifdef HAVE_MDNS_SUPPORT
#include "util/lruhash.h"
#endif
#include "rr-iter.h"
#include "anchor.h"
#include "tls.h"
struct getdns_dns_req;
struct ub_ctx;
enum filechgs { GETDNS_FCHG_ERRORS = -1
, GETDNS_FCHG_NOERROR = 0
, GETDNS_FCHG_NOCHANGES = 0
, GETDNS_FCHG_MTIME = 1
, GETDNS_FCHG_CTIME = 2};
/** function pointer typedefs */
typedef void (*getdns_update_callback) (struct getdns_context *,
getdns_context_code_t);
typedef void (*getdns_update_callback2) (struct getdns_context *,
getdns_context_code_t, void *userarg);
/* internal use only for detecting changes to system files */
struct filechg {
char fn[_GETDNS_PATH_MAX];
int changes;
int errors;
struct stat prevstat;
};
typedef enum getdns_tls_hs_state {
GETDNS_HS_NONE,
GETDNS_HS_WRITE,
GETDNS_HS_READ,
GETDNS_HS_DONE,
GETDNS_HS_FAILED
} getdns_tls_hs_state_t;
typedef enum getdns_conn_state {
GETDNS_CONN_CLOSED,
GETDNS_CONN_SETUP,
GETDNS_CONN_OPEN,
GETDNS_CONN_TEARDOWN,
GETDNS_CONN_BACKOFF
} getdns_conn_state_t;
typedef enum getdns_tasrc {
GETDNS_TASRC_NONE,
GETDNS_TASRC_ZONE,
GETDNS_TASRC_APP,
GETDNS_TASRC_FETCHING,
GETDNS_TASRC_XML,
GETDNS_TASRC_XML_UPDATE,
GETDNS_TASRC_FAILED
} getdns_tasrc;
typedef enum getdns_tsig_algo {
GETDNS_NO_TSIG = 0, /* Do not use tsig */
GETDNS_HMAC_MD5 = 1, /* 128 bits */
GETDNS_GSS_TSIG = 2, /* Not supported */
GETDNS_HMAC_SHA1 = 3, /* 160 bits */
GETDNS_HMAC_SHA224 = 4,
GETDNS_HMAC_SHA256 = 5,
GETDNS_HMAC_SHA384 = 6,
GETDNS_HMAC_SHA512 = 7
} getdns_tsig_algo;
typedef struct getdns_tsig_info {
getdns_tsig_algo alg;
const char *name;
size_t strlen_name;
const uint8_t *dname;
size_t dname_len;
size_t min_size; /* in # octets */
size_t max_size; /* Actual size in # octets */
} getdns_tsig_info;
const getdns_tsig_info *_getdns_get_tsig_info(getdns_tsig_algo tsig_alg);
/* for doing public key pinning of TLS-capable upstreams: */
typedef struct sha256_pin {
char pin[SHA256_DIGEST_LENGTH];
struct sha256_pin *next;
} sha256_pin_t;
typedef struct getdns_upstream {
/* backpointer to containing upstreams structure */
struct getdns_upstreams *upstreams;
socklen_t addr_len;
struct sockaddr_storage addr;
char addr_str[INET6_ADDRSTRLEN];
/**
* How is this upstream doing over UDP?
*
* to_retry = 1, back_off = 1, in context.c:upstream_init()
*
* When querying over UDP, first a upstream is selected which to_retry
* value > 0 in stub.c:upstream_select().
*
* Every time a udp request times out, to_retry is decreased, and if
* it reaches 0, it is set to minus back_off in
* stub.c:stub_next_upstream().
*
* to_retry will become > 0 again. because each time an upstream is
* selected for a UDP query in stub.c:upstream_select(), all to_retry
* counters <= 0 are incremented.
*
* On continuous failure, the stubs are less likely to be reselected,
* because each time to_retry is set to minus back_off, in
* stub.c:stub_next_upstream(), the back_off value is doubled.
*
* Finally, if all upstreams are failing, the upstreams with the
* smallest back_off value will be selected, and the back_off value
* decremented by one.
*/
int to_retry; /* (initialized to 1) */
int back_off; /* (initialized to 1) */
size_t udp_responses;
size_t udp_timeouts;
/* For stateful upstreams, need to share the connection and track the
activity on the connection */
int fd;
getdns_transport_list_t transport;
getdns_eventloop_event event;
getdns_eventloop *loop;
getdns_tcp_state tcp;
/* These are running totals or historical info */
size_t conn_completed;
size_t conn_shutdowns;
size_t conn_setup_failed;
time_t conn_retry_time;
uint16_t conn_backoff_interval;
size_t conn_backoffs;
size_t total_responses;
size_t total_timeouts;
getdns_auth_state_t best_tls_auth_state;
getdns_auth_state_t last_tls_auth_state;
/* These are per connection. */
getdns_conn_state_t conn_state;
size_t queries_sent;
size_t responses_received;
size_t responses_timeouts;
size_t keepalive_shutdown;
uint64_t keepalive_timeout;
int server_keepalive_received;
/* Management of outstanding requests on stateful transports */
getdns_network_req *write_queue;
getdns_network_req *write_queue_last;
_getdns_rbtree_t netreq_by_query_id;
/* TLS specific connection handling*/
_getdns_tls_connection* tls_obj;
_getdns_tls_session* tls_session;
getdns_tls_hs_state_t tls_hs_state;
getdns_auth_state_t tls_auth_state;
unsigned tls_fallback_ok : 1;
char *tls_cipher_list;
char *tls_curves_list;
/* Auth credentials*/
char tls_auth_name[256];
sha256_pin_t *tls_pubkey_pinset;
/* When requests have been scheduled asynchronously on an upstream
* that is kept open, and a synchronous call is then done with the
* upstream before all scheduled requests have been answered, answers
* for the asynchronous requests may be received on the open upstream.
* Those cannot be processed immediately, because then asynchronous
* callbacks will be fired as a side-effect.
*
* finished_dnsreqs is a list of dnsreqs for which answers have been
* received during a synchronous request. They will be processed
* when the asynchronous eventloop is run. For this the finished_event
* will be scheduled to the registered asynchronous event loop with a
* timeout of 1, so it will fire immediately (but not while scheduling)
* when the asynchronous eventloop is run.
*/
getdns_dns_req *finished_dnsreqs;
getdns_eventloop_event finished_event;
unsigned is_sync_loop : 1;
/* EDNS cookies */
uint32_t secret;
uint8_t client_cookie[8];
uint8_t prev_client_cookie[8];
uint8_t server_cookie[32];
unsigned has_client_cookie : 1;
unsigned has_prev_client_cookie : 1;
unsigned has_server_cookie : 1;
unsigned server_cookie_len : 5;
/* TSIG */
uint8_t tsig_dname[256];
size_t tsig_dname_len;
size_t tsig_size;
uint8_t tsig_key[256];
getdns_tsig_algo tsig_alg;
} getdns_upstream;
typedef struct getdns_log_config {
getdns_logfunc_type func;
void *userarg;
uint64_t system;
getdns_loglevel_type level;
} getdns_log_config;
typedef struct getdns_upstreams {
struct mem_funcs mf;
size_t referenced;
size_t count;
size_t current_udp;
size_t current_stateful;
uint16_t max_backoff_value;
uint16_t tls_backoff_time;
uint16_t tls_connection_retries;
getdns_log_config log;
getdns_upstream upstreams[];
} getdns_upstreams;
typedef enum tas_state {
TAS_LOOKUP_ADDRESSES = 0,
TAS_WRITE_GET_XML,
TAS_READ_XML_HDR,
TAS_READ_XML_DOC,
TAS_WRITE_GET_PS7,
TAS_READ_PS7_HDR,
TAS_READ_PS7_DOC,
TAS_DONE,
TAS_RETRY,
TAS_RETRY_GET_PS7,
TAS_RETRY_PS7_HDR,
TAS_RETRY_PS7_DOC,
TAS_RETRY_DONE
} tas_state;
typedef enum _getdns_property {
PROP_INHERIT = 0,
PROP_UNKNOWN = 1,
PROP_UNABLE = 2,
PROP_ABLE = 3
} _getdns_property;
typedef struct tas_connection {
getdns_eventloop *loop;
getdns_network_req *req;
_getdns_rrset_spc rrset_spc;
_getdns_rrset *rrset;
_getdns_rrtype_iter rr_spc;
_getdns_rrtype_iter *rr;
int fd;
getdns_eventloop_event event;
tas_state state;
getdns_tcp_state tcp;
char *http;
getdns_bindata xml;
} tas_connection;
struct getdns_context {
/* Context values */
getdns_resolution_t resolution_type;
getdns_namespace_t *namespaces;
size_t namespace_count;
uint64_t timeout;
uint64_t idle_timeout;
getdns_redirects_t follow_redirects;
getdns_list *dns_root_servers;
#if defined(HAVE_LIBUNBOUND) && !defined(HAVE_UB_CTX_SET_STUB)
char root_servers_fn[FILENAME_MAX];
#endif
getdns_append_name_t append_name;
/* Suffix buffer containing a list of (length byte | dname) where
* length bytes contains the length of the following dname.
* The last dname should be the zero byte.
*/
const uint8_t *suffixes;
/* Length of all suffixes in the suffix buffer */
size_t suffixes_len;
uint8_t *trust_anchors;
size_t trust_anchors_len;
getdns_tasrc trust_anchors_source;
tas_connection a;
tas_connection aaaa;
uint8_t tas_hdr_spc[512];
char *trust_anchors_url;
char *trust_anchors_verify_CA;
char *trust_anchors_verify_email;
_getdns_ksks root_ksk;
char *appdata_dir;
_getdns_property can_write_appdata;
char *tls_ca_path;
char *tls_ca_file;
char *tls_cipher_list;
char *tls_curves_list;
getdns_upstreams *upstreams;
uint16_t limit_outstanding_queries;
uint32_t dnssec_allowed_skew;
getdns_tls_authentication_t tls_auth; /* What user requested for TLS*/
getdns_tls_authentication_t tls_auth_min; /* Derived minimum auth allowed*/
uint8_t round_robin_upstreams;
uint16_t max_backoff_value;
uint16_t tls_backoff_time;
uint16_t tls_connection_retries;
getdns_transport_list_t *dns_transports;
size_t dns_transport_count;
uint8_t edns_extended_rcode;
uint8_t edns_version;
uint8_t edns_do_bit;
int edns_maximum_udp_payload_size; /* -1 is unset */
uint8_t edns_client_subnet_private;
uint16_t tls_query_padding_blocksize;
_getdns_tls_context* tls_ctx;
getdns_update_callback update_callback;
getdns_update_callback2 update_callback2;
void *update_userarg;
getdns_log_config log;
int processing;
int destroying;
struct mem_funcs mf;
struct mem_funcs my_mf;
#ifdef HAVE_LIBUNBOUND
/* The underlying contexts that do the real work */
struct ub_ctx *unbound_ctx;
int unbound_ta_set;
#ifdef HAVE_UNBOUND_EVENT_API
_getdns_ub_loop ub_loop;
#endif
#endif
/* A tree to hold local host information*/
_getdns_rbtree_t local_hosts;
/* which resolution type the contexts are configured for
* 0 means nothing set
*/
getdns_resolution_t resolution_type_set;
/*
* outbound requests -> transaction to getdns_dns_req
*/
_getdns_rbtree_t outbound_requests;
/* network requests
*/
size_t netreqs_in_flight;
_getdns_rbtree_t pending_netreqs;
getdns_network_req *first_pending_netreq;
getdns_eventloop_event pending_timeout_event;
struct listen_set *server;
/* Event loop extension. */
getdns_eventloop *extension;
#ifdef HAVE_LIBUNBOUND
getdns_eventloop_event ub_event;
/* lock to prevent nested ub_event scheduling */
int ub_event_scheduling;
#endif
/* The default extension */
_getdns_default_eventloop default_eventloop;
_getdns_default_eventloop sync_eventloop;
/* request extension defaults */
getdns_dict *header;
getdns_dict *add_opt_parameters;
unsigned add_warning_for_bad_dns : 1;
unsigned dnssec_return_all_statuses : 1;
unsigned dnssec_return_full_validation_chain : 1;
unsigned dnssec_return_only_secure : 1;
unsigned dnssec_return_status : 1;
unsigned dnssec_return_validation_chain : 1;
#ifdef DNSSEC_ROADBLOCK_AVOIDANCE
unsigned dnssec_roadblock_avoidance : 1;
#endif
unsigned edns_cookies : 1;
unsigned return_api_information : 1; /* Not used */
unsigned return_both_v4_and_v6 : 1;
unsigned return_call_reporting : 1;
uint16_t specify_class;
/*
* Context for doing system queries.
* For example to resolve data.iana.org or to resolver the addresses
* of upstreams without specified addresses.
*/
getdns_context *sys_ctxt;
/* List of dnsreqs that want to be notified when we have fetched a
* trust anchor from data.iana.org.
*/
getdns_dns_req *ta_notify;
/*
* state data used to detect changes to the system config files
*/
struct filechg fchg_resolvconf;
struct filechg fchg_hosts;
uint8_t trust_anchors_spc[1024];
#ifdef USE_WINSOCK
/* We need to run WSAStartup() to be able to use getaddrinfo() */
WSADATA wsaData;
#endif
/* MDNS */
#ifdef HAVE_MDNS_SUPPORT
/*
* If supporting MDNS, context may be instantiated either in basic mode
* or in full mode. If working in extended mode, two multicast sockets are
* left open, for IPv4 and IPv6. Data can be received on either socket.
* The context also keeps a list of open queries, characterized by a
* name and an RR type, and a list of received answers, characterized
* by name, RR type and data value.
*/
int mdns_extended_support; /* 0 = no support, 1 = supported, 2 = initialization needed */
int mdns_connection_nb; /* typically 0 or 2 for IPv4 and IPv6 */
struct mdns_network_connection * mdns_connection;
struct lruhash * mdns_cache;
#endif /* HAVE_MDNS_SUPPORT */
}; /* getdns_context */
void _getdns_upstream_log(getdns_upstream *upstream, uint64_t system,
getdns_loglevel_type level, const char *fmt, ...);
void _getdns_context_log(getdns_context *context, uint64_t system,
getdns_loglevel_type level, const char *fmt, ...);
/** internal functions **/
/**
* Sets up the unbound contexts with stub or recursive behavior
* if needed.
* @param context previously initialized getdns_context
* @return GETDNS_RETURN_GOOD on success
*/
getdns_return_t _getdns_context_prepare_for_resolution(getdns_context *context);
/* Register a getdns_dns_req with context.
* - Without pluggable unbound event API,
* ub_fd() is scheduled when this was the first request.
*/
void _getdns_context_track_outbound_request(getdns_dns_req *dnsreq);
/* Deregister getdns_dns_req from the context.
* - Without pluggable unbound event API,
* ub_fd() is scheduled when this was the first request.
* - Potential timeout events will be cleared.
* - All associated getdns_dns_reqs (to get the validation chain)
* will be canceled.
*/
void _getdns_context_clear_outbound_request(getdns_dns_req *dnsreq);
/* Cancels and frees a getdns_dns_req (without calling user callbacks)
* - Deregisters getdns_dns_req with _getdns_context_clear_outbound_request()
* - Cancels associated getdns_network_reqs
* (by calling ub_cancel() or _getdns_cancel_stub_request())
* - Frees the getdns_dns_req
*/
void _getdns_context_cancel_request(getdns_dns_req *dnsreq);
/* Calls user callback (with GETDNS_CALLBACK_TIMEOUT + response dict), then
* cancels and frees the getdns_dns_req with _getdns_context_cancel_request()
*/
void _getdns_context_request_timed_out(getdns_dns_req *dnsreq);
struct getdns_bindata *_getdns_bindata_copy(
struct mem_funcs *mfs, size_t size, const uint8_t *data);
void _getdns_bindata_destroy(
struct mem_funcs *mfs,
struct getdns_bindata *bindata);
/* perform name resolution in /etc/hosts */
getdns_return_t _getdns_context_local_namespace_resolve(
getdns_dns_req* req, struct getdns_dict **response);
void _getdns_context_ub_read_cb(void *userarg);
void _getdns_upstreams_dereference(getdns_upstreams *upstreams);
void _getdns_upstream_shutdown(getdns_upstream *upstream);
FILE *_getdns_context_get_priv_fp(getdns_context *context, const char *fn);
uint8_t *_getdns_context_get_priv_file(getdns_context *context,
const char *fn, uint8_t *buf, size_t buf_len, size_t *file_sz);
int _getdns_context_write_priv_file(getdns_context *context,
const char *fn, getdns_bindata *content);
int _getdns_context_can_write_appdata(getdns_context *context);
getdns_context *_getdns_context_get_sys_ctxt(
getdns_context *context, getdns_eventloop *loop);
#endif /* _GETDNS_CONTEXT_H_ */