diff --git a/src/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs b/src/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs index 06fda65b62cc..4f36e43edb17 100644 --- a/src/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs +++ b/src/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs @@ -21,29 +21,21 @@ internal enum GetAddrInfoErrorFlags : int EAI_NONAME = 5, // NAME or SERVICE is unknown. EAI_BADARG = 6, // One or more input arguments were invalid. EAI_NOMORE = 7, // No more entries are present in the list. - } - - //opaque structure to maintain consistency with native function signature - internal unsafe struct addrinfo - { - + EAI_MEMORY = 8, // Out of memory. } [StructLayout(LayoutKind.Sequential)] internal unsafe struct HostEntry { - internal byte* CanonicalName; // Canonical Name of the Host - internal byte** Aliases; // List of aliases for the host - internal addrinfo* AddressListHandle; // Handle for socket address list - internal int IPAddressCount; // Number of IP addresses in the list + internal byte* CanonicalName; // Canonical Name of the Host + internal byte** Aliases; // List of aliases for the host + internal IPAddress* IPAddressList; // Handle for socket address list + internal int IPAddressCount; // Number of IP addresses in the list } [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetHostEntryForName")] internal static extern unsafe int GetHostEntryForName(string address, HostEntry* entry); - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetNextIPAddress")] - internal static extern unsafe int GetNextIPAddress(HostEntry* entry, addrinfo** addressListHandle, IPAddress* endPoint); - [DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_FreeHostEntry")] internal static extern unsafe void FreeHostEntry(HostEntry* entry); } diff --git a/src/Native/Unix/System.Native/pal_networking.c b/src/Native/Unix/System.Native/pal_networking.c index 8ee0569eefa9..60aac3ae5366 100644 --- a/src/Native/Unix/System.Native/pal_networking.c +++ b/src/Native/Unix/System.Native/pal_networking.c @@ -50,11 +50,9 @@ #elif HAVE_SENDFILE_6 #include #endif -#if !HAVE_IN_PKTINFO #if HAVE_GETIFADDRS #include #endif -#endif #if HAVE_LINUX_CAN_H #include #endif @@ -219,6 +217,8 @@ static int32_t ConvertGetAddrInfoAndGetNameInfoErrorsToPal(int32_t error) #endif case EAI_FAMILY: return GetAddrInfoErrorFlags_EAI_FAMILY; + case EAI_MEMORY: + return GetAddrInfoErrorFlags_EAI_MEMORY; case EAI_NONAME: #ifdef EAI_NODATA case EAI_NODATA: @@ -230,6 +230,29 @@ static int32_t ConvertGetAddrInfoAndGetNameInfoErrorsToPal(int32_t error) return -1; } +static int32_t CopySockAddrToIPAddress(sockaddr* addr, sa_family_t family, IPAddress* ipAddress) +{ + if (family == AF_INET) + { + struct sockaddr_in* inetSockAddr = (struct sockaddr_in*)addr; + + ConvertInAddrToByteArray(ipAddress->Address, NUM_BYTES_IN_IPV4_ADDRESS, &inetSockAddr->sin_addr); + ipAddress->IsIPv6 = 0; + return 0; + } + else if (family == AF_INET6) + { + struct sockaddr_in6* inet6SockAddr = (struct sockaddr_in6*)addr; + + ConvertIn6AddrToByteArray(ipAddress->Address, NUM_BYTES_IN_IPV6_ADDRESS, &inet6SockAddr->sin6_addr); + ipAddress->IsIPv6 = 1; + ipAddress->ScopeId = inet6SockAddr->sin6_scope_id; + return 0; + } + + return -1; +} + int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entry) { if (address == NULL || entry == NULL) @@ -237,13 +260,19 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entr 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; - struct addrinfo* info = NULL; int result = getaddrinfo((const char*)address, NULL, &hint, &info); if (result != 0) { @@ -252,8 +281,8 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entr entry->CanonicalName = NULL; entry->Aliases = NULL; - entry->AddressListHandle = info; - entry->IPAddressCount = 0; + entry->IPAddressList = NULL; + entry->IPAddressCount = 0; // Find the canonical name for this host (if any) and count the number of IP end points. for (struct addrinfo* ai = info; ai != NULL; ai = ai->ai_next) @@ -261,7 +290,12 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entr // If we haven't found a canonical name yet and this addrinfo has one, copy it if ((entry->CanonicalName == NULL) && (ai->ai_canonname != NULL)) { - entry->CanonicalName = (uint8_t*)ai->ai_canonname; + entry->CanonicalName = (uint8_t*)strdup(ai->ai_canonname); + if (entry->CanonicalName == NULL) + { + ret = ConvertGetAddrInfoAndGetNameInfoErrorsToPal(EAI_MEMORY); + goto cleanup; + } } if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) @@ -270,64 +304,144 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entr } } - return GetAddrInfoErrorFlags_EAI_SUCCESS; -} +#if HAVE_GETIFADDRS + char name[_POSIX_HOST_NAME_MAX]; + result = gethostname((char*)name, _POSIX_HOST_NAME_MAX); -static int32_t GetNextIPAddressFromAddrInfo(struct addrinfo** info, IPAddress* endPoint) -{ - assert(info != NULL); - assert(endPoint != NULL); + bool includeIPv4Loopback = true; + bool includeIPv6Loopback = true; - for (struct addrinfo* ai = *info; ai != NULL; ai = ai->ai_next) + if (result == 0 && strcasecmp((const char*)address, name) == 0) { - switch (ai->ai_family) + // Get all interface addresses if the host name corresponds to the local host. + result = getifaddrs(&addrs); + + // If getifaddrs fails, just skip it, the data are not crucial for the result. + if (result == 0) { - case AF_INET: + // Count the number of IP end points. + for (struct ifaddrs* ifa = addrs; ifa != NULL; ifa = ifa->ifa_next) { - struct sockaddr_in* inetSockAddr = (struct sockaddr_in*)ai->ai_addr; + if (ifa->ifa_addr == NULL) + { + continue; + } - ConvertInAddrToByteArray(endPoint->Address, NUM_BYTES_IN_IPV4_ADDRESS, &inetSockAddr->sin_addr); - endPoint->IsIPv6 = 0; - break; + // Skip the interface if it isn't UP. + if ((ifa->ifa_flags & IFF_UP) == 0) + { + continue; + } + + if (ifa->ifa_addr->sa_family == AF_INET) + { + // Remember if there's at least one non-loopback address for IPv4, so that they will be skipped. + if ((ifa->ifa_flags & IFF_LOOPBACK) == 0) + { + includeIPv4Loopback = false; + } + + entry->IPAddressCount++; + } + else if (ifa->ifa_addr->sa_family == AF_INET6) + { + // Remember if there's at least one non-loopback address for IPv6, so that they will be skipped. + if ((ifa->ifa_flags & IFF_LOOPBACK) == 0) + { + includeIPv6Loopback = false; + } + + entry->IPAddressCount++; + } } + } + } +#endif - case AF_INET6: - { - struct sockaddr_in6* inet6SockAddr = (struct sockaddr_in6*)ai->ai_addr; + if (entry->IPAddressCount > 0) + { + entry->IPAddressList = (IPAddress*)calloc((size_t)entry->IPAddressCount, sizeof(IPAddress)); + if (entry->IPAddressList == NULL) + { + ret = ConvertGetAddrInfoAndGetNameInfoErrorsToPal(EAI_MEMORY); + goto cleanup; + } - ConvertIn6AddrToByteArray(endPoint->Address, NUM_BYTES_IN_IPV6_ADDRESS, &inet6SockAddr->sin6_addr); - endPoint->IsIPv6 = 1; - endPoint->ScopeId = inet6SockAddr->sin6_scope_id; - break; + IPAddress* ipAddressList = entry->IPAddressList; + + for (struct addrinfo* ai = info; ai != NULL; ai = ai->ai_next) + { + if (CopySockAddrToIPAddress(ai->ai_addr, (sa_family_t)ai->ai_family, ipAddressList) == 0) + { + ++ipAddressList; } + } + +#if HAVE_GETIFADDRS + if (addrs != NULL) + { + for (struct ifaddrs* ifa = addrs; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == NULL) + { + continue; + } + + // Skip the interface if it isn't UP. + if ((ifa->ifa_flags & IFF_UP) == 0) + { + continue; + } - default: - // Skip non-IPv4 and non-IPv6 addresses - continue; + // Skip loopback addresses if at least one interface has non-loopback one. + if ((!includeIPv4Loopback && ifa->ifa_addr->sa_family == AF_INET && (ifa->ifa_flags & IFF_LOOPBACK) != 0) || + (!includeIPv6Loopback && ifa->ifa_addr->sa_family == AF_INET6 && (ifa->ifa_flags & IFF_LOOPBACK) != 0)) + { + entry->IPAddressCount--; + continue; + } + + if (CopySockAddrToIPAddress(ifa->ifa_addr, ifa->ifa_addr->sa_family, ipAddressList) == 0) + { + ++ipAddressList; + } + } } +#endif + } - *info = ai->ai_next; - return GetAddrInfoErrorFlags_EAI_SUCCESS; +cleanup: + if (info != NULL) + { + freeaddrinfo(info); } - return GetAddrInfoErrorFlags_EAI_NOMORE; -} +#if HAVE_GETIFADDRS + if (addrs != NULL) + { + freeifaddrs(addrs); + } +#endif -int32_t SystemNative_GetNextIPAddress(const HostEntry* hostEntry, struct addrinfo** addressListHandle, IPAddress* endPoint) -{ - if (hostEntry == NULL || addressListHandle == NULL || endPoint == NULL) + // If the returned code is not success, the FreeHostEntry will not be called from the managed code. + if (ret != GetAddrInfoErrorFlags_EAI_SUCCESS) { - return GetAddrInfoErrorFlags_EAI_BADARG; + SystemNative_FreeHostEntry(entry); } - - return GetNextIPAddressFromAddrInfo(addressListHandle, endPoint); + + return ret; } void SystemNative_FreeHostEntry(HostEntry* entry) { if (entry != NULL) - { - freeaddrinfo(entry->AddressListHandle); + { + free(entry->CanonicalName); + free(entry->IPAddressList); + + entry->CanonicalName = NULL; + entry->IPAddressList = NULL; + entry->IPAddressCount = 0; } } diff --git a/src/Native/Unix/System.Native/pal_networking.h b/src/Native/Unix/System.Native/pal_networking.h index 4a51cce60f07..e92f39a0f07f 100644 --- a/src/Native/Unix/System.Native/pal_networking.h +++ b/src/Native/Unix/System.Native/pal_networking.h @@ -22,6 +22,7 @@ typedef enum GetAddrInfoErrorFlags_EAI_NONAME = 5, // NAME or SERVICE is unknown. GetAddrInfoErrorFlags_EAI_BADARG = 6, // One or more input arguments were invalid. GetAddrInfoErrorFlags_EAI_NOMORE = 7, // No more entries are present in the list. + GetAddrInfoErrorFlags_EAI_MEMORY = 8, // Out of memory. } GetAddrInfoErrorFlags; /** @@ -249,10 +250,10 @@ typedef struct typedef struct { - uint8_t* CanonicalName; // Canonical name of the host - uint8_t** Aliases; // List of aliases for the host - struct addrinfo* AddressListHandle; // Handle for host socket addresses - int32_t IPAddressCount; // Number of IP end points in the list + uint8_t* CanonicalName; // Canonical name of the host + uint8_t** Aliases; // List of aliases for the host + IPAddress* IPAddressList; // Handle for host socket addresses + int32_t IPAddressCount; // Number of IP end points in the socket address list } HostEntry; typedef struct @@ -311,10 +312,9 @@ typedef struct DLLEXPORT int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entry); -DLLEXPORT int32_t SystemNative_GetNextIPAddress(const HostEntry* entry, struct addrinfo** addressListHandle, IPAddress* endPoint); - DLLEXPORT void SystemNative_FreeHostEntry(HostEntry* entry); + DLLEXPORT int32_t SystemNative_GetNameInfo(const uint8_t* address, int32_t addressLength, int8_t isIPv6, diff --git a/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj b/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj index 1a9e6a12d480..b5de51756e9e 100644 --- a/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj +++ b/src/System.Net.NameResolution/src/System.Net.NameResolution.csproj @@ -147,7 +147,7 @@ Interop\Unix\System.Native\Interop.HostEntries.cs - Interop\Unix\System.Native\Interop.HostEntries.cs + Interop\Unix\System.Native\Interop.IPAddress.cs Interop\Unix\System.Native\Interop.Socket.cs diff --git a/src/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs b/src/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs index 47a6a2055041..ff923551b831 100644 --- a/src/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs +++ b/src/System.Net.NameResolution/src/System/Net/NameResolutionPal.Unix.cs @@ -38,6 +38,8 @@ private static SocketError GetSocketErrorForNativeError(int error) return SocketError.AddressFamilyNotSupported; case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_NONAME: return SocketError.HostNotFound; + case (int)Interop.Sys.GetAddrInfoErrorFlags.EAI_MEMORY: + throw new OutOfMemoryException(); default: Debug.Fail("Unexpected error: " + error.ToString()); return SocketError.SocketError; @@ -73,16 +75,12 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool var nativeAddresses = new Interop.Sys.IPAddress[hostEntry.IPAddressCount]; int nativeAddressCount = 0; - Interop.Sys.addrinfo* addressListHandle = hostEntry.AddressListHandle; + Interop.Sys.IPAddress* addressHandle = hostEntry.IPAddressList; for (int i = 0; i < hostEntry.IPAddressCount; i++) { - Interop.Sys.IPAddress nativeIPAddress = default; - int err = Interop.Sys.GetNextIPAddress(&hostEntry, &addressListHandle, &nativeIPAddress); - Debug.Assert(err == 0); - - if (Array.IndexOf(nativeAddresses, nativeIPAddress, 0, nativeAddressCount) == -1) + if (Array.IndexOf(nativeAddresses, addressHandle[i], 0, nativeAddressCount) == -1) { - nativeAddresses[nativeAddressCount++] = nativeIPAddress; + nativeAddresses[nativeAddressCount++] = addressHandle[i]; } }