Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/icmp.c | 108 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 100 insertions(+), 8 deletions(-)
diff --git a/dlls/iphlpapi/icmp.c b/dlls/iphlpapi/icmp.c index 8ef0321..ceeab69 100644 --- a/dlls/iphlpapi/icmp.c +++ b/dlls/iphlpapi/icmp.c @@ -106,9 +106,42 @@ WINE_DECLARE_DEBUG_CHANNEL(winediag);
typedef struct { int sid; + LONG lock_count; + HANDLE lock_event; IP_OPTION_INFORMATION default_opts; } icmp_t;
+static void icmp_lock(icmp_t *icp) +{ + InterlockedIncrement(&icp->lock_count); +} + +static void icmp_unlock(icmp_t *icp) +{ + if (InterlockedDecrement(&icp->lock_count) == 0x80000000) + { + if (icp->lock_event) + SetEvent(icp->lock_event); + } +} + +static void icmp_wait_for_released_locks(icmp_t *icp) +{ + HANDLE event = CreateEventW(NULL, TRUE, FALSE, NULL); + + icp->lock_event = event; + if (InterlockedExchangeAdd(&icp->lock_count, 0x80000000) != 0) + { + do + { + if (event) WaitForSingleObjectEx(event, INFINITE, TRUE); + else SleepEx(1, TRUE); + } + while (icp->lock_count != 0x80000000); + } + if (event) CloseHandle(event); +} + #define IP_OPTS_UNKNOWN 0 #define IP_OPTS_DEFAULT 1 #define IP_OPTS_CUSTOM 2 @@ -347,6 +380,30 @@ done: return res; }
+struct icmp_get_reply_async_ctx +{ + icmp_t *icp; + HANDLE event; + unsigned char *buffer; + void *reply_buf; + DWORD reply_size; + DWORD send_time; + DWORD timeout; +}; + +static DWORD WINAPI icmp_get_reply_async_func(VOID *parameter) +{ + struct icmp_get_reply_async_ctx *ctx = parameter; + + icmp_get_reply(ctx->icp->sid, ctx->buffer, ctx->send_time, ctx->reply_buf, ctx->reply_size, ctx->timeout); + + SetEvent(ctx->event); + + icmp_unlock(ctx->icp); + HeapFree(GetProcessHeap(), 0, ctx); + return 0; +} +
/* @@ -381,6 +438,7 @@ HANDLE WINAPI Icmp6CreateFile(VOID) } icp->sid=sid; icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN; + icp->lock_count=0; return (HANDLE)icp; }
@@ -439,6 +497,7 @@ HANDLE WINAPI IcmpCreateFile(VOID) } icp->sid=sid; icp->default_opts.OptionsSize=IP_OPTS_UNKNOWN; + icp->lock_count=0; return (HANDLE)icp; }
@@ -455,6 +514,9 @@ BOOL WINAPI IcmpCloseHandle(HANDLE IcmpHandle) return FALSE; }
+ /* Windows waits until all outstanding async requests are complete or timed out */ + icmp_wait_for_released_locks(icp); + close( icp->sid ); HeapFree(GetProcessHeap (), 0, icp); return TRUE; @@ -526,6 +588,7 @@ DWORD WINAPI IcmpSendEcho2Ex( unsigned char *buffer; int reqsize, repsize; DWORD send_time; + DWORD res = 0;
TRACE("(%p, %p, %p, %p, %08x, %08x, %p, %d, %p, %p, %d, %d)\n", IcmpHandle, Event, ApcRoutine, ApcContext, SourceAddress, DestinationAddress, RequestData, @@ -553,11 +616,6 @@ DWORD WINAPI IcmpSendEcho2Ex( return 0; }
- if (Event) - { - FIXME("unsupported for events\n"); - return 0; - } if (ApcRoutine) { FIXME("unsupported for APCs\n"); @@ -569,6 +627,8 @@ DWORD WINAPI IcmpSendEcho2Ex( return 0; }
+ icmp_lock(icp); + /* Prepare the request */ id=getpid() & 0xFFFF; seq=InterlockedIncrement(&icmp_sequence) & 0xFFFF; @@ -580,7 +640,7 @@ DWORD WINAPI IcmpSendEcho2Ex( buffer = HeapAlloc(GetProcessHeap(), 0, max( repsize, reqsize )); if (buffer == NULL) { SetLastError(ERROR_OUTOFMEMORY); - return 0; + goto done; }
icmp_header=(struct icmp*)buffer; @@ -661,10 +721,42 @@ DWORD WINAPI IcmpSendEcho2Ex( } } HeapFree(GetProcessHeap(), 0, buffer); - return 0; + goto done; }
- return icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, ReplySize, Timeout); + if (Event) + { + struct icmp_get_reply_async_ctx *ctx = HeapAlloc(GetProcessHeap(), 0, sizeof(*ctx)); + + if (ctx) + { + ctx->icp = icp; + ctx->event = Event; + ctx->buffer = buffer; + ctx->reply_buf = ReplyBuffer; + ctx->reply_size = ReplySize; + ctx->send_time = send_time; + ctx->timeout = Timeout; + if (QueueUserWorkItem(icmp_get_reply_async_func, ctx, WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION)) + { + SetLastError(ERROR_IO_PENDING); + return 0; + } + + HeapFree(GetProcessHeap(), 0, ctx); + } + else + SetLastError(ERROR_OUTOFMEMORY); + + HeapFree(GetProcessHeap(), 0, buffer); + goto done; + } + + res = icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, ReplySize, Timeout); + +done: + icmp_unlock(icp); + return res; }
/*
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/tests/iphlpapi.c | 203 +++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 358084f..1c3b29f 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" @@ -865,9 +866,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)); @@ -881,6 +884,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(); if (icmp == INVALID_HANDLE_VALUE) { @@ -1028,6 +1040,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/icmp.c | 112 +++++++++++++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 25 deletions(-)
diff --git a/dlls/iphlpapi/icmp.c b/dlls/iphlpapi/icmp.c index ceeab69..61a991f 100644 --- a/dlls/iphlpapi/icmp.c +++ b/dlls/iphlpapi/icmp.c @@ -71,6 +71,7 @@ #include "winternl.h" #include "ipexport.h" #include "icmpapi.h" +#include "wine/asm.h" #include "wine/debug.h"
/* Set up endianness macros for the ip and ip_icmp BSD headers */ @@ -178,11 +179,11 @@ static int in_cksum(u_short *addr, int len) }
/* Receive a reply (IPv4); this function uses, takes ownership of and will always free `buffer` */ -static DWORD icmp_get_reply(int sid, unsigned char *buffer, DWORD send_time, void *reply_buf, DWORD reply_size, DWORD timeout) +static DWORD icmp_get_reply(int sid, unsigned char *buffer, DWORD send_time, void *reply_buf, DWORD *reply_size, DWORD timeout) { - int repsize = MAXIPLEN + MAXICMPLEN + min(65535, reply_size); + int repsize = MAXIPLEN + MAXICMPLEN + min(65535, *reply_size); struct icmp *icmp_header = (struct icmp*)buffer; - char *endbuf = (char*)reply_buf + reply_size; + char *endbuf = (char*)reply_buf + *reply_size; struct ip *ip_header = (struct ip*)buffer; struct icmp_echo_reply *ier = reply_buf; unsigned short id, seq, cksum; @@ -345,11 +346,13 @@ done: if (res) { /* Move the data so there's no gap between it and the ICMP_ECHO_REPLY array */ + char *reply_end = (char*)reply_buf + *reply_size; DWORD gap_size = endbuf - (char*)ier;
if (gap_size) { - memmove(ier, endbuf, ((char*)reply_buf + reply_size) - endbuf); + *reply_size -= gap_size; + memmove(ier, endbuf, reply_end - endbuf);
/* Fix the pointers */ while (ier-- != reply_buf) @@ -367,13 +370,15 @@ done: it and write it out if there's enough space available in the buffer. */ if (gap_size >= sizeof(IO_STATUS_BLOCK)) { - IO_STATUS_BLOCK *io = (IO_STATUS_BLOCK*)((char*)reply_buf + reply_size - sizeof(IO_STATUS_BLOCK)); + IO_STATUS_BLOCK *io = (IO_STATUS_BLOCK*)(reply_end - sizeof(IO_STATUS_BLOCK));
io->Pointer = NULL; /* Always NULL or STATUS_SUCCESS */ - io->Information = reply_size - gap_size; + io->Information = *reply_size; } } } + else + *reply_size = 8; /* ICMP error message */
HeapFree(GetProcessHeap(), 0, buffer); TRACE("received %d replies\n",res); @@ -384,6 +389,9 @@ struct icmp_get_reply_async_ctx { icmp_t *icp; HANDLE event; + PIO_APC_ROUTINE apc; + void *apc_ctx; + HANDLE thread; unsigned char *buffer; void *reply_buf; DWORD reply_size; @@ -391,13 +399,52 @@ struct icmp_get_reply_async_ctx DWORD timeout; };
+#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_get_reply_async_call_apc(ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3); +__ASM_GLOBAL_FUNC(icmp_get_reply_async_call_apc, + "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_get_reply_async_call_apc(ULONG_PTR arg1, ULONG_PTR arg2, ULONG_PTR arg3) +{ + IO_STATUS_BLOCK io; + + io.Pointer = NULL; /* Always NULL or STATUS_SUCCESS */ + io.Information = arg3; + ((PIO_APC_ROUTINE)arg1)(arg2, &io, 0); +} +#endif + static DWORD WINAPI icmp_get_reply_async_func(VOID *parameter) { struct icmp_get_reply_async_ctx *ctx = parameter;
- icmp_get_reply(ctx->icp->sid, ctx->buffer, ctx->send_time, ctx->reply_buf, ctx->reply_size, ctx->timeout); + icmp_get_reply(ctx->icp->sid, ctx->buffer, ctx->send_time, ctx->reply_buf, &ctx->reply_size, ctx->timeout);
- SetEvent(ctx->event); + if (ctx->apc) + { + NtQueueApcThread(ctx->thread, icmp_get_reply_async_call_apc, (ULONG_PTR)ctx->apc, (ULONG_PTR)ctx->apc_ctx, ctx->reply_size); + CloseHandle(ctx->thread); + } + if (ctx->event) + SetEvent(ctx->event);
icmp_unlock(ctx->icp); HeapFree(GetProcessHeap(), 0, ctx); @@ -616,11 +663,6 @@ DWORD WINAPI IcmpSendEcho2Ex( return 0; }
- if (ApcRoutine) - { - FIXME("unsupported for APCs\n"); - return 0; - } if (SourceAddress) { FIXME("unsupported for source addresses\n"); @@ -724,25 +766,45 @@ DWORD WINAPI IcmpSendEcho2Ex( goto done; }
- if (Event) + if (Event || ApcRoutine) { struct icmp_get_reply_async_ctx *ctx = HeapAlloc(GetProcessHeap(), 0, sizeof(*ctx));
if (ctx) { - ctx->icp = icp; - ctx->event = Event; - ctx->buffer = buffer; - ctx->reply_buf = ReplyBuffer; - ctx->reply_size = ReplySize; - ctx->send_time = send_time; - ctx->timeout = Timeout; - if (QueueUserWorkItem(icmp_get_reply_async_func, ctx, WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION)) + BOOL failed = FALSE; + + /* The APC is executed only if there's no event on Vista and later */ + ctx->apc = NULL; + if (ApcRoutine && !(Event && LOBYTE(LOWORD(GetVersion())) >= 6)) { - SetLastError(ERROR_IO_PENDING); - return 0; + if (DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), + &ctx->thread, 0, FALSE, DUPLICATE_SAME_ACCESS)) + ctx->apc = ApcRoutine; + else + failed = TRUE; }
+ if (!failed) + { + ctx->icp = icp; + ctx->event = Event; + ctx->apc_ctx = ApcContext; + ctx->buffer = buffer; + ctx->reply_buf = ReplyBuffer; + ctx->reply_size = ReplySize; + ctx->send_time = send_time; + ctx->timeout = Timeout; + + if (QueueUserWorkItem(icmp_get_reply_async_func, ctx, WT_EXECUTEDEFAULT | WT_EXECUTELONGFUNCTION)) + { + SetLastError(ERROR_IO_PENDING); + return 0; + } + + if (ctx->apc) + CloseHandle(ctx->thread); + } HeapFree(GetProcessHeap(), 0, ctx); } else @@ -752,7 +814,7 @@ DWORD WINAPI IcmpSendEcho2Ex( goto done; }
- res = icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, ReplySize, Timeout); + res = icmp_get_reply(icp->sid, buffer, send_time, ReplyBuffer, &ReplySize, Timeout);
done: icmp_unlock(icp);
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/iphlpapi/tests/iphlpapi.c | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)
diff --git a/dlls/iphlpapi/tests/iphlpapi.c b/dlls/iphlpapi/tests/iphlpapi.c index 1c3b29f..47442f3 100644 --- a/dlls/iphlpapi/tests/iphlpapi.c +++ b/dlls/iphlpapi/tests/iphlpapi.c @@ -862,8 +862,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)]; @@ -1231,6 +1251,32 @@ 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); + address = htonl(INADDR_LOOPBACK); + for (i = 0; i < ARRAY_SIZE(senddata); i++) senddata[i] = ~i & 0xff; + icmp_send_echo_test_apc_expect = TRUE; + /* + NOTE: On versions Vista and up, supplying both an event and apc results in only the event being used. + This is unreliable since older versions tend to either use both, or only the apc, 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); }