1

I recently found out that the default Windows ping <IP> -t statistic is a bit off and the reason is that Windows counts a "Destination unreachable" as a valid reply (which may even be technically correct, since it is a reply of the router).

My intent of the action was to figure out whether my destination host is offline and I expected the packets to be lost, which was obviously not the case.

While I might use some other tools to figure out whether my host is down or not, I wonder whether there is a ping replacement out there that basically does the same thing, but lists each responses with their exact ICMP type and code (Wikipedia).

Expected input similar to ping: pingverbose <IP> [-t]

Expected output similar to:

Request Type 8 Code 0 : 12054 (100%) - Sent

Reply   Type 0       : 11802 (x%) - Reply
               Code 0:       11802 (x%) - Echo Reply
Reply   Type 3       :   202 (x%) - Destination unreachable
               Code 1:         182 (x%) - Destination host unreachable
               Code 0:          20 (x%) - Destination network unreachable    
Reply   Type 11      :    50 (x%) - Time exceeded
               Code 0:          50 (x%) - TTL expired in transit

I don't need the single requests, since it is a long-term ping. I'd be more interested in live statistics.

| improve this question | | | | |
  • 1
    I recently wrote a Ping Logger program that logs the time for a particular address at fixed intervals. The purpose was to have a log of when the router/ISP are down. If thsi works for you, have a look at superuser.com/questions/1003693/good-ping-indicator/… – Rohit Gupta Nov 24 '15 at 23:26
  • @RohitGupta: certainly a good starting point. I downloaded a binary executable. Would you mind making it open source or is it already? – Thomas Weller Nov 25 '15 at 8:48
  • It is freeware, I just haven't written the help file for it to say so. As for open source, I don't have an issue with that, but its written in Delphi which is not widely used. – Rohit Gupta Nov 25 '15 at 10:11
  • @Rohit Gupta, please provide the source code. – アレックス Nov 25 '15 at 19:20
  • @アレックス Done below as an answer. Thomas, maybe you can get ideas from it to write your own. Or let me know of any changes and I will see if I can make them. – Rohit Gupta Nov 26 '15 at 2:20
2

Here is the source of one I wrote recently. I am declaring it Open Source.

{ Log Pings }
program PING_LOG;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  ActiveX,
  ComObj,
  Variants,
  Vcl.Graphics,
  Windows;

const
  Version   = '1.0';
  CopyRight = '(C) Copyright 2015 by Blue Pacific Software';
  Contact   = 'rohitguptanz@gmail.com';
  Name      = 'Ping Log';
  sCRLF     = #$0D#$0A;

resourcestring
  msg_Ping_Help = sCRLF +
                  'Ping_Log [Options] Address' + sCRLF + sCRLF +
                  '/Cnn  = Ping nn times in a Cluster (default 1)' + sCRLF +
                  '/CAnn = Ping nn times in a Cluster and Average (default 0)' + sCRLF +
                  '/Rnn  = Ping every nn seconds (default 5)' + sCRLF +
                  '/Fnn  = For nn hours (default 0)' + sCRLF +
                  '/Bnn  = Buffer Size in bytes (default 32)' + sCRLF +
                  '/S    = Silent Mode (default Verbose)' + sCRLF +
                  '/Oxx  = Output to File xx (default none)' + sCRLF;
  msg_Ping_Waiting  = 'Please press ENTER';
  msg_Ping_Info     = sCRLF + 'Pinging %s with %d bytes of data:';
  msg_Ping_Reply_Lt = 'Reply from %s: bytes=%s time=<1ms TTL=%s';
  msg_Ping_Reply_Eq = 'Reply from %s: bytes=%s time=%sms TTL=%s';
  msg_Ping_Reply    = 'Reply from %s: %s';
  err_Proc_Rqst     = 'Error processing request';
  msg_Ping_Options  = 'Options : ';
  msg_Ping_Cluster  = '%d samples ';
  msg_Ping_Average  = 'averaged  ';
  msg_Ping_Sleep    = 'every %f seconds ';
  msg_Ping_Duration = 'for %d hours  ';
  msg_Ping_Output   = 'output to file "%s"';
  msg_Ping_Abort    = sCRLF + 'To abort at next ping, press Enter and wait.';

const
  fmt_Hdr = '%s [%s]  %s ' + sCRLF + 'Contact %s' + sCRLF;

var
  Address     : string  = '';
  Cluster     : Integer = 1;
  Average     : Boolean = False;
  SleepMs     : Integer = 5;
  DurationHrs : Integer = 0;
  DurationDays: Single  = 0;
  BufferSize  : Integer = 32;
  Verbose     : Boolean = True;
  OutputTo    : string  = '';
  Logging     : Boolean = False;
  Log_File    : Text;

function KeyPressed : Boolean;
var
  lpNumberOfEvents     : DWORD;
  lpBuffer             : TInputRecord;
  lpNumberOfEventsRead : DWORD;
  nStdHandle           : THandle;
begin
  Result := False;
  { Get the console handle }
  nStdHandle := GetStdHandle (STD_INPUT_HANDLE);
  lpNumberOfEvents := 0;
  { Get the number of events }
  GetNumberOfConsoleInputEvents (nStdHandle, lpNumberOfEvents);
  if lpNumberOfEvents <> 0
  then begin
       { Retrieve the event }
       PeekConsoleInput (nStdHandle,lpBuffer,1,lpNumberOfEventsRead);
       if lpNumberOfEventsRead <> 0
       then begin
            { Result is True if it is a Keyboard Event and
              a Key was pressed }
            if lpBuffer.EventType = KEY_EVENT
            then begin
                 if lpBuffer.Event.KeyEvent.bKeyDown
                 then Result := True
                 else FlushConsoleInputBuffer (nStdHandle);
            end
            else FlushConsoleInputBuffer (nStdHandle);
       end;
  end;
end;

function Get_Params : Boolean;
var
  lIdx    : Integer;
  lParams : Integer;
  lCmd    : char;
  lArg    : string;
begin
  Result := False;
  Writeln (Format (fmt_Hdr, [Name, Version, CopyRight, Contact]));
  lParams := ParamCount;
  for lIdx := 1 to lParams
  do begin
     lArg := ParamStr (lIdx);
     if length(lArg) >= 2
     then if lArg[1] in ['/','-','\']
     then begin
          Delete (lArg,1,1);
          lCmd := UpCase (lArg[1]);
          Delete (lArg,1,1);
          case lCmd of
            'C' : begin
                    if length(lArg) > 1
                    then if UpCase (lArg[1]) = 'A'
                    then begin
                         Delete (lArg,1,1);
                         Average := True;
                    end;
                    Cluster := StrToInt (lArg);
                  end;
            'R' : SleepMs := StrToInt (lArg) * 1000;
            'F' : DurationHrs := StrToInt (lArg);
            'S' : Verbose := False;
            'O' : OutputTo := lArg;
          end;
     end
     else Address := lArg;
  end;
  if (lParams = 0) or (Address = '')
  then begin
       Writeln (msg_Ping_Help);
       Write (msg_Ping_Waiting);
       ReadLn;
       Result := False;
  end;
  DurationDays := Round (DurationHrs) / HoursPerDay;
  Result := True;
end;

procedure Close_File;
begin
  if (OutputTo > '') and Logging
  then CloseFile (Log_File);
end;

function ConsoleEventProc (CtrlType : DWORD) : BOOL; stdcall;
begin
  if (CtrlType = CTRL_CLOSE_EVENT)
  then begin
       Close_File;
  end;
  Result := True;
end;

function GetStatusCodeStr (AStatusCode : integer) : string;
begin
  case AStatusCode of
    0     : Result := 'Success';
    11001 : Result := 'Buffer Too Small';
    11002 : Result := 'Destination Net Unreachable';
    11003 : Result := 'Destination Host Unreachable';
    11004 : Result := 'Destination Protocol Unreachable';
    11005 : Result := 'Destination Port Unreachable';
    11006 : Result := 'No Resources';
    11007 : Result := 'Bad Option';
    11008 : Result := 'Hardware Error';
    11009 : Result := 'Packet Too Big';
    11010 : Result := 'Request Timed Out';
    11011 : Result := 'Bad Request';
    11012 : Result := 'Bad Route';
    11013 : Result := 'TimeToLive Expired Transit';
    11014 : Result := 'TimeToLive Expired Reassembly';
    11015 : Result := 'Parameter Problem';
    11016 : Result := 'Source Quench';
    11017 : Result := 'Option Too Big';
    11018 : Result := 'Bad Destination';
    11032 : Result := 'Negotiating IPSEC';
    11050 : Result := 'General Failure'
    else
    Result := 'Unknown';
  end;
end;


{ The form of the Address parameter can be either the
  computer name (wxyz1234),
  IPv4 address (192.168.177.124), or
  IPv6 address (2010:836B:4179::836B:4179).}
function Ping (const AAddress : string;
               const ARetries, ABufferSize : Word)
              : Single;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;
  i             : Integer;

  lPacketsReceived : Integer;
  lResponse        : Integer;
  lMinimum         : Integer;
  lMaximum         : Integer;
  lAverage         : Integer;
begin;
  lPacketsReceived := 0;
  lMinimum         := 0;
  lMaximum         := 0;
  lAverage         := 0;
  lResponse        := 0;
  if Verbose
  then Writeln (Format(msg_Ping_Info,[AAddress,ABufferSize]));
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  for i := 0 to ARetries-1 do
  begin
    FWbemObjectSet:= FWMIService.ExecQuery(Format('SELECT * FROM Win32_PingStatus where Address=%s AND BufferSize=%d',[QuotedStr(AAddress),ABufferSize]),'WQL',0);
    oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
    if oEnum.Next(1, FWbemObject, iValue) = 0
    then begin
         if FWbemObject.StatusCode = 0
         then begin
              if Verbose
              then begin
                 if FWbemObject.ResponseTime > 0
                 then Writeln (Format(Msg_Ping_Reply_Eq,
                      [FWbemObject.ProtocolAddress,FWbemObject.ReplySize,
                       FWbemObject.ResponseTime,FWbemObject.TimeToLive]))
                 else Writeln (Format(msg_Ping_Reply_lt,
                      [FWbemObject.ProtocolAddress,FWbemObject.ReplySize,
                       FWbemObject.TimeToLive]));
              end;

              Inc (lPacketsReceived);
              lResponse := FWbemObject.ResponseTime;

              if lResponse > lMaximum
              then lMaximum := lResponse;
              if lMinimum = 0
              then lMinimum := lMaximum;
              if lResponse < lMinimum
              then lMinimum := lResponse;

              lAverage := lAverage+lResponse;
         end
         else begin
              if not VarIsNull(FWbemObject.StatusCode)
              then Writeln (Format(msg_Ping_Reply,
                    [FWbemObject.ProtocolAddress,
                     GetStatusCodeStr(FWbemObject.StatusCode)]))
              else Writeln (Format(msg_Ping_Reply,
                    [AAddress,err_Proc_Rqst]));
         end;
    end;
    FWbemObject := Unassigned;
    FWbemObjectSet := Unassigned;
    //Sleep(500);
  end;
  if lPacketsReceived > 0
  then lAverage := Round(lAverage/lPacketsReceived);
  if Verbose
  then begin
       Writeln ('');
       Writeln (Format('Ping statistics for %s:',[AAddress]));
       Writeln (Format('    Packets: Sent = %d, Received = %d, Lost = %d (%d%% loss),',
                [ARetries,lPacketsReceived,ARetries-lPacketsReceived,
                 Round((ARetries-lPacketsReceived)*100/ARetries)]));
       if lPacketsReceived > 0
       then begin
            Writeln ('Approximate round trip times in milli-seconds:');
            Writeln (Format('    Minimum = %dms, Maximum = %dms, Average = %dms',
                     [lMinimum,lMaximum,lAverage]));
       end;
  end;
  if Average
  then Result := lAverage
  else Result := lResponse;
end;

var
  lValue  : Single;
  lExpiry : TDateTime;
begin
 try
    if not Get_Params
    then Exit;
    { Open File If Required }
    if OutputTo <> ''
    then begin
         AssignFile (Log_File, OutputTo);
         if FileExists (OutputTo)
         then Append (Log_File)
         else Rewrite (Log_File);
         Logging := True;
         { Handle Close Event }
         SetConsoleCtrlHandler (@ConsoleEventProc, True);
    end;
    { Initaiize OLE }
    CoInitialize (nil);
    lExpiry  := Now + DurationDays;
    { Tell User the options }
    Write (msg_Ping_Options);
    if Cluster > 0
    then Write (Format (msg_Ping_Cluster, [Cluster]));
    if Average
    then Write (msg_Ping_Average);
    if SleepMs > 0
    then Write (Format (msg_Ping_Sleep, [Round (SleepMs) / 1000]));
    if DurationHrs > 0
    then Writeln (Format (msg_Ping_Duration, [DurationHrs]));
    if OutputTo <> ''
    then Writeln (Format (msg_Ping_Output, [OutputTo]));
    Writeln (msg_Ping_Abort);

    try
      repeat
        lValue := Ping (Address,Cluster,BufferSize);
        if OutputTo <> ''
        then Writeln (Log_File, '"' +
                      DateTimeToStr (Now) + '", ' +
                      FloatToStr (lValue));
        Sleep (SleepMs);
      until (Now >= lExpiry) or KeyPressed
    finally
      CoUninitialize;
      { Close File If Required }
      Close_File;
    end;
 except
    on E:Exception do
        Writeln (E.Classname, ':', E.Message);
 end;
 Readln;
end.
| improve this answer | | | | |
  • Thanks. Would you mind adding the name of the programming language and where I can download a compiler? It looks a bit like Pascal, except for the beginning. – Thomas Weller Aug 3 '16 at 19:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.