Merge pull request #529 from wtoorop/proxy-config-doh

Proxy config doh
This commit is contained in:
Willem Toorop 2022-10-31 10:54:23 +01:00 committed by GitHub
commit bcaf8fa563
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 833 additions and 119 deletions

View File

@ -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})

View File

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

View File

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

View File

@ -88,9 +88,11 @@ typedef unsigned short in_port_t;
#define GETDNS_PORT_ZERO 0
#define GETDNS_PORT_DNS 53
#define GETDNS_PORT_DNS_OVER_TLS 853
#define GETDNS_PORT_DNS_OVER_HTTPS 443
#define GETDNS_STR_PORT_ZERO "0"
#define GETDNS_STR_PORT_DNS "53"
#define GETDNS_STR_PORT_DNS_OVER_TLS "853"
#define GETDNS_STR_PORT_DNS_OVER_HTTPS "443"
#ifdef HAVE_PTHREAD
static pthread_mutex_t ssl_init_lock = PTHREAD_MUTEX_INITIALIZER;
@ -132,7 +134,7 @@ getdns_port_array[GETDNS_UPSTREAM_TRANSPORTS] = {
};
static char*
getdns_port_str_array[] = {
getdns_port_str_array[GETDNS_UPSTREAM_TRANSPORTS] = {
GETDNS_STR_PORT_DNS,
GETDNS_STR_PORT_DNS_OVER_TLS
};
@ -635,6 +637,12 @@ _getdns_upstreams_dereference(getdns_upstreams *upstreams)
_getdns_context_cancel_request(dnsreq);
}
}
#ifdef HAVE_LIBNGHTTP2
if (upstream->doh_session != NULL) {
nghttp2_session_del(upstream->doh_session);
upstream->doh_session = NULL;
}
#endif
if (upstream->tls_session != NULL)
_getdns_tls_session_free(&upstreams->mf, upstream->tls_session);
@ -768,6 +776,12 @@ _getdns_upstream_reset(getdns_upstream *upstream)
upstream->loop->vmt->clear(
upstream->loop, &upstream->event);
}
#ifdef HAVE_LIBNGHTTP2
if (upstream->doh_session != NULL) {
nghttp2_session_del(upstream->doh_session);
upstream->doh_session = NULL;
}
#endif
if (upstream->tls_obj != NULL) {
_getdns_tls_connection_shutdown(upstream->tls_obj);
_getdns_tls_connection_free(&upstream->upstreams->mf, upstream->tls_obj);
@ -962,6 +976,9 @@ upstream_init(getdns_upstream *upstream,
upstream->tls_fallback_ok = 0;
upstream->tls_obj = NULL;
upstream->tls_session = NULL;
#ifdef HAVE_LIBNGHTTP2
upstream->doh_session = NULL;
#endif
upstream->tls_cipher_list = NULL;
upstream->tls_ciphersuites = NULL;
upstream->tls_curves_list = NULL;
@ -975,6 +992,8 @@ upstream_init(getdns_upstream *upstream,
upstream->best_tls_auth_state = GETDNS_AUTH_NONE;
upstream->tls_pubkey_pinset = NULL;
upstream->tls_dane_records = NULL;
upstream->alpn = NULL;
strcpy(upstream->doh_path, "dns-query");
upstream->loop = NULL;
(void) getdns_eventloop_event_init(
&upstream->event, upstream, NULL, NULL, NULL);
@ -1539,7 +1558,9 @@ getdns_context_create_with_extended_memory_functions(
result->edns_client_subnet_private = 0;
result->tls_query_padding_blocksize = 1; /* default is to pad queries sensibly */
result->tls_ctx = NULL;
#ifdef HAVE_LIBNGHTTP2
result->doh_callbacks = NULL;
#endif
result->extension = &result->default_eventloop.loop;
_getdns_default_eventloop_init(&result->mf, &result->default_eventloop);
_getdns_default_eventloop_init(&result->mf, &result->sync_eventloop);
@ -1726,6 +1747,10 @@ getdns_context_destroy(struct getdns_context *context)
if (context->tls_ctx)
_getdns_tls_context_free(&context->my_mf, context->tls_ctx);
#ifdef HAVE_LIBNGHTTP2
if (context->doh_callbacks)
nghttp2_session_callbacks_del(context->doh_callbacks);
#endif
getdns_list_destroy(context->dns_root_servers);
#if defined(HAVE_LIBUNBOUND) && !defined(HAVE_UB_CTX_SET_STUB)
@ -2882,6 +2907,7 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
getdns_bindata *address_type;
getdns_bindata *address_data;
getdns_bindata *tls_auth_name;
getdns_bindata *doh_path;
struct sockaddr_storage addr;
getdns_bindata *scope_id;
@ -3019,17 +3045,30 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
for (j = 0; j < GETDNS_UPSTREAM_TRANSPORTS; j++) {
uint32_t port;
struct addrinfo *ai;
getdns_bindata *alpn = NULL;
static const char *alpn_dot = "dot";
static const char *alpn_h2 = "h2";
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);
}
if (!dict)
; /* pass */
else if (getdns_upstream_transports[j]
!= GETDNS_TRANSPORT_TLS)
getdns_dict_get_int(dict, "port", &port);
else if (!getdns_dict_get_bindata(dict, "alpn", &alpn)
&& alpn->size == 2 && alpn->data[0] == 'h'
&& alpn->data[1] == '2') {
port = GETDNS_PORT_DNS_OVER_HTTPS;
getdns_dict_get_int(dict, "tls_port", &port);
} else
getdns_dict_get_int(dict, "tls_port", &port);
(void) snprintf(portstr, 1024, "%d", (int)port);
if (getaddrinfo(addrstr, portstr, &hints, &ai))
@ -3045,7 +3084,21 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
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) {
if (!alpn)
; /* pass */
else if (alpn->size == 3 && alpn->data[0] == 'd'
&& alpn->data[1] == 'o' && alpn->data[2] == 't')
upstream->alpn = alpn_dot;
else if (alpn->size == 2 && alpn->data[0] == 'h'
&& alpn->data[1] == '2')
upstream->alpn = alpn_h2;
else
goto invalid_parameter;
if (dict && getdns_upstream_transports[j] ==
GETDNS_TRANSPORT_TLS) {
getdns_list *pubkey_pinset = NULL;
getdns_list *dane_records = NULL;
getdns_bindata *tls_cipher_list = NULL;
@ -3075,6 +3128,19 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
upstream->tls_auth_name
[tls_auth_name->size] = '\0';
}
if ((r = getdns_dict_get_bindata(
dict, "doh_path", &doh_path)) == GETDNS_RETURN_GOOD) {
if (doh_path->size >= sizeof(upstream->doh_path)) {
freeaddrinfo(ai);
goto invalid_parameter;
}
memcpy(upstream->doh_path,
(char *)doh_path->data,
doh_path->size);
upstream->doh_path
[doh_path->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
@ -4490,6 +4556,22 @@ _getdns_ns_dns_setup(struct getdns_context *context)
return GETDNS_RETURN_BAD_CONTEXT;
}
#ifdef HAVE_LIBNGHTTP2
ssize_t
_doh_send_callback(nghttp2_session *session, const uint8_t *data, size_t length,
int flags, void *user_data);
ssize_t
_doh_recv_callback(nghttp2_session *session, uint8_t *buf, size_t length,
int flags, void *user_data);
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);
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);
#endif
getdns_return_t
_getdns_context_prepare_for_resolution(getdns_context *context)
{
@ -4557,6 +4639,22 @@ _getdns_context_prepare_for_resolution(getdns_context *context)
}
_getdns_tls_context_pinset_init(context->tls_ctx);
}
#ifdef HAVE_LIBNGHTTP2
if (!context->doh_callbacks) {
if (nghttp2_session_callbacks_new(&context->doh_callbacks)) {
context->doh_callbacks = NULL;
return GETDNS_RETURN_MEMORY_ERROR;
}
nghttp2_session_callbacks_set_send_callback(
context->doh_callbacks, _doh_send_callback);
nghttp2_session_callbacks_set_recv_callback(
context->doh_callbacks, _doh_recv_callback);
nghttp2_session_callbacks_set_on_data_chunk_recv_callback(
context->doh_callbacks, _doh_on_data_chunk_recv_callback);
nghttp2_session_callbacks_set_on_header_callback(
context->doh_callbacks, _doh_on_header_callback);
}
#endif
}
/* Block use of TLS ONLY in recursive mode as it won't work */
@ -4993,6 +5091,17 @@ getdns_context_get_api_information(const getdns_context* context)
&& ! _getdns_tls_get_api_information(result)
#ifdef HAVE_LIBNGHTTP2
&& ! getdns_dict_set_int(
result, "nghttp2_version_number", nghttp2_version(0)->version_num)
&& ! getdns_dict_util_set_string(
result, "nghttp2_version_string", nghttp2_version(0)->version_str)
&& ! getdns_dict_util_set_string(
result, "nghttp2_protocol_string", nghttp2_version(0)->proto_str)
#endif
&& ! getdns_dict_set_int(
result, "resolution_type", context->resolution_type)
@ -5382,52 +5491,71 @@ getdns_context_get_upstream_recursive_servers(
(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))))
if (upstream->transport != GETDNS_TRANSPORT_TLS)
continue;
if (!is_doh_upstream(upstream)
&& upstream_port(upstream) != GETDNS_PORT_DNS_OVER_TLS
&& (r = getdns_dict_set_int(d, "tls_port",
(uint32_t) upstream_port(upstream))))
break;
if (is_doh_upstream(upstream)
&& upstream_port(upstream) != GETDNS_PORT_DNS_OVER_HTTPS
&& (r = getdns_dict_set_int(d, "tls_port",
(uint32_t) upstream_port(upstream))))
break;
if (upstream->alpn
&& (r = getdns_dict_util_set_string(
d, "alpn", upstream->alpn)))
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_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_ciphersuites) {
(void) getdns_dict_util_set_string(
d, "tls_ciphersuites",
upstream->tls_ciphersuites);
}
if (upstream->tls_curves_list) {
(void) getdns_dict_util_set_string(
d, "tls_curves_list",
upstream->tls_curves_list);
}
if (upstream->tls_min_version) {
(void) getdns_dict_set_int(
d, "tls_min_version",
upstream->tls_min_version);
}
if (upstream->tls_max_version) {
(void) getdns_dict_set_int(
d, "tls_max_version",
upstream->tls_max_version);
}
}
if (upstream->tls_cipher_list) {
(void) getdns_dict_util_set_string(
d, "tls_cipher_list",
upstream->tls_cipher_list);
}
if (upstream->tls_ciphersuites) {
(void) getdns_dict_util_set_string(
d, "tls_ciphersuites",
upstream->tls_ciphersuites);
}
if (upstream->tls_curves_list) {
(void) getdns_dict_util_set_string(
d, "tls_curves_list",
upstream->tls_curves_list);
}
if (upstream->tls_min_version) {
(void) getdns_dict_set_int(
d, "tls_min_version",
upstream->tls_min_version);
}
if (upstream->tls_max_version) {
(void) getdns_dict_set_int(
d, "tls_max_version",
upstream->tls_max_version);
}
if (strcmp(upstream->doh_path, "dns-query")
&& (r = getdns_dict_util_set_string(
d, "doh_path",
upstream->doh_path)))
break;
}
if (!r)
if (!(r = _getdns_list_append_this_dict(upstreams, d)))
@ -5689,6 +5817,9 @@ _getdns_context_config_setting(getdns_context *context,
&& !_streq(setting, "openssl_platform")
&& !_streq(setting, "openssl_dir")
&& !_streq(setting, "openssl_engines_dir")
&& !_streq(setting, "nghttp2_version_number")
&& !_streq(setting, "nghttp2_version_string")
&& !_streq(setting, "nghttp2_protocol_string")
) {
r = GETDNS_RETURN_NOT_IMPLEMENTED;
}

View File

@ -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"
@ -223,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;
@ -230,6 +236,10 @@ 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;
@ -271,6 +281,10 @@ 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
@ -426,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;

View File

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

View File

@ -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)
@ -800,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;

View File

@ -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)
@ -1155,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;

View File

@ -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 */
/*****************************/
@ -669,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);
@ -680,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
@ -994,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);
@ -1002,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;
@ -1235,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*/
/***************************/
@ -1333,8 +1556,8 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp,
int q;
fprintf(stderr, "in stub_tls_write\n");
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);
@ -1352,21 +1575,6 @@ fprintf(stderr, "in stub_tls_write\n");
/* 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);
@ -1428,7 +1636,96 @@ fprintf(stderr, "in stub_tls_write\n");
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;
@ -1437,9 +1734,7 @@ fprintf(stderr, "in stub_tls_write\n");
/* We were able to write everything! Start reading. */
return (int) query_id;
}
return STUB_TCP_ERROR;
}
@ -1676,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
@ -1698,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;
@ -1819,18 +2161,22 @@ upstream_write_cb(void *userarg)
{
getdns_upstream *upstream = (getdns_upstream *)userarg;
getdns_network_req *netreq = upstream->write_queue;
int q;
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 ||
@ -1839,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
@ -1863,11 +2255,13 @@ 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) {

View File

@ -227,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.
*
@ -384,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.

View File

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