iphlpapi: Implement IcmpSendEcho2Ex() using nsiproxy.

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@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Huw Davies 2021-10-06 10:12:58 +01:00 committed by Alexandre Julliard
parent 9d8abb1ee5
commit 7156d2b1e4
6 changed files with 177 additions and 1019 deletions

View File

@ -5,7 +5,6 @@ IMPORTS = advapi32 dnsapi nsi uuid
EXTRADLLFLAGS = -mcygwin
C_SRCS = \
icmp.c \
iphlpapi_main.c
RC_SRCS = version.rc

View File

@ -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.
*
*/

View File

@ -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

View File

@ -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

View File

@ -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.@)
*/

View File

@ -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);