/*
- * 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;
}
{
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 */