From 6dc5ae876d757300581ab6d1409af5817ff15f89 Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Thu, 18 Aug 2022 12:03:44 +0200 Subject: [PATCH 01/14] Support for proxy control option --- src/server.c | 486 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 485 insertions(+), 1 deletion(-) diff --git a/src/server.c b/src/server.c index 0a62817..c113e74 100644 --- a/src/server.c +++ b/src/server.c @@ -119,6 +119,47 @@ static void servfail(dns_msg *msg, getdns_dict **resp_p) (void) getdns_dict_set_int(*resp_p, "/header/ad", 0); } +static void error_reply(dns_msg *msg, getdns_dict **resp_p, unsigned error) +{ + unsigned rcode, ex_rcode; + getdns_dict *dict; + getdns_list *list; + + rcode= error & 0xf; /* Lowest 4 bits */ + ex_rcode= (error >> 4); /* Higher 8 bits */ + + if (*resp_p) + getdns_dict_destroy(*resp_p); + if (!(*resp_p = getdns_dict_create())) + return; + if (msg) { + if (!getdns_dict_get_dict(msg->request, "header", &dict)) + getdns_dict_set_dict(*resp_p, "header", dict); + if (!getdns_dict_get_dict(msg->request, "question", &dict)) + getdns_dict_set_dict(*resp_p, "question", dict); + (void) getdns_dict_set_int(*resp_p, "/header/ra", + msg->rt == GETDNS_RESOLUTION_RECURSING ? 1 : 0); + } + (void) getdns_dict_set_int( + *resp_p, "/header/rcode", rcode); + (void) getdns_dict_set_int(*resp_p, "/header/qr", 1); + (void) getdns_dict_set_int(*resp_p, "/header/ad", 0); + + if (ex_rcode) + { + dict= getdns_dict_create(); + getdns_dict_set_int(dict, "do", 0); + getdns_dict_set_int(dict, "extended_rcode", ex_rcode); + getdns_dict_set_int(dict, "type", GETDNS_RRTYPE_OPT); + getdns_dict_set_int(dict, "udp_payload_size", 1232); + getdns_dict_set_int(dict, "version", 0); + getdns_dict_set_int(dict, "z", 0); + list= getdns_list_create(); + getdns_list_set_dict(list, 0, dict); + getdns_dict_set_list(*resp_p, "additional", list); + } +} + static getdns_return_t _handle_edns0( getdns_dict *response, int has_edns0) { @@ -312,6 +353,34 @@ static void request_cb( getdns_dict_destroy(response); } +#include + +#define MAX_PROXY_CONTROL_OPTS 10 +#define PROXY_ADDRS_MAX 10 + +static struct upstream +{ + unsigned dns_error; + unsigned opts_count; + struct proxy_opt + { + /* Original bindata of option */ + getdns_bindata bindata; + + /* Decoded option */ + uint16_t flags1; + uint16_t flags2; + int addr_count; + struct sockaddr_storage addrs[PROXY_ADDRS_MAX]; + + /* Getdns context */ + getdns_context *context; + } opts[MAX_PROXY_CONTROL_OPTS]; +} *upstreams; +static int upstreams_count; + +static struct upstream *get_upstreams_for_policy(getdns_dict *opt_rr); + static void incoming_request_handler(getdns_context *context, getdns_callback_type_t callback_type, getdns_dict *request, void *userarg, getdns_transaction_t request_id) @@ -332,10 +401,15 @@ static void incoming_request_handler(getdns_context *context, getdns_list *additional; getdns_dict *rr; uint32_t rr_type; + unsigned dns_error = GETDNS_RCODE_SERVFAIL; + struct upstream *usp; (void)callback_type; (void)userarg; + printf("incoming_request_handler: got request %s\n", + getdns_pretty_print_dict(request)); + if (!(qext = getdns_dict_create_with_context(context)) || !(msg = malloc(sizeof(dns_msg)))) goto error; @@ -349,6 +423,7 @@ static void incoming_request_handler(getdns_context *context, msg->rt = GETDNS_RESOLUTION_RECURSING; (void) getdns_dict_get_int(request, "/header/ad", &msg->ad_bit); (void) getdns_dict_get_int(request, "/header/cd", &msg->cd_bit); + usp= NULL; if (!getdns_dict_get_list(request, "additional", &additional)) { if (getdns_list_get_length(additional, &len)) len = 0; @@ -361,9 +436,19 @@ static void incoming_request_handler(getdns_context *context, continue; msg->has_edns0 = 1; (void) getdns_dict_get_int(rr, "do", &msg->do_bit); + + usp= get_upstreams_for_policy(rr); + break; } } + + if (usp && usp->dns_error) + { + dns_error= usp->dns_error; + goto error; + } + if ((r = getdns_context_get_resolution_type(context, &msg->rt))) stubby_error("Could get resolution type from context: %s", stubby_getdns_strerror(r)); @@ -433,7 +518,10 @@ static void incoming_request_handler(getdns_context *context, free(qname_str); if (qext) getdns_dict_destroy(qext); - servfail(msg, &response); + if (dns_error == GETDNS_RCODE_SERVFAIL) + servfail(msg, &response); + else + error_reply(msg, &response, dns_error); #if defined(SERVER_DEBUG) && SERVER_DEBUG do { char *request_str = getdns_pretty_print_dict(request); @@ -459,6 +547,402 @@ static void incoming_request_handler(getdns_context *context, getdns_dict_destroy(response); } +#define OPT_PROXY_CONTROL 42 + +static void decode_proxy_option(struct proxy_opt *opt); +static void setup_upstream(struct upstream *usp); + +static struct upstream *get_upstreams_for_policy(getdns_dict *opt_rr) +{ + int i; + unsigned u, proxy_control_opts_count; + uint32_t option_code; + size_t list_count, size; + getdns_list *opt_list; + getdns_dict *opt_dict; + struct upstream *new_upstreams, *usp; + void *data; + getdns_bindata *proxy_control_opts[MAX_PROXY_CONTROL_OPTS]; + + proxy_control_opts_count= 0; + if (getdns_dict_get_list(opt_rr, "/rdata/options", &opt_list) != + GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "get_upstreams_for_policy: no options, should use default\n"); + abort(); + } + + if (getdns_list_get_length(opt_list, &list_count) != + GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "get_upstreams_for_policy: can't get lenght of list\n"); + abort(); + } + for (u= 0; u= MAX_PROXY_CONTROL_OPTS) + { + fprintf(stderr, "get_upstreams_for_policy: too many options in request, should return error\n"); + abort(); + } + + if (getdns_dict_get_bindata(opt_dict, "option_data", + &proxy_control_opts[proxy_control_opts_count]) != + GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "get_upstreams_for_policy: can't get option_data\n"); + abort(); + } + + proxy_control_opts_count++; + + } + + if (!proxy_control_opts_count) + { + fprintf(stderr, + "get_upstreams_for_policy: no options, should use default\n"); + abort(); + } + + /* Try to find an existing upstream */ + for (i= 0, usp= upstreams; iopts_count != proxy_control_opts_count) + continue; + for (u= 0; usize; + if (size != usp->opts[u].bindata.size) + break; + if (size && memcmp(proxy_control_opts[u]->data, + usp->opts[u].bindata.data, size) != 0) + { + break; + } + } + if (u != proxy_control_opts_count) + { + /* No match */ + continue; + } + return usp; + } + + new_upstreams= realloc(upstreams, + (upstreams_count+1)*sizeof(upstreams[0])); + upstreams= new_upstreams; + usp= &upstreams[upstreams_count]; + usp->opts_count= proxy_control_opts_count; + for (u= 0; usize; + if (size) + { + data= malloc(size); + memcpy(data, proxy_control_opts[u]->data, size); + } + else + data= NULL; + + usp->opts[u].bindata.data= data; + usp->opts[u].bindata.size= size; + + decode_proxy_option(&usp->opts[u]); + setup_upstream(usp); + } + upstreams_count++; + + return usp; +} + +static void decode_proxy_option(struct proxy_opt *opt) +{ + uint8_t addr_type, addr_length, name_length, svc_length, inf_length; + uint16_t u16; + int i; + size_t o, size; + uint8_t *p; + struct sockaddr_in6 *sin6p; + + size= opt->bindata.size; + p= opt->bindata.data; + + if (size < 2) + goto error; + + memcpy(&u16, p, 2); + opt->flags1= ntohs(u16); + + p += 2; + size -= 2; + + if (size < 2) + goto error; + + memcpy(&u16, p, 2); + opt->flags2= ntohs(u16); + + p += 2; + size -= 2; + + if (size < 2) + goto error; + + addr_type= p[0]; + addr_length= p[1]; + + p += 2; + size -= 2; + + opt->addr_count= 0; + if (addr_length) + { + if (size < addr_length) + goto error; + + switch(addr_type) + { + case 2: + if (addr_length > + PROXY_ADDRS_MAX*sizeof(struct in6_addr)) + { + fprintf(stderr, + "decode_proxy_option: more addresses than supported\n"); + goto error; + } + for(o= 0, i= 0; + o+sizeof(struct in6_addr) <= addr_length; + o += sizeof(struct in6_addr), i++) + { + sin6p= (struct sockaddr_in6 *) + &opt->addrs[i]; + sin6p->sin6_family= AF_INET6; + memcpy(&sin6p->sin6_addr, + p+o, sizeof(struct in6_addr)); + } + if (o != addr_length) + { + fprintf(stderr, + "decode_proxy_option: bad addr_length\n"); + goto error; + } + opt->addr_count= i; + break; + + default: + fprintf(stderr, + "decode_proxy_option: unknown addr_type %d\n", + addr_type); + goto error; + } + p += addr_length; + size -= addr_length; + } + + if (size < 1) + goto error; + + name_length= p[0]; + + p += 1; + size -= 1; + + if (name_length) + { + fprintf(stderr, "decode_proxy_option: should handle name\n"); + + p += name_length; + size -= name_length; + } + + if (size < 1) + goto error; + + svc_length= p[0]; + + p += 1; + size -= 1; + + if (svc_length) + { + fprintf(stderr, "decode_proxy_option: should handle svc\n"); + + p += svc_length; + size -= svc_length; + } + + if (size < 1) + goto error; + + inf_length= p[0]; + + p += 1; + size -= 1; + + if (inf_length) + { + fprintf(stderr, "decode_proxy_option: should handle inf\n"); + + p += inf_length; + size -= inf_length; + } + + if (size != 0) + { + fprintf(stderr, + "decode_proxy_option: garbage at end of option\n"); + goto error; + } + return; + +error: + fprintf(stderr, "decode_proxy_option: should handle error\n"); + abort(); +} + +#define PROXY_CONTROL_FLAG_U (1 << 15) +#define PROXY_CONTROL_FLAG_UA (1 << 14) +#define PROXY_CONTROL_FLAG_A (1 << 13) +#define PROXY_CONTROL_FLAG_P (1 << 12) +#define PROXY_CONTROL_FLAG_D (1 << 11) + +#define BADPROXYPOLICY 42 + +static void setup_upstream(struct upstream *usp) +{ + int r; + unsigned u, U_flag, UA_flag, A_flag, P_flag, D_flag; + size_t transports_count; + struct proxy_opt *po; + getdns_context *context; + getdns_transport_list_t transports[3]; + + usp->dns_error= 0; + for (u= 0, po= usp->opts; uopts_count; u++, po++) + { + /* Check U, UA, and A flags */ + U_flag= !!(po->flags1 & PROXY_CONTROL_FLAG_U); + UA_flag= !!(po->flags1 & PROXY_CONTROL_FLAG_UA); + A_flag= !!(po->flags1 & PROXY_CONTROL_FLAG_A); + if (U_flag + UA_flag + A_flag > 1) + { + fprintf(stderr, + "setup_upstream: too many of U, UA, A\n"); + usp->dns_error= BADPROXYPOLICY; + goto error; + } + if (U_flag + UA_flag + A_flag == 0) + { + /* No preference. Select GETDNS_TRANSPORT_TLS + * followed by GETDNS_TRANSPORT_UDP and + * GETDNS_TRANSPORT_TCP + */ + transports[0]= GETDNS_TRANSPORT_TLS; + transports[1]= GETDNS_TRANSPORT_UDP; + transports[2]= GETDNS_TRANSPORT_TCP; + transports_count= 3; + } + else if (U_flag) + { + /* Only unencrypted. Select GETDNS_TRANSPORT_UDP + * followed by GETDNS_TRANSPORT_TCP + */ + transports[0]= GETDNS_TRANSPORT_UDP; + transports[1]= GETDNS_TRANSPORT_TCP; + transports_count= 2; + } + else if (UA_flag) + { + /* Only unauthenticated. Select GETDNS_TRANSPORT_TLS + */ + transports[0]= GETDNS_TRANSPORT_TLS; + transports_count= 1; + } + else if (A_flag) + { + fprintf(stderr, + "setup_upstream: authentication not supported by getdns\n"); + usp->dns_error= BADPROXYPOLICY; + goto error; + } + else + { + fprintf(stderr, "setup_upstream: weird state\n"); + abort(); + } + + /* P and D flags can only be set if the A flag is set. */ + P_flag= !!(po->flags1 & PROXY_CONTROL_FLAG_P); + D_flag= !!(po->flags1 & PROXY_CONTROL_FLAG_D); +fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags1, P_flag, D_flag); + if ((P_flag || D_flag) && !A_flag) + { + fprintf(stderr, "setup_upstream: P or D but not A\n"); + usp->dns_error= BADPROXYPOLICY; + goto error; + } + if ((r = getdns_context_create(&context, 1 /*set_from_os*/)) + != GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "setup_upstream: getdns_context_create failed\n"); + abort(); + } + po->context = context; + if ((r = getdns_context_set_dns_transport_list(context, + transports_count, transports)) != GETDNS_RETURN_GOOD) + + { + fprintf(stderr, + "setup_upstream: cannot set transports\n"); + abort(); + } + } + fprintf(stderr, "setup_upstream: not finished\n"); + abort(); +#if 0 +static struct upstream +{ + unsigned opts_count; + struct proxy_opt + { + /* Original bindata of option */ + getdns_bindata bindata; + + /* Decoded option */ + uint16_t flags1; + uint16_t flags2; + int addr_count; + struct sockaddr_storage addrs[PROXY_ADDRS_MAX]; + } opts[MAX_PROXY_CONTROL_OPTS]; +} *upstreams; +#endif +error: + fprintf(stderr, + "setup_upstream: error, should clear getdns contexts\n"); +} + int server_listen(getdns_context *context, int validate_dnssec) { const getdns_list *listen_list; From 158180b5a00939557628a6a1a82fd373a0821f1c Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Mon, 22 Aug 2022 10:34:48 +0200 Subject: [PATCH 02/14] Create a getdns context for each proxy control option --- src/server.c | 129 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 100 insertions(+), 29 deletions(-) diff --git a/src/server.c b/src/server.c index c113e74..f06f74f 100644 --- a/src/server.c +++ b/src/server.c @@ -370,8 +370,10 @@ static struct upstream /* Decoded option */ uint16_t flags1; uint16_t flags2; + char *name; int addr_count; struct sockaddr_storage addrs[PROXY_ADDRS_MAX]; + char *infname; /* Getdns context */ getdns_context *context; @@ -381,7 +383,7 @@ static int upstreams_count; static struct upstream *get_upstreams_for_policy(getdns_dict *opt_rr); -static void incoming_request_handler(getdns_context *context, +static void incoming_request_handler(getdns_context *down_context, getdns_callback_type_t callback_type, getdns_dict *request, void *userarg, getdns_transaction_t request_id) { @@ -403,6 +405,7 @@ static void incoming_request_handler(getdns_context *context, uint32_t rr_type; unsigned dns_error = GETDNS_RCODE_SERVFAIL; struct upstream *usp; + getdns_context *up_context; (void)callback_type; (void)userarg; @@ -410,7 +413,7 @@ static void incoming_request_handler(getdns_context *context, printf("incoming_request_handler: got request %s\n", getdns_pretty_print_dict(request)); - if (!(qext = getdns_dict_create_with_context(context)) || + if (!(qext = getdns_dict_create_with_context(down_context)) || !(msg = malloc(sizeof(dns_msg)))) goto error; @@ -449,7 +452,17 @@ static void incoming_request_handler(getdns_context *context, goto error; } - if ((r = getdns_context_get_resolution_type(context, &msg->rt))) + if (usp) + { + /* Only try first context */ + up_context = usp->opts[0].context; + } + else + { + up_context = down_context; + } + + if ((r = getdns_context_get_resolution_type(up_context, &msg->rt))) stubby_error("Could get resolution type from context: %s", stubby_getdns_strerror(r)); @@ -502,7 +515,7 @@ static void incoming_request_handler(getdns_context *context, stubby_error("Could set class from query: %s", stubby_getdns_strerror(r)); - else if ((r = getdns_general(context, qname_str, qtype, + else if ((r = getdns_general(up_context, qname_str, qtype, qext, msg, &transaction_id, request_cb))) stubby_error("Could not schedule query: %s", stubby_getdns_strerror(r)); @@ -532,11 +545,11 @@ static void incoming_request_handler(getdns_context *context, free(request_str); } while(0); #endif - if ((r = getdns_reply(context, response, request_id))) { + if ((r = getdns_reply(down_context, response, request_id))) { stubby_error("Could not reply: %s", stubby_getdns_strerror(r)); /* Cancel reply */ - getdns_reply(context, NULL, request_id); + getdns_reply(down_context, NULL, request_id); } if (msg) { if (msg->request) @@ -681,7 +694,7 @@ static void decode_proxy_option(struct proxy_opt *opt) uint8_t addr_type, addr_length, name_length, svc_length, inf_length; uint16_t u16; int i; - size_t o, size; + size_t o, llen, no, size; uint8_t *p; struct sockaddr_in6 *sin6p; @@ -768,9 +781,37 @@ static void decode_proxy_option(struct proxy_opt *opt) p += 1; size -= 1; + opt->name= NULL; if (name_length) { - fprintf(stderr, "decode_proxy_option: should handle name\n"); + /* Assume that the decoded name fits in a buffer sized + * name_length + */ + opt->name = malloc(name_length); + o = 0; + no = 0; + while (o < name_length) + { + llen = p[o]; + o++; + if (o + llen > name_length) + goto error; + if (llen) + { + memcpy(opt->name+no, p+o, llen); + no += llen; + opt->name[no] = '.'; + no++; + } + o += llen; + } + if (no == 0) + { + /* Add a '.' */ + opt->name[no] = '.'; + no++; + } + opt->name[no] = '\0'; p += name_length; size -= name_length; @@ -800,9 +841,12 @@ static void decode_proxy_option(struct proxy_opt *opt) p += 1; size -= 1; + opt->infname = NULL; if (inf_length) { - fprintf(stderr, "decode_proxy_option: should handle inf\n"); + opt->infname = malloc(inf_length+1); + memcpy(opt->infname, p, inf_length); + opt->infname[inf_length] = '\0'; p += inf_length; size -= inf_length; @@ -831,12 +875,16 @@ static void decode_proxy_option(struct proxy_opt *opt) static void setup_upstream(struct upstream *usp) { - int r; + int i, r; unsigned u, U_flag, UA_flag, A_flag, P_flag, D_flag; size_t transports_count; struct proxy_opt *po; getdns_context *context; + getdns_list *list; + getdns_dict *dict; + struct sockaddr_in6 *sin6p; getdns_transport_list_t transports[3]; + getdns_bindata bindata; usp->dns_error= 0; for (u= 0, po= usp->opts; uopts_count; u++, po++) @@ -918,26 +966,49 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags "setup_upstream: cannot set transports\n"); abort(); } - } - fprintf(stderr, "setup_upstream: not finished\n"); - abort(); -#if 0 -static struct upstream -{ - unsigned opts_count; - struct proxy_opt - { - /* Original bindata of option */ - getdns_bindata bindata; - /* Decoded option */ - uint16_t flags1; - uint16_t flags2; - int addr_count; - struct sockaddr_storage addrs[PROXY_ADDRS_MAX]; - } opts[MAX_PROXY_CONTROL_OPTS]; -} *upstreams; -#endif + if (po->addr_count) + { + list = getdns_list_create(); + for (i= 0; iaddr_count; i++) + { + dict = getdns_dict_create(); + switch(po->addrs[i].ss_family) + { + case AF_INET6: + sin6p= (struct sockaddr_in6 *) + &po->addrs[i]; + bindata.data = (uint8_t *)"IPv6"; + bindata.size = strlen((char *) + bindata.data); + getdns_dict_set_bindata(dict, + "address_type", &bindata); + bindata.data = (uint8_t *) + &sin6p->sin6_addr; + bindata.size = sizeof(sin6p->sin6_addr); + getdns_dict_set_bindata(dict, + "address_data", &bindata); + break; + default: + fprintf(stderr, + "setup_upstream: unknown address family\n"); + abort(); + } + getdns_list_set_dict(list, i, dict); + } + fprintf(stderr, "setup_upstream: addr list %s\n", + getdns_pretty_print_list(list)); + if ((r = getdns_context_set_upstream_recursive_servers( + context, list)) != GETDNS_RETURN_GOOD) + + { + fprintf(stderr, + "setup_upstream: cannot set upstream list: %d\n", + r); + abort(); + } + } + } error: fprintf(stderr, "setup_upstream: error, should clear getdns contexts\n"); From e30e4cbd0499302d8bbd090145f7a61018ebed0a Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Mon, 22 Aug 2022 13:48:21 +0200 Subject: [PATCH 03/14] Set event loop in new getdns contexts --- src/server.c | 39 ++++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 11 deletions(-) diff --git a/src/server.c b/src/server.c index f06f74f..ef1446a 100644 --- a/src/server.c +++ b/src/server.c @@ -381,7 +381,8 @@ static struct upstream } *upstreams; static int upstreams_count; -static struct upstream *get_upstreams_for_policy(getdns_dict *opt_rr); +static struct upstream *get_upstreams_for_policy(getdns_context *down_context, + getdns_dict *opt_rr); static void incoming_request_handler(getdns_context *down_context, getdns_callback_type_t callback_type, getdns_dict *request, @@ -440,7 +441,7 @@ static void incoming_request_handler(getdns_context *down_context, msg->has_edns0 = 1; (void) getdns_dict_get_int(rr, "do", &msg->do_bit); - usp= get_upstreams_for_policy(rr); + usp= get_upstreams_for_policy(down_context, rr); break; } @@ -563,9 +564,10 @@ static void incoming_request_handler(getdns_context *down_context, #define OPT_PROXY_CONTROL 42 static void decode_proxy_option(struct proxy_opt *opt); -static void setup_upstream(struct upstream *usp); +static void setup_upstream(getdns_context *down_context, struct upstream *usp); -static struct upstream *get_upstreams_for_policy(getdns_dict *opt_rr) +static struct upstream *get_upstreams_for_policy(getdns_context *down_context, + getdns_dict *opt_rr) { int i; unsigned u, proxy_control_opts_count; @@ -682,7 +684,7 @@ static struct upstream *get_upstreams_for_policy(getdns_dict *opt_rr) usp->opts[u].bindata.size= size; decode_proxy_option(&usp->opts[u]); - setup_upstream(usp); + setup_upstream(down_context, usp); } upstreams_count++; @@ -873,19 +875,27 @@ static void decode_proxy_option(struct proxy_opt *opt) #define BADPROXYPOLICY 42 -static void setup_upstream(struct upstream *usp) +static void setup_upstream(getdns_context *down_context, struct upstream *usp) { int i, r; unsigned u, U_flag, UA_flag, A_flag, P_flag, D_flag; size_t transports_count; struct proxy_opt *po; - getdns_context *context; + getdns_context *up_context; getdns_list *list; getdns_dict *dict; struct sockaddr_in6 *sin6p; + getdns_eventloop *eventloop; getdns_transport_list_t transports[3]; getdns_bindata bindata; + r = getdns_context_get_eventloop(down_context, &eventloop); + if (r != GETDNS_RETURN_GOOD) + { + fprintf(stderr, "setup_upstream: unable to get eventloop\n"); + abort(); + } + usp->dns_error= 0; for (u= 0, po= usp->opts; uopts_count; u++, po++) { @@ -950,15 +960,22 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags usp->dns_error= BADPROXYPOLICY; goto error; } - if ((r = getdns_context_create(&context, 1 /*set_from_os*/)) + if ((r = getdns_context_create(&up_context, 1 /*set_from_os*/)) != GETDNS_RETURN_GOOD) { fprintf(stderr, "setup_upstream: getdns_context_create failed\n"); abort(); } - po->context = context; - if ((r = getdns_context_set_dns_transport_list(context, + r = getdns_context_set_eventloop(up_context, eventloop); + if (r != GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "setup_upstream: unable to set eventloop\n"); + abort(); + } + po->context = up_context; + if ((r = getdns_context_set_dns_transport_list(up_context, transports_count, transports)) != GETDNS_RETURN_GOOD) { @@ -999,7 +1016,7 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags fprintf(stderr, "setup_upstream: addr list %s\n", getdns_pretty_print_list(list)); if ((r = getdns_context_set_upstream_recursive_servers( - context, list)) != GETDNS_RETURN_GOOD) + up_context, list)) != GETDNS_RETURN_GOOD) { fprintf(stderr, From edb3d27fa51c1fd5aedae1b473bed20707de76cd Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Mon, 22 Aug 2022 17:06:03 +0200 Subject: [PATCH 04/14] Support for authenticated connections --- src/server.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/server.c b/src/server.c index ef1446a..790ac67 100644 --- a/src/server.c +++ b/src/server.c @@ -939,10 +939,8 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) } else if (A_flag) { - fprintf(stderr, - "setup_upstream: authentication not supported by getdns\n"); - usp->dns_error= BADPROXYPOLICY; - goto error; + transports[0]= GETDNS_TRANSPORT_TLS; + transports_count= 1; } else { @@ -974,6 +972,26 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags "setup_upstream: unable to set eventloop\n"); abort(); } + r = getdns_context_set_resolution_type(up_context, + GETDNS_RESOLUTION_STUB); + if (r != GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "setup_upstream: getdns_context_set_resolution_type failed\n"); + abort(); + } + if (A_flag) + { + r = getdns_context_set_tls_authentication(up_context, + GETDNS_AUTHENTICATION_REQUIRED); + if (r != GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "setup_upstream: getdns_context_set_tls_authentication failed\n"); + abort(); + } + } + po->context = up_context; if ((r = getdns_context_set_dns_transport_list(up_context, transports_count, transports)) != GETDNS_RETURN_GOOD) @@ -1011,6 +1029,14 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags "setup_upstream: unknown address family\n"); abort(); } + if (po->name) + { + bindata.data = (uint8_t *)po->name; + bindata.size = strlen((char *) + bindata.data) - 1; + getdns_dict_set_bindata(dict, + "tls_auth_name", &bindata); + } getdns_list_set_dict(list, i, dict); } fprintf(stderr, "setup_upstream: addr list %s\n", From 0c1f51ba1ad38f2c52d420528635b8c5134b91d9 Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Wed, 24 Aug 2022 12:14:49 +0200 Subject: [PATCH 05/14] Support for the Ax and Dx flags --- src/server.c | 136 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 113 insertions(+), 23 deletions(-) diff --git a/src/server.c b/src/server.c index 790ac67..1335050 100644 --- a/src/server.c +++ b/src/server.c @@ -813,6 +813,8 @@ static void decode_proxy_option(struct proxy_opt *opt) opt->name[no] = '.'; no++; } + if (no > 0 && opt->name[no-1] == '.') + no--; /* Remove trailing dot */ opt->name[no] = '\0'; p += name_length; @@ -867,18 +869,33 @@ static void decode_proxy_option(struct proxy_opt *opt) abort(); } -#define PROXY_CONTROL_FLAG_U (1 << 15) -#define PROXY_CONTROL_FLAG_UA (1 << 14) -#define PROXY_CONTROL_FLAG_A (1 << 13) -#define PROXY_CONTROL_FLAG_P (1 << 12) -#define PROXY_CONTROL_FLAG_D (1 << 11) +#define PROXY_CONTROL_FLAG1_U (1 << 15) +#define PROXY_CONTROL_FLAG1_UA (1 << 14) +#define PROXY_CONTROL_FLAG1_A (1 << 13) +#define PROXY_CONTROL_FLAG1_P (1 << 12) +#define PROXY_CONTROL_FLAG1_D (1 << 11) +#define PROXY_CONTROL_FLAG1_DD (1 << 10) + +#define PROXY_CONTROL_FLAG2_A53 (1 << 15) +#define PROXY_CONTROL_FLAG2_D53 (1 << 14) +#define PROXY_CONTROL_FLAG2_AT (1 << 13) +#define PROXY_CONTROL_FLAG2_DT (1 << 12) +#define PROXY_CONTROL_FLAG2_AH2 (1 << 11) +#define PROXY_CONTROL_FLAG2_DH2 (1 << 10) +#define PROXY_CONTROL_FLAG2_AH3 (1 << 9) +#define PROXY_CONTROL_FLAG2_DH3 (1 << 8) +#define PROXY_CONTROL_FLAG2_AQ (1 << 7) +#define PROXY_CONTROL_FLAG2_DQ (1 << 6) #define BADPROXYPOLICY 42 static void setup_upstream(getdns_context *down_context, struct upstream *usp) { int i, r; - unsigned u, U_flag, UA_flag, A_flag, P_flag, D_flag; + unsigned u, U_flag, UA_flag, A_flag, P_flag, D_flag, DD_flag; + unsigned A53_flag, D53_flag, AT_flag, DT_flag, AH2_flag, DH2_flag, + AH3_flag, DH3_flag, AQ_flag, DQ_flag; + unsigned do_Do53, do_DoT, do_DoH2; size_t transports_count; struct proxy_opt *po; getdns_context *up_context; @@ -899,10 +916,59 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) usp->dns_error= 0; for (u= 0, po= usp->opts; uopts_count; u++, po++) { + /* First set flags for individual protocols, we + * need them later + */ + DD_flag= !!(po->flags1 & PROXY_CONTROL_FLAG1_DD); + A53_flag= !!(po->flags2 & PROXY_CONTROL_FLAG2_A53); + D53_flag= !!(po->flags2 & PROXY_CONTROL_FLAG2_D53); + AT_flag= !!(po->flags2 & PROXY_CONTROL_FLAG2_AT); + DT_flag= !!(po->flags2 & PROXY_CONTROL_FLAG2_DT); + AH2_flag= !!(po->flags2 & PROXY_CONTROL_FLAG2_AH2); + DH2_flag= !!(po->flags2 & PROXY_CONTROL_FLAG2_DH2); + AH3_flag= !!(po->flags2 & PROXY_CONTROL_FLAG2_AH3); + DH3_flag= !!(po->flags2 & PROXY_CONTROL_FLAG2_DH3); + AQ_flag= !!(po->flags2 & PROXY_CONTROL_FLAG2_AQ); + DQ_flag= !!(po->flags2 & PROXY_CONTROL_FLAG2_DQ); + + /* Reject if both Ax and Dx are set */ + if ( (A53_flag && D53_flag) || + (AT_flag && DT_flag) || + (AH2_flag && DH2_flag) || + (AH3_flag && DH3_flag) || + (AQ_flag && DQ_flag)) + { + fprintf(stderr, "setup_upstream: Ax and Dx\n"); + usp->dns_error= BADPROXYPOLICY; + goto error; + } + + /* Compute what protocols we can use. Currently we support + * only Do53, DoT, and DoH2 + */ + if (DD_flag) + { + /* Support only protocols that are explictly + * enabled. + */ + do_Do53 = A53_flag; + do_DoT = AT_flag; + do_DoH2 = AH2_flag; + } + else + { + /* Support all protocols expect those explictly + * disabled. + */ + do_Do53 = D53_flag ? 0 : 1; + do_DoT = DT_flag ? 0 : 1; + do_DoH2 = DH2_flag ? 0 : 1; + } + /* Check U, UA, and A flags */ - U_flag= !!(po->flags1 & PROXY_CONTROL_FLAG_U); - UA_flag= !!(po->flags1 & PROXY_CONTROL_FLAG_UA); - A_flag= !!(po->flags1 & PROXY_CONTROL_FLAG_A); + U_flag= !!(po->flags1 & PROXY_CONTROL_FLAG1_U); + UA_flag= !!(po->flags1 & PROXY_CONTROL_FLAG1_UA); + A_flag= !!(po->flags1 & PROXY_CONTROL_FLAG1_A); if (U_flag + UA_flag + A_flag > 1) { fprintf(stderr, @@ -916,31 +982,44 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) * followed by GETDNS_TRANSPORT_UDP and * GETDNS_TRANSPORT_TCP */ - transports[0]= GETDNS_TRANSPORT_TLS; - transports[1]= GETDNS_TRANSPORT_UDP; - transports[2]= GETDNS_TRANSPORT_TCP; - transports_count= 3; + i = 0; + if (do_DoT || do_DoH2) + transports[i++] = GETDNS_TRANSPORT_TLS; + if (do_Do53) + { + transports[i++] = GETDNS_TRANSPORT_UDP; + transports[i++] = GETDNS_TRANSPORT_TCP; + } + transports_count = i; } else if (U_flag) { /* Only unencrypted. Select GETDNS_TRANSPORT_UDP * followed by GETDNS_TRANSPORT_TCP */ - transports[0]= GETDNS_TRANSPORT_UDP; - transports[1]= GETDNS_TRANSPORT_TCP; - transports_count= 2; + i = 0; + if (do_Do53) + { + transports[i++] = GETDNS_TRANSPORT_UDP; + transports[i++] = GETDNS_TRANSPORT_TCP; + } + transports_count = i; } else if (UA_flag) { /* Only unauthenticated. Select GETDNS_TRANSPORT_TLS */ - transports[0]= GETDNS_TRANSPORT_TLS; - transports_count= 1; + i = 0; + if (do_DoT || do_DoH2) + transports[i++] = GETDNS_TRANSPORT_TLS; + transports_count = i; } else if (A_flag) { - transports[0]= GETDNS_TRANSPORT_TLS; - transports_count= 1; + i = 0; + if (do_DoT || do_DoH2) + transports[i++] = GETDNS_TRANSPORT_TLS; + transports_count = i; } else { @@ -948,9 +1027,17 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) abort(); } + if (transports_count == 0) + { + fprintf(stderr, + "setup_upstream: no matching transports\n"); + usp->dns_error= BADPROXYPOLICY; + goto error; + } + /* P and D flags can only be set if the A flag is set. */ - P_flag= !!(po->flags1 & PROXY_CONTROL_FLAG_P); - D_flag= !!(po->flags1 & PROXY_CONTROL_FLAG_D); + P_flag= !!(po->flags1 & PROXY_CONTROL_FLAG1_P); + D_flag= !!(po->flags1 & PROXY_CONTROL_FLAG1_D); fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags1, P_flag, D_flag); if ((P_flag || D_flag) && !A_flag) { @@ -958,6 +1045,7 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags usp->dns_error= BADPROXYPOLICY; goto error; } + if ((r = getdns_context_create(&up_context, 1 /*set_from_os*/)) != GETDNS_RETURN_GOOD) { @@ -1033,7 +1121,7 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags { bindata.data = (uint8_t *)po->name; bindata.size = strlen((char *) - bindata.data) - 1; + bindata.data); getdns_dict_set_bindata(dict, "tls_auth_name", &bindata); } @@ -1052,6 +1140,8 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags } } } + return; + error: fprintf(stderr, "setup_upstream: error, should clear getdns contexts\n"); From 777db1755b9ee3ee58d0a7906b53e986cd21f873 Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Wed, 31 Aug 2022 15:13:27 +0200 Subject: [PATCH 06/14] Add proxy control option in reply that describes transport actually used for query --- src/server.c | 608 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 606 insertions(+), 2 deletions(-) diff --git a/src/server.c b/src/server.c index 1335050..97cc721 100644 --- a/src/server.c +++ b/src/server.c @@ -85,6 +85,7 @@ typedef struct dns_msg { uint32_t do_bit; uint32_t cd_bit; int has_edns0; + struct upstream *upstream; } dns_msg; #if defined(SERVER_DEBUG) && SERVER_DEBUG @@ -197,6 +198,9 @@ static getdns_return_t _handle_edns0( return GETDNS_RETURN_GOOD; } +static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, + size_t bufsize); + static void request_cb( getdns_context *context, getdns_callback_type_t callback_type, getdns_dict *response, void *userarg, getdns_transaction_t transaction_id) @@ -209,6 +213,10 @@ static void request_cb( size_t n_options; uint32_t arcount; char i_as_jptr[80]; + static uint8_t optbuf[2048]; + + fprintf(stderr, "request_cb: got response %s\n", + getdns_pretty_print_dict(response)); #if defined(SERVER_DEBUG) && SERVER_DEBUG getdns_bindata *qname; @@ -340,6 +348,12 @@ static void request_cb( (void) getdns_dict_remove_name(response, i_as_jptr); } } + + if (msg->upstream) + { + response_add_proxy_option(response, optbuf, sizeof(optbuf)); + } + if ((r = getdns_reply(context, response, msg->request_id))) { stubby_error("Could not reply: %s", stubby_getdns_strerror(r)); /* Cancel reply */ @@ -425,6 +439,7 @@ static void incoming_request_handler(getdns_context *down_context, msg->ad_bit = msg->do_bit = msg->cd_bit = 0; msg->has_edns0 = 0; msg->rt = GETDNS_RESOLUTION_RECURSING; + msg->upstream = NULL; (void) getdns_dict_get_int(request, "/header/ad", &msg->ad_bit); (void) getdns_dict_get_int(request, "/header/cd", &msg->cd_bit); usp= NULL; @@ -443,6 +458,11 @@ static void incoming_request_handler(getdns_context *down_context, usp= get_upstreams_for_policy(down_context, rr); + /* Delete all options. Assume that options are not + * transitive. Some options may need to be copied. + */ + getdns_dict_remove_name(rr, "/rdata/options"); + break; } } @@ -455,8 +475,17 @@ static void incoming_request_handler(getdns_context *down_context, if (usp) { + msg->upstream = usp; + /* Only try first context */ up_context = usp->opts[0].context; + + if ((r = getdns_dict_set_int(qext, "return_call_reporting", + GETDNS_EXTENSION_TRUE))) + { + stubby_error("Could set return_call_reporting: %s", + stubby_getdns_strerror(r)); + } } else { @@ -561,7 +590,7 @@ static void incoming_request_handler(getdns_context *down_context, getdns_dict_destroy(response); } -#define OPT_PROXY_CONTROL 42 +#define GLDNS_EDNS_PROXY_CONTROL 42 static void decode_proxy_option(struct proxy_opt *opt); static void setup_upstream(getdns_context *down_context, struct upstream *usp); @@ -611,7 +640,7 @@ static struct upstream *get_upstreams_for_policy(getdns_context *down_context, "get_upstreams_for_policy: can't get option_code\n"); abort(); } - if (option_code != OPT_PROXY_CONTROL) + if (option_code != GLDNS_EDNS_PROXY_CONTROL) continue; if (proxy_control_opts_count >= MAX_PROXY_CONTROL_OPTS) @@ -698,6 +727,7 @@ static void decode_proxy_option(struct proxy_opt *opt) int i; size_t o, llen, no, size; uint8_t *p; + struct sockaddr_in *sin4p; struct sockaddr_in6 *sin6p; size= opt->bindata.size; @@ -738,6 +768,33 @@ static void decode_proxy_option(struct proxy_opt *opt) switch(addr_type) { + case 1: + if (addr_length > + PROXY_ADDRS_MAX*sizeof(struct in_addr)) + { + fprintf(stderr, + "decode_proxy_option: more addresses than supported\n"); + goto error; + } + for(o= 0, i= 0; + o+sizeof(struct in_addr) <= addr_length; + o += sizeof(struct in_addr), i++) + { + sin4p= (struct sockaddr_in *) + &opt->addrs[i]; + sin4p->sin_family= AF_INET; + memcpy(&sin4p->sin_addr, + p+o, sizeof(struct in_addr)); + } + if (o != addr_length) + { + fprintf(stderr, + "decode_proxy_option: bad addr_length\n"); + goto error; + } + opt->addr_count= i; + break; + case 2: if (addr_length > PROXY_ADDRS_MAX*sizeof(struct in6_addr)) @@ -901,6 +958,7 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) getdns_context *up_context; getdns_list *list; getdns_dict *dict; + struct sockaddr_in *sin4p; struct sockaddr_in6 *sin6p; getdns_eventloop *eventloop; getdns_transport_list_t transports[3]; @@ -1098,6 +1156,20 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags dict = getdns_dict_create(); switch(po->addrs[i].ss_family) { + case AF_INET: + sin4p= (struct sockaddr_in *) + &po->addrs[i]; + bindata.data = (uint8_t *)"IPv4"; + bindata.size = strlen((char *) + bindata.data); + getdns_dict_set_bindata(dict, + "address_type", &bindata); + bindata.data = (uint8_t *) + &sin4p->sin_addr; + bindata.size = sizeof(sin4p->sin_addr); + getdns_dict_set_bindata(dict, + "address_data", &bindata); + break; case AF_INET6: sin6p= (struct sockaddr_in6 *) &po->addrs[i]; @@ -1147,6 +1219,538 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags "setup_upstream: error, should clear getdns contexts\n"); } +#define POLICY_N_ADDR 3 +#define POLICY_N_SVCPARAMS 8 + +typedef struct getdns_proxy_policy { + uint16_t flags1, flags2; + 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; + +static void proxy_policy2opt(getdns_proxy_policy *policy, int do_ipv6, + uint8_t *buf, size_t *sizep); + +static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, + size_t bufsize) +{ + unsigned u; + uint32_t transport, type; + size_t add_list_len, options_len; + getdns_bindata *bindatap; + getdns_list *add_list, *opt_list; + getdns_dict *rr_dict, *proxy_rr; + struct sockaddr_in *sin4p; + struct sockaddr_in6 *sin6p; + getdns_proxy_policy policy; + getdns_bindata bindata; + + fprintf(stderr, "response_add_proxy_option(start): response %s\n", + getdns_pretty_print_dict(response)); + + if (getdns_dict_get_int(response, "/call_reporting/0/transport", + &transport) != GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "reponse_add_proxy_option: can't get /call_reporting/0/transport\n"); + return; + } + policy.flags1 = 0; + policy.flags2 = 0; + switch(transport) + { + case GETDNS_TRANSPORT_UDP: + case GETDNS_TRANSPORT_TCP: + policy.flags1 |= PROXY_CONTROL_FLAG1_U; + policy.flags2 |= PROXY_CONTROL_FLAG2_A53; + break; + + case GETDNS_TRANSPORT_TLS: + policy.flags2 |= PROXY_CONTROL_FLAG2_AT; + + if (getdns_dict_get_bindata(response, + "/call_reporting/0/tls_auth_status", + &bindatap) != GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "reponse_add_proxy_option: can't get " + "/call_reporting/0//call_reporting/0/tls_auth_status\n"); + return; + } + + if (bindatap->size == 4 && + memcmp(bindatap->data, "None", 4) == 0) + { + /* No authentication */ + policy.flags1 |= PROXY_CONTROL_FLAG1_UA; + } + else if (bindatap->size == 6 && + memcmp(bindatap->data, "Failed", 4) == 0) + { + /* Authentication failed */ + policy.flags1 |= PROXY_CONTROL_FLAG1_UA; + + /* We should check if authentication was required */ + } + else + { + + fprintf(stderr, + "reponse_add_proxy_option: unknown tls_auth_status '%.*s'\n", + (int)bindatap->size, bindatap->data); + abort(); + } + break; + + default: + fprintf(stderr, + "reponse_add_proxy_option: unknown transport %d\n", + transport); + abort(); + } + + policy.flags1 |= PROXY_CONTROL_FLAG1_DD; + + if (getdns_dict_get_bindata(response, + "/call_reporting/0/query_to/address_type", &bindatap) != + GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "reponse_add_proxy_option: can't get query_to/address_type\n"); + return; + } + if (bindatap->size == 4 && memcmp(bindatap->data, "IPv4", 4) == 0) + { + if (getdns_dict_get_bindata(response, + "/call_reporting/0/query_to/address_data", + &bindatap) != GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "reponse_add_proxy_option: can't get query_to/address_data\n"); + return; + } + sin4p= (struct sockaddr_in *)&policy.addrs[0]; + sin4p->sin_family = AF_INET; + memcpy(&sin4p->sin_addr, bindatap->data, + sizeof(sin4p->sin_addr)); + policy.addr_count = 1; + } + else if (bindatap->size == 4 && memcmp(bindatap->data, "IPv6", 4) == 0) + { + if (getdns_dict_get_bindata(response, + "/call_reporting/0/query_to/address_data", + &bindatap) != GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "reponse_add_proxy_option: can't get query_to/address_data\n"); + return; + } + sin6p= (struct sockaddr_in6 *)&policy.addrs[0]; + sin6p->sin6_family = AF_INET6; + memcpy(&sin6p->sin6_addr, bindatap->data, + sizeof(sin6p->sin6_addr)); + policy.addr_count = 1; + } + else + { + fprintf(stderr, + "reponse_add_proxy_option: unknown address type %.*s\n", + (int)bindatap->size, bindatap->data); + abort(); + } + + policy.domainname = NULL; + policy.svcparams[0].key = NULL; + policy.interface = NULL; + + proxy_policy2opt(&policy, 1, buf, &bufsize); + fprintf(stderr, "reponse_add_proxy_option: size %lu\n", bufsize); + fprintf(stderr, "reponse_add_proxy_option: buf"); + for (u = 0; u= add_list_len) + { + fprintf(stderr, "reponse_add_proxy_option: add OPT RR\n"); + abort(); + } + getdns_dict_remove_name(rr_dict, "/rdata/rdata_raw"); + if (getdns_dict_get_list(rr_dict, "/rdata/options", &opt_list) != + GETDNS_RETURN_GOOD) + { + opt_list = getdns_list_create(); + getdns_dict_set_list(rr_dict, "/rdata/options", opt_list); + getdns_dict_get_list(rr_dict, "/rdata/options", &opt_list); + } + if (getdns_list_get_length(opt_list, &options_len) != + GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "reponse_add_proxy_option: can't get length for options\n"); + abort(); + } + proxy_rr = getdns_dict_create(); + getdns_dict_set_int(proxy_rr, "option_code", GLDNS_EDNS_PROXY_CONTROL); + bindata.data = buf; + bindata.size = bufsize; + getdns_dict_set_bindata(proxy_rr, "option_data", &bindata); + getdns_list_set_dict(opt_list, options_len, proxy_rr); + + fprintf(stderr, "reponse_add_proxy_option(end): result dict: %s\n", + getdns_pretty_print_dict(response)); +} + +#include + +#define SVC_KEY_ALPN 1 + +/* Note: this table must be kept sort based on 'value' */ +static struct +{ + uint16_t value; + char *key; +} svckeys[] = +{ + { SVC_KEY_ALPN, "alpn" }, /* 1 */ + { 0, NULL } +}; + +/* Copied from getdns/src/context.c. This needs to be a library function */ +static void proxy_policy2opt(getdns_proxy_policy *policy, int do_ipv6, + uint8_t *buf, size_t *sizep) +{ + uint8_t inflen; + uint16_t key16, len16; + int i, j, addr_count; + ptrdiff_t len, totlen; + size_t datalen; + char *l, *dot, *key, *v, *vp, *comma; + uint8_t *bp, *addr_type, *addr_len, *addrp, *domainlenp, + *domainp, *svclenp, *svcp, *inflenp, *infp, *lastp, *endp, + *datap, *wire_keyp, *wire_lenp, *wire_datap; + uint16_t *flags1p, *flags2p; + struct sockaddr_in *sin4p; + struct sockaddr_in6 *sin6p; + uint8_t wirebuf[256]; + + endp= buf + *sizep; + + flags1p= (uint16_t *)buf; + flags2p= &flags1p[1]; + + addr_type= (uint8_t *)&flags2p[1]; + addr_len= &addr_type[1]; + + *flags1p= htons(policy->flags1); + *flags2p= htons(policy->flags2); + +fprintf(stderr, "proxy_policy2opt: flag1 0x%x, flags2 0x%x\n", + ntohs(*flags1p), ntohs(*flags2p)); + + /* Count the number of addresses to include */ + addr_count= 0; + for (i= 0; iaddr_count; i++) + { + if (!do_ipv6 && policy->addrs[i].ss_family == AF_INET) + addr_count++; + if (do_ipv6 && policy->addrs[i].ss_family == AF_INET6) + addr_count++; + } + + if (addr_count) + { + if (do_ipv6) + { + *addr_type = 2; + *addr_len = addr_count * sizeof(struct in6_addr); + addrp= &addr_len[1]; + if (endp - addrp < *addr_len) + { + fprintf(stderr, + "proxy_policy2opt: not enogh space\n"); + abort(); + } + for (i= 0; iaddr_count; i++) + { + if (policy->addrs[i].ss_family != AF_INET6) + continue; + sin6p = (struct sockaddr_in6 *) + &policy->addrs[i]; + memcpy(addrp, &sin6p->sin6_addr, + sizeof(sin6p->sin6_addr)); + addrp += sizeof(sin6p->sin6_addr); + } + } + else + { + *addr_type = 1; + *addr_len = addr_count * sizeof(struct in_addr); + addrp= &addr_len[1]; + if (endp - addrp < *addr_len) + { + fprintf(stderr, + "proxy_policy2opt: not enogh space\n"); + abort(); + } + for (i= 0; iaddr_count; i++) + { + if (policy->addrs[i].ss_family != AF_INET) + continue; + sin4p = (struct sockaddr_in *) + &policy->addrs[i]; + memcpy(addrp, &sin4p->sin_addr, + sizeof(sin4p->sin_addr)); + addrp += sizeof(sin4p->sin_addr); + } + } + assert (addrp - &addr_len[1] == *addr_len); + domainlenp= addrp; + } + else + { + *addr_type = 0; + *addr_len = 0; + domainlenp= &addr_len[1]; + } + + if (endp - domainlenp < 1) + { + fprintf(stderr, + "proxy_policy2opt: not enough space\n"); + abort(); + } + *domainlenp = 0; + domainp= &domainlenp[1]; + if (policy->domainname) + { + l= policy->domainname; + while(l) + { + dot= strchr(l, '.'); + if (dot) + len= dot-l; + else + len= strlen(l); + if (len > 63) + { + fprintf(stderr, + "proxy_policy2opt: bad label length\n"); + abort(); + } + if (endp - domainlenp < 1) + { + fprintf(stderr, + "proxy_policy2opt: not enough space\n"); + abort(); + } + *domainp= len; + domainp++; + if (endp - domainlenp < len) + { + fprintf(stderr, + "proxy_policy2opt: not enough space\n"); + abort(); + } + memcpy(domainp, l, len); + domainp += len; + + if (!dot) + break; + l= dot+1; + if (l[0] == '\0') + break; + } + + /* Add trailing label */ + if (endp - domainlenp < 1) + { + fprintf(stderr, "proxy_policy2opt: not enough space\n"); + abort(); + } + *domainp= 0; + domainp++; + + *domainlenp = domainp - &domainlenp[1]; + } + + svclenp = domainp; + if (endp - svclenp < 1) + { + fprintf(stderr, + "proxy_policy2opt: not enough space\n"); + abort(); + } + *svclenp = 0; + svcp = &svclenp[1]; + if (policy->svcparams[0].key != NULL) + { + for (i = 0; svckeys[i].key != NULL; i++) + { + key = svckeys[i].key; + for (j = 0; jsvcparams[j].key == NULL) + break; + if (strcmp(key, policy->svcparams[j].key) == 0) + break; + } + if (j >= POLICY_N_SVCPARAMS || + policy->svcparams[j].key == NULL) + { + /* Key not needed */ + continue; + } + + switch(svckeys[i].value) + { + case SVC_KEY_ALPN: + v = policy->svcparams[j].value; + if (v == NULL) + { + fprintf(stderr, + "proxy_policy2opt: value expected for alpn\n"); + abort(); + } + if (strlen(v) + 1 > sizeof(wirebuf)) + { + fprintf(stderr, + "proxy_policy2opt: alpn list too long\n"); + abort(); + } + vp= v; + bp= wirebuf; + while (vp) + { + comma = strchr(vp, ','); + if (comma) + { + len = comma - vp; + *bp = len; + bp++; + memcpy(bp, vp, len); + bp += len; + vp = comma + 1; + continue; + } + len = strlen(vp); + *bp = len; + bp++; + memcpy(bp, vp, len); + bp += len; + break;; + } + datap = wirebuf; + datalen = bp-wirebuf; + break; + + default: + fprintf(stderr, + "proxy_policy2opt: unknown svc key value\n"); + abort(); + } + + totlen = 4 + datalen; + if (endp - svcp < totlen) + { + fprintf(stderr, + "proxy_policy2opt: not enough space\n"); + abort(); + } + wire_keyp = svcp; + wire_lenp = &wire_keyp[2]; + wire_datap = &wire_lenp[2]; + + key16 = htons(svckeys[i].value); + memcpy(wire_keyp, &key16, 2); + len16 = htons(datalen); + memcpy(wire_lenp, &len16, 2); + if (datalen) + memcpy(wire_datap, datap, datalen); + svcp = wire_datap + datalen; + } + } + len = svcp - &svclenp[1]; + if (len > 255) + { + fprintf(stderr, "svc too large\n"); + abort(); + } + *svclenp = len; + + inflenp = svcp; + if (endp - inflenp < 1) + { + fprintf(stderr, + "proxy_policy2opt: not enough space\n"); + abort(); + } + + inflen = 0; + if (policy->interface) + { + inflen= strlen(policy->interface); + } + *inflenp = inflen; + infp= &inflenp[1]; + if (inflen) + { + if (endp - infp < inflen) + { + fprintf(stderr, + "proxy_policy2opt: not enough space\n"); + abort(); + } + memcpy(infp, policy->interface, inflen); + } +fprintf(stderr, "inflen %d, interface %s\n", inflen, policy->interface); + + lastp = &infp[inflen]; + + *sizep= lastp - buf; +} + + int server_listen(getdns_context *context, int validate_dnssec) { const getdns_list *listen_list; From 4b8e37abc5bb4453d04d987e69213bdaaa90a0e7 Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Fri, 2 Sep 2022 15:09:29 +0200 Subject: [PATCH 07/14] Support 'Success' and no OPT record --- src/server.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/server.c b/src/server.c index 97cc721..1dec74c 100644 --- a/src/server.c +++ b/src/server.c @@ -398,6 +398,8 @@ static int upstreams_count; static struct upstream *get_upstreams_for_policy(getdns_context *down_context, getdns_dict *opt_rr); +#define BADPROXYPOLICY 42 + static void incoming_request_handler(getdns_context *down_context, getdns_callback_type_t callback_type, getdns_dict *request, void *userarg, getdns_transaction_t request_id) @@ -547,9 +549,14 @@ static void incoming_request_handler(getdns_context *down_context, else if ((r = getdns_general(up_context, qname_str, qtype, qext, msg, &transaction_id, request_cb))) + { stubby_error("Could not schedule query: %s", stubby_getdns_strerror(r)); + dns_error = BADPROXYPOLICY; + } else { + fprintf(stderr, "incoming_request_handler: qext %s\n", + getdns_pretty_print_dict(qext)); DEBUG_SERVER("scheduled: %p %"PRIu64" for %s %d\n", (void *)msg, transaction_id, qname_str, (int)qtype); getdns_dict_destroy(qext); @@ -944,8 +951,6 @@ static void decode_proxy_option(struct proxy_opt *opt) #define PROXY_CONTROL_FLAG2_AQ (1 << 7) #define PROXY_CONTROL_FLAG2_DQ (1 << 6) -#define BADPROXYPOLICY 42 - static void setup_upstream(getdns_context *down_context, struct upstream *usp) { int i, r; @@ -1292,13 +1297,19 @@ static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, policy.flags1 |= PROXY_CONTROL_FLAG1_UA; } else if (bindatap->size == 6 && - memcmp(bindatap->data, "Failed", 4) == 0) + memcmp(bindatap->data, "Failed", 6) == 0) { /* Authentication failed */ policy.flags1 |= PROXY_CONTROL_FLAG1_UA; /* We should check if authentication was required */ } + else if (bindatap->size == 7 && + memcmp(bindatap->data, "Success", 7) == 0) + { + /* Authentication succeeded */ + policy.flags1 |= PROXY_CONTROL_FLAG1_A; + } else { @@ -1328,6 +1339,7 @@ static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, } if (bindatap->size == 4 && memcmp(bindatap->data, "IPv4", 4) == 0) { +fprintf(stderr, "reponse_add_proxy_option: IPv4 address\n"); if (getdns_dict_get_bindata(response, "/call_reporting/0/query_to/address_data", &bindatap) != GETDNS_RETURN_GOOD) @@ -1344,6 +1356,7 @@ static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, } else if (bindatap->size == 4 && memcmp(bindatap->data, "IPv6", 4) == 0) { +fprintf(stderr, "reponse_add_proxy_option: IPv6 address\n"); if (getdns_dict_get_bindata(response, "/call_reporting/0/query_to/address_data", &bindatap) != GETDNS_RETURN_GOOD) @@ -1370,7 +1383,8 @@ static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, policy.svcparams[0].key = NULL; policy.interface = NULL; - proxy_policy2opt(&policy, 1, buf, &bufsize); + proxy_policy2opt(&policy, policy.addrs[0].ss_family == AF_INET6, + buf, &bufsize); fprintf(stderr, "reponse_add_proxy_option: size %lu\n", bufsize); fprintf(stderr, "reponse_add_proxy_option: buf"); for (u = 0; u= add_list_len) { - fprintf(stderr, "reponse_add_proxy_option: add OPT RR\n"); - abort(); + rr_dict = getdns_dict_create(); + getdns_dict_set_int(rr_dict, "do", 0); + getdns_dict_set_int(rr_dict, "extended_rcode", 0); + getdns_dict_set_int(rr_dict, "type", GETDNS_RRTYPE_OPT); + getdns_dict_set_int(rr_dict, "udp_payload_size", 512); + getdns_dict_set_int(rr_dict, "version", 0); + getdns_dict_set_int(rr_dict, "z", 0); + getdns_list_set_dict(add_list, u, rr_dict); + if (getdns_list_get_dict(add_list, u, &rr_dict) != + GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "reponse_add_proxy_option: failed to get list item %d\n", + u); + abort(); + } } getdns_dict_remove_name(rr_dict, "/rdata/rdata_raw"); if (getdns_dict_get_list(rr_dict, "/rdata/options", &opt_list) != From c1e1840287c55d41099057684f4bfad018a5600f Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Fri, 2 Sep 2022 16:18:47 +0200 Subject: [PATCH 08/14] Currently no support for selection of the outgoing interface --- src/server.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/server.c b/src/server.c index 1dec74c..f7c269c 100644 --- a/src/server.c +++ b/src/server.c @@ -549,11 +549,8 @@ static void incoming_request_handler(getdns_context *down_context, else if ((r = getdns_general(up_context, qname_str, qtype, qext, msg, &transaction_id, request_cb))) - { stubby_error("Could not schedule query: %s", stubby_getdns_strerror(r)); - dns_error = BADPROXYPOLICY; - } else { fprintf(stderr, "incoming_request_handler: qext %s\n", getdns_pretty_print_dict(qext)); @@ -568,6 +565,7 @@ static void incoming_request_handler(getdns_context *down_context, free(qname_str); if (qext) getdns_dict_destroy(qext); + fprintf(stderr, "incoming_request_handler: dns_error %d\n", dns_error); if (dns_error == GETDNS_RCODE_SERVFAIL) servfail(msg, &response); else @@ -1109,6 +1107,15 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags goto error; } + if (po->infname) + { + /* Currently we can't support selection of + * outgoing interfaces. + */ + usp->dns_error= BADPROXYPOLICY; + goto error; + } + if ((r = getdns_context_create(&up_context, 1 /*set_from_os*/)) != GETDNS_RETURN_GOOD) { From 3d5632852826736b14a5b86b6f19117f7bce97da Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Thu, 8 Sep 2022 19:11:09 +0200 Subject: [PATCH 09/14] Cancelled outstanding queries should keep the call_reporting --- src/server.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/server.c b/src/server.c index f7c269c..5e80cf3 100644 --- a/src/server.c +++ b/src/server.c @@ -100,24 +100,31 @@ typedef struct dns_msg { static void servfail(dns_msg *msg, getdns_dict **resp_p) { - getdns_dict *dict; + getdns_dict *dict; + getdns_list *list; + getdns_dict *new_resp; - if (*resp_p) + if (!(new_resp = getdns_dict_create())) { getdns_dict_destroy(*resp_p); - if (!(*resp_p = getdns_dict_create())) + *resp_p = NULL; return; + } if (msg) { if (!getdns_dict_get_dict(msg->request, "header", &dict)) - getdns_dict_set_dict(*resp_p, "header", dict); + getdns_dict_set_dict(new_resp, "header", dict); if (!getdns_dict_get_dict(msg->request, "question", &dict)) - getdns_dict_set_dict(*resp_p, "question", dict); - (void) getdns_dict_set_int(*resp_p, "/header/ra", + getdns_dict_set_dict(new_resp, "question", dict); + (void) getdns_dict_set_int(new_resp, "/header/ra", msg->rt == GETDNS_RESOLUTION_RECURSING ? 1 : 0); } (void) getdns_dict_set_int( - *resp_p, "/header/rcode", GETDNS_RCODE_SERVFAIL); - (void) getdns_dict_set_int(*resp_p, "/header/qr", 1); - (void) getdns_dict_set_int(*resp_p, "/header/ad", 0); + new_resp, "/header/rcode", GETDNS_RCODE_SERVFAIL); + (void) getdns_dict_set_int(new_resp, "/header/qr", 1); + (void) getdns_dict_set_int(new_resp, "/header/ad", 0); + if (!getdns_dict_get_list(*resp_p, "call_reporting", &list)) + getdns_dict_set_list(new_resp, "call_reporting", list); + getdns_dict_destroy(*resp_p); + *resp_p = new_resp; } static void error_reply(dns_msg *msg, getdns_dict **resp_p, unsigned error) @@ -1112,6 +1119,7 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags /* Currently we can't support selection of * outgoing interfaces. */ + fprintf(stderr, "setup_upstream: interface name not supported yet\n"); usp->dns_error= BADPROXYPOLICY; goto error; } From a3c38df314b6ac5b10c9d6199dc9b06c54fc9330 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 13 Sep 2022 22:25:04 +0200 Subject: [PATCH 10/14] Equip upstreams with DANE (if required) --- src/server.c | 315 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 267 insertions(+), 48 deletions(-) diff --git a/src/server.c b/src/server.c index 5e80cf3..addd040 100644 --- a/src/server.c +++ b/src/server.c @@ -379,6 +379,15 @@ static void request_cb( #define MAX_PROXY_CONTROL_OPTS 10 #define PROXY_ADDRS_MAX 10 +struct incoming_request { + getdns_context *down_context; + getdns_callback_type_t callback_type; + getdns_dict *request; + void *userarg; + getdns_transaction_t request_id; + struct incoming_request *next; +}; + static struct upstream { unsigned dns_error; @@ -397,7 +406,10 @@ static struct upstream char *infname; /* Getdns context */ - getdns_context *context; + getdns_context *context; + int ready; + getdns_transaction_t dane_request_id; + struct incoming_request *incoming_requests; } opts[MAX_PROXY_CONTROL_OPTS]; } *upstreams; static int upstreams_count; @@ -407,6 +419,44 @@ static struct upstream *get_upstreams_for_policy(getdns_context *down_context, #define BADPROXYPOLICY 42 +#define PROXY_CONTROL_FLAG1_U (1 << 15) +#define PROXY_CONTROL_FLAG1_UA (1 << 14) +#define PROXY_CONTROL_FLAG1_A (1 << 13) +#define PROXY_CONTROL_FLAG1_P (1 << 12) +#define PROXY_CONTROL_FLAG1_D (1 << 11) +#define PROXY_CONTROL_FLAG1_DD (1 << 10) + +#define PROXY_CONTROL_FLAG2_A53 (1 << 15) +#define PROXY_CONTROL_FLAG2_D53 (1 << 14) +#define PROXY_CONTROL_FLAG2_AT (1 << 13) +#define PROXY_CONTROL_FLAG2_DT (1 << 12) +#define PROXY_CONTROL_FLAG2_AH2 (1 << 11) +#define PROXY_CONTROL_FLAG2_DH2 (1 << 10) +#define PROXY_CONTROL_FLAG2_AH3 (1 << 9) +#define PROXY_CONTROL_FLAG2_DH3 (1 << 8) +#define PROXY_CONTROL_FLAG2_AQ (1 << 7) +#define PROXY_CONTROL_FLAG2_DQ (1 << 6) + + +#define POLICY_N_ADDR 3 +#define POLICY_N_SVCPARAMS 8 + +typedef struct getdns_proxy_policy { + uint16_t flags1, flags2; + 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; + +static void proxy_policy2opt(getdns_proxy_policy *policy, int do_ipv6, + uint8_t *buf, size_t *sizep); + static void incoming_request_handler(getdns_context *down_context, getdns_callback_type_t callback_type, getdns_dict *request, void *userarg, getdns_transaction_t request_id) @@ -423,7 +473,7 @@ static void incoming_request_handler(getdns_context *down_context, getdns_dict *qext = NULL; dns_msg *msg = NULL; getdns_dict *response = NULL; - size_t i, len; + size_t i, len = 0; getdns_list *additional; getdns_dict *rr; uint32_t rr_type; @@ -466,12 +516,6 @@ static void incoming_request_handler(getdns_context *down_context, (void) getdns_dict_get_int(rr, "do", &msg->do_bit); usp= get_upstreams_for_policy(down_context, rr); - - /* Delete all options. Assume that options are not - * transitive. Some options may need to be copied. - */ - getdns_dict_remove_name(rr, "/rdata/options"); - break; } } @@ -479,6 +523,11 @@ static void incoming_request_handler(getdns_context *down_context, if (usp && usp->dns_error) { dns_error= usp->dns_error; + + /* Delete all options. Assume that options are not + * transitive. Some options may need to be copied. + */ + getdns_dict_remove_name(request, "/additional/0/rdata/options"); goto error; } @@ -488,6 +537,24 @@ static void incoming_request_handler(getdns_context *down_context, /* Only try first context */ up_context = usp->opts[0].context; + if (!usp->opts[0].ready) { + struct incoming_request *ir; + + fprintf(stderr, "queueing incoming request waiting " + "for upstream to get ready\n"); + getdns_dict_destroy(qext); + free(msg); + if (!(ir = malloc(sizeof(struct incoming_request)))) + goto error; + ir->down_context = down_context; + ir->callback_type = callback_type; + ir->request = request; + ir->userarg = userarg; + ir->request_id = request_id; + ir->next = usp->opts[0].incoming_requests; + usp->opts[0].incoming_requests = ir; + return; + } if ((r = getdns_dict_set_int(qext, "return_call_reporting", GETDNS_EXTENSION_TRUE))) @@ -499,6 +566,10 @@ static void incoming_request_handler(getdns_context *down_context, else { up_context = down_context; + /* Delete all options. Assume that options are not + * transitive. Some options may need to be copied. + */ + getdns_dict_remove_name(request, "/additional/0/rdata/options"); } if ((r = getdns_context_get_resolution_type(up_context, &msg->rt))) @@ -602,6 +673,162 @@ static void incoming_request_handler(getdns_context *down_context, getdns_dict_destroy(response); } +static void dane_request_cb( + getdns_context *context, getdns_callback_type_t callback_type, + getdns_dict *response, void *userarg, getdns_transaction_t transaction_id) +{ + struct proxy_opt *po = (struct proxy_opt *)userarg; + getdns_return_t r = GETDNS_RETURN_GOOD; + uint32_t rcode, dnssec_status, ancount; + char *dict_str; + getdns_list *upstreams; + getdns_list *answers; + + if ((dict_str = getdns_pretty_print_dict(response))) { + stubby_debug("dane_request_cb: got reponse %s\n", dict_str); + free(dict_str); + } + if (!response + || getdns_dict_get_int( response, "/replies_tree/0/header/rcode" + , &rcode) + || getdns_dict_get_int( response, "/replies_tree/0/dnssec_status" + , &dnssec_status) + || getdns_dict_get_int( response, "/replies_tree/0/header/ancount" + , &ancount) + || rcode != GETDNS_RCODE_NOERROR + || dnssec_status != GETDNS_DNSSEC_SECURE + || ancount < 2) + stubby_debug("No suitable DANE records found\n"); + + else if ((r = getdns_dict_get_list( response, "/replies_tree/0/answer" + , &answers))) + stubby_error("Could not get answer section: %s\n" + , stubby_getdns_strerror(r)); + + else if ((r = getdns_context_get_upstream_recursive_servers(po->context, + &upstreams))) + stubby_error("Could not get upstreams: %s\n" + , stubby_getdns_strerror(r)); + + else { + size_t i = 0; + getdns_dict *upstream; + + while (!(r = getdns_list_get_dict(upstreams, i, &upstream))) { + getdns_dict_set_list(upstream, "dane_records", answers); + i += 1; + } + if (r != GETDNS_RETURN_NO_SUCH_LIST_ITEM) + stubby_error("Enumerating upstreams error: %s" + , stubby_getdns_strerror(r)); + + else if ((r = getdns_context_set_upstream_recursive_servers( + po->context, upstreams))) + stubby_error("Could not set upstreams: %s" + , stubby_getdns_strerror(r)); + else { + po->ready = 1; + /* resubmit requests for upstream */ + while (po->incoming_requests) { + struct incoming_request *request + = po->incoming_requests; + struct incoming_request *next = request->next; + + incoming_request_handler( request->down_context + , request->callback_type + , request->request + , request->userarg + , request->request_id + ); + free(request); + po->incoming_requests = next; + } + return; + } + }; + /* TODO: + * - Either remove the upstream, or set a timer to retry after X minutes + */ + /* failed, cancel incoming_requests */ + if (!po->incoming_requests) + stubby_debug("No incoming requests to cancel\n"); + + else while (po->incoming_requests) { + struct incoming_request *request = po->incoming_requests; + struct incoming_request *next = request->next; + getdns_dict *response = request->request; + getdns_proxy_policy policy; + uint8_t buf[2048]; + size_t bufsize = sizeof(buf); + getdns_dict *opt_dict; + getdns_bindata bindata; + uint32_t rd; + + /* Turn request into error response + */ + getdns_dict_set_int(response, "/header/ra", + !getdns_dict_get_int(response, "/header/rd", &rd) ? rd : 0); + getdns_dict_set_int(response, "/header/rcode", + GETDNS_RCODE_SERVFAIL); + getdns_dict_set_int(response, "/header/qr", 1); + getdns_dict_set_int(response, "/header/ad", 0); + getdns_dict_set_int(response, "/header/arcount", 0); + getdns_dict_remove_name(response, "/additional"); + + policy.flags1 = 0; + policy.flags2 = 0; + policy.addr_count = po->addr_count; + memcpy(policy.addrs, po->addrs, sizeof(policy.addrs)); + policy.domainname = po->name; + policy.svcparams[0].key = NULL; + policy.interface = po->infname; + /* DANE for DoT for now */ + policy.flags2 |= PROXY_CONTROL_FLAG2_AT; + /* DANE authentication failed, PKIX might still work */ + policy.flags1 |= PROXY_CONTROL_FLAG1_A; + policy.flags1 |= PROXY_CONTROL_FLAG1_UA; + policy.flags1 |= PROXY_CONTROL_FLAG1_DD; + proxy_policy2opt(&policy, policy.addrs[0].ss_family == AF_INET6, + buf, &bufsize); + if (getdns_str2dict( "{ do : 1" + ", extended_rcode : 0" + ", type : GETDNS_RRTYPE_OPT" + ", udp_payload_size: 512" + ", version : 0" + ", z : 0" + ", rdata :" + " { options: [ { option_code: 42 } ] }" + "}" + , &opt_dict) == GETDNS_RETURN_GOOD) { + + bindata.data = buf; + bindata.size = bufsize; + getdns_dict_set_bindata(opt_dict, + "/rdata/options/0/option_data", &bindata); + getdns_dict_set_dict(response, + "/additional/-", opt_dict); + getdns_dict_set_int(response, "/header/arcount", 1); + } + if ((dict_str = getdns_pretty_print_dict(response))) { + stubby_debug("dane_request_cb: canceling incoming " + "request with: %s\n", dict_str); + free(dict_str); + } + if ((r = getdns_reply( request->down_context, response + , request->request_id))) { + stubby_error("Could not reply: %s" + , stubby_getdns_strerror(r)); + /* Cancel reply */ + getdns_reply( request->down_context, NULL + , request->request_id); + } + getdns_dict_destroy(response); + free(request); + po->incoming_requests = next; + } +} + + #define GLDNS_EDNS_PROXY_CONTROL 42 static void decode_proxy_option(struct proxy_opt *opt); @@ -938,24 +1165,6 @@ static void decode_proxy_option(struct proxy_opt *opt) abort(); } -#define PROXY_CONTROL_FLAG1_U (1 << 15) -#define PROXY_CONTROL_FLAG1_UA (1 << 14) -#define PROXY_CONTROL_FLAG1_A (1 << 13) -#define PROXY_CONTROL_FLAG1_P (1 << 12) -#define PROXY_CONTROL_FLAG1_D (1 << 11) -#define PROXY_CONTROL_FLAG1_DD (1 << 10) - -#define PROXY_CONTROL_FLAG2_A53 (1 << 15) -#define PROXY_CONTROL_FLAG2_D53 (1 << 14) -#define PROXY_CONTROL_FLAG2_AT (1 << 13) -#define PROXY_CONTROL_FLAG2_DT (1 << 12) -#define PROXY_CONTROL_FLAG2_AH2 (1 << 11) -#define PROXY_CONTROL_FLAG2_DH2 (1 << 10) -#define PROXY_CONTROL_FLAG2_AH3 (1 << 9) -#define PROXY_CONTROL_FLAG2_DH3 (1 << 8) -#define PROXY_CONTROL_FLAG2_AQ (1 << 7) -#define PROXY_CONTROL_FLAG2_DQ (1 << 6) - static void setup_upstream(getdns_context *down_context, struct upstream *usp) { int i, r; @@ -1157,8 +1366,11 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags abort(); } } - po->context = up_context; + po->ready = 1; + po->dane_request_id = 0; + po->incoming_requests = NULL; + if ((r = getdns_context_set_dns_transport_list(up_context, transports_count, transports)) != GETDNS_RETURN_GOOD) @@ -1231,33 +1443,40 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags abort(); } } + if (A_flag && D_flag) { + /* get DANE records first */ + static getdns_dict *dnssec = NULL; + char qname[1025] = "_853._tcp."; + + qname[1024] = 0; + strncat(qname, po->name, sizeof(qname) + - sizeof("_853._tcp.") - 1); + + fprintf(stderr, "setup_upstream: lookup DANE records " + "for %s before this upstream is ready" + "\n", qname); + + if (!dnssec && (r = getdns_str2dict( + "{dnssec_return_all_statuses:" + "GETDNS_EXTENSION_TRUE}", &dnssec))) + stubby_error("Could make dnssec extension " + "dict: %s", stubby_getdns_strerror(r)); + + if ((r = getdns_general(down_context, qname, + GETDNS_RRTYPE_TLSA, dnssec, po, + &po->dane_request_id, dane_request_cb))) + stubby_error("Could not schedule dane " + "query: %s", stubby_getdns_strerror(r)); + + po->ready = 0; + } } return; - error: fprintf(stderr, "setup_upstream: error, should clear getdns contexts\n"); } -#define POLICY_N_ADDR 3 -#define POLICY_N_SVCPARAMS 8 - -typedef struct getdns_proxy_policy { - uint16_t flags1, flags2; - 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; - -static void proxy_policy2opt(getdns_proxy_policy *policy, int do_ipv6, - uint8_t *buf, size_t *sizep); - static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, size_t bufsize) { @@ -1301,7 +1520,7 @@ static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, { fprintf(stderr, "reponse_add_proxy_option: can't get " - "/call_reporting/0//call_reporting/0/tls_auth_status\n"); + "/call_reporting/0/tls_auth_status\n"); return; } From f6363249b870651af81342a0377bb16f58c4c1b1 Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Wed, 7 Sep 2022 14:10:53 +0200 Subject: [PATCH 11/14] Support for svc parameters --- src/server.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 150 insertions(+), 14 deletions(-) diff --git a/src/server.c b/src/server.c index addd040..a3b41a0 100644 --- a/src/server.c +++ b/src/server.c @@ -377,7 +377,8 @@ static void request_cb( #include #define MAX_PROXY_CONTROL_OPTS 10 -#define PROXY_ADDRS_MAX 10 +#define PROXY_ADDRS_MAX 10 +#define ALPN_MAX 10 struct incoming_request { getdns_context *down_context; @@ -401,8 +402,12 @@ static struct upstream uint16_t flags1; uint16_t flags2; char *name; + int alpn_count; + char *alpn[ALPN_MAX]; + int no_default_alpn; int addr_count; struct sockaddr_storage addrs[PROXY_ADDRS_MAX]; + uint16_t port; char *infname; /* Getdns context */ @@ -828,10 +833,13 @@ static void dane_request_cb( } } +#define GLDNS_EDNS_PROXY_CONTROL 42 +#define SVC_KEY_MANDATORY 0 +#define SVC_KEY_ALPN 1 +#define SVC_KEY_NDA 2 +#define SVC_KEY_PORT 3 -#define GLDNS_EDNS_PROXY_CONTROL 42 - -static void decode_proxy_option(struct proxy_opt *opt); +static int decode_proxy_option(struct proxy_opt *opt); static void setup_upstream(getdns_context *down_context, struct upstream *usp); static struct upstream *get_upstreams_for_policy(getdns_context *down_context, @@ -951,7 +959,11 @@ static struct upstream *get_upstreams_for_policy(getdns_context *down_context, usp->opts[u].bindata.data= data; usp->opts[u].bindata.size= size; - decode_proxy_option(&usp->opts[u]); + if (!decode_proxy_option(&usp->opts[u])) + { + usp->dns_error = BADPROXYPOLICY; + break; + } setup_upstream(down_context, usp); } upstreams_count++; @@ -959,15 +971,16 @@ static struct upstream *get_upstreams_for_policy(getdns_context *down_context, return usp; } -static void decode_proxy_option(struct proxy_opt *opt) +static int decode_proxy_option(struct proxy_opt *opt) { uint8_t addr_type, addr_length, name_length, svc_length, inf_length; - uint16_t u16; + uint16_t u16, svc_key, svc_val_len; int i; - size_t o, llen, no, size; + size_t o, oa, ol, llen, no, size; uint8_t *p; struct sockaddr_in *sin4p; struct sockaddr_in6 *sin6p; + char *alpn; size= opt->bindata.size; p= opt->bindata.data; @@ -1125,9 +1138,104 @@ static void decode_proxy_option(struct proxy_opt *opt) p += 1; size -= 1; + opt->alpn_count = 0; + opt->no_default_alpn = 0; + opt->port = 0; if (svc_length) { - fprintf(stderr, "decode_proxy_option: should handle svc\n"); + o = 0; + while (o < svc_length) + { +fprintf(stderr, "decode_proxy_option: o %lu, svc_length %d\n", o, svc_length); + if (o+2 > svc_length) + goto error; + memcpy(&u16, p+o, 2); + o += 2; + svc_key = ntohs(u16); +fprintf(stderr, "decode_proxy_option: key %d\n", svc_key); + if (o+2 > svc_length) + goto error; + memcpy(&u16, p+o, 2); + o += 2; + svc_val_len = ntohs(u16); +fprintf(stderr, "decode_proxy_option: len %d\n", svc_val_len); + fprintf(stderr, + "decode_proxy_option: key %d, length %d\n", + svc_key, svc_val_len); + if (o+svc_val_len > svc_length) + goto error; + + switch(svc_key) + { + case SVC_KEY_MANDATORY: + oa = 0; + while (oa < svc_val_len) + { + if (oa + 2 > svc_val_len) + goto error; + memcpy(&u16, p+o+oa, sizeof(u16)); + u16 = ntohs(u16); + switch(u16) + { + case SVC_KEY_ALPN: + case SVC_KEY_NDA: + case SVC_KEY_PORT: + /* Keys that we know */ + break; + default: + fprintf(stderr, + "decode_proxy_option: unknown mandatory key %u\n", + u16); + goto error; + } + oa += 2; + } + break; + + case SVC_KEY_ALPN: + oa = 0; + while (oa < svc_val_len) + { +fprintf(stderr, "decode_proxy_option: oa %lu len %d\n", oa, svc_length); +fprintf(stderr, "decode_proxy_option: alpn_count %d\n", opt->alpn_count); + if (opt->alpn_count >= ALPN_MAX) + goto error; + if (oa + 1 > svc_val_len) + goto error; + ol = p[o+oa]; +fprintf(stderr, "decode_proxy_option: ol %lu\n", ol); + oa++; + if (oa + ol > svc_val_len) + goto error; + alpn = malloc(ol+1); + memcpy(alpn, p+o+oa, ol); + oa += ol; + alpn[ol] = '\0'; + opt->alpn[opt->alpn_count++] = alpn; + } + break; + + case SVC_KEY_NDA: + if (svc_val_len != 0) + goto error; + opt->no_default_alpn = 1; + break; + + case SVC_KEY_PORT: + if (svc_val_len != 2) + goto error; + memcpy(&u16, p+o, 2); + opt->port = ntohs(u16); + break; + + default: + fprintf(stderr, + "decode_proxy_option: unknown SVC parameter key %d\n", + svc_key); + break; + } + o += svc_val_len; + } p += svc_length; size -= svc_length; @@ -1158,16 +1266,15 @@ static void decode_proxy_option(struct proxy_opt *opt) "decode_proxy_option: garbage at end of option\n"); goto error; } - return; + return 1; error: - fprintf(stderr, "decode_proxy_option: should handle error\n"); - abort(); + return 0; } static void setup_upstream(getdns_context *down_context, struct upstream *usp) { - int i, r; + int i, j, r; unsigned u, U_flag, UA_flag, A_flag, P_flag, D_flag, DD_flag; unsigned A53_flag, D53_flag, AT_flag, DT_flag, AH2_flag, DH2_flag, AH3_flag, DH3_flag, AQ_flag, DQ_flag; @@ -1175,7 +1282,7 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) size_t transports_count; struct proxy_opt *po; getdns_context *up_context; - getdns_list *list; + getdns_list *list, *alpn_list; getdns_dict *dict; struct sockaddr_in *sin4p; struct sockaddr_in6 *sin6p; @@ -1217,6 +1324,7 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) { fprintf(stderr, "setup_upstream: Ax and Dx\n"); usp->dns_error= BADPROXYPOLICY; +fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); goto error; } @@ -1251,6 +1359,7 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) fprintf(stderr, "setup_upstream: too many of U, UA, A\n"); usp->dns_error= BADPROXYPOLICY; +fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); goto error; } if (U_flag + UA_flag + A_flag == 0) @@ -1309,6 +1418,7 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) fprintf(stderr, "setup_upstream: no matching transports\n"); usp->dns_error= BADPROXYPOLICY; +fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); goto error; } @@ -1320,6 +1430,7 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags { fprintf(stderr, "setup_upstream: P or D but not A\n"); usp->dns_error= BADPROXYPOLICY; +fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); goto error; } @@ -1330,6 +1441,7 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags */ fprintf(stderr, "setup_upstream: interface name not supported yet\n"); usp->dns_error= BADPROXYPOLICY; +fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); goto error; } @@ -1429,6 +1541,30 @@ fprintf(stderr, "setup_upstream: flags1 0x%x, P_flag %d, D_flag %d\n", po->flags getdns_dict_set_bindata(dict, "tls_auth_name", &bindata); } + if (po->alpn_count) + { + alpn_list = getdns_list_create(); + for (j = 0; jalpn_count; j++) + { + bindata.data = + (unsigned char *) + po->alpn[j]; + bindata.size = + strlen(po->alpn[j]); + getdns_list_set_bindata( + alpn_list, j, + &bindata); + } + getdns_dict_set_list(dict, "alpn", + alpn_list); + } + if (po->port) + { + getdns_dict_set_int(dict, "port", + po->port); + getdns_dict_set_int(dict, "tls_port", + po->port); + } getdns_list_set_dict(list, i, dict); } fprintf(stderr, "setup_upstream: addr list %s\n", From 25f15ef7338cbe380bbc423516f3040f5f41e36c Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Wed, 14 Sep 2022 14:10:18 +0200 Subject: [PATCH 12/14] Initial part of 'happy eyeballs' for DNS upstreams --- src/server.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 219 insertions(+), 15 deletions(-) diff --git a/src/server.c b/src/server.c index a3b41a0..b559ee5 100644 --- a/src/server.c +++ b/src/server.c @@ -222,6 +222,8 @@ static void request_cb( char i_as_jptr[80]; static uint8_t optbuf[2048]; + fprintf(stderr, "request_cb: got context %p\n", + (void *)context); fprintf(stderr, "request_cb: got response %s\n", getdns_pretty_print_dict(response)); @@ -389,6 +391,14 @@ struct incoming_request { struct incoming_request *next; }; +/* Values for target and status */ +#define TS_NOT_STARTED 1 +#define TS_FAILED 2 +#define TS_UNENCRYPTED 3 +#define TS_UNAUTHENTICATED 4 +#define TS_OPPORTUNISTIC 5 +#define TS_AUTHENTICATED 6 + static struct upstream { unsigned dns_error; @@ -415,7 +425,14 @@ static struct upstream int ready; getdns_transaction_t dane_request_id; struct incoming_request *incoming_requests; + + /* Current status */ + unsigned target; /* Target authentication status of + * this getdns context. + */ + unsigned status; /* Last known result */ } opts[MAX_PROXY_CONTROL_OPTS]; + unsigned sorted[MAX_PROXY_CONTROL_OPTS]; } *upstreams; static int upstreams_count; @@ -462,6 +479,8 @@ typedef struct getdns_proxy_policy { static void proxy_policy2opt(getdns_proxy_policy *policy, int do_ipv6, uint8_t *buf, size_t *sizep); +static int select_upstream(struct upstream *usp); + static void incoming_request_handler(getdns_context *down_context, getdns_callback_type_t callback_type, getdns_dict *request, void *userarg, getdns_transaction_t request_id) @@ -483,13 +502,14 @@ static void incoming_request_handler(getdns_context *down_context, getdns_dict *rr; uint32_t rr_type; unsigned dns_error = GETDNS_RCODE_SERVFAIL; + int up_index; struct upstream *usp; getdns_context *up_context; (void)callback_type; (void)userarg; - printf("incoming_request_handler: got request %s\n", + fprintf(stderr, "incoming_request_handler: got request %s\n", getdns_pretty_print_dict(request)); if (!(qext = getdns_dict_create_with_context(down_context)) || @@ -520,6 +540,10 @@ static void incoming_request_handler(getdns_context *down_context, msg->has_edns0 = 1; (void) getdns_dict_get_int(rr, "do", &msg->do_bit); + fprintf(stderr, +"incoming_request_handler: options dict %s\n", getdns_pretty_print_dict(rr)); + fprintf(stderr, +"incoming_request_handler: calling get_upstreams_for_policy\n"); usp= get_upstreams_for_policy(down_context, rr); break; } @@ -541,8 +565,9 @@ static void incoming_request_handler(getdns_context *down_context, msg->upstream = usp; /* Only try first context */ - up_context = usp->opts[0].context; - if (!usp->opts[0].ready) { + up_index = select_upstream(usp); + fprintf(stderr, "incoming_request_handler: up_index %d\n", up_index); + if (!usp->opts[up_index].ready) { struct incoming_request *ir; fprintf(stderr, "queueing incoming request waiting " @@ -556,10 +581,12 @@ static void incoming_request_handler(getdns_context *down_context, ir->request = request; ir->userarg = userarg; ir->request_id = request_id; - ir->next = usp->opts[0].incoming_requests; - usp->opts[0].incoming_requests = ir; + ir->next = usp->opts[up_index]. + incoming_requests; + usp->opts[up_index].incoming_requests = ir; return; } + up_context = usp->opts[up_index].context; if ((r = getdns_dict_set_int(qext, "return_call_reporting", GETDNS_EXTENSION_TRUE))) @@ -610,6 +637,9 @@ static void incoming_request_handler(getdns_context *down_context, (void)getdns_dict_set_list(qext, "/add_opt_parameters/options", list); + fprintf(stderr, "incoming_request_handler: using upstream context %p\n", + (void *)up_context); + if ((r = getdns_dict_get_bindata(request,"/question/qname",&qname))) stubby_error("Could not get qname from query: %s", stubby_getdns_strerror(r)); @@ -959,13 +989,17 @@ static struct upstream *get_upstreams_for_policy(getdns_context *down_context, usp->opts[u].bindata.data= data; usp->opts[u].bindata.size= size; + fprintf(stderr, + "get_upstreams_for_policy: calling decode_proxy_option\n"); if (!decode_proxy_option(&usp->opts[u])) { usp->dns_error = BADPROXYPOLICY; break; } - setup_upstream(down_context, usp); } + fprintf(stderr, "get_upstreams_for_policy: calling setup_upstream\n"); + setup_upstream(down_context, usp); + upstreams_count++; return usp; @@ -1146,22 +1180,16 @@ static int decode_proxy_option(struct proxy_opt *opt) o = 0; while (o < svc_length) { -fprintf(stderr, "decode_proxy_option: o %lu, svc_length %d\n", o, svc_length); if (o+2 > svc_length) goto error; memcpy(&u16, p+o, 2); o += 2; svc_key = ntohs(u16); -fprintf(stderr, "decode_proxy_option: key %d\n", svc_key); if (o+2 > svc_length) goto error; memcpy(&u16, p+o, 2); o += 2; svc_val_len = ntohs(u16); -fprintf(stderr, "decode_proxy_option: len %d\n", svc_val_len); - fprintf(stderr, - "decode_proxy_option: key %d, length %d\n", - svc_key, svc_val_len); if (o+svc_val_len > svc_length) goto error; @@ -1196,14 +1224,11 @@ fprintf(stderr, "decode_proxy_option: len %d\n", svc_val_len); oa = 0; while (oa < svc_val_len) { -fprintf(stderr, "decode_proxy_option: oa %lu len %d\n", oa, svc_length); -fprintf(stderr, "decode_proxy_option: alpn_count %d\n", opt->alpn_count); if (opt->alpn_count >= ALPN_MAX) goto error; if (oa + 1 > svc_val_len) goto error; ol = p[o+oa]; -fprintf(stderr, "decode_proxy_option: ol %lu\n", ol); oa++; if (oa + ol > svc_val_len) goto error; @@ -1272,6 +1297,32 @@ fprintf(stderr, "decode_proxy_option: ol %lu\n", ol); return 0; } +#define PROXY_CONTROL_FLAG1_U (1 << 15) +#define PROXY_CONTROL_FLAG1_UA (1 << 14) +#define PROXY_CONTROL_FLAG1_A (1 << 13) +#define PROXY_CONTROL_FLAG1_P (1 << 12) +#define PROXY_CONTROL_FLAG1_D (1 << 11) +#define PROXY_CONTROL_FLAG1_DD (1 << 10) + +#define PROXY_CONTROL_FLAG2_A53 (1 << 15) +#define PROXY_CONTROL_FLAG2_D53 (1 << 14) +#define PROXY_CONTROL_FLAG2_AT (1 << 13) +#define PROXY_CONTROL_FLAG2_DT (1 << 12) +#define PROXY_CONTROL_FLAG2_AH2 (1 << 11) +#define PROXY_CONTROL_FLAG2_DH2 (1 << 10) +#define PROXY_CONTROL_FLAG2_AH3 (1 << 9) +#define PROXY_CONTROL_FLAG2_DH3 (1 << 8) +#define PROXY_CONTROL_FLAG2_AQ (1 << 7) +#define PROXY_CONTROL_FLAG2_DQ (1 << 6) + +static int opt_cmp(const void *v1, const void *v2); + +struct sort_help +{ + struct upstream *usp; + unsigned index; +}; + static void setup_upstream(getdns_context *down_context, struct upstream *usp) { int i, j, r; @@ -1289,6 +1340,7 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) getdns_eventloop *eventloop; getdns_transport_list_t transports[3]; getdns_bindata bindata; + struct sort_help sort_help[MAX_PROXY_CONTROL_OPTS]; r = getdns_context_get_eventloop(down_context, &eventloop); if (r != GETDNS_RETURN_GOOD) @@ -1297,9 +1349,14 @@ static void setup_upstream(getdns_context *down_context, struct upstream *usp) abort(); } + fprintf(stderr, "setup_upstream: starting, opts_count %d\n", + usp->opts_count); + usp->dns_error= 0; for (u= 0, po= usp->opts; uopts_count; u++, po++) { + fprintf(stderr, "setup_upstream: opt %d\n", u); + /* First set flags for individual protocols, we * need them later */ @@ -1350,6 +1407,9 @@ fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); do_DoH2 = DH2_flag ? 0 : 1; } + po->target = 0; + po->status = TS_NOT_STARTED; + /* Check U, UA, and A flags */ U_flag= !!(po->flags1 & PROXY_CONTROL_FLAG1_U); UA_flag= !!(po->flags1 & PROXY_CONTROL_FLAG1_UA); @@ -1377,6 +1437,8 @@ fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); transports[i++] = GETDNS_TRANSPORT_TCP; } transports_count = i; + + po->target = TS_OPPORTUNISTIC; } else if (U_flag) { @@ -1390,6 +1452,8 @@ fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); transports[i++] = GETDNS_TRANSPORT_TCP; } transports_count = i; + + po->target = TS_UNENCRYPTED; } else if (UA_flag) { @@ -1399,6 +1463,8 @@ fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); if (do_DoT || do_DoH2) transports[i++] = GETDNS_TRANSPORT_TLS; transports_count = i; + + po->target = TS_UNAUTHENTICATED; } else if (A_flag) { @@ -1406,6 +1472,8 @@ fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); if (do_DoT || do_DoH2) transports[i++] = GETDNS_TRANSPORT_TLS; transports_count = i; + + po->target = TS_AUTHENTICATED; } else { @@ -1452,6 +1520,8 @@ fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); "setup_upstream: getdns_context_create failed\n"); abort(); } + fprintf(stderr, "setup_upstream: context %p\n", + (void *)up_context); r = getdns_context_set_eventloop(up_context, eventloop); if (r != GETDNS_RETURN_GOOD) { @@ -1492,6 +1562,8 @@ fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); abort(); } + fprintf(stderr, "setup_upstream: addr_count %d\n", + po->addr_count); if (po->addr_count) { list = getdns_list_create(); @@ -1606,13 +1678,145 @@ fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); po->ready = 0; } + + sort_help[u].usp = usp; + sort_help[u].index = u; + } + + /* Create sorted */ + qsort(sort_help, usp->opts_count, sizeof(sort_help[0]), opt_cmp); + + for (u= 0; uopts_count; u++) + { + usp->sorted[u] = sort_help[u].index; + + fprintf(stderr, "setup_upstream: sorted %d -> %d\n", + u, usp->sorted[u]); } + return; error: fprintf(stderr, "setup_upstream: error, should clear getdns contexts\n"); } +static int opt_cmp(const void *v1, const void *v2) +{ + int ip1, ip2; + const struct sort_help *e1, *e2; + const struct proxy_opt *p1, *p2; + + e1= v1; + e2= v2; + + fprintf(stderr, "opt_cmp: e1 index %d, e2 index %d\n", + e1->index, e2->index); + + p1= &e1->usp->opts[e1->index]; + p2= &e2->usp->opts[e2->index]; + + if (p1->target > p2->target) + return -1; + if (p1->target < p2->target) + return 1; + + /* Authentication options are the same. Prefer IPv6. Assume that each + * option has one type of addresses. + */ + ip1 = 0; /* No addresses */ + ip2 = 0; + if (p1->addr_count != 0) + { + if (p1->addrs[0].ss_family == AF_INET6) + ip1 = -1; + else + ip1 = 1; + } + if (p2->addr_count != 0) + { + if (p2->addrs[0].ss_family == AF_INET6) + ip2 = -1; + else + ip2 = 1; + } + + if (ip1 > ip2) + return -1; + if (ip2 > ip1) + return 1; + return 0; +} + +static int select_upstream(struct upstream *usp) +{ + int Xbest_completed, Xbest_potential; + unsigned u, best_completed_status, best_potential_target; + int s; + struct proxy_opt *op; + + /* Select an uptream to try next */ + Xbest_completed = -1; + best_completed_status = 0; + Xbest_potential = -1; + best_potential_target = 0; + + for (u= 0; uopts_count; u++) + { + s = usp->sorted[u]; + op = &usp->opts[s]; + + fprintf(stderr, "select_upstream: u %d, s %d\n", u, s); + + if (op->status != TS_NOT_STARTED) + { + if (Xbest_completed == -1) + { + Xbest_completed = s; + best_completed_status = op->status; + } + else if (op->status > best_completed_status) + { + Xbest_completed = s; + best_completed_status = op->status; + } + } + else + { + if (Xbest_potential == -1) + { + Xbest_potential = s; + best_potential_target = op->target; + } + else if (op->target > best_potential_target) + { + Xbest_potential = s; + best_potential_target = op->target; + } + } + } + + if (Xbest_completed == -1 && Xbest_potential == -1) + { + fprintf(stderr, "select_upstream: no upstream?\n"); + abort(); + } + if (Xbest_completed == -1) + return Xbest_potential; + if (Xbest_potential == -1) + return Xbest_completed; + + if (best_potential_target > best_completed_status) + return Xbest_potential; + else + return Xbest_completed; +} + +#define POLICY_N_ADDR 3 +#define POLICY_N_SVCPARAMS 8 + +static void proxy_policy2opt(getdns_proxy_policy *policy, int do_ipv6, + uint8_t *buf, size_t *sizep); + static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, size_t bufsize) { From 2eab450c458e87f07488a011b0e9c64a4fa9b82c Mon Sep 17 00:00:00 2001 From: Philip Homburg Date: Thu, 29 Sep 2022 12:19:12 +0200 Subject: [PATCH 13/14] Interate over all upstreams --- src/server.c | 319 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 243 insertions(+), 76 deletions(-) diff --git a/src/server.c b/src/server.c index b559ee5..3c52ebd 100644 --- a/src/server.c +++ b/src/server.c @@ -85,7 +85,13 @@ typedef struct dns_msg { uint32_t do_bit; uint32_t cd_bit; int has_edns0; - struct upstream *upstream; + char *qname_str; + uint32_t qtype; + getdns_dict *qext; + struct upstream *upstream; + int upstream_index; + unsigned best_status; + getdns_dict *best_result; } dns_msg; #if defined(SERVER_DEBUG) && SERVER_DEBUG @@ -205,8 +211,61 @@ static getdns_return_t _handle_edns0( return GETDNS_RETURN_GOOD; } +#define MAX_PROXY_CONTROL_OPTS 10 +#define PROXY_ADDRS_MAX 10 +#define ALPN_MAX 10 + +/* Values for target and status */ +#define TS_NOT_STARTED 1 +#define TS_FAILED 2 +#define TS_UNENCRYPTED 3 +#define TS_UNAUTHENTICATED 4 +#define TS_OPPORTUNISTIC 5 +#define TS_AUTHENTICATED 6 + +#include + +static struct upstream +{ + unsigned dns_error; + unsigned opts_count; + struct proxy_opt + { + /* Original bindata of option */ + getdns_bindata bindata; + + /* Decoded option */ + uint16_t flags1; + uint16_t flags2; + char *name; + int alpn_count; + char *alpn[ALPN_MAX]; + int no_default_alpn; + int addr_count; + struct sockaddr_storage addrs[PROXY_ADDRS_MAX]; + uint16_t port; + char *infname; + + /* Getdns context */ + getdns_context *context; + int ready; + getdns_transaction_t dane_request_id; + struct incoming_request *incoming_requests; + + /* Current status */ + unsigned target; /* Target authentication status of + * this getdns context. + */ + unsigned status; /* Last known result */ + } opts[MAX_PROXY_CONTROL_OPTS]; + unsigned sorted[MAX_PROXY_CONTROL_OPTS]; +} *upstreams; +static int upstreams_count; + +static int response2status(getdns_dict *response); static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, size_t bufsize); +static int select_upstream(struct upstream *usp, unsigned *new_statusp); static void request_cb( getdns_context *context, getdns_callback_type_t callback_type, @@ -219,6 +278,10 @@ static void request_cb( getdns_list *options; size_t n_options; uint32_t arcount; + int up_index, new_index; + unsigned new_status, status; + struct proxy_opt *op; + getdns_context *up_context; char i_as_jptr[80]; static uint8_t optbuf[2048]; @@ -246,6 +309,9 @@ static void request_cb( #endif assert(msg); + if (msg->upstream && callback_type == GETDNS_CALLBACK_ERROR) + goto update_upstream; + if (callback_type != GETDNS_CALLBACK_COMPLETE) SERVFAIL("Callback type not complete", (int)callback_type, msg, &response); @@ -358,8 +424,49 @@ static void request_cb( } } +update_upstream: if (msg->upstream) { + up_index = msg->upstream_index; + op = &msg->upstream->opts[up_index]; + + status = response2status(response); + fprintf(stderr, + "request_cb: upstream %p, got status %d for index %d\n", + (void *)msg->upstream, status, up_index); + + op->status = status; + if (status > msg->best_status) + { + if (msg->best_result) + getdns_dict_destroy(msg->best_result); + msg->best_status = status; + msg->best_result = response; + response = NULL; + } + new_index = select_upstream(msg->upstream, &new_status); + fprintf(stderr, + "request_cb: new_index %d, new_status %d\n", + new_index, new_status); + if (new_status > msg->best_status) + { + msg->upstream_index = new_index; + up_context = msg->upstream->opts[new_index].context; + + fprintf(stderr, "request_cb: before getdns_general\n"); + if ((r = getdns_general(up_context, msg->qname_str, + msg->qtype, msg->qext, msg, + &transaction_id, request_cb))) + stubby_error("Could not schedule query: %s", + stubby_getdns_strerror(r)); + fprintf(stderr, "request_cb: r = %d\n", r); + return; + } + + /* New upstream is not better. Reply current best */ + response = msg->best_result; + msg->best_result = NULL; + response_add_proxy_option(response, optbuf, sizeof(optbuf)); } @@ -376,12 +483,6 @@ static void request_cb( getdns_dict_destroy(response); } -#include - -#define MAX_PROXY_CONTROL_OPTS 10 -#define PROXY_ADDRS_MAX 10 -#define ALPN_MAX 10 - struct incoming_request { getdns_context *down_context; getdns_callback_type_t callback_type; @@ -391,51 +492,6 @@ struct incoming_request { struct incoming_request *next; }; -/* Values for target and status */ -#define TS_NOT_STARTED 1 -#define TS_FAILED 2 -#define TS_UNENCRYPTED 3 -#define TS_UNAUTHENTICATED 4 -#define TS_OPPORTUNISTIC 5 -#define TS_AUTHENTICATED 6 - -static struct upstream -{ - unsigned dns_error; - unsigned opts_count; - struct proxy_opt - { - /* Original bindata of option */ - getdns_bindata bindata; - - /* Decoded option */ - uint16_t flags1; - uint16_t flags2; - char *name; - int alpn_count; - char *alpn[ALPN_MAX]; - int no_default_alpn; - int addr_count; - struct sockaddr_storage addrs[PROXY_ADDRS_MAX]; - uint16_t port; - char *infname; - - /* Getdns context */ - getdns_context *context; - int ready; - getdns_transaction_t dane_request_id; - struct incoming_request *incoming_requests; - - /* Current status */ - unsigned target; /* Target authentication status of - * this getdns context. - */ - unsigned status; /* Last known result */ - } opts[MAX_PROXY_CONTROL_OPTS]; - unsigned sorted[MAX_PROXY_CONTROL_OPTS]; -} *upstreams; -static int upstreams_count; - static struct upstream *get_upstreams_for_policy(getdns_context *down_context, getdns_dict *opt_rr); @@ -479,8 +535,6 @@ typedef struct getdns_proxy_policy { static void proxy_policy2opt(getdns_proxy_policy *policy, int do_ipv6, uint8_t *buf, size_t *sizep); -static int select_upstream(struct upstream *usp); - static void incoming_request_handler(getdns_context *down_context, getdns_callback_type_t callback_type, getdns_dict *request, void *userarg, getdns_transaction_t request_id) @@ -503,6 +557,7 @@ static void incoming_request_handler(getdns_context *down_context, uint32_t rr_type; unsigned dns_error = GETDNS_RCODE_SERVFAIL; int up_index; + unsigned status; struct upstream *usp; getdns_context *up_context; @@ -526,6 +581,8 @@ static void incoming_request_handler(getdns_context *down_context, msg->upstream = NULL; (void) getdns_dict_get_int(request, "/header/ad", &msg->ad_bit); (void) getdns_dict_get_int(request, "/header/cd", &msg->cd_bit); + msg->qname_str = NULL; + msg->qext = NULL; usp= NULL; if (!getdns_dict_get_list(request, "additional", &additional)) { if (getdns_list_get_length(additional, &len)) @@ -563,10 +620,12 @@ static void incoming_request_handler(getdns_context *down_context, if (usp) { msg->upstream = usp; + msg->best_status = TS_NOT_STARTED; + msg->best_result = NULL; - /* Only try first context */ - up_index = select_upstream(usp); - fprintf(stderr, "incoming_request_handler: up_index %d\n", up_index); + up_index = select_upstream(usp, &status); + fprintf(stderr, "incoming_request_handler: up_index %d\n", + up_index); if (!usp->opts[up_index].ready) { struct incoming_request *ir; @@ -586,6 +645,13 @@ static void incoming_request_handler(getdns_context *down_context, usp->opts[up_index].incoming_requests = ir; return; } + if (status == TS_FAILED) + { + fprintf(stderr, + "incoming_request_handler: need to handle failed upstream\n"); + abort(); + } + msg->upstream_index = up_index; up_context = usp->opts[up_index].context; if ((r = getdns_dict_set_int(qext, "return_call_reporting", @@ -660,8 +726,16 @@ static void incoming_request_handler(getdns_context *down_context, stubby_error("Could set class from query: %s", stubby_getdns_strerror(r)); - else if ((r = getdns_general(up_context, qname_str, qtype, - qext, msg, &transaction_id, request_cb))) + if (r) goto error; + msg->qname_str = qname_str; + qname_str = NULL; + msg->qtype = qtype; + msg->qext = qext; + qext = NULL; + + fprintf(stderr, "incoming_request_handler: before getdns_general\n"); + if ((r = getdns_general(up_context, msg->qname_str, qtype, + msg->qext, msg, &transaction_id, request_cb))) stubby_error("Could not schedule query: %s", stubby_getdns_strerror(r)); else { @@ -673,6 +747,14 @@ static void incoming_request_handler(getdns_context *down_context, free(qname_str); return; } + if (msg->upstream && r == GETDNS_RETURN_NO_UPSTREAM_AVAILABLE) + { + /* We need a fake call to request_cb */ + request_cb(up_context, GETDNS_CALLBACK_ERROR, NULL, msg, + transaction_id); + return; + } + fprintf(stderr, "incoming_request_handler: r = %d\n", r); error: if (qname_str) free(qname_str); @@ -1670,6 +1752,8 @@ fprintf(stderr, "%s, %d\n", __FILE__, __LINE__); stubby_error("Could make dnssec extension " "dict: %s", stubby_getdns_strerror(r)); + fprintf(stderr, + "setup_upstream: before getdns_general\n"); if ((r = getdns_general(down_context, qname, GETDNS_RRTYPE_TLSA, dnssec, po, &po->dane_request_id, dane_request_cb))) @@ -1747,17 +1831,17 @@ static int opt_cmp(const void *v1, const void *v2) return 0; } -static int select_upstream(struct upstream *usp) +static int select_upstream(struct upstream *usp, unsigned *new_statusp) { - int Xbest_completed, Xbest_potential; + int best_completed, best_potential; unsigned u, best_completed_status, best_potential_target; int s; struct proxy_opt *op; /* Select an uptream to try next */ - Xbest_completed = -1; + best_completed = -1; best_completed_status = 0; - Xbest_potential = -1; + best_potential = -1; best_potential_target = 0; for (u= 0; uopts_count; u++) @@ -1769,46 +1853,58 @@ static int select_upstream(struct upstream *usp) if (op->status != TS_NOT_STARTED) { - if (Xbest_completed == -1) + if (best_completed == -1) { - Xbest_completed = s; + best_completed = s; best_completed_status = op->status; } else if (op->status > best_completed_status) { - Xbest_completed = s; + best_completed = s; best_completed_status = op->status; } } else { - if (Xbest_potential == -1) + if (best_potential == -1) { - Xbest_potential = s; + best_potential = s; best_potential_target = op->target; } else if (op->target > best_potential_target) { - Xbest_potential = s; + best_potential = s; best_potential_target = op->target; } } } - if (Xbest_completed == -1 && Xbest_potential == -1) + if (best_completed == -1 && best_potential == -1) { fprintf(stderr, "select_upstream: no upstream?\n"); abort(); } - if (Xbest_completed == -1) - return Xbest_potential; - if (Xbest_potential == -1) - return Xbest_completed; + if (best_completed == -1) + { + *new_statusp = best_potential_target; + return best_potential; + } + if (best_potential == -1) + { + *new_statusp = best_completed_status; + return best_completed; + } if (best_potential_target > best_completed_status) - return Xbest_potential; + { + *new_statusp = best_potential_target; + return best_potential; + } else - return Xbest_completed; + { + *new_statusp = best_completed_status; + return best_completed; + } } #define POLICY_N_ADDR 3 @@ -1817,6 +1913,77 @@ static int select_upstream(struct upstream *usp) static void proxy_policy2opt(getdns_proxy_policy *policy, int do_ipv6, uint8_t *buf, size_t *sizep); +static int response2status(getdns_dict *response) +{ + uint32_t transport; + getdns_bindata *bindatap; + + fprintf(stderr, "response2status: response %s\n", + getdns_pretty_print_dict(response)); + + if (!response) + return TS_FAILED; + + if (getdns_dict_get_int(response, "/call_reporting/0/transport", + &transport) != GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "reponse_add_proxy_option: can't get /call_reporting/0/transport\n"); + abort(); + } + switch(transport) + { + case GETDNS_TRANSPORT_UDP: + case GETDNS_TRANSPORT_TCP: + return TS_UNENCRYPTED; + + case GETDNS_TRANSPORT_TLS: + if (getdns_dict_get_bindata(response, + "/call_reporting/0/tls_auth_status", + &bindatap) != GETDNS_RETURN_GOOD) + { + fprintf(stderr, + "reponse_add_proxy_option: can't get " + "/call_reporting/0/tls_auth_status\n"); + abort(); + } + + if (bindatap->size == 4 && + memcmp(bindatap->data, "None", 4) == 0) + { + /* No authentication */ + return TS_UNAUTHENTICATED; + } + else if (bindatap->size == 6 && + memcmp(bindatap->data, "Failed", 6) == 0) + { + /* Authentication failed */ + return TS_FAILED; + } + else if (bindatap->size == 7 && + memcmp(bindatap->data, "Success", 7) == 0) + { + /* Authentication succeeded */ + return TS_AUTHENTICATED; + } + else + { + + fprintf(stderr, + "reponse_add_proxy_option: unknown tls_auth_status '%.*s'\n", + (int)bindatap->size, bindatap->data); + abort(); + } + break; + + default: + fprintf(stderr, + "reponse_add_proxy_option: unknown transport %d\n", + transport); + abort(); + } +} + static void response_add_proxy_option(getdns_dict *response, uint8_t *buf, size_t bufsize) { From 959a40faaac073e8088ade1de8fe382a5a51e14b Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Mon, 3 Oct 2022 13:20:09 +0200 Subject: [PATCH 14/14] Pass along DANE records with upstreams Also anticipates PKIX bit --- src/server.c | 40 ++++++++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/server.c b/src/server.c index b559ee5..358601a 100644 --- a/src/server.c +++ b/src/server.c @@ -719,6 +719,9 @@ static void dane_request_cb( getdns_list *upstreams; getdns_list *answers; + /* unused parameters */ + (void)context; (void)callback_type; (void)transaction_id; + if ((dict_str = getdns_pretty_print_dict(response))) { stubby_debug("dane_request_cb: got reponse %s\n", dict_str); free(dict_str); @@ -746,13 +749,31 @@ static void dane_request_cb( , stubby_getdns_strerror(r)); else { - size_t i = 0; - getdns_dict *upstream; - - while (!(r = getdns_list_get_dict(upstreams, i, &upstream))) { - getdns_dict_set_list(upstream, "dane_records", answers); - i += 1; + size_t i; + getdns_dict *answer, *upstream; + + if (!(po->flags1 & PROXY_CONTROL_FLAG1_P)) { + for (i = 0 + ; !(r = getdns_list_get_dict(answers, i, &answer)) + ; i += 1) { + uint32_t usage; + + if (!getdns_dict_get_int(answer, + "/rdata/certificate_usage", &usage)) + getdns_dict_set_int(answer, + "/rdata/certificate_usage", + (usage & 1) + ); + } + if (r != GETDNS_RETURN_NO_SUCH_LIST_ITEM) + stubby_error("Enumerating answers error: %s" + , stubby_getdns_strerror(r)); } + for ( i = 0 + ; !(r = getdns_list_get_dict(upstreams, i, &upstream)) + ; i += 1) + getdns_dict_set_list(upstream, "dane_records", answers); + if (r != GETDNS_RETURN_NO_SUCH_LIST_ITEM) stubby_error("Enumerating upstreams error: %s" , stubby_getdns_strerror(r)); @@ -762,6 +783,13 @@ static void dane_request_cb( stubby_error("Could not set upstreams: %s" , stubby_getdns_strerror(r)); else { + char *upstreams_str = getdns_pretty_print_list( + upstreams); + if (upstreams_str) { + stubby_debug("DANE upstreams: %s\n", + upstreams_str); + free(upstreams_str); + } po->ready = 1; /* resubmit requests for upstream */ while (po->incoming_requests) {