[PATCH 1/3] iphlpapi: Implement asynchronous events for IcmpSendEcho2Ex.

Gabriel Ivăncescu gabrielopcode at gmail.com
Fri Oct 8 07:17:52 CDT 2021


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




More information about the wine-devel mailing list