Vijay Shrivastav is currently developing a "Windows NT TDI Reference" at
http://www.rabox.com/atage/ and this promises to be a useful resource for TDI
client development.
Gary Nebbett
-------------------------
extern "C" {
#include <ntddk.h>
#include "tdikrnl.h"
}
NTSTATUS
CreateConnection(PHANDLE Handle, PFILE_OBJECT *FileObject)
{
UNICODE_STRING Name; RtlInitUnicodeString(&Name, L"\\Device\\Tcp");
OBJECT_ATTRIBUTES Attr; InitializeObjectAttributes(&Attr, &Name,
OBJ_CASE_INSENSITIVE, 0, 0);
CHAR Buffer[sizeof (FILE_FULL_EA_INFORMATION) +
TDI_CONNECTION_CONTEXT_LENGTH];
PFILE_FULL_EA_INFORMATION Ea = PFILE_FULL_EA_INFORMATION(Buffer);
Ea->NextEntryOffset = 0;
Ea->Flags = 0;
Ea->EaNameLength = TDI_CONNECTION_CONTEXT_LENGTH;
Ea->EaValueLength = 0;
RtlCopyMemory(Ea->EaName, TdiConnectionContext, Ea->EaNameLength + 1);
IO_STATUS_BLOCK IoStatus;
NTSTATUS Status = ZwCreateFile(Handle, 0, &Attr, &IoStatus, 0,
FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, 0, Ea, sizeof Buffer);
if (!NT_SUCCESS(Status)) return Status;
return ObReferenceObjectByHandle(*Handle, GENERIC_READ | GENERIC_WRITE, 0,
KernelMode,
(PVOID *)FileObject, 0);
}
NTSTATUS
CreateAddress(PHANDLE Handle, PFILE_OBJECT *FileObject)
{
UNICODE_STRING Name; RtlInitUnicodeString(&Name, L"\\Device\\Tcp");
OBJECT_ATTRIBUTES Attr; InitializeObjectAttributes(&Attr, &Name,
OBJ_CASE_INSENSITIVE, 0, 0);
CHAR Buffer[sizeof (FILE_FULL_EA_INFORMATION) +
TDI_TRANSPORT_ADDRESS_LENGTH +
sizeof (TA_IP_ADDRESS)];
PFILE_FULL_EA_INFORMATION Ea = PFILE_FULL_EA_INFORMATION(Buffer);
Ea->NextEntryOffset = 0;
Ea->Flags = 0;
Ea->EaNameLength = TDI_TRANSPORT_ADDRESS_LENGTH;
Ea->EaValueLength = sizeof (TA_IP_ADDRESS);
RtlCopyMemory(Ea->EaName, TdiTransportAddress, Ea->EaNameLength + 1);
PTA_IP_ADDRESS Sin = PTA_IP_ADDRESS(Ea->EaName + Ea->EaNameLength + 1);
Sin->TAAddressCount = 1;
Sin->Address[0].AddressLength = TDI_ADDRESS_LENGTH_IP;
Sin->Address[0].AddressType = TDI_ADDRESS_TYPE_IP;
Sin->Address[0].Address[0].sin_port = 0;
Sin->Address[0].Address[0].in_addr = 0;
RtlZeroMemory(Sin->Address[0].Address[0].sin_zero, sizeof
Sin->Address[0].Address[0].sin_zero);
IO_STATUS_BLOCK IoStatus;
NTSTATUS Status = ZwCreateFile(Handle, 0, &Attr, &IoStatus, 0,
FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, 0, Ea, sizeof Buffer);
if (!NT_SUCCESS(Status)) return Status;
return ObReferenceObjectByHandle(*Handle, GENERIC_READ | GENERIC_WRITE, 0,
KernelMode,
(PVOID *)FileObject, 0);
}
NTSTATUS
SetEventHandler(PFILE_OBJECT FileObject, LONG EventType, PVOID EventHandler,
PVOID EventContext)
{
KEVENT Event; KeInitializeEvent(&Event, NotificationEvent, FALSE);
PDEVICE_OBJECT DeviceObject = IoGetRelatedDeviceObject(FileObject);
IO_STATUS_BLOCK IoStatus;
PIRP Irp = TdiBuildInternalDeviceControlIrp(TDI_SET_EVENT_HANDLER,
DeviceObject, FileObject,
&Event, &IoStatus);
if (Irp == 0) return STATUS_INSUFFICIENT_RESOURCES;
TdiBuildSetEventHandler(Irp, DeviceObject, FileObject, 0, 0,
EventType, EventHandler, EventContext);
NTSTATUS Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
Status = KeWaitForSingleObject(&Event, UserRequest, KernelMode, FALSE, 0);
return Status == STATUS_SUCCESS ? IoStatus.Status : Status;
}
NTSTATUS
Bind(PFILE_OBJECT FileObject, HANDLE Address)
{
KEVENT Event; KeInitializeEvent(&Event, NotificationEvent, FALSE);
PDEVICE_OBJECT DeviceObject = IoGetRelatedDeviceObject(FileObject);
IO_STATUS_BLOCK IoStatus;
PIRP Irp = TdiBuildInternalDeviceControlIrp(TDI_ASSOCIATE_ADDRESS,
DeviceObject, FileObject,
&Event, &IoStatus);
if (Irp == 0) return STATUS_INSUFFICIENT_RESOURCES;
TdiBuildAssociateAddress(Irp, DeviceObject, FileObject, 0, 0, Address);
NTSTATUS Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
Status = KeWaitForSingleObject(&Event, UserRequest, KernelMode, FALSE, 0);
return Status == STATUS_SUCCESS ? IoStatus.Status : Status;
}
NTSTATUS
Connect(PFILE_OBJECT FileObject, ULONG Addr, USHORT Port)
{
KEVENT Event; KeInitializeEvent(&Event, NotificationEvent, FALSE);
PDEVICE_OBJECT DeviceObject = IoGetRelatedDeviceObject(FileObject);
IO_STATUS_BLOCK IoStatus;
PIRP Irp = TdiBuildInternalDeviceControlIrp(TDI_CONNECT, DeviceObject,
FileObject,
&Event, &IoStatus);
if (Irp == 0) return STATUS_INSUFFICIENT_RESOURCES;
TA_IP_ADDRESS DstAddr = {1, {TDI_ADDRESS_LENGTH_IP, TDI_ADDRESS_TYPE_IP,
{Port, Addr}}};
TDI_CONNECTION_INFORMATION Dst = {0, 0, 0, 0, sizeof DstAddr, &DstAddr};
TdiBuildConnect(Irp, DeviceObject, FileObject, 0, 0, 0, &Dst, 0);
NTSTATUS Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
Status = KeWaitForSingleObject(&Event, UserRequest, KernelMode, FALSE, 0);
return Status == STATUS_SUCCESS ? IoStatus.Status : Status;
}
NTSTATUS
Disconnect(PFILE_OBJECT FileObject)
{
KEVENT Event; KeInitializeEvent(&Event, NotificationEvent, FALSE);
PDEVICE_OBJECT DeviceObject = IoGetRelatedDeviceObject(FileObject);
IO_STATUS_BLOCK IoStatus;
PIRP Irp = TdiBuildInternalDeviceControlIrp(TDI_DISCONNECT, DeviceObject,
FileObject,
&Event, &IoStatus);
if (Irp == 0) return STATUS_INSUFFICIENT_RESOURCES;
TdiBuildDisconnect(Irp, DeviceObject, FileObject, 0, 0, 0,
TDI_DISCONNECT_RELEASE, 0, 0);
NTSTATUS Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
Status = KeWaitForSingleObject(&Event, UserRequest, KernelMode, FALSE, 0);
return Status == STATUS_SUCCESS ? IoStatus.Status : Status;
}
NTSTATUS
Send(PFILE_OBJECT FileObject, PVOID Data, ULONG Length)
{
KEVENT Event; KeInitializeEvent(&Event, NotificationEvent, FALSE);
PDEVICE_OBJECT DeviceObject = IoGetRelatedDeviceObject(FileObject);
IO_STATUS_BLOCK IoStatus;
PIRP Irp = TdiBuildInternalDeviceControlIrp(TDI_SEND, DeviceObject,
FileObject,
&Event, &IoStatus);
if (Irp == 0) return STATUS_INSUFFICIENT_RESOURCES;
PMDL Mdl = IoAllocateMdl(Data, Length, FALSE, FALSE, Irp);
if (Mdl == 0) return STATUS_INSUFFICIENT_RESOURCES;
MmProbeAndLockPages(Mdl, KernelMode, IoModifyAccess);
TdiBuildSend(Irp, DeviceObject, FileObject, 0, 0, Mdl, 0, Length);
NTSTATUS Status = IoCallDriver(DeviceObject, Irp);
if (Status == STATUS_PENDING)
Status = KeWaitForSingleObject(&Event, UserRequest, KernelMode, FALSE, 0);
return Status == STATUS_SUCCESS ? IoStatus.Status : Status;
}
NTSTATUS
Recv(PFILE_OBJECT FileObject, PVOID Data, ULONG Length, PKEVENT Event,
PIO_STATUS_BLOCK IoStatus)
{
PDEVICE_OBJECT DeviceObject = IoGetRelatedDeviceObject(FileObject);
PIRP Irp = TdiBuildInternalDeviceControlIrp(TDI_RECEIVE, DeviceObject,
FileObject,
Event, IoStatus);
if (Irp == 0) return STATUS_INSUFFICIENT_RESOURCES;
PMDL Mdl = IoAllocateMdl(Data, Length, FALSE, FALSE, Irp);
MmProbeAndLockPages(Mdl, KernelMode, IoModifyAccess);
TdiBuildReceive(Irp, DeviceObject, FileObject, 0, 0, Mdl,
TDI_RECEIVE_NORMAL, Length);
return IoCallDriver(DeviceObject, Irp);
}
NTSTATUS
EventDisconnect(PVOID EventContext, CONNECTION_CONTEXT, LONG, PVOID, LONG,
PVOID, ULONG Flags)
{
DbgPrint("Flags = %lx\n", Flags);
KeSetEvent(PKEVENT(EventContext), 0, FALSE);
return STATUS_SUCCESS;
}
NTSTATUS
EventError(PVOID, NTSTATUS Status)
{
DbgPrint("Error = %lx\n", Status);
return STATUS_SUCCESS;
}
NTSTATUS
EventReceive(PVOID, CONNECTION_CONTEXT, ULONG Flags, ULONG Indicated, ULONG
Available,
PULONG Taken, PVOID, PIRP *Irp)
{
DbgPrint("Receive Flags = %lx, Ind = %ld, Avl = %ld\n", Flags, Indicated,
Available);
*Taken = Available; *Irp = 0;
return STATUS_SUCCESS;
}
NTSTATUS
Test()
{
PFILE_OBJECT ConnectionFileObject, AddressFileObject;
HANDLE AddressHandle, ConnectionHandle;
CHAR Buffer[80], Data[] = "Hello from Gary";
NTSTATUS Status;
KEVENT Done; KeInitializeEvent(&Done, NotificationEvent, FALSE);
Status = CreateConnection(&ConnectionHandle, &ConnectionFileObject);
if (!NT_SUCCESS(Status)) return Status;
Status = CreateAddress(&AddressHandle, &AddressFileObject);
if (!NT_SUCCESS(Status)) return Status;
do {
Status = SetEventHandler(AddressFileObject, TDI_EVENT_DISCONNECT,
EventDisconnect, &Done);
if (!NT_SUCCESS(Status)) break;
Status = SetEventHandler(AddressFileObject, TDI_EVENT_ERROR, EventError, 0);
if (!NT_SUCCESS(Status)) break;
Status = SetEventHandler(AddressFileObject, TDI_EVENT_RECEIVE, EventReceive,
0);
if (!NT_SUCCESS(Status)) break;
Status = Bind(ConnectionFileObject, AddressHandle);
if (!NT_SUCCESS(Status)) break;
Status = Connect(ConnectionFileObject, 0x41A1F6A8, 0x700);
if (!NT_SUCCESS(Status)) break;
IO_STATUS_BLOCK IoStatus;
KEVENT Event; KeInitializeEvent(&Event, NotificationEvent, FALSE);
Status = Recv(ConnectionFileObject, Buffer, sizeof Buffer, &Event, &IoStatus);
if (!NT_SUCCESS(Status)) break;
Status = Send(ConnectionFileObject, Data, sizeof Data);
if (!NT_SUCCESS(Status)) break;
Status = Send(ConnectionFileObject, Data, sizeof Data);
if (!NT_SUCCESS(Status)) break;
Status = Send(ConnectionFileObject, Data, sizeof Data);
if (!NT_SUCCESS(Status)) break;
Status = Disconnect(ConnectionFileObject);
if (!NT_SUCCESS(Status)) break;
Status = KeWaitForSingleObject(&Event, UserRequest, KernelMode, FALSE, 0);
if (Status == STATUS_SUCCESS) Status = IoStatus.Status;
DbgPrint("Status = %lx, IoStatus.Status = %lx, IoStatus.Information = %ld\n",
Status, IoStatus.Status, IoStatus.Information);
KeWaitForSingleObject(&Done, UserRequest, KernelMode, FALSE, 0);
} while (0);
ObDereferenceObject(ConnectionFileObject);
ObDereferenceObject(AddressFileObject);
ZwClose(ConnectionHandle);
ZwClose(AddressHandle);
return Status;
}
NTSTATUS
DriverOpenClose(PDEVICE_OBJECT, PIRP Irp)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
VOID
DriverUnload(PDRIVER_OBJECT DriverObject)
{
IoDeleteDevice(DriverObject->DeviceObject);
}
extern "C" NTSTATUS
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
NTSTATUS Status = Test();
if (!NT_SUCCESS(Status)) return Status;
DriverObject->MajorFunction[IRP_MJ_CREATE] = DriverOpenClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DriverOpenClose;
DriverObject->DriverUnload = DriverUnload;
UNICODE_STRING Name; RtlInitUnicodeString(&Name, L"\\Device\\TdiTest");
PDEVICE_OBJECT DeviceObject;
return IoCreateDevice(DriverObject, 0, &Name, FILE_DEVICE_UNKNOWN, 0, TRUE,
&DeviceObject);
}