Skip to content

Commit 8e8a6e0

Browse files
simonrozsivalrzikm
andauthored
[release/6.0] Fix parsing the output of ping on Samsung phones (#80692)
* Explicitly check for TTL expiration when using Ping utility (#65312) * Explicitly check for TTL expiration when using Ping utility * Fixes * Improve the implementation * Account for different formats of ping output * Avoid allocation Co-authored-by: Radek Zikmund <[email protected]>
1 parent dea5221 commit 8e8a6e0

File tree

2 files changed

+52
-24
lines changed

2 files changed

+52
-24
lines changed

src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.PingUtility.cs

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,15 @@ private PingReply SendWithPingUtility(IPAddress address, byte[] buffer, int time
4545
using (Process p = GetPingProcess(address, buffer, timeout, options))
4646
{
4747
p.Start();
48-
if (!p.WaitForExit(timeout) || p.ExitCode == 1 || p.ExitCode == 2)
48+
if (!p.WaitForExit(timeout))
4949
{
5050
return CreatePingReply(IPStatus.TimedOut);
5151
}
5252

5353
try
5454
{
55-
string output = p.StandardOutput.ReadToEnd();
56-
return ParsePingUtilityOutput(address, output);
55+
string stdout = p.StandardOutput.ReadToEnd();
56+
return ParsePingUtilityOutput(address, p.ExitCode, stdout);
5757
}
5858
catch (Exception)
5959
{
@@ -82,16 +82,10 @@ private async Task<PingReply> SendWithPingUtilityAsync(IPAddress address, byte[]
8282
return CreatePingReply(IPStatus.TimedOut);
8383
}
8484

85-
if (p.ExitCode == 1 || p.ExitCode == 2)
86-
{
87-
// Throw timeout for known failure return codes from ping functions.
88-
return CreatePingReply(IPStatus.TimedOut);
89-
}
90-
9185
try
9286
{
93-
string output = await p.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
94-
return ParsePingUtilityOutput(address, output);
87+
string stdout = await p.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
88+
return ParsePingUtilityOutput(address, p.ExitCode, stdout);
9589
}
9690
catch (Exception)
9791
{
@@ -101,15 +95,49 @@ private async Task<PingReply> SendWithPingUtilityAsync(IPAddress address, byte[]
10195
}
10296
}
10397

104-
private PingReply ParsePingUtilityOutput(IPAddress address, string output)
98+
private static PingReply ParsePingUtilityOutput(IPAddress address, int exitCode, string stdout)
10599
{
106-
long rtt = UnixCommandLinePing.ParseRoundTripTime(output);
107-
return new PingReply(
108-
address,
109-
null, // Ping utility cannot accommodate these, return null to indicate they were ignored.
110-
IPStatus.Success,
111-
rtt,
112-
Array.Empty<byte>()); // Ping utility doesn't deliver this info.
100+
// Throw timeout for known failure return codes from ping functions.
101+
if (exitCode == 1 || exitCode == 2)
102+
{
103+
// TTL exceeded may have occured
104+
if (TryParseTtlExceeded(stdout, out PingReply? reply))
105+
{
106+
return reply!;
107+
}
108+
109+
// otherwise assume timeout
110+
return CreatePingReply(IPStatus.TimedOut);
111+
}
112+
113+
// On success, report RTT
114+
long rtt = UnixCommandLinePing.ParseRoundTripTime(stdout);
115+
return CreatePingReply(IPStatus.Success, address, rtt);
116+
}
117+
118+
private static bool TryParseTtlExceeded(string stdout, out PingReply? reply)
119+
{
120+
reply = null;
121+
if (!stdout.Contains("Time to live exceeded", StringComparison.Ordinal))
122+
{
123+
return false;
124+
}
125+
126+
// look for address in:
127+
// - "From 172.21.64.1 icmp_seq=1 Time to live exceeded"
128+
// - "From 172.21.64.1: icmp_seq=1 Time to live exceeded"
129+
int addressStart = stdout.IndexOf("From ", StringComparison.Ordinal) + 5;
130+
int addressLength = stdout.AsSpan(Math.Max(addressStart, 0)).IndexOfAny(' ', ':');
131+
IPAddress? address;
132+
if (addressStart < 5 || addressLength <= 0 || !IPAddress.TryParse(stdout.AsSpan(addressStart, addressLength), out address))
133+
{
134+
// failed to parse source address (which in case of TTL is different than the original
135+
// destination address), fallback to all 0
136+
address = new IPAddress(0);
137+
}
138+
139+
reply = CreatePingReply(IPStatus.TimeExceeded, address);
140+
return true;
113141
}
114142
}
115143
}

src/libraries/System.Net.Ping/src/System/Net/NetworkInformation/Ping.RawSocket.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ private unsafe SocketConfig GetSocketConfig(IPAddress address, byte[] buffer, in
2929
bool ipv4 = address.AddressFamily == AddressFamily.InterNetwork;
3030
bool sendIpHeader = ipv4 && options != null && SendIpHeader;
3131

32-
if (sendIpHeader)
33-
{
32+
if (sendIpHeader)
33+
{
3434
iph.VersionAndLength = 0x45;
3535
// On OSX this strangely must be host byte order.
3636
iph.TotalLength = (ushort)(sizeof(IpHeader) + checked(sizeof(IcmpHeader) + buffer.Length));
@@ -42,7 +42,7 @@ private unsafe SocketConfig GetSocketConfig(IPAddress address, byte[] buffer, in
4242
#pragma warning restore 618
4343
// No need to fill in SourceAddress or checksum.
4444
// If left blank, kernel will fill it in - at least on OSX.
45-
}
45+
}
4646

4747
return new SocketConfig(
4848
new IPEndPoint(address, 0), timeout, options,
@@ -260,11 +260,11 @@ await socket.SendToAsync(
260260
}
261261
}
262262

263-
private static PingReply CreatePingReply(IPStatus status)
263+
private static PingReply CreatePingReply(IPStatus status, IPAddress? address = null, long rtt = 0)
264264
{
265265
// Documentation indicates that you should only pay attention to the IPStatus value when
266266
// its value is not "Success", but the rest of these values match that of the Windows implementation.
267-
return new PingReply(new IPAddress(0), null, status, 0, Array.Empty<byte>());
267+
return new PingReply(address ?? new IPAddress(0), null, status, rtt, Array.Empty<byte>());
268268
}
269269

270270
#if DEBUG

0 commit comments

Comments
 (0)