|
|
/*++
Copyright (c) 1998, Microsoft Corporation
Module Name:
natconn.c
Abstract:
This module contains code for the NAT's RAS connection management. This includes * code to support 'shared-access', in which a RAS client-connection serves as the NAT public network. * code to support 'on-demand dialing', in which a routing-failure results in our attempting to establish a dialup connection with the help of the autodial service.
Author:
Abolade Gbadegesin (aboladeg) 2-May-1998
Revision History:
Jonathan Burstein (jonburs) 6-July-2000 Updated to new config APIs
--*/
#include "precomp.h"
#pragma hdrstop
#include <ras.h>
#include <rasuip.h>
#include <raserror.h>
#include <dnsapi.h>
#include "beacon.h"
//
// EXTERNAL DECLARATIONS
//
extern "C" ULONG APIENTRY RasGetEntryHrasconnW( LPCWSTR Phonebook, LPCWSTR EntryName, LPHRASCONN Hrasconn );
extern "C" ULONG SetIpForwardEntryToStack( PMIB_IPFORWARDROW IpForwardRow );
extern "C" ULONG NhpAllocateAndGetInterfaceInfoFromStack( IP_INTERFACE_NAME_INFO** Table, PULONG Count, BOOL SortOutput, HANDLE AllocationHeap, ULONG AllocationFlags );
//
// Notifications
//
HANDLE NatConfigurationChangedEvent = NULL; HANDLE NatpConfigurationChangedWaitHandle = NULL; HANDLE NatConnectionNotifyEvent = NULL; HANDLE NatpConnectionNotifyWaitHandle = NULL; HANDLE NatpEnableRouterEvent = NULL; OVERLAPPED NatpEnableRouterOverlapped; HANDLE NatpEnableRouterWaitHandle = NULL; IO_STATUS_BLOCK NatpRoutingFailureIoStatus; IP_NAT_ROUTING_FAILURE_NOTIFICATION NatpRoutingFailureNotification;
//
// Connection information
//
LIST_ENTRY NatpConnectionList = {NULL, NULL}; ULONG NatpFirewallConnectionCount = 0; BOOLEAN NatpSharedConnectionPresent = FALSE; PCHAR NatpSharedConnectionDomainName = NULL; LONG NatpNextInterfaceIndex = 1;
#define INADDR_LOOPBACK_NO 0x0100007f // 127.0.0.1 in network order
//
// FORWARD DECLARATIONS
//
HRESULT NatpAddConnectionEntry( IUnknown *pUnk );
ULONG NatpBindConnection( PNAT_CONNECTION_ENTRY pConEntry, HRASCONN Hrasconn, ULONG AdapterIndex OPTIONAL, PIP_ADAPTER_BINDING_INFO BindingInfo OPTIONAL );
HRESULT NatpBuildPortMappingList( PNAT_CONNECTION_ENTRY pConEntry, PIP_ADAPTER_BINDING_INFO pBindingInfo );
VOID NTAPI NatpConfigurationChangedCallbackRoutine( PVOID Context, BOOLEAN TimedOut );
VOID NTAPI NatpConnectionNotifyCallbackRoutine( PVOID Context, BOOLEAN TimedOut );
VOID NTAPI NatpEnableRouterCallbackRoutine( PVOID Context, BOOLEAN TimedOut );
VOID NatpFreeConnectionEntry( PNAT_CONNECTION_ENTRY pConEntry );
VOID NatpFreePortMappingList( PNAT_CONNECTION_ENTRY pConEntry );
PNAT_INTERFACE NatpLookupInterface( ULONG Index, OUT PLIST_ENTRY* InsertionPoint OPTIONAL );
ULONG NatpQueryConnectionAdapter( ULONG Index );
PIP_NAT_INTERFACE_INFO NatpQueryConnectionInformation( PNAT_CONNECTION_ENTRY pConEntry, PIP_ADAPTER_BINDING_INFO BindingInfo );
VOID NatpProcessConfigurationChanged( VOID );
VOID NatpProcessConnectionNotify( VOID );
VOID NTAPI NatpRoutingFailureCallbackRoutine( PVOID Context, PIO_STATUS_BLOCK IoStatus, ULONG Reserved );
VOID NTAPI NatpRoutingFailureWorkerRoutine( PVOID Context );
ULONG NatpStartSharedConnectionManagement( VOID );
ULONG NatpStopSharedConnectionManagement( VOID );
VOID NatpUpdateSharedConnectionDomainName( ULONG AdapterIndex );
BOOLEAN NatpUnbindConnection( PNAT_CONNECTION_ENTRY pConEntry );
PNAT_CONNECTION_ENTRY NatFindConnectionEntry( GUID *pGuid )
/*++
Routine Description:
Locates a connection entry by guid
Arguments:
pGuid - identifies the connection to locate Return Value:
PNAT_CONNECTION_ENTRY - a pointer to the connection, or NULL if not found Environment:
Invoked with 'NatInterfaceLock' held by the caller.
--*/
{ PNAT_CONNECTION_ENTRY pConnection; PLIST_ENTRY pLink;
for (pLink = NatpConnectionList.Flink; pLink != &NatpConnectionList; pLink = pLink->Flink) { pConnection = CONTAINING_RECORD(pLink, NAT_CONNECTION_ENTRY, Link); if (IsEqualGUID(pConnection->Guid, *pGuid)) { return pConnection; } }
return NULL; } // NatFindConnectionEntry
PNAT_PORT_MAPPING_ENTRY NatFindPortMappingEntry( PNAT_CONNECTION_ENTRY pConnection, GUID *pGuid )
/*++
Routine Description:
Locates a port mapping entry for a connection Arguments:
pConnection - the connection to search pGuid - identifies the port mapping entry to locate Return Value:
PNAT_PORT_MAPPING_ENTRY - a pointer to the port mapping, or NULL if not found Environment:
Invoked with 'NatInterfaceLock' held by the caller.
--*/
{ PNAT_PORT_MAPPING_ENTRY pMapping; PLIST_ENTRY pLink;
for (pLink = pConnection->PortMappingList.Flink; pLink != &pConnection->PortMappingList; pLink = pLink->Flink) { pMapping = CONTAINING_RECORD(pLink, NAT_PORT_MAPPING_ENTRY, Link); if (IsEqualGUID(*pMapping->pProtocolGuid, *pGuid)) { return pMapping; } }
return NULL; } // NatFindPortMappingEntry
VOID NatFreePortMappingEntry( PNAT_PORT_MAPPING_ENTRY pEntry )
/*++
Routine Description:
Frees all resources associated with a port mapping entry. This entry must have already been removed from the containing port mapping list and destroyed at the kernel / UDP broadcast mapper level.
Arguments:
pEntry - the entry to free
Return Value:
none.
--*/
{ ASSERT(NULL != pEntry); if (NULL != pEntry->pProtocolGuid) { CoTaskMemFree(pEntry->pProtocolGuid); }
if (NULL != pEntry->pProtocol) { pEntry->pProtocol->Release(); }
if (NULL != pEntry->pBinding) { pEntry->pBinding->Release(); }
NH_FREE(pEntry); } // NatFreePortMappingEntry
HRESULT NatpAddConnectionEntry( IUnknown *pUnk )
/*++
Routine Description:
Creates a NAT_CONNECTION_ENTRY for a firewalled or Ics public connection.
Arguments:
pUnk - pointer to an IHNetFirewalledConnection or IHNetIcsPublicConnection. This need not be the canonical IUnknown (i.e., it's fine to pass in a pointer of either of the above interfaces).
Return Value:
Standard HRESULT
Environment:
Invoked with 'NatInterfaceLock' held by the caller.
--*/
{ HRESULT hr = S_OK; PNAT_CONNECTION_ENTRY pNewEntry = NULL; IHNetConnection *pNetCon = NULL;
//
// Allocate new entry stucture
//
pNewEntry = reinterpret_cast<PNAT_CONNECTION_ENTRY>( NH_ALLOCATE(sizeof(*pNewEntry)) );
if (NULL != pNewEntry) { RtlZeroMemory(pNewEntry, sizeof(*pNewEntry)); InitializeListHead(&pNewEntry->Link); InitializeListHead(&pNewEntry->PortMappingList); } else { hr = E_OUTOFMEMORY; }
//
// Get IHNetConnection interface
//
if (S_OK == hr) { hr = pUnk->QueryInterface(IID_PPV_ARG(IHNetConnection, &pNetCon));
if (SUCCEEDED(hr)) { pNewEntry->pHNetConnection = pNetCon; pNewEntry->pHNetConnection->AddRef();
HNET_CONN_PROPERTIES *pProps;
//
// Get the properties for the connection
//
hr = pNetCon->GetProperties(&pProps);
if (SUCCEEDED(hr)) { //
// Copy properties into entry
//
RtlCopyMemory( &pNewEntry->HNetProperties, pProps, sizeof(*pProps) );
CoTaskMemFree(pProps); } }
if (SUCCEEDED(hr)) { GUID *pGuid;
//
// Get the guid of the connectoin
//
hr = pNetCon->GetGuid(&pGuid);
if (SUCCEEDED(hr)) { RtlCopyMemory(&pNewEntry->Guid, pGuid, sizeof(GUID)); CoTaskMemFree(pGuid); } }
if (SUCCEEDED(hr) && !pNewEntry->HNetProperties.fLanConnection) { //
// Get the RAS phonebook path. We don't cache the
// name since that can change over time.
//
hr = pNetCon->GetRasPhonebookPath( &pNewEntry->wszPhonebookPath ); } }
if (SUCCEEDED(hr) && pNewEntry->HNetProperties.fFirewalled) { //
// Get the firewall control interface
//
hr = pNetCon->GetControlInterface( IID_PPV_ARG(IHNetFirewalledConnection, &pNewEntry->pHNetFwConnection) );
if (SUCCEEDED(hr)) { NatpFirewallConnectionCount += 1; } }
if (SUCCEEDED(hr) && pNewEntry->HNetProperties.fIcsPublic) { //
// Get the ICS public control interface
//
hr = pNetCon->GetControlInterface( IID_PPV_ARG(IHNetIcsPublicConnection, &pNewEntry->pHNetIcsPublicConnection) );
if (SUCCEEDED(hr)) { //
// Remember that we now have a shared connection
//
NatpSharedConnectionPresent = TRUE; } }
if (NULL != pNetCon) { pNetCon->Release(); }
if (SUCCEEDED(hr)) { //
// Add the new entry to the connection list. Ordering doesn't matter.
//
InsertTailList(&NatpConnectionList, &pNewEntry->Link); } else if (NULL != pNewEntry) { //
// Cleanup the partially constructed entry
//
NatpFreeConnectionEntry(pNewEntry); }
return hr; }
ULONG NatpBindConnection( PNAT_CONNECTION_ENTRY pConEntry, HRASCONN Hrasconn, ULONG AdapterIndex, PIP_ADAPTER_BINDING_INFO BindingInfo )
/*++
Routine Description:
This routine is responsible for binding the shared-connection.
Arguments:
pConEntry - the entry to bind
Hrasconn - if the connection is a dialup connection, contains the handle for the active RAS connection.
AdapterIndex - if the connection is a LAN connection, contains the adapter index for the active LAN connection.
BindingInfo - if the connection is a LAN connection, contains the binding information for the active LAN interface.
Return Value:
ULONG - Win32 error.
Environment:
Invoked with 'NatInterfaceLock' held by the caller.
--*/
{ ULONG Error; MIB_IPFORWARDROW IpForwardRow; GUID Guid; RASPPPIPA RasPppIp; ULONG Size; PLIST_ENTRY InsertionPoint; PLIST_ENTRY Link; PNAT_PORT_MAPPING_ENTRY PortMapping; HRESULT hr;
if (NAT_INTERFACE_BOUND(&pConEntry->Interface)) { return NO_ERROR; }
//
// LAN public interfaces are handled differently than RAS public interfaces.
// With a LAN interface, the binding information is passed in from
// 'NatpProcessConnectionNotify'.
// With a RAS inteface, though, we retrieve the projection-information
// for the active connection, and map the address to an adapter index.
//
if (!pConEntry->HNetProperties.fLanConnection) {
//
// Allocate space for the binding info, if this has not yet
// occured. (This memory will be freed in NatpFreeConnectionEntry.)
//
if (NULL == pConEntry->pBindingInfo) { pConEntry->pBindingInfo = reinterpret_cast<PIP_ADAPTER_BINDING_INFO>( NH_ALLOCATE( FIELD_OFFSET(IP_ADAPTER_BINDING_INFO, Address) + sizeof(IP_LOCAL_BINDING) ) );
if (NULL == pConEntry->pBindingInfo) { NhTrace( TRACE_FLAG_NAT, "NatpBindConnection: Unable to allocate binding info" ); return ERROR_NOT_ENOUGH_MEMORY; } }
//
// Retrieve the PPP projection information for the interface.
//
ZeroMemory(&RasPppIp, sizeof(RasPppIp)); Size = RasPppIp.dwSize = sizeof(RasPppIp); Error = RasGetProjectionInfoA( Hrasconn, RASP_PppIp, &RasPppIp, &Size ); if (Error) { NhTrace( TRACE_FLAG_NAT, "NatpBindConnection: RasGetProjectionInfoA=%d", Error ); return Error; }
//
// Convert the projection information to our format
//
BindingInfo = pConEntry->pBindingInfo; BindingInfo->AddressCount = 1; BindingInfo->RemoteAddress = 0; BindingInfo->Address[0].Address = inet_addr(RasPppIp.szIpAddress); BindingInfo->Address[0].Mask = 0xffffffff;
//
// Attempt to find the TCP/IP adapter index for the connection
//
AdapterIndex = NhMapAddressToAdapter(BindingInfo->Address[0].Address); if (AdapterIndex == (ULONG)-1) { NhTrace( TRACE_FLAG_NAT, "NatpBindConnection: MapAddressToAdapter failed" ); return ERROR_NO_SUCH_INTERFACE; }
//
// Install a default route through the interface, if this is
// the shared connection. (We don't want to do this for a
// firewall-only connection.)
//
if (pConEntry->HNetProperties.fIcsPublic) { ZeroMemory(&IpForwardRow, sizeof(IpForwardRow)); IpForwardRow.dwForwardNextHop = BindingInfo->Address[0].Address; IpForwardRow.dwForwardIfIndex = AdapterIndex; IpForwardRow.dwForwardType = MIB_IPROUTE_TYPE_DIRECT; IpForwardRow.dwForwardProto = PROTO_IP_NAT; IpForwardRow.dwForwardMetric1 = 1;
Error = SetIpForwardEntryToStack(&IpForwardRow); if (Error) { NhTrace( TRACE_FLAG_NAT, "NatpBindConnection: SetIpForwardEntryToStack=%d", Error ); return Error; } } }
pConEntry->AdapterIndex = AdapterIndex;
//
// Make sure the interface type is correct.
//
pConEntry->Interface.Type = ROUTER_IF_TYPE_INTERNAL;
//
// Set the interface index value. This can be anything except 0
// (as 0 is reserved for the private connection).
//
do { pConEntry->Interface.Index = static_cast<ULONG>(InterlockedIncrement(&NatpNextInterfaceIndex)); } while (0 == pConEntry->Interface.Index);
//
// Build the port mapping list for this connection
//
hr = NatpBuildPortMappingList(pConEntry, BindingInfo); if (FAILED(hr)) { NhTrace( TRACE_FLAG_NAT, "NatpBindConnection: NatpBuildPortMappingList=0x%08x", hr ); return ERROR_CAN_NOT_COMPLETE; }
//
// Bind the interface, building its configuration to include
// any port-mappings configured as part of shared access settings.
//
pConEntry->Interface.Info = NatpQueryConnectionInformation(pConEntry, BindingInfo);
if (NULL == pConEntry->Interface.Info) { NhTrace( TRACE_FLAG_NAT, "NatpBindConnection[%i]: NatpQueryConnectionInformation failed", pConEntry->Interface.Index );
//
// Free the port mapping list
//
NatpFreePortMappingList(pConEntry); return ERROR_CAN_NOT_COMPLETE; }
Error = NatBindInterface( pConEntry->Interface.Index, &pConEntry->Interface, BindingInfo, AdapterIndex );
if (Error) { NhTrace( TRACE_FLAG_NAT, "NatpBindConnection[%i]: NatBindInterface=%d", pConEntry->Interface.Index, Error );
//
// Free the port mapping list
//
NatpFreePortMappingList(pConEntry);
return Error; }
//
// At this point NAT_INTERFACE_FLAG_BOUND has been set on the
// interface, so we don't need to clean up the port mapping
// list on error, as the list will be cleaned up in
// NatpUnbindConnection.
//
//
// Create UDP broadcast mappings if this is the ICS
// public connection.
//
if (pConEntry->HNetProperties.fIcsPublic && 0 != pConEntry->UdpBroadcastPortMappingCount) { DWORD dwAddress; DWORD dwMask; DWORD dwBroadcastAddress;
ASSERT(NULL != NhpUdpBroadcastMapper); ASSERT(!IsListEmpty(&pConEntry->PortMappingList));
if (NhQueryScopeInformation(&dwAddress, &dwMask)) { dwBroadcastAddress = (dwAddress & dwMask) | ~dwMask; for (Link = pConEntry->PortMappingList.Flink; Link != &pConEntry->PortMappingList; Link = Link->Flink) { PortMapping = CONTAINING_RECORD(Link, NAT_PORT_MAPPING_ENTRY, Link);
if (!PortMapping->fUdpBroadcastMapping) { continue; }
hr = NhpUdpBroadcastMapper->CreateUdpBroadcastMapping( PortMapping->usPublicPort, AdapterIndex, dwBroadcastAddress, &PortMapping->pvBroadcastCookie );
if (FAILED(hr)) { //
// We'll continue if an error occurs here.
//
NhTrace( TRACE_FLAG_INIT, "NatpBindConnection: CreateUdpBroadcastMapping=0x%08x", hr ); } } } }
//
// Make sure that the interface is on the global list (so that the
// FTP, ALG, and H.323 proxies will be able to find its configuration).
//
if (!NatpLookupInterface( pConEntry->Interface.Index, &InsertionPoint )) { InsertTailList(InsertionPoint, &pConEntry->Interface.Link); }
#ifndef NO_FTP_PROXY
//
// Add the interface the the FTP proxy, if this has not yet
// happened.
//
if (!NAT_INTERFACE_ADDED_FTP(&pConEntry->Interface)) { Error = FtpRmAddInterface( NULL, pConEntry->Interface.Index, PERMANENT, IF_TYPE_OTHER, IF_ACCESS_BROADCAST, IF_CONNECTION_DEDICATED, NULL, IP_NAT_VERSION, 0, 0 ); if (Error) { NhTrace( TRACE_FLAG_INIT, "NatpBindConnection: FtpRmAddInterface=%d", Error ); return Error; }
pConEntry->Interface.Flags |= NAT_INTERFACE_FLAG_ADDED_FTP; }
//
// Bind and enable the interface for FTP
//
Error = FtpRmBindInterface(pConEntry->Interface.Index, BindingInfo); if (Error) { NhTrace( TRACE_FLAG_INIT, "NatpBindConnection: FtpRmBindInterface=%d", Error ); return Error; }
Error = FtpRmEnableInterface(pConEntry->Interface.Index); if (Error) { NhTrace( TRACE_FLAG_INIT, "NatpBindConnection: FtpRmEnableInterface=%d", Error ); return Error; } #endif
//
// Add the interface the the ALG proxy, if this has not yet
// happened.
//
if (!NAT_INTERFACE_ADDED_ALG(&pConEntry->Interface)) { Error = AlgRmAddInterface( NULL, pConEntry->Interface.Index, PERMANENT, IF_TYPE_OTHER, IF_ACCESS_BROADCAST, IF_CONNECTION_DEDICATED, NULL, IP_NAT_VERSION, 0, 0 ); if (Error) { NhTrace( TRACE_FLAG_INIT, "NatpBindConnection: AlgRmAddInterface=%d", Error ); return Error; }
pConEntry->Interface.Flags |= NAT_INTERFACE_FLAG_ADDED_ALG; }
//
// Bind and enable the interface for ALG
//
Error = AlgRmBindInterface(pConEntry->Interface.Index, BindingInfo); if (Error) { NhTrace( TRACE_FLAG_INIT, "NatpBindConnection: AlgRmBindInterface=%d", Error ); return Error; }
Error = AlgRmEnableInterface(pConEntry->Interface.Index); if (Error) { NhTrace( TRACE_FLAG_INIT, "NatpBindConnection: AlgRmEnableInterface=%d", Error ); return Error; }
//
// Add the interface the the H.323 proxy, if this has not yet
// happened.
//
if (!NAT_INTERFACE_ADDED_H323(&pConEntry->Interface)) { Error = H323RmAddInterface( NULL, pConEntry->Interface.Index, PERMANENT, IF_TYPE_OTHER, IF_ACCESS_BROADCAST, IF_CONNECTION_DEDICATED, NULL, IP_NAT_VERSION, 0, 0 ); if (Error) { NhTrace( TRACE_FLAG_INIT, "NatpBindConnection: H323RmAddInterface=%d", Error ); return Error; }
pConEntry->Interface.Flags |= NAT_INTERFACE_FLAG_ADDED_H323; }
//
// Bind and enable the interface for H323
//
Error = H323RmBindInterface(pConEntry->Interface.Index, BindingInfo); if (Error) { NhTrace( TRACE_FLAG_INIT, "NatpBindConnection: H323RmBindInterface=%d", Error ); return Error; }
Error = H323RmEnableInterface(pConEntry->Interface.Index); if (Error) { NhTrace( TRACE_FLAG_INIT, "NatpBindConnection: H323RmEnableInterface=%d", Error ); return Error; }
if (pConEntry->HNetProperties.fIcsPublic) {
//
// Finally, update the DNS domain name cached for the shared connection.
//
NatpUpdateSharedConnectionDomainName(AdapterIndex); }
return NO_ERROR; } // NatpBindConnection
HRESULT NatpBuildPortMappingList( PNAT_CONNECTION_ENTRY pConEntry, PIP_ADAPTER_BINDING_INFO pBindingInfo )
/*++
Routine Description:
Builds the list of port mappings for a connection entry
Arguments:
pConEntry - the entry to build the list for
pBindingInfo - the binding info for that entry
Return Value:
Standard HRESULT.
Environment:
NatInterfaceLock must be held by the caller. --*/
{ HRESULT hr; IHNetPortMappingBinding *pBinding; PNAT_PORT_MAPPING_ENTRY pEntry; IEnumHNetPortMappingBindings *pEnum; PLIST_ENTRY pLink; IHNetPortMappingProtocol *pProtocol; ULONG ulCount; PROFILE("NatpBuildPortMappingList");
hr = pConEntry->pHNetConnection->EnumPortMappings(TRUE, &pEnum);
if (FAILED(hr)) { NhTrace( TRACE_FLAG_NAT, "NatpBuildPortMappingList: EnumPortMappings 0x%08x", hr );
return hr; }
//
// Process enumeration, creating the port mapping entries.
//
do { hr = pEnum->Next(1, &pBinding, &ulCount);
if (SUCCEEDED(hr) && 1 == ulCount) { pEntry = reinterpret_cast<PNAT_PORT_MAPPING_ENTRY>( NH_ALLOCATE(sizeof(*pEntry)) );
if (NULL != pEntry) { ZeroMemory(pEntry, sizeof(*pEntry)); //
// Get the protocol for the binding
//
hr = pBinding->GetProtocol(&pProtocol); } else { hr = E_OUTOFMEMORY; }
if (SUCCEEDED(hr)) { //
// Fill out the entry
//
hr = pProtocol->GetGuid(&pEntry->pProtocolGuid);
if (SUCCEEDED(hr)) { hr = pProtocol->GetIPProtocol(&pEntry->ucProtocol); }
if (SUCCEEDED(hr)) { hr = pProtocol->GetPort(&pEntry->usPublicPort); }
if (SUCCEEDED(hr)) { hr = pBinding->GetTargetPort(&pEntry->usPrivatePort); }
if (SUCCEEDED(hr)) { //
// We need to know if the name is active in order to
// avoid rebuilding the DHCP reservation list more
// than necessary.
//
hr = pBinding->GetCurrentMethod(&pEntry->fNameActive); }
if (SUCCEEDED(hr)) { //
// If this is a FW-only connection, use the address from
// our binding info instead of the protocol binding.
//
if (!pConEntry->HNetProperties.fIcsPublic) { pEntry->ulPrivateAddress = pBindingInfo->Address[0].Address; } else { hr = pBinding->GetTargetComputerAddress( &pEntry->ulPrivateAddress );
if (SUCCEEDED(hr) && INADDR_LOOPBACK_NO == pEntry->ulPrivateAddress) { //
// If the port mapping targets the loopback address
// we want to use the address from the binding
// info instead.
//
pEntry->ulPrivateAddress = pBindingInfo->Address[0].Address; }
} }
if (SUCCEEDED(hr)) { pEntry->pBinding = pBinding; pEntry->pBinding->AddRef(); pEntry->pProtocol = pProtocol; pEntry->pProtocol->AddRef();
//
// Check to see if this mapping is:
// 1) targeted at the broadcast address, and
// 2) is UDP.
//
if (NAT_PROTOCOL_UDP == pEntry->ucProtocol && 0xffffffff == pEntry->ulPrivateAddress) { pEntry->fUdpBroadcastMapping = TRUE; pConEntry->UdpBroadcastPortMappingCount += 1; } else { pConEntry->PortMappingCount += 1; }
InsertTailList(&pConEntry->PortMappingList, &pEntry->Link); } else { NatFreePortMappingEntry(pEntry); }
pProtocol->Release(); }
//
// If anything failed above we still want to continue operation --
// it's preferable to have the firewall running w/ some port
// mapping entries missing instead of not having the firewall
// run at all.
//
hr = S_OK;
pBinding->Release(); } } while (SUCCEEDED(hr) && 1 == ulCount);
pEnum->Release();
if (FAILED(hr)) { //
// Free the port mapping list
//
NatpFreePortMappingList(pConEntry); }
return hr; }// NatpBuildPortMappingList
VOID NTAPI NatpConfigurationChangedCallbackRoutine( PVOID Context, BOOLEAN TimedOut )
/*++
Routine Description:
This routine is invoked upon a change in the NAT/Firewall configuration. It may also be invoked when cleanup is in progress.
Arguments:
Context - unused
TimedOut - unused
Return Value:
none.
Environment:
The routine runs in the context of an Rtl wait-thread. (See 'RtlRegisterWait'.) A reference to the component will have been made on our behalf when 'RtlRegisterWait' was called. The reference is released and re-acquired here.
--*/
{ BOOLEAN ComInitialized = TRUE; HRESULT hr; PROFILE("NatpConfigurationChangedCallbackRoutine");
//
// See whether cleanup has occurred
//
EnterCriticalSection(&NatInterfaceLock); if (!NatConfigurationChangedEvent) { LeaveCriticalSection(&NatInterfaceLock); DEREFERENCE_NAT(); return; } LeaveCriticalSection(&NatInterfaceLock);
//
// Acquire a new reference to the component (and release
// our original reference on failure).
//
if (!REFERENCE_NAT()) { DEREFERENCE_NAT(); return; }
//
// Make sure the thread is COM-initialized
//
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE ); if (FAILED(hr)) { ComInitialized = FALSE; if (RPC_E_CHANGED_MODE == hr) { ASSERT(FALSE); hr = S_OK;
NhTrace( TRACE_FLAG_NAT, "NatpConfigurationChangedCallbackRoutine: Unexpectedly in STA." ); } }
//
// Process connection notifications
//
if (SUCCEEDED(hr)) { NatpProcessConfigurationChanged(); }
//
// Uninitialize COM, if necessary
//
if (TRUE == ComInitialized) { CoUninitialize(); }
//
// Release our original reference to the component.
//
DEREFERENCE_NAT();
} // NatpConfigurationChangedCallbackRoutine
VOID NTAPI NatpConnectionNotifyCallbackRoutine( PVOID Context, BOOLEAN TimedOut )
/*++
Routine Description:
This routine is invoked upon connection or disconnection of a RAS phonebook entry. It may also be invoked when cleanup is in progress.
Arguments:
Context - unused
TimedOut - unused
Return Value:
none.
Environment:
The routine runs in the context of an Rtl wait-thread. (See 'RtlRegisterWait'.) A reference to the component will have been made on our behalf when 'RtlRegisterWait' was called. The reference is released and re-acquired here.
--*/
{ BOOLEAN ComInitialized = TRUE; HRESULT hr; PROFILE("NatpConnectionNotifyCallbackRoutine");
//
// See whether cleanup has occurred
//
EnterCriticalSection(&NatInterfaceLock); if (!NatConnectionNotifyEvent) { LeaveCriticalSection(&NatInterfaceLock); DEREFERENCE_NAT(); return; } LeaveCriticalSection(&NatInterfaceLock);
//
// Acquire a new reference to the component (and release
// our original reference on failure).
//
if (!REFERENCE_NAT()) { DEREFERENCE_NAT(); return; }
//
// Make sure the thread is COM-initialized
//
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE ); if (FAILED(hr)) { ComInitialized = FALSE; if (RPC_E_CHANGED_MODE == hr) { ASSERT(FALSE); hr = S_OK;
NhTrace( TRACE_FLAG_NAT, "NatpConnectionNotifyCallbackRoutine: Unexpectedly in STA." ); } }
//
// Process connection notifications
//
if (SUCCEEDED(hr)) { NatpProcessConnectionNotify(); }
//
// Uninitialize COM, if necessary
//
if (TRUE == ComInitialized) { CoUninitialize(); }
//
// Release our original reference to the component.
//
DEREFERENCE_NAT();
} // NatpConnectionNotifyCallbackRoutine
VOID NTAPI NatpEnableRouterCallbackRoutine( PVOID Context, BOOLEAN TimedOut )
/*++
Routine Description:
This routine is invoked upon completion or cancellation of an outstanding request to enable IP forwarding. It determines whether the module is still running and, if so, re-enables forwarding. Otherwise, it cancels any existing request and returns control immediately.
Arguments:
none used.
Return Value:
none.
Environment:
The routine runs in the context of an Rtl wait-thread. (See 'RtlRegisterWait'.) A reference to the component will have been made on our behalf when 'RtlRegisterWait' was called. The reference is released and re-acquired here.
--*/
{ ULONG Error; HANDLE UnusedHandle; PROFILE("NatpEnableRouterCallbackRoutine");
//
// See whether cleanup has occurred and, if so, restore forwarding
// to its original setting. Otherwise, acquire a new reference to the
// component, and release the original reference.
//
EnterCriticalSection(&NatInterfaceLock); if (!NatpEnableRouterEvent || !REFERENCE_NAT()) { UnenableRouter(&NatpEnableRouterOverlapped, NULL); LeaveCriticalSection(&NatInterfaceLock); DEREFERENCE_NAT(); return; }
DEREFERENCE_NAT();
//
// Re-enable forwarding
//
ZeroMemory(&NatpEnableRouterOverlapped, sizeof(OVERLAPPED)); NatpEnableRouterOverlapped.hEvent = NatpEnableRouterEvent; Error = EnableRouter(&UnusedHandle, &NatpEnableRouterOverlapped); if (Error != ERROR_IO_PENDING) { NhTrace( TRACE_FLAG_NAT, "NatpEnableRouterCallbackRoutine: EnableRouter=%d", Error ); } LeaveCriticalSection(&NatInterfaceLock); } // NatpEnableRouterCallbackRoutine
VOID NatpFreeConnectionEntry( PNAT_CONNECTION_ENTRY pConEntry )
/*++
Routine Description:
Frees all resources associated with a connection entry. This entry must have already been removed from the connection list.
Arguments:
pConEntry - the entry to free
Return Value:
none.
--*/
{ PROFILE("NatpFreeConnectionEntry");
if (NULL != pConEntry->pInterfaceInfo) { NH_FREE(pConEntry->pInterfaceInfo); }
if (NULL != pConEntry->pBindingInfo) { NH_FREE(pConEntry->pBindingInfo); }
if (NULL != pConEntry->pHNetConnection) { pConEntry->pHNetConnection->Release(); }
if (NULL != pConEntry->pHNetFwConnection) { pConEntry->pHNetFwConnection->Release(); }
if (NULL != pConEntry->pHNetIcsPublicConnection) { pConEntry->pHNetIcsPublicConnection->Release(); }
if (NULL != pConEntry->wszPhonebookPath) { CoTaskMemFree(pConEntry->wszPhonebookPath); }
NatpFreePortMappingList(pConEntry);
NH_FREE(pConEntry);
} // NatpFreeConnectionEntry
VOID NatpFreePortMappingList( PNAT_CONNECTION_ENTRY pConEntry )
/*++
Routine Description:
Frees the port mapping list for a connection entry. This includes cancelling any active UDP broadcast mappings.
Arguments:
pConEntry - the entry to free
Return Value:
none.
Environment:
Invoked w/ NatInterfaceLock held by the caller
--*/
{ PLIST_ENTRY pLink; PNAT_PORT_MAPPING_ENTRY pMapping; while (!IsListEmpty(&pConEntry->PortMappingList)) { pLink = RemoveHeadList(&pConEntry->PortMappingList); pMapping = CONTAINING_RECORD(pLink, NAT_PORT_MAPPING_ENTRY, Link);
if (pMapping->fUdpBroadcastMapping && NULL != pMapping->pvBroadcastCookie) { ASSERT(NULL != NhpUdpBroadcastMapper); NhpUdpBroadcastMapper->CancelUdpBroadcastMapping( pMapping->pvBroadcastCookie ); } NatFreePortMappingEntry(pMapping); }
pConEntry->PortMappingCount = 0; pConEntry->UdpBroadcastPortMappingCount = 0; } // NatpFreePortMappingList
VOID NatpProcessConfigurationChanged( VOID )
/*++
Routine Description:
This routine is invoked to see when the NAT/Firewall configuration changes. It unbinds the old interfaces, and binds the new ones. It is also responsible for making sure that the autodial service is running.
Arguments:
none.
Return Value:
none.
--*/
{ PLIST_ENTRY Link; PNAT_CONNECTION_ENTRY pConEntry; HRESULT hr; IHNetCfgMgr *pCfgMgr = NULL; IHNetFirewallSettings *pFwSettings; IHNetIcsSettings *pIcsSettings; IEnumHNetFirewalledConnections *pFwEnum; IHNetFirewalledConnection *pFwConn; IEnumHNetIcsPublicConnections *pIcsEnum; IHNetIcsPublicConnection *pIcsConn; ULONG ulCount; UNICODE_STRING UnicodeString;
PROFILE("NatpProcessConfigurationChanged");
EnterCriticalSection(&NatInterfaceLock);
//
// Start by deleting all of our current connections
//
while (!IsListEmpty(&NatpConnectionList)) { Link = RemoveHeadList(&NatpConnectionList); pConEntry = CONTAINING_RECORD(Link, NAT_CONNECTION_ENTRY, Link);
NatpUnbindConnection(pConEntry); NatpFreeConnectionEntry(pConEntry); }
//
// Reset other items to initial state
//
NatpFirewallConnectionCount = 0; NatpSharedConnectionPresent = FALSE; if (NULL != NatpSharedConnectionDomainName) { NH_FREE(NatpSharedConnectionDomainName); NatpSharedConnectionDomainName = NULL; }
//
// Get the configuration manager
//
hr = NhGetHNetCfgMgr(&pCfgMgr);
if (NhPolicyAllowsFirewall) { if (SUCCEEDED(hr)) { //
// Get the firewall settings interface
//
hr = pCfgMgr->QueryInterface( IID_PPV_ARG(IHNetFirewallSettings, &pFwSettings) ); }
if (SUCCEEDED(hr)) { //
// Get the enumeration of firewalled connections
//
hr = pFwSettings->EnumFirewalledConnections(&pFwEnum); pFwSettings->Release(); }
if (SUCCEEDED(hr)) { //
// Process the enumeration
//
do { hr = pFwEnum->Next(1, &pFwConn, &ulCount);
if (SUCCEEDED(hr) && 1 == ulCount) { //
// We don't check the return code for NatpAddConnectionEntry.
// NatpAddConnectionEntry will clean up gracefully if an
// error occurs and will leave the system in a consistent
// state, so an error will not prevent us from processing
// the rest of the connections.
//
NatpAddConnectionEntry(pFwConn); pFwConn->Release(); } } while (SUCCEEDED(hr) && 1 == ulCount);
pFwEnum->Release(); } }
//
// If we don't yet have a shared connection (i.e., none of the
// firewalled connections were also IcsPublic), retrieve that
// enumeration now.
//
if (FALSE == NatpSharedConnectionPresent && NULL != pCfgMgr && NhPolicyAllowsSharing) { //
// Get the IcsSettings interface
//
hr = pCfgMgr->QueryInterface( IID_PPV_ARG(IHNetIcsSettings, &pIcsSettings) );
if (SUCCEEDED(hr)) { //
// Get the enumeration of ICS public connections
//
hr = pIcsSettings->EnumIcsPublicConnections(&pIcsEnum); pIcsSettings->Release(); }
if (SUCCEEDED(hr)) { //
// See if we can get a connection out of the enum
//
hr = pIcsEnum->Next(1, &pIcsConn, &ulCount);
if (SUCCEEDED(hr) && 1 == ulCount) { //
// We don't check the return code for NatpAddConnectionEntry.
// NatpAddConnectionEntry will clean up gracefully if an
// error occurs and will leave the system in a consistent
// state, so an error will not prevent us from processing
// the rest of the connections.
//
NatpAddConnectionEntry(pIcsConn); pIcsConn->Release(); }
pIcsEnum->Release(); } }
if (TRUE == NatpSharedConnectionPresent && NhPolicyAllowsSharing) { //
// Make sure shared connection management is started
//
NatpStartSharedConnectionManagement(); } else { //
// Stop shared connection management
//
NatpStopSharedConnectionManagement(); }
//
// Notify the firewall subsystem as to whether it needs to
// start or stop logging. (These calls are effectively no-ops if
// the logger is already in the correct state.)
//
if (NatpFirewallConnectionCount > 0 && NhPolicyAllowsFirewall) { FwStartLogging(); } else { FwStopLogging(); }
//
// Bind connections
//
NatpProcessConnectionNotify();
if (NULL != pCfgMgr) { pCfgMgr->Release(); }
LeaveCriticalSection(&NatInterfaceLock);
} // NatpProcessConfigurationChanged
VOID NatpProcessConnectionNotify( VOID )
/*++
Routine Description:
This routine is invoked to see if the shared or firewall connections, if any, have been connected or disconnected since its last invocation.
Arguments:
none.
Return Value:
none.
--*/
{ PLIST_ENTRY Link; PNAT_CONNECTION_ENTRY pConEntry; BOOLEAN Active; ULONG i; ULONG AdapterIndex; PIP_ADAPTER_BINDING_INFO BindingInfo = NULL; ULONG Error; HRASCONN Hrasconn; GUID Guid; UNICODE_STRING UnicodeString; NTSTATUS Status;
BOOLEAN bUPnPEventAlreadyFired = FALSE;
PROFILE("NatpProcessConnectionNotify");
EnterCriticalSection(&NatInterfaceLock);
//
// Walk through the connection list
//
for (Link = NatpConnectionList.Flink; Link != &NatpConnectionList; Link = Link->Flink) {
pConEntry = CONTAINING_RECORD(Link, NAT_CONNECTION_ENTRY, Link);
//
// If the connection is a LAN connection,
// it is always active.
//
// If the connection is a dialup connection,
// find out whether the connection is active.
//
if (pConEntry->HNetProperties.fLanConnection) { Hrasconn = NULL; Active = TRUE;
//
// The connection is a LAN connection, so we need to detect
// any changes to its IP address if it is already bound.
// To do so we retrieve the current binding information
// and compare it to the active binding information.
// If the two are different, we unbind the interface and rebind.
//
Status = RtlStringFromGUID(pConEntry->Guid, &UnicodeString); if (NT_SUCCESS(Status)) { AdapterIndex = NhMapGuidToAdapter(UnicodeString.Buffer); RtlFreeUnicodeString(&UnicodeString); } else { AdapterIndex = (ULONG)-1; NhTrace( TRACE_FLAG_NAT, "NatpProcessConnectionNotify: RtlStringFromGUID failed\n" ); } if (AdapterIndex == (ULONG)-1) { NhTrace( TRACE_FLAG_NAT, "NatpProcessConnectionNotify: MapGuidToAdapter failed\n" ); Active = FALSE; } else {
BindingInfo = NhQueryBindingInformation(AdapterIndex); if (!BindingInfo) { NhTrace( TRACE_FLAG_NAT, "NatpProcessConnectionNotify: QueryBinding failed\n" ); Active = FALSE; } else if (NAT_INTERFACE_BOUND(&pConEntry->Interface)) {
//
// The interface is already bound;
// compare the retrieved binding to the active binding,
// and unbind the connection if they are different.
//
if (!pConEntry->pBindingInfo || BindingInfo->AddressCount != pConEntry->pBindingInfo->AddressCount || !BindingInfo->AddressCount || !RtlEqualMemory( &BindingInfo->Address[0], &pConEntry->pBindingInfo->Address[0], sizeof(IP_LOCAL_BINDING) )) { NatpUnbindConnection(pConEntry);
if ( pConEntry->HNetProperties.fIcsPublic ) { FireNATEvent_PublicIPAddressChanged(); bUPnPEventAlreadyFired = TRUE; } } else {
//
// The bindings are the same, and the interface is bound
// already, so we won't be needing the newly-retrieved
// binding information.
//
NH_FREE(BindingInfo); BindingInfo = NULL; } } } } else { AdapterIndex = (ULONG)-1; Hrasconn = NULL; //
// Obtain the name of the connection
//
HRESULT hr; LPWSTR wszEntryName;
hr = pConEntry->pHNetConnection->GetName(&wszEntryName);
if (SUCCEEDED(hr)) { Error = RasGetEntryHrasconnW( pConEntry->wszPhonebookPath, wszEntryName, &Hrasconn ); CoTaskMemFree(wszEntryName); } Active = ((FAILED(hr) || Error || !Hrasconn) ? FALSE : TRUE);
}
//
// Activate or deactivate the shared-connection as needed;
// when activating a LAN connection, we save the binding information
// so we can detect address changes later on.
//
if (!Active && NAT_INTERFACE_BOUND(&pConEntry->Interface)) { NatpUnbindConnection(pConEntry); if (pConEntry->HNetProperties.fIcsPublic && (FALSE == bUPnPEventAlreadyFired)) { FireNATEvent_PublicIPAddressChanged(); } } else if (Active && !NAT_INTERFACE_BOUND(&pConEntry->Interface)) {
//
// N.B. When a media-sense event occurs and TCP/IP revokes the IP
// address for a LAN connection, the connection's IP address becomes
// 0.0.0.0. We treat that as though we don't have an IP address at all,
// and bypass the binding below. When the IP address is reinstated,
// we will rebind correctly, since we will then detect the change.
//
if (pConEntry->HNetProperties.fLanConnection) { if (BindingInfo->AddressCount != 1 || BindingInfo->Address[0].Address) { NatpBindConnection(pConEntry, Hrasconn, AdapterIndex, BindingInfo); } if (pConEntry->pBindingInfo) { NH_FREE(pConEntry->pBindingInfo); } pConEntry->pBindingInfo = BindingInfo; } else { NatpBindConnection(pConEntry, Hrasconn, AdapterIndex, BindingInfo); } if ( pConEntry->HNetProperties.fIcsPublic && (FALSE == bUPnPEventAlreadyFired) && NAT_INTERFACE_BOUND(&pConEntry->Interface)) { FireNATEvent_PublicIPAddressChanged(); }
} }
//
// If we have a shared connection, also need to update the private interface
//
if (NatpSharedConnectionPresent) { NhUpdatePrivateInterface(); }
LeaveCriticalSection(&NatInterfaceLock);
} // NatpProcessConnectionNotify
ULONG NatpQueryConnectionAdapter( PNAT_CONNECTION_ENTRY pConEntry )
/*++
Routine Description:
This routine is invoked to determine the adapter index corresponding to a connection, if active.
Arguments:
pConEntry - the connection entry
Return Value:
ULONG - the adapter index if found, otherwise (ULONG)-1.
Environment:
Invoked with 'NatInterfaceLock' held by the caller.
--*/
{ ULONG AdapterIndex = (ULONG)-1; ULONG Error; HRASCONN Hrasconn = NULL; RASPPPIPA RasPppIp; ULONG Size; UNICODE_STRING UnicodeString;
if (pConEntry->HNetProperties.fLanConnection) { RtlStringFromGUID(pConEntry->Guid, &UnicodeString); AdapterIndex = NhMapGuidToAdapter(UnicodeString.Buffer); RtlFreeUnicodeString(&UnicodeString); } else { HRESULT hr; LPWSTR wszEntryName;
hr = pConEntry->pHNetConnection->GetName(&wszEntryName);
if (SUCCEEDED(hr)) { Error = RasGetEntryHrasconnW( pConEntry->wszPhonebookPath, wszEntryName, &Hrasconn ); if (!Error && Hrasconn) { ZeroMemory(&RasPppIp, sizeof(RasPppIp)); Size = RasPppIp.dwSize = sizeof(RasPppIp); Error = RasGetProjectionInfoA( Hrasconn, RASP_PppIp, &RasPppIp, &Size ); if (!Error) { AdapterIndex = NhMapAddressToAdapter(inet_addr(RasPppIp.szIpAddress)); } }
CoTaskMemFree(wszEntryName); } } NhTrace(TRACE_FLAG_NAT, "NatpQueryConnectionAdapter: %d", AdapterIndex); return AdapterIndex; } // NatpQueryConnectionAdapter
PIP_NAT_INTERFACE_INFO NatpQueryConnectionInformation( PNAT_CONNECTION_ENTRY pConEntry, PIP_ADAPTER_BINDING_INFO BindingInfo )
/*++
Routine Description:
This routine is invoked to construct the configuration of a connection. The configuration consists of basic settings (e.g. interface type and flags) as well as extended information loaded from the configuration store (e.g. port mappings).
Arguments:
pConEntry - the connection entry
BindingInfo - the binding info for the connection
Return Value:
PIP_NAT_INTERFACE_INFO - the configuration allocated; on error, returns NULL
Environment:
Invoked with 'NatInterfaceLock' held by the caller.
--*/
{ PIP_NAT_PORT_MAPPING Array = NULL; ULONG Count = 0; ULONG Error; PIP_NAT_INTERFACE_INFO Info; PRTR_INFO_BLOCK_HEADER Header; HRESULT hr; ULONG Length; PLIST_ENTRY Link; PRTR_INFO_BLOCK_HEADER NewHeader; PNAT_PORT_MAPPING_ENTRY PortMapping;
PROFILE("NatpQueryConnectionInformation");
//
// Build the port mapping array from the list
//
if (pConEntry->PortMappingCount) { Array = reinterpret_cast<PIP_NAT_PORT_MAPPING>( NH_ALLOCATE(pConEntry->PortMappingCount * sizeof(IP_NAT_PORT_MAPPING)) );
if (NULL == Array) { NhTrace( TRACE_FLAG_NAT, "NatpQueryConnectionInformation: Unable to allocate array" ); return NULL; }
for (Link = pConEntry->PortMappingList.Flink; Link != &pConEntry->PortMappingList; Link = Link->Flink) { PortMapping = CONTAINING_RECORD(Link, NAT_PORT_MAPPING_ENTRY, Link);
if (PortMapping->fUdpBroadcastMapping) { continue; }
Array[Count].PublicAddress = IP_NAT_ADDRESS_UNSPECIFIED; Array[Count].Protocol = PortMapping->ucProtocol; Array[Count].PublicPort = PortMapping->usPublicPort; Array[Count].PrivateAddress = PortMapping->ulPrivateAddress; Array[Count].PrivatePort = PortMapping->usPrivatePort;
Count += 1; }
ASSERT(Count == pConEntry->PortMappingCount); }
//
// Create an info-block header and add the port-mapping array
// as the single entry in the info-block.
// This info-block header will occupy the 'Header' field
// of the final 'IP_NAT_INTERFACE_INFO'.
//
Error = MprInfoCreate(IP_NAT_VERSION, reinterpret_cast<LPVOID*>(&Header)); if (Error) { if (Array) { NH_FREE(Array); } return NULL; }
if (Count) { Error = MprInfoBlockAdd( Header, IP_NAT_PORT_MAPPING_TYPE, sizeof(IP_NAT_PORT_MAPPING), Count, (PUCHAR)Array, reinterpret_cast<LPVOID*>(&NewHeader) ); MprInfoDelete(Header); NH_FREE(Array); Header = NewHeader; if (Error) { return NULL; } } else if (Array) { NH_FREE(Array); }
//
// For firewalled entries, get ICMP settings
//
if (pConEntry->HNetProperties.fFirewalled && NhPolicyAllowsFirewall) { HNET_FW_ICMP_SETTINGS *pIcmpSettings; DWORD dwIcmpFlags = 0;
hr = pConEntry->pHNetConnection->GetIcmpSettings(&pIcmpSettings);
if (SUCCEEDED(hr)) { if (pIcmpSettings->fAllowOutboundDestinationUnreachable) { dwIcmpFlags |= IP_NAT_ICMP_ALLOW_OB_DEST_UNREACH; }
if (pIcmpSettings->fAllowOutboundSourceQuench) { dwIcmpFlags |= IP_NAT_ICMP_ALLOW_OB_SOURCE_QUENCH; }
if (pIcmpSettings->fAllowRedirect) { dwIcmpFlags |= IP_NAT_ICMP_ALLOW_REDIRECT; }
if (pIcmpSettings->fAllowInboundEchoRequest) { dwIcmpFlags |= IP_NAT_ICMP_ALLOW_IB_ECHO; }
if (pIcmpSettings->fAllowInboundRouterRequest) { dwIcmpFlags |= IP_NAT_ICMP_ALLOW_IB_ROUTER; }
if (pIcmpSettings->fAllowOutboundTimeExceeded) { dwIcmpFlags |= IP_NAT_ICMP_ALLOW_OB_TIME_EXCEEDED; }
if (pIcmpSettings->fAllowOutboundParameterProblem) { dwIcmpFlags |= IP_NAT_ICMP_ALLOW_OB_PARAM_PROBLEM; }
if (pIcmpSettings->fAllowInboundTimestampRequest) { dwIcmpFlags |= IP_NAT_ICMP_ALLOW_IB_TIMESTAMP; }
if (pIcmpSettings->fAllowInboundMaskRequest) { dwIcmpFlags |= IP_NAT_ICMP_ALLOW_IB_MASK; }
CoTaskMemFree(pIcmpSettings);
Error = MprInfoBlockAdd( Header, IP_NAT_ICMP_CONFIG_TYPE, sizeof(DWORD), 1, (PUCHAR)&dwIcmpFlags, reinterpret_cast<LPVOID*>(&NewHeader) );
if (NO_ERROR == Error) { MprInfoDelete(Header); Header = NewHeader; } } else { NhTrace( TRACE_FLAG_NAT, "NatpQueryConnectionInformation: GetIcmpSettings 0x%08x", hr );
//
// This is a 'soft' error -- we'll still continue even if we
// couldn't get the ICMP settings, as our default stance
// is more secure than if any of the flags were set.
//
} }
//
// Allocate an 'IP_NAT_INTERFACE_INFO' which is large enough to hold
// the info-block header which we've just constructed.
//
Length = FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header) + Header->Size; Info = reinterpret_cast<PIP_NAT_INTERFACE_INFO>(NH_ALLOCATE(Length));
if (Info) { RtlZeroMemory(Info, FIELD_OFFSET(IP_NAT_INTERFACE_INFO, Header));
//
// Set appropriate flags
//
if (pConEntry->HNetProperties.fFirewalled && NhPolicyAllowsFirewall) { Info->Flags |= IP_NAT_INTERFACE_FLAGS_FW; }
if (pConEntry->HNetProperties.fIcsPublic && NhPolicyAllowsSharing) { Info->Flags |= IP_NAT_INTERFACE_FLAGS_BOUNDARY | IP_NAT_INTERFACE_FLAGS_NAPT; }
//
// Copy the info-block header into the info structure
//
RtlCopyMemory(&Info->Header, Header, Header->Size); }
MprInfoDelete(Header);
return Info; } // NatpQuerySharedConnectionInformation
VOID NTAPI NatpRoutingFailureCallbackRoutine( PVOID Context, PIO_STATUS_BLOCK IoStatus, ULONG Reserved )
/*++
Routine Description:
This routine is invoked when a routing-failure notification occurs, or when the request is cancelled (e.g. because the request's thread exited).
Arguments:
Context - unused
IoStatus - contains the status of the operation
Reserved - unused
Return Value:
none.
Environment:
Invoked with a reference made to the component on our behalf. That reference is released here, and if notification is re-requested, it is re-acquired.
--*/
{ CHAR DestinationAddress[32]; ULONG Error; IP_NAT_REQUEST_NOTIFICATION RequestNotification;
PROFILE("NatpRoutingFailureCallbackRoutine");
//
// See if cleanup has occurred
//
EnterCriticalSection(&NatInterfaceLock); if (!NatConnectionNotifyEvent) { LeaveCriticalSection(&NatInterfaceLock); DEREFERENCE_NAT(); return; } LeaveCriticalSection(&NatInterfaceLock);
//
// Acquire a new reference, and release the old one
//
if (!REFERENCE_NAT()) { DEREFERENCE_NAT(); return; } DEREFERENCE_NAT();
lstrcpyA( DestinationAddress, inet_ntoa(*(PIN_ADDR)&NatpRoutingFailureNotification.DestinationAddress) ); NhTrace( TRACE_FLAG_NAT, "NatpRoutingFailureCallbackRoutine: %s->%s", inet_ntoa(*(PIN_ADDR)&NatpRoutingFailureNotification.SourceAddress), DestinationAddress );
//
// Request an automatic connection if the notification succeeded
//
if (NT_SUCCESS(IoStatus->Status)) {
//
// First see if this is a known autodial destination,
// requesting a connection if so.
//
ULONG Count; ULONG Size;
Size = 0; Error = RasGetAutodialAddressA( DestinationAddress, NULL, NULL, &Size, &Count ); if (Error != ERROR_BUFFER_TOO_SMALL) {
//
// This is not a known destination;
// try the default shared connection, if any
//
NhDialSharedConnection(); } else {
//
// Try initiating a normal autodial connection;
// normal autodial may yet lead to the shared-connection.
//
HINSTANCE Hinstance = LoadLibraryA("RASADHLP.DLL"); if (Hinstance) { BOOL (*WSAttemptAutodialAddr)(PSOCKADDR_IN, INT) = (BOOL (*)(PSOCKADDR_IN, INT)) GetProcAddress( Hinstance, "WSAttemptAutodialAddr" ); if (WSAttemptAutodialAddr) { SOCKADDR_IN SockAddr; SockAddr.sin_family = AF_INET; SockAddr.sin_addr.s_addr = NatpRoutingFailureNotification.DestinationAddress; WSAttemptAutodialAddr(&SockAddr, sizeof(SockAddr)); } FreeLibrary(Hinstance); } } }
//
// Submit a new request
//
EnterCriticalSection(&NatInterfaceLock); RequestNotification.Code = NatRoutingFailureNotification; NtDeviceIoControlFile( NatFileHandle, NULL, NatpRoutingFailureCallbackRoutine, NULL, &NatpRoutingFailureIoStatus, IOCTL_IP_NAT_REQUEST_NOTIFICATION, (PVOID)&RequestNotification, sizeof(RequestNotification), &NatpRoutingFailureNotification, sizeof(NatpRoutingFailureNotification) ); LeaveCriticalSection(&NatInterfaceLock);
} // NatpRoutingFailureCallbackRoutine
VOID NTAPI NatpRoutingFailureWorkerRoutine( PVOID Context )
/*++
Routine Description:
This routine initiates the notification of routing-failures.
Arguments:
none used.
Return Value:
none.
Environment:
Invoked in the context of an alertable I/O worker thread.
--*/
{ IP_NAT_REQUEST_NOTIFICATION RequestNotification; PROFILE("NatpRoutingFailureWorkerRoutine");
//
// Request notification of routing-failures
//
EnterCriticalSection(&NatInterfaceLock); RequestNotification.Code = NatRoutingFailureNotification; NtDeviceIoControlFile( NatFileHandle, NULL, NatpRoutingFailureCallbackRoutine, NULL, &NatpRoutingFailureIoStatus, IOCTL_IP_NAT_REQUEST_NOTIFICATION, (PVOID)&RequestNotification, sizeof(RequestNotification), &NatpRoutingFailureNotification, sizeof(NatpRoutingFailureNotification) ); LeaveCriticalSection(&NatInterfaceLock); } // NatpRoutingFailureWorkerRoutine
ULONG NatpStartSharedConnectionManagement( VOID )
/*++
Routine Description:
This routine is called to install routing failure-notification, and to enable the router
Arguments:
none.
Return Value:
ULONG - Win32 status code.
--*/
{ ULONG Error; BOOL SharedAutoDial; NTSTATUS status;
PROFILE("NatpStartSharedConnectionManagement");
//
// See if the user has enabled shared-autodial.
// If so, make sure the autodial service is running,
// since it will be needed for performing on-demand dialing.
//
// (IHNetIcsSettings::GetAutodialEnabled just calls the RAS api below,
// which is why we're not getting the information that way right now...)
//
if (!RasQuerySharedAutoDial(&SharedAutoDial) && SharedAutoDial) { SC_HANDLE ScmHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if (ScmHandle) { SC_HANDLE ServiceHandle = OpenService(ScmHandle, TEXT("RasAuto"), SERVICE_ALL_ACCESS); if (ServiceHandle) { StartService(ServiceHandle, 0, NULL); CloseServiceHandle(ServiceHandle); } CloseServiceHandle(ScmHandle); } }
EnterCriticalSection(&NatInterfaceLock); if (NatpEnableRouterEvent) { LeaveCriticalSection(&NatInterfaceLock); return NO_ERROR; }
//
// Acquire a component-reference on behalf of
// (1) the enable-router callback routine
// (2) the routing-failure-notification worker routine.
//
if (!REFERENCE_NAT()) { LeaveCriticalSection(&NatInterfaceLock); return ERROR_CAN_NOT_COMPLETE; } else if (!REFERENCE_NAT()) { LeaveCriticalSection(&NatInterfaceLock); DEREFERENCE_NAT(); return ERROR_CAN_NOT_COMPLETE; }
do { //
// Start DNS and DHCP modules
//
Error = NhStartICSProtocols(); if (Error) break;
//
// Enable IP forwarding:
// Create an event to be used in the overlapped I/O structure
// that will be passed to the 'EnableRouter' API routine,
// set up the overlapped structure, and schedule the request
// by signalling the event.
//
NatpEnableRouterEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (!NatpEnableRouterEvent) { Error = GetLastError(); break; }
status = RtlRegisterWait( &NatpEnableRouterWaitHandle, NatpEnableRouterEvent, NatpEnableRouterCallbackRoutine, NULL, INFINITE, 0 ); if (!NT_SUCCESS(status)) { Error = RtlNtStatusToDosError(status); break; }
SetEvent(NatpEnableRouterEvent);
//
// Queue a work item in whose context we will make a request
// for routing-failure notification from the NAT driver.
// We use a work-item rather than issuing the request directly
// to avoid having our I/O request cancelled if and when the current
// (thread pool) thread exits.
//
RtlQueueWorkItem( NatpRoutingFailureWorkerRoutine, NULL, WT_EXECUTEINIOTHREAD );
LeaveCriticalSection(&NatInterfaceLock); return NO_ERROR;
} while (FALSE);
if (NatpEnableRouterWaitHandle) { RtlDeregisterWait(NatpEnableRouterWaitHandle); NatpEnableRouterWaitHandle = NULL; } if (NatpEnableRouterEvent) { CloseHandle(NatpEnableRouterEvent); NatpEnableRouterEvent = NULL; }
LeaveCriticalSection(&NatInterfaceLock); DEREFERENCE_NAT(); DEREFERENCE_NAT();
return Error;
} // NatpStartSharedConnectionManagement
ULONG NatpStopSharedConnectionManagement( VOID )
/*++
Routine Description:
This routine is called to stop the DNS & DHCP modules and also to remove the routing failure-notification, and to disable the router
Arguments:
none.
Return Value:
ULONG - Win32 status code.
--*/
{ ULONG Error = NO_ERROR;
PROFILE("NatpStopSharedConnectionManagement");
EnterCriticalSection(&NatInterfaceLock);
//
// Stop the DHCP, DNS, QoSWindowAdjustment and Beacon modules
//
Error = NhStopICSProtocols();
if (NatpEnableRouterWaitHandle) { RtlDeregisterWait(NatpEnableRouterWaitHandle); NatpEnableRouterWaitHandle = NULL; }
if (NatpEnableRouterEvent) { CloseHandle(NatpEnableRouterEvent); NatpEnableRouterEvent = NULL; NatpEnableRouterCallbackRoutine(NULL, FALSE); }
LeaveCriticalSection(&NatInterfaceLock);
return Error;
} // NatpStopSharedConnectionManagement
BOOLEAN NatpUnbindConnection( PNAT_CONNECTION_ENTRY pConEntry )
/*++
Routine Description:
This routine is invoked to unbind a currently-active connection.
Arguments:
Index - index into the connection array
Return Value:
TRUE if the entry was previously bound; FALSE otherwise.
Environment:
Invoked with 'NatInterfaceLock' held by the caller.
--*/
{ LIST_ENTRY *pLink; PNAT_PORT_MAPPING_ENTRY pMapping; PROFILE("NatpUnbindConnection");
if (NAT_INTERFACE_BOUND(&pConEntry->Interface)) { NatUnbindInterface( pConEntry->Interface.Index, &pConEntry->Interface );
#ifndef NO_FTP_PROXY
if (NAT_INTERFACE_ADDED_FTP(&pConEntry->Interface)) { FtpRmDeleteInterface(pConEntry->Interface.Index); pConEntry->Interface.Flags &= ~NAT_INTERFACE_FLAG_ADDED_FTP; } #endif
if (NAT_INTERFACE_ADDED_ALG(&pConEntry->Interface)) { AlgRmDeleteInterface(pConEntry->Interface.Index); pConEntry->Interface.Flags &= ~NAT_INTERFACE_FLAG_ADDED_ALG; }
if (NAT_INTERFACE_ADDED_H323(&pConEntry->Interface)) { H323RmDeleteInterface(pConEntry->Interface.Index); pConEntry->Interface.Flags &= ~NAT_INTERFACE_FLAG_ADDED_H323; }
RemoveEntryList(&pConEntry->Interface.Link); InitializeListHead(&pConEntry->Interface.Link);
if (pConEntry->Interface.Info) { NH_FREE(pConEntry->Interface.Info); pConEntry->Interface.Info = NULL; }
//
// Clean up the port mapping list
//
NatpFreePortMappingList(pConEntry);
return TRUE; } return FALSE; } // NatpUnbindConnection
VOID NatpUpdateSharedConnectionDomainName( ULONG AdapterIndex )
/*++
Routine Description:
This routine is called to update the cached DNS domain name, if any, for the shared connection.
Arguments:
AdapterIndex - the index of the adapter for the shared connection
Return Value:
none.
--*/
{ PADAPTER_INFORMATION AdapterInformation; ANSI_STRING AnsiString; ULONG Count; ULONG Error; ULONG i; PDNS_NETWORK_INFORMATION NetworkInformation = NULL; PIP_INTERFACE_NAME_INFO Table = NULL; UNICODE_STRING UnicodeString; PROFILE("NatpUpdateSharedConnectionDomainName");
RtlInitAnsiString(&AnsiString, NULL); RtlInitUnicodeString(&UnicodeString, NULL); EnterCriticalSection(&NatInterfaceLock); if (AdapterIndex == (ULONG)-1) { PLIST_ENTRY Link; PNAT_CONNECTION_ENTRY pConEntry;
//
// Make sure that the connection list has been initialized; if
// it hasn't, Flink will be NULL.
//
if (!NatpConnectionList.Flink) { LeaveCriticalSection(&NatInterfaceLock); return; }
//
// See if we actually have a shared connection
//
for (Link = NatpConnectionList.Flink; Link != &NatpConnectionList; Link = Link->Flink) { pConEntry = CONTAINING_RECORD(Link, NAT_CONNECTION_ENTRY, Link);
if (pConEntry->HNetProperties.fIcsPublic) { AdapterIndex = NatpQueryConnectionAdapter(pConEntry); break; } }
if (AdapterIndex == (ULONG)-1) { LeaveCriticalSection(&NatInterfaceLock); return; } }
do {
//
// Obtain the GUID for the adapter with the given index,
// by querying TCP/IP for information on all available interfaces.
// The GUID will then be used to map the shared connection's adapter
// to a DNS domain name.
//
Error = NhpAllocateAndGetInterfaceInfoFromStack( &Table, &Count, FALSE, GetProcessHeap(), 0 ); if (Error != NO_ERROR) { break; }
for (i = 0; i < Count && Table[i].Index != AdapterIndex; i++) { } if (i >= Count) { Error = ERROR_INTERNAL_ERROR; break; }
RtlStringFromGUID(Table[i].DeviceGuid, &UnicodeString); RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
//
// Query the DNS client for the current network parameters,
// and search through the network parameters to find the entry
// for the shared-connection's current adapter.
//
NetworkInformation = (PDNS_NETWORK_INFORMATION) DnsQueryConfigAlloc( DnsConfigNetworkInformation, NULL ); if (!NetworkInformation) { Error = ERROR_INTERNAL_ERROR; break; } for (i = 0; i < NetworkInformation->cAdapterCount; i++) { AdapterInformation = NetworkInformation->aAdapterInfoList[i]; if (lstrcmpiA( AnsiString.Buffer, AdapterInformation->pszAdapterGuidName ) == 0) { break; } } if (i >= NetworkInformation->cAdapterCount) { Error = ERROR_INTERNAL_ERROR; break; }
//
// 'AdapterInformation' is the entry for the shared-connection's
// current adapter.
// Clear the previously-cached string, and read in the new value,
// if any.
//
if (NatpSharedConnectionDomainName) { NH_FREE(NatpSharedConnectionDomainName); NatpSharedConnectionDomainName = NULL; } if (AdapterInformation->pszDomain) { NatpSharedConnectionDomainName = reinterpret_cast<PCHAR>( NH_ALLOCATE(lstrlenA(AdapterInformation->pszDomain) + 1) ); if (!NatpSharedConnectionDomainName) { Error = ERROR_INTERNAL_ERROR; break; } lstrcpyA( NatpSharedConnectionDomainName, AdapterInformation->pszDomain ); } Error = NO_ERROR;
} while(FALSE);
if (UnicodeString.Buffer) { RtlFreeUnicodeString(&UnicodeString); } if (AnsiString.Buffer) { RtlFreeAnsiString(&AnsiString); } if (NetworkInformation) { DnsFreeConfigStructure( NetworkInformation, DnsConfigNetworkInformation ); } if (Table) { HeapFree(GetProcessHeap(), 0, Table); } if (Error) { if (NatpSharedConnectionDomainName) { NH_FREE(NatpSharedConnectionDomainName); NatpSharedConnectionDomainName = NULL; } }
LeaveCriticalSection(&NatInterfaceLock); } // NatpUpdateSharedConnectionDomainName
PCHAR NatQuerySharedConnectionDomainName( VOID )
/*++
Routine Description:
This routine is called to retrieve a copy of the DNS domain name cached for the shared connection, if available. Otherwise, it returns the primary DNS domain name for the local machine.
Arguments:
none.
Return Value:
PCHAR - contains the allocated copy of the DNS domain name.
--*/
{ PCHAR DomainName; PROFILE("NatQuerySharedConnectionDomainName");
//
// See if there is a cached domain name for the shared connection.
// If not, refresh the cache. If there is still no domain name,
// return a copy of the local machine's primary DNS domain name.
//
EnterCriticalSection(&NatInterfaceLock); if (!NatpSharedConnectionDomainName) { NatpUpdateSharedConnectionDomainName((ULONG)-1); } if (NatpSharedConnectionDomainName) { DomainName = reinterpret_cast<PCHAR>( NH_ALLOCATE(lstrlenA(NatpSharedConnectionDomainName) + 1) ); if (DomainName) { lstrcpyA(DomainName, NatpSharedConnectionDomainName); } } else { PCHAR DnsDomainName = (PCHAR) DnsQueryConfigAlloc( DnsConfigPrimaryDomainName_A, NULL ); if (!DnsDomainName) { DomainName = NULL; } else { DomainName = reinterpret_cast<PCHAR>( NH_ALLOCATE(lstrlenA(DnsDomainName) + 1) ); if (DomainName) { lstrcpyA(DomainName, DnsDomainName); } DnsFreeConfigStructure( DnsDomainName, DnsConfigPrimaryDomainName_A ); } } LeaveCriticalSection(&NatInterfaceLock); return DomainName; } // NatQuerySharedConnectionDomainName
ULONG NatStartConnectionManagement( VOID )
/*++
Routine Description:
This routine is called to install connection change-notification.
Arguments:
none.
Return Value:
ULONG - Win32 status code.
--*/
{ ULONG Error; NTSTATUS status;
PROFILE("NatStartConnectionManagement");
EnterCriticalSection(&NatInterfaceLock); if (NatConnectionNotifyEvent) { LeaveCriticalSection(&NatInterfaceLock); return NO_ERROR; }
//
// Initialize the connection list
//
InitializeListHead(&NatpConnectionList);
//
// Acquire a component-reference on behalf of
// (1) the connection-notification routine
// (2) the configuration-changed routine
//
if (!REFERENCE_NAT()) { LeaveCriticalSection(&NatInterfaceLock); return ERROR_CAN_NOT_COMPLETE; } if (!REFERENCE_NAT()) { LeaveCriticalSection(&NatInterfaceLock); DEREFERENCE_NAT(); return ERROR_CAN_NOT_COMPLETE; }
do { //
// Create the connection-notification event, register a wait
// on the event, and register for connect and disconnect notification.
// We expect at least one invocation as a result of this registration,
// hence the reference made to the NAT module above.
//
NatConnectionNotifyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (!NatConnectionNotifyEvent) { Error = GetLastError(); break; }
status = RtlRegisterWait( &NatpConnectionNotifyWaitHandle, NatConnectionNotifyEvent, NatpConnectionNotifyCallbackRoutine, NULL, INFINITE, 0 ); if (!NT_SUCCESS(status)) { Error = RtlNtStatusToDosError(status); break; }
Error = RasConnectionNotification( (HRASCONN)INVALID_HANDLE_VALUE, NatConnectionNotifyEvent, RASCN_Connection|RASCN_Disconnection ); if (Error) { break; }
//
// Create the configuartion-change event and register a wait
// on the event.
//
NatConfigurationChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (!NatConfigurationChangedEvent) { Error = GetLastError(); break; }
status = RtlRegisterWait( &NatpConfigurationChangedWaitHandle, NatConfigurationChangedEvent, NatpConfigurationChangedCallbackRoutine, NULL, INFINITE, 0 ); if (!NT_SUCCESS(status)) { Error = RtlNtStatusToDosError(status); break; }
LeaveCriticalSection(&NatInterfaceLock);
//
// Pick up any existing connections, by signalling the configuration
// change event. We cannot invoke the function directly
// because it invokes service-control functions to start autodial,
// and we could currently be running in a service-controller thread.
//
NtSetEvent(NatConfigurationChangedEvent, NULL); return NO_ERROR;
} while(FALSE);
//
// A failure occurred; perform cleanup
//
if (NatpConnectionNotifyWaitHandle) { RtlDeregisterWait(NatpConnectionNotifyWaitHandle); NatpConnectionNotifyWaitHandle = NULL; } if (NatConnectionNotifyEvent) { CloseHandle(NatConnectionNotifyEvent); NatConnectionNotifyEvent = NULL; } if (NatpConfigurationChangedWaitHandle) { RtlDeregisterWait(NatpConfigurationChangedWaitHandle); NatpConfigurationChangedWaitHandle = NULL; } if (NatConfigurationChangedEvent) { CloseHandle(NatConfigurationChangedEvent); NatConfigurationChangedEvent = NULL; }
LeaveCriticalSection(&NatInterfaceLock); DEREFERENCE_NAT(); DEREFERENCE_NAT();
return Error;
} // NatStartConnectionManagement
VOID NatStopConnectionManagement( VOID )
/*++
Routine Description:
This routine is invoked to stop the connection-monitoring activity initiated by 'NatStartConnectionManagement' above.
Arguments:
none.
Return Value:
none.
Environment:
Invoked when 'StopProtocol' is received from the IP router-manager.
--*/
{ PLIST_ENTRY Link; PNAT_CONNECTION_ENTRY pConEntry; PROFILE("NatStopConnectionManagement");
EnterCriticalSection(&NatInterfaceLock);
//
// Cleanup the wait-handle and event used to receive notification
// of RAS connections and disconnections.
//
if (NatpConnectionNotifyWaitHandle) { RtlDeregisterWait(NatpConnectionNotifyWaitHandle); NatpConnectionNotifyWaitHandle = NULL; }
if (NatConnectionNotifyEvent) { RasConnectionNotification( (HRASCONN)INVALID_HANDLE_VALUE, NatConnectionNotifyEvent, 0 ); CloseHandle(NatConnectionNotifyEvent); NatConnectionNotifyEvent = NULL; NatpConnectionNotifyCallbackRoutine(NULL, FALSE); }
if (NatpEnableRouterWaitHandle) { RtlDeregisterWait(NatpEnableRouterWaitHandle); NatpEnableRouterWaitHandle = NULL; }
if (NatpEnableRouterEvent) { CloseHandle(NatpEnableRouterEvent); NatpEnableRouterEvent = NULL; NatpEnableRouterCallbackRoutine(NULL, FALSE); }
if (NatpConfigurationChangedWaitHandle) { RtlDeregisterWait(NatpConfigurationChangedWaitHandle); NatpConfigurationChangedWaitHandle = NULL; }
if (NatConfigurationChangedEvent) { CloseHandle(NatConfigurationChangedEvent); NatConfigurationChangedEvent = NULL; NatpConfigurationChangedCallbackRoutine(NULL, FALSE); }
if (NatpConnectionList.Flink) { //
// Make certain that all of our connections are disabled
//
NatUnbindAllConnections();
//
// Walk through the connection list, freeing all of the entries
//
while (!IsListEmpty(&NatpConnectionList)) { Link = RemoveHeadList(&NatpConnectionList); pConEntry = CONTAINING_RECORD(Link, NAT_CONNECTION_ENTRY, Link); NatpFreeConnectionEntry(pConEntry); }
//
// Make sure all ICS protocols are stopped
//
NhStopICSProtocols(); }
//
// Clean up the DNS domain name cached for the shared connection.
//
if (NatpSharedConnectionDomainName) { NH_FREE(NatpSharedConnectionDomainName); NatpSharedConnectionDomainName = NULL; }
//
// Reset tracking variables to initial state
//
NatpFirewallConnectionCount = 0; NatpSharedConnectionPresent = FALSE;
LeaveCriticalSection(&NatInterfaceLock);
} // NatStopConnectionManagement
BOOLEAN NatUnbindAllConnections( VOID )
/*++
Routine Description:
This routine is invoked to unbind a currently-active connection.
Arguments:
Index - index into the connection array
Return Value:
BOOLEAN - TRUE if any interfaces were unbound.
Environment:
Invoked with 'NatInterfaceLock' held by the caller.
--*/
{
PLIST_ENTRY Link; PNAT_CONNECTION_ENTRY pConEntry; BOOLEAN Result = FALSE; PROFILE("NatUnbindAllConnections");
for (Link = NatpConnectionList.Flink; Link != &NatpConnectionList; Link = Link->Flink) { pConEntry = CONTAINING_RECORD(Link, NAT_CONNECTION_ENTRY, Link); Result |= NatpUnbindConnection(pConEntry); }
return Result; } // NatpUnbindConnection
|