Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Return all local IPs on Linux #41764

Merged
merged 18 commits into from
Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions src/Common/src/Interop/Unix/System.Native/Interop.HostEntry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
196 changes: 155 additions & 41 deletions src/Native/Unix/System.Native/pal_networking.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,9 @@
#elif HAVE_SENDFILE_6
#include <sys/uio.h>
#endif
#if !HAVE_IN_PKTINFO
#if HAVE_GETIFADDRS
#include <ifaddrs.h>
#endif
#endif
#if HAVE_LINUX_CAN_H
#include <linux/can.h>
#endif
Expand Down Expand Up @@ -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:
Expand All @@ -230,20 +230,49 @@ 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)
{
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)
{
Expand All @@ -252,16 +281,21 @@ 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)
{
// 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);
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
if (entry->CanonicalName == NULL)
{
ret = ConvertGetAddrInfoAndGetNameInfoErrorsToPal(EAI_MEMORY);
goto cleanup;
}
}

if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6)
Expand All @@ -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;
}
}
ManickaP marked this conversation as resolved.
Show resolved Hide resolved

#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;
}
}
ManickaP marked this conversation as resolved.
Show resolved Hide resolved
}
#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;
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/Native/Unix/System.Native/pal_networking.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@
<Link>Interop\Unix\System.Native\Interop.HostEntries.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.IPAddress.cs">
<Link>Interop\Unix\System.Native\Interop.HostEntries.cs</Link>
<Link>Interop\Unix\System.Native\Interop.IPAddress.cs</Link>
</Compile>
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.Socket.cs">
<Link>Interop\Unix\System.Native\Interop.Socket.cs</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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];
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down