Mike Littlewood
asked on
Ping result missing some failures
I have written a very small ping wild card application that will search an entire range of ip's, i.e. 192.168.0.x so it will ping from 192.168.0.0 to 192.168.0.254 and tell me whether it is getting a response or not and hen if it does it does a reverse dns lookup.
My problem is that the result is sometimes incorrect for a specific scenario.
I only thought I would expect a pass or fail but it seems that is counts a TTL expired in transit response as a pass, obviously because of the simple method I am using, or a return value I dont know to check.
Below is a copy of the unit of code I am using, and I just call ping( 192.168.0.0)
The result obviously comes from result := ( DW <> 0);
Is there anything else I can check?
As for some more information
A valid ping response looks like this
Pinging 192.168.0.0 with 32 bytes of data:
Reply from 192.168.0.0: bytes=32 time=6ms TTL=255
Reply from 192.168.0.0: bytes=32 time=1ms TTL=255
Reply from 192.168.0.0: bytes=32 time=4ms TTL=255
Reply from 192.168.0.0: bytes=32 time=1ms TTL=255
Ping statistics for 192.168.0.0:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 1ms, Maximum = 6ms, Average = 3ms
and an invalid one looks might look like this:
Pinging 192.168.0.1 with 32 bytes of data:
Request timed out.
Request timed out.
Request timed out.
Request timed out.
Ping statistics for 192.168.0.1:
Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),
or look like this
Pinging 192.168.0.2 with 32 bytes of data:
Reply from 192.168.0.2: TTL expired in transit.
Reply from 192.168.0.2: TTL expired in transit.
Reply from 192.168.0.2: TTL expired in transit.
Reply from 192.168.0.2: TTL expired in transit.
Ping statistics for 192.168.0.2:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
My problem is that the result is sometimes incorrect for a specific scenario.
I only thought I would expect a pass or fail but it seems that is counts a TTL expired in transit response as a pass, obviously because of the simple method I am using, or a return value I dont know to check.
Below is a copy of the unit of code I am using, and I just call ping( 192.168.0.0)
The result obviously comes from result := ( DW <> 0);
Is there anything else I can check?
As for some more information
A valid ping response looks like this
Pinging 192.168.0.0 with 32 bytes of data:
Reply from 192.168.0.0: bytes=32 time=6ms TTL=255
Reply from 192.168.0.0: bytes=32 time=1ms TTL=255
Reply from 192.168.0.0: bytes=32 time=4ms TTL=255
Reply from 192.168.0.0: bytes=32 time=1ms TTL=255
Ping statistics for 192.168.0.0:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 1ms, Maximum = 6ms, Average = 3ms
and an invalid one looks might look like this:
Pinging 192.168.0.1 with 32 bytes of data:
Request timed out.
Request timed out.
Request timed out.
Request timed out.
Ping statistics for 192.168.0.1:
Packets: Sent = 4, Received = 0, Lost = 4 (100% loss),
or look like this
Pinging 192.168.0.2 with 32 bytes of data:
Reply from 192.168.0.2: TTL expired in transit.
Reply from 192.168.0.2: TTL expired in transit.
Reply from 192.168.0.2: TTL expired in transit.
Reply from 192.168.0.2: TTL expired in transit.
Ping statistics for 192.168.0.2:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms
unit raw_ping;
interface
uses
Windows, SysUtils, Classes;
type
TSunB = packed record
s_b1, s_b2, s_b3, s_b4: byte;
end;
TSunW = packed record
s_w1, s_w2: word;
end;
PIPAddr = ^TIPAddr;
TIPAddr = record
case integer of
0: (S_un_b: TSunB);
1: (S_un_w: TSunW);
2: (S_addr: longword);
end;
IPAddr = TIPAddr;
function IcmpCreateFile: THandle; stdcall; external 'icmp.dll';
function IcmpCloseHandle( icmpHandle : THandle) : boolean; stdcall; external 'icmp.dll'
function IcmpSendEcho( IcmpHandle : THandle; DestinationAddress : IPAddr;
RequestData : Pointer; RequestSize : Smallint;
RequestOptions : pointer;
ReplyBuffer : Pointer;
ReplySize : DWORD;
Timeout : DWORD) : DWORD; stdcall; external 'icmp.dll';
function Ping(InetAddress : string) : boolean;
procedure TranslateStringToTInAddr( AIP: string; var AInAddr);
implementation
uses
WinSock;
function Fetch( var AInput: string; const ADelim: string = ' ';
const ADelete: Boolean = true): string;
var
iPos: Integer;
begin
if ADelim = #0 then
begin
// AnsiPos does not work with #0
iPos := Pos(ADelim, AInput);
end else
begin
iPos := Pos(ADelim, AInput);
end;
if iPos = 0 then
begin
Result := AInput;
if ADelete then
begin
AInput := '';
end;
end else
begin
result := Copy(AInput, 1, iPos - 1);
if ADelete then
begin
Delete(AInput, 1, iPos + Length(ADelim) - 1);
end;
end;
end;
procedure TranslateStringToTInAddr(AIP: string; var AInAddr);
var
phe: PHostEnt;
pac: PChar;
GInitData: TWSAData;
begin
WSAStartup($101, GInitData);
try
phe := GetHostByName(PChar(AIP));
if Assigned(phe) then
begin
pac := phe^.h_addr_list^;
if Assigned(pac) then
begin
with TIPAddr(AInAddr).S_un_b do
begin
s_b1 := Byte(pac[0]);
s_b2 := Byte(pac[1]);
s_b3 := Byte(pac[2]);
s_b4 := Byte(pac[3]);
end;
end else
begin
raise Exception.Create('Error getting IP from HostName');
end;
end else
begin
raise Exception.Create('Error getting HostName');
end;
except
FillChar(AInAddr, SizeOf(AInAddr), #0);
end;
WSACleanup;
end;
function Ping( InetAddress : string) : boolean;
var
handle: THandle;
inAddr: IPAddr;
DW: DWORD;
rep: array[ 1..128] of byte;
begin
result := false;
handle := IcmpCreateFile;
if handle = INVALID_HANDLE_VALUE then
exit;
translateStringToTInAddr( inetAddress, inAddr);
DW := icmpSendEcho( handle, inAddr, nil, 0, nil, @rep, 128, 0);
result := ( DW <> 0);
icmpCloseHandle(Handle);
end;
end.
ASKER
I'm wondering if in your code
case AReplyStatus.ReplyStatusTy pe of
rsEcho: Temp := 'Echo';
rsError: Temp := 'Error';
rsTimeOut: Temp := 'TimeOut';
rsErrorUnreachable: Temp := 'Unreachable';
rsErrorTTLExceeded: Temp := 'TTLExceeded';
end;
is similar to the result from in mine
DW := icmpSendEcho( handle, inAddr, nil, 0, nil, @rep, 128, 0);
Do you have the declaration section for your thread object?
case AReplyStatus.ReplyStatusTy
rsEcho: Temp := 'Echo';
rsError: Temp := 'Error';
rsTimeOut: Temp := 'TimeOut';
rsErrorUnreachable: Temp := 'Unreachable';
rsErrorTTLExceeded: Temp := 'TTLExceeded';
end;
is similar to the result from in mine
DW := icmpSendEcho( handle, inAddr, nil, 0, nil, @rep, 128, 0);
Do you have the declaration section for your thread object?
ASKER
I've tried a slightly different method using the TIdIcmpClient object from Indy.
IdIcmpClient1.ReceiveTimeo ut := 25;
IdIcmpClient1.Host := '192.168.0.1';
IdIcmpClient1.Ping;
As soon as I call ping though it throws an error
*************************
Socket Error # 10049
Cannot assign requested address
*************************
All I can assume is that it might be because the port number is set to 0, though if it needs to be something else, I have no idea what value is required.
IdIcmpClient1.ReceiveTimeo
IdIcmpClient1.Host := '192.168.0.1';
IdIcmpClient1.Ping;
As soon as I call ping though it throws an error
*************************
Socket Error # 10049
Cannot assign requested address
*************************
All I can assume is that it might be because the port number is set to 0, though if it needs to be something else, I have no idea what value is required.
SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
basically you can ping on any port
ping 21 : check if FTP port is open
ping 80 : check if HTTP port is open
ping 21 : check if FTP port is open
ping 80 : check if HTTP port is open
Below is the source for my IcmpUtils unit, and Ping is one of the functions exposed from it. It can take the address in DNS or dotted format, and allows you to pass a timeout value to it. If you pass zero, it will use its default of 5000 ms. As to the response from IcmpEcho, if it succeeded, that does not mean the ping went through. You need to check the Reply's (TICMPEchoReply) status field to ensure its IP_SUCCESS (0), and not one of the following:
IP_STATUS_BASE = 11000;
IP_BUF_TOO_SMALL = (IP_STATUS_BASE + 1);
IP_DEST_NET_UNREACHABLE = (IP_STATUS_BASE + 2);
IP_DEST_HOST_UNREACHABLE= (IP_STATUS_BASE + 3);
IP_DEST_PROT_UNREACHABLE= (IP_STATUS_BASE + 4);
IP_DEST_PORT_UNREACHABLE= (IP_STATUS_BASE + 5);
IP_NO_RESOURCES = (IP_STATUS_BASE + 6);
IP_BAD_OPTION = (IP_STATUS_BASE + 7);
IP_HW_ERROR = (IP_STATUS_BASE + 8);
IP_PACKET_TOO_BIG = (IP_STATUS_BASE + 9);
IP_REQ_TIMED_OUT = (IP_STATUS_BASE + 10);
IP_BAD_REQ = (IP_STATUS_BASE + 11);
IP_BAD_ROUTE = (IP_STATUS_BASE + 12);
IP_TTL_EXPIRED_TRANSIT = (IP_STATUS_BASE + 13);
IP_TTL_EXPIRED_REASSEM = (IP_STATUS_BASE + 14);
IP_PARAM_PROBLEM = (IP_STATUS_BASE + 15);
IP_SOURCE_QUENCH = (IP_STATUS_BASE + 16);
IP_OPTION_TOO_BIG = (IP_STATUS_BASE + 17);
IP_BAD_DESTINATION = (IP_STATUS_BASE + 18);
Hope this helps,
Russell
----
unit IcmpUtils;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
//
// Unit : IcmpUtils
// Author : rllibby
// Date : 07.18.2006
// Description : Set of ICMP utility routines based off the iphlpapi library
// in Windows. Exposes the following:
//
// - Ability to get inbound / outbound ICMP statistics
// - Ping
// - TraceRoute
//
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
interface
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
// Include units
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
uses
Windows, SysUtils, Classes, WinSock;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ///
// ICMP library constants
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
const
IPHLPAPI_LIBRARY = 'iphlpapi.dll';
////////////////////////// ////////// ////////// ///
// This can optionally be set to use the icmp.dll
////////////////////////// ////////// ////////// ///
ICMP_LIBRARY = IPHLPAPI_LIBRARY;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
// ICMP error constants
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
const
IP_SUCCESS = ERROR_SUCCESS;
IP_STATUS_BASE = 11000;
IP_BUF_TOO_SMALL = (IP_STATUS_BASE + 1);
IP_DEST_NET_UNREACHABLE = (IP_STATUS_BASE + 2);
IP_DEST_HOST_UNREACHABLE= (IP_STATUS_BASE + 3);
IP_DEST_PROT_UNREACHABLE= (IP_STATUS_BASE + 4);
IP_DEST_PORT_UNREACHABLE= (IP_STATUS_BASE + 5);
IP_NO_RESOURCES = (IP_STATUS_BASE + 6);
IP_BAD_OPTION = (IP_STATUS_BASE + 7);
IP_HW_ERROR = (IP_STATUS_BASE + 8);
IP_PACKET_TOO_BIG = (IP_STATUS_BASE + 9);
IP_REQ_TIMED_OUT = (IP_STATUS_BASE + 10);
IP_BAD_REQ = (IP_STATUS_BASE + 11);
IP_BAD_ROUTE = (IP_STATUS_BASE + 12);
IP_TTL_EXPIRED_TRANSIT = (IP_STATUS_BASE + 13);
IP_TTL_EXPIRED_REASSEM = (IP_STATUS_BASE + 14);
IP_PARAM_PROBLEM = (IP_STATUS_BASE + 15);
IP_SOURCE_QUENCH = (IP_STATUS_BASE + 16);
IP_OPTION_TOO_BIG = (IP_STATUS_BASE + 17);
IP_BAD_DESTINATION = (IP_STATUS_BASE + 18);
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
// ICMP option types
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
const
IP_OPT_EOL = $00; // End of list option
IP_OPT_NOP = $01; // No operation
IP_OPT_SECURITY = $82; // Security option.
IP_OPT_LSRR = $83; // Loose source route.
IP_OPT_SSRR = $89; // Strict source route.
IP_OPT_RR = $07; // Record route.
IP_OPT_TS = $44; // Timestamp.
IP_OPT_SID = $88; // Stream ID (obsolete)
const
MAX_OPT_SIZE = 40;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
// ICMP structures
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
type
ICMP_OPTION_INFORMATION = packed record
Ttl: u_char;
Tos: u_char;
Flags: u_char;
OptionsSize: u_char;
OptionsData: Pointer;
end;
PICMP_OPTION_INFORMATION= ^ICMP_OPTION_INFORMATION;
TICMPOptionInformation = ICMP_OPTION_INFORMATION;
PICMPOptionInformation = ^TICMPOptionInformation;
type
ICMP_ECHO_REPLY = packed record
Address: u_long;
Status: u_long;
RTTime: u_long;
DataSize: u_short;
Reserved: u_short;
Data: Pointer;
Options: TICMPOptionInformation;
end;
PICMPP_ECHO_REPLY = ^ICMP_ECHO_REPLY;
TICMPEchoReply = ICMP_ECHO_REPLY;
PICMPEchoReply = ^TICMPEchoReply;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
// ICMP statistics structures
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
type
MIBICMPSTATS = packed record
dwMsgs: DWORD; // number of messages
dwErrors: DWORD; // number of errors
dwDestUnreachs: DWORD; // destination unreachable messages
dwTimeExcds: DWORD; // time-to-live exceeded messages
dwParmProbs: DWORD; // parameter problem messages
dwSrcQuenchs: DWORD; // source quench messages
dwRedirects: DWORD; // redirection messages
dwEchos: DWORD; // echo requests
dwEchoReps: DWORD; // echo replies
dwTimestamps: DWORD; // time-stamp requests
dwTimestampReps: DWORD; // time-stamp replies
dwAddrMasks: DWORD; // address mask requests
dwAddrMaskReps: DWORD; // address mask replies
end;
LPMIBICMPSTATS = ^MIBICMPSTATS;
TMIBIcmpStats = MIBICMPSTATS;
PMIBIcmpStats = ^TMIBIcmpStats;
MIBICMPINFO = packed record
icmpInStats: MIBICMPSTATS;
icmpOutStats: MIBICMPSTATS;
end;
LPMIBICMPINFO = ^MIBICMPINFO;
TMIBIcmpInfo = MIBICMPINFO;
PMIBIcmpInfo = ^TMIBIcmpInfo;
MIB_ICMP = packed record
stats: MIBICMPINFO;
end;
LPMIB_ICMP = ^MIB_ICMP;
TMIBIcmp = MIB_ICMP;
PMIBIcmp = ^TMIBIcmp;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
// Ping constants
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
const
PING_DEF_TIMEOUT = 5000;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
// ICMP imported functions
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
function IcmpCreateFile: THandle; stdcall; external ICMP_LIBRARY;
function IcmpCloseHandle(icmpHandle : THandle): BOOL; stdcall; external ICMP_LIBRARY;
function IcmpSendEcho(icmpHandle: THandle; DestinationAddress: TInAddr; RequestData: Pointer; RequestSize: Word; RequestOptions: Pointer; ReplyBuffer: Pointer; ReplySize, Timeout: DWORD): DWORD; stdcall; external ICMP_LIBRARY;
function GetIcmpStatistics(pStats: LPMIB_ICMP): DWORD; stdcall; external IPHLPAPI_LIBRARY;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
//
// GetLocalIPAddress
//
// Returns
//
// Returns the local ip address string in the form of x.x.x.x if
// successful, an empty string on failure.
//
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
function GetLocalIPAddress: String;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
//
// GetTraceRoute
//
// HostName = [in] The name of the host system to trace a route to. The
// name can be in either DNS format, or dotted name notation.
//
// List = [in] A TStrings or descendant class to fill in with the
// result of the trace route to the specified host name. The
// string values in the list will contain the IP address in
// x.x.x.x notation, and the Objects[x] property for each list
// item will be the integer value for round trip time.
//
// Returns
//
// If the trace completes, an ERROR_SUCCESS will be returned. Otherwise,
// the error code causing the failure will be returned.
//
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
function GetTraceRoute(HostName: String; List: TStrings): Integer;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
//
// Ping
//
// HostName = [in] The name of the host system to get the network address
// for. The name can be in either DNS format, or dotted name
// notation.
//
// TimeOut = [in/out] On input, determines how long to wait (in ms) for
// an echo reply from the host system. If zero is passed, the
// default timeout of PING_DEF_TIMEOUT is used. On return, this
// will be filled in with the roundtrip time (in ms) to the host.
//
// Returns
//
// Returns ERROR_SUCCESS if the host replied to the echo command, else
// the error code indicating the failure.
//
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
function Ping(HostName: String; var Timeout: DWORD): Integer;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
//
// ResolveAddress
//
// HostName = [in] The name of the host system to get the network address
// for. The name can be in either DNS format, or dotted name
// notation.
//
// Address = [out] The binary address of the host system on success.
//
// Returns
//
// Returns ERROR_SUCCESS if the host name was converted to a binary ip
// address, else the error code indicating the failure. On success, the
// Address param will be filled in with the binary ip address.
//
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
function ResolveAddress(HostName: String; out Address: DWORD): Integer;
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
//
// TraceRoute
//
// HostName = [in] The name of the host system to trace a route to. The
// name can be in either DNS format, or dotted name notation.
//
// CallBack = [in] The function to callback when an address hop is
// resolved. This is not a required param, but if passed in,
// returning a False result from the callback will cause the
// trace to complete with a result of ERROR_CANCELLED.
//
// lParam = [in] Specifies a 32-bit, application-defined value to be
// passed to the callback function.
//
// Returns
//
// If the trace completes, an ERROR_SUCCESS will be returned. Otherwise,
// the error code causing the failure will be returned.
//
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
type
TTraceCallback = function(HopNumber: Byte; Address: PChar; RoundTripTime: Cardinal; lParam: Integer): Boolean;
function TraceRoute(HostName: String; CallBack: TTraceCallback; lParam: Integer): Integer;
implementation
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
// Protected variables
////////////////////////// ////////// ////////// ////////// ////////// ////////// ////
var
lpData: TWSAData;
function ResolveAddress(HostName: String; out Address: DWORD): Integer;
var lpHost: PHostEnt;
begin
// Set default address
Address:=DWORD(INADDR_NONE );
// Resource protection
try
// Check host name length
if (Length(HostName) > 0) then
begin
// Try converting the hostname
Address:=inet_addr(PChar(H ostName));
// Check address
if (DWORD(Address) = DWORD(INADDR_NONE)) then
begin
// Attempt to get host by name
lpHost:=gethostbyname(PCha r(HostName ));
// Check host ent structure for valid ip address
if Assigned(lpHost) and Assigned(lpHost^.h_addr_li st^) then
begin
// Get the address from the list
Address:=u_long(PLongInt(l pHost^.h_a ddr_list^) ^);
end;
end;
end;
finally
// Check result address
if (DWORD(Address) = DWORD(INADDR_NONE)) then
// Invalid host specified
result:=IP_BAD_DESTINATION
else
// Converted correctly
result:=ERROR_SUCCESS;
end;
end;
function GetTraceRouteCallback(HopN umber: Byte; Address: PChar; RoundTripTime: Cardinal; lParam: Integer): Boolean;
begin
// Resource protection
try
// Add address and round trip time to list
TStrings(lParam).AddObject (Address, Pointer(RoundTripTime));
finally
// Keep enumerating
result:=True;
end;
end;
function GetTraceRoute(HostName: String; List: TStrings): Integer;
begin
// Check the passed list
if Assigned(List) then
begin
// Lock the list
List.BeginUpdate;
// Resource protection
try
// Clear the list
List.Clear;
// Perform the trace route
result:=TraceRoute(HostNam e, GetTraceRouteCallback, Integer(List));
finally
// Unlock the list
List.EndUpdate;
end;
end
else
// List must be passed
result:=ERROR_INVALID_PARA METER;
end;
function Ping(HostName: String; var Timeout: Cardinal): Integer;
var lpSend: Array [0..7] of Integer;
lpReply: PICMPEchoReply;
icmpHandle: THandle;
dwAddress: DWORD;
dwTTL: DWORD;
begin
// Convert the address
result:=ResolveAddress(Hos tName, dwAddress);
// Check result
if (result = ERROR_SUCCESS) then
begin
// Open an icmp handle
icmpHandle:=IcmpCreateFile ;
// Check handle
if not(icmpHandle = INVALID_HANDLE_VALUE) then
begin
// Resource protection
try
// Allocate memory for reply
lpReply:=AllocMem(SizeOf(I CMP_ECHO_R EPLY) * 2 + SizeOf(lpSend));
// Resource protection
try
// Get timeout value
if (Timeout = 0) then
// Use default
dwTTL:=PING_DEF_TIMEOUT
else
// Use passed value
dwTTL:=Timeout;
// Send echo to the host
if (IcmpSendEcho(icmpHandle, in_addr(dwAddress), @lpSend, SizeOf(lpSend), nil, lpReply, SizeOf(ICMP_ECHO_REPLY) + SizeOf(lpSend), dwTTL) = 1) then
begin
// Fill in the round trip time
Timeout:=lpReply^.RTTime;
// Return the status
result:=lpReply^.Status;
end
else
// Request timed out
result:=IP_REQ_TIMED_OUT;
finally
// Free memory
FreeMem(lpReply);
end;
finally
// Close handle
IcmpCloseHandle(icmpHandle );
end;
end
else
// Failed to open icmp handle, return last error
result:=GetLastError;
end;
end;
function TraceRoute(HostName: String; CallBack: TTraceCallback; lParam: Integer): Integer;
var lpSend: Array [0..7] of Integer;
lpAddr: Array [0..255] of Char;
lpOpts: TICMPOptionInformation;
lpReply: PICMPEchoReply;
icmpHandle: THandle;
dwAddress: DWORD;
dwAttempt: Integer;
dwTTL: Integer;
begin
// Convert the address
result:=ResolveAddress(Hos tName, dwAddress);
// Check result
if (result = ERROR_SUCCESS) then
begin
// Open an icmp handle
icmpHandle:=IcmpCreateFile ;
// Check handle
if not(icmpHandle = INVALID_HANDLE_VALUE) then
begin
// Resource protection
try
// Clear option structure
FillChar(lpOpts, SizeOf(lpOpts), 0);
// Allocate memory for reply
lpReply:=AllocMem(SizeOf(I CMP_ECHO_R EPLY) * 2 + SizeOf(lpSend));
// Resource protection
try
// Set starting TTL count
dwTTL:=1;
// Perform the trace route
while (dwTTL < 256) do
begin
// Set option time to live
lpOpts.Ttl:=Char(dwTTL);
// Set starting attempt
dwAttempt:=0;
// Retry up to 3 times
while (dwAttempt < 3) do
begin
// Send 32 bytes of data, break on success
if (IcmpSendEcho(icmpHandle, in_addr(dwAddress), @lpSend, SizeOf(lpSend), @lpOpts, lpReply, SizeOf(ICMP_ECHO_REPLY) + SizeOf(lpSend), 1000) = 1) then break;
// Increment the attempt
Inc(dwAttempt);
end;
// Check attempt counter
if (dwAttempt = 3) then
begin
// Set result
result:=IP_REQ_TIMED_OUT;
// Done
break;
end;
// Check callback
if Assigned(Callback) then
begin
// Copy address
StrLCopy(@lpAddr, inet_ntoa(in_addr(lpReply^ .Address)) , Pred(SizeOf(lpAddr)));
// If false is returned then we need to stop the trace route
if not(Callback(Ord(dwTTL), lpAddr, lpReply^.RTTime, lParam)) then
begin
// Set result
result:=ERROR_CANCELLED;
// Done
break;
end;
end;
// Check reply status, if IP_SUCCESS then we have reached the desired host
if (lpReply^.Status = IP_SUCCESS) then break;
// Increment the time to live
Inc(dwTTL);
end;
finally
// Free memory
FreeMem(lpReply);
end;
finally
// Close handle
IcmpCloseHandle(icmpHandle );
end;
end
else
// Failed to open icmp handle, return last error
result:=GetLastError;
end;
end;
function GetLocalIPAddress: String;
var lpszName: Array [0..MAX_PATH] of Char;
lpHost: PHostEnt;
begin
// Get host name
if (gethostname(@lpszName, SizeOf(lpszName)) = 0) then
begin
// Get host ent structure from remote name
lpHost:=gethostbyname(@lps zName);
// Check host ent structure for valid ip address
if Assigned(lpHost) and Assigned(lpHost^.h_addr_li st^) then
// Convert to dot notation string
result:=inet_ntoa(in_addr( PLongInt(l pHost^.h_a ddr_list^) ^))
else
// Failed to resolve name
SetLength(result, 0);
end
else
// Failed to get name
SetLength(result, 0);
end;
initialization
// Initialize winsock
WSAStartup(MakeWord(1, 1), lpData);
finalization
// Cleanup winsock
WSACleanup;
end.
IP_STATUS_BASE = 11000;
IP_BUF_TOO_SMALL = (IP_STATUS_BASE + 1);
IP_DEST_NET_UNREACHABLE = (IP_STATUS_BASE + 2);
IP_DEST_HOST_UNREACHABLE= (IP_STATUS_BASE + 3);
IP_DEST_PROT_UNREACHABLE= (IP_STATUS_BASE + 4);
IP_DEST_PORT_UNREACHABLE= (IP_STATUS_BASE + 5);
IP_NO_RESOURCES = (IP_STATUS_BASE + 6);
IP_BAD_OPTION = (IP_STATUS_BASE + 7);
IP_HW_ERROR = (IP_STATUS_BASE + 8);
IP_PACKET_TOO_BIG = (IP_STATUS_BASE + 9);
IP_REQ_TIMED_OUT = (IP_STATUS_BASE + 10);
IP_BAD_REQ = (IP_STATUS_BASE + 11);
IP_BAD_ROUTE = (IP_STATUS_BASE + 12);
IP_TTL_EXPIRED_TRANSIT = (IP_STATUS_BASE + 13);
IP_TTL_EXPIRED_REASSEM = (IP_STATUS_BASE + 14);
IP_PARAM_PROBLEM = (IP_STATUS_BASE + 15);
IP_SOURCE_QUENCH = (IP_STATUS_BASE + 16);
IP_OPTION_TOO_BIG = (IP_STATUS_BASE + 17);
IP_BAD_DESTINATION = (IP_STATUS_BASE + 18);
Hope this helps,
Russell
----
unit IcmpUtils;
//////////////////////////
//
// Unit : IcmpUtils
// Author : rllibby
// Date : 07.18.2006
// Description : Set of ICMP utility routines based off the iphlpapi library
// in Windows. Exposes the following:
//
// - Ability to get inbound / outbound ICMP statistics
// - Ping
// - TraceRoute
//
//////////////////////////
interface
//////////////////////////
// Include units
//////////////////////////
uses
Windows, SysUtils, Classes, WinSock;
//////////////////////////
// ICMP library constants
//////////////////////////
const
IPHLPAPI_LIBRARY = 'iphlpapi.dll';
//////////////////////////
// This can optionally be set to use the icmp.dll
//////////////////////////
ICMP_LIBRARY = IPHLPAPI_LIBRARY;
//////////////////////////
// ICMP error constants
//////////////////////////
const
IP_SUCCESS = ERROR_SUCCESS;
IP_STATUS_BASE = 11000;
IP_BUF_TOO_SMALL = (IP_STATUS_BASE + 1);
IP_DEST_NET_UNREACHABLE = (IP_STATUS_BASE + 2);
IP_DEST_HOST_UNREACHABLE= (IP_STATUS_BASE + 3);
IP_DEST_PROT_UNREACHABLE= (IP_STATUS_BASE + 4);
IP_DEST_PORT_UNREACHABLE= (IP_STATUS_BASE + 5);
IP_NO_RESOURCES = (IP_STATUS_BASE + 6);
IP_BAD_OPTION = (IP_STATUS_BASE + 7);
IP_HW_ERROR = (IP_STATUS_BASE + 8);
IP_PACKET_TOO_BIG = (IP_STATUS_BASE + 9);
IP_REQ_TIMED_OUT = (IP_STATUS_BASE + 10);
IP_BAD_REQ = (IP_STATUS_BASE + 11);
IP_BAD_ROUTE = (IP_STATUS_BASE + 12);
IP_TTL_EXPIRED_TRANSIT = (IP_STATUS_BASE + 13);
IP_TTL_EXPIRED_REASSEM = (IP_STATUS_BASE + 14);
IP_PARAM_PROBLEM = (IP_STATUS_BASE + 15);
IP_SOURCE_QUENCH = (IP_STATUS_BASE + 16);
IP_OPTION_TOO_BIG = (IP_STATUS_BASE + 17);
IP_BAD_DESTINATION = (IP_STATUS_BASE + 18);
//////////////////////////
// ICMP option types
//////////////////////////
const
IP_OPT_EOL = $00; // End of list option
IP_OPT_NOP = $01; // No operation
IP_OPT_SECURITY = $82; // Security option.
IP_OPT_LSRR = $83; // Loose source route.
IP_OPT_SSRR = $89; // Strict source route.
IP_OPT_RR = $07; // Record route.
IP_OPT_TS = $44; // Timestamp.
IP_OPT_SID = $88; // Stream ID (obsolete)
const
MAX_OPT_SIZE = 40;
//////////////////////////
// ICMP structures
//////////////////////////
type
ICMP_OPTION_INFORMATION = packed record
Ttl: u_char;
Tos: u_char;
Flags: u_char;
OptionsSize: u_char;
OptionsData: Pointer;
end;
PICMP_OPTION_INFORMATION= ^ICMP_OPTION_INFORMATION;
TICMPOptionInformation = ICMP_OPTION_INFORMATION;
PICMPOptionInformation = ^TICMPOptionInformation;
type
ICMP_ECHO_REPLY = packed record
Address: u_long;
Status: u_long;
RTTime: u_long;
DataSize: u_short;
Reserved: u_short;
Data: Pointer;
Options: TICMPOptionInformation;
end;
PICMPP_ECHO_REPLY = ^ICMP_ECHO_REPLY;
TICMPEchoReply = ICMP_ECHO_REPLY;
PICMPEchoReply = ^TICMPEchoReply;
//////////////////////////
// ICMP statistics structures
//////////////////////////
type
MIBICMPSTATS = packed record
dwMsgs: DWORD; // number of messages
dwErrors: DWORD; // number of errors
dwDestUnreachs: DWORD; // destination unreachable messages
dwTimeExcds: DWORD; // time-to-live exceeded messages
dwParmProbs: DWORD; // parameter problem messages
dwSrcQuenchs: DWORD; // source quench messages
dwRedirects: DWORD; // redirection messages
dwEchos: DWORD; // echo requests
dwEchoReps: DWORD; // echo replies
dwTimestamps: DWORD; // time-stamp requests
dwTimestampReps: DWORD; // time-stamp replies
dwAddrMasks: DWORD; // address mask requests
dwAddrMaskReps: DWORD; // address mask replies
end;
LPMIBICMPSTATS = ^MIBICMPSTATS;
TMIBIcmpStats = MIBICMPSTATS;
PMIBIcmpStats = ^TMIBIcmpStats;
MIBICMPINFO = packed record
icmpInStats: MIBICMPSTATS;
icmpOutStats: MIBICMPSTATS;
end;
LPMIBICMPINFO = ^MIBICMPINFO;
TMIBIcmpInfo = MIBICMPINFO;
PMIBIcmpInfo = ^TMIBIcmpInfo;
MIB_ICMP = packed record
stats: MIBICMPINFO;
end;
LPMIB_ICMP = ^MIB_ICMP;
TMIBIcmp = MIB_ICMP;
PMIBIcmp = ^TMIBIcmp;
//////////////////////////
// Ping constants
//////////////////////////
const
PING_DEF_TIMEOUT = 5000;
//////////////////////////
// ICMP imported functions
//////////////////////////
function IcmpCreateFile: THandle; stdcall; external ICMP_LIBRARY;
function IcmpCloseHandle(icmpHandle
function IcmpSendEcho(icmpHandle: THandle; DestinationAddress: TInAddr; RequestData: Pointer; RequestSize: Word; RequestOptions: Pointer; ReplyBuffer: Pointer; ReplySize, Timeout: DWORD): DWORD; stdcall; external ICMP_LIBRARY;
function GetIcmpStatistics(pStats: LPMIB_ICMP): DWORD; stdcall; external IPHLPAPI_LIBRARY;
//////////////////////////
//
// GetLocalIPAddress
//
// Returns
//
// Returns the local ip address string in the form of x.x.x.x if
// successful, an empty string on failure.
//
//////////////////////////
function GetLocalIPAddress: String;
//////////////////////////
//
// GetTraceRoute
//
// HostName = [in] The name of the host system to trace a route to. The
// name can be in either DNS format, or dotted name notation.
//
// List = [in] A TStrings or descendant class to fill in with the
// result of the trace route to the specified host name. The
// string values in the list will contain the IP address in
// x.x.x.x notation, and the Objects[x] property for each list
// item will be the integer value for round trip time.
//
// Returns
//
// If the trace completes, an ERROR_SUCCESS will be returned. Otherwise,
// the error code causing the failure will be returned.
//
//////////////////////////
function GetTraceRoute(HostName: String; List: TStrings): Integer;
//////////////////////////
//
// Ping
//
// HostName = [in] The name of the host system to get the network address
// for. The name can be in either DNS format, or dotted name
// notation.
//
// TimeOut = [in/out] On input, determines how long to wait (in ms) for
// an echo reply from the host system. If zero is passed, the
// default timeout of PING_DEF_TIMEOUT is used. On return, this
// will be filled in with the roundtrip time (in ms) to the host.
//
// Returns
//
// Returns ERROR_SUCCESS if the host replied to the echo command, else
// the error code indicating the failure.
//
//////////////////////////
function Ping(HostName: String; var Timeout: DWORD): Integer;
//////////////////////////
//
// ResolveAddress
//
// HostName = [in] The name of the host system to get the network address
// for. The name can be in either DNS format, or dotted name
// notation.
//
// Address = [out] The binary address of the host system on success.
//
// Returns
//
// Returns ERROR_SUCCESS if the host name was converted to a binary ip
// address, else the error code indicating the failure. On success, the
// Address param will be filled in with the binary ip address.
//
//////////////////////////
function ResolveAddress(HostName: String; out Address: DWORD): Integer;
//////////////////////////
//
// TraceRoute
//
// HostName = [in] The name of the host system to trace a route to. The
// name can be in either DNS format, or dotted name notation.
//
// CallBack = [in] The function to callback when an address hop is
// resolved. This is not a required param, but if passed in,
// returning a False result from the callback will cause the
// trace to complete with a result of ERROR_CANCELLED.
//
// lParam = [in] Specifies a 32-bit, application-defined value to be
// passed to the callback function.
//
// Returns
//
// If the trace completes, an ERROR_SUCCESS will be returned. Otherwise,
// the error code causing the failure will be returned.
//
//////////////////////////
type
TTraceCallback = function(HopNumber: Byte; Address: PChar; RoundTripTime: Cardinal; lParam: Integer): Boolean;
function TraceRoute(HostName: String; CallBack: TTraceCallback; lParam: Integer): Integer;
implementation
//////////////////////////
// Protected variables
//////////////////////////
var
lpData: TWSAData;
function ResolveAddress(HostName: String; out Address: DWORD): Integer;
var lpHost: PHostEnt;
begin
// Set default address
Address:=DWORD(INADDR_NONE
// Resource protection
try
// Check host name length
if (Length(HostName) > 0) then
begin
// Try converting the hostname
Address:=inet_addr(PChar(H
// Check address
if (DWORD(Address) = DWORD(INADDR_NONE)) then
begin
// Attempt to get host by name
lpHost:=gethostbyname(PCha
// Check host ent structure for valid ip address
if Assigned(lpHost) and Assigned(lpHost^.h_addr_li
begin
// Get the address from the list
Address:=u_long(PLongInt(l
end;
end;
end;
finally
// Check result address
if (DWORD(Address) = DWORD(INADDR_NONE)) then
// Invalid host specified
result:=IP_BAD_DESTINATION
else
// Converted correctly
result:=ERROR_SUCCESS;
end;
end;
function GetTraceRouteCallback(HopN
begin
// Resource protection
try
// Add address and round trip time to list
TStrings(lParam).AddObject
finally
// Keep enumerating
result:=True;
end;
end;
function GetTraceRoute(HostName: String; List: TStrings): Integer;
begin
// Check the passed list
if Assigned(List) then
begin
// Lock the list
List.BeginUpdate;
// Resource protection
try
// Clear the list
List.Clear;
// Perform the trace route
result:=TraceRoute(HostNam
finally
// Unlock the list
List.EndUpdate;
end;
end
else
// List must be passed
result:=ERROR_INVALID_PARA
end;
function Ping(HostName: String; var Timeout: Cardinal): Integer;
var lpSend: Array [0..7] of Integer;
lpReply: PICMPEchoReply;
icmpHandle: THandle;
dwAddress: DWORD;
dwTTL: DWORD;
begin
// Convert the address
result:=ResolveAddress(Hos
// Check result
if (result = ERROR_SUCCESS) then
begin
// Open an icmp handle
icmpHandle:=IcmpCreateFile
// Check handle
if not(icmpHandle = INVALID_HANDLE_VALUE) then
begin
// Resource protection
try
// Allocate memory for reply
lpReply:=AllocMem(SizeOf(I
// Resource protection
try
// Get timeout value
if (Timeout = 0) then
// Use default
dwTTL:=PING_DEF_TIMEOUT
else
// Use passed value
dwTTL:=Timeout;
// Send echo to the host
if (IcmpSendEcho(icmpHandle, in_addr(dwAddress), @lpSend, SizeOf(lpSend), nil, lpReply, SizeOf(ICMP_ECHO_REPLY) + SizeOf(lpSend), dwTTL) = 1) then
begin
// Fill in the round trip time
Timeout:=lpReply^.RTTime;
// Return the status
result:=lpReply^.Status;
end
else
// Request timed out
result:=IP_REQ_TIMED_OUT;
finally
// Free memory
FreeMem(lpReply);
end;
finally
// Close handle
IcmpCloseHandle(icmpHandle
end;
end
else
// Failed to open icmp handle, return last error
result:=GetLastError;
end;
end;
function TraceRoute(HostName: String; CallBack: TTraceCallback; lParam: Integer): Integer;
var lpSend: Array [0..7] of Integer;
lpAddr: Array [0..255] of Char;
lpOpts: TICMPOptionInformation;
lpReply: PICMPEchoReply;
icmpHandle: THandle;
dwAddress: DWORD;
dwAttempt: Integer;
dwTTL: Integer;
begin
// Convert the address
result:=ResolveAddress(Hos
// Check result
if (result = ERROR_SUCCESS) then
begin
// Open an icmp handle
icmpHandle:=IcmpCreateFile
// Check handle
if not(icmpHandle = INVALID_HANDLE_VALUE) then
begin
// Resource protection
try
// Clear option structure
FillChar(lpOpts, SizeOf(lpOpts), 0);
// Allocate memory for reply
lpReply:=AllocMem(SizeOf(I
// Resource protection
try
// Set starting TTL count
dwTTL:=1;
// Perform the trace route
while (dwTTL < 256) do
begin
// Set option time to live
lpOpts.Ttl:=Char(dwTTL);
// Set starting attempt
dwAttempt:=0;
// Retry up to 3 times
while (dwAttempt < 3) do
begin
// Send 32 bytes of data, break on success
if (IcmpSendEcho(icmpHandle, in_addr(dwAddress), @lpSend, SizeOf(lpSend), @lpOpts, lpReply, SizeOf(ICMP_ECHO_REPLY) + SizeOf(lpSend), 1000) = 1) then break;
// Increment the attempt
Inc(dwAttempt);
end;
// Check attempt counter
if (dwAttempt = 3) then
begin
// Set result
result:=IP_REQ_TIMED_OUT;
// Done
break;
end;
// Check callback
if Assigned(Callback) then
begin
// Copy address
StrLCopy(@lpAddr, inet_ntoa(in_addr(lpReply^
// If false is returned then we need to stop the trace route
if not(Callback(Ord(dwTTL), lpAddr, lpReply^.RTTime, lParam)) then
begin
// Set result
result:=ERROR_CANCELLED;
// Done
break;
end;
end;
// Check reply status, if IP_SUCCESS then we have reached the desired host
if (lpReply^.Status = IP_SUCCESS) then break;
// Increment the time to live
Inc(dwTTL);
end;
finally
// Free memory
FreeMem(lpReply);
end;
finally
// Close handle
IcmpCloseHandle(icmpHandle
end;
end
else
// Failed to open icmp handle, return last error
result:=GetLastError;
end;
end;
function GetLocalIPAddress: String;
var lpszName: Array [0..MAX_PATH] of Char;
lpHost: PHostEnt;
begin
// Get host name
if (gethostname(@lpszName, SizeOf(lpszName)) = 0) then
begin
// Get host ent structure from remote name
lpHost:=gethostbyname(@lps
// Check host ent structure for valid ip address
if Assigned(lpHost) and Assigned(lpHost^.h_addr_li
// Convert to dot notation string
result:=inet_ntoa(in_addr(
else
// Failed to resolve name
SetLength(result, 0);
end
else
// Failed to get name
SetLength(result, 0);
end;
initialization
// Initialize winsock
WSAStartup(MakeWord(1, 1), lpData);
finalization
// Cleanup winsock
WSACleanup;
end.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
rllibby, thanks for ICMPUtils.pas - it's saved me several hours of work. I noticed your website doesn't have your latest version (posted above) hosted there. It'd be great to see that up to date.
I found it necessary to change typecast inet_addr() in line 201.
I found it necessary to change typecast inet_addr() in line 201.
Address := inet_addr(PChar(HostName)) ;
becomes
Address := DWORD(inet_addr(PChar(Host Name)));
in order to avoid range check errors.
The app pinged several servers. 1 thread for each server (or ip address)
In the end I got this code (extract) using Indy TIdIcmpClient
FIFOPush is just a stacked log routine
Open in new window