unit icmp;
interface
{$IFDEF VER80}
{$ENDIF}
uses
Windows, SysUtils, Classes, WinSock;
const
IcmpVersion = 102;
IcmpDLL = 'icmp.dll';
IP_SUCCESS = 0;
IP_STATUS_BASE = 11000;
IP_BUF_TOO_SMALL = (IP_STATUS_BASE + 1);
IP_DEST_NET_UNREACHABLE = (IP_STATUS_BASE + 2);
IP_DEST_HOST_UNREACHABLE = (IP_STATUS_BASE + 3);
IP_DEST_PROT_UNREACHABLE = (IP_STATUS_BASE + 4);
IP_DEST_PORT_UNREACHABLE = (IP_STATUS_BASE + 5);
IP_NO_RESOURCES = (IP_STATUS_BASE + 6);
IP_BAD_OPTION = (IP_STATUS_BASE + 7);
IP_HW_ERROR = (IP_STATUS_BASE + 8);
IP_PACKET_TOO_BIG = (IP_STATUS_BASE + 9);
IP_REQ_TIMED_OUT = (IP_STATUS_BASE + 10);
IP_BAD_REQ = (IP_STATUS_BASE + 11);
IP_BAD_ROUTE = (IP_STATUS_BASE + 12);
IP_TTL_EXPIRED_TRANSIT = (IP_STATUS_BASE + 13);
IP_TTL_EXPIRED_REASSEM = (IP_STATUS_BASE + 14);
IP_PARAM_PROBLEM = (IP_STATUS_BASE + 15);
IP_SOURCE_QUENCH = (IP_STATUS_BASE + 16);
IP_OPTION_TOO_BIG = (IP_STATUS_BASE + 17);
IP_BAD_DESTINATION = (IP_STATUS_BASE + 18);
IP_ADDR_DELETED = (IP_STATUS_BASE + 19);
IP_SPEC_MTU_CHANGE = (IP_STATUS_BASE + 20);
IP_MTU_CHANGE = (IP_STATUS_BASE + 21);
IP_GENERAL_FAILURE = (IP_STATUS_BASE + 50);
MAX_IP_STATUS = IP_GENERAL_FAILURE;
IP_PENDING = (IP_STATUS_BASE + 255);
IP_FLAG_DF = $02;
IP_OPT_EOL = $00;
IP_OPT_NOP = $01;
IP_OPT_SECURITY = $82;
IP_OPT_LSRR = $83;
IP_OPT_SSRR = $89;
IP_OPT_RR = $07;
IP_OPT_TS = $44;
IP_OPT_SID = $88;
MAX_OPT_SIZE = $40;
type
TIPAddr = DWORD;
TIPMask = DWORD;
TIPStatus = DWORD;
PIPOptionInformation = ^TIPOptionInformation;
TIPOptionInformation = packed record
TTL: Byte;
TOS: Byte;
Flags: Byte;
OptionsSize: Byte;
OptionsData: PChar;
end;
PIcmpEchoReply = ^TIcmpEchoReply;
TIcmpEchoReply = packed record
Address: TIPAddr;
Status: DWORD;
RTT: DWORD;
DataSize: Word;
Reserved: Word;
Data: Pointer;
Options: TIPOptionInformation;
end;
TIcmpCreateFile = function: THandle; stdcall;
TIcmpCloseHandle = function(IcmpHandle: THandle): Boolean; stdcall;
TIcmpSendEcho = function(IcmpHandle: THandle; DestinationAddress: TIPAddr;
RequestData: Pointer; RequestSize: Word;
RequestOptions: PIPOptionInformation; ReplyBuffer: Pointer;
ReplySize: DWORD; Timeout: DWORD): DWORD; stdcall;
TICMPDisplay = procedure(Sender: TObject; Msg: String) of object;
TICMPReply = procedure(Sender: TObject; Error: Integer) of object;
TICMP = class(TObject)
private
hICMPdll: HModule;
IcmpCreateFile: TIcmpCreateFile;
IcmpCloseHandle: TIcmpCloseHandle;
IcmpSendEcho: TIcmpSendEcho;
hICMP: THandle;
FReply: TIcmpEchoReply;
FAddress: String;
FHostName: String;
FHostIP: String;
FIPAddress: TIPAddr;
FSize: Integer;
FTimeOut: Integer;
FTTL: Integer;
FOnDisplay: TICMPDisplay;
FOnEchoRequest: TNotifyEvent;
FOnEchoReply: TICMPReply;
FLastError: DWORD;
FAddrResolved: Boolean;
procedure ResolveAddr;
public
constructor Create; virtual;
destructor Destroy; override;
function Ping: Integer;
procedure SetAddress(Value: String);
function GetErrorString: String;
property Address: String read FAddress write SetAddress;
property Size: Integer read FSize write FSize;
property Timeout: Integer read FTimeOut write FTimeOut;
property Reply: TIcmpEchoReply read FReply;
property TTL: Integer read FTTL write FTTL;
property ErrorCode: Integer read FLastError;
property ErrorString: String read GetErrorString;
property HostName: String read FHostName;
property HostIP: String read FHostIP;
property OnDisplay: TICMPDisplay read FOnDisplay write FOnDisplay;
property OnEchoRequest: TNotifyEvent read FOnEchoRequest
write FOnEchoRequest;
property OnEchoReply: TICMPReply read FOnEchoReply write FOnEchoReply;
end;
TICMPException = class(Exception);
implementation
constructor TICMP.Create;
var
WSAData: TWSAData;
begin
hICMP := INVALID_HANDLE_VALUE;
FSize := 56;
FTTL := 64;
FTimeOut := 4000;
if WSAStartup($101, WSAData) <> 0 then
raise TICMPException.Create('Error initialising Winsock');
hICMPdll := LoadLibrary(IcmpDLL);
if hICMPdll = 0 then
raise TICMPException.Create('Unable to register ' + IcmpDLL);
@IcmpCreateFile := GetProcAddress(hICMPdll, 'IcmpCreateFile');
@IcmpCloseHandle := GetProcAddress(hICMPdll, 'IcmpCloseHandle');
@IcmpSendEcho := GetProcAddress(hICMPdll, 'IcmpSendEcho');
if (@IcmpCreateFile = Nil) or (@IcmpCloseHandle = Nil) or (@IcmpSendEcho = Nil)
then
raise TICMPException.Create('Error loading dll functions');
hICMP := IcmpCreateFile;
if hICMP = INVALID_HANDLE_VALUE then
raise TICMPException.Create('Unable to get ping handle');
end;
destructor TICMP.Destroy;
begin
if hICMP <> INVALID_HANDLE_VALUE then
IcmpCloseHandle(hICMP);
if hICMPdll <> 0 then
FreeLibrary(hICMPdll);
WSACleanup;
inherited Destroy;
end;
function MinInteger(X, Y: Integer): Integer;
begin
if X >= Y then
Result := Y
else
Result := X;
end;
procedure TICMP.ResolveAddr;
var
Phe: PHostEnt;
begin
FIPAddress := inet_addr(PChar(FAddress));
if FIPAddress <> INADDR_NONE then
FHostName := FAddress
else
begin
Phe := GetHostByName(PChar(FAddress));
if Phe = nil then
begin
FLastError := GetLastError;
if Assigned(FOnDisplay) then
FOnDisplay(Self, 'Unable to resolve ' + FAddress);
Exit;
end;
FIPAddress := longint(plongint(Phe^.h_addr_list^)^);
FHostName := Phe^.h_name;
end;
FHostIP := StrPas(inet_ntoa(TInAddr(FIPAddress)));
FAddrResolved := TRUE;
end;
procedure TICMP.SetAddress(Value: String);
begin
if FAddress = Value then
Exit;
FAddress := Value;
FAddrResolved := FALSE;
end;
function TICMP.GetErrorString: String;
begin
case FLastError of
IP_SUCCESS:
Result := 'No error';
IP_BUF_TOO_SMALL:
Result := 'Buffer too small';
IP_DEST_NET_UNREACHABLE:
Result := 'Destination network unreachable';
IP_DEST_HOST_UNREACHABLE:
Result := 'Destination host unreachable';
IP_DEST_PROT_UNREACHABLE:
Result := 'Destination protocol unreachable';
IP_DEST_PORT_UNREACHABLE:
Result := 'Destination port unreachable';
IP_NO_RESOURCES:
Result := 'No resources';
IP_BAD_OPTION:
Result := 'Bad option';
IP_HW_ERROR:
Result := 'Hardware error';
IP_PACKET_TOO_BIG:
Result := 'Packet too big';
IP_REQ_TIMED_OUT:
Result := 'Request timed out';
IP_BAD_REQ:
Result := 'Bad request';
IP_BAD_ROUTE:
Result := 'Bad route';
IP_TTL_EXPIRED_TRANSIT:
Result := 'TTL expired in transit';
IP_TTL_EXPIRED_REASSEM:
Result := 'TTL expired in reassembly';
IP_PARAM_PROBLEM:
Result := 'Parameter problem';
IP_SOURCE_QUENCH:
Result := 'Source quench';
IP_OPTION_TOO_BIG:
Result := 'Option too big';
IP_BAD_DESTINATION:
Result := 'Bad Destination';
IP_ADDR_DELETED:
Result := 'Address deleted';
IP_SPEC_MTU_CHANGE:
Result := 'Spec MTU change';
IP_MTU_CHANGE:
Result := 'MTU change';
IP_GENERAL_FAILURE:
Result := 'General failure';
IP_PENDING:
Result := 'Pending';
else
Result := 'ICMP error #' + IntToStr(FLastError);
end;
end;
function TICMP.Ping: Integer;
var
BufferSize: Integer;
pReqData, pData: Pointer;
pIPE: PIcmpEchoReply;
IPOpt: TIPOptionInformation;
Msg: String;
begin
Result := 0;
FLastError := 0;
if not FAddrResolved then
ResolveAddr;
if FIPAddress = INADDR_NONE then
begin
FLastError := IP_BAD_DESTINATION;
if Assigned(FOnDisplay) then
FOnDisplay(Self, 'Invalid host address');
Exit;
end;
BufferSize := SizeOf(TIcmpEchoReply) + FSize;
GetMem(pReqData, FSize);
GetMem(pData, FSize);
GetMem(pIPE, BufferSize);
try
FillChar(pReqData^, FSize, $20);
Msg := 'Pinging from Delphi code written by F. Piette';
Move(Msg[1], pReqData^, MinInteger(FSize, Length(Msg)));
pIPE^.Data := pData;
FillChar(pIPE^, SizeOf(pIPE^), 0);
if Assigned(FOnEchoRequest) then
FOnEchoRequest(Self);
FillChar(IPOpt, SizeOf(IPOpt), 0);
IPOpt.TTL := FTTL;
Result := IcmpSendEcho(hICMP, FIPAddress, pReqData, FSize, @IPOpt, pIPE,
BufferSize, FTimeOut);
FLastError := GetLastError;
FReply := pIPE^;
if Assigned(FOnEchoReply) then
FOnEchoReply(Self, Result);
finally
FreeMem(pIPE);
FreeMem(pData);
FreeMem(pReqData);
end;
end;
end.