未验证 提交 a71d6f1c 编写于 作者: R Radek Zikmund 提交者: GitHub

Explicitly check for TTL expiration when using Ping utility (#65312)

* Explicitly check for TTL expiration when using Ping utility

* Fixes

* Improve the implementation
上级 87ab8638
......@@ -53,15 +53,15 @@ private PingReply SendWithPingUtility(IPAddress address, byte[] buffer, int time
using (Process p = GetPingProcess(address, buffer, timeout, options))
{
p.Start();
if (!p.WaitForExit(timeout) || p.ExitCode == 1 || p.ExitCode == 2)
if (!p.WaitForExit(timeout))
{
return CreatePingReply(IPStatus.TimedOut);
}
try
{
string output = p.StandardOutput.ReadToEnd();
return ParsePingUtilityOutput(address, output);
string stdout = p.StandardOutput.ReadToEnd();
return ParsePingUtilityOutput(address, p.ExitCode, stdout);
}
catch (Exception)
{
......@@ -90,16 +90,10 @@ private async Task<PingReply> SendWithPingUtilityAsync(IPAddress address, byte[]
return CreatePingReply(IPStatus.TimedOut);
}
if (p.ExitCode == 1 || p.ExitCode == 2)
{
// Throw timeout for known failure return codes from ping functions.
return CreatePingReply(IPStatus.TimedOut);
}
try
{
string output = await p.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
return ParsePingUtilityOutput(address, output);
string stdout = await p.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
return ParsePingUtilityOutput(address, p.ExitCode, stdout);
}
catch (Exception)
{
......@@ -109,15 +103,47 @@ private async Task<PingReply> SendWithPingUtilityAsync(IPAddress address, byte[]
}
}
private PingReply ParsePingUtilityOutput(IPAddress address, string output)
private static PingReply ParsePingUtilityOutput(IPAddress address, int exitCode, string stdout)
{
long rtt = UnixCommandLinePing.ParseRoundTripTime(output);
return new PingReply(
address,
null, // Ping utility cannot accommodate these, return null to indicate they were ignored.
IPStatus.Success,
rtt,
Array.Empty<byte>()); // Ping utility doesn't deliver this info.
// Throw timeout for known failure return codes from ping functions.
if (exitCode == 1 || exitCode == 2)
{
// TTL exceeded may have occured
if (TryParseTtlExceeded(stdout, out PingReply? reply))
{
return reply!;
}
// otherwise assume timeout
return CreatePingReply(IPStatus.TimedOut);
}
// On success, report RTT
long rtt = UnixCommandLinePing.ParseRoundTripTime(stdout);
return CreatePingReply(IPStatus.Success, address, rtt);
}
private static bool TryParseTtlExceeded(string stdout, out PingReply? reply)
{
reply = null;
if (!stdout.Contains("Time to live exceeded", StringComparison.Ordinal))
{
return false;
}
// look for address in "From 172.21.64.1 icmp_seq=1 Time to live exceeded"
int addressStart = stdout.IndexOf("From ", StringComparison.Ordinal) + 5;
int addressLength = stdout.IndexOf(' ', Math.Max(addressStart, 0)) - addressStart;
IPAddress? address;
if (addressStart < 5 || addressLength <= 0 || !IPAddress.TryParse(stdout.AsSpan(addressStart, addressLength), out address))
{
// failed to parse source address (which in case of TTL is different than the original
// destination address), fallback to all 0
address = new IPAddress(0);
}
reply = CreatePingReply(IPStatus.TimeExceeded, address);
return true;
}
}
}
......@@ -32,10 +32,10 @@ private unsafe SocketConfig GetSocketConfig(IPAddress address, byte[] buffer, in
bool sendIpHeader = ipv4 && options != null && SendIpHeader;
int totalLength = 0;
if (sendIpHeader)
{
if (sendIpHeader)
{
iph.VersionAndLength = 0x45;
totalLength = sizeof(IpHeader) + checked(sizeof(IcmpHeader) + buffer.Length);
totalLength = sizeof(IpHeader) + checked(sizeof(IcmpHeader) + buffer.Length);
// On OSX this strangely must be host byte order.
iph.TotalLength = OperatingSystem.IsFreeBSD() ? (ushort)IPAddress.HostToNetworkOrder((short)totalLength) : (ushort)totalLength;
iph.Protocol = 1; // ICMP
......@@ -46,7 +46,7 @@ private unsafe SocketConfig GetSocketConfig(IPAddress address, byte[] buffer, in
#pragma warning restore 618
// No need to fill in SourceAddress or checksum.
// If left blank, kernel will fill it in - at least on OSX.
}
}
return new SocketConfig(
new IPEndPoint(address, 0), timeout, options,
......@@ -340,11 +340,11 @@ private async Task<PingReply> SendIcmpEchoRequestOverRawSocketAsync(IPAddress ad
}
}
private static PingReply CreatePingReply(IPStatus status)
private static PingReply CreatePingReply(IPStatus status, IPAddress? address = null, long rtt = 0)
{
// Documentation indicates that you should only pay attention to the IPStatus value when
// its value is not "Success", but the rest of these values match that of the Windows implementation.
return new PingReply(new IPAddress(0), null, status, 0, Array.Empty<byte>());
return new PingReply(address ?? new IPAddress(0), null, status, rtt, Array.Empty<byte>());
}
#if DEBUG
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册