IdIcmpClient.pas 29 KB

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