[PATCH resend 3/4] iphlpapi: Implement asynchronous APC for IcmpSendEcho2(Ex).
Gabriel Ivăncescu
gabrielopcode at gmail.com
Tue Jun 15 07:50:26 CDT 2021
Signed-off-by: Gabriel Ivăncescu <gabrielopcode at 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);
--
2.31.1
More information about the wine-devel
mailing list