/*++

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