NMICMP.pas
上传用户:szzdds
上传日期:2013-09-18
资源大小:293k
文件大小:18k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. unit NMICMP;
  2. interface
  3. uses
  4.   Windows, Messages, SysUtils, Classes, Forms, Winsock,NMConst;
  5. {$IFDEF VER110}
  6. {$OBJEXPORTALL On}
  7. {$ENDIF}
  8. {$IFDEF VER120}
  9. {$OBJEXPORTALL On}
  10. {$ENDIF}
  11. {$IFDEF VER125}
  12. {$OBJEXPORTALL On}
  13. {$ENDIF}
  14. const
  15.   WM_LOOKUPADDRESS = WM_USER + 101; // Message when looking up host
  16.   // ICMP Status Codes
  17.   BASE = 11000;
  18.   OPER_ABORT = -1;
  19.   ICMP_SUCCESS = 0;
  20.   BUFF_TOO_SMALL = BASE + 1;
  21.   DEST_NET_UNREACHABLE = BASE + 2;
  22.   DEST_HOST_UNREACHABLE = BASE + 3;
  23.   DEST_PROT_UNREACHABLE = BASE + 4;
  24.   DEST_PORT_UNREACHABLE = BASE + 5;
  25.   NO_RESOURCES = BASE + 6;
  26.   BAD_OPTIONS = BASE + 7;
  27.   HW_ERROR = BASE + 8;
  28.   PACKET_TOO_BIG = BASE + 9;
  29.   REQ_TIMED_OUT = BASE + 10;
  30.   BAD_REQUEST = BASE + 11;
  31.   BAD_ROUTE = BASE + 12;
  32.   TTL_EXP_TRANSIT = BASE + 13;
  33.   TTL_EXP_REASSMBLE = BASE + 14;
  34.   PARAM_PROBLEM = BASE + 15;
  35.   SOURCE_QUENCH = BASE + 16;
  36.   OPTIONS_TOO_BIG = BASE + 17;
  37.   BAD_DEST = BASE + 18;
  38.   ADDR_DELETED = BASE + 19;
  39.   SPEC_MTU_CHANGE = BASE + 20;
  40.   MTU_CHANGE = BASE + 21;
  41.   UNLOAD = BASE + 22;
  42.   GENERAL_FAILURE = BASE + 50;
  43.   IP_STATUS = GENERAL_FAILURE;
  44.   PENDING = BASE + 255;
  45.   // String constants
  46.   con_abort = 'Operation aborted';
  47.   con_lookup_fail = 'Host lookup failed';
  48.   con_cantload = 'Unable to load ICMP.DLL';
  49.   con_winserror = 'Error starting Winsock';
  50.   con_icmperr = 'Error initializing ICMP Handle';
  51.   con_datachar = '#';
  52.   con_icmpdll = 'ICMP.DLL';
  53.   con_icmpcreatefile = 'IcmpCreateFile';
  54.   con_icmpclosehandle = 'IcmpCloseHandle';
  55.   con_icmpsendecho = 'IcmpSendEcho';
  56.   con_localabort = 'Local Abort';
  57.   con_badimports = 'Failure to import one or more routines from ICMP.DLL';
  58.   con_hosttimedout = 'Host lookup timed out';
  59. type
  60.   THandle = Integer;
  61.   // Record type for ICMP options
  62.   PIPOptionInfo = ^TIPOptionInfo;
  63.   TIPOptionInfo = packed record
  64.     TTL: Byte; // time to live (for TraceRt)
  65.     TOS: Byte; // Type of Service
  66.     Flags: Byte; // IP Header Flags
  67.     OptionSize: Byte; // Size of OptionData
  68.     OptionData: Pointer; // pointer to option data
  69.   end;
  70.   // Record type for ICMP replies
  71.   PIPEchoReply = ^TIPEchoReply;
  72.   TIPEchoReply = packed record
  73.     Address: u_long; // replying address
  74.     Status: u_long; // Reply Status
  75.     RTT: u_long; //Round tip time in milliseconds
  76.     DataSize: word; // Size of data
  77.     Reserved: word; // Reserved for sys use
  78.     Data: Pointer; // Pointer to echoed data
  79.     IPOptions: TIPOptionInfo; // Reply options
  80.   end;
  81.   //-------------Types for routines from ICMP.DLL
  82.   TICMPCreateFile = function: THandle; stdcall;
  83.   TICMPCloseHandle = function(ICMPHandle: THandle): Boolean; stdcall;
  84.   TICMPSendEcho = function(ICMPHandle: THandle; // Handle gotten from ICMPCreateFile
  85.     DestAddress: longint; // Target IP (in NBO)
  86.     RequestData: Pointer; // Pointer to request data to send
  87.     RequestSize: word; // Length of RequestData
  88.     RequestOptions: PIPOptionInfo;
  89.     ReplyBuffer: Pointer;
  90.     ReplySize: dword; // Length of Reply
  91.     Timeout: dword // Time in milliseconds before TimeOut
  92.     ): dword; stdcall;
  93.   //-------------Event types-------------//
  94.   // When a ping comes back
  95.   TPingEvent = procedure(Sender: TObject; Host: string; Size, Time: Integer) of object;
  96.   // When a TraceRt packet "hops"
  97.   THopEvent = procedure(Sender: TObject; Host: string; Time1, Time2, Time3: Integer; HopNo: Integer) of object;
  98.   // Generic event when a host name might need to be known
  99.   THostEvent = procedure(Sender: TObject; Host: string) of object;
  100.   // Status Event
  101.   TStatusEvent = procedure(Sender: TObject; Status: Integer; Host: string) of object;
  102.   EICMPError = class(Exception);
  103.   // Exception for ICMP Errors
  104.   TNMICMP = class(TComponent)
  105.   // NMICMP Class, base for NMPing and NMTraceRt
  106.   private
  107.     { Private declarations }
  108.     DLLHandle: THandle; // Handle for ICMP.DLL
  109.     ICMPHandle: THandle; // Handle for ICMP Functions
  110.     WinHandle: HWND; // Window handle
  111.     MyWSAData: TWSAData; // Winsock Data
  112.     FHost: string; // Target host
  113.     FTimeOut: Integer; // Timeout in milliseconds
  114.     FPacketSize: Integer; // Size of data packets
  115.     FAborted: Boolean; // If the current process has been aborted or not
  116. //    FResolveIP: Boolean; // Resolve IPs to addresses
  117.     FOnAbort: TNotifyEvent; // Called when the Abort method is used
  118.     FOnInvalidHost: TNotifyEvent; // Called when the specified host is invalid
  119.     FOnTimeOut: TNotifyEvent; // Called when an ICMP packet times out
  120.     FHostUnreachable: THostEvent; // Destination host is unreachable
  121.     FOnStatus: TStatusEvent; // For ICMP status messages
  122.   protected
  123.     { Protected declarations }
  124.     // Functions from ICMP.DLL
  125.     ICMPCreateFile: TICMPCreateFile;
  126.     ICMPCloseHandle: TICMPCloseHandle;
  127.     ICMPSendEcho: TICMPSendEcho;
  128.     IPOptions: PIPOptionInfo; // Options for echo
  129.     NetworkAddress: longint; // Network address of target host
  130.     HostInfo: PHostEnt; // Winsock struct contains info on remote host
  131.     AddressInfo: TSockAddr; // Contains address info for remote host
  132.     Success: Boolean; // Simple Success flag
  133.     HostLookup: Boolean; // Set when the remote host lookup returns
  134.     procedure WndProc(var Msg: TMessage); virtual; // Handles messages
  135.     procedure ResolveAddresses; // Resolves network address/IP Address
  136.     function GetHostName(InetAddr: longint): string;
  137.     // Events
  138.     property OnTimeOut: TNotifyEvent read FOnTimeOut write FOnTimeOut;
  139.   public
  140.     { Public declarations }
  141. //    HostName: String;
  142.     constructor Create(AOwner: TComponent); override;
  143.     destructor Destroy; override;
  144.     procedure Abort; // Aborts current operation
  145.   published
  146.     { Published declarations }
  147.     property Host: string read FHost write FHost;
  148.     property PacketSize: Integer read FPacketSize write FPacketSize;
  149.     property Timeout: Integer read FTimeOut write FTimeOut;
  150. //    property ResolveIPs: Boolean read FResolveIP write FResolveIP;
  151.     //Events
  152.     property OnAbort: TNotifyEvent read FOnAbort write FOnAbort;
  153.     property OnInvalidHost: TNotifyEvent read FOnInvalidHost write FOnInvalidHost;
  154.     property OnHostUnreachable: THostEvent read FHostUnreachable write FHostUnreachable;
  155.     property OnStatus: TStatusEvent read FOnStatus write FOnStatus;
  156.   end;
  157.   TNMPing = class(TNMICMP)
  158.   // NMPing, for pinging remote hosts
  159.   private
  160.     FOnPing: TPingEvent;
  161.     FPings: Integer;
  162.   protected
  163.   public
  164.     constructor Create(AOwner: TComponent); override;
  165.     procedure Ping;
  166.   published
  167.     property Pings: Integer read FPings write FPings;
  168.     property OnPing: TPingEvent read FOnPing write FOnPing;
  169.     property OnTimeOut; // From TNMICMP
  170.   end;
  171.   TNMTraceRt = class(TNMICMP)
  172.   // NMTraceRt, for tracing the route to remote hosts
  173.   private
  174.     FHops: Integer; // Maximum number of hops (hosts to pass)
  175.     FTraceComplete: TNotifyEvent;
  176.     FOnHop: THopEvent; // Hop event
  177.   protected
  178.     TraceDone: Boolean; // Is the trace done?
  179.   public
  180.     constructor Create(AOwner: TComponent); override;
  181.     procedure Trace;
  182.   published
  183.     // properties
  184.     property MaxHops: Integer read FHops write FHops;
  185.     // Events
  186.     property OnHop: THopEvent read FOnHop write FOnHop;
  187.     property OnTraceComplete: TNotifyEvent read FTraceComplete write FTraceComplete;
  188.   end;
  189. implementation
  190. //--------------------------------------------------------------------------//
  191. //------------TNMICMP (base class for TNMPing and TNMTraceRt----------------//
  192. //--------------------------------------------------------------------------//
  193. procedure TNMICMP.WndProc(var Msg: TMessage);
  194. begin
  195.   Success := false;
  196.   if Msg.Msg = WM_LOOKUPADDRESS then
  197.   begin
  198.     if Msg.lparamhi = 0 then
  199.       Success := true
  200.     else
  201.       Success := false;
  202.     HostLookup := true;
  203.   end;
  204. end;
  205. function TNMICMP.GetHostName(InetAddr: longint): string;
  206. var
  207.   HostRes: PHostEnt;
  208. begin
  209.   // Returns Host name from a network address
  210.   GetMem(HostRes, MAXGETHOSTSTRUCT);
  211.   try
  212.     WSAAsyncGetHostByAddr(WinHandle, WM_LOOKUPADDRESS, PChar(InetAddr), 4, PF_INET, PChar(HostRes), MAXGETHOSTSTRUCT);
  213.     repeat
  214.       Application.ProcessMessages;
  215.     until HostLookup or FAborted;
  216.     if FAborted then
  217.       raise EICMPError.Create(con_abort);
  218. (******* Need to check this out, to see if the host resolution is working right. ****)
  219.     Result := StrPas(HostRes^.h_name);
  220.   finally
  221.     FreeMem(HostRes, MAXGETHOSTSTRUCT);
  222.   end;
  223. end;
  224. //---This procedure needs to set the Network Address for the target host.
  225. procedure TNMICMP.ResolveAddresses;
  226. var
  227.   Buff: array[0..127] of Char;
  228. begin
  229.   // See if an IP Address was set as the host
  230.   AddressInfo.sin_addr.s_addr := Inet_Addr(StrPCopy(Buff, FHost));
  231.   if AddressInfo.sin_addr.s_addr = SOCKET_ERROR then
  232.   begin // If not, resolve it a different way
  233.     AddressInfo.sin_addr.s_addr := 0;
  234.     HostLookup := false;
  235.     WSAAsyncGetHostByName(WinHandle, WM_LOOKUPADDRESS, Buff, PChar(HostInfo), MAXGETHOSTSTRUCT);
  236.     repeat
  237.       Application.ProcessMessages;
  238.     until HostLookup or FAborted;
  239.     // If the host lookup was aborted
  240.     if FAborted then
  241.       raise EICMPError.Create(con_abort);
  242.     // if the host lookup failed
  243.     if (not HostLookup) or (not Success) then
  244.     begin
  245.       if Assigned(FOnInvalidHost) then
  246.         FOnInvalidHost(Self);
  247.       raise EICMPError.Create(con_lookup_fail);
  248.     end
  249.     else
  250.     begin
  251.       // Look up host name if resolve IP is true
  252.       with AddressInfo.sin_addr.S_un_b do
  253.       begin
  254.         s_b1 := HostInfo.h_addr_list^[0];
  255.         s_b2 := HostInfo.h_addr_list^[1];
  256.         s_b3 := HostInfo.h_addr_list^[2];
  257.         s_b4 := HostInfo.h_addr_list^[3];
  258.       end;
  259.     end;
  260.   end;
  261.   NetworkAddress := AddressInfo.sin_addr.s_addr;
  262. //  If FResolveIP then
  263. //    HostName := GetHostName(NetworkAddress);
  264. end;
  265. constructor TNMICMP.Create(AOwner: TComponent);
  266. begin
  267.  // Basic TComponent create
  268.   inherited Create(AOwner);
  269.   // Allocate space for remote host info
  270.   GetMem(HostInfo, MAXGETHOSTSTRUCT);
  271.   ICMPHandle := -1; // Nullify the ICMP Handle
  272.   // Constant expression violates subrange bounds
  273.   FTimeOut := 5000; // default timeout to 5 seconds
  274.   FPacketSize := 32; // Default packetsize to 32 bytes
  275.   DLLHandle := -1; // Nullify DLL handle
  276.   // Constant expression violates subrange bounds
  277.   FAborted := false; // Operation not aborted
  278.   @ICMPCreateFile := nil;
  279.   @ICMPCloseHandle := nil;
  280.   @ICMPSendEcho := nil;
  281.   // Allocate window handle and message handling procedure
  282.   // For winsock calls (just looking up host names)
  283.   WinHandle := AllocateHwnd(Self.WndProc);
  284.   // Dynamically load ICMP.DLL
  285.   DLLHandle := LoadLibrary(PChar(con_icmpdll));
  286.   // Setting up ICMP Functions from ICMP.DLL
  287.   if DLLHandle <> -1 then
  288.   begin
  289.     @ICMPCreateFile := GetProcAddress(DLLHandle, con_icmpcreatefile);
  290.     @ICMPCloseHandle := GetProcAddress(DLLHandle, con_icmpclosehandle);
  291.     @ICMPSendEcho := GetProcAddress(DLLHandle, con_icmpsendecho);
  292.   end
  293.   else
  294.     raise EICMPError.Create(con_cantload);
  295.   if (@ICMPCreateFile = nil) or
  296.     (@ICMPCloseHandle = nil) or
  297.     (@ICMPSendEcho = nil) then
  298.     raise EICMPError.Create(con_badimports);
  299.   // Init winsock for getting host names and stuff
  300.   if WSAStartUp($0101, MyWSAData) <> 0 then
  301.     raise EICMPError.Create(con_winserror);
  302.   // Init memory for IPOptions
  303.   GetMem(IPOptions, SizeOf(TIPOptionInfo));
  304.   // Allocate ICMP Handle
  305.   ICMPHandle := ICMPCreateFile;
  306. end;
  307. destructor TNMICMP.Destroy;
  308. begin
  309.   // Free window handle
  310.   DeAllocateHWnd(WinHandle);
  311.   // Free the ICMP handle
  312.   if ICMPHandle <> -1 then
  313.     ICMPCloseHandle(ICMPHandle);
  314.   // Free the DLL library
  315.   if DLLHandle <> -1 then
  316.     FreeLibrary(DLLHandle);
  317.   // cleanup winsock
  318.   WSACleanup;
  319.   // Free memory for IPOptions
  320.   if IPOptions <> nil then
  321.     FreeMem(IPOptions, SizeOf(TIPOptionInfo));
  322.   // Free memory allocated for HostInfo structure
  323.   if HostInfo <> nil then
  324.     FreeMem(HostInfo, MAXGETHOSTSTRUCT);
  325.   // basic TComponent destroy
  326.   inherited Destroy;
  327. end;
  328. procedure TNMICMP.Abort;
  329. begin
  330.   // Set the abort switch to True
  331.   FAborted := true;
  332.   // Call the abort event if it's been set
  333.   if Assigned(FOnStatus) then
  334.     FOnStatus(Self, OPER_ABORT, con_localabort);
  335.   if Assigned(FOnAbort) then
  336.     FOnAbort(Self);
  337. end;
  338. //--------------------------------------------------------------------------//
  339. //----------------------------TNMPing---------------------------------------//
  340. //--------------------------------------------------------------------------//
  341. constructor TNMPing.Create(AOwner: TComponent);
  342. begin
  343.   inherited Create(AOwner);
  344.   FPings := 4;
  345. end;
  346. procedure TNMPing.Ping;
  347. var
  348.   Tms, ReplySize: Integer;
  349.   ReqData: Pointer;
  350.   EchoReply: PIPEchoReply;
  351.   ReplyAddress: TInAddr;
  352. begin
  353.   FAborted := false;
  354.   ResolveAddresses;
  355.   if ICMPHandle = -1 then
  356.     raise EICMPError.Create(con_icmperr);
  357.   GetMem(ReqData, FPacketSize);
  358.   ReplySize := SizeOf(TIPEchoReply) + FPacketSize + 16;
  359.   GetMem(EchoReply, ReplySize);
  360.   try
  361.     with IPOptions^ do
  362.     begin
  363.       TTL := 255; // TTL 255 for a ping
  364.       TOS := 0; // Type of Service
  365.       Flags := 0;
  366.       OptionSize := 0;
  367.       OptionData := nil;
  368.     end;
  369.     FillChar(ReqData^, FPacketSize, con_datachar);
  370.     for Tms := 1 to FPings do
  371.     begin
  372.       // Pinging
  373.       // If the operation has been aborted, exit the loop
  374.       Application.ProcessMessages;
  375.       if FAborted then
  376.       begin
  377.         FAborted := false;
  378.         Exit;
  379.       end;
  380.       ICMPSendEcho(ICMPHandle, NetworkAddress, ReqData, FPacketSize, IPOptions, EchoReply, ReplySize, FTimeOut);
  381.       ReplyAddress.s_addr := EchoReply^.Address;
  382.       case EchoReply^.Status of
  383.         ICMP_SUCCESS:
  384.           if Assigned(FOnPing) then
  385. //            If (not FResolveIP) then
  386.             FOnPing(Self, StrPas(inet_ntoa(ReplyAddress)), EchoReply^.DataSize, EchoReply^.RTT);
  387. //            else
  388. //              FOnPing(Self, HostName, EchoReply^.DataSize, EchoReply^.RTT);
  389.         DEST_NET_UNREACHABLE, DEST_HOST_UNREACHABLE:
  390.           if Assigned(FHostUnreachable) then
  391. //            If (not FResolveIP) then
  392.             FHostUnreachable(Self, StrPas(inet_ntoa(ReplyAddress)));
  393. //            else
  394. //              FHostUnreachable(Self, HostName);
  395.         REQ_TIMED_OUT:
  396.           if Assigned(FOnTimeOut) then
  397.             FOnTimeOut(Self);
  398.       end;
  399.       if Assigned(FOnStatus) then
  400. //        If (not FResolveIP) then
  401.         FOnStatus(Self, EchoReply^.Status, StrPas(inet_ntoa(ReplyAddress)));
  402. //        else
  403. //          FOnStatus(Self, EchoReply^.Status, HostName);
  404.     end;
  405.   finally
  406.     if ReqData <> nil then
  407.       FreeMem(ReqData, FPacketSize);
  408.     if EchoReply <> nil then
  409.       FreeMem(EchoReply, ReplySize);
  410.   end;
  411. end;
  412. //--------------------------------------------------------------------------//
  413. //---------------------------TNMTraceRt-------------------------------------//
  414. //--------------------------------------------------------------------------//
  415. constructor TNMTraceRt.Create(AOwner: TComponent);
  416. begin
  417.   inherited Create(AOwner);
  418.   FHops := 30;
  419. end;
  420. procedure TNMTraceRt.Trace;
  421. var
  422.   Tmp, Tms, ReplySize: Integer;
  423.   ReqData: Pointer;
  424.   EchoReply: PIPEchoReply;
  425.   ReplyAddress: TInAddr;
  426.   ReplyTime: array[1..3] of Integer;
  427. begin
  428.   FAborted := false;
  429.   TraceDone := false;
  430.   ResolveAddresses;
  431.   if ICMPHandle = -1 then
  432.     raise EICMPError.Create(con_icmperr);
  433.   GetMem(ReqData, FPacketSize);
  434.   FillChar(ReqData^, FPacketSize, con_datachar);
  435.   ReplySize := SizeOf(TIPEchoReply) + FPacketSize + 16;
  436.   GetMem(EchoReply, ReplySize);
  437.   try
  438.     Tms := 0;
  439.     while (Tms <= FHops) and (not FAborted) and (not TraceDone) do
  440.     begin
  441.       Inc(Tms);
  442.       for Tmp := 1 to 3 do
  443.       begin
  444.         with IPOptions^ do
  445.         begin
  446.           TTL := Tms; // TTL 255 for a ping
  447.           TOS := 0; // Type of Service
  448.           Flags := 0;
  449.           OptionSize := 0;
  450.           OptionData := nil;
  451.         end;
  452.         // Send the actual data packet
  453.         ICMPSendEcho(ICMPHandle, NetworkAddress, ReqData, FPacketSize, IPOptions, EchoReply, ReplySize, FTimeOut);
  454.         Application.ProcessMessages;
  455.         if FAborted then
  456.           Break;
  457.         // Put replying address into a TInAddr struct for resolution
  458.         ReplyAddress.s_addr := EchoReply^.Address;
  459.         case EchoReply^.Status of
  460.           // Successful hop
  461.           ICMP_SUCCESS, TTL_EXP_TRANSIT:
  462.             ReplyTime[Tmp] := EchoReply^.RTT;
  463.           // If the packet timed out, set a -1 reply time
  464.           REQ_TIMED_OUT:
  465.             begin
  466.               ReplyTime[Tmp] := -1;
  467.               if Assigned(FOnTimeOut) then
  468.                 FOnTimeOut(Self);
  469.             end;
  470.         end;
  471.       //End;
  472.       end;
  473.       if FAborted then
  474.       begin
  475.         Break;
  476.       end;
  477.       case EchoReply^.Status of
  478.         // If it's a successful hop, fire off the event
  479.         ICMP_SUCCESS, TTL_EXP_TRANSIT, REQ_TIMED_OUT:
  480.           if Assigned(FOnHop) then
  481. //            If (not FResolveIP) then
  482.             FOnHop(Self, StrPas(inet_ntoa(ReplyAddress)), ReplyTime[1], ReplyTime[2], ReplyTime[3], Tms);
  483. //            else
  484. //              FOnHop(Self, HostName, ReplyTime[1], ReplyTime[2], ReplyTime[3], Tms);
  485.         DEST_HOST_UNREACHABLE, DEST_NET_UNREACHABLE:
  486.           begin
  487.             if Assigned(FHostUnreachable) then
  488. //            If (not FResolveIP) then
  489.               FHostUnreachable(Self, StrPas(inet_ntoa(ReplyAddress)));
  490. //            else
  491. //              FHostUnreachable(Self, HostName);
  492.             TraceDone := true;
  493.           end;
  494.       end;
  495.       // Fire off status event
  496.       if Assigned(FOnStatus) then
  497. //        If (not FResolveIP) then
  498.         FOnStatus(Self, EchoReply^.Status, StrPas(inet_ntoa(ReplyAddress)));
  499. //        else
  500. //          FOnStatus(Self, EchoReply^.Status, HostName);
  501.       // If the address reached this time is the target, and the echo was successful, the trace is over
  502.       if (EchoReply^.Address = NetworkAddress) and
  503.         (EchoReply^.Status = ICMP_SUCCESS) then
  504.       begin
  505.         if Assigned(FTraceComplete) then
  506.           FTraceComplete(Self);
  507.         TraceDone := true;
  508.       end;
  509.     end;
  510.   finally
  511.     if ReqData <> nil then
  512.       FreeMem(ReqData, FPacketSize);
  513.     if EchoReply <> nil then
  514.       FreeMem(EchoReply, ReplySize);
  515.   end;
  516. end;
  517. end.