From 6cf4e276c9ba1803fbaab2dcf202cb1b5b6f9d63 Mon Sep 17 00:00:00 2001 From: Nathan Moinvaziri Date: Tue, 12 Dec 2023 10:54:31 -0800 Subject: [PATCH 1/2] Fixed local ip lookup on macOS GitHub instances. myIpAddress() and myIpAddressEx() should be enumerating the network interfaces and returning the local addresses with getifaddrs/GetAdapterAddresses. Where as, dnsResolve() and dnsResolveEx() enumerates resolved host names using getaddrinfo. --- execute_jscore.c | 18 +-- execute_wsh_dispatch.h | 4 +- net_util.c | 169 +++++++++++++++++-------- net_util.h | 8 +- test/CMakeLists.txt | 8 +- test/test_execute.cc | 8 +- test/{test_dns.cc => test_net_util.cc} | 119 +++++++++-------- 7 files changed, 202 insertions(+), 132 deletions(-) rename test/{test_dns.cc => test_net_util.cc} (51%) diff --git a/execute_jscore.c b/execute_jscore.c index 0ddaf66..edc1b9a 100644 --- a/execute_jscore.c +++ b/execute_jscore.c @@ -200,7 +200,7 @@ static JSValueRef proxy_execute_jscore_dns_resolve_ex(JSContextRef ctx, JSObject static JSValueRef proxy_execute_jscore_my_ip_address(JSContextRef ctx, JSObjectRef function, JSObjectRef object, size_t argc, const JSValueRef argv[], JSValueRef *exception) { - char *address = dns_resolve(NULL, NULL); + char *address = my_ip_address(); if (!address) return NULL; @@ -216,18 +216,18 @@ static JSValueRef proxy_execute_jscore_my_ip_address(JSContextRef ctx, JSObjectR static JSValueRef proxy_execute_jscore_my_ip_address_ex(JSContextRef ctx, JSObjectRef function, JSObjectRef object, size_t argc, const JSValueRef argv[], JSValueRef *exception) { - char *address = dns_resolve_ex(NULL, NULL); - if (!address) + char *addresses = my_ip_address_ex(); + if (!addresses) return NULL; - JSStringRef address_string = g_proxy_execute_jscore.JSStringCreateWithUTF8CString(address); - free(address); - if (!address_string) + JSStringRef addresses_string = g_proxy_execute_jscore.JSStringCreateWithUTF8CString(addresses); + free(addresses); + if (!addresses_string) return NULL; - JSValueRef address_value = g_proxy_execute_jscore.JSValueMakeString(ctx, address_string); - g_proxy_execute_jscore.JSStringRelease(address_string); - return address_value; + JSValueRef addresses_value = g_proxy_execute_jscore.JSValueMakeString(ctx, addresses_string); + g_proxy_execute_jscore.JSStringRelease(addresses_string); + return addresses_value; } bool proxy_execute_register_function(void *ctx, JSGlobalContextRef global, const char *name, diff --git a/execute_wsh_dispatch.h b/execute_wsh_dispatch.h index ad7042e..f6e2fa1 100644 --- a/execute_wsh_dispatch.h +++ b/execute_wsh_dispatch.h @@ -146,9 +146,9 @@ static HRESULT STDMETHODCALLTYPE script_dispatch_invoke(IDispatch *dispatch, DIS char *ip = NULL; if (disp_id == SCRIPT_DISPATCH_MY_IP_ADDRESS_EX_ID) - ip = dns_resolve_ex(NULL, NULL); + ip = my_ip_address_ex(); else - ip = dns_resolve(NULL, NULL); + ip = my_ip_address(); if (!ip) return E_FAIL; diff --git a/net_util.c b/net_util.c index 19ec49f..abb37c9 100644 --- a/net_util.c +++ b/net_util.c @@ -4,6 +4,7 @@ #include #include #include +#include #ifdef _WIN32 # include @@ -16,104 +17,172 @@ # include #endif +#include "net_adapter.h" #include "util.h" +typedef struct address_list { + int32_t family; + int32_t max_addrs; + char *string; + size_t string_len; + size_t max_string; +} address_list; + +// Calculate the max length of the address string +static bool my_ip_address_list_length(void *user_data, net_adapter_s *adapter) { + address_list *list = (address_list *)user_data; + // Use different length depending on the address type + list->max_string += ((list->family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN); + // Add room for semi-colon separators + list->max_string += 2; + return true; +} + +// Copy each localhost address into a string buffer seperated by semi-colons +static bool my_ip_address_list_populate(void *user_data, net_adapter_s *adapter) { + address_list *list = (address_list *)user_data; + + if (!list->max_addrs || !adapter->is_connected || !*adapter->ip) + return true; + +#ifdef _WIN32 + if (!adapter->is_dhcp_v4) + return true; +#endif + + if (list->family == AF_INET || list->family == AF_UNSPEC) { + char ip_str[INET_ADDRSTRLEN] = {0}; + inet_ntop(AF_INET, adapter->ip, ip_str, sizeof(ip_str)); + strncat(list->string + list->string_len, ip_str, list->max_string - list->string_len - 1); + list->string_len += strlen(ip_str); + } + + if (adapter->is_ipv6 && (list->family == AF_INET6 || list->family == AF_UNSPEC)) { + // Append semi-colon separator + if (list->max_string - list->string_len > 1) { + list->string[list->string_len++] = ';'; + list->string[list->string_len] = 0; + } + + char ipv6_str[INET6_ADDRSTRLEN] = {0}; + inet_ntop(AF_INET6, adapter->ipv6, ipv6_str, sizeof(ipv6_str)); + strncat(list->string + list->string_len, ipv6_str, list->max_string - list->string_len - 1); + list->string_len += strlen(ipv6_str); + } + + list->max_addrs--; + + // Append semi-colon separator + if (list->max_addrs && list->max_string - list->string_len > 1) { + list->string[list->string_len++] = ';'; + list->string[list->string_len] = 0; + } + return true; +} + +// Enumerate network adapters and get localhost addresses with a filter +static char *my_ip_address_filter(int32_t family, int32_t max_addrs) { + address_list list = {family, max_addrs, NULL, 0, 1}; + int32_t err = 0; + + if (!net_adapter_enum(&list, my_ip_address_list_length)) + return NULL; + + // Allocate buffer for the return string + list.string = (char *)calloc(1, list.max_string); + if (list.string) { + if (!net_adapter_enum(&list, my_ip_address_list_populate)) { + free(list.string); + list.string = NULL; + } + } + + return list.string; +} + +// Get local IPv4 address for localhost +char *my_ip_address(void) { + return my_ip_address_filter(AF_INET, 1); +} + +// Get local IPv6 and IPv6 addresses for localhost +char *my_ip_address_ex(void) { + return my_ip_address_filter(AF_UNSPEC, UINT8_MAX); +} + // Resolve a host name to its addresses with a filter and custom separator static char *dns_resolve_filter(const char *host, int32_t family, uint8_t max_addrs, int32_t *error) { - char name[HOST_MAX] = {0}; + address_list list = {family, max_addrs, NULL, 0, 1}; struct addrinfo hints = {0}; struct addrinfo *address_info = NULL; struct addrinfo *address = NULL; - char *ai_string = NULL; - size_t ai_string_len = 0; int32_t err = 0; - // If no host supplied, then use local machine name if (!host) { - err = gethostname(name, sizeof(name)); - if (err != 0) - goto dns_resolve_error; - } else { - // Otherwise copy the host provided - strncat(name, host, sizeof(name) - 1); + if (error) + *error = EINVAL; + return NULL; } - hints.ai_flags = AI_NUMERICHOST; - hints.ai_family = PF_UNSPEC; + hints.ai_family = family; hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - err = getaddrinfo(name, NULL, &hints, &address_info); - if (err != EAI_NONAME) { - if (err != 0) - goto dns_resolve_error; - // Name is already an IP address - freeaddrinfo(address_info); - return strdup(name); - } - - hints.ai_flags = 0; - err = getaddrinfo(name, NULL, &hints, &address_info); + err = getaddrinfo(host, NULL, &hints, &address_info); if (err != 0) goto dns_resolve_error; // Calculate the length of the return string - size_t max_ai_string = 1; address = address_info; while (address) { // Use different length depending on the address type - if (address->ai_family == AF_INET) - max_ai_string += INET_ADDRSTRLEN; - else - max_ai_string += INET6_ADDRSTRLEN; - + list.max_string += (address->ai_family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; // Add room for semi-colon separator - max_ai_string++; + list.max_string++; address = address->ai_next; } // Allocate buffer for the return string - ai_string = (char *)calloc(1, max_ai_string); - if (!ai_string) + list.string = (char *)calloc(1, list.max_string); + if (!list.string) goto dns_resolve_error; // Enumerate each address address = address_info; - while (address && max_addrs) { + while (address && list.max_addrs) { // Only copy addresses that match the family filter - if (family == AF_UNSPEC || address->ai_family == family) { + if (list.family == AF_UNSPEC || address->ai_family == list.family) { // Ensure there is room to copy something into return string buffer - if (ai_string_len >= max_ai_string) + if (list.string_len >= list.max_string) break; - // Copy address name into return string - err = getnameinfo(address->ai_addr, (socklen_t)address->ai_addrlen, ai_string + ai_string_len, - (uint32_t)(max_ai_string - ai_string_len), NULL, 0, NI_NUMERICHOST); - if (err != 0) - goto dns_resolve_error; + // Convert address name into a numeric host string + err = getnameinfo(address->ai_addr, (socklen_t)address->ai_addrlen, list.string + list.string_len, + (uint32_t)(list.max_string - list.string_len), NULL, 0, NI_NUMERICHOST); + if (err != 0) { + continue; + } - max_addrs--; + list.max_addrs--; // Append semi-colon separator - ai_string_len = strlen(ai_string); - if (max_addrs && address->ai_next && ai_string_len + 1 < max_ai_string) { - ai_string[ai_string_len++] = ';'; - ai_string[ai_string_len] = 0; + list.string_len = strlen(list.string); + if (list.max_addrs && address->ai_next && list.string_len + 1 < list.max_string) { + list.string[list.string_len++] = ';'; + list.string[list.string_len] = 0; } } address = address->ai_next; } - if (err != 0) + if (err != 0 && list.string_len == 0) goto dns_resolve_error; - return ai_string; + return list.string; dns_resolve_error: - free(ai_string); + free(list.string); if (address_info) freeaddrinfo(address_info); diff --git a/net_util.h b/net_util.h index 4d20eb8..96fe6c7 100644 --- a/net_util.h +++ b/net_util.h @@ -4,10 +4,16 @@ extern "C" { #endif +// Get local IPv4 address for localhost +char *my_ip_address(void); + +// Get local IPv6 and IPv6 addresses for localhost +char *my_ip_address_ex(void); + // Resolve a host name to it an IPv4 address char *dns_resolve(const char *host, int32_t *error); -// Resolve a host name to its addresses +// Resolve a host name to its IPv6 and IPv6 addresses char *dns_resolve_ex(const char *host, int32_t *error); // Check if the ipv4 address matches the cidr notation range diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9d964d6..3f191b7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,8 +7,8 @@ if(PROXYRES_BUILD_CLI) uwp/Square150x150Logo.png uwp/StoreLogo.png uwp/Package.appxmanifest) - - set_source_files_properties(${PROXYCLI_ASSETS} PROPERTIES + + set_source_files_properties(${PROXYCLI_ASSETS} PROPERTIES VS_DEPLOYMENT_CONTENT 1 VS_DEPLOYMENT_LOCATION "Assets") endif() @@ -85,6 +85,8 @@ if(PROXYRES_BUILD_TESTS) set(TEST_SRCS test_config.cc test_main.cc + test_net_util.cc + test_net_adapter.cc test_threadpool.cc test_util.cc) if(WIN32) @@ -96,10 +98,8 @@ if(PROXYRES_BUILD_TESTS) endif() if(PROXYRES_EXECUTE) list(APPEND TEST_SRCS - test_dns.cc test_execute.cc test_fetch.cc - test_net_adapter.cc test_wpad.cc) endif() diff --git a/test/test_execute.cc b/test/test_execute.cc index 43af4da..9eb9d99 100644 --- a/test/test_execute.cc +++ b/test/test_execute.cc @@ -74,9 +74,7 @@ INSTANTIATE_TEST_SUITE_P(execute, execute, testing::ValuesIn(execute_tests)); // Construct expected output for myip test case static char *expected_my_ip(void) { - int32_t error = 0; - char *address = dns_resolve(NULL, &error); - EXPECT_EQ(error, 0); + char *address = my_ip_address(); if (!address) return NULL; @@ -92,9 +90,7 @@ static char *expected_my_ip(void) { // Construct expected output for myipex test case static char *expected_my_ip_ex(void) { - int32_t error = 0; - char *addresses = dns_resolve_ex(NULL, &error); - EXPECT_EQ(error, 0); + char *addresses = my_ip_address_ex(); if (!addresses) return NULL; diff --git a/test/test_dns.cc b/test/test_net_util.cc similarity index 51% rename from test/test_dns.cc rename to test/test_net_util.cc index 34cd0a2..997c7d6 100644 --- a/test/test_dns.cc +++ b/test/test_net_util.cc @@ -1,60 +1,59 @@ -#include -#include -#include -#include - -#include - -#include "net_util.h" - -TEST(dns, resolve_local) { - int32_t error = 0; - char *ip = dns_resolve(NULL, &error); - EXPECT_EQ(error, 0); - EXPECT_NE(ip, nullptr); - if (ip) { - EXPECT_TRUE(strstr(ip, ".") != NULL); - free(ip); - } -} - -TEST(dns, resolve_google) { - int32_t error = 0; - char *ip = dns_resolve("google.com", &error); - EXPECT_EQ(error, 0); - EXPECT_TRUE(ip != NULL); - if (ip) { - EXPECT_TRUE(strstr(ip, ".") != NULL); - EXPECT_TRUE(strstr(ip, ";") == NULL); - free(ip); - } -} - -TEST(dns, resolve_bad) { - int32_t error = 0; - char *ip = dns_resolve("hopefully-doesnt-exist.com", &error); - EXPECT_EQ(ip, nullptr); - EXPECT_NE(error, 0); -} - -TEST(dns_ex, resolve_local) { - int32_t error = 0; - char *ips = dns_resolve_ex(NULL, &error); - EXPECT_EQ(error, 0); - EXPECT_TRUE(ips != NULL); - if (ips) { - EXPECT_TRUE(strstr(ips, ".") != NULL || strstr(ips, ":") != NULL); - free(ips); - } -} - -TEST(dns_ex, resolve_google) { - int32_t error = 0; - char *ips = dns_resolve_ex("google.com", &error); - EXPECT_EQ(error, 0); - EXPECT_TRUE(ips != NULL); - if (ips) { - EXPECT_TRUE(strstr(ips, ".") != NULL || strstr(ips, ":") != NULL); - free(ips); - } -} +#include +#include +#include +#include + +#include + +#include "net_util.h" + +TEST(net_util, my_ip_address) { + char *address = my_ip_address(); + EXPECT_TRUE(address != NULL); + if (address) { + EXPECT_TRUE(strstr(address, ".") != NULL); + EXPECT_TRUE(strstr(address, ";") == NULL); + EXPECT_TRUE(strstr(address, ":") == NULL); + free(address); + } +} + +TEST(net_util, my_ip_address_ex) { + char *addresses = my_ip_address_ex(); + EXPECT_TRUE(addresses != NULL); + if (addresses) { + EXPECT_TRUE(strstr(addresses, ".") != NULL || strstr(addresses, ":") != NULL); + free(addresses); + } +} + +TEST(net_util, dns_resolve_google) { + int32_t error = 0; + char *ip = dns_resolve("google.com", &error); + EXPECT_EQ(error, 0); + EXPECT_TRUE(ip != NULL); + if (ip) { + EXPECT_TRUE(strstr(ip, ".") != NULL); + EXPECT_TRUE(strstr(ip, ";") == NULL); + EXPECT_TRUE(strstr(ip, ":") == NULL); + free(ip); + } +} + +TEST(net_util, dns_resolve_bad) { + int32_t error = 0; + char *ip = dns_resolve("hopefully-doesnt-exist.com", &error); + EXPECT_EQ(ip, nullptr); + EXPECT_NE(error, 0); +} + +TEST(net_util, dns_ex_resolve_google) { + int32_t error = 0; + char *ips = dns_resolve_ex("google.com", &error); + EXPECT_EQ(error, 0); + EXPECT_TRUE(ips != NULL); + if (ips) { + EXPECT_TRUE(strstr(ips, ".") != NULL || strstr(ips, ":") != NULL); + free(ips); + } +} From af06618db3a9e04384a32d2298d7072efba29cb4 Mon Sep 17 00:00:00 2001 From: Nathan Moinvaziri Date: Tue, 12 Dec 2023 14:07:42 -0800 Subject: [PATCH 2/2] Clean up net_util unit tests for C++. --- test/test_net_util.cc | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/test/test_net_util.cc b/test/test_net_util.cc index 997c7d6..83ed194 100644 --- a/test/test_net_util.cc +++ b/test/test_net_util.cc @@ -9,20 +9,19 @@ TEST(net_util, my_ip_address) { char *address = my_ip_address(); - EXPECT_TRUE(address != NULL); + EXPECT_NE(address, nullptr); if (address) { - EXPECT_TRUE(strstr(address, ".") != NULL); - EXPECT_TRUE(strstr(address, ";") == NULL); - EXPECT_TRUE(strstr(address, ":") == NULL); + EXPECT_NE(strchr(address, '.'), nullptr); + EXPECT_EQ(strpbrk(address, ";:"), nullptr); free(address); } } TEST(net_util, my_ip_address_ex) { char *addresses = my_ip_address_ex(); - EXPECT_TRUE(addresses != NULL); + EXPECT_NE(addresses, nullptr); if (addresses) { - EXPECT_TRUE(strstr(addresses, ".") != NULL || strstr(addresses, ":") != NULL); + EXPECT_NE(strpbrk(addresses, ".:"), nullptr); free(addresses); } } @@ -31,11 +30,10 @@ TEST(net_util, dns_resolve_google) { int32_t error = 0; char *ip = dns_resolve("google.com", &error); EXPECT_EQ(error, 0); - EXPECT_TRUE(ip != NULL); + EXPECT_NE(ip, nullptr); if (ip) { - EXPECT_TRUE(strstr(ip, ".") != NULL); - EXPECT_TRUE(strstr(ip, ";") == NULL); - EXPECT_TRUE(strstr(ip, ":") == NULL); + EXPECT_NE(strstr(ip, "."), nullptr); + EXPECT_EQ(strpbrk(ip, ";:"), nullptr); free(ip); } } @@ -51,9 +49,9 @@ TEST(net_util, dns_ex_resolve_google) { int32_t error = 0; char *ips = dns_resolve_ex("google.com", &error); EXPECT_EQ(error, 0); - EXPECT_TRUE(ips != NULL); + EXPECT_NE(ips, nullptr); if (ips) { - EXPECT_TRUE(strstr(ips, ".") != NULL || strstr(ips, ":") != NULL); + EXPECT_NE(strpbrk(ips, ".:"), nullptr); free(ips); } }