/*++ 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 #include #include // // 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 NatExpandWildcardMappings( IN PIP_NAT_INTERFACE_INFO InInfo, IN PIP_ADAPTER_BINDING_INFO BindingInfo, OUT PIP_NAT_INTERFACE_INFO *ExpandedInfo ); 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; PIP_NAT_INTERFACE_INFO ExpandedInfo = NULL; ULONG ExpandedSize; 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( 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; } // // If a mapping with // public ip address = 0 // private ip address = 127.0.0.1 // exists, expand that mapping to one mapping for each of the ipaddresses // bound to the interface. // Error = NatExpandWildcardMappings( Interfacep->Info, BindingInfo, &ExpandedInfo); if ( Error != NO_ERROR ) { ULONG AdapterIndex = Interfacep->AdapterIndex; LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatBindInterface: Failed to expand wildcard mappings. Error %d", Error ); NhErrorLog( IP_NAT_LOG_EXPANSION_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; } // // Now set its configuration // ExpandedInfo->Index = Interfacep->AdapterIndex; ExpandedSize = FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + ExpandedInfo->Header.Size; status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_SET_INTERFACE_INFO, (PVOID)ExpandedInfo, ExpandedSize, NULL, 0 ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; } // // If new Interface Info was allocated, free it // if ( ExpandedInfo != Interfacep->Info ) { NH_FREE(ExpandedInfo); ExpandedInfo = NULL; } 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 = NO_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) { DWORD ExpandedSize = 0; PIP_ADAPTER_BINDING_INFO BindingInfo; PIP_NAT_INTERFACE_INFO ExpandedInfo = NULL; // // If both, address and port translation are disabled, // and a mapping with // public ip address = 0 // private ip address = 127.0.0.1 // exists, expand that mapping to one mapping for each of // the ipaddresses bound to the interface. // BindingInfo = NhQueryBindingInformation(Interfacep->AdapterIndex); if ( BindingInfo == NULL ) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatConfigureInterface: Failed to Query Binding " "Information. Error %d", Error ); NhErrorLog( IP_NAT_LOG_EXPANSION_FAILED, Error, "" ); NH_FREE(Info); return Error; } Error = NatExpandWildcardMappings( Info, BindingInfo, &ExpandedInfo ); if ( Error != NO_ERROR ) { LeaveCriticalSection(&NatInterfaceLock); NhTrace( TRACE_FLAG_NAT, "NatConfigureInterface: Failed to expand wildcard " "mappings. Error %d", Error ); NhErrorLog( IP_NAT_LOG_EXPANSION_FAILED, Error, "" ); NH_FREE(Info); NH_FREE(BindingInfo); return Error; } NH_FREE(BindingInfo); BindingInfo = NULL; ExpandedInfo->Index = Info->Index = Interfacep->AdapterIndex; ExpandedSize = FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + ExpandedInfo->Header.Size; // // Attempt to configure the interface // status = NtDeviceIoControlFile( NatFileHandle, WaitEvent, NULL, NULL, &IoStatus, IOCTL_IP_NAT_SET_INTERFACE_INFO, (PVOID)ExpandedInfo, ExpandedSize, NULL, 0 ); if (status == STATUS_PENDING) { WaitForSingleObject(WaitEvent, INFINITE); status = IoStatus.Status; } CloseHandle(WaitEvent); // // If new Interface Info was allocated during the call to // NatExpandWildcardMappings, free it // if ( ExpandedInfo != Info ) { NH_FREE(ExpandedInfo); ExpandedInfo = NULL; } } 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(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( 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; } ULONG NatExpandWildcardMappings( IN PIP_NAT_INTERFACE_INFO InInfo, IN PIP_ADAPTER_BINDING_INFO BindingInfo, OUT PIP_NAT_INTERFACE_INFO *ExpandedInfo ) /*++ Routine Description: This routine is invoked to expand any wildcard(address) mappings, into one mapping per ipaddress bound to the interface Assumes that information pointed to by InInfo and BindingInfo does not change during the duration of this function Arguments: InInfo - Pointer to the set of mappings to be expanded ExpandedInfo - On successful completion, ExpandedInfo contains a pointer to the expanded set of mappings. If there were mappings that needed expansion, the function will allocate the memory for this new set of mappings. If no expansion is required, ExpandedInfo will point to the same location as InInfo. Return Value: ULONG - Win32 status code. If there are no wildcard mappings, it returns ERROR_EXTENDED_ERROR In case of success: NO_ERROR is returned --*/ { ULONG Error; ULONG Size; DWORD j, k; DWORD MappingSize; DWORD CountOldMappings, CountNewMappings, CountWildcardMappings; DWORD tempCount; PIP_NAT_PORT_MAPPING OldMappings = NULL; PIP_NAT_PORT_MAPPING NewMappings = NULL; PIP_NAT_INTERFACE_INFO NewInterfaceInfo = NULL; PRTR_INFO_BLOCK_HEADER NewHeader = NULL; Error = NO_ERROR; // // If a mapping with // public ip address = 0 // private ip address = 127.0.0.1 // exists, expand that mapping to one mapping for each of the ipaddresses // bound to the interface. // NewInterfaceInfo = InInfo; do { // // We don't do any expansion in case of a private interface // if ( InInfo->Flags == 0 ) { break; } // // If no port mappings exist, break out // Error = MprInfoBlockFind( (LPVOID)&(InInfo->Header), IP_NAT_PORT_MAPPING_TYPE, &MappingSize, &CountOldMappings, (LPBYTE *)&OldMappings); if ( Error != NO_ERROR ) { if ( Error == ERROR_NOT_FOUND ) { Error = NO_ERROR; } break; } // // Count how many mappings need to be expanded // CountWildcardMappings = 0; for ( j = 0; j < CountOldMappings; j++ ) { if ( IS_WILDCARD_MAPPING(&(OldMappings[j])) ) { CountWildcardMappings++; } } // // If there are no wildcard mappings // return ERROR_EXTENDED_ERROR // if ( CountWildcardMappings == 0 ) { break; } // // allocate memory to hold the new set of mappings // Number of new mappings will be // // ( (OldMappings - WildcardMappings) + // (WildcardMappings * "no of bound ipaddresses") ) // CountNewMappings = (CountOldMappings + (CountWildcardMappings * (BindingInfo->AddressCount-1))); Size = CountNewMappings * MappingSize; NewMappings = (PIP_NAT_PORT_MAPPING)NH_ALLOCATE(Size); if ( NewMappings == NULL ) { NhTrace( TRACE_FLAG_NAT, "Failed to allocate buffer for expanded mappings. Size: %d", Size ); NhErrorLog( IP_NAT_LOG_ALLOCATION_FAILED, 0, "%d", Size ); // // If we can't get the buffer, forget about expanding the mappings // and go on with life. // Error = ERROR_NOT_ENOUGH_MEMORY; break; } tempCount = 0; for ( j = 0; j < CountOldMappings; j++ ) { if (IS_WILDCARD_MAPPING(&(OldMappings[j]))) { for ( k = 0; k < BindingInfo->AddressCount; k++ ) { CopyMemory(&(NewMappings[tempCount]), &(OldMappings[j]), sizeof(IP_NAT_PORT_MAPPING)); NewMappings[tempCount].PublicAddress = NewMappings[tempCount].PrivateAddress = BindingInfo->Address[k].Address; tempCount++; } } else { CopyMemory(&(NewMappings[tempCount]), &(OldMappings[j]), sizeof(IP_NAT_PORT_MAPPING)); tempCount++; } } if ( tempCount != CountNewMappings ) { NhTrace( TRACE_FLAG_NAT, "NatExpandWildcardMappings: Unexpected number of expanded " "mappings: %d. Expected: %d", tempCount, CountNewMappings ); Error = ERROR_INVALID_DATA; break; } #if DBG { char publicAddress[128]; char privateAddress[128]; NhTrace( TRACE_FLAG_NAT, "NatExpandWildcardMappings: Expanded set of " "mappings. Total: %d\r\n", CountNewMappings ); for ( j = 0; j < CountNewMappings; j++ ) { _snprintf( publicAddress, 127, INET_NTOA(NewMappings[j].PublicAddress) ); _snprintf( privateAddress, 127, INET_NTOA(NewMappings[j].PrivateAddress) ); NhTrace( TRACE_FLAG_NAT, "\tProto: %u, PubPort: %hu, PubAdd: %s, " "PrivPort: %hu, PrivAdd: %s\r\n", NewMappings[j].Protocol & 0xFF, ntohs(NewMappings[j].PublicPort), publicAddress, ntohs(NewMappings[j].PrivatePort), privateAddress ); } } #endif // // Now create the new header // Error = MprInfoBlockSet( &(InInfo->Header), IP_NAT_PORT_MAPPING_TYPE, sizeof(IP_NAT_PORT_MAPPING), CountNewMappings, (LPBYTE)NewMappings, (LPVOID *)&NewHeader); if ( Error != NO_ERROR ) { NhTrace( TRACE_FLAG_NAT, "MprInfoBlockSet failed with error: %d", Error ); break; } // // Allocate space for a new IP_NAT_INTERFACE_INFO struct. // Size = FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + NewHeader->Size; NewInterfaceInfo = (PIP_NAT_INTERFACE_INFO)NH_ALLOCATE(Size); if ( NewInterfaceInfo == NULL ) { NhTrace( TRACE_FLAG_NAT, "Failed to allocate buffer for new NatInterfaceInfo. Size: %d", Size ); NhErrorLog( IP_NAT_LOG_ALLOCATION_FAILED, 0, "%d", Size ); // // If we can't get the buffer, forget about fixing the mappings // and go on with life. // Error = ERROR_NOT_ENOUGH_MEMORY; break; } // // Copy the old IP_NAT_INFTERFACE_INFO. Leave out the old header // CopyMemory( NewInterfaceInfo, InInfo, FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header)); // // Now copy the new header // CopyMemory( ((BYTE *)NewInterfaceInfo + FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header)), NewHeader, NewHeader->Size); } while (FALSE); if ( NewMappings ) { NH_FREE(NewMappings); } if ( NewHeader ) { NH_FREE(NewHeader); } if ( Error == NO_ERROR ) { *ExpandedInfo = NewInterfaceInfo; } else { if ( NewInterfaceInfo && NewInterfaceInfo != InInfo ) { NH_FREE(NewInterfaceInfo); } } return Error; } // NatExpandWildcardMappings