diff --git a/src/context.c b/src/context.c index 79dde85b..549c604b 100644 --- a/src/context.c +++ b/src/context.c @@ -881,6 +881,7 @@ getdns_context_create_with_extended_memory_functions( result->edns_extended_rcode = 0; result->edns_version = 0; result->edns_do_bit = 0; + result->edns_client_subnet_private = 0; result-> tls_ctx = NULL; result->extension = &result->mini_event.loop; @@ -1897,6 +1898,26 @@ getdns_context_set_edns_do_bit(struct getdns_context *context, uint8_t value) return GETDNS_RETURN_GOOD; } /* getdns_context_set_edns_do_bit */ +/* + * getdns_context_set_edns_client_subnet_private + * + */ +getdns_return_t +getdns_context_set_edns_client_subnet_private(struct getdns_context *context, uint8_t value) +{ + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + /* only allow 1 */ + if (value != 0 && value != 1) { + return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; + } + + context->edns_client_subnet_private = value; + + dispatch_updated(context, GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE); + + return GETDNS_RETURN_GOOD; +} /* getdns_context_set_edns_client_subnet_private */ + /* * getdns_context_set_extended_memory_functions * @@ -2966,4 +2987,12 @@ getdns_context_get_edns_do_bit(getdns_context *context, uint8_t* value) { return GETDNS_RETURN_GOOD; } +getdns_return_t +getdns_context_get_edns_client_subnet_private(getdns_context *context, uint8_t* value) { + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER); + *value = context->edns_client_subnet_private; + return GETDNS_RETURN_GOOD; +} + /* context.c */ diff --git a/src/context.h b/src/context.h index 8a205e4e..5b6620e5 100644 --- a/src/context.h +++ b/src/context.h @@ -157,6 +157,7 @@ struct getdns_context { uint8_t edns_version; uint8_t edns_do_bit; int edns_maximum_udp_payload_size; /* -1 is unset */ + uint8_t edns_client_subnet_private; SSL_CTX* tls_ctx; getdns_update_callback update_callback; diff --git a/src/getdns/getdns_extra.h.in b/src/getdns/getdns_extra.h.in index bd46a4c2..141dcc58 100644 --- a/src/getdns/getdns_extra.h.in +++ b/src/getdns/getdns_extra.h.in @@ -194,6 +194,11 @@ getdns_context_get_edns_version(getdns_context *context, uint8_t* value); getdns_return_t getdns_context_get_edns_do_bit(getdns_context *context, uint8_t* value); +getdns_return_t +getdns_context_set_edns_client_subnet_private(getdns_context *context, uint8_t value); +getdns_return_t +getdns_context_get_edns_client_subnet_private(getdns_context *context, uint8_t* value); + /** * Pretty print the getdns_dict in a given buffer snprintf style. @@ -366,6 +371,8 @@ typedef enum getdns_tls_authentication_t { #define GETDNS_CONTEXT_CODE_TLS_AUTHENTICATION 618 #define GETDNS_CONTEXT_CODE_TLS_AUTHENTICATION_TEXT "Change related to getdns_context_set_tls_authentication" +#define GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE 619 +#define GETDNS_CONTEXT_CODE_EDNS_CLIENT_SUBNET_PRIVATE_TEXT "Change related to getdns_context_set_edns_client_subnet_private" getdns_return_t getdns_context_set_tls_authentication( diff --git a/src/libgetdns.symbols b/src/libgetdns.symbols index c0241c31..9991f010 100644 --- a/src/libgetdns.symbols +++ b/src/libgetdns.symbols @@ -13,6 +13,7 @@ getdns_context_get_dnssec_allowed_skew getdns_context_get_dnssec_trust_anchors getdns_context_get_dns_transport getdns_context_get_dns_transport_list +getdns_context_get_edns_client_subnet_private getdns_context_get_edns_do_bit getdns_context_get_edns_extended_rcode getdns_context_get_edns_maximum_udp_payload_size @@ -36,6 +37,7 @@ getdns_context_set_dnssec_allowed_skew getdns_context_set_dnssec_trust_anchors getdns_context_set_dns_transport getdns_context_set_dns_transport_list +getdns_context_set_edns_client_subnet_private getdns_context_set_edns_do_bit getdns_context_set_edns_extended_rcode getdns_context_set_edns_maximum_udp_payload_size diff --git a/src/request-internal.c b/src/request-internal.c index 42c296ec..ddad29ec 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -367,7 +367,7 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, with_opt = edns_do_bit != 0 || edns_maximum_udp_payload_size != 512 || edns_extended_rcode != 0 || edns_version != 0 || noptions || - edns_cookies; + edns_cookies || context->edns_client_subnet_private; edns_maximum_udp_payload_size = with_opt && ( edns_maximum_udp_payload_size == -1 || @@ -437,6 +437,7 @@ _getdns_dns_req_new(getdns_context *context, getdns_eventloop *loop, result->dnssec_return_only_secure = dnssec_return_only_secure; result->dnssec_return_validation_chain = dnssec_return_validation_chain; result->edns_cookies = edns_cookies; + result->edns_client_subnet_private = context->edns_client_subnet_private; /* will be set by caller */ result->user_pointer = NULL; diff --git a/src/stub.c b/src/stub.c index 582c02c5..0698e0eb 100644 --- a/src/stub.c +++ b/src/stub.c @@ -130,6 +130,20 @@ calc_new_cookie(getdns_upstream *upstream, uint8_t *cookie) cookie[i % 8] ^= md_value[i]; } +static getdns_return_t +attach_edns_client_subnet_private(getdns_network_req *req) +{ + /* see + * https://tools.ietf.org/html/draft-ietf-dnsop-edns-client-subnet-04#section-6 */ + /* all-zeros is a request to not leak the data further: */ + /* "\x00\x00" FAMILY: 0 (because no address) */ + /* "\x00" SOURCE PREFIX-LENGTH: 0 */ + /* "\x00"; SCOPE PREFIX-LENGTH: 0 */ + return _getdns_network_req_add_upstream_option(req, + GLDNS_EDNS_CLIENT_SUBNET, + 4, NULL); +} + static getdns_return_t attach_edns_cookie(getdns_network_req *req) { @@ -702,6 +716,9 @@ stub_tcp_write(int fd, getdns_tcp_state *tcp, getdns_network_req *netreq) if (netreq->owner->edns_cookies) if (attach_edns_cookie(netreq)) return STUB_OUT_OF_OPTIONS; + if (netreq->owner->edns_client_subnet_private) + if (attach_edns_client_subnet_private(netreq)) + return STUB_OUT_OF_OPTIONS; } pkt_len = netreq->response - netreq->query; /* We have an initialized packet buffer. @@ -1153,6 +1170,9 @@ stub_tls_write(getdns_upstream *upstream, getdns_tcp_state *tcp, /* we do not edns_cookie over TLS, since TLS * provides stronger guarantees than cookies * already */ + if (netreq->owner->edns_client_subnet_private) + if (attach_edns_client_subnet_private(netreq)) + return STUB_OUT_OF_OPTIONS; } pkt_len = netreq->response - netreq->query; @@ -1260,6 +1280,9 @@ stub_udp_write_cb(void *userarg) if (netreq->owner->edns_cookies) if (attach_edns_cookie(netreq)) return; /* too many upstream options */ + if (netreq->owner->edns_client_subnet_private) + if (attach_edns_client_subnet_private(netreq)) + return; /* too many upstream options */ } pkt_len = netreq->response - netreq->query; if ((ssize_t)pkt_len != sendto(netreq->fd, netreq->query, pkt_len, 0, diff --git a/src/types-internal.h b/src/types-internal.h index 202fbc61..7c05e2b9 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -275,6 +275,7 @@ typedef struct getdns_dns_req { int dnssec_return_only_secure; int dnssec_return_validation_chain; int edns_cookies; + int edns_client_subnet_private; /* Internally used by return_validation_chain */ int dnssec_ok_checking_disabled;