[PATCH 4/7] iphlpapi: Implement IcmpSendEcho2Ex() using nsiproxy.

Huw Davies huw at codeweavers.com
Wed Oct 6 04:12:58 CDT 2021


This results in a small change in behaviour reflected by the change to
the tests: previously IcmpCreateFile() would fail if neither a
SOCK_RAW nor a SOCK_DGRAM socket were available.  With this patch, that
failure is delayed until IcmpSendEcho2Ex().  There's no evidence that
the original behaviour matches Windows; it's likely the tests were
written this way to match Wine's implementation.  If there does turn
out to be an app that depends on the old behaviour, it would be
possible to send an ioctl during IcmpCreateFile() to probe for this.

Signed-off-by: Huw Davies <huw at codeweavers.com>
---
 dlls/iphlpapi/Makefile.in      |   1 -
 dlls/iphlpapi/icmp.c           | 643 ---------------------------------
 dlls/iphlpapi/ip.h             | 180 ---------
 dlls/iphlpapi/ip_icmp.h        | 186 ----------
 dlls/iphlpapi/iphlpapi_main.c  | 172 +++++++++
 dlls/iphlpapi/tests/iphlpapi.c |  14 +-
 6 files changed, 177 insertions(+), 1019 deletions(-)
 delete mode 100644 dlls/iphlpapi/icmp.c
 delete mode 100644 dlls/iphlpapi/ip.h
 delete mode 100644 dlls/iphlpapi/ip_icmp.h

diff --git a/dlls/iphlpapi/Makefile.in b/dlls/iphlpapi/Makefile.in
index d5f7818be5f..e413337a420 100644
--- a/dlls/iphlpapi/Makefile.in
+++ b/dlls/iphlpapi/Makefile.in
@@ -5,7 +5,6 @@ IMPORTS   = advapi32 dnsapi nsi uuid
 EXTRADLLFLAGS = -mcygwin
 
 C_SRCS = \
-	icmp.c \
 	iphlpapi_main.c
 
 RC_SRCS = version.rc
diff --git a/dlls/iphlpapi/icmp.c b/dlls/iphlpapi/icmp.c
deleted file mode 100644
index 6441d7dd8ad..00000000000
--- a/dlls/iphlpapi/icmp.c
+++ /dev/null
@@ -1,643 +0,0 @@
-/*
- * 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.
- *
- * 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
- */
-
-/* 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.
- */
-
-#include "config.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 <errno.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
-
-#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"
-
-/* 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(icmp);
-WINE_DECLARE_DEBUG_CHANNEL(winediag);
-
-
-typedef struct {
-    int sid;
-    IP_OPTION_INFORMATION default_opts;
-} icmp_t;
-
-#define IP_OPTS_UNKNOWN     0
-#define IP_OPTS_DEFAULT     1
-#define IP_OPTS_CUSTOM      2
-
-#define MAXIPLEN            60
-#define MAXICMPLEN          76
-
-/* The sequence number is unique process wide, so that all threads
- * have a distinct sequence number.
- */
-static LONG icmp_sequence=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;
-    }
-
-    if (nleft == 1) {
-        *(u_char *)(&answer) = *(u_char *)w;
-        sum += answer;
-    }
-
-    sum = (sum >> 16) + (sum & 0xffff);
-    sum  += (sum >> 16);
-    answer = ~sum;
-    return(answer);
-}
-
-/* Receive a reply (IPv4); this function uses, takes ownership of and will always free `buffer` */
-static DWORD icmp_get_reply(int sid, unsigned char *buffer, DWORD send_time, void *reply_buf, DWORD reply_size, DWORD timeout)
-{
-    int repsize = MAXIPLEN + MAXICMPLEN + min(65535, reply_size);
-    struct icmp *icmp_header = (struct icmp*)buffer;
-    char *endbuf = (char*)reply_buf + reply_size;
-    struct ip *ip_header = (struct ip*)buffer;
-    struct icmp_echo_reply *ier = reply_buf;
-    unsigned short id, seq, cksum;
-    struct sockaddr_in addr;
-    int ip_header_len = 0;
-    socklen_t addrlen;
-    struct pollfd fdr;
-    DWORD recv_time;
-    int res;
-
-    id = icmp_header->icmp_id;
-    seq = icmp_header->icmp_seq;
-    cksum = icmp_header->icmp_cksum;
-    fdr.fd = sid;
-    fdr.events = POLLIN;
-    addrlen = sizeof(addr);
-
-    while (poll(&fdr,1,timeout)>0) {
-        recv_time = GetTickCount();
-        res=recvfrom(sid, buffer, repsize, 0, (struct sockaddr*)&addr, &addrlen);
-        TRACE("received %d bytes from %s\n",res, inet_ntoa(addr.sin_addr));
-        ier->Status=IP_REQ_TIMED_OUT;
-
-        /* 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;
-                    SetLastError(NO_ERROR);
-                }
-            } 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.
-             */
-            DWORD t = (recv_time - send_time);
-            if (timeout > t) timeout -= t;
-            else             timeout = 0;
-            continue;
-        } else {
-            /* Check free space, should be large enough for an ICMP_ECHO_REPLY and remainning icmp data */
-            if (endbuf-(char *)ier < sizeof(struct icmp_echo_reply)+(res-ip_header_len-ICMP_MINLEN)) {
-                res=ier-(ICMP_ECHO_REPLY *)reply_buf;
-                SetLastError(IP_GENERAL_FAILURE);
-                goto done;
-            }
-            /* 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;
-            memcpy(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 */
-                memcpy(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++;
-
-            /* Check out whether there is more but don't wait this time */
-            timeout=0;
-        }
-    }
-    res=ier-(ICMP_ECHO_REPLY*)reply_buf;
-    if (res==0)
-        SetLastError(IP_REQ_TIMED_OUT);
-done:
-    if (res)
-    {
-        /* Move the data so there's no gap between it and the ICMP_ECHO_REPLY array */
-        DWORD gap_size = endbuf - (char*)ier;
-
-        if (gap_size)
-        {
-            memmove(ier, endbuf, ((char*)reply_buf + reply_size) - endbuf);
-
-            /* Fix the pointers */
-            while (ier-- != reply_buf)
-            {
-                ier->Data = (char*)ier->Data - gap_size;
-                if (ier->Options.OptionsData)
-                    ier->Options.OptionsData -= gap_size;
-            }
-
-            /* According to MSDN, the reply buffer needs to hold space for a IO_STATUS_BLOCK,
-               found at the very end of the reply. This is confirmed on Windows XP, but Vista
-               and later do not store it anywhere and in fact don't even require it at all.
-
-               However, in case old apps analyze this IO_STATUS_BLOCK and expect it, we mimic
-               it and write it out if there's enough space available in the buffer. */
-            if (gap_size >= sizeof(IO_STATUS_BLOCK))
-            {
-                IO_STATUS_BLOCK *io = (IO_STATUS_BLOCK*)((char*)reply_buf + reply_size - sizeof(IO_STATUS_BLOCK));
-
-                io->Pointer = NULL;  /* Always NULL or STATUS_SUCCESS */
-                io->Information = reply_size - gap_size;
-            }
-        }
-    }
-
-    HeapFree(GetProcessHeap(), 0, buffer);
-    TRACE("received %d replies\n",res);
-    return res;
-}
-
-
-
-/*
- * Exported Routines.
- */
-
-/***********************************************************************
- *		IcmpCreateFile (IPHLPAPI.@)
- */
-HANDLE WINAPI IcmpCreateFile(VOID)
-{
-    icmp_t* icp;
-
-    int sid=socket(AF_INET,SOCK_RAW,IPPROTO_ICMP);
-    if (sid < 0)
-    {
-        /* Some systems (e.g. Linux 3.0+ and Mac OS X) support
-           non-privileged ICMP via SOCK_DGRAM type. */
-        sid=socket(AF_INET,SOCK_DGRAM,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;
-    }
-
-    icp=HeapAlloc(GetProcessHeap(), 0, sizeof(*icp));
-    if (icp==NULL) {
-        close(sid);
-        SetLastError(IP_NO_RESOURCES);
-        return INVALID_HANDLE_VALUE;
-    }
-    icp->sid=sid;
-    icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN;
-    return (HANDLE)icp;
-}
-
-
-/***********************************************************************
- *		IcmpCloseHandle (IPHLPAPI.@)
- */
-BOOL WINAPI IcmpCloseHandle(HANDLE  IcmpHandle)
-{
-    icmp_t* icp=(icmp_t*)IcmpHandle;
-    if (IcmpHandle==INVALID_HANDLE_VALUE) {
-        /* FIXME: in fact win98 seems to ignore the handle value !!! */
-        SetLastError(ERROR_INVALID_HANDLE);
-        return FALSE;
-    }
-
-    close( icp->sid );
-    HeapFree(GetProcessHeap (), 0, icp);
-    return TRUE;
-}
-
-
-/***********************************************************************
- *		IcmpSendEcho (IPHLPAPI.@)
- */
-DWORD WINAPI IcmpSendEcho(
-    HANDLE                   IcmpHandle,
-    IPAddr                   DestinationAddress,
-    LPVOID                   RequestData,
-    WORD                     RequestSize,
-    PIP_OPTION_INFORMATION   RequestOptions,
-    LPVOID                   ReplyBuffer,
-    DWORD                    ReplySize,
-    DWORD                    Timeout
-    )
-{
-    return IcmpSendEcho2Ex(IcmpHandle, NULL, NULL, NULL, 0, DestinationAddress,
-            RequestData, RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout);
-}
-
-/***********************************************************************
- *		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
-    )
-{
-    return IcmpSendEcho2Ex(IcmpHandle, Event, ApcRoutine, ApcContext, 0,
-            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
-    )
-{
-    icmp_t* icp=(icmp_t*)IcmpHandle;
-    struct icmp* icmp_header;
-    struct sockaddr_in addr;
-    unsigned short id, seq;
-    unsigned char *buffer;
-    int reqsize, repsize;
-    DWORD send_time;
-
-    TRACE("(%p, %p, %p, %p, %08x, %08x, %p, %d, %p, %p, %d, %d)\n", IcmpHandle,
-            Event, ApcRoutine, ApcContext, SourceAddress, DestinationAddress, RequestData,
-            RequestSize, RequestOptions, ReplyBuffer, ReplySize, Timeout);
-
-    if (IcmpHandle==INVALID_HANDLE_VALUE) {
-        /* FIXME: in fact win98 seems to ignore the handle value !!! */
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return 0;
-    }
-
-    if (!ReplyBuffer||!ReplySize) {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return 0;
-    }
-
-    if (ReplySize<sizeof(ICMP_ECHO_REPLY)) {
-        SetLastError(IP_BUF_TOO_SMALL);
-        return 0;
-    }
-    /* check the request size against SO_MAX_MSG_SIZE using getsockopt */
-
-    if (!DestinationAddress) {
-        SetLastError(ERROR_INVALID_NETNAME);
-        return 0;
-    }
-
-    if (Event)
-    {
-        FIXME("unsupported for events\n");
-        return 0;
-    }
-    if (ApcRoutine)
-    {
-        FIXME("unsupported for APCs\n");
-        return 0;
-    }
-    if (SourceAddress)
-    {
-        FIXME("unsupported for source addresses\n");
-        return 0;
-    }
-
-    /* Prepare the request */
-    id=getpid() & 0xFFFF;
-    seq=InterlockedIncrement(&icmp_sequence) & 0xFFFF;
-
-    reqsize=ICMP_MINLEN+RequestSize;
-    /* max ip header + max icmp header and error data + reply size(max 65535 on Windows) */
-    /* FIXME: request size of 65535 is not supported yet because max buffer size of raw socket on linux is 32767 */
-    repsize = MAXIPLEN + MAXICMPLEN + min( 65535, ReplySize );
-    buffer = HeapAlloc(GetProcessHeap(), 0, max( repsize, reqsize ));
-    if (buffer == NULL) {
-        SetLastError(ERROR_OUTOFMEMORY);
-        return 0;
-    }
-
-    icmp_header=(struct icmp*)buffer;
-    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;
-    memcpy(buffer+ICMP_MINLEN, RequestData, RequestSize);
-    icmp_header->icmp_cksum=in_cksum((u_short*)buffer,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 */
-        }
-
-        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 */
-
-        icp->default_opts.OptionsSize=IP_OPTS_CUSTOM;
-    } else if (icp->default_opts.OptionsSize==IP_OPTS_CUSTOM) {
-        int val;
-
-        /* 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 */
-
-        icp->default_opts.OptionsSize=IP_OPTS_DEFAULT;
-    }
-
-    /* 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)){
-        int i;
-        printf("Output buffer:\n");
-        for (i=0;i<reqsize;i++)
-            printf("%2x,", buffer[i]);
-        printf("\n");
-    }
-#endif
-
-    send_time = GetTickCount();
-    if (sendto(icp->sid, buffer, reqsize, 0, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
-        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);
-            }
-        }
-        HeapFree(GetProcessHeap(), 0, buffer);
-        return 0;
-    }
-
-    return icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, ReplySize, Timeout);
-}
-
-/*
- * 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.
- *
- */
diff --git a/dlls/iphlpapi/ip.h b/dlls/iphlpapi/ip.h
deleted file mode 100644
index 9c669d9fd88..00000000000
--- a/dlls/iphlpapi/ip.h
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Copyright (c) 1982, 1986, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * 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.
- *
- *	@(#)ip.h	8.2 (Berkeley) 6/1/94
- * $FreeBSD: src/sys/netinet/ip.h,v 1.16 1999/08/28 00:49:19 peter Exp $
- */
-
-#ifndef _NETINET_IP_H_
-#define _NETINET_IP_H_
-
-/*
- * Definitions for internet protocol version 4.
- * Per RFC 791, September 1981.
- */
-#define	IPVERSION	4
-
-/*
- * Structure of an internet header, naked of options.
- */
-struct ip {
-#ifdef _IP_VHL
-	u_char	ip_vhl;			/* version << 4 | header length >> 2 */
-#else
-#if BYTE_ORDER == LITTLE_ENDIAN
-	u_int	ip_hl:4,		/* header length */
-		ip_v:4;			/* version */
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
-	u_int	ip_v:4,			/* version */
-		ip_hl:4;		/* header length */
-#endif
-#endif /* not _IP_VHL */
-	u_char	ip_tos;			/* type of service */
-	u_short	ip_len;			/* total length */
-	u_short	ip_id;			/* identification */
-	u_short	ip_off;			/* fragment offset field */
-#define	IP_RF 0x8000			/* reserved fragment flag */
-#define	IP_DF 0x4000			/* don't fragment flag */
-#define	IP_MF 0x2000			/* more fragments flag */
-#define	IP_OFFMASK 0x1fff		/* mask for fragmenting bits */
-	u_char	ip_ttl;			/* time to live */
-	u_char	ip_p;			/* protocol */
-	u_short	ip_sum;			/* checksum */
-	struct	in_addr ip_src,ip_dst;	/* source and dest address */
-};
-
-#ifdef _IP_VHL
-#define	IP_MAKE_VHL(v, hl)	((v) << 4 | (hl))
-#define	IP_VHL_HL(vhl)		((vhl) & 0x0f)
-#define	IP_VHL_V(vhl)		((vhl) >> 4)
-#define	IP_VHL_BORING		0x45
-#endif
-
-#define	IP_MAXPACKET	65535		/* maximum packet size */
-
-/*
- * Definitions for IP type of service (ip_tos)
- */
-#define	IPTOS_LOWDELAY		0x10
-#define	IPTOS_THROUGHPUT	0x08
-#define	IPTOS_RELIABILITY	0x04
-#define	IPTOS_MINCOST		0x02
-
-/*
- * Definitions for IP precedence (also in ip_tos) (hopefully unused)
- */
-#define	IPTOS_PREC_NETCONTROL		0xe0
-#define	IPTOS_PREC_INTERNETCONTROL	0xc0
-#define	IPTOS_PREC_CRITIC_ECP		0xa0
-#define	IPTOS_PREC_FLASHOVERRIDE	0x80
-#define	IPTOS_PREC_FLASH		0x60
-#define	IPTOS_PREC_IMMEDIATE		0x40
-#define	IPTOS_PREC_PRIORITY		0x20
-#define	IPTOS_PREC_ROUTINE		0x00
-
-/*
- * Definitions for options.
- */
-#define	IPOPT_COPIED(o)		((o)&0x80)
-#define	IPOPT_CLASS(o)		((o)&0x60)
-#define	IPOPT_NUMBER(o)		((o)&0x1f)
-
-#define	IPOPT_CONTROL		0x00
-#define	IPOPT_RESERVED1		0x20
-#define	IPOPT_DEBMEAS		0x40
-#define	IPOPT_RESERVED2		0x60
-
-#define	IPOPT_EOL		0		/* end of option list */
-#define	IPOPT_NOP		1		/* no operation */
-
-#define	IPOPT_RR		7		/* record packet route */
-#define	IPOPT_TS		68		/* timestamp */
-#define	IPOPT_SECURITY		130		/* provide s,c,h,tcc */
-#define	IPOPT_LSRR		131		/* loose source route */
-#define	IPOPT_SATID		136		/* satnet id */
-#define	IPOPT_SSRR		137		/* strict source route */
-#define	IPOPT_RA		148		/* router alert */
-
-/*
- * Offsets to fields in options other than EOL and NOP.
- */
-#define	IPOPT_OPTVAL		0		/* option ID */
-#define	IPOPT_OLEN		1		/* option length */
-#define IPOPT_OFFSET		2		/* offset within option */
-#define	IPOPT_MINOFF		4		/* min value of above */
-
-/*
- * Time stamp option structure.
- */
-struct	ip_timestamp {
-	u_char	ipt_code;		/* IPOPT_TS */
-	u_char	ipt_len;		/* size of structure (variable) */
-	u_char	ipt_ptr;		/* index of current entry */
-#if BYTE_ORDER == LITTLE_ENDIAN
-	u_int	ipt_flg:4,		/* flags, see below */
-		ipt_oflw:4;		/* overflow counter */
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
-	u_int	ipt_oflw:4,		/* overflow counter */
-		ipt_flg:4;		/* flags, see below */
-#endif
-	union ipt_timestamp {
-		n_long	ipt_time[1];
-		struct	ipt_ta {
-			struct in_addr ipt_addr;
-			n_long ipt_time;
-		} ipt_ta[1];
-	} ipt_timestamp;
-};
-
-/* flag bits for ipt_flg */
-#define	IPOPT_TS_TSONLY		0		/* timestamps only */
-#define	IPOPT_TS_TSANDADDR	1		/* timestamps and addresses */
-#define	IPOPT_TS_PRESPEC	3		/* specified modules only */
-
-/* bits for security (not byte swapped) */
-#define	IPOPT_SECUR_UNCLASS	0x0000
-#define	IPOPT_SECUR_CONFID	0xf135
-#define	IPOPT_SECUR_EFTO	0x789a
-#define	IPOPT_SECUR_MMMM	0xbc4d
-#define	IPOPT_SECUR_RESTR	0xaf13
-#define	IPOPT_SECUR_SECRET	0xd788
-#define	IPOPT_SECUR_TOPSECRET	0x6bc5
-
-/*
- * Internet implementation parameters.
- */
-#define	MAXTTL		255		/* maximum time to live (seconds) */
-#define	IPDEFTTL	64		/* default ttl, from RFC 1340 */
-#define	IPFRAGTTL	60		/* time to live for frags, slowhz */
-#define	IPTTLDEC	1		/* subtracted when forwarding */
-
-#define	IP_MSS		576		/* default maximum segment size */
-
-#endif
diff --git a/dlls/iphlpapi/ip_icmp.h b/dlls/iphlpapi/ip_icmp.h
deleted file mode 100644
index ce5449526d2..00000000000
--- a/dlls/iphlpapi/ip_icmp.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright (c) 1982, 1986, 1993
- *	The Regents of the University of California.  All rights reserved.
- *
- * 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.
- *
- *	@(#)ip_icmp.h	8.1 (Berkeley) 6/10/93
- * $FreeBSD: src/sys/netinet/ip_icmp.h,v 1.13 1999/08/28 00:49:24 peter Exp $
- */
-
-#ifndef _NETINET_IP_ICMP_H_
-#define _NETINET_IP_ICMP_H_
-
-/*
- * Interface Control Message Protocol Definitions.
- * Per RFC 792, September 1981.
- */
-
-/*
- * Internal of an ICMP Router Advertisement
- */
-struct icmp_ra_addr {
-	u_int32_t ira_addr;
-	u_int32_t ira_preference;
-};
-
-/*
- * Structure of an icmp header.
- */
-struct icmp {
-	u_char	icmp_type;		/* type of message, see below */
-	u_char	icmp_code;		/* type sub code */
-	u_short	icmp_cksum;		/* ones complement cksum of struct */
-	union {
-		u_char ih_pptr;			/* ICMP_PARAMPROB */
-		struct in_addr ih_gwaddr;	/* ICMP_REDIRECT */
-		struct ih_idseq {
-			n_short	icd_id;
-			n_short	icd_seq;
-		} ih_idseq;
-		int ih_void;
-
-		/* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
-		struct ih_pmtu {
-			n_short ipm_void;
-			n_short ipm_nextmtu;
-		} ih_pmtu;
-
-		struct ih_rtradv {
-			u_char irt_num_addrs;
-			u_char irt_wpa;
-			u_int16_t irt_lifetime;
-		} ih_rtradv;
-	} icmp_hun;
-#define	icmp_pptr	icmp_hun.ih_pptr
-#define	icmp_gwaddr	icmp_hun.ih_gwaddr
-#define	icmp_id		icmp_hun.ih_idseq.icd_id
-#define	icmp_seq	icmp_hun.ih_idseq.icd_seq
-#define	icmp_void	icmp_hun.ih_void
-#define	icmp_pmvoid	icmp_hun.ih_pmtu.ipm_void
-#define	icmp_nextmtu	icmp_hun.ih_pmtu.ipm_nextmtu
-#define	icmp_num_addrs	icmp_hun.ih_rtradv.irt_num_addrs
-#define	icmp_wpa	icmp_hun.ih_rtradv.irt_wpa
-#define	icmp_lifetime	icmp_hun.ih_rtradv.irt_lifetime
-	union {
-		struct id_ts {
-			n_time its_otime;
-			n_time its_rtime;
-			n_time its_ttime;
-		} id_ts;
-		struct id_ip  {
-			struct ip idi_ip;
-			/* options and then 64 bits of data */
-		} id_ip;
-		struct icmp_ra_addr id_radv;
-		u_int32_t id_mask;
-		char	id_data[1];
-	} icmp_dun;
-#define	icmp_otime	icmp_dun.id_ts.its_otime
-#define	icmp_rtime	icmp_dun.id_ts.its_rtime
-#define	icmp_ttime	icmp_dun.id_ts.its_ttime
-#define	icmp_ip		icmp_dun.id_ip.idi_ip
-#define	icmp_radv	icmp_dun.id_radv
-#define	icmp_mask	icmp_dun.id_mask
-#define	icmp_data	icmp_dun.id_data
-};
-
-/*
- * Lower bounds on packet lengths for various types.
- * For the error advice packets must first insure that the
- * packet is large enough to contain the returned ip header.
- * Only then can we do the check to see if 64 bits of packet
- * data have been returned, since we need to check the returned
- * ip header length.
- */
-#define	ICMP_MINLEN	8				/* abs minimum */
-#define	ICMP_TSLEN	(8 + 3 * sizeof (n_time))	/* timestamp */
-#define	ICMP_MASKLEN	12				/* address mask */
-#define	ICMP_ADVLENMIN	(8 + sizeof (struct ip) + 8)	/* min */
-#ifndef _IP_VHL
-#define	ICMP_ADVLEN(p)	(8 + ((p)->icmp_ip.ip_hl << 2) + 8)
-	/* N.B.: must separately check that ip_hl >= 5 */
-#else
-#define	ICMP_ADVLEN(p)	(8 + (IP_VHL_HL((p)->icmp_ip.ip_vhl) << 2) + 8)
-	/* N.B.: must separately check that header length >= 5 */
-#endif
-
-/*
- * Definition of type and code field values.
- */
-#define	ICMP_ECHOREPLY		0		/* echo reply */
-#define	ICMP_UNREACH		3		/* dest unreachable, codes: */
-#define		ICMP_UNREACH_NET	0		/* bad net */
-#define		ICMP_UNREACH_HOST	1		/* bad host */
-#define		ICMP_UNREACH_PROTOCOL	2		/* bad protocol */
-#define		ICMP_UNREACH_PORT	3		/* bad port */
-#define		ICMP_UNREACH_NEEDFRAG	4		/* IP_DF caused drop */
-#define		ICMP_UNREACH_SRCFAIL	5		/* src route failed */
-#define		ICMP_UNREACH_NET_UNKNOWN 6		/* unknown net */
-#define		ICMP_UNREACH_HOST_UNKNOWN 7		/* unknown host */
-#define		ICMP_UNREACH_ISOLATED	8		/* src host isolated */
-#define		ICMP_UNREACH_NET_PROHIB	9		/* prohibited access */
-#define		ICMP_UNREACH_HOST_PROHIB 10		/* ditto */
-#define		ICMP_UNREACH_TOSNET	11		/* bad tos for net */
-#define		ICMP_UNREACH_TOSHOST	12		/* bad tos for host */
-#define		ICMP_UNREACH_FILTER_PROHIB 13		/* admin prohib */
-#define		ICMP_UNREACH_HOST_PRECEDENCE 14		/* host prec vio. */
-#define		ICMP_UNREACH_PRECEDENCE_CUTOFF 15	/* prec cutoff */
-#define	ICMP_SOURCEQUENCH	4		/* packet lost, slow down */
-#define	ICMP_REDIRECT		5		/* shorter route, codes: */
-#define		ICMP_REDIRECT_NET	0		/* for network */
-#define		ICMP_REDIRECT_HOST	1		/* for host */
-#define		ICMP_REDIRECT_TOSNET	2		/* for tos and net */
-#define		ICMP_REDIRECT_TOSHOST	3		/* for tos and host */
-#define	ICMP_ECHO		8		/* echo service */
-#define	ICMP_ROUTERADVERT	9		/* router advertisement */
-#define	ICMP_ROUTERSOLICIT	10		/* router solicitation */
-#define	ICMP_TIMXCEED		11		/* time exceeded, code: */
-#define		ICMP_TIMXCEED_INTRANS	0		/* ttl==0 in transit */
-#define		ICMP_TIMXCEED_REASS	1		/* ttl==0 in reass */
-#define	ICMP_PARAMPROB		12		/* ip header bad */
-#define		ICMP_PARAMPROB_OPTABSENT 1		/* req. opt. absent */
-#define	ICMP_TSTAMP		13		/* timestamp request */
-#define	ICMP_TSTAMPREPLY	14		/* timestamp reply */
-#define	ICMP_IREQ		15		/* information request */
-#define	ICMP_IREQREPLY		16		/* information reply */
-#define	ICMP_MASKREQ		17		/* address mask request */
-#define	ICMP_MASKREPLY		18		/* address mask reply */
-
-#define	ICMP_MAXTYPE		18
-
-#define	ICMP_INFOTYPE(type) \
-	((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
-	(type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \
-	(type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
-	(type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
-	(type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
-
-#ifdef KERNEL
-void	icmp_error __P((struct mbuf *, int, int, n_long, struct ifnet *));
-void	icmp_input __P((struct mbuf *, int));
-#endif
-
-#endif
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c
index 2286c732383..6cdf7948901 100644
--- a/dlls/iphlpapi/iphlpapi_main.c
+++ b/dlls/iphlpapi/iphlpapi_main.c
@@ -37,6 +37,7 @@
 #include "tcpestats.h"
 #include "ip2string.h"
 #include "netiodef.h"
+#include "icmpapi.h"
 
 #include "wine/nsi.h"
 #include "wine/debug.h"
@@ -4543,6 +4544,48 @@ DWORD WINAPI ParseNetworkString(const WCHAR *str, DWORD type,
     return ERROR_INVALID_PARAMETER;
 }
 
+struct icmp_handle_data
+{
+    HANDLE nsi_device;
+};
+
+/***********************************************************************
+ *    IcmpCloseHandle (IPHLPAPI.@)
+ */
+BOOL WINAPI IcmpCloseHandle( HANDLE handle )
+{
+    struct icmp_handle_data *data = (struct icmp_handle_data *)handle;
+
+    CloseHandle( data->nsi_device );
+    heap_free( data );
+    return TRUE;
+}
+
+/***********************************************************************
+ *    IcmpCreateFile (IPHLPAPI.@)
+ */
+HANDLE WINAPI IcmpCreateFile( void )
+{
+    struct icmp_handle_data *data = heap_alloc( sizeof(*data) );
+    static const WCHAR device_name[] = {'\\','\\','.','\\','N','s','i',0};
+
+    if (!data)
+    {
+        SetLastError( IP_NO_RESOURCES );
+        return INVALID_HANDLE_VALUE;
+    }
+
+    data->nsi_device = CreateFileW( device_name, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+                                    FILE_FLAG_OVERLAPPED, NULL );
+    if (data->nsi_device == INVALID_HANDLE_VALUE)
+    {
+        heap_free( data );
+        return INVALID_HANDLE_VALUE;
+    }
+
+    return (HANDLE)data;
+}
+
 /******************************************************************
  *    IcmpParseReplies (IPHLPAPI.@)
  */
@@ -4556,6 +4599,135 @@ DWORD WINAPI IcmpParseReplies( void *reply, DWORD reply_size )
     return num_pkts;
 }
 
+/*************************************************************************
+ *    icmpv4_echo_reply_fixup
+ *
+ * Convert struct nsiproxy_icmpv4_echo_reply into ICMP_ECHO_REPLY.
+ *
+ * This is necessary due to the different sizes of ICMP_ECHO_REPLY on
+ * 32 and 64-bits.  Despite mention of ICMP_ECHO_REPLY32, 64-bit Windows
+ * actually does return a full 64-bit version.
+ */
+static void icmpv4_echo_reply_fixup( ICMP_ECHO_REPLY *dst, struct nsiproxy_icmp_echo_reply *reply )
+{
+    dst->Address = reply->addr.Ipv4.sin_addr.s_addr;
+    dst->Status = reply->status;
+    dst->RoundTripTime = reply->round_trip_time;
+    dst->DataSize = reply->data_size;
+    dst->Reserved = reply->num_of_pkts;
+    dst->Data = (BYTE *)(dst + 1) + ((reply->opts.options_size + 3) & ~3);
+    dst->Options.Ttl = reply->opts.ttl;
+    dst->Options.Tos = reply->opts.tos;
+    dst->Options.Flags = reply->opts.flags;
+    dst->Options.OptionsSize = reply->opts.options_size;
+    dst->Options.OptionsData = (BYTE *)(reply + 1);
+
+    memcpy( dst->Options.OptionsData, (BYTE *)reply + reply->opts.options_offset, reply->opts.options_size );
+    memcpy( dst->Data, (BYTE *)reply + reply->data_offset, reply->data_size );
+}
+
+/***********************************************************************
+ *    IcmpSendEcho (IPHLPAPI.@)
+ */
+DWORD WINAPI IcmpSendEcho( HANDLE handle, IPAddr dst, void *request, WORD request_size,
+                           IP_OPTION_INFORMATION *opts, void *reply, DWORD reply_size,
+                           DWORD timeout )
+{
+    return IcmpSendEcho2Ex( handle, NULL, NULL, NULL, INADDR_ANY, dst, request, request_size,
+                            opts, reply, reply_size, timeout );
+}
+
+/***********************************************************************
+ *    IcmpSendEcho2 (IPHLPAPI.@)
+ */
+DWORD WINAPI IcmpSendEcho2( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine, void *apc_ctxt,
+                            IPAddr dst, void *request, WORD request_size, IP_OPTION_INFORMATION *opts,
+                            void *reply, DWORD reply_size, DWORD timeout )
+{
+    return IcmpSendEcho2Ex( handle, event, apc_routine, apc_ctxt, INADDR_ANY, dst, request, request_size,
+                            opts, reply, reply_size, timeout );
+}
+
+/***********************************************************************
+ *    IcmpSendEcho2Ex (IPHLPAPI.@)
+ */
+DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_routine, void *apc_ctxt,
+                              IPAddr src, IPAddr dst, void *request, WORD request_size, IP_OPTION_INFORMATION *opts,
+                              void *reply, DWORD reply_size, DWORD timeout )
+{
+    struct icmp_handle_data *data = (struct icmp_handle_data *)handle;
+    DWORD opt_size, in_size, ret = 0, out_size;
+    struct nsiproxy_icmp_echo *in;
+    struct nsiproxy_icmp_echo_reply *out;
+    HANDLE request_event;
+    IO_STATUS_BLOCK iosb;
+    NTSTATUS status;
+
+    if (event || apc_routine)
+    {
+        FIXME( "Async requests not yet supported\n" );
+        return 0;
+    }
+
+    if (handle == INVALID_HANDLE_VALUE || !reply)
+    {
+        SetLastError( ERROR_INVALID_PARAMETER );
+        return 0;
+    }
+
+    opt_size = opts ? (opts->OptionsSize + 3) & ~3 : 0;
+    in_size = FIELD_OFFSET(struct nsiproxy_icmp_echo, data[opt_size + request_size]);
+    in = heap_alloc_zero( in_size );
+    out_size = reply_size - sizeof(ICMP_ECHO_REPLY) + sizeof(*out);
+    out = heap_alloc( out_size );
+
+    if (!in || !out)
+    {
+        heap_free( out );
+        heap_free( in );
+        SetLastError( IP_NO_RESOURCES );
+        return 0;
+    }
+
+    in->src.Ipv4.sin_family = AF_INET;
+    in->src.Ipv4.sin_addr.s_addr = src;
+    in->dst.Ipv4.sin_family = AF_INET;
+    in->dst.Ipv4.sin_addr.s_addr = dst;
+    if (opts)
+    {
+        in->ttl = opts->Ttl;
+        in->tos = opts->Tos;
+        in->flags = opts->Flags;
+        memcpy( in->data, opts->OptionsData, opts->OptionsSize );
+        in->opt_size = opts->OptionsSize;
+    }
+    in->req_size = request_size;
+    in->timeout = timeout;
+    memcpy( in->data + opt_size, request, request_size );
+
+    request_event = CreateEventW( NULL, 0, 0, NULL );
+
+    status = NtDeviceIoControlFile( data->nsi_device, request_event, NULL, NULL,
+                                    &iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, in, in_size,
+                                    out, out_size );
+
+    if (status == STATUS_PENDING && !WaitForSingleObject( request_event, INFINITE ))
+        status = iosb.u.Status;
+
+    if (!status)
+    {
+        icmpv4_echo_reply_fixup( reply, out );
+        ret = IcmpParseReplies( reply, reply_size );
+    }
+
+    CloseHandle( request_event );
+    heap_free( out );
+    heap_free( in );
+
+    if (status) SetLastError( RtlNtStatusToDosError( status ) );
+    return ret;
+}
+
 /***********************************************************************
  *    Icmp6CreateFile (IPHLPAPI.@)
  */
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c
index 080feb364f9..e20ada88f16 100644
--- a/dlls/iphlpapi/tests/iphlpapi.c
+++ b/dlls/iphlpapi/tests/iphlpapi.c
@@ -894,15 +894,6 @@ static void testIcmpSendEcho(void)
         "expected 87, got %d\n", error);
 
     icmp = IcmpCreateFile();
-    if (icmp == INVALID_HANDLE_VALUE)
-    {
-        error = GetLastError();
-        if (error == ERROR_ACCESS_DENIED)
-        {
-            skip ("ICMP is not available.\n");
-            return;
-        }
-    }
     ok (icmp != INVALID_HANDLE_VALUE, "IcmpCreateFile failed unexpectedly with error %d\n", GetLastError());
 
     address = 0;
@@ -924,6 +915,11 @@ static void testIcmpSendEcho(void)
     SetLastError(0xdeadbeef);
     ret = IcmpSendEcho(icmp, address, senddata, 0, NULL, replydata, replysz, 1000);
     error = GetLastError();
+    if (!ret && error == ERROR_ACCESS_DENIED)
+    {
+        skip( "ICMP is not available.\n" );
+        return;
+    }
     ok (ret, "IcmpSendEcho failed unexpectedly with error %d\n", error);
 
     SetLastError(0xdeadbeef);
-- 
2.25.1




More information about the wine-devel mailing list