https://git.reactos.org/?p=reactos.git;a=commitdiff;h=dd2ff41dfcf08341182273309205666a1a82c31a

commit dd2ff41dfcf08341182273309205666a1a82c31a
Author:     Victor Perevertkin <victor.perevert...@reactos.org>
AuthorDate: Mon Nov 18 20:54:03 2019 +0300
Commit:     Victor Perevertkin <vic...@perevertkin.ru>
CommitDate: Tue Apr 7 05:32:40 2020 +0300

    [IPHLPAPI] Make icmp functions use IOCTL_ICMP_ECHO_REQUEST from tcpip.sys
    This adds missing features like using events and APCs within IcmpSendEcho2
    functions and others.
    CORE-10742 CORE-14411
    
    Co-authored-by: Tim Crawford <crawf...@gmail.com>
---
 dll/win32/iphlpapi/icmp.c        | 1423 +++++++++++---------------------------
 dll/win32/iphlpapi/iphlpapi.spec |    4 +-
 sdk/include/psdk/ipexport.h      |   51 +-
 sdk/include/psdk/tcpioctl.h      |    3 +
 4 files changed, 464 insertions(+), 1017 deletions(-)

diff --git a/dll/win32/iphlpapi/icmp.c b/dll/win32/iphlpapi/icmp.c
index dec33c13ca9..b32aeca8876 100644
--- a/dll/win32/iphlpapi/icmp.c
+++ b/dll/win32/iphlpapi/icmp.c
@@ -1,1129 +1,552 @@
 /*
- * ICMP
- *
- * Francois Gouget, 1999, based on the work of
- *   RW Hall, 1999, based on public domain code PING.C by Mike Muus (1983)
- *   and later works (c) 1989 Regents of Univ. of California - see copyright
- *   notice at end of source-code.
- * Copyright 2015 Sebastian Lackner
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ * PROJECT:         ReactOS IP Helper API
+ * LICENSE:         LGPL-2.1-or-later 
(https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE:         ICMP functions
+ * COPYRIGHT:       2016 Tim Crawford (crawf...@gmail.com)
+ *                  2019 Victor Perevertkin (victor.perevert...@reactos.org)
  */
 
-/* Future work:
- * - Systems like FreeBSD don't seem to support the IP_TTL option and maybe 
others.
- *   But using IP_HDRINCL and building the IP header by hand might work.
- * - Not all IP options are supported.
- * - Are ICMP handles real handles, i.e. inheritable and all? There might be 
some
- *   more work to do here, including server side stuff with synchronization.
- * - This API should probably be thread safe. Is it really?
- * - Using the winsock functions has not been tested.
- */
-
-#ifdef __REACTOS__
 #include "iphlpapi_private.h"
-#else // ! __REACTOS__
-#include "config.h"
-#include "wine/port.h"
-
-#include <sys/types.h>
-#ifdef HAVE_SYS_SOCKET_H
-# include <sys/socket.h>
-#endif
-#ifdef HAVE_NETDB_H
-# include <netdb.h>
-#endif
-#ifdef HAVE_NETINET_IN_SYSTM_H
-# include <netinet/in_systm.h>
-#endif
-#ifdef HAVE_NETINET_IN_H
-# include <netinet/in.h>
-#endif
-
-#ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-#include <stdarg.h>
-#include <string.h>
-#include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
-#ifdef HAVE_ARPA_INET_H
-# include <arpa/inet.h>
-#endif
-#ifdef HAVE_SYS_POLL_H
-# include <sys/poll.h>
-#endif
-#ifdef HAVE_SYS_WAIT_H
-# include <sys/wait.h>
-#endif
-
-#define USE_WS_PREFIX
-
-#include "windef.h"
-#include "winbase.h"
-#include "winerror.h"
-#include "winternl.h"
-#include "ipexport.h"
-#include "icmpapi.h"
-#include "wine/debug.h"
-#endif // ! __REACTOS__
-
-/* Set up endianness macros for the ip and ip_icmp BSD headers */
-#ifndef BIG_ENDIAN
-#define BIG_ENDIAN       4321
-#endif
-#ifndef LITTLE_ENDIAN
-#define LITTLE_ENDIAN    1234
-#endif
-#ifndef BYTE_ORDER
-#ifdef WORDS_BIGENDIAN
-#define BYTE_ORDER       BIG_ENDIAN
-#else
-#define BYTE_ORDER       LITTLE_ENDIAN
-#endif
-#endif /* BYTE_ORDER */
-
-#define u_int16_t  WORD
-#define u_int32_t  DWORD
-
-/* These are BSD headers. We use these here because they are needed on
- * libc5 Linux systems. On other platforms they are usually simply more
- * complete than the native stuff, and cause less portability problems
- * so we use them anyway.
- */
-#include "ip.h"
-#include "ip_icmp.h"
 
+WINE_DEFAULT_DEBUG_CHANNEL(iphlpapi);
 
-WINE_DEFAULT_DEBUG_CHANNEL(icmp);
-WINE_DECLARE_DEBUG_CHANNEL(winediag);
-
+HANDLE
+WINAPI
+Icmp6CreateFile(void)
+{
+    HANDLE IcmpFile;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatusBlock;
+    UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\Ip6");
+    NTSTATUS Status;
+
+    InitializeObjectAttributes(
+        &ObjectAttributes,
+        &DeviceName,
+        OBJ_CASE_INSENSITIVE,
+        NULL,
+        NULL);
+
+    Status = NtCreateFile(
+        &IcmpFile,
+        GENERIC_EXECUTE,
+        &ObjectAttributes,
+        &IoStatusBlock,
+        NULL,
+        FILE_ATTRIBUTE_NORMAL,
+        FILE_SHARE_READ | FILE_SHARE_WRITE,
+        FILE_OPEN_IF,
+        0,
+        NULL,
+        0);
+
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastError(RtlNtStatusToDosError(Status));
+        return INVALID_HANDLE_VALUE;
+    }
 
-typedef struct {
-    int sid;
-    IP_OPTION_INFORMATION default_opts;
-} icmp_t;
+    return IcmpFile;
+}
 
-#define IP_OPTS_UNKNOWN     0
-#define IP_OPTS_DEFAULT     1
-#define IP_OPTS_CUSTOM      2
+DWORD
+WINAPI
+Icmp6ParseReplies(
+    _In_ LPVOID ReplyBuffer,
+    _In_ DWORD  ReplySize)
+{
+    PICMPV6_ECHO_REPLY pEcho;
 
-/* The sequence number is unique process wide, so that all threads
- * have a distinct sequence number.
- */
-static LONG icmp_sequence=0;
+    if (ReplyBuffer == NULL || ReplySize == 0)
+        return 0;
 
-static int in_cksum(u_short *addr, int len)
-{
-    int nleft=len;
-    u_short *w = addr;
-    int sum = 0;
-    u_short answer = 0;
-
-    while (nleft > 1) {
-        sum += *w++;
-        nleft -= 2;
-    }
+    pEcho = (PICMPV6_ECHO_REPLY)ReplyBuffer;
 
-    if (nleft == 1) {
-        *(u_char *)(&answer) = *(u_char *)w;
-        sum += answer;
+    // XXX: MSDN also says IP_TTL_EXPIRED_TRANSIT.
+    if (pEcho->Status == IP_SUCCESS)
+    {
+        return 1;
     }
 
-    sum = (sum >> 16) + (sum & 0xffff);
-    sum  += (sum >> 16);
-    answer = ~sum;
-    return(answer);
+    SetLastError(pEcho->Status);
+    return 0;
 }
 
-
-
-/*
- * Exported Routines.
- */
-
-/***********************************************************************
- *             Icmp6CreateFile (IPHLPAPI.@)
- */
-HANDLE WINAPI Icmp6CreateFile(VOID)
+DWORD
+WINAPI
+Icmp6SendEcho2(
+    _In_     HANDLE                 IcmpHandle,
+    _In_opt_ HANDLE                 Event,
+    _In_opt_ PIO_APC_ROUTINE        ApcRoutine,
+    _In_opt_ PVOID                  ApcContext,
+    _In_     struct sockaddr_in6    *SourceAddress,
+    _In_     struct sockaddr_in6    *DestinationAddress,
+    _In_     LPVOID                 RequestData,
+    _In_     WORD                   RequestSize,
+    _In_     PIP_OPTION_INFORMATION RequestOptions,
+    _Out_    LPVOID                 ReplyBuffer,
+    _In_     DWORD                  ReplySize,
+    _In_     DWORD                  Timeout)
 {
-    icmp_t* icp;
-#ifdef __REACTOS__
-    int sid;
-    WSADATA wsaData;
+    HANDLE hEvent;
+    PIO_STATUS_BLOCK IoStatusBlock;
+    PVOID InputBuffer;
+    ULONG InputBufferLength;
+    //ULONG OutputBufferLength;
+    PICMPV6_ECHO_REQUEST Request;
+    NTSTATUS Status;
+
+    InputBufferLength = sizeof(ICMPV6_ECHO_REQUEST) + RequestSize;
 
-    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != ERROR_SUCCESS)
+    if (ReplySize < sizeof(ICMPV6_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK))
     {
-        ERR_(winediag)("Failed to use ICMPV6 (network ping), this requires 
special permissions.\n");
-        return INVALID_HANDLE_VALUE;
+        SetLastError(IP_BUF_TOO_SMALL);
+        return 0;
     }
 
-    sid=socket(AF_INET6,SOCK_RAW,IPPROTO_ICMPV6);
-#else
+    // IO_STATUS_BLOCK will be stored inside ReplyBuffer (in the end)
+    // that's because the function may return before device request ends
+    IoStatusBlock = (PIO_STATUS_BLOCK)((PUCHAR)ReplyBuffer + ReplySize - 
sizeof(IO_STATUS_BLOCK));
+    ReplySize -= sizeof(IO_STATUS_BLOCK);
 
-    int sid=socket(AF_INET6,SOCK_RAW,IPPROTO_ICMPV6);
-    if (sid < 0)
+    InputBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 
InputBufferLength);
+    if (InputBuffer == NULL)
     {
-        /* Mac OS X supports non-privileged ICMP via SOCK_DGRAM type. */
-        sid=socket(AF_INET6,SOCK_DGRAM,IPPROTO_ICMPV6);
-    }
-#endif
-    if (sid < 0) {
-        ERR_(winediag)("Failed to use ICMPV6 (network ping), this requires 
special permissions.\n");
-        SetLastError(ERROR_ACCESS_DENIED);
-        return INVALID_HANDLE_VALUE;
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return 0;
     }
 
-    icp=HeapAlloc(GetProcessHeap(), 0, sizeof(*icp));
-    if (icp==NULL) {
-#ifdef __REACTOS__
-        closesocket(sid);
-        WSACleanup();
-#else
-        close(sid);
-#endif
-        SetLastError(IP_NO_RESOURCES);
-        return INVALID_HANDLE_VALUE;
-    }
-    icp->sid=sid;
-    icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN;
-    return (HANDLE)icp;
-}
+    Request = (PICMPV6_ECHO_REQUEST)InputBuffer;
 
+    Request->DestinationAddress.sin6_port = DestinationAddress->sin6_port;
+    Request->DestinationAddress.sin6_flowinfo = 
DestinationAddress->sin6_flowinfo;
+    CopyMemory(&(Request->DestinationAddress.sin6_addr), 
&(DestinationAddress->sin6_addr), 
sizeof(Request->DestinationAddress.sin6_addr));
+    Request->DestinationAddress.sin6_scope_id = 
DestinationAddress->sin6_scope_id;
 
-/***********************************************************************
- *             Icmp6SendEcho2 (IPHLPAPI.@)
- */
-DWORD WINAPI Icmp6SendEcho2(
-    HANDLE                   IcmpHandle,
-    HANDLE                   Event,
-    PIO_APC_ROUTINE          ApcRoutine,
-    PVOID                    ApcContext,
-    struct sockaddr_in6*     SourceAddress,
-    struct sockaddr_in6*     DestinationAddress,
-    LPVOID                   RequestData,
-    WORD                     RequestSize,
-    PIP_OPTION_INFORMATION   RequestOptions,
-    LPVOID                   ReplyBuffer,
-    DWORD                    ReplySize,
-    DWORD                    Timeout
-    )
-{
-    FIXME("(%p, %p, %p, %p, %p, %p, %p, %d, %p, %p, %d, %d): stub\n", 
IcmpHandle, Event,
-            ApcRoutine, ApcContext, SourceAddress, DestinationAddress, 
RequestData,
-            RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout);
-    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
-    return 0;
-}
+    Request->SourceAddress.sin6_port = SourceAddress->sin6_port;
+    Request->SourceAddress.sin6_flowinfo = SourceAddress->sin6_flowinfo;
+    CopyMemory(&(Request->SourceAddress.sin6_addr), 
&(SourceAddress->sin6_addr), sizeof(Request->SourceAddress.sin6_addr));
+    Request->SourceAddress.sin6_scope_id = SourceAddress->sin6_scope_id;
 
+    // XXX: What is this and why is it sometimes 0x72?
+    Request->Unknown1 = 0x72;
 
-/***********************************************************************
- *             IcmpCreateFile (IPHLPAPI.@)
- */
-HANDLE WINAPI IcmpCreateFile(VOID)
-{
-#ifndef __REACTOS__
-    static int once;
-#endif
-    icmp_t* icp;
-#ifdef __REACTOS__
-    int sid;
-    WSADATA wsaData;
-
-    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != ERROR_SUCCESS)
+    Request->Timeout = Timeout;
+    Request->Ttl = RequestOptions->Ttl;
+    Request->Flags = RequestOptions->Flags;
+
+    if (RequestSize > 0)
     {
-        ERR_(winediag)("Failed to use ICMPV6 (network ping), this requires 
special permissions.\n");
-        return INVALID_HANDLE_VALUE;
+        CopyMemory((PBYTE)InputBuffer + sizeof(ICMPV6_ECHO_REQUEST), 
RequestData, RequestSize);
     }
-    sid=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
-    if (sid < 0) {
-        ERR_(winediag)("Failed to use ICMP (network ping), this requires 
special permissions.\n");
-        SetLastError(ERROR_ACCESS_DENIED);
-        return INVALID_HANDLE_VALUE;
+
+    if (Event == NULL && ApcRoutine == NULL)
+    {
+        hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    }
+    else
+    {
+        hEvent = Event;
+    }
+
+    Status = NtDeviceIoControlFile(
+        IcmpHandle,
+        hEvent,
+        ApcRoutine,
+        ApcContext,
+        IoStatusBlock,
+        IOCTL_ICMP_ECHO_REQUEST,
+        InputBuffer,
+        InputBufferLength,
+        ReplyBuffer,
+        ReplySize);         // TODO: Determine how Windows calculates 
OutputBufferLength.
+
+    if (Event != NULL || ApcRoutine != NULL)
+    {
+        SetLastError(RtlNtStatusToDosError(Status));
+        HeapFree(GetProcessHeap(), 0, InputBuffer);
+        return 0;
     }
-#else
 
-    int sid=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
-    if (sid < 0)
+    if (Status == STATUS_PENDING)
     {
-        /* Mac OS X supports non-privileged ICMP via SOCK_DGRAM type. */
-        sid=socket(AF_INET,SOCK_DGRAM,IPPROTO_ICMP);
+        Status = NtWaitForSingleObject(hEvent, FALSE, NULL);
+
+        if (NT_SUCCESS(Status))
+        {
+            Status = IoStatusBlock->Status;
+        }
     }
-    if (sid < 0 && !once++) {
-        FIXME_(winediag)("Failed to use ICMP (network ping), this requires 
special permissions.\n");
-        FIXME_(winediag)("Falling back to system 'ping' command as a 
workaround.\n");
+
+    CloseHandle(hEvent);
+    HeapFree(GetProcessHeap(), 0, InputBuffer);
+
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastError(RtlNtStatusToDosError(Status));
+        return 0;
     }
-#endif
-
-    icp=HeapAlloc(GetProcessHeap(), 0, sizeof(*icp));
-    if (icp==NULL) {
-#ifdef __REACTOS__
-        closesocket(sid);
-        WSACleanup();
-#else
-        if (sid >= 0) close(sid);
-#endif
-        SetLastError(IP_NO_RESOURCES);
-        return INVALID_HANDLE_VALUE;
+
+    Status = ((PICMPV6_ECHO_REPLY)ReplyBuffer)->Status;
+    if (Status != IP_SUCCESS)
+    {
+        SetLastError(Status);
+        return 0;
     }
-    icp->sid=sid;
-    icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN;
-    return (HANDLE)icp;
-}
 
+    return 1;
+}
 
-/***********************************************************************
- *             IcmpCloseHandle (IPHLPAPI.@)
- */
-BOOL WINAPI IcmpCloseHandle(HANDLE  IcmpHandle)
+BOOL
+WINAPI
+IcmpCloseHandle(
+    _In_ HANDLE IcmpHandle)
 {
-    icmp_t* icp=(icmp_t*)IcmpHandle;
-#ifdef __REACTOS__
-    // REACTOS: Added a check for NULL handle, CORE-10707
-    if (IcmpHandle==INVALID_HANDLE_VALUE || IcmpHandle==NULL) {
-#else
-    if (IcmpHandle==INVALID_HANDLE_VALUE) {
-#endif
-        /* FIXME: in fact win98 seems to ignore the handle value !!! */
-        SetLastError(ERROR_INVALID_HANDLE);
+    NTSTATUS Status;
+
+    Status = NtClose(IcmpHandle);
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastError(RtlNtStatusToDosError(Status));
         return FALSE;
     }
 
-#ifdef __REACTOS__
-    shutdown(icp->sid,2);
-#else
-    if (icp->sid >= 0) close(icp->sid);
-#endif
-    HeapFree(GetProcessHeap (), 0, icp);
-#ifdef __REACTOS__
-    WSACleanup();
-#endif
     return TRUE;
 }
 
-#ifndef __REACTOS__
-static DWORD system_icmp(
-    IPAddr                   DestinationAddress,
-    LPVOID                   RequestData,
-    WORD                     RequestSize,
-    PIP_OPTION_INFORMATION   RequestOptions,
-    LPVOID                   ReplyBuffer,
-    DWORD                    ReplySize,
-    DWORD                    Timeout
-    )
+HANDLE
+WINAPI
+IcmpCreateFile(void)
 {
-#ifdef HAVE_FORK
-    ICMP_ECHO_REPLY *reply = ReplyBuffer;
-    char ntoa_buffer[16]; /* 4*3 digits + 3 '.' + 1 '\0' */
-    char size_buffer[6];  /* 5 digits + '\0' */
-    char tos_buffer[4];   /* 3 digits + '\0' */
-    char ttl_buffer[4];   /* 3 digits + '\0' */
-    char time_buffer[11]; /* 10 digits + '\0' */
-    int i, pos, res, status, argc;
-    const char *argv[20];
-    struct in_addr addr;
-    int pipe_out[2];
-    pid_t pid, wpid;
-    char *ptr, *eol;
-    char buf[127];
-
-    /* Assemble the ping commandline */
-    argc = 0;
-    argv[argc++] = "ping";
-    argv[argc++] = "-c";    /* only send a single ping */
-    argv[argc++] = "1";
-    argv[argc++] = "-n";    /* numeric output only */
-    argv[argc++] = "-s";    /* request size */
-    sprintf(size_buffer, "%u", (RequestSize >= 16) ? RequestSize : 16);
-    argv[argc++] = size_buffer;
-    argv[argc++] = "-W";    /* timeout */
-#ifdef __linux__
-    /* The linux 'ping' utlity expects a time in seconds */
-    Timeout = (Timeout + 999) / 1000;
-#endif
-    sprintf(time_buffer, "%u", Timeout);
-    argv[argc++] = time_buffer;
-
-    if (RequestOptions)
+    HANDLE IcmpFile;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatusBlock;
+    UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\Ip");
+    NTSTATUS Status;
+
+    InitializeObjectAttributes(
+        &ObjectAttributes,
+        &DeviceName,
+        OBJ_CASE_INSENSITIVE,
+        NULL,
+        NULL);
+
+    Status = NtCreateFile(
+        &IcmpFile,
+        GENERIC_EXECUTE,
+        &ObjectAttributes,
+        &IoStatusBlock,
+        NULL,
+        FILE_ATTRIBUTE_NORMAL,
+        FILE_SHARE_READ | FILE_SHARE_WRITE,
+        FILE_OPEN_IF,
+        0,
+        NULL,
+        0);
+
+    if (!NT_SUCCESS(Status))
     {
-    #ifdef __linux__
-        argv[argc++] = "-Q";    /* tos option */
-    #else
-        argv[argc++] = "-z";    /* tos option */
-    #endif
-        sprintf(tos_buffer, "%u", RequestOptions->Tos);
-        argv[argc++] = tos_buffer;
-    #ifdef __linux__
-        /* TTL can only be specified for multicast addresses on FreeBSD/MacOS 
*/
-        argv[argc++] = "-t";    /* ttl option */
-        sprintf(ttl_buffer, "%u", RequestOptions->Ttl);
-        argv[argc++] = ttl_buffer;
-    #endif
+        SetLastError(RtlNtStatusToDosError(Status));
+        return INVALID_HANDLE_VALUE;
     }
 
-    addr.s_addr = DestinationAddress;
-    if (!(ptr = inet_ntoa(addr)))
-    {
-        SetLastError(ERROR_INVALID_PARAMETER);
+    return IcmpFile;
+}
+
+DWORD
+WINAPI
+IcmpParseReplies(
+    _In_ LPVOID ReplyBuffer,
+    _In_ DWORD  ReplySize)
+{
+    PICMP_ECHO_REPLY pEcho;
+    DWORD nReplies;
+
+    if (ReplyBuffer == NULL || ReplySize == 0)
         return 0;
-    }
-    strcpy(ntoa_buffer, ptr);
-    argv[argc++] = ntoa_buffer;
-    argv[argc] = NULL;
 
-    /* Dump commandline for debugging purposes */
-    TRACE("Ping commandline: ");
-    for (i = 0; i < argc; i++)
-    {
-        TRACE("%s ", debugstr_a(argv[i]));
-    }
-    TRACE("\n");
-
-    /* Prefill the reply struct with fallback values */
-    memset(reply, 0, sizeof(*reply));
-    reply->Address       = DestinationAddress;
-    reply->RoundTripTime = 40;
-    reply->Options.Ttl   = 120;
-
-    /* Create communication pipes */
-#ifdef HAVE_PIPE2
-    if (pipe2(pipe_out, O_CLOEXEC) < 0)
-#endif
+    // TODO: Handle ReplyBuffer having more than 1 ICMP_ECHO_REPLY.
+
+    pEcho = (PICMP_ECHO_REPLY)ReplyBuffer;
+
+    if (pEcho->Reserved == 0)
     {
-        if (pipe(pipe_out) < 0)
-        {
-            SetLastError(ERROR_OUTOFMEMORY);
-            return 0;
-        }
-        fcntl(pipe_out[0], F_SETFD, FD_CLOEXEC);
-        fcntl(pipe_out[1], F_SETFD, FD_CLOEXEC);
+        SetLastError(pEcho->Status);
     }
 
-    /* Fork child process */
-    pid = fork();
-    if (pid == -1)
+    nReplies = pEcho->Reserved;
+    pEcho->Reserved = 0;
+
+    return nReplies;
+}
+
+DWORD
+WINAPI
+IcmpSendEcho2(
+    _In_     HANDLE                 IcmpHandle,
+    _In_opt_ HANDLE                 Event,
+    _In_opt_ PIO_APC_ROUTINE        ApcRoutine,
+    _In_opt_ PVOID                  ApcContext,
+    _In_     IPAddr                 DestinationAddress,
+    _In_     LPVOID                 RequestData,
+    _In_     WORD                   RequestSize,
+    _In_opt_ PIP_OPTION_INFORMATION RequestOptions,
+    _Out_    LPVOID                 ReplyBuffer,
+    _In_     DWORD                  ReplySize,
+    _In_     DWORD                  Timeout)
+{
+    HANDLE hEvent;
+    PIO_STATUS_BLOCK IoStatusBlock;
+    PVOID InputBuffer;
+    PICMP_ECHO_REQUEST Request;
+    DWORD nReplies;
+    NTSTATUS Status;
+
+    if (ReplySize < sizeof(ICMP_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK))
     {
-        close(pipe_out[0]);
-        close(pipe_out[1]);
-        SetLastError(ERROR_OUTOFMEMORY);
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
         return 0;
     }
 
-    /* Child process */
-    if (pid == 0)
+    if (ReplySize < RequestSize + sizeof(ICMP_ECHO_REPLY))
     {
-        static char lang_env[] = "LANG=C";
+        SetLastError(IP_GENERAL_FAILURE);
+        return 0;
+    }
 
-        dup2(pipe_out[1], 1);
-        close(pipe_out[0]);
-        close(pipe_out[1]);
-        close(0);
-        close(2);
+    // IO_STATUS_BLOCK will be stored inside ReplyBuffer (in the end)
+    // that's because the function may return before device request ends
+    IoStatusBlock = (PIO_STATUS_BLOCK)((PUCHAR)ReplyBuffer + ReplySize - 
sizeof(IO_STATUS_BLOCK));
+    ReplySize -= sizeof(IO_STATUS_BLOCK);
 
-        putenv(lang_env);
-        execvp(argv[0], (char **)argv);
-        _exit(1);
+    InputBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ReplySize);
+    if (InputBuffer == NULL)
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return 0;
     }
 
-    close(pipe_out[1]);
+    Request = (PICMP_ECHO_REQUEST)InputBuffer;
+    Request->Address = DestinationAddress;
+    Request->Timeout = Timeout;
+    Request->OptionsOffset = sizeof(ICMP_ECHO_REQUEST);
+    Request->DataOffset = sizeof(ICMP_ECHO_REQUEST);
 
-    /* Wait for child and read output */
-    pos = 0;
-    do
+    if (RequestOptions != NULL)
     {
-        if (pos >= sizeof(buf) - 1)
-        {
-            ERR("line too long, dropping buffer\n");
-            pos = 0;
-        }
+        Request->HasOptions = TRUE;
+        Request->Ttl = RequestOptions->Ttl;
+        Request->Tos = RequestOptions->Tos;
+        Request->Flags = RequestOptions->Flags;
 
-        /* read next block */
-        do
+        if (RequestOptions->OptionsSize > 0)
         {
-            res = read(pipe_out[0], &buf[pos], (sizeof(buf) - 1) - pos);
-        }
-        while (res < 0 && errno == EINTR);
-        if (res < 0)
-        {
-            ERR("read failed: %s\n", strerror(errno));
-            break;
-        }
+            Request->OptionsSize = RequestOptions->OptionsSize;
+            Request->DataOffset += Request->OptionsSize;
 
-        pos += res;
-        while (pos)
-        {
-            eol = memchr(buf, '\n', pos);
-            if (!eol) break;
-            *eol = 0;
-
-            TRACE("Received line: %s\n", debugstr_a(buf));
-
-            /* Interpret address */
-            if ((ptr = strstr(buf, "from ")))
-            {
-                int a, b, c, d;
-                if (sscanf(ptr + 5, "%u.%u.%u.%u", &a, &b, &c, &d) >= 4)
-                {
-                    reply->Address = a | (b << 8) | (c << 16) | (d << 24);
-                    addr.s_addr = reply->Address;
-                    TRACE("Got address %s\n", inet_ntoa(addr));
-                }
-            }
-
-            /* Interpret ttl */
-            if ((ptr = strstr(buf, "ttl=")))
-            {
-                int val;
-                if (sscanf(ptr + 4, "%u", &val) >= 1)
-                {
-                    reply->Options.Ttl = val;
-                    TRACE("Got ttl %u\n", val);
-                }
-            }
-
-            /* Interpret time */
-            if ((ptr = strstr(buf, "time=")))
-            {
-                float val;
-                if (sscanf(ptr + 5, "%f", &val) >= 1)
-                {
-                    reply->RoundTripTime = (unsigned int)(val + 0.5);
-                    TRACE("Got rtt = %u\n", reply->RoundTripTime);
-                }
-            }
-
-            memmove(buf, eol + 1, pos - (eol + 1 - buf));
-            pos -= (eol + 1 - buf);
+            CopyMemory(
+                (PUCHAR)InputBuffer + sizeof(ICMP_ECHO_REQUEST),
+                RequestOptions->OptionsData,
+                Request->OptionsSize);
         }
     }
-    while (res > 0);
-    close(pipe_out[0]);
 
-    /* reap the child process */
-    do
+    if (RequestSize > 0)
     {
-        wpid = waitpid(pid, &status, 0);
+        Request->DataSize = RequestSize;
+        CopyMemory((PUCHAR)InputBuffer + Request->DataOffset, RequestData, 
RequestSize);
     }
-    while (wpid < 0 && errno == EINTR);
 
-    /* fill out remaining struct fields */
-    if (wpid >= 0 && WIFEXITED(status) && WEXITSTATUS(status) == 0)
+    if (Event == NULL && ApcRoutine == NULL)
     {
-        if (ReplySize < RequestSize + sizeof(*reply))
-        {
-            reply->Status   = IP_BUF_TOO_SMALL;
-            reply->DataSize = 0;
-            reply->Data     = NULL;
-        }
-        else
-        {
-            reply->Status   = 0;
-            reply->DataSize = RequestSize;
-            reply->Data     = (char *)reply + sizeof(*reply);
-            memcpy(reply->Data, RequestData, RequestSize);
-        }
-        return 1;
+        hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
     }
-
-    SetLastError(IP_REQ_TIMED_OUT);
-    return 0;
-#else
-    ERR("no fork support on this platform\n");
-    SetLastError(ERROR_NOT_SUPPORTED);
-    return 0;
-#endif
-}
-#else // __REACTOS__
-BOOL
-GetIPv4ByIndex(
-    _In_  DWORD              Index,
-    _Out_ IPAddr *           Address
-)
-{
-    PMIB_IPADDRTABLE pIpAddrTable;
-    ULONG dwSize = 0;
-    BOOL result = FALSE;
-
-    if (GetIpAddrTable(NULL, &dwSize, FALSE) != ERROR_INSUFFICIENT_BUFFER)
+    else
+    {
+        hEvent = Event;
+    }
+
+    Status = NtDeviceIoControlFile(
+        IcmpHandle,
+        hEvent,
+        ApcRoutine,
+        ApcContext,
+        IoStatusBlock,
+        IOCTL_ICMP_ECHO_REQUEST,
+        InputBuffer,
+        ReplySize,
+        ReplyBuffer,
+        ReplySize);         // TODO: Determine how Windows calculates 
OutputBufferLength.
+
+    // If called asynchronously, return for the caller to handle.
+    if (Event != NULL || ApcRoutine != NULL)
     {
-        return result;
+        SetLastError(RtlNtStatusToDosError(Status));
+        HeapFree(GetProcessHeap(), 0, InputBuffer);
+        return 0;
     }
-    pIpAddrTable = HeapAlloc(GetProcessHeap(), 0, dwSize);
 
-    if (GetIpAddrTable(pIpAddrTable, &dwSize, FALSE) == NO_ERROR)
+    // Otherwise handle it like IcmpSendEcho.
+    if (Status == STATUS_PENDING)
     {
-        INT i;
+        Status = NtWaitForSingleObject(hEvent, FALSE, NULL);
 
-        for (i = 0; i < (*pIpAddrTable).dwNumEntries; i++)
+        if (NT_SUCCESS(Status))
         {
-            if ((*pIpAddrTable).table[i].dwIndex == Index)
-            {
-                *Address = (IPAddr)(*pIpAddrTable).table[i].dwAddr;
-                result = TRUE;
-                break;
-            }
+            Status = IoStatusBlock->Status;
         }
     }
-    HeapFree(GetProcessHeap(), 0, pIpAddrTable);
-    return result;
-}
-#endif // __REACTOS__
 
-/***********************************************************************
- *             IcmpSendEcho (IPHLPAPI.@)
- */
-DWORD WINAPI IcmpSendEcho(
-    HANDLE                   IcmpHandle,
-    IPAddr                   DestinationAddress,
-    LPVOID                   RequestData,
-    WORD                     RequestSize,
-    PIP_OPTION_INFORMATION   RequestOptions,
-    LPVOID                   ReplyBuffer,
-    DWORD                    ReplySize,
-    DWORD                    Timeout
-    )
-{
-    icmp_t* icp=(icmp_t*)IcmpHandle;
-    unsigned char* reqbuf;
-    int reqsize;
-
-    struct icmp_echo_reply* ier;
-    struct ip* ip_header;
-    struct icmp* icmp_header;
-    char* endbuf;
-    int ip_header_len;
-    int maxlen;
-#ifdef __REACTOS__
-    fd_set fdr;
-    struct timeval timeout;
-#else
-    struct pollfd fdr;
-#endif
-    DWORD send_time,recv_time;
-    struct sockaddr_in addr;
-    socklen_t addrlen;
-    unsigned short id,seq,cksum;
-    int res;
-
-    if (IcmpHandle==INVALID_HANDLE_VALUE) {
-        /* FIXME: in fact win98 seems to ignore the handle value !!! */
-        SetLastError(ERROR_INVALID_HANDLE);
-        return 0;
-    }
+    CloseHandle(hEvent);
+    HeapFree(GetProcessHeap(), 0, InputBuffer);
 
-#ifdef __REACTOS__
-    if (ReplySize<sizeof(ICMP_ECHO_REPLY)) {
-        SetLastError(ERROR_INVALID_PARAMETER);
+    if (!NT_SUCCESS(Status))
+    {
+        SetLastError(RtlNtStatusToDosError(Status));
         return 0;
     }
 
-    if (ReplySize-RequestSize<sizeof(ICMP_ECHO_REPLY)) {
-        SetLastError(IP_GENERAL_FAILURE);
-        return 0;
+    Status = ((PICMP_ECHO_REPLY)ReplyBuffer)->Status;
+    if (Status != IP_SUCCESS)
+    {
+        SetLastError(Status);
     }
 
-    if (!ReplyBuffer) {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return 0;
-    }
+    nReplies = ((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved;
+    ((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved = 0;
 
-    if (Timeout == 0 || Timeout == -1) {
+    return nReplies;
+}
+
+DWORD
+WINAPI
+IcmpSendEcho(
+    _In_     HANDLE                 IcmpHandle,
+    _In_     IPAddr                 DestinationAddress,
+    _In_     LPVOID                 RequestData,
+    _In_     WORD                   RequestSize,
+    _In_opt_ PIP_OPTION_INFORMATION RequestOptions,
+    _Out_    LPVOID                 ReplyBuffer,
+    _In_     DWORD                  ReplySize,
+    _In_     DWORD                  Timeout)
+{
+    HANDLE hEvent;
+    IO_STATUS_BLOCK IoStatusBlock;
+    PVOID InputBuffer;
+    ULONG InputBufferLength;
+    PICMP_ECHO_REQUEST Request;
+    DWORD nReplies;
+    NTSTATUS Status;
+
+    if (Timeout == 0 || Timeout == (DWORD)-1)
+    {
         SetLastError(ERROR_INVALID_PARAMETER);
-#else
-    if (ReplySize<sizeof(ICMP_ECHO_REPLY)+ICMP_MINLEN) {
-        SetLastError(IP_BUF_TOO_SMALL);
-#endif
         return 0;
     }
-    /* check the request size against SO_MAX_MSG_SIZE using getsockopt */
 
-    if (!DestinationAddress) {
-        SetLastError(ERROR_INVALID_NETNAME);
+    if (ReplySize < sizeof(ICMP_ECHO_REPLY))
+    {
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
         return 0;
     }
 
-#ifndef __REACTOS__
-    if (icp->sid < 0) {
-        WARN("using system ping command since SOCK_RAW was not supported.\n");
-        return system_icmp(DestinationAddress, RequestData, RequestSize,
-                           RequestOptions, ReplyBuffer, ReplySize, Timeout);
-    }
-#endif
-
-    /* Prepare the request */
-#ifdef __REACTOS__
-    id = GetCurrentProcessId() & 0xFFFF;
-#else
-    id=getpid() & 0xFFFF;
-#endif
-    seq=InterlockedIncrement(&icmp_sequence) & 0xFFFF;
-
-#ifdef __REACTOS__
-    reqsize=ICMP_MINLEN;
-    if (RequestData && RequestSize > 0)
-        reqsize += RequestSize;
-#else
-    reqsize=ICMP_MINLEN+RequestSize;
-#endif
-    reqbuf=HeapAlloc(GetProcessHeap(), 0, reqsize);
-    if (reqbuf==NULL) {
-        SetLastError(ERROR_OUTOFMEMORY);
+    if (ReplySize < RequestSize + sizeof(ICMP_ECHO_REPLY))
+    {
+        SetLastError(IP_GENERAL_FAILURE);
         return 0;
     }
 
-    icmp_header=(struct icmp*)reqbuf;
-    icmp_header->icmp_type=ICMP_ECHO;
-    icmp_header->icmp_code=0;
-    icmp_header->icmp_cksum=0;
-    icmp_header->icmp_id=id;
-    icmp_header->icmp_seq=seq;
-#ifdef __REACTOS__
-    if (RequestData && RequestSize > 0)
-        memcpy(reqbuf+ICMP_MINLEN, RequestData, RequestSize);
-#else
-    memcpy(reqbuf+ICMP_MINLEN, RequestData, RequestSize);
-#endif
-    icmp_header->icmp_cksum=cksum=in_cksum((u_short*)reqbuf,reqsize);
-
-    addr.sin_family=AF_INET;
-    addr.sin_addr.s_addr=DestinationAddress;
-    addr.sin_port=0;
-
-    if (RequestOptions!=NULL) {
-        int val;
-        if (icp->default_opts.OptionsSize==IP_OPTS_UNKNOWN) {
-            socklen_t len;
-            /* Before we mess with the options, get the default values */
-            len=sizeof(val);
-            getsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,&len);
-            icp->default_opts.Ttl=val;
-
-            len=sizeof(val);
-            getsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,&len);
-            icp->default_opts.Tos=val;
-            /* FIXME: missing: handling of IP 'flags', and all the other 
options */
-        }
+    InputBufferLength = sizeof(ICMP_ECHO_REQUEST) + RequestSize;
+    if (RequestOptions != NULL)
+        InputBufferLength += RequestOptions->OptionsSize;
 
-        val=RequestOptions->Ttl;
-        setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val));
-        val=RequestOptions->Tos;
-        setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val));
-        /* FIXME:  missing: handling of IP 'flags', and all the other options 
*/
+    if (InputBufferLength < ReplySize)
+        InputBufferLength = ReplySize;
 
-        icp->default_opts.OptionsSize=IP_OPTS_CUSTOM;
-    } else if (icp->default_opts.OptionsSize==IP_OPTS_CUSTOM) {
-        int val;
+    InputBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 
InputBufferLength);
+    if (InputBuffer == NULL)
+    {
+        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+        return 0;
+    }
 
-        /* Restore the default options */
-        val=icp->default_opts.Ttl;
-        setsockopt(icp->sid,IPPROTO_IP,IP_TTL,(char *)&val,sizeof(val));
-        val=icp->default_opts.Tos;
-        setsockopt(icp->sid,IPPROTO_IP,IP_TOS,(char *)&val,sizeof(val));
-        /* FIXME: missing: handling of IP 'flags', and all the other options */
+    Request = (PICMP_ECHO_REQUEST)InputBuffer;
+    Request->Address = DestinationAddress;
+    Request->Timeout = Timeout;
+    Request->OptionsOffset = sizeof(ICMP_ECHO_REQUEST);
+    Request->DataOffset = sizeof(ICMP_ECHO_REQUEST);
 
-        icp->default_opts.OptionsSize=IP_OPTS_DEFAULT;
-    }
+    if (RequestOptions != NULL)
+    {
+        Request->HasOptions = TRUE;
+        Request->Ttl = RequestOptions->Ttl;
+        Request->Tos = RequestOptions->Tos;
+        Request->Flags = RequestOptions->Flags;
 
-    /* Get ready for receiving the reply
-     * Do it before we send the request to minimize the risk of introducing 
delays
-     */
-#ifdef __REACTOS__
-    FD_ZERO(&fdr);
-    FD_SET(icp->sid,&fdr);
-    timeout.tv_sec=Timeout/1000;
-    timeout.tv_usec=(Timeout % 1000)*1000;
-#else
-    fdr.fd = icp->sid;
-    fdr.events = POLLIN;
-#endif
-    addrlen=sizeof(addr);
-    ier=ReplyBuffer;
-#ifdef __REACTOS__
-    endbuf=((char *) ReplyBuffer)+ReplySize;
-    maxlen=sizeof(struct ip)+ICMP_MINLEN+RequestSize;
-#else
-    ip_header=(struct ip *) ((char *) ReplyBuffer+sizeof(ICMP_ECHO_REPLY));
-    endbuf=(char *) ReplyBuffer+ReplySize;
-    maxlen=ReplySize-sizeof(ICMP_ECHO_REPLY);
-#endif
-
-    /* Send the packet */
-    TRACE("Sending %d bytes (RequestSize=%d) to %s\n", reqsize, RequestSize, 
inet_ntoa(addr.sin_addr));
-#if 0
-    if (TRACE_ON(icmp)){
-        unsigned char* buf=(unsigned char*)reqbuf;
-        int i;
-        printf("Output buffer:\n");
-        for (i=0;i<reqsize;i++)
-            printf("%2x,", buf[i]);
-        printf("\n");
-    }
-#endif
-
-    send_time = GetTickCount();
-#ifdef __REACTOS__
-    res=sendto(icp->sid, (const char*)reqbuf, reqsize, 0, (struct 
sockaddr*)&addr, sizeof(addr));
-#else
-    res=sendto(icp->sid, reqbuf, reqsize, 0, (struct sockaddr*)&addr, 
sizeof(addr));
-#endif
-    HeapFree(GetProcessHeap (), 0, reqbuf);
-    if (res<0) {
-#ifdef __REACTOS__
-        DWORD dwBestIfIndex;
-        IPAddr IP4Addr;
-
-        ZeroMemory(&ier->Address, sizeof(ier->Address));
-
-        if (GetBestInterface(addr.sin_addr.s_addr, &dwBestIfIndex) == NO_ERROR 
&&
-            GetIPv4ByIndex(dwBestIfIndex, &IP4Addr))
+        if (RequestOptions->OptionsSize > 0)
         {
-            memcpy(&ier->Address, &IP4Addr, sizeof(IP4Addr));
-        }
+            Request->OptionsSize = RequestOptions->OptionsSize;
+            Request->DataOffset += Request->OptionsSize;
 
-        if (WSAGetLastError()==WSAEMSGSIZE)
-            ier->Status = IP_PACKET_TOO_BIG;
-        else {
-            switch (WSAGetLastError()) {
-            case WSAENETUNREACH:
-                ier->Status = IP_DEST_NET_UNREACHABLE;
-                break;
-            case WSAEHOSTUNREACH:
-                ier->Status = IP_DEST_HOST_UNREACHABLE;
-                break;
-            default:
-                TRACE("unknown error: errno=%d\n",WSAGetLastError());
-                ier->Status = IP_GENERAL_FAILURE;
-                ZeroMemory(&ier->Address, sizeof(ier->Address));
-            }
-        }
-        return 1;
-#else
-        if (errno==EMSGSIZE)
-            SetLastError(IP_PACKET_TOO_BIG);
-        else {
-            switch (errno) {
-            case ENETUNREACH:
-                SetLastError(IP_DEST_NET_UNREACHABLE);
-                break;
-            case EHOSTUNREACH:
-                SetLastError(IP_DEST_HOST_UNREACHABLE);
-                break;
-            default:
-                TRACE("unknown error: errno=%d\n",errno);
-                SetLastError(IP_GENERAL_FAILURE);
-            }
+            CopyMemory(
+                (PUCHAR)InputBuffer + sizeof(ICMP_ECHO_REQUEST),
+                RequestOptions->OptionsData,
+                Request->OptionsSize);
         }
-        return 0;
-#endif
     }
 
-    /* Get the reply */
-#ifdef __REACTOS__
-    ip_header=HeapAlloc(GetProcessHeap(), 0, maxlen);
-#endif
-    ip_header_len=0; /* because gcc was complaining */
-#ifdef __REACTOS__
-    while ((res=select(icp->sid+1,&fdr,NULL,NULL,&timeout))>0) {
-#else
-    while (poll(&fdr,1,Timeout)>0) {
-#endif
-        recv_time = GetTickCount();
-#ifdef __REACTOS__
-        res=recvfrom(icp->sid, (char*)ip_header, maxlen, 0, (struct 
sockaddr*)&addr,(int*)&addrlen);
-#else
-        res=recvfrom(icp->sid, (char*)ip_header, maxlen, 0, (struct 
sockaddr*)&addr,&addrlen);
-#endif
-        TRACE("received %d bytes from %s\n",res, inet_ntoa(addr.sin_addr));
-        ier->Status=IP_REQ_TIMED_OUT;
-#ifdef __REACTOS__
-        if (res < 0)
-            break;
-#endif
-
-        /* Check whether we should ignore this packet */
-        if ((ip_header->ip_p==IPPROTO_ICMP) && (res>=sizeof(struct 
ip)+ICMP_MINLEN)) {
-            ip_header_len=ip_header->ip_hl << 2;
-            icmp_header=(struct icmp*)(((char*)ip_header)+ip_header_len);
-            TRACE("received an ICMP packet of 
type,code=%d,%d\n",icmp_header->icmp_type,icmp_header->icmp_code);
-            if (icmp_header->icmp_type==ICMP_ECHOREPLY) {
-                if ((icmp_header->icmp_id==id) && (icmp_header->icmp_seq==seq))
-                    ier->Status=IP_SUCCESS;
-            } else {
-                switch (icmp_header->icmp_type) {
-                case ICMP_UNREACH:
-                    switch (icmp_header->icmp_code) {
-                    case ICMP_UNREACH_HOST:
-#ifdef ICMP_UNREACH_HOST_UNKNOWN
-                    case ICMP_UNREACH_HOST_UNKNOWN:
-#endif
-#ifdef ICMP_UNREACH_ISOLATED
-                    case ICMP_UNREACH_ISOLATED:
-#endif
-#ifdef ICMP_UNREACH_HOST_PROHIB
-                    case ICMP_UNREACH_HOST_PROHIB:
-#endif
-#ifdef ICMP_UNREACH_TOSHOST
-                    case ICMP_UNREACH_TOSHOST:
-#endif
-                        ier->Status=IP_DEST_HOST_UNREACHABLE;
-                        break;
-                    case ICMP_UNREACH_PORT:
-                        ier->Status=IP_DEST_PORT_UNREACHABLE;
-                        break;
-                    case ICMP_UNREACH_PROTOCOL:
-                        ier->Status=IP_DEST_PROT_UNREACHABLE;
-                        break;
-                    case ICMP_UNREACH_SRCFAIL:
-                        ier->Status=IP_BAD_ROUTE;
-                        break;
-                    default:
-                        ier->Status=IP_DEST_NET_UNREACHABLE;
-                    }
-                    break;
-                case ICMP_TIMXCEED:
-                    if (icmp_header->icmp_code==ICMP_TIMXCEED_REASS)
-                        ier->Status=IP_TTL_EXPIRED_REASSEM;
-                    else
-                        ier->Status=IP_TTL_EXPIRED_TRANSIT;
-                    break;
-                case ICMP_PARAMPROB:
-                    ier->Status=IP_PARAM_PROBLEM;
-                    break;
-                case ICMP_SOURCEQUENCH:
-                    ier->Status=IP_SOURCE_QUENCH;
-                    break;
-                }
-                if (ier->Status!=IP_REQ_TIMED_OUT) {
-                    struct ip* rep_ip_header;
-                    struct icmp* rep_icmp_header;
-                    /* The ICMP header size of all the packets we accept is 
the same */
-                    rep_ip_header=(struct 
ip*)(((char*)icmp_header)+ICMP_MINLEN);
-                    rep_icmp_header=(struct 
icmp*)(((char*)rep_ip_header)+(rep_ip_header->ip_hl << 2));
-
-                    /* Make sure that this is really a reply to our packet */
-                    if (ip_header_len+ICMP_MINLEN+(rep_ip_header->ip_hl << 
2)+ICMP_MINLEN>ip_header->ip_len) {
-                        ier->Status=IP_REQ_TIMED_OUT;
-                    } else if ((rep_icmp_header->icmp_type!=ICMP_ECHO) ||
-                        (rep_icmp_header->icmp_code!=0) ||
-                        (rep_icmp_header->icmp_id!=id) ||
-                        /* windows doesn't check this checksum, else tracert */
-                        /* behind a Linux 2.2 masquerading firewall would 
fail*/
-                        /* (rep_icmp_header->icmp_cksum!=cksum) || */
-                        (rep_icmp_header->icmp_seq!=seq)) {
-                        /* This was not a reply to one of our packets after 
all */
-                        TRACE("skipping type,code=%d,%d id,seq=%d,%d 
cksum=%d\n",
-                            
rep_icmp_header->icmp_type,rep_icmp_header->icmp_code,
-                            rep_icmp_header->icmp_id,rep_icmp_header->icmp_seq,
-                            rep_icmp_header->icmp_cksum);
-                        TRACE("expected type,code=8,0 id,seq=%d,%d cksum=%d\n",
-                            id,seq,
-                            cksum);
-                        ier->Status=IP_REQ_TIMED_OUT;
-                    }
-                }
-            }
-        }
-
-        if (ier->Status==IP_REQ_TIMED_OUT) {
-            /* This packet was not for us.
-             * Decrease the timeout so that we don't enter an endless loop even
-             * if we get flooded with ICMP packets that are not for us.
-             */
-#ifdef __REACTOS__
-            int t = Timeout - (recv_time - send_time);
-            if (t < 0) t = 0;
-            timeout.tv_sec = t / 1000;
-            timeout.tv_usec = (t % 1000) * 1000;
-            FD_ZERO(&fdr);
-            FD_SET(icp->sid, &fdr);
-#else
-            DWORD t = (recv_time - send_time);
-            if (Timeout > t) Timeout -= t;
-            else             Timeout = 0;
-#endif
-            continue;
-        } else {
-            /* This is a reply to our packet */
-            memcpy(&ier->Address,&ip_header->ip_src,sizeof(IPAddr));
-            /* Status is already set */
-            ier->RoundTripTime= recv_time - send_time;
-            ier->DataSize=res-ip_header_len-ICMP_MINLEN;
-            ier->Reserved=0;
-            ier->Data=endbuf-ier->DataSize;
-            
memmove(ier->Data,((char*)ip_header)+ip_header_len+ICMP_MINLEN,ier->DataSize);
-            ier->Options.Ttl=ip_header->ip_ttl;
-            ier->Options.Tos=ip_header->ip_tos;
-            ier->Options.Flags=ip_header->ip_off >> 13;
-            ier->Options.OptionsSize=ip_header_len-sizeof(struct ip);
-            if (ier->Options.OptionsSize!=0) {
-                ier->Options.OptionsData=(unsigned char *) 
ier->Data-ier->Options.OptionsSize;
-                /* FIXME: We are supposed to rearrange the option's 'source 
route' data */
-                
memmove(ier->Options.OptionsData,((char*)ip_header)+ip_header_len,ier->Options.OptionsSize);
-                endbuf=(char*)ier->Options.OptionsData;
-            } else {
-                ier->Options.OptionsData=NULL;
-                endbuf=ier->Data;
-            }
-
-            /* Prepare for the next packet */
-            ier++;
-#ifndef __REACTOS__
-            ip_header=(struct ip*)(((char*)ip_header)+sizeof(ICMP_ECHO_REPLY));
-            maxlen=endbuf-(char*)ip_header;
-#endif
-
-            /* Check out whether there is more but don't wait this time */
-#ifdef __REACTOS__
-            timeout.tv_sec=0;
-            timeout.tv_usec=0;
-#else
-            Timeout=0;
-#endif
-        }
-#ifdef __REACTOS__
-        FD_ZERO(&fdr);
-        FD_SET(icp->sid,&fdr);
-#endif
-    }
-#ifdef __REACTOS__
-    HeapFree(GetProcessHeap(), 0, ip_header);
-#endif
-    res=ier-(ICMP_ECHO_REPLY*)ReplyBuffer;
-    if (res==0)
-#ifdef __REACTOS__
+    if (RequestSize > 0)
     {
-        ier->Status = IP_REQ_TIMED_OUT;
-#endif
-        SetLastError(IP_REQ_TIMED_OUT);
-#ifdef __REACTOS__
+        Request->DataSize = RequestSize;
+        CopyMemory((PUCHAR)InputBuffer + Request->DataOffset, RequestData, 
RequestSize);
     }
-#endif
-    TRACE("received %d replies\n",res);
-    return res;
-}
 
-/***********************************************************************
- *             IcmpSendEcho2 (IPHLPAPI.@)
- */
-DWORD WINAPI IcmpSendEcho2(
-    HANDLE                   IcmpHandle,
-    HANDLE                   Event,
-    PIO_APC_ROUTINE          ApcRoutine,
-    PVOID                    ApcContext,
-    IPAddr                   DestinationAddress,
-    LPVOID                   RequestData,
-    WORD                     RequestSize,
-    PIP_OPTION_INFORMATION   RequestOptions,
-    LPVOID                   ReplyBuffer,
-    DWORD                    ReplySize,
-    DWORD                    Timeout
-    )
-{
-    TRACE("(%p, %p, %p, %p, %08x, %p, %d, %p, %p, %d, %d): stub\n", IcmpHandle,
-            Event, ApcRoutine, ApcContext, DestinationAddress, RequestData,
-            RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout);
-
-    if (Event)
-    {
-        FIXME("unsupported for events\n");
-        return 0;
-    }
-    if (ApcRoutine)
+    hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+    if (hEvent == NULL)
     {
-        FIXME("unsupported for APCs\n");
+        HeapFree(GetProcessHeap(), 0, InputBuffer);
         return 0;
     }
-    return IcmpSendEcho(IcmpHandle, DestinationAddress, RequestData,
-            RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout);
-}
 
-/***********************************************************************
- *             IcmpSendEcho2Ex (IPHLPAPI.@)
- */
-DWORD WINAPI IcmpSendEcho2Ex(
-    HANDLE                   IcmpHandle,
-    HANDLE                   Event,
-    PIO_APC_ROUTINE          ApcRoutine,
-    PVOID                    ApcContext,
-    IPAddr                   SourceAddress,
-    IPAddr                   DestinationAddress,
-    LPVOID                   RequestData,
-    WORD                     RequestSize,
-    PIP_OPTION_INFORMATION   RequestOptions,
-    LPVOID                   ReplyBuffer,
-    DWORD                    ReplySize,
-    DWORD                    Timeout
-    )
-{
-    TRACE("(%p, %p, %p, %p, %08x, %08x, %p, %d, %p, %p, %d, %d): stub\n", 
IcmpHandle,
-            Event, ApcRoutine, ApcContext, SourceAddress, DestinationAddress, 
RequestData,
-            RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout);
+    Status = NtDeviceIoControlFile(
+        IcmpHandle,
+        hEvent,
+        NULL,
+        NULL,
+        &IoStatusBlock,
+        IOCTL_ICMP_ECHO_REQUEST,
+        InputBuffer,
+        InputBufferLength,
+        ReplyBuffer,
+        ReplySize);
 
-    if (Event)
+    if (Status == STATUS_PENDING)
     {
-        FIXME("unsupported for events\n");
-        return 0;
+        Status = NtWaitForSingleObject(hEvent, FALSE, NULL);
+
+        if (NT_SUCCESS(Status))
+        {
+            Status = IoStatusBlock.Status;
+        }
     }
-    if (ApcRoutine)
+
+    CloseHandle(hEvent);
+    HeapFree(GetProcessHeap(), 0, InputBuffer);
+
+    if (!NT_SUCCESS(Status))
     {
-        FIXME("unsupported for APCs\n");
+        SetLastError(RtlNtStatusToDosError(Status));
         return 0;
     }
-    if (SourceAddress)
+
+    Status = ((PICMP_ECHO_REPLY)ReplyBuffer)->Status;
+    if (Status != IP_SUCCESS)
     {
-        FIXME("unsupported for source addresses\n");
-        return 0;
+        SetLastError(Status);
     }
 
-    return IcmpSendEcho(IcmpHandle, DestinationAddress, RequestData,
-            RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout);
-}
+    nReplies = ((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved;
+    ((PICMP_ECHO_REPLY)ReplyBuffer)->Reserved = 0;
 
-/*
- * Copyright (c) 1989 The Regents of the University of California.
- * All rights reserved.
- *
- * This code is derived from software contributed to Berkeley by
- * Mike Muuss.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- */
+    return nReplies;
+}
diff --git a/dll/win32/iphlpapi/iphlpapi.spec b/dll/win32/iphlpapi/iphlpapi.spec
index 6d186f35152..3151b5f40db 100644
--- a/dll/win32/iphlpapi/iphlpapi.spec
+++ b/dll/win32/iphlpapi/iphlpapi.spec
@@ -80,11 +80,11 @@
 @ stub GetUdpTableFromStack
 @ stdcall GetUniDirectionalAdapterInfo( ptr ptr )
 @ stdcall Icmp6CreateFile()
-@ stdcall -stub Icmp6ParseReplies(ptr long)
+@ stdcall Icmp6ParseReplies(ptr long)
 @ stdcall Icmp6SendEcho2(ptr ptr ptr ptr ptr ptr ptr long ptr ptr long long)
 @ stdcall IcmpCloseHandle(ptr)
 @ stdcall IcmpCreateFile()
-@ stdcall -stub IcmpParseReplies(ptr long)
+@ stdcall IcmpParseReplies(ptr long)
 @ stdcall IcmpSendEcho2(ptr ptr ptr ptr long ptr long ptr ptr long long)
 @ stdcall IcmpSendEcho(ptr long ptr long ptr ptr long long)
 @ stub InternalCreateIpForwardEntry
diff --git a/sdk/include/psdk/ipexport.h b/sdk/include/psdk/ipexport.h
index 98cd3a9b82f..82c89a1531d 100644
--- a/sdk/include/psdk/ipexport.h
+++ b/sdk/include/psdk/ipexport.h
@@ -28,14 +28,14 @@ typedef ULONG IPAddr;
 typedef ULONG IPMask;
 typedef ULONG IP_STATUS;
 
-struct ip_option_information
+typedef struct ip_option_information
 {
     unsigned char  Ttl;
     unsigned char  Tos;
     unsigned char  Flags;
     unsigned char  OptionsSize;
     unsigned char* OptionsData;
-};
+} IP_OPTION_INFORMATION, *PIP_OPTION_INFORMATION;
 
 #if defined(_WIN64)
 
@@ -64,20 +64,31 @@ struct ip_option_information32
 #define MAX_OPT_SIZE    40
 
 
-struct icmp_echo_reply
+typedef struct icmp_echo_request
 {
-    IPAddr                       Address;
-    ULONG                        Status;
-    ULONG                        RoundTripTime;
-    unsigned short               DataSize;
-    unsigned short               Reserved;
-    void*                        Data;
-    struct ip_option_information Options;
-};
-
-typedef struct ip_option_information IP_OPTION_INFORMATION, 
*PIP_OPTION_INFORMATION;
-
-typedef struct icmp_echo_reply ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY;
+    IPAddr Address;
+    UINT32 Timeout;
+    UINT16 DataOffset;
+    UINT16 DataSize;
+    UINT8 HasOptions;
+    UINT8 Ttl;
+    UINT8 Tos;
+    UINT8 Flags;
+    UINT16 OptionsOffset;
+    UINT8 OptionsSize;
+    UINT8 Padding;
+} ICMP_ECHO_REQUEST, *PICMP_ECHO_REQUEST;
+
+typedef struct icmp_echo_reply
+{
+    IPAddr Address;
+    UINT32 Status;
+    UINT32 RoundTripTime;
+    UINT16 DataSize;
+    UINT16 Reserved;
+    PVOID Data;
+    IP_OPTION_INFORMATION Options;
+} ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY;
 
 #ifdef _WIN64
 struct icmp_echo_reply32
@@ -164,6 +175,16 @@ typedef struct _IPV6_ADDRESS_EX {
 } IPV6_ADDRESS_EX, *PIPV6_ADDRESS_EX;
 #include <poppack.h>
 
+typedef struct _ICMPV6_ECHO_REQUEST
+{
+    IPV6_ADDRESS_EX DestinationAddress;
+    IPV6_ADDRESS_EX SourceAddress;
+    UINT32          Timeout;
+    UINT16          Unknown1;
+    UINT16          Ttl;            // XXX: These seem unnecessarily large.
+    UINT32          Flags;          //      Is something else in the struct?
+} ICMPV6_ECHO_REQUEST, *PICMPV6_ECHO_REQUEST;
+
 typedef struct icmpv6_echo_reply_lh {
   IPV6_ADDRESS_EX Address;
   ULONG Status;
diff --git a/sdk/include/psdk/tcpioctl.h b/sdk/include/psdk/tcpioctl.h
index af7389155ca..cb9744bd508 100644
--- a/sdk/include/psdk/tcpioctl.h
+++ b/sdk/include/psdk/tcpioctl.h
@@ -46,6 +46,9 @@
 #define IOCTL_DELETE_IP_ADDRESS \
     _TCP_CTL_CODE(16, METHOD_BUFFERED, FILE_WRITE_ACCESS)
 
+#define IOCTL_ICMP_ECHO_REQUEST \
+    _TCP_CTL_CODE(0, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
 #define IF_MIB_STATS_ID                 1
 #define IP_MIB_STATS_ID                 1
 #define IP_MIB_ARPTABLE_ENTRY_ID        0x101

Reply via email to