--- /dev/null
+/*
+ * 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 */
+