[PATCH v2 2/3] iphlpapi: Implement APC for IcmpSendEcho2Ex.

Gabriel Ivăncescu gabrielopcode at gmail.com
Fri Oct 8 07:48:10 CDT 2021


Signed-off-by: Gabriel Ivăncescu <gabrielopcode at gmail.com>
---
 dlls/iphlpapi/iphlpapi_main.c  | 67 ++++++++++++++++++++++++++++++----
 dlls/iphlpapi/tests/iphlpapi.c | 45 +++++++++++++++++++++++
 2 files changed, 104 insertions(+), 8 deletions(-)

diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c
index c0ea214..c4e7ea3 100644
--- a/dlls/iphlpapi/iphlpapi_main.c
+++ b/dlls/iphlpapi/iphlpapi_main.c
@@ -38,6 +38,7 @@
 #include "icmpapi.h"
 
 #include "wine/nsi.h"
+#include "wine/asm.h"
 #include "wine/debug.h"
 #include "wine/heap.h"
 
@@ -4534,6 +4535,9 @@ struct icmp_echo_async_ctx
 {
     HANDLE event;
     HANDLE request_event;
+    HANDLE thread;
+    PIO_APC_ROUTINE apc;
+    void *apc_ctx;
     struct nsiproxy_icmp_echo *in;
     struct nsiproxy_icmp_echo_reply *out;
     ICMP_ECHO_REPLY *reply;
@@ -4616,6 +4620,39 @@ static void icmpv4_echo_reply_fixup( ICMP_ECHO_REPLY *dst, struct nsiproxy_icmp_
     memcpy( dst->Data, (BYTE *)reply + reply->data_offset, reply->data_size );
 }
 
+#ifdef __i386__
+/* The stdcall calling convention has the callee clean the stack. Vista and later
+ * have different callback signatures, so we can't rely on it restoring the stack.
+ */
+extern void CALLBACK icmp_echo_async_apc( ULONG_PTR apc, ULONG_PTR ctx, ULONG_PTR reply_size ) DECLSPEC_HIDDEN;
+__ASM_STDCALL_FUNC(icmp_echo_async_apc, 12,
+    "pushl %ebp\n\t"
+    __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t")
+    __ASM_CFI(".cfi_rel_offset %ebp,0\n\t")
+    "movl %esp,%ebp\n\t"
+    __ASM_CFI(".cfi_def_cfa_register %ebp\n\t")
+    "pushl 16(%ebp)\n\t"    /* io.Information */
+    "pushl $0\n\t"          /* io.Status */
+    "movl %esp,%eax\n\t"
+    "pushl $0\n\t"
+    "pushl %eax\n\t"
+    "pushl 12(%ebp)\n\t"
+    "call *8(%ebp)\n\t"
+    "leave\n\t"
+    __ASM_CFI(".cfi_def_cfa %esp,4\n\t")
+    __ASM_CFI(".cfi_same_value %ebp\n\t")
+    "ret $12")
+#else
+static void CALLBACK icmp_echo_async_apc( ULONG_PTR apc, ULONG_PTR ctx, ULONG_PTR reply_size )
+{
+    IO_STATUS_BLOCK io;
+
+    io.Pointer = NULL;
+    io.Information = reply_size;
+    ((PIO_APC_ROUTINE)apc)( (void*)ctx, &io, 0 );
+}
+#endif
+
 /*************************************************************************
  *    icmpv4_echo_async
  *
@@ -4624,6 +4661,7 @@ static void icmpv4_echo_reply_fixup( ICMP_ECHO_REPLY *dst, struct nsiproxy_icmp_
 static DWORD WINAPI icmpv4_echo_async( VOID *parameter )
 {
     struct icmp_echo_async_ctx *ctx = parameter;
+    ULONG_PTR reply_size;
 
     WaitForSingleObject( ctx->request_event, INFINITE );
     CloseHandle( ctx->request_event );
@@ -4631,14 +4669,21 @@ static DWORD WINAPI icmpv4_echo_async( VOID *parameter )
     if (!ctx->iosb.Status)
     {
         icmpv4_echo_reply_fixup( ctx->reply, ctx->out );
+        reply_size = ctx->iosb.Information - sizeof(struct nsiproxy_icmp_echo_reply) + sizeof(ICMP_ECHO_REPLY);
     }
     else
     {
         memset( ctx->reply, 0, sizeof(ICMP_ECHO_REPLY) );
         ctx->reply->Status = IP_GENERAL_FAILURE;
+        reply_size = 8;  /* ICMP error message */
     }
 
-    SetEvent( ctx->event );
+    if (ctx->apc)
+    {
+        NtQueueApcThread( ctx->thread, icmp_echo_async_apc, (ULONG_PTR)ctx->apc, (ULONG_PTR)ctx->apc_ctx, reply_size );
+        CloseHandle( ctx->thread );
+    }
+    if (ctx->event) SetEvent( ctx->event );
 
     heap_free( ctx->out );
     heap_free( ctx->in );
@@ -4684,12 +4729,6 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r
     HANDLE request_event;
     NTSTATUS status;
 
-    if (apc_routine)
-    {
-        FIXME( "Async requests not yet supported\n" );
-        return 0;
-    }
-
     if (handle == INVALID_HANDLE_VALUE || !reply || !reply_size)
     {
         SetLastError( ERROR_INVALID_PARAMETER );
@@ -4702,7 +4741,7 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r
         return 0;
     }
 
-    if (event)
+    if (event || apc_routine)
     {
         async_ctx = heap_alloc( sizeof(*async_ctx) );
         if (!async_ctx)
@@ -4756,15 +4795,27 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r
         {
             async_ctx->event = event;
             async_ctx->request_event = request_event;
+            async_ctx->apc = NULL;
             async_ctx->in = in;
             async_ctx->out = out;
             async_ctx->reply = reply;
+            if (apc_routine)
+            {
+                if (DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
+                                     &async_ctx->thread, 0, FALSE, DUPLICATE_SAME_ACCESS ))
+                {
+                    async_ctx->apc = apc_routine;
+                    async_ctx->apc_ctx = apc_ctxt;
+                }
+            }
             status = RtlQueueWorkItem( icmpv4_echo_async, async_ctx, WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION );
             if (!status)
             {
                 SetLastError( ERROR_IO_PENDING );
                 return 0;
             }
+            if (async_ctx->apc) CloseHandle( async_ctx->thread );
+
             /* fall back to sync, we can't have the buffers released now */
         }
         if (!WaitForSingleObject( request_event, INFINITE ))
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c
index 93a5843..7118887 100644
--- a/dlls/iphlpapi/tests/iphlpapi.c
+++ b/dlls/iphlpapi/tests/iphlpapi.c
@@ -874,8 +874,28 @@ static void testSetTcpEntry(void)
        "got %u, expected %u\n", ret, ERROR_MR_MID_NOT_FOUND);
 }
 
+static BOOL icmp_send_echo_test_apc_expect;
+static void WINAPI icmp_send_echo_test_apc_xp(void *context)
+{
+    ok(icmp_send_echo_test_apc_expect, "Unexpected APC execution\n");
+    ok(context == (void*)0xdeadc0de, "Wrong context: %p\n", context);
+    icmp_send_echo_test_apc_expect = FALSE;
+}
+
+static void WINAPI icmp_send_echo_test_apc(void *context, IO_STATUS_BLOCK *io_status, ULONG reserved)
+{
+    icmp_send_echo_test_apc_xp(context);
+    ok(io_status->Status == 0, "Got IO Status 0x%08x\n", io_status->Status);
+    ok(io_status->Information == sizeof(ICMP_ECHO_REPLY) + 32 /* sizeof(senddata) */,
+        "Got IO Information %lu\n", io_status->Information);
+}
+
 static void testIcmpSendEcho(void)
 {
+    /* The APC function's signature is different pre-Vista */
+    const PIO_APC_ROUTINE apc = broken(LOBYTE(LOWORD(GetVersion())) < 6)
+                                ? (PIO_APC_ROUTINE)icmp_send_echo_test_apc_xp
+                                : icmp_send_echo_test_apc;
     HANDLE icmp;
     char senddata[32], replydata[sizeof(senddata) + sizeof(ICMP_ECHO_REPLY)];
     char replydata2[sizeof(replydata) + sizeof(IO_STATUS_BLOCK)];
@@ -1239,6 +1259,31 @@ static void testIcmpSendEcho(void)
     ok(!memcmp(senddata, reply + 1, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n");
 
     CloseHandle(event);
+
+    /* asynchronous tests with APC */
+    SetLastError(0xdeadbeef);
+    replysz = sizeof(replydata2) + 10;
+    address = htonl(INADDR_LOOPBACK);
+    for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = ~i & 0xff;
+    icmp_send_echo_test_apc_expect = TRUE;
+    /*
+       NOTE: Supplying both event and apc has varying behavior across Windows versions, so not tested.
+    */
+    ret = IcmpSendEcho2(icmp, NULL, apc, (void*)0xdeadc0de, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000);
+    error = GetLastError();
+    ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n");
+    ok(error == ERROR_IO_PENDING, "Expect last error: 0x%08x, got: 0x%08x\n", ERROR_IO_PENDING, error);
+    SleepEx(200, TRUE);
+    SleepEx(0, TRUE);
+    ok(icmp_send_echo_test_apc_expect == FALSE, "APC was not executed!\n");
+    reply = (ICMP_ECHO_REPLY*)replydata2;
+    ok(ntohl(reply->Address) == INADDR_LOOPBACK, "Address mismatch, expect: %s, got: %s\n", ntoa(INADDR_LOOPBACK),
+       ntoa(reply->Address));
+    ok(reply->Status == IP_SUCCESS, "Expect status: 0x%08x, got: 0x%08x\n", IP_SUCCESS, reply->Status);
+    ok(reply->DataSize == sizeof(senddata), "Got size: %d\n", reply->DataSize);
+    /* pre-Vista, reply->Data is an offset; otherwise it's a pointer, so hardcode the offset */
+    ok(!memcmp(senddata, reply + 1, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n");
+
     IcmpCloseHandle(icmp);
 }
 
-- 
2.31.1




More information about the wine-devel mailing list