pingsend.pas 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709
  1. {==============================================================================|
  2. | Project : Ararat Synapse | 004.000.000 |
  3. |==============================================================================|
  4. | Content: PING sender |
  5. |==============================================================================|
  6. | Copyright (c)1999-2007, Lukas Gebauer |
  7. | All rights reserved. |
  8. | |
  9. | Redistribution and use in source and binary forms, with or without |
  10. | modification, are permitted provided that the following conditions are met: |
  11. | |
  12. | Redistributions of source code must retain the above copyright notice, this |
  13. | list of conditions and the following disclaimer. |
  14. | |
  15. | Redistributions in binary form must reproduce the above copyright notice, |
  16. | this list of conditions and the following disclaimer in the documentation |
  17. | and/or other materials provided with the distribution. |
  18. | |
  19. | Neither the name of Lukas Gebauer nor the names of its contributors may |
  20. | be used to endorse or promote products derived from this software without |
  21. | specific prior written permission. |
  22. | |
  23. | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
  24. | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
  25. | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
  26. | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR |
  27. | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
  28. | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
  29. | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
  30. | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
  31. | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
  32. | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
  33. | DAMAGE. |
  34. |==============================================================================|
  35. | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).|
  36. | Portions created by Lukas Gebauer are Copyright (c)2000-2007. |
  37. | All Rights Reserved. |
  38. |==============================================================================|
  39. | Contributor(s): |
  40. |==============================================================================|
  41. | History: see HISTORY.HTM from distribution package |
  42. | (Found at URL: http://www.ararat.cz/synapse/) |
  43. |==============================================================================}
  44. {:@abstract(ICMP PING implementation.)
  45. Allows create PING and TRACEROUTE. Or you can diagnose your network.
  46. This unit using IpHlpApi (on WinXP or higher) if available. Otherwise it trying
  47. to use RAW sockets.
  48. Warning: For use of RAW sockets you must have some special rights on some
  49. systems. So, it working allways when you have administator/root rights.
  50. Otherwise you can have problems!
  51. Note: This unit is NOT portable to .NET!
  52. Use native .NET classes for Ping instead.
  53. }
  54. {$IFDEF FPC}
  55. {$MODE DELPHI}
  56. {$ENDIF}
  57. {$Q-}
  58. {$R-}
  59. {$H+}
  60. {$IFDEF CIL}
  61. Sorry, this unit is not for .NET!
  62. {$ENDIF}
  63. unit pingsend;
  64. interface
  65. uses
  66. SysUtils,
  67. synsock, blcksock, synautil, synafpc, synaip
  68. {$IFDEF WIN32}
  69. , windows
  70. {$ENDIF}
  71. ;
  72. const
  73. ICMP_ECHO = 8;
  74. ICMP_ECHOREPLY = 0;
  75. ICMP_UNREACH = 3;
  76. ICMP_TIME_EXCEEDED = 11;
  77. //rfc-2292
  78. ICMP6_ECHO = 128;
  79. ICMP6_ECHOREPLY = 129;
  80. ICMP6_UNREACH = 1;
  81. ICMP6_TIME_EXCEEDED = 3;
  82. type
  83. {:List of possible ICMP reply packet types.}
  84. TICMPError = (
  85. IE_NoError,
  86. IE_Other,
  87. IE_TTLExceed,
  88. IE_UnreachOther,
  89. IE_UnreachRoute,
  90. IE_UnreachAdmin,
  91. IE_UnreachAddr,
  92. IE_UnreachPort
  93. );
  94. {:@abstract(Implementation of ICMP PING and ICMPv6 PING.)}
  95. TPINGSend = class(TSynaClient)
  96. private
  97. FSock: TICMPBlockSocket;
  98. FBuffer: string;
  99. FSeq: Integer;
  100. FId: Integer;
  101. FPacketSize: Integer;
  102. FPingTime: Integer;
  103. FIcmpEcho: Byte;
  104. FIcmpEchoReply: Byte;
  105. FIcmpUnreach: Byte;
  106. FReplyFrom: string;
  107. FReplyType: byte;
  108. FReplyCode: byte;
  109. FReplyError: TICMPError;
  110. FReplyErrorDesc: string;
  111. FTTL: Byte;
  112. Fsin: TVarSin;
  113. function Checksum(Value: string): Word;
  114. function Checksum6(Value: string): Word;
  115. function ReadPacket: Boolean;
  116. procedure TranslateError;
  117. procedure TranslateErrorIpHlp(value: integer);
  118. function InternalPing(const Host: string): Boolean;
  119. function InternalPingIpHlp(const Host: string): Boolean;
  120. function IsHostIP6(const Host: string): Boolean;
  121. procedure GenErrorDesc;
  122. public
  123. {:Send ICMP ping to host and count @link(pingtime). If ping OK, result is
  124. @true.}
  125. function Ping(const Host: string): Boolean;
  126. constructor Create;
  127. destructor Destroy; override;
  128. published
  129. {:Size of PING packet. Default size is 32 bytes.}
  130. property PacketSize: Integer read FPacketSize Write FPacketSize;
  131. {:Time between request and reply.}
  132. property PingTime: Integer read FPingTime;
  133. {:From this address is sended reply for your PING request. It maybe not your
  134. requested destination, when some error occured!}
  135. property ReplyFrom: string read FReplyFrom;
  136. {:ICMP type of PING reply. Each protocol using another values! For IPv4 and
  137. IPv6 are used different values!}
  138. property ReplyType: byte read FReplyType;
  139. {:ICMP code of PING reply. Each protocol using another values! For IPv4 and
  140. IPv6 are used different values! For protocol independent value look to
  141. @link(ReplyError)}
  142. property ReplyCode: byte read FReplyCode;
  143. {:Return type of returned ICMP message. This value is independent on used
  144. protocol!}
  145. property ReplyError: TICMPError read FReplyError;
  146. {:Return human readable description of returned packet type.}
  147. property ReplyErrorDesc: string read FReplyErrorDesc;
  148. {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.}
  149. property Sock: TICMPBlockSocket read FSock;
  150. {:TTL value for ICMP query}
  151. property TTL: byte read FTTL write FTTL;
  152. end;
  153. {:A very useful function and example of its use would be found in the TPINGSend
  154. object. Use it to ping to any host. If successful, returns the ping time in
  155. milliseconds. Returns -1 if an error occurred.}
  156. function PingHost(const Host: string): Integer;
  157. {:A very useful function and example of its use would be found in the TPINGSend
  158. object. Use it to TraceRoute to any host.}
  159. function TraceRouteHost(const Host: string): string;
  160. implementation
  161. type
  162. {:Record for ICMP ECHO packet header.}
  163. TIcmpEchoHeader = record
  164. i_type: Byte;
  165. i_code: Byte;
  166. i_checkSum: Word;
  167. i_Id: Word;
  168. i_seq: Word;
  169. TimeStamp: integer;
  170. end;
  171. {:record used internally by TPingSend for compute checksum of ICMPv6 packet
  172. pseudoheader.}
  173. TICMP6Packet = record
  174. in_source: TInAddr6;
  175. in_dest: TInAddr6;
  176. Length: integer;
  177. free0: Byte;
  178. free1: Byte;
  179. free2: Byte;
  180. proto: Byte;
  181. end;
  182. {$IFDEF WIN32}
  183. const
  184. DLLIcmpName = 'iphlpapi.dll';
  185. type
  186. TIP_OPTION_INFORMATION = packed record
  187. TTL: Byte;
  188. TOS: Byte;
  189. Flags: Byte;
  190. OptionsSize: Byte;
  191. OptionsData: PChar;
  192. end;
  193. PIP_OPTION_INFORMATION = ^TIP_OPTION_INFORMATION;
  194. TICMP_ECHO_REPLY = packed record
  195. Address: TInAddr;
  196. Status: integer;
  197. RoundTripTime: integer;
  198. DataSize: Word;
  199. Reserved: Word;
  200. Data: pointer;
  201. Options: TIP_OPTION_INFORMATION;
  202. end;
  203. PICMP_ECHO_REPLY = ^TICMP_ECHO_REPLY;
  204. TICMPV6_ECHO_REPLY = packed record
  205. Address: TSockAddrIn6;
  206. Status: integer;
  207. RoundTripTime: integer;
  208. end;
  209. PICMPV6_ECHO_REPLY = ^TICMPV6_ECHO_REPLY;
  210. TIcmpCreateFile = function: integer; stdcall;
  211. TIcmpCloseHandle = function(handle: integer): boolean; stdcall;
  212. TIcmpSendEcho2 = function(handle: integer; Event: pointer; ApcRoutine: pointer;
  213. ApcContext: pointer; DestinationAddress: TInAddr; RequestData: pointer;
  214. RequestSize: integer; RequestOptions: PIP_OPTION_INFORMATION;
  215. ReplyBuffer: pointer; ReplySize: integer; Timeout: Integer): integer; stdcall;
  216. TIcmp6CreateFile = function: integer; stdcall;
  217. TIcmp6SendEcho2 = function(handle: integer; Event: pointer; ApcRoutine: pointer;
  218. ApcContext: pointer; SourceAddress: PSockAddrIn6; DestinationAddress: PSockAddrIn6;
  219. RequestData: pointer; RequestSize: integer; RequestOptions: PIP_OPTION_INFORMATION;
  220. ReplyBuffer: pointer; ReplySize: integer; Timeout: Integer): integer; stdcall;
  221. var
  222. IcmpDllHandle: TLibHandle = 0;
  223. IcmpHelper4: boolean = false;
  224. IcmpHelper6: boolean = false;
  225. IcmpCreateFile: TIcmpCreateFile = nil;
  226. IcmpCloseHandle: TIcmpCloseHandle = nil;
  227. IcmpSendEcho2: TIcmpSendEcho2 = nil;
  228. Icmp6CreateFile: TIcmp6CreateFile = nil;
  229. Icmp6SendEcho2: TIcmp6SendEcho2 = nil;
  230. {$ENDIF}
  231. {==============================================================================}
  232. constructor TPINGSend.Create;
  233. begin
  234. inherited Create;
  235. FSock := TICMPBlockSocket.Create;
  236. FTimeout := 5000;
  237. FPacketSize := 32;
  238. FSeq := 0;
  239. Randomize;
  240. FTTL := 128;
  241. end;
  242. destructor TPINGSend.Destroy;
  243. begin
  244. FSock.Free;
  245. inherited Destroy;
  246. end;
  247. function TPINGSend.ReadPacket: Boolean;
  248. begin
  249. FBuffer := FSock.RecvPacket(Ftimeout);
  250. Result := FSock.LastError = 0;
  251. end;
  252. procedure TPINGSend.GenErrorDesc;
  253. begin
  254. case FReplyError of
  255. IE_NoError:
  256. FReplyErrorDesc := '';
  257. IE_Other:
  258. FReplyErrorDesc := 'Unknown error';
  259. IE_TTLExceed:
  260. FReplyErrorDesc := 'TTL Exceeded';
  261. IE_UnreachOther:
  262. FReplyErrorDesc := 'Unknown unreachable';
  263. IE_UnreachRoute:
  264. FReplyErrorDesc := 'No route to destination';
  265. IE_UnreachAdmin:
  266. FReplyErrorDesc := 'Administratively prohibited';
  267. IE_UnreachAddr:
  268. FReplyErrorDesc := 'Address unreachable';
  269. IE_UnreachPort:
  270. FReplyErrorDesc := 'Port unreachable';
  271. end;
  272. end;
  273. function TPINGSend.IsHostIP6(const Host: string): Boolean;
  274. var
  275. f: integer;
  276. begin
  277. f := AF_UNSPEC;
  278. if IsIp(Host) then
  279. f := AF_INET
  280. else
  281. if IsIp6(Host) then
  282. f := AF_INET6;
  283. synsock.SetVarSin(Fsin, host, '0', f,
  284. IPPROTO_UDP, SOCK_DGRAM, Fsock.PreferIP4);
  285. result := Fsin.sin_family = AF_INET6;
  286. end;
  287. function TPINGSend.Ping(const Host: string): Boolean;
  288. var
  289. b: boolean;
  290. begin
  291. FPingTime := -1;
  292. FReplyFrom := '';
  293. FReplyType := 0;
  294. FReplyCode := 0;
  295. FReplyError := IE_Other;
  296. GenErrorDesc;
  297. FBuffer := StringOfChar(#55, SizeOf(TICMPEchoHeader) + FPacketSize);
  298. {$IFDEF WIN32}
  299. b := IsHostIP6(host);
  300. if not(b) and IcmpHelper4 then
  301. result := InternalPingIpHlp(host)
  302. else
  303. if b and IcmpHelper6 then
  304. result := InternalPingIpHlp(host)
  305. else
  306. result := InternalPing(host);
  307. {$ELSE}
  308. result := InternalPing(host);
  309. {$ENDIF}
  310. end;
  311. function TPINGSend.InternalPing(const Host: string): Boolean;
  312. var
  313. IPHeadPtr: ^TIPHeader;
  314. IpHdrLen: Integer;
  315. IcmpEchoHeaderPtr: ^TICMPEchoHeader;
  316. t: Boolean;
  317. x: cardinal;
  318. IcmpReqHead: string;
  319. begin
  320. Result := False;
  321. FSock.TTL := FTTL;
  322. FSock.Bind(FIPInterface, cAnyPort);
  323. FSock.Connect(Host, '0');
  324. if FSock.LastError <> 0 then
  325. Exit;
  326. FSock.SizeRecvBuffer := 60 * 1024;
  327. if FSock.IP6used then
  328. begin
  329. FIcmpEcho := ICMP6_ECHO;
  330. FIcmpEchoReply := ICMP6_ECHOREPLY;
  331. FIcmpUnreach := ICMP6_UNREACH;
  332. end
  333. else
  334. begin
  335. FIcmpEcho := ICMP_ECHO;
  336. FIcmpEchoReply := ICMP_ECHOREPLY;
  337. FIcmpUnreach := ICMP_UNREACH;
  338. end;
  339. IcmpEchoHeaderPtr := Pointer(FBuffer);
  340. with IcmpEchoHeaderPtr^ do
  341. begin
  342. i_type := FIcmpEcho;
  343. i_code := 0;
  344. i_CheckSum := 0;
  345. FId := System.Random(32767);
  346. i_Id := FId;
  347. TimeStamp := GetTick;
  348. Inc(FSeq);
  349. i_Seq := FSeq;
  350. if fSock.IP6used then
  351. i_CheckSum := CheckSum6(FBuffer)
  352. else
  353. i_CheckSum := CheckSum(FBuffer);
  354. end;
  355. FSock.SendString(FBuffer);
  356. // remember first 8 bytes of ICMP packet
  357. IcmpReqHead := Copy(FBuffer, 1, 8);
  358. x := GetTick;
  359. repeat
  360. t := ReadPacket;
  361. if not t then
  362. break;
  363. if fSock.IP6used then
  364. begin
  365. {$IFNDEF WIN32}
  366. IcmpEchoHeaderPtr := Pointer(FBuffer);
  367. {$ELSE}
  368. //WinXP SP1 with networking update doing this think by another way ;-O
  369. // FBuffer := StringOfChar(#0, 4) + FBuffer;
  370. IcmpEchoHeaderPtr := Pointer(FBuffer);
  371. // IcmpEchoHeaderPtr^.i_type := FIcmpEchoReply;
  372. {$ENDIF}
  373. end
  374. else
  375. begin
  376. IPHeadPtr := Pointer(FBuffer);
  377. IpHdrLen := (IPHeadPtr^.VerLen and $0F) * 4;
  378. IcmpEchoHeaderPtr := @FBuffer[IpHdrLen + 1];
  379. end;
  380. //check for timeout
  381. if TickDelta(x, GetTick) > FTimeout then
  382. begin
  383. t := false;
  384. Break;
  385. end;
  386. //it discard sometimes possible 'echoes' of previosly sended packet
  387. //or other unwanted ICMP packets...
  388. until (IcmpEchoHeaderPtr^.i_type <> FIcmpEcho)
  389. and ((IcmpEchoHeaderPtr^.i_id = FId)
  390. or (Pos(IcmpReqHead, FBuffer) > 0));
  391. if t then
  392. begin
  393. FPingTime := TickDelta(x, GetTick);
  394. FReplyFrom := FSock.GetRemoteSinIP;
  395. FReplyType := IcmpEchoHeaderPtr^.i_type;
  396. FReplyCode := IcmpEchoHeaderPtr^.i_code;
  397. TranslateError;
  398. Result := True;
  399. end;
  400. end;
  401. function TPINGSend.Checksum(Value: string): Word;
  402. var
  403. CkSum: integer;
  404. Num, Remain: Integer;
  405. n, i: Integer;
  406. begin
  407. Num := Length(Value) div 2;
  408. Remain := Length(Value) mod 2;
  409. CkSum := 0;
  410. i := 1;
  411. for n := 0 to Num - 1 do
  412. begin
  413. CkSum := CkSum + Synsock.HtoNs(DecodeInt(Value, i));
  414. inc(i, 2);
  415. end;
  416. if Remain <> 0 then
  417. CkSum := CkSum + Ord(Value[Length(Value)]);
  418. CkSum := (CkSum shr 16) + (CkSum and $FFFF);
  419. CkSum := CkSum + (CkSum shr 16);
  420. Result := Word(not CkSum);
  421. end;
  422. function TPINGSend.Checksum6(Value: string): Word;
  423. const
  424. IOC_OUT = $40000000;
  425. IOC_IN = $80000000;
  426. IOC_INOUT = (IOC_IN or IOC_OUT);
  427. IOC_WS2 = $08000000;
  428. SIO_ROUTING_INTERFACE_QUERY = 20 or IOC_WS2 or IOC_INOUT;
  429. var
  430. ICMP6Ptr: ^TICMP6Packet;
  431. s: string;
  432. b: integer;
  433. ip6: TSockAddrIn6;
  434. x: integer;
  435. begin
  436. Result := 0;
  437. {$IFDEF WIN32}
  438. s := StringOfChar(#0, SizeOf(TICMP6Packet)) + Value;
  439. ICMP6Ptr := Pointer(s);
  440. x := synsock.WSAIoctl(FSock.Socket, SIO_ROUTING_INTERFACE_QUERY,
  441. @FSock.RemoteSin, SizeOf(FSock.RemoteSin),
  442. @ip6, SizeOf(ip6), @b, nil, nil);
  443. if x <> -1 then
  444. ICMP6Ptr^.in_dest := ip6.sin6_addr
  445. else
  446. ICMP6Ptr^.in_dest := FSock.LocalSin.sin6_addr;
  447. ICMP6Ptr^.in_source := FSock.RemoteSin.sin6_addr;
  448. ICMP6Ptr^.Length := synsock.htonl(Length(Value));
  449. ICMP6Ptr^.proto := IPPROTO_ICMPV6;
  450. Result := Checksum(s);
  451. {$ENDIF}
  452. end;
  453. procedure TPINGSend.TranslateError;
  454. begin
  455. if fSock.IP6used then
  456. begin
  457. case FReplyType of
  458. ICMP6_ECHOREPLY:
  459. FReplyError := IE_NoError;
  460. ICMP6_TIME_EXCEEDED:
  461. FReplyError := IE_TTLExceed;
  462. ICMP6_UNREACH:
  463. case FReplyCode of
  464. 0:
  465. FReplyError := IE_UnreachRoute;
  466. 3:
  467. FReplyError := IE_UnreachAddr;
  468. 4:
  469. FReplyError := IE_UnreachPort;
  470. 1:
  471. FReplyError := IE_UnreachAdmin;
  472. else
  473. FReplyError := IE_UnreachOther;
  474. end;
  475. else
  476. FReplyError := IE_Other;
  477. end;
  478. end
  479. else
  480. begin
  481. case FReplyType of
  482. ICMP_ECHOREPLY:
  483. FReplyError := IE_NoError;
  484. ICMP_TIME_EXCEEDED:
  485. FReplyError := IE_TTLExceed;
  486. ICMP_UNREACH:
  487. case FReplyCode of
  488. 0:
  489. FReplyError := IE_UnreachRoute;
  490. 1:
  491. FReplyError := IE_UnreachAddr;
  492. 3:
  493. FReplyError := IE_UnreachPort;
  494. 13:
  495. FReplyError := IE_UnreachAdmin;
  496. else
  497. FReplyError := IE_UnreachOther;
  498. end;
  499. else
  500. FReplyError := IE_Other;
  501. end;
  502. end;
  503. GenErrorDesc;
  504. end;
  505. procedure TPINGSend.TranslateErrorIpHlp(value: integer);
  506. begin
  507. case value of
  508. 11000, 0:
  509. FReplyError := IE_NoError;
  510. 11013:
  511. FReplyError := IE_TTLExceed;
  512. 11002:
  513. FReplyError := IE_UnreachRoute;
  514. 11003:
  515. FReplyError := IE_UnreachAddr;
  516. 11005:
  517. FReplyError := IE_UnreachPort;
  518. 11004:
  519. FReplyError := IE_UnreachAdmin;
  520. else
  521. FReplyError := IE_Other;
  522. end;
  523. GenErrorDesc;
  524. end;
  525. function TPINGSend.InternalPingIpHlp(const Host: string): Boolean;
  526. {$IFDEF WIN32}
  527. var
  528. PingIp6: boolean;
  529. PingHandle: integer;
  530. r: integer;
  531. ipo: TIP_OPTION_INFORMATION;
  532. RBuff: string;
  533. ip4reply: PICMP_ECHO_REPLY;
  534. ip6reply: PICMPV6_ECHO_REPLY;
  535. ip6: TSockAddrIn6;
  536. begin
  537. Result := False;
  538. PingIp6 := Fsin.sin_family = AF_INET6;
  539. if pingIp6 then
  540. PingHandle := Icmp6CreateFile
  541. else
  542. PingHandle := IcmpCreateFile;
  543. if PingHandle <> -1 then
  544. begin
  545. try
  546. ipo.TTL := FTTL;
  547. ipo.TOS := 0;
  548. ipo.Flags := 0;
  549. ipo.OptionsSize := 0;
  550. ipo.OptionsData := nil;
  551. setlength(RBuff, 4096);
  552. if pingIp6 then
  553. begin
  554. FillChar(ip6, sizeof(ip6), 0);
  555. r := Icmp6SendEcho2(PingHandle, nil, nil, nil, @ip6, @Fsin,
  556. Pchar(FBuffer), length(FBuffer), @ipo, pchar(RBuff), length(RBuff), FTimeout);
  557. if r > 0 then
  558. begin
  559. RBuff := #0 + #0 + RBuff;
  560. ip6reply := PICMPV6_ECHO_REPLY(pointer(RBuff));
  561. FPingTime := ip6reply^.RoundTripTime;
  562. ip6reply^.Address.sin6_family := AF_INET6;
  563. FReplyFrom := GetSinIp(TVarSin(ip6reply^.Address));
  564. TranslateErrorIpHlp(ip6reply^.Status);
  565. Result := True;
  566. end;
  567. end
  568. else
  569. begin
  570. r := IcmpSendEcho2(PingHandle, nil, nil, nil, Fsin.sin_addr,
  571. Pchar(FBuffer), length(FBuffer), @ipo, pchar(RBuff), length(RBuff), FTimeout);
  572. if r > 0 then
  573. begin
  574. ip4reply := PICMP_ECHO_REPLY(pointer(RBuff));
  575. FPingTime := ip4reply^.RoundTripTime;
  576. FReplyFrom := IpToStr(swapbytes(ip4reply^.Address.S_addr));
  577. TranslateErrorIpHlp(ip4reply^.Status);
  578. Result := True;
  579. end;
  580. end
  581. finally
  582. IcmpCloseHandle(PingHandle);
  583. end;
  584. end;
  585. end;
  586. {$ELSE}
  587. begin
  588. result := false;
  589. end;
  590. {$ENDIF}
  591. {==============================================================================}
  592. function PingHost(const Host: string): Integer;
  593. begin
  594. with TPINGSend.Create do
  595. try
  596. Result := -1;
  597. if Ping(Host) then
  598. if ReplyError = IE_NoError then
  599. Result := PingTime;
  600. finally
  601. Free;
  602. end;
  603. end;
  604. function TraceRouteHost(const Host: string): string;
  605. var
  606. Ping: TPingSend;
  607. ttl : byte;
  608. begin
  609. Result := '';
  610. Ping := TPINGSend.Create;
  611. try
  612. ttl := 1;
  613. repeat
  614. ping.TTL := ttl;
  615. inc(ttl);
  616. if ttl > 30 then
  617. Break;
  618. if not ping.Ping(Host) then
  619. begin
  620. Result := Result + cAnyHost+ ' Timeout' + CRLF;
  621. continue;
  622. end;
  623. if (ping.ReplyError <> IE_NoError)
  624. and (ping.ReplyError <> IE_TTLExceed) then
  625. begin
  626. Result := Result + Ping.ReplyFrom + ' ' + Ping.ReplyErrorDesc + CRLF;
  627. break;
  628. end;
  629. Result := Result + Ping.ReplyFrom + ' ' + IntToStr(Ping.PingTime) + CRLF;
  630. until ping.ReplyError = IE_NoError;
  631. finally
  632. Ping.Free;
  633. end;
  634. end;
  635. {$IFDEF WIN32}
  636. initialization
  637. begin
  638. IcmpHelper4 := false;
  639. IcmpHelper6 := false;
  640. IcmpDllHandle := LoadLibrary(DLLIcmpName);
  641. if IcmpDllHandle <> 0 then
  642. begin
  643. IcmpCreateFile := GetProcAddress(IcmpDLLHandle, 'IcmpCreateFile');
  644. IcmpCloseHandle := GetProcAddress(IcmpDLLHandle, 'IcmpCloseHandle');
  645. IcmpSendEcho2 := GetProcAddress(IcmpDLLHandle, 'IcmpSendEcho2');
  646. Icmp6CreateFile := GetProcAddress(IcmpDLLHandle, 'Icmp6CreateFile');
  647. Icmp6SendEcho2 := GetProcAddress(IcmpDLLHandle, 'Icmp6SendEcho2');
  648. IcmpHelper4 := assigned(IcmpCreateFile)
  649. and assigned(IcmpCloseHandle)
  650. and assigned(IcmpSendEcho2);
  651. IcmpHelper6 := assigned(Icmp6CreateFile)
  652. and assigned(Icmp6SendEcho2);
  653. end;
  654. end;
  655. finalization
  656. begin
  657. FreeLibrary(IcmpDllHandle);
  658. end;
  659. {$ENDIF}
  660. end.