|
|
/*++
Copyright (c) 1998, Microsoft Corporation
Module Name:
natio.h
Abstract:
This module contains declarations for the NAT's I/O interface to the kernel-mode driver.
Author:
Abolade Gbadegesin (aboladeg) 10-Mar-1998
Revision History:
--*/
#include "precomp.h"
#pragma hdrstop
#include <ras.h>
#include <rasuip.h>
#include <raserror.h>
//
// PRIVATE GLOBAL VARIABLES
//
HANDLE NatFileHandle; LIST_ENTRY NatInterfaceList; //
// Controls access to 'NatFileHandle' and 'NatInterfaceList'.
//
CRITICAL_SECTION NatInterfaceLock;
//
// FORWARD DECLARATIONS
//
VOID NatpDisableLoadDriverPrivilege( PBOOLEAN WasEnabled );
BOOLEAN NatpEnableLoadDriverPrivilege( PBOOLEAN WasEnabled );
PNAT_INTERFACE NatpLookupInterface( ULONG Index, OUT PLIST_ENTRY* InsertionPoint OPTIONAL );
ULONG NatBindInterface( ULONG Index, PNAT_INTERFACE Interfacep OPTIONAL, PIP_ADAPTER_BINDING_INFO BindingInfo, ULONG AdapterIndex )
/*++
Routine Description:
This routine is invoked to bind the NAT to an interface.
Arguments:
Index - the interface to be bound
Interfacep - optionally supplies the interface-structure to be bound (See 'NATCONN.C' which passes in a static interface-structure).
BindingInfo - the interface's address-information
AdapterIndex - optionally specifies the interface's TCP/IP adapter index. This is set only for home-router interfaces.
Return Value:
ULONG - Win32 status code.
--*/
{ PIP_NAT_CREATE_INTERFACE CreateInterface; ULONG Error; IO_STATUS_BLOCK IoStatus; ULONG Size; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatBindInterface");
Error = NO_ERROR;
//
// Look up the interface to be bound
//
EnterCriticalSection(&NatInterfaceLock); if (!Interfacep && !(Interfacep = NatpLookupInterface(Index, NULL))) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatBindInterface: interface %d not found", Index ); return ERROR_NO_SUCH_INTERFACE; }
//
// Make sure the interface isn't already bound
//
if (NAT_INTERFACE_BOUND(Interfacep)) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatBindInterface: interface %d is already bound", Index ); return ERROR_ADDRESS_ALREADY_ASSOCIATED; }
//
// Allocate the bind-structure
//
Size = sizeof(IP_NAT_CREATE_INTERFACE) + SIZEOF_IP_BINDING(BindingInfo->AddressCount);
CreateInterface = reinterpret_cast<PIP_NAT_CREATE_INTERFACE>( NH_ALLOCATE(Size));
if (!CreateInterface) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatBindInterface: allocation failed for interface %d binding", Index ); NhErrorLog( IP_NAT_LOG_ALLOCATION_FAILED, 0, "%d", Size ); return ERROR_NOT_ENOUGH_MEMORY; }
Interfacep->AdapterIndex = (AdapterIndex != (ULONG)-1) ? AdapterIndex : NhMapInterfaceToAdapter(Interfacep->Index); if (Interfacep->AdapterIndex == (ULONG)-1) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatBindInterface: NhMapInterfaceToAdapter failed for %d", Index ); return ERROR_INVALID_INDEX; } CreateInterface->Index = Interfacep->AdapterIndex; CopyMemory( CreateInterface->BindingInfo, BindingInfo, SIZEOF_IP_BINDING(BindingInfo->AddressCount) );
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatBindInterface: CreateEvent failed [%d] for interface %d", GetLastError(), Index ); return ERROR_NOT_ENOUGH_MEMORY; }
//
// Install the interface
//
status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_CREATE_INTERFACE, (PVOID)CreateInterface, Size, NULL, 0 ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; } NH_FREE(CreateInterface);
if (!NT_SUCCESS(status)) { CloseHandle(WaitEvent); LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatBindInterface: status %08x binding interface %d", status, Index ); Error = RtlNtStatusToDosError(status); NhErrorLog( IP_NAT_LOG_IOCTL_FAILED, Error, "" ); return Error; }
//
// Now set its configuration
//
Interfacep->Info->Index = Interfacep->AdapterIndex; Size = FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + Interfacep->Info->Header.Size;
status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_SET_INTERFACE_INFO, (PVOID)Interfacep->Info, Size, NULL, 0 ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; }
if (!NT_SUCCESS(status)) { ULONG AdapterIndex = Interfacep->AdapterIndex; LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatBindInterface: status %08x setting info for interface %d (%d)", status, Index, AdapterIndex ); Error = RtlNtStatusToDosError(status); NhErrorLog( IP_NAT_LOG_IOCTL_FAILED, Error, "" ); status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_DELETE_INTERFACE, (PVOID)&AdapterIndex, sizeof(ULONG), NULL, 0 ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; } CloseHandle(WaitEvent); return Error; }
Interfacep->Flags |= NAT_INTERFACE_FLAG_BOUND;
if (Interfacep->Type == ROUTER_IF_TYPE_DEDICATED) { NatUpdateProxyArp(Interfacep, TRUE); }
CloseHandle(WaitEvent);
LeaveCriticalSection(&NatInterfaceLock);
return Error;
} // NatBindInterface
ULONG NatConfigureDriver( PIP_NAT_GLOBAL_INFO GlobalInfo )
/*++
Routine Description:
This routine is called to update the configuration for the NAT driver.
Arguments:
GlobalInfo - the new configuration for the NAT.
Return Value:
ULONG - Win32 status code.
--*/
{ ULONG Error = NO_ERROR; IO_STATUS_BLOCK IoStatus; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatConfigureDriver");
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { NhTrace( TRACE_FLAG_NAT, "NatConfigureDriver: CreateEvent failed [%d]", GetLastError() ); return ERROR_NOT_ENOUGH_MEMORY; }
//
// Attempt to configure the driver
//
EnterCriticalSection(&NatInterfaceLock); status = NtDeviceIoControlFile( NatFileHandle, 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; } LeaveCriticalSection(&NatInterfaceLock);
if (!NT_SUCCESS(status)) { NhTrace( TRACE_FLAG_NAT, "NatConfigureDriver: status %08x setting global info", status ); Error = RtlNtStatusToDosError(status); NhErrorLog( IP_NAT_LOG_IOCTL_FAILED, Error, "" ); }
CloseHandle(WaitEvent);
return Error;
} // NatConfigureDriver
ULONG NatConfigureInterface( ULONG Index, PIP_NAT_INTERFACE_INFO InterfaceInfo )
/*++
Routine Description:
This routine is invoked to set the configuration for a NAT interface.
Arguments:
Index - the interface to be configured
InterfaceInfo - the configuration for the interface
Return Value:
ULONG - Win32 status code.
--*/
{ ULONG Error; PIP_NAT_INTERFACE_INFO Info; PNAT_INTERFACE Interfacep; IO_STATUS_BLOCK IoStatus; ULONG Size; NTSTATUS status;
PROFILE("NatConfigureInterface");
if (!InterfaceInfo) { NhTrace( TRACE_FLAG_NAT, "NatConfigureInterface: no interface info for %d", Index ); return ERROR_INVALID_PARAMETER; }
//
// Make a copy of the information
//
Size = FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + InterfaceInfo->Header.Size;
Info = (PIP_NAT_INTERFACE_INFO)NH_ALLOCATE(Size);
if (!Info) { NhTrace( TRACE_FLAG_NAT, "NatConfigureInterface: error allocating copy of configuration" ); NhErrorLog( IP_NAT_LOG_ALLOCATION_FAILED, 0, "%d", Size ); return ERROR_NOT_ENOUGH_MEMORY; }
CopyMemory( Info, InterfaceInfo, Size );
//
// Look up the interface to be configured
//
EnterCriticalSection(&NatInterfaceLock); if (!(Interfacep = NatpLookupInterface(Index, NULL))) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatConfigureInterface: interface %d not found", Index ); NH_FREE(Info); return ERROR_NO_SUCH_INTERFACE; }
//
// See if the configuration changed
//
if ((Size == FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + Interfacep->Info->Header.Size) && memcmp(InterfaceInfo, Interfacep->Info, Size) == 0 ) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatConfigureInterface: no change to interface %d configuration", Index ); NH_FREE(Info); return NO_ERROR; }
//
// See if the interface is bound;
// if so we need to update the kernel-mode driver's configuration.
//
if (!NAT_INTERFACE_BOUND(Interfacep)) { status = STATUS_SUCCESS; } else { HANDLE WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
if (WaitEvent != NULL) { Info->Index = Interfacep->AdapterIndex;
//
// Attempt to configure the interface
//
status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_SET_INTERFACE_INFO, (PVOID)Info, Size, NULL, 0 ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; } CloseHandle(WaitEvent); } else { status = STATUS_UNSUCCESSFUL; NhTrace( TRACE_FLAG_NAT, "NatConfigureInterface: CreateEvent failed [%d]", GetLastError() ); } }
if (!NT_SUCCESS(status)) { NH_FREE(Info); NhTrace( TRACE_FLAG_NAT, "NatConfigureInterface: status %08x setting interface info", status ); Error = RtlNtStatusToDosError(status); NhErrorLog( IP_NAT_LOG_IOCTL_FAILED, Error, "" ); } else { Error = NO_ERROR;
//
// Update proxy ARP entries for LAN interfaces
//
if (NAT_INTERFACE_BOUND(Interfacep) && Interfacep->Type == ROUTER_IF_TYPE_DEDICATED ) { NatUpdateProxyArp(Interfacep, FALSE); }
if (Interfacep->Info) { NH_FREE(Interfacep->Info); } Interfacep->Info = Info;
if (NAT_INTERFACE_BOUND(Interfacep) && Interfacep->Type == ROUTER_IF_TYPE_DEDICATED ) { NatUpdateProxyArp(Interfacep, TRUE); } }
LeaveCriticalSection(&NatInterfaceLock);
if (NT_SUCCESS(status)) { if (InterfaceInfo->Flags & IP_NAT_INTERFACE_FLAGS_BOUNDARY) { NhSignalNatInterface( Index, TRUE ); } else { NhSignalNatInterface( Index, FALSE ); } }
return Error;
} // NatConfigureInterface
ULONG NatCreateInterface( ULONG Index, NET_INTERFACE_TYPE Type, PIP_NAT_INTERFACE_INFO InterfaceInfo )
/*++
Routine Description:
This routine is invoked to create an interface with the NAT driver.
Arguments:
Index - the index of the new interface
InterfaceInfo - the configuration for the new interface
Return Value:
ULONG - Win32 status code.
--*/
{ ULONG Error; PIP_NAT_INTERFACE_INFO Info; PLIST_ENTRY InsertionPoint; PNAT_INTERFACE Interfacep; IO_STATUS_BLOCK IoStatus; ULONG Size; NTSTATUS status; ROUTER_INTERFACE_TYPE IfType;
PROFILE("NatCreateInterface");
if (!InterfaceInfo) { NhTrace( TRACE_FLAG_NAT, "NatCreateInterface: no interface info for %d", Index ); return ERROR_INVALID_PARAMETER; }
//
// Check for the interface in our table
//
EnterCriticalSection(&NatInterfaceLock); if (NatpLookupInterface(Index, &InsertionPoint)) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatCreateInterface: interface %d already exists", Index ); return ERROR_INTERFACE_ALREADY_EXISTS; }
//
// Allocate a new interface
//
Interfacep = reinterpret_cast<PNAT_INTERFACE>(NH_ALLOCATE(sizeof(NAT_INTERFACE)));
if (!Interfacep) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatCreateInterface: error allocating interface" ); NhErrorLog( IP_NAT_LOG_ALLOCATION_FAILED, 0, "%d", sizeof(NAT_INTERFACE) ); return ERROR_NOT_ENOUGH_MEMORY; }
//
// Make a copy of the information
//
Size = FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + InterfaceInfo->Header.Size;
Info = (PIP_NAT_INTERFACE_INFO)NH_ALLOCATE(Size);
if (!Info) { LeaveCriticalSection(&NatInterfaceLock); NH_FREE(Interfacep); NhTrace( TRACE_FLAG_NAT, "NatCreateInterface: error allocating copy of configuration" ); return ERROR_NOT_ENOUGH_MEMORY; }
CopyMemory( Info, InterfaceInfo, Size );
//
// Initialize the new interface
//
ZeroMemory(Interfacep, sizeof(*Interfacep));
Interfacep->Index = Index; Interfacep->AdapterIndex = (ULONG)-1; Interfacep->Type = IfType = ((Type == PERMANENT) ? ROUTER_IF_TYPE_DEDICATED : ROUTER_IF_TYPE_FULL_ROUTER); Interfacep->Info = Info; InsertTailList(InsertionPoint, &Interfacep->Link);
LeaveCriticalSection(&NatInterfaceLock);
if (InterfaceInfo->Flags & IP_NAT_INTERFACE_FLAGS_BOUNDARY) { NhSignalNatInterface( Index, TRUE ); } else { NhSignalNatInterface( Index, FALSE ); }
return NO_ERROR;
} // NatCreateInterface
ULONG NatCreateTicket( ULONG InterfaceIndex, UCHAR Protocol, USHORT PublicPort, ULONG PublicAddress, USHORT PrivatePort, ULONG PrivateAddress )
/*++
Routine Description:
This routine is invoked to add a ticket (static port mapping) to an interface.
Arguments:
InterfaceIndex - the interface to which to add the ticket
Protocol, PublicPort, PublicAddress, PrivatePort, PrivateAddress - describes the ticket to be created
Return Value:
ULONG - Win32 status code.
--*/
{ IP_NAT_CREATE_TICKET CreateTicket; ULONG Error; IO_STATUS_BLOCK IoStatus; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatCreateTicket");
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { NhTrace( TRACE_FLAG_NAT, "NatCreateTicket: CreateEvent failed [%d]", GetLastError() ); return ERROR_NOT_ENOUGH_MEMORY; }
CreateTicket.InterfaceIndex = InterfaceIndex; CreateTicket.PortMapping.Protocol = Protocol; CreateTicket.PortMapping.PublicPort = PublicPort; CreateTicket.PortMapping.PublicAddress = PublicAddress; CreateTicket.PortMapping.PrivatePort = PrivatePort; CreateTicket.PortMapping.PrivateAddress = PrivateAddress;
EnterCriticalSection(&NatInterfaceLock);
status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_CREATE_TICKET, (PVOID)&CreateTicket, sizeof(CreateTicket), NULL, 0 );
LeaveCriticalSection(&NatInterfaceLock); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; }
if (NT_SUCCESS(status)) { Error = NO_ERROR; } else { Error = RtlNtStatusToDosError(status); NhTrace( TRACE_FLAG_NAT, "NatCreateTicket: Ioctl = %d", Error ); } CloseHandle(WaitEvent); return Error; } // NatCreateTicket
ULONG NatDeleteInterface( ULONG Index )
/*++
Routine Description:
This routine is invoked to remove an interface from the NAT.
Arguments:
Index - the interface to be removed
Return Value:
ULONG - Win32 status code.
--*/
{ ULONG Error; PNAT_INTERFACE Interfacep; IO_STATUS_BLOCK IoStatus; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatDeleteInterface");
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { NhTrace( TRACE_FLAG_NAT, "NatDeleteInterface: CreateEvent failed [%d]", GetLastError() ); return ERROR_NOT_ENOUGH_MEMORY; }
//
// Retrieve the interface to be deleted.
//
EnterCriticalSection(&NatInterfaceLock); if (!(Interfacep = NatpLookupInterface(Index, NULL))) { LeaveCriticalSection(&NatInterfaceLock); CloseHandle(WaitEvent); NhTrace( TRACE_FLAG_NAT, "NatDeleteInterface: interface %d not found", Index ); return ERROR_NO_SUCH_INTERFACE; }
Error = NO_ERROR; if (NAT_INTERFACE_BOUND(Interfacep)) {
//
// Delete the interface from the kernel-mode driver
//
status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_DELETE_INTERFACE, (PVOID)&Interfacep->AdapterIndex, sizeof(ULONG), NULL, 0 ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; }
if (NT_SUCCESS(status)) { Error = NO_ERROR; } else { Error = RtlNtStatusToDosError(status); NhErrorLog( IP_NAT_LOG_IOCTL_FAILED, Error, "" ); } } CloseHandle(WaitEvent);
//
// Remove the interface from our list
//
RemoveEntryList(&Interfacep->Link); if (Interfacep->Info) { NH_FREE(Interfacep->Info); } NH_FREE(Interfacep);
LeaveCriticalSection(&NatInterfaceLock);
NhSignalNatInterface( Index, FALSE );
return Error;
} // NatDeleteInterface
ULONG NatDeleteTicket( ULONG InterfaceIndex, UCHAR Protocol, USHORT PublicPort, ULONG PublicAddress, USHORT PrivatePort, ULONG PrivateAddress )
/*++
Routine Description:
This routine is invoked to remove a ticket (static port mapping) from an interface.
Arguments:
InterfaceIndex - the interface from which to remove the ticket
Protocol, PublicPort, PublicAddress, PrivatePort, PrivateAddress - describes the ticket to be deleted
Return Value:
ULONG - Win32 status code.
--*/
{ IP_NAT_CREATE_TICKET DeleteTicket; ULONG Error; IO_STATUS_BLOCK IoStatus; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatDeleteTicket");
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { NhTrace( TRACE_FLAG_NAT, "NatDeleteTicket: CreateEvent failed [%d]", GetLastError() ); return ERROR_NOT_ENOUGH_MEMORY; }
DeleteTicket.InterfaceIndex = InterfaceIndex; DeleteTicket.PortMapping.Protocol = Protocol; DeleteTicket.PortMapping.PublicPort = PublicPort; DeleteTicket.PortMapping.PublicAddress = PublicAddress; DeleteTicket.PortMapping.PrivatePort = PrivatePort; DeleteTicket.PortMapping.PrivateAddress = PrivateAddress;
EnterCriticalSection(&NatInterfaceLock);
status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_DELETE_TICKET, (PVOID)&DeleteTicket, sizeof(DeleteTicket), NULL, 0 );
LeaveCriticalSection(&NatInterfaceLock); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; }
if (NT_SUCCESS(status)) { Error = NO_ERROR; } else { Error = RtlNtStatusToDosError(status); NhTrace( TRACE_FLAG_NAT, "NatDeleteTicket: Ioctl = %d", Error ); } CloseHandle(WaitEvent); return Error; } // NatDeleteTicket
ULONG NatGetInterfaceCharacteristics( ULONG Index )
/*++
Routine Description:
This routine is invoked to determine whether the given interface: 1) Is a NAT boundary interface 2) Is a NAT private interface 3) Has the firewall enabled
Note that this routine may be invoked even when the NAT is neither installed nor running; it operates as expected, since the interface list and lock are always initialized in 'DllMain'.
Arguments:
Index - the interface in question
IsNatInterface - optionally set to TRUE if the given index is at all a NAT interface.
Return Value:
BOOLEAN - TRUE if the interface is a NAT boundary interface, FALSE otherwise.
--*/
{ ULONG Result = 0; PNAT_INTERFACE Interfacep; PROFILE("NatGetInterfaceCharacteristics");
EnterCriticalSection(&NatInterfaceLock); if (!(Interfacep = NatpLookupInterface(Index, NULL))) { LeaveCriticalSection(&NatInterfaceLock); return Result; }
if (Interfacep->Info && (Interfacep->Info->Flags & IP_NAT_INTERFACE_FLAGS_FW)) { Result = NAT_IF_CHAR_FW; }
if (Interfacep->Info && (Interfacep->Info->Flags & IP_NAT_INTERFACE_FLAGS_BOUNDARY)) { Result |= NAT_IF_CHAR_BOUNDARY; } else if (!NAT_IFC_FW(Result)) {
//
// As the interface isn't public and isn't firewalled, it must
// be a private interface (or we wouldn't have a record of it).
//
Result |= NAT_IF_CHAR_PRIVATE; }
LeaveCriticalSection(&NatInterfaceLock); return Result; } // NatGetInterfaceCharacteristics
VOID NatInstallApplicationSettings( VOID )
/*++
Routine Description:
This routine is invoked to update the application settings (i.e., dynamic tickets) stored with the kernel-mode translation module.
Arguments:
none
Return Value:
none.
--*/
{ PNAT_APP_ENTRY pAppEntry; ULONG Count; PIP_NAT_CREATE_DYNAMIC_TICKET CreateTicket; IO_STATUS_BLOCK IoStatus; ULONG Length; PLIST_ENTRY Link; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatInstallApplicationSettings");
//
// Install a dynamic ticket for each entry in the applications list
//
EnterCriticalSection(&NatInterfaceLock); EnterCriticalSection(&NhLock);
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { LeaveCriticalSection(&NhLock); LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatInstallSharedAccessSettings: CreateEvent failed [%d]", GetLastError() ); return; }
for (Link = NhApplicationSettingsList.Flink; Link != &NhApplicationSettingsList; Link = Link->Flink) {
//
// Each 'application' has a list of 'responses' which specify
// the ports on which response-sessions are expected.
// Enumerate the responses and allocate a ticket-structure
// large enough to hold the list as an array.
//
pAppEntry = CONTAINING_RECORD(Link, NAT_APP_ENTRY, Link);
Length = pAppEntry->ResponseCount * sizeof(CreateTicket->ResponseArray[0]) + FIELD_OFFSET(IP_NAT_CREATE_DYNAMIC_TICKET, ResponseArray);
if (!(CreateTicket = reinterpret_cast<PIP_NAT_CREATE_DYNAMIC_TICKET>( NH_ALLOCATE(Length) ))) { break; }
//
// Fill in the ticket structure from the application entry
// and its list of response-entries.
//
CreateTicket->Protocol = pAppEntry->Protocol; CreateTicket->Port = pAppEntry->Port; CreateTicket->ResponseCount = pAppEntry->ResponseCount; for (Count = 0; Count < pAppEntry->ResponseCount; Count++) { CreateTicket->ResponseArray[Count].Protocol = pAppEntry->ResponseArray[Count].ucIPProtocol; CreateTicket->ResponseArray[Count].StartPort = pAppEntry->ResponseArray[Count].usStartPort; CreateTicket->ResponseArray[Count].EndPort = pAppEntry->ResponseArray[Count].usEndPort; }
//
// Install the dynamic ticket for this application, and continue.
//
status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_CREATE_DYNAMIC_TICKET, (PVOID)CreateTicket, Length, NULL, 0 ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; } NH_FREE(CreateTicket); }
LeaveCriticalSection(&NhLock); LeaveCriticalSection(&NatInterfaceLock);
CloseHandle(WaitEvent); } // NatInstallApplicationSettings
BOOLEAN NatIsBoundaryInterface( ULONG Index, PBOOLEAN IsNatInterface OPTIONAL )
/*++
Routine Description:
This routine is invoked to determine whether the given interface has the NAT enabled and is marked as a boundary interface. Note that this routine may be invoked even when the NAT is neither installed nor running; it operates as expected, since the interface list and lock are always initialized in 'DllMain'.
Arguments:
Index - the interface in question
IsNatInterface - optionally set to TRUE if the given index is at all a NAT interface.
Return Value:
BOOLEAN - TRUE if the interface is a NAT boundary interface, FALSE otherwise.
--*/
{ PNAT_INTERFACE Interfacep; PROFILE("NatIsBoundaryInterface");
EnterCriticalSection(&NatInterfaceLock); if (!(Interfacep = NatpLookupInterface(Index, NULL))) { LeaveCriticalSection(&NatInterfaceLock); if (IsNatInterface) { *IsNatInterface = FALSE; } return FALSE; }
if (IsNatInterface) { *IsNatInterface = TRUE; }
if (Interfacep->Info && (Interfacep->Info->Flags & IP_NAT_INTERFACE_FLAGS_BOUNDARY)) { LeaveCriticalSection(&NatInterfaceLock); return TRUE; } LeaveCriticalSection(&NatInterfaceLock); return FALSE;
} // NatIsBoundaryInterface
PNAT_INTERFACE NatpLookupInterface( ULONG Index, OUT PLIST_ENTRY* InsertionPoint OPTIONAL )
/*++
Routine Description:
This routine is called to retrieve an interface given its index.
Arguments:
Index - the index of the interface to be retrieved
InsertionPoint - if the interface is not found, optionally receives the point where the interface would be inserted in the interface list
Return Value:
PNAT_INTERFACE - the interface, if found; otherwise, NULL.
Environment:
Invoked internally from an arbitrary context, with 'NatInterfaceLock' held by caller.
--*/
{ PNAT_INTERFACE Interfacep; PLIST_ENTRY Link;
PROFILE("NatpLookupInterface");
for (Link = NatInterfaceList.Flink; Link != &NatInterfaceList; Link = Link->Flink) { Interfacep = CONTAINING_RECORD(Link, NAT_INTERFACE, Link); if (Index > Interfacep->Index) { continue; } else if (Index < Interfacep->Index) { break; } return Interfacep; }
if (InsertionPoint) { *InsertionPoint = Link; }
return NULL;
} // NatpLookupInterface
ULONG NatQueryInterface( ULONG Index, PIP_NAT_INTERFACE_INFO InterfaceInfo, PULONG InterfaceInfoSize )
/*++
Routine Description:
This routine is invoked to retrieve the information for a NAT interface.
Arguments:
Index - the interface whose information is to be queried
InterfaceInfo - receives the information
InterfaceInfoSize - receives the information size
Return Value:
ULONG - Win32 status code.
--*/
{ ULONG Error; PNAT_INTERFACE Interfacep; ULONG Size;
PROFILE("NatQueryInterface");
//
// Look up the interface to be queried
//
EnterCriticalSection(&NatInterfaceLock); if (!(Interfacep = NatpLookupInterface(Index, NULL))) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatQueryInterface: interface %d not found", Index ); return ERROR_NO_SUCH_INTERFACE; }
//
// Compute the required size
//
Size = FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + Interfacep->Info->Header.Size;
if (Size >= *InterfaceInfoSize) { *InterfaceInfoSize = Size; Error = ERROR_INSUFFICIENT_BUFFER; } else { *InterfaceInfoSize = Size; CopyMemory( InterfaceInfo, Interfacep->Info, Size ); Error = NO_ERROR; }
LeaveCriticalSection(&NatInterfaceLock);
return Error;
} // NatQueryInterface
ULONG NatQueryInterfaceMappingTable( ULONG Index, PIP_NAT_ENUMERATE_SESSION_MAPPINGS EnumerateTable, PULONG EnumerateTableSize )
/*++
Routine Description:
This routine is invoked to retrieve the session mappings for an interface.
Arguments:
EnumerateTable - receives the enumerated mappings
EnumerateTableSize - indicates the size of 'EnumerateTable'
Return Value:
ULONG - Win32 error code.
--*/
{ IP_NAT_ENUMERATE_SESSION_MAPPINGS Enumerate; PNAT_INTERFACE Interfacep; IO_STATUS_BLOCK IoStatus; ULONG RequiredSize; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatQueryInterfaceMappingTable");
EnterCriticalSection(&NatInterfaceLock); if (!(Interfacep = NatpLookupInterface(Index, NULL))) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatQueryInterfaceMappingTable: interface %d not found", Index ); return ERROR_NO_SUCH_INTERFACE; }
if (!NAT_INTERFACE_BOUND(Interfacep)) {
//
// The interface is not bound, so there aren't any mappings.
// Indicate zero mappings in the caller's request-buffer.
//
LeaveCriticalSection(&NatInterfaceLock);
RequiredSize = FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable[0]);
if (*EnumerateTableSize < RequiredSize) { *EnumerateTableSize = RequiredSize; return ERROR_INSUFFICIENT_BUFFER; }
EnumerateTable->Index = Index; EnumerateTable->EnumerateContext[0] = 0; EnumerateTable->EnumerateCount = 0; *EnumerateTableSize = RequiredSize;
return NO_ERROR; }
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatQueryInterfaceMappingTable: CreateEvent failed [%d]", GetLastError() ); return ERROR_NOT_ENOUGH_MEMORY; }
//
// Determine the amount of space required
//
Enumerate.Index = Interfacep->AdapterIndex; Enumerate.EnumerateCount = 0; Enumerate.EnumerateContext[0] = 0; status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_GET_INTERFACE_MAPPING_TABLE, (PVOID)&Enumerate, sizeof(Enumerate), (PVOID)&Enumerate, sizeof(Enumerate) ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; } if (!NT_SUCCESS(status)) { CloseHandle(WaitEvent); LeaveCriticalSection(&NatInterfaceLock); *EnumerateTableSize = 0; return RtlNtStatusToDosError(status); }
RequiredSize = FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable[0]) + Enumerate.EnumerateTotalHint * sizeof(IP_NAT_SESSION_MAPPING);
//
// If the caller doesn't have enough space for all these mappings, fail
//
if (*EnumerateTableSize < RequiredSize) { CloseHandle(WaitEvent); LeaveCriticalSection(&NatInterfaceLock); *EnumerateTableSize = RequiredSize + 5 * sizeof(IP_NAT_SESSION_MAPPING); return ERROR_INSUFFICIENT_BUFFER; }
//
// Attempt to read the mappings
//
Enumerate.Index = Interfacep->AdapterIndex; Enumerate.EnumerateCount = 0; Enumerate.EnumerateContext[0] = 0; status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_GET_INTERFACE_MAPPING_TABLE, (PVOID)&Enumerate, sizeof(Enumerate), (PVOID)EnumerateTable, *EnumerateTableSize ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; } CloseHandle(WaitEvent); LeaveCriticalSection(&NatInterfaceLock);
EnumerateTable->Index = Index; *EnumerateTableSize = FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable[0]) + EnumerateTable->EnumerateCount * sizeof(IP_NAT_SESSION_MAPPING);
return NT_SUCCESS(status) ? NO_ERROR : RtlNtStatusToDosError(status);
} // NatQueryInterfaceMappingTable
ULONG NatQueryMappingTable( PIP_NAT_ENUMERATE_SESSION_MAPPINGS EnumerateTable, PULONG EnumerateTableSize )
/*++
Routine Description:
This routine is invoked to retrieve the session mappings for an interface.
Arguments:
EnumerateTable - receives the enumerated mappings
EnumerateTableSize - indicates the size of 'EnumerateTable'
Return Value:
ULONG - Win32 error code.
--*/
{ IP_NAT_ENUMERATE_SESSION_MAPPINGS Enumerate; IO_STATUS_BLOCK IoStatus; ULONG RequiredSize; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatQueryMappingTable");
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { NhTrace( TRACE_FLAG_NAT, "NatQueryMappingTable: CreateEvent failed [%d]", GetLastError() ); return ERROR_NOT_ENOUGH_MEMORY; }
EnterCriticalSection(&NatInterfaceLock);
//
// Determine the amount of space required
//
Enumerate.EnumerateCount = 0; Enumerate.EnumerateContext[0] = 0; status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_GET_MAPPING_TABLE, (PVOID)&Enumerate, sizeof(Enumerate), (PVOID)&Enumerate, sizeof(Enumerate) ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; }
if (!NT_SUCCESS(status)) { LeaveCriticalSection(&NatInterfaceLock); CloseHandle(WaitEvent); *EnumerateTableSize = 0; return RtlNtStatusToDosError(status); }
RequiredSize = FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable[0]) + Enumerate.EnumerateTotalHint * sizeof(IP_NAT_SESSION_MAPPING);
//
// If the caller doesn't have enough space for all these mappings, fail
//
if (*EnumerateTableSize < RequiredSize) { LeaveCriticalSection(&NatInterfaceLock); CloseHandle(WaitEvent); *EnumerateTableSize = RequiredSize + 5 * sizeof(IP_NAT_SESSION_MAPPING); return ERROR_INSUFFICIENT_BUFFER; }
//
// Attempt to read the mappings
//
Enumerate.EnumerateCount = 0; Enumerate.EnumerateContext[0] = 0; status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_GET_MAPPING_TABLE, (PVOID)&Enumerate, sizeof(Enumerate), (PVOID)EnumerateTable, *EnumerateTableSize ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; }
CloseHandle(WaitEvent);
LeaveCriticalSection(&NatInterfaceLock);
EnumerateTable->Index = (ULONG)-1; *EnumerateTableSize = FIELD_OFFSET(IP_NAT_ENUMERATE_SESSION_MAPPINGS, EnumerateTable[0]) + EnumerateTable->EnumerateCount * sizeof(IP_NAT_SESSION_MAPPING);
return NT_SUCCESS(status) ? NO_ERROR : RtlNtStatusToDosError(status);
} // NatQueryMappingTable
ULONG NatQueryStatisticsInterface( ULONG Index, PIP_NAT_INTERFACE_STATISTICS InterfaceStatistics, PULONG InterfaceStatisticsSize )
/*++
Routine Description:
This routine is invoked to retrieve the statistics for a NAT interface.
Arguments:
Index - the index of the interface whose statistics are to be retrieved
Return Value:
ULONG - Win32 error code.
--*/
{ PNAT_INTERFACE Interfacep; IO_STATUS_BLOCK IoStatus; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatQueryStatisticsInterface");
//
// Look up the interface to be queried
//
EnterCriticalSection(&NatInterfaceLock); if (!(Interfacep = NatpLookupInterface(Index, NULL))) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatQueryStatisticsInterface: interface %d not found", Index ); return ERROR_NO_SUCH_INTERFACE; }
//
// If the interface is not bound, supply zero statistics.
//
if (!NAT_INTERFACE_BOUND(Interfacep)) {
LeaveCriticalSection(&NatInterfaceLock);
if (*InterfaceStatisticsSize < sizeof(IP_NAT_INTERFACE_STATISTICS)) { *InterfaceStatisticsSize = sizeof(IP_NAT_INTERFACE_STATISTICS); return ERROR_INSUFFICIENT_BUFFER; }
*InterfaceStatisticsSize = sizeof(IP_NAT_INTERFACE_STATISTICS); ZeroMemory(InterfaceStatistics, sizeof(IP_NAT_INTERFACE_STATISTICS));
return NO_ERROR; }
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatQueryStatisticsInterface: CreateEvent failed [%d]", GetLastError() ); return ERROR_NOT_ENOUGH_MEMORY; }
//
// Attempt to read the statistics for the interface
//
status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_GET_INTERFACE_STATISTICS, (PVOID)&Interfacep->AdapterIndex, sizeof(ULONG), (PVOID)InterfaceStatistics, *InterfaceStatisticsSize ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; }
CloseHandle(WaitEvent);
LeaveCriticalSection(&NatInterfaceLock);
if (NT_SUCCESS(status) && IoStatus.Information > *InterfaceStatisticsSize) { *InterfaceStatisticsSize = (ULONG)IoStatus.Information; return ERROR_INSUFFICIENT_BUFFER; }
*InterfaceStatisticsSize = (ULONG)IoStatus.Information;
return NT_SUCCESS(status) ? NO_ERROR : RtlNtStatusToDosError(status);
} // NatQueryStatisticsInterface
VOID NatRemoveApplicationSettings( VOID )
/*++
Routine Description:
This routine is invoked to remove the advanced application settings (i.e., dynamic tickets), and supply the settings to the kernel-mode translation module.
Arguments:
none.
Return Value:
none.
--*/
{ PNAT_APP_ENTRY pAppEntry; IP_NAT_DELETE_DYNAMIC_TICKET DeleteTicket; IO_STATUS_BLOCK IoStatus; PLIST_ENTRY Link; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatRemoveApplicationSettings");
//
// Each 'application' entry in the shared access settings
// corresponds to a dynamic ticket for the kernel-mode translator.
// We begin by removing the dynamic tickets for the old settings, if any,
// and then we free the old settings in preparation for reloading.
//
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { NhTrace( TRACE_FLAG_NAT, "NatRemoveSharedAccessSettings: CreateEvent failed [%d]", GetLastError() ); return; }
EnterCriticalSection(&NatInterfaceLock); EnterCriticalSection(&NhLock); for (Link = NhApplicationSettingsList.Flink; Link != &NhApplicationSettingsList; Link = Link->Flink) { pAppEntry = CONTAINING_RECORD(Link, NAT_APP_ENTRY, Link); DeleteTicket.Protocol = pAppEntry->Protocol; DeleteTicket.Port = pAppEntry->Port; status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_DELETE_DYNAMIC_TICKET, (PVOID)&DeleteTicket, sizeof(DeleteTicket), NULL, 0 ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; } } LeaveCriticalSection(&NhLock); LeaveCriticalSection(&NatInterfaceLock);
CloseHandle(WaitEvent); } // NatRemoveSharedAccessSettings
ULONG NatUnbindInterface( ULONG Index, PNAT_INTERFACE Interfacep )
/*++
Routine Description:
This routine is invoked to remove a binding from the NAT.
Arguments:
Index - the interface to be unbound
Interfacep - optionally supplies the interface-structure to be unbound (See 'NATCONN.C' which passes in a static interface-structure).
Return Value:
ULONG - Win32 status code.
--*/
{ ULONG Error; IO_STATUS_BLOCK IoStatus; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatUnbindInterface");
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { NhTrace( TRACE_FLAG_NAT, "NatUnbindInterface: CreateEvent failed [%d]", GetLastError() ); return ERROR_NOT_ENOUGH_MEMORY; }
//
// Retrieve the interface to be unbound.
//
EnterCriticalSection(&NatInterfaceLock); if (!Interfacep && !(Interfacep = NatpLookupInterface(Index, NULL))) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatUnbindInterface: interface %d not found", Index ); return ERROR_NO_SUCH_INTERFACE; }
//
// Make sure the interface is not already unbound
//
if (!NAT_INTERFACE_BOUND(Interfacep)) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatUnbindInterface: interface %d already unbound", Index ); return ERROR_ADDRESS_NOT_ASSOCIATED; }
Interfacep->Flags &= ~NAT_INTERFACE_FLAG_BOUND;
if (Interfacep->Type == ROUTER_IF_TYPE_DEDICATED) { NatUpdateProxyArp(Interfacep, FALSE); }
//
// Remove the interface from the kernel-mode driver
//
status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_DELETE_INTERFACE, (PVOID)&Interfacep->AdapterIndex, sizeof(ULONG), NULL, 0 ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; } LeaveCriticalSection(&NatInterfaceLock); CloseHandle(WaitEvent);
Error = NT_SUCCESS(status) ? NO_ERROR : RtlNtStatusToDosError(status);
if (Error) { NhErrorLog( IP_NAT_LOG_IOCTL_FAILED, Error, "" ); }
return Error;
} // NatUnbindInterface
ULONG NatLookupPortMappingAdapter( ULONG AdapterIndex, UCHAR Protocol, ULONG PublicAddress, USHORT PublicPort, PIP_NAT_PORT_MAPPING PortMappingp )
/*++
Routine Description:
This routine is invoked to find a mapping that matches the given adapter, protocol, public address and public port number. The routine tries to match both port and address mapping.
Arguments:
AdapterIndex - the adapter to be looked up Protocol - protocol used to match a mapping PublicAddress - public address used to match a mapping PublicPort - public port number used to match a mapping PortMappingp - pointer to a caller-supplied storage to save the mapping if found
Return Value:
ULONG - Win32 status code.
--*/
{ IP_NAT_CREATE_TICKET LookupTicket; ULONG Error; IO_STATUS_BLOCK IoStatus; NTSTATUS status; HANDLE WaitEvent;
PROFILE("NatLookupPortMappingAdapter");
Error = NO_ERROR;
WaitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (WaitEvent == NULL) { NhTrace( TRACE_FLAG_NAT, "NatLookupPortMappingAdapter:" " CreateEvent failed [%d] for adapter %d", GetLastError(), AdapterIndex ); return ERROR_NOT_ENOUGH_MEMORY; }
LookupTicket.InterfaceIndex = AdapterIndex; LookupTicket.PortMapping.Protocol = Protocol; LookupTicket.PortMapping.PublicPort = PublicPort; LookupTicket.PortMapping.PublicAddress = PublicAddress; LookupTicket.PortMapping.PrivatePort = 0; LookupTicket.PortMapping.PrivateAddress = 0;
status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_LOOKUP_TICKET, (PVOID)&LookupTicket, sizeof(LookupTicket), (PVOID)PortMappingp, sizeof(*PortMappingp) ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; }
if (!NT_SUCCESS(status)) { NhTrace( TRACE_FLAG_NAT, "NatLookupPortMappingAdapter:" " status %08x getting info for adapter %d", status, AdapterIndex ); Error = RtlNtStatusToDosError(status); }
CloseHandle(WaitEvent);
return Error; }
|