Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ public struct NativeTcpConnectionInformation
public TcpState State;
}

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetEstimatedTcpConnectionCount")]
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetEstimatedTcpConnectionCount", SetLastError = true)]
public static partial int GetEstimatedTcpConnectionCount();

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetActiveTcpConnectionInfos")]
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetActiveTcpConnectionInfos", SetLastError = true)]
public static unsafe partial int GetActiveTcpConnectionInfos(NativeTcpConnectionInformation* infos, int* infoCount);

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetEstimatedUdpListenerCount")]
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetEstimatedUdpListenerCount", SetLastError = true)]
public static partial int GetEstimatedUdpListenerCount();

[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetActiveUdpListeners")]
[LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_GetActiveUdpListeners", SetLastError = true)]
public static unsafe partial int GetActiveUdpListeners(IPEndPointInfo* infos, int* infoCount);
}
}
Original file line number Diff line number Diff line change
@@ -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.Diagnostics.CodeAnalysis;
using System.Runtime.Versioning;

namespace System.Net.NetworkInformation
Expand All @@ -12,8 +13,7 @@ private unsafe TcpConnectionInformation[] GetTcpConnections(bool listeners)
int realCount = Interop.Sys.GetEstimatedTcpConnectionCount();
if (realCount == -1)
{
// The platform (e.g. OpenBSD) does not expose the TCP connection table.
throw new PlatformNotSupportedException(SR.net_InformationUnavailableOnPlatform);
ThrowExceptionForNativeError();
}

int infoCount = realCount * 2;
Expand All @@ -22,7 +22,7 @@ private unsafe TcpConnectionInformation[] GetTcpConnections(bool listeners)
{
if (Interop.Sys.GetActiveTcpConnectionInfos(infosPtr, &infoCount) == -1)
{
throw new NetworkInformationException(SR.net_PInvokeError);
ThrowExceptionForNativeError();
}
}

Expand Down Expand Up @@ -78,8 +78,7 @@ public override unsafe IPEndPoint[] GetActiveUdpListeners()
int realCount = Interop.Sys.GetEstimatedUdpListenerCount();
if (realCount == -1)
{
// The platform (e.g. OpenBSD) does not expose the UDP listener table.
throw new PlatformNotSupportedException(SR.net_InformationUnavailableOnPlatform);
ThrowExceptionForNativeError();
}

int infoCount = realCount * 2;
Expand All @@ -88,7 +87,7 @@ public override unsafe IPEndPoint[] GetActiveUdpListeners()
{
if (Interop.Sys.GetActiveUdpListeners(infosPtr, &infoCount) == -1)
{
throw new NetworkInformationException(SR.net_PInvokeError);
ThrowExceptionForNativeError();
}
}

Expand All @@ -107,6 +106,18 @@ public override unsafe IPEndPoint[] GetActiveUdpListeners()
return endPoints;
}

[DoesNotReturn]
private static void ThrowExceptionForNativeError()
{
Interop.ErrorInfo error = Interop.Sys.GetLastErrorInfo();
if (error.Error == Interop.Error.ENOTSUP)
{
throw new PlatformNotSupportedException(SR.net_InformationUnavailableOnPlatform);
}

throw new NetworkInformationException(error.RawErrno);
}

public override IcmpV4Statistics GetIcmpV4Statistics()
{
return new BsdIcmpV4Statistics();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,19 @@

using System.Collections.Generic;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.DotNet.RemoteExecutor;
using Microsoft.DotNet.XUnitExtensions;
using Xunit;
using Xunit.Abstractions;

namespace System.Net.NetworkInformation.Tests
{
public class IPGlobalPropertiesTest
public partial class IPGlobalPropertiesTest
{
private const int OperationNotPermitted = 1; // EPERM on macOS.
private const int RemoteTimeoutMilliseconds = 10_000;
private readonly ITestOutputHelper _log;

public static IEnumerable<object[]> Loopbacks()
Expand All @@ -32,6 +36,56 @@ public IPGlobalPropertiesTest(ITestOutputHelper output)
_log = output;
}

[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[PlatformSpecific(TestPlatforms.OSX)]
[InlineData(nameof(IPGlobalProperties.GetActiveTcpConnections), "pcbcount")]
[InlineData(nameof(IPGlobalProperties.GetActiveTcpConnections), "pcblist")]
[InlineData(nameof(IPGlobalProperties.GetActiveTcpListeners), "pcbcount")]
[InlineData(nameof(IPGlobalProperties.GetActiveTcpListeners), "pcblist")]
[InlineData(nameof(IPGlobalProperties.GetActiveUdpListeners), "pcbcount")]
[InlineData(nameof(IPGlobalProperties.GetActiveUdpListeners), "pcblist")]
public void IPGlobalProperties_SysctlDenied_ThrowsNetworkInformationException(string methodName, string queryName)
{
// This uses macOS's deprecated raw-profile sandbox API and requires an unsandboxed test host.
// A fresh child isolates the irreversible restriction but cannot escape an inherited sandbox.
RemoteExecutor.Invoke(static (methodName, queryName) =>
{
string protocol = methodName == nameof(IPGlobalProperties.GetActiveUdpListeners) ? "udp" : "tcp";
string profile = $"(version 1)(allow default)(deny sysctl-read (sysctl-name \"net.inet.{protocol}.{queryName}\"))";
int result = SandboxInit(profile, 0, out nint errorBuffer);
try
{
Assert.True(result == 0, Marshal.PtrToStringUTF8(errorBuffer));
}
finally
{
if (errorBuffer != 0)
{
SandboxFreeError(errorBuffer);
}
}

IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
Func<Array> query = methodName switch
{
nameof(IPGlobalProperties.GetActiveTcpConnections) => properties.GetActiveTcpConnections,
nameof(IPGlobalProperties.GetActiveTcpListeners) => properties.GetActiveTcpListeners,
nameof(IPGlobalProperties.GetActiveUdpListeners) => properties.GetActiveUdpListeners,
_ => throw new InvalidOperationException(methodName)
};

Marshal.SetLastPInvokeError(0);
NetworkInformationException exception = Assert.Throws<NetworkInformationException>(() => query());
Assert.Equal(OperationNotPermitted, exception.ErrorCode);
}, methodName, queryName, new RemoteInvokeOptions { TimeOut = RemoteTimeoutMilliseconds }).Dispose();
}

[LibraryImport("/usr/lib/libsandbox.dylib", EntryPoint = "sandbox_init", StringMarshalling = StringMarshalling.Utf8)]
private static partial int SandboxInit(string profile, ulong flags, out nint errorBuffer);

[LibraryImport("/usr/lib/libsandbox.dylib", EntryPoint = "sandbox_free_error")]
private static partial void SandboxFreeError(nint errorBuffer);

[Fact]
[SkipOnPlatform(TestPlatforms.Android, "Expected behavior is different on Android")]
[SkipOnPlatform(TestPlatforms.OpenBSD, "TCP/UDP connection enumeration is unsupported on OpenBSD")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
<StringResourcesPath>../../src/Resources/Strings.resx</StringResourcesPath>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<IgnoreForCI Condition="'$(TargetOS)' == 'browser' or '$(TargetOS)' == 'wasi'">true</IgnoreForCI>
Expand Down
Loading
Loading