[PING]
[reactos.git] / reactos / base / applications / network / ping / ping.c
index f0f2f73..bbeafa6 100644 (file)
 /*
- * COPYRIGHT:   See COPYING in the top level directory
- * PROJECT:     ReactOS ping utility
+ * Copyright (c) 2015 Tim Crawford
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+/*
+ * PROJECT:     ReactOS Ping Command
+ * LICENSE:     MIT
  * FILE:        base/applications/network/ping/ping.c
  * PURPOSE:     Network test utility
- * PROGRAMMERS:
+ * PROGRAMMERS: Tim Crawford <crawfxrd@gmail.com>
  */
 
+#include <stdlib.h>
+
+#define WIN32_LEAN_AND_MEAN
+
 #define WIN32_NO_STATUS
-#include <stdarg.h>
 #include <windef.h>
 #include <winbase.h>
-#include <winuser.h>
-#include <winnls.h>
-#include <wincon.h>
-#define _INC_WINDOWS
+#include <winsock2.h>
 #include <ws2tcpip.h>
-#include <stdio.h>
-#include <stdlib.h>
+#include <iphlpapi.h>
+#include <icmpapi.h>
+
+#include <conutils.h>
 
 #include "resource.h"
 
 #define NDEBUG
+#include <debug.h>
+
+#define SIZEOF_ICMP_ERROR 8
+#define SIZEOF_IO_STATUS_BLOCK 8
+#define DEFAULT_TIMEOUT 1000
+#define MAX_SEND_SIZE 65500
+
+static BOOL ParseCmdLine(int argc, PWSTR argv[]);
+static BOOL ResolveTarget(PCWSTR target);
+static void Ping(void);
+static void PrintStats(void);
+static BOOL WINAPI ConsoleCtrlHandler(DWORD ControlType);
+
+static HANDLE hIcmpFile = INVALID_HANDLE_VALUE;
+static ULONG Timeout = 4000;
+static int Family = AF_UNSPEC;
+static ULONG RequestSize = 32;
+static ULONG PingCount = 4;
+static BOOL PingForever = FALSE;
+static PADDRINFOW Target = NULL;
+static PCWSTR TargetName = NULL;
+static WCHAR Address[46];
+static WCHAR CanonName[NI_MAXHOST];
+static BOOL ResolveAddress = FALSE;
+
+static ULONG RTTMax = 0;
+static ULONG RTTMin = 0;
+static ULONG RTTTotal = 0;
+static ULONG EchosSent = 0;
+static ULONG EchosReceived = 0;
+static ULONG EchosSuccessful = 0;
+
+static IP_OPTION_INFORMATION IpOptions;
+
+int
+wmain(int argc, WCHAR *argv[])
+{
+    WSADATA wsaData;
+    ULONG i;
+    DWORD StrLen = 46;
+    int Status;
 
-/* General ICMP constants */
-#define ICMP_MINSIZE        8     /* Minimum ICMP packet size */
-#define ICMP_MAXSIZE        65535 /* Maximum ICMP packet size */
-
-/* ICMP message types */
-#define ICMPMSG_ECHOREQUEST 8     /* ICMP ECHO request message */
-#define ICMPMSG_ECHOREPLY   0     /* ICMP ECHO reply message */
-
-#pragma pack(4)
+    /* Initialize the Console Standard Streams */
+    ConInitStdStreams();
 
-/* IPv4 header structure */
-typedef struct _IPv4_HEADER
-{
-    unsigned char IHL:4;
-    unsigned char Version:4;
-    unsigned char TOS;
-    unsigned short Length;
-    unsigned short Id;
-    unsigned short FragFlags;
-    unsigned char TTL;
-    unsigned char Protocol;
-    unsigned short Checksum;
-    unsigned int SrcAddress;
-    unsigned int DstAddress;
-} IPv4_HEADER, *PIPv4_HEADER;
-
-/* ICMP echo request/reply header structure */
-typedef struct _ICMP_HEADER
-{
-    unsigned char Type;
-    unsigned char Code;
-    unsigned short Checksum;
-    unsigned short Id;
-    unsigned short SeqNum;
-} ICMP_HEADER, *PICMP_HEADER;
-
-typedef struct _ICMP_ECHO_PACKET
-{
-    ICMP_HEADER Icmp;
-} ICMP_ECHO_PACKET, *PICMP_ECHO_PACKET;
-
-#pragma pack(1)
-
-BOOL                NeverStop;
-BOOL                ResolveAddresses;
-UINT                PingCount;
-UINT                DataSize;   /* ICMP echo request data size */
-BOOL                DontFragment;
-ULONG               TTLValue;
-ULONG               TOSValue;
-ULONG               Timeout;
-WCHAR               TargetName[256];
-SOCKET              IcmpSock;
-SOCKADDR_IN         Target;
-WCHAR               TargetIP[16];
-FD_SET              Fds;
-TIMEVAL             Timeval;
-UINT                CurrentSeqNum;
-UINT                SentCount;
-UINT                LostCount;
-BOOL                MinRTTSet;
-LARGE_INTEGER       MinRTT;     /* Minimum round trip time in microseconds */
-LARGE_INTEGER       MaxRTT;
-LARGE_INTEGER       SumRTT;
-LARGE_INTEGER       AvgRTT;
-LARGE_INTEGER       TicksPerMs; /* Ticks per millisecond */
-LARGE_INTEGER       TicksPerUs; /* Ticks per microsecond */
-LARGE_INTEGER       SentTime;
-BOOL                UsePerformanceCounter;
-HANDLE              hStdOutput;
-
-#ifndef NDEBUG
-/* Display the contents of a buffer */
-static VOID DisplayBuffer(
-    PVOID Buffer,
-    DWORD Size)
-{
-    UINT i;
-    PCHAR p;
+    IpOptions.Ttl = 128;
 
-    printf("Buffer (0x%p)  Size (0x%lX).\n", Buffer, Size);
+    if (!ParseCmdLine(argc, argv))
+        return 1;
 
-    p = (PCHAR)Buffer;
-    for (i = 0; i < Size; i++)
+    if (!SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE))
     {
-        if (i % 16 == 0)
-            printf("\n");
-        printf("%02X ", (p[i]) & 0xFF);
+        DPRINT("Failed to set control handler: %lu\n", GetLastError());
+        return 1;
     }
-}
-#endif /* !NDEBUG */
-
-LPWSTR
-MyLoadString(UINT uID)
-{
-    HRSRC hres;
-    HGLOBAL hResData;
-    WCHAR *pwsz;
-    UINT string_num, i;
-
-    hres = FindResourceW(NULL, MAKEINTRESOURCEW((LOWORD(uID) >> 4) + 1), RT_STRING);
-    if (!hres) return NULL;
-
-    hResData = LoadResource(NULL, hres);
-    if (!hResData) return NULL;
-
-    pwsz = LockResource(hResData);
-    if (!pwsz) return NULL;
-    
-    string_num = uID & 15;
-    for (i = 0; i < string_num; i++)
-        pwsz += *pwsz + 1;
-
-    return pwsz + 1;
-}
-
-void FormatOutput(UINT uID, ...)
-{
-    va_list valist;
-
-    WCHAR Buf[1024];
-    CHAR AnsiBuf[1024];
-    LPWSTR pBuf = Buf;
-    PCHAR pAnsiBuf = AnsiBuf;
-    LPWSTR Format;
-    DWORD written;
-    UINT DataLength;
-    int AnsiLength;
-
-    va_start(valist, uID);
 
-    Format = MyLoadString(uID);
-    if (!Format) return;
+    Status = WSAStartup(MAKEWORD(2, 2), &wsaData);
+    if (Status != 0)
+    {
+        ConResPrintf(StdErr, IDS_WINSOCK_FAIL, Status);
+        return 1;
+    }
 
-    DataLength = FormatMessage(FORMAT_MESSAGE_FROM_STRING, Format, 0, 0, Buf,\
-                  sizeof(Buf) / sizeof(WCHAR), &valist);
+    if (!ResolveTarget(TargetName))
+    {
+        WSACleanup();
+        return 1;
+    }
 
-    if(!DataLength)
+    if (WSAAddressToStringW(Target->ai_addr, (DWORD)Target->ai_addrlen, NULL, Address, &StrLen) != 0)
     {
-        if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
-            return;
+        DPRINT("WSAAddressToStringW failed: %d\n", WSAGetLastError());
+        FreeAddrInfoW(Target);
+        WSACleanup();
+        return 1;
+    }
 
-        DataLength = FormatMessage(FORMAT_MESSAGE_FROM_STRING |\
-                                    FORMAT_MESSAGE_ALLOCATE_BUFFER,\
-                                    Format, 0, 0, (LPWSTR)&pBuf, 0, &valist);
+    if (Family == AF_INET6)
+        hIcmpFile = Icmp6CreateFile();
+    else
+        hIcmpFile = IcmpCreateFile();
 
-        if(!DataLength)
-            return;
-    }
 
-    if(GetFileType(hStdOutput) == FILE_TYPE_CHAR)
+    if (hIcmpFile == INVALID_HANDLE_VALUE)
     {
-        /* Is a console or a printer */
-        WriteConsole(hStdOutput, pBuf, DataLength, &written, NULL);
+        DPRINT("IcmpCreateFile failed: %lu\n", GetLastError());
+        FreeAddrInfoW(Target);
+        WSACleanup();
+        return 1;
     }
+
+    if (*CanonName)
+        ConResPrintf(StdOut, IDS_PINGING_HOSTNAME, CanonName, Address);
     else
-    {
-        /* Is a pipe, socket, file or other */
-        AnsiLength = WideCharToMultiByte(CP_ACP, 0, pBuf, DataLength,\
-                                         NULL, 0, NULL, NULL);
+        ConResPrintf(StdOut, IDS_PINGING_ADDRESS, Address);
 
-        if(AnsiLength >= sizeof(AnsiBuf))
-            pAnsiBuf = (PCHAR)HeapAlloc(GetProcessHeap(), 0, AnsiLength);
+    ConResPrintf(StdOut, IDS_PING_SIZE, RequestSize);
 
-        AnsiLength = WideCharToMultiByte(CP_OEMCP, 0, pBuf, DataLength,\
-                                         pAnsiBuf, AnsiLength, " ", NULL);
+    Ping();
 
-        WriteFile(hStdOutput, pAnsiBuf, AnsiLength, &written, NULL);
+    i = 1;
+    while (i < PingCount)
+    {
+        Sleep(1000);
+        Ping();
 
-        if(pAnsiBuf != AnsiBuf)
-            HeapFree(NULL, 0, pAnsiBuf);
+        if (!PingForever)
+            i++;
     }
 
-    if(pBuf != Buf)
-        LocalFree(pBuf);
-}
+    PrintStats();
 
-/* Display usage information on screen */
-static VOID Usage(VOID)
-{
-    FormatOutput(IDS_USAGE);
-}
+    IcmpCloseHandle(hIcmpFile);
+    FreeAddrInfoW(Target);
+    WSACleanup();
 
-/* Reset configuration to default values */
-static VOID Reset(VOID)
-{
-    LARGE_INTEGER PerformanceCounterFrequency;
-
-    NeverStop             = FALSE;
-    ResolveAddresses      = FALSE;
-    PingCount             = 4;
-    DataSize              = 32;
-    DontFragment          = FALSE;
-    TTLValue              = 128;
-    TOSValue              = 0;
-    Timeout               = 1000;
-    UsePerformanceCounter = QueryPerformanceFrequency(&PerformanceCounterFrequency);
-
-    if (UsePerformanceCounter)
-    {
-        /* Performance counters may return incorrect results on some multiprocessor
-           platforms so we restrict execution on the first processor. This may fail
-           on Windows NT so we fall back to GetCurrentTick() for timing */
-        if (SetThreadAffinityMask (GetCurrentThread(), 1) == 0)
-            UsePerformanceCounter = FALSE;
-
-        /* Convert frequency to ticks per millisecond */
-        TicksPerMs.QuadPart = PerformanceCounterFrequency.QuadPart / 1000;
-        /* And to ticks per microsecond */
-        TicksPerUs.QuadPart = PerformanceCounterFrequency.QuadPart / 1000000;
-    }
-    if (!UsePerformanceCounter)
-    {
-        /* 1 tick per millisecond for GetCurrentTick() */
-        TicksPerMs.QuadPart = 1;
-        /* GetCurrentTick() cannot handle microseconds */
-        TicksPerUs.QuadPart = 1;
-    }
+    return 0;
 }
 
-/* Parse command line parameters */
-static BOOL ParseCmdline(int argc, LPWSTR argv[])
+static
+BOOL
+ParseCmdLine(int argc, PWSTR argv[])
 {
-    INT i;
-    BOOL FoundTarget = FALSE, InvalidOption = FALSE;
+    int i;
 
     if (argc < 2)
     {
-        Usage();
+        ConResPrintf(StdOut, IDS_USAGE);
         return FALSE;
     }
 
@@ -260,508 +181,448 @@ static BOOL ParseCmdline(int argc, LPWSTR argv[])
         {
             switch (argv[i][1])
             {
-                case L't': NeverStop = TRUE; break;
-                case L'a': ResolveAddresses = TRUE; break;
-                case L'n':
-                    if (i + 1 < argc)
-                        PingCount = wcstoul(argv[++i], NULL, 0);
-                    else
-                        InvalidOption = TRUE;
-                    break;
-                case L'l':
-                    if (i + 1 < argc)
+            case L't':
+                PingForever = TRUE;
+                break;
+
+            case L'a':
+                ResolveAddress = TRUE;
+                break;
+
+            case L'n':
+            {
+                if (i + 1 < argc)
+                {
+                    PingForever = FALSE;
+                    PingCount = wcstoul(argv[++i], NULL, 0);
+                    if (PingCount == 0)
                     {
-                        DataSize = wcstoul(argv[++i], NULL, 0);
-                        
-                        if (DataSize > ICMP_MAXSIZE - sizeof(ICMP_ECHO_PACKET) - sizeof(IPv4_HEADER))
-                        {
-                            FormatOutput(IDS_BAD_VALUE_OPTION_L, ICMP_MAXSIZE - \
-                                         (int)sizeof(ICMP_ECHO_PACKET) - \
-                                         (int)sizeof(IPv4_HEADER));
-                            return FALSE;
-                        }
-                    } else
-                        InvalidOption = TRUE;
-                    break;
-                case L'f': DontFragment = TRUE; break;
-                case L'i':
-                    if (i + 1 < argc)
-                        TTLValue = wcstoul(argv[++i], NULL, 0);
-                    else
-                        InvalidOption = TRUE;
-                    break;
-                case L'v':
-                    if (i + 1 < argc)
-                        TOSValue = wcstoul(argv[++i], NULL, 0);
-                    else
-                        InvalidOption = TRUE;
-                    break;
-                case L'w':
-                    if (i + 1 < argc)
-                        Timeout = wcstoul(argv[++i], NULL, 0);
-                    else
-                        InvalidOption = TRUE;
-                    break;
-                case '?':
-                    Usage();
-                    return FALSE;
-                default:
-                    FormatOutput(IDS_BAD_OPTION, argv[i]);
+                        ConResPrintf(StdErr, IDS_BAD_VALUE, argv[i - 1], 1, UINT_MAX);
+                        return FALSE;
+                    }
+                }
+                else
+                {
+                    ConResPrintf(StdErr, IDS_MISSING_VALUE, argv[i]);
                     return FALSE;
+                }
+                break;
             }
-            if (InvalidOption)
+
+            case L'l':
             {
-                FormatOutput(IDS_BAD_OPTION_FORMAT, argv[i]);
-                return FALSE;
+                if (i + 1 < argc)
+                {
+                    RequestSize = wcstoul(argv[++i], NULL, 0);
+                    if (RequestSize > MAX_SEND_SIZE)
+                    {
+                        ConResPrintf(StdErr, IDS_BAD_VALUE, argv[i - 1], 0, MAX_SEND_SIZE);
+                        return FALSE;
+                    }
+                }
+                else
+                {
+                    ConResPrintf(StdErr, IDS_MISSING_VALUE, argv[i]);
+                    return FALSE;
+                }
+                break;
             }
-        }
-        else
-        {
-            if (FoundTarget)
+
+            case L'f':
             {
-                FormatOutput(IDS_BAD_PARAMETER, argv[i]);
-                return FALSE;
+                if (Family == AF_INET6)
+                {
+                    ConResPrintf(StdErr, IDS_WRONG_FAMILY, argv[i], L"IPv4");
+                    return FALSE;
+                }
+
+                Family = AF_INET;
+                IpOptions.Flags |= IP_FLAG_DF;
+                break;
             }
-            else
+
+            case L'i':
             {
-                wcscpy(TargetName, argv[i]);
-                FoundTarget = TRUE;
+                if (i + 1 < argc)
+                {
+                    ULONG Ttl = wcstoul(argv[++i], NULL, 0);
+
+                    if ((Ttl == 0) || (Ttl > UCHAR_MAX))
+                    {
+                        ConResPrintf(StdErr, IDS_BAD_VALUE, argv[i - 1], 1, UCHAR_MAX);
+                        return FALSE;
+                    }
+
+                    IpOptions.Ttl = (UCHAR)Ttl;
+                }
+                else
+                {
+                    ConResPrintf(StdErr, IDS_MISSING_VALUE, argv[i]);
+                    return FALSE;
+                }
+                break;
             }
-        }
-    }
 
-    if (!FoundTarget)
-    {
-        FormatOutput(IDS_DEST_MUST_BE_SPECIFIED);
-        return FALSE;
-    }
+            case L'v':
+            {
+                if (Family == AF_INET6)
+                {
+                    ConResPrintf(StdErr, IDS_WRONG_FAMILY, argv[i], L"IPv4");
+                    return FALSE;
+                }
+
+                Family = AF_INET;
+
+                if (i + 1 < argc)
+                {
+                    /* This option has been deprecated. Don't do anything. */
+                    i++;
+                }
+                else
+                {
+                    ConResPrintf(StdErr, IDS_MISSING_VALUE, argv[i]);
+                    return FALSE;
+                }
 
-    return TRUE;
-}
+                break;
+            }
 
-/* Calculate checksum of data */
-static WORD Checksum(PUSHORT data, UINT size)
-{
-    ULONG sum = 0;
+            case L'w':
+            {
+                if (i + 1 < argc)
+                {
+                    Timeout = wcstoul(argv[++i], NULL, 0);
+                    if (Timeout < DEFAULT_TIMEOUT)
+                        Timeout = DEFAULT_TIMEOUT;
+                }
+                else
+                {
+                    ConResPrintf(StdErr, IDS_MISSING_VALUE, argv[i]);
+                    return FALSE;
+                }
+                break;
+            }
 
-    while (size > 1)
-    {
-        sum  += *data++;
-        size -= sizeof(USHORT);
-    }
+            case L'R':
+            {
+                if (Family == AF_INET)
+                {
+                    ConResPrintf(StdErr, IDS_WRONG_FAMILY, argv[i], L"IPv6");
+                    return FALSE;
+                }
 
-    if (size)
-        sum += *(UCHAR*)data;
+                Family = AF_INET6;
 
-    sum = (sum >> 16) + (sum & 0xFFFF);
-    sum += (sum >> 16);
+                /* This option has been deprecated. Don't do anything. */
+                break;
+            }
 
-    return (USHORT)(~sum);
-}
+            case L'4':
+            {
+                if (Family == AF_INET6)
+                {
+                    ConResPrintf(StdErr, IDS_WRONG_FAMILY, argv[i], L"IPv4");
+                    return FALSE;
+                }
 
-/* Prepare to ping target */
-static BOOL Setup(VOID)
-{
-    WORD     wVersionRequested;
-    WSADATA  WsaData;
-    INT      Status;
-    ULONG    Addr;
-    PHOSTENT phe;
-    CHAR     aTargetName[256];
+                Family = AF_INET;
+                break;
+            }
 
-    wVersionRequested = MAKEWORD(2, 2);
+            case L'6':
+            {
+                if (Family == AF_INET)
+                {
+                    ConResPrintf(StdErr, IDS_WRONG_FAMILY, argv[i], L"IPv6");
+                    return FALSE;
+                }
 
-    Status = WSAStartup(wVersionRequested, &WsaData);
-    if (Status != 0)
-    {
-        FormatOutput(IDS_COULD_NOT_INIT_WINSOCK);
-        return FALSE;
-    }
+                Family = AF_INET6;
+                break;
+            }
 
-    IcmpSock = WSASocket(AF_INET, SOCK_RAW, IPPROTO_ICMP, NULL, 0, 0);
-    if (IcmpSock == INVALID_SOCKET)
-    {
-        FormatOutput(IDS_COULD_NOT_CREATE_SOCKET, WSAGetLastError());
-        return FALSE;
-    }
+            case L'?':
+                ConResPrintf(StdOut, IDS_USAGE);
+                return FALSE;
 
-    if (setsockopt(IcmpSock,
-                   IPPROTO_IP,
-                   IP_DONTFRAGMENT,
-                   (const char *)&DontFragment,
-                   sizeof(DontFragment)) == SOCKET_ERROR)
-    {
-        FormatOutput(IDS_SETSOCKOPT_FAILED, WSAGetLastError());
-        return FALSE;
-    }
+            default:
+                ConResPrintf(StdErr, IDS_BAD_OPTION, argv[i]);
+                ConResPrintf(StdErr, IDS_USAGE);
+                return FALSE;
+            }
+        }
+        else
+        {
+            if (TargetName != NULL)
+            {
+                ConResPrintf(StdErr, IDS_BAD_PARAMETER, argv[i]);
+                return FALSE;
+            }
 
-    if (setsockopt(IcmpSock,
-                   IPPROTO_IP,
-                   IP_TTL,
-                   (const char *)&TTLValue,
-                   sizeof(TTLValue)) == SOCKET_ERROR)
-    {
-        FormatOutput(IDS_SETSOCKOPT_FAILED, WSAGetLastError());
-        return FALSE;
+            TargetName = argv[i];
+        }
     }
 
-
-    if(!WideCharToMultiByte(CP_ACP, 0, TargetName, -1, aTargetName,\
-                            sizeof(aTargetName), NULL, NULL))
+    if (TargetName == NULL)
     {
-        FormatOutput(IDS_UNKNOWN_HOST, TargetName);
+        ConResPrintf(StdErr, IDS_MISSING_ADDRESS);
         return FALSE;
     }
 
-    ZeroMemory(&Target, sizeof(Target));
-    phe = NULL;
-    Addr = inet_addr(aTargetName);
-    if (Addr == INADDR_NONE)
-    {
-        phe = gethostbyname(aTargetName);
-        if (phe == NULL)
-        {
-            FormatOutput(IDS_UNKNOWN_HOST, TargetName);
-            return FALSE;
-        }
-
-        CopyMemory(&Target.sin_addr, phe->h_addr, phe->h_length);
-        Target.sin_family = phe->h_addrtype;
-    }
-    else
-    {
-        Target.sin_addr.s_addr = Addr;
-        Target.sin_family = AF_INET;
-    }
-
-
-    swprintf(TargetIP, L"%d.%d.%d.%d", Target.sin_addr.S_un.S_un_b.s_b1,\
-                                       Target.sin_addr.S_un.S_un_b.s_b2,\
-                                       Target.sin_addr.S_un.S_un_b.s_b3,\
-                                       Target.sin_addr.S_un.S_un_b.s_b4);
-    CurrentSeqNum = 1;
-    SentCount = 0;
-    LostCount = 0;
-    MinRTT.QuadPart = 0;
-    MaxRTT.QuadPart = 0;
-    SumRTT.QuadPart = 0;
-    MinRTTSet       = FALSE;
     return TRUE;
 }
 
-/* Close socket */
-static VOID Cleanup(VOID)
+static
+BOOL
+ResolveTarget(PCWSTR target)
 {
-    if (IcmpSock != INVALID_SOCKET)
-        closesocket(IcmpSock);
+    ADDRINFOW hints;
+    int Status;
 
-    WSACleanup();
-}
+    ZeroMemory(&hints, sizeof(hints));
+    hints.ai_family = Family;
+    hints.ai_flags = AI_NUMERICHOST;
 
-static VOID QueryTime(PLARGE_INTEGER Time)
-{
-    if (UsePerformanceCounter)
+    Status = GetAddrInfoW(target, NULL, &hints, &Target);
+    if (Status != 0)
     {
-        if (QueryPerformanceCounter(Time) == 0)
-        {
-            /* This should not happen, but we fall
-               back to GetCurrentTick() if it does */
-            Time->u.LowPart  = (ULONG)GetTickCount();
-            Time->u.HighPart = 0;
+        hints.ai_flags = AI_CANONNAME;
 
-            /* 1 tick per millisecond for GetCurrentTick() */
-            TicksPerMs.QuadPart = 1;
-            /* GetCurrentTick() cannot handle microseconds */
-            TicksPerUs.QuadPart = 1;
-
-            UsePerformanceCounter = FALSE;
+        Status = GetAddrInfoW(target, NULL, &hints, &Target);
+        if (Status != 0)
+        {
+            ConResPrintf(StdOut, IDS_UNKNOWN_HOST, target);
+            return FALSE;
         }
+
+        wcsncpy(CanonName, Target->ai_canonname, wcslen(Target->ai_canonname));
     }
-    else
+    else if (ResolveAddress)
     {
-        Time->u.LowPart  = (ULONG)GetTickCount();
-        Time->u.HighPart = 0;
+        Status = GetNameInfoW(Target->ai_addr, Target->ai_addrlen,
+                              CanonName, _countof(CanonName),
+                              NULL, 0,
+                              NI_NAMEREQD);
+        if (Status != 0)
+        {
+            DPRINT("GetNameInfoW failed: %d\n", WSAGetLastError());
+        }
     }
-}
-
-static VOID TimeToMsString(LPWSTR String, LARGE_INTEGER Time)
-{
-    WCHAR         Convstr[40];
-    LARGE_INTEGER LargeTime;
-    LPWSTR ms;
 
-    LargeTime.QuadPart = Time.QuadPart / TicksPerMs.QuadPart;
+    Family = Target->ai_family;
 
-    _i64tow(LargeTime.QuadPart, Convstr, 10);
-    wcscpy(String, Convstr);
-    ms = MyLoadString(IDS_MS);
-    wcscat(String, ms);
+    return TRUE;
 }
 
-/* Locate the ICMP data and print it. Returns TRUE if the packet was good,
-   FALSE if not */
-static BOOL DecodeResponse(PCHAR buffer, UINT size, PSOCKADDR_IN from)
+static
+void
+Ping(void)
 {
-    PIPv4_HEADER      IpHeader;
-    PICMP_ECHO_PACKET Icmp;
-    UINT              IphLength;
-    WCHAR             Time[100];
-    LARGE_INTEGER     RelativeTime;
-    LARGE_INTEGER     LargeTime;
-    WCHAR             Sign[2];
-    WCHAR wfromIP[16];
+    PVOID ReplyBuffer = NULL;
+    PVOID SendBuffer = NULL;
+    DWORD ReplySize = 0;
+    DWORD Status;
 
-    IpHeader = (PIPv4_HEADER)buffer;
-
-    IphLength = IpHeader->IHL * 4;
-
-    if (size  < IphLength + ICMP_MINSIZE)
+    SendBuffer = malloc(RequestSize);
+    if (SendBuffer == NULL)
     {
-#ifndef NDEBUG
-        printf("Bad size (0x%X < 0x%X)\n", size, IphLength + ICMP_MINSIZE);
-#endif /* !NDEBUG */
-        return FALSE;
+        ConResPrintf(StdErr, IDS_NO_RESOURCES);
+        exit(1);
     }
 
-    Icmp = (PICMP_ECHO_PACKET)(buffer + IphLength);
+    ZeroMemory(SendBuffer, RequestSize);
 
-    if (Icmp->Icmp.Type != ICMPMSG_ECHOREPLY)
-    {
-#ifndef NDEBUG
-        printf("Bad ICMP type (0x%X should be 0x%X)\n", Icmp->Icmp.Type, ICMPMSG_ECHOREPLY);
-#endif /* !NDEBUG */
-        return FALSE;
-    }
+    if (Family == AF_INET6)
+        ReplySize += sizeof(ICMPV6_ECHO_REPLY);
+    else
+        ReplySize += sizeof(ICMP_ECHO_REPLY);
 
-    if (Icmp->Icmp.Id != (USHORT)GetCurrentProcessId())
-    {
-#ifndef NDEBUG
-        printf("Bad ICMP id (0x%X should be 0x%X)\n", Icmp->Icmp.Id, (USHORT)GetCurrentProcessId());
-#endif /* !NDEBUG */
-        return FALSE;
-    }
+    ReplySize += RequestSize + SIZEOF_ICMP_ERROR + SIZEOF_IO_STATUS_BLOCK;
 
-    if (from->sin_addr.s_addr != Target.sin_addr.s_addr)
+    ReplyBuffer = malloc(ReplySize);
+    if (ReplyBuffer == NULL)
     {
-#ifndef NDEBUG
-        printf("Bad source address (%s should be %s)\n", inet_ntoa(from->sin_addr), inet_ntoa(Target.sin_addr));
-#endif /* !NDEBUG */
-        return FALSE;
+        ConResPrintf(StdErr, IDS_NO_RESOURCES);
+        free(SendBuffer);
+        exit(1);
     }
 
-    QueryTime(&LargeTime);
+    ZeroMemory(ReplyBuffer, ReplySize);
 
-    RelativeTime.QuadPart = (LargeTime.QuadPart - SentTime.QuadPart);
+    EchosSent++;
 
-    if ((RelativeTime.QuadPart / TicksPerMs.QuadPart) < 1)
+    if (Family == AF_INET6)
     {
-        LPWSTR ms1;
+        struct sockaddr_in6 Source;
+
+        ZeroMemory(&Source, sizeof(Source));
+        Source.sin6_family = AF_INET6;
 
-        wcscpy(Sign, L"<");
-        ms1 = MyLoadString(IDS_1MS);
-        wcscpy(Time, ms1);
+        Status = Icmp6SendEcho2(hIcmpFile, NULL, NULL, NULL,
+                                &Source,
+                                (struct sockaddr_in6 *)Target->ai_addr,
+                                SendBuffer, (USHORT)RequestSize, &IpOptions,
+                                ReplyBuffer, ReplySize, Timeout);
     }
     else
     {
-        wcscpy(Sign, L"=");
-        TimeToMsString(Time, RelativeTime);
+        Status = IcmpSendEcho2(hIcmpFile, NULL, NULL, NULL,
+                               ((PSOCKADDR_IN)Target->ai_addr)->sin_addr.s_addr,
+                               SendBuffer, (USHORT)RequestSize, &IpOptions,
+                               ReplyBuffer, ReplySize, Timeout);
     }
 
+    free(SendBuffer);
 
-    swprintf(wfromIP, L"%d.%d.%d.%d", from->sin_addr.S_un.S_un_b.s_b1,\
-                                      from->sin_addr.S_un.S_un_b.s_b2,\
-                                      from->sin_addr.S_un.S_un_b.s_b3,\
-                                      from->sin_addr.S_un.S_un_b.s_b4);
-    FormatOutput(IDS_REPLY_FROM, wfromIP,\
-                 size - IphLength - (int)sizeof(ICMP_ECHO_PACKET),\
-                 Sign, Time, IpHeader->TTL);
-
-    if (RelativeTime.QuadPart < MinRTT.QuadPart || !MinRTTSet)
+    if (Status == 0)
     {
-        MinRTT.QuadPart = RelativeTime.QuadPart;
-        MinRTTSet = TRUE;
-    }
-    if (RelativeTime.QuadPart > MaxRTT.QuadPart)
-        MaxRTT.QuadPart = RelativeTime.QuadPart;
+        Status = GetLastError();
+        switch (Status)
+        {
+        case IP_DEST_HOST_UNREACHABLE:
+            ConResPrintf(StdOut, IDS_DEST_HOST_UNREACHABLE);
+            break;
 
-    SumRTT.QuadPart += RelativeTime.QuadPart;
+        case IP_DEST_NET_UNREACHABLE:
+            ConResPrintf(StdOut, IDS_DEST_NET_UNREACHABLE);
+            break;
 
-    return TRUE;
-}
+        case IP_REQ_TIMED_OUT:
+            ConResPrintf(StdOut, IDS_REQUEST_TIMED_OUT);
+            break;
 
-/* Send and receive one ping */
-static BOOL Ping(VOID)
-{
-    INT                 Status;
-    SOCKADDR            From;
-    INT                 Length;
-    PVOID               Buffer;
-    UINT                Size;
-    PICMP_ECHO_PACKET   Packet;
-
-    /* Account for extra space for IP header when packet is received */
-    Size   = DataSize + 128;
-    Buffer = GlobalAlloc(0, Size);
-    if (!Buffer)
-    {
-        FormatOutput(IDS_NOT_ENOUGH_RESOURCES);
-        return FALSE;
+        default:
+            ConResPrintf(StdOut, IDS_TRANSMIT_FAILED, Status);
+            break;
+        }
     }
+    else
+    {
+        EchosReceived++;
 
-    ZeroMemory(Buffer, Size);
-    Packet = (PICMP_ECHO_PACKET)Buffer;
+        ConResPrintf(StdOut, IDS_REPLY_FROM, Address);
 
-    /* Assemble ICMP echo request packet */
-    Packet->Icmp.Type     = ICMPMSG_ECHOREQUEST;
-    Packet->Icmp.Code     = 0;
-    Packet->Icmp.Id       = (USHORT)GetCurrentProcessId();
-    Packet->Icmp.SeqNum   = htons((USHORT)CurrentSeqNum);
-    Packet->Icmp.Checksum = 0;
+        if (Family == AF_INET6)
+        {
+            PICMPV6_ECHO_REPLY pEchoReply;
 
-    /* Calculate checksum for ICMP header and data area */
-    Packet->Icmp.Checksum = Checksum((PUSHORT)&Packet->Icmp, sizeof(ICMP_ECHO_PACKET) + DataSize);
+            pEchoReply = (PICMPV6_ECHO_REPLY)ReplyBuffer;
 
-    CurrentSeqNum++;
+            switch (pEchoReply->Status)
+            {
+            case IP_SUCCESS:
+            {
+                EchosSuccessful++;
 
-    /* Send ICMP echo request */
+                if (pEchoReply->RoundTripTime == 0)
+                    ConResPrintf(StdOut, IDS_REPLY_TIME_0MS);
+                else
+                    ConResPrintf(StdOut, IDS_REPLY_TIME_MS, pEchoReply->RoundTripTime);
 
-    FD_ZERO(&Fds);
-    FD_SET(IcmpSock, &Fds);
-    Timeval.tv_sec  = Timeout / 1000;
-    Timeval.tv_usec = Timeout % 1000;
-    Status = select(0, NULL, &Fds, NULL, &Timeval);
-    if ((Status != SOCKET_ERROR) && (Status != 0))
-    {
+                if (pEchoReply->RoundTripTime < RTTMin || RTTMin == 0)
+                    RTTMin = pEchoReply->RoundTripTime;
 
-#ifndef NDEBUG
-        printf("Sending packet\n");
-        DisplayBuffer(Buffer, sizeof(ICMP_ECHO_PACKET) + DataSize);
-        printf("\n");
-#endif /* !NDEBUG */
+                if (pEchoReply->RoundTripTime > RTTMax || RTTMax == 0)
+                    RTTMax = pEchoReply->RoundTripTime;
 
-        Status = sendto(IcmpSock, Buffer, sizeof(ICMP_ECHO_PACKET) + DataSize,
-            0, (SOCKADDR*)&Target, sizeof(Target));
-        QueryTime(&SentTime);
-        SentCount++;
-    }
-    if (Status == SOCKET_ERROR)
-    {
-        if (WSAGetLastError() == WSAEHOSTUNREACH)
-            FormatOutput(IDS_DEST_UNREACHABLE);
-        else
-            FormatOutput(IDS_COULD_NOT_TRANSMIT, WSAGetLastError());
-        GlobalFree(Buffer);
-        return FALSE;
-    }
+                ConPuts(StdOut, L"\n");
+
+                RTTTotal += pEchoReply->RoundTripTime;
+                break;
+            }
 
-    /* Expect to receive ICMP echo reply */
-    FD_ZERO(&Fds);
-    FD_SET(IcmpSock, &Fds);
-    Timeval.tv_sec  = Timeout / 1000;
-    Timeval.tv_usec = Timeout % 1000;
+            case IP_TTL_EXPIRED_TRANSIT:
+                ConResPrintf(StdOut, IDS_TTL_EXPIRED);
+                break;
 
-    do {
-        Status = select(0, &Fds, NULL, NULL, &Timeval);
-        if ((Status != SOCKET_ERROR) && (Status != 0))
-        {
-            Length = sizeof(From);
-            Status = recvfrom(IcmpSock, Buffer, Size, 0, &From, &Length);
-
-#ifndef NDEBUG
-            printf("Received packet\n");
-            DisplayBuffer(Buffer, Status);
-            printf("\n");
-#endif /* !NDEBUG */
+            default:
+                ConResPrintf(StdOut, IDS_REPLY_STATUS, pEchoReply->Status);
+                break;
+            }
         }
         else
-            LostCount++;
-        if (Status == SOCKET_ERROR)
         {
-            if (WSAGetLastError() != WSAETIMEDOUT)
-            {
-                FormatOutput(IDS_COULD_NOT_RECV, WSAGetLastError());
-                GlobalFree(Buffer);
-                return FALSE;
-            }
-            Status = 0;
-        }
+            PICMP_ECHO_REPLY pEchoReply;
 
-        if (Status == 0)
-        {
-            FormatOutput(IDS_REQUEST_TIMEOUT);
-            GlobalFree(Buffer);
-            return TRUE;
-        }
+            pEchoReply = (PICMP_ECHO_REPLY)ReplyBuffer;
 
-    } while (!DecodeResponse(Buffer, Status, (PSOCKADDR_IN)&From));
+            switch (pEchoReply->Status)
+            {
+            case IP_SUCCESS:
+            {
+                EchosSuccessful++;
 
-    GlobalFree(Buffer);
-    return TRUE;
-}
+                ConResPrintf(StdOut, IDS_REPLY_BYTES, pEchoReply->DataSize);
 
+                if (pEchoReply->RoundTripTime == 0)
+                    ConResPrintf(StdOut, IDS_REPLY_TIME_0MS);
+                else
+                    ConResPrintf(StdOut, IDS_REPLY_TIME_MS, pEchoReply->RoundTripTime);
 
-/* Program entry point */
-int wmain(int argc, LPWSTR argv[])
-{
-    UINT Count;
-    WCHAR MinTime[20];
-    WCHAR MaxTime[20];
-    WCHAR AvgTime[20];
+                ConResPrintf(StdOut, IDS_REPLY_TTL, pEchoReply->Options.Ttl);
 
-    hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+                if (pEchoReply->RoundTripTime < RTTMin || RTTMin == 0)
+                    RTTMin = pEchoReply->RoundTripTime;
 
-    Reset();
+                if (pEchoReply->RoundTripTime > RTTMax || RTTMax == 0)
+                    RTTMax = pEchoReply->RoundTripTime;
 
-    if ((ParseCmdline(argc, argv)) && (Setup()))
-    {
+                RTTTotal += pEchoReply->RoundTripTime;
+                break;
+            }
 
-        FormatOutput(IDS_PING_WITH_BYTES, TargetName, TargetIP, DataSize);
+            case IP_TTL_EXPIRED_TRANSIT:
+                ConResPrintf(StdOut, IDS_TTL_EXPIRED);
+                break;
 
-        Count = 0;
-        while ((NeverStop) || (Count < PingCount))
-        {
-            Ping();
-            Count++;
-            if((NeverStop) || (Count < PingCount))
-                Sleep(Timeout);
-        };
+            default:
+                ConResPrintf(StdOut, IDS_REPLY_STATUS, pEchoReply->Status);
+                break;
+            }
+        }
+    }
 
-        Cleanup();
+    free(ReplyBuffer);
+}
 
-        /* Calculate avarage round trip time */
-        if ((SentCount - LostCount) > 0)
-            AvgRTT.QuadPart = SumRTT.QuadPart / (SentCount - LostCount);
-        else
-            AvgRTT.QuadPart = 0;
+static
+void
+PrintStats(void)
+{
+    ULONG EchosLost = EchosSent - EchosReceived;
+    ULONG PercentLost = (ULONG)((EchosLost / (double)EchosSent) * 100.0);
 
-        /* Calculate loss percent */
-        Count = SentCount ? (LostCount * 100) / SentCount : 0;
+    ConResPrintf(StdOut, IDS_STATISTICS, Address, EchosSent, EchosReceived, EchosLost, PercentLost);
 
-        if (!MinRTTSet)
-            MinRTT = MaxRTT;
+    if (EchosSuccessful > 0)
+    {
+        ULONG RTTAverage = RTTTotal / EchosSuccessful;
+        ConResPrintf(StdOut, IDS_APPROXIMATE_RTT, RTTMin, RTTMax, RTTAverage);
+    }
+}
 
-        TimeToMsString(MinTime, MinRTT);
-        TimeToMsString(MaxTime, MaxRTT);
-        TimeToMsString(AvgTime, AvgRTT);
+static
+BOOL
+WINAPI
+ConsoleCtrlHandler(DWORD ControlType)
+{
+    switch (ControlType)
+    {
+    case CTRL_C_EVENT:
+        PrintStats();
+        ConResPrintf(StdOut, IDS_CTRL_C);
+        return FALSE;
 
-        /* Print statistics */
-        FormatOutput(IDS_PING_STATISTICS, TargetIP);
-        FormatOutput(IDS_PACKETS_SENT_RECEIVED_LOST,\
-                     SentCount, SentCount - LostCount, LostCount, Count);
+    case CTRL_BREAK_EVENT:
+        PrintStats();
+        ConResPrintf(StdOut, IDS_CTRL_BREAK);
+        return TRUE;
 
+    case CTRL_CLOSE_EVENT:
+        PrintStats();
+        return FALSE;
 
-        /* Print approximate times or NO approximate times if 100% loss */
-        if ((SentCount - LostCount) > 0)
-        {
-            FormatOutput(IDS_APPROXIMATE_ROUND_TRIP);
-            FormatOutput(IDS_MIN_MAX_AVERAGE, MinTime, MaxTime, AvgTime);
-        }
-    }
-    else
-    {
-        return 1;
+    default:
+        return FALSE;
     }
-    return 0;
 }
-
-/* EOF */