const PacketSize=32;
type PICMP = ^TICMP; //Aufbau eines ICMP-Paketes
TICMP = packed record
Typ : Byte;
Code : Byte;
CheckSum : Word;
ID : Word;
Seq_Num : Word;
Data : array[1..PacketSize] of Byte;
end;
function Ping(const aIPAddr:String; //IP-Adresse
aSeqNum:Word) //fortlaufende Sequencenummer
:String; //Antwort hier als String
var Addr:TSockAddr; //IP-Adress-Record
Sock:TSocket; //Socket-Handle
ICMP:TICMP; //gesendetes ICMP-Paket
ICMPret:PICMP; //bekommenes ICMP-Paket
i:Integer;
Start,Ende:Integer; //Zeit merken
Read:TFDSet; //Record für Wartenfunktion des Sockets
TimeOut:TTimeVal;
buf:array[0..1023] of Byte; //Antwortbuffer
IPHeaderLen,IPLen:Integer;
Checksum:Word; //alles zur Checksummenberechnung
CheckSumTemp:Integer;
pw:PWord;
begin
//Socket Initialisieren
Sock := Socket(AF_INET,SOCK_RAW, IPPROTO_ICMP);
if Sock=invalid_Socket then
raise Exception.Create('Konnte Socket nicht erstellen'+#13#10+syserrormessage(wsagetlasterror));
try
//Paket befüllen
ICMP.Typ := 8; //Echo-Request = Ping
ICMP.Code := 0;
ICMP.CheckSum := 0; //mit 0 vorbelegen (für die BErechnung)
ICMP.ID := $CDEF; // eine "eindeutige" ID
ICMP.Seq_Num := swap(aSeqNum); // Sequence Nr. in Big Endian (network order)
for i:=1 to PacketSize do
ICMP.Data[i] := 5; //Irgendwelche Daten
//Checksummer berechnen
{The 16 bit one's complement of the one's complement sum of all 16
bit words in the header. For computing the checksum, the checksum
field should be zero.}
//Achtung! Big Endian (deswegen swap)
pw:=@ICMP;
CheckSum:=0;
for i:=1 to sizeof(ICMP) div 2 do
begin
CheckSumTemp:=CheckSum+not(swap(pw^));
CheckSum:=CheckSumtemp and $FFFF;
inc(CheckSum,(CheckSumTemp and $10000) shr 16);
inc(pw);
end;
if sizeof(ICMP) mod 2=1 then
begin
CheckSumTemp:=CheckSum+not(swap(word(ICMP.data[high(ICMP.data)])));
CheckSum:=CheckSumtemp and $FFFF;
inc(CheckSum,(CheckSumTemp and $10000) shr 16);
end;
ICMP.CheckSum:=swap(CheckSum);
//Zieladdresse festlegen
addr.sin_family := AF_INET; //ist Internetaddresse
addr.sin_port := 0; //Port ist egal
addr.sin_addr.S_addr := Inet_Addr(PAnsiChar(aIPAddr)); //Addresse in 32 bit
//Paket abschicken
if sendto(sock,ICMP,sizeof(ICMP),0,addr,sizeof(Addr))=Socket_Error then
raise Exception.Create('Fehler bei sendto'+#13#10+syserrormessage(wsagetlasterror));
start:=gettickcount; //Zeit merken
//auf Antwort warten
FD_ZERO(Read);
FD_Set(sock,Read);
TimeOut.tv_sec:=2;
TimeOut.tv_usec:=0;
if Select(0,@Read,nil,nil,@TimeOut)>0 then //hier wird bis Timeout auf Read-Event gewartet
begin
IPLen:=recv(sock,buf,length(buf),0); //Antwort lesen (Achtung! Antwort ist inkl. IP-Header)
Ende:=gettickcount;
if IPLen=Socket_Error then
raise Exception.Create('Fehler bei recv'+#13#10+syserrormessage(wsagetlasterror));
if ((buf[0] and $F0) shr 4 = 4 ) then //wenn IPv4 (IPv6 geht anders)
begin
IPHeaderlen:=(buf[0] and $0F) * 4; //Länge des IPv4-Headers berechnen
if IPHEaderlen+sizeof(ICMP)<IPLen then
raise Exception.Create('Antwortpaket zu kurz');
ICMPret:=@buf[IPHeaderlen]; //IPHeader überspringen
if (ICMPret^.ID=ICMP.ID ) and
(ICMPret^.Seq_Num=ICMP.Seq_Num) then
begin
//Antwort lesen:
case ICMPret^.Typ of
//Man könnte jetzt noch die Richtigkeit der Checksummer und der Daten überprüfen
0 : //Alles ok
result:=format('Antwort in %d ms erhalten',[Ende-Start]);
3 : //Ziel nicht erreichbar; Hier kann noch ICMPret^.Code für den Grund ausgewertet werden
result:='Ziel nicht erreichbar';
11: //TTL zu klein - Weg zu weit
result:='Zeitlimit (TTL) überschritten';
else //etwas anderes sollte nicht passieren, aber falls doch:
result:=format('Unbekannte Antwort: Typ %d',[ICMPret^.Code]);
end;
end else
result:='Falsche Antwort-ID oder -Sequencenummer';
end else
result:='Kann IPv6 nicht lesen.';
end else
//Keine Rückantwort erhalten im gegebenen ZeitIntervall
result:='TimeOut';
finally
closesocket(sock);
end;
end;
Lesezeichen