mirror of https://github.com/tongzx/nt5src
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.
3439 lines
83 KiB
3439 lines
83 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);
|
|
}
|
|
|
|
#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
|