[ARPING]
authorPierre Schweitzer <pierre@reactos.org>
Sun, 29 Nov 2015 19:44:29 +0000 (19:44 +0000)
committerPierre Schweitzer <pierre@reactos.org>
Sun, 29 Nov 2015 19:44:29 +0000 (19:44 +0000)
Implement an arping tool. Some portions of code are just plain copy/paste from ping tool code (booh, it's bad!).
It was designed and tested on W2K3.

It's in RosApps for two major reasons: such a tool doesn't exist by default in Windows 2K3.
And it doesn't work in ReactOS.

svn path=/trunk/; revision=70212

rosapps/applications/cmdutils/CMakeLists.txt
rosapps/applications/cmdutils/arping/CMakeLists.txt [new file with mode: 0644]
rosapps/applications/cmdutils/arping/arping.c [new file with mode: 0644]
rosapps/applications/cmdutils/arping/arping.rc [new file with mode: 0644]
rosapps/applications/cmdutils/arping/lang/en-US.rc [new file with mode: 0644]
rosapps/applications/cmdutils/arping/resource.h [new file with mode: 0644]

index 16c508e..f3057d5 100644 (file)
@@ -1,4 +1,5 @@
 add_subdirectory(appwiz)
+add_subdirectory(arping)
 add_subdirectory(cat)
 add_subdirectory(hackssign)
 add_subdirectory(ntfsinfo)
diff --git a/rosapps/applications/cmdutils/arping/CMakeLists.txt b/rosapps/applications/cmdutils/arping/CMakeLists.txt
new file mode 100644 (file)
index 0000000..828b279
--- /dev/null
@@ -0,0 +1,11 @@
+
+add_definitions(-D__USE_W32_SOCKETS)
+add_executable(arping arping.c arping.rc)
+set_module_type(arping win32cui UNICODE)
+add_importlibs(arping user32 ws2_32 iphlpapi msvcrt kernel32)
+
+if(MSVC)
+    add_importlibs(arping ntdll)
+endif()
+
+add_cd_file(TARGET arping DESTINATION reactos/system32 FOR all)
diff --git a/rosapps/applications/cmdutils/arping/arping.c b/rosapps/applications/cmdutils/arping/arping.c
new file mode 100644 (file)
index 0000000..461abbf
--- /dev/null
@@ -0,0 +1,424 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS ping utility
+ * FILE:        applications/cmdutils/arping/arping.c
+ * PURPOSE:     Network test utility
+ * PROGRAMMERS: Pierre Schweitzer <pierre@reactos.org>
+ */
+
+#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 <ws2tcpip.h>
+#include <iphlpapi.h>
+#include <ws2def.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "resource.h"
+
+BOOL                NeverStop;
+UINT                PingCount;
+WCHAR               TargetName[256];
+WCHAR               SourceName[256];
+DWORD               SourceAddr;
+DWORD               TargetAddr;
+WCHAR               TargetIP[16];
+WCHAR               SourceIP[16];
+SOCKADDR_IN         Target;
+HANDLE              hStdOutput;
+ULONG               Timeout;
+LARGE_INTEGER       TicksPerMs;
+LARGE_INTEGER       TicksPerUs;
+BOOL                UsePerformanceCounter;
+UINT                Sent;
+UINT                Received;
+
+void FormatOutput(UINT uID, ...)
+{
+    va_list valist;
+
+    WCHAR Buf[1024];
+    CHAR AnsiBuf[1024];
+    LPWSTR pBuf = Buf;
+    PCHAR pAnsiBuf = AnsiBuf;
+    WCHAR Format[1024];
+    DWORD written;
+    UINT DataLength;
+    int AnsiLength;
+
+    if (!LoadString(GetModuleHandle(NULL), uID,
+                    Format, sizeof(Format) / sizeof(WCHAR)))
+    {
+        return;
+    }
+
+    va_start(valist, uID);
+
+    DataLength = FormatMessage(FORMAT_MESSAGE_FROM_STRING, Format, 0, 0, Buf,\
+                  sizeof(Buf) / sizeof(WCHAR), &valist);
+
+    if(!DataLength)
+    {
+        if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+        {
+            va_end(valist);
+            return;
+        }
+
+        DataLength = FormatMessage(FORMAT_MESSAGE_FROM_STRING |\
+                                    FORMAT_MESSAGE_ALLOCATE_BUFFER,\
+                                    Format, 0, 0, (LPWSTR)&pBuf, 0, &valist);
+    }
+
+    if(!DataLength)
+    {
+        va_end(valist);
+        return;
+    }
+
+    if(GetFileType(hStdOutput) == FILE_TYPE_CHAR)
+    {
+        /* Is a console or a printer */
+        WriteConsole(hStdOutput, pBuf, DataLength, &written, NULL);
+    }
+    else
+    {
+        /* Is a pipe, socket, file or other */
+        AnsiLength = WideCharToMultiByte(CP_ACP, 0, pBuf, DataLength,\
+                                         NULL, 0, NULL, NULL);
+
+        if(AnsiLength >= sizeof(AnsiBuf))
+            pAnsiBuf = (PCHAR)HeapAlloc(GetProcessHeap(), 0, AnsiLength);
+
+        AnsiLength = WideCharToMultiByte(CP_OEMCP, 0, pBuf, DataLength,\
+                                         pAnsiBuf, AnsiLength, " ", NULL);
+
+        WriteFile(hStdOutput, pAnsiBuf, AnsiLength, &written, NULL);
+
+        if(pAnsiBuf != AnsiBuf)
+            HeapFree(NULL, 0, pAnsiBuf);
+    }
+
+    if(pBuf != Buf)
+        LocalFree(pBuf);
+}
+
+static VOID Usage(VOID)
+{
+    FormatOutput(IDS_USAGE);
+}
+
+static BOOL ParseCmdline(int argc, LPWSTR argv[])
+{
+    INT i;
+
+    if (argc < 3)
+    {
+        Usage();
+        return FALSE;
+    }
+
+    for (i = 1; i < argc; i++)
+    {
+        if (argv[i][0] == L'-' || argv[i][0] == L'/')
+        {
+            switch (argv[i][1])
+            {
+                case L't': NeverStop = TRUE; break;
+                case L'n':
+                    if (i + 1 < argc)
+                    {
+                        PingCount = wcstoul(argv[++i], NULL, 0);
+
+                        if (PingCount == 0)
+                        {
+                            FormatOutput(IDS_BAD_VALUE_OPTION_N, UINT_MAX);
+                            return FALSE;
+                        }
+                    }
+                    else
+                    {
+                        FormatOutput(IDS_BAD_OPTION_FORMAT, argv[i]);
+                        return FALSE;
+                    }
+                    break;
+
+                case L's':
+                    if (SourceName[0] != 0)
+                    {
+                        FormatOutput(IDS_BAD_PARAMETER, argv[i]);
+                        return FALSE;
+                    }
+
+                    if (i + 1 < argc)
+                    {
+                        wcscpy(SourceName, argv[++i]);
+                    }
+                    else
+                    {
+                        FormatOutput(IDS_BAD_OPTION_FORMAT, argv[i]);
+                        return FALSE;
+                    }
+                    break;
+
+                case '?':
+                    Usage();
+                    return FALSE;
+
+                default:
+                    FormatOutput(IDS_BAD_OPTION, argv[i]);
+                    return FALSE;
+            }
+        }
+        else
+        {
+            if (TargetName[0] != 0)
+            {
+                FormatOutput(IDS_BAD_PARAMETER, argv[i]);
+                return FALSE;
+            }
+            else
+            {
+                wcscpy(TargetName, argv[i]);
+            }
+        }
+    }
+
+    if (TargetName[0] == 0)
+    {
+        FormatOutput(IDS_DEST_MUST_BE_SPECIFIED);
+        return FALSE;
+    }
+
+    if (SourceName[0] == 0)
+    {
+        FormatOutput(IDS_SRC_MUST_BE_SPECIFIED);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static BOOL WINAPI StopLoop(DWORD dwCtrlType)
+{
+    NeverStop = FALSE;
+    PingCount = 0;
+
+    return TRUE;
+}
+
+static BOOL Setup(VOID)
+{
+    WORD     wVersionRequested;
+    WSADATA  WsaData;
+    INT      Status;
+    PHOSTENT phe;
+    CHAR     aTargetName[256];
+    IN_ADDR  Target;
+
+    wVersionRequested = MAKEWORD(2, 2);
+
+    Status = WSAStartup(wVersionRequested, &WsaData);
+    if (Status != 0)
+    {
+        FormatOutput(IDS_COULD_NOT_INIT_WINSOCK);
+        return FALSE;
+    }
+
+    if (!WideCharToMultiByte(CP_ACP, 0, TargetName, -1, aTargetName,
+                             sizeof(aTargetName), NULL, NULL))
+    {
+        FormatOutput(IDS_UNKNOWN_HOST, TargetName);
+        return FALSE;
+    }
+
+    phe = NULL;
+    TargetAddr = inet_addr(aTargetName);
+    if (TargetAddr == INADDR_NONE)
+    {
+        phe = gethostbyname(aTargetName);
+        if (phe == NULL)
+        {
+            FormatOutput(IDS_UNKNOWN_HOST, TargetName);
+            return FALSE;
+        }
+
+        CopyMemory(&TargetAddr, phe->h_addr, phe->h_length);
+    }
+
+    Target.S_un.S_addr = TargetAddr;
+    swprintf(TargetIP, L"%d.%d.%d.%d", Target.S_un.S_un_b.s_b1,
+                                       Target.S_un.S_un_b.s_b2,
+                                       Target.S_un.S_un_b.s_b3,
+                                       Target.S_un.S_un_b.s_b4);
+
+    if (!WideCharToMultiByte(CP_ACP, 0, SourceName, -1, aTargetName,
+                             sizeof(aTargetName), NULL, NULL))
+    {
+        FormatOutput(IDS_UNKNOWN_HOST, SourceName);
+        return FALSE;
+    }
+
+    SourceAddr = inet_addr(aTargetName);
+    if (SourceAddr == INADDR_NONE)
+    {
+        FormatOutput(IDS_UNKNOWN_HOST, SourceName);
+        return FALSE;
+    }
+
+    SetConsoleCtrlHandler(StopLoop, TRUE);
+
+    return TRUE;
+}
+
+static VOID Cleanup(VOID)
+{
+    WSACleanup();
+}
+
+static VOID QueryTime(PLARGE_INTEGER Time)
+{
+    if (UsePerformanceCounter)
+    {
+        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;
+
+            /* 1 tick per millisecond for GetCurrentTick() */
+            TicksPerMs.QuadPart = 1;
+            /* GetCurrentTick() cannot handle microseconds */
+            TicksPerUs.QuadPart = 1;
+
+            UsePerformanceCounter = FALSE;
+        }
+    }
+    else
+    {
+        Time->u.LowPart  = (ULONG)GetTickCount();
+        Time->u.HighPart = 0;
+    }
+}
+
+static VOID TimeToMsString(LPWSTR String, ULONG Length, LARGE_INTEGER Time)
+{
+    WCHAR         Convstr[40];
+    LARGE_INTEGER LargeTime;
+    LPWSTR ms;
+
+    LargeTime.QuadPart = Time.QuadPart / TicksPerMs.QuadPart;
+
+    _i64tow(LargeTime.QuadPart, Convstr, 10);
+    wcscpy(String, Convstr);
+    ms = String + wcslen(String);
+    LoadString(GetModuleHandle(NULL), IDS_MS, ms, Length - (ms - String));
+}
+
+static BOOL Ping(VOID)
+{
+    LARGE_INTEGER RelativeTime;
+    LARGE_INTEGER LargeTime;
+    LARGE_INTEGER SentTime;
+    DWORD Ret;
+    BYTE TargetHW[6];
+    ULONG Size;
+    WCHAR Sign[2];
+    WCHAR Time[100];
+    WCHAR StrHwAddr[18];
+
+    QueryTime(&SentTime);
+    Size = sizeof(TargetHW);
+    memset(TargetHW, 0xff, Size);
+    ++Sent;
+    Ret = SendARP(TargetAddr, SourceAddr, (PULONG)TargetHW, &Size);
+    if (Ret == ERROR_SUCCESS)
+    {
+        QueryTime(&LargeTime);
+
+        RelativeTime.QuadPart = (LargeTime.QuadPart - SentTime.QuadPart);
+
+        if ((RelativeTime.QuadPart / TicksPerMs.QuadPart) < 1)
+        {
+            wcscpy(Sign, L"<");
+            LoadString(GetModuleHandle(NULL), IDS_1MS, Time, sizeof(Time) / sizeof(WCHAR));
+        }
+        else
+        {
+            wcscpy(Sign, L"=");
+            TimeToMsString(Time, sizeof(Time) / sizeof(WCHAR), RelativeTime);
+        }
+
+        swprintf(StrHwAddr, L"%02x:%02x:%02x:%02x:%02x:%02x", TargetHW[0], TargetHW[1],
+                                                              TargetHW[2], TargetHW[3],
+                                                              TargetHW[4], TargetHW[5]);
+        FormatOutput(IDS_REPLY_FROM, TargetIP, StrHwAddr, Sign, Time);
+        Received++;
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+int wmain(int argc, LPWSTR argv[])
+{
+    UINT Count;
+    LARGE_INTEGER PerformanceCounterFrequency;
+
+    PingCount = 4;
+    Timeout = 1000;
+    hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+
+    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;
+    }
+
+    if (!ParseCmdline(argc, argv) || !Setup())
+    {
+        return 1;
+    }
+
+    FormatOutput(IDS_ARPING_TO_FROM, TargetIP, SourceName);
+
+    Count = 0;
+    while (Count < PingCount || NeverStop)
+    {
+        Ping();
+        Count++;
+        if (Count < PingCount || NeverStop)
+            Sleep(Timeout);
+    }
+
+    Cleanup();
+
+    FormatOutput(IDS_ARPING_STATISTICS, Sent, Received);
+
+    return 0;
+}
diff --git a/rosapps/applications/cmdutils/arping/arping.rc b/rosapps/applications/cmdutils/arping/arping.rc
new file mode 100644 (file)
index 0000000..d5ab59c
--- /dev/null
@@ -0,0 +1,15 @@
+#include <windef.h>
+
+#include "resource.h"
+
+#define REACTOS_STR_FILE_DESCRIPTION   "ReactOS TCP/IPv4 Win32 ARP Ping"
+#define REACTOS_STR_INTERNAL_NAME      "arpping"
+#define REACTOS_STR_ORIGINAL_FILENAME  "arpping.exe"
+#include <reactos/version.rc>
+
+/* UTF-8 */
+#pragma code_page(65001)
+
+#ifdef LANGUAGE_EN_US
+    #include "lang/en-US.rc"
+#endif
diff --git a/rosapps/applications/cmdutils/arping/lang/en-US.rc b/rosapps/applications/cmdutils/arping/lang/en-US.rc
new file mode 100644 (file)
index 0000000..d709db2
--- /dev/null
@@ -0,0 +1,25 @@
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+STRINGTABLE
+BEGIN
+    IDS_USAGE "\nUsage: arping [-t] [-n count] -s source-ip destination-host\n\n\
+Options:\n\
+    -t             Ping the specified host until stopped.\n\
+                   To stop - type Control-C.\n\
+    -n count       Number of probes to send.\n\
+    -s             Use this source IP.\n\n\0"
+    IDS_BAD_OPTION_FORMAT "Bad option format %1.\n\0"
+    IDS_BAD_OPTION "Bad option %1.\n\0"
+    IDS_BAD_PARAMETER "Bad parameter %1.\n\0"
+    IDS_DEST_MUST_BE_SPECIFIED "Name or IP address of destination host must be specified.\n\0"
+    IDS_COULD_NOT_INIT_WINSOCK "Could not initialize winsock dll.\n\0"
+    IDS_UNKNOWN_HOST "Unknown host %1.\n\0"
+    IDS_SRC_MUST_BE_SPECIFIED "IP address of source host must be specified.\n\0"
+    IDS_BAD_VALUE_OPTION_N "Bad value for option -n, valid range is from 1 to %1!u!.\n\0"
+    IDS_ARPING_TO_FROM "ARPING %1 from %2\n\0"
+    IDS_MS "ms\0"
+    IDS_1MS "1ms\0"
+    IDS_REPLY_FROM "Unicast reply from %1 [%2]  %3%4\n\0"
+    IDS_ARPING_STATISTICS "Sent %1!u! probes\n\
+Received %2!u! response(s)\n\0"
+END
diff --git a/rosapps/applications/cmdutils/arping/resource.h b/rosapps/applications/cmdutils/arping/resource.h
new file mode 100644 (file)
index 0000000..e09ab04
--- /dev/null
@@ -0,0 +1,16 @@
+#pragma once
+
+#define IDS_USAGE                      0
+#define IDS_BAD_OPTION_FORMAT          1
+#define IDS_BAD_OPTION                 2
+#define IDS_BAD_PARAMETER              3
+#define IDS_DEST_MUST_BE_SPECIFIED     4
+#define IDS_COULD_NOT_INIT_WINSOCK     5
+#define IDS_UNKNOWN_HOST               6
+#define IDS_SRC_MUST_BE_SPECIFIED      7
+#define IDS_BAD_VALUE_OPTION_N         8
+#define IDS_ARPING_TO_FROM             9
+#define IDS_MS                         10
+#define IDS_1MS                        11
+#define IDS_REPLY_FROM                 12
+#define IDS_ARPING_STATISTICS          13