/*++ 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 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 // LONG DllReferenceAdded = 0; 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; } // // Add a reference to ipnathlp.dll if necessary; this reference // will never be released. This is needed to prevent a race condition // between the dll unloading during RRAS shutdown and the completion // of cleaning up all dynamic redirects and associated registered // waits (see bug 448249) // if (0 == InterlockedExchange(&DllReferenceAdded, 1)) { if (NULL == LoadLibraryW(L"ipnathlp.dll")) { // // For some reason the reference attempt failed. Reset // our tracking variable so the next caller through will // try this again. // InterlockedExchange(&DllReferenceAdded, 0); } } // // 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