Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/iphlpapi_main.c | 91 ++++++++++++++- dlls/iphlpapi/tests/iphlpapi.c | 203 +++++++++++++++++++++++++++++++++ 2 files changed, 288 insertions(+), 6 deletions(-)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index d9a85be..c0ea214 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4530,6 +4530,16 @@ struct icmp_handle_data HANDLE nsi_device; };
+struct icmp_echo_async_ctx +{ + HANDLE event; + HANDLE request_event; + struct nsiproxy_icmp_echo *in; + struct nsiproxy_icmp_echo_reply *out; + ICMP_ECHO_REPLY *reply; + IO_STATUS_BLOCK iosb; +}; + /*********************************************************************** * IcmpCloseHandle (IPHLPAPI.@) */ @@ -4606,6 +4616,36 @@ static void icmpv4_echo_reply_fixup( ICMP_ECHO_REPLY *dst, struct nsiproxy_icmp_ memcpy( dst->Data, (BYTE *)reply + reply->data_offset, reply->data_size ); }
+/************************************************************************* + * icmpv4_echo_async + * + * Wait for the reply, convert it into ICMP_ECHO_REPLY, and signal the event. + */ +static DWORD WINAPI icmpv4_echo_async( VOID *parameter ) +{ + struct icmp_echo_async_ctx *ctx = parameter; + + WaitForSingleObject( ctx->request_event, INFINITE ); + CloseHandle( ctx->request_event ); + + if (!ctx->iosb.Status) + { + icmpv4_echo_reply_fixup( ctx->reply, ctx->out ); + } + else + { + memset( ctx->reply, 0, sizeof(ICMP_ECHO_REPLY) ); + ctx->reply->Status = IP_GENERAL_FAILURE; + } + + SetEvent( ctx->event ); + + heap_free( ctx->out ); + heap_free( ctx->in ); + heap_free( ctx ); + return 0; +} + /*********************************************************************** * IcmpSendEcho (IPHLPAPI.@) */ @@ -4636,25 +4676,43 @@ 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; + IO_STATUS_BLOCK iosb_stack, *iosb = &iosb_stack; + struct icmp_echo_async_ctx *async_ctx = NULL; DWORD opt_size, in_size, ret = 0, out_size; struct nsiproxy_icmp_echo *in; struct nsiproxy_icmp_echo_reply *out; HANDLE request_event; - IO_STATUS_BLOCK iosb; NTSTATUS status;
- if (event || apc_routine) + if (apc_routine) { FIXME( "Async requests not yet supported\n" ); return 0; }
- if (handle == INVALID_HANDLE_VALUE || !reply) + if (handle == INVALID_HANDLE_VALUE || !reply || !reply_size) { SetLastError( ERROR_INVALID_PARAMETER ); return 0; }
+ if (reply_size < sizeof(ICMP_ECHO_REPLY)) + { + SetLastError( IP_BUF_TOO_SMALL ); + return 0; + } + + if (event) + { + async_ctx = heap_alloc( sizeof(*async_ctx) ); + if (!async_ctx) + { + SetLastError( IP_NO_RESOURCES ); + return 0; + } + iosb = &async_ctx->iosb; + } + 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 ); @@ -4663,6 +4721,7 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r
if (!in || !out) { + heap_free( async_ctx ); heap_free( out ); heap_free( in ); SetLastError( IP_NO_RESOURCES ); @@ -4688,19 +4747,39 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r request_event = CreateEventW( NULL, 0, 0, NULL );
status = NtDeviceIoControlFile( data->nsi_device, request_event, NULL, NULL, - &iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, in, in_size, + iosb, IOCTL_NSIPROXY_WINE_ICMP_ECHO, in, in_size, out, out_size );
- if (status == STATUS_PENDING && !WaitForSingleObject( request_event, INFINITE )) - status = iosb.Status; + if (status == STATUS_PENDING) + { + if (async_ctx) + { + async_ctx->event = event; + async_ctx->request_event = request_event; + async_ctx->in = in; + async_ctx->out = out; + async_ctx->reply = reply; + status = RtlQueueWorkItem( icmpv4_echo_async, async_ctx, WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION ); + if (!status) + { + SetLastError( ERROR_IO_PENDING ); + return 0; + } + /* fall back to sync, we can't have the buffers released now */ + } + if (!WaitForSingleObject( request_event, INFINITE )) + status = iosb->Status; + }
if (!status) { icmpv4_echo_reply_fixup( reply, out ); ret = IcmpParseReplies( reply, reply_size ); + if (event) SetEvent( event ); }
CloseHandle( request_event ); + heap_free( async_ctx ); heap_free( out ); heap_free( in );
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index e20ada8..93a5843 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -38,6 +38,7 @@ #include "winsock2.h" #include "windef.h" #include "winbase.h" +#include "winternl.h" #include "ws2tcpip.h" #include "windns.h" #include "iphlpapi.h" @@ -877,9 +878,11 @@ static void testIcmpSendEcho(void) { HANDLE icmp; char senddata[32], replydata[sizeof(senddata) + sizeof(ICMP_ECHO_REPLY)]; + char replydata2[sizeof(replydata) + sizeof(IO_STATUS_BLOCK)]; DWORD ret, error, replysz = sizeof(replydata); IPAddr address; ICMP_ECHO_REPLY *reply; + HANDLE event; INT i;
memset(senddata, 0, sizeof(senddata)); @@ -893,6 +896,15 @@ static void testIcmpSendEcho(void) || broken(error == ERROR_INVALID_HANDLE) /* <= 2003 */, "expected 87, got %d\n", error);
+ address = htonl(INADDR_LOOPBACK); + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(INVALID_HANDLE_VALUE, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata, replysz, 1000); + error = GetLastError(); + ok (!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok (error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INVALID_HANDLE) /* <= 2003 */, + "expected 87, got %d\n", error); + icmp = IcmpCreateFile(); ok (icmp != INVALID_HANDLE_VALUE, "IcmpCreateFile failed unexpectedly with error %d\n", GetLastError());
@@ -1036,6 +1048,197 @@ static void testIcmpSendEcho(void) ok(reply->DataSize == sizeof(senddata), "Got size:%d\n", reply->DataSize); ok(!memcmp(senddata, reply->Data, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n");
+ + /* + * IcmpSendEcho2 + */ + address = 0; + replysz = sizeof(replydata2); + memset(senddata, 0, sizeof(senddata)); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_NETNAME + || broken(error == IP_BAD_DESTINATION) /* <= 2003 */, + "expected 1214, got %d\n", error); + + event = CreateEventW(NULL, FALSE, FALSE, NULL); + ok(event != NULL, "CreateEventW failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 returned success unexpectedly\n"); + ok(error == ERROR_INVALID_NETNAME + || broken(error == ERROR_IO_PENDING) /* <= 2003 */, + "Got last error: 0x%08x\n", error); + if (error == ERROR_IO_PENDING) + { + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %u\n", ret); + } + + address = htonl(INADDR_LOOPBACK); + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_NOACCESS) /* <= 2003 */, + "expected 87, got %d\n", error); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, replysz, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_NOACCESS) /* <= 2003 */, + "expected 87, got %d\n", error); + ok(WaitForSingleObjectEx(event, 0, TRUE) == WAIT_TIMEOUT, "Event was unexpectedly signalled.\n"); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + ok(WaitForSingleObjectEx(event, 0, TRUE) == WAIT_TIMEOUT, "Event was unexpectedly signalled.\n"); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, NULL, 0, 1000); + error = GetLastError(); + ok(!ret, "IcmpSendEcho2 succeeded unexpectedly\n"); + ok(error == ERROR_INVALID_PARAMETER + || broken(error == ERROR_INSUFFICIENT_BUFFER) /* <= 2003 */, + "expected 87, got %d\n", error); + ok(WaitForSingleObjectEx(event, 0, TRUE) == WAIT_TIMEOUT, "Event was unexpectedly signalled.\n"); + + /* synchronous tests */ + SetLastError(0xdeadbeef); + address = htonl(INADDR_LOOPBACK); + replysz = sizeof(ICMP_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, 0, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, NULL, 0, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, 0, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + replysz = sizeof(ICMP_ECHO_REPLY) + sizeof(IO_STATUS_BLOCK) + ICMP_MINLEN; + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, ICMP_MINLEN, NULL, replydata2, replysz, 1000); + ok(ret, "IcmpSendEcho2 failed unexpectedly with error %d\n", GetLastError()); + + SetLastError(0xdeadbeef); + replysz = sizeof(replydata2); + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + if (!ret) + { + error = GetLastError(); + skip("Failed to ping with error %d, is lo interface down?\n", error); + } + else if (winetest_debug > 1) + { + reply = (ICMP_ECHO_REPLY*)replydata2; + trace("send addr : %s\n", ntoa(address)); + trace("reply addr : %s\n", ntoa(reply->Address)); + trace("reply size : %u\n", replysz); + trace("roundtrip : %u ms\n", reply->RoundTripTime); + trace("status : %u\n", reply->Status); + trace("recv size : %u\n", reply->DataSize); + trace("ttl : %u\n", reply->Options.Ttl); + trace("flags : 0x%x\n", reply->Options.Flags); + } + + SetLastError(0xdeadbeef); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = i & 0xff; + ret = IcmpSendEcho2(icmp, NULL, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + reply = (ICMP_ECHO_REPLY*)replydata2; + ok(ret, "IcmpSendEcho2 failed unexpectedly\n"); + ok(error == NO_ERROR, "Expect last error: 0x%08x, got: 0x%08x\n", NO_ERROR, error); + 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); + ok(!memcmp(senddata, reply->Data, min(sizeof(senddata), reply->DataSize)), "Data mismatch\n"); + + /* asynchronous tests with event */ + SetLastError(0xdeadbeef); + replysz = sizeof(replydata2); + address = htonl(INADDR_LOOPBACK); + memset(senddata, 0, sizeof(senddata)); + ret = IcmpSendEcho2(icmp, event, NULL, NULL, address, senddata, sizeof(senddata), NULL, replydata2, replysz, 1000); + error = GetLastError(); + if (!ret && error != ERROR_IO_PENDING) + { + skip("Failed to ping with error %d, is lo interface down?\n", error); + } + else + { + 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); + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %u\n", ret); + 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); + if (winetest_debug > 1) + { + reply = (ICMP_ECHO_REPLY*)replydata2; + trace("send addr : %s\n", ntoa(address)); + trace("reply addr : %s\n", ntoa(reply->Address)); + trace("reply size : %u\n", replysz); + trace("roundtrip : %u ms\n", reply->RoundTripTime); + trace("status : %u\n", reply->Status); + trace("recv size : %u\n", reply->DataSize); + trace("ttl : %u\n", reply->Options.Ttl); + trace("flags : 0x%x\n", reply->Options.Flags); + } + } + + SetLastError(0xdeadbeef); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = i & 0xff; + ret = IcmpSendEcho2(icmp, event, NULL, NULL, 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); + ret = WaitForSingleObjectEx(event, 2000, TRUE); + ok(ret == WAIT_OBJECT_0, "WaitForSingleObjectEx failed unexpectedly with %u\n", ret); + 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"); + + CloseHandle(event); IcmpCloseHandle(icmp); }
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); }
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com ---
I sent this as a separate patch, since IMO it's better to have it for safety. Because this would be hell to debug if an app does depend on it and then we get corrupted memory (assuming it releases the buffers after it closes it, for example). At the same time, I'm using a basic loop to keep it extremely simple, and in the common case where it doesn't wait, there's no extra complexity or overhead involved at all.
dlls/iphlpapi/iphlpapi_main.c | 12 ++++++++++++ 1 file changed, 12 insertions(+)
diff --git a/dlls/iphlpapi/iphlpapi_main.c b/dlls/iphlpapi/iphlpapi_main.c index fcc1991..71e6e30 100644 --- a/dlls/iphlpapi/iphlpapi_main.c +++ b/dlls/iphlpapi/iphlpapi_main.c @@ -4529,10 +4529,12 @@ DWORD WINAPI ParseNetworkString(const WCHAR *str, DWORD type, struct icmp_handle_data { HANDLE nsi_device; + LONG async_count; };
struct icmp_echo_async_ctx { + struct icmp_handle_data *data; HANDLE event; HANDLE request_event; HANDLE thread; @@ -4552,6 +4554,11 @@ BOOL WINAPI IcmpCloseHandle( HANDLE handle ) struct icmp_handle_data *data = (struct icmp_handle_data *)handle;
CloseHandle( data->nsi_device ); + + /* Windows waits until all outstanding async requests are complete or timed out */ + while (data->async_count) + SleepEx( 1, TRUE ); + heap_free( data ); return TRUE; } @@ -4576,6 +4583,7 @@ HANDLE WINAPI IcmpCreateFile( void ) heap_free( data ); return INVALID_HANDLE_VALUE; } + data->async_count = 0;
return (HANDLE)data; } @@ -4685,6 +4693,7 @@ static DWORD WINAPI icmpv4_echo_async( VOID *parameter ) } if (ctx->event) SetEvent( ctx->event );
+ InterlockedDecrement( &ctx->data->async_count ); heap_free( ctx->out ); heap_free( ctx->in ); heap_free( ctx ); @@ -4793,6 +4802,7 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r { if (async_ctx) { + async_ctx->data = data; async_ctx->event = event; async_ctx->request_event = request_event; async_ctx->apc = NULL; @@ -4808,6 +4818,7 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r async_ctx->apc_ctx = apc_ctxt; } } + InterlockedIncrement( &data->async_count ); status = RtlQueueWorkItem( icmpv4_echo_async, async_ctx, WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION ); if (!status) { @@ -4815,6 +4826,7 @@ DWORD WINAPI IcmpSendEcho2Ex( HANDLE handle, HANDLE event, PIO_APC_ROUTINE apc_r return 0; } if (async_ctx->apc) CloseHandle( async_ctx->thread ); + InterlockedDecrement( &data->async_count );
/* fall back to sync, we can't have the buffers released now */ }