IdIcmpClient.pas 30 KB

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