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

24372 lines
653 KiB

  1. /***************************************************************************
  2. *
  3. * Copyright (C) 2001-2002 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: dpnhupnpintfobj.cpp
  6. *
  7. * Content: DPNHUPNP main interface object class.
  8. *
  9. * History:
  10. * Date By Reason
  11. * ======== ======== =========
  12. * 04/16/01 VanceO Split DPNATHLP into DPNHUPNP and DPNHPAST.
  13. *
  14. ***************************************************************************/
  15. #include "dpnhupnpi.h"
  16. //=============================================================================
  17. // Definitions
  18. //=============================================================================
  19. #define ACTIVE_MAPPING_VERSION 2 // version identifier for active mapping registry data
  20. #define MAX_LONG_LOCK_WAITING_THREADS 0xFFFF // that's a lot of simultaneous threads!
  21. #define UPNP_SEARCH_MESSAGE_INTERVAL 499 // how often discovery multicast messages should be sent, in case of packet loss (note Win9x errata for values 500-1000ms)
  22. #define UPNP_DGRAM_RECV_BUFFER_SIZE 1500
  23. #define UPNP_STREAM_RECV_BUFFER_INITIAL_SIZE (4 * 1024) // 4 K, must be less than MAX_RECEIVE_BUFFER_SIZE
  24. #define MAX_UPNP_HEADER_LENGTH UPNP_STREAM_RECV_BUFFER_INITIAL_SIZE
  25. #define LEASE_RENEW_TIME 120000 // renew if less than 2 minutes remaining
  26. #define FAKE_PORT_LEASE_TIME 300000 // 5 minutes
  27. #define IOCOMPLETE_WAIT_INTERVAL 100 // 100 ms between attempts
  28. #define MAX_NUM_IOCOMPLETE_WAITS 10 // wait at most 1 second
  29. #define MAX_NUM_HOMENETUNMAP_ATTEMPTS 3 // 3 tries
  30. #define HOMENETUNMAP_SLEEP_FACTOR 10 // 10 ms, 20 ms, 30 ms
  31. #define MAX_UPNP_MAPPING_DESCRIPTION_SIZE 256 // 255 characters + NULL termination
  32. #define MAX_INSTANCENAMEDOBJECT_SIZE 64
  33. #define INSTANCENAMEDOBJECT_FORMATSTRING _T("DPNHUPnP Instance %u")
  34. #define GUID_STRING_LENGTH 42 // maximum length of "{xxx...}" guid string, without NULL termination
  35. #define PORTMAPPINGPROTOCOL_TCP 6
  36. #define PORTMAPPINGPROTOCOL_UDP 17
  37. #define MAX_RESERVED_PORT 1024
  38. #define MAX_NUM_INSTANCE_EVENT_ATTEMPTS 5
  39. #define MAX_NUM_RANDOM_PORT_TRIES 5
  40. #define MAX_NUM_TIMEWAITCONNECT_RETRIES 20 // allow a decent number since these are detected quickly and without net traffic
  41. #ifdef DBG
  42. #define MAX_TRANSACTION_LOG_SIZE (5 * 1024 * 1024) // 5 MB
  43. #endif // DBG
  44. #ifndef DPNBUILD_NOWINSOCK2
  45. //=============================================================================
  46. // WinSock 1 version of IP options
  47. //=============================================================================
  48. #define IP_MULTICAST_IF_WINSOCK1 2
  49. #define IP_MULTICAST_TTL_WINSOCK1 3
  50. #define IP_TTL_WINSOCK1 7
  51. #endif // ! DPNBUILD_NOWINSOCK2
  52. //=============================================================================
  53. // Macros
  54. //=============================================================================
  55. //#ifdef _X86
  56. #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
  57. #define NTOHS(x) ( (((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00) )
  58. #define HTONS(x) NTOHS(x)
  59. //#endif _X86
  60. //=============================================================================
  61. // HTTP/SSDP/SOAP/UPnP header strings (from upnpmsgs.h)
  62. //=============================================================================
  63. const char * c_szResponseHeaders[] =
  64. {
  65. //
  66. // Headers used in discovery response
  67. //
  68. "CACHE-CONTROL",
  69. "DATE",
  70. "EXT",
  71. "LOCATION",
  72. "SERVER",
  73. "ST",
  74. "USN",
  75. //
  76. // Additional headers used in description response
  77. //
  78. "CONTENT-LANGUAGE",
  79. "CONTENT-LENGTH",
  80. "CONTENT-TYPE",
  81. "TRANSFER-ENCODING",
  82. //
  83. // Other known headers
  84. //
  85. "HOST",
  86. "NT",
  87. "NTS",
  88. "MAN",
  89. "MX",
  90. "AL",
  91. "CALLBACK",
  92. "TIMEOUT",
  93. "SCOPE",
  94. "SID",
  95. "SEQ",
  96. };
  97. //=============================================================================
  98. // Pre-built UPnP message strings (from upnpmsgs.h)
  99. //=============================================================================
  100. const char c_szUPnPMsg_Discover_Service_WANIPConnection[] = "M-SEARCH * " HTTP_VERSION EOL
  101. "HOST: " UPNP_DISCOVERY_MULTICAST_ADDRESS ":" UPNP_PORT_A EOL
  102. "MAN: \"ssdp:discover\"" EOL
  103. "MX: 2" EOL
  104. "ST: " URI_SERVICE_WANIPCONNECTION_A EOL
  105. EOL;
  106. const char c_szUPnPMsg_Discover_Service_WANPPPConnection[] = "M-SEARCH * " HTTP_VERSION EOL
  107. "HOST: " UPNP_DISCOVERY_MULTICAST_ADDRESS ":" UPNP_PORT_A EOL
  108. "MAN: \"ssdp:discover\"" EOL
  109. "MX: 2" EOL
  110. "ST: " URI_SERVICE_WANPPPCONNECTION_A EOL
  111. EOL;
  112. //
  113. // The disclaimer:
  114. //
  115. // A UPnP device may implement both the WANIPConnection and WANPPPConnection
  116. // services. We do not have any fancy logic to pick one, we just use the first
  117. // device that responds to our discovery requests, and use the first matching
  118. // service we encounter in the device description XML.
  119. //
  120. // Additionally, future UPnP devices may wish to present multiple device or
  121. // service instances with the intention that one client gets control of the
  122. // entire instance (additional clients would need to use a different instance).
  123. // It's not clear to me what a UPnP device (or client, for that matter) would
  124. // really gain by having such a setup. I imagine a new error code would have
  125. // to be returned whenever a client tried to control an instance that another
  126. // client already owned (don't ask me how it would know that, by picking the
  127. // first user or selectively responding to discovery requests, I guess).
  128. // Regardless, we do not currently support that. As noted above, we pick the
  129. // first instance and run with it.
  130. //
  131. //
  132. // Topmost <?xml> tag is considered optional for all XML and is ignored.
  133. //
  134. //
  135. // This solution assumes InternetGatewayDevice (not WANDevice or
  136. // WANConnectionDevice) will be the topmost item in the response. This is based
  137. // on the following UPnP spec excerpt:
  138. //
  139. // "Note that a single physical device may include multiple logical devices.
  140. // Multiple logical devices can be modeled as a single root device with
  141. // embedded devices (and services) or as multiple root devices (perhaps with
  142. // no embedded devices). In the former case, there is one UPnP device
  143. // description for the root device, and that device description contains a
  144. // description for all embedded devices. In the latter case, there are
  145. // multiple UPnP device descriptions, one for each root device."
  146. //
  147. const char * c_szElementStack_service[] =
  148. {
  149. "root",
  150. "device", // InternetGatewayDevice
  151. "deviceList",
  152. "device", // WANDevice
  153. "deviceList",
  154. "device", // WANConnectionDevice
  155. "serviceList",
  156. "service"
  157. };
  158. /*
  159. const char * c_szElementStack_QueryStateVariableResponse[] =
  160. {
  161. "Envelope",
  162. "Body",
  163. CONTROL_QUERYSTATEVARIABLE_A CONTROL_RESPONSESUFFIX_A
  164. };
  165. */
  166. const char * c_szElementStack_GetExternalIPAddressResponse[] =
  167. {
  168. "Envelope",
  169. "Body",
  170. ACTION_GETEXTERNALIPADDRESS_A CONTROL_RESPONSESUFFIX_A
  171. };
  172. const char * c_szElementStack_AddPortMappingResponse[] =
  173. {
  174. "Envelope",
  175. "Body",
  176. ACTION_ADDPORTMAPPING_A CONTROL_RESPONSESUFFIX_A
  177. };
  178. const char * c_szElementStack_GetSpecificPortMappingEntryResponse[] =
  179. {
  180. "Envelope",
  181. "Body",
  182. ACTION_GETSPECIFICPORTMAPPINGENTRY_A CONTROL_RESPONSESUFFIX_A
  183. };
  184. const char * c_szElementStack_DeletePortMappingResponse[] =
  185. {
  186. "Envelope",
  187. "Body",
  188. ACTION_DELETEPORTMAPPING_A CONTROL_RESPONSESUFFIX_A
  189. };
  190. const char * c_szElementStack_ControlResponseFailure[] =
  191. {
  192. "Envelope",
  193. "Body",
  194. "Fault",
  195. "detail",
  196. "UPnPError"
  197. };
  198. #ifdef WINNT
  199. //=============================================================================
  200. // Related UPnP services
  201. //=============================================================================
  202. TCHAR * c_tszUPnPServices[] =
  203. {
  204. _T("SSDPSRV"), // SSDP Discovery Service
  205. _T("UPNPHOST"), // Universal Plug and Play Device Host - we key off this even though it's for device hosts instead of control points
  206. };
  207. #endif // WINNT
  208. //=============================================================================
  209. // Local structures
  210. //=============================================================================
  211. typedef struct _CONTROLRESPONSEPARSECONTEXT
  212. {
  213. CONTROLRESPONSETYPE ControlResponseType; // type of control response expected
  214. CUPnPDevice * pUPnPDevice; // pointer to UPnP device being used
  215. DWORD dwHTTPResponseCode; // HTTP response code for this message
  216. PUPNP_CONTROLRESPONSE_INFO pControlResponseInfo; // place to info returned in control response
  217. } CONTROLRESPONSEPARSECONTEXT, * PCONTROLRESPONSEPARSECONTEXT;
  218. typedef struct _DPNHACTIVEFIREWALLMAPPING
  219. {
  220. DWORD dwVersion; // version identifier for this mapping
  221. DWORD dwInstanceKey; // key identifying DPNHUPNP instance that created this mapping
  222. DWORD dwFlags; // flags describing port being registered
  223. DWORD dwAddressV4; // address being mapped
  224. WORD wPort; // port being mapped
  225. } DPNHACTIVEFIREWALLMAPPING, * PDPNHACTIVEFIREWALLMAPPING;
  226. typedef struct _DPNHACTIVENATMAPPING
  227. {
  228. DWORD dwVersion; // version identifier for this mapping
  229. DWORD dwInstanceKey; // key identifying DPNHUPNP instance that created this mapping
  230. DWORD dwUPnPDeviceID; // identifier for particular UPnP device corresponding to this mapping (meaningful only to owning instance)
  231. DWORD dwFlags; // flags describing port being registered
  232. DWORD dwInternalAddressV4; // internal client address being mapped
  233. WORD wInternalPort; // internal client port being mapped
  234. DWORD dwExternalAddressV4; // external public address that was mapped
  235. WORD wExternalPort; // external public port that was mapped
  236. } DPNHACTIVENATMAPPING, * PDPNHACTIVENATMAPPING;
  237. //=============================================================================
  238. // Local functions
  239. //=============================================================================
  240. VOID strtrim(CHAR ** pszStr);
  241. #ifdef WINCE
  242. void GetExeName(WCHAR * wszPath);
  243. #endif // WINCE
  244. #undef DPF_MODNAME
  245. #define DPF_MODNAME "CNATHelpUPnP::CNATHelpUPnP"
  246. //=============================================================================
  247. // CNATHelpUPnP constructor
  248. //-----------------------------------------------------------------------------
  249. //
  250. // Description: Initializes the new CNATHelpUPnP object.
  251. //
  252. // Arguments:
  253. // BOOL fNotCreatedWithCOM - TRUE if this object is being instantiated
  254. // without COM, FALSE if it is through COM.
  255. //
  256. // Returns: None (the object).
  257. //=============================================================================
  258. CNATHelpUPnP::CNATHelpUPnP(const BOOL fNotCreatedWithCOM)
  259. {
  260. this->m_blList.Initialize();
  261. this->m_Sig[0] = 'N';
  262. this->m_Sig[1] = 'A';
  263. this->m_Sig[2] = 'T';
  264. this->m_Sig[3] = 'H';
  265. this->m_lRefCount = 1; // someone must have a pointer to this object
  266. if (fNotCreatedWithCOM)
  267. {
  268. this->m_dwFlags = NATHELPUPNPOBJ_NOTCREATEDWITHCOM;
  269. }
  270. else
  271. {
  272. this->m_dwFlags = 0;
  273. }
  274. this->m_hLongLockSemaphore = NULL;
  275. this->m_lNumLongLockWaitingThreads = 0;
  276. this->m_dwLockThreadID = 0;
  277. #ifndef DPNBUILD_NOWINSOCK2
  278. this->m_hAlertEvent = NULL;
  279. this->m_hAlertIOCompletionPort = NULL;
  280. this->m_dwAlertCompletionKey = 0;
  281. #endif // ! DPNBUILD_NOWINSOCK2
  282. this->m_blDevices.Initialize();
  283. this->m_blRegisteredPorts.Initialize();
  284. this->m_blUnownedPorts.Initialize();
  285. this->m_dwLastUpdateServerStatusTime = 0;
  286. this->m_dwNextPollInterval = 0;
  287. this->m_dwNumLeases = 0;
  288. this->m_dwEarliestLeaseExpirationTime = 0;
  289. this->m_blUPnPDevices.Initialize();
  290. this->m_dwInstanceKey = 0;
  291. this->m_dwCurrentUPnPDeviceID = 0;
  292. this->m_hMappingStillActiveNamedObject = NULL;
  293. #ifndef DPNBUILD_NOWINSOCK2
  294. this->m_hIpHlpApiDLL = NULL;
  295. this->m_pfnGetAdaptersInfo = NULL;
  296. this->m_pfnGetIpForwardTable = NULL;
  297. this->m_pfnGetBestRoute = NULL;
  298. this->m_hRasApi32DLL = NULL;
  299. this->m_pfnRasGetEntryHrasconnW = NULL;
  300. this->m_pfnRasGetProjectionInfo = NULL;
  301. this->m_sIoctls = INVALID_SOCKET;
  302. this->m_polAddressListChange = NULL;
  303. #endif // ! DPNBUILD_NOWINSOCK2
  304. this->m_hWinSockDLL = NULL;
  305. this->m_pfnWSAStartup = NULL;
  306. this->m_pfnWSACleanup = NULL;
  307. this->m_pfnWSAGetLastError = NULL;
  308. this->m_pfnsocket = NULL;
  309. this->m_pfnclosesocket = NULL;
  310. this->m_pfnbind = NULL;
  311. this->m_pfnsetsockopt = NULL;
  312. this->m_pfngetsockname = NULL;
  313. this->m_pfnselect = NULL;
  314. this->m_pfn__WSAFDIsSet = NULL;
  315. this->m_pfnrecvfrom = NULL;
  316. this->m_pfnsendto = NULL;
  317. this->m_pfngethostname = NULL;
  318. this->m_pfngethostbyname = NULL;
  319. this->m_pfninet_addr = NULL;
  320. #ifndef DPNBUILD_NOWINSOCK2
  321. this->m_pfnWSASocketA = NULL;
  322. this->m_pfnWSAIoctl = NULL;
  323. this->m_pfnWSAGetOverlappedResult = NULL;
  324. #endif // ! DPNBUILD_NOWINSOCK2
  325. this->m_pfnioctlsocket = NULL;
  326. this->m_pfnconnect = NULL;
  327. this->m_pfnshutdown = NULL;
  328. this->m_pfnsend = NULL;
  329. this->m_pfnrecv = NULL;
  330. #ifdef DBG
  331. this->m_pfngetsockopt = NULL;
  332. this->m_dwNumDeviceAdds = 0;
  333. this->m_dwNumDeviceRemoves = 0;
  334. this->m_dwNumServerFailures = 0;
  335. #endif // DBG
  336. } // CNATHelpUPnP::CNATHelpUPnP
  337. #undef DPF_MODNAME
  338. #define DPF_MODNAME "CNATHelpUPnP::~CNATHelpUPnP"
  339. //=============================================================================
  340. // CNATHelpUPnP destructor
  341. //-----------------------------------------------------------------------------
  342. //
  343. // Description: Frees the CNATHelpUPnP object.
  344. //
  345. // Arguments: None.
  346. //
  347. // Returns: None.
  348. //=============================================================================
  349. CNATHelpUPnP::~CNATHelpUPnP(void)
  350. {
  351. DPFX(DPFPREP, 8, "(0x%p) NumDeviceAdds = %u, NumDeviceRemoves = %u, NumServerFailures = %u",
  352. this, this->m_dwNumDeviceAdds, this->m_dwNumDeviceRemoves,
  353. this->m_dwNumServerFailures);
  354. DNASSERT(this->m_blList.IsEmpty());
  355. DNASSERT(this->m_lRefCount == 0);
  356. DNASSERT((this->m_dwFlags & ~NATHELPUPNPOBJ_NOTCREATEDWITHCOM) == 0);
  357. DNASSERT(this->m_hLongLockSemaphore == NULL);
  358. DNASSERT(this->m_lNumLongLockWaitingThreads == 0);
  359. DNASSERT(this->m_dwLockThreadID == 0);
  360. #ifndef DPNBUILD_NOWINSOCK2
  361. DNASSERT(this->m_hAlertEvent == NULL);
  362. DNASSERT(this->m_hAlertIOCompletionPort == NULL);
  363. #endif // ! DPNBUILD_NOWINSOCK2
  364. DNASSERT(this->m_blDevices.IsEmpty());
  365. DNASSERT(this->m_blRegisteredPorts.IsEmpty());
  366. DNASSERT(this->m_blUnownedPorts.IsEmpty());
  367. DNASSERT(this->m_dwNumLeases == 0);
  368. DNASSERT(this->m_blUPnPDevices.IsEmpty());
  369. DNASSERT(this->m_hMappingStillActiveNamedObject == NULL);
  370. #ifndef DPNBUILD_NOWINSOCK2
  371. DNASSERT(this->m_hIpHlpApiDLL == NULL);
  372. DNASSERT(this->m_hRasApi32DLL == NULL);
  373. DNASSERT(this->m_hWinSockDLL == NULL);
  374. DNASSERT(this->m_sIoctls == INVALID_SOCKET);
  375. DNASSERT(this->m_polAddressListChange == NULL);
  376. #endif // ! DPNBUILD_NOWINSOCK2
  377. //
  378. // For grins, change the signature before deleting the object.
  379. //
  380. this->m_Sig[3] = 'h';
  381. } // CNATHelpUPnP::~CNATHelpUPnP
  382. #undef DPF_MODNAME
  383. #define DPF_MODNAME "CNATHelpUPnP::QueryInterface"
  384. //=============================================================================
  385. // CNATHelpUPnP::QueryInterface
  386. //-----------------------------------------------------------------------------
  387. //
  388. // Description: Retrieves a new reference for an interfaces supported by this
  389. // CNATHelpUPnP object.
  390. //
  391. // Arguments:
  392. // REFIID riid - Reference to interface ID GUID.
  393. // LPVOID * ppvObj - Place to store pointer to object.
  394. //
  395. // Returns: HRESULT
  396. // S_OK - Returning a valid interface pointer.
  397. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  398. // DPNHERR_INVALIDPOINTER - The destination pointer is invalid.
  399. // E_NOINTERFACE - Invalid interface was specified.
  400. //=============================================================================
  401. STDMETHODIMP CNATHelpUPnP::QueryInterface(REFIID riid, LPVOID * ppvObj)
  402. {
  403. HRESULT hr = DPNH_OK;
  404. DPFX(DPFPREP, 3, "(0x%p) Parameters: (REFIID, 0x%p)", this, ppvObj);
  405. //
  406. // Validate the object.
  407. //
  408. if (! this->IsValidObject())
  409. {
  410. DPFX(DPFPREP, 0, "Invalid NATHelper object!");
  411. hr = DPNHERR_INVALIDOBJECT;
  412. goto Failure;
  413. }
  414. //
  415. // Validate the parameters.
  416. //
  417. if ((! IsEqualIID(riid, IID_IUnknown)) &&
  418. (! IsEqualIID(riid, IID_IDirectPlayNATHelp)))
  419. {
  420. DPFX(DPFPREP, 0, "Unsupported interface!");
  421. hr = E_NOINTERFACE;
  422. goto Failure;
  423. }
  424. if ((ppvObj == NULL) ||
  425. (IsBadWritePtr(ppvObj, sizeof(void*))))
  426. {
  427. DPFX(DPFPREP, 0, "Invalid interface pointer specified!");
  428. hr = DPNHERR_INVALIDPOINTER;
  429. goto Failure;
  430. }
  431. //
  432. // Add a reference, and return the interface pointer (which is actually
  433. // just the object pointer, they line up because CNATHelpUPnP inherits from
  434. // the interface declaration).
  435. //
  436. this->AddRef();
  437. (*ppvObj) = this;
  438. Exit:
  439. DPFX(DPFPREP, 3, "(0x%p) Returning: [0x%lx]", this, hr);
  440. return hr;
  441. Failure:
  442. goto Exit;
  443. } // CNATHelpUPnP::QueryInterface
  444. #undef DPF_MODNAME
  445. #define DPF_MODNAME "CNATHelpUPnP::AddRef"
  446. //=============================================================================
  447. // CNATHelpUPnP::AddRef
  448. //-----------------------------------------------------------------------------
  449. //
  450. // Description: Adds a reference to this CNATHelpUPnP object.
  451. //
  452. // Arguments: None.
  453. //
  454. // Returns: New refcount.
  455. //=============================================================================
  456. STDMETHODIMP_(ULONG) CNATHelpUPnP::AddRef(void)
  457. {
  458. LONG lRefCount;
  459. DNASSERT(this->IsValidObject());
  460. //
  461. // There must be at least 1 reference to this object, since someone is
  462. // calling AddRef.
  463. //
  464. DNASSERT(this->m_lRefCount > 0);
  465. lRefCount = InterlockedIncrement(&this->m_lRefCount);
  466. DPFX(DPFPREP, 3, "[0x%p] RefCount [0x%lx]", this, lRefCount);
  467. return lRefCount;
  468. } // CNATHelpUPnP::AddRef
  469. #undef DPF_MODNAME
  470. #define DPF_MODNAME "CNATHelpUPnP::Release"
  471. //=============================================================================
  472. // CNATHelpUPnP::Release
  473. //-----------------------------------------------------------------------------
  474. //
  475. // Description: Removes a reference to this CNATHelpUPnP object. When the
  476. // refcount reaches 0, this object is destroyed.
  477. // You must NULL out your pointer to this object after calling
  478. // this function.
  479. //
  480. // Arguments: None.
  481. //
  482. // Returns: New refcount.
  483. //=============================================================================
  484. STDMETHODIMP_(ULONG) CNATHelpUPnP::Release(void)
  485. {
  486. LONG lRefCount;
  487. DNASSERT(this->IsValidObject());
  488. //
  489. // There must be at least 1 reference to this object, since someone is
  490. // calling Release.
  491. //
  492. DNASSERT(this->m_lRefCount > 0);
  493. lRefCount = InterlockedDecrement(&this->m_lRefCount);
  494. //
  495. // Was that the last reference? If so, we're going to destroy this object.
  496. //
  497. if (lRefCount == 0)
  498. {
  499. DPFX(DPFPREP, 3, "[0x%p] RefCount hit 0, destroying object.", this);
  500. //
  501. // First pull it off the global list.
  502. //
  503. DNEnterCriticalSection(&g_csGlobalsLock);
  504. this->m_blList.RemoveFromList();
  505. DNASSERT(g_lOutstandingInterfaceCount > 0);
  506. g_lOutstandingInterfaceCount--; // update count so DLL can unload now works correctly
  507. DNLeaveCriticalSection(&g_csGlobalsLock);
  508. //
  509. // Make sure it's closed.
  510. //
  511. if (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED)
  512. {
  513. //
  514. // Assert so that the user can fix his/her broken code!
  515. //
  516. DNASSERT(! "DirectPlayNATHelpUPNP object being released without calling Close first!");
  517. //
  518. // Then go ahead and do the right thing. Ignore error, we can't do
  519. // much about it.
  520. //
  521. this->Close(0);
  522. }
  523. //
  524. // Then uninitialize the object.
  525. //
  526. this->UninitializeObject();
  527. //
  528. // Finally delete this (!) object.
  529. //
  530. delete this;
  531. }
  532. else
  533. {
  534. DPFX(DPFPREP, 3, "[0x%p] RefCount [0x%lx]", this, lRefCount);
  535. }
  536. return lRefCount;
  537. } // CNATHelpUPnP::Release
  538. #undef DPF_MODNAME
  539. #define DPF_MODNAME "CNATHelpUPnP::Initialize"
  540. //=============================================================================
  541. // CNATHelpUPnP::Initialize
  542. //-----------------------------------------------------------------------------
  543. //
  544. // Description: Prepares the object for use. No attempt is made to contact
  545. // any Internet gateway servers at this time. The user should
  546. // call GetCaps with the DPNHGETCAPS_UPDATESERVERSTATUS flag to
  547. // search for a server.
  548. //
  549. // Initialize must be called before using any other function,
  550. // and must be balanced with a call to Close. Initialize can only
  551. // be called once unless Close returns it to the uninitialized
  552. // state.
  553. //
  554. // One of DPNHINITIALIZE_DISABLEREMOTENATSUPPORT or
  555. // DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT may be specified,
  556. // but not both.
  557. //
  558. // Arguments:
  559. // DWORD dwFlags - Flags to use when initializing.
  560. //
  561. // Returns: HRESULT
  562. // DPNH_OK - Initialization was successful.
  563. // DPNHERR_ALREADYINITIALIZED - Initialize has already been called.
  564. // DPNHERR_GENERIC - An error occurred while initializing.
  565. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  566. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  567. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  568. // DPNHERR_OUTOFMEMORY - There is not enough memory to initialize.
  569. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  570. // thread.
  571. //=============================================================================
  572. STDMETHODIMP CNATHelpUPnP::Initialize(const DWORD dwFlags)
  573. {
  574. HRESULT hr;
  575. BOOL fHaveLock = FALSE;
  576. BOOL fSetFlags = FALSE;
  577. #ifndef WINCE
  578. OSVERSIONINFO osvi;
  579. #endif // ! WINCE
  580. BOOL fWinSockStarted = FALSE;
  581. WSADATA wsadata;
  582. int iError;
  583. #ifndef DPNBUILD_NOWINSOCK2
  584. SOCKADDR_IN saddrinTemp;
  585. #endif // ! DPNBUILD_NOWINSOCK2
  586. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  587. PSECURITY_ATTRIBUTES pSecurityAttributes;
  588. DWORD dwTry;
  589. #ifdef WINNT
  590. SID_IDENTIFIER_AUTHORITY SidIdentifierAuthorityWorld = SECURITY_WORLD_SID_AUTHORITY;
  591. PSID pSid = NULL;
  592. DWORD dwAclLength;
  593. ACL * pAcl = NULL;
  594. BYTE abSecurityDescriptorBuffer[SECURITY_DESCRIPTOR_MIN_LENGTH];
  595. SECURITY_ATTRIBUTES SecurityAttributes;
  596. #endif // WINNT
  597. #ifdef DBG
  598. DWORD dwError;
  599. #endif // DBG
  600. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%lx)", this, dwFlags);
  601. #ifndef WINCE
  602. //
  603. // Print info about the current build.
  604. //
  605. #ifdef WINNT
  606. DPFX(DPFPREP, 7, "Build type = NT, platform = %s",
  607. ((DNGetOSType() == VER_PLATFORM_WIN32_NT) ? _T("NT") : _T("9x")));
  608. #else // ! WINNT
  609. DPFX(DPFPREP, 7, "Build type = 9x, platform = %s, filedate = %s",
  610. ((DNGetOSType() == VER_PLATFORM_WIN32_NT) ? _T("NT") : _T("9x")));
  611. #endif // ! WINNT
  612. #endif // ! WINCE
  613. //
  614. // Validate the object.
  615. //
  616. if (! this->IsValidObject())
  617. {
  618. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  619. hr = DPNHERR_INVALIDOBJECT;
  620. //
  621. // Skip the failure cleanup code, we haven't set anything up.
  622. //
  623. goto Exit;
  624. }
  625. //
  626. // Validate the parameters.
  627. //
  628. if (dwFlags & ~(DPNHINITIALIZE_DISABLEGATEWAYSUPPORT | DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT))
  629. {
  630. DPFX(DPFPREP, 0, "Invalid flags specified!");
  631. hr = DPNHERR_INVALIDFLAGS;
  632. //
  633. // Skip the failure cleanup code, we haven't set anything up.
  634. //
  635. goto Exit;
  636. }
  637. //
  638. // Both flags cannot be specified at the same time. If the caller doesn't
  639. // want any NAT functionality, why use this object all?
  640. //
  641. if ((dwFlags & (DPNHINITIALIZE_DISABLEGATEWAYSUPPORT | DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT)) == (DPNHINITIALIZE_DISABLEGATEWAYSUPPORT | DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT))
  642. {
  643. DPFX(DPFPREP, 0, "Either DISABLEGATEWAYSUPPORT flag or DISABLELOCALFIREWALLSUPPORT flag can be used, but not both!");
  644. hr = DPNHERR_INVALIDFLAGS;
  645. //
  646. // Skip the failure cleanup code, we haven't set anything up.
  647. //
  648. goto Exit;
  649. }
  650. //
  651. // Attempt to take the lock, but be prepared for the re-entrancy error.
  652. //
  653. hr = this->TakeLock();
  654. if (hr != DPNH_OK)
  655. {
  656. DPFX(DPFPREP, 0, "Could not lock object!");
  657. //
  658. // Skip the failure cleanup code, we haven't set anything up.
  659. //
  660. goto Exit;
  661. }
  662. fHaveLock = TRUE;
  663. //
  664. // Make sure object is in right state.
  665. //
  666. if ((this->m_dwFlags & ~NATHELPUPNPOBJ_NOTCREATEDWITHCOM) != 0)
  667. {
  668. DPFX(DPFPREP, 0, "Object already initialized!");
  669. hr = DPNHERR_ALREADYINITIALIZED;
  670. //
  671. // Skip the failure cleanup code, we haven't set anything up.
  672. //
  673. goto Exit;
  674. }
  675. //
  676. // Read in the manual override settings from the registry
  677. //
  678. ReadRegistrySettings();
  679. //
  680. // We're not completely initialized yet, but set the flag(s) now.
  681. //
  682. this->m_dwFlags |= NATHELPUPNPOBJ_INITIALIZED;
  683. fSetFlags = TRUE;
  684. //
  685. // Store the user's settings.
  686. //
  687. if (dwFlags & DPNHINITIALIZE_DISABLEGATEWAYSUPPORT)
  688. {
  689. DPFX(DPFPREP, 1, "User requested that Internet gateways not be supported.");
  690. }
  691. else
  692. {
  693. this->m_dwFlags |= NATHELPUPNPOBJ_USEUPNP;
  694. }
  695. #ifndef DPNBUILD_NOHNETFWAPI
  696. if (dwFlags & DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT)
  697. {
  698. DPFX(DPFPREP, 1, "User requested that local firewalls not be supported.");
  699. }
  700. else
  701. {
  702. this->m_dwFlags |= NATHELPUPNPOBJ_USEHNETFWAPI;
  703. }
  704. #endif // ! DPNBUILD_NOHNETFWAPI
  705. switch (g_dwUPnPMode)
  706. {
  707. case OVERRIDEMODE_FORCEON:
  708. {
  709. //
  710. // Force UPnP on.
  711. //
  712. DPFX(DPFPREP, 1, "Forcing UPnP support on.");
  713. this->m_dwFlags |= NATHELPUPNPOBJ_USEUPNP;
  714. break;
  715. }
  716. case OVERRIDEMODE_FORCEOFF:
  717. {
  718. //
  719. // Force UPnP off.
  720. //
  721. DPFX(DPFPREP, 1, "Forcing UPnP support off.");
  722. this->m_dwFlags &= ~NATHELPUPNPOBJ_USEUPNP;
  723. break;
  724. }
  725. default:
  726. {
  727. //
  728. // Leave UPnP settings as they were set by the application.
  729. //
  730. #ifdef WINNT
  731. //
  732. // But if UPnP related service(s) are disabled, we'll take that as
  733. // our cue to not use UPnP NAT traversal even though we don't
  734. // actually use those services. We assume the user wanted to
  735. // squelch all SSDP/UPnP activity. It can still be forced back on
  736. // with a reg key, though, as indicated by the other switch cases.
  737. //
  738. if (this->IsUPnPServiceDisabled())
  739. {
  740. DPFX(DPFPREP, 1, "Not using UPnP because a related service was disabled.");
  741. this->m_dwFlags &= ~NATHELPUPNPOBJ_USEUPNP;
  742. }
  743. #endif // WINNT
  744. break;
  745. }
  746. }
  747. #ifndef DPNBUILD_NOHNETFWAPI
  748. switch (g_dwHNetFWAPIMode)
  749. {
  750. case OVERRIDEMODE_FORCEON:
  751. {
  752. //
  753. // Force HNet firewall API on.
  754. //
  755. DPFX(DPFPREP, 1, "Forcing HNet firewall API support on.");
  756. this->m_dwFlags |= NATHELPUPNPOBJ_USEHNETFWAPI;
  757. break;
  758. }
  759. case OVERRIDEMODE_FORCEOFF:
  760. {
  761. //
  762. // Force HNet firewall API off.
  763. //
  764. DPFX(DPFPREP, 1, "Forcing HNet firewall API support off.");
  765. this->m_dwFlags &= ~NATHELPUPNPOBJ_USEHNETFWAPI;
  766. break;
  767. }
  768. default:
  769. {
  770. //
  771. // Leave HNet firewall API settings alone.
  772. //
  773. break;
  774. }
  775. }
  776. #endif // ! DPNBUILD_NOHNETFWAPI
  777. #ifndef WINCE
  778. //
  779. // Determine whether we're on a Win2K or higher NT OS, and if so, use the
  780. // "Global\\" prefix for named kernel objects so we can have Terminal
  781. // Server and Fast User Switching support.
  782. //
  783. ZeroMemory(&osvi, sizeof(osvi));
  784. osvi.dwOSVersionInfoSize = sizeof(osvi);
  785. if (GetVersionEx(&osvi))
  786. {
  787. if ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
  788. (osvi.dwMajorVersion >= 5))
  789. {
  790. DPFX(DPFPREP, 8, "Running Win2K or higher NT OS, using \"Global\\\" prefix.");
  791. this->m_dwFlags |= NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX;
  792. }
  793. #ifdef DBG
  794. else
  795. {
  796. DPFX(DPFPREP, 8, "Not on NT, or its pre-Win2K, not using \"Global\\\" prefix.");
  797. }
  798. #endif // DBG
  799. }
  800. #ifdef DBG
  801. else
  802. {
  803. dwError = GetLastError();
  804. DPFX(DPFPREP, 0, "Couldn't get OS version information (err = %u)! Not using \"Global\\\" prefix.",
  805. dwError);
  806. }
  807. #endif // DBG
  808. #endif // ! WINCE
  809. #ifdef DPNBUILD_NOWINSOCK2
  810. #if defined(WINCE) && !defined(WINCE_ON_DESKTOP)
  811. this->m_hWinSockDLL = LoadLibrary( _T("winsock.dll") );
  812. #else // ! WINCE
  813. this->m_hWinSockDLL = LoadLibrary( _T("wsock32.dll") );
  814. #endif // ! WINCE
  815. if (this->m_hWinSockDLL == NULL)
  816. {
  817. #ifdef DBG
  818. dwError = GetLastError();
  819. DPFX(DPFPREP, 0, "Couldn't load WinSock 1 DLL (err = 0x%lx)!.",
  820. dwError);
  821. #endif // DBG
  822. hr = DPNHERR_GENERIC;
  823. goto Failure;
  824. }
  825. #else // ! DPNBUILD_NOWINSOCK2
  826. //
  827. // Try loading the IP helper DLL.
  828. //
  829. this->m_hIpHlpApiDLL = LoadLibrary( _T("iphlpapi.dll") );
  830. if (this->m_hIpHlpApiDLL == NULL)
  831. {
  832. #ifdef DBG
  833. dwError = GetLastError();
  834. DPFX(DPFPREP, 1, "Unable to load \"iphlpapi.dll\" (error = 0x%lx).",
  835. dwError);
  836. #endif // DBG
  837. //
  838. // That's not fatal, we can still function.
  839. //
  840. }
  841. else
  842. {
  843. //
  844. // Load the functions we'll use.
  845. //
  846. this->m_pfnGetAdaptersInfo = (PFN_GETADAPTERSINFO) GetProcAddress(this->m_hIpHlpApiDLL,
  847. _TWINCE("GetAdaptersInfo"));
  848. if (this->m_pfnGetAdaptersInfo == NULL)
  849. {
  850. #ifdef DBG
  851. dwError = GetLastError();
  852. DPFX(DPFPREP, 0, "Unable to get \"GetAdaptersInfo\" function (error = 0x%lx)!",
  853. dwError);
  854. #endif // DBG
  855. goto Exit;
  856. }
  857. this->m_pfnGetIpForwardTable = (PFN_GETIPFORWARDTABLE) GetProcAddress(this->m_hIpHlpApiDLL,
  858. _TWINCE("GetIpForwardTable"));
  859. if (this->m_pfnGetIpForwardTable == NULL)
  860. {
  861. #ifdef DBG
  862. dwError = GetLastError();
  863. DPFX(DPFPREP, 0, "Unable to get \"GetIpForwardTable\" function (error = 0x%lx)!",
  864. dwError);
  865. #endif // DBG
  866. goto Exit;
  867. }
  868. this->m_pfnGetBestRoute = (PFN_GETBESTROUTE) GetProcAddress(this->m_hIpHlpApiDLL,
  869. _TWINCE("GetBestRoute"));
  870. if (this->m_pfnGetBestRoute == NULL)
  871. {
  872. #ifdef DBG
  873. dwError = GetLastError();
  874. DPFX(DPFPREP, 0, "Unable to get \"GetBestRoute\" function (error = 0x%lx)!",
  875. dwError);
  876. #endif // DBG
  877. goto Exit;
  878. }
  879. }
  880. //
  881. // Try loading the RAS API DLL.
  882. //
  883. this->m_hRasApi32DLL = LoadLibrary( _T("rasapi32.dll") );
  884. if (this->m_hRasApi32DLL == NULL)
  885. {
  886. #ifdef DBG
  887. dwError = GetLastError();
  888. DPFX(DPFPREP, 1, "Unable to load \"rasapi32.dll\" (error = 0x%lx).",
  889. dwError);
  890. #endif // DBG
  891. //
  892. // That's not fatal, we can still function.
  893. //
  894. }
  895. else
  896. {
  897. //
  898. // Load the functions we'll use.
  899. //
  900. this->m_pfnRasGetEntryHrasconnW = (PFN_RASGETENTRYHRASCONNW) GetProcAddress(this->m_hRasApi32DLL,
  901. _TWINCE("RasGetEntryHrasconnW"));
  902. if (this->m_pfnRasGetEntryHrasconnW == NULL)
  903. {
  904. //
  905. // This function does not exist on non-NT platforms. That's fine,
  906. // just dump the DLL handle so we don't try to use it.
  907. //
  908. #ifdef DBG
  909. dwError = GetLastError();
  910. DPFX(DPFPREP, 1, "Unable to get \"RasGetEntryHrasconnW\" function (error = 0x%lx), forgetting RAS DLL.",
  911. dwError);
  912. #endif // DBG
  913. FreeLibrary(this->m_hRasApi32DLL);
  914. this->m_hRasApi32DLL = NULL;
  915. }
  916. else
  917. {
  918. this->m_pfnRasGetProjectionInfo = (PFN_RASGETPROJECTIONINFO) GetProcAddress(this->m_hRasApi32DLL,
  919. #ifdef UNICODE
  920. _TWINCE("RasGetProjectionInfoW"));
  921. #else // ! UNICODE
  922. _TWINCE("RasGetProjectionInfoA"));
  923. #endif // ! UNICODE
  924. if (this->m_pfnRasGetProjectionInfo == NULL)
  925. {
  926. #ifdef DBG
  927. dwError = GetLastError();
  928. DPFX(DPFPREP, 0, "Unable to get \"RasGetProjectionInfoA/W\" function (error = 0x%lx)!",
  929. dwError);
  930. #endif // DBG
  931. goto Exit;
  932. }
  933. }
  934. }
  935. //
  936. // Load WinSock because we may be using our private UPnP implementation, or
  937. // we just need to get the devices.
  938. //
  939. this->m_hWinSockDLL = LoadLibrary( _T("ws2_32.dll") );
  940. if (this->m_hWinSockDLL == NULL)
  941. {
  942. #ifdef DBG
  943. dwError = GetLastError();
  944. DPFX(DPFPREP, 1, "Couldn't load \"ws2_32.dll\" (err = 0x%lx), resorting to WinSock 1 functionality.",
  945. dwError);
  946. #endif // DBG
  947. this->m_hWinSockDLL = LoadLibrary( _T("wsock32.dll") );
  948. if (this->m_hWinSockDLL == NULL)
  949. {
  950. #ifdef DBG
  951. dwError = GetLastError();
  952. DPFX(DPFPREP, 0, "Couldn't load \"wsock32.dll\" either (err = 0x%lx)!.",
  953. dwError);
  954. #endif // DBG
  955. hr = DPNHERR_GENERIC;
  956. goto Failure;
  957. }
  958. //
  959. // Remember that we had to resort to WinSock 1.
  960. //
  961. this->m_dwFlags |= NATHELPUPNPOBJ_WINSOCK1;
  962. }
  963. else
  964. {
  965. DPFX(DPFPREP, 1, "Loaded \"ws2_32.dll\", using WinSock 2 functionality.");
  966. }
  967. #endif // DPNBUILD_NOWINSOCK2
  968. //
  969. // Load pointers to all the functions we use in WinSock.
  970. //
  971. hr = this->LoadWinSockFunctionPointers();
  972. if (hr != DPNH_OK)
  973. {
  974. DPFX(DPFPREP, 0, "Couldn't load WinSock function pointers!");
  975. goto Failure;
  976. }
  977. //
  978. // Fire up WinSock. Request 2.2 if we can. For the most part we only use
  979. // version 1.1 capabilities and interfaces anyway. The only exceptions are
  980. // using the event or I/O completion port handles for notification.
  981. //
  982. ZeroMemory(&wsadata, sizeof(wsadata));
  983. #ifndef DPNBUILD_NOWINSOCK2
  984. if (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1)
  985. {
  986. #endif // ! DPNBUILD_NOWINSOCK2
  987. iError = this->m_pfnWSAStartup(MAKEWORD(1, 1), &wsadata);
  988. #ifndef DPNBUILD_NOWINSOCK2
  989. }
  990. else
  991. {
  992. iError = this->m_pfnWSAStartup(MAKEWORD(2, 2), &wsadata);
  993. }
  994. #endif // ! DPNBUILD_NOWINSOCK2
  995. if (iError != 0)
  996. {
  997. DPFX(DPFPREP, 0, "Couldn't startup WinSock (error = %i)!", iError);
  998. hr = DPNHERR_GENERIC;
  999. goto Failure;
  1000. }
  1001. fWinSockStarted = TRUE;
  1002. DPFX(DPFPREP, 4, "Initialized WinSock version %u.%u.",
  1003. LOBYTE(wsadata.wVersion), HIBYTE(wsadata.wVersion));
  1004. #ifndef DPNBUILD_NOWINSOCK2
  1005. //
  1006. // Try creating a UDP socket for use with WSAIoctl. Do this even if we're
  1007. // WinSock 1 and can't use WSAIoctl socket. This allows us to make sure
  1008. // TCP/IP is installed and working.
  1009. //
  1010. this->m_sIoctls = this->m_pfnsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  1011. if (this->m_sIoctls == INVALID_SOCKET)
  1012. {
  1013. #ifdef DBG
  1014. dwError = this->m_pfnWSAGetLastError();
  1015. DPFX(DPFPREP, 0, "Couldn't create Ioctl socket, error = %u!", dwError);
  1016. #endif // DBG
  1017. hr = DPNHERR_GENERIC;
  1018. goto Failure;
  1019. }
  1020. //
  1021. // Try binding the socket. This is a continuation of the validation.
  1022. //
  1023. ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
  1024. saddrinTemp.sin_family = AF_INET;
  1025. //saddrinTemp.sin_addr.S_un.S_addr = INADDR_ANY;
  1026. //saddrinTemp.sin_port = 0;
  1027. if (this->m_pfnbind(this->m_sIoctls,
  1028. (SOCKADDR *) (&saddrinTemp),
  1029. sizeof(saddrinTemp)) != 0)
  1030. {
  1031. #ifdef DBG
  1032. dwError = this->m_pfnWSAGetLastError();
  1033. DPFX(DPFPREP, 0, "Couldn't bind the Ioctl socket to arbitrary port on any interface, error = %u!",
  1034. dwError);
  1035. #endif // DBG
  1036. hr = DPNHERR_GENERIC;
  1037. goto Failure;
  1038. }
  1039. #endif // ! DPNBUILD_NOWINSOCK2
  1040. //
  1041. // Build appropriate access control structures. On NT, we want to allow
  1042. // read access to everyone. On other platforms, security is ignored.
  1043. //
  1044. #ifdef WINNT
  1045. if (! AllocateAndInitializeSid(&SidIdentifierAuthorityWorld,
  1046. 1,
  1047. SECURITY_WORLD_RID,
  1048. 0,
  1049. 0,
  1050. 0,
  1051. 0,
  1052. 0,
  1053. 0,
  1054. 0,
  1055. &pSid))
  1056. {
  1057. #ifdef DEBUG
  1058. dwError = GetLastError();
  1059. DPFX(DPFPREP, 0, "Couldn't allocate and initialize SID, error = %u!",
  1060. dwError);
  1061. #endif // DEBUG
  1062. hr = DPNHERR_GENERIC;
  1063. goto Failure;
  1064. }
  1065. dwAclLength = sizeof(ACL)
  1066. + sizeof(ACCESS_ALLOWED_ACE)
  1067. - sizeof(DWORD) // subtract out sizeof(ACCESS_ALLOWED_ACE.SidStart)
  1068. + GetLengthSid(pSid);
  1069. pAcl = (ACL*) DNMalloc(dwAclLength);
  1070. if (pAcl == NULL)
  1071. {
  1072. hr = DPNHERR_OUTOFMEMORY;
  1073. goto Failure;
  1074. }
  1075. if (! InitializeAcl(pAcl, dwAclLength, ACL_REVISION))
  1076. {
  1077. #ifdef DEBUG
  1078. dwError = GetLastError();
  1079. DPFX(DPFPREP, 0, "Couldn't initialize ACL, error = %u!",
  1080. dwError);
  1081. #endif // DEBUG
  1082. hr = DPNHERR_GENERIC;
  1083. goto Failure;
  1084. }
  1085. if (! AddAccessAllowedAce(pAcl, ACL_REVISION, SYNCHRONIZE, pSid))
  1086. {
  1087. #ifdef DEBUG
  1088. dwError = GetLastError();
  1089. DPFX(DPFPREP, 0, "Couldn't add access allowed ACE, error = %u!",
  1090. dwError);
  1091. #endif // DEBUG
  1092. hr = DPNHERR_GENERIC;
  1093. goto Failure;
  1094. }
  1095. if (! InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR) abSecurityDescriptorBuffer,
  1096. SECURITY_DESCRIPTOR_REVISION))
  1097. {
  1098. #ifdef DEBUG
  1099. dwError = GetLastError();
  1100. DPFX(DPFPREP, 0, "Couldn't initialize security descriptor, error = %u!",
  1101. dwError);
  1102. #endif // DEBUG
  1103. hr = DPNHERR_GENERIC;
  1104. goto Failure;
  1105. }
  1106. if (! SetSecurityDescriptorDacl((PSECURITY_DESCRIPTOR) abSecurityDescriptorBuffer,
  1107. TRUE,
  1108. pAcl,
  1109. FALSE))
  1110. {
  1111. #ifdef DEBUG
  1112. dwError = GetLastError();
  1113. DPFX(DPFPREP, 0, "Couldn't set security descriptor DACL, error = %u!",
  1114. dwError);
  1115. #endif // DEBUG
  1116. hr = DPNHERR_GENERIC;
  1117. goto Failure;
  1118. }
  1119. SecurityAttributes.nLength = sizeof(SecurityAttributes);
  1120. SecurityAttributes.lpSecurityDescriptor = abSecurityDescriptorBuffer;
  1121. SecurityAttributes.bInheritHandle = FALSE;
  1122. pSecurityAttributes = &SecurityAttributes;
  1123. #else // ! WINNT
  1124. pSecurityAttributes = NULL;
  1125. #endif // ! WINNT
  1126. //
  1127. // Use a random number for the instance key and event. We use this to let
  1128. // other instances know that we're alive to avoid the crash-cleanup code.
  1129. // Try to create the named event a couple times before giving up.
  1130. //
  1131. dwTry = 0;
  1132. do
  1133. {
  1134. this->m_dwInstanceKey = GetGlobalRand();
  1135. DPFX(DPFPREP, 2, "Using crash cleanup key %u.", this->m_dwInstanceKey);
  1136. #ifndef WINCE
  1137. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  1138. {
  1139. wsprintf(tszObjectName, _T("Global\\") INSTANCENAMEDOBJECT_FORMATSTRING, this->m_dwInstanceKey);
  1140. this->m_hMappingStillActiveNamedObject = DNCreateEvent(pSecurityAttributes, FALSE, FALSE, tszObjectName);
  1141. }
  1142. else
  1143. #endif // ! WINCE
  1144. {
  1145. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, this->m_dwInstanceKey);
  1146. this->m_hMappingStillActiveNamedObject = DNCreateEvent(pSecurityAttributes, FALSE, FALSE, tszObjectName);
  1147. }
  1148. if (this->m_hMappingStillActiveNamedObject == NULL)
  1149. {
  1150. #ifdef DBG
  1151. dwError = GetLastError();
  1152. DPFX(DPFPREP, 0, "Couldn't create mapping-still-active named object, error = %u!", dwError);
  1153. #endif // DBG
  1154. dwTry++;
  1155. if (dwTry >= MAX_NUM_INSTANCE_EVENT_ATTEMPTS)
  1156. {
  1157. hr = DPNHERR_GENERIC;
  1158. goto Failure;
  1159. }
  1160. //
  1161. // Continue...
  1162. //
  1163. }
  1164. }
  1165. while (this->m_hMappingStillActiveNamedObject == NULL);
  1166. #ifdef WINNT
  1167. DNFree(pAcl);
  1168. pAcl = NULL;
  1169. FreeSid(pSid);
  1170. pSid = NULL;
  1171. #endif // WINNT
  1172. //
  1173. // Build the list of IP capable devices.
  1174. //
  1175. hr = this->CheckForNewDevices(NULL);
  1176. if (hr != DPNH_OK)
  1177. {
  1178. DPFX(DPFPREP, 0, "Couldn't build device list!");
  1179. goto Failure;
  1180. }
  1181. //
  1182. // We could technically try to contact UPnP devices right now, but we don't
  1183. // because it's a slow blocking operation, and users have to call GetCaps
  1184. // at least once anyway.
  1185. //
  1186. Exit:
  1187. if (fHaveLock)
  1188. {
  1189. this->DropLock();
  1190. fHaveLock = FALSE;
  1191. }
  1192. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  1193. return hr;
  1194. Failure:
  1195. if (this->m_hMappingStillActiveNamedObject != NULL)
  1196. {
  1197. DNCloseHandle(this->m_hMappingStillActiveNamedObject);
  1198. this->m_hMappingStillActiveNamedObject = NULL;
  1199. }
  1200. #ifdef WINNT
  1201. if (pAcl != NULL)
  1202. {
  1203. DNFree(pAcl);
  1204. pAcl = NULL;
  1205. }
  1206. if (pSid != NULL)
  1207. {
  1208. FreeSid(pSid);
  1209. pSid = NULL;
  1210. }
  1211. #endif // WINNT
  1212. this->RemoveAllItems();
  1213. #ifndef DPNBUILD_NOWINSOCK2
  1214. if (this->m_sIoctls != INVALID_SOCKET)
  1215. {
  1216. this->m_pfnclosesocket(this->m_sIoctls); // ignore error
  1217. this->m_sIoctls = INVALID_SOCKET;
  1218. }
  1219. #endif // ! DPNBUILD_NOWINSOCK2
  1220. if (fWinSockStarted)
  1221. {
  1222. this->m_pfnWSACleanup(); // ignore error
  1223. }
  1224. #ifndef DPNBUILD_NOWINSOCK2
  1225. if (this->m_hWinSockDLL != NULL)
  1226. {
  1227. this->m_pfnWSAStartup = NULL;
  1228. this->m_pfnWSACleanup = NULL;
  1229. this->m_pfnWSAGetLastError = NULL;
  1230. this->m_pfnsocket = NULL;
  1231. this->m_pfnclosesocket = NULL;
  1232. this->m_pfnbind = NULL;
  1233. this->m_pfnsetsockopt = NULL;
  1234. this->m_pfngetsockname = NULL;
  1235. this->m_pfnselect = NULL;
  1236. this->m_pfn__WSAFDIsSet = NULL;
  1237. this->m_pfnrecvfrom = NULL;
  1238. this->m_pfnsendto = NULL;
  1239. this->m_pfngethostname = NULL;
  1240. this->m_pfngethostbyname = NULL;
  1241. this->m_pfninet_addr = NULL;
  1242. this->m_pfnWSASocketA = NULL;
  1243. this->m_pfnWSAIoctl = NULL;
  1244. this->m_pfnWSAGetOverlappedResult = NULL;
  1245. this->m_pfnioctlsocket = NULL;
  1246. this->m_pfnconnect = NULL;
  1247. this->m_pfnshutdown = NULL;
  1248. this->m_pfnsend = NULL;
  1249. this->m_pfnrecv = NULL;
  1250. #ifdef DBG
  1251. this->m_pfngetsockopt = NULL;
  1252. #endif // DBG
  1253. this->m_dwFlags &= ~NATHELPUPNPOBJ_WINSOCK1;
  1254. FreeLibrary(this->m_hWinSockDLL);
  1255. this->m_hWinSockDLL = NULL;
  1256. }
  1257. if (this->m_hRasApi32DLL != NULL)
  1258. {
  1259. this->m_pfnRasGetEntryHrasconnW = NULL;
  1260. this->m_pfnRasGetProjectionInfo = NULL;
  1261. FreeLibrary(this->m_hRasApi32DLL);
  1262. this->m_hRasApi32DLL = NULL;
  1263. }
  1264. if (this->m_hIpHlpApiDLL != NULL)
  1265. {
  1266. this->m_pfnGetAdaptersInfo = NULL;
  1267. this->m_pfnGetIpForwardTable = NULL;
  1268. this->m_pfnGetBestRoute = NULL;
  1269. FreeLibrary(this->m_hIpHlpApiDLL);
  1270. this->m_hIpHlpApiDLL = NULL;
  1271. }
  1272. #endif // ! DPNBUILD_NOWINSOCK2
  1273. if (fSetFlags)
  1274. {
  1275. this->m_dwFlags &= ~(NATHELPUPNPOBJ_INITIALIZED |
  1276. NATHELPUPNPOBJ_USEUPNP |
  1277. #ifndef DPNBUILD_NOHNETFWAPI
  1278. NATHELPUPNPOBJ_USEHNETFWAPI |
  1279. #endif // ! DPNBUILD_NOHNETFWAPI
  1280. #ifdef WINCE
  1281. NATHELPUPNPOBJ_DEVICECHANGED);
  1282. #else // ! WINCE
  1283. NATHELPUPNPOBJ_DEVICECHANGED |
  1284. NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX);
  1285. #endif // ! WINCE
  1286. }
  1287. goto Exit;
  1288. } // CNATHelpUPnP::Initialize
  1289. #undef DPF_MODNAME
  1290. #define DPF_MODNAME "CNATHelpUPnP::Close"
  1291. //=============================================================================
  1292. // CNATHelpUPnP::Close
  1293. //-----------------------------------------------------------------------------
  1294. //
  1295. // Description: Shuts down and de-registers this application with any
  1296. // Internet gateway servers. All port assignments are implicitly
  1297. // freed as a result of this operation.
  1298. //
  1299. // This must balance a successful call to Initialize.
  1300. //
  1301. // Arguments:
  1302. // DWORD dwFlags - Unused, must be zero.
  1303. //
  1304. // Returns: HRESULT
  1305. // DPNH_OK - Closing the helper API was successful.
  1306. // DPNHERR_GENERIC - An error occurred while closing.
  1307. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  1308. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  1309. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  1310. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  1311. // DPNHERR_OUTOFMEMORY - There is not enough memory to close.
  1312. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  1313. // thread.
  1314. //=============================================================================
  1315. STDMETHODIMP CNATHelpUPnP::Close(const DWORD dwFlags)
  1316. {
  1317. HRESULT hr;
  1318. BOOL fHaveLock = FALSE;
  1319. int iError;
  1320. #ifdef DBG
  1321. DWORD dwError;
  1322. #endif // DBG
  1323. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%lx)", this, dwFlags);
  1324. //
  1325. // Validate the object.
  1326. //
  1327. if (! this->IsValidObject())
  1328. {
  1329. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  1330. hr = DPNHERR_INVALIDOBJECT;
  1331. goto Failure;
  1332. }
  1333. //
  1334. // Validate the parameters.
  1335. //
  1336. if (dwFlags != 0)
  1337. {
  1338. DPFX(DPFPREP, 0, "Invalid flags specified!");
  1339. hr = DPNHERR_INVALIDFLAGS;
  1340. goto Failure;
  1341. }
  1342. //
  1343. // Attempt to take the lock, but be prepared for the re-entrancy error.
  1344. //
  1345. hr = this->TakeLock();
  1346. if (hr != DPNH_OK)
  1347. {
  1348. DPFX(DPFPREP, 0, "Could not lock object!");
  1349. goto Failure;
  1350. }
  1351. fHaveLock = TRUE;
  1352. //
  1353. // Make sure object is in right state.
  1354. //
  1355. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  1356. {
  1357. DPFX(DPFPREP, 0, "Object not initialized!");
  1358. hr = DPNHERR_NOTINITIALIZED;
  1359. goto Failure;
  1360. }
  1361. //
  1362. // We need to actively deregister any devices which are registered with
  1363. // Internet gateways.
  1364. //
  1365. this->RemoveAllItems();
  1366. //
  1367. // Close the named object since this process is going away.
  1368. //
  1369. if (this->m_hMappingStillActiveNamedObject != NULL)
  1370. {
  1371. DNCloseHandle(this->m_hMappingStillActiveNamedObject);
  1372. this->m_hMappingStillActiveNamedObject = NULL;
  1373. }
  1374. #ifndef DPNBUILD_NOWINSOCK2
  1375. //
  1376. // Close the Ioctl socket.
  1377. //
  1378. DNASSERT(this->m_sIoctls != INVALID_SOCKET);
  1379. this->m_pfnclosesocket(this->m_sIoctls); // ignore error
  1380. this->m_sIoctls = INVALID_SOCKET;
  1381. //
  1382. // If we submitted overlapped I/O, see if it got cancelled.
  1383. //
  1384. if (this->m_polAddressListChange != NULL)
  1385. {
  1386. OSVERSIONINFO osvi;
  1387. OSVERSIONINFOEX osvix;
  1388. BOOL fCanWait;
  1389. DWORD dwAttempt;
  1390. ZeroMemory(&osvi, sizeof(osvi));
  1391. osvi.dwOSVersionInfoSize = sizeof(osvi);
  1392. if (GetVersionEx(&osvi))
  1393. {
  1394. //
  1395. // Any platform but Win2K Gold, Win2K + SP1, or Win2K + SP2 can
  1396. // just go ahead and wait for the I/O to complete.
  1397. //
  1398. if ((osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) ||
  1399. (osvi.dwMajorVersion > 5) ||
  1400. (osvi.dwMinorVersion > 0))
  1401. {
  1402. DPFX(DPFPREP, 3, "Windows %s version %u.%u detected, waiting for address list change Ioctl to complete.",
  1403. ((osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) ? _T("9x") : _T("NT")),
  1404. osvi.dwMajorVersion, osvi.dwMinorVersion);
  1405. fCanWait = TRUE;
  1406. }
  1407. else
  1408. {
  1409. //
  1410. // Win2K versions < SP3 have a bug where the I/O is not always
  1411. // cancelled by closing the socket. We can't wait for the
  1412. // completion, sometimes it doesn't happen.
  1413. //
  1414. fCanWait = FALSE;
  1415. ZeroMemory(&osvix, sizeof(osvix));
  1416. osvix.dwOSVersionInfoSize = sizeof(osvix);
  1417. if (GetVersionEx((LPOSVERSIONINFO) (&osvix)))
  1418. {
  1419. //
  1420. // If SP3 or later is applied, we know it's fixed.
  1421. //
  1422. if (osvix.wServicePackMajor >= 3)
  1423. {
  1424. DPFX(DPFPREP, 3, "Windows 2000 Service Pack %u detected, waiting for address list change Ioctl to complete.",
  1425. osvix.wServicePackMajor);
  1426. fCanWait = TRUE;
  1427. }
  1428. #ifdef DBG
  1429. else
  1430. {
  1431. if (osvix.wServicePackMajor == 0)
  1432. {
  1433. DPFX(DPFPREP, 2, "Windows 2000 Gold detected, not waiting for address list change Ioctl to complete.");
  1434. }
  1435. else
  1436. {
  1437. DPFX(DPFPREP, 2, "Windows 2000 Service Pack %u detected, not waiting for address list change Ioctl to complete.",
  1438. osvix.wServicePackMajor);
  1439. }
  1440. }
  1441. #endif // DBG
  1442. }
  1443. #ifdef DBG
  1444. else
  1445. {
  1446. dwError = GetLastError();
  1447. DPFX(DPFPREP, 0, "Couldn't get extended OS version information (err = %u)! Assuming not Win2K < SP3.",
  1448. dwError);
  1449. }
  1450. #endif // DBG
  1451. }
  1452. //
  1453. // Wait, if we can. Otherwise, leak the memory.
  1454. //
  1455. if (fCanWait)
  1456. {
  1457. //
  1458. // Keep looping until I/O completes. We will give up after a
  1459. // while to prevent hangs.
  1460. //
  1461. dwAttempt = 0;
  1462. while (! HasOverlappedIoCompleted(this->m_polAddressListChange))
  1463. {
  1464. DPFX(DPFPREP, 2, "Waiting %u ms for address list change Ioctl to complete.",
  1465. IOCOMPLETE_WAIT_INTERVAL);
  1466. //
  1467. // Give the OS some time to complete it.
  1468. //
  1469. Sleep(IOCOMPLETE_WAIT_INTERVAL);
  1470. dwAttempt++;
  1471. if (dwAttempt >= MAX_NUM_IOCOMPLETE_WAITS)
  1472. {
  1473. break;
  1474. }
  1475. }
  1476. }
  1477. else
  1478. {
  1479. //
  1480. // Just leak the memory. See above notes and debug print
  1481. // statements
  1482. //
  1483. }
  1484. }
  1485. #ifdef DBG
  1486. else
  1487. {
  1488. dwError = GetLastError();
  1489. DPFX(DPFPREP, 0, "Couldn't get OS version information (err = %u)! Assuming not Win2K < SP3.",
  1490. dwError);
  1491. }
  1492. #endif // DBG
  1493. //
  1494. // We've either freed the memory or committed to leaking the object.
  1495. //
  1496. if (HasOverlappedIoCompleted(this->m_polAddressListChange))
  1497. {
  1498. //
  1499. // We didn't allocate it through DNMalloc, use the matching free
  1500. // function.
  1501. //
  1502. HeapFree(GetProcessHeap(), 0, this->m_polAddressListChange);
  1503. }
  1504. else
  1505. {
  1506. DPFX(DPFPREP, 1, "Overlapped address list change Ioctl has not completed yet, leaking %u byte overlapped structure at 0x%p.",
  1507. sizeof(WSAOVERLAPPED), this->m_polAddressListChange);
  1508. }
  1509. this->m_polAddressListChange = NULL;
  1510. }
  1511. #endif // ! DPNBUILD_NOWINSOCK2
  1512. //
  1513. // Cleanup WinSock.
  1514. //
  1515. iError = this->m_pfnWSACleanup();
  1516. if (iError != 0)
  1517. {
  1518. #ifdef DBG
  1519. dwError = this->m_pfnWSAGetLastError();
  1520. DPFX(DPFPREP, 0, "Couldn't cleanup WinSock (error = %u)!", dwError);
  1521. #endif // DBG
  1522. //
  1523. // Continue anyway, so we can finish cleaning up the object.
  1524. //
  1525. }
  1526. //
  1527. // Unload the library.
  1528. //
  1529. this->m_pfnWSAStartup = NULL;
  1530. this->m_pfnWSACleanup = NULL;
  1531. this->m_pfnWSAGetLastError = NULL;
  1532. this->m_pfnsocket = NULL;
  1533. this->m_pfnclosesocket = NULL;
  1534. this->m_pfnbind = NULL;
  1535. this->m_pfnsetsockopt = NULL;
  1536. this->m_pfngetsockname = NULL;
  1537. this->m_pfnselect = NULL;
  1538. this->m_pfn__WSAFDIsSet = NULL;
  1539. this->m_pfnrecvfrom = NULL;
  1540. this->m_pfnsendto = NULL;
  1541. this->m_pfngethostname = NULL;
  1542. this->m_pfngethostbyname = NULL;
  1543. this->m_pfninet_addr = NULL;
  1544. #ifndef DPNBUILD_NOWINSOCK2
  1545. this->m_pfnWSASocketA = NULL;
  1546. this->m_pfnWSAIoctl = NULL;
  1547. this->m_pfnWSAGetOverlappedResult = NULL;
  1548. #endif // ! DPNBUILD_NOWINSOCK2
  1549. this->m_pfnioctlsocket = NULL;
  1550. this->m_pfnconnect = NULL;
  1551. this->m_pfnshutdown = NULL;
  1552. this->m_pfnsend = NULL;
  1553. this->m_pfnrecv = NULL;
  1554. #ifdef DBG
  1555. this->m_pfngetsockopt = NULL;
  1556. #endif // DBG
  1557. FreeLibrary(this->m_hWinSockDLL);
  1558. this->m_hWinSockDLL = NULL;
  1559. #ifndef DPNBUILD_NOWINSOCK2
  1560. //
  1561. // If we loaded RASAPI32.DLL, unload it.
  1562. //
  1563. if (this->m_hRasApi32DLL != NULL)
  1564. {
  1565. this->m_pfnRasGetEntryHrasconnW = NULL;
  1566. this->m_pfnRasGetProjectionInfo = NULL;
  1567. FreeLibrary(this->m_hRasApi32DLL);
  1568. this->m_hRasApi32DLL = NULL;
  1569. }
  1570. //
  1571. // If we loaded IPHLPAPI.DLL, unload it.
  1572. //
  1573. if (this->m_hIpHlpApiDLL != NULL)
  1574. {
  1575. this->m_pfnGetAdaptersInfo = NULL;
  1576. this->m_pfnGetIpForwardTable = NULL;
  1577. this->m_pfnGetBestRoute = NULL;
  1578. FreeLibrary(this->m_hIpHlpApiDLL);
  1579. this->m_hIpHlpApiDLL = NULL;
  1580. }
  1581. //
  1582. // If there was an alert event, we're done with it.
  1583. //
  1584. if (this->m_hAlertEvent != NULL)
  1585. {
  1586. CloseHandle(this->m_hAlertEvent);
  1587. this->m_hAlertEvent = NULL;
  1588. }
  1589. //
  1590. // If there was an alert I/O completion port, we're done with it.
  1591. //
  1592. if (this->m_hAlertIOCompletionPort != NULL)
  1593. {
  1594. CloseHandle(this->m_hAlertIOCompletionPort);
  1595. this->m_hAlertIOCompletionPort = NULL;
  1596. }
  1597. #endif // ! DPNBUILD_NOWINSOCK2
  1598. //
  1599. // Turn off flags which should reset it back to 0 or just the
  1600. // NOTCREATEDWITHCOM flag.
  1601. //
  1602. this->m_dwFlags &= ~(NATHELPUPNPOBJ_INITIALIZED |
  1603. NATHELPUPNPOBJ_USEUPNP |
  1604. #ifndef DPNBUILD_NOHNETFWAPI
  1605. NATHELPUPNPOBJ_USEHNETFWAPI |
  1606. #endif // ! DPNBUILD_NOHNETFWAPI
  1607. #ifndef DPNBUILD_NOWINSOCK2
  1608. NATHELPUPNPOBJ_WINSOCK1 |
  1609. #endif // ! DPNBUILD_NOWINSOCK2
  1610. NATHELPUPNPOBJ_DEVICECHANGED |
  1611. NATHELPUPNPOBJ_ADDRESSESCHANGED |
  1612. #ifdef WINCE
  1613. NATHELPUPNPOBJ_PORTREGISTERED);
  1614. #else // ! WINCE
  1615. NATHELPUPNPOBJ_PORTREGISTERED |
  1616. NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX);
  1617. #endif // ! WINCE
  1618. DNASSERT((this->m_dwFlags & ~NATHELPUPNPOBJ_NOTCREATEDWITHCOM) == 0);
  1619. this->DropLock();
  1620. fHaveLock = FALSE;
  1621. Exit:
  1622. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  1623. return hr;
  1624. Failure:
  1625. if (fHaveLock)
  1626. {
  1627. this->DropLock();
  1628. fHaveLock = FALSE;
  1629. }
  1630. goto Exit;
  1631. } // CNATHelpUPnP::Close
  1632. #undef DPF_MODNAME
  1633. #define DPF_MODNAME "CNATHelpUPnP::GetCaps"
  1634. //=============================================================================
  1635. // CNATHelpUPnP::GetCaps
  1636. //-----------------------------------------------------------------------------
  1637. //
  1638. // Description: Retrieves the capabilities of the Internet gateway server(s)
  1639. // and information on leased ports. This function should be
  1640. // called periodically with the DPNHGETCAPS_UPDATESERVERSTATUS
  1641. // flag to automatically extend port leases that are about to
  1642. // expire (that are in last 2 minutes of their lease).
  1643. //
  1644. // The DPNHGETCAPS_UPDATESERVERSTATUS flag also causes
  1645. // detection of changes in the servers' status since the last
  1646. // similar call to GetCaps. If a new server becomes available, an
  1647. // existing one became unavailable, or a server's public address
  1648. // changed in a way that affects an existing registered port
  1649. // mapping, then DPNHSUCCESS_ADDRESSESCHANGED is returned instead
  1650. // of DPNH_OK. The user should then update its port binding
  1651. // information via GetRegisteredAddresses.
  1652. //
  1653. // When DPNHGETCAPS_UPDATESERVERSTATUS is specified, this
  1654. // function may block for a short period of time while attempts
  1655. // are made to communicate with the server(s).
  1656. //
  1657. // GetCaps must be called with the
  1658. // DPNHGETCAPS_UPDATESERVERSTATUS flag at least once prior to
  1659. // using the GetRegisteredAddresses or QueryAddress methods.
  1660. //
  1661. // Arguments:
  1662. // DPNHCAPS * pdpnhcaps - Pointer to structure to be filled with the NAT
  1663. // helper's current capabilities. The dwSize
  1664. // field of the structure must be filled in before
  1665. // calling GetCaps.
  1666. // DWORD dwFlags - Flags to use when retrieving capabilities
  1667. // (DPNHGETCAPS_xxx).
  1668. //
  1669. // Returns: HRESULT
  1670. // DPNH_OK - Determining capabilities was successful.
  1671. // Address status has not changed.
  1672. // DPNHSUCCESS_ADDRESSESCHANGED - One or more of the registered port
  1673. // mappings' addresses changed, retrieve
  1674. // updated mappings with
  1675. // GetRegisteredAddress.
  1676. // DPNHERR_GENERIC - An error occurred while determining
  1677. // capabilities.
  1678. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  1679. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  1680. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  1681. // DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
  1682. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  1683. // DPNHERR_OUTOFMEMORY - There is not enough memory to get
  1684. // capabilities.
  1685. // DPNHERR_REENTRANT - The interface has been re-entered on the
  1686. // same thread.
  1687. //=============================================================================
  1688. STDMETHODIMP CNATHelpUPnP::GetCaps(DPNHCAPS * const pdpnhcaps,
  1689. const DWORD dwFlags)
  1690. {
  1691. HRESULT hr;
  1692. BOOL fHaveLock = FALSE;
  1693. DWORD dwCurrentTime;
  1694. DWORD dwLeaseTimeRemaining;
  1695. CBilink * pBilink;
  1696. CRegisteredPort * pRegisteredPort;
  1697. CDevice * pDevice;
  1698. CUPnPDevice * pUPnPDevice = NULL;
  1699. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx)",
  1700. this, pdpnhcaps, dwFlags);
  1701. //
  1702. // Validate the object.
  1703. //
  1704. if (! this->IsValidObject())
  1705. {
  1706. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  1707. hr = DPNHERR_INVALIDOBJECT;
  1708. goto Failure;
  1709. }
  1710. //
  1711. // Validate the parameters.
  1712. //
  1713. if ((pdpnhcaps == NULL) ||
  1714. (IsBadWritePtr(pdpnhcaps, sizeof(DPNHCAPS))))
  1715. {
  1716. DPFX(DPFPREP, 0, "Invalid caps structure pointer specified!");
  1717. hr = DPNHERR_INVALIDPOINTER;
  1718. goto Failure;
  1719. }
  1720. if (pdpnhcaps->dwSize != sizeof(DPNHCAPS))
  1721. {
  1722. DPFX(DPFPREP, 0, "Invalid caps structure specified, dwSize must be %u!",
  1723. sizeof(DPNHCAPS));
  1724. hr = DPNHERR_INVALIDPARAM;
  1725. goto Failure;
  1726. }
  1727. if (dwFlags & ~DPNHGETCAPS_UPDATESERVERSTATUS)
  1728. {
  1729. DPFX(DPFPREP, 0, "Invalid flags specified!");
  1730. hr = DPNHERR_INVALIDFLAGS;
  1731. goto Failure;
  1732. }
  1733. //
  1734. // Attempt to take the lock, but be prepared for the re-entrancy error.
  1735. //
  1736. hr = this->TakeLock();
  1737. if (hr != DPNH_OK)
  1738. {
  1739. DPFX(DPFPREP, 0, "Could not lock object!");
  1740. goto Failure;
  1741. }
  1742. fHaveLock = TRUE;
  1743. //
  1744. // Make sure object is in right state.
  1745. //
  1746. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  1747. {
  1748. DPFX(DPFPREP, 0, "Object not initialized!");
  1749. hr = DPNHERR_NOTINITIALIZED;
  1750. goto Failure;
  1751. }
  1752. //
  1753. // Fill in the base caps structure.
  1754. //
  1755. pdpnhcaps->dwFlags = 0;
  1756. pdpnhcaps->dwNumRegisteredPorts = 0;
  1757. pdpnhcaps->dwMinLeaseTimeRemaining = -1;
  1758. //
  1759. // pdpnhcaps->dwRecommendedGetCapsInterval is initialized below
  1760. //
  1761. if (dwFlags & DPNHGETCAPS_UPDATESERVERSTATUS)
  1762. {
  1763. //
  1764. // Remove any cached mappings that have expired.
  1765. //
  1766. this->ExpireOldCachedMappings();
  1767. //
  1768. // Extend leases, if necessary.
  1769. //
  1770. hr = this->ExtendAllExpiringLeases();
  1771. if (hr != DPNH_OK)
  1772. {
  1773. DPFX(DPFPREP, 0, "Extending all expiring leases failed!");
  1774. goto Failure;
  1775. }
  1776. //
  1777. // Check for any new devices.
  1778. //
  1779. hr = this->CheckForNewDevices(NULL);
  1780. if (hr != DPNH_OK)
  1781. {
  1782. DPFX(DPFPREP, 0, "Checking for new devices failed!");
  1783. goto Failure;
  1784. }
  1785. //
  1786. // Check for possible changes in any server's status. The
  1787. // ADDRESSESCHANGED flag will be set on this object if there were
  1788. // changes that affected existing port mappings.
  1789. //
  1790. hr = this->UpdateServerStatus();
  1791. if (hr != DPNH_OK)
  1792. {
  1793. DPFX(DPFPREP, 0, "Updating servers' status failed!");
  1794. goto Failure;
  1795. }
  1796. //
  1797. // Okay, so if things are different, alert the caller.
  1798. //
  1799. if (this->m_dwFlags & NATHELPUPNPOBJ_ADDRESSESCHANGED)
  1800. {
  1801. hr = DPNHSUCCESS_ADDRESSESCHANGED;
  1802. this->m_dwFlags &= ~NATHELPUPNPOBJ_ADDRESSESCHANGED;
  1803. }
  1804. #ifdef DBG
  1805. //
  1806. // This flag should have been turned off by now if it ever got turned
  1807. // on.
  1808. //
  1809. DNASSERT(! (this->m_dwFlags & NATHELPUPNPOBJ_DEVICECHANGED));
  1810. //
  1811. // Print the current device and mapping status for debugging purposes.
  1812. //
  1813. this->DebugPrintCurrentStatus();
  1814. #endif // DBG
  1815. }
  1816. else
  1817. {
  1818. //
  1819. // Not extending expiring leases or updating server status.
  1820. //
  1821. }
  1822. //
  1823. // Loop through all the devices, getting their gateway capabilities.
  1824. //
  1825. pBilink = this->m_blDevices.GetNext();
  1826. while (pBilink != (&this->m_blDevices))
  1827. {
  1828. DNASSERT(! pBilink->IsEmpty());
  1829. pDevice = DEVICE_FROM_BILINK(pBilink);
  1830. #ifndef DPNBUILD_NOHNETFWAPI
  1831. if (pDevice->IsHNetFirewalled())
  1832. {
  1833. //
  1834. // The firewall does not actively notify you of it going down.
  1835. //
  1836. pdpnhcaps->dwFlags |= DPNHCAPSFLAG_LOCALFIREWALLPRESENT | DPNHCAPSFLAG_PUBLICADDRESSAVAILABLE | DPNHCAPSFLAG_NOTALLSUPPORTACTIVENOTIFY;
  1837. }
  1838. #endif // ! DPNBUILD_NOHNETFWAPI
  1839. pUPnPDevice = pDevice->GetUPnPDevice();
  1840. if (pUPnPDevice != NULL)
  1841. {
  1842. DNASSERT(pUPnPDevice->IsReady());
  1843. pdpnhcaps->dwFlags |= DPNHCAPSFLAG_GATEWAYPRESENT;
  1844. if (pUPnPDevice->IsLocal())
  1845. {
  1846. pdpnhcaps->dwFlags |= DPNHCAPSFLAG_GATEWAYISLOCAL;
  1847. }
  1848. if (pUPnPDevice->GetExternalIPAddressV4() != 0)
  1849. {
  1850. pdpnhcaps->dwFlags |= DPNHCAPSFLAG_PUBLICADDRESSAVAILABLE;
  1851. }
  1852. //
  1853. // The custom UPnP stack currently does not support active
  1854. // notification...
  1855. //
  1856. pdpnhcaps->dwFlags |= DPNHCAPSFLAG_NOTALLSUPPORTACTIVENOTIFY;
  1857. }
  1858. pBilink = pBilink->GetNext();
  1859. }
  1860. //
  1861. // Loop through all registered ports, counting them.
  1862. // We have the appropriate lock.
  1863. //
  1864. pBilink = this->m_blRegisteredPorts.GetNext();
  1865. dwCurrentTime = GETTIMESTAMP();
  1866. while (pBilink != (&this->m_blRegisteredPorts))
  1867. {
  1868. DNASSERT(! pBilink->IsEmpty());
  1869. pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
  1870. //
  1871. // Count these registered addresses toward the total.
  1872. //
  1873. pdpnhcaps->dwNumRegisteredPorts += pRegisteredPort->GetNumAddresses();
  1874. pDevice = pRegisteredPort->GetOwningDevice();
  1875. if (pDevice != NULL)
  1876. {
  1877. DNASSERT(! (pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts)));
  1878. //
  1879. // If they're registered with any UPnP devices using a non-
  1880. // permanent lease, calculate the minimum lease time remaining.
  1881. //
  1882. if ((pRegisteredPort->HasUPnPPublicAddresses()) &&
  1883. (! pRegisteredPort->HasPermanentUPnPLease()))
  1884. {
  1885. dwLeaseTimeRemaining = pRegisteredPort->GetUPnPLeaseExpiration() - dwCurrentTime;
  1886. if (dwLeaseTimeRemaining < pdpnhcaps->dwMinLeaseTimeRemaining)
  1887. {
  1888. //
  1889. // Temporarily store how much time remains.
  1890. //
  1891. pdpnhcaps->dwMinLeaseTimeRemaining = dwLeaseTimeRemaining;
  1892. }
  1893. }
  1894. }
  1895. else
  1896. {
  1897. DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts));
  1898. }
  1899. pBilink = pBilink->GetNext();
  1900. }
  1901. //
  1902. // There are different default recommended GetCaps intervals depending on
  1903. // whether there's a server present, and whether it supports active address
  1904. // change notification (that we can alert on) or not.
  1905. //
  1906. // If there are any leases which need to be renewed before that default
  1907. // time, the recommendation will be shortened appropriately.
  1908. //
  1909. //
  1910. // If GetCaps hasn't been called with UPDATESERVERSTATUS yet, recommend an
  1911. // immediate check.
  1912. //
  1913. if (this->m_dwLastUpdateServerStatusTime == 0)
  1914. {
  1915. DPFX(DPFPREP, 1, "Server status has not been updated yet, recommending immediate GetCaps.");
  1916. //
  1917. // Drop the lock, we're done here.
  1918. //
  1919. this->DropLock();
  1920. fHaveLock = FALSE;
  1921. goto Exit;
  1922. }
  1923. //
  1924. // In an ideal world, we could get notified of changes and we would never
  1925. // have to poll. Unfortunately that isn't the case. We need to recommend
  1926. // a relatively short poll interval.
  1927. //
  1928. // Start by figuring out how long it's been since the last server update.
  1929. // This calculation really should not go negative. If it does, it means
  1930. // the caller hasn't updated the server status in ages anyway, so we should
  1931. // recommend immediate GetCaps.
  1932. //
  1933. // Otherwise if the 'port registered' flag is still set at this point, then
  1934. // the user must have called GetCaps previously, then RegisterPorts, then
  1935. // made this second GetCaps call before g_dwMinUpdateServerStatusInterval
  1936. // elapsed. Recommend that the user call us again as soon as the minimum
  1937. // update interval does elapse.
  1938. //
  1939. // In all other cases, generate a recommendation based on the current
  1940. // backed off poll interval.
  1941. //
  1942. dwCurrentTime = dwCurrentTime - this->m_dwLastUpdateServerStatusTime;
  1943. if ((int) dwCurrentTime < 0)
  1944. {
  1945. DPFX(DPFPREP, 1, "Server status was last updated a really long time ago (%u ms), recommending immediate GetCaps.",
  1946. dwCurrentTime);
  1947. pdpnhcaps->dwRecommendedGetCapsInterval = 0;
  1948. }
  1949. else if (this->m_dwFlags & NATHELPUPNPOBJ_PORTREGISTERED)
  1950. {
  1951. DPFX(DPFPREP, 1, "Didn't handle new port registration because server was last updated %u ms ago, (poll interval staying at %u ms).",
  1952. dwCurrentTime, this->m_dwNextPollInterval);
  1953. pdpnhcaps->dwRecommendedGetCapsInterval = g_dwMinUpdateServerStatusInterval - dwCurrentTime;
  1954. if ((int) pdpnhcaps->dwRecommendedGetCapsInterval < 0)
  1955. {
  1956. pdpnhcaps->dwRecommendedGetCapsInterval = 0;
  1957. }
  1958. }
  1959. else
  1960. {
  1961. DPFX(DPFPREP, 7, "Server was last updated %u ms ago, current poll interval is %u ms.",
  1962. dwCurrentTime, this->m_dwNextPollInterval);
  1963. //
  1964. // Calculate a new recommended interval based on the current value, and
  1965. // backoff that interval if necessary.
  1966. //
  1967. pdpnhcaps->dwRecommendedGetCapsInterval = this->m_dwNextPollInterval - dwCurrentTime;
  1968. this->m_dwNextPollInterval += GetGlobalRand() % g_dwPollIntervalBackoff;
  1969. if (this->m_dwNextPollInterval > g_dwMaxPollInterval)
  1970. {
  1971. this->m_dwNextPollInterval = g_dwMaxPollInterval;
  1972. DPFX(DPFPREP, 3, "Capping next poll interval at %u ms.",
  1973. this->m_dwNextPollInterval);
  1974. }
  1975. else
  1976. {
  1977. DPFX(DPFPREP, 8, "Next poll interval will be %u ms.",
  1978. this->m_dwNextPollInterval);
  1979. }
  1980. //
  1981. // If that time went negative, then it implies that the interval has
  1982. // already elapsed. Recommend immediate GetCaps.
  1983. //
  1984. if (((int) pdpnhcaps->dwRecommendedGetCapsInterval) < 0)
  1985. {
  1986. DPFX(DPFPREP, 1, "Recommended interval already elapsed (%i ms), suggesting immediate GetCaps.",
  1987. ((int) pdpnhcaps->dwRecommendedGetCapsInterval));
  1988. pdpnhcaps->dwRecommendedGetCapsInterval = 0;
  1989. }
  1990. }
  1991. this->DropLock();
  1992. fHaveLock = FALSE;
  1993. //
  1994. // If there is a non-INFINITE lease time remaining, see if that affects the
  1995. // GetCaps interval.
  1996. //
  1997. if (pdpnhcaps->dwMinLeaseTimeRemaining != -1)
  1998. {
  1999. //
  2000. // If there are leases that need to be refreshed before the default
  2001. // recommendation, then use those instead.
  2002. //
  2003. if (pdpnhcaps->dwMinLeaseTimeRemaining < LEASE_RENEW_TIME)
  2004. {
  2005. DPFX(DPFPREP, 1, "Lease needs renewing right away (min %u < %u ms), recommending immediate GetCaps.",
  2006. pdpnhcaps->dwMinLeaseTimeRemaining, LEASE_RENEW_TIME);
  2007. pdpnhcaps->dwRecommendedGetCapsInterval = 0;
  2008. }
  2009. else
  2010. {
  2011. //
  2012. // Either pick the time when the lease should be renewed or leave
  2013. // it as the recommended time, whichever is shorter.
  2014. //
  2015. if ((pdpnhcaps->dwMinLeaseTimeRemaining - LEASE_RENEW_TIME) < pdpnhcaps->dwRecommendedGetCapsInterval)
  2016. {
  2017. pdpnhcaps->dwRecommendedGetCapsInterval = pdpnhcaps->dwMinLeaseTimeRemaining - LEASE_RENEW_TIME;
  2018. }
  2019. }
  2020. }
  2021. DPFX(DPFPREP, 7, "GetCaps flags = 0x%lx, num registered ports = %u, min lease time remaining = %i, recommended interval = %i.",
  2022. pdpnhcaps->dwFlags,
  2023. pdpnhcaps->dwNumRegisteredPorts,
  2024. ((int) pdpnhcaps->dwMinLeaseTimeRemaining),
  2025. ((int) pdpnhcaps->dwRecommendedGetCapsInterval));
  2026. Exit:
  2027. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  2028. return hr;
  2029. Failure:
  2030. if (fHaveLock)
  2031. {
  2032. this->DropLock();
  2033. fHaveLock = FALSE;
  2034. }
  2035. goto Exit;
  2036. } // CNATHelpUPnP::GetCaps
  2037. #undef DPF_MODNAME
  2038. #define DPF_MODNAME "CNATHelpUPnP::RegisterPorts"
  2039. //=============================================================================
  2040. // CNATHelpUPnP::RegisterPorts
  2041. //-----------------------------------------------------------------------------
  2042. //
  2043. // Description: Asks for public realm port(s) that are aliases for the local
  2044. // port(s) on this private realm node. If a server is available,
  2045. // all traffic directed to the gateway on the public side at the
  2046. // allocated public ports-- which the gateway provides and
  2047. // specifies in the response-- will be directed to the specified
  2048. // local ports. If the DPNHREGISTERPORTS_FIXEDPORTS flag is not
  2049. // specified, the ports assigned on the public interface are
  2050. // arbitrary (i.e. may not be the same as those in awLocalPort).
  2051. // The address and ports actually allocated can be retrieved by
  2052. // calling GetRegisteredAddresses.
  2053. //
  2054. // The address component for every SOCKADDR structure in the
  2055. // array must be the same. A separate RegisterPorts call is
  2056. // required to register multiple ports that are not using the same
  2057. // interface. The address can be INADDR_ANY, in which case the
  2058. // "best" server will be used. If multiple servers are available
  2059. // via different adapters, an adapter with an Internet gateway is
  2060. // selected. If no adapters have Internet gateways, the first
  2061. // adapter with a local firewall is selected. If neither are
  2062. // available, then the first one where either a gateway or a
  2063. // firewall becomes available will be automatically selected.
  2064. // Once one of the adapters has been assigned, it cannot be
  2065. // changed. Since the server chosen by this method may not be
  2066. // optimal for a particular application, it is recommended that
  2067. // individual addresses be registered instead of INADDR_ANY.
  2068. //
  2069. // If the address in aLocalAddresses is not one of those
  2070. // available to the local machine, the registration will still
  2071. // succeed. If an adapter with that address becomes available,
  2072. // the port mapping will automatically be applied, and it will
  2073. // gain a public mapping with any server available to that
  2074. // adapter. If the address was originally available but the
  2075. // network adapter is subsequently removed from the system, any
  2076. // public address mapping is lost. It will be automatically
  2077. // regained if the local address becomes available again. It is
  2078. // recommended that the caller detect local address changes
  2079. // independently and de-register/re-register mappings per adapter
  2080. // as appropriate for maximum control.
  2081. //
  2082. // If the DPNHREGISTERPORTS_SHAREDPORTS flag is used, the
  2083. // server will allow other NAT clients to register it as well.
  2084. // Any UDP traffic received on the public interface will be
  2085. // forwarded to all clients registered. This requires the
  2086. // DPNHREGISTERPORTS_FIXEDPORTS flag and cannot be used with
  2087. // DPNHREGISTERPORTS_TCP.
  2088. //
  2089. // The user should specify a requested lease time that the
  2090. // server will attempt to honor. The actual time remaining can be
  2091. // can be retrieved by calling GetRegisteredAddresses.
  2092. //
  2093. // Note that if a server is not available, this function will
  2094. // still succeed. GetRegisteredAddresses will return
  2095. // DPNHERR_NOMAPPING for the handle returned in phRegisteredPorts
  2096. // in that case. If the server arrives later during the session,
  2097. // calling GetCaps periodically can detect this and automatically
  2098. // map previously registered ports. Use GetRegisteredAddresses to
  2099. // retrieve the newly mapped address when that occurs.
  2100. //
  2101. // Only 16 ports may be registered at a time, but RegisterPorts
  2102. // may be called as many times as desired.
  2103. //
  2104. // The same array of addresses may be registered more than
  2105. // once. Each DPNHHANDLE returned must be released with
  2106. // DeregisterPorts or Close. If an individual address was
  2107. // previously registered but in a different array or a different
  2108. // order in the array, then the DPNHERR_PORTALREADYREGISTERED
  2109. // error code is returned.
  2110. //
  2111. // Arguments:
  2112. // SOCKADDR * aLocalAddresses - Array of local address and port tuples
  2113. // for which remote ports are requested.
  2114. // DWORD dwAddressesSize - Size of entire local addresses array.
  2115. // DWORD dwNumAddresses - Number of SOCKADDR structures in local
  2116. // addresses array.
  2117. // DWORD dwLeaseTime - Requested time, in milliseconds, to lease
  2118. // the ports. As long as GetCaps is
  2119. // called before this time has expired,
  2120. // the lease will automatically be
  2121. // renewed.
  2122. // DPNHHANDLE * phRegisteredPorts - Place to store an identifier for this
  2123. // binding which can later be used to
  2124. // query or release the binding.
  2125. // DWORD dwFlags - Flags to use when registering the port
  2126. // (DPNHREGISTERPORTS_xxx).
  2127. //
  2128. // Returns: HRESULT
  2129. // DPNH_OK - The ports were successfully registered
  2130. // (although no public address may be
  2131. // available yet).
  2132. // DPNHERR_GENERIC - An error occurred that prevented
  2133. // registration of the requested ports.
  2134. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  2135. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  2136. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  2137. // DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
  2138. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  2139. // DPNHERR_OUTOFMEMORY - There is not enough memory to register
  2140. // the ports.
  2141. // DPNHERR_PORTALREADYREGISTERED - At least one of the ports has already
  2142. // been registered in a different address
  2143. // array or order.
  2144. // DPNHERR_REENTRANT - The interface has been re-entered on the
  2145. // same thread.
  2146. //=============================================================================
  2147. STDMETHODIMP CNATHelpUPnP::RegisterPorts(const SOCKADDR * const aLocalAddresses,
  2148. const DWORD dwAddressesSize,
  2149. const DWORD dwNumAddresses,
  2150. const DWORD dwLeaseTime,
  2151. DPNHHANDLE * const phRegisteredPorts,
  2152. const DWORD dwFlags)
  2153. {
  2154. HRESULT hr;
  2155. ULONG ulFirstAddress;
  2156. DWORD dwTemp;
  2157. DWORD dwMatch;
  2158. BOOL fHaveLock = FALSE;
  2159. CRegisteredPort * pRegisteredPort = NULL;
  2160. CDevice * pDevice = NULL;
  2161. CBilink * pBilink;
  2162. SOCKADDR_IN * pasaddrinTemp;
  2163. CUPnPDevice * pUPnPDevice = NULL;
  2164. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, %u, %u, %u, 0x%p, 0x%lx)",
  2165. this, aLocalAddresses, dwAddressesSize, dwNumAddresses, dwLeaseTime,
  2166. phRegisteredPorts, dwFlags);
  2167. //
  2168. // Validate the object.
  2169. //
  2170. if (! this->IsValidObject())
  2171. {
  2172. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  2173. hr = DPNHERR_INVALIDOBJECT;
  2174. goto Failure;
  2175. }
  2176. //
  2177. // Validate the parameters.
  2178. //
  2179. if (aLocalAddresses == NULL)
  2180. {
  2181. DPFX(DPFPREP, 0, "Local addresses array cannot be NULL!");
  2182. hr = DPNHERR_INVALIDPOINTER;
  2183. goto Failure;
  2184. }
  2185. if (dwNumAddresses == 0)
  2186. {
  2187. DPFX(DPFPREP, 0, "Number of addresses cannot be 0!");
  2188. hr = DPNHERR_INVALIDPARAM;
  2189. goto Failure;
  2190. }
  2191. if (dwAddressesSize != (dwNumAddresses * sizeof(SOCKADDR)))
  2192. {
  2193. DPFX(DPFPREP, 0, "Addresses array size invalid!");
  2194. hr = DPNHERR_INVALIDPARAM;
  2195. goto Failure;
  2196. }
  2197. if (IsBadReadPtr(aLocalAddresses, dwAddressesSize))
  2198. {
  2199. DPFX(DPFPREP, 0, "Local addresses array buffer is invalid!");
  2200. hr = DPNHERR_INVALIDPOINTER;
  2201. goto Failure;
  2202. }
  2203. if (dwNumAddresses > DPNH_MAX_SIMULTANEOUS_PORTS)
  2204. {
  2205. DPFX(DPFPREP, 0, "Only %u ports may be registered at a time!", DPNH_MAX_SIMULTANEOUS_PORTS);
  2206. hr = DPNHERR_INVALIDPARAM;
  2207. goto Failure;
  2208. }
  2209. if (((SOCKADDR_IN*) aLocalAddresses)->sin_family != AF_INET)
  2210. {
  2211. DPFX(DPFPREP, 0, "First address in array is not AF_INET, only IPv4 addresses are supported!");
  2212. hr = DPNHERR_INVALIDPARAM;
  2213. goto Failure;
  2214. }
  2215. if (((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_addr == INADDR_BROADCAST)
  2216. {
  2217. DPFX(DPFPREP, 0, "First address cannot be broadcast address!");
  2218. hr = DPNHERR_INVALIDPARAM;
  2219. goto Failure;
  2220. }
  2221. if (((SOCKADDR_IN*) aLocalAddresses)->sin_port == 0)
  2222. {
  2223. DPFX(DPFPREP, 0, "First port in array is 0, a valid port must be specified!");
  2224. hr = DPNHERR_INVALIDPARAM;
  2225. goto Failure;
  2226. }
  2227. ulFirstAddress = ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_addr;
  2228. for(dwTemp = 1; dwTemp < dwNumAddresses; dwTemp++)
  2229. {
  2230. //
  2231. // Make sure this address family type is supported.
  2232. //
  2233. if (((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_family != AF_INET)
  2234. {
  2235. DPFX(DPFPREP, 0, "Address at array index %u is not AF_INET, all items in the array must be the same IPv4 address!",
  2236. dwTemp);
  2237. hr = DPNHERR_INVALIDPARAM;
  2238. goto Failure;
  2239. }
  2240. //
  2241. // If this address doesn't match the first, then the caller broke the
  2242. // rules.
  2243. //
  2244. if (((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_addr != ulFirstAddress)
  2245. {
  2246. //
  2247. // Don't use inet_ntoa because we may not be initialized yet.
  2248. //
  2249. DPFX(DPFPREP, 0, "Address %u.%u.%u.%u at array index %u differs from the first, all addresses in the array must match!",
  2250. ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b1,
  2251. ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b2,
  2252. ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b3,
  2253. ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b4,
  2254. dwTemp);
  2255. hr = DPNHERR_INVALIDPARAM;
  2256. goto Failure;
  2257. }
  2258. //
  2259. // Make sure this port isn't 0 either.
  2260. //
  2261. if (((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_port == 0)
  2262. {
  2263. DPFX(DPFPREP, 0, "Port at array index %u is 0, valid ports must be specified!", dwTemp);
  2264. hr = DPNHERR_INVALIDPARAM;
  2265. goto Failure;
  2266. }
  2267. }
  2268. if (dwLeaseTime == 0)
  2269. {
  2270. DPFX(DPFPREP, 0, "Invalid lease time specified!");
  2271. hr = DPNHERR_INVALIDPARAM;
  2272. goto Failure;
  2273. }
  2274. if ((phRegisteredPorts == NULL) ||
  2275. (IsBadWritePtr(phRegisteredPorts, sizeof(DPNHHANDLE))))
  2276. {
  2277. DPFX(DPFPREP, 0, "Invalid port mapping handle pointer specified!");
  2278. hr = DPNHERR_INVALIDPOINTER;
  2279. goto Failure;
  2280. }
  2281. if (dwFlags & ~(DPNHREGISTERPORTS_TCP | DPNHREGISTERPORTS_FIXEDPORTS | DPNHREGISTERPORTS_SHAREDPORTS))
  2282. {
  2283. DPFX(DPFPREP, 0, "Invalid flags specified!");
  2284. hr = DPNHERR_INVALIDFLAGS;
  2285. goto Failure;
  2286. }
  2287. if (dwFlags & DPNHREGISTERPORTS_SHAREDPORTS)
  2288. {
  2289. //
  2290. // SHAREDPORTS cannot be used with TCP and requires a FIXEDPORTS.
  2291. //
  2292. if ((dwFlags & DPNHREGISTERPORTS_TCP) || (! (dwFlags & DPNHREGISTERPORTS_FIXEDPORTS)))
  2293. {
  2294. DPFX(DPFPREP, 0, "SHAREDPORTS flag requires FIXEDPORTS flag and cannot be used with TCP flag!");
  2295. hr = DPNHERR_INVALIDFLAGS;
  2296. goto Failure;
  2297. }
  2298. }
  2299. //
  2300. // Attempt to take the lock, but be prepared for the re-entrancy error.
  2301. //
  2302. hr = this->TakeLock();
  2303. if (hr != DPNH_OK)
  2304. {
  2305. DPFX(DPFPREP, 0, "Could not lock object!");
  2306. goto Failure;
  2307. }
  2308. fHaveLock = TRUE;
  2309. //
  2310. // Make sure object is in right state.
  2311. //
  2312. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  2313. {
  2314. DPFX(DPFPREP, 0, "Object not initialized!");
  2315. hr = DPNHERR_NOTINITIALIZED;
  2316. goto Failure;
  2317. }
  2318. //
  2319. // Loop through all existing registered port mappings and look for this
  2320. // array of ports.
  2321. //
  2322. pBilink = this->m_blRegisteredPorts.GetNext();
  2323. while (pBilink != &this->m_blRegisteredPorts)
  2324. {
  2325. DNASSERT(! pBilink->IsEmpty());
  2326. pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
  2327. //
  2328. // Don't bother looking at addresses of the wrong type or at arrays of
  2329. // the wrong size.
  2330. //
  2331. if (((pRegisteredPort->IsTCP() && (dwFlags & DPNHREGISTERPORTS_TCP)) ||
  2332. ((! pRegisteredPort->IsTCP()) && (! (dwFlags & DPNHREGISTERPORTS_TCP)))) &&
  2333. (pRegisteredPort->GetNumAddresses() == dwNumAddresses))
  2334. {
  2335. pasaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
  2336. for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
  2337. {
  2338. //
  2339. // If the addresses don't match, stop looping.
  2340. //
  2341. if ((pasaddrinTemp[dwTemp].sin_port != ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_port) ||
  2342. (pasaddrinTemp[dwTemp].sin_addr.S_un.S_addr != ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_addr))
  2343. {
  2344. break;
  2345. }
  2346. }
  2347. //
  2348. // If all the addresses matched, then this item was already
  2349. // registered.
  2350. //
  2351. if (dwTemp >= dwNumAddresses)
  2352. {
  2353. DPFX(DPFPREP, 1, "Array of %u addresses was already registered, returning existing mapping 0x%p.",
  2354. dwNumAddresses, pRegisteredPort);
  2355. goto ReturnUserHandle;
  2356. }
  2357. DPFX(DPFPREP, 7, "Existing mapping 0x%p does not match all %u addresses.",
  2358. pRegisteredPort, dwNumAddresses);
  2359. }
  2360. else
  2361. {
  2362. //
  2363. // Existing mapping isn't same type or doesn't have same number of
  2364. // items in array.
  2365. //
  2366. }
  2367. pBilink = pBilink->GetNext();
  2368. }
  2369. //
  2370. // If we're here, none of the existing mappings match. Loop through each
  2371. // of the ports and make sure they aren't already registered inside some
  2372. // other mapping.
  2373. //
  2374. for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
  2375. {
  2376. pBilink = this->m_blRegisteredPorts.GetNext();
  2377. while (pBilink != &this->m_blRegisteredPorts)
  2378. {
  2379. DNASSERT(! pBilink->IsEmpty());
  2380. pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
  2381. pasaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
  2382. for(dwMatch = 0; dwMatch < pRegisteredPort->GetNumAddresses(); dwMatch++)
  2383. {
  2384. //
  2385. // If the addresses match, then we can't map these ports.
  2386. //
  2387. if ((pasaddrinTemp[dwMatch].sin_port == ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_port) &&
  2388. (pasaddrinTemp[dwMatch].sin_addr.S_un.S_addr == ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_addr))
  2389. {
  2390. DPFX(DPFPREP, 0, "Existing mapping 0x%p already registered the address %u.%u.%u.%u:%u!",
  2391. pRegisteredPort,
  2392. pasaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b1,
  2393. pasaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b2,
  2394. pasaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b3,
  2395. pasaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b4,
  2396. NTOHS(pasaddrinTemp[dwMatch].sin_port));
  2397. //
  2398. // Clear the pointer so we don't delete the object.
  2399. //
  2400. pRegisteredPort = NULL;
  2401. hr = DPNHERR_PORTALREADYREGISTERED;
  2402. goto Failure;
  2403. }
  2404. }
  2405. pBilink = pBilink->GetNext();
  2406. }
  2407. }
  2408. //
  2409. // If we're here the ports are all unique. Create a new mapping object
  2410. // we'll use to refer to the binding.
  2411. //
  2412. pRegisteredPort = new CRegisteredPort(dwLeaseTime, dwFlags);
  2413. if (pRegisteredPort == NULL)
  2414. {
  2415. hr = DPNHERR_OUTOFMEMORY;
  2416. goto Failure;
  2417. }
  2418. hr = pRegisteredPort->SetPrivateAddresses((SOCKADDR_IN*) aLocalAddresses, dwNumAddresses);
  2419. if (hr != DPNH_OK)
  2420. {
  2421. DPFX(DPFPREP, 0, "Couldn't store private addresses array!");
  2422. goto Failure;
  2423. }
  2424. //
  2425. // Find the device that matches the given addresses.
  2426. //
  2427. // The first entry of aLocalAddresses is representative of all entries since
  2428. // they should all share the same address.
  2429. //
  2430. // Since there won't be an existing registered port for this address, don't
  2431. // bother looking through them for a matching address.
  2432. //
  2433. pDevice = this->FindMatchingDevice((SOCKADDR_IN*) (&aLocalAddresses[0]),
  2434. FALSE);
  2435. if (pDevice == NULL)
  2436. {
  2437. DPFX(DPFPREP, 1, "No device for given address (%u.%u.%u.%u), storing 0x%p in unowned list.",
  2438. ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b1,
  2439. ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b2,
  2440. ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b3,
  2441. ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b4,
  2442. pRegisteredPort);
  2443. pRegisteredPort->m_blDeviceList.InsertBefore(&this->m_blUnownedPorts);
  2444. }
  2445. else
  2446. {
  2447. pRegisteredPort->MakeDeviceOwner(pDevice);
  2448. #ifndef DPNBUILD_NOHNETFWAPI
  2449. //
  2450. // Start by mapping with the local firewall, if there is one.
  2451. //
  2452. if (pDevice->IsHNetFirewalled())
  2453. {
  2454. hr = this->CheckForLocalHNetFirewallAndMapPorts(pDevice,
  2455. pRegisteredPort);
  2456. if (hr != DPNH_OK)
  2457. {
  2458. DPFX(DPFPREP, 0, "Couldn't check for local HNet firewall and map ports (err = 0x%lx)! Continuing.",
  2459. hr);
  2460. DNASSERT(! pDevice->IsHNetFirewalled());
  2461. hr = DPNH_OK;
  2462. }
  2463. }
  2464. else
  2465. {
  2466. //
  2467. // No local HomeNet firewall (last time we checked).
  2468. //
  2469. }
  2470. #endif // ! DPNBUILD_NOHNETFWAPI
  2471. //
  2472. // Map the ports on the UPnP device, if there is one.
  2473. //
  2474. pUPnPDevice = pDevice->GetUPnPDevice();
  2475. if (pUPnPDevice != NULL)
  2476. {
  2477. //
  2478. // GetUPnPDevice did not add a reference to pUPnPDevice for us.
  2479. //
  2480. pUPnPDevice->AddRef();
  2481. DNASSERT(pUPnPDevice->IsReady());
  2482. //
  2483. // Actually map the ports.
  2484. //
  2485. hr = this->MapPortsOnUPnPDevice(pUPnPDevice, pRegisteredPort);
  2486. if (hr != DPNH_OK)
  2487. {
  2488. DPFX(DPFPREP, 0, "Couldn't map ports on UPnP device 0x%p (0x%lx)! Ignoring.",
  2489. pUPnPDevice, hr);
  2490. //
  2491. // It may have been cleared already, but doing it twice
  2492. // shouldn't be harmful.
  2493. //
  2494. this->ClearDevicesUPnPDevice(pRegisteredPort->GetOwningDevice());
  2495. hr = DPNH_OK;
  2496. }
  2497. pUPnPDevice->DecRef();
  2498. pUPnPDevice = NULL;
  2499. }
  2500. else
  2501. {
  2502. //
  2503. // No UPnP device.
  2504. //
  2505. }
  2506. }
  2507. //
  2508. // Save the mapping in the global list (we have the lock).
  2509. //
  2510. pRegisteredPort->m_blGlobalList.InsertBefore(&this->m_blRegisteredPorts);
  2511. ReturnUserHandle:
  2512. //
  2513. // Remember that a port has been registered.
  2514. //
  2515. this->m_dwFlags |= NATHELPUPNPOBJ_PORTREGISTERED;
  2516. //
  2517. // We're about to give the port to the user.
  2518. //
  2519. pRegisteredPort->AddUserRef();
  2520. //
  2521. // We're going to give the user a direct pointer to the object (disguised
  2522. // as an opaque DPNHHANDLE, of course).
  2523. //
  2524. (*phRegisteredPorts) = (DPNHHANDLE) pRegisteredPort;
  2525. this->DropLock();
  2526. fHaveLock = FALSE;
  2527. DPFX(DPFPREP, 5, "Returning registered port 0x%p (first private address = %u.%u.%u.%u:%u).",
  2528. pRegisteredPort,
  2529. ((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b1,
  2530. ((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b2,
  2531. ((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b3,
  2532. ((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b4,
  2533. NTOHS(((SOCKADDR_IN*) aLocalAddresses)[0].sin_port));
  2534. hr = DPNH_OK;
  2535. Exit:
  2536. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  2537. return hr;
  2538. Failure:
  2539. if (pUPnPDevice != NULL)
  2540. {
  2541. pUPnPDevice->DecRef();
  2542. }
  2543. if (pRegisteredPort != NULL)
  2544. {
  2545. #ifndef DPNBUILD_NOHNETFWAPI
  2546. if (pRegisteredPort->IsMappedOnHNetFirewall())
  2547. {
  2548. HRESULT temphr;
  2549. //
  2550. // Unmap the port.
  2551. //
  2552. // Don't bother alerting user about address change. It would have
  2553. // already been taken care of if this was due to a fatal error, and
  2554. // there would be no perceived changed if not.
  2555. //
  2556. temphr = this->UnmapPortOnLocalHNetFirewall(pRegisteredPort, TRUE, FALSE);
  2557. if (temphr != DPNH_OK)
  2558. {
  2559. DPFX(DPFPREP, 0, "Failed unmapping registered port 0x%p on local HomeNet firewall (err = 0x%lx)! Ignoring.",
  2560. pRegisteredPort, temphr);
  2561. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  2562. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  2563. }
  2564. }
  2565. #endif // ! DPNBUILD_NOHNETFWAPI
  2566. if (pDevice != NULL)
  2567. {
  2568. pRegisteredPort->ClearDeviceOwner();
  2569. }
  2570. pRegisteredPort->ClearPrivateAddresses();
  2571. delete pRegisteredPort;
  2572. }
  2573. if (fHaveLock)
  2574. {
  2575. this->DropLock();
  2576. fHaveLock = FALSE;
  2577. }
  2578. goto Exit;
  2579. } // CNATHelpUPnP::RegisterPorts
  2580. #undef DPF_MODNAME
  2581. #define DPF_MODNAME "CNATHelpUPnP::GetRegisteredAddresses"
  2582. //=============================================================================
  2583. // CNATHelpUPnP::GetRegisteredAddresses
  2584. //-----------------------------------------------------------------------------
  2585. //
  2586. // Description: Returns the current public address mappings for a given
  2587. // registered port group. If there are no servers currently
  2588. // available, then DPNHERR_SERVERNOTAVAILABLE is returned. If the
  2589. // servers' public interfaces are not currently valid, then
  2590. // DPNHERR_NOMAPPING is returned, but appropriate values will
  2591. // still be placed in pdwAddressTypeFlags and
  2592. // pdwLeaseTimeRemaining.
  2593. //
  2594. // If the mapping was registered with the
  2595. // DPNHREGISTERPORTS_FIXEDPORTS flag, but at least one port is
  2596. // already in use on the gateway, then DPNHERR_PORTUNAVAILABLE is
  2597. // returned and appropriate flags will still be placed in
  2598. // pdwAddressTypeFlags.
  2599. //
  2600. // If the local machine has a cooperative firewall installed,
  2601. // the requested port is opened locally on the firewall before
  2602. // being mapped on the Internet gateway. Normally this function
  2603. // returns the public address on the Internet gateway address when
  2604. // both are present. Since some firewalls remap the port number
  2605. // when opening non-fixed ports, the
  2606. // DPNHGETREGISTEREDADDRESSES_LOCALFIREWALLREMAPONLY allows the
  2607. // caller to retrieve the locally remapped address, even if there
  2608. // is a mapping on an Internet gateway.
  2609. //
  2610. // Some gateway devices do not natively support ports that are
  2611. // not fixed, and may generate the DPNHERR_PORTUNAVAILABLE return
  2612. // code even when the DPNHREGISTERPORTS_FIXEDPORTS flag was not
  2613. // specified. The caller should de-register the port mapping
  2614. // handle, rebind the application to different ports, and call
  2615. // RegisterPorts again.
  2616. //
  2617. // If the buffer indicated by paPublicAddresses is too small,
  2618. // then the size required is returned in pdwPublicAddressesSize
  2619. // and DPNHERR_BUFFERTOOSMALL is returned. Otherwise the number of
  2620. // bytes written is returned in pdwPublicAddressesSize.
  2621. //
  2622. // Even though the addresses are returned as individual
  2623. // SOCKADDRs, all ports registered at the same time will share the
  2624. // same public address. Only the port components will vary.
  2625. //
  2626. // All buffers are optional and may be NULL, but if
  2627. // paPublicAddresses is specified, it must be accompanied by an
  2628. // appropriate size in pdwPublicAddressesSize.
  2629. //
  2630. // If GetCaps has not been previously called with the
  2631. // DPNHGETCAPS_UPDATESERVERSTATUS flag at least once, then the
  2632. // error code DPNHERR_UPDATESERVERSTATUS is returned.
  2633. //
  2634. // Arguments:
  2635. // DPNHHANDLE hRegisteredPorts - Handle for a specific binding returned by
  2636. // RegisterPorts.
  2637. // SOCKADDR * paPublicAddresses - Buffer to return assigned public realm
  2638. // address, or NULL if not desired.
  2639. // DWORD * pdwPublicAddressesSize - Pointer to size of paPublicAddresses
  2640. // buffer, or place to store size
  2641. // required/written. Cannot be NULL if
  2642. // paPublicAddresses is not NULL.
  2643. // DWORD * pdwAddressTypeFlags - Place to store flags describing the
  2644. // address types returned, or NULL if not
  2645. // desired.
  2646. // DWORD * pdwLeaseTimeRemaining - Place to store approximate number of
  2647. // milliseconds remaining in the port
  2648. // lease, or NULL if not desired. Call
  2649. // GetCaps to automatically extend leases
  2650. // about to expire.
  2651. // DWORD dwFlags - Unused, must be zero.
  2652. //
  2653. // Returns: HRESULT
  2654. // DPNH_OK - Information on the port mapping was found and
  2655. // the addresses were stored in
  2656. // paPublicAddresses.
  2657. // DPNHERR_BUFFERTOOSMALL - There was not enough room in the buffer to
  2658. // store the addresses.
  2659. // DPNHERR_GENERIC - An error occurred while retrieving the
  2660. // requested port mapping.
  2661. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  2662. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  2663. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  2664. // DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
  2665. // DPNHERR_NOMAPPING - The server(s) do not have valid public
  2666. // interfaces.
  2667. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  2668. // DPNHERR_OUTOFMEMORY - There is not enough memory to get the
  2669. // addresses.
  2670. // DPNHERR_PORTUNAVAILABLE - At least one of the ports is not available on
  2671. // the server.
  2672. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  2673. // thread.
  2674. // DPNHERR_SERVERNOTAVAILABLE - No servers are currently present.
  2675. // DPNHERR_UPDATESERVERSTATUS - GetCaps has not been called with the
  2676. // DPNHGETCAPS_UPDATESERVERSTATUS flag yet.
  2677. //=============================================================================
  2678. STDMETHODIMP CNATHelpUPnP::GetRegisteredAddresses(const DPNHHANDLE hRegisteredPorts,
  2679. SOCKADDR * const paPublicAddresses,
  2680. DWORD * const pdwPublicAddressesSize,
  2681. DWORD * const pdwAddressTypeFlags,
  2682. DWORD * const pdwLeaseTimeRemaining,
  2683. const DWORD dwFlags)
  2684. {
  2685. HRESULT hr;
  2686. CRegisteredPort * pRegisteredPort;
  2687. BOOL fHaveLock = FALSE;
  2688. BOOL fRegisteredWithServer = FALSE;
  2689. BOOL fFoundValidMapping = FALSE;
  2690. BOOL fPortIsUnavailable = FALSE;
  2691. DWORD dwSizeRequired;
  2692. DWORD dwAddressTypeFlags;
  2693. DWORD dwCurrentTime;
  2694. //DWORD dwTempLeaseTimeRemaining;
  2695. DWORD dwLeaseTimeRemaining = -1;
  2696. CDevice * pDevice;
  2697. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%p, 0x%p, 0x%lx)",
  2698. this, hRegisteredPorts, paPublicAddresses, pdwPublicAddressesSize,
  2699. pdwAddressTypeFlags, pdwLeaseTimeRemaining, dwFlags);
  2700. //
  2701. // Validate the object.
  2702. //
  2703. if (! this->IsValidObject())
  2704. {
  2705. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  2706. hr = DPNHERR_INVALIDOBJECT;
  2707. goto Failure;
  2708. }
  2709. //
  2710. // Validate the parameters.
  2711. //
  2712. pRegisteredPort = (CRegisteredPort*) hRegisteredPorts;
  2713. if (! pRegisteredPort->IsValidObject())
  2714. {
  2715. DPFX(DPFPREP, 0, "Invalid registered port mapping handle specified!");
  2716. hr = DPNHERR_INVALIDPARAM;
  2717. goto Failure;
  2718. }
  2719. if (paPublicAddresses != NULL)
  2720. {
  2721. if ((pdwPublicAddressesSize == NULL) ||
  2722. (IsBadWritePtr(pdwPublicAddressesSize, sizeof(DWORD))))
  2723. {
  2724. DPFX(DPFPREP, 0, "When specifying a public addresses buffer, a valid size must be given!");
  2725. hr = DPNHERR_INVALIDPOINTER;
  2726. goto Failure;
  2727. }
  2728. if (IsBadWritePtr(paPublicAddresses, (*pdwPublicAddressesSize)))
  2729. {
  2730. DPFX(DPFPREP, 0, "The public addresses buffer is invalid!");
  2731. hr = DPNHERR_INVALIDPOINTER;
  2732. goto Failure;
  2733. }
  2734. }
  2735. else
  2736. {
  2737. if ((pdwPublicAddressesSize != NULL) &&
  2738. (IsBadWritePtr(pdwPublicAddressesSize, sizeof(DWORD))))
  2739. {
  2740. DPFX(DPFPREP, 0, "Invalid pointer for size of public addresses buffer!");
  2741. hr = DPNHERR_INVALIDPOINTER;
  2742. goto Failure;
  2743. }
  2744. }
  2745. if ((pdwAddressTypeFlags != NULL) &&
  2746. (IsBadWritePtr(pdwAddressTypeFlags, sizeof(DWORD))))
  2747. {
  2748. DPFX(DPFPREP, 0, "Invalid pointer for address type flags!");
  2749. hr = DPNHERR_INVALIDPOINTER;
  2750. goto Failure;
  2751. }
  2752. if ((pdwLeaseTimeRemaining != NULL) &&
  2753. (IsBadWritePtr(pdwLeaseTimeRemaining, sizeof(DWORD))))
  2754. {
  2755. DPFX(DPFPREP, 0, "Invalid pointer for lease time remaining!");
  2756. hr = DPNHERR_INVALIDPOINTER;
  2757. goto Failure;
  2758. }
  2759. if (dwFlags & ~DPNHGETREGISTEREDADDRESSES_LOCALFIREWALLREMAPONLY)
  2760. {
  2761. DPFX(DPFPREP, 0, "Invalid flags specified!");
  2762. hr = DPNHERR_INVALIDFLAGS;
  2763. goto Failure;
  2764. }
  2765. //
  2766. // Start the flags off with the information regarding TCP vs. UDP.
  2767. //
  2768. if (pRegisteredPort->IsTCP())
  2769. {
  2770. dwAddressTypeFlags = DPNHADDRESSTYPE_TCP;
  2771. }
  2772. else
  2773. {
  2774. dwAddressTypeFlags = 0;
  2775. }
  2776. //
  2777. // Add in other flags we know already.
  2778. //
  2779. if (pRegisteredPort->IsFixedPort())
  2780. {
  2781. dwAddressTypeFlags |= DPNHADDRESSTYPE_FIXEDPORTS;
  2782. }
  2783. if (pRegisteredPort->IsSharedPort())
  2784. {
  2785. dwAddressTypeFlags |= DPNHADDRESSTYPE_SHAREDPORTS;
  2786. }
  2787. //
  2788. // Attempt to take the lock, but be prepared for the re-entrancy error.
  2789. //
  2790. hr = this->TakeLock();
  2791. if (hr != DPNH_OK)
  2792. {
  2793. DPFX(DPFPREP, 0, "Could not lock object!");
  2794. goto Failure;
  2795. }
  2796. fHaveLock = TRUE;
  2797. //
  2798. // Make sure object is in right state.
  2799. //
  2800. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  2801. {
  2802. DPFX(DPFPREP, 0, "Object not initialized!");
  2803. hr = DPNHERR_NOTINITIALIZED;
  2804. goto Failure;
  2805. }
  2806. if (this->m_dwLastUpdateServerStatusTime == 0)
  2807. {
  2808. DPFX(DPFPREP, 0, "GetCaps has not been called with UPDATESERVERSTATUS flag yet!");
  2809. hr = DPNHERR_UPDATESERVERSTATUS;
  2810. goto Failure;
  2811. }
  2812. //
  2813. // Get a shortcut pointer to the device (may not exist).
  2814. //
  2815. pDevice = pRegisteredPort->GetOwningDevice();
  2816. //
  2817. // Get the current time for both the remote and local lease
  2818. // calculations.
  2819. //
  2820. dwCurrentTime = GETTIMESTAMP();
  2821. if (! (dwFlags & DPNHGETREGISTEREDADDRESSES_LOCALFIREWALLREMAPONLY))
  2822. {
  2823. CUPnPDevice * pUPnPDevice;
  2824. //
  2825. // First check for a mapping on the UPnP device.
  2826. //
  2827. if (pRegisteredPort->HasUPnPPublicAddresses())
  2828. {
  2829. DNASSERT(pDevice != NULL);
  2830. pUPnPDevice = pDevice->GetUPnPDevice();
  2831. DNASSERT(pUPnPDevice != NULL);
  2832. fRegisteredWithServer = TRUE;
  2833. //
  2834. // Make sure the UPnP device currently has a valid external
  2835. // address. If so, hand the mapping out.
  2836. //
  2837. if (pUPnPDevice->GetExternalIPAddressV4() != 0)
  2838. {
  2839. if (pdwPublicAddressesSize != NULL)
  2840. {
  2841. dwSizeRequired = pRegisteredPort->GetAddressesSize();
  2842. if ((paPublicAddresses == NULL) ||
  2843. (dwSizeRequired > (*pdwPublicAddressesSize)))
  2844. {
  2845. //
  2846. // Not enough room in buffer, return the size required
  2847. // and the BUFFERTOOSMALL error code.
  2848. //
  2849. (*pdwPublicAddressesSize) = dwSizeRequired;
  2850. hr = DPNHERR_BUFFERTOOSMALL;
  2851. }
  2852. else
  2853. {
  2854. //
  2855. // Buffer was large enough, return the size written.
  2856. //
  2857. (*pdwPublicAddressesSize) = dwSizeRequired;
  2858. pRegisteredPort->CopyUPnPPublicAddresses((SOCKADDR_IN*) paPublicAddresses);
  2859. }
  2860. }
  2861. else
  2862. {
  2863. //
  2864. // Not using address buffer.
  2865. //
  2866. }
  2867. fFoundValidMapping = TRUE;
  2868. }
  2869. else
  2870. {
  2871. DPFX(DPFPREP, 8, "The UPnP Internet Gateway Device does not currently have a valid public address.");
  2872. }
  2873. //
  2874. // Add in the flag indicating that there's a UPnP device.
  2875. //
  2876. dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAY;
  2877. //
  2878. // See if the UPnP device is local.
  2879. //
  2880. if (pUPnPDevice->IsLocal())
  2881. {
  2882. dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAYISLOCAL;
  2883. }
  2884. //
  2885. // Get the relative UPnP lease time remaining, if it's not
  2886. // permanent.
  2887. //
  2888. if (! pRegisteredPort->HasPermanentUPnPLease())
  2889. {
  2890. dwLeaseTimeRemaining = pRegisteredPort->GetUPnPLeaseExpiration() - dwCurrentTime;
  2891. if (((int) dwLeaseTimeRemaining) < 0)
  2892. {
  2893. DPFX(DPFPREP, 1, "Registered port mapping's UPnP lease has already expired, returning 0 for lease time remaining.");
  2894. dwLeaseTimeRemaining = 0;
  2895. }
  2896. }
  2897. }
  2898. else if (pRegisteredPort->IsUPnPPortUnavailable())
  2899. {
  2900. DNASSERT(pDevice != NULL);
  2901. pUPnPDevice = pDevice->GetUPnPDevice();
  2902. DNASSERT(pUPnPDevice != NULL);
  2903. fRegisteredWithServer = TRUE;
  2904. fPortIsUnavailable = TRUE;
  2905. DPFX(DPFPREP, 8, "The UPnP device indicates the port(s) are unavailable.");
  2906. //
  2907. // Add in the flag indicating that there's a UPnP device.
  2908. //
  2909. dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAY;
  2910. //
  2911. // See if the UPnP device is local.
  2912. //
  2913. if (pUPnPDevice->IsLocal())
  2914. {
  2915. dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAYISLOCAL;
  2916. }
  2917. }
  2918. }
  2919. else
  2920. {
  2921. //
  2922. // We're not allowed to return the UPnP mapping.
  2923. //
  2924. DPFX(DPFPREP, 8, "Ignoring any Internet gateway mappings, LOCALFIREWALLREMAPONLY was specified.");
  2925. }
  2926. #ifndef DPNBUILD_NOHNETFWAPI
  2927. //
  2928. // Finally, check for a mapping on a local firewall.
  2929. //
  2930. if (pRegisteredPort->IsMappedOnHNetFirewall())
  2931. {
  2932. DNASSERT(pDevice != NULL);
  2933. DNASSERT(pDevice->IsHNetFirewalled());
  2934. fRegisteredWithServer = TRUE;
  2935. //
  2936. // If we didn't already get a remote mapping, return this local one.
  2937. //
  2938. if (! fFoundValidMapping)
  2939. {
  2940. if (pdwPublicAddressesSize != NULL)
  2941. {
  2942. dwSizeRequired = pRegisteredPort->GetAddressesSize();
  2943. if ((paPublicAddresses == NULL) ||
  2944. (dwSizeRequired > (*pdwPublicAddressesSize)))
  2945. {
  2946. //
  2947. // Not enough room in buffer, return the size required
  2948. // and the BUFFERTOOSMALL error code.
  2949. //
  2950. (*pdwPublicAddressesSize) = dwSizeRequired;
  2951. hr = DPNHERR_BUFFERTOOSMALL;
  2952. }
  2953. else
  2954. {
  2955. SOCKADDR_IN * pasaddrinPrivate;
  2956. DWORD dwTemp;
  2957. //
  2958. // Buffer was large enough, return the size written.
  2959. //
  2960. (*pdwPublicAddressesSize) = dwSizeRequired;
  2961. //
  2962. // Note that the addresses mapped on the firewall are the
  2963. // same as the private addresses.
  2964. //
  2965. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  2966. DNASSERT(pasaddrinPrivate != NULL);
  2967. memcpy(paPublicAddresses, pasaddrinPrivate, dwSizeRequired);
  2968. //
  2969. // However, we don't want to ever return 0.0.0.0, so make
  2970. // sure they get the device address.
  2971. //
  2972. if (pasaddrinPrivate[0].sin_addr.S_un.S_addr == INADDR_ANY)
  2973. {
  2974. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  2975. {
  2976. ((SOCKADDR_IN*) paPublicAddresses)[dwTemp].sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  2977. }
  2978. DPFX(DPFPREP, 7, "Returning device address %u.%u.%u.%u instead of INADDR_ANY for firewalled port mapping 0x%p.",
  2979. ((SOCKADDR_IN*) paPublicAddresses)[0].sin_addr.S_un.S_un_b.s_b1,
  2980. ((SOCKADDR_IN*) paPublicAddresses)[0].sin_addr.S_un.S_un_b.s_b2,
  2981. ((SOCKADDR_IN*) paPublicAddresses)[0].sin_addr.S_un.S_un_b.s_b3,
  2982. ((SOCKADDR_IN*) paPublicAddresses)[0].sin_addr.S_un.S_un_b.s_b4,
  2983. pRegisteredPort);
  2984. }
  2985. }
  2986. }
  2987. else
  2988. {
  2989. //
  2990. // Not using address buffer.
  2991. //
  2992. }
  2993. fFoundValidMapping = TRUE;
  2994. }
  2995. else
  2996. {
  2997. DPFX(DPFPREP, 6, "Ignoring local HomeNet firewall mapping due to UPnP mapping.");
  2998. }
  2999. //
  3000. // Add in the flag indicating the local firewall.
  3001. //
  3002. dwAddressTypeFlags |= DPNHADDRESSTYPE_LOCALFIREWALL;
  3003. //
  3004. // The firewall API does not allow for lease times.
  3005. //
  3006. }
  3007. else
  3008. {
  3009. if (pRegisteredPort->IsHNetFirewallPortUnavailable())
  3010. {
  3011. DNASSERT(pDevice != NULL);
  3012. DNASSERT(pDevice->IsHNetFirewalled());
  3013. fRegisteredWithServer = TRUE;
  3014. fPortIsUnavailable = TRUE;
  3015. DPFX(DPFPREP, 8, "The local HomeNet firewall indicates the port(s) are unavailable.");
  3016. //
  3017. // Add in the flag indicating the local firewall.
  3018. //
  3019. dwAddressTypeFlags |= DPNHADDRESSTYPE_LOCALFIREWALL;
  3020. }
  3021. #ifdef DBG
  3022. else
  3023. {
  3024. //
  3025. // No local firewall or it's an unowned port.
  3026. //
  3027. if (pDevice != NULL)
  3028. {
  3029. DNASSERT(! pDevice->IsHNetFirewalled());
  3030. }
  3031. else
  3032. {
  3033. DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts));
  3034. }
  3035. }
  3036. #endif // DBG
  3037. }
  3038. #endif // ! DPNBUILD_NOHNETFWAPI
  3039. this->DropLock();
  3040. fHaveLock = FALSE;
  3041. if (fRegisteredWithServer)
  3042. {
  3043. DNASSERT(dwAddressTypeFlags & (DPNHADDRESSTYPE_LOCALFIREWALL | DPNHADDRESSTYPE_GATEWAY));
  3044. if (! fFoundValidMapping)
  3045. {
  3046. if (fPortIsUnavailable)
  3047. {
  3048. //
  3049. // The servers indicated that the ports were already in use.
  3050. // Return PORTUNAVAILABLE.
  3051. //
  3052. DPFX(DPFPREP, 1, "The Internet gateway(s) could not map the port, returning PORTUNAVAILABLE.");
  3053. hr = DPNHERR_PORTUNAVAILABLE;
  3054. }
  3055. else
  3056. {
  3057. //
  3058. // The servers didn't have public addresses. Return NOMAPPING.
  3059. //
  3060. DPFX(DPFPREP, 1, "The Internet gateway(s) did not offer valid public addresses, returning NOMAPPING.");
  3061. hr = DPNHERR_NOMAPPING;
  3062. }
  3063. }
  3064. else
  3065. {
  3066. //
  3067. // One of the servers had a public address.
  3068. //
  3069. DNASSERT((hr == DPNH_OK) || (hr == DPNHERR_BUFFERTOOSMALL));
  3070. }
  3071. }
  3072. else
  3073. {
  3074. //
  3075. // The ports aren't registered, because there aren't any gateways.
  3076. // Return SERVERNOTAVAILABLE.
  3077. //
  3078. DPFX(DPFPREP, 1, "No Internet gateways, returning SERVERNOTAVAILABLE.");
  3079. hr = DPNHERR_SERVERNOTAVAILABLE;
  3080. }
  3081. //
  3082. // If the caller wants information on the type of these addresses, return
  3083. // the flags we detected.
  3084. //
  3085. if (pdwAddressTypeFlags != NULL)
  3086. {
  3087. (*pdwAddressTypeFlags) = dwAddressTypeFlags;
  3088. }
  3089. //
  3090. // Return the minimum lease time remaining that we already calculated, if
  3091. // the caller wants it.
  3092. //
  3093. if (pdwLeaseTimeRemaining != NULL)
  3094. {
  3095. (*pdwLeaseTimeRemaining) = dwLeaseTimeRemaining;
  3096. }
  3097. #ifdef DBG
  3098. //
  3099. // If the port is unavailable or there aren't any servers, we better not
  3100. // have a lease time.
  3101. //
  3102. if ((hr == DPNHERR_PORTUNAVAILABLE) ||
  3103. (hr == DPNHERR_SERVERNOTAVAILABLE))
  3104. {
  3105. DNASSERT(dwLeaseTimeRemaining == -1);
  3106. }
  3107. //
  3108. // If there aren't any servers, we better not have server flags.
  3109. //
  3110. if (hr == DPNHERR_SERVERNOTAVAILABLE)
  3111. {
  3112. DNASSERT(! (dwAddressTypeFlags & (DPNHADDRESSTYPE_LOCALFIREWALL | DPNHADDRESSTYPE_GATEWAY)));
  3113. }
  3114. #endif // DBG
  3115. DPFX(DPFPREP, 5, "Registered port 0x%p addr type flags = 0x%lx, lease time remaining = %i.",
  3116. pRegisteredPort, dwAddressTypeFlags, (int) dwLeaseTimeRemaining);
  3117. Exit:
  3118. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  3119. return hr;
  3120. Failure:
  3121. if (fHaveLock)
  3122. {
  3123. this->DropLock();
  3124. fHaveLock = FALSE;
  3125. }
  3126. goto Exit;
  3127. } // CNATHelpUPnP::GetRegisteredAddresses
  3128. #undef DPF_MODNAME
  3129. #define DPF_MODNAME "CNATHelpUPnP::DeregisterPorts"
  3130. //=============================================================================
  3131. // CNATHelpUPnP::DeregisterPorts
  3132. //-----------------------------------------------------------------------------
  3133. //
  3134. // Description: Removes the lease record for the port group and informs the
  3135. // Internet gateway server that the binding is no longer needed.
  3136. // The port mapping handle must not be used after de-registering
  3137. // it.
  3138. //
  3139. // Arguments:
  3140. // DPNHHANDLE hRegisteredPorts - Handle for a specific binding returned by
  3141. // RegisterPorts.
  3142. // DWORD dwFlags - Unused, must be zero.
  3143. //
  3144. // Returns: HRESULT
  3145. // DPNH_OK - The binding was successfully released.
  3146. // DPNHERR_GENERIC - An error occurred that prevented the
  3147. // de-registration of the ports.
  3148. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  3149. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  3150. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  3151. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  3152. // DPNHERR_OUTOFMEMORY - There is not enough memory to de-register.
  3153. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  3154. // thread.
  3155. //=============================================================================
  3156. STDMETHODIMP CNATHelpUPnP::DeregisterPorts(const DPNHHANDLE hRegisteredPorts,
  3157. const DWORD dwFlags)
  3158. {
  3159. HRESULT hr;
  3160. CRegisteredPort * pRegisteredPort;
  3161. BOOL fHaveLock = FALSE;
  3162. LONG lResult;
  3163. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx)",
  3164. this, hRegisteredPorts, dwFlags);
  3165. //
  3166. // Validate the object.
  3167. //
  3168. if (! this->IsValidObject())
  3169. {
  3170. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  3171. hr = DPNHERR_INVALIDOBJECT;
  3172. goto Failure;
  3173. }
  3174. //
  3175. // Validate the parameters.
  3176. //
  3177. pRegisteredPort = (CRegisteredPort*) hRegisteredPorts;
  3178. if (! pRegisteredPort->IsValidObject())
  3179. {
  3180. DPFX(DPFPREP, 0, "Invalid registered port mapping handle specified!");
  3181. hr = DPNHERR_INVALIDPARAM;
  3182. goto Failure;
  3183. }
  3184. if (dwFlags != 0)
  3185. {
  3186. DPFX(DPFPREP, 0, "Invalid flags specified!");
  3187. hr = DPNHERR_INVALIDFLAGS;
  3188. goto Failure;
  3189. }
  3190. //
  3191. // Attempt to take the lock, but be prepared for the re-entrancy error.
  3192. //
  3193. hr = this->TakeLock();
  3194. if (hr != DPNH_OK)
  3195. {
  3196. DPFX(DPFPREP, 0, "Could not lock object!");
  3197. goto Failure;
  3198. }
  3199. fHaveLock = TRUE;
  3200. //
  3201. // Make sure object is in right state.
  3202. //
  3203. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  3204. {
  3205. DPFX(DPFPREP, 0, "Object not initialized!");
  3206. hr = DPNHERR_NOTINITIALIZED;
  3207. goto Failure;
  3208. }
  3209. //
  3210. // If this isn't the last user reference on the registered port, don't
  3211. // unmap it yet.
  3212. //
  3213. lResult = pRegisteredPort->DecUserRef();
  3214. if (lResult != 0)
  3215. {
  3216. DPFX(DPFPREP, 1, "Still %i references left on registered port 0x%p, not unmapping.",
  3217. lResult, pRegisteredPort);
  3218. goto Exit;
  3219. }
  3220. //
  3221. // First unmap from UPnP device, if necessary.
  3222. //
  3223. if (pRegisteredPort->HasUPnPPublicAddresses())
  3224. {
  3225. hr = this->UnmapUPnPPort(pRegisteredPort,
  3226. pRegisteredPort->GetNumAddresses(), // free all ports
  3227. TRUE);
  3228. if (hr != DPNH_OK)
  3229. {
  3230. DPFX(DPFPREP, 0, "Couldn't delete port mapping with UPnP device (0x%lx)! Ignoring.", hr);
  3231. //
  3232. // We'll treat this as non-fatal, but we have to dump the device.
  3233. //
  3234. this->ClearDevicesUPnPDevice(pRegisteredPort->GetOwningDevice());
  3235. hr = DPNH_OK;
  3236. }
  3237. }
  3238. #ifndef DPNBUILD_NOHNETFWAPI
  3239. //
  3240. // Then unmap from the local firewall, if necessary.
  3241. //
  3242. if (pRegisteredPort->IsMappedOnHNetFirewall())
  3243. {
  3244. //
  3245. // Unmap the port.
  3246. //
  3247. // Don't bother alerting user about address change, this is normal
  3248. // operation.
  3249. //
  3250. hr = this->UnmapPortOnLocalHNetFirewall(pRegisteredPort, TRUE, FALSE);
  3251. if (hr != DPNH_OK)
  3252. {
  3253. DPFX(DPFPREP, 0, "Failed unmapping registered port 0x%p on local HomeNet firewall (err = 0x%lx)! Ignoring.",
  3254. pRegisteredPort, hr);
  3255. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  3256. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  3257. hr = DPNH_OK;
  3258. }
  3259. }
  3260. #endif // ! DPNBUILD_NOHNETFWAPI
  3261. //
  3262. // Pull the item out of the lists.
  3263. // We have the appropriate lock.
  3264. //
  3265. DNASSERT(pRegisteredPort->m_blGlobalList.IsListMember(&this->m_blRegisteredPorts));
  3266. pRegisteredPort->m_blGlobalList.RemoveFromList();
  3267. if (pRegisteredPort->GetOwningDevice() != NULL)
  3268. {
  3269. DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&((pRegisteredPort->GetOwningDevice())->m_blOwnedRegPorts)));
  3270. pRegisteredPort->ClearDeviceOwner();
  3271. }
  3272. else
  3273. {
  3274. DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts));
  3275. pRegisteredPort->m_blDeviceList.RemoveFromList();
  3276. }
  3277. pRegisteredPort->ClearPrivateAddresses();
  3278. delete pRegisteredPort;
  3279. Exit:
  3280. if (fHaveLock)
  3281. {
  3282. this->DropLock();
  3283. fHaveLock = FALSE;
  3284. }
  3285. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  3286. return hr;
  3287. Failure:
  3288. goto Exit;
  3289. } // CNATHelpUPnP::DeregisterPorts
  3290. #undef DPF_MODNAME
  3291. #define DPF_MODNAME "CNATHelpUPnP::QueryAddress"
  3292. //=============================================================================
  3293. // CNATHelpUPnP::QueryAddress
  3294. //-----------------------------------------------------------------------------
  3295. //
  3296. // Description: Some Internet gateways do not loopback if an attempt is made
  3297. // to connect to an address behind (on the same private side of)
  3298. // the public interface. QueryAddress is used to determine a
  3299. // possible private alias for a given public address.
  3300. //
  3301. // In most cases, this function is called prior to connecting
  3302. // to a new address. pSourceAddress should contain the address of
  3303. // the socket that will perform the connect. Similar to
  3304. // RegisterPorts, the address may be INADDR_ANY, in which case the
  3305. // "best" server will be used. Since the server chosen may not be
  3306. // optimal for a particular application, it is recommended that a
  3307. // specific network interface be used instead of INADDR_ANY, when
  3308. // possible.
  3309. //
  3310. // If no mapping for that address has been made by the gateway,
  3311. // the error code DPNHERR_NOMAPPING is returned. When the
  3312. // DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED flag is used, an
  3313. // extra effort is made to determine whether the address is behind
  3314. // the same Internet gateway without being mapped on the gateway.
  3315. // If that is the case, DPNHERR_NOMAPPINGBUTPRIVATE is returned.
  3316. // DPNHERR_NOMAPPING is still returned for addresses that are
  3317. // neither mapped nor private.
  3318. //
  3319. // pQueryAddress may not be INADDR_ANY or INADDR_BROADCAST.
  3320. // The port component may be zero if and only if the
  3321. // DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED flag is used. If
  3322. // the port is zero, a specific mapping cannot be verified, and
  3323. // only the DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED aspect of
  3324. // the address is tested.
  3325. //
  3326. // The resulting address (or lack thereof) can be cached for
  3327. // quick future retrieval using the DPNHQUERYADDRESS_CACHEFOUND
  3328. // and DPNHQUERYADDRESS_CACHENOTFOUND flags. The cached mappings
  3329. // will expire in 1 minute, or whenever the server's address
  3330. // changes.
  3331. //
  3332. // If the given source address is not currently connected to an
  3333. // Internet gateway, then the error DPNHERR_SERVERNOTAVAILABLE is
  3334. // returned.
  3335. //
  3336. // If GetCaps has not been previously called with the
  3337. // DPNHGETCAPS_UPDATESERVERSTATUS flag at least once, then the
  3338. // error code DPNHERR_UPDATESERVERSTATUS is returned.
  3339. //
  3340. // Arguments:
  3341. // SOCKADDR * pSourceAddress - Address for network interface that is using
  3342. // the address in question.
  3343. // SOCKADDR * pQueryAddress - Address to look up.
  3344. // SOCKADDR * pResponseAddress - Place to store public address, if one exists.
  3345. // int iAddressesSize - Size of the SOCKADDR structure used for the
  3346. // pSourceAddress, pQueryAddress and
  3347. // pResponseAddress buffers.
  3348. // DWORD dwFlags - Flags to use when querying
  3349. // (DPNHQUERYADDRESS_xxx).
  3350. //
  3351. // Returns: HRESULT
  3352. // DPNH_OK - The address was found and its mapping was
  3353. // stored in pResponseAddress.
  3354. // DPNHERR_GENERIC - An error occurred that prevented mapping the
  3355. // requested address.
  3356. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  3357. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  3358. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  3359. // DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
  3360. // DPNHERR_NOMAPPING - The server indicated that no mapping for the
  3361. // requested address was found.
  3362. // DPNHERR_NOMAPPINGBUTPRIVATE - The server indicated that no mapping was
  3363. // found, but it is a private address.
  3364. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  3365. // DPNHERR_OUTOFMEMORY - There is not enough memory to query.
  3366. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  3367. // thread.
  3368. // DPNHERR_SERVERNOTAVAILABLE - There are no servers to query.
  3369. // DPNHERR_UPDATESERVERSTATUS - GetCaps has not been called with the
  3370. // DPNHGETCAPS_UPDATESERVERSTATUS flag yet.
  3371. //=============================================================================
  3372. STDMETHODIMP CNATHelpUPnP::QueryAddress(const SOCKADDR * const pSourceAddress,
  3373. const SOCKADDR * const pQueryAddress,
  3374. SOCKADDR * const pResponseAddress,
  3375. const int iAddressesSize,
  3376. const DWORD dwFlags)
  3377. {
  3378. HRESULT hr;
  3379. BOOL fHaveLock = FALSE;
  3380. CDevice * pDevice;
  3381. SOCKADDR_IN * psaddrinNextServerQueryAddress = NULL;
  3382. CUPnPDevice * pUPnPDevice = NULL;
  3383. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, %i, 0x%lx)",
  3384. this, pSourceAddress, pQueryAddress, pResponseAddress, iAddressesSize,
  3385. dwFlags);
  3386. //
  3387. // Validate the object.
  3388. //
  3389. if (! this->IsValidObject())
  3390. {
  3391. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  3392. hr = DPNHERR_INVALIDOBJECT;
  3393. goto Failure;
  3394. }
  3395. //
  3396. // Validate the parameters.
  3397. //
  3398. if (pSourceAddress == NULL)
  3399. {
  3400. DPFX(DPFPREP, 0, "Invalid source address specified!");
  3401. hr = DPNHERR_INVALIDPOINTER;
  3402. goto Failure;
  3403. }
  3404. if (pQueryAddress == NULL)
  3405. {
  3406. DPFX(DPFPREP, 0, "Invalid query address specified!");
  3407. hr = DPNHERR_INVALIDPOINTER;
  3408. goto Failure;
  3409. }
  3410. if (pResponseAddress == NULL)
  3411. {
  3412. DPFX(DPFPREP, 0, "Invalid response address specified!");
  3413. hr = DPNHERR_INVALIDPOINTER;
  3414. goto Failure;
  3415. }
  3416. if (iAddressesSize < sizeof(SOCKADDR_IN))
  3417. {
  3418. DPFX(DPFPREP, 0, "The address buffers must be at least %i bytes!",
  3419. sizeof(SOCKADDR_IN));
  3420. hr = DPNHERR_INVALIDPARAM;
  3421. goto Failure;
  3422. }
  3423. if (IsBadReadPtr(pSourceAddress, sizeof(SOCKADDR_IN)))
  3424. {
  3425. DPFX(DPFPREP, 0, "Invalid source address buffer used!");
  3426. hr = DPNHERR_INVALIDPOINTER;
  3427. goto Failure;
  3428. }
  3429. if (IsBadReadPtr(pQueryAddress, sizeof(SOCKADDR_IN)))
  3430. {
  3431. DPFX(DPFPREP, 0, "Invalid query address buffer used!");
  3432. hr = DPNHERR_INVALIDPOINTER;
  3433. goto Failure;
  3434. }
  3435. if (IsBadWritePtr(pResponseAddress, sizeof(SOCKADDR_IN)))
  3436. {
  3437. DPFX(DPFPREP, 0, "Invalid response address buffer used!");
  3438. hr = DPNHERR_INVALIDPOINTER;
  3439. goto Failure;
  3440. }
  3441. if ((((SOCKADDR_IN*) pSourceAddress)->sin_family != AF_INET) ||
  3442. (((SOCKADDR_IN*) pQueryAddress)->sin_family != AF_INET))
  3443. {
  3444. DPFX(DPFPREP, 0, "Source or query address is not AF_INET, only IPv4 addresses are supported!");
  3445. hr = DPNHERR_INVALIDPARAM;
  3446. goto Failure;
  3447. }
  3448. if (((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_addr == INADDR_BROADCAST)
  3449. {
  3450. DPFX(DPFPREP, 0, "Source address cannot be broadcast address!");
  3451. hr = DPNHERR_INVALIDPARAM;
  3452. goto Failure;
  3453. }
  3454. if ((((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_addr == INADDR_ANY) ||
  3455. (((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_addr == INADDR_BROADCAST))
  3456. {
  3457. //
  3458. // Don't use inet_ntoa because we may not be initialized yet.
  3459. //
  3460. DPFX(DPFPREP, 0, "Query address (%u.%u.%u.%u) is invalid, cannot be zero or broadcast!",
  3461. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b1,
  3462. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b2,
  3463. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b3,
  3464. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b4);
  3465. hr = DPNHERR_INVALIDPARAM;
  3466. goto Failure;
  3467. }
  3468. if (dwFlags & ~(DPNHQUERYADDRESS_TCP | DPNHQUERYADDRESS_CACHEFOUND | DPNHQUERYADDRESS_CACHENOTFOUND | DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED))
  3469. {
  3470. DPFX(DPFPREP, 0, "Invalid flags specified!");
  3471. hr = DPNHERR_INVALIDFLAGS;
  3472. goto Failure;
  3473. }
  3474. if ((((SOCKADDR_IN*) pQueryAddress)->sin_port == 0) &&
  3475. (! (dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED)))
  3476. {
  3477. DPFX(DPFPREP, 0, "Query address port cannot be zero unless CHECKFORPRIVATEBUTUNMAPPED is specified!");
  3478. hr = DPNHERR_INVALIDPARAM;
  3479. goto Failure;
  3480. }
  3481. //
  3482. // Attempt to take the lock, but be prepared for the re-entrancy error.
  3483. //
  3484. hr = this->TakeLock();
  3485. if (hr != DPNH_OK)
  3486. {
  3487. DPFX(DPFPREP, 0, "Could not lock object!");
  3488. goto Failure;
  3489. }
  3490. fHaveLock = TRUE;
  3491. //
  3492. // Make sure object is in right state.
  3493. //
  3494. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  3495. {
  3496. DPFX(DPFPREP, 0, "Object not initialized!");
  3497. hr = DPNHERR_NOTINITIALIZED;
  3498. goto Failure;
  3499. }
  3500. if (this->m_dwLastUpdateServerStatusTime == 0)
  3501. {
  3502. DPFX(DPFPREP, 0, "GetCaps has not been called with UPDATESERVERSTATUS flag yet!");
  3503. hr = DPNHERR_UPDATESERVERSTATUS;
  3504. goto Failure;
  3505. }
  3506. pDevice = this->FindMatchingDevice((SOCKADDR_IN*) pSourceAddress, TRUE);
  3507. if (pDevice == NULL)
  3508. {
  3509. DPFX(DPFPREP, 1, "Couldn't determine owning device for source %u.%u.%u.%u, returning SERVERNOTAVAILABLE for query %u.%u.%u.%u:%u.",
  3510. ((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_un_b.s_b1,
  3511. ((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_un_b.s_b2,
  3512. ((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_un_b.s_b3,
  3513. ((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_un_b.s_b4,
  3514. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b1,
  3515. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b2,
  3516. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b3,
  3517. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b4,
  3518. NTOHS(((SOCKADDR_IN*) pQueryAddress)->sin_port));
  3519. hr = DPNHERR_SERVERNOTAVAILABLE;
  3520. goto Exit;
  3521. }
  3522. //
  3523. // Assume no servers are available. This will get overridden as
  3524. // appropriate.
  3525. //
  3526. hr = DPNHERR_SERVERNOTAVAILABLE;
  3527. //
  3528. // Start by querying the address passed in.
  3529. //
  3530. psaddrinNextServerQueryAddress = (SOCKADDR_IN*) pQueryAddress;
  3531. //
  3532. // If the port is zero, then we can't actually lookup a mapping. Just do
  3533. // the address locality check.
  3534. //
  3535. if (psaddrinNextServerQueryAddress->sin_port == 0)
  3536. {
  3537. //
  3538. // We should have caught this in parameter validation above, but I'm
  3539. // being paranoid.
  3540. //
  3541. DNASSERT(dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED);
  3542. //
  3543. // We don't cache these results, since there's no server (and thus, no
  3544. // network traffic) associated with it. No need to look anything up.
  3545. //
  3546. //
  3547. // If there aren't any Internet gateways, then no need to check.
  3548. //
  3549. #ifdef DPNBUILD_NOHNETFWAPI
  3550. if (pDevice->GetUPnPDevice() == NULL)
  3551. #else // ! DPNBUILD_NOHNETFWAPI
  3552. if ((pDevice->GetUPnPDevice() == NULL) &&
  3553. (! pDevice->IsHNetFirewalled()))
  3554. #endif // ! DPNBUILD_NOHNETFWAPI
  3555. {
  3556. DPFX(DPFPREP, 5, "No port queried and there aren't any gateways, returning SERVERNOTAVAILABLE.");
  3557. hr = DPNHERR_SERVERNOTAVAILABLE;
  3558. }
  3559. else
  3560. {
  3561. //
  3562. // There is an Internet gateway of some kind, our locality check
  3563. // would be meaningful.
  3564. //
  3565. if (this->IsAddressLocal(pDevice, psaddrinNextServerQueryAddress))
  3566. {
  3567. DPFX(DPFPREP, 5, "No port queried, but address appears to be local, returning NOMAPPINGBUTPRIVATE.");
  3568. hr = DPNHERR_NOMAPPINGBUTPRIVATE;
  3569. }
  3570. else
  3571. {
  3572. DPFX(DPFPREP, 5, "No port queried and address does not appear to be local, returning NOMAPPING.");
  3573. hr = DPNHERR_NOMAPPING;
  3574. }
  3575. }
  3576. //
  3577. // We've done all we can do.
  3578. //
  3579. goto Exit;
  3580. }
  3581. //
  3582. // Query the UPnP gateway, if there is one.
  3583. //
  3584. pUPnPDevice = pDevice->GetUPnPDevice();
  3585. if (pUPnPDevice != NULL)
  3586. {
  3587. //
  3588. // GetUPnPDevice did not add a reference to pUPnPDevice for us.
  3589. //
  3590. pUPnPDevice->AddRef();
  3591. DNASSERT(pUPnPDevice->IsReady());
  3592. //
  3593. // Actually query the device.
  3594. //
  3595. hr = this->InternalUPnPQueryAddress(pUPnPDevice,
  3596. psaddrinNextServerQueryAddress,
  3597. (SOCKADDR_IN*) pResponseAddress,
  3598. dwFlags);
  3599. switch (hr)
  3600. {
  3601. case DPNH_OK:
  3602. {
  3603. //
  3604. // There was a mapping.
  3605. //
  3606. //psaddrinNextServerQueryAddress = (SOCKADDR_IN*) pResponseAddress;
  3607. break;
  3608. }
  3609. case DPNHERR_NOMAPPING:
  3610. {
  3611. //
  3612. // There's no mapping.
  3613. //
  3614. break;
  3615. }
  3616. case DPNHERR_NOMAPPINGBUTPRIVATE:
  3617. {
  3618. //
  3619. // There's no mapping although the address is private.
  3620. //
  3621. break;
  3622. }
  3623. case DPNHERR_SERVERNOTRESPONDING:
  3624. {
  3625. //
  3626. // The device stopped responding, so we should get rid of it.
  3627. //
  3628. DPFX(DPFPREP, 1, "UPnP device stopped responding while querying port mapping, removing it.");
  3629. this->ClearDevicesUPnPDevice(pDevice);
  3630. //
  3631. // We also set the return code back to SERVERNOTAVAILABLE.
  3632. //
  3633. hr = DPNHERR_SERVERNOTAVAILABLE;
  3634. //
  3635. // Continue through to querying the HomeNet firewall.
  3636. //
  3637. break;
  3638. }
  3639. default:
  3640. {
  3641. DPFX(DPFPREP, 0, "Querying UPnP device for port mapping failed!");
  3642. goto Failure;
  3643. break;
  3644. }
  3645. }
  3646. pUPnPDevice->DecRef();
  3647. pUPnPDevice = NULL;
  3648. }
  3649. else
  3650. {
  3651. //
  3652. // No UPnP device.
  3653. //
  3654. }
  3655. #ifndef DPNBUILD_NOHNETFWAPI
  3656. //
  3657. // If there's a HomeNet firewall and we didn't already get a UPnP result,
  3658. // take the easy way out and return NOMAPPING instead of going through the
  3659. // trouble of looking up the mapping and returning success only if it maps
  3660. // to a local address.
  3661. //
  3662. // Note: we may want to look it up, but right now I'm not seeing any
  3663. // benefit to implementing that code.
  3664. //
  3665. if ((pDevice->IsHNetFirewalled()) && (hr == DPNHERR_SERVERNOTAVAILABLE))
  3666. {
  3667. DPFX(DPFPREP, 7, "Device is HomeNet firewalled, and no UPnP result obtained, returning NOMAPPING.");
  3668. hr = DPNHERR_NOMAPPING;
  3669. }
  3670. #endif // ! DPNBUILD_NOHNETFWAPI
  3671. //
  3672. // If we got here with hr still set to SERVERNOTAVAILABLE, that means
  3673. // there weren't any servers. The error code is appropriate, leave it
  3674. // alone.
  3675. //
  3676. #ifdef DBG
  3677. if (hr == DPNHERR_SERVERNOTAVAILABLE)
  3678. {
  3679. DPFX(DPFPREP, 1, "No Internet gateways, unable to query port mapping.");
  3680. }
  3681. #endif // DBG
  3682. Exit:
  3683. if (fHaveLock)
  3684. {
  3685. this->DropLock();
  3686. fHaveLock = FALSE;
  3687. }
  3688. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  3689. return hr;
  3690. Failure:
  3691. if (pUPnPDevice != NULL)
  3692. {
  3693. pUPnPDevice->DecRef();
  3694. }
  3695. goto Exit;
  3696. } // CNATHelpUPnP::QueryAddress
  3697. #undef DPF_MODNAME
  3698. #define DPF_MODNAME "CNATHelpUPnP::SetAlertEvent"
  3699. //=============================================================================
  3700. // CNATHelpUPnP::SetAlertEvent
  3701. //-----------------------------------------------------------------------------
  3702. //
  3703. // Description: This function allows the user to specify an event that will
  3704. // be set when some maintenance needs to be performed. The user
  3705. // should call GetCaps using the DPNHGETCAPS_UPDATESERVERSTATUS
  3706. // flag when the event is signalled.
  3707. //
  3708. // This function is not available on Windows 95 without WinSock
  3709. // 2, may only be called once, and cannot be used after
  3710. // SetAlertIOCompletionPort is called.
  3711. //
  3712. // Note that the event is used in addition to the regular
  3713. // polling of GetCaps, it simply allows the polling to be less
  3714. // frequent.
  3715. //
  3716. // Arguments:
  3717. // HANDLE hEvent - Handle to event to signal when GetCaps is to be called.
  3718. // DWORD dwFlags - Unused, must be zero.
  3719. //
  3720. // Returns: HRESULT
  3721. // DPNH_OK - The event was successfully registered.
  3722. // DPNHERR_GENERIC - An error occurred that prevented registering the
  3723. // event.
  3724. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  3725. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  3726. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  3727. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  3728. // DPNHERR_OUTOFMEMORY - There is not enough memory.
  3729. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  3730. // thread.
  3731. //=============================================================================
  3732. STDMETHODIMP CNATHelpUPnP::SetAlertEvent(const HANDLE hEvent,
  3733. const DWORD dwFlags)
  3734. {
  3735. #ifdef DPNBUILD_NOWINSOCK2
  3736. DPFX(DPFPREP, 0, "Cannot set alert event (0x%p)!", hEvent);
  3737. return E_NOTIMPL;
  3738. #else // ! DPNBUILD_NOWINSOCK2
  3739. HRESULT hr;
  3740. BOOL fHaveLock = FALSE;
  3741. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx)", this, hEvent, dwFlags);
  3742. //
  3743. // Validate the object.
  3744. //
  3745. if (! this->IsValidObject())
  3746. {
  3747. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  3748. hr = DPNHERR_INVALIDOBJECT;
  3749. goto Failure;
  3750. }
  3751. //
  3752. // Validate the parameters.
  3753. //
  3754. if (hEvent == NULL)
  3755. {
  3756. DPFX(DPFPREP, 0, "Invalid event handle specified!");
  3757. hr = DPNHERR_INVALIDPARAM;
  3758. goto Failure;
  3759. }
  3760. if (dwFlags != 0)
  3761. {
  3762. DPFX(DPFPREP, 0, "Invalid flags specified!");
  3763. hr = DPNHERR_INVALIDFLAGS;
  3764. goto Failure;
  3765. }
  3766. //
  3767. // Attempt to take the lock, but be prepared for the re-entrancy error.
  3768. //
  3769. hr = this->TakeLock();
  3770. if (hr != DPNH_OK)
  3771. {
  3772. DPFX(DPFPREP, 0, "Could not lock object!");
  3773. goto Failure;
  3774. }
  3775. fHaveLock = TRUE;
  3776. //
  3777. // Make sure object is in right state.
  3778. //
  3779. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED))
  3780. {
  3781. DPFX(DPFPREP, 0, "Object not initialized!");
  3782. hr = DPNHERR_NOTINITIALIZED;
  3783. goto Failure;
  3784. }
  3785. if (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1)
  3786. {
  3787. DPFX(DPFPREP, 0, "Cannot use alert mechanism on WinSock 1!");
  3788. hr = DPNHERR_GENERIC;
  3789. goto Failure;
  3790. }
  3791. if ((this->m_hAlertEvent != NULL) || (this->m_hAlertIOCompletionPort != NULL))
  3792. {
  3793. DPFX(DPFPREP, 0, "An alert event or I/O completion port has already been set!");
  3794. hr = DPNHERR_GENERIC;
  3795. goto Failure;
  3796. }
  3797. //
  3798. // Now save the event handle.
  3799. //
  3800. if (! DuplicateHandle(GetCurrentProcess(),
  3801. hEvent,
  3802. GetCurrentProcess(),
  3803. &this->m_hAlertEvent,
  3804. 0,
  3805. FALSE,
  3806. DUPLICATE_SAME_ACCESS))
  3807. {
  3808. #ifdef DBG
  3809. DWORD dwError;
  3810. dwError = GetLastError();
  3811. DPFX(DPFPREP, 0, "Couldn't duplicate event (error = %u)!", dwError);
  3812. #endif // DBG
  3813. DNASSERT(this->m_hAlertEvent == NULL);
  3814. hr = DPNHERR_INVALIDPARAM;
  3815. goto Failure;
  3816. }
  3817. //
  3818. // Create overlapped structure. Don't allocate it through DNMalloc,
  3819. // because we may have to leak it on purpose. We don't want those memory
  3820. // allocation asserts firing in that case.
  3821. //
  3822. this->m_polAddressListChange = (WSAOVERLAPPED*) HeapAlloc(GetProcessHeap(),
  3823. HEAP_ZERO_MEMORY,
  3824. sizeof(WSAOVERLAPPED));
  3825. if (this->m_polAddressListChange == NULL)
  3826. {
  3827. //
  3828. // Close the alert handle we set.
  3829. //
  3830. CloseHandle(this->m_hAlertEvent);
  3831. this->m_hAlertEvent = NULL;
  3832. hr = DPNHERR_OUTOFMEMORY;
  3833. goto Failure;
  3834. }
  3835. //
  3836. // Save the event in the address list change overlapped structure.
  3837. //
  3838. this->m_polAddressListChange->hEvent = this->m_hAlertEvent;
  3839. //
  3840. // Start getting notified of local address changes.
  3841. //
  3842. hr = this->RequestLocalAddressListChangeNotification();
  3843. if (hr != DPNH_OK)
  3844. {
  3845. DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
  3846. //
  3847. // Free the memory we allocated.
  3848. //
  3849. HeapFree(GetProcessHeap(), 0, this->m_polAddressListChange);
  3850. this->m_polAddressListChange = NULL;
  3851. //
  3852. // Close the alert handle we set.
  3853. //
  3854. CloseHandle(this->m_hAlertEvent);
  3855. this->m_hAlertEvent = NULL;
  3856. goto Failure;
  3857. }
  3858. Exit:
  3859. if (fHaveLock)
  3860. {
  3861. this->DropLock();
  3862. fHaveLock = FALSE;
  3863. }
  3864. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  3865. return hr;
  3866. Failure:
  3867. goto Exit;
  3868. #endif // ! DPNBUILD_NOWINSOCK2
  3869. } // CNATHelpUPnP::SetAlertEvent
  3870. #undef DPF_MODNAME
  3871. #define DPF_MODNAME "CNATHelpUPnP::SetAlertIOCompletionPort"
  3872. //=============================================================================
  3873. // CNATHelpUPnP::SetAlertIOCompletionPort
  3874. //-----------------------------------------------------------------------------
  3875. //
  3876. // Description: This function allows the user to specify an I/O completion
  3877. // port that will receive notification when some maintenance needs
  3878. // to be performed. The user should call GetCaps using the
  3879. // DPNHGETCAPS_UPDATESERVERSTATUS flag when the packet with the
  3880. // given completion key is dequeued.
  3881. //
  3882. // This function is only available on Windows NT, may only be
  3883. // called once, and cannot be used after SetAlertEvent is called.
  3884. //
  3885. // Note that the completion port is used in addition to the
  3886. // regular polling of GetCaps, it simply allows the polling to be
  3887. // less frequent.
  3888. //
  3889. // Arguments:
  3890. // HANDLE hIOCompletionPort - Handle to I/O completion port which will
  3891. // be used to signal when GetCaps is to be
  3892. // called.
  3893. // DWORD dwCompletionKey - Key to use when indicating I/O
  3894. // completion.
  3895. // DWORD dwNumConcurrentThreads - Number of concurrent threads allowed to
  3896. // process, or zero for default.
  3897. // DWORD dwFlags - Unused, must be zero.
  3898. //
  3899. // Returns: HRESULT
  3900. // DPNH_OK - The I/O completion port was successfully
  3901. // registered.
  3902. // DPNHERR_GENERIC - An error occurred that prevented registering the
  3903. // I/O completion port.
  3904. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  3905. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  3906. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  3907. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  3908. // DPNHERR_OUTOFMEMORY - There is not enough memory.
  3909. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  3910. // thread.
  3911. //=============================================================================
  3912. STDMETHODIMP CNATHelpUPnP::SetAlertIOCompletionPort(const HANDLE hIOCompletionPort,
  3913. const DWORD dwCompletionKey,
  3914. const DWORD dwNumConcurrentThreads,
  3915. const DWORD dwFlags)
  3916. {
  3917. #ifdef DPNBUILD_NOWINSOCK2
  3918. DPFX(DPFPREP, 0, "Cannot set alert I/O completion port (0x%p, %u, %u)!",
  3919. hIOCompletionPort, dwCompletionKey, dwNumConcurrentThreads);
  3920. return E_NOTIMPL;
  3921. #else // ! DPNBUILD_NOWINSOCK2
  3922. HRESULT hr;
  3923. BOOL fHaveLock = FALSE;
  3924. HANDLE hIOCompletionPortResult;
  3925. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx, %u, 0x%lx)",
  3926. this, hIOCompletionPort, dwCompletionKey, dwNumConcurrentThreads, dwFlags);
  3927. //
  3928. // Validate the object.
  3929. //
  3930. if (! this->IsValidObject())
  3931. {
  3932. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  3933. hr = DPNHERR_INVALIDOBJECT;
  3934. goto Failure;
  3935. }
  3936. //
  3937. // Validate the parameters.
  3938. //
  3939. if (hIOCompletionPort == NULL)
  3940. {
  3941. DPFX(DPFPREP, 0, "Invalid I/O completion port handle specified!");
  3942. hr = DPNHERR_INVALIDPARAM;
  3943. goto Failure;
  3944. }
  3945. if (dwFlags != 0)
  3946. {
  3947. DPFX(DPFPREP, 0, "Invalid flags specified!");
  3948. hr = DPNHERR_INVALIDFLAGS;
  3949. goto Failure;
  3950. }
  3951. //
  3952. // Attempt to take the lock, but be prepared for the re-entrancy error.
  3953. //
  3954. hr = this->TakeLock();
  3955. if (hr != DPNH_OK)
  3956. {
  3957. DPFX(DPFPREP, 0, "Could not lock object!");
  3958. goto Failure;
  3959. }
  3960. fHaveLock = TRUE;
  3961. //
  3962. // Make sure object is in right state.
  3963. //
  3964. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED))
  3965. {
  3966. DPFX(DPFPREP, 0, "Object not initialized!");
  3967. hr = DPNHERR_NOTINITIALIZED;
  3968. goto Failure;
  3969. }
  3970. if (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1)
  3971. {
  3972. DPFX(DPFPREP, 0, "Cannot use alert mechanism on WinSock 1!");
  3973. hr = DPNHERR_GENERIC;
  3974. goto Failure;
  3975. }
  3976. if ((this->m_hAlertEvent != NULL) || (this->m_hAlertIOCompletionPort != NULL))
  3977. {
  3978. DPFX(DPFPREP, 0, "An alert event or I/O completion port has already been set!");
  3979. hr = DPNHERR_GENERIC;
  3980. goto Failure;
  3981. }
  3982. //
  3983. // Now save the I/O completion port handle.
  3984. //
  3985. if (! DuplicateHandle(GetCurrentProcess(),
  3986. hIOCompletionPort,
  3987. GetCurrentProcess(),
  3988. &this->m_hAlertIOCompletionPort,
  3989. 0,
  3990. FALSE,
  3991. DUPLICATE_SAME_ACCESS))
  3992. {
  3993. #ifdef DBG
  3994. DWORD dwError;
  3995. dwError = GetLastError();
  3996. DPFX(DPFPREP, 0, "Couldn't duplicate I/O completion port (error = %u)!", dwError);
  3997. #endif // DBG
  3998. DNASSERT(this->m_hAlertIOCompletionPort == NULL);
  3999. hr = DPNHERR_INVALIDPARAM;
  4000. goto Failure;
  4001. }
  4002. this->m_dwAlertCompletionKey = dwCompletionKey;
  4003. //
  4004. // Associate our Ioctl socket with this IO completion port.
  4005. //
  4006. DNASSERT(this->m_sIoctls != INVALID_SOCKET);
  4007. hIOCompletionPortResult = CreateIoCompletionPort((HANDLE) this->m_sIoctls,
  4008. this->m_hAlertIOCompletionPort,
  4009. dwCompletionKey,
  4010. dwNumConcurrentThreads);
  4011. if (hIOCompletionPortResult == NULL)
  4012. {
  4013. #ifdef DBG
  4014. DWORD dwError;
  4015. dwError = GetLastError();
  4016. DPFX(DPFPREP, 0, "Couldn't associate I/O completion port with Ioctl socket (error = %u)!", dwError);
  4017. #endif // DBG
  4018. hr = DPNHERR_GENERIC;
  4019. goto Failure;
  4020. }
  4021. //
  4022. // We should have just gotten the same I/O completion port back.
  4023. //
  4024. DNASSERT(hIOCompletionPortResult == this->m_hAlertIOCompletionPort);
  4025. //
  4026. // Create overlapped structure. Don't allocate it through DNMalloc,
  4027. // because we may have to leak it on purpose. We don't want those memory
  4028. // allocation asserts firing in that case.
  4029. //
  4030. this->m_polAddressListChange = (WSAOVERLAPPED*) HeapAlloc(GetProcessHeap(),
  4031. HEAP_ZERO_MEMORY,
  4032. sizeof(WSAOVERLAPPED));
  4033. if (this->m_polAddressListChange == NULL)
  4034. {
  4035. //
  4036. // Close the alert IOCP we set.
  4037. //
  4038. CloseHandle(this->m_hAlertIOCompletionPort);
  4039. this->m_hAlertIOCompletionPort = NULL;
  4040. hr = DPNHERR_OUTOFMEMORY;
  4041. goto Failure;
  4042. }
  4043. //
  4044. // Start getting notified of local address changes.
  4045. //
  4046. hr = this->RequestLocalAddressListChangeNotification();
  4047. if (hr != DPNH_OK)
  4048. {
  4049. DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
  4050. //
  4051. // Free the memory we allocated.
  4052. //
  4053. HeapFree(GetProcessHeap(), 0, this->m_polAddressListChange);
  4054. this->m_polAddressListChange = NULL;
  4055. //
  4056. // Close the alert IOCP we set.
  4057. //
  4058. CloseHandle(this->m_hAlertIOCompletionPort);
  4059. this->m_hAlertIOCompletionPort = NULL;
  4060. goto Failure;
  4061. }
  4062. Exit:
  4063. if (fHaveLock)
  4064. {
  4065. this->DropLock();
  4066. fHaveLock = FALSE;
  4067. }
  4068. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  4069. return hr;
  4070. Failure:
  4071. goto Exit;
  4072. #endif // ! DPNBUILD_NOWINSOCK2
  4073. } // CNATHelpUPnP::SetAlertIOCompletionPort
  4074. #undef DPF_MODNAME
  4075. #define DPF_MODNAME "CNATHelpUPnP::ExtendRegisteredPortsLease"
  4076. //=============================================================================
  4077. // CNATHelpUPnP::ExtendRegisteredPortsLease
  4078. //-----------------------------------------------------------------------------
  4079. //
  4080. // Description: Manually extends the lease of the given registered port
  4081. // mapping by the requested time. The periodic calling of GetCaps
  4082. // can take care of this for the user, this function is only
  4083. // necessary to change the lease extension time or for finer
  4084. // control of individual mappings.
  4085. //
  4086. // The user should specify a requested lease extension time
  4087. // that the server will attempt to honor. It will be added to any
  4088. // time remaining in the existing lease, and the new total can be
  4089. // retrieved by calling GetRegisteredAddresses.
  4090. //
  4091. // Arguments:
  4092. // DPNHHANDLE hRegisteredPorts - Handle for a specific binding returned by
  4093. // RegisterPorts.
  4094. // DWORD dwLeaseTime - Requested time, in milliseconds, to
  4095. // extend the lease. If 0, the previous
  4096. // requested lease time is used.
  4097. // DWORD dwFlags - Unused, must be zero.
  4098. //
  4099. // Returns: HRESULT
  4100. // DPNH_OK - The lease was successfully extended.
  4101. // DPNHERR_GENERIC - An error occurred that prevented the extending
  4102. // the lease.
  4103. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  4104. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  4105. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  4106. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  4107. // DPNHERR_OUTOFMEMORY - There is not enough memory to extend the lease.
  4108. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  4109. // thread.
  4110. //=============================================================================
  4111. STDMETHODIMP CNATHelpUPnP::ExtendRegisteredPortsLease(const DPNHHANDLE hRegisteredPorts,
  4112. const DWORD dwLeaseTime,
  4113. const DWORD dwFlags)
  4114. {
  4115. HRESULT hr;
  4116. CRegisteredPort * pRegisteredPort;
  4117. CDevice * pDevice;
  4118. BOOL fHaveLock = FALSE;
  4119. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, %u, 0x%lx)",
  4120. this, hRegisteredPorts, dwLeaseTime, dwFlags);
  4121. //
  4122. // Validate the object.
  4123. //
  4124. if (! this->IsValidObject())
  4125. {
  4126. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  4127. hr = DPNHERR_INVALIDOBJECT;
  4128. goto Failure;
  4129. }
  4130. //
  4131. // Validate the parameters.
  4132. //
  4133. pRegisteredPort = (CRegisteredPort*) hRegisteredPorts;
  4134. if (! pRegisteredPort->IsValidObject())
  4135. {
  4136. DPFX(DPFPREP, 0, "Invalid registered port mapping handle specified!");
  4137. hr = DPNHERR_INVALIDPARAM;
  4138. goto Failure;
  4139. }
  4140. if (dwFlags != 0)
  4141. {
  4142. DPFX(DPFPREP, 0, "Invalid flags specified!");
  4143. hr = DPNHERR_INVALIDFLAGS;
  4144. goto Failure;
  4145. }
  4146. //
  4147. // Attempt to take the lock, but be prepared for the re-entrancy error.
  4148. //
  4149. hr = this->TakeLock();
  4150. if (hr != DPNH_OK)
  4151. {
  4152. DPFX(DPFPREP, 0, "Could not lock object!");
  4153. goto Failure;
  4154. }
  4155. fHaveLock = TRUE;
  4156. //
  4157. // Make sure object is in right state.
  4158. //
  4159. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  4160. {
  4161. DPFX(DPFPREP, 0, "Object not initialized!");
  4162. hr = DPNHERR_NOTINITIALIZED;
  4163. goto Failure;
  4164. }
  4165. //
  4166. // If they wanted to change the lease time, update it.
  4167. //
  4168. if (dwLeaseTime != 0)
  4169. {
  4170. pRegisteredPort->UpdateRequestedLeaseTime(dwLeaseTime);
  4171. }
  4172. pDevice = pRegisteredPort->GetOwningDevice();
  4173. //
  4174. // If the port is registered with the UPnP device, extend that lease.
  4175. //
  4176. if (pRegisteredPort->HasUPnPPublicAddresses())
  4177. {
  4178. DNASSERT(pDevice != NULL);
  4179. hr = this->ExtendUPnPLease(pRegisteredPort);
  4180. if (hr != DPNH_OK)
  4181. {
  4182. DPFX(DPFPREP, 0, "Couldn't extend port mapping lease on UPnP device (0x%lx)! Ignoring.", hr);
  4183. //
  4184. // We'll treat this as non-fatal, but we have to dump the
  4185. // server. This may have already been done, but doing it
  4186. // twice shouldn't be harmful.
  4187. //
  4188. this->ClearDevicesUPnPDevice(pDevice);
  4189. hr = DPNH_OK;
  4190. }
  4191. }
  4192. else
  4193. {
  4194. DPFX(DPFPREP, 2, "Port mapping not registered with UPnP gateway device.");
  4195. }
  4196. //
  4197. // Firewall mappings never have lease times to extend.
  4198. //
  4199. this->DropLock();
  4200. fHaveLock = FALSE;
  4201. Exit:
  4202. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  4203. return hr;
  4204. Failure:
  4205. if (fHaveLock)
  4206. {
  4207. this->DropLock();
  4208. fHaveLock = FALSE;
  4209. }
  4210. goto Exit;
  4211. } // CNATHelpUPnP::ExtendRegisteredPortsLease
  4212. #undef DPF_MODNAME
  4213. #define DPF_MODNAME "CNATHelpUPnP::InitializeObject"
  4214. //=============================================================================
  4215. // CNATHelpUPnP::InitializeObject
  4216. //-----------------------------------------------------------------------------
  4217. //
  4218. // Description: Sets up the object for use like the constructor, but may
  4219. // fail with OUTOFMEMORY. Should only be called by class factory
  4220. // creation routine.
  4221. //
  4222. // Arguments: None.
  4223. //
  4224. // Returns: HRESULT
  4225. // S_OK - Initialization was successful.
  4226. // E_OUTOFMEMORY - There is not enough memory to initialize.
  4227. //=============================================================================
  4228. HRESULT CNATHelpUPnP::InitializeObject(void)
  4229. {
  4230. HRESULT hr;
  4231. BOOL fInittedCriticalSection = FALSE;
  4232. DPFX(DPFPREP, 5, "(0x%p) Enter", this);
  4233. DNASSERT(this->IsValidObject());
  4234. //
  4235. // Create the lock.
  4236. //
  4237. if (! DNInitializeCriticalSection(&this->m_csLock))
  4238. {
  4239. hr = E_OUTOFMEMORY;
  4240. goto Failure;
  4241. }
  4242. fInittedCriticalSection = TRUE;
  4243. //
  4244. // Don't allow critical section reentry.
  4245. //
  4246. DebugSetCriticalSectionRecursionCount(&this->m_csLock, 0);
  4247. this->m_hLongLockSemaphore = DNCreateSemaphore(NULL,
  4248. 0,
  4249. MAX_LONG_LOCK_WAITING_THREADS,
  4250. NULL);
  4251. if (this->m_hLongLockSemaphore == NULL)
  4252. {
  4253. hr = DPNHERR_GENERIC;
  4254. goto Failure;
  4255. }
  4256. hr = S_OK;
  4257. Exit:
  4258. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  4259. return hr;
  4260. Failure:
  4261. if (fInittedCriticalSection)
  4262. {
  4263. DNDeleteCriticalSection(&this->m_csLock);
  4264. fInittedCriticalSection = FALSE;
  4265. }
  4266. goto Exit;
  4267. } // CNATHelpUPnP::InitializeObject
  4268. #undef DPF_MODNAME
  4269. #define DPF_MODNAME "CNATHelpUPnP::UninitializeObject"
  4270. //=============================================================================
  4271. // CNATHelpUPnP::UninitializeObject
  4272. //-----------------------------------------------------------------------------
  4273. //
  4274. // Description: Cleans up the object like the destructor, mostly to balance
  4275. // InitializeObject.
  4276. //
  4277. // Arguments: None.
  4278. //
  4279. // Returns: None.
  4280. //=============================================================================
  4281. void CNATHelpUPnP::UninitializeObject(void)
  4282. {
  4283. DPFX(DPFPREP, 5, "(0x%p) Enter", this);
  4284. DNASSERT(this->IsValidObject());
  4285. DNCloseHandle(this->m_hLongLockSemaphore);
  4286. this->m_hLongLockSemaphore = NULL;
  4287. DNDeleteCriticalSection(&this->m_csLock);
  4288. DPFX(DPFPREP, 5, "(0x%p) Leave", this);
  4289. } // CNATHelpUPnP::UninitializeObject
  4290. #undef DPF_MODNAME
  4291. #define DPF_MODNAME "CNATHelpUPnP::TakeLock"
  4292. //=============================================================================
  4293. // CNATHelpUPnP::TakeLock
  4294. //-----------------------------------------------------------------------------
  4295. //
  4296. // Description: Takes the main object lock. If some other thread is already
  4297. // holding the long lock, we wait for that first.
  4298. //
  4299. // Arguments: None.
  4300. //
  4301. // Returns: DPNH_OK if lock was taken successfully, DPNHERR_REENTRANT if lock
  4302. // was re-entered.
  4303. //=============================================================================
  4304. HRESULT CNATHelpUPnP::TakeLock(void)
  4305. {
  4306. HRESULT hr = DPNH_OK;
  4307. #ifdef DBG
  4308. DWORD dwStartTime;
  4309. dwStartTime = GETTIMESTAMP();
  4310. #endif // DBG
  4311. DNEnterCriticalSection(&this->m_csLock);
  4312. //
  4313. // If this same thread is already holding the lock, then bail.
  4314. //
  4315. if (this->m_dwLockThreadID == GetCurrentThreadId())
  4316. {
  4317. DPFX(DPFPREP, 0, "Thread re-entering!");
  4318. goto Failure;
  4319. }
  4320. //
  4321. // If someone is holding the long lock, we need to wait for that. Of
  4322. // course another thread could come in and take the long lock after the
  4323. // first one drops it and before we can take the main one. This algorithm
  4324. // does not attempt to be fair in this case. Theoretically we could wait
  4325. // forever if this continued to occur. That shouldn't happen in the real
  4326. // world.
  4327. // This whole mess of code is a huge... uh... workaround for stress hits
  4328. // involving critical section timeouts.
  4329. //
  4330. while (this->m_dwFlags & NATHELPUPNPOBJ_LONGLOCK)
  4331. {
  4332. DNASSERT(this->m_lNumLongLockWaitingThreads >= 0);
  4333. this->m_lNumLongLockWaitingThreads++;
  4334. //
  4335. // We need to keep looping until we do get the lock.
  4336. //
  4337. DNLeaveCriticalSection(&this->m_csLock);
  4338. DPFX(DPFPREP, 3, "Waiting for long lock to be released.");
  4339. DNWaitForSingleObject(this->m_hLongLockSemaphore, INFINITE);
  4340. DNEnterCriticalSection(&this->m_csLock);
  4341. //
  4342. // If this same thread is already holding the lock, then bail.
  4343. //
  4344. if (this->m_dwLockThreadID == GetCurrentThreadId())
  4345. {
  4346. DPFX(DPFPREP, 0, "Thread re-entering after waiting for long lock!");
  4347. goto Failure;
  4348. }
  4349. }
  4350. #ifdef DBG
  4351. DPFX(DPFPREP, 8, "Took main object lock, elapsed time = %u ms.",
  4352. (GETTIMESTAMP() - dwStartTime));
  4353. #endif // DBG
  4354. //
  4355. // Save this thread's ID so we know who's holding the lock.
  4356. //
  4357. this->m_dwLockThreadID = GetCurrentThreadId();
  4358. Exit:
  4359. return hr;
  4360. Failure:
  4361. //
  4362. // We're reentering. Drop the lock and return the failure.
  4363. //
  4364. DNLeaveCriticalSection(&this->m_csLock);
  4365. hr = DPNHERR_REENTRANT;
  4366. goto Exit;
  4367. } // CNATHelpUPnP::TakeLock
  4368. #undef DPF_MODNAME
  4369. #define DPF_MODNAME "CNATHelpUPnP::DropLock"
  4370. //=============================================================================
  4371. // CNATHelpUPnP::DropLock
  4372. //-----------------------------------------------------------------------------
  4373. //
  4374. // Description: Drops the main object lock.
  4375. //
  4376. // Arguments: None.
  4377. //
  4378. // Returns: None.
  4379. //=============================================================================
  4380. void CNATHelpUPnP::DropLock(void)
  4381. {
  4382. DNASSERT(! (this->m_dwFlags & NATHELPUPNPOBJ_LONGLOCK));
  4383. DNASSERT(this->m_lNumLongLockWaitingThreads == 0);
  4384. DNASSERT(this->m_dwLockThreadID == GetCurrentThreadId());
  4385. this->m_dwLockThreadID = 0;
  4386. DNLeaveCriticalSection(&this->m_csLock);
  4387. DPFX(DPFPREP, 8, "Dropped main object lock.");
  4388. } // CNATHelpUPnP::DropLock
  4389. #undef DPF_MODNAME
  4390. #define DPF_MODNAME "CNATHelpUPnP::SwitchToLongLock"
  4391. //=============================================================================
  4392. // CNATHelpUPnP::SwitchToLongLock
  4393. //-----------------------------------------------------------------------------
  4394. //
  4395. // Description: Switches from holding the main object lock to holding the
  4396. // long lock.
  4397. //
  4398. // Arguments: None.
  4399. //
  4400. // Returns: None.
  4401. //=============================================================================
  4402. void CNATHelpUPnP::SwitchToLongLock(void)
  4403. {
  4404. AssertCriticalSectionIsTakenByThisThread(&this->m_csLock, TRUE);
  4405. DNASSERT(! (this->m_dwFlags & NATHELPUPNPOBJ_LONGLOCK));
  4406. DNASSERT(this->m_lNumLongLockWaitingThreads == 0);
  4407. DPFX(DPFPREP, 8, "Switching to long lock.");
  4408. this->m_dwFlags |= NATHELPUPNPOBJ_LONGLOCK;
  4409. DNLeaveCriticalSection(&this->m_csLock);
  4410. } // CNATHelpUPnP::SwitchToLongLock
  4411. #undef DPF_MODNAME
  4412. #define DPF_MODNAME "CNATHelpUPnP::SwitchFromLongLock"
  4413. //=============================================================================
  4414. // CNATHelpUPnP::SwitchFromLongLock
  4415. //-----------------------------------------------------------------------------
  4416. //
  4417. // Description: Switches from holding the long lock back to holding the main
  4418. // object lock.
  4419. //
  4420. // Arguments: None.
  4421. //
  4422. // Returns: None.
  4423. //=============================================================================
  4424. void CNATHelpUPnP::SwitchFromLongLock(void)
  4425. {
  4426. DNEnterCriticalSection(&this->m_csLock);
  4427. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_LONGLOCK);
  4428. this->m_dwFlags &= ~NATHELPUPNPOBJ_LONGLOCK;
  4429. DPFX(DPFPREP, 8, "Switching from long lock, alerting %i threads.",
  4430. this->m_lNumLongLockWaitingThreads);
  4431. //
  4432. // This is non-optimal in that we release the semaphore but the waiting
  4433. // threads still won't actually be able to do anything since we now hold
  4434. // the main lock.
  4435. //
  4436. DNASSERT(this->m_lNumLongLockWaitingThreads >= 0);
  4437. DNReleaseSemaphore(this->m_hLongLockSemaphore,
  4438. this->m_lNumLongLockWaitingThreads,
  4439. NULL);
  4440. this->m_lNumLongLockWaitingThreads = 0;
  4441. } // CNATHelpUPnP::SwitchFromLongLock
  4442. #undef DPF_MODNAME
  4443. #define DPF_MODNAME "CNATHelpUPnP::LoadWinSockFunctionPointers"
  4444. //=============================================================================
  4445. // CNATHelpUPnP::LoadWinSockFunctionPointers
  4446. //-----------------------------------------------------------------------------
  4447. //
  4448. // Description: Loads pointers to all the functions that we use in WinSock.
  4449. //
  4450. // The object lock is assumed to be held.
  4451. //
  4452. // Arguments: None.
  4453. //
  4454. // Returns: HRESULT
  4455. // DPNH_OK - Loading was successful.
  4456. // DPNHERR_GENERIC - An error occurred.
  4457. //=============================================================================
  4458. HRESULT CNATHelpUPnP::LoadWinSockFunctionPointers(void)
  4459. {
  4460. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4461. #ifdef DBG
  4462. #define PRINTERRORIFDEBUG(name) \
  4463. {\
  4464. dwError = GetLastError();\
  4465. DPFX(DPFPREP, 0, "Couldn't get \"%hs\" function! 0x%lx", name, dwError);\
  4466. }
  4467. #else // ! DBG
  4468. #define PRINTERRORIFDEBUG(name)
  4469. #endif // ! DBG
  4470. #define LOADWINSOCKFUNCTION(var, proctype, name) \
  4471. {\
  4472. var = (##proctype) GetProcAddress(this->m_hWinSockDLL, _TWINCE(name));\
  4473. if (var == NULL)\
  4474. {\
  4475. PRINTERRORIFDEBUG(name);\
  4476. hr = DPNHERR_GENERIC;\
  4477. goto Failure;\
  4478. }\
  4479. }
  4480. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4481. HRESULT hr = DPNH_OK;
  4482. #ifdef DBG
  4483. DWORD dwError;
  4484. #endif // DBG
  4485. LOADWINSOCKFUNCTION(this->m_pfnWSAStartup, LPFN_WSASTARTUP, "WSAStartup");
  4486. LOADWINSOCKFUNCTION(this->m_pfnWSACleanup, LPFN_WSACLEANUP, "WSACleanup");
  4487. #ifdef WINCE
  4488. this->m_pfnWSAGetLastError = (LPFN_WSAGETLASTERROR) GetLastError;
  4489. #else // ! WINCE
  4490. LOADWINSOCKFUNCTION(this->m_pfnWSAGetLastError, LPFN_WSAGETLASTERROR, "WSAGetLastError");
  4491. #endif // ! WINCE
  4492. LOADWINSOCKFUNCTION(this->m_pfnsocket, LPFN_SOCKET, "socket");
  4493. LOADWINSOCKFUNCTION(this->m_pfnclosesocket, LPFN_CLOSESOCKET, "closesocket");
  4494. LOADWINSOCKFUNCTION(this->m_pfnbind, LPFN_BIND, "bind");
  4495. LOADWINSOCKFUNCTION(this->m_pfnsetsockopt, LPFN_SETSOCKOPT, "setsockopt");
  4496. LOADWINSOCKFUNCTION(this->m_pfngetsockname, LPFN_GETSOCKNAME, "getsockname");
  4497. LOADWINSOCKFUNCTION(this->m_pfnselect, LPFN_SELECT, "select");
  4498. LOADWINSOCKFUNCTION(this->m_pfn__WSAFDIsSet, LPFN___WSAFDISSET, "__WSAFDIsSet");
  4499. LOADWINSOCKFUNCTION(this->m_pfnrecvfrom, LPFN_RECVFROM, "recvfrom");
  4500. LOADWINSOCKFUNCTION(this->m_pfnsendto, LPFN_SENDTO, "sendto");
  4501. LOADWINSOCKFUNCTION(this->m_pfngethostname, LPFN_GETHOSTNAME, "gethostname");
  4502. LOADWINSOCKFUNCTION(this->m_pfngethostbyname, LPFN_GETHOSTBYNAME, "gethostbyname");
  4503. LOADWINSOCKFUNCTION(this->m_pfninet_addr, LPFN_INET_ADDR, "inet_addr");
  4504. #ifndef DPNBUILD_NOWINSOCK2
  4505. if (! (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1))
  4506. {
  4507. LOADWINSOCKFUNCTION(this->m_pfnWSASocketA, LPFN_WSASOCKETA, "WSASocketA");
  4508. LOADWINSOCKFUNCTION(this->m_pfnWSAIoctl, LPFN_WSAIOCTL, "WSAIoctl");
  4509. LOADWINSOCKFUNCTION(this->m_pfnWSAGetOverlappedResult, LPFN_WSAGETOVERLAPPEDRESULT, "WSAGetOverlappedResult");
  4510. }
  4511. #endif // ! DPNBUILD_NOWINSOCK2
  4512. LOADWINSOCKFUNCTION(this->m_pfnioctlsocket, LPFN_IOCTLSOCKET, "ioctlsocket");
  4513. LOADWINSOCKFUNCTION(this->m_pfnconnect, LPFN_CONNECT, "connect");
  4514. LOADWINSOCKFUNCTION(this->m_pfnshutdown, LPFN_SHUTDOWN, "shutdown");
  4515. LOADWINSOCKFUNCTION(this->m_pfnsend, LPFN_SEND, "send");
  4516. LOADWINSOCKFUNCTION(this->m_pfnrecv, LPFN_RECV, "recv");
  4517. #ifdef DBG
  4518. LOADWINSOCKFUNCTION(this->m_pfngetsockopt, LPFN_GETSOCKOPT, "getsockopt");
  4519. #endif // DBG
  4520. Exit:
  4521. return hr;
  4522. Failure:
  4523. hr = DPNHERR_GENERIC;
  4524. goto Exit;
  4525. } // CNATHelpUPnP::LoadWinSockFunctionPointers
  4526. #undef DPF_MODNAME
  4527. #define DPF_MODNAME "CNATHelpUPnP::CheckForNewDevices"
  4528. //=============================================================================
  4529. // CNATHelpUPnP::CheckForNewDevices
  4530. //-----------------------------------------------------------------------------
  4531. //
  4532. // Description: Detects new IP capable devices that have been added and
  4533. // removes old ones no longer available.
  4534. //
  4535. // The object lock is assumed to be held.
  4536. //
  4537. // Arguments:
  4538. // BOOL * pfFoundNewDevices Pointer to boolean to set to TRUE if new
  4539. // devices were added, or NULL if don't care.
  4540. //
  4541. // Returns: HRESULT
  4542. // DPNH_OK - The check was successful.
  4543. // DPNHERR_GENERIC - An error occurred.
  4544. //=============================================================================
  4545. HRESULT CNATHelpUPnP::CheckForNewDevices(BOOL * const pfFoundNewDevices)
  4546. {
  4547. HRESULT hr = DPNH_OK;
  4548. #if ((defined(DBG)) || (! defined(DPNBUILD_NOWINSOCK2)))
  4549. DWORD dwError;
  4550. #endif // DBG or ! DPNBUILD_NOWINSOCK2
  4551. #ifndef DPNBUILD_NOWINSOCK2
  4552. int iReturn;
  4553. #endif // ! DPNBUILD_NOWINSOCK2
  4554. char szName[1000];
  4555. PHOSTENT phostent;
  4556. IN_ADDR ** ppinaddr;
  4557. DWORD dwAddressesSize = 0;
  4558. DWORD dwNumAddresses = 0;
  4559. IN_ADDR * painaddrAddresses = NULL;
  4560. CBilink * pBilinkDevice;
  4561. CDevice * pDevice = NULL; // NULL it for PREfix, even though fDeviceCreated guards it
  4562. BOOL fDeviceCreated = FALSE;
  4563. BOOL fFound;
  4564. CBilink * pBilinkRegPort;
  4565. CRegisteredPort * pRegisteredPort;
  4566. SOCKET sTemp = INVALID_SOCKET;
  4567. SOCKADDR_IN saddrinTemp;
  4568. DWORD dwTemp;
  4569. #ifndef DPNBUILD_NOWINSOCK2
  4570. SOCKET_ADDRESS * paSocketAddresses;
  4571. #endif // ! DPNBUILD_NOWINSOCK2
  4572. DPFX(DPFPREP, 5, "(0x%p) Parameters (0x%p)", this, pfFoundNewDevices);
  4573. #ifndef DPNBUILD_NOWINSOCK2
  4574. //
  4575. // Handle any address list change Ioctl completions that may have gotten us
  4576. // here.
  4577. //
  4578. if ((this->m_hAlertEvent != NULL) ||
  4579. (this->m_hAlertIOCompletionPort != NULL))
  4580. {
  4581. DNASSERT(this->m_sIoctls != INVALID_SOCKET);
  4582. DNASSERT(this->m_polAddressListChange != NULL);
  4583. if (this->m_pfnWSAGetOverlappedResult(this->m_sIoctls, //
  4584. this->m_polAddressListChange, //
  4585. &dwTemp, // ignore bytes transferred
  4586. FALSE, // don't wait
  4587. &dwTemp)) // ignore flags
  4588. {
  4589. DPFX(DPFPREP, 1, "Received address list change notification.");
  4590. //
  4591. // Overlapped result completed. Reissue it.
  4592. //
  4593. hr = this->RequestLocalAddressListChangeNotification();
  4594. if (hr != DPNH_OK)
  4595. {
  4596. DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
  4597. goto Failure;
  4598. }
  4599. }
  4600. else
  4601. {
  4602. //
  4603. // Figure out what error it was.
  4604. //
  4605. dwError = this->m_pfnWSAGetLastError();
  4606. switch (dwError)
  4607. {
  4608. case WSA_IO_INCOMPLETE:
  4609. {
  4610. //
  4611. // It hasn't completed yet.
  4612. //
  4613. break;
  4614. }
  4615. case ERROR_OPERATION_ABORTED:
  4616. {
  4617. //
  4618. // The thread that we originally submitted the Ioctl on
  4619. // went away and so the OS kindly cancelled the operation
  4620. // on us. How nice. Well, let's try resubmitting it.
  4621. //
  4622. DPFX(DPFPREP, 1, "Thread that submitted previous address list change notification went away, rerequesting.");
  4623. hr = this->RequestLocalAddressListChangeNotification();
  4624. if (hr != DPNH_OK)
  4625. {
  4626. DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
  4627. goto Failure;
  4628. }
  4629. break;
  4630. }
  4631. default:
  4632. {
  4633. DPFX(DPFPREP, 0, "Couldn't get overlapped result, error = %u! Ignoring.", dwError);
  4634. break;
  4635. }
  4636. } // end switch (on error)
  4637. }
  4638. }
  4639. //
  4640. // If we're on WinSock 2, let's try getting the address list with
  4641. // an Ioctl.
  4642. //
  4643. if (! (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1))
  4644. {
  4645. DNASSERT(this->m_sIoctls != INVALID_SOCKET);
  4646. DNASSERT(this->m_pfnWSAIoctl != NULL);
  4647. //
  4648. // Keep trying to get the address list until we have a large enough
  4649. // buffer. We use the IN_ADDR array pointer simply because it's
  4650. // already there. We know that IN_ADDRs are smaller than
  4651. // SOCKET_ADDRESSes, so we can reuse the same buffer.
  4652. //
  4653. do
  4654. {
  4655. iReturn = this->m_pfnWSAIoctl(this->m_sIoctls, // use the special Ioctl socket
  4656. SIO_ADDRESS_LIST_QUERY, //
  4657. NULL, // no input data
  4658. 0, // no input data
  4659. painaddrAddresses, // output buffer
  4660. dwAddressesSize, // output buffer size
  4661. &dwTemp, // bytes needed
  4662. NULL, // no overlapped structure
  4663. NULL); // no completion routine
  4664. if (iReturn != 0)
  4665. {
  4666. dwError = this->m_pfnWSAGetLastError();
  4667. //
  4668. // Free the previous buffer, no matter what error it was.
  4669. //
  4670. if (painaddrAddresses != NULL)
  4671. {
  4672. DNFree(painaddrAddresses);
  4673. painaddrAddresses = NULL;
  4674. }
  4675. if (dwError != WSAEFAULT)
  4676. {
  4677. DPFX(DPFPREP, 1, "Retrieving address list failed (err = %u), trying WinSock 1 method.", dwError);
  4678. //
  4679. // We'll try the old-fashioned WinSock 1 way.
  4680. //
  4681. break;
  4682. }
  4683. //
  4684. // Be absolutely sure WinSock isn't causing us trouble.
  4685. //
  4686. if (dwTemp < sizeof(SOCKET_ADDRESS_LIST))
  4687. {
  4688. DPFX(DPFPREP, 0, "Received an invalid buffer size (%u < %u)!",
  4689. dwTemp, sizeof(SOCKET_ADDRESS_LIST));
  4690. //
  4691. // We'll try the old-fashioned WinSock 1 way.
  4692. //
  4693. break;
  4694. }
  4695. //
  4696. // The buffer wasn't large enough. Try again.
  4697. //
  4698. painaddrAddresses = (IN_ADDR*) DNMalloc(dwTemp);
  4699. if (painaddrAddresses == NULL)
  4700. {
  4701. hr = DPNHERR_OUTOFMEMORY;
  4702. goto Failure;
  4703. }
  4704. dwAddressesSize = dwTemp;
  4705. }
  4706. else
  4707. {
  4708. //
  4709. // Success! We're going to being sneaky and reuse the buffer.
  4710. // We know that the SOCKET_ADDRESS_LIST returned will be larger
  4711. // than an array of IN_ADDRs, so we can save a malloc.
  4712. //
  4713. // But first, be absolutely sure WinSock isn't causing us
  4714. // trouble.
  4715. //
  4716. if (painaddrAddresses == NULL)
  4717. {
  4718. DPFX(DPFPREP, 0, "WinSock returned success with a NULL buffer!");
  4719. //
  4720. // We'll try the old-fashioned WinSock 1 way.
  4721. //
  4722. break;
  4723. }
  4724. dwNumAddresses = ((SOCKET_ADDRESS_LIST*) painaddrAddresses)->iAddressCount;
  4725. dwAddressesSize = 0;
  4726. //
  4727. // Make sure there are addresses.
  4728. //
  4729. if (dwNumAddresses > 0)
  4730. {
  4731. DPFX(DPFPREP, 7, "WinSock 2 Ioctl returned %u addresses:", dwNumAddresses);
  4732. paSocketAddresses = ((SOCKET_ADDRESS_LIST*) painaddrAddresses)->Address;
  4733. for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
  4734. {
  4735. DNASSERT(paSocketAddresses[dwTemp].iSockaddrLength == sizeof(SOCKADDR_IN));
  4736. DNASSERT(paSocketAddresses[dwTemp].lpSockaddr != NULL);
  4737. DNASSERT(paSocketAddresses[dwTemp].lpSockaddr->sa_family == AF_INET);
  4738. //
  4739. // Ignore 0.0.0.0 addresses.
  4740. //
  4741. if (((SOCKADDR_IN*) (paSocketAddresses[dwTemp].lpSockaddr))->sin_addr.S_un.S_addr != INADDR_NONE)
  4742. {
  4743. //
  4744. // Move the IN_ADDR component of this address
  4745. // toward the front of the buffer, into it's
  4746. // correct place in the array.
  4747. //
  4748. painaddrAddresses[dwTemp].S_un.S_addr = ((SOCKADDR_IN*) (paSocketAddresses[dwTemp].lpSockaddr))->sin_addr.S_un.S_addr;
  4749. DPFX(DPFPREP, 7, "\t%u- %u.%u.%u.%u",
  4750. dwTemp,
  4751. painaddrAddresses[dwTemp].S_un.S_un_b.s_b1,
  4752. painaddrAddresses[dwTemp].S_un.S_un_b.s_b2,
  4753. painaddrAddresses[dwTemp].S_un.S_un_b.s_b3,
  4754. painaddrAddresses[dwTemp].S_un.S_un_b.s_b4);
  4755. }
  4756. else
  4757. {
  4758. DPFX(DPFPREP, 1, "\t%u- Ignoring 0.0.0.0 address.", dwTemp);
  4759. dwAddressesSize++;
  4760. //
  4761. // The code should handle this fine, but why is
  4762. // WinSock doing this to us?
  4763. //
  4764. DNASSERT(FALSE);
  4765. }
  4766. }
  4767. //
  4768. // Subtract out any invalid addresses that we skipped.
  4769. //
  4770. dwNumAddresses -= dwAddressesSize;
  4771. if (dwNumAddresses == 0)
  4772. {
  4773. DPFX(DPFPREP, 1, "WinSock 2 reported only invalid addresses, hoping WinSock 1 method picks up the loopback address.");
  4774. DNFree(painaddrAddresses);
  4775. painaddrAddresses = NULL;
  4776. }
  4777. }
  4778. else
  4779. {
  4780. DPFX(DPFPREP, 1, "WinSock 2 Ioctl did not report any valid addresses, hoping WinSock 1 method picks up the loopback address.");
  4781. DNFree(painaddrAddresses);
  4782. painaddrAddresses = NULL;
  4783. }
  4784. //
  4785. // Get out of the loop.
  4786. //
  4787. break;
  4788. }
  4789. }
  4790. while (TRUE);
  4791. }
  4792. //
  4793. // Get the list of all available addresses from the WinSock 1 API if we
  4794. // don't already have them.
  4795. //
  4796. if (painaddrAddresses == NULL)
  4797. #endif // ! DPNBUILD_NOWINSOCK2
  4798. {
  4799. if (this->m_pfngethostname(szName, 1000) != 0)
  4800. {
  4801. #ifdef DBG
  4802. dwError = this->m_pfnWSAGetLastError();
  4803. DPFX(DPFPREP, 0, "Couldn't get host name, error = %u!", dwError);
  4804. #endif // DBG
  4805. hr = DPNHERR_GENERIC;
  4806. goto Failure;
  4807. }
  4808. phostent = this->m_pfngethostbyname(szName);
  4809. if (phostent == NULL)
  4810. {
  4811. #ifdef DBG
  4812. dwError = this->m_pfnWSAGetLastError();
  4813. DPFX(DPFPREP, 0, "Couldn't retrieve addresses, error = %u!", dwError);
  4814. #endif // DBG
  4815. hr = DPNHERR_GENERIC;
  4816. goto Failure;
  4817. }
  4818. //
  4819. // WinSock says that you need to copy this data before you make any
  4820. // other API calls. So first we count the number of entries we need to
  4821. // copy.
  4822. //
  4823. ppinaddr = (IN_ADDR**) phostent->h_addr_list;
  4824. while ((*ppinaddr) != NULL)
  4825. {
  4826. //
  4827. // Ignore 0.0.0.0 addresses.
  4828. //
  4829. if ((*ppinaddr)->S_un.S_addr != INADDR_NONE)
  4830. {
  4831. dwNumAddresses++;
  4832. }
  4833. else
  4834. {
  4835. DPFX(DPFPREP, 1, "Ignoring 0.0.0.0 address.");
  4836. //
  4837. // The code should handle this fine, but why is WinSock doing
  4838. // this to us?
  4839. //
  4840. DNASSERT(FALSE);
  4841. }
  4842. ppinaddr++;
  4843. }
  4844. //
  4845. // If there aren't any addresses, we must fail. WinSock 1 ought to
  4846. // report the loopback address at least.
  4847. //
  4848. if (dwNumAddresses == 0)
  4849. {
  4850. DPFX(DPFPREP, 0, "WinSock 1 did not report any valid addresses!");
  4851. hr = DPNHERR_GENERIC;
  4852. goto Failure;
  4853. }
  4854. DPFX(DPFPREP, 7, "WinSock 1 method returned %u valid addresses:", dwNumAddresses);
  4855. painaddrAddresses = (IN_ADDR*) DNMalloc(dwNumAddresses * sizeof(IN_ADDR));
  4856. if (painaddrAddresses == NULL)
  4857. {
  4858. hr = DPNHERR_OUTOFMEMORY;
  4859. goto Failure;
  4860. }
  4861. //
  4862. // Now copy all the addresses.
  4863. //
  4864. ppinaddr = (IN_ADDR**) phostent->h_addr_list;
  4865. dwTemp = 0;
  4866. while ((*ppinaddr) != NULL)
  4867. {
  4868. //
  4869. // Ignore 0.0.0.0 addresses again.
  4870. //
  4871. if ((*ppinaddr)->S_un.S_addr != INADDR_NONE)
  4872. {
  4873. painaddrAddresses[dwTemp].S_un.S_addr = (*ppinaddr)->S_un.S_addr;
  4874. DPFX(DPFPREP, 7, "\t%u- %u.%u.%u.%u",
  4875. dwTemp,
  4876. painaddrAddresses[dwTemp].S_un.S_un_b.s_b1,
  4877. painaddrAddresses[dwTemp].S_un.S_un_b.s_b2,
  4878. painaddrAddresses[dwTemp].S_un.S_un_b.s_b3,
  4879. painaddrAddresses[dwTemp].S_un.S_un_b.s_b4);
  4880. dwTemp++;
  4881. }
  4882. ppinaddr++;
  4883. }
  4884. DNASSERT(dwTemp == dwNumAddresses);
  4885. }
  4886. /*
  4887. else
  4888. {
  4889. //
  4890. // Already have addresses array.
  4891. //
  4892. }
  4893. */
  4894. //
  4895. // Make sure that all of the devices we currently know about are still
  4896. // around.
  4897. //
  4898. pBilinkDevice = this->m_blDevices.GetNext();
  4899. while (pBilinkDevice != &this->m_blDevices)
  4900. {
  4901. DNASSERT(! pBilinkDevice->IsEmpty());
  4902. pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
  4903. pBilinkDevice = pBilinkDevice->GetNext();
  4904. fFound = FALSE;
  4905. for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
  4906. {
  4907. if (painaddrAddresses[dwTemp].S_un.S_addr == pDevice->GetLocalAddressV4())
  4908. {
  4909. fFound = TRUE;
  4910. break;
  4911. }
  4912. }
  4913. if (fFound)
  4914. {
  4915. //
  4916. // It may be time for this device to use a different port...
  4917. //
  4918. dwTemp = pDevice->GetFirstUPnPDiscoveryTime();
  4919. if ((dwTemp != 0) && ((GETTIMESTAMP() - dwTemp) > g_dwReusePortTime))
  4920. {
  4921. ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
  4922. saddrinTemp.sin_family = AF_INET;
  4923. saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  4924. sTemp = this->CreateSocket(&saddrinTemp, SOCK_DGRAM, IPPROTO_UDP);
  4925. if (sTemp != INVALID_SOCKET)
  4926. {
  4927. //
  4928. // Sanity check that we didn't lose the device address.
  4929. //
  4930. DNASSERT(saddrinTemp.sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4());
  4931. DPFX(DPFPREP, 4, "Device 0x%p UPnP discovery socket 0x%p (%u.%u.%u.%u:%u) created to replace port %u.",
  4932. pDevice,
  4933. sTemp,
  4934. saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
  4935. saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
  4936. saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
  4937. saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
  4938. NTOHS(saddrinTemp.sin_port),
  4939. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  4940. #ifndef DPNBUILD_NOHNETFWAPI
  4941. //
  4942. // If we used the HomeNet firewall API to open a hole for UPnP
  4943. // discovery multicasts, close it.
  4944. //
  4945. if (pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall())
  4946. {
  4947. hr = this->CloseDevicesUPnPDiscoveryPort(pDevice, NULL);
  4948. if (hr != DPNH_OK)
  4949. {
  4950. DPFX(DPFPREP, 0, "Couldn't close device 0x%p's previous UPnP discovery socket's port on firewall (err = 0x%lx)! Ignoring.",
  4951. pDevice, hr);
  4952. //
  4953. // Continue...
  4954. //
  4955. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  4956. hr = DPNH_OK;
  4957. }
  4958. }
  4959. #endif // ! DPNBUILD_NOHNETFWAPI
  4960. pDevice->SetUPnPDiscoverySocketPort(saddrinTemp.sin_port);
  4961. pDevice->SetFirstUPnPDiscoveryTime(0);
  4962. //
  4963. // Close the existing socket.
  4964. //
  4965. this->m_pfnclosesocket(pDevice->GetUPnPDiscoverySocket());
  4966. //
  4967. // Transfer ownership of the new socket to the device.
  4968. //
  4969. pDevice->SetUPnPDiscoverySocket(sTemp);
  4970. sTemp = INVALID_SOCKET;
  4971. DPFX(DPFPREP, 8, "Device 0x%p got re-assigned UPnP socket 0x%p.",
  4972. pDevice, pDevice->GetUPnPDiscoverySocket());
  4973. //
  4974. // We'll let the normal "check for firewall" code detect
  4975. // the fact that the discovery socket is not mapped on the
  4976. // firewall and try to do so there (if it even needs to be
  4977. // mapped). See UpdateServerStatus.
  4978. //
  4979. }
  4980. else
  4981. {
  4982. DPFX(DPFPREP, 0, "Couldn't create a replacement UPnP discovery socket for device 0x%p! Using existing port %u.",
  4983. pDevice, NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  4984. }
  4985. }
  4986. }
  4987. else
  4988. {
  4989. //
  4990. // Didn't find this device in the returned list, forget about
  4991. // it.
  4992. //
  4993. #ifdef DBG
  4994. {
  4995. IN_ADDR inaddrTemp;
  4996. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  4997. DPFX(DPFPREP, 1, "Device 0x%p no longer exists, removing (address was %u.%u.%u.%u).",
  4998. pDevice,
  4999. inaddrTemp.S_un.S_un_b.s_b1,
  5000. inaddrTemp.S_un.S_un_b.s_b2,
  5001. inaddrTemp.S_un.S_un_b.s_b3,
  5002. inaddrTemp.S_un.S_un_b.s_b4);
  5003. }
  5004. this->m_dwNumDeviceRemoves++;
  5005. #endif // DBG
  5006. //
  5007. // Override the minimum UpdateServerStatus interval so that we can
  5008. // get information on any local public address changes due to the
  5009. // possible loss of a server on this interface.
  5010. //
  5011. this->m_dwFlags |= NATHELPUPNPOBJ_DEVICECHANGED;
  5012. //
  5013. // Since there was a change in the network, go back to polling
  5014. // relatively quickly.
  5015. //
  5016. this->ResetNextPollInterval();
  5017. //
  5018. // Forcefully mark the UPnP gateway device as disconnected.
  5019. //
  5020. if (pDevice->GetUPnPDevice() != NULL)
  5021. {
  5022. this->ClearDevicesUPnPDevice(pDevice);
  5023. }
  5024. //
  5025. // Mark all ports that were registered to this device as unowned
  5026. // by putting them into the wildcard list. First unmap them from
  5027. // the firewall.
  5028. //
  5029. pBilinkRegPort = pDevice->m_blOwnedRegPorts.GetNext();
  5030. while (pBilinkRegPort != &pDevice->m_blOwnedRegPorts)
  5031. {
  5032. DNASSERT(! pBilinkRegPort->IsEmpty());
  5033. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegPort);
  5034. pBilinkRegPort = pBilinkRegPort->GetNext();
  5035. DPFX(DPFPREP, 1, "Registered port 0x%p's device went away, marking as unowned.",
  5036. pRegisteredPort);
  5037. #ifndef DPNBUILD_NOHNETFWAPI
  5038. //
  5039. // Even though the device is gone, we can still remove the
  5040. // firewall mapping.
  5041. //
  5042. if (pRegisteredPort->IsMappedOnHNetFirewall())
  5043. {
  5044. //
  5045. // Unmap the port.
  5046. //
  5047. // Alert the user since this is unexpected.
  5048. //
  5049. hr = this->UnmapPortOnLocalHNetFirewall(pRegisteredPort,
  5050. TRUE,
  5051. TRUE);
  5052. if (hr != DPNH_OK)
  5053. {
  5054. DPFX(DPFPREP, 0, "Couldn't unmap registered port 0x%p from device 0x%p's firewall (err = 0x%lx)! Ignoring.",
  5055. pRegisteredPort, pDevice, hr);
  5056. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  5057. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  5058. //
  5059. // Continue anyway.
  5060. //
  5061. hr = DPNH_OK;
  5062. }
  5063. }
  5064. pRegisteredPort->NoteNotHNetFirewallPortUnavailable();
  5065. #endif // ! DPNBUILD_NOHNETFWAPI
  5066. DNASSERT(! pRegisteredPort->HasUPnPPublicAddresses());
  5067. DNASSERT(! pRegisteredPort->IsUPnPPortUnavailable());
  5068. pRegisteredPort->ClearDeviceOwner();
  5069. pRegisteredPort->m_blDeviceList.RemoveFromList();
  5070. pRegisteredPort->m_blDeviceList.InsertBefore(&this->m_blUnownedPorts);
  5071. //
  5072. // The user doesn't directly need to be informed. If the ports
  5073. // previously had public addresses, the ADDRESSESCHANGED flag
  5074. // would have already been set by ClearDevicesUPnPDevice. If
  5075. // they didn't have ports with public addresses, then the user
  5076. // won't see any difference and thus ADDRESSESCHANGED wouldn't
  5077. // need to be set.
  5078. //
  5079. }
  5080. #ifndef DPNBUILD_NOHNETFWAPI
  5081. //
  5082. // If we used the HomeNet firewall API to open a hole for UPnP
  5083. // discovery multicasts, close it.
  5084. //
  5085. if (pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall())
  5086. {
  5087. hr = this->CloseDevicesUPnPDiscoveryPort(pDevice, NULL);
  5088. if (hr != DPNH_OK)
  5089. {
  5090. DPFX(DPFPREP, 0, "Couldn't close device 0x%p's UPnP discovery socket's port on firewall (err = 0x%lx)! Ignoring.",
  5091. pDevice, hr);
  5092. //
  5093. // Continue...
  5094. //
  5095. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  5096. hr = DPNH_OK;
  5097. }
  5098. }
  5099. #endif // ! DPNBUILD_NOHNETFWAPI
  5100. pDevice->m_blList.RemoveFromList();
  5101. //
  5102. // Close the socket, if we had one.
  5103. //
  5104. if (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP)
  5105. {
  5106. this->m_pfnclosesocket(pDevice->GetUPnPDiscoverySocket());
  5107. pDevice->SetUPnPDiscoverySocket(INVALID_SOCKET);
  5108. }
  5109. delete pDevice;
  5110. }
  5111. }
  5112. //
  5113. // Search for all returned devices in our existing list, and add new
  5114. // entries for each one that we didn't already know about.
  5115. //
  5116. for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
  5117. {
  5118. fFound = FALSE;
  5119. pBilinkDevice = this->m_blDevices.GetNext();
  5120. while (pBilinkDevice != &this->m_blDevices)
  5121. {
  5122. DNASSERT(! pBilinkDevice->IsEmpty());
  5123. pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
  5124. pBilinkDevice = pBilinkDevice->GetNext();
  5125. if (pDevice->GetLocalAddressV4() == painaddrAddresses[dwTemp].S_un.S_addr)
  5126. {
  5127. fFound = TRUE;
  5128. break;
  5129. }
  5130. }
  5131. if (! fFound)
  5132. {
  5133. //
  5134. // We didn't know about this device. Create a new object.
  5135. //
  5136. pDevice = new CDevice(painaddrAddresses[dwTemp].S_un.S_addr);
  5137. if (pDevice == NULL)
  5138. {
  5139. hr = DPNHERR_OUTOFMEMORY;
  5140. goto Failure;
  5141. }
  5142. fDeviceCreated = TRUE;
  5143. #ifdef DBG
  5144. DPFX(DPFPREP, 1, "Found new device %u.%u.%u.%u, (object = 0x%p).",
  5145. painaddrAddresses[dwTemp].S_un.S_un_b.s_b1,
  5146. painaddrAddresses[dwTemp].S_un.S_un_b.s_b2,
  5147. painaddrAddresses[dwTemp].S_un.S_un_b.s_b3,
  5148. painaddrAddresses[dwTemp].S_un.S_un_b.s_b4,
  5149. pDevice);
  5150. this->m_dwNumDeviceAdds++;
  5151. #endif // DBG
  5152. //
  5153. // Override the minimum UpdateServerStatus interval so that we can
  5154. // get information on this new device.
  5155. //
  5156. this->m_dwFlags |= NATHELPUPNPOBJ_DEVICECHANGED;
  5157. //
  5158. // Since there was a change in the network, go back to polling
  5159. // relatively quickly.
  5160. //
  5161. this->ResetNextPollInterval();
  5162. //
  5163. // Create the UPnP discovery socket, if we're allowed.
  5164. //
  5165. if (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP)
  5166. {
  5167. ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
  5168. saddrinTemp.sin_family = AF_INET;
  5169. saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  5170. sTemp = this->CreateSocket(&saddrinTemp, SOCK_DGRAM, IPPROTO_UDP);
  5171. if (sTemp == INVALID_SOCKET)
  5172. {
  5173. DPFX(DPFPREP, 0, "Couldn't create a UPnP discovery socket! Ignoring address (and destroying device 0x%p).",
  5174. pDevice);
  5175. //
  5176. // Get rid of the device.
  5177. //
  5178. delete pDevice;
  5179. pDevice = NULL;
  5180. //
  5181. // Forget about device in case of failure later.
  5182. //
  5183. fDeviceCreated = FALSE;
  5184. //
  5185. // Move to next address.
  5186. //
  5187. continue;
  5188. }
  5189. //
  5190. // Sanity check that we didn't lose the device address.
  5191. //
  5192. DNASSERT(saddrinTemp.sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4());
  5193. DPFX(DPFPREP, 4, "Device 0x%p UPnP discovery socket 0x%p (%u.%u.%u.%u:%u) created.",
  5194. pDevice,
  5195. sTemp,
  5196. saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
  5197. saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
  5198. saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
  5199. saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
  5200. NTOHS(saddrinTemp.sin_port));
  5201. pDevice->SetUPnPDiscoverySocketPort(saddrinTemp.sin_port);
  5202. //
  5203. // Transfer ownership of the socket to the device.
  5204. //
  5205. pDevice->SetUPnPDiscoverySocket(sTemp);
  5206. sTemp = INVALID_SOCKET;
  5207. DPFX(DPFPREP, 8, "Device 0x%p got assigned UPnP socket 0x%p.",
  5208. pDevice, pDevice->GetUPnPDiscoverySocket());
  5209. }
  5210. #ifndef DPNBUILD_NOHNETFWAPI
  5211. if (this->m_dwFlags & NATHELPUPNPOBJ_USEHNETFWAPI)
  5212. {
  5213. //
  5214. // Check if the local firewall is enabled.
  5215. //
  5216. hr = this->CheckForLocalHNetFirewallAndMapPorts(pDevice, NULL);
  5217. if (hr != DPNH_OK)
  5218. {
  5219. DPFX(DPFPREP, 0, "Couldn't check for local HNet firewall and map ports (err = 0x%lx)! Continuing.",
  5220. hr);
  5221. DNASSERT(! pDevice->IsHNetFirewalled());
  5222. hr = DPNH_OK;
  5223. }
  5224. }
  5225. else
  5226. {
  5227. //
  5228. // Not using firewall traversal.
  5229. //
  5230. }
  5231. #endif // ! DPNBUILD_NOHNETFWAPI
  5232. //
  5233. // Add the device to our known list.
  5234. //
  5235. pDevice->m_blList.InsertBefore(&this->m_blDevices);
  5236. //
  5237. // Inform the caller if they care.
  5238. //
  5239. if (pfFoundNewDevices != NULL)
  5240. {
  5241. (*pfFoundNewDevices) = TRUE;
  5242. }
  5243. //
  5244. // Forget about device in case of failure later.
  5245. //
  5246. fDeviceCreated = FALSE;
  5247. }
  5248. }
  5249. //
  5250. // If we got some very weird failures and ended up here without any
  5251. // devices, complain to management (or the caller of this function, that's
  5252. // probably more convenient).
  5253. //
  5254. if (this->m_blDevices.IsEmpty())
  5255. {
  5256. DPFX(DPFPREP, 0, "No usable devices, cannot proceed!", 0);
  5257. DNASSERTX(! "No usable devices!", 2);
  5258. hr = DPNHERR_GENERIC;
  5259. goto Failure;
  5260. }
  5261. Exit:
  5262. if (painaddrAddresses != NULL)
  5263. {
  5264. DNFree(painaddrAddresses);
  5265. painaddrAddresses = NULL;
  5266. }
  5267. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  5268. return hr;
  5269. Failure:
  5270. if (sTemp != INVALID_SOCKET)
  5271. {
  5272. this->m_pfnclosesocket(sTemp);
  5273. sTemp = INVALID_SOCKET;
  5274. }
  5275. if (fDeviceCreated)
  5276. {
  5277. delete pDevice;
  5278. }
  5279. goto Exit;
  5280. } // CNATHelpUPnP::CheckForNewDevices
  5281. #ifndef DPNBUILD_NOHNETFWAPI
  5282. #undef DPF_MODNAME
  5283. #define DPF_MODNAME "CNATHelpUPnP::CheckForLocalHNetFirewallAndMapPorts"
  5284. //=============================================================================
  5285. // CNATHelpUPnP::CheckForLocalHNetFirewallAndMapPorts
  5286. //-----------------------------------------------------------------------------
  5287. //
  5288. // Description: Looks for a local HomeNet API aware firewall, and ensures
  5289. // there are mappings for each of the device's registered ports,
  5290. // if a firewall is found.
  5291. //
  5292. // If any registered port (except pDontAlertRegisteredPort if
  5293. // not NULL) gets mapped, then it will trigger an address update
  5294. // alert the next time the user calls GetCaps.
  5295. //
  5296. // The main object lock is assumed to be held. It will be
  5297. // converted into the long lock for the duration of this function.
  5298. //
  5299. // Arguments:
  5300. // CDevice * pDevice - Pointer to device to check.
  5301. // CRegisteredPort * pDontAlertRegisteredPort - Pointer to registered port
  5302. // that should not trigger an
  5303. // address update alert, or
  5304. // NULL.
  5305. //
  5306. // Returns: HRESULT
  5307. // DPNH_OK - Search completed successfully. There may or may not
  5308. // be a firewall.
  5309. // DPNHERR_GENERIC - An error occurred.
  5310. //=============================================================================
  5311. HRESULT CNATHelpUPnP::CheckForLocalHNetFirewallAndMapPorts(CDevice * const pDevice,
  5312. CRegisteredPort * const pDontAlertRegisteredPort)
  5313. {
  5314. HRESULT hr = DPNH_OK;
  5315. BOOL fSwitchedToLongLock = FALSE;
  5316. BOOL fUninitializeCOM = FALSE;
  5317. IHNetCfgMgr * pHNetCfgMgr = NULL;
  5318. IHNetConnection * pHNetConnection = NULL;
  5319. CBilink * pBilink;
  5320. CRegisteredPort * pRegisteredPort;
  5321. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  5322. this, pDevice, pDontAlertRegisteredPort);
  5323. //
  5324. // If this is the loopback address, don't bother trying to map anything.
  5325. //
  5326. if (pDevice->GetLocalAddressV4() == NETWORKBYTEORDER_INADDR_LOOPBACK)
  5327. {
  5328. DPFX(DPFPREP, 7, "No firewall behavior necessary with loopback device 0x%p.",
  5329. pDevice);
  5330. goto Exit;
  5331. }
  5332. //
  5333. // If we don't have IPHLPAPI or RASAPI32, we can't do anything (and
  5334. // shouldn't need to).
  5335. //
  5336. if ((this->m_hIpHlpApiDLL == NULL) || (this->m_hRasApi32DLL == NULL))
  5337. {
  5338. DPFX(DPFPREP, 7, "Didn't load IPHLPAPI and/or RASAPI32, not getting HNet interfaces for device 0x%p.",
  5339. pDevice);
  5340. goto Exit;
  5341. }
  5342. //
  5343. // Using the HomeNet API (particularly the out-of-proc COM calls) during
  5344. // stress is really, really, painfully slow. Since we have one global lock
  5345. // the controls everything, other threads may be sitting for an equally
  5346. // long time... so long, in fact, that the critical section timeout fires
  5347. // and we get a false stress hit. So we have a sneaky workaround to
  5348. // prevent that from happening while still maintaining ownership of the
  5349. // object.
  5350. //
  5351. this->SwitchToLongLock();
  5352. fSwitchedToLongLock = TRUE;
  5353. //
  5354. // Try to initialize COM if we weren't instantiated through COM. It may
  5355. // have already been initialized in a different mode, which is okay. As
  5356. // long as it has been initialized somehow, we're fine.
  5357. //
  5358. if (this->m_dwFlags & NATHELPUPNPOBJ_NOTCREATEDWITHCOM)
  5359. {
  5360. hr = CoInitializeEx(NULL, (COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE));
  5361. switch (hr)
  5362. {
  5363. case S_OK:
  5364. {
  5365. //
  5366. // Success, that's good. Cleanup when we're done.
  5367. //
  5368. DPFX(DPFPREP, 8, "Successfully initialized COM.");
  5369. fUninitializeCOM = TRUE;
  5370. break;
  5371. }
  5372. case S_FALSE:
  5373. {
  5374. //
  5375. // Someone else already initialized COM, but that's okay.
  5376. // Cleanup when we're done.
  5377. //
  5378. DPFX(DPFPREP, 8, "Initialized COM (again).");
  5379. fUninitializeCOM = TRUE;
  5380. break;
  5381. }
  5382. case RPC_E_CHANGED_MODE:
  5383. {
  5384. //
  5385. // Someone else already initialized COM in a different mode.
  5386. // It should be okay, but we don't have to balance the CoInit
  5387. // call with a CoUninit.
  5388. //
  5389. DPFX(DPFPREP, 8, "Didn't initialize COM, already initialized in a different mode.");
  5390. break;
  5391. }
  5392. default:
  5393. {
  5394. //
  5395. // Hmm, something else is going on. We can't handle that.
  5396. //
  5397. DPFX(DPFPREP, 0, "Initializing COM failed (err = 0x%lx)!", hr);
  5398. goto Failure;
  5399. break;
  5400. }
  5401. }
  5402. }
  5403. else
  5404. {
  5405. DPFX(DPFPREP, 8, "Object was instantiated through COM, no need to initialize COM.");
  5406. }
  5407. //
  5408. // Try creating the main HNet manager object.
  5409. //
  5410. hr = CoCreateInstance(CLSID_HNetCfgMgr, NULL, CLSCTX_INPROC_SERVER,
  5411. IID_IHNetCfgMgr, (PVOID*) (&pHNetCfgMgr));
  5412. if (hr != S_OK)
  5413. {
  5414. DPFX(DPFPREP, 1, "Couldn't create IHNetCfgMgr interface for device 0x%p (err = 0x%lx), assuming firewall control interface unavailable.",
  5415. pDevice, hr);
  5416. hr = DPNH_OK;
  5417. goto Exit;
  5418. }
  5419. //
  5420. // We created the IHNetCfgMgr object as in-proc, so there's no proxy that
  5421. // requires security settings.
  5422. //
  5423. //SETDEFAULTPROXYBLANKET(pHNetCfgMgr);
  5424. //
  5425. // Get the HNetConnection object for this device.
  5426. //
  5427. hr = this->GetIHNetConnectionForDeviceIfFirewalled(pDevice,
  5428. pHNetCfgMgr,
  5429. &pHNetConnection);
  5430. if (hr != DPNH_OK)
  5431. {
  5432. DPFX(DPFPREP, 1, "Couldn't get IHNetConnection interface for device 0x%p (err = 0x%lx), assuming firewall not enabled.",
  5433. pDevice, hr);
  5434. //
  5435. // If the device was previously firewalled, we need to clear our info.
  5436. //
  5437. if (pDevice->IsHNetFirewalled())
  5438. {
  5439. DPFX(DPFPREP, 2, "Firewall is no longer enabled for device 0x%p.",
  5440. pDevice);
  5441. //
  5442. // Since there was a change in the network, go back to polling
  5443. // relatively quickly.
  5444. //
  5445. this->ResetNextPollInterval();
  5446. DNASSERT(pDevice->HasCheckedForFirewallAvailability());
  5447. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  5448. while (pBilink != &pDevice->m_blOwnedRegPorts)
  5449. {
  5450. DNASSERT(! pBilink->IsEmpty());
  5451. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  5452. //
  5453. // Unmap items mapped on the firewall.
  5454. //
  5455. if (pRegisteredPort->IsMappedOnHNetFirewall())
  5456. {
  5457. DPFX(DPFPREP, 1, "Unmapping registered port 0x%p from device 0x%p's disappearing firewall.",
  5458. pRegisteredPort, pDevice);
  5459. hr = this->UnmapPortOnLocalHNetFirewallInternal(pRegisteredPort,
  5460. TRUE,
  5461. pHNetCfgMgr);
  5462. if (hr != DPNH_OK)
  5463. {
  5464. DPFX(DPFPREP, 0, "Couldn't unmap registered port 0x%p from device 0x%p's firewall (err = 0x%lx)! Ignoring.",
  5465. pRegisteredPort, pDevice, hr);
  5466. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  5467. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  5468. //
  5469. // Continue anyway.
  5470. //
  5471. hr = DPNH_OK;
  5472. }
  5473. //
  5474. // Alert the user.
  5475. //
  5476. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  5477. }
  5478. else
  5479. {
  5480. DPFX(DPFPREP, 1, "Registered port 0x%p was not mapped on device 0x%p's disappearing firewall, assuming being called within RegisterPorts.",
  5481. pRegisteredPort, pDevice);
  5482. }
  5483. //
  5484. // Go to next port.
  5485. //
  5486. pBilink = pBilink->GetNext();
  5487. }
  5488. //
  5489. // If we used the HomeNet firewall API to open a hole for UPnP
  5490. // discovery multicasts, unmap that, too.
  5491. //
  5492. if (pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall())
  5493. {
  5494. DPFX(DPFPREP, 0, "Device 0x%p's UPnP discovery socket's forcefully unmapped from disappearing firewall.",
  5495. pDevice);
  5496. hr = this->CloseDevicesUPnPDiscoveryPort(pDevice, pHNetCfgMgr);
  5497. if (hr != DPNH_OK)
  5498. {
  5499. DPFX(DPFPREP, 0, "Couldn't close device 0x%p's UPnP discovery socket's port on firewall (err = 0x%lx)! Ignoring.",
  5500. pDevice, hr);
  5501. //
  5502. // Continue...
  5503. //
  5504. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  5505. hr = DPNH_OK;
  5506. }
  5507. }
  5508. //
  5509. // Turn off the flag now that all registered ports have been
  5510. // removed.
  5511. //
  5512. pDevice->NoteNotHNetFirewalled();
  5513. }
  5514. else
  5515. {
  5516. if (! pDevice->HasCheckedForFirewallAvailability())
  5517. {
  5518. //
  5519. // The firewall is not enabled.
  5520. //
  5521. DPFX(DPFPREP, 2, "Firewall is not enabled for device 0x%p.",
  5522. pDevice);
  5523. pDevice->NoteCheckedForFirewallAvailability();
  5524. //
  5525. // Since it is possible to remove mappings even without the
  5526. // firewall enabled, we can be courteous and unmap any stale
  5527. // entries left by previous app crashes when the firewall was
  5528. // still enabled.
  5529. //
  5530. //
  5531. // Pretend that it currently had been firewalled.
  5532. //
  5533. pDevice->NoteHNetFirewalled();
  5534. //
  5535. // Cleanup the mappings.
  5536. //
  5537. hr = this->CleanupInactiveFirewallMappings(pDevice, pHNetCfgMgr);
  5538. if (hr != DPNH_OK)
  5539. {
  5540. DPFX(DPFPREP, 0, "Failed cleaning up inactive firewall mappings with device 0x%p (firewall not initially enabled)!",
  5541. pDevice);
  5542. goto Failure;
  5543. }
  5544. //
  5545. // Turn off the flag we temporarily enabled while clearing
  5546. // the mappings.
  5547. //
  5548. pDevice->NoteNotHNetFirewalled();
  5549. }
  5550. else
  5551. {
  5552. //
  5553. // The firewall is still not enabled.
  5554. //
  5555. DPFX(DPFPREP, 2, "Firewall is still not enabled for device 0x%p.",
  5556. pDevice);
  5557. }
  5558. }
  5559. hr = DPNH_OK;
  5560. goto Exit;
  5561. }
  5562. //
  5563. // If firewalling is enabled now, and wasn't before, we need to map all the
  5564. // existing ports. If it had been, we're fine.
  5565. //
  5566. if (! pDevice->IsHNetFirewalled())
  5567. {
  5568. DPFX(DPFPREP, 2, "Firewall is now enabled for device 0x%p.",
  5569. pDevice);
  5570. pDevice->NoteCheckedForFirewallAvailability();
  5571. pDevice->NoteHNetFirewalled();
  5572. //
  5573. // Since there was a change in the network, go back to polling
  5574. // relatively quickly.
  5575. //
  5576. this->ResetNextPollInterval();
  5577. //
  5578. // If we're allowed, we need to try opening a hole so that we can
  5579. // receive responses from device discovery multicasts. We'll
  5580. // ignore failures, since this is only to support the funky case of
  5581. // enabling firewall behind a NAT.
  5582. //
  5583. if ((g_fMapUPnPDiscoverySocket) &&
  5584. (pDevice->GetLocalAddressV4() != NETWORKBYTEORDER_INADDR_LOOPBACK) &&
  5585. (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP))
  5586. {
  5587. hr = this->OpenDevicesUPnPDiscoveryPort(pDevice,
  5588. pHNetCfgMgr,
  5589. pHNetConnection);
  5590. if (hr != DPNH_OK)
  5591. {
  5592. DPFX(DPFPREP, 0, "Couldn't open device 0x%p's UPnP discovery socket's port on firewall (err = 0x%lx)! Ignoring, NAT may be undetectable.",
  5593. pDevice, hr);
  5594. hr = DPNH_OK;
  5595. //
  5596. // Continue...
  5597. //
  5598. }
  5599. }
  5600. else
  5601. {
  5602. DPFX(DPFPREP, 3, "Not opening device 0x%p's UPnP discovery port (domap = %i, loopback = %i, upnp = %i).",
  5603. pDevice,
  5604. g_fMapUPnPDiscoverySocket,
  5605. ((pDevice->GetLocalAddressV4() != NETWORKBYTEORDER_INADDR_LOOPBACK) ? FALSE : TRUE),
  5606. ((this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP) ? TRUE : FALSE));
  5607. }
  5608. //
  5609. // Try to remove any mappings that were not freed earlier because
  5610. // we crashed.
  5611. //
  5612. hr = this->CleanupInactiveFirewallMappings(pDevice, pHNetCfgMgr);
  5613. if (hr != DPNH_OK)
  5614. {
  5615. DPFX(DPFPREP, 0, "Failed cleaning up inactive firewall mappings with device 0x%p's new firewall!",
  5616. pDevice);
  5617. goto Failure;
  5618. }
  5619. }
  5620. else
  5621. {
  5622. DPFX(DPFPREP, 2, "Firewall is still enabled for device 0x%p.",
  5623. pDevice);
  5624. DNASSERT(pDevice->HasCheckedForFirewallAvailability());
  5625. //
  5626. // Try to map the discovery socket if it hasn't been (and we're allowed
  5627. // & supposed to).
  5628. //
  5629. if ((g_fMapUPnPDiscoverySocket) &&
  5630. (! pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall()) &&
  5631. (pDevice->GetLocalAddressV4() != NETWORKBYTEORDER_INADDR_LOOPBACK) &&
  5632. (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP))
  5633. {
  5634. hr = this->OpenDevicesUPnPDiscoveryPort(pDevice,
  5635. pHNetCfgMgr,
  5636. pHNetConnection);
  5637. if (hr != DPNH_OK)
  5638. {
  5639. DPFX(DPFPREP, 0, "Couldn't open device 0x%p's UPnP discovery socket's port on firewall (err = 0x%lx)! Ignoring, NAT may be undetectable.",
  5640. pDevice, hr);
  5641. hr = DPNH_OK;
  5642. //
  5643. // Continue...
  5644. //
  5645. }
  5646. }
  5647. else
  5648. {
  5649. DPFX(DPFPREP, 3, "Not opening device 0x%p's UPnP discovery port (domap = %i, already = %i, loopback = %i, upnp = %i).",
  5650. pDevice,
  5651. g_fMapUPnPDiscoverySocket,
  5652. pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall(),
  5653. ((pDevice->GetLocalAddressV4() != NETWORKBYTEORDER_INADDR_LOOPBACK) ? FALSE : TRUE),
  5654. ((this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP) ? TRUE : FALSE));
  5655. }
  5656. }
  5657. //
  5658. // Map all the ports that haven't been yet.
  5659. //
  5660. hr = this->MapUnmappedPortsOnLocalHNetFirewall(pDevice,
  5661. pHNetCfgMgr,
  5662. pHNetConnection,
  5663. pDontAlertRegisteredPort);
  5664. if (hr != DPNH_OK)
  5665. {
  5666. DPFX(DPFPREP, 0, "Couldn't map ports on device 0x%p's new firewall (err = 0x%lx)!",
  5667. pDevice, hr);
  5668. goto Failure;
  5669. }
  5670. DNASSERT(hr == DPNH_OK);
  5671. Exit:
  5672. if (pHNetConnection != NULL)
  5673. {
  5674. pHNetConnection->Release();
  5675. pHNetConnection = NULL;
  5676. }
  5677. if (pHNetCfgMgr != NULL)
  5678. {
  5679. pHNetCfgMgr->Release();
  5680. pHNetCfgMgr = NULL;
  5681. }
  5682. if (fUninitializeCOM)
  5683. {
  5684. DPFX(DPFPREP, 8, "Uninitializing COM.");
  5685. CoUninitialize();
  5686. fUninitializeCOM = FALSE;
  5687. }
  5688. if (fSwitchedToLongLock)
  5689. {
  5690. this->SwitchFromLongLock();
  5691. fSwitchedToLongLock = FALSE;
  5692. }
  5693. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  5694. return hr;
  5695. Failure:
  5696. //
  5697. // Ensure that the device is not considered to be firewalled.
  5698. //
  5699. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  5700. pDevice->NoteNotHNetFirewalled();
  5701. //
  5702. // Make sure no registered ports are marked as firewalled either.
  5703. //
  5704. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  5705. while (pBilink != &pDevice->m_blOwnedRegPorts)
  5706. {
  5707. DNASSERT(! pBilink->IsEmpty());
  5708. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  5709. if (pRegisteredPort->IsMappedOnHNetFirewall())
  5710. {
  5711. DPFX(DPFPREP, 1, "Registered port 0x%p forcefully marked as not mapped on HomeNet firewall.",
  5712. pRegisteredPort);
  5713. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  5714. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  5715. }
  5716. pBilink = pBilink->GetNext();
  5717. }
  5718. goto Exit;
  5719. } // CNATHelpUPnP::CheckForLocalHNetFirewallAndMapPorts
  5720. #undef DPF_MODNAME
  5721. #define DPF_MODNAME "CNATHelpUPnP::GetIHNetConnectionForDeviceIfFirewalled"
  5722. //=============================================================================
  5723. // CNATHelpUPnP::GetIHNetConnectionForDeviceIfFirewalled
  5724. //-----------------------------------------------------------------------------
  5725. //
  5726. // Description: Returns an IHNetConnection interface for the given device.
  5727. //
  5728. // COM is assumed to have been initialized.
  5729. //
  5730. // The object lock is assumed to be held.
  5731. //
  5732. // Arguments:
  5733. // CDevice * pDevice - Pointer to device whose
  5734. // IHNetConnection interface
  5735. // should be retrieved.
  5736. // IHNetCfgMgr * pHNetCfgMgr - IHNetCfgMgr interface to use.
  5737. // IHNetConnection ** ppHNetConnection - Place to store IHetConnection
  5738. // interface retrieved.
  5739. //
  5740. // Returns: HRESULT
  5741. // DPNH_OK - Interface retrieved successfully.
  5742. // DPNHERR_GENERIC - An error occurred.
  5743. //=============================================================================
  5744. HRESULT CNATHelpUPnP::GetIHNetConnectionForDeviceIfFirewalled(CDevice * const pDevice,
  5745. IHNetCfgMgr * const pHNetCfgMgr,
  5746. IHNetConnection ** const ppHNetConnection)
  5747. {
  5748. HRESULT hr;
  5749. DWORD dwError;
  5750. IHNetFirewallSettings * pHNetFirewallSettings = NULL;
  5751. IEnumHNetFirewalledConnections * pEnumHNetFirewalledConnections = NULL;
  5752. IHNetFirewalledConnection * pHNetFirewalledConnection = NULL;
  5753. ULONG ulNumFound;
  5754. IHNetConnection * pHNetConnection = NULL;
  5755. HNET_CONN_PROPERTIES * pHNetConnProperties;
  5756. BOOL fLanConnection;
  5757. IN_ADDR inaddrTemp;
  5758. TCHAR tszDeviceIPAddress[16]; // "nnn.nnn.nnn.nnn" + NULL termination
  5759. BOOL fHaveDeviceGUID = FALSE;
  5760. TCHAR tszGuidDevice[GUID_STRING_LENGTH + 1]; // include NULL termination
  5761. TCHAR tszGuidHNetConnection[GUID_STRING_LENGTH + 1]; // include NULL termination
  5762. GUID * pguidHNetConnection = NULL;
  5763. WCHAR * pwszPhonebookPath = NULL;
  5764. WCHAR * pwszName = NULL;
  5765. HRASCONN hrasconn;
  5766. RASPPPIP raspppip;
  5767. DWORD dwSize;
  5768. DPFX(DPFPREP, 6, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p)",
  5769. this, pDevice, pHNetCfgMgr, ppHNetConnection);
  5770. //
  5771. // Convert the IP address right away. We use it frequently so there's no
  5772. // sense in continually regenerating it.
  5773. //
  5774. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  5775. wsprintf(tszDeviceIPAddress, _T("%u.%u.%u.%u"),
  5776. inaddrTemp.S_un.S_un_b.s_b1,
  5777. inaddrTemp.S_un.S_un_b.s_b2,
  5778. inaddrTemp.S_un.S_un_b.s_b3,
  5779. inaddrTemp.S_un.S_un_b.s_b4);
  5780. //
  5781. // Here is what we're going to do in this function:
  5782. //
  5783. // IHNetCfgMgr::QueryInterface for IHNetFirewallSettings
  5784. // IHNetFirewallSettings::EnumFirewalledConnections
  5785. // IHNetFirewalledConnection::QueryInterface for IHNetConnection
  5786. // get the IHNetConnection's HNET_CONN_PROPERTIES
  5787. // if HNET_CONN_PROPERTIES.fLanConnection
  5788. // IHNetConnection::GetGuid()
  5789. // if GUID matches IPHLPAPI GUID
  5790. // We've got the one we want, we're done
  5791. // else
  5792. // Keep looping
  5793. // else
  5794. // IHNetConnection::GetRasPhonebookPath and IHNetConnection::GetName to pass into RasGetEntryHrasconnW as pszPhonebook and pszEntry, respectively
  5795. // if got HRASCONN
  5796. // RasGetProjectionInfo
  5797. // if IP matches the IP we're looking for
  5798. // We've got the one we want, we're done
  5799. // else
  5800. // Keep looping
  5801. // else
  5802. // RAS entry is not dialed, keep looping
  5803. // if didn't find object
  5804. // it's not firewalled
  5805. //
  5806. //
  5807. // Get the firewall settings object.
  5808. //
  5809. hr = pHNetCfgMgr->QueryInterface(IID_IHNetFirewallSettings,
  5810. (PVOID*) (&pHNetFirewallSettings));
  5811. if (hr != S_OK)
  5812. {
  5813. DPFX(DPFPREP, 0, "Couldn't query for IHNetFirewallSettings interface (err = 0x%lx)!",
  5814. hr);
  5815. goto Failure;
  5816. }
  5817. //
  5818. // The HNetxxx objects appear to not be proxied...
  5819. //
  5820. //SETDEFAULTPROXYBLANKET(pHNetFirewallSettings);
  5821. //
  5822. // Get the firewalled connections enumeration via IHNetFirewallSettings.
  5823. //
  5824. hr = pHNetFirewallSettings->EnumFirewalledConnections(&pEnumHNetFirewalledConnections);
  5825. if (hr != S_OK)
  5826. {
  5827. DPFX(DPFPREP, 0, "Couldn't query for IHNetFirewallSettings interface (err = 0x%lx)!",
  5828. hr);
  5829. //
  5830. // Make sure we don't try to release a bogus pointer in case it got
  5831. // set.
  5832. //
  5833. pEnumHNetFirewalledConnections = NULL;
  5834. goto Failure;
  5835. }
  5836. //
  5837. // The HNetxxx objects appear to not be proxied...
  5838. //
  5839. //SETDEFAULTPROXYBLANKET(pEnumHNetFirewalledConnections);
  5840. //
  5841. // Don't need the IHNetFirewallSettings interface anymore.
  5842. //
  5843. pHNetFirewallSettings->Release();
  5844. pHNetFirewallSettings = NULL;
  5845. //
  5846. // Keep looping until we find the item or run out of items.
  5847. //
  5848. do
  5849. {
  5850. hr = pEnumHNetFirewalledConnections->Next(1,
  5851. &pHNetFirewalledConnection,
  5852. &ulNumFound);
  5853. if (FAILED(hr))
  5854. {
  5855. DPFX(DPFPREP, 0, "Couldn't get next connection (err = 0x%lx)!",
  5856. hr);
  5857. goto Failure;
  5858. }
  5859. //
  5860. // If there aren't any more items, bail.
  5861. //
  5862. if (ulNumFound == 0)
  5863. {
  5864. //
  5865. // pEnumHNetFirewalledConnections->Next might have returned
  5866. // S_FALSE.
  5867. //
  5868. hr = DPNH_OK;
  5869. break;
  5870. }
  5871. //
  5872. // The HNetxxx objects appear to not be proxied...
  5873. //
  5874. //SETDEFAULTPROXYBLANKET(pHNetFirewalledConnection);
  5875. //
  5876. // Get the IHNetConnection interface.
  5877. //
  5878. hr = pHNetFirewalledConnection->QueryInterface(IID_IHNetConnection,
  5879. (PVOID*) (&pHNetConnection));
  5880. if (hr != S_OK)
  5881. {
  5882. DPFX(DPFPREP, 0, "Couldn't query for IHNetConnection interface (err = 0x%lx)!",
  5883. hr);
  5884. goto Failure;
  5885. }
  5886. //
  5887. // The HNetxxx objects appear to not be proxied...
  5888. //
  5889. //SETDEFAULTPROXYBLANKET(pHNetConnection);
  5890. //
  5891. // We don't need the firewalled connection object anymore.
  5892. //
  5893. pHNetFirewalledConnection->Release();
  5894. pHNetFirewalledConnection = NULL;
  5895. //
  5896. // Get the internal properties for this adapter.
  5897. //
  5898. hr = pHNetConnection->GetProperties(&pHNetConnProperties);
  5899. if (hr != S_OK)
  5900. {
  5901. DPFX(DPFPREP, 0, "Couldn't get home net connection properties (err = 0x%lx)!",
  5902. hr);
  5903. goto Failure;
  5904. }
  5905. //
  5906. // Be somewhat picky about whether adapters returned by
  5907. // IEnumHNetFirewalledConnections actually be firewalled.
  5908. //
  5909. DNASSERTX(pHNetConnProperties->fFirewalled, 2);
  5910. fLanConnection = pHNetConnProperties->fLanConnection;
  5911. //
  5912. // Free the properties buffer.
  5913. //
  5914. CoTaskMemFree(pHNetConnProperties);
  5915. //pHNetConnProperties = NULL;
  5916. //
  5917. // Now if it's a LAN connection, see if the GUID matches the one
  5918. // returned by IPHLPAPI.
  5919. // If it's a RAS connection, see if this phonebook entry is dialed and
  5920. // has the right IP address.
  5921. //
  5922. if (fLanConnection)
  5923. {
  5924. //
  5925. // LAN case. If we haven't already retrieved the device's GUID, do
  5926. // so now.
  5927. //
  5928. if (! fHaveDeviceGUID)
  5929. {
  5930. hr = this->GetIPAddressGuidString(tszDeviceIPAddress, tszGuidDevice);
  5931. if (hr != S_OK)
  5932. {
  5933. DPFX(DPFPREP, 0, "Couldn't get device 0x%p's GUID (err = 0x%lx)!",
  5934. hr);
  5935. goto Failure;
  5936. }
  5937. fHaveDeviceGUID = TRUE;
  5938. }
  5939. //
  5940. // Get the HNetConnection object's GUID.
  5941. //
  5942. hr = pHNetConnection->GetGuid(&pguidHNetConnection);
  5943. if (hr != S_OK)
  5944. {
  5945. DPFX(DPFPREP, 0, "Couldn't get HNetConnection 0x%p's GUID (err = 0x%lx)!",
  5946. pHNetConnection, hr);
  5947. goto Failure;
  5948. }
  5949. //
  5950. // Convert the GUID into a string.
  5951. //
  5952. wsprintf(tszGuidHNetConnection,
  5953. _T("{%-08.8X-%-04.4X-%-04.4X-%02.2X%02.2X-%02.2X%02.2X%02.2X%02.2X%02.2X%02.2X}"),
  5954. pguidHNetConnection->Data1,
  5955. pguidHNetConnection->Data2,
  5956. pguidHNetConnection->Data3,
  5957. pguidHNetConnection->Data4[0],
  5958. pguidHNetConnection->Data4[1],
  5959. pguidHNetConnection->Data4[2],
  5960. pguidHNetConnection->Data4[3],
  5961. pguidHNetConnection->Data4[4],
  5962. pguidHNetConnection->Data4[5],
  5963. pguidHNetConnection->Data4[6],
  5964. pguidHNetConnection->Data4[7]);
  5965. CoTaskMemFree(pguidHNetConnection);
  5966. pguidHNetConnection = NULL;
  5967. #ifdef DBG
  5968. //
  5969. // Attempt to get the HNetConnection object's name for debugging
  5970. // purposes.
  5971. //
  5972. hr = pHNetConnection->GetName(&pwszName);
  5973. if (hr != S_OK)
  5974. {
  5975. DPFX(DPFPREP, 0, "Couldn't get HNetConnection 0x%p name (err = 0x%lx)!",
  5976. pHNetConnection, hr);
  5977. goto Failure;
  5978. }
  5979. #endif // DBG
  5980. //
  5981. // See if we found the object we need.
  5982. //
  5983. if (_tcsicmp(tszGuidHNetConnection, tszGuidDevice) == 0)
  5984. {
  5985. DPFX(DPFPREP, 7, "Matched IHNetConnection object 0x%p \"%ls\" to device 0x%p (LAN GUID %s).",
  5986. pHNetConnection, pwszName, pDevice, tszGuidHNetConnection);
  5987. //
  5988. // Transfer reference to caller.
  5989. //
  5990. (*ppHNetConnection) = pHNetConnection;
  5991. pHNetConnection = NULL;
  5992. #ifdef DBG
  5993. CoTaskMemFree(pwszName);
  5994. pwszName = NULL;
  5995. #endif // DBG
  5996. //
  5997. // We're done here.
  5998. //
  5999. hr = DPNH_OK;
  6000. goto Exit;
  6001. }
  6002. DPFX(DPFPREP, 7, "Non-matching IHNetConnection 0x%p \"%ls\"",
  6003. pHNetConnection, pwszName);
  6004. DPFX(DPFPREP, 7, "\t(LAN GUID %s <> %s).",
  6005. tszGuidHNetConnection, tszGuidDevice);
  6006. #ifdef DBG
  6007. CoTaskMemFree(pwszName);
  6008. pwszName = NULL;
  6009. #endif // DBG
  6010. }
  6011. else
  6012. {
  6013. //
  6014. // RAS case.
  6015. //
  6016. DNASSERT(this->m_hRasApi32DLL != NULL);
  6017. //
  6018. // Get the HNetConnection object's phonebook path.
  6019. //
  6020. hr = pHNetConnection->GetRasPhonebookPath(&pwszPhonebookPath);
  6021. if (hr != S_OK)
  6022. {
  6023. DPFX(DPFPREP, 0, "Couldn't get HNetConnection's RAS phonebook path (err = 0x%lx)!",
  6024. hr);
  6025. goto Failure;
  6026. }
  6027. //
  6028. // Get the HNetConnection object's name.
  6029. //
  6030. hr = pHNetConnection->GetName(&pwszName);
  6031. if (hr != S_OK)
  6032. {
  6033. DPFX(DPFPREP, 0, "Couldn't get HNetConnection's name (err = 0x%lx)!",
  6034. hr);
  6035. goto Failure;
  6036. }
  6037. //
  6038. // Look for an active RAS connection from that phonebook with that
  6039. // name.
  6040. //
  6041. dwError = this->m_pfnRasGetEntryHrasconnW(pwszPhonebookPath, pwszName, &hrasconn);
  6042. if (dwError != 0)
  6043. {
  6044. //
  6045. // It's probably ERROR_NO_CONNECTION (668).
  6046. //
  6047. DPFX(DPFPREP, 1, "Couldn't get entry's active RAS connection (err = %u), assuming not dialed",
  6048. dwError);
  6049. DPFX(DPFPREP, 1, "\tname \"%ls\", phonebook \"%ls\".",
  6050. pwszName, pwszPhonebookPath);
  6051. }
  6052. else
  6053. {
  6054. //
  6055. // Get the IP address.
  6056. //
  6057. ZeroMemory(&raspppip, sizeof(raspppip));
  6058. raspppip.dwSize = sizeof(raspppip);
  6059. dwSize = sizeof(raspppip);
  6060. dwError = this->m_pfnRasGetProjectionInfo(hrasconn, RASP_PppIp, &raspppip, &dwSize);
  6061. if (dwError != 0)
  6062. {
  6063. DPFX(DPFPREP, 0, "Couldn't get RAS connection's IP information (err = %u)!",
  6064. dwError);
  6065. hr = DPNHERR_GENERIC;
  6066. goto Failure;
  6067. }
  6068. //
  6069. // See if we found the object we need.
  6070. //
  6071. if (_tcsicmp(raspppip.szIpAddress, tszDeviceIPAddress) == 0)
  6072. {
  6073. DPFX(DPFPREP, 7, "Matched IHNetConnection object 0x%p to device 0x%p (RAS IP %s)",
  6074. pHNetConnection, pDevice, raspppip.szIpAddress);
  6075. DPFX(DPFPREP, 7, "\tname \"%ls\", phonebook \"%ls\".",
  6076. pwszName, pwszPhonebookPath);
  6077. //
  6078. // Transfer reference to caller.
  6079. //
  6080. (*ppHNetConnection) = pHNetConnection;
  6081. pHNetConnection = NULL;
  6082. //
  6083. // We're done here.
  6084. //
  6085. hr = DPNH_OK;
  6086. goto Exit;
  6087. }
  6088. DPFX(DPFPREP, 7, "Non-matching IHNetConnection 0x%p (RAS IP %s != %s)",
  6089. pHNetConnection, raspppip.szIpAddress, tszDeviceIPAddress);
  6090. DPFX(DPFPREP, 7, "\tname \"%ls\", phonebook \"%ls\").",
  6091. pwszName, pwszPhonebookPath);
  6092. }
  6093. CoTaskMemFree(pwszPhonebookPath);
  6094. pwszPhonebookPath = NULL;
  6095. CoTaskMemFree(pwszName);
  6096. pwszName = NULL;
  6097. }
  6098. //
  6099. // If we're here, we pHNetConnection is not the one we're looking for.
  6100. //
  6101. pHNetConnection->Release();
  6102. pHNetConnection = NULL;
  6103. }
  6104. while (TRUE);
  6105. //
  6106. // If we're here, then we didn't find a matching firewall connection.
  6107. //
  6108. DPFX(DPFPREP, 3, "Didn't find device 0x%p in list of firewalled connections.",
  6109. pDevice);
  6110. hr = DPNHERR_GENERIC;
  6111. Exit:
  6112. if (pEnumHNetFirewalledConnections != NULL)
  6113. {
  6114. pEnumHNetFirewalledConnections->Release();
  6115. pEnumHNetFirewalledConnections = NULL;
  6116. }
  6117. DPFX(DPFPREP, 6, "(0x%p) Returning: [0x%lx]", this, hr);
  6118. return hr;
  6119. Failure:
  6120. if (pwszName != NULL)
  6121. {
  6122. CoTaskMemFree(pwszName);
  6123. pwszName = NULL;
  6124. }
  6125. if (pwszPhonebookPath == NULL)
  6126. {
  6127. CoTaskMemFree(pwszPhonebookPath);
  6128. pwszPhonebookPath = NULL;
  6129. }
  6130. if (pHNetConnection != NULL)
  6131. {
  6132. pHNetConnection->Release();
  6133. pHNetConnection = NULL;
  6134. }
  6135. if (pHNetFirewalledConnection != NULL)
  6136. {
  6137. pHNetFirewalledConnection->Release();
  6138. pHNetFirewalledConnection = NULL;
  6139. }
  6140. if (pHNetFirewallSettings != NULL)
  6141. {
  6142. pHNetFirewallSettings->Release();
  6143. pHNetFirewallSettings = NULL;
  6144. }
  6145. goto Exit;
  6146. } // CNATHelpUPnP::GetIHNetConnectionForDeviceIfFirewalled
  6147. #undef DPF_MODNAME
  6148. #define DPF_MODNAME "CNATHelpUPnP::GetIPAddressGuidString"
  6149. //=============================================================================
  6150. // CNATHelpUPnP::GetIPAddressGuidString
  6151. //-----------------------------------------------------------------------------
  6152. //
  6153. // Description: Retrieves the IPHLPAPI assigned GUID (in string format) for
  6154. // the given IP address string. ptszGuidString must be able to
  6155. // hold GUID_STRING_LENGTH + 1 characters.
  6156. //
  6157. // The object lock is assumed to be held.
  6158. //
  6159. // Arguments:
  6160. // TCHAR * tszDeviceIPAddress - IP address string to lookup.
  6161. // TCHAR * ptszGuidString - Place to store device's GUID string.
  6162. //
  6163. // Returns: HRESULT
  6164. // DPNH_OK - Interface retrieved successfully.
  6165. // DPNHERR_GENERIC - An error occurred.
  6166. //=============================================================================
  6167. HRESULT CNATHelpUPnP::GetIPAddressGuidString(const TCHAR * const tszDeviceIPAddress,
  6168. TCHAR * const ptszGuidString)
  6169. {
  6170. HRESULT hr = DPNH_OK;
  6171. DWORD dwError;
  6172. PIP_ADAPTER_INFO pAdaptersBuffer = NULL;
  6173. ULONG ulSize;
  6174. PIP_ADAPTER_INFO pAdapterInfo;
  6175. PIP_ADDR_STRING pIPAddrString;
  6176. char * pszAdapterGuid = NULL;
  6177. #ifdef UNICODE
  6178. char szDeviceIPAddress[16]; // "nnn.nnn.nnn.nnn" + NULL termination
  6179. #endif // UNICODE
  6180. #ifdef DBG
  6181. char szIPList[256];
  6182. char * pszCurrentIP;
  6183. #endif // DBG
  6184. DPFX(DPFPREP, 6, "(0x%p) Parameters: (\"%s\", 0x%p)",
  6185. this, tszDeviceIPAddress, ptszGuidString);
  6186. DNASSERT(this->m_hIpHlpApiDLL != NULL);
  6187. //
  6188. // Keep trying to get the list of adapters until we get ERROR_SUCCESS or a
  6189. // legitimate error (other than ERROR_BUFFER_OVERFLOW or
  6190. // ERROR_INSUFFICIENT_BUFFER).
  6191. //
  6192. ulSize = 0;
  6193. do
  6194. {
  6195. dwError = this->m_pfnGetAdaptersInfo(pAdaptersBuffer, &ulSize);
  6196. if (dwError == ERROR_SUCCESS)
  6197. {
  6198. //
  6199. // We succeeded, we should be set. But make sure there are
  6200. // adapters for us to use.
  6201. //
  6202. if (ulSize < sizeof(IP_ADAPTER_INFO))
  6203. {
  6204. DPFX(DPFPREP, 0, "Getting adapters info succeeded but didn't return any valid adapters (%u < %u)!",
  6205. ulSize, sizeof(IP_ADAPTER_INFO));
  6206. hr = DPNHERR_GENERIC;
  6207. goto Failure;
  6208. }
  6209. break;
  6210. }
  6211. if ((dwError != ERROR_BUFFER_OVERFLOW) &&
  6212. (dwError != ERROR_INSUFFICIENT_BUFFER))
  6213. {
  6214. DPFX(DPFPREP, 0, "Unable to get adapters info (error = 0x%lx)!",
  6215. dwError);
  6216. hr = DPNHERR_GENERIC;
  6217. goto Failure;
  6218. }
  6219. //
  6220. // We need more adapter space. Make sure there are adapters for us to
  6221. // use.
  6222. //
  6223. if (ulSize < sizeof(IP_ADAPTER_INFO))
  6224. {
  6225. DPFX(DPFPREP, 0, "Getting adapters info didn't return any valid adapters (%u < %u)!",
  6226. ulSize, sizeof(IP_ADAPTER_INFO));
  6227. hr = DPNHERR_GENERIC;
  6228. goto Failure;
  6229. }
  6230. //
  6231. // If we previously had a buffer, free it.
  6232. //
  6233. if (pAdaptersBuffer != NULL)
  6234. {
  6235. DNFree(pAdaptersBuffer);
  6236. }
  6237. //
  6238. // Allocate the buffer.
  6239. //
  6240. pAdaptersBuffer = (PIP_ADAPTER_INFO) DNMalloc(ulSize);
  6241. if (pAdaptersBuffer == NULL)
  6242. {
  6243. DPFX(DPFPREP, 0, "Unable to allocate memory for adapters info!");
  6244. hr = DPNHERR_OUTOFMEMORY;
  6245. goto Failure;
  6246. }
  6247. }
  6248. while (TRUE);
  6249. #ifdef UNICODE
  6250. STR_jkWideToAnsi(szDeviceIPAddress,
  6251. tszDeviceIPAddress,
  6252. 16);
  6253. szDeviceIPAddress[15] = 0; // ensure it's NULL terminated
  6254. #endif // UNICODE
  6255. //
  6256. // Now find the device in the adapter list returned. Loop through all
  6257. // adapters.
  6258. //
  6259. pAdapterInfo = pAdaptersBuffer;
  6260. while (pAdapterInfo != NULL)
  6261. {
  6262. #ifdef DBG
  6263. //
  6264. // Initialize IP address list string.
  6265. //
  6266. szIPList[0] = '\0';
  6267. pszCurrentIP = szIPList;
  6268. #endif // DBG
  6269. //
  6270. // Loop through all addresses for this adapter looking for the one for
  6271. // the device we have bound.
  6272. //
  6273. pIPAddrString = &pAdapterInfo->IpAddressList;
  6274. while (pIPAddrString != NULL)
  6275. {
  6276. #ifdef DBG
  6277. int iStrLen;
  6278. //
  6279. // Copy the IP address string (if there's enough room), then tack
  6280. // on a space and NULL terminator.
  6281. //
  6282. iStrLen = strlen(pIPAddrString->IpAddress.String);
  6283. if ((pszCurrentIP + iStrLen + 2) < (szIPList + sizeof(szIPList)))
  6284. {
  6285. memcpy(pszCurrentIP, pIPAddrString->IpAddress.String, iStrLen);
  6286. pszCurrentIP += iStrLen;
  6287. (*pszCurrentIP) = ' ';
  6288. pszCurrentIP++;
  6289. (*pszCurrentIP) = '\0';
  6290. pszCurrentIP++;
  6291. }
  6292. #endif // DBG
  6293. #ifdef UNICODE
  6294. if (strcmp(pIPAddrString->IpAddress.String, szDeviceIPAddress) == 0)
  6295. #else // ! UNICODE
  6296. if (strcmp(pIPAddrString->IpAddress.String, tszDeviceIPAddress) == 0)
  6297. #endif // ! UNICODE
  6298. {
  6299. DPFX(DPFPREP, 8, "Found %s under adapter index %u (\"%hs\").",
  6300. tszDeviceIPAddress, pAdapterInfo->Index, pAdapterInfo->Description);
  6301. DNASSERT(pszAdapterGuid == NULL);
  6302. pszAdapterGuid = pAdapterInfo->AdapterName;
  6303. //
  6304. // Drop out of the loop in retail, keep going in debug.
  6305. //
  6306. #ifndef DBG
  6307. break;
  6308. #endif // ! DBG
  6309. }
  6310. pIPAddrString = pIPAddrString->Next;
  6311. }
  6312. //
  6313. // Drop out of the loop in retail, print this entry and keep going in
  6314. // debug.
  6315. //
  6316. #ifdef DBG
  6317. DPFX(DPFPREP, 7, "Adapter index %u IPs = %hs, %hs, \"%hs\".",
  6318. pAdapterInfo->Index,
  6319. szIPList,
  6320. pAdapterInfo->AdapterName,
  6321. pAdapterInfo->Description);
  6322. #else // ! DBG
  6323. if (pszAdapterGuid != NULL)
  6324. {
  6325. break;
  6326. }
  6327. #endif // ! DBG
  6328. pAdapterInfo = pAdapterInfo->Next;
  6329. }
  6330. //
  6331. // pszAdapterGuid will be NULL if we never found the device.
  6332. //
  6333. if (pszAdapterGuid == NULL)
  6334. {
  6335. DPFX(DPFPREP, 0, "Did not find adapter with matching address for address %s!",
  6336. tszDeviceIPAddress);
  6337. hr = DPNHERR_GENERIC;
  6338. goto Failure;
  6339. }
  6340. //
  6341. // Copy the adapter GUID string to the buffer supplied.
  6342. //
  6343. #ifdef UNICODE
  6344. STR_jkAnsiToWide(ptszGuidString,
  6345. pszAdapterGuid,
  6346. (GUID_STRING_LENGTH + 1));
  6347. #else // ! UNICODE
  6348. strncpy(ptszGuidString, pszAdapterGuid, (GUID_STRING_LENGTH + 1));
  6349. #endif // ! UNICODE
  6350. ptszGuidString[GUID_STRING_LENGTH] = 0; // ensure it's NULL terminated
  6351. Exit:
  6352. if (pAdaptersBuffer != NULL)
  6353. {
  6354. DNFree(pAdaptersBuffer);
  6355. pAdaptersBuffer = NULL;
  6356. }
  6357. DPFX(DPFPREP, 6, "(0x%p) Returning: [0x%lx]", this, hr);
  6358. return hr;
  6359. Failure:
  6360. goto Exit;
  6361. } // CNATHelpUPnP::GetIPAddressGuidString
  6362. #undef DPF_MODNAME
  6363. #define DPF_MODNAME "CNATHelpUPnP::OpenDevicesUPnPDiscoveryPort"
  6364. //=============================================================================
  6365. // CNATHelpUPnP::OpenDevicesUPnPDiscoveryPort
  6366. //-----------------------------------------------------------------------------
  6367. //
  6368. // Description: Maps the UPnP discovery socket's port if a firewall is
  6369. // found.
  6370. //
  6371. // COM is assumed to have been initialized.
  6372. //
  6373. // The object lock is assumed to be held.
  6374. //
  6375. // Arguments:
  6376. // CDevice * pDevice - Pointer to device whose port should
  6377. // be opened.
  6378. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr interface to
  6379. // use.
  6380. // IHNetConnection * pHNetConnection - Pointer to IHNetConnection interface
  6381. // for the given device.
  6382. //
  6383. // Returns: HRESULT
  6384. // DPNH_OK - Mapping completed successfully. There may or may not
  6385. // be a firewall.
  6386. // DPNHERR_GENERIC - An error occurred.
  6387. //=============================================================================
  6388. HRESULT CNATHelpUPnP::OpenDevicesUPnPDiscoveryPort(CDevice * const pDevice,
  6389. IHNetCfgMgr * const pHNetCfgMgr,
  6390. IHNetConnection * const pHNetConnection)
  6391. {
  6392. HRESULT hr = DPNH_OK;
  6393. CRegisteredPort * pRegisteredPort = NULL;
  6394. SOCKADDR_IN saddrinTemp;
  6395. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p)",
  6396. this, pDevice, pHNetCfgMgr, pHNetConnection);
  6397. DNASSERT(pDevice->IsHNetFirewalled());
  6398. //
  6399. // Create a fake UDP registered port to map.
  6400. //
  6401. pRegisteredPort = new CRegisteredPort(0, 0);
  6402. if (pRegisteredPort == NULL)
  6403. {
  6404. hr = DPNHERR_OUTOFMEMORY;
  6405. goto Failure;
  6406. }
  6407. pRegisteredPort->MakeDeviceOwner(pDevice);
  6408. ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
  6409. saddrinTemp.sin_family = AF_INET;
  6410. saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  6411. saddrinTemp.sin_port = pDevice->GetUPnPDiscoverySocketPort();
  6412. DNASSERT(saddrinTemp.sin_port != 0);
  6413. hr = pRegisteredPort->SetPrivateAddresses(&saddrinTemp, 1);
  6414. if (hr != DPNH_OK)
  6415. {
  6416. DPFX(DPFPREP, 0, "Couldn't set registered port 0x%p's private addresses (err = 0x%lx)!",
  6417. pRegisteredPort, hr);
  6418. goto Failure;
  6419. }
  6420. //
  6421. // Map the port.
  6422. //
  6423. hr = this->MapPortOnLocalHNetFirewall(pRegisteredPort,
  6424. pHNetCfgMgr,
  6425. pHNetConnection,
  6426. FALSE);
  6427. if (hr != DPNH_OK)
  6428. {
  6429. DPFX(DPFPREP, 0, "Couldn't map UPnP discovery socket port (temp regport = 0x%p) on device 0x%p's initial firewall (err = 0x%lx)!",
  6430. pRegisteredPort, pDevice, hr);
  6431. goto Failure;
  6432. }
  6433. //
  6434. // If the port was unavailable, we have to give up on supporting the
  6435. // scenario (firewall enabled behind a NAT). Otherwise, remember the fact
  6436. // that we mapped the port, and then delete the registered port object. We
  6437. // will unmap it when we shut down the device.
  6438. //
  6439. if (! pRegisteredPort->IsHNetFirewallPortUnavailable())
  6440. {
  6441. DPFX(DPFPREP, 3, "Mapped UPnP discovery socket for device 0x%p on firewall (removing temp regport 0x%p).",
  6442. pDevice, pRegisteredPort);
  6443. pDevice->NoteUPnPDiscoverySocketMappedOnHNetFirewall();
  6444. //
  6445. // Clear this to prevent an assert.
  6446. //
  6447. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  6448. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  6449. }
  6450. else
  6451. {
  6452. DPFX(DPFPREP, 1, "Could not map UPnP discovery socket on firewall for device 0x%p, unable to support an upstream NAT.",
  6453. pDevice);
  6454. }
  6455. pRegisteredPort->ClearPrivateAddresses();
  6456. pRegisteredPort->ClearDeviceOwner();
  6457. //
  6458. // Delete the item.
  6459. //
  6460. delete pRegisteredPort;
  6461. pRegisteredPort = NULL;
  6462. Exit:
  6463. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  6464. return hr;
  6465. Failure:
  6466. if (pRegisteredPort != NULL)
  6467. {
  6468. //
  6469. // Clear any settings that might cause an assert.
  6470. //
  6471. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  6472. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  6473. pRegisteredPort->ClearPrivateAddresses();
  6474. pRegisteredPort->ClearDeviceOwner();
  6475. //
  6476. // Delete the item.
  6477. //
  6478. delete pRegisteredPort;
  6479. pRegisteredPort = NULL;
  6480. }
  6481. goto Exit;
  6482. } // CNATHelpUPnP::OpenDevicesUPnPDiscoveryPort
  6483. #undef DPF_MODNAME
  6484. #define DPF_MODNAME "CNATHelpUPnP::CloseDevicesUPnPDiscoveryPort"
  6485. //=============================================================================
  6486. // CNATHelpUPnP::CloseDevicesUPnPDiscoveryPort
  6487. //-----------------------------------------------------------------------------
  6488. //
  6489. // Description: Unmaps the UPnP discovery socket's port from the firewall.
  6490. // pHNetCfgMgr can be NULL if it has not previously been obtained.
  6491. //
  6492. // COM is assumed to have been initialized if pHNetCfgMgr is
  6493. // not NULL.
  6494. //
  6495. // The object lock is assumed to be held.
  6496. //
  6497. // Arguments:
  6498. // CDevice * pDevice - Pointer to device whose port should be
  6499. // close.
  6500. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr interface to use, or
  6501. // NULL if not previously obtained.
  6502. //
  6503. // Returns: HRESULT
  6504. // DPNH_OK - Unmapping completed successfully.
  6505. // DPNHERR_GENERIC - An error occurred.
  6506. //=============================================================================
  6507. HRESULT CNATHelpUPnP::CloseDevicesUPnPDiscoveryPort(CDevice * const pDevice,
  6508. IHNetCfgMgr * const pHNetCfgMgr)
  6509. {
  6510. HRESULT hr = DPNH_OK;
  6511. CRegisteredPort * pRegisteredPort = NULL;
  6512. SOCKADDR_IN saddrinTemp;
  6513. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  6514. this, pDevice, pHNetCfgMgr);
  6515. //
  6516. // Create a fake UDP registered port to unmap.
  6517. //
  6518. pRegisteredPort = new CRegisteredPort(0, 0);
  6519. if (pRegisteredPort == NULL)
  6520. {
  6521. hr = DPNHERR_OUTOFMEMORY;
  6522. goto Failure;
  6523. }
  6524. pRegisteredPort->MakeDeviceOwner(pDevice);
  6525. pRegisteredPort->NoteMappedOnHNetFirewall();
  6526. ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
  6527. saddrinTemp.sin_family = AF_INET;
  6528. saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  6529. saddrinTemp.sin_port = pDevice->GetUPnPDiscoverySocketPort();
  6530. DNASSERT(saddrinTemp.sin_port != 0);
  6531. hr = pRegisteredPort->SetPrivateAddresses(&saddrinTemp, 1);
  6532. if (hr != DPNH_OK)
  6533. {
  6534. DPFX(DPFPREP, 0, "Couldn't set registered port 0x%p's private addresses (err = 0x%lx)!",
  6535. pRegisteredPort, hr);
  6536. goto Failure;
  6537. }
  6538. //
  6539. // Unmap the port using the internal method if possible.
  6540. //
  6541. if (pHNetCfgMgr != NULL)
  6542. {
  6543. hr = this->UnmapPortOnLocalHNetFirewallInternal(pRegisteredPort, TRUE, pHNetCfgMgr);
  6544. }
  6545. else
  6546. {
  6547. //
  6548. // Don't alert the user since he/she doesn't know about this port.
  6549. //
  6550. hr = this->UnmapPortOnLocalHNetFirewall(pRegisteredPort, TRUE, FALSE);
  6551. }
  6552. if (hr != DPNH_OK)
  6553. {
  6554. DPFX(DPFPREP, 0, "Couldn't unmap UPnP discovery socket port (temp regport = 0x%p) on device 0x%p's firewall (err = 0x%lx)!",
  6555. pRegisteredPort, pDevice, hr);
  6556. goto Failure;
  6557. }
  6558. //
  6559. // Destroy the registered port object (note that the port mapping still
  6560. // exists). We will unmap when we shut down the device.
  6561. //
  6562. pRegisteredPort->ClearPrivateAddresses();
  6563. pRegisteredPort->ClearDeviceOwner();
  6564. //
  6565. // Delete the item.
  6566. //
  6567. delete pRegisteredPort;
  6568. pRegisteredPort = NULL;
  6569. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  6570. Exit:
  6571. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  6572. return hr;
  6573. Failure:
  6574. if (pRegisteredPort != NULL)
  6575. {
  6576. //
  6577. // Clear any settings that might cause an assert.
  6578. //
  6579. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  6580. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  6581. pRegisteredPort->ClearPrivateAddresses();
  6582. pRegisteredPort->ClearDeviceOwner();
  6583. //
  6584. // Delete the item.
  6585. //
  6586. delete pRegisteredPort;
  6587. pRegisteredPort = NULL;
  6588. }
  6589. goto Exit;
  6590. } // CNATHelpUPnP::CloseDevicesUPnPDiscoveryPort
  6591. #undef DPF_MODNAME
  6592. #define DPF_MODNAME "CNATHelpUPnP::MapUnmappedPortsOnLocalHNetFirewall"
  6593. //=============================================================================
  6594. // CNATHelpUPnP::MapUnmappedPortsOnLocalHNetFirewall
  6595. //-----------------------------------------------------------------------------
  6596. //
  6597. // Description: Maps any ports associated with the given device that have
  6598. // not been mapped with the local firewall yet.
  6599. //
  6600. // COM is assumed to have been initialized.
  6601. //
  6602. // The object lock is assumed to be held.
  6603. //
  6604. // Arguments:
  6605. // CDevice * pDevice - Device with (new) firewall.
  6606. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr
  6607. // interface to use.
  6608. // IHNetConnection * pHNetConnection - Pointer to IHNetConnection
  6609. // interface for the given
  6610. // device.
  6611. // CRegisteredPort * pDontAlertRegisteredPort - Pointer to registered port
  6612. // that should not trigger an
  6613. // address update alert, or
  6614. // NULL.
  6615. //
  6616. // Returns: HRESULT
  6617. // DPNH_OK - Mapping completed successfully. Note that the ports
  6618. // may be marked as unavailable.
  6619. // DPNHERR_GENERIC - An error occurred.
  6620. //=============================================================================
  6621. HRESULT CNATHelpUPnP::MapUnmappedPortsOnLocalHNetFirewall(CDevice * const pDevice,
  6622. IHNetCfgMgr * const pHNetCfgMgr,
  6623. IHNetConnection * const pHNetConnection,
  6624. CRegisteredPort * const pDontAlertRegisteredPort)
  6625. {
  6626. HRESULT hr = DPNH_OK;
  6627. CBilink * pBilink;
  6628. CRegisteredPort * pRegisteredPort;
  6629. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%p)",
  6630. this, pDevice, pHNetCfgMgr, pHNetConnection, pDontAlertRegisteredPort);
  6631. DNASSERT(pDevice->IsHNetFirewalled());
  6632. //
  6633. // Loop through all the registered ports associated with the device.
  6634. //
  6635. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  6636. while (pBilink != &pDevice->m_blOwnedRegPorts)
  6637. {
  6638. DNASSERT(! pBilink->IsEmpty());
  6639. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  6640. pBilink = pBilink->GetNext();
  6641. //
  6642. // If this port has already been mapped, we can skip it.
  6643. //
  6644. if (pRegisteredPort->IsMappedOnHNetFirewall())
  6645. {
  6646. DPFX(DPFPREP, 7, "Registered port 0x%p has already been mapped on the firewall for device 0x%p.",
  6647. pRegisteredPort, pDevice);
  6648. continue;
  6649. }
  6650. //
  6651. // If this port has already been determined to be unavailable, we can
  6652. // skip it.
  6653. //
  6654. if (pRegisteredPort->IsHNetFirewallPortUnavailable())
  6655. {
  6656. DPFX(DPFPREP, 7, "Registered port 0x%p has already been determined to be unavailable on the firewall for device 0x%p.",
  6657. pRegisteredPort, pDevice);
  6658. continue;
  6659. }
  6660. DPFX(DPFPREP, 3, "Mapping registered port 0x%p on firewall for device 0x%p.",
  6661. pRegisteredPort, pDevice);
  6662. hr = this->MapPortOnLocalHNetFirewall(pRegisteredPort,
  6663. pHNetCfgMgr,
  6664. pHNetConnection,
  6665. ((pRegisteredPort == pDontAlertRegisteredPort) ? FALSE : TRUE));
  6666. if (hr != DPNH_OK)
  6667. {
  6668. DPFX(DPFPREP, 7, "Failed mapping registered port 0x%p on firewall for device 0x%p.",
  6669. pRegisteredPort, pDevice);
  6670. goto Failure;
  6671. }
  6672. //
  6673. // Go to next registered port.
  6674. //
  6675. }
  6676. Exit:
  6677. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  6678. return hr;
  6679. Failure:
  6680. goto Exit;
  6681. } // CNATHelpUPnP::MapUnmappedPortsOnLocalHNetFirewall
  6682. #undef DPF_MODNAME
  6683. #define DPF_MODNAME "CNATHelpUPnP::MapPortOnLocalHNetFirewall"
  6684. //=============================================================================
  6685. // CNATHelpUPnP::MapPortOnLocalHNetFirewall
  6686. //-----------------------------------------------------------------------------
  6687. //
  6688. // Description: Maps the given port with the corresponding firewall.
  6689. //
  6690. // COM is assumed to have been initialized.
  6691. //
  6692. // The object lock is assumed to be held.
  6693. //
  6694. // Arguments:
  6695. // CRegisteredPort * pRegisteredPort - Port to map.
  6696. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr interface to
  6697. // use.
  6698. // IHNetConnection * pHNetConnection - Pointer to IHNetConnection interface
  6699. // for the given device.
  6700. // BOOL fNoteAddressChange - Whether to alert the user of the
  6701. // address change or not.
  6702. //
  6703. // Returns: HRESULT
  6704. // DPNH_OK - Mapping completed successfully. Note that the ports
  6705. // may be marked as unavailable.
  6706. // DPNHERR_GENERIC - An error occurred.
  6707. //=============================================================================
  6708. HRESULT CNATHelpUPnP::MapPortOnLocalHNetFirewall(CRegisteredPort * const pRegisteredPort,
  6709. IHNetCfgMgr * const pHNetCfgMgr,
  6710. IHNetConnection * const pHNetConnection,
  6711. const BOOL fNoteAddressChange)
  6712. {
  6713. HRESULT hr = DPNH_OK;
  6714. CDevice * pDevice;
  6715. SOCKADDR_IN * pasaddrinPrivate;
  6716. UCHAR ucProtocolToMatch;
  6717. ULONG ulNumFound;
  6718. BOOLEAN fTemp;
  6719. IHNetProtocolSettings * pHNetProtocolSettings = NULL;
  6720. IEnumHNetPortMappingProtocols * pEnumHNetPortMappingProtocols = NULL;
  6721. IHNetPortMappingProtocol ** papHNetPortMappingProtocol = NULL;
  6722. DWORD dwTemp;
  6723. BOOL fCreatedCurrentPortMappingProtocol = FALSE;
  6724. IHNetPortMappingBinding * pHNetPortMappingBinding = NULL;
  6725. DWORD dwTargetAddressV4;
  6726. WORD wPort;
  6727. UCHAR ucProtocol;
  6728. DWORD dwDescriptionLength;
  6729. TCHAR tszPort[32];
  6730. CRegistry RegObject;
  6731. WCHAR wszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  6732. DPNHACTIVEFIREWALLMAPPING dpnhafm;
  6733. BOOLEAN fBuiltIn = FALSE;
  6734. WCHAR * pwszPortMappingProtocolName = NULL;
  6735. #ifdef UNICODE
  6736. TCHAR * ptszDescription = wszDescription;
  6737. #else // ! UNICODE
  6738. char szDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  6739. TCHAR * ptszDescription = szDescription;
  6740. #endif // ! UNICODE
  6741. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, %i)",
  6742. this, pRegisteredPort, pHNetCfgMgr, pHNetConnection, fNoteAddressChange);
  6743. DNASSERT(! pRegisteredPort->IsMappedOnHNetFirewall());
  6744. DNASSERT(! pRegisteredPort->IsHNetFirewallPortUnavailable());
  6745. pDevice = pRegisteredPort->GetOwningDevice();
  6746. DNASSERT(pDevice != NULL);
  6747. DNASSERT(pDevice->IsHNetFirewalled());
  6748. //
  6749. // Get a protocol settings interface.
  6750. //
  6751. hr = pHNetCfgMgr->QueryInterface(IID_IHNetProtocolSettings,
  6752. (PVOID*) (&pHNetProtocolSettings));
  6753. if (hr != S_OK)
  6754. {
  6755. DPFX(DPFPREP, 0, "Couldn't get IHNetProtocolSettings interface from IHNetCfgMgr 0x%p (err = 0x%lx)!",
  6756. pHNetCfgMgr, hr);
  6757. goto Failure;
  6758. }
  6759. //
  6760. // The HNetxxx objects appear to not be proxied...
  6761. //
  6762. //SETDEFAULTPROXYBLANKET(pHNetProtocolSettings);
  6763. //
  6764. // Get ready to enumerate the existing mappings.
  6765. //
  6766. hr = pHNetProtocolSettings->EnumPortMappingProtocols(&pEnumHNetPortMappingProtocols);
  6767. if (hr != S_OK)
  6768. {
  6769. DPFX(DPFPREP, 0, "Couldn't enumerate port mapping protocols (err = 0x%lx)!",
  6770. hr);
  6771. goto Failure;
  6772. }
  6773. //
  6774. // The HNetxxx objects appear to not be proxied...
  6775. //
  6776. //SETDEFAULTPROXYBLANKET(pEnumHNetPortMappingProtocols);
  6777. //
  6778. // Allocate an array to keep track of previous ports in case of failure.
  6779. //
  6780. papHNetPortMappingProtocol = (IHNetPortMappingProtocol**) DNMalloc(DPNH_MAX_SIMULTANEOUS_PORTS * sizeof(IHNetPortMappingProtocol*));
  6781. if (papHNetPortMappingProtocol == NULL)
  6782. {
  6783. hr = DPNHERR_OUTOFMEMORY;
  6784. goto Failure;
  6785. }
  6786. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  6787. if (pRegisteredPort->IsTCP())
  6788. {
  6789. ucProtocolToMatch = PORTMAPPINGPROTOCOL_TCP;
  6790. }
  6791. else
  6792. {
  6793. ucProtocolToMatch = PORTMAPPINGPROTOCOL_UDP;
  6794. }
  6795. //
  6796. // Map each individual address associated with the port.
  6797. //
  6798. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  6799. {
  6800. DNASSERT(pasaddrinPrivate[dwTemp].sin_port != 0);
  6801. //
  6802. // Loop until we find a duplicate item or run out of items.
  6803. //
  6804. do
  6805. {
  6806. hr = pEnumHNetPortMappingProtocols->Next(1,
  6807. &papHNetPortMappingProtocol[dwTemp],
  6808. &ulNumFound);
  6809. if (FAILED(hr))
  6810. {
  6811. DPFX(DPFPREP, 0, "Couldn't get next port mapping protocol (err = 0x%lx)!",
  6812. hr);
  6813. goto Failure;
  6814. }
  6815. //
  6816. // If there aren't any more items, bail.
  6817. //
  6818. if (ulNumFound == 0)
  6819. {
  6820. //
  6821. // pEnumHNetPortMappingProtocols->Next might have returned
  6822. // S_FALSE.
  6823. //
  6824. hr = DPNH_OK;
  6825. break;
  6826. }
  6827. //
  6828. // The HNetxxx objects appear to not be proxied...
  6829. //
  6830. //SETDEFAULTPROXYBLANKET(papHNetPortMappingProtocol[dwTemp]);
  6831. //
  6832. // Get the port.
  6833. //
  6834. hr = papHNetPortMappingProtocol[dwTemp]->GetPort(&wPort);
  6835. if (hr != S_OK)
  6836. {
  6837. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol 0x%p's port (err = 0x%lx)!",
  6838. papHNetPortMappingProtocol[dwTemp], hr);
  6839. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetPort!"), 2);
  6840. goto Failure;
  6841. }
  6842. //
  6843. // Get the protocol.
  6844. //
  6845. hr = papHNetPortMappingProtocol[dwTemp]->GetIPProtocol(&ucProtocol);
  6846. if (hr != S_OK)
  6847. {
  6848. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol 0x%p's IP protocol (err = 0x%lx)!",
  6849. papHNetPortMappingProtocol[dwTemp], hr);
  6850. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetIPProtocol!"), 2);
  6851. goto Failure;
  6852. }
  6853. #ifdef DBG
  6854. hr = papHNetPortMappingProtocol[dwTemp]->GetName(&pwszPortMappingProtocolName);
  6855. if (hr == S_OK)
  6856. {
  6857. DPFX(DPFPREP, 7, "Found %s port mapping protocol 0x%p (\"%ls\") for port %u.",
  6858. (((wPort == pasaddrinPrivate[dwTemp].sin_port) && (ucProtocol == ucProtocolToMatch)) ? _T("matching") : _T("non-matching")),
  6859. papHNetPortMappingProtocol[dwTemp],
  6860. pwszPortMappingProtocolName,
  6861. NTOHS(wPort));
  6862. CoTaskMemFree(pwszPortMappingProtocolName);
  6863. pwszPortMappingProtocolName = NULL;
  6864. }
  6865. else
  6866. {
  6867. DPFX(DPFPREP, 7, "Found %s port mapping protocol 0x%p for port %u, (unable to retrieve name, err = %0lx).",
  6868. (((wPort == pasaddrinPrivate[dwTemp].sin_port) && (ucProtocol == ucProtocolToMatch)) ? _T("matching") : _T("non-matching")),
  6869. NTOHS(wPort),
  6870. papHNetPortMappingProtocol[dwTemp],
  6871. hr);
  6872. }
  6873. #endif // DBG
  6874. //
  6875. // See if we found the object we need.
  6876. //
  6877. if ((wPort == pasaddrinPrivate[dwTemp].sin_port) &&
  6878. (ucProtocol == ucProtocolToMatch))
  6879. {
  6880. break;
  6881. }
  6882. //
  6883. // Get ready for the next object.
  6884. //
  6885. papHNetPortMappingProtocol[dwTemp]->Release();
  6886. papHNetPortMappingProtocol[dwTemp] = NULL;
  6887. }
  6888. while (TRUE);
  6889. //
  6890. // Generate a description for this mapping. The format is:
  6891. //
  6892. // [executable_name] nnnnn {"TCP" | "UDP"}
  6893. //
  6894. // unless it's shared, in which case it's
  6895. //
  6896. // [executable_name] (255.255.255.255:nnnnn) nnnnn {"TCP" | "UDP"}
  6897. //
  6898. // That way nothing needs to be localized.
  6899. //
  6900. wsprintf(tszPort, _T("%u"),
  6901. NTOHS(pasaddrinPrivate[dwTemp].sin_port));
  6902. dwDescriptionLength = GetModuleFileName(NULL,
  6903. ptszDescription,
  6904. (MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1));
  6905. if (dwDescriptionLength != 0)
  6906. {
  6907. //
  6908. // Be paranoid and make sure the description string is valid.
  6909. //
  6910. ptszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1] = 0;
  6911. //
  6912. // Get just the executable name from the path.
  6913. //
  6914. #ifdef WINCE
  6915. GetExeName(ptszDescription);
  6916. #else // ! WINCE
  6917. #ifdef UNICODE
  6918. _wsplitpath(ptszDescription, NULL, NULL, ptszDescription, NULL);
  6919. #else // ! UNICODE
  6920. _splitpath(ptszDescription, NULL, NULL, ptszDescription, NULL);
  6921. #endif // ! UNICODE
  6922. #endif // ! WINCE
  6923. if (pRegisteredPort->IsSharedPort())
  6924. {
  6925. dwDescriptionLength = _tcslen(ptszDescription) // executable name
  6926. + strlen(" (255.255.255.255:") // " (255.255.255.255:"
  6927. + _tcslen(tszPort) // port
  6928. + strlen(") ") // ") "
  6929. + _tcslen(tszPort) // port
  6930. + 4; // " TCP" | " UDP"
  6931. }
  6932. else
  6933. {
  6934. dwDescriptionLength = _tcslen(ptszDescription) // executable name
  6935. + 1 // " "
  6936. +_tcslen(tszPort) // port
  6937. + 4; // " TCP" | " UDP"
  6938. }
  6939. //
  6940. // Make sure the long string will fit. If not, use the
  6941. // abbreviated version.
  6942. //
  6943. if (dwDescriptionLength > MAX_UPNP_MAPPING_DESCRIPTION_SIZE)
  6944. {
  6945. dwDescriptionLength = 0;
  6946. }
  6947. }
  6948. if (dwDescriptionLength == 0)
  6949. {
  6950. //
  6951. // Use the abbreviated version we know will fit.
  6952. //
  6953. if (pRegisteredPort->IsSharedPort())
  6954. {
  6955. wsprintf(ptszDescription,
  6956. _T("(255.255.255.255:%s) %s %s"),
  6957. tszPort,
  6958. tszPort,
  6959. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  6960. }
  6961. else
  6962. {
  6963. wsprintf(ptszDescription,
  6964. _T("%s %s"),
  6965. tszPort,
  6966. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  6967. }
  6968. }
  6969. else
  6970. {
  6971. //
  6972. // There's enough room, tack on the rest of the description.
  6973. //
  6974. if (pRegisteredPort->IsSharedPort())
  6975. {
  6976. wsprintf((ptszDescription + _tcslen(ptszDescription)),
  6977. _T(" (255.255.255.255:%s) %s %s"),
  6978. tszPort,
  6979. tszPort,
  6980. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  6981. }
  6982. else
  6983. {
  6984. wsprintf((ptszDescription + _tcslen(ptszDescription)),
  6985. _T(" %s %s"),
  6986. tszPort,
  6987. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  6988. }
  6989. }
  6990. #ifndef UNICODE
  6991. dwDescriptionLength = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  6992. hr = STR_AnsiToWide(szDescription, -1, wszDescription, &dwDescriptionLength);
  6993. if (hr != S_OK)
  6994. {
  6995. DPFX(DPFPREP, 0, "Couldn't convert NAT mapping description to Unicode (err = 0x%lx)!",
  6996. hr);
  6997. goto Failure;
  6998. }
  6999. #endif // ! UNICODE
  7000. //
  7001. // If there wasn't a port mapping already, create it. Otherwise make
  7002. // sure it's not already in use by some other client.
  7003. //
  7004. if (papHNetPortMappingProtocol[dwTemp] == NULL)
  7005. {
  7006. DPFX(DPFPREP, 7, "Creating new port mapping protocol \"%ls\".",
  7007. wszDescription);
  7008. //
  7009. // Create a new port mapping protocol.
  7010. //
  7011. DPFX(DPFPREP, 9, "++ pHNetProtocolSettings(0x%p)->CreatePortMappingProtocol(\"%ls\", %u, 0x%lx, 0x%p)", pHNetProtocolSettings, wszDescription, ucProtocolToMatch, pasaddrinPrivate[dwTemp].sin_port, &papHNetPortMappingProtocol[dwTemp]);
  7012. hr = pHNetProtocolSettings->CreatePortMappingProtocol(wszDescription,
  7013. ucProtocolToMatch,
  7014. pasaddrinPrivate[dwTemp].sin_port,
  7015. &papHNetPortMappingProtocol[dwTemp]);
  7016. DPFX(DPFPREP, 9, "-- pHNetProtocolSettings(0x%p)->CreatePortMappingProtocol = 0x%lx", pHNetProtocolSettings, hr);
  7017. if (hr != S_OK)
  7018. {
  7019. //
  7020. // This might be WBEM_E_ACCESSDENIED (0x80041003), which means
  7021. // the current user doesn't have permissions to open holes in
  7022. // the firewall.
  7023. //
  7024. DPFX(DPFPREP, 0, "Couldn't create new port mapping protocol (err = 0x%lx)!",
  7025. hr);
  7026. goto Failure;
  7027. }
  7028. //
  7029. // The HNetxxx objects appear to not be proxied...
  7030. //
  7031. //SETDEFAULTPROXYBLANKET(papHNetPortMappingProtocol[dwTemp]);
  7032. fCreatedCurrentPortMappingProtocol = TRUE;
  7033. //
  7034. // Retrieve its binding.
  7035. //
  7036. DPFX(DPFPREP, 9, "++ pHNetConnection(0x%p)->GetBindingForPortMappingProtocol(0x%p, 0x%p)", pHNetConnection, papHNetPortMappingProtocol[dwTemp], &pHNetPortMappingBinding);
  7037. hr = pHNetConnection->GetBindingForPortMappingProtocol(papHNetPortMappingProtocol[dwTemp],
  7038. &pHNetPortMappingBinding);
  7039. DPFX(DPFPREP, 9, "-- pHNetConnection(0x%p)->GetBindingForPortMappingProtocol = 0x%lx", pHNetConnection, hr);
  7040. if (hr != S_OK)
  7041. {
  7042. DPFX(DPFPREP, 0, "Couldn't get binding for port mapping protocol 0x%p (err = 0x%lx)!",
  7043. papHNetPortMappingProtocol[dwTemp], hr);
  7044. goto Failure;
  7045. }
  7046. //
  7047. // The HNetxxx objects appear to not be proxied...
  7048. //
  7049. //SETDEFAULTPROXYBLANKET(pHNetPortMappingBinding);
  7050. //
  7051. // Make sure it refers to the local device (or the broadcast
  7052. // address, if shared). Although shared ports are a strange
  7053. // concept on a firewall, Microsoft's firewall implementation
  7054. // shares mappings with the NAT, so we'd rather be safe than sorry.
  7055. // Mapping it to the broadcast address makes it behave the same if
  7056. // the firewalled adapter also happens to be shared.
  7057. //
  7058. if (pRegisteredPort->IsSharedPort())
  7059. {
  7060. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress((broadcast) 0x%lx)", pHNetPortMappingBinding, INADDR_BROADCAST);
  7061. hr = pHNetPortMappingBinding->SetTargetComputerAddress(INADDR_BROADCAST);
  7062. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress = 0x%lx", pHNetPortMappingBinding, hr);
  7063. }
  7064. else
  7065. {
  7066. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress(0x%lx)", pHNetPortMappingBinding, pDevice->GetLocalAddressV4());
  7067. hr = pHNetPortMappingBinding->SetTargetComputerAddress(pDevice->GetLocalAddressV4());
  7068. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress = 0x%lx", pHNetPortMappingBinding, hr);
  7069. }
  7070. if (hr != S_OK)
  7071. {
  7072. DPFX(DPFPREP, 0, "Couldn't set binding 0x%p's target computer address (err = 0x%lx)!",
  7073. pHNetPortMappingBinding, hr);
  7074. goto Failure;
  7075. }
  7076. }
  7077. else
  7078. {
  7079. //
  7080. // Retrieve the existing binding.
  7081. //
  7082. DPFX(DPFPREP, 9, "++ pHNetConnection(0x%p)->GetBindingForPortMappingProtocol(0x%p, 0x%p)", pHNetConnection, papHNetPortMappingProtocol[dwTemp], &pHNetPortMappingBinding);
  7083. hr = pHNetConnection->GetBindingForPortMappingProtocol(papHNetPortMappingProtocol[dwTemp],
  7084. &pHNetPortMappingBinding);
  7085. DPFX(DPFPREP, 9, "-- pHNetConnection(0x%p)->GetBindingForPortMappingProtocol = 0x%lx", pHNetConnection, hr);
  7086. if (hr != S_OK)
  7087. {
  7088. DPFX(DPFPREP, 0, "Couldn't get binding for port mapping protocol 0x%p (err = 0x%lx)!",
  7089. papHNetPortMappingProtocol[dwTemp], hr);
  7090. goto Failure;
  7091. }
  7092. //
  7093. // The HNetxxx objects appear to not be proxied...
  7094. //
  7095. //SETDEFAULTPROXYBLANKET(pHNetPortMappingBinding);
  7096. //
  7097. // Find out where this mapping goes.
  7098. //
  7099. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->GetTargetComputerAddress(0x%p)", pHNetPortMappingBinding, &dwTargetAddressV4);
  7100. hr = pHNetPortMappingBinding->GetTargetComputerAddress(&dwTargetAddressV4);
  7101. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->GetTargetComputerAddress = 0x%lx", pHNetPortMappingBinding, hr);
  7102. if (hr != S_OK)
  7103. {
  7104. DPFX(DPFPREP, 0, "Couldn't get binding 0x%p's target computer address (err = 0x%lx)!",
  7105. pHNetPortMappingBinding, hr);
  7106. goto Failure;
  7107. }
  7108. //
  7109. // If it's not for the local device, we may have to leave it alone.
  7110. //
  7111. if ((dwTargetAddressV4 != pDevice->GetLocalAddressV4()) &&
  7112. ((! pRegisteredPort->IsSharedPort()) ||
  7113. (dwTargetAddressV4 != INADDR_BROADCAST)))
  7114. {
  7115. //
  7116. // Find out if it's turned on.
  7117. //
  7118. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->GetEnabled(0x%p)", pHNetPortMappingBinding, &fTemp);
  7119. hr = pHNetPortMappingBinding->GetEnabled(&fTemp);
  7120. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->GetEnabled = 0x%lx", pHNetPortMappingBinding, hr);
  7121. if (hr != S_OK)
  7122. {
  7123. DPFX(DPFPREP, 0, "Couldn't get binding 0x%p's target computer address (err = 0x%lx)!",
  7124. pHNetPortMappingBinding, hr);
  7125. goto Failure;
  7126. }
  7127. //
  7128. // If it's currently active, it's better to be safe than sorry.
  7129. // Don't attempt to replace it.
  7130. //
  7131. if (fTemp)
  7132. {
  7133. DPFX(DPFPREP, 1, "Existing active binding points to different target %u.%u.%u.%u, can't reuse for device 0x%p.",
  7134. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b1,
  7135. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b2,
  7136. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b3,
  7137. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b4,
  7138. pDevice);
  7139. //
  7140. // Mark this port as unavailable.
  7141. //
  7142. pRegisteredPort->NoteHNetFirewallPortUnavailable();
  7143. //
  7144. // Cleanup this port mapping.
  7145. //
  7146. pHNetPortMappingBinding->Release();
  7147. pHNetPortMappingBinding = NULL;
  7148. papHNetPortMappingProtocol[dwTemp]->Release();
  7149. papHNetPortMappingProtocol[dwTemp] = NULL;
  7150. //
  7151. // Reset for next port.
  7152. //
  7153. DPFX(DPFPREP, 9, "++ pEnumHNetPortMappingProtocols(0x%p)->Reset()", pEnumHNetPortMappingProtocols);
  7154. hr = pEnumHNetPortMappingProtocols->Reset();
  7155. DPFX(DPFPREP, 9, "-- pEnumHNetPortMappingProtocols(0x%p)->Reset = 0x%lx", pEnumHNetPortMappingProtocols, hr);
  7156. if (hr != S_OK)
  7157. {
  7158. DPFX(DPFPREP, 0, "Couldn't reset port mapping protocol enumeration 0x%p (err = 0x%lx)!",
  7159. pEnumHNetPortMappingProtocols, hr);
  7160. goto Failure;
  7161. }
  7162. //
  7163. // Get out of the loop.
  7164. //
  7165. break;
  7166. }
  7167. //
  7168. // It's inactive.
  7169. //
  7170. DPFX(DPFPREP, 7, "Modifying inactive port mapping protocol (target was %u.%u.%u.%u) for device 0x%p (new name = \"%ls\").",
  7171. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b1,
  7172. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b2,
  7173. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b3,
  7174. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b4,
  7175. pDevice,
  7176. wszDescription);
  7177. }
  7178. else
  7179. {
  7180. //
  7181. // It matches the local device, or we're mapping a shared port
  7182. // and the mapping pointed to the broadcast address.
  7183. // Assume it's okay to replace.
  7184. //
  7185. DPFX(DPFPREP, 7, "Modifying existing port mapping protocol (device = 0x%p, new name = \"%ls\" unless built-in).",
  7186. pDevice,
  7187. wszDescription);
  7188. }
  7189. //
  7190. // Otherwise, it's safe to change it.
  7191. //
  7192. //
  7193. // Make sure it refers to the local device (or the broadcast
  7194. // address, if shared). Although shared ports are a strange
  7195. // concept on a firewall, Microsoft's firewall implementation
  7196. // shares mappings with the NAT, so we'd rather be safe than sorry.
  7197. // Mapping it to the broadcast address makes it behave the same if
  7198. // the firewalled adapter also happens to be shared.
  7199. //
  7200. if (pRegisteredPort->IsSharedPort())
  7201. {
  7202. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress((broadcast) 0x%lx)", pHNetPortMappingBinding, INADDR_BROADCAST);
  7203. hr = pHNetPortMappingBinding->SetTargetComputerAddress(INADDR_BROADCAST);
  7204. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress = 0x%lx", pHNetPortMappingBinding, hr);
  7205. }
  7206. else
  7207. {
  7208. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress(0x%lx)", pHNetPortMappingBinding, pDevice->GetLocalAddressV4());
  7209. hr = pHNetPortMappingBinding->SetTargetComputerAddress(pDevice->GetLocalAddressV4());
  7210. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress = 0x%lx", pHNetPortMappingBinding, hr);
  7211. }
  7212. if (hr != S_OK)
  7213. {
  7214. DPFX(DPFPREP, 0, "Couldn't set binding 0x%p's target computer address (err = 0x%lx)!",
  7215. pHNetPortMappingBinding, hr);
  7216. goto Failure;
  7217. }
  7218. //
  7219. // See if this protocol is built-in.
  7220. //
  7221. DPFX(DPFPREP, 9, "++ papHNetPortMappingProtocol[%u](0x%p)->GetBuiltIn(0x%p)", dwTemp, papHNetPortMappingProtocol[dwTemp], &fBuiltIn);
  7222. hr = papHNetPortMappingProtocol[dwTemp]->GetBuiltIn(&fBuiltIn);
  7223. DPFX(DPFPREP, 9, "-- papHNetPortMappingProtocol[%u](0x%p)->GetBuiltIn = 0x%lx", dwTemp, papHNetPortMappingProtocol[dwTemp], hr);
  7224. if (hr != S_OK)
  7225. {
  7226. DPFX(DPFPREP, 0, "Couldn't get protocol 0x%p's built-in status (err = 0x%lx)!",
  7227. papHNetPortMappingProtocol[dwTemp], hr);
  7228. goto Failure;
  7229. }
  7230. //
  7231. // If it's not built-in, we can change the name.
  7232. //
  7233. if (! fBuiltIn)
  7234. {
  7235. //
  7236. // Update the description.
  7237. //
  7238. DPFX(DPFPREP, 9, "++ papHNetPortMappingProtocol[%u](0x%p)->SetName(\"%ls\")", dwTemp, papHNetPortMappingProtocol[dwTemp], wszDescription);
  7239. hr = papHNetPortMappingProtocol[dwTemp]->SetName(wszDescription);
  7240. DPFX(DPFPREP, 9, "-- papHNetPortMappingProtocol[%u](0x%p)->SetName = 0x%lx", dwTemp, papHNetPortMappingProtocol[dwTemp], hr);
  7241. if (hr != S_OK)
  7242. {
  7243. //
  7244. // This might be WBEM_E_ACCESSDENIED (0x80041003), which
  7245. // means the current user doesn't truly have permissions to
  7246. // open holes in the firewall (even though the
  7247. // SetTargetComputerAddress call above succeeded).
  7248. //
  7249. DPFX(DPFPREP, 0, "Couldn't rename existing port mapping protocol 0x%p (err = 0x%lx)!",
  7250. papHNetPortMappingProtocol[dwTemp], hr);
  7251. goto Failure;
  7252. }
  7253. }
  7254. else
  7255. {
  7256. pRegisteredPort->NoteHNetFirewallMappingBuiltIn();
  7257. DPFX(DPFPREP, 9, "++ papHNetPortMappingProtocol[%u](0x%p)->GetName(0x%p)", dwTemp, papHNetPortMappingProtocol[dwTemp], &pwszPortMappingProtocolName);
  7258. hr = papHNetPortMappingProtocol[dwTemp]->GetName(&pwszPortMappingProtocolName);
  7259. DPFX(DPFPREP, 9, "-- papHNetPortMappingProtocol[%u](0x%p)->GetName = 0x%lx", dwTemp, papHNetPortMappingProtocol[dwTemp], hr);
  7260. if (hr != S_OK)
  7261. {
  7262. DPFX(DPFPREP, 0, "Couldn't get built-in port mapping protocol 0x%p's name (err = 0x%lx)!",
  7263. papHNetPortMappingProtocol[dwTemp], hr);
  7264. goto Failure;
  7265. }
  7266. DPFX(DPFPREP, 1, "Re-using built in port mapping protocol \"%ls\" (can't rename to \"%ls\").",
  7267. pwszPortMappingProtocolName, wszDescription);
  7268. }
  7269. } // end else (found port mapping protocol)
  7270. //
  7271. // Enable the binding.
  7272. //
  7273. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->SetEnabled(TRUE)", pHNetPortMappingBinding);
  7274. hr = pHNetPortMappingBinding->SetEnabled(TRUE);
  7275. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->SetEnabled = 0x%lx", pHNetPortMappingBinding, hr);
  7276. if (hr != S_OK)
  7277. {
  7278. //
  7279. // This might be WBEM_E_ACCESSDENIED (0x80041003), which means the
  7280. // current user doesn't truly have permissions to open holes in the
  7281. // firewall (even though the SetTargetComputerAddress call above
  7282. // succeeded).
  7283. //
  7284. DPFX(DPFPREP, 0, "Couldn't enable binding 0x%p (err = 0x%lx)!",
  7285. pHNetPortMappingBinding, hr);
  7286. goto Failure;
  7287. }
  7288. //
  7289. // Remember this firewall mapping, in case we crash before cleaning it
  7290. // up in this session. That we can clean it up next time we launch.
  7291. // Don't do this if the port is shared, since we can't tell when it's
  7292. // no longer in use.
  7293. //
  7294. if (! pRegisteredPort->IsSharedPort())
  7295. {
  7296. if (fBuiltIn)
  7297. {
  7298. DPFX(DPFPREP, 7, "Remembering built-in firewall mapping \"%ls\" (a.k.a. \"%ls\") in case of crash.",
  7299. pwszPortMappingProtocolName, wszDescription);
  7300. }
  7301. else
  7302. {
  7303. DPFX(DPFPREP, 7, "Remembering regular firewall mapping \"%ls\" in case of crash.",
  7304. wszDescription);
  7305. }
  7306. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  7307. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVEFIREWALLMAPPINGS,
  7308. FALSE,
  7309. TRUE,
  7310. TRUE,
  7311. DPN_KEY_ALL_ACCESS))
  7312. {
  7313. DPFX(DPFPREP, 0, "Couldn't open active firewall mapping key, unable to save in case of crash!");
  7314. }
  7315. else
  7316. {
  7317. DNASSERT(this->m_dwInstanceKey != 0);
  7318. ZeroMemory(&dpnhafm, sizeof(dpnhafm));
  7319. dpnhafm.dwVersion = ACTIVE_MAPPING_VERSION;
  7320. dpnhafm.dwInstanceKey = this->m_dwInstanceKey;
  7321. dpnhafm.dwFlags = pRegisteredPort->GetFlags();
  7322. dpnhafm.dwAddressV4 = pDevice->GetLocalAddressV4();
  7323. dpnhafm.wPort = pasaddrinPrivate[dwTemp].sin_port;
  7324. //
  7325. // If it's built-in, use its existing name since it couldn't be
  7326. // renamed. This allows the unmapping code to find it in the
  7327. // registry again. See UnmapPortOnLocalHNetFirewallInternal.
  7328. //
  7329. RegObject.WriteBlob(((fBuiltIn) ? pwszPortMappingProtocolName : wszDescription),
  7330. (LPBYTE) (&dpnhafm),
  7331. sizeof(dpnhafm));
  7332. RegObject.Close();
  7333. }
  7334. }
  7335. else
  7336. {
  7337. DPFX(DPFPREP, 7, "Not remembering shared port firewall mapping \"%ls\".",
  7338. wszDescription);
  7339. }
  7340. //
  7341. // Cleanup from this port mapping, and get ready for the next one.
  7342. //
  7343. if (fBuiltIn)
  7344. {
  7345. CoTaskMemFree(pwszPortMappingProtocolName);
  7346. pwszPortMappingProtocolName = NULL;
  7347. }
  7348. pHNetPortMappingBinding->Release();
  7349. pHNetPortMappingBinding = NULL;
  7350. fCreatedCurrentPortMappingProtocol = FALSE;
  7351. hr = pEnumHNetPortMappingProtocols->Reset();
  7352. if (hr != S_OK)
  7353. {
  7354. DPFX(DPFPREP, 0, "Couldn't reset port mapping protocol enumeration 0x%p (err = 0x%lx)!",
  7355. pEnumHNetPortMappingProtocols, hr);
  7356. goto Failure;
  7357. }
  7358. //
  7359. // Alert the user to the change the next time GetCaps is called, if
  7360. // requested.
  7361. //
  7362. if (fNoteAddressChange)
  7363. {
  7364. DPFX(DPFPREP, 8, "Noting that addresses changed (for registered port 0x%p).",
  7365. pRegisteredPort);
  7366. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  7367. }
  7368. //
  7369. // Go on to the next port.
  7370. //
  7371. }
  7372. //
  7373. // dwTemp == pRegisteredPort->GetNumAddresses() if everything succeeded, or
  7374. // or the index of the item that was unavailable if not.
  7375. //
  7376. //
  7377. // Free all the port mapping protocol objects. If we successfully bound
  7378. // all of them, that's all we need to do. If the port was unavailable, we
  7379. // have to unmap any ports that were successful up to the one that failed.
  7380. //
  7381. while (dwTemp > 0)
  7382. {
  7383. dwTemp--;
  7384. //
  7385. // If we failed to map all ports, delete this previous mapping.
  7386. //
  7387. if (pRegisteredPort->IsHNetFirewallPortUnavailable())
  7388. {
  7389. papHNetPortMappingProtocol[dwTemp]->Delete(); // ignore error
  7390. }
  7391. //
  7392. // Free the object.
  7393. //
  7394. papHNetPortMappingProtocol[dwTemp]->Release();
  7395. papHNetPortMappingProtocol[dwTemp] = NULL;
  7396. }
  7397. //
  7398. // If we succeeded, mark the registered port as mapped.
  7399. //
  7400. if (! pRegisteredPort->IsHNetFirewallPortUnavailable())
  7401. {
  7402. pRegisteredPort->NoteMappedOnHNetFirewall();
  7403. }
  7404. DNFree(papHNetPortMappingProtocol);
  7405. papHNetPortMappingProtocol = NULL;
  7406. DNASSERT(hr == DPNH_OK);
  7407. Exit:
  7408. if (pEnumHNetPortMappingProtocols != NULL)
  7409. {
  7410. pEnumHNetPortMappingProtocols->Release();
  7411. pEnumHNetPortMappingProtocols = NULL;
  7412. }
  7413. if (pHNetProtocolSettings != NULL)
  7414. {
  7415. pHNetProtocolSettings->Release();
  7416. pHNetProtocolSettings = NULL;
  7417. }
  7418. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  7419. return hr;
  7420. Failure:
  7421. if (pwszPortMappingProtocolName != NULL)
  7422. {
  7423. CoTaskMemFree(pwszPortMappingProtocolName);
  7424. pwszPortMappingProtocolName = NULL;
  7425. }
  7426. //
  7427. // If we have an array, then we need to clean it up. dwTemp will still
  7428. // hold the index of the item we were working on.
  7429. //
  7430. if (papHNetPortMappingProtocol != NULL)
  7431. {
  7432. //
  7433. // Delete the one we were working on, if we created it.
  7434. //
  7435. if (papHNetPortMappingProtocol[dwTemp] != NULL)
  7436. {
  7437. if (fCreatedCurrentPortMappingProtocol)
  7438. {
  7439. papHNetPortMappingProtocol[dwTemp]->Delete(); // ignore error
  7440. }
  7441. papHNetPortMappingProtocol[dwTemp]->Release();
  7442. papHNetPortMappingProtocol[dwTemp] = NULL;
  7443. }
  7444. //
  7445. // Delete all the mappings we successfully made up to the last one.
  7446. //
  7447. while (dwTemp > 0)
  7448. {
  7449. dwTemp--;
  7450. DNASSERT(papHNetPortMappingProtocol[dwTemp] != NULL);
  7451. papHNetPortMappingProtocol[dwTemp]->Delete(); // ignore error
  7452. papHNetPortMappingProtocol[dwTemp]->Release();
  7453. papHNetPortMappingProtocol[dwTemp] = NULL;
  7454. }
  7455. DNFree(papHNetPortMappingProtocol);
  7456. papHNetPortMappingProtocol = NULL;
  7457. }
  7458. if (pHNetPortMappingBinding != NULL)
  7459. {
  7460. pHNetPortMappingBinding->Release();
  7461. pHNetPortMappingBinding = NULL;
  7462. }
  7463. goto Exit;
  7464. } // CNATHelpUPnP::MapPortOnLocalHNetFirewall
  7465. #undef DPF_MODNAME
  7466. #define DPF_MODNAME "CNATHelpUPnP::UnmapPortOnLocalHNetFirewall"
  7467. //=============================================================================
  7468. // CNATHelpUPnP::UnmapPortOnLocalHNetFirewall
  7469. //-----------------------------------------------------------------------------
  7470. //
  7471. // Description: Removes the mappings for the given ports from the local
  7472. // firewall.
  7473. //
  7474. // The main object lock is assumed to be held. It will be
  7475. // converted into the long lock for the duration of this function.
  7476. //
  7477. // Arguments:
  7478. // CRegisteredPort * pRegisteredPort - Pointer to port to be opened on the
  7479. // firewall.
  7480. // BOOL fNeedToDeleteRegValue - Whether the corresponding crash
  7481. // recovery registry value needs to
  7482. // be deleted as well.
  7483. // BOOL fNoteAddressChange - Whether to alert the user of the
  7484. // address change or not.
  7485. //
  7486. // Returns: HRESULT
  7487. // DPNH_OK - Unmapping completed successfully.
  7488. // DPNHERR_GENERIC - An error occurred.
  7489. //=============================================================================
  7490. HRESULT CNATHelpUPnP::UnmapPortOnLocalHNetFirewall(CRegisteredPort * const pRegisteredPort,
  7491. const BOOL fNeedToDeleteRegValue,
  7492. const BOOL fNoteAddressChange)
  7493. {
  7494. HRESULT hr = DPNH_OK;
  7495. BOOL fSwitchedToLongLock = FALSE;
  7496. BOOL fUninitializeCOM = FALSE;
  7497. IHNetCfgMgr * pHNetCfgMgr = NULL;
  7498. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i, %i)",
  7499. this, pRegisteredPort, fNeedToDeleteRegValue, fNoteAddressChange);
  7500. DNASSERT(pRegisteredPort->IsMappedOnHNetFirewall());
  7501. //
  7502. // If the port is shared, leave it mapped since we can't tell when the
  7503. // last person using it is done with it.
  7504. //
  7505. if (pRegisteredPort->IsSharedPort())
  7506. {
  7507. DPFX(DPFPREP, 2, "Leaving shared registered port 0x%p mapped.",
  7508. pRegisteredPort);
  7509. //
  7510. // Pretend like we unmapped it, though.
  7511. //
  7512. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  7513. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  7514. goto Exit;
  7515. }
  7516. //
  7517. // Using the HomeNet API (particularly the out-of-proc COM calls) during
  7518. // stress is really, really, painfully slow. Since we have one global lock
  7519. // the controls everything, other threads may be sitting for an equally
  7520. // long time... so long, in fact, that the critical section timeout fires
  7521. // and we get a false stress hit. So we have a sneaky workaround to
  7522. // prevent that from happening while still maintaining ownership of the
  7523. // object.
  7524. //
  7525. this->SwitchToLongLock();
  7526. fSwitchedToLongLock = TRUE;
  7527. //
  7528. // Try to initialize COM if we weren't instantiated through COM. It may
  7529. // have already been initialized in a different mode, which is okay. As
  7530. // long as it has been initialized somehow, we're fine.
  7531. //
  7532. if (this->m_dwFlags & NATHELPUPNPOBJ_NOTCREATEDWITHCOM)
  7533. {
  7534. hr = CoInitializeEx(NULL, (COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE));
  7535. switch (hr)
  7536. {
  7537. case S_OK:
  7538. {
  7539. //
  7540. // Success, that's good. Cleanup when we're done.
  7541. //
  7542. DPFX(DPFPREP, 8, "Successfully initialized COM.");
  7543. fUninitializeCOM = TRUE;
  7544. break;
  7545. }
  7546. case S_FALSE:
  7547. {
  7548. //
  7549. // Someone else already initialized COM, but that's okay.
  7550. // Cleanup when we're done.
  7551. //
  7552. DPFX(DPFPREP, 8, "Initialized COM (again).");
  7553. fUninitializeCOM = TRUE;
  7554. break;
  7555. }
  7556. case RPC_E_CHANGED_MODE:
  7557. {
  7558. //
  7559. // Someone else already initialized COM in a different mode.
  7560. // It should be okay, but we don't have to balance the CoInit
  7561. // call with a CoUninit.
  7562. //
  7563. DPFX(DPFPREP, 8, "Didn't initialize COM, already initialized in a different mode.");
  7564. break;
  7565. }
  7566. default:
  7567. {
  7568. //
  7569. // Hmm, something else is going on. We can't handle that.
  7570. //
  7571. DPFX(DPFPREP, 0, "Initializing COM failed (err = 0x%lx)!", hr);
  7572. goto Failure;
  7573. break;
  7574. }
  7575. }
  7576. }
  7577. else
  7578. {
  7579. DPFX(DPFPREP, 8, "Object was instantiated through COM, no need to initialize COM.");
  7580. }
  7581. //
  7582. // Create the main HNet manager object.
  7583. //
  7584. hr = CoCreateInstance(CLSID_HNetCfgMgr, NULL, CLSCTX_INPROC_SERVER,
  7585. IID_IHNetCfgMgr, (PVOID*) (&pHNetCfgMgr));
  7586. if (hr != S_OK)
  7587. {
  7588. DPFX(DPFPREP, 1, "Couldn't create IHNetCfgMgr interface (err = 0x%lx)!",
  7589. hr);
  7590. goto Failure;
  7591. }
  7592. //
  7593. // We created the IHNetCfgMgr object as in-proc, so there's no proxy that
  7594. // requires security settings.
  7595. //
  7596. //SETDEFAULTPROXYBLANKET(pHNetCfgMgr);
  7597. //
  7598. // Actually unmap the port(s).
  7599. //
  7600. hr = this->UnmapPortOnLocalHNetFirewallInternal(pRegisteredPort,
  7601. fNeedToDeleteRegValue,
  7602. pHNetCfgMgr);
  7603. if (hr != DPNH_OK)
  7604. {
  7605. DPFX(DPFPREP, 0, "Couldn't unmap ports from local HNet firewall (err = 0x%lx)!",
  7606. hr);
  7607. goto Failure;
  7608. }
  7609. //
  7610. // Alert the user to the change the next time GetCaps is called, if requested.
  7611. //
  7612. if (fNoteAddressChange)
  7613. {
  7614. DPFX(DPFPREP, 8, "Noting that addresses changed (for registered port 0x%p).",
  7615. pRegisteredPort);
  7616. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  7617. }
  7618. Exit:
  7619. if (pHNetCfgMgr != NULL)
  7620. {
  7621. pHNetCfgMgr->Release();
  7622. pHNetCfgMgr = NULL;
  7623. }
  7624. if (fUninitializeCOM)
  7625. {
  7626. DPFX(DPFPREP, 8, "Uninitializing COM.");
  7627. CoUninitialize();
  7628. fUninitializeCOM = FALSE;
  7629. }
  7630. if (fSwitchedToLongLock)
  7631. {
  7632. this->SwitchFromLongLock();
  7633. fSwitchedToLongLock = FALSE;
  7634. }
  7635. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  7636. return hr;
  7637. Failure:
  7638. goto Exit;
  7639. } // CNATHelpUPnP::UnmapPortOnLocalHNetFirewall
  7640. #undef DPF_MODNAME
  7641. #define DPF_MODNAME "CNATHelpUPnP::UnmapPortOnLocalHNetFirewallInternal"
  7642. //=============================================================================
  7643. // CNATHelpUPnP::UnmapPortOnLocalHNetFirewallInternal
  7644. //-----------------------------------------------------------------------------
  7645. //
  7646. // Description: Removes the mappings for the given ports from the local
  7647. // firewall.
  7648. //
  7649. // COM is assumed to have been initialized.
  7650. //
  7651. // The object lock is assumed to be held.
  7652. //
  7653. // Arguments:
  7654. // CRegisteredPort * pRegisteredPort - Pointer to port to be opened on the
  7655. // firewall.
  7656. // BOOL fNeedToDeleteRegValue - Whether the corresponding crash
  7657. // recovery registry value needs to
  7658. // be deleted as well.
  7659. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr interface to
  7660. // use.
  7661. //
  7662. // Returns: HRESULT
  7663. // DPNH_OK - Unmapping completed successfully.
  7664. // DPNHERR_GENERIC - An error occurred.
  7665. //=============================================================================
  7666. HRESULT CNATHelpUPnP::UnmapPortOnLocalHNetFirewallInternal(CRegisteredPort * const pRegisteredPort,
  7667. const BOOL fNeedToDeleteRegValue,
  7668. IHNetCfgMgr * const pHNetCfgMgr)
  7669. {
  7670. HRESULT hr = DPNH_OK;
  7671. CDevice * pDevice;
  7672. DWORD dwAttempts = 0;
  7673. IHNetProtocolSettings * pHNetProtocolSettings = NULL;
  7674. IEnumHNetPortMappingProtocols * pEnumHNetPortMappingProtocols = NULL;
  7675. SOCKADDR_IN * pasaddrinPrivate;
  7676. UCHAR ucProtocolToMatch;
  7677. IHNetPortMappingProtocol * pHNetPortMappingProtocol = NULL;
  7678. DWORD dwStartingPort = 0;
  7679. DWORD dwTemp;
  7680. ULONG ulNumFound;
  7681. WORD wPort;
  7682. UCHAR ucProtocol;
  7683. WCHAR * pwszName = NULL;
  7684. BOOLEAN fBuiltIn;
  7685. CRegistry RegObject;
  7686. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i, 0x%p)",
  7687. this, pRegisteredPort, fNeedToDeleteRegValue, pHNetCfgMgr);
  7688. DNASSERT(pRegisteredPort->IsMappedOnHNetFirewall());
  7689. pDevice = pRegisteredPort->GetOwningDevice();
  7690. DNASSERT(pDevice != NULL);
  7691. DNASSERT(pDevice->IsHNetFirewalled());
  7692. DNASSERT(this->m_hIpHlpApiDLL != NULL);
  7693. Restart:
  7694. //
  7695. // Get a protocol settings interface.
  7696. //
  7697. hr = pHNetCfgMgr->QueryInterface(IID_IHNetProtocolSettings,
  7698. (PVOID*) (&pHNetProtocolSettings));
  7699. if (hr != S_OK)
  7700. {
  7701. DPFX(DPFPREP, 0, "Couldn't get IHNetProtocolSettings interface from IHNetCfgMgr 0x%p (err = 0x%lx)!",
  7702. pHNetCfgMgr, hr);
  7703. goto Failure;
  7704. }
  7705. //
  7706. // The HNetxxx objects appear to not be proxied...
  7707. //
  7708. //SETDEFAULTPROXYBLANKET(pHNetProtocolSettings);
  7709. //
  7710. // Get ready to enumerate the existing mappings.
  7711. //
  7712. hr = pHNetProtocolSettings->EnumPortMappingProtocols(&pEnumHNetPortMappingProtocols);
  7713. if (hr != S_OK)
  7714. {
  7715. DPFX(DPFPREP, 0, "Couldn't enumerate port mapping protocols (err = 0x%lx)!",
  7716. hr);
  7717. goto Failure;
  7718. }
  7719. //
  7720. // The HNetxxx objects appear to not be proxied...
  7721. //
  7722. //SETDEFAULTPROXYBLANKET(pEnumHNetPortMappingProtocols);
  7723. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  7724. if (pRegisteredPort->IsTCP())
  7725. {
  7726. ucProtocolToMatch = PORTMAPPINGPROTOCOL_TCP;
  7727. }
  7728. else
  7729. {
  7730. ucProtocolToMatch = PORTMAPPINGPROTOCOL_UDP;
  7731. }
  7732. //
  7733. // Loop through all the ports (that we haven't successfully unmapped yet).
  7734. //
  7735. for(dwTemp = dwStartingPort; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  7736. {
  7737. //
  7738. // Loop until we find a duplicate item or run out of items.
  7739. //
  7740. do
  7741. {
  7742. hr = pEnumHNetPortMappingProtocols->Next(1,
  7743. &pHNetPortMappingProtocol,
  7744. &ulNumFound);
  7745. if (FAILED(hr))
  7746. {
  7747. dwAttempts++;
  7748. if (dwAttempts < MAX_NUM_HOMENETUNMAP_ATTEMPTS)
  7749. {
  7750. DPFX(DPFPREP, 0, "Couldn't get next port mapping protocol (err = 0x%lx)! Trying again after %u ms.",
  7751. hr, (dwAttempts * HOMENETUNMAP_SLEEP_FACTOR));
  7752. //
  7753. // Dump the object pointers we currently have.
  7754. //
  7755. pEnumHNetPortMappingProtocols->Release();
  7756. pEnumHNetPortMappingProtocols = NULL;
  7757. pHNetProtocolSettings->Release();
  7758. pHNetProtocolSettings = NULL;
  7759. //
  7760. // Sleep, then go back to the top and try again.
  7761. //
  7762. Sleep(dwAttempts * HOMENETUNMAP_SLEEP_FACTOR);
  7763. goto Restart;
  7764. }
  7765. DPFX(DPFPREP, 0, "Couldn't get next port mapping protocol (err = 0x%lx)!",
  7766. hr);
  7767. goto Failure;
  7768. }
  7769. //
  7770. // If there aren't any more items, bail.
  7771. //
  7772. if (ulNumFound == 0)
  7773. {
  7774. //
  7775. // Be sure that IEnumHNetPortMappingProtocols::Next returned
  7776. // the right thing, for PREfix's sake.
  7777. //
  7778. if (pHNetPortMappingProtocol != NULL)
  7779. {
  7780. pHNetPortMappingProtocol->Release();
  7781. pHNetPortMappingProtocol = NULL;
  7782. }
  7783. //
  7784. // pEnumHNetPortMappingProtocols->Next might have returned
  7785. // S_FALSE.
  7786. //
  7787. hr = DPNH_OK;
  7788. break;
  7789. }
  7790. //
  7791. // The HNetxxx objects appear to not be proxied...
  7792. //
  7793. //SETDEFAULTPROXYBLANKET(pHNetPortMappingProtocol);
  7794. //
  7795. // Get the port.
  7796. //
  7797. hr = pHNetPortMappingProtocol->GetPort(&wPort);
  7798. if (hr != S_OK)
  7799. {
  7800. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetPort!"), 2);
  7801. //
  7802. // Dump the unusable mapping object.
  7803. //
  7804. pHNetPortMappingProtocol->Release();
  7805. pHNetPortMappingProtocol = NULL;
  7806. dwAttempts++;
  7807. if (dwAttempts < MAX_NUM_HOMENETUNMAP_ATTEMPTS)
  7808. {
  7809. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol port (err = 0x%lx)! Trying again after %u ms.",
  7810. hr, (dwAttempts * HOMENETUNMAP_SLEEP_FACTOR));
  7811. //
  7812. // Dump the object pointers we currently have.
  7813. //
  7814. pEnumHNetPortMappingProtocols->Release();
  7815. pEnumHNetPortMappingProtocols = NULL;
  7816. pHNetProtocolSettings->Release();
  7817. pHNetProtocolSettings = NULL;
  7818. //
  7819. // Sleep, then go back to the top and try again.
  7820. //
  7821. Sleep(dwAttempts * HOMENETUNMAP_SLEEP_FACTOR);
  7822. goto Restart;
  7823. }
  7824. //
  7825. // Break out of the search loop, but continue.
  7826. //
  7827. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol port (err = 0x%lx)!",
  7828. hr);
  7829. break;
  7830. }
  7831. //
  7832. // Get the protocol.
  7833. //
  7834. hr = pHNetPortMappingProtocol->GetIPProtocol(&ucProtocol);
  7835. if (hr != S_OK)
  7836. {
  7837. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetIPProtocol!"), 2);
  7838. //
  7839. // Dump the unusable mapping object.
  7840. //
  7841. pHNetPortMappingProtocol->Release();
  7842. pHNetPortMappingProtocol = NULL;
  7843. dwAttempts++;
  7844. if (dwAttempts < MAX_NUM_HOMENETUNMAP_ATTEMPTS)
  7845. {
  7846. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's IP protocol (err = 0x%lx)! Trying again after %u ms.",
  7847. hr, (dwAttempts * HOMENETUNMAP_SLEEP_FACTOR));
  7848. //
  7849. // Dump the object pointers we currently have.
  7850. //
  7851. pEnumHNetPortMappingProtocols->Release();
  7852. pEnumHNetPortMappingProtocols = NULL;
  7853. pHNetProtocolSettings->Release();
  7854. pHNetProtocolSettings = NULL;
  7855. //
  7856. // Sleep, then go back to the top and try again.
  7857. //
  7858. Sleep(dwAttempts * HOMENETUNMAP_SLEEP_FACTOR);
  7859. goto Restart;
  7860. }
  7861. //
  7862. // Break out of the search loop, but continue.
  7863. //
  7864. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's IP protocol (err = 0x%lx)!",
  7865. hr);
  7866. break;
  7867. }
  7868. //
  7869. // See if we found the object we need. Note that we don't verify
  7870. // the target address for simplicity (neither does UPnP).
  7871. //
  7872. if ((wPort == pasaddrinPrivate[dwTemp].sin_port) &&
  7873. (ucProtocol == ucProtocolToMatch))
  7874. {
  7875. //
  7876. // Retrieve the mapping name.
  7877. //
  7878. hr = pHNetPortMappingProtocol->GetName(&pwszName);
  7879. if (hr != S_OK)
  7880. {
  7881. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetName!"), 2);
  7882. //
  7883. // Dump the unusable mapping object.
  7884. //
  7885. pHNetPortMappingProtocol->Release();
  7886. pHNetPortMappingProtocol = NULL;
  7887. dwAttempts++;
  7888. if (dwAttempts < MAX_NUM_HOMENETUNMAP_ATTEMPTS)
  7889. {
  7890. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's name (err = 0x%lx)! Trying again after %u ms.",
  7891. hr, (dwAttempts * HOMENETUNMAP_SLEEP_FACTOR));
  7892. //
  7893. // Dump the object pointers we currently have.
  7894. //
  7895. pEnumHNetPortMappingProtocols->Release();
  7896. pEnumHNetPortMappingProtocols = NULL;
  7897. pHNetProtocolSettings->Release();
  7898. pHNetProtocolSettings = NULL;
  7899. //
  7900. // Sleep, then go back to the top and try again.
  7901. //
  7902. Sleep(dwAttempts * HOMENETUNMAP_SLEEP_FACTOR);
  7903. goto Restart;
  7904. }
  7905. //
  7906. // Break out of the search loop, but continue.
  7907. //
  7908. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's name (err = 0x%lx)!",
  7909. hr);
  7910. break;
  7911. }
  7912. DPFX(DPFPREP, 8, "Found port mapping protocol 0x%p (\"%ls\").",
  7913. pHNetPortMappingProtocol, pwszName);
  7914. //
  7915. // See if this protocol is built-in.
  7916. //
  7917. hr = pHNetPortMappingProtocol->GetBuiltIn(&fBuiltIn);
  7918. if (hr != S_OK)
  7919. {
  7920. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetBuiltIn!"), 2);
  7921. //
  7922. // Dump the unusable mapping object and its name.
  7923. //
  7924. pHNetPortMappingProtocol->Release();
  7925. pHNetPortMappingProtocol = NULL;
  7926. CoTaskMemFree(pwszName);
  7927. pwszName = NULL;
  7928. dwAttempts++;
  7929. if (dwAttempts < MAX_NUM_HOMENETUNMAP_ATTEMPTS)
  7930. {
  7931. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's built-in status (err = 0x%lx)! Trying again after %u ms.",
  7932. hr, (dwAttempts * HOMENETUNMAP_SLEEP_FACTOR));
  7933. //
  7934. // Dump the object pointers we currently have.
  7935. //
  7936. pEnumHNetPortMappingProtocols->Release();
  7937. pEnumHNetPortMappingProtocols = NULL;
  7938. pHNetProtocolSettings->Release();
  7939. pHNetProtocolSettings = NULL;
  7940. //
  7941. // Sleep, then go back to the top and try again.
  7942. //
  7943. Sleep(dwAttempts * HOMENETUNMAP_SLEEP_FACTOR);
  7944. goto Restart;
  7945. }
  7946. //
  7947. // Break out of the search loop, but continue.
  7948. //
  7949. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's built-in status (err = 0x%lx)!",
  7950. hr);
  7951. break;
  7952. }
  7953. break;
  7954. }
  7955. #ifdef DBG
  7956. else
  7957. {
  7958. //
  7959. // Try to retrieve the mapping name for informational purposes.
  7960. //
  7961. hr = pHNetPortMappingProtocol->GetName(&pwszName);
  7962. if (hr != S_OK)
  7963. {
  7964. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol 0x%p's name (err = 0x%lx)!",
  7965. pHNetPortMappingProtocol, hr);
  7966. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetName!"), 2);
  7967. //
  7968. // Ignore error...
  7969. //
  7970. }
  7971. else
  7972. {
  7973. DPFX(DPFPREP, 7, "Skipping non-matching port mapping protocol 0x%p (\"%ls\").",
  7974. pHNetPortMappingProtocol, pwszName);
  7975. CoTaskMemFree(pwszName);
  7976. pwszName = NULL;
  7977. }
  7978. }
  7979. #endif // DBG
  7980. //
  7981. // Get ready for the next object.
  7982. //
  7983. pHNetPortMappingProtocol->Release();
  7984. pHNetPortMappingProtocol = NULL;
  7985. }
  7986. while (TRUE);
  7987. //
  7988. // Remove the mapping (if we found it).
  7989. //
  7990. if (pHNetPortMappingProtocol != NULL)
  7991. {
  7992. //
  7993. // If the mapping is built-in we can't delete it. Disabling it is
  7994. // the best we can do.
  7995. //
  7996. if (fBuiltIn)
  7997. {
  7998. DPFX(DPFPREP, 7, "Disabling built-in port mapping protocol \"%ls\".", pwszName);
  7999. DNASSERT(pRegisteredPort->IsHNetFirewallMappingBuiltIn());
  8000. hr = this->DisableAllBindingsForHNetPortMappingProtocol(pHNetPortMappingProtocol,
  8001. pHNetCfgMgr);
  8002. if (hr != DPNH_OK)
  8003. {
  8004. DPFX(DPFPREP, 0, "Couldn't disable all bindings for built-in port mapping protocol \"%ls\" (err = 0x%lx)!",
  8005. pwszName, hr);
  8006. goto Failure;
  8007. }
  8008. }
  8009. else
  8010. {
  8011. DPFX(DPFPREP, 7, "Deleting port mapping protocol \"%ls\".", pwszName);
  8012. DNASSERT(! pRegisteredPort->IsHNetFirewallMappingBuiltIn());
  8013. hr = pHNetPortMappingProtocol->Delete();
  8014. if (hr != S_OK)
  8015. {
  8016. //
  8017. // This might be WBEM_E_ACCESSDENIED (0x80041003), which
  8018. // means the current user doesn't have permissions to
  8019. // modify firewall mappings.
  8020. //
  8021. DPFX(DPFPREP, 0, "Couldn't delete port mapping protocol (err = 0x%lx)!",
  8022. hr);
  8023. goto Failure;
  8024. }
  8025. }
  8026. if (fNeedToDeleteRegValue)
  8027. {
  8028. //
  8029. // Delete the crash cleanup registry entry. The mapping
  8030. // description/name will match the registry key name even in
  8031. // the case of built-in mappings with names we didn't generate.
  8032. // See MapPortOnLocalHNetFirewall.
  8033. //
  8034. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  8035. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVEFIREWALLMAPPINGS,
  8036. FALSE,
  8037. TRUE,
  8038. TRUE,
  8039. DPN_KEY_ALL_ACCESS))
  8040. {
  8041. DPFX(DPFPREP, 0, "Couldn't open active firewall mapping key, unable to remove crash cleanup reference!");
  8042. }
  8043. else
  8044. {
  8045. BOOL fResult;
  8046. //
  8047. // Ignore error.
  8048. //
  8049. fResult = RegObject.DeleteValue(pwszName);
  8050. if (! fResult)
  8051. {
  8052. DPFX(DPFPREP, 0, "Couldn't delete firewall mapping value \"%ls\"! Continuing.",
  8053. pwszName);
  8054. }
  8055. RegObject.Close();
  8056. }
  8057. }
  8058. else
  8059. {
  8060. DPFX(DPFPREP, 6, "No need to delete firewall crash cleanup registry key \"%ls\".", pwszName);
  8061. }
  8062. //
  8063. // Cleanup pointers we accumulated.
  8064. //
  8065. CoTaskMemFree(pwszName);
  8066. pwszName = NULL;
  8067. pHNetPortMappingProtocol->Release();
  8068. pHNetPortMappingProtocol = NULL;
  8069. }
  8070. else
  8071. {
  8072. //
  8073. // We didn't find the mapping.
  8074. //
  8075. DPFX(DPFPREP, 0, "Didn't find port mapping protocol for port %u %s! Continuing.",
  8076. NTOHS(pasaddrinPrivate[dwTemp].sin_port),
  8077. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  8078. }
  8079. //
  8080. // Cleanup from this port mapping, and get ready for the next one.
  8081. //
  8082. hr = pEnumHNetPortMappingProtocols->Reset();
  8083. if (hr != S_OK)
  8084. {
  8085. DPFX(DPFPREP, 0, "Couldn't reset port mapping protocol enumeration 0x%p (err = 0x%lx)!",
  8086. pEnumHNetPortMappingProtocols, hr);
  8087. goto Failure;
  8088. }
  8089. //
  8090. // Go on to the next port, and update the starting counter in case we
  8091. // encounter a failure next time.
  8092. //
  8093. dwStartingPort++;
  8094. }
  8095. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  8096. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  8097. DNASSERT(hr == DPNH_OK);
  8098. Exit:
  8099. if (pHNetPortMappingProtocol != NULL)
  8100. {
  8101. pHNetPortMappingProtocol->Release();
  8102. pHNetPortMappingProtocol = NULL;
  8103. }
  8104. if (pEnumHNetPortMappingProtocols != NULL)
  8105. {
  8106. pEnumHNetPortMappingProtocols->Release();
  8107. pEnumHNetPortMappingProtocols = NULL;
  8108. }
  8109. if (pHNetProtocolSettings != NULL)
  8110. {
  8111. pHNetProtocolSettings->Release();
  8112. pHNetProtocolSettings = NULL;
  8113. }
  8114. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  8115. return hr;
  8116. Failure:
  8117. if (pwszName != NULL)
  8118. {
  8119. CoTaskMemFree(pwszName);
  8120. pwszName = NULL;
  8121. }
  8122. goto Exit;
  8123. } // CNATHelpUPnP::UnmapPortOnLocalHNetFirewallInternal
  8124. #undef DPF_MODNAME
  8125. #define DPF_MODNAME "CNATHelpUPnP::DisableAllBindingsForHNetPortMappingProtocol"
  8126. //=============================================================================
  8127. // CNATHelpUPnP::DisableAllBindingsForHNetPortMappingProtocol
  8128. //-----------------------------------------------------------------------------
  8129. //
  8130. // Description: Disables all HNetPortMappingBindings on all HNetConnection
  8131. // interfaces for the given port mapping protocol object.
  8132. //
  8133. // COM is assumed to have been initialized.
  8134. //
  8135. // The object lock is assumed to be held.
  8136. //
  8137. // Arguments:
  8138. // IHNetPortMappingProtocol * pHNetPortMappingProtocol - Pointer to port
  8139. // mapping
  8140. // protocol to
  8141. // disable on all
  8142. // connections.
  8143. // IHNetCfgMgr * pHNetCfgMgr - Pointer to
  8144. // IHNetCfgMgr
  8145. // interface to
  8146. // use.
  8147. //
  8148. // Returns: HRESULT
  8149. // DPNH_OK - Disabling was successful.
  8150. // DPNHERR_GENERIC - An error occurred.
  8151. //=============================================================================
  8152. HRESULT CNATHelpUPnP::DisableAllBindingsForHNetPortMappingProtocol(IHNetPortMappingProtocol * const pHNetPortMappingProtocol,
  8153. IHNetCfgMgr * const pHNetCfgMgr)
  8154. {
  8155. HRESULT hr;
  8156. INetConnectionManager * pNetConnectionManager = NULL;
  8157. IEnumNetConnection * pEnumNetConnections = NULL;
  8158. ULONG ulNumFound;
  8159. INetConnection * pNetConnection = NULL;
  8160. IHNetConnection * pHNetConnection = NULL;
  8161. IHNetPortMappingBinding * pHNetPortMappingBinding = NULL;
  8162. #ifdef DBG
  8163. WCHAR * pwszName = NULL;
  8164. #endif // DBG
  8165. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  8166. this, pHNetPortMappingProtocol, pHNetCfgMgr);
  8167. //
  8168. // Try creating the base connection object.
  8169. //
  8170. hr = CoCreateInstance(CLSID_ConnectionManager,
  8171. NULL,
  8172. CLSCTX_SERVER,
  8173. IID_INetConnectionManager,
  8174. (PVOID*) (&pNetConnectionManager));
  8175. if (hr != S_OK)
  8176. {
  8177. DPFX(DPFPREP, 0, "Couldn't create INetConnectionManager interface (err = 0x%lx)!",
  8178. hr);
  8179. goto Failure;
  8180. }
  8181. SETDEFAULTPROXYBLANKET(pNetConnectionManager);
  8182. DPFX(DPFPREP, 7, "Successfully created net connection manager object 0x%p.",
  8183. pNetConnectionManager);
  8184. //
  8185. // Get the net connection enumeration object.
  8186. //
  8187. hr = pNetConnectionManager->EnumConnections(NCME_DEFAULT, &pEnumNetConnections);
  8188. if (hr != S_OK)
  8189. {
  8190. DPFX(DPFPREP, 0, "Couldn't enum connections (err = 0x%lx)!",
  8191. hr);
  8192. goto Failure;
  8193. }
  8194. SETDEFAULTPROXYBLANKET(pEnumNetConnections);
  8195. //
  8196. // We don't need the base object anymore.
  8197. //
  8198. pNetConnectionManager->Release();
  8199. pNetConnectionManager = NULL;
  8200. //
  8201. // Keep looping until we find the item or run out of items.
  8202. //
  8203. do
  8204. {
  8205. hr = pEnumNetConnections->Next(1, &pNetConnection, &ulNumFound);
  8206. if (FAILED(hr))
  8207. {
  8208. DPFX(DPFPREP, 0, "Couldn't get next connection (err = 0x%lx)!",
  8209. hr);
  8210. goto Failure;
  8211. }
  8212. //
  8213. // If there aren't any more items, bail.
  8214. //
  8215. if (ulNumFound == 0)
  8216. {
  8217. //
  8218. // pEnumNetConnections->Next might have returned S_FALSE.
  8219. //
  8220. hr = DPNH_OK;
  8221. break;
  8222. }
  8223. SETDEFAULTPROXYBLANKET(pNetConnection);
  8224. //
  8225. // Get the HNetConnection object for this NetConnection.
  8226. //
  8227. hr = pHNetCfgMgr->GetIHNetConnectionForINetConnection(pNetConnection,
  8228. &pHNetConnection);
  8229. if (hr != S_OK)
  8230. {
  8231. DPFX(DPFPREP, 0, "Couldn't get IHNetConnection interface for INetConnection 0x%p (err = 0x%lx)!",
  8232. pNetConnection, hr);
  8233. goto Failure;
  8234. }
  8235. //
  8236. // The HNetxxx objects appear to not be proxied...
  8237. //
  8238. //SETDEFAULTPROXYBLANKET(pHNetConnection);
  8239. //
  8240. // Don't need the INetConnection interface anymore.
  8241. //
  8242. pNetConnection->Release();
  8243. pNetConnection = NULL;
  8244. #ifdef DBG
  8245. //
  8246. // Retrieve the connection name, for debug printing purposes.
  8247. //
  8248. hr = pHNetConnection->GetName(&pwszName);
  8249. if (hr != S_OK)
  8250. {
  8251. DPFX(DPFPREP, 0, "Couldn't get name of HNetConnection 0x%p (err = 0x%lx)!",
  8252. pHNetConnection, hr);
  8253. goto Failure;
  8254. }
  8255. #endif // DBG
  8256. //
  8257. // Retrieve the existing binding.
  8258. //
  8259. hr = pHNetConnection->GetBindingForPortMappingProtocol(pHNetPortMappingProtocol,
  8260. &pHNetPortMappingBinding);
  8261. if (hr != S_OK)
  8262. {
  8263. DPFX(DPFPREP, 0, "Couldn't get binding for port mapping protocol 0x%p (err = 0x%lx)!",
  8264. pHNetPortMappingProtocol, hr);
  8265. goto Failure;
  8266. }
  8267. //
  8268. // The HNetxxx objects appear to not be proxied...
  8269. //
  8270. //SETDEFAULTPROXYBLANKET(pHNetPortMappingBinding);
  8271. //
  8272. // Don't need the HomeNet Connection object anymore.
  8273. //
  8274. pHNetConnection->Release();
  8275. pHNetConnection = NULL;
  8276. DPFX(DPFPREP, 6, "Disabling binding 0x%p on connection \"%ls\".",
  8277. pHNetPortMappingBinding, pwszName);
  8278. //
  8279. // Disable it.
  8280. //
  8281. hr = pHNetPortMappingBinding->SetEnabled(FALSE);
  8282. if (hr != S_OK)
  8283. {
  8284. DPFX(DPFPREP, 0, "Couldn't disable port mapping binding 0x%p (err = 0x%lx)!",
  8285. pHNetPortMappingBinding, hr);
  8286. goto Failure;
  8287. }
  8288. pHNetPortMappingBinding->Release();
  8289. pHNetPortMappingBinding = NULL;
  8290. //
  8291. // Go to the next mapping.
  8292. //
  8293. #ifdef DBG
  8294. CoTaskMemFree(pwszName);
  8295. pwszName = NULL;
  8296. #endif // DBG
  8297. }
  8298. while (TRUE);
  8299. //
  8300. // If we're here, we made it through unscathed.
  8301. //
  8302. hr = DPNH_OK;
  8303. Exit:
  8304. if (pEnumNetConnections != NULL)
  8305. {
  8306. pEnumNetConnections->Release();
  8307. pEnumNetConnections = NULL;
  8308. }
  8309. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  8310. return hr;
  8311. Failure:
  8312. if (pHNetPortMappingBinding != NULL)
  8313. {
  8314. pHNetPortMappingBinding->Release();
  8315. pHNetPortMappingBinding = NULL;
  8316. }
  8317. #ifdef DBG
  8318. if (pwszName != NULL)
  8319. {
  8320. CoTaskMemFree(pwszName);
  8321. pwszName = NULL;
  8322. }
  8323. #endif // DBG
  8324. if (pHNetConnection != NULL)
  8325. {
  8326. pHNetConnection->Release();
  8327. pHNetConnection = NULL;
  8328. }
  8329. if (pNetConnection != NULL)
  8330. {
  8331. pNetConnection->Release();
  8332. pNetConnection = NULL;
  8333. }
  8334. if (pNetConnectionManager != NULL)
  8335. {
  8336. pNetConnectionManager->Release();
  8337. pNetConnectionManager = NULL;
  8338. }
  8339. goto Exit;
  8340. } // CNATHelpUPnP::DisableAllBindingsForHNetPortMappingProtocol
  8341. #undef DPF_MODNAME
  8342. #define DPF_MODNAME "CNATHelpUPnP::CleanupInactiveFirewallMappings"
  8343. //=============================================================================
  8344. // CNATHelpUPnP::CleanupInactiveFirewallMappings
  8345. //-----------------------------------------------------------------------------
  8346. //
  8347. // Description: Looks for any mappings previously made by other DPNATHLP
  8348. // instances that are no longer active (because of a crash), and
  8349. // unmaps them.
  8350. //
  8351. // COM is assumed to have been initialized.
  8352. //
  8353. // The object lock is assumed to be held.
  8354. //
  8355. // Arguments:
  8356. // CDevice * pDevice - Pointer to device to use.
  8357. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr interface to
  8358. // use.
  8359. //
  8360. // Returns: HRESULT
  8361. // DPNH_OK - The cleanup was successful.
  8362. // DPNHERR_GENERIC - An error occurred.
  8363. //=============================================================================
  8364. HRESULT CNATHelpUPnP::CleanupInactiveFirewallMappings(CDevice * const pDevice,
  8365. IHNetCfgMgr * const pHNetCfgMgr)
  8366. {
  8367. HRESULT hr = DPNH_OK;
  8368. CRegistry RegObject;
  8369. BOOL fOpenedRegistry = FALSE;
  8370. DWORD dwIndex;
  8371. WCHAR wszValueName[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  8372. DWORD dwValueNameSize;
  8373. DPNHACTIVEFIREWALLMAPPING dpnhafm;
  8374. DWORD dwValueSize;
  8375. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  8376. DNHANDLE hNamedObject = NULL;
  8377. CRegisteredPort * pRegisteredPort = NULL;
  8378. BOOL fSetPrivateAddresses = FALSE;
  8379. SOCKADDR_IN saddrinPrivate;
  8380. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  8381. this, pDevice, pHNetCfgMgr);
  8382. DNASSERT(pDevice != NULL);
  8383. DNASSERT(pDevice->IsHNetFirewalled());
  8384. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  8385. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVEFIREWALLMAPPINGS,
  8386. FALSE,
  8387. TRUE,
  8388. TRUE,
  8389. DPN_KEY_ALL_ACCESS))
  8390. {
  8391. DPFX(DPFPREP, 1, "Couldn't open active firewall mapping key, not performing crash cleanup.");
  8392. DNASSERT(hr == DPNH_OK);
  8393. goto Exit;
  8394. }
  8395. fOpenedRegistry = TRUE;
  8396. //
  8397. // Walk the list of active mappings.
  8398. //
  8399. dwIndex = 0;
  8400. do
  8401. {
  8402. dwValueNameSize = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  8403. if (! RegObject.EnumValues(wszValueName, &dwValueNameSize, dwIndex))
  8404. {
  8405. //
  8406. // There was an error or there aren't any more keys. We're done.
  8407. //
  8408. break;
  8409. }
  8410. //
  8411. // Try reading that mapping's data.
  8412. //
  8413. dwValueSize = sizeof(dpnhafm);
  8414. if (! RegObject.ReadBlob(wszValueName, (LPBYTE) (&dpnhafm), &dwValueSize))
  8415. {
  8416. //
  8417. // We don't have a lock protecting the registry, so some other
  8418. // instance could have deleted the key between when we enumerated
  8419. // it and now. We'll stop trying (and hopefully that other
  8420. // instance will cover the rest of the items).
  8421. //
  8422. DPFX(DPFPREP, 0, "Couldn't read \"%ls\" mapping value! Done with cleanup.",
  8423. wszValueName);
  8424. DNASSERT(hr == DPNH_OK);
  8425. goto Exit;
  8426. }
  8427. //
  8428. // Validate the data read.
  8429. //
  8430. if ((dwValueSize != sizeof(dpnhafm)) ||
  8431. (dpnhafm.dwVersion != ACTIVE_MAPPING_VERSION))
  8432. {
  8433. DPFX(DPFPREP, 0, "The \"%ls\" mapping value is invalid! Done with cleanup.",
  8434. wszValueName);
  8435. //
  8436. // Move to next item.
  8437. //
  8438. dwIndex++;
  8439. continue;
  8440. }
  8441. //
  8442. // See if that DPNHUPNP instance is still around.
  8443. //
  8444. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  8445. {
  8446. wsprintf(tszObjectName, _T("Global\\") INSTANCENAMEDOBJECT_FORMATSTRING, dpnhafm.dwInstanceKey);
  8447. }
  8448. else
  8449. {
  8450. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, dpnhafm.dwInstanceKey);
  8451. }
  8452. hNamedObject = DNOpenEvent(SYNCHRONIZE, FALSE, tszObjectName);
  8453. if (hNamedObject != NULL)
  8454. {
  8455. //
  8456. // This is still an active mapping.
  8457. //
  8458. DPFX(DPFPREP, 4, "Firewall mapping \"%ls\" belongs to instance %u, which is still active.",
  8459. wszValueName, dpnhafm.dwInstanceKey);
  8460. DNCloseHandle(hNamedObject);
  8461. hNamedObject = NULL;
  8462. //
  8463. // Move to next item.
  8464. //
  8465. dwIndex++;
  8466. continue;
  8467. }
  8468. DPFX(DPFPREP, 4, "Firewall mapping \"%ls\" belongs to instance %u, which no longer exists.",
  8469. wszValueName, dpnhafm.dwInstanceKey);
  8470. //
  8471. // Delete the value now that we have the information we need.
  8472. //
  8473. if (! RegObject.DeleteValue(wszValueName))
  8474. {
  8475. //
  8476. // See ReadBlob comments. Stop trying to cleanup.
  8477. //
  8478. DPFX(DPFPREP, 0, "Couldn't delete \"%ls\"! Done with cleanup.",
  8479. wszValueName);
  8480. DNASSERT(hr == DPNH_OK);
  8481. goto Exit;
  8482. }
  8483. //
  8484. // Create a fake registered port that we will deregister. Ignore the
  8485. // NAT state flags.
  8486. //
  8487. pRegisteredPort = new CRegisteredPort(0, (dpnhafm.dwFlags & REGPORTOBJMASK_HNETFWAPI));
  8488. if (pRegisteredPort == NULL)
  8489. {
  8490. hr = DPNHERR_OUTOFMEMORY;
  8491. goto Failure;
  8492. }
  8493. //
  8494. // Assert that the other information/state flags are correct.
  8495. //
  8496. DNASSERT(! pRegisteredPort->IsHNetFirewallPortUnavailable());
  8497. DNASSERT(! pRegisteredPort->IsRemovingUPnPLease());
  8498. //
  8499. // Temporarily associate the registered port with the device.
  8500. //
  8501. pRegisteredPort->MakeDeviceOwner(pDevice);
  8502. ZeroMemory(&saddrinPrivate, sizeof(saddrinPrivate));
  8503. saddrinPrivate.sin_family = AF_INET;
  8504. saddrinPrivate.sin_addr.S_un.S_addr = dpnhafm.dwAddressV4;
  8505. saddrinPrivate.sin_port = dpnhafm.wPort;
  8506. //
  8507. // Store the private address.
  8508. //
  8509. hr = pRegisteredPort->SetPrivateAddresses(&saddrinPrivate, 1);
  8510. if (hr != DPNH_OK)
  8511. {
  8512. DPFX(DPFPREP, 0, "Failed creating UPnP address array!");
  8513. goto Failure;
  8514. }
  8515. fSetPrivateAddresses = TRUE;
  8516. //
  8517. // Pretend it has been mapped on the local firewall. Note that this
  8518. // flag shouldn't have been set at the time it was stored in registry
  8519. // but we masked it out if it had been.
  8520. //
  8521. pRegisteredPort->NoteMappedOnHNetFirewall();
  8522. //
  8523. // Actually free the port.
  8524. //
  8525. hr = this->UnmapPortOnLocalHNetFirewallInternal(pRegisteredPort,
  8526. FALSE,
  8527. pHNetCfgMgr);
  8528. if (hr != DPNH_OK)
  8529. {
  8530. DPFX(DPFPREP, 0, "Failed deleting temporary HNet firewall port (err = 0x%lx)! Ignoring.",
  8531. hr);
  8532. //
  8533. // Jump to the failure cleanup case, but don't actually return a
  8534. // failure.
  8535. //
  8536. hr = DPNH_OK;
  8537. goto Failure;
  8538. }
  8539. pRegisteredPort->ClearPrivateAddresses();
  8540. fSetPrivateAddresses = FALSE;
  8541. pRegisteredPort->ClearDeviceOwner();
  8542. delete pRegisteredPort;
  8543. pRegisteredPort = NULL;
  8544. //
  8545. // Move to the next mapping. Don't increment index since we just
  8546. // deleted the previous entry and everything shifts down one.
  8547. //
  8548. }
  8549. while (TRUE);
  8550. Exit:
  8551. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  8552. return hr;
  8553. Failure:
  8554. if (pRegisteredPort != NULL)
  8555. {
  8556. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  8557. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  8558. if (fSetPrivateAddresses)
  8559. {
  8560. pRegisteredPort->ClearPrivateAddresses();
  8561. fSetPrivateAddresses = FALSE;
  8562. }
  8563. pRegisteredPort->ClearDeviceOwner();
  8564. delete pRegisteredPort;
  8565. pRegisteredPort = NULL;
  8566. }
  8567. if (fOpenedRegistry)
  8568. {
  8569. RegObject.Close();
  8570. }
  8571. goto Exit;
  8572. } // CNATHelpUPnP::CleanupInactiveFirewallMappings
  8573. #endif // ! DPNBUILD_NOHNETFWAPI
  8574. #undef DPF_MODNAME
  8575. #define DPF_MODNAME "CNATHelpUPnP::RemoveAllItems"
  8576. //=============================================================================
  8577. // CNATHelpUPnP::RemoveAllItems
  8578. //-----------------------------------------------------------------------------
  8579. //
  8580. // Description: Removes all devices (de-registering with Internet gateways
  8581. // if necessary). This removes all registered port mapping
  8582. // objects and UPnP device objects, as well.
  8583. //
  8584. // The object lock is assumed to be held.
  8585. //
  8586. // Arguments: None.
  8587. //
  8588. // Returns: None.
  8589. //=============================================================================
  8590. void CNATHelpUPnP::RemoveAllItems(void)
  8591. {
  8592. HRESULT hr;
  8593. CBilink * pBilinkDevice;
  8594. CDevice * pDevice;
  8595. CBilink * pBilinkRegisteredPort;
  8596. CRegisteredPort * pRegisteredPort;
  8597. CUPnPDevice * pUPnPDevice;
  8598. DPFX(DPFPREP, 7, "(0x%p) Enter", this);
  8599. pBilinkDevice = this->m_blDevices.GetNext();
  8600. while (pBilinkDevice != &this->m_blDevices)
  8601. {
  8602. DNASSERT(! pBilinkDevice->IsEmpty());
  8603. pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
  8604. pBilinkDevice = pBilinkDevice->GetNext();
  8605. DPFX(DPFPREP, 5, "Destroying device 0x%p.",
  8606. pDevice);
  8607. pDevice->m_blList.RemoveFromList();
  8608. //
  8609. // All of the device's registered ports are implicitly freed.
  8610. //
  8611. pBilinkRegisteredPort = pDevice->m_blOwnedRegPorts.GetNext();
  8612. while (pBilinkRegisteredPort != &pDevice->m_blOwnedRegPorts)
  8613. {
  8614. DNASSERT(! pBilinkRegisteredPort->IsEmpty());
  8615. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
  8616. pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
  8617. DPFX(DPFPREP, 5, "Destroying registered port 0x%p (under device 0x%p).",
  8618. pRegisteredPort, pDevice);
  8619. //
  8620. // Unmap on UPnP server if necessary.
  8621. //
  8622. if (pRegisteredPort->HasUPnPPublicAddresses())
  8623. {
  8624. hr = this->UnmapUPnPPort(pRegisteredPort,
  8625. pRegisteredPort->GetNumAddresses(), // free all ports
  8626. TRUE);
  8627. if (hr != DPNH_OK)
  8628. {
  8629. DPFX(DPFPREP, 0, "Couldn't delete UPnP registered port 0x%p mapping (err = 0x%lx)! Ignoring.",
  8630. pRegisteredPort, hr);
  8631. //
  8632. // Continue anyway, so we can finish cleaning up the object.
  8633. //
  8634. }
  8635. DNASSERT(! pRegisteredPort->HasUPnPPublicAddresses());
  8636. pRegisteredPort->NoteNotPermanentUPnPLease();
  8637. pRegisteredPort->NoteNotUPnPPortUnavailable();
  8638. }
  8639. #ifndef DPNBUILD_NOHNETFWAPI
  8640. //
  8641. // Then unmap from the local firewall, if necessary.
  8642. //
  8643. if (pRegisteredPort->IsMappedOnHNetFirewall())
  8644. {
  8645. //
  8646. // Unmap the port.
  8647. //
  8648. // Alert the user since this is unexpected.
  8649. //
  8650. hr = this->UnmapPortOnLocalHNetFirewall(pRegisteredPort,
  8651. TRUE,
  8652. TRUE);
  8653. if (hr != DPNH_OK)
  8654. {
  8655. DPFX(DPFPREP, 0, "Failed unmapping registered port 0x%p on local HomeNet firewall (err = 0x%lx)! Ignoring.",
  8656. pRegisteredPort, hr);
  8657. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  8658. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  8659. //
  8660. // Continue anyway, so we can finish cleaning up the object.
  8661. //
  8662. }
  8663. }
  8664. #endif // ! DPNBUILD_NOHNETFWAPI
  8665. pRegisteredPort->ClearDeviceOwner();
  8666. DNASSERT(pRegisteredPort->m_blGlobalList.IsListMember(&this->m_blRegisteredPorts));
  8667. pRegisteredPort->m_blGlobalList.RemoveFromList();
  8668. pRegisteredPort->ClearPrivateAddresses();
  8669. //
  8670. // The user implicitly released this port.
  8671. //
  8672. pRegisteredPort->ClearAllUserRefs();
  8673. delete pRegisteredPort;
  8674. }
  8675. //
  8676. // The device's UPnP gateway is implicitly removed.
  8677. //
  8678. pUPnPDevice = pDevice->GetUPnPDevice();
  8679. if (pUPnPDevice != NULL)
  8680. {
  8681. if ((pUPnPDevice->IsConnecting()) || (pUPnPDevice->IsConnected()))
  8682. {
  8683. if (this->m_pfnshutdown(pUPnPDevice->GetControlSocket(), 0) != 0)
  8684. {
  8685. #ifdef DBG
  8686. int iError;
  8687. iError = this->m_pfnWSAGetLastError();
  8688. DPFX(DPFPREP, 0, "Failed shutting down UPnP device 0x%p's control socket (err = %u)! Ignoring.",
  8689. pUPnPDevice, iError);
  8690. #endif // DBG
  8691. }
  8692. }
  8693. pUPnPDevice->ClearDeviceOwner();
  8694. DNASSERT(pUPnPDevice->m_blList.IsListMember(&this->m_blUPnPDevices));
  8695. pUPnPDevice->m_blList.RemoveFromList();
  8696. //
  8697. // Transfer list reference to our pointer, since GetUPnPDevice did
  8698. // not give us one.
  8699. //
  8700. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  8701. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  8702. pUPnPDevice->ClearLocationURL();
  8703. pUPnPDevice->ClearUSN();
  8704. pUPnPDevice->ClearServiceControlURL();
  8705. pUPnPDevice->DestroyReceiveBuffer();
  8706. pUPnPDevice->RemoveAllCachedMappings();
  8707. pUPnPDevice->DecRef();
  8708. pUPnPDevice = NULL;
  8709. }
  8710. #ifndef DPNBUILD_NOHNETFWAPI
  8711. //
  8712. // If we used the HomeNet firewall API to open a hole for UPnP
  8713. // discovery multicasts, close it.
  8714. //
  8715. if (pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall())
  8716. {
  8717. hr = this->CloseDevicesUPnPDiscoveryPort(pDevice, NULL);
  8718. if (hr != DPNH_OK)
  8719. {
  8720. DPFX(DPFPREP, 0, "Couldn't close device 0x%p's UPnP discovery socket's port on firewall (err = 0x%lx)! Ignoring.",
  8721. pDevice, hr);
  8722. //
  8723. // Continue...
  8724. //
  8725. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  8726. hr = DPNH_OK;
  8727. }
  8728. }
  8729. #endif // ! DPNBUILD_NOHNETFWAPI
  8730. //
  8731. // Close the socket.
  8732. //
  8733. if (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP)
  8734. {
  8735. this->m_pfnclosesocket(pDevice->GetUPnPDiscoverySocket());
  8736. pDevice->SetUPnPDiscoverySocket(INVALID_SOCKET);
  8737. }
  8738. //
  8739. // Now we can dump the device object.
  8740. //
  8741. delete pDevice;
  8742. }
  8743. //
  8744. // Removing all the devices normally removes all the registered ports, but
  8745. // there may still be more wildcard ports that were never associated with
  8746. // any device.
  8747. //
  8748. pBilinkRegisteredPort = this->m_blUnownedPorts.GetNext();
  8749. while (pBilinkRegisteredPort != &this->m_blUnownedPorts)
  8750. {
  8751. DNASSERT(! pBilinkRegisteredPort->IsEmpty());
  8752. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
  8753. pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
  8754. DPFX(DPFPREP, 5, "Destroying unowned registered port 0x%p.",
  8755. pRegisteredPort);
  8756. pRegisteredPort->m_blDeviceList.RemoveFromList();
  8757. DNASSERT(pRegisteredPort->m_blGlobalList.IsListMember(&this->m_blRegisteredPorts));
  8758. pRegisteredPort->m_blGlobalList.RemoveFromList();
  8759. pRegisteredPort->ClearPrivateAddresses();
  8760. #ifndef DPNBUILD_NOHNETFWAPI
  8761. DNASSERT(! pRegisteredPort->IsMappedOnHNetFirewall());
  8762. DNASSERT(! pRegisteredPort->IsHNetFirewallPortUnavailable());
  8763. #endif // ! DPNBUILD_NOHNETFWAPI
  8764. DNASSERT(! pRegisteredPort->HasUPnPPublicAddresses());
  8765. DNASSERT(! pRegisteredPort->IsUPnPPortUnavailable());
  8766. //
  8767. // The user implicitly released this port.
  8768. //
  8769. pRegisteredPort->ClearAllUserRefs();
  8770. delete pRegisteredPort;
  8771. }
  8772. #ifdef DBG
  8773. DNASSERT(this->m_blRegisteredPorts.IsEmpty());
  8774. DNASSERT(this->m_blUPnPDevices.IsEmpty());
  8775. //
  8776. // Print all items still in the registry.
  8777. //
  8778. #ifndef DPNBUILD_NOHNETFWAPI
  8779. this->DebugPrintActiveFirewallMappings();
  8780. #endif // ! DPNBUILD_NOHNETFWAPI
  8781. this->DebugPrintActiveNATMappings();
  8782. #endif // DBG
  8783. DPFX(DPFPREP, 7, "(0x%p) Leave", this);
  8784. } // CNATHelpUPnP::RemoveAllItems
  8785. #undef DPF_MODNAME
  8786. #define DPF_MODNAME "CNATHelpUPnP::FindMatchingDevice"
  8787. //=============================================================================
  8788. // CNATHelpUPnP::FindMatchingDevice
  8789. //-----------------------------------------------------------------------------
  8790. //
  8791. // Description: Searches the list of devices for the object matching the
  8792. // given address, or NULL if one could not be found. If the
  8793. // address is INADDR_ANY, then the first device with a remote NAT
  8794. // is selected. If none exist, then the first device with a local
  8795. // firewall is selected.
  8796. //
  8797. // If fUseAllInfoSources is TRUE, the list of registered ports
  8798. // associated with devices is searched first for an exact match to
  8799. // the address passed in. If that fails, then devices are
  8800. // searched as above. In addition, if the address is INADDR_ANY,
  8801. /// the first device with a local NAT can be selected.
  8802. //
  8803. // The object lock is assumed to be held.
  8804. //
  8805. // Arguments:
  8806. // SOCKADDR_IN * psaddrinMatch - Pointer to address to look up.
  8807. // BOOL fUseAllInfoSources - Whether all possible sources of
  8808. // information should be considered.
  8809. //
  8810. // Returns: CDevice
  8811. // NULL if no match, valid object otherwise.
  8812. //=============================================================================
  8813. CDevice * CNATHelpUPnP::FindMatchingDevice(const SOCKADDR_IN * const psaddrinMatch,
  8814. const BOOL fUseAllInfoSources)
  8815. {
  8816. HRESULT hr;
  8817. BOOL fUpdatedDeviceList = FALSE;
  8818. CDevice * pDeviceUPnPGateway = NULL;
  8819. #ifndef DPNBUILD_NOHNETFWAPI
  8820. CDevice * pDeviceLocalHNetFirewall = NULL;
  8821. #endif // ! DPNBUILD_NOHNETFWAPI
  8822. SOCKADDR_IN * pasaddrinTemp;
  8823. CBilink * pBilink;
  8824. CRegisteredPort * pRegisteredPort;
  8825. CDevice * pDevice;
  8826. DWORD dwTemp;
  8827. do
  8828. {
  8829. //
  8830. // First, make sure there are devices to choose from.
  8831. //
  8832. if (this->m_blDevices.IsEmpty())
  8833. {
  8834. DPFX(DPFPREP, 0, "No devices, can't match address %u.%u.%u.%u!",
  8835. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  8836. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  8837. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  8838. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
  8839. pDevice = NULL;
  8840. goto Exit;
  8841. }
  8842. //
  8843. // It's possible that the address we're trying to match is an already
  8844. // registered port. Look through all owned port mappings for this
  8845. // address, if we're allowed.
  8846. //
  8847. if (fUseAllInfoSources)
  8848. {
  8849. pBilink = this->m_blRegisteredPorts.GetNext();
  8850. while (pBilink != &this->m_blRegisteredPorts)
  8851. {
  8852. DNASSERT(! pBilink->IsEmpty());
  8853. pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
  8854. //
  8855. // Only check this registered port if it has an owning device.
  8856. //
  8857. pDevice = pRegisteredPort->GetOwningDevice();
  8858. if (pDevice != NULL)
  8859. {
  8860. //
  8861. // Check each port in the array.
  8862. //
  8863. pasaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
  8864. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  8865. {
  8866. //
  8867. // If the address matches, we have a winner.
  8868. //
  8869. if ((pasaddrinTemp[dwTemp].sin_addr.S_un.S_addr == psaddrinMatch->sin_addr.S_un.S_addr) &&
  8870. (pasaddrinTemp[dwTemp].sin_port == psaddrinMatch->sin_port))
  8871. {
  8872. DPFX(DPFPREP, 7, "Registered port 0x%p index %u matches address %u.%u.%u.%u:%u, returning owning device 0x%p.",
  8873. pRegisteredPort,
  8874. dwTemp,
  8875. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  8876. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  8877. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  8878. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4,
  8879. NTOHS(psaddrinMatch->sin_port),
  8880. pDevice);
  8881. goto Exit;
  8882. }
  8883. }
  8884. }
  8885. pBilink = pBilink->GetNext();
  8886. }
  8887. }
  8888. //
  8889. // Darn, the address is not already registered. Well, match it up with
  8890. // a device as best as possible.
  8891. //
  8892. pBilink = this->m_blDevices.GetNext();
  8893. do
  8894. {
  8895. DNASSERT(! pBilink->IsEmpty());
  8896. pDevice = DEVICE_FROM_BILINK(pBilink);
  8897. if ((pDevice->GetLocalAddressV4() == psaddrinMatch->sin_addr.S_un.S_addr))
  8898. {
  8899. DPFX(DPFPREP, 7, "Device 0x%p matches address %u.%u.%u.%u.",
  8900. pDevice,
  8901. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  8902. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  8903. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  8904. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
  8905. goto Exit;
  8906. }
  8907. //
  8908. // Remember this device if it has the first remote UPnP gateway
  8909. // device we've seen.
  8910. //
  8911. if ((pDevice->GetUPnPDevice() != NULL) &&
  8912. ((! pDevice->GetUPnPDevice()->IsLocal()) || (fUseAllInfoSources)) &&
  8913. (pDeviceUPnPGateway == NULL))
  8914. {
  8915. pDeviceUPnPGateway = pDevice;
  8916. }
  8917. #ifndef DPNBUILD_NOHNETFWAPI
  8918. //
  8919. // Remember this device if it has the first HomeNet firewall we've
  8920. // seen.
  8921. //
  8922. if ((pDevice->IsHNetFirewalled()) &&
  8923. (pDeviceLocalHNetFirewall == NULL))
  8924. {
  8925. pDeviceLocalHNetFirewall = pDevice;
  8926. }
  8927. #endif // ! DPNBUILD_NOHNETFWAPI
  8928. DPFX(DPFPREP, 7, "Device 0x%p does not match address %u.%u.%u.%u.",
  8929. pDevice,
  8930. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  8931. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  8932. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  8933. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
  8934. pBilink = pBilink->GetNext();
  8935. }
  8936. while (pBilink != &this->m_blDevices);
  8937. //
  8938. // If we got here, there's no matching device. It might be because the
  8939. // caller detected an address change faster than we did. Try updating
  8940. // our device list and searching again (if we haven't already).
  8941. //
  8942. if (fUpdatedDeviceList)
  8943. {
  8944. break;
  8945. }
  8946. //
  8947. // Don't bother updating the list to match INADDR_ANY, we know that
  8948. // will never match anything.
  8949. //
  8950. if (psaddrinMatch->sin_addr.S_un.S_addr == INADDR_ANY)
  8951. {
  8952. DPFX(DPFPREP, 7, "Couldn't find matching device for INADDR_ANY, as expected.");
  8953. break;
  8954. }
  8955. DPFX(DPFPREP, 5, "Couldn't find matching device for %u.%u.%u.%u, updating device list and searching again.",
  8956. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  8957. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  8958. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  8959. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
  8960. hr = this->CheckForNewDevices(&fUpdatedDeviceList);
  8961. if (hr != DPNH_OK)
  8962. {
  8963. DPFX(DPFPREP, 0, "Couldn't check for new devices (0x%lx), continuing.",
  8964. hr);
  8965. //
  8966. // Hmm, we have to treat it as non-fatal. Don't search again,
  8967. // though.
  8968. //
  8969. break;
  8970. }
  8971. //
  8972. // If we didn't actually get any new devices, don't bother searching
  8973. // again.
  8974. //
  8975. if (! fUpdatedDeviceList)
  8976. {
  8977. break;
  8978. }
  8979. //
  8980. // fUpdatedDeviceList is set to TRUE so we'll only loop one more time.
  8981. //
  8982. }
  8983. while (TRUE);
  8984. //
  8985. // If we got here, there's still no matching device. If it's the wildcard
  8986. // value, that's to be expected, but we need to pick a device in the
  8987. // following order:
  8988. // 1. device has an Internet gateway
  8989. // 2. device has a firewall
  8990. // If none of those exists or it's not the wildcard value, we have to give
  8991. // up.
  8992. //
  8993. if (psaddrinMatch->sin_addr.S_un.S_addr == INADDR_ANY)
  8994. {
  8995. if (pDeviceUPnPGateway != NULL)
  8996. {
  8997. pDevice = pDeviceUPnPGateway;
  8998. DPFX(DPFPREP, 1, "Picking device 0x%p with UPnP gateway device to match INADDR_ANY.",
  8999. pDevice);
  9000. }
  9001. #ifndef DPNBUILD_NOHNETFWAPI
  9002. else if (pDeviceLocalHNetFirewall != NULL)
  9003. {
  9004. pDevice = pDeviceLocalHNetFirewall;
  9005. DPFX(DPFPREP, 1, "Picking device 0x%p with local HomeNet firewall to match INADDR_ANY.",
  9006. pDevice);
  9007. }
  9008. #endif // ! DPNBUILD_NOHNETFWAPI
  9009. else
  9010. {
  9011. pDevice = NULL;
  9012. DPFX(DPFPREP, 1, "No suitable device to match INADDR_ANY.");
  9013. }
  9014. }
  9015. else
  9016. {
  9017. pDevice = NULL;
  9018. DPFX(DPFPREP, 7, "No devices match address %u.%u.%u.%u.",
  9019. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  9020. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  9021. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  9022. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
  9023. }
  9024. Exit:
  9025. return pDevice;
  9026. } // CNATHelpUPnP::FindMatchingDevice
  9027. #undef DPF_MODNAME
  9028. #define DPF_MODNAME "CNATHelpUPnP::ExtendAllExpiringLeases"
  9029. //=============================================================================
  9030. // CNATHelpUPnP::ExtendAllExpiringLeases
  9031. //-----------------------------------------------------------------------------
  9032. //
  9033. // Description: Renews any port leases that are close to expiring (within 2
  9034. // minutes of expiration time).
  9035. //
  9036. // The object lock is assumed to be held.
  9037. //
  9038. // Arguments: None.
  9039. //
  9040. // Returns: HRESULT
  9041. // DPNH_OK - Lease extension was successful.
  9042. // DPNHERR_GENERIC - An error occurred.
  9043. //=============================================================================
  9044. HRESULT CNATHelpUPnP::ExtendAllExpiringLeases(void)
  9045. {
  9046. HRESULT hr = DPNH_OK;
  9047. CBilink * pBilink;
  9048. CRegisteredPort * pRegisteredPort;
  9049. CDevice * pDevice;
  9050. DWORD dwLeaseTimeRemaining;
  9051. DPFX(DPFPREP, 5, "Enter");
  9052. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  9053. //
  9054. // Walk the list of all registered ports and check for leases that need to
  9055. // be extended.
  9056. // The lock is already held.
  9057. //
  9058. pBilink = this->m_blRegisteredPorts.GetNext();
  9059. while (pBilink != (&this->m_blRegisteredPorts))
  9060. {
  9061. DNASSERT(! pBilink->IsEmpty());
  9062. pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
  9063. pDevice = pRegisteredPort->GetOwningDevice();
  9064. //
  9065. // If the port is registered with the UPnP device, extend that lease,
  9066. // if necessary.
  9067. //
  9068. if ((pRegisteredPort->HasUPnPPublicAddresses()) &&
  9069. (! pRegisteredPort->HasPermanentUPnPLease()))
  9070. {
  9071. DNASSERT(pDevice != NULL);
  9072. dwLeaseTimeRemaining = pRegisteredPort->GetUPnPLeaseExpiration() - GETTIMESTAMP();
  9073. if (dwLeaseTimeRemaining < LEASE_RENEW_TIME)
  9074. {
  9075. hr = this->ExtendUPnPLease(pRegisteredPort);
  9076. if (hr != DPNH_OK)
  9077. {
  9078. DPFX(DPFPREP, 0, "Couldn't extend port mapping lease on remote UPnP device (0x%lx)! Ignoring.", hr);
  9079. //
  9080. // We'll treat this as non-fatal, but we have to dump the
  9081. // server. This may have already been done, but doing it
  9082. // twice shouldn't be harmful.
  9083. //
  9084. this->ClearDevicesUPnPDevice(pDevice);
  9085. hr = DPNH_OK;
  9086. }
  9087. }
  9088. }
  9089. //
  9090. // The local firewall never uses leases, no need to extend.
  9091. //
  9092. pBilink = pBilink->GetNext();
  9093. }
  9094. DNASSERT(hr == DPNH_OK);
  9095. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  9096. return hr;
  9097. } // CNATHelpUPnP::ExtendAllExpiringLeases
  9098. #undef DPF_MODNAME
  9099. #define DPF_MODNAME "CNATHelpUPnP::UpdateServerStatus"
  9100. //=============================================================================
  9101. // CNATHelpUPnP::UpdateServerStatus
  9102. //-----------------------------------------------------------------------------
  9103. //
  9104. // Description: Checks to see if any Internet gateways have stopped
  9105. // responding or are now available.
  9106. //
  9107. // The object lock is assumed to be held.
  9108. //
  9109. // Arguments: None.
  9110. //
  9111. // Returns: HRESULT
  9112. // DPNH_OK - The update was successful.
  9113. // DPNHERR_GENERIC - An error occurred.
  9114. //=============================================================================
  9115. HRESULT CNATHelpUPnP::UpdateServerStatus(void)
  9116. {
  9117. HRESULT hr = DPNH_OK;
  9118. DWORD dwMinUpdateServerStatusInterval;
  9119. DWORD dwCurrentTime;
  9120. CBilink * pBilink;
  9121. CDevice * pDevice;
  9122. CUPnPDevice * pUPnPDevice = NULL;
  9123. CDevice * pDeviceRemoteUPnPGateway = NULL;
  9124. #ifndef DPNBUILD_NOHNETFWAPI
  9125. CDevice * pDeviceLocalHNetFirewall = NULL;
  9126. #endif // ! DPNBUILD_NOHNETFWAPI
  9127. BOOL fSendRemoteGatewayDiscovery;
  9128. DPFX(DPFPREP, 5, "Enter");
  9129. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  9130. //
  9131. // Cache the current value of the global. This should be atomic so no need
  9132. // to take the globals lock.
  9133. //
  9134. dwMinUpdateServerStatusInterval = g_dwMinUpdateServerStatusInterval;
  9135. //
  9136. // Capture the current time.
  9137. //
  9138. dwCurrentTime = GETTIMESTAMP();
  9139. //
  9140. // If this isn't the first time to update server status, but it hasn't been
  9141. // very long since we last checked, don't. This will prevent unnecessary
  9142. // network traffic if GetCaps is called frequently (in response to many
  9143. // alert events, for example).
  9144. //
  9145. // However, if we just found a new device, update the status anyway.
  9146. //
  9147. if (this->m_dwLastUpdateServerStatusTime != 0)
  9148. {
  9149. if ((dwCurrentTime - this->m_dwLastUpdateServerStatusTime) < dwMinUpdateServerStatusInterval)
  9150. {
  9151. if (! (this->m_dwFlags & NATHELPUPNPOBJ_DEVICECHANGED))
  9152. {
  9153. DPFX(DPFPREP, 5, "Server status was just updated at %u, not updating again (time = %u, min interval = %u).",
  9154. this->m_dwLastUpdateServerStatusTime,
  9155. dwCurrentTime,
  9156. dwMinUpdateServerStatusInterval);
  9157. //
  9158. // hr == DPNH_OK
  9159. //
  9160. goto Exit;
  9161. }
  9162. 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.",
  9163. this->m_dwLastUpdateServerStatusTime,
  9164. dwCurrentTime,
  9165. dwMinUpdateServerStatusInterval);
  9166. //
  9167. // Continue...
  9168. //
  9169. }
  9170. //
  9171. // If we're allowed to keep polling for remote gateways after startup,
  9172. // do so. Otherwise, only do it if a device has changed or a port has
  9173. // been registered since our last check.
  9174. //
  9175. if ((g_fKeepPollingForRemoteGateway) ||
  9176. (this->m_dwFlags & NATHELPUPNPOBJ_DEVICECHANGED) ||
  9177. (this->m_dwFlags & NATHELPUPNPOBJ_PORTREGISTERED))
  9178. {
  9179. fSendRemoteGatewayDiscovery = TRUE;
  9180. }
  9181. else
  9182. {
  9183. fSendRemoteGatewayDiscovery = FALSE;
  9184. }
  9185. }
  9186. else
  9187. {
  9188. //
  9189. // We always poll for new remote gateways during startup.
  9190. //
  9191. fSendRemoteGatewayDiscovery = TRUE;
  9192. }
  9193. //
  9194. // Prevent the timer from landing exactly on 0.
  9195. //
  9196. if (dwCurrentTime == 0)
  9197. {
  9198. dwCurrentTime = 1;
  9199. }
  9200. this->m_dwLastUpdateServerStatusTime = dwCurrentTime;
  9201. //
  9202. // Turn off the 'device changed' and 'port registered' flags, if they were
  9203. // on.
  9204. //
  9205. this->m_dwFlags &= ~(NATHELPUPNPOBJ_DEVICECHANGED | NATHELPUPNPOBJ_PORTREGISTERED);
  9206. //
  9207. // Locate any new UPnP devices.
  9208. //
  9209. if (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP)
  9210. {
  9211. //
  9212. // We're not listening on the UPnP multicast address and can't hear
  9213. // unsolicited new device announcements. In order to detect new
  9214. // devices, we need to resend the discovery request periodically so
  9215. // that responses get sent directly to our listening socket.
  9216. //
  9217. hr = this->CheckForUPnPAnnouncements(g_dwUPnPAnnounceResponseWaitTime,
  9218. fSendRemoteGatewayDiscovery);
  9219. if (hr != DPNH_OK)
  9220. {
  9221. DPFX(DPFPREP, 0, "Couldn't check for UPnP announcements!");
  9222. goto Failure;
  9223. }
  9224. }
  9225. else
  9226. {
  9227. //
  9228. // Not using UPnP.
  9229. //
  9230. }
  9231. //
  9232. // Loop through all the devices.
  9233. //
  9234. pBilink = this->m_blDevices.GetNext();
  9235. while (pBilink != &this->m_blDevices)
  9236. {
  9237. DNASSERT(! pBilink->IsEmpty());
  9238. pDevice = DEVICE_FROM_BILINK(pBilink);
  9239. //
  9240. // This might be a new device, so register any ports with this address
  9241. // that were previously unowned (because this device's address was
  9242. // unknown at the time).
  9243. //
  9244. hr = this->RegisterPreviouslyUnownedPortsWithDevice(pDevice, FALSE);
  9245. if (hr != DPNH_OK)
  9246. {
  9247. DPFX(DPFPREP, 0, "Couldn't register previously unowned ports with device 0x%p!.",
  9248. pDevice);
  9249. goto Failure;
  9250. }
  9251. #ifndef DPNBUILD_NOHNETFWAPI
  9252. if (this->m_dwFlags & NATHELPUPNPOBJ_USEHNETFWAPI)
  9253. {
  9254. //
  9255. // See if the local firewall state has changed.
  9256. //
  9257. hr = this->CheckForLocalHNetFirewallAndMapPorts(pDevice, NULL);
  9258. if (hr != DPNH_OK)
  9259. {
  9260. DPFX(DPFPREP, 0, "Couldn't check for local HNet firewall and map ports (err = 0x%lx)! Ignoring.",
  9261. hr);
  9262. DNASSERT(! pDevice->IsHNetFirewalled());
  9263. hr = DPNH_OK;
  9264. }
  9265. //
  9266. // If there's a local firewall, remember the device if it's the
  9267. // first one we've found.
  9268. //
  9269. if ((pDevice->IsHNetFirewalled()) &&
  9270. (pDeviceLocalHNetFirewall == NULL))
  9271. {
  9272. pDeviceLocalHNetFirewall = pDevice;
  9273. }
  9274. }
  9275. else
  9276. {
  9277. //
  9278. // Not using firewall traversal.
  9279. //
  9280. }
  9281. #endif // ! DPNBUILD_NOHNETFWAPI
  9282. if (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP)
  9283. {
  9284. pUPnPDevice = pDevice->GetUPnPDevice();
  9285. if (pUPnPDevice != NULL)
  9286. {
  9287. //
  9288. // GetUPnPDevice did not add a reference to pUPnPDevice for us.
  9289. //
  9290. pUPnPDevice->AddRef();
  9291. //
  9292. // Update the public addresses for the UPnP device, if any.
  9293. //
  9294. hr = this->UpdateUPnPExternalAddress(pUPnPDevice, TRUE);
  9295. if (hr != DPNH_OK)
  9296. {
  9297. DPFX(DPFPREP, 0, "Failed updating UPnP device external address!");
  9298. //
  9299. // It may have been cleared already, but doing it twice
  9300. // shouldn't be harmful.
  9301. //
  9302. this->ClearDevicesUPnPDevice(pDevice);
  9303. hr = DPNH_OK;
  9304. }
  9305. else
  9306. {
  9307. //
  9308. // Save this UPnP device, if it's the first one we've
  9309. // found and it's not local.
  9310. //
  9311. if ((pDeviceRemoteUPnPGateway == NULL) &&
  9312. (! pUPnPDevice->IsLocal()))
  9313. {
  9314. pDeviceRemoteUPnPGateway = pDevice;
  9315. }
  9316. }
  9317. pUPnPDevice->DecRef();
  9318. pUPnPDevice = NULL;
  9319. }
  9320. else
  9321. {
  9322. //
  9323. // No UPnP device.
  9324. //
  9325. }
  9326. }
  9327. else
  9328. {
  9329. //
  9330. // Not using UPnP.
  9331. //
  9332. }
  9333. pBilink = pBilink->GetNext();
  9334. }
  9335. //
  9336. // Some new servers may have come online. If so, we can now map wildcard
  9337. // ports that were registered previously. Figure out which device that is.
  9338. //
  9339. if (pDeviceRemoteUPnPGateway != NULL)
  9340. {
  9341. pDevice = pDeviceRemoteUPnPGateway;
  9342. }
  9343. #ifndef DPNBUILD_NOHNETFWAPI
  9344. else if (pDeviceLocalHNetFirewall != NULL)
  9345. {
  9346. pDevice = pDeviceLocalHNetFirewall;
  9347. }
  9348. #endif // ! DPNBUILD_NOHNETFWAPI
  9349. else
  9350. {
  9351. pDevice = NULL;
  9352. }
  9353. if (pDevice != NULL)
  9354. {
  9355. //
  9356. // Register any wildcard ports that are unowned with this best device.
  9357. //
  9358. hr = this->RegisterPreviouslyUnownedPortsWithDevice(pDevice, TRUE);
  9359. if (hr != DPNH_OK)
  9360. {
  9361. DPFX(DPFPREP, 0, "Couldn't register unowned wildcard ports with device 0x%p!.",
  9362. pDevice);
  9363. goto Failure;
  9364. }
  9365. }
  9366. #ifdef DBG
  9367. else
  9368. {
  9369. DPFX(DPFPREP, 7, "No devices have a UPnP gateway device or a local HomeNet firewall.");
  9370. }
  9371. #endif // DBG
  9372. DPFX(DPFPREP, 7, "Spent %u ms updating server status, starting at %u.",
  9373. (GETTIMESTAMP() - dwCurrentTime), dwCurrentTime);
  9374. Exit:
  9375. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  9376. return hr;
  9377. Failure:
  9378. goto Exit;
  9379. } // CNATHelpUPnP::UpdateServerStatus
  9380. #undef DPF_MODNAME
  9381. #define DPF_MODNAME "CNATHelpUPnP::RegisterPreviouslyUnownedPortsWithDevice"
  9382. //=============================================================================
  9383. // CNATHelpUPnP::RegisterPreviouslyUnownedPortsWithDevice
  9384. //-----------------------------------------------------------------------------
  9385. //
  9386. // Description: Associates unknown ports with the given device, and
  9387. // registers them with the device's UPnP device or firewall.
  9388. //
  9389. // If fWildcardToo is FALSE, only previously unowned ports that
  9390. // match the device's address are associated. If TRUE, unowned
  9391. // INADDR_ANY ports are associated as well.
  9392. //
  9393. // The object lock is assumed to be held.
  9394. //
  9395. // Arguments:
  9396. // CDevice * pDevice - Pointer to device to take ownership of ports.
  9397. // BOOL fAll - Whether all ports should be associated.
  9398. //
  9399. // Returns: HRESULT
  9400. // DPNH_OK - The extension was successful.
  9401. // DPNHERR_GENERIC - An error occurred.
  9402. //=============================================================================
  9403. HRESULT CNATHelpUPnP::RegisterPreviouslyUnownedPortsWithDevice(CDevice * const pDevice,
  9404. const BOOL fWildcardToo)
  9405. {
  9406. HRESULT hr = DPNH_OK;
  9407. CBilink * pBilink;
  9408. CRegisteredPort * pRegisteredPort;
  9409. SOCKADDR_IN * pasaddrinPrivate;
  9410. CUPnPDevice * pUPnPDevice;
  9411. #ifdef DBG
  9412. BOOL fAssignedPort = FALSE;
  9413. IN_ADDR inaddrTemp;
  9414. #endif // DBG
  9415. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)",
  9416. this, pDevice, fWildcardToo);
  9417. //
  9418. // Loop through all unowned ports, assign them to the device if
  9419. // appropriate, then register them.
  9420. //
  9421. pBilink = this->m_blUnownedPorts.GetNext();
  9422. while (pBilink != &this->m_blUnownedPorts)
  9423. {
  9424. DNASSERT(! pBilink->IsEmpty());
  9425. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  9426. pBilink = pBilink->GetNext();
  9427. //
  9428. // The registered port must match the device's address in order to
  9429. // associate them. If wildcards are allowed, then INADDR_ANY
  9430. // registrations can be associated, too.
  9431. //
  9432. //
  9433. // All addresses should be same (if there are more than one), so just
  9434. // compare the first one in the array.
  9435. //
  9436. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  9437. if (pasaddrinPrivate[0].sin_addr.S_un.S_addr != pDevice->GetLocalAddressV4())
  9438. {
  9439. if (pasaddrinPrivate[0].sin_addr.S_un.S_addr != INADDR_ANY)
  9440. {
  9441. DPFX(DPFPREP, 7, "Unowned registered port 0x%p private address %u.%u.%u.%u doesn't match device 0x%p's, skipping.",
  9442. pRegisteredPort,
  9443. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b1,
  9444. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b2,
  9445. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b3,
  9446. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b4,
  9447. pDevice);
  9448. continue;
  9449. }
  9450. #ifdef DBG
  9451. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  9452. #endif // DBG
  9453. if (! fWildcardToo)
  9454. {
  9455. #ifdef DBG
  9456. 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.",
  9457. pRegisteredPort,
  9458. pDevice,
  9459. inaddrTemp.S_un.S_un_b.s_b1,
  9460. inaddrTemp.S_un.S_un_b.s_b2,
  9461. inaddrTemp.S_un.S_un_b.s_b3,
  9462. inaddrTemp.S_un.S_un_b.s_b4);
  9463. #endif // DBG
  9464. continue;
  9465. }
  9466. #ifdef DBG
  9467. DPFX(DPFPREP, 7, "Unowned registered port 0x%p (INADDR_ANY) becoming associated with device 0x%p (address %u.%u.%u.%u).",
  9468. pRegisteredPort,
  9469. pDevice,
  9470. inaddrTemp.S_un.S_un_b.s_b1,
  9471. inaddrTemp.S_un.S_un_b.s_b2,
  9472. inaddrTemp.S_un.S_un_b.s_b3,
  9473. inaddrTemp.S_un.S_un_b.s_b4);
  9474. #endif // DBG
  9475. }
  9476. else
  9477. {
  9478. DPFX(DPFPREP, 7, "Unowned registered port 0x%p private address %u.%u.%u.%u matches device 0x%p's, associating.",
  9479. pRegisteredPort,
  9480. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b1,
  9481. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b2,
  9482. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b3,
  9483. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b4,
  9484. pDevice);
  9485. //
  9486. // The way it's currently implemented, all non-wildcard ports
  9487. // should be registered before we even try to register the wildcard
  9488. // ones.
  9489. //
  9490. DNASSERT(! fWildcardToo);
  9491. }
  9492. //
  9493. // If we made it here, we can associate the port with the device.
  9494. //
  9495. pRegisteredPort->m_blDeviceList.RemoveFromList();
  9496. pRegisteredPort->MakeDeviceOwner(pDevice);
  9497. #ifndef DPNBUILD_NOHNETFWAPI
  9498. //
  9499. // Start by automatically mapping with the local firewall, if there is
  9500. // one and we're allowed.
  9501. //
  9502. if (this->m_dwFlags & NATHELPUPNPOBJ_USEHNETFWAPI)
  9503. {
  9504. hr = this->CheckForLocalHNetFirewallAndMapPorts(pDevice, NULL);
  9505. if (hr != DPNH_OK)
  9506. {
  9507. DPFX(DPFPREP, 0, "Couldn't check for local HNet firewall and map ports (err = 0x%lx)! Ignoring.",
  9508. hr);
  9509. DNASSERT(! pDevice->IsHNetFirewalled());
  9510. hr = DPNH_OK;
  9511. }
  9512. }
  9513. else
  9514. {
  9515. //
  9516. // Not using firewall traversal.
  9517. //
  9518. }
  9519. #endif // ! DPNBUILD_NOHNETFWAPI
  9520. //
  9521. // Attempt to automatically map it with the (new) UPnP gateway device,
  9522. // if present.
  9523. //
  9524. pUPnPDevice = pDevice->GetUPnPDevice();
  9525. if (pUPnPDevice != NULL)
  9526. {
  9527. //
  9528. // GetUPnPDevice did not add a reference to pUPnPDevice for us.
  9529. //
  9530. pUPnPDevice->AddRef();
  9531. DNASSERT(pUPnPDevice->IsReady());
  9532. hr = this->MapPortsOnUPnPDevice(pUPnPDevice, pRegisteredPort);
  9533. if (hr != DPNH_OK)
  9534. {
  9535. DPFX(DPFPREP, 0, "Couldn't map existing ports on UPnP device 0x%p!",
  9536. pUPnPDevice);
  9537. //
  9538. // It may have been cleared already, but doing it twice
  9539. // shouldn't be harmful.
  9540. //
  9541. this->ClearDevicesUPnPDevice(pDevice);
  9542. hr = DPNH_OK;
  9543. }
  9544. pUPnPDevice->DecRef();
  9545. pUPnPDevice = NULL;
  9546. }
  9547. }
  9548. #ifdef DBG
  9549. if (! fAssignedPort)
  9550. {
  9551. DPFX(DPFPREP, 1, "No unowned ports were bound to device object 0x%p.",
  9552. pDevice);
  9553. }
  9554. #endif // DBG
  9555. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  9556. return hr;
  9557. } // CNATHelpUPnP::RegisterPreviouslyUnownedPortsWithDevice
  9558. #undef DPF_MODNAME
  9559. #define DPF_MODNAME "CNATHelpUPnP::SendUPnPSearchMessagesForDevice"
  9560. //=============================================================================
  9561. // CNATHelpUPnP::SendUPnPSearchMessagesForDevice
  9562. //-----------------------------------------------------------------------------
  9563. //
  9564. // Description: Sends one UPnP search message via the given device locally
  9565. // and if fRemoteAllowed is TRUE, to the multicast or gateway
  9566. // address.
  9567. //
  9568. // The object lock is assumed to be held.
  9569. //
  9570. // Arguments:
  9571. // CDevice * pDevice - Pointer to device to use.
  9572. // BOOL fRemoteAllowed - Whether we can search remotely or not.
  9573. //
  9574. // Returns: HRESULT
  9575. // DPNH_OK - The messages were sent successfully.
  9576. // DPNHERR_GENERIC - An error occurred.
  9577. //=============================================================================
  9578. HRESULT CNATHelpUPnP::SendUPnPSearchMessagesForDevice(CDevice * const pDevice,
  9579. const BOOL fRemoteAllowed)
  9580. {
  9581. HRESULT hr = DPNH_OK;
  9582. SOCKADDR_IN saddrinRemote;
  9583. SOCKADDR_IN saddrinLocal;
  9584. BOOL fTryRemote;
  9585. int iWANIPConnectionMsgSize;
  9586. int iWANPPPConnectionMsgSize;
  9587. int iReturn;
  9588. SOCKET sTemp = INVALID_SOCKET;
  9589. #ifdef DBG
  9590. DWORD dwError;
  9591. #endif // DBG
  9592. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)", this, pDevice, fRemoteAllowed);
  9593. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  9594. DNASSERT(pDevice->GetUPnPDiscoverySocket() != INVALID_SOCKET);
  9595. ZeroMemory(&saddrinRemote, sizeof(saddrinRemote));
  9596. saddrinRemote.sin_family = AF_INET;
  9597. //saddrinRemote.sin_addr.S_un.S_addr = ?
  9598. saddrinRemote.sin_port = HTONS(UPNP_PORT);
  9599. //
  9600. // If we're allowed to try remotely, use the gateway's address, or the
  9601. // multicast address, as appropriate.
  9602. //
  9603. if ((fRemoteAllowed) && (! pDevice->GotRemoteUPnPDiscoveryConnReset()))
  9604. {
  9605. if (g_fUseMulticastUPnPDiscovery)
  9606. {
  9607. saddrinRemote.sin_addr.S_un.S_addr = this->m_pfninet_addr(UPNP_DISCOVERY_MULTICAST_ADDRESS);
  9608. fTryRemote = TRUE;
  9609. }
  9610. else
  9611. {
  9612. //
  9613. // Try to get the device's gateway's address. This might return FALSE
  9614. // if the device does not have a gateway. In that case, we will ignore
  9615. // the device. Otherwise the address should be filled in with the
  9616. // gateway or broadcast address.
  9617. //
  9618. fTryRemote = this->GetAddressToReachGateway(pDevice,
  9619. &saddrinRemote.sin_addr);
  9620. }
  9621. }
  9622. else
  9623. {
  9624. fTryRemote = FALSE;
  9625. }
  9626. ZeroMemory(&saddrinLocal, sizeof(saddrinLocal));
  9627. saddrinLocal.sin_family = AF_INET;
  9628. saddrinLocal.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  9629. saddrinLocal.sin_port = HTONS(UPNP_PORT);
  9630. //
  9631. // Note that these message strings contain:
  9632. //
  9633. // HOST: multicast_addr:port
  9634. //
  9635. // even though we send the messages to addresses other than the multicast
  9636. // address. It shouldn't matter.
  9637. //
  9638. iWANIPConnectionMsgSize = strlen(c_szUPnPMsg_Discover_Service_WANIPConnection);
  9639. iWANPPPConnectionMsgSize = strlen(c_szUPnPMsg_Discover_Service_WANPPPConnection);
  9640. #ifdef DBG
  9641. this->PrintUPnPTransactionToFile(c_szUPnPMsg_Discover_Service_WANIPConnection,
  9642. iWANIPConnectionMsgSize,
  9643. "Outbound WANIPConnection discovery messages",
  9644. pDevice);
  9645. this->PrintUPnPTransactionToFile(c_szUPnPMsg_Discover_Service_WANPPPConnection,
  9646. iWANPPPConnectionMsgSize,
  9647. "Outbound WANPPPConnection discovery messages",
  9648. pDevice);
  9649. #endif // DBG
  9650. DNASSERT(pDevice->GetUPnPDiscoverySocket() != INVALID_SOCKET);
  9651. //
  9652. // First, fire off messages to the remote gateway, if possible.
  9653. //
  9654. if (fTryRemote)
  9655. {
  9656. DPFX(DPFPREP, 7, "Sending UPnP discovery messages (WANIPConnection and WANPPPConnection) to gateway/multicast %u.%u.%u.%u:%u via device 0x%p.",
  9657. saddrinRemote.sin_addr.S_un.S_un_b.s_b1,
  9658. saddrinRemote.sin_addr.S_un.S_un_b.s_b2,
  9659. saddrinRemote.sin_addr.S_un.S_un_b.s_b3,
  9660. saddrinRemote.sin_addr.S_un.S_un_b.s_b4,
  9661. NTOHS(saddrinRemote.sin_port),
  9662. pDevice);
  9663. //
  9664. // Remember that we're trying remotely.
  9665. //
  9666. pDevice->NotePerformingRemoteUPnPDiscovery();
  9667. //
  9668. // Remember the current time, if this is the first thing we've sent
  9669. // from this port.
  9670. //
  9671. if (pDevice->GetFirstUPnPDiscoveryTime() == 0)
  9672. {
  9673. pDevice->SetFirstUPnPDiscoveryTime(GETTIMESTAMP());
  9674. }
  9675. //
  9676. // Multicast/send to gateway a WANIPConnection discovery message.
  9677. //
  9678. iReturn = this->m_pfnsendto(pDevice->GetUPnPDiscoverySocket(),
  9679. c_szUPnPMsg_Discover_Service_WANIPConnection,
  9680. iWANIPConnectionMsgSize,
  9681. 0,
  9682. (SOCKADDR*) (&saddrinRemote),
  9683. sizeof(saddrinRemote));
  9684. if (iReturn == SOCKET_ERROR)
  9685. {
  9686. #ifdef DBG
  9687. dwError = this->m_pfnWSAGetLastError();
  9688. DPFX(DPFPREP, 0, "Got sockets error %u when sending WANIPConnection discovery to UPnP gateway/multicast address on device 0x%p! Ignoring.",
  9689. dwError, pDevice);
  9690. #endif // DBG
  9691. //
  9692. // It's possible that we caught WinSock at a bad time,
  9693. // particularly with WSAEADDRNOTAVAIL (10049), which seems to
  9694. // occur if the address is going away (and we haven't detected
  9695. // it in CheckForNewDevices yet).
  9696. //
  9697. // Ignore the error, we can survive.
  9698. //
  9699. }
  9700. else
  9701. {
  9702. if (iReturn != iWANIPConnectionMsgSize)
  9703. {
  9704. DPFX(DPFPREP, 0, "Didn't multicast send entire WANIPConnection discovery datagram on device 0x%p (%i != %i)?!",
  9705. pDevice, iReturn, iWANIPConnectionMsgSize);
  9706. DNASSERT(FALSE);
  9707. hr = DPNHERR_GENERIC;
  9708. goto Failure;
  9709. }
  9710. }
  9711. //
  9712. // Multicast/send to gateway a WANPPPConnection discovery message.
  9713. //
  9714. iReturn = this->m_pfnsendto(pDevice->GetUPnPDiscoverySocket(),
  9715. c_szUPnPMsg_Discover_Service_WANPPPConnection,
  9716. iWANPPPConnectionMsgSize,
  9717. 0,
  9718. (SOCKADDR*) (&saddrinRemote),
  9719. sizeof(saddrinRemote));
  9720. if (iReturn == SOCKET_ERROR)
  9721. {
  9722. #ifdef DBG
  9723. dwError = this->m_pfnWSAGetLastError();
  9724. DPFX(DPFPREP, 0, "Got sockets error %u when sending WANPPPConnection discovery to UPnP multicast/gateway address on device 0x%p! Ignoring.",
  9725. dwError, pDevice);
  9726. #endif // DBG
  9727. //
  9728. // It's possible that we caught WinSock at a bad time,
  9729. // particularly with WSAEADDRNOTAVAIL (10049), which seems to
  9730. // occur if the address is going away (and we haven't detected
  9731. // it in CheckForNewDevices yet).
  9732. //
  9733. // Ignore the error, we can survive.
  9734. //
  9735. }
  9736. else
  9737. {
  9738. if (iReturn != iWANPPPConnectionMsgSize)
  9739. {
  9740. DPFX(DPFPREP, 0, "Didn't multicast send entire WANPPPConnection discovery datagram on device 0x%p (%i != %i)?!",
  9741. pDevice, iReturn, iWANPPPConnectionMsgSize);
  9742. DNASSERT(FALSE);
  9743. hr = DPNHERR_GENERIC;
  9744. goto Failure;
  9745. }
  9746. }
  9747. }
  9748. else
  9749. {
  9750. DPFX(DPFPREP, 2, "Device 0x%p should not attempt to reach a remote gateway.",
  9751. pDevice);
  9752. //
  9753. // Remember that we're not trying remotely.
  9754. //
  9755. pDevice->NoteNotPerformingRemoteUPnPDiscovery();
  9756. }
  9757. //
  9758. // If we didn't already get a CONNRESET from a previous attempt, try to
  9759. // bind a socket locally to the UPnP discovery port. If it's not in use,
  9760. // then we know nobody will be listening so there's no point in trying the
  9761. // local address. If it is in use, then this computer might be a UPnP
  9762. // gateway itself.
  9763. //
  9764. if (! pDevice->GotLocalUPnPDiscoveryConnReset())
  9765. {
  9766. sTemp = this->m_pfnsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  9767. if (sTemp == INVALID_SOCKET)
  9768. {
  9769. #ifdef DBG
  9770. dwError = this->m_pfnWSAGetLastError();
  9771. DPFX(DPFPREP, 0, "Couldn't create temporary datagram socket, error = %u!", dwError);
  9772. #endif // DBG
  9773. hr = DPNHERR_GENERIC;
  9774. goto Failure;
  9775. }
  9776. if (this->m_pfnbind(sTemp,
  9777. (SOCKADDR *) (&saddrinLocal),
  9778. sizeof(saddrinLocal)) != 0)
  9779. {
  9780. #ifdef DBG
  9781. dwError = this->m_pfnWSAGetLastError();
  9782. DPFX(DPFPREP, 2, "Couldn't bind socket to UPnP discovery port (%u.%u.%u.%u:%u), assuming local device (error = %u).",
  9783. saddrinLocal.sin_addr.S_un.S_un_b.s_b1,
  9784. saddrinLocal.sin_addr.S_un.S_un_b.s_b2,
  9785. saddrinLocal.sin_addr.S_un.S_un_b.s_b3,
  9786. saddrinLocal.sin_addr.S_un.S_un_b.s_b4,
  9787. NTOHS(saddrinLocal.sin_port),
  9788. dwError);
  9789. #endif // DBG
  9790. //
  9791. // Remember that we're trying locally.
  9792. //
  9793. pDevice->NotePerformingLocalUPnPDiscovery();
  9794. //
  9795. // Remember the current time, if this is the first thing we've sent
  9796. // from this port.
  9797. //
  9798. if (pDevice->GetFirstUPnPDiscoveryTime() == 0)
  9799. {
  9800. pDevice->SetFirstUPnPDiscoveryTime(GETTIMESTAMP());
  9801. }
  9802. //
  9803. // Do WANIPConnection first.
  9804. //
  9805. DPFX(DPFPREP, 7, "Sending UPnP discovery messages (WANIPConnection and WANPPPConnection) locally to device 0x%p (address %u.%u.%u.%u:%u).",
  9806. pDevice,
  9807. saddrinLocal.sin_addr.S_un.S_un_b.s_b1,
  9808. saddrinLocal.sin_addr.S_un.S_un_b.s_b2,
  9809. saddrinLocal.sin_addr.S_un.S_un_b.s_b3,
  9810. saddrinLocal.sin_addr.S_un.S_un_b.s_b4,
  9811. NTOHS(saddrinLocal.sin_port));
  9812. iReturn = this->m_pfnsendto(pDevice->GetUPnPDiscoverySocket(),
  9813. c_szUPnPMsg_Discover_Service_WANIPConnection,
  9814. iWANIPConnectionMsgSize,
  9815. 0,
  9816. (SOCKADDR*) (&saddrinLocal),
  9817. sizeof(saddrinLocal));
  9818. if (iReturn == SOCKET_ERROR)
  9819. {
  9820. #ifdef DBG
  9821. dwError = this->m_pfnWSAGetLastError();
  9822. DPFX(DPFPREP, 0, "Got sockets error %u when sending WANIPConnection discovery to local address on device 0x%p! Ignoring.",
  9823. dwError, pDevice);
  9824. #endif // DBG
  9825. //
  9826. // It's possible that we caught WinSock at a bad time,
  9827. // particularly with WSAEADDRNOTAVAIL (10049), which seems to
  9828. // occur if the address is going away (and we haven't detected
  9829. // it in CheckForNewDevices yet).
  9830. //
  9831. // Ignore the error, we can survive.
  9832. //
  9833. }
  9834. else
  9835. {
  9836. if (iReturn != iWANIPConnectionMsgSize)
  9837. {
  9838. DPFX(DPFPREP, 0, "Didn't send entire WANIPConnection discovery datagram locally on device 0x%p (%i != %i)?!",
  9839. pDevice, iReturn, iWANIPConnectionMsgSize);
  9840. DNASSERT(FALSE);
  9841. hr = DPNHERR_GENERIC;
  9842. goto Failure;
  9843. }
  9844. }
  9845. //
  9846. // Now send WANPPPConnection discovery message locally.
  9847. //
  9848. iReturn = this->m_pfnsendto(pDevice->GetUPnPDiscoverySocket(),
  9849. c_szUPnPMsg_Discover_Service_WANPPPConnection,
  9850. iWANPPPConnectionMsgSize,
  9851. 0,
  9852. (SOCKADDR*) (&saddrinLocal),
  9853. sizeof(saddrinLocal));
  9854. if (iReturn == SOCKET_ERROR)
  9855. {
  9856. #ifdef DBG
  9857. dwError = this->m_pfnWSAGetLastError();
  9858. DPFX(DPFPREP, 0, "Got sockets error %u when sending WANPPPConnection discovery to local address on device 0x%p! Ignoring.",
  9859. dwError, pDevice);
  9860. #endif // DBG
  9861. //
  9862. // It's possible that we caught WinSock at a bad time,
  9863. // particularly with WSAEADDRNOTAVAIL (10049), which seems to
  9864. // occur if the address is going away (and we haven't detected
  9865. // it in CheckForNewDevices yet).
  9866. //
  9867. // Ignore the error, we can survive.
  9868. //
  9869. }
  9870. else
  9871. {
  9872. if (iReturn != iWANPPPConnectionMsgSize)
  9873. {
  9874. DPFX(DPFPREP, 0, "Didn't send entire WANPPPConnection discovery datagram locally on device 0x%p (%i != %i)?!",
  9875. pDevice, iReturn, iWANPPPConnectionMsgSize);
  9876. DNASSERT(FALSE);
  9877. hr = DPNHERR_GENERIC;
  9878. goto Failure;
  9879. }
  9880. }
  9881. }
  9882. else
  9883. {
  9884. DPFX(DPFPREP, 2, "Successfully bound socket to UPnP discovery port (%u.%u.%u.%u:%u), assuming no local UPnP device.",
  9885. saddrinLocal.sin_addr.S_un.S_un_b.s_b1,
  9886. saddrinLocal.sin_addr.S_un.S_un_b.s_b2,
  9887. saddrinLocal.sin_addr.S_un.S_un_b.s_b3,
  9888. saddrinLocal.sin_addr.S_un.S_un_b.s_b4,
  9889. NTOHS(saddrinLocal.sin_port));
  9890. //
  9891. // Remember that we're not trying locally.
  9892. //
  9893. pDevice->NoteNotPerformingLocalUPnPDiscovery();
  9894. }
  9895. }
  9896. else
  9897. {
  9898. //
  9899. // We got a CONNRESET last time.
  9900. //
  9901. }
  9902. Exit:
  9903. if (sTemp != INVALID_SOCKET)
  9904. {
  9905. this->m_pfnclosesocket(sTemp);
  9906. sTemp = INVALID_SOCKET;
  9907. }
  9908. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  9909. return hr;
  9910. Failure:
  9911. goto Exit;
  9912. } // CNATHelpUPnP::SendUPnPSearchMessagesForDevice
  9913. #undef DPF_MODNAME
  9914. #define DPF_MODNAME "CNATHelpUPnP::SendUPnPDescriptionRequest"
  9915. //=============================================================================
  9916. // CNATHelpUPnP::SendUPnPDescriptionRequest
  9917. //-----------------------------------------------------------------------------
  9918. //
  9919. // Description: Requests a description from the given UPnP device.
  9920. //
  9921. // The object lock is assumed to be held.
  9922. //
  9923. // Arguments:
  9924. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device to use.
  9925. //
  9926. // Returns: HRESULT
  9927. // DPNH_OK - The message was sent successfully.
  9928. // DPNHERR_GENERIC - An error occurred.
  9929. //=============================================================================
  9930. HRESULT CNATHelpUPnP::SendUPnPDescriptionRequest(CUPnPDevice * const pUPnPDevice)
  9931. {
  9932. HRESULT hr = DPNH_OK;
  9933. SOCKADDR_IN * psaddrinHost;
  9934. TCHAR tszHost[22]; // "xxx.xxx.xxx.xxx:xxxxx" + NULL termination
  9935. char * pszMessage = NULL;
  9936. int iMsgSize;
  9937. int iReturn;
  9938. #ifdef DBG
  9939. DWORD dwError;
  9940. #endif // DBG
  9941. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pUPnPDevice);
  9942. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  9943. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  9944. DNASSERT(pUPnPDevice->IsConnected());
  9945. DNASSERT(pUPnPDevice->GetLocationURL() != NULL);
  9946. psaddrinHost = pUPnPDevice->GetHostAddress();
  9947. wsprintf(tszHost, _T("%u.%u.%u.%u:%u"),
  9948. psaddrinHost->sin_addr.S_un.S_un_b.s_b1,
  9949. psaddrinHost->sin_addr.S_un.S_un_b.s_b2,
  9950. psaddrinHost->sin_addr.S_un.S_un_b.s_b3,
  9951. psaddrinHost->sin_addr.S_un.S_un_b.s_b4,
  9952. NTOHS(psaddrinHost->sin_port));
  9953. iMsgSize = strlen("GET ") + strlen(pUPnPDevice->GetLocationURL()) + strlen(" " HTTP_VERSION EOL)
  9954. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  9955. + strlen("ACCEPT-LANGUAGE: en" EOL)
  9956. + strlen(EOL);
  9957. pszMessage = (char*) DNMalloc(iMsgSize + 1); // include room for NULL termination that isn't actually sent
  9958. if (pszMessage == NULL)
  9959. {
  9960. hr = DPNHERR_OUTOFMEMORY;
  9961. goto Failure;
  9962. }
  9963. strcpy(pszMessage, "GET ");
  9964. strcat(pszMessage, pUPnPDevice->GetLocationURL());
  9965. strcat(pszMessage, " " HTTP_VERSION EOL);
  9966. strcat(pszMessage, "HOST: ");
  9967. #ifdef UNICODE
  9968. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  9969. tszHost,
  9970. (_tcslen(tszHost) + 1));
  9971. #else // ! UNICODE
  9972. strcat(pszMessage, tszHost);
  9973. #endif // ! UNICODE
  9974. strcat(pszMessage, EOL);
  9975. strcat(pszMessage, "ACCEPT-LANGUAGE: en" EOL);
  9976. strcat(pszMessage, EOL);
  9977. #ifdef DBG
  9978. this->PrintUPnPTransactionToFile(pszMessage,
  9979. iMsgSize,
  9980. "Outbound description request",
  9981. pUPnPDevice->GetOwningDevice());
  9982. #endif // DBG
  9983. iReturn = this->m_pfnsend(pUPnPDevice->GetControlSocket(),
  9984. pszMessage,
  9985. iMsgSize,
  9986. 0);
  9987. if (iReturn == SOCKET_ERROR)
  9988. {
  9989. #ifdef DBG
  9990. dwError = this->m_pfnWSAGetLastError();
  9991. DPFX(DPFPREP, 0, "Got sockets error %u when sending to UPnP device!", dwError);
  9992. #endif // DBG
  9993. hr = DPNHERR_GENERIC;
  9994. goto Failure;
  9995. }
  9996. if (iReturn != iMsgSize)
  9997. {
  9998. DPFX(DPFPREP, 0, "Didn't send entire message (%i != %i)?!", iReturn, iMsgSize);
  9999. DNASSERT(FALSE);
  10000. hr = DPNHERR_GENERIC;
  10001. goto Failure;
  10002. }
  10003. Exit:
  10004. if (pszMessage != NULL)
  10005. {
  10006. DNFree(pszMessage);
  10007. pszMessage = NULL;
  10008. }
  10009. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  10010. return hr;
  10011. Failure:
  10012. goto Exit;
  10013. } // CNATHelpUPnP::SendUPnPDescriptionRequest
  10014. #undef DPF_MODNAME
  10015. #define DPF_MODNAME "CNATHelpUPnP::UpdateUPnPExternalAddress"
  10016. //=============================================================================
  10017. // CNATHelpUPnP::UpdateUPnPExternalAddress
  10018. //-----------------------------------------------------------------------------
  10019. //
  10020. // Description: Retreives the current external address for the given UPnP
  10021. // Internet Gateway Device.
  10022. //
  10023. // The UPnP device may get removed from list if a failure
  10024. // occurs, the caller needs to have a reference.
  10025. //
  10026. // The object lock is assumed to be held.
  10027. //
  10028. // Arguments:
  10029. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device that should be
  10030. // updated.
  10031. // BOOL fUpdateRegisteredPorts - TRUE if existing registered ports should be
  10032. // updated to reflect the new address if it
  10033. // changed, FALSE if not.
  10034. //
  10035. // Returns: HRESULT
  10036. // DPNH_OK - The update was successful.
  10037. // DPNHERR_GENERIC - An error occurred.
  10038. // DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
  10039. // message.
  10040. //=============================================================================
  10041. HRESULT CNATHelpUPnP::UpdateUPnPExternalAddress(CUPnPDevice * const pUPnPDevice,
  10042. const BOOL fUpdateRegisteredPorts)
  10043. {
  10044. HRESULT hr;
  10045. BOOL fStartedWaitingForControlResponse = FALSE;
  10046. CDevice * pDevice;
  10047. SOCKADDR_IN * psaddrinTemp;
  10048. int iContentLength;
  10049. TCHAR tszContentLength[32];
  10050. TCHAR tszHost[22]; // "xxx.xxx.xxx.xxx:xxxxx" + NULL termination
  10051. char * pszMessage = NULL;
  10052. int iMsgSize;
  10053. int iPrevMsgSize = 0;
  10054. int iReturn;
  10055. UPNP_CONTROLRESPONSE_INFO RespInfo;
  10056. DWORD dwStartTime;
  10057. DWORD dwTimeout;
  10058. CBilink * pBilink;
  10059. CRegisteredPort * pRegisteredPort;
  10060. #ifdef DBG
  10061. DWORD dwError;
  10062. #endif // DBG
  10063. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)",
  10064. this, pUPnPDevice, fUpdateRegisteredPorts);
  10065. DNASSERT(pUPnPDevice != NULL);
  10066. DNASSERT(pUPnPDevice->IsReady());
  10067. pDevice = pUPnPDevice->GetOwningDevice();
  10068. DNASSERT(pDevice != NULL);
  10069. DNASSERT(this->m_dwFlags & (NATHELPUPNPOBJ_INITIALIZED | NATHELPUPNPOBJ_USEUPNP));
  10070. DNASSERT(pUPnPDevice->GetServiceControlURL() != NULL);
  10071. //
  10072. // If the control socket got disconnected after the last message, then
  10073. // reconnect.
  10074. //
  10075. if (! pUPnPDevice->IsConnected())
  10076. {
  10077. hr = this->ReconnectUPnPControlSocket(pUPnPDevice);
  10078. if (hr != S_OK)
  10079. {
  10080. DPFX(DPFPREP, 0, "Couldn't reconnect UPnP control socket!");
  10081. goto Failure;
  10082. }
  10083. }
  10084. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  10085. psaddrinTemp = pUPnPDevice->GetHostAddress();
  10086. wsprintf(tszHost, _T("%u.%u.%u.%u:%u"),
  10087. psaddrinTemp->sin_addr.S_un.S_un_b.s_b1,
  10088. psaddrinTemp->sin_addr.S_un.S_un_b.s_b2,
  10089. psaddrinTemp->sin_addr.S_un.S_un_b.s_b3,
  10090. psaddrinTemp->sin_addr.S_un.S_un_b.s_b4,
  10091. NTOHS(psaddrinTemp->sin_port));
  10092. /*
  10093. iContentLength = strlen("<s:Envelope" EOL)
  10094. + strlen(" xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL)
  10095. + strlen(" s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL)
  10096. + strlen(" <s:Body>" EOL)
  10097. + strlen(" <u:" CONTROL_QUERYSTATEVARIABLE_A " xmlns:u=\"" URI_CONTROL_A "\">" EOL)
  10098. + strlen(" <u:" ARG_CONTROL_VARNAME_A ">" VAR_EXTERNALIPADDRESS_A "</u:" ARG_CONTROL_VARNAME_A ">" EOL)
  10099. + strlen(" </u:" CONTROL_QUERYSTATEVARIABLE_A ">" EOL)
  10100. + strlen(" </s:Body>" EOL)
  10101. + strlen("</s:Envelope>" EOL)
  10102. + strlen(EOL);
  10103. wsprintf(tszContentLength, _T("%i"), iContentLength);
  10104. iMsgSize = strlen("POST ") + strlen(pUPnPDevice->GetServiceControlURL()) + strlen(" " HTTP_VERSION EOL)
  10105. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  10106. + strlen("CONTENT-LENGTH: ") + strlen(szContentLength) + strlen(EOL)
  10107. + strlen("CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL)
  10108. + strlen("SOAPACTION: " URI_CONTROL_A "#" CONTROL_QUERYSTATEVARIABLE_A "" EOL)
  10109. + strlen(EOL)
  10110. + iContentLength;
  10111. */
  10112. iContentLength = strlen("<s:Envelope" EOL)
  10113. + strlen(" xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL)
  10114. + strlen(" s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL)
  10115. + strlen(" <s:Body>" EOL)
  10116. + strlen(" <u:" ACTION_GETEXTERNALIPADDRESS_A " xmlns:u=\"") + pUPnPDevice->GetStaticServiceURILength() + strlen("\">" EOL)
  10117. + strlen(" </u:" ACTION_GETEXTERNALIPADDRESS_A ">" EOL)
  10118. + strlen(" </s:Body>" EOL)
  10119. + strlen("</s:Envelope>" EOL)
  10120. + strlen(EOL);
  10121. wsprintf(tszContentLength, _T("%i"), iContentLength);
  10122. iMsgSize = strlen("POST ") + strlen(pUPnPDevice->GetServiceControlURL()) + strlen(" " HTTP_VERSION EOL)
  10123. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  10124. + strlen("CONTENT-LENGTH: ") + _tcslen(tszContentLength) + strlen(EOL)
  10125. + strlen("CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL)
  10126. + strlen("SOAPACTION: ") + pUPnPDevice->GetStaticServiceURILength() + strlen("#" ACTION_GETEXTERNALIPADDRESS_A EOL)
  10127. + strlen(EOL)
  10128. + iContentLength;
  10129. //
  10130. // Allocate (or reallocate) the message buffer.
  10131. //
  10132. if (iMsgSize > iPrevMsgSize)
  10133. {
  10134. if (pszMessage != NULL)
  10135. {
  10136. DNFree(pszMessage);
  10137. pszMessage = NULL;
  10138. }
  10139. pszMessage = (char*) DNMalloc(iMsgSize + 1); // include room for NULL termination that isn't actually sent
  10140. if (pszMessage == NULL)
  10141. {
  10142. hr = DPNHERR_OUTOFMEMORY;
  10143. goto Failure;
  10144. }
  10145. iPrevMsgSize = iMsgSize;
  10146. }
  10147. /*
  10148. strcpy(pszMessage, "POST ");
  10149. strcat(pszMessage, pUPnPDevice->GetServiceControlURL());
  10150. strcat(pszMessage, " " HTTP_VERSION EOL);
  10151. strcat(pszMessage, "HOST: ");
  10152. #ifdef UNICODE
  10153. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10154. tszHost,
  10155. (_tcslen(tszHost) + 1));
  10156. #else // ! UNICODE
  10157. strcat(pszMessage, tszHost);
  10158. #endif // ! UNICODE
  10159. strcat(pszMessage, EOL);
  10160. strcat(pszMessage, "CONTENT-LENGTH: ");
  10161. #ifdef UNICODE
  10162. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10163. tszContentLength,
  10164. (_tcslen(tszContentLength) + 1));
  10165. #else // ! UNICODE
  10166. strcat(pszMessage, tszContentLength);
  10167. #endif // ! UNICODE
  10168. strcat(pszMessage, EOL);
  10169. strcat(pszMessage, "CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL);
  10170. strcat(pszMessage, "SOAPACTION: " URI_CONTROL_A "#" CONTROL_QUERYSTATEVARIABLE_A "" EOL);
  10171. strcat(pszMessage, EOL);
  10172. strcat(pszMessage, "<s:Envelope" EOL);
  10173. strcat(pszMessage, " xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL);
  10174. strcat(pszMessage, " s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL);
  10175. strcat(pszMessage, " <s:Body>" EOL);
  10176. strcat(pszMessage, " <u:" CONTROL_QUERYSTATEVARIABLE_A " xmlns:u=\"" URI_CONTROL_A "\">" EOL);
  10177. strcat(pszMessage, " <u:" ARG_CONTROL_VARNAME_A ">" VAR_EXTERNALIPADDRESS_A "</u:" ARG_CONTROL_VARNAME_A ">" EOL);
  10178. strcat(pszMessage, " </u:" CONTROL_QUERYSTATEVARIABLE_A ">" EOL);
  10179. strcat(pszMessage, " </s:Body>" EOL);
  10180. strcat(pszMessage, "</s:Envelope>" EOL);
  10181. strcat(pszMessage, EOL);
  10182. */
  10183. strcpy(pszMessage, "POST ");
  10184. strcat(pszMessage, pUPnPDevice->GetServiceControlURL());
  10185. strcat(pszMessage, " " HTTP_VERSION EOL);
  10186. strcat(pszMessage, "HOST: ");
  10187. #ifdef UNICODE
  10188. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10189. tszHost,
  10190. (_tcslen(tszHost) + 1));
  10191. #else // ! UNICODE
  10192. strcat(pszMessage, tszHost);
  10193. #endif // ! UNICODE
  10194. strcat(pszMessage, EOL);
  10195. strcat(pszMessage, "CONTENT-LENGTH: ");
  10196. #ifdef UNICODE
  10197. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10198. tszContentLength,
  10199. (_tcslen(tszContentLength) + 1));
  10200. #else // ! UNICODE
  10201. strcat(pszMessage, tszContentLength);
  10202. #endif // ! UNICODE
  10203. strcat(pszMessage, EOL);
  10204. strcat(pszMessage, "CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL);
  10205. strcat(pszMessage, "SOAPACTION: ");
  10206. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  10207. strcat(pszMessage, "#" ACTION_GETEXTERNALIPADDRESS_A EOL);
  10208. strcat(pszMessage, EOL);
  10209. strcat(pszMessage, "<s:Envelope" EOL);
  10210. strcat(pszMessage, " xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL);
  10211. strcat(pszMessage, " s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL);
  10212. strcat(pszMessage, " <s:Body>" EOL);
  10213. strcat(pszMessage, " <u:" ACTION_GETEXTERNALIPADDRESS_A " xmlns:u=\"");
  10214. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  10215. strcat(pszMessage, "\">" EOL);
  10216. strcat(pszMessage, " </u:" ACTION_GETEXTERNALIPADDRESS_A ">" EOL);
  10217. strcat(pszMessage, " </s:Body>" EOL);
  10218. strcat(pszMessage, "</s:Envelope>" EOL);
  10219. strcat(pszMessage, EOL);
  10220. #ifdef DBG
  10221. this->PrintUPnPTransactionToFile(pszMessage,
  10222. iMsgSize,
  10223. //"Outbound query external IP address",
  10224. "Outbound get external IP address",
  10225. pDevice);
  10226. #endif // DBG
  10227. iReturn = this->m_pfnsend(pUPnPDevice->GetControlSocket(),
  10228. pszMessage,
  10229. iMsgSize,
  10230. 0);
  10231. if (iReturn == SOCKET_ERROR)
  10232. {
  10233. #ifdef DBG
  10234. dwError = this->m_pfnWSAGetLastError();
  10235. DPFX(DPFPREP, 0, "Got sockets error %u when sending control request to UPnP device!", dwError);
  10236. #endif // DBG
  10237. hr = DPNHERR_GENERIC;
  10238. goto Failure;
  10239. }
  10240. if (iReturn != iMsgSize)
  10241. {
  10242. DPFX(DPFPREP, 0, "Didn't send entire message (%i != %i)?!", iReturn, iMsgSize);
  10243. DNASSERT(FALSE);
  10244. hr = DPNHERR_GENERIC;
  10245. goto Failure;
  10246. }
  10247. //
  10248. // We have the lock so no one could have tried to receive data from the
  10249. // control socket yet. Mark the device as waiting for a response.
  10250. //
  10251. ZeroMemory(&RespInfo, sizeof(RespInfo));
  10252. //pUPnPDevice->StartWaitingForControlResponse(CONTROLRESPONSETYPE_QUERYSTATEVARIABLE_EXTERNALIPADDRESS,
  10253. pUPnPDevice->StartWaitingForControlResponse(CONTROLRESPONSETYPE_GETEXTERNALIPADDRESS,
  10254. &RespInfo);
  10255. fStartedWaitingForControlResponse = TRUE;
  10256. //
  10257. // Actually wait for the response.
  10258. //
  10259. dwStartTime = GETTIMESTAMP();
  10260. dwTimeout = g_dwUPnPResponseTimeout;
  10261. do
  10262. {
  10263. hr = this->CheckForReceivedUPnPMsgsOnDevice(pUPnPDevice, dwTimeout);
  10264. if (hr != DPNH_OK)
  10265. {
  10266. DPFX(DPFPREP, 0, "Failed receiving UPnP messages!");
  10267. goto Failure;
  10268. }
  10269. //
  10270. // We either timed out or got some data. Check if we got a
  10271. // response of some type.
  10272. //
  10273. if (! pUPnPDevice->IsWaitingForControlResponse())
  10274. {
  10275. break;
  10276. }
  10277. //
  10278. // Make sure our device is still connected.
  10279. //
  10280. if (! pUPnPDevice->IsConnected())
  10281. {
  10282. DPFX(DPFPREP, 0, "UPnP device 0x%p disconnected while retrieving external IP address!",
  10283. pUPnPDevice);
  10284. pUPnPDevice->StopWaitingForControlResponse();
  10285. hr = DPNHERR_SERVERNOTRESPONDING;
  10286. goto Failure;
  10287. }
  10288. //
  10289. // Calculate how long we have left to wait. If the calculation
  10290. // goes negative, it means we're done.
  10291. //
  10292. dwTimeout = g_dwUPnPResponseTimeout - (GETTIMESTAMP() - dwStartTime);
  10293. }
  10294. while (((int) dwTimeout > 0));
  10295. //
  10296. // If we never got the response, stop waiting for it.
  10297. //
  10298. if (pUPnPDevice->IsWaitingForControlResponse())
  10299. {
  10300. pUPnPDevice->StopWaitingForControlResponse();
  10301. DPFX(DPFPREP, 0, "Server didn't respond in time!");
  10302. hr = DPNHERR_SERVERNOTRESPONDING;
  10303. goto Failure;
  10304. }
  10305. //
  10306. // If we're here, then we've gotten a valid response from the server.
  10307. //
  10308. if (RespInfo.hrErrorCode != DPNH_OK)
  10309. {
  10310. DPFX(DPFPREP, 1, "Server returned failure response 0x%lx when retrieving external IP address.",
  10311. RespInfo.hrErrorCode);
  10312. hr = RespInfo.hrErrorCode;
  10313. goto Failure;
  10314. }
  10315. DPFX(DPFPREP, 1, "Server returned external IP address \"%u.%u.%u.%u\".",
  10316. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b1,
  10317. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b2,
  10318. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b3,
  10319. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b4);
  10320. //
  10321. // Convert the loopback address to the device address.
  10322. //
  10323. if (RespInfo.dwExternalIPAddressV4 == NETWORKBYTEORDER_INADDR_LOOPBACK)
  10324. {
  10325. RespInfo.dwExternalIPAddressV4 = pDevice->GetLocalAddressV4();
  10326. DPFX(DPFPREP, 0, "Converted private loopback address to device address (%u.%u.%u.%u)!",
  10327. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b1,
  10328. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b2,
  10329. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b3,
  10330. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b4);
  10331. DNASSERTX(! "Got loopback address as external IP address!", 2);
  10332. }
  10333. #ifdef DBG
  10334. //
  10335. // If this is a local UPnP gateway, print out the device corresponding to
  10336. // the public address.
  10337. //
  10338. if (pUPnPDevice->IsLocal())
  10339. {
  10340. if (RespInfo.dwExternalIPAddressV4 != 0)
  10341. {
  10342. CDevice * pPublicDevice;
  10343. //
  10344. // Loop through every device.
  10345. //
  10346. pBilink = this->m_blDevices.GetNext();
  10347. while (pBilink != &this->m_blDevices)
  10348. {
  10349. pPublicDevice = DEVICE_FROM_BILINK(pBilink);
  10350. if (pPublicDevice->GetLocalAddressV4() == RespInfo.dwExternalIPAddressV4)
  10351. {
  10352. DPFX(DPFPREP, 7, "Local UPnP gateway 0x%p for device 0x%p's public address is device 0x%p.",
  10353. pUPnPDevice, pDevice, pPublicDevice);
  10354. break;
  10355. }
  10356. pBilink = pBilink->GetNext();
  10357. }
  10358. //
  10359. // If we made it through the entire list without matching the device,
  10360. // that's odd. It's possible we're slow in detecting new devices, so
  10361. // don't get bent out of shape.
  10362. //
  10363. if (pBilink == &this->m_blDevices)
  10364. {
  10365. DPFX(DPFPREP, 0, "Couldn't match up local UPnP gateway 0x%p (device 0x%p)'s public address to a device!",
  10366. pUPnPDevice, pDevice);
  10367. DNASSERTX(! "Couldn't match up local UPnP gateway public address to a device!", 2);
  10368. }
  10369. }
  10370. else
  10371. {
  10372. DPFX(DPFPREP, 4, "Local UPnP gateway 0x%p (device 0x%p) does not have a valid public address.",
  10373. pUPnPDevice, pDevice);
  10374. }
  10375. }
  10376. #endif // DBG
  10377. //
  10378. // If the public address has changed, update all the existing mappings.
  10379. //
  10380. if (RespInfo.dwExternalIPAddressV4 != pUPnPDevice->GetExternalIPAddressV4())
  10381. {
  10382. DPFX(DPFPREP, 1, "UPnP Internet Gateway Device (0x%p) external address changed.",
  10383. pUPnPDevice);
  10384. //
  10385. // Since there was a change in the network, go back to polling
  10386. // relatively quickly.
  10387. //
  10388. this->ResetNextPollInterval();
  10389. //
  10390. // Loop through all the existing registered ports and update their
  10391. // public addresses, if allowed.
  10392. //
  10393. if (fUpdateRegisteredPorts)
  10394. {
  10395. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  10396. while (pBilink != &pDevice->m_blOwnedRegPorts)
  10397. {
  10398. DNASSERT(! pBilink->IsEmpty());
  10399. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  10400. if (! pRegisteredPort->IsUPnPPortUnavailable())
  10401. {
  10402. DPFX(DPFPREP, 7, "Updating registered port 0x%p's public address.",
  10403. pRegisteredPort);
  10404. pRegisteredPort->UpdateUPnPPublicV4Addresses(RespInfo.dwExternalIPAddressV4);
  10405. //
  10406. // The user should call GetCaps to detect the address change.
  10407. //
  10408. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  10409. }
  10410. else
  10411. {
  10412. DPFX(DPFPREP, 7, "Not updating registered port 0x%p's public address because the port is unavailable.",
  10413. pRegisteredPort);
  10414. }
  10415. pBilink = pBilink->GetNext();
  10416. }
  10417. }
  10418. //
  10419. // Store the new public address.
  10420. //
  10421. pUPnPDevice->SetExternalIPAddressV4(RespInfo.dwExternalIPAddressV4);
  10422. }
  10423. Exit:
  10424. if (pszMessage != NULL)
  10425. {
  10426. DNFree(pszMessage);
  10427. pszMessage = NULL;
  10428. }
  10429. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  10430. return hr;
  10431. Failure:
  10432. //
  10433. // If we started waiting for a response, clear that.
  10434. //
  10435. if (fStartedWaitingForControlResponse)
  10436. {
  10437. pUPnPDevice->StopWaitingForControlResponse();
  10438. }
  10439. goto Exit;
  10440. } // CNATHelpUPnP::UpdateUPnPExternalAddress
  10441. #undef DPF_MODNAME
  10442. #define DPF_MODNAME "CNATHelpUPnP::MapPortsOnUPnPDevice"
  10443. //=============================================================================
  10444. // CNATHelpUPnP::MapPortsOnUPnPDevice
  10445. //-----------------------------------------------------------------------------
  10446. //
  10447. // Description: Maps the given ports on the given UPnP device.
  10448. //
  10449. // The UPnP device may get removed from list if a failure
  10450. // occurs, the caller needs to have a reference.
  10451. //
  10452. // The object lock is assumed to be held.
  10453. //
  10454. // Arguments:
  10455. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device to use.
  10456. // CRegisteredPort * pRegisteredPort - Pointer to ports to register.
  10457. //
  10458. // Returns: HRESULT
  10459. // DPNH_OK - The message was sent successfully.
  10460. // DPNHERR_GENERIC - An error occurred.
  10461. // DPNHERR_SERVERNOTRESPONDING - A response didn't arrive in time.
  10462. //=============================================================================
  10463. HRESULT CNATHelpUPnP::MapPortsOnUPnPDevice(CUPnPDevice * const pUPnPDevice,
  10464. CRegisteredPort * const pRegisteredPort)
  10465. {
  10466. HRESULT hr = DPNH_OK;
  10467. HRESULT temphr;
  10468. BOOL fStartedWaitingForControlResponse = FALSE;
  10469. CDevice * pDevice;
  10470. DWORD dwLeaseExpiration;
  10471. IN_ADDR inaddrTemp;
  10472. SOCKADDR_IN * psaddrinTemp;
  10473. WORD wOriginalExternalPortHostOrder = 0;
  10474. WORD wExternalPortHostOrder;
  10475. TCHAR tszInternalPort[32];
  10476. TCHAR tszExternalPort[32];
  10477. TCHAR tszInternalClient[16]; // "xxx.xxx.xxx.xxx" + NULL termination
  10478. TCHAR tszLeaseDuration[32];
  10479. TCHAR tszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  10480. int iContentLength;
  10481. TCHAR tszContentLength[32];
  10482. TCHAR tszHost[22]; // "xxx.xxx.xxx.xxx:xxxxx" + NULL termination
  10483. char * pszMessage = NULL;
  10484. int iMsgSize;
  10485. int iPrevMsgSize = 0;
  10486. int iReturn;
  10487. DWORD dwTemp = 0;
  10488. UPNP_CONTROLRESPONSE_INFO RespInfo;
  10489. DWORD dwStartTime;
  10490. DWORD dwTimeout;
  10491. BOOL fFirstLease;
  10492. DWORD dwDescriptionLength;
  10493. #ifndef DPNBUILD_NOWINSOCK2
  10494. BOOL fResult;
  10495. #endif // ! DPNBUILD_NOWINSOCK2
  10496. #ifdef DBG
  10497. DWORD dwError;
  10498. #endif // DBG
  10499. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pUPnPDevice);
  10500. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  10501. DNASSERT(pUPnPDevice->IsReady());
  10502. pDevice = pRegisteredPort->GetOwningDevice();
  10503. DNASSERT(pDevice != NULL);
  10504. DNASSERT(pUPnPDevice->GetOwningDevice() == pDevice);
  10505. //
  10506. // Set up variables we'll need.
  10507. //
  10508. DNASSERT(pUPnPDevice->GetServiceControlURL() != NULL);
  10509. //
  10510. // If the port is shared, register the broadcast address instead of the
  10511. // local device address.
  10512. //
  10513. if (pRegisteredPort->IsSharedPort())
  10514. {
  10515. _tcscpy(tszInternalClient, _T("255.255.255.255"));
  10516. }
  10517. else
  10518. {
  10519. //
  10520. // Note that the device address is not necessarily the same as the
  10521. // address the user originally registered, particularly the 0.0.0.0
  10522. // wildcard address will get remapped.
  10523. //
  10524. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  10525. wsprintf(tszInternalClient, _T("%u.%u.%u.%u"),
  10526. inaddrTemp.S_un.S_un_b.s_b1,
  10527. inaddrTemp.S_un.S_un_b.s_b2,
  10528. inaddrTemp.S_un.S_un_b.s_b3,
  10529. inaddrTemp.S_un.S_un_b.s_b4);
  10530. }
  10531. //
  10532. // The current Microsoft UPnP Internet Gateway Device implementation--
  10533. // and most others-- do not support lease durations that are not set
  10534. // for INFINITE time, so we will almost certainly fail. Because of
  10535. // that, and since there's no server to test against anyway, we will
  10536. // not even attempt to use non-INFINITE leases. However, you can use
  10537. // the registry key to take a shot at it if you like living on the
  10538. // edge.
  10539. // Note that there's no way to detect whether a given UPnP
  10540. // implementation will allow non-INFINITE lease durations ahead of time,
  10541. // so we have to try it first, and fall back to the infinite lease
  10542. // behavior if it doesn't work. Ugh.
  10543. //
  10544. if ((! pUPnPDevice->DoesNotSupportLeaseDurations()) && (g_fUseLeaseDurations))
  10545. {
  10546. wsprintf(tszLeaseDuration, _T("%u"),
  10547. (pRegisteredPort->GetRequestedLeaseTime() / 1000));
  10548. }
  10549. else
  10550. {
  10551. _tcscpy(tszLeaseDuration, _T("0"));
  10552. pRegisteredPort->NotePermanentUPnPLease();
  10553. }
  10554. psaddrinTemp = pUPnPDevice->GetHostAddress();
  10555. wsprintf(tszHost, _T("%u.%u.%u.%u:%u"),
  10556. psaddrinTemp->sin_addr.S_un.S_un_b.s_b1,
  10557. psaddrinTemp->sin_addr.S_un.S_un_b.s_b2,
  10558. psaddrinTemp->sin_addr.S_un.S_un_b.s_b3,
  10559. psaddrinTemp->sin_addr.S_un.S_un_b.s_b4,
  10560. NTOHS(psaddrinTemp->sin_port));
  10561. //
  10562. // Create the array to hold the resulting public addresses.
  10563. //
  10564. hr = pRegisteredPort->CreateUPnPPublicAddressesArray();
  10565. if (hr != DPNH_OK)
  10566. {
  10567. DPFX(DPFPREP, 0, "Couldn't create UPnP public addresses array!");
  10568. goto Failure;
  10569. }
  10570. //
  10571. // Note whether this was the first lease or not.
  10572. //
  10573. fFirstLease = (this->m_dwNumLeases == 0) ? TRUE : FALSE;
  10574. this->m_dwNumLeases++;
  10575. DPFX(DPFPREP, 7, "UPnP lease for 0x%p added, total num leases = %u.",
  10576. pRegisteredPort, this->m_dwNumLeases);
  10577. //
  10578. // Assuming all goes well, the first port lease will expire approximately
  10579. // GetRequestedLeaseTime() ms from now.
  10580. // See above note about whether this lease will actually be used, though.
  10581. //
  10582. dwLeaseExpiration = GETTIMESTAMP() + pRegisteredPort->GetRequestedLeaseTime();
  10583. //
  10584. // Get a pointer to the addresses we're mapping. We don't have to worry
  10585. // about whether it's mapped on a local firewall since the HomeNet API will
  10586. // always map it to the same port.
  10587. //
  10588. psaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
  10589. //
  10590. // Loop through each port and map it.
  10591. //
  10592. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  10593. {
  10594. //
  10595. // Determine the public port number to register.
  10596. //
  10597. if (! pRegisteredPort->IsFixedPort())
  10598. {
  10599. //
  10600. // UPnP does not support wildcard ports (where the gateway
  10601. // device picks an unused public port number for us). We must
  10602. // select a port ahead of time to try mapping on the server.
  10603. //
  10604. // Worse, UPnP does not require the device support selecting a
  10605. // public port that is different from the client's private port
  10606. // (a.k.a. asymmetric, x to y, or floating port mappings).
  10607. // This means that even non fixed ports will act that way. To
  10608. // top it all off, there's no way to detect whether a given
  10609. // UPnP implementation will allow the ports to differ ahead of
  10610. // time, so we have to try it first, and fall back to the fixed
  10611. // port behavior if it doesn't work. Ugh.
  10612. //
  10613. if (pUPnPDevice->DoesNotSupportAsymmetricMappings())
  10614. {
  10615. //
  10616. // We are forced to use the client's private port.
  10617. //
  10618. wExternalPortHostOrder = NTOHS(psaddrinTemp[dwTemp].sin_port);
  10619. }
  10620. else
  10621. {
  10622. if (wOriginalExternalPortHostOrder == 0)
  10623. {
  10624. DNASSERT(dwTemp == 0);
  10625. //
  10626. // Ideally we would pick a random port that isn't in
  10627. // the reserved range (i.e. is greater than 1024).
  10628. // However, truly random ports cause problems with the
  10629. // Windows XP ICS implementation in a fairly obscure
  10630. // manner:
  10631. //
  10632. // 1. DPlay application starts hosting behind ICS on
  10633. // port 2302.
  10634. // 2. Port 2302 gets mapped to random port x.
  10635. // 3. External DPlay client is told to connect to x.
  10636. // 4. ICS detects inbound traffic to x, sees mapping
  10637. // for internal_ip:2302, and creates a virtual
  10638. // connection.
  10639. // 5. Internal client removes 2302<->x mapping and
  10640. // closes socket.
  10641. // 6. Internal DPlay application begins hosting on
  10642. // port 2302 again.
  10643. // 7. Port 2302 gets mapped to random port y.
  10644. // 8. External DPlay client is told to connect to y.
  10645. // 9. ICS detects inbound traffic to y, sees mapping
  10646. // for internal_ip:2302, but can't create virtual
  10647. // connection because the 2302<->x connection
  10648. // still exists.
  10649. //
  10650. // Windows XP ICS keeps the virtual connections around
  10651. // and cleans them up every 60 seconds. If the
  10652. // reconnect occurs within (up to) 2 minutes, the
  10653. // packets might get dropped due to the mapping
  10654. // collision.
  10655. //
  10656. // This causes all sorts of heartache with automated
  10657. // NAT tests that bring up and tear down connections
  10658. // between the same two machines across the NAT over
  10659. // and over.
  10660. //
  10661. // So, to avoid that, we need to make mappings
  10662. // deterministic, such that if the same internal client
  10663. // tries to map the same internal port, it should ask
  10664. // for the same external port every time. That way, if
  10665. // there happens to be a virtual connection hanging
  10666. // around from a previous attempt, we'll reuse it
  10667. // instead of clashing and getting the packet dropped.
  10668. //
  10669. // Our actual algorithm:
  10670. // 1. start with the internal client IP.
  10671. // 2. combine in the internal port being mapped.
  10672. // 3. add 1025 if necessary to get it out of the
  10673. // reserved range.
  10674. //
  10675. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  10676. wOriginalExternalPortHostOrder = inaddrTemp.S_un.S_un_w.s_w1;
  10677. wOriginalExternalPortHostOrder ^= inaddrTemp.S_un.S_un_w.s_w2;
  10678. wOriginalExternalPortHostOrder ^= psaddrinTemp[dwTemp].sin_port;
  10679. if (wOriginalExternalPortHostOrder <= MAX_RESERVED_PORT)
  10680. {
  10681. wOriginalExternalPortHostOrder += MAX_RESERVED_PORT + 1;
  10682. }
  10683. //
  10684. // This is the starting point, we'll increment by one
  10685. // as we go.
  10686. //
  10687. wExternalPortHostOrder = wOriginalExternalPortHostOrder;
  10688. }
  10689. else
  10690. {
  10691. //
  10692. // Go to the next sequential port. If we've wrapped
  10693. // around to 0, move to the first non reserved range
  10694. // port.
  10695. //
  10696. wExternalPortHostOrder++;
  10697. if (wExternalPortHostOrder == 0)
  10698. {
  10699. wExternalPortHostOrder = MAX_RESERVED_PORT + 1;
  10700. }
  10701. //
  10702. // If we wrapped all the way back around to the first
  10703. // port we tried, we have to fail.
  10704. //
  10705. if (wExternalPortHostOrder == wOriginalExternalPortHostOrder)
  10706. {
  10707. DPFX(DPFPREP, 0, "All ports were exhausted (before index %u), marking port as unavailable.",
  10708. dwTemp);
  10709. //
  10710. // Delete all mappings successfully made up until
  10711. // now.
  10712. //
  10713. DNASSERT(dwTemp > 0);
  10714. hr = this->UnmapUPnPPort(pRegisteredPort, dwTemp, TRUE);
  10715. if (hr != DPNH_OK)
  10716. {
  10717. DPFX(DPFPREP, 0, "Failed deleting %u previously mapped ports after getting failure response 0x%lx!",
  10718. dwTemp, RespInfo.hrErrorCode);
  10719. goto Failure;
  10720. }
  10721. //
  10722. // The port is unavailable.
  10723. //
  10724. pRegisteredPort->NoteUPnPPortUnavailable();
  10725. //
  10726. // We're done here. Ideally we would goto Exit, but
  10727. // we want to execute the public address array
  10728. // cleanup code. hr will == DPNH_OK.
  10729. //
  10730. goto Failure;
  10731. }
  10732. } // end if (haven't selected first port yet)
  10733. }
  10734. }
  10735. else
  10736. {
  10737. //
  10738. // Use the fixed port.
  10739. //
  10740. wExternalPortHostOrder = NTOHS(psaddrinTemp[dwTemp].sin_port);
  10741. }
  10742. wsprintf(tszInternalPort, _T("%u"),
  10743. NTOHS(psaddrinTemp[dwTemp].sin_port));
  10744. Retry:
  10745. //
  10746. // Because the UPnP spec allows a device to overwrite existing
  10747. // mappings if they are for the same client, we have to make sure
  10748. // that no local DPNHUPNP instances, including this one, have an
  10749. // active mapping for that public port.
  10750. // Rather than having the retry code in multiple places, I'll use
  10751. // the somewhat ugly 'goto' to jump straight to the existing port
  10752. // unavailable handling.
  10753. //
  10754. if (this->IsNATPublicPortInUseLocally(wExternalPortHostOrder))
  10755. {
  10756. DPFX(DPFPREP, 1, "Port %u is already in use locally.");
  10757. RespInfo.hrErrorCode = DPNHERR_PORTUNAVAILABLE;
  10758. goto PortUnavailable;
  10759. }
  10760. //
  10761. // If the control socket got disconnected after the last message,
  10762. // then reconnect.
  10763. //
  10764. if (! pUPnPDevice->IsConnected())
  10765. {
  10766. hr = this->ReconnectUPnPControlSocket(pUPnPDevice);
  10767. if (hr != S_OK)
  10768. {
  10769. DPFX(DPFPREP, 0, "Couldn't reconnect UPnP control socket!");
  10770. goto Failure;
  10771. }
  10772. }
  10773. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  10774. wsprintf(tszExternalPort, _T("%u"), wExternalPortHostOrder);
  10775. //
  10776. // Generate a description for this mapping. The format is:
  10777. //
  10778. // [executable_name] (nnn.nnn.nnn.nnn:nnnnn) nnnnn {"TCP" | "UDP"}
  10779. //
  10780. // That way nothing needs to be localized.
  10781. //
  10782. dwDescriptionLength = GetModuleFileName(NULL,
  10783. tszDescription,
  10784. (MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1));
  10785. if (dwDescriptionLength != 0)
  10786. {
  10787. //
  10788. // Be paranoid and make sure the description string is valid.
  10789. //
  10790. tszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1] = 0;
  10791. //
  10792. // Get just the executable name from the path.
  10793. //
  10794. #ifdef WINCE
  10795. GetExeName(tszDescription);
  10796. #else // ! WINCE
  10797. #ifdef UNICODE
  10798. _wsplitpath(tszDescription, NULL, NULL, tszDescription, NULL);
  10799. #else // ! UNICODE
  10800. _splitpath(tszDescription, NULL, NULL, tszDescription, NULL);
  10801. #endif // ! UNICODE
  10802. #endif // ! WINCE
  10803. dwDescriptionLength = _tcslen(tszDescription) // executable name
  10804. + 2 // " ("
  10805. + _tcslen(tszInternalClient) // private IP address
  10806. + 1 // ":"
  10807. + _tcslen(tszInternalPort) // private port
  10808. + 2 // ") "
  10809. + _tcslen(tszExternalPort) // public port
  10810. + 4; // " TCP" | " UDP"
  10811. //
  10812. // Make sure the long string will fit. If not, use the
  10813. // abbreviated version.
  10814. //
  10815. if (dwDescriptionLength > MAX_UPNP_MAPPING_DESCRIPTION_SIZE)
  10816. {
  10817. dwDescriptionLength = 0;
  10818. }
  10819. }
  10820. if (dwDescriptionLength == 0)
  10821. {
  10822. //
  10823. // Use the abbreviated version we know will fit.
  10824. //
  10825. wsprintf(tszDescription,
  10826. _T("(%s:%s) %s %s"),
  10827. tszInternalClient,
  10828. tszInternalPort,
  10829. tszExternalPort,
  10830. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  10831. }
  10832. else
  10833. {
  10834. //
  10835. // There's enough room, tack on the rest of the description.
  10836. //
  10837. wsprintf((tszDescription + _tcslen(tszDescription)),
  10838. _T(" (%s:%s) %s %s"),
  10839. tszInternalClient,
  10840. tszInternalPort,
  10841. tszExternalPort,
  10842. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  10843. }
  10844. DPFX(DPFPREP, 6, "Requesting mapping \"%s\".", tszDescription);
  10845. iContentLength = strlen("<s:Envelope" EOL)
  10846. + strlen(" xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL)
  10847. + strlen(" s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL)
  10848. + strlen(" <s:Body>" EOL)
  10849. + strlen(" <u:" ACTION_ADDPORTMAPPING_A " xmlns:u=\"") + pUPnPDevice->GetStaticServiceURILength() + strlen("\">" EOL)
  10850. + strlen(" <" ARG_ADDPORTMAPPING_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_ADDPORTMAPPING_NEWREMOTEHOST_A ">" EOL)
  10851. + strlen(" <" ARG_ADDPORTMAPPING_NEWEXTERNALPORT_A ">") + _tcslen(tszExternalPort) + strlen("</" ARG_ADDPORTMAPPING_NEWEXTERNALPORT_A ">" EOL)
  10852. + strlen(" <" ARG_ADDPORTMAPPING_NEWPROTOCOL_A ">") + 3 + strlen("</" ARG_ADDPORTMAPPING_NEWPROTOCOL_A ">" EOL)
  10853. + strlen(" <" ARG_ADDPORTMAPPING_NEWINTERNALPORT_A ">") + _tcslen(tszInternalPort) + strlen("</" ARG_ADDPORTMAPPING_NEWINTERNALPORT_A ">" EOL)
  10854. + strlen(" <" ARG_ADDPORTMAPPING_NEWINTERNALCLIENT_A ">") + _tcslen(tszInternalClient) + strlen("</" ARG_ADDPORTMAPPING_NEWINTERNALCLIENT_A ">" EOL)
  10855. + strlen(" <" ARG_ADDPORTMAPPING_NEWENABLED_A ">" UPNP_BOOLEAN_TRUE "</" ARG_ADDPORTMAPPING_NEWENABLED_A ">" EOL)
  10856. + strlen(" <" ARG_ADDPORTMAPPING_NEWPORTMAPPINGDESCRIPTION_A ">") + _tcslen(tszDescription) + strlen("</" ARG_ADDPORTMAPPING_NEWPORTMAPPINGDESCRIPTION_A ">" EOL)
  10857. + strlen(" <" ARG_ADDPORTMAPPING_NEWLEASEDURATION_A ">") + _tcslen(tszLeaseDuration) + strlen("</" ARG_ADDPORTMAPPING_NEWLEASEDURATION_A ">" EOL)
  10858. + strlen(" </u:" ACTION_ADDPORTMAPPING_A ">" EOL)
  10859. + strlen(" </s:Body>" EOL)
  10860. + strlen("</s:Envelope>" EOL)
  10861. + strlen(EOL);
  10862. wsprintf(tszContentLength, _T("%i"), iContentLength);
  10863. iMsgSize = strlen("POST ") + strlen(pUPnPDevice->GetServiceControlURL()) + strlen(" " HTTP_VERSION EOL)
  10864. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  10865. + strlen("CONTENT-LENGTH: ") + _tcslen(tszContentLength) + strlen(EOL)
  10866. + strlen("CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL)
  10867. + strlen("SOAPACTION: ") + pUPnPDevice->GetStaticServiceURILength() + strlen("#" ACTION_ADDPORTMAPPING_A EOL)
  10868. + strlen(EOL)
  10869. + iContentLength;
  10870. //
  10871. // Allocate (or reallocate) the message buffer.
  10872. //
  10873. if (iMsgSize > iPrevMsgSize)
  10874. {
  10875. if (pszMessage != NULL)
  10876. {
  10877. DNFree(pszMessage);
  10878. pszMessage = NULL;
  10879. }
  10880. pszMessage = (char*) DNMalloc(iMsgSize + 1); // include room for NULL termination that isn't actually sent
  10881. if (pszMessage == NULL)
  10882. {
  10883. hr = DPNHERR_OUTOFMEMORY;
  10884. goto Failure;
  10885. }
  10886. iPrevMsgSize = iMsgSize;
  10887. }
  10888. strcpy(pszMessage, "POST ");
  10889. strcat(pszMessage, pUPnPDevice->GetServiceControlURL());
  10890. strcat(pszMessage, " " HTTP_VERSION EOL);
  10891. strcat(pszMessage, "HOST: ");
  10892. #ifdef UNICODE
  10893. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10894. tszHost,
  10895. (_tcslen(tszHost) + 1));
  10896. #else // ! UNICODE
  10897. strcat(pszMessage, tszHost);
  10898. #endif // ! UNICODE
  10899. strcat(pszMessage, EOL);
  10900. strcat(pszMessage, "CONTENT-LENGTH: ");
  10901. #ifdef UNICODE
  10902. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10903. tszContentLength,
  10904. (_tcslen(tszContentLength) + 1));
  10905. #else // ! UNICODE
  10906. strcat(pszMessage, tszContentLength);
  10907. #endif // ! UNICODE
  10908. strcat(pszMessage, EOL);
  10909. strcat(pszMessage, "CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL);
  10910. strcat(pszMessage, "SOAPACTION: ");
  10911. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  10912. strcat(pszMessage, "#" ACTION_ADDPORTMAPPING_A EOL);
  10913. strcat(pszMessage, EOL);
  10914. strcat(pszMessage, "<s:Envelope" EOL);
  10915. strcat(pszMessage, " xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL);
  10916. strcat(pszMessage, " s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL);
  10917. strcat(pszMessage, " <s:Body>" EOL);
  10918. strcat(pszMessage, " <u:" ACTION_ADDPORTMAPPING_A " xmlns:u=\"");
  10919. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  10920. strcat(pszMessage, "\">" EOL);
  10921. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_ADDPORTMAPPING_NEWREMOTEHOST_A ">" EOL);
  10922. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWEXTERNALPORT_A ">");
  10923. #ifdef UNICODE
  10924. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10925. tszExternalPort,
  10926. (_tcslen(tszExternalPort) + 1));
  10927. #else // ! UNICODE
  10928. strcat(pszMessage, tszExternalPort);
  10929. #endif // ! UNICODE
  10930. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWEXTERNALPORT_A ">" EOL);
  10931. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWPROTOCOL_A ">");
  10932. strcat(pszMessage, ((pRegisteredPort->IsTCP()) ? "TCP" : "UDP"));
  10933. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWPROTOCOL_A ">" EOL);
  10934. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWINTERNALPORT_A ">");
  10935. #ifdef UNICODE
  10936. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10937. tszInternalPort,
  10938. (_tcslen(tszInternalPort) + 1));
  10939. #else // ! UNICODE
  10940. strcat(pszMessage, tszInternalPort);
  10941. #endif // ! UNICODE
  10942. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWINTERNALPORT_A ">" EOL);
  10943. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWINTERNALCLIENT_A ">");
  10944. #ifdef UNICODE
  10945. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10946. tszInternalClient,
  10947. (_tcslen(tszInternalClient) + 1));
  10948. #else // ! UNICODE
  10949. strcat(pszMessage, tszInternalClient);
  10950. #endif // ! UNICODE
  10951. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWINTERNALCLIENT_A ">" EOL);
  10952. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWENABLED_A ">" UPNP_BOOLEAN_TRUE "</" ARG_ADDPORTMAPPING_NEWENABLED_A ">" EOL);
  10953. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWPORTMAPPINGDESCRIPTION_A ">");
  10954. #ifdef UNICODE
  10955. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10956. tszDescription,
  10957. (_tcslen(tszDescription) + 1));
  10958. #else // ! UNICODE
  10959. strcat(pszMessage, tszDescription);
  10960. #endif // ! UNICODE
  10961. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWPORTMAPPINGDESCRIPTION_A ">" EOL);
  10962. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWLEASEDURATION_A ">");
  10963. #ifdef UNICODE
  10964. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10965. tszLeaseDuration,
  10966. (_tcslen(tszLeaseDuration) + 1));
  10967. #else // ! UNICODE
  10968. strcat(pszMessage, tszLeaseDuration);
  10969. #endif // ! UNICODE
  10970. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWLEASEDURATION_A ">" EOL);
  10971. strcat(pszMessage, " </u:" ACTION_ADDPORTMAPPING_A ">" EOL);
  10972. strcat(pszMessage, " </s:Body>" EOL);
  10973. strcat(pszMessage, "</s:Envelope>" EOL);
  10974. strcat(pszMessage, EOL);
  10975. #ifdef DBG
  10976. this->PrintUPnPTransactionToFile(pszMessage,
  10977. iMsgSize,
  10978. "Outbound add port mapping request",
  10979. pDevice);
  10980. #endif // DBG
  10981. iReturn = this->m_pfnsend(pUPnPDevice->GetControlSocket(),
  10982. pszMessage,
  10983. iMsgSize,
  10984. 0);
  10985. if (iReturn == SOCKET_ERROR)
  10986. {
  10987. #ifdef DBG
  10988. dwError = this->m_pfnWSAGetLastError();
  10989. DPFX(DPFPREP, 0, "Got sockets error %u when sending control request to UPnP device!", dwError);
  10990. #endif // DBG
  10991. hr = DPNHERR_GENERIC;
  10992. goto Failure;
  10993. }
  10994. if (iReturn != iMsgSize)
  10995. {
  10996. DPFX(DPFPREP, 0, "Didn't send entire message (%i != %i)?!", iReturn, iMsgSize);
  10997. DNASSERT(FALSE);
  10998. hr = DPNHERR_GENERIC;
  10999. goto Failure;
  11000. }
  11001. //
  11002. // We have the lock so no one could have tried to receive data from
  11003. // the control socket yet. Mark the device as waiting for a
  11004. // response.
  11005. //
  11006. ZeroMemory(&RespInfo, sizeof(RespInfo));
  11007. pUPnPDevice->StartWaitingForControlResponse(CONTROLRESPONSETYPE_ADDPORTMAPPING,
  11008. &RespInfo);
  11009. fStartedWaitingForControlResponse = TRUE;
  11010. //
  11011. // Actually wait for the response.
  11012. //
  11013. dwStartTime = GETTIMESTAMP();
  11014. dwTimeout = g_dwUPnPResponseTimeout;
  11015. do
  11016. {
  11017. hr = this->CheckForReceivedUPnPMsgsOnDevice(pUPnPDevice, dwTimeout);
  11018. if (hr != DPNH_OK)
  11019. {
  11020. DPFX(DPFPREP, 0, "Failed receiving UPnP messages!");
  11021. goto Failure;
  11022. }
  11023. //
  11024. // We either timed out or got some data. Check if we got the
  11025. // response we need.
  11026. //
  11027. if (! pUPnPDevice->IsWaitingForControlResponse())
  11028. {
  11029. if (RespInfo.hrErrorCode != DPNH_OK)
  11030. {
  11031. //
  11032. // Make sure it's not the "I can't handle asymmetric
  11033. // mappings" error. If it is, note the fact that this
  11034. // device is going to force us to have worse behavior
  11035. // and try again.
  11036. //
  11037. if (RespInfo.hrErrorCode == (HRESULT) UPNPERR_IGD_SAMEPORTVALUESREQUIRED)
  11038. {
  11039. DPFX(DPFPREP, 1, "UPnP device 0x%p does not support asymmetric mappings.",
  11040. pUPnPDevice);
  11041. //
  11042. // Make sure we're not getting this error from a bad
  11043. // device. Otherwise we might get caught in a loop.
  11044. //
  11045. if ((! pUPnPDevice->DoesNotSupportAsymmetricMappings()) &&
  11046. (! pRegisteredPort->IsFixedPort()) &&
  11047. (dwTemp == 0))
  11048. {
  11049. //
  11050. // Remember that this device is unfriendly.
  11051. //
  11052. pUPnPDevice->NoteDoesNotSupportAsymmetricMappings();
  11053. //
  11054. // Use the same port externally next time.
  11055. //
  11056. DNASSERT(wExternalPortHostOrder != NTOHS(psaddrinTemp[dwTemp].sin_port));
  11057. wExternalPortHostOrder = NTOHS(psaddrinTemp[dwTemp].sin_port);
  11058. //
  11059. // Try again.
  11060. //
  11061. goto Retry;
  11062. }
  11063. DPFX(DPFPREP, 1, "DoesNotSupportAsymmetricMappings = %i, fixed port = %i, port index = %u",
  11064. pUPnPDevice->DoesNotSupportAsymmetricMappings(),
  11065. pRegisteredPort->IsFixedPort(),
  11066. dwTemp);
  11067. DNASSERTX(! "Getting UPNPERR_IGD_SAMEPORTVALUESREQUIRED from bad device!", 2);
  11068. //
  11069. // Continue through to failure case...
  11070. //
  11071. }
  11072. //
  11073. // Make sure it's not the "I can't handle lease times"
  11074. // error. If it is, note the fact that this device is
  11075. // going to force us to have worse behavior and try
  11076. // again.
  11077. //
  11078. if (RespInfo.hrErrorCode == (HRESULT) UPNPERR_IGD_ONLYPERMANENTLEASESSUPPORTED)
  11079. {
  11080. DPFX(DPFPREP, 1, "UPnP device 0x%p does not support non-INFINITE lease durations.",
  11081. pUPnPDevice);
  11082. //
  11083. // Make sure we're not getting this error from a bad
  11084. // device. Otherwise we might get caught in a loop.
  11085. //
  11086. if ((! pUPnPDevice->DoesNotSupportLeaseDurations()) &&
  11087. (dwTemp == 0))
  11088. {
  11089. //
  11090. // Remember that this device is unfriendly.
  11091. //
  11092. pUPnPDevice->NoteDoesNotSupportLeaseDurations();
  11093. //
  11094. // Use an INFINITE lease next time around.
  11095. //
  11096. DNASSERT(_tcscmp(tszLeaseDuration, _T("0")) != 0);
  11097. _tcscpy(tszLeaseDuration, _T("0"));
  11098. pRegisteredPort->NotePermanentUPnPLease();
  11099. //
  11100. // Try again.
  11101. //
  11102. goto Retry;
  11103. }
  11104. DPFX(DPFPREP, 1, "DoesNotSupportLeaseDurations = %i, port index = %u",
  11105. pUPnPDevice->DoesNotSupportLeaseDurations(), dwTemp);
  11106. DNASSERTX(! "Getting UPNPERR_IGD_ONLYPERMANENTLEASESSUPPORTED from bad device!", 2);
  11107. //
  11108. // Continue through to failure case...
  11109. //
  11110. }
  11111. #ifdef DBG
  11112. if (RespInfo.hrErrorCode == DPNHERR_PORTUNAVAILABLE)
  11113. {
  11114. DPFX(DPFPREP, 2, "Port %u (for address index %u) is reportedly unavailable.",
  11115. wExternalPortHostOrder, dwTemp);
  11116. }
  11117. #endif // DBG
  11118. PortUnavailable:
  11119. //
  11120. // If it's the port-unavailable error but we're able to
  11121. // try a different port, go for it.
  11122. //
  11123. if ((RespInfo.hrErrorCode == DPNHERR_PORTUNAVAILABLE) &&
  11124. (! pRegisteredPort->IsFixedPort()) &&
  11125. (! pUPnPDevice->DoesNotSupportAsymmetricMappings()))
  11126. {
  11127. //
  11128. // Go to the next sequential port. If we've wrapped
  11129. // around to 0, move to the first non reserved range
  11130. // port.
  11131. //
  11132. wExternalPortHostOrder++;
  11133. if (wExternalPortHostOrder == 0)
  11134. {
  11135. wExternalPortHostOrder = MAX_RESERVED_PORT + 1;
  11136. }
  11137. //
  11138. // If we haven't wrapped all the way back around to
  11139. // the first port we tried, try again.
  11140. //
  11141. if (wExternalPortHostOrder != wOriginalExternalPortHostOrder)
  11142. {
  11143. DPFX(DPFPREP, 2, "Retrying next port (%u) for index %u.",
  11144. wExternalPortHostOrder, dwTemp);
  11145. goto Retry;
  11146. }
  11147. DPFX(DPFPREP, 0, "All ports were exhausted (after index %u), marking port as unavailable.",
  11148. dwTemp);
  11149. }
  11150. //
  11151. // If it's not the port-unavailable error, it's
  11152. // serious. Bail.
  11153. //
  11154. if (RespInfo.hrErrorCode != DPNHERR_PORTUNAVAILABLE)
  11155. {
  11156. DPFX(DPFPREP, 1, "Port index %u got failure response 0x%lx.",
  11157. dwTemp, RespInfo.hrErrorCode);
  11158. hr = DPNHERR_SERVERNOTRESPONDING;
  11159. goto Failure;
  11160. }
  11161. //
  11162. // The port is unavailable.
  11163. //
  11164. pRegisteredPort->NoteUPnPPortUnavailable();
  11165. //
  11166. // We're done here. Ideally we would goto Exit, but we
  11167. // want to execute the public address array cleanup
  11168. // code. hr should == DPNH_OK.
  11169. //
  11170. DNASSERT(hr == DPNH_OK);
  11171. goto Failure;
  11172. }
  11173. //
  11174. // If we got here, we successfully registered this port.
  11175. //
  11176. //
  11177. // UPnP Internet Gateway Device mapping protocol doesn't
  11178. // hand you the external IP address in the success response
  11179. // message. We had to have known it through other means
  11180. // (querying the ExternalIPAddress variable).
  11181. //
  11182. DPFX(DPFPREP, 2, "Port index %u got success response.", dwTemp);
  11183. pRegisteredPort->SetUPnPPublicV4Address(dwTemp,
  11184. pUPnPDevice->GetExternalIPAddressV4(),
  11185. HTONS(wExternalPortHostOrder));
  11186. //
  11187. // If the lease is permanent and it's not for a shared
  11188. // port, we need to remember it, in case we crash before
  11189. // cleaning it up in this session. That we can clean it up
  11190. // next time we launch.
  11191. //
  11192. if ((pRegisteredPort->HasPermanentUPnPLease()) &&
  11193. (! pRegisteredPort->IsSharedPort()))
  11194. {
  11195. CRegistry RegObject;
  11196. DPNHACTIVENATMAPPING dpnhanm;
  11197. #ifndef UNICODE
  11198. WCHAR wszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  11199. #endif // ! UNICODE
  11200. DPFX(DPFPREP, 7, "Remembering NAT lease \"%s\" in case of crash.",
  11201. tszDescription);
  11202. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  11203. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVENATMAPPINGS,
  11204. FALSE,
  11205. TRUE,
  11206. TRUE,
  11207. DPN_KEY_ALL_ACCESS))
  11208. {
  11209. DPFX(DPFPREP, 0, "Couldn't open active NAT mapping key, unable to save in case of crash!");
  11210. }
  11211. else
  11212. {
  11213. #ifndef UNICODE
  11214. dwDescriptionLength = strlen(tszDescription) + 1;
  11215. hr = STR_jkAnsiToWide(wszDescription, tszDescription, dwDescriptionLength);
  11216. if (hr != S_OK)
  11217. {
  11218. DPFX(DPFPREP, 0, "Couldn't convert NAT mapping description to Unicode (err = 0x%lx), unable to save in case of crash!",
  11219. hr);
  11220. //
  11221. // Ignore error and continue.
  11222. //
  11223. hr = S_OK;
  11224. }
  11225. else
  11226. #endif // ! UNICODE
  11227. {
  11228. DNASSERT(this->m_dwInstanceKey != 0);
  11229. ZeroMemory(&dpnhanm, sizeof(dpnhanm));
  11230. dpnhanm.dwVersion = ACTIVE_MAPPING_VERSION;
  11231. dpnhanm.dwInstanceKey = this->m_dwInstanceKey;
  11232. dpnhanm.dwUPnPDeviceID = pUPnPDevice->GetID();
  11233. dpnhanm.dwFlags = pRegisteredPort->GetFlags();
  11234. dpnhanm.dwInternalAddressV4 = pDevice->GetLocalAddressV4();
  11235. dpnhanm.wInternalPort = psaddrinTemp[dwTemp].sin_port;
  11236. dpnhanm.dwExternalAddressV4 = pUPnPDevice->GetExternalIPAddressV4();
  11237. dpnhanm.wExternalPort = HTONS(wExternalPortHostOrder);
  11238. #ifdef UNICODE
  11239. RegObject.WriteBlob(tszDescription,
  11240. #else // ! UNICODE
  11241. RegObject.WriteBlob(wszDescription,
  11242. #endif // ! UNICODE
  11243. (LPBYTE) (&dpnhanm),
  11244. sizeof(dpnhanm));
  11245. }
  11246. RegObject.Close();
  11247. }
  11248. }
  11249. //
  11250. // Break out of the wait loop.
  11251. //
  11252. break;
  11253. }
  11254. //
  11255. // Make sure our device is still connected.
  11256. //
  11257. if (! pUPnPDevice->IsConnected())
  11258. {
  11259. DPFX(DPFPREP, 0, "UPnP device 0x%p disconnected while adding port index %u!",
  11260. pUPnPDevice, dwTemp);
  11261. pUPnPDevice->StopWaitingForControlResponse();
  11262. hr = DPNHERR_SERVERNOTRESPONDING;
  11263. goto Failure;
  11264. }
  11265. //
  11266. // Calculate how long we have left to wait. If the calculation
  11267. // goes negative, it means we're done.
  11268. //
  11269. dwTimeout = g_dwUPnPResponseTimeout - (GETTIMESTAMP() - dwStartTime);
  11270. }
  11271. while (((int) dwTimeout > 0));
  11272. //
  11273. // If we never got the response, stop waiting for it.
  11274. //
  11275. if (pUPnPDevice->IsWaitingForControlResponse())
  11276. {
  11277. pUPnPDevice->StopWaitingForControlResponse();
  11278. DPFX(DPFPREP, 0, "Port index %u didn't get response in time!", dwTemp);
  11279. hr = DPNHERR_SERVERNOTRESPONDING;
  11280. goto Failure;
  11281. }
  11282. //
  11283. // Continue on to the next port.
  11284. //
  11285. }
  11286. //
  11287. // If we're here, all ports were successfully registered.
  11288. //
  11289. if (pRegisteredPort->HasPermanentUPnPLease())
  11290. {
  11291. DPFX(DPFPREP, 3, "All %u ports successfully registered with UPnP device (no expiration).",
  11292. dwTemp);
  11293. }
  11294. else
  11295. {
  11296. pRegisteredPort->SetUPnPLeaseExpiration(dwLeaseExpiration);
  11297. DPFX(DPFPREP, 3, "All %u ports successfully registered with UPnP device, expiration = %u.",
  11298. dwTemp, dwLeaseExpiration);
  11299. //
  11300. // Remember this expiration time if it's the one that's going to expire
  11301. // soonest.
  11302. //
  11303. if ((fFirstLease) ||
  11304. ((int) (dwLeaseExpiration - this->m_dwEarliestLeaseExpirationTime) < 0))
  11305. {
  11306. if (fFirstLease)
  11307. {
  11308. DPFX(DPFPREP, 1, "Registered port 0x%p's UPnP lease is the first lease (expires at %u).",
  11309. pRegisteredPort, dwLeaseExpiration);
  11310. }
  11311. else
  11312. {
  11313. DPFX(DPFPREP, 1, "Registered port 0x%p's UPnP lease expires at %u which is earlier than the next earliest lease expiration (%u).",
  11314. pRegisteredPort,
  11315. dwLeaseExpiration,
  11316. this->m_dwEarliestLeaseExpirationTime);
  11317. }
  11318. this->m_dwEarliestLeaseExpirationTime = dwLeaseExpiration;
  11319. #ifndef DPNBUILD_NOWINSOCK2
  11320. //
  11321. // Ping the event if there is one so that the user's GetCaps
  11322. // interval doesn't miss this new, shorter lease.
  11323. //
  11324. if (this->m_hAlertEvent != NULL)
  11325. {
  11326. fResult = SetEvent(this->m_hAlertEvent);
  11327. #ifdef DBG
  11328. if (! fResult)
  11329. {
  11330. dwError = GetLastError();
  11331. DPFX(DPFPREP, 0, "Couldn't set alert event 0x%p! err = %u",
  11332. this->m_hAlertEvent, dwError);
  11333. //
  11334. // Ignore failure...
  11335. //
  11336. }
  11337. #endif // DBG
  11338. }
  11339. //
  11340. // Ping the I/O completion port if there is one so that the user's
  11341. // GetCaps interval doesn't miss this new, shorter lease.
  11342. //
  11343. if (this->m_hAlertIOCompletionPort != NULL)
  11344. {
  11345. fResult = PostQueuedCompletionStatus(this->m_hAlertIOCompletionPort,
  11346. 0,
  11347. this->m_dwAlertCompletionKey,
  11348. NULL);
  11349. #ifdef DBG
  11350. if (! fResult)
  11351. {
  11352. dwError = GetLastError();
  11353. DPFX(DPFPREP, 0, "Couldn't queue key %u on alert IO completion port 0x%p! err = %u",
  11354. this->m_dwAlertCompletionKey,
  11355. this->m_hAlertIOCompletionPort,
  11356. dwError);
  11357. //
  11358. // Ignore failure...
  11359. //
  11360. }
  11361. #endif // DBG
  11362. }
  11363. #endif // ! DPNBUILD_NOWINSOCK2
  11364. }
  11365. else
  11366. {
  11367. //
  11368. // Not first or shortest lease.
  11369. //
  11370. }
  11371. } // end if (not permanent UPnP lease)
  11372. Exit:
  11373. if (pszMessage != NULL)
  11374. {
  11375. DNFree(pszMessage);
  11376. pszMessage = NULL;
  11377. }
  11378. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  11379. return hr;
  11380. Failure:
  11381. //
  11382. // If we allocated the array, clean it up.
  11383. //
  11384. if (pRegisteredPort->HasUPnPPublicAddresses())
  11385. {
  11386. //
  11387. // If we started waiting for a response, clear that.
  11388. //
  11389. if (fStartedWaitingForControlResponse)
  11390. {
  11391. pUPnPDevice->StopWaitingForControlResponse();
  11392. }
  11393. //
  11394. // Delete all mappings successfully made up until now.
  11395. //
  11396. if (dwTemp > 0)
  11397. {
  11398. temphr = this->UnmapUPnPPort(pRegisteredPort, dwTemp, TRUE);
  11399. if (temphr != DPNH_OK)
  11400. {
  11401. DPFX(DPFPREP, 0, "Failed deleting %u previously mapped ports! Err = 0x%lx",
  11402. dwTemp, temphr);
  11403. if (hr == DPNH_OK)
  11404. {
  11405. hr = temphr;
  11406. }
  11407. }
  11408. }
  11409. else
  11410. {
  11411. //
  11412. // Remove the addresses array we created.
  11413. //
  11414. pRegisteredPort->DestroyUPnPPublicAddressesArray();
  11415. //
  11416. // Remove the lease counter.
  11417. //
  11418. DNASSERT(this->m_dwNumLeases > 0);
  11419. this->m_dwNumLeases--;
  11420. DPFX(DPFPREP, 7, "UPnP lease for 0x%p removed, total num leases = %u.",
  11421. pRegisteredPort, this->m_dwNumLeases);
  11422. }
  11423. }
  11424. //
  11425. // Turn off the permanent lease flag we may have turned on at the top of
  11426. // this function.
  11427. //
  11428. pRegisteredPort->NoteNotPermanentUPnPLease();
  11429. goto Exit;
  11430. } // CNATHelpUPnP::MapPortsOnUPnPDevice
  11431. #undef DPF_MODNAME
  11432. #define DPF_MODNAME "CNATHelpUPnP::InternalUPnPQueryAddress"
  11433. //=============================================================================
  11434. // CNATHelpUPnP::InternalUPnPQueryAddress
  11435. //-----------------------------------------------------------------------------
  11436. //
  11437. // Description: Queries a port mapping with a UPnP device.
  11438. //
  11439. // The UPnP device may get removed from list if a failure
  11440. // occurs, the caller needs to have a reference.
  11441. //
  11442. // The object lock is assumed to be held.
  11443. //
  11444. // Arguments:
  11445. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device that
  11446. // should be queried.
  11447. // SOCKADDR_IN * psaddrinQueryAddress - Address to look up.
  11448. // SOCKADDR_IN * psaddrinResponseAddress - Place to store public address, if
  11449. // one exists.
  11450. // DWORD dwFlags - Flags to use when querying.
  11451. //
  11452. // Returns: HRESULT
  11453. // DPNH_OK - The query was successful.
  11454. // DPNHERR_GENERIC - An error occurred.
  11455. // DPNHERR_NOMAPPING - The server did not have a mapping for the
  11456. // given address.
  11457. // DPNHERR_NOMAPPINGBUTPRIVATE - The server indicated that no mapping was
  11458. // found, but it is a private address.
  11459. // DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
  11460. // message.
  11461. //=============================================================================
  11462. HRESULT CNATHelpUPnP::InternalUPnPQueryAddress(CUPnPDevice * const pUPnPDevice,
  11463. const SOCKADDR_IN * const psaddrinQueryAddress,
  11464. SOCKADDR_IN * const psaddrinResponseAddress,
  11465. const DWORD dwFlags)
  11466. {
  11467. HRESULT hr;
  11468. BOOL fStartedWaitingForControlResponse = FALSE;
  11469. BOOL fNoPortMapping = FALSE;
  11470. CDevice * pDevice;
  11471. CBilink * pblCachedMaps;
  11472. DWORD dwCurrentTime;
  11473. CBilink * pBilink;
  11474. CCacheMap * pCacheMap;
  11475. SOCKADDR_IN * psaddrinTemp;
  11476. TCHAR tszExternalPort[32];
  11477. int iContentLength;
  11478. TCHAR tszContentLength[32];
  11479. TCHAR tszHost[22]; // "xxx.xxx.xxx.xxx:xxxxx" + NULL termination
  11480. char * pszMessage = NULL;
  11481. int iMsgSize;
  11482. int iPrevMsgSize = 0;
  11483. int iReturn;
  11484. UPNP_CONTROLRESPONSE_INFO RespInfo;
  11485. DWORD dwStartTime;
  11486. DWORD dwTimeout;
  11487. DWORD dwCacheMapFlags;
  11488. #ifdef DBG
  11489. DWORD dwError;
  11490. #endif // DBG
  11491. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%lx)",
  11492. this, pUPnPDevice, psaddrinQueryAddress, psaddrinResponseAddress, dwFlags);
  11493. DNASSERT(pUPnPDevice != NULL);
  11494. pDevice = pUPnPDevice->GetOwningDevice();
  11495. DNASSERT(pDevice != NULL);
  11496. DNASSERT(pUPnPDevice->IsReady());
  11497. DNASSERT(psaddrinQueryAddress != NULL);
  11498. DNASSERT(psaddrinResponseAddress != NULL);
  11499. DNASSERT(this->m_dwFlags & (NATHELPUPNPOBJ_INITIALIZED | NATHELPUPNPOBJ_USEUPNP));
  11500. DPFX(DPFPREP, 7, "Querying for address %u.%u.%u.%u:%u %hs.",
  11501. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b1,
  11502. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b2,
  11503. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b3,
  11504. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b4,
  11505. NTOHS(psaddrinQueryAddress->sin_port),
  11506. ((dwFlags & DPNHQUERYADDRESS_TCP) ? "TCP" : "UDP"));
  11507. //
  11508. // First, check if we've looked this address up recently and already have
  11509. // the result cached.
  11510. // The lock is already held.
  11511. //
  11512. pblCachedMaps = pUPnPDevice->GetCachedMaps();
  11513. dwCurrentTime = GETTIMESTAMP();
  11514. pBilink = pblCachedMaps->GetNext();
  11515. while (pBilink != pblCachedMaps)
  11516. {
  11517. DNASSERT(! pBilink->IsEmpty());
  11518. pCacheMap = CACHEMAP_FROM_BILINK(pBilink);
  11519. pBilink = pBilink->GetNext();
  11520. //
  11521. // Make sure this cached mapping hasn't expired.
  11522. //
  11523. if ((int) (pCacheMap->GetExpirationTime() - dwCurrentTime) < 0)
  11524. {
  11525. DPFX(DPFPREP, 5, "Cached mapping 0x%p has expired.", pCacheMap);
  11526. pCacheMap->m_blList.RemoveFromList();
  11527. delete pCacheMap;
  11528. }
  11529. else
  11530. {
  11531. //
  11532. // If this mapping is for the right address and type of address,
  11533. // then we've already got our answer.
  11534. //
  11535. if (pCacheMap->DoesMatchQuery(psaddrinQueryAddress, dwFlags))
  11536. {
  11537. if (pCacheMap->IsNotFound())
  11538. {
  11539. if ((dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED) &&
  11540. (pCacheMap->IsPrivateButUnmapped()))
  11541. {
  11542. DPFX(DPFPREP, 5, "Address was already determined to not have a mapping but still be private.");
  11543. hr = DPNHERR_NOMAPPINGBUTPRIVATE;
  11544. }
  11545. else
  11546. {
  11547. DPFX(DPFPREP, 5, "Address was already determined to not have a mapping.");
  11548. hr = DPNHERR_NOMAPPING;
  11549. }
  11550. }
  11551. else
  11552. {
  11553. pCacheMap->GetResponseAddressV4(psaddrinResponseAddress);
  11554. DPFX(DPFPREP, 5, "Address was already determined to have a mapping.");
  11555. hr = DPNH_OK;
  11556. }
  11557. goto Exit;
  11558. }
  11559. }
  11560. }
  11561. //
  11562. // If the address we're querying isn't the NAT's public address, it can't
  11563. // possibly be mapped. So only perform the actual query if it's
  11564. // appropriate.
  11565. //
  11566. if (psaddrinQueryAddress->sin_addr.S_un.S_addr == pUPnPDevice->GetExternalIPAddressV4())
  11567. {
  11568. //
  11569. // If we're here, we haven't already cached the answer. Query the UPnP
  11570. // device.
  11571. //
  11572. DNASSERT(pUPnPDevice->GetServiceControlURL() != NULL);
  11573. //
  11574. // If the control socket got disconnected after the last message,
  11575. // then reconnect.
  11576. //
  11577. if (! pUPnPDevice->IsConnected())
  11578. {
  11579. hr = this->ReconnectUPnPControlSocket(pUPnPDevice);
  11580. if (hr != S_OK)
  11581. {
  11582. DPFX(DPFPREP, 0, "Couldn't reconnect UPnP control socket!");
  11583. goto Failure;
  11584. }
  11585. }
  11586. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  11587. wsprintf(tszExternalPort, _T("%u"),
  11588. NTOHS(psaddrinQueryAddress->sin_port));
  11589. psaddrinTemp = pUPnPDevice->GetHostAddress();
  11590. wsprintf(tszHost, _T("%u.%u.%u.%u:%u"),
  11591. psaddrinTemp->sin_addr.S_un.S_un_b.s_b1,
  11592. psaddrinTemp->sin_addr.S_un.S_un_b.s_b2,
  11593. psaddrinTemp->sin_addr.S_un.S_un_b.s_b3,
  11594. psaddrinTemp->sin_addr.S_un.S_un_b.s_b4,
  11595. NTOHS(psaddrinTemp->sin_port));
  11596. iContentLength = strlen("<s:Envelope" EOL)
  11597. + strlen(" xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL)
  11598. + strlen(" s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL)
  11599. + strlen(" <s:Body>" EOL)
  11600. + strlen(" <u:" ACTION_GETSPECIFICPORTMAPPINGENTRY_A " xmlns:u=\"") + pUPnPDevice->GetStaticServiceURILength() + strlen("\">" EOL)
  11601. + strlen(" <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWREMOTEHOST_A ">" EOL)
  11602. + strlen(" <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWEXTERNALPORT_A ">") + _tcslen(tszExternalPort) + strlen("</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWEXTERNALPORT_A ">" EOL)
  11603. + strlen(" <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPROTOCOL_A ">") + 3 + strlen("</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPROTOCOL_A ">" EOL)
  11604. + strlen(" </u:" ACTION_GETSPECIFICPORTMAPPINGENTRY_A ">" EOL)
  11605. + strlen(" </s:Body>" EOL)
  11606. + strlen("</s:Envelope>" EOL)
  11607. + strlen(EOL);
  11608. wsprintf(tszContentLength, _T("%i"), iContentLength);
  11609. iMsgSize = strlen("POST ") + strlen(pUPnPDevice->GetServiceControlURL()) + strlen(" " HTTP_VERSION EOL)
  11610. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  11611. + strlen("CONTENT-LENGTH: ") + _tcslen(tszContentLength) + strlen(EOL)
  11612. + strlen("CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL)
  11613. + strlen("SOAPACTION: ") + pUPnPDevice->GetStaticServiceURILength() + strlen("#" ACTION_GETSPECIFICPORTMAPPINGENTRY_A EOL)
  11614. + strlen(EOL)
  11615. + iContentLength;
  11616. //
  11617. // Allocate (or reallocate) the message buffer.
  11618. //
  11619. if (iMsgSize > iPrevMsgSize)
  11620. {
  11621. if (pszMessage != NULL)
  11622. {
  11623. DNFree(pszMessage);
  11624. pszMessage = NULL;
  11625. }
  11626. pszMessage = (char*) DNMalloc(iMsgSize + 1); // include room for NULL termination that isn't actually sent
  11627. if (pszMessage == NULL)
  11628. {
  11629. hr = DPNHERR_OUTOFMEMORY;
  11630. goto Failure;
  11631. }
  11632. iPrevMsgSize = iMsgSize;
  11633. }
  11634. strcpy(pszMessage, "POST ");
  11635. strcat(pszMessage, pUPnPDevice->GetServiceControlURL());
  11636. strcat(pszMessage, " " HTTP_VERSION EOL);
  11637. strcat(pszMessage, "HOST: ");
  11638. #ifdef UNICODE
  11639. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  11640. tszHost,
  11641. (_tcslen(tszHost) + 1));
  11642. #else // ! UNICODE
  11643. strcat(pszMessage, tszHost);
  11644. #endif // ! UNICODE
  11645. strcat(pszMessage, EOL);
  11646. strcat(pszMessage, "CONTENT-LENGTH: ");
  11647. #ifdef UNICODE
  11648. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  11649. tszContentLength,
  11650. (_tcslen(tszContentLength) + 1));
  11651. #else // ! UNICODE
  11652. strcat(pszMessage, tszContentLength);
  11653. #endif // ! UNICODE
  11654. strcat(pszMessage, EOL);
  11655. strcat(pszMessage, "CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL);
  11656. strcat(pszMessage, "SOAPACTION: ");
  11657. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  11658. strcat(pszMessage, "#" ACTION_GETSPECIFICPORTMAPPINGENTRY_A EOL);
  11659. strcat(pszMessage, EOL);
  11660. strcat(pszMessage, "<s:Envelope" EOL);
  11661. strcat(pszMessage, " xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL);
  11662. strcat(pszMessage, " s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL);
  11663. strcat(pszMessage, " <s:Body>" EOL);
  11664. strcat(pszMessage, " <u:" ACTION_GETSPECIFICPORTMAPPINGENTRY_A " xmlns:u=\"");
  11665. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  11666. strcat(pszMessage, "\">" EOL);
  11667. strcat(pszMessage, " <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWREMOTEHOST_A ">" EOL);
  11668. strcat(pszMessage, " <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWEXTERNALPORT_A ">");
  11669. #ifdef UNICODE
  11670. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  11671. tszExternalPort,
  11672. (_tcslen(tszExternalPort) + 1));
  11673. #else // ! UNICODE
  11674. strcat(pszMessage, tszExternalPort);
  11675. #endif // ! UNICODE
  11676. strcat(pszMessage, "</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWEXTERNALPORT_A ">" EOL);
  11677. strcat(pszMessage, " <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPROTOCOL_A ">");
  11678. strcat(pszMessage, ((dwFlags & DPNHQUERYADDRESS_TCP) ? "TCP" : "UDP"));
  11679. strcat(pszMessage, "</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPROTOCOL_A ">" EOL);
  11680. strcat(pszMessage, " </u:" ACTION_GETSPECIFICPORTMAPPINGENTRY_A ">" EOL);
  11681. strcat(pszMessage, " </s:Body>" EOL);
  11682. strcat(pszMessage, "</s:Envelope>" EOL);
  11683. strcat(pszMessage, EOL);
  11684. #ifdef DBG
  11685. this->PrintUPnPTransactionToFile(pszMessage,
  11686. iMsgSize,
  11687. "Outbound get port mapping request",
  11688. pDevice);
  11689. #endif // DBG
  11690. iReturn = this->m_pfnsend(pUPnPDevice->GetControlSocket(),
  11691. pszMessage,
  11692. iMsgSize,
  11693. 0);
  11694. if (iReturn == SOCKET_ERROR)
  11695. {
  11696. #ifdef DBG
  11697. dwError = this->m_pfnWSAGetLastError();
  11698. DPFX(DPFPREP, 0, "Got sockets error %u when sending control request to UPnP device!", dwError);
  11699. #endif // DBG
  11700. hr = DPNHERR_GENERIC;
  11701. goto Failure;
  11702. }
  11703. if (iReturn != iMsgSize)
  11704. {
  11705. DPFX(DPFPREP, 0, "Didn't send entire message (%i != %i)?!", iReturn, iMsgSize);
  11706. DNASSERT(FALSE);
  11707. hr = DPNHERR_GENERIC;
  11708. goto Failure;
  11709. }
  11710. //
  11711. // We have the lock so no one could have tried to receive data from
  11712. // the control socket yet. Mark the device as waiting for a response.
  11713. //
  11714. ZeroMemory(&RespInfo, sizeof(RespInfo));
  11715. pUPnPDevice->StartWaitingForControlResponse(CONTROLRESPONSETYPE_GETSPECIFICPORTMAPPINGENTRY,
  11716. &RespInfo);
  11717. fStartedWaitingForControlResponse = TRUE;
  11718. //
  11719. // Actually wait for the response.
  11720. //
  11721. dwStartTime = GETTIMESTAMP();
  11722. dwTimeout = g_dwUPnPResponseTimeout;
  11723. do
  11724. {
  11725. hr = this->CheckForReceivedUPnPMsgsOnDevice(pUPnPDevice, dwTimeout);
  11726. if (hr != DPNH_OK)
  11727. {
  11728. DPFX(DPFPREP, 0, "Failed receiving UPnP messages!");
  11729. goto Failure;
  11730. }
  11731. //
  11732. // We either timed out or got some data. Check if we got a
  11733. // response of some type.
  11734. //
  11735. if (! pUPnPDevice->IsWaitingForControlResponse())
  11736. {
  11737. break;
  11738. }
  11739. //
  11740. // Make sure our device is still connected.
  11741. //
  11742. if (! pUPnPDevice->IsConnected())
  11743. {
  11744. DPFX(DPFPREP, 0, "UPnP device 0x%p disconnected while querying port!",
  11745. pUPnPDevice);
  11746. pUPnPDevice->StopWaitingForControlResponse();
  11747. hr = DPNHERR_SERVERNOTRESPONDING;
  11748. goto Failure;
  11749. }
  11750. //
  11751. // Calculate how long we have left to wait. If the calculation
  11752. // goes negative, it means we're done.
  11753. //
  11754. dwTimeout = g_dwUPnPResponseTimeout - (GETTIMESTAMP() - dwStartTime);
  11755. }
  11756. while (((int) dwTimeout > 0));
  11757. //
  11758. // If we never got the response, stop waiting for it.
  11759. //
  11760. if (pUPnPDevice->IsWaitingForControlResponse())
  11761. {
  11762. pUPnPDevice->StopWaitingForControlResponse();
  11763. DPFX(DPFPREP, 0, "Server didn't respond in time!");
  11764. hr = DPNHERR_SERVERNOTRESPONDING;
  11765. goto Failure;
  11766. }
  11767. //
  11768. // If we're here, then we've gotten a valid response from the server.
  11769. //
  11770. if (RespInfo.hrErrorCode != DPNH_OK)
  11771. {
  11772. DPFX(DPFPREP, 1, "Server returned failure response 0x%lx, assuming no port mapping.",
  11773. RespInfo.hrErrorCode);
  11774. fNoPortMapping = TRUE;
  11775. }
  11776. }
  11777. else
  11778. {
  11779. DPFX(DPFPREP, 1, "Address %u.%u.%u.%u doesn't match NAT's external IP address, not querying.",
  11780. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b1,
  11781. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b2,
  11782. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b3,
  11783. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b4);
  11784. fNoPortMapping = TRUE;
  11785. }
  11786. dwCacheMapFlags = QUERYFLAGSMASK(dwFlags);
  11787. //
  11788. // Determine address locality (if requested) and cache the no-mapping
  11789. // result.
  11790. //
  11791. if (fNoPortMapping)
  11792. {
  11793. //
  11794. // Try determining if the address is local, if allowed.
  11795. //
  11796. if (dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED)
  11797. {
  11798. if (this->IsAddressLocal(pDevice, psaddrinQueryAddress))
  11799. {
  11800. DPFX(DPFPREP, 5, "Address appears to be local, returning NOMAPPINGBUTPRIVATE.");
  11801. dwCacheMapFlags |= CACHEMAPOBJ_PRIVATEBUTUNMAPPED;
  11802. hr = DPNHERR_NOMAPPINGBUTPRIVATE;
  11803. }
  11804. else
  11805. {
  11806. DPFX(DPFPREP, 5, "Address does not appear to be local, returning NOMAPPING.");
  11807. hr = DPNHERR_NOMAPPING;
  11808. }
  11809. }
  11810. else
  11811. {
  11812. hr = DPNHERR_NOMAPPING;
  11813. }
  11814. //
  11815. // Cache the fact that we could not determine a mapping for that
  11816. // address, if allowed.
  11817. //
  11818. if (dwFlags & DPNHQUERYADDRESS_CACHENOTFOUND)
  11819. {
  11820. pCacheMap = new CCacheMap(psaddrinQueryAddress,
  11821. (GETTIMESTAMP() + g_dwCacheLifeNotFound),
  11822. (dwCacheMapFlags | CACHEMAPOBJ_NOTFOUND));
  11823. if (pCacheMap == NULL)
  11824. {
  11825. hr = DPNHERR_OUTOFMEMORY;
  11826. goto Failure;
  11827. }
  11828. pCacheMap->m_blList.InsertBefore(pblCachedMaps);
  11829. }
  11830. goto Failure;
  11831. }
  11832. DPFX(DPFPREP, 1, "Server returned a private mapping (%u.%u.%u.%u:%u).",
  11833. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b1,
  11834. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b2,
  11835. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b3,
  11836. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b4,
  11837. NTOHS(RespInfo.wInternalPort));
  11838. //
  11839. // Convert the loopback address to the device address.
  11840. //
  11841. if (RespInfo.dwInternalClientV4 == NETWORKBYTEORDER_INADDR_LOOPBACK)
  11842. {
  11843. RespInfo.dwInternalClientV4 = pDevice->GetLocalAddressV4();
  11844. DPFX(DPFPREP, 1, "Converted private loopback address to device address (%u.%u.%u.%u).",
  11845. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b1,
  11846. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b2,
  11847. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b3,
  11848. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b4);
  11849. }
  11850. //
  11851. // If the UPnP device doesn't support asymmetric mappings and thus didn't
  11852. // return a port, or it did return one but it's the bogus port 0, assume
  11853. // the internal port is the same port as the external one.
  11854. //
  11855. if (RespInfo.wInternalPort == 0)
  11856. {
  11857. RespInfo.wInternalPort = psaddrinQueryAddress->sin_port;
  11858. DPFX(DPFPREP, 2, "Converted invalid internal port to the query address public port (%u).",
  11859. NTOHS(psaddrinQueryAddress->sin_port));
  11860. }
  11861. //
  11862. // Ensure that we're not getting something bogus.
  11863. //
  11864. SOCKADDR_IN saddrinTemp;
  11865. saddrinTemp.sin_addr.S_un.S_addr = RespInfo.dwInternalClientV4;
  11866. if ((RespInfo.dwInternalClientV4 == 0) ||
  11867. (! this->IsAddressLocal(pDevice, &saddrinTemp)))
  11868. {
  11869. //
  11870. // If the returned address is the same as the NAT's public address,
  11871. // it's probably Windows ICS returning an ICF mapping. We still treat
  11872. // it as an invalid mapping, but we will cache the results since there
  11873. // are legimitate cases where we can see this.
  11874. //
  11875. if (RespInfo.dwInternalClientV4 == pUPnPDevice->GetExternalIPAddressV4())
  11876. {
  11877. DPFX(DPFPREP, 1, "UPnP device returned its public address as the private address (%u.%u.%u.%u:%u). Probably ICS + ICF, but treating as no mapping.",
  11878. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b1,
  11879. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b2,
  11880. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b3,
  11881. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b4,
  11882. NTOHS(RespInfo.wInternalPort));
  11883. DNASSERTX(! "UPnP device returned public address as the private address!", 3);
  11884. //
  11885. // Cache the fact that we did not get a valid mapping for that
  11886. // address, if allowed.
  11887. //
  11888. if (dwFlags & DPNHQUERYADDRESS_CACHENOTFOUND)
  11889. {
  11890. pCacheMap = new CCacheMap(psaddrinQueryAddress,
  11891. (GETTIMESTAMP() + g_dwCacheLifeNotFound),
  11892. (dwCacheMapFlags | CACHEMAPOBJ_NOTFOUND));
  11893. if (pCacheMap == NULL)
  11894. {
  11895. hr = DPNHERR_OUTOFMEMORY;
  11896. goto Failure;
  11897. }
  11898. pCacheMap->m_blList.InsertBefore(pblCachedMaps);
  11899. }
  11900. }
  11901. else
  11902. {
  11903. DPFX(DPFPREP, 0, "UPnP device returned an invalid private address (%u.%u.%u.%u:%u)! Assuming no mapping.",
  11904. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b1,
  11905. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b2,
  11906. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b3,
  11907. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b4,
  11908. NTOHS(RespInfo.wInternalPort));
  11909. DNASSERTX(! "Why is UPnP device returning invalid private address?", 2);
  11910. }
  11911. hr = DPNHERR_NOMAPPING;
  11912. goto Failure;
  11913. }
  11914. //
  11915. // Return the address mapping to our caller.
  11916. //
  11917. ZeroMemory(psaddrinResponseAddress, sizeof(SOCKADDR_IN));
  11918. psaddrinResponseAddress->sin_family = AF_INET;
  11919. psaddrinResponseAddress->sin_addr.s_addr = RespInfo.dwInternalClientV4;
  11920. psaddrinResponseAddress->sin_port = RespInfo.wInternalPort;
  11921. //
  11922. // Cache the fact that we found a mapping for that address, if allowed.
  11923. //
  11924. if (dwFlags & DPNHQUERYADDRESS_CACHEFOUND)
  11925. {
  11926. pCacheMap = new CCacheMap(psaddrinQueryAddress,
  11927. (GETTIMESTAMP() + g_dwCacheLifeFound),
  11928. dwCacheMapFlags);
  11929. if (pCacheMap == NULL)
  11930. {
  11931. hr = DPNHERR_OUTOFMEMORY;
  11932. goto Failure;
  11933. }
  11934. pCacheMap->SetResponseAddressV4(psaddrinResponseAddress->sin_addr.S_un.S_addr,
  11935. psaddrinResponseAddress->sin_port);
  11936. pCacheMap->m_blList.InsertBefore(pblCachedMaps);
  11937. }
  11938. Exit:
  11939. if (pszMessage != NULL)
  11940. {
  11941. DNFree(pszMessage);
  11942. pszMessage = NULL;
  11943. }
  11944. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  11945. return hr;
  11946. Failure:
  11947. //
  11948. // If we started waiting for a response, clear that.
  11949. //
  11950. if (fStartedWaitingForControlResponse)
  11951. {
  11952. pUPnPDevice->StopWaitingForControlResponse();
  11953. }
  11954. goto Exit;
  11955. } // CNATHelpUPnP::InternalUPnPQueryAddress
  11956. #undef DPF_MODNAME
  11957. #define DPF_MODNAME "CNATHelpUPnP::ExtendUPnPLease"
  11958. //=============================================================================
  11959. // CNATHelpUPnP::ExtendUPnPLease
  11960. //-----------------------------------------------------------------------------
  11961. //
  11962. // Description: Asks the UPnP server to extend a port mapping lease.
  11963. //
  11964. // The UPnP device may get removed from list if a failure
  11965. // occurs, the caller needs to have a reference if it's using the
  11966. // pointer.
  11967. //
  11968. // The object lock is assumed to be held.
  11969. //
  11970. // Arguments:
  11971. // CRegisteredPort * pRegisteredPort - Pointer to port object mapping to
  11972. // extend.
  11973. //
  11974. // Returns: HRESULT
  11975. // DPNH_OK - The extension was successful.
  11976. // DPNHERR_GENERIC - An error occurred.
  11977. // DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
  11978. // message.
  11979. //=============================================================================
  11980. HRESULT CNATHelpUPnP::ExtendUPnPLease(CRegisteredPort * const pRegisteredPort)
  11981. {
  11982. HRESULT hr;
  11983. CDevice * pDevice;
  11984. CUPnPDevice * pUPnPDevice;
  11985. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pRegisteredPort);
  11986. DNASSERT(pRegisteredPort != NULL);
  11987. pDevice = pRegisteredPort->GetOwningDevice();
  11988. DNASSERT(pDevice != NULL);
  11989. pUPnPDevice = pDevice->GetUPnPDevice();
  11990. DNASSERT(pUPnPDevice != NULL);
  11991. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  11992. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  11993. #ifdef DBG
  11994. if (pRegisteredPort->HasPermanentUPnPLease())
  11995. {
  11996. DPFX(DPFPREP, 1, "Extending already permanent UPnP lease for registered port 0x%p.",
  11997. pRegisteredPort);
  11998. }
  11999. #endif // DBG
  12000. //
  12001. // UPnP devices don't have port extension per se, you just reregister the
  12002. // mapping.
  12003. //
  12004. hr = this->MapPortsOnUPnPDevice(pUPnPDevice, pRegisteredPort);
  12005. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  12006. return hr;
  12007. } // CNATHelpUPnP::ExtendUPnPLease
  12008. #undef DPF_MODNAME
  12009. #define DPF_MODNAME "CNATHelpUPnP::UnmapUPnPPort"
  12010. //=============================================================================
  12011. // CNATHelpUPnP::UnmapUPnPPort
  12012. //-----------------------------------------------------------------------------
  12013. //
  12014. // Description: Asks the UPnP server to release a port mapping.
  12015. //
  12016. // The UPnP device may get removed from list if a failure
  12017. // occurs, the caller needs to have a reference if it's using the
  12018. // device.
  12019. //
  12020. // The object lock is assumed to be held.
  12021. //
  12022. // Arguments:
  12023. // CRegisteredPort * pRegisteredPort - Pointer to port object mapping to
  12024. // release.
  12025. // DWORD dwMaxValidPort - Highest address index in array to
  12026. // try freeing. This may be one more
  12027. // than the actual number to indicate
  12028. // all should be freed.
  12029. // BOOL fNeedToDeleteRegValue - Whether the corresponding crash
  12030. // recovery registry value needs to
  12031. // be deleted as well.
  12032. //
  12033. // Returns: HRESULT
  12034. // DPNH_OK - The extension was successful.
  12035. // DPNHERR_GENERIC - An error occurred.
  12036. // DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
  12037. // message.
  12038. //=============================================================================
  12039. HRESULT CNATHelpUPnP::UnmapUPnPPort(CRegisteredPort * const pRegisteredPort,
  12040. const DWORD dwMaxValidPort,
  12041. const BOOL fNeedToDeleteRegValue)
  12042. {
  12043. HRESULT hr = DPNH_OK;
  12044. BOOL fStartedWaitingForControlResponse = FALSE;
  12045. CDevice * pDevice;
  12046. CUPnPDevice * pUPnPDevice;
  12047. SOCKADDR_IN * psaddrinPublic;
  12048. SOCKADDR_IN * psaddrinPrivate;
  12049. TCHAR tszExternalPort[32];
  12050. int iContentLength;
  12051. TCHAR tszContentLength[32];
  12052. TCHAR tszHost[22]; // "xxx.xxx.xxx.xxx:xxxxx" + NULL termination
  12053. char * pszMessage = NULL;
  12054. int iMsgSize;
  12055. int iPrevMsgSize = 0;
  12056. int iReturn;
  12057. DWORD dwTemp;
  12058. UPNP_CONTROLRESPONSE_INFO RespInfo;
  12059. DWORD dwStartTime;
  12060. DWORD dwTimeout;
  12061. SOCKADDR_IN * psaddrinHostAddress;
  12062. #ifdef DBG
  12063. DWORD dwError;
  12064. #endif // DBG
  12065. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i, %i)",
  12066. this, pRegisteredPort, ((int) dwMaxValidPort), fNeedToDeleteRegValue);
  12067. DNASSERT(pRegisteredPort != NULL);
  12068. DNASSERT(dwMaxValidPort != 0);
  12069. DNASSERT(dwMaxValidPort <= pRegisteredPort->GetNumAddresses());
  12070. pDevice = pRegisteredPort->GetOwningDevice();
  12071. DNASSERT(pDevice != NULL);
  12072. pUPnPDevice = pDevice->GetUPnPDevice();
  12073. DNASSERT(pUPnPDevice != NULL);
  12074. //
  12075. // GetUPnPDevice did not add a reference to pUPnPDevice for us.
  12076. //
  12077. pUPnPDevice->AddRef();
  12078. DNASSERT(pUPnPDevice->IsReady());
  12079. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  12080. //
  12081. // Prevent trying to remove the lease twice.
  12082. //
  12083. pRegisteredPort->NoteRemovingUPnPLease();
  12084. if (dwMaxValidPort == pRegisteredPort->GetNumAddresses())
  12085. {
  12086. DPFX(DPFPREP, 7, "Unmapping all %u addresses for registered port 0x%p on UPnP device 0x%p.",
  12087. dwMaxValidPort, pRegisteredPort, pUPnPDevice);
  12088. }
  12089. else
  12090. {
  12091. DPFX(DPFPREP, 7, "Error cleanup code, only unmapping first %u addresses (of %u possible) for registered port 0x%p on UPnP device 0x%p.",
  12092. dwMaxValidPort, pRegisteredPort->GetNumAddresses(),
  12093. pRegisteredPort, pUPnPDevice);
  12094. }
  12095. //
  12096. // Set up variables we'll need.
  12097. //
  12098. DNASSERT(pUPnPDevice->GetServiceControlURL() != NULL);
  12099. psaddrinHostAddress = pUPnPDevice->GetHostAddress();
  12100. wsprintf(tszHost, _T("%u.%u.%u.%u:%u"),
  12101. psaddrinHostAddress->sin_addr.S_un.S_un_b.s_b1,
  12102. psaddrinHostAddress->sin_addr.S_un.S_un_b.s_b2,
  12103. psaddrinHostAddress->sin_addr.S_un.S_un_b.s_b3,
  12104. psaddrinHostAddress->sin_addr.S_un.S_un_b.s_b4,
  12105. NTOHS(psaddrinHostAddress->sin_port));
  12106. //
  12107. // Get the array of ports we're releasing.
  12108. //
  12109. psaddrinPublic = pRegisteredPort->GetUPnPPublicAddressesArray();
  12110. psaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  12111. //
  12112. // Loop through each port that we're unmapping.
  12113. //
  12114. for(dwTemp = 0; dwTemp < dwMaxValidPort; dwTemp++)
  12115. {
  12116. //
  12117. // The UPnP Internet Gateway Device spec does not require reference
  12118. // counting for mapped ports. If you register something that had
  12119. // already been registered, it will succeed silently.
  12120. //
  12121. // This means that we will never be able to tell which NAT client was
  12122. // the last person to use a given shared port. You could try detecting
  12123. // any other users at the app level (above DPNATHLP), but there is
  12124. // always a race condition. You could also have a concept of shared-
  12125. // port owner, but then you'd have to implement owner migration a la
  12126. // DPlay host migration. That is sooo not worth it.
  12127. //
  12128. // The other option is to just never unmap shared ports. You can
  12129. // probably imagine the implications of this solution, but it's what we
  12130. // have to do.
  12131. //
  12132. if (pRegisteredPort->IsSharedPort())
  12133. {
  12134. DPFX(DPFPREP, 1, "Registered port 0x%p address index %u (private address %u.%u.%u.%u:%u) is shared, not unmapping.",
  12135. pRegisteredPort, dwTemp,
  12136. psaddrinPublic[dwTemp].sin_addr.S_un.S_un_b.s_b1,
  12137. psaddrinPublic[dwTemp].sin_addr.S_un.S_un_b.s_b2,
  12138. psaddrinPublic[dwTemp].sin_addr.S_un.S_un_b.s_b3,
  12139. psaddrinPublic[dwTemp].sin_addr.S_un.S_un_b.s_b4,
  12140. NTOHS(psaddrinPublic[dwTemp].sin_port));
  12141. continue;
  12142. }
  12143. //
  12144. // If the control socket got disconnected after the last message,
  12145. // then reconnect.
  12146. //
  12147. if (! pUPnPDevice->IsConnected())
  12148. {
  12149. hr = this->ReconnectUPnPControlSocket(pUPnPDevice);
  12150. if (hr != S_OK)
  12151. {
  12152. DPFX(DPFPREP, 0, "Couldn't reconnect UPnP control socket!");
  12153. goto Failure;
  12154. }
  12155. }
  12156. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  12157. wsprintf(tszExternalPort, _T("%u"),
  12158. NTOHS(psaddrinPublic[dwTemp].sin_port));
  12159. iContentLength = strlen("<s:Envelope" EOL)
  12160. + strlen(" xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL)
  12161. + strlen(" s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL)
  12162. + strlen(" <s:Body>" EOL)
  12163. + strlen(" <u:" ACTION_DELETEPORTMAPPING_A " xmlns:u=\"") + pUPnPDevice->GetStaticServiceURILength() + strlen("\">" EOL)
  12164. + strlen(" <" ARG_DELETEPORTMAPPING_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_DELETEPORTMAPPING_NEWREMOTEHOST_A ">" EOL)
  12165. + strlen(" <" ARG_DELETEPORTMAPPING_NEWEXTERNALPORT_A ">") + _tcslen(tszExternalPort) + strlen("</" ARG_DELETEPORTMAPPING_NEWEXTERNALPORT_A ">" EOL)
  12166. + strlen(" <" ARG_DELETEPORTMAPPING_NEWPROTOCOL_A ">") + 3 + strlen("</" ARG_DELETEPORTMAPPING_NEWPROTOCOL_A ">" EOL)
  12167. + strlen(" </u:" ACTION_DELETEPORTMAPPING_A ">" EOL)
  12168. + strlen(" </s:Body>" EOL)
  12169. + strlen("</s:Envelope>" EOL)
  12170. + strlen(EOL);
  12171. wsprintf(tszContentLength, _T("%i"), iContentLength);
  12172. iMsgSize = strlen("POST ") + strlen(pUPnPDevice->GetServiceControlURL()) + strlen(" " HTTP_VERSION EOL)
  12173. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  12174. + strlen("CONTENT-LENGTH: ") + _tcslen(tszContentLength) + strlen(EOL)
  12175. + strlen("CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL)
  12176. + strlen("SOAPACTION: ") + pUPnPDevice->GetStaticServiceURILength() + strlen("#" ACTION_DELETEPORTMAPPING_A EOL)
  12177. + strlen(EOL)
  12178. + iContentLength;
  12179. //
  12180. // Allocate (or reallocate) the message buffer.
  12181. //
  12182. if (iMsgSize > iPrevMsgSize)
  12183. {
  12184. if (pszMessage != NULL)
  12185. {
  12186. DNFree(pszMessage);
  12187. pszMessage = NULL;
  12188. }
  12189. pszMessage = (char*) DNMalloc(iMsgSize + 1); // include room for NULL termination that isn't actually sent
  12190. if (pszMessage == NULL)
  12191. {
  12192. hr = DPNHERR_OUTOFMEMORY;
  12193. goto Failure;
  12194. }
  12195. iPrevMsgSize = iMsgSize;
  12196. }
  12197. strcpy(pszMessage, "POST ");
  12198. strcat(pszMessage, pUPnPDevice->GetServiceControlURL());
  12199. strcat(pszMessage, " " HTTP_VERSION EOL);
  12200. strcat(pszMessage, "HOST: ");
  12201. #ifdef UNICODE
  12202. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  12203. tszHost,
  12204. (_tcslen(tszHost) + 1));
  12205. #else // ! UNICODE
  12206. strcat(pszMessage, tszHost);
  12207. #endif // ! UNICODE
  12208. strcat(pszMessage, EOL);
  12209. strcat(pszMessage, "CONTENT-LENGTH: ");
  12210. #ifdef UNICODE
  12211. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  12212. tszContentLength,
  12213. (_tcslen(tszContentLength) + 1));
  12214. #else // ! UNICODE
  12215. strcat(pszMessage, tszContentLength);
  12216. #endif // ! UNICODE
  12217. strcat(pszMessage, EOL);
  12218. strcat(pszMessage, "CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL);
  12219. strcat(pszMessage, "SOAPACTION: ");
  12220. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  12221. strcat(pszMessage, "#" ACTION_DELETEPORTMAPPING_A EOL);
  12222. strcat(pszMessage, EOL);
  12223. strcat(pszMessage, "<s:Envelope" EOL);
  12224. strcat(pszMessage, " xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL);
  12225. strcat(pszMessage, " s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL);
  12226. strcat(pszMessage, " <s:Body>" EOL);
  12227. strcat(pszMessage, " <u:" ACTION_DELETEPORTMAPPING_A " xmlns:u=\"");
  12228. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  12229. strcat(pszMessage, "\">" EOL);
  12230. strcat(pszMessage, " <" ARG_DELETEPORTMAPPING_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_DELETEPORTMAPPING_NEWREMOTEHOST_A ">" EOL);
  12231. strcat(pszMessage, " <" ARG_DELETEPORTMAPPING_NEWEXTERNALPORT_A ">");
  12232. #ifdef UNICODE
  12233. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  12234. tszExternalPort,
  12235. (_tcslen(tszExternalPort) + 1));
  12236. #else // ! UNICODE
  12237. strcat(pszMessage, tszExternalPort);
  12238. #endif // ! UNICODE
  12239. strcat(pszMessage, "</" ARG_DELETEPORTMAPPING_NEWEXTERNALPORT_A ">" EOL);
  12240. strcat(pszMessage, " <" ARG_DELETEPORTMAPPING_NEWPROTOCOL_A ">");
  12241. strcat(pszMessage, ((pRegisteredPort->IsTCP()) ? "TCP" : "UDP"));
  12242. strcat(pszMessage, "</" ARG_DELETEPORTMAPPING_NEWPROTOCOL_A ">" EOL);
  12243. strcat(pszMessage, " </u:" ACTION_DELETEPORTMAPPING_A ">" EOL);
  12244. strcat(pszMessage, " </s:Body>" EOL);
  12245. strcat(pszMessage, "</s:Envelope>" EOL);
  12246. strcat(pszMessage, EOL);
  12247. #ifdef DBG
  12248. this->PrintUPnPTransactionToFile(pszMessage,
  12249. iMsgSize,
  12250. "Outbound delete port mapping request",
  12251. pDevice);
  12252. #endif // DBG
  12253. iReturn = this->m_pfnsend(pUPnPDevice->GetControlSocket(),
  12254. pszMessage,
  12255. iMsgSize,
  12256. 0);
  12257. if (iReturn == SOCKET_ERROR)
  12258. {
  12259. #ifdef DBG
  12260. dwError = this->m_pfnWSAGetLastError();
  12261. DPFX(DPFPREP, 0, "Got sockets error %u when sending control request to UPnP device!", dwError);
  12262. #endif // DBG
  12263. hr = DPNHERR_GENERIC;
  12264. goto Failure;
  12265. }
  12266. if (iReturn != iMsgSize)
  12267. {
  12268. DPFX(DPFPREP, 0, "Didn't send entire message (%i != %i)?!", iReturn, iMsgSize);
  12269. DNASSERT(FALSE);
  12270. hr = DPNHERR_GENERIC;
  12271. goto Failure;
  12272. }
  12273. //
  12274. // We have the lock so no one could have tried to receive data from
  12275. // the control socket yet. Mark the device as waiting for a
  12276. // response.
  12277. //
  12278. ZeroMemory(&RespInfo, sizeof(RespInfo));
  12279. pUPnPDevice->StartWaitingForControlResponse(CONTROLRESPONSETYPE_DELETEPORTMAPPING,
  12280. &RespInfo);
  12281. fStartedWaitingForControlResponse = TRUE;
  12282. //
  12283. // Actually wait for the response.
  12284. //
  12285. dwStartTime = GETTIMESTAMP();
  12286. dwTimeout = g_dwUPnPResponseTimeout;
  12287. do
  12288. {
  12289. hr = this->CheckForReceivedUPnPMsgsOnDevice(pUPnPDevice, dwTimeout);
  12290. if (hr != DPNH_OK)
  12291. {
  12292. DPFX(DPFPREP, 0, "Failed receiving UPnP messages!");
  12293. goto Failure;
  12294. }
  12295. //
  12296. // We either timed out or got some data. Check if we got a
  12297. // response of some type.
  12298. //
  12299. if (! pUPnPDevice->IsWaitingForControlResponse())
  12300. {
  12301. break;
  12302. }
  12303. //
  12304. // Make sure our device is still connected.
  12305. //
  12306. if (! pUPnPDevice->IsConnected())
  12307. {
  12308. DPFX(DPFPREP, 0, "UPnP device 0x%p disconnected while deleting port index %u!",
  12309. pUPnPDevice, dwTemp);
  12310. pUPnPDevice->StopWaitingForControlResponse();
  12311. hr = DPNHERR_SERVERNOTRESPONDING;
  12312. goto Failure;
  12313. }
  12314. //
  12315. // Calculate how long we have left to wait. If the calculation
  12316. // goes negative, it means we're done.
  12317. //
  12318. dwTimeout = g_dwUPnPResponseTimeout - (GETTIMESTAMP() - dwStartTime);
  12319. }
  12320. while (((int) dwTimeout > 0));
  12321. //
  12322. // If we never got the response, stop waiting for it.
  12323. //
  12324. if (pUPnPDevice->IsWaitingForControlResponse())
  12325. {
  12326. pUPnPDevice->StopWaitingForControlResponse();
  12327. DPFX(DPFPREP, 0, "Server didn't respond in time for port index %u!",
  12328. dwTemp);
  12329. hr = DPNHERR_SERVERNOTRESPONDING;
  12330. goto Failure;
  12331. }
  12332. //
  12333. // If we're here, then we've gotten a valid response (success or
  12334. // failure) from the server. If it's a failure, print out a note
  12335. // but continue.
  12336. //
  12337. #ifdef DBG
  12338. switch (RespInfo.hrErrorCode)
  12339. {
  12340. case DPNH_OK:
  12341. {
  12342. //
  12343. // Succeeded.
  12344. //
  12345. break;
  12346. }
  12347. case DPNHERR_NOMAPPING:
  12348. {
  12349. //
  12350. // UPnP device didn't know what we were talking about.
  12351. //
  12352. DPFX(DPFPREP, 1, "Server didn't recognize mapping for port index %u, continuing.",
  12353. dwTemp);
  12354. break;
  12355. }
  12356. default:
  12357. {
  12358. //
  12359. // Something else happened.
  12360. //
  12361. DPFX(DPFPREP, 0, "Server returned failure response 0x%lx for port index %u! Ignoring.",
  12362. RespInfo.hrErrorCode, dwTemp);
  12363. break;
  12364. }
  12365. }
  12366. #endif // DBG
  12367. //
  12368. // If the lease is permanent, we need to remove the reference from
  12369. // the registry.
  12370. //
  12371. if (pRegisteredPort->HasPermanentUPnPLease())
  12372. {
  12373. IN_ADDR inaddrTemp;
  12374. TCHAR tszInternalClient[16]; // "xxx.xxx.xxx.xxx" + NULL termination
  12375. TCHAR tszInternalPort[32];
  12376. DWORD dwDescriptionLength;
  12377. CRegistry RegObject;
  12378. WCHAR wszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  12379. #ifdef UNICODE
  12380. TCHAR * ptszDescription = wszDescription;
  12381. #else // ! UNICODE
  12382. char szDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  12383. TCHAR * ptszDescription = szDescription;
  12384. #endif // ! UNICODE
  12385. //
  12386. // Note that the device address is not necessarily the same as
  12387. // the address the user originally registered, particularly the
  12388. // 0.0.0.0 wildcard address will get remapped.
  12389. //
  12390. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  12391. wsprintf(tszInternalClient, _T("%u.%u.%u.%u"),
  12392. inaddrTemp.S_un.S_un_b.s_b1,
  12393. inaddrTemp.S_un.S_un_b.s_b2,
  12394. inaddrTemp.S_un.S_un_b.s_b3,
  12395. inaddrTemp.S_un.S_un_b.s_b4);
  12396. wsprintf(tszInternalPort, _T("%u"),
  12397. NTOHS(psaddrinPrivate[dwTemp].sin_port));
  12398. //
  12399. // Generate a description for this mapping. The format is:
  12400. //
  12401. // [executable_name] (nnn.nnn.nnn.nnn:nnnnn) nnnnn {"TCP" | "UDP"}
  12402. //
  12403. // That way nothing needs to be localized.
  12404. //
  12405. dwDescriptionLength = GetModuleFileName(NULL,
  12406. ptszDescription,
  12407. (MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1));
  12408. if (dwDescriptionLength != 0)
  12409. {
  12410. //
  12411. // Be paranoid and make sure the description string is valid.
  12412. //
  12413. ptszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1] = 0;
  12414. //
  12415. // Get just the executable name from the path.
  12416. //
  12417. #ifdef WINCE
  12418. GetExeName(ptszDescription);
  12419. #else // ! WINCE
  12420. #ifdef UNICODE
  12421. _wsplitpath(ptszDescription, NULL, NULL, ptszDescription, NULL);
  12422. #else // ! UNICODE
  12423. _splitpath(ptszDescription, NULL, NULL, ptszDescription, NULL);
  12424. #endif // ! UNICODE
  12425. #endif // ! WINCE
  12426. dwDescriptionLength = _tcslen(ptszDescription) // executable name
  12427. + 2 // " ("
  12428. + _tcslen(tszInternalClient) // private IP address
  12429. + 1 // ":"
  12430. + _tcslen(tszInternalPort) // private port
  12431. + 2 // ") "
  12432. + _tcslen(tszExternalPort) // public port
  12433. + 4; // " TCP" | " UDP"
  12434. //
  12435. // Make sure the long string will fit. If not, use the
  12436. // abbreviated version.
  12437. //
  12438. if (dwDescriptionLength > MAX_UPNP_MAPPING_DESCRIPTION_SIZE)
  12439. {
  12440. dwDescriptionLength = 0;
  12441. }
  12442. }
  12443. if (dwDescriptionLength == 0)
  12444. {
  12445. //
  12446. // Use the abbreviated version we know will fit.
  12447. //
  12448. wsprintf(ptszDescription,
  12449. _T("(%s:%s) %s %s"),
  12450. tszInternalClient,
  12451. tszInternalPort,
  12452. tszExternalPort,
  12453. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  12454. }
  12455. else
  12456. {
  12457. //
  12458. // There's enough room, tack on the rest of the
  12459. // description.
  12460. //
  12461. wsprintf((ptszDescription + _tcslen(ptszDescription)),
  12462. _T(" (%s:%s) %s %s"),
  12463. tszInternalClient,
  12464. tszInternalPort,
  12465. tszExternalPort,
  12466. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  12467. }
  12468. if (fNeedToDeleteRegValue)
  12469. {
  12470. DPFX(DPFPREP, 7, "Removing NAT lease \"%s\" crash cleanup registry entry.",
  12471. ptszDescription);
  12472. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  12473. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVENATMAPPINGS,
  12474. FALSE,
  12475. TRUE,
  12476. TRUE,
  12477. DPN_KEY_ALL_ACCESS))
  12478. {
  12479. DPFX(DPFPREP, 0, "Couldn't open active NAT mapping key, unable to remove crash cleanup reference!");
  12480. }
  12481. else
  12482. {
  12483. #ifndef UNICODE
  12484. dwDescriptionLength = strlen(szDescription) + 1;
  12485. hr = STR_jkAnsiToWide(wszDescription, szDescription, dwDescriptionLength);
  12486. if (hr != S_OK)
  12487. {
  12488. DPFX(DPFPREP, 0, "Couldn't convert NAT mapping description to Unicode (err = 0x%lx), unable to remove crash cleanup reference!",
  12489. hr);
  12490. //
  12491. // Ignore error and continue.
  12492. //
  12493. hr = S_OK;
  12494. }
  12495. else
  12496. #endif // ! UNICODE
  12497. {
  12498. BOOL fResult;
  12499. //
  12500. // Ignore error.
  12501. //
  12502. fResult = RegObject.DeleteValue(wszDescription);
  12503. if (! fResult)
  12504. {
  12505. DPFX(DPFPREP, 0, "Couldn't delete NAT mapping value \"%s\"! Continuing.",
  12506. ptszDescription);
  12507. }
  12508. }
  12509. RegObject.Close();
  12510. }
  12511. }
  12512. else
  12513. {
  12514. DPFX(DPFPREP, 6, "No need to remove NAT lease \"%s\" crash cleanup registry entry.",
  12515. ptszDescription);
  12516. }
  12517. }
  12518. else
  12519. {
  12520. //
  12521. // Registered port doesn't have a permanent UPnP lease.
  12522. //
  12523. }
  12524. //
  12525. // Move on to next port.
  12526. //
  12527. }
  12528. //
  12529. // If we're here, everything was successful.
  12530. //
  12531. DPFX(DPFPREP, 8, "Registered port 0x%p mapping successfully deleted from UPnP device (0x%p).",
  12532. pRegisteredPort, pUPnPDevice);
  12533. Exit:
  12534. if (pszMessage != NULL)
  12535. {
  12536. DNFree(pszMessage);
  12537. pszMessage = NULL;
  12538. }
  12539. //
  12540. // No matter whether we succeeded or failed, remove the UPnP public addresses
  12541. // array and decrement the total lease count.
  12542. //
  12543. pRegisteredPort->DestroyUPnPPublicAddressesArray();
  12544. DNASSERT(this->m_dwNumLeases > 0);
  12545. this->m_dwNumLeases--;
  12546. DPFX(DPFPREP, 7, "UPnP lease for 0x%p removed, total num leases = %u.",
  12547. pRegisteredPort, this->m_dwNumLeases);
  12548. pRegisteredPort->NoteNotPermanentUPnPLease();
  12549. pRegisteredPort->NoteNotRemovingUPnPLease();
  12550. pUPnPDevice->DecRef();
  12551. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  12552. return hr;
  12553. Failure:
  12554. //
  12555. // If we started waiting for a response, clear that.
  12556. //
  12557. if (fStartedWaitingForControlResponse)
  12558. {
  12559. pUPnPDevice->StopWaitingForControlResponse();
  12560. }
  12561. goto Exit;
  12562. } // CNATHelpUPnP::UnmapUPnPPort
  12563. #undef DPF_MODNAME
  12564. #define DPF_MODNAME "CNATHelpUPnP::CleanupInactiveNATMappings"
  12565. //=============================================================================
  12566. // CNATHelpUPnP::CleanupInactiveNATMappings
  12567. //-----------------------------------------------------------------------------
  12568. //
  12569. // Description: Looks for any mappings previously made by other DPNHUPNP
  12570. // instances that are no longer active (because of a crash), and
  12571. // unmaps them.
  12572. //
  12573. // The UPnP device may get removed from list if a failure
  12574. // occurs, the caller needs to have a reference if it's using the
  12575. // device.
  12576. //
  12577. // The object lock is assumed to be held.
  12578. //
  12579. // Arguments:
  12580. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device to use.
  12581. //
  12582. // Returns: HRESULT
  12583. // DPNH_OK - The extension was successful.
  12584. // DPNHERR_GENERIC - An error occurred.
  12585. // DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
  12586. // message.
  12587. //=============================================================================
  12588. HRESULT CNATHelpUPnP::CleanupInactiveNATMappings(CUPnPDevice * const pUPnPDevice)
  12589. {
  12590. HRESULT hr = DPNH_OK;
  12591. CDevice * pDevice;
  12592. CRegistry RegObject;
  12593. BOOL fOpenedRegistry = FALSE;
  12594. DWORD dwIndex;
  12595. WCHAR wszValueName[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  12596. DWORD dwValueNameSize;
  12597. DPNHACTIVENATMAPPING dpnhanm;
  12598. DWORD dwValueSize;
  12599. CBilink * pBilink;
  12600. CUPnPDevice * pUPnPDeviceTemp = NULL; // NULLed out because PREfast has been hassling me for a while, even though the code is safe
  12601. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  12602. DNHANDLE hNamedObject = NULL;
  12603. CRegisteredPort * pRegisteredPort = NULL;
  12604. BOOL fSetPrivateAddresses = FALSE;
  12605. SOCKADDR_IN saddrinPrivate;
  12606. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pUPnPDevice);
  12607. DNASSERT(pUPnPDevice != NULL);
  12608. pDevice = pUPnPDevice->GetOwningDevice();
  12609. DNASSERT(pDevice != NULL);
  12610. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  12611. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVENATMAPPINGS,
  12612. FALSE,
  12613. TRUE,
  12614. TRUE,
  12615. DPN_KEY_ALL_ACCESS))
  12616. {
  12617. DPFX(DPFPREP, 1, "Couldn't open active NAT mapping key, not performing crash cleanup.");
  12618. DNASSERT(hr == DPNH_OK);
  12619. goto Exit;
  12620. }
  12621. fOpenedRegistry = TRUE;
  12622. //
  12623. // Walk the list of active mappings.
  12624. //
  12625. dwIndex = 0;
  12626. do
  12627. {
  12628. dwValueNameSize = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  12629. if (! RegObject.EnumValues(wszValueName, &dwValueNameSize, dwIndex))
  12630. {
  12631. //
  12632. // There was an error or there aren't any more keys. We're done.
  12633. //
  12634. break;
  12635. }
  12636. //
  12637. // Try reading that mapping's data.
  12638. //
  12639. dwValueSize = sizeof(dpnhanm);
  12640. if (! RegObject.ReadBlob(wszValueName, (LPBYTE) (&dpnhanm), &dwValueSize))
  12641. {
  12642. //
  12643. // We don't have a lock protecting the registry, so some other
  12644. // instance could have deleted the key between when we enumerated
  12645. // it and now. We'll stop trying (and hopefully that other
  12646. // instance will cover the rest of the items).
  12647. //
  12648. DPFX(DPFPREP, 0, "Couldn't read \"%ls\" mapping value! Done with cleanup.",
  12649. wszValueName);
  12650. DNASSERT(hr == DPNH_OK);
  12651. goto Exit;
  12652. }
  12653. //
  12654. // Validate the data read.
  12655. //
  12656. if ((dwValueSize != sizeof(dpnhanm)) ||
  12657. (dpnhanm.dwVersion != ACTIVE_MAPPING_VERSION))
  12658. {
  12659. DPFX(DPFPREP, 0, "The \"%ls\" mapping value is invalid! Done with cleanup.",
  12660. wszValueName);
  12661. //
  12662. // Move to next item.
  12663. //
  12664. dwIndex++;
  12665. continue;
  12666. }
  12667. //
  12668. // See if it's owned by the local NATHelp instance.
  12669. //
  12670. if (dpnhanm.dwInstanceKey == this->m_dwInstanceKey)
  12671. {
  12672. //
  12673. // We own(ed) it. See if it was associated with a UPnP device
  12674. // that's now gone.
  12675. //
  12676. pBilink = this->m_blUPnPDevices.GetNext();
  12677. while (pBilink != &this->m_blUPnPDevices)
  12678. {
  12679. DNASSERT(! pBilink->IsEmpty());
  12680. pUPnPDeviceTemp = UPNPDEVICE_FROM_BILINK(pBilink);
  12681. if (pUPnPDeviceTemp->GetID() == dpnhanm.dwUPnPDeviceID)
  12682. {
  12683. //
  12684. // This mapping truly is active, leave it alone.
  12685. //
  12686. break;
  12687. }
  12688. pBilink = pBilink->GetNext();
  12689. }
  12690. //
  12691. // If we found the mapping, go on to the next one.
  12692. //
  12693. if (pBilink != &this->m_blUPnPDevices)
  12694. {
  12695. //
  12696. // Note that despite what PREfast v1.0.1195 says,
  12697. // pUPnPDeviceTemp will always be valid if we get here.
  12698. // However, I gave in and NULLed out the pointer up top.
  12699. //
  12700. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to current instance (%u)'s UPnP device 0x%p.",
  12701. wszValueName, dpnhanm.dwInstanceKey, pUPnPDeviceTemp);
  12702. //
  12703. // Move to next item.
  12704. //
  12705. dwIndex++;
  12706. continue;
  12707. }
  12708. //
  12709. // Otherwise, we gave up on this mapping earlier.
  12710. //
  12711. DNASSERT((this->m_dwNumDeviceRemoves > 0) || (this->m_dwNumServerFailures > 0));
  12712. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" was owned by current instance (%u)'s UPnP device ID %u that no longer exists.",
  12713. wszValueName, dpnhanm.dwInstanceKey, dpnhanm.dwUPnPDeviceID);
  12714. }
  12715. else
  12716. {
  12717. //
  12718. // See if that DPNHUPNP instance is still around.
  12719. //
  12720. #ifndef WINCE
  12721. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  12722. {
  12723. wsprintf(tszObjectName, _T( "Global\\" ) INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  12724. }
  12725. else
  12726. #endif // ! WINCE
  12727. {
  12728. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  12729. }
  12730. hNamedObject = DNOpenEvent(SYNCHRONIZE, FALSE, tszObjectName);
  12731. if (hNamedObject != NULL)
  12732. {
  12733. //
  12734. // This is still an active mapping.
  12735. //
  12736. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to instance %u, which is still active.",
  12737. wszValueName, dpnhanm.dwInstanceKey);
  12738. DNCloseHandle(hNamedObject);
  12739. hNamedObject = NULL;
  12740. //
  12741. // Move to next item.
  12742. //
  12743. dwIndex++;
  12744. continue;
  12745. }
  12746. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to instance %u, which no longer exists.",
  12747. wszValueName, dpnhanm.dwInstanceKey);
  12748. }
  12749. //
  12750. // Delete the value now that we have the information we need.
  12751. //
  12752. if (! RegObject.DeleteValue(wszValueName))
  12753. {
  12754. //
  12755. // See ReadBlob comments. Stop trying to cleanup.
  12756. //
  12757. DPFX(DPFPREP, 0, "Couldn't delete \"%ls\"! Done with cleanup.",
  12758. wszValueName);
  12759. DNASSERT(hr == DPNH_OK);
  12760. goto Exit;
  12761. }
  12762. //
  12763. // Create a fake registered port that we will deregister. Ignore the
  12764. // firewall state flags.
  12765. //
  12766. pRegisteredPort = new CRegisteredPort(0, (dpnhanm.dwFlags & REGPORTOBJMASK_UPNP));
  12767. if (pRegisteredPort == NULL)
  12768. {
  12769. hr = DPNHERR_OUTOFMEMORY;
  12770. goto Failure;
  12771. }
  12772. //
  12773. // Assert that the other UPnP information/state flags are not set.
  12774. //
  12775. DNASSERT(! pRegisteredPort->IsUPnPPortUnavailable());
  12776. DNASSERT(! pRegisteredPort->IsRemovingUPnPLease());
  12777. //
  12778. // Temporarily associate the registered port with the device.
  12779. //
  12780. pRegisteredPort->MakeDeviceOwner(pDevice);
  12781. ZeroMemory(&saddrinPrivate, sizeof(saddrinPrivate));
  12782. saddrinPrivate.sin_family = AF_INET;
  12783. saddrinPrivate.sin_addr.S_un.S_addr = dpnhanm.dwInternalAddressV4;
  12784. saddrinPrivate.sin_port = dpnhanm.wInternalPort;
  12785. //
  12786. // Store the private address.
  12787. //
  12788. hr = pRegisteredPort->SetPrivateAddresses(&saddrinPrivate, 1);
  12789. if (hr != DPNH_OK)
  12790. {
  12791. DPFX(DPFPREP, 0, "Failed creating UPnP address array!");
  12792. goto Failure;
  12793. }
  12794. fSetPrivateAddresses = TRUE;
  12795. //
  12796. // Create the public address array.
  12797. //
  12798. hr = pRegisteredPort->CreateUPnPPublicAddressesArray();
  12799. if (hr != DPNH_OK)
  12800. {
  12801. DPFX(DPFPREP, 0, "Failed creating UPnP address array!");
  12802. goto Failure;
  12803. }
  12804. //
  12805. // Fake increase the number of leases we have. It will just get
  12806. // decremented in UnmapUPnPPort.
  12807. //
  12808. DPFX(DPFPREP, 7, "Creating temporary UPnP lease 0x%p, total num leases = %u.",
  12809. pRegisteredPort, this->m_dwNumLeases);
  12810. this->m_dwNumLeases++;
  12811. //
  12812. // Store the public port.
  12813. //
  12814. pRegisteredPort->SetUPnPPublicV4Address(0,
  12815. dpnhanm.dwExternalAddressV4,
  12816. dpnhanm.wExternalPort);
  12817. //
  12818. // Actually free the port.
  12819. //
  12820. hr = this->UnmapUPnPPort(pRegisteredPort, 1, FALSE);
  12821. if (hr != DPNH_OK)
  12822. {
  12823. DPFX(DPFPREP, 0, "Failed deleting temporary UPnP port!");
  12824. goto Failure;
  12825. }
  12826. pRegisteredPort->ClearPrivateAddresses();
  12827. fSetPrivateAddresses = FALSE;
  12828. pRegisteredPort->ClearDeviceOwner();
  12829. delete pRegisteredPort;
  12830. pRegisteredPort = NULL;
  12831. //
  12832. // Move to the next mapping. Don't increment index since we just
  12833. // deleted the previous entry and everything shifts down one.
  12834. //
  12835. }
  12836. while (TRUE);
  12837. Exit:
  12838. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  12839. return hr;
  12840. Failure:
  12841. if (pRegisteredPort != NULL)
  12842. {
  12843. if (pRegisteredPort->HasUPnPPublicAddresses())
  12844. {
  12845. pRegisteredPort->DestroyUPnPPublicAddressesArray();
  12846. //
  12847. // Remove the lease counter.
  12848. //
  12849. DNASSERT(this->m_dwNumLeases > 0);
  12850. this->m_dwNumLeases--;
  12851. DPFX(DPFPREP, 7, "UPnP lease for 0x%p removed, total num leases = %u.",
  12852. pRegisteredPort, this->m_dwNumLeases);
  12853. }
  12854. if (fSetPrivateAddresses)
  12855. {
  12856. pRegisteredPort->ClearPrivateAddresses();
  12857. fSetPrivateAddresses = FALSE;
  12858. }
  12859. pRegisteredPort->ClearDeviceOwner();
  12860. delete pRegisteredPort;
  12861. pRegisteredPort = NULL;
  12862. }
  12863. if (fOpenedRegistry)
  12864. {
  12865. RegObject.Close();
  12866. }
  12867. goto Exit;
  12868. } // CNATHelpUPnP::CleanupInactiveNATMappings
  12869. #undef DPF_MODNAME
  12870. #define DPF_MODNAME "CNATHelpUPnP::IsNATPublicPortInUseLocally"
  12871. //=============================================================================
  12872. // CNATHelpUPnP::IsNATPublicPortInUseLocally
  12873. //-----------------------------------------------------------------------------
  12874. //
  12875. // Description: Looks for any mappings previously made by DPNHUPNP instances
  12876. // that are still active that use the given public port.
  12877. //
  12878. // The object lock is assumed to be held.
  12879. //
  12880. // Arguments:
  12881. // WORD wPortHostOrder - Port to check, in host byte order.
  12882. //
  12883. // Returns: BOOL
  12884. //=============================================================================
  12885. BOOL CNATHelpUPnP::IsNATPublicPortInUseLocally(const WORD wPortHostOrder)
  12886. {
  12887. BOOL fResult = FALSE;
  12888. WORD wExternalPort;
  12889. CRegistry RegObject;
  12890. BOOL fOpenedRegistry = FALSE;
  12891. DWORD dwIndex;
  12892. WCHAR wszValueName[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  12893. DWORD dwValueNameSize;
  12894. DPNHACTIVENATMAPPING dpnhanm;
  12895. CBilink * pBilink;
  12896. CUPnPDevice * pUPnPDevice = NULL; // NULLed out because PREfast has been hassling me for a while, even though the code is safe
  12897. DWORD dwValueSize;
  12898. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  12899. DNHANDLE hNamedObject = NULL;
  12900. DPFX(DPFPREP, 6, "(0x%p) Parameters: (%u)", this, wPortHostOrder);
  12901. wExternalPort = HTONS(wPortHostOrder);
  12902. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  12903. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVENATMAPPINGS,
  12904. FALSE,
  12905. TRUE,
  12906. TRUE,
  12907. DPN_KEY_ALL_ACCESS))
  12908. {
  12909. DPFX(DPFPREP, 1, "Couldn't open active NAT mapping key, assuming port not in use.");
  12910. goto Exit;
  12911. }
  12912. fOpenedRegistry = TRUE;
  12913. //
  12914. // Walk the list of active mappings.
  12915. //
  12916. dwIndex = 0;
  12917. do
  12918. {
  12919. dwValueNameSize = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  12920. if (! RegObject.EnumValues(wszValueName, &dwValueNameSize, dwIndex))
  12921. {
  12922. //
  12923. // There was an error or there aren't any more keys. We're done.
  12924. //
  12925. break;
  12926. }
  12927. //
  12928. // Try reading that mapping's data.
  12929. //
  12930. dwValueSize = sizeof(dpnhanm);
  12931. if (! RegObject.ReadBlob(wszValueName, (LPBYTE) (&dpnhanm), &dwValueSize))
  12932. {
  12933. //
  12934. // We don't have a lock protecting the registry, so some other
  12935. // instance could have deleted the key between when we enumerated
  12936. // it and now. We'll stop trying (and hopefully that other
  12937. // instance will cover the rest of the items).
  12938. //
  12939. DPFX(DPFPREP, 0, "Couldn't read \"%ls\" mapping value, assuming port not in use.",
  12940. wszValueName);
  12941. goto Exit;
  12942. }
  12943. //
  12944. // Validate the data read.
  12945. //
  12946. if ((dwValueSize != sizeof(dpnhanm)) ||
  12947. (dpnhanm.dwVersion != ACTIVE_MAPPING_VERSION))
  12948. {
  12949. DPFX(DPFPREP, 0, "The \"%ls\" mapping value is invalid, assuming port not in use.",
  12950. wszValueName);
  12951. //
  12952. // Move to next item.
  12953. //
  12954. dwIndex++;
  12955. continue;
  12956. }
  12957. //
  12958. // Is this the right port?
  12959. //
  12960. if (dpnhanm.wExternalPort == wExternalPort)
  12961. {
  12962. //
  12963. // See if it's owned by the local NATHelp instance.
  12964. //
  12965. if (dpnhanm.dwInstanceKey == this->m_dwInstanceKey)
  12966. {
  12967. //
  12968. // We own(ed) it. See if it was associated with a UPnP device
  12969. // that's now gone.
  12970. //
  12971. pBilink = this->m_blUPnPDevices.GetNext();
  12972. while (pBilink != &this->m_blUPnPDevices)
  12973. {
  12974. DNASSERT(! pBilink->IsEmpty());
  12975. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  12976. if (pUPnPDevice->GetID() == dpnhanm.dwUPnPDeviceID)
  12977. {
  12978. //
  12979. // This mapping truly still active.
  12980. //
  12981. fResult = TRUE;
  12982. break;
  12983. }
  12984. pBilink = pBilink->GetNext();
  12985. }
  12986. if (pBilink != &this->m_blUPnPDevices)
  12987. {
  12988. //
  12989. // Note that despite what PREfast v1.0.1195 says,
  12990. // pUPnPDevice will always be valid if we get here.
  12991. // However, I gave in and NULLed out the pointer up top.
  12992. //
  12993. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to current instance (%u)'s UPnP device 0x%p.",
  12994. wszValueName, dpnhanm.dwInstanceKey, pUPnPDevice);
  12995. }
  12996. else
  12997. {
  12998. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" was owned by current instance (%u)'s UPnP device ID %u that no longer exists.",
  12999. wszValueName, dpnhanm.dwInstanceKey, dpnhanm.dwUPnPDeviceID);
  13000. }
  13001. }
  13002. else
  13003. {
  13004. //
  13005. // See if that DPNHUPNP instance is still around.
  13006. //
  13007. #ifndef WINCE
  13008. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  13009. {
  13010. wsprintf(tszObjectName, _T( "Global\\" ) INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  13011. }
  13012. else
  13013. #endif // ! WINCE
  13014. {
  13015. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  13016. }
  13017. hNamedObject = DNOpenEvent(SYNCHRONIZE, FALSE, tszObjectName);
  13018. if (hNamedObject != NULL)
  13019. {
  13020. //
  13021. // This is still an active instance. Since we can't walk
  13022. // his list of UPnP devices, we have to assume the port is
  13023. // still in use.
  13024. //
  13025. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to instance %u, which is still active. Assuming port in use.",
  13026. wszValueName, dpnhanm.dwInstanceKey);
  13027. DNCloseHandle(hNamedObject);
  13028. hNamedObject = NULL;
  13029. fResult = TRUE;
  13030. }
  13031. else
  13032. {
  13033. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to instance %u, which no longer exists.",
  13034. wszValueName, dpnhanm.dwInstanceKey);
  13035. }
  13036. }
  13037. //
  13038. // We found the mapping. We have our result now.
  13039. //
  13040. goto Exit;
  13041. }
  13042. //
  13043. // If we're here, this is not the external port we're looking for.
  13044. //
  13045. DPFX(DPFPREP, 8, "NAT mapping \"%ls\" does not use external port %u.",
  13046. wszValueName, wPortHostOrder);
  13047. //
  13048. // Move to the next mapping.
  13049. //
  13050. dwIndex++;
  13051. }
  13052. while (TRUE);
  13053. //
  13054. // If we're here, we didn't find the mapping.
  13055. //
  13056. DPFX(DPFPREP, 4, "Didn't find any local NAT mappings that use external port %u.",
  13057. wPortHostOrder);
  13058. Exit:
  13059. if (fOpenedRegistry)
  13060. {
  13061. RegObject.Close();
  13062. }
  13063. DPFX(DPFPREP, 6, "(0x%p) Returning: [%i]", this, fResult);
  13064. return fResult;
  13065. } // CNATHelpUPnP::IsNATPublicPortInUseLocally
  13066. #undef DPF_MODNAME
  13067. #define DPF_MODNAME "CNATHelpUPnP::CheckForUPnPAnnouncements"
  13068. //=============================================================================
  13069. // CNATHelpUPnP::CheckForUPnPAnnouncements
  13070. //-----------------------------------------------------------------------------
  13071. //
  13072. // Description: Receives any UPnP announcement messages sent to this control
  13073. // point. The entire timeout period will elapse, unless all
  13074. // devices get responses earlier.
  13075. //
  13076. // This will only send discovery requests for local devices
  13077. // unless fSendRemoteGatewayDiscovery is TRUE. However, we may
  13078. // still detect new ones if we got a straggling response from the
  13079. // last time we were allowed to send remotely.
  13080. //
  13081. // The object lock is assumed to be held.
  13082. //
  13083. // Arguments:
  13084. // DWORD dwTimeout - How long to wait for messages to
  13085. // arrive.
  13086. // BOOL fSendRemoteGatewayDiscovery - Whether we can search remotely or
  13087. // not.
  13088. //
  13089. // Returns: HRESULT
  13090. // DPNH_OK - Messages were received successfully.
  13091. // DPNHERR_GENERIC - An error occurred.
  13092. //=============================================================================
  13093. HRESULT CNATHelpUPnP::CheckForUPnPAnnouncements(const DWORD dwTimeout,
  13094. const BOOL fSendRemoteGatewayDiscovery)
  13095. {
  13096. HRESULT hr;
  13097. DWORD dwNumberOfTimes = 0;
  13098. DWORD dwCurrentTime;
  13099. DWORD dwEndTime;
  13100. DWORD dwNextSearchMessageTime;
  13101. FD_SET fdsRead;
  13102. DWORD dwNumDevicesSearchingForUPnPDevices;
  13103. timeval tv;
  13104. CBilink * pBilink;
  13105. CDevice * pDevice;
  13106. int iReturn;
  13107. int iRecvAddressSize;
  13108. char acBuffer[UPNP_DGRAM_RECV_BUFFER_SIZE];
  13109. SOCKADDR_IN saddrinRecvAddress;
  13110. DWORD dwError;
  13111. BOOL fInitiatedConnect = FALSE;
  13112. #ifdef DBG
  13113. BOOL fGotData = FALSE;
  13114. #endif // DBG
  13115. DPFX(DPFPREP, 5, "(0x%p) Parameters:(%u, %i)",
  13116. this, dwTimeout, fSendRemoteGatewayDiscovery);
  13117. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  13118. dwCurrentTime = GETTIMESTAMP();
  13119. dwEndTime = dwCurrentTime + dwTimeout;
  13120. dwNextSearchMessageTime = dwCurrentTime;
  13121. //
  13122. // Keep looping until the timeout elapses.
  13123. //
  13124. do
  13125. {
  13126. FD_ZERO(&fdsRead);
  13127. dwNumDevicesSearchingForUPnPDevices = 0;
  13128. //
  13129. // Build an FD_SET for all the sockets and send out search messages for
  13130. // all devices.
  13131. //
  13132. DNASSERT(! this->m_blDevices.IsEmpty());
  13133. pBilink = this->m_blDevices.GetNext();
  13134. while (pBilink != &this->m_blDevices)
  13135. {
  13136. DNASSERT(! pBilink->IsEmpty());
  13137. pDevice = DEVICE_FROM_BILINK(pBilink);
  13138. //
  13139. // We add it to the set whether we search or not, since if we're
  13140. // not searching, we're going to be clearing straggling messages.
  13141. //
  13142. DNASSERT(pDevice->GetUPnPDiscoverySocket() != INVALID_SOCKET);
  13143. FD_SET(pDevice->GetUPnPDiscoverySocket(), &fdsRead);
  13144. //
  13145. // Don't send search messages if we already have a UPnP device or
  13146. // this is the loopback adapter.
  13147. //
  13148. if ((pDevice->GetUPnPDevice() == NULL) &&
  13149. (pDevice->GetLocalAddressV4() != NETWORKBYTEORDER_INADDR_LOOPBACK))
  13150. {
  13151. //
  13152. // If this is the first time through the loop, clear the
  13153. // CONNRESET warning flags.
  13154. //
  13155. if (dwNumberOfTimes == 0)
  13156. {
  13157. pDevice->NoteNotGotRemoteUPnPDiscoveryConnReset();
  13158. pDevice->NoteNotGotLocalUPnPDiscoveryConnReset();
  13159. }
  13160. //
  13161. // Send out search messages if it's time.
  13162. //
  13163. if ((int) (dwNextSearchMessageTime - dwCurrentTime) <= 0)
  13164. {
  13165. hr = this->SendUPnPSearchMessagesForDevice(pDevice,
  13166. fSendRemoteGatewayDiscovery);
  13167. if (hr != DPNH_OK)
  13168. {
  13169. DPFX(DPFPREP, 0, "Couldn't send UPnP search messages via every device!");
  13170. goto Failure;
  13171. }
  13172. }
  13173. else
  13174. {
  13175. //
  13176. // Not time to send search messages.
  13177. //
  13178. }
  13179. //
  13180. // For subsequent times through the loop, make sure we didn't
  13181. // get a CONNRESET earlier telling us not to try again.
  13182. // The "attempt?" flags got set in
  13183. // SendUPnPSearchMessagesForDevice, and the CONNRESET flags
  13184. // were cleared the first time we entered here.
  13185. //
  13186. if ((pDevice->IsOKToPerformRemoteUPnPDiscovery()) ||
  13187. (pDevice->IsOKToPerformLocalUPnPDiscovery()))
  13188. {
  13189. //
  13190. // Remember that we're trying to detect an Internet Gateway
  13191. // for this device. See caveat immediately following, and
  13192. // below for this variable's usage.
  13193. //
  13194. dwNumDevicesSearchingForUPnPDevices++;
  13195. //
  13196. // Minor optimization:
  13197. //
  13198. // If we're only supposed to be trying locally, and we're
  13199. // the public address for a local gateway, assume that we
  13200. // actually shouldn't be trying locally. This is because
  13201. // Windows XP ICS keeps port 1900 open even on the public
  13202. // adapter, so we think we need to look for a local one
  13203. // even though we won't find one. So once the remote
  13204. // lookup comes back with a CONNRESET, we no longer need to
  13205. // bother trying.
  13206. //
  13207. // So first check if we're only trying locally.
  13208. //
  13209. if ((pDevice->IsOKToPerformLocalUPnPDiscovery()) &&
  13210. (! pDevice->IsOKToPerformRemoteUPnPDiscovery()))
  13211. {
  13212. CBilink * pBilinkPrivateDevice;
  13213. CDevice * pPrivateDevice;
  13214. CUPnPDevice * pUPnPDevice;
  13215. //
  13216. // Then loop through every device.
  13217. //
  13218. pBilinkPrivateDevice = this->m_blDevices.GetNext();
  13219. while (pBilinkPrivateDevice != &this->m_blDevices)
  13220. {
  13221. pPrivateDevice = DEVICE_FROM_BILINK(pBilinkPrivateDevice);
  13222. pUPnPDevice = pPrivateDevice->GetUPnPDevice();
  13223. //
  13224. // If it's not the device we're querying and it has
  13225. // a ready UPnP device, dig deeper.
  13226. //
  13227. if ((pPrivateDevice != pDevice) &&
  13228. (pUPnPDevice != NULL) &&
  13229. (pUPnPDevice->IsReady()))
  13230. {
  13231. //
  13232. // If its a local UPnP device and its public
  13233. // address is this device's address, we found a
  13234. // match.
  13235. //
  13236. if ((pUPnPDevice->IsLocal()) &&
  13237. (pUPnPDevice->GetExternalIPAddressV4() == pDevice->GetLocalAddressV4()))
  13238. {
  13239. DPFX(DPFPREP, 4, "Device 0x%p is the public address for device 0x%p's local UPnP device 0x%p, not including in search.",
  13240. pDevice, pPrivateDevice, pUPnPDevice);
  13241. //
  13242. // Remove the count we added above.
  13243. //
  13244. dwNumDevicesSearchingForUPnPDevices--;
  13245. //
  13246. // Stop searching.
  13247. //
  13248. break;
  13249. }
  13250. //
  13251. // Otherwise keep going.
  13252. //
  13253. DPFX(DPFPREP, 8, "Skipping device 0x%p, UPnP device 0x%p not local (%i, control addr = %u.%u.%u.%u) or its public address doesn't match device 0x%p's address.",
  13254. pPrivateDevice,
  13255. pUPnPDevice,
  13256. (! pUPnPDevice->IsLocal()),
  13257. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b1,
  13258. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b2,
  13259. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b3,
  13260. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b4,
  13261. pDevice);
  13262. }
  13263. else
  13264. {
  13265. DPFX(DPFPREP, 8, "Skipping device 0x%p, it's the one we're looking for (matched 0x%p), it doesn't have a UPnP device (0x%p is NULL), and/or its UPnP device is not ready.",
  13266. pPrivateDevice, pDevice, pUPnPDevice);
  13267. }
  13268. //
  13269. // Go on to next device.
  13270. //
  13271. pBilinkPrivateDevice = pBilinkPrivateDevice->GetNext();
  13272. }
  13273. }
  13274. else
  13275. {
  13276. //
  13277. // Either not searching locally, or searching both
  13278. // locally and remotely.
  13279. //
  13280. DPFX(DPFPREP, 8, "Device 0x%p local search OK = %i, remote search OK = %i.",
  13281. pDevice,
  13282. pDevice->IsOKToPerformLocalUPnPDiscovery(),
  13283. pDevice->IsOKToPerformRemoteUPnPDiscovery());
  13284. }
  13285. }
  13286. else
  13287. {
  13288. DPFX(DPFPREP, 3, "Device 0x%p should not perform UPnP discovery.",
  13289. pDevice);
  13290. }
  13291. }
  13292. else
  13293. {
  13294. DPFX(DPFPREP, 8, "Device 0x%p already has UPnP device (0x%p) or is loopback address.",
  13295. pDevice, pDevice->GetUPnPDevice());
  13296. }
  13297. pBilink = pBilink->GetNext();
  13298. }
  13299. //
  13300. // Wait for any data, unless all devices already have an Internet
  13301. // Gateway, in which case we only want to clear the receive queue for
  13302. // the sockets.
  13303. //
  13304. if (dwNumDevicesSearchingForUPnPDevices == 0)
  13305. {
  13306. DPFX(DPFPREP, 7, "No devices need to search for UPnP devices, clearing straggling messages from sockets.");
  13307. tv.tv_usec = 0;
  13308. }
  13309. else
  13310. {
  13311. //
  13312. // Calculate the next time to send if we just sent search messages.
  13313. //
  13314. if ((int) (dwNextSearchMessageTime - dwCurrentTime) <= 0)
  13315. {
  13316. dwNextSearchMessageTime += UPNP_SEARCH_MESSAGE_INTERVAL;
  13317. //
  13318. // If we took way longer than expected in a previous loop
  13319. // (because of stress or Win9x errata), the next search time
  13320. // may have already passed. Just search right now if that's
  13321. // the case.
  13322. //
  13323. if ((int) (dwNextSearchMessageTime - dwCurrentTime) <= 0)
  13324. {
  13325. dwNextSearchMessageTime = dwCurrentTime;
  13326. }
  13327. }
  13328. //
  13329. // See how long we should wait for responses. Choose the total end
  13330. // time or the next search message time, whichever is shorter.
  13331. //
  13332. if ((int) (dwEndTime - dwNextSearchMessageTime) < 0)
  13333. {
  13334. DPFX(DPFPREP, 7, "Waiting %u ms for incoming responses.",
  13335. (dwEndTime - dwCurrentTime));
  13336. tv.tv_usec = (dwEndTime - dwCurrentTime) * 1000;
  13337. }
  13338. else
  13339. {
  13340. DPFX(DPFPREP, 7, "Waiting %u ms for incoming responses, and then might send search messages again.",
  13341. (dwNextSearchMessageTime - dwCurrentTime));
  13342. tv.tv_usec = (dwNextSearchMessageTime - dwCurrentTime) * 1000;
  13343. }
  13344. }
  13345. tv.tv_sec = 0;
  13346. iReturn = this->m_pfnselect(0, &fdsRead, NULL, NULL, &tv);
  13347. if (iReturn == SOCKET_ERROR)
  13348. {
  13349. #ifdef DBG
  13350. dwError = this->m_pfnWSAGetLastError();
  13351. DPFX(DPFPREP, 0, "Got sockets error %u trying to select on UPnP discovery sockets!", dwError);
  13352. #endif // DBG
  13353. hr = DPNHERR_GENERIC;
  13354. goto Failure;
  13355. }
  13356. //
  13357. // See if any sockets were selected.
  13358. //
  13359. if (iReturn > 0)
  13360. {
  13361. //
  13362. // Loop through all devices, looking for those that have data.
  13363. //
  13364. pBilink = this->m_blDevices.GetNext();
  13365. while (pBilink != &this->m_blDevices)
  13366. {
  13367. DNASSERT(! pBilink->IsEmpty());
  13368. pDevice = DEVICE_FROM_BILINK(pBilink);
  13369. //
  13370. // If this device's socket is set there's data to read.
  13371. //
  13372. //if (FD_ISSET(pDevice->GetUPnPDiscoverySocket(), &fdsRead))
  13373. if (this->m_pfn__WSAFDIsSet(pDevice->GetUPnPDiscoverySocket(), &fdsRead))
  13374. {
  13375. #ifdef DBG
  13376. fGotData = TRUE;
  13377. #endif // DBG
  13378. iRecvAddressSize = sizeof(saddrinRecvAddress);
  13379. iReturn = this->m_pfnrecvfrom(pDevice->GetUPnPDiscoverySocket(),
  13380. acBuffer,
  13381. (sizeof(acBuffer) - 1), // -1 to allow string termination
  13382. 0,
  13383. (SOCKADDR*) (&saddrinRecvAddress),
  13384. &iRecvAddressSize);
  13385. if ((iReturn == 0) || (iReturn == SOCKET_ERROR))
  13386. {
  13387. dwError = this->m_pfnWSAGetLastError();
  13388. //
  13389. // WSAENOBUFS means WinSock is out of memory.
  13390. //
  13391. if (dwError == WSAENOBUFS)
  13392. {
  13393. DPFX(DPFPREP, 0, "WinSock returned WSAENOBUFS while receiving discovery response!");
  13394. hr = DPNHERR_OUTOFMEMORY;
  13395. goto Failure;
  13396. }
  13397. //
  13398. // All other errors besides WSAECONNRESET are
  13399. // unexpected and mean we should bail.
  13400. //
  13401. if (dwError != WSAECONNRESET)
  13402. {
  13403. DPFX(DPFPREP, 0, "Got sockets error %u trying to receive on device 0x%p!",
  13404. dwError, pDevice);
  13405. hr = DPNHERR_GENERIC;
  13406. goto Failure;
  13407. }
  13408. //
  13409. // If we're here, it must be WSAECONNRESET. Correlate
  13410. // it with the outbound message that generated it so we
  13411. // don't bother waiting for a response from that
  13412. // location.
  13413. // Validate that it's for the port to which the message
  13414. // should have been sent.
  13415. //
  13416. if (saddrinRecvAddress.sin_port == HTONS(UPNP_PORT))
  13417. {
  13418. if (saddrinRecvAddress.sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4())
  13419. {
  13420. DPFX(DPFPREP, 1, "Got CONNRESET for local discovery attempt on device 0x%p (%u.%u.%u.%u:%u).",
  13421. pDevice,
  13422. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b1,
  13423. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b2,
  13424. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b3,
  13425. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b4,
  13426. NTOHS(saddrinRecvAddress.sin_port));
  13427. //
  13428. // Note the local error.
  13429. //
  13430. pDevice->NoteGotLocalUPnPDiscoveryConnReset();
  13431. }
  13432. else
  13433. {
  13434. if (! g_fUseMulticastUPnPDiscovery)
  13435. {
  13436. IN_ADDR inaddrGateway;
  13437. if ((! this->GetAddressToReachGateway(pDevice, &inaddrGateway)) ||
  13438. (inaddrGateway.S_un.S_addr == INADDR_BROADCAST) ||
  13439. (saddrinRecvAddress.sin_addr.S_un.S_addr == inaddrGateway.S_un.S_addr))
  13440. {
  13441. DPFX(DPFPREP, 2, "Got CONNRESET for remote discovery attempt on device 0x%p (%u.%u.%u.%u:%u).",
  13442. pDevice,
  13443. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b1,
  13444. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b2,
  13445. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b3,
  13446. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b4,
  13447. NTOHS(saddrinRecvAddress.sin_port));
  13448. //
  13449. // Note the remote error.
  13450. //
  13451. pDevice->NoteGotRemoteUPnPDiscoveryConnReset();
  13452. }
  13453. else
  13454. {
  13455. DPFX(DPFPREP, 1, "Ignoring CONNRESET on device 0x%p, sender %u.%u.%u.%u is not gateway %u.%u.%u.%u.",
  13456. pDevice,
  13457. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b1,
  13458. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b2,
  13459. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b3,
  13460. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b4,
  13461. inaddrGateway.S_un.S_un_b.s_b1,
  13462. inaddrGateway.S_un.S_un_b.s_b2,
  13463. inaddrGateway.S_un.S_un_b.s_b3,
  13464. inaddrGateway.S_un.S_un_b.s_b4);
  13465. }
  13466. }
  13467. else
  13468. {
  13469. DPFX(DPFPREP, 1, "Ignoring CONNRESET on device 0x%p from sender %u.%u.%u.%u, we are using multicast discovery.",
  13470. pDevice,
  13471. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b1,
  13472. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b2,
  13473. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b3,
  13474. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b4);
  13475. }
  13476. }
  13477. }
  13478. else
  13479. {
  13480. DPFX(DPFPREP, 1, "Ignoring CONNRESET on device 0x%p for invalid port (%u.%u.%u.%u:%u).",
  13481. pDevice,
  13482. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b1,
  13483. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b2,
  13484. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b3,
  13485. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b4,
  13486. NTOHS(saddrinRecvAddress.sin_port));
  13487. }
  13488. }
  13489. else
  13490. {
  13491. DNASSERT(iRecvAddressSize == sizeof(saddrinRecvAddress));
  13492. DNASSERT(iReturn < sizeof(acBuffer));
  13493. hr = this->HandleUPnPDiscoveryResponseMsg(pDevice,
  13494. &saddrinRecvAddress,
  13495. acBuffer,
  13496. iReturn,
  13497. &fInitiatedConnect);
  13498. if (hr != DPNH_OK)
  13499. {
  13500. DPFX(DPFPREP, 0, "Couldn't handle UPnP discovery response message (err = 0x%lx), ignoring.",
  13501. hr);
  13502. }
  13503. }
  13504. }
  13505. pBilink = pBilink->GetNext();
  13506. }
  13507. //
  13508. // Make sure we actually found a socket with data.
  13509. //
  13510. DNASSERT(fGotData);
  13511. }
  13512. else
  13513. {
  13514. //
  13515. // We timed out. If we were just clearing receive buffers for the
  13516. // socket(s), we're done.
  13517. //
  13518. if (dwNumDevicesSearchingForUPnPDevices == 0)
  13519. {
  13520. break;
  13521. }
  13522. }
  13523. //
  13524. // Increase the counter.
  13525. //
  13526. dwNumberOfTimes++;
  13527. //
  13528. // Get current time for figuring out how much longer to wait.
  13529. //
  13530. dwCurrentTime = GETTIMESTAMP();
  13531. }
  13532. while ((int) (dwEndTime - dwCurrentTime) > 0);
  13533. hr = DPNH_OK;
  13534. //
  13535. // If we initiated connections to any UPnP devices, wait for them to
  13536. // complete.
  13537. //
  13538. if (fInitiatedConnect)
  13539. {
  13540. hr = this->WaitForUPnPConnectCompletions();
  13541. if (hr != DPNH_OK)
  13542. {
  13543. DPFX(DPFPREP, 0, "Couldn't wait for UPnP connect completions!");
  13544. goto Failure;
  13545. }
  13546. }
  13547. Exit:
  13548. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  13549. return hr;
  13550. Failure:
  13551. goto Exit;
  13552. } // CNATHelpUPnP::CheckForUPnPAnnouncements
  13553. #undef DPF_MODNAME
  13554. #define DPF_MODNAME "CNATHelpUPnP::WaitForUPnPConnectCompletions"
  13555. //=============================================================================
  13556. // CNATHelpUPnP::WaitForUPnPConnectCompletions
  13557. //-----------------------------------------------------------------------------
  13558. //
  13559. // Description: Waits for completions for pending TCP connects to UPnP
  13560. // Internet gateway devices.
  13561. //
  13562. // UPnP devices may get removed from list if a failure occurs.
  13563. //
  13564. // The object lock is assumed to be held.
  13565. //
  13566. // Arguments: None.
  13567. //
  13568. // Returns: HRESULT
  13569. // DPNH_OK - Connects were handled successfully.
  13570. // DPNHERR_GENERIC - An error occurred.
  13571. //=============================================================================
  13572. HRESULT CNATHelpUPnP::WaitForUPnPConnectCompletions(void)
  13573. {
  13574. HRESULT hr;
  13575. int iNumSockets;
  13576. FD_SET fdsWrite;
  13577. FD_SET fdsExcept;
  13578. CBilink * pBilink;
  13579. CUPnPDevice * pUPnPDevice;
  13580. timeval tv;
  13581. int iReturn;
  13582. BOOL fRequestedDescription = FALSE;
  13583. DWORD dwStartTime;
  13584. DWORD dwTimeout;
  13585. CDevice * pDevice;
  13586. #ifdef DBG
  13587. BOOL fFoundCompletion;
  13588. DWORD dwError;
  13589. #endif // DBG
  13590. DPFX(DPFPREP, 5, "(0x%p) Enter", this);
  13591. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  13592. //
  13593. // Loop until all sockets are connected or there's a timeout.
  13594. //
  13595. do
  13596. {
  13597. //
  13598. // Check for any connect completions. Start by building two FD_SETs
  13599. // for all the sockets with pending connects.
  13600. //
  13601. FD_ZERO(&fdsWrite);
  13602. FD_ZERO(&fdsExcept);
  13603. iNumSockets = 0;
  13604. pBilink = this->m_blUPnPDevices.GetNext();
  13605. while (pBilink != &this->m_blUPnPDevices)
  13606. {
  13607. DNASSERT(! pBilink->IsEmpty());
  13608. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13609. if (pUPnPDevice->IsConnecting())
  13610. {
  13611. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  13612. FD_SET(pUPnPDevice->GetControlSocket(), &fdsWrite);
  13613. FD_SET(pUPnPDevice->GetControlSocket(), &fdsExcept);
  13614. iNumSockets++;
  13615. }
  13616. pBilink = pBilink->GetNext();
  13617. }
  13618. //
  13619. // If there weren't any sockets that had pending connections, then
  13620. // we're done here.
  13621. //
  13622. if (iNumSockets <= 0)
  13623. {
  13624. DPFX(DPFPREP, 7, "No more UPnP device control sockets with pending connections.");
  13625. break;
  13626. }
  13627. DPFX(DPFPREP, 7, "There are %i UPnP device control sockets with pending connections.",
  13628. iNumSockets);
  13629. //
  13630. // Wait for connect completions. We don't wait for the full TCP/IP
  13631. // timeout (which is why we made it non-blocking).
  13632. //
  13633. tv.tv_usec = 0;
  13634. tv.tv_sec = g_dwUPnPConnectTimeout;
  13635. iReturn = this->m_pfnselect(0, NULL, &fdsWrite, &fdsExcept, &tv);
  13636. if (iReturn == SOCKET_ERROR)
  13637. {
  13638. #ifdef DBG
  13639. dwError = this->m_pfnWSAGetLastError();
  13640. DPFX(DPFPREP, 0, "Got sockets error %u trying to select on UPnP device sockets!",
  13641. dwError);
  13642. #endif // DBG
  13643. hr = DPNHERR_GENERIC;
  13644. goto Failure;
  13645. }
  13646. //
  13647. // If no sockets were selected, that means the connections timed out.
  13648. // Remove all the devices that were waiting.
  13649. //
  13650. if (iReturn == 0)
  13651. {
  13652. DPFX(DPFPREP, 3, "Select for %u seconds timed out.", g_dwUPnPConnectTimeout);
  13653. pBilink = this->m_blUPnPDevices.GetNext();
  13654. while (pBilink != &this->m_blUPnPDevices)
  13655. {
  13656. DNASSERT(! pBilink->IsEmpty());
  13657. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13658. pBilink = pBilink->GetNext();
  13659. if (pUPnPDevice->IsConnecting())
  13660. {
  13661. DPFX(DPFPREP, 7, "UPnP device 0x%p is still connecting, removing.",
  13662. pUPnPDevice);
  13663. //
  13664. // Dump this unusable UPnP device and continue.
  13665. //
  13666. pDevice = pUPnPDevice->GetOwningDevice();
  13667. DNASSERT(pUPnPDevice->GetOwningDevice() != NULL);
  13668. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  13669. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  13670. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  13671. //
  13672. // This may cause our pUPnPDevice pointer to become
  13673. // invalid.
  13674. //
  13675. this->ClearDevicesUPnPDevice(pDevice);
  13676. #ifdef DBG
  13677. iNumSockets--;
  13678. #endif // DBG
  13679. }
  13680. else
  13681. {
  13682. DPFX(DPFPREP, 7, "UPnP device 0x%p is not trying to connect or has safely connected.",
  13683. pUPnPDevice);
  13684. }
  13685. }
  13686. //
  13687. // We should have destroyed the same number of devices that were
  13688. // waiting.
  13689. //
  13690. DNASSERT(iNumSockets == 0);
  13691. //
  13692. // Continue on to handling any sockets succeeded previously.
  13693. //
  13694. break;
  13695. }
  13696. //
  13697. // If we're here, some sockets were signalled.
  13698. //
  13699. #ifdef DBG
  13700. DPFX(DPFPREP, 7, "There are %i sockets with connect activity.", iReturn);
  13701. fFoundCompletion = FALSE;
  13702. #endif // DBG
  13703. pBilink = this->m_blUPnPDevices.GetNext();
  13704. while (pBilink != &this->m_blUPnPDevices)
  13705. {
  13706. DNASSERT(! pBilink->IsEmpty());
  13707. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13708. pBilink = pBilink->GetNext();
  13709. if (pUPnPDevice->IsConnecting())
  13710. {
  13711. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  13712. //
  13713. // If this UPnP device's socket is in the write set it
  13714. // connected successfully.
  13715. //
  13716. //if (FD_ISSET(pUPnPDevice->GetControlSocket(), &fdsWrite))
  13717. if (this->m_pfn__WSAFDIsSet(pUPnPDevice->GetControlSocket(), &fdsWrite))
  13718. {
  13719. pUPnPDevice->NoteConnected();
  13720. #ifdef DBG
  13721. fFoundCompletion = TRUE;
  13722. #endif // DBG
  13723. if (! pUPnPDevice->IsReady())
  13724. {
  13725. DPFX(DPFPREP, 2, "UPnP device object 0x%p now connected to Internet gateway device.",
  13726. pUPnPDevice);
  13727. hr = this->SendUPnPDescriptionRequest(pUPnPDevice);
  13728. if (hr != DPNH_OK)
  13729. {
  13730. DPFX(DPFPREP, 0, "Couldn't send UPnP description request to device object 0x%p! Disconnecting.",
  13731. pUPnPDevice);
  13732. //
  13733. // Dump this unusable UPnP device and continue.
  13734. //
  13735. pDevice = pUPnPDevice->GetOwningDevice();
  13736. DNASSERT(pUPnPDevice->GetOwningDevice() != NULL);
  13737. //
  13738. // This may cause our pUPnPDevice pointer to become
  13739. // invalid.
  13740. //
  13741. this->ClearDevicesUPnPDevice(pDevice);
  13742. }
  13743. else
  13744. {
  13745. fRequestedDescription = TRUE;
  13746. }
  13747. }
  13748. else
  13749. {
  13750. DPFX(DPFPREP, 2, "UPnP device object 0x%p successfully reconnected to Internet gateway device.",
  13751. pUPnPDevice);
  13752. }
  13753. }
  13754. else
  13755. {
  13756. //
  13757. // If this UPnP device's socket is in the except set it
  13758. // failed to connect.
  13759. //
  13760. //if (FD_ISSET(pUPnPDevice->GetControlSocket(), &fdsExcept))
  13761. if (this->m_pfn__WSAFDIsSet(pUPnPDevice->GetControlSocket(), &fdsExcept))
  13762. {
  13763. #ifdef DBG
  13764. int iError;
  13765. int iErrorSize;
  13766. fFoundCompletion = TRUE;
  13767. //
  13768. // Print out the reason why it couldn't connect.
  13769. // Ignore the direct return code from getsockopt.
  13770. //
  13771. iError = 0;
  13772. iErrorSize = sizeof(iError);
  13773. this->m_pfngetsockopt(pUPnPDevice->GetControlSocket(),
  13774. SOL_SOCKET,
  13775. SO_ERROR,
  13776. (char*) (&iError),
  13777. &iErrorSize);
  13778. DPFX(DPFPREP, 1, "Connecting to UPnP device object 0x%p failed with error %i, removing from list.",
  13779. pUPnPDevice, iError);
  13780. #endif // DBG
  13781. //
  13782. // This UPnP device is useless if it doesn't respond.
  13783. // Throw it out.
  13784. //
  13785. pDevice = pUPnPDevice->GetOwningDevice();
  13786. DNASSERT(pUPnPDevice->GetOwningDevice() != NULL);
  13787. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  13788. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  13789. //
  13790. // This may cause our pUPnPDevice pointer to become
  13791. // invalid.
  13792. //
  13793. this->ClearDevicesUPnPDevice(pDevice);
  13794. }
  13795. else
  13796. {
  13797. //
  13798. // Socket is still connecting.
  13799. //
  13800. }
  13801. }
  13802. }
  13803. else
  13804. {
  13805. //
  13806. // This socket is already connected.
  13807. //
  13808. }
  13809. }
  13810. //
  13811. // Make sure we actually found a socket with a connect completion this
  13812. // time through.
  13813. //
  13814. DNASSERT(fFoundCompletion);
  13815. }
  13816. while (TRUE);
  13817. //
  13818. // If we're here, all UPnP devices are connected or have since been
  13819. // destroyed.
  13820. //
  13821. if (fRequestedDescription)
  13822. {
  13823. //
  13824. // Wait for the description responses to come back.
  13825. //
  13826. dwStartTime = GETTIMESTAMP();
  13827. dwTimeout = g_dwUPnPResponseTimeout;
  13828. do
  13829. {
  13830. hr = this->CheckForReceivedUPnPMsgsOnAllDevices(dwTimeout);
  13831. if (hr != DPNH_OK)
  13832. {
  13833. DPFX(DPFPREP, 0, "Failed receiving UPnP messages!");
  13834. goto Failure;
  13835. }
  13836. //
  13837. // We either timed out or got some data. Check if we got the
  13838. // response(s) we need. Reuse the fRequestedDescription
  13839. // boolean.
  13840. //
  13841. fRequestedDescription = FALSE;
  13842. pBilink = this->m_blUPnPDevices.GetNext();
  13843. while (pBilink != &this->m_blUPnPDevices)
  13844. {
  13845. DNASSERT(! pBilink->IsEmpty());
  13846. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13847. if (! pUPnPDevice->IsReady())
  13848. {
  13849. if (pUPnPDevice->IsConnected())
  13850. {
  13851. DPFX(DPFPREP, 7, "UPnP device 0x%p is not ready yet.",
  13852. pUPnPDevice);
  13853. fRequestedDescription = TRUE;
  13854. }
  13855. else
  13856. {
  13857. DPFX(DPFPREP, 4, "UPnP device 0x%p got disconnected before receiving description response.",
  13858. pUPnPDevice);
  13859. }
  13860. break;
  13861. }
  13862. pBilink = pBilink->GetNext();
  13863. }
  13864. if (! fRequestedDescription)
  13865. {
  13866. DPFX(DPFPREP, 6, "All UPnP devices are ready or disconnected now.");
  13867. //
  13868. // Break out of the wait loop.
  13869. //
  13870. break;
  13871. }
  13872. //
  13873. // Calculate how long we have left to wait. If the calculation
  13874. // goes negative, it means we're done.
  13875. //
  13876. dwTimeout = g_dwUPnPResponseTimeout - (GETTIMESTAMP() - dwStartTime);
  13877. }
  13878. while (((int) dwTimeout > 0));
  13879. //
  13880. // Any devices that still aren't ready yet were either disconnected or
  13881. // are taking too long and should be removed.
  13882. //
  13883. pBilink = this->m_blUPnPDevices.GetNext();
  13884. while (pBilink != &this->m_blUPnPDevices)
  13885. {
  13886. DNASSERT(! pBilink->IsEmpty());
  13887. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13888. pBilink = pBilink->GetNext();
  13889. if (! pUPnPDevice->IsReady())
  13890. {
  13891. DPFX(DPFPREP, 1, "UPnP device 0x%p got disconnected or took too long to get ready, removing.",
  13892. pUPnPDevice);
  13893. pDevice = pUPnPDevice->GetOwningDevice();
  13894. DNASSERT(pUPnPDevice->GetOwningDevice() != NULL);
  13895. //
  13896. // This may cause our pUPnPDevice pointer to become
  13897. // invalid.
  13898. //
  13899. this->ClearDevicesUPnPDevice(pDevice);
  13900. }
  13901. }
  13902. }
  13903. else
  13904. {
  13905. DPFX(DPFPREP, 7, "Did not request any descriptions.");
  13906. }
  13907. //
  13908. // If we're here, everything is successful and we're done.
  13909. //
  13910. hr = DPNH_OK;
  13911. Exit:
  13912. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  13913. return hr;
  13914. Failure:
  13915. goto Exit;
  13916. } // CNATHelpUPnP::WaitForUPnPConnectCompletions
  13917. #undef DPF_MODNAME
  13918. #define DPF_MODNAME "CNATHelpUPnP::CheckForReceivedUPnPMsgsOnAllDevices"
  13919. //=============================================================================
  13920. // CNATHelpUPnP::CheckForReceivedUPnPMsgsOnAllDevices
  13921. //-----------------------------------------------------------------------------
  13922. //
  13923. // Description: Handles any incoming data on the TCP sockets connect to UPnP
  13924. // Internet gateway devices.
  13925. //
  13926. // UPnP devices with failures may get removed from the list.
  13927. //
  13928. // The object lock is assumed to be held.
  13929. //
  13930. // Arguments:
  13931. // DWORD dwTimeout - How long to wait for messages to arrive, or 0 to just
  13932. // poll.
  13933. //
  13934. // Returns: HRESULT
  13935. // DPNH_OK - Messages were handled successfully.
  13936. // DPNHERR_GENERIC - An error occurred.
  13937. //=============================================================================
  13938. HRESULT CNATHelpUPnP::CheckForReceivedUPnPMsgsOnAllDevices(const DWORD dwTimeout)
  13939. {
  13940. HRESULT hr;
  13941. int iNumSockets;
  13942. FD_SET fdsRead;
  13943. CBilink * pBilink;
  13944. CUPnPDevice * pUPnPDevice;
  13945. timeval tv;
  13946. int iReturn;
  13947. #ifdef DBG
  13948. BOOL fFoundData = FALSE;
  13949. DWORD dwError;
  13950. #endif // DBG
  13951. DPFX(DPFPREP, 5, "(0x%p) Parameters: (%u)", this, dwTimeout);
  13952. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  13953. Rewait:
  13954. iNumSockets = 0;
  13955. //
  13956. // Check for any data. Start by building an FD_SET for all the sockets
  13957. // with completed connections.
  13958. //
  13959. FD_ZERO(&fdsRead);
  13960. pBilink = this->m_blUPnPDevices.GetNext();
  13961. while (pBilink != &this->m_blUPnPDevices)
  13962. {
  13963. DNASSERT(! pBilink->IsEmpty());
  13964. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13965. if (pUPnPDevice->IsConnected())
  13966. {
  13967. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  13968. FD_SET(pUPnPDevice->GetControlSocket(), &fdsRead);
  13969. iNumSockets++;
  13970. }
  13971. pBilink = pBilink->GetNext();
  13972. }
  13973. //
  13974. // If there weren't any sockets that were connected, then we're done here.
  13975. //
  13976. if (iNumSockets <= 0)
  13977. {
  13978. DPFX(DPFPREP, 7, "No connected UPnP device control sockets.");
  13979. hr = DPNH_OK;
  13980. goto Exit;
  13981. }
  13982. DPFX(DPFPREP, 7, "There are %i connected UPnP device control sockets.",
  13983. iNumSockets);
  13984. //
  13985. // Wait for received data.
  13986. //
  13987. tv.tv_usec = dwTimeout * 1000;
  13988. tv.tv_sec = 0;
  13989. iReturn = this->m_pfnselect(0, &fdsRead, NULL, NULL, &tv);
  13990. if (iReturn == SOCKET_ERROR)
  13991. {
  13992. #ifdef DBG
  13993. dwError = this->m_pfnWSAGetLastError();
  13994. DPFX(DPFPREP, 0, "Got sockets error %u trying to select on UPnP device sockets!",
  13995. dwError);
  13996. #endif // DBG
  13997. hr = DPNHERR_GENERIC;
  13998. goto Failure;
  13999. }
  14000. //
  14001. // If no sockets were selected, we're done.
  14002. //
  14003. if (iReturn == 0)
  14004. {
  14005. DPFX(DPFPREP, 7, "Timed out waiting for data on %i sockets.",
  14006. iNumSockets);
  14007. hr = DPNH_OK;
  14008. goto Exit;
  14009. }
  14010. //
  14011. // If we're here, some sockets were signalled.
  14012. //
  14013. pBilink = this->m_blUPnPDevices.GetNext();
  14014. while (pBilink != &this->m_blUPnPDevices)
  14015. {
  14016. DNASSERT(! pBilink->IsEmpty());
  14017. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  14018. pBilink = pBilink->GetNext();
  14019. if (pUPnPDevice->IsConnected())
  14020. {
  14021. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  14022. //
  14023. // If this UPnP device's socket is in the read set it has data.
  14024. //
  14025. //if (FD_ISSET(pUPnPDevice->GetControlSocket(), &fdsRead))
  14026. if (this->m_pfn__WSAFDIsSet(pUPnPDevice->GetControlSocket(), &fdsRead))
  14027. {
  14028. #ifdef DBG
  14029. fFoundData = TRUE;
  14030. #endif // DBG
  14031. //
  14032. // Grab a reference, since ReceiveUPnPDataStream may clear the
  14033. // device.
  14034. //
  14035. pUPnPDevice->AddRef();
  14036. hr = this->ReceiveUPnPDataStream(pUPnPDevice);
  14037. if (hr != DPNH_OK)
  14038. {
  14039. DPFX(DPFPREP, 0, "Couldn't receive UPnP stream from device object 0x%p (err = 0x%lx)! Disconnecting.",
  14040. pUPnPDevice, hr);
  14041. //
  14042. // Dump this unusable UPnP device and continue.
  14043. //
  14044. if (pUPnPDevice->GetOwningDevice() != NULL)
  14045. {
  14046. this->ClearDevicesUPnPDevice(pUPnPDevice->GetOwningDevice());
  14047. }
  14048. else
  14049. {
  14050. DPFX(DPFPREP, 1, "UPnP device 0x%p's has already been removed from owning device.",
  14051. pUPnPDevice);
  14052. }
  14053. hr = DPNH_OK;
  14054. }
  14055. //
  14056. // Remove the reference we added.
  14057. //
  14058. pUPnPDevice->DecRef();
  14059. }
  14060. else
  14061. {
  14062. //
  14063. // Socket does not have any data.
  14064. //
  14065. DPFX(DPFPREP, 8, "Skipping UPnP device 0x%p because it does not have any data.",
  14066. pUPnPDevice);
  14067. }
  14068. }
  14069. else
  14070. {
  14071. //
  14072. // This socket is not connected yet/anymore.
  14073. //
  14074. DPFX(DPFPREP, 7, "Skipping unconnected UPnP device 0x%p.", pUPnPDevice);
  14075. }
  14076. }
  14077. //
  14078. // Make sure we actually found a socket with data.
  14079. //
  14080. DNASSERT(fFoundData);
  14081. //
  14082. // We found data, so see if there's more. Connection should be closed
  14083. // after responses.
  14084. //
  14085. DPFX(DPFPREP, 7, "Waiting for more data on the sockets.");
  14086. goto Rewait;
  14087. Exit:
  14088. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  14089. return hr;
  14090. Failure:
  14091. goto Exit;
  14092. } // CNATHelpUPnP::CheckForReceivedUPnPMsgsOnAllDevices
  14093. #undef DPF_MODNAME
  14094. #define DPF_MODNAME "CNATHelpUPnP::CheckForReceivedUPnPMsgsOnDevice"
  14095. //=============================================================================
  14096. // CNATHelpUPnP::CheckForReceivedUPnPMsgsOnDevice
  14097. //-----------------------------------------------------------------------------
  14098. //
  14099. // Description: Handles any incoming data on the TCP socket for the given
  14100. // UPnP device.
  14101. //
  14102. // If the UPnP device encounters a failure, it may get removed
  14103. // from the list.
  14104. //
  14105. // The object lock is assumed to be held.
  14106. //
  14107. // Arguments:
  14108. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device to receive data.
  14109. // DWORD dwTimeout - How long to wait for messages to arrive, or 0
  14110. // to just poll.
  14111. //
  14112. // Returns: HRESULT
  14113. // DPNH_OK - Messages were handled successfully.
  14114. // DPNHERR_GENERIC - An error occurred.
  14115. //=============================================================================
  14116. HRESULT CNATHelpUPnP::CheckForReceivedUPnPMsgsOnDevice(CUPnPDevice * const pUPnPDevice,
  14117. const DWORD dwTimeout)
  14118. {
  14119. HRESULT hr;
  14120. FD_SET fdsRead;
  14121. timeval tv;
  14122. int iReturn;
  14123. #ifdef DBG
  14124. DWORD dwError;
  14125. #endif // DBG
  14126. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %u)", this, pUPnPDevice, dwTimeout);
  14127. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  14128. DNASSERT(pUPnPDevice != NULL);
  14129. DNASSERT(pUPnPDevice->IsConnected());
  14130. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  14131. do
  14132. {
  14133. //
  14134. // Create an FD_SET for the socket in question.
  14135. //
  14136. FD_ZERO(&fdsRead);
  14137. FD_SET(pUPnPDevice->GetControlSocket(), &fdsRead);
  14138. //
  14139. // Wait for received data.
  14140. //
  14141. tv.tv_usec = dwTimeout * 1000;
  14142. tv.tv_sec = 0;
  14143. iReturn = this->m_pfnselect(0, &fdsRead, NULL, NULL, &tv);
  14144. if (iReturn == SOCKET_ERROR)
  14145. {
  14146. #ifdef DBG
  14147. dwError = this->m_pfnWSAGetLastError();
  14148. DPFX(DPFPREP, 0, "Got sockets error %u trying to select on UPnP device sockets!",
  14149. dwError);
  14150. #endif // DBG
  14151. hr = DPNHERR_GENERIC;
  14152. goto Failure;
  14153. }
  14154. //
  14155. // If no sockets were selected, we're done.
  14156. //
  14157. if (iReturn == 0)
  14158. {
  14159. DPFX(DPFPREP, 7, "Timed out waiting for data on UPnP device 0x%p's socket.",
  14160. pUPnPDevice);
  14161. break;
  14162. }
  14163. DNASSERT(iReturn == 1);
  14164. //DNASSERT(FD_ISSET(pUPnPDevice->GetControlSocket(), &fdsRead));
  14165. DNASSERT(this->m_pfn__WSAFDIsSet(pUPnPDevice->GetControlSocket(), &fdsRead));
  14166. hr = this->ReceiveUPnPDataStream(pUPnPDevice);
  14167. if (hr != DPNH_OK)
  14168. {
  14169. DPFX(DPFPREP, 0, "Couldn't receive UPnP stream from device object 0x%p! Disconnecting.",
  14170. pUPnPDevice);
  14171. //
  14172. // Dump this unusable UPnP device and continue.
  14173. //
  14174. if (pUPnPDevice->GetOwningDevice() != NULL)
  14175. {
  14176. this->ClearDevicesUPnPDevice(pUPnPDevice->GetOwningDevice());
  14177. }
  14178. else
  14179. {
  14180. DPFX(DPFPREP, 1, "UPnP device 0x%p's has already been removed from owning device.",
  14181. pUPnPDevice);
  14182. }
  14183. break;
  14184. }
  14185. //
  14186. // If the UPnP device is no longer connected, we're done.
  14187. //
  14188. if (! pUPnPDevice->IsConnected())
  14189. {
  14190. DPFX(DPFPREP, 7, "UPnP device 0x%p no longer connected.", pUPnPDevice);
  14191. break;
  14192. }
  14193. //
  14194. // We found data, so see if there's more. Connection should be closed
  14195. // after responses.
  14196. //
  14197. DPFX(DPFPREP, 7, "Waiting for more data on the UPnP device 0x%p's socket.", pUPnPDevice);
  14198. }
  14199. while (TRUE);
  14200. //
  14201. // If we're here, we're no worse for wear.
  14202. //
  14203. hr = DPNH_OK;
  14204. Exit:
  14205. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  14206. return hr;
  14207. Failure:
  14208. goto Exit;
  14209. } // CNATHelpUPnP::CheckForReceivedUPnPMsgsOnDevice
  14210. #undef DPF_MODNAME
  14211. #define DPF_MODNAME "CNATHelpUPnP::HandleUPnPDiscoveryResponseMsg"
  14212. //=============================================================================
  14213. // CNATHelpUPnP::HandleUPnPDiscoveryResponseMsg
  14214. //-----------------------------------------------------------------------------
  14215. //
  14216. // Description: Handles a UPnP discovery response message sent to this
  14217. // control point.
  14218. //
  14219. // The object lock is assumed to be held.
  14220. //
  14221. // Arguments:
  14222. // CDevice * pDevice - Pointer to device which received message.
  14223. // SOCKADDR_IN * psaddrinSource - Pointer to address that sent the response
  14224. // message.
  14225. // char * pcMsg - Pointer to buffer containing the UPnP
  14226. // message. It will be modified.
  14227. // int iMsgSize - Size of message buffer in bytes. There
  14228. // must be an extra byte after the end of
  14229. // the message.
  14230. // BOOL * pfInitiatedConnect - Pointer to boolean to set to TRUE if a
  14231. // new UPnP device was found and a
  14232. // connection to it was begun.
  14233. //
  14234. // Returns: HRESULT
  14235. // DPNH_OK - Message was handled successfully.
  14236. // DPNHERR_GENERIC - An error occurred.
  14237. //=============================================================================
  14238. HRESULT CNATHelpUPnP::HandleUPnPDiscoveryResponseMsg(CDevice * const pDevice,
  14239. const SOCKADDR_IN * const psaddrinSource,
  14240. char * const pcMsg,
  14241. const int iMsgSize,
  14242. BOOL * const pfInitiatedConnect)
  14243. {
  14244. HRESULT hr = DPNH_OK;
  14245. char * pszToken;
  14246. UPNP_HEADER_INFO HeaderInfo;
  14247. SOCKADDR_IN saddrinHost;
  14248. char * pszRelativePath;
  14249. SOCKET sTemp = INVALID_SOCKET;
  14250. SOCKADDR_IN saddrinLocal;
  14251. CUPnPDevice * pUPnPDevice = NULL;
  14252. DWORD dwNumConnectRetries = 0;
  14253. DWORD dwError;
  14254. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, %i, 0x%p)",
  14255. this, pDevice, psaddrinSource, pcMsg, iMsgSize, pfInitiatedConnect);
  14256. #ifdef DBG
  14257. //
  14258. // Log the message.
  14259. //
  14260. this->PrintUPnPTransactionToFile(pcMsg,
  14261. iMsgSize,
  14262. "Inbound UPnP datagram headers",
  14263. pDevice);
  14264. #endif // DBG
  14265. //
  14266. // Any errors we get while analyzing the message will cause us to jump to
  14267. // the Exit label with hr == DPNH_OK. Once we start trying to connect to
  14268. // the UPnP device, that will change. See below.
  14269. //
  14270. //
  14271. // First of all, if this device already has a UPnP device, then we'll just
  14272. // ignore this response. Either it's a duplicate of an earlier response,
  14273. // a cache-refresh, or it's from a different device. Duplicates we should
  14274. // ignore. Cache-refresh is essentially a duplicate. We can't handle any
  14275. // information changes, so ignore those too. And finally, we don't handle
  14276. // multiple Internet gateway UPnP devices, so ignore those, too.
  14277. //
  14278. pUPnPDevice = pDevice->GetUPnPDevice();
  14279. if (pUPnPDevice != NULL)
  14280. {
  14281. DPFX(DPFPREP, 6, "Already have UPnP device (0x%p) ignoring message.",
  14282. pUPnPDevice);
  14283. //
  14284. // GetUPnPDevice did not add a reference to pUPnPDevice.
  14285. //
  14286. goto Exit;
  14287. }
  14288. //
  14289. // Make sure the sender of this response is valid. It should be either
  14290. // the local device address, or the address of the gateway. If we
  14291. // broadcasted or multicasted, we'll need to be more lenient. We'll just
  14292. // ensure that the response came from someone local (it doesn't make sense
  14293. // that in order to make mappings for our private network we would need to
  14294. // contact something outside).
  14295. //
  14296. if (psaddrinSource->sin_addr.S_un.S_addr != pDevice->GetLocalAddressV4())
  14297. {
  14298. if (g_fUseMulticastUPnPDiscovery)
  14299. {
  14300. if (! this->IsAddressLocal(pDevice, psaddrinSource))
  14301. {
  14302. DPFX(DPFPREP, 1, "Multicast search responding device (%u.%u.%u.%u:%u) is not local, ignoring message.",
  14303. psaddrinSource->sin_addr.S_un.S_un_b.s_b1,
  14304. psaddrinSource->sin_addr.S_un.S_un_b.s_b2,
  14305. psaddrinSource->sin_addr.S_un.S_un_b.s_b3,
  14306. psaddrinSource->sin_addr.S_un.S_un_b.s_b4,
  14307. NTOHS(psaddrinSource->sin_port));
  14308. goto Exit;
  14309. }
  14310. }
  14311. else
  14312. {
  14313. //
  14314. // Retrieve the gateway's address (using saddrinHost as a temporary
  14315. // variable. If that fails or returns the broadcast address, just
  14316. // make sure the address is local. Otherwise, expect an exact
  14317. // match.
  14318. //
  14319. if ((! this->GetAddressToReachGateway(pDevice, &saddrinHost.sin_addr)) ||
  14320. (saddrinHost.sin_addr.S_un.S_addr == INADDR_BROADCAST))
  14321. {
  14322. if (! this->IsAddressLocal(pDevice, psaddrinSource))
  14323. {
  14324. DPFX(DPFPREP, 1, "No gateway/broadcast search responding device (%u.%u.%u.%u:%u) is not local, ignoring message.",
  14325. psaddrinSource->sin_addr.S_un.S_un_b.s_b1,
  14326. psaddrinSource->sin_addr.S_un.S_un_b.s_b2,
  14327. psaddrinSource->sin_addr.S_un.S_un_b.s_b3,
  14328. psaddrinSource->sin_addr.S_un.S_un_b.s_b4,
  14329. NTOHS(psaddrinSource->sin_port));
  14330. goto Exit;
  14331. }
  14332. }
  14333. else
  14334. {
  14335. if (psaddrinSource->sin_addr.S_un.S_addr != saddrinHost.sin_addr.S_un.S_addr)
  14336. {
  14337. DPFX(DPFPREP, 1, "Unicast search responding device (%u.%u.%u.%u:%u) is not gateway (%u.%u.%u.%u), ignoring message.",
  14338. psaddrinSource->sin_addr.S_un.S_un_b.s_b1,
  14339. psaddrinSource->sin_addr.S_un.S_un_b.s_b2,
  14340. psaddrinSource->sin_addr.S_un.S_un_b.s_b3,
  14341. psaddrinSource->sin_addr.S_un.S_un_b.s_b4,
  14342. NTOHS(psaddrinSource->sin_port),
  14343. saddrinHost.sin_addr.S_un.S_un_b.s_b1,
  14344. saddrinHost.sin_addr.S_un.S_un_b.s_b2,
  14345. saddrinHost.sin_addr.S_un.S_un_b.s_b3,
  14346. saddrinHost.sin_addr.S_un.S_un_b.s_b4);
  14347. goto Exit;
  14348. }
  14349. }
  14350. } // end else (not multicasting search)
  14351. }
  14352. else
  14353. {
  14354. //
  14355. // Response was from the local device.
  14356. //
  14357. }
  14358. //
  14359. // Ensure the buffer is NULL terminated to prevent buffer overruns when
  14360. // using the string routines.
  14361. //
  14362. pcMsg[iMsgSize] = '\0';
  14363. //
  14364. // Find the version string.
  14365. //
  14366. pszToken = strtok(pcMsg, " \t\n");
  14367. if (pszToken == NULL)
  14368. {
  14369. DPFX(DPFPREP, 9, "Could not locate first white-space separator.");
  14370. goto Exit;
  14371. }
  14372. //
  14373. // Check the version string, case insensitive.
  14374. //
  14375. if ((_stricmp(pszToken, HTTP_VERSION) != 0) &&
  14376. (_stricmp(pszToken, HTTP_VERSION_ALT) != 0))
  14377. {
  14378. DPFX(DPFPREP, 1, "The version specified in the response message is not \"" HTTP_VERSION "\" or \"" HTTP_VERSION_ALT "\" (it's \"%hs\").",
  14379. pszToken);
  14380. goto Exit;
  14381. }
  14382. //
  14383. // Find the response code string.
  14384. //
  14385. pszToken = strtok(NULL, " ");
  14386. if (pszToken == NULL)
  14387. {
  14388. DPFX(DPFPREP, 1, "Could not find the response code space.");
  14389. goto Exit;
  14390. }
  14391. //
  14392. // Make sure it's the success result, case insensitive.
  14393. //
  14394. if (_stricmp(pszToken, "200") != 0)
  14395. {
  14396. DPFX(DPFPREP, 1, "The response code specified is not \"200\" (it's \"%hs\").",
  14397. pszToken);
  14398. goto Exit;
  14399. }
  14400. //
  14401. // Find the response code message.
  14402. //
  14403. pszToken = strtok(NULL, " \t\r");
  14404. if (pszToken == NULL)
  14405. {
  14406. DPFX(DPFPREP, 9, "Could not locate response code message white-space separator.");
  14407. goto Exit;
  14408. }
  14409. //
  14410. // Make sure it's the right string, case insensitive.
  14411. //
  14412. if (_stricmp(pszToken, "OK") != 0)
  14413. {
  14414. DPFX(DPFPREP, 1, "The response code message specified is not \"OK\" (it's \"%hs\").",
  14415. pszToken);
  14416. goto Exit;
  14417. }
  14418. //
  14419. // Parse the header information.
  14420. //
  14421. ZeroMemory(&HeaderInfo, sizeof(HeaderInfo));
  14422. this->ParseUPnPHeaders((pszToken + strlen(pszToken) + 1),
  14423. &HeaderInfo);
  14424. //
  14425. // Skip responses which don't include the required headers.
  14426. //
  14427. if ((HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CACHECONTROL] == NULL) ||
  14428. (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_EXT] == NULL) ||
  14429. (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_LOCATION] == NULL) ||
  14430. (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_SERVER] == NULL) ||
  14431. (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_ST] == NULL) ||
  14432. (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_USN] == NULL))
  14433. {
  14434. DPFX(DPFPREP, 1, "One of the expected headers was not specified, ignoring message.");
  14435. goto Exit;
  14436. }
  14437. //
  14438. // Make sure the service type is correct.
  14439. //
  14440. if ((_stricmp(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_ST], URI_SERVICE_WANIPCONNECTION_A) != 0) &&
  14441. (_stricmp(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_ST], URI_SERVICE_WANPPPCONNECTION_A) != 0))
  14442. {
  14443. DPFX(DPFPREP, 1, "Service type \"%hs\" is not desired \"" URI_SERVICE_WANIPCONNECTION_A "\" or \"" URI_SERVICE_WANPPPCONNECTION_A "\", ignoring message.",
  14444. HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_ST]);
  14445. goto Exit;
  14446. }
  14447. //
  14448. // Parse the location header into an address and port.
  14449. //
  14450. hr = this->GetAddressFromURL(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_LOCATION],
  14451. &saddrinHost,
  14452. &pszRelativePath);
  14453. if (hr != DPNH_OK)
  14454. {
  14455. DPFX(DPFPREP, 1, "Couldn't get address from URL (err = 0x%lx), ignoring message.",
  14456. hr);
  14457. hr = DPNH_OK;
  14458. goto Exit;
  14459. }
  14460. //
  14461. // Don't accept responses that refer to addresses other than the one that
  14462. // sent this response.
  14463. //
  14464. if (psaddrinSource->sin_addr.S_un.S_addr != saddrinHost.sin_addr.S_un.S_addr)
  14465. {
  14466. DPFX(DPFPREP, 1, "Host IP address designated (%u.%u.%u.%u:%u) is not the same as source of response (%u.%u.%u.%u:%u), ignoring message.",
  14467. saddrinHost.sin_addr.S_un.S_un_b.s_b1,
  14468. saddrinHost.sin_addr.S_un.S_un_b.s_b2,
  14469. saddrinHost.sin_addr.S_un.S_un_b.s_b3,
  14470. saddrinHost.sin_addr.S_un.S_un_b.s_b4,
  14471. NTOHS(saddrinHost.sin_port),
  14472. psaddrinSource->sin_addr.S_un.S_un_b.s_b1,
  14473. psaddrinSource->sin_addr.S_un.S_un_b.s_b2,
  14474. psaddrinSource->sin_addr.S_un.S_un_b.s_b3,
  14475. psaddrinSource->sin_addr.S_un.S_un_b.s_b4,
  14476. NTOHS(psaddrinSource->sin_port));
  14477. hr = DPNH_OK;
  14478. goto Exit;
  14479. }
  14480. //
  14481. // Don't accept responses that refer to ports in the reserved range (less
  14482. // than or equal to 1024), other than the standard HTTP port.
  14483. //
  14484. if ((NTOHS(saddrinHost.sin_port) <= MAX_RESERVED_PORT) &&
  14485. (saddrinHost.sin_port != HTONS(HTTP_PORT)))
  14486. {
  14487. DPFX(DPFPREP, 1, "Host address designated invalid port %u, ignoring message.",
  14488. NTOHS(saddrinHost.sin_port));
  14489. hr = DPNH_OK;
  14490. goto Exit;
  14491. }
  14492. //
  14493. // Any errors we get from here on out will cause us to jump to the Failure
  14494. // label, instead of going straight to Exit.
  14495. //
  14496. //
  14497. // Create a socket to connect to that address.
  14498. //
  14499. ZeroMemory(&saddrinLocal, sizeof(saddrinLocal));
  14500. saddrinLocal.sin_family = AF_INET;
  14501. saddrinLocal.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  14502. CreateSocket:
  14503. sTemp = this->CreateSocket(&saddrinLocal, SOCK_STREAM, 0);
  14504. if (sTemp == INVALID_SOCKET)
  14505. {
  14506. DPFX(DPFPREP, 0, "Couldn't create stream socket!");
  14507. hr = DPNHERR_GENERIC;
  14508. goto Failure;
  14509. }
  14510. //
  14511. // Initiate the connection to the UPnP device. It is expected that connect
  14512. // will return WSAEWOULDBLOCK.
  14513. //
  14514. if (this->m_pfnconnect(sTemp,
  14515. (SOCKADDR*) (&saddrinHost),
  14516. sizeof(saddrinHost)) != 0)
  14517. {
  14518. dwError = this->m_pfnWSAGetLastError();
  14519. //
  14520. // If we hit a particular local/remote pair that is in TCP's TIME_WAIT
  14521. // state, then we need to pick a different port.
  14522. //
  14523. if (dwError == WSAEADDRINUSE)
  14524. {
  14525. dwNumConnectRetries++;
  14526. if (dwNumConnectRetries > MAX_NUM_TIMEWAITCONNECT_RETRIES)
  14527. {
  14528. DPFX(DPFPREP, 0, "Could not connect to %u.%u.%u.%u:%u after %u attempts!",
  14529. saddrinHost.sin_addr.S_un.S_un_b.s_b1,
  14530. saddrinHost.sin_addr.S_un.S_un_b.s_b2,
  14531. saddrinHost.sin_addr.S_un.S_un_b.s_b3,
  14532. saddrinHost.sin_addr.S_un.S_un_b.s_b4,
  14533. NTOHS(saddrinHost.sin_port),
  14534. dwNumConnectRetries);
  14535. hr = DPNHERR_GENERIC;
  14536. goto Failure;
  14537. }
  14538. DPFX(DPFPREP, 1, "Could not connect to %u.%u.%u.%u:%u using local address %u.%u.%u.%u:%u, picking different local port.",
  14539. saddrinHost.sin_addr.S_un.S_un_b.s_b1,
  14540. saddrinHost.sin_addr.S_un.S_un_b.s_b2,
  14541. saddrinHost.sin_addr.S_un.S_un_b.s_b3,
  14542. saddrinHost.sin_addr.S_un.S_un_b.s_b4,
  14543. NTOHS(saddrinHost.sin_port),
  14544. saddrinLocal.sin_addr.S_un.S_un_b.s_b1,
  14545. saddrinLocal.sin_addr.S_un.S_un_b.s_b2,
  14546. saddrinLocal.sin_addr.S_un.S_un_b.s_b3,
  14547. saddrinLocal.sin_addr.S_un.S_un_b.s_b4,
  14548. NTOHS(saddrinLocal.sin_port));
  14549. this->m_pfnclosesocket(sTemp);
  14550. sTemp = INVALID_SOCKET;
  14551. goto CreateSocket;
  14552. }
  14553. if (dwError != WSAEWOULDBLOCK)
  14554. {
  14555. DPFX(DPFPREP, 0, "Couldn't connect socket, error = %u!", dwError);
  14556. hr = DPNHERR_GENERIC;
  14557. goto Failure;
  14558. }
  14559. }
  14560. else
  14561. {
  14562. //
  14563. // connect() on non-blocking sockets is explicitly documented as
  14564. // always returning WSAEWOULDBLOCK, but CE seems to do it anyway.
  14565. //
  14566. DPFX(DPFPREP, 8, "Socket connected right away.");
  14567. }
  14568. //
  14569. // Create a new object to represent the UPnP device to which we're trying
  14570. // to connect.
  14571. //
  14572. pUPnPDevice = new CUPnPDevice(this->m_dwCurrentUPnPDeviceID++);
  14573. if (pUPnPDevice == NULL)
  14574. {
  14575. hr = DPNHERR_OUTOFMEMORY;
  14576. goto Failure;
  14577. }
  14578. hr = pUPnPDevice->SetLocationURL(pszRelativePath);
  14579. if (hr != DPNH_OK)
  14580. {
  14581. DPFX(DPFPREP, 0, "Couldn't set UPnP device's location URL!");
  14582. goto Failure;
  14583. }
  14584. pUPnPDevice->SetHostAddress(&saddrinHost);
  14585. hr = pUPnPDevice->SetUSN(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_USN]);
  14586. if (hr != DPNH_OK)
  14587. {
  14588. DPFX(DPFPREP, 0, "Couldn't set UPnP device's USN!");
  14589. goto Failure;
  14590. }
  14591. hr = pUPnPDevice->CreateReceiveBuffer(UPNP_STREAM_RECV_BUFFER_INITIAL_SIZE);
  14592. if (hr != DPNH_OK)
  14593. {
  14594. DPFX(DPFPREP, 0, "Couldn't create UPnP device's receive buffer!");
  14595. goto Failure;
  14596. }
  14597. DPFX(DPFPREP, 7, "Created new UPnP device object 0x%p ID %u.",
  14598. pUPnPDevice, pUPnPDevice->GetID());
  14599. //
  14600. // It's connecting...
  14601. //
  14602. pUPnPDevice->NoteConnecting();
  14603. //
  14604. // See if we need to avoid trying asymmetric port mappings.
  14605. //
  14606. if (g_fNoAsymmetricMappings)
  14607. {
  14608. DPFX(DPFPREP, 1, "Preventing asymmetric port mappings on new UPnP device 0x%p.",
  14609. pUPnPDevice);
  14610. pUPnPDevice->NoteDoesNotSupportAsymmetricMappings();
  14611. }
  14612. //
  14613. // Transfer ownership of the socket to the object.
  14614. //
  14615. pUPnPDevice->SetControlSocket(sTemp);
  14616. //
  14617. // Associate it with the device.
  14618. //
  14619. pUPnPDevice->MakeDeviceOwner(pDevice);
  14620. //
  14621. // Add it to the global list, and transfer ownership of the reference.
  14622. //
  14623. pUPnPDevice->m_blList.InsertBefore(&this->m_blUPnPDevices);
  14624. pUPnPDevice = NULL;
  14625. //
  14626. // Inform the caller that there's a new connection pending.
  14627. //
  14628. (*pfInitiatedConnect) = TRUE;
  14629. //
  14630. // Clear the device's discovery flags, now that we have a device, we're not
  14631. // going to be searching anymore.
  14632. //
  14633. pDevice->NoteNotPerformingRemoteUPnPDiscovery();
  14634. pDevice->NoteNotPerformingLocalUPnPDiscovery();
  14635. Exit:
  14636. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  14637. return hr;
  14638. Failure:
  14639. if (pUPnPDevice != NULL)
  14640. {
  14641. //pUPnPDevice->DestroyReceiveBuffer();
  14642. pUPnPDevice->ClearUSN();
  14643. pUPnPDevice->ClearLocationURL();
  14644. pUPnPDevice->DecRef();
  14645. }
  14646. if (sTemp != INVALID_SOCKET)
  14647. {
  14648. this->m_pfnclosesocket(sTemp);
  14649. sTemp = INVALID_SOCKET;
  14650. }
  14651. goto Exit;
  14652. } // CNATHelpUPnP::HandleUPnPDiscoveryResponseMsg
  14653. #undef DPF_MODNAME
  14654. #define DPF_MODNAME "CNATHelpUPnP::ReconnectUPnPControlSocket"
  14655. //=============================================================================
  14656. // CNATHelpUPnP::ReconnectUPnPControlSocket
  14657. //-----------------------------------------------------------------------------
  14658. //
  14659. // Description: Re-establishes a UPnP device TCP/IP connection.
  14660. //
  14661. // UPnP devices may get removed from list if a failure occurs.
  14662. //
  14663. // The object lock is assumed to be held.
  14664. //
  14665. // Arguments:
  14666. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device to reconnect.
  14667. //
  14668. // Returns: HRESULT
  14669. // DPNH_OK - Message was handled successfully.
  14670. // DPNHERR_GENERIC - An error occurred.
  14671. //=============================================================================
  14672. HRESULT CNATHelpUPnP::ReconnectUPnPControlSocket(CUPnPDevice * const pUPnPDevice)
  14673. {
  14674. HRESULT hr = DPNH_OK;
  14675. SOCKET sTemp = INVALID_SOCKET;
  14676. CDevice * pDevice;
  14677. SOCKADDR_IN saddrinLocal;
  14678. DWORD dwNumConnectRetries = 0;
  14679. DWORD dwError;
  14680. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pUPnPDevice);
  14681. DNASSERT(pUPnPDevice->GetControlSocket() == INVALID_SOCKET);
  14682. //
  14683. // Create a socket to connect to that address.
  14684. //
  14685. pDevice = pUPnPDevice->GetOwningDevice();
  14686. DNASSERT(pDevice != NULL);
  14687. ZeroMemory(&saddrinLocal, sizeof(saddrinLocal));
  14688. saddrinLocal.sin_family = AF_INET;
  14689. saddrinLocal.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  14690. CreateSocket:
  14691. sTemp = this->CreateSocket(&saddrinLocal, SOCK_STREAM, 0);
  14692. if (sTemp == INVALID_SOCKET)
  14693. {
  14694. DPFX(DPFPREP, 0, "Couldn't create stream socket!");
  14695. hr = DPNHERR_GENERIC;
  14696. goto Failure;
  14697. }
  14698. //
  14699. // Initiate the connection to the UPnP device. It is expected that connect
  14700. // will return WSAEWOULDBLOCK.
  14701. //
  14702. if (this->m_pfnconnect(sTemp,
  14703. (SOCKADDR*) (pUPnPDevice->GetControlAddress()),
  14704. sizeof(SOCKADDR_IN)) != 0)
  14705. {
  14706. dwError = this->m_pfnWSAGetLastError();
  14707. //
  14708. // If we hit a particular local/remote pair that is in TCP's TIME_WAIT
  14709. // state, then we need to pick a different port.
  14710. //
  14711. if (dwError == WSAEADDRINUSE)
  14712. {
  14713. dwNumConnectRetries++;
  14714. if (dwNumConnectRetries > MAX_NUM_TIMEWAITCONNECT_RETRIES)
  14715. {
  14716. DPFX(DPFPREP, 0, "Could not connect to %u.%u.%u.%u:%u after %u attempts!",
  14717. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b1,
  14718. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b2,
  14719. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b3,
  14720. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b4,
  14721. NTOHS(pUPnPDevice->GetControlAddress()->sin_port),
  14722. dwNumConnectRetries);
  14723. hr = DPNHERR_GENERIC;
  14724. goto Failure;
  14725. }
  14726. DPFX(DPFPREP, 1, "Could not connect to %u.%u.%u.%u:%u using local address %u.%u.%u.%u:%u, picking different local port.",
  14727. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b1,
  14728. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b2,
  14729. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b3,
  14730. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b4,
  14731. NTOHS(pUPnPDevice->GetControlAddress()->sin_port),
  14732. saddrinLocal.sin_addr.S_un.S_un_b.s_b1,
  14733. saddrinLocal.sin_addr.S_un.S_un_b.s_b2,
  14734. saddrinLocal.sin_addr.S_un.S_un_b.s_b3,
  14735. saddrinLocal.sin_addr.S_un.S_un_b.s_b4,
  14736. NTOHS(saddrinLocal.sin_port));
  14737. this->m_pfnclosesocket(sTemp);
  14738. sTemp = INVALID_SOCKET;
  14739. goto CreateSocket;
  14740. }
  14741. if (dwError != WSAEWOULDBLOCK)
  14742. {
  14743. DPFX(DPFPREP, 0, "Couldn't connect socket, error = %u!", dwError);
  14744. hr = DPNHERR_GENERIC;
  14745. goto Failure;
  14746. }
  14747. }
  14748. else
  14749. {
  14750. //
  14751. // connect() on non-blocking sockets is explicitly documented as
  14752. // always returning WSAEWOULDBLOCK, but CE seems to do it anyway.
  14753. //
  14754. DPFX(DPFPREP, 8, "Socket connected right away.");
  14755. }
  14756. //
  14757. // It's reconnecting...
  14758. //
  14759. pUPnPDevice->NoteConnecting();
  14760. //
  14761. // Transfer ownership of the socket to the object.
  14762. //
  14763. pUPnPDevice->SetControlSocket(sTemp);
  14764. sTemp = INVALID_SOCKET;
  14765. //
  14766. // Wait for the connect to complete.
  14767. //
  14768. hr = this->WaitForUPnPConnectCompletions();
  14769. if (hr != DPNH_OK)
  14770. {
  14771. DPFX(DPFPREP, 0, "Couldn't wait for UPnP connect completions!");
  14772. goto Failure;
  14773. }
  14774. //
  14775. // Make sure the connect completed successfully.
  14776. //
  14777. if (! pUPnPDevice->IsConnected())
  14778. {
  14779. DPFX(DPFPREP, 0, "UPnP device 0x%p failed reconnecting!", pUPnPDevice);
  14780. //
  14781. // Note that the device is cleaned up and is not in any lists anymore.
  14782. //
  14783. hr = DPNHERR_SERVERNOTRESPONDING;
  14784. goto Exit;
  14785. }
  14786. Exit:
  14787. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  14788. return hr;
  14789. Failure:
  14790. if (sTemp != INVALID_SOCKET)
  14791. {
  14792. this->m_pfnclosesocket(sTemp);
  14793. sTemp = INVALID_SOCKET;
  14794. }
  14795. goto Exit;
  14796. } // CNATHelpUPnP::ReconnectUPnPControlSocket
  14797. #undef DPF_MODNAME
  14798. #define DPF_MODNAME "CNATHelpUPnP::ReceiveUPnPDataStream"
  14799. //=============================================================================
  14800. // CNATHelpUPnP::ReceiveUPnPDataStream
  14801. //-----------------------------------------------------------------------------
  14802. //
  14803. // Description: Receives incoming data from a UPnP TCP connection.
  14804. //
  14805. // The UPnP device may get removed from list if a failure
  14806. // occurs, the caller needs to have a reference.
  14807. //
  14808. // The object lock is assumed to be held.
  14809. //
  14810. // Arguments:
  14811. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device with data to receive.
  14812. //
  14813. // Returns: HRESULT
  14814. // DPNH_OK - Data was received successfully.
  14815. // DPNHERR_GENERIC - An error occurred.
  14816. //=============================================================================
  14817. HRESULT CNATHelpUPnP::ReceiveUPnPDataStream(CUPnPDevice * const pUPnPDevice)
  14818. {
  14819. HRESULT hr = DPNH_OK;
  14820. char * pszDeChunkedBuffer = NULL;
  14821. int iReturn;
  14822. DWORD dwError;
  14823. char * pszCurrent;
  14824. char * pszEndOfBuffer;
  14825. UPNP_HEADER_INFO HeaderInfo;
  14826. DWORD dwContentLength;
  14827. char * pszToken;
  14828. DWORD dwHTTPResponseCode;
  14829. int iHeaderLength;
  14830. DWORD dwBufferRemaining;
  14831. char * pszChunkData;
  14832. DWORD dwChunkSize;
  14833. char * pszDestination;
  14834. #ifdef DBG
  14835. char * pszPrintIfFailed = NULL;
  14836. #endif // DBG
  14837. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pUPnPDevice);
  14838. do
  14839. {
  14840. //
  14841. // Make sure there's room in the buffer to actually get the data.
  14842. //
  14843. if (pUPnPDevice->GetRemainingReceiveBufferSize() == 0)
  14844. {
  14845. DPFX(DPFPREP, 7, "Increasing receive buffer size prior to receiving.");
  14846. hr = pUPnPDevice->IncreaseReceiveBufferSize();
  14847. if (hr != DPNH_OK)
  14848. {
  14849. DPFX(DPFPREP, 0, "Couldn't increase receive buffer size prior to receiving!");
  14850. goto Failure;
  14851. }
  14852. }
  14853. //
  14854. // Actually get the data that was indicated.
  14855. //
  14856. iReturn = this->m_pfnrecv(pUPnPDevice->GetControlSocket(),
  14857. pUPnPDevice->GetCurrentReceiveBufferPtr(),
  14858. pUPnPDevice->GetRemainingReceiveBufferSize(),
  14859. 0);
  14860. switch (iReturn)
  14861. {
  14862. case 0:
  14863. {
  14864. //
  14865. // Since the connection has been broken, shutdown the socket.
  14866. //
  14867. this->m_pfnshutdown(pUPnPDevice->GetControlSocket(), 0); // ignore error
  14868. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  14869. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  14870. //
  14871. // Mark the socket as not connected.
  14872. //
  14873. pUPnPDevice->NoteNotConnected();
  14874. //
  14875. // There may have been HTTP success/error information sent
  14876. // before the connection was closed.
  14877. //
  14878. if (pUPnPDevice->GetUsedReceiveBufferSize() == 0)
  14879. {
  14880. DPFX(DPFPREP, 3, "UPnP device 0x%p shut down connection (no more data).",
  14881. pUPnPDevice);
  14882. //
  14883. // Hopefully we got what we needed, but we're done now.
  14884. //
  14885. goto Exit;
  14886. }
  14887. DPFX(DPFPREP, 3, "UPnP device 0x%p gracefully closed connection after sending data.",
  14888. pUPnPDevice);
  14889. //
  14890. // Continue through and parse what data we have.
  14891. //
  14892. break;
  14893. }
  14894. case SOCKET_ERROR:
  14895. {
  14896. dwError = this->m_pfnWSAGetLastError();
  14897. switch (dwError)
  14898. {
  14899. case WSAEMSGSIZE:
  14900. {
  14901. //
  14902. // There's not enough room in the buffer. Double the
  14903. // buffer and try again.
  14904. //
  14905. DPFX(DPFPREP, 7, "Increasing receive buffer size after WSAEMSGSIZE error.");
  14906. hr = pUPnPDevice->IncreaseReceiveBufferSize();
  14907. if (hr != DPNH_OK)
  14908. {
  14909. DPFX(DPFPREP, 0, "Couldn't increase receive buffer size!");
  14910. goto Failure;
  14911. }
  14912. break;
  14913. }
  14914. case WSAECONNABORTED:
  14915. case WSAECONNRESET:
  14916. {
  14917. DPFX(DPFPREP, 1, "UPnP device shutdown connection (err = %u).", dwError);
  14918. //
  14919. // Our caller should remove this device.
  14920. //
  14921. hr = DPNHERR_GENERIC;
  14922. goto Failure;
  14923. break;
  14924. }
  14925. case WSAENOBUFS:
  14926. {
  14927. DPFX(DPFPREP, 0, "WinSock returned WSAENOBUFS while receiving!");
  14928. //
  14929. // Our caller should remove this device.
  14930. //
  14931. hr = DPNHERR_OUTOFMEMORY;
  14932. goto Failure;
  14933. break;
  14934. }
  14935. default:
  14936. {
  14937. DPFX(DPFPREP, 0, "Got unknown sockets error %u while receiving data!", dwError);
  14938. hr = DPNHERR_GENERIC;
  14939. goto Failure;
  14940. break;
  14941. }
  14942. }
  14943. break;
  14944. }
  14945. default:
  14946. {
  14947. DPFX(DPFPREP, 2, "Received %i bytes of data from UPnP device 0%p.",
  14948. iReturn, pUPnPDevice);
  14949. pUPnPDevice->UpdateUsedReceiveBufferSize(iReturn);
  14950. //
  14951. // We'll also break out of the do-while loop below.
  14952. //
  14953. break;
  14954. }
  14955. }
  14956. }
  14957. while (iReturn == SOCKET_ERROR);
  14958. //
  14959. // If we're here, we've gotten the data that has currently arrived.
  14960. //
  14961. //
  14962. // If we have all the headers, specifically the content length header, we
  14963. // can tell whether we have the whole message or not. If not, we can't do
  14964. // anything until the rest of the data comes in.
  14965. //
  14966. if (pUPnPDevice->IsWaitingForContent())
  14967. {
  14968. dwContentLength = pUPnPDevice->GetExpectedContentSize();
  14969. if (dwContentLength == -1)
  14970. {
  14971. //
  14972. // We have all the headers, but a faulty server implementation did
  14973. // not send a content-length header. We're going to wait until the
  14974. // socket is closed by the other side, and then consider all of the
  14975. // data received at that time to be the content. NOTE: It is
  14976. // expected that there will be a higher level timeout preventing us
  14977. // from waiting forever.
  14978. //
  14979. // So if the device is still connected, keep waiting.
  14980. //
  14981. // If we're using chunked transfer we won't get a content-length
  14982. // header or a total size legitimately. We will know the sizes of
  14983. // individual chunks, but that doesn't help us much. We basically
  14984. // need to scan for the "last chunk" indicator (or socket
  14985. // shutdown).
  14986. //
  14987. if (pUPnPDevice->IsConnected())
  14988. {
  14989. //
  14990. // If we're using chunked transfer, see if we have enough
  14991. // information already to determine if we're done.
  14992. //
  14993. if (pUPnPDevice->IsUsingChunkedTransferEncoding())
  14994. {
  14995. //
  14996. // Walk all of the chunks we have so far to see if we have
  14997. // the last one (the zero terminator).
  14998. //
  14999. pszCurrent = pUPnPDevice->GetReceiveBufferStart();
  15000. dwBufferRemaining = pUPnPDevice->GetUsedReceiveBufferSize();
  15001. do
  15002. {
  15003. if (! this->GetNextChunk(pszCurrent,
  15004. dwBufferRemaining,
  15005. &pszChunkData,
  15006. &dwChunkSize,
  15007. &pszCurrent,
  15008. &dwBufferRemaining))
  15009. {
  15010. DPFX(DPFPREP, 1, "Body contains invalid chunk (at offset %u)! Disconnecting.",
  15011. (DWORD_PTR) (pszCurrent - pUPnPDevice->GetReceiveBufferStart()));
  15012. goto Failure;
  15013. }
  15014. if (pszChunkData == NULL)
  15015. {
  15016. DPFX(DPFPREP, 1, "Did not receive end of chunked data (%u bytes received so far), continuing to waiting for data.",
  15017. pUPnPDevice->GetUsedReceiveBufferSize());
  15018. goto Exit;
  15019. }
  15020. }
  15021. while (dwChunkSize != 0);
  15022. }
  15023. else
  15024. {
  15025. DPFX(DPFPREP, 1, "Waiting for connection to be shutdown (%u bytes received).",
  15026. pUPnPDevice->GetUsedReceiveBufferSize());
  15027. goto Exit;
  15028. }
  15029. }
  15030. DPFX(DPFPREP, 1, "Socket closed with %u bytes received, parsing.",
  15031. pUPnPDevice->GetUsedReceiveBufferSize());
  15032. }
  15033. else
  15034. {
  15035. if (dwContentLength > pUPnPDevice->GetUsedReceiveBufferSize())
  15036. {
  15037. //
  15038. // We still haven't received all the data yet. Keep waiting
  15039. // (unless the socket is closed).
  15040. //
  15041. if (pUPnPDevice->IsConnected())
  15042. {
  15043. DPFX(DPFPREP, 1, "Still waiting for all content (%u bytes of %u total received).",
  15044. pUPnPDevice->GetUsedReceiveBufferSize(), dwContentLength);
  15045. goto Exit;
  15046. }
  15047. DPFX(DPFPREP, 1, "Socket closed before all content received (%u bytes of %u total), parsing anyway.",
  15048. pUPnPDevice->GetUsedReceiveBufferSize(), dwContentLength);
  15049. //
  15050. // Try parsing it anyway.
  15051. //
  15052. }
  15053. }
  15054. //
  15055. // Retrieve the HTTP response code stored earlier.
  15056. //
  15057. dwHTTPResponseCode = pUPnPDevice->GetHTTPResponseCode();
  15058. //
  15059. // All of the content that's going to arrive, has.
  15060. //
  15061. pUPnPDevice->NoteNotWaitingForContent();
  15062. //
  15063. // Make sure the buffer is NULL terminated. But first ensure the
  15064. // buffer can hold a new NULL termination character.
  15065. //
  15066. if (pUPnPDevice->GetRemainingReceiveBufferSize() == 0)
  15067. {
  15068. DPFX(DPFPREP, 7, "Increasing receive buffer size to hold NULL termination (for content).");
  15069. hr = pUPnPDevice->IncreaseReceiveBufferSize();
  15070. if (hr != DPNH_OK)
  15071. {
  15072. DPFX(DPFPREP, 0, "Couldn't increase receive buffer size to accommodate NULL termination (for content)!");
  15073. goto Failure;
  15074. }
  15075. }
  15076. //
  15077. // Move to the end of the buffer and NULL terminate it for string ops.
  15078. //
  15079. pszEndOfBuffer = pUPnPDevice->GetReceiveBufferStart()
  15080. + pUPnPDevice->GetUsedReceiveBufferSize();
  15081. (*pszEndOfBuffer) = '\0';
  15082. #ifdef DBG
  15083. //
  15084. // Print from the start of the buffer if we fail.
  15085. //
  15086. pszPrintIfFailed = pUPnPDevice->GetReceiveBufferStart();
  15087. #endif // DBG
  15088. //
  15089. // We now have all the data in a string buffer. Continue...
  15090. //
  15091. }
  15092. else
  15093. {
  15094. //
  15095. // We haven't already received the headers. The data we just got
  15096. // should be those headers.
  15097. //
  15098. pszCurrent = pUPnPDevice->GetReceiveBufferStart();
  15099. //
  15100. // Quick check to make sure the buffer starts with something reasonable
  15101. // in hopes of catching completely bogus responses earlier. Note that
  15102. // the buffer is not necessarily NULL terminated or completely
  15103. // available yet.
  15104. //
  15105. if ((pUPnPDevice->GetUsedReceiveBufferSize() >= strlen(HTTP_PREFIX)) &&
  15106. (_strnicmp(pszCurrent, HTTP_PREFIX, strlen(HTTP_PREFIX)) != 0))
  15107. {
  15108. DPFX(DPFPREP, 1, "Headers do not begin with \"" HTTP_PREFIX "\"! Disconnecting.");
  15109. goto Failure;
  15110. }
  15111. //
  15112. // We don't want to walk off the end of the buffer, so only search up
  15113. // to the last possible location for the sequence, which is the end of
  15114. // the buffer minus the double EOL sequence.
  15115. //
  15116. pszEndOfBuffer = pszCurrent
  15117. + pUPnPDevice->GetUsedReceiveBufferSize()
  15118. - strlen(EOL EOL);
  15119. while (pszCurrent < pszEndOfBuffer)
  15120. {
  15121. if (_strnicmp(pszCurrent, EOL EOL, strlen(EOL EOL)) == 0)
  15122. {
  15123. //
  15124. // Found end of headers.
  15125. //
  15126. //
  15127. // Possible loss of data on 64-bit is okay, we're just saving
  15128. // this for logging purposes.
  15129. //
  15130. iHeaderLength = (int) ((INT_PTR) (pszCurrent - pUPnPDevice->GetReceiveBufferStart()));
  15131. break;
  15132. }
  15133. pszCurrent++;
  15134. }
  15135. //
  15136. // If we didn't find the end of the headers, we're done (for now).
  15137. //
  15138. if (pszCurrent >= pszEndOfBuffer)
  15139. {
  15140. //
  15141. // We still haven't received all the data yet. Keep waiting
  15142. // (unless the socket is closed).
  15143. //
  15144. if (pUPnPDevice->IsConnected())
  15145. {
  15146. //
  15147. // Make sure the length is still within reason.
  15148. //
  15149. if (pUPnPDevice->GetUsedReceiveBufferSize() > MAX_UPNP_HEADER_LENGTH)
  15150. {
  15151. DPFX(DPFPREP, 1, "Headers are too large (%u > %u)! Disconnecting.",
  15152. pUPnPDevice->GetUsedReceiveBufferSize(), MAX_UPNP_HEADER_LENGTH);
  15153. goto Failure;
  15154. }
  15155. DPFX(DPFPREP, 1, "Have not detected end of headers yet (%u bytes received).",
  15156. pUPnPDevice->GetUsedReceiveBufferSize());
  15157. goto Exit;
  15158. }
  15159. DPFX(DPFPREP, 1, "Socket closed before end of headers detected (%u bytes received), parsing anyway.",
  15160. pUPnPDevice->GetUsedReceiveBufferSize());
  15161. //
  15162. // Consider the whole buffer the headers length.
  15163. //
  15164. iHeaderLength = pUPnPDevice->GetUsedReceiveBufferSize();
  15165. //
  15166. // Try parsing it anyway.
  15167. //
  15168. }
  15169. #ifdef DBG
  15170. //
  15171. // Log the headers.
  15172. //
  15173. this->PrintUPnPTransactionToFile(pUPnPDevice->GetReceiveBufferStart(),
  15174. iHeaderLength,
  15175. "Inbound UPnP stream headers",
  15176. pUPnPDevice->GetOwningDevice());
  15177. #endif // DBG
  15178. //
  15179. // Make sure the buffer is NULL terminated. But first ensure the
  15180. // buffer can hold a new NULL termination character.
  15181. //
  15182. if (pUPnPDevice->GetRemainingReceiveBufferSize() == 0)
  15183. {
  15184. DPFX(DPFPREP, 7, "Increasing receive buffer size to hold NULL termination (for headers).");
  15185. hr = pUPnPDevice->IncreaseReceiveBufferSize();
  15186. if (hr != DPNH_OK)
  15187. {
  15188. DPFX(DPFPREP, 0, "Couldn't increase receive buffer size to accommodate NULL termination (for headers)!");
  15189. goto Failure;
  15190. }
  15191. //
  15192. // Find the end of the new buffer, and make sure it's NULL
  15193. // terminated for string ops.
  15194. //
  15195. pszEndOfBuffer = pUPnPDevice->GetReceiveBufferStart()
  15196. + pUPnPDevice->GetUsedReceiveBufferSize();
  15197. }
  15198. else
  15199. {
  15200. //
  15201. // Move to the end of the buffer and NULL terminate it for string
  15202. // ops.
  15203. //
  15204. pszEndOfBuffer += strlen(EOL EOL);
  15205. }
  15206. (*pszEndOfBuffer) = '\0';
  15207. //
  15208. // Make sure the buffer is a valid response. Find the version string.
  15209. //
  15210. pszToken = strtok(pUPnPDevice->GetReceiveBufferStart(), " \t\n");
  15211. if (pszToken == NULL)
  15212. {
  15213. DPFX(DPFPREP, 1, "Could not locate first white-space separator! Disconnecting.");
  15214. goto Failure;
  15215. }
  15216. //
  15217. // Check the version string, case insensitive.
  15218. //
  15219. if ((_stricmp(pszToken, HTTP_VERSION) != 0) &&
  15220. (_stricmp(pszToken, HTTP_VERSION_ALT) != 0))
  15221. {
  15222. DPFX(DPFPREP, 1, "The version specified in the response message is not \"" HTTP_VERSION "\" or \"" HTTP_VERSION_ALT "\" (it's \"%hs\")! Disconnecting.",
  15223. pszToken);
  15224. goto Failure;
  15225. }
  15226. //
  15227. // Find the response code number string.
  15228. //
  15229. pszToken = strtok(NULL, " ");
  15230. if (pszToken == NULL)
  15231. {
  15232. DPFX(DPFPREP, 1, "Could not find the response code number space! Disconnecting.");
  15233. goto Failure;
  15234. }
  15235. //
  15236. // Retrieve the success/failure code value.
  15237. //
  15238. dwHTTPResponseCode = atoi(pszToken);
  15239. //
  15240. // Find the response code message.
  15241. //
  15242. pszToken = strtok(NULL, "\t\r");
  15243. if (pszToken == NULL)
  15244. {
  15245. DPFX(DPFPREP, 1, "Could not locate response code message white-space separator! Disconnecting.");
  15246. goto Failure;
  15247. }
  15248. DPFX(DPFPREP, 1, "Received HTTP response %u \"%hs\".",
  15249. dwHTTPResponseCode, pszToken);
  15250. //
  15251. // Try parsing the headers (after the response status line).
  15252. //
  15253. ZeroMemory(&HeaderInfo, sizeof(HeaderInfo));
  15254. this->ParseUPnPHeaders((pszToken + strlen(pszToken) + 1),
  15255. &HeaderInfo);
  15256. #ifdef DBG
  15257. //
  15258. // Print from the start of the message body if we fail.
  15259. //
  15260. pszPrintIfFailed = HeaderInfo.pszMsgBody;
  15261. #endif // DBG
  15262. if ((HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_TRANSFERENCODING] != NULL) &&
  15263. (_strnicmp(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_TRANSFERENCODING], "chunked", strlen("chunked")) == 0))
  15264. {
  15265. pUPnPDevice->NoteUsingChunkedTransferEncoding();
  15266. }
  15267. //
  15268. // We're pretty lenient about missing headers...
  15269. //
  15270. if (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTLENGTH] == NULL)
  15271. {
  15272. DPFX(DPFPREP, 1, "Content-length header was not specified in response (chunked = %i).",
  15273. pUPnPDevice->IsUsingChunkedTransferEncoding());
  15274. //
  15275. // May be because we're using chunked transfer encoding, or it
  15276. // could be a bad device. Either way, we'll continue...
  15277. //
  15278. dwContentLength = -1;
  15279. }
  15280. else
  15281. {
  15282. dwContentLength = atoi(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTLENGTH]);
  15283. #ifdef DBG
  15284. if (dwContentLength == 0)
  15285. {
  15286. DPFX(DPFPREP, 1, "Content length (\"%hs\") is zero.",
  15287. HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTLENGTH]);
  15288. }
  15289. #endif // DBG
  15290. if (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTTYPE] == NULL)
  15291. {
  15292. DPFX(DPFPREP, 1, "Expected content-type header was not specified in response, continuing.");
  15293. }
  15294. else
  15295. {
  15296. if (_strnicmp(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTTYPE], "text/xml", strlen("text/xml")) != 0)
  15297. {
  15298. DPFX(DPFPREP, 1, "Content type does not start with \"text/xml\" (it's \"%hs\")! Disconnecting.",
  15299. HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTTYPE]);
  15300. goto Failure;
  15301. }
  15302. #ifdef DBG
  15303. //
  15304. // Note whether the content type is qualified with
  15305. // "charset=utf-8" or not.
  15306. //
  15307. if (_stricmp(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTTYPE], "text/xml; charset=\"utf-8\"") != 0)
  15308. {
  15309. DPFX(DPFPREP, 1, "Content type is xml, but it's not \"text/xml; charset=\"utf-8\"\" (it's \"%hs\"), continuing.",
  15310. HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTTYPE]);
  15311. //
  15312. // The check was just for information purposes, continue.
  15313. //
  15314. }
  15315. #endif // DBG
  15316. }
  15317. }
  15318. //
  15319. // Content length may be valid, or the special value -1 at this
  15320. // point.
  15321. //
  15322. DPFX(DPFPREP, 7, "Moving past 0x%p bytes of header.",
  15323. HeaderInfo.pszMsgBody - pUPnPDevice->GetReceiveBufferStart());
  15324. //
  15325. // Forget about all the headers, we only care about data now.
  15326. //
  15327. pUPnPDevice->UpdateReceiveBufferStart(HeaderInfo.pszMsgBody);
  15328. //
  15329. // The buffer has been destroyed up to the end of the headers (meaning
  15330. // that calling ParseUPnPHeaders won't work on the same buffer again),
  15331. // so if we don't have all the content yet, we need to save the
  15332. // response code, remember the fact that we're not done yet, and
  15333. // continue waiting for the rest of the data.
  15334. // Of course, if there wasn't a content-length header, we have to wait
  15335. // for the socket to shutdown before we can parse.
  15336. //
  15337. // Also, if we're using chunked transfer we won't get a content-length
  15338. // header or a total size legitimately. We will know the sizes of
  15339. // individual chunks, but that doesn't help us much. We basically need
  15340. // to scan for the "last chunk" indicator (or socket shutdown).
  15341. //
  15342. if (dwContentLength == -1)
  15343. {
  15344. if (pUPnPDevice->IsConnected())
  15345. {
  15346. //
  15347. // If we're using chunked transfer, see if we have enough
  15348. // information already to determine if we're done.
  15349. //
  15350. if (pUPnPDevice->IsUsingChunkedTransferEncoding())
  15351. {
  15352. //
  15353. // Walk all of the chunks we have so far to see if we have
  15354. // the last one (the zero terminator).
  15355. //
  15356. pszCurrent = pUPnPDevice->GetReceiveBufferStart();
  15357. dwBufferRemaining = pUPnPDevice->GetUsedReceiveBufferSize();
  15358. do
  15359. {
  15360. if (! this->GetNextChunk(pszCurrent,
  15361. dwBufferRemaining,
  15362. &pszChunkData,
  15363. &dwChunkSize,
  15364. &pszCurrent,
  15365. &dwBufferRemaining))
  15366. {
  15367. DPFX(DPFPREP, 1, "Body contains invalid chunk (at offset %u)! Disconnecting.",
  15368. (DWORD_PTR) (pszCurrent - pUPnPDevice->GetReceiveBufferStart()));
  15369. goto Failure;
  15370. }
  15371. if (pszChunkData == NULL)
  15372. {
  15373. DPFX(DPFPREP, 1, "Did not receive end of chunked data (%u bytes received so far), continuing to waiting for data.",
  15374. pUPnPDevice->GetUsedReceiveBufferSize());
  15375. pUPnPDevice->NoteWaitingForContent(dwContentLength, dwHTTPResponseCode);
  15376. goto Exit;
  15377. }
  15378. }
  15379. while (dwChunkSize != 0);
  15380. }
  15381. else
  15382. {
  15383. DPFX(DPFPREP, 1, "Unknown content length (%u bytes received so far), waiting for connection to close before parsing.",
  15384. pUPnPDevice->GetUsedReceiveBufferSize());
  15385. pUPnPDevice->NoteWaitingForContent(dwContentLength, dwHTTPResponseCode);
  15386. goto Exit;
  15387. }
  15388. }
  15389. }
  15390. else
  15391. {
  15392. if ((pUPnPDevice->IsConnected()) &&
  15393. (dwContentLength > pUPnPDevice->GetUsedReceiveBufferSize()))
  15394. {
  15395. DPFX(DPFPREP, 1, "Not all content has been received (%u bytes of %u total), waiting for remainder of message.",
  15396. pUPnPDevice->GetUsedReceiveBufferSize(), dwContentLength);
  15397. pUPnPDevice->NoteWaitingForContent(dwContentLength, dwHTTPResponseCode);
  15398. goto Exit;
  15399. }
  15400. }
  15401. //
  15402. // We have all the data already (and it's in string form).
  15403. // Continue...
  15404. //
  15405. }
  15406. //
  15407. // If we got here, it means we have all the data that we're expecting.
  15408. // Shutdown the socket if it hasn't been already.
  15409. //
  15410. if (pUPnPDevice->IsConnected())
  15411. {
  15412. DPFX(DPFPREP, 7, "Forcing UPnP device 0x%p socket disconnection.",
  15413. pUPnPDevice);
  15414. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  15415. this->m_pfnshutdown(pUPnPDevice->GetControlSocket(), 0); // ignore error
  15416. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  15417. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  15418. pUPnPDevice->NoteNotConnected();
  15419. }
  15420. else
  15421. {
  15422. DNASSERT(pUPnPDevice->GetControlSocket() == INVALID_SOCKET);
  15423. }
  15424. //
  15425. // If the sender used chunked-transfer encoding, copy each of the chunks
  15426. // into a contiguous "dechunked" buffer.
  15427. //
  15428. if (pUPnPDevice->IsUsingChunkedTransferEncoding())
  15429. {
  15430. //
  15431. // Prepare a dechunked buffer.
  15432. //
  15433. pszDeChunkedBuffer = (char*) DNMalloc(pUPnPDevice->GetUsedReceiveBufferSize());
  15434. if (pszDeChunkedBuffer == NULL)
  15435. {
  15436. hr = DPNHERR_OUTOFMEMORY;
  15437. goto Failure;
  15438. }
  15439. pszDestination = pszDeChunkedBuffer;
  15440. //
  15441. // Walk all of the chunks.
  15442. //
  15443. pszCurrent = pUPnPDevice->GetReceiveBufferStart();
  15444. dwBufferRemaining = pUPnPDevice->GetUsedReceiveBufferSize();
  15445. do
  15446. {
  15447. if (! this->GetNextChunk(pszCurrent,
  15448. dwBufferRemaining,
  15449. &pszChunkData,
  15450. &dwChunkSize,
  15451. &pszCurrent,
  15452. &dwBufferRemaining))
  15453. {
  15454. DPFX(DPFPREP, 1, "Body contains invalid chunk (at offset %u)!",
  15455. (DWORD_PTR) (pszCurrent - pUPnPDevice->GetReceiveBufferStart()));
  15456. goto Failure;
  15457. }
  15458. //
  15459. // If this chunk is unfinished, bail.
  15460. //
  15461. if (pszChunkData == NULL)
  15462. {
  15463. DPFX(DPFPREP, 1, "Did not receive complete chunked data!",
  15464. pUPnPDevice->GetUsedReceiveBufferSize());
  15465. goto Failure;
  15466. }
  15467. //
  15468. // If this is the last chunk, terminate the string here and stop.
  15469. //
  15470. if (dwChunkSize == 0)
  15471. {
  15472. (*pszDestination) = '\0';
  15473. break;
  15474. }
  15475. //
  15476. // Otherwise copy the chunk data to the dechunked buffer.
  15477. //
  15478. memcpy(pszDestination, pszChunkData, dwChunkSize);
  15479. pszDestination += dwChunkSize;
  15480. }
  15481. while (TRUE);
  15482. //
  15483. // Turn off the flag since it's no longer relevant.
  15484. //
  15485. pUPnPDevice->NoteNotUsingChunkedTransferEncoding();
  15486. //
  15487. // Parse the dechunked version of the message.
  15488. //
  15489. pszCurrent = pszDeChunkedBuffer;
  15490. }
  15491. else
  15492. {
  15493. //
  15494. // Get a pointer to the start of the message body.
  15495. //
  15496. pszCurrent = pUPnPDevice->GetReceiveBufferStart();
  15497. }
  15498. //
  15499. // Clear the buffer for the next message. Note that this does not
  15500. // invalidate the pszMessageBody pointer we just retrieved because
  15501. // ClearReceiveBuffer just resets the pointers back to the beginning (it
  15502. // does not zero out the buffer). We need to reset the buffer because the
  15503. // handler we're about to call may try to receive data as well. The buffer
  15504. // must be "empty" (reset) at that time. Of course, if the handler does do
  15505. // that, then it had better have saved off copies of any strings it needs
  15506. // because they will get overwritten once receiving starts.
  15507. //
  15508. // Content length of -1 means we never detected a valid CONTENT-LENGTH
  15509. // header, so we will assume the rest of the data that has been received up
  15510. // to now is (all of) the content.
  15511. //
  15512. #ifdef DBG
  15513. if ((dwContentLength != -1) &&
  15514. (dwContentLength < pUPnPDevice->GetUsedReceiveBufferSize()))
  15515. {
  15516. //
  15517. // The string was terminated before this data, so the handler will
  15518. // never even see it.
  15519. //
  15520. DPFX(DPFPREP, 1, "Ignoring %u bytes of extra data after response from UPnP device 0x%p.",
  15521. (pUPnPDevice->GetUsedReceiveBufferSize() - dwContentLength),
  15522. pUPnPDevice);
  15523. }
  15524. //
  15525. // HandleUPnPControlResponseBody or HandleUPnPDescriptionResponseBody might
  15526. // print out the body or overwrite the data, so we can't print it out if
  15527. // they fail.
  15528. //
  15529. pszPrintIfFailed = NULL;
  15530. #endif // DBG
  15531. pUPnPDevice->ClearReceiveBuffer();
  15532. if (pUPnPDevice->IsWaitingForControlResponse())
  15533. {
  15534. //
  15535. // It looks like it's a control response, because someone is waiting
  15536. // for one.
  15537. //
  15538. hr = this->HandleUPnPControlResponseBody(pUPnPDevice,
  15539. dwHTTPResponseCode,
  15540. pszCurrent);
  15541. if (hr != DPNH_OK)
  15542. {
  15543. DPFX(DPFPREP, 0, "Couldn't handle control response!", hr);
  15544. goto Failure;
  15545. }
  15546. }
  15547. else
  15548. {
  15549. //
  15550. // Not waiting for a control response, assume it's a description
  15551. // response.
  15552. //
  15553. hr = this->HandleUPnPDescriptionResponseBody(pUPnPDevice,
  15554. dwHTTPResponseCode,
  15555. pszCurrent);
  15556. if (hr != DPNH_OK)
  15557. {
  15558. //
  15559. // UPnP device may have been removed from list.
  15560. //
  15561. DPFX(DPFPREP, 0, "Couldn't handle description response!", hr);
  15562. goto Failure;
  15563. }
  15564. }
  15565. Exit:
  15566. if (pszDeChunkedBuffer != NULL)
  15567. {
  15568. DNFree(pszDeChunkedBuffer);
  15569. pszDeChunkedBuffer = NULL;
  15570. }
  15571. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  15572. return hr;
  15573. Failure:
  15574. //
  15575. // Something went wrong, break the connection if it exists.
  15576. //
  15577. if (pUPnPDevice->IsConnected())
  15578. {
  15579. this->m_pfnshutdown(pUPnPDevice->GetControlSocket(), 0); // ignore error
  15580. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  15581. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  15582. //
  15583. // Mark the socket as not connected.
  15584. //
  15585. pUPnPDevice->NoteNotConnected();
  15586. }
  15587. #ifdef DBG
  15588. if (pszPrintIfFailed != NULL)
  15589. {
  15590. this->PrintUPnPTransactionToFile(pszPrintIfFailed,
  15591. strlen(pszPrintIfFailed),
  15592. "Inbound ignored data",
  15593. pUPnPDevice->GetOwningDevice());
  15594. }
  15595. #endif // DBG
  15596. //
  15597. // Forget all data received.
  15598. //
  15599. pUPnPDevice->ClearReceiveBuffer();
  15600. goto Exit;
  15601. } // CNATHelpUPnP::ReceiveUPnPDataStream
  15602. #undef DPF_MODNAME
  15603. #define DPF_MODNAME "CNATHelpUPnP::ParseUPnPHeaders"
  15604. //=============================================================================
  15605. // CNATHelpUPnP::ParseUPnPHeaders
  15606. //-----------------------------------------------------------------------------
  15607. //
  15608. // Description: Parses UPnP header information out of a message buffer.
  15609. //
  15610. // Arguments:
  15611. // char * pszMsg - Pointer to string containing the UPnP
  15612. // message. It will be modified.
  15613. // UPNP_HEADER_INFO * pHeaderInfo - Structure used to return parsing results.
  15614. //
  15615. // Returns: None.
  15616. //=============================================================================
  15617. void CNATHelpUPnP::ParseUPnPHeaders(char * const pszMsg,
  15618. UPNP_HEADER_INFO * pHeaderInfo)
  15619. {
  15620. char * pszCurrent;
  15621. char * pszLineStart;
  15622. char * pszHeaderDelimiter;
  15623. int i;
  15624. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  15625. this, pszMsg, pHeaderInfo);
  15626. //
  15627. // Loop until we reach the last SSDP header (indicated by a blank line).
  15628. //
  15629. pszCurrent = pszMsg;
  15630. pszLineStart = pszMsg;
  15631. do
  15632. {
  15633. //
  15634. // Find the end of the current line (CR LF).
  15635. //
  15636. while ((*pszCurrent) != '\n')
  15637. {
  15638. if ((*pszCurrent) == '\0')
  15639. {
  15640. //
  15641. // We hit the end of the buffer. Bail.
  15642. //
  15643. DPFX(DPFPREP, 1, "Hit end of buffer, parsing terminated.");
  15644. return;
  15645. }
  15646. pszCurrent++;
  15647. }
  15648. //
  15649. // Assuming this is the last header line, update the message body
  15650. // pointer to be after this line.
  15651. //
  15652. pHeaderInfo->pszMsgBody = pszCurrent + 1;
  15653. //
  15654. // If it's a valid line, then a CR will precede the LF we just found.
  15655. // If so, truncate the string there. If not, we'll just continue. The
  15656. // wacky newline in the middle of nowhere will probably just be
  15657. // ignored.
  15658. //
  15659. if ((pszCurrent > (pszMsg + 1)) &&
  15660. (*(pszCurrent - 1)) == '\r')
  15661. {
  15662. //
  15663. // Truncate the string at the effective end of the line (i.e.
  15664. // replace the CR with NULL terminator).
  15665. //
  15666. *(pszCurrent - 1) = '\0';
  15667. //
  15668. // If this is the empty line denoting the end of the headers, we're
  15669. // done here.
  15670. //
  15671. if (strlen(pszLineStart) == 0)
  15672. {
  15673. //
  15674. // Stop looping.
  15675. //
  15676. break;
  15677. }
  15678. }
  15679. else
  15680. {
  15681. //
  15682. // Truncate the string here.
  15683. //
  15684. (*pszCurrent) = '\0';
  15685. DPFX(DPFPREP, 9, "Line has a newline in it (offset 0x%p) that isn't preceded by a carriage return.",
  15686. (pszCurrent - pszMsg));
  15687. }
  15688. //
  15689. // Whitespace means continuation of previous line, so if this line
  15690. // starts that way, erase the termination for the previous line (unless
  15691. // this is the first line).
  15692. //
  15693. if (((*pszLineStart) == ' ') || ((*pszLineStart) == '\t'))
  15694. {
  15695. if (pszLineStart >= (pszMsg + 2))
  15696. {
  15697. //
  15698. // The previous line should have ended with {CR, LF}, which
  15699. // gets modified to {NULL termination, LF}.
  15700. //
  15701. if ((*(pszLineStart - 2) != '\0') ||
  15702. (*(pszLineStart - 1) != '\n'))
  15703. {
  15704. DPFX(DPFPREP, 7, "Ignoring line \"%hs\" because previous character sequence was not {NULL terminator, LF}.",
  15705. pszLineStart);
  15706. }
  15707. else
  15708. {
  15709. DPFX(DPFPREP, 7, "Appending line \"%hs\" to previous line.",
  15710. pszLineStart);
  15711. //
  15712. // Replace the NULL terminator/LF pair with spaces so
  15713. // future parsing sees the previous line and this one as
  15714. // one string.
  15715. //
  15716. *(pszLineStart - 2) = ' ';
  15717. *(pszLineStart - 1) = ' ';
  15718. }
  15719. }
  15720. else
  15721. {
  15722. DPFX(DPFPREP, 7, "Ignoring initial line \"%hs\" that starts with whitespace.",
  15723. pszLineStart);
  15724. }
  15725. }
  15726. //
  15727. // Find the colon separating the header.
  15728. //
  15729. pszHeaderDelimiter = strchr(pszLineStart, ':');
  15730. if (pszHeaderDelimiter != NULL)
  15731. {
  15732. //
  15733. // Truncate the string at the end of the header.
  15734. //
  15735. (*pszHeaderDelimiter) = '\0';
  15736. //
  15737. // Remove the white space surrounding the header name.
  15738. //
  15739. strtrim(&pszLineStart);
  15740. //
  15741. // Parse the header type.
  15742. //
  15743. for(i = 0; i < NUM_RESPONSE_HEADERS; i++)
  15744. {
  15745. if (_stricmp(c_szResponseHeaders[i], pszLineStart) == 0)
  15746. {
  15747. //
  15748. // Found the header. Save it if it's not a duplicate.
  15749. //
  15750. if (pHeaderInfo->apszHeaderStrings[i] == NULL)
  15751. {
  15752. char * pszTrimmedValue;
  15753. //
  15754. // Skip leading and trailing whitespace in the value.
  15755. //
  15756. pszTrimmedValue = pszHeaderDelimiter + 1;
  15757. strtrim(&pszTrimmedValue);
  15758. pHeaderInfo->apszHeaderStrings[i] = pszTrimmedValue;
  15759. DPFX(DPFPREP, 7, "Recognized header %i:\"%hs\", data = \"%hs\".",
  15760. i, pszLineStart, pHeaderInfo->apszHeaderStrings[i]);
  15761. }
  15762. else
  15763. {
  15764. DPFX(DPFPREP, 7, "Ignoring duplicate header %i:\"%hs\", data = \"%hs\".",
  15765. i, pszLineStart, (pszHeaderDelimiter + 1));
  15766. }
  15767. break;
  15768. }
  15769. }
  15770. #ifdef DBG
  15771. //
  15772. // Print unrecognized headers.
  15773. //
  15774. if (i >= NUM_RESPONSE_HEADERS)
  15775. {
  15776. DPFX(DPFPREP, 7, "Ignoring unrecognized header \"%hs\", data = \"%hs\".",
  15777. pszLineStart, (pszHeaderDelimiter + 1));
  15778. }
  15779. #endif // DBG
  15780. }
  15781. else
  15782. {
  15783. DPFX(DPFPREP, 7, "Ignoring line \"%hs\", no header delimiter.",
  15784. pszLineStart);
  15785. }
  15786. //
  15787. // Go to the next UPnP header (if any).
  15788. //
  15789. pszCurrent++;
  15790. pszLineStart = pszCurrent;
  15791. }
  15792. while (TRUE);
  15793. //
  15794. // At this point pHeaderInfo->apszHeaderStrings should contain pointers to
  15795. // the data for all the headers that were found, and
  15796. // pHeaderInfo->pszMsgBody should point to the end of the headers.
  15797. //
  15798. } // CNATHelpUPnP::ParseUPnPHeaders
  15799. #undef DPF_MODNAME
  15800. #define DPF_MODNAME "CNATHelpUPnP::GetAddressFromURL"
  15801. //=============================================================================
  15802. // CNATHelpUPnP::GetAddressFromURL
  15803. //-----------------------------------------------------------------------------
  15804. //
  15805. // Description: Parses a UPnP URL into a SOCKADDR_IN structure. Only "http://"
  15806. // URLs are parsed. The string passed in may be temporarily
  15807. // modified.
  15808. //
  15809. // Arguments:
  15810. // char * pszLocation - Pointer to buffer containing the Location
  15811. // header. It will be modified.
  15812. // SOCKADDR_IN * psaddrinLocation - Place to store address contained in
  15813. // header string.
  15814. // char ** ppszRelativePath - Place to store pointer to rest of path
  15815. // (stuff after hostname and optional
  15816. // port).
  15817. //
  15818. // Returns: HRESULT
  15819. // DPNH_OK - String was parsed successfully.
  15820. // DPNHERR_GENERIC - An error occurred.
  15821. //=============================================================================
  15822. HRESULT CNATHelpUPnP::GetAddressFromURL(char * const pszLocation,
  15823. SOCKADDR_IN * psaddrinLocation,
  15824. char ** ppszRelativePath)
  15825. {
  15826. HRESULT hr;
  15827. BOOL fModifiedDelimiterChar = FALSE;
  15828. char * pszStart;
  15829. char * pszDelimiter;
  15830. char cTempChar;
  15831. PHOSTENT phostent;
  15832. //
  15833. // Initialize the address. Default to the standard HTTP port.
  15834. //
  15835. ZeroMemory(psaddrinLocation, sizeof(SOCKADDR_IN));
  15836. psaddrinLocation->sin_family = AF_INET;
  15837. psaddrinLocation->sin_port = HTONS(HTTP_PORT);
  15838. //
  15839. // Clear the relative path pointer.
  15840. //
  15841. (*ppszRelativePath) = NULL;
  15842. //
  15843. // Skip past "http://". If it's not "http://", then fail.
  15844. //
  15845. if (_strnicmp(pszLocation, "http://", strlen("http://")) != 0)
  15846. {
  15847. DPFX(DPFPREP, 1, "Location URL (\"%hs\") does not start with \"http://\".",
  15848. pszLocation);
  15849. hr = DPNHERR_GENERIC;
  15850. goto Exit;
  15851. }
  15852. pszStart = pszLocation + strlen("http://");
  15853. //
  15854. // See if there's a port specified or any extraneous junk after an IP
  15855. // address or hostname to figure out the string to use. Search from the
  15856. // start of the string until we hit the end of the string or a reserved URL
  15857. // character.
  15858. //
  15859. pszDelimiter = pszStart + 1;
  15860. while (((*pszDelimiter) != '\0') &&
  15861. ((*pszDelimiter) != '/') &&
  15862. ((*pszDelimiter) != '?') &&
  15863. ((*pszDelimiter) != '=') &&
  15864. ((*pszDelimiter) != '#'))
  15865. {
  15866. if ((*pszDelimiter) == ':')
  15867. {
  15868. char * pszPortEnd;
  15869. //
  15870. // We found the start of a port, search for the end. It must
  15871. // contain only numeric characters.
  15872. //
  15873. pszPortEnd = pszDelimiter + 1;
  15874. while (((*pszPortEnd) >= '0') && ((*pszPortEnd) <= '9'))
  15875. {
  15876. pszPortEnd++;
  15877. }
  15878. //
  15879. // Temporarily truncate the string.
  15880. //
  15881. cTempChar = (*pszPortEnd);
  15882. (*pszPortEnd) = '\0';
  15883. DPFX(DPFPREP, 7, "Found port \"%hs\".", (pszDelimiter + 1));
  15884. psaddrinLocation->sin_port = HTONS((u_short) atoi(pszDelimiter + 1));
  15885. //
  15886. // Restore the character.
  15887. //
  15888. (*pszPortEnd) = cTempChar;
  15889. //
  15890. // Save the relative path
  15891. //
  15892. (*ppszRelativePath) = pszPortEnd;
  15893. break;
  15894. }
  15895. pszDelimiter++;
  15896. }
  15897. //
  15898. // Remember the character that stopped the search, and then temporarily
  15899. // truncate the string.
  15900. //
  15901. cTempChar = (*pszDelimiter);
  15902. (*pszDelimiter) = '\0';
  15903. fModifiedDelimiterChar = TRUE;
  15904. //
  15905. // Save the relative path if we haven't already (because of a port).
  15906. //
  15907. if ((*ppszRelativePath) == NULL)
  15908. {
  15909. (*ppszRelativePath) = pszDelimiter;
  15910. }
  15911. DPFX(DPFPREP, 7, "Relative path = \"%hs\".", (*ppszRelativePath));
  15912. //
  15913. // Convert the hostname.
  15914. //
  15915. psaddrinLocation->sin_addr.S_un.S_addr = this->m_pfninet_addr(pszStart);
  15916. //
  15917. // If it's bogus, give up.
  15918. //
  15919. if (psaddrinLocation->sin_addr.S_un.S_addr == INADDR_ANY)
  15920. {
  15921. DPFX(DPFPREP, 0, "Host name \"%hs\" is invalid!",
  15922. pszStart);
  15923. hr = DPNHERR_GENERIC;
  15924. goto Exit;
  15925. }
  15926. if (psaddrinLocation->sin_addr.S_un.S_addr == INADDR_NONE)
  15927. {
  15928. //
  15929. // It's not a straight IP address. Lookup the hostname.
  15930. //
  15931. phostent = this->m_pfngethostbyname(pszStart);
  15932. if (phostent == NULL)
  15933. {
  15934. DPFX(DPFPREP, 0, "Couldn't lookup host name \"%hs\"!",
  15935. pszStart);
  15936. hr = DPNHERR_GENERIC;
  15937. goto Exit;
  15938. }
  15939. if (phostent->h_addr_list[0] == NULL)
  15940. {
  15941. DPFX(DPFPREP, 0, "Host name \"%hs\" has no address entries!",
  15942. pszStart);
  15943. hr = DPNHERR_GENERIC;
  15944. goto Exit;
  15945. }
  15946. //
  15947. // Pick the first address returned.
  15948. //
  15949. #ifdef DBG
  15950. {
  15951. IN_ADDR ** ppinaddr;
  15952. DWORD dwNumAddrs;
  15953. ppinaddr = (IN_ADDR**) phostent->h_addr_list;
  15954. dwNumAddrs = 0;
  15955. while ((*ppinaddr) != NULL)
  15956. {
  15957. ppinaddr++;
  15958. dwNumAddrs++;
  15959. }
  15960. DPFX(DPFPREP, 7, "Picking first (of %u IP addresses) for \"%hs\".",
  15961. dwNumAddrs, pszStart);
  15962. }
  15963. #endif // DBG
  15964. psaddrinLocation->sin_addr.S_un.S_addr = ((IN_ADDR*) phostent->h_addr_list[0])->S_un.S_addr;
  15965. }
  15966. else
  15967. {
  15968. DPFX(DPFPREP, 7, "Successfully converted IP address \"%hs\".", pszStart);
  15969. }
  15970. hr = DPNH_OK;
  15971. Exit:
  15972. //
  15973. // If we found a port, restore the string. If not, use the default port.
  15974. //
  15975. if (fModifiedDelimiterChar)
  15976. {
  15977. //
  15978. // Note that PREfast reported this as being used before being
  15979. // initialized for a while. For some reason it didn't notice that I
  15980. // key off of fModifiedDelimiterChar. This appeared to get fixed, but
  15981. // PREfast is still giving me a false hit for a similar reason
  15982. // elsewhere.
  15983. //
  15984. (*pszDelimiter) = cTempChar;
  15985. }
  15986. DPFX(DPFPREP, 8, "Returning %u.%u.%u.%u:%u, hr = 0x%lx.",
  15987. psaddrinLocation->sin_addr.S_un.S_un_b.s_b1,
  15988. psaddrinLocation->sin_addr.S_un.S_un_b.s_b2,
  15989. psaddrinLocation->sin_addr.S_un.S_un_b.s_b3,
  15990. psaddrinLocation->sin_addr.S_un.S_un_b.s_b4,
  15991. NTOHS(psaddrinLocation->sin_port),
  15992. hr);
  15993. return hr;
  15994. } // CNATHelpUPnP::GetAddressFromURL
  15995. #undef DPF_MODNAME
  15996. #define DPF_MODNAME "CNATHelpUPnP::HandleUPnPDescriptionResponseBody"
  15997. //=============================================================================
  15998. // CNATHelpUPnP::HandleUPnPDescriptionResponseBody
  15999. //-----------------------------------------------------------------------------
  16000. //
  16001. // Description: Handles a UPnP device description response. The string will
  16002. // be modified.
  16003. //
  16004. // The UPnP device may get removed from list if a failure
  16005. // occurs, the caller needs to have a reference.
  16006. //
  16007. // The object lock is assumed to be held.
  16008. //
  16009. // Arguments:
  16010. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device being described.
  16011. // DWORD dwHTTPResponseCode - HTTP header response code.
  16012. // char * pszDescriptionXML - UPnP device description XML string.
  16013. //
  16014. // Returns: HRESULT
  16015. // DPNH_OK - Description response was handled successfully.
  16016. // DPNHERR_GENERIC - An error occurred.
  16017. //=============================================================================
  16018. HRESULT CNATHelpUPnP::HandleUPnPDescriptionResponseBody(CUPnPDevice * const pUPnPDevice,
  16019. const DWORD dwHTTPResponseCode,
  16020. char * const pszDescriptionXML)
  16021. {
  16022. HRESULT hr;
  16023. PARSEXML_SUBELEMENT aSubElements[MAX_NUM_DESCRIPTION_XML_SUBELEMENTS];
  16024. PARSEXML_ELEMENT ParseElement;
  16025. CDevice * pDevice;
  16026. CBilink * pBilink;
  16027. CRegisteredPort * pRegisteredPort;
  16028. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %u, 0x%p)",
  16029. this, pUPnPDevice, dwHTTPResponseCode, pszDescriptionXML);
  16030. //
  16031. // Make sure it was the success result.
  16032. //
  16033. if (dwHTTPResponseCode != 200)
  16034. {
  16035. DPFX(DPFPREP, 0, "Got error response %u from UPnP description request!",
  16036. dwHTTPResponseCode);
  16037. hr = DPNHERR_GENERIC;
  16038. goto Failure;
  16039. }
  16040. ZeroMemory(aSubElements, sizeof(aSubElements));
  16041. ZeroMemory(&ParseElement, sizeof(ParseElement));
  16042. ParseElement.papszElementStack = (char**) (&c_szElementStack_service);
  16043. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_service) / sizeof(char*);
  16044. ParseElement.paSubElements = (PARSEXML_SUBELEMENT*) (aSubElements);
  16045. ParseElement.dwMaxNumSubElements = MAX_NUM_DESCRIPTION_XML_SUBELEMENTS;
  16046. //ParseElement.dwNumSubElements = 0;
  16047. //ParseElement.fFoundMatchingElement = FALSE;
  16048. hr = this->ParseXML(pszDescriptionXML,
  16049. &ParseElement,
  16050. PARSECALLBACK_DESCRIPTIONRESPONSE,
  16051. pUPnPDevice);
  16052. if (hr != DPNH_OK)
  16053. {
  16054. DPFX(DPFPREP, 0, "Couldn't parse XML!");
  16055. goto Failure;
  16056. }
  16057. //
  16058. // If we did not find a WANIPConnection or WANPPPConnection service, then
  16059. // this response was not valid.
  16060. //
  16061. if (pUPnPDevice->GetServiceControlURL() == NULL)
  16062. {
  16063. DPFX(DPFPREP, 0, "Couldn't find WANIPConnection or WANPPPConnection service in XML description!");
  16064. hr = DPNHERR_GENERIC;
  16065. goto Failure;
  16066. }
  16067. //
  16068. // The UPnP device is now controllable.
  16069. //
  16070. pUPnPDevice->NoteReady();
  16071. //
  16072. // Find out what the device's external IP address is. Note that calling
  16073. // UpdateUPnPExternalAddress will overwrite the buffer containing the
  16074. // pszDescriptionXML string. That's fine, because we've saved all the
  16075. // stuff in there that we need already.
  16076. //
  16077. hr = this->UpdateUPnPExternalAddress(pUPnPDevice, FALSE);
  16078. if (hr != DPNH_OK)
  16079. {
  16080. DPFX(DPFPREP, 0, "Couldn't update new UPnP device 0x%p's external address!",
  16081. pUPnPDevice);
  16082. goto Failure;
  16083. }
  16084. //
  16085. // Map existing registered ports with this new UPnP device.
  16086. //
  16087. pDevice = pUPnPDevice->GetOwningDevice();
  16088. DNASSERT(pDevice != NULL);
  16089. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  16090. while (pBilink != &pDevice->m_blOwnedRegPorts)
  16091. {
  16092. DNASSERT(! pBilink->IsEmpty());
  16093. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  16094. //
  16095. // Note that calling MapPortsOnUPnPDevice will overwrite the buffer
  16096. // containing the pszDescriptionXML string. That's fine, because
  16097. // we've saved all the stuff in there that we need already.
  16098. //
  16099. hr = this->MapPortsOnUPnPDevice(pUPnPDevice, pRegisteredPort);
  16100. if (hr != DPNH_OK)
  16101. {
  16102. DPFX(DPFPREP, 0, "Couldn't map existing ports on new UPnP device 0x%p!",
  16103. pUPnPDevice);
  16104. goto Failure;
  16105. }
  16106. //
  16107. // Let the user know the addresses changed next time GetCaps is
  16108. // called.
  16109. //
  16110. DPFX(DPFPREP, 8, "Noting that addresses changed (for registered port 0x%p).",
  16111. pRegisteredPort);
  16112. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  16113. pBilink = pBilink->GetNext();
  16114. }
  16115. //
  16116. // Try to remove any mappings that were not freed earlier because we
  16117. // crashed.
  16118. //
  16119. hr = this->CleanupInactiveNATMappings(pUPnPDevice);
  16120. if (hr != DPNH_OK)
  16121. {
  16122. DPFX(DPFPREP, 0, "Failed cleaning up inactive mappings with new UPnP device 0x%p!",
  16123. pUPnPDevice);
  16124. goto Failure;
  16125. }
  16126. Exit:
  16127. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  16128. return hr;
  16129. Failure:
  16130. goto Exit;
  16131. } // CNATHelpUPnP::HandleUPnPDescriptionResponseBody
  16132. #undef DPF_MODNAME
  16133. #define DPF_MODNAME "CNATHelpUPnP::HandleUPnPControlResponseBody"
  16134. //=============================================================================
  16135. // CNATHelpUPnP::HandleUPnPControlResponseBody
  16136. //-----------------------------------------------------------------------------
  16137. //
  16138. // Description: Handles a UPnP control response. The string will be
  16139. // modified.
  16140. //
  16141. // The object lock is assumed to be held.
  16142. //
  16143. // Arguments:
  16144. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device being described.
  16145. // DWORD dwHTTPResponseCode - HTTP header response code.
  16146. // char * pszControlResponseSOAP - UPnP device response SOAP XML string.
  16147. //
  16148. // Returns: HRESULT
  16149. // DPNH_OK - Description response was handled successfully.
  16150. // DPNHERR_GENERIC - An error occurred.
  16151. //=============================================================================
  16152. HRESULT CNATHelpUPnP::HandleUPnPControlResponseBody(CUPnPDevice * const pUPnPDevice,
  16153. const DWORD dwHTTPResponseCode,
  16154. char * const pszControlResponseSOAP)
  16155. {
  16156. HRESULT hr = DPNH_OK;
  16157. CONTROLRESPONSEPARSECONTEXT crpc;
  16158. PARSEXML_SUBELEMENT aSubElements[MAX_NUM_UPNPCONTROLOUTARGS];
  16159. PARSEXML_ELEMENT ParseElement;
  16160. DNASSERT(pUPnPDevice->IsWaitingForControlResponse());
  16161. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %u, 0x%p)",
  16162. this, pUPnPDevice, dwHTTPResponseCode, pszControlResponseSOAP);
  16163. ZeroMemory(&crpc, sizeof(crpc));
  16164. crpc.ControlResponseType = pUPnPDevice->GetControlResponseType();
  16165. crpc.pUPnPDevice = pUPnPDevice;
  16166. crpc.dwHTTPResponseCode = dwHTTPResponseCode;
  16167. crpc.pControlResponseInfo = pUPnPDevice->GetControlResponseInfo();
  16168. ZeroMemory(aSubElements, sizeof(aSubElements));
  16169. ZeroMemory(&ParseElement, sizeof(ParseElement));
  16170. if (dwHTTPResponseCode == 200)
  16171. {
  16172. switch (crpc.ControlResponseType)
  16173. {
  16174. /*
  16175. case CONTROLRESPONSETYPE_QUERYSTATEVARIABLE_EXTERNALIPADDRESS:
  16176. {
  16177. ParseElement.papszElementStack = (char**) (&c_szElementStack_QueryStateVariableResponse);
  16178. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_QueryStateVariableResponse) / sizeof(char*);
  16179. break;
  16180. }
  16181. */
  16182. case CONTROLRESPONSETYPE_GETEXTERNALIPADDRESS:
  16183. {
  16184. ParseElement.papszElementStack = (char**) (&c_szElementStack_GetExternalIPAddressResponse);
  16185. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_GetExternalIPAddressResponse) / sizeof(char*);
  16186. break;
  16187. }
  16188. case CONTROLRESPONSETYPE_ADDPORTMAPPING:
  16189. {
  16190. ParseElement.papszElementStack = (char**) (&c_szElementStack_AddPortMappingResponse);
  16191. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_AddPortMappingResponse) / sizeof(char*);
  16192. break;
  16193. }
  16194. case CONTROLRESPONSETYPE_GETSPECIFICPORTMAPPINGENTRY:
  16195. {
  16196. ParseElement.papszElementStack = (char**) (&c_szElementStack_GetSpecificPortMappingEntryResponse);
  16197. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_GetSpecificPortMappingEntryResponse) / sizeof(char*);
  16198. break;
  16199. }
  16200. case CONTROLRESPONSETYPE_DELETEPORTMAPPING:
  16201. {
  16202. ParseElement.papszElementStack = (char**) (&c_szElementStack_DeletePortMappingResponse);
  16203. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_DeletePortMappingResponse) / sizeof(char*);
  16204. break;
  16205. }
  16206. default:
  16207. {
  16208. DNASSERT(FALSE);
  16209. hr = DPNHERR_GENERIC;
  16210. goto Failure;
  16211. break;
  16212. }
  16213. }
  16214. }
  16215. else
  16216. {
  16217. ParseElement.papszElementStack = (char**) (&c_szElementStack_ControlResponseFailure);
  16218. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_ControlResponseFailure) / sizeof(char*);
  16219. }
  16220. ParseElement.paSubElements = (PARSEXML_SUBELEMENT*) (aSubElements);
  16221. ParseElement.dwMaxNumSubElements = MAX_NUM_UPNPCONTROLOUTARGS;
  16222. //ParseElement.dwNumSubElements = 0;
  16223. //ParseElement.fFoundMatchingElement = FALSE;
  16224. hr = this->ParseXML(pszControlResponseSOAP,
  16225. &ParseElement,
  16226. PARSECALLBACK_CONTROLRESPONSE,
  16227. &crpc);
  16228. if (hr != DPNH_OK)
  16229. {
  16230. DPFX(DPFPREP, 0, "Couldn't parse XML!");
  16231. goto Failure;
  16232. }
  16233. //
  16234. // If we didn't a matching item, map it to a generic failure.
  16235. //
  16236. if (! ParseElement.fFoundMatchingElement)
  16237. {
  16238. if (dwHTTPResponseCode == 200)
  16239. {
  16240. DPFX(DPFPREP, 1, "Didn't find XML items in success response, mapping to generic failure.");
  16241. }
  16242. else
  16243. {
  16244. DPFX(DPFPREP, 1, "Didn't find failure XML items, using generic failure.");
  16245. }
  16246. crpc.pControlResponseInfo->hrErrorCode = DPNHERR_GENERIC;
  16247. }
  16248. pUPnPDevice->StopWaitingForControlResponse();
  16249. Exit:
  16250. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  16251. return hr;
  16252. Failure:
  16253. goto Exit;
  16254. } // CNATHelpUPnP::HandleUPnPControlResponseBody
  16255. #undef DPF_MODNAME
  16256. #define DPF_MODNAME "CNATHelpUPnP::ParseXML"
  16257. //=============================================================================
  16258. // CNATHelpUPnP::ParseXML
  16259. //-----------------------------------------------------------------------------
  16260. //
  16261. // Description: Parses an XML string for a specific element, and calls a
  16262. // helper function for each instance found.
  16263. //
  16264. // Subelement values cannot themselves contain subelements. If
  16265. // they do, the sub-subelements will be ignored.
  16266. //
  16267. // The string buffer is modified.
  16268. //
  16269. // Arguments:
  16270. // char * pszXML - XML string to parse.
  16271. // PARSEXML_ELEMENT * pParseElement - Pointer to element whose sub element
  16272. // values should be retrieved.
  16273. // PARSECALLBACK ParseCallback - Enum indicating what helper function
  16274. // to use.
  16275. // PVOID pvContext - Pointer to context value to pass to
  16276. // helper function.
  16277. //
  16278. // Returns: HRESULT
  16279. // DPNH_OK - Description response was handled successfully.
  16280. // DPNHERR_GENERIC - An error occurred.
  16281. //=============================================================================
  16282. HRESULT CNATHelpUPnP::ParseXML(char * const pszXML,
  16283. PARSEXML_ELEMENT * const pParseElement,
  16284. const PARSECALLBACK ParseCallback,
  16285. PVOID pvContext)
  16286. {
  16287. HRESULT hr = DPNH_OK;
  16288. PARSEXML_STACKENTRY aElementStack[MAX_XMLELEMENT_DEPTH];
  16289. DWORD dwCurrentElementDepth = 0;
  16290. char * pszElementTagStart = NULL;
  16291. BOOL fInElement = FALSE;
  16292. BOOL fEmptyElement = FALSE;
  16293. char * pszCurrent;
  16294. DWORD dwStackDepth;
  16295. PARSEXML_SUBELEMENT * pSubElement;
  16296. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  16297. this, pszXML, pParseElement);
  16298. //
  16299. // Need room for entire stack + at least one subelement level.
  16300. //
  16301. DNASSERT(pParseElement->dwElementStackDepth < MAX_XMLELEMENT_DEPTH);
  16302. #ifdef DBG
  16303. this->PrintUPnPTransactionToFile(pszXML,
  16304. strlen(pszXML),
  16305. "Inbound XML Body",
  16306. NULL);
  16307. #endif // DBG
  16308. //
  16309. // Loop through the XML looking for the given elements.
  16310. //
  16311. pszCurrent = pszXML;
  16312. while ((*pszCurrent) != '\0')
  16313. {
  16314. switch (*pszCurrent)
  16315. {
  16316. case '<':
  16317. {
  16318. //
  16319. // If we're in an element tag already, this is bogus XML or a
  16320. // CDATA section (which we don't handle). Fail.
  16321. //
  16322. if (pszElementTagStart != NULL)
  16323. {
  16324. DPFX(DPFPREP, 0, "Encountered '<' character in element tag, XML parsing failed.");
  16325. goto Failure;
  16326. }
  16327. //
  16328. // Truncate the string here in case this is the start of an
  16329. // element end tag. This delimits a value string.
  16330. //
  16331. (*pszCurrent) = '\0';
  16332. pszElementTagStart = pszCurrent + 1;
  16333. if ((*pszElementTagStart) == '\0')
  16334. {
  16335. DPFX(DPFPREP, 0, "Encountered '<' character at end of string, XML parsing failed.");
  16336. goto Failure;
  16337. }
  16338. break;
  16339. }
  16340. case '>':
  16341. {
  16342. //
  16343. // If we're not in an element tag, this is bogus XML or a CDATA
  16344. // section (which we don't handle). Fail.
  16345. //
  16346. if (pszElementTagStart == NULL)
  16347. {
  16348. DPFX(DPFPREP, 0, "Encountered '>' character outside of element tag, XML parsing failed.");
  16349. goto Failure;
  16350. }
  16351. //
  16352. // Truncate the string here.
  16353. //
  16354. (*pszCurrent) = '\0';
  16355. //
  16356. // This could either be a start or an end tag. If the first
  16357. // character of the tag is '/', then it's an end tag.
  16358. //
  16359. // Note that empty element tags begin by being parsed like a
  16360. // start tag, but then jump into the end tag clause.
  16361. //
  16362. if ((*pszElementTagStart) == '/')
  16363. {
  16364. pszElementTagStart++;
  16365. //
  16366. // Make sure the element tag stack is valid. The name of
  16367. // this end tag should match the start tag at the top of
  16368. // the stack. XML elements are case sensitive.
  16369. //
  16370. if (dwCurrentElementDepth == 0)
  16371. {
  16372. DPFX(DPFPREP, 0, "Encountered extra element end tag \"%hs\", XML parsing failed.",
  16373. pszElementTagStart);
  16374. goto Failure;
  16375. }
  16376. if (strcmp(pszElementTagStart, aElementStack[dwCurrentElementDepth - 1].pszName) != 0)
  16377. {
  16378. DPFX(DPFPREP, 0, "Encountered non-matching element end tag (\"%hs\" != \"%hs\"), XML parsing failed.",
  16379. pszElementTagStart,
  16380. aElementStack[dwCurrentElementDepth - 1].pszName);
  16381. goto Failure;
  16382. }
  16383. TagEnd:
  16384. //
  16385. // If we're here, then we have a complete element. If we
  16386. // were in the element, then it's either:
  16387. // the end of a sub-sub element,
  16388. // the end of a sub element, or
  16389. // the end of the element itself.
  16390. //
  16391. if (fInElement)
  16392. {
  16393. switch (dwCurrentElementDepth - pParseElement->dwElementStackDepth)
  16394. {
  16395. case 0:
  16396. {
  16397. //
  16398. // It's the end of the element. Call the
  16399. // helper function. Reuse fInElement as the
  16400. // fContinueParsing BOOL.
  16401. //
  16402. switch (ParseCallback)
  16403. {
  16404. case PARSECALLBACK_DESCRIPTIONRESPONSE:
  16405. {
  16406. hr = this->ParseXMLCallback_DescriptionResponse(pParseElement,
  16407. pvContext,
  16408. aElementStack,
  16409. &fInElement);
  16410. if (hr != DPNH_OK)
  16411. {
  16412. DPFX(DPFPREP, 0, "Description response parse helper function failed!");
  16413. goto Failure;
  16414. }
  16415. break;
  16416. }
  16417. case PARSECALLBACK_CONTROLRESPONSE:
  16418. {
  16419. hr = this->ParseXMLCallback_ControlResponse(pParseElement,
  16420. pvContext,
  16421. aElementStack,
  16422. &fInElement);
  16423. if (hr != DPNH_OK)
  16424. {
  16425. DPFX(DPFPREP, 0, "Control response parse helper function failed!");
  16426. goto Failure;
  16427. }
  16428. break;
  16429. }
  16430. default:
  16431. {
  16432. DNASSERT(FALSE);
  16433. break;
  16434. }
  16435. }
  16436. if (! fInElement)
  16437. {
  16438. DPFX(DPFPREP, 1, "Parse callback function discontinued parsing.");
  16439. goto Exit;
  16440. }
  16441. //
  16442. // Keep parsing, but we're no longer in the
  16443. // element. Reset the sub element counter in
  16444. // case we found entries.
  16445. //
  16446. fInElement = FALSE;
  16447. pParseElement->dwNumSubElements = 0;
  16448. break;
  16449. }
  16450. case 1:
  16451. {
  16452. //
  16453. // It's the end of a subelement. Complete this
  16454. // instance, if there's room.
  16455. //
  16456. if (pParseElement->dwNumSubElements < pParseElement->dwMaxNumSubElements)
  16457. {
  16458. pSubElement = &pParseElement->paSubElements[pParseElement->dwNumSubElements];
  16459. pSubElement->pszNameFound = pszElementTagStart;
  16460. pSubElement->dwNumAttributes = aElementStack[dwCurrentElementDepth - 1].dwNumAttributes;
  16461. if (pSubElement->dwNumAttributes > 0)
  16462. {
  16463. memcpy(pSubElement->apszAttributeNames,
  16464. aElementStack[dwCurrentElementDepth - 1].apszAttributeNames,
  16465. (pSubElement->dwNumAttributes * sizeof(char*)));
  16466. memcpy(pSubElement->apszAttributeValues,
  16467. aElementStack[dwCurrentElementDepth - 1].apszAttributeValues,
  16468. (pSubElement->dwNumAttributes * sizeof(char*)));
  16469. }
  16470. pSubElement->pszValueFound = aElementStack[dwCurrentElementDepth - 1].pszValue;
  16471. pParseElement->dwNumSubElements++;
  16472. DPFX(DPFPREP, 7, "Completed subelement instance #%u, name = \"%hs\", %u attributes, value = \"%hs\".",
  16473. pParseElement->dwNumSubElements,
  16474. pSubElement->pszNameFound,
  16475. pSubElement->dwNumAttributes,
  16476. pSubElement->pszValueFound);
  16477. }
  16478. else
  16479. {
  16480. DPFX(DPFPREP, 0, "Ignoring subelement instance \"%hs\" (%u attributes, value = \"%hs\"), no room in array.",
  16481. pszElementTagStart,
  16482. aElementStack[dwCurrentElementDepth - 1].dwNumAttributes,
  16483. aElementStack[dwCurrentElementDepth - 1].pszValue);
  16484. }
  16485. break;
  16486. }
  16487. default:
  16488. {
  16489. //
  16490. // It's the end of a sub-subelement.
  16491. //
  16492. DPFX(DPFPREP, 1, "Ignoring sub-sub element \"%hs\" (%u attributes, value = \"%hs\").",
  16493. pszElementTagStart,
  16494. aElementStack[dwCurrentElementDepth - 1].dwNumAttributes,
  16495. aElementStack[dwCurrentElementDepth - 1].pszValue);
  16496. break;
  16497. }
  16498. }
  16499. }
  16500. //
  16501. // Pop the element off the stack.
  16502. //
  16503. dwCurrentElementDepth--;
  16504. }
  16505. else
  16506. {
  16507. //
  16508. // It's not an end tag, but it might be an empty element
  16509. // (i.e. "<tag/>").
  16510. //
  16511. if (*(pszCurrent - 1) == '/')
  16512. {
  16513. //
  16514. // Truncate the string early.
  16515. //
  16516. *(pszCurrent - 1) = '\0';
  16517. //
  16518. // Remember this state so we can parse it properly.
  16519. //
  16520. fEmptyElement = TRUE;
  16521. DPFX(DPFPREP, 7, "XML element \"%hs\" is empty (i.e. is both a start and end tag).",
  16522. pszElementTagStart);
  16523. }
  16524. //
  16525. // Push the element on the tag stack, if there's room.
  16526. //
  16527. if (dwCurrentElementDepth >= MAX_XMLELEMENT_DEPTH)
  16528. {
  16529. DPFX(DPFPREP, 0, "Too many nested element tags (%u), XML parsing failed.",
  16530. dwCurrentElementDepth);
  16531. goto Failure;
  16532. }
  16533. aElementStack[dwCurrentElementDepth].pszName = pszElementTagStart;
  16534. //
  16535. // If there are attributes to this element, separate them
  16536. // into a different array. They will not be parsed,
  16537. // though.
  16538. // Attributes are delimited by whitespace.
  16539. //
  16540. while ((*pszElementTagStart) != '\0')
  16541. {
  16542. pszElementTagStart++;
  16543. //
  16544. // If it's whitespace, that's the end of the element
  16545. // name. Truncate the string and break out of the
  16546. // loops.
  16547. //
  16548. if (((*pszElementTagStart) == ' ') ||
  16549. ((*pszElementTagStart) == '\t') ||
  16550. ((*pszElementTagStart) == '\r') ||
  16551. ((*pszElementTagStart) == '\n'))
  16552. {
  16553. (*pszElementTagStart) = '\0';
  16554. pszElementTagStart++;
  16555. DPFX(DPFPREP, 8, "Attribute whitespace found at offset 0x%p, string length = %i.",
  16556. (pszElementTagStart - aElementStack[dwCurrentElementDepth].pszName),
  16557. strlen(pszElementTagStart));
  16558. break;
  16559. }
  16560. }
  16561. //
  16562. // If there weren't any attributes, pszElementTagStart will
  16563. // just point to an empty (but not NULL) string.
  16564. //
  16565. // So save the start of the value string.
  16566. //
  16567. aElementStack[dwCurrentElementDepth].pszValue = pszElementTagStart + strlen(pszElementTagStart) + 1;
  16568. //
  16569. // Then parse out the attributes.
  16570. //
  16571. this->ParseXMLAttributes(pszElementTagStart,
  16572. aElementStack[dwCurrentElementDepth].apszAttributeNames,
  16573. aElementStack[dwCurrentElementDepth].apszAttributeValues,
  16574. MAX_XMLNAMESPACES_PER_ELEMENT,
  16575. &(aElementStack[dwCurrentElementDepth].dwNumAttributes));
  16576. //
  16577. // The <?xml> tag is considered optional by this parser,
  16578. // and will be ignored.
  16579. //
  16580. if (_stricmp(aElementStack[dwCurrentElementDepth].pszName, "?xml") != 0)
  16581. {
  16582. //
  16583. // Bump the stack pointer.
  16584. //
  16585. dwCurrentElementDepth++;
  16586. //
  16587. // See if this the right element. If the stack depth
  16588. // isn't right, it can't be the desired item.
  16589. // Otherwise, make sure the stack matches.
  16590. //
  16591. if (dwCurrentElementDepth == pParseElement->dwElementStackDepth)
  16592. {
  16593. //
  16594. // Work through the entire element stack, making
  16595. // sure each name matches.
  16596. //
  16597. for(dwStackDepth = 0; dwStackDepth < dwCurrentElementDepth; dwStackDepth++)
  16598. {
  16599. if (! this->MatchesXMLStringWithoutNamespace(aElementStack[dwStackDepth].pszName,
  16600. pParseElement->papszElementStack[dwStackDepth],
  16601. aElementStack,
  16602. NULL,
  16603. (dwStackDepth + 1)))
  16604. {
  16605. //
  16606. // It didn't match. Stop looping.
  16607. //
  16608. break;
  16609. }
  16610. }
  16611. //
  16612. // If they all matched, we found the value desired.
  16613. //
  16614. if (dwStackDepth == dwCurrentElementDepth)
  16615. {
  16616. fInElement = TRUE;
  16617. DPFX(DPFPREP, 7, "Found requested element \"%hs\" at depth %u, has %u attributes.",
  16618. aElementStack[dwCurrentElementDepth - 1].pszName,
  16619. dwCurrentElementDepth,
  16620. aElementStack[dwCurrentElementDepth - 1].dwNumAttributes);
  16621. }
  16622. }
  16623. }
  16624. else
  16625. {
  16626. DPFX(DPFPREP, 7, "Ignoring element \"%hs\" at depth %u that has %u attributes.",
  16627. aElementStack[dwCurrentElementDepth].pszName,
  16628. dwCurrentElementDepth,
  16629. aElementStack[dwCurrentElementDepth].dwNumAttributes);
  16630. //
  16631. // If this assertion fails and it is an empty element,
  16632. // dwCurrentElementDepth will be off by -1 when we jump
  16633. // to TagEnd.
  16634. //
  16635. DNASSERT(! fInElement);
  16636. }
  16637. //
  16638. // If this is an empty element, go immediately to handling
  16639. // the tag closure.
  16640. //
  16641. if (fEmptyElement)
  16642. {
  16643. fEmptyElement = FALSE;
  16644. goto TagEnd;
  16645. }
  16646. }
  16647. //
  16648. // Search for another element tag.
  16649. //
  16650. pszElementTagStart = NULL;
  16651. break;
  16652. }
  16653. default:
  16654. {
  16655. //
  16656. // Ordinary character, continue.
  16657. //
  16658. break;
  16659. }
  16660. }
  16661. //
  16662. // Move to the next character
  16663. //
  16664. pszCurrent++;
  16665. }
  16666. Exit:
  16667. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  16668. return hr;
  16669. Failure:
  16670. hr = DPNHERR_GENERIC;
  16671. goto Exit;
  16672. } // CNATHelpUPnP::ParseXML
  16673. #undef DPF_MODNAME
  16674. #define DPF_MODNAME "CNATHelpUPnP::ParseXMLAttributes"
  16675. //=============================================================================
  16676. // CNATHelpUPnP::ParseXMLAttributes
  16677. //-----------------------------------------------------------------------------
  16678. //
  16679. // Description: Parses any XML attributes out of the given string. The input
  16680. // string buffer will be modified.
  16681. //
  16682. // Arguments:
  16683. // char * pszString - Pointer to attributes string to parse.
  16684. // This will be modified.
  16685. // char ** apszAttributeNames - Array in which to store attribute name
  16686. // string pointers.
  16687. // char ** apszAttributeValues - Matching array in which to store cor-
  16688. // responding attribute value strings.
  16689. // DWORD dwMaxNumAttributes - Maximum number of entries allowed in
  16690. // previous arrays.
  16691. // DWORD * pdwNumAttributes - Place to store number of attributes that
  16692. // were found.
  16693. //
  16694. // Returns: HRESULT
  16695. // DPNH_OK - Description response was handled successfully.
  16696. // DPNHERR_GENERIC - An error occurred.
  16697. //=============================================================================
  16698. void CNATHelpUPnP::ParseXMLAttributes(char * const pszString,
  16699. char ** const apszAttributeNames,
  16700. char ** const apszAttributeValues,
  16701. const DWORD dwMaxNumAttributes,
  16702. DWORD * const pdwNumAttributes)
  16703. {
  16704. char * pszStart;
  16705. char * pszCurrent;
  16706. char * pszEndOfString;
  16707. BOOL fInValueString = FALSE;
  16708. BOOL fInQuotes = FALSE;
  16709. BOOL fEmptyString = FALSE;
  16710. #ifdef EXTRA_PARSING_SPEW
  16711. DPFX(DPFPREP, 8, "(0x%p) Parameters: (\"%hs\", 0x%p, 0x%p, %u, 0x%p)",
  16712. this, pszString, apszAttributeNames, apszAttributeValues,
  16713. dwMaxNumAttributes, pdwNumAttributes);
  16714. #endif // EXTRA_PARSING_SPEW
  16715. //
  16716. // Start at the beginning with no entries.
  16717. //
  16718. (*pdwNumAttributes) = 0;
  16719. pszStart = pszString;
  16720. pszCurrent = pszStart;
  16721. pszEndOfString = pszString + strlen(pszString);
  16722. //
  16723. // Skip empty strings.
  16724. //
  16725. if (pszEndOfString == pszStart)
  16726. {
  16727. return;
  16728. }
  16729. //
  16730. // Loop through the entire string.
  16731. //
  16732. while (pszCurrent <= pszEndOfString)
  16733. {
  16734. switch (*pszCurrent)
  16735. {
  16736. case '=':
  16737. {
  16738. //
  16739. // If we're not in quotes or a value string, this is the end of
  16740. // the attribute name and the start of a value string.
  16741. //
  16742. if ((! fInQuotes) && (! fInValueString))
  16743. {
  16744. (*pszCurrent) = '\0';
  16745. apszAttributeNames[(*pdwNumAttributes)] = pszStart;
  16746. pszStart = pszCurrent + 1;
  16747. fInValueString = TRUE;
  16748. }
  16749. break;
  16750. }
  16751. case '\0':
  16752. case ' ':
  16753. case '\t':
  16754. case '\r':
  16755. case '\n':
  16756. {
  16757. //
  16758. // Whitespace or the end of the string. If we're not in
  16759. // quotes, that means it's the end of an attribute. Of course
  16760. // if it's the end of the string, then we force the end of the
  16761. // attribute/value.
  16762. //
  16763. if ((! fInQuotes) || ((*pszCurrent) == '\0'))
  16764. {
  16765. (*pszCurrent) = '\0';
  16766. if (fInValueString)
  16767. {
  16768. //
  16769. // End of the value string.
  16770. //
  16771. apszAttributeValues[(*pdwNumAttributes)] = pszStart;
  16772. fInValueString = FALSE;
  16773. DPFX(DPFPREP, 7, "Found attribute \"%hs\" with value \"%hs\".",
  16774. apszAttributeNames[(*pdwNumAttributes)],
  16775. apszAttributeValues[(*pdwNumAttributes)]);
  16776. }
  16777. else
  16778. {
  16779. //
  16780. // This may be another whitespace character immediately
  16781. // following a previous one. If so, ignore it. If
  16782. // not, save the attribute.
  16783. //
  16784. if (pszCurrent == pszStart)
  16785. {
  16786. fEmptyString = TRUE;
  16787. #ifdef EXTRA_PARSING_SPEW
  16788. DPFX(DPFPREP, 9, "Ignoring extra whitespace at offset 0x%p.",
  16789. (pszCurrent - pszString));
  16790. #endif // EXTRA_PARSING_SPEW
  16791. }
  16792. else
  16793. {
  16794. //
  16795. // End of the attribute. Force an empty value string.
  16796. //
  16797. apszAttributeNames[(*pdwNumAttributes)] = pszStart;
  16798. apszAttributeValues[(*pdwNumAttributes)] = pszCurrent;
  16799. DPFX(DPFPREP, 7, "Found attribute \"%hs\" with no value string.",
  16800. apszAttributeNames[(*pdwNumAttributes)]);
  16801. }
  16802. }
  16803. //
  16804. // Update the pointer for the start of the next attribute.
  16805. //
  16806. pszStart = pszCurrent + 1;
  16807. //
  16808. // Move to next attribute storage location, if this is not
  16809. // an empty string. If that was the last storage slot,
  16810. // we're done here.
  16811. //
  16812. if (fEmptyString)
  16813. {
  16814. fEmptyString = FALSE;
  16815. }
  16816. else
  16817. {
  16818. (*pdwNumAttributes)++;
  16819. if ((*pdwNumAttributes) >= dwMaxNumAttributes)
  16820. {
  16821. DPFX(DPFPREP, 1, "Maximum number of attributes reached, discontinuing attribute parsing.");
  16822. pszCurrent = pszEndOfString;
  16823. }
  16824. }
  16825. }
  16826. break;
  16827. }
  16828. case '"':
  16829. {
  16830. //
  16831. // Make sure it's not an escaped quote character.
  16832. //
  16833. if ((pszCurrent == pszString) || (*(pszCurrent - 1) != '\\'))
  16834. {
  16835. //
  16836. // Toggle the quote state.
  16837. //
  16838. if (fInQuotes)
  16839. {
  16840. //
  16841. // Force the string to terminate here so we skip the
  16842. // trailing quote character.
  16843. //
  16844. fInQuotes = FALSE;
  16845. (*pszCurrent) = '\0';
  16846. }
  16847. else
  16848. {
  16849. fInQuotes = TRUE;
  16850. //
  16851. // This should be the start of a (value) string. Skip
  16852. // this quote character.
  16853. //
  16854. if (pszCurrent == pszStart)
  16855. {
  16856. pszStart++;
  16857. }
  16858. else
  16859. {
  16860. DPFX(DPFPREP, 1, "Found starting quote that wasn't at the beginning of the string! Continuing.");
  16861. }
  16862. }
  16863. }
  16864. else
  16865. {
  16866. //
  16867. // It's an escaped quote character.
  16868. //
  16869. }
  16870. break;
  16871. }
  16872. default:
  16873. {
  16874. //
  16875. // Ignore the character and move on.
  16876. //
  16877. break;
  16878. }
  16879. }
  16880. //
  16881. // Move to next character.
  16882. //
  16883. pszCurrent++;
  16884. }
  16885. #ifdef EXTRA_PARSING_SPEW
  16886. DPFX(DPFPREP, 8, "(0x%p) Leave (found %u items)", this, (*pdwNumAttributes));
  16887. #endif // EXTRA_PARSING_SPEW
  16888. } // CNATHelpUPnP::ParseXMLNamespaceAttributes
  16889. #undef DPF_MODNAME
  16890. #define DPF_MODNAME "CNATHelpUPnP::MatchesXMLStringWithoutNamespace"
  16891. //=============================================================================
  16892. // CNATHelpUPnP::MatchesXMLStringWithoutNamespace
  16893. //-----------------------------------------------------------------------------
  16894. //
  16895. // Description: Determines whether the szCompareString matches szMatchString
  16896. // when all namespace prefixes in szCompareString are ignored.
  16897. // TRUE is returned if they do match, FALSE if not.
  16898. //
  16899. // Arguments:
  16900. // char * szCompareString - String that may contain namespace
  16901. // prefixes to be ignored.
  16902. // char * szMatchString - Shortened string without any
  16903. // namespace prefixes to be matched.
  16904. // PARSEXML_STACKENTRY * aElementStack - Array of nested elements whose
  16905. // attributes may define XML namespace
  16906. // aliases.
  16907. // PARSEXML_SUBELEMENT * pSubElement - Optional subelement entry with
  16908. // additional attributes to check.
  16909. // DWORD dwElementStackDepth - Number of entries in previous array.
  16910. //
  16911. // Returns: BOOL
  16912. //=============================================================================
  16913. BOOL CNATHelpUPnP::MatchesXMLStringWithoutNamespace(const char * const szCompareString,
  16914. const char * const szMatchString,
  16915. const PARSEXML_STACKENTRY * const aElementStack,
  16916. const PARSEXML_SUBELEMENT * const pSubElement,
  16917. const DWORD dwElementStackDepth)
  16918. {
  16919. BOOL fResult;
  16920. char * pszCompareStringNoNamespace;
  16921. //
  16922. // First, do a straight-forward string comparison.
  16923. //
  16924. if (_stricmp(szCompareString, szMatchString) == 0)
  16925. {
  16926. DPFX(DPFPREP, 7, "\"%hs\" exactly matches the short string.",
  16927. szCompareString);
  16928. fResult = TRUE;
  16929. }
  16930. else
  16931. {
  16932. //
  16933. // Skip past any namespace prefixes.
  16934. //
  16935. pszCompareStringNoNamespace = this->GetStringWithoutNamespacePrefix(szCompareString,
  16936. aElementStack,
  16937. pSubElement,
  16938. dwElementStackDepth);
  16939. DNASSERT((pszCompareStringNoNamespace >= szCompareString) && (pszCompareStringNoNamespace <= (szCompareString + strlen(szCompareString))));
  16940. //
  16941. // Now try comparing again, if we found any prefixes.
  16942. //
  16943. if (pszCompareStringNoNamespace > szCompareString)
  16944. {
  16945. if (_stricmp(pszCompareStringNoNamespace, szMatchString) == 0)
  16946. {
  16947. DPFX(DPFPREP, 7, "\"%hs\" matches the short string \"%hs\" starting at offset 0x%p.",
  16948. szCompareString, szMatchString,
  16949. (pszCompareStringNoNamespace - szCompareString));
  16950. fResult = TRUE;
  16951. }
  16952. else
  16953. {
  16954. DPFX(DPFPREP, 8, "\"%hs\" does not match the short string \"%hs\" (starting at offset 0x%p).",
  16955. szCompareString, szMatchString,
  16956. (pszCompareStringNoNamespace - szCompareString));
  16957. fResult = FALSE;
  16958. }
  16959. }
  16960. else
  16961. {
  16962. DPFX(DPFPREP, 8, "\"%hs\" does not have any namespace prefixes and does not match \"%hs\".",
  16963. szCompareString, szMatchString);
  16964. fResult = FALSE;
  16965. }
  16966. }
  16967. return fResult;
  16968. } // CNATHelpUPnP::MatchesXMLStringWithoutNamespace
  16969. #undef DPF_MODNAME
  16970. #define DPF_MODNAME "CNATHelpUPnP::GetStringWithoutNamespacePrefix"
  16971. //=============================================================================
  16972. // CNATHelpUPnP::GetStringWithoutNamespacePrefix
  16973. //-----------------------------------------------------------------------------
  16974. //
  16975. // Description: Returns a pointer to the first part of the string after any
  16976. // prefixes found. This will be the start of the string if none
  16977. // are found.
  16978. //
  16979. // Arguments:
  16980. // char * szString - String that may contain namespace
  16981. // prefixes to be skipped.
  16982. // PARSEXML_STACKENTRY * aElementStack - Array of nested elements whose
  16983. // attributes may define XML namespace
  16984. // aliases.
  16985. // PARSEXML_SUBELEMENT * pSubElement - Optional subelement entry with
  16986. // additional attributes to check.
  16987. // DWORD dwElementStackDepth - Number of entries in previous array.
  16988. //
  16989. // Returns: char *
  16990. //=============================================================================
  16991. char * CNATHelpUPnP::GetStringWithoutNamespacePrefix(const char * const szString,
  16992. const PARSEXML_STACKENTRY * const aElementStack,
  16993. const PARSEXML_SUBELEMENT * const pSubElement,
  16994. const DWORD dwElementStackDepth)
  16995. {
  16996. char * pszResult;
  16997. UINT uiXMLNSPrefixLength;
  16998. DWORD dwAttribute;
  16999. UINT uiNamespaceNameLength;
  17000. UINT uiNamespaceValueLength;
  17001. DWORD dwStackDepth;
  17002. //
  17003. // Store the prefix value since we use it frequently in this function.
  17004. //
  17005. uiXMLNSPrefixLength = strlen(XML_NAMESPACEDEFINITIONPREFIX);
  17006. //
  17007. // Search the stack for an XML namespace definition that matches. Start at
  17008. // the bottom and work up, since later definitions take precendence over
  17009. // earlier ones.
  17010. //
  17011. // In fact, if this is a subelement, there are attributes associated with
  17012. // this item that need to be checked, too. Do that first.
  17013. //
  17014. if (pSubElement != NULL)
  17015. {
  17016. //
  17017. // Search each attribute
  17018. //
  17019. for(dwAttribute = 0; dwAttribute < pSubElement->dwNumAttributes; dwAttribute++)
  17020. {
  17021. //
  17022. // Work with this attribute if it's a valid XML namespace
  17023. // definition. It needs to start with the prefix, plus have one
  17024. // extra character for the actual name.
  17025. //
  17026. uiNamespaceNameLength = strlen(pSubElement->apszAttributeNames[dwAttribute]);
  17027. if ((uiNamespaceNameLength >= (uiXMLNSPrefixLength + 1)) &&
  17028. (_strnicmp(pSubElement->apszAttributeNames[dwAttribute], XML_NAMESPACEDEFINITIONPREFIX, uiXMLNSPrefixLength) == 0))
  17029. {
  17030. uiNamespaceNameLength -= uiXMLNSPrefixLength;
  17031. //
  17032. // Only work with the item's value if it's valid.
  17033. //
  17034. uiNamespaceValueLength = strlen(pSubElement->apszAttributeValues[dwAttribute]);
  17035. if (uiNamespaceValueLength > 0)
  17036. {
  17037. //
  17038. // Okay, here's an item. See if the name prefixes the
  17039. // passed in string.
  17040. //
  17041. if (_strnicmp(szString, (pSubElement->apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength), uiNamespaceNameLength) == 0)
  17042. {
  17043. DPFX(DPFPREP, 8, "\"%hs\" begins with prefix \"%hs\" (subelement).",
  17044. szString,
  17045. (pSubElement->apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength));
  17046. //
  17047. // Cast to lose the const.
  17048. //
  17049. pszResult = ((char*) szString) + uiNamespaceNameLength;
  17050. //
  17051. // Skip the colon delimiter.
  17052. //
  17053. if ((*pszResult) == ':')
  17054. {
  17055. pszResult++;
  17056. }
  17057. else
  17058. {
  17059. DPFX(DPFPREP, 1, "\"%hs\" begins with prefix \"%hs\" but does not have colon separator (subelement)! Continuing.",
  17060. szString,
  17061. (pSubElement->apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength));
  17062. }
  17063. goto Exit;
  17064. }
  17065. //
  17066. // Namespace doesn't match
  17067. //
  17068. #ifdef EXTRA_PARSING_SPEW
  17069. DPFX(DPFPREP, 9, "\"%hs\" does not begin with prefix \"%hs\" (subelement).",
  17070. szString,
  17071. (pSubElement->apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength));
  17072. #endif // EXTRA_PARSING_SPEW
  17073. }
  17074. else
  17075. {
  17076. //
  17077. // Namespace value is bogus, ignore it.
  17078. //
  17079. DPFX(DPFPREP, 1, "Ignoring namespace definition \"%hs\" with empty value string (subelement).",
  17080. pSubElement->apszAttributeNames[dwAttribute]);
  17081. }
  17082. }
  17083. else
  17084. {
  17085. //
  17086. // Not an XML namespace definition.
  17087. //
  17088. #ifdef EXTRA_PARSING_SPEW
  17089. DPFX(DPFPREP, 9, "Attribute \"%hs\" is not a valid namespace definition (subelement).",
  17090. pSubElement->apszAttributeNames[dwAttribute]);
  17091. #endif // EXTRA_PARSING_SPEW
  17092. }
  17093. } // end for (each attribute)
  17094. }
  17095. else
  17096. {
  17097. //
  17098. // No subelement to check.
  17099. //
  17100. }
  17101. //
  17102. // Do the same thing for the all items above this one.
  17103. //
  17104. dwStackDepth = dwElementStackDepth;
  17105. while (dwStackDepth > 0)
  17106. {
  17107. dwStackDepth--;
  17108. //
  17109. // Search each attribute
  17110. //
  17111. for(dwAttribute = 0; dwAttribute < aElementStack[dwStackDepth].dwNumAttributes; dwAttribute++)
  17112. {
  17113. //
  17114. // Work with this attribute if it's a valid XML namespace
  17115. // definition. It needs to start with the prefix, plus have one
  17116. // extra character for the actual name.
  17117. //
  17118. uiNamespaceNameLength = strlen(aElementStack[dwStackDepth].apszAttributeNames[dwAttribute]);
  17119. if ((uiNamespaceNameLength >= (uiXMLNSPrefixLength + 1)) &&
  17120. (_strnicmp(aElementStack[dwStackDepth].apszAttributeNames[dwAttribute], XML_NAMESPACEDEFINITIONPREFIX, uiXMLNSPrefixLength) == 0))
  17121. {
  17122. uiNamespaceNameLength -= uiXMLNSPrefixLength;
  17123. //
  17124. // Only work with the item's value if it's valid.
  17125. //
  17126. uiNamespaceValueLength = strlen(aElementStack[dwStackDepth].apszAttributeValues[dwAttribute]);
  17127. if (uiNamespaceValueLength > 0)
  17128. {
  17129. //
  17130. // Okay, here's an item. See if the value prefixes the
  17131. // passed in string.
  17132. //
  17133. if (_strnicmp(szString, (aElementStack[dwStackDepth].apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength), uiNamespaceNameLength) == 0)
  17134. {
  17135. DPFX(DPFPREP, 8, "\"%hs\" begins with prefix \"%hs\" (stack depth %u).",
  17136. szString,
  17137. (aElementStack[dwStackDepth].apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength),
  17138. dwStackDepth);
  17139. //
  17140. // Cast to lose the const.
  17141. //
  17142. pszResult = ((char*) szString) + uiNamespaceNameLength;
  17143. //
  17144. // Skip the colon delimiter.
  17145. //
  17146. if ((*pszResult) == ':')
  17147. {
  17148. pszResult++;
  17149. }
  17150. else
  17151. {
  17152. DPFX(DPFPREP, 1, "\"%hs\" begins with prefix \"%hs\" but does not have colon separator (stack depth %u)! Continuing.",
  17153. szString,
  17154. (aElementStack[dwStackDepth].apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength),
  17155. dwStackDepth);
  17156. }
  17157. goto Exit;
  17158. }
  17159. //
  17160. // Namespace doesn't match
  17161. //
  17162. #ifdef EXTRA_PARSING_SPEW
  17163. DPFX(DPFPREP, 9, "\"%hs\" does not begin with prefix \"%hs\" (stack depth %u).",
  17164. szString,
  17165. (aElementStack[dwStackDepth].apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength),
  17166. dwStackDepth);
  17167. #endif // EXTRA_PARSING_SPEW
  17168. }
  17169. else
  17170. {
  17171. //
  17172. // Namespace value is bogus, ignore it.
  17173. //
  17174. DPFX(DPFPREP, 1, "Ignoring namespace definition \"%hs\" with empty value string (stack depth %u).",
  17175. aElementStack[dwStackDepth].apszAttributeNames[dwAttribute],
  17176. dwStackDepth);
  17177. }
  17178. }
  17179. else
  17180. {
  17181. //
  17182. // Not an XML namespace definition.
  17183. //
  17184. #ifdef EXTRA_PARSING_SPEW
  17185. DPFX(DPFPREP, 9, "Attribute \"%hs\" is not a valid namespace definition (stack depth %u).",
  17186. aElementStack[dwStackDepth].apszAttributeNames[dwAttribute],
  17187. dwStackDepth);
  17188. #endif // EXTRA_PARSING_SPEW
  17189. }
  17190. } // end for (each attribute)
  17191. }
  17192. //
  17193. // If we're here, it didn't match, even with namespace expansion.
  17194. //
  17195. DPFX(DPFPREP, 8, "\"%hs\" does not contain any namespace prefixes.",
  17196. szString);
  17197. //
  17198. // Cast to lose the const.
  17199. //
  17200. pszResult = (char*) szString;
  17201. Exit:
  17202. return pszResult;
  17203. } // CNATHelpUPnP::GetStringWithoutNamespacePrefix
  17204. #undef DPF_MODNAME
  17205. #define DPF_MODNAME "CNATHelpUPnP::GetNextChunk"
  17206. //=============================================================================
  17207. // CNATHelpUPnP::GetNextChunk
  17208. //-----------------------------------------------------------------------------
  17209. //
  17210. // Description: Attempts to parse the next chunk out of a message buffer.
  17211. // If invalid data is encountered, this function returns FALSE.
  17212. // If not enough data has been received to complete the chunk,
  17213. // TRUE is returned, but NULL is returned in ppszChunkData.
  17214. // Otherwise, a pointer to the start of the chunk data is placed
  17215. // in ppszChunkData, the size of the chunk is placed in
  17216. // pdwChunkSize, ppszBufferRemaining is set to the start of the
  17217. // next potential chunk, and pdwBufferSizeRemaining is set to the
  17218. // amount of the buffer remaining starting at the returned
  17219. // ppszBufferRemaining value.
  17220. //
  17221. // Note that the chunk size may be zero, in which case the
  17222. // pointer in ppszChunkData & ppszBufferRemaining will be non-
  17223. // NULL, but useless.
  17224. //
  17225. // Arguments:
  17226. // char * pszBuffer - Pointer to string containing the message
  17227. // received so far.
  17228. // DWORD dwBufferSize - Size of the message buffer.
  17229. // char ** ppszChunkData - Place to store pointer to chunk data, or
  17230. // NULL if full chunk is not available.
  17231. // DWORD * pdwChunkSize - Place to store size of chunk.
  17232. // char ** ppszBufferRemaining - Place to store pointer to end of chunk
  17233. // if full chunk is available.
  17234. // DWORD * pdwBufferSizeRemaining - Place to store size of buffer remaining
  17235. // after returned chunk.
  17236. //
  17237. // Returns: None.
  17238. //=============================================================================
  17239. BOOL CNATHelpUPnP::GetNextChunk(char * const pszBuffer,
  17240. const DWORD dwBufferSize,
  17241. char ** const ppszChunkData,
  17242. DWORD * const pdwChunkSize,
  17243. char ** const ppszBufferRemaining,
  17244. DWORD * const pdwBufferSizeRemaining)
  17245. {
  17246. BOOL fReturn = TRUE;
  17247. char * pszCurrent;
  17248. char * pszEndOfBuffer;
  17249. BOOL fFoundChunkSizeEnd;
  17250. BOOL fExtensions;
  17251. BOOL fInQuotedString;
  17252. DPFX(DPFPREP, 8, "(0x%p) Parameters: (0x%p, %u, 0x%p, 0x%p, 0x%p, 0x%p)",
  17253. this, pszBuffer, dwBufferSize, ppszChunkData, pdwChunkSize,
  17254. ppszBufferRemaining, pdwBufferSizeRemaining);
  17255. pszCurrent = pszBuffer;
  17256. pszEndOfBuffer = pszCurrent + dwBufferSize;
  17257. fFoundChunkSizeEnd = FALSE;
  17258. fExtensions = FALSE;
  17259. fInQuotedString = FALSE;
  17260. (*ppszChunkData) = NULL;
  17261. (*pdwChunkSize) = 0;
  17262. //
  17263. // The buffer must be large enough to hold 1 hex digit, CR LF chunk size
  17264. // terminator, and CR LF chunk trailer.
  17265. //
  17266. if (dwBufferSize < 5)
  17267. {
  17268. DPFX(DPFPREP, 3, "Buffer is not large enough (%u bytes) to hold one valid chunk.",
  17269. dwBufferSize);
  17270. goto Exit;
  17271. }
  17272. //
  17273. // Be paranoid to make sure we're not going to having wrap problems.
  17274. //
  17275. if (pszEndOfBuffer < pszCurrent)
  17276. {
  17277. DPFX(DPFPREP, 0, "Buffer pointer 0x%p cannot have size %u!",
  17278. pszBuffer, dwBufferSize);
  17279. goto Failure;
  17280. }
  17281. while (pszCurrent < pszEndOfBuffer)
  17282. {
  17283. //
  17284. // Make sure we have a valid hex chunk size string and convert it
  17285. // as we go.
  17286. //
  17287. if (((*pszCurrent) >= '0') && ((*pszCurrent) <= '9'))
  17288. {
  17289. (*pdwChunkSize) = ((*pdwChunkSize) * 16) + ((*pszCurrent) - '0');
  17290. }
  17291. else if (((*pszCurrent) >= 'a') && ((*pszCurrent) <= 'f'))
  17292. {
  17293. (*pdwChunkSize) = ((*pdwChunkSize) * 16) + ((*pszCurrent) - 'a' + 10);
  17294. }
  17295. else if (((*pszCurrent) >= 'A') && ((*pszCurrent) <= 'F'))
  17296. {
  17297. (*pdwChunkSize) = ((*pdwChunkSize) * 16) + ((*pszCurrent) - 'A' + 10);
  17298. }
  17299. else if ((*pszCurrent) == '\r')
  17300. {
  17301. //
  17302. // This should be the end of the chunk size string.
  17303. //
  17304. fFoundChunkSizeEnd = TRUE;
  17305. break;
  17306. }
  17307. else if ((*pszCurrent) == ';')
  17308. {
  17309. //
  17310. // This should be the end of the chunk size string, and the
  17311. // beginning of extensions. Loop until we find the true end.
  17312. //
  17313. while ((*pszCurrent) != '\r')
  17314. {
  17315. pszCurrent++;
  17316. if (pszCurrent >= pszEndOfBuffer)
  17317. {
  17318. DPFX(DPFPREP, 5, "Buffer stops in middle of chunk extension.");
  17319. goto Exit;
  17320. }
  17321. //
  17322. // We do not support quoted extension value strings that
  17323. // theoretically contain CR characters...
  17324. //
  17325. }
  17326. fFoundChunkSizeEnd = TRUE;
  17327. break;
  17328. }
  17329. else
  17330. {
  17331. //
  17332. // There's a bogus character. This can't be a validly encoded
  17333. // message.
  17334. //
  17335. DPFX(DPFPREP, 1, "Chunk size string contains invalid character 0x%x at offset %u!",
  17336. (*pszCurrent), (DWORD_PTR) (pszCurrent - pszBuffer));
  17337. goto Failure;
  17338. }
  17339. //
  17340. // Validate the chunk size we have so far.
  17341. //
  17342. if ((*pdwChunkSize) > MAX_RECEIVE_BUFFER_SIZE)
  17343. {
  17344. DPFX(DPFPREP, 1, "Chunk size %u is too large!",
  17345. (*pdwChunkSize));
  17346. goto Failure;
  17347. }
  17348. pszCurrent++;
  17349. }
  17350. //
  17351. // If we're here, see if we found the end of the chunk size string.
  17352. // Make sure we've received enough data, and then validate that the CR
  17353. // stopping character is followed by the LF character.
  17354. //
  17355. if (fFoundChunkSizeEnd)
  17356. {
  17357. pszCurrent++;
  17358. if (pszCurrent < pszEndOfBuffer)
  17359. {
  17360. if ((*pszCurrent) != '\n')
  17361. {
  17362. DPFX(DPFPREP, 1, "Chunk size string did not end with CRLF sequence (offset %u)!",
  17363. (DWORD_PTR) (pszCurrent - pszBuffer));
  17364. goto Failure;
  17365. }
  17366. //
  17367. // Otherwise we got a complete chunk size string.
  17368. //
  17369. pszCurrent++;
  17370. //
  17371. // If we have received all of the chunk already, make sure we have
  17372. // the trailing CR LF sequence before returning the pointer to the
  17373. // caller.
  17374. if (((*pdwChunkSize) + 2) <= ((DWORD_PTR) (pszEndOfBuffer - pszCurrent)))
  17375. {
  17376. if ((*(pszCurrent + (*pdwChunkSize)) != '\r') ||
  17377. (*(pszCurrent + (*pdwChunkSize) + 1) != '\n'))
  17378. {
  17379. DPFX(DPFPREP, 1, "Chunk data did not end with CRLF sequence (offset %u)!",
  17380. (DWORD_PTR) (pszCurrent - pszBuffer + (*pdwChunkSize)));
  17381. goto Failure;
  17382. }
  17383. //
  17384. // Return the data pointers to the caller. In the case of the
  17385. // zero size terminating chunk, the ppszChunkData pointer will
  17386. // actually be useless, but the caller should recognize that.
  17387. //
  17388. (*ppszChunkData) = pszCurrent;
  17389. (*ppszBufferRemaining) = pszCurrent + ((*pdwChunkSize) + 2);
  17390. (*pdwBufferSizeRemaining) = (DWORD) ((DWORD_PTR) (pszEndOfBuffer - (*ppszBufferRemaining)));
  17391. }
  17392. }
  17393. }
  17394. //
  17395. // If we're here, we didn't encounter invalid data.
  17396. //
  17397. Exit:
  17398. DPFX(DPFPREP, 8, "(0x%p) Returning: [%i]", this, fReturn);
  17399. return fReturn;
  17400. Failure:
  17401. fReturn = FALSE;
  17402. goto Exit;
  17403. } // CNATHelpUPnP::GetNextChunk
  17404. #undef DPF_MODNAME
  17405. #define DPF_MODNAME "CNATHelpUPnP::ParseXMLCallback_DescriptionResponse"
  17406. //=============================================================================
  17407. // CNATHelpUPnP::ParseXMLCallback_DescriptionResponse
  17408. //-----------------------------------------------------------------------------
  17409. //
  17410. // Description: Handles a completed parsed element in the description response
  17411. // XML.
  17412. //
  17413. // Arguments:
  17414. // PARSEXML_ELEMENT * pParseElement - Pointer to element which was found.
  17415. // PVOID pvContext - Pointer to parsing context.
  17416. // PARSEXML_STACKENTRY * aElementStack - Array of parent elements containing
  17417. // the completed element.
  17418. // BOOL * pfContinueParsing - Pointer to BOOL that should be set
  17419. // to FALSE if the calling function
  17420. // should stop parsing the XML.
  17421. //
  17422. // Returns: HRESULT
  17423. // DPNH_OK - Description response was handled successfully.
  17424. // DPNHERR_GENERIC - An error occurred.
  17425. //=============================================================================
  17426. HRESULT CNATHelpUPnP::ParseXMLCallback_DescriptionResponse(PARSEXML_ELEMENT * const pParseElement,
  17427. PVOID pvContext,
  17428. PARSEXML_STACKENTRY * const aElementStack,
  17429. BOOL * const pfContinueParsing)
  17430. {
  17431. HRESULT hr = DPNH_OK;
  17432. CUPnPDevice * pUPnPDevice;
  17433. char * pszServiceType = NULL;
  17434. char * pszServiceId = NULL;
  17435. char * pszControlURL = NULL;
  17436. DWORD dwSubElement;
  17437. PARSEXML_SUBELEMENT * pSubElement;
  17438. SOCKADDR_IN saddrinControl;
  17439. SOCKADDR_IN * psaddrinHost;
  17440. char * pszRelativePath;
  17441. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%p)",
  17442. this, pParseElement, pvContext, aElementStack, pfContinueParsing);
  17443. pUPnPDevice = (CUPnPDevice *) pvContext;
  17444. DNASSERT(pUPnPDevice != NULL);
  17445. DNASSERT(pParseElement->papszElementStack == (char **) (&c_szElementStack_service));
  17446. //
  17447. // Look for the subelements we want.
  17448. //
  17449. for(dwSubElement = 0; dwSubElement < pParseElement->dwNumSubElements; dwSubElement++)
  17450. {
  17451. pSubElement = &pParseElement->paSubElements[dwSubElement];
  17452. if (_stricmp(pSubElement->pszNameFound, XML_DEVICEDESCRIPTION_SERVICETYPE) == 0)
  17453. {
  17454. if (pszServiceType == NULL)
  17455. {
  17456. pszServiceType = pSubElement->pszValueFound;
  17457. }
  17458. else
  17459. {
  17460. DPFX(DPFPREP, 7, "Ignoring duplicate \"" XML_DEVICEDESCRIPTION_SERVICETYPE "\" subelement (value = \"%hs\").",
  17461. pSubElement->pszValueFound);
  17462. }
  17463. }
  17464. else if (_stricmp(pSubElement->pszNameFound, XML_DEVICEDESCRIPTION_SERVICEID) == 0)
  17465. {
  17466. if (pszServiceId == NULL)
  17467. {
  17468. pszServiceId = pSubElement->pszValueFound;
  17469. }
  17470. else
  17471. {
  17472. DPFX(DPFPREP, 7, "Ignoring duplicate \"" XML_DEVICEDESCRIPTION_SERVICEID "\" subelement (value = \"%hs\").",
  17473. pSubElement->pszValueFound);
  17474. }
  17475. }
  17476. else if (_stricmp(pSubElement->pszNameFound, XML_DEVICEDESCRIPTION_CONTROLURL) == 0)
  17477. {
  17478. if (pszControlURL == NULL)
  17479. {
  17480. pszControlURL = pSubElement->pszValueFound;
  17481. }
  17482. else
  17483. {
  17484. DPFX(DPFPREP, 7, "Ignoring duplicate \"" XML_DEVICEDESCRIPTION_CONTROLURL "\" subelement (value = \"%hs\").",
  17485. pSubElement->pszValueFound);
  17486. }
  17487. }
  17488. else
  17489. {
  17490. DPFX(DPFPREP, 7, "Ignoring subelement \"%hs\" (value = \"%hs\").",
  17491. pSubElement->pszNameFound, pSubElement->pszValueFound);
  17492. }
  17493. }
  17494. //
  17495. // If one of those elements was not specified, then this element is not
  17496. // helpful.
  17497. //
  17498. if ((pszServiceType == NULL) || (pszServiceId == NULL) || (pszControlURL == NULL))
  17499. {
  17500. DPFX(DPFPREP, 1, "Couldn't find either \"" XML_DEVICEDESCRIPTION_SERVICETYPE "\", \"" XML_DEVICEDESCRIPTION_SERVICEID "\", or \"" XML_DEVICEDESCRIPTION_CONTROLURL "\" in XML description, ignoring element.");
  17501. goto Exit;
  17502. }
  17503. //
  17504. // If the service type is not one of the ones we want, ignore the element.
  17505. //
  17506. if (_stricmp(pszServiceType, URI_SERVICE_WANIPCONNECTION_A) == 0)
  17507. {
  17508. DPFX(DPFPREP, 7, "Found \"" URI_SERVICE_WANIPCONNECTION_A "\".");
  17509. DNASSERT(! pUPnPDevice->IsWANPPPConnection());
  17510. }
  17511. else if (_stricmp(pszServiceType, URI_SERVICE_WANPPPCONNECTION_A) == 0)
  17512. {
  17513. DPFX(DPFPREP, 7, "Found \"" URI_SERVICE_WANPPPCONNECTION_A "\".");
  17514. pUPnPDevice->NoteWANPPPConnection();
  17515. }
  17516. else
  17517. {
  17518. DPFX(DPFPREP, 1, "Ignoring unknown service type \"%hs\".", pszServiceType);
  17519. goto Exit;
  17520. }
  17521. pParseElement->fFoundMatchingElement = TRUE;
  17522. (*pfContinueParsing) = FALSE;
  17523. //
  17524. // Validate and store the service control URL.
  17525. //
  17526. hr = this->GetAddressFromURL(pszControlURL,
  17527. &saddrinControl,
  17528. &pszRelativePath);
  17529. if (hr != DPNH_OK)
  17530. {
  17531. psaddrinHost = pUPnPDevice->GetHostAddress();
  17532. DPFX(DPFPREP, 1, "No control address in URL, using host address %u.%u.%u.%u:%u and full URL as relative path (\"%hs\").",
  17533. psaddrinHost->sin_addr.S_un.S_un_b.s_b1,
  17534. psaddrinHost->sin_addr.S_un.S_un_b.s_b2,
  17535. psaddrinHost->sin_addr.S_un.S_un_b.s_b3,
  17536. psaddrinHost->sin_addr.S_un.S_un_b.s_b4,
  17537. NTOHS(psaddrinHost->sin_port),
  17538. pszControlURL);
  17539. memcpy(&saddrinControl, psaddrinHost, sizeof(SOCKADDR_IN));
  17540. pszRelativePath = pszControlURL;
  17541. }
  17542. else
  17543. {
  17544. #if 0
  17545. //
  17546. // Ensure that the address to use is local. It doesn't make sense that
  17547. // in order to make mappings for our private network we would need to
  17548. // contact something outside.
  17549. //
  17550. if (! this->IsAddressLocal(pUPnPDevice->GetOwningDevice(), &saddrinControl))
  17551. {
  17552. DPFX(DPFPREP, 1, "Control address designated (%u.%u.%u.%u:%u) is not local, ignoring message.",
  17553. saddrinControl.sin_addr.S_un.S_un_b.s_b1,
  17554. saddrinControl.sin_addr.S_un.S_un_b.s_b2,
  17555. saddrinControl.sin_addr.S_un.S_un_b.s_b3,
  17556. saddrinControl.sin_addr.S_un.S_un_b.s_b4,
  17557. NTOHS(saddrinControl.sin_port));
  17558. goto Exit;
  17559. }
  17560. #else
  17561. psaddrinHost = pUPnPDevice->GetHostAddress();
  17562. //
  17563. // Don't accept responses that refer to addresses other than the one
  17564. // that sent this response.
  17565. //
  17566. if (saddrinControl.sin_addr.S_un.S_addr != psaddrinHost->sin_addr.S_un.S_addr)
  17567. {
  17568. DPFX(DPFPREP, 1, "Control IP address designated (%u.%u.%u.%u:%u) is not the same as host IP address (%u.%u.%u.%u:%u), ignoring message.",
  17569. saddrinControl.sin_addr.S_un.S_un_b.s_b1,
  17570. saddrinControl.sin_addr.S_un.S_un_b.s_b2,
  17571. saddrinControl.sin_addr.S_un.S_un_b.s_b3,
  17572. saddrinControl.sin_addr.S_un.S_un_b.s_b4,
  17573. NTOHS(saddrinControl.sin_port),
  17574. psaddrinHost->sin_addr.S_un.S_un_b.s_b1,
  17575. psaddrinHost->sin_addr.S_un.S_un_b.s_b2,
  17576. psaddrinHost->sin_addr.S_un.S_un_b.s_b3,
  17577. psaddrinHost->sin_addr.S_un.S_un_b.s_b4,
  17578. NTOHS(psaddrinHost->sin_port));
  17579. goto Exit;
  17580. }
  17581. #endif
  17582. //
  17583. // Don't accept responses that refer to ports in the reserved range
  17584. // (less than or equal to 1024) other than the standard HTTP port.
  17585. //
  17586. if ((NTOHS(saddrinControl.sin_port) <= MAX_RESERVED_PORT) &&
  17587. (saddrinControl.sin_port != HTONS(HTTP_PORT)))
  17588. {
  17589. DPFX(DPFPREP, 1, "Control address designated invalid port %u, ignoring message.",
  17590. NTOHS(saddrinControl.sin_port));
  17591. goto Exit;
  17592. }
  17593. }
  17594. pUPnPDevice->SetControlAddress(&saddrinControl);
  17595. //
  17596. // Save the service control URL.
  17597. //
  17598. hr = pUPnPDevice->SetServiceControlURL(pszRelativePath);
  17599. if (hr != DPNH_OK)
  17600. {
  17601. DPFX(DPFPREP, 0, "Couldn't store service control URL!");
  17602. goto Failure;
  17603. }
  17604. Exit:
  17605. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  17606. return hr;
  17607. Failure:
  17608. hr = DPNHERR_GENERIC;
  17609. goto Exit;
  17610. } // CNATHelpUPnP::ParseXMLCallback_DescriptionResponse
  17611. #undef DPF_MODNAME
  17612. #define DPF_MODNAME "CNATHelpUPnP::ParseXMLCallback_ControlResponse"
  17613. //=============================================================================
  17614. // CNATHelpUPnP::ParseXMLCallback_ControlResponse
  17615. //-----------------------------------------------------------------------------
  17616. //
  17617. // Description: Handles a completed parsed element in a control SOAP response.
  17618. //
  17619. // Arguments:
  17620. // PARSEXML_ELEMENT * pParseElement - Pointer to element which was found.
  17621. // PVOID pvContext - Pointer to parsing context.
  17622. // PARSEXML_STACKENTRY * aElementStack - Array of parent elements containing
  17623. // the completed element.
  17624. // BOOL * pfContinueParsing - Pointer to BOOL that should be set
  17625. // to FALSE if the calling function
  17626. // should stop parsing the XML.
  17627. //
  17628. // Returns: HRESULT
  17629. // DPNH_OK - Description response was handled successfully.
  17630. // DPNHERR_GENERIC - An error occurred.
  17631. //=============================================================================
  17632. HRESULT CNATHelpUPnP::ParseXMLCallback_ControlResponse(PARSEXML_ELEMENT * const pParseElement,
  17633. PVOID pvContext,
  17634. PARSEXML_STACKENTRY * const aElementStack,
  17635. BOOL * const pfContinueParsing)
  17636. {
  17637. HRESULT hr = DPNH_OK;
  17638. PCONTROLRESPONSEPARSECONTEXT pContext;
  17639. //char * pszReturn = NULL;
  17640. char * pszExternalIPAddress = NULL;
  17641. char * pszInternalPort = NULL;
  17642. char * pszInternalClient = NULL;
  17643. char * pszEnabled = NULL;
  17644. char * pszPortMappingDescription = NULL;
  17645. char * pszLeaseDuration = NULL;
  17646. char * pszErrorCode = NULL;
  17647. char * pszErrorDescription = NULL;
  17648. DWORD dwSubElement;
  17649. PARSEXML_SUBELEMENT * pSubElement;
  17650. int iErrorCode;
  17651. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%p)",
  17652. this, pParseElement, pvContext, aElementStack, pfContinueParsing);
  17653. pContext = (PCONTROLRESPONSEPARSECONTEXT) pvContext;
  17654. DNASSERT(pContext != NULL);
  17655. DNASSERT(pContext->pUPnPDevice != NULL);
  17656. //
  17657. // Look for the subelements we want.
  17658. //
  17659. for(dwSubElement = 0; dwSubElement < pParseElement->dwNumSubElements; dwSubElement++)
  17660. {
  17661. pSubElement = &pParseElement->paSubElements[dwSubElement];
  17662. /*
  17663. if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17664. ARG_CONTROL_RETURN_A,
  17665. aElementStack,
  17666. pSubElement,
  17667. pParseElement->dwElementStackDepth))
  17668. {
  17669. if (pszReturn == NULL)
  17670. {
  17671. pszReturn = pSubElement->pszValueFound;
  17672. }
  17673. else
  17674. {
  17675. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_CONTROL_RETURN_A "\" subelement (value = \"%hs\").",
  17676. pSubElement->pszValueFound);
  17677. }
  17678. }
  17679. */
  17680. if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17681. ARG_GETEXTERNALIPADDRESS_NEWEXTERNALIPADDRESS_A,
  17682. aElementStack,
  17683. pSubElement,
  17684. pParseElement->dwElementStackDepth))
  17685. {
  17686. if (pszExternalIPAddress == NULL)
  17687. {
  17688. pszExternalIPAddress = pSubElement->pszValueFound;
  17689. }
  17690. else
  17691. {
  17692. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETEXTERNALIPADDRESS_NEWEXTERNALIPADDRESS_A "\" subelement (value = \"%hs\").",
  17693. pSubElement->pszValueFound);
  17694. }
  17695. }
  17696. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17697. ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALPORT_A,
  17698. aElementStack,
  17699. pSubElement,
  17700. pParseElement->dwElementStackDepth))
  17701. {
  17702. if (pszInternalPort == NULL)
  17703. {
  17704. pszInternalPort = pSubElement->pszValueFound;
  17705. }
  17706. else
  17707. {
  17708. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALPORT_A "\" subelement (value = \"%hs\").",
  17709. pSubElement->pszValueFound);
  17710. }
  17711. }
  17712. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17713. ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALCLIENT_A,
  17714. aElementStack,
  17715. pSubElement,
  17716. pParseElement->dwElementStackDepth))
  17717. {
  17718. if (pszInternalClient == NULL)
  17719. {
  17720. pszInternalClient = pSubElement->pszValueFound;
  17721. }
  17722. else
  17723. {
  17724. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALCLIENT_A "\" subelement (value = \"%hs\").",
  17725. pSubElement->pszValueFound);
  17726. }
  17727. }
  17728. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17729. ARG_GETSPECIFICPORTMAPPINGENTRY_NEWENABLED_A,
  17730. aElementStack,
  17731. pSubElement,
  17732. pParseElement->dwElementStackDepth))
  17733. {
  17734. if (pszEnabled == NULL)
  17735. {
  17736. pszEnabled = pSubElement->pszValueFound;
  17737. }
  17738. else
  17739. {
  17740. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWENABLED_A "\" subelement (value = \"%hs\").",
  17741. pSubElement->pszValueFound);
  17742. }
  17743. }
  17744. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17745. ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPORTMAPPINGDESCRIPTION_A,
  17746. aElementStack,
  17747. pSubElement,
  17748. pParseElement->dwElementStackDepth))
  17749. {
  17750. if (pszPortMappingDescription == NULL)
  17751. {
  17752. pszPortMappingDescription = pSubElement->pszValueFound;
  17753. }
  17754. else
  17755. {
  17756. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPORTMAPPINGDESCRIPTION_A "\" subelement (value = \"%hs\").",
  17757. pSubElement->pszValueFound);
  17758. }
  17759. }
  17760. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17761. ARG_GETSPECIFICPORTMAPPINGENTRY_NEWLEASEDURATION_A,
  17762. aElementStack,
  17763. pSubElement,
  17764. pParseElement->dwElementStackDepth))
  17765. {
  17766. if (pszLeaseDuration == NULL)
  17767. {
  17768. pszLeaseDuration = pSubElement->pszValueFound;
  17769. }
  17770. else
  17771. {
  17772. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWLEASEDURATION_A "\" subelement (value = \"%hs\").",
  17773. pSubElement->pszValueFound);
  17774. }
  17775. }
  17776. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17777. ARG_CONTROL_ERROR_ERRORCODE_A,
  17778. aElementStack,
  17779. pSubElement,
  17780. pParseElement->dwElementStackDepth))
  17781. {
  17782. if (pszErrorCode == NULL)
  17783. {
  17784. pszErrorCode = pSubElement->pszValueFound;
  17785. }
  17786. else
  17787. {
  17788. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_CONTROL_ERROR_ERRORCODE_A "\" subelement (value = \"%hs\").",
  17789. pSubElement->pszValueFound);
  17790. }
  17791. }
  17792. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17793. ARG_CONTROL_ERROR_ERRORDESCRIPTION_A,
  17794. aElementStack,
  17795. pSubElement,
  17796. pParseElement->dwElementStackDepth))
  17797. {
  17798. if (pszErrorDescription == NULL)
  17799. {
  17800. pszErrorDescription = pSubElement->pszValueFound;
  17801. }
  17802. else
  17803. {
  17804. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_CONTROL_ERROR_ERRORDESCRIPTION_A "\" subelement (value = \"%hs\").",
  17805. pSubElement->pszValueFound);
  17806. }
  17807. }
  17808. else
  17809. {
  17810. DPFX(DPFPREP, 7, "Ignoring subelement \"%hs\" (value = \"%hs\").",
  17811. pSubElement->pszNameFound, pSubElement->pszValueFound);
  17812. }
  17813. } // end for (each sub element)
  17814. if (pContext->dwHTTPResponseCode == 200)
  17815. {
  17816. //
  17817. // The action succeeded.
  17818. //
  17819. switch (pContext->ControlResponseType)
  17820. {
  17821. /*
  17822. case CONTROLRESPONSETYPE_QUERYSTATEVARIABLE_EXTERNALIPADDRESS:
  17823. {
  17824. if (pszReturn == NULL)
  17825. {
  17826. DPFX(DPFPREP, 1, "Couldn't find \"" ARG_CONTROL_RETURN_A "\" in SOAP response, ignoring element.");
  17827. goto Exit;
  17828. }
  17829. DPFX(DPFPREP, 2, "QueryStateVariable returned \"%hs\".",
  17830. pszReturn);
  17831. / *
  17832. //
  17833. // Key off of the variable we were querying.
  17834. //
  17835. switch (pContext->ControlResponseType)
  17836. {
  17837. case CONTROLRESPONSETYPE_QUERYSTATEVARIABLE_EXTERNALIPADDRESS:
  17838. {
  17839. * /
  17840. pContext->pControlResponseInfo->dwExternalIPAddressV4 = this->m_pfninet_addr(pszReturn);
  17841. if (pContext->pControlResponseInfo->dwExternalIPAddressV4 == INADDR_NONE)
  17842. {
  17843. DPFX(DPFPREP, 1, "External IP address string \"%hs\" is invalid, using INADDR_ANY.");
  17844. pContext->pControlResponseInfo->dwExternalIPAddressV4 = INADDR_ANY;
  17845. }
  17846. / *
  17847. break;
  17848. }
  17849. }
  17850. * /
  17851. break;
  17852. }
  17853. */
  17854. case CONTROLRESPONSETYPE_GETEXTERNALIPADDRESS:
  17855. {
  17856. if (pszExternalIPAddress == NULL)
  17857. {
  17858. DPFX(DPFPREP, 1, "Couldn't find \"" ARG_GETEXTERNALIPADDRESS_NEWEXTERNALIPADDRESS_A "\" in SOAP response, ignoring element.");
  17859. goto Exit;
  17860. }
  17861. DPFX(DPFPREP, 2, "GetExternalIPAddress returned \"%hs\".",
  17862. pszExternalIPAddress);
  17863. pContext->pControlResponseInfo->dwExternalIPAddressV4 = this->m_pfninet_addr(pszExternalIPAddress);
  17864. if ((pContext->pControlResponseInfo->dwExternalIPAddressV4 == INADDR_NONE) ||
  17865. (IS_CLASSD_IPV4_ADDRESS(pContext->pControlResponseInfo->dwExternalIPAddressV4)))
  17866. {
  17867. DPFX(DPFPREP, 1, "External IP address string \"%hs\" is invalid, using INADDR_ANY.");
  17868. pContext->pControlResponseInfo->dwExternalIPAddressV4 = INADDR_ANY;
  17869. }
  17870. break;
  17871. }
  17872. case CONTROLRESPONSETYPE_ADDPORTMAPPING:
  17873. {
  17874. DPFX(DPFPREP, 2, "AddPortMapping got success response.");
  17875. break;
  17876. }
  17877. case CONTROLRESPONSETYPE_GETSPECIFICPORTMAPPINGENTRY:
  17878. {
  17879. /*
  17880. if ((pszInternalPort == NULL) ||
  17881. (pszInternalClient == NULL) ||
  17882. (pszEnabled == NULL) ||
  17883. (pszPortMappingDescription == NULL) ||
  17884. (pszLeaseDuration == NULL))
  17885. */
  17886. if (pszInternalClient == NULL)
  17887. {
  17888. DPFX(DPFPREP, 1, "Couldn't find \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALCLIENT_A "\" in SOAP response, ignoring element.");
  17889. goto Exit;
  17890. }
  17891. if (pszInternalPort == NULL)
  17892. {
  17893. DPFX(DPFPREP, 1, "Couldn't find \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALPORT_A "\" in SOAP response, assuming asymmetric mappings are not supported.");
  17894. pszInternalPort = "0";
  17895. }
  17896. DPFX(DPFPREP, 2, "GetPortMappingPrivateIP returned \"%hs:%hs\".",
  17897. pszInternalClient, pszInternalPort);
  17898. pContext->pControlResponseInfo->dwInternalClientV4 = this->m_pfninet_addr(pszInternalClient);
  17899. if (pContext->pControlResponseInfo->dwInternalClientV4 == INADDR_ANY)
  17900. {
  17901. DPFX(DPFPREP, 0, "Internal client address is INADDR_ANY!");
  17902. hr = DPNHERR_GENERIC;
  17903. goto Failure;
  17904. }
  17905. pContext->pControlResponseInfo->wInternalPort = HTONS((WORD) atoi(pszInternalPort));
  17906. break;
  17907. }
  17908. case CONTROLRESPONSETYPE_DELETEPORTMAPPING:
  17909. {
  17910. DPFX(DPFPREP, 2, "DeletePortMapping got success response.");
  17911. break;
  17912. }
  17913. default:
  17914. {
  17915. DNASSERT(FALSE);
  17916. hr = DPNHERR_GENERIC;
  17917. goto Failure;
  17918. break;
  17919. }
  17920. }
  17921. //
  17922. // The action completed successfully.
  17923. //
  17924. pContext->pControlResponseInfo->hrErrorCode = DPNH_OK;
  17925. }
  17926. else
  17927. {
  17928. //
  17929. // The action failed.
  17930. //
  17931. //
  17932. // See if we found an error description that we can print for
  17933. // informational purposes.
  17934. //
  17935. if ((pszErrorCode != NULL) && (pszErrorDescription != NULL))
  17936. {
  17937. iErrorCode = atoi(pszErrorCode);
  17938. switch (iErrorCode)
  17939. {
  17940. case UPNPERR_IGD_NOSUCHENTRYINARRAY:
  17941. {
  17942. DPFX(DPFPREP, 1, "Control action was rejected with NoSuchEntryInArray error %hs (description = \"%hs\").",
  17943. pszErrorCode, pszErrorDescription);
  17944. pContext->pControlResponseInfo->hrErrorCode = DPNHERR_NOMAPPING;
  17945. break;
  17946. }
  17947. case UPNPERR_IGD_CONFLICTINMAPPINGENTRY:
  17948. {
  17949. DPFX(DPFPREP, 1, "Control action was rejected with ConflictInMappingEntry error %hs (description = \"%hs\").",
  17950. pszErrorCode, pszErrorDescription);
  17951. pContext->pControlResponseInfo->hrErrorCode = DPNHERR_PORTUNAVAILABLE;
  17952. break;
  17953. }
  17954. case UPNPERR_IGD_SAMEPORTVALUESREQUIRED:
  17955. {
  17956. DPFX(DPFPREP, 1, "Control action was rejected with SamePortValuesRequired error %hs (description = \"%hs\").",
  17957. pszErrorCode, pszErrorDescription);
  17958. pContext->pControlResponseInfo->hrErrorCode = (HRESULT) UPNPERR_IGD_SAMEPORTVALUESREQUIRED;
  17959. break;
  17960. }
  17961. case UPNPERR_IGD_ONLYPERMANENTLEASESSUPPORTED:
  17962. {
  17963. DPFX(DPFPREP, 1, "Control action was rejected with OnlyPermanentLeasesSupported error %hs (description = \"%hs\").",
  17964. pszErrorCode, pszErrorDescription);
  17965. pContext->pControlResponseInfo->hrErrorCode = (HRESULT) UPNPERR_IGD_ONLYPERMANENTLEASESSUPPORTED;
  17966. break;
  17967. }
  17968. default:
  17969. {
  17970. DPFX(DPFPREP, 1, "Control action was rejected with unknown error \"%hs\", \"%hs\", assuming generic failure.",
  17971. pszErrorCode, pszErrorDescription);
  17972. pContext->pControlResponseInfo->hrErrorCode = DPNHERR_GENERIC;
  17973. break;
  17974. }
  17975. }
  17976. }
  17977. else
  17978. {
  17979. DPFX(DPFPREP, 1, "Couldn't find either \"" ARG_CONTROL_ERROR_ERRORCODE_A "\", or \"" ARG_CONTROL_ERROR_ERRORDESCRIPTION_A "\" in SOAP response, assuming generic failure.");
  17980. pContext->pControlResponseInfo->hrErrorCode = DPNHERR_GENERIC;
  17981. }
  17982. }
  17983. //
  17984. // If we got here, we got the information we needed.
  17985. //
  17986. pParseElement->fFoundMatchingElement = TRUE;
  17987. (*pfContinueParsing) = FALSE;
  17988. Exit:
  17989. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  17990. return hr;
  17991. Failure:
  17992. goto Exit;
  17993. } // CNATHelpUPnP::ParseXMLCallback_ControlResponse
  17994. #undef DPF_MODNAME
  17995. #define DPF_MODNAME "CNATHelpUPnP::ClearDevicesUPnPDevice"
  17996. //=============================================================================
  17997. // CNATHelpUPnP::ClearDevicesUPnPDevice
  17998. //-----------------------------------------------------------------------------
  17999. //
  18000. // Description: Forcefully simulates de-registration with a UPnP device
  18001. /// without actually going to the network. This clears all bind
  18002. // IDs, public addresses, and cached mappings for a given device's
  18003. // local or remote server, and should only be called after the
  18004. // server appears to have died.
  18005. //
  18006. // The object lock is assumed to be held.
  18007. //
  18008. // Arguments:
  18009. // CDevice * pDevice - Pointer to device whose UPnP device should be
  18010. // removed.
  18011. //
  18012. // Returns: None.
  18013. //=============================================================================
  18014. void CNATHelpUPnP::ClearDevicesUPnPDevice(CDevice * const pDevice)
  18015. {
  18016. CUPnPDevice * pUPnPDevice;
  18017. DNASSERT(pDevice != NULL);
  18018. pUPnPDevice = pDevice->GetUPnPDevice();
  18019. if (pUPnPDevice != NULL)
  18020. {
  18021. #ifdef DBG
  18022. DPFX(DPFPREP, 1, "Clearing device 0x%p's UPnP device (0x%p).",
  18023. pDevice, pUPnPDevice);
  18024. pDevice->IncrementUPnPDeviceFailures();
  18025. this->m_dwNumServerFailures++;
  18026. #endif // DBG
  18027. //
  18028. // Since there was a change in the network, go back to polling
  18029. // relatively quickly.
  18030. //
  18031. this->ResetNextPollInterval();
  18032. pUPnPDevice->ClearDeviceOwner();
  18033. DNASSERT(pUPnPDevice->m_blList.IsListMember(&this->m_blUPnPDevices));
  18034. pUPnPDevice->m_blList.RemoveFromList();
  18035. //
  18036. // Transfer list reference to our pointer, since GetUPnPDevice did not give
  18037. // us one.
  18038. //
  18039. if (pUPnPDevice->IsConnected())
  18040. {
  18041. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  18042. this->m_pfnshutdown(pUPnPDevice->GetControlSocket(), 0); // ignore error
  18043. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  18044. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  18045. }
  18046. else
  18047. {
  18048. DNASSERT(pUPnPDevice->GetControlSocket() == INVALID_SOCKET);
  18049. }
  18050. this->ClearAllUPnPRegisteredPorts(pDevice);
  18051. pUPnPDevice->ClearLocationURL();
  18052. pUPnPDevice->ClearUSN();
  18053. pUPnPDevice->ClearServiceControlURL();
  18054. pUPnPDevice->DestroyReceiveBuffer();
  18055. pUPnPDevice->RemoveAllCachedMappings();
  18056. pUPnPDevice->DecRef();
  18057. }
  18058. else
  18059. {
  18060. DPFX(DPFPREP, 1, "Can't clear device 0x%p's UPnP device, it doesn't exist.",
  18061. pDevice);
  18062. }
  18063. } // CNATHelpUPnP::ClearDevicesUPnPDevice
  18064. #undef DPF_MODNAME
  18065. #define DPF_MODNAME "CNATHelpUPnP::ClearAllUPnPRegisteredPorts"
  18066. //=============================================================================
  18067. // CNATHelpUPnP::ClearAllUPnPRegisteredPorts
  18068. //-----------------------------------------------------------------------------
  18069. //
  18070. // Description: Clears all bind IDs and public addresses for a given
  18071. // device's UPnP Internet gateway. This should only be called
  18072. // after the UPnP device dies.
  18073. //
  18074. // The object lock is assumed to be held.
  18075. //
  18076. // Arguments:
  18077. // CDevice * pDevice - Pointer to device whose ports should be unbound.
  18078. // BOOL fRemote - TRUE if clearing remote server, FALSE if clearing
  18079. // local server.
  18080. //
  18081. // Returns: None.
  18082. //=============================================================================
  18083. void CNATHelpUPnP::ClearAllUPnPRegisteredPorts(CDevice * const pDevice)
  18084. {
  18085. CBilink * pBilink;
  18086. CRegisteredPort * pRegisteredPort;
  18087. DNASSERT(pDevice != NULL);
  18088. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  18089. while (pBilink != &pDevice->m_blOwnedRegPorts)
  18090. {
  18091. DNASSERT(! pBilink->IsEmpty());
  18092. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  18093. if (pRegisteredPort->HasUPnPPublicAddresses())
  18094. {
  18095. if (! pRegisteredPort->IsRemovingUPnPLease())
  18096. {
  18097. DPFX(DPFPREP, 1, "Registered port 0x%p losing UPnP public address.",
  18098. pRegisteredPort);
  18099. //
  18100. // Let the user know the next time GetCaps is called.
  18101. //
  18102. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  18103. //
  18104. // Note that this means the crash recovery entry will be left
  18105. // in the registry for the next person to come along and clean
  18106. // up. That should be okay, since we should only be doing this
  18107. // if we had a problem talking to the UPnP device (either it
  18108. // went AWOL, or we lost the local network interface). In
  18109. // either case, we can't really clean it up now, so we have to
  18110. // leave it for someone else to do.
  18111. //
  18112. pRegisteredPort->DestroyUPnPPublicAddressesArray();
  18113. pRegisteredPort->NoteNotPermanentUPnPLease();
  18114. DNASSERT(this->m_dwNumLeases > 0);
  18115. this->m_dwNumLeases--;
  18116. DPFX(DPFPREP, 7, "UPnP lease for 0x%p cleared, total num leases = %u.",
  18117. pRegisteredPort, this->m_dwNumLeases);
  18118. }
  18119. else
  18120. {
  18121. DPFX(DPFPREP, 1, "Registered port 0x%p already has had UPnP public address removed, skipping.",
  18122. pRegisteredPort);
  18123. }
  18124. }
  18125. else
  18126. {
  18127. //
  18128. // Port no longer unavailable (if it had been).
  18129. //
  18130. pRegisteredPort->NoteNotUPnPPortUnavailable();
  18131. }
  18132. pBilink = pBilink->GetNext();
  18133. }
  18134. } // CNATHelpUPnP::ClearAllUPnPRegisteredPorts
  18135. #ifndef DPNBUILD_NOWINSOCK2
  18136. #undef DPF_MODNAME
  18137. #define DPF_MODNAME "CNATHelpUPnP::RequestLocalAddressListChangeNotification"
  18138. //=============================================================================
  18139. // CNATHelpUPnP::RequestLocalAddressListChangeNotification
  18140. //-----------------------------------------------------------------------------
  18141. //
  18142. // Description: Attempts to request asynchronous notification (via the
  18143. // user's alert event or I/O completion port) when the local
  18144. // address list changes.
  18145. //
  18146. // The object lock is assumed to be held.
  18147. //
  18148. // Arguments: None.
  18149. //
  18150. // Returns: HRESULT
  18151. // DPNH_OK - The notification request was successfully submitted.
  18152. // DPNHERR_GENERIC - An error occurred.
  18153. //=============================================================================
  18154. HRESULT CNATHelpUPnP::RequestLocalAddressListChangeNotification(void)
  18155. {
  18156. HRESULT hr;
  18157. DWORD dwTemp;
  18158. int iReturn;
  18159. DPFX(DPFPREP, 5, "(0x%p) Enter", this);
  18160. DNASSERT(! (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1));
  18161. DNASSERT(this->m_sIoctls != INVALID_SOCKET);
  18162. DNASSERT(this->m_pfnWSAIoctl != NULL);
  18163. DNASSERT((this->m_hAlertEvent != NULL) || (this->m_hAlertIOCompletionPort != NULL));
  18164. DNASSERT(this->m_polAddressListChange != NULL);
  18165. do
  18166. {
  18167. iReturn = this->m_pfnWSAIoctl(this->m_sIoctls, // use the special Ioctl socket
  18168. SIO_ADDRESS_LIST_CHANGE, //
  18169. NULL, // no input data
  18170. 0, // no input data
  18171. NULL, // no output data
  18172. 0, // no output data
  18173. &dwTemp, // ignore bytes returned
  18174. this->m_polAddressListChange, // overlapped structure
  18175. NULL); // no completion routine
  18176. if (iReturn != 0)
  18177. {
  18178. dwTemp = this->m_pfnWSAGetLastError();
  18179. if (dwTemp != WSA_IO_PENDING)
  18180. {
  18181. DPFX(DPFPREP, 0, "Submitting address list change notification request failed (err = %u)!", dwTemp);
  18182. hr = DPNHERR_GENERIC;
  18183. goto Failure;
  18184. }
  18185. //
  18186. // Pending is what we want, we're set.
  18187. //
  18188. hr = DPNH_OK;
  18189. break;
  18190. }
  18191. //
  18192. // Address list changed right away?
  18193. //
  18194. DPFX(DPFPREP, 1, "Address list changed right away somehow, submitting again.");
  18195. }
  18196. while (TRUE);
  18197. Exit:
  18198. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  18199. return hr;
  18200. Failure:
  18201. goto Exit;
  18202. } // CNATHelpUPnP::RequestLocalAddressListChangeNotification
  18203. #endif // ! DPNBUILD_NOWINSOCK2
  18204. #undef DPF_MODNAME
  18205. #define DPF_MODNAME "CNATHelpUPnP::CreateSocket"
  18206. //=============================================================================
  18207. // CNATHelpUPnP::CreateSocket
  18208. //-----------------------------------------------------------------------------
  18209. //
  18210. // Description: Creates a UPnP discovery socket bound to a new random port
  18211. // on the specified IP interface. Completely random (but non-
  18212. // reserved) port numbers are chosen first, but if those ports are
  18213. // in use, WinSock is allowed to choose. The port actually
  18214. // selected will be returned in psaddrinAddress.
  18215. //
  18216. // Arguments:
  18217. // SOCKADDR_IN * psaddrinAddress - Pointer to base address to use when
  18218. // binding. The port will be modified.
  18219. //
  18220. // Returns: SOCKET
  18221. //=============================================================================
  18222. SOCKET CNATHelpUPnP::CreateSocket(SOCKADDR_IN * const psaddrinAddress,
  18223. int iType,
  18224. int iProtocol)
  18225. {
  18226. SOCKET sTemp;
  18227. DWORD dwTry;
  18228. int iTemp;
  18229. BOOL fTemp;
  18230. ULONG ulEnable;
  18231. #ifdef DBG
  18232. DWORD dwError;
  18233. #endif // DBG
  18234. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i, %i)",
  18235. this, psaddrinAddress, iType, iProtocol);
  18236. //
  18237. // Create the socket.
  18238. //
  18239. sTemp = this->m_pfnsocket(AF_INET, iType, iProtocol);
  18240. if (sTemp == INVALID_SOCKET)
  18241. {
  18242. #ifdef DBG
  18243. dwError = this->m_pfnWSAGetLastError();
  18244. DPFX(DPFPREP, 0, "Couldn't create datagram socket, error = %u!", dwError);
  18245. #endif // DBG
  18246. goto Failure;
  18247. }
  18248. //
  18249. // Try binding the socket to a completely random port a few times.
  18250. //
  18251. for(dwTry = 0; dwTry < MAX_NUM_RANDOM_PORT_TRIES; dwTry++)
  18252. {
  18253. //
  18254. // Pick a completely random port. For the moment, the value is stored
  18255. // in host byte order while we make sure it's not a reserved value.
  18256. //
  18257. do
  18258. {
  18259. psaddrinAddress->sin_port = (WORD) GetGlobalRand();
  18260. }
  18261. while ((psaddrinAddress->sin_port <= MAX_RESERVED_PORT) ||
  18262. (psaddrinAddress->sin_port == 1900) || // SSDP
  18263. (psaddrinAddress->sin_port == 2234) || // PAST
  18264. (psaddrinAddress->sin_port == 6073) || // DPNSVR
  18265. (psaddrinAddress->sin_port == 47624)); // DPLAYSVR
  18266. //
  18267. // Now try binding to the port (in network byte order).
  18268. //
  18269. psaddrinAddress->sin_port = HTONS(psaddrinAddress->sin_port);
  18270. if (this->m_pfnbind(sTemp, (SOCKADDR*) psaddrinAddress, sizeof(SOCKADDR_IN)) == 0)
  18271. {
  18272. //
  18273. // We successfully bound to the port.
  18274. //
  18275. break;
  18276. }
  18277. //
  18278. // Assume that the port is in use.
  18279. //
  18280. #ifdef DBG
  18281. dwError = this->m_pfnWSAGetLastError();
  18282. DPFX(DPFPREP, 2, "Couldn't bind to port %u (err = %u), continuing.",
  18283. NTOHS(psaddrinAddress->sin_port), dwError);
  18284. #endif // DBG
  18285. psaddrinAddress->sin_port = 0;
  18286. }
  18287. //
  18288. // If we ran out of completely random port attempts, just let WinSock
  18289. // choose it.
  18290. //
  18291. if (psaddrinAddress->sin_port == 0)
  18292. {
  18293. if (this->m_pfnbind(sTemp, (SOCKADDR*) psaddrinAddress, sizeof(SOCKADDR_IN)) != 0)
  18294. {
  18295. #ifdef DBG
  18296. dwError = this->m_pfnWSAGetLastError();
  18297. DPFX(DPFPREP, 0, "Failed binding to any port (err = %u)!",
  18298. dwError);
  18299. #endif // DBG
  18300. goto Failure;
  18301. }
  18302. //
  18303. // Find out what port WinSock chose.
  18304. //
  18305. iTemp = sizeof(SOCKADDR_IN);
  18306. if (this->m_pfngetsockname(sTemp,
  18307. (SOCKADDR *) psaddrinAddress,
  18308. &iTemp) != 0)
  18309. {
  18310. #ifdef DBG
  18311. dwError = this->m_pfnWSAGetLastError();
  18312. DPFX(DPFPREP, 0, "Couldn't get the socket's address, error = %u!",
  18313. dwError);
  18314. #endif // DBG
  18315. goto Failure;
  18316. }
  18317. DNASSERT(psaddrinAddress->sin_port != 0);
  18318. }
  18319. //
  18320. // Set the unicast TTL, if requested. Use the appropriate constant for the
  18321. // version of WinSock we're using.
  18322. //
  18323. if (g_iUnicastTTL != 0)
  18324. {
  18325. iTemp = this->m_pfnsetsockopt(sTemp,
  18326. IPPROTO_IP,
  18327. #ifdef DPNBUILD_NOWINSOCK2
  18328. IP_TTL,
  18329. #else // ! DPNBUILD_NOWINSOCK2
  18330. ((this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1) ? IP_TTL_WINSOCK1 : IP_TTL),
  18331. #endif // ! DPNBUILD_NOWINSOCK2
  18332. (char *) (&g_iUnicastTTL),
  18333. sizeof(g_iUnicastTTL));
  18334. if (iTemp != 0)
  18335. {
  18336. #ifdef DBG
  18337. dwError = this->m_pfnWSAGetLastError();
  18338. DPFX(DPFPREP, 0, "Couldn't set unicast TTL socket option, error = %u! Ignoring.",
  18339. dwError);
  18340. #endif // DBG
  18341. //
  18342. // Continue...
  18343. //
  18344. }
  18345. }
  18346. if (iType == SOCK_DGRAM)
  18347. {
  18348. if (g_fUseMulticastUPnPDiscovery)
  18349. {
  18350. //
  18351. // Set the multicast interface. Use the appropriate constant for
  18352. // the version of WinSock we're using.
  18353. //
  18354. iTemp = this->m_pfnsetsockopt(sTemp,
  18355. IPPROTO_IP,
  18356. #ifdef DPNBUILD_NOWINSOCK2
  18357. IP_MULTICAST_IF,
  18358. #else // ! DPNBUILD_NOWINSOCK2
  18359. ((this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1) ? IP_MULTICAST_IF_WINSOCK1 : IP_MULTICAST_IF),
  18360. #endif // ! DPNBUILD_NOWINSOCK2
  18361. (char *) (&psaddrinAddress->sin_addr.S_un.S_addr),
  18362. sizeof(psaddrinAddress->sin_addr.S_un.S_addr));
  18363. if (iTemp != 0)
  18364. {
  18365. #ifdef DBG
  18366. dwError = this->m_pfnWSAGetLastError();
  18367. DPFX(DPFPREP, 1, "Couldn't set multicast interface socket option, error = %u, ignoring.",
  18368. dwError);
  18369. #endif // DBG
  18370. //
  18371. // Continue...
  18372. //
  18373. }
  18374. //
  18375. // Set the multicast TTL, if requested. Use the appropriate
  18376. // constant for the version of WinSock we're using.
  18377. //
  18378. if (g_iMulticastTTL != 0)
  18379. {
  18380. iTemp = this->m_pfnsetsockopt(sTemp,
  18381. IPPROTO_IP,
  18382. #ifdef DPNBUILD_NOWINSOCK2
  18383. IP_MULTICAST_TTL,
  18384. #else // ! DPNBUILD_NOWINSOCK2
  18385. ((this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1) ? IP_MULTICAST_TTL_WINSOCK1 : IP_MULTICAST_TTL),
  18386. #endif // ! DPNBUILD_NOWINSOCK2
  18387. (char *) (&g_iMulticastTTL),
  18388. sizeof(g_iMulticastTTL));
  18389. if (iTemp != 0)
  18390. {
  18391. #ifdef DBG
  18392. dwError = this->m_pfnWSAGetLastError();
  18393. DPFX(DPFPREP, 0, "Couldn't set multicast TTL socket option, error = %u! Ignoring.",
  18394. dwError);
  18395. #endif // DBG
  18396. //
  18397. // Continue...
  18398. //
  18399. }
  18400. }
  18401. }
  18402. else
  18403. {
  18404. //
  18405. // Not using multicast. Set the socket up to allow broadcasts in
  18406. // case we can't determine the gateway.
  18407. //
  18408. fTemp = TRUE;
  18409. if (this->m_pfnsetsockopt(sTemp,
  18410. SOL_SOCKET,
  18411. SO_BROADCAST,
  18412. (char *) (&fTemp),
  18413. sizeof(fTemp)) != 0)
  18414. {
  18415. #ifdef DBG
  18416. dwError = this->m_pfnWSAGetLastError();
  18417. DPFX(DPFPREP, 0, "Couldn't set broadcast socket option, error = %u!", dwError);
  18418. #endif // DBG
  18419. goto Failure;
  18420. }
  18421. }
  18422. }
  18423. else
  18424. {
  18425. //
  18426. // Make the socket non-blocking.
  18427. //
  18428. ulEnable = 1;
  18429. if (this->m_pfnioctlsocket(sTemp, FIONBIO, &ulEnable) != 0)
  18430. {
  18431. #ifdef DBG
  18432. dwError = this->m_pfnWSAGetLastError();
  18433. DPFX(DPFPREP, 0, "Couldn't make socket non-blocking, error = %u!", dwError);
  18434. #endif // DBG
  18435. goto Failure;
  18436. }
  18437. }
  18438. Exit:
  18439. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%x]", this, sTemp);
  18440. return sTemp;
  18441. Failure:
  18442. if (sTemp != INVALID_SOCKET)
  18443. {
  18444. this->m_pfnclosesocket(sTemp);
  18445. sTemp = INVALID_SOCKET;
  18446. }
  18447. goto Exit;
  18448. } // CNATHelpUPnP::CreateSocket
  18449. #undef DPF_MODNAME
  18450. #define DPF_MODNAME "CNATHelpUPnP::GetAddressToReachGateway"
  18451. //=============================================================================
  18452. // CNATHelpUPnP::GetAddressToReachGateway
  18453. //-----------------------------------------------------------------------------
  18454. //
  18455. // Description: Retrieves the address of the gateway for the given device,
  18456. // or the broadcast address if unable to be determined.
  18457. //
  18458. // This will return TRUE if the gateway's address was found, or
  18459. // the IPHLPAPI DLL could not be used (Win95). FALSE is returned
  18460. // if IPHLPAPI reported that there was no gateway (ICS private
  18461. // side adapter).
  18462. //
  18463. // Arguments:
  18464. // CDevice * pDevice - Pointer to device whose gateway should be retrieved.
  18465. // IN_ADDR * pinaddr - Place to store gateway or broadcast address.
  18466. //
  18467. // Returns: BOOL
  18468. // TRUE - Gateway address was found or had to use broadcast.
  18469. // FALSE - There is no gateway, do not attempt to use the address.
  18470. //=============================================================================
  18471. BOOL CNATHelpUPnP::GetAddressToReachGateway(CDevice * const pDevice,
  18472. IN_ADDR * const pinaddr)
  18473. {
  18474. #ifdef DPNBUILD_NOWINSOCK2
  18475. //
  18476. // Fill in the default address. This should be atomic, so don't worry
  18477. // about locking the globals.
  18478. //
  18479. pinaddr->S_un.S_addr = g_dwDefaultGatewayV4;
  18480. return TRUE;
  18481. #else // ! DPNBUILD_NOWINSOCK2
  18482. DWORD dwError;
  18483. BOOL fResult = TRUE;
  18484. ULONG ulSize;
  18485. PIP_ADAPTER_INFO pAdaptersBuffer = NULL;
  18486. PIP_ADAPTER_INFO pAdapterInfo;
  18487. PIP_ADDR_STRING pIPAddrString;
  18488. DWORD dwAdapterIndex;
  18489. PMIB_IPFORWARDTABLE pIPForwardTableBuffer = NULL;
  18490. DWORD dwTemp;
  18491. PMIB_IPFORWARDROW pIPForwardRow;
  18492. //
  18493. // Fill in the default address. This should be atomic, so don't worry
  18494. // about locking the globals.
  18495. //
  18496. pinaddr->S_un.S_addr = g_dwDefaultGatewayV4;
  18497. #ifdef DBG
  18498. pDevice->ClearGatewayFlags();
  18499. #endif // DBG
  18500. //
  18501. // If this is the loopback address, then don't bother looking for a
  18502. // gateway, we won't find one.
  18503. //
  18504. if (pDevice->GetLocalAddressV4() == NETWORKBYTEORDER_INADDR_LOOPBACK)
  18505. {
  18506. DPFX(DPFPREP, 8, "No gateway for loopback address (device = 0x%p).",
  18507. pDevice);
  18508. //
  18509. // No gateway.
  18510. //
  18511. #ifdef DBG
  18512. pDevice->NoteNoGateway();
  18513. #endif // DBG
  18514. fResult = FALSE;
  18515. goto Exit;
  18516. }
  18517. //
  18518. // If we didn't load the IP helper DLL, we can't do our fancy gateway
  18519. // tricks.
  18520. //
  18521. if (this->m_hIpHlpApiDLL == NULL)
  18522. {
  18523. DPFX(DPFPREP, 4, "Didn't load \"iphlpapi.dll\", returning default address for device 0x%p.",
  18524. pDevice);
  18525. goto Exit;
  18526. }
  18527. //
  18528. // Keep trying to get the list of adapters until we get ERROR_SUCCESS or a
  18529. // legitimate error (other than ERROR_BUFFER_OVERFLOW or
  18530. // ERROR_INSUFFICIENT_BUFFER).
  18531. //
  18532. ulSize = 0;
  18533. do
  18534. {
  18535. dwError = this->m_pfnGetAdaptersInfo(pAdaptersBuffer, &ulSize);
  18536. if (dwError == ERROR_SUCCESS)
  18537. {
  18538. //
  18539. // We succeeded, we should be set. But make sure there are
  18540. // adapters for us to use.
  18541. //
  18542. if (ulSize < sizeof(IP_ADAPTER_INFO))
  18543. {
  18544. DPFX(DPFPREP, 0, "Getting adapters info succeeded but didn't return any valid adapters (%u < %u), returning default address for device 0x%p.",
  18545. ulSize, sizeof(IP_ADAPTER_INFO), pDevice);
  18546. goto Exit;
  18547. }
  18548. break;
  18549. }
  18550. if ((dwError != ERROR_BUFFER_OVERFLOW) &&
  18551. (dwError != ERROR_INSUFFICIENT_BUFFER))
  18552. {
  18553. DPFX(DPFPREP, 0, "Unable to get adapters info (error = 0x%lx), returning default address for device 0x%p.",
  18554. dwError, pDevice);
  18555. goto Exit;
  18556. }
  18557. //
  18558. // We need more adapter space. Make sure there are adapters for us to
  18559. // use.
  18560. //
  18561. if (ulSize < sizeof(IP_ADAPTER_INFO))
  18562. {
  18563. DPFX(DPFPREP, 0, "Getting adapters info didn't return any valid adapters (%u < %u), returning default address for device 0x%p.",
  18564. ulSize, sizeof(IP_ADAPTER_INFO), pDevice);
  18565. goto Exit;
  18566. }
  18567. //
  18568. // If we previously had a buffer, free it.
  18569. //
  18570. if (pAdaptersBuffer != NULL)
  18571. {
  18572. DNFree(pAdaptersBuffer);
  18573. }
  18574. //
  18575. // Allocate the buffer.
  18576. //
  18577. pAdaptersBuffer = (PIP_ADAPTER_INFO) DNMalloc(ulSize);
  18578. if (pAdaptersBuffer == NULL)
  18579. {
  18580. DPFX(DPFPREP, 0, "Unable to allocate memory for adapters info, returning default address for device 0x%p.",
  18581. pDevice);
  18582. goto Exit;
  18583. }
  18584. }
  18585. while (TRUE);
  18586. //
  18587. // Now find the device in the adapter list returned. Loop through all
  18588. // adapters.
  18589. //
  18590. pAdapterInfo = pAdaptersBuffer;
  18591. while (pAdapterInfo != NULL)
  18592. {
  18593. //
  18594. // Loop through all addresses for this adapter looking for the one for
  18595. // the device we have bound.
  18596. //
  18597. pIPAddrString = &pAdapterInfo->IpAddressList;
  18598. while (pIPAddrString != NULL)
  18599. {
  18600. if (this->m_pfninet_addr(pIPAddrString->IpAddress.String) == pDevice->GetLocalAddressV4())
  18601. {
  18602. pinaddr->S_un.S_addr = this->m_pfninet_addr(pAdapterInfo->GatewayList.IpAddress.String);
  18603. if ((pinaddr->S_un.S_addr == INADDR_ANY) ||
  18604. (pinaddr->S_un.S_addr == INADDR_NONE))
  18605. {
  18606. DPFX(DPFPREP, 8, "Found address for device 0x%p under adapter index %u (\"%hs\") but there is no gateway.",
  18607. pDevice, pAdapterInfo->Index, pAdapterInfo->Description);
  18608. //
  18609. // Although this isn't reporting a gateway, we may still
  18610. // want to use this adapter. That's because this could be
  18611. // a multihomed machine with multiple NICs on the same
  18612. // network, where this one isn't the "default" adapter.
  18613. // So save the index so we can search for it later.
  18614. //
  18615. dwAdapterIndex = pAdapterInfo->Index;
  18616. goto CheckRouteTable;
  18617. }
  18618. //
  18619. // Make sure the address doesn't match the local device.
  18620. //
  18621. if (pinaddr->S_un.S_addr == pDevice->GetLocalAddressV4())
  18622. {
  18623. DPFX(DPFPREP, 1, "Gateway address for device 0x%p (adapter index %u, \"%hs\") matches device IP address %hs! Forcing no gateway.",
  18624. pDevice, pAdapterInfo->Index, pAdapterInfo->Description,
  18625. pAdapterInfo->GatewayList.IpAddress.String);
  18626. //
  18627. // Pretend there's no gateway, since the one we received is
  18628. // bogus.
  18629. //
  18630. #ifdef DBG
  18631. pDevice->NoteNoGateway();
  18632. #endif // DBG
  18633. fResult = FALSE;
  18634. }
  18635. else
  18636. {
  18637. DPFX(DPFPREP, 7, "Found address for device 0x%p under adapter index %u (\"%hs\"), gateway = %hs.",
  18638. pDevice, pAdapterInfo->Index, pAdapterInfo->Description,
  18639. pAdapterInfo->GatewayList.IpAddress.String);
  18640. #ifdef DBG
  18641. pDevice->NotePrimaryDevice();
  18642. #endif // DBG
  18643. }
  18644. goto Exit;
  18645. }
  18646. pIPAddrString = pIPAddrString->Next;
  18647. }
  18648. if (! fResult)
  18649. {
  18650. break;
  18651. }
  18652. pAdapterInfo = pAdapterInfo->Next;
  18653. }
  18654. //
  18655. // If we got here, then we didn't find the address. fResult will still be
  18656. // TRUE.
  18657. //
  18658. DPFX(DPFPREP, 0, "Did not find adapter with matching address, returning default address for device 0x%p.",
  18659. pDevice);
  18660. goto Exit;
  18661. CheckRouteTable:
  18662. //
  18663. // The adapter info structure said that the device doesn't have a gateway.
  18664. // However for some reason the gateway is only reported for the "default"
  18665. // device when multiple NICs can reach the same network. Check the routing
  18666. // table to determine if there's a gateway for secondary devices.
  18667. //
  18668. //
  18669. // Keep trying to get the routing table until we get ERROR_SUCCESS or a
  18670. // legitimate error (other than ERROR_BUFFER_OVERFLOW or
  18671. // ERROR_INSUFFICIENT_BUFFER).
  18672. //
  18673. ulSize = 0;
  18674. do
  18675. {
  18676. dwError = this->m_pfnGetIpForwardTable(pIPForwardTableBuffer, &ulSize, TRUE);
  18677. if (dwError == ERROR_SUCCESS)
  18678. {
  18679. //
  18680. // We succeeded, we should be set. But make sure the size is
  18681. // valid.
  18682. //
  18683. if (ulSize < sizeof(MIB_IPFORWARDTABLE))
  18684. {
  18685. 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.",
  18686. ulSize, sizeof(MIB_IPFORWARDTABLE), pDevice);
  18687. fResult = FALSE;
  18688. goto Exit;
  18689. }
  18690. break;
  18691. }
  18692. if ((dwError != ERROR_BUFFER_OVERFLOW) &&
  18693. (dwError != ERROR_INSUFFICIENT_BUFFER))
  18694. {
  18695. DPFX(DPFPREP, 0, "Unable to get IP forward table (error = 0x%lx), returning \"no gateway\" indication for device 0x%p.",
  18696. dwError, pDevice);
  18697. fResult = FALSE;
  18698. goto Exit;
  18699. }
  18700. //
  18701. // We need more table space. Make sure there are adapters for us to
  18702. // use.
  18703. //
  18704. if (ulSize < sizeof(MIB_IPFORWARDTABLE))
  18705. {
  18706. DPFX(DPFPREP, 0, "Getting IP forward table didn't return any valid adapters (%u < %u), returning \"no gateway\" indication for device 0x%p.",
  18707. ulSize, sizeof(MIB_IPFORWARDTABLE), pDevice);
  18708. fResult = FALSE;
  18709. goto Exit;
  18710. }
  18711. //
  18712. // If we previously had a buffer, free it.
  18713. //
  18714. if (pIPForwardTableBuffer != NULL)
  18715. {
  18716. DNFree(pIPForwardTableBuffer);
  18717. }
  18718. //
  18719. // Allocate the buffer.
  18720. //
  18721. pIPForwardTableBuffer = (PMIB_IPFORWARDTABLE) DNMalloc(ulSize);
  18722. if (pIPForwardTableBuffer == NULL)
  18723. {
  18724. DPFX(DPFPREP, 0, "Unable to allocate memory for IP forward table, returning \"no gateway\" indication for device 0x%p.",
  18725. pDevice);
  18726. fResult = FALSE;
  18727. goto Exit;
  18728. }
  18729. }
  18730. while (TRUE);
  18731. //
  18732. // Now find the interface. Note that we don't look it up as a destination
  18733. // address. Instead, we look for it as the interface to use for a 0.0.0.0
  18734. // network destination.
  18735. //
  18736. // We're looking for a route entry:
  18737. //
  18738. // Network Destination Netmask Gateway Interface Metric
  18739. // 0.0.0.0 0.0.0.0 xxx.xxx.xxx.xxx yyy.yyy.yyy.yyy 1
  18740. //
  18741. // We have yyy.yyy.yyy.yyy, we're trying to get xxx.xxx.xxx.xxx
  18742. //
  18743. pIPForwardRow = pIPForwardTableBuffer->table;
  18744. for(dwTemp = 0; dwTemp < pIPForwardTableBuffer->dwNumEntries; dwTemp++)
  18745. {
  18746. //
  18747. // Is this a 0.0.0.0 network destination?
  18748. //
  18749. if (pIPForwardRow->dwForwardDest == INADDR_ANY)
  18750. {
  18751. DNASSERT(pIPForwardRow->dwForwardMask == INADDR_ANY);
  18752. //
  18753. // Is this the right interface?
  18754. //
  18755. if (pIPForwardRow->dwForwardIfIndex == dwAdapterIndex)
  18756. {
  18757. if (pIPForwardRow->dwForwardNextHop == INADDR_ANY)
  18758. {
  18759. DPFX(DPFPREP, 8, "Found route table entry, but it didn't have a gateway (device = 0x%p).",
  18760. pDevice);
  18761. //
  18762. // No gateway.
  18763. //
  18764. #ifdef DBG
  18765. pDevice->NoteNoGateway();
  18766. #endif // DBG
  18767. fResult = FALSE;
  18768. }
  18769. else
  18770. {
  18771. //
  18772. // Make sure the address doesn't match the local device.
  18773. //
  18774. if (pinaddr->S_un.S_addr == pDevice->GetLocalAddressV4())
  18775. {
  18776. DPFX(DPFPREP, 1, "Route table gateway for device 0x%p matches device's IP address %u.%u.%u.%u! Forcing no gateway.",
  18777. pDevice,
  18778. pinaddr->S_un.S_un_b.s_b1,
  18779. pinaddr->S_un.S_un_b.s_b2,
  18780. pinaddr->S_un.S_un_b.s_b3,
  18781. pinaddr->S_un.S_un_b.s_b4);
  18782. //
  18783. // Pretend there's no gateway, since the one we
  18784. // received is bogus.
  18785. //
  18786. #ifdef DBG
  18787. pDevice->NoteNoGateway();
  18788. #endif // DBG
  18789. fResult = FALSE;
  18790. }
  18791. else
  18792. {
  18793. pinaddr->S_un.S_addr = pIPForwardRow->dwForwardNextHop;
  18794. DPFX(DPFPREP, 8, "Found route table entry, gateway = %u.%u.%u.%u (device = 0x%p).",
  18795. pinaddr->S_un.S_un_b.s_b1,
  18796. pinaddr->S_un.S_un_b.s_b2,
  18797. pinaddr->S_un.S_un_b.s_b3,
  18798. pinaddr->S_un.S_un_b.s_b4,
  18799. pDevice);
  18800. //
  18801. // We found a gateway after all, fResult == TRUE.
  18802. //
  18803. #ifdef DBG
  18804. pDevice->NoteSecondaryDevice();
  18805. #endif // DBG
  18806. }
  18807. }
  18808. //
  18809. // We're done here.
  18810. //
  18811. goto Exit;
  18812. }
  18813. }
  18814. //
  18815. // Move to next row.
  18816. //
  18817. pIPForwardRow++;
  18818. }
  18819. //
  18820. // If we got here, then we couldn't find an appropriate entry in the
  18821. // routing table.
  18822. //
  18823. DPFX(DPFPREP, 1, "Did not find adapter in routing table, returning \"no gateway\" indication for device 0x%p.",
  18824. pDevice);
  18825. #ifdef DBG
  18826. pDevice->NoteNoGateway();
  18827. #endif // DBG
  18828. fResult = FALSE;
  18829. Exit:
  18830. if (pAdaptersBuffer != NULL)
  18831. {
  18832. DNFree(pAdaptersBuffer);
  18833. pAdaptersBuffer = NULL;
  18834. }
  18835. if (pIPForwardTableBuffer != NULL)
  18836. {
  18837. DNFree(pIPForwardTableBuffer);
  18838. pIPForwardTableBuffer = NULL;
  18839. }
  18840. return fResult;
  18841. #endif // ! DPNBUILD_NOWINSOCK2
  18842. } // CNATHelpUPnP::GetAddressToReachGateway
  18843. #undef DPF_MODNAME
  18844. #define DPF_MODNAME "CNATHelpUPnP::IsAddressLocal"
  18845. //=============================================================================
  18846. // CNATHelpUPnP::IsAddressLocal
  18847. //-----------------------------------------------------------------------------
  18848. //
  18849. // Description: Returns TRUE if the given address is local to the given
  18850. // device; that is, if the device can send to the address directly
  18851. // without having to go through the gateway.
  18852. //
  18853. // Note that if IPHLPAPI is not available (Win95), this
  18854. // function will make an educated guess using a reasonable subnet
  18855. // mask.
  18856. //
  18857. // Arguments:
  18858. // CDevice * pDevice - Pointer to device to use.
  18859. // SOCKADDR_IN * psaddrinAddress - Address whose locality is in question.
  18860. //
  18861. // Returns: BOOL
  18862. // TRUE - Address is behind the same gateway as the device.
  18863. // FALSE - Address is not behind the same gateway as the device.
  18864. //=============================================================================
  18865. BOOL CNATHelpUPnP::IsAddressLocal(CDevice * const pDevice,
  18866. const SOCKADDR_IN * const psaddrinAddress)
  18867. {
  18868. BOOL fResult;
  18869. DWORD dwSubnetMaskV4;
  18870. #ifndef DPNBUILD_NOWINSOCK2
  18871. DWORD dwError;
  18872. MIB_IPFORWARDROW IPForwardRow;
  18873. #endif // ! DPNBUILD_NOWINSOCK2
  18874. //
  18875. // If the address to query matches the device's local address exactly, then
  18876. // of course it's local.
  18877. //
  18878. if (psaddrinAddress->sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4())
  18879. {
  18880. DPFX(DPFPREP, 6, "The address %u.%u.%u.%u matches device 0x%p's local address exactly.",
  18881. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18882. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18883. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18884. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  18885. pDevice);
  18886. fResult = TRUE;
  18887. goto Exit;
  18888. }
  18889. //
  18890. // If it's a multicast address, then it should not be considered local.
  18891. //
  18892. if (IS_CLASSD_IPV4_ADDRESS(psaddrinAddress->sin_addr.S_un.S_addr))
  18893. {
  18894. DPFX(DPFPREP, 6, "Address %u.%u.%u.%u is multicast, not considered local for device 0x%p.",
  18895. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18896. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18897. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18898. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  18899. pDevice);
  18900. fResult = FALSE;
  18901. goto Exit;
  18902. }
  18903. #ifndef DPNBUILD_NOWINSOCK2
  18904. //
  18905. // If we didn't load the IP helper DLL, we will have to guess.
  18906. //
  18907. if (this->m_hIpHlpApiDLL == NULL)
  18908. {
  18909. goto EducatedGuess;
  18910. }
  18911. //
  18912. // Figure out what IPHLPAPI says about how to get there.
  18913. //
  18914. ZeroMemory(&IPForwardRow, sizeof(IPForwardRow));
  18915. dwError = this->m_pfnGetBestRoute(psaddrinAddress->sin_addr.S_un.S_addr,
  18916. pDevice->GetLocalAddressV4(),
  18917. &IPForwardRow);
  18918. if (dwError != ERROR_SUCCESS)
  18919. {
  18920. DPFX(DPFPREP, 0, "Unable to get best route to %u.%u.%u.%u via device 0x%p (error = 0x%lx)! Using subnet mask.",
  18921. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18922. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18923. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18924. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  18925. pDevice,
  18926. dwError);
  18927. goto EducatedGuess;
  18928. }
  18929. //
  18930. // Key off what IPHLPAPI returned.
  18931. //
  18932. switch (IPForwardRow.dwForwardType)
  18933. {
  18934. case 1:
  18935. {
  18936. //
  18937. // Other.
  18938. //
  18939. DPFX(DPFPREP, 6, "The route from device 0x%p to %u.%u.%u.%u is unknown.",
  18940. pDevice,
  18941. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18942. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18943. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18944. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
  18945. fResult = FALSE;
  18946. break;
  18947. }
  18948. case 2:
  18949. {
  18950. //
  18951. // The route is invalid.
  18952. //
  18953. DPFX(DPFPREP, 6, "The route from device 0x%p to %u.%u.%u.%u is invalid.",
  18954. pDevice,
  18955. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18956. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18957. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18958. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
  18959. fResult = FALSE;
  18960. break;
  18961. }
  18962. case 3:
  18963. {
  18964. //
  18965. // The next hop is the final destination (local route).
  18966. // Unfortunately, on multi-NIC machines querying an address
  18967. // reachable by another device returns success... not sure why, but
  18968. // if that's the case we need to further qualify this result. We
  18969. // do that by making sure the next hop address is actually the
  18970. // device with which we're querying.
  18971. //
  18972. if (IPForwardRow.dwForwardNextHop == pDevice->GetLocalAddressV4())
  18973. {
  18974. DPFX(DPFPREP, 6, "Device 0x%p can reach %u.%u.%u.%u directly, it's local.",
  18975. pDevice,
  18976. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18977. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18978. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18979. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
  18980. fResult = TRUE;
  18981. }
  18982. else
  18983. {
  18984. DPFX(DPFPREP, 6, "Device 0x%p can reach %u.%u.%u.%u but it would be routed via another device (%u.%u.%u.%u).",
  18985. pDevice,
  18986. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18987. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18988. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18989. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  18990. ((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b1,
  18991. ((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b2,
  18992. ((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b3,
  18993. ((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b4);
  18994. fResult = FALSE;
  18995. }
  18996. break;
  18997. }
  18998. case 4:
  18999. {
  19000. //
  19001. // The next hop is not the final destination (remote route).
  19002. //
  19003. DPFX(DPFPREP, 6, "Device 0x%p cannot reach %u.%u.%u.%u directly.",
  19004. pDevice,
  19005. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  19006. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  19007. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  19008. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
  19009. fResult = FALSE;
  19010. break;
  19011. }
  19012. default:
  19013. {
  19014. //
  19015. // What?
  19016. //
  19017. DPFX(DPFPREP, 0, "Unexpected forward type %u for device 0x%p and address %u.%u.%u.%u!",
  19018. IPForwardRow.dwForwardType,
  19019. pDevice,
  19020. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  19021. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  19022. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  19023. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
  19024. fResult = FALSE;
  19025. break;
  19026. }
  19027. }
  19028. goto Exit;
  19029. EducatedGuess:
  19030. #endif // ! DPNBUILD_NOWINSOCK2
  19031. //
  19032. // This should be atomic, so don't worry about locking.
  19033. //
  19034. dwSubnetMaskV4 = g_dwSubnetMaskV4;
  19035. if ((pDevice->GetLocalAddressV4() & dwSubnetMaskV4) == (psaddrinAddress->sin_addr.S_un.S_addr & dwSubnetMaskV4))
  19036. {
  19037. DPFX(DPFPREP, 4, "Didn't load \"iphlpapi.dll\", guessing that device 0x%p can reach %u.%u.%u.%u (using subnet mask 0x%08x).",
  19038. pDevice,
  19039. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  19040. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  19041. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  19042. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  19043. dwSubnetMaskV4);
  19044. fResult = TRUE;
  19045. }
  19046. else
  19047. {
  19048. DPFX(DPFPREP, 4, "Didn't load \"iphlpapi.dll\", guessing that device 0x%p cannot reach %u.%u.%u.%u (using subnet mask 0x%08x).",
  19049. pDevice,
  19050. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  19051. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  19052. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  19053. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  19054. dwSubnetMaskV4);
  19055. fResult = FALSE;
  19056. }
  19057. Exit:
  19058. return fResult;
  19059. } // CNATHelpUPnP::IsAddressLocal
  19060. #undef DPF_MODNAME
  19061. #define DPF_MODNAME "CNATHelpUPnP::ExpireOldCachedMappings"
  19062. //=============================================================================
  19063. // CNATHelpUPnP::ExpireOldCachedMappings
  19064. //-----------------------------------------------------------------------------
  19065. //
  19066. // Description: Removes any cached mappings for any device which has
  19067. // expired.
  19068. //
  19069. // The object lock is assumed to be held.
  19070. //
  19071. // Arguments: None.
  19072. //
  19073. // Returns: None.
  19074. //=============================================================================
  19075. void CNATHelpUPnP::ExpireOldCachedMappings(void)
  19076. {
  19077. DWORD dwCurrentTime;
  19078. CBilink * pBilinkUPnPDevice;
  19079. CBilink * pCachedMaps;
  19080. CBilink * pBilinkCacheMap;
  19081. CCacheMap * pCacheMap;
  19082. CUPnPDevice * pUPnPDevice;
  19083. DPFX(DPFPREP, 7, "(0x%p) Enter", this);
  19084. dwCurrentTime = GETTIMESTAMP();
  19085. //
  19086. // Check the UPnP device cached mappings.
  19087. //
  19088. pBilinkUPnPDevice = this->m_blUPnPDevices.GetNext();
  19089. while (pBilinkUPnPDevice != &this->m_blUPnPDevices)
  19090. {
  19091. DNASSERT(! pBilinkUPnPDevice->IsEmpty());
  19092. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilinkUPnPDevice);
  19093. //
  19094. // Check the actual cached mappings.
  19095. //
  19096. pCachedMaps = pUPnPDevice->GetCachedMaps();
  19097. pBilinkCacheMap = pCachedMaps->GetNext();
  19098. while (pBilinkCacheMap != pCachedMaps)
  19099. {
  19100. DNASSERT(! pBilinkCacheMap->IsEmpty());
  19101. pCacheMap = CACHEMAP_FROM_BILINK(pBilinkCacheMap);
  19102. pBilinkCacheMap = pBilinkCacheMap->GetNext();
  19103. if ((int) (pCacheMap->GetExpirationTime() - dwCurrentTime) < 0)
  19104. {
  19105. DPFX(DPFPREP, 5, "UPnP device 0x%p cached mapping 0x%p has expired.",
  19106. pUPnPDevice, pCacheMap);
  19107. pCacheMap->m_blList.RemoveFromList();
  19108. delete pCacheMap;
  19109. }
  19110. }
  19111. pBilinkUPnPDevice = pBilinkUPnPDevice->GetNext();
  19112. }
  19113. DPFX(DPFPREP, 7, "(0x%p) Leave", this);
  19114. } // CNATHelpUPnP::ExpireOldCachedMappings
  19115. #ifdef WINNT
  19116. #undef DPF_MODNAME
  19117. #define DPF_MODNAME "CNATHelpUPnP::IsUPnPServiceDisabled"
  19118. //=============================================================================
  19119. // CNATHelpUPnP::IsUPnPServiceDisabled
  19120. //-----------------------------------------------------------------------------
  19121. //
  19122. // Description: Returns TRUE if at least one UPnP related service is
  19123. // disabled, FALSE if no UPnP related services are disabled.
  19124. //
  19125. // Arguments:
  19126. // char * szString - Pointer to string to print.
  19127. // int iStringLength - Length of string to print.
  19128. // char * szDescription - Description header for the transaction.
  19129. // CDevice * pDevice - Device handling transaction, or NULL if not
  19130. // known.
  19131. //
  19132. // Returns: BOOL
  19133. // TRUE - A UPnP related service was disabled.
  19134. // FALSE - No UPnP related services were disabled.
  19135. //=============================================================================
  19136. BOOL CNATHelpUPnP::IsUPnPServiceDisabled(void)
  19137. {
  19138. BOOL fResult = FALSE;
  19139. SC_HANDLE schSCManager = NULL;
  19140. DWORD dwTemp;
  19141. SC_HANDLE schService = NULL;
  19142. QUERY_SERVICE_CONFIG * pQueryServiceConfig = NULL;
  19143. DWORD dwQueryServiceConfigSize = 0;
  19144. DWORD dwError;
  19145. schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  19146. if (schSCManager == NULL)
  19147. {
  19148. #ifdef DBG
  19149. dwError = GetLastError();
  19150. DPFX(DPFPREP, 0, "Couldn't open SC Manager (err = %u)!", dwError);
  19151. #endif // DBG
  19152. goto Exit;
  19153. }
  19154. //
  19155. // Loop through each relevant service.
  19156. //
  19157. for(dwTemp = 0; dwTemp < (sizeof(c_tszUPnPServices) / sizeof(TCHAR*)); dwTemp++)
  19158. {
  19159. schService = OpenService(schSCManager, c_tszUPnPServices[dwTemp], SERVICE_QUERY_CONFIG);
  19160. if (schService != NULL)
  19161. {
  19162. do
  19163. {
  19164. if (QueryServiceConfig(schService,
  19165. pQueryServiceConfig,
  19166. dwQueryServiceConfigSize,
  19167. &dwQueryServiceConfigSize))
  19168. {
  19169. //
  19170. // Make sure the size written is valid.
  19171. //
  19172. if (dwQueryServiceConfigSize < sizeof(QUERY_SERVICE_CONFIG))
  19173. {
  19174. DPFX(DPFPREP, 0, "Got invalid service config size for \"%s\" (%u < %u)!",
  19175. c_tszUPnPServices[dwTemp], dwQueryServiceConfigSize, sizeof(QUERY_SERVICE_CONFIG));
  19176. goto Exit;
  19177. }
  19178. break;
  19179. }
  19180. //
  19181. // Otherwise, we failed. Make sure it's because our buffer was
  19182. // too small.
  19183. //
  19184. dwError = GetLastError();
  19185. if (dwError != ERROR_INSUFFICIENT_BUFFER)
  19186. {
  19187. DPFX(DPFPREP, 0, "Couldn't query \"%s\" service config (err = %u)!",
  19188. c_tszUPnPServices[dwTemp], dwError);
  19189. goto Exit;
  19190. }
  19191. //
  19192. // Make sure the size needed is valid.
  19193. //
  19194. if (dwQueryServiceConfigSize < sizeof(QUERY_SERVICE_CONFIG))
  19195. {
  19196. DPFX(DPFPREP, 0, "Got invalid service config size for \"%s\" (%u < %u)!",
  19197. c_tszUPnPServices[dwTemp], dwQueryServiceConfigSize, sizeof(QUERY_SERVICE_CONFIG));
  19198. goto Exit;
  19199. }
  19200. //
  19201. // (Re)-allocate the buffer.
  19202. //
  19203. if (pQueryServiceConfig != NULL)
  19204. {
  19205. DNFree(pQueryServiceConfig);
  19206. }
  19207. pQueryServiceConfig = (QUERY_SERVICE_CONFIG*) DNMalloc(dwQueryServiceConfigSize);
  19208. if (pQueryServiceConfig == NULL)
  19209. {
  19210. DPFX(DPFPREP, 0, "Couldn't allocate memory to query service config.");
  19211. goto Exit;
  19212. }
  19213. }
  19214. while (TRUE);
  19215. //
  19216. // If the service was disabled, we're done here.
  19217. //
  19218. if (pQueryServiceConfig->dwStartType == SERVICE_DISABLED)
  19219. {
  19220. DPFX(DPFPREP, 1, "The \"%s\" service has been disabled.",
  19221. c_tszUPnPServices[dwTemp]);
  19222. fResult = TRUE;
  19223. goto Exit;
  19224. }
  19225. DPFX(DPFPREP, 7, "The \"%s\" service is not disabled (start type = %u).",
  19226. c_tszUPnPServices[dwTemp], pQueryServiceConfig->dwStartType);
  19227. }
  19228. else
  19229. {
  19230. //
  19231. // Win2K doesn't have these services, so it will always fail.
  19232. //
  19233. #ifdef DBG
  19234. dwError = GetLastError();
  19235. DPFX(DPFPREP, 1, "Couldn't open \"%s\" service (err = %u), continuing.",
  19236. c_tszUPnPServices[dwTemp], dwError);
  19237. #endif // DBG
  19238. }
  19239. }
  19240. Exit:
  19241. if (pQueryServiceConfig != NULL)
  19242. {
  19243. DNFree(pQueryServiceConfig);
  19244. pQueryServiceConfig = NULL;
  19245. }
  19246. if (schService != NULL)
  19247. {
  19248. CloseServiceHandle(schService);
  19249. schService = NULL;
  19250. }
  19251. if (schSCManager != NULL)
  19252. {
  19253. CloseServiceHandle(schSCManager);
  19254. schSCManager = NULL;
  19255. }
  19256. return fResult;
  19257. } // CNATHelpUPnP::IsUPnPServiceDisabled
  19258. #endif // WINNT
  19259. #ifdef DBG
  19260. #undef DPF_MODNAME
  19261. #define DPF_MODNAME "CNATHelpUPnP::PrintUPnPTransactionToFile"
  19262. //=============================================================================
  19263. // CNATHelpUPnP::PrintUPnPTransactionToFile
  19264. //-----------------------------------------------------------------------------
  19265. //
  19266. // Description: Prints a given UPnP transaction to the file if logging is
  19267. // enabled.
  19268. //
  19269. // The object lock is assumed to be held.
  19270. //
  19271. // Arguments:
  19272. // char * szString - Pointer to string to print.
  19273. // int iStringLength - Length of string to print.
  19274. // char * szDescription - Description header for the transaction.
  19275. // CDevice * pDevice - Device handling transaction, or NULL if not
  19276. // known.
  19277. //
  19278. // Returns: None.
  19279. //=============================================================================
  19280. void CNATHelpUPnP::PrintUPnPTransactionToFile(const char * const szString,
  19281. const int iStringLength,
  19282. const char * const szDescription,
  19283. CDevice * const pDevice)
  19284. {
  19285. DNHANDLE hFile;
  19286. DWORD dwNumBytesWritten;
  19287. TCHAR tszHeaderPrefix[256];
  19288. DWORD dwError;
  19289. #ifdef UNICODE
  19290. char szHeaderPrefix[256];
  19291. #endif // UNICODE
  19292. //
  19293. // Lock the globals so nobody touches the string while we use it.
  19294. //
  19295. DNEnterCriticalSection(&g_csGlobalsLock);
  19296. //
  19297. // Only print it if UPnP transaction logging is turned on.
  19298. //
  19299. if (wcslen(g_wszUPnPTransactionLog) > 0)
  19300. {
  19301. #ifndef UNICODE
  19302. HRESULT hr;
  19303. char szUPnPTransactionLog[sizeof(g_wszUPnPTransactionLog) / sizeof(WCHAR)];
  19304. DWORD dwLength;
  19305. //
  19306. // Convert the Unicode file name/path into ANSI.
  19307. //
  19308. dwLength = sizeof(szUPnPTransactionLog) / sizeof(char);
  19309. hr = STR_WideToAnsi(g_wszUPnPTransactionLog,
  19310. -1, // NULL terminated
  19311. szUPnPTransactionLog,
  19312. &dwLength);
  19313. if (hr != S_OK)
  19314. {
  19315. DPFX(DPFPREP, 0, "Couldn't convert UPnP transaction log file string from Unicode to ANSI (err = 0x%lx)!",
  19316. hr);
  19317. hFile = DNINVALID_HANDLE_VALUE;
  19318. }
  19319. else
  19320. {
  19321. //
  19322. // Open the file if it exists, or create a new one if it doesn't.
  19323. //
  19324. hFile = DNCreateFile(szUPnPTransactionLog,
  19325. (GENERIC_READ | GENERIC_WRITE),
  19326. FILE_SHARE_READ,
  19327. NULL,
  19328. OPEN_ALWAYS,
  19329. 0,
  19330. NULL);
  19331. }
  19332. #else // UNICODE
  19333. //
  19334. // Open the file if it exists, or create a new one if it doesn't.
  19335. //
  19336. hFile = DNCreateFile(g_wszUPnPTransactionLog,
  19337. (GENERIC_READ | GENERIC_WRITE),
  19338. FILE_SHARE_READ,
  19339. NULL,
  19340. OPEN_ALWAYS,
  19341. 0,
  19342. NULL);
  19343. #endif // UNICODE
  19344. if (hFile == DNINVALID_HANDLE_VALUE)
  19345. {
  19346. dwError = GetLastError();
  19347. DPFX(DPFPREP, 0, "Couldn't open UPnP transaction log file, err = %u!",
  19348. dwError);
  19349. }
  19350. else
  19351. {
  19352. //
  19353. // Move the write pointer to the end of the file unless the file
  19354. // has exceeded the maximum size, in which case just start over.
  19355. // Ignore error.
  19356. //
  19357. if (GetFileSize(HANDLE_FROM_DNHANDLE(hFile), NULL) >= MAX_TRANSACTION_LOG_SIZE)
  19358. {
  19359. DPFX(DPFPREP, 0, "Transaction log maximum size exceeded, overwriting existing contents!");
  19360. SetFilePointer(HANDLE_FROM_DNHANDLE(hFile), 0, NULL, FILE_BEGIN);
  19361. }
  19362. else
  19363. {
  19364. SetFilePointer(HANDLE_FROM_DNHANDLE(hFile), 0, NULL, FILE_END);
  19365. }
  19366. //
  19367. // Write the descriptive header. Ignore errors.
  19368. //
  19369. if (pDevice != NULL)
  19370. {
  19371. IN_ADDR inaddr;
  19372. inaddr.S_un.S_addr = pDevice->GetLocalAddressV4();
  19373. wsprintf(tszHeaderPrefix,
  19374. _T("%u\t0x%lx\t0x%lx\t(0x%p, %u.%u.%u.%u) UPnP transaction \""),
  19375. GETTIMESTAMP(),
  19376. GetCurrentProcessId(),
  19377. GetCurrentThreadId(),
  19378. pDevice,
  19379. inaddr.S_un.S_un_b.s_b1,
  19380. inaddr.S_un.S_un_b.s_b2,
  19381. inaddr.S_un.S_un_b.s_b3,
  19382. inaddr.S_un.S_un_b.s_b4);
  19383. }
  19384. else
  19385. {
  19386. wsprintf(tszHeaderPrefix,
  19387. _T("%u\t0x%lx\t0x%lx\t(no device) UPnP transaction \""),
  19388. GETTIMESTAMP(),
  19389. GetCurrentProcessId(),
  19390. GetCurrentThreadId());
  19391. }
  19392. #ifdef UNICODE
  19393. STR_jkWideToAnsi(szHeaderPrefix,
  19394. tszHeaderPrefix,
  19395. (_tcslen(tszHeaderPrefix) + 1));
  19396. WriteFile(HANDLE_FROM_DNHANDLE(hFile), szHeaderPrefix, strlen(szHeaderPrefix), &dwNumBytesWritten, NULL);
  19397. #else // ! UNICODE
  19398. WriteFile(HANDLE_FROM_DNHANDLE(hFile), tszHeaderPrefix, _tcslen(tszHeaderPrefix), &dwNumBytesWritten, NULL);
  19399. #endif // ! UNICODE
  19400. WriteFile(HANDLE_FROM_DNHANDLE(hFile), szDescription, strlen(szDescription), &dwNumBytesWritten, NULL);
  19401. WriteFile(HANDLE_FROM_DNHANDLE(hFile), "\"\r\n", strlen("\"\r\n"), &dwNumBytesWritten, NULL);
  19402. //
  19403. // Write the transaction. Ignore error.
  19404. //
  19405. WriteFile(HANDLE_FROM_DNHANDLE(hFile), szString, iStringLength, &dwNumBytesWritten, NULL);
  19406. //
  19407. // Add blank space. Ignore error.
  19408. //
  19409. WriteFile(HANDLE_FROM_DNHANDLE(hFile), "\r\n\r\n", strlen("\r\n\r\n"), &dwNumBytesWritten, NULL);
  19410. //
  19411. // Truncate the log at this point in case we are overwriting
  19412. // existing contents. Ignore error.
  19413. //
  19414. SetEndOfFile(HANDLE_FROM_DNHANDLE(hFile));
  19415. //
  19416. // Close the file.
  19417. //
  19418. DNCloseHandle(hFile);
  19419. }
  19420. }
  19421. //
  19422. // Drop the globals lock.
  19423. //
  19424. DNLeaveCriticalSection(&g_csGlobalsLock);
  19425. } // CNATHelpUPnP::PrintUPnPTransactionToFile
  19426. #undef DPF_MODNAME
  19427. #define DPF_MODNAME "CNATHelpUPnP::DebugPrintCurrentStatus"
  19428. //=============================================================================
  19429. // CNATHelpUPnP::DebugPrintCurrentStatus
  19430. //-----------------------------------------------------------------------------
  19431. //
  19432. // Description: Prints all the devices and mappings to the debug log
  19433. // routines.
  19434. //
  19435. // The object lock is assumed to be held.
  19436. //
  19437. // Arguments: None.
  19438. //
  19439. // Returns: None.
  19440. //=============================================================================
  19441. void CNATHelpUPnP::DebugPrintCurrentStatus(void)
  19442. {
  19443. CBilink * pBilinkDevice;
  19444. CBilink * pBilinkRegisteredPort;
  19445. CDevice * pDevice;
  19446. CRegisteredPort * pRegisteredPort;
  19447. IN_ADDR inaddrTemp;
  19448. DWORD dwTemp;
  19449. SOCKADDR_IN * pasaddrinTemp;
  19450. SOCKADDR_IN * pasaddrinPrivate;
  19451. CUPnPDevice * pUPnPDevice;
  19452. SOCKADDR_IN * pasaddrinUPnPPublic;
  19453. DPFX(DPFPREP, 3, "Object flags = 0x%08x", this->m_dwFlags);
  19454. pBilinkDevice = this->m_blDevices.GetNext();
  19455. while (pBilinkDevice != &this->m_blDevices)
  19456. {
  19457. DNASSERT(! pBilinkDevice->IsEmpty());
  19458. pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
  19459. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  19460. DPFX(DPFPREP, 3, "Device 0x%p (%u.%u.%u.%u):",
  19461. pDevice,
  19462. inaddrTemp.S_un.S_un_b.s_b1,
  19463. inaddrTemp.S_un.S_un_b.s_b2,
  19464. inaddrTemp.S_un.S_un_b.s_b3,
  19465. inaddrTemp.S_un.S_un_b.s_b4);
  19466. //
  19467. // Print the search information. We should have detected it by now.
  19468. //
  19469. if (pDevice->IsPerformingRemoteUPnPDiscovery())
  19470. {
  19471. if (pDevice->GotRemoteUPnPDiscoveryConnReset())
  19472. {
  19473. DPFX(DPFPREP, 3, " Performed remote UPnP discovery (from port %u), but got conn reset.",
  19474. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19475. }
  19476. else
  19477. {
  19478. DPFX(DPFPREP, 3, " Performed remote UPnP discovery (from port %u).",
  19479. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19480. }
  19481. }
  19482. else
  19483. {
  19484. //DPFX(DPFPREP, 3, " Didn't perform remote UPnP discovery (from port %u).",
  19485. // NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19486. }
  19487. if (pDevice->IsPerformingLocalUPnPDiscovery())
  19488. {
  19489. if (pDevice->GotLocalUPnPDiscoveryConnReset())
  19490. {
  19491. DPFX(DPFPREP, 3, " Performed local UPnP discovery (from port %u), but got conn reset.",
  19492. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19493. }
  19494. else
  19495. {
  19496. DPFX(DPFPREP, 3, " Performed local UPnP discovery (from port %u).",
  19497. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19498. }
  19499. }
  19500. else
  19501. {
  19502. //DPFX(DPFPREP, 3, " Didn't perform local UPnP discovery (from port %u).",
  19503. // NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19504. }
  19505. #ifndef DPNBUILD_NOWINSOCK2
  19506. //
  19507. // Print the gateway information. We may not have detected it yet,
  19508. // that's okay.
  19509. //
  19510. if (pDevice->IsPrimaryDevice())
  19511. {
  19512. DPFX(DPFPREP, 3, " Primary device.");
  19513. }
  19514. else if (pDevice->IsSecondaryDevice())
  19515. {
  19516. DPFX(DPFPREP, 3, " Secondary device.");
  19517. }
  19518. else if (pDevice->HasNoGateway())
  19519. {
  19520. DPFX(DPFPREP, 3, " Has no gateway.");
  19521. }
  19522. else
  19523. {
  19524. DPFX(DPFPREP, 3, " No gateway information known.");
  19525. }
  19526. #endif // ! DPNBUILD_NOWINSOCK2
  19527. #ifndef DPNBUILD_NOHNETFWAPI
  19528. if (pDevice->IsHNetFirewalled())
  19529. {
  19530. DPFX(DPFPREP, 3, " HNet firewalled.");
  19531. }
  19532. else
  19533. {
  19534. DNASSERT(! pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall());
  19535. }
  19536. if (pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall())
  19537. {
  19538. DNASSERT(pDevice->IsHNetFirewalled());
  19539. DPFX(DPFPREP, 3, " UPnP discovery socket (port %u) mapped on HNet firewall.",
  19540. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19541. }
  19542. #endif // ! DPNBUILD_NOHNETFWAPI
  19543. pUPnPDevice = pDevice->GetUPnPDevice();
  19544. if (pUPnPDevice != NULL)
  19545. {
  19546. pasaddrinTemp = pUPnPDevice->GetControlAddress();
  19547. DPFX(DPFPREP, 3, " UPnP device (0x%p, ID = %u, control = %u.%u.%u.%u:%u).",
  19548. pUPnPDevice, pUPnPDevice->GetID(),
  19549. pasaddrinTemp->sin_addr.S_un.S_un_b.s_b1,
  19550. pasaddrinTemp->sin_addr.S_un.S_un_b.s_b2,
  19551. pasaddrinTemp->sin_addr.S_un.S_un_b.s_b3,
  19552. pasaddrinTemp->sin_addr.S_un.S_un_b.s_b4,
  19553. NTOHS(pasaddrinTemp->sin_port));
  19554. if (pasaddrinTemp->sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4())
  19555. {
  19556. DPFX(DPFPREP, 3, " Is local.");
  19557. }
  19558. DNASSERT(pUPnPDevice->IsReady());
  19559. if (pUPnPDevice->IsConnected())
  19560. {
  19561. DPFX(DPFPREP, 3, " Is connected.");
  19562. }
  19563. if (pUPnPDevice->DoesNotSupportAsymmetricMappings())
  19564. {
  19565. DPFX(DPFPREP, 3, " Does not support asymmetric mappings.");
  19566. }
  19567. if (pUPnPDevice->DoesNotSupportLeaseDurations())
  19568. {
  19569. DPFX(DPFPREP, 3, " Does not support lease durations.");
  19570. }
  19571. inaddrTemp.S_un.S_addr = pUPnPDevice->GetExternalIPAddressV4();
  19572. if (pUPnPDevice->GetExternalIPAddressV4() == 0)
  19573. {
  19574. DPFX(DPFPREP, 3, " Does not have a valid external IP address.");
  19575. }
  19576. else
  19577. {
  19578. DPFX(DPFPREP, 3, " Has external IP %u.%u.%u.%u.",
  19579. inaddrTemp.S_un.S_un_b.s_b1,
  19580. inaddrTemp.S_un.S_un_b.s_b2,
  19581. inaddrTemp.S_un.S_un_b.s_b3,
  19582. inaddrTemp.S_un.S_un_b.s_b4);
  19583. }
  19584. }
  19585. if (pDevice->m_blOwnedRegPorts.IsEmpty())
  19586. {
  19587. DPFX(DPFPREP, 3, " No registered port mappings.");
  19588. }
  19589. else
  19590. {
  19591. DPFX(DPFPREP, 3, " Registered port mappings:");
  19592. pBilinkRegisteredPort = pDevice->m_blOwnedRegPorts.GetNext();
  19593. while (pBilinkRegisteredPort != &pDevice->m_blOwnedRegPorts)
  19594. {
  19595. DNASSERT(! pBilinkRegisteredPort->IsEmpty());
  19596. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
  19597. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  19598. if ((pDevice->GetUPnPDevice() != NULL) &&
  19599. (! pRegisteredPort->IsUPnPPortUnavailable()))
  19600. {
  19601. if (pRegisteredPort->HasUPnPPublicAddresses())
  19602. {
  19603. pasaddrinUPnPPublic = pRegisteredPort->GetUPnPPublicAddressesArray();
  19604. }
  19605. else
  19606. {
  19607. pasaddrinUPnPPublic = NULL;
  19608. }
  19609. }
  19610. else
  19611. {
  19612. pasaddrinUPnPPublic = NULL;
  19613. }
  19614. DPFX(DPFPREP, 3, " Registered port 0x%p:",
  19615. pRegisteredPort);
  19616. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  19617. {
  19618. //
  19619. // Print private address.
  19620. //
  19621. DPFX(DPFPREP, 3, " %u-\tPrivate = %u.%u.%u.%u:%u",
  19622. dwTemp,
  19623. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b1,
  19624. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b2,
  19625. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b3,
  19626. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b4,
  19627. NTOHS(pasaddrinPrivate[dwTemp].sin_port));
  19628. //
  19629. // Print flags.
  19630. //
  19631. DPFX(DPFPREP, 3, " \tFlags = 0x%lx",
  19632. pRegisteredPort->GetFlags());
  19633. //
  19634. // Print UPnP information.
  19635. //
  19636. if (pasaddrinUPnPPublic != NULL)
  19637. {
  19638. if (pRegisteredPort->HasPermanentUPnPLease())
  19639. {
  19640. DPFX(DPFPREP, 3, " \tUPnP = %u.%u.%u.%u:%u, permanently leased",
  19641. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b1,
  19642. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b2,
  19643. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b3,
  19644. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b4,
  19645. NTOHS(pasaddrinUPnPPublic[dwTemp].sin_port));
  19646. }
  19647. else
  19648. {
  19649. DPFX(DPFPREP, 3, " \tUPnP = %u.%u.%u.%u:%u, lease expires at %u",
  19650. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b1,
  19651. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b2,
  19652. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b3,
  19653. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b4,
  19654. NTOHS(pasaddrinUPnPPublic[dwTemp].sin_port),
  19655. pRegisteredPort->GetUPnPLeaseExpiration());
  19656. }
  19657. }
  19658. else if (pRegisteredPort->IsUPnPPortUnavailable())
  19659. {
  19660. DPFX(DPFPREP, 3, " \tUPnP = port unavailable");
  19661. }
  19662. else if (pDevice->GetUPnPDevice() != NULL)
  19663. {
  19664. DPFX(DPFPREP, 3, " \tUPnP = not registered");
  19665. }
  19666. else
  19667. {
  19668. //
  19669. // No UPnP gateway device.
  19670. //
  19671. }
  19672. #ifndef DPNBUILD_NOHNETFWAPI
  19673. //
  19674. // Print firewall status.
  19675. //
  19676. if (pRegisteredPort->IsMappedOnHNetFirewall())
  19677. {
  19678. DNASSERT(pDevice->IsHNetFirewalled());
  19679. if (pRegisteredPort->IsHNetFirewallMappingBuiltIn())
  19680. {
  19681. DPFX(DPFPREP, 3, " \tHNet firewall = built-in mapping");
  19682. }
  19683. else
  19684. {
  19685. DPFX(DPFPREP, 3, " \tHNet firewall = mapped");
  19686. }
  19687. }
  19688. else if (pRegisteredPort->IsHNetFirewallPortUnavailable())
  19689. {
  19690. DNASSERT(! pRegisteredPort->IsMappedOnHNetFirewall());
  19691. DPFX(DPFPREP, 3, " \tHNet firewall = port unavailable");
  19692. }
  19693. else
  19694. {
  19695. //
  19696. // It is not mapped on the firewall.
  19697. //
  19698. DNASSERT(! pDevice->IsHNetFirewalled());
  19699. DNASSERT(! pRegisteredPort->IsMappedOnHNetFirewall());
  19700. DNASSERT(! pRegisteredPort->IsHNetFirewallMappingBuiltIn());
  19701. }
  19702. #endif // ! DPNBUILD_NOHNETFWAPI
  19703. }
  19704. pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
  19705. }
  19706. }
  19707. pBilinkDevice = pBilinkDevice->GetNext();
  19708. }
  19709. if (this->m_blUnownedPorts.IsEmpty())
  19710. {
  19711. DPFX(DPFPREP, 3, "No unowned registered port mappings.");
  19712. }
  19713. else
  19714. {
  19715. DPFX(DPFPREP, 3, "Unowned registered port mappings:");
  19716. pBilinkRegisteredPort = this->m_blUnownedPorts.GetNext();
  19717. while (pBilinkRegisteredPort != &this->m_blUnownedPorts)
  19718. {
  19719. DNASSERT(! pBilinkRegisteredPort->IsEmpty());
  19720. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
  19721. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  19722. DNASSERT(pRegisteredPort->GetOwningDevice() == NULL);
  19723. DNASSERT(! (pRegisteredPort->HasUPnPPublicAddresses()));
  19724. #ifndef DPNBUILD_NOHNETFWAPI
  19725. DNASSERT(! (pRegisteredPort->IsMappedOnHNetFirewall()));
  19726. #endif // ! DPNBUILD_NOHNETFWAPI
  19727. DPFX(DPFPREP, 3, " Registered port 0x%p:", pRegisteredPort);
  19728. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  19729. {
  19730. //
  19731. // Print private address.
  19732. //
  19733. DPFX(DPFPREP, 3, " %u-\tPrivate = %u.%u.%u.%u:%u",
  19734. dwTemp,
  19735. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b1,
  19736. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b2,
  19737. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b3,
  19738. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b4,
  19739. NTOHS(pasaddrinPrivate[dwTemp].sin_port));
  19740. //
  19741. // Print flags.
  19742. //
  19743. DPFX(DPFPREP, 3, " \tFlags = 0x%lx",
  19744. pRegisteredPort->GetFlags());
  19745. }
  19746. pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
  19747. }
  19748. }
  19749. } // CNATHelpUPnP::DebugPrintCurrentStatus
  19750. #ifndef DPNBUILD_NOHNETFWAPI
  19751. #undef DPF_MODNAME
  19752. #define DPF_MODNAME "CNATHelpUPnP::DebugPrintActiveFirewallMappings"
  19753. //=============================================================================
  19754. // CNATHelpUPnP::DebugPrintActiveFirewallMappings
  19755. //-----------------------------------------------------------------------------
  19756. //
  19757. // Description: Prints all the active firewall mapping registry entries to
  19758. // the debug log routines.
  19759. //
  19760. // Arguments: None.
  19761. //
  19762. // Returns: None.
  19763. //=============================================================================
  19764. void CNATHelpUPnP::DebugPrintActiveFirewallMappings(void)
  19765. {
  19766. HRESULT hr = DPNH_OK;
  19767. CRegistry RegObject;
  19768. DWORD dwIndex;
  19769. WCHAR wszValueName[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  19770. DWORD dwValueNameSize;
  19771. DPNHACTIVEFIREWALLMAPPING dpnhafm;
  19772. DWORD dwValueSize;
  19773. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  19774. DNHANDLE hNamedObject = NULL;
  19775. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  19776. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVEFIREWALLMAPPINGS,
  19777. FALSE,
  19778. TRUE,
  19779. TRUE,
  19780. DPN_KEY_ALL_ACCESS))
  19781. {
  19782. DPFX(DPFPREP, 1, "Couldn't open active firewall mapping key, not dumping entries (local instance = %u).",
  19783. this->m_dwInstanceKey);
  19784. }
  19785. else
  19786. {
  19787. //
  19788. // Walk the list of active mappings.
  19789. //
  19790. dwIndex = 0;
  19791. do
  19792. {
  19793. dwValueNameSize = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  19794. if (! RegObject.EnumValues(wszValueName, &dwValueNameSize, dwIndex))
  19795. {
  19796. //
  19797. // There was an error or there aren't any more keys. We're done.
  19798. //
  19799. break;
  19800. }
  19801. //
  19802. // Try reading that mapping's data.
  19803. //
  19804. dwValueSize = sizeof(dpnhafm);
  19805. if ((! RegObject.ReadBlob(wszValueName, (LPBYTE) (&dpnhafm), &dwValueSize)) ||
  19806. (dwValueSize != sizeof(dpnhafm)) ||
  19807. (dpnhafm.dwVersion != ACTIVE_MAPPING_VERSION))
  19808. {
  19809. DPFX(DPFPREP, 1, "Couldn't read \"%ls\" mapping value (index %u) or it was invalid! Ignoring.",
  19810. wszValueName, dwIndex);
  19811. }
  19812. else
  19813. {
  19814. //
  19815. // See if that DPNHUPNP instance is still around.
  19816. //
  19817. #ifndef WINCE
  19818. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  19819. {
  19820. wsprintf(tszObjectName, _T( "Global\\" ) INSTANCENAMEDOBJECT_FORMATSTRING, dpnhafm.dwInstanceKey);
  19821. }
  19822. else
  19823. #endif // ! WINCE
  19824. {
  19825. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, dpnhafm.dwInstanceKey);
  19826. }
  19827. hNamedObject = DNOpenEvent(SYNCHRONIZE, FALSE, tszObjectName);
  19828. if (hNamedObject != NULL)
  19829. {
  19830. //
  19831. // This is still an active mapping.
  19832. //
  19833. DPFX(DPFPREP, 5, "%u: Firewall mapping \"%ls\" belongs to instance %u (local instance = %u), which is still active.",
  19834. dwIndex, wszValueName, dpnhafm.dwInstanceKey,
  19835. this->m_dwInstanceKey);
  19836. DNCloseHandle(hNamedObject);
  19837. hNamedObject = NULL;
  19838. }
  19839. else
  19840. {
  19841. DPFX(DPFPREP, 5, "%u: Firewall mapping \"%ls\" belongs to instance %u (local instance = %u), which no longer exists.",
  19842. dwIndex, wszValueName, dpnhafm.dwInstanceKey,
  19843. this->m_dwInstanceKey);
  19844. }
  19845. }
  19846. //
  19847. // Move to next item.
  19848. //
  19849. dwIndex++;
  19850. }
  19851. while (TRUE);
  19852. //
  19853. // Close the registry object.
  19854. //
  19855. RegObject.Close();
  19856. DPFX(DPFPREP, 5, "Done reading %u registry entries (local instance = %u).",
  19857. dwIndex, this->m_dwInstanceKey);
  19858. }
  19859. } // CNATHelpUPnP::DebugPrintActiveFirewallMappings
  19860. #endif // ! DPNBUILD_NOHNETFWAPI
  19861. #undef DPF_MODNAME
  19862. #define DPF_MODNAME "CNATHelpUPnP::DebugPrintActiveNATMappings"
  19863. //=============================================================================
  19864. // CNATHelpUPnP::DebugPrintActiveNATMappings
  19865. //-----------------------------------------------------------------------------
  19866. //
  19867. // Description: Prints all the active NAT mapping registry entries to the
  19868. // debug log routines.
  19869. //
  19870. // Arguments: None.
  19871. //
  19872. // Returns: None.
  19873. //=============================================================================
  19874. void CNATHelpUPnP::DebugPrintActiveNATMappings(void)
  19875. {
  19876. HRESULT hr = DPNH_OK;
  19877. CRegistry RegObject;
  19878. DWORD dwIndex;
  19879. WCHAR wszValueName[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  19880. DWORD dwValueNameSize;
  19881. DPNHACTIVENATMAPPING dpnhanm;
  19882. DWORD dwValueSize;
  19883. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  19884. DNHANDLE hNamedObject = NULL;
  19885. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  19886. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVENATMAPPINGS,
  19887. FALSE,
  19888. TRUE,
  19889. TRUE,
  19890. DPN_KEY_ALL_ACCESS))
  19891. {
  19892. DPFX(DPFPREP, 1, "Couldn't open active NAT mapping key, not dumping entries (local instance = %u).",
  19893. this->m_dwInstanceKey);
  19894. }
  19895. else
  19896. {
  19897. //
  19898. // Walk the list of active mappings.
  19899. //
  19900. dwIndex = 0;
  19901. do
  19902. {
  19903. dwValueNameSize = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  19904. if (! RegObject.EnumValues(wszValueName, &dwValueNameSize, dwIndex))
  19905. {
  19906. //
  19907. // There was an error or there aren't any more keys. We're done.
  19908. //
  19909. break;
  19910. }
  19911. //
  19912. // Try reading that mapping's data.
  19913. //
  19914. dwValueSize = sizeof(dpnhanm);
  19915. if ((! RegObject.ReadBlob(wszValueName, (LPBYTE) (&dpnhanm), &dwValueSize)) ||
  19916. (dwValueSize != sizeof(dpnhanm)) ||
  19917. (dpnhanm.dwVersion != ACTIVE_MAPPING_VERSION))
  19918. {
  19919. DPFX(DPFPREP, 1, "Couldn't read \"%ls\" mapping value (index %u) or it was invalid! Ignoring.",
  19920. wszValueName, dwIndex);
  19921. }
  19922. else
  19923. {
  19924. //
  19925. // See if that DPNHUPNP instance is still around.
  19926. //
  19927. #ifndef WINCE
  19928. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  19929. {
  19930. wsprintf(tszObjectName, _T( "Global\\" ) INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  19931. }
  19932. else
  19933. #endif // ! WINCE
  19934. {
  19935. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  19936. }
  19937. hNamedObject = DNOpenEvent(SYNCHRONIZE, FALSE, tszObjectName);
  19938. if (hNamedObject != NULL)
  19939. {
  19940. //
  19941. // This is still an active mapping.
  19942. //
  19943. DPFX(DPFPREP, 5, "%u: NAT mapping \"%ls\" belongs to instance %u UPnP device %u (local instance = %u), which is still active.",
  19944. dwIndex, wszValueName, dpnhanm.dwInstanceKey,
  19945. dpnhanm.dwUPnPDeviceID, this->m_dwInstanceKey);
  19946. DNCloseHandle(hNamedObject);
  19947. hNamedObject = NULL;
  19948. }
  19949. else
  19950. {
  19951. DPFX(DPFPREP, 5, "%u: NAT mapping \"%ls\" belongs to instance %u UPnP device %u (local instance = %u), which no longer exists.",
  19952. dwIndex, wszValueName, dpnhanm.dwInstanceKey,
  19953. dpnhanm.dwUPnPDeviceID, this->m_dwInstanceKey);
  19954. }
  19955. }
  19956. //
  19957. // Move to next item.
  19958. //
  19959. dwIndex++;
  19960. }
  19961. while (TRUE);
  19962. //
  19963. // Close the registry object.
  19964. //
  19965. RegObject.Close();
  19966. DPFX(DPFPREP, 5, "Done reading %u registry entries (local instance = %u).",
  19967. dwIndex, this->m_dwInstanceKey);
  19968. }
  19969. } // CNATHelpUPnP::DebugPrintActiveNATMappings
  19970. #endif // DBG
  19971. #undef DPF_MODNAME
  19972. #define DPF_MODNAME "strtrim"
  19973. //=============================================================================
  19974. // strtrim
  19975. //-----------------------------------------------------------------------------
  19976. //
  19977. // Description: Removes surrounding white space from the given string. Taken
  19978. // from \nt\net\upnp\ssdp\common\ssdpparser\parser.cpp (author
  19979. // TingCai).
  19980. //
  19981. // Arguments:
  19982. // CHAR ** pszStr - Pointer to input string, and place to store resulting
  19983. // pointer.
  19984. //
  19985. // Returns: None.
  19986. //=============================================================================
  19987. VOID strtrim(CHAR ** pszStr)
  19988. {
  19989. CHAR *end;
  19990. CHAR *begin;
  19991. // Empty string. Nothing to do.
  19992. //
  19993. if (!(**pszStr))
  19994. {
  19995. return;
  19996. }
  19997. begin = *pszStr;
  19998. end = begin + strlen(*pszStr) - 1;
  19999. while (*begin == ' ' || *begin == '\t')
  20000. {
  20001. begin++;
  20002. }
  20003. *pszStr = begin;
  20004. while (*end == ' ' || *end == '\t')
  20005. {
  20006. end--;
  20007. }
  20008. *(end+1) = '\0';
  20009. } // strtrim
  20010. #ifdef WINCE
  20011. #undef DPF_MODNAME
  20012. #define DPF_MODNAME "GetExeName"
  20013. //=============================================================================
  20014. // GetExeName
  20015. //-----------------------------------------------------------------------------
  20016. //
  20017. // Description: Updates a path string to hold only the executable name
  20018. // contained in the path.
  20019. //
  20020. // Arguments:
  20021. // WCHAR * wszPath - Input path string, and place to store resulting
  20022. // string.
  20023. //
  20024. // Returns: None.
  20025. //=============================================================================
  20026. void GetExeName(WCHAR * wszPath)
  20027. {
  20028. WCHAR * pCurrent;
  20029. pCurrent = wszPath + wcslen(wszPath);
  20030. while (pCurrent > wszPath)
  20031. {
  20032. if ((*pCurrent) == L'\\')
  20033. {
  20034. break;
  20035. }
  20036. pCurrent--;
  20037. }
  20038. if (pCurrent != wszPath)
  20039. {
  20040. memcpy(wszPath, (pCurrent + 1), ((wcslen(pCurrent) + 1) * sizeof(WCHAR)));
  20041. }
  20042. } // GetExeName
  20043. #endif // WINCE