1 {*_* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
3 Author: François PIETTE
4 Description: This unit encapsulate the ICMP.DLL into an object of type TICMP.
5 Using this object, you can easily ping any host on your network.
6 Works only in 32 bits mode (no Delphi 1) under NT or 95.
7 TICMP is perfect for a console mode program, but if you build a
8 GUI program, you could use the TPing object wich is a true VCL
9 encapsulating the TICMP object. Then you can use object inspector
10 to change properties or event handler. This is much simpler to
11 use for a GUI program.
12 EMail: http://users.swing.be/francois.piette francois.piette@swing.be
13 http://www.rtfm.be/fpiette francois.piette@rtfm.be
14 francois.piette@pophost.eunet.be
15 Creation: January 6, 1997
17 Support: Use the mailing list twsocket@rtfm.be See website for details.
18 Legal issues: Copyright (C) 1997-2000 by François PIETTE
19 Rue de Grady 24, 4053 Embourg, Belgium. Fax: +32-4-365.74.56
20 <francois.piette@pophost.eunet.be>
22 This software is provided 'as-is', without any express or
23 implied warranty. In no event will the author be held liable
24 for any damages arising from the use of this software.
26 Permission is granted to anyone to use this software for any
27 purpose, including commercial applications, and to alter it
28 and redistribute it freely, subject to the following
31 1. The origin of this software must not be misrepresented,
32 you must not claim that you wrote the original software.
33 If you use this software in a product, an acknowledgment
34 in the product documentation would be appreciated but is
37 2. Altered source versions must be plainly marked as such, and
38 must not be misrepresented as being the original software.
40 3. This notice may not be removed or altered from any source
43 4. You must register this software by sending a picture postcard
44 to the author. Use a nice stamp and mention your name, street
45 address, EMail address and any comment you like to say.
48 Dec 13, 1997 V1.01 Added OnEchoRequest and OnEchoReply events and removed the
49 corresponding OnDisplay event. This require to modify existing
51 Mar 15, 1998 V1.02 Deplaced address resolution just before use
52 Sep 24, 1998 V1.93 Changed TIPAddr and others to LongInt to avoid range error
53 problems with Delphi 4
54 Jan 24, 1999 V1.11 Surfaced Flags property to allow fragmentation check
55 (Flags = IP_FLAG_DF to enable fragmentation check)
58 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
64 // This source file is *NOT* compatible with Delphi 1 because it uses
69 Windows
, SysUtils
, Classes
, WinSock
, kol
;
75 // IP status codes returned to transports and user IOCTLs.
77 IP_STATUS_BASE
= 11000;
78 IP_BUF_TOO_SMALL
= (IP_STATUS_BASE
+ 1);
79 IP_DEST_NET_UNREACHABLE
= (IP_STATUS_BASE
+ 2);
80 IP_DEST_HOST_UNREACHABLE
= (IP_STATUS_BASE
+ 3);
81 IP_DEST_PROT_UNREACHABLE
= (IP_STATUS_BASE
+ 4);
82 IP_DEST_PORT_UNREACHABLE
= (IP_STATUS_BASE
+ 5);
83 IP_NO_RESOURCES
= (IP_STATUS_BASE
+ 6);
84 IP_BAD_OPTION
= (IP_STATUS_BASE
+ 7);
85 IP_HW_ERROR
= (IP_STATUS_BASE
+ 8);
86 IP_PACKET_TOO_BIG
= (IP_STATUS_BASE
+ 9);
87 IP_REQ_TIMED_OUT
= (IP_STATUS_BASE
+ 10);
88 IP_BAD_REQ
= (IP_STATUS_BASE
+ 11);
89 IP_BAD_ROUTE
= (IP_STATUS_BASE
+ 12);
90 IP_TTL_EXPIRED_TRANSIT
= (IP_STATUS_BASE
+ 13);
91 IP_TTL_EXPIRED_REASSEM
= (IP_STATUS_BASE
+ 14);
92 IP_PARAM_PROBLEM
= (IP_STATUS_BASE
+ 15);
93 IP_SOURCE_QUENCH
= (IP_STATUS_BASE
+ 16);
94 IP_OPTION_TOO_BIG
= (IP_STATUS_BASE
+ 17);
95 IP_BAD_DESTINATION
= (IP_STATUS_BASE
+ 18);
97 // status codes passed up on status indications.
98 IP_ADDR_DELETED
= (IP_STATUS_BASE
+ 19);
99 IP_SPEC_MTU_CHANGE
= (IP_STATUS_BASE
+ 20);
100 IP_MTU_CHANGE
= (IP_STATUS_BASE
+ 21);
102 IP_GENERAL_FAILURE
= (IP_STATUS_BASE
+ 50);
104 MAX_IP_STATUS
= IP_GENERAL_FAILURE
;
106 IP_PENDING
= (IP_STATUS_BASE
+ 255);
109 IP_FLAG_DF
= $02; // Don't fragment this packet.
112 IP_OPT_EOL
= $00; // End of list option
113 IP_OPT_NOP
= $01; // No operation
114 IP_OPT_SECURITY
= $82; // Security option.
115 IP_OPT_LSRR
= $83; // Loose source route.
116 IP_OPT_SSRR
= $89; // Strict source route.
117 IP_OPT_RR
= $07; // Record route.
118 IP_OPT_TS
= $44; // Timestamp.
119 IP_OPT_SID
= $88; // Stream ID (obsolete)
124 TIPAddr
= LongInt; // An IP address.
125 TIPMask
= LongInt; // An IP subnet mask.
126 TIPStatus
= LongInt; // Status code returned from IP APIs.
128 PIPOptionInformation
= ^TIPOptionInformation
;
129 TIPOptionInformation
= packed record
130 TTL
: Byte; // Time To Live (used for traceroute)
131 TOS
: Byte; // Type Of Service (usually 0)
132 Flags
: Byte; // IP header flags (usually 0)
133 OptionsSize
: Byte; // Size of options data (usually 0, max 40)
134 OptionsData
: PChar
; // Options data buffer
137 PIcmpEchoReply
= ^TIcmpEchoReply
;
138 TIcmpEchoReply
= packed record
139 Address
: TIPAddr
; // Replying address
140 Status
: DWord
; // IP status value
141 RTT
: DWord
; // Round Trip Time in milliseconds
142 DataSize
: Word; // Reply data size
143 Reserved
: Word; // Reserved
144 Data
: Pointer; // Pointer to reply data buffer
145 Options
: TIPOptionInformation
; // Reply options
149 // Opens a handle on which ICMP Echo Requests can be issued.
153 // An open file handle or INVALID_HANDLE_VALUE. Extended error information
154 // is available by calling GetLastError().
155 TIcmpCreateFile
= function: THandle
; stdcall;
158 // Closes a handle opened by ICMPOpenFile.
160 // IcmpHandle - The handle to close.
162 // TRUE if the handle was closed successfully, otherwise FALSE. Extended
163 // error information is available by calling GetLastError().
164 TIcmpCloseHandle
= function(IcmpHandle
: THandle
): Boolean; stdcall;
167 // Sends an ICMP Echo request and returns one or more replies. The
168 // call returns when the timeout has expired or the reply buffer
171 // IcmpHandle - An open handle returned by ICMPCreateFile.
172 // DestinationAddress - The destination of the echo request.
173 // RequestData - A buffer containing the data to send in the
175 // RequestSize - The number of bytes in the request data buffer.
176 // RequestOptions - Pointer to the IP header options for the request.
178 // ReplyBuffer - A buffer to hold any replies to the request.
179 // On return, the buffer will contain an array of
180 // ICMP_ECHO_REPLY structures followed by options
181 // and data. The buffer should be large enough to
182 // hold at least one ICMP_ECHO_REPLY structure
183 // and 8 bytes of data - this is the size of
184 // an ICMP error message.
185 // ReplySize - The size in bytes of the reply buffer.
186 // Timeout - The time in milliseconds to wait for replies.
188 // Returns the number of replies received and stored in ReplyBuffer. If
189 // the return value is zero, extended error information is available
190 // via GetLastError().
191 TIcmpSendEcho
= function(IcmpHandle
: THandle
;
192 DestinationAddress
: TIPAddr
;
193 RequestData
: Pointer;
195 RequestOptions
: PIPOptionInformation
;
196 ReplyBuffer
: Pointer;
201 // Event handler type declaration for TICMP.OnDisplay event.
202 TICMPDisplay
= procedure(Sender
: PObj
; Msg
: String) of object;
203 TICMPReply
= procedure(Sender
: PObj
; Error
: Integer) of object;
205 // The object wich encapsulate the ICMP.DLL
208 hICMPdll
: HModule
; // Handle for ICMP.DLL
209 IcmpCreateFile
: TIcmpCreateFile
;
210 IcmpCloseHandle
: TIcmpCloseHandle
;
211 IcmpSendEcho
: TIcmpSendEcho
;
212 hICMP
: THandle
; // Handle for the ICMP Calls
213 FReply
: TIcmpEchoReply
; // ICMP Echo reply buffer
214 FAddress
: String; // Address given
215 FHostName
: String; // Dotted IP of host (output)
216 FHostIP
: String; // Name of host (Output)
217 FIPAddress
: TIPAddr
; // Address of host to contact
218 FSize
: Integer; // Packet size (default to 56)
219 FTimeOut
: Integer; // Timeout (default to 4000mS)
220 FTTL
: Integer; // Time To Live (for send)
221 FFlags
: Integer; // Options flags
222 FOnDisplay
: TICMPDisplay
; // Event handler to display
223 FOnEchoRequest
: TOnEvent
;
224 FOnEchoReply
: TICMPReply
;
225 FLastError
: DWORD
; // After sending ICMP packet
226 FAddrResolved
: Boolean;
227 procedure ResolveAddr
;
229 // constructor Create; virtual;
230 destructor Destroy
; virtual;//override;
231 function Ping
: Integer;
232 procedure SetAddress(Value
: String);
233 function GetErrorString
: String;
235 property Address
: String read FAddress write SetAddress
;
236 property Size
: Integer read FSize write FSize
;
237 property Timeout
: Integer read FTimeout write FTimeout
;
238 property Reply
: TIcmpEchoReply read FReply
;
239 property TTL
: Integer read FTTL write FTTL
;
240 Property Flags
: Integer read FFlags write FFlags
;
241 property ErrorCode
: DWORD read FLastError
;
242 property ErrorString
: String read GetErrorString
;
243 property HostName
: String read FHostName
;
244 property HostIP
: String read FHostIP
;
245 property OnDisplay
: TICMPDisplay read FOnDisplay write FOnDisplay
;
246 property OnEchoRequest
: TOnEvent read FOnEchoRequest
247 write FOnEchoRequest
;
248 property OnEchoReply
: TICMPReply read FOnEchoReply
254 // TICMPException = class(Exception);
255 function NewICMP
:PICMP
;
259 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
260 //constructor TICMP.Create;
261 function NewICMP
:PICMP
;
265 New( Result
, Create
);
268 hICMP
:= INVALID_HANDLE_VALUE
;
273 // initialise winsock
274 if WSAStartup($101, WSAData
) <> 0 then
276 // raise TICMPException.Create('Error initialising Winsock');
278 // register the icmp.dll stuff
279 hICMPdll
:= LoadLibrary(icmpDLL
);
280 // if hICMPdll = 0 then
281 // raise TICMPException.Create('Unable to register ' + icmpDLL);
283 @ICMPCreateFile
:= GetProcAddress(hICMPdll
, 'IcmpCreateFile');
284 @IcmpCloseHandle
:= GetProcAddress(hICMPdll
, 'IcmpCloseHandle');
285 @IcmpSendEcho
:= GetProcAddress(hICMPdll
, 'IcmpSendEcho');
287 { if (@ICMPCreateFile = Nil) or
288 (@IcmpCloseHandle = Nil) or
289 (@IcmpSendEcho = Nil) then
290 raise TICMPException.Create('Error loading dll functions');
293 hICMP
:= IcmpCreateFile
;
294 // if hICMP = INVALID_HANDLE_VALUE then
295 // raise TICMPException.Create('Unable to get ping handle');
300 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
301 destructor TICMP
.Destroy
;
303 if hICMP
<> INVALID_HANDLE_VALUE
then
304 IcmpCloseHandle(hICMP
);
305 if hICMPdll
<> 0 then
306 FreeLibrary(hICMPdll
);
312 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
313 function MinInteger(X
, Y
: Integer): Integer;
322 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
323 procedure TICMP
.ResolveAddr
;
325 Phe
: PHostEnt
; // HostEntry buffer for name lookup
327 // Convert host address to IP address
328 FIPAddress
:= inet_addr(PChar(FAddress
));
329 if FIPAddress
<> LongInt(INADDR_NONE
) then
330 // Was a numeric dotted address let it in this format
331 FHostName
:= FAddress
333 // Not a numeric dotted address, try to resolve by name
334 Phe
:= GetHostByName(PChar(FAddress
));
335 if Phe
= nil then begin
336 FLastError
:= GetLastError
;
337 if Assigned(FOnDisplay
) then
338 FOnDisplay(@Self
, 'Unable to resolve ' + FAddress
);
342 FIPAddress
:= longint(plongint(Phe
^.h_addr_list
^)^);
343 FHostName
:= Phe
^.h_name
;
346 FHostIP
:= StrPas(inet_ntoa(TInAddr(FIPAddress
)));
347 FAddrResolved
:= TRUE;
351 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
352 procedure TICMP
.SetAddress(Value
: String);
354 // Only change if needed (could take a long time)
355 if FAddress
= Value
then
358 FAddrResolved
:= FALSE;
363 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
364 function TICMP
.GetErrorString
: String;
367 IP_SUCCESS
: Result
:= 'No error';
368 IP_BUF_TOO_SMALL
: Result
:= 'Buffer too small';
369 IP_DEST_NET_UNREACHABLE
: Result
:= 'Destination network unreachable';
370 IP_DEST_HOST_UNREACHABLE
: Result
:= 'Destination host unreachable';
371 IP_DEST_PROT_UNREACHABLE
: Result
:= 'Destination protocol unreachable';
372 IP_DEST_PORT_UNREACHABLE
: Result
:= 'Destination port unreachable';
373 IP_NO_RESOURCES
: Result
:= 'No resources';
374 IP_BAD_OPTION
: Result
:= 'Bad option';
375 IP_HW_ERROR
: Result
:= 'Hardware error';
376 IP_PACKET_TOO_BIG
: Result
:= 'Packet too big';
377 IP_REQ_TIMED_OUT
: Result
:= 'Request timed out';
378 IP_BAD_REQ
: Result
:= 'Bad request';
379 IP_BAD_ROUTE
: Result
:= 'Bad route';
380 IP_TTL_EXPIRED_TRANSIT
: Result
:= 'TTL expired in transit';
381 IP_TTL_EXPIRED_REASSEM
: Result
:= 'TTL expired in reassembly';
382 IP_PARAM_PROBLEM
: Result
:= 'Parameter problem';
383 IP_SOURCE_QUENCH
: Result
:= 'Source quench';
384 IP_OPTION_TOO_BIG
: Result
:= 'Option too big';
385 IP_BAD_DESTINATION
: Result
:= 'Bad Destination';
386 IP_ADDR_DELETED
: Result
:= 'Address deleted';
387 IP_SPEC_MTU_CHANGE
: Result
:= 'Spec MTU change';
388 IP_MTU_CHANGE
: Result
:= 'MTU change';
389 IP_GENERAL_FAILURE
: Result
:= 'General failure';
390 IP_PENDING
: Result
:= 'Pending';
392 Result
:= 'ICMP error #' + IntToStr(FLastError
);
397 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
398 function TICMP
.Ping
: Integer;
401 pReqData
, pData
: Pointer;
402 pIPE
: PIcmpEchoReply
; // ICMP Echo reply buffer
403 IPOpt
: TIPOptionInformation
; // IP Options for packet to send
409 if not FAddrResolved
then
412 if FIPAddress
= LongInt(INADDR_NONE
) then begin
413 FLastError
:= IP_BAD_DESTINATION
;
414 if Assigned(FOnDisplay
) then
415 FOnDisplay(@Self
, 'Invalid host address');
419 // Allocate space for data buffer space
420 BufferSize
:= SizeOf(TICMPEchoReply
) + FSize
;
421 GetMem(pReqData
, FSize
);
422 GetMem(pData
, FSize
);
423 GetMem(pIPE
, BufferSize
);
426 // Fill data buffer with some data bytes
427 FillChar(pReqData
^, FSize
, $20);
428 Msg
:= 'Pinging from Delphi code written by F. Piette';
429 Move(Msg
[1], pReqData
^, MinInteger(FSize
, Length(Msg
)));
432 FillChar(pIPE
^, SizeOf(pIPE
^), 0);
434 if Assigned(FOnEchoRequest
) then
435 FOnEchoRequest(@Self
);
437 FillChar(IPOpt
, SizeOf(IPOpt
), 0);
439 IPOpt
.Flags
:= FFlags
;
440 Result
:= IcmpSendEcho(hICMP
, FIPAddress
, pReqData
, FSize
,
441 @IPOpt
, pIPE
, BufferSize
, FTimeOut
);
442 FLastError
:= GetLastError
;
445 if Assigned(FOnEchoReply
) then
446 FOnEchoReply(@Self
, Result
);
448 // Free those buffers
456 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}