diff --git a/src/libraries/System.Net.Ping/src/Resources/Strings.resx b/src/libraries/System.Net.Ping/src/Resources/Strings.resx index 1a1c7112b5d30d..a2a8a43068beb6 100644 --- a/src/libraries/System.Net.Ping/src/Resources/Strings.resx +++ b/src/libraries/System.Net.Ping/src/Resources/Strings.resx @@ -87,4 +87,7 @@ System.Net.Ping is not supported on this platform. - \ No newline at end of file + + Unable to send custom ping payload. Run program under privileged user account or grant cap_net_raw capability using setcap(8). + + diff --git a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.PingUtility.cs b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.PingUtility.cs index 74e85ca493a24e..dc9f5d2124633e 100644 --- a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.PingUtility.cs +++ b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.PingUtility.cs @@ -24,6 +24,14 @@ private Process GetPingProcess(IPAddress address, byte[] buffer, int timeout, Pi throw new PlatformNotSupportedException(SR.net_ping_utility_not_found); } + // although the ping utility supports custom pattern via -p option, it supports + // specifying only up to 16B pattern which repeats in the payload. The option also might + // not be present in all distributions, so we forbid ping payload in general. + if (buffer != DefaultSendBuffer && buffer != Array.Empty()) + { + throw new PlatformNotSupportedException(SR.net_ping_utility_custom_payload); + } + UnixCommandLinePing.PingFragmentOptions fragmentOption = UnixCommandLinePing.PingFragmentOptions.Default; if (options != null && address.AddressFamily == AddressFamily.InterNetwork) { diff --git a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs index fcb658a462c2c7..51bde54c68e3a1 100644 --- a/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs +++ b/src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.cs @@ -227,7 +227,7 @@ public PingReply Send(IPAddress address, int timeout, byte[] buffer, PingOptions { return SendPingCore(addressSnapshot, buffer, timeout, options); } - catch (Exception e) + catch (Exception e) when (e is not PlatformNotSupportedException) { throw new PingException(SR.net_ping, e); } @@ -336,7 +336,7 @@ private async Task SendPingAsyncInternal(IPAddress address, int timeo Task pingReplyTask = SendPingAsyncCore(addressSnapshot, buffer, timeout, options); return await pingReplyTask.ConfigureAwait(false); } - catch (Exception e) + catch (Exception e) when (e is not PlatformNotSupportedException) { throw new PingException(SR.net_ping, e); } @@ -388,7 +388,7 @@ private PingReply GetAddressAndSend(string hostNameOrAddress, int timeout, byte[ IPAddress[] addresses = Dns.GetHostAddresses(hostNameOrAddress); return SendPingCore(addresses[0], buffer, timeout, options); } - catch (Exception e) + catch (Exception e) when (e is not PlatformNotSupportedException) { throw new PingException(SR.net_ping, e); } @@ -407,7 +407,7 @@ private async Task GetAddressAndSendAsync(string hostNameOrAddress, i Task pingReplyTask = SendPingAsyncCore(addresses[0], buffer, timeout, options); return await pingReplyTask.ConfigureAwait(false); } - catch (Exception e) + catch (Exception e) when (e is not PlatformNotSupportedException) { throw new PingException(SR.net_ping, e); } diff --git a/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs b/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs index ebd7d44bb49438..57a0a33c44609a 100644 --- a/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs +++ b/src/libraries/System.Net.Ping/tests/FunctionalTests/PingTest.cs @@ -6,8 +6,6 @@ using System.Linq; using System.Net.Sockets; using System.Net.Test.Common; -using System.Runtime.InteropServices; -using System.Threading; using System.Threading.Tasks; using Microsoft.DotNet.RemoteExecutor; @@ -74,6 +72,16 @@ private static void PingResultValidator(PingReply pingReply, IPAddress[] localIp Assert.Contains(pingReply.Address, localIpAddresses); ///, "Reply address {pingReply.Address} is not expected local address."); } + private static byte[] GetPingPayload(AddressFamily addressFamily) + // On Unix, Non-root processes cannot send arbitrary data in the ping packet payload + => Capability.CanUseRawSockets(addressFamily) || PlatformDetection.IsOSXLike + ? TestSettings.PayloadAsBytes + : Array.Empty(); + + public static bool DoesNotUsePingUtility => !UsesPingUtility; + + public static bool UsesPingUtility => OperatingSystem.IsLinux() && !Capability.CanUseRawSockets(TestSettings.GetLocalIPAddress().AddressFamily); + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task SendPingAsync_InvalidArgs() { @@ -220,12 +228,11 @@ await SendBatchPingAsync( }); } - [PlatformSpecific(TestPlatforms.Windows)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. [Fact] public void SendPingWithIPAddressAndTimeoutAndBuffer() { - byte[] buffer = TestSettings.PayloadAsBytes; IPAddress localIpAddress = TestSettings.GetLocalIPAddress(); + byte[] buffer = GetPingPayload(localIpAddress.AddressFamily); SendBatchPing( (ping) => ping.Send(localIpAddress, TestSettings.PingTimeout, buffer), @@ -236,12 +243,11 @@ public void SendPingWithIPAddressAndTimeoutAndBuffer() }); } - [PlatformSpecific(TestPlatforms.Windows)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task SendPingAsyncWithIPAddressAndTimeoutAndBuffer() { - byte[] buffer = TestSettings.PayloadAsBytes; IPAddress localIpAddress = await TestSettings.GetLocalIPAddressAsync(); + byte[] buffer = GetPingPayload(localIpAddress.AddressFamily); await SendBatchPingAsync( (ping) => ping.SendPingAsync(localIpAddress, TestSettings.PingTimeout, buffer), @@ -252,57 +258,7 @@ await SendBatchPingAsync( }); } - [PlatformSpecific(TestPlatforms.AnyUnix)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - [Fact] - public void SendPingWithIPAddressAndTimeoutAndBuffer_Unix() - { - byte[] buffer = TestSettings.PayloadAsBytes; - IPAddress localIpAddress = TestSettings.GetLocalIPAddress(); - - SendBatchPing( - (ping) => ping.Send(localIpAddress, TestSettings.PingTimeout, buffer), - (pingReply) => - { - PingResultValidator(pingReply, localIpAddress); - - // Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - if (Capability.CanUseRawSockets(localIpAddress.AddressFamily) || PlatformDetection.IsOSXLike) - { - Assert.Equal(buffer, pingReply.Buffer); - } - else - { - Assert.Equal(Array.Empty(), pingReply.Buffer); - } - }); - } - - [PlatformSpecific(TestPlatforms.AnyUnix)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - public async Task SendPingAsyncWithIPAddressAndTimeoutAndBuffer_Unix() - { - byte[] buffer = TestSettings.PayloadAsBytes; - IPAddress localIpAddress = await TestSettings.GetLocalIPAddressAsync(); - - await SendBatchPingAsync( - (ping) => ping.SendPingAsync(localIpAddress, TestSettings.PingTimeout, buffer), - (pingReply) => - { - PingResultValidator(pingReply, localIpAddress); - - // Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - if (Capability.CanUseRawSockets(localIpAddress.AddressFamily) || PlatformDetection.IsOSXLike) - { - Assert.Equal(buffer, pingReply.Buffer); - } - else - { - Assert.Equal(Array.Empty(), pingReply.Buffer); - } - }); - } - - [PlatformSpecific(TestPlatforms.Windows)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. + [PlatformSpecific(TestPlatforms.Windows)] [Fact] public void SendPingWithIPAddressAndTimeoutAndBufferAndPingOptions() { @@ -320,7 +276,7 @@ public void SendPingWithIPAddressAndTimeoutAndBufferAndPingOptions() }); } - [PlatformSpecific(TestPlatforms.Windows)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. + [PlatformSpecific(TestPlatforms.Windows)] [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task SendPingAsyncWithIPAddressAndTimeoutAndBufferAndPingOptions() { @@ -338,7 +294,7 @@ await SendBatchPingAsync( }); } - [PlatformSpecific(TestPlatforms.AnyUnix)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. + [PlatformSpecific(TestPlatforms.AnyUnix)] [Theory] [InlineData(AddressFamily.InterNetwork)] [InlineData(AddressFamily.InterNetworkV6)] @@ -351,26 +307,18 @@ public void SendPingWithIPAddressAndTimeoutAndBufferAndPingOptions_Unix(AddressF return; } - byte[] buffer = TestSettings.PayloadAsBytes; + byte[] buffer = GetPingPayload(localIpAddress.AddressFamily); + SendBatchPing( (ping) => ping.Send(localIpAddress, TestSettings.PingTimeout, buffer, new PingOptions()), (pingReply) => { PingResultValidator(pingReply, localIpAddress); - - // Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - if (Capability.CanUseRawSockets(localIpAddress.AddressFamily) || PlatformDetection.IsOSXLike) - { - Assert.Equal(buffer, pingReply.Buffer); - } - else - { - Assert.Equal(Array.Empty(), pingReply.Buffer); - } + Assert.Equal(buffer, pingReply.Buffer); }); } - [PlatformSpecific(TestPlatforms.AnyUnix)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. + [PlatformSpecific(TestPlatforms.AnyUnix)] [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] [InlineData(AddressFamily.InterNetwork)] [InlineData(AddressFamily.InterNetworkV6)] @@ -383,22 +331,14 @@ public async Task SendPingAsyncWithIPAddressAndTimeoutAndBufferAndPingOptions_Un return; } - byte[] buffer = TestSettings.PayloadAsBytes; + byte[] buffer = GetPingPayload(localIpAddress.AddressFamily); + await SendBatchPingAsync( (ping) => ping.SendPingAsync(localIpAddress, TestSettings.PingTimeout, buffer, new PingOptions()), (pingReply) => { PingResultValidator(pingReply, localIpAddress); - - // Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - if (Capability.CanUseRawSockets(localIpAddress.AddressFamily) || PlatformDetection.IsOSXLike) - { - Assert.Equal(buffer, pingReply.Buffer); - } - else - { - Assert.Equal(Array.Empty(), pingReply.Buffer); - } + Assert.Equal(buffer, pingReply.Buffer); }); } @@ -454,13 +394,12 @@ await SendBatchPingAsync( }); } - [PlatformSpecific(TestPlatforms.Windows)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. [Fact] public void SendPingWithHostAndTimeoutAndBuffer() { IPAddress localIpAddress = TestSettings.GetLocalIPAddress(); + byte[] buffer = GetPingPayload(localIpAddress.AddressFamily); - byte[] buffer = TestSettings.PayloadAsBytes; SendBatchPing( (ping) => ping.Send(TestSettings.LocalHost, TestSettings.PingTimeout, buffer), (pingReply) => @@ -470,13 +409,12 @@ public void SendPingWithHostAndTimeoutAndBuffer() }); } - [PlatformSpecific(TestPlatforms.Windows)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task SendPingAsyncWithHostAndTimeoutAndBuffer() { IPAddress localIpAddress = await TestSettings.GetLocalIPAddressAsync(); + byte[] buffer = GetPingPayload(localIpAddress.AddressFamily); - byte[] buffer = TestSettings.PayloadAsBytes; await SendBatchPingAsync( (ping) => ping.SendPingAsync(TestSettings.LocalHost, TestSettings.PingTimeout, buffer), (pingReply) => @@ -486,80 +424,27 @@ await SendBatchPingAsync( }); } - [PlatformSpecific(TestPlatforms.AnyUnix)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - [Fact] - public void SendPingWithHostAndTimeoutAndBuffer_Unix() - { - IPAddress[] localIpAddresses = TestSettings.GetLocalIPAddresses(); - - byte[] buffer = TestSettings.PayloadAsBytes; - SendBatchPing( - (ping) => ping.Send(TestSettings.LocalHost, TestSettings.PingTimeout, buffer), - (pingReply) => - { - PingResultValidator(pingReply, localIpAddresses); - - // Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - if (Capability.CanUseRawSockets(pingReply.Address.AddressFamily) || PlatformDetection.IsOSXLike) - { - Assert.Equal(buffer, pingReply.Buffer); - } - else - { - Assert.Equal(Array.Empty(), pingReply.Buffer); - } - }); - } - - [PlatformSpecific(TestPlatforms.AnyUnix)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - public async Task SendPingAsyncWithHostAndTimeoutAndBuffer_Unix() - { - IPAddress[] localIpAddresses = await TestSettings.GetLocalIPAddressesAsync(); - - byte[] buffer = TestSettings.PayloadAsBytes; - await SendBatchPingAsync( - (ping) => ping.SendPingAsync(TestSettings.LocalHost, TestSettings.PingTimeout, buffer), - (pingReply) => - { - PingResultValidator(pingReply, localIpAddresses); - - // Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - if (Capability.CanUseRawSockets(pingReply.Address.AddressFamily) || PlatformDetection.IsOSXLike) - { - Assert.Equal(buffer, pingReply.Buffer); - } - else - { - Assert.Equal(Array.Empty(), pingReply.Buffer); - } - }); - } - - [PlatformSpecific(TestPlatforms.Windows)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. [Fact] public void SendPingWithHostAndTimeoutAndBufferAndPingOptions() { IPAddress localIpAddress = TestSettings.GetLocalIPAddress(); + byte[] buffer = GetPingPayload(localIpAddress.AddressFamily); - byte[] buffer = TestSettings.PayloadAsBytes; SendBatchPing( (ping) => ping.Send(TestSettings.LocalHost, TestSettings.PingTimeout, buffer, new PingOptions()), (pingReply) => { PingResultValidator(pingReply, localIpAddress); - Assert.Equal(buffer, pingReply.Buffer); }); } - [PlatformSpecific(TestPlatforms.Windows)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] public async Task SendPingAsyncWithHostAndTimeoutAndBufferAndPingOptions() { IPAddress localIpAddress = await TestSettings.GetLocalIPAddressAsync(); + byte[] buffer = GetPingPayload(localIpAddress.AddressFamily); - byte[] buffer = TestSettings.PayloadAsBytes; await SendBatchPingAsync( (ping) => ping.SendPingAsync(TestSettings.LocalHost, TestSettings.PingTimeout, buffer, new PingOptions()), (pingReply) => @@ -570,57 +455,7 @@ await SendBatchPingAsync( }); } - [PlatformSpecific(TestPlatforms.AnyUnix)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - [Fact] - public void SendPingWithHostAndTimeoutAndBufferAndPingOptions_Unix() - { - IPAddress[] localIpAddresses = TestSettings.GetLocalIPAddresses(); - - byte[] buffer = TestSettings.PayloadAsBytes; - SendBatchPing( - (ping) => ping.Send(TestSettings.LocalHost, TestSettings.PingTimeout, buffer, new PingOptions()), - (pingReply) => - { - PingResultValidator(pingReply, localIpAddresses); - - // Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - if (Capability.CanUseRawSockets(pingReply.Address.AddressFamily) || PlatformDetection.IsOSXLike) - { - Assert.Equal(buffer, pingReply.Buffer); - } - else - { - Assert.Equal(Array.Empty(), pingReply.Buffer); - } - }); - } - - [PlatformSpecific(TestPlatforms.AnyUnix)] // On Unix, Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsThreadingSupported))] - public async Task SendPingAsyncWithHostAndTimeoutAndBufferAndPingOptions_Unix() - { - IPAddress[] localIpAddresses = await TestSettings.GetLocalIPAddressesAsync(); - - byte[] buffer = TestSettings.PayloadAsBytes; - await SendBatchPingAsync( - (ping) => ping.SendPingAsync(TestSettings.LocalHost, TestSettings.PingTimeout, buffer, new PingOptions()), - (pingReply) => - { - PingResultValidator(pingReply, localIpAddresses); - - // Non-root pings cannot send arbitrary data in the buffer, and do not receive it back in the PingReply. - if (Capability.CanUseRawSockets(pingReply.Address.AddressFamily) || PlatformDetection.IsOSXLike) - { - Assert.Equal(buffer, pingReply.Buffer); - } - else - { - Assert.Equal(Array.Empty(), pingReply.Buffer); - } - }); - } - - [Fact] + [ConditionalFact(nameof(DoesNotUsePingUtility))] public async Task SendPingWithIPAddressAndBigSize() { IPAddress localIpAddress = TestSettings.GetLocalIPAddress(); @@ -635,8 +470,7 @@ public async Task SendPingWithIPAddressAndBigSize() // // On Windows 10 the maximum ping size seems essentially limited to 65500 bytes and thus any buffer // size on the loopback ping succeeds. On macOS anything bigger than 8184 will report packet too - // big error. On Linux/Unix the result differs for privileged and unprivileged processes and may - // change with different platform versions. + // big error. if (OperatingSystem.IsMacOS()) { Assert.Equal(IPStatus.PacketTooBig, pingReply.Status); @@ -813,7 +647,7 @@ public async Task SendPingAsyncWithHostAndTtlAndFragmentPingOptions(bool fragmen { IPAddress[] localIpAddresses = await TestSettings.GetLocalIPAddressesAsync(); - byte[] buffer = TestSettings.PayloadAsBytes; + byte[] buffer = GetPingPayload(localIpAddresses[0].AddressFamily); PingOptions options = new PingOptions(); options.Ttl = 32; @@ -1003,5 +837,25 @@ await SendBatchPingAsync( }); }, localIpAddress.ToString(), new RemoteInvokeOptions { StartInfo = remoteInvokeStartInfo }).Dispose(); } + + [ConditionalFact(nameof(UsesPingUtility))] + public void SendPing_CustomPayload_InsufficientPrivileges_Throws() + { + IPAddress[] localIpAddresses = TestSettings.GetLocalIPAddresses(); + + byte[] buffer = TestSettings.PayloadAsBytes; + Ping ping = new Ping(); + Assert.Throws(() => ping.Send(TestSettings.LocalHost, TestSettings.PingTimeout, buffer)); + } + + [ConditionalFact(nameof(UsesPingUtility))] + public async Task SendPingAsync_CustomPayload_InsufficientPrivileges_Throws() + { + IPAddress[] localIpAddresses = TestSettings.GetLocalIPAddresses(); + + byte[] buffer = TestSettings.PayloadAsBytes; + Ping ping = new Ping(); + await Assert.ThrowsAsync(() => ping.SendPingAsync(TestSettings.LocalHost, TestSettings.PingTimeout, buffer)); + } } }