IdIcmpClient.pas 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.8 2004-04-25 12:08:24 Mattias
  18. Fixed multithreading issue
  19. Rev 1.7 2004.02.03 4:16:42 PM czhower
  20. For unit name changes.
  21. Rev 1.6 2/1/2004 4:53:30 PM JPMugaas
  22. Removed Todo;
  23. Rev 1.5 2004.01.20 10:03:24 PM czhower
  24. InitComponent
  25. Rev 1.4 2003.12.31 10:37:54 PM czhower
  26. GetTickcount --> Ticks
  27. Rev 1.3 10/16/2003 11:06:14 PM SPerry
  28. Moved ICMP_MIN to IdRawHeaders
  29. Rev 1.2 2003.10.11 5:48:04 PM czhower
  30. -VCL fixes for servers
  31. -Chain suport for servers (Super core)
  32. -Scheduler upgrades
  33. -Full yarn support
  34. Rev 1.1 2003.09.30 1:22:56 PM czhower
  35. Stack split for DotNet
  36. Rev 1.0 11/13/2002 08:44:30 AM JPMugaas
  37. 25/1/02: SGrobety:
  38. Modified the component to support multithreaded PING and traceroute
  39. NOTE!!!
  40. The component no longer use the timing informations contained
  41. in the packet to compute the roundtrip time. This is because
  42. that information is only correctly set in case of ECHOREPLY
  43. In case of TTL, it is incorrect.
  44. }
  45. unit IdIcmpClient;
  46. {
  47. Note that we can NOT remove the DotNET IFDEFS from this unit. The reason is
  48. that Microsoft NET Framework 1.1 does not support ICMPv6 and that's required
  49. for IPv6. In Win32 and Linux, we definately can and want to support IPv6.
  50. If we support a later version of the NET framework that has a better API, I may
  51. consider revisiting this.
  52. }
  53. // SG 25/1/02: Modified the component to support multithreaded PING and traceroute
  54. interface
  55. {$I IdCompilerDefines.inc}
  56. //Put FPC into Delphi mode
  57. uses
  58. Classes,
  59. IdGlobal,
  60. IdRawBase,
  61. IdRawClient,
  62. IdStackConsts,
  63. IdBaseComponent;
  64. const
  65. DEF_PACKET_SIZE = 32;
  66. MAX_PACKET_SIZE = 1024;
  67. Id_TIDICMP_ReceiveTimeout = 5000;
  68. type
  69. TReplyStatusTypes = (rsEcho,
  70. rsError, rsTimeOut, rsErrorUnreachable,
  71. rsErrorTTLExceeded,rsErrorPacketTooBig,
  72. rsErrorParameter,
  73. rsErrorDatagramConversion,
  74. rsErrorSecurityFailure,
  75. rsSourceQuench,
  76. rsRedirect,
  77. rsTimeStamp,
  78. rsInfoRequest,
  79. rsAddressMaskRequest,
  80. rsTraceRoute,
  81. rsMobileHostReg,
  82. rsMobileHostRedir,
  83. rsIPv6WhereAreYou,
  84. rsIPv6IAmHere,
  85. rsSKIP);
  86. TReplyStatus = class(TObject)
  87. protected
  88. FBytesReceived: integer; // number of bytes in reply from host
  89. FFromIpAddress: string; // IP address of replying host
  90. FToIpAddress : string; //who receives it (i.e., us. This is for multihorned machines
  91. FMsgType: byte;
  92. FMsgCode : Byte;
  93. FSequenceId: word; // sequence id of ping reply
  94. // TODO: roundtrip time in ping reply should be float, not byte
  95. FMsRoundTripTime: UInt32; // ping round trip time in milliseconds
  96. FTimeToLive: byte; // time to live
  97. FReplyStatusType: TReplyStatusTypes;
  98. FPacketNumber : Integer;//number in packet for TraceRoute
  99. FHostName : String; //Hostname of computer that replied, used with TraceRoute
  100. FMsg : String;
  101. FRedirectTo : String; // valid only for rsRedirect
  102. public
  103. property RedirectTo : String read FRedirectTo write FRedirectTo;
  104. property Msg : String read FMsg write FMsg;
  105. property BytesReceived: integer read FBytesReceived write FBytesReceived; // number of bytes in reply from host
  106. property FromIpAddress: string read FFromIpAddress write FFromIpAddress; // IP address of replying host
  107. property ToIpAddress : string read FToIpAddress write FToIpAddress; //who receives it (i.e., us. This is for multihorned machines
  108. property MsgType: byte read FMsgType write FMsgType;
  109. property MsgCode : Byte read FMsgCode write FMsgCode;
  110. property SequenceId: word read FSequenceId write FSequenceId; // sequence id of ping reply
  111. // TODO: roundtrip time in ping reply should be float, not byte
  112. property MsRoundTripTime: UInt32 read FMsRoundTripTime write FMsRoundTripTime; // ping round trip time in milliseconds
  113. property TimeToLive: byte read FTimeToLive write FTimeToLive; // time to live
  114. property ReplyStatusType: TReplyStatusTypes read FReplyStatusType write FReplyStatusType;
  115. property HostName : String read FHostName write FHostName;
  116. property PacketNumber : Integer read FPacketNumber write FPacketNumber;
  117. end;
  118. TOnReplyEvent = procedure(ASender: TComponent; const AReplyStatus: TReplyStatus) of object;
  119. // TODO: on MacOSX (and maybe iOS?), can use a UDP socket instead of a RAW
  120. // socket so that non-privilege processes do not require root access...
  121. // TODO: on Windows, can use IcmpSendEcho() instead of a RAW so that
  122. // non-privilege processes do not require admin access...
  123. TIdCustomIcmpClient = class(TIdRawClient)
  124. protected
  125. FStartTime : TIdTicks; //this is a fallback if no packet is returned
  126. FPacketSize : Integer;
  127. FBufReceive: TIdBytes;
  128. FBufIcmp: TIdBytes;
  129. wSeqNo: word;
  130. iDataSize: integer;
  131. FReplyStatus: TReplyStatus;
  132. FOnReply: TOnReplyEvent;
  133. FReplydata: String;
  134. //
  135. {$IFNDEF DOTNET_1_1}
  136. function DecodeIPv6Packet(BytesRead: UInt32): Boolean;
  137. {$ENDIF}
  138. function DecodeIPv4Packet(BytesRead: UInt32): Boolean;
  139. function DecodeResponse(BytesRead: UInt32): Boolean;
  140. procedure DoReply; virtual;
  141. procedure GetEchoReply;
  142. procedure InitComponent; override;
  143. {$IFNDEF DOTNET_1_1}
  144. procedure PrepareEchoRequestIPv6(const ABuffer: String);
  145. {$ENDIF}
  146. procedure PrepareEchoRequestIPv4(const ABuffer: String);
  147. procedure PrepareEchoRequest(const ABuffer: String);
  148. procedure SendEchoRequest; overload;
  149. procedure SendEchoRequest(const AIP : String); overload;
  150. function GetPacketSize: Integer;
  151. procedure SetPacketSize(const AValue: Integer);
  152. //these are made public in the client
  153. procedure InternalPing(const AIP : String; const ABuffer: String = ''; SequenceID: Word = 0); overload; {Do not Localize}
  154. //
  155. property PacketSize : Integer read GetPacketSize write SetPacketSize default DEF_PACKET_SIZE;
  156. property ReplyData: string read FReplydata;
  157. property ReplyStatus: TReplyStatus read FReplyStatus;
  158. property OnReply: TOnReplyEvent read FOnReply write FOnReply;
  159. public
  160. destructor Destroy; override;
  161. procedure Send(const AHost: string; const APort: TIdPort; const ABuffer : TIdBytes); override;
  162. procedure Send(const ABuffer : TIdBytes); override;
  163. function Receive(ATimeOut: Integer): TReplyStatus;
  164. end;
  165. TIdIcmpClient = class(TIdCustomIcmpClient)
  166. public
  167. procedure Ping(const ABuffer: String = ''; SequenceID: Word = 0); {Do not Localize}
  168. property ReplyData;
  169. property ReplyStatus;
  170. published
  171. property Host;
  172. {$IFNDEF DOTNET_1_1}
  173. property IPVersion default ID_DEFAULT_IP_VERSION;
  174. {$ENDIF}
  175. property PacketSize;
  176. property ReceiveTimeout default Id_TIDICMP_ReceiveTimeout;
  177. property OnReply;
  178. end;
  179. implementation
  180. uses
  181. //facilitate inlining only.
  182. {$IFDEF WINDOWS}
  183. Windows,
  184. {$ENDIF}
  185. {$IFDEF USE_VCL_POSIX}
  186. {$IFDEF OSX}
  187. Macapi.CoreServices,
  188. {$ENDIF}
  189. {$ENDIF}
  190. IdExceptionCore, IdRawHeaders, IdResourceStringsCore,
  191. IdStack, IdStruct, SysUtils;
  192. { TIdCustomIcmpClient }
  193. procedure TIdCustomIcmpClient.PrepareEchoRequest(const ABuffer: String);
  194. begin
  195. {$IFNDEF DOTNET_1_1}
  196. if IPVersion = Id_IPv6 then begin
  197. PrepareEchoRequestIPv6(ABuffer);
  198. Exit;
  199. end;
  200. {$ENDIF}
  201. PrepareEchoRequestIPv4(ABuffer);
  202. end;
  203. { TIdIPv4_ICMP }
  204. type
  205. TIdIPv4_ICMP = class(TIdStruct)
  206. protected
  207. Fip_hdr: TIdIPHdr;
  208. Ficmp_hdr: TIdICMPHdr;
  209. function GetBytesLen: UInt32; override;
  210. public
  211. constructor Create; override;
  212. destructor Destroy; override;
  213. procedure ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32); override;
  214. procedure WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32); override;
  215. property ip_hdr: TIdIPHdr read Fip_hdr;
  216. property icmp_hdr: TIdICMPHdr read Ficmp_hdr;
  217. end;
  218. constructor TIdIPv4_ICMP.Create;
  219. begin
  220. inherited Create;
  221. Fip_hdr := TIdIPHdr.Create;
  222. Ficmp_hdr := TIdICMPHdr.Create;
  223. end;
  224. destructor TIdIPv4_ICMP.Destroy;
  225. begin
  226. FreeAndNil(Fip_hdr);
  227. FreeAndNil(Ficmp_hdr);
  228. inherited Destroy;
  229. end;
  230. function TIdIPv4_ICMP.GetBytesLen: UInt32;
  231. begin
  232. Result := inherited GetBytesLen + Fip_hdr.BytesLen + Ficmp_hdr.BytesLen;
  233. end;
  234. procedure TIdIPv4_ICMP.ReadStruct(const ABytes : TIdBytes; var VIndex : UInt32);
  235. begin
  236. inherited ReadStruct(ABytes, VIndex);
  237. Fip_hdr.ReadStruct(ABytes, VIndex);
  238. Ficmp_hdr.ReadStruct(ABytes, VIndex);
  239. end;
  240. procedure TIdIPv4_ICMP.WriteStruct(var VBytes : TIdBytes; var VIndex : UInt32);
  241. begin
  242. inherited WriteStruct(VBytes, VIndex);
  243. Fip_hdr.WriteStruct(VBytes, VIndex);
  244. Ficmp_hdr.WriteStruct(VBytes, VIndex);
  245. end;
  246. { TIdCustomIcmpClient }
  247. procedure TIdCustomIcmpClient.SendEchoRequest;
  248. begin
  249. Send(FBufIcmp);
  250. end;
  251. function TIdCustomIcmpClient.DecodeResponse(BytesRead: UInt32): Boolean;
  252. begin
  253. if BytesRead = 0 then begin
  254. // Timed out
  255. FReplyStatus.MsRoundTripTime := GetElapsedTicks(FStartTime);
  256. FReplyStatus.BytesReceived := 0;
  257. if IPVersion = Id_IPv4 then
  258. begin
  259. FReplyStatus.FromIpAddress := '0.0.0.0';
  260. FReplyStatus.ToIpAddress := '0.0.0.0';
  261. end else
  262. begin
  263. FReplyStatus.FromIpAddress := '::0';
  264. FReplyStatus.ToIpAddress := '::0';
  265. end;
  266. FReplyStatus.MsgType := 0;
  267. FReplyStatus.SequenceId := wSeqNo;
  268. FReplyStatus.TimeToLive := 0;
  269. FReplyStatus.ReplyStatusType := rsTimeOut;
  270. Result := True;
  271. end else
  272. begin
  273. FReplyStatus.ReplyStatusType := rsError;
  274. {$IFNDEF DOTNET_1_1}
  275. if IPVersion = Id_IPv6 then begin
  276. Result := DecodeIPv6Packet(BytesRead);
  277. Exit;
  278. end;
  279. {$ENDIF}
  280. Result := DecodeIPv4Packet(BytesRead);
  281. end;
  282. end;
  283. procedure TIdCustomIcmpClient.GetEchoReply;
  284. begin
  285. Receive(FReceiveTimeout);
  286. end;
  287. function TIdCustomIcmpClient.Receive(ATimeOut: Integer): TReplyStatus;
  288. var
  289. BytesRead : Integer;
  290. TripTime: UInt32;
  291. begin
  292. Result := FReplyStatus;
  293. FillBytes(FBufReceive, Length(FBufReceive), 0);
  294. FStartTime := Ticks64;
  295. repeat
  296. BytesRead := ReceiveBuffer(FBufReceive, ATimeOut);
  297. if DecodeResponse(BytesRead) then begin
  298. Break;
  299. end;
  300. TripTime := GetElapsedTicks(FStartTime);
  301. ATimeOut := ATimeOut - Integer(TripTime); // compute new timeout value
  302. FReplyStatus.MsRoundTripTime := TripTime;
  303. FReplyStatus.Msg := RSICMPTimeout;
  304. // We caught a response that wasn't meant for this thread - so we must
  305. // make sure we don't report it as such in case we time out after this
  306. FReplyStatus.BytesReceived := 0;
  307. if IPVersion = Id_IPv4 then
  308. begin
  309. FReplyStatus.FromIpAddress := '0.0.0.0';
  310. FReplyStatus.ToIpAddress := '0.0.0.0';
  311. end else
  312. begin
  313. FReplyStatus.FromIpAddress := '::0';
  314. FReplyStatus.ToIpAddress := '::0';
  315. end;
  316. FReplyStatus.MsgType := 0;
  317. FReplyStatus.SequenceId := wSeqNo;
  318. FReplyStatus.TimeToLive := 0;
  319. FReplyStatus.ReplyStatusType := rsTimeOut;
  320. until ATimeOut <= 0;
  321. end;
  322. procedure TIdCustomIcmpClient.DoReply;
  323. begin
  324. if Assigned(FOnReply) then begin
  325. FOnReply(Self, FReplyStatus);
  326. end;
  327. end;
  328. procedure TIdCustomIcmpClient.InitComponent;
  329. begin
  330. inherited InitComponent;
  331. FReplyStatus:= TReplyStatus.Create;
  332. FProtocol := Id_IPPROTO_ICMP;
  333. {$IFNDEF DOTNET_1_1}
  334. ProtocolIPv6 := Id_IPPROTO_ICMPv6;
  335. {$ENDIF}
  336. wSeqNo := 3489; // SG 25/1/02: Arbitrary Constant <> 0
  337. FReceiveTimeOut := Id_TIDICMP_ReceiveTimeout;
  338. FPacketSize := DEF_PACKET_SIZE;
  339. end;
  340. destructor TIdCustomIcmpClient.Destroy;
  341. begin
  342. FreeAndNil(FReplyStatus);
  343. inherited Destroy;
  344. end;
  345. function TIdCustomIcmpClient.DecodeIPv4Packet(BytesRead: UInt32): Boolean;
  346. var
  347. LIPHeaderLen: UInt32;
  348. LIdx: UInt32;
  349. RTTime: UInt32;
  350. LActualSeqID: UInt16;
  351. LIcmp: TIdIPv4_ICMP;
  352. LIcmpts: TIdICMPTs;
  353. begin
  354. Result := False;
  355. LIpHeaderLen := (FBufReceive[0] and $0F) * 4;
  356. if BytesRead < (LIpHeaderLen + ICMP_MIN) then begin
  357. raise EIdIcmpException.Create(RSICMPNotEnoughtBytes);
  358. end;
  359. LIdx := 0;
  360. LIcmp := TIdIPv4_ICMP.Create;
  361. try
  362. LIcmp.ReadStruct(FBufReceive, LIdx);
  363. {$IFDEF LINUX}
  364. // TODO: baffled as to why linux kernel sends back echo from localhost
  365. {$ENDIF}
  366. case LIcmp.icmp_hdr.icmp_type of
  367. Id_ICMP_ECHOREPLY, Id_ICMP_ECHO:
  368. begin
  369. FReplyStatus.ReplyStatusType := rsEcho;
  370. FReplyData := BytesToStringRaw(FBufReceive, LIdx, -1);
  371. // result is only valid if the seq. number is correct
  372. end;
  373. Id_ICMP_UNREACH:
  374. FReplyStatus.ReplyStatusType := rsErrorUnreachable;
  375. Id_ICMP_TIMXCEED:
  376. FReplyStatus.ReplyStatusType := rsErrorTTLExceeded;
  377. Id_ICMP_PARAMPROB :
  378. FReplyStatus.ReplyStatusType := rsErrorParameter;
  379. Id_ICMP_REDIRECT :
  380. FReplyStatus.ReplyStatusType := rsRedirect;
  381. Id_ICMP_TSTAMP, Id_ICMP_TSTAMPREPLY :
  382. FReplyStatus.ReplyStatusType := rsTimeStamp;
  383. Id_ICMP_IREQ, Id_ICMP_IREQREPLY :
  384. FReplyStatus.ReplyStatusType := rsInfoRequest;
  385. Id_ICMP_MASKREQ, Id_ICMP_MASKREPLY :
  386. FReplyStatus.ReplyStatusType := rsAddressMaskRequest;
  387. Id_ICMP_TRACEROUTE :
  388. FReplyStatus.ReplyStatusType := rsTraceRoute;
  389. Id_ICMP_DATAGRAM_CONV :
  390. FReplyStatus.ReplyStatusType := rsErrorDatagramConversion;
  391. Id_ICMP_MOB_HOST_REDIR :
  392. FReplyStatus.ReplyStatusType := rsMobileHostRedir;
  393. Id_ICMP_IPv6_WHERE_ARE_YOU :
  394. FReplyStatus.ReplyStatusType := rsIPv6WhereAreYou;
  395. Id_ICMP_IPv6_I_AM_HERE :
  396. FReplyStatus.ReplyStatusType := rsIPv6IAmHere;
  397. Id_ICMP_MOB_REG_REQ, Id_ICMP_MOB_REG_REPLY :
  398. FReplyStatus.ReplyStatusType := rsMobileHostReg;
  399. Id_ICMP_PHOTURIS :
  400. FReplyStatus.ReplyStatusType := rsErrorSecurityFailure;
  401. else
  402. raise EIdICMPException.Create(RSICMPNonEchoResponse);// RSICMPNonEchoResponse = 'Non-echo type response received'
  403. end; // case
  404. // check if we got a reply to the packet that was actually sent
  405. case FReplyStatus.ReplyStatusType of
  406. rsEcho:
  407. begin
  408. LActualSeqID := LIcmp.icmp_hdr.icmp_hun.echo_seq;
  409. RTTime := GetElapsedTicks(BytesToTicks(FBufReceive, LIdx));
  410. end;
  411. rsTimeStamp:
  412. begin
  413. LActualSeqID := LIcmp.icmp_hdr.icmp_hun.echo_seq;
  414. LIcmpts := TIdICMPTs.Create;
  415. try
  416. LIcmpts.ReadStruct(FBufReceive, LIpHeaderLen);
  417. RTTime := (LIcmpts.ttime and $80000000) - (LIcmpts.otime and $80000000);
  418. finally
  419. LIcmpts.Free;
  420. end;
  421. end;
  422. else
  423. begin
  424. // not an echo or timestamp reply: the original IP frame is
  425. // contained withing the DATA section of the packet...
  426. // pOriginalIP := PIdIPHdr(@picmp^.icmp_dun.data);
  427. // TODO: verify this! I don't think it is indexing far enough into the data
  428. LActualSeqID := BytesToUInt16(FBufReceive, LIpHeaderLen+8+6);//pOriginalICMP^.icmp_hun.echo.seq;
  429. RTTime := GetElapsedTicks(BytesToTicks(FBufReceive, LIpHeaderLen+8+8)); //pOriginalICMP^.icmp_dun.ts.otime;
  430. // move to offset
  431. // pOriginalICMP := Pointer(PtrUInt(pOriginalIP) + (iIpHeaderLen));
  432. // extract information from original ICMP frame
  433. // ActualSeqID := pOriginalICMP^.icmp_hun.echo.seq;
  434. // RTTime := Ticks64 - pOriginalICMP^.icmp_dun.ts.otime;
  435. // Result := pOriginalICMP^.icmp_hun.echo.seq = wSeqNo;
  436. end;
  437. end;
  438. Result := LActualSeqID = wSeqNo;//;picmp^.icmp_hun.echo.seq = wSeqNo;
  439. if Result then
  440. begin
  441. if FReplyStatus.ReplyStatusType = rsEcho then begin
  442. FReplyStatus.BytesReceived := BytesRead - (Id_IP_HSIZE + ICMP_MIN + SizeOf(TIdTicks));
  443. end else begin
  444. FReplyStatus.BytesReceived := BytesRead - (Id_IP_HSIZE + ICMP_MIN);
  445. end;
  446. FReplyStatus.FromIpAddress := MakeUInt32IntoIPv4Address(GStack.NetworkToHost(Licmp.ip_hdr.ip_src.s_l));
  447. FReplyStatus.ToIpAddress := MakeUInt32IntoIPv4Address(GStack.NetworkToHost(Licmp.ip_hdr.ip_dst.s_l));
  448. FReplyStatus.MsgType := LIcmp.icmp_hdr.icmp_type; //picmp^.icmp_type;
  449. FReplyStatus.MsgCode := LIcmp.icmp_hdr.icmp_code; //picmp^.icmp_code;
  450. FReplyStatus.SequenceId := LActualSeqID;
  451. FReplyStatus.MsRoundTripTime := RTTime;
  452. FReplyStatus.TimeToLive := LIcmp.ip_hdr.ip_ttl;
  453. // now process our message stuff
  454. case FReplyStatus.MsgType of
  455. Id_ICMP_UNREACH:
  456. begin
  457. case FReplyStatus.MsgCode of
  458. Id_ICMP_UNREACH_NET : FReplyStatus.Msg := RSICMPNetUnreachable;
  459. Id_ICMP_UNREACH_HOST : FReplyStatus.Msg := RSICMPHostUnreachable;
  460. Id_ICMP_UNREACH_PROTOCOL : FReplyStatus.Msg := RSICMPProtUnreachable;
  461. Id_ICMP_UNREACH_NEEDFRAG : FReplyStatus.Msg := RSICMPFragmentNeeded;
  462. Id_ICMP_UNREACH_SRCFAIL : FReplyStatus.Msg := RSICMPSourceRouteFailed;
  463. Id_ICMP_UNREACH_NET_UNKNOWN : FReplyStatus.Msg := RSICMPDestNetUnknown;
  464. Id_ICMP_UNREACH_HOST_UNKNOWN : FReplyStatus.Msg := RSICMPDestHostUnknown;
  465. Id_ICMP_UNREACH_ISOLATED : FReplyStatus.Msg := RSICMPSourceIsolated;
  466. Id_ICMP_UNREACH_NET_PROHIB : FReplyStatus.Msg := RSICMPDestNetProhibitted;
  467. Id_ICMP_UNREACH_HOST_PROHIB : FReplyStatus.Msg := RSICMPDestHostProhibitted;
  468. Id_ICMP_UNREACH_TOSNET : FReplyStatus.Msg := RSICMPTOSNetUnreach;
  469. Id_ICMP_UNREACH_TOSHOST : FReplyStatus.Msg := RSICMPTOSHostUnreach;
  470. Id_ICMP_UNREACH_FILTER_PROHIB : FReplyStatus.Msg := RSICMPAdminProhibitted;
  471. Id_ICMP_UNREACH_HOST_PRECEDENCE : FReplyStatus.Msg := RSICMPHostPrecViolation;
  472. Id_ICMP_UNREACH_PRECEDENCE_CUTOFF : FReplyStatus.Msg := RSICMPPrecedenceCutoffInEffect;
  473. end;
  474. end;
  475. Id_ICMP_TIMXCEED:
  476. begin
  477. case FReplyStatus.MsgCode of
  478. 0 : FReplyStatus.Msg := RSICMPTTLExceeded;
  479. 1 : FReplyStatus.Msg := RSICMPFragAsmExceeded;
  480. end;
  481. end;
  482. Id_ICMP_PARAMPROB : FReplyStatus.Msg := IndyFormat(RSICMPParamError, [FReplyStatus.MsgCode]);
  483. Id_ICMP_REDIRECT:
  484. begin
  485. FReplyStatus.RedirectTo := MakeUInt32IntoIPv4Address(GStack.NetworkToHOst(LIcmp.icmp_hdr.icmp_hun.gateway_s_l));
  486. case FReplyStatus.MsgCode of
  487. 0 : FReplyStatus.Msg := RSICMPRedirNet;
  488. 1 : FReplyStatus.Msg := RSICMPRedirHost;
  489. 2 : FReplyStatus.Msg := RSICMPRedirTOSNet;
  490. 3 : FReplyStatus.Msg := RSICMPRedirTOSHost;
  491. end;
  492. end;
  493. Id_ICMP_SOURCEQUENCH : FReplyStatus.Msg := RSICMPSourceQuenchMsg;
  494. Id_ICMP_ECHOREPLY, Id_ICMP_ECHO : FReplyStatus.Msg := RSICMPEcho;
  495. Id_ICMP_TSTAMP, Id_ICMP_TSTAMPREPLY : FReplyStatus.Msg := RSICMPTimeStamp;
  496. Id_ICMP_IREQ, Id_ICMP_IREQREPLY : FReplyStatus.Msg := RSICMPTimeStamp;
  497. Id_ICMP_MASKREQ, Id_ICMP_MASKREPLY : FReplyStatus.Msg := RSICMPMaskRequest;
  498. Id_ICMP_TRACEROUTE :
  499. begin
  500. case FReplyStatus.MsgCode of
  501. Id_ICMP_TRACEROUTE_PACKET_FORWARDED : FReplyStatus.Msg := RSICMPTracePacketForwarded;
  502. Id_ICMP_TRACEROUTE_NO_ROUTE : FReplyStatus.Msg := RSICMPTraceNoRoute;
  503. end;
  504. end;
  505. Id_ICMP_DATAGRAM_CONV:
  506. begin
  507. case FReplyStatus.MsgCode of
  508. Id_ICMP_CONV_UNSPEC : FReplyStatus.Msg := RSICMPTracePacketForwarded;
  509. Id_ICMP_CONV_DONTCONV_OPTION : FReplyStatus.Msg := RSICMPTraceNoRoute;
  510. Id_ICMP_CONV_UNKNOWN_MAN_OPTION : FReplyStatus.Msg := RSICMPConvUnknownMandOptPresent;
  511. Id_ICMP_CONV_UNKNWON_UNSEP_OPTION : FReplyStatus.Msg := RSICMPConvKnownUnsupportedOptionPresent;
  512. Id_ICMP_CONV_UNSEP_TRANSPORT : FReplyStatus.Msg := RSICMPConvUnsupportedTransportProtocol;
  513. Id_ICMP_CONV_OVERALL_LENGTH_EXCEEDED : FReplyStatus.Msg := RSICMPConvOverallLengthExceeded;
  514. Id_ICMP_CONV_IP_HEADER_LEN_EXCEEDED : FReplyStatus.Msg := RSICMPConvIPHeaderLengthExceeded;
  515. Id_ICMP_CONV_TRANS_PROT_255 : FReplyStatus.Msg := RSICMPConvTransportProtocol_255;
  516. Id_ICMP_CONV_PORT_OUT_OF_RANGE : FReplyStatus.Msg := RSICMPConvPortConversionOutOfRange;
  517. Id_ICMP_CONV_TRANS_HEADER_LEN_EXCEEDED : FReplyStatus.Msg := RSICMPConvTransportHeaderLengthExceeded;
  518. Id_ICMP_CONV_32BIT_ROLLOVER_AND_ACK : FReplyStatus.Msg := RSICMPConv32BitRolloverMissingAndACKSet;
  519. Id_ICMP_CONV_UNKNOWN_MAN_TRANS_OPTION : FReplyStatus.Msg := RSICMPConvUnknownMandatoryTransportOptionPresent;
  520. end;
  521. end;
  522. Id_ICMP_MOB_HOST_REDIR : FReplyStatus.Msg := RSICMPMobileHostRedirect;
  523. Id_ICMP_IPv6_WHERE_ARE_YOU : FReplyStatus.Msg := RSICMPIPv6WhereAreYou;
  524. Id_ICMP_IPv6_I_AM_HERE : FReplyStatus.Msg := RSICMPIPv6IAmHere;
  525. Id_ICMP_MOB_REG_REQ, Id_ICMP_MOB_REG_REPLY : FReplyStatus.Msg := RSICMPIPv6IAmHere;
  526. Id_ICMP_SKIP : FReplyStatus.Msg := RSICMPSKIP;
  527. Id_ICMP_PHOTURIS :
  528. begin
  529. case FReplyStatus.MsgCode of
  530. Id_ICMP_BAD_SPI : FReplyStatus.Msg := RSICMPSecBadSPI;
  531. Id_ICMP_AUTH_FAILED : FReplyStatus.Msg := RSICMPSecAuthenticationFailed;
  532. Id_ICMP_DECOMPRESS_FAILED : FReplyStatus.Msg := RSICMPSecDecompressionFailed;
  533. Id_ICMP_DECRYPTION_FAILED : FReplyStatus.Msg := RSICMPSecDecryptionFailed;
  534. Id_ICMP_NEED_AUTHENTICATION : FReplyStatus.Msg := RSICMPSecNeedAuthentication;
  535. Id_ICMP_NEED_AUTHORIZATION : FReplyStatus.Msg := RSICMPSecNeedAuthorization;
  536. end;
  537. end;
  538. end;
  539. end;
  540. finally
  541. FreeAndNil(LIcmp);
  542. end;
  543. end;
  544. procedure TIdCustomIcmpClient.PrepareEchoRequestIPv4(const ABuffer: String);
  545. var
  546. LIcmp: TIdICMPHdr;
  547. LIdx: UInt32;
  548. LBuffer: TIdBytes;
  549. LBufferLen: Integer;
  550. begin
  551. LBuffer := ToBytes(ABuffer, IndyTextEncoding_8Bit);
  552. LBufferLen := IndyMin(Length(LBuffer), FPacketSize);
  553. SetLength(FBufIcmp, ICMP_MIN + SizeOf(TIdTicks) + LBufferLen);
  554. FillBytes(FBufIcmp, Length(FBufIcmp), 0);
  555. SetLength(FBufReceive, Id_IP_HSIZE + Length(FBufIcmp));
  556. LIdx := 0;
  557. LIcmp := TIdICMPHdr.Create;
  558. try
  559. LIcmp.icmp_type := Id_ICMP_ECHO;
  560. LIcmp.icmp_code := 0;
  561. LIcmp.icmp_sum := 0;
  562. LIcmp.icmp_hun.echo_id := Word(CurrentProcessId);
  563. LIcmp.icmp_hun.echo_seq := wSeqNo;
  564. LIcmp.WriteStruct(FBufIcmp, LIdx);
  565. CopyTIdTicks(Ticks64, FBufIcmp, LIdx);
  566. Inc(LIdx, SizeOf(TIdTicks));
  567. if LBufferLen > 0 then begin
  568. CopyTIdBytes(LBuffer, 0, FBufIcmp, LIdx, LBufferLen);
  569. end;
  570. finally
  571. FreeAndNil(LIcmp);
  572. end;
  573. end;
  574. {$IFNDEF DOTNET_1_1}
  575. procedure TIdCustomIcmpClient.PrepareEchoRequestIPv6(const ABuffer: String);
  576. var
  577. LIcmp : TIdicmp6_hdr;
  578. LIdx : UInt32;
  579. LBuffer: TIdBytes;
  580. LBufferLen: Integer;
  581. begin
  582. LBuffer := ToBytes(ABuffer, IndyTextEncoding_8Bit);
  583. LBufferLen := IndyMin(Length(LBuffer), FPacketSize);
  584. SetLength(FBufIcmp, ICMP_MIN + SizeOf(TIdTicks) + LBufferLen);
  585. FillBytes(FBufIcmp, Length(FBufIcmp), 0);
  586. SetLength(FBufReceive, Length(FBufIcmp) + (Id_IPv6_HSIZE*2));
  587. LIdx := 0;
  588. LIcmp := TIdicmp6_hdr.Create;
  589. try
  590. LIcmp.icmp6_type := ICMP6_ECHO_REQUEST;
  591. LIcmp.icmp6_code := 0;
  592. LIcmp.data.icmp6_un_data16[0] := Word(CurrentProcessId);
  593. LIcmp.data.icmp6_un_data16[1] := wSeqNo;
  594. LIcmp.icmp6_cksum := 0;
  595. LIcmp.WriteStruct(FBufIcmp, LIdx);
  596. CopyTIdTicks(Ticks64, FBufIcmp, LIdx);
  597. Inc(LIdx, SizeOf(TIdTicks));
  598. if LBufferLen > 0 then begin
  599. CopyTIdBytes(LBuffer, 0, FBufIcmp, LIdx, LBufferLen);
  600. end;
  601. finally
  602. FreeAndNil(LIcmp);
  603. end;
  604. end;
  605. function TIdCustomIcmpClient.DecodeIPv6Packet(BytesRead: UInt32): Boolean;
  606. var
  607. LIdx : UInt32;
  608. LIcmp : TIdicmp6_hdr;
  609. RTTime : UInt32;
  610. LActualSeqID : Word;
  611. begin
  612. LIdx := 0;
  613. LIcmp := TIdicmp6_hdr.Create;
  614. try
  615. // Note that IPv6 raw headers are not being returned.
  616. LIcmp.ReadStruct(FBufReceive, LIdx);
  617. case LIcmp.icmp6_type of
  618. ICMP6_ECHO_REQUEST,
  619. ICMP6_ECHO_REPLY : FReplyStatus.ReplyStatusType := rsEcho;
  620. //group membership messages
  621. ICMP6_MEMBERSHIP_QUERY : ;
  622. ICMP6_MEMBERSHIP_REPORT : ;
  623. ICMP6_MEMBERSHIP_REDUCTION : ;
  624. //errors
  625. ICMP6_DST_UNREACH : FReplyStatus.ReplyStatusType := rsErrorUnreachable;
  626. ICMP6_PACKET_TOO_BIG : FReplyStatus.ReplyStatusType := rsErrorPacketTooBig;
  627. ICMP6_TIME_EXCEEDED : FReplyStatus.ReplyStatusType := rsErrorTTLExceeded;
  628. ICMP6_PARAM_PROB : FReplyStatus.ReplyStatusType := rsErrorParameter;
  629. else FReplyStatus.ReplyStatusType := rsError;
  630. end;
  631. FReplyStatus.MsgType := LIcmp.icmp6_type; //picmp^.icmp_type;
  632. FReplyStatus.MsgCode := LIcmp.icmp6_code;
  633. //errors are values less than ICMP6_INFOMSG_MASK
  634. if LIcmp.icmp6_type < ICMP6_INFOMSG_MASK then
  635. begin
  636. //read info from the original packet part
  637. LIcmp.ReadStruct(FBufReceive, LIdx);
  638. end;
  639. LActualSeqID := LIcmp.data.icmp6_seq;
  640. Result := LActualSeqID = wSeqNo;
  641. RTTime := GetElapsedTicks(BytesToTicks(FBufReceive, LIdx));
  642. Inc(LIdx, SizeOf(TIdTicks));
  643. if Result then
  644. begin
  645. FReplyStatus.BytesReceived := BytesRead - LIdx;
  646. FReplyStatus.SequenceId := LActualSeqID;
  647. FReplyStatus.MsRoundTripTime := RTTime;
  648. // TimeToLive := FBufReceive[8];
  649. // TimeToLive := pip^.ip_ttl;
  650. FReplyStatus.TimeToLive := FPkt.TTL;
  651. FReplyStatus.FromIpAddress := FPkt.SourceIP;
  652. FReplyStatus.ToIpAddress := FPkt.DestIP;
  653. case FReplyStatus.MsgType of
  654. ICMP6_ECHO_REQUEST, ICMP6_ECHO_REPLY : FReplyStatus.Msg := RSICMPEcho;
  655. ICMP6_TIME_EXCEEDED :
  656. begin
  657. case FReplyStatus.MsgCode of
  658. ICMP6_TIME_EXCEED_TRANSIT : FReplyStatus.Msg := RSICMPHopLimitExceeded;
  659. ICMP6_TIME_EXCEED_REASSEMBLY : FReplyStatus.Msg := RSICMPFragAsmExceeded;
  660. end;
  661. end;
  662. ICMP6_DST_UNREACH :
  663. begin
  664. case FReplyStatus.MsgCode of
  665. ICMP6_DST_UNREACH_NOROUTE : FReplyStatus.Msg := RSICMPNoRouteToDest;
  666. ICMP6_DST_UNREACH_ADMIN : FReplyStatus.Msg := RSICMPAdminProhibitted;
  667. ICMP6_DST_UNREACH_ADDR : FReplyStatus.Msg := RSICMPHostUnreachable;
  668. ICMP6_DST_UNREACH_NOPORT : FReplyStatus.Msg := RSICMPProtUnreachable;
  669. ICMP6_DST_UNREACH_SOURCE_FILTERING : FReplyStatus.Msg := RSICMPSourceFilterFailed;
  670. ICMP6_DST_UNREACH_REJCT_DST : FReplyStatus.Msg := RSICMPRejectRoutToDest;
  671. end;
  672. end;
  673. ICMP6_PACKET_TOO_BIG : FReplyStatus.Msg := IndyFormat(RSICMPPacketTooBig, [LIcmp.data.icmp6_mtu]);
  674. ICMP6_PARAM_PROB :
  675. begin
  676. case FReplyStatus.MsgCode of
  677. ICMP6_PARAMPROB_HEADER : FReplyStatus.Msg := IndyFormat(RSICMPParamHeader, [LIcmp.data.icmp6_pptr]);
  678. ICMP6_PARAMPROB_NEXTHEADER : FReplyStatus.Msg := IndyFormat(RSICMPParamNextHeader, [LIcmp.data.icmp6_pptr]);
  679. ICMP6_PARAMPROB_OPTION : FReplyStatus.Msg := IndyFormat(RSICMPUnrecognizedOpt, [LIcmp.data.icmp6_pptr]);
  680. end;
  681. end;
  682. ICMP6_MEMBERSHIP_QUERY : ;
  683. ICMP6_MEMBERSHIP_REPORT : ;
  684. ICMP6_MEMBERSHIP_REDUCTION :;
  685. end;
  686. end;
  687. finally
  688. FreeAndNil(LIcmp);
  689. end;
  690. end;
  691. {$ENDIF}
  692. procedure TIdCustomIcmpClient.Send(const AHost: string; const APort: TIdPort;
  693. const ABuffer: TIdBytes);
  694. var
  695. LBuffer : TIdBytes;
  696. LIP : String;
  697. begin
  698. LBuffer := ABuffer;
  699. LIP := GStack.ResolveHost(AHost, IPVersion);
  700. GStack.WriteChecksum(Binding.Handle, LBuffer, 2, LIP, APort, IPVersion);
  701. FBinding.SendTo(LIP, APort, LBuffer, IPVersion);
  702. end;
  703. procedure TIdCustomIcmpClient.Send(const ABuffer: TIdBytes);
  704. var
  705. LBuffer : TIdBytes;
  706. LIP : String;
  707. begin
  708. LBuffer := ABuffer;
  709. LIP := GStack.ResolveHost(Host, IPVersion);
  710. GStack.WriteChecksum(Binding.Handle, LBuffer, 2, LIP, Port, IPVersion);
  711. FBinding.SendTo(LIP, Port, LBuffer, IPVersion);
  712. end;
  713. function TIdCustomIcmpClient.GetPacketSize: Integer;
  714. begin
  715. Result := FPacketSize;
  716. end;
  717. procedure TIdCustomIcmpClient.SetPacketSize(const AValue: Integer);
  718. begin
  719. if AValue < 0 then begin
  720. FPacketSize := 0;
  721. end else begin
  722. FPacketSize := IndyMin(AValue, MAX_PACKET_SIZE);
  723. end;
  724. end;
  725. procedure TIdCustomIcmpClient.InternalPing(const AIP, ABuffer: String; SequenceID: Word);
  726. begin
  727. if SequenceID <> 0 then begin
  728. wSeqNo := SequenceID;
  729. end;
  730. PrepareEchoRequest(ABuffer);
  731. SendEchoRequest(AIP);
  732. GetEchoReply;
  733. Binding.CloseSocket;
  734. DoReply;
  735. Inc(wSeqNo); // SG 25/1/02: Only increase sequence number when finished.
  736. end;
  737. procedure TIdCustomIcmpClient.SendEchoRequest(const AIP: String);
  738. begin
  739. Send(AIP, 0, FBufIcmp);
  740. end;
  741. { TIdIcmpClient }
  742. procedure TIdIcmpClient.Ping(const ABuffer: String; SequenceID: Word);
  743. begin
  744. InternalPing(GStack.ResolveHost(Host, IPVersion), ABuffer, SequenceID);
  745. end;
  746. end.