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 17 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
174 changes: 130 additions & 44 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,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)
Expand All @@ -252,16 +275,20 @@ 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)
{
ConvertGetAddrInfoAndGetNameInfoErrorsToPal(EAI_MEMORY);
ManickaP marked this conversation as resolved.
Show resolved Hide resolved
}
}

if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6)
Expand All @@ -270,64 +297,123 @@ int32_t SystemNative_GetHostEntryForName(const uint8_t* address, HostEntry* entr
}
}

return GetAddrInfoErrorFlags_EAI_SUCCESS;
}

static int32_t GetNextIPAddressFromAddrInfo(struct addrinfo** info, IPAddress* endPoint)
{
assert(info != NULL);
assert(endPoint != NULL);
#if HAVE_GETIFADDRS
char name[_POSIX_HOST_NAME_MAX];
result = gethostname((char*)name, _POSIX_HOST_NAME_MAX);
bool includeIPv4Loopback = true;
bool includeIPv6Loopback = true;

for (struct addrinfo* ai = *info; ai != NULL; ai = ai->ai_next)
struct ifaddrs* addrs = NULL;
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;
}

case AF_INET6:
{
struct sockaddr_in6* inet6SockAddr = (struct sockaddr_in6*)ai->ai_addr;
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;
}

ConvertIn6AddrToByteArray(endPoint->Address, NUM_BYTES_IN_IPV6_ADDRESS, &inet6SockAddr->sin6_addr);
endPoint->IsIPv6 = 1;
endPoint->ScopeId = inet6SockAddr->sin6_scope_id;
break;
entry->IPAddressCount++;
}

if (ifa->ifa_addr->sa_family == AF_INET6)
ManickaP marked this conversation as resolved.
Show resolved Hide resolved
{
// 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

default:
// Skip non-IPv4 and non-IPv6 addresses
continue;
if (entry->IPAddressCount > 0)
{
entry->IPAddressList = (IPAddress*)calloc((size_t)entry->IPAddressCount, sizeof(IPAddress));
if (entry->IPAddressList == NULL)
{
ConvertGetAddrInfoAndGetNameInfoErrorsToPal(EAI_MEMORY);
ManickaP marked this conversation as resolved.
Show resolved Hide resolved
}

*info = ai->ai_next;
return GetAddrInfoErrorFlags_EAI_SUCCESS;
}
IPAddress* ipAddressList = entry->IPAddressList;

return GetAddrInfoErrorFlags_EAI_NOMORE;
}
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
freeaddrinfo(info);
ManickaP marked this conversation as resolved.
Show resolved Hide resolved

int32_t SystemNative_GetNextIPAddress(const HostEntry* hostEntry, struct addrinfo** addressListHandle, IPAddress* endPoint)
{
if (hostEntry == NULL || addressListHandle == NULL || endPoint == NULL)
{
return GetAddrInfoErrorFlags_EAI_BADARG;
#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;
}

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

if (CopySockAddrToIPAddress(ifa->ifa_addr, ifa->ifa_addr->sa_family, ipAddressList) == 0)
{
++ipAddressList;
}
}
ManickaP marked this conversation as resolved.
Show resolved Hide resolved
freeifaddrs(addrs);
}
#endif
}
return GetNextIPAddressFromAddrInfo(addressListHandle, endPoint);

return GetAddrInfoErrorFlags_EAI_SUCCESS;
}

void SystemNative_FreeHostEntry(HostEntry* entry)
{
if (entry != NULL)
{
freeaddrinfo(entry->AddressListHandle);
{
free(entry->IPAddressList);
free(entry->CanonicalName);
}
}

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 @@ -70,19 +72,15 @@ private static unsafe void ParseHostEntry(Interop.Sys.HostEntry hostEntry, bool
// is likely to involve extra allocations, hashing, etc., and so will probably be more expensive than
// this one in the typical (short list) case.

var nativeAddresses = new Interop.Sys.IPAddress[hostEntry.IPAddressCount];
Interop.Sys.IPAddress[] nativeAddresses = new Interop.Sys.IPAddress[hostEntry.IPAddressCount];
ManickaP marked this conversation as resolved.
Show resolved Hide resolved
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