windows-nt/Source/XPSP1/NT/net/rras/ip/nathlp/natapi/natapi.c

2318 lines
60 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1998, Microsoft Corporation
Module Name:
natapi.c
Abstract:
This module contains code for API routines which provide translation
functionality to user-mode clients of the NAT. This functionality
differs from the 'normal' mode, in which a boundary-interface is designated
and packets are transparently modified as they cross the boundary.
This module instead allows an application to stipulate that certain
modifications be made to a packet on any interface it is received.
Author:
Abolade Gbadegesin (aboladeg) 08-May-1998
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include <ipnatapi.h>
C_ASSERT(NAT_INVALID_IF_INDEX == INVALID_IF_INDEX);
//
// PRIVATE STRUCTURE DECLARATIONS
//
//
// Structure: NAT_REDIRECT
//
// Encapsulates information about an outstanding redirect-instance.
// For a normal redirect, the structure holds the caller-specified
// completion-parameters and output statistics.
// For a dynamic redirect instance, the structure links this instance
// into the dynamic redirect's instance-list, and contains the notification
// event for the instance.
//
typedef struct _NAT_REDIRECT {
union {
struct _NAT_REDIRECT_TAIL {
IO_STATUS_BLOCK IoStatus;
PNAT_COMPLETION_ROUTINE CompletionRoutine;
PVOID CompletionContext;
IP_NAT_REDIRECT_STATISTICS Statistics;
};
struct _NAT_DYNAMIC_REDIRECT_TAIL {
LIST_ENTRY Link;
ULONG InstanceId;
HANDLE Event;
HANDLE WaitHandle;
struct _NAT_DYNAMIC_REDIRECT_CONTEXT* Context;
};
};
} NAT_REDIRECT, *PNAT_REDIRECT;
//
// Structure: NAT_DYNAMIC_REDIRECT
//
// Encapsulates information about an outstanding dynamic redirect.
// A dynamic redirect is automatically reissued using the caller's original
// parameters whenever the number of instances drops below a given minimum
// specified by the creator. We maintain a list of all instances of a dynamic
// redirect, and we replenish the list whenever an instance is activated
// or terminated without being activated.
//
// For each dynamic redirect, we maintain a reference-count which is used
// to control its lifetime. We make references to the dynamic redirect when
// * the redirect is initially created, on behalf of its existence,
// * an additional instance is issued, on behalf of the notification routine
// for the instance.
//
// The usual rules for synchronization apply, to wit, to access any fields
// a reference must be held, and to add a reference the lock must be held,
// except at creation-time when the initial reference is made.
//
typedef struct _NAT_DYNAMIC_REDIRECT {
CRITICAL_SECTION Lock;
ULONG ReferenceCount;
ULONG Flags;
HANDLE TranslatorHandle;
ULONG MinimumBacklog;
LIST_ENTRY InstanceList;
IP_NAT_CREATE_REDIRECT_EX CreateRedirect;
} NAT_DYNAMIC_REDIRECT, *PNAT_DYNAMIC_REDIRECT;
#define NAT_DYNAMIC_REDIRECT_FLAG_DELETED 0x80000000
#define NAT_DYNAMIC_REDIRECT_DELETED(d) \
((d)->Flags & NAT_DYNAMIC_REDIRECT_FLAG_DELETED)
#define NAT_REFERENCE_DYNAMIC_REDIRECT(d) \
REFERENCE_OBJECT(d, NAT_DYNAMIC_REDIRECT_DELETED)
#define NAT_DEREFERENCE_DYNAMIC_REDIRECT(d) \
DEREFERENCE_OBJECT(d, NatpCleanupDynamicRedirect)
#define DEFAULT_DYNAMIC_REDIRECT_BACKLOG 5
//
// Structure: NAT_DYNAMIC_REDIRECT_CONTEXT
//
// Used as the context-parameter for the notification and completion routines
// of each instance of a dynamic redirect.
//
typedef struct _NAT_DYNAMIC_REDIRECT_CONTEXT {
PNAT_DYNAMIC_REDIRECT DynamicRedirectp;
ULONG InstanceId;
} NAT_DYNAMIC_REDIRECT_CONTEXT, *PNAT_DYNAMIC_REDIRECT_CONTEXT;
//
// GLOBAL DATA DEFINITIONS
//
const WCHAR NatpServicePath[] =
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\IPNAT";
ULONG NextRedirectInstanceId = 0;
IO_STATUS_BLOCK UnusedIoStatus;
IP_NAT_REDIRECT_STATISTICS UnusedStatistics;
//
// FORWARD DECLARATIONS
//
VOID
NatCloseDriver(
HANDLE FileHandle
);
ULONG
NatLoadDriver(
OUT PHANDLE FileHandle,
PIP_NAT_GLOBAL_INFO GlobalInfo
);
ULONG
NatOpenDriver(
OUT PHANDLE FileHandle
);
VOID
NatpCleanupDynamicRedirect(
PNAT_DYNAMIC_REDIRECT DynamicRedirectp
);
VOID
NatpDisableLoadDriverPrivilege(
PBOOLEAN WasEnabled
);
VOID NTAPI
NatpDynamicRedirectNotificationRoutine(
PVOID Context,
BOOLEAN WaitCompleted
);
BOOLEAN
NatpEnableLoadDriverPrivilege(
PBOOLEAN WasEnabled
);
VOID NTAPI
NatpRedirectCompletionRoutine(
PVOID Context,
PIO_STATUS_BLOCK IoStatus,
ULONG Reserved
);
VOID
NatpCreateDynamicRedirectInstance(
PNAT_DYNAMIC_REDIRECT DynamicRedirectp
);
VOID
NatpDeleteDynamicRedirectInstance(
PNAT_DYNAMIC_REDIRECT DynamicRedirectp,
PNAT_REDIRECT Redirectp
);
BOOLEAN
NatpValidateRedirectParameters(
ULONG Flags,
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG SourceAddress,
USHORT SourcePort,
ULONG NewDestinationAddress,
USHORT NewDestinationPort,
ULONG NewSourceAddress,
USHORT NewSourcePort,
ULONG RestrictAdapterIndex OPTIONAL
);
ULONG
NatUnloadDriver(
HANDLE FileHandle
);
ULONG
NatCancelDynamicRedirect(
HANDLE DynamicRedirectHandle
)
/*++
Routine Description:
This routine is called to cancel the given dynamic redirect.
It cancels all instances of the dynamic redirect and releases the initial
reference to the dynamic redirect, thus causing cleanup to occur as soon
as all active references are released.
Arguments:
DynamicRedirectHandle - the handle to the dynamic redirect to be cancelled
Return Value:
ULONG - Win32 status code.
--*/
{
PNAT_DYNAMIC_REDIRECT DynamicRedirectp =
(PNAT_DYNAMIC_REDIRECT)DynamicRedirectHandle;
//
// Lock the dynamic redirect, mark it 'deleted' to ensure that
// no more instances are created by our notification routines,
// and delete all outstanding instances.
//
EnterCriticalSection(&DynamicRedirectp->Lock);
if (NAT_DYNAMIC_REDIRECT_DELETED(DynamicRedirectp)) {
LeaveCriticalSection(&DynamicRedirectp->Lock);
return ERROR_INVALID_PARAMETER;
}
DynamicRedirectp->Flags |= NAT_DYNAMIC_REDIRECT_FLAG_DELETED;
while (!IsListEmpty(&DynamicRedirectp->InstanceList)) {
PNAT_REDIRECT Redirectp =
CONTAINING_RECORD(
DynamicRedirectp->InstanceList.Flink,
NAT_REDIRECT,
Link
);
NatpDeleteDynamicRedirectInstance(DynamicRedirectp, Redirectp);
}
LeaveCriticalSection(&DynamicRedirectp->Lock);
//
// Release the initial reference to the dynamic redirect and return.
//
NAT_DEREFERENCE_DYNAMIC_REDIRECT(DynamicRedirectp);
return NO_ERROR;
} // NatCancelDynamicRedirect
ULONG
NatCancelRedirect(
HANDLE TranslatorHandle,
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG SourceAddress,
USHORT SourcePort,
ULONG NewDestinationAddress,
USHORT NewDestinationPort,
ULONG NewSourceAddress,
USHORT NewSourcePort
)
/*++
Routine Description:
This routine is invoked to cancel a redirect for a session.
Arguments:
TranslatorHandle - handle supplied by 'NatInitializeTranslator'
* - specify the redirect to be cancelled
Return Value:
ULONG - Win32 status code.
--*/
{
IP_NAT_LOOKUP_REDIRECT CancelRedirect;
IO_STATUS_BLOCK IoStatus;
NTSTATUS status;
HANDLE WaitEvent;
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (WaitEvent == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
CancelRedirect.Flags = 0;
CancelRedirect.RedirectApcContext = NULL;
CancelRedirect.Protocol = Protocol;
CancelRedirect.DestinationAddress = DestinationAddress;
CancelRedirect.DestinationPort = DestinationPort;
CancelRedirect.SourceAddress = SourceAddress;
CancelRedirect.SourcePort = SourcePort;
CancelRedirect.NewDestinationAddress = NewDestinationAddress;
CancelRedirect.NewDestinationPort = NewDestinationPort;
CancelRedirect.NewSourceAddress = NewSourceAddress;
CancelRedirect.NewSourcePort = NewSourcePort;
status =
NtDeviceIoControlFile(
TranslatorHandle,
WaitEvent,
NULL,
NULL,
&IoStatus,
IOCTL_IP_NAT_CANCEL_REDIRECT,
(PVOID)&CancelRedirect,
sizeof(CancelRedirect),
NULL,
0
);
if (status == STATUS_PENDING) {
WaitForSingleObject(WaitEvent, INFINITE);
status = IoStatus.Status;
}
CloseHandle(WaitEvent);
return NT_SUCCESS(status) ? NO_ERROR : RtlNtStatusToDosError(status);
} // NatCancelRedirect
VOID
NatCloseDriver(
HANDLE FileHandle
)
/*++
Routine Description:
This routine is called to close a handle to the NAT driver's device-object.
Arguments:
FileHandle - the handle to be closed.
Return Value:
none.
--*/
{
NtClose(FileHandle);
} // NatCloseDriver
ULONG
NatCreateDynamicFullRedirect(
ULONG Flags,
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG SourceAddress,
USHORT SourcePort,
ULONG NewDestinationAddress,
USHORT NewDestinationPort,
ULONG NewSourceAddress,
USHORT NewSourcePort,
ULONG RestrictSourceAddress OPTIONAL,
ULONG RestrictAdapterIndex OPTIONAL,
ULONG MinimumBacklog OPTIONAL,
OUT PHANDLE DynamicRedirectHandlep
)
/*++
Routine Description:
This routine is invoked to create a redirect which is dynamically
managed to ensure that there are always at least a specified minimum
number of instances active. It is suitable for use by transparent proxies,
which require assurance that all sessions matching a given description
will be redirected by the kernel-mode translation module.
The routine creates and initializes a structure which encapsulates all the
information required to establish an instance of the caller's redirect.
It then creates a series of instances of the redirect, and returns.
We rely on notification routines to replace each instance that is
activated or terminated.
Arguments:
Flags - specifies options for the redirect
Protocol - IP protocol of the session to be redirected
Destination* - destination endpoint of the session to be redirected
Source* - source endpoint of the session to be redirected
NewDestination* - replacement destination endpoint for the session
NewSource* - replacement source endpoint for the session
RestrictSourceAddress - optionally specifies the source address to which
the redirect should be applied
RestrictAdapterIndex - optionally specifies the adapter index that this
redirect should be restricted to
MinimumBacklog - optionally specifies the number of pending redirect
instances to leave as a backlog
DynamicRedirectHandlep - on output, receives a handle to the newly-created
dynamic redirect.
Return Value:
ULONG - Win32 status code.
--*/
{
PNAT_DYNAMIC_REDIRECT DynamicRedirectp;
ULONG Error;
ULONG i;
if (!DynamicRedirectHandlep ||
!NatpValidateRedirectParameters(
Flags,
Protocol,
DestinationAddress,
DestinationPort,
(Flags & NatRedirectFlagRestrictSource) ? RestrictSourceAddress : SourceAddress,
SourcePort,
NewDestinationAddress,
NewDestinationPort,
NewSourceAddress,
NewSourcePort,
RestrictAdapterIndex
)) {
return ERROR_INVALID_PARAMETER;
}
//
// Create and initialize the new dynamic redirect.
//
DynamicRedirectp = MALLOC(sizeof(*DynamicRedirectp));
if (!DynamicRedirectp) {
return ERROR_NOT_ENOUGH_MEMORY;
}
ZeroMemory(DynamicRedirectp, sizeof(*DynamicRedirectp));
__try {
InitializeCriticalSection(&DynamicRedirectp->Lock);
} __except(EXCEPTION_EXECUTE_HANDLER) {
Error = GetExceptionCode();
FREE(DynamicRedirectp);
return Error;
}
DynamicRedirectp->ReferenceCount = 1;
InitializeListHead(&DynamicRedirectp->InstanceList);
DynamicRedirectp->TranslatorHandle = NULL;
DynamicRedirectp->MinimumBacklog =
(MinimumBacklog ? MinimumBacklog : DEFAULT_DYNAMIC_REDIRECT_BACKLOG);
DynamicRedirectp->CreateRedirect.Flags =
Flags | IP_NAT_REDIRECT_FLAG_ASYNCHRONOUS;
DynamicRedirectp->CreateRedirect.Protocol = Protocol;
DynamicRedirectp->CreateRedirect.DestinationAddress = DestinationAddress;
DynamicRedirectp->CreateRedirect.DestinationPort = DestinationPort;
DynamicRedirectp->CreateRedirect.SourceAddress = SourceAddress;
DynamicRedirectp->CreateRedirect.SourcePort = SourcePort;
DynamicRedirectp->CreateRedirect.NewDestinationAddress =
NewDestinationAddress;
DynamicRedirectp->CreateRedirect.NewDestinationPort = NewDestinationPort;
DynamicRedirectp->CreateRedirect.NewSourceAddress = NewSourceAddress;
DynamicRedirectp->CreateRedirect.NewSourcePort = NewSourcePort;
DynamicRedirectp->CreateRedirect.RestrictSourceAddress =
RestrictSourceAddress;
DynamicRedirectp->CreateRedirect.RestrictAdapterIndex =
((Flags & NatRedirectFlagRestrictAdapter)
? RestrictAdapterIndex
: NAT_INVALID_IF_INDEX);
//
// Obtain a private handle to the kernel-mode translation module.
// It is important that this handle be private because, as noted
// in 'NatpDeleteDynamicRedirectInstance', we may mistakenly cancel
// redirects during normal execution, and they had better belong to us.
//
if (Error = NatOpenDriver(&DynamicRedirectp->TranslatorHandle)) {
NatpCleanupDynamicRedirect(DynamicRedirectp);
return Error;
}
//
// Issue the first set of redirects for the caller's minimum backlog.
//
EnterCriticalSection(&DynamicRedirectp->Lock);
for (i = 0; i < DynamicRedirectp->MinimumBacklog; i++) {
NatpCreateDynamicRedirectInstance(DynamicRedirectp);
}
LeaveCriticalSection(&DynamicRedirectp->Lock);
*DynamicRedirectHandlep = (HANDLE)DynamicRedirectp;
return NO_ERROR;
} // NatCreateDynamicFullRedirect
ULONG
NatCreateDynamicRedirect(
ULONG Flags,
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG NewDestinationAddress,
USHORT NewDestinationPort,
ULONG RestrictSourceAddress OPTIONAL,
ULONG MinimumBacklog OPTIONAL,
OUT PHANDLE DynamicRedirectHandlep
)
{
return
NatCreateDynamicFullRedirect(
Flags,
Protocol,
DestinationAddress,
DestinationPort,
0,
0,
NewDestinationAddress,
NewDestinationPort,
0,
0,
RestrictSourceAddress,
0,
MinimumBacklog,
DynamicRedirectHandlep
);
}
ULONG
NatCreateDynamicRedirectEx(
ULONG Flags,
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG NewDestinationAddress,
USHORT NewDestinationPort,
ULONG RestrictSourceAddress OPTIONAL,
ULONG RestrictAdapterIndex OPTIONAL,
ULONG MinimumBacklog OPTIONAL,
OUT PHANDLE DynamicRedirectHandlep
)
{
return
NatCreateDynamicFullRedirect(
Flags,
Protocol,
DestinationAddress,
DestinationPort,
0,
0,
NewDestinationAddress,
NewDestinationPort,
0,
0,
RestrictSourceAddress,
RestrictAdapterIndex,
MinimumBacklog,
DynamicRedirectHandlep
);
}
ULONG
NatCreateRedirect(
HANDLE TranslatorHandle,
ULONG Flags,
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG SourceAddress,
USHORT SourcePort,
ULONG NewDestinationAddress,
USHORT NewDestinationPort,
ULONG NewSourceAddress,
USHORT NewSourcePort,
PNAT_COMPLETION_ROUTINE CompletionRoutine,
PVOID CompletionContext,
HANDLE NotifyEvent OPTIONAL
)
{
return NatCreateRedirectEx(
TranslatorHandle,
Flags,
Protocol,
DestinationAddress,
DestinationPort,
SourceAddress,
SourcePort,
NewDestinationAddress,
NewDestinationPort,
NewSourceAddress,
NewSourcePort,
0,
CompletionRoutine,
CompletionContext,
NotifyEvent
);
}
ULONG
NatCreateRedirectEx(
HANDLE TranslatorHandle,
ULONG Flags,
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG SourceAddress,
USHORT SourcePort,
ULONG NewDestinationAddress,
USHORT NewDestinationPort,
ULONG NewSourceAddress,
USHORT NewSourcePort,
ULONG RestrictAdapterIndex OPTIONAL,
PNAT_COMPLETION_ROUTINE CompletionRoutine,
PVOID CompletionContext,
HANDLE NotifyEvent OPTIONAL
)
/*++
Routine Description:
This routine is invoked to install a redirect for a session.
Arguments:
TranslatorHandle - handle supplied by 'NatInitializeTranslator'
Flags - specifies options for the redirect
Protocol - IP protocol of the session to be redirected
Destination* - destination endpoint of the session to be redirected
Source* - source endpoint of the session to be redirected
NewDestination* - replacement destination endpoint for the session
NewSource* - replacement source endpoint for the session
RestrictAdapterIndex - optionally specifies the adapter index that this
redirect should be restricted to
Completion* - specifies routine invoked on completion of the session,
and the context to be passed to the routine
NotifyEvent - optionally specifies an event to be signalled
when a session matches the redirect.
Return Value:
ULONG - Win32 status code.
--*/
{
IP_NAT_CREATE_REDIRECT_EX CreateRedirect;
PNAT_REDIRECT Redirectp;
PIO_STATUS_BLOCK IoStatus;
NTSTATUS status;
HANDLE CompletionEvent;
if (!NatpValidateRedirectParameters(
Flags,
Protocol,
DestinationAddress,
DestinationPort,
SourceAddress,
SourcePort,
NewDestinationAddress,
NewDestinationPort,
NewSourceAddress,
NewSourcePort,
RestrictAdapterIndex
)) {
return ERROR_INVALID_PARAMETER;
}
if (!CompletionRoutine) {
Redirectp = NULL;
IoStatus = &UnusedIoStatus;
CompletionEvent = NULL;
} else if (IPNATAPI_SET_EVENT_ON_COMPLETION == CompletionRoutine) {
Redirectp = NULL;
IoStatus = &UnusedIoStatus;
CompletionEvent = (HANDLE)CompletionContext;
} else {
Redirectp = (PNAT_REDIRECT)MALLOC(sizeof(*Redirectp));
if (!Redirectp) { return ERROR_NOT_ENOUGH_MEMORY; }
Redirectp->CompletionRoutine = CompletionRoutine;
Redirectp->CompletionContext = CompletionContext;
IoStatus = &Redirectp->IoStatus;
}
if (Flags & NatRedirectFlagRestrictSource) {
CreateRedirect.RestrictSourceAddress = SourceAddress;
SourceAddress = 0;
} else {
CreateRedirect.RestrictSourceAddress = 0;
}
CreateRedirect.Flags = Flags;
CreateRedirect.Protocol = Protocol;
CreateRedirect.DestinationAddress = DestinationAddress;
CreateRedirect.DestinationPort = DestinationPort;
CreateRedirect.SourceAddress = SourceAddress;
CreateRedirect.SourcePort = SourcePort;
CreateRedirect.NewDestinationAddress = NewDestinationAddress;
CreateRedirect.NewDestinationPort = NewDestinationPort;
CreateRedirect.NewSourceAddress = NewSourceAddress;
CreateRedirect.NewSourcePort = NewSourcePort;
CreateRedirect.NotifyEvent = NotifyEvent;
CreateRedirect.RestrictAdapterIndex =
((Flags & NatRedirectFlagRestrictAdapter)
? RestrictAdapterIndex
: NAT_INVALID_IF_INDEX);
if (!CompletionRoutine
|| IPNATAPI_SET_EVENT_ON_COMPLETION == CompletionRoutine ) {
status =
NtDeviceIoControlFile(
TranslatorHandle,
CompletionEvent,
NULL,
NULL,
IoStatus,
IOCTL_IP_NAT_CREATE_REDIRECT_EX,
(PVOID)&CreateRedirect,
sizeof(CreateRedirect),
(PVOID)&UnusedStatistics,
sizeof(UnusedStatistics)
);
} else {
status =
NtDeviceIoControlFile(
TranslatorHandle,
NULL,
NatpRedirectCompletionRoutine,
Redirectp,
IoStatus,
IOCTL_IP_NAT_CREATE_REDIRECT_EX,
(PVOID)&CreateRedirect,
sizeof(CreateRedirect),
(PVOID)&Redirectp->Statistics,
sizeof(Redirectp->Statistics)
);
}
return NT_SUCCESS(status) ? NO_ERROR : RtlNtStatusToDosError(status);
} // NatCreateRedirect
ULONG
NatInitializeTranslator(
PHANDLE TranslatorHandle
)
/*++
Routine Description:
This routine is invoked to prepare for translation by loading the NAT
and installing all local adapters as interfaces.
Arguments:
TranslatorHandle - receives the file handle of the NAT driver
Return Value:
ULONG - Win32 status code.
--*/
{
ULONG Error;
IP_NAT_GLOBAL_INFO GlobalInfo;
//
// Initialize the NAT's global configuration
//
ZeroMemory(&GlobalInfo, sizeof(GlobalInfo));
GlobalInfo.Header.Version = IP_NAT_VERSION;
GlobalInfo.Header.Size = FIELD_OFFSET(RTR_INFO_BLOCK_HEADER, TocEntry);
//
// Start the NAT module.
// This step causes the driver to be loaded.
//
Error = NatLoadDriver(TranslatorHandle, &GlobalInfo);
if (Error) {
return Error;
}
return NO_ERROR;
} // NatInitializeTranslator
ULONG
NatLoadDriver(
PHANDLE FileHandle,
PIP_NAT_GLOBAL_INFO GlobalInfo
)
/*++
Routine Description:
This routine is invoked to initialize the NAT's data and start the driver.
Arguments:
FileHandle - receives the handle for the NAT's file-object
GlobalInfo - the global information for the NAT.
Return Value:
ULONG - Win32 status code.
--*/
{
UNICODE_STRING DeviceName;
ULONG Error;
IO_STATUS_BLOCK IoStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS status;
HANDLE WaitEvent;
#if 0
{
SC_HANDLE ScmHandle;
SC_HANDLE ServiceHandle;
SERVICE_STATUS ServiceStatus;
//
// Request that the service controller load the driver.
// Note that this will either succeed immediately or fail immediately;
// there is no 'checkpoint' processing for starting drivers.
//
if (!(ScmHandle = OpenSCManager(NULL, NULL, GENERIC_READ))) {
Error = GetLastError();
} else {
if (!(ServiceHandle =
OpenServiceA(ScmHandle, IP_NAT_SERVICE_NAME, GENERIC_EXECUTE))) {
Error = GetLastError();
} else {
if (!StartService(ServiceHandle, 0, NULL) &&
(Error = GetLastError()) != ERROR_SERVICE_ALREADY_RUNNING) {
} else {
Error = NO_ERROR;
}
CloseServiceHandle(ServiceHandle);
}
CloseServiceHandle(ScmHandle);
}
if (Error) {
return Error;
}
}
#else
{
UNICODE_STRING ServicePath;
BOOLEAN WasEnabled;
//
// Turn on our driver-loading ability
//
if (!NatpEnableLoadDriverPrivilege(&WasEnabled)) {
return ERROR_ACCESS_DENIED;
}
RtlInitUnicodeString(&ServicePath, NatpServicePath);
//
// Load the driver
//
status = NtLoadDriver(&ServicePath);
//
// Turn off the privilege
//
NatpDisableLoadDriverPrivilege(&WasEnabled);
//
// See if the load-attempt succeeded
//
if (!NT_SUCCESS(status) && status != STATUS_IMAGE_ALREADY_LOADED) {
Error = RtlNtStatusToDosError(status);
return Error;
}
}
#endif
//
// Obtain a handle to the NAT's device-object.
//
Error = NatOpenDriver(FileHandle);
if (Error) {
return Error;
}
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (WaitEvent == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// Set the global configuration of the NAT
//
status =
NtDeviceIoControlFile(
*FileHandle,
WaitEvent,
NULL,
NULL,
&IoStatus,
IOCTL_IP_NAT_SET_GLOBAL_INFO,
(PVOID)GlobalInfo,
FIELD_OFFSET(IP_NAT_GLOBAL_INFO, Header) + GlobalInfo->Header.Size,
NULL,
0
);
if (status == STATUS_PENDING) {
WaitForSingleObject(WaitEvent, INFINITE);
status = IoStatus.Status;
}
CloseHandle(WaitEvent);
if (!NT_SUCCESS(status)) {
Error = RtlNtStatusToDosError(status);
return Error;
}
return NO_ERROR;
} // NatLoadDriver
ULONG
NatLookupAndQueryInformationSessionMapping(
HANDLE TranslatorHandle,
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG SourceAddress,
USHORT SourcePort,
OUT PVOID Information,
IN OUT PULONG InformationLength,
NAT_SESSION_MAPPING_INFORMATION_CLASS InformationClass
)
/*++
Routine Description:
This routine attempts to locate a particular session mapping using either
its forward key or reverse key, and to query information for the mapping,
if found.
Arguments:
TranslatorHandle - handle supplied by 'NatInitializeTranslator'
Protocol - the IP protocol for the mapping to be located
Destination* - the destination endpoint for the mapping
Source* - the source endpoint for the mapping
Information - on output, receives the requested information
InformationLength - on input, contains the length of the buffer
at 'Information'; on output, receives the length of the information
stored in 'Information', or the length of the buffer required.
InformationClass - specifies
Return Value:
ULONG - Win32 status code.
--*/
{
IO_STATUS_BLOCK IoStatus;
IP_NAT_LOOKUP_SESSION_MAPPING LookupMapping;
NTSTATUS status;
HANDLE WaitEvent;
if (!InformationLength ||
InformationClass >= NatMaximumSessionMappingInformation) {
return ERROR_INVALID_PARAMETER;
}
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (WaitEvent == NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
LookupMapping.Protocol = Protocol;
LookupMapping.DestinationAddress = DestinationAddress;
LookupMapping.DestinationPort = DestinationPort;
LookupMapping.SourceAddress = SourceAddress;
LookupMapping.SourcePort = SourcePort;
if (InformationClass == NatKeySessionMappingInformation) {
status =
NtDeviceIoControlFile(
TranslatorHandle,
WaitEvent,
NULL,
NULL,
&IoStatus,
IOCTL_IP_NAT_LOOKUP_SESSION_MAPPING_KEY,
(PVOID)&LookupMapping,
sizeof(LookupMapping),
(PVOID)Information,
*InformationLength
);
if (status == STATUS_PENDING) {
WaitForSingleObject(WaitEvent, INFINITE);
status = IoStatus.Status;
}
} else if (InformationClass == NatStatisticsSessionMappingInformation) {
status =
NtDeviceIoControlFile(
TranslatorHandle,
WaitEvent,
NULL,
NULL,
&IoStatus,
IOCTL_IP_NAT_LOOKUP_SESSION_MAPPING_STATISTICS,
(PVOID)&LookupMapping,
sizeof(LookupMapping),
(PVOID)Information,
*InformationLength
);
if (status == STATUS_PENDING) {
WaitForSingleObject(WaitEvent, INFINITE);
status = IoStatus.Status;
}
} else if (InformationClass == NatKeySessionMappingExInformation) {
status =
NtDeviceIoControlFile(
TranslatorHandle,
WaitEvent,
NULL,
NULL,
&IoStatus,
IOCTL_IP_NAT_LOOKUP_SESSION_MAPPING_KEY_EX,
(PVOID)&LookupMapping,
sizeof(LookupMapping),
(PVOID)Information,
*InformationLength
);
if (status == STATUS_PENDING) {
WaitForSingleObject(WaitEvent, INFINITE);
status = IoStatus.Status;
}
} else {
CloseHandle(WaitEvent);
return ERROR_INVALID_PARAMETER;
}
CloseHandle(WaitEvent);
if (!NT_SUCCESS(status)) { return RtlNtStatusToDosError(status); }
switch(InformationClass) {
case NatKeySessionMappingInformation: {
*InformationLength = sizeof(NAT_KEY_SESSION_MAPPING_INFORMATION);
break;
}
case NatStatisticsSessionMappingInformation: {
*InformationLength =
sizeof(NAT_STATISTICS_SESSION_MAPPING_INFORMATION);
break;
}
case NatKeySessionMappingExInformation: {
*InformationLength =
sizeof(NAT_KEY_SESSION_MAPPING_EX_INFORMATION);
break;
}
default: {
return ERROR_INVALID_PARAMETER;
}
}
return NO_ERROR;
} // NatLookupAndQueryInformationSessionMapping
ULONG
NatOpenDriver(
OUT PHANDLE FileHandle
)
/*++
Routine Description:
This routine is called to open the NAT driver's device-object.
It assumes that the caller has loaded the driver already.
Arguments:
FileHandle - on output, receives the new handle
Return Value:
ULONG - Win32 status code.
--*/
{
UNICODE_STRING DeviceName;
IO_STATUS_BLOCK IoStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
NTSTATUS status;
//
// Obtain a handle to the NAT's device-object.
//
RtlInitUnicodeString(&DeviceName, DD_IP_NAT_DEVICE_NAME);
InitializeObjectAttributes(
&ObjectAttributes,
&DeviceName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
status =
NtOpenFile(
FileHandle,
SYNCHRONIZE|FILE_READ_DATA|FILE_WRITE_DATA,
&ObjectAttributes,
&IoStatus,
FILE_SHARE_READ|FILE_SHARE_WRITE,
0
);
if (!NT_SUCCESS(status)) {
return RtlNtStatusToDosError(status);
}
return NO_ERROR;
} // NatOpenDriver
VOID
NatpCleanupDynamicRedirect(
PNAT_DYNAMIC_REDIRECT DynamicRedirectp
)
/*++
Routine Description:
This routine is invoked when the last reference to a dynamic redirect
is released. It is responsible for cleaning up all resources in use
by the redirect.
Arguments:
DynamicRedirectp - the dynamic redirect to be cleaned up.
Return Value:
none.
Environment:
Invoked from an arbitrary context.
--*/
{
ASSERT(IsListEmpty(&DynamicRedirectp->InstanceList));
if (DynamicRedirectp->TranslatorHandle) {
NatCloseDriver(DynamicRedirectp->TranslatorHandle);
}
DeleteCriticalSection(&DynamicRedirectp->Lock);
FREE(DynamicRedirectp);
} // NatpCleanupDynamicRedirect
VOID
NatpCreateDynamicRedirectInstance(
PNAT_DYNAMIC_REDIRECT DynamicRedirectp
)
/*++
Routine Description:
This routine is invoked to submit an additional instance of the given
dynamic redirect. The redirect is associated with a notification event
so that this module is notified when the redirect is either activated
or terminated. In either case, another instance of the redirect will be
created.
Arguments:
DynamicRedirectp - the dynamic redirect to be reissued
Return Value:
none.
Environment:
Invoked with the dynamic-redirect's lock held by the caller.
--*/
{
PNAT_REDIRECT Redirectp = NULL;
NTSTATUS status;
do {
//
// Allocate and initialize a new redirect-instance
//
if (!NAT_REFERENCE_DYNAMIC_REDIRECT(DynamicRedirectp)) { break; }
Redirectp = MALLOC(sizeof(*Redirectp));
if (!Redirectp) {
NAT_DEREFERENCE_DYNAMIC_REDIRECT(DynamicRedirectp);
break;
}
ZeroMemory(Redirectp, sizeof(*Redirectp));
Redirectp->InstanceId = InterlockedIncrement(&NextRedirectInstanceId);
InsertTailList(&DynamicRedirectp->InstanceList, &Redirectp->Link);
//
// Create an event on which to receive notification of the redirect's
// activation or termination, allocate a notification context block,
// and register our notification routine for the event.
//
if (!(Redirectp->Event = CreateEvent(NULL, FALSE, FALSE, NULL))) {
break;
} else if (!(Redirectp->Context =
MALLOC(sizeof(*Redirectp->Context)))) {
break;
} else {
Redirectp->Context->DynamicRedirectp = DynamicRedirectp;
Redirectp->Context->InstanceId = Redirectp->InstanceId;
if (!RegisterWaitForSingleObject(
&Redirectp->WaitHandle,
Redirectp->Event,
NatpDynamicRedirectNotificationRoutine,
Redirectp->Context,
INFINITE,
WT_EXECUTEINIOTHREAD | WT_EXECUTEONLYONCE
)) {
break;
}
}
//
// Issue the actual redirect request.
// Now we will notified either by the kernel-mode translation module
// when the instance is activated, or by the I/O manager when the
// I/O control completes or is cancelled.
//
DynamicRedirectp->CreateRedirect.NotifyEvent = Redirectp->Event;
status =
NtDeviceIoControlFile(
DynamicRedirectp->TranslatorHandle,
Redirectp->Event,
NULL,
NULL,
&UnusedIoStatus,
IOCTL_IP_NAT_CREATE_REDIRECT_EX,
(PVOID)&DynamicRedirectp->CreateRedirect,
sizeof(DynamicRedirectp->CreateRedirect),
(PVOID)&UnusedStatistics,
sizeof(UnusedStatistics)
);
if (!NT_SUCCESS(status)) {
if (UnregisterWait(Redirectp->WaitHandle)) {
FREE(Redirectp->Context);
NAT_DEREFERENCE_DYNAMIC_REDIRECT(DynamicRedirectp);
}
Redirectp->WaitHandle = NULL;
break;
}
return;
} while(FALSE);
if (Redirectp) {
NatpDeleteDynamicRedirectInstance(DynamicRedirectp, Redirectp);
}
} // NatpCreateDynamicRedirectInstance
VOID
NatpDeleteDynamicRedirectInstance(
PNAT_DYNAMIC_REDIRECT DynamicRedirectp,
PNAT_REDIRECT Redirectp
)
/*++
Routine Description:
This routine is invoked to delete a given instance of a dynamic redirect.
The redirect is cancelled, synchronizing with the notification-routine
for the instance.
Arguments:
DynamicRedirectp - the dynamic redirect whose instance is to be deleted
Redirectp - the dynamic redirect instance to be deleted
Return Value:
none.
Environment:
Invoked with the dynamic redirect's lock held by the caller.
--*/
{
//
// We need to cancel the outstanding redirect, which will have been created
// if the wait-handle is non-NULL. However, when we issue the cancellation
// we have no way to know if the instance in question is already being
// completed by the kernel-mode translation module. If that is the case,
// our cancellation may affect some other instance issued on this
// translator-handle. It will not affect any instance issued on any other
// translator-handle since the kernel-mode translator will not allow
// redirects issued on one file-object to be cancelled from another
// file-object.
//
// Since we own the translation-handle, though, it is alright for us to
// erroneously cancel instances in this manner. The notification routine
// for the cancelled instance will just create a replacement.
//
// There is additional point of synchronization to be noted.
// If the notification routine runs, it is responsible for deleting
// the notification context and releasing the reference to the dynamic
// redirect. However, if we unregister our wait and the notification
// routine never runs, we are responsible for both tasks.
// The return code from 'UnregisterWait' is therefore used below as an
// indication of whether the two tasks should be performed here or left
// for the notification routine to perform.
//
// Finally, the instance only needs to be cancelled if its wait-handle
// is valid, since otherwise the instance must have never been issued.
//
if (Redirectp->WaitHandle) {
if (UnregisterWait(Redirectp->WaitHandle)) {
FREE(Redirectp->Context);
NAT_DEREFERENCE_DYNAMIC_REDIRECT(DynamicRedirectp);
}
Redirectp->WaitHandle = NULL;
NatCancelRedirect(
DynamicRedirectp->TranslatorHandle,
DynamicRedirectp->CreateRedirect.Protocol,
DynamicRedirectp->CreateRedirect.DestinationAddress,
DynamicRedirectp->CreateRedirect.DestinationPort,
DynamicRedirectp->CreateRedirect.SourceAddress,
DynamicRedirectp->CreateRedirect.SourcePort,
DynamicRedirectp->CreateRedirect.NewDestinationAddress,
DynamicRedirectp->CreateRedirect.NewDestinationPort,
DynamicRedirectp->CreateRedirect.NewSourceAddress,
DynamicRedirectp->CreateRedirect.NewSourcePort
);
}
if (Redirectp->Event) {
CloseHandle(Redirectp->Event); Redirectp->Event = NULL;
}
RemoveEntryList(&Redirectp->Link);
FREE(Redirectp);
} // NatpDeleteDynamicRedirectInstance
VOID
NatpDisableLoadDriverPrivilege(
PBOOLEAN WasEnabled
)
/*++
Routine Description:
This routine is invoked to disable the previously-enable 'LoadDriver'
privilege for the calling thread.
Arguments:
WasEnabled - on input, indicates whether the privilege was already enabled.
Return Value:
none.
--*/
{
NTSTATUS Status;
//
// See if we had to enable SE_LOAD_DRIVER_PRIVILEGE
//
if (!*WasEnabled) {
//
// relinquish "Load-Driver" privileges for this thread
//
Status =
RtlAdjustPrivilege(
SE_LOAD_DRIVER_PRIVILEGE,
FALSE,
TRUE,
WasEnabled
);
}
//
// return the thread to its previous access token
//
RevertToSelf();
} // NatpDisableLoadDriverPrivilege
VOID NTAPI
NatpDynamicRedirectNotificationRoutine(
PVOID Context,
BOOLEAN WaitCompleted
)
/*++
Routine Description:
This routine is invoked upon activation or termination of one of a
dynamic redirect's instantiated redirects by an incoming session.
It attempts to locate the corresponding instance and, if successful,
closes the wait-handle and event for the instance, and adds another
instance of the dynamic redirect to replace the one which has been
activated or terminated.
Arguments:
Context - contains context information for the notification
WaitCompleted - indicates whether the wait completed or timed out
Return Value:
none.
Environment:
Invoked in the context of a system wait thread.
--*/
{
PNAT_DYNAMIC_REDIRECT_CONTEXT Contextp =
(PNAT_DYNAMIC_REDIRECT_CONTEXT)Context;
PNAT_DYNAMIC_REDIRECT DynamicRedirectp = Contextp->DynamicRedirectp;
PLIST_ENTRY Link;
PNAT_REDIRECT Redirectp;
//
// Search the dynamic redirect's list of instances for the instance
// whose event has been signalled, and remove it after clearing the
// wait-handle to ensure that the deletion-routine does not attempt
// to cancel the redirect.
//
EnterCriticalSection(&DynamicRedirectp->Lock);
for (Link = DynamicRedirectp->InstanceList.Flink;
Link != &DynamicRedirectp->InstanceList; Link = Link->Flink) {
Redirectp = CONTAINING_RECORD(Link, NAT_REDIRECT, Link);
if (Redirectp->InstanceId == Contextp->InstanceId) {
UnregisterWait(Redirectp->WaitHandle);
Redirectp->WaitHandle = NULL;
NatpDeleteDynamicRedirectInstance(DynamicRedirectp, Redirectp);
break;
}
}
FREE(Contextp);
//
// If the dynamic redirect has not been deleted,
// replace the instance deleted above, if any.
//
if (!NAT_DYNAMIC_REDIRECT_DELETED(DynamicRedirectp)) {
NatpCreateDynamicRedirectInstance(DynamicRedirectp);
}
LeaveCriticalSection(&DynamicRedirectp->Lock);
//
// Drop the original reference to the dynamic redirect, and return.
//
NAT_DEREFERENCE_DYNAMIC_REDIRECT(DynamicRedirectp);
} // NatpDynamicRedirectNotificationRoutine
BOOLEAN
NatpEnableLoadDriverPrivilege(
PBOOLEAN WasEnabled
)
/*++
Routine Description:
This routine is invoked to enable the 'LoadDriver' privilege
of the calling thread.
Arguments:
WasEnabled - on output indicates whether the privilege was already enabled
Return Value:
BOOLEAN - TRUE if successful, FALSE otherwise.
--*/
{
NTSTATUS Status;
//
// Obtain the process' access token for the current thread
//
Status = RtlImpersonateSelf(SecurityImpersonation);
if (!NT_SUCCESS(Status)) {
return FALSE;
}
//
// request "Load-Driver" privileges for this thread
//
Status =
RtlAdjustPrivilege(
SE_LOAD_DRIVER_PRIVILEGE,
TRUE,
TRUE,
WasEnabled
);
if (!NT_SUCCESS(Status)) {
RevertToSelf();
return FALSE;
}
return TRUE;
} // NatpEnableLoadDriverPrivilege
VOID NTAPI
NatpRedirectCompletionRoutine(
PVOID Context,
PIO_STATUS_BLOCK IoStatus,
ULONG Reserved
)
/*++
Routine Description:
This routine is invoked upon completion of a redirect-IRP.
Arguments:
Context - indicates the redirect which was completed
IoStatus - contains the final status of the request
Reserved - unused
Return Value:
none.
--*/
{
PNAT_REDIRECT Redirectp = (PNAT_REDIRECT)Context;
if (Redirectp->CompletionRoutine) {
Redirectp->CompletionRoutine(
(HANDLE)Redirectp,
(BOOLEAN)((IoStatus->Status == STATUS_CANCELLED) ? TRUE : FALSE),
Redirectp->CompletionContext
);
}
FREE(Redirectp);
} // NatpRedirectCompletionRoutine
BOOLEAN
NatpValidateRedirectParameters(
ULONG Flags,
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG SourceAddress,
USHORT SourcePort,
ULONG NewDestinationAddress,
USHORT NewDestinationPort,
ULONG NewSourceAddress,
USHORT NewSourcePort,
ULONG RestrictAdapterIndex OPTIONAL
)
/*++
Routine Description:
This routine validates redirect parameters
Arguments:
Flags - specifies options for the redirect
Protocol - IP protocol of the session to be redirected
Destination* - destination endpoint of the session to be redirected
Source* - source endpoint of the session to be redirected
NewDestination* - replacement destination endpoint for the session
NewSource* - replacement source endpoint for the session
Return Value:
BOOLEAN: TRUE if parameters are OK; FALSE otherwise
--*/
{
//
// Make sure no invalid flags are specified
//
if (Flags & ~NatRedirectFlagsAll)
{
return FALSE;
}
//
// TCP and UDP are the only valid protocols
//
if (Protocol != NAT_PROTOCOL_TCP && Protocol != NAT_PROTOCOL_UDP)
{
return FALSE;
}
//
// Validate endpoint information. There are two different sets of
// behavior, based on the presence of NatRedirectFlagSourceRedirect
//
if (!(Flags & NatRedirectFlagSourceRedirect))
{
//
// A destination address must be specified, unless
// NatRedirectFlagPortRedirect is set
//
if (!DestinationAddress & !(Flags & NatRedirectFlagPortRedirect))
{
return FALSE;
}
//
// There must be a destination port
//
if (!DestinationPort)
{
return FALSE;
}
//
// Both the replacement destination address and port must be specified
//
if (!NewDestinationAddress || !NewDestinationPort)
{
return FALSE;
}
//
// The replacement source address and port are both specified or
// unspecified
//
if (!!NewSourceAddress ^ !!NewSourcePort)
{
return FALSE;
}
//
// The source port must be unspecified if the source address
// is unspecified
//
if (!SourceAddress && SourcePort)
{
return FALSE;
}
//
// The replacement source is unspecified then the source port
// is also unspecified.
//
if (!NewSourceAddress && SourcePort)
{
return FALSE;
}
//
// If the source address is specified w/o a replacement source,
// the caller must specify the restrict-source flag indicating
// that this is a partial redirect restricted to a particular source.
//
if (!NewSourceAddress && SourceAddress
&& !(Flags & NatRedirectFlagRestrictSource))
{
return FALSE;
}
//
// If the restrict-source flag is specified, the caller is specifiying
// a partial redirect w/ a source address
//
if ((Flags & NatRedirectFlagRestrictSource)
&& (NewSourceAddress || !SourceAddress))
{
return FALSE;
}
//
// If the port-redirect flag is specified, the caller is specifying
// only the destination port, replacement destination address, and
// replacement destination port
//
if ((Flags & NatRedirectFlagPortRedirect)
&& (DestinationAddress || SourceAddress || SourcePort
|| NewSourceAddress || NewSourcePort))
{
return FALSE;
}
}
else
{
//
// The source address must be specified, unless
// NatRedirectFlagPortRedirect is specified
//
if (!SourceAddress && !(Flags & NatRedirectFlagPortRedirect))
{
return FALSE;
}
//
// The source port must be specified
//
if (!SourcePort)
{
return FALSE;
}
//
// No destination information may be specified
//
if (DestinationAddress || DestinationPort)
{
return FALSE;
}
//
// The replacement destination address and port are both specified
// or unspecified
//
if (!!NewDestinationAddress ^ !!NewDestinationPort)
{
return FALSE;
}
//
// The replacement source address and port must be specified,
// unless the port-redirect flag is set
//
if ((!NewSourceAddress || !NewSourcePort)
&& !(Flags & NatRedirectFlagPortRedirect))
{
return FALSE;
}
//
// If the port-redirect flag is specified, the caller is specifying
// only the source port, replacement destination address, and
// replacement destination port
//
if ((Flags & NatRedirectFlagPortRedirect)
&& (SourceAddress || DestinationAddress || DestinationPort
|| NewSourceAddress || NewSourcePort))
{
return FALSE;
}
//
// The restrict-source-address flag is invalid
//
if (Flags & NatRedirectFlagRestrictSource)
{
return FALSE;
}
}
//
// The unidirectional flag is specified only for UDP redirects
//
if (Flags & NatRedirectFlagUnidirectional
&& Protocol != NAT_PROTOCOL_UDP)
{
return FALSE;
}
//
// If the restrict-adapter-index flag is specified, the caller
// has given a valid, non-zero (i.e., local) interface index
//
if ((Flags & NatRedirectFlagRestrictAdapter)
&& (NAT_INVALID_IF_INDEX == RestrictAdapterIndex
|| LOCAL_IF_INDEX == RestrictAdapterIndex))
{
return FALSE;
}
return TRUE;
}
ULONG
NatQueryInformationRedirect(
HANDLE TranslatorHandle,
UCHAR Protocol,
ULONG DestinationAddress,
USHORT DestinationPort,
ULONG SourceAddress,
USHORT SourcePort,
ULONG NewDestinationAddress,
USHORT NewDestinationPort,
ULONG NewSourceAddress,
USHORT NewSourcePort,
OUT PVOID Information,
IN OUT PULONG InformationLength,
NAT_REDIRECT_INFORMATION_CLASS InformationClass
)
/*++
Routine Description:
This routine is called to obtain information about the session
for a completed redirect.
Arguments:
TranslatorHandle - handle supplied by 'NatInitializeTranslator'
* - specify the redirect to be queried
Information - receives the retrieved information
InformationLength - specifies the size of 'Information' on input;
contains the required size on output
InformationClass - indicates the class of information requested
Return Value:
ULONG - Win32 status code.
--*/
{
ULONG Error = NO_ERROR;
IO_STATUS_BLOCK IoStatus;
ULONG Length;
IP_NAT_LOOKUP_REDIRECT QueryRedirect;
IP_NAT_REDIRECT_STATISTICS RedirectStatistics;
IP_NAT_REDIRECT_SOURCE_MAPPING RedirectSourceMapping;
IP_NAT_REDIRECT_DESTINATION_MAPPING RedirectDestinationMapping;
NTSTATUS status;
HANDLE WaitEvent;
if (!InformationLength ||
InformationClass >= NatMaximumRedirectInformation) {
return ERROR_INVALID_PARAMETER;
}
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (WaitEvent== NULL) {
return ERROR_NOT_ENOUGH_MEMORY;
}
QueryRedirect.Flags = 0;
QueryRedirect.RedirectApcContext = NULL;
QueryRedirect.Protocol = Protocol;
QueryRedirect.DestinationAddress = DestinationAddress;
QueryRedirect.DestinationPort = DestinationPort;
QueryRedirect.SourceAddress = SourceAddress;
QueryRedirect.SourcePort = SourcePort;
QueryRedirect.NewDestinationAddress = NewDestinationAddress;
QueryRedirect.NewDestinationPort = NewDestinationPort;
QueryRedirect.NewSourceAddress = NewSourceAddress;
QueryRedirect.NewSourcePort = NewSourcePort;
if (InformationClass == NatDestinationMappingRedirectInformation) {
status =
NtDeviceIoControlFile(
TranslatorHandle,
WaitEvent,
NULL,
NULL,
&IoStatus,
IOCTL_IP_NAT_GET_REDIRECT_DESTINATION_MAPPING,
(PVOID)&QueryRedirect,
sizeof(QueryRedirect),
(PVOID)&RedirectDestinationMapping,
sizeof(RedirectDestinationMapping)
);
} else if (InformationClass == NatSourceMappingRedirectInformation) {
status =
NtDeviceIoControlFile(
TranslatorHandle,
WaitEvent,
NULL,
NULL,
&IoStatus,
IOCTL_IP_NAT_GET_REDIRECT_SOURCE_MAPPING,
(PVOID)&QueryRedirect,
sizeof(QueryRedirect),
(PVOID)&RedirectSourceMapping,
sizeof(RedirectSourceMapping)
);
} else {
status =
NtDeviceIoControlFile(
TranslatorHandle,
WaitEvent,
NULL,
NULL,
&IoStatus,
IOCTL_IP_NAT_GET_REDIRECT_STATISTICS,
(PVOID)&QueryRedirect,
sizeof(QueryRedirect),
(PVOID)&RedirectStatistics,
sizeof(RedirectStatistics)
);
}
if (status == STATUS_PENDING) {
WaitForSingleObject(WaitEvent, INFINITE);
status = IoStatus.Status;
}
CloseHandle(WaitEvent);
if (!NT_SUCCESS(status)) { return RtlNtStatusToDosError(status); }
switch (InformationClass) {
case NatByteCountRedirectInformation: {
PNAT_BYTE_COUNT_REDIRECT_INFORMATION ByteCount =
(PNAT_BYTE_COUNT_REDIRECT_INFORMATION)Information;
if (*InformationLength < sizeof(*ByteCount)) {
Error = ERROR_INSUFFICIENT_BUFFER;
} else {
ByteCount->BytesForward = RedirectStatistics.BytesForward;
ByteCount->BytesReverse = RedirectStatistics.BytesReverse;
}
*InformationLength = sizeof(*ByteCount);
break;
}
case NatRejectRedirectInformation: {
PNAT_REJECT_REDIRECT_INFORMATION Reject =
(PNAT_REJECT_REDIRECT_INFORMATION)Information;
if (*InformationLength < sizeof(*Reject)) {
Error = ERROR_INSUFFICIENT_BUFFER;
} else {
Reject->RejectsForward = RedirectStatistics.RejectsForward;
Reject->RejectsReverse = RedirectStatistics.RejectsReverse;
}
*InformationLength = sizeof(*Reject);
break;
}
case NatDestinationMappingRedirectInformation: {
PNAT_DESTINATION_MAPPING_REDIRECT_INFORMATION DestinationMapping =
(PNAT_DESTINATION_MAPPING_REDIRECT_INFORMATION)Information;
if (*InformationLength < sizeof(*DestinationMapping)) {
Error = ERROR_INSUFFICIENT_BUFFER;
} else {
DestinationMapping->DestinationAddress =
RedirectDestinationMapping.DestinationAddress;
DestinationMapping->DestinationPort =
RedirectDestinationMapping.DestinationPort;
DestinationMapping->NewDestinationAddress =
RedirectDestinationMapping.NewDestinationAddress;
DestinationMapping->NewDestinationPort =
RedirectDestinationMapping.NewDestinationPort;
}
*InformationLength = sizeof(*DestinationMapping);
break;
}
case NatSourceMappingRedirectInformation: {
PNAT_SOURCE_MAPPING_REDIRECT_INFORMATION SourceMapping =
(PNAT_SOURCE_MAPPING_REDIRECT_INFORMATION)Information;
if (*InformationLength < sizeof(*SourceMapping)) {
Error = ERROR_INSUFFICIENT_BUFFER;
} else {
SourceMapping->SourceAddress =
RedirectSourceMapping.SourceAddress;
SourceMapping->SourcePort =
RedirectSourceMapping.SourcePort;
SourceMapping->NewSourceAddress =
RedirectSourceMapping.NewSourceAddress;
SourceMapping->NewSourcePort =
RedirectSourceMapping.NewSourcePort;
}
*InformationLength = sizeof(*SourceMapping);
break;
}
default:
return ERROR_INVALID_PARAMETER;
}
return Error;
} // NatQueryInformationRedirect
ULONG
NatQueryInformationRedirectHandle(
HANDLE RedirectHandle,
OUT PVOID Information,
IN OUT PULONG InformationLength,
NAT_REDIRECT_INFORMATION_CLASS InformationClass
)
/*++
Routine Description:
This routine is invoked to retrieve information about a redirect upon
completion of the associated I/O request. At this point, the kernel-mode
driver is no longer aware of the redirect, and hence we read the requested
information from the output-buffer for the redirect.
Arguments:
RedirectHandle - identifies the redirect to be queried
Information - receives the retrieved information
InformationLength - specifies the size of 'Information' on input;
contains the required size on output
InformationClass - indicates the class of information requested
Return Value:
ULONG - Win32 status code.
--*/
{
ULONG Error = NO_ERROR;
ULONG Length;
PNAT_REDIRECT Redirectp;
if (!InformationLength) { return ERROR_INVALID_PARAMETER; }
Redirectp = (PNAT_REDIRECT)RedirectHandle;
switch (InformationClass) {
case NatByteCountRedirectInformation: {
PNAT_BYTE_COUNT_REDIRECT_INFORMATION ByteCount =
(PNAT_BYTE_COUNT_REDIRECT_INFORMATION)Information;
Length = sizeof(*ByteCount);
if (*InformationLength < Length) {
Error = ERROR_INSUFFICIENT_BUFFER;
} else {
ByteCount->BytesForward = Redirectp->Statistics.BytesForward;
ByteCount->BytesReverse = Redirectp->Statistics.BytesReverse;
}
*InformationLength = Length;
break;
}
case NatRejectRedirectInformation: {
PNAT_REJECT_REDIRECT_INFORMATION Reject =
(PNAT_REJECT_REDIRECT_INFORMATION)Information;
Length = sizeof(*Reject);
if (*InformationLength < Length) {
Error = ERROR_INSUFFICIENT_BUFFER;
} else {
Reject->RejectsForward = Redirectp->Statistics.RejectsForward;
Reject->RejectsReverse = Redirectp->Statistics.RejectsReverse;
}
*InformationLength = Length;
break;
}
default:
return ERROR_INVALID_PARAMETER;
}
return Error;
} // NatQueryInformationRedirectHandle
VOID
NatShutdownTranslator(
HANDLE TranslatorHandle
)
/*++
Routine Description:
This routine is invoked to shut down the NAT.
Arguments:
TranslatorHandle - handle supplied by 'NatInitializeTranslator'
Return Value:
none.
--*/
{
NatUnloadDriver(TranslatorHandle);
} // NatShutdownTranslator
ULONG
NatUnloadDriver(
HANDLE FileHandle
)
/*++
Routine Description:
This routine is invoked to unload the NAT driver as the protocol stops.
Arguments:
FileHandle - identifies the file-object for the NAT driver
Return Value:
ULONG - Win32 status code.
--*/
{
ULONG Error;
//
// Close our file-handle to the driver
//
if (FileHandle) { NtClose(FileHandle); }
#if 0
{
SC_HANDLE ScmHandle;
SC_HANDLE ServiceHandle;
SERVICE_STATUS ServiceStatus;
//
// Notify the service controller that the driver should be stopped.
// If other processes are using the driver, this control will be ignored.
//
ScmHandle = OpenSCManager(NULL, NULL, GENERIC_READ);
if (ScmHandle) {
ServiceHandle =
OpenServiceA(ScmHandle, IP_NAT_SERVICE_NAME, GENERIC_EXECUTE);
if (ServiceHandle) {
ControlService(ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus);
CloseServiceHandle(ServiceHandle);
}
CloseServiceHandle(ScmHandle);
}
}
#else
{
UNICODE_STRING ServicePath;
NTSTATUS status;
BOOLEAN WasEnabled;
//
// Turn on our driver-unloading ability
//
if (!NatpEnableLoadDriverPrivilege(&WasEnabled)) {
return ERROR_ACCESS_DENIED;
}
RtlInitUnicodeString(&ServicePath, NatpServicePath);
//
// Load the driver
//
status = NtUnloadDriver(&ServicePath);
//
// Turn off the privilege
//
NatpDisableLoadDriverPrivilege(&WasEnabled);
//
// See if the unload-attempt succeeded
//
if (!NT_SUCCESS(status)) {
Error = RtlNtStatusToDosError(status);
return Error;
}
}
#endif
return NO_ERROR;
} // NatUnloadDriver