Signed-off-by: Gabriel Ivăncescu gabrielopcode@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); }