private async Task<PingReply> SendWithPingUtility(IPAddress address, byte[] buffer, int timeout, PingOptions options)
{
bool isIpv4 = address.AddressFamily == AddressFamily.InterNetwork;
string pingExecutable = isIpv4 ? UnixCommandLinePing.Ping4UtilityPath : UnixCommandLinePing.Ping6UtilityPath;
if (pingExecutable == null)
{
throw new PlatformNotSupportedException(SR.net_ping_utility_not_found);
}
string processArgs = UnixCommandLinePing.ConstructCommandLine(buffer.Length, address.ToString(), isIpv4);
ProcessStartInfo psi = new ProcessStartInfo(pingExecutable, processArgs);
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
Process p = new Process() { StartInfo = psi };
var processCompletion = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
p.EnableRaisingEvents = true;
p.Exited += (s, e) => processCompletion.SetResult(true);
p.Start();
var cts = new CancellationTokenSource();
Task timeoutTask = Task.Delay(timeout, cts.Token);
Task finished = await Task.WhenAny(processCompletion.Task, timeoutTask).ConfigureAwait(false);
if (finished == timeoutTask && !p.HasExited)
{
// Try to kill the ping process if it didn't return. If it is already in the process of exiting, a Win32Exception will be thrown.
try
{
p.Kill();
}
catch (Win32Exception) { }
return CreateTimedOutPingReply();
}
else
{
cts.Cancel();
if (p.ExitCode != 0)
{
// This means no reply was received, although transmission may have been successful.
return CreateTimedOutPingReply();
}
try
{
string output = await p.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
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.
}
catch (Exception)
{
// If the standard output cannot be successfully parsed, throw a generic PingException.
throw new PingException(SR.net_ping);
}
}
}