12628 lines
329 KiB
C++
12628 lines
329 KiB
C++
/***************************************************************************
|
|
*
|
|
* 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
|