[PATCH v3 1/3] nsiproxy: Convert the reply to ICMP_ECHO_REPLY in the output buffer.

Gabriel Ivăncescu gabrielopcode at gmail.com
Mon Oct 11 10:14:38 CDT 2021


Because iphlpapi has no opportunity to convert the buffer in async mode.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---

Handles architecture differences and family (can be easily extended to
support IPv6's struct).

 dlls/iphlpapi/iphlpapi_main.c        |  44 ++----------
 dlls/nsiproxy.sys/device.c           | 103 ++++++++++++++++++++++++---
 dlls/nsiproxy.sys/nsiproxy_private.h |  55 ++++++++++++++
 include/wine/nsi.h                   |  21 +-----
 4 files changed, 155 insertions(+), 68 deletions(-)

diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c
index d9a85be..a024224 100644
--- a/dlls/iphlpapi/iphlpapi_main.c
+++ b/dlls/iphlpapi/iphlpapi_main.c
@@ -4579,33 +4579,6 @@ 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.@)
  */
@@ -4636,9 +4609,8 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r
                               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;
+    DWORD opt_size, in_size, ret = 0;
     struct nsiproxy_icmp_echo *in;
-    struct nsiproxy_icmp_echo_reply *out;
     HANDLE request_event;
     IO_STATUS_BLOCK iosb;
     NTSTATUS status;
@@ -4658,17 +4630,15 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r
     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)
+    if (!in)
     {
-        heap_free( out );
-        heap_free( in );
         SetLastError( IP_NO_RESOURCES );
         return 0;
     }
 
+    in->addr = (ULONG_PTR)reply;
+    in->bits = sizeof(void*) * 8;
     in->src.Ipv4.sin_family = AF_INET;
     in->src.Ipv4.sin_addr.s_addr = src;
     in->dst.Ipv4.sin_family = AF_INET;
@@ -4689,19 +4659,15 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r
 
     status = NtDeviceIoControlFile( data->nsi_device, request_event, NULL, NULL,
                                     &iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, in, in_size,
-                                    out, out_size );
+                                    reply, reply_size );
 
     if (status == STATUS_PENDING && !WaitForSingleObject( request_event, INFINITE ))
         status = iosb.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 ) );
diff --git a/dlls/nsiproxy.sys/device.c b/dlls/nsiproxy.sys/device.c
index 90a7a3d..e310805 100644
--- a/dlls/nsiproxy.sys/device.c
+++ b/dlls/nsiproxy.sys/device.c
@@ -31,6 +31,7 @@
 #include "netiodef.h"
 #include "wine/nsi.h"
 #include "wine/debug.h"
+#include "wine/heap.h"
 #include "wine/unixlib.h"
 
 #include "nsiproxy_private.h"
@@ -211,6 +212,57 @@ static void WINAPI icmp_echo_cancel( DEVICE_OBJECT *device, IRP *irp )
     LeaveCriticalSection( &nsiproxy_cs );
 }
 
+static void icmp_echo_reply_fixup( void *out, struct nsiproxy_icmp_echo_reply *reply, ULONGLONG addr, ULONG family, ULONG bits )
+{
+    void *data = out;
+    if (family == AF_INET)
+    {
+        void *options_data;
+        if (bits == 32)
+        {
+            struct icmp_echo_reply_32 *dst = out;
+            dst->addr = reply->addr.Ipv4.sin_addr.s_addr;
+            dst->status = reply->status;
+            dst->round_trip_time = reply->round_trip_time;
+            dst->data_size = reply->data_size;
+            dst->num_of_pkts = reply->num_of_pkts;
+            dst->data_ptr = addr + sizeof(*dst) + ((reply->opts.options_size + 3) & ~3);
+            dst->opts.ttl = reply->opts.ttl;
+            dst->opts.tos = reply->opts.tos;
+            dst->opts.flags = reply->opts.flags;
+            dst->opts.options_size = reply->opts.options_size;
+            dst->opts.options_ptr = addr + sizeof(*dst);
+            options_data = dst + 1;
+        }
+        else
+        {
+            struct icmp_echo_reply_64 *dst = out;
+            dst->addr = reply->addr.Ipv4.sin_addr.s_addr;
+            dst->status = reply->status;
+            dst->round_trip_time = reply->round_trip_time;
+            dst->data_size = reply->data_size;
+            dst->num_of_pkts = reply->num_of_pkts;
+            dst->data_ptr = addr + sizeof(*dst) + ((reply->opts.options_size + 3) & ~3);
+            dst->opts.ttl = reply->opts.ttl;
+            dst->opts.tos = reply->opts.tos;
+            dst->opts.flags = reply->opts.flags;
+            dst->opts.options_size = reply->opts.options_size;
+            dst->opts.options_ptr = addr + sizeof(*dst);
+            options_data = dst + 1;
+        }
+        memcpy( options_data, (BYTE *)reply + reply->opts.options_offset, reply->opts.options_size );
+        data = (BYTE *)options_data + ((reply->opts.options_size + 3) & ~3);
+    }
+    memcpy( data, (BYTE *)reply + reply->data_offset, reply->data_size );
+}
+
+static int icmp_echo_reply_struct_len( ULONG family, ULONG bits )
+{
+    if (family == AF_INET)
+        return (bits == 32) ? sizeof(struct icmp_echo_reply_32) : sizeof(struct icmp_echo_reply_64);
+    return 0;
+}
+
 static NTSTATUS nsiproxy_icmp_echo( IRP *irp )
 {
     IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
@@ -222,7 +274,7 @@ static NTSTATUS nsiproxy_icmp_echo( IRP *irp )
 
     if (in_len < offsetof(struct nsiproxy_icmp_echo, data[0]) ||
         in_len < offsetof(struct nsiproxy_icmp_echo, data[((in->opt_size + 3) & ~3) + in->req_size]) ||
-        out_len < sizeof(struct nsiproxy_icmp_echo_reply))
+        out_len < icmp_echo_reply_struct_len( in->dst.si_family, in->bits ))
         return STATUS_INVALID_PARAMETER;
 
     switch (in->dst.si_family)
@@ -320,19 +372,34 @@ static DWORD WINAPI listen_thread_proc( void *arg )
 {
     IRP *irp = arg;
     IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation( irp );
+    unsigned int reply_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
     struct nsiproxy_icmp_echo *in = irp->AssociatedIrp.SystemBuffer;
     struct icmp_listen_params params;
+    ULONG family = in->dst.si_family;
+    ULONGLONG addr = in->addr;
+    ULONG bits = in->bits;
+    ULONG struct_len;
     NTSTATUS status;
 
     TRACE( "\n" );
 
+    struct_len = icmp_echo_reply_struct_len( family, bits );
     params.handle = irp_get_icmp_handle( irp );
     params.timeout = in->timeout;
-    params.reply = irp->AssociatedIrp.SystemBuffer;
-    params.reply_len = irpsp->Parameters.DeviceIoControl.OutputBufferLength;
+    params.reply_len = reply_len - struct_len + sizeof(struct nsiproxy_icmp_echo_reply);
+    params.reply = heap_alloc( params.reply_len );
 
-    status = nsiproxy_call( icmp_listen, &params );
-    TRACE( "icmp_listen rets %08x\n", status );
+    if (params.reply)
+    {
+        void *reply = irp->AssociatedIrp.SystemBuffer;
+        status = nsiproxy_call( icmp_listen, &params );
+        TRACE( "icmp_listen rets %08x\n", status );
+
+        icmp_echo_reply_fixup( reply, params.reply, addr, family, bits );
+        heap_free( params.reply );
+        reply_len = params.reply_len - sizeof(struct nsiproxy_icmp_echo_reply) + struct_len;
+    }
+    else status = STATUS_NO_MEMORY;
 
     EnterCriticalSection( &nsiproxy_cs );
 
@@ -340,7 +407,7 @@ static DWORD WINAPI listen_thread_proc( void *arg )
 
     irp->IoStatus.Status = status;
     if (status == STATUS_SUCCESS)
-        irp->IoStatus.Information = params.reply_len;
+        irp->IoStatus.Information = reply_len;
     else
         irp->IoStatus.Information = 0;
     IoCompleteRequest( irp, IO_NO_INCREMENT );
@@ -353,8 +420,10 @@ static DWORD WINAPI listen_thread_proc( void *arg )
 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;
+    void *out = irp->AssociatedIrp.SystemBuffer;
     struct icmp_send_echo_params params;
+    ULONG family = in->dst.si_family;
+    ULONG bits = in->bits;
     NTSTATUS status;
 
     TRACE( "\n" );
@@ -372,9 +441,23 @@ static void handle_queued_send_echo( IRP *irp )
         irp->IoStatus.Status = status;
         if (status == STATUS_SUCCESS)
         {
-            memset( reply, 0, sizeof(*reply) );
-            reply->status = params.ip_status;
-            irp->IoStatus.Information = sizeof(*reply);
+            if (family == AF_INET)
+            {
+                if (bits == 32)
+                {
+                    struct icmp_echo_reply_32 *reply = out;
+                    memset( reply, 0, sizeof(*reply) );
+                    reply->status = params.ip_status;
+                    irp->IoStatus.Information = sizeof(*reply);
+                }
+                else
+                {
+                    struct icmp_echo_reply_64 *reply = out;
+                    memset( reply, 0, sizeof(*reply) );
+                    reply->status = params.ip_status;
+                    irp->IoStatus.Information = sizeof(*reply);
+                }
+            }
         }
         IoCompleteRequest( irp, IO_NO_INCREMENT );
     }
diff --git a/dlls/nsiproxy.sys/nsiproxy_private.h b/dlls/nsiproxy.sys/nsiproxy_private.h
index 91dd393..0694757 100644
--- a/dlls/nsiproxy.sys/nsiproxy_private.h
+++ b/dlls/nsiproxy.sys/nsiproxy_private.h
@@ -34,3 +34,58 @@ struct icmp_send_echo_params
     HANDLE handle;
     ULONG ip_status;
 };
+
+/* output for IOCTL_NSIPROXY_WINE_ICMP_ECHO - cf. ICMP_ECHO_REPLY */
+struct nsiproxy_icmp_echo_reply
+{
+    SOCKADDR_INET addr;
+    ULONG status;
+    ULONG round_trip_time;
+    USHORT data_size;
+    USHORT num_of_pkts;
+    DWORD data_offset;
+    struct
+    {
+        BYTE ttl;
+        BYTE tos;
+        BYTE flags;
+        BYTE options_size;
+        DWORD options_offset;
+    } opts;
+};
+
+struct icmp_echo_reply_32
+{
+    ULONG addr;
+    ULONG status;
+    ULONG round_trip_time;
+    USHORT data_size;
+    USHORT num_of_pkts;
+    ULONG data_ptr;
+    struct
+    {
+        BYTE ttl;
+        BYTE tos;
+        BYTE flags;
+        BYTE options_size;
+        ULONG options_ptr;
+    } opts;
+};
+
+struct icmp_echo_reply_64
+{
+    ULONG addr;
+    ULONG status;
+    ULONG round_trip_time;
+    USHORT data_size;
+    USHORT num_of_pkts;
+    ULONGLONG data_ptr;
+    struct
+    {
+        BYTE ttl;
+        BYTE tos;
+        BYTE flags;
+        BYTE options_size;
+        ULONGLONG options_ptr;
+    } opts;
+};
diff --git a/include/wine/nsi.h b/include/wine/nsi.h
index 9664b53..4e60ff6 100644
--- a/include/wine/nsi.h
+++ b/include/wine/nsi.h
@@ -426,6 +426,8 @@ struct nsiproxy_icmp_echo
 {
     SOCKADDR_INET src;
     SOCKADDR_INET dst;
+    ULONGLONG addr; /* user-mode reply buffer address */
+    BYTE bits;
     BYTE ttl;
     BYTE tos;
     BYTE flags;
@@ -435,25 +437,6 @@ struct nsiproxy_icmp_echo
     BYTE data[1]; /* ((opt_size + 3) & ~3) + req_size */
 };
 
-/* output for IOCTL_NSIPROXY_WINE_ICMP_ECHO - cf. ICMP_ECHO_REPLY */
-struct nsiproxy_icmp_echo_reply
-{
-    SOCKADDR_INET addr;
-    ULONG status;
-    ULONG round_trip_time;
-    USHORT data_size;
-    USHORT num_of_pkts;
-    DWORD data_offset;
-    struct
-    {
-        BYTE ttl;
-        BYTE tos;
-        BYTE flags;
-        BYTE options_size;
-        DWORD options_offset;
-    } opts;
-};
-
 /* Undocumented Nsi api */
 
 #define NSI_PARAM_TYPE_RW      0
-- 
2.31.1




More information about the wine-devel mailing list