Git conversion: Make reactos the root directory, move rosapps, rostests, wallpapers...
[reactos.git] / modules / rosapps / applications / net / tditest / tditest / tditest.c
diff --git a/modules/rosapps/applications/net/tditest/tditest/tditest.c b/modules/rosapps/applications/net/tditest/tditest/tditest.c
new file mode 100644 (file)
index 0000000..38ab0d7
--- /dev/null
@@ -0,0 +1,952 @@
+/*
+ * COPYRIGHT:   See COPYING in the top level directory
+ * PROJECT:     ReactOS TDI test driver
+ * FILE:        tditest.c
+ * PURPOSE:     Testing TDI drivers
+ * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net)
+ *              Vizzini (vizzini@plasmic.com)
+ * REVISIONS:
+ *   CSH 01/08-2000 Created
+ *   26-Nov-2003 Vizzini Updated to run properly on Win2ksp4
+ */
+#include <tditest.h>
+#include <pseh/pseh2.h>
+
+
+#if DBG
+
+/* See debug.h for debug/trace constants */
+ULONG DebugTraceLevel = -1;
+
+#endif /* DBG */
+
+
+HANDLE TdiTransport             = 0;
+PFILE_OBJECT TdiTransportObject = NULL;
+ULONG LocalAddress;
+BOOLEAN OpenError;
+KEVENT StopEvent;
+HANDLE SendThread;
+HANDLE ReceiveThread;
+
+NTSTATUS TdiCall(
+    PIRP Irp,
+    PDEVICE_OBJECT DeviceObject,
+    PIO_STATUS_BLOCK IoStatusBlock,
+    BOOLEAN CanCancel)
+/*
+ * FUNCTION: Calls a transport driver device
+ * ARGUMENTS:
+ *     Irp           = Pointer to I/O Request Packet
+ *     DeviceObject  = Pointer to device object to call
+ *     IoStatusBlock = Address of buffer with I/O status block
+ *     CanCancel     = TRUE if the IRP can be cancelled, FALSE if not
+ * RETURNS:
+ *     Status of operation
+ * NOTES
+ *     All requests are completed synchronously. A request may be cancelled
+ */
+{
+       KEVENT Event;
+       PKEVENT Events[2];
+       NTSTATUS Status;
+       Events[0] = &StopEvent;
+       Events[1] = &Event;
+
+       KeInitializeEvent(&Event, NotificationEvent, FALSE);
+       Irp->UserEvent = &Event;
+       Irp->UserIosb  = IoStatusBlock;
+
+       Status = IoCallDriver(DeviceObject, Irp);
+
+       if (Status == STATUS_PENDING)
+               {
+                       if (CanCancel)
+                               {
+                                       Status = KeWaitForMultipleObjects(2, (PVOID)Events, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);
+
+                                       if (KeReadStateEvent(&StopEvent) != 0)
+                                               {
+                                                       if (IoCancelIrp(Irp))
+                                                               {
+                                                                       TDI_DbgPrint(MAX_TRACE, ("Cancelled IRP.\n"));
+                                                               }
+                                                       else
+                                                               {
+                                                                       TDI_DbgPrint(MIN_TRACE, ("Could not cancel IRP.\n"));
+                                                               }
+                                                       return STATUS_CANCELLED;
+                                               }
+                               }
+                       else
+                               Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
+               }
+
+       return (Status == STATUS_SUCCESS)? IoStatusBlock->Status : STATUS_SUCCESS;
+}
+
+
+NTSTATUS TdiOpenDevice(
+    PWSTR Protocol,
+    ULONG EaLength,
+    PFILE_FULL_EA_INFORMATION EaInfo,
+    PHANDLE Handle,
+    PFILE_OBJECT *Object)
+/*
+ * FUNCTION: Opens a device
+ * ARGUMENTS:
+ *     Protocol = Pointer to buffer with name of device
+ *     EaLength = Length of EA information
+ *     EaInfo   = Pointer to buffer with EA information
+ *     Handle   = Address of buffer to place device handle
+ *     Object   = Address of buffer to place device object
+ * RETURNS:
+ *     Status of operation
+ */
+{
+       OBJECT_ATTRIBUTES Attr;
+       IO_STATUS_BLOCK Iosb;
+       UNICODE_STRING Name;
+       NTSTATUS Status;
+
+       RtlInitUnicodeString(&Name, Protocol);
+       InitializeObjectAttributes(
+               &Attr,                   /* Attribute buffer */
+               &Name,                   /* Device name */
+               OBJ_CASE_INSENSITIVE,    /* Attributes */
+               NULL,                    /* Root directory */
+               NULL);                   /* Security descriptor */
+
+       Status = ZwCreateFile(
+               Handle,                               /* Return file handle */
+               GENERIC_READ | GENERIC_WRITE,         /* Desired access */
+               &Attr,                                /* Object attributes */
+               &Iosb,                                /* IO status */
+               0,                                    /* Initial allocation size */
+               FILE_ATTRIBUTE_NORMAL,                /* File attributes */
+               FILE_SHARE_READ | FILE_SHARE_WRITE,   /* Share access */
+               FILE_OPEN_IF,                         /* Create disposition */
+               0,                                    /* Create options */
+               EaInfo,                               /* EA buffer */
+               EaLength);                            /* EA length */
+
+       if (NT_SUCCESS(Status))
+               {
+                       Status  = ObReferenceObjectByHandle(
+                               *Handle,                        /* Handle to open file */
+                               GENERIC_READ | GENERIC_WRITE,   /* Access mode */
+                               NULL,                           /* Object type */
+                               KernelMode,                     /* Access mode */
+                               (PVOID*)Object,                 /* Pointer to object */
+                               NULL);                          /* Handle information */
+
+                       if (!NT_SUCCESS(Status))
+                               {
+                                       TDI_DbgPrint(MIN_TRACE, ("ObReferenceObjectByHandle() failed with status (0x%X).\n", Status));
+                                       ZwClose(*Handle);
+                               }
+               }
+       else
+               {
+                       TDI_DbgPrint(MIN_TRACE, ("ZwCreateFile() failed with status (0x%X)\n", Status));
+               }
+
+    return Status;
+}
+
+
+NTSTATUS TdiCloseDevice(
+    HANDLE Handle,
+    PFILE_OBJECT FileObject)
+{
+       if (FileObject)
+               ObDereferenceObject(FileObject);
+
+       if (Handle)
+               ZwClose(Handle);
+
+       return STATUS_SUCCESS;
+}
+
+
+NTSTATUS TdiOpenTransport(
+    PWSTR Protocol,
+    USHORT Port,
+    PHANDLE Transport,
+    PFILE_OBJECT *TransportObject)
+/*
+ * FUNCTION: Opens a transport driver
+ * ARGUMENTS:
+ *     Protocol        = Pointer to buffer with name of device
+ *     Port            = Port number to use
+ *     Transport       = Address of buffer to place transport device handle
+ *     TransportObject = Address of buffer to place transport object
+ * RETURNS:
+ *     Status of operation
+ */
+{
+       PFILE_FULL_EA_INFORMATION EaInfo;
+       PTA_IP_ADDRESS Address;
+       NTSTATUS Status;
+       ULONG EaLength;
+
+       /* EaName must be 0-termed, even though TDI_TRANSPORT_ADDRESS_LENGTH does *not* include the 0 */
+       EaLength = sizeof(FILE_FULL_EA_INFORMATION) + TDI_TRANSPORT_ADDRESS_LENGTH + sizeof(TA_IP_ADDRESS) + 1;
+       EaInfo = (PFILE_FULL_EA_INFORMATION)ExAllocatePool(NonPagedPool, EaLength);
+
+       if (!EaInfo)
+               {
+                       TDI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+                       return STATUS_INSUFFICIENT_RESOURCES;
+               }
+
+       RtlZeroMemory(EaInfo, EaLength);
+
+       EaInfo->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
+
+       /* don't copy the 0; we have already zeroed it */
+       RtlCopyMemory(EaInfo->EaName, TdiTransportAddress, TDI_TRANSPORT_ADDRESS_LENGTH);
+
+       EaInfo->EaValueLength = sizeof(TA_IP_ADDRESS);
+       Address = (PTA_IP_ADDRESS)(EaInfo->EaName + TDI_TRANSPORT_ADDRESS_LENGTH + 1); // 0-term
+       Address->TAAddressCount                 = 1;
+       Address->Address[0].AddressLength       = TDI_ADDRESS_LENGTH_IP;
+       Address->Address[0].AddressType         = TDI_ADDRESS_TYPE_IP;
+       Address->Address[0].Address[0].sin_port = WH2N(Port);
+       Address->Address[0].Address[0].in_addr  = 0;
+
+       Status = TdiOpenDevice(Protocol, EaLength, EaInfo, Transport, TransportObject);
+
+       ExFreePool(EaInfo);
+
+       return Status;
+}
+
+
+NTSTATUS TdiQueryDeviceControl(
+    PFILE_OBJECT FileObject,
+    ULONG IoControlCode,
+    PVOID InputBuffer,
+    ULONG InputBufferLength,
+    PVOID OutputBuffer,
+    ULONG OutputBufferLength,
+    PULONG Return)
+/*
+ * FUNCTION: Queries a device for information
+ * ARGUMENTS:
+ *     FileObject         = Pointer to device object
+ *     IoControlCode      = I/O control code
+ *     InputBuffer        = Pointer to buffer with input data
+ *     InputBufferLength  = Length of InputBuffer
+ *     OutputBuffer       = Address of buffer to place output data
+ *     OutputBufferLength = Length of OutputBuffer
+ * RETURNS:
+ *     Status of operation
+ */
+{
+       PDEVICE_OBJECT DeviceObject;
+       PIO_STACK_LOCATION IoStack;
+       IO_STATUS_BLOCK Iosb;
+       NTSTATUS Status;
+       PIRP Irp;
+
+       DeviceObject = IoGetRelatedDeviceObject(FileObject);
+       Irp = IoBuildDeviceIoControlRequest(IoControlCode, DeviceObject, InputBuffer, InputBufferLength, OutputBuffer,
+               OutputBufferLength, FALSE, NULL, NULL);
+
+       if (!Irp)
+               {
+                       TDI_DbgPrint(MIN_TRACE, ("IoBuildDeviceIoControlRequest() failed.\n"));
+                       return STATUS_INSUFFICIENT_RESOURCES;
+               }
+
+       IoStack               = IoGetNextIrpStackLocation(Irp);
+       IoStack->DeviceObject = DeviceObject;
+       IoStack->FileObject   = FileObject;
+       Status = TdiCall(Irp, DeviceObject, &Iosb, FALSE);
+
+       if (Return)
+               *Return = Iosb.Information;
+
+       return Status;
+}
+
+
+NTSTATUS TdiQueryInformationEx(
+    PFILE_OBJECT FileObject,
+    ULONG Entity,
+    ULONG Instance,
+    ULONG Class,
+    ULONG Type,
+    ULONG Id,
+    PVOID OutputBuffer,
+    PULONG OutputLength)
+/*
+ * FUNCTION: Extended query for information
+ * ARGUMENTS:
+ *     FileObject   = Pointer to transport object
+ *     Entity       = Entity
+ *     Instance     = Instance
+ *     Class        = Entity class
+ *     Type         = Entity type
+ *     Id           = Entity id
+ *     OutputBuffer = Address of buffer to place data
+ *     OutputLength = Address of buffer with length of OutputBuffer (updated)
+ * RETURNS:
+ *     Status of operation
+ */
+{
+       TCP_REQUEST_QUERY_INFORMATION_EX QueryInfo;
+
+       RtlZeroMemory(&QueryInfo, sizeof(TCP_REQUEST_QUERY_INFORMATION_EX));
+       QueryInfo.ID.toi_entity.tei_entity   = Entity;
+       QueryInfo.ID.toi_entity.tei_instance = Instance;
+       QueryInfo.ID.toi_class = Class;
+       QueryInfo.ID.toi_type  = Type;
+       QueryInfo.ID.toi_id    = Id;
+
+       return TdiQueryDeviceControl(
+               FileObject,                                /* Transport/connection object */
+               IOCTL_TCP_QUERY_INFORMATION_EX,            /* Control code */
+               &QueryInfo,                                /* Input buffer */
+               sizeof(TCP_REQUEST_QUERY_INFORMATION_EX),  /* Input buffer length */
+               OutputBuffer,                              /* Output buffer */
+               *OutputLength,                             /* Output buffer length */
+               OutputLength);                             /* Return information */
+}
+
+
+NTSTATUS TdiQueryAddress(
+    PFILE_OBJECT FileObject,
+    PULONG Address)
+/*
+ * FUNCTION: Queries for a local IP address
+ * ARGUMENTS:
+ *     FileObject = Pointer to file object
+ *     Address    = Address of buffer to place local address
+ * RETURNS:
+ *     Status of operation
+ */
+{
+       ULONG i;
+       TDIEntityID *Entities;
+       ULONG EntityCount;
+       ULONG EntityType;
+       IPSNMP_INFO SnmpInfo;
+       PIPADDR_ENTRY IpAddress;
+       ULONG BufferSize;
+       NTSTATUS Status = STATUS_SUCCESS;
+
+       TDI_DbgPrint(MAX_TRACE, ("Called\n"));
+
+       BufferSize = sizeof(TDIEntityID) * 20;
+       Entities   = (TDIEntityID*)ExAllocatePool(NonPagedPool, BufferSize);
+
+       if (!Entities)
+               {
+                       TDI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+                       return STATUS_INSUFFICIENT_RESOURCES;
+               }
+
+       /* Query device for supported entities */
+       Status = TdiQueryInformationEx(
+               FileObject,          /* File object */
+               GENERIC_ENTITY,      /* Entity */
+               TL_INSTANCE,         /* Instance */
+               INFO_CLASS_GENERIC,  /* Entity class */
+               INFO_TYPE_PROVIDER,  /* Entity type */
+               ENTITY_LIST_ID,      /* Entity id */
+               Entities,            /* Output buffer */
+               &BufferSize);        /* Output buffer size */
+
+       if (!NT_SUCCESS(Status))
+               {
+                       TDI_DbgPrint(MIN_TRACE, ("Unable to get list of supported entities (Status = 0x%X).\n", Status));
+                       ExFreePool(Entities);
+                       return Status;
+               }
+
+       /* Locate an IP entity */
+       EntityCount = BufferSize / sizeof(TDIEntityID);
+
+       TDI_DbgPrint(MAX_TRACE, ("EntityCount = %d\n", EntityCount));
+
+       for (i = 0; i < EntityCount; i++)
+               {
+                       if (Entities[i].tei_entity == CL_NL_ENTITY)
+                               {
+                                       /* Query device for entity type */
+                                       BufferSize = sizeof(EntityType);
+                                       Status = TdiQueryInformationEx(
+                                               FileObject,                  /* File object */
+                                               CL_NL_ENTITY,                /* Entity */
+                                               Entities[i].tei_instance,    /* Instance */
+                                               INFO_CLASS_GENERIC,          /* Entity class */
+                                               INFO_TYPE_PROVIDER,          /* Entity type */
+                                               ENTITY_TYPE_ID,              /* Entity id */
+                                               &EntityType,                 /* Output buffer */
+                                               &BufferSize);                /* Output buffer size */
+
+                                       if (!NT_SUCCESS(Status) || (EntityType != CL_NL_IP))
+                                               {
+                                                       TDI_DbgPrint(MIN_TRACE, ("Unable to get entity of type IP (Status = 0x%X).\n", Status));
+                                                       break;
+                                               }
+
+                                       /* Query device for SNMP information */
+                                       BufferSize = sizeof(SnmpInfo);
+                                       Status = TdiQueryInformationEx(
+                                               FileObject,                  /* File object */
+                                               CL_NL_ENTITY,                /* Entity */
+                                               Entities[i].tei_instance,    /* Instance */
+                                               INFO_CLASS_PROTOCOL,         /* Entity class */
+                                               INFO_TYPE_PROVIDER,          /* Entity type */
+                                               IP_MIB_STATS_ID,             /* Entity id */
+                                               &SnmpInfo,                   /* Output buffer */
+                                               &BufferSize);                /* Output buffer size */
+
+                                       if (!NT_SUCCESS(Status) || (SnmpInfo.NumAddr == 0))
+                                               {
+                                                       TDI_DbgPrint(MIN_TRACE, ("Unable to get SNMP information or no IP addresses available (Status = 0x%X).\n", Status));
+                                                       break;
+                                               }
+
+                                       /* Query device for all IP addresses */
+                                       if (SnmpInfo.NumAddr != 0)
+                                               {
+                                                       BufferSize = SnmpInfo.NumAddr * sizeof(IPADDR_ENTRY);
+                                                       IpAddress = (PIPADDR_ENTRY)ExAllocatePool(NonPagedPool, BufferSize);
+                                                       if (!IpAddress)
+                                                               {
+                                                                       TDI_DbgPrint(MIN_TRACE, ("Insufficient resources.\n"));
+                                                                       break;
+                                                               }
+
+                                               Status = TdiQueryInformationEx(
+                                                       FileObject,                  /* File object */
+                                                       CL_NL_ENTITY,                /* Entity */
+                                                       Entities[i].tei_instance,    /* Instance */
+                                                       INFO_CLASS_PROTOCOL,         /* Entity class */
+                                                       INFO_TYPE_PROVIDER,          /* Entity type */
+                                                       IP_MIB_ADDRTABLE_ENTRY_ID,   /* Entity id */
+                                                       IpAddress,                   /* Output buffer */
+                                                       &BufferSize);                /* Output buffer size */
+
+                                               if (!NT_SUCCESS(Status))
+                                                       {
+                                                               TDI_DbgPrint(MIN_TRACE, ("Unable to get IP address (Status = 0x%X).\n", Status));
+                                                               ExFreePool(IpAddress);
+                                                               break;
+                                                       }
+
+                                               if (SnmpInfo.NumAddr != 1)
+                                                       {
+                                                               /* Skip loopback address */
+                                                               *Address = DN2H(((PIPADDR_ENTRY)((ULONG)IpAddress + sizeof(IPADDR_ENTRY)))->Addr);
+                                                       }
+                                               else
+                                                       {
+                                                               /* Select the first address returned */
+                                                               *Address = DN2H(IpAddress->Addr);
+                                                       }
+                                                               ExFreePool(IpAddress);
+
+                                               }
+                                       else
+                                               {
+                                                       Status = STATUS_UNSUCCESSFUL;
+                                                       break;
+                                       }
+                       }
+       }
+
+       ExFreePool(Entities);
+
+       TDI_DbgPrint(MAX_TRACE, ("Leaving\n"));
+
+       return Status;
+}
+
+
+NTSTATUS TdiSendDatagram(
+    PFILE_OBJECT TransportObject,
+    USHORT Port,
+    ULONG Address,
+    PVOID Buffer,
+    ULONG BufferSize)
+/*
+ * FUNCTION: Sends a datagram
+ * ARGUMENTS:
+ *     TransportObject = Pointer to transport object
+ *     Port            = Remote port
+ *     Address         = Remote address
+ *     Buffer          = Pointer to buffer with data to send
+ *     BufferSize      = Length of Buffer
+ * RETURNS:
+ *     Status of operation
+ */
+{
+       PIRP Irp;
+       PMDL Mdl;
+       PDEVICE_OBJECT DeviceObject;
+       PTDI_CONNECTION_INFORMATION ConnectInfo;
+       PTA_IP_ADDRESS TA;
+       PTDI_ADDRESS_IP IpAddress;
+       IO_STATUS_BLOCK Iosb;
+       NTSTATUS Status;
+
+       DeviceObject = IoGetRelatedDeviceObject(TransportObject);
+       ConnectInfo  = (PTDI_CONNECTION_INFORMATION)
+               ExAllocatePool(NonPagedPool,
+               sizeof(TDI_CONNECTION_INFORMATION) +
+               sizeof(TA_IP_ADDRESS));
+
+       if (!ConnectInfo)
+               return STATUS_INSUFFICIENT_RESOURCES;
+
+       RtlZeroMemory(ConnectInfo, sizeof(TDI_CONNECTION_INFORMATION) + sizeof(TA_IP_ADDRESS));
+
+       ConnectInfo->RemoteAddressLength = sizeof(TA_IP_ADDRESS);
+       ConnectInfo->RemoteAddress       = (PUCHAR) ((ULONG)ConnectInfo + sizeof(TDI_CONNECTION_INFORMATION));
+
+       TA = (PTA_IP_ADDRESS)(ConnectInfo->RemoteAddress);
+       TA->TAAddressCount           = 1;
+       TA->Address[0].AddressLength = sizeof(TDI_ADDRESS_IP);
+       TA->Address[0].AddressType   = TDI_ADDRESS_TYPE_IP;
+       IpAddress           = (PTDI_ADDRESS_IP)(TA->Address[0].Address);
+       IpAddress->sin_port = WH2N(Port);
+       IpAddress->in_addr  = DH2N(Address);
+       Irp = TdiBuildInternalDeviceControlIrp(
+               TDI_SEND_DATAGRAM,   /* Sub function */
+               DeviceObject,        /* Device object */
+               TransportObject,     /* File object */
+               NULL,                /* Event */
+               NULL);               /* Return buffer */
+
+       if (!Irp)
+               {
+                       TDI_DbgPrint(MIN_TRACE, ("TdiBuildInternalDeviceControlIrp() failed.\n"));
+                       ExFreePool(ConnectInfo);
+                       return STATUS_INSUFFICIENT_RESOURCES;
+               }
+
+       Mdl = IoAllocateMdl(
+               Buffer,     /* Virtual address of buffer */
+               BufferSize, /* Length of buffer */
+               FALSE,      /* Not secondary */
+               FALSE,      /* Don't charge quota */
+               NULL);      /* Don't use IRP */
+
+       if (!Mdl)
+               {
+                       TDI_DbgPrint(MIN_TRACE, ("IoAllocateMdl() failed.\n"));
+                       IoFreeIrp(Irp);
+                       ExFreePool(ConnectInfo);
+                       return STATUS_INSUFFICIENT_RESOURCES;
+               }
+
+       _SEH2_TRY
+       {
+               MmProbeAndLockPages(Mdl, KernelMode, IoModifyAccess);
+       }
+       _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+       {
+               TDI_DbgPrint(MIN_TRACE, ("MmProbeAndLockPages() failed.\n"));
+               IoFreeMdl(Mdl);
+               IoFreeIrp(Irp);
+               ExFreePool(ConnectInfo);
+               _SEH2_YIELD(return STATUS_UNSUCCESSFUL);
+       } _SEH2_END;
+
+       TdiBuildSendDatagram(
+               Irp,               /* I/O Request Packet */
+               DeviceObject,      /* Device object */
+               TransportObject,   /* File object */
+               NULL,              /* Completion routine */
+               NULL,              /* Completion context */
+               Mdl,               /* Descriptor for data buffer */
+               BufferSize,        /* Size of data to send */
+               ConnectInfo);      /* Connection information */
+
+       Status = TdiCall(Irp, DeviceObject, &Iosb, FALSE);
+
+       ExFreePool(ConnectInfo);
+
+       return Status;
+}
+
+
+NTSTATUS TdiReceiveDatagram(
+    PFILE_OBJECT TransportObject,
+    USHORT Port,
+    PULONG Address,
+    PUCHAR Buffer,
+    PULONG BufferSize)
+/*
+ * FUNCTION: Receives a datagram
+ * ARGUMENTS:
+ *     TransportObject = Pointer to transport object
+ *     Port            = Port to receive on
+ *     Address         = Address of buffer to place remote address
+ *     Buffer          = Address of buffer to place received data
+ *     BufferSize      = Address of buffer with length of Buffer (updated)
+ * RETURNS:
+ *     Status of operation
+ */
+{
+       PTDI_CONNECTION_INFORMATION ReceiveInfo;
+       PTDI_CONNECTION_INFORMATION ReturnInfo;
+       PTA_IP_ADDRESS ReturnAddress;
+       PDEVICE_OBJECT DeviceObject;
+       PTDI_ADDRESS_IP IpAddress;
+       IO_STATUS_BLOCK Iosb;
+       PVOID MdlBuffer;
+       NTSTATUS Status;
+       PIRP Irp;
+       PMDL Mdl;
+
+       DeviceObject = IoGetRelatedDeviceObject(TransportObject);
+       if (!DeviceObject)
+               return STATUS_INVALID_PARAMETER;
+
+       ReceiveInfo = (PTDI_CONNECTION_INFORMATION) ExAllocatePool(NonPagedPool,
+               sizeof(TDI_CONNECTION_INFORMATION) +
+               sizeof(TDI_CONNECTION_INFORMATION) +
+               sizeof(TA_IP_ADDRESS));
+
+       if (!ReceiveInfo)
+               return STATUS_INSUFFICIENT_RESOURCES;
+
+       MdlBuffer = ExAllocatePool(PagedPool, *BufferSize);
+       if (!MdlBuffer)
+               return STATUS_INSUFFICIENT_RESOURCES;
+
+       RtlZeroMemory(ReceiveInfo, sizeof(TDI_CONNECTION_INFORMATION) + sizeof(TDI_CONNECTION_INFORMATION) +
+               sizeof(TA_IP_ADDRESS));
+
+       RtlCopyMemory(MdlBuffer, Buffer, *BufferSize);
+
+       /* Receive from any address */
+       ReceiveInfo->RemoteAddressLength = 0;
+       ReceiveInfo->RemoteAddress       = NULL;
+
+       ReturnInfo = (PTDI_CONNECTION_INFORMATION) ((ULONG)ReceiveInfo + sizeof(TDI_CONNECTION_INFORMATION));
+       ReturnInfo->RemoteAddressLength = sizeof(TA_IP_ADDRESS);
+       ReturnInfo->RemoteAddress       = (PUCHAR) ((ULONG)ReturnInfo + sizeof(TDI_CONNECTION_INFORMATION));
+
+       ReturnAddress = (PTA_IP_ADDRESS)(ReturnInfo->RemoteAddress);
+       ReturnAddress->TAAddressCount           = 1;
+       ReturnAddress->Address[0].AddressLength = sizeof(TDI_ADDRESS_IP);
+       ReturnAddress->Address[0].AddressType   = TDI_ADDRESS_TYPE_IP;
+
+       IpAddress = (PTDI_ADDRESS_IP)(ReturnAddress->Address[0].Address);
+       IpAddress->sin_port = WH2N(Port);
+       IpAddress->in_addr  = DH2N(LocalAddress);
+
+       Irp = TdiBuildInternalDeviceControlIrp(
+               TDI_RECEIVE_DATAGRAM,    /* Sub function */
+               DeviceObject,            /* Device object */
+               TransportObject,         /* File object */
+               NULL,                    /* Event */
+               NULL);                   /* Return buffer */
+
+       if (!Irp)
+               {
+                       ExFreePool(MdlBuffer);
+                       ExFreePool(ReceiveInfo);
+                       return STATUS_INSUFFICIENT_RESOURCES;
+               }
+
+       Mdl = IoAllocateMdl(
+               MdlBuffer,      /* Virtual address */
+               *BufferSize,    /* Length of buffer */
+               FALSE,          /* Not secondary */
+               FALSE,          /* Don't charge quota */
+               NULL);          /* Don't use IRP */
+
+       if (!Mdl)
+               {
+                       IoFreeIrp(Irp);
+                       ExFreePool(MdlBuffer);
+                       ExFreePool(ReceiveInfo);
+                       return STATUS_INSUFFICIENT_RESOURCES;
+               }
+
+       _SEH2_TRY
+       {
+               MmProbeAndLockPages(Mdl, KernelMode, IoModifyAccess);
+       }
+       _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
+       {
+               TDI_DbgPrint(MIN_TRACE, ("MmProbeAndLockPages() failed.\n"));
+               IoFreeMdl(Mdl);
+               IoFreeIrp(Irp);
+               ExFreePool(MdlBuffer);
+               ExFreePool(ReceiveInfo);
+               _SEH2_YIELD(return STATUS_INSUFFICIENT_RESOURCES);
+       } _SEH2_END;
+
+       TdiBuildReceiveDatagram(
+               Irp,                    /* I/O Request Packet */
+               DeviceObject,           /* Device object */
+               TransportObject,        /* File object */
+               NULL,                   /* Completion routine */
+               NULL,                   /* Completion context */
+               Mdl,                    /* Data buffer */
+               *BufferSize,            /* Size of data buffer */
+               ReceiveInfo,            /* Connection information */
+               ReturnInfo,             /* Connection information */
+               TDI_RECEIVE_NORMAL);    /* Flags */
+
+       Status = TdiCall(Irp, DeviceObject, &Iosb, TRUE);
+
+       if (NT_SUCCESS(Status))
+               {
+                       RtlCopyMemory(Buffer, MdlBuffer, Iosb.Information);
+                       *BufferSize = Iosb.Information;
+                       *Address    = DN2H(IpAddress->in_addr);
+               }
+
+       ExFreePool(MdlBuffer);
+       ExFreePool(ReceiveInfo);
+
+       return Status;
+}
+
+
+VOID TdiSendThread(
+    PVOID Context)
+/*
+ * FUNCTION: Send thread
+ * ARGUMENTS:
+ *     Context = Pointer to context information
+ * NOTES:
+ *     Transmits an UDP packet every two seconds to ourselves on the chosen port
+ */
+{
+       KEVENT Event;
+       PKEVENT Events[2];
+       LARGE_INTEGER Timeout;
+       NTSTATUS Status = STATUS_SUCCESS;
+       UCHAR Data[40]  = "Testing one, two, three, ...";
+
+       if (!OpenError)
+               {
+                       Timeout.QuadPart = 10000000L;           /* Second factor */
+                       Timeout.QuadPart *= 2;                  /* Number of seconds */
+                       Timeout.QuadPart = -(Timeout.QuadPart); /* Relative time */
+
+                       KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
+
+                       Events[0] = &StopEvent;
+                       Events[1] = &Event;
+
+                       while (NT_SUCCESS(Status))
+                               {
+                                       /* Wait until timeout or stop flag is set */
+                                       KeWaitForMultipleObjects( 2, (PVOID)Events, WaitAny, Executive, KernelMode, FALSE, &Timeout, NULL);
+
+                                       if (KeReadStateEvent(&StopEvent) != 0)
+                                               {
+                                                       TDI_DbgPrint(MAX_TRACE, ("Received terminate signal...\n"));
+                                                       break;
+                                               }
+
+                                       DbgPrint("Sending data - '%s'\n", Data);
+
+                                       Status = TdiSendDatagram(TdiTransportObject, TEST_PORT, LocalAddress, Data, sizeof(Data));
+
+                                       if (!NT_SUCCESS(Status))
+                                               DbgPrint("Failed sending data (Status = 0x%X)\n", Status);
+                               }
+               }
+
+       TDI_DbgPrint(MAX_TRACE, ("Terminating send thread...\n"));
+
+       PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+
+VOID TdiReceiveThread(
+    PVOID Context)
+/*
+ * FUNCTION: Receive thread
+ * ARGUMENTS:
+ *     Context = Pointer to context information
+ * NOTES:
+ *     Waits until an UDP packet is received on the chosen endpoint and displays the data
+ */
+{
+       ULONG Address;
+       UCHAR Data[40];
+       ULONG Size;
+       NTSTATUS Status = STATUS_SUCCESS;
+
+       if (!OpenError)
+               {
+                       while (NT_SUCCESS(Status))
+                               {
+                                       Size = sizeof(Data);
+                                       RtlZeroMemory(Data, Size);
+
+                                       Status = TdiReceiveDatagram(TdiTransportObject, TEST_PORT, &Address, Data, &Size);
+
+                                       if (NT_SUCCESS(Status))
+                                               {
+                                                       DbgPrint("Received data - '%s'\n", Data);
+                                               }
+                                       else
+                                               if (Status != STATUS_CANCELLED)
+                                                       {
+                                                               TDI_DbgPrint(MIN_TRACE, ("Receive error (Status = 0x%X).\n", Status));
+                                                       }
+                                               else
+                                                       {
+                                                               TDI_DbgPrint(MAX_TRACE, ("IRP was cancelled.\n"));
+                                                       }
+                               }
+               }
+
+       TDI_DbgPrint(MAX_TRACE, ("Terminating receive thread...\n"));
+
+       PsTerminateSystemThread(STATUS_SUCCESS);
+}
+
+
+VOID TdiOpenThread(
+    PVOID Context)
+/*
+ * FUNCTION: Open thread
+ * ARGUMENTS:
+ *     Context = Pointer to context information (event)
+ */
+{
+       NTSTATUS Status;
+
+       TDI_DbgPrint(MAX_TRACE, ("Called.\n"));
+
+       OpenError = TRUE;
+
+       Status = TdiOpenTransport(UDP_DEVICE_NAME, TEST_PORT, &TdiTransport, &TdiTransportObject);
+
+       if (NT_SUCCESS(Status))
+               {
+                       Status = TdiQueryAddress(TdiTransportObject, &LocalAddress);
+
+                       if (NT_SUCCESS(Status))
+                               {
+                                       OpenError = FALSE;
+                                       DbgPrint("Using local IP address 0x%X\n", LocalAddress);
+                               }
+                       else
+                               {
+                                       TDI_DbgPrint(MIN_TRACE, ("Unable to determine local IP address.\n"));
+                               }
+                       }
+       else
+               TDI_DbgPrint(MIN_TRACE, ("Cannot open transport (Status = 0x%X).\n", Status));
+
+       TDI_DbgPrint(MAX_TRACE, ("Setting close event.\n"));
+
+       KeSetEvent((PKEVENT)Context, 0, FALSE);
+
+       TDI_DbgPrint(MIN_TRACE, ("Leaving.\n"));
+}
+
+
+VOID TdiUnload(
+    PDRIVER_OBJECT DriverObject)
+/*
+ * FUNCTION: Unload routine
+ * ARGUMENTS:
+ *     DriverObject = Pointer to a driver object for this driver
+ */
+{
+       PVOID ReceiveThreadObject = 0;
+       PVOID SendThreadObject = 0;
+
+       TDI_DbgPrint(MAX_TRACE, ("Setting stop flag\n"));
+
+       /* Get pointers to the thread objects */
+       ObReferenceObjectByHandle(SendThread, THREAD_ALL_ACCESS, NULL, KernelMode, &SendThreadObject, NULL);
+       ObReferenceObjectByHandle(ReceiveThread, THREAD_ALL_ACCESS, NULL, KernelMode, &ReceiveThreadObject, NULL);
+
+       KeSetEvent(&StopEvent, 0, FALSE);
+
+       /* Wait for send thread to stop */
+       KeWaitForSingleObject(SendThreadObject, Executive, KernelMode, FALSE, NULL);
+
+       /* Wait for receive thread to stop */
+       KeWaitForSingleObject(ReceiveThreadObject, Executive, KernelMode, FALSE, NULL);
+
+       /* Close device */
+       TdiCloseDevice(TdiTransport, TdiTransportObject);
+}
+
+
+NTSTATUS
+NTAPI
+DriverEntry(
+    PDRIVER_OBJECT DriverObject,
+    PUNICODE_STRING RegistryPath)
+/*
+ * FUNCTION: Main driver entry point
+ * ARGUMENTS:
+ *     DriverObject = Pointer to a driver object for this driver
+ *     RegistryPath = Registry node for configuration parameters
+ * RETURNS:
+ *     Status of driver initialization
+ */
+{
+       KEVENT Event;
+       NTSTATUS Status;
+       WORK_QUEUE_ITEM WorkItem;
+
+       KeInitializeEvent(&StopEvent, NotificationEvent, FALSE);
+
+       /* Call TdiOpenThread() */
+       KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
+       ExInitializeWorkItem(&WorkItem, (PWORKER_THREAD_ROUTINE)TdiOpenThread, &Event);
+       ExQueueWorkItem(&WorkItem, DelayedWorkQueue);
+       KeWaitForSingleObject(&Event, Executive, KernelMode, TRUE, NULL);
+
+       /* Create a UDP send thread that sends a dgram every 2 seconds */
+       Status = PsCreateSystemThread(
+               &SendThread,                      /* Thread handle */
+               0,                                /* Desired access */
+               NULL,                             /* Object attributes */
+               NULL,                             /* Process handle */
+               NULL,                             /* Client id */
+               (PKSTART_ROUTINE)TdiSendThread,   /* Start routine */
+               NULL);                            /* Start context */
+
+       if (!NT_SUCCESS(Status))
+               {
+                       TDI_DbgPrint(MIN_TRACE, ("PsCreateSystemThread() failed for send thread (Status = 0x%X).\n", Status));
+                       return STATUS_INSUFFICIENT_RESOURCES;
+               }
+
+       /* Create a UDP receive thread */
+       Status = PsCreateSystemThread(
+               &ReceiveThread,                       /* Thread handle */
+               0,                                    /* Desired access */
+               NULL,                                 /* Object attributes */
+               NULL,                                 /* Process handle */
+               NULL,                                 /* Client id */
+               (PKSTART_ROUTINE)TdiReceiveThread,    /* Start routine */
+               NULL);                                /* Start context */
+
+       if (!NT_SUCCESS(Status))
+               {
+                       TDI_DbgPrint(MIN_TRACE, ("PsCreateSystemThread() failed for receive thread (Status = 0x%X).\n", Status));
+                       ZwClose(SendThread);
+                       return STATUS_INSUFFICIENT_RESOURCES;
+               }
+
+       DriverObject->DriverUnload = (PDRIVER_UNLOAD)TdiUnload;
+
+       return STATUS_SUCCESS;
+}
+
+/* EOF */
+