/*************************************************************************** * * 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