From c556f307c9cb58ede24ee47e436c2a2ae060d2b8 Mon Sep 17 00:00:00 2001 From: Michael Mann Date: Sun, 1 Sep 2019 12:02:27 -0400 Subject: [PATCH] Add support for "custom" DNS servers to be used in address resolution Use the C-ARES ares_set_servers() API to set a list of DNS servers to use. The list of DNS servers is controlled through a UAT. There is also a boolean preference to determine if the custom list should be used. The boolean preference was added to arbitrate between the "default" list of servers that C-ARES creates during initialization and using the "custom" list (which may be empty and we don't want the "default" list to be overwritten) Bug: 15960 Change-Id: I4ba071777f6bb9bc2fef313b7618908d805c0690 Reviewed-on: https://code.wireshark.org/review/34419 Petri-Dish: Michael Mann Tested-by: Petri Dish Buildbot Reviewed-by: Alexis La Goutte Reviewed-by: Michael Mann --- epan/addr_resolv.c | 141 +++++++++++++++++++++++++++++++++++++++++++++ epan/addr_resolv.h | 1 + epan/prefs.c | 2 +- 3 files changed, 143 insertions(+), 1 deletion(-) diff --git a/epan/addr_resolv.c b/epan/addr_resolv.c index fc2a13f222..0cfa4199e4 100644 --- a/epan/addr_resolv.c +++ b/epan/addr_resolv.c @@ -94,6 +94,7 @@ #include #include #include +#include #define ENAME_HOSTS "hosts" #define ENAME_SUBNETS "subnets" @@ -365,6 +366,52 @@ static gboolean async_dns_initialized = FALSE; static guint async_dns_in_flight = 0; static wmem_list_t *async_dns_queue_head = NULL; +//UAT for providing a list of DNS servers to C-ARES for name resolution +gboolean use_custom_dns_server_list = FALSE; +struct dns_server_data { + char *ipaddr; +}; + +UAT_CSTRING_CB_DEF(dnsserverlist_uats, ipaddr, struct dns_server_data) + +static uat_t *dnsserver_uat = NULL; +static struct dns_server_data *dnsserverlist_uats = NULL; +static guint ndnsservers = 0; + +static void +dns_server_free_cb(void *data) +{ + struct dns_server_data *h = (struct dns_server_data*)data; + + g_free(h->ipaddr); +} + +static void* +dns_server_copy_cb(void *dst_, const void *src_, size_t len _U_) +{ + const struct dns_server_data *src = (const struct dns_server_data *)src_; + struct dns_server_data *dst = (struct dns_server_data *)dst_; + + dst->ipaddr = g_strdup(src->ipaddr); + + return dst; +} + +static gboolean +dnsserver_uat_fld_ip_chk_cb(void* r _U_, const char* ipaddr, guint len _U_, const void* u1 _U_, const void* u2 _U_, char** err) +{ + //Check for a valid IPv4 or IPv6 address. + if (ipaddr && g_hostname_is_ip_address(ipaddr)) { + *err = NULL; + return TRUE; + } + + *err = g_strdup_printf("No valid IP address given."); + return FALSE; +} + + + static void c_ares_ghba_sync_cb(void *arg, int status, int timeouts _U_, struct hostent *he) { sync_dns_data_t *sdd = (sync_dns_data_t *)arg; @@ -504,6 +551,63 @@ set_resolution_synchrony(gboolean synchronous) { resolve_synchronously = synchronous; } + +static void +c_ares_set_dns_servers(void) +{ + if ((!async_dns_initialized) || (!use_custom_dns_server_list)) + return; + + if (ndnsservers == 0) { + //clear the list of servers. This may effectively disable name resolution + ares_set_servers(ghba_chan, NULL); + ares_set_servers(ghbn_chan, NULL); + } else { + struct ares_addr_node* servers = wmem_alloc_array(NULL, struct ares_addr_node, ndnsservers); + ws_in4_addr ipv4addr; + ws_in6_addr ipv6addr; + gboolean invalid_IP_found = FALSE; + struct ares_addr_node* server; + guint i; + for (i = 0, server = servers; i < ndnsservers-1; i++, server++) { + if (ws_inet_pton6(dnsserverlist_uats[i].ipaddr, &ipv6addr)) { + server->family = AF_INET6; + memcpy(&server->addr.addr6, &ipv6addr, 16); + } else if (ws_inet_pton4(dnsserverlist_uats[i].ipaddr, &ipv4addr)) { + server->family = AF_INET; + memcpy(&server->addr.addr4, &ipv4addr, 4); + } else { + //This shouldn't happen, but just in case... + invalid_IP_found = TRUE; + server->family = 0; + memset(&server->addr.addr4, 0, 4); + break; + } + + server->next = (server+1); + } + if (!invalid_IP_found) { + if (ws_inet_pton6(dnsserverlist_uats[i].ipaddr, &ipv6addr)) { + server->family = AF_INET6; + memcpy(&server->addr.addr6, &ipv6addr, 16); + } + else if (ws_inet_pton4(dnsserverlist_uats[i].ipaddr, &ipv4addr)) { + server->family = AF_INET; + memcpy(&server->addr.addr4, &ipv4addr, 4); + } else { + //This shouldn't happen, but just in case... + server->family = 0; + memset(&server->addr.addr4, 0, 4); + } + } + server->next = NULL; + + ares_set_servers(ghba_chan, servers); + ares_set_servers(ghbn_chan, servers); + wmem_free(NULL, servers); + } +} + #else void set_resolution_synchrony(gboolean synchronous _U_) @@ -2692,6 +2796,35 @@ addr_resolve_pref_init(module_t *nameres) " is enabled.", &gbl_resolv_flags.use_external_net_name_resolver); + prefs_register_bool_preference(nameres, "use_custom_dns_servers", + "Use custom list of DNS servers for name resolution", + "Uses DNS Servers list to resolve network names if TRUE. If FALSE, default information is used", + &use_custom_dns_server_list); + + static uat_field_t dns_server_uats_flds[] = { + UAT_FLD_CSTRING_OTHER(dnsserverlist_uats, ipaddr, "IP address", dnsserver_uat_fld_ip_chk_cb, "IPv4 or IPv6 address"), + UAT_END_FIELDS + }; + + dnsserver_uat = uat_new("DNS Servers", + sizeof(struct dns_server_data), + "addr_resolve_dns_servers", /* filename */ + TRUE, /* from_profile */ + &dnsserverlist_uats, /* data_ptr */ + &ndnsservers, /* numitems_ptr */ + UAT_AFFECTS_DISSECTION, + NULL, + dns_server_copy_cb, + NULL, + dns_server_free_cb, + c_ares_set_dns_servers, + NULL, + dns_server_uats_flds); + prefs_register_uat_preference(nameres, "dns_servers", + "DNS Servers", + "A table of IPv4 and IPv6 addresses that will be used resolve IP names and addresses", + dnsserver_uat); + prefs_register_obsolete_preference(nameres, "concurrent_dns"); prefs_register_uint_preference(nameres, "name_resolve_concurrency", @@ -2731,6 +2864,13 @@ addr_resolve_pref_init(module_t *nameres) } +void addr_resolve_pref_apply(void) +{ +#ifdef HAVE_C_ARES + c_ares_set_dns_servers(); +#endif +} + void disable_name_resolution(void) { gbl_resolv_flags.mac_name = FALSE; @@ -2995,6 +3135,7 @@ host_name_lookup_init(void) #endif if (ares_init(&ghba_chan) == ARES_SUCCESS && ares_init(&ghbn_chan) == ARES_SUCCESS) { async_dns_initialized = TRUE; + c_ares_set_dns_servers(); } #ifdef CARES_HAVE_ARES_LIBRARY_INIT } diff --git a/epan/addr_resolv.h b/epan/addr_resolv.h index 5d78df12a6..94b564d3a1 100644 --- a/epan/addr_resolv.h +++ b/epan/addr_resolv.h @@ -174,6 +174,7 @@ WS_DLL_PUBLIC int port_with_resolution_to_str_buf(gchar *buf, gulong buf_size, /* Setup name resolution preferences */ struct pref_module; extern void addr_resolve_pref_init(struct pref_module *nameres); +extern void addr_resolve_pref_apply(void); /* * disable_name_resolution() sets all relevant gbl_resolv_flags to FALSE. diff --git a/epan/prefs.c b/epan/prefs.c index 7d6b9475f8..b58af89c01 100644 --- a/epan/prefs.c +++ b/epan/prefs.c @@ -3535,7 +3535,7 @@ prefs_register_modules(void) /* Name Resolution */ nameres_module = prefs_register_module(NULL, "nameres", "Name Resolution", - "Name Resolution", NULL, TRUE); + "Name Resolution", addr_resolve_pref_apply, TRUE); addr_resolve_pref_init(nameres_module); oid_pref_init(nameres_module); maxmind_db_pref_init(nameres_module);