Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

12627 lines
329 KiB

/***************************************************************************
*
* Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved.
*
* File: dpnhpastintfobj.cpp
*
* Content: DPNHPAST main interface object class.
*
* History:
* Date By Reason
* ======== ======== =========
* 04/16/01 VanceO Split DPNATHLP into DPNHUPNP and DPNHPAST.
*
***************************************************************************/
#include "dpnhpasti.h"
//=============================================================================
// Definitions
//=============================================================================
#define DEFAULT_INITIAL_PAST_RETRY_TIMEOUT 25000 // start retry timer at 25 ms
#define PAST_CONNECT_RETRY_INTERVAL_MS 250 // 250 ms
#define PAST_CONNECT_RETRY_INTERVAL_US (PAST_CONNECT_RETRY_INTERVAL_MS * 1000)
#define MAX_NUM_PAST_TRIES_CONNECT 2 // how many times to send a registration request message, in case of packet loss
#define MAX_PAST_RETRY_TIME_US 250000 // max retry interval is 250 ms
#define MAX_NUM_PAST_TRIES 5
#define PAST_RESPONSE_BUFFER_SIZE 150
#define LEASE_RENEW_TIME 120000 // renew if less than 2 minutes remaining
#define FAKE_PORT_LEASE_TIME 300000 // 5 minutes
#define IOCOMPLETE_WAIT_INTERVAL 100 // 100 ms between attempts
#define MAX_NUM_IOCOMPLETE_WAITS 10 // wait at most 1 second
#define MAX_RESERVED_PORT 1024
#define MAX_NUM_RANDOM_PORT_TRIES 5
#ifndef DPNBUILD_NOWINSOCK2
//=============================================================================
// WinSock 1 version of IP options
//=============================================================================
#define IP_TTL_WINSOCK1 7
#endif // ! DPNBUILD_NOWINSOCK2
//=============================================================================
// Macros
//=============================================================================
//#ifdef _X86
#define IS_CLASSD_IPV4_ADDRESS(dwAddr) (( (*((BYTE*) &(dwAddr))) & 0xF0) == 0xE0) // 1110 high bits or 224.0.0.0 - 239.255.255.255 multicast address, in network byte order
#define NTOHS(x) ( (((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00) )
#define HTONS(x) NTOHS(x)
//#endif _X86
//=============================================================================
// Local static variables
//=============================================================================
static timeval s_tv0 = {0, 0};
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::CNATHelpPAST"
//=============================================================================
// CNATHelpPAST constructor
//-----------------------------------------------------------------------------
//
// Description: Initializes the new CNATHelpPAST object.
//
// Arguments:
// BOOL fNotCreatedWithCOM - TRUE if this object is being instantiated
// without COM, FALSE if it is through COM.
//
// Returns: None (the object).
//=============================================================================
CNATHelpPAST::CNATHelpPAST(const BOOL fNotCreatedWithCOM)
{
this->m_blList.Initialize();
this->m_Sig[0] = 'N';
this->m_Sig[1] = 'A';
this->m_Sig[2] = 'T';
this->m_Sig[3] = 'H';
this->m_lRefCount = 1; // someone must have a pointer to this object
if (fNotCreatedWithCOM)
{
this->m_dwFlags = NATHELPPASTOBJ_NOTCREATEDWITHCOM;
}
else
{
this->m_dwFlags = 0;
}
this->m_dwLockThreadID = 0;
this->m_hAlertEvent = NULL;
this->m_hAlertIOCompletionPort = NULL;
this->m_dwAlertCompletionKey = 0;
this->m_blDevices.Initialize();
this->m_blRegisteredPorts.Initialize();
this->m_blUnownedPorts.Initialize();
this->m_dwLastUpdateServerStatusTime = 0;
this->m_dwNextPollInterval = 0;
this->m_dwNumLeases = 0;
this->m_dwEarliestLeaseExpirationTime = 0;
this->m_hIpHlpApiDLL = NULL;
this->m_pfnGetAdaptersInfo = NULL;
this->m_pfnGetIpForwardTable = NULL;
this->m_pfnGetBestRoute = NULL;
this->m_sIoctls = INVALID_SOCKET;
this->m_wIoctlSocketPort = 0;
this->m_polAddressListChange = NULL;
this->m_hWinSockDLL = NULL;
this->m_pfnWSAStartup = NULL;
this->m_pfnWSACleanup = NULL;
this->m_pfnWSAGetLastError = NULL;
this->m_pfnsocket = NULL;
this->m_pfnclosesocket = NULL;
this->m_pfnbind = NULL;
this->m_pfnsetsockopt = NULL;
this->m_pfngetsockname = NULL;
this->m_pfnselect = NULL;
this->m_pfn__WSAFDIsSet = NULL;
this->m_pfnrecvfrom = NULL;
this->m_pfnsendto = NULL;
this->m_pfngethostname = NULL;
this->m_pfngethostbyname = NULL;
this->m_pfninet_addr = NULL;
this->m_pfnWSASocketA = NULL;
this->m_pfnWSAIoctl = NULL;
this->m_pfnWSAGetOverlappedResult = NULL;
#ifdef DBG
this->m_dwNumDeviceAdds = 0;
this->m_dwNumDeviceRemoves = 0;
this->m_dwNumServerFailures = 0;
#endif // DBG
} // CNATHelpPAST::CNATHelpPAST
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::~CNATHelpPAST"
//=============================================================================
// CNATHelpPAST destructor
//-----------------------------------------------------------------------------
//
// Description: Frees the CNATHelpPAST object.
//
// Arguments: None.
//
// Returns: None.
//=============================================================================
CNATHelpPAST::~CNATHelpPAST(void)
{
DPFX(DPFPREP, 7, "(0x%p) NumDeviceAdds = %u, NumDeviceRemoves = %u, NumServerFailures = %u",
this, this->m_dwNumDeviceAdds, this->m_dwNumDeviceRemoves,
this->m_dwNumServerFailures);
DNASSERT(this->m_blList.IsEmpty());
DNASSERT(this->m_lRefCount == 0);
DNASSERT((this->m_dwFlags & ~NATHELPPASTOBJ_NOTCREATEDWITHCOM) == 0);
DNASSERT(this->m_dwLockThreadID == 0);
DNASSERT(this->m_hAlertEvent == NULL);
DNASSERT(this->m_hAlertIOCompletionPort == NULL);
DNASSERT(this->m_blDevices.IsEmpty());
DNASSERT(this->m_blRegisteredPorts.IsEmpty());
DNASSERT(this->m_blUnownedPorts.IsEmpty());
DNASSERT(this->m_dwNumLeases == 0);
DNASSERT(this->m_hIpHlpApiDLL == NULL);
DNASSERT(this->m_hWinSockDLL == NULL);
DNASSERT(this->m_sIoctls == INVALID_SOCKET);
DNASSERT(this->m_polAddressListChange == NULL);
//
// For grins, change the signature before deleting the object.
//
this->m_Sig[3] = 'h';
} // CNATHelpPAST::~CNATHelpPAST
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::QueryInterface"
//=============================================================================
// CNATHelpPAST::QueryInterface
//-----------------------------------------------------------------------------
//
// Description: Retrieves a new reference for an interfaces supported by this
// CNATHelpPAST object.
//
// Arguments:
// REFIID riid - Reference to interface ID GUID.
// LPVOID * ppvObj - Place to store pointer to object.
//
// Returns: HRESULT
// S_OK - Returning a valid interface pointer.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPOINTER - The destination pointer is invalid.
// E_NOINTERFACE - Invalid interface was specified.
//=============================================================================
STDMETHODIMP CNATHelpPAST::QueryInterface(REFIID riid, LPVOID * ppvObj)
{
HRESULT hr = DPNH_OK;
DPFX(DPFPREP, 3, "(0x%p) Parameters: (REFIID, 0x%p)", this, ppvObj);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid NATHelper object!");
hr = DPNHERR_INVALIDOBJECT;
goto Failure;
}
//
// Validate the parameters.
//
if ((! IsEqualIID(riid, IID_IUnknown)) &&
(! IsEqualIID(riid, IID_IDirectPlayNATHelp)))
{
DPFX(DPFPREP, 0, "Unsupported interface!");
hr = E_NOINTERFACE;
goto Failure;
}
if ((ppvObj == NULL) ||
(IsBadWritePtr(ppvObj, sizeof(void*))))
{
DPFX(DPFPREP, 0, "Invalid interface pointer specified!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
//
// Add a reference, and return the interface pointer (which is actually
// just the object pointer, they line up because CNATHelpPAST inherits from
// the interface declaration).
//
this->AddRef();
(*ppvObj) = this;
Exit:
DPFX(DPFPREP, 3, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::QueryInterface
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::AddRef"
//=============================================================================
// CNATHelpPAST::AddRef
//-----------------------------------------------------------------------------
//
// Description: Adds a reference to this CNATHelpPAST object.
//
// Arguments: None.
//
// Returns: New refcount.
//=============================================================================
STDMETHODIMP_(ULONG) CNATHelpPAST::AddRef(void)
{
LONG lRefCount;
DNASSERT(this->IsValidObject());
//
// There must be at least 1 reference to this object, since someone is
// calling AddRef.
//
DNASSERT(this->m_lRefCount > 0);
lRefCount = InterlockedIncrement(&this->m_lRefCount);
DPFX(DPFPREP, 3, "[0x%p] RefCount [0x%lx]", this, lRefCount);
return lRefCount;
} // CNATHelpPAST::AddRef
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::Release"
//=============================================================================
// CNATHelpPAST::Release
//-----------------------------------------------------------------------------
//
// Description: Removes a reference to this CNATHelpPAST object. When the
// refcount reaches 0, this object is destroyed.
// You must NULL out your pointer to this object after calling
// this function.
//
// Arguments: None.
//
// Returns: New refcount.
//=============================================================================
STDMETHODIMP_(ULONG) CNATHelpPAST::Release(void)
{
LONG lRefCount;
DNASSERT(this->IsValidObject());
//
// There must be at least 1 reference to this object, since someone is
// calling Release.
//
DNASSERT(this->m_lRefCount > 0);
lRefCount = InterlockedDecrement(&this->m_lRefCount);
//
// Was that the last reference? If so, we're going to destroy this object.
//
if (lRefCount == 0)
{
DPFX(DPFPREP, 3, "[0x%p] RefCount hit 0, destroying object.", this);
//
// First pull it off the global list.
//
DNEnterCriticalSection(&g_csGlobalsLock);
this->m_blList.RemoveFromList();
DNASSERT(g_lOutstandingInterfaceCount > 0);
g_lOutstandingInterfaceCount--; // update count so DLL can unload now works correctly
DNLeaveCriticalSection(&g_csGlobalsLock);
//
// Make sure it's closed.
//
if (this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED)
{
//
// Assert so that the user can fix his/her broken code!
//
DNASSERT(! "DirectPlayNATHelpPAST object being released without calling Close first!");
//
// Then go ahead and do the right thing. Ignore error, we can't do
// much about it.
//
this->Close(0);
}
//
// Then uninitialize the object.
//
this->UninitializeObject();
//
// Finally delete this (!) object.
//
delete this;
}
else
{
DPFX(DPFPREP, 3, "[0x%p] RefCount [0x%lx]", this, lRefCount);
}
return lRefCount;
} // CNATHelpPAST::Release
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::Initialize"
//=============================================================================
// CNATHelpPAST::Initialize
//-----------------------------------------------------------------------------
//
// Description: Prepares the object for use. No attempt is made to contact
// any Internet gateway servers at this time. The user should
// call GetCaps with the DPNHGETCAPS_UPDATESERVERSTATUS flag to
// search for a server.
//
// Initialize must be called before using any other function,
// and must be balanced with a call to Close. Initialize can only
// be called once unless Close returns it to the uninitialized
// state.
//
// One of DPNHINITIALIZE_DISABLEREMOTENATSUPPORT or
// DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT may be specified,
// but not both.
//
// Arguments:
// DWORD dwFlags - Flags to use when initializing.
//
// Returns: HRESULT
// DPNH_OK - Initialization was successful.
// DPNHERR_ALREADYINITIALIZED - Initialize has already been called.
// DPNHERR_GENERIC - An error occurred while initializing.
// DPNHERR_INVALIDFLAGS - Invalid flags were specified.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPARAM - An invalid parameter was specified.
// DPNHERR_OUTOFMEMORY - There is not enough memory to initialize.
// DPNHERR_REENTRANT - The interface has been re-entered on the same
// thread.
//=============================================================================
STDMETHODIMP CNATHelpPAST::Initialize(const DWORD dwFlags)
{
HRESULT hr;
BOOL fHaveLock = FALSE;
BOOL fSetFlags = FALSE;
BOOL fWinSockStarted = FALSE;
WSADATA wsadata;
int iError;
SOCKADDR_IN saddrinTemp;
int iAddressSize;
#ifdef DBG
DWORD dwError;
#endif // DBG
DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%lx)", this, dwFlags);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
hr = DPNHERR_INVALIDOBJECT;
//
// Skip the failure cleanup code, we haven't set anything up.
//
goto Exit;
}
//
// Validate the parameters.
//
if (dwFlags & ~(DPNHINITIALIZE_DISABLEGATEWAYSUPPORT | DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT))
{
DPFX(DPFPREP, 0, "Invalid flags specified!");
hr = DPNHERR_INVALIDFLAGS;
//
// Skip the failure cleanup code, we haven't set anything up.
//
goto Exit;
}
//
// Both flags cannot be specified at the same time. If the caller doesn't
// want any NAT functionality, why use this object all?
//
if ((dwFlags & (DPNHINITIALIZE_DISABLEGATEWAYSUPPORT | DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT)) == (DPNHINITIALIZE_DISABLEGATEWAYSUPPORT | DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT))
{
DPFX(DPFPREP, 0, "Either DISABLEGATEWAYSUPPORT flag or DISABLELOCALFIREWALLSUPPORT flag can be used, but not both!");
hr = DPNHERR_INVALIDFLAGS;
//
// Skip the failure cleanup code, we haven't set anything up.
//
goto Exit;
}
//
// Attempt to take the lock, but be prepared for the re-entrancy error.
//
hr = this->TakeLock();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Could not lock object!");
//
// Skip the failure cleanup code, we haven't set anything up.
//
goto Exit;
}
fHaveLock = TRUE;
//
// Make sure object is in right state.
//
if ((this->m_dwFlags & ~NATHELPPASTOBJ_NOTCREATEDWITHCOM) != 0)
{
DPFX(DPFPREP, 0, "Object already initialized!");
hr = DPNHERR_ALREADYINITIALIZED;
//
// Skip the failure cleanup code, we haven't set anything up.
//
goto Exit;
}
//
// Read in the manual override settings from the registry
//
ReadRegistrySettings();
//
// We're not completely initialized yet, but set the flag(s) now.
//
this->m_dwFlags |= NATHELPPASTOBJ_INITIALIZED;
fSetFlags = TRUE;
//
// Store the user's settings.
//
if (dwFlags & DPNHINITIALIZE_DISABLEGATEWAYSUPPORT)
{
DPFX(DPFPREP, 1, "User requested that Internet gateways not be supported.");
}
else
{
this->m_dwFlags |= NATHELPPASTOBJ_USEPASTICS;
}
if (dwFlags & DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT)
{
DPFX(DPFPREP, 1, "User requested that local PAST PFW not be supported.");
}
else
{
this->m_dwFlags |= NATHELPPASTOBJ_USEPASTPFW;
}
//
// Take into account the registry override settings.
//
switch (g_dwPASTICSMode)
{
case OVERRIDEMODE_FORCEON:
{
//
// Force PAST ICS on.
//
DPFX(DPFPREP, 1, "Forcing PAST ICS support on.");
this->m_dwFlags |= NATHELPPASTOBJ_USEPASTICS;
break;
}
case OVERRIDEMODE_FORCEOFF:
{
//
// Force PAST ICS off.
//
DPFX(DPFPREP, 1, "Forcing PAST ICS support off.");
this->m_dwFlags &= ~NATHELPPASTOBJ_USEPASTICS;
break;
}
default:
{
//
// Leave PAST ICS settings alone.
//
break;
}
}
switch (g_dwPASTPFWMode)
{
case OVERRIDEMODE_FORCEON:
{
//
// Force PAST PFW on.
//
DPFX(DPFPREP, 1, "Forcing PAST PFW support on.");
this->m_dwFlags |= NATHELPPASTOBJ_USEPASTPFW;
break;
}
case OVERRIDEMODE_FORCEOFF:
{
//
// Force PAST PFW off.
//
DPFX(DPFPREP, 1, "Forcing PAST PFW support off.");
this->m_dwFlags &= ~NATHELPPASTOBJ_USEPASTPFW;
break;
}
default:
{
//
// Leave PAST PFW settings alone.
//
break;
}
}
//
// Try loading the IP helper DLL.
//
this->m_hIpHlpApiDLL = LoadLibrary( _T("iphlpapi.dll") );
if (this->m_hIpHlpApiDLL == NULL)
{
#ifdef DBG
dwError = GetLastError();
DPFX(DPFPREP, 1, "Unable to load \"iphlpapi.dll\" (error = 0x%lx).",
dwError);
#endif // DBG
//
// That's not fatal, we can still function.
//
}
else
{
//
// Load the functions we'll use.
//
this->m_pfnGetAdaptersInfo = (PFN_GETADAPTERSINFO) GetProcAddress(this->m_hIpHlpApiDLL,
"GetAdaptersInfo");
if (this->m_pfnGetAdaptersInfo == NULL)
{
#ifdef DBG
dwError = GetLastError();
DPFX(DPFPREP, 0, "Unable to get \"GetAdaptersInfo\" function (error = 0x%lx)!",
dwError);
#endif // DBG
goto Exit;
}
this->m_pfnGetIpForwardTable = (PFN_GETIPFORWARDTABLE) GetProcAddress(this->m_hIpHlpApiDLL,
"GetIpForwardTable");
if (this->m_pfnGetIpForwardTable == NULL)
{
#ifdef DBG
dwError = GetLastError();
DPFX(DPFPREP, 0, "Unable to get \"GetIpForwardTable\" function (error = 0x%lx)!",
dwError);
#endif // DBG
goto Exit;
}
this->m_pfnGetBestRoute = (PFN_GETBESTROUTE) GetProcAddress(this->m_hIpHlpApiDLL,
"GetBestRoute");
if (this->m_pfnGetBestRoute == NULL)
{
#ifdef DBG
dwError = GetLastError();
DPFX(DPFPREP, 0, "Unable to get \"GetBestRoute\" function (error = 0x%lx)!",
dwError);
#endif // DBG
goto Exit;
}
}
//
// Load WinSock, we always need it.
//
this->m_hWinSockDLL = LoadLibrary( _T("ws2_32.dll") );
if (this->m_hWinSockDLL == NULL)
{
#ifdef DBG
dwError = GetLastError();
DPFX(DPFPREP, 1, "Couldn't load \"ws2_32.dll\" (err = 0x%lx), resorting to WinSock 1 functionality.",
dwError);
#endif // DBG
this->m_hWinSockDLL = LoadLibrary( _T("wsock32.dll") );
if (this->m_hWinSockDLL == NULL)
{
#ifdef DBG
dwError = GetLastError();
DPFX(DPFPREP, 0, "Couldn't load \"wsock32.dll\" either (err = 0x%lx)!.",
dwError);
#endif // DBG
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Remember that we had to resort to WinSock 1.
//
this->m_dwFlags |= NATHELPPASTOBJ_WINSOCK1;
}
else
{
DPFX(DPFPREP, 1, "Loaded \"ws2_32.dll\", using WinSock 2 functionality.");
}
//
// Load pointers to all the functions we use in WinSock.
//
hr = this->LoadWinSockFunctionPointers();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't load WinSock function pointers!");
goto Failure;
}
//
// Fire up WinSock. Request 2.2 if we can. For the most part we only use
// version 1.1 capabilities and interfaces anyway. The only exceptions are
// using using the event or I/O completion port handles for notification.
//
ZeroMemory(&wsadata, sizeof(wsadata));
if (this->m_dwFlags & NATHELPPASTOBJ_WINSOCK1)
{
iError = this->m_pfnWSAStartup(MAKEWORD(1, 1), &wsadata);
}
else
{
iError = this->m_pfnWSAStartup(MAKEWORD(2, 2), &wsadata);
}
if (iError != 0)
{
DPFX(DPFPREP, 0, "Couldn't startup WinSock (error = %i)!", iError);
hr = DPNHERR_GENERIC;
goto Failure;
}
fWinSockStarted = TRUE;
DPFX(DPFPREP, 4, "Initialized WinSock version %u.%u.",
LOBYTE(wsadata.wVersion), HIBYTE(wsadata.wVersion));
//
// Try creating a UDP socket for use with WSAIoctl. Do this even if we're
// WinSock 1 and can't use WSAIoctl socket. This allows us to make sure
// TCP/IP is installed and working.
//
this->m_sIoctls = this->m_pfnsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (this->m_sIoctls == INVALID_SOCKET)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't create Ioctl socket, error = %u!", dwError);
#endif // DBG
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Try binding the socket. This is a continuation of the validation.
//
ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
saddrinTemp.sin_family = AF_INET;
//saddrinTemp.sin_addr.S_un.S_addr = INADDR_ANY;
//saddrinTemp.sin_port = 0;
if (this->m_pfnbind(this->m_sIoctls,
(SOCKADDR *) (&saddrinTemp),
sizeof(saddrinTemp)) != 0)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't bind the Ioctl socket to arbitrary port on any interface, error = %u!",
dwError);
#endif // DBG
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Get the address + port tuple actually bound.
//
iAddressSize = sizeof(saddrinTemp);
if (this->m_pfngetsockname(this->m_sIoctls,
(SOCKADDR*) (&saddrinTemp),
&iAddressSize) != 0)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't get socket name, error = %u!", dwError);
#endif // DBG
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// WinSock needs to have bound to all adapters (because we told it to).
//
DNASSERT(saddrinTemp.sin_addr.S_un.S_addr == INADDR_ANY);
this->m_wIoctlSocketPort = saddrinTemp.sin_port;
DPFX(DPFPREP, 8, "Bound Ioctl socket to port %u.",
NTOHS(this->m_wIoctlSocketPort));
//
// Build the list of IP capable devices.
//
hr = this->CheckForNewDevices(NULL);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't build device list!");
goto Failure;
}
//
// We could technically try to contact PAST servers right now, but we don't
// because it's a slow blocking operation, and users have to call GetCaps
// at least once anyway.
//
Exit:
if (fHaveLock)
{
this->DropLock();
fHaveLock = FALSE;
}
DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
this->RemoveAllItems();
if (this->m_sIoctls != INVALID_SOCKET)
{
this->m_pfnclosesocket(this->m_sIoctls); // ignore error
this->m_sIoctls = INVALID_SOCKET;
}
if (fWinSockStarted)
{
this->m_pfnWSACleanup(); // ignore error
}
if (this->m_hWinSockDLL != NULL)
{
this->m_pfnWSAStartup = NULL;
this->m_pfnWSACleanup = NULL;
this->m_pfnWSAGetLastError = NULL;
this->m_pfnsocket = NULL;
this->m_pfnclosesocket = NULL;
this->m_pfnbind = NULL;
this->m_pfnsetsockopt = NULL;
this->m_pfngetsockname = NULL;
this->m_pfnselect = NULL;
this->m_pfn__WSAFDIsSet = NULL;
this->m_pfnrecvfrom = NULL;
this->m_pfnsendto = NULL;
this->m_pfngethostname = NULL;
this->m_pfngethostbyname = NULL;
this->m_pfninet_addr = NULL;
this->m_pfnWSASocketA = NULL;
this->m_pfnWSAIoctl = NULL;
this->m_pfnWSAGetOverlappedResult = NULL;
this->m_dwFlags &= ~NATHELPPASTOBJ_WINSOCK1;
FreeLibrary(this->m_hWinSockDLL);
this->m_hWinSockDLL = NULL;
}
if (this->m_hIpHlpApiDLL != NULL)
{
this->m_pfnGetAdaptersInfo = NULL;
this->m_pfnGetIpForwardTable = NULL;
this->m_pfnGetBestRoute = NULL;
FreeLibrary(this->m_hIpHlpApiDLL);
this->m_hIpHlpApiDLL = NULL;
}
if (fSetFlags)
{
this->m_dwFlags &= ~(NATHELPPASTOBJ_INITIALIZED |
NATHELPPASTOBJ_USEPASTICS |
NATHELPPASTOBJ_USEPASTPFW |
NATHELPPASTOBJ_DEVICECHANGED);
}
goto Exit;
} // CNATHelpPAST::Initialize
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::Close"
//=============================================================================
// CNATHelpPAST::Close
//-----------------------------------------------------------------------------
//
// Description: Shuts down and de-registers this application with any
// Internet gateway servers. All port assignments are implicitly
// freed as a result of this operation.
//
// This must balance a successful call to Initialize.
//
// Arguments:
// DWORD dwFlags - Unused, must be zero.
//
// Returns: HRESULT
// DPNH_OK - Closing the helper API was successful.
// DPNHERR_GENERIC - An error occurred while closing.
// DPNHERR_INVALIDFLAGS - Invalid flags were specified.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPARAM - An invalid parameter was specified.
// DPNHERR_NOTINITIALIZED - Initialize has not been called.
// DPNHERR_OUTOFMEMORY - There is not enough memory to close.
// DPNHERR_REENTRANT - The interface has been re-entered on the same
// thread.
//=============================================================================
STDMETHODIMP CNATHelpPAST::Close(const DWORD dwFlags)
{
HRESULT hr;
BOOL fHaveLock = FALSE;
int iError;
#ifdef DBG
DWORD dwError;
#endif // DBG
DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%lx)", this, dwFlags);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
hr = DPNHERR_INVALIDOBJECT;
goto Failure;
}
//
// Validate the parameters.
//
if (dwFlags != 0)
{
DPFX(DPFPREP, 0, "Invalid flags specified!");
hr = DPNHERR_INVALIDFLAGS;
goto Failure;
}
//
// Attempt to take the lock, but be prepared for the re-entrancy error.
//
hr = this->TakeLock();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Could not lock object!");
goto Failure;
}
fHaveLock = TRUE;
//
// Make sure object is in right state.
//
if (! (this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED) )
{
DPFX(DPFPREP, 0, "Object not initialized!");
hr = DPNHERR_NOTINITIALIZED;
goto Failure;
}
//
// We need to actively deregister any devices which are registered with
// Internet gateways.
//
this->RemoveAllItems();
//
// Close the Ioctl socket.
//
DNASSERT(this->m_sIoctls != INVALID_SOCKET);
this->m_pfnclosesocket(this->m_sIoctls); // ignore error
this->m_sIoctls = INVALID_SOCKET;
//
// If we submitted overlapped I/O, see if it got cancelled.
//
if (this->m_polAddressListChange != NULL)
{
OSVERSIONINFO osvi;
OSVERSIONINFOEX osvix;
BOOL fCanWait;
DWORD dwAttempt;
ZeroMemory(&osvi, sizeof(osvi));
osvi.dwOSVersionInfoSize = sizeof(osvi);
if (GetVersionEx(&osvi))
{
//
// Any platform but Win2K Gold, Win2K + SP1, or Win2K + SP2 can
// just go ahead and wait for the I/O to complete.
//
if ((osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) ||
(osvi.dwMajorVersion > 5) ||
(osvi.dwMinorVersion > 0))
{
DPFX(DPFPREP, 3, "Windows %s version %u.%u detected, waiting for address list change Ioctl to complete.",
((osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) ? _T("9x") : _T("NT")),
osvi.dwMajorVersion, osvi.dwMinorVersion);
fCanWait = TRUE;
}
else
{
//
// Win2K versions < SP3 have a bug where the I/O is not always
// cancelled by closing the socket. We can't wait for the
// completion, sometimes it doesn't happen.
//
fCanWait = FALSE;
ZeroMemory(&osvix, sizeof(osvix));
osvix.dwOSVersionInfoSize = sizeof(osvix);
if (GetVersionEx((LPOSVERSIONINFO) (&osvix)))
{
//
// If SP3 or later is applied, we know it's fixed.
//
if (osvix.wServicePackMajor >= 3)
{
DPFX(DPFPREP, 3, "Windows 2000 Service Pack %u detected, waiting for address list change Ioctl to complete.",
osvix.wServicePackMajor);
fCanWait = TRUE;
}
#ifdef DBG
else
{
if (osvix.wServicePackMajor == 0)
{
DPFX(DPFPREP, 2, "Windows 2000 Gold detected, not waiting for address list change Ioctl to complete.");
}
else
{
DPFX(DPFPREP, 2, "Windows 2000 Service Pack %u detected, not waiting for address list change Ioctl to complete.",
osvix.wServicePackMajor);
}
}
#endif // DBG
}
#ifdef DBG
else
{
dwError = GetLastError();
DPFX(DPFPREP, 0, "Couldn't get extended OS version information (err = %u)!.",
dwError);
}
#endif // DBG
}
//
// Wait, if we can. Otherwise, leak the memory.
//
if (fCanWait)
{
//
// Keep looping until I/O completes. We will give up after a
// while to prevent hangs.
//
dwAttempt = 0;
while (! HasOverlappedIoCompleted(this->m_polAddressListChange))
{
DPFX(DPFPREP, 2, "Waiting %u ms for address list change Ioctl to complete.",
IOCOMPLETE_WAIT_INTERVAL);
//
// Give the OS some time to complete it.
//
Sleep(IOCOMPLETE_WAIT_INTERVAL);
dwAttempt++;
if (dwAttempt >= MAX_NUM_IOCOMPLETE_WAITS)
{
break;
}
}
}
else
{
//
// Just leak the memory. See above notes and debug print
// statements
//
}
}
#ifdef DBG
else
{
dwError = GetLastError();
DPFX(DPFPREP, 0, "Couldn't get OS version information (err = %u)!",
dwError);
}
#endif // DBG
//
// We've either freed the memory or committed to leaking the object.
//
if (HasOverlappedIoCompleted(this->m_polAddressListChange))
{
//
// We didn't allocate it through DNMalloc, use the matching free
// function.
//
HeapFree(GetProcessHeap(), 0, this->m_polAddressListChange);
}
else
{
DPFX(DPFPREP, 1, "Overlapped address list change Ioctl has not completed yet, leaking %u byte overlapped structure at 0x%p.",
sizeof(WSAOVERLAPPED), this->m_polAddressListChange);
}
this->m_polAddressListChange = NULL;
}
//
// Cleanup WinSock.
//
iError = this->m_pfnWSACleanup();
if (iError != 0)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't cleanup WinSock (error = %u)!", dwError);
#endif // DBG
//
// Continue anyway, so we can finish cleaning up the object.
//
}
//
// Unload the library.
//
this->m_pfnWSAStartup = NULL;
this->m_pfnWSACleanup = NULL;
this->m_pfnWSAGetLastError = NULL;
this->m_pfnsocket = NULL;
this->m_pfnclosesocket = NULL;
this->m_pfnbind = NULL;
this->m_pfnsetsockopt = NULL;
this->m_pfngetsockname = NULL;
this->m_pfnselect = NULL;
this->m_pfn__WSAFDIsSet = NULL;
this->m_pfnrecvfrom = NULL;
this->m_pfnsendto = NULL;
this->m_pfngethostname = NULL;
this->m_pfngethostbyname = NULL;
this->m_pfninet_addr = NULL;
this->m_pfnWSASocketA = NULL;
this->m_pfnWSAIoctl = NULL;
this->m_pfnWSAGetOverlappedResult = NULL;
FreeLibrary(this->m_hWinSockDLL);
this->m_hWinSockDLL = NULL;
//
// If we loaded IPHLPAPI.DLL, unload it.
//
if (this->m_hIpHlpApiDLL != NULL)
{
this->m_pfnGetAdaptersInfo = NULL;
this->m_pfnGetIpForwardTable = NULL;
this->m_pfnGetBestRoute = NULL;
FreeLibrary(this->m_hIpHlpApiDLL);
this->m_hIpHlpApiDLL = NULL;
}
//
// If there was an alert event, we're done with it.
//
if (this->m_hAlertEvent != NULL)
{
CloseHandle(this->m_hAlertEvent);
this->m_hAlertEvent = NULL;
}
//
// If there was an alert I/O completion port, we're done with it.
//
if (this->m_hAlertIOCompletionPort != NULL)
{
CloseHandle(this->m_hAlertIOCompletionPort);
this->m_hAlertIOCompletionPort = NULL;
}
//
// Turn off flags which should reset it back to 0 or just the
// NOTCREATEDWITHCOM flag.
//
this->m_dwFlags &= ~(NATHELPPASTOBJ_INITIALIZED |
NATHELPPASTOBJ_USEPASTICS |
NATHELPPASTOBJ_USEPASTPFW |
NATHELPPASTOBJ_WINSOCK1 |
NATHELPPASTOBJ_DEVICECHANGED |
NATHELPPASTOBJ_ADDRESSESCHANGED |
NATHELPPASTOBJ_PORTREGISTERED);
DNASSERT((this->m_dwFlags & ~NATHELPPASTOBJ_NOTCREATEDWITHCOM) == 0);
this->DropLock();
fHaveLock = FALSE;
Exit:
DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
if (fHaveLock)
{
this->DropLock();
fHaveLock = FALSE;
}
goto Exit;
} // CNATHelpPAST::Close
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::GetCaps"
//=============================================================================
// CNATHelpPAST::GetCaps
//-----------------------------------------------------------------------------
//
// Description: Retrieves the capabilities of the Internet gateway server(s)
// and information on leased ports. This function should be
// called periodically with the DPNHGETCAPS_UPDATESERVERSTATUS
// flag to automatically extend port leases that are about to
// expire (that are in last 2 minutes of their lease).
//
// The DPNHGETCAPS_UPDATESERVERSTATUS flag also causes
// detection of changes in the servers' status since the last
// similar call to GetCaps. If a new server becomes available, an
// existing one became unavailable, or a server's public address
// changed in a way that affects an existing registered port
// mapping, then DPNHSUCCESS_ADDRESSESCHANGED is returned instead
// of DPNH_OK. The user should then update its port binding
// information via GetRegisteredAddresses.
//
// When DPNHGETCAPS_UPDATESERVERSTATUS is specified, this
// function may block for a short period of time while attempts
// are made to communicate with the server(s).
//
// GetCaps must be called with the
// DPNHGETCAPS_UPDATESERVERSTATUS flag at least once prior to
// using the GetRegisteredAddresses or QueryAddress methods.
//
// Arguments:
// DPNHCAPS * pdpnhcaps - Pointer to structure to be filled with the NAT
// helper's current capabilities. The dwSize
// field of the structure must be filled in before
// calling GetCaps.
// DWORD dwFlags - Flags to use when retrieving capabilities
// (DPNHGETCAPS_xxx).
//
// Returns: HRESULT
// DPNH_OK - Determining capabilities was successful.
// Address status has not changed.
// DPNHSUCCESS_ADDRESSESCHANGED - One or more of the registered port
// mappings' addresses changed, retrieve
// updated mappings with
// GetRegisteredAddress.
// DPNHERR_GENERIC - An error occurred while determining
// capabilities.
// DPNHERR_INVALIDFLAGS - Invalid flags were specified.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPARAM - An invalid parameter was specified.
// DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
// DPNHERR_NOTINITIALIZED - Initialize has not been called.
// DPNHERR_OUTOFMEMORY - There is not enough memory to get
// capabilities.
// DPNHERR_REENTRANT - The interface has been re-entered on the
// same thread.
//=============================================================================
STDMETHODIMP CNATHelpPAST::GetCaps(DPNHCAPS * const pdpnhcaps,
const DWORD dwFlags)
{
HRESULT hr;
BOOL fHaveLock = FALSE;
DWORD dwCurrentTime;
DWORD dwLeaseTimeRemaining;
CBilink * pBilink;
CRegisteredPort * pRegisteredPort;
CDevice * pDevice;
DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx)",
this, pdpnhcaps, dwFlags);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
hr = DPNHERR_INVALIDOBJECT;
goto Failure;
}
//
// Validate the parameters.
//
if ((pdpnhcaps == NULL) ||
(IsBadWritePtr(pdpnhcaps, sizeof(DPNHCAPS))))
{
DPFX(DPFPREP, 0, "Invalid caps structure pointer specified!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (pdpnhcaps->dwSize != sizeof(DPNHCAPS))
{
DPFX(DPFPREP, 0, "Invalid caps structure specified, dwSize must be %u!",
sizeof(DPNHCAPS));
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (dwFlags & ~DPNHGETCAPS_UPDATESERVERSTATUS)
{
DPFX(DPFPREP, 0, "Invalid flags specified!");
hr = DPNHERR_INVALIDFLAGS;
goto Failure;
}
//
// Attempt to take the lock, but be prepared for the re-entrancy error.
//
hr = this->TakeLock();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Could not lock object!");
goto Failure;
}
fHaveLock = TRUE;
//
// Make sure object is in right state.
//
if (! (this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED) )
{
DPFX(DPFPREP, 0, "Object not initialized!");
hr = DPNHERR_NOTINITIALIZED;
goto Failure;
}
//
// Fill in the base caps structure.
//
pdpnhcaps->dwFlags = 0;
pdpnhcaps->dwNumRegisteredPorts = 0;
pdpnhcaps->dwMinLeaseTimeRemaining = -1;
//
// pdpnhcaps->dwRecommendedGetCapsInterval is initialized below
//
if (dwFlags & DPNHGETCAPS_UPDATESERVERSTATUS)
{
//
// Remove any cached mappings that have expired.
//
this->ExpireOldCachedMappings();
//
// Extend leases, if necessary.
//
hr = this->ExtendAllExpiringLeases();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Extending all expiring leases failed!");
goto Failure;
}
//
// Check for any new devices.
//
hr = this->CheckForNewDevices(NULL);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Checking for new devices failed!");
goto Failure;
}
//
// Check for possible changes in any server's status. The
// ADDRESSESCHANGED flag will be set on this object if there were
// changes that affected existing port mappings.
//
hr = this->UpdateServerStatus();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Updating servers' status failed!");
goto Failure;
}
//
// Okay, so if things are different, alert the caller.
//
if (this->m_dwFlags & NATHELPPASTOBJ_ADDRESSESCHANGED)
{
hr = DPNHSUCCESS_ADDRESSESCHANGED;
this->m_dwFlags &= ~NATHELPPASTOBJ_ADDRESSESCHANGED;
}
#ifdef DBG
//
// This flag should have been turned off by now if it ever got turned
// on.
//
DNASSERT(! (this->m_dwFlags & NATHELPPASTOBJ_DEVICECHANGED));
//
// Print the current device and mapping status for debugging purposes.
//
this->DebugPrintCurrentStatus();
#endif // DBG
}
else
{
//
// Not extending expiring leases or updating server status.
//
}
//
// Loop through all the devices, getting their gateway capabilities.
//
pBilink = this->m_blDevices.GetNext();
while (pBilink != (&this->m_blDevices))
{
pDevice = DEVICE_FROM_BILINK(pBilink);
if (pDevice->GetPASTClientID(TRUE) != 0)
{
//
// PAST servers do not actively notify you of address changes.
//
pdpnhcaps->dwFlags |= DPNHCAPSFLAG_GATEWAYPRESENT | DPNHCAPSFLAG_NOTALLSUPPORTACTIVENOTIFY;
if (pDevice->IsPASTPublicAddressAvailable(TRUE))
{
pdpnhcaps->dwFlags |= DPNHCAPSFLAG_PUBLICADDRESSAVAILABLE;
}
}
if (pDevice->GetPASTClientID(FALSE) != 0)
{
if (pDevice->HasLocalPFWOnlyPASTServer())
{
//
// PAST servers do not actively notify you of address changes.
//
pdpnhcaps->dwFlags |= DPNHCAPSFLAG_LOCALFIREWALLPRESENT | DPNHCAPSFLAG_NOTALLSUPPORTACTIVENOTIFY;
DNASSERT(pDevice->IsPASTPublicAddressAvailable(FALSE));
}
else
{
//
// PAST servers do not actively notify you of address changes.
//
pdpnhcaps->dwFlags |= DPNHCAPSFLAG_GATEWAYPRESENT | DPNHCAPSFLAG_GATEWAYISLOCAL | DPNHCAPSFLAG_NOTALLSUPPORTACTIVENOTIFY;
if (pDevice->IsPASTPublicAddressAvailable(FALSE))
{
pdpnhcaps->dwFlags |= DPNHCAPSFLAG_PUBLICADDRESSAVAILABLE;
}
}
}
pBilink = pBilink->GetNext();
}
//
// Loop through all registered ports, counting them.
// We have the appropriate lock.
//
pBilink = this->m_blRegisteredPorts.GetNext();
dwCurrentTime = timeGetTime();
while (pBilink != (&this->m_blRegisteredPorts))
{
pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
//
// Count these registered addresses toward the total.
//
pdpnhcaps->dwNumRegisteredPorts += pRegisteredPort->GetNumAddresses();
pDevice = pRegisteredPort->GetOwningDevice();
if (pDevice != NULL)
{
DNASSERT(! (pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts)));
//
// If they're registered with any PAST servers and also calculate
// the minimum lease time remaining.
//
if (pDevice->GetPASTClientID(FALSE) != 0)
{
dwLeaseTimeRemaining = pRegisteredPort->GetPASTLeaseExpiration(FALSE) - dwCurrentTime;
if (dwLeaseTimeRemaining < pdpnhcaps->dwMinLeaseTimeRemaining)
{
//
// Temporarily store how much time remains.
//
pdpnhcaps->dwMinLeaseTimeRemaining = dwLeaseTimeRemaining;
}
}
if (pDevice->GetPASTClientID(TRUE) != 0)
{
dwLeaseTimeRemaining = pRegisteredPort->GetPASTLeaseExpiration(TRUE) - dwCurrentTime;
if (dwLeaseTimeRemaining < pdpnhcaps->dwMinLeaseTimeRemaining)
{
//
// Temporarily store how much time remains.
//
pdpnhcaps->dwMinLeaseTimeRemaining = dwLeaseTimeRemaining;
}
}
}
else
{
DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts));
}
pBilink = pBilink->GetNext();
}
//
// There are different default recommended GetCaps intervals depending on
// whether there's a server present, and whether it supports active address
// change notification (that we can alert on) or not.
//
// If there are any leases which need to be renewed before that default
// time, the recommendation will be shortened appropriately.
//
//
// If GetCaps hasn't been called with UPDATESERVERSTATUS yet, recommend an
// immediate check.
//
if (this->m_dwLastUpdateServerStatusTime == 0)
{
DPFX(DPFPREP, 1, "Server status has not been updated yet, recommending immediate GetCaps.");
//
// Drop the lock, we're done here.
//
this->DropLock();
fHaveLock = FALSE;
goto Exit;
}
//
// In an ideal world, we could get notified of changes and we would never
// have to poll. Unfortunately that isn't the case. We need to recommend
// a relatively short poll interval.
//
// Start by figuring out how long it's been since the last server update.
// This calculation really should not go negative. If it does, it means
// the caller hasn't updated the server status in ages anyway, so we should
// recommend immediate GetCaps.
//
// Otherwise if the 'port registered' flag is still set at this point, then
// the user must have called GetCaps previously, then RegisterPorts, then
// made this second GetCaps call before g_dwMinUpdateServerStatusInterval
// elapsed. Recommend that the user call us again as soon as the minimum
// update interval does elapse.
//
// In all other cases, generate a recommendation based on the current
// backed off poll interval.
//
dwCurrentTime = dwCurrentTime - this->m_dwLastUpdateServerStatusTime;
if ((int) dwCurrentTime < 0)
{
DPFX(DPFPREP, 1, "Server status was last updated a really long time ago (%u ms), recommending immediate GetCaps.",
dwCurrentTime);
pdpnhcaps->dwRecommendedGetCapsInterval = 0;
}
else if (this->m_dwFlags & NATHELPPASTOBJ_PORTREGISTERED)
{
DPFX(DPFPREP, 1, "Didn't handle new port registration because server was last updated %u ms ago, (poll interval staying at %u ms).",
dwCurrentTime, this->m_dwNextPollInterval);
pdpnhcaps->dwRecommendedGetCapsInterval = g_dwMinUpdateServerStatusInterval - dwCurrentTime;
if ((int) pdpnhcaps->dwRecommendedGetCapsInterval < 0)
{
pdpnhcaps->dwRecommendedGetCapsInterval = 0;
}
}
else
{
DPFX(DPFPREP, 7, "Server was last updated %u ms ago, current poll interval is %u ms.",
dwCurrentTime, this->m_dwNextPollInterval);
//
// Calculate a new recommended interval based on the current value, and
// backoff that interval if necessary.
//
pdpnhcaps->dwRecommendedGetCapsInterval = this->m_dwNextPollInterval - dwCurrentTime;
this->m_dwNextPollInterval += GetGlobalRand() % g_dwPollIntervalBackoff;
if (this->m_dwNextPollInterval > g_dwMaxPollInterval)
{
this->m_dwNextPollInterval = g_dwMaxPollInterval;
DPFX(DPFPREP, 3, "Capping next poll interval at %u ms.",
this->m_dwNextPollInterval);
}
else
{
DPFX(DPFPREP, 8, "Next poll interval will be %u ms.",
this->m_dwNextPollInterval);
}
//
// If that time went negative, then it implies that the interval has
// already elapsed. Recommend immediate GetCaps.
//
if (((int) pdpnhcaps->dwRecommendedGetCapsInterval) < 0)
{
DPFX(DPFPREP, 1, "Recommended interval already elapsed (%i ms), suggesting immediate GetCaps.",
((int) pdpnhcaps->dwRecommendedGetCapsInterval));
pdpnhcaps->dwRecommendedGetCapsInterval = 0;
}
}
this->DropLock();
fHaveLock = FALSE;
//
// If there is a non-INFINITE lease time remaining, see if that affects the
// GetCaps interval.
//
if (pdpnhcaps->dwMinLeaseTimeRemaining != -1)
{
//
// If there are leases that need to be refreshed before the default
// recommendation, then use those instead.
//
if (pdpnhcaps->dwMinLeaseTimeRemaining < LEASE_RENEW_TIME)
{
DPFX(DPFPREP, 1, "Lease needs renewing right away (min %u < %u ms), recommending immediate GetCaps.",
pdpnhcaps->dwMinLeaseTimeRemaining, LEASE_RENEW_TIME);
pdpnhcaps->dwRecommendedGetCapsInterval = 0;
}
else
{
//
// Either pick the time when the lease should be renewed or leave
// it as the recommended time, whichever is shorter.
//
if ((pdpnhcaps->dwMinLeaseTimeRemaining - LEASE_RENEW_TIME) < pdpnhcaps->dwRecommendedGetCapsInterval)
{
pdpnhcaps->dwRecommendedGetCapsInterval = pdpnhcaps->dwMinLeaseTimeRemaining - LEASE_RENEW_TIME;
}
}
}
DPFX(DPFPREP, 7, "GetCaps flags = 0x%lx, num registered ports = %u, min lease time remaining = %i, recommended interval = %i.",
pdpnhcaps->dwFlags,
pdpnhcaps->dwNumRegisteredPorts,
((int) pdpnhcaps->dwMinLeaseTimeRemaining),
((int) pdpnhcaps->dwRecommendedGetCapsInterval));
Exit:
DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
if (fHaveLock)
{
this->DropLock();
fHaveLock = FALSE;
}
goto Exit;
} // CNATHelpPAST::GetCaps
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::RegisterPorts"
//=============================================================================
// CNATHelpPAST::RegisterPorts
//-----------------------------------------------------------------------------
//
// Description: Asks for public realm port(s) that are aliases for the local
// port(s) on this private realm node. If a server is available,
// all traffic directed to the gateway on the public side at the
// allocated public ports-- which the gateway provides and
// specifies in the response-- will be directed to the specified
// local ports. If the DPNHREGISTERPORTS_FIXEDPORTS flag is not
// specified, the ports assigned on the public interface are
// arbitrary (i.e. may not be the same as those in awLocalPort).
// The address and ports actually allocated can be retrieved by
// calling GetRegisteredAddresses.
//
// The address component for every SOCKADDR structure in the
// array must be the same. A separate RegisterPorts call is
// required to register multiple ports that are not using the same
// interface. The address can be INADDR_ANY, in which case the
// "best" server will be used. If multiple servers are available
// via different adapters, an adapter with an Internet gateway is
// selected. If no adapters have Internet gateways, the first
// adapter with a local firewall is selected. If neither are
// available, then the first one where either a gateway or a
// firewall becomes available will be automatically selected.
// Once one of the adapters has been assigned, it cannot be
// changed. Since the server chosen by this method may not be
// optimal for a particular application, it is recommended that
// individual addresses be registered instead of INADDR_ANY.
//
// If the address in aLocalAddresses is not one of those
// available to the local machine, the registration will still
// succeed. If an adapter with that address becomes available,
// the port mapping will automatically be applied, and it will
// gain a public mapping with any server available to that
// adapter. If the address was originally available but the
// network adapter is subsequently removed from the system, any
// public address mapping is lost. It will be automatically
// regained if the local address becomes available again. It is
// recommended that the caller detect local address changes
// independently and de-register/re-register mappings per adapter
// as appropriate for maximum control.
//
// If the DPNHREGISTERPORTS_SHAREDPORTS flag is used, the
// server will allow other NAT clients to register it as well.
// Any UDP traffic received on the public interface will be
// forwarded to all clients registered. This requires the
// DPNHREGISTERPORTS_FIXEDPORTS flag and cannot be used with
// DPNHREGISTERPORTS_TCP.
//
// The user should specify a requested lease time that the
// server will attempt to honor. The actual time remaining can be
// can be retrieved by calling GetRegisteredAddresses.
//
// Note that if a server is not available, this function will
// still succeed. GetRegisteredAddresses will return
// DPNHERR_NOMAPPING for the handle returned in phRegisteredPorts
// in that case. If the server arrives later during the session,
// calling GetCaps periodically can detect this and automatically
// map previously registered ports. Use GetRegisteredAddresses to
// retrieve the newly mapped address when that occurs.
//
// Only 16 ports may be registered at a time, but RegisterPorts
// may be called as many times as desired.
//
// The same array of addresses may be registered more than
// once. Each DPNHHANDLE returned must be released with
// DeregisterPorts or Close. If an individual address was
// previously registered but in a different array or a different
// order in the array, then the DPNHERR_PORTALREADYREGISTERED
// error code is returned.
//
// Arguments:
// SOCKADDR * aLocalAddresses - Array of local address and port tuples
// for which remote ports are requested.
// DWORD dwAddressesSize - Size of entire local addresses array.
// DWORD dwNumAddresses - Number of SOCKADDR structures in local
// addresses array.
// DWORD dwLeaseTime - Requested time, in milliseconds, to lease
// the ports. As long as GetCaps is
// called before this time has expired,
// the lease will automatically be
// renewed.
// DPNHHANDLE * phRegisteredPorts - Place to store an identifier for this
// binding which can later be used to
// query or release the binding.
// DWORD dwFlags - Flags to use when registering the port
// (DPNHREGISTERPORTS_xxx).
//
// Returns: HRESULT
// DPNH_OK - The ports were successfully registered
// (although no public address may be
// available yet).
// DPNHERR_GENERIC - An error occurred that prevented
// registration of the requested ports.
// DPNHERR_INVALIDFLAGS - Invalid flags were specified.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPARAM - An invalid parameter was specified.
// DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
// DPNHERR_NOTINITIALIZED - Initialize has not been called.
// DPNHERR_OUTOFMEMORY - There is not enough memory to register
// the ports.
// DPNHERR_PORTALREADYREGISTERED - At least one of the ports has already
// been registered in a different address
// array or order.
// DPNHERR_REENTRANT - The interface has been re-entered on the
// same thread.
//=============================================================================
STDMETHODIMP CNATHelpPAST::RegisterPorts(const SOCKADDR * const aLocalAddresses,
const DWORD dwAddressesSize,
const DWORD dwNumAddresses,
const DWORD dwLeaseTime,
DPNHHANDLE * const phRegisteredPorts,
const DWORD dwFlags)
{
HRESULT hr;
HRESULT temphr;
ULONG ulFirstAddress;
DWORD dwTemp;
DWORD dwMatch;
BOOL fHaveLock = FALSE;
BOOL fGotLocalPASTMapping = FALSE;
BOOL fGotRemotePASTMapping = FALSE;
CRegisteredPort * pRegisteredPort = NULL;
CDevice * pDevice = NULL;
CBilink * pBilink;
SOCKADDR_IN * psaddrinTemp;
DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, %u, %u, %u, 0x%p, 0x%lx)",
this, aLocalAddresses, dwAddressesSize, dwNumAddresses, dwLeaseTime,
phRegisteredPorts, dwFlags);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
hr = DPNHERR_INVALIDOBJECT;
goto Failure;
}
//
// Validate the parameters.
//
if (aLocalAddresses == NULL)
{
DPFX(DPFPREP, 0, "Local addresses array cannot be NULL!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (dwNumAddresses == 0)
{
DPFX(DPFPREP, 0, "Number of addresses cannot be 0!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (dwAddressesSize != (dwNumAddresses * sizeof(SOCKADDR)))
{
DPFX(DPFPREP, 0, "Addresses array size invalid!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (IsBadReadPtr(aLocalAddresses, dwAddressesSize))
{
DPFX(DPFPREP, 0, "Local addresses array buffer is invalid!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (dwNumAddresses > DPNH_MAX_SIMULTANEOUS_PORTS)
{
DPFX(DPFPREP, 0, "Only %u ports may be registered at a time!", DPNH_MAX_SIMULTANEOUS_PORTS);
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (((SOCKADDR_IN*) aLocalAddresses)->sin_family != AF_INET)
{
DPFX(DPFPREP, 0, "First address in array is not AF_INET, only IPv4 addresses are supported!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_addr == INADDR_BROADCAST)
{
DPFX(DPFPREP, 0, "First address cannot be broadcast address!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (((SOCKADDR_IN*) aLocalAddresses)->sin_port == 0)
{
DPFX(DPFPREP, 0, "First port in array is 0, a valid port must be specified!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
ulFirstAddress = ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_addr;
for(dwTemp = 1; dwTemp < dwNumAddresses; dwTemp++)
{
//
// Make sure this address family type is supported.
//
if (((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_family != AF_INET)
{
DPFX(DPFPREP, 0, "Address at array index %u is not AF_INET, all items in the array must be the same IPv4 address!",
dwTemp);
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
//
// If this address doesn't match the first, then the caller broke the
// rules.
//
if (((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_addr != ulFirstAddress)
{
//
// Don't use inet_ntoa because we may not be initialized yet.
//
DPFX(DPFPREP, 0, "Address %u.%u.%u.%u at array index %u differs from the first, all addresses in the array must match!",
((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b1,
((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b2,
((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b3,
((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b4,
dwTemp);
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
//
// Make sure this port isn't 0 either.
//
if (((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_port == 0)
{
DPFX(DPFPREP, 0, "Port at array index %u is 0, valid ports must be specified!", dwTemp);
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
}
if (dwLeaseTime == 0)
{
DPFX(DPFPREP, 0, "Invalid lease time specified!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if ((phRegisteredPorts == NULL) ||
(IsBadWritePtr(phRegisteredPorts, sizeof(DPNHHANDLE))))
{
DPFX(DPFPREP, 0, "Invalid port mapping handle pointer specified!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (dwFlags & ~(DPNHREGISTERPORTS_TCP | DPNHREGISTERPORTS_FIXEDPORTS | DPNHREGISTERPORTS_SHAREDPORTS))
{
DPFX(DPFPREP, 0, "Invalid flags specified!");
hr = DPNHERR_INVALIDFLAGS;
goto Failure;
}
if (dwFlags & DPNHREGISTERPORTS_SHAREDPORTS)
{
//
// SHAREDPORTS cannot be used with TCP and requires a FIXEDPORTS.
//
if ((dwFlags & DPNHREGISTERPORTS_TCP) || (! (dwFlags & DPNHREGISTERPORTS_FIXEDPORTS)))
{
DPFX(DPFPREP, 0, "SHAREDPORTS flag requires FIXEDPORTS flag and cannot be used with TCP flag!");
hr = DPNHERR_INVALIDFLAGS;
goto Failure;
}
}
//
// Attempt to take the lock, but be prepared for the re-entrancy error.
//
hr = this->TakeLock();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Could not lock object!");
goto Failure;
}
fHaveLock = TRUE;
//
// Make sure object is in right state.
//
if (! (this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED) )
{
DPFX(DPFPREP, 0, "Object not initialized!");
hr = DPNHERR_NOTINITIALIZED;
goto Failure;
}
//
// Loop through all existing registered port mappings and look for this
// array of ports.
//
pBilink = this->m_blRegisteredPorts.GetNext();
while (pBilink != &this->m_blRegisteredPorts)
{
pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
//
// Don't bother looking at addresses of the wrong type or at arrays of
// the wrong size.
//
if (((pRegisteredPort->IsTCP() && (dwFlags & DPNHREGISTERPORTS_TCP)) ||
((! pRegisteredPort->IsTCP()) && (! (dwFlags & DPNHREGISTERPORTS_TCP)))) &&
(pRegisteredPort->GetNumAddresses() == dwNumAddresses))
{
psaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
{
//
// If the addresses don't match, stop looping.
//
if ((psaddrinTemp[dwTemp].sin_port != ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_port) ||
(psaddrinTemp[dwTemp].sin_addr.S_un.S_addr != ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_addr))
{
break;
}
}
//
// If all the addresses matched, then this item was already
// registered.
//
if (dwTemp >= dwNumAddresses)
{
DPFX(DPFPREP, 1, "Array of %u addresses was already registered, returning existing mapping 0x%p.",
dwNumAddresses, pRegisteredPort);
goto ReturnUserHandle;
}
DPFX(DPFPREP, 7, "Existing mapping 0x%p does not match all %u addresses.",
pRegisteredPort, dwNumAddresses);
}
else
{
//
// Existing mapping isn't same type or doesn't have same number of
// items in array.
//
}
pBilink = pBilink->GetNext();
}
//
// If we're here, none of the existing mappings match. Loop through each
// of the ports and make sure they aren't already registered inside some
// other mapping.
//
for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
{
pBilink = this->m_blRegisteredPorts.GetNext();
while (pBilink != &this->m_blRegisteredPorts)
{
pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
psaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
for(dwMatch = 0; dwMatch < pRegisteredPort->GetNumAddresses(); dwMatch++)
{
//
// If the addresses match, then we can't map these ports.
//
if ((psaddrinTemp[dwMatch].sin_port == ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_port) &&
(psaddrinTemp[dwMatch].sin_addr.S_un.S_addr == ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_addr))
{
DPFX(DPFPREP, 0, "Existing mapping 0x%p already registered the address %u.%u.%u.%u:%u!",
pRegisteredPort,
psaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b1,
psaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b2,
psaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b3,
psaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b4,
NTOHS(psaddrinTemp[dwMatch].sin_port));
//
// Clear the pointer so we don't delete the object.
//
pRegisteredPort = NULL;
hr = DPNHERR_PORTALREADYREGISTERED;
goto Failure;
}
}
pBilink = pBilink->GetNext();
}
}
//
// If we're here the ports are all unique. Create a new mapping object
// we'll use to refer to the binding.
//
pRegisteredPort = new CRegisteredPort(dwLeaseTime, dwFlags);
if (pRegisteredPort == NULL)
{
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
hr = pRegisteredPort->SetPrivateAddresses((SOCKADDR_IN*) aLocalAddresses, dwNumAddresses);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't store private addresses array!");
goto Failure;
}
//
// Find the device that matches the given addresses.
//
// The first entry of aLocalAddresses is representative of all entries since
// they should all share the same address.
//
// Since there won't be an existing registered port for this address, don't
// bother looking through them for a matching address.
//
pDevice = this->FindMatchingDevice((SOCKADDR_IN*) (&aLocalAddresses[0]),
FALSE);
if (pDevice == NULL)
{
DPFX(DPFPREP, 1, "No device for given address (%u.%u.%u.%u), storing 0x%p in unowned list.",
((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b1,
((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b2,
((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b3,
((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b4,
pRegisteredPort);
pRegisteredPort->m_blDeviceList.InsertBefore(&this->m_blUnownedPorts);
}
else
{
pRegisteredPort->MakeDeviceOwner(pDevice);
//
// If we have a client ID, that means the server is available. So if
// there's either a local or remote PAST server, we can request the
// mapping right now.
// Start with the local PAST server.
//
if (pDevice->GetPASTClientID(FALSE) != 0)
{
hr = this->AssignOrListenPASTPort(pRegisteredPort, FALSE);
if (hr != DPNH_OK)
{
if (hr != DPNHERR_PORTUNAVAILABLE)
{
DPFX(DPFPREP, 0, "Couldn't assign port mapping with local PAST server (0x%lx)! Ignoring.", hr);
//
// We'll treat this as non-fatal, but we have to dump the
// server.
//
this->ClearDevicesPASTServer(pDevice, FALSE);
}
else
{
DPFX(DPFPREP, 1, "Local PAST server indicated that the port was unavailable.");
pRegisteredPort->NotePASTPortUnavailable(FALSE);
}
hr = DPNH_OK;
}
else
{
fGotLocalPASTMapping = TRUE;
}
}
else
{
//
// No local PAST server.
//
}
//
// Check remote PAST server, too.
//
if (pDevice->GetPASTClientID(TRUE) != 0)
{
hr = this->AssignOrListenPASTPort(pRegisteredPort, TRUE);
if (hr != DPNH_OK)
{
if (hr != DPNHERR_PORTUNAVAILABLE)
{
DPFX(DPFPREP, 0, "Couldn't assign port mapping with remote PAST server (0x%lx)! Ignoring.", hr);
//
// We'll treat this as non-fatal, but we have to dump the
// server.
//
this->ClearDevicesPASTServer(pDevice, TRUE);
}
else
{
DPFX(DPFPREP, 1, "Remote PAST server indicated that the port was unavailable.");
pRegisteredPort->NotePASTPortUnavailable(TRUE);
}
hr = DPNH_OK;
}
else
{
fGotRemotePASTMapping = TRUE;
}
}
else
{
//
// No remote PAST server.
//
}
}
//
// Save the mapping in the global list (we have the lock).
//
pRegisteredPort->m_blGlobalList.InsertBefore(&this->m_blRegisteredPorts);
ReturnUserHandle:
//
// Remember that a port has been registered.
//
this->m_dwFlags |= NATHELPPASTOBJ_PORTREGISTERED;
//
// We're about to give the port to the user.
//
pRegisteredPort->AddUserRef();
//
// We're going to give the user a direct pointer to the object (disguised
// as an opaque DPNHHANDLE, of course).
//
(*phRegisteredPorts) = (DPNHHANDLE) pRegisteredPort;
this->DropLock();
fHaveLock = FALSE;
DPFX(DPFPREP, 5, "Returning registered port 0x%p (first private address = %u.%u.%u.%u:%u).",
pRegisteredPort,
((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b1,
((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b2,
((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b3,
((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b4,
NTOHS(((SOCKADDR_IN*) aLocalAddresses)[0].sin_port));
hr = DPNH_OK;
Exit:
DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
if (fGotRemotePASTMapping)
{
temphr = this->FreePASTPort(pRegisteredPort, TRUE);
if (temphr != DPNH_OK)
{
//
// We'll treat this as non-fatal, but we have to dump the server.
//
this->ClearDevicesPASTServer(pRegisteredPort->GetOwningDevice(), TRUE);
}
}
if (fGotLocalPASTMapping)
{
temphr = this->FreePASTPort(pRegisteredPort, FALSE);
if (temphr != DPNH_OK)
{
//
// We'll treat this as non-fatal, but we have to dump the server.
//
this->ClearDevicesPASTServer(pRegisteredPort->GetOwningDevice(), FALSE);
}
}
if (pRegisteredPort != NULL)
{
if (pDevice != NULL)
{
pRegisteredPort->ClearDeviceOwner();
}
pRegisteredPort->ClearPrivateAddresses();
delete pRegisteredPort;
}
if (fHaveLock)
{
this->DropLock();
fHaveLock = FALSE;
}
goto Exit;
} // CNATHelpPAST::RegisterPorts
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::GetRegisteredAddresses"
//=============================================================================
// CNATHelpPAST::GetRegisteredAddresses
//-----------------------------------------------------------------------------
//
// Description: Returns the current public address mappings for a given
// registered port group. If there are no servers currently
// available, then DPNHERR_SERVERNOTAVAILABLE is returned. If the
// servers' public interfaces are not currently valid, then
// DPNHERR_NOMAPPING is returned, but appropriate values will
// still be placed in pdwAddressTypeFlags and
// pdwLeaseTimeRemaining.
//
// If the mapping was registered with the
// DPNHREGISTERPORTS_FIXEDPORTS flag, but at least one port is
// already in use on the gateway, then DPNHERR_PORTUNAVAILABLE is
// returned and appropriate flags will still be placed in
// pdwAddressTypeFlags.
//
// If the local machine has a cooperative firewall installed,
// the requested port is opened locally on the firewall before
// being mapped on the Internet gateway. Normally this function
// returns the public address on the Internet gateway address when
// both are present. Since some firewalls remap the port number
// when opening non-fixed ports, the
// DPNHGETREGISTEREDADDRESSES_LOCALFIREWALLREMAPONLY allows the
// caller to retrieve the locally remapped address, even if there
// is a mapping on an Internet gateway.
//
// Some gateway devices do not natively support ports that are
// not fixed, and may generate the DPNHERR_PORTUNAVAILABLE return
// code even when the DPNHREGISTERPORTS_FIXEDPORTS flag was not
// specified. The caller should de-register the port mapping
// handle, rebind the application to different ports, and call
// RegisterPorts again.
//
// If the buffer indicated by paPublicAddresses is too small,
// then the size required is returned in pdwPublicAddressesSize
// and DPNHERR_BUFFERTOOSMALL is returned. Otherwise the number of
// bytes written is returned in pdwPublicAddressesSize.
//
// Even though the addresses are returned as individual
// SOCKADDRs, all ports registered at the same time will share the
// same public address. Only the port components will vary.
//
// All buffers are optional and may be NULL, but if
// paPublicAddresses is specified, it must be accompanied by an
// appropriate size in pdwPublicAddressesSize.
//
// If GetCaps has not been previously called with the
// DPNHGETCAPS_UPDATESERVERSTATUS flag at least once, then the
// error code DPNHERR_UPDATESERVERSTATUS is returned.
//
// Arguments:
// DPNHHANDLE hRegisteredPorts - Handle for a specific binding returned by
// RegisterPorts.
// SOCKADDR * paPublicAddresses - Buffer to return assigned public realm
// address, or NULL if not desired.
// DWORD * pdwPublicAddressesSize - Pointer to size of paPublicAddresses
// buffer, or place to store size
// required/written. Cannot be NULL if
// paPublicAddresses is not NULL.
// DWORD * pdwAddressTypeFlags - Place to store flags describing the
// address types returned, or NULL if not
// desired.
// DWORD * pdwLeaseTimeRemaining - Place to store approximate number of
// milliseconds remaining in the port
// lease, or NULL if not desired. Call
// GetCaps to automatically extend leases
// about to expire.
// DWORD dwFlags - Unused, must be zero.
//
// Returns: HRESULT
// DPNH_OK - Information on the port mapping was found and
// the addresses were stored in
// paPublicAddresses.
// DPNHERR_BUFFERTOOSMALL - There was not enough room in the buffer to
// store the addresses.
// DPNHERR_GENERIC - An error occurred while retrieving the
// requested port mapping.
// DPNHERR_INVALIDFLAGS - Invalid flags were specified.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPARAM - An invalid parameter was specified.
// DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
// DPNHERR_NOMAPPING - The server(s) do not have valid public
// interfaces.
// DPNHERR_NOTINITIALIZED - Initialize has not been called.
// DPNHERR_OUTOFMEMORY - There is not enough memory to get the
// addresses.
// DPNHERR_PORTUNAVAILABLE - At least one of the ports is not available on
// the server.
// DPNHERR_REENTRANT - The interface has been re-entered on the same
// thread.
// DPNHERR_SERVERNOTAVAILABLE - No servers are currently present.
// DPNHERR_UPDATESERVERSTATUS - GetCaps has not been called with the
// DPNHGETCAPS_UPDATESERVERSTATUS flag yet.
//=============================================================================
STDMETHODIMP CNATHelpPAST::GetRegisteredAddresses(const DPNHHANDLE hRegisteredPorts,
SOCKADDR * const paPublicAddresses,
DWORD * const pdwPublicAddressesSize,
DWORD * const pdwAddressTypeFlags,
DWORD * const pdwLeaseTimeRemaining,
const DWORD dwFlags)
{
HRESULT hr;
CRegisteredPort * pRegisteredPort;
BOOL fHaveLock = FALSE;
BOOL fRegisteredWithServer = FALSE;
BOOL fFoundValidMapping = FALSE;
BOOL fPortIsUnavailable = FALSE;
DWORD dwSizeRequired;
DWORD dwAddressTypeFlags;
DWORD dwCurrentTime;
DWORD dwTempLeaseTimeRemaining;
DWORD dwLeaseTimeRemaining = -1;
CDevice * pDevice;
DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%p, 0x%p, 0x%lx)",
this, hRegisteredPorts, paPublicAddresses, pdwPublicAddressesSize,
pdwAddressTypeFlags, pdwLeaseTimeRemaining, dwFlags);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
hr = DPNHERR_INVALIDOBJECT;
goto Failure;
}
//
// Validate the parameters.
//
pRegisteredPort = (CRegisteredPort*) hRegisteredPorts;
if (! pRegisteredPort->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid registered port mapping handle specified!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (paPublicAddresses != NULL)
{
if ((pdwPublicAddressesSize == NULL) ||
(IsBadWritePtr(pdwPublicAddressesSize, sizeof(DWORD))))
{
DPFX(DPFPREP, 0, "When specifying a public addresses buffer, a valid size must be given!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (IsBadWritePtr(paPublicAddresses, (*pdwPublicAddressesSize)))
{
DPFX(DPFPREP, 0, "The public addresses buffer is invalid!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
}
else
{
if ((pdwPublicAddressesSize != NULL) &&
(IsBadWritePtr(pdwPublicAddressesSize, sizeof(DWORD))))
{
DPFX(DPFPREP, 0, "Invalid pointer for size of public addresses buffer!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
}
if ((pdwAddressTypeFlags != NULL) &&
(IsBadWritePtr(pdwAddressTypeFlags, sizeof(DWORD))))
{
DPFX(DPFPREP, 0, "Invalid pointer for address type flags!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if ((pdwLeaseTimeRemaining != NULL) &&
(IsBadWritePtr(pdwLeaseTimeRemaining, sizeof(DWORD))))
{
DPFX(DPFPREP, 0, "Invalid pointer for lease time remaining!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (dwFlags & ~DPNHGETREGISTEREDADDRESSES_LOCALFIREWALLREMAPONLY)
{
DPFX(DPFPREP, 0, "Invalid flags specified!");
hr = DPNHERR_INVALIDFLAGS;
goto Failure;
}
//
// Start the flags off with the information regarding TCP vs. UDP.
//
if (pRegisteredPort->IsTCP())
{
dwAddressTypeFlags = DPNHADDRESSTYPE_TCP;
}
else
{
dwAddressTypeFlags = 0;
}
//
// Add in other flags we know already.
//
if (pRegisteredPort->IsFixedPort())
{
dwAddressTypeFlags |= DPNHADDRESSTYPE_FIXEDPORTS;
}
if (pRegisteredPort->IsSharedPort())
{
dwAddressTypeFlags |= DPNHADDRESSTYPE_SHAREDPORTS;
}
//
// Attempt to take the lock, but be prepared for the re-entrancy error.
//
hr = this->TakeLock();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Could not lock object!");
goto Failure;
}
fHaveLock = TRUE;
//
// Make sure object is in right state.
//
if (! (this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED) )
{
DPFX(DPFPREP, 0, "Object not initialized!");
hr = DPNHERR_NOTINITIALIZED;
goto Failure;
}
if (this->m_dwLastUpdateServerStatusTime == 0)
{
DPFX(DPFPREP, 0, "GetCaps has not been called with UPDATESERVERSTATUS flag yet!");
hr = DPNHERR_UPDATESERVERSTATUS;
goto Failure;
}
//
// Get a shortcut pointer to the device (may not exist).
//
pDevice = pRegisteredPort->GetOwningDevice();
//
// Get the current time for both the remote and local lease calculations.
//
dwCurrentTime = timeGetTime();
if (! (dwFlags & DPNHGETREGISTEREDADDRESSES_LOCALFIREWALLREMAPONLY))
{
//
// Check for a mapping on a remote PAST server.
//
if (pRegisteredPort->GetPASTBindID(TRUE) != 0)
{
DNASSERT(pDevice != NULL);
//
// There's a bind ID, so there must be a public address array.
//
DNASSERT(pRegisteredPort->HasPASTPublicAddressesArray(TRUE));
fRegisteredWithServer = TRUE;
//
// Make sure the remote PAST server is currently giving out a valid
// public address. If so, hand it out.
//
if (pDevice->IsPASTPublicAddressAvailable(TRUE))
{
if (pdwPublicAddressesSize != NULL)
{
dwSizeRequired = pRegisteredPort->GetAddressesSize();
if ((paPublicAddresses == NULL) ||
(dwSizeRequired > (*pdwPublicAddressesSize)))
{
//
// Not enough room in buffer, return the size required
// and the BUFFERTOOSMALL error code.
//
(*pdwPublicAddressesSize) = dwSizeRequired;
hr = DPNHERR_BUFFERTOOSMALL;
}
else
{
//
// Buffer was large enough, return the size written.
//
(*pdwPublicAddressesSize) = dwSizeRequired;
pRegisteredPort->CopyPASTPublicAddresses((SOCKADDR_IN*) paPublicAddresses,
TRUE);
}
}
else
{
//
// Not using address buffer.
//
}
fFoundValidMapping = TRUE;
}
else
{
DPFX(DPFPREP, 8, "The remote PAST server is not currently handing out valid public address mappings.");
}
//
// Add in the flag indicating that there's a remote ICS server.
//
dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAY;
//
// Get the relative remote PAST server lease time remaining.
//
dwTempLeaseTimeRemaining = pRegisteredPort->GetPASTLeaseExpiration(TRUE) - dwCurrentTime;
if (((int) dwTempLeaseTimeRemaining) < 0)
{
DPFX(DPFPREP, 1, "Registered port mapping's remote PAST server lease has already expired, returning 0 for lease time remaining.");
dwLeaseTimeRemaining = 0;
}
else
{
//
// The values are relative, so we don't have to worry about
// wraparound.
//
//if (dwTempLeaseTimeRemaining < dwLeaseTimeRemaining)
//{
dwLeaseTimeRemaining = dwTempLeaseTimeRemaining;
//}
}
}
else
{
//
// No bind ID, no public address array.
//
DNASSERT(! pRegisteredPort->HasPASTPublicAddressesArray(TRUE));
if (pRegisteredPort->IsPASTPortUnavailable(TRUE))
{
DNASSERT(pDevice != NULL);
fRegisteredWithServer = TRUE;
fPortIsUnavailable = TRUE;
DPFX(DPFPREP, 8, "The remote PAST server indicates the port(s) are unavailable.");
//
// Add in the flag indicating that there's a remote ICS server.
//
dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAY;
}
#ifdef DBG
else
{
//
// No remote PAST server or it's an unowned port.
//
if (pDevice != NULL)
{
DNASSERT(pDevice->GetPASTClientID(TRUE) == 0);
}
else
{
DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts));
}
}
#endif // DBG
}
}
else
{
//
// We're not allowed to return the remote PAST mapping.
//
DPFX(DPFPREP, 8, "Ignoring any Internet gateway mappings, LOCALFIREWALLREMAPONLY was specified.");
}
//
// Finally, check for a mapping on a local PAST server.
//
if (pRegisteredPort->GetPASTBindID(FALSE) != 0)
{
DNASSERT(pDevice != NULL);
//
// There's a bind ID, so there must be a public address array.
//
DNASSERT(pRegisteredPort->HasPASTPublicAddressesArray(FALSE));
fRegisteredWithServer = TRUE;
//
// If we didn't already get a remote mapping, return this local one.
//
if (! fFoundValidMapping)
{
//
// Make sure the local PAST server is currently giving out a valid
// public address. If so, hand it out.
//
if (pDevice->IsPASTPublicAddressAvailable(FALSE))
{
if (pdwPublicAddressesSize != NULL)
{
dwSizeRequired = pRegisteredPort->GetAddressesSize();
if ((paPublicAddresses == NULL) ||
(dwSizeRequired > (*pdwPublicAddressesSize)))
{
//
// Not enough room in buffer, return the size required
// and the BUFFERTOOSMALL error code.
//
(*pdwPublicAddressesSize) = dwSizeRequired;
hr = DPNHERR_BUFFERTOOSMALL;
}
else
{
//
// Buffer was large enough, return the size written.
//
(*pdwPublicAddressesSize) = dwSizeRequired;
pRegisteredPort->CopyPASTPublicAddresses((SOCKADDR_IN*) paPublicAddresses,
FALSE);
}
}
else
{
//
// Not using address buffer.
//
}
fFoundValidMapping = TRUE;
}
else
{
DPFX(DPFPREP, 8, "The local PAST server is not currently handing out valid public address mappings.");
}
}
else
{
DPFX(DPFPREP, 6, "Ignoring possible local PAST server mapping due to remote PAST server mapping.");
}
//
// Add in the flag indicating whether this is a PFW only or ICS server.
//
if (pDevice->HasLocalPFWOnlyPASTServer())
{
dwAddressTypeFlags |= DPNHADDRESSTYPE_LOCALFIREWALL;
}
else
{
dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAY | DPNHADDRESSTYPE_GATEWAYISLOCAL;
}
//
// Get the relative time remaining.
//
dwTempLeaseTimeRemaining = pRegisteredPort->GetPASTLeaseExpiration(FALSE) - dwCurrentTime;
if (((int) dwTempLeaseTimeRemaining) < 0)
{
DPFX(DPFPREP, 1, "Registered port mapping's local PAST server lease has already expired, returning 0 for lease time remaining.");
dwLeaseTimeRemaining = 0;
}
else
{
//
// If that time remaining is less than the remote PAST server lease
// time (if any), overwrite it with the shorter time. The values
// are relative, so we don't have to worry about wraparound.
//
if (dwTempLeaseTimeRemaining < dwLeaseTimeRemaining)
{
dwLeaseTimeRemaining = dwTempLeaseTimeRemaining;
}
}
}
else
{
//
// No bind ID, no public address array.
//
DNASSERT(! pRegisteredPort->HasPASTPublicAddressesArray(FALSE));
if (pRegisteredPort->IsPASTPortUnavailable(FALSE))
{
DNASSERT(pDevice != NULL);
fRegisteredWithServer = TRUE;
fPortIsUnavailable = TRUE;
DPFX(DPFPREP, 8, "The local PAST server indicates the port(s) are unavailable.");
//
// Add in the flag indicating whether this is a PFW only or ICS server.
//
if (pDevice->HasLocalPFWOnlyPASTServer())
{
dwAddressTypeFlags |= DPNHADDRESSTYPE_LOCALFIREWALL;
}
else
{
dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAY | DPNHADDRESSTYPE_GATEWAYISLOCAL;
}
}
#ifdef DBG
else
{
//
// No local PAST server or it's an unowned port.
//
if (pDevice != NULL)
{
DNASSERT(pDevice->GetPASTClientID(FALSE) == 0);
}
else
{
DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts));
}
}
#endif // DBG
}
this->DropLock();
fHaveLock = FALSE;
if (fRegisteredWithServer)
{
DNASSERT(dwAddressTypeFlags & (DPNHADDRESSTYPE_LOCALFIREWALL | DPNHADDRESSTYPE_GATEWAY));
if (! fFoundValidMapping)
{
if (fPortIsUnavailable)
{
//
// The servers indicated that the ports were already in use.
// Return PORTUNAVAILABLE.
//
DPFX(DPFPREP, 1, "The Internet gateway(s) could not map the port, returning PORTUNAVAILABLE.");
hr = DPNHERR_PORTUNAVAILABLE;
}
else
{
//
// The servers didn't have public addresses. Return NOMAPPING.
//
DPFX(DPFPREP, 1, "The Internet gateway(s) did not offer valid public addresses, returning NOMAPPING.");
hr = DPNHERR_NOMAPPING;
}
}
else
{
//
// One of the servers had a public address.
//
DNASSERT((hr == DPNH_OK) || (hr == DPNHERR_BUFFERTOOSMALL));
}
}
else
{
//
// The ports aren't registered, because there aren't any gateways.
// Return SERVERNOTAVAILABLE.
//
DPFX(DPFPREP, 1, "No Internet gateways, returning SERVERNOTAVAILABLE.");
hr = DPNHERR_SERVERNOTAVAILABLE;
}
//
// If the caller wants information on the type of these addresses, return
// the flags we detected.
//
if (pdwAddressTypeFlags != NULL)
{
(*pdwAddressTypeFlags) = dwAddressTypeFlags;
}
//
// Return the minimum lease time remaining that we already calculated, if
// the caller wants it.
//
if (pdwLeaseTimeRemaining != NULL)
{
(*pdwLeaseTimeRemaining) = dwLeaseTimeRemaining;
}
#ifdef DBG
//
// If the port is unavailable or there aren't any servers, we better not
// have a lease time.
//
if ((hr == DPNHERR_PORTUNAVAILABLE) ||
(hr == DPNHERR_SERVERNOTAVAILABLE))
{
DNASSERT(dwLeaseTimeRemaining == -1);
}
//
// If there aren't any servers, we better not have server flags.
//
if (hr == DPNHERR_SERVERNOTAVAILABLE)
{
DNASSERT(! (dwAddressTypeFlags & (DPNHADDRESSTYPE_LOCALFIREWALL | DPNHADDRESSTYPE_GATEWAY)));
}
#endif // DBG
DPFX(DPFPREP, 5, "Registered port 0x%p addr type flags = 0x%lx, lease time remaining = %i.",
pRegisteredPort, dwAddressTypeFlags, (int) dwLeaseTimeRemaining);
Exit:
DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
if (fHaveLock)
{
this->DropLock();
fHaveLock = FALSE;
}
goto Exit;
} // CNATHelpPAST::GetRegisteredAddresses
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::DeregisterPorts"
//=============================================================================
// CNATHelpPAST::DeregisterPorts
//-----------------------------------------------------------------------------
//
// Description: Removes the lease record for the port group and informs the
// Internet gateway server that the binding is no longer needed.
// The port mapping handle must not be used after de-registering
// it.
//
// Arguments:
// DPNHHANDLE hRegisteredPorts - Handle for a specific binding returned by
// RegisterPorts.
// DWORD dwFlags - Unused, must be zero.
//
// Returns: HRESULT
// DPNH_OK - The binding was successfully released.
// DPNHERR_GENERIC - An error occurred that prevented the
// de-registration of the ports.
// DPNHERR_INVALIDFLAGS - Invalid flags were specified.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPARAM - An invalid parameter was specified.
// DPNHERR_NOTINITIALIZED - Initialize has not been called.
// DPNHERR_OUTOFMEMORY - There is not enough memory to de-register.
// DPNHERR_REENTRANT - The interface has been re-entered on the same
// thread.
//=============================================================================
STDMETHODIMP CNATHelpPAST::DeregisterPorts(const DPNHHANDLE hRegisteredPorts,
const DWORD dwFlags)
{
HRESULT hr;
CRegisteredPort * pRegisteredPort;
BOOL fHaveLock = FALSE;
LONG lResult;
DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx)",
this, hRegisteredPorts, dwFlags);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
hr = DPNHERR_INVALIDOBJECT;
goto Failure;
}
//
// Validate the parameters.
//
pRegisteredPort = (CRegisteredPort*) hRegisteredPorts;
if (! pRegisteredPort->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid registered port mapping handle specified!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (dwFlags != 0)
{
DPFX(DPFPREP, 0, "Invalid flags specified!");
hr = DPNHERR_INVALIDFLAGS;
goto Failure;
}
//
// Attempt to take the lock, but be prepared for the re-entrancy error.
//
hr = this->TakeLock();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Could not lock object!");
goto Failure;
}
fHaveLock = TRUE;
//
// Make sure object is in right state.
//
if (! (this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED) )
{
DPFX(DPFPREP, 0, "Object not initialized!");
hr = DPNHERR_NOTINITIALIZED;
goto Failure;
}
//
// If this isn't the last user reference on the registered port, don't
// unmap it yet.
//
lResult = pRegisteredPort->DecUserRef();
if (lResult != 0)
{
DPFX(DPFPREP, 1, "Still %i references left on registered port 0x%p, not unmapping.",
lResult, pRegisteredPort);
goto Exit;
}
//
// First, unmap from remote PAST server, if necessary.
//
if (pRegisteredPort->GetPASTBindID(TRUE) != 0)
{
DNASSERT(pRegisteredPort->GetOwningDevice() != NULL);
//
// Free the port.
//
hr = this->FreePASTPort(pRegisteredPort, TRUE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't free port mapping with remote PAST server (0x%lx)! Ignoring.", hr);
//
// We'll treat this as non-fatal, but we have to dump the server.
//
this->ClearDevicesPASTServer(pRegisteredPort->GetOwningDevice(), TRUE);
hr = DPNH_OK;
}
}
//
// Then unmap from local PAST server, if necessary.
//
if (pRegisteredPort->GetPASTBindID(FALSE) != 0)
{
DNASSERT(pRegisteredPort->GetOwningDevice() != NULL);
//
// Free the port.
//
hr = this->FreePASTPort(pRegisteredPort, FALSE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't free port mapping with local PAST server (0x%lx)! Ignoring.", hr);
//
// We'll treat this as non-fatal, but we have to dump the server.
//
this->ClearDevicesPASTServer(pRegisteredPort->GetOwningDevice(), FALSE);
hr = DPNH_OK;
}
}
//
// Pull the item out of the lists.
// We have the appropriate lock.
//
pRegisteredPort->m_blGlobalList.RemoveFromList();
if (pRegisteredPort->GetOwningDevice() != NULL)
{
pRegisteredPort->ClearDeviceOwner();
}
else
{
DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts));
pRegisteredPort->m_blDeviceList.RemoveFromList();
}
pRegisteredPort->ClearPrivateAddresses();
delete pRegisteredPort;
Exit:
if (fHaveLock)
{
this->DropLock();
fHaveLock = FALSE;
}
DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::DeregisterPorts
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::QueryAddress"
//=============================================================================
// CNATHelpPAST::QueryAddress
//-----------------------------------------------------------------------------
//
// Description: Some Internet gateways do not loopback if an attempt is made
// to connect to an address behind (on the same private side of)
// the public interface. QueryAddress is used to determine a
// possible private alias for a given public address.
//
// In most cases, this function is called prior to connecting
// to a new address. pSourceAddress should contain the address of
// the socket that will perform the connect. Similar to
// RegisterPorts, the address may be INADDR_ANY, in which case the
// "best" server will be used. Since the server chosen may not be
// optimal for a particular application, it is recommended that a
// specific network interface be used instead of INADDR_ANY, when
// possible.
//
// If no mapping for that address has been made by the gateway,
// the error code DPNHERR_NOMAPPING is returned. When the
// DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED flag is used, an
// extra effort is made to determine whether the address is behind
// the same Internet gateway without being mapped on the gateway.
// If that is the case, DPNHERR_NOMAPPINGBUTPRIVATE is returned.
// DPNHERR_NOMAPPING is still returned for addresses that are
// neither mapped nor private.
//
// pQueryAddress may not be INADDR_ANY or INADDR_BROADCAST.
// The port component may be zero if and only if the
// DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED flag is used. If
// the port is zero, a specific mapping cannot be verified, and
// only the DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED aspect of
// the address is tested.
//
// The resulting address (or lack thereof) can be cached for
// quick future retrieval using the DPNHQUERYADDRESS_CACHEFOUND
// and DPNHQUERYADDRESS_CACHENOTFOUND flags. The cached mappings
// will expire in 1 minute, or whenever the server's address
// changes.
//
// If the given source address is not currently connected to an
// Internet gateway, then the error DPNHERR_SERVERNOTAVAILABLE is
// returned.
//
// If GetCaps has not been previously called with the
// DPNHGETCAPS_UPDATESERVERSTATUS flag at least once, then the
// error code DPNHERR_UPDATESERVERSTATUS is returned.
//
// Arguments:
// SOCKADDR * pSourceAddress - Address for network interface that is using
// the address in question.
// SOCKADDR * pQueryAddress - Address to look up.
// SOCKADDR * pResponseAddress - Place to store public address, if one exists.
// int iAddressesSize - Size of the SOCKADDR structure used for the
// pSourceAddress, pQueryAddress and
// pResponseAddress buffers.
// DWORD dwFlags - Flags to use when querying
// (DPNHQUERYADDRESS_xxx).
//
// Returns: HRESULT
// DPNH_OK - The address was found and its mapping was
// stored in pResponseAddress.
// DPNHERR_GENERIC - An error occurred that prevented mapping the
// requested address.
// DPNHERR_INVALIDFLAGS - Invalid flags were specified.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPARAM - An invalid parameter was specified.
// DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
// DPNHERR_NOMAPPING - The server indicated that no mapping for the
// requested address was found.
// DPNHERR_NOMAPPINGBUTPRIVATE - The server indicated that no mapping was
// found, but it is a private address.
// DPNHERR_NOTINITIALIZED - Initialize has not been called.
// DPNHERR_OUTOFMEMORY - There is not enough memory to query.
// DPNHERR_REENTRANT - The interface has been re-entered on the same
// thread.
// DPNHERR_SERVERNOTAVAILABLE - There are no servers to query.
// DPNHERR_UPDATESERVERSTATUS - GetCaps has not been called with the
// DPNHGETCAPS_UPDATESERVERSTATUS flag yet.
//=============================================================================
STDMETHODIMP CNATHelpPAST::QueryAddress(const SOCKADDR * const pSourceAddress,
const SOCKADDR * const pQueryAddress,
SOCKADDR * const pResponseAddress,
const int iAddressesSize,
const DWORD dwFlags)
{
HRESULT hr;
HRESULT temphr;
BOOL fHaveLock = FALSE;
CDevice * pDevice;
SOCKADDR_IN * psaddrinNextServerQueryAddress = NULL;
DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, %i, 0x%lx)",
this, pSourceAddress, pQueryAddress, pResponseAddress, iAddressesSize,
dwFlags);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
hr = DPNHERR_INVALIDOBJECT;
goto Failure;
}
//
// Validate the parameters.
//
if (pSourceAddress == NULL)
{
DPFX(DPFPREP, 0, "Invalid source address specified!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (pQueryAddress == NULL)
{
DPFX(DPFPREP, 0, "Invalid query address specified!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (pResponseAddress == NULL)
{
DPFX(DPFPREP, 0, "Invalid response address specified!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (iAddressesSize < sizeof(SOCKADDR_IN))
{
DPFX(DPFPREP, 0, "The address buffers must be at least %i bytes!",
sizeof(SOCKADDR_IN));
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (IsBadReadPtr(pSourceAddress, sizeof(SOCKADDR_IN)))
{
DPFX(DPFPREP, 0, "Invalid source address buffer used!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (IsBadReadPtr(pQueryAddress, sizeof(SOCKADDR_IN)))
{
DPFX(DPFPREP, 0, "Invalid query address buffer used!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if (IsBadWritePtr(pResponseAddress, sizeof(SOCKADDR_IN)))
{
DPFX(DPFPREP, 0, "Invalid response address buffer used!");
hr = DPNHERR_INVALIDPOINTER;
goto Failure;
}
if ((((SOCKADDR_IN*) pSourceAddress)->sin_family != AF_INET) ||
(((SOCKADDR_IN*) pQueryAddress)->sin_family != AF_INET))
{
DPFX(DPFPREP, 0, "Source or query address is not AF_INET, only IPv4 addresses are supported!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_addr == INADDR_BROADCAST)
{
DPFX(DPFPREP, 0, "Source address cannot be broadcast address!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if ((((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_addr == INADDR_ANY) ||
(((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_addr == INADDR_BROADCAST))
{
//
// Don't use inet_ntoa because we may not be initialized yet.
//
DPFX(DPFPREP, 0, "Query address (%u.%u.%u.%u) is invalid, cannot be zero or broadcast!",
((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b1,
((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b2,
((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b3,
((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b4);
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (dwFlags & ~(DPNHQUERYADDRESS_TCP | DPNHQUERYADDRESS_CACHEFOUND | DPNHQUERYADDRESS_CACHENOTFOUND | DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED))
{
DPFX(DPFPREP, 0, "Invalid flags specified!");
hr = DPNHERR_INVALIDFLAGS;
goto Failure;
}
if ((((SOCKADDR_IN*) pQueryAddress)->sin_port == 0) &&
(! (dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED)))
{
DPFX(DPFPREP, 0, "Query address port cannot be zero unless CHECKFORPRIVATEBUTUNMAPPED is specified!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
//
// Attempt to take the lock, but be prepared for the re-entrancy error.
//
hr = this->TakeLock();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Could not lock object!");
goto Failure;
}
fHaveLock = TRUE;
//
// Make sure object is in right state.
//
if (! (this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED) )
{
DPFX(DPFPREP, 0, "Object not initialized!");
hr = DPNHERR_NOTINITIALIZED;
goto Failure;
}
if (this->m_dwLastUpdateServerStatusTime == 0)
{
DPFX(DPFPREP, 0, "GetCaps has not been called with UPDATESERVERSTATUS flag yet!");
hr = DPNHERR_UPDATESERVERSTATUS;
goto Failure;
}
pDevice = this->FindMatchingDevice((SOCKADDR_IN*) pSourceAddress, TRUE);
if (pDevice == NULL)
{
DPFX(DPFPREP, 1, "Couldn't determine appropriate owning device for given source address, returning SERVERNOTAVAILABLE.");
hr = DPNHERR_SERVERNOTAVAILABLE;
goto Exit;
}
//
// Assume no servers are available. This will get overridden as
// appropriate.
//
hr = DPNHERR_SERVERNOTAVAILABLE;
//
// Start by querying the address passed in.
//
psaddrinNextServerQueryAddress = (SOCKADDR_IN*) pQueryAddress;
//
// If the port is zero, then we can't actually lookup a mapping. Just do
// the address locality check.
//
if (psaddrinNextServerQueryAddress->sin_port == 0)
{
//
// We should have caught this in parameter validation above, but I'm
// being paranoid.
//
DNASSERT(dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED);
//
// We don't cache these results, since there's no server (and thus, no
// network traffic) associated with it. No need to look anything up.
//
//
// If there aren't any servers, then no need to check.
//
if ((pDevice->GetPASTClientID(TRUE) == 0) &&
(pDevice->GetPASTClientID(FALSE) == 0))
{
DPFX(DPFPREP, 5, "No port queried and there aren't any gateways, returning SERVERNOTAVAILABLE.");
hr = DPNHERR_SERVERNOTAVAILABLE;
}
else
{
//
// There is an Internet gateway of some kind, our locality check
// would be meaningful.
//
if (this->IsAddressLocal(pDevice, psaddrinNextServerQueryAddress))
{
DPFX(DPFPREP, 5, "No port queried, but address appears to be local, returning NOMAPPINGBUTPRIVATE.");
hr = DPNHERR_NOMAPPINGBUTPRIVATE;
}
else
{
DPFX(DPFPREP, 5, "No port queried and address does not appear to be local, returning NOMAPPING.");
hr = DPNHERR_NOMAPPING;
}
}
//
// We've done all we can do.
//
goto Exit;
}
//
// First try to query the remote PAST server, if there is one.
//
if (pDevice->GetPASTClientID(TRUE) != 0)
{
DNASSERT(pDevice->GetPASTSocket() != INVALID_SOCKET);
hr = this->InternalPASTQueryAddress(pDevice,
psaddrinNextServerQueryAddress,
(SOCKADDR_IN*) pResponseAddress,
dwFlags,
TRUE);
switch (hr)
{
case DPNH_OK:
{
//
// If there is a local PAST server, we want to query it for the
// public address we were just returned. This technically
// would allow us to chain more than one level of Internet
// gateways, however it really won't work. I'm just doing it
// because it doesn't hurt.
//
// Handing the same buffer to InternalPASTQueryAddress for both
// the query and response addresses is okay.
//
psaddrinNextServerQueryAddress = (SOCKADDR_IN*) pResponseAddress;
break;
}
case DPNHERR_NOMAPPING:
{
//
// There's no mapping, continue to the local PAST server query.
//
break;
}
case DPNHERR_NOMAPPINGBUTPRIVATE:
{
//
// There's no mapping although the address is private, continue
// to the local PAST server query.
//
break;
}
case DPNHERR_SERVERNOTRESPONDING:
{
//
// The server stopped responding, but treat that as non-fatal.
// It will be detected properly the next time GetCaps is
// called. We set the return code to DPNHERR_NOMAPPING in case
// there is no local PAST server to override the value.
//
DPFX(DPFPREP, 1, "Remote PAST server stopped responding while querying port mapping.");
hr = DPNHERR_NOMAPPING;
//
// Continue through to querying the local server.
//
break;
}
default:
{
DPFX(DPFPREP, 0, "Querying remote PAST server for port mapping failed!");
goto Failure;
break;
}
}
}
else
{
//
// No remote PAST server.
//
}
//
// Then try to query the local PAST server, if there is one.
//
if (pDevice->GetPASTClientID(FALSE) != 0)
{
DNASSERT(pDevice->GetPASTSocket() != INVALID_SOCKET);
temphr = this->InternalPASTQueryAddress(pDevice,
psaddrinNextServerQueryAddress,
(SOCKADDR_IN*) pResponseAddress,
dwFlags,
FALSE);
switch (temphr)
{
case DPNH_OK:
{
//
// Success!
//
hr = DPNH_OK;
break;
}
case DPNHERR_NOMAPPING:
{
//
// There's no mapping. Overwrite the return value if we didn't
// have a remote PAST server to query.
//
if (hr == DPNHERR_SERVERNOTAVAILABLE)
{
hr = DPNHERR_NOMAPPING;
}
break;
}
case DPNHERR_NOMAPPINGBUTPRIVATE:
{
//
// There's no mapping although the address is private.
// Overwrite the return value if we didn't have a remote PAST
// server to query.
//
if (hr == DPNHERR_SERVERNOTAVAILABLE)
{
hr = DPNHERR_NOMAPPINGBUTPRIVATE;
}
break;
}
case DPNHERR_SERVERNOTRESPONDING:
{
//
// The server stopped responding, but treat that as non-fatal.
// It will be detected properly the next time GetCaps is
// called.
//
DPFX(DPFPREP, 1, "Local PAST server stopped responding while querying port mapping.");
//
// Overwrite the return value if we didn't have a remote PAST
// server to query.
//
if (hr == DPNHERR_SERVERNOTAVAILABLE)
{
hr = DPNHERR_NOMAPPING;
}
break;
}
default:
{
DPFX(DPFPREP, 0, "Querying local PAST server for port mapping failed!");
goto Failure;
break;
}
}
}
else
{
//
// No local PAST server.
//
}
//
// If we got here with hr still set to SERVERNOTAVAILABLE, that means
// there weren't any servers. The error code is appropriate, leave it
// alone.
//
#ifdef DBG
if (hr == DPNHERR_SERVERNOTAVAILABLE)
{
DPFX(DPFPREP, 1, "No Internet gateways, unable to query port mapping.");
}
#endif // DBG
Exit:
if (fHaveLock)
{
this->DropLock();
fHaveLock = FALSE;
}
DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::QueryAddress
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::SetAlertEvent"
//=============================================================================
// CNATHelpPAST::SetAlertEvent
//-----------------------------------------------------------------------------
//
// Description: This function allows the user to specify an event that will
// be set when some maintenance needs to be performed. The user
// should call GetCaps using the DPNHGETCAPS_UPDATESERVERSTATUS
// flag when the event is signalled.
//
// This function is not available on Windows 95 without WinSock
// 2, may only be called once, and cannot be used after
// SetAlertIOCompletionPort is called.
//
// Note that the event is used in addition to the regular
// polling of GetCaps, it simply allows the polling to be less
// frequent.
//
// Arguments:
// HANDLE hEvent - Handle to event to signal when GetCaps is to be called.
// DWORD dwFlags - Unused, must be zero.
//
// Returns: HRESULT
// DPNH_OK - The event was successfully registered.
// DPNHERR_GENERIC - An error occurred that prevented registering the
// event.
// DPNHERR_INVALIDFLAGS - Invalid flags were specified.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPARAM - An invalid parameter was specified.
// DPNHERR_NOTINITIALIZED - Initialize has not been called.
// DPNHERR_OUTOFMEMORY - There is not enough memory.
// DPNHERR_REENTRANT - The interface has been re-entered on the same
// thread.
//=============================================================================
STDMETHODIMP CNATHelpPAST::SetAlertEvent(const HANDLE hEvent,
const DWORD dwFlags)
{
HRESULT hr;
BOOL fHaveLock = FALSE;
DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx)", this, hEvent, dwFlags);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
hr = DPNHERR_INVALIDOBJECT;
goto Failure;
}
//
// Validate the parameters.
//
if (hEvent == NULL)
{
DPFX(DPFPREP, 0, "Invalid event handle specified!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (dwFlags != 0)
{
DPFX(DPFPREP, 0, "Invalid flags specified!");
hr = DPNHERR_INVALIDFLAGS;
goto Failure;
}
//
// Attempt to take the lock, but be prepared for the re-entrancy error.
//
hr = this->TakeLock();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Could not lock object!");
goto Failure;
}
fHaveLock = TRUE;
//
// Make sure object is in right state.
//
if (! (this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED))
{
DPFX(DPFPREP, 0, "Object not initialized!");
hr = DPNHERR_NOTINITIALIZED;
goto Failure;
}
if (this->m_dwFlags & NATHELPPASTOBJ_WINSOCK1)
{
DPFX(DPFPREP, 0, "Cannot use alert mechanism on WinSock 1!");
hr = DPNHERR_GENERIC;
goto Failure;
}
if ((this->m_hAlertEvent != NULL) || (this->m_hAlertIOCompletionPort != NULL))
{
DPFX(DPFPREP, 0, "An alert event or I/O completion port has already been set!");
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Now save the event handle.
//
if (! DuplicateHandle(GetCurrentProcess(),
hEvent,
GetCurrentProcess(),
&this->m_hAlertEvent,
0,
FALSE,
DUPLICATE_SAME_ACCESS))
{
#ifdef DBG
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Couldn't duplicate event (error = %u)!", dwError);
#endif // DBG
DNASSERT(this->m_hAlertEvent == NULL);
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
//
// Create overlapped structure. Don't allocate it through DNMalloc,
// because we may have to leak it on purpose. We don't want those memory
// allocation asserts firing in that case.
//
this->m_polAddressListChange = (WSAOVERLAPPED*) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(WSAOVERLAPPED));
if (this->m_polAddressListChange == NULL)
{
//
// Close the alert handle we set.
//
CloseHandle(this->m_hAlertEvent);
this->m_hAlertEvent = NULL;
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
//
// Save the event in the address list change overlapped structure.
//
this->m_polAddressListChange->hEvent = this->m_hAlertEvent;
//
// Start getting notified of local address changes.
//
hr = this->RequestLocalAddressListChangeNotification();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
//
// Free the memory we allocated.
//
HeapFree(GetProcessHeap(), 0, this->m_polAddressListChange);
this->m_polAddressListChange = NULL;
//
// Close the alert handle we set.
//
CloseHandle(this->m_hAlertEvent);
this->m_hAlertEvent = NULL;
goto Failure;
}
Exit:
if (fHaveLock)
{
this->DropLock();
fHaveLock = FALSE;
}
DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::SetAlertEvent
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::SetAlertIOCompletionPort"
//=============================================================================
// CNATHelpPAST::SetAlertIOCompletionPort
//-----------------------------------------------------------------------------
//
// Description: This function allows the user to specify an I/O completion
// port that will receive notification when some maintenance needs
// to be performed. The user should call GetCaps using the
// DPNHGETCAPS_UPDATESERVERSTATUS flag when the packet with the
// given completion key is dequeued.
//
// This function is only available on Windows NT, may only be
// called once, and cannot be used after SetAlertEvent is called.
//
// Note that the completion port is used in addition to the
// regular polling of GetCaps, it simply allows the polling to be
// less frequent.
//
// Arguments:
// HANDLE hIOCompletionPort - Handle to I/O completion port which will
// be used to signal when GetCaps is to be
// called.
// DWORD dwCompletionKey - Key to use when indicating I/O
// completion.
// DWORD dwNumConcurrentThreads - Number of concurrent threads allowed to
// process, or zero for default.
// DWORD dwFlags - Unused, must be zero.
//
// Returns: HRESULT
// DPNH_OK - The I/O completion port was successfully
// registered.
// DPNHERR_GENERIC - An error occurred that prevented registering the
// I/O completion port.
// DPNHERR_INVALIDFLAGS - Invalid flags were specified.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPARAM - An invalid parameter was specified.
// DPNHERR_NOTINITIALIZED - Initialize has not been called.
// DPNHERR_OUTOFMEMORY - There is not enough memory.
// DPNHERR_REENTRANT - The interface has been re-entered on the same
// thread.
//=============================================================================
STDMETHODIMP CNATHelpPAST::SetAlertIOCompletionPort(const HANDLE hIOCompletionPort,
const DWORD dwCompletionKey,
const DWORD dwNumConcurrentThreads,
const DWORD dwFlags)
{
HRESULT hr;
BOOL fHaveLock = FALSE;
HANDLE hIOCompletionPortResult;
DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx, %u, 0x%lx)",
this, hIOCompletionPort, dwCompletionKey, dwNumConcurrentThreads, dwFlags);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
hr = DPNHERR_INVALIDOBJECT;
goto Failure;
}
//
// Validate the parameters.
//
if (hIOCompletionPort == NULL)
{
DPFX(DPFPREP, 0, "Invalid I/O completion port handle specified!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (dwFlags != 0)
{
DPFX(DPFPREP, 0, "Invalid flags specified!");
hr = DPNHERR_INVALIDFLAGS;
goto Failure;
}
//
// Attempt to take the lock, but be prepared for the re-entrancy error.
//
hr = this->TakeLock();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Could not lock object!");
goto Failure;
}
fHaveLock = TRUE;
//
// Make sure object is in right state.
//
if (! (this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED))
{
DPFX(DPFPREP, 0, "Object not initialized!");
hr = DPNHERR_NOTINITIALIZED;
goto Failure;
}
if (this->m_dwFlags & NATHELPPASTOBJ_WINSOCK1)
{
DPFX(DPFPREP, 0, "Cannot use alert mechanism on WinSock 1!");
hr = DPNHERR_GENERIC;
goto Failure;
}
if ((this->m_hAlertEvent != NULL) || (this->m_hAlertIOCompletionPort != NULL))
{
DPFX(DPFPREP, 0, "An alert event or I/O completion port has already been set!");
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Now save the I/O completion port handle.
//
if (! DuplicateHandle(GetCurrentProcess(),
hIOCompletionPort,
GetCurrentProcess(),
&this->m_hAlertIOCompletionPort,
0,
FALSE,
DUPLICATE_SAME_ACCESS))
{
#ifdef DBG
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Couldn't duplicate I/O completion port (error = %u)!", dwError);
#endif // DBG
DNASSERT(this->m_hAlertIOCompletionPort == NULL);
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
this->m_dwAlertCompletionKey = dwCompletionKey;
//
// Associate our Ioctl socket with this IO completion port.
//
DNASSERT(this->m_sIoctls != INVALID_SOCKET);
hIOCompletionPortResult = CreateIoCompletionPort((HANDLE) this->m_sIoctls,
this->m_hAlertIOCompletionPort,
dwCompletionKey,
dwNumConcurrentThreads);
if (hIOCompletionPortResult == NULL)
{
#ifdef DBG
DWORD dwError;
dwError = GetLastError();
DPFX(DPFPREP, 0, "Couldn't associate I/O completion port with Ioctl socket (error = %u)!", dwError);
#endif // DBG
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// We should have just gotten the same I/O completion port back.
//
DNASSERT(hIOCompletionPortResult == this->m_hAlertIOCompletionPort);
//
// Create overlapped structure. Don't allocate it through DNMalloc,
// because we may have to leak it on purpose. We don't want those memory
// allocation asserts firing in that case.
//
this->m_polAddressListChange = (WSAOVERLAPPED*) HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(WSAOVERLAPPED));
if (this->m_polAddressListChange == NULL)
{
//
// Close the alert IOCP we set.
//
CloseHandle(this->m_hAlertIOCompletionPort);
this->m_hAlertIOCompletionPort = NULL;
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
//
// Start getting notified of local address changes.
//
hr = this->RequestLocalAddressListChangeNotification();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
//
// Free the memory we allocated.
//
HeapFree(GetProcessHeap(), 0, this->m_polAddressListChange);
this->m_polAddressListChange = NULL;
//
// Close the alert IOCP we set.
//
CloseHandle(this->m_hAlertIOCompletionPort);
this->m_hAlertIOCompletionPort = NULL;
goto Failure;
}
Exit:
if (fHaveLock)
{
this->DropLock();
fHaveLock = FALSE;
}
DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::SetAlertIOCompletionPort
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::ExtendRegisteredPortsLease"
//=============================================================================
// CNATHelpPAST::ExtendRegisteredPortsLease
//-----------------------------------------------------------------------------
//
// Description: Manually extends the lease of the given registered port
// mapping by the requested time. The periodic calling of GetCaps
// can take care of this for the user, this function is only
// necessary to change the lease extension time or for finer
// control of individual mappings.
//
// The user should specify a requested lease extension time
// that the server will attempt to honor. It will be added to any
// time remaining in the existing lease, and the new total can be
// retrieved by calling GetRegisteredAddresses.
//
// Arguments:
// DPNHHANDLE hRegisteredPorts - Handle for a specific binding returned by
// RegisterPorts.
// DWORD dwLeaseTime - Requested time, in milliseconds, to
// extend the lease. If 0, the previous
// requested lease time is used.
// DWORD dwFlags - Unused, must be zero.
//
// Returns: HRESULT
// DPNH_OK - The lease was successfully extended.
// DPNHERR_GENERIC - An error occurred that prevented the extending
// the lease.
// DPNHERR_INVALIDFLAGS - Invalid flags were specified.
// DPNHERR_INVALIDOBJECT - The interface object is invalid.
// DPNHERR_INVALIDPARAM - An invalid parameter was specified.
// DPNHERR_NOTINITIALIZED - Initialize has not been called.
// DPNHERR_OUTOFMEMORY - There is not enough memory to extend the lease.
// DPNHERR_REENTRANT - The interface has been re-entered on the same
// thread.
//=============================================================================
STDMETHODIMP CNATHelpPAST::ExtendRegisteredPortsLease(const DPNHHANDLE hRegisteredPorts,
const DWORD dwLeaseTime,
const DWORD dwFlags)
{
HRESULT hr;
CRegisteredPort * pRegisteredPort;
CDevice * pDevice;
BOOL fHaveLock = FALSE;
DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, %u, 0x%lx)",
this, hRegisteredPorts, dwLeaseTime, dwFlags);
//
// Validate the object.
//
if (! this->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
hr = DPNHERR_INVALIDOBJECT;
goto Failure;
}
//
// Validate the parameters.
//
pRegisteredPort = (CRegisteredPort*) hRegisteredPorts;
if (! pRegisteredPort->IsValidObject())
{
DPFX(DPFPREP, 0, "Invalid registered port mapping handle specified!");
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
if (dwFlags != 0)
{
DPFX(DPFPREP, 0, "Invalid flags specified!");
hr = DPNHERR_INVALIDFLAGS;
goto Failure;
}
//
// Attempt to take the lock, but be prepared for the re-entrancy error.
//
hr = this->TakeLock();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Could not lock object!");
goto Failure;
}
fHaveLock = TRUE;
//
// Make sure object is in right state.
//
if (! (this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED) )
{
DPFX(DPFPREP, 0, "Object not initialized!");
hr = DPNHERR_NOTINITIALIZED;
goto Failure;
}
//
// If they wanted to change the lease time, update it.
//
if (dwLeaseTime != 0)
{
pRegisteredPort->UpdateRequestedLeaseTime(dwLeaseTime);
}
pDevice = pRegisteredPort->GetOwningDevice();
//
// If the ports are mapped on a remote PAST servers, extend that lease.
//
if (pRegisteredPort->GetPASTBindID(TRUE) != 0)
{
DNASSERT(pDevice != NULL);
hr = this->ExtendPASTLease(pRegisteredPort, TRUE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't extend port mapping lease on remote PAST server (0x%lx)! Ignoring.", hr);
//
// We'll treat this as non-fatal, but we have to dump the server.
//
this->ClearDevicesPASTServer(pDevice, TRUE);
hr = DPNH_OK;
}
}
else
{
DPFX(DPFPREP, 2, "Port mapping not registered with remote PAST server.");
}
//
// Next extend the mappings on the local PAST server, if possible.
//
if (pRegisteredPort->GetPASTBindID(FALSE) != 0)
{
DNASSERT(pDevice != NULL);
hr = this->ExtendPASTLease(pRegisteredPort, FALSE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't extend port mapping lease on local PAST server (0x%lx)! Ignoring.", hr);
//
// We'll treat this as non-fatal, but we have to dump the server.
//
this->ClearDevicesPASTServer(pDevice, FALSE);
hr = DPNH_OK;
}
}
else
{
DPFX(DPFPREP, 2, "Port mapping not registered with local PAST server.");
}
this->DropLock();
fHaveLock = FALSE;
Exit:
DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
if (fHaveLock)
{
this->DropLock();
fHaveLock = FALSE;
}
goto Exit;
} // CNATHelpPAST::ExtendRegisteredPortsLease
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::InitializeObject"
//=============================================================================
// CNATHelpPAST::InitializeObject
//-----------------------------------------------------------------------------
//
// Description: Sets up the object for use like the constructor, but may
// fail with OUTOFMEMORY. Should only be called by class factory
// creation routine.
//
// Arguments: None.
//
// Returns: HRESULT
// S_OK - Initialization was successful.
// E_OUTOFMEMORY - There is not enough memory to initialize.
//=============================================================================
HRESULT CNATHelpPAST::InitializeObject(void)
{
HRESULT hr;
DPFX(DPFPREP, 5, "(0x%p) Enter", this);
DNASSERT(this->IsValidObject());
//
// Create the lock.
//
if (! DNInitializeCriticalSection(&this->m_csLock))
{
hr = E_OUTOFMEMORY;
goto Failure;
}
//
// Don't allow critical section reentry.
//
DebugSetCriticalSectionRecursionCount(&this->m_csLock, 0);
hr = S_OK;
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::InitializeObject
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::UninitializeObject"
//=============================================================================
// CNATHelpPAST::UninitializeObject
//-----------------------------------------------------------------------------
//
// Description: Cleans up the object like the destructor, mostly to balance
// InitializeObject.
//
// Arguments: None.
//
// Returns: None.
//=============================================================================
void CNATHelpPAST::UninitializeObject(void)
{
DPFX(DPFPREP, 5, "(0x%p) Enter", this);
DNASSERT(this->IsValidObject());
DNDeleteCriticalSection(&this->m_csLock);
DPFX(DPFPREP, 5, "(0x%p) Leave", this);
} // CNATHelpPAST::UninitializeObject
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::TakeLock"
//=============================================================================
// CNATHelpPAST::TakeLock
//-----------------------------------------------------------------------------
//
// Description: Takes the main object lock.
//
// Arguments: None.
//
// Returns: DPNH_OK if lock was taken successfully, DPNHERR_REENTRANT if lock
// was re-entered.
//=============================================================================
HRESULT CNATHelpPAST::TakeLock(void)
{
HRESULT hr = DPNH_OK;
#ifdef DBG
DWORD dwStartTime;
dwStartTime = timeGetTime();
#endif // DBG
DNEnterCriticalSection(&this->m_csLock);
//
// If this same thread is already holding the lock, then bail.
//
if (this->m_dwLockThreadID == GetCurrentThreadId())
{
DPFX(DPFPREP, 0, "Thread re-entering!");
goto Failure;
}
#ifdef DBG
DPFX(DPFPREP, 8, "Took main object lock, elapsed time = %u ms.",
(timeGetTime() - dwStartTime));
#endif // DBG
//
// Save this thread's ID so we know who's holding the lock.
//
this->m_dwLockThreadID = GetCurrentThreadId();
Exit:
return hr;
Failure:
//
// We're reentering. Drop the lock and return the failure.
//
DNLeaveCriticalSection(&this->m_csLock);
hr = DPNHERR_REENTRANT;
goto Exit;
} // CNATHelpPAST::TakeLock
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::DropLock"
//=============================================================================
// CNATHelpPAST::DropLock
//-----------------------------------------------------------------------------
//
// Description: Drops the main object lock.
//
// Arguments: None.
//
// Returns: None.
//=============================================================================
void CNATHelpPAST::DropLock(void)
{
DNASSERT(this->m_dwLockThreadID == GetCurrentThreadId());
this->m_dwLockThreadID = 0;
DNLeaveCriticalSection(&this->m_csLock);
DPFX(DPFPREP, 8, "Dropped main object lock.");
} // CNATHelpPAST::DropLock
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::LoadWinSockFunctionPointers"
//=============================================================================
// CNATHelpPAST::LoadWinSockFunctionPointers
//-----------------------------------------------------------------------------
//
// Description: Loads pointers to all the functions that we use in WinSock.
//
// The object lock is assumed to be held.
//
// Arguments: None.
//
// Returns: HRESULT
// DPNH_OK - Loading was successful.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::LoadWinSockFunctionPointers(void)
{
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#ifdef DBG
#define PRINTERRORIFDEBUG(name) \
{\
dwError = GetLastError();\
DPFX(DPFPREP, 0, "Couldn't get \"%hs\" function! 0x%lx", name, dwError);\
}
#else
#define PRINTERRORIFDEBUG(name)
#endif // DBG
#define LOADWINSOCKFUNCTION(var, proctype, name) \
{\
var = (##proctype) GetProcAddress(this->m_hWinSockDLL, name);\
if (var == NULL)\
{\
PRINTERRORIFDEBUG(name);\
hr = DPNHERR_GENERIC;\
goto Failure;\
}\
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
HRESULT hr = DPNH_OK;
#ifdef DBG
DWORD dwError;
#endif // DBG
LOADWINSOCKFUNCTION(this->m_pfnWSAStartup, LPFN_WSASTARTUP, "WSAStartup");
LOADWINSOCKFUNCTION(this->m_pfnWSACleanup, LPFN_WSACLEANUP, "WSACleanup");
LOADWINSOCKFUNCTION(this->m_pfnWSAGetLastError, LPFN_WSAGETLASTERROR, "WSAGetLastError");
LOADWINSOCKFUNCTION(this->m_pfnsocket, LPFN_SOCKET, "socket");
LOADWINSOCKFUNCTION(this->m_pfnclosesocket, LPFN_CLOSESOCKET, "closesocket");
LOADWINSOCKFUNCTION(this->m_pfnbind, LPFN_BIND, "bind");
LOADWINSOCKFUNCTION(this->m_pfnsetsockopt, LPFN_SETSOCKOPT, "setsockopt");
LOADWINSOCKFUNCTION(this->m_pfngetsockname, LPFN_GETSOCKNAME, "getsockname");
LOADWINSOCKFUNCTION(this->m_pfnselect, LPFN_SELECT, "select");
LOADWINSOCKFUNCTION(this->m_pfn__WSAFDIsSet, LPFN___WSAFDISSET, "__WSAFDIsSet");
LOADWINSOCKFUNCTION(this->m_pfnrecvfrom, LPFN_RECVFROM, "recvfrom");
LOADWINSOCKFUNCTION(this->m_pfnsendto, LPFN_SENDTO, "sendto");
LOADWINSOCKFUNCTION(this->m_pfngethostname, LPFN_GETHOSTNAME, "gethostname");
LOADWINSOCKFUNCTION(this->m_pfngethostbyname, LPFN_GETHOSTBYNAME, "gethostbyname");
LOADWINSOCKFUNCTION(this->m_pfninet_addr, LPFN_INET_ADDR, "inet_addr");
if (! (this->m_dwFlags & NATHELPPASTOBJ_WINSOCK1))
{
LOADWINSOCKFUNCTION(this->m_pfnWSASocketA, LPFN_WSASOCKETA, "WSASocketA");
LOADWINSOCKFUNCTION(this->m_pfnWSAIoctl, LPFN_WSAIOCTL, "WSAIoctl");
LOADWINSOCKFUNCTION(this->m_pfnWSAGetOverlappedResult, LPFN_WSAGETOVERLAPPEDRESULT, "WSAGetOverlappedResult");
}
Exit:
return hr;
Failure:
hr = DPNHERR_GENERIC;
goto Exit;
} // CNATHelpPAST::LoadWinSockFunctionPointers
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::CheckForNewDevices"
//=============================================================================
// CNATHelpPAST::CheckForNewDevices
//-----------------------------------------------------------------------------
//
// Description: Detects new IP capable devices that have been added and
// removes old ones no longer available.
//
// The object lock is assumed to be held.
//
// Arguments:
// BOOL * pfFoundNewDevices Pointer to boolean to set to TRUE if new
// devices were added, or NULL if don't care.
//
// Returns: HRESULT
// DPNH_OK - The check was successful.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::CheckForNewDevices(BOOL * const pfFoundNewDevices)
{
HRESULT hr = DPNH_OK;
DWORD dwError;
int iReturn;
char szName[1000];
PHOSTENT phostent;
IN_ADDR ** ppinaddr;
DWORD dwAddressesSize = 0;
DWORD dwNumAddresses = 0;
IN_ADDR * painaddrAddresses = NULL;
CBilink * pBilinkDevice;
CDevice * pDevice = NULL; // NULL it for PREfix, even though fDeviceCreated guards it
BOOL fDeviceCreated = FALSE;
BOOL fFound;
CBilink * pBilinkRegPort;
CRegisteredPort * pRegisteredPort;
SOCKET sTemp = INVALID_SOCKET;
SOCKADDR_IN saddrinTemp;
//int iAddressSize;
BOOL fTemp;
DWORD dwTemp;
SOCKET_ADDRESS * paSocketAddresses;
DPFX(DPFPREP, 5, "(0x%p) Enter", this);
//
// Handle any address list change Ioctl completions that may have gotten us
// here.
//
if ((this->m_hAlertEvent != NULL) ||
(this->m_hAlertIOCompletionPort != NULL))
{
DNASSERT(this->m_sIoctls != INVALID_SOCKET);
DNASSERT(this->m_polAddressListChange != NULL);
if (this->m_pfnWSAGetOverlappedResult(this->m_sIoctls, //
this->m_polAddressListChange, //
&dwTemp, // ignore bytes transferred
FALSE, // don't wait
&dwTemp)) // ignore flags
{
DPFX(DPFPREP, 1, "Received address list change notification.");
//
// Overlapped result completed. Reissue it.
//
hr = this->RequestLocalAddressListChangeNotification();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
goto Failure;
}
}
else
{
//
// Figure out what error it was.
//
dwError = this->m_pfnWSAGetLastError();
switch (dwError)
{
case WSA_IO_INCOMPLETE:
{
//
// It hasn't completed yet.
//
break;
}
case ERROR_OPERATION_ABORTED:
{
//
// The thread that we originally submitted the Ioctl on
// went away and so the OS kindly cancelled the operation
// on us. How nice. Well, let's try resubmitting it.
//
DPFX(DPFPREP, 1, "Thread that submitted previous address list change notification went away, rerequesting.");
hr = this->RequestLocalAddressListChangeNotification();
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
goto Failure;
}
break;
}
default:
{
DPFX(DPFPREP, 0, "Couldn't get overlapped result, error = %u! Ignoring.", dwError);
break;
}
} // end switch (on error)
}
}
//
// If we're on WinSock 2, let's try getting the address list with
// an Ioctl.
//
if (! (this->m_dwFlags & NATHELPPASTOBJ_WINSOCK1))
{
DNASSERT(this->m_sIoctls != INVALID_SOCKET);
DNASSERT(this->m_pfnWSAIoctl != NULL);
//
// Keep trying to get the address list until we have a large enough
// buffer. We use the IN_ADDR array pointer simply because it's
// already there. We know that IN_ADDRs are smaller than
// SOCKET_ADDRESSes, so we can reuse the same buffer.
//
do
{
iReturn = this->m_pfnWSAIoctl(this->m_sIoctls, // use the special Ioctl socket
SIO_ADDRESS_LIST_QUERY, //
NULL, // no input data
0, // no input data
painaddrAddresses, // output buffer
dwAddressesSize, // output buffer size
&dwTemp, // bytes needed
NULL, // no overlapped structure
NULL); // no completion routine
if (iReturn != 0)
{
dwError = this->m_pfnWSAGetLastError();
//
// Free the previous buffer, no matter what error it was.
//
if (painaddrAddresses != NULL)
{
DNFree(painaddrAddresses);
painaddrAddresses = NULL;
}
if (dwError != WSAEFAULT)
{
DPFX(DPFPREP, 0, "Retrieving address list failed (err = %u)!", dwError);
//
// We'll try the old-fashioned WinSock 1 way.
//
break;
}
//
// Be absolutely sure WinSock isn't causing us trouble.
//
if (dwTemp < sizeof(SOCKET_ADDRESS_LIST))
{
DPFX(DPFPREP, 0, "Received an invalid buffer size (%u < %u)!",
dwTemp, sizeof(SOCKET_ADDRESS_LIST));
//
// We'll try the old-fashioned WinSock 1 way.
//
break;
}
//
// The buffer wasn't large enough. Try again.
//
painaddrAddresses = (IN_ADDR*) DNMalloc(dwTemp);
if (painaddrAddresses == NULL)
{
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
dwAddressesSize = dwTemp;
}
else
{
//
// Success! We're going to being sneaky and reuse the buffer.
// We know that the SOCKET_ADDRESS_LIST returned will be larger
// than an array of IN_ADDRs, so we can save a malloc.
//
// But first, be absolutely sure WinSock isn't causing us
// trouble.
//
if (painaddrAddresses == NULL)
{
DPFX(DPFPREP, 0, "WinSock returned success with a NULL buffer!");
//
// We'll try the old-fashioned WinSock 1 way.
//
break;
}
dwNumAddresses = ((SOCKET_ADDRESS_LIST*) painaddrAddresses)->iAddressCount;
dwAddressesSize = 0;
//
// Make sure there are addresses.
//
if (dwNumAddresses > 0)
{
DPFX(DPFPREP, 7, "WinSock 2 Ioctl returned %u addresses:", dwNumAddresses);
paSocketAddresses = ((SOCKET_ADDRESS_LIST*) painaddrAddresses)->Address;
for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
{
DNASSERT(paSocketAddresses[dwTemp].iSockaddrLength == sizeof(SOCKADDR_IN));
DNASSERT(paSocketAddresses[dwTemp].lpSockaddr != NULL);
DNASSERT(paSocketAddresses[dwTemp].lpSockaddr->sa_family == AF_INET);
//
// Ignore 0.0.0.0 addresses.
//
if (((SOCKADDR_IN*) (paSocketAddresses[dwTemp].lpSockaddr))->sin_addr.S_un.S_addr != INADDR_NONE)
{
//
// Move the IN_ADDR component of this address
// toward the front of the buffer, into it's
// correct place in the array.
//
painaddrAddresses[dwTemp].S_un.S_addr = ((SOCKADDR_IN*) (paSocketAddresses[dwTemp].lpSockaddr))->sin_addr.S_un.S_addr;
DPFX(DPFPREP, 7, "\t%u- %u.%u.%u.%u",
dwTemp,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b1,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b2,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b3,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b4);
}
else
{
DPFX(DPFPREP, 1, "\t%u- Ignoring 0.0.0.0 address.", dwTemp);
dwAddressesSize++;
//
// The code should handle this fine, but why is
// WinSock doing this to us?
//
DNASSERT(FALSE);
}
}
//
// Subtract out any invalid addresses that we skipped.
//
dwNumAddresses -= dwAddressesSize;
if (dwNumAddresses == 0)
{
DPFX(DPFPREP, 1, "WinSock 2 reported only invalid addresses, hoping WinSock 1 method picks up the loopback address.");
DNFree(painaddrAddresses);
painaddrAddresses = NULL;
}
}
else
{
DPFX(DPFPREP, 1, "WinSock 2 Ioctl did not report any valid addresses, hoping WinSock 1 method picks up the loopback address.");
DNFree(painaddrAddresses);
painaddrAddresses = NULL;
}
//
// Get out of the loop.
//
break;
}
}
while (TRUE);
}
//
// Get the list of all available addresses from the WinSock 1 API if we
// don't already have them.
//
if (painaddrAddresses == NULL)
{
if (this->m_pfngethostname(szName, 1000) != 0)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't get host name, error = %u!", dwError);
#endif // DBG
hr = DPNHERR_GENERIC;
goto Failure;
}
phostent = this->m_pfngethostbyname(szName);
if (phostent == NULL)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't retrieve addresses, error = %u!", dwError);
#endif // DBG
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// WinSock says that you need to copy this data before you make any
// other API calls. So first we count the number of entries we need to
// copy.
//
ppinaddr = (IN_ADDR**) phostent->h_addr_list;
while ((*ppinaddr) != NULL)
{
//
// Ignore 0.0.0.0 addresses.
//
if ((*ppinaddr)->S_un.S_addr != INADDR_NONE)
{
dwNumAddresses++;
}
else
{
DPFX(DPFPREP, 1, "Ignoring 0.0.0.0 address.");
//
// The code should handle this fine, but why is WinSock doing
// this to us?
//
DNASSERT(FALSE);
}
ppinaddr++;
}
//
// If there aren't any addresses, we must fail. WinSock 1 ought to
// report the loopback address at least.
//
if (dwNumAddresses == 0)
{
DPFX(DPFPREP, 0, "WinSock 1 did not report any valid addresses!");
hr = DPNHERR_GENERIC;
goto Failure;
}
DPFX(DPFPREP, 7, "WinSock 1 method returned %u valid addresses:", dwNumAddresses);
painaddrAddresses = (IN_ADDR*) DNMalloc(dwNumAddresses * sizeof(IN_ADDR));
if (painaddrAddresses == NULL)
{
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
//
// Now copy all the addresses.
//
ppinaddr = (IN_ADDR**) phostent->h_addr_list;
dwTemp = 0;
while ((*ppinaddr) != NULL)
{
//
// Ignore 0.0.0.0 addresses again.
//
if ((*ppinaddr)->S_un.S_addr != INADDR_NONE)
{
painaddrAddresses[dwTemp].S_un.S_addr = (*ppinaddr)->S_un.S_addr;
DPFX(DPFPREP, 7, "\t%u- %u.%u.%u.%u",
dwTemp,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b1,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b2,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b3,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b4);
dwTemp++;
}
ppinaddr++;
}
DNASSERT(dwTemp == dwNumAddresses);
}
else
{
//
// Already have addresses array.
//
}
//
// Make sure that all of the devices we currently know about are still
// around.
//
pBilinkDevice = this->m_blDevices.GetNext();
while (pBilinkDevice != &this->m_blDevices)
{
pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
pBilinkDevice = pBilinkDevice->GetNext();
fFound = FALSE;
for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
{
if (painaddrAddresses[dwTemp].S_un.S_addr == pDevice->GetLocalAddressV4())
{
fFound = TRUE;
break;
}
}
if (fFound)
{
//
// It may be time for this device to use a different port...
//
dwTemp = pDevice->GetFirstPASTDiscoveryTime();
if ((dwTemp != 0) && ((GETTIMESTAMP() - dwTemp) > g_dwReusePortTime))
{
ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
saddrinTemp.sin_family = AF_INET;
saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
sTemp = this->CreatePASTSocket(&saddrinTemp);
if (sTemp != INVALID_SOCKET)
{
//
// Sanity check that we didn't lose the device address.
//
DNASSERT(saddrinTemp.sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4());
DPFX(DPFPREP, 4, "Device 0x%p PAST socket 0x%p (%u.%u.%u.%u:%u) created to replace existing one.",
pDevice,
sTemp,
saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinTemp.sin_port));
pDevice->SetFirstPASTDiscoveryTime(0);
//
// Close the existing socket.
//
this->m_pfnclosesocket(pDevice->GetPASTSocket());
//
// Transfer ownership of the new socket to the device.
//
pDevice->SetPASTSocket(sTemp);
sTemp = INVALID_SOCKET;
DPFX(DPFPREP, 8, "Device 0x%p got re-assigned PAST socket 0x%p.",
pDevice, pDevice->GetPASTSocket());
}
else
{
DPFX(DPFPREP, 0, "Couldn't create a replacement PAST socket for device 0x%p! Using existing one.",
pDevice);
}
}
}
else
{
//
// Didn't find this device in the returned list, forget about
// it.
//
#ifdef DBG
{
IN_ADDR inaddrTemp;
inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
DPFX(DPFPREP, 1, "Device 0x%p no longer exists, removing (address was %u.%u.%u.%u).",
pDevice,
inaddrTemp.S_un.S_un_b.s_b1,
inaddrTemp.S_un.S_un_b.s_b2,
inaddrTemp.S_un.S_un_b.s_b3,
inaddrTemp.S_un.S_un_b.s_b4);
}
this->m_dwNumDeviceRemoves++;
#endif // DBG
//
// Override the minimum UpdateServerStatus interval so that we can
// get information on any local public address changes due to the
// possible loss of a server on this interface.
//
this->m_dwFlags |= NATHELPPASTOBJ_DEVICECHANGED;
//
// Since there was a change in the network, go back to polling
// relatively quickly.
//
this->ResetNextPollInterval();
//
// Forcefully mark the PAST servers as disconnected.
//
if (pDevice->GetPASTClientID(TRUE) != 0)
{
this->ClearDevicesPASTServer(pDevice, TRUE);
}
if (pDevice->GetPASTClientID(FALSE) != 0)
{
this->ClearDevicesPASTServer(pDevice, FALSE);
}
//
// Mark all ports that were registered to this device as unowned
// by putting them into the wildcard list.
//
pBilinkRegPort = pDevice->m_blOwnedRegPorts.GetNext();
while (pBilinkRegPort != &pDevice->m_blOwnedRegPorts)
{
pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegPort);
pBilinkRegPort = pBilinkRegPort->GetNext();
DPFX(DPFPREP, 1, "Registered port 0x%p's device went away, marking as unowned.",
pRegisteredPort);
DNASSERT(pRegisteredPort->GetPASTBindID(TRUE) == 0);
DNASSERT(pRegisteredPort->GetPASTBindID(FALSE) == 0);
DNASSERT(! pRegisteredPort->HasPASTPublicAddressesArray(TRUE));
DNASSERT(! pRegisteredPort->HasPASTPublicAddressesArray(FALSE));
DNASSERT(! pRegisteredPort->IsPASTPortUnavailable(TRUE));
DNASSERT(! pRegisteredPort->IsPASTPortUnavailable(FALSE));
pRegisteredPort->ClearDeviceOwner();
pRegisteredPort->m_blDeviceList.RemoveFromList();
pRegisteredPort->m_blDeviceList.InsertBefore(&this->m_blUnownedPorts);
//
// The user doesn't directly need to be informed. If the ports
// previously had public addresses, the ADDRESSESCHANGED flag
// would have already been set by ClearDevicesPASTServer. If
// they didn't have ports with public addresses, then the user
// won't see any difference and thus ADDRESSESCHANGED wouldn't
// need to be set.
//
}
pDevice->m_blList.RemoveFromList();
//
// Close the sockets.
//
this->m_pfnclosesocket(pDevice->GetPASTSocket());
pDevice->SetPASTSocket(INVALID_SOCKET);
delete pDevice;
}
}
//
// Search for all returned devices in our existing list, and add new
// entries for each one that we didn't already know about.
//
for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
{
fFound = FALSE;
pBilinkDevice = this->m_blDevices.GetNext();
while (pBilinkDevice != &this->m_blDevices)
{
pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
pBilinkDevice = pBilinkDevice->GetNext();
if (pDevice->GetLocalAddressV4() == painaddrAddresses[dwTemp].S_un.S_addr)
{
fFound = TRUE;
break;
}
}
if (! fFound)
{
//
// We didn't know about this device. Create a new object.
//
pDevice = new CDevice(painaddrAddresses[dwTemp].S_un.S_addr);
if (pDevice == NULL)
{
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
fDeviceCreated = TRUE;
#ifdef DBG
DPFX(DPFPREP, 1, "Found new device %u.%u.%u.%u, (object = 0x%p).",
painaddrAddresses[dwTemp].S_un.S_un_b.s_b1,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b2,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b3,
painaddrAddresses[dwTemp].S_un.S_un_b.s_b4,
pDevice);
this->m_dwNumDeviceAdds++;
#endif // DBG
//
// Override the minimum UpdateServerStatus interval so that we can
// get information on this new device.
//
this->m_dwFlags |= NATHELPPASTOBJ_DEVICECHANGED;
//
// Since there was a change in the network, go back to polling
// relatively quickly.
//
this->ResetNextPollInterval();
//
// Create the PAST socket.
//
ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
saddrinTemp.sin_family = AF_INET;
saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
sTemp = this->CreatePASTSocket(&saddrinTemp);
if (sTemp == INVALID_SOCKET)
{
DPFX(DPFPREP, 0, "Couldn't create PAST socket! Ignoring address (and destroying device 0x%p).",
pDevice);
//
// Get rid of the device.
//
delete pDevice;
pDevice = NULL;
//
// Forget about device in case of failure later.
//
fDeviceCreated = FALSE;
//
// Move to next address.
//
continue;
}
//
// Sanity check that we didn't lose the device address.
//
DNASSERT(saddrinTemp.sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4());
DPFX(DPFPREP, 4, "Device 0x%p PAST socket 0x%p (%u.%u.%u.%u:%u) created.",
pDevice,
sTemp,
saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinTemp.sin_port));
//
// Transfer ownership of the socket to the device.
//
pDevice->SetPASTSocket(sTemp);
sTemp = INVALID_SOCKET;
DPFX(DPFPREP, 8, "Device 0x%p got assigned PAST socket 0x%p.",
pDevice, pDevice->GetPASTSocket());
//
// Add the device to our known list.
//
pDevice->m_blList.InsertBefore(&this->m_blDevices);
//
// Inform the caller if they care.
//
if (pfFoundNewDevices != NULL)
{
(*pfFoundNewDevices) = TRUE;
}
//
// Forget about device in case of failure later.
//
fDeviceCreated = FALSE;
}
}
//
// If we got some very weird failures and ended up here without any
// devices, complain to management (or the caller of this function, that's
// probably more convenient).
//
if (this->m_blDevices.IsEmpty())
{
DPFX(DPFPREP, 0, "No usable devices, cannot proceed!", 0);
DNASSERTX(! "No usable devices!", 2);
hr = DPNHERR_GENERIC;
goto Failure;
}
Exit:
if (painaddrAddresses != NULL)
{
DNFree(painaddrAddresses);
painaddrAddresses = NULL;
}
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
if (sTemp != INVALID_SOCKET)
{
this->m_pfnclosesocket(sTemp);
sTemp = INVALID_SOCKET;
}
if (fDeviceCreated)
{
delete pDevice;
}
goto Exit;
} // CNATHelpPAST::CheckForNewDevices
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::CheckForLocalPASTServerAndRegister"
//=============================================================================
// CNATHelpPAST::CheckForLocalPASTServerAndRegister
//-----------------------------------------------------------------------------
//
// Description: Checks for a local PAST server on the given device. If one
// is found, a new client is registered.
//
// The object lock is assumed to be held.
//
// Arguments:
// CDevice * pDevice - Pointer to device to check.
//
// Returns: HRESULT
// DPNH_OK - The check was successful (may not be a server,
// though).
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::CheckForLocalPASTServerAndRegister(CDevice * const pDevice)
{
HRESULT hr = DPNH_OK;
DWORD dwError;
SOCKET sTemp = INVALID_SOCKET;
SOCKADDR_IN saddrinTemp;
BOOL fAddressAlreadyChanged;
DWORD dwOriginalNextPollInterval;
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pDevice);
//
// Open a datagram socket on this device.
//
sTemp = this->m_pfnsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sTemp == INVALID_SOCKET)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't create datagram socket, error = %u!", dwError);
#endif // DBG
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Try binding it to the PAST port first to see if there's a local PAST
// server on the device.
//
// We could just go ahead and try to register with a server, but if one
// doesn't exist, the registration code would block during the timeout. By
// simply checking if the port is in use, we can avoid that timeout. The
// only slight cost is if two PASTHelps happened to be trying this at the
// exact same time, one could be tricked by the other into thinking that
// the port was in use. In that case, it would falsely try to register...
// but then timeout. Not a big deal, that's treated as non-fatal below.
//
ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
saddrinTemp.sin_family = AF_INET;
saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
saddrinTemp.sin_port = HTONS(PAST_HOST_PORT);
if (this->m_pfnbind(sTemp,
(SOCKADDR *) (&saddrinTemp),
sizeof(saddrinTemp)) != 0)
{
//
// The bind failed. We'll print the exact error, but assume it was
// because the port was in use.
//
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 1, "Couldn't bind datagram socket %u.%u.%u.%u:%u to PAST server port, assuming because there's a local PAST server (error = %u).",
saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinTemp.sin_port),
dwError);
#endif // DBG
//
// Save the current poll interval in case we need to restore it.
//
dwOriginalNextPollInterval = this->m_dwNextPollInterval;
//
// We should register with this local PAST server right now.
// This might reset the poll interval.
//
hr = this->RegisterWithLocalPASTServer(pDevice);
if (hr != DPNH_OK)
{
if (hr == DPNHERR_SERVERNOTRESPONDING)
{
//
// If the server isn't responding, we'll treat it as non-fatal,
// but obviously we can't use the server.
//
DPFX(DPFPREP, 1, "Local PAST server does not respond to registrations, ignoring.");
}
else
{
//
// Some other failure. Annoying, but ignore it.
//
DPFX(DPFPREP, 0, "Couldn't register with local PAST server (err = 0x%lx)! Ignoring.",
hr);
}
hr = DPNH_OK;
//
// Drop through.
//
}
else
{
fAddressAlreadyChanged = (this->m_dwFlags & NATHELPPASTOBJ_ADDRESSESCHANGED) ? TRUE : FALSE;
//
// We need to bind a temporary port to detect ICS vs. FW-only.
// UpdatePASTPublicAddressValidity does this in addition to
// checking public address validity like the function name
// says.
//
hr = this->UpdatePASTPublicAddressValidity(pDevice, FALSE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't update new local PAST server public address validity!", hr);
goto Failure;
}
//
// If we encountered an error that caused the PAST server to be
// removed, then we shouldn't (and can't) try registering any
// existing ports.
//
// Otherwise, make sure we're allowed to work with the type of PAST
// server found. If we aren't de-register. If we are, bind any
// ports already associated with this device.
//
if ((pDevice->GetPASTClientID(FALSE) == 0) ||
((pDevice->HasLocalICSPASTServer()) && (! (this->m_dwFlags & NATHELPPASTOBJ_USEPASTICS))) ||
((pDevice->HasLocalPFWOnlyPASTServer()) && (! (this->m_dwFlags & NATHELPPASTOBJ_USEPASTPFW))))
{
if (pDevice->GetPASTClientID(FALSE) != 0)
{
DPFX(DPFPREP, 2, "Not allowed to use local %s PAST server, de-registering.",
((pDevice->HasLocalICSPASTServer()) ? _T("ICS") : _T("PFW only")));
hr = this->DeregisterWithPASTServer(pDevice, FALSE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't de-register with local PAST server (err = 0x%lx)! Ignoring.",
hr);
//
// Consider ourselves de-registered.
//
pDevice->SetPASTClientID(0, FALSE);
//
// Continue anyway...
//
hr = DPNH_OK;
}
this->ClearAllPASTServerRegisteredPorts(pDevice, FALSE);
this->RemoveAllPASTCachedMappings(pDevice, FALSE);
pDevice->NoteNoPASTPublicAddressAvailable(FALSE);
pDevice->NoteNoLocalPASTServer();
}
else
{
DPFX(DPFPREP, 1, "Local PAST server was removed while trying to update public address validity.");
}
//
// Prevent the user from thinking the addresses changed unless
// something else caused the address change notification.
//
if (! fAddressAlreadyChanged)
{
this->m_dwFlags &= ~NATHELPPASTOBJ_ADDRESSESCHANGED;
}
//
// Go back to the previous poll interval.
//
this->m_dwNextPollInterval = dwOriginalNextPollInterval;
}
else
{
if (! pDevice->m_blOwnedRegPorts.IsEmpty())
{
DPFX(DPFPREP, 2, "Local PAST server now available, registering existing ports.");
hr = this->RegisterAllPortsWithPAST(pDevice, FALSE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't register existing ports with new local PAST server!", hr);
goto Failure;
}
#ifdef DBG
//
// If we didn't encounter an error that caused the PAST
// server to be removed, then the
// NATHELPPASTOBJ_ADDRESSESCHANGED flag must have been set
// by the AssignOrListenPorts function.
//
if (pDevice->GetPASTClientID(TRUE) != 0)
{
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_ADDRESSESCHANGED);
}
else
{
DPFX(DPFPREP, 1, "Local PAST server was removed while trying to register existing ports.");
}
#endif // DBG
}
else
{
DPFX(DPFPREP, 2, "Local PAST server now available, but no previously registered ports.");
}
}
} // end else (successfully registered with local PAST server)
}
else
{
//
// The bind succeeded. Doesn't look like there's a local PAST server.
//
DPFX(DPFPREP, 7, "Bound datagram socket %u.%u.%u.%u:%u, no local PAST server present.",
saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinTemp.sin_port));
}
Exit:
if (sTemp != INVALID_SOCKET)
{
this->m_pfnclosesocket(sTemp);
}
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::CheckForLocalPASTServerAndRegister
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::RemoveAllItems"
//=============================================================================
// CNATHelpPAST::RemoveAllItems
//-----------------------------------------------------------------------------
//
// Description: Removes all devices (de-registering with Internet gateways
// if necessary). This removes all registered port mapping
// objects, as well.
//
// The object lock is assumed to be held.
//
// Arguments: None.
//
// Returns: None.
//=============================================================================
void CNATHelpPAST::RemoveAllItems(void)
{
HRESULT hr;
CBilink * pBilinkDevice;
CDevice * pDevice;
CBilink * pBilinkRegisteredPort;
CRegisteredPort * pRegisteredPort;
DPFX(DPFPREP, 7, "(0x%p) Enter", this);
pBilinkDevice = this->m_blDevices.GetNext();
while (pBilinkDevice != &this->m_blDevices)
{
pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
pBilinkDevice = pBilinkDevice->GetNext();
DPFX(DPFPREP, 5, "Destroying device 0x%p.",
pDevice);
pDevice->m_blList.RemoveFromList();
//
// De-register from remote PAST server if necessary.
//
if (pDevice->GetPASTClientID(TRUE) != 0)
{
hr = this->DeregisterWithPASTServer(pDevice, TRUE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't de-register with remote PAST server (err = 0x%lx)! Ignoring.",
hr);
//
// Consider ourselves de-registered.
//
pDevice->SetPASTClientID(0, TRUE);
//
// Continue anyway, so we can finish cleaning up the object.
//
}
this->ClearAllPASTServerRegisteredPorts(pDevice, TRUE);
this->RemoveAllPASTCachedMappings(pDevice, TRUE);
pDevice->NoteNoPASTPublicAddressAvailable(TRUE);
}
//
// De-register from local PAST server if necessary.
//
if (pDevice->GetPASTClientID(FALSE) != 0)
{
hr = this->DeregisterWithPASTServer(pDevice, FALSE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't de-register with local PAST server (err = 0x%lx)! Ignoring.",
hr);
//
// Consider ourselves de-registered.
//
pDevice->SetPASTClientID(0, FALSE);
//
// Continue anyway, so we can finish cleaning up the object.
//
}
this->ClearAllPASTServerRegisteredPorts(pDevice, FALSE);
this->RemoveAllPASTCachedMappings(pDevice, FALSE);
pDevice->NoteNoPASTPublicAddressAvailable(FALSE);
}
//
// All of the device's registered ports are implicitly freed.
//
pBilinkRegisteredPort = pDevice->m_blOwnedRegPorts.GetNext();
while (pBilinkRegisteredPort != &pDevice->m_blOwnedRegPorts)
{
pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
DPFX(DPFPREP, 5, "Destroying registered port 0x%p (under device 0x%p).",
pRegisteredPort, pDevice);
pRegisteredPort->ClearDeviceOwner();
pRegisteredPort->m_blGlobalList.RemoveFromList();
if (pRegisteredPort->GetPASTBindID(TRUE) != 0)
{
pRegisteredPort->SetPASTBindID(0, TRUE);
pRegisteredPort->ClearPASTPublicAddresses(TRUE);
DNASSERT(this->m_dwNumLeases > 0);
this->m_dwNumLeases--;
DPFX(DPFPREP, 7, "Remote PAST lease for 0x%p cleared, total num leases = %u.",
pRegisteredPort, this->m_dwNumLeases);
}
if (pRegisteredPort->GetPASTBindID(FALSE) != 0)
{
pRegisteredPort->SetPASTBindID(0, FALSE);
pRegisteredPort->ClearPASTPublicAddresses(FALSE);
DNASSERT(this->m_dwNumLeases > 0);
this->m_dwNumLeases--;
DPFX(DPFPREP, 7, "Local PAST lease for 0x%p cleared, total num leases = %u.",
pRegisteredPort, this->m_dwNumLeases);
}
pRegisteredPort->ClearPrivateAddresses();
//
// The user implicitly released this port.
//
pRegisteredPort->ClearAllUserRefs();
delete pRegisteredPort;
}
//
// Close the socket.
//
this->m_pfnclosesocket(pDevice->GetPASTSocket());
pDevice->SetPASTSocket(INVALID_SOCKET);
//
// Now we can dump the device object.
//
delete pDevice;
}
//
// Removing all the devices normally removes all the registered ports, but
// there may still be more wildcard ports that were never associated with
// any device.
//
pBilinkRegisteredPort = this->m_blUnownedPorts.GetNext();
while (pBilinkRegisteredPort != &this->m_blUnownedPorts)
{
pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
DPFX(DPFPREP, 5, "Destroying unowned registered port 0x%p.",
pRegisteredPort);
pRegisteredPort->m_blDeviceList.RemoveFromList();
pRegisteredPort->m_blGlobalList.RemoveFromList();
pRegisteredPort->ClearPrivateAddresses();
DNASSERT(pRegisteredPort->GetPASTBindID(TRUE) == 0);
DNASSERT(pRegisteredPort->GetPASTBindID(FALSE) == 0);
//
// The user implicitly released this port.
//
pRegisteredPort->ClearAllUserRefs();
delete pRegisteredPort;
}
DNASSERT(this->m_blRegisteredPorts.IsEmpty());
DPFX(DPFPREP, 7, "(0x%p) Leave", this);
} // CNATHelpPAST::RemoveAllItems
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::FindMatchingDevice"
//=============================================================================
// CNATHelpPAST::FindMatchingDevice
//-----------------------------------------------------------------------------
//
// Description: Searches the list of devices for the object matching the
// given address, or NULL if one could not be found.
//
// If fMatchRegPort is TRUE, the list of registered ports
// associated with devices is searched first for an exact match to
// the address passed in.
//
// The object lock is assumed to be held.
//
// Arguments:
// SOCKADDR_IN * psaddrinMatch - Pointer to address to look up.
// BOOL fMatchRegPort - Whether existing registered ports should
// be checked for an exact match first.
//
// Returns: CDevice
// NULL if no match, valid object otherwise.
//=============================================================================
CDevice * CNATHelpPAST::FindMatchingDevice(const SOCKADDR_IN * const psaddrinMatch,
const BOOL fMatchRegPort)
{
HRESULT hr;
BOOL fUpdatedDeviceList = FALSE;
CDevice * pDeviceRemoteICSServer = NULL;
CDevice * pDeviceLocalICSServer = NULL;
CDevice * pDeviceLocalPFWOnlyServer = NULL;
SOCKADDR_IN * psaddrinTemp;
CBilink * pBilink;
CRegisteredPort * pRegisteredPort;
CDevice * pDevice;
DWORD dwTemp;
do
{
//
// First, make sure there are devices to choose from.
//
if (this->m_blDevices.IsEmpty())
{
DPFX(DPFPREP, 0, "No devices, can't match address %u.%u.%u.%u!",
psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
pDevice = NULL;
goto Exit;
}
//
// It's possible that the address we're trying to match is an already
// registered port. Look through all owned port mappings for this
// address, if we're allowed.
//
if (fMatchRegPort)
{
pBilink = this->m_blRegisteredPorts.GetNext();
while (pBilink != &this->m_blRegisteredPorts)
{
pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
//
// Only check this registered port if it has an owning device.
//
pDevice = pRegisteredPort->GetOwningDevice();
if (pDevice != NULL)
{
//
// Check each port in the array.
//
psaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
{
//
// If the address matches, we have a winner.
//
if ((psaddrinTemp[dwTemp].sin_addr.S_un.S_addr == psaddrinMatch->sin_addr.S_un.S_addr) &&
(psaddrinTemp[dwTemp].sin_port == psaddrinMatch->sin_port))
{
DPFX(DPFPREP, 7, "Registered port 0x%p index %u matches address %u.%u.%u.%u:%u, returning owning device 0x%p.",
pRegisteredPort,
dwTemp,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b4,
NTOHS(psaddrinMatch->sin_port),
pDevice);
goto Exit;
}
}
}
pBilink = pBilink->GetNext();
}
}
//
// Darn, the address is not already registered. Well, match it up with
// a device as best as possible.
//
pBilink = this->m_blDevices.GetNext();
do
{
pDevice = DEVICE_FROM_BILINK(pBilink);
if ((pDevice->GetLocalAddressV4() == psaddrinMatch->sin_addr.S_un.S_addr))
{
DPFX(DPFPREP, 7, "Device 0x%p matches address %u.%u.%u.%u.",
pDevice,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
goto Exit;
}
//
// Remember this device if it has the first remote ICS server we've
// seen.
//
if ((pDevice->GetPASTClientID(TRUE) != 0) &&
(pDeviceRemoteICSServer == NULL))
{
pDeviceRemoteICSServer = pDevice;
}
//
// Remember this device if it has the first local ICS or firewall
// only server we've seen.
//
if (pDevice->GetPASTClientID(FALSE) != 0)
{
if ((pDevice->HasLocalICSPASTServer()) &&
(pDeviceLocalICSServer == NULL))
{
pDeviceLocalICSServer = pDevice;
}
else if ((pDevice->HasLocalPFWOnlyPASTServer()) &&
(pDeviceLocalPFWOnlyServer == NULL))
{
pDeviceLocalPFWOnlyServer = pDevice;
}
}
DPFX(DPFPREP, 7, "Device 0x%p does not match address %u.%u.%u.%u.",
pDevice,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
pBilink = pBilink->GetNext();
}
while (pBilink != &this->m_blDevices);
//
// If we got here, there's no matching device. It might be because the
// caller detected an address change faster than we did. Try updating
// our device list and searching again (if we haven't already).
//
if (fUpdatedDeviceList)
{
break;
}
//
// Don't bother updating the list to match INADDR_ANY, we know that
// will never match anything.
//
if (psaddrinMatch->sin_addr.S_un.S_addr == INADDR_ANY)
{
DPFX(DPFPREP, 7, "Couldn't find matching device for INADDR_ANY, as expected.");
break;
}
DPFX(DPFPREP, 7, "Couldn't find matching device for %u.%u.%u.%u, updating device list and searching again.",
psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
hr = this->CheckForNewDevices(&fUpdatedDeviceList);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't check for new devices (0x%lx), continuing.",
hr);
//
// Hmm, we have to treat it as non-fatal. Don't search again,
// though.
//
break;
}
//
// If we didn't actually get any new devices, don't bother searching
// again.
//
if (! fUpdatedDeviceList)
{
break;
}
//
// fUpdatedDeviceList is set to TRUE so we'll only loop one more time.
//
}
while (TRUE);
//
// If we got here, there's still no matching device. If it's the wildcard
// value, that's to be expected, but we need to pick a device in the
// following order:
// 1. device has an Internet gateway
// 2. device has a firewall
// If none of those exists or it's not the wildcard value, we have to give
// up.
//
if (psaddrinMatch->sin_addr.S_un.S_addr == INADDR_ANY)
{
if (pDeviceRemoteICSServer != NULL)
{
pDevice = pDeviceRemoteICSServer;
DPFX(DPFPREP, 1, "Picking device 0x%p with remote ICS server to match INADDR_ANY.",
pDevice);
}
else if (pDeviceLocalICSServer != NULL)
{
pDevice = pDeviceLocalICSServer;
DPFX(DPFPREP, 1, "Picking device 0x%p with local ICS server to match INADDR_ANY.",
pDevice);
}
else if (pDeviceLocalPFWOnlyServer != NULL)
{
pDevice = pDeviceLocalPFWOnlyServer;
DPFX(DPFPREP, 1, "Picking device 0x%p with local PFW-only server to match INADDR_ANY.",
pDevice);
}
else
{
pDevice = NULL;
DPFX(DPFPREP, 1, "No suitable device to match INADDR_ANY.");
}
}
else
{
pDevice = NULL;
DPFX(DPFPREP, 7, "No devices match address %u.%u.%u.%u.",
psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
}
Exit:
return pDevice;
} // CNATHelpPAST::FindMatchingDevice
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::RegisterWithLocalPASTServer"
//=============================================================================
// CNATHelpPAST::RegisterWithLocalPASTServer
//-----------------------------------------------------------------------------
//
// Description: Attempts to register with a local PAST server.
//
// The object lock is assumed to be held.
//
// Arguments:
// CDevice * pDevice - Device to use when registering.
//
// Returns: HRESULT
// DPNH_OK - The registration attempt completed
// without error.
// DPNHERR_SERVERNOTRESPONDING - No server responded to the registration
// attempt.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::RegisterWithLocalPASTServer(CDevice * const pDevice)
{
HRESULT hr;
SOCKADDR_IN saddrinServerAddress;
DWORD dwMsgID;
PAST_MSG_REGISTER RegisterReq;
PAST_RESPONSE_INFO RespInfo;
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pDevice);
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED);
DNASSERT(pDevice->GetPASTSocket() != INVALID_SOCKET);
//
// Create a SOCKADDR to address the PAST service.
//
// Also initialize the message sequencing. Each message response pair is
// numbered sequentially to allow differentiation from retries over UDP.
//
// And finally, reset the current retry timeout (even though it doesn't
// affect this special case Register message).
//
ZeroMemory(&saddrinServerAddress, sizeof(saddrinServerAddress));
saddrinServerAddress.sin_family = AF_INET;
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
saddrinServerAddress.sin_port = HTONS(PAST_HOST_PORT);
DNASSERT(pDevice->GetPASTClientID(FALSE) == 0);
pDevice->ResetLocalPASTMsgIDAndRetryTimeout(DEFAULT_INITIAL_PAST_RETRY_TIMEOUT);
dwMsgID = pDevice->GetNextLocalPASTMsgID();
DNASSERT(dwMsgID == 0);
//
// Remember the current time, if this is the first thing we've sent from
// this port.
//
if (pDevice->GetFirstPASTDiscoveryTime() == 0)
{
pDevice->SetFirstPASTDiscoveryTime(GETTIMESTAMP());
}
//
// Build the request message.
//
ZeroMemory(&RegisterReq, sizeof(RegisterReq));
RegisterReq.version = PAST_VERSION;
RegisterReq.command = PAST_MSGID_REGISTER_REQUEST;
RegisterReq.msgid.code = PAST_PARAMID_MESSAGEID;
RegisterReq.msgid.len = sizeof(RegisterReq.msgid) - sizeof(PAST_PARAM);
RegisterReq.msgid.msgid = dwMsgID;
//
// Send the message and get the reply.
//
hr = this->ExchangeAndParsePAST(pDevice->GetPASTSocket(),
(SOCKADDR*) (&saddrinServerAddress),
sizeof(saddrinServerAddress),
(char *) &RegisterReq,
sizeof(RegisterReq),
dwMsgID,
NULL,
&RespInfo);
if (hr != DPNH_OK)
{
//
// If exchanging the registration attempt failed, see if it was because
// the server wasn't responding.
//
if (hr != DPNHERR_SERVERNOTRESPONDING)
{
DPFX(DPFPREP, 0, "Registering with a PAST server failed!");
goto Failure;
}
//
// Server non-existence is considered non-fatal, but it still means
// we're done here.
//
DPFX(DPFPREP, 1, "No PAST server responded, registration was not successful.");
goto Exit;
}
if (RespInfo.cMsgType != PAST_MSGID_REGISTER_RESPONSE)
{
DPFX(DPFPREP, 0, "Unexpected response type %u, failing registration!", RespInfo.cMsgType);
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// If we got here, then we successfully registered with a server.
//
pDevice->SetPASTClientID(RespInfo.dwClientID, FALSE);
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::RegisterWithLocalPASTServer
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::DeregisterWithPASTServer"
//=============================================================================
// CNATHelpPAST::DeregisterWithPASTServer
//-----------------------------------------------------------------------------
//
// Description: Attempts to deregister with the PAST server (local or
// remote, as determined by fRemote).
//
// All port assignments are implicitly freed.
//
// The object lock is assumed to be held.
//
// Arguments:
// CDevice * pDevice - Device to use when deregistering.
// BOOL fRemote - TRUE if should attempt to deregister with remote
// server, FALSE if deregistering with local server.
//
// Returns: HRESULT
// DPNH_OK - The deregistration completed successfully.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::DeregisterWithPASTServer(CDevice * const pDevice,
const BOOL fRemote)
{
HRESULT hr = DPNH_OK;
SOCKADDR_IN saddrinServerAddress;
DWORD dwClientID;
DWORD dwMsgID;
DWORD * ptuRetry;
PAST_MSG_DEREGISTER_REQUEST DeregisterReq;
PAST_RESPONSE_INFO RespInfo;
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)", this, pDevice, fRemote);
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED);
DNASSERT(pDevice->GetPASTSocket() != INVALID_SOCKET);
//
// Create a SOCKADDR to address the PAST service, and get the appropriate
// client ID, initial retry timeout, and next message ID to use.
//
ZeroMemory(&saddrinServerAddress, sizeof(saddrinServerAddress));
saddrinServerAddress.sin_family = AF_INET;
saddrinServerAddress.sin_port = HTONS(PAST_HOST_PORT);
if (fRemote)
{
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetRemotePASTServerAddressV4();
dwMsgID = pDevice->GetNextRemotePASTMsgID();
ptuRetry = pDevice->GetRemotePASTRetryTimeoutPtr();
}
else
{
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
dwMsgID = pDevice->GetNextLocalPASTMsgID();
ptuRetry = pDevice->GetLocalPASTRetryTimeoutPtr();
}
dwClientID = pDevice->GetPASTClientID(fRemote);
DNASSERT(dwClientID != 0);
//
// Build the request message.
//
ZeroMemory(&DeregisterReq, sizeof(DeregisterReq));
DeregisterReq.version = PAST_VERSION;
DeregisterReq.command = PAST_MSGID_DEREGISTER_REQUEST;
DeregisterReq.clientid.code = PAST_PARAMID_CLIENTID;
DeregisterReq.clientid.len = sizeof(DeregisterReq.clientid) - sizeof(PAST_PARAM);
DeregisterReq.clientid.clientid = dwClientID;
DeregisterReq.msgid.code = PAST_PARAMID_MESSAGEID;
DeregisterReq.msgid.len = sizeof(DeregisterReq.msgid) - sizeof(PAST_PARAM);
DeregisterReq.msgid.msgid = dwMsgID;
//
// Send the message and get the reply.
//
hr = this->ExchangeAndParsePAST(pDevice->GetPASTSocket(),
(SOCKADDR*) (&saddrinServerAddress),
sizeof(saddrinServerAddress),
(char *) &DeregisterReq,
sizeof(DeregisterReq),
dwMsgID,
ptuRetry,
&RespInfo);
if (hr != DPNH_OK)
{
if (hr != DPNHERR_SERVERNOTRESPONDING)
{
DPFX(DPFPREP, 0, "De-registering with server failed!");
goto Failure;
}
//
// Server stopped responding, but who cares, we were de-registering
// anyway.
//
DPFX(DPFPREP, 1, "Server stopped responding while de-registering! Ignoring.");
hr = DPNH_OK;
}
else
{
if (RespInfo.cMsgType != PAST_MSGID_DEREGISTER_RESPONSE)
{
DPFX(DPFPREP, 0, "Got unexpected response type %u, failed de-registering!", RespInfo.cMsgType);
hr = DPNHERR_GENERIC;
goto Failure;
}
}
pDevice->SetPASTClientID(0, fRemote);
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::DeregisterWithPASTServer
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::ExtendAllExpiringLeases"
//=============================================================================
// CNATHelpPAST::ExtendAllExpiringLeases
//-----------------------------------------------------------------------------
//
// Description: Renews any port leases that are close to expiring (within 2
// minutes of expiration time).
//
// The object lock is assumed to be held.
//
// Arguments: None.
//
// Returns: HRESULT
// DPNH_OK - Lease extension was successful.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::ExtendAllExpiringLeases(void)
{
HRESULT hr = DPNH_OK;
CBilink * pBilink;
CRegisteredPort * pRegisteredPort;
CDevice * pDevice;
DWORD dwLeaseTimeRemaining;
DPFX(DPFPREP, 5, "Enter");
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED);
//
// Walk the list of all registered ports and check for leases that need to
// be extended.
// The lock is already held.
//
pBilink = this->m_blRegisteredPorts.GetNext();
while (pBilink != (&this->m_blRegisteredPorts))
{
pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
pDevice = pRegisteredPort->GetOwningDevice();
//
// If the port is registered remotely, extend that lease, if necessary.
//
if (pRegisteredPort->GetPASTBindID(TRUE) != 0)
{
DNASSERT(pDevice != NULL);
dwLeaseTimeRemaining = pRegisteredPort->GetPASTLeaseExpiration(TRUE) - timeGetTime();
if (dwLeaseTimeRemaining < LEASE_RENEW_TIME)
{
hr = this->ExtendPASTLease(pRegisteredPort, TRUE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't extend port mapping lease on remote PAST server (0x%lx)! Ignoring.", hr);
//
// We'll treat this as non-fatal, but we have to dump the server.
//
this->ClearDevicesPASTServer(pDevice, TRUE);
hr = DPNH_OK;
}
}
}
//
// If the port is registered locally, extend that lease, if necessary.
//
if (pRegisteredPort->GetPASTBindID(FALSE) != 0)
{
DNASSERT(pDevice != NULL);
dwLeaseTimeRemaining = pRegisteredPort->GetPASTLeaseExpiration(FALSE) - timeGetTime();
if (dwLeaseTimeRemaining < LEASE_RENEW_TIME)
{
hr = this->ExtendPASTLease(pRegisteredPort, FALSE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't extend port mapping lease on local PAST server (0x%lx)! Ignoring.", hr);
//
// We'll treat this as non-fatal, but we have to dump the server.
//
this->ClearDevicesPASTServer(pDevice, FALSE);
hr = DPNH_OK;
}
}
}
pBilink = pBilink->GetNext();
}
DNASSERT(hr == DPNH_OK);
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
} // CNATHelpPAST::ExtendAllExpiringLeases
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::UpdateServerStatus"
//=============================================================================
// CNATHelpPAST::UpdateServerStatus
//-----------------------------------------------------------------------------
//
// Description: Checks to see if any Internet gateways have stopped
// responding or are now available.
//
// The object lock is assumed to be held.
//
// Arguments: None.
//
// Returns: HRESULT
// DPNH_OK - The update was successful.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::UpdateServerStatus(void)
{
HRESULT hr = DPNH_OK;
CBilink blNoRemotePASTList;
DWORD dwMinUpdateServerStatusInterval;
DWORD dwCurrentTime;
CBilink * pBilink;
CDevice * pDevice;
CDevice * pDeviceRemoteICSServer = NULL;
CDevice * pDeviceLocalICSServer = NULL;
CDevice * pDeviceLocalPFWOnlyServer = NULL;
BOOL fSendRemoteGatewayDiscovery;
DPFX(DPFPREP, 5, "Enter");
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED);
blNoRemotePASTList.Initialize();
//
// Cache the current value of the global. This should be atomic so no need
// to take the globals lock.
//
dwMinUpdateServerStatusInterval = g_dwMinUpdateServerStatusInterval;
//
// Capture the current time.
//
dwCurrentTime = timeGetTime();
//
// If this isn't the first time to update server status, but it hasn't been
// very long since we last checked, don't. This will prevent unnecessary
// network traffic if GetCaps is called frequently (in response to many
// alert events, for example).
//
// However, if we just found a new device, update the status anyway.
//
//
if (this->m_dwLastUpdateServerStatusTime != 0)
{
if ((dwCurrentTime - this->m_dwLastUpdateServerStatusTime) < dwMinUpdateServerStatusInterval)
{
if (! (this->m_dwFlags & NATHELPPASTOBJ_DEVICECHANGED))
{
DPFX(DPFPREP, 5, "Server status was just updated at %u, not updating again (time = %u, min interval = %u).",
this->m_dwLastUpdateServerStatusTime,
dwCurrentTime,
dwMinUpdateServerStatusInterval);
//
// hr == DPNH_OK
//
goto Exit;
}
DPFX(DPFPREP, 5, "Server status was just updated at %u (time = %u, min interval = %u), but there was a device change that may affect things.",
this->m_dwLastUpdateServerStatusTime,
dwCurrentTime,
dwMinUpdateServerStatusInterval);
//
// Continue...
//
}
//
// If we're allowed to keep polling for remote gateways after startup,
// do so. Otherwise, only do it if a device has changed or a port has
// been registered since our last check.
//
if ((g_fKeepPollingForRemoteGateway) ||
(this->m_dwFlags & NATHELPPASTOBJ_DEVICECHANGED) ||
(this->m_dwFlags & NATHELPPASTOBJ_PORTREGISTERED))
{
fSendRemoteGatewayDiscovery = TRUE;
}
else
{
fSendRemoteGatewayDiscovery = FALSE;
}
}
else
{
//
// We always poll for new remote gateways during startup.
//
fSendRemoteGatewayDiscovery = TRUE;
}
//
// Prevent the timer from landing exactly on 0.
//
if (dwCurrentTime == 0)
{
dwCurrentTime = 1;
}
this->m_dwLastUpdateServerStatusTime = dwCurrentTime;
//
// Turn off the 'device changed' and 'port registered' flags, if they were
// on.
//
this->m_dwFlags &= ~(NATHELPPASTOBJ_DEVICECHANGED | NATHELPPASTOBJ_PORTREGISTERED);
//
// Loop through all the devices.
//
pBilink = this->m_blDevices.GetNext();
while (pBilink != &this->m_blDevices)
{
pDevice = DEVICE_FROM_BILINK(pBilink);
//
// This might be a new device, so register any ports with this address
// that were previously unowned (because this device's address was
// unknown at the time).
//
hr = this->RegisterPreviouslyUnownedPortsWithDevice(pDevice, FALSE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't register previously unowned ports with device 0x%p!.",
pDevice);
goto Failure;
}
if (pDevice->GetPASTClientID(FALSE) == 0)
{
//
// The device did not previously have a local PAST server. See if
// one came online.
//
DNASSERT((pDevice->GetPASTCachedMaps(FALSE))->IsEmpty());
hr = this->CheckForLocalPASTServerAndRegister(pDevice);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't check for local PAST server (and register with it) on device 0x%p!",
pDevice);
goto Failure;
}
if (pDevice->GetPASTClientID(FALSE) != 0)
{
//
// Wow, a local PAST server is now available.
//
//
// Remember the device if it has the first local ICS or PFW
// PAST server we've seen.
//
if ((pDevice->HasLocalICSPASTServer()) &&
(pDeviceLocalICSServer == NULL))
{
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_USEPASTICS);
pDeviceLocalICSServer = pDevice;
}
else if ((pDevice->HasLocalPFWOnlyPASTServer()) &&
(pDeviceLocalPFWOnlyServer == NULL))
{
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_USEPASTPFW);
pDeviceLocalPFWOnlyServer = pDevice;
}
//
// We don't need to register all the existing mappings
// associated with the device that the user has already
// requested. CheckForLocalPASTServerAndRegister took care of
// that for us.
//
}
else
{
DPFX(DPFPREP, 7, "Still no local PAST server on device 0x%p.",
pDevice);
}
}
else
{
//
// The device previously had a local PAST server.
//
//
// Make sure the server is still alive and see whether it's handing
// out valid public addresses or not.
//
hr = this->UpdatePASTPublicAddressValidity(pDevice, FALSE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't update local PAST server public address validity on device 0x%p!",
pDevice);
goto Failure;
}
//
// If there's still a PAST server, remember the device if it's the
// first local ICS or PFW PAST server we've seen.
//
if (pDevice->GetPASTClientID(FALSE) != 0)
{
if ((pDevice->HasLocalICSPASTServer()) &&
(pDeviceLocalICSServer == NULL))
{
pDeviceLocalICSServer = pDevice;
}
else if ((pDevice->HasLocalPFWOnlyPASTServer()) &&
(pDeviceLocalPFWOnlyServer == NULL))
{
pDeviceLocalPFWOnlyServer = pDevice;
}
}
}
if (this->m_dwFlags & NATHELPPASTOBJ_USEPASTICS)
{
if (pDevice->GetPASTClientID(TRUE) == 0)
{
//
// The device did not previously have a remote PAST server.
// Remember it so we can check if one came online below,
// unless we're not allowed to perform remote gateway
// discovery.
//
DNASSERT((pDevice->GetPASTCachedMaps(TRUE))->IsEmpty());
if (fSendRemoteGatewayDiscovery)
{
pDevice->m_blTempList.InsertBefore(&blNoRemotePASTList);
}
}
else
{
//
// The device previously had a remote PAST server.
//
//
// Make sure the server is still alive and see whether it's
// handing out valid public addresses or not.
//
hr = this->UpdatePASTPublicAddressValidity(pDevice, TRUE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't update remote PAST server public address validity on device 0x%p!",
pDevice);
goto Failure;
}
//
// If there's still a PAST server, remember the device if it's
// the first remote PAST server we've seen.
//
if ((pDevice->GetPASTClientID(TRUE) != 0) &&
(pDeviceRemoteICSServer == NULL))
{
pDeviceRemoteICSServer = pDevice;
}
}
}
else
{
//
// Not using PAST for ICS NAT traversal.
//
}
pBilink = pBilink->GetNext();
}
//
// Update any devices that don't currently have remote PAST servers.
//
if (! blNoRemotePASTList.IsEmpty())
{
DNASSERT(fSendRemoteGatewayDiscovery);
hr = this->RegisterMultipleDevicesWithRemotePAST(&blNoRemotePASTList,
&pDeviceRemoteICSServer);
if (hr != S_OK)
{
DPFX(DPFPREP, 0, "Couldn't register multiple devices with remote PAST servers!");
goto Failure;
}
}
//
// Some new servers may have come online. If so, we can now map wildcard
// ports that were registered previously. Figure out which device that is.
//
if (pDeviceRemoteICSServer != NULL)
{
pDevice = pDeviceRemoteICSServer;
}
else if (pDeviceLocalICSServer != NULL)
{
pDevice = pDeviceLocalICSServer;
}
else if (pDeviceLocalPFWOnlyServer != NULL)
{
pDevice = pDeviceLocalPFWOnlyServer;
}
else
{
pDevice = NULL;
}
if (pDevice != NULL)
{
//
// Register any wildcard ports that are unowned with this best device.
//
hr = this->RegisterPreviouslyUnownedPortsWithDevice(pDevice, TRUE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't register unowned wildcard ports with device 0x%p!.",
pDevice);
goto Failure;
}
}
#ifdef DBG
else
{
DPFX(DPFPREP, 7, "No devices have a remote or local PAST server.");
}
#endif // DBG
DPFX(DPFPREP, 7, "Spent %u ms updating server status, starting at %u.",
(timeGetTime() - dwCurrentTime), dwCurrentTime);
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
//
// Remove any items still in the temp list.
//
pBilink = blNoRemotePASTList.GetNext();
while (pBilink != &blNoRemotePASTList)
{
pDevice = DEVICE_FROM_TEMP_BILINK(pBilink);
pBilink = pBilink->GetNext();
pDevice->m_blTempList.RemoveFromList();
}
goto Exit;
} // CNATHelpPAST::UpdateServerStatus
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::AssignOrListenPASTPort"
//=============================================================================
// CNATHelpPAST::AssignOrListenPASTPort
//-----------------------------------------------------------------------------
//
// Description: Attempts to assign a port mapping with the PAST server.
// This may detect a change in server address.
//
// The object lock is assumed to be held.
//
// Arguments:
// CRegisteredPort * pRegisteredPort - Pointer to port object mapping to
// assign.
// BOOL fRemote - TRUE if should assign with remote
// server, FALSE if assign with local
// server.
//
// Returns: HRESULT
// DPNH_OK - The assignment was successful.
// DPNHERR_GENERIC - An error occurred.
// DPNHERR_PORTUNAVAILABLE - The server could not bind one of the
// ports.
// DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
// message.
//=============================================================================
HRESULT CNATHelpPAST::AssignOrListenPASTPort(CRegisteredPort * const pRegisteredPort,
const BOOL fRemote)
{
HRESULT hr = DPNH_OK;
CDevice * pDevice;
SOCKADDR_IN saddrinServerAddress;
DWORD dwClientID;
DWORD dwMsgID;
DWORD * ptuRetry;
BOOL fListenRequest;
BOOL fSharedUDPListener;
CHAR cNumPorts;
DWORD dwLeaseTimeInSecs;
SOCKADDR_IN * pasaddrinAddressesToAssign;
DWORD dwMsgSize;
PVOID pvRequest = NULL;
PBYTE pbCurrent;
CHAR cTemp;
PAST_RESPONSE_INFO RespInfo;
CBilink * pBilink;
CRegisteredPort * pTempRegisteredPort;
BOOL fResult;
BOOL fFirstLease;
HRESULT temphr;
#ifdef DBG
DWORD dwError;
#endif // DBG
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)",
this, pRegisteredPort, fRemote);
DNASSERT(pRegisteredPort != NULL);
pDevice = pRegisteredPort->GetOwningDevice();
DNASSERT(pDevice != NULL);
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED);
DNASSERT(pDevice->GetPASTSocket() != INVALID_SOCKET);
//
// Create a SOCKADDR to address the PAST service, and get the appropriate
// client ID, initial retry timeout, and next message ID to use.
//
ZeroMemory(&saddrinServerAddress, sizeof(saddrinServerAddress));
saddrinServerAddress.sin_family = AF_INET;
saddrinServerAddress.sin_port = HTONS(PAST_HOST_PORT);
if (fRemote)
{
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetRemotePASTServerAddressV4();
dwMsgID = pDevice->GetNextRemotePASTMsgID();
ptuRetry = pDevice->GetRemotePASTRetryTimeoutPtr();
}
else
{
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
dwMsgID = pDevice->GetNextLocalPASTMsgID();
ptuRetry = pDevice->GetLocalPASTRetryTimeoutPtr();
}
dwClientID = pDevice->GetPASTClientID(fRemote);
DNASSERT(dwClientID != 0);
fListenRequest = pRegisteredPort->IsFixedPort();
fSharedUDPListener = pRegisteredPort->IsSharedPort();
pasaddrinAddressesToAssign = pRegisteredPort->GetPrivateAddressesArray();
cNumPorts = (CHAR) pRegisteredPort->GetNumAddresses(); // the possible loss of data is okay, capped at DPNH_MAX_SIMULTANEOUS_PORTS anyway
DNASSERT(cNumPorts > 0);
dwLeaseTimeInSecs = pRegisteredPort->GetRequestedLeaseTime() / 1000;
//
// If this is a remote attempt and it was already mapped with a local
// Personal Firewall PAST server, be sure to use those addresses. The
// PFW PAST server will give different port values, and if there's a NAT
// upstream we need to map the ports that are actually reachable
// externally. Yes, you could consider it user error to have a firewall
// enabled when you're behind a NAT: why wouldn't they just enable firewall
// behavior on the NAT? However, we'll support that.
//
if ((fRemote) &&
(pDevice->HasLocalPFWOnlyPASTServer()) &&
(pDevice->IsPASTPublicAddressAvailable(FALSE)) &&
(! pRegisteredPort->IsPASTPortUnavailable(FALSE)))
{
DNASSERT(pDevice->GetPASTClientID(FALSE) != 0);
DNASSERT(pRegisteredPort->GetPASTBindID(FALSE) != 0);
DPFX(DPFPREP, 2, "Using public addresses previously returned by local Personal Firewall-only PAST server.");
pasaddrinAddressesToAssign = pRegisteredPort->GetPASTPublicAddressesArray(FALSE);
}
DNASSERT(pasaddrinAddressesToAssign != NULL);
DPFX(DPFPREP, 7, "Sending %s%sfor %u ports (first = %u.%u.%u.%u:%u), requesting %u second lease.",
((fListenRequest) ? _T("LISTEN_REQUEST") : _T("ASSIGN_REQUEST_RSAP_IP")),
((fSharedUDPListener) ? _T(" (with SHARED UDP LISTENER vendor code) ") : _T(" ")),
cNumPorts,
pasaddrinAddressesToAssign[0].sin_addr.S_un.S_un_b.s_b1,
pasaddrinAddressesToAssign[0].sin_addr.S_un.S_un_b.s_b2,
pasaddrinAddressesToAssign[0].sin_addr.S_un.S_un_b.s_b3,
pasaddrinAddressesToAssign[0].sin_addr.S_un.S_un_b.s_b4,
NTOHS(pasaddrinAddressesToAssign[0].sin_port),
dwLeaseTimeInSecs);
//
// Build the request message. We take advantage of the fact that both the
// ASSIGN_REQUEST, LISTEN_REQUEST, and SHAREDLISTEN_REQUEST messages look
// almost identical, except SHAREDLISTEN_REQUESTs have an extra vendor
// option.
//
dwMsgSize = (sizeof(PAST_MSG_ASSIGNORLISTEN_REQUEST) + ((cNumPorts - 1) * sizeof(WORD) * 2));
if (! fSharedUDPListener)
{
dwMsgSize -= sizeof(PAST_PARAM_MSVENDOR_CODE);
}
pvRequest = DNMalloc(dwMsgSize);
if (pvRequest == NULL)
{
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
pbCurrent = (PBYTE) pvRequest;
(*pbCurrent++) = PAST_VERSION;
(*pbCurrent++) = (fListenRequest) ? PAST_MSGID_LISTEN_REQUEST : PAST_MSGID_ASSIGN_REQUEST_RSAP_IP;
((PAST_PARAM_CLIENTID*) pbCurrent)->code = PAST_PARAMID_CLIENTID;
((PAST_PARAM_CLIENTID*) pbCurrent)->len = sizeof(PAST_PARAM_CLIENTID) - sizeof(PAST_PARAM);
((PAST_PARAM_CLIENTID*) pbCurrent)->clientid = dwClientID;
pbCurrent += sizeof (PAST_PARAM_CLIENTID);
//
// Local Address (will be returned by PAST server, use don't-care value).
//
((PAST_PARAM_ADDRESS*) pbCurrent)->code = PAST_PARAMID_ADDRESS;
((PAST_PARAM_ADDRESS*) pbCurrent)->len = sizeof(PAST_PARAM_ADDRESS) - sizeof(PAST_PARAM);
((PAST_PARAM_ADDRESS*) pbCurrent)->version = PAST_ADDRESSTYPE_IPV4;
((PAST_PARAM_ADDRESS*) pbCurrent)->addr = PAST_ANY_ADDRESS;
pbCurrent += sizeof (PAST_PARAM_ADDRESS);
//
// Local Port, this is the port the user has opened for which we are
// assigning a global alias.
//
// NOTE: Ports appeared to be transferred in x86 format, contrary to the
// spec, which says network byte order.
//
(*pbCurrent++) = PAST_PARAMID_PORTS;
*((WORD*) pbCurrent) = sizeof(CHAR) + (sizeof(WORD) * cNumPorts);
pbCurrent += 2;
(*pbCurrent++) = cNumPorts;
for(cTemp = 0; cTemp < cNumPorts; cTemp++)
{
*((WORD*) pbCurrent) = NTOHS(pasaddrinAddressesToAssign[cTemp].sin_port);
pbCurrent += 2;
}
//
// Remote Address (not used with our flow control policy and reserved for
// future use, use don't-care value)
//
((PAST_PARAM_ADDRESS*) pbCurrent)->code = PAST_PARAMID_ADDRESS;
((PAST_PARAM_ADDRESS*) pbCurrent)->len = sizeof(PAST_PARAM_ADDRESS) - sizeof(PAST_PARAM);
((PAST_PARAM_ADDRESS*) pbCurrent)->version = PAST_ADDRESSTYPE_IPV4;
((PAST_PARAM_ADDRESS*) pbCurrent)->addr = PAST_ANY_ADDRESS;
pbCurrent += sizeof (PAST_PARAM_ADDRESS);
(*pbCurrent++) = PAST_PARAMID_PORTS;
*((WORD*) pbCurrent) = sizeof(CHAR) + (sizeof(WORD) * cNumPorts);
pbCurrent += 2;
(*pbCurrent++) = cNumPorts;
//for(cTemp = 0; cTemp < cNumPorts; cTemp++)
//{
// *((WORD*) pbCurrent) = NTOHS(PAST_ANY_PORT);
// pbCurrent += 2;
//}
pbCurrent += cNumPorts * sizeof(WORD);
//
// The following parameters are optional according to PAST spec.
//
//
// Lease code, ask for what the user wants, but they shouldn't count on
// getting that.
//
((PAST_PARAM_LEASE*) pbCurrent)->code = PAST_PARAMID_LEASE;
((PAST_PARAM_LEASE*) pbCurrent)->len = sizeof(PAST_PARAM_LEASE) - sizeof(PAST_PARAM);
((PAST_PARAM_LEASE*) pbCurrent)->leasetime = dwLeaseTimeInSecs;
pbCurrent += sizeof (PAST_PARAM_LEASE);
//
// Tunnel Type is IP-IP (PAST currently ignores it, actually).
//
((PAST_PARAM_TUNNELTYPE*) pbCurrent)->code = PAST_PARAMID_TUNNELTYPE;
((PAST_PARAM_TUNNELTYPE*) pbCurrent)->len = sizeof(PAST_PARAM_TUNNELTYPE) - sizeof(PAST_PARAM);
((PAST_PARAM_TUNNELTYPE*) pbCurrent)->tunneltype = PAST_TUNNEL_IP_IP;
pbCurrent += sizeof (PAST_PARAM_TUNNELTYPE);
//
// Message ID is optional, but we use it since we use UDP for a transport
// it is required.
//
((PAST_PARAM_MESSAGEID*) pbCurrent)->code = PAST_PARAMID_MESSAGEID;
((PAST_PARAM_MESSAGEID*) pbCurrent)->len = sizeof(PAST_PARAM_MESSAGEID) - sizeof(PAST_PARAM);
((PAST_PARAM_MESSAGEID*) pbCurrent)->msgid = dwMsgID;
pbCurrent += sizeof (PAST_PARAM_MESSAGEID);
//
// The following parameters are vendor specific options.
//
//
// Specify port type MS vendor option.
//
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->code = PAST_PARAMID_VENDOR;
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->len = sizeof(PAST_PARAM_MSVENDOR_CODE) - sizeof(PAST_PARAM);
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->vendorid = PAST_MS_VENDOR_ID;
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->option = (pRegisteredPort->IsTCP()) ? PAST_VC_MS_TCP_PORT : PAST_VC_MS_UDP_PORT;
pbCurrent += sizeof (PAST_PARAM_MSVENDOR_CODE);
//
// Specify no-tunneling MS vendor option.
//
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->code = PAST_PARAMID_VENDOR;
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->len = sizeof(PAST_PARAM_MSVENDOR_CODE) - sizeof(PAST_PARAM);
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->vendorid = PAST_MS_VENDOR_ID;
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->option = PAST_VC_MS_NO_TUNNEL;
pbCurrent += sizeof (PAST_PARAM_MSVENDOR_CODE);
//
// A shared UDP port listen type has an extra vendor option.
//
if (fSharedUDPListener)
{
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->code = PAST_PARAMID_VENDOR;
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->len = sizeof(PAST_PARAM_MSVENDOR_CODE) - sizeof(PAST_PARAM);
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->vendorid = PAST_MS_VENDOR_ID;
((PAST_PARAM_MSVENDOR_CODE*) pbCurrent)->option = PAST_VC_MS_SHARED_UDP_LISTENER;
pbCurrent += sizeof (PAST_PARAM_MSVENDOR_CODE);
}
//
// Send the message and get the reply.
//
hr = this->ExchangeAndParsePAST(pDevice->GetPASTSocket(),
(SOCKADDR*) (&saddrinServerAddress),
sizeof(saddrinServerAddress),
(char *) pvRequest,
dwMsgSize,
dwMsgID,
ptuRetry,
&RespInfo);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Sending Assign/Listen Port request to server failed (err = 0x%lx)!", hr);
goto Failure;
}
if ((RespInfo.cMsgType == PAST_MSGID_ERROR_RESPONSE) &&
(RespInfo.wError == PASTERR_PORTUNAVAILABLE))
{
DPFX(DPFPREP, 1, "Couldn't assign/listen, port was unavailable.");
hr = DPNHERR_PORTUNAVAILABLE;
goto Failure;
}
if (((fListenRequest) && (RespInfo.cMsgType != PAST_MSGID_LISTEN_RESPONSE)) ||
((! fListenRequest) && (RespInfo.cMsgType != PAST_MSGID_ASSIGN_RESPONSE_RSAP_IP)))
{
DPFX(DPFPREP, 0, "Got unexpected response type %u, failed assign/listen!", RespInfo.cMsgType);
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Make sure the port count is valid.
//
if (RespInfo.cNumLocalPorts != cNumPorts)
{
DPFX(DPFPREP, 0, "PAST server returned an invalid number of local ports with success message (%u != %u)! Assuming port unavailable.",
RespInfo.cNumLocalPorts, cNumPorts);
DNASSERTX(! "Why is PAST server returning bogus number of ports?", 2);
hr = DPNHERR_PORTUNAVAILABLE;
goto Failure;
}
//
// Store the public address for the registered port, even if it's the
// no-public-address address (0.0.0.0).
//
hr = pRegisteredPort->SetPASTPublicV4Addresses(RespInfo.dwLocalAddressV4,
RespInfo.awLocalPorts,
RespInfo.cNumLocalPorts,
fRemote);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't set requested mapping's public addresses!");
goto Failure;
}
//
// Remember the bind ID, actual lease time (convert back to milliseconds),
// and address we were given.
//
pRegisteredPort->SetPASTBindID(RespInfo.dwBindID, fRemote);
pRegisteredPort->SetPASTLeaseExpiration((timeGetTime() + (RespInfo.dwLeaseTime * 1000)),
fRemote);
//
// Note whether this was the first lease or not.
//
fFirstLease = (this->m_dwNumLeases == 0) ? TRUE : FALSE;
this->m_dwNumLeases++;
DPFX(DPFPREP, 7, "%s PAST lease for 0x%p added, total num leases = %u.",
((fRemote) ? _T("Remote") : _T("Local")), pRegisteredPort, this->m_dwNumLeases);
//
// We have different behavior whether the address is valid or not.
//
if (RespInfo.dwLocalAddressV4 == 0)
{
DPFX(DPFPREP, 1, "PAST server gave 0x%p an invalid address mapping for %u seconds (expires at %u).",
pRegisteredPort, RespInfo.dwLeaseTime,
pRegisteredPort->GetPASTLeaseExpiration(fRemote));
//
// If the mapping's IP address was zero, then we can't go handing it
// around.
//
// Further, if any other ports were registered with the server, their
// addresses are now all bogus as well, since it is assumed that the
// server will always hand out the same address for all assignments.
//
if (pDevice->IsPASTPublicAddressAvailable(fRemote))
{
pDevice->NoteNoPASTPublicAddressAvailable(fRemote);
//
// Since there was a change in the network, go back to polling
// relatively quickly.
//
this->ResetNextPollInterval();
//
// Any cached mappings could now be invalid. Force future
// re-queries to hit the network.
//
this->RemoveAllPASTCachedMappings(pDevice, fRemote);
//
// Loop through the existing registered port mappings and clear the
// addresses.
// We have the appropriate lock.
//
pBilink = pDevice->m_blOwnedRegPorts.GetNext();
while (pBilink != &pDevice->m_blOwnedRegPorts)
{
pTempRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
if (pTempRegisteredPort != pRegisteredPort)
{
if (pTempRegisteredPort->GetPASTBindID(fRemote) != 0)
{
DPFX(DPFPREP, 3, "Existing registered port mapping 0x%p is no longer valid.",
pTempRegisteredPort);
DNASSERT(! pTempRegisteredPort->IsPASTPortUnavailable(fRemote));
DNASSERT(pTempRegisteredPort->HasPASTPublicAddressesArray(fRemote));
DNASSERT(pTempRegisteredPort->IsFirstPASTPublicV4AddressDifferent(0, fRemote));
//
// Sorry, the address is gone.
//
pTempRegisteredPort->UpdatePASTPublicV4Addresses(RespInfo.dwLocalAddressV4,
fRemote);
//
// The user should call GetCaps to detect the address
// change. Note that GetRegisteredAddresses will already be
// returning NOMAPPING now, though.
//
this->m_dwFlags |= NATHELPPASTOBJ_ADDRESSESCHANGED;
}
else
{
DNASSERT(! pTempRegisteredPort->HasPASTPublicAddressesArray(fRemote));
}
}
else
{
//
// Skip the port we just registered.
//
}
pBilink = pBilink->GetNext();
}
}
#ifdef DBG
else
{
DPFX(DPFPREP, 7, "Still no public address for any ports.");
//
// Loop through the existing registered port mappings and make sure
// they don't have addresses (debug only).
// We have the appropriate lock.
//
pBilink = pDevice->m_blOwnedRegPorts.GetNext();
while (pBilink != &pDevice->m_blOwnedRegPorts)
{
pTempRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
if (pTempRegisteredPort->IsPASTPortUnavailable(fRemote))
{
DNASSERT(pTempRegisteredPort->GetPASTBindID(fRemote) == 0);
DNASSERT(! pTempRegisteredPort->HasPASTPublicAddressesArray(fRemote));
}
else
{
//
// Can't assert these things because we may not have tried
// mapping this port yet.
//
/*
DNASSERT(pTempRegisteredPort->GetPASTBindID(fRemote) != 0);
DNASSERT(pTempRegisteredPort->HasPASTPublicAddressesArray(fRemote));
DNASSERT(! (pTempRegisteredPort->IsFirstPASTPublicV4AddressDifferent(RespInfo.dwLocalAddressV4, fRemote)));
*/
}
pBilink = pBilink->GetNext();
}
}
#endif // DBG
}
else
{
DPFX(DPFPREP, 2, "PAST server gave 0x%p a valid address mapping for %u seconds (expires at %u).",
pRegisteredPort, RespInfo.dwLeaseTime,
pRegisteredPort->GetPASTLeaseExpiration(fRemote));
//
// The server may have previously been handing back invalid mappings.
// If any other ports were registered with the server that didn't get
// mapped, those addresses are now valid as well, since it is assumed
// that the server will will always hand out the same address for all
// assignments.
//
if (! pDevice->IsPASTPublicAddressAvailable(fRemote))
{
pDevice->NotePASTPublicAddressAvailable(fRemote);
//
// Since there was a change in the network, go back to polling
// relatively quickly.
//
this->ResetNextPollInterval();
//
// Any cached mappings could now be invalid. Force future
// re-queries to hit the network.
//
this->RemoveAllPASTCachedMappings(pDevice, fRemote);
//
// Loop through the existing registered port mappings and set the
// addresses.
// We have the appropriate lock.
//
pBilink = pDevice->m_blOwnedRegPorts.GetNext();
while (pBilink != &pDevice->m_blOwnedRegPorts)
{
pTempRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
if (pTempRegisteredPort != pRegisteredPort)
{
if (pTempRegisteredPort->GetPASTBindID(fRemote) != 0)
{
DPFX(DPFPREP, 3, "Existing registered port mapping 0x%p now has an address.",
pTempRegisteredPort);
DNASSERT(! pTempRegisteredPort->IsPASTPortUnavailable(fRemote));
DNASSERT(pTempRegisteredPort->HasPASTPublicAddressesArray(fRemote));
//
// Woohoo, there's an address now.
//
pTempRegisteredPort->UpdatePASTPublicV4Addresses(RespInfo.dwLocalAddressV4,
fRemote);
//
// The user should call GetCaps to detect the address
// change. Note that GetRegisteredAddresses will already be
// returning the new addresses now, though.
//
this->m_dwFlags |= NATHELPPASTOBJ_ADDRESSESCHANGED;
}
else
{
DNASSERT(! pTempRegisteredPort->HasPASTPublicAddressesArray(fRemote));
}
}
else
{
//
// Skip the port we just registered.
//
}
pBilink = pBilink->GetNext();
}
}
else
{
//
// We assume that the server will always hand out the same address
// to every mapping. Double check that if there's something in the
// list already then it has the same IP address. If not, the
// address has changed. Loop through the existing registered port
// mappings and convert them to use the same address as was just
// handed out.
// We have the appropriate lock.
//
pBilink = pDevice->m_blOwnedRegPorts.GetNext();
while (pBilink != &pDevice->m_blOwnedRegPorts)
{
pTempRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
if (pTempRegisteredPort != pRegisteredPort)
{
if (pTempRegisteredPort->GetPASTBindID(fRemote) != 0)
{
DNASSERT(! pTempRegisteredPort->IsPASTPortUnavailable(fRemote));
DNASSERT(pTempRegisteredPort->HasPASTPublicAddressesArray(fRemote));
DNASSERT(pTempRegisteredPort->IsFirstPASTPublicV4AddressDifferent(0, fRemote));
if (pTempRegisteredPort->IsFirstPASTPublicV4AddressDifferent(RespInfo.dwLocalAddressV4, fRemote))
{
DPFX(DPFPREP, 3, "Existing registered port mapping 0x%p differs from address just returned from PAST server.",
pTempRegisteredPort);
pTempRegisteredPort->UpdatePASTPublicV4Addresses(RespInfo.dwLocalAddressV4, fRemote);
//
// The user should call GetCaps to detect the address
// change. Note that GetRegisteredAddresses will already be
// returning the new addresses now, though.
//
this->m_dwFlags |= NATHELPPASTOBJ_ADDRESSESCHANGED;
}
else
{
//
// Address is same.
//
}
}
else
{
DNASSERT(! pTempRegisteredPort->HasPASTPublicAddressesArray(fRemote));
}
}
else
{
//
// Skip the port we just registered.
//
}
pBilink = pBilink->GetNext();
}
}
}
//
// Remember this expiration time if it's the one that's going to expire
// soonest.
//
if ((fFirstLease) ||
((int) (pRegisteredPort->GetPASTLeaseExpiration(fRemote) - this->m_dwEarliestLeaseExpirationTime) < 0))
{
if (fFirstLease)
{
DPFX(DPFPREP, 1, "Registered port 0x%p's %s PAST lease is the first lease (expires at %u).",
pRegisteredPort,
((fRemote) ? _T("remote") : _T("local")),
pRegisteredPort->GetPASTLeaseExpiration(fRemote));
}
else
{
DPFX(DPFPREP, 1, "Registered port 0x%p's %s PAST lease expires at %u which is earlier than the next earliest lease expiration (%u).",
pRegisteredPort,
((fRemote) ? _T("remote") : _T("local")),
pRegisteredPort->GetPASTLeaseExpiration(fRemote),
this->m_dwEarliestLeaseExpirationTime);
}
this->m_dwEarliestLeaseExpirationTime = pRegisteredPort->GetPASTLeaseExpiration(fRemote);
//
// Ping the event if there is one so that the user's GetCaps interval
// doesn't miss this new, shorter lease.
//
if (this->m_hAlertEvent != NULL)
{
fResult = SetEvent(this->m_hAlertEvent);
#ifdef DBG
if (! fResult)
{
dwError = GetLastError();
DPFX(DPFPREP, 0, "Couldn't set alert event 0x%p! err = %u",
this->m_hAlertEvent, dwError);
//
// Ignore failure...
//
}
#endif // DBG
}
//
// Ping the I/O completion port if there is one so that the user's
// GetCaps interval doesn't miss this new, shorter lease.
//
if (this->m_hAlertIOCompletionPort != NULL)
{
fResult = PostQueuedCompletionStatus(this->m_hAlertIOCompletionPort,
0,
this->m_dwAlertCompletionKey,
NULL);
#ifdef DBG
if (! fResult)
{
dwError = GetLastError();
DPFX(DPFPREP, 0, "Couldn't queue key %u on alert IO completion port 0x%p! err = %u",
this->m_dwAlertCompletionKey,
this->m_hAlertIOCompletionPort,
dwError);
//
// Ignore failure...
//
}
#endif // DBG
}
}
Exit:
if (pvRequest != NULL)
{
DNFree(pvRequest);
pvRequest = NULL;
}
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
//
// If we registered the port, de-register it.
//
if (pRegisteredPort->GetPASTBindID(fRemote) != 0)
{
temphr = this->FreePASTPort(pRegisteredPort, fRemote);
if (temphr != DPNH_OK)
{
//
// We'll treat this as non-fatal, but we have to dump the server.
//
this->ClearDevicesPASTServer(pRegisteredPort->GetOwningDevice(), fRemote);
}
}
goto Exit;
} // CNATHelpPAST::AssignOrListenPASTPort
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::FreePASTPort"
//=============================================================================
// CNATHelpPAST::FreePASTPort
//-----------------------------------------------------------------------------
//
// Description: Release a port mapping with the PAST server.
//
// The object lock is assumed to be held.
//
// Arguments:
// CRegisteredPort * pRegisteredPort - Pointer to port object mapping to
// release.
// BOOL fRemote - TRUE if should free from remote
// server, FALSE if freeing from local
// server.
//
// Returns: HRESULT
// DPNH_OK - The release was successful.
// DPNHERR_GENERIC - An error occurred.
// DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
// message.
//=============================================================================
HRESULT CNATHelpPAST::FreePASTPort(CRegisteredPort * const pRegisteredPort,
const BOOL fRemote)
{
HRESULT hr = DPNH_OK;
CDevice * pDevice;
SOCKADDR_IN saddrinServerAddress;
DWORD dwClientID;
DWORD dwMsgID;
DWORD * ptuRetry;
PAST_MSG_FREE_REQUEST FreeReq;
PAST_RESPONSE_INFO RespInfo;
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)",
this, pRegisteredPort, fRemote);
DNASSERT(pRegisteredPort != NULL);
pDevice = pRegisteredPort->GetOwningDevice();
DNASSERT(pDevice != NULL);
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED);
DNASSERT(pDevice->GetPASTSocket() != INVALID_SOCKET);
//
// Create a SOCKADDR to address the PAST service, and get the appropriate
// client ID, initial retry timeout, and next message ID to use.
//
ZeroMemory(&saddrinServerAddress, sizeof(saddrinServerAddress));
saddrinServerAddress.sin_family = AF_INET;
saddrinServerAddress.sin_port = HTONS(PAST_HOST_PORT);
if (fRemote)
{
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetRemotePASTServerAddressV4();
dwMsgID = pDevice->GetNextRemotePASTMsgID();
ptuRetry = pDevice->GetRemotePASTRetryTimeoutPtr();
}
else
{
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
dwMsgID = pDevice->GetNextLocalPASTMsgID();
ptuRetry = pDevice->GetLocalPASTRetryTimeoutPtr();
}
dwClientID = pDevice->GetPASTClientID(fRemote);
DNASSERT(dwClientID != 0);
//
// Build the request message.
//
ZeroMemory(&FreeReq, sizeof(FreeReq));
FreeReq.version = PAST_VERSION;
FreeReq.command = PAST_MSGID_FREE_REQUEST;
FreeReq.clientid.code = PAST_PARAMID_CLIENTID;
FreeReq.clientid.len = sizeof(FreeReq.clientid) - sizeof(PAST_PARAM);
FreeReq.clientid.clientid = dwClientID;
FreeReq.bindid.code = PAST_PARAMID_BINDID;
FreeReq.bindid.len = sizeof(FreeReq.bindid) - sizeof(PAST_PARAM);
FreeReq.bindid.bindid = pRegisteredPort->GetPASTBindID(fRemote);
FreeReq.msgid.code = PAST_PARAMID_MESSAGEID;
FreeReq.msgid.len = sizeof(FreeReq.msgid) - sizeof(PAST_PARAM);
FreeReq.msgid.msgid = dwMsgID;
//
// Send the message and get the reply.
//
hr = this->ExchangeAndParsePAST(pDevice->GetPASTSocket(),
(SOCKADDR*) (&saddrinServerAddress),
sizeof(saddrinServerAddress),
(char *) &FreeReq,
sizeof(FreeReq),
dwMsgID,
ptuRetry,
&RespInfo);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Freeing port mapping failed!");
goto Failure;
}
if (RespInfo.cMsgType != PAST_MSGID_FREE_RESPONSE)
{
DPFX(DPFPREP, 0, "Got unexpected response type %u, failed freeing port mapping!", RespInfo.cMsgType);
hr = DPNHERR_GENERIC;
goto Failure;
}
pRegisteredPort->SetPASTBindID(0, fRemote);
pRegisteredPort->ClearPASTPublicAddresses(fRemote);
//
// One more lease is gone.
//
DNASSERT(this->m_dwNumLeases > 0);
this->m_dwNumLeases--;
DPFX(DPFPREP, 7, "%s PAST lease for 0x%p removed, total num leases = %u.",
((fRemote) ? _T("Remote") : _T("Local")), pRegisteredPort, this->m_dwNumLeases);
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::FreePASTPort
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::InternalPASTQueryAddress"
//=============================================================================
// CNATHelpPAST::InternalPASTQueryAddress
//-----------------------------------------------------------------------------
//
// Description: Queries a port mapping with the PAST server.
//
// The object lock is assumed to be held.
//
// Arguments:
// CDevice * pDevice - Pointer to device whose PAST
// server should be queried.
// SOCKADDR_IN * psaddrinQueryAddress - Address to look up.
// SOCKADDR_IN * psaddrinResponseAddress - Place to store public address, if
// one exists.
// DWORD dwFlags - Flags to use when querying.
// BOOL fRemote - TRUE if querying remote, FALSE
// querying local server.
//
// Returns: HRESULT
// DPNH_OK - The query was successful.
// DPNHERR_GENERIC - An error occurred.
// DPNHERR_NOMAPPING - The server did not have a mapping for the
// given address.
// DPNHERR_NOMAPPINGBUTPRIVATE - The server indicated that no mapping
// exists, but the address is private.
// DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
// message.
//=============================================================================
HRESULT CNATHelpPAST::InternalPASTQueryAddress(CDevice * const pDevice,
const SOCKADDR_IN * const psaddrinQueryAddress,
SOCKADDR_IN * const psaddrinResponseAddress,
const DWORD dwFlags,
const BOOL fRemote)
{
HRESULT hr;
CBilink * pblCachedMaps;
DWORD dwCurrentTime;
CBilink * pBilink;
CCacheMap * pCacheMap;
SOCKADDR_IN saddrinServerAddress;
DWORD dwClientID;
DWORD dwMsgID;
DWORD * ptuRetry;
PAST_MSG_QUERY_REQUEST_PORTS QueryReq;
PAST_RESPONSE_INFO RespInfo;
DWORD dwCacheMapFlags;
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%lx, %i)",
this, pDevice, psaddrinQueryAddress, psaddrinResponseAddress,
dwFlags, fRemote);
DNASSERT(pDevice != NULL);
DNASSERT(psaddrinQueryAddress != NULL);
DNASSERT(psaddrinResponseAddress != NULL);
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED);
DNASSERT(pDevice->GetPASTSocket() != INVALID_SOCKET);
DPFX(DPFPREP, 7, "Querying for address %u.%u.%u.%u:%u %s.",
psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b4,
NTOHS(psaddrinQueryAddress->sin_port),
((dwFlags & DPNHQUERYADDRESS_TCP) ? _T("TCP") : _T("UDP")));
//
// First, check if we've looked this address up recently and already have
// the result cached.
// The lock is already held.
//
pblCachedMaps = pDevice->GetPASTCachedMaps(fRemote);
dwCurrentTime = timeGetTime();
pBilink = pblCachedMaps->GetNext();
while (pBilink != pblCachedMaps)
{
pCacheMap = CACHEMAP_FROM_BILINK(pBilink);
pBilink = pBilink->GetNext();
//
// Make sure this cached mapping hasn't expired.
//
if ((int) (pCacheMap->GetExpirationTime() - dwCurrentTime) < 0)
{
DPFX(DPFPREP, 5, "Cached mapping 0x%p has expired.", pCacheMap);
pCacheMap->m_blList.RemoveFromList();
delete pCacheMap;
}
else
{
//
// If this mapping is for the right address and type of address,
// then we've already got our answer.
//
if (pCacheMap->DoesMatchQuery(psaddrinQueryAddress, dwFlags))
{
if (pCacheMap->IsNotFound())
{
if ((dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED) &&
(pCacheMap->IsPrivateButUnmapped()))
{
DPFX(DPFPREP, 5, "Address was already determined to not have a mapping but still be private.");
hr = DPNHERR_NOMAPPINGBUTPRIVATE;
}
else
{
DPFX(DPFPREP, 5, "Address was already determined to not have a mapping.");
hr = DPNHERR_NOMAPPING;
}
}
else
{
pCacheMap->GetResponseAddressV4(psaddrinResponseAddress);
DPFX(DPFPREP, 5, "Address was already determined to have a mapping.");
hr = DPNH_OK;
}
goto Exit;
}
}
}
dwCacheMapFlags = QUERYFLAGSMASK(dwFlags);
//
// If we're here, we haven't already cached the answer.
//
// Create a SOCKADDR to address the PAST service, and get the appropriate
// client ID, initial retry timeout, and next message ID to use.
//
ZeroMemory(&saddrinServerAddress, sizeof(saddrinServerAddress));
saddrinServerAddress.sin_family = AF_INET;
saddrinServerAddress.sin_port = HTONS(PAST_HOST_PORT);
if (fRemote)
{
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetRemotePASTServerAddressV4();
dwMsgID = pDevice->GetNextRemotePASTMsgID();
ptuRetry = pDevice->GetRemotePASTRetryTimeoutPtr();
}
else
{
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
dwMsgID = pDevice->GetNextLocalPASTMsgID();
ptuRetry = pDevice->GetLocalPASTRetryTimeoutPtr();
}
dwClientID = pDevice->GetPASTClientID(fRemote);
DNASSERT(dwClientID != 0);
//
// Build the request message for asking the server.
//
ZeroMemory(&QueryReq, sizeof(QueryReq));
QueryReq.version = PAST_VERSION;
QueryReq.command = PAST_MSGID_QUERY_REQUEST;
QueryReq.clientid.code = PAST_PARAMID_CLIENTID;
QueryReq.clientid.len = sizeof(QueryReq.clientid) - sizeof(PAST_PARAM);
QueryReq.clientid.clientid = dwClientID;
QueryReq.address.code = PAST_PARAMID_ADDRESS;
QueryReq.address.len = sizeof(QueryReq.address) - sizeof(PAST_PARAM);
QueryReq.address.version = PAST_ADDRESSTYPE_IPV4;
QueryReq.address.addr = psaddrinQueryAddress->sin_addr.s_addr;
//
// NOTE: Ports appeared to be transferred in x86 format, contrary to
// the spec, which says network byte order.
//
QueryReq.port.code = PAST_PARAMID_PORTS;
QueryReq.port.len = sizeof(QueryReq.port) - sizeof(PAST_PARAM);
QueryReq.port.nports = 1;
QueryReq.port.port = NTOHS(psaddrinQueryAddress->sin_port);
QueryReq.porttype.code = PAST_PARAMID_VENDOR;
QueryReq.porttype.len = sizeof(QueryReq.porttype) - sizeof(PAST_PARAM);
QueryReq.porttype.vendorid = PAST_MS_VENDOR_ID;
QueryReq.porttype.option = (dwFlags & DPNHQUERYADDRESS_TCP) ? PAST_VC_MS_TCP_PORT : PAST_VC_MS_UDP_PORT;
QueryReq.querytype.code = PAST_PARAMID_VENDOR;
QueryReq.querytype.len = sizeof(QueryReq.querytype) - sizeof(PAST_PARAM);
QueryReq.querytype.vendorid = PAST_MS_VENDOR_ID;
QueryReq.querytype.option = PAST_VC_MS_QUERY_MAPPING;
QueryReq.msgid.code = PAST_PARAMID_MESSAGEID;
QueryReq.msgid.len = sizeof(QueryReq.msgid) - sizeof(PAST_PARAM);
QueryReq.msgid.msgid = dwMsgID;
//
// Send the message and get the reply.
//
hr = this->ExchangeAndParsePAST(pDevice->GetPASTSocket(),
(SOCKADDR*) (&saddrinServerAddress),
sizeof(saddrinServerAddress),
(char *) &QueryReq,
sizeof(QueryReq),
dwMsgID,
ptuRetry,
&RespInfo);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Querying port mapping failed!");
goto Failure;
}
if (RespInfo.cMsgType != PAST_MSGID_QUERY_RESPONSE)
{
//
// We got something, but it's not the right response. Try determining
// if the address is local, if allowed.
//
if (dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED)
{
//
// If Personal Firewall is enabled, don't bother checking if the
// address is local. Anything on the other side of the firewall is
// considered non-local.
//
if ((pDevice->GetPASTClientID(FALSE) == 0) ||
(! pDevice->HasLocalPFWOnlyPASTServer()))
{
if (this->IsAddressLocal(pDevice, psaddrinQueryAddress))
{
DPFX(DPFPREP, 5, "Address appears to be local, returning NOMAPPINGBUTPRIVATE.");
dwCacheMapFlags |= CACHEMAPOBJ_PRIVATEBUTUNMAPPED;
hr = DPNHERR_NOMAPPINGBUTPRIVATE;
}
else
{
DPFX(DPFPREP, 5, "Address does not appear to be local, returning NOMAPPING.");
hr = DPNHERR_NOMAPPING;
}
}
else
{
DPFX(DPFPREP, 5, "Device 0x%p has Personal Firewall enabled, not checking address locality and returning NOMAPPING.");
hr = DPNHERR_NOMAPPING;
}
}
else
{
DPFX(DPFPREP, 1, "Got non-success response type %u while querying port mapping, assuming does not exist.", RespInfo.cMsgType);
hr = DPNHERR_NOMAPPING;
}
//
// Cache the fact that we could not determine a mapping for that
// address, if allowed.
//
if (dwFlags & DPNHQUERYADDRESS_CACHENOTFOUND)
{
pCacheMap = new CCacheMap(psaddrinQueryAddress,
(GETTIMESTAMP() + g_dwCacheLifeNotFound),
(dwCacheMapFlags | CACHEMAPOBJ_NOTFOUND));
if (pCacheMap == NULL)
{
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
pCacheMap->m_blList.InsertBefore(pblCachedMaps);
}
goto Failure;
}
//
// Make sure the port count is valid.
//
if (RespInfo.cNumLocalPorts != 1)
{
DPFX(DPFPREP, 0, "PAST server returned an invalid number of local ports with success message (%u)! Assuming no mapping.",
RespInfo.cNumLocalPorts);
DNASSERTX(! "Why is PAST server returning bogus number of ports?", 2);
hr = DPNHERR_NOMAPPING;
goto Failure;
}
//
// Convert the loopback address to the device address.
//
if (RespInfo.dwLocalAddressV4 == NETWORKBYTEORDER_INADDR_LOOPBACK)
{
RespInfo.dwLocalAddressV4 = pDevice->GetLocalAddressV4();
DPFX(DPFPREP, 1, "Converted loopback address to device address (%u.%u.%u.%u).",
((IN_ADDR*) (&RespInfo.dwLocalAddressV4))->S_un.S_un_b.s_b1,
((IN_ADDR*) (&RespInfo.dwLocalAddressV4))->S_un.S_un_b.s_b2,
((IN_ADDR*) (&RespInfo.dwLocalAddressV4))->S_un.S_un_b.s_b3,
((IN_ADDR*) (&RespInfo.dwLocalAddressV4))->S_un.S_un_b.s_b4);
}
//
// Ensure that we're not getting something bogus. Use saddrinServerAddress
// as a temporary variable.
//
saddrinServerAddress.sin_addr.S_un.S_addr = RespInfo.dwLocalAddressV4;
if ((RespInfo.dwLocalAddressV4 == 0) ||
(RespInfo.awLocalPorts[0] == 0) ||
(! this->IsAddressLocal(pDevice, &saddrinServerAddress)))
{
DPFX(DPFPREP, 0, "PAST server returned an invalid private address (%u.%u.%u.%u:%u)! Assuming no mapping.",
((IN_ADDR*) (&RespInfo.dwLocalAddressV4))->S_un.S_un_b.s_b1,
((IN_ADDR*) (&RespInfo.dwLocalAddressV4))->S_un.S_un_b.s_b2,
((IN_ADDR*) (&RespInfo.dwLocalAddressV4))->S_un.S_un_b.s_b3,
((IN_ADDR*) (&RespInfo.dwLocalAddressV4))->S_un.S_un_b.s_b4,
NTOHS(RespInfo.awLocalPorts[0]));
DNASSERTX(! "Why is PAST server returning invalid private address?", 2);
hr = DPNHERR_NOMAPPING;
goto Failure;
}
//
// Return the address mapping to our caller.
//
ZeroMemory(psaddrinResponseAddress, sizeof(SOCKADDR_IN));
psaddrinResponseAddress->sin_family = AF_INET;
psaddrinResponseAddress->sin_addr.s_addr = RespInfo.dwLocalAddressV4;
psaddrinResponseAddress->sin_port = RespInfo.awLocalPorts[0];
//
// Cache the fact that we found a mapping for that address, if allowed.
//
if (dwFlags & DPNHQUERYADDRESS_CACHEFOUND)
{
pCacheMap = new CCacheMap(psaddrinQueryAddress,
(GETTIMESTAMP() + g_dwCacheLifeFound),
dwCacheMapFlags);
if (pCacheMap == NULL)
{
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
pCacheMap->SetResponseAddressV4(RespInfo.dwLocalAddressV4,
RespInfo.awLocalPorts[0]);
pCacheMap->m_blList.InsertBefore(pblCachedMaps);
}
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::InternalPASTQueryAddress
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::ExtendPASTLease"
//=============================================================================
// CNATHelpPAST::ExtendPASTLease
//-----------------------------------------------------------------------------
//
// Description: Asks the PAST server to extend a port mapping lease.
//
// The object lock is assumed to be held.
//
// Arguments:
// CRegisteredPort * pRegisteredPort - Pointer to port object mapping to
// extend.
// BOOL fRemote - TRUE if extending on remote server,
// FALSE if extending on local server.
//
// Returns: HRESULT
// DPNH_OK - The extension was successful.
// DPNHERR_GENERIC - An error occurred.
// DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
// message.
//=============================================================================
HRESULT CNATHelpPAST::ExtendPASTLease(CRegisteredPort * const pRegisteredPort,
const BOOL fRemote)
{
HRESULT hr = DPNH_OK;
CDevice * pDevice;
SOCKADDR_IN saddrinServerAddress;
DWORD dwClientID;
DWORD dwMsgID;
DWORD * ptuRetry;
PAST_MSG_EXTEND_REQUEST ExtendReq;
PAST_RESPONSE_INFO RespInfo;
BOOL fResult;
#ifdef DBG
DWORD dwError;
#endif // DBG
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)",
this, pRegisteredPort, fRemote);
DNASSERT(pRegisteredPort != NULL);
pDevice = pRegisteredPort->GetOwningDevice();
DNASSERT(pDevice != NULL);
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED);
DNASSERT(pDevice->GetPASTSocket() != INVALID_SOCKET);
//
// Create a SOCKADDR to address the PAST service, and get the appropriate
// client ID, initial retry timeout, and next message ID to use.
//
ZeroMemory(&saddrinServerAddress, sizeof(saddrinServerAddress));
saddrinServerAddress.sin_family = AF_INET;
saddrinServerAddress.sin_port = HTONS(PAST_HOST_PORT);
if (fRemote)
{
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetRemotePASTServerAddressV4();
dwMsgID = pDevice->GetNextRemotePASTMsgID();
ptuRetry = pDevice->GetRemotePASTRetryTimeoutPtr();
}
else
{
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
dwMsgID = pDevice->GetNextLocalPASTMsgID();
ptuRetry = pDevice->GetLocalPASTRetryTimeoutPtr();
}
dwClientID = pDevice->GetPASTClientID(fRemote);
DNASSERT(dwClientID != 0);
//
// Build the request message.
//
ZeroMemory(&ExtendReq, sizeof(ExtendReq));
ExtendReq.version = PAST_VERSION;
ExtendReq.command = PAST_MSGID_EXTEND_REQUEST;
ExtendReq.clientid.code = PAST_PARAMID_CLIENTID;
ExtendReq.clientid.len = sizeof(ExtendReq.clientid) - sizeof(PAST_PARAM);
ExtendReq.clientid.clientid = dwClientID;
ExtendReq.bindid.code = PAST_PARAMID_BINDID;
ExtendReq.bindid.len = sizeof(DWORD);
ExtendReq.bindid.bindid = pRegisteredPort->GetPASTBindID(fRemote);
//
// Lease code, ask for what the user wants, but they shouldn't count on
// getting that.
// Convert the request (in milliseconds) to PAST's time unit (in seconds).
//
ExtendReq.lease.code = PAST_PARAMID_LEASE;
ExtendReq.lease.len = sizeof(ExtendReq.lease) - sizeof(PAST_PARAM);
ExtendReq.lease.leasetime = pRegisteredPort->GetRequestedLeaseTime() / 1000;
ExtendReq.msgid.code = PAST_PARAMID_MESSAGEID;
ExtendReq.msgid.len = sizeof(ExtendReq.msgid) - sizeof(PAST_PARAM);
ExtendReq.msgid.msgid = dwMsgID;
//
// Send the message and get the reply.
//
hr = this->ExchangeAndParsePAST(pDevice->GetPASTSocket(),
(SOCKADDR*) (&saddrinServerAddress),
sizeof(saddrinServerAddress),
(char *) &ExtendReq,
sizeof(ExtendReq),
dwMsgID,
ptuRetry,
&RespInfo);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Sending port lease extension request to server failed!");
goto Failure;
}
if (RespInfo.cMsgType != PAST_MSGID_EXTEND_RESPONSE)
{
DPFX(DPFPREP, 0, "Got unexpected response type %u, failed port lease extension!", RespInfo.cMsgType);
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Note the lease time extension (which is in seconds). It had better be
// longer than our auto-extension rate. We use it to extend the current
// lease expiration (which is in milliseconds).
//
DNASSERT((RespInfo.dwLeaseTime * 1000) > LEASE_RENEW_TIME);
pRegisteredPort->SetPASTLeaseExpiration((timeGetTime() + (RespInfo.dwLeaseTime * 1000)),
fRemote);
DPFX(DPFPREP, 8, "Extended lease (registered port = 0x%p) to %u seconds from now, expires at %u.",
pRegisteredPort, RespInfo.dwLeaseTime,
pRegisteredPort->GetPASTLeaseExpiration(fRemote));
//
// Remember this expiration time if it's the one that's going to expire
// soonest.
//
DNASSERT(this->m_dwNumLeases > 0);
if ((int) (pRegisteredPort->GetPASTLeaseExpiration(fRemote) - this->m_dwEarliestLeaseExpirationTime) < 0)
{
DPFX(DPFPREP, 1, "Registered port 0x%p's %s PAST lease expires at %u which is earlier than the next earliest lease expiration (%u).",
pRegisteredPort,
((fRemote) ? _T("remote") : _T("local")),
pRegisteredPort->GetPASTLeaseExpiration(fRemote),
this->m_dwEarliestLeaseExpirationTime);
this->m_dwEarliestLeaseExpirationTime = pRegisteredPort->GetPASTLeaseExpiration(fRemote);
//
// Ping the event if there is one so that the user's GetCaps interval
// doesn't miss this new, shorter lease.
//
if (this->m_hAlertEvent != NULL)
{
fResult = SetEvent(this->m_hAlertEvent);
#ifdef DBG
if (! fResult)
{
dwError = GetLastError();
DPFX(DPFPREP, 0, "Couldn't set alert event 0x%p! err = %u",
this->m_hAlertEvent, dwError);
//
// Ignore failure...
//
}
#endif // DBG
}
//
// Ping the I/O completion port if there is one so that the user's
// GetCaps interval doesn't miss this new, shorter lease.
//
if (this->m_hAlertIOCompletionPort != NULL)
{
fResult = PostQueuedCompletionStatus(this->m_hAlertIOCompletionPort,
0,
this->m_dwAlertCompletionKey,
NULL);
#ifdef DBG
if (! fResult)
{
dwError = GetLastError();
DPFX(DPFPREP, 0, "Couldn't queue key %u on alert IO completion port 0x%p! err = %u",
this->m_dwAlertCompletionKey,
this->m_hAlertIOCompletionPort,
dwError);
//
// Ignore failure...
//
}
#endif // DBG
}
}
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::ExtendPASTLease
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::UpdatePASTPublicAddressValidity"
//=============================================================================
// CNATHelpPAST::UpdatePASTPublicAddressValidity
//-----------------------------------------------------------------------------
//
// Description: Checks to see if the given device's PAST server is handing
// out valid public addresses or not.
//
// The PAST server information might be cleared if a failure
// occurs.
//
// The object lock is assumed to be held.
//
// Arguments:
// CDevice * pDevice - Pointer to device that should be checked.
// BOOL fRemote - Whether the local or remote PAST server should be
// checked.
//
// Returns: HRESULT
// DPNH_OK - The update was successful.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::UpdatePASTPublicAddressValidity(CDevice * const pDevice,
const BOOL fRemote)
{
HRESULT hr;
SOCKADDR_IN saddrinTemp;
CRegisteredPort * pTempRegisteredPort = NULL;
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)",
this, pDevice, fRemote);
//
// Create a temporary port mapping object. We mark it as "local PAST port
// unavailable" when checking remove PAST servers because we have not and
// will not attempt to map the local port when there is a local PAST
// server. Since AssignOrListenPASTPort assumes the local port will have
// been mapped first, we need to prevent it from trying to use that address
// and crashing.
//
pTempRegisteredPort = new CRegisteredPort(FAKE_PORT_LEASE_TIME,
((fRemote) ? REGPORTOBJ_PORTUNAVAILABLE_PAST_LOCAL : 0));
if (pTempRegisteredPort == NULL)
{
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
pTempRegisteredPort->MakeDeviceOwner(pDevice);
//
// Pick an arbitrary port value to map. In this case, we should just use
// the socket we're using for Ioctl communication, since that port is
// guaranteed to be unique by WinSock (so no multi-PASTHelp-instance race
// conditions like if we had used a hardcoded temporary port).
// One difference: the Ioctl socket is bound to INADDR_ANY, but we'll use
// the specific device address so our ICS vs. PFW-only address comparison
// below works correctly. This shouldn't make a difference, since a port
// in use by INADDR_ANY should be the same as the same port in use on each
// of the adapters.
//
ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
saddrinTemp.sin_family = AF_INET;
saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
saddrinTemp.sin_port = this->m_wIoctlSocketPort;
//
// Assign it the communication socket's address.
//
hr = pTempRegisteredPort->SetPrivateAddresses(&saddrinTemp, 1);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't set temporary private addresses!");
goto Failure;
}
DPFX(DPFPREP, 7, "Temporarily assigning port %u.%u.%u.%u:%u to check server status.",
saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
NTOHS(this->m_wIoctlSocketPort));
//
// Attempt to map it. AssignOrListenPASTPort will handle the actual logic
// of changing addresses.
//
hr = this->AssignOrListenPASTPort(pTempRegisteredPort, fRemote);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't assign temporary port mapping with server (0x%lx)! Ignoring.", hr);
//
// We'll treat this as non-fatal, but we'll dump the server.
//
this->ClearDevicesPASTServer(pDevice, fRemote);
hr = DPNH_OK;
}
else
{
//
// If we're checking the local server, determine whether it's a local ICS
// server or it's Personal Firewall only.
//
if (! fRemote)
{
//
// If public address is the same, it's PFW only, if it's different,
// it's ICS (although that could be PFWed, too). If there's no
// public address, then we can't really tell. It should be safe to
// assume that it's ICS, since this device must have an address or
// else it wouldn't exist.
//
if ((! pDevice->IsPASTPublicAddressAvailable(FALSE)) ||
(pTempRegisteredPort->IsFirstPASTPublicV4AddressDifferent(saddrinTemp.sin_addr.S_un.S_addr, FALSE)))
{
#ifdef DBG
if (! pDevice->IsPASTPublicAddressAvailable(FALSE))
{
DPFX(DPFPREP, 2, "Device %u.%u.%u.%u (object = 0x%p) doesn't have public address, assuming ICS.",
saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
pDevice);
}
else
{
SOCKADDR_IN * pasaddrinPublic;
pasaddrinPublic = pTempRegisteredPort->GetPASTPublicAddressesArray(FALSE);
DPFX(DPFPREP, 2, "Device %u.%u.%u.%u (object = 0x%p) has different public address (%u.%u.%u.%u), appears to be ICS.",
saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
pDevice,
pasaddrinPublic[0].sin_addr.S_un.S_un_b.s_b1,
pasaddrinPublic[0].sin_addr.S_un.S_un_b.s_b2,
pasaddrinPublic[0].sin_addr.S_un.S_un_b.s_b3,
pasaddrinPublic[0].sin_addr.S_un.S_un_b.s_b4);
}
#endif // DBG
pDevice->NoteLocalPASTServerIsICS();
}
else
{
DPFX(DPFPREP, 2, "Device %u.%u.%u.%u (object = 0x%p) has same public address, appears to be firewall only.",
saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
pDevice);
pDevice->NoteLocalPASTServerIsPFWOnly();
}
}
else
{
//
// A remote PAST server.
//
}
//
// Free the temporary port, because we don't need it.
//
hr = this->FreePASTPort(pTempRegisteredPort, fRemote);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't free temporary port mapping (0x%lx)!", hr);
//
// We'll treat this as non-fatal, but we have to drop the PAST
// server.
//
this->ClearDevicesPASTServer(pDevice, fRemote);
hr = DPNH_OK;
}
}
//
// If we're here we either never mapped the temporary port, explicitly
// freed the port, or deregistered (which implies freeing the port). So
// we're done with the object.
//
pTempRegisteredPort->ClearDeviceOwner();
pTempRegisteredPort->ClearPrivateAddresses();
delete pTempRegisteredPort;
pTempRegisteredPort = NULL;
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
if (pTempRegisteredPort != NULL)
{
delete pTempRegisteredPort;
}
goto Exit;
} // CNATHelpPAST::UpdatePASTPublicAddressValidity
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::RegisterAllPortsWithPAST"
//=============================================================================
// CNATHelpPAST::RegisterAllPortsWithPAST
//-----------------------------------------------------------------------------
//
// Description: Registers all ports owned by the given device with the PAST
// server.
//
// The object lock is assumed to be held.
//
// Arguments:
// CDevice * pDevice - Pointer to device whose ports should be registered.
// BOOL fRemote - TRUE if registering on remote server, FALSE if
// registering on local server.
//
// Returns: HRESULT
// DPNH_OK - The extension was successful.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::RegisterAllPortsWithPAST(CDevice * const pDevice,
const BOOL fRemote)
{
HRESULT hr = DPNH_OK;
CBilink * pBilink;
CRegisteredPort * pRegisteredPort;
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)",
this, pDevice, fRemote);
DNASSERT(pDevice != NULL);
DNASSERT(pDevice->GetPASTSocket() != INVALID_SOCKET);
pBilink = pDevice->m_blOwnedRegPorts.GetNext();
while (pBilink != &pDevice->m_blOwnedRegPorts)
{
pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
DNASSERT(pRegisteredPort->GetPASTBindID(fRemote) == 0);
hr = this->AssignOrListenPASTPort(pRegisteredPort, fRemote);
if (hr != DPNH_OK)
{
if (hr != DPNHERR_PORTUNAVAILABLE)
{
DPFX(DPFPREP, 0, "Couldn't assign port mapping with server (0x%lx)! Ignoring.", hr);
//
// We'll treat this as non-fatal, but we have to dump the
// server and bail out of here.
//
this->ClearDevicesPASTServer(pDevice, fRemote);
hr = DPNH_OK;
goto Exit;
}
//
// Uh oh, the server couldn't register one of the ports! We don't
// have a way of directly informing the user now. We'll have to
// mark the port and continue.
//
DPFX(DPFPREP, 1, "Server indicated that the port was unavailable.");
pRegisteredPort->NotePASTPortUnavailable(fRemote);
hr = DPNH_OK;
}
else
{
//
// Hey, this now has an address.
//
DPFX(DPFPREP, 2, "Previously registered port 0x%p now has a %s PAST server public address.",
pRegisteredPort, ((fRemote) ? _T("remote") : _T("local")));
this->m_dwFlags |= NATHELPPASTOBJ_ADDRESSESCHANGED;
}
pBilink = pBilink->GetNext();
}
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
} // CNATHelpPAST::RegisterAllPortsWithPAST
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::RegisterPreviouslyUnownedPortsWithDevice"
//=============================================================================
// CNATHelpPAST::RegisterPreviouslyUnownedPortsWithDevice
//-----------------------------------------------------------------------------
//
// Description: Associates unknown ports with the given device, and
// registers them with the device's PAST server(s).
//
// If fWildcardToo is FALSE, only previously unowned ports that
// match the device's address are associated. If TRUE, unowned
// INADDR_ANY ports are associated as well.
//
// The object lock is assumed to be held.
//
// Arguments:
// CDevice * pDevice - Pointer to device to take ownership of ports.
// BOOL fAll - Whether all ports should be associated.
//
// Returns: HRESULT
// DPNH_OK - The extension was successful.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::RegisterPreviouslyUnownedPortsWithDevice(CDevice * const pDevice,
const BOOL fWildcardToo)
{
HRESULT hr = DPNH_OK;
CBilink * pBilink;
CRegisteredPort * pRegisteredPort;
SOCKADDR_IN * pasaddrinPrivate;
#ifdef DBG
BOOL fAssignedPort = FALSE;
IN_ADDR inaddrTemp;
#endif // DBG
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)",
this, pDevice, fWildcardToo);
//
// Loop through all unowned ports, assign them to the device if
// appropriate, then register them.
//
pBilink = this->m_blUnownedPorts.GetNext();
while (pBilink != &this->m_blUnownedPorts)
{
pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
pBilink = pBilink->GetNext();
//
// The registered port must match the device's address in order to
// associate them. If wildcards are allowed, then INADDR_ANY
// registrations can be associated, too.
//
//
// All addresses should be same (if there are more than one), so just
// compare the first one in the array.
//
pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
if (pasaddrinPrivate[0].sin_addr.S_un.S_addr != pDevice->GetLocalAddressV4())
{
if (pasaddrinPrivate[0].sin_addr.S_un.S_addr != INADDR_ANY)
{
DPFX(DPFPREP, 7, "Unowned registered port 0x%p private address %u.%u.%u.%u doesn't match device 0x%p's, skipping.",
pRegisteredPort,
pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b1,
pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b2,
pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b3,
pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b4,
pDevice);
continue;
}
#ifdef DBG
inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
#endif // DBG
if (! fWildcardToo)
{
#ifdef DBG
DPFX(DPFPREP, 7, "Unowned registered port 0x%p (INADDR_ANY) not allowed to be associated with device 0x%p (address %u.%u.%u.%u), skipping.",
pRegisteredPort,
pDevice,
inaddrTemp.S_un.S_un_b.s_b1,
inaddrTemp.S_un.S_un_b.s_b2,
inaddrTemp.S_un.S_un_b.s_b3,
inaddrTemp.S_un.S_un_b.s_b4);
#endif // DBG
continue;
}
#ifdef DBG
DPFX(DPFPREP, 7, "Unowned registered port 0x%p (INADDR_ANY) becoming associated with device 0x%p (address %u.%u.%u.%u).",
pRegisteredPort,
pDevice,
inaddrTemp.S_un.S_un_b.s_b1,
inaddrTemp.S_un.S_un_b.s_b2,
inaddrTemp.S_un.S_un_b.s_b3,
inaddrTemp.S_un.S_un_b.s_b4);
#endif // DBG
}
else
{
DPFX(DPFPREP, 7, "Unowned registered port 0x%p private address %u.%u.%u.%u matches device 0x%p's, associating.",
pRegisteredPort,
pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b1,
pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b2,
pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b3,
pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b4,
pDevice);
//
// The way it's currently implemented, all non-wildcard ports
// should be registered before we even try to register the wildcard
// ones.
//
DNASSERT(! fWildcardToo);
}
//
// If we made it here, we can associate the port with the device.
//
pRegisteredPort->m_blDeviceList.RemoveFromList();
pRegisteredPort->MakeDeviceOwner(pDevice);
//
// Attempt to automatically map it with the (new) local server, if
// present.
//
if (pDevice->GetPASTClientID(FALSE) != 0)
{
hr = this->AssignOrListenPASTPort(pRegisteredPort, FALSE);
if (hr != DPNH_OK)
{
if (hr != DPNHERR_PORTUNAVAILABLE)
{
DPFX(DPFPREP, 0, "Couldn't assign port mapping 0x%p with local server (0x%lx)! Ignoring.",
pRegisteredPort, hr);
//
// We'll treat this as non-fatal, but we have to dump the
// server.
//
this->ClearDevicesPASTServer(pDevice, FALSE);
}
else
{
//
// Uh oh, the server couldn't register one of the ports!
// We don't have a way of directly informing the user now.
// We'll have to mark the port and continue.
//
DPFX(DPFPREP, 1, "Local PAST server indicated that the port (0x%p) was unavailable.",
pRegisteredPort);
pRegisteredPort->NotePASTPortUnavailable(FALSE);
}
hr = DPNH_OK;
}
else
{
//
// Ta da, user needs to note address mapping changes.
//
DPFX(DPFPREP, 1, "Previously unowned port 0x%p now bound to device object 0x%p and mapped locally.",
pRegisteredPort, pDevice);
this->m_dwFlags |= NATHELPPASTOBJ_ADDRESSESCHANGED;
#ifdef DBG
fAssignedPort = TRUE;
#endif // DBG
}
}
//
// Attempt to automatically map it with the (new) remote PAST server,
// if present.
//
if (pDevice->GetPASTClientID(TRUE) != 0)
{
hr = this->AssignOrListenPASTPort(pRegisteredPort, TRUE);
if (hr != DPNH_OK)
{
if (hr != DPNHERR_PORTUNAVAILABLE)
{
DPFX(DPFPREP, 0, "Couldn't assign port mapping 0x%p with remote PAST server (0x%lx)! Ignoring.",
pRegisteredPort, hr);
//
// We'll treat this as non-fatal, but we have to dump the
// server.
//
this->ClearDevicesPASTServer(pDevice, TRUE);
}
else
{
//
// Uh oh, the server couldn't register one of the ports!
// We don't have a way of directly informing the user now.
// We'll have to mark the port and continue.
//
DPFX(DPFPREP, 1, "Remote PAST server indicated that the port (0x%p) was unavailable.",
pRegisteredPort);
pRegisteredPort->NotePASTPortUnavailable(TRUE);
}
hr = DPNH_OK;
}
else
{
//
// Ta da, user needs to note address mapping changes.
//
DPFX(DPFPREP, 1, "Previously unowned port 0x%p now bound to device object 0x%p and mapped remotely.",
pRegisteredPort, pDevice);
this->m_dwFlags |= NATHELPPASTOBJ_ADDRESSESCHANGED;
#ifdef DBG
fAssignedPort = TRUE;
#endif // DBG
}
}
}
#ifdef DBG
if (! fAssignedPort)
{
DPFX(DPFPREP, 1, "No unowned ports were bound to device object 0x%p.",
pDevice);
}
#endif // DBG
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
} // CNATHelpPAST::RegisterPreviouslyUnownedPortsWithDevice
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::ExchangeAndParsePAST"
//=============================================================================
// CNATHelpPAST::ExchangeAndParsePAST
//-----------------------------------------------------------------------------
//
// Description: Sends a message to the PAST server and waits for a response,
// which is parsed into a PAST_RESPONSE_INFO buffer. The message
// will be continually retried at the current retry interval
// unless ptuRetry == NULL, which indicates that this is the
// initial message looking for a server, in which case it will be
// tried twice over a second. If there is no response,
// DPNHERR_SERVERNOTRESPONDING is returned.
//
// Since there is almost no scenario where we don't immediately
// need to know the response, there is no point in doing this
// asynchronously. The assumption is that a PAST server is
// sufficiently local that long retries are not necessary.
//
// Arguments:
// SOCKET sSocket - Socket to use when sending.
// SOCKADDR * psaddrServerAddress - Pointer to address of server.
// int iAddressesSize - Size of psaddrServerAddress and
// psaddrRecvAddress.
// char * pcRequestBuffer - Pointer to request message to send.
// int iRequestBufferSize - Size of request message.
// DWORD dwMsgID - ID of the message being sent (to
// correlate responses).
// DWORD * ptuRetry - Pointer to current retry time value
// (may be modified), or NULL if this is
// an initial registration (a different
// timer strategy is used).
// PAST_RESPONSE_INFO * pRespInfo - Pointer to structure that will be filled
// in with values from the server's
// response message.
//
// Returns: HRESULT
// DPNH_OK - The request and response were
// successfully exchanged.
// DPNHERR_GENERIC - An error occurred.
// DPNHERR_SERVERNOTRESPONDING - The server did not respond to the message.
//=============================================================================
HRESULT CNATHelpPAST::ExchangeAndParsePAST(const SOCKET sSocket,
const SOCKADDR * const psaddrServerAddress,
const int iAddressesSize,
const char * const pcRequestBuffer,
const int iRequestBufferSize,
const DWORD dwMsgID,
DWORD * const ptuRetry,
PAST_RESPONSE_INFO * const pRespInfo)
{
HRESULT hr = DPNH_OK;
char acRespBuffer[PAST_RESPONSE_BUFFER_SIZE];
timeval tv;
FD_SET fdsRead;
DWORD dwTriesRemaining;
int iReturn;
SOCKADDR_IN saddrinReceive;
int iRecvAddressSize = iAddressesSize;
DWORD dwError;
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, %i, 0x%p, %i, %u, 0x%p, 0x%p)",
this, sSocket, psaddrServerAddress, iAddressesSize, pcRequestBuffer,
iRequestBufferSize, dwMsgID, ptuRetry, pRespInfo);
DNASSERT(sSocket != INVALID_SOCKET);
DNASSERT(psaddrServerAddress != NULL);
DNASSERT(iAddressesSize == sizeof(SOCKADDR_IN));
DNASSERT(pcRequestBuffer != NULL);
DNASSERT(iRequestBufferSize != 0);
DNASSERT(pRespInfo != NULL);
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED);
ZeroMemory(pRespInfo, sizeof(*pRespInfo));
#ifdef DBG
ZeroMemory(acRespBuffer, sizeof(acRespBuffer));
#endif // DBG
if (ptuRetry == NULL)
{
//
// On initial registration requests we try twice with a 250ms total
// timeout.
//
tv.tv_usec = PAST_CONNECT_RETRY_INTERVAL_US;
tv.tv_sec = 0;
dwTriesRemaining = MAX_NUM_PAST_TRIES_CONNECT;
}
else
{
//
// All normal traffic uses the current retry interval and a default
// number of tries.
//
tv.tv_usec = (*ptuRetry);
tv.tv_sec = 0;
dwTriesRemaining = MAX_NUM_PAST_TRIES;
}
FD_ZERO(&fdsRead);
FD_SET(sSocket, &fdsRead);
//
// First clear out any extraneous responses from previous communication.
//
while (this->m_pfnselect(0, &fdsRead, NULL, NULL, &s_tv0) != 0)
{
iReturn = this->m_pfnrecvfrom(sSocket,
acRespBuffer,
sizeof(acRespBuffer),
0,
(SOCKADDR*) (&saddrinReceive),
&iRecvAddressSize);
if ((iReturn == 0) || (iReturn == SOCKET_ERROR))
{
dwError = this->m_pfnWSAGetLastError();
//
// WSAENOBUFS means WinSock is out of memory.
//
if (dwError == WSAENOBUFS)
{
DPFX(DPFPREP, 0, "WinSock returned WSAENOBUFS while clearing incoming queue!");
hr = DPNHERR_OUTOFMEMORY;
goto Failure;
}
//
// Ignore WSAECONNRESET, that's just WinSock's way of telling us
// that the target actively refused a previous message we tried to
// send. Since we don't know which send WinSock means, we can't do
// a whole lot about it.
//
if (dwError != WSAECONNRESET)
{
DPFX(DPFPREP, 0, "Got sockets error %u trying to receive (clearing incoming queue)!", dwError);
hr = DPNHERR_GENERIC;
goto Failure;
}
DPFX(DPFPREP, 2, "Ignoring CONNRESET while clearing incoming queue.");
}
else
{
//
// If we got here, that means there's a message.
//
#ifdef DBG
DPFX(DPFPREP, 2, "Found extra response from previous PAST request (sent by %u.%u.%u.%u:%u), parsing for fun.",
saddrinReceive.sin_addr.S_un.S_un_b.s_b1,
saddrinReceive.sin_addr.S_un.S_un_b.s_b2,
saddrinReceive.sin_addr.S_un.S_un_b.s_b3,
saddrinReceive.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinReceive.sin_port));
//
// For grins, let's see what this message is. Ignore errors.
//
this->ParsePASTMessage(acRespBuffer, iReturn, pRespInfo);
ZeroMemory(pRespInfo, sizeof(*pRespInfo));
#endif // DBG
if (ptuRetry != NULL)
{
//
// Don't re-try so quickly, since responses to our retries are
// lagging.
//
if ((*ptuRetry) < MAX_PAST_RETRY_TIME_US)
{
(*ptuRetry) *= 2;
DPFX(DPFPREP, 8, "Backing initial retry timer off to %u usec.",
(*ptuRetry));
}
}
}
DNASSERT(iRecvAddressSize == iAddressesSize);
FD_ZERO(&fdsRead);
FD_SET(sSocket, &fdsRead);
}
//
// Now do the exchange, get a response to the request (does retries too).
//
do
{
DPFX(DPFPREP, 7, "Sending PAST request type %u (%i bytes, msg id = %u) to server (%u.%u.%u.%u:%u).",
((PPAST_MSG) pcRequestBuffer)->msgtype, iRequestBufferSize, dwMsgID,
((SOCKADDR_IN*) psaddrServerAddress)->sin_addr.S_un.S_un_b.s_b1,
((SOCKADDR_IN*) psaddrServerAddress)->sin_addr.S_un.S_un_b.s_b2,
((SOCKADDR_IN*) psaddrServerAddress)->sin_addr.S_un.S_un_b.s_b3,
((SOCKADDR_IN*) psaddrServerAddress)->sin_addr.S_un.S_un_b.s_b4,
NTOHS(((SOCKADDR_IN*) psaddrServerAddress)->sin_port));
//
// First, send off the request.
//
iReturn = this->m_pfnsendto(sSocket,
pcRequestBuffer,
iRequestBufferSize,
0,
psaddrServerAddress,
iAddressesSize);
if (iReturn == SOCKET_ERROR)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Got sockets error %u when sending to PAST gateway!", dwError);
#endif // DBG
//
// It's possible that we caught WinSock at a bad time,
// particularly with WSAEADDRNOTAVAIL (10049), which seems to
// occur if the address is going away (and we haven't detected
// it in CheckForNewDevices yet).
//
// Break out of the receive loop, which should result in the
// DPNHERR_SERVERNOTRESPONDING error.
//
break;
}
if (iReturn != iRequestBufferSize)
{
DPFX(DPFPREP, 0, "Didn't send entire datagram (%i != %i)?!", iReturn, iRequestBufferSize);
DNASSERT(FALSE);
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Now see if we get a response.
//
Wait:
FD_ZERO(&fdsRead);
FD_SET(sSocket, &fdsRead);
iReturn = this->m_pfnselect(0, &fdsRead, NULL, NULL, &tv);
if (iReturn == SOCKET_ERROR)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Got sockets error %u trying to select on PAST socket!", dwError);
#endif // DBG
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// If our socket was signalled, there's data to read.
//
//if (FD_ISSET(sSocket, &fdsRead))
if (this->m_pfn__WSAFDIsSet(sSocket, &fdsRead))
{
iReturn = this->m_pfnrecvfrom(sSocket,
acRespBuffer,
sizeof(acRespBuffer),
0,
(SOCKADDR*) (&saddrinReceive),
&iRecvAddressSize);
if ((iReturn == 0) || (iReturn == SOCKET_ERROR))
{
dwError = this->m_pfnWSAGetLastError();
//
// If we get WSAECONNRESET here, we can probably assume that
// it's because of the message we just sent. That means that
// there's no PAST server on the gateway.
//
if (dwError == WSAECONNRESET)
{
if (ptuRetry == NULL)
{
DPFX(DPFPREP, 2, "Got CONNRESET while waiting for initial registration response, assuming no PAST server.");
}
else
{
DPFX(DPFPREP, 0, "Got CONNRESET while waiting for PAST server response!");
}
//
// Break out of the loop, it should result in the
// DPNHERR_SERVERNOTRESPONDING error code.
//
break;
}
//
// WSAENOBUFS means WinSock is out of memory.
//
if (dwError == WSAENOBUFS)
{
DPFX(DPFPREP, 0, "WinSock returned WSAENOBUFS when waiting for response!");
hr = DPNHERR_OUTOFMEMORY;
}
else
{
DPFX(DPFPREP, 0, "Got sockets error %u trying to receive (waiting for response)!", dwError);
hr = DPNHERR_GENERIC;
}
goto Failure;
}
//
// We got some data.
//
DNASSERT(iRecvAddressSize == iAddressesSize);
hr = this->ParsePASTMessage(acRespBuffer, iReturn, pRespInfo);
if (hr != DPNH_OK)
{
//
// We couldn't handle the response, try again (without touching
// the remaining tries or backoff timer).
//
DPFX(DPFPREP, 0, "Failed parsing message from %u.%u.%u.%u:%u (err = %lx), ignoring.",
saddrinReceive.sin_addr.S_un.S_un_b.s_b1,
saddrinReceive.sin_addr.S_un.S_un_b.s_b2,
saddrinReceive.sin_addr.S_un.S_un_b.s_b3,
saddrinReceive.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinReceive.sin_port),
hr);
goto Wait;
}
if (saddrinReceive.sin_addr.S_un.S_addr != ((SOCKADDR_IN*) psaddrServerAddress)->sin_addr.S_un.S_addr)
{
//
// This message is not from the server to which we sent the
// request. Ignore it.
//
DPFX(DPFPREP, 0, "Got message from unexpected source (%u.%u.%u.%u:%u), ignoring %i byte message.",
saddrinReceive.sin_addr.S_un.S_un_b.s_b1,
saddrinReceive.sin_addr.S_un.S_un_b.s_b2,
saddrinReceive.sin_addr.S_un.S_un_b.s_b3,
saddrinReceive.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinReceive.sin_port),
iReturn);
goto Wait;
}
if (pRespInfo->dwMsgID != dwMsgID)
{
//
// We got a response to a different message, try again
// (without touching the remaining tries or backoff timer).
//
DPFX(DPFPREP, 0, "Got messageid %u, expecting messageid %u, ignoring %i byte message.",
pRespInfo->dwMsgID, dwMsgID, iReturn);
goto Wait;
}
//
// If we got here, then it looks like we got the response we want.
// We're done.
//
DPFX(DPFPREP, 8, "Received expected messageid %u from %u.%u.%u.%u:%u, it's %i bytes.",
pRespInfo->dwMsgID,
saddrinReceive.sin_addr.S_un.S_un_b.s_b1,
saddrinReceive.sin_addr.S_un.S_un_b.s_b2,
saddrinReceive.sin_addr.S_un.S_un_b.s_b3,
saddrinReceive.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinReceive.sin_port),
iReturn);
goto Exit;
}
//
// The initial registration attempt is a special case since we don't
// know if the server exists or not. Once we're registered, we expect
// the server to be available, so we'll retry more times and
// temporarily back off further each time.
//
if (ptuRetry != NULL)
{
tv.tv_usec *= 2; // exponential backoff.
DPFX(DPFPREP, 7, "Didn't get response, increasing temporary timeout value to %u.", tv.tv_usec);
}
dwTriesRemaining--;
}
while (dwTriesRemaining > 0);
//
// If we got here, it means we didn't get a response.
//
hr = DPNHERR_SERVERNOTRESPONDING;
//goto Failure;
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::ExchangeAndParsePAST
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::RegisterMultipleDevicesWithRemotePAST"
//=============================================================================
// CNATHelpPAST::RegisterMultipleDevicesWithRemotePAST
//-----------------------------------------------------------------------------
//
// Description: Sends a registration message to the remote PAST server for
// all the devices in the temporary pSourceList simultaneously
// and waits for replies. A pointer to the first device that
// successfully registers with a remote PAST server will be placed
// in ppFirstDeviceWithRemoteServer. All devices will be removed
// removed from pSourceList.
//
// The object lock is assumed to be held.
//
// Arguments:
// CBilink * pSourceList - Temporary bilink with all
// devices to check. Will be
// emptied.
// CDevice ** ppFirstDeviceWithRemoteServer - Place to store first device
// which successfully
// registers with a remote
// PAST server
//
// Returns: HRESULT
// DPNH_OK - The devices were successfully handled.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::RegisterMultipleDevicesWithRemotePAST(CBilink * pSourceList,
CDevice ** ppFirstDeviceWithRemoteServer)
{
HRESULT hr;
CBilink * pBilink;
CDevice * pDevice;
CBilink * pBilinkSameGateway;
CDevice * pDeviceSameGateway;
char acRespBuffer[PAST_RESPONSE_BUFFER_SIZE];
timeval tv;
FD_SET fdsRead;
int iReturn;
PAST_MSG_REGISTER RegisterReq;
SOCKADDR_IN saddrinServerAddress;
int iRecvAddressSize = sizeof(SOCKADDR_IN);
PAST_RESPONSE_INFO RespInfo;
DWORD dwError;
DWORD dwTry;
DWORD dwFinishTime;
DWORD dwTimeRemaining;
SOCKADDR_IN saddrinReceive;
DPFX(DPFPREP, 5, "(0x%p) Parameters (0x%p, 0x%p)",
this, pSourceList, ppFirstDeviceWithRemoteServer);
DNASSERT(this->m_dwFlags & NATHELPPASTOBJ_INITIALIZED);
#ifdef DBG
ZeroMemory(acRespBuffer, sizeof(acRespBuffer));
#endif // DBG
//
// Loop through each device.
//
pBilink = pSourceList->GetNext();
while (pBilink != pSourceList)
{
pDevice = DEVICE_FROM_TEMP_BILINK(pBilink);
pBilink = pBilink->GetNext();
DNASSERT(pDevice->GetPASTClientID(TRUE) == 0);
//
// Create the temporary set.
//
FD_ZERO(&fdsRead);
FD_SET(pDevice->GetPASTSocket(), &fdsRead);
//
// Try to get the device's gateway's address. This might return FALSE
// if the device does not have a gateway. In that case, we will ignore
// the device. Otherwise the address should be filled in with the
// gateway or broadcast address.
//
if (! this->GetAddressToReachGateway(pDevice,
&saddrinServerAddress.sin_addr))
{
DPFX(DPFPREP, 2, "Device 0x%p should not attempt to reach a gateway.",
pDevice);
//
// Take it out of the list, but continue.
//
pDevice->m_blTempList.RemoveFromList();
}
else
{
//
// Store the address with the device for use below.
//
pDevice->SetRemotePASTServerAddressV4(saddrinServerAddress.sin_addr.S_un.S_addr);
//
// Clear out any extraneous responses from previous communication.
//
while (this->m_pfnselect(0, &fdsRead, NULL, NULL, &s_tv0) != 0)
{
iReturn = this->m_pfnrecvfrom(pDevice->GetPASTSocket(),
acRespBuffer,
sizeof(acRespBuffer),
0,
(SOCKADDR*) (&saddrinReceive),
&iRecvAddressSize);
if ((iReturn == 0) || (iReturn == SOCKET_ERROR))
{
dwError = this->m_pfnWSAGetLastError();
//
// Ignore WSAECONNRESET, that's just WinSock's way of
// telling us that the target actively refused a previous
// message we tried to send. Since we don't know which
// send WinSock means, we can't do a whole lot about it.
//
if (dwError != WSAECONNRESET)
{
DPFX(DPFPREP, 0, "Got sockets error %u on device 0x%p trying to receive (clearing incoming queue)! Ignoring.",
dwError, pDevice);
//
// Take it out of the list, but continue.
//
pDevice->m_blTempList.RemoveFromList();
}
else
{
DPFX(DPFPREP, 2, "Ignoring CONNRESET on device 0x%p while clearing incoming queue.",
pDevice);
}
}
else
{
//
// If we got here, that means there's a message.
//
#ifdef DBG
DPFX(DPFPREP, 2, "Found extra response from previous PAST request on device 0x%p (sent by %u.%u.%u.%u:%u), parsing for fun.",
pDevice,
saddrinReceive.sin_addr.S_un.S_un_b.s_b1,
saddrinReceive.sin_addr.S_un.S_un_b.s_b2,
saddrinReceive.sin_addr.S_un.S_un_b.s_b3,
saddrinReceive.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinReceive.sin_port));
//
// For grins, let's see what this message is. Ignore
// errors.
//
ZeroMemory(&RespInfo, sizeof(RespInfo));
this->ParsePASTMessage(acRespBuffer, iReturn, &RespInfo);
#endif // DBG
}
DNASSERT(iRecvAddressSize == sizeof(saddrinServerAddress));
FD_ZERO(&fdsRead);
FD_SET(pDevice->GetPASTSocket(), &fdsRead);
}
} // end else (device can try to reach gateway)
}
//
// If there aren't any devices that aren't registered, we're done.
//
if (pSourceList->IsEmpty())
{
DPFX(DPFPREP, 8, "No devices remaining to be checked.");
hr = DPNH_OK;
goto Exit;
}
//
// Build the registration request message.
//
ZeroMemory(&RegisterReq, sizeof(RegisterReq));
RegisterReq.version = PAST_VERSION;
RegisterReq.command = PAST_MSGID_REGISTER_REQUEST;
RegisterReq.msgid.code = PAST_PARAMID_MESSAGEID;
RegisterReq.msgid.len = sizeof(RegisterReq.msgid) - sizeof(PAST_PARAM);
//RegisterReq.msgid.msgid = 0; // it always starts at 0 for registration
ZeroMemory(&saddrinServerAddress, sizeof(saddrinServerAddress));
saddrinServerAddress.sin_family = AF_INET;
//saddrinServerAddress.sin_addr = ? // filled in below
saddrinServerAddress.sin_port = HTONS(PAST_HOST_PORT);
//
// Now actually try to register with all the ports. Keep looping until
// either all devices are registered or we exceed the number of tries.
//
for(dwTry = 0; dwTry < MAX_NUM_PAST_TRIES_CONNECT; dwTry++)
{
//
// Send the message for all devices that aren't registered yet.
//
pBilink = pSourceList->GetNext();
while (pBilink != pSourceList)
{
pDevice = DEVICE_FROM_TEMP_BILINK(pBilink);
pBilink = pBilink->GetNext();
//
// Remember the current time, if this is the first thing we've sent
// from this port.
//
if (pDevice->GetFirstPASTDiscoveryTime() == 0)
{
pDevice->SetFirstPASTDiscoveryTime(GETTIMESTAMP());
}
//
// Retrieve the gateway address we detected above.
//
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetRemotePASTServerAddressV4();
//
// Try sending the registration message.
//
DPFX(DPFPREP, 7, "Device 0x%p sending PAST registration request (type %u, %i bytes) to server (%u.%u.%u.%u:%u).",
pDevice, RegisterReq.command, sizeof(RegisterReq),
saddrinServerAddress.sin_addr.S_un.S_un_b.s_b1,
saddrinServerAddress.sin_addr.S_un.S_un_b.s_b2,
saddrinServerAddress.sin_addr.S_un.S_un_b.s_b3,
saddrinServerAddress.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinServerAddress.sin_port));
iReturn = this->m_pfnsendto(pDevice->GetPASTSocket(),
(char*) (&RegisterReq),
sizeof(RegisterReq),
0,
(SOCKADDR*) (&saddrinServerAddress),
sizeof(saddrinServerAddress));
if (iReturn == SOCKET_ERROR)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Device 0x%p got sockets error %u when sending to PAST gateway!",
pDevice, dwError);
#endif // DBG
//
// It's possible that we caught WinSock at a bad time,
// particularly with WSAEADDRNOTAVAIL (10049), which seems to
// occur if the address is going away (and we haven't detected
// it in CheckForNewDevices yet).
//
// Ignore the error, we can survive. Take the out of the list,
// but continue.
//
pDevice->m_blTempList.RemoveFromList();
//
// If there aren't any more devices, we can bail.
//
if (pSourceList->IsEmpty())
{
DPFX(DPFPREP, 1, "Last device got sendto error, exiting gracefully.");
hr = DPNH_OK;
goto Exit;
}
}
else
{
if (iReturn != sizeof(RegisterReq))
{
DPFX(DPFPREP, 0, "Didn't send entire datagram (%i != %i)?!",
iReturn, sizeof(RegisterReq));
DNASSERT(FALSE);
hr = DPNHERR_GENERIC;
goto Failure;
}
}
}
//
// Remember how long to wait for replies to this send attempt.
//
dwFinishTime = timeGetTime() + PAST_CONNECT_RETRY_INTERVAL_MS;
dwTimeRemaining = PAST_CONNECT_RETRY_INTERVAL_MS;
//
// Keep looping until all devices are registered or we timeout.
//
do
{
//
// Rebuild the socket set.
//
FD_ZERO(&fdsRead);
pBilink = pSourceList->GetNext();
while (pBilink != pSourceList)
{
pDevice = DEVICE_FROM_TEMP_BILINK(pBilink);
DNASSERT(pDevice->GetPASTClientID(TRUE) == 0);
FD_SET(pDevice->GetPASTSocket(), &fdsRead);
//
// Move to next device
//
pBilink = pBilink->GetNext();
}
tv.tv_usec = dwTimeRemaining * 1000;
tv.tv_sec = 0;
//
// Wait for data to come in on any of the sockets.
//
iReturn = this->m_pfnselect(0, &fdsRead, NULL, NULL, &tv);
if (iReturn == SOCKET_ERROR)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Got sockets error %u trying to select on PAST sockets!", dwError);
#endif // DBG
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Stop looping if we timed out; it's time to repeat the message
// (or exit, if no more tries left).
//
if (iReturn == 0)
{
DPFX(DPFPREP, 7, "No more sockets have data, try = %u of %u.",
(dwTry + 1), MAX_NUM_PAST_TRIES_CONNECT);
break;
}
//
// If we're here, data came in on at least one of the sockets.
// Read it and parse it.
//
pBilink = pSourceList->GetNext();
while (pBilink != pSourceList)
{
pDevice = DEVICE_FROM_TEMP_BILINK(pBilink);
pBilink = pBilink->GetNext();
//
// If this device's socket is set there's data to read.
//
//if (FD_ISSET(pDevice->GetPASTSocket(), &fdsRead))
if (this->m_pfn__WSAFDIsSet(pDevice->GetPASTSocket(), &fdsRead))
{
iReturn = this->m_pfnrecvfrom(pDevice->GetPASTSocket(),
acRespBuffer,
sizeof(acRespBuffer),
0,
(SOCKADDR*) (&saddrinReceive),
&iRecvAddressSize);
if ((iReturn == 0) || (iReturn == SOCKET_ERROR))
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
//
// If we get WSAECONNRESET here, we can probably assume
// that it's because of the message we just sent. That
// means that there's no PAST server on the gateway.
//
if (dwError == WSAECONNRESET)
{
DPFX(DPFPREP, 2, "Got CONNRESET while waiting for registration response on device 0x%p, assuming no PAST server.",
pDevice);
}
else
{
DPFX(DPFPREP, 0, "Got sockets error %u trying to receive on device 0x%p (waiting for response)! Ignoring",
dwError);
}
#endif // DBG
//
// Take it out of the list, but continue.
//
pDevice->m_blTempList.RemoveFromList();
//
// We can also be smart and remove any other devices
// with the same gateway because they will have the
// same problem; WinSock might not even give another
// notification for the other sockets. Don't try this
// trick for the broadcast address, though.
//
if (pDevice->GetRemotePASTServerAddressV4() != INADDR_BROADCAST)
{
pBilinkSameGateway = pSourceList->GetNext();
while (pBilinkSameGateway != pSourceList)
{
pDeviceSameGateway = DEVICE_FROM_TEMP_BILINK(pBilinkSameGateway);
pBilinkSameGateway = pBilinkSameGateway->GetNext();
if (pDeviceSameGateway->GetRemotePASTServerAddressV4() == pDevice->GetRemotePASTServerAddressV4())
{
#ifdef DBG
saddrinServerAddress.sin_addr.S_un.S_addr = pDevice->GetRemotePASTServerAddressV4();
DPFX(DPFPREP, 3, "Removing device 0x%p, because it shares gateway %u.%u.%u.%u with device 0x%p.",
pDeviceSameGateway,
saddrinServerAddress.sin_addr.S_un.S_un_b.s_b1,
saddrinServerAddress.sin_addr.S_un.S_un_b.s_b2,
saddrinServerAddress.sin_addr.S_un.S_un_b.s_b3,
saddrinServerAddress.sin_addr.S_un.S_un_b.s_b4,
pDevice);
#endif // DBG
//
// Before we remove this similar device,
// make sure we don't screw up the list of
// remaining devices. Otherwise, that
// outer list traversal may get caught in
// an infinite loop.
//
if ((&pDeviceSameGateway->m_blTempList) == pBilink)
{
pBilink = pBilink->GetNext();
}
pDeviceSameGateway->m_blTempList.RemoveFromList();
}
}
}
else
{
//
// Sent to broadcast address, can't optimize.
//
}
//
// If there aren't any more devices, we can bail.
//
if (pSourceList->IsEmpty())
{
DPFX(DPFPREP, 1, "Last device got recvfrom error, exiting gracefully.");
hr = DPNH_OK;
goto Exit;
}
}
else
{
//
// We got some data.
//
DNASSERT(iRecvAddressSize == sizeof(saddrinServerAddress));
ZeroMemory(&RespInfo, sizeof(RespInfo));
hr = this->ParsePASTMessage(acRespBuffer, iReturn, &RespInfo);
if (hr != DPNH_OK)
{
//
// We couldn't handle the response, try again.
//
DPFX(DPFPREP, 0, "Failed parsing message (err = %lx), ignoring.",
hr);
}
else
{
if (RespInfo.dwMsgID != 0)
{
//
// We got a response to a different message,
// try again.
//
DPFX(DPFPREP, 0, "Got messageid %u, expecting messageid 0, ignoring.",
RespInfo.dwMsgID);
}
else
{
if (RespInfo.cMsgType != PAST_MSGID_REGISTER_RESPONSE)
{
//
// We got an unxpected response type, try
// again.
//
DPFX(DPFPREP, 0, "Got message type %u, expecting %u, ignoring.",
RespInfo.cMsgType, PAST_MSGID_REGISTER_RESPONSE);
}
else
{
//
// If we got here, then it looks like we
// got the response we want.
//
//
// If we were broadcasting, accept the
// first response we receive. Otherwise,
// throw out the message if it's not from
// the address we expect.
//
if (saddrinServerAddress.sin_addr.S_un.S_addr == INADDR_BROADCAST)
{
DPFX(DPFPREP, 4, "Accepting response from PAST server %u.%u.%u.%u:%u.",
saddrinReceive.sin_addr.S_un.S_un_b.s_b1,
saddrinReceive.sin_addr.S_un.S_un_b.s_b2,
saddrinReceive.sin_addr.S_un.S_un_b.s_b3,
saddrinReceive.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinReceive.sin_port));
pDevice->SetRemotePASTServerAddressV4(saddrinReceive.sin_addr.S_un.S_addr);
}
if (pDevice->GetRemotePASTServerAddressV4() == saddrinReceive.sin_addr.S_un.S_addr)
{
pDevice->SetPASTClientID(RespInfo.dwClientID, TRUE);
//
// Move to the next message ID.
//
pDevice->ResetRemotePASTMsgIDAndRetryTimeout(DEFAULT_INITIAL_PAST_RETRY_TIMEOUT);
pDevice->GetNextRemotePASTMsgID();
//
// Pull this device from the list of
// remaining devices.
//
pDevice->m_blTempList.RemoveFromList();
//
// Register all the existing mappings
// associated with the device that the
// user has already requested.
//
if (! pDevice->m_blOwnedRegPorts.IsEmpty())
{
hr = this->RegisterAllPortsWithPAST(pDevice, TRUE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't register all existing ports with remote PAST server!");
goto Failure;
}
#ifdef DBG
//
// If we didn't encounter an error
// that caused the PAST server to
// be removed, then the
// NATHELPPASTOBJ_ADDRESSESCHANGED
// flag must have been set by the
// AssignOrListenPorts function.
//
if (pDevice->GetPASTClientID(TRUE) != 0)
{
if (! (this->m_dwFlags & NATHELPPASTOBJ_ADDRESSESCHANGED))
{
DPFX(DPFPREP, 1, "Successfully registered with remote PAST server, but no addresses changed; all ports should be unavailable.");
}
}
else
{
DPFX(DPFPREP, 1, "Remote PAST server was removed while trying to register existing ports.");
}
#endif // DBG
}
else
{
BOOL fAddressAlreadyChanged;
DPFX(DPFPREP, 2, "Remote PAST server now available for device 0x%p, but no ports are currently registered.",
pDevice);
fAddressAlreadyChanged = (this->m_dwFlags & NATHELPPASTOBJ_ADDRESSESCHANGED) ? TRUE : FALSE;
//
// Forcefully check if there's a
// public address available.
//
hr = this->UpdatePASTPublicAddressValidity(pDevice, TRUE);
if (hr != DPNH_OK)
{
DPFX(DPFPREP, 0, "Couldn't update remote PAST server address validity!");
goto Failure;
}
//
// Prevent the user from thinking
// the addresses changed unless
// something else already caused
// the address change notification.
//
if (! fAddressAlreadyChanged)
{
this->m_dwFlags &= ~NATHELPPASTOBJ_ADDRESSESCHANGED;
}
}
//
// Store this device, if it's the first
// one that successfully registered.
//
if ((*ppFirstDeviceWithRemoteServer) == NULL)
{
DPFX(DPFPREP, 7, "Saving device 0x%p that was just registered with new remote PAST server.",
pDevice);
(*ppFirstDeviceWithRemoteServer) = pDevice;
}
else
{
DPFX(DPFPREP, 7, "Already have device with remote PAST server 0x%p, not storing newly registered 0x%p.",
(*ppFirstDeviceWithRemoteServer), pDevice);
}
//
// If there aren't any more devices, we
// can bail.
//
if (pSourceList->IsEmpty())
{
DPFX(DPFPREP, 7, "Last device (0x%p) got registered with remote PAST server.",
pDevice);
hr = DPNH_OK;
goto Exit;
}
}
else
{
DPFX(DPFPREP, 0, "Ignoring correctly formed message from %u.%u.%u.%u:%u!",
saddrinReceive.sin_addr.S_un.S_un_b.s_b1,
saddrinReceive.sin_addr.S_un.S_un_b.s_b2,
saddrinReceive.sin_addr.S_un.S_un_b.s_b3,
saddrinReceive.sin_addr.S_un.S_un_b.s_b4,
NTOHS(saddrinReceive.sin_port));
}
} // end else (got correct message type)
} // end else (got correct message ID)
} // end else (failed parsing message)
} // end else (successfully received data)
}
else
{
//
// Socket did not receive any data.
//
}
}
//
// Calculate how much time remains. If that went negative, loop
// one extra time (with a timeout of 0). We will bail after the
// select if no data arrived while we were processing the last
// round of data.
//
dwTimeRemaining = dwFinishTime - timeGetTime();
if ((int) dwTimeRemaining < 0)
{
dwTimeRemaining = 0;
}
}
while (TRUE);
//
// Go to next attempt
//
}
//
// If we're here some devices still do not have remote PAST servers.
// There should still be items in the temp source list.
//
DNASSERT(! pSourceList->IsEmpty());
//
// Remove any remaining items from the temp source list.
//
pBilink = pSourceList->GetNext();
while (pBilink != pSourceList)
{
pDevice = DEVICE_FROM_TEMP_BILINK(pBilink);
pBilink = pBilink->GetNext();
pDevice->m_blTempList.RemoveFromList();
}
hr = DPNH_OK;
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::RegisterMultipleDevicesWithRemotePAST
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::ParsePASTMessage"
//=============================================================================
// CNATHelpPAST::ParsePASTMessage
//-----------------------------------------------------------------------------
//
// Description: Parses a PAST message and extracts the codes into fields in
// a standardized structure.
//
// This is not completely general, as we know that we will only
// operate with v4 addresses and our commands will never deal with
// more than 1 address/port at a time. PAST allows for multiple
// ports to be allocated in a single request, but we do not take
// advantage of this feature. If you need to handle such multiple
// address requests and responses, then you will need to change
// this function.
//
// Arguments:
// char * pcMsg - Pointer to buffer containing a PAST
// request or response message.
// int iMsgSize - Size of message buffer in bytes.
// PAST_RESPONSE_INFO * pRespInfo - Pointer to structure that is filled with
// the parameters from the PAST message.
//
// Returns: HRESULT
// DPNH_OK - Parsing was successful.
// DPNHERR_INVALIDPARAM - An invalid buffer was given.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::ParsePASTMessage(const char * const pcMsg,
const int iMsgSize,
PAST_RESPONSE_INFO * const pRespInfo)
{
HRESULT hr = DPNH_OK;
BOOL fGotlAddress = FALSE;
BOOL fGotlPort = FALSE;
PPAST_PARAM pParam;
PPAST_PARAM pNextParam;
const char * pcEnd;
char * pcData;
char cTemp;
#ifdef DBG
char szPortList[(DPNH_MAX_SIMULTANEOUS_PORTS * 7) + 1]; // "nnnnn, " + NULL termination
char * pszTemp;
#endif // DBG
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i, 0x%p)",
this, pcMsg, iMsgSize, pRespInfo);
if (iMsgSize < 2)
{
DPFX(DPFPREP, 0, "Buffer too small to be valid PAST message (%i bytes)!", iMsgSize);
DNASSERTX(! "Buffer too small to be valid PAST message!", 2);
hr = DPNHERR_INVALIDPARAM;
goto Failure;
}
pRespInfo->cVersion = ((PPAST_MSG) pcMsg)->version;
pRespInfo->cMsgType = ((PPAST_MSG) pcMsg)->msgtype;
DPFX(DPFPREP, 3, "version %u msgtype %u", pRespInfo->cVersion, pRespInfo->cMsgType);
pParam = (PPAST_PARAM) (pcMsg + sizeof(PAST_MSG));
pcEnd = pcMsg + iMsgSize;
while ((char *) (pParam + 1) < pcEnd)
{
pcData = (char *) (pParam + 1);
pNextParam = (PPAST_PARAM) (pcData + pParam->len);
if ((pParam->len > (iMsgSize - sizeof(PAST_MSG) - sizeof(PAST_PARAM))) ||
((char *) pNextParam > pcEnd))
{
break;
}
switch (pParam->code)
{
case PAST_PARAMID_ADDRESS:
{
if (pParam->len >= 1)
{
//
// Addresses are type[1]|addr[?]
//
if ((*pcData) != PAST_ADDRESSTYPE_IPV4)
{
DPFX(DPFPREP, 0, "Got unexpected PAST address code type %u!", (*pcData));
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Validate the size of the address parameter.
//
if (pParam->len == 5)
{
if (! fGotlAddress)
{
CopyMemory(&pRespInfo->dwLocalAddressV4, pcData + 1, 4);
DPFX(DPFPREP, 3, "Got local address %u.%u.%u.%u.",
((IN_ADDR *) (&pRespInfo->dwLocalAddressV4))->S_un.S_un_b.s_b1,
((IN_ADDR *) (&pRespInfo->dwLocalAddressV4))->S_un.S_un_b.s_b2,
((IN_ADDR *) (&pRespInfo->dwLocalAddressV4))->S_un.S_un_b.s_b3,
((IN_ADDR *) (&pRespInfo->dwLocalAddressV4))->S_un.S_un_b.s_b4);
if ((pRespInfo->dwLocalAddressV4 == INADDR_BROADCAST) ||
(IS_CLASSD_IPV4_ADDRESS(pRespInfo->dwLocalAddressV4)))
{
DPFX(DPFPREP, 1, "Ignoring invalid local address and using INADDR_ANY instead.");
pRespInfo->dwLocalAddressV4 = 0;
}
fGotlAddress = TRUE;
}
else
{
fGotlPort = TRUE; // just in case there wasn't a local port
CopyMemory(&pRespInfo->dwRemoteAddressV4, pcData + 1, 4);
DPFX(DPFPREP, 3, "Got remote address %u.%u.%u.%u.",
((IN_ADDR *) (&pRespInfo->dwRemoteAddressV4))->S_un.S_un_b.s_b1,
((IN_ADDR *) (&pRespInfo->dwRemoteAddressV4))->S_un.S_un_b.s_b2,
((IN_ADDR *) (&pRespInfo->dwRemoteAddressV4))->S_un.S_un_b.s_b3,
((IN_ADDR *) (&pRespInfo->dwRemoteAddressV4))->S_un.S_un_b.s_b4);
if ((pRespInfo->dwRemoteAddressV4 == INADDR_BROADCAST) ||
(IS_CLASSD_IPV4_ADDRESS(pRespInfo->dwRemoteAddressV4)))
{
DPFX(DPFPREP, 7, "Ignoring invalid remote address and using INADDR_ANY instead.");
pRespInfo->dwRemoteAddressV4 = 0;
}
}
}
else
{
DPFX(DPFPREP, 0, "Ignoring address parameter with invalid length (%u)!",
pParam->len);
}
}
else
{
DPFX(DPFPREP, 1, "Ignoring 0 byte address parameter.");
}
break;
}
case PAST_PARAMID_PORTS:
{
if (pParam->len >= 1)
{
//
// Validate the port count.
//
if ((*pcData) == 0)
{
DPFX(DPFPREP, 1, "Ignoring %u byte port parameter with 0 ports.",
pParam->len);
break;
}
//
// Validate the size of the parameter.
//
if (pParam->len < (((*pcData) * sizeof (WORD)) + 1))
{
DPFX(DPFPREP, 0, "Port parameter is %u bytes, but is reporting %u ports! Ignoring.",
pParam->len, (*pcData));
break;
}
//
// NOTE: Ports appeared to be transferred in x86 format,
// contrary to the spec, which says network byte order.
//
//
// Ports are Count[1]|Port[2]....Port[2]
//
if (! fGotlPort)
{
pRespInfo->cNumLocalPorts = (*pcData);
if (pRespInfo->cNumLocalPorts > DPNH_MAX_SIMULTANEOUS_PORTS)
{
DPFX(DPFPREP, 0, "Got %u local ports, only using first %u:",
pRespInfo->cNumLocalPorts, DPNH_MAX_SIMULTANEOUS_PORTS);
pRespInfo->cNumLocalPorts = DPNH_MAX_SIMULTANEOUS_PORTS;
}
else
{
DPFX(DPFPREP, 3, "Got %u local ports:",
pRespInfo->cNumLocalPorts);
}
//
// Copy the port array.
//
CopyMemory(pRespInfo->awLocalPorts, (pcData + 1),
(pRespInfo->cNumLocalPorts * sizeof (WORD)));
//
// Unfortunately we want the array to have ports in
// network byte order for real. Loop through each port
// and switch them.
//
#ifdef DBG
szPortList[0] = '\0'; // initialize
pszTemp = szPortList;
#endif // DBG
for(cTemp = 0; cTemp < pRespInfo->cNumLocalPorts; cTemp++)
{
#ifdef DBG
pszTemp += wsprintfA(pszTemp, "%u, ", pRespInfo->awLocalPorts[cTemp]);
#endif // DBG
pRespInfo->awLocalPorts[cTemp] = HTONS(pRespInfo->awLocalPorts[cTemp]);
}
#ifdef DBG
szPortList[strlen(szPortList) - 2] = '\0'; // chop off trailing ", "
DPFX(DPFPREP, 3, " {%hs}", szPortList);
#endif // DBG
fGotlPort = TRUE;
}
else
{
if (pRespInfo->cNumRemotePorts > 0)
{
DPFX(DPFPREP, 0, "Already received %u remote ports, ignoring %u more.",
pRespInfo->cNumRemotePorts, (*pcData));
}
else
{
pRespInfo->cNumRemotePorts = (*pcData);
if (pRespInfo->cNumRemotePorts > DPNH_MAX_SIMULTANEOUS_PORTS)
{
DPFX(DPFPREP, 0, "Got %u remote ports, only using first %u:",
pRespInfo->cNumRemotePorts, DPNH_MAX_SIMULTANEOUS_PORTS);
pRespInfo->cNumRemotePorts = DPNH_MAX_SIMULTANEOUS_PORTS;
}
else
{
DPFX(DPFPREP, 3, "Got %u remote ports:",
pRespInfo->cNumRemotePorts);
}
//
// Copy the port array.
//
CopyMemory(pRespInfo->awRemotePorts, (pcData + 1),
(pRespInfo->cNumRemotePorts * sizeof (WORD)));
//
// Unfortunately we want the array to have ports in
// network byte order for real. Loop through each
// port and switch them.
//
#ifdef DBG
szPortList[0] = '\0'; // initialize
pszTemp = szPortList;
#endif // DBG
for(cTemp = 0; cTemp < pRespInfo->cNumRemotePorts; cTemp++)
{
#ifdef DBG
pszTemp += wsprintfA(pszTemp, "%u, ", pRespInfo->awRemotePorts[cTemp]);
#endif // DBG
pRespInfo->awRemotePorts[cTemp] = HTONS(pRespInfo->awRemotePorts[cTemp]);
}
#ifdef DBG
szPortList[strlen(szPortList) - 2] = '\0'; // chop off trailing ", "
DPFX(DPFPREP, 3, " {%hs}", szPortList);
#endif // DBG
}
}
}
else
{
DPFX(DPFPREP, 1, "Ignoring 0 byte port parameter.");
}
break;
}
case PAST_PARAMID_LEASE:
{
if (pParam->len == 4)
{
CopyMemory(&pRespInfo->dwLeaseTime, pcData, 4);
DPFX(DPFPREP, 3, "Got lease of %u seconds.", pRespInfo->dwLeaseTime);
}
else
{
if (pParam->len == 0)
{
DPFX(DPFPREP, 1, "Ignoring empty lease parameter.");
}
else
{
DPFX(DPFPREP, 0, "Ignoring lease parameter with invalid length (%u)!",
pParam->len);
}
}
break;
}
case PAST_PARAMID_CLIENTID:
{
if (pParam->len == 4)
{
CopyMemory(&pRespInfo->dwClientID, pcData, 4);
DPFX(DPFPREP, 3, "Got client ID %u.", pRespInfo->dwClientID);
}
else
{
if (pParam->len == 0)
{
DPFX(DPFPREP, 1, "Ignoring empty client ID parameter.");
}
else
{
DPFX(DPFPREP, 0, "Ignoring client ID parameter with invalid length (%u)!",
pParam->len);
}
}
break;
}
case PAST_PARAMID_BINDID:
{
if (pParam->len == 4)
{
CopyMemory(&pRespInfo->dwBindID, pcData, 4);
DPFX(DPFPREP, 3, "Got bind ID %u.", pRespInfo->dwBindID);
}
else
{
if (pParam->len == 0)
{
DPFX(DPFPREP, 1, "Ignoring empty bind ID parameter.");
}
else
{
DPFX(DPFPREP, 0, "Ignoring bind ID parameter with invalid length (%u)!",
pParam->len);
}
}
break;
}
case PAST_PARAMID_MESSAGEID:
{
if (pParam->len == 4)
{
CopyMemory(&pRespInfo->dwMsgID, pcData, 4);
DPFX(DPFPREP, 3, "Got message ID %u.", pRespInfo->dwMsgID);
}
else
{
if (pParam->len == 0)
{
DPFX(DPFPREP, 1, "Ignoring empty message ID parameter.");
}
else
{
DPFX(DPFPREP, 0, "Ignoring message ID parameter with invalid length (%u)!",
pParam->len);
}
}
break;
}
case PAST_PARAMID_TUNNELTYPE:
{
if (pParam->len == 1)
{
DPFX(DPFPREP, 3, "Got tunnel type %u, ignoring.", (*pcData));
}
else
{
if (pParam->len == 0)
{
DPFX(DPFPREP, 1, "Ignoring empty tunnel type parameter.");
}
else
{
DPFX(DPFPREP, 0, "Ignoring tunnel type parameter with invalid length (%u)!",
pParam->len);
}
}
break;
}
case PAST_PARAMID_PASTMETHOD:
{
if (pParam->len == 1)
{
DPFX(DPFPREP, 3, "Got PAST method %u, ignoring.", (*pcData));
}
else
{
if (pParam->len == 0)
{
DPFX(DPFPREP, 1, "Ignoring empty PAST method parameter.");
}
else
{
DPFX(DPFPREP, 0, "Ignoring PAST method parameter with invalid length (%u)!",
pParam->len);
}
}
break;
}
case PAST_PARAMID_ERROR:
{
if (pParam->len == 2)
{
#ifdef DBG
char * pszErrorString;
#endif // DBG
CopyMemory(&pRespInfo->wError, pcData, 2);
#ifdef DBG
switch (pRespInfo->wError)
{
case PASTERR_UNKNOWNERROR:
{
pszErrorString = "UNKNOWNERROR";
break;
}
case PASTERR_BADBINDID:
{
pszErrorString = "BADBINDID";
break;
}
case PASTERR_BADCLIENTID:
{
pszErrorString = "BADCLIENTID";
break;
}
case PASTERR_MISSINGPARAM:
{
pszErrorString = "MISSINGPARAM";
break;
}
case PASTERR_DUPLICATEPARAM:
{
pszErrorString = "DUPLICATEPARAM";
break;
}
case PASTERR_ILLEGALPARAM:
{
pszErrorString = "ILLEGALPARAM";
break;
}
case PASTERR_ILLEGALMESSAGE:
{
pszErrorString = "ILLEGALMESSAGE";
break;
}
case PASTERR_REGISTERFIRST:
{
pszErrorString = "REGISTERFIRST";
break;
}
case PASTERR_BADMESSAGEID:
{
pszErrorString = "BADMESSAGEID";
break;
}
case PASTERR_ALREADYREGISTERED:
{
pszErrorString = "ALREADYREGISTERED";
break;
}
case PASTERR_ALREADYUNREGISTERED:
{
pszErrorString = "ALREADYUNREGISTERED";
break;
}
case PASTERR_BADTUNNELTYPE:
{
pszErrorString = "BADTUNNELTYPE";
break;
}
case PASTERR_ADDRUNAVAILABLE:
{
pszErrorString = "ADDRUNAVAILABLE";
break;
}
case PASTERR_PORTUNAVAILABLE:
{
pszErrorString = "PORTUNAVAILABLE";
break;
}
default:
{
pszErrorString = "? unknown ?";
break;
}
}
DPFX(DPFPREP, 1, "Got PAST error %u, %hs.",
pRespInfo->wError, pszErrorString);
#endif // DBG
}
else
{
if (pParam->len == 0)
{
DPFX(DPFPREP, 1, "Ignoring empty PAST error parameter.");
}
else
{
DPFX(DPFPREP, 0, "Ignoring PAST error parameter with invalid length (%u)!",
pParam->len);
}
}
break;
}
case PAST_PARAMID_FLOWPOLICY:
{
if (pParam->len == 2)
{
DPFX(DPFPREP, 3, "Got PAST flow policy local %u, remote %u; ignoring.",
(*pcData), *(pcData + 1));
}
else
{
if (pParam->len == 0)
{
DPFX(DPFPREP, 1, "Ignoring empty flow policy parameter.");
}
else
{
DPFX(DPFPREP, 0, "Ignoring flow policy parameter with invalid length (%u)!",
pParam->len);
}
}
break;
}
case PAST_PARAMID_VENDOR:
{
DPFX(DPFPREP, 1, "Got %u byte vendor code parameter, ignoring.",
pParam->len);
break;
}
default:
{
DPFX(DPFPREP, 0, "Got %u byte unknown parameter code %u, ignoring.",
pParam->len, pParam->code);
break;
}
}
pParam = pNextParam;
}
Failure:
//Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
} // CNATHelpPAST::ParsePASTMessage
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::RequestLocalAddressListChangeNotification"
//=============================================================================
// CNATHelpPAST::RequestLocalAddressListChangeNotification
//-----------------------------------------------------------------------------
//
// Description: Attempts to request asynchronous notification (via the
// user's alert event or I/O completion port) when the local
// address list changes.
//
// The object lock is assumed to be held.
//
// Arguments: None.
//
// Returns: HRESULT
// DPNH_OK - The notification request was successfully submitted.
// DPNHERR_GENERIC - An error occurred.
//=============================================================================
HRESULT CNATHelpPAST::RequestLocalAddressListChangeNotification(void)
{
HRESULT hr;
DWORD dwTemp;
int iReturn;
DPFX(DPFPREP, 5, "(0x%p) Enter", this);
DNASSERT(! (this->m_dwFlags & NATHELPPASTOBJ_WINSOCK1));
DNASSERT(this->m_sIoctls != INVALID_SOCKET);
DNASSERT(this->m_pfnWSAIoctl != NULL);
DNASSERT((this->m_hAlertEvent != NULL) || (this->m_hAlertIOCompletionPort != NULL));
DNASSERT(this->m_polAddressListChange != NULL);
do
{
iReturn = this->m_pfnWSAIoctl(this->m_sIoctls, // use the special Ioctl socket
SIO_ADDRESS_LIST_CHANGE, //
NULL, // no input data
0, // no input data
NULL, // no output data
0, // no output data
&dwTemp, // ignore bytes returned
this->m_polAddressListChange, // overlapped structure
NULL); // no completion routine
if (iReturn != 0)
{
dwTemp = this->m_pfnWSAGetLastError();
if (dwTemp != WSA_IO_PENDING)
{
DPFX(DPFPREP, 0, "Submitting address list change notification request failed (err = %u)!", dwTemp);
hr = DPNHERR_GENERIC;
goto Failure;
}
//
// Pending is what we want, we're set.
//
hr = DPNH_OK;
break;
}
//
// Address list changed right away?
//
DPFX(DPFPREP, 1, "Address list changed right away somehow, submitting again.");
}
while (TRUE);
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
return hr;
Failure:
goto Exit;
} // CNATHelpPAST::RequestLocalAddressListChangeNotification
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::CreatePASTSocket"
//=============================================================================
// CNATHelpPAST::CreatePASTSocket
//-----------------------------------------------------------------------------
//
// Description: Creates a PAST communication socket bound to a new random
// port on the specified IP interface. Completely random (but
// non-reserved) port numbers are chosen first, but if those ports
// are in use, WinSock is allowed to choose. The port actually
// selected will be returned in psaddrinAddress.
//
// Arguments:
// SOCKADDR_IN * psaddrinAddress - Pointer to base address to use when
// binding. The port will be modified.
//
// Returns: SOCKET
//=============================================================================
SOCKET CNATHelpPAST::CreatePASTSocket(SOCKADDR_IN * const psaddrinAddress)
{
SOCKET sTemp;
DWORD dwTry;
int iTemp;
BOOL fTemp;
#ifdef DBG
DWORD dwError;
#endif // DBG
DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, psaddrinAddress);
//
// Create the socket.
//
sTemp = this->m_pfnsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sTemp == INVALID_SOCKET)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't create datagram socket, error = %u!", dwError);
#endif // DBG
goto Failure;
}
//
// Try binding the socket to a completely random port a few times.
//
for(dwTry = 0; dwTry < MAX_NUM_RANDOM_PORT_TRIES; dwTry++)
{
//
// Pick a completely random port. For the moment, the value is stored
// in host byte order while we make sure it's not a reserved value.
//
do
{
psaddrinAddress->sin_port = (WORD) GetGlobalRand();
}
while ((psaddrinAddress->sin_port <= MAX_RESERVED_PORT) ||
(psaddrinAddress->sin_port == 1900) || // SSDP
(psaddrinAddress->sin_port == 2234) || // PAST
(psaddrinAddress->sin_port == 6073) || // DPNSVR
(psaddrinAddress->sin_port == 47624)); // DPLAYSVR
//
// Now try binding to the port (in network byte order).
//
psaddrinAddress->sin_port = HTONS(psaddrinAddress->sin_port);
if (this->m_pfnbind(sTemp, (SOCKADDR*) psaddrinAddress, sizeof(SOCKADDR_IN)) == 0)
{
//
// We successfully bound to the port.
//
break;
}
//
// Assume that the port is in use.
//
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 2, "Couldn't bind to port %u (err = %u), continuing.",
NTOHS(psaddrinAddress->sin_port), dwError);
#endif // DBG
psaddrinAddress->sin_port = 0;
}
//
// If we ran out of completely random port attempts, just let WinSock
// choose it.
//
if (psaddrinAddress->sin_port == 0)
{
if (this->m_pfnbind(sTemp, (SOCKADDR*) psaddrinAddress, sizeof(SOCKADDR_IN)) != 0)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Failed binding to any port (err = %u)!",
dwError);
#endif // DBG
goto Failure;
}
//
// Find out what port WinSock chose.
//
iTemp = sizeof(SOCKADDR_IN);
if (this->m_pfngetsockname(sTemp,
(SOCKADDR *) psaddrinAddress,
&iTemp) != 0)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't get the socket's address, error = %u!",
dwError);
#endif // DBG
goto Failure;
}
DNASSERT(psaddrinAddress->sin_port != 0);
}
//
// Set the unicast TTL, if requested. Use the appropriate constant for the
// the version of WinSock we're using.
//
if (g_iUnicastTTL != 0)
{
iTemp = this->m_pfnsetsockopt(sTemp,
IPPROTO_IP,
#ifdef DPNBUILD_NOWINSOCK2
IP_TTL,
#else // ! DPNBUILD_NOWINSOCK2
((this->m_dwFlags & NATHELPPASTOBJ_WINSOCK1) ? IP_TTL_WINSOCK1 : IP_TTL),
#endif // ! DPNBUILD_NOWINSOCK2
(char *) (&g_iUnicastTTL),
sizeof(g_iUnicastTTL));
if (iTemp != 0)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't set unicast TTL socket option, error = %u! Ignoring.",
dwError);
#endif // DBG
//
// Continue...
//
}
}
//
// Set the socket up to allow broadcasts in case we can't determine the
// gateway.
//
fTemp = TRUE;
if (this->m_pfnsetsockopt(sTemp,
SOL_SOCKET,
SO_BROADCAST,
(char *) (&fTemp),
sizeof(fTemp)) != 0)
{
#ifdef DBG
dwError = this->m_pfnWSAGetLastError();
DPFX(DPFPREP, 0, "Couldn't set broadcast socket option, error = %u!", dwError);
#endif // DBG
goto Failure;
}
Exit:
DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%x]", this, sTemp);
return sTemp;
Failure:
if (sTemp != INVALID_SOCKET)
{
this->m_pfnclosesocket(sTemp);
sTemp = INVALID_SOCKET;
}
goto Exit;
} // CNATHelpPAST::CreatePASTSocket
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::GetAddressToReachGateway"
//=============================================================================
// CNATHelpPAST::GetAddressToReachGateway
//-----------------------------------------------------------------------------
//
// Description: Retrieves the address of the gateway for the given device,
// or the broadcast address if unable to be determined.
//
// This will return TRUE if the gateway's address was found, or
// the IPHLPAPI DLL could not be used (Win95). FALSE is returned
// if IPHLPAPI reported that there was no gateway (ICS private
// side adapter).
//
// Arguments:
// CDevice * pDevice - Pointer to device whose gateway should be retrieved.
// IN_ADDR * pinaddr - Place to store gateway or broadcast address.
//
// Returns: BOOL
// TRUE - Gateway address was found or had to use broadcast.
// FALSE - There is no gateway, do not attempt to use the address.
//=============================================================================
BOOL CNATHelpPAST::GetAddressToReachGateway(CDevice * const pDevice,
IN_ADDR * const pinaddr)
{
DWORD dwError;
BOOL fResult = TRUE;
ULONG ulSize;
PIP_ADAPTER_INFO pAdaptersBuffer = NULL;
PIP_ADAPTER_INFO pAdapterInfo;
PIP_ADDR_STRING pIPAddrString;
DWORD dwAdapterIndex;
PMIB_IPFORWARDTABLE pIPForwardTableBuffer = NULL;
DWORD dwTemp;
PMIB_IPFORWARDROW pIPForwardRow;
//
// Fill in the default address. This should be atomic, so don't worry
// about locking the globals.
//
pinaddr->S_un.S_addr = g_dwDefaultGatewayV4;
#ifdef DBG
pDevice->ClearGatewayFlags();
#endif // DBG
//
// If this is the loopback address, then don't bother looking for a
// gateway, we won't find one.
//
if (pDevice->GetLocalAddressV4() == NETWORKBYTEORDER_INADDR_LOOPBACK)
{
DPFX(DPFPREP, 8, "No gateway for loopback address (device = 0x%p).",
pDevice);
//
// No gateway.
//
#ifdef DBG
pDevice->NoteNoGateway();
#endif // DBG
fResult = FALSE;
goto Exit;
}
//
// If we didn't load the IP helper DLL, we can't do our fancy gateway
// tricks.
//
if (this->m_hIpHlpApiDLL == NULL)
{
DPFX(DPFPREP, 4, "Didn't load \"iphlpapi.dll\", returning default address for device 0x%p.",
pDevice);
goto Exit;
}
//
// Keep trying to get the list of adapters until we get ERROR_SUCCESS or a
// legitimate error (other than ERROR_BUFFER_OVERFLOW or
// ERROR_INSUFFICIENT_BUFFER).
//
ulSize = 0;
do
{
dwError = this->m_pfnGetAdaptersInfo(pAdaptersBuffer, &ulSize);
if (dwError == ERROR_SUCCESS)
{
//
// We succeeded, we should be set. But make sure there are
// adapters for us to use.
//
if (ulSize < sizeof(IP_ADAPTER_INFO))
{
DPFX(DPFPREP, 0, "Getting adapters info succeeded but didn't return any valid adapters (%u < %u), returning default address for device 0x%p.",
ulSize, sizeof(IP_ADAPTER_INFO), pDevice);
goto Exit;
}
break;
}
if ((dwError != ERROR_BUFFER_OVERFLOW) &&
(dwError != ERROR_INSUFFICIENT_BUFFER))
{
DPFX(DPFPREP, 0, "Unable to get adapters info (error = 0x%lx), returning default address for device 0x%p.",
dwError, pDevice);
goto Exit;
}
//
// We need more adapter space. Make sure there are adapters for us to
// use.
//
if (ulSize < sizeof(IP_ADAPTER_INFO))
{
DPFX(DPFPREP, 0, "Getting adapters info didn't return any valid adapters (%u < %u), returning default address for device 0x%p.",
ulSize, sizeof(IP_ADAPTER_INFO), pDevice);
goto Exit;
}
//
// If we previously had a buffer, free it.
//
if (pAdaptersBuffer != NULL)
{
DNFree(pAdaptersBuffer);
}
//
// Allocate the buffer.
//
pAdaptersBuffer = (PIP_ADAPTER_INFO) DNMalloc(ulSize);
if (pAdaptersBuffer == NULL)
{
DPFX(DPFPREP, 0, "Unable to allocate memory for adapters info, returning default address for device 0x%p.",
pDevice);
goto Exit;
}
}
while (TRUE);
//
// Now find the device in the adapter list returned. Loop through all
// adapters.
//
pAdapterInfo = pAdaptersBuffer;
while (pAdapterInfo != NULL)
{
//
// Loop through all addresses for this adapter looking for the one for
// the device we have bound.
//
pIPAddrString = &pAdapterInfo->IpAddressList;
while (pIPAddrString != NULL)
{
if (this->m_pfninet_addr(pIPAddrString->IpAddress.String) == pDevice->GetLocalAddressV4())
{
pinaddr->S_un.S_addr = this->m_pfninet_addr(pAdapterInfo->GatewayList.IpAddress.String);
if ((pinaddr->S_un.S_addr == INADDR_ANY) ||
(pinaddr->S_un.S_addr == INADDR_NONE))
{
DPFX(DPFPREP, 8, "Found address for device 0x%p under adapter index %u (\"%hs\") but there is no gateway.",
pDevice, pAdapterInfo->Index, pAdapterInfo->Description,
pAdapterInfo->GatewayList.IpAddress.String);
//
// Although this isn't reporting a gateway, we may still
// want to use this adapter. That's because this could be
// a multihomed machine with multiple NICs on the same
// network, where this one isn't the "default" adapter.
// So save the index so we can search for it later.
//
dwAdapterIndex = pAdapterInfo->Index;
goto CheckRouteTable;
}
//
// Make sure the address doesn't match the local device.
//
if (pinaddr->S_un.S_addr == pDevice->GetLocalAddressV4())
{
DPFX(DPFPREP, 1, "Gateway address for device 0x%p (adapter index %u, \"%hs\") matches device IP address %hs! Forcing no gateway.",
pDevice, pAdapterInfo->Index, pAdapterInfo->Description,
pAdapterInfo->GatewayList.IpAddress.String);
//
// Pretend there's no gateway, since the one we received is
// bogus.
//
#ifdef DBG
pDevice->NoteNoGateway();
#endif // DBG
fResult = FALSE;
}
else
{
DPFX(DPFPREP, 7, "Found address for device 0x%p under adapter index %u (\"%hs\"), gateway = %hs.",
pDevice, pAdapterInfo->Index, pAdapterInfo->Description,
pAdapterInfo->GatewayList.IpAddress.String);
#ifdef DBG
pDevice->NotePrimaryDevice();
#endif // DBG
}
goto Exit;
}
pIPAddrString = pIPAddrString->Next;
}
if (! fResult)
{
break;
}
pAdapterInfo = pAdapterInfo->Next;
}
//
// If we got here, then we didn't find the address. fResult will still be
// TRUE.
//
DPFX(DPFPREP, 0, "Did not find adapter with matching address, returning default address for device 0x%p.",
pDevice);
goto Exit;
CheckRouteTable:
//
// The adapter info structure said that the device doesn't have a gateway.
// However for some reason the gateway is only reported for the "default"
// device when multiple NICs can reach the same network. Check the routing
// table to determine if there's a gateway for secondary devices.
//
//
// Keep trying to get the routing table until we get ERROR_SUCCESS or a
// legitimate error (other than ERROR_BUFFER_OVERFLOW or
// ERROR_INSUFFICIENT_BUFFER).
//
ulSize = 0;
do
{
dwError = this->m_pfnGetIpForwardTable(pIPForwardTableBuffer, &ulSize, TRUE);
if (dwError == ERROR_SUCCESS)
{
//
// We succeeded, we should be set. But make sure the size is
// valid.
//
if (ulSize < sizeof(MIB_IPFORWARDTABLE))
{
DPFX(DPFPREP, 0, "Getting IP forward table succeeded but didn't return a valid buffer (%u < %u), returning \"no gateway\" indication for device 0x%p.",
ulSize, sizeof(MIB_IPFORWARDTABLE), pDevice);
fResult = FALSE;
goto Exit;
}
break;
}
if ((dwError != ERROR_BUFFER_OVERFLOW) &&
(dwError != ERROR_INSUFFICIENT_BUFFER))
{
DPFX(DPFPREP, 0, "Unable to get IP forward table (error = 0x%lx), returning \"no gateway\" indication for device 0x%p.",
dwError, pDevice);
fResult = FALSE;
goto Exit;
}
//
// We need more table space. Make sure there are adapters for us to
// use.
//
if (ulSize < sizeof(MIB_IPFORWARDTABLE))
{
DPFX(DPFPREP, 0, "Getting IP forward table didn't return any valid adapters (%u < %u), returning \"no gateway\" indication for device 0x%p.",
ulSize, sizeof(MIB_IPFORWARDTABLE), pDevice);
fResult = FALSE;
goto Exit;
}
//
// If we previously had a buffer, free it.
//
if (pIPForwardTableBuffer != NULL)
{
DNFree(pIPForwardTableBuffer);
}
//
// Allocate the buffer.
//
pIPForwardTableBuffer = (PMIB_IPFORWARDTABLE) DNMalloc(ulSize);
if (pIPForwardTableBuffer == NULL)
{
DPFX(DPFPREP, 0, "Unable to allocate memory for IP forward table, returning \"no gateway\" indication for device 0x%p.",
pDevice);
fResult = FALSE;
goto Exit;
}
}
while (TRUE);
//
// Now find the interface. Note that we don't look it up as a destination
// address. Instead, we look for it as the interface to use for a 0.0.0.0
// network destination.
//
// We're looking for a route entry:
//
// Network Destination Netmask Gateway Interface Metric
// 0.0.0.0 0.0.0.0 xxx.xxx.xxx.xxx yyy.yyy.yyy.yyy 1
//
// We have yyy.yyy.yyy.yyy, we're trying to get xxx.xxx.xxx.xxx
//
pIPForwardRow = pIPForwardTableBuffer->table;
for(dwTemp = 0; dwTemp < pIPForwardTableBuffer->dwNumEntries; dwTemp++)
{
//
// Is this a 0.0.0.0 network destination?
//
if (pIPForwardRow->dwForwardDest == INADDR_ANY)
{
DNASSERT(pIPForwardRow->dwForwardMask == INADDR_ANY);
//
// Is this the right interface?
//
if (pIPForwardRow->dwForwardIfIndex == dwAdapterIndex)
{
if (pIPForwardRow->dwForwardNextHop == INADDR_ANY)
{
DPFX(DPFPREP, 8, "Found route table entry, but it didn't have a gateway (device = 0x%p).",
pDevice);
//
// No gateway.
//
#ifdef DBG
pDevice->NoteNoGateway();
#endif // DBG
fResult = FALSE;
}
else
{
//
// Make sure the address doesn't match the local device.
//
if (pinaddr->S_un.S_addr == pDevice->GetLocalAddressV4())
{
DPFX(DPFPREP, 1, "Route table gateway for device 0x%p matches device's IP address %u.%u.%u.%u! Forcing no gateway.",
pDevice,
pinaddr->S_un.S_un_b.s_b1,
pinaddr->S_un.S_un_b.s_b2,
pinaddr->S_un.S_un_b.s_b3,
pinaddr->S_un.S_un_b.s_b4);
//
// Pretend there's no gateway, since the one we
// received is bogus.
//
#ifdef DBG
pDevice->NoteNoGateway();
#endif // DBG
fResult = FALSE;
}
else
{
pinaddr->S_un.S_addr = pIPForwardRow->dwForwardNextHop;
DPFX(DPFPREP, 8, "Found route table entry, gateway = %u.%u.%u.%u (device = 0x%p).",
pinaddr->S_un.S_un_b.s_b1,
pinaddr->S_un.S_un_b.s_b2,
pinaddr->S_un.S_un_b.s_b3,
pinaddr->S_un.S_un_b.s_b4,
pDevice);
//
// We found a gateway after all, fResult == TRUE.
//
#ifdef DBG
pDevice->NoteSecondaryDevice();
#endif // DBG
}
}
//
// We're done here.
//
goto Exit;
}
}
//
// Move to next row.
//
pIPForwardRow++;
}
//
// If we got here, then we couldn't find an appropriate entry in the
// routing table.
//
DPFX(DPFPREP, 1, "Did not find adapter in routing table, returning \"no gateway\" indication for device 0x%p.",
pDevice);
#ifdef DBG
pDevice->NoteNoGateway();
#endif // DBG
fResult = FALSE;
Exit:
if (pAdaptersBuffer != NULL)
{
DNFree(pAdaptersBuffer);
pAdaptersBuffer = NULL;
}
if (pIPForwardTableBuffer != NULL)
{
DNFree(pIPForwardTableBuffer);
pIPForwardTableBuffer = NULL;
}
return fResult;
} // CNATHelpPAST::GetAddressToReachGateway
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::IsAddressLocal"
//=============================================================================
// CNATHelpPAST::IsAddressLocal
//-----------------------------------------------------------------------------
//
// Description: Returns TRUE if the given address is local to the given
// device; that is, if the device can send to the address directly
// without having to go through the gateway.
//
// Note that if IPHLPAPI is not available (Win95), this
// function will make an educated guess using a reasonable subnet
// mask.
//
// Arguments:
// CDevice * pDevice - Pointer to device to use.
// SOCKADDR_IN * psaddrinAddress - Address whose locality is in question.
//
// Returns: BOOL
// TRUE - Address is behind the same gateway as the device.
// FALSE - Address is not behind the same gateway as the device.
//=============================================================================
BOOL CNATHelpPAST::IsAddressLocal(CDevice * const pDevice,
const SOCKADDR_IN * const psaddrinAddress)
{
DWORD dwError;
BOOL fResult;
MIB_IPFORWARDROW IPForwardRow;
DWORD dwSubnetMaskV4;
//
// If the address to query matches the device's local address exactly, then
// of course it's local.
//
if (psaddrinAddress->sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4())
{
DPFX(DPFPREP, 6, "The address %u.%u.%u.%u matches device 0x%p's local address exactly.",
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
pDevice);
fResult = TRUE;
goto Exit;
}
//
// If it's a multicast address, then it should not be considered local.
//
if (IS_CLASSD_IPV4_ADDRESS(psaddrinAddress->sin_addr.S_un.S_addr))
{
DPFX(DPFPREP, 6, "Address %u.%u.%u.%u is multicast, not considered local for device 0x%p.",
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
pDevice);
fResult = FALSE;
goto Exit;
}
//
// If we didn't load the IP helper DLL, we will have to guess.
//
if (this->m_hIpHlpApiDLL == NULL)
{
goto EducatedGuess;
}
//
// Figure out what IPHLPAPI says about how to get there.
//
ZeroMemory(&IPForwardRow, sizeof(IPForwardRow));
dwError = this->m_pfnGetBestRoute(psaddrinAddress->sin_addr.S_un.S_addr,
pDevice->GetLocalAddressV4(),
&IPForwardRow);
if (dwError != ERROR_SUCCESS)
{
DPFX(DPFPREP, 0, "Unable to get best route to %u.%u.%u.%u via device 0x%p (error = 0x%lx)! Using subnet mask.",
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
pDevice,
dwError);
goto EducatedGuess;
}
//
// Key off what IPHLPAPI returned.
//
switch (IPForwardRow.dwForwardType)
{
case 1:
{
//
// Other.
//
DPFX(DPFPREP, 6, "The route from device 0x%p to %u.%u.%u.%u is unknown.",
pDevice,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
fResult = FALSE;
break;
}
case 2:
{
//
// The route is invalid.
//
DPFX(DPFPREP, 6, "The route from device 0x%p to %u.%u.%u.%u is invalid.",
pDevice,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
fResult = FALSE;
break;
}
case 3:
{
//
// The next hop is the final destination (local route).
// Unfortunately, on multi-NIC machines querying an address
// reachable by another device returns success... not sure why, but
// if that's the case we need to further qualify this result. We
// do that by making sure the next hop address is actually the
// device with which we're querying.
//
if (IPForwardRow.dwForwardNextHop == pDevice->GetLocalAddressV4())
{
DPFX(DPFPREP, 6, "Device 0x%p can reach %u.%u.%u.%u directly, it's local.",
pDevice,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
fResult = TRUE;
}
else
{
DPFX(DPFPREP, 6, "Device 0x%p can reach %u.%u.%u.%u but it would be routed via another device (%u.%u.%u.%u).",
pDevice,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b1,
((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b2,
((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b3,
((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b4);
fResult = FALSE;
}
break;
}
case 4:
{
//
// The next hop is not the final destination (remote route).
//
DPFX(DPFPREP, 6, "Device 0x%p cannot reach %u.%u.%u.%u directly.",
pDevice,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
fResult = FALSE;
break;
}
default:
{
//
// What?
//
DPFX(DPFPREP, 0, "Unexpected forward type %u for device 0x%p and address %u.%u.%u.%u!",
IPForwardRow.dwForwardType, pDevice,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
fResult = FALSE;
break;
}
}
goto Exit;
EducatedGuess:
//
// This should be atomic, so don't worry about locking.
//
dwSubnetMaskV4 = g_dwSubnetMaskV4;
if ((pDevice->GetLocalAddressV4() & dwSubnetMaskV4) == (psaddrinAddress->sin_addr.S_un.S_addr & dwSubnetMaskV4))
{
DPFX(DPFPREP, 4, "Didn't load \"iphlpapi.dll\", guessing that device 0x%p can reach %u.%u.%u.%u (using subnet mask %u.%u.%u.%u).",
pDevice,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
((IN_ADDR*) (&dwSubnetMaskV4))->S_un.S_un_b.s_b1,
((IN_ADDR*) (&dwSubnetMaskV4))->S_un.S_un_b.s_b2,
((IN_ADDR*) (&dwSubnetMaskV4))->S_un.S_un_b.s_b3,
((IN_ADDR*) (&dwSubnetMaskV4))->S_un.S_un_b.s_b4);
fResult = TRUE;
}
else
{
DPFX(DPFPREP, 4, "Didn't load \"iphlpapi.dll\", guessing that device 0x%p cannot reach %u.%u.%u.%u (using subnet mask %u.%u.%u.%u).",
pDevice,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
((IN_ADDR*) (&dwSubnetMaskV4))->S_un.S_un_b.s_b1,
((IN_ADDR*) (&dwSubnetMaskV4))->S_un.S_un_b.s_b2,
((IN_ADDR*) (&dwSubnetMaskV4))->S_un.S_un_b.s_b3,
((IN_ADDR*) (&dwSubnetMaskV4))->S_un.S_un_b.s_b4);
fResult = FALSE;
}
Exit:
return fResult;
} // CNATHelpPAST::IsAddressLocal
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::ClearDevicesPASTServer"
//=============================================================================
// CNATHelpPAST::ClearDevicesPASTServer
//-----------------------------------------------------------------------------
//
// Description: Forcefully simulates de-registration with a PAST server
/// without actually going to the network. This clears all bind
// IDs, public addresses, and cached mappings for a given device's
// local or remote server, and should only be called after the
// server appears to have died.
//
// The object lock is assumed to be held.
//
// Arguments:
// CDevice * pDevice - Pointer to device whose ports should be unbound.
// BOOL fRemote - TRUE if clearing remote PAST server, FALSE if
// clearing local PAST server.
//
// Returns: None.
//=============================================================================
void CNATHelpPAST::ClearDevicesPASTServer(CDevice * const pDevice,
const BOOL fRemote)
{
#ifdef DBG
DNASSERT(pDevice->GetPASTClientID(fRemote) != 0);
DPFX(DPFPREP, 1, "Clearing PAST server, device = 0x%p, remote = %i",
pDevice, fRemote);
pDevice->IncrementPASTServerFailures(fRemote);
this->m_dwNumServerFailures++;
#endif // DBG
if (! fRemote)
{
pDevice->NoteNoLocalPASTServer();
}
pDevice->SetPASTClientID(0, fRemote);
this->ClearAllPASTServerRegisteredPorts(pDevice, fRemote);
pDevice->NoteNoPASTPublicAddressAvailable(fRemote);
this->RemoveAllPASTCachedMappings(pDevice, fRemote);
//
// Since there was a change in the network, go back to polling relatively
// quickly.
//
this->ResetNextPollInterval();
} // CNATHelpPAST::ClearDevicesPASTServer
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::ClearAllPASTServerRegisteredPorts"
//=============================================================================
// CNATHelpPAST::ClearAllPASTServerRegisteredPorts
//-----------------------------------------------------------------------------
//
// Description: Clears all bind IDs and public addresses for a given
// device's local or remote PAST server. This should only be
// called after the PAST server dies because the registered ports
// remain associated with the device.
//
// The object lock is assumed to be held.
//
// Arguments:
// CDevice * pDevice - Pointer to device whose ports should be unbound.
// BOOL fRemote - TRUE if clearing remote server, FALSE if clearing
// local server.
//
// Returns: None.
//=============================================================================
void CNATHelpPAST::ClearAllPASTServerRegisteredPorts(CDevice * const pDevice,
const BOOL fRemote)
{
CBilink * pBilink;
CRegisteredPort * pRegisteredPort;
DPFX(DPFPREP, 7, "(0x%p) Parameters: (0x%p, %i)", this, pDevice, fRemote);
pBilink = pDevice->m_blOwnedRegPorts.GetNext();
while (pBilink != &pDevice->m_blOwnedRegPorts)
{
pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
if (pRegisteredPort->GetPASTBindID(fRemote) != 0)
{
DNASSERT(pRegisteredPort->HasPASTPublicAddressesArray(fRemote));
DNASSERT(! pRegisteredPort->IsPASTPortUnavailable(fRemote));
if (pDevice->IsPASTPublicAddressAvailable(fRemote))
{
DPFX(DPFPREP, 1, "Registered port 0x%p losing %s PAST public address.",
pRegisteredPort, ((fRemote) ? _T("remote") : _T("local")));
//
// Let the user know next time GetCaps is called.
//
this->m_dwFlags |= NATHELPPASTOBJ_ADDRESSESCHANGED;
}
else
{
DPFX(DPFPREP, 1, "Registered port 0x%p losing %s PAST binding, but it didn't have a public address.",
pRegisteredPort, ((fRemote) ? _T("remote") : _T("local")));
}
pRegisteredPort->ClearPASTPublicAddresses(fRemote);
pRegisteredPort->SetPASTBindID(0, fRemote);
DNASSERT(this->m_dwNumLeases > 0);
this->m_dwNumLeases--;
DPFX(DPFPREP, 7, "%s PAST lease for 0x%p cleared, total num leases = %u.",
((fRemote) ? _T("Remote") : _T("Local")), pRegisteredPort, this->m_dwNumLeases);
}
else
{
DNASSERT(! pRegisteredPort->HasPASTPublicAddressesArray(fRemote));
//
// Port no longer unavailable (if it had been).
//
pRegisteredPort->NoteNotPASTPortUnavailable(fRemote);
}
pBilink = pBilink->GetNext();
}
DPFX(DPFPREP, 7, "(0x%p) Leave", this);
} // CNATHelpPAST::ClearAllPASTServerRegisteredPorts
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::ExpireOldCachedMappings"
//=============================================================================
// CNATHelpPAST::ExpireOldCachedMappings
//-----------------------------------------------------------------------------
//
// Description: Removes any cached mappings for any device which has
// expired.
//
// The object lock is assumed to be held.
//
// Arguments: None.
//
// Returns: None.
//=============================================================================
void CNATHelpPAST::ExpireOldCachedMappings(void)
{
DWORD dwCurrentTime;
CBilink * pBilinkDevice;
CDevice * pDevice;
CBilink * pCachedMaps;
CBilink * pBilinkCacheMap;
CCacheMap * pCacheMap;
DPFX(DPFPREP, 7, "(0x%p) Enter", this);
dwCurrentTime = timeGetTime();
//
// Check the PAST cached mappings.
//
pBilinkDevice = this->m_blDevices.GetNext();
while (pBilinkDevice != &this->m_blDevices)
{
pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
//
// Check the remote PAST server mappings.
//
pCachedMaps = pDevice->GetPASTCachedMaps(TRUE);
pBilinkCacheMap = pCachedMaps->GetNext();
while (pBilinkCacheMap != pCachedMaps)
{
pCacheMap = CACHEMAP_FROM_BILINK(pBilinkCacheMap);
pBilinkCacheMap = pBilinkCacheMap->GetNext();
if ((int) (pCacheMap->GetExpirationTime() - dwCurrentTime) < 0)
{
DPFX(DPFPREP, 5, "Remote PAST server cached mapping 0x%p has expired.", pCacheMap);
pCacheMap->m_blList.RemoveFromList();
delete pCacheMap;
}
}
//
// Check the local PAST server mappings.
//
pCachedMaps = pDevice->GetPASTCachedMaps(FALSE);
pBilinkCacheMap = pCachedMaps->GetNext();
while (pBilinkCacheMap != pCachedMaps)
{
pCacheMap = CACHEMAP_FROM_BILINK(pBilinkCacheMap);
pBilinkCacheMap = pBilinkCacheMap->GetNext();
if ((int) (pCacheMap->GetExpirationTime() - dwCurrentTime) < 0)
{
DPFX(DPFPREP, 5, "Local PAST server cached mapping 0x%p has expired.", pCacheMap);
pCacheMap->m_blList.RemoveFromList();
delete pCacheMap;
}
}
pBilinkDevice = pBilinkDevice->GetNext();
}
DPFX(DPFPREP, 7, "(0x%p) Leave", this);
} // CNATHelpPAST::ExpireOldCachedMappings
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::RemoveAllPASTCachedMappings"
//=============================================================================
// CNATHelpPAST::RemoveAllPASTCachedMappings
//-----------------------------------------------------------------------------
//
// Description: Removes all cached mappings for a given device's local or
// remote PAST server.
//
// The object lock is assumed to be held.
//
// Arguments:
// CDevice * pDevice - Pointer to device whose cache should be emptied.
// BOOL fRemote - TRUE if emptying remote server, FALSE if emptying
// local server.
//
// Returns: None.
//=============================================================================
void CNATHelpPAST::RemoveAllPASTCachedMappings(CDevice * const pDevice,
const BOOL fRemote)
{
CBilink * pCachedMaps;
CBilink * pBilink;
CCacheMap * pCacheMap;
pCachedMaps = pDevice->GetPASTCachedMaps(fRemote);
pBilink = pCachedMaps->GetNext();
while (pBilink != pCachedMaps)
{
pCacheMap = CACHEMAP_FROM_BILINK(pBilink);
pBilink = pBilink->GetNext();
DPFX(DPFPREP, 5, "Removing cached mapping 0x%p.", pCacheMap);
pCacheMap->m_blList.RemoveFromList();
delete pCacheMap;
}
} // CNATHelpPAST::RemoveAllPASTCachedMappings
#ifdef DBG
#undef DPF_MODNAME
#define DPF_MODNAME "CNATHelpPAST::DebugPrintCurrentStatus"
//=============================================================================
// CNATHelpPAST::DebugPrintCurrentStatus
//-----------------------------------------------------------------------------
//
// Description: Prints all the devices and mappings to the debug log
// routines.
//
// The object lock is assumed to be held.
//
// Arguments: None.
//
// Returns: None.
//=============================================================================
void CNATHelpPAST::DebugPrintCurrentStatus(void)
{
CBilink * pBilinkDevice;
CBilink * pBilinkRegisteredPort;
CDevice * pDevice;
CRegisteredPort * pRegisteredPort;
IN_ADDR inaddrTemp;
DWORD dwTemp;
SOCKADDR_IN * pasaddrinPrivate;
SOCKADDR_IN * pasaddrinRemotePASTPublic;
SOCKADDR_IN * pasaddrinLocalPASTPublic;
DPFX(DPFPREP, 3, "Object flags = 0x%08x", this->m_dwFlags);
pBilinkDevice = this->m_blDevices.GetNext();
while (pBilinkDevice != &this->m_blDevices)
{
pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
DPFX(DPFPREP, 3, "Device 0x%p (%u.%u.%u.%u):",
pDevice,
inaddrTemp.S_un.S_un_b.s_b1,
inaddrTemp.S_un.S_un_b.s_b2,
inaddrTemp.S_un.S_un_b.s_b3,
inaddrTemp.S_un.S_un_b.s_b4);
//
// Print the gateway information. We may not have detected it yet,
// that's okay.
//
if (pDevice->IsPrimaryDevice())
{
DPFX(DPFPREP, 3, " Primary device.");
}
else if (pDevice->IsSecondaryDevice())
{
DPFX(DPFPREP, 3, " Secondary device.");
}
else if (pDevice->HasNoGateway())
{
DPFX(DPFPREP, 3, " Has no gateway.");
}
else
{
DPFX(DPFPREP, 3, " No gateway information known.");
}
if (pDevice->GetPASTClientID(TRUE) != 0)
{
inaddrTemp.S_un.S_addr = pDevice->GetRemotePASTServerAddressV4();
DPFX(DPFPREP, 3, " Remote ICS PAST server %u.%u.%u.%u, client ID is %u.",
inaddrTemp.S_un.S_un_b.s_b1,
inaddrTemp.S_un.S_un_b.s_b2,
inaddrTemp.S_un.S_un_b.s_b3,
inaddrTemp.S_un.S_un_b.s_b4,
pDevice->GetPASTClientID(TRUE));
}
if (pDevice->HasLocalICSPASTServer())
{
DPFX(DPFPREP, 3, " Local ICS PAST server, client ID is %u.",
pDevice->GetPASTClientID(FALSE));
}
if (pDevice->HasLocalPFWOnlyPASTServer())
{
DPFX(DPFPREP, 3, " Local PFW-only PAST server, client ID is %u.",
pDevice->GetPASTClientID(FALSE));
}
if (pDevice->m_blOwnedRegPorts.IsEmpty())
{
DPFX(DPFPREP, 3, " No registered port mappings.");
}
else
{
DPFX(DPFPREP, 3, " Registered port mappings:");
pBilinkRegisteredPort = pDevice->m_blOwnedRegPorts.GetNext();
while (pBilinkRegisteredPort != &pDevice->m_blOwnedRegPorts)
{
pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
if ((pDevice->GetPASTClientID(TRUE) != 0) &&
(pDevice->IsPASTPublicAddressAvailable(TRUE)) &&
(! pRegisteredPort->IsPASTPortUnavailable(TRUE)))
{
if (pRegisteredPort->HasPASTPublicAddressesArray(TRUE))
{
pasaddrinRemotePASTPublic = pRegisteredPort->GetPASTPublicAddressesArray(TRUE);
}
else
{
pasaddrinRemotePASTPublic = NULL;
}
}
else
{
pasaddrinRemotePASTPublic = NULL;
}
if ((pDevice->GetPASTClientID(FALSE) != 0) &&
(pDevice->IsPASTPublicAddressAvailable(FALSE)) &&
(! pRegisteredPort->IsPASTPortUnavailable(FALSE)))
{
if (pRegisteredPort->HasPASTPublicAddressesArray(FALSE))
{
pasaddrinLocalPASTPublic = pRegisteredPort->GetPASTPublicAddressesArray(FALSE);
}
else
{
pasaddrinLocalPASTPublic = NULL;
}
}
else
{
pasaddrinLocalPASTPublic = NULL;
}
DPFX(DPFPREP, 3, " Registered port 0x%p:",
pRegisteredPort);
for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
{
//
// Print private address.
//
DPFX(DPFPREP, 3, " %u-\tPrivate = %u.%u.%u.%u:%u",
dwTemp,
pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b1,
pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b2,
pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b3,
pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b4,
NTOHS(pasaddrinPrivate[dwTemp].sin_port));
//
// Print flags.
//
DPFX(DPFPREP, 3, " \tFlags = 0x%lx",
pRegisteredPort->GetFlags());
//
// Print remote PAST information.
//
if (pasaddrinRemotePASTPublic != NULL)
{
DPFX(DPFPREP, 3, " \tRemote PAST = %u.%u.%u.%u:%u, lease %u expires at %u",
pasaddrinRemotePASTPublic[dwTemp].sin_addr.S_un.S_un_b.s_b1,
pasaddrinRemotePASTPublic[dwTemp].sin_addr.S_un.S_un_b.s_b2,
pasaddrinRemotePASTPublic[dwTemp].sin_addr.S_un.S_un_b.s_b3,
pasaddrinRemotePASTPublic[dwTemp].sin_addr.S_un.S_un_b.s_b4,
NTOHS(pasaddrinRemotePASTPublic[dwTemp].sin_port),
pRegisteredPort->GetPASTBindID(TRUE),
pRegisteredPort->GetPASTLeaseExpiration(TRUE));
}
else if (pRegisteredPort->GetPASTBindID(TRUE) != 0)
{
//
// We should have caught address availability up above.
//
DNASSERT(! pDevice->IsPASTPublicAddressAvailable(TRUE));
DPFX(DPFPREP, 3, " \tRemote PAST = no public address (lease %u expires at %u)",
pRegisteredPort->GetPASTBindID(TRUE),
pRegisteredPort->GetPASTLeaseExpiration(TRUE));
}
else if (pRegisteredPort->IsPASTPortUnavailable(TRUE))
{
DPFX(DPFPREP, 3, " \tRemote PAST = port unavailable");
}
else if (pDevice->GetPASTClientID(TRUE) != 0)
{
DPFX(DPFPREP, 3, " \tRemote PAST = not registered");
}
else
{
//
// No remote PAST server.
//
}
//
// Print local PAST information.
//
if (pasaddrinLocalPASTPublic != NULL)
{
DPFX(DPFPREP, 3, " \tLocal PAST = %u.%u.%u.%u:%u, lease %u expires at %u",
pasaddrinLocalPASTPublic[dwTemp].sin_addr.S_un.S_un_b.s_b1,
pasaddrinLocalPASTPublic[dwTemp].sin_addr.S_un.S_un_b.s_b2,
pasaddrinLocalPASTPublic[dwTemp].sin_addr.S_un.S_un_b.s_b3,
pasaddrinLocalPASTPublic[dwTemp].sin_addr.S_un.S_un_b.s_b4,
NTOHS(pasaddrinLocalPASTPublic[dwTemp].sin_port),
pRegisteredPort->GetPASTBindID(FALSE),
pRegisteredPort->GetPASTLeaseExpiration(FALSE));
}
else if (pRegisteredPort->GetPASTBindID(FALSE) != 0)
{
//
// We should have caught address availability up above.
//
DNASSERT(! pDevice->IsPASTPublicAddressAvailable(FALSE));
DPFX(DPFPREP, 3, " \tLocal PAST = no public address (lease %u expires at %u)",
pRegisteredPort->GetPASTBindID(FALSE),
pRegisteredPort->GetPASTLeaseExpiration(FALSE));
}
else if (pRegisteredPort->IsPASTPortUnavailable(FALSE))
{
DPFX(DPFPREP, 3, " \tLocal PAST = port unavailable");
}
else if (pDevice->GetPASTClientID(TRUE) != 0)
{
DPFX(DPFPREP, 3, " \tLocal PAST = not registered");
}
else
{
//
// No local PAST server.
//
}
}
pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
}
}
pBilinkDevice = pBilinkDevice->GetNext();
}
if (this->m_blUnownedPorts.IsEmpty())
{
DPFX(DPFPREP, 3, "No unowned registered port mappings.");
}
else
{
DPFX(DPFPREP, 3, "Unowned registered port mappings:");
pBilinkRegisteredPort = this->m_blUnownedPorts.GetNext();
while (pBilinkRegisteredPort != &this->m_blUnownedPorts)
{
pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
DNASSERT(pRegisteredPort->GetOwningDevice() == NULL);
DNASSERT(! (pRegisteredPort->HasPASTPublicAddressesArray(TRUE)));
DNASSERT(! (pRegisteredPort->HasPASTPublicAddressesArray(FALSE)));
DNASSERT(pRegisteredPort->GetPASTBindID(TRUE) == 0);
DNASSERT(pRegisteredPort->GetPASTBindID(FALSE) == 0);
DPFX(DPFPREP, 3, " Registered port 0x%p:", pRegisteredPort);
for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
{
//
// Print private address.
//
DPFX(DPFPREP, 3, " %u-\tPrivate = %u.%u.%u.%u:%u",
dwTemp,
pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b1,
pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b2,
pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b3,
pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b4,
NTOHS(pasaddrinPrivate[dwTemp].sin_port));
//
// Print flags.
//
DPFX(DPFPREP, 3, " \tFlags = 0x%lx",
pRegisteredPort->GetFlags());
}
pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
}
}
} // CNATHelpPAST::DebugPrintCurrentStatus
#endif // DBG