mirror of
git://source.winehq.org/git/wine.git
synced 2024-07-21 11:14:09 +00:00
nsiproxy: Actually send the ICMP ECHO request.
However, don't yet wait for the reply. Signed-off-by: Huw Davies <huw@codeweavers.com> Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
parent
f59a64144b
commit
aa4e0d466b
|
@ -7,6 +7,7 @@ EXTRADLLFLAGS = -Wl,--subsystem,native
|
|||
|
||||
C_SRCS = \
|
||||
device.c \
|
||||
icmp_echo.c \
|
||||
ip.c \
|
||||
ndis.c \
|
||||
nsi.c \
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
#include "wine/debug.h"
|
||||
#include "wine/unixlib.h"
|
||||
|
||||
#include "nsiproxy_private.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(nsi);
|
||||
|
||||
static unixlib_handle_t nsiproxy_handle;
|
||||
|
@ -56,6 +58,7 @@ static NTSTATUS nsiproxy_call( unsigned int code, void *args )
|
|||
|
||||
enum unix_calls
|
||||
{
|
||||
icmp_send_echo,
|
||||
nsi_enumerate_all_ex,
|
||||
nsi_get_all_parameters_ex,
|
||||
nsi_get_parameter_ex,
|
||||
|
@ -289,6 +292,36 @@ static int add_device( DRIVER_OBJECT *driver )
|
|||
return 1;
|
||||
}
|
||||
|
||||
static void handle_queued_send_echo( IRP *irp )
|
||||
{
|
||||
struct nsiproxy_icmp_echo *in = (struct nsiproxy_icmp_echo *)irp->AssociatedIrp.SystemBuffer;
|
||||
struct nsiproxy_icmp_echo_reply *reply = (struct nsiproxy_icmp_echo_reply *)irp->AssociatedIrp.SystemBuffer;
|
||||
struct icmp_send_echo_params params;
|
||||
NTSTATUS status;
|
||||
|
||||
TRACE( "\n" );
|
||||
params.request = in->data + ((in->opt_size + 3) & ~3);
|
||||
params.request_size = in->req_size;
|
||||
params.ttl = in->ttl;
|
||||
params.tos = in->tos;
|
||||
params.dst = &in->dst;
|
||||
|
||||
status = nsiproxy_call( icmp_send_echo, ¶ms );
|
||||
TRACE( "icmp_send_echo rets %08x\n", status );
|
||||
|
||||
if (1)
|
||||
{
|
||||
irp->IoStatus.Status = status;
|
||||
if (status == STATUS_SUCCESS)
|
||||
{
|
||||
memset( reply, 0, sizeof(*reply) );
|
||||
reply->status = params.ip_status;
|
||||
irp->IoStatus.Information = sizeof(*reply);
|
||||
}
|
||||
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD WINAPI request_thread_proc( void *arg )
|
||||
{
|
||||
LIST_ENTRY *entry;
|
||||
|
@ -309,9 +342,7 @@ static DWORD WINAPI request_thread_proc( void *arg )
|
|||
continue;
|
||||
}
|
||||
|
||||
/* FIXME */
|
||||
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
||||
IoCompleteRequest( irp, IO_NO_INCREMENT );
|
||||
handle_queued_send_echo( irp );
|
||||
}
|
||||
LeaveCriticalSection( &nsiproxy_cs );
|
||||
}
|
||||
|
|
321
dlls/nsiproxy.sys/icmp_echo.c
Normal file
321
dlls/nsiproxy.sys/icmp_echo.c
Normal file
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* nsiproxy.sys icmp_echo implementation
|
||||
*
|
||||
* Copyright 2021 Huw Davies
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
#if 0
|
||||
#pragma makedep unix
|
||||
#endif
|
||||
|
||||
#define _NTSYSTEM_
|
||||
|
||||
#include "config.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#ifdef HAVE_SYS_SOCKET_H
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETINET_IN_H
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETINET_IP_H
|
||||
#include <netinet/ip.h>
|
||||
#endif
|
||||
|
||||
#include "ntstatus.h"
|
||||
#define WIN32_NO_STATUS
|
||||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "winternl.h"
|
||||
#include "winioctl.h"
|
||||
#define USE_WS_PREFIX
|
||||
#include "ddk/wdm.h"
|
||||
#include "ifdef.h"
|
||||
#include "netiodef.h"
|
||||
#include "ipexport.h"
|
||||
#include "ipmib.h"
|
||||
#include "wine/nsi.h"
|
||||
#include "wine/debug.h"
|
||||
|
||||
#include "nsiproxy_private.h"
|
||||
|
||||
WINE_DEFAULT_DEBUG_CHANNEL(nsi);
|
||||
|
||||
static LONG icmp_sequence;
|
||||
|
||||
struct ip_hdr
|
||||
{
|
||||
uint8_t v_hl; /* version << 4 | hdr_len */
|
||||
uint8_t tos;
|
||||
uint16_t tot_len;
|
||||
uint16_t id;
|
||||
uint16_t frag_off;
|
||||
uint8_t ttl;
|
||||
uint8_t protocol;
|
||||
uint16_t checksum;
|
||||
uint32_t saddr;
|
||||
uint32_t daddr;
|
||||
};
|
||||
|
||||
struct icmp_hdr
|
||||
{
|
||||
uint8_t type;
|
||||
uint8_t code;
|
||||
uint16_t checksum;
|
||||
union
|
||||
{
|
||||
struct
|
||||
{
|
||||
uint16_t id;
|
||||
uint16_t sequence;
|
||||
} echo;
|
||||
} un;
|
||||
};
|
||||
|
||||
struct family_ops;
|
||||
struct icmp_data
|
||||
{
|
||||
LARGE_INTEGER send_time;
|
||||
int socket;
|
||||
unsigned short id;
|
||||
unsigned short seq;
|
||||
const struct family_ops *ops;
|
||||
};
|
||||
|
||||
static void ipv4_init_icmp_hdr( struct icmp_data *data, struct icmp_hdr *icmp_hdr )
|
||||
{
|
||||
icmp_hdr->type = ICMP4_ECHO_REQUEST;
|
||||
icmp_hdr->code = 0;
|
||||
icmp_hdr->checksum = 0;
|
||||
icmp_hdr->un.echo.id = data->id = getpid() & 0xffff; /* will be overwritten for linux ping socks */
|
||||
icmp_hdr->un.echo.sequence = data->seq = InterlockedIncrement( &icmp_sequence ) & 0xffff;
|
||||
}
|
||||
|
||||
/* rfc 1071 checksum */
|
||||
static unsigned short chksum( BYTE *data, unsigned int count )
|
||||
{
|
||||
unsigned int sum = 0, carry = 0;
|
||||
unsigned short check, s;
|
||||
|
||||
while (count > 1)
|
||||
{
|
||||
s = *(unsigned short *)data;
|
||||
data += 2;
|
||||
sum += carry;
|
||||
sum += s;
|
||||
carry = s > sum;
|
||||
count -= 2;
|
||||
}
|
||||
sum += carry; /* This won't produce another carry */
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
if (count) sum += *data; /* LE-only */
|
||||
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
/* fold in any carry */
|
||||
sum = (sum & 0xffff) + (sum >> 16);
|
||||
|
||||
check = ~sum;
|
||||
return check;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static unsigned short null_chksum( BYTE *data, unsigned int count )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ipv4_set_socket_opts( struct icmp_data *data, struct icmp_send_echo_params *params )
|
||||
{
|
||||
int val;
|
||||
|
||||
val = params->ttl;
|
||||
if (val) setsockopt( data->socket, IPPROTO_IP, IP_TTL, &val, sizeof(val) );
|
||||
val = params->tos;
|
||||
if (val) setsockopt( data->socket, IPPROTO_IP, IP_TOS, &val, sizeof(val) );
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static void ipv4_linux_ping_set_socket_opts( struct icmp_data *data, struct icmp_send_echo_params *params )
|
||||
{
|
||||
static const int val = 1;
|
||||
|
||||
ipv4_set_socket_opts( data, params );
|
||||
|
||||
setsockopt( data->socket, IPPROTO_IP, IP_RECVTTL, &val, sizeof(val) );
|
||||
setsockopt( data->socket, IPPROTO_IP, IP_RECVTOS, &val, sizeof(val) );
|
||||
}
|
||||
#endif
|
||||
|
||||
struct family_ops
|
||||
{
|
||||
int family;
|
||||
int icmp_protocol;
|
||||
void (*init_icmp_hdr)( struct icmp_data *data, struct icmp_hdr *icmp_hdr );
|
||||
unsigned short (*chksum)( BYTE *data, unsigned int count );
|
||||
void (*set_socket_opts)( struct icmp_data *data, struct icmp_send_echo_params *params );
|
||||
};
|
||||
|
||||
static const struct family_ops ipv4 =
|
||||
{
|
||||
AF_INET,
|
||||
IPPROTO_ICMP,
|
||||
ipv4_init_icmp_hdr,
|
||||
chksum,
|
||||
ipv4_set_socket_opts,
|
||||
};
|
||||
|
||||
#ifdef __linux__
|
||||
/* linux ipv4 ping sockets behave more like ipv6 raw sockets */
|
||||
static const struct family_ops ipv4_linux_ping =
|
||||
{
|
||||
AF_INET,
|
||||
IPPROTO_ICMP,
|
||||
ipv4_init_icmp_hdr,
|
||||
null_chksum,
|
||||
ipv4_linux_ping_set_socket_opts,
|
||||
};
|
||||
#endif
|
||||
|
||||
static IP_STATUS errno_to_ip_status( int err )
|
||||
{
|
||||
switch( err )
|
||||
{
|
||||
case EHOSTUNREACH: return IP_DEST_HOST_UNREACHABLE;
|
||||
default: return IP_GENERAL_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
static int SOCKADDR_INET_to_sockaddr( const SOCKADDR_INET *in, struct sockaddr *out, int len )
|
||||
{
|
||||
switch (in->si_family)
|
||||
{
|
||||
case WS_AF_INET:
|
||||
{
|
||||
struct sockaddr_in *sa = (struct sockaddr_in *)out;
|
||||
|
||||
if (len < sizeof(*sa)) return 0;
|
||||
sa->sin_family = AF_INET;
|
||||
sa->sin_port = in->Ipv4.sin_port;
|
||||
sa->sin_addr.s_addr = in->Ipv4.sin_addr.WS_s_addr;
|
||||
return sizeof(*sa);
|
||||
}
|
||||
case WS_AF_INET6:
|
||||
{
|
||||
struct sockaddr_in6 *sa = (struct sockaddr_in6 *)out;
|
||||
|
||||
if (len < sizeof(*sa)) return 0;
|
||||
sa->sin6_family = AF_INET6;
|
||||
sa->sin6_port = in->Ipv6.sin6_port;
|
||||
sa->sin6_flowinfo = in->Ipv6.sin6_flowinfo;
|
||||
memcpy( sa->sin6_addr.s6_addr, in->Ipv6.sin6_addr.WS_s6_addr, sizeof(sa->sin6_addr.s6_addr) );
|
||||
sa->sin6_scope_id = in->Ipv6.sin6_scope_id;
|
||||
return sizeof(*sa);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NTSTATUS icmp_data_create( ADDRESS_FAMILY win_family, struct icmp_data **icmp_data )
|
||||
{
|
||||
struct icmp_data *data;
|
||||
const struct family_ops *ops;
|
||||
|
||||
if (win_family == WS_AF_INET) ops = &ipv4;
|
||||
else return STATUS_INVALID_PARAMETER;
|
||||
|
||||
data = malloc( sizeof(*data) );
|
||||
if (!data) return STATUS_NO_MEMORY;
|
||||
|
||||
data->socket = socket( ops->family, SOCK_RAW, ops->icmp_protocol );
|
||||
if (data->socket < 0) /* Try a ping-socket */
|
||||
{
|
||||
TRACE( "failed to open raw sock, trying a dgram sock\n" );
|
||||
data->socket = socket( ops->family, SOCK_DGRAM, ops->icmp_protocol );
|
||||
if (data->socket < 0)
|
||||
{
|
||||
WARN( "Unable to create socket\n" );
|
||||
free( data );
|
||||
return STATUS_ACCESS_DENIED;
|
||||
}
|
||||
#ifdef __linux__
|
||||
if (ops->family == AF_INET) ops = &ipv4_linux_ping;
|
||||
#endif
|
||||
}
|
||||
data->ops = ops;
|
||||
|
||||
*icmp_data = data;
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void icmp_data_free( struct icmp_data *data )
|
||||
{
|
||||
close( data->socket );
|
||||
free( data );
|
||||
}
|
||||
|
||||
NTSTATUS icmp_send_echo( void *args )
|
||||
{
|
||||
struct icmp_send_echo_params *params = args;
|
||||
struct icmp_hdr *icmp_hdr; /* this is the same for both ipv4 and ipv6 */
|
||||
struct sockaddr_storage dst_storage;
|
||||
struct sockaddr *dst = (struct sockaddr *)&dst_storage;
|
||||
struct icmp_data *data;
|
||||
int dst_len, ret;
|
||||
NTSTATUS status;
|
||||
|
||||
status = icmp_data_create( params->dst->si_family, &data );
|
||||
if (status) return status;
|
||||
data->ops->set_socket_opts( data, params );
|
||||
|
||||
icmp_hdr = malloc( sizeof(*icmp_hdr) + params->request_size );
|
||||
if (!icmp_hdr)
|
||||
{
|
||||
icmp_data_free( data );
|
||||
return STATUS_NO_MEMORY;
|
||||
}
|
||||
data->ops->init_icmp_hdr( data, icmp_hdr );
|
||||
memcpy( icmp_hdr + 1, params->request, params->request_size );
|
||||
icmp_hdr->checksum = data->ops->chksum( (BYTE *)icmp_hdr, sizeof(*icmp_hdr) + params->request_size );
|
||||
|
||||
dst_len = SOCKADDR_INET_to_sockaddr( params->dst, dst, sizeof(dst_storage) );
|
||||
|
||||
NtQueryPerformanceCounter( &data->send_time, NULL );
|
||||
ret = sendto( data->socket, icmp_hdr, sizeof(*icmp_hdr) + params->request_size, 0, dst, dst_len );
|
||||
free( icmp_hdr );
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
TRACE( "sendto() rets %d errno %d\n", ret, errno );
|
||||
icmp_data_free( data );
|
||||
params->ip_status = errno_to_ip_status( errno );
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
/* FIXME */
|
||||
icmp_data_free( data );
|
||||
return STATUS_NOT_SUPPORTED;
|
||||
}
|
|
@ -147,6 +147,7 @@ static NTSTATUS unix_nsi_get_parameter_ex( void *args )
|
|||
|
||||
const unixlib_entry_t __wine_unix_call_funcs[] =
|
||||
{
|
||||
icmp_send_echo,
|
||||
unix_nsi_enumerate_all_ex,
|
||||
unix_nsi_get_all_parameters_ex,
|
||||
unix_nsi_get_parameter_ex
|
||||
|
|
28
dlls/nsiproxy.sys/nsiproxy_private.h
Normal file
28
dlls/nsiproxy.sys/nsiproxy_private.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* nsiproxy.sys
|
||||
*
|
||||
* Copyright 2021 Huw Davies
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
struct icmp_send_echo_params
|
||||
{
|
||||
SOCKADDR_INET *dst;
|
||||
void *request;
|
||||
DWORD request_size;
|
||||
BYTE ttl, tos;
|
||||
ULONG ip_status;
|
||||
};
|
|
@ -156,3 +156,5 @@ static inline int ascii_strcasecmp( const char *s1, const char *s2 )
|
|||
{
|
||||
return ascii_strncasecmp( s1, s2, -1 );
|
||||
}
|
||||
|
||||
NTSTATUS icmp_send_echo( void *args ) DECLSPEC_HIDDEN;
|
||||
|
|
Loading…
Reference in a new issue