IdIcmpClient.pas 30 KB

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