[AFD_APITEST] Introduce a test for directly creating and using sockets via AFD. CORE...
authorThomas Faber <thomas.faber@reactos.org>
Sun, 25 Feb 2018 09:34:49 +0000 (10:34 +0100)
committerThomas Faber <thomas.faber@reactos.org>
Mon, 5 Mar 2018 13:52:56 +0000 (14:52 +0100)
The initial tests in send.c validate correct behavior of send/sendto on
disconnected sockets (CORE-9810), fixed in r68129.
However, the helper functions are generic, so they can be used for additional
tests against AFD. Because AFD's create packet structure changes between
Windows versions, the functions check the OS version to determine the right
layout.
Tests succeed on Win2003 as well as Win10.

modules/rostests/apitests/CMakeLists.txt
modules/rostests/apitests/afd/AfdHelpers.c [new file with mode: 0644]
modules/rostests/apitests/afd/AfdHelpers.h [new file with mode: 0644]
modules/rostests/apitests/afd/CMakeLists.txt [new file with mode: 0644]
modules/rostests/apitests/afd/precomp.h [new file with mode: 0644]
modules/rostests/apitests/afd/send.c [new file with mode: 0644]
modules/rostests/apitests/afd/testlist.c [new file with mode: 0644]

index 6b6149c..5312b28 100644 (file)
@@ -1,6 +1,7 @@
 include_directories(include)
 
 add_subdirectory(advapi32)
+add_subdirectory(afd)
 add_subdirectory(apphelp)
 add_subdirectory(appshim)
 add_subdirectory(atl)
diff --git a/modules/rostests/apitests/afd/AfdHelpers.c b/modules/rostests/apitests/afd/AfdHelpers.c
new file mode 100644 (file)
index 0000000..ad7793e
--- /dev/null
@@ -0,0 +1,384 @@
+/*
+ * PROJECT:     ReactOS API Tests
+ * LICENSE:     LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
+ * PURPOSE:     Utility function definitions for calling AFD
+ * COPYRIGHT:   Copyright 2015-2018 Thomas Faber (thomas.faber@reactos.org)
+ */
+
+#include "precomp.h"
+
+#define DD_UDP_DEVICE_NAME L"\\Device\\Udp"
+
+typedef struct _AFD_CREATE_PACKET_NT6 {
+    DWORD                               EndpointFlags;
+    DWORD                               GroupID;
+    DWORD                               AddressFamily;
+    DWORD                               SocketType;
+    DWORD                               Protocol;
+    DWORD                               SizeOfTransportName;
+    WCHAR                               TransportName[1];
+} AFD_CREATE_PACKET_NT6, *PAFD_CREATE_PACKET_NT6;
+
+NTSTATUS
+AfdCreateSocket(
+    _Out_ PHANDLE SocketHandle,
+    _In_ int AddressFamily,
+    _In_ int SocketType,
+    _In_ int Protocol)
+{
+    NTSTATUS Status;
+    OBJECT_ATTRIBUTES ObjectAttributes;
+    IO_STATUS_BLOCK IoStatus;
+    PFILE_FULL_EA_INFORMATION EaBuffer = NULL;
+    ULONG EaLength;
+    PAFD_CREATE_PACKET AfdPacket;
+    PAFD_CREATE_PACKET_NT6 AfdPacket6;
+    ULONG SizeOfPacket;
+    ANSI_STRING EaName = RTL_CONSTANT_STRING(AfdCommand);
+    UNICODE_STRING TcpTransportName = RTL_CONSTANT_STRING(DD_TCP_DEVICE_NAME);
+    UNICODE_STRING UdpTransportName = RTL_CONSTANT_STRING(DD_UDP_DEVICE_NAME);
+    UNICODE_STRING TransportName = SocketType == SOCK_STREAM ? TcpTransportName : UdpTransportName;
+    UNICODE_STRING DeviceName = RTL_CONSTANT_STRING(L"\\Device\\Afd\\Endpoint");
+
+    *SocketHandle = NULL;
+
+    if (LOBYTE(LOWORD(GetVersion())) >= 6)
+    {
+        SizeOfPacket = FIELD_OFFSET(AFD_CREATE_PACKET_NT6, TransportName) + TransportName.Length + sizeof(UNICODE_NULL);
+    }
+    else
+    {
+        SizeOfPacket = FIELD_OFFSET(AFD_CREATE_PACKET, TransportName) + TransportName.Length + sizeof(UNICODE_NULL);
+    }
+    EaLength = SizeOfPacket + FIELD_OFFSET(FILE_FULL_EA_INFORMATION, EaName) + EaName.Length + sizeof(ANSI_NULL);
+
+    /* Set up EA Buffer */
+    EaBuffer = RtlAllocateHeap(RtlGetProcessHeap(), HEAP_ZERO_MEMORY, EaLength);
+    if (!EaBuffer)
+    {
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    EaBuffer->NextEntryOffset = 0;
+    EaBuffer->Flags = 0;
+    EaBuffer->EaNameLength = EaName.Length;
+    RtlCopyMemory(EaBuffer->EaName,
+                  EaName.Buffer,
+                  EaName.Length + sizeof(ANSI_NULL));
+    EaBuffer->EaValueLength = SizeOfPacket;
+
+    if (LOBYTE(LOWORD(GetVersion())) >= 6)
+    {
+        AfdPacket6 = (PAFD_CREATE_PACKET_NT6)(EaBuffer->EaName + EaBuffer->EaNameLength + sizeof(ANSI_NULL));
+        AfdPacket6->GroupID = 0;
+        if (SocketType == SOCK_DGRAM)
+        {
+            AfdPacket6->EndpointFlags = AFD_ENDPOINT_CONNECTIONLESS;
+        }
+        else if (SocketType == SOCK_STREAM)
+        {
+            AfdPacket6->EndpointFlags = AFD_ENDPOINT_MESSAGE_ORIENTED;
+        }
+        AfdPacket6->AddressFamily = AddressFamily;
+        AfdPacket6->SocketType = SocketType;
+        AfdPacket6->Protocol = Protocol;
+        AfdPacket6->SizeOfTransportName = TransportName.Length;
+        RtlCopyMemory(AfdPacket6->TransportName,
+                      TransportName.Buffer,
+                      TransportName.Length + sizeof(UNICODE_NULL));
+    }
+    else
+    {
+        AfdPacket = (PAFD_CREATE_PACKET)(EaBuffer->EaName + EaBuffer->EaNameLength + sizeof(ANSI_NULL));
+        AfdPacket->GroupID = 0;
+        if (SocketType == SOCK_DGRAM)
+        {
+            AfdPacket->EndpointFlags = AFD_ENDPOINT_CONNECTIONLESS;
+        }
+        else if (SocketType == SOCK_STREAM)
+        {
+            AfdPacket->EndpointFlags = AFD_ENDPOINT_MESSAGE_ORIENTED;
+        }
+        AfdPacket->SizeOfTransportName = TransportName.Length;
+        RtlCopyMemory(AfdPacket->TransportName,
+                      TransportName.Buffer,
+                      TransportName.Length + sizeof(UNICODE_NULL));
+    }
+
+    InitializeObjectAttributes(&ObjectAttributes,
+                               &DeviceName,
+                               OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
+                               0,
+                               0);
+
+    Status = NtCreateFile(SocketHandle,
+                          GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
+                          &ObjectAttributes,
+                          &IoStatus,
+                          NULL,
+                          0,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE,
+                          FILE_OPEN_IF,
+                          0,
+                          EaBuffer,
+                          EaLength);
+
+    RtlFreeHeap(RtlGetProcessHeap(), 0, EaBuffer);
+
+    return Status;
+}
+
+
+NTSTATUS
+AfdBind(
+    _In_ HANDLE SocketHandle,
+    _In_ const struct sockaddr *Address,
+    _In_ ULONG AddressLength)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatus;
+    PAFD_BIND_DATA BindInfo;
+    ULONG BindInfoLength;
+    HANDLE Event;
+
+    Status = NtCreateEvent(&Event,
+                           EVENT_ALL_ACCESS,
+                           NULL,
+                           NotificationEvent,
+                           FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    BindInfoLength = FIELD_OFFSET(AFD_BIND_DATA, Address.Address[0].Address) +
+                     AddressLength - FIELD_OFFSET(struct sockaddr, sa_data);
+    BindInfo = RtlAllocateHeap(RtlGetProcessHeap(),
+                               0,
+                               BindInfoLength);
+    if (!BindInfo)
+    {
+        NtClose(Event);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+    BindInfo->ShareType = AFD_SHARE_UNIQUE;
+    BindInfo->Address.TAAddressCount = 1;
+    BindInfo->Address.Address[0].AddressType = Address->sa_family;
+    BindInfo->Address.Address[0].AddressLength = AddressLength - FIELD_OFFSET(struct sockaddr, sa_data);
+    RtlCopyMemory(&BindInfo->Address.Address[0].Address,
+                  Address->sa_data,
+                  BindInfo->Address.Address[0].AddressLength);
+
+    Status = NtDeviceIoControlFile(SocketHandle,
+                                   Event,
+                                   NULL,
+                                   NULL,
+                                   &IoStatus,
+                                   IOCTL_AFD_BIND,
+                                   BindInfo,
+                                   BindInfoLength,
+                                   BindInfo,
+                                   BindInfoLength);
+    if (Status == STATUS_PENDING)
+    {
+        NtWaitForSingleObject(Event, FALSE, NULL);
+        Status = IoStatus.Status;
+    }
+
+    RtlFreeHeap(RtlGetProcessHeap(), 0, BindInfo);
+    NtClose(Event);
+
+    return Status;
+}
+
+NTSTATUS
+AfdConnect(
+    _In_ HANDLE SocketHandle,
+    _In_ const struct sockaddr *Address,
+    _In_ ULONG AddressLength)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatus;
+    PAFD_CONNECT_INFO ConnectInfo;
+    ULONG ConnectInfoLength;
+    HANDLE Event;
+
+    Status = NtCreateEvent(&Event,
+                           EVENT_ALL_ACCESS,
+                           NULL,
+                           NotificationEvent,
+                           FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    ASSERT(FIELD_OFFSET(AFD_CONNECT_INFO, RemoteAddress.Address[0].Address) == 20);
+    ConnectInfoLength = FIELD_OFFSET(AFD_CONNECT_INFO, RemoteAddress.Address[0].Address) +
+                        AddressLength - FIELD_OFFSET(struct sockaddr, sa_data);
+    ConnectInfo = RtlAllocateHeap(RtlGetProcessHeap(),
+                                  0,
+                                  ConnectInfoLength);
+    if (!ConnectInfo)
+    {
+        NtClose(Event);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+
+
+    ConnectInfo->UseSAN = FALSE;
+    ConnectInfo->Root = 0;
+    ConnectInfo->Unknown = 0;
+    ConnectInfo->RemoteAddress.TAAddressCount = 1;
+    ConnectInfo->RemoteAddress.Address[0].AddressType = Address->sa_family;
+    ConnectInfo->RemoteAddress.Address[0].AddressLength = AddressLength - FIELD_OFFSET(struct sockaddr, sa_data);
+    RtlCopyMemory(&ConnectInfo->RemoteAddress.Address[0].Address,
+                  Address->sa_data,
+                  ConnectInfo->RemoteAddress.Address[0].AddressLength);
+
+    Status = NtDeviceIoControlFile(SocketHandle,
+                                   Event,
+                                   NULL,
+                                   NULL,
+                                   &IoStatus,
+                                   IOCTL_AFD_CONNECT,
+                                   ConnectInfo,
+                                   ConnectInfoLength,
+                                   NULL,
+                                   0);
+    if (Status == STATUS_PENDING)
+    {
+        NtWaitForSingleObject(Event, FALSE, NULL);
+        Status = IoStatus.Status;
+    }
+
+    RtlFreeHeap(RtlGetProcessHeap(), 0, ConnectInfo);
+    NtClose(Event);
+
+    return Status;
+}
+
+NTSTATUS
+AfdSend(
+    _In_ HANDLE SocketHandle,
+    _In_ const void *Buffer,
+    _In_ ULONG BufferLength)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatus;
+    AFD_SEND_INFO SendInfo;
+    HANDLE Event;
+    AFD_WSABUF AfdBuffer;
+
+    Status = NtCreateEvent(&Event,
+                           EVENT_ALL_ACCESS,
+                           NULL,
+                           NotificationEvent,
+                           FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    AfdBuffer.buf = (PVOID)Buffer;
+    AfdBuffer.len = BufferLength;
+    SendInfo.BufferArray = &AfdBuffer;
+    SendInfo.BufferCount = 1;
+    SendInfo.TdiFlags = 0;
+    SendInfo.AfdFlags = 0;
+
+    Status = NtDeviceIoControlFile(SocketHandle,
+                                   Event,
+                                   NULL,
+                                   NULL,
+                                   &IoStatus,
+                                   IOCTL_AFD_SEND,
+                                   &SendInfo,
+                                   sizeof(SendInfo),
+                                   NULL,
+                                   0);
+    if (Status == STATUS_PENDING)
+    {
+        NtWaitForSingleObject(Event, FALSE, NULL);
+        Status = IoStatus.Status;
+    }
+
+    NtClose(Event);
+
+    return Status;
+}
+
+NTSTATUS
+AfdSendTo(
+    _In_ HANDLE SocketHandle,
+    _In_ const void *Buffer,
+    _In_ ULONG BufferLength,
+    _In_ const struct sockaddr *Address,
+    _In_ ULONG AddressLength)
+{
+    NTSTATUS Status;
+    IO_STATUS_BLOCK IoStatus;
+    AFD_SEND_INFO_UDP SendInfo;
+    HANDLE Event;
+    AFD_WSABUF AfdBuffer;
+    PTRANSPORT_ADDRESS TransportAddress;
+    ULONG TransportAddressLength;
+
+    Status = NtCreateEvent(&Event,
+                           EVENT_ALL_ACCESS,
+                           NULL,
+                           NotificationEvent,
+                           FALSE);
+    if (!NT_SUCCESS(Status))
+    {
+        return Status;
+    }
+
+    TransportAddressLength = FIELD_OFFSET(TRANSPORT_ADDRESS, Address[0].Address) +
+                             AddressLength - FIELD_OFFSET(struct sockaddr, sa_data);
+    TransportAddress = RtlAllocateHeap(RtlGetProcessHeap(),
+                                       0,
+                                       TransportAddressLength);
+    if (!TransportAddress)
+    {
+        NtClose(Event);
+        return STATUS_INSUFFICIENT_RESOURCES;
+    }
+    TransportAddress->TAAddressCount = 1;
+    TransportAddress->Address[0].AddressType = Address->sa_family;
+    TransportAddress->Address[0].AddressLength = AddressLength - FIELD_OFFSET(struct sockaddr, sa_data);
+    RtlCopyMemory(&TransportAddress->Address[0].Address,
+                  Address->sa_data,
+                  TransportAddress->Address[0].AddressLength);
+
+    AfdBuffer.buf = (PVOID)Buffer;
+    AfdBuffer.len = BufferLength;
+    RtlZeroMemory(&SendInfo, sizeof(SendInfo));
+    SendInfo.BufferArray = &AfdBuffer;
+    SendInfo.BufferCount = 1;
+    SendInfo.AfdFlags = 0;
+    SendInfo.TdiConnection.RemoteAddress = TransportAddress;
+    SendInfo.TdiConnection.RemoteAddressLength = TransportAddressLength;
+
+    Status = NtDeviceIoControlFile(SocketHandle,
+                                   Event,
+                                   NULL,
+                                   NULL,
+                                   &IoStatus,
+                                   IOCTL_AFD_SEND_DATAGRAM,
+                                   &SendInfo,
+                                   sizeof(SendInfo),
+                                   NULL,
+                                   0);
+    if (Status == STATUS_PENDING)
+    {
+        NtWaitForSingleObject(Event, FALSE, NULL);
+        Status = IoStatus.Status;
+    }
+
+    RtlFreeHeap(RtlGetProcessHeap(), 0, TransportAddress);
+    NtClose(Event);
+
+    return Status;
+}
diff --git a/modules/rostests/apitests/afd/AfdHelpers.h b/modules/rostests/apitests/afd/AfdHelpers.h
new file mode 100644 (file)
index 0000000..fadd03d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * PROJECT:     ReactOS API Tests
+ * LICENSE:     LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
+ * PURPOSE:     Utility function declarations for calling AFD
+ * COPYRIGHT:   Copyright 2015 Thomas Faber (thomas.faber@reactos.org)
+ */
+
+#pragma once
+
+NTSTATUS
+AfdCreateSocket(
+    _Out_ PHANDLE SocketHandle,
+    _In_ int AddressFamily,
+    _In_ int SocketType,
+    _In_ int Protocol);
+
+NTSTATUS
+AfdBind(
+    _In_ HANDLE SocketHandle,
+    _In_ const struct sockaddr *Address,
+    _In_ ULONG AddressLength);
+
+NTSTATUS
+AfdConnect(
+    _In_ HANDLE SocketHandle,
+    _In_ const struct sockaddr *Address,
+    _In_ ULONG AddressLength);
+
+NTSTATUS
+AfdSend(
+    _In_ HANDLE SocketHandle,
+    _In_ const void *Buffer,
+    _In_ ULONG BufferLength);
+
+NTSTATUS
+AfdSendTo(
+    _In_ HANDLE SocketHandle,
+    _In_ const void *Buffer,
+    _In_ ULONG BufferLength,
+    _In_ const struct sockaddr *Address,
+    _In_ ULONG AddressLength);
diff --git a/modules/rostests/apitests/afd/CMakeLists.txt b/modules/rostests/apitests/afd/CMakeLists.txt
new file mode 100644 (file)
index 0000000..412c27e
--- /dev/null
@@ -0,0 +1,15 @@
+
+include_directories(
+    ${REACTOS_SOURCE_DIR}/sdk/include/reactos/drivers)
+
+list(APPEND SOURCE
+    AfdHelpers.c
+    send.c
+    precomp.h)
+
+add_executable(afd_apitest ${SOURCE} testlist.c)
+target_link_libraries(afd_apitest wine)
+set_module_type(afd_apitest win32cui)
+add_importlibs(afd_apitest ws2_32 msvcrt kernel32 ntdll)
+add_pch(afd_apitest precomp.h SOURCE)
+add_rostests_file(TARGET afd_apitest)
diff --git a/modules/rostests/apitests/afd/precomp.h b/modules/rostests/apitests/afd/precomp.h
new file mode 100644 (file)
index 0000000..c9a2c84
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * PROJECT:     ReactOS API Tests
+ * LICENSE:     LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
+ * PURPOSE:     Precompiled header for afd_apitest
+ * COPYRIGHT:   Copyright 2018 Thomas Faber (thomas.faber@reactos.org)
+ */
+
+#if !defined(_AFD_APITEST_PRECOMP_H_)
+#define _AFD_APITEST_PRECOMP_H_
+
+#include <apitest.h>
+
+#define WIN32_NO_STATUS
+#include <ndk/exfuncs.h>
+#include <ndk/iofuncs.h>
+#include <ndk/obfuncs.h>
+#include <ndk/rtlfuncs.h>
+
+#include <winsock2.h>
+#include <tcpioctl.h>
+#include <tdi.h>
+#include <afd/shared.h>
+
+#include "AfdHelpers.h"
+
+#endif /* _AFD_APITEST_PRECOMP_H_ */
diff --git a/modules/rostests/apitests/afd/send.c b/modules/rostests/apitests/afd/send.c
new file mode 100644 (file)
index 0000000..9452e78
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * PROJECT:     ReactOS API Tests
+ * LICENSE:     LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
+ * PURPOSE:     Test for IOCTL_AFD_SEND/IOCTL_AFD_SEND_DATAGRAM
+ * COPYRIGHT:   Copyright 2015 Thomas Faber (thomas.faber@reactos.org)
+ */
+
+#include "precomp.h"
+
+static
+void
+TestSend(void)
+{
+    NTSTATUS Status;
+    HANDLE SocketHandle;
+    CHAR Buffer[32];
+    struct sockaddr_in addr;
+
+    RtlZeroMemory(Buffer, sizeof(Buffer));
+
+    Status = AfdCreateSocket(&SocketHandle, AF_INET, SOCK_STREAM, IPPROTO_TCP);
+    ok(Status == STATUS_SUCCESS, "AfdCreateSocket failed with %lx\n", Status);
+
+    Status = AfdSend(SocketHandle, NULL, 0);
+    ok(Status == STATUS_INVALID_CONNECTION, "AfdSend failed with %lx\n", Status);
+
+    Status = AfdSend(SocketHandle, Buffer, sizeof(Buffer));
+    ok(Status == STATUS_INVALID_CONNECTION, "AfdSend failed with %lx\n", Status);
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
+    addr.sin_port = htons(0);
+
+    Status = AfdBind(SocketHandle, (const struct sockaddr *)&addr, sizeof(addr));
+    ok(Status == STATUS_SUCCESS, "AfdBind failed with %lx\n", Status);
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = inet_addr("8.8.8.8");
+    addr.sin_port = htons(53);
+
+    Status = AfdConnect(SocketHandle, (const struct sockaddr *)&addr, sizeof(addr));
+    ok(Status == STATUS_SUCCESS, "AfdConnect failed with %lx\n", Status);
+
+    Status = AfdSend(SocketHandle, NULL, 0);
+    ok(Status == STATUS_SUCCESS, "AfdSend failed with %lx\n", Status);
+
+    Status = AfdSend(SocketHandle, Buffer, sizeof(Buffer));
+    ok(Status == STATUS_SUCCESS, "AfdSend failed with %lx\n", Status);
+
+    NtClose(SocketHandle);
+}
+
+static
+void
+TestSendTo(void)
+{
+    NTSTATUS Status;
+    HANDLE SocketHandle;
+    CHAR Buffer[32];
+    struct sockaddr_in addr;
+
+    RtlZeroMemory(Buffer, sizeof(Buffer));
+
+    Status = AfdCreateSocket(&SocketHandle, AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+    ok(Status == STATUS_SUCCESS, "AfdCreateSocket failed with %lx\n", Status);
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
+    addr.sin_port = htons(0);
+
+    Status = AfdBind(SocketHandle, (const struct sockaddr *)&addr, sizeof(addr));
+    ok(Status == STATUS_SUCCESS, "AfdBind failed with %lx\n", Status);
+
+    memset(&addr, 0, sizeof(addr));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = inet_addr("8.8.8.8");
+    addr.sin_port = htons(53);
+
+    Status = AfdSendTo(SocketHandle, NULL, 0, (const struct sockaddr *)&addr, sizeof(addr));
+    ok(Status == STATUS_SUCCESS, "AfdSendTo failed with %lx\n", Status);
+
+    Status = AfdSendTo(SocketHandle, Buffer, sizeof(Buffer), (const struct sockaddr *)&addr, sizeof(addr));
+    ok(Status == STATUS_SUCCESS, "AfdSendTo failed with %lx\n", Status);
+
+    NtClose(SocketHandle);
+}
+
+START_TEST(send)
+{
+    TestSend();
+    TestSendTo();
+}
diff --git a/modules/rostests/apitests/afd/testlist.c b/modules/rostests/apitests/afd/testlist.c
new file mode 100644 (file)
index 0000000..17c82b9
--- /dev/null
@@ -0,0 +1,10 @@
+#define STANDALONE
+#include <apitest.h>
+
+extern void func_send(void);
+
+const struct test winetest_testlist[] =
+{
+    { "send", func_send },
+    { 0, 0 }
+};