mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1270 lines
34 KiB
1270 lines
34 KiB
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
dispatch.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the code for handling I/O request packets.
|
|
|
|
Author:
|
|
|
|
Abolade Gbadegesin (t-abolag) 11-July-1997
|
|
|
|
Revision History:
|
|
|
|
Abolade Gbadegesin (aboladeg) 19-July-1998
|
|
|
|
Cleaned up fast-path processing, and corrected input/output buffer logic
|
|
while making the mapping-tree global rather than per-interface.
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
|
|
//
|
|
// Fast-io-dispatch structure; we only support fast-IO for IOCTLs
|
|
//
|
|
|
|
FAST_IO_DISPATCH NatFastIoDispatch =
|
|
{
|
|
FIELD_OFFSET(FAST_IO_DISPATCH, FastIoDeviceControl),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NatFastIoDeviceControl
|
|
};
|
|
|
|
//
|
|
// Spinlock to guard file object create / close
|
|
//
|
|
|
|
KSPIN_LOCK NatFileObjectLock;
|
|
|
|
//
|
|
// The process that owns the outstanding file objects
|
|
//
|
|
|
|
HANDLE NatOwnerProcessId;
|
|
|
|
//
|
|
// The count of outstanding user-mode file objects.
|
|
//
|
|
|
|
ULONG NatFileObjectCount;
|
|
|
|
//
|
|
// FORWARD DECLARATIONS
|
|
//
|
|
|
|
NTSTATUS
|
|
NatpExecuteIoDeviceControl(
|
|
PIRP Irp,
|
|
PFILE_OBJECT FileObject,
|
|
MODE RequestorMode,
|
|
PVOID InputBuffer,
|
|
ULONG InputBufferLength,
|
|
PVOID OutputBuffer,
|
|
ULONG OutputBufferLength,
|
|
ULONG IoControlCode,
|
|
PULONG Size
|
|
);
|
|
|
|
NTSTATUS
|
|
NatpSetGlobalInfo(
|
|
PVOID InputBuffer,
|
|
ULONG InputBufferLength,
|
|
PVOID OutputBuffer,
|
|
ULONG OutputBufferLength,
|
|
PULONG Size
|
|
);
|
|
|
|
BOOLEAN FASTCALL
|
|
NatpValidateHeader(
|
|
PRTR_INFO_BLOCK_HEADER Header,
|
|
ULONG Size
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
NatDispatch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to handle interrupt-request packets
|
|
queued to the NAT's device object. A single routine serves
|
|
to handle all the varios requests in which we are interested.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - the NAT's device-object
|
|
|
|
Irp - the interrupt request packet
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PVOID Buffer;
|
|
PRTR_TOC_ENTRY Entry;
|
|
PRTR_INFO_BLOCK_HEADER Header;
|
|
ULONG i;
|
|
PIO_STACK_LOCATION IrpSp;
|
|
KIRQL Irql;
|
|
HANDLE ProcessId;
|
|
ULONG Size = 0;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
BOOLEAN ShouldComplete = TRUE;
|
|
CALLTRACE(("NatDispatch\n"));
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
Buffer = Irp->AssociatedIrp.SystemBuffer;
|
|
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
switch (IrpSp->MajorFunction) {
|
|
|
|
case IRP_MJ_CREATE: {
|
|
|
|
//
|
|
// If this is a user-mode request check process
|
|
// ownership.
|
|
//
|
|
|
|
if (UserMode == Irp->RequestorMode) {
|
|
|
|
ProcessId = PsGetCurrentProcessId();
|
|
KeAcquireSpinLock(&NatFileObjectLock, &Irql);
|
|
|
|
if (0 == NatFileObjectCount) {
|
|
|
|
//
|
|
// No process currently owns the NAT -- record
|
|
// the new owning process id, and update the
|
|
// outstanding file object count.
|
|
//
|
|
|
|
ASSERT(NULL == NatOwnerProcessId);
|
|
|
|
NatOwnerProcessId = ProcessId;
|
|
NatFileObjectCount = 1;
|
|
|
|
} else if (ProcessId == NatOwnerProcessId) {
|
|
|
|
//
|
|
// The owning process is creating another
|
|
// file object.
|
|
//
|
|
|
|
NatFileObjectCount += 1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// A process that is not our owner is trying
|
|
// to create a file object -- fail the request.
|
|
//
|
|
|
|
status = STATUS_ACCESS_DENIED;
|
|
}
|
|
|
|
KeReleaseSpinLock(&NatFileObjectLock, Irql);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case IRP_MJ_CLEANUP: {
|
|
NatDeleteAnyAssociatedInterface(IrpSp->FileObject);
|
|
NatCleanupAnyAssociatedRedirect(IrpSp->FileObject);
|
|
NatCleanupAnyAssociatedNotification(IrpSp->FileObject);
|
|
NatDeleteAnyAssociatedDynamicTicket(IrpSp->FileObject);
|
|
break;
|
|
}
|
|
|
|
case IRP_MJ_CLOSE: {
|
|
|
|
//
|
|
// If this is a user-mode request update the outstanding
|
|
// file object count and process ownership.
|
|
//
|
|
|
|
if (UserMode == Irp->RequestorMode) {
|
|
|
|
KeAcquireSpinLock(&NatFileObjectLock, &Irql);
|
|
|
|
ASSERT(NatFileObjectCount > 0);
|
|
ASSERT(PsGetCurrentProcessId() == NatOwnerProcessId);
|
|
|
|
NatFileObjectCount -= 1;
|
|
|
|
if (0 == NatFileObjectCount) {
|
|
|
|
//
|
|
// The process has closed its last outstanding
|
|
// file object, and thus is no longer our
|
|
// owner.
|
|
//
|
|
|
|
NatOwnerProcessId = NULL;
|
|
}
|
|
|
|
KeReleaseSpinLock(&NatFileObjectLock, Irql);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IRP_MJ_DEVICE_CONTROL: {
|
|
|
|
status =
|
|
NatpExecuteIoDeviceControl(
|
|
Irp,
|
|
IrpSp->FileObject,
|
|
Irp->RequestorMode,
|
|
Buffer,
|
|
IrpSp->Parameters.DeviceIoControl.InputBufferLength,
|
|
Buffer,
|
|
IrpSp->Parameters.DeviceIoControl.OutputBufferLength,
|
|
IrpSp->Parameters.DeviceIoControl.IoControlCode,
|
|
&Size
|
|
);
|
|
|
|
break;
|
|
}
|
|
|
|
#if NAT_WMI
|
|
case IRP_MJ_SYSTEM_CONTROL: {
|
|
|
|
status =
|
|
NatExecuteSystemControl(
|
|
DeviceObject,
|
|
Irp,
|
|
&ShouldComplete
|
|
);
|
|
|
|
if (ShouldComplete) {
|
|
ShouldComplete = FALSE;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
if (status != STATUS_PENDING && ShouldComplete) {
|
|
Irp->IoStatus.Status = status;
|
|
Irp->IoStatus.Information = Size;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return status;
|
|
|
|
} // NatDispatch
|
|
|
|
|
|
BOOLEAN
|
|
NatFastIoDeviceControl(
|
|
PFILE_OBJECT FileObject,
|
|
BOOLEAN Wait,
|
|
PVOID InputBuffer,
|
|
ULONG InputBufferLength,
|
|
PVOID OutputBuffer,
|
|
ULONG OutputBufferLength,
|
|
ULONG IoControlCode,
|
|
PIO_STATUS_BLOCK IoStatus,
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked by the I/O system in an attempt to complete
|
|
an I/O control request without constructing an IRP.
|
|
|
|
Arguments:
|
|
|
|
FileObject - the file associated with the I/O request
|
|
|
|
Wait - indicates whether a wait is allowed in this context
|
|
|
|
InputBuffer - input information for the I/O request
|
|
|
|
InputBufferLength - length of 'InputBuffer'
|
|
|
|
OutputBuffer - output information for the I/O request
|
|
|
|
OutputBufferLength - length of 'OutputBuffer'
|
|
|
|
IoControlCode - I/O request code
|
|
|
|
IoStatus - receives the status of the I/O request
|
|
|
|
DeviceObject - device object of the NAT
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if completed synchronously, FALSE otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Size = 0;
|
|
NTSTATUS Status;
|
|
PVOID LocalInputBuffer;
|
|
MODE PreviousMode;
|
|
//
|
|
// We are in the context of the requesting thread,
|
|
// so exceptions may occur, and must be handled.
|
|
// To deal with modifications to the user-provided information
|
|
// capture the contents of the input buffer in non-paged pool.
|
|
//
|
|
if (!InputBufferLength) {
|
|
LocalInputBuffer = NULL;
|
|
} else {
|
|
LocalInputBuffer =
|
|
ExAllocatePoolWithTag(
|
|
NonPagedPool,
|
|
InputBufferLength,
|
|
NAT_TAG_IOCTL
|
|
);
|
|
if (!LocalInputBuffer) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
PreviousMode = ExGetPreviousMode();
|
|
__try {
|
|
if (InputBufferLength) {
|
|
if (PreviousMode != KernelMode) {
|
|
ProbeForRead(InputBuffer, InputBufferLength, sizeof(UCHAR));
|
|
}
|
|
RtlCopyMemory(LocalInputBuffer, InputBuffer, InputBufferLength);
|
|
}
|
|
Status =
|
|
NatpExecuteIoDeviceControl(
|
|
NULL,
|
|
FileObject,
|
|
PreviousMode,
|
|
LocalInputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
IoControlCode,
|
|
&Size
|
|
);
|
|
if (Status != STATUS_PENDING && NT_SUCCESS(Status)) {
|
|
IoStatus->Information = Size;
|
|
IoStatus->Status = Status;
|
|
} else {
|
|
Status = STATUS_PENDING;
|
|
}
|
|
} __except(EXCEPTION_EXECUTE_HANDLER) {
|
|
if (LocalInputBuffer) { ExFreePool(LocalInputBuffer); }
|
|
return FALSE;
|
|
}
|
|
if (LocalInputBuffer) { ExFreePool(LocalInputBuffer); }
|
|
return ((Status == STATUS_PENDING) ? FALSE : TRUE);
|
|
} // NatFastIoDeviceControl
|
|
|
|
|
|
NTSTATUS
|
|
NatpExecuteIoDeviceControl(
|
|
PIRP Irp,
|
|
PFILE_OBJECT FileObject,
|
|
MODE RequestorMode,
|
|
PVOID InputBuffer,
|
|
ULONG InputBufferLength,
|
|
PVOID OutputBuffer,
|
|
ULONG OutputBufferLength,
|
|
ULONG IoControlCode,
|
|
PULONG Size
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to handle I/O controls, either in the context
|
|
of the requesting thread (via FastIoDispatch) or in the context of a
|
|
system thread (with a corresponding IRP).
|
|
|
|
For certain requests, particularly those requiring output information,
|
|
we return 'STATUS_PENDING' when invoked in the fast path since we cannot
|
|
write into the output buffer at raised IRQL. Instead, we wait to be
|
|
reinvoked via the slow path with a non-paged system buffer.
|
|
|
|
Arguments:
|
|
|
|
Irp - in the slow-path, the IRP associated with the control;
|
|
in the fast-path, NULL
|
|
|
|
FileObject - the file-object associated with the control
|
|
|
|
RequestorMode - indicates whether the requestor is in kernel-mode
|
|
or user-mode
|
|
|
|
InputBuffer/InputBufferLength - describe data passed in with the control;
|
|
may be user-mode or kernel-mode buffer
|
|
|
|
OutputBuffer/OutputBufferLength - describe space in which to return
|
|
information; may be user-mode or kernel-mode buffer
|
|
|
|
IoControlCode - indicates control requested
|
|
|
|
Size - on output, number of bytes stored in 'OutputBuffer'.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIP_ADAPTER_BINDING_INFO BindingInfo;
|
|
PRTR_TOC_ENTRY Entry;
|
|
PRTR_INFO_BLOCK_HEADER Header;
|
|
ULONG i;
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
*Size = 0;
|
|
|
|
switch (IoControlCode) {
|
|
|
|
case IOCTL_IP_NAT_REQUEST_NOTIFICATION: {
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
if (InputBufferLength < sizeof(IP_NAT_REQUEST_NOTIFICATION)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatRequestNotification(
|
|
(PIP_NAT_REQUEST_NOTIFICATION)InputBuffer,
|
|
Irp,
|
|
FileObject
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_SET_GLOBAL_INFO: {
|
|
status =
|
|
NatpSetGlobalInfo(
|
|
InputBuffer,
|
|
InputBufferLength,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
Size
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_CREATE_INTERFACE: {
|
|
|
|
if (InputBufferLength <
|
|
sizeof(IP_NAT_CREATE_INTERFACE) +
|
|
sizeof(IP_ADAPTER_BINDING_INFO)
|
|
) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
BindingInfo =
|
|
(PIP_ADAPTER_BINDING_INFO)
|
|
((PIP_NAT_CREATE_INTERFACE)InputBuffer)->BindingInfo;
|
|
if (BindingInfo->AddressCount >= MAXLONG / sizeof(NAT_ADDRESS) ||
|
|
SIZEOF_IP_BINDING(BindingInfo->AddressCount) +
|
|
sizeof(IP_NAT_CREATE_INTERFACE) > InputBufferLength) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatCreateInterface(
|
|
(PIP_NAT_CREATE_INTERFACE)InputBuffer,
|
|
FileObject
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_DELETE_INTERFACE: {
|
|
|
|
if (InputBufferLength != sizeof(ULONG)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatDeleteInterface(
|
|
*(PULONG)InputBuffer,
|
|
FileObject
|
|
);
|
|
if (status == STATUS_PENDING) {
|
|
//
|
|
// A return of STATUS_PENDING indicates that the interface
|
|
// is now marked for deletion but an active thread holds
|
|
// a reference to it; convert this to a STATUS_SUCCESS code
|
|
// to avoid bypassing our IRP-completion code in 'NatDispatch'.
|
|
//
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_SET_INTERFACE_INFO: {
|
|
|
|
if (InputBufferLength <
|
|
FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) +
|
|
FIELD_OFFSET(RTR_INFO_BLOCK_HEADER, TocEntry)
|
|
) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
Header = &((PIP_NAT_INTERFACE_INFO)InputBuffer)->Header;
|
|
|
|
if (!NatpValidateHeader(
|
|
Header,
|
|
InputBufferLength -
|
|
FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header)
|
|
)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatConfigureInterface(
|
|
(PIP_NAT_INTERFACE_INFO)InputBuffer,
|
|
FileObject
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_GET_INTERFACE_INFO: {
|
|
|
|
*Size = OutputBufferLength;
|
|
|
|
if (InputBufferLength != sizeof(ULONG)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatQueryInformationInterface(
|
|
*(PULONG)InputBuffer,
|
|
(PIP_NAT_INTERFACE_INFO)OutputBuffer,
|
|
Size
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_GET_INTERFACE_STATISTICS: {
|
|
|
|
if (InputBufferLength != sizeof(ULONG)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
if (OutputBufferLength < sizeof(IP_NAT_INTERFACE_STATISTICS)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
*Size = sizeof(IP_NAT_INTERFACE_STATISTICS);
|
|
|
|
status =
|
|
NatQueryStatisticsInterface(
|
|
*(PULONG)InputBuffer,
|
|
(PIP_NAT_INTERFACE_STATISTICS)OutputBuffer
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_GET_INTERFACE_MAPPING_TABLE: {
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
|
|
*Size = OutputBufferLength;
|
|
|
|
if (InputBufferLength <
|
|
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS,
|
|
EnumerateTable)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
if (OutputBufferLength <
|
|
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS,
|
|
EnumerateTable)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatQueryInterfaceMappingTable(
|
|
(PIP_NAT_ENUMERATE_SESSION_MAPPINGS)InputBuffer,
|
|
(PIP_NAT_ENUMERATE_SESSION_MAPPINGS)OutputBuffer,
|
|
Size
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_GET_MAPPING_TABLE: {
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
|
|
*Size = OutputBufferLength;
|
|
|
|
if (InputBufferLength <
|
|
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS,
|
|
EnumerateTable)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
if (OutputBufferLength <
|
|
FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS,
|
|
EnumerateTable)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatQueryMappingTable(
|
|
(PIP_NAT_ENUMERATE_SESSION_MAPPINGS)InputBuffer,
|
|
(PIP_NAT_ENUMERATE_SESSION_MAPPINGS)OutputBuffer,
|
|
Size
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_REGISTER_DIRECTOR: {
|
|
|
|
*Size = sizeof(IP_NAT_REGISTER_DIRECTOR);
|
|
|
|
//
|
|
// Only kernel-mode drivers can register as directors
|
|
//
|
|
|
|
if (RequestorMode != KernelMode ||
|
|
SharedUserData->NtProductType == NtProductWinNt) {
|
|
status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_REGISTER_DIRECTOR)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
if (OutputBufferLength < sizeof(IP_NAT_REGISTER_DIRECTOR)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Perform the director-registration
|
|
//
|
|
|
|
status =
|
|
NatCreateDirector(
|
|
(PIP_NAT_REGISTER_DIRECTOR)InputBuffer
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_GET_DIRECTOR_TABLE: {
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
|
|
*Size = OutputBufferLength;
|
|
|
|
if (InputBufferLength <
|
|
FIELD_OFFSET(IP_NAT_ENUMERATE_DIRECTORS, EnumerateTable)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
if (OutputBufferLength <
|
|
FIELD_OFFSET(IP_NAT_ENUMERATE_DIRECTORS, EnumerateTable)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatQueryDirectorTable(
|
|
(PIP_NAT_ENUMERATE_DIRECTORS)InputBuffer,
|
|
(PIP_NAT_ENUMERATE_DIRECTORS)OutputBuffer,
|
|
Size
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_REGISTER_EDITOR: {
|
|
|
|
*Size = sizeof(IP_NAT_REGISTER_EDITOR);
|
|
|
|
//
|
|
// Only kernel-mode drivers can register as editors
|
|
//
|
|
|
|
if (RequestorMode != KernelMode) {
|
|
status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_REGISTER_EDITOR)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
if (OutputBufferLength < sizeof(IP_NAT_REGISTER_EDITOR)) {
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Perform the editor-registration
|
|
//
|
|
|
|
status = NatCreateEditor((PIP_NAT_REGISTER_EDITOR)InputBuffer);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_GET_EDITOR_TABLE: {
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
|
|
*Size = OutputBufferLength;
|
|
|
|
if (InputBufferLength <
|
|
FIELD_OFFSET(IP_NAT_ENUMERATE_EDITORS, EnumerateTable)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
if (OutputBufferLength <
|
|
FIELD_OFFSET(IP_NAT_ENUMERATE_EDITORS, EnumerateTable)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatQueryEditorTable(
|
|
(PIP_NAT_ENUMERATE_EDITORS)InputBuffer,
|
|
(PIP_NAT_ENUMERATE_EDITORS)OutputBuffer,
|
|
Size
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_CREATE_REDIRECT: {
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
#if 0
|
|
if (SharedUserData->NtProductType == NtProductWinNt) {
|
|
status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_CREATE_REDIRECT) ||
|
|
OutputBufferLength != sizeof(IP_NAT_REDIRECT_STATISTICS)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatCreateRedirect(
|
|
(PIP_NAT_CREATE_REDIRECT)InputBuffer,
|
|
Irp,
|
|
FileObject
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_CREATE_REDIRECT_EX: {
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
#if 0
|
|
if (SharedUserData->NtProductType == NtProductWinNt) {
|
|
status = STATUS_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_CREATE_REDIRECT_EX) ||
|
|
OutputBufferLength != sizeof(IP_NAT_REDIRECT_STATISTICS)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatCreateRedirectEx(
|
|
(PIP_NAT_CREATE_REDIRECT_EX)InputBuffer,
|
|
Irp,
|
|
FileObject
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_CANCEL_REDIRECT: {
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_REDIRECT)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatCancelRedirect(
|
|
(PIP_NAT_LOOKUP_REDIRECT)InputBuffer,
|
|
FileObject
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_GET_REDIRECT_STATISTICS: {
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_REDIRECT) ||
|
|
OutputBufferLength != sizeof(IP_NAT_REDIRECT_STATISTICS)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatQueryInformationRedirect(
|
|
(PIP_NAT_LOOKUP_REDIRECT)InputBuffer,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
NatStatisticsRedirectInformation
|
|
);
|
|
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_GET_REDIRECT_DESTINATION_MAPPING: {
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_REDIRECT) ||
|
|
OutputBufferLength !=
|
|
sizeof(IP_NAT_REDIRECT_DESTINATION_MAPPING)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatQueryInformationRedirect(
|
|
(PIP_NAT_LOOKUP_REDIRECT)InputBuffer,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
NatDestinationMappingRedirectInformation
|
|
);
|
|
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_GET_REDIRECT_SOURCE_MAPPING: {
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_REDIRECT) ||
|
|
OutputBufferLength != sizeof(IP_NAT_REDIRECT_SOURCE_MAPPING)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatQueryInformationRedirect(
|
|
(PIP_NAT_LOOKUP_REDIRECT)InputBuffer,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
NatSourceMappingRedirectInformation
|
|
);
|
|
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_LOOKUP_SESSION_MAPPING_KEY: {
|
|
PIP_NAT_LOOKUP_SESSION_MAPPING LookupMapping;
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_SESSION_MAPPING) ||
|
|
OutputBufferLength != sizeof(IP_NAT_SESSION_MAPPING_KEY)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
LookupMapping = (PIP_NAT_LOOKUP_SESSION_MAPPING)InputBuffer;
|
|
status =
|
|
NatLookupAndQueryInformationMapping(
|
|
LookupMapping->Protocol,
|
|
LookupMapping->DestinationAddress,
|
|
LookupMapping->DestinationPort,
|
|
LookupMapping->SourceAddress,
|
|
LookupMapping->SourcePort,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
NatKeySessionMappingInformation
|
|
);
|
|
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_LOOKUP_SESSION_MAPPING_KEY_EX: {
|
|
PIP_NAT_LOOKUP_SESSION_MAPPING LookupMapping;
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_SESSION_MAPPING) ||
|
|
OutputBufferLength != sizeof(IP_NAT_SESSION_MAPPING_KEY_EX)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
LookupMapping = (PIP_NAT_LOOKUP_SESSION_MAPPING)InputBuffer;
|
|
status =
|
|
NatLookupAndQueryInformationMapping(
|
|
LookupMapping->Protocol,
|
|
LookupMapping->DestinationAddress,
|
|
LookupMapping->DestinationPort,
|
|
LookupMapping->SourceAddress,
|
|
LookupMapping->SourcePort,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
NatKeySessionMappingExInformation
|
|
);
|
|
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_LOOKUP_SESSION_MAPPING_STATISTICS: {
|
|
PIP_NAT_LOOKUP_SESSION_MAPPING LookupMapping;
|
|
|
|
if (!Irp) { return STATUS_PENDING; }
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_LOOKUP_SESSION_MAPPING) ||
|
|
OutputBufferLength != sizeof(IP_NAT_SESSION_MAPPING_STATISTICS)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
LookupMapping = (PIP_NAT_LOOKUP_SESSION_MAPPING)InputBuffer;
|
|
status =
|
|
NatLookupAndQueryInformationMapping(
|
|
LookupMapping->Protocol,
|
|
LookupMapping->DestinationAddress,
|
|
LookupMapping->DestinationPort,
|
|
LookupMapping->SourceAddress,
|
|
LookupMapping->SourcePort,
|
|
OutputBuffer,
|
|
OutputBufferLength,
|
|
NatStatisticsSessionMappingInformation
|
|
);
|
|
if (NT_SUCCESS(status)) { *Size = OutputBufferLength; }
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_CREATE_DYNAMIC_TICKET: {
|
|
|
|
if (InputBufferLength < sizeof(IP_NAT_CREATE_DYNAMIC_TICKET)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatCreateDynamicTicket(
|
|
(PIP_NAT_CREATE_DYNAMIC_TICKET)InputBuffer,
|
|
InputBufferLength,
|
|
FileObject
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_DELETE_DYNAMIC_TICKET: {
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_DELETE_DYNAMIC_TICKET)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatDeleteDynamicTicket(
|
|
(PIP_NAT_DELETE_DYNAMIC_TICKET)InputBuffer,
|
|
FileObject
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_CREATE_TICKET: {
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_CREATE_TICKET)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatProcessCreateTicket(
|
|
(PIP_NAT_CREATE_TICKET)InputBuffer,
|
|
FileObject
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_DELETE_TICKET: {
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_CREATE_TICKET)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatProcessDeleteTicket(
|
|
(PIP_NAT_CREATE_TICKET)InputBuffer,
|
|
FileObject
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IOCTL_IP_NAT_LOOKUP_TICKET: {
|
|
|
|
if (InputBufferLength != sizeof(IP_NAT_CREATE_TICKET)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
if (OutputBufferLength != sizeof(IP_NAT_PORT_MAPPING)) {
|
|
status = STATUS_INVALID_BUFFER_SIZE;
|
|
break;
|
|
}
|
|
|
|
status =
|
|
NatProcessLookupTicket(
|
|
(PIP_NAT_CREATE_TICKET)InputBuffer,
|
|
(PIP_NAT_PORT_MAPPING)OutputBuffer,
|
|
FileObject
|
|
);
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
|
|
} // NatpExecuteIoDeviceControl
|
|
|
|
|
|
NTSTATUS
|
|
NatpSetGlobalInfo(
|
|
PVOID InputBuffer,
|
|
ULONG InputBufferLength,
|
|
PVOID OutputBuffer,
|
|
ULONG OutputBufferLength,
|
|
PULONG Size
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked upon receipt of the NAT's configuration.
|
|
|
|
Arguments:
|
|
|
|
InputBuffer/InputBufferLength - describe configuration information
|
|
|
|
OutputBuffer/OutputBufferLength - unused.
|
|
|
|
Size - unused
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
PRTR_TOC_ENTRY Entry;
|
|
PRTR_INFO_BLOCK_HEADER Header;
|
|
ULONG i;
|
|
ULONG Protocol;
|
|
|
|
if (InputBufferLength <
|
|
FIELD_OFFSET(IP_NAT_GLOBAL_INFO, Header) +
|
|
FIELD_OFFSET(RTR_INFO_BLOCK_HEADER, TocEntry)
|
|
) {
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
|
|
Header = &((PIP_NAT_GLOBAL_INFO)InputBuffer)->Header;
|
|
|
|
if (!NatpValidateHeader(
|
|
Header,
|
|
InputBufferLength - FIELD_OFFSET(IP_NAT_GLOBAL_INFO, Header)
|
|
)) {
|
|
return STATUS_INVALID_BUFFER_SIZE;
|
|
}
|
|
|
|
for (i = 0; i < Header->TocEntriesCount; i++) {
|
|
|
|
Entry = &Header->TocEntry[i];
|
|
switch (Entry->InfoType) {
|
|
|
|
case IP_NAT_TIMEOUT_TYPE: {
|
|
PIP_NAT_TIMEOUT Timeout = GetInfoFromTocEntry(Header,Entry);
|
|
InterlockedExchange(
|
|
&TcpTimeoutSeconds,
|
|
Timeout->TCPTimeoutSeconds
|
|
);
|
|
InterlockedExchange(
|
|
&UdpTimeoutSeconds,
|
|
Timeout->UDPTimeoutSeconds
|
|
);
|
|
break;
|
|
}
|
|
|
|
case IP_NAT_PROTOCOLS_ALLOWED_TYPE: {
|
|
PIP_NAT_PROTOCOLS_ALLOWED ProtocolsAllowed =
|
|
GetInfoFromTocEntry(Header,Entry);
|
|
//
|
|
// The protocols allowed are specified using a 256-bit bitmap;
|
|
// an allowed protocol has the bit for its protocol number set.
|
|
// For each protocol enabled in the bitmap, we now install the
|
|
// default IP-header translation routine, with the exception
|
|
// of protocols which are always enabled.
|
|
//
|
|
#define IS_BIT_SET(b,i) ((b)[(i) / 32] & (1 << ((i) & 31)))
|
|
for (Protocol = 0; Protocol < 256; Protocol++) {
|
|
if (Protocol == NAT_PROTOCOL_ICMP ||
|
|
Protocol == NAT_PROTOCOL_PPTP ||
|
|
Protocol == NAT_PROTOCOL_TCP ||
|
|
Protocol == NAT_PROTOCOL_UDP
|
|
) {
|
|
continue;
|
|
}
|
|
if (IS_BIT_SET(ProtocolsAllowed->Bitmap, Protocol)) {
|
|
InterlockedExchangePointer(
|
|
(PVOID)TranslateRoutineTable[Protocol],
|
|
(PVOID)NatTranslateIp
|
|
);
|
|
}
|
|
else {
|
|
InterlockedExchangePointer(
|
|
(PVOID)TranslateRoutineTable[Protocol],
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOLEAN FASTCALL
|
|
NatpValidateHeader(
|
|
PRTR_INFO_BLOCK_HEADER Header,
|
|
ULONG Size
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is invoked to ensure that the given header is consistent.
|
|
This is the case if
|
|
* the header's size is less than or equal to 'Size'
|
|
* each entry in the header is contained in 'Header->Size'.
|
|
* the data for each entry is contained in 'Header->Size'.
|
|
|
|
Arguments:
|
|
|
|
Header - the header to be validated
|
|
|
|
Size - the size of the buffer in which 'Header' appears
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if valid, FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
ULONG64 Length;
|
|
|
|
//
|
|
// Check that the base structure is present
|
|
//
|
|
|
|
if (Size < FIELD_OFFSET(RTR_INFO_BLOCK_HEADER, TocEntry) ||
|
|
Size < Header->Size) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check that the table of contents is present
|
|
//
|
|
|
|
Length = (ULONG64)Header->TocEntriesCount * sizeof(RTR_TOC_ENTRY);
|
|
if (Length > MAXLONG) {
|
|
return FALSE;
|
|
}
|
|
|
|
Length += FIELD_OFFSET(RTR_INFO_BLOCK_HEADER, TocEntry);
|
|
if (Length > Header->Size) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Check that all the data is present
|
|
//
|
|
|
|
for (i = 0; i < Header->TocEntriesCount; i++) {
|
|
Length =
|
|
(ULONG64)Header->TocEntry[i].Count * Header->TocEntry[i].InfoSize;
|
|
if (Length > MAXLONG) {
|
|
return FALSE;
|
|
}
|
|
if ((Length + Header->TocEntry[i].Offset) > Header->Size) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
} // NatpValidateHeader
|
|
|