Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3571 lines
90 KiB

/*++
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);
}
//
// 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))
{
hr = NatpGetTargetAddressForPortMappingEntry(
pConEntry->HNetProperties.fIcsPublic,
pEntry->fNameActive,
pBindingInfo->Address[0].Address,
pBinding,
&pEntry->ulPrivateAddress
);
}
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
HRESULT
NatpGetTargetAddressForPortMappingEntry(
BOOLEAN fPublic,
BOOLEAN fNameActive,
ULONG BindingAddress,
IHNetPortMappingBinding *pBinding,
OUT ULONG *pPrivateAddress
)
/*++
Routine Description:
This routine is invoked to get the binding address for the port mapping.
If this is a FW only connection, we return the address from the binding
info and not the protocol binding. If its a public connection, and the
target address is a client machine, we return its address. We check to
see if we can find the clients' current address, if so, we choose it
instead of the address currently in our store - refresh of the WMI store
is done as needed.
Arguments:
fPublic - if the interface is public or not
fNameActive - if the mapping is active by name (TRUE) or IP address (FALSE)
BindingAddress - binding address from BindingInfo
pBinding - pointer to port mapping binding
pPrivateAddress - receives relevant binding address
Return Value:
Standard HRESULT
--*/
{
HRESULT hr = S_OK;
//
// If this is a FW-only connection, use the address from
// our binding info instead of the protocol binding.
//
if (!fPublic)
{
*pPrivateAddress = BindingAddress;
}
else
{
hr = pBinding->GetTargetComputerAddress(pPrivateAddress);
if (SUCCEEDED(hr))
{
if (INADDR_LOOPBACK_NO == *pPrivateAddress)
{
//
// If the port mapping targets the loopback address
// we want to use the address from the binding
// info instead.
//
*pPrivateAddress = BindingAddress;
}
else if (fNameActive)
{
PWCHAR pszICSDomainSuffix = NULL;
NTSTATUS status;
PWSTR pszName = NULL;
ULONG CurrentAddress = 0;
ULONG DhcpScopeAddress = 0;
ULONG DhcpScopeMask = 0;
IHNetCfgMgr *pCfgMgr = NULL;
IHNetIcsSettings *pIcsSettings = NULL;
do
{
//
// Get computer name
//
hr = pBinding->GetTargetComputerName(
&pszName
);
if (FAILED(hr))
{
break;
}
//
// Get the ICS Domain Suffix (if any)
//
status = NhQueryICSDomainSuffix(
&pszICSDomainSuffix
);
if (!NT_SUCCESS(status))
{
//
// error in creating the suffix string
//
break;
}
hr = NhGetHNetCfgMgr(&pCfgMgr);
if (FAILED(hr))
{
break;
}
//
// Get the ICS settings interface
//
hr = pCfgMgr->QueryInterface(
IID_PPV_ARG(IHNetIcsSettings, &pIcsSettings)
);
if (FAILED(hr))
{
break;
}
//
// Get DHCP scope information
//
hr = pIcsSettings->GetDhcpScopeSettings(
&DhcpScopeAddress,
&DhcpScopeMask
);
if (FAILED(hr))
{
break;
}
CurrentAddress = NhQueryHostByName(
pszName,
pszICSDomainSuffix,
(DhcpScopeAddress & DhcpScopeMask),
DhcpScopeMask
);
//
// keep the current address if the following match
// (1) its non-zero
// (2) it doesnt match what has already been reserved
//
if (CurrentAddress && (CurrentAddress != *pPrivateAddress))
{
hr = pIcsSettings->RefreshTargetComputerAddress(
pszName,
CurrentAddress
);
if (SUCCEEDED(hr))
{
NhTrace(
TRACE_FLAG_NAT,
"NatpGetTargetAddressForPortMappingEntry: "
"old address (0x%08x) "
"new address (0x%08x) for %S",
*pPrivateAddress,
CurrentAddress,
pszName
);
*pPrivateAddress = CurrentAddress;
}
}
} while (FALSE);
if (pszName)
{
CoTaskMemFree(pszName);
}
if (NULL != pszICSDomainSuffix)
{
NH_FREE(pszICSDomainSuffix);
}
if (NULL != pIcsSettings)
{
pIcsSettings->Release();
}
if (NULL != pCfgMgr)
{
pCfgMgr->Release();
}
}
}
}
return hr;
}
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"
);
}
if (AdapterIndex == (ULONG)-1) {
NhTrace(
TRACE_FLAG_NAT,
"NatpProcessConnectionNotify: MapGuidToAdapter failed"
);
Active = FALSE;
} else {
BindingInfo = NhQueryBindingInformation(AdapterIndex);
if (!BindingInfo) {
NhTrace(
TRACE_FLAG_NAT,
"NatpProcessConnectionNotify: QueryBinding failed"
);
Active = FALSE;
} else if (0 == BindingInfo->AddressCount) {
NhTrace(
TRACE_FLAG_NAT,
"NatpProcessConnectionNotify: Adapter has no addresses"
);
Active = FALSE;
NH_FREE(BindingInfo);
BindingInfo = NULL;
} 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 ||
!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 && NhPolicyAllowsSharing) {
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;
NTSTATUS Status;
UNICODE_STRING UnicodeString;
if (pConEntry->HNetProperties.fLanConnection) {
Status = RtlStringFromGUID(pConEntry->Guid, &UnicodeString);
if (NT_SUCCESS(Status)) {
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
);
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.
--*/
{
PDNS_ADAPTER_INFOA AdapterInformation;
ANSI_STRING AnsiString;
ULONG Count;
ULONG Error;
ULONG i;
PDNS_NETWORK_INFOA NetworkInformation = NULL;
NTSTATUS Status;
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; }
Status = RtlStringFromGUID(Table[i].DeviceGuid, &UnicodeString);
if (!NT_SUCCESS(Status)) { break; }
Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, TRUE);
if (!NT_SUCCESS(Status)) { break; }
//
// 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_INFOA)
DnsQueryConfigAlloc(
DnsConfigNetworkInfoA,
NULL );
if (!NetworkInformation) { Error = ERROR_INTERNAL_ERROR; break; }
for (i = 0; i < NetworkInformation->AdapterCount; i++) {
AdapterInformation = &NetworkInformation->AdapterArray[i];
if (lstrcmpiA(
AnsiString.Buffer,
AdapterInformation->pszAdapterGuidName
) == 0) {
break;
}
}
if (i >= NetworkInformation->AdapterCount) {
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->pszAdapterDomain) {
NatpSharedConnectionDomainName =
reinterpret_cast<PCHAR>(
NH_ALLOCATE(lstrlenA(AdapterInformation->pszAdapterDomain) + 1)
);
if (!NatpSharedConnectionDomainName) {
Error = ERROR_INTERNAL_ERROR;
break;
}
lstrcpyA(
NatpSharedConnectionDomainName,
AdapterInformation->pszAdapterDomain
);
}
Error = NO_ERROR;
} while(FALSE);
if (UnicodeString.Buffer) {
RtlFreeUnicodeString(&UnicodeString);
}
if (AnsiString.Buffer) {
RtlFreeAnsiString(&AnsiString);
}
if (NetworkInformation) {
DnsFreeConfigStructure(
NetworkInformation,
// DnsConfigNetworkInformation );
DnsConfigNetworkInfoA ); // <--- jwesth: this seems to be the correct freetype
}
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