706 lines
17 KiB
C
706 lines
17 KiB
C
/*++
|
||
|
||
Module Name:
|
||
|
||
director.c
|
||
|
||
Abstract:
|
||
|
||
This module implements a driver which demonstrates the use of
|
||
the IP NAT's support for directing incoming sessions.
|
||
|
||
The driver reads a protocol, port and server-list from its 'Parameters'
|
||
subkey, and directs all sessions on the specified protocol and port
|
||
to the servers in the list, in a round-robin fashion.
|
||
|
||
The expected registry configuration is as follows:
|
||
|
||
IPNATDIR\Parameters
|
||
ServerProtocol REG_DWORD 0x6 (TCP) or 0x11 (UDP)
|
||
ServerPort REG_DWORD 1-65535
|
||
ServerList REG_MULTI_SZ List of dotted-decimal IP addresses.
|
||
|
||
Author:
|
||
|
||
Abolade Gbadegesin (aboladeg) 16-Feb-1998
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include <ntddk.h>
|
||
#include <ipnat.h>
|
||
|
||
#define NT_DEVICE_NAME L"\\Device\\IPNATDIR"
|
||
#define DOS_DEVICE_NAME L"\\DosDevices\\IPNATDIR"
|
||
#define ULONG_CHAR_LIST(a) \
|
||
((a) & 0x000000FF), (((a) & 0x0000FF00) >> 8), \
|
||
(((a) & 0x00FF0000) >> 16), (((a) & 0xFF000000) >> 24)
|
||
#define NTOHS(p) ((((p) & 0xFF00) >> 8) | (((UCHAR)(p) << 8)))
|
||
|
||
typedef struct _SERVER_ENTRY {
|
||
LIST_ENTRY Link;
|
||
ULONG Address;
|
||
ULONG SessionCount;
|
||
} SERVER_ENTRY, *PSERVER_ENTRY;
|
||
|
||
//
|
||
// GLOBAL DATA
|
||
//
|
||
|
||
IP_NAT_REGISTER_DIRECTOR DirRegisterDirector;
|
||
LIST_ENTRY DirServerList;
|
||
KSPIN_LOCK DirServerLock;
|
||
USHORT DirServerPort = NTOHS(1000);
|
||
UCHAR DirServerProtocol = NAT_PROTOCOL_TCP;
|
||
|
||
//
|
||
// FORWARD DECLARATIONS
|
||
//
|
||
|
||
NTSTATUS
|
||
DirCleanup(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
DirClose(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
DirOpen(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
ULONG
|
||
InetAddr(
|
||
PWCHAR String
|
||
);
|
||
|
||
NTSTATUS
|
||
DirRegister(
|
||
VOID
|
||
);
|
||
|
||
VOID
|
||
DirUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
);
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine implements the standard driver-entry for an NT driver.
|
||
It is responsible for reading configuration from the registry,
|
||
and registering our entrypoints with the NAT driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - object to be initialized with NT driver entrypoints
|
||
|
||
RegistryPath - contains path to this driver's registry key
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - indicates success/failure.
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_OBJECT DeviceObject = NULL;
|
||
UNICODE_STRING NtDeviceName;
|
||
OBJECT_ATTRIBUTES ObjectAttributes;
|
||
HANDLE ServiceKey;
|
||
NTSTATUS status;
|
||
UNICODE_STRING Win32DeviceName;
|
||
|
||
KdPrint(("DirDriverEntry\n"));
|
||
|
||
InitializeListHead(&DirServerList);
|
||
KeInitializeSpinLock(&DirServerLock);
|
||
|
||
//
|
||
// Create the device object
|
||
//
|
||
|
||
RtlInitUnicodeString(&NtDeviceName, NT_DEVICE_NAME);
|
||
status =
|
||
IoCreateDevice(
|
||
DriverObject,
|
||
0,
|
||
&NtDeviceName,
|
||
FILE_DEVICE_UNKNOWN,
|
||
0,
|
||
TRUE,
|
||
&DeviceObject
|
||
);
|
||
if (!NT_SUCCESS(status)) {
|
||
KdPrint(("DirDriverEntry: IoCreateDevice=%08x\n", status));
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Create dispatch points for create/open, close, unload.
|
||
//
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_CREATE] = DirOpen;
|
||
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = DirCleanup;
|
||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = DirClose;
|
||
DriverObject->DriverUnload = DirUnload;
|
||
|
||
//
|
||
// Create counted string version of our Win32 device name.
|
||
//
|
||
|
||
RtlInitUnicodeString(&Win32DeviceName, DOS_DEVICE_NAME);
|
||
|
||
//
|
||
// Create a link from our device name to a name in the Win32 namespace.
|
||
//
|
||
|
||
status = IoCreateSymbolicLink(&Win32DeviceName, &NtDeviceName);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
KdPrint(("DirDriverEntry: IoCreateSymbolLink=%08x\n", status));
|
||
IoDeleteDevice(DriverObject->DeviceObject);
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Read registry configuration, if any.
|
||
//
|
||
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
RegistryPath,
|
||
OBJ_CASE_INSENSITIVE,
|
||
NULL,
|
||
NULL
|
||
);
|
||
status = ZwOpenKey(&ServiceKey, KEY_READ, &ObjectAttributes);
|
||
if (NT_SUCCESS(status)) {
|
||
HANDLE Key;
|
||
UNICODE_STRING UnicodeString;
|
||
RtlInitUnicodeString(&UnicodeString, L"Parameters");
|
||
InitializeObjectAttributes(
|
||
&ObjectAttributes,
|
||
&UnicodeString,
|
||
OBJ_CASE_INSENSITIVE,
|
||
ServiceKey,
|
||
NULL
|
||
);
|
||
status = ZwOpenKey(&Key, KEY_READ, &ObjectAttributes);
|
||
ZwClose(ServiceKey);
|
||
if (NT_SUCCESS(status)) {
|
||
UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + 32];
|
||
ULONG Length;
|
||
PKEY_VALUE_PARTIAL_INFORMATION Value =
|
||
(PKEY_VALUE_PARTIAL_INFORMATION)Buffer;
|
||
//
|
||
// Read the protocol name
|
||
//
|
||
RtlInitUnicodeString(&UnicodeString, L"ServerProtocol");
|
||
status =
|
||
ZwQueryValueKey(
|
||
Key,
|
||
&UnicodeString,
|
||
KeyValuePartialInformation,
|
||
(PKEY_VALUE_PARTIAL_INFORMATION)Buffer,
|
||
sizeof(Buffer),
|
||
&Length
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
if (_wcsicmp((PWCHAR)Value->Data, L"UDP") == 0) {
|
||
DirServerPort = NAT_PROTOCOL_UDP;
|
||
KdPrint(("DirDriverEntry: read protocol UDP\n"));
|
||
} else {
|
||
DirServerPort = NAT_PROTOCOL_TCP;
|
||
KdPrint(("DirDriverEntry: read protocol TCP\n"));
|
||
}
|
||
}
|
||
//
|
||
// Read the destination port
|
||
//
|
||
RtlInitUnicodeString(&UnicodeString, L"ServerPort");
|
||
status =
|
||
ZwQueryValueKey(
|
||
Key,
|
||
&UnicodeString,
|
||
KeyValuePartialInformation,
|
||
(PKEY_VALUE_PARTIAL_INFORMATION)Buffer,
|
||
sizeof(Buffer),
|
||
&Length
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
DirServerPort = (USHORT)*(PULONG)Value->Data;
|
||
KdPrint(("DirDriverEntry: read port %d\n", DirServerPort));
|
||
DirServerPort = NTOHS(DirServerPort);
|
||
}
|
||
//
|
||
// Read the list of servers
|
||
//
|
||
RtlInitUnicodeString(&UnicodeString, L"ServerList");
|
||
status =
|
||
ZwQueryValueKey(
|
||
Key,
|
||
&UnicodeString,
|
||
KeyValuePartialInformation,
|
||
(PKEY_VALUE_PARTIAL_INFORMATION)Buffer,
|
||
sizeof(KEY_VALUE_PARTIAL_INFORMATION),
|
||
&Length
|
||
);
|
||
if (status == STATUS_BUFFER_OVERFLOW) {
|
||
Value = ExAllocatePool(PagedPool, Length);
|
||
if (Value) {
|
||
status =
|
||
ZwQueryValueKey(
|
||
Key,
|
||
&UnicodeString,
|
||
KeyValuePartialInformation,
|
||
Value,
|
||
Length,
|
||
&Length
|
||
);
|
||
if (NT_SUCCESS(status)) {
|
||
//
|
||
// Parse the server-list
|
||
//
|
||
PWCHAR String;
|
||
PSERVER_ENTRY Entry;
|
||
for (String = (PWCHAR)Value->Data;
|
||
String[0];
|
||
String += wcslen(String) + 1
|
||
) {
|
||
KdPrint(("DirDriverEntry: read %ls\n", String));
|
||
Entry = ExAllocatePool(PagedPool, sizeof(*Entry));
|
||
if (!Entry) { continue; }
|
||
Entry->Address = InetAddr(String);
|
||
if (!Entry->Address) {
|
||
ExFreePool(Entry);
|
||
} else {
|
||
Entry->SessionCount = 0;
|
||
InsertTailList(&DirServerList, &Entry->Link);
|
||
}
|
||
}
|
||
}
|
||
ExFreePool(Value);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Register with the NAT
|
||
//
|
||
|
||
return DirRegister();
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DirCleanup(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
KdPrint(("DirCleanup\n"));
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = 0;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DirClose(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
KdPrint(("DirClose\n"));
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = 0;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DirOpen(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
KdPrint(("DirOpen\n"));
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
Irp->IoStatus.Information = 0;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DirpCreateHandler(
|
||
IN PVOID SessionHandle,
|
||
IN PVOID DirectorContext,
|
||
IN PVOID DirectorSessionContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked by the NAT when a session-mapping is successfully
|
||
created after a query has been made.
|
||
All we do here is increment the count of active sessions.
|
||
|
||
Arguments:
|
||
|
||
SessionHandle - opaquely identifies the new session
|
||
|
||
DirectorContext - our director-object context
|
||
|
||
DirectorSessionContext - our session-mapping context
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSERVER_ENTRY ServerEntry = (PSERVER_ENTRY)DirectorSessionContext;
|
||
KdPrint((
|
||
"DirCreateHandler: %d.%d.%d.%d [SessionCount=%d]\n",
|
||
ULONG_CHAR_LIST(ServerEntry->Address),
|
||
InterlockedIncrement(&ServerEntry->SessionCount)
|
||
));
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DirpDeleteHandler(
|
||
IN PVOID SessionHandle,
|
||
IN PVOID DirectorContext,
|
||
IN PVOID DirectorSessionContext,
|
||
IN IP_NAT_DELETE_REASON DeleteReason
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked by the NAT in the following cases:
|
||
(a) when a session-mapping is deleted (SessionHandle != NULL)
|
||
(b) when a session-mapping cannot be created using the information
|
||
supplied by previous call to 'QueryHandler' (SessionHandle == NULL).
|
||
|
||
Arguments:
|
||
|
||
SessionHandle - identifies the failed session
|
||
|
||
DirectorContext - our director-object context
|
||
|
||
DirectorSessionContext - our session-mapping context
|
||
|
||
DeleteReason - the cause for session deletion
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS
|
||
|
||
--*/
|
||
|
||
{
|
||
PSERVER_ENTRY ServerEntry = (PSERVER_ENTRY)DirectorSessionContext;
|
||
ULONG SessionCount =
|
||
SessionHandle
|
||
? InterlockedDecrement(&ServerEntry->SessionCount)
|
||
: ServerEntry->SessionCount;
|
||
KdPrint((
|
||
"DirDeleteHandler: %d.%d.%d.%d [SessionCount=%d]\n",
|
||
ULONG_CHAR_LIST(ServerEntry->Address),
|
||
SessionCount
|
||
));
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DirpQueryHandler(
|
||
PIP_NAT_DIRECTOR_QUERY DirectorQuery
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked by the NAT to determine whether a change
|
||
should be made to an incoming packet which matches the protocol
|
||
and port for which we are registered as a director.
|
||
|
||
On input, 'DirectorQuery' contains the packets source and destination
|
||
endpoints as well as flags which provide further information.
|
||
On output, 'DirectorQuery' may be filled with replacement information
|
||
and flags may be modified to control the creation of a mapping
|
||
for the packet's session.
|
||
|
||
Arguments:
|
||
|
||
DirectorQuery - describes the session on input,
|
||
filled with information for a mapping on output.
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS - indicates whether a mapping should be created.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSERVER_ENTRY Entry;
|
||
KdPrint((
|
||
"DirQueryHandler:protocol=%d,%d.%d.%d.%d/%d-%d.%d.%d.%d/%d\n",
|
||
DirectorQuery->Protocol,
|
||
ULONG_CHAR_LIST(DirectorQuery->DestinationAddress),
|
||
NTOHS(DirectorQuery->DestinationPort),
|
||
ULONG_CHAR_LIST(DirectorQuery->SourceAddress),
|
||
NTOHS(DirectorQuery->SourcePort)
|
||
));
|
||
KeAcquireSpinLockAtDpcLevel(&DirServerLock);
|
||
if (IsListEmpty(&DirServerList)) {
|
||
KeReleaseSpinLockFromDpcLevel(&DirServerLock);
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
//
|
||
// Direct the session to the first server-entry on the list
|
||
//
|
||
Entry = CONTAINING_RECORD(DirServerList.Flink, SERVER_ENTRY, Link);
|
||
DirectorQuery->DirectorSessionContext = Entry;
|
||
DirectorQuery->NewDestinationAddress = Entry->Address;
|
||
DirectorQuery->NewDestinationPort = DirectorQuery->DestinationPort;
|
||
DirectorQuery->NewSourceAddress = DirectorQuery->SourceAddress;
|
||
DirectorQuery->NewSourcePort = DirectorQuery->SourcePort;
|
||
//
|
||
// Move the server-entry to the end of the list
|
||
//
|
||
RemoveEntryList(&Entry->Link);
|
||
InsertTailList(&DirServerList, &Entry->Link);
|
||
KeReleaseSpinLockFromDpcLevel(&DirServerLock);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
VOID
|
||
DirpUnloadHandler(
|
||
IN PVOID DirectorContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked by the NAT when the NAT driver is being unloaded.
|
||
|
||
Arguments:
|
||
|
||
DirectorContext - the context for this driver (unused)
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
KdPrint(("DirpUnloadHandler\n"));
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
DirRegister(
|
||
VOID
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked to register this driver with the NAT
|
||
as a director for the configured protocol and port.
|
||
|
||
Arguments:
|
||
|
||
none.
|
||
|
||
Return Status:
|
||
|
||
NTSTATUS - indicates success/failure
|
||
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_OBJECT DeviceObject;
|
||
UNICODE_STRING DeviceString;
|
||
KEVENT Event;
|
||
PFILE_OBJECT FileObject;
|
||
IO_STATUS_BLOCK IoStatus;
|
||
PIRP Irp;
|
||
NTSTATUS status;
|
||
|
||
KdPrint(("DirRegisterDirector\n"));
|
||
|
||
//
|
||
// Initialize the registration information
|
||
//
|
||
|
||
RtlZeroMemory(&DirRegisterDirector, sizeof(DirRegisterDirector));
|
||
DirRegisterDirector.Version = IP_NAT_VERSION;
|
||
DirRegisterDirector.Protocol = NAT_PROTOCOL_TCP;
|
||
DirRegisterDirector.Port = DirServerPort;
|
||
DirRegisterDirector.CreateHandler = DirpCreateHandler;
|
||
DirRegisterDirector.DeleteHandler = DirpDeleteHandler;
|
||
DirRegisterDirector.QueryHandler = DirpQueryHandler;
|
||
|
||
//
|
||
// Retrieve a pointer to the NAT device object
|
||
//
|
||
|
||
RtlInitUnicodeString(&DeviceString, DD_IP_NAT_DEVICE_NAME);
|
||
status =
|
||
IoGetDeviceObjectPointer(
|
||
&DeviceString,
|
||
SYNCHRONIZE|GENERIC_READ|GENERIC_WRITE,
|
||
&FileObject,
|
||
&DeviceObject
|
||
);
|
||
if (!NT_SUCCESS(status)) { return status; }
|
||
|
||
//
|
||
// Create an IRP and use it to register with the NAT
|
||
//
|
||
|
||
ObReferenceObject(DeviceObject);
|
||
KeInitializeEvent(&Event, SynchronizationEvent, FALSE);
|
||
Irp =
|
||
IoBuildDeviceIoControlRequest(
|
||
IOCTL_IP_NAT_REGISTER_DIRECTOR,
|
||
DeviceObject,
|
||
(PVOID)&DirRegisterDirector,
|
||
sizeof(DirRegisterDirector),
|
||
(PVOID)&DirRegisterDirector,
|
||
sizeof(DirRegisterDirector),
|
||
FALSE,
|
||
&Event,
|
||
&IoStatus
|
||
);
|
||
if (!Irp) {
|
||
status = STATUS_UNSUCCESSFUL;
|
||
} else {
|
||
status = IoCallDriver(DeviceObject, Irp);
|
||
if (status == STATUS_PENDING) {
|
||
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
|
||
status = IoStatus.Status;
|
||
}
|
||
}
|
||
|
||
ObDereferenceObject((PVOID)FileObject);
|
||
ObDereferenceObject(DeviceObject);
|
||
return status;
|
||
|
||
} // DirRegisterDirector
|
||
|
||
|
||
VOID
|
||
DirUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is invoked by the I/O manager to unload this driver.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - the object for this driver
|
||
|
||
Return Value:
|
||
|
||
none.
|
||
|
||
--*/
|
||
|
||
{
|
||
PSERVER_ENTRY Entry;
|
||
UNICODE_STRING Win32DeviceName;
|
||
KdPrint(("DirUnload\n"));
|
||
|
||
//
|
||
// Deregister with the NAT, and cleanup our list of servers
|
||
//
|
||
|
||
DirRegisterDirector.Deregister(DirRegisterDirector.DirectorHandle);
|
||
|
||
while (!IsListEmpty(&DirServerList)) {
|
||
Entry = CONTAINING_RECORD(DirServerList.Flink, SERVER_ENTRY, Link);
|
||
RemoveEntryList(&Entry->Link);
|
||
ExFreePool(Entry);
|
||
}
|
||
|
||
//
|
||
// Delete the link from our device name to a name in the Win32 namespace,
|
||
// and delete our device object
|
||
//
|
||
|
||
RtlInitUnicodeString(&Win32DeviceName, DOS_DEVICE_NAME);
|
||
IoDeleteSymbolicLink(&Win32DeviceName);
|
||
IoDeleteDevice( DriverObject->DeviceObject );
|
||
}
|
||
|
||
|
||
ULONG
|
||
InetAddr(
|
||
PWCHAR String
|
||
)
|
||
{
|
||
ULONG Digit;
|
||
ULONG Fields[4] = {0, 0, 0, 0};
|
||
ULONG i = 0;
|
||
for (Digit = (*String - L'0');
|
||
Digit <= 9 && i < 4;
|
||
Digit = (*String - L'0')
|
||
) {
|
||
Fields[i] = Fields[i] * 10 + Digit;
|
||
if (*(++String) == L'.') { ++i; ++String; }
|
||
}
|
||
if (*String != L'\0' ||
|
||
i != 3 ||
|
||
Fields[0] > 255 ||
|
||
Fields[1] > 255 ||
|
||
Fields[2] > 255 ||
|
||
Fields[3] > 255
|
||
) {
|
||
return 0;
|
||
}
|
||
return
|
||
(Fields[0]) |
|
||
(Fields[1] << 8) |
|
||
(Fields[2] << 16) |
|
||
(Fields[3] << 24);
|
||
}
|
||
|