diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b41fc6..7ca2ca7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,309 +1,309 @@ -cmake_minimum_required(VERSION 3.12) - -include(CheckIncludeFile) - -option(PROXYRES_CURL "Enable support for downloading PAC scripts using curl." OFF) -option(PROXYRES_EXECUTE "Enable support for PAC script execution." ON) - -option(PROXYRES_BUILD_CLI "Build command line utility." ON) -option(PROXYRES_BUILD_TESTS "Build Googletest unit tests project." ON) - -option(PROXYRES_CODE_COVERAGE "Build for code coverage." OFF) - -if(UNIX AND NOT APPLE AND NOT PROXYRES_EXECUTE) - message(STATUS "PAC script execution enabled on linux") - set(PROXYRES_EXECUTE ON) -endif() - -project(proxyres) - -set(PROXYRES_SRCS - include/proxyres/config.h - include/proxyres/proxyres.h - include/proxyres/resolver.h) -if(PROXYRES_EXECUTE) - list(APPEND PROXYRES_SRCS - include/proxyres/execute.h) -endif() - -list(APPEND PROXYRES_SRCS - config_i.h - config.c - event.h - log.h - mutex.h - net_util.c - net_util.h - proxyres.c - resolver_i.h - resolver.c - threadpool.h - util.c - util.h) -if(PROXYRES_EXECUTE) - list(APPEND PROXYRES_SRCS - execute_i.h - execute.c - fetch.h - mozilla_js.h - net_adapter.c - net_adapter.h - resolver_posix.c - resolver_posix.h - wpad_dhcp_posix.c - wpad_dhcp_posix.h - wpad_dhcp.c - wpad_dhcp.h - wpad_dns.c - wpad_dns.h) -endif() -if(WIN32) - list(APPEND PROXYRES_SRCS - config_win.c - config_win.h - event_win.c - mutex_win.c - net_adapter_win.c - util_win.c - util_win.h) - if(PROXYRES_EXECUTE) - list(APPEND PROXYRES_SRCS - wpad_dhcp_win.c - wpad_dhcp_win.h) - endif() - if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") - list(APPEND PROXYRES_SRCS - resolver_winrt.c - resolver_winrt.h) - else() - list(APPEND PROXYRES_SRCS - resolver_win8.c - resolver_win8.h - resolver_winxp.c - resolver_winxp.h) - if(PROXYRES_EXECUTE) - list(APPEND PROXYRES_SRCS - execute_wsh_dispatch.h - execute_wsh_site.h - execute_wsh.c - execute_wsh.h) - endif() - endif() - - if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION GREATER_EQUAL 6.1) - list(APPEND PROXYRES_SRCS - threadpool_winvista.c) - else() - list(APPEND PROXYRES_SRCS - threadpool_winxp.c) - endif() -elseif(APPLE) - list(APPEND PROXYRES_SRCS - config_mac.c - config_mac.h - event_pthread.c - mutex_pthread.c - resolver_mac.c - resolver_mac.h - threadpool_pthread.c) - if(PROXYRES_EXECUTE) - list(APPEND PROXYRES_SRCS - execute_jscore.c - execute_jscore.h - net_adapter_mac.c - wpad_dhcp_mac.c - wpad_dhcp_mac.h) - endif() -elseif(UNIX) - list(APPEND PROXYRES_SRCS - config_env.c - config_env.h - config_gnome2.c - config_gnome2.h - config_gnome3.c - config_gnome3.h - config_kde.c - config_kde.h - event_pthread.c - execute_jscore.c - execute_jscore.h - mutex_pthread.c - net_adapter_linux.c - resolver_gnome3.c - resolver_gnome3.h - threadpool_pthread.c - util_linux.c - util_linux.h) -endif() - -if(WIN32) - add_compile_definitions( - _CRT_SECURE_NO_WARNINGS=1 - _CRT_NONSTDC_NO_WARNINGS=1 - _WINSOCK_DEPRECATED_NO_WARNINGS=1 - COBJMACROS) -elseif(APPLE) -elseif(UNIX) - add_compile_definitions(_GNU_SOURCE=1) -endif() - -add_compile_definitions($,_DEBUG,_NDEBUG>) - -if(PROXYRES_CODE_COVERAGE AND NOT MSVC) - include(CheckCCompilerFlag) - - # Check for -coverage flag support for Clang/GCC - if(CMAKE_VERSION VERSION_LESS 3.14) - set(CMAKE_REQUIRED_LIBRARIES -lgcov) - else() - set(CMAKE_REQUIRED_LINK_OPTIONS -coverage) - endif() - check_c_compiler_flag(-coverage HAVE_COVERAGE) - set(CMAKE_REQUIRED_LIBRARIES) - set(CMAKE_REQUIRED_LINK_OPTIONS) - - if(HAVE_COVERAGE) - add_compile_options(-coverage) - add_link_options(-coverage) - - if(CMAKE_C_COMPILER_ID MATCHES "GNU") - link_libraries(gcov) - endif() - - # Disable optimization for code coverage. Prepend the optimization flags to - # override any default flags set by CMake for the configuration type on Windows. - set(CMAKE_C_FLAGS "-O0 ${CMAKE_C_FLAGS}") - set(CMAKE_CXX_FLAGS "-O0 ${CMAKE_CXX_FLAGS}") - - message(STATUS "Code coverage enabled using: -coverage") - else() - message(WARNING "Code coverage not supported") - endif() -endif() - -add_library(proxyres ${PROXYRES_SRCS}) -set_property(TARGET proxyres PROPERTY C_STANDARD 99) -target_include_directories(proxyres PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/include/proxyres) -target_include_directories(proxyres PUBLIC - $) - -if(NOT WIN32) - check_include_file("net/if_arp.h" HAVE_NET_IF_ARP_H) - if(HAVE_NET_IF_ARP_H) - target_compile_definitions(proxyres PRIVATE HAVE_NET_IF_ARP_H) - endif() -endif() - -if(PROXYRES_CURL AND (PROXYRES_EXECUTE OR PROXYRES_BUILD_CLI)) - if(NOT TARGET CURL::libcurl) - include(FetchContent) - - # Disable compression and SSL support to avoid pulling in zlib and OpenSSL - set(CURL_ZLIB OFF CACHE BOOL "Build curl with ZLIB support" FORCE) - set(CURL_USE_LIBSSH2 OFF CACHE BOOL "Use libSSH2" FORCE) - - # Disable tests - set(BUILD_CURL_EXE OFF CACHE BOOL "Build executable" FORCE) - set(CURL_DISABLE_TESTS ON CACHE BOOL "Disable tests" FORCE) - set(CURL_ENABLE_EXPORT_TARGET OFF CACHE BOOL "to enable cmake export target" FORCE) - - # Disable unnecessary protocols - set(CURL_DISABLE_HSTS ON CACHE BOOL "to disable HSTS support" FORCE) - set(CURL_DISABLE_FTP ON CACHE BOOL "Disable FTP" FORCE) - set(CURL_DISABLE_TELNET ON CACHE BOOL "Disable TELNET" FORCE) - set(CURL_DISABLE_LDAP ON CACHE BOOL "Disable LDAP" FORCE) - set(CURL_DISABLE_LDAPS ON CACHE BOOL "Disable LDAPS" FORCE) - set(CURL_DISABLE_DICT ON CACHE BOOL "Disable DICT" FORCE) - set(CURL_DISABLE_TFTP ON CACHE BOOL "Disable TFTP" FORCE) - set(CURL_DISABLE_GOPHER ON CACHE BOOL "Disable GOPHER" FORCE) - set(CURL_DISABLE_POP3 ON CACHE BOOL "Disable POP3" FORCE) - set(CURL_DISABLE_IMAP ON CACHE BOOL "Disable IMAP" FORCE) - set(CURL_DISABLE_SMB ON CACHE BOOL "Disable SMB" FORCE) - set(CURL_DISABLE_SMTP ON CACHE BOOL "Disable SMTP" FORCE) - set(CURL_DISABLE_RTSP ON CACHE BOOL "Disable RTSP" FORCE) - set(CURL_DISABLE_MQTT ON CACHE BOOL "Disable MQTT" FORCE) - set(CURL_DISABLE_ALTSVC ON CACHE BOOL "Disable alt-svc support" FORCE) - set(CURL_DISABLE_GETOPTIONS ON CACHE BOOL "Disables curl_easy_options API" FORCE) - set(CURL_DISABLE_MIME ON CACHE BOOL "Disables MIME support" FORCE) - set(CURL_DISABLE_NETRC ON CACHE BOOL "Disables netrc parser" FORCE) - set(CURL_DISABLE_PROGRESS_METER ON CACHE BOOL "Disables built-in progress meter" FORCE) - - # Allow specifying alternative curl repository - if(NOT DEFINED CURL_REPOSITORY) - set(CURL_REPOSITORY https://github.com/curl/curl.git) - endif() - - # Fetch curl source code from official repository - FetchContent_Declare(curl - GIT_REPOSITORY ${CURL_REPOSITORY}) - - FetchContent_GetProperties(curl) - if(NOT curl_POPULATED) - FetchContent_Populate(curl) - add_subdirectory(${curl_SOURCE_DIR} ${curl_BINARY_DIR} EXCLUDE_FROM_ALL) - endif() - - if(NOT TARGET CURL::libcurl) - add_library(CURL::libcurl ALIAS libcurl) - endif() - endif() -endif() - -if(PROXYRES_EXECUTE) - target_compile_definitions(proxyres PUBLIC PROXYRES_EXECUTE=1) - - if(TARGET CURL::libcurl) - target_compile_definitions(proxyres PUBLIC HAVE_CURL=1) - target_sources(proxyres PRIVATE fetch_curl.c) - target_link_libraries(proxyres CURL::libcurl) - else() - target_sources(proxyres PRIVATE fetch_posix.c) - endif() -endif() - -if(WIN32) - target_link_libraries(proxyres dhcpcsvc.lib iphlpapi.lib wininet winhttp ws2_32) -elseif(APPLE) - find_library(CFNETWORK_LIBRARY CFNetwork) - target_link_libraries(proxyres ${CFNETWORK_LIBRARY}) - - find_library(COREFOUNDATION_LIBRARY CoreFoundation) - target_link_libraries(proxyres ${COREFOUNDATION_LIBRARY}) - - find_library(SYSTEMCONFIGURATION_LIBRARY SystemConfiguration) - target_link_libraries(proxyres ${SYSTEMCONFIGURATION_LIBRARY}) - - find_package(Threads REQUIRED) - target_compile_definitions(proxyres PRIVATE HAVE_PTHREADS) - target_link_libraries(proxyres ${CMAKE_THREAD_LIBS_INIT}) - - set_target_properties(proxyres PROPERTIES LINK_FLAGS -Wl,-F/Library/Frameworks) -elseif(UNIX) - find_package(PkgConfig REQUIRED) - - pkg_check_modules(deps REQUIRED IMPORTED_TARGET glib-2.0) - target_link_libraries(proxyres PkgConfig::deps) - - pkg_check_modules(GConf REQUIRED gconf-2.0) - # Don't link libraries at compile time since we dynamically load them at runtime - target_include_directories(proxyres PRIVATE ${GConf_INCLUDE_DIRS}) - - pkg_search_module(JSCoreGTK REQUIRED javascriptcoregtk-4.0 javascriptcoregtk-3.0 javascriptcoregtk-1.0) - # Don't link libraries at compile time since we dynamically load them at runtime - target_include_directories(proxyres PRIVATE ${JSCoreGTK_INCLUDE_DIRS}) - - find_package(Threads REQUIRED) - target_compile_definitions(proxyres PRIVATE HAVE_PTHREADS) - target_link_libraries(proxyres ${CMAKE_THREAD_LIBS_INIT}) - - target_link_libraries(proxyres ${CMAKE_DL_LIBS}) -endif() - -if(PROXYRES_BUILD_CLI OR PROXYRES_BUILD_TESTS) - # Should be enabled in source root CMakeLists.txt - enable_testing() - - add_subdirectory(test) -endif() +cmake_minimum_required(VERSION 3.12) + +include(CheckIncludeFile) + +option(PROXYRES_CURL "Enable support for downloading PAC scripts using curl." OFF) +option(PROXYRES_EXECUTE "Enable support for PAC script execution." ON) + +option(PROXYRES_BUILD_CLI "Build command line utility." ON) +option(PROXYRES_BUILD_TESTS "Build Googletest unit tests project." ON) + +option(PROXYRES_CODE_COVERAGE "Build for code coverage." OFF) + +if(UNIX AND NOT APPLE AND NOT PROXYRES_EXECUTE) + message(STATUS "PAC script execution enabled on linux") + set(PROXYRES_EXECUTE ON) +endif() + +project(proxyres) + +set(PROXYRES_SRCS + include/proxyres/config.h + include/proxyres/proxyres.h + include/proxyres/resolver.h) +if(PROXYRES_EXECUTE) + list(APPEND PROXYRES_SRCS + include/proxyres/execute.h) +endif() + +list(APPEND PROXYRES_SRCS + config_i.h + config.c + event.h + log.h + mutex.h + net_util.c + net_util.h + proxyres.c + resolver_i.h + resolver.c + threadpool.h + util.c + util.h) +if(PROXYRES_EXECUTE) + list(APPEND PROXYRES_SRCS + execute_i.h + execute.c + fetch.h + mozilla_js.h + net_adapter.c + net_adapter.h + resolver_posix.c + resolver_posix.h + wpad_dhcp_posix.c + wpad_dhcp_posix.h + wpad_dhcp.c + wpad_dhcp.h + wpad_dns.c + wpad_dns.h) +endif() +if(WIN32) + list(APPEND PROXYRES_SRCS + config_win.c + config_win.h + event_win.c + mutex_win.c + net_adapter_win.c + util_win.c + util_win.h) + if(PROXYRES_EXECUTE) + list(APPEND PROXYRES_SRCS + wpad_dhcp_win.c + wpad_dhcp_win.h) + endif() + if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") + list(APPEND PROXYRES_SRCS + resolver_winrt.c + resolver_winrt.h) + else() + list(APPEND PROXYRES_SRCS + resolver_win8.c + resolver_win8.h + resolver_winxp.c + resolver_winxp.h) + if(PROXYRES_EXECUTE) + list(APPEND PROXYRES_SRCS + execute_wsh_dispatch.h + execute_wsh_site.h + execute_wsh.c + execute_wsh.h) + endif() + endif() + + if(CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION GREATER_EQUAL 6.1) + list(APPEND PROXYRES_SRCS + threadpool_winvista.c) + else() + list(APPEND PROXYRES_SRCS + threadpool_winxp.c) + endif() +elseif(APPLE) + list(APPEND PROXYRES_SRCS + config_mac.c + config_mac.h + event_pthread.c + mutex_pthread.c + resolver_mac.c + resolver_mac.h + threadpool_pthread.c) + if(PROXYRES_EXECUTE) + list(APPEND PROXYRES_SRCS + execute_jscore.c + execute_jscore.h + net_adapter_mac.c + wpad_dhcp_mac.c + wpad_dhcp_mac.h) + endif() +elseif(UNIX) + list(APPEND PROXYRES_SRCS + config_env.c + config_env.h + config_gnome2.c + config_gnome2.h + config_gnome3.c + config_gnome3.h + config_kde.c + config_kde.h + event_pthread.c + execute_jscore.c + execute_jscore.h + mutex_pthread.c + net_adapter_linux.c + resolver_gnome3.c + resolver_gnome3.h + threadpool_pthread.c + util_linux.c + util_linux.h) +endif() + +if(WIN32) + add_compile_definitions( + _CRT_SECURE_NO_WARNINGS=1 + _CRT_NONSTDC_NO_WARNINGS=1 + _WINSOCK_DEPRECATED_NO_WARNINGS=1 + COBJMACROS) +elseif(APPLE) +elseif(UNIX) + add_compile_definitions(_GNU_SOURCE=1) +endif() + +add_compile_definitions($,_DEBUG,_NDEBUG>) + +if(PROXYRES_CODE_COVERAGE AND NOT MSVC) + include(CheckCCompilerFlag) + + # Check for -coverage flag support for Clang/GCC + if(CMAKE_VERSION VERSION_LESS 3.14) + set(CMAKE_REQUIRED_LIBRARIES -lgcov) + else() + set(CMAKE_REQUIRED_LINK_OPTIONS -coverage) + endif() + check_c_compiler_flag(-coverage HAVE_COVERAGE) + set(CMAKE_REQUIRED_LIBRARIES) + set(CMAKE_REQUIRED_LINK_OPTIONS) + + if(HAVE_COVERAGE) + add_compile_options(-coverage) + add_link_options(-coverage) + + if(CMAKE_C_COMPILER_ID MATCHES "GNU") + link_libraries(gcov) + endif() + + # Disable optimization for code coverage. Prepend the optimization flags to + # override any default flags set by CMake for the configuration type on Windows. + set(CMAKE_C_FLAGS "-O0 ${CMAKE_C_FLAGS}") + set(CMAKE_CXX_FLAGS "-O0 ${CMAKE_CXX_FLAGS}") + + message(STATUS "Code coverage enabled using: -coverage") + else() + message(WARNING "Code coverage not supported") + endif() +endif() + +add_library(proxyres ${PROXYRES_SRCS}) +set_property(TARGET proxyres PROPERTY C_STANDARD 99) +target_include_directories(proxyres PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include/proxyres) +target_include_directories(proxyres PUBLIC + $) + +if(NOT WIN32) + check_include_file("net/if_arp.h" HAVE_NET_IF_ARP_H) + if(HAVE_NET_IF_ARP_H) + target_compile_definitions(proxyres PRIVATE HAVE_NET_IF_ARP_H) + endif() +endif() + +if(PROXYRES_CURL AND (PROXYRES_EXECUTE OR PROXYRES_BUILD_CLI)) + if(NOT TARGET CURL::libcurl) + include(FetchContent) + + # Disable compression and SSL support to avoid pulling in zlib and OpenSSL + set(CURL_ZLIB OFF CACHE BOOL "Build curl with ZLIB support" FORCE) + set(CURL_USE_LIBSSH2 OFF CACHE BOOL "Use libSSH2" FORCE) + + # Disable tests + set(BUILD_CURL_EXE OFF CACHE BOOL "Build executable" FORCE) + set(CURL_DISABLE_TESTS ON CACHE BOOL "Disable tests" FORCE) + set(CURL_ENABLE_EXPORT_TARGET OFF CACHE BOOL "to enable cmake export target" FORCE) + + # Disable unnecessary protocols + set(CURL_DISABLE_HSTS ON CACHE BOOL "to disable HSTS support" FORCE) + set(CURL_DISABLE_FTP ON CACHE BOOL "Disable FTP" FORCE) + set(CURL_DISABLE_TELNET ON CACHE BOOL "Disable TELNET" FORCE) + set(CURL_DISABLE_LDAP ON CACHE BOOL "Disable LDAP" FORCE) + set(CURL_DISABLE_LDAPS ON CACHE BOOL "Disable LDAPS" FORCE) + set(CURL_DISABLE_DICT ON CACHE BOOL "Disable DICT" FORCE) + set(CURL_DISABLE_TFTP ON CACHE BOOL "Disable TFTP" FORCE) + set(CURL_DISABLE_GOPHER ON CACHE BOOL "Disable GOPHER" FORCE) + set(CURL_DISABLE_POP3 ON CACHE BOOL "Disable POP3" FORCE) + set(CURL_DISABLE_IMAP ON CACHE BOOL "Disable IMAP" FORCE) + set(CURL_DISABLE_SMB ON CACHE BOOL "Disable SMB" FORCE) + set(CURL_DISABLE_SMTP ON CACHE BOOL "Disable SMTP" FORCE) + set(CURL_DISABLE_RTSP ON CACHE BOOL "Disable RTSP" FORCE) + set(CURL_DISABLE_MQTT ON CACHE BOOL "Disable MQTT" FORCE) + set(CURL_DISABLE_ALTSVC ON CACHE BOOL "Disable alt-svc support" FORCE) + set(CURL_DISABLE_GETOPTIONS ON CACHE BOOL "Disables curl_easy_options API" FORCE) + set(CURL_DISABLE_MIME ON CACHE BOOL "Disables MIME support" FORCE) + set(CURL_DISABLE_NETRC ON CACHE BOOL "Disables netrc parser" FORCE) + set(CURL_DISABLE_PROGRESS_METER ON CACHE BOOL "Disables built-in progress meter" FORCE) + + # Allow specifying alternative curl repository + if(NOT DEFINED CURL_REPOSITORY) + set(CURL_REPOSITORY https://github.com/curl/curl.git) + endif() + + # Fetch curl source code from official repository + FetchContent_Declare(curl + GIT_REPOSITORY ${CURL_REPOSITORY}) + + FetchContent_GetProperties(curl) + if(NOT curl_POPULATED) + FetchContent_Populate(curl) + add_subdirectory(${curl_SOURCE_DIR} ${curl_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() + + if(NOT TARGET CURL::libcurl) + add_library(CURL::libcurl ALIAS libcurl) + endif() + endif() +endif() + +if(PROXYRES_EXECUTE) + target_compile_definitions(proxyres PUBLIC PROXYRES_EXECUTE=1) + + if(TARGET CURL::libcurl) + target_compile_definitions(proxyres PUBLIC HAVE_CURL=1) + target_sources(proxyres PRIVATE fetch_curl.c) + target_link_libraries(proxyres CURL::libcurl) + else() + target_sources(proxyres PRIVATE fetch_posix.c) + endif() +endif() + +if(WIN32) + target_link_libraries(proxyres dhcpcsvc.lib iphlpapi.lib wininet winhttp ws2_32) +elseif(APPLE) + find_library(CFNETWORK_LIBRARY CFNetwork) + target_link_libraries(proxyres ${CFNETWORK_LIBRARY}) + + find_library(COREFOUNDATION_LIBRARY CoreFoundation) + target_link_libraries(proxyres ${COREFOUNDATION_LIBRARY}) + + find_library(SYSTEMCONFIGURATION_LIBRARY SystemConfiguration) + target_link_libraries(proxyres ${SYSTEMCONFIGURATION_LIBRARY}) + + find_package(Threads REQUIRED) + target_compile_definitions(proxyres PRIVATE HAVE_PTHREADS) + target_link_libraries(proxyres ${CMAKE_THREAD_LIBS_INIT}) + + set_target_properties(proxyres PROPERTIES LINK_FLAGS -Wl,-F/Library/Frameworks) +elseif(UNIX) + find_package(PkgConfig REQUIRED) + + pkg_check_modules(deps REQUIRED IMPORTED_TARGET glib-2.0) + target_link_libraries(proxyres PkgConfig::deps) + + pkg_check_modules(GConf REQUIRED gconf-2.0) + # Don't link libraries at compile time since we dynamically load them at runtime + target_include_directories(proxyres PRIVATE ${GConf_INCLUDE_DIRS}) + + pkg_search_module(JSCoreGTK REQUIRED javascriptcoregtk-4.0 javascriptcoregtk-3.0 javascriptcoregtk-1.0) + # Don't link libraries at compile time since we dynamically load them at runtime + target_include_directories(proxyres PRIVATE ${JSCoreGTK_INCLUDE_DIRS}) + + find_package(Threads REQUIRED) + target_compile_definitions(proxyres PRIVATE HAVE_PTHREADS) + target_link_libraries(proxyres ${CMAKE_THREAD_LIBS_INIT}) + + target_link_libraries(proxyres ${CMAKE_DL_LIBS}) +endif() + +if(PROXYRES_BUILD_CLI OR PROXYRES_BUILD_TESTS) + # Should be enabled in source root CMakeLists.txt + enable_testing() + + add_subdirectory(test) +endif() 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..c825c9b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,113 +1,113 @@ -if(PROXYRES_BUILD_CLI) - set(PROXYCLI_SRCS proxycli.c) - - if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") - set(PROXYCLI_ASSETS - uwp/Square44x44Logo.png - uwp/Square150x150Logo.png - uwp/StoreLogo.png - uwp/Package.appxmanifest) - - set_source_files_properties(${PROXYCLI_ASSETS} PROPERTIES - VS_DEPLOYMENT_CONTENT 1 - VS_DEPLOYMENT_LOCATION "Assets") - endif() - - add_executable(proxycli ${PROXYCLI_SRCS} ${PROXYCLI_ASSETS}) - target_link_libraries(proxycli PRIVATE proxyres) - - if(TARGET CURL::libcurl) - add_executable(curl_proxyres curl_proxyres.c) - target_link_libraries(curl_proxyres PRIVATE proxyres CURL::libcurl) - - add_test(NAME curl_proxyres-help - COMMAND curl_proxyres --help) - add_test(NAME curl_proxyres-google - COMMAND curl_proxyres http://google.com/) - endif() - - if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") - return() - endif() - - add_test(NAME proxycli-help - COMMAND proxycli --help) - add_test(NAME proxycli-config - COMMAND proxycli config) - add_test(NAME proxycli-resolve-google - COMMAND proxycli resolve https://simple.com/ https://google.com/) - if(PROXYRES_EXECUTE OR (UNIX AND NOT APPLE)) - add_test(NAME proxycli-execute-google - COMMAND proxycli execute ${CMAKE_SOURCE_DIR}/test/pac.js https://simple.com/ https://google.com/) - endif() -endif() - -if(PROXYRES_BUILD_TESTS) - if(NOT TARGET GTest::GTest) - find_package(GTest QUIET) - endif() - - if(NOT TARGET GTest::GTest) - include(FetchContent) - - # Prevent overriding the parent project's compiler/linker settings for Windows - set(gtest_force_shared_crt ON CACHE BOOL - "Use shared (DLL) run-time lib even when Google Test is built as static lib." FORCE) - - # Allow specifying alternative Google test repository - if(NOT DEFINED GTEST_REPOSITORY) - set(GTEST_REPOSITORY https://github.com/google/googletest.git) - endif() - if(NOT DEFINED GTEST_TAG) - # Use older version of Google test to support older versions of GCC - if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 5.3) - set(GTEST_TAG release-1.10.0) - else() - set(GTEST_TAG release-1.11.0) - endif() - endif() - - # Fetch Google test source code from official repository - FetchContent_Declare(googletest - GIT_REPOSITORY ${GTEST_REPOSITORY} - GIT_TAG ${GTEST_TAG}) - - FetchContent_GetProperties(googletest) - if(NOT googletest_POPULATED) - FetchContent_Populate(googletest) - add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL) - endif() - - add_library(GTest::GTest ALIAS gtest) - add_library(GTest::Main ALIAS gtest_main) - endif() - - set(TEST_SRCS - test_config.cc - test_main.cc - test_threadpool.cc - test_util.cc) - if(WIN32) - list(APPEND TEST_SRCS - test_util_win.cc) - elseif(UNIX AND NOT APPLE) - list(APPEND TEST_SRCS - test_util_linux.cc) - 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() - - add_executable(gtest_proxyres ${TEST_SRCS}) - target_link_libraries(gtest_proxyres PRIVATE proxyres GTest::GTest) - target_include_directories(gtest_proxyres PRIVATE - ${CMAKE_SOURCE_DIR} - ${CMAKE_SOURCE_DIR}/include/proxyres) - - add_test(NAME gtest_proxyres COMMAND gtest_proxyres) -endif() +if(PROXYRES_BUILD_CLI) + set(PROXYCLI_SRCS proxycli.c) + + if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") + set(PROXYCLI_ASSETS + uwp/Square44x44Logo.png + uwp/Square150x150Logo.png + uwp/StoreLogo.png + uwp/Package.appxmanifest) + + set_source_files_properties(${PROXYCLI_ASSETS} PROPERTIES + VS_DEPLOYMENT_CONTENT 1 + VS_DEPLOYMENT_LOCATION "Assets") + endif() + + add_executable(proxycli ${PROXYCLI_SRCS} ${PROXYCLI_ASSETS}) + target_link_libraries(proxycli PRIVATE proxyres) + + if(TARGET CURL::libcurl) + add_executable(curl_proxyres curl_proxyres.c) + target_link_libraries(curl_proxyres PRIVATE proxyres CURL::libcurl) + + add_test(NAME curl_proxyres-help + COMMAND curl_proxyres --help) + add_test(NAME curl_proxyres-google + COMMAND curl_proxyres http://google.com/) + endif() + + if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore") + return() + endif() + + add_test(NAME proxycli-help + COMMAND proxycli --help) + add_test(NAME proxycli-config + COMMAND proxycli config) + add_test(NAME proxycli-resolve-google + COMMAND proxycli resolve https://simple.com/ https://google.com/) + if(PROXYRES_EXECUTE OR (UNIX AND NOT APPLE)) + add_test(NAME proxycli-execute-google + COMMAND proxycli execute ${CMAKE_SOURCE_DIR}/test/pac.js https://simple.com/ https://google.com/) + endif() +endif() + +if(PROXYRES_BUILD_TESTS) + if(NOT TARGET GTest::GTest) + find_package(GTest QUIET) + endif() + + if(NOT TARGET GTest::GTest) + include(FetchContent) + + # Prevent overriding the parent project's compiler/linker settings for Windows + set(gtest_force_shared_crt ON CACHE BOOL + "Use shared (DLL) run-time lib even when Google Test is built as static lib." FORCE) + + # Allow specifying alternative Google test repository + if(NOT DEFINED GTEST_REPOSITORY) + set(GTEST_REPOSITORY https://github.com/google/googletest.git) + endif() + if(NOT DEFINED GTEST_TAG) + # Use older version of Google test to support older versions of GCC + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 5.3) + set(GTEST_TAG release-1.10.0) + else() + set(GTEST_TAG release-1.11.0) + endif() + endif() + + # Fetch Google test source code from official repository + FetchContent_Declare(googletest + GIT_REPOSITORY ${GTEST_REPOSITORY} + GIT_TAG ${GTEST_TAG}) + + FetchContent_GetProperties(googletest) + if(NOT googletest_POPULATED) + FetchContent_Populate(googletest) + add_subdirectory(${googletest_SOURCE_DIR} ${googletest_BINARY_DIR} EXCLUDE_FROM_ALL) + endif() + + add_library(GTest::GTest ALIAS gtest) + add_library(GTest::Main ALIAS gtest_main) + endif() + + 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) + list(APPEND TEST_SRCS + test_util_win.cc) + elseif(UNIX AND NOT APPLE) + list(APPEND TEST_SRCS + test_util_linux.cc) + endif() + if(PROXYRES_EXECUTE) + list(APPEND TEST_SRCS + test_execute.cc + test_fetch.cc + test_wpad.cc) + endif() + + add_executable(gtest_proxyres ${TEST_SRCS}) + target_link_libraries(gtest_proxyres PRIVATE proxyres GTest::GTest) + target_include_directories(gtest_proxyres PRIVATE + ${CMAKE_SOURCE_DIR} + ${CMAKE_SOURCE_DIR}/include/proxyres) + + add_test(NAME gtest_proxyres COMMAND gtest_proxyres) +endif() diff --git a/test/test_execute.cc b/test/test_execute.cc index 43af4da..99c28be 100644 --- a/test/test_execute.cc +++ b/test/test_execute.cc @@ -1,153 +1,149 @@ -#include -#include -#include -#include - -#include - -#include "execute.h" -#include "net_util.h" -#include "util.h" - -struct execute_param { - const char *url; - const char *expected; - - friend std::ostream &operator<<(std::ostream &os, const execute_param ¶m) { - return os << "url: " << param.url; - } -}; - -static const char *script = R"( -function FindProxyForURL(url, host) { - if (host == "myip") { - return "PROXY " + myIpAddress() + ":80"; - } - if (host == "myipex") { - var addresses = myIpAddressEx().split(';'); - var proxies = ""; - for (var i = 0; i < addresses.length; i++) { - proxies += "PROXY " + addresses[i] + ":80"; - if (i != addresses.length - 1) { - proxies += ";"; - } - } - return proxies; - } - if (host == "localhost") { - return "PROXY " + dnsResolve(host) + ":80"; - } - if (host == "::1") { - return "PROXY " + dnsResolveEx(host) + ":80"; - } - if (host == "127.0.0.1") { - return "PROXY localhost:30"; - } - if (isPlainHostName(host)) { - return "HTTP plain"; - } - if (host == "simple.com") { - return "PROXY no-such-proxy:80"; - } - if (shExpMatch(url, '*microsoft.com/*')) { - return "PROXY microsoft.com:80"; - } - return "DIRECT"; -})"; - -constexpr execute_param execute_tests[] = {{"your-pc", "HTTP plain"}, - {"127.0.0.1", "PROXY localhost:30"}, - {"localhost", "PROXY 127.0.0.1:80"}, - {"::1", "PROXY ::1:80"}, - {"myip", NULL}, - {"myipex", NULL}, - {"http://127.0.0.1/", "PROXY localhost:30"}, - {"http://simple.com/", "PROXY no-such-proxy:80"}, - {"http://example2.com/", "DIRECT"}, - {"http://microsoft.com/test", "PROXY microsoft.com:80"}, - {"file:///c:/test", NULL}, - {"file:////home/test", NULL}}; - -class execute : public ::testing::TestWithParam {}; - -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); - if (!address) - return NULL; - - size_t max_expected = strlen(address) + 16; - char *expected = (char *)calloc(max_expected, sizeof(char)); - if (!expected) - return NULL; - - snprintf(expected, max_expected, "PROXY %s:80", address); - free(address); - return expected; -} - -// 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); - if (!addresses) - return NULL; - - int32_t address_count = str_count_chr(addresses, ';') + 1; - size_t max_expected = (address_count + 16) * HOST_MAX; - char *expected = (char *)calloc(max_expected, sizeof(char)); - if (!expected) - return NULL; - - // Enumerate each address and append expected proxy to string - const char *addressesp = addresses; - while (addressesp) { - char *address = str_sep_dup(&addressesp, ";"); - size_t expected_len = strlen(expected); - if (expected_len) { - strncat(expected, ";", max_expected - expected_len); - expected_len++; - } - snprintf(expected + expected_len, max_expected - expected_len, "PROXY %s:80", address); - free(address); - } - return expected; -} - -TEST_P(execute, get_proxies_for_url) { - const auto ¶m = GetParam(); - void *proxy_execute = proxy_execute_create(); - EXPECT_NE(proxy_execute, nullptr); - if (proxy_execute) { - EXPECT_TRUE(proxy_execute_get_proxies_for_url(proxy_execute, script, param.url)); - EXPECT_EQ(proxy_execute_get_error(proxy_execute), 0); - const char *list = proxy_execute_get_list(proxy_execute); - EXPECT_NE(list, nullptr); - if (list) { - if (!param.expected) { - // Test expected result from myIpAddress() and myIpAddressEx() - if (strcmp(param.url, "myip") == 0) { - char *expected = expected_my_ip(); - EXPECT_NE(expected, nullptr); - if (expected) - EXPECT_STREQ(list, expected); - free(expected); - } else if (strcmp(param.url, "myipex") == 0) { - char *expected = expected_my_ip_ex(); - EXPECT_NE(expected, nullptr); - if (expected) - EXPECT_STREQ(list, expected); - free(expected); - } - } else { - EXPECT_STREQ(list, param.expected); - } - } - proxy_execute_delete(&proxy_execute); - } -} +#include +#include +#include +#include + +#include + +#include "execute.h" +#include "net_util.h" +#include "util.h" + +struct execute_param { + const char *url; + const char *expected; + + friend std::ostream &operator<<(std::ostream &os, const execute_param ¶m) { + return os << "url: " << param.url; + } +}; + +static const char *script = R"( +function FindProxyForURL(url, host) { + if (host == "myip") { + return "PROXY " + myIpAddress() + ":80"; + } + if (host == "myipex") { + var addresses = myIpAddressEx().split(';'); + var proxies = ""; + for (var i = 0; i < addresses.length; i++) { + proxies += "PROXY " + addresses[i] + ":80"; + if (i != addresses.length - 1) { + proxies += ";"; + } + } + return proxies; + } + if (host == "localhost") { + return "PROXY " + dnsResolve(host) + ":80"; + } + if (host == "::1") { + return "PROXY " + dnsResolveEx(host) + ":80"; + } + if (host == "127.0.0.1") { + return "PROXY localhost:30"; + } + if (isPlainHostName(host)) { + return "HTTP plain"; + } + if (host == "simple.com") { + return "PROXY no-such-proxy:80"; + } + if (shExpMatch(url, '*microsoft.com/*')) { + return "PROXY microsoft.com:80"; + } + return "DIRECT"; +})"; + +constexpr execute_param execute_tests[] = {{"your-pc", "HTTP plain"}, + {"127.0.0.1", "PROXY localhost:30"}, + {"localhost", "PROXY 127.0.0.1:80"}, + {"::1", "PROXY ::1:80"}, + {"myip", NULL}, + {"myipex", NULL}, + {"http://127.0.0.1/", "PROXY localhost:30"}, + {"http://simple.com/", "PROXY no-such-proxy:80"}, + {"http://example2.com/", "DIRECT"}, + {"http://microsoft.com/test", "PROXY microsoft.com:80"}, + {"file:///c:/test", NULL}, + {"file:////home/test", NULL}}; + +class execute : public ::testing::TestWithParam {}; + +INSTANTIATE_TEST_SUITE_P(execute, execute, testing::ValuesIn(execute_tests)); + +// Construct expected output for myip test case +static char *expected_my_ip(void) { + char *address = my_ip_address(); + if (!address) + return NULL; + + size_t max_expected = strlen(address) + 16; + char *expected = (char *)calloc(max_expected, sizeof(char)); + if (!expected) + return NULL; + + snprintf(expected, max_expected, "PROXY %s:80", address); + free(address); + return expected; +} + +// Construct expected output for myipex test case +static char *expected_my_ip_ex(void) { + char *addresses = my_ip_address_ex(); + if (!addresses) + return NULL; + + int32_t address_count = str_count_chr(addresses, ';') + 1; + size_t max_expected = (address_count + 16) * HOST_MAX; + char *expected = (char *)calloc(max_expected, sizeof(char)); + if (!expected) + return NULL; + + // Enumerate each address and append expected proxy to string + const char *addressesp = addresses; + while (addressesp) { + char *address = str_sep_dup(&addressesp, ";"); + size_t expected_len = strlen(expected); + if (expected_len) { + strncat(expected, ";", max_expected - expected_len); + expected_len++; + } + snprintf(expected + expected_len, max_expected - expected_len, "PROXY %s:80", address); + free(address); + } + return expected; +} + +TEST_P(execute, get_proxies_for_url) { + const auto ¶m = GetParam(); + void *proxy_execute = proxy_execute_create(); + EXPECT_NE(proxy_execute, nullptr); + if (proxy_execute) { + EXPECT_TRUE(proxy_execute_get_proxies_for_url(proxy_execute, script, param.url)); + EXPECT_EQ(proxy_execute_get_error(proxy_execute), 0); + const char *list = proxy_execute_get_list(proxy_execute); + EXPECT_NE(list, nullptr); + if (list) { + if (!param.expected) { + // Test expected result from myIpAddress() and myIpAddressEx() + if (strcmp(param.url, "myip") == 0) { + char *expected = expected_my_ip(); + EXPECT_NE(expected, nullptr); + if (expected) + EXPECT_STREQ(list, expected); + free(expected); + } else if (strcmp(param.url, "myipex") == 0) { + char *expected = expected_my_ip_ex(); + EXPECT_NE(expected, nullptr); + if (expected) + EXPECT_STREQ(list, expected); + free(expected); + } + } else { + EXPECT_STREQ(list, param.expected); + } + } + proxy_execute_delete(&proxy_execute); + } +} 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); + } +}