[PATCH 2/3] iphlpapi: Implement APC for IcmpSendEcho2Ex.
Gabriel Ivăncescu
gabrielopcode at gmail.com
Fri Oct 8 07:17:53 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..fcc1991 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")
+#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