@@ -45,15 +45,15 @@ private PingReply SendWithPingUtility(IPAddress address, byte[] buffer, int time
45
45
using ( Process p = GetPingProcess ( address , buffer , timeout , options ) )
46
46
{
47
47
p . Start ( ) ;
48
- if ( ! p . WaitForExit ( timeout ) || p . ExitCode == 1 || p . ExitCode == 2 )
48
+ if ( ! p . WaitForExit ( timeout ) )
49
49
{
50
50
return CreatePingReply ( IPStatus . TimedOut ) ;
51
51
}
52
52
53
53
try
54
54
{
55
- string output = p . StandardOutput . ReadToEnd ( ) ;
56
- return ParsePingUtilityOutput ( address , output ) ;
55
+ string stdout = p . StandardOutput . ReadToEnd ( ) ;
56
+ return ParsePingUtilityOutput ( address , p . ExitCode , stdout ) ;
57
57
}
58
58
catch ( Exception )
59
59
{
@@ -82,16 +82,10 @@ private async Task<PingReply> SendWithPingUtilityAsync(IPAddress address, byte[]
82
82
return CreatePingReply ( IPStatus . TimedOut ) ;
83
83
}
84
84
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
-
91
85
try
92
86
{
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 ) ;
95
89
}
96
90
catch ( Exception )
97
91
{
@@ -101,15 +95,49 @@ private async Task<PingReply> SendWithPingUtilityAsync(IPAddress address, byte[]
101
95
}
102
96
}
103
97
104
- private PingReply ParsePingUtilityOutput ( IPAddress address , string output )
98
+ private static PingReply ParsePingUtilityOutput ( IPAddress address , int exitCode , string stdout )
105
99
{
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 ;
113
141
}
114
142
}
115
143
}
0 commit comments