From: Artem Boldariev Date: Mon, 16 Oct 2023 19:54:13 +0000 (+0300) Subject: Integrate PROXYv2 support into dig X-Git-Tag: v9.19.19~10^2~12 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=e0afd614c32eebceac0eb08418ad051bc2b91e3e;p=thirdparty%2Fbind9.git Integrate PROXYv2 support into dig This commit adds PROXYv2 support into dig by the means of adding +[no]proxy and +[no]proxy-plain options. Since this commit dig supports sending PROXYv2 headers on all supported DNS-transports. The support for PROXYv2 is modelled after that one in kdig. --- diff --git a/bin/dig/dig.c b/bin/dig/dig.c index 54f7f81e33d..54d1425ea56 100644 --- a/bin/dig/dig.c +++ b/bin/dig/dig.c @@ -252,6 +252,14 @@ help(void) { "request)\n" " +padding=### (Set padding block size " "[0])\n" + " " + "+[no]proxy[=src_addr[#src_port]-dst_addr[#dst_port]] " + "(Add PROXYv2 headers to the queries. If addresses are omitted, " + "LOCAL PROXYv2 headers are added)\n" + " " + "+[no]proxy-plain[=src_addr[#src_port]-dst_addr[#dst_port]] " + "(The same as '+[no]proxy', but send PROXYv2 headers ahead of " + "any encryption if an encrypted transport is used)\n" " +qid=### (Specify the query ID to " "use when sending queries)\n" " +[no]qr (Print question before " @@ -365,6 +373,39 @@ received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) { } printf(";; SERVER: %s(%s) (%s)\n", fromtext, query->userarg, proto); + + if (query->lookup->proxy_mode) { + printf(";; CLIENT PROXY HEADER"); + + if ((dig_lookup_is_tls(query->lookup) || + (query->lookup->https_mode && + !query->lookup->http_plain)) && + query->lookup->proxy_plain) + { + printf(" (plain)"); + } + + printf(": "); + + if (!query->lookup->proxy_local) { + char src_buf[ISC_SOCKADDR_FORMATSIZE] = { 0 }; + char dst_buf[ISC_SOCKADDR_FORMATSIZE] = { 0 }; + + isc_sockaddr_format( + &query->lookup->proxy_src_addr, src_buf, + sizeof(src_buf)); + + isc_sockaddr_format( + &query->lookup->proxy_dst_addr, dst_buf, + sizeof(dst_buf)); + printf("source: %s, destination: %s", src_buf, + dst_buf); + } else { + printf("LOCAL"); + } + + printf("\n"); + } time(&tnow); (void)localtime_r(&tnow, &tmnow); @@ -1052,6 +1093,317 @@ printgreeting(int argc, char **argv, dig_lookup_t *lookup) { goto invalid_option; \ } while (0) +/* + * Parse source and destination addresses in the same format as used by "kdig": + * + * SRC_ADDR[#SRC_PORT]-DST_ADDR[#DST_PORT] + * + * This can be described (pretty closely for our purpose) using the + * following EBNF grammar: + * + * S = proxy-addrs. (* start rule *) + * proxy-addrs = addr "-" addr EOF. + * addr = addr-char { addr-char } ["#" port ]. + * port = digit { digit }. + * addr-char = . + * EOF = '\0'. + */ +#define MATCH(ch) (st->str[0] == (ch)) +#define MATCH_DIGIT() isdigit((unsigned char)(st->str[0])) +#define ADVANCE() st->str++ +#define GETP() (st->str) + +typedef struct isc_proxy_addrs_parser_state { + const char *str; + + const char *last_addr_start; + size_t last_addr_len; + + const char *last_port_start; + size_t last_port_len; + + const char *src_addr_start; + size_t src_addr_len; + + const char *src_port_start; + size_t src_port_len; + + const char *dst_addr_start; + size_t dst_addr_len; + + const char *dst_port_start; + size_t dst_port_len; +} isc_proxy_addrs_parser_state_t; + +static bool +rule_proxy_addrs(isc_proxy_addrs_parser_state_t *st); + +static bool +rule_addr(isc_proxy_addrs_parser_state_t *st); + +static bool +rule_port(isc_proxy_addrs_parser_state_t *st); + +static bool +rule_addr_char(isc_proxy_addrs_parser_state_t *st); + +static void +proxy_handle_port_string(const char *port_start, const size_t port_len, + in_port_t *pport) { + char buf[512] = { 0 }; /* max */ + size_t string_size = 0, max_string_bytes = 0; + + string_size = port_len + 1; + max_string_bytes = string_size > sizeof(buf) ? sizeof(buf) + : string_size; + + (void)strlcpy(buf, port_start, max_string_bytes); + *pport = (in_port_t)atoi(buf); +} + +static isc_result_t +proxy_handle_addr_string(const char *addr_start, const size_t addr_len, + const in_port_t addr_port, isc_sockaddr_t *addr) { + isc_result_t result = ISC_R_FAILURE; + char buf[512] = { 0 }; /* max */ + size_t string_size = 0, max_string_bytes = 0; + struct in_addr ipv4 = { 0 }; + struct in6_addr ipv6 = { 0 }; + int ret = 0; + + string_size = addr_len + 1; + max_string_bytes = string_size > sizeof(buf) ? sizeof(buf) + : string_size; + + (void)strlcpy(buf, addr_start, max_string_bytes); + + ret = inet_pton(AF_INET, buf, &ipv4); + if (ret == 1) { + isc_sockaddr_fromin(addr, &ipv4, addr_port); + result = ISC_R_SUCCESS; + } else { + ret = inet_pton(AF_INET6, buf, &ipv6); + if (ret == 1) { + isc_sockaddr_fromin6(addr, &ipv6, addr_port); + result = ISC_R_SUCCESS; + } + } + + return (result); +} + +static bool +parse_proxy_addresses(const char *addrs, isc_sockaddr_t *psrc, + isc_sockaddr_t *pdst) { + isc_result_t result = ISC_R_FAILURE; + isc_sockaddr_t src = { 0 }, dst = { 0 }; + isc_proxy_addrs_parser_state_t st = { 0 }; + in_port_t src_port = 0, dst_port = 53; /* Follow kdig footsteps */ + + REQUIRE(addrs != NULL && *addrs != '\0'); + REQUIRE(psrc != NULL); + REQUIRE(pdst != NULL); + + st.str = addrs; + + /* start syntax analysis and verification */ + if (!rule_proxy_addrs(&st)) { + warn("PROXY source and destination addresses cannot be parsed"); + return (false); + } + + /* get port numeric values */ + if (st.src_port_len > 0) { + INSIST(st.src_port_start != NULL); + proxy_handle_port_string(st.src_port_start, st.src_port_len, + &src_port); + } + + if (st.dst_port_len > 0) { + INSIST(st.dst_port_start != NULL); + proxy_handle_port_string(st.dst_port_start, st.dst_port_len, + &dst_port); + } + + /* get addresses */ + INSIST(st.src_addr_len > 0); + INSIST(st.src_addr_start != NULL); + INSIST(st.dst_addr_len > 0); + INSIST(st.dst_addr_start != NULL); + + result = proxy_handle_addr_string(st.src_addr_start, st.src_addr_len, + src_port, &src); + if (result != ISC_R_SUCCESS) { + warn("Cannot get PROXY source address: %s", + isc_result_totext(result)); + return (false); + } + + result = proxy_handle_addr_string(st.dst_addr_start, st.dst_addr_len, + dst_port, &dst); + if (result != ISC_R_SUCCESS) { + warn("Cannot get PROXY destination address: %s", + isc_result_totext(result)); + return (false); + } + + /* addresses should be of the same type */ + if (isc_sockaddr_pf(&src) != isc_sockaddr_pf(&dst)) { + warn("PROXY source and destination addresses must be of the " + "same type"); + return (false); + } + + *psrc = src; + *pdst = dst; + + return (true); +} + +static bool +rule_proxy_addrs(isc_proxy_addrs_parser_state_t *st) { + if (!rule_addr(st)) { + return (false); + } + + st->src_addr_start = st->last_addr_start; + st->src_addr_len = st->last_addr_len; + st->src_port_start = st->last_port_start; + st->src_port_len = st->last_port_len; + + if (!MATCH('-')) { + return (false); + } + + ADVANCE(); + + if (!rule_addr(st)) { + return (false); + } + + st->dst_addr_start = st->last_addr_start; + st->dst_addr_len = st->last_addr_len; + st->dst_port_start = st->last_port_start; + st->dst_port_len = st->last_port_len; + + if (!MATCH('\0')) { + return (false); + } + + return (true); +} + +static bool +rule_addr(isc_proxy_addrs_parser_state_t *st) { + const char *start = GETP(); + if (!rule_addr_char(st)) { + return (false); + } + + while (rule_addr_char(st)) { + /* skip */ + } + + st->last_addr_start = start; + st->last_addr_len = GETP() - start; + + if (MATCH('#')) { + ADVANCE(); + + if (!rule_port(st)) { + return (false); + } + } + + return (true); +} + +static bool +rule_port(isc_proxy_addrs_parser_state_t *st) { + const char *start = GETP(); + if (!MATCH_DIGIT()) { + return (false); + } + + ADVANCE(); + + while (MATCH_DIGIT()) { + ADVANCE(); + } + + st->last_port_start = start; + st->last_port_len = GETP() - start; + + return (true); +} + +static bool +rule_addr_char(isc_proxy_addrs_parser_state_t *st) { + if (MATCH('#') || MATCH('-') || MATCH('\0')) { + return (false); + } + + ADVANCE(); + + return (true); +} + +#undef GETP +#undef ADVANCE +#undef MATCH_DIGIT +#undef MATCH + +static bool +plus_proxy_handle_addresses(const char *value, const bool state, + dig_lookup_t *lookup) { + lookup->proxy_mode = state; + if (!state) { + /* + * We are not interested in the option value in that + * case + */ + return (true); + } + + if (value == NULL || *value == '\0') { + lookup->proxy_local = true; + return (true); + } + + if (!parse_proxy_addresses(value, &lookup->proxy_src_addr, + &lookup->proxy_dst_addr)) + { + return (false); + } + return (true); +} + +static bool +plus_proxy_options(const char *cmd, const char *value, const bool state, + dig_lookup_t *lookup) { + switch (cmd[5]) { + case '-': + FULLCHECK("proxy-plain"); + lookup->proxy_plain = state; + if (!plus_proxy_handle_addresses(value, state, lookup)) { + goto invalid_option; + } + break; + case '\0': + FULLCHECK("proxy"); + if (!plus_proxy_handle_addresses(value, state, lookup)) { + goto invalid_option; + } + break; + default: + goto invalid_option; + } + return (true); + +invalid_option: + return (false); +} + static bool plus_tls_options(const char *cmd, const char *value, const bool state, dig_lookup_t *lookup) { @@ -1797,19 +2149,30 @@ plus_option(char *option, bool is_batchfile, bool *need_clone, } break; case 'p': - FULLCHECK("padding"); - if (state && lookup->edns == -1) { - lookup->edns = DEFAULT_EDNS_VERSION; - } - if (value == NULL) { - goto need_value; - } - result = parse_uint(&num, value, 512, "padding"); - if (result != ISC_R_SUCCESS) { - warn("Couldn't parse padding"); - goto exit_or_usage; + switch (cmd[1]) { + case 'a': + FULLCHECK("padding"); + if (state && lookup->edns == -1) { + lookup->edns = DEFAULT_EDNS_VERSION; + } + if (value == NULL) { + goto need_value; + } + result = parse_uint(&num, value, 512, "padding"); + if (result != ISC_R_SUCCESS) { + warn("Couldn't parse padding"); + goto exit_or_usage; + } + lookup->padding = (uint16_t)num; + break; + case 'r': + if (!plus_proxy_options(cmd, value, state, lookup)) { + goto invalid_option; + } + break; + default: + goto invalid_option; } - lookup->padding = (uint16_t)num; break; case 'q': switch (cmd[1]) { diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 901e7837c8d..571614c07b6 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -794,6 +794,11 @@ clone_lookup(dig_lookup_t *lookold, bool servers) { looknew->rrcomments = lookold->rrcomments; looknew->fuzzing = lookold->fuzzing; looknew->fuzztime = lookold->fuzztime; + looknew->proxy_mode = lookold->proxy_mode; + looknew->proxy_plain = lookold->proxy_plain; + looknew->proxy_local = lookold->proxy_local; + looknew->proxy_src_addr = lookold->proxy_src_addr; + looknew->proxy_dst_addr = lookold->proxy_dst_addr; if (lookold->ecs_addr != NULL) { looknew->ecs_addr = isc_mem_get(mctx, @@ -2921,6 +2926,9 @@ start_tcp(dig_query_t *query) { bool tls_mode = false; isc_tlsctx_client_session_cache_t *sess_cache = NULL; int local_timeout; + isc_nm_proxy_type_t proxy_type = ISC_NM_PROXY_NONE; + isc_nm_proxyheader_info_t proxy_info = { 0 }; + isc_nm_proxyheader_info_t *ppi = NULL; REQUIRE(DIG_VALID_QUERY(query)); @@ -3012,6 +3020,22 @@ start_tcp(dig_query_t *query) { } } + if (query->lookup->proxy_mode) { + proxy_type = ISC_NM_PROXY_PLAIN; + if ((tls_mode || (query->lookup->https_mode && + !query->lookup->http_plain)) && + !query->lookup->proxy_plain) + { + proxy_type = ISC_NM_PROXY_ENCRYPTED; + } + if (!query->lookup->proxy_local) { + isc_nm_proxyheader_info_init( + &proxy_info, &query->lookup->proxy_src_addr, + &query->lookup->proxy_dst_addr, NULL); + ppi = &proxy_info; + } + } + REQUIRE(query != NULL); query_attach(query, &connectquery); @@ -3025,7 +3049,7 @@ start_tcp(dig_query_t *query) { isc_nm_streamdnsconnect(netmgr, &localaddr, &query->sockaddr, tcp_connected, connectquery, local_timeout, tlsctx, sess_cache, - ISC_NM_PROXY_NONE, NULL); + proxy_type, ppi); #if HAVE_LIBNGHTTP2 } else if (query->lookup->https_mode) { char uri[4096] = { 0 }; @@ -3045,13 +3069,13 @@ start_tcp(dig_query_t *query) { isc_nm_httpconnect(netmgr, &localaddr, &query->sockaddr, uri, !query->lookup->https_get, tcp_connected, connectquery, tlsctx, sess_cache, - local_timeout, ISC_NM_PROXY_NONE, NULL); + local_timeout, proxy_type, ppi); #endif } else { isc_nm_streamdnsconnect(netmgr, &localaddr, &query->sockaddr, tcp_connected, connectquery, - local_timeout, NULL, NULL, - ISC_NM_PROXY_NONE, NULL); + local_timeout, NULL, NULL, proxy_type, + ppi); } return; @@ -3301,9 +3325,24 @@ start_udp(dig_query_t *query) { } query_attach(query, &connectquery); - isc_nm_udpconnect(netmgr, &localaddr, &query->sockaddr, udp_ready, - connectquery, - (timeout ? timeout : UDP_TIMEOUT) * 1000); + if (query->lookup->proxy_mode) { + isc_nm_proxyheader_info_t proxy_info = { 0 }; + isc_nm_proxyheader_info_t *ppi = NULL; + if (!query->lookup->proxy_local) { + isc_nm_proxyheader_info_init( + &proxy_info, &query->lookup->proxy_src_addr, + &query->lookup->proxy_dst_addr, NULL); + ppi = &proxy_info; + } + isc_nm_proxyudpconnect(netmgr, &localaddr, &query->sockaddr, + udp_ready, connectquery, + (timeout ? timeout : UDP_TIMEOUT) * 1000, + ppi); + } else { + isc_nm_udpconnect(netmgr, &localaddr, &query->sockaddr, + udp_ready, connectquery, + (timeout ? timeout : UDP_TIMEOUT) * 1000); + } } /*% diff --git a/bin/dig/dighost.h b/bin/dig/dighost.h index af4379ce4cc..799fb4a7d83 100644 --- a/bin/dig/dighost.h +++ b/bin/dig/dighost.h @@ -187,6 +187,13 @@ struct dig_lookup { char *tls_key_file; isc_tlsctx_cache_t *tls_ctx_cache; }; + struct { + bool proxy_mode; + bool proxy_plain; + bool proxy_local; + isc_sockaddr_t proxy_src_addr; + isc_sockaddr_t proxy_dst_addr; + }; isc_stdtime_t fuzztime; };