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_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;
@ -135,7 +137,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
};
@ -978,6 +980,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;
upstream->doh_path[0] = '\0';
upstream->loop = NULL;
(void) getdns_eventloop_event_init(
&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_data;
getdns_bindata *tls_auth_name;
getdns_bindata *doh_path;
struct sockaddr_storage addr;
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++) {
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))
@ -3048,7 +3066,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;
@ -3078,6 +3110,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
@ -5396,52 +5441,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 (upstream->doh_path[0] != '\0'
&& (r = getdns_dict_util_set_string(
d, "doh_path",
upstream->doh_path)))
break;
}
if (!r)
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_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 +275,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

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

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

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);
if (!r && 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)
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);
/**
* 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.
*

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