initial commit
[rofl0r-KOL.git] / units / ics / KOLIcmp.pas
blobaef85d7c1e6cfe23c19d2b824289671fa79083f3
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
16 Version: 1.04
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
29 restrictions:
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
35 not required.
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
41 distribution.
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.
47 Updates:
48 Dec 13, 1997 V1.01 Added OnEchoRequest and OnEchoReply events and removed the
49 corresponding OnDisplay event. This require to modify existing
50 programs.
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 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
59 unit KOLIcmp;
61 interface
63 {$IFDEF VER80}
64 // This source file is *NOT* compatible with Delphi 1 because it uses
65 // Win 32 features.
66 {$ENDIF}
68 uses
69 Windows, SysUtils, Classes, WinSock, kol;
71 const
72 IcmpVersion = 102;
73 IcmpDLL = 'icmp.dll';
75 // IP status codes returned to transports and user IOCTLs.
76 IP_SUCCESS = 0;
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);
108 // IP header flags
109 IP_FLAG_DF = $02; // Don't fragment this packet.
111 // IP Option Types
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)
120 MAX_OPT_SIZE = $40;
122 type
123 // IP types
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
135 end;
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
146 end;
148 // IcmpCreateFile:
149 // Opens a handle on which ICMP Echo Requests can be issued.
150 // Arguments:
151 // None.
152 // Return Value:
153 // An open file handle or INVALID_HANDLE_VALUE. Extended error information
154 // is available by calling GetLastError().
155 TIcmpCreateFile = function: THandle; stdcall;
157 // IcmpCloseHandle:
158 // Closes a handle opened by ICMPOpenFile.
159 // Arguments:
160 // IcmpHandle - The handle to close.
161 // Return Value:
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;
166 // IcmpSendEcho:
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
169 // is filled.
170 // Arguments:
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
174 // request.
175 // RequestSize - The number of bytes in the request data buffer.
176 // RequestOptions - Pointer to the IP header options for the request.
177 // May be NULL.
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.
187 // Return Value:
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;
194 RequestSize: Word;
195 RequestOptions: PIPOptionInformation;
196 ReplyBuffer: Pointer;
197 ReplySize: DWord;
198 Timeout: DWord
199 ): DWord; stdcall;
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
206 TICMP = object(TObj)
207 private
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;
228 public
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
249 write FOnEchoReply;
250 end;
252 PICMP=^TICMP;
254 // TICMPException = class(Exception);
255 function NewICMP:PICMP;
257 implementation
259 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
260 //constructor TICMP.Create;
261 function NewICMP:PICMP;
263 WSAData: TWSAData;
264 begin
265 New( Result, Create );
266 with result^ do
267 begin
268 hICMP := INVALID_HANDLE_VALUE;
269 FSize := 56;
270 FTTL := 64;
271 FTimeOut := 4000;
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');
296 end;
297 end;
300 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
301 destructor TICMP.Destroy;
302 begin
303 if hICMP <> INVALID_HANDLE_VALUE then
304 IcmpCloseHandle(hICMP);
305 if hICMPdll <> 0 then
306 FreeLibrary(hICMPdll);
307 WSACleanup;
308 inherited Destroy;
309 end;
312 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
313 function MinInteger(X, Y: Integer): Integer;
314 begin
315 if X >= Y then
316 Result := Y
317 else
318 Result := X;
319 end;
322 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
323 procedure TICMP.ResolveAddr;
325 Phe : PHostEnt; // HostEntry buffer for name lookup
326 begin
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
332 else begin
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);
339 Exit;
340 end;
342 FIPAddress := longint(plongint(Phe^.h_addr_list^)^);
343 FHostName := Phe^.h_name;
344 end;
346 FHostIP := StrPas(inet_ntoa(TInAddr(FIPAddress)));
347 FAddrResolved := TRUE;
348 end;
351 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
352 procedure TICMP.SetAddress(Value : String);
353 begin
354 // Only change if needed (could take a long time)
355 if FAddress = Value then
356 Exit;
357 FAddress := Value;
358 FAddrResolved := FALSE;
359 // ResolveAddr;
360 end;
363 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
364 function TICMP.GetErrorString : String;
365 begin
366 case FLastError of
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';
391 else
392 Result := 'ICMP error #' + IntToStr(FLastError);
393 end;
394 end;
397 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
398 function TICMP.Ping : Integer;
400 BufferSize: Integer;
401 pReqData, pData: Pointer;
402 pIPE: PIcmpEchoReply; // ICMP Echo reply buffer
403 IPOpt: TIPOptionInformation; // IP Options for packet to send
404 Msg: String;
405 begin
406 Result := 0;
407 FLastError := 0;
409 if not FAddrResolved then
410 ResolveAddr;
412 if FIPAddress = LongInt(INADDR_NONE) then begin
413 FLastError := IP_BAD_DESTINATION;
414 if Assigned(FOnDisplay) then
415 FOnDisplay(@Self, 'Invalid host address');
416 Exit;
417 end;
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)));
431 pIPE^.Data := pData;
432 FillChar(pIPE^, SizeOf(pIPE^), 0);
434 if Assigned(FOnEchoRequest) then
435 FOnEchoRequest(@Self);
437 FillChar(IPOpt, SizeOf(IPOpt), 0);
438 IPOpt.TTL := FTTL;
439 IPOpt.Flags := FFlags;
440 Result := IcmpSendEcho(hICMP, FIPAddress, pReqData, FSize,
441 @IPOpt, pIPE, BufferSize, FTimeOut);
442 FLastError := GetLastError;
443 FReply := pIPE^;
445 if Assigned(FOnEchoReply) then
446 FOnEchoReply(@Self, Result);
447 finally
448 // Free those buffers
449 FreeMem(pIPE);
450 FreeMem(pData);
451 FreeMem(pReqData);
452 end;
453 end;
456 {* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}
458 end.