From fc09ca1d733becf0693f8c826bf28b78f0c0af59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Mon, 6 Apr 2020 20:57:57 +0200 Subject: [PATCH 01/32] Native implementation --- .../Native/Unix/Common/pal_config.h.in | 1 + .../Native/Unix/System.Native/CMakeLists.txt | 4 + .../Unix/System.Native/pal_networking.c | 171 +++++++++++++++--- .../Unix/System.Native/pal_networking.h | 6 +- src/libraries/Native/Unix/configure.cmake | 15 +- 5 files changed, 168 insertions(+), 29 deletions(-) diff --git a/src/libraries/Native/Unix/Common/pal_config.h.in b/src/libraries/Native/Unix/Common/pal_config.h.in index d177e6606767f..6a0f32519cb33 100644 --- a/src/libraries/Native/Unix/Common/pal_config.h.in +++ b/src/libraries/Native/Unix/Common/pal_config.h.in @@ -9,6 +9,7 @@ #cmakedefine01 HAVE_F_DUPFD_CLOEXEC #cmakedefine01 HAVE_O_CLOEXEC #cmakedefine01 HAVE_GETIFADDRS +#cmakedefine01 HAVE_GETADDRINFO_A #cmakedefine01 HAVE_UTSNAME_DOMAINNAME #cmakedefine01 HAVE_STAT64 #cmakedefine01 HAVE_VFORK diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index 11d3456ec7c3f..bf2e3787607fc 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -62,6 +62,10 @@ if (GEN_SHARED_LIB) endif () endif () install_with_stripped_symbols (System.Native PROGRAMS .) + + if (HAVE_GETADDRINFO_A) + target_link_libraries(System.Native anl) + endif () endif () add_library(System.Native-Static diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index 7faa21b3923ac..9d2cef068789d 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -56,6 +56,10 @@ #if HAVE_LINUX_CAN_H #include #endif +#if HAVE_GETADDRINFO_A +#include +#include +#endif #if HAVE_KQUEUE #if KEVENT_HAS_VOID_UDATA static void* GetKeventUdata(uintptr_t udata) @@ -253,32 +257,14 @@ static int32_t CopySockAddrToIPAddress(sockaddr* addr, sa_family_t family, IPAdd return -1; } -int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entry) +static int32_t GetHostEntries(const uint8_t* address, struct addrinfo* info, HostEntry* entry) { - if (address == NULL || entry == NULL) - { - return GetAddrInfoErrorFlags_EAI_BADARG; - } - int32_t ret = GetAddrInfoErrorFlags_EAI_SUCCESS; - struct addrinfo* info = NULL; #if HAVE_GETIFADDRS struct ifaddrs* addrs = NULL; #endif - // Get all address families and the canonical name - struct addrinfo hint; - memset(&hint, 0, sizeof(struct addrinfo)); - hint.ai_family = AF_UNSPEC; - hint.ai_flags = AI_CANONNAME; - - int result = getaddrinfo((const char*)address, NULL, &hint, &info); - if (result != 0) - { - return ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); - } - entry->CanonicalName = NULL; entry->Aliases = NULL; entry->IPAddressList = NULL; @@ -306,7 +292,8 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entr #if HAVE_GETIFADDRS char name[_POSIX_HOST_NAME_MAX]; - result = gethostname((char*)name, _POSIX_HOST_NAME_MAX); + + int result = gethostname((char*)name, _POSIX_HOST_NAME_MAX); bool includeIPv4Loopback = true; bool includeIPv6Loopback = true; @@ -432,6 +419,136 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entr return ret; } +#if HAVE_GETADDRINFO_A +struct GetAddrInfoAsyncState +{ + struct gaicb gai_request; + struct gaicb* gai_requests; + struct sigevent sigevent; + + const uint8_t* address; + HostEntry* entry; + GetHostEntryForNameCallback callback; +}; + +static void GetHostEntryForNameAsyncComplete(sigval_t context) +{ + struct GetAddrInfoAsyncState* state = (struct GetAddrInfoAsyncState*)context.sival_ptr; + + atomic_thread_fence(memory_order_acquire); + + GetHostEntryForNameCallback callback = state->callback; + + int result = gai_error(&state->gai_request); + HostEntry* entry = state->entry; + + int ret = ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); + + if (result == 0) + { + const uint8_t* address = state->address; + struct addrinfo* info = state->gai_request.ar_result; + + ret = GetHostEntries(address, info, entry); + } + + free(state); + + if (callback != NULL) + { + callback(entry, ret); + } +} +#endif + +static void GetHostEntryForNameCreateHint(struct addrinfo* hint) +{ + // Get all address families and the canonical name + + memset(hint, 0, sizeof(struct addrinfo)); + hint->ai_family = AF_UNSPEC; + hint->ai_flags = AI_CANONNAME; +} + +int32_t SystemNative_PlatformSupportsGetAddrInfoAsync() +{ +#if HAVE_GETADDRINFO_A + return 1; +#else + return 0; +#endif +} + +int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entry) +{ + if (address == NULL || entry == NULL) + { + return GetAddrInfoErrorFlags_EAI_BADARG; + } + + struct addrinfo hint; + GetHostEntryForNameCreateHint(&hint); + + struct addrinfo* info = NULL; + + int result = getaddrinfo((const char*)address, NULL, &hint, &info); + if (result != 0) + { + return ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); + } + + return GetHostEntries(address, info, entry); +} + +int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* entry, GetHostEntryForNameCallback callback) +{ +#if HAVE_GETADDRINFO_A + if (address == NULL) + { + return GetAddrInfoErrorFlags_EAI_BADARG; + } + + struct addrinfo hint; + GetHostEntryForNameCreateHint(&hint); + + struct GetAddrInfoAsyncState* state = malloc(sizeof(struct GetAddrInfoAsyncState)); + + state->gai_request.ar_name = (const char*)address; + state->gai_request.ar_service = NULL; + state->gai_request.ar_request = &hint; + state->gai_request.ar_result = NULL; + state->gai_requests = &state->gai_request; + + state->sigevent.sigev_notify = SIGEV_THREAD; + state->sigevent.sigev_value.sival_ptr = state; + state->sigevent.sigev_notify_function = GetHostEntryForNameAsyncComplete; + + state->address = address; + state->entry = entry; + state->callback = callback; + + atomic_thread_fence(memory_order_release); + + int32_t result = getaddrinfo_a(GAI_NOWAIT, &state->gai_requests, 1, &state->sigevent); + + if (result != 0) + { + free(state); + return ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); + } + + return result; +#else + // Actually not needed, but otherwise the parameters are unused. + assert(address != NULL); + assert(entry != NULL); + assert(callback != NULL); + + // GetHostEntryForNameAsync is not supported on this platform. + return -1; +#endif +} + void SystemNative_FreeHostEntry(HostEntry* entry) { if (entry != NULL) @@ -468,13 +585,13 @@ static inline NativeFlagsType ConvertGetNameInfoFlagsToNative(int32_t flags) } int32_t SystemNative_GetNameInfo(const uint8_t* address, - int32_t addressLength, - int8_t isIPv6, - uint8_t* host, - int32_t hostLength, - uint8_t* service, - int32_t serviceLength, - int32_t flags) + int32_t addressLength, + int8_t isIPv6, + uint8_t* host, + int32_t hostLength, + uint8_t* service, + int32_t serviceLength, + int32_t flags) { assert(address != NULL); assert(addressLength > 0); diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.h b/src/libraries/Native/Unix/System.Native/pal_networking.h index dbc4ebc211066..3b55e950216d8 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.h +++ b/src/libraries/Native/Unix/System.Native/pal_networking.h @@ -310,10 +310,14 @@ typedef struct uint32_t Padding; // Pad out to 8-byte alignment } SocketEvent; +PALEXPORT int32_t SystemNative_PlatformSupportsGetAddrInfoAsync(void); + PALEXPORT int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entry); -PALEXPORT void SystemNative_FreeHostEntry(HostEntry* entry); +typedef void (*GetHostEntryForNameCallback)(HostEntry* entry, int status); +PALEXPORT int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* entry, GetHostEntryForNameCallback callback); +PALEXPORT void SystemNative_FreeHostEntry(HostEntry* entry); PALEXPORT int32_t SystemNative_GetNameInfo(const uint8_t* address, int32_t addressLength, diff --git a/src/libraries/Native/Unix/configure.cmake b/src/libraries/Native/Unix/configure.cmake index c97d318fee90a..c6c574d51019e 100644 --- a/src/libraries/Native/Unix/configure.cmake +++ b/src/libraries/Native/Unix/configure.cmake @@ -6,7 +6,7 @@ include(CheckPrototypeDefinition) include(CheckStructHasMember) include(CheckSymbolExists) include(CheckTypeSize) - +include(CMakePushCheckState) if (CLR_CMAKE_TARGET_ANDROID) set(PAL_UNIX_NAME \"ANDROID\") @@ -822,6 +822,19 @@ check_symbol_exists( HAVE_INOTIFY_RM_WATCH) set (CMAKE_REQUIRED_LIBRARIES ${PREVIOUS_CMAKE_REQUIRED_LIBRARIES}) +if (CLR_CMAKE_TARGET_LINUX) + cmake_push_check_state(RESET) + set (CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") + set (CMAKE_REQUIRED_LIBRARIES "-lanl") + + check_symbol_exists( + getaddrinfo_a + netdb.h + HAVE_GETADDRINFO_A) + + cmake_pop_check_state() +endif () + set (HAVE_INOTIFY 0) if (HAVE_INOTIFY_INIT AND HAVE_INOTIFY_ADD_WATCH AND HAVE_INOTIFY_RM_WATCH) set (HAVE_INOTIFY 1) From 8c955bc4bd1d0dbe4b2823f4327ad52bf9856fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Mon, 6 Apr 2020 20:58:31 +0200 Subject: [PATCH 02/32] Native tests --- src/libraries/Native/Unix/CMakeLists.txt | 2 + .../Native/Unix/System.Native/CMakeLists.txt | 31 +++++++ .../Native/Unix/System.Native/tests.c | 86 +++++++++++++++++++ 3 files changed, 119 insertions(+) create mode 100644 src/libraries/Native/Unix/System.Native/tests.c diff --git a/src/libraries/Native/Unix/CMakeLists.txt b/src/libraries/Native/Unix/CMakeLists.txt index b3c9613a0e10b..0769eaf19d73c 100644 --- a/src/libraries/Native/Unix/CMakeLists.txt +++ b/src/libraries/Native/Unix/CMakeLists.txt @@ -210,3 +210,5 @@ endif() if(CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_IOS) add_subdirectory(System.Security.Cryptography.Native.Apple) endif() + +enable_testing() diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index bf2e3787607fc..aae0a19eef445 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -80,3 +80,34 @@ endif () set_target_properties(System.Native-Static PROPERTIES OUTPUT_NAME System.Native CLEAN_DIRECT_OUTPUT 1) install (TARGETS System.Native-Static DESTINATION .) + +if (HAVE_GETADDRINFO_A) + add_executable(tests + tests.c + ) + + target_link_libraries(tests + System.Native + pthread + ) + + enable_testing() + + add_test(no_args tests) + set_tests_properties(no_args PROPERTIES WILL_FAIL TRUE) + set_tests_properties(no_args PROPERTIES TIMEOUT 1) + + add_test(hostname_local tests localhost) + set_tests_properties(hostname_local PROPERTIES TIMEOUT 1) + + add_test(hostname_ok tests google.at) + set_tests_properties(hostname_ok PROPERTIES TIMEOUT 1) + + add_test(hostname_invalid tests abc.test01) + set_tests_properties(hostname_invalid PROPERTIES WILL_FAIL TRUE) + set_tests_properties(hostname_invalid PROPERTIES TIMEOUT 1) + + add_test(hostname_null tests null) + set_tests_properties(hostname_null PROPERTIES WILL_FAIL TRUE) + set_tests_properties(hostname_null PROPERTIES TIMEOUT 1) +endif () diff --git a/src/libraries/Native/Unix/System.Native/tests.c b/src/libraries/Native/Unix/System.Native/tests.c new file mode 100644 index 0000000000000..e555cadd9c143 --- /dev/null +++ b/src/libraries/Native/Unix/System.Native/tests.c @@ -0,0 +1,86 @@ +#include "pal_networking.h" + +#include +#include +#include +#include +#include +#include +#include + +struct state +{ + HostEntry entry; + char* hostName; + int errorCode; + sem_t semaphore; +}; + +static void callback(HostEntry* entry, int errorCode) +{ + printf("(%lu) handler: enter, errorCode: %d\n", pthread_self(), errorCode); + + struct state* state = (struct state*)entry; + + if (errorCode == 0) + { + printf("(%lu) callback: # of addresses = %d\n", pthread_self(), entry->IPAddressCount); + for (int i = 0; i < entry->IPAddressCount; ++i) + { + IPAddress ipAddress = entry->IPAddressList[i]; + + char buffer[256]; + inet_ntop(ipAddress.IsIPv6 ? AF_INET6 : AF_INET, ipAddress.Address, buffer, sizeof(buffer)); + printf("(%lu) ip for %s: %s\n", pthread_self(), entry->CanonicalName, buffer); + } + } + + state->errorCode = errorCode; + + sem_post(&state->semaphore); +} + +int main(int argc, char** argv) +{ + if (argc != 2) + { + printf("hostname must be given as argument\n"); + return EXIT_FAILURE; + } + + if (!SystemNative_PlatformSupportsGetAddrInfoAsync()) + { + printf("platform support not available\n"); + return EXIT_FAILURE; + } + + printf("platform support available\n"); + + char* hostName = argv[1]; + printf("(%lu) hostname: %s\n", pthread_self(), hostName); + + if (strcmp(hostName, "null")==0) + { + hostName = NULL; + } + + struct state state; + sem_init(&state.semaphore, 0, 0); + + int error = SystemNative_GetHostEntryForNameAsync((uint8_t*)hostName, &state.entry, callback); + + if (error != 0) + { + printf("(%lu) OS call failed with error %d\n", pthread_self(), error); + return EXIT_FAILURE; + } + + printf("(%lu) main: waiting for semaphore\n", pthread_self()); + + sem_wait(&state.semaphore); + sem_destroy(&state.semaphore); + + printf("(%lu) main: exit, errorCode: %d\n", pthread_self(), state.errorCode); + + return state.errorCode; +} From cf13c708e238abf9c3abe61ac225787d7445a8a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Mon, 6 Apr 2020 20:59:05 +0200 Subject: [PATCH 03/32] Managed implementation --- .../Unix/System.Native/Interop.GetHostName.cs | 28 +- .../Unix/System.Native/Interop.HostEntry.cs | 9 +- .../src/System/Net/Dns.cs | 2 +- .../src/System/Net/NameResolutionPal.Unix.cs | 331 ++++++++++++++---- 4 files changed, 282 insertions(+), 88 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetHostName.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetHostName.cs index f2555b0fc8276..065f76cd0d84d 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetHostName.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetHostName.cs @@ -2,8 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Diagnostics; using System.Runtime.InteropServices; internal static partial class Interop @@ -11,30 +9,6 @@ internal static partial class Interop internal static partial class Sys { [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetHostName", SetLastError = true)] - private static extern unsafe int GetHostName(byte* name, int nameLength); - - internal static unsafe string GetHostName() - { - const int HOST_NAME_MAX = 255; - const int ArrLength = HOST_NAME_MAX + 1; - - byte* name = stackalloc byte[ArrLength]; - int err = GetHostName(name, ArrLength); - if (err != 0) - { - // This should never happen. According to the man page, - // the only possible errno for gethostname is ENAMETOOLONG, - // which should only happen if the buffer we supply isn't big - // enough, and we're using a buffer size that the man page - // says is the max for POSIX (and larger than the max for Linux). - Debug.Fail($"GetHostName failed with error {err}"); - throw new InvalidOperationException($"{nameof(GetHostName)}: {err}"); - } - - // If the hostname is truncated, it is unspecified whether the returned buffer includes a terminating null byte. - name[ArrLength - 1] = 0; - - return Marshal.PtrToStringAnsi((IntPtr)name)!; - } + internal static extern unsafe int GetHostName(byte* name, int nameLength); } } diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs index 4f36e43edb17f..7a1c49b524183 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Runtime.InteropServices; internal static partial class Interop @@ -33,9 +32,17 @@ internal unsafe struct HostEntry internal int IPAddressCount; // Number of IP addresses in the list } + internal unsafe delegate void GetHostEntryForNameCallback(HostEntry* entry, int status); + + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PlatformSupportsGetAddrInfoAsync")] + internal static extern bool PlatformSupportsGetAddrInfoAsync(); + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetHostEntryForName")] internal static extern unsafe int GetHostEntryForName(string address, HostEntry* entry); + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetHostEntryForNameAsync")] + internal static extern unsafe int GetHostEntryForNameAsync(string address, HostEntry* entry, GetHostEntryForNameCallback callback); + [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FreeHostEntry")] internal static extern unsafe void FreeHostEntry(HostEntry* entry); } diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs b/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs index 5a18dd685aaa4..646725030e094 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs @@ -490,7 +490,7 @@ private static void ValidateHostName(string hostName) const int MaxHostName = 255; if (hostName.Length > MaxHostName || - (hostName.Length == MaxHostName && hostName[MaxHostName - 1] != '.')) // If 255 chars, the last one must be a dot. + (hostName.Length == MaxHostName && hostName[MaxHostName - 1] != '.')) // If 255 chars, the last one must be a dot. { throw new ArgumentOutOfRangeException(nameof(hostName), SR.Format(SR.net_toolong, nameof(hostName), MaxHostName.ToString(NumberFormatInfo.CurrentInfo))); diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index d76240e706b10..1551d687a52af 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -2,24 +2,206 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; using System.Diagnostics; -using System.Net.Internals; using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; -using System.Text; +using System.Threading; using System.Threading.Tasks; namespace System.Net { internal static partial class NameResolutionPal { - public const bool SupportsGetAddrInfoAsync = false; + private static volatile bool s_initialized; + private static readonly object s_initializedLock = new object(); - public static void EnsureSocketsAreInitialized() { } // No-op for Unix + private static readonly unsafe Interop.Sys.GetHostEntryForNameCallback s_getHostEntryForNameCallback = GetHostEntryForNameCallback; + private static bool s_platformSupportsGetAddrInfoAsync; - internal static Task GetAddrInfoAsync(string hostName, bool justAddresses) => - throw new NotSupportedException(); + public static void EnsureSocketsAreInitialized() + { + // Actually a No-op for Unix as sockets don't need a startup, but used + // to check whether async address resolution is available or not. + + if (!s_initialized) + { + Initialize(); + } + + static void Initialize() + { + lock (s_initializedLock) + { + if (!s_initialized) + { + s_platformSupportsGetAddrInfoAsync = Interop.Sys.PlatformSupportsGetAddrInfoAsync(); + s_initialized = true; + } + } + } + } + + public static bool SupportsGetAddrInfoAsync + { + get + { + EnsureSocketsAreInitialized(); + return s_platformSupportsGetAddrInfoAsync; + } + } + + public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, out string? hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode) + { + if (name == "") + { + // To match documented behavior on Windows, if an empty string is passed in, use the local host's name. + name = Dns.GetHostName(); + } + + Interop.Sys.HostEntry entry; + int result = Interop.Sys.GetHostEntryForName(name, &entry); + if (result != 0) + { + nativeErrorCode = result; + hostName = name; + aliases = Array.Empty(); + addresses = Array.Empty(); + return GetSocketErrorForNativeError(result); + } + + ParseHostEntry(entry, justAddresses, out hostName, out aliases, out addresses); + nativeErrorCode = 0; + return SocketError.Success; + } + + public static unsafe string? TryGetNameInfo(IPAddress addr, out SocketError socketError, out int nativeErrorCode) + { + byte* buffer = stackalloc byte[Interop.Sys.NI_MAXHOST + 1 /*for null*/]; + + byte isIPv6; + int rawAddressLength; + if (addr.AddressFamily == AddressFamily.InterNetwork) + { + isIPv6 = 0; + rawAddressLength = IPAddressParserStatics.IPv4AddressBytes; + } + else + { + isIPv6 = 1; + rawAddressLength = IPAddressParserStatics.IPv6AddressBytes; + } + + byte* rawAddress = stackalloc byte[rawAddressLength]; + addr.TryWriteBytes(new Span(rawAddress, rawAddressLength), out int bytesWritten); + Debug.Assert(bytesWritten == rawAddressLength); + + int error = Interop.Sys.GetNameInfo( + rawAddress, + (uint)rawAddressLength, + isIPv6, + buffer, + Interop.Sys.NI_MAXHOST, + null, + 0, + Interop.Sys.GetNameInfoFlags.NI_NAMEREQD); + + socketError = GetSocketErrorForNativeError(error); + nativeErrorCode = error; + return socketError == SocketError.Success ? Marshal.PtrToStringAnsi((IntPtr)buffer) : null; + } + + public static unsafe string GetHostName() + { + // We do not cache the result in case the hostname changes. + + const int HOST_NAME_MAX = 255; + const int ArrLength = HOST_NAME_MAX + 1; + + byte* name = stackalloc byte[ArrLength]; + int err = Interop.Sys.GetHostName(name, ArrLength); + + if (err != 0) + { + // This should never happen. According to the man page, + // the only possible errno for gethostname is ENAMETOOLONG, + // which should only happen if the buffer we supply isn't big + // enough, and we're using a buffer size that the man page + // says is the max for POSIX (and larger than the max for Linux). + Debug.Fail($"GetHostName failed with error {err}"); + throw new InvalidOperationException($"{nameof(GetHostName)}: {err}"); + } + + // If the hostname is truncated, it is unspecified whether the returned buffer includes a terminating null byte. + name[ArrLength - 1] = 0; + + return new string((sbyte*)name); + } + + public static unsafe Task GetAddrInfoAsync(string hostName, bool justAddresses) + { + GetHostEntryForNameContext* context = GetHostEntryForNameContext.AllocateContext(); + + GetHostEntryForNameState state; + try + { + state = new GetHostEntryForNameState(hostName, justAddresses); + context->State = state.CreateHandle(); + } + catch + { + GetHostEntryForNameContext.FreeContext(context); + throw; + } + + int errorCode = Interop.Sys.GetHostEntryForNameAsync(hostName, &context->Result, s_getHostEntryForNameCallback); + + if (errorCode != 0) + { + ProcessResult(GetSocketErrorForNativeError(errorCode), context); + } + + return state.Task; + } + + private static unsafe void GetHostEntryForNameCallback(Interop.Sys.HostEntry* entry, int error) + { + // Can be casted directly to GetHostEntryForNameContext* because the HostEntry is its first field + GetHostEntryForNameContext* context = (GetHostEntryForNameContext*)entry; + + ProcessResult(GetSocketErrorForNativeError(error), context); + } + + private static unsafe void ProcessResult(SocketError errorCode, GetHostEntryForNameContext* context) + { + try + { + GetHostEntryForNameState state = GetHostEntryForNameState.FromHandleAndFree(context->State); + + if (errorCode == SocketError.Success) + { + ParseHostEntry(context->Result, state.JustAddresses, out string? hostName, out string[] aliases, out IPAddress[] addresses); + + state.SetResult(state.JustAddresses + ? (object)addresses + : new IPHostEntry + { + HostName = hostName ?? state.HostName, + Aliases = aliases, + AddressList = addresses + }); + } + else + { + state.SetResult(ExceptionDispatchInfo.SetCurrentStackTrace(new SocketException((int)errorCode))); + } + } + finally + { + GetHostEntryForNameContext.FreeContext(context); + } + } private static SocketError GetSocketErrorForNativeError(int error) { @@ -50,9 +232,9 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool { try { - hostName = !justAddresses && hostEntry.CanonicalName != null ? - Marshal.PtrToStringAnsi((IntPtr)hostEntry.CanonicalName) : - null; + hostName = !justAddresses && hostEntry.CanonicalName != null + ? new string((sbyte*)hostEntry.CanonicalName) + : null; IPAddress[] localAddresses; if (hostEntry.IPAddressCount == 0) @@ -62,13 +244,13 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool else { // getaddrinfo returns multiple entries per address, for each socket type (datagram, stream, etc.). - // Our callers expect just one entry for each address. So we need to deduplicate the results. + // Our callers expect just one entry for each address. So we need to deduplicate the results. // It's important to keep the addresses in order, since they are returned in the order in which // connections should be attempted. // // We assume that the list returned by getaddrinfo is relatively short; after all, the intent is that // the caller may need to attempt to contact every address in the list before giving up on a connection - // attempt. So an O(N^2) algorithm should be fine here. Keep in mind that any "better" algorithm + // attempt. So an O(N^2) algorithm should be fine here. Keep in mind that any "better" algorithm // is likely to involve extra allocations, hashing, etc., and so will probably be more expensive than // this one in the typical (short list) case. @@ -105,7 +287,7 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool localAliases = new string[numAliases]; for (int i = 0; i < localAliases.Length; i++) { - localAliases[i] = Marshal.PtrToStringAnsi((IntPtr)hostEntry.Aliases[i])!; + localAliases[i] = new string((sbyte*)hostEntry.Aliases[i]); } } } @@ -119,66 +301,97 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool } } - public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, out string? hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode) + private sealed class GetHostEntryForNameState : IThreadPoolWorkItem { - if (name == "") - { - // To match documented behavior on Windows, if an empty string is passed in, use the local host's name. - name = Dns.GetHostName(); - } + private AsyncTaskMethodBuilder _ipAddressArrayBuilder; + private AsyncTaskMethodBuilder _ipHostEntryBuilder; + private object? _result; - Interop.Sys.HostEntry entry; - int result = Interop.Sys.GetHostEntryForName(name, &entry); - if (result != 0) + public GetHostEntryForNameState(string hostName, bool justAddresses) { - nativeErrorCode = result; - hostName = name; - aliases = Array.Empty(); - addresses = Array.Empty(); - return GetSocketErrorForNativeError(result); + HostName = hostName; + JustAddresses = justAddresses; + + if (justAddresses) + { + _ipAddressArrayBuilder = AsyncTaskMethodBuilder.Create(); + _ = _ipAddressArrayBuilder.Task; // force initialization + } + else + { + _ipHostEntryBuilder = AsyncTaskMethodBuilder.Create(); + _ = _ipHostEntryBuilder.Task; // force initialization + } } - ParseHostEntry(entry, justAddresses, out hostName, out aliases, out addresses); - nativeErrorCode = 0; - return SocketError.Success; - } + public string HostName { get; } + public bool JustAddresses { get; } - public static unsafe string? TryGetNameInfo(IPAddress addr, out SocketError socketError, out int nativeErrorCode) - { - byte* buffer = stackalloc byte[Interop.Sys.NI_MAXHOST + 1 /*for null*/]; + public Task Task => JustAddresses ? (Task)_ipAddressArrayBuilder.Task : _ipHostEntryBuilder.Task; - byte isIPv6; - int rawAddressLength; - if (addr.AddressFamily == AddressFamily.InterNetwork) + public void SetResult(object result) { - isIPv6 = 0; - rawAddressLength = IPAddressParserStatics.IPv4AddressBytes; + // Store the result and then queue this object to the thread pool to actually complete the Tasks, as we + // want to avoid invoking continuations on the OS callback thread. Effectively we're manually + // implementing TaskCreationOptions.RunContinuationsAsynchronously, which we can't use because we're + // using AsyncTaskMethodBuilder, which we're using in order to create either a strongly-typed Task + // or Task without allocating additional objects. + Debug.Assert(result is Exception || result is IPAddress[] || result is IPHostEntry); + _result = result; + ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false); } - else + + void IThreadPoolWorkItem.Execute() { - isIPv6 = 1; - rawAddressLength = IPAddressParserStatics.IPv6AddressBytes; + if (JustAddresses) + { + if (_result is Exception e) + { + _ipAddressArrayBuilder.SetException(e); + } + else + { + _ipAddressArrayBuilder.SetResult((IPAddress[])_result!); + } + } + else + { + if (_result is Exception e) + { + _ipHostEntryBuilder.SetException(e); + } + else + { + _ipHostEntryBuilder.SetResult((IPHostEntry)_result!); + } + } } - byte* rawAddress = stackalloc byte[rawAddressLength]; - addr.TryWriteBytes(new Span(rawAddress, rawAddressLength), out int bytesWritten); - Debug.Assert(bytesWritten == rawAddressLength); - - int error = Interop.Sys.GetNameInfo( - rawAddress, - (uint)rawAddressLength, - isIPv6, - buffer, - Interop.Sys.NI_MAXHOST, - null, - 0, - Interop.Sys.GetNameInfoFlags.NI_NAMEREQD); + public IntPtr CreateHandle() => GCHandle.ToIntPtr(GCHandle.Alloc(this, GCHandleType.Normal)); - socketError = GetSocketErrorForNativeError(error); - nativeErrorCode = error; - return socketError == SocketError.Success ? Marshal.PtrToStringAnsi((IntPtr)buffer) : null; + public static GetHostEntryForNameState FromHandleAndFree(IntPtr handle) + { + GCHandle gCHandle = GCHandle.FromIntPtr(handle); + var state = (GetHostEntryForNameState)gCHandle.Target!; + gCHandle.Free(); + return state; + } } - public static string GetHostName() => Interop.Sys.GetHostName(); + [StructLayout(LayoutKind.Sequential)] + private unsafe struct GetHostEntryForNameContext + { + public Interop.Sys.HostEntry Result; + public IntPtr State; + + public static GetHostEntryForNameContext* AllocateContext() + { + var context = (GetHostEntryForNameContext*)Marshal.AllocHGlobal(sizeof(GetHostEntryForNameContext)); + *context = default; + return context; + } + + public static void FreeContext(GetHostEntryForNameContext* context) => Marshal.FreeHGlobal((IntPtr)context); + } } } From 4614267d909794899188d28ae05bddcdd53e7b08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Mon, 6 Apr 2020 20:59:25 +0200 Subject: [PATCH 04/32] Managed tests --- .../tests/PalTests/NameResolutionPalTests.cs | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs index ed10320d6bb82..94d7e562b9dbd 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs @@ -5,6 +5,7 @@ using System.IO; using System.Net.Sockets; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -48,6 +49,33 @@ public void TryGetAddrInfo_LocalHost(bool justAddresses) } Assert.NotNull(aliases); Assert.NotNull(addresses); + Assert.True(addresses.Length > 0); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task GetAddrInfoAsync_LocalHost(bool justAddresses) + { + if (!NameResolutionPal.SupportsGetAddrInfoAsync) + { + return; + } + + if (justAddresses) + { + IPAddress[] addresses = await ((Task)NameResolutionPal.GetAddrInfoAsync("localhost", justAddresses)).ConfigureAwait(false); + + Assert.NotNull(addresses); + Assert.True(addresses.Length > 0); + } + else + { + IPHostEntry hostEntry = await ((Task)NameResolutionPal.GetAddrInfoAsync("localhost", justAddresses)).ConfigureAwait(false); + + Assert.NotNull(hostEntry); + Assert.True(hostEntry.AddressList.Length > 0); + } } [Theory] @@ -160,6 +188,40 @@ public void TryGetAddrInfo_ExternalHost(bool justAddresses) Assert.Equal(SocketError.Success, error); Assert.NotNull(aliases); Assert.NotNull(addresses); + Assert.True(addresses.Length > 0); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task GetAddrInfoAsync_ExternalHost(bool justAddresses) + { + if (!NameResolutionPal.SupportsGetAddrInfoAsync) + { + return; + } + + const string hostName = "microsoft.com"; + + if (!NameResolutionPal.SupportsGetAddrInfoAsync) + { + return; + } + + if (justAddresses) + { + IPAddress[] addresses = await ((Task)NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses)).ConfigureAwait(false); + + Assert.NotNull(addresses); + Assert.True(addresses.Length > 0); + } + else + { + IPHostEntry hostEntry = await ((Task)NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses)).ConfigureAwait(false); + + Assert.NotNull(hostEntry); + Assert.True(hostEntry.AddressList.Length > 0); + } } [Theory] From 1b2daab0f880d9955498dd137ce60b510f7a09d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 7 Apr 2020 19:00:08 +0200 Subject: [PATCH 05/32] Revert Interop.GetHostName change -- it's needed at other places too So this change was a bad idea ;-) --- .../Unix/System.Native/Interop.GetHostName.cs | 28 ++++++++++++++++++- .../src/System/Net/NameResolutionPal.Unix.cs | 27 +----------------- 2 files changed, 28 insertions(+), 27 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetHostName.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetHostName.cs index 065f76cd0d84d..f2555b0fc8276 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetHostName.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetHostName.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Diagnostics; using System.Runtime.InteropServices; internal static partial class Interop @@ -9,6 +11,30 @@ internal static partial class Interop internal static partial class Sys { [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetHostName", SetLastError = true)] - internal static extern unsafe int GetHostName(byte* name, int nameLength); + private static extern unsafe int GetHostName(byte* name, int nameLength); + + internal static unsafe string GetHostName() + { + const int HOST_NAME_MAX = 255; + const int ArrLength = HOST_NAME_MAX + 1; + + byte* name = stackalloc byte[ArrLength]; + int err = GetHostName(name, ArrLength); + if (err != 0) + { + // This should never happen. According to the man page, + // the only possible errno for gethostname is ENAMETOOLONG, + // which should only happen if the buffer we supply isn't big + // enough, and we're using a buffer size that the man page + // says is the max for POSIX (and larger than the max for Linux). + Debug.Fail($"GetHostName failed with error {err}"); + throw new InvalidOperationException($"{nameof(GetHostName)}: {err}"); + } + + // If the hostname is truncated, it is unspecified whether the returned buffer includes a terminating null byte. + name[ArrLength - 1] = 0; + + return Marshal.PtrToStringAnsi((IntPtr)name)!; + } } } diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index 1551d687a52af..1ec087c8c07c3 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -112,32 +112,7 @@ public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, return socketError == SocketError.Success ? Marshal.PtrToStringAnsi((IntPtr)buffer) : null; } - public static unsafe string GetHostName() - { - // We do not cache the result in case the hostname changes. - - const int HOST_NAME_MAX = 255; - const int ArrLength = HOST_NAME_MAX + 1; - - byte* name = stackalloc byte[ArrLength]; - int err = Interop.Sys.GetHostName(name, ArrLength); - - if (err != 0) - { - // This should never happen. According to the man page, - // the only possible errno for gethostname is ENAMETOOLONG, - // which should only happen if the buffer we supply isn't big - // enough, and we're using a buffer size that the man page - // says is the max for POSIX (and larger than the max for Linux). - Debug.Fail($"GetHostName failed with error {err}"); - throw new InvalidOperationException($"{nameof(GetHostName)}: {err}"); - } - - // If the hostname is truncated, it is unspecified whether the returned buffer includes a terminating null byte. - name[ArrLength - 1] = 0; - - return new string((sbyte*)name); - } + public static unsafe string GetHostName() => Interop.Sys.GetHostName(); public static unsafe Task GetAddrInfoAsync(string hostName, bool justAddresses) { From b9629e929fd8861887c3440c1e1fee073b2ed4fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 7 Apr 2020 20:47:11 +0200 Subject: [PATCH 06/32] Fixed builds failures due to "unused parameter" --- .../Native/Unix/System.Native/pal_networking.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index 9d2cef068789d..27b809149f626 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -343,6 +343,9 @@ static int32_t GetHostEntries(const uint8_t* address, struct addrinfo* info, Hos } } } +#else + // Actually not needed, but to use the parameter in case of HAVE_GETIFADDRS expands to 0. + address = NULL; #endif if (entry->IPAddressCount > 0) @@ -540,9 +543,10 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* return result; #else // Actually not needed, but otherwise the parameters are unused. - assert(address != NULL); - assert(entry != NULL); - assert(callback != NULL); + if (address != NULL && entry != NULL && callback != NULL) + { + return -1; + } // GetHostEntryForNameAsync is not supported on this platform. return -1; From 6f64c86cb3150c97746e2e02660801f08fb1a8ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 7 Apr 2020 22:57:52 +0200 Subject: [PATCH 07/32] Native: move struct addrinfo hint from stack-alloc to heap-alloc Before I didn't last the async operation as it was on the stack. Now it's part of the GetAddrInfoAsyncState which gets heap allocted, so it last. I'm not sure if addrinfo needs to last the async operation, as the native tests pass either way. This change makes it more correct, nevertheless. --- src/libraries/Native/Unix/System.Native/pal_networking.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index 27b809149f626..0d89cda853bda 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -425,6 +425,7 @@ static int32_t GetHostEntries(const uint8_t* address, struct addrinfo* info, Hos #if HAVE_GETADDRINFO_A struct GetAddrInfoAsyncState { + struct addrinfo hint; struct gaicb gai_request; struct gaicb* gai_requests; struct sigevent sigevent; @@ -511,14 +512,12 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* return GetAddrInfoErrorFlags_EAI_BADARG; } - struct addrinfo hint; - GetHostEntryForNameCreateHint(&hint); - struct GetAddrInfoAsyncState* state = malloc(sizeof(struct GetAddrInfoAsyncState)); + GetHostEntryForNameCreateHint(&state->hint); state->gai_request.ar_name = (const char*)address; state->gai_request.ar_service = NULL; - state->gai_request.ar_request = &hint; + state->gai_request.ar_request = &state->hint; state->gai_request.ar_result = NULL; state->gai_requests = &state->gai_request; From e7df0cc5ff4555cd0bf792700beede60cff5fda4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Wed, 8 Apr 2020 19:48:24 +0200 Subject: [PATCH 08/32] PR feedback --- .../Unix/System.Native/pal_networking.c | 88 ++++++++----------- .../src/System/Net/NameResolutionPal.Unix.cs | 36 +------- 2 files changed, 41 insertions(+), 83 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index 0d89cda853bda..9a383dcb7d528 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -344,8 +344,7 @@ static int32_t GetHostEntries(const uint8_t* address, struct addrinfo* info, Hos } } #else - // Actually not needed, but to use the parameter in case of HAVE_GETIFADDRS expands to 0. - address = NULL; + (void)address; #endif if (entry->IPAddressCount > 0) @@ -425,7 +424,6 @@ static int32_t GetHostEntries(const uint8_t* address, struct addrinfo* info, Hos #if HAVE_GETADDRINFO_A struct GetAddrInfoAsyncState { - struct addrinfo hint; struct gaicb gai_request; struct gaicb* gai_requests; struct sigevent sigevent; @@ -443,46 +441,33 @@ static void GetHostEntryForNameAsyncComplete(sigval_t context) GetHostEntryForNameCallback callback = state->callback; - int result = gai_error(&state->gai_request); - HostEntry* entry = state->entry; + int ret = ConvertGetAddrInfoAndGetNameInfoErrorsToPal(gai_error(&state->gai_request)); - int ret = ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); - - if (result == 0) + if (ret == 0) { const uint8_t* address = state->address; struct addrinfo* info = state->gai_request.ar_result; - ret = GetHostEntries(address, info, entry); + ret = GetHostEntries(address, info, state->entry); } - free(state); - if (callback != NULL) { - callback(entry, ret); + callback(state->entry, ret); } -} -#endif -static void GetHostEntryForNameCreateHint(struct addrinfo* hint) -{ - // Get all address families and the canonical name - - memset(hint, 0, sizeof(struct addrinfo)); - hint->ai_family = AF_UNSPEC; - hint->ai_flags = AI_CANONNAME; + free(state); } +#endif int32_t SystemNative_PlatformSupportsGetAddrInfoAsync() { -#if HAVE_GETADDRINFO_A - return 1; -#else - return 0; -#endif + return HAVE_GETADDRINFO_A; } +// Get all address families and the canonical name +static const struct addrinfo s_hostEntryForNameHints = {.ai_family = AF_UNSPEC, .ai_flags = AI_CANONNAME}; + int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entry) { if (address == NULL || entry == NULL) @@ -490,12 +475,9 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entr return GetAddrInfoErrorFlags_EAI_BADARG; } - struct addrinfo hint; - GetHostEntryForNameCreateHint(&hint); - struct addrinfo* info = NULL; - int result = getaddrinfo((const char*)address, NULL, &hint, &info); + int result = getaddrinfo((const char*)address, NULL, &s_hostEntryForNameHints, &info); if (result != 0) { return ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); @@ -512,22 +494,32 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* return GetAddrInfoErrorFlags_EAI_BADARG; } - struct GetAddrInfoAsyncState* state = malloc(sizeof(struct GetAddrInfoAsyncState)); - - GetHostEntryForNameCreateHint(&state->hint); - state->gai_request.ar_name = (const char*)address; - state->gai_request.ar_service = NULL; - state->gai_request.ar_request = &state->hint; - state->gai_request.ar_result = NULL; - state->gai_requests = &state->gai_request; + struct GetAddrInfoAsyncState* state = malloc(sizeof(*state)); - state->sigevent.sigev_notify = SIGEV_THREAD; - state->sigevent.sigev_value.sival_ptr = state; - state->sigevent.sigev_notify_function = GetHostEntryForNameAsyncComplete; + if (state == NULL) + { + return GetAddrInfoErrorFlags_EAI_MEMORY; + } - state->address = address; - state->entry = entry; - state->callback = callback; + *state = (struct GetAddrInfoAsyncState){ + .gai_request = { + .ar_name = (const char*)address, + .ar_service = NULL, + .ar_request = &s_hostEntryForNameHints, + .ar_result = NULL + }, + .gai_requests = &state->gai_request, + .sigevent = { + .sigev_notify = SIGEV_THREAD, + .sigev_value = { + .sival_ptr = state + }, + .sigev_notify_function = GetHostEntryForNameAsyncComplete + }, + .address = address, + .entry = entry, + .callback = callback + }; atomic_thread_fence(memory_order_release); @@ -541,11 +533,9 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* return result; #else - // Actually not needed, but otherwise the parameters are unused. - if (address != NULL && entry != NULL && callback != NULL) - { - return -1; - } + (void)address; + (void)entry; + (void)callback; // GetHostEntryForNameAsync is not supported on this platform. return -1; diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index 1ec087c8c07c3..1eee6afeb51d4 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -14,43 +14,11 @@ namespace System.Net { internal static partial class NameResolutionPal { - private static volatile bool s_initialized; - private static readonly object s_initializedLock = new object(); - private static readonly unsafe Interop.Sys.GetHostEntryForNameCallback s_getHostEntryForNameCallback = GetHostEntryForNameCallback; - private static bool s_platformSupportsGetAddrInfoAsync; - - public static void EnsureSocketsAreInitialized() - { - // Actually a No-op for Unix as sockets don't need a startup, but used - // to check whether async address resolution is available or not. - if (!s_initialized) - { - Initialize(); - } + public static bool SupportsGetAddrInfoAsync { get; } = Interop.Sys.PlatformSupportsGetAddrInfoAsync(); - static void Initialize() - { - lock (s_initializedLock) - { - if (!s_initialized) - { - s_platformSupportsGetAddrInfoAsync = Interop.Sys.PlatformSupportsGetAddrInfoAsync(); - s_initialized = true; - } - } - } - } - - public static bool SupportsGetAddrInfoAsync - { - get - { - EnsureSocketsAreInitialized(); - return s_platformSupportsGetAddrInfoAsync; - } - } + public static void EnsureSocketsAreInitialized() { } // No-op for Unix public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, out string? hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode) { From d7cb2be7eb3ec09f27a537c89341fb63e176e0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Thu, 9 Apr 2020 14:02:09 +0200 Subject: [PATCH 09/32] Fixed leak in tests.c according to valgrind's run --- src/libraries/Native/Unix/System.Native/tests.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Native/tests.c b/src/libraries/Native/Unix/System.Native/tests.c index e555cadd9c143..fda2e49e58082 100644 --- a/src/libraries/Native/Unix/System.Native/tests.c +++ b/src/libraries/Native/Unix/System.Native/tests.c @@ -11,7 +11,6 @@ struct state { HostEntry entry; - char* hostName; int errorCode; sem_t semaphore; }; @@ -65,6 +64,7 @@ int main(int argc, char** argv) } struct state state; + memset(&state.entry, 0, sizeof(HostEntry)); sem_init(&state.semaphore, 0, 0); int error = SystemNative_GetHostEntryForNameAsync((uint8_t*)hostName, &state.entry, callback); @@ -80,6 +80,8 @@ int main(int argc, char** argv) sem_wait(&state.semaphore); sem_destroy(&state.semaphore); + SystemNative_FreeHostEntry(&state.entry); + printf("(%lu) main: exit, errorCode: %d\n", pthread_self(), state.errorCode); return state.errorCode; From 74b30319792fc5de02e61b1b3ea502672c1211d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Thu, 9 Apr 2020 20:40:08 +0200 Subject: [PATCH 10/32] Fixed bug due to marshalled string argument --- .../Unix/System.Native/pal_networking.c | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index 9a383dcb7d528..d996158e2e85f 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -428,7 +428,7 @@ struct GetAddrInfoAsyncState struct gaicb* gai_requests; struct sigevent sigevent; - const uint8_t* address; + uint8_t* address; HostEntry* entry; GetHostEntryForNameCallback callback; }; @@ -456,6 +456,7 @@ static void GetHostEntryForNameAsyncComplete(sigval_t context) callback(state->entry, ret); } + free(state->address); free(state); } #endif @@ -501,9 +502,11 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* return GetAddrInfoErrorFlags_EAI_MEMORY; } - *state = (struct GetAddrInfoAsyncState){ + char* localAddress = strdup((const char*)address); + + *state = (struct GetAddrInfoAsyncState) { .gai_request = { - .ar_name = (const char*)address, + .ar_name = localAddress, .ar_service = NULL, .ar_request = &s_hostEntryForNameHints, .ar_result = NULL @@ -516,7 +519,7 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* }, .sigev_notify_function = GetHostEntryForNameAsyncComplete }, - .address = address, + .address = (uint8_t*)localAddress, .entry = entry, .callback = callback }; @@ -527,6 +530,7 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* if (result != 0) { + free(localAddress); free(state); return ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); } @@ -546,8 +550,15 @@ void SystemNative_FreeHostEntry(HostEntry* entry) { if (entry != NULL) { - free(entry->CanonicalName); - free(entry->IPAddressList); + if (entry->CanonicalName != NULL) + { + free(entry->CanonicalName); + } + + if (entry->IPAddressList != NULL) + { + free(entry->IPAddressList); + } entry->CanonicalName = NULL; entry->IPAddressList = NULL; From 775ff72181d7af3c6b8a377a7754f15f0f3e1130 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Thu, 9 Apr 2020 21:37:36 +0200 Subject: [PATCH 11/32] More managed PalTests --- .../tests/PalTests/NameResolutionPalTests.cs | 211 ++++++++++++------ 1 file changed, 146 insertions(+), 65 deletions(-) diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs index 94d7e562b9dbd..84db333d18225 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs @@ -30,12 +30,6 @@ private void LogUnixInfo() _output.WriteLine("------"); } - [Fact] - public void HostName_NotNull() - { - Assert.NotNull(NameResolutionPal.GetHostName()); - } - [Theory] [InlineData(false)] [InlineData(true)] @@ -52,32 +46,6 @@ public void TryGetAddrInfo_LocalHost(bool justAddresses) Assert.True(addresses.Length > 0); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public async Task GetAddrInfoAsync_LocalHost(bool justAddresses) - { - if (!NameResolutionPal.SupportsGetAddrInfoAsync) - { - return; - } - - if (justAddresses) - { - IPAddress[] addresses = await ((Task)NameResolutionPal.GetAddrInfoAsync("localhost", justAddresses)).ConfigureAwait(false); - - Assert.NotNull(addresses); - Assert.True(addresses.Length > 0); - } - else - { - IPHostEntry hostEntry = await ((Task)NameResolutionPal.GetAddrInfoAsync("localhost", justAddresses)).ConfigureAwait(false); - - Assert.NotNull(hostEntry); - Assert.True(hostEntry.AddressList.Length > 0); - } - } - [Theory] [InlineData(false)] [InlineData(true)] @@ -104,6 +72,32 @@ public void TryGetAddrInfo_HostName(bool justAddresses) Assert.NotNull(addresses); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void TryGetAddrInfo_ExternalHost(bool justAddresses) + { + string hostName = "microsoft.com"; + + SocketError error = NameResolutionPal.TryGetAddrInfo(hostName, justAddresses, out hostName, out string[] aliases, out IPAddress[] addresses, out _); + Assert.Equal(SocketError.Success, error); + Assert.NotNull(aliases); + Assert.NotNull(addresses); + Assert.True(addresses.Length > 0); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + [OuterLoop("Uses external server")] + public void TryGetAddrInfo_UnknownHost(bool justAddresses) + { + SocketError error = NameResolutionPal.TryGetAddrInfo("test.123", justAddresses, out string? _, out string[] _, out IPAddress[] _, out int nativeErrorCode); + + Assert.Equal(SocketError.HostNotFound, error); + Assert.NotEqual(0, nativeErrorCode); + } + [Fact] public void TryGetNameInfo_LocalHost_IPv4() { @@ -180,29 +174,56 @@ public void TryGetAddrInfo_HostName_TryGetNameInfo() [Theory] [InlineData(false)] [InlineData(true)] - public void TryGetAddrInfo_ExternalHost(bool justAddresses) + public void TryGetNameInfo_LocalHost_IPv4_TryGetAddrInfo(bool justAddresses) { - string hostName = "microsoft.com"; + string name = NameResolutionPal.TryGetNameInfo(new IPAddress(new byte[] { 127, 0, 0, 1 }), out SocketError error, out _); + Assert.Equal(SocketError.Success, error); + Assert.NotNull(name); - SocketError error = NameResolutionPal.TryGetAddrInfo(hostName, justAddresses, out hostName, out string[] aliases, out IPAddress[] addresses, out _); + error = NameResolutionPal.TryGetAddrInfo(name, justAddresses, out string hostName, out string[] aliases, out IPAddress[] addresses, out _); Assert.Equal(SocketError.Success, error); Assert.NotNull(aliases); Assert.NotNull(addresses); - Assert.True(addresses.Length > 0); } [Theory] [InlineData(false)] [InlineData(true)] - public async Task GetAddrInfoAsync_ExternalHost(bool justAddresses) + public void TryGetNameInfo_LocalHost_IPv6_TryGetAddrInfo(bool justAddresses) { - if (!NameResolutionPal.SupportsGetAddrInfoAsync) + SocketError error; + int nativeErrorCode; + string name = NameResolutionPal.TryGetNameInfo(new IPAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), out error, out nativeErrorCode); + if (SocketError.Success != error && Environment.OSVersion.Platform == PlatformID.Unix) { - return; + LogUnixInfo(); } - const string hostName = "microsoft.com"; + Assert.Equal(SocketError.Success, error); + Assert.NotNull(name); + + error = NameResolutionPal.TryGetAddrInfo(name, justAddresses, out string hostName, out string[] aliases, out IPAddress[] addresses, out _); + if (SocketError.Success != error && Environment.OSVersion.Platform == PlatformID.Unix) + { + LogUnixInfo(); + } + Assert.Equal(SocketError.Success, error); + Assert.NotNull(aliases); + Assert.NotNull(addresses); + } + + [Fact] + public void HostName_NotNull() + { + Assert.NotNull(NameResolutionPal.GetHostName()); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task GetAddrInfoAsync_LocalHost(bool justAddresses) + { if (!NameResolutionPal.SupportsGetAddrInfoAsync) { return; @@ -210,14 +231,14 @@ public async Task GetAddrInfoAsync_ExternalHost(bool justAddresses) if (justAddresses) { - IPAddress[] addresses = await ((Task)NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses)).ConfigureAwait(false); + IPAddress[] addresses = await ((Task)NameResolutionPal.GetAddrInfoAsync("localhost", justAddresses)).ConfigureAwait(false); Assert.NotNull(addresses); Assert.True(addresses.Length > 0); } else { - IPHostEntry hostEntry = await ((Task)NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses)).ConfigureAwait(false); + IPHostEntry hostEntry = await ((Task)NameResolutionPal.GetAddrInfoAsync("localhost", justAddresses)).ConfigureAwait(false); Assert.NotNull(hostEntry); Assert.True(hostEntry.AddressList.Length > 0); @@ -227,50 +248,110 @@ public async Task GetAddrInfoAsync_ExternalHost(bool justAddresses) [Theory] [InlineData(false)] [InlineData(true)] - public void TryGetNameInfo_LocalHost_IPv4_TryGetAddrInfo(bool justAddresses) + [OuterLoop("Uses external server")] + public async Task GetAddrInfoAsync_HostName(bool justAddresses) { - string name = NameResolutionPal.TryGetNameInfo(new IPAddress(new byte[] { 127, 0, 0, 1 }), out SocketError error, out _); - Assert.Equal(SocketError.Success, error); - Assert.NotNull(name); + if (!NameResolutionPal.SupportsGetAddrInfoAsync) + { + return; + } - error = NameResolutionPal.TryGetAddrInfo(name, justAddresses, out string hostName, out string[] aliases, out IPAddress[] addresses, out _); - Assert.Equal(SocketError.Success, error); - Assert.NotNull(aliases); - Assert.NotNull(addresses); + string hostName = NameResolutionPal.GetHostName(); + Assert.NotNull(hostName); + + Task task = NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses); + + try + { + await task.ConfigureAwait(false); + } + catch (SocketException ex) + { + SocketError error = ex.SocketErrorCode; + + if (error == SocketError.HostNotFound && (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))) + { + // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the + // machine configurations, which varies by distro and is often inconsistent. + return; + } + + throw; + } + + if (justAddresses) + { + IPAddress[] addresses = ((Task)task).Result; + + Assert.NotNull(addresses); + Assert.True(addresses.Length > 0); + } + else + { + IPHostEntry hostEntry = ((Task)task).Result; + + Assert.NotNull(hostEntry); + Assert.True(hostEntry.AddressList.Length > 0); + } } [Theory] [InlineData(false)] [InlineData(true)] - public void TryGetNameInfo_LocalHost_IPv6_TryGetAddrInfo(bool justAddresses) + public async Task GetAddrInfoAsync_ExternalHost(bool justAddresses) { - SocketError error; - int nativeErrorCode; - string name = NameResolutionPal.TryGetNameInfo(new IPAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }), out error, out nativeErrorCode); - if (SocketError.Success != error && Environment.OSVersion.Platform == PlatformID.Unix) + if (!NameResolutionPal.SupportsGetAddrInfoAsync) { - LogUnixInfo(); + return; } - Assert.Equal(SocketError.Success, error); - Assert.NotNull(name); + const string hostName = "microsoft.com"; - error = NameResolutionPal.TryGetAddrInfo(name, justAddresses, out string hostName, out string[] aliases, out IPAddress[] addresses, out _); - if (SocketError.Success != error && Environment.OSVersion.Platform == PlatformID.Unix) + if (!NameResolutionPal.SupportsGetAddrInfoAsync) { - LogUnixInfo(); + return; } - Assert.Equal(SocketError.Success, error); - Assert.NotNull(aliases); - Assert.NotNull(addresses); + if (justAddresses) + { + IPAddress[] addresses = await ((Task)NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses)).ConfigureAwait(false); + + Assert.NotNull(addresses); + Assert.True(addresses.Length > 0); + } + else + { + IPHostEntry hostEntry = await ((Task)NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses)).ConfigureAwait(false); + + Assert.NotNull(hostEntry); + Assert.True(hostEntry.AddressList.Length > 0); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + [OuterLoop("Uses external server")] + public async Task GetAddrInfoAsync_UnknownHost(bool justAddresses) + { + if (!NameResolutionPal.SupportsGetAddrInfoAsync) + { + return; + } + + const string hostName = "test.123"; + + SocketException socketException = await Assert.ThrowsAnyAsync(() => NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses)).ConfigureAwait(false); + SocketError socketError = socketException.SocketErrorCode; + + Assert.Equal(SocketError.HostNotFound, socketError); } [Fact] [PlatformSpecific(TestPlatforms.AnyUnix)] public void Exception_HostNotFound_Success() { - var ex = new SocketException((int)SocketError.HostNotFound); + var ex = new SocketException((int)SocketError.HostNotFound); Assert.Equal(-1, ex.Message.IndexOf("Device")); } From ec8522fb7613a52bcdd49ddb76991d26a46a6fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Thu, 9 Apr 2020 21:39:45 +0200 Subject: [PATCH 12/32] Revert "Native tests" This reverts commit 8c955bc4bd1d0dbe4b2823f4327ad52bf9856fd7 and d7cb2be7eb3ec09f27a537c89341fb63e176e0bc. --- src/libraries/Native/Unix/CMakeLists.txt | 2 - .../Native/Unix/System.Native/CMakeLists.txt | 31 ------- .../Native/Unix/System.Native/tests.c | 88 ------------------- 3 files changed, 121 deletions(-) delete mode 100644 src/libraries/Native/Unix/System.Native/tests.c diff --git a/src/libraries/Native/Unix/CMakeLists.txt b/src/libraries/Native/Unix/CMakeLists.txt index 0769eaf19d73c..b3c9613a0e10b 100644 --- a/src/libraries/Native/Unix/CMakeLists.txt +++ b/src/libraries/Native/Unix/CMakeLists.txt @@ -210,5 +210,3 @@ endif() if(CLR_CMAKE_TARGET_OSX OR CLR_CMAKE_TARGET_IOS) add_subdirectory(System.Security.Cryptography.Native.Apple) endif() - -enable_testing() diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index aae0a19eef445..bf2e3787607fc 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -80,34 +80,3 @@ endif () set_target_properties(System.Native-Static PROPERTIES OUTPUT_NAME System.Native CLEAN_DIRECT_OUTPUT 1) install (TARGETS System.Native-Static DESTINATION .) - -if (HAVE_GETADDRINFO_A) - add_executable(tests - tests.c - ) - - target_link_libraries(tests - System.Native - pthread - ) - - enable_testing() - - add_test(no_args tests) - set_tests_properties(no_args PROPERTIES WILL_FAIL TRUE) - set_tests_properties(no_args PROPERTIES TIMEOUT 1) - - add_test(hostname_local tests localhost) - set_tests_properties(hostname_local PROPERTIES TIMEOUT 1) - - add_test(hostname_ok tests google.at) - set_tests_properties(hostname_ok PROPERTIES TIMEOUT 1) - - add_test(hostname_invalid tests abc.test01) - set_tests_properties(hostname_invalid PROPERTIES WILL_FAIL TRUE) - set_tests_properties(hostname_invalid PROPERTIES TIMEOUT 1) - - add_test(hostname_null tests null) - set_tests_properties(hostname_null PROPERTIES WILL_FAIL TRUE) - set_tests_properties(hostname_null PROPERTIES TIMEOUT 1) -endif () diff --git a/src/libraries/Native/Unix/System.Native/tests.c b/src/libraries/Native/Unix/System.Native/tests.c deleted file mode 100644 index fda2e49e58082..0000000000000 --- a/src/libraries/Native/Unix/System.Native/tests.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "pal_networking.h" - -#include -#include -#include -#include -#include -#include -#include - -struct state -{ - HostEntry entry; - int errorCode; - sem_t semaphore; -}; - -static void callback(HostEntry* entry, int errorCode) -{ - printf("(%lu) handler: enter, errorCode: %d\n", pthread_self(), errorCode); - - struct state* state = (struct state*)entry; - - if (errorCode == 0) - { - printf("(%lu) callback: # of addresses = %d\n", pthread_self(), entry->IPAddressCount); - for (int i = 0; i < entry->IPAddressCount; ++i) - { - IPAddress ipAddress = entry->IPAddressList[i]; - - char buffer[256]; - inet_ntop(ipAddress.IsIPv6 ? AF_INET6 : AF_INET, ipAddress.Address, buffer, sizeof(buffer)); - printf("(%lu) ip for %s: %s\n", pthread_self(), entry->CanonicalName, buffer); - } - } - - state->errorCode = errorCode; - - sem_post(&state->semaphore); -} - -int main(int argc, char** argv) -{ - if (argc != 2) - { - printf("hostname must be given as argument\n"); - return EXIT_FAILURE; - } - - if (!SystemNative_PlatformSupportsGetAddrInfoAsync()) - { - printf("platform support not available\n"); - return EXIT_FAILURE; - } - - printf("platform support available\n"); - - char* hostName = argv[1]; - printf("(%lu) hostname: %s\n", pthread_self(), hostName); - - if (strcmp(hostName, "null")==0) - { - hostName = NULL; - } - - struct state state; - memset(&state.entry, 0, sizeof(HostEntry)); - sem_init(&state.semaphore, 0, 0); - - int error = SystemNative_GetHostEntryForNameAsync((uint8_t*)hostName, &state.entry, callback); - - if (error != 0) - { - printf("(%lu) OS call failed with error %d\n", pthread_self(), error); - return EXIT_FAILURE; - } - - printf("(%lu) main: waiting for semaphore\n", pthread_self()); - - sem_wait(&state.semaphore); - sem_destroy(&state.semaphore); - - SystemNative_FreeHostEntry(&state.entry); - - printf("(%lu) main: exit, errorCode: %d\n", pthread_self(), state.errorCode); - - return state.errorCode; -} From 298cf039b1ca7cd859045a3febf114c84d5f1a29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Thu, 9 Apr 2020 22:17:14 +0200 Subject: [PATCH 13/32] Handle the case of HostName="" and tests for this --- .../src/System/Net/NameResolutionPal.Unix.cs | 6 ++ .../tests/PalTests/NameResolutionPalTests.cs | 71 +++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index 1eee6afeb51d4..d73a768eb65f9 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -84,6 +84,12 @@ public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, public static unsafe Task GetAddrInfoAsync(string hostName, bool justAddresses) { + if (hostName == "") + { + // To match documented behavior on Windows, if an empty string is passed in, use the local host's name. + hostName = Dns.GetHostName(); + } + GetHostEntryForNameContext* context = GetHostEntryForNameContext.AllocateContext(); GetHostEntryForNameState state; diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs index 84db333d18225..ab2b64c32197e 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs @@ -46,6 +46,29 @@ public void TryGetAddrInfo_LocalHost(bool justAddresses) Assert.True(addresses.Length > 0); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void TryGetAddrInfo_EmptyHost(bool justAddresses) + { + SocketError error = NameResolutionPal.TryGetAddrInfo("", justAddresses, out string hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode); + if (error == SocketError.HostNotFound && (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))) + { + // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the + // machine configurations, which varies by distro and is often inconsistent. + return; + } + + Assert.Equal(SocketError.Success, error); + if (!justAddresses) + { + Assert.NotNull(hostName); + } + Assert.NotNull(aliases); + Assert.NotNull(addresses); + Assert.True(addresses.Length > 0); + } + [Theory] [InlineData(false)] [InlineData(true)] @@ -70,6 +93,7 @@ public void TryGetAddrInfo_HostName(bool justAddresses) } Assert.NotNull(aliases); Assert.NotNull(addresses); + Assert.True(addresses.Length > 0); } [Theory] @@ -245,6 +269,53 @@ public async Task GetAddrInfoAsync_LocalHost(bool justAddresses) } } + [Theory] + [InlineData(false)] + [InlineData(true)] + [OuterLoop("Uses external server")] + public async Task GetAddrInfoAsync_EmptyHost(bool justAddresses) + { + if (!NameResolutionPal.SupportsGetAddrInfoAsync) + { + return; + } + + Task task = NameResolutionPal.GetAddrInfoAsync("", justAddresses); + + try + { + await task.ConfigureAwait(false); + } + catch (SocketException ex) + { + SocketError error = ex.SocketErrorCode; + + if (error == SocketError.HostNotFound && (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))) + { + // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the + // machine configurations, which varies by distro and is often inconsistent. + return; + } + + throw; + } + + if (justAddresses) + { + IPAddress[] addresses = ((Task)task).Result; + + Assert.NotNull(addresses); + Assert.True(addresses.Length > 0); + } + else + { + IPHostEntry hostEntry = ((Task)task).Result; + + Assert.NotNull(hostEntry); + Assert.True(hostEntry.AddressList.Length > 0); + } + } + [Theory] [InlineData(false)] [InlineData(true)] From 458579b71af2456bc97e410ecfb99219309f5cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Thu, 9 Apr 2020 23:19:33 +0200 Subject: [PATCH 14/32] Use flexible array member to hold the address in struct state Cf. https://github.com/dotnet/runtime/pull/34633#discussion_r406447963 --- .../Unix/System.Native/pal_networking.c | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index d996158e2e85f..eaaa89991e417 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -428,9 +428,9 @@ struct GetAddrInfoAsyncState struct gaicb* gai_requests; struct sigevent sigevent; - uint8_t* address; HostEntry* entry; GetHostEntryForNameCallback callback; + char address[]; }; static void GetHostEntryForNameAsyncComplete(sigval_t context) @@ -445,7 +445,7 @@ static void GetHostEntryForNameAsyncComplete(sigval_t context) if (ret == 0) { - const uint8_t* address = state->address; + const uint8_t* address = (const uint8_t*)state->address; struct addrinfo* info = state->gai_request.ar_result; ret = GetHostEntries(address, info, state->entry); @@ -456,7 +456,6 @@ static void GetHostEntryForNameAsyncComplete(sigval_t context) callback(state->entry, ret); } - free(state->address); free(state); } #endif @@ -495,18 +494,25 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* return GetAddrInfoErrorFlags_EAI_BADARG; } - struct GetAddrInfoAsyncState* state = malloc(sizeof(*state)); + size_t addrlen = strlen((const char*)address); + + if (addrlen > _POSIX_HOST_NAME_MAX) + { + return GetAddrInfoErrorFlags_EAI_BADARG; + } + + struct GetAddrInfoAsyncState* state = malloc(sizeof(*state) + addrlen + 1); if (state == NULL) { return GetAddrInfoErrorFlags_EAI_MEMORY; } - char* localAddress = strdup((const char*)address); + memcpy(state->address, address, addrlen + 1); *state = (struct GetAddrInfoAsyncState) { .gai_request = { - .ar_name = localAddress, + .ar_name = state->address, .ar_service = NULL, .ar_request = &s_hostEntryForNameHints, .ar_result = NULL @@ -519,7 +525,6 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* }, .sigev_notify_function = GetHostEntryForNameAsyncComplete }, - .address = (uint8_t*)localAddress, .entry = entry, .callback = callback }; @@ -530,7 +535,6 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* if (result != 0) { - free(localAddress); free(state); return ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); } From 8f691640addf25d93d951369447649d75b9b2595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Fri, 10 Apr 2020 10:52:47 +0200 Subject: [PATCH 15/32] Nits in native layer Cf. https://github.com/dotnet/runtime/pull/34633#discussion_r406494734 --- .../Native/Unix/System.Native/pal_networking.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index eaaa89991e417..80488a4560d6e 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -489,7 +489,7 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entr int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, HostEntry* entry, GetHostEntryForNameCallback callback) { #if HAVE_GETADDRINFO_A - if (address == NULL) + if (address == NULL || entry == NULL) { return GetAddrInfoErrorFlags_EAI_BADARG; } @@ -554,15 +554,8 @@ void SystemNative_FreeHostEntry(HostEntry* entry) { if (entry != NULL) { - if (entry->CanonicalName != NULL) - { - free(entry->CanonicalName); - } - - if (entry->IPAddressList != NULL) - { - free(entry->IPAddressList); - } + free(entry->CanonicalName); + free(entry->IPAddressList); entry->CanonicalName = NULL; entry->IPAddressList = NULL; From 43f889f532b6e8ffdc79a0f72698bcd2bf80efe2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 15 Dec 2020 16:29:24 +0100 Subject: [PATCH 16/32] Fixed native merge conflict + added AddressFamily-handling --- .../Unix/System.Native/pal_networking.c | 194 +++++++++++++++--- .../Unix/System.Native/pal_networking.h | 8 + 2 files changed, 170 insertions(+), 32 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index 927c9cfe31af3..61838e371011f 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -60,6 +60,10 @@ #if HAVE_LINUX_CAN_H #include #endif +#if HAVE_GETADDRINFO_A +#include +#include +#endif #if HAVE_SYS_FILIO_H #include #endif @@ -335,37 +339,14 @@ static int32_t CopySockAddrToIPAddress(sockaddr* addr, sa_family_t family, IPAdd return -1; } -int32_t SystemNative_GetHostEntryForName(const uint8_t* address, int32_t addressFamily, HostEntry* entry) +static int32_t GetHostEntries(const uint8_t* address, struct addrinfo* info, HostEntry* entry) { - if (address == NULL || entry == NULL) - { - return GetAddrInfoErrorFlags_EAI_BADARG; - } - int32_t ret = GetAddrInfoErrorFlags_EAI_SUCCESS; - struct addrinfo* info = NULL; #if HAVE_GETIFADDRS struct ifaddrs* addrs = NULL; #endif - sa_family_t platformFamily; - if (!TryConvertAddressFamilyPalToPlatform(addressFamily, &platformFamily)) - { - return GetAddrInfoErrorFlags_EAI_FAMILY; - } - - struct addrinfo hint; - memset(&hint, 0, sizeof(struct addrinfo)); - hint.ai_flags = AI_CANONNAME; - hint.ai_family = platformFamily; - - int result = getaddrinfo((const char*)address, NULL, &hint, &info); - if (result != 0) - { - return ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); - } - entry->CanonicalName = NULL; entry->Aliases = NULL; entry->IPAddressList = NULL; @@ -393,7 +374,8 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, int32_t address #if HAVE_GETIFADDRS char name[_POSIX_HOST_NAME_MAX]; - result = gethostname((char*)name, _POSIX_HOST_NAME_MAX); + + int result = gethostname((char*)name, _POSIX_HOST_NAME_MAX); bool includeIPv4Loopback = true; bool includeIPv6Loopback = true; @@ -443,6 +425,8 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, int32_t address } } } +#else + (void)address; #endif if (entry->IPAddressCount > 0) @@ -519,6 +503,152 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, int32_t address return ret; } +#if HAVE_GETADDRINFO_A +struct GetAddrInfoAsyncState +{ + struct gaicb gai_request; + struct gaicb* gai_requests; + struct sigevent sigevent; + + HostEntry* entry; + GetHostEntryForNameCallback callback; + char address[]; +}; + +static void GetHostEntryForNameAsyncComplete(sigval_t context) +{ + struct GetAddrInfoAsyncState* state = (struct GetAddrInfoAsyncState*)context.sival_ptr; + + atomic_thread_fence(memory_order_acquire); + + GetHostEntryForNameCallback callback = state->callback; + + int ret = ConvertGetAddrInfoAndGetNameInfoErrorsToPal(gai_error(&state->gai_request)); + + if (ret == 0) + { + const uint8_t* address = (const uint8_t*)state->address; + struct addrinfo* info = state->gai_request.ar_result; + + ret = GetHostEntries(address, info, state->entry); + } + + assert(callback != NULL); + callback(state->entry, ret); + + free(state); +} +#endif + +int32_t SystemNative_PlatformSupportsGetAddrInfoAsync() +{ + return HAVE_GETADDRINFO_A; +} + +int32_t SystemNative_GetHostEntryForName(const uint8_t* address, int32_t addressFamily, HostEntry* entry) +{ + if (address == NULL || entry == NULL) + { + return GetAddrInfoErrorFlags_EAI_BADARG; + } + + sa_family_t platformFamily; + if (!TryConvertAddressFamilyPalToPlatform(addressFamily, &platformFamily)) + { + return GetAddrInfoErrorFlags_EAI_FAMILY; + } + + struct addrinfo hint; + memset(&hint, 0, sizeof(struct addrinfo)); + hint.ai_flags = AI_CANONNAME; + hint.ai_family = platformFamily; + + struct addrinfo* info = NULL; + + int result = getaddrinfo((const char*)address, NULL, &hint, &info); + if (result != 0) + { + return ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); + } + + return GetHostEntries(address, info, entry); +} + +int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, int32_t addressFamily, HostEntry* entry, GetHostEntryForNameCallback callback) +{ +#if HAVE_GETADDRINFO_A + if (address == NULL || entry == NULL) + { + return GetAddrInfoErrorFlags_EAI_BADARG; + } + + size_t addrlen = strlen((const char*)address); + + if (addrlen > _POSIX_HOST_NAME_MAX) + { + return GetAddrInfoErrorFlags_EAI_BADARG; + } + + sa_family_t platformFamily; + if (!TryConvertAddressFamilyPalToPlatform(addressFamily, &platformFamily)) + { + return GetAddrInfoErrorFlags_EAI_FAMILY; + } + + struct addrinfo hint; + memset(&hint, 0, sizeof(struct addrinfo)); + hint.ai_flags = AI_CANONNAME; + hint.ai_family = platformFamily; + + struct GetAddrInfoAsyncState* state = malloc(sizeof(*state) + addrlen + 1); + + if (state == NULL) + { + return GetAddrInfoErrorFlags_EAI_MEMORY; + } + + memcpy(state->address, address, addrlen + 1); + + *state = (struct GetAddrInfoAsyncState) { + .gai_request = { + .ar_name = state->address, + .ar_service = NULL, + .ar_request = &hint, + .ar_result = NULL + }, + .gai_requests = &state->gai_request, + .sigevent = { + .sigev_notify = SIGEV_THREAD, + .sigev_value = { + .sival_ptr = state + }, + .sigev_notify_function = GetHostEntryForNameAsyncComplete + }, + .entry = entry, + .callback = callback + }; + + atomic_thread_fence(memory_order_release); + + int32_t result = getaddrinfo_a(GAI_NOWAIT, &state->gai_requests, 1, &state->sigevent); + + if (result != 0) + { + free(state); + return ConvertGetAddrInfoAndGetNameInfoErrorsToPal(result); + } + + return result; +#else + (void)address; + (void)entry; + (void)callback; + + // GetHostEntryForNameAsync is not supported on this platform. + return -1; +#endif +} + void SystemNative_FreeHostEntry(HostEntry* entry) { if (entry != NULL) @@ -555,13 +685,13 @@ static inline NativeFlagsType ConvertGetNameInfoFlagsToNative(int32_t flags) } int32_t SystemNative_GetNameInfo(const uint8_t* address, - int32_t addressLength, - int8_t isIPv6, - uint8_t* host, - int32_t hostLength, - uint8_t* service, - int32_t serviceLength, - int32_t flags) + int32_t addressLength, + int8_t isIPv6, + uint8_t* host, + int32_t hostLength, + uint8_t* service, + int32_t serviceLength, + int32_t flags) { assert(address != NULL); assert(addressLength > 0); diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.h b/src/libraries/Native/Unix/System.Native/pal_networking.h index bbb0bc0785cce..69d0d1759bb5d 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.h +++ b/src/libraries/Native/Unix/System.Native/pal_networking.h @@ -301,8 +301,16 @@ typedef struct uint32_t Padding; // Pad out to 8-byte alignment } SocketEvent; +PALEXPORT int32_t SystemNative_PlatformSupportsGetAddrInfoAsync(void); + PALEXPORT int32_t SystemNative_GetHostEntryForName(const uint8_t* address, int32_t addressFamily, HostEntry* entry); +typedef void (*GetHostEntryForNameCallback)(HostEntry* entry, int status); +PALEXPORT int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, + int32_t addressFamily, + HostEntry* entry, + GetHostEntryForNameCallback callback); + PALEXPORT void SystemNative_FreeHostEntry(HostEntry* entry); From bd16c74442e089ac7b73dd122cf7acf2882f41e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 15 Dec 2020 17:56:16 +0100 Subject: [PATCH 17/32] Fixed managed merge conflicts + added AddressFamily-handling --- .../Unix/System.Native/Interop.HostEntry.cs | 5 +- .../src/System/Net/NameResolutionPal.Unix.cs | 281 ++++++++++++++---- 2 files changed, 227 insertions(+), 59 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs index e29f2a6bf3a04..ada474e01f366 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Net.Sockets; using System.Runtime.InteropServices; internal static partial class Interop @@ -37,10 +38,10 @@ internal unsafe struct HostEntry internal static extern bool PlatformSupportsGetAddrInfoAsync(); [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetHostEntryForName")] - internal static extern unsafe int GetHostEntryForName(string address, System.Net.Sockets.AddressFamily family, HostEntry* entry); + internal static extern unsafe int GetHostEntryForName(string address, AddressFamily family, HostEntry* entry); [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetHostEntryForNameAsync")] - internal static extern unsafe int GetHostEntryForNameAsync(string address, HostEntry* entry, GetHostEntryForNameCallback callback); + internal static extern unsafe int GetHostEntryForNameAsync(string address, AddressFamily family, HostEntry* entry, GetHostEntryForNameCallback callback); [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FreeHostEntry")] internal static extern unsafe void FreeHostEntry(HostEntry* entry); diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index cc9037b032a15..d5ccc87106be6 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -1,12 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Collections.Generic; using System.Diagnostics; -using System.Net.Internals; using System.Net.Sockets; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; -using System.Text; using System.Threading; using System.Threading.Tasks; @@ -14,10 +13,147 @@ namespace System.Net { internal static partial class NameResolutionPal { - public const bool SupportsGetAddrInfoAsync = false; + private static readonly unsafe Interop.Sys.GetHostEntryForNameCallback s_getHostEntryForNameCallback = GetHostEntryForNameCallback; - internal static Task GetAddrInfoAsync(string hostName, bool justAddresses, AddressFamily family, CancellationToken cancellationToken) => - throw new NotSupportedException(); + public static bool SupportsGetAddrInfoAsync { get; } = Interop.Sys.PlatformSupportsGetAddrInfoAsync(); + + public static void EnsureSocketsAreInitialized() { } // No-op for Unix + + public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, AddressFamily addressFamily, out string? hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode) + { + Debug.Assert(name is not null); + + if (name.Length == 0) + { + // To match documented behavior on Windows, if an empty string is passed in, use the local host's name. + name = Dns.GetHostName(); + } + + Interop.Sys.HostEntry entry; + int result = Interop.Sys.GetHostEntryForName(name, addressFamily, &entry); + if (result != 0) + { + nativeErrorCode = result; + hostName = name; + aliases = Array.Empty(); + addresses = Array.Empty(); + return GetSocketErrorForNativeError(result); + } + + ParseHostEntry(entry, justAddresses, out hostName, out aliases, out addresses); + nativeErrorCode = 0; + return SocketError.Success; + } + + public static unsafe string? TryGetNameInfo(IPAddress addr, out SocketError socketError, out int nativeErrorCode) + { + byte* buffer = stackalloc byte[Interop.Sys.NI_MAXHOST + 1 /*for null*/]; + + byte isIPv6; + int rawAddressLength; + if (addr.AddressFamily == AddressFamily.InterNetwork) + { + isIPv6 = 0; + rawAddressLength = IPAddressParserStatics.IPv4AddressBytes; + } + else + { + isIPv6 = 1; + rawAddressLength = IPAddressParserStatics.IPv6AddressBytes; + } + + byte* rawAddress = stackalloc byte[rawAddressLength]; + addr.TryWriteBytes(new Span(rawAddress, rawAddressLength), out int bytesWritten); + Debug.Assert(bytesWritten == rawAddressLength); + + int error = Interop.Sys.GetNameInfo( + rawAddress, + (uint)rawAddressLength, + isIPv6, + buffer, + Interop.Sys.NI_MAXHOST, + null, + 0, + Interop.Sys.GetNameInfoFlags.NI_NAMEREQD); + + socketError = GetSocketErrorForNativeError(error); + nativeErrorCode = error; + return socketError == SocketError.Success ? Marshal.PtrToStringAnsi((IntPtr)buffer) : null; + } + + public static unsafe string GetHostName() => Interop.Sys.GetHostName(); + + public static unsafe Task GetAddrInfoAsync(string hostName, bool justAddresses, AddressFamily addressFamily, CancellationToken _) + { + Debug.Assert(hostName is not null); + + if (hostName.Length == 0) + { + // To match documented behavior on Windows, if an empty string is passed in, use the local host's name. + hostName = Dns.GetHostName(); + } + + GetHostEntryForNameContext* context = GetHostEntryForNameContext.AllocateContext(); + + GetHostEntryForNameState state; + try + { + state = new GetHostEntryForNameState(hostName, justAddresses); + context->State = state.CreateHandle(); + } + catch + { + GetHostEntryForNameContext.FreeContext(context); + throw; + } + + int errorCode = Interop.Sys.GetHostEntryForNameAsync(hostName, addressFamily, &context->Result, s_getHostEntryForNameCallback); + + if (errorCode != 0) + { + ProcessResult(GetSocketErrorForNativeError(errorCode), context); + } + + return state.Task; + } + + private static unsafe void GetHostEntryForNameCallback(Interop.Sys.HostEntry* entry, int error) + { + // Can be casted directly to GetHostEntryForNameContext* because the HostEntry is its first field + GetHostEntryForNameContext* context = (GetHostEntryForNameContext*)entry; + + ProcessResult(GetSocketErrorForNativeError(error), context); + } + + private static unsafe void ProcessResult(SocketError errorCode, GetHostEntryForNameContext* context) + { + try + { + GetHostEntryForNameState state = GetHostEntryForNameState.FromHandleAndFree(context->State); + + if (errorCode == SocketError.Success) + { + ParseHostEntry(context->Result, state.JustAddresses, out string? hostName, out string[] aliases, out IPAddress[] addresses); + + state.SetResult(state.JustAddresses + ? (object)addresses + : new IPHostEntry + { + HostName = hostName ?? state.HostName, + Aliases = aliases, + AddressList = addresses + }); + } + else + { + state.SetResult(ExceptionDispatchInfo.SetCurrentStackTrace(new SocketException((int)errorCode))); + } + } + finally + { + GetHostEntryForNameContext.FreeContext(context); + } + } private static SocketError GetSocketErrorForNativeError(int error) { @@ -48,9 +184,9 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool { try { - hostName = !justAddresses && hostEntry.CanonicalName != null ? - Marshal.PtrToStringAnsi((IntPtr)hostEntry.CanonicalName) : - null; + hostName = !justAddresses && hostEntry.CanonicalName != null + ? Marshal.PtrToStringAnsi((IntPtr)hostEntry.CanonicalName) + : null; IPAddress[] localAddresses; if (hostEntry.IPAddressCount == 0) @@ -60,13 +196,13 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool else { // getaddrinfo returns multiple entries per address, for each socket type (datagram, stream, etc.). - // Our callers expect just one entry for each address. So we need to deduplicate the results. + // Our callers expect just one entry for each address. So we need to deduplicate the results. // It's important to keep the addresses in order, since they are returned in the order in which // connections should be attempted. // // We assume that the list returned by getaddrinfo is relatively short; after all, the intent is that // the caller may need to attempt to contact every address in the list before giving up on a connection - // attempt. So an O(N^2) algorithm should be fine here. Keep in mind that any "better" algorithm + // attempt. So an O(N^2) algorithm should be fine here. Keep in mind that any "better" algorithm // is likely to involve extra allocations, hashing, etc., and so will probably be more expensive than // this one in the typical (short list) case. @@ -117,66 +253,97 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool } } - public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, AddressFamily addressFamily, out string? hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode) + private sealed class GetHostEntryForNameState : IThreadPoolWorkItem { - if (name == "") - { - // To match documented behavior on Windows, if an empty string is passed in, use the local host's name. - name = Dns.GetHostName(); - } + private AsyncTaskMethodBuilder _ipAddressArrayBuilder; + private AsyncTaskMethodBuilder _ipHostEntryBuilder; + private object? _result; - Interop.Sys.HostEntry entry; - int result = Interop.Sys.GetHostEntryForName(name, addressFamily, &entry); - if (result != 0) + public string HostName { get; } + public bool JustAddresses { get; } + + public GetHostEntryForNameState(string hostName, bool justAddresses) { - nativeErrorCode = result; - hostName = name; - aliases = Array.Empty(); - addresses = Array.Empty(); - return GetSocketErrorForNativeError(result); + HostName = hostName; + JustAddresses = justAddresses; + + if (justAddresses) + { + _ipAddressArrayBuilder = AsyncTaskMethodBuilder.Create(); + _ = _ipAddressArrayBuilder.Task; // force initialization + } + else + { + _ipHostEntryBuilder = AsyncTaskMethodBuilder.Create(); + _ = _ipHostEntryBuilder.Task; // force initialization + } } - ParseHostEntry(entry, justAddresses, out hostName, out aliases, out addresses); - nativeErrorCode = 0; - return SocketError.Success; - } + public Task Task => JustAddresses ? _ipAddressArrayBuilder.Task : _ipHostEntryBuilder.Task; - public static unsafe string? TryGetNameInfo(IPAddress addr, out SocketError socketError, out int nativeErrorCode) - { - byte* buffer = stackalloc byte[Interop.Sys.NI_MAXHOST + 1 /*for null*/]; + public void SetResult(object result) + { + // Store the result and then queue this object to the thread pool to actually complete the Tasks, as we + // want to avoid invoking continuations on the OS callback thread. Effectively we're manually + // implementing TaskCreationOptions.RunContinuationsAsynchronously, which we can't use because we're + // using AsyncTaskMethodBuilder, which we're using in order to create either a strongly-typed Task + // or Task without allocating additional objects. + Debug.Assert(result is Exception or IPAddress[] or IPHostEntry); + _result = result; + ThreadPool.UnsafeQueueUserWorkItem(this, preferLocal: false); + } - byte isIPv6; - int rawAddressLength; - if (addr.AddressFamily == AddressFamily.InterNetwork) + void IThreadPoolWorkItem.Execute() { - isIPv6 = 0; - rawAddressLength = IPAddressParserStatics.IPv4AddressBytes; + if (JustAddresses) + { + if (_result is Exception e) + { + _ipAddressArrayBuilder.SetException(e); + } + else + { + _ipAddressArrayBuilder.SetResult((IPAddress[])_result!); + } + } + else + { + if (_result is Exception e) + { + _ipHostEntryBuilder.SetException(e); + } + else + { + _ipHostEntryBuilder.SetResult((IPHostEntry)_result!); + } + } } - else + + public IntPtr CreateHandle() => GCHandle.ToIntPtr(GCHandle.Alloc(this, GCHandleType.Normal)); + + public static GetHostEntryForNameState FromHandleAndFree(IntPtr handle) { - isIPv6 = 1; - rawAddressLength = IPAddressParserStatics.IPv6AddressBytes; + GCHandle gCHandle = GCHandle.FromIntPtr(handle); + var state = (GetHostEntryForNameState)gCHandle.Target!; + gCHandle.Free(); + return state; } + } - byte* rawAddress = stackalloc byte[rawAddressLength]; - addr.TryWriteBytes(new Span(rawAddress, rawAddressLength), out int bytesWritten); - Debug.Assert(bytesWritten == rawAddressLength); + [StructLayout(LayoutKind.Sequential)] + private unsafe struct GetHostEntryForNameContext + { + public Interop.Sys.HostEntry Result; + public IntPtr State; - int error = Interop.Sys.GetNameInfo( - rawAddress, - (uint)rawAddressLength, - isIPv6, - buffer, - Interop.Sys.NI_MAXHOST, - null, - 0, - Interop.Sys.GetNameInfoFlags.NI_NAMEREQD); + public static GetHostEntryForNameContext* AllocateContext() + { + GetHostEntryForNameContext* context = (GetHostEntryForNameContext*)Marshal.AllocHGlobal(sizeof(GetHostEntryForNameContext)); + *context = default; + return context; + } - socketError = GetSocketErrorForNativeError(error); - nativeErrorCode = error; - return socketError == SocketError.Success ? Marshal.PtrToStringAnsi((IntPtr)buffer) : null; + public static void FreeContext(GetHostEntryForNameContext* context) => Marshal.FreeHGlobal((IntPtr)context); } - - public static string GetHostName() => Interop.Sys.GetHostName(); } } From ed5c39f65fccbc04b7b62ad7a7adfec09ec45354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 15 Dec 2020 18:17:27 +0100 Subject: [PATCH 18/32] Updated NameResolutionPalTests for AddressFamily --- .../tests/PalTests/NameResolutionPalTests.cs | 47 +++++++++---------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs index 37a9088137cce..e505318d8a8d7 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs @@ -3,6 +3,8 @@ using System.IO; using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Threading; using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; @@ -11,10 +13,11 @@ namespace System.Net.NameResolution.PalTests { public class NameResolutionPalTests { - private ITestOutputHelper _output; + private readonly ITestOutputHelper _output; public NameResolutionPalTests(ITestOutputHelper output) { + NameResolutionPal.EnsureSocketsAreInitialized(); _output = output; } @@ -32,7 +35,7 @@ private void LogUnixInfo() [InlineData(true)] public void TryGetAddrInfo_LocalHost(bool justAddresses) { - SocketError error = NameResolutionPal.TryGetAddrInfo("localhost", justAddresses, out string hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode); + SocketError error = NameResolutionPal.TryGetAddrInfo("localhost", justAddresses, AddressFamily.Unspecified, out string hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode); Assert.Equal(SocketError.Success, error); if (!justAddresses) { @@ -48,7 +51,14 @@ public void TryGetAddrInfo_LocalHost(bool justAddresses) [InlineData(true)] public void TryGetAddrInfo_EmptyHost(bool justAddresses) { - SocketError error = NameResolutionPal.TryGetAddrInfo("localhost", justAddresses, AddressFamily.Unspecified, out string hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode); + SocketError error = NameResolutionPal.TryGetAddrInfo("", justAddresses, AddressFamily.Unspecified, out string hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode); + if (error == SocketError.HostNotFound && (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))) + { + // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the + // machine configurations, which varies by distro and is often inconsistent. + return; + } + Assert.Equal(SocketError.Success, error); if (!justAddresses) { @@ -93,7 +103,7 @@ public void TryGetAddrInfo_ExternalHost(bool justAddresses) { string hostName = "microsoft.com"; - SocketError error = NameResolutionPal.TryGetAddrInfo(hostName, justAddresses, out hostName, out string[] aliases, out IPAddress[] addresses, out _); + SocketError error = NameResolutionPal.TryGetAddrInfo(hostName, justAddresses, AddressFamily.Unspecified, out hostName, out string[] aliases, out IPAddress[] addresses, out _); Assert.Equal(SocketError.Success, error); Assert.NotNull(aliases); Assert.NotNull(addresses); @@ -106,7 +116,7 @@ public void TryGetAddrInfo_ExternalHost(bool justAddresses) [OuterLoop("Uses external server")] public void TryGetAddrInfo_UnknownHost(bool justAddresses) { - SocketError error = NameResolutionPal.TryGetAddrInfo("test.123", justAddresses, out string? _, out string[] _, out IPAddress[] _, out int nativeErrorCode); + SocketError error = NameResolutionPal.TryGetAddrInfo("test.123", justAddresses, AddressFamily.Unspecified, out string? _, out string[] _, out IPAddress[] _, out int nativeErrorCode); Assert.Equal(SocketError.HostNotFound, error); Assert.NotEqual(0, nativeErrorCode); @@ -185,19 +195,6 @@ public void TryGetAddrInfo_HostName_TryGetNameInfo() Assert.NotNull(name); } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void TryGetAddrInfo_ExternalHost(bool justAddresses) - { - string hostName = "microsoft.com"; - - SocketError error = NameResolutionPal.TryGetAddrInfo(hostName, justAddresses, AddressFamily.Unspecified, out hostName, out string[] aliases, out IPAddress[] addresses, out _); - Assert.Equal(SocketError.Success, error); - Assert.NotNull(aliases); - Assert.NotNull(addresses); - } - [Theory] [InlineData(false)] [InlineData(true)] @@ -258,14 +255,14 @@ public async Task GetAddrInfoAsync_LocalHost(bool justAddresses) if (justAddresses) { - IPAddress[] addresses = await ((Task)NameResolutionPal.GetAddrInfoAsync("localhost", justAddresses)).ConfigureAwait(false); + IPAddress[] addresses = await ((Task)NameResolutionPal.GetAddrInfoAsync("localhost", justAddresses, AddressFamily.Unspecified, CancellationToken.None)).ConfigureAwait(false); Assert.NotNull(addresses); Assert.True(addresses.Length > 0); } else { - IPHostEntry hostEntry = await ((Task)NameResolutionPal.GetAddrInfoAsync("localhost", justAddresses)).ConfigureAwait(false); + IPHostEntry hostEntry = await ((Task)NameResolutionPal.GetAddrInfoAsync("localhost", justAddresses, AddressFamily.Unspecified, CancellationToken.None)).ConfigureAwait(false); Assert.NotNull(hostEntry); Assert.True(hostEntry.AddressList.Length > 0); @@ -283,7 +280,7 @@ public async Task GetAddrInfoAsync_EmptyHost(bool justAddresses) return; } - Task task = NameResolutionPal.GetAddrInfoAsync("", justAddresses); + Task task = NameResolutionPal.GetAddrInfoAsync("", justAddresses, AddressFamily.Unspecified, CancellationToken.None); try { @@ -333,7 +330,7 @@ public async Task GetAddrInfoAsync_HostName(bool justAddresses) string hostName = NameResolutionPal.GetHostName(); Assert.NotNull(hostName); - Task task = NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses); + Task task = NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses, AddressFamily.Unspecified, CancellationToken.None); try { @@ -388,14 +385,14 @@ public async Task GetAddrInfoAsync_ExternalHost(bool justAddresses) if (justAddresses) { - IPAddress[] addresses = await ((Task)NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses)).ConfigureAwait(false); + IPAddress[] addresses = await ((Task)NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses, AddressFamily.Unspecified, CancellationToken.None)).ConfigureAwait(false); Assert.NotNull(addresses); Assert.True(addresses.Length > 0); } else { - IPHostEntry hostEntry = await ((Task)NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses)).ConfigureAwait(false); + IPHostEntry hostEntry = await ((Task)NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses, AddressFamily.Unspecified, CancellationToken.None)).ConfigureAwait(false); Assert.NotNull(hostEntry); Assert.True(hostEntry.AddressList.Length > 0); @@ -415,7 +412,7 @@ public async Task GetAddrInfoAsync_UnknownHost(bool justAddresses) const string hostName = "test.123"; - SocketException socketException = await Assert.ThrowsAnyAsync(() => NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses)).ConfigureAwait(false); + SocketException socketException = await Assert.ThrowsAnyAsync(() => NameResolutionPal.GetAddrInfoAsync(hostName, justAddresses, AddressFamily.Unspecified, CancellationToken.None)).ConfigureAwait(false); SocketError socketError = socketException.SocketErrorCode; Assert.Equal(SocketError.HostNotFound, socketError); From b8efde5deaad8ed95ceb12cc8e6623307c0a04e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 15 Dec 2020 19:44:29 +0100 Subject: [PATCH 19/32] Removed EnsureSocketsAreInitialized On Unix this was a nop anyway, https://github.com/dotnet/runtime/pull/43284 removed it from Windows. --- .../src/System/Net/NameResolutionPal.Unix.cs | 2 -- .../tests/PalTests/NameResolutionPalTests.cs | 1 - 2 files changed, 3 deletions(-) diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index d5ccc87106be6..93e4950a63c39 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -17,8 +17,6 @@ internal static partial class NameResolutionPal public static bool SupportsGetAddrInfoAsync { get; } = Interop.Sys.PlatformSupportsGetAddrInfoAsync(); - public static void EnsureSocketsAreInitialized() { } // No-op for Unix - public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, AddressFamily addressFamily, out string? hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode) { Debug.Assert(name is not null); diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs index e505318d8a8d7..1c9954662c780 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs @@ -17,7 +17,6 @@ public class NameResolutionPalTests public NameResolutionPalTests(ITestOutputHelper output) { - NameResolutionPal.EnsureSocketsAreInitialized(); _output = output; } From 04dbd8eb7a67f25905f5349ec1f25fd39ca775a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 15 Dec 2020 20:28:41 +0100 Subject: [PATCH 20/32] Fixed bug at native side Seems like "hint" must last the async operation, so stack-only won't do it. Tests will crash with ``` The active test run was aborted. Reason: Test host process crashed : .../runtime/src/libraries/Native/Unix/System.Native/pal_networking.c (315): error -7: Unknown AddrInfo error flag. Unknown error -7 (0 failed) ``` If put into the heap, it succeeds. --- .../Native/Unix/System.Native/pal_networking.c | 12 ++++++------ .../src/System/Net/NameResolutionPal.Unix.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index 61838e371011f..9703f0c697c3f 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -510,6 +510,7 @@ struct GetAddrInfoAsyncState struct gaicb* gai_requests; struct sigevent sigevent; + struct addrinfo hint; HostEntry* entry; GetHostEntryForNameCallback callback; char address[]; @@ -595,11 +596,6 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, int32_t ad return GetAddrInfoErrorFlags_EAI_FAMILY; } - struct addrinfo hint; - memset(&hint, 0, sizeof(struct addrinfo)); - hint.ai_flags = AI_CANONNAME; - hint.ai_family = platformFamily; - struct GetAddrInfoAsyncState* state = malloc(sizeof(*state) + addrlen + 1); if (state == NULL) @@ -607,13 +603,17 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, int32_t ad return GetAddrInfoErrorFlags_EAI_MEMORY; } + memset(&state->hint, 0, sizeof(struct addrinfo)); + state->hint.ai_flags = AI_CANONNAME; + state->hint.ai_family = platformFamily; + memcpy(state->address, address, addrlen + 1); *state = (struct GetAddrInfoAsyncState) { .gai_request = { .ar_name = state->address, .ar_service = NULL, - .ar_request = &hint, + .ar_request = &state->hint, .ar_result = NULL }, .gai_requests = &state->gai_request, diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index 93e4950a63c39..ec07031e33e15 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -79,7 +79,7 @@ public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, return socketError == SocketError.Success ? Marshal.PtrToStringAnsi((IntPtr)buffer) : null; } - public static unsafe string GetHostName() => Interop.Sys.GetHostName(); + public static string GetHostName() => Interop.Sys.GetHostName(); public static unsafe Task GetAddrInfoAsync(string hostName, bool justAddresses, AddressFamily addressFamily, CancellationToken _) { From 37e4dd3491416484b7aefdb565bb5da6bcf7777a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 15 Dec 2020 21:54:57 +0100 Subject: [PATCH 21/32] Refactor setting of the address family into TrySetAddressFamily --- .../Unix/System.Native/pal_networking.c | 33 +++++++++++++------ 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index 9703f0c697c3f..357b7f5d85412 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -541,6 +541,22 @@ static void GetHostEntryForNameAsyncComplete(sigval_t context) } #endif +static bool TrySetAddressFamily(int32_t addressFamily, struct addrinfo* hint) +{ + sa_family_t platformFamily; + if (!TryConvertAddressFamilyPalToPlatform(addressFamily, &platformFamily)) + { + return false; + } + + memset(hint, 0, sizeof(struct addrinfo)); + + hint->ai_flags = AI_CANONNAME; + hint->ai_family = platformFamily; + + return true; +} + int32_t SystemNative_PlatformSupportsGetAddrInfoAsync() { return HAVE_GETADDRINFO_A; @@ -553,17 +569,12 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, int32_t address return GetAddrInfoErrorFlags_EAI_BADARG; } - sa_family_t platformFamily; - if (!TryConvertAddressFamilyPalToPlatform(addressFamily, &platformFamily)) + struct addrinfo hint; + if (!TrySetAddressFamily(addressFamily, &hint)) { return GetAddrInfoErrorFlags_EAI_FAMILY; } - struct addrinfo hint; - memset(&hint, 0, sizeof(struct addrinfo)); - hint.ai_flags = AI_CANONNAME; - hint.ai_family = platformFamily; - struct addrinfo* info = NULL; int result = getaddrinfo((const char*)address, NULL, &hint, &info); @@ -603,9 +614,11 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, int32_t ad return GetAddrInfoErrorFlags_EAI_MEMORY; } - memset(&state->hint, 0, sizeof(struct addrinfo)); - state->hint.ai_flags = AI_CANONNAME; - state->hint.ai_family = platformFamily; + if (!TrySetAddressFamily(addressFamily, &state->hint)) + { + free(state); + return GetAddrInfoErrorFlags_EAI_FAMILY; + } memcpy(state->address, address, addrlen + 1); From 71763785a759675111da7fee1468557e2c375a01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Tue, 15 Dec 2020 22:00:08 +0100 Subject: [PATCH 22/32] Fixed unused parameter warning / failure --- src/libraries/Native/Unix/System.Native/pal_networking.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/Native/Unix/System.Native/pal_networking.c b/src/libraries/Native/Unix/System.Native/pal_networking.c index 357b7f5d85412..5e0f37635d326 100644 --- a/src/libraries/Native/Unix/System.Native/pal_networking.c +++ b/src/libraries/Native/Unix/System.Native/pal_networking.c @@ -654,6 +654,7 @@ int32_t SystemNative_GetHostEntryForNameAsync(const uint8_t* address, int32_t ad return result; #else (void)address; + (void)addressFamily; (void)entry; (void)callback; From b64867b14ce0b345ef744230bc47bf72065efd21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Wed, 16 Dec 2020 17:20:28 +0100 Subject: [PATCH 23/32] Little cleanup --- .../System.Net.NameResolution/src/System/Net/Dns.cs | 2 -- .../src/System/Net/NameResolutionPal.Unix.cs | 3 ++- .../tests/FunctionalTests/LoggingTest.cs | 10 +++++----- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs b/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs index 623d741c890ef..abe462a51f591 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs @@ -527,8 +527,6 @@ private static Task GetHostEntryOrAddressesCoreAsync(string hostName, bool justR } else if (NameResolutionPal.SupportsGetAddrInfoAsync) { -#pragma warning disable CS0162 // Unreachable code detected -- SupportsGetAddrInfoAsync is a constant on *nix. - // If the OS supports it and 'hostName' is not an IP Address, resolve the name asynchronously // instead of calling the synchronous version in the ThreadPool. diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index ec07031e33e15..bb66d786a2f1c 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -144,7 +144,8 @@ private static unsafe void ProcessResult(SocketError errorCode, GetHostEntryForN } else { - state.SetResult(ExceptionDispatchInfo.SetCurrentStackTrace(new SocketException((int)errorCode))); + Exception ex = new SocketException((int)errorCode); + state.SetResult(ExceptionDispatchInfo.SetCurrentStackTrace(ex)); } } finally diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs index d8921a09a5581..4ce06febecdd5 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs @@ -42,7 +42,7 @@ public void GetHostEntry_InvalidHost_LogsError() try { Dns.GetHostEntry(Configuration.Sockets.InvalidHost); - throw new SkipTestException("GetHostEntry() should fail but it did not."); + throw new SkipTestException("GetHostEntry should fail but it did not."); } catch (SocketException e) when (e.SocketErrorCode == SocketError.HostNotFound) { @@ -53,7 +53,7 @@ public void GetHostEntry_InvalidHost_LogsError() } }); - Assert.True(events.Count() > 0); + Assert.True(events.Count > 0, "events.Count should be > 0"); foreach (EventWrittenEventArgs ev in events) { Assert.True(ev.Payload.Count >= 3); @@ -77,18 +77,18 @@ public void GetHostEntryAsync_InvalidHost_LogsError() try { Dns.GetHostEntryAsync(Configuration.Sockets.InvalidHost).GetAwaiter().GetResult(); - throw new SkipTestException("GetHostEntry() should fail but it did not."); + throw new SkipTestException("GetHostEntryAsync should fail but it did not."); } catch (SocketException e) when (e.SocketErrorCode == SocketError.HostNotFound) { } catch (Exception e) { - throw new SkipTestException($"GetHostEntry failed unexpectedly: {e.Message}"); + throw new SkipTestException($"GetHostEntryAsync failed unexpectedly: {e.Message}"); } }); - Assert.True(events.Count() > 0); + Assert.True(events.Count > 0, "events.Count should be > 0"); foreach (EventWrittenEventArgs ev in events) { Assert.True(ev.Payload.Count >= 3); From 900834ed89229c5394e81d4316fdc20fd3daa78d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Wed, 16 Dec 2020 18:29:55 +0100 Subject: [PATCH 24/32] Fixed (unrelated) test failures Cf. https://github.com/dotnet/runtime/pull/34633#issuecomment-746377633 --- .../src/System/Net/Dns.cs | 22 +++++++++++++++++-- .../tests/FunctionalTests/LoggingTest.cs | 2 ++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs b/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs index abe462a51f591..0ab7576a69e79 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/Dns.cs @@ -126,8 +126,26 @@ public static Task GetHostEntryAsync(string hostNameOrAddress, Addr if (NetEventSource.Log.IsEnabled()) { Task t = GetHostEntryCoreAsync(hostNameOrAddress, justReturnParsedIp: false, throwOnIIPAny: true, family, cancellationToken); - t.ContinueWith((t, s) => NetEventSource.Info((string)s!, $"{t.Result} with {((IPHostEntry)t.Result).AddressList.Length} entries"), - hostNameOrAddress, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Default); + t.ContinueWith(static (t, s) => + { + string hostNameOrAddress = (string)s!; + + if (t.Status == TaskStatus.RanToCompletion) + { + NetEventSource.Info(hostNameOrAddress, $"{t.Result} with {t.Result.AddressList.Length} entries"); + } + + Exception? ex = t.Exception?.InnerException; + + if (ex is SocketException soex) + { + NetEventSource.Error(hostNameOrAddress, $"{hostNameOrAddress} DNS lookup failed with {soex.ErrorCode}"); + } + else if (ex is OperationCanceledException) + { + NetEventSource.Error(hostNameOrAddress, $"{hostNameOrAddress} DNS lookup was canceled"); + } + }, hostNameOrAddress, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); return t; } else diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs index 4ce06febecdd5..e5f951e44ed7a 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs @@ -81,6 +81,8 @@ public void GetHostEntryAsync_InvalidHost_LogsError() } catch (SocketException e) when (e.SocketErrorCode == SocketError.HostNotFound) { + // Wait a bit to let the event source write it's log + Task.Delay(100).GetAwaiter().GetResult(); } catch (Exception e) { From 198717ef1bc89b5812225380ccc4e0561620588a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Wed, 16 Dec 2020 18:36:26 +0100 Subject: [PATCH 25/32] Made LoggingTest async instead of GetAwaiter().GetResult() --- .../tests/FunctionalTests/LoggingTest.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs index e5f951e44ed7a..dacc47eaae2af 100644 --- a/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs +++ b/src/libraries/System.Net.NameResolution/tests/FunctionalTests/LoggingTest.cs @@ -5,11 +5,9 @@ using System.Diagnostics.Tracing; using System.Linq; using System.Net.Sockets; -using System.Reflection; using System.Threading.Tasks; using Microsoft.DotNet.XUnitExtensions; using Xunit; -using Xunit.Abstractions; namespace System.Net.NameResolution.Tests { @@ -66,29 +64,29 @@ public void GetHostEntry_InvalidHost_LogsError() [ConditionalFact] [PlatformSpecific(~TestPlatforms.Windows)] // Unreliable on Windows. - public void GetHostEntryAsync_InvalidHost_LogsError() + public async Task GetHostEntryAsync_InvalidHost_LogsError() { using (var listener = new TestEventListener("Private.InternalDiagnostics.System.Net.NameResolution", EventLevel.Error)) { var events = new ConcurrentQueue(); - listener.RunWithCallback(ev => events.Enqueue(ev), () => + await listener.RunWithCallbackAsync(ev => events.Enqueue(ev), async () => { try { - Dns.GetHostEntryAsync(Configuration.Sockets.InvalidHost).GetAwaiter().GetResult(); + await Dns.GetHostEntryAsync(Configuration.Sockets.InvalidHost).ConfigureAwait(false); throw new SkipTestException("GetHostEntryAsync should fail but it did not."); } catch (SocketException e) when (e.SocketErrorCode == SocketError.HostNotFound) { // Wait a bit to let the event source write it's log - Task.Delay(100).GetAwaiter().GetResult(); + await Task.Delay(100).ConfigureAwait(false); } catch (Exception e) { throw new SkipTestException($"GetHostEntryAsync failed unexpectedly: {e.Message}"); } - }); + }).ConfigureAwait(false); Assert.True(events.Count > 0, "events.Count should be > 0"); foreach (EventWrittenEventArgs ev in events) From f85c316051f7ba53e54349620e09e034dbd1a812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Wed, 16 Dec 2020 20:55:00 +0100 Subject: [PATCH 26/32] Use function pointer --- .../src/Interop/Unix/System.Native/Interop.HostEntry.cs | 8 +++++--- .../src/System/Net/NameResolutionPal.Unix.cs | 5 ++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs index ada474e01f366..12c2c3941d224 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs @@ -32,8 +32,6 @@ internal unsafe struct HostEntry internal int IPAddressCount; // Number of IP addresses in the list } - internal unsafe delegate void GetHostEntryForNameCallback(HostEntry* entry, int status); - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PlatformSupportsGetAddrInfoAsync")] internal static extern bool PlatformSupportsGetAddrInfoAsync(); @@ -41,7 +39,11 @@ internal unsafe struct HostEntry internal static extern unsafe int GetHostEntryForName(string address, AddressFamily family, HostEntry* entry); [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetHostEntryForNameAsync")] - internal static extern unsafe int GetHostEntryForNameAsync(string address, AddressFamily family, HostEntry* entry, GetHostEntryForNameCallback callback); + internal static extern unsafe int GetHostEntryForNameAsync( + string address, + AddressFamily family, + HostEntry* entry, + delegate* unmanaged callback); [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FreeHostEntry")] internal static extern unsafe void FreeHostEntry(HostEntry* entry); diff --git a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index bb66d786a2f1c..27e41862d2dd4 100644 --- a/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/libraries/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -13,8 +13,6 @@ namespace System.Net { internal static partial class NameResolutionPal { - private static readonly unsafe Interop.Sys.GetHostEntryForNameCallback s_getHostEntryForNameCallback = GetHostEntryForNameCallback; - public static bool SupportsGetAddrInfoAsync { get; } = Interop.Sys.PlatformSupportsGetAddrInfoAsync(); public static unsafe SocketError TryGetAddrInfo(string name, bool justAddresses, AddressFamily addressFamily, out string? hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode) @@ -105,7 +103,7 @@ public static unsafe Task GetAddrInfoAsync(string hostName, bool justAddresses, throw; } - int errorCode = Interop.Sys.GetHostEntryForNameAsync(hostName, addressFamily, &context->Result, s_getHostEntryForNameCallback); + int errorCode = Interop.Sys.GetHostEntryForNameAsync(hostName, addressFamily, &context->Result, &GetHostEntryForNameCallback); if (errorCode != 0) { @@ -115,6 +113,7 @@ public static unsafe Task GetAddrInfoAsync(string hostName, bool justAddresses, return state.Task; } + [UnmanagedCallersOnly] private static unsafe void GetHostEntryForNameCallback(Interop.Sys.HostEntry* entry, int error) { // Can be casted directly to GetHostEntryForNameContext* because the HostEntry is its first field From 438c75a98daec32899d9bb18a7b4cd9f158f7b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Fri, 18 Dec 2020 11:11:01 +0100 Subject: [PATCH 27/32] Use OperatinngSystem instead of RuntimeInformation in tests --- .../tests/PalTests/NameResolutionPalTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs index 1c9954662c780..a7c1d20a82341 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs @@ -51,7 +51,7 @@ public void TryGetAddrInfo_LocalHost(bool justAddresses) public void TryGetAddrInfo_EmptyHost(bool justAddresses) { SocketError error = NameResolutionPal.TryGetAddrInfo("", justAddresses, AddressFamily.Unspecified, out string hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode); - if (error == SocketError.HostNotFound && (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))) + if (error == SocketError.HostNotFound && (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())) { // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the // machine configurations, which varies by distro and is often inconsistent. @@ -289,7 +289,7 @@ public async Task GetAddrInfoAsync_EmptyHost(bool justAddresses) { SocketError error = ex.SocketErrorCode; - if (error == SocketError.HostNotFound && (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))) + if (error == SocketError.HostNotFound && (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())) { // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the // machine configurations, which varies by distro and is often inconsistent. @@ -339,7 +339,7 @@ public async Task GetAddrInfoAsync_HostName(bool justAddresses) { SocketError error = ex.SocketErrorCode; - if (error == SocketError.HostNotFound && (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))) + if (error == SocketError.HostNotFound && (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())) { // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the // machine configurations, which varies by distro and is often inconsistent. From 33da8da9dd3c2ceced296876af8bd9189cc3741f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Fri, 18 Dec 2020 12:53:28 +0100 Subject: [PATCH 28/32] PR Feedback for CMake --- src/libraries/Native/Unix/System.Native/CMakeLists.txt | 4 ---- src/libraries/Native/Unix/System.Native/entrypoints.c | 2 ++ src/libraries/Native/Unix/System.Native/extra_libs.cmake | 4 ++++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/libraries/Native/Unix/System.Native/CMakeLists.txt b/src/libraries/Native/Unix/System.Native/CMakeLists.txt index e206db7bf994d..68f4df1f7ea17 100644 --- a/src/libraries/Native/Unix/System.Native/CMakeLists.txt +++ b/src/libraries/Native/Unix/System.Native/CMakeLists.txt @@ -72,10 +72,6 @@ if (GEN_SHARED_LIB) endif() install_with_stripped_symbols (System.Native PROGRAMS .) - - if (HAVE_GETADDRINFO_A) - target_link_libraries(System.Native anl) - endif () endif () add_library(System.Native-Static diff --git a/src/libraries/Native/Unix/System.Native/entrypoints.c b/src/libraries/Native/Unix/System.Native/entrypoints.c index 4a6c65ac505e4..309eef0c8643f 100644 --- a/src/libraries/Native/Unix/System.Native/entrypoints.c +++ b/src/libraries/Native/Unix/System.Native/entrypoints.c @@ -115,7 +115,9 @@ static const Entry s_sysNative[] = DllImportEntry(SystemNative_ReadEvents) DllImportEntry(SystemNative_CreateNetworkChangeListenerSocket) DllImportEntry(SystemNative_CloseNetworkChangeListenerSocket) + DllImportEntry(SystemNative_PlatformSupportsGetAddrInfoAsync) DllImportEntry(SystemNative_GetHostEntryForName) + DllImportEntry(SystemNative_GetHostEntryForNameAsync) DllImportEntry(SystemNative_FreeHostEntry) DllImportEntry(SystemNative_GetNameInfo) DllImportEntry(SystemNative_GetDomainName) diff --git a/src/libraries/Native/Unix/System.Native/extra_libs.cmake b/src/libraries/Native/Unix/System.Native/extra_libs.cmake index 0b3f8424d2954..7a04e35f46e30 100644 --- a/src/libraries/Native/Unix/System.Native/extra_libs.cmake +++ b/src/libraries/Native/Unix/System.Native/extra_libs.cmake @@ -13,4 +13,8 @@ macro(append_extra_system_libs NativeLibsExtra) if (CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) list(APPEND ${NativeLibsExtra} "-framework Foundation") endif () + + if (CLR_CMAKE_TARGET_LINUX AND HAVE_GETADDRINFO_A) + list(APPEND ${NativeLibsExtra} anl) + endif () endmacro() From fa9c6f90b7ef5033e3c30a46dfe34dc47d5ef91e Mon Sep 17 00:00:00 2001 From: Vladimir Sadov Date: Fri, 18 Dec 2020 10:17:50 -0800 Subject: [PATCH 29/32] Update src/libraries/Native/Unix/System.Native/extra_libs.cmake MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Günther Foidl --- src/libraries/Native/Unix/System.Native/extra_libs.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Native/extra_libs.cmake b/src/libraries/Native/Unix/System.Native/extra_libs.cmake index 7a04e35f46e30..1b890a684cf40 100644 --- a/src/libraries/Native/Unix/System.Native/extra_libs.cmake +++ b/src/libraries/Native/Unix/System.Native/extra_libs.cmake @@ -14,7 +14,7 @@ macro(append_extra_system_libs NativeLibsExtra) list(APPEND ${NativeLibsExtra} "-framework Foundation") endif () - if (CLR_CMAKE_TARGET_LINUX AND HAVE_GETADDRINFO_A) + if (CLR_CMAKE_TARGET_LINUX) list(APPEND ${NativeLibsExtra} anl) endif () endmacro() From 8266dd3dc8aa29935fd2b1083f59cff9e7dedc2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Fri, 8 Jan 2021 09:09:39 +0100 Subject: [PATCH 30/32] Revert "Update src/libraries/Native/Unix/System.Native/extra_libs.cmake" This reverts commit fa9c6f90b7ef5033e3c30a46dfe34dc47d5ef91e. --- src/libraries/Native/Unix/System.Native/extra_libs.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Native/Unix/System.Native/extra_libs.cmake b/src/libraries/Native/Unix/System.Native/extra_libs.cmake index 1b890a684cf40..7a04e35f46e30 100644 --- a/src/libraries/Native/Unix/System.Native/extra_libs.cmake +++ b/src/libraries/Native/Unix/System.Native/extra_libs.cmake @@ -14,7 +14,7 @@ macro(append_extra_system_libs NativeLibsExtra) list(APPEND ${NativeLibsExtra} "-framework Foundation") endif () - if (CLR_CMAKE_TARGET_LINUX) + if (CLR_CMAKE_TARGET_LINUX AND HAVE_GETADDRINFO_A) list(APPEND ${NativeLibsExtra} anl) endif () endmacro() From 75e52c7990599cac1d602a151c3f19d6186bde3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Fri, 8 Jan 2021 09:22:22 +0100 Subject: [PATCH 31/32] Another to build single file host Based on https://github.com/dotnet/runtime/pull/34633#issuecomment-756045320 --- .../corehost/cli/apphost/static/configure.cmake | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/installer/corehost/cli/apphost/static/configure.cmake b/src/installer/corehost/cli/apphost/static/configure.cmake index e3741d7e056a5..fe9e8a06a885f 100644 --- a/src/installer/corehost/cli/apphost/static/configure.cmake +++ b/src/installer/corehost/cli/apphost/static/configure.cmake @@ -1,4 +1,5 @@ include(CheckIncludeFiles) +include(CMakePushCheckState) check_include_files( GSS/GSS.h @@ -10,3 +11,16 @@ if (HeimdalGssApi) gssapi/gssapi.h HAVE_HEIMDAL_HEADERS) endif() + +if (CLR_CMAKE_TARGET_LINUX) + cmake_push_check_state(RESET) + set (CMAKE_REQUIRED_DEFINITIONS "-D_GNU_SOURCE") + set (CMAKE_REQUIRED_LIBRARIES "-lanl") + + check_symbol_exists( + getaddrinfo_a + netdb.h + HAVE_GETADDRINFO_A) + + cmake_pop_check_state() +endif () From dd1e2453c70d20d2be664c7f0041be7b724113e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=BCnther=20Foidl?= Date: Thu, 14 Jan 2021 14:49:30 +0100 Subject: [PATCH 32/32] Test for !Windows instead excluding several Unix-flavors Cf. https://github.com/dotnet/runtime/pull/34633#issuecomment-760205332 --- .../tests/PalTests/NameResolutionPalTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs index a7c1d20a82341..6bb0cf6cf5c60 100644 --- a/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs +++ b/src/libraries/System.Net.NameResolution/tests/PalTests/NameResolutionPalTests.cs @@ -51,7 +51,7 @@ public void TryGetAddrInfo_LocalHost(bool justAddresses) public void TryGetAddrInfo_EmptyHost(bool justAddresses) { SocketError error = NameResolutionPal.TryGetAddrInfo("", justAddresses, AddressFamily.Unspecified, out string hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode); - if (error == SocketError.HostNotFound && (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())) + if (error == SocketError.HostNotFound && !OperatingSystem.IsWindows()) { // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the // machine configurations, which varies by distro and is often inconsistent. @@ -78,7 +78,7 @@ public void TryGetAddrInfo_HostName(bool justAddresses) Assert.NotNull(hostName); SocketError error = NameResolutionPal.TryGetAddrInfo(hostName, justAddresses, AddressFamily.Unspecified, out hostName, out string[] aliases, out IPAddress[] addresses, out int nativeErrorCode); - if (error == SocketError.HostNotFound && (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())) + if (error == SocketError.HostNotFound && !OperatingSystem.IsWindows()) { // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the // machine configurations, which varies by distro and is often inconsistent. @@ -289,7 +289,7 @@ public async Task GetAddrInfoAsync_EmptyHost(bool justAddresses) { SocketError error = ex.SocketErrorCode; - if (error == SocketError.HostNotFound && (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())) + if (error == SocketError.HostNotFound && !OperatingSystem.IsWindows()) { // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the // machine configurations, which varies by distro and is often inconsistent. @@ -339,7 +339,7 @@ public async Task GetAddrInfoAsync_HostName(bool justAddresses) { SocketError error = ex.SocketErrorCode; - if (error == SocketError.HostNotFound && (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())) + if (error == SocketError.HostNotFound && !OperatingSystem.IsWindows()) { // On Unix, we are not guaranteed to be able to resove the local host. The ability to do so depends on the // machine configurations, which varies by distro and is often inconsistent.