alpn determines DoH upstream

This commit is contained in:
Willem Toorop 2022-10-16 12:51:55 +02:00
parent ca416a8f9b
commit 7deed7e1d4
7 changed files with 175 additions and 55 deletions

View File

@ -91,9 +91,11 @@ typedef unsigned short in_port_t;
#define GETDNS_PORT_ZERO 0 #define GETDNS_PORT_ZERO 0
#define GETDNS_PORT_DNS 53 #define GETDNS_PORT_DNS 53
#define GETDNS_PORT_DNS_OVER_TLS 853 #define GETDNS_PORT_DNS_OVER_TLS 853
#define GETDNS_PORT_DNS_OVER_HTTPS 443
#define GETDNS_STR_PORT_ZERO "0" #define GETDNS_STR_PORT_ZERO "0"
#define GETDNS_STR_PORT_DNS "53" #define GETDNS_STR_PORT_DNS "53"
#define GETDNS_STR_PORT_DNS_OVER_TLS "853" #define GETDNS_STR_PORT_DNS_OVER_TLS "853"
#define GETDNS_STR_PORT_DNS_OVER_HTTPS "443"
#ifdef HAVE_PTHREAD #ifdef HAVE_PTHREAD
static pthread_mutex_t ssl_init_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t ssl_init_lock = PTHREAD_MUTEX_INITIALIZER;
@ -135,7 +137,7 @@ getdns_port_array[GETDNS_UPSTREAM_TRANSPORTS] = {
}; };
static char* static char*
getdns_port_str_array[] = { getdns_port_str_array[GETDNS_UPSTREAM_TRANSPORTS] = {
GETDNS_STR_PORT_DNS, GETDNS_STR_PORT_DNS,
GETDNS_STR_PORT_DNS_OVER_TLS GETDNS_STR_PORT_DNS_OVER_TLS
}; };
@ -978,6 +980,8 @@ upstream_init(getdns_upstream *upstream,
upstream->best_tls_auth_state = GETDNS_AUTH_NONE; upstream->best_tls_auth_state = GETDNS_AUTH_NONE;
upstream->tls_pubkey_pinset = NULL; upstream->tls_pubkey_pinset = NULL;
upstream->tls_dane_records = NULL; upstream->tls_dane_records = NULL;
upstream->alpn = NULL;
upstream->doh_path[0] = '\0';
upstream->loop = NULL; upstream->loop = NULL;
(void) getdns_eventloop_event_init( (void) getdns_eventloop_event_init(
&upstream->event, upstream, NULL, NULL, NULL); &upstream->event, upstream, NULL, NULL, NULL);
@ -2885,6 +2889,7 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
getdns_bindata *address_type; getdns_bindata *address_type;
getdns_bindata *address_data; getdns_bindata *address_data;
getdns_bindata *tls_auth_name; getdns_bindata *tls_auth_name;
getdns_bindata *doh_path;
struct sockaddr_storage addr; struct sockaddr_storage addr;
getdns_bindata *scope_id; getdns_bindata *scope_id;
@ -3022,17 +3027,30 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
for (j = 0; j < GETDNS_UPSTREAM_TRANSPORTS; j++) { for (j = 0; j < GETDNS_UPSTREAM_TRANSPORTS; j++) {
uint32_t port; uint32_t port;
struct addrinfo *ai; struct addrinfo *ai;
getdns_bindata *alpn = NULL;
static const char *alpn_dot = "dot";
static const char *alpn_h2 = "h2";
port = getdns_port_array[j]; port = getdns_port_array[j];
if (port == GETDNS_PORT_ZERO) if (port == GETDNS_PORT_ZERO)
continue; continue;
if (getdns_upstream_transports[j] != GETDNS_TRANSPORT_TLS) { if (!dict)
if (dict) ; /* pass */
(void) getdns_dict_get_int(dict, "port", &port);
} else { else if (getdns_upstream_transports[j]
if (dict) != GETDNS_TRANSPORT_TLS)
(void) getdns_dict_get_int(dict, "tls_port", &port); 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); (void) snprintf(portstr, 1024, "%d", (int)port);
if (getaddrinfo(addrstr, portstr, &hints, &ai)) if (getaddrinfo(addrstr, portstr, &hints, &ai))
@ -3048,7 +3066,21 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
upstream->addr.ss_family = addr.ss_family; upstream->addr.ss_family = addr.ss_family;
upstream_init(upstream, upstreams, ai); upstream_init(upstream, upstreams, ai);
upstream->transport = getdns_upstream_transports[j]; 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 *pubkey_pinset = NULL;
getdns_list *dane_records = NULL; getdns_list *dane_records = NULL;
getdns_bindata *tls_cipher_list = NULL; getdns_bindata *tls_cipher_list = NULL;
@ -3078,6 +3110,19 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context,
upstream->tls_auth_name upstream->tls_auth_name
[tls_auth_name->size] = '\0'; [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", if ((r = getdns_dict_get_list(dict, "tls_pubkey_pinset",
&pubkey_pinset)) == GETDNS_RETURN_GOOD) { &pubkey_pinset)) == GETDNS_RETURN_GOOD) {
/* TODO: what if the user supplies tls_pubkey_pinset with /* TODO: what if the user supplies tls_pubkey_pinset with
@ -5396,52 +5441,71 @@ getdns_context_get_upstream_recursive_servers(
(uint32_t)upstream_port(upstream)))) (uint32_t)upstream_port(upstream))))
break; break;
if (upstream->transport == GETDNS_TRANSPORT_TLS) { if (upstream->transport != GETDNS_TRANSPORT_TLS)
if (upstream_port(upstream) != getdns_port_array[j] && continue;
(r = getdns_dict_set_int(d, "tls_port",
(uint32_t) upstream_port(upstream)))) 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; 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 (upstream->doh_path[0] != '\0'
&& (r = getdns_dict_util_set_string(
d, "doh_path",
upstream->doh_path)))
break;
} }
if (!r) if (!r)
if (!(r = _getdns_list_append_this_dict(upstreams, d))) if (!(r = _getdns_list_append_this_dict(upstreams, d)))

View File

@ -230,6 +230,10 @@ typedef struct getdns_upstream {
getdns_tls_version_t tls_min_version; getdns_tls_version_t tls_min_version;
getdns_tls_version_t tls_max_version; getdns_tls_version_t tls_max_version;
/* DoH settings */
const char *alpn;
char doh_path[256];
/* Auth credentials */ /* Auth credentials */
char tls_auth_name[256]; char tls_auth_name[256];
sha256_pin_t *tls_pubkey_pinset; sha256_pin_t *tls_pubkey_pinset;
@ -271,6 +275,10 @@ typedef struct getdns_upstream {
} 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_ADDR 3
#define POLICY_N_SVCPARAMS 8 #define POLICY_N_SVCPARAMS 8

View File

@ -1126,6 +1126,8 @@ _getdns_ipaddr_dict_mf(struct mem_funcs *mf, const char *ipstr)
char *p = strchr(ipstr, '@'), *portstr = ""; char *p = strchr(ipstr, '@'), *portstr = "";
char *t = strchr(ipstr, '#'), *tls_portstr = ""; char *t = strchr(ipstr, '#'), *tls_portstr = "";
char *n = strchr(ipstr, '~'), *tls_namestr = ""; char *n = strchr(ipstr, '~'), *tls_namestr = "";
char *P = strchr(ipstr, '/'), *doh_pathstr = "";
char *A = strchr(ipstr, '_'), *alpnstr = "";
/* ^[alg:]name:key */ /* ^[alg:]name:key */
char *T = strchr(ipstr, '^'), *tsig_name_str = "" char *T = strchr(ipstr, '^'), *tsig_name_str = ""
, *tsig_secret_str = "" , *tsig_secret_str = ""
@ -1173,6 +1175,14 @@ _getdns_ipaddr_dict_mf(struct mem_funcs *mf, const char *ipstr)
*n = 0; *n = 0;
tls_namestr = n + 1; tls_namestr = n + 1;
} }
if (P) {
*P = 0;
doh_pathstr = P + 1;
}
if (A) {
*A = 0;
alpnstr = A + 1;
}
if (T) { if (T) {
*T = 0; *T = 0;
tsig_name_str = T + 1; 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)); getdns_dict_set_int(r, "port", (int32_t)atoi(portstr));
if (*tls_portstr) if (*tls_portstr)
getdns_dict_set_int(r, "tls_port", (int32_t)atoi(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); 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) if (*scope_id_str)
getdns_dict_util_set_string(r, "scope_id", scope_id_str); getdns_dict_util_set_string(r, "scope_id", scope_id_str);
if (*tsig_name_str) if (*tsig_name_str)

View File

@ -807,6 +807,25 @@ getdns_return_t _getdns_tls_connection_set_curves_list(_getdns_tls_connection* c
return GETDNS_RETURN_GOOD; 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) getdns_return_t _getdns_tls_connection_set_session(_getdns_tls_connection* conn, _getdns_tls_session* s)
{ {
if (!conn || !conn->ssl || !s || !s->ssl) if (!conn || !conn->ssl || !s || !s->ssl)

View File

@ -1006,6 +1006,8 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
r = _getdns_tls_connection_set_curves_list(tls, upstream->tls_curves_list); r = _getdns_tls_connection_set_curves_list(tls, upstream->tls_curves_list);
if (!r && upstream->tls_ciphersuites) if (!r && upstream->tls_ciphersuites)
r = _getdns_tls_connection_set_cipher_suites(tls, upstream->tls_ciphersuites); r = _getdns_tls_connection_set_cipher_suites(tls, upstream->tls_ciphersuites);
if (!r)
r = _getdns_tls_connection_set_alpn(tls, upstream->alpn);
if (!r) if (!r)
r = _getdns_tls_connection_set_min_max_tls_version(tls, upstream->tls_min_version, upstream->tls_max_version); r = _getdns_tls_connection_set_min_max_tls_version(tls, upstream->tls_min_version, upstream->tls_max_version);

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); 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. * Set list of allowed curves on this connection.
* *

View File

@ -178,10 +178,12 @@ print_usage(FILE *out, const char *progname)
#endif #endif
fprintf(out, "\ndefault mode: " DEFAULT_RESOLUTION_TYPE fprintf(out, "\ndefault mode: " DEFAULT_RESOLUTION_TYPE
", synchronous resolution of NS record\n\t\tusing UDP with TCP fallback\n"); ", 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 <ip>@<port> may be given as <IPv4>:<port>");
fprintf(out, "\n or \'[\'<IPv6>[%%<scope_id>]\']\':<port> too\n"); fprintf(out, "\n or \'[\'<IPv6>[%%<scope_id>]\']\':<port> too\n");
fprintf(out, "\ntsig spec: [<algorithm>:]<name>:<secret in Base64>\n"); fprintf(out, "\ntsig spec: [<algorithm>:]<name>:<secret in Base64>\n");
fprintf(out, "\nalpn: [dot | h2]\n");
fprintf(out, "\nextensions:\n"); fprintf(out, "\nextensions:\n");
fprintf(out, "\t+add_warning_for_bad_dns\n"); fprintf(out, "\t+add_warning_for_bad_dns\n");
fprintf(out, "\t+dnssec\n"); fprintf(out, "\t+dnssec\n");