mirror of https://github.com/getdnsapi/getdns.git
Merge bcaf8fa563
into 9c076ca34b
This commit is contained in:
commit
d34fea794b
|
@ -164,6 +164,8 @@ option(BUILD_LIBEV "Build libev support library if available." ON)
|
|||
option(BUILD_LIBEVENT2 "Build libevent2 support library if available." ON)
|
||||
option(BUILD_LIBUV "Build libuv support library available." ON)
|
||||
|
||||
option(BUILD_DOH "Build DNS-over-HTTPS support if libnghttp2 library is available." ON)
|
||||
|
||||
option(USE_LIBIDN2 "Use libidn2 if available." ON)
|
||||
option(USE_GNUTLS "Use GnuTLS for TLS connections." OFF)
|
||||
|
||||
|
@ -193,6 +195,7 @@ set(STUB_NATIVE_DNSSEC ${ENABLE_NATIVE_STUB_DNSSEC})
|
|||
set(USE_LIBEV ${BUILD_LIBEV})
|
||||
set(USE_LIBEVENT2 ${BUILD_LIBEVENT2})
|
||||
set(USE_LIBUV ${BUILD_LIBUV})
|
||||
set(USE_DOH ${BUILD_DOH})
|
||||
|
||||
option(ENABLE_DEBUG_KEEP_CONNECTIONS_OPEN "Disable connection idle timeout. Do not enable.")
|
||||
mark_as_advanced(ENABLE_DEBUG_KEEP_CONNECTIONS_OPEN)
|
||||
|
@ -409,6 +412,17 @@ if (USE_LIBIDN2)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# DNS-over-HTTPS support
|
||||
if (USE_DOH)
|
||||
find_package(Libnghttp2)
|
||||
if (Libnghttp2_FOUND)
|
||||
set(HAVE_LIBNGHTTP2 1)
|
||||
else()
|
||||
message(WARNING "DNS-over-HTTPS support build requested, but libnghttp2 not found. Disabled.")
|
||||
unset(USE_DOH)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
# GnuTLS and Nettle. If using GnuTLS, we need the Nettle dev stuff to
|
||||
# handle digital signature algorithms. GnuTLS uses Nettle internally.
|
||||
if (USE_GNUTLS)
|
||||
|
@ -697,6 +711,9 @@ endif ()
|
|||
if (GnuTLS_FOUND)
|
||||
target_include_directories(getdns_objects PRIVATE ${GNUTLS_INCLUDE_DIR})
|
||||
endif ()
|
||||
if (Libnghttp2_FOUND)
|
||||
target_include_directories(getdns_objects PRIVATE ${LIBNGHTTP2_INCLUDE_DIR})
|
||||
endif ()
|
||||
|
||||
# Don't compile separate objects for shared and static libraries.
|
||||
# Yes, -fPIC is slightly suboptimal for static libraries, but it looks
|
||||
|
@ -730,6 +747,9 @@ if (ENABLE_STATIC)
|
|||
if (Nettle_FOUND)
|
||||
target_link_libraries(getdns PUBLIC Nettle::Nettle Nettle::Hogweed)
|
||||
endif ()
|
||||
if (Libnghttp2_FOUND)
|
||||
target_link_libraries(getdns PUBLIC Libnghttp2::Libnghttp2)
|
||||
endif ()
|
||||
set_target_properties(getdns PROPERTIES OUTPUT_NAME getdns${static_lib_suffix})
|
||||
endif ()
|
||||
|
||||
|
@ -759,6 +779,9 @@ if (ENABLE_SHARED)
|
|||
if (Nettle_FOUND)
|
||||
target_link_libraries(getdns_shared PUBLIC Nettle::Nettle Nettle::Hogweed)
|
||||
endif ()
|
||||
if (Libnghttp2_FOUND)
|
||||
target_link_libraries(getdns_shared PUBLIC Libnghttp2::Libnghttp2)
|
||||
endif ()
|
||||
set_target_properties(getdns_shared PROPERTIES OUTPUT_NAME getdns)
|
||||
target_shared_library_version(getdns_shared ${GETDNS_VERSION_CURRENT} ${GETDNS_VERSION_REVISION} ${GETDNS_VERSION_AGE})
|
||||
|
||||
|
|
|
@ -188,6 +188,8 @@
|
|||
#cmakedefine HAVE_NETTLE_DSA_COMPAT_H 1
|
||||
#cmakedefine HAVE_NETTLE_EDDSA_H 1
|
||||
|
||||
#cmakedefine HAVE_LIBNGHTTP2 1
|
||||
|
||||
#cmakedefine HAVE_EVENT2_EVENT_H 1
|
||||
#cmakedefine HAVE_EVENT_BASE_NEW 1
|
||||
#cmakedefine HAVE_EVENT_BASE_FREE 1
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
#[=======================================================================[.rst:
|
||||
FindLibnghttp2
|
||||
-----------
|
||||
|
||||
Find the Libnghttp2 library
|
||||
|
||||
Imported targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module defines the following :prop_tgt:`IMPORTED` targets:
|
||||
|
||||
``Libnghttp2::Libnghttp2``
|
||||
The Libnghttp2 library, if found.
|
||||
|
||||
Result variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
This module will set the following variables in your project:
|
||||
|
||||
``Libnghttp2_FOUND``
|
||||
If false, do not try to use Libnghttp2.
|
||||
``LIBNGHTTP2_INCLUDE_DIR``
|
||||
where to find libnghttp2 headers.
|
||||
``LIBNGHTTP2_LIBRARIES``
|
||||
the libraries needed to use Libnghttp2.
|
||||
``LIBNGHTTP2_VERSION``
|
||||
the version of the Libnghttp2 library found
|
||||
|
||||
#]=======================================================================]
|
||||
|
||||
find_package(PkgConfig QUIET)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
pkg_check_modules(PkgLibNghttp2 IMPORTED_TARGET GLOBAL libnghttp2)
|
||||
endif ()
|
||||
|
||||
if (PkgLibNghttp2_FOUND)
|
||||
set(LIBNGHTTP2_INCLUDE_DIR ${PkgLibNghttp2_INCLUDE_DIRS} CACHE FILEPATH "libnghttp2 include path")
|
||||
set(LIBNGHTTP2_LIBRARIES ${PkgLibNghttp2_LIBRARIES} CACHE STRING "libnghttp2 libraries")
|
||||
set(LIBNGHTTP2_VERSION ${PkgLibNghttp2_VERSION})
|
||||
add_library(Libnghttp2::Libnghttp2 ALIAS PkgConfig::PkgLibNghttp2)
|
||||
if (NOT TARGET Libnghttp2::Libnghttp2)
|
||||
message(STATUS "No Libnghttp2::Libnghttp2 target")
|
||||
add_library(Libnghttp2::Libnghttp2 UNKNOWN IMPORTED)
|
||||
set_target_properties(Libnghttp2::Libnghttp2 PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBNGHTTP2_INCLUDE_DIR}"
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${LIBNGHTTP2_LIBRARIES}"
|
||||
)
|
||||
endif ()
|
||||
set(Libnghttp2_FOUND ON)
|
||||
else ()
|
||||
find_path(LIBNGHTTP2_INCLUDE_DIR nghttp2/nghttp2.h
|
||||
HINTS
|
||||
"${LIBNGHTTP2_DIR}"
|
||||
"${LIBNGHTTP2_DIR}/include"
|
||||
)
|
||||
|
||||
find_library(LIBNGHTTP2_LIBRARIES NAMES nghttp2 libnghttp2
|
||||
HINTS
|
||||
"${LIBNGHTTP2_DIR}"
|
||||
"${LIBNGHTTP2_DIR}/lib"
|
||||
)
|
||||
|
||||
if (LIBNGHTTP2_INCLUDE_DIR AND LIBNGHTTP2_LIBRARIES)
|
||||
if (NOT TARGET Libnghttp2::Libnghttp2)
|
||||
add_library(Libnghttp2::Libnghttp2 UNKNOWN IMPORTED)
|
||||
set_target_properties(Libnghttp2::Libnghttp2 PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LIBNGHTTP2_INCLUDE_DIR}"
|
||||
IMPORTED_LINK_INTERFACE_LANGUAGES "C"
|
||||
IMPORTED_LOCATION "${LIBNGHTTP2_LIBRARIES}"
|
||||
)
|
||||
endif ()
|
||||
|
||||
if (NOT LIBNGHTTP2_VERSION AND LIBNGHTTP2_INCLUDE_DIR AND EXISTS "${LIBNGHTTP2_INCLUDE_DIR}/nghttp2/nghttp2.h")
|
||||
file(STRINGS "${LIBNGHTTP2_INCLUDE_DIR}/nghttp2/nghttp2.h" LIBNGHTTP2_H REGEX "^[ \t]*#[ \t]*define[ \t]+NGHTTP2_VERSION[ \t]")
|
||||
string(REGEX REPLACE "^.*NGHTTP2_VERSION[ \t]+\"([0-9.]+)\".*$" "\\1" LIBNGHTTP2_VERSION "${LIBNGHTTP2_H}")
|
||||
endif ()
|
||||
endif ()
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Libnghttp2
|
||||
REQUIRED_VARS LIBNGHTTP2_LIBRARIES LIBNGHTTP2_INCLUDE_DIR
|
||||
VERSION_VAR LIBNGHTTP2_VERSION
|
||||
)
|
||||
endif ()
|
||||
|
||||
mark_as_advanced(LIBNGHTTP2_INCLUDE_DIR LIBNGHTTP2_LIBRARIES)
|
|
@ -109,6 +109,7 @@ static struct const_info consts_info[] = {
|
|||
{ 902, "GETDNS_RESPSTATUS_ALL_TIMEOUT", GETDNS_RESPSTATUS_ALL_TIMEOUT_TEXT },
|
||||
{ 903, "GETDNS_RESPSTATUS_NO_SECURE_ANSWERS", GETDNS_RESPSTATUS_NO_SECURE_ANSWERS_TEXT },
|
||||
{ 904, "GETDNS_RESPSTATUS_ALL_BOGUS_ANSWERS", GETDNS_RESPSTATUS_ALL_BOGUS_ANSWERS_TEXT },
|
||||
{ 950, "GETDNS_RESPSTATUS_ALL_ERRED", GETDNS_RESPSTATUS_ALL_ERRED_TEXT },
|
||||
{ 1000, "GETDNS_EXTENSION_TRUE", GETDNS_EXTENSION_TRUE_TEXT },
|
||||
{ 1001, "GETDNS_EXTENSION_FALSE", GETDNS_EXTENSION_FALSE_TEXT },
|
||||
{ 1100, "GETDNS_BAD_DNS_CNAME_IN_TARGET", GETDNS_BAD_DNS_CNAME_IN_TARGET_TEXT },
|
||||
|
@ -272,6 +273,7 @@ static struct const_name_info consts_name_info[] = {
|
|||
{ "GETDNS_RESOLUTION_RECURSING", 521 },
|
||||
{ "GETDNS_RESOLUTION_STUB", 520 },
|
||||
{ "GETDNS_RESPSTATUS_ALL_BOGUS_ANSWERS", 904 },
|
||||
{ "GETDNS_RESPSTATUS_ALL_ERRED", 950 },
|
||||
{ "GETDNS_RESPSTATUS_ALL_TIMEOUT", 902 },
|
||||
{ "GETDNS_RESPSTATUS_GOOD", 900 },
|
||||
{ "GETDNS_RESPSTATUS_NO_NAME", 901 },
|
||||
|
|
1117
src/context.c
1117
src/context.c
File diff suppressed because it is too large
Load Diff
|
@ -40,6 +40,9 @@
|
|||
#include "getdns/getdns.h"
|
||||
#include "getdns/getdns_extra.h"
|
||||
#include "config.h"
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
#include <nghttp2/nghttp2.h>
|
||||
#endif
|
||||
#include "types-internal.h"
|
||||
#include "extension/default_eventloop.h"
|
||||
#include "util/rbtree.h"
|
||||
|
@ -132,6 +135,16 @@ typedef struct sha256_pin {
|
|||
struct sha256_pin *next;
|
||||
} sha256_pin_t;
|
||||
|
||||
/* for doing DANE authentication of TLS-capable upstreams: */
|
||||
typedef struct dane_record {
|
||||
struct dane_record *next;
|
||||
uint8_t usage;
|
||||
uint8_t selector;
|
||||
uint8_t type;
|
||||
size_t size;
|
||||
uint8_t data[];
|
||||
} dane_record_t;
|
||||
|
||||
typedef struct getdns_upstream {
|
||||
/* backpointer to containing upstreams structure */
|
||||
struct getdns_upstreams *upstreams;
|
||||
|
@ -213,6 +226,9 @@ typedef struct getdns_upstream {
|
|||
* This is how long a handshake may
|
||||
* take.
|
||||
*/
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
nghttp2_session* doh_session;
|
||||
#endif
|
||||
/* TLS settings */
|
||||
char *tls_cipher_list;
|
||||
char *tls_ciphersuites;
|
||||
|
@ -220,9 +236,14 @@ typedef struct getdns_upstream {
|
|||
getdns_tls_version_t tls_min_version;
|
||||
getdns_tls_version_t tls_max_version;
|
||||
|
||||
/* DoH settings */
|
||||
const char *alpn;
|
||||
char doh_path[256];
|
||||
|
||||
/* Auth credentials */
|
||||
char tls_auth_name[256];
|
||||
sha256_pin_t *tls_pubkey_pinset;
|
||||
dane_record_t *tls_dane_records;
|
||||
|
||||
/* When requests have been scheduled asynchronously on an upstream
|
||||
* that is kept open, and a synchronous call is then done with the
|
||||
|
@ -260,6 +281,26 @@ typedef struct getdns_upstream {
|
|||
|
||||
} getdns_upstream;
|
||||
|
||||
INLINE int is_doh_upstream(getdns_upstream *u)
|
||||
{ return u && u->alpn && u->alpn[0] == 'h'
|
||||
&& (u->alpn[1] == '2' || u->alpn[1] == '3'); }
|
||||
|
||||
#define POLICY_N_ADDR 3
|
||||
#define POLICY_N_SVCPARAMS 8
|
||||
|
||||
typedef struct getdns_proxy_policy {
|
||||
unsigned flags;
|
||||
int addr_count;
|
||||
struct sockaddr_storage addrs[POLICY_N_ADDR];
|
||||
char *domainname;
|
||||
struct
|
||||
{
|
||||
char *key;
|
||||
char *value;
|
||||
} svcparams[POLICY_N_SVCPARAMS];
|
||||
char *interface;
|
||||
} getdns_proxy_policy;
|
||||
|
||||
typedef struct getdns_log_config {
|
||||
getdns_logfunc_type func;
|
||||
void *userarg;
|
||||
|
@ -280,6 +321,15 @@ typedef struct getdns_upstreams {
|
|||
getdns_upstream upstreams[];
|
||||
} getdns_upstreams;
|
||||
|
||||
typedef struct getdns_proxy_policies {
|
||||
struct mem_funcs mf;
|
||||
size_t referenced;
|
||||
size_t count;
|
||||
uint8_t *policy_opts;
|
||||
size_t policy_opts_size;
|
||||
getdns_proxy_policy policies[];
|
||||
} getdns_proxy_policies;
|
||||
|
||||
typedef enum tas_state {
|
||||
TAS_LOOKUP_ADDRESSES = 0,
|
||||
TAS_WRITE_GET_XML,
|
||||
|
@ -368,6 +418,8 @@ struct getdns_context {
|
|||
getdns_tls_version_t tls_min_version;
|
||||
getdns_tls_version_t tls_max_version;
|
||||
|
||||
getdns_proxy_policies *proxy_policies;
|
||||
|
||||
getdns_upstreams *upstreams;
|
||||
uint16_t limit_outstanding_queries;
|
||||
uint32_t dnssec_allowed_skew;
|
||||
|
@ -388,6 +440,9 @@ struct getdns_context {
|
|||
uint8_t edns_client_subnet_private;
|
||||
uint16_t tls_query_padding_blocksize;
|
||||
_getdns_tls_context* tls_ctx;
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
nghttp2_session_callbacks* doh_callbacks;
|
||||
#endif
|
||||
|
||||
getdns_update_callback update_callback;
|
||||
getdns_update_callback2 update_callback2;
|
||||
|
|
|
@ -1126,6 +1126,8 @@ _getdns_ipaddr_dict_mf(struct mem_funcs *mf, const char *ipstr)
|
|||
char *p = strchr(ipstr, '@'), *portstr = "";
|
||||
char *t = strchr(ipstr, '#'), *tls_portstr = "";
|
||||
char *n = strchr(ipstr, '~'), *tls_namestr = "";
|
||||
char *P = strchr(ipstr, '/'), *doh_pathstr = "";
|
||||
char *A = strchr(ipstr, '_'), *alpnstr = "";
|
||||
/* ^[alg:]name:key */
|
||||
char *T = strchr(ipstr, '^'), *tsig_name_str = ""
|
||||
, *tsig_secret_str = ""
|
||||
|
@ -1173,6 +1175,14 @@ _getdns_ipaddr_dict_mf(struct mem_funcs *mf, const char *ipstr)
|
|||
*n = 0;
|
||||
tls_namestr = n + 1;
|
||||
}
|
||||
if (P) {
|
||||
*P = 0;
|
||||
doh_pathstr = P + 1;
|
||||
}
|
||||
if (A) {
|
||||
*A = 0;
|
||||
alpnstr = A + 1;
|
||||
}
|
||||
if (T) {
|
||||
*T = 0;
|
||||
tsig_name_str = T + 1;
|
||||
|
@ -1213,9 +1223,12 @@ _getdns_ipaddr_dict_mf(struct mem_funcs *mf, const char *ipstr)
|
|||
getdns_dict_set_int(r, "port", (int32_t)atoi(portstr));
|
||||
if (*tls_portstr)
|
||||
getdns_dict_set_int(r, "tls_port", (int32_t)atoi(tls_portstr));
|
||||
if (*tls_namestr) {
|
||||
if (*tls_namestr)
|
||||
getdns_dict_util_set_string(r, "tls_auth_name", tls_namestr);
|
||||
}
|
||||
if (*doh_pathstr)
|
||||
getdns_dict_util_set_string(r, "doh_path", doh_pathstr);
|
||||
if (*alpnstr)
|
||||
getdns_dict_util_set_string(r, "alpn", alpnstr);
|
||||
if (*scope_id_str)
|
||||
getdns_dict_util_set_string(r, "scope_id", scope_id_str);
|
||||
if (*tsig_name_str)
|
||||
|
|
|
@ -658,13 +658,17 @@ getdns_return_t
|
|||
getdns_dict_util_set_string(getdns_dict *dict,
|
||||
const char *name, const char *value)
|
||||
{
|
||||
static const char *nill_value = "<nil>";
|
||||
getdns_item *item;
|
||||
getdns_bindata *newbindata;
|
||||
getdns_return_t r;
|
||||
|
||||
if (!dict || !name || !value)
|
||||
if (!dict || !name)
|
||||
return GETDNS_RETURN_INVALID_PARAMETER;
|
||||
|
||||
if (!value)
|
||||
value = nill_value;
|
||||
|
||||
if (!(newbindata = _getdns_bindata_copy(
|
||||
&dict->mf, strlen(value) + 1, (uint8_t *)value)))
|
||||
return GETDNS_RETURN_MEMORY_ERROR;
|
||||
|
|
|
@ -63,6 +63,8 @@ void _getdns_call_user_callback(getdns_dns_req *dnsreq, getdns_dict *response)
|
|||
#if defined(REQ_DEBUG) && REQ_DEBUG
|
||||
debug_req(__FUNC__, *dnsreq->netreqs);
|
||||
#endif
|
||||
if (!response && dnsreq->return_call_reporting)
|
||||
response = _getdns_create_getdns_response(dnsreq);
|
||||
if (dnsreq->user_callback) {
|
||||
dnsreq->context->processing = 1;
|
||||
dnsreq->user_callback(dnsreq->context,
|
||||
|
|
|
@ -1719,6 +1719,10 @@ getdns_return_t
|
|||
getdns_context_set_upstream_recursive_servers(getdns_context *context,
|
||||
getdns_list *upstream_list);
|
||||
|
||||
getdns_return_t
|
||||
getdns_context_set_local_proxy_policy(getdns_context *context,
|
||||
getdns_dict *proxy_policy);
|
||||
|
||||
/**
|
||||
* Set the maximum UDP payload size advertised in a EDNS0 OPT record.
|
||||
* When not set (the default), outgoing values will adhere to the suggestions
|
||||
|
|
|
@ -119,6 +119,16 @@ extern "C" {
|
|||
/** @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* \addtogroup respstatus Status Codes for responses and texts
|
||||
* @{
|
||||
*/
|
||||
|
||||
#define GETDNS_RESPSTATUS_ALL_ERRED 950
|
||||
#define GETDNS_RESPSTATUS_ALL_ERRED_TEXT "All queries for the name erred"
|
||||
|
||||
/** @}
|
||||
*/
|
||||
|
||||
/**
|
||||
* \defgroup versions Version values
|
||||
|
@ -574,6 +584,8 @@ typedef enum getdns_loglevel_type {
|
|||
#define GETDNS_LOG_INFO_TEXT "Informational message"
|
||||
#define GETDNS_LOG_DEBUG_TEXT "Debug-level message"
|
||||
|
||||
#define GETDNS_LOG_UPSTREAMS 0x1000
|
||||
#define GETDNS_LOG_UPSTREAMS_TEXT "Log messages about upstreams"
|
||||
#define GETDNS_LOG_UPSTREAM_STATS 0x3000
|
||||
#define GETDNS_LOG_UPSTREAM_STATS_TEXT "Log messages about upstream statistics"
|
||||
#define GETDNS_LOG_SYS_STUB 0x2000
|
||||
|
|
|
@ -441,7 +441,9 @@ enum gldns_enum_edns_option
|
|||
GLDNS_EDNS_KEEPALIVE = 11, /* draft-ietf-dnsop-edns-tcp-keepalive*/
|
||||
GLDNS_EDNS_PADDING = 12, /* RFC7830 */
|
||||
GLDNS_EDNS_EDE = 15, /* RFC8914 */
|
||||
GLDNS_EDNS_CLIENT_TAG = 16 /* draft-bellis-dnsop-edns-tags-01 */
|
||||
GLDNS_EDNS_CLIENT_TAG = 16, /* draft-bellis-dnsop-edns-tags-01 */
|
||||
|
||||
GLDNS_EDNS_PROXY_CONTROL = 42 /* XXX unassigned */
|
||||
};
|
||||
typedef enum gldns_enum_edns_option gldns_edns_option;
|
||||
|
||||
|
|
|
@ -482,6 +482,21 @@ getdns_return_t _getdns_tls_connection_set_cipher_suites(_getdns_tls_connection*
|
|||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
getdns_return_t
|
||||
_getdns_tls_connection_set_alpn(_getdns_tls_connection* conn, const char* alpn)
|
||||
{
|
||||
if (!conn || !conn->tls)
|
||||
return GETDNS_RETURN_INVALID_PARAMETER;
|
||||
|
||||
gnutls_datum_t proto;
|
||||
proto.data = (unsigned char *)alpn;
|
||||
proto.size = strlen(alpn);
|
||||
if (gnutls_alpn_set_protocols(res->tls, &proto, 1, 0) != GNUTLS_E_SUCCESS)
|
||||
goto GETDNS_RETURN_GENERIC_ERROR;
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
getdns_return_t _getdns_tls_connection_set_curves_list(_getdns_tls_connection* conn, const char* list)
|
||||
{
|
||||
if (!conn || !conn->tls)
|
||||
|
@ -536,6 +551,23 @@ const char* _getdns_tls_connection_get_version(_getdns_tls_connection* conn)
|
|||
return gnutls_protocol_get_name(gnutls_protocol_get_version(conn->tls));
|
||||
}
|
||||
|
||||
/* CBN:TODO Implement! */
|
||||
int _getdns_tls_connection_get_pkix_auth(_getdns_tls_connection* conn)
|
||||
{
|
||||
if (!conn || !conn->ssl)
|
||||
return 0;
|
||||
|
||||
return 2 /* 2 is unknown */;
|
||||
}
|
||||
|
||||
/* CBN:TODO Implement! */
|
||||
int _getdns_tls_connection_get_pin_auth(_getdns_tls_connection* conn)
|
||||
{
|
||||
if (!conn || !conn->ssl)
|
||||
return 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
getdns_return_t _getdns_tls_connection_do_handshake(_getdns_tls_connection* conn)
|
||||
{
|
||||
int r;
|
||||
|
@ -783,7 +815,7 @@ getdns_return_t _getdns_tls_connection_read(_getdns_tls_connection* conn, uint8_
|
|||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
getdns_return_t _getdns_tls_connection_write(_getdns_tls_connection* conn, uint8_t* buf, size_t to_write, size_t* written)
|
||||
getdns_return_t _getdns_tls_connection_write(_getdns_tls_connection* conn, const uint8_t* buf, size_t to_write, size_t* written)
|
||||
{
|
||||
int swritten;
|
||||
|
||||
|
|
|
@ -807,6 +807,25 @@ getdns_return_t _getdns_tls_connection_set_curves_list(_getdns_tls_connection* c
|
|||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
getdns_return_t _getdns_tls_connection_set_alpn(_getdns_tls_connection* conn, const char* alpn)
|
||||
{
|
||||
uint8_t protos[] = "\x03" "dot";
|
||||
if (!conn || !conn->ssl)
|
||||
return GETDNS_RETURN_INVALID_PARAMETER;
|
||||
if (!alpn)
|
||||
;
|
||||
else if (strlen(alpn) > sizeof(protos) - 2)
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
else {
|
||||
strcpy((char *)(protos + 1), alpn);
|
||||
protos[0] = (uint8_t)strlen(alpn);
|
||||
}
|
||||
if (SSL_set_alpn_protos(conn->ssl, protos, protos[0] + 1))
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
else
|
||||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
getdns_return_t _getdns_tls_connection_set_session(_getdns_tls_connection* conn, _getdns_tls_session* s)
|
||||
{
|
||||
if (!conn || !conn->ssl || !s || !s->ssl)
|
||||
|
@ -842,6 +861,26 @@ const char* _getdns_tls_connection_get_version(_getdns_tls_connection* conn)
|
|||
return SSL_get_version(conn->ssl);
|
||||
}
|
||||
|
||||
int _getdns_tls_connection_get_pkix_auth(_getdns_tls_connection* conn)
|
||||
{
|
||||
uint8_t usage = 255; /* 0 and 1 for also PKIX, 2 and 3 for DANE only */
|
||||
|
||||
if (!conn || !conn->ssl)
|
||||
return 0;
|
||||
|
||||
if (SSL_get0_dane_tlsa(conn->ssl, &usage, NULL, NULL, NULL, NULL) < 0)
|
||||
return SSL_get_verify_result(conn->ssl) == X509_V_OK ? 1 : 0;
|
||||
|
||||
return usage <= 1 ? 1 : 2 /* 2 is unknown */;
|
||||
}
|
||||
|
||||
int _getdns_tls_connection_get_pin_auth(_getdns_tls_connection* conn)
|
||||
{
|
||||
if (!conn || !conn->ssl)
|
||||
return 0;
|
||||
return SSL_get0_dane_authority(conn->ssl, NULL, NULL) >= 0;
|
||||
}
|
||||
|
||||
getdns_return_t _getdns_tls_connection_do_handshake(_getdns_tls_connection* conn)
|
||||
{
|
||||
int r;
|
||||
|
@ -975,6 +1014,70 @@ getdns_return_t _getdns_tls_connection_set_host_pinset(_getdns_tls_connection* c
|
|||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
getdns_return_t _getdns_tls_connection_set_dane_records(_getdns_tls_connection* conn, const char* auth_name, const dane_record_t* dane_records)
|
||||
{
|
||||
if (!conn || !conn->ssl || !auth_name)
|
||||
return GETDNS_RETURN_INVALID_PARAMETER;
|
||||
|
||||
#if 0 && defined(USE_DANESSL)
|
||||
/* Stash auth name and pinset away for use in cert verification. */
|
||||
conn->auth_name = auth_name;
|
||||
conn->dane_records = dane_records;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_SSL_DANE_ENABLE)
|
||||
int osr = SSL_dane_enable(conn->ssl, *auth_name ? auth_name : NULL);
|
||||
(void) osr; /* unused parameter */
|
||||
DEBUG_STUB("%s %-35s: DEBUG: SSL_dane_enable(\"%s\") -> %d\n"
|
||||
, STUB_DEBUG_SETUP_TLS, __FUNC__, auth_name, osr);
|
||||
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, _getdns_tls_verify_always_ok);
|
||||
const dane_record_t *dane_p;
|
||||
size_t n_records= 0;
|
||||
for (dane_p = dane_records; dane_p; dane_p = dane_p->next) {
|
||||
osr = SSL_dane_tlsa_add(conn->ssl, dane_p->usage,
|
||||
dane_p->selector, dane_p->type, dane_p->data, dane_p->size);
|
||||
DEBUG_STUB("%s %-35s: DEBUG: SSL_dane_tlsa_add() -> %d\n"
|
||||
, STUB_DEBUG_SETUP_TLS, __FUNC__, osr);
|
||||
if (osr > 0)
|
||||
++n_records;
|
||||
}
|
||||
#elif 0 && defined(USE_DANESSL)
|
||||
conn->pinset = pinset;
|
||||
if (pinset) {
|
||||
const char *auth_names[2] = { auth_name, NULL };
|
||||
int osr = DANESSL_init(conn->ssl,
|
||||
*auth_name ? auth_name : NULL,
|
||||
*auth_name ? auth_names : NULL);
|
||||
(void) osr; /* unused parameter */
|
||||
DEBUG_STUB("%s %-35s: DEBUG: DANESSL_init(\"%s\") -> %d\n"
|
||||
, STUB_DEBUG_SETUP_TLS, __FUNC__, auth_name, osr);
|
||||
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, _getdns_tls_verify_always_ok);
|
||||
const sha256_pin_t *pin_p;
|
||||
size_t n_pins = 0;
|
||||
for (pin_p = pinset; pin_p; pin_p = pin_p->next) {
|
||||
osr = DANESSL_add_tlsa(conn->ssl, 3, 1, "sha256",
|
||||
(unsigned char *)pin_p->pin, SHA256_DIGEST_LENGTH);
|
||||
DEBUG_STUB("%s %-35s: DEBUG: DANESSL_add_tlsa() -> %d\n"
|
||||
, STUB_DEBUG_SETUP_TLS, __FUNC__, osr);
|
||||
if (osr > 0)
|
||||
++n_pins;
|
||||
osr = DANESSL_add_tlsa(conn->ssl, 2, 1, "sha256",
|
||||
(unsigned char *)pin_p->pin, SHA256_DIGEST_LENGTH);
|
||||
DEBUG_STUB("%s %-35s: DEBUG: DANESSL_add_tlsa() -> %d\n"
|
||||
, STUB_DEBUG_SETUP_TLS, __FUNC__, osr);
|
||||
if (osr > 0)
|
||||
++n_pins;
|
||||
}
|
||||
} else {
|
||||
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, _getdns_tls_verify_always_ok);
|
||||
}
|
||||
#else
|
||||
#error Must have either DANE SSL or OpenSSL v1.1.
|
||||
#endif
|
||||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
|
||||
getdns_return_t _getdns_tls_connection_certificate_verify(_getdns_tls_connection* conn, long* errnum, const char** errmsg)
|
||||
{
|
||||
if (!conn || !conn->ssl)
|
||||
|
@ -1071,7 +1174,7 @@ getdns_return_t _getdns_tls_connection_read(_getdns_tls_connection* conn, uint8_
|
|||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
getdns_return_t _getdns_tls_connection_write(_getdns_tls_connection* conn, uint8_t* buf, size_t to_write, size_t* written)
|
||||
getdns_return_t _getdns_tls_connection_write(_getdns_tls_connection* conn, const uint8_t* buf, size_t to_write, size_t* written)
|
||||
{
|
||||
int swritten;
|
||||
|
||||
|
|
|
@ -218,6 +218,11 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner,
|
|||
net_req->debug_tls_peer_cert.size = 0;
|
||||
net_req->debug_tls_peer_cert.data = NULL;
|
||||
net_req->debug_tls_version = NULL;
|
||||
net_req->debug_pkix_auth = 0; /* 1 == authenticated with PKIX
|
||||
* 0 == not authenticated with PKIX
|
||||
* 2 == unknown
|
||||
*/
|
||||
net_req->debug_pin_auth = 0; /* == 1 if authenticated with pinset */
|
||||
net_req->debug_udp = 0;
|
||||
|
||||
/* Scheduling, touch only via _getdns_netreq_change_state!
|
||||
|
@ -403,6 +408,51 @@ _getdns_network_req_add_upstream_option(getdns_network_req * req, uint16_t code,
|
|||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
/* add_upstream_options appends multiple options. */
|
||||
getdns_return_t
|
||||
_getdns_network_req_add_upstream_options(getdns_network_req * req, const void* data, size_t sz)
|
||||
{
|
||||
uint16_t oldlen;
|
||||
uint32_t newlen;
|
||||
uint32_t pktlen;
|
||||
size_t cur_upstream_option_sz;
|
||||
|
||||
/* if no options are set, we can't add upstream options */
|
||||
if (!req->opt)
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
|
||||
/* if TCP, no overflow allowed for length field
|
||||
https://tools.ietf.org/html/rfc1035#section-4.2.2 */
|
||||
pktlen = req->response - req->query;
|
||||
pktlen += sz;
|
||||
if (pktlen > UINT16_MAX)
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
|
||||
/* no overflow allowed for OPT size either (maybe this is overkill
|
||||
given the above check?) */
|
||||
oldlen = gldns_read_uint16(req->opt + 9);
|
||||
newlen = oldlen + sz;
|
||||
if (newlen > UINT16_MAX)
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
|
||||
/* avoid overflowing MAXIMUM_UPSTREAM_OPTION_SPACE */
|
||||
cur_upstream_option_sz = (size_t)oldlen - req->base_query_option_sz;
|
||||
if (cur_upstream_option_sz + sz > MAXIMUM_UPSTREAM_OPTION_SPACE)
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
|
||||
/* actually add the option: */
|
||||
memcpy(req->opt + 11 + oldlen, data, sz);
|
||||
gldns_write_uint16(req->opt + 9, newlen);
|
||||
|
||||
/* the response should start right after the options end: */
|
||||
req->response = req->opt + 11 + newlen;
|
||||
|
||||
/* for TCP, adjust the size of the wire format itself: */
|
||||
gldns_write_uint16(req->query - 2, pktlen);
|
||||
|
||||
return GETDNS_RETURN_GOOD;
|
||||
}
|
||||
|
||||
size_t
|
||||
_getdns_network_req_add_tsig(getdns_network_req *req)
|
||||
{
|
||||
|
|
587
src/stub.c
587
src/stub.c
|
@ -50,6 +50,7 @@
|
|||
#include "gldns/rrdef.h"
|
||||
#include "gldns/str2wire.h"
|
||||
#include "gldns/wire2str.h"
|
||||
#include "gldns/parseutil.h"
|
||||
#include "rr-iter.h"
|
||||
#include "context.h"
|
||||
#include "util-internal.h"
|
||||
|
@ -57,6 +58,15 @@
|
|||
#include "general.h"
|
||||
#include "pubkey-pinning.h"
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
#define MAKE_NV(NAME, VALUE, VALUELEN) { (uint8_t *)NAME, (uint8_t *)VALUE, \
|
||||
sizeof(NAME) - 1, VALUELEN, NGHTTP2_NV_FLAG_NONE }
|
||||
|
||||
#define MAKE_NV2(NAME, VALUE) { (uint8_t *)NAME, (uint8_t *)VALUE, \
|
||||
sizeof(NAME) - 1, sizeof(VALUE) - 1, NGHTTP2_NV_FLAG_NONE }
|
||||
|
||||
#define ARRLEN(x) (sizeof(x) / sizeof(x[0]))
|
||||
#endif
|
||||
|
||||
/* WSA TODO:
|
||||
* STUB_TCP_RETRY added to deal with edge triggered event loops (versus
|
||||
|
@ -71,6 +81,7 @@
|
|||
#define STUB_TCP_MORE_TO_READ -3
|
||||
#define STUB_TCP_MORE_TO_WRITE -3
|
||||
#define STUB_TCP_ERROR -2
|
||||
#define STUB_NOOP -1
|
||||
|
||||
/* Don't currently have access to the context whilst doing handshake */
|
||||
#define MIN_TLS_HS_TIMEOUT 2500
|
||||
|
@ -94,6 +105,10 @@ static int fallback_on_write(getdns_network_req *netreq);
|
|||
|
||||
static void stub_timeout_cb(void *userarg);
|
||||
uint64_t _getdns_get_time_as_uintt64();
|
||||
static void netreq_equip_tls_debug_info(getdns_network_req *netreq);
|
||||
static void process_finished_cb(void *userarg);
|
||||
|
||||
|
||||
/*****************************/
|
||||
/* General utility functions */
|
||||
/*****************************/
|
||||
|
@ -177,6 +192,15 @@ attach_edns_cookie(getdns_network_req *req)
|
|||
req, EDNS_COOKIE_OPCODE, 8, req->client_cookie);
|
||||
}
|
||||
|
||||
static getdns_return_t
|
||||
attach_proxy_policies_opt(getdns_network_req *req)
|
||||
{
|
||||
fprintf(stderr, "attach_proxy_policies_opt: adding option\n");
|
||||
return _getdns_network_req_add_upstream_options(req,
|
||||
req->owner->context->proxy_policies->policy_opts,
|
||||
req->owner->context->proxy_policies->policy_opts_size);
|
||||
}
|
||||
|
||||
/* Will find a matching OPT RR, but leaves the caller to validate it
|
||||
*/
|
||||
#define MATCH_OPT_FOUND 2
|
||||
|
@ -660,10 +684,29 @@ stub_cleanup(getdns_network_req *netreq)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
upstream_teardown(getdns_upstream *upstream)
|
||||
{
|
||||
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
||||
upstream->conn_state = GETDNS_CONN_TEARDOWN;
|
||||
|
||||
while (upstream->write_queue)
|
||||
upstream_write_cb(upstream);
|
||||
|
||||
while (upstream->netreq_by_query_id.count) {
|
||||
getdns_network_req *netreq = (getdns_network_req *)
|
||||
_getdns_rbtree_first(&upstream->netreq_by_query_id);
|
||||
|
||||
stub_cleanup(netreq);
|
||||
_getdns_netreq_change_state(netreq, NET_REQ_ERRORED);
|
||||
_getdns_check_dns_req_complete(netreq->owner);
|
||||
}
|
||||
_getdns_upstream_shutdown(upstream);
|
||||
}
|
||||
|
||||
static void
|
||||
upstream_failed(getdns_upstream *upstream, int during_setup)
|
||||
{
|
||||
getdns_network_req *netreq;
|
||||
|
||||
DEBUG_STUB("%s %-35s: FD: %d Failure during connection setup = %d\n",
|
||||
STUB_DEBUG_CLEANUP, __FUNC__, upstream->fd, during_setup);
|
||||
|
@ -671,26 +714,13 @@ upstream_failed(getdns_upstream *upstream, int during_setup)
|
|||
when idle.*/
|
||||
/* [TLS1]TODO: Work out how to re-open the connection and re-try
|
||||
the queries if there is only one upstream.*/
|
||||
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
||||
if (during_setup) {
|
||||
upstream->conn_setup_failed++;
|
||||
} else {
|
||||
upstream->conn_shutdowns++;
|
||||
/* [TLS1]TODO: Re-try these queries if possible.*/
|
||||
}
|
||||
upstream->conn_state = GETDNS_CONN_TEARDOWN;
|
||||
|
||||
while (upstream->write_queue)
|
||||
upstream_write_cb(upstream);
|
||||
|
||||
while (upstream->netreq_by_query_id.count) {
|
||||
netreq = (getdns_network_req *)
|
||||
_getdns_rbtree_first(&upstream->netreq_by_query_id);
|
||||
stub_cleanup(netreq);
|
||||
_getdns_netreq_change_state(netreq, NET_REQ_ERRORED);
|
||||
_getdns_check_dns_req_complete(netreq->owner);
|
||||
}
|
||||
_getdns_upstream_shutdown(upstream);
|
||||
upstream_teardown(upstream);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -845,6 +875,7 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq)
|
|||
intptr_t query_id_intptr;
|
||||
|
||||
int q = tcp_connected(netreq->upstream);
|
||||
fprintf(stderr, "in stub_tcp_write\n");
|
||||
if (q != 0)
|
||||
return q;
|
||||
|
||||
|
@ -875,6 +906,10 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq)
|
|||
if (netreq->owner->edns_client_subnet_private)
|
||||
if (attach_edns_client_subnet_private(netreq))
|
||||
return STUB_OUT_OF_OPTIONS;
|
||||
fprintf(stderr, "stub_tcp_write: before proxy_policies check\n");
|
||||
if (netreq->owner->context->proxy_policies)
|
||||
if (attach_proxy_policies_opt(netreq))
|
||||
return STUB_OUT_OF_OPTIONS;
|
||||
if (netreq->upstream->queries_sent == 0 &&
|
||||
netreq->owner->context->idle_timeout != 0) {
|
||||
/* Add the keepalive option to the first query on this connection*/
|
||||
|
@ -980,6 +1015,7 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
|
|||
{
|
||||
/* Create SSL instance and connect with a file descriptor */
|
||||
getdns_context *context = dnsreq->context;
|
||||
|
||||
if (context->tls_ctx == NULL)
|
||||
return NULL;
|
||||
_getdns_tls_connection* tls = _getdns_tls_connection_new(&context->my_mf, context->tls_ctx, fd, &upstream->upstreams->log);
|
||||
|
@ -988,21 +1024,46 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
|
|||
|
||||
getdns_return_t r = GETDNS_RETURN_GOOD;
|
||||
|
||||
if (upstream->tls_curves_list)
|
||||
r = _getdns_tls_connection_set_curves_list(tls, upstream->tls_curves_list);
|
||||
if (!r && upstream->tls_ciphersuites)
|
||||
r = _getdns_tls_connection_set_cipher_suites(tls, upstream->tls_ciphersuites);
|
||||
if (!r)
|
||||
r = _getdns_tls_connection_set_min_max_tls_version(tls, upstream->tls_min_version, upstream->tls_max_version);
|
||||
if (upstream->tls_curves_list
|
||||
&& (r = _getdns_tls_connection_set_curves_list(tls, upstream->tls_curves_list)))
|
||||
; /* pass */
|
||||
else if (upstream->tls_ciphersuites
|
||||
&& (r = _getdns_tls_connection_set_cipher_suites(tls, upstream->tls_ciphersuites)))
|
||||
; /* pass */
|
||||
else if ((r =_getdns_tls_connection_set_min_max_tls_version(tls, upstream->tls_min_version, upstream->tls_max_version)))
|
||||
; /* pass */
|
||||
else if (upstream->tls_fallback_ok
|
||||
&& (r = _getdns_tls_connection_set_cipher_list(tls, NULL)))
|
||||
; /* pass */
|
||||
else if (upstream->tls_cipher_list
|
||||
&& (r = _getdns_tls_connection_set_cipher_list(tls, upstream->tls_cipher_list)))
|
||||
; /* pass */
|
||||
else if ((r = _getdns_tls_connection_set_alpn(tls, upstream->alpn)))
|
||||
; /* pass */
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
else if (!is_doh_upstream(upstream) || !context->doh_callbacks)
|
||||
; /* pass */
|
||||
else if (nghttp2_session_client_new(&upstream->doh_session
|
||||
, context->doh_callbacks
|
||||
, upstream))
|
||||
r = GETDNS_RETURN_MEMORY_ERROR;
|
||||
else {
|
||||
int rv;
|
||||
nghttp2_settings_entry iv[1] = {
|
||||
{NGHTTP2_SETTINGS_MAX_CONCURRENT_STREAMS, 100}};
|
||||
|
||||
if (!r)
|
||||
{
|
||||
if (upstream->tls_fallback_ok)
|
||||
r = _getdns_tls_connection_set_cipher_list(tls, NULL);
|
||||
else if (upstream->tls_cipher_list)
|
||||
r = _getdns_tls_connection_set_cipher_list(tls, upstream->tls_cipher_list);
|
||||
/* client 24 bytes magic string will be sent by nghttp2 library */
|
||||
if ((rv = nghttp2_submit_settings(upstream->doh_session,
|
||||
NGHTTP2_FLAG_NONE, iv, ARRLEN(iv)))) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR,
|
||||
"%-40s : Could not submit DoH settings: %s\n",
|
||||
upstream->addr_str,
|
||||
nghttp2_strerror(rv));
|
||||
r = GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
if (r) {
|
||||
_getdns_tls_connection_free(&upstream->upstreams->mf, tls);
|
||||
upstream->tls_auth_state = GETDNS_AUTH_NONE;
|
||||
|
@ -1035,8 +1096,9 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
|
|||
/* Lack of host name is OK unless only authenticated
|
||||
* TLS is specified and we have no pubkey_pinset */
|
||||
if (dnsreq->netreqs[0]->tls_auth_min == GETDNS_AUTHENTICATION_REQUIRED) {
|
||||
if (upstream->tls_pubkey_pinset) {
|
||||
DEBUG_STUB("%s %-35s: Proceeding with only pubkey pinning authentication\n",
|
||||
if (upstream->tls_pubkey_pinset
|
||||
|| upstream->tls_dane_records) {
|
||||
DEBUG_STUB("%s %-35s: Proceeding with only pubkey pinning and/or DANE authentication\n",
|
||||
STUB_DEBUG_SETUP_TLS, __FUNC__);
|
||||
} else {
|
||||
DEBUG_STUB("%s %-35s: ERROR:No auth name or pinset provided for this upstream for Strict TLS authentication\n",
|
||||
|
@ -1061,6 +1123,10 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
|
|||
_getdns_tls_connection_set_host_pinset(
|
||||
tls, upstream->tls_auth_name, upstream->tls_pubkey_pinset);
|
||||
|
||||
if (upstream->tls_dane_records)
|
||||
_getdns_tls_connection_set_dane_records(
|
||||
tls, upstream->tls_auth_name, upstream->tls_dane_records);
|
||||
|
||||
/* Session resumption. There are trade-offs here. Want to do it when
|
||||
possible only if we have the right type of connection. Note a change
|
||||
to the upstream auth info creates a new upstream so never re-uses.*/
|
||||
|
@ -1216,6 +1282,182 @@ tls_connected(getdns_upstream* upstream)
|
|||
return tls_do_handshake(upstream);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
ssize_t
|
||||
_doh_send_callback(nghttp2_session *session, const uint8_t *data, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
getdns_upstream *upstream = (getdns_upstream *)user_data;
|
||||
size_t written;
|
||||
getdns_return_t r;
|
||||
|
||||
(void)session;
|
||||
(void)flags;
|
||||
|
||||
if (!upstream || !upstream->doh_session || !upstream->tls_obj)
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
if (!(r = _getdns_tls_connection_write(
|
||||
upstream->tls_obj, data, length, &written)))
|
||||
return (ssize_t)written;
|
||||
return ( r == GETDNS_RETURN_TLS_WANT_READ
|
||||
|| r == GETDNS_RETURN_TLS_WANT_WRITE )
|
||||
? NGHTTP2_ERR_WOULDBLOCK
|
||||
: NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
_doh_recv_callback(nghttp2_session *session, uint8_t *buf, size_t length,
|
||||
int flags, void *user_data)
|
||||
{
|
||||
getdns_upstream *upstream = (getdns_upstream *)user_data;
|
||||
size_t read;
|
||||
getdns_return_t r;
|
||||
|
||||
(void)session;
|
||||
(void)flags;
|
||||
|
||||
if (!upstream || !upstream->doh_session || !upstream->tls_obj)
|
||||
return NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
|
||||
if (!(r = _getdns_tls_connection_read(
|
||||
upstream->tls_obj, buf, length, &read)))
|
||||
return (ssize_t)read;
|
||||
return ( r == GETDNS_RETURN_TLS_WANT_READ
|
||||
|| r == GETDNS_RETURN_TLS_WANT_WRITE )
|
||||
? NGHTTP2_ERR_WOULDBLOCK
|
||||
: NGHTTP2_ERR_CALLBACK_FAILURE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_doh_on_data_chunk_recv_callback(nghttp2_session *session, uint8_t flags,
|
||||
int32_t stream_id, const uint8_t *data, size_t len, void *user_data)
|
||||
{
|
||||
getdns_upstream *upstream = (getdns_upstream *)user_data;
|
||||
intptr_t stream_id_intptr = (intptr_t)stream_id;
|
||||
getdns_network_req *netreq;
|
||||
getdns_dns_req *dnsreq;
|
||||
|
||||
(void)session;
|
||||
(void)flags;
|
||||
|
||||
netreq = (getdns_network_req *)_getdns_rbtree_search(
|
||||
&upstream->netreq_by_query_id, (void *)stream_id_intptr);
|
||||
if (!netreq) /* Netreq might have been canceled (so okay!) */
|
||||
return 0;
|
||||
|
||||
if (netreq->query_id_registered == &upstream->netreq_by_query_id) {
|
||||
netreq->query_id_registered = NULL;
|
||||
netreq->node.key = NULL;
|
||||
|
||||
} else if (netreq->query_id_registered) {
|
||||
(void) _getdns_rbtree_delete(
|
||||
netreq->query_id_registered, netreq->node.key);
|
||||
netreq->query_id_registered = NULL;
|
||||
netreq->node.key = NULL;
|
||||
}
|
||||
DEBUG_STUB("%s %-35s: MSG: %p (read)\n",
|
||||
STUB_DEBUG_READ, __FUNC__, (void*)netreq);
|
||||
_getdns_netreq_change_state(netreq, NET_REQ_FINISHED);
|
||||
if (netreq->response < netreq->wire_data
|
||||
|| netreq->response + len > netreq->wire_data + netreq->wire_data_sz)
|
||||
netreq->response = GETDNS_XMALLOC(
|
||||
upstream->upstreams->mf, uint8_t, len);
|
||||
memcpy(netreq->response, data, (netreq->response_len = len));
|
||||
upstream->responses_received++;
|
||||
|
||||
if (netreq->owner->edns_cookies &&
|
||||
match_and_process_server_cookie(netreq->upstream, netreq))
|
||||
return 0; /* Client cookie didn't match (or FORMERR) */
|
||||
|
||||
if (netreq->owner->context->idle_timeout != 0)
|
||||
process_keepalive(netreq->upstream, netreq);
|
||||
|
||||
netreq->debug_end_time = _getdns_get_time_as_uintt64();
|
||||
/* This also reschedules events for the upstream */
|
||||
stub_cleanup(netreq);
|
||||
|
||||
if (!upstream->is_sync_loop || netreq->owner->is_sync_request)
|
||||
_getdns_check_dns_req_complete(netreq->owner);
|
||||
|
||||
else {
|
||||
assert(upstream->is_sync_loop &&
|
||||
!netreq->owner->is_sync_request);
|
||||
|
||||
/* We have a result for an asynchronously scheduled
|
||||
* netreq, while processing the synchronous loop.
|
||||
* Queue dns_req_complete checks.
|
||||
*/
|
||||
|
||||
/* First check if one for the dns_req already exists */
|
||||
for ( dnsreq = upstream->finished_dnsreqs
|
||||
; dnsreq && dnsreq != netreq->owner
|
||||
; dnsreq = dnsreq->finished_next)
|
||||
; /* pass */
|
||||
|
||||
if (!dnsreq) {
|
||||
/* Schedule dns_req_complete check for this
|
||||
* netreq's owner
|
||||
*/
|
||||
dnsreq = netreq->owner;
|
||||
dnsreq->finished_next =
|
||||
upstream->finished_dnsreqs;
|
||||
upstream->finished_dnsreqs = dnsreq;
|
||||
|
||||
if (!upstream->finished_event.timeout_cb) {
|
||||
upstream->finished_event.timeout_cb
|
||||
= process_finished_cb;
|
||||
GETDNS_SCHEDULE_EVENT(
|
||||
dnsreq->context->extension,
|
||||
-1, 1, &upstream->finished_event);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_doh_on_header_callback (nghttp2_session *session, const nghttp2_frame *frame,
|
||||
const uint8_t *name, size_t namelen, const uint8_t *value, size_t valuelen,
|
||||
uint8_t flags, void *user_data)
|
||||
{
|
||||
getdns_upstream *upstream = (getdns_upstream *)user_data;
|
||||
intptr_t stream_id_intptr;
|
||||
getdns_network_req *netreq;
|
||||
|
||||
(void)session;
|
||||
(void)flags;
|
||||
|
||||
if (frame->hd.type != NGHTTP2_HEADERS
|
||||
|| frame->headers.cat != NGHTTP2_HCAT_RESPONSE)
|
||||
return 0;
|
||||
#if 0
|
||||
fprintf(stderr, "incoming header: ");
|
||||
fwrite(name, 1, namelen, stderr);
|
||||
fprintf(stderr, ": ");
|
||||
fwrite(value, 1, valuelen, stderr);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
if (namelen == 7 && !strncasecmp((const char *)name, ":status", 7)) {
|
||||
if (valuelen == 3 && !strncmp((const char *)value, "200", 3))
|
||||
return 0;
|
||||
|
||||
stream_id_intptr = (intptr_t)frame->hd.stream_id;
|
||||
netreq = (getdns_network_req *)_getdns_rbtree_search(
|
||||
&upstream->netreq_by_query_id, (void *)stream_id_intptr);
|
||||
if (!netreq) /* Netreq might have been canceled (so okay!) */
|
||||
return 0;
|
||||
|
||||
stub_cleanup(netreq);
|
||||
_getdns_netreq_change_state(netreq, NET_REQ_ERRORED);
|
||||
netreq->debug_end_time = _getdns_get_time_as_uintt64();
|
||||
_getdns_check_dns_req_complete(netreq->owner);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************/
|
||||
/* TLS read/write functions*/
|
||||
/***************************/
|
||||
|
@ -1314,7 +1556,8 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp,
|
|||
|
||||
int q;
|
||||
|
||||
if (netreq->owner->expires > upstream->expires)
|
||||
fprintf(stderr, "in stub_tls_write\n");
|
||||
if (netreq && netreq->owner->expires > upstream->expires)
|
||||
upstream->expires = netreq->owner->expires;
|
||||
|
||||
q = tls_connected(upstream);
|
||||
|
@ -1332,21 +1575,6 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp,
|
|||
/* No, this is an initial write. Try to send
|
||||
*/
|
||||
|
||||
/* Find a unique query_id not already written (or in
|
||||
* the write_queue) for that upstream. Register this netreq
|
||||
* by query_id in the process.
|
||||
*/
|
||||
do {
|
||||
query_id = arc4random();
|
||||
query_id_intptr = (intptr_t)query_id;
|
||||
netreq->node.key = (void *)query_id_intptr;
|
||||
|
||||
} while (!_getdns_rbtree_insert(
|
||||
&netreq->upstream->netreq_by_query_id, &netreq->node));
|
||||
netreq->query_id_registered = &netreq->upstream->netreq_by_query_id;
|
||||
|
||||
GLDNS_ID_SET(netreq->query, query_id);
|
||||
|
||||
/* TODO: Review if more EDNS0 handling can be centralised.*/
|
||||
if (netreq->opt) {
|
||||
_getdns_network_req_clear_upstream_options(netreq);
|
||||
|
@ -1408,7 +1636,96 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp,
|
|||
r = GETDNS_RETURN_GOOD;
|
||||
} else
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
if (upstream->doh_session) {
|
||||
size_t i;
|
||||
nghttp2_nv hdrs[5];
|
||||
const char *authority = upstream->tls_auth_name[0]
|
||||
? upstream->tls_auth_name
|
||||
: upstream->addr_str;
|
||||
char path_spc[2048], *path = path_spc;
|
||||
size_t url_sz, path_sz;
|
||||
int32_t stream_id;
|
||||
|
||||
url_sz = sizeof("/") - 1
|
||||
+ strlen(upstream->doh_path)
|
||||
+ sizeof("?dns=") - 1;
|
||||
path_sz = url_sz
|
||||
+ gldns_b64_ntop_calculate_size(pkt_len);
|
||||
|
||||
if (path_sz > sizeof(path_spc)
|
||||
&& !(path = malloc(path_sz)))
|
||||
return STUB_TCP_ERROR;
|
||||
|
||||
strcat(strcat( strcpy(path, "/")
|
||||
, upstream->doh_path), "?dns=");
|
||||
|
||||
path_sz = url_sz + gldns_b64url_ntop(netreq->query,
|
||||
pkt_len, path + url_sz, path_sz - url_sz);
|
||||
path[path_sz] = 0;
|
||||
|
||||
GLDNS_ID_SET(netreq->query, 0);
|
||||
|
||||
hdrs[0].name = (uint8_t*)":method";
|
||||
hdrs[0].value = (uint8_t*)"GET";
|
||||
hdrs[1].name = (uint8_t*)":scheme";
|
||||
hdrs[1].value = (uint8_t*)"https";
|
||||
hdrs[2].name = (uint8_t*)":authority";
|
||||
hdrs[2].value = (uint8_t*)authority;
|
||||
hdrs[3].name = (uint8_t*)":path";
|
||||
hdrs[3].value = (uint8_t*)path;
|
||||
hdrs[4].name = (uint8_t*)"content-type";
|
||||
hdrs[4].value = (uint8_t*)"application/dns-message";
|
||||
for (i = 0; i < ARRLEN(hdrs); i++) {
|
||||
hdrs[i].namelen = strlen((char*)hdrs[i].name);
|
||||
hdrs[i].valuelen = strlen((char*)hdrs[i].value);
|
||||
hdrs[i].flags = NGHTTP2_NV_FLAG_NONE;
|
||||
fprintf(stderr, "HEADER[%d] %s: %s\n"
|
||||
, (int)i
|
||||
, (char*)hdrs[i].name
|
||||
, (char*)hdrs[i].value);
|
||||
}
|
||||
stream_id = nghttp2_submit_request(
|
||||
upstream->doh_session, NULL,
|
||||
hdrs, ARRLEN(hdrs), NULL, upstream);
|
||||
|
||||
if (stream_id < 0) {
|
||||
/* TODO: Log error */
|
||||
return STUB_TCP_ERROR;
|
||||
}
|
||||
query_id_intptr = (intptr_t)stream_id;
|
||||
netreq->node.key = (void *)query_id_intptr;
|
||||
_getdns_rbtree_insert(
|
||||
&netreq->upstream->netreq_by_query_id,
|
||||
&netreq->node);
|
||||
netreq->query_id_registered =
|
||||
&netreq->upstream->netreq_by_query_id;
|
||||
/* Unqueue the netreq from the write_queue */
|
||||
remove_from_write_queue(upstream, netreq);
|
||||
if (netreq->owner->return_call_reporting)
|
||||
netreq_equip_tls_debug_info(netreq);
|
||||
upstream->queries_sent++;
|
||||
return STUB_NOOP;
|
||||
} else {
|
||||
#endif
|
||||
/* Find a unique query_id not already written (or in
|
||||
* the write_queue) for that upstream. Register this netreq
|
||||
* by query_id in the process.
|
||||
*/
|
||||
do {
|
||||
query_id = arc4random();
|
||||
query_id_intptr = (intptr_t)query_id;
|
||||
netreq->node.key = (void *)query_id_intptr;
|
||||
|
||||
} while (!_getdns_rbtree_insert(
|
||||
&netreq->upstream->netreq_by_query_id, &netreq->node));
|
||||
netreq->query_id_registered = &netreq->upstream->netreq_by_query_id;
|
||||
GLDNS_ID_SET(netreq->query, query_id);
|
||||
r = _getdns_tls_connection_write(tls_obj, netreq->query - 2, pkt_len + 2, &written);
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
}
|
||||
#endif
|
||||
if (r == GETDNS_RETURN_TLS_WANT_READ ||
|
||||
r == GETDNS_RETURN_TLS_WANT_WRITE)
|
||||
return STUB_TCP_RETRY;
|
||||
|
@ -1417,9 +1734,7 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp,
|
|||
|
||||
/* We were able to write everything! Start reading. */
|
||||
return (int) query_id;
|
||||
|
||||
}
|
||||
|
||||
return STUB_TCP_ERROR;
|
||||
}
|
||||
|
||||
|
@ -1569,6 +1884,7 @@ stub_udp_write_cb(void *userarg)
|
|||
ssize_t written;
|
||||
DEBUG_STUB("%s %-35s: MSG: %p \n", STUB_DEBUG_WRITE,
|
||||
__FUNC__, (void *)netreq);
|
||||
fprintf(stderr, "in stub_udp_write_cb\n");
|
||||
|
||||
GETDNS_CLEAR_EVENT(dnsreq->loop, &netreq->event);
|
||||
|
||||
|
@ -1588,6 +1904,9 @@ stub_udp_write_cb(void *userarg)
|
|||
if (netreq->owner->edns_client_subnet_private)
|
||||
if (attach_edns_client_subnet_private(netreq))
|
||||
return; /* too many upstream options */
|
||||
if (netreq->owner->context->proxy_policies)
|
||||
if (attach_proxy_policies_opt(netreq))
|
||||
return; /* too many upstream options */
|
||||
}
|
||||
pkt_len = _getdns_network_req_add_tsig(netreq);
|
||||
if ((ssize_t)pkt_len != (written = sendto(
|
||||
|
@ -1652,12 +1971,57 @@ upstream_read_cb(void *userarg)
|
|||
DEBUG_STUB("%s %-35s: FD: %d \n", STUB_DEBUG_READ, __FUNC__,
|
||||
upstream->fd);
|
||||
getdns_network_req *netreq;
|
||||
int q;
|
||||
int q = STUB_NOOP;
|
||||
uint16_t query_id;
|
||||
intptr_t query_id_intptr;
|
||||
getdns_dns_req *dnsreq;
|
||||
|
||||
if (upstream->transport == GETDNS_TRANSPORT_TLS)
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
if (!upstream->doh_session)
|
||||
; /* pass */
|
||||
|
||||
else if ((q = tls_connected(upstream)))
|
||||
; /* pass */
|
||||
|
||||
else if (nghttp2_session_want_read(upstream->doh_session)) {
|
||||
int rv = nghttp2_session_recv(upstream->doh_session);
|
||||
|
||||
if (rv) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR,
|
||||
"%-40s : Could not receive from DoH connection: %s\n",
|
||||
upstream->addr_str,
|
||||
nghttp2_strerror(rv));
|
||||
q = STUB_TCP_ERROR;
|
||||
|
||||
} else if (nghttp2_session_want_read(upstream->doh_session))
|
||||
return;
|
||||
|
||||
else if (nghttp2_session_want_write(upstream->doh_session)) {
|
||||
/* Reschedule for reading */
|
||||
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
||||
upstream->event.read_cb = upstream_read_cb;
|
||||
upstream->event.write_cb = upstream_write_cb;
|
||||
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
||||
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
||||
return;
|
||||
} else
|
||||
q = STUB_CONN_GONE;
|
||||
|
||||
} else if (nghttp2_session_want_write(upstream->doh_session)) {
|
||||
/* Reschedule for reading */
|
||||
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
||||
upstream->event.read_cb = upstream_read_cb;
|
||||
upstream->event.write_cb = upstream_write_cb;
|
||||
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
||||
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
||||
return;
|
||||
} else
|
||||
q = STUB_CONN_GONE;
|
||||
#endif
|
||||
if (q != STUB_NOOP)
|
||||
; /* pass */
|
||||
else if (upstream->transport == GETDNS_TRANSPORT_TLS)
|
||||
q = stub_tls_read(upstream, &upstream->tcp,
|
||||
&upstream->upstreams->mf);
|
||||
else
|
||||
|
@ -1674,7 +2038,9 @@ upstream_read_cb(void *userarg)
|
|||
case STUB_TCP_ERROR:
|
||||
upstream_failed(upstream, (q == STUB_TCP_ERROR ? 0:1) );
|
||||
return;
|
||||
|
||||
case STUB_CONN_GONE:
|
||||
upstream_teardown(upstream);
|
||||
return;
|
||||
default:
|
||||
/* Lookup netreq */
|
||||
query_id = (uint16_t) q;
|
||||
|
@ -1760,24 +2126,57 @@ upstream_read_cb(void *userarg)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
netreq_equip_tls_debug_info(getdns_network_req *netreq)
|
||||
{
|
||||
_getdns_tls_x509 *cert;
|
||||
|
||||
if (!netreq || !netreq->upstream)
|
||||
return;
|
||||
netreq->debug_tls_auth_status = netreq->upstream->tls_auth_state;
|
||||
if (!netreq->upstream->tls_obj)
|
||||
return;
|
||||
if (netreq->debug_tls_peer_cert.data) {
|
||||
GETDNS_FREE( netreq->owner->my_mf
|
||||
, netreq->debug_tls_peer_cert.data);
|
||||
netreq->debug_tls_peer_cert.data = NULL;
|
||||
netreq->debug_tls_peer_cert.size = 0;
|
||||
}
|
||||
if ((cert = _getdns_tls_connection_get_peer_certificate(
|
||||
&netreq->owner->my_mf, netreq->upstream->tls_obj))) {
|
||||
_getdns_tls_x509_to_der( &netreq->owner->my_mf, cert
|
||||
, &netreq->debug_tls_peer_cert);
|
||||
_getdns_tls_x509_free(&netreq->owner->my_mf, cert);
|
||||
}
|
||||
netreq->debug_tls_version = _getdns_tls_connection_get_version(
|
||||
netreq->upstream->tls_obj);
|
||||
netreq->debug_pkix_auth = _getdns_tls_connection_get_pkix_auth(
|
||||
netreq->upstream->tls_obj);
|
||||
netreq->debug_pin_auth = _getdns_tls_connection_get_pin_auth(
|
||||
netreq->upstream->tls_obj);
|
||||
}
|
||||
|
||||
static void
|
||||
upstream_write_cb(void *userarg)
|
||||
{
|
||||
getdns_upstream *upstream = (getdns_upstream *)userarg;
|
||||
getdns_network_req *netreq = upstream->write_queue;
|
||||
int q;
|
||||
_getdns_tls_x509 *cert;
|
||||
int q = STUB_NOOP;
|
||||
|
||||
if (!netreq) {
|
||||
if (!netreq
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
&& !upstream->doh_session
|
||||
#endif
|
||||
) {
|
||||
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
||||
upstream->event.write_cb = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
netreq->debug_start_time = _getdns_get_time_as_uintt64();
|
||||
DEBUG_STUB("%s %-35s: MSG: %p (writing)\n", STUB_DEBUG_WRITE,
|
||||
__FUNC__, (void*)netreq);
|
||||
|
||||
if (netreq) {
|
||||
netreq->debug_start_time = _getdns_get_time_as_uintt64();
|
||||
DEBUG_STUB("%s %-35s: MSG: %p (writing)\n", STUB_DEBUG_WRITE,
|
||||
__FUNC__, (void*)netreq);
|
||||
}
|
||||
/* Health checks on current connection */
|
||||
if (upstream->conn_state == GETDNS_CONN_TEARDOWN ||
|
||||
upstream->conn_state == GETDNS_CONN_CLOSED ||
|
||||
|
@ -1786,11 +2185,57 @@ upstream_write_cb(void *userarg)
|
|||
else if (!upstream_working_ok(upstream))
|
||||
q = STUB_TCP_ERROR;
|
||||
/* Seems ok, now try to write */
|
||||
else if (tls_requested(netreq))
|
||||
else if (netreq && tls_requested(netreq))
|
||||
q = stub_tls_write(upstream, &upstream->tcp, netreq);
|
||||
else
|
||||
else if (netreq)
|
||||
q = stub_tcp_write(upstream->fd, &upstream->tcp, netreq);
|
||||
|
||||
#ifdef HAVE_LIBNGHTTP2
|
||||
if (!upstream->doh_session || q != STUB_NOOP)
|
||||
; /* pass */
|
||||
|
||||
else if (nghttp2_session_want_write(upstream->doh_session)) {
|
||||
int rv = nghttp2_session_send(upstream->doh_session);
|
||||
|
||||
if (rv) {
|
||||
_getdns_upstream_log(upstream,
|
||||
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR,
|
||||
"%-40s : Could not send to DoH connection: %s\n",
|
||||
upstream->addr_str,
|
||||
nghttp2_strerror(rv));
|
||||
q = STUB_TCP_ERROR;
|
||||
|
||||
} else if (nghttp2_session_want_write(upstream->doh_session))
|
||||
return;
|
||||
|
||||
else if (upstream->write_queue)
|
||||
return;
|
||||
|
||||
else if (nghttp2_session_want_read(upstream->doh_session)) {
|
||||
/* Reschedule for reading */
|
||||
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
||||
upstream->event.read_cb = upstream_read_cb;
|
||||
upstream->event.write_cb = NULL;
|
||||
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
||||
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
||||
return;
|
||||
} else
|
||||
q = STUB_CONN_GONE;
|
||||
|
||||
} else if (upstream->write_queue)
|
||||
return;
|
||||
|
||||
else if (nghttp2_session_want_read(upstream->doh_session)) {
|
||||
/* Reschedule for reading */
|
||||
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
|
||||
upstream->event.read_cb = upstream_read_cb;
|
||||
upstream->event.write_cb = NULL;
|
||||
GETDNS_SCHEDULE_EVENT(upstream->loop,
|
||||
upstream->fd, TIMEOUT_FOREVER, &upstream->event);
|
||||
return;
|
||||
} else
|
||||
q = STUB_CONN_GONE;
|
||||
#endif
|
||||
switch (q) {
|
||||
case STUB_TCP_MORE_TO_WRITE:
|
||||
/* WSA TODO: if callback is still upstream_write_cb, do it again
|
||||
|
@ -1810,11 +2255,15 @@ upstream_write_cb(void *userarg)
|
|||
case STUB_CONN_GONE:
|
||||
case STUB_NO_AUTH:
|
||||
/* Cleaning up after connection or auth check failure. Need to fallback. */
|
||||
stub_cleanup(netreq);
|
||||
_getdns_upstream_log(upstream, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG,
|
||||
"%-40s : Conn closed: %s - *Failure*\n",
|
||||
upstream->addr_str,
|
||||
(upstream->transport == GETDNS_TRANSPORT_TLS ? "TLS" : "TCP"));
|
||||
if (!netreq)
|
||||
return;
|
||||
stub_cleanup(netreq);
|
||||
if (netreq->owner->return_call_reporting)
|
||||
netreq_equip_tls_debug_info(netreq);
|
||||
if (fallback_on_write(netreq) == STUB_TCP_ERROR) {
|
||||
/* TODO: Need new state to report transport unavailable*/
|
||||
_getdns_netreq_change_state(netreq, NET_REQ_ERRORED);
|
||||
|
@ -1825,18 +2274,8 @@ upstream_write_cb(void *userarg)
|
|||
default:
|
||||
/* Unqueue the netreq from the write_queue */
|
||||
remove_from_write_queue(upstream, netreq);
|
||||
|
||||
if (netreq->owner->return_call_reporting &&
|
||||
netreq->upstream->tls_obj) {
|
||||
if (netreq->debug_tls_peer_cert.data == NULL &&
|
||||
(cert = _getdns_tls_connection_get_peer_certificate(&upstream->upstreams->mf, netreq->upstream->tls_obj))) {
|
||||
_getdns_tls_x509_to_der(&upstream->upstreams->mf, cert, &netreq->debug_tls_peer_cert);
|
||||
_getdns_tls_x509_free(&upstream->upstreams->mf, cert);
|
||||
}
|
||||
netreq->debug_tls_version = _getdns_tls_connection_get_version(netreq->upstream->tls_obj);
|
||||
}
|
||||
/* Need this because auth status is reset on connection close */
|
||||
netreq->debug_tls_auth_status = netreq->upstream->tls_auth_state;
|
||||
if (netreq->owner->return_call_reporting)
|
||||
netreq_equip_tls_debug_info(netreq);
|
||||
upstream->queries_sent++;
|
||||
|
||||
/* Empty write_queue?, then deschedule upstream write_cb */
|
||||
|
|
42
src/tls.h
42
src/tls.h
|
@ -43,6 +43,7 @@
|
|||
/* Forward declare type. */
|
||||
struct sha256_pin;
|
||||
struct getdns_log_config;
|
||||
struct dane_record;
|
||||
|
||||
/* Additional return codes required by TLS abstraction. Internal use only. */
|
||||
#define GETDNS_RETURN_TLS_WANT_READ ((getdns_return_t) 420)
|
||||
|
@ -226,6 +227,18 @@ getdns_return_t _getdns_tls_connection_set_cipher_list(_getdns_tls_connection* c
|
|||
*/
|
||||
getdns_return_t _getdns_tls_connection_set_cipher_suites(_getdns_tls_connection* conn, const char* list);
|
||||
|
||||
/**
|
||||
* Set alpn to send on this connection.
|
||||
*
|
||||
* @param conn the connection.
|
||||
* @param alpn the application layer protocol negotiation (alpn) value.
|
||||
NULL for default setting (dot).
|
||||
* @return GETDNS_RETURN_GOOD on success.
|
||||
* @return GETDNS_RETURN_INVALID_PARAMETER on bad context pointer.
|
||||
* @return GETDNS_RETURN_BAD_CONTEXT on failure.
|
||||
*/
|
||||
getdns_return_t _getdns_tls_connection_set_alpn(_getdns_tls_connection* conn, const char* alpn);
|
||||
|
||||
/**
|
||||
* Set list of allowed curves on this connection.
|
||||
*
|
||||
|
@ -265,6 +278,22 @@ _getdns_tls_session* _getdns_tls_connection_get_session(struct mem_funcs* mfs, _
|
|||
*/
|
||||
const char* _getdns_tls_connection_get_version(_getdns_tls_connection* conn);
|
||||
|
||||
/**
|
||||
* Return whether or not the peer cert PKIX validated
|
||||
*
|
||||
* @param conn the connection
|
||||
* @return 1 when the peer cert PKIX validated, 0 if it did not validate, 2 otherwise
|
||||
*/
|
||||
int _getdns_tls_connection_get_pkix_auth(_getdns_tls_connection* conn);
|
||||
|
||||
/**
|
||||
* Return whether or not a pin from the pinset matched
|
||||
*
|
||||
* @param conn the connection
|
||||
* @return 1 when the peer cert matched a pinset, 0 otherwise
|
||||
*/
|
||||
int _getdns_tls_connection_get_pin_auth(_getdns_tls_connection* conn);
|
||||
|
||||
/**
|
||||
* Attempt TLS handshake.
|
||||
*
|
||||
|
@ -316,6 +345,17 @@ getdns_return_t _getdns_tls_connection_setup_hostname_auth(_getdns_tls_connectio
|
|||
*/
|
||||
getdns_return_t _getdns_tls_connection_set_host_pinset(_getdns_tls_connection* conn, const char* auth_name, const struct sha256_pin* pinset);
|
||||
|
||||
/**
|
||||
* Set host pinset.
|
||||
*
|
||||
* @param conn the connection.
|
||||
* @param auth_name the hostname.
|
||||
* @param dane_records the DANE records that must match.
|
||||
* @return GETDNS_RETURN_GOOD if all OK.
|
||||
* @return GETDNS_RETURN_INVALID_PARAMETER if conn is null or has no SSL.
|
||||
*/
|
||||
getdns_return_t _getdns_tls_connection_set_dane_records(_getdns_tls_connection* conn, const char* auth_name, const struct dane_record* dane_records);
|
||||
|
||||
/**
|
||||
* Get result of certificate verification.
|
||||
*
|
||||
|
@ -356,7 +396,7 @@ getdns_return_t _getdns_tls_connection_read(_getdns_tls_connection* conn, uint8_
|
|||
* @return GETDNS_RETURN_TLS_WANT_WRITE if the write needs to be retried.
|
||||
* @return GETDNS_RETURN_GENERIC_ERROR if write failed.
|
||||
*/
|
||||
getdns_return_t _getdns_tls_connection_write(_getdns_tls_connection* conn, uint8_t* buf, size_t to_write, size_t* written);
|
||||
getdns_return_t _getdns_tls_connection_write(_getdns_tls_connection* conn, const uint8_t* buf, size_t to_write, size_t* written);
|
||||
|
||||
/**
|
||||
* Free a session.
|
||||
|
|
|
@ -178,10 +178,12 @@ print_usage(FILE *out, const char *progname)
|
|||
#endif
|
||||
fprintf(out, "\ndefault mode: " DEFAULT_RESOLUTION_TYPE
|
||||
", synchronous resolution of NS record\n\t\tusing UDP with TCP fallback\n");
|
||||
fprintf(out, "\nupstreams: @<ip>[%%<scope_id>][@<port>][#<tls port>][~<tls name>][^<tsig spec>]");
|
||||
fprintf(out, "\nupstreams: @<ip>[%%<scope_id>][@<port>][^<tsig spec>]");
|
||||
fprintf(out, "\n [#<tls port>][~<tls name>][_<alpn>][/<doh path>]");
|
||||
fprintf(out, "\n <ip>@<port> may be given as <IPv4>:<port>");
|
||||
fprintf(out, "\n or \'[\'<IPv6>[%%<scope_id>]\']\':<port> too\n");
|
||||
fprintf(out, "\ntsig spec: [<algorithm>:]<name>:<secret in Base64>\n");
|
||||
fprintf(out, "\nalpn: [dot | h2]\n");
|
||||
fprintf(out, "\nextensions:\n");
|
||||
fprintf(out, "\t+add_warning_for_bad_dns\n");
|
||||
fprintf(out, "\t+dnssec\n");
|
||||
|
|
|
@ -244,6 +244,9 @@ typedef struct getdns_network_req
|
|||
const char *debug_tls_version;
|
||||
|
||||
/* Some booleans */
|
||||
unsigned debug_pkix_auth: 2; /* 1 if TLS connection is PKIX valid
|
||||
2 if this is unknown */
|
||||
unsigned debug_pin_auth : 1; /* 1 if one of the pinset's matched */
|
||||
unsigned debug_udp : 1;
|
||||
unsigned keepalive_sent : 1;
|
||||
unsigned badcookie_retry: 1;
|
||||
|
@ -445,6 +448,8 @@ void _getdns_dns_req_free(getdns_dns_req * req);
|
|||
/* network request utils */
|
||||
getdns_return_t _getdns_network_req_add_upstream_option(getdns_network_req * req,
|
||||
uint16_t code, uint16_t sz, const void* data);
|
||||
getdns_return_t _getdns_network_req_add_upstream_options(getdns_network_req * req,
|
||||
const void* data, size_t sz);
|
||||
void _getdns_network_req_clear_upstream_options(getdns_network_req * req);
|
||||
|
||||
/* Adds TSIG signature (if needed) and returns query length */
|
||||
|
|
|
@ -846,9 +846,11 @@ _getdns_create_call_reporting_dict(
|
|||
, netreq->request_type ) ||
|
||||
|
||||
/* Safe, because uint32_t facilitates RRT's of almost 50 days*/
|
||||
getdns_dict_set_int(netreq_debug, "run_time/ms",
|
||||
(uint32_t)(( netreq->debug_end_time
|
||||
- netreq->debug_start_time)/1000))) {
|
||||
getdns_dict_set_int(netreq_debug, "run_time/ms",
|
||||
(uint32_t) ( netreq->debug_end_time
|
||||
- netreq->debug_start_time) >= 1000
|
||||
? (uint32_t)(( netreq->debug_end_time
|
||||
- netreq->debug_start_time) / 1000) : 0 )) {
|
||||
|
||||
getdns_dict_destroy(netreq_debug);
|
||||
return NULL;
|
||||
|
@ -966,6 +968,18 @@ _getdns_create_call_reporting_dict(
|
|||
getdns_dict_destroy(netreq_debug);
|
||||
return NULL;
|
||||
}
|
||||
if (getdns_dict_set_int(netreq_debug, "tls_auth_pin",
|
||||
netreq->debug_pin_auth)) {
|
||||
|
||||
getdns_dict_destroy(netreq_debug);
|
||||
return NULL;
|
||||
}
|
||||
if (getdns_dict_set_int(netreq_debug, "tls_auth_pkix",
|
||||
netreq->debug_pkix_auth)) {
|
||||
|
||||
getdns_dict_destroy(netreq_debug);
|
||||
return NULL;
|
||||
}
|
||||
if (getdns_dict_util_set_string(netreq_debug, "tls_version",
|
||||
netreq->debug_tls_version)){
|
||||
|
||||
|
@ -1165,7 +1179,7 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request)
|
|||
int rrsigs_in_answer = 0;
|
||||
getdns_dict *reply;
|
||||
getdns_bindata *canonical_name = NULL;
|
||||
int nreplies = 0, nanswers = 0;
|
||||
int nreplies = 0, nerred = 0, ntimedout = 0, nanswers = 0;
|
||||
int nsecure = 0, ninsecure = 0, nindeterminate = 0, nbogus = 0;
|
||||
getdns_dict *netreq_debug;
|
||||
_srvs srvs = { 0, 0, NULL };
|
||||
|
@ -1225,8 +1239,7 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request)
|
|||
getdns_bindata *tmp_ipv4_address;
|
||||
getdns_bindata *tmp_ipv6_address;
|
||||
|
||||
if (call_reporting && ( netreq->response_len
|
||||
|| netreq->state == NET_REQ_TIMED_OUT)) {
|
||||
if (call_reporting) {
|
||||
if (!(netreq_debug =
|
||||
_getdns_create_call_reporting_dict(context,netreq)))
|
||||
goto error;
|
||||
|
@ -1237,6 +1250,11 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request)
|
|||
|
||||
netreq_debug = NULL;
|
||||
}
|
||||
switch (netreq->state) {
|
||||
case NET_REQ_TIMED_OUT: ntimedout++; break;
|
||||
case NET_REQ_ERRORED : nerred++ ; break;
|
||||
default : break;
|
||||
}
|
||||
if (! netreq->response_len)
|
||||
continue;
|
||||
|
||||
|
@ -1371,19 +1389,20 @@ _getdns_create_getdns_response(getdns_dns_req *completed_request)
|
|||
GETDNS_FREE(context->mf, srvs.rrs);
|
||||
}
|
||||
if (getdns_dict_set_int(result, GETDNS_STR_KEY_STATUS,
|
||||
completed_request->request_timed_out ||
|
||||
nreplies == 0 ? GETDNS_RESPSTATUS_ALL_TIMEOUT :
|
||||
( completed_request->dnssec
|
||||
&& nsecure == 0 && nindeterminate ) > 0
|
||||
? GETDNS_RESPSTATUS_NO_SECURE_ANSWERS :
|
||||
( completed_request->dnssec_return_only_secure
|
||||
&& nsecure == 0 && ninsecure ) > 0
|
||||
? GETDNS_RESPSTATUS_NO_SECURE_ANSWERS :
|
||||
( completed_request->dnssec_return_only_secure
|
||||
|| completed_request->dnssec ) && nsecure == 0 && nbogus > 0
|
||||
? GETDNS_RESPSTATUS_ALL_BOGUS_ANSWERS :
|
||||
nanswers == 0 ? GETDNS_RESPSTATUS_NO_NAME
|
||||
: GETDNS_RESPSTATUS_GOOD))
|
||||
completed_request->request_timed_out || nreplies == 0
|
||||
? ( nerred > ntimedout ? GETDNS_RESPSTATUS_ALL_ERRED
|
||||
: GETDNS_RESPSTATUS_ALL_TIMEOUT )
|
||||
: completed_request->dnssec && nsecure == 0 && nindeterminate > 0
|
||||
? GETDNS_RESPSTATUS_NO_SECURE_ANSWERS
|
||||
: completed_request->dnssec_return_only_secure && nsecure == 0
|
||||
&& ninsecure > 0
|
||||
? GETDNS_RESPSTATUS_NO_SECURE_ANSWERS
|
||||
: ( completed_request->dnssec_return_only_secure
|
||||
|| completed_request->dnssec ) && nsecure == 0 && nbogus > 0
|
||||
? GETDNS_RESPSTATUS_ALL_BOGUS_ANSWERS
|
||||
: nanswers == 0
|
||||
? GETDNS_RESPSTATUS_NO_NAME
|
||||
: GETDNS_RESPSTATUS_GOOD))
|
||||
goto error_free_result;
|
||||
|
||||
return result;
|
||||
|
|
2
stubby
2
stubby
|
@ -1 +1 @@
|
|||
Subproject commit d4eff9bf415a968b1849c99720d51af98a10bdee
|
||||
Subproject commit a550394f874817bd5fda022c34c0c96e7f819bc2
|
Loading…
Reference in New Issue