Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

24294 lines
627 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. #ifdef DBG
  41. #define MAX_TRANSACTION_LOG_SIZE (5 * 1024 * 1024) // 5 MB
  42. #endif // DBG
  43. #ifndef DPNBUILD_NOWINSOCK2
  44. //=============================================================================
  45. // WinSock 1 version of IP options
  46. //=============================================================================
  47. #define IP_MULTICAST_IF_WINSOCK1 2
  48. #define IP_MULTICAST_TTL_WINSOCK1 3
  49. #define IP_TTL_WINSOCK1 7
  50. #endif // ! DPNBUILD_NOWINSOCK2
  51. //=============================================================================
  52. // Macros
  53. //=============================================================================
  54. //#ifdef _X86
  55. #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
  56. #define NTOHS(x) ( (((x) >> 8) & 0x00FF) | (((x) << 8) & 0xFF00) )
  57. #define HTONS(x) NTOHS(x)
  58. //#endif _X86
  59. //=============================================================================
  60. // HTTP/SSDP/SOAP/UPnP header strings (from upnpmsgs.h)
  61. //=============================================================================
  62. const char * c_szResponseHeaders[] =
  63. {
  64. //
  65. // Headers used in discovery response
  66. //
  67. "CACHE-CONTROL",
  68. "DATE",
  69. "EXT",
  70. "LOCATION",
  71. "SERVER",
  72. "ST",
  73. "USN",
  74. //
  75. // Additional headers used in description response
  76. //
  77. "CONTENT-LANGUAGE",
  78. "CONTENT-LENGTH",
  79. "CONTENT-TYPE",
  80. "TRANSFER-ENCODING",
  81. //
  82. // Other known headers
  83. //
  84. "HOST",
  85. "NT",
  86. "NTS",
  87. "MAN",
  88. "MX",
  89. "AL",
  90. "CALLBACK",
  91. "TIMEOUT",
  92. "SCOPE",
  93. "SID",
  94. "SEQ",
  95. };
  96. //=============================================================================
  97. // Pre-built UPnP message strings (from upnpmsgs.h)
  98. //=============================================================================
  99. const char c_szUPnPMsg_Discover_Service_WANIPConnection[] = "M-SEARCH * " HTTP_VERSION EOL
  100. "HOST: " UPNP_DISCOVERY_MULTICAST_ADDRESS ":" UPNP_PORT_A EOL
  101. "MAN: \"ssdp:discover\"" EOL
  102. "MX: 2" EOL
  103. "ST: " URI_SERVICE_WANIPCONNECTION_A EOL
  104. EOL;
  105. const char c_szUPnPMsg_Discover_Service_WANPPPConnection[] = "M-SEARCH * " HTTP_VERSION EOL
  106. "HOST: " UPNP_DISCOVERY_MULTICAST_ADDRESS ":" UPNP_PORT_A EOL
  107. "MAN: \"ssdp:discover\"" EOL
  108. "MX: 2" EOL
  109. "ST: " URI_SERVICE_WANPPPCONNECTION_A EOL
  110. EOL;
  111. //
  112. // The disclaimer:
  113. //
  114. // A UPnP device may implement both the WANIPConnection and WANPPPConnection
  115. // services. We do not have any fancy logic to pick one, we just use the first
  116. // device that responds to our discovery requests, and use the first matching
  117. // service we encounter in the device description XML.
  118. //
  119. // Additionally, future UPnP devices may wish to present multiple device or
  120. // service instances with the intention that one client gets control of the
  121. // entire instance (additional clients would need to use a different instance).
  122. // It's not clear to me what a UPnP device (or client, for that matter) would
  123. // really gain by having such a setup. I imagine a new error code would have
  124. // to be returned whenever a client tried to control an instance that another
  125. // client already owned (don't ask me how it would know that, by picking the
  126. // first user or selectively responding to discovery requests, I guess).
  127. // Regardless, we do not currently support that. As noted above, we pick the
  128. // first instance and run with it.
  129. //
  130. //
  131. // Topmost <?xml> tag is considered optional for all XML and is ignored.
  132. //
  133. //
  134. // This solution assumes InternetGatewayDevice (not WANDevice or
  135. // WANConnectionDevice) will be the topmost item in the response. This is based
  136. // on the following UPnP spec excerpt:
  137. //
  138. // "Note that a single physical device may include multiple logical devices.
  139. // Multiple logical devices can be modeled as a single root device with
  140. // embedded devices (and services) or as multiple root devices (perhaps with
  141. // no embedded devices). In the former case, there is one UPnP device
  142. // description for the root device, and that device description contains a
  143. // description for all embedded devices. In the latter case, there are
  144. // multiple UPnP device descriptions, one for each root device."
  145. //
  146. const char * c_szElementStack_service[] =
  147. {
  148. "root",
  149. "device", // InternetGatewayDevice
  150. "deviceList",
  151. "device", // WANDevice
  152. "deviceList",
  153. "device", // WANConnectionDevice
  154. "serviceList",
  155. "service"
  156. };
  157. /*
  158. const char * c_szElementStack_QueryStateVariableResponse[] =
  159. {
  160. "Envelope",
  161. "Body",
  162. CONTROL_QUERYSTATEVARIABLE_A CONTROL_RESPONSESUFFIX_A
  163. };
  164. */
  165. const char * c_szElementStack_GetExternalIPAddressResponse[] =
  166. {
  167. "Envelope",
  168. "Body",
  169. ACTION_GETEXTERNALIPADDRESS_A CONTROL_RESPONSESUFFIX_A
  170. };
  171. const char * c_szElementStack_AddPortMappingResponse[] =
  172. {
  173. "Envelope",
  174. "Body",
  175. ACTION_ADDPORTMAPPING_A CONTROL_RESPONSESUFFIX_A
  176. };
  177. const char * c_szElementStack_GetSpecificPortMappingEntryResponse[] =
  178. {
  179. "Envelope",
  180. "Body",
  181. ACTION_GETSPECIFICPORTMAPPINGENTRY_A CONTROL_RESPONSESUFFIX_A
  182. };
  183. const char * c_szElementStack_DeletePortMappingResponse[] =
  184. {
  185. "Envelope",
  186. "Body",
  187. ACTION_DELETEPORTMAPPING_A CONTROL_RESPONSESUFFIX_A
  188. };
  189. const char * c_szElementStack_ControlResponseFailure[] =
  190. {
  191. "Envelope",
  192. "Body",
  193. "Fault",
  194. "detail",
  195. "UPnPError"
  196. };
  197. #ifdef WINNT
  198. //=============================================================================
  199. // Related UPnP services
  200. //=============================================================================
  201. TCHAR * c_tszUPnPServices[] =
  202. {
  203. _T("SSDPSRV"), // SSDP Discovery Service
  204. _T("UPNPHOST"), // Universal Plug and Play Device Host - we key off this even though it's for device hosts instead of control points
  205. };
  206. #endif // WINNT
  207. //=============================================================================
  208. // Local structures
  209. //=============================================================================
  210. typedef struct _CONTROLRESPONSEPARSECONTEXT
  211. {
  212. CONTROLRESPONSETYPE ControlResponseType; // type of control response expected
  213. CUPnPDevice * pUPnPDevice; // pointer to UPnP device being used
  214. DWORD dwHTTPResponseCode; // HTTP response code for this message
  215. PUPNP_CONTROLRESPONSE_INFO pControlResponseInfo; // place to info returned in control response
  216. } CONTROLRESPONSEPARSECONTEXT, * PCONTROLRESPONSEPARSECONTEXT;
  217. typedef struct _DPNHACTIVEFIREWALLMAPPING
  218. {
  219. DWORD dwVersion; // version identifier for this mapping
  220. DWORD dwInstanceKey; // key identifying DPNHUPNP instance that created this mapping
  221. DWORD dwFlags; // flags describing port being registered
  222. DWORD dwAddressV4; // address being mapped
  223. WORD wPort; // port being mapped
  224. } DPNHACTIVEFIREWALLMAPPING, * PDPNHACTIVEFIREWALLMAPPING;
  225. typedef struct _DPNHACTIVENATMAPPING
  226. {
  227. DWORD dwVersion; // version identifier for this mapping
  228. DWORD dwInstanceKey; // key identifying DPNHUPNP instance that created this mapping
  229. DWORD dwUPnPDeviceID; // identifier for particular UPnP device corresponding to this mapping (meaningful only to owning instance)
  230. DWORD dwFlags; // flags describing port being registered
  231. DWORD dwInternalAddressV4; // internal client address being mapped
  232. WORD wInternalPort; // internal client port being mapped
  233. DWORD dwExternalAddressV4; // external public address that was mapped
  234. WORD wExternalPort; // external public port that was mapped
  235. } DPNHACTIVENATMAPPING, * PDPNHACTIVENATMAPPING;
  236. //=============================================================================
  237. // Local functions
  238. //=============================================================================
  239. VOID strtrim(CHAR ** pszStr);
  240. #ifdef WINCE
  241. void GetExeName(WCHAR * wszPath);
  242. #endif // WINCE
  243. #undef DPF_MODNAME
  244. #define DPF_MODNAME "CNATHelpUPnP::CNATHelpUPnP"
  245. //=============================================================================
  246. // CNATHelpUPnP constructor
  247. //-----------------------------------------------------------------------------
  248. //
  249. // Description: Initializes the new CNATHelpUPnP object.
  250. //
  251. // Arguments:
  252. // BOOL fNotCreatedWithCOM - TRUE if this object is being instantiated
  253. // without COM, FALSE if it is through COM.
  254. //
  255. // Returns: None (the object).
  256. //=============================================================================
  257. CNATHelpUPnP::CNATHelpUPnP(const BOOL fNotCreatedWithCOM)
  258. {
  259. this->m_blList.Initialize();
  260. this->m_Sig[0] = 'N';
  261. this->m_Sig[1] = 'A';
  262. this->m_Sig[2] = 'T';
  263. this->m_Sig[3] = 'H';
  264. this->m_lRefCount = 1; // someone must have a pointer to this object
  265. if (fNotCreatedWithCOM)
  266. {
  267. this->m_dwFlags = NATHELPUPNPOBJ_NOTCREATEDWITHCOM;
  268. }
  269. else
  270. {
  271. this->m_dwFlags = 0;
  272. }
  273. this->m_hLongLockSemaphore = NULL;
  274. this->m_lNumLongLockWaitingThreads = 0;
  275. this->m_dwLockThreadID = 0;
  276. #ifndef DPNBUILD_NOWINSOCK2
  277. this->m_hAlertEvent = NULL;
  278. this->m_hAlertIOCompletionPort = NULL;
  279. this->m_dwAlertCompletionKey = 0;
  280. #endif // ! DPNBUILD_NOWINSOCK2
  281. this->m_blDevices.Initialize();
  282. this->m_blRegisteredPorts.Initialize();
  283. this->m_blUnownedPorts.Initialize();
  284. this->m_dwLastUpdateServerStatusTime = 0;
  285. this->m_dwNextPollInterval = 0;
  286. this->m_dwNumLeases = 0;
  287. this->m_dwEarliestLeaseExpirationTime = 0;
  288. this->m_blUPnPDevices.Initialize();
  289. this->m_dwInstanceKey = 0;
  290. this->m_dwCurrentUPnPDeviceID = 0;
  291. this->m_hMappingStillActiveNamedObject = NULL;
  292. #ifndef DPNBUILD_NOWINSOCK2
  293. this->m_hIpHlpApiDLL = NULL;
  294. this->m_pfnGetAdaptersInfo = NULL;
  295. this->m_pfnGetIpForwardTable = NULL;
  296. this->m_pfnGetBestRoute = NULL;
  297. this->m_hRasApi32DLL = NULL;
  298. this->m_pfnRasGetEntryHrasconnW = NULL;
  299. this->m_pfnRasGetProjectionInfo = NULL;
  300. this->m_sIoctls = INVALID_SOCKET;
  301. this->m_polAddressListChange = NULL;
  302. #endif // ! DPNBUILD_NOWINSOCK2
  303. this->m_hWinSockDLL = NULL;
  304. this->m_pfnWSAStartup = NULL;
  305. this->m_pfnWSACleanup = NULL;
  306. this->m_pfnWSAGetLastError = NULL;
  307. this->m_pfnsocket = NULL;
  308. this->m_pfnclosesocket = NULL;
  309. this->m_pfnbind = NULL;
  310. this->m_pfnsetsockopt = NULL;
  311. this->m_pfngetsockname = NULL;
  312. this->m_pfnselect = NULL;
  313. this->m_pfn__WSAFDIsSet = NULL;
  314. this->m_pfnrecvfrom = NULL;
  315. this->m_pfnsendto = NULL;
  316. this->m_pfngethostname = NULL;
  317. this->m_pfngethostbyname = NULL;
  318. this->m_pfninet_addr = NULL;
  319. #ifndef DPNBUILD_NOWINSOCK2
  320. this->m_pfnWSASocketA = NULL;
  321. this->m_pfnWSAIoctl = NULL;
  322. this->m_pfnWSAGetOverlappedResult = NULL;
  323. #endif // ! DPNBUILD_NOWINSOCK2
  324. this->m_pfnioctlsocket = NULL;
  325. this->m_pfnconnect = NULL;
  326. this->m_pfnshutdown = NULL;
  327. this->m_pfnsend = NULL;
  328. this->m_pfnrecv = NULL;
  329. #ifdef DBG
  330. this->m_pfngetsockopt = NULL;
  331. this->m_dwNumDeviceAdds = 0;
  332. this->m_dwNumDeviceRemoves = 0;
  333. this->m_dwNumServerFailures = 0;
  334. #endif // DBG
  335. } // CNATHelpUPnP::CNATHelpUPnP
  336. #undef DPF_MODNAME
  337. #define DPF_MODNAME "CNATHelpUPnP::~CNATHelpUPnP"
  338. //=============================================================================
  339. // CNATHelpUPnP destructor
  340. //-----------------------------------------------------------------------------
  341. //
  342. // Description: Frees the CNATHelpUPnP object.
  343. //
  344. // Arguments: None.
  345. //
  346. // Returns: None.
  347. //=============================================================================
  348. CNATHelpUPnP::~CNATHelpUPnP(void)
  349. {
  350. DPFX(DPFPREP, 8, "(0x%p) NumDeviceAdds = %u, NumDeviceRemoves = %u, NumServerFailures = %u",
  351. this, this->m_dwNumDeviceAdds, this->m_dwNumDeviceRemoves,
  352. this->m_dwNumServerFailures);
  353. DNASSERT(this->m_blList.IsEmpty());
  354. DNASSERT(this->m_lRefCount == 0);
  355. DNASSERT((this->m_dwFlags & ~NATHELPUPNPOBJ_NOTCREATEDWITHCOM) == 0);
  356. DNASSERT(this->m_hLongLockSemaphore == NULL);
  357. DNASSERT(this->m_lNumLongLockWaitingThreads == 0);
  358. DNASSERT(this->m_dwLockThreadID == 0);
  359. #ifndef DPNBUILD_NOWINSOCK2
  360. DNASSERT(this->m_hAlertEvent == NULL);
  361. DNASSERT(this->m_hAlertIOCompletionPort == NULL);
  362. #endif // ! DPNBUILD_NOWINSOCK2
  363. DNASSERT(this->m_blDevices.IsEmpty());
  364. DNASSERT(this->m_blRegisteredPorts.IsEmpty());
  365. DNASSERT(this->m_blUnownedPorts.IsEmpty());
  366. DNASSERT(this->m_dwNumLeases == 0);
  367. DNASSERT(this->m_blUPnPDevices.IsEmpty());
  368. DNASSERT(this->m_hMappingStillActiveNamedObject == NULL);
  369. #ifndef DPNBUILD_NOWINSOCK2
  370. DNASSERT(this->m_hIpHlpApiDLL == NULL);
  371. DNASSERT(this->m_hRasApi32DLL == NULL);
  372. DNASSERT(this->m_hWinSockDLL == NULL);
  373. DNASSERT(this->m_sIoctls == INVALID_SOCKET);
  374. DNASSERT(this->m_polAddressListChange == NULL);
  375. #endif // ! DPNBUILD_NOWINSOCK2
  376. //
  377. // For grins, change the signature before deleting the object.
  378. //
  379. this->m_Sig[3] = 'h';
  380. } // CNATHelpUPnP::~CNATHelpUPnP
  381. #undef DPF_MODNAME
  382. #define DPF_MODNAME "CNATHelpUPnP::QueryInterface"
  383. //=============================================================================
  384. // CNATHelpUPnP::QueryInterface
  385. //-----------------------------------------------------------------------------
  386. //
  387. // Description: Retrieves a new reference for an interfaces supported by this
  388. // CNATHelpUPnP object.
  389. //
  390. // Arguments:
  391. // REFIID riid - Reference to interface ID GUID.
  392. // LPVOID * ppvObj - Place to store pointer to object.
  393. //
  394. // Returns: HRESULT
  395. // S_OK - Returning a valid interface pointer.
  396. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  397. // DPNHERR_INVALIDPOINTER - The destination pointer is invalid.
  398. // E_NOINTERFACE - Invalid interface was specified.
  399. //=============================================================================
  400. STDMETHODIMP CNATHelpUPnP::QueryInterface(REFIID riid, LPVOID * ppvObj)
  401. {
  402. HRESULT hr = DPNH_OK;
  403. DPFX(DPFPREP, 3, "(0x%p) Parameters: (REFIID, 0x%p)", this, ppvObj);
  404. //
  405. // Validate the object.
  406. //
  407. if (! this->IsValidObject())
  408. {
  409. DPFX(DPFPREP, 0, "Invalid NATHelper object!");
  410. hr = DPNHERR_INVALIDOBJECT;
  411. goto Failure;
  412. }
  413. //
  414. // Validate the parameters.
  415. //
  416. if ((! IsEqualIID(riid, IID_IUnknown)) &&
  417. (! IsEqualIID(riid, IID_IDirectPlayNATHelp)))
  418. {
  419. DPFX(DPFPREP, 0, "Unsupported interface!");
  420. hr = E_NOINTERFACE;
  421. goto Failure;
  422. }
  423. if ((ppvObj == NULL) ||
  424. (IsBadWritePtr(ppvObj, sizeof(void*))))
  425. {
  426. DPFX(DPFPREP, 0, "Invalid interface pointer specified!");
  427. hr = DPNHERR_INVALIDPOINTER;
  428. goto Failure;
  429. }
  430. //
  431. // Add a reference, and return the interface pointer (which is actually
  432. // just the object pointer, they line up because CNATHelpUPnP inherits from
  433. // the interface declaration).
  434. //
  435. this->AddRef();
  436. (*ppvObj) = this;
  437. Exit:
  438. DPFX(DPFPREP, 3, "(0x%p) Returning: [0x%lx]", this, hr);
  439. return hr;
  440. Failure:
  441. goto Exit;
  442. } // CNATHelpUPnP::QueryInterface
  443. #undef DPF_MODNAME
  444. #define DPF_MODNAME "CNATHelpUPnP::AddRef"
  445. //=============================================================================
  446. // CNATHelpUPnP::AddRef
  447. //-----------------------------------------------------------------------------
  448. //
  449. // Description: Adds a reference to this CNATHelpUPnP object.
  450. //
  451. // Arguments: None.
  452. //
  453. // Returns: New refcount.
  454. //=============================================================================
  455. STDMETHODIMP_(ULONG) CNATHelpUPnP::AddRef(void)
  456. {
  457. LONG lRefCount;
  458. DNASSERT(this->IsValidObject());
  459. //
  460. // There must be at least 1 reference to this object, since someone is
  461. // calling AddRef.
  462. //
  463. DNASSERT(this->m_lRefCount > 0);
  464. lRefCount = InterlockedIncrement(&this->m_lRefCount);
  465. DPFX(DPFPREP, 3, "[0x%p] RefCount [0x%lx]", this, lRefCount);
  466. return lRefCount;
  467. } // CNATHelpUPnP::AddRef
  468. #undef DPF_MODNAME
  469. #define DPF_MODNAME "CNATHelpUPnP::Release"
  470. //=============================================================================
  471. // CNATHelpUPnP::Release
  472. //-----------------------------------------------------------------------------
  473. //
  474. // Description: Removes a reference to this CNATHelpUPnP object. When the
  475. // refcount reaches 0, this object is destroyed.
  476. // You must NULL out your pointer to this object after calling
  477. // this function.
  478. //
  479. // Arguments: None.
  480. //
  481. // Returns: New refcount.
  482. //=============================================================================
  483. STDMETHODIMP_(ULONG) CNATHelpUPnP::Release(void)
  484. {
  485. LONG lRefCount;
  486. DNASSERT(this->IsValidObject());
  487. //
  488. // There must be at least 1 reference to this object, since someone is
  489. // calling Release.
  490. //
  491. DNASSERT(this->m_lRefCount > 0);
  492. lRefCount = InterlockedDecrement(&this->m_lRefCount);
  493. //
  494. // Was that the last reference? If so, we're going to destroy this object.
  495. //
  496. if (lRefCount == 0)
  497. {
  498. DPFX(DPFPREP, 3, "[0x%p] RefCount hit 0, destroying object.", this);
  499. //
  500. // First pull it off the global list.
  501. //
  502. DNEnterCriticalSection(&g_csGlobalsLock);
  503. this->m_blList.RemoveFromList();
  504. DNASSERT(g_lOutstandingInterfaceCount > 0);
  505. g_lOutstandingInterfaceCount--; // update count so DLL can unload now works correctly
  506. DNLeaveCriticalSection(&g_csGlobalsLock);
  507. //
  508. // Make sure it's closed.
  509. //
  510. if (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED)
  511. {
  512. //
  513. // Assert so that the user can fix his/her broken code!
  514. //
  515. DNASSERT(! "DirectPlayNATHelpUPNP object being released without calling Close first!");
  516. //
  517. // Then go ahead and do the right thing. Ignore error, we can't do
  518. // much about it.
  519. //
  520. this->Close(0);
  521. }
  522. //
  523. // Then uninitialize the object.
  524. //
  525. this->UninitializeObject();
  526. //
  527. // Finally delete this (!) object.
  528. //
  529. delete this;
  530. }
  531. else
  532. {
  533. DPFX(DPFPREP, 3, "[0x%p] RefCount [0x%lx]", this, lRefCount);
  534. }
  535. return lRefCount;
  536. } // CNATHelpUPnP::Release
  537. #undef DPF_MODNAME
  538. #define DPF_MODNAME "CNATHelpUPnP::Initialize"
  539. //=============================================================================
  540. // CNATHelpUPnP::Initialize
  541. //-----------------------------------------------------------------------------
  542. //
  543. // Description: Prepares the object for use. No attempt is made to contact
  544. // any Internet gateway servers at this time. The user should
  545. // call GetCaps with the DPNHGETCAPS_UPDATESERVERSTATUS flag to
  546. // search for a server.
  547. //
  548. // Initialize must be called before using any other function,
  549. // and must be balanced with a call to Close. Initialize can only
  550. // be called once unless Close returns it to the uninitialized
  551. // state.
  552. //
  553. // One of DPNHINITIALIZE_DISABLEREMOTENATSUPPORT or
  554. // DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT may be specified,
  555. // but not both.
  556. //
  557. // Arguments:
  558. // DWORD dwFlags - Flags to use when initializing.
  559. //
  560. // Returns: HRESULT
  561. // DPNH_OK - Initialization was successful.
  562. // DPNHERR_ALREADYINITIALIZED - Initialize has already been called.
  563. // DPNHERR_GENERIC - An error occurred while initializing.
  564. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  565. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  566. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  567. // DPNHERR_OUTOFMEMORY - There is not enough memory to initialize.
  568. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  569. // thread.
  570. //=============================================================================
  571. STDMETHODIMP CNATHelpUPnP::Initialize(const DWORD dwFlags)
  572. {
  573. HRESULT hr;
  574. BOOL fHaveLock = FALSE;
  575. BOOL fSetFlags = FALSE;
  576. #ifndef WINCE
  577. OSVERSIONINFO osvi;
  578. #endif // ! WINCE
  579. BOOL fWinSockStarted = FALSE;
  580. WSADATA wsadata;
  581. int iError;
  582. #ifndef DPNBUILD_NOWINSOCK2
  583. SOCKADDR_IN saddrinTemp;
  584. #endif // ! DPNBUILD_NOWINSOCK2
  585. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  586. PSECURITY_ATTRIBUTES pSecurityAttributes;
  587. DWORD dwTry;
  588. #ifdef WINNT
  589. SID_IDENTIFIER_AUTHORITY SidIdentifierAuthorityWorld = SECURITY_WORLD_SID_AUTHORITY;
  590. PSID pSid = NULL;
  591. DWORD dwAclLength;
  592. ACL * pAcl = NULL;
  593. BYTE abSecurityDescriptorBuffer[SECURITY_DESCRIPTOR_MIN_LENGTH];
  594. SECURITY_ATTRIBUTES SecurityAttributes;
  595. #endif // WINNT
  596. #ifdef DBG
  597. DWORD dwError;
  598. #endif // DBG
  599. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%lx)", this, dwFlags);
  600. #ifndef WINCE
  601. //
  602. // Print info about the current build.
  603. //
  604. #ifdef WINNT
  605. DPFX(DPFPREP, 7, "Build type = NT, platform = %s",
  606. ((DNGetOSType() == VER_PLATFORM_WIN32_NT) ? _T("NT") : _T("9x")));
  607. #else // ! WINNT
  608. DPFX(DPFPREP, 7, "Build type = 9x, platform = %s, filedate = %s",
  609. ((DNGetOSType() == VER_PLATFORM_WIN32_NT) ? _T("NT") : _T("9x")));
  610. #endif // ! WINNT
  611. #endif // ! WINCE
  612. //
  613. // Validate the object.
  614. //
  615. if (! this->IsValidObject())
  616. {
  617. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  618. hr = DPNHERR_INVALIDOBJECT;
  619. //
  620. // Skip the failure cleanup code, we haven't set anything up.
  621. //
  622. goto Exit;
  623. }
  624. //
  625. // Validate the parameters.
  626. //
  627. if (dwFlags & ~(DPNHINITIALIZE_DISABLEGATEWAYSUPPORT | DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT))
  628. {
  629. DPFX(DPFPREP, 0, "Invalid flags specified!");
  630. hr = DPNHERR_INVALIDFLAGS;
  631. //
  632. // Skip the failure cleanup code, we haven't set anything up.
  633. //
  634. goto Exit;
  635. }
  636. //
  637. // Both flags cannot be specified at the same time. If the caller doesn't
  638. // want any NAT functionality, why use this object all?
  639. //
  640. if ((dwFlags & (DPNHINITIALIZE_DISABLEGATEWAYSUPPORT | DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT)) == (DPNHINITIALIZE_DISABLEGATEWAYSUPPORT | DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT))
  641. {
  642. DPFX(DPFPREP, 0, "Either DISABLEGATEWAYSUPPORT flag or DISABLELOCALFIREWALLSUPPORT flag can be used, but not both!");
  643. hr = DPNHERR_INVALIDFLAGS;
  644. //
  645. // Skip the failure cleanup code, we haven't set anything up.
  646. //
  647. goto Exit;
  648. }
  649. //
  650. // Attempt to take the lock, but be prepared for the re-entrancy error.
  651. //
  652. hr = this->TakeLock();
  653. if (hr != DPNH_OK)
  654. {
  655. DPFX(DPFPREP, 0, "Could not lock object!");
  656. //
  657. // Skip the failure cleanup code, we haven't set anything up.
  658. //
  659. goto Exit;
  660. }
  661. fHaveLock = TRUE;
  662. //
  663. // Make sure object is in right state.
  664. //
  665. if ((this->m_dwFlags & ~NATHELPUPNPOBJ_NOTCREATEDWITHCOM) != 0)
  666. {
  667. DPFX(DPFPREP, 0, "Object already initialized!");
  668. hr = DPNHERR_ALREADYINITIALIZED;
  669. //
  670. // Skip the failure cleanup code, we haven't set anything up.
  671. //
  672. goto Exit;
  673. }
  674. //
  675. // Read in the manual override settings from the registry
  676. //
  677. ReadRegistrySettings();
  678. //
  679. // We're not completely initialized yet, but set the flag(s) now.
  680. //
  681. this->m_dwFlags |= NATHELPUPNPOBJ_INITIALIZED;
  682. fSetFlags = TRUE;
  683. //
  684. // Store the user's settings.
  685. //
  686. if (dwFlags & DPNHINITIALIZE_DISABLEGATEWAYSUPPORT)
  687. {
  688. DPFX(DPFPREP, 1, "User requested that Internet gateways not be supported.");
  689. }
  690. else
  691. {
  692. this->m_dwFlags |= NATHELPUPNPOBJ_USEUPNP;
  693. }
  694. #ifndef DPNBUILD_NOHNETFWAPI
  695. if (dwFlags & DPNHINITIALIZE_DISABLELOCALFIREWALLSUPPORT)
  696. {
  697. DPFX(DPFPREP, 1, "User requested that local firewalls not be supported.");
  698. }
  699. else
  700. {
  701. this->m_dwFlags |= NATHELPUPNPOBJ_USEHNETFWAPI;
  702. }
  703. #endif // ! DPNBUILD_NOHNETFWAPI
  704. switch (g_dwUPnPMode)
  705. {
  706. case OVERRIDEMODE_FORCEON:
  707. {
  708. //
  709. // Force UPnP on.
  710. //
  711. DPFX(DPFPREP, 1, "Forcing UPnP support on.");
  712. this->m_dwFlags |= NATHELPUPNPOBJ_USEUPNP;
  713. break;
  714. }
  715. case OVERRIDEMODE_FORCEOFF:
  716. {
  717. //
  718. // Force UPnP off.
  719. //
  720. DPFX(DPFPREP, 1, "Forcing UPnP support off.");
  721. this->m_dwFlags &= ~NATHELPUPNPOBJ_USEUPNP;
  722. break;
  723. }
  724. default:
  725. {
  726. //
  727. // Leave UPnP settings as they were set by the application.
  728. //
  729. #ifdef WINNT
  730. //
  731. // But if UPnP related service(s) are disabled, we'll take that as
  732. // our cue to not use UPnP NAT traversal even though we don't
  733. // actually use those services. We assume the user wanted to
  734. // squelch all SSDP/UPnP activity. It can still be forced back on
  735. // with a reg key, though, as indicated by the other switch cases.
  736. //
  737. if (this->IsUPnPServiceDisabled())
  738. {
  739. DPFX(DPFPREP, 1, "Not using UPnP because a related service was disabled.");
  740. this->m_dwFlags &= ~NATHELPUPNPOBJ_USEUPNP;
  741. }
  742. #endif // WINNT
  743. break;
  744. }
  745. }
  746. #ifndef DPNBUILD_NOHNETFWAPI
  747. switch (g_dwHNetFWAPIMode)
  748. {
  749. case OVERRIDEMODE_FORCEON:
  750. {
  751. //
  752. // Force HNet firewall API on.
  753. //
  754. DPFX(DPFPREP, 1, "Forcing HNet firewall API support on.");
  755. this->m_dwFlags |= NATHELPUPNPOBJ_USEHNETFWAPI;
  756. break;
  757. }
  758. case OVERRIDEMODE_FORCEOFF:
  759. {
  760. //
  761. // Force HNet firewall API off.
  762. //
  763. DPFX(DPFPREP, 1, "Forcing HNet firewall API support off.");
  764. this->m_dwFlags &= ~NATHELPUPNPOBJ_USEHNETFWAPI;
  765. break;
  766. }
  767. default:
  768. {
  769. //
  770. // Leave HNet firewall API settings alone.
  771. //
  772. break;
  773. }
  774. }
  775. #endif // ! DPNBUILD_NOHNETFWAPI
  776. #ifndef WINCE
  777. //
  778. // Determine whether we're on a Win2K or higher NT OS, and if so, use the
  779. // "Global\\" prefix for named kernel objects so we can have Terminal
  780. // Server and Fast User Switching support.
  781. //
  782. ZeroMemory(&osvi, sizeof(osvi));
  783. osvi.dwOSVersionInfoSize = sizeof(osvi);
  784. if (GetVersionEx(&osvi))
  785. {
  786. if ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) &&
  787. (osvi.dwMajorVersion >= 5))
  788. {
  789. DPFX(DPFPREP, 8, "Running Win2K or higher NT OS, using \"Global\\\" prefix.");
  790. this->m_dwFlags |= NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX;
  791. }
  792. #ifdef DBG
  793. else
  794. {
  795. DPFX(DPFPREP, 8, "Not on NT, or its pre-Win2K, not using \"Global\\\" prefix.");
  796. }
  797. #endif // DBG
  798. }
  799. #ifdef DBG
  800. else
  801. {
  802. dwError = GetLastError();
  803. DPFX(DPFPREP, 0, "Couldn't get OS version information (err = %u)! Not using \"Global\\\" prefix.",
  804. dwError);
  805. }
  806. #endif // DBG
  807. #endif // ! WINCE
  808. #ifdef DPNBUILD_NOWINSOCK2
  809. #if defined(WINCE) && !defined(WINCE_ON_DESKTOP)
  810. this->m_hWinSockDLL = LoadLibrary( _T("winsock.dll") );
  811. #else // ! WINCE
  812. this->m_hWinSockDLL = LoadLibrary( _T("wsock32.dll") );
  813. #endif // ! WINCE
  814. if (this->m_hWinSockDLL == NULL)
  815. {
  816. #ifdef DBG
  817. dwError = GetLastError();
  818. DPFX(DPFPREP, 0, "Couldn't load WinSock 1 DLL (err = 0x%lx)!.",
  819. dwError);
  820. #endif // DBG
  821. hr = DPNHERR_GENERIC;
  822. goto Failure;
  823. }
  824. #else // ! DPNBUILD_NOWINSOCK2
  825. //
  826. // Try loading the IP helper DLL.
  827. //
  828. this->m_hIpHlpApiDLL = LoadLibrary( _T("iphlpapi.dll") );
  829. if (this->m_hIpHlpApiDLL == NULL)
  830. {
  831. #ifdef DBG
  832. dwError = GetLastError();
  833. DPFX(DPFPREP, 1, "Unable to load \"iphlpapi.dll\" (error = 0x%lx).",
  834. dwError);
  835. #endif // DBG
  836. //
  837. // That's not fatal, we can still function.
  838. //
  839. }
  840. else
  841. {
  842. //
  843. // Load the functions we'll use.
  844. //
  845. this->m_pfnGetAdaptersInfo = (PFN_GETADAPTERSINFO) GetProcAddress(this->m_hIpHlpApiDLL,
  846. _TWINCE("GetAdaptersInfo"));
  847. if (this->m_pfnGetAdaptersInfo == NULL)
  848. {
  849. #ifdef DBG
  850. dwError = GetLastError();
  851. DPFX(DPFPREP, 0, "Unable to get \"GetAdaptersInfo\" function (error = 0x%lx)!",
  852. dwError);
  853. #endif // DBG
  854. goto Exit;
  855. }
  856. this->m_pfnGetIpForwardTable = (PFN_GETIPFORWARDTABLE) GetProcAddress(this->m_hIpHlpApiDLL,
  857. _TWINCE("GetIpForwardTable"));
  858. if (this->m_pfnGetIpForwardTable == NULL)
  859. {
  860. #ifdef DBG
  861. dwError = GetLastError();
  862. DPFX(DPFPREP, 0, "Unable to get \"GetIpForwardTable\" function (error = 0x%lx)!",
  863. dwError);
  864. #endif // DBG
  865. goto Exit;
  866. }
  867. this->m_pfnGetBestRoute = (PFN_GETBESTROUTE) GetProcAddress(this->m_hIpHlpApiDLL,
  868. _TWINCE("GetBestRoute"));
  869. if (this->m_pfnGetBestRoute == NULL)
  870. {
  871. #ifdef DBG
  872. dwError = GetLastError();
  873. DPFX(DPFPREP, 0, "Unable to get \"GetBestRoute\" function (error = 0x%lx)!",
  874. dwError);
  875. #endif // DBG
  876. goto Exit;
  877. }
  878. }
  879. //
  880. // Try loading the RAS API DLL.
  881. //
  882. this->m_hRasApi32DLL = LoadLibrary( _T("rasapi32.dll") );
  883. if (this->m_hRasApi32DLL == NULL)
  884. {
  885. #ifdef DBG
  886. dwError = GetLastError();
  887. DPFX(DPFPREP, 1, "Unable to load \"rasapi32.dll\" (error = 0x%lx).",
  888. dwError);
  889. #endif // DBG
  890. //
  891. // That's not fatal, we can still function.
  892. //
  893. }
  894. else
  895. {
  896. //
  897. // Load the functions we'll use.
  898. //
  899. this->m_pfnRasGetEntryHrasconnW = (PFN_RASGETENTRYHRASCONNW) GetProcAddress(this->m_hRasApi32DLL,
  900. _TWINCE("RasGetEntryHrasconnW"));
  901. if (this->m_pfnRasGetEntryHrasconnW == NULL)
  902. {
  903. //
  904. // This function does not exist on non-NT platforms. That's fine,
  905. // just dump the DLL handle so we don't try to use it.
  906. //
  907. #ifdef DBG
  908. dwError = GetLastError();
  909. DPFX(DPFPREP, 1, "Unable to get \"RasGetEntryHrasconnW\" function (error = 0x%lx), forgetting RAS DLL.",
  910. dwError);
  911. #endif // DBG
  912. FreeLibrary(this->m_hRasApi32DLL);
  913. this->m_hRasApi32DLL = NULL;
  914. }
  915. else
  916. {
  917. this->m_pfnRasGetProjectionInfo = (PFN_RASGETPROJECTIONINFO) GetProcAddress(this->m_hRasApi32DLL,
  918. #ifdef UNICODE
  919. _TWINCE("RasGetProjectionInfoW"));
  920. #else // ! UNICODE
  921. _TWINCE("RasGetProjectionInfoA"));
  922. #endif // ! UNICODE
  923. if (this->m_pfnRasGetProjectionInfo == NULL)
  924. {
  925. #ifdef DBG
  926. dwError = GetLastError();
  927. DPFX(DPFPREP, 0, "Unable to get \"RasGetProjectionInfoA/W\" function (error = 0x%lx)!",
  928. dwError);
  929. #endif // DBG
  930. goto Exit;
  931. }
  932. }
  933. }
  934. //
  935. // Load WinSock because we may be using our private UPnP implementation, or
  936. // we just need to get the devices.
  937. //
  938. this->m_hWinSockDLL = LoadLibrary( _T("ws2_32.dll") );
  939. if (this->m_hWinSockDLL == NULL)
  940. {
  941. #ifdef DBG
  942. dwError = GetLastError();
  943. DPFX(DPFPREP, 1, "Couldn't load \"ws2_32.dll\" (err = 0x%lx), resorting to WinSock 1 functionality.",
  944. dwError);
  945. #endif // DBG
  946. this->m_hWinSockDLL = LoadLibrary( _T("wsock32.dll") );
  947. if (this->m_hWinSockDLL == NULL)
  948. {
  949. #ifdef DBG
  950. dwError = GetLastError();
  951. DPFX(DPFPREP, 0, "Couldn't load \"wsock32.dll\" either (err = 0x%lx)!.",
  952. dwError);
  953. #endif // DBG
  954. hr = DPNHERR_GENERIC;
  955. goto Failure;
  956. }
  957. //
  958. // Remember that we had to resort to WinSock 1.
  959. //
  960. this->m_dwFlags |= NATHELPUPNPOBJ_WINSOCK1;
  961. }
  962. else
  963. {
  964. DPFX(DPFPREP, 1, "Loaded \"ws2_32.dll\", using WinSock 2 functionality.");
  965. }
  966. #endif // DPNBUILD_NOWINSOCK2
  967. //
  968. // Load pointers to all the functions we use in WinSock.
  969. //
  970. hr = this->LoadWinSockFunctionPointers();
  971. if (hr != DPNH_OK)
  972. {
  973. DPFX(DPFPREP, 0, "Couldn't load WinSock function pointers!");
  974. goto Failure;
  975. }
  976. //
  977. // Fire up WinSock. Request 2.2 if we can. For the most part we only use
  978. // version 1.1 capabilities and interfaces anyway. The only exceptions are
  979. // using the event or I/O completion port handles for notification.
  980. //
  981. ZeroMemory(&wsadata, sizeof(wsadata));
  982. #ifndef DPNBUILD_NOWINSOCK2
  983. if (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1)
  984. {
  985. #endif // ! DPNBUILD_NOWINSOCK2
  986. iError = this->m_pfnWSAStartup(MAKEWORD(1, 1), &wsadata);
  987. #ifndef DPNBUILD_NOWINSOCK2
  988. }
  989. else
  990. {
  991. iError = this->m_pfnWSAStartup(MAKEWORD(2, 2), &wsadata);
  992. }
  993. #endif // ! DPNBUILD_NOWINSOCK2
  994. if (iError != 0)
  995. {
  996. DPFX(DPFPREP, 0, "Couldn't startup WinSock (error = %i)!", iError);
  997. hr = DPNHERR_GENERIC;
  998. goto Failure;
  999. }
  1000. fWinSockStarted = TRUE;
  1001. DPFX(DPFPREP, 4, "Initialized WinSock version %u.%u.",
  1002. LOBYTE(wsadata.wVersion), HIBYTE(wsadata.wVersion));
  1003. #ifndef DPNBUILD_NOWINSOCK2
  1004. //
  1005. // Try creating a UDP socket for use with WSAIoctl. Do this even if we're
  1006. // WinSock 1 and can't use WSAIoctl socket. This allows us to make sure
  1007. // TCP/IP is installed and working.
  1008. //
  1009. this->m_sIoctls = this->m_pfnsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  1010. if (this->m_sIoctls == INVALID_SOCKET)
  1011. {
  1012. #ifdef DBG
  1013. dwError = this->m_pfnWSAGetLastError();
  1014. DPFX(DPFPREP, 0, "Couldn't create Ioctl socket, error = %u!", dwError);
  1015. #endif // DBG
  1016. hr = DPNHERR_GENERIC;
  1017. goto Failure;
  1018. }
  1019. //
  1020. // Try binding the socket. This is a continuation of the validation.
  1021. //
  1022. ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
  1023. saddrinTemp.sin_family = AF_INET;
  1024. //saddrinTemp.sin_addr.S_un.S_addr = INADDR_ANY;
  1025. //saddrinTemp.sin_port = 0;
  1026. if (this->m_pfnbind(this->m_sIoctls,
  1027. (SOCKADDR *) (&saddrinTemp),
  1028. sizeof(saddrinTemp)) != 0)
  1029. {
  1030. #ifdef DBG
  1031. dwError = this->m_pfnWSAGetLastError();
  1032. DPFX(DPFPREP, 0, "Couldn't bind the Ioctl socket to arbitrary port on any interface, error = %u!",
  1033. dwError);
  1034. #endif // DBG
  1035. hr = DPNHERR_GENERIC;
  1036. goto Failure;
  1037. }
  1038. #endif // ! DPNBUILD_NOWINSOCK2
  1039. //
  1040. // Build appropriate access control structures. On NT, we want to allow
  1041. // read access to everyone. On other platforms, security is ignored.
  1042. //
  1043. #ifdef WINNT
  1044. if (! AllocateAndInitializeSid(&SidIdentifierAuthorityWorld,
  1045. 1,
  1046. SECURITY_WORLD_RID,
  1047. 0,
  1048. 0,
  1049. 0,
  1050. 0,
  1051. 0,
  1052. 0,
  1053. 0,
  1054. &pSid))
  1055. {
  1056. #ifdef DEBUG
  1057. dwError = GetLastError();
  1058. DPFX(DPFPREP, 0, "Couldn't allocate and initialize SID, error = %u!",
  1059. dwError);
  1060. #endif // DEBUG
  1061. hr = DPNHERR_GENERIC;
  1062. goto Failure;
  1063. }
  1064. dwAclLength = sizeof(ACL)
  1065. + sizeof(ACCESS_ALLOWED_ACE)
  1066. - sizeof(DWORD) // subtract out sizeof(ACCESS_ALLOWED_ACE.SidStart)
  1067. + GetLengthSid(pSid);
  1068. pAcl = (ACL*) DNMalloc(dwAclLength);
  1069. if (pAcl == NULL)
  1070. {
  1071. hr = DPNHERR_OUTOFMEMORY;
  1072. goto Failure;
  1073. }
  1074. if (! InitializeAcl(pAcl, dwAclLength, ACL_REVISION))
  1075. {
  1076. #ifdef DEBUG
  1077. dwError = GetLastError();
  1078. DPFX(DPFPREP, 0, "Couldn't initialize ACL, error = %u!",
  1079. dwError);
  1080. #endif // DEBUG
  1081. hr = DPNHERR_GENERIC;
  1082. goto Failure;
  1083. }
  1084. if (! AddAccessAllowedAce(pAcl, ACL_REVISION, SYNCHRONIZE, pSid))
  1085. {
  1086. #ifdef DEBUG
  1087. dwError = GetLastError();
  1088. DPFX(DPFPREP, 0, "Couldn't add access allowed ACE, error = %u!",
  1089. dwError);
  1090. #endif // DEBUG
  1091. hr = DPNHERR_GENERIC;
  1092. goto Failure;
  1093. }
  1094. if (! InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR) abSecurityDescriptorBuffer,
  1095. SECURITY_DESCRIPTOR_REVISION))
  1096. {
  1097. #ifdef DEBUG
  1098. dwError = GetLastError();
  1099. DPFX(DPFPREP, 0, "Couldn't initialize security descriptor, error = %u!",
  1100. dwError);
  1101. #endif // DEBUG
  1102. hr = DPNHERR_GENERIC;
  1103. goto Failure;
  1104. }
  1105. if (! SetSecurityDescriptorDacl((PSECURITY_DESCRIPTOR) abSecurityDescriptorBuffer,
  1106. TRUE,
  1107. pAcl,
  1108. FALSE))
  1109. {
  1110. #ifdef DEBUG
  1111. dwError = GetLastError();
  1112. DPFX(DPFPREP, 0, "Couldn't set security descriptor DACL, error = %u!",
  1113. dwError);
  1114. #endif // DEBUG
  1115. hr = DPNHERR_GENERIC;
  1116. goto Failure;
  1117. }
  1118. SecurityAttributes.nLength = sizeof(SecurityAttributes);
  1119. SecurityAttributes.lpSecurityDescriptor = abSecurityDescriptorBuffer;
  1120. SecurityAttributes.bInheritHandle = FALSE;
  1121. pSecurityAttributes = &SecurityAttributes;
  1122. #else // ! WINNT
  1123. pSecurityAttributes = NULL;
  1124. #endif // ! WINNT
  1125. //
  1126. // Use a random number for the instance key and event. We use this to let
  1127. // other instances know that we're alive to avoid the crash-cleanup code.
  1128. // Try to create the named event a couple times before giving up.
  1129. //
  1130. dwTry = 0;
  1131. do
  1132. {
  1133. this->m_dwInstanceKey = GetGlobalRand();
  1134. DPFX(DPFPREP, 2, "Using crash cleanup key %u.", this->m_dwInstanceKey);
  1135. #ifndef WINCE
  1136. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  1137. {
  1138. wsprintf(tszObjectName, _T("Global\\") INSTANCENAMEDOBJECT_FORMATSTRING, this->m_dwInstanceKey);
  1139. this->m_hMappingStillActiveNamedObject = DNCreateEvent(pSecurityAttributes, FALSE, FALSE, tszObjectName);
  1140. }
  1141. else
  1142. #endif // ! WINCE
  1143. {
  1144. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, this->m_dwInstanceKey);
  1145. this->m_hMappingStillActiveNamedObject = DNCreateEvent(pSecurityAttributes, FALSE, FALSE, tszObjectName);
  1146. }
  1147. if (this->m_hMappingStillActiveNamedObject == NULL)
  1148. {
  1149. #ifdef DBG
  1150. dwError = GetLastError();
  1151. DPFX(DPFPREP, 0, "Couldn't create mapping-still-active named object, error = %u!", dwError);
  1152. #endif // DBG
  1153. dwTry++;
  1154. if (dwTry >= MAX_NUM_INSTANCE_EVENT_ATTEMPTS)
  1155. {
  1156. hr = DPNHERR_GENERIC;
  1157. goto Failure;
  1158. }
  1159. //
  1160. // Continue...
  1161. //
  1162. }
  1163. }
  1164. while (this->m_hMappingStillActiveNamedObject == NULL);
  1165. #ifdef WINNT
  1166. DNFree(pAcl);
  1167. pAcl = NULL;
  1168. FreeSid(pSid);
  1169. pSid = NULL;
  1170. #endif // WINNT
  1171. //
  1172. // Build the list of IP capable devices.
  1173. //
  1174. hr = this->CheckForNewDevices(NULL);
  1175. if (hr != DPNH_OK)
  1176. {
  1177. DPFX(DPFPREP, 0, "Couldn't build device list!");
  1178. goto Failure;
  1179. }
  1180. //
  1181. // We could technically try to contact UPnP devices right now, but we don't
  1182. // because it's a slow blocking operation, and users have to call GetCaps
  1183. // at least once anyway.
  1184. //
  1185. Exit:
  1186. if (fHaveLock)
  1187. {
  1188. this->DropLock();
  1189. fHaveLock = FALSE;
  1190. }
  1191. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  1192. return hr;
  1193. Failure:
  1194. if (this->m_hMappingStillActiveNamedObject != NULL)
  1195. {
  1196. DNCloseHandle(this->m_hMappingStillActiveNamedObject);
  1197. this->m_hMappingStillActiveNamedObject = NULL;
  1198. }
  1199. #ifdef WINNT
  1200. if (pAcl != NULL)
  1201. {
  1202. DNFree(pAcl);
  1203. pAcl = NULL;
  1204. }
  1205. if (pSid != NULL)
  1206. {
  1207. FreeSid(pSid);
  1208. pSid = NULL;
  1209. }
  1210. #endif // WINNT
  1211. this->RemoveAllItems();
  1212. #ifndef DPNBUILD_NOWINSOCK2
  1213. if (this->m_sIoctls != INVALID_SOCKET)
  1214. {
  1215. this->m_pfnclosesocket(this->m_sIoctls); // ignore error
  1216. this->m_sIoctls = INVALID_SOCKET;
  1217. }
  1218. #endif // ! DPNBUILD_NOWINSOCK2
  1219. if (fWinSockStarted)
  1220. {
  1221. this->m_pfnWSACleanup(); // ignore error
  1222. }
  1223. #ifndef DPNBUILD_NOWINSOCK2
  1224. if (this->m_hWinSockDLL != NULL)
  1225. {
  1226. this->m_pfnWSAStartup = NULL;
  1227. this->m_pfnWSACleanup = NULL;
  1228. this->m_pfnWSAGetLastError = NULL;
  1229. this->m_pfnsocket = NULL;
  1230. this->m_pfnclosesocket = NULL;
  1231. this->m_pfnbind = NULL;
  1232. this->m_pfnsetsockopt = NULL;
  1233. this->m_pfngetsockname = NULL;
  1234. this->m_pfnselect = NULL;
  1235. this->m_pfn__WSAFDIsSet = NULL;
  1236. this->m_pfnrecvfrom = NULL;
  1237. this->m_pfnsendto = NULL;
  1238. this->m_pfngethostname = NULL;
  1239. this->m_pfngethostbyname = NULL;
  1240. this->m_pfninet_addr = NULL;
  1241. this->m_pfnWSASocketA = NULL;
  1242. this->m_pfnWSAIoctl = NULL;
  1243. this->m_pfnWSAGetOverlappedResult = NULL;
  1244. this->m_pfnioctlsocket = NULL;
  1245. this->m_pfnconnect = NULL;
  1246. this->m_pfnshutdown = NULL;
  1247. this->m_pfnsend = NULL;
  1248. this->m_pfnrecv = NULL;
  1249. #ifdef DBG
  1250. this->m_pfngetsockopt = NULL;
  1251. #endif // DBG
  1252. this->m_dwFlags &= ~NATHELPUPNPOBJ_WINSOCK1;
  1253. FreeLibrary(this->m_hWinSockDLL);
  1254. this->m_hWinSockDLL = NULL;
  1255. }
  1256. if (this->m_hRasApi32DLL != NULL)
  1257. {
  1258. this->m_pfnRasGetEntryHrasconnW = NULL;
  1259. this->m_pfnRasGetProjectionInfo = NULL;
  1260. FreeLibrary(this->m_hRasApi32DLL);
  1261. this->m_hRasApi32DLL = NULL;
  1262. }
  1263. if (this->m_hIpHlpApiDLL != NULL)
  1264. {
  1265. this->m_pfnGetAdaptersInfo = NULL;
  1266. this->m_pfnGetIpForwardTable = NULL;
  1267. this->m_pfnGetBestRoute = NULL;
  1268. FreeLibrary(this->m_hIpHlpApiDLL);
  1269. this->m_hIpHlpApiDLL = NULL;
  1270. }
  1271. #endif // ! DPNBUILD_NOWINSOCK2
  1272. if (fSetFlags)
  1273. {
  1274. this->m_dwFlags &= ~(NATHELPUPNPOBJ_INITIALIZED |
  1275. NATHELPUPNPOBJ_USEUPNP |
  1276. #ifndef DPNBUILD_NOHNETFWAPI
  1277. NATHELPUPNPOBJ_USEHNETFWAPI |
  1278. #endif // ! DPNBUILD_NOHNETFWAPI
  1279. #ifdef WINCE
  1280. NATHELPUPNPOBJ_DEVICECHANGED);
  1281. #else // ! WINCE
  1282. NATHELPUPNPOBJ_DEVICECHANGED |
  1283. NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX);
  1284. #endif // ! WINCE
  1285. }
  1286. goto Exit;
  1287. } // CNATHelpUPnP::Initialize
  1288. #undef DPF_MODNAME
  1289. #define DPF_MODNAME "CNATHelpUPnP::Close"
  1290. //=============================================================================
  1291. // CNATHelpUPnP::Close
  1292. //-----------------------------------------------------------------------------
  1293. //
  1294. // Description: Shuts down and de-registers this application with any
  1295. // Internet gateway servers. All port assignments are implicitly
  1296. // freed as a result of this operation.
  1297. //
  1298. // This must balance a successful call to Initialize.
  1299. //
  1300. // Arguments:
  1301. // DWORD dwFlags - Unused, must be zero.
  1302. //
  1303. // Returns: HRESULT
  1304. // DPNH_OK - Closing the helper API was successful.
  1305. // DPNHERR_GENERIC - An error occurred while closing.
  1306. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  1307. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  1308. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  1309. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  1310. // DPNHERR_OUTOFMEMORY - There is not enough memory to close.
  1311. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  1312. // thread.
  1313. //=============================================================================
  1314. STDMETHODIMP CNATHelpUPnP::Close(const DWORD dwFlags)
  1315. {
  1316. HRESULT hr;
  1317. BOOL fHaveLock = FALSE;
  1318. int iError;
  1319. #ifdef DBG
  1320. DWORD dwError;
  1321. #endif // DBG
  1322. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%lx)", this, dwFlags);
  1323. //
  1324. // Validate the object.
  1325. //
  1326. if (! this->IsValidObject())
  1327. {
  1328. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  1329. hr = DPNHERR_INVALIDOBJECT;
  1330. goto Failure;
  1331. }
  1332. //
  1333. // Validate the parameters.
  1334. //
  1335. if (dwFlags != 0)
  1336. {
  1337. DPFX(DPFPREP, 0, "Invalid flags specified!");
  1338. hr = DPNHERR_INVALIDFLAGS;
  1339. goto Failure;
  1340. }
  1341. //
  1342. // Attempt to take the lock, but be prepared for the re-entrancy error.
  1343. //
  1344. hr = this->TakeLock();
  1345. if (hr != DPNH_OK)
  1346. {
  1347. DPFX(DPFPREP, 0, "Could not lock object!");
  1348. goto Failure;
  1349. }
  1350. fHaveLock = TRUE;
  1351. //
  1352. // Make sure object is in right state.
  1353. //
  1354. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  1355. {
  1356. DPFX(DPFPREP, 0, "Object not initialized!");
  1357. hr = DPNHERR_NOTINITIALIZED;
  1358. goto Failure;
  1359. }
  1360. //
  1361. // We need to actively deregister any devices which are registered with
  1362. // Internet gateways.
  1363. //
  1364. this->RemoveAllItems();
  1365. //
  1366. // Close the named object since this process is going away.
  1367. //
  1368. if (this->m_hMappingStillActiveNamedObject != NULL)
  1369. {
  1370. DNCloseHandle(this->m_hMappingStillActiveNamedObject);
  1371. this->m_hMappingStillActiveNamedObject = NULL;
  1372. }
  1373. #ifndef DPNBUILD_NOWINSOCK2
  1374. //
  1375. // Close the Ioctl socket.
  1376. //
  1377. DNASSERT(this->m_sIoctls != INVALID_SOCKET);
  1378. this->m_pfnclosesocket(this->m_sIoctls); // ignore error
  1379. this->m_sIoctls = INVALID_SOCKET;
  1380. //
  1381. // If we submitted overlapped I/O, see if it got cancelled.
  1382. //
  1383. if (this->m_polAddressListChange != NULL)
  1384. {
  1385. OSVERSIONINFO osvi;
  1386. OSVERSIONINFOEX osvix;
  1387. BOOL fCanWait;
  1388. DWORD dwAttempt;
  1389. ZeroMemory(&osvi, sizeof(osvi));
  1390. osvi.dwOSVersionInfoSize = sizeof(osvi);
  1391. if (GetVersionEx(&osvi))
  1392. {
  1393. //
  1394. // Any platform but Win2K Gold, Win2K + SP1, or Win2K + SP2 can
  1395. // just go ahead and wait for the I/O to complete.
  1396. //
  1397. if ((osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) ||
  1398. (osvi.dwMajorVersion > 5) ||
  1399. (osvi.dwMinorVersion > 0))
  1400. {
  1401. DPFX(DPFPREP, 3, "Windows %s version %u.%u detected, waiting for address list change Ioctl to complete.",
  1402. ((osvi.dwPlatformId != VER_PLATFORM_WIN32_NT) ? _T("9x") : _T("NT")),
  1403. osvi.dwMajorVersion, osvi.dwMinorVersion);
  1404. fCanWait = TRUE;
  1405. }
  1406. else
  1407. {
  1408. //
  1409. // Win2K versions < SP3 have a bug where the I/O is not always
  1410. // cancelled by closing the socket. We can't wait for the
  1411. // completion, sometimes it doesn't happen.
  1412. //
  1413. fCanWait = FALSE;
  1414. ZeroMemory(&osvix, sizeof(osvix));
  1415. osvix.dwOSVersionInfoSize = sizeof(osvix);
  1416. if (GetVersionEx((LPOSVERSIONINFO) (&osvix)))
  1417. {
  1418. //
  1419. // If SP3 or later is applied, we know it's fixed.
  1420. //
  1421. if (osvix.wServicePackMajor >= 3)
  1422. {
  1423. DPFX(DPFPREP, 3, "Windows 2000 Service Pack %u detected, waiting for address list change Ioctl to complete.",
  1424. osvix.wServicePackMajor);
  1425. fCanWait = TRUE;
  1426. }
  1427. #ifdef DBG
  1428. else
  1429. {
  1430. if (osvix.wServicePackMajor == 0)
  1431. {
  1432. DPFX(DPFPREP, 2, "Windows 2000 Gold detected, not waiting for address list change Ioctl to complete.");
  1433. }
  1434. else
  1435. {
  1436. DPFX(DPFPREP, 2, "Windows 2000 Service Pack %u detected, not waiting for address list change Ioctl to complete.",
  1437. osvix.wServicePackMajor);
  1438. }
  1439. }
  1440. #endif // DBG
  1441. }
  1442. #ifdef DBG
  1443. else
  1444. {
  1445. dwError = GetLastError();
  1446. DPFX(DPFPREP, 0, "Couldn't get extended OS version information (err = %u)! Assuming not Win2K < SP3.",
  1447. dwError);
  1448. }
  1449. #endif // DBG
  1450. }
  1451. //
  1452. // Wait, if we can. Otherwise, leak the memory.
  1453. //
  1454. if (fCanWait)
  1455. {
  1456. //
  1457. // Keep looping until I/O completes. We will give up after a
  1458. // while to prevent hangs.
  1459. //
  1460. dwAttempt = 0;
  1461. while (! HasOverlappedIoCompleted(this->m_polAddressListChange))
  1462. {
  1463. DPFX(DPFPREP, 2, "Waiting %u ms for address list change Ioctl to complete.",
  1464. IOCOMPLETE_WAIT_INTERVAL);
  1465. //
  1466. // Give the OS some time to complete it.
  1467. //
  1468. Sleep(IOCOMPLETE_WAIT_INTERVAL);
  1469. dwAttempt++;
  1470. if (dwAttempt >= MAX_NUM_IOCOMPLETE_WAITS)
  1471. {
  1472. break;
  1473. }
  1474. }
  1475. }
  1476. else
  1477. {
  1478. //
  1479. // Just leak the memory. See above notes and debug print
  1480. // statements
  1481. //
  1482. }
  1483. }
  1484. #ifdef DBG
  1485. else
  1486. {
  1487. dwError = GetLastError();
  1488. DPFX(DPFPREP, 0, "Couldn't get OS version information (err = %u)! Assuming not Win2K < SP3.",
  1489. dwError);
  1490. }
  1491. #endif // DBG
  1492. //
  1493. // We've either freed the memory or committed to leaking the object.
  1494. //
  1495. if (HasOverlappedIoCompleted(this->m_polAddressListChange))
  1496. {
  1497. //
  1498. // We didn't allocate it through DNMalloc, use the matching free
  1499. // function.
  1500. //
  1501. HeapFree(GetProcessHeap(), 0, this->m_polAddressListChange);
  1502. }
  1503. else
  1504. {
  1505. DPFX(DPFPREP, 1, "Overlapped address list change Ioctl has not completed yet, leaking %u byte overlapped structure at 0x%p.",
  1506. sizeof(WSAOVERLAPPED), this->m_polAddressListChange);
  1507. }
  1508. this->m_polAddressListChange = NULL;
  1509. }
  1510. #endif // ! DPNBUILD_NOWINSOCK2
  1511. //
  1512. // Cleanup WinSock.
  1513. //
  1514. iError = this->m_pfnWSACleanup();
  1515. if (iError != 0)
  1516. {
  1517. #ifdef DBG
  1518. dwError = this->m_pfnWSAGetLastError();
  1519. DPFX(DPFPREP, 0, "Couldn't cleanup WinSock (error = %u)!", dwError);
  1520. #endif // DBG
  1521. //
  1522. // Continue anyway, so we can finish cleaning up the object.
  1523. //
  1524. }
  1525. //
  1526. // Unload the library.
  1527. //
  1528. this->m_pfnWSAStartup = NULL;
  1529. this->m_pfnWSACleanup = NULL;
  1530. this->m_pfnWSAGetLastError = NULL;
  1531. this->m_pfnsocket = NULL;
  1532. this->m_pfnclosesocket = NULL;
  1533. this->m_pfnbind = NULL;
  1534. this->m_pfnsetsockopt = NULL;
  1535. this->m_pfngetsockname = NULL;
  1536. this->m_pfnselect = NULL;
  1537. this->m_pfn__WSAFDIsSet = NULL;
  1538. this->m_pfnrecvfrom = NULL;
  1539. this->m_pfnsendto = NULL;
  1540. this->m_pfngethostname = NULL;
  1541. this->m_pfngethostbyname = NULL;
  1542. this->m_pfninet_addr = NULL;
  1543. #ifndef DPNBUILD_NOWINSOCK2
  1544. this->m_pfnWSASocketA = NULL;
  1545. this->m_pfnWSAIoctl = NULL;
  1546. this->m_pfnWSAGetOverlappedResult = NULL;
  1547. #endif // ! DPNBUILD_NOWINSOCK2
  1548. this->m_pfnioctlsocket = NULL;
  1549. this->m_pfnconnect = NULL;
  1550. this->m_pfnshutdown = NULL;
  1551. this->m_pfnsend = NULL;
  1552. this->m_pfnrecv = NULL;
  1553. #ifdef DBG
  1554. this->m_pfngetsockopt = NULL;
  1555. #endif // DBG
  1556. FreeLibrary(this->m_hWinSockDLL);
  1557. this->m_hWinSockDLL = NULL;
  1558. #ifndef DPNBUILD_NOWINSOCK2
  1559. //
  1560. // If we loaded RASAPI32.DLL, unload it.
  1561. //
  1562. if (this->m_hRasApi32DLL != NULL)
  1563. {
  1564. this->m_pfnRasGetEntryHrasconnW = NULL;
  1565. this->m_pfnRasGetProjectionInfo = NULL;
  1566. FreeLibrary(this->m_hRasApi32DLL);
  1567. this->m_hRasApi32DLL = NULL;
  1568. }
  1569. //
  1570. // If we loaded IPHLPAPI.DLL, unload it.
  1571. //
  1572. if (this->m_hIpHlpApiDLL != NULL)
  1573. {
  1574. this->m_pfnGetAdaptersInfo = NULL;
  1575. this->m_pfnGetIpForwardTable = NULL;
  1576. this->m_pfnGetBestRoute = NULL;
  1577. FreeLibrary(this->m_hIpHlpApiDLL);
  1578. this->m_hIpHlpApiDLL = NULL;
  1579. }
  1580. //
  1581. // If there was an alert event, we're done with it.
  1582. //
  1583. if (this->m_hAlertEvent != NULL)
  1584. {
  1585. CloseHandle(this->m_hAlertEvent);
  1586. this->m_hAlertEvent = NULL;
  1587. }
  1588. //
  1589. // If there was an alert I/O completion port, we're done with it.
  1590. //
  1591. if (this->m_hAlertIOCompletionPort != NULL)
  1592. {
  1593. CloseHandle(this->m_hAlertIOCompletionPort);
  1594. this->m_hAlertIOCompletionPort = NULL;
  1595. }
  1596. #endif // ! DPNBUILD_NOWINSOCK2
  1597. //
  1598. // Turn off flags which should reset it back to 0 or just the
  1599. // NOTCREATEDWITHCOM flag.
  1600. //
  1601. this->m_dwFlags &= ~(NATHELPUPNPOBJ_INITIALIZED |
  1602. NATHELPUPNPOBJ_USEUPNP |
  1603. #ifndef DPNBUILD_NOHNETFWAPI
  1604. NATHELPUPNPOBJ_USEHNETFWAPI |
  1605. #endif // ! DPNBUILD_NOHNETFWAPI
  1606. #ifndef DPNBUILD_NOWINSOCK2
  1607. NATHELPUPNPOBJ_WINSOCK1 |
  1608. #endif // ! DPNBUILD_NOWINSOCK2
  1609. NATHELPUPNPOBJ_DEVICECHANGED |
  1610. NATHELPUPNPOBJ_ADDRESSESCHANGED |
  1611. #ifdef WINCE
  1612. NATHELPUPNPOBJ_PORTREGISTERED);
  1613. #else // ! WINCE
  1614. NATHELPUPNPOBJ_PORTREGISTERED |
  1615. NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX);
  1616. #endif // ! WINCE
  1617. DNASSERT((this->m_dwFlags & ~NATHELPUPNPOBJ_NOTCREATEDWITHCOM) == 0);
  1618. this->DropLock();
  1619. fHaveLock = FALSE;
  1620. Exit:
  1621. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  1622. return hr;
  1623. Failure:
  1624. if (fHaveLock)
  1625. {
  1626. this->DropLock();
  1627. fHaveLock = FALSE;
  1628. }
  1629. goto Exit;
  1630. } // CNATHelpUPnP::Close
  1631. #undef DPF_MODNAME
  1632. #define DPF_MODNAME "CNATHelpUPnP::GetCaps"
  1633. //=============================================================================
  1634. // CNATHelpUPnP::GetCaps
  1635. //-----------------------------------------------------------------------------
  1636. //
  1637. // Description: Retrieves the capabilities of the Internet gateway server(s)
  1638. // and information on leased ports. This function should be
  1639. // called periodically with the DPNHGETCAPS_UPDATESERVERSTATUS
  1640. // flag to automatically extend port leases that are about to
  1641. // expire (that are in last 2 minutes of their lease).
  1642. //
  1643. // The DPNHGETCAPS_UPDATESERVERSTATUS flag also causes
  1644. // detection of changes in the servers' status since the last
  1645. // similar call to GetCaps. If a new server becomes available, an
  1646. // existing one became unavailable, or a server's public address
  1647. // changed in a way that affects an existing registered port
  1648. // mapping, then DPNHSUCCESS_ADDRESSESCHANGED is returned instead
  1649. // of DPNH_OK. The user should then update its port binding
  1650. // information via GetRegisteredAddresses.
  1651. //
  1652. // When DPNHGETCAPS_UPDATESERVERSTATUS is specified, this
  1653. // function may block for a short period of time while attempts
  1654. // are made to communicate with the server(s).
  1655. //
  1656. // GetCaps must be called with the
  1657. // DPNHGETCAPS_UPDATESERVERSTATUS flag at least once prior to
  1658. // using the GetRegisteredAddresses or QueryAddress methods.
  1659. //
  1660. // Arguments:
  1661. // DPNHCAPS * pdpnhcaps - Pointer to structure to be filled with the NAT
  1662. // helper's current capabilities. The dwSize
  1663. // field of the structure must be filled in before
  1664. // calling GetCaps.
  1665. // DWORD dwFlags - Flags to use when retrieving capabilities
  1666. // (DPNHGETCAPS_xxx).
  1667. //
  1668. // Returns: HRESULT
  1669. // DPNH_OK - Determining capabilities was successful.
  1670. // Address status has not changed.
  1671. // DPNHSUCCESS_ADDRESSESCHANGED - One or more of the registered port
  1672. // mappings' addresses changed, retrieve
  1673. // updated mappings with
  1674. // GetRegisteredAddress.
  1675. // DPNHERR_GENERIC - An error occurred while determining
  1676. // capabilities.
  1677. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  1678. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  1679. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  1680. // DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
  1681. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  1682. // DPNHERR_OUTOFMEMORY - There is not enough memory to get
  1683. // capabilities.
  1684. // DPNHERR_REENTRANT - The interface has been re-entered on the
  1685. // same thread.
  1686. //=============================================================================
  1687. STDMETHODIMP CNATHelpUPnP::GetCaps(DPNHCAPS * const pdpnhcaps,
  1688. const DWORD dwFlags)
  1689. {
  1690. HRESULT hr;
  1691. BOOL fHaveLock = FALSE;
  1692. DWORD dwCurrentTime;
  1693. DWORD dwLeaseTimeRemaining;
  1694. CBilink * pBilink;
  1695. CRegisteredPort * pRegisteredPort;
  1696. CDevice * pDevice;
  1697. CUPnPDevice * pUPnPDevice = NULL;
  1698. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx)",
  1699. this, pdpnhcaps, dwFlags);
  1700. //
  1701. // Validate the object.
  1702. //
  1703. if (! this->IsValidObject())
  1704. {
  1705. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  1706. hr = DPNHERR_INVALIDOBJECT;
  1707. goto Failure;
  1708. }
  1709. //
  1710. // Validate the parameters.
  1711. //
  1712. if ((pdpnhcaps == NULL) ||
  1713. (IsBadWritePtr(pdpnhcaps, sizeof(DPNHCAPS))))
  1714. {
  1715. DPFX(DPFPREP, 0, "Invalid caps structure pointer specified!");
  1716. hr = DPNHERR_INVALIDPOINTER;
  1717. goto Failure;
  1718. }
  1719. if (pdpnhcaps->dwSize != sizeof(DPNHCAPS))
  1720. {
  1721. DPFX(DPFPREP, 0, "Invalid caps structure specified, dwSize must be %u!",
  1722. sizeof(DPNHCAPS));
  1723. hr = DPNHERR_INVALIDPARAM;
  1724. goto Failure;
  1725. }
  1726. if (dwFlags & ~DPNHGETCAPS_UPDATESERVERSTATUS)
  1727. {
  1728. DPFX(DPFPREP, 0, "Invalid flags specified!");
  1729. hr = DPNHERR_INVALIDFLAGS;
  1730. goto Failure;
  1731. }
  1732. //
  1733. // Attempt to take the lock, but be prepared for the re-entrancy error.
  1734. //
  1735. hr = this->TakeLock();
  1736. if (hr != DPNH_OK)
  1737. {
  1738. DPFX(DPFPREP, 0, "Could not lock object!");
  1739. goto Failure;
  1740. }
  1741. fHaveLock = TRUE;
  1742. //
  1743. // Make sure object is in right state.
  1744. //
  1745. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  1746. {
  1747. DPFX(DPFPREP, 0, "Object not initialized!");
  1748. hr = DPNHERR_NOTINITIALIZED;
  1749. goto Failure;
  1750. }
  1751. //
  1752. // Fill in the base caps structure.
  1753. //
  1754. pdpnhcaps->dwFlags = 0;
  1755. pdpnhcaps->dwNumRegisteredPorts = 0;
  1756. pdpnhcaps->dwMinLeaseTimeRemaining = -1;
  1757. //
  1758. // pdpnhcaps->dwRecommendedGetCapsInterval is initialized below
  1759. //
  1760. if (dwFlags & DPNHGETCAPS_UPDATESERVERSTATUS)
  1761. {
  1762. //
  1763. // Remove any cached mappings that have expired.
  1764. //
  1765. this->ExpireOldCachedMappings();
  1766. //
  1767. // Extend leases, if necessary.
  1768. //
  1769. hr = this->ExtendAllExpiringLeases();
  1770. if (hr != DPNH_OK)
  1771. {
  1772. DPFX(DPFPREP, 0, "Extending all expiring leases failed!");
  1773. goto Failure;
  1774. }
  1775. //
  1776. // Check for any new devices.
  1777. //
  1778. hr = this->CheckForNewDevices(NULL);
  1779. if (hr != DPNH_OK)
  1780. {
  1781. DPFX(DPFPREP, 0, "Checking for new devices failed!");
  1782. goto Failure;
  1783. }
  1784. //
  1785. // Check for possible changes in any server's status. The
  1786. // ADDRESSESCHANGED flag will be set on this object if there were
  1787. // changes that affected existing port mappings.
  1788. //
  1789. hr = this->UpdateServerStatus();
  1790. if (hr != DPNH_OK)
  1791. {
  1792. DPFX(DPFPREP, 0, "Updating servers' status failed!");
  1793. goto Failure;
  1794. }
  1795. //
  1796. // Okay, so if things are different, alert the caller.
  1797. //
  1798. if (this->m_dwFlags & NATHELPUPNPOBJ_ADDRESSESCHANGED)
  1799. {
  1800. hr = DPNHSUCCESS_ADDRESSESCHANGED;
  1801. this->m_dwFlags &= ~NATHELPUPNPOBJ_ADDRESSESCHANGED;
  1802. }
  1803. #ifdef DBG
  1804. //
  1805. // This flag should have been turned off by now if it ever got turned
  1806. // on.
  1807. //
  1808. DNASSERT(! (this->m_dwFlags & NATHELPUPNPOBJ_DEVICECHANGED));
  1809. //
  1810. // Print the current device and mapping status for debugging purposes.
  1811. //
  1812. this->DebugPrintCurrentStatus();
  1813. #endif // DBG
  1814. }
  1815. else
  1816. {
  1817. //
  1818. // Not extending expiring leases or updating server status.
  1819. //
  1820. }
  1821. //
  1822. // Loop through all the devices, getting their gateway capabilities.
  1823. //
  1824. pBilink = this->m_blDevices.GetNext();
  1825. while (pBilink != (&this->m_blDevices))
  1826. {
  1827. DNASSERT(! pBilink->IsEmpty());
  1828. pDevice = DEVICE_FROM_BILINK(pBilink);
  1829. #ifndef DPNBUILD_NOHNETFWAPI
  1830. if (pDevice->IsHNetFirewalled())
  1831. {
  1832. //
  1833. // The firewall does not actively notify you of it going down.
  1834. //
  1835. pdpnhcaps->dwFlags |= DPNHCAPSFLAG_LOCALFIREWALLPRESENT | DPNHCAPSFLAG_PUBLICADDRESSAVAILABLE | DPNHCAPSFLAG_NOTALLSUPPORTACTIVENOTIFY;
  1836. }
  1837. #endif // ! DPNBUILD_NOHNETFWAPI
  1838. pUPnPDevice = pDevice->GetUPnPDevice();
  1839. if (pUPnPDevice != NULL)
  1840. {
  1841. DNASSERT(pUPnPDevice->IsReady());
  1842. pdpnhcaps->dwFlags |= DPNHCAPSFLAG_GATEWAYPRESENT;
  1843. if (pUPnPDevice->IsLocal())
  1844. {
  1845. pdpnhcaps->dwFlags |= DPNHCAPSFLAG_GATEWAYISLOCAL;
  1846. }
  1847. if (pUPnPDevice->GetExternalIPAddressV4() != 0)
  1848. {
  1849. pdpnhcaps->dwFlags |= DPNHCAPSFLAG_PUBLICADDRESSAVAILABLE;
  1850. }
  1851. //
  1852. // The custom UPnP stack currently does not support active
  1853. // notification...
  1854. //
  1855. pdpnhcaps->dwFlags |= DPNHCAPSFLAG_NOTALLSUPPORTACTIVENOTIFY;
  1856. }
  1857. pBilink = pBilink->GetNext();
  1858. }
  1859. //
  1860. // Loop through all registered ports, counting them.
  1861. // We have the appropriate lock.
  1862. //
  1863. pBilink = this->m_blRegisteredPorts.GetNext();
  1864. dwCurrentTime = GETTIMESTAMP();
  1865. while (pBilink != (&this->m_blRegisteredPorts))
  1866. {
  1867. DNASSERT(! pBilink->IsEmpty());
  1868. pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
  1869. //
  1870. // Count these registered addresses toward the total.
  1871. //
  1872. pdpnhcaps->dwNumRegisteredPorts += pRegisteredPort->GetNumAddresses();
  1873. pDevice = pRegisteredPort->GetOwningDevice();
  1874. if (pDevice != NULL)
  1875. {
  1876. DNASSERT(! (pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts)));
  1877. //
  1878. // If they're registered with any UPnP devices using a non-
  1879. // permanent lease, calculate the minimum lease time remaining.
  1880. //
  1881. if ((pRegisteredPort->HasUPnPPublicAddresses()) &&
  1882. (! pRegisteredPort->HasPermanentUPnPLease()))
  1883. {
  1884. dwLeaseTimeRemaining = pRegisteredPort->GetUPnPLeaseExpiration() - dwCurrentTime;
  1885. if (dwLeaseTimeRemaining < pdpnhcaps->dwMinLeaseTimeRemaining)
  1886. {
  1887. //
  1888. // Temporarily store how much time remains.
  1889. //
  1890. pdpnhcaps->dwMinLeaseTimeRemaining = dwLeaseTimeRemaining;
  1891. }
  1892. }
  1893. }
  1894. else
  1895. {
  1896. DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts));
  1897. }
  1898. pBilink = pBilink->GetNext();
  1899. }
  1900. //
  1901. // There are different default recommended GetCaps intervals depending on
  1902. // whether there's a server present, and whether it supports active address
  1903. // change notification (that we can alert on) or not.
  1904. //
  1905. // If there are any leases which need to be renewed before that default
  1906. // time, the recommendation will be shortened appropriately.
  1907. //
  1908. //
  1909. // If GetCaps hasn't been called with UPDATESERVERSTATUS yet, recommend an
  1910. // immediate check.
  1911. //
  1912. if (this->m_dwLastUpdateServerStatusTime == 0)
  1913. {
  1914. DPFX(DPFPREP, 1, "Server status has not been updated yet, recommending immediate GetCaps.");
  1915. //
  1916. // Drop the lock, we're done here.
  1917. //
  1918. this->DropLock();
  1919. fHaveLock = FALSE;
  1920. goto Exit;
  1921. }
  1922. //
  1923. // In an ideal world, we could get notified of changes and we would never
  1924. // have to poll. Unfortunately that isn't the case. We need to recommend
  1925. // a relatively short poll interval.
  1926. //
  1927. // Start by figuring out how long it's been since the last server update.
  1928. // This calculation really should not go negative. If it does, it means
  1929. // the caller hasn't updated the server status in ages anyway, so we should
  1930. // recommend immediate GetCaps.
  1931. //
  1932. // Otherwise if the 'port registered' flag is still set at this point, then
  1933. // the user must have called GetCaps previously, then RegisterPorts, then
  1934. // made this second GetCaps call before g_dwMinUpdateServerStatusInterval
  1935. // elapsed. Recommend that the user call us again as soon as the minimum
  1936. // update interval does elapse.
  1937. //
  1938. // In all other cases, generate a recommendation based on the current
  1939. // backed off poll interval.
  1940. //
  1941. dwCurrentTime = dwCurrentTime - this->m_dwLastUpdateServerStatusTime;
  1942. if ((int) dwCurrentTime < 0)
  1943. {
  1944. DPFX(DPFPREP, 1, "Server status was last updated a really long time ago (%u ms), recommending immediate GetCaps.",
  1945. dwCurrentTime);
  1946. pdpnhcaps->dwRecommendedGetCapsInterval = 0;
  1947. }
  1948. else if (this->m_dwFlags & NATHELPUPNPOBJ_PORTREGISTERED)
  1949. {
  1950. DPFX(DPFPREP, 1, "Didn't handle new port registration because server was last updated %u ms ago, (poll interval staying at %u ms).",
  1951. dwCurrentTime, this->m_dwNextPollInterval);
  1952. pdpnhcaps->dwRecommendedGetCapsInterval = g_dwMinUpdateServerStatusInterval - dwCurrentTime;
  1953. if ((int) pdpnhcaps->dwRecommendedGetCapsInterval < 0)
  1954. {
  1955. pdpnhcaps->dwRecommendedGetCapsInterval = 0;
  1956. }
  1957. }
  1958. else
  1959. {
  1960. DPFX(DPFPREP, 7, "Server was last updated %u ms ago, current poll interval is %u ms.",
  1961. dwCurrentTime, this->m_dwNextPollInterval);
  1962. //
  1963. // Calculate a new recommended interval based on the current value, and
  1964. // backoff that interval if necessary.
  1965. //
  1966. pdpnhcaps->dwRecommendedGetCapsInterval = this->m_dwNextPollInterval - dwCurrentTime;
  1967. this->m_dwNextPollInterval += GetGlobalRand() % g_dwPollIntervalBackoff;
  1968. if (this->m_dwNextPollInterval > g_dwMaxPollInterval)
  1969. {
  1970. this->m_dwNextPollInterval = g_dwMaxPollInterval;
  1971. DPFX(DPFPREP, 3, "Capping next poll interval at %u ms.",
  1972. this->m_dwNextPollInterval);
  1973. }
  1974. else
  1975. {
  1976. DPFX(DPFPREP, 8, "Next poll interval will be %u ms.",
  1977. this->m_dwNextPollInterval);
  1978. }
  1979. //
  1980. // If that time went negative, then it implies that the interval has
  1981. // already elapsed. Recommend immediate GetCaps.
  1982. //
  1983. if (((int) pdpnhcaps->dwRecommendedGetCapsInterval) < 0)
  1984. {
  1985. DPFX(DPFPREP, 1, "Recommended interval already elapsed (%i ms), suggesting immediate GetCaps.",
  1986. ((int) pdpnhcaps->dwRecommendedGetCapsInterval));
  1987. pdpnhcaps->dwRecommendedGetCapsInterval = 0;
  1988. }
  1989. }
  1990. this->DropLock();
  1991. fHaveLock = FALSE;
  1992. //
  1993. // If there is a non-INFINITE lease time remaining, see if that affects the
  1994. // GetCaps interval.
  1995. //
  1996. if (pdpnhcaps->dwMinLeaseTimeRemaining != -1)
  1997. {
  1998. //
  1999. // If there are leases that need to be refreshed before the default
  2000. // recommendation, then use those instead.
  2001. //
  2002. if (pdpnhcaps->dwMinLeaseTimeRemaining < LEASE_RENEW_TIME)
  2003. {
  2004. DPFX(DPFPREP, 1, "Lease needs renewing right away (min %u < %u ms), recommending immediate GetCaps.",
  2005. pdpnhcaps->dwMinLeaseTimeRemaining, LEASE_RENEW_TIME);
  2006. pdpnhcaps->dwRecommendedGetCapsInterval = 0;
  2007. }
  2008. else
  2009. {
  2010. //
  2011. // Either pick the time when the lease should be renewed or leave
  2012. // it as the recommended time, whichever is shorter.
  2013. //
  2014. if ((pdpnhcaps->dwMinLeaseTimeRemaining - LEASE_RENEW_TIME) < pdpnhcaps->dwRecommendedGetCapsInterval)
  2015. {
  2016. pdpnhcaps->dwRecommendedGetCapsInterval = pdpnhcaps->dwMinLeaseTimeRemaining - LEASE_RENEW_TIME;
  2017. }
  2018. }
  2019. }
  2020. DPFX(DPFPREP, 7, "GetCaps flags = 0x%lx, num registered ports = %u, min lease time remaining = %i, recommended interval = %i.",
  2021. pdpnhcaps->dwFlags,
  2022. pdpnhcaps->dwNumRegisteredPorts,
  2023. ((int) pdpnhcaps->dwMinLeaseTimeRemaining),
  2024. ((int) pdpnhcaps->dwRecommendedGetCapsInterval));
  2025. Exit:
  2026. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  2027. return hr;
  2028. Failure:
  2029. if (fHaveLock)
  2030. {
  2031. this->DropLock();
  2032. fHaveLock = FALSE;
  2033. }
  2034. goto Exit;
  2035. } // CNATHelpUPnP::GetCaps
  2036. #undef DPF_MODNAME
  2037. #define DPF_MODNAME "CNATHelpUPnP::RegisterPorts"
  2038. //=============================================================================
  2039. // CNATHelpUPnP::RegisterPorts
  2040. //-----------------------------------------------------------------------------
  2041. //
  2042. // Description: Asks for public realm port(s) that are aliases for the local
  2043. // port(s) on this private realm node. If a server is available,
  2044. // all traffic directed to the gateway on the public side at the
  2045. // allocated public ports-- which the gateway provides and
  2046. // specifies in the response-- will be directed to the specified
  2047. // local ports. If the DPNHREGISTERPORTS_FIXEDPORTS flag is not
  2048. // specified, the ports assigned on the public interface are
  2049. // arbitrary (i.e. may not be the same as those in awLocalPort).
  2050. // The address and ports actually allocated can be retrieved by
  2051. // calling GetRegisteredAddresses.
  2052. //
  2053. // The address component for every SOCKADDR structure in the
  2054. // array must be the same. A separate RegisterPorts call is
  2055. // required to register multiple ports that are not using the same
  2056. // interface. The address can be INADDR_ANY, in which case the
  2057. // "best" server will be used. If multiple servers are available
  2058. // via different adapters, an adapter with an Internet gateway is
  2059. // selected. If no adapters have Internet gateways, the first
  2060. // adapter with a local firewall is selected. If neither are
  2061. // available, then the first one where either a gateway or a
  2062. // firewall becomes available will be automatically selected.
  2063. // Once one of the adapters has been assigned, it cannot be
  2064. // changed. Since the server chosen by this method may not be
  2065. // optimal for a particular application, it is recommended that
  2066. // individual addresses be registered instead of INADDR_ANY.
  2067. //
  2068. // If the address in aLocalAddresses is not one of those
  2069. // available to the local machine, the registration will still
  2070. // succeed. If an adapter with that address becomes available,
  2071. // the port mapping will automatically be applied, and it will
  2072. // gain a public mapping with any server available to that
  2073. // adapter. If the address was originally available but the
  2074. // network adapter is subsequently removed from the system, any
  2075. // public address mapping is lost. It will be automatically
  2076. // regained if the local address becomes available again. It is
  2077. // recommended that the caller detect local address changes
  2078. // independently and de-register/re-register mappings per adapter
  2079. // as appropriate for maximum control.
  2080. //
  2081. // If the DPNHREGISTERPORTS_SHAREDPORTS flag is used, the
  2082. // server will allow other NAT clients to register it as well.
  2083. // Any UDP traffic received on the public interface will be
  2084. // forwarded to all clients registered. This requires the
  2085. // DPNHREGISTERPORTS_FIXEDPORTS flag and cannot be used with
  2086. // DPNHREGISTERPORTS_TCP.
  2087. //
  2088. // The user should specify a requested lease time that the
  2089. // server will attempt to honor. The actual time remaining can be
  2090. // can be retrieved by calling GetRegisteredAddresses.
  2091. //
  2092. // Note that if a server is not available, this function will
  2093. // still succeed. GetRegisteredAddresses will return
  2094. // DPNHERR_NOMAPPING for the handle returned in phRegisteredPorts
  2095. // in that case. If the server arrives later during the session,
  2096. // calling GetCaps periodically can detect this and automatically
  2097. // map previously registered ports. Use GetRegisteredAddresses to
  2098. // retrieve the newly mapped address when that occurs.
  2099. //
  2100. // Only 16 ports may be registered at a time, but RegisterPorts
  2101. // may be called as many times as desired.
  2102. //
  2103. // The same array of addresses may be registered more than
  2104. // once. Each DPNHHANDLE returned must be released with
  2105. // DeregisterPorts or Close. If an individual address was
  2106. // previously registered but in a different array or a different
  2107. // order in the array, then the DPNHERR_PORTALREADYREGISTERED
  2108. // error code is returned.
  2109. //
  2110. // Arguments:
  2111. // SOCKADDR * aLocalAddresses - Array of local address and port tuples
  2112. // for which remote ports are requested.
  2113. // DWORD dwAddressesSize - Size of entire local addresses array.
  2114. // DWORD dwNumAddresses - Number of SOCKADDR structures in local
  2115. // addresses array.
  2116. // DWORD dwLeaseTime - Requested time, in milliseconds, to lease
  2117. // the ports. As long as GetCaps is
  2118. // called before this time has expired,
  2119. // the lease will automatically be
  2120. // renewed.
  2121. // DPNHHANDLE * phRegisteredPorts - Place to store an identifier for this
  2122. // binding which can later be used to
  2123. // query or release the binding.
  2124. // DWORD dwFlags - Flags to use when registering the port
  2125. // (DPNHREGISTERPORTS_xxx).
  2126. //
  2127. // Returns: HRESULT
  2128. // DPNH_OK - The ports were successfully registered
  2129. // (although no public address may be
  2130. // available yet).
  2131. // DPNHERR_GENERIC - An error occurred that prevented
  2132. // registration of the requested ports.
  2133. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  2134. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  2135. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  2136. // DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
  2137. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  2138. // DPNHERR_OUTOFMEMORY - There is not enough memory to register
  2139. // the ports.
  2140. // DPNHERR_PORTALREADYREGISTERED - At least one of the ports has already
  2141. // been registered in a different address
  2142. // array or order.
  2143. // DPNHERR_REENTRANT - The interface has been re-entered on the
  2144. // same thread.
  2145. //=============================================================================
  2146. STDMETHODIMP CNATHelpUPnP::RegisterPorts(const SOCKADDR * const aLocalAddresses,
  2147. const DWORD dwAddressesSize,
  2148. const DWORD dwNumAddresses,
  2149. const DWORD dwLeaseTime,
  2150. DPNHHANDLE * const phRegisteredPorts,
  2151. const DWORD dwFlags)
  2152. {
  2153. HRESULT hr;
  2154. ULONG ulFirstAddress;
  2155. DWORD dwTemp;
  2156. DWORD dwMatch;
  2157. BOOL fHaveLock = FALSE;
  2158. CRegisteredPort * pRegisteredPort = NULL;
  2159. CDevice * pDevice = NULL;
  2160. CBilink * pBilink;
  2161. SOCKADDR_IN * pasaddrinTemp;
  2162. CUPnPDevice * pUPnPDevice = NULL;
  2163. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, %u, %u, %u, 0x%p, 0x%lx)",
  2164. this, aLocalAddresses, dwAddressesSize, dwNumAddresses, dwLeaseTime,
  2165. phRegisteredPorts, dwFlags);
  2166. //
  2167. // Validate the object.
  2168. //
  2169. if (! this->IsValidObject())
  2170. {
  2171. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  2172. hr = DPNHERR_INVALIDOBJECT;
  2173. goto Failure;
  2174. }
  2175. //
  2176. // Validate the parameters.
  2177. //
  2178. if (aLocalAddresses == NULL)
  2179. {
  2180. DPFX(DPFPREP, 0, "Local addresses array cannot be NULL!");
  2181. hr = DPNHERR_INVALIDPOINTER;
  2182. goto Failure;
  2183. }
  2184. if (dwNumAddresses == 0)
  2185. {
  2186. DPFX(DPFPREP, 0, "Number of addresses cannot be 0!");
  2187. hr = DPNHERR_INVALIDPARAM;
  2188. goto Failure;
  2189. }
  2190. if (dwAddressesSize != (dwNumAddresses * sizeof(SOCKADDR)))
  2191. {
  2192. DPFX(DPFPREP, 0, "Addresses array size invalid!");
  2193. hr = DPNHERR_INVALIDPARAM;
  2194. goto Failure;
  2195. }
  2196. if (IsBadReadPtr(aLocalAddresses, dwAddressesSize))
  2197. {
  2198. DPFX(DPFPREP, 0, "Local addresses array buffer is invalid!");
  2199. hr = DPNHERR_INVALIDPOINTER;
  2200. goto Failure;
  2201. }
  2202. if (dwNumAddresses > DPNH_MAX_SIMULTANEOUS_PORTS)
  2203. {
  2204. DPFX(DPFPREP, 0, "Only %u ports may be registered at a time!", DPNH_MAX_SIMULTANEOUS_PORTS);
  2205. hr = DPNHERR_INVALIDPARAM;
  2206. goto Failure;
  2207. }
  2208. if (((SOCKADDR_IN*) aLocalAddresses)->sin_family != AF_INET)
  2209. {
  2210. DPFX(DPFPREP, 0, "First address in array is not AF_INET, only IPv4 addresses are supported!");
  2211. hr = DPNHERR_INVALIDPARAM;
  2212. goto Failure;
  2213. }
  2214. if (((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_addr == INADDR_BROADCAST)
  2215. {
  2216. DPFX(DPFPREP, 0, "First address cannot be broadcast address!");
  2217. hr = DPNHERR_INVALIDPARAM;
  2218. goto Failure;
  2219. }
  2220. if (((SOCKADDR_IN*) aLocalAddresses)->sin_port == 0)
  2221. {
  2222. DPFX(DPFPREP, 0, "First port in array is 0, a valid port must be specified!");
  2223. hr = DPNHERR_INVALIDPARAM;
  2224. goto Failure;
  2225. }
  2226. ulFirstAddress = ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_addr;
  2227. for(dwTemp = 1; dwTemp < dwNumAddresses; dwTemp++)
  2228. {
  2229. //
  2230. // Make sure this address family type is supported.
  2231. //
  2232. if (((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_family != AF_INET)
  2233. {
  2234. DPFX(DPFPREP, 0, "Address at array index %u is not AF_INET, all items in the array must be the same IPv4 address!",
  2235. dwTemp);
  2236. hr = DPNHERR_INVALIDPARAM;
  2237. goto Failure;
  2238. }
  2239. //
  2240. // If this address doesn't match the first, then the caller broke the
  2241. // rules.
  2242. //
  2243. if (((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_addr != ulFirstAddress)
  2244. {
  2245. //
  2246. // Don't use inet_ntoa because we may not be initialized yet.
  2247. //
  2248. DPFX(DPFPREP, 0, "Address %u.%u.%u.%u at array index %u differs from the first, all addresses in the array must match!",
  2249. ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b1,
  2250. ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b2,
  2251. ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b3,
  2252. ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_un_b.s_b4,
  2253. dwTemp);
  2254. hr = DPNHERR_INVALIDPARAM;
  2255. goto Failure;
  2256. }
  2257. //
  2258. // Make sure this port isn't 0 either.
  2259. //
  2260. if (((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_port == 0)
  2261. {
  2262. DPFX(DPFPREP, 0, "Port at array index %u is 0, valid ports must be specified!", dwTemp);
  2263. hr = DPNHERR_INVALIDPARAM;
  2264. goto Failure;
  2265. }
  2266. }
  2267. if (dwLeaseTime == 0)
  2268. {
  2269. DPFX(DPFPREP, 0, "Invalid lease time specified!");
  2270. hr = DPNHERR_INVALIDPARAM;
  2271. goto Failure;
  2272. }
  2273. if ((phRegisteredPorts == NULL) ||
  2274. (IsBadWritePtr(phRegisteredPorts, sizeof(DPNHHANDLE))))
  2275. {
  2276. DPFX(DPFPREP, 0, "Invalid port mapping handle pointer specified!");
  2277. hr = DPNHERR_INVALIDPOINTER;
  2278. goto Failure;
  2279. }
  2280. if (dwFlags & ~(DPNHREGISTERPORTS_TCP | DPNHREGISTERPORTS_FIXEDPORTS | DPNHREGISTERPORTS_SHAREDPORTS))
  2281. {
  2282. DPFX(DPFPREP, 0, "Invalid flags specified!");
  2283. hr = DPNHERR_INVALIDFLAGS;
  2284. goto Failure;
  2285. }
  2286. if (dwFlags & DPNHREGISTERPORTS_SHAREDPORTS)
  2287. {
  2288. //
  2289. // SHAREDPORTS cannot be used with TCP and requires a FIXEDPORTS.
  2290. //
  2291. if ((dwFlags & DPNHREGISTERPORTS_TCP) || (! (dwFlags & DPNHREGISTERPORTS_FIXEDPORTS)))
  2292. {
  2293. DPFX(DPFPREP, 0, "SHAREDPORTS flag requires FIXEDPORTS flag and cannot be used with TCP flag!");
  2294. hr = DPNHERR_INVALIDFLAGS;
  2295. goto Failure;
  2296. }
  2297. }
  2298. //
  2299. // Attempt to take the lock, but be prepared for the re-entrancy error.
  2300. //
  2301. hr = this->TakeLock();
  2302. if (hr != DPNH_OK)
  2303. {
  2304. DPFX(DPFPREP, 0, "Could not lock object!");
  2305. goto Failure;
  2306. }
  2307. fHaveLock = TRUE;
  2308. //
  2309. // Make sure object is in right state.
  2310. //
  2311. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  2312. {
  2313. DPFX(DPFPREP, 0, "Object not initialized!");
  2314. hr = DPNHERR_NOTINITIALIZED;
  2315. goto Failure;
  2316. }
  2317. //
  2318. // Loop through all existing registered port mappings and look for this
  2319. // array of ports.
  2320. //
  2321. pBilink = this->m_blRegisteredPorts.GetNext();
  2322. while (pBilink != &this->m_blRegisteredPorts)
  2323. {
  2324. DNASSERT(! pBilink->IsEmpty());
  2325. pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
  2326. //
  2327. // Don't bother looking at addresses of the wrong type or at arrays of
  2328. // the wrong size.
  2329. //
  2330. if (((pRegisteredPort->IsTCP() && (dwFlags & DPNHREGISTERPORTS_TCP)) ||
  2331. ((! pRegisteredPort->IsTCP()) && (! (dwFlags & DPNHREGISTERPORTS_TCP)))) &&
  2332. (pRegisteredPort->GetNumAddresses() == dwNumAddresses))
  2333. {
  2334. pasaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
  2335. for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
  2336. {
  2337. //
  2338. // If the addresses don't match, stop looping.
  2339. //
  2340. if ((pasaddrinTemp[dwTemp].sin_port != ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_port) ||
  2341. (pasaddrinTemp[dwTemp].sin_addr.S_un.S_addr != ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_addr))
  2342. {
  2343. break;
  2344. }
  2345. }
  2346. //
  2347. // If all the addresses matched, then this item was already
  2348. // registered.
  2349. //
  2350. if (dwTemp >= dwNumAddresses)
  2351. {
  2352. DPFX(DPFPREP, 1, "Array of %u addresses was already registered, returning existing mapping 0x%p.",
  2353. dwNumAddresses, pRegisteredPort);
  2354. goto ReturnUserHandle;
  2355. }
  2356. DPFX(DPFPREP, 7, "Existing mapping 0x%p does not match all %u addresses.",
  2357. pRegisteredPort, dwNumAddresses);
  2358. }
  2359. else
  2360. {
  2361. //
  2362. // Existing mapping isn't same type or doesn't have same number of
  2363. // items in array.
  2364. //
  2365. }
  2366. pBilink = pBilink->GetNext();
  2367. }
  2368. //
  2369. // If we're here, none of the existing mappings match. Loop through each
  2370. // of the ports and make sure they aren't already registered inside some
  2371. // other mapping.
  2372. //
  2373. for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
  2374. {
  2375. pBilink = this->m_blRegisteredPorts.GetNext();
  2376. while (pBilink != &this->m_blRegisteredPorts)
  2377. {
  2378. DNASSERT(! pBilink->IsEmpty());
  2379. pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
  2380. pasaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
  2381. for(dwMatch = 0; dwMatch < pRegisteredPort->GetNumAddresses(); dwMatch++)
  2382. {
  2383. //
  2384. // If the addresses match, then we can't map these ports.
  2385. //
  2386. if ((pasaddrinTemp[dwMatch].sin_port == ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_port) &&
  2387. (pasaddrinTemp[dwMatch].sin_addr.S_un.S_addr == ((SOCKADDR_IN*) (&aLocalAddresses[dwTemp]))->sin_addr.S_un.S_addr))
  2388. {
  2389. DPFX(DPFPREP, 0, "Existing mapping 0x%p already registered the address %u.%u.%u.%u:%u!",
  2390. pRegisteredPort,
  2391. pasaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b1,
  2392. pasaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b2,
  2393. pasaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b3,
  2394. pasaddrinTemp[dwMatch].sin_addr.S_un.S_un_b.s_b4,
  2395. NTOHS(pasaddrinTemp[dwMatch].sin_port));
  2396. //
  2397. // Clear the pointer so we don't delete the object.
  2398. //
  2399. pRegisteredPort = NULL;
  2400. hr = DPNHERR_PORTALREADYREGISTERED;
  2401. goto Failure;
  2402. }
  2403. }
  2404. pBilink = pBilink->GetNext();
  2405. }
  2406. }
  2407. //
  2408. // If we're here the ports are all unique. Create a new mapping object
  2409. // we'll use to refer to the binding.
  2410. //
  2411. pRegisteredPort = new CRegisteredPort(dwLeaseTime, dwFlags);
  2412. if (pRegisteredPort == NULL)
  2413. {
  2414. hr = DPNHERR_OUTOFMEMORY;
  2415. goto Failure;
  2416. }
  2417. hr = pRegisteredPort->SetPrivateAddresses((SOCKADDR_IN*) aLocalAddresses, dwNumAddresses);
  2418. if (hr != DPNH_OK)
  2419. {
  2420. DPFX(DPFPREP, 0, "Couldn't store private addresses array!");
  2421. goto Failure;
  2422. }
  2423. //
  2424. // Find the device that matches the given addresses.
  2425. //
  2426. // The first entry of aLocalAddresses is representative of all entries since
  2427. // they should all share the same address.
  2428. //
  2429. // Since there won't be an existing registered port for this address, don't
  2430. // bother looking through them for a matching address.
  2431. //
  2432. pDevice = this->FindMatchingDevice((SOCKADDR_IN*) (&aLocalAddresses[0]),
  2433. FALSE);
  2434. if (pDevice == NULL)
  2435. {
  2436. DPFX(DPFPREP, 1, "No device for given address (%u.%u.%u.%u), storing 0x%p in unowned list.",
  2437. ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b1,
  2438. ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b2,
  2439. ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b3,
  2440. ((SOCKADDR_IN*) aLocalAddresses)->sin_addr.S_un.S_un_b.s_b4,
  2441. pRegisteredPort);
  2442. pRegisteredPort->m_blDeviceList.InsertBefore(&this->m_blUnownedPorts);
  2443. }
  2444. else
  2445. {
  2446. pRegisteredPort->MakeDeviceOwner(pDevice);
  2447. #ifndef DPNBUILD_NOHNETFWAPI
  2448. //
  2449. // Start by mapping with the local firewall, if there is one.
  2450. //
  2451. if (pDevice->IsHNetFirewalled())
  2452. {
  2453. hr = this->CheckForLocalHNetFirewallAndMapPorts(pDevice,
  2454. pRegisteredPort);
  2455. if (hr != DPNH_OK)
  2456. {
  2457. DPFX(DPFPREP, 0, "Couldn't check for local HNet firewall and map ports (err = 0x%lx)! Continuing.",
  2458. hr);
  2459. DNASSERT(! pDevice->IsHNetFirewalled());
  2460. hr = DPNH_OK;
  2461. }
  2462. }
  2463. else
  2464. {
  2465. //
  2466. // No local HomeNet firewall (last time we checked).
  2467. //
  2468. }
  2469. #endif // ! DPNBUILD_NOHNETFWAPI
  2470. //
  2471. // Map the ports on the UPnP device, if there is one.
  2472. //
  2473. pUPnPDevice = pDevice->GetUPnPDevice();
  2474. if (pUPnPDevice != NULL)
  2475. {
  2476. //
  2477. // GetUPnPDevice did not add a reference to pUPnPDevice for us.
  2478. //
  2479. pUPnPDevice->AddRef();
  2480. DNASSERT(pUPnPDevice->IsReady());
  2481. //
  2482. // Actually map the ports.
  2483. //
  2484. hr = this->MapPortsOnUPnPDevice(pUPnPDevice, pRegisteredPort);
  2485. if (hr != DPNH_OK)
  2486. {
  2487. DPFX(DPFPREP, 0, "Couldn't map ports on UPnP device 0x%p (0x%lx)! Ignoring.",
  2488. pUPnPDevice, hr);
  2489. //
  2490. // It may have been cleared already, but doing it twice
  2491. // shouldn't be harmful.
  2492. //
  2493. this->ClearDevicesUPnPDevice(pRegisteredPort->GetOwningDevice());
  2494. hr = DPNH_OK;
  2495. }
  2496. pUPnPDevice->DecRef();
  2497. pUPnPDevice = NULL;
  2498. }
  2499. else
  2500. {
  2501. //
  2502. // No UPnP device.
  2503. //
  2504. }
  2505. }
  2506. //
  2507. // Save the mapping in the global list (we have the lock).
  2508. //
  2509. pRegisteredPort->m_blGlobalList.InsertBefore(&this->m_blRegisteredPorts);
  2510. ReturnUserHandle:
  2511. //
  2512. // Remember that a port has been registered.
  2513. //
  2514. this->m_dwFlags |= NATHELPUPNPOBJ_PORTREGISTERED;
  2515. //
  2516. // We're about to give the port to the user.
  2517. //
  2518. pRegisteredPort->AddUserRef();
  2519. //
  2520. // We're going to give the user a direct pointer to the object (disguised
  2521. // as an opaque DPNHHANDLE, of course).
  2522. //
  2523. (*phRegisteredPorts) = (DPNHHANDLE) pRegisteredPort;
  2524. this->DropLock();
  2525. fHaveLock = FALSE;
  2526. DPFX(DPFPREP, 5, "Returning registered port 0x%p (first private address = %u.%u.%u.%u:%u).",
  2527. pRegisteredPort,
  2528. ((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b1,
  2529. ((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b2,
  2530. ((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b3,
  2531. ((SOCKADDR_IN*) aLocalAddresses)[0].sin_addr.S_un.S_un_b.s_b4,
  2532. NTOHS(((SOCKADDR_IN*) aLocalAddresses)[0].sin_port));
  2533. hr = DPNH_OK;
  2534. Exit:
  2535. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  2536. return hr;
  2537. Failure:
  2538. if (pUPnPDevice != NULL)
  2539. {
  2540. pUPnPDevice->DecRef();
  2541. }
  2542. if (pRegisteredPort != NULL)
  2543. {
  2544. #ifndef DPNBUILD_NOHNETFWAPI
  2545. if (pRegisteredPort->IsMappedOnHNetFirewall())
  2546. {
  2547. HRESULT temphr;
  2548. //
  2549. // Unmap the port.
  2550. //
  2551. // Don't bother alerting user about address change. It would have
  2552. // already been taken care of if this was due to a fatal error, and
  2553. // there would be no perceived changed if not.
  2554. //
  2555. temphr = this->UnmapPortOnLocalHNetFirewall(pRegisteredPort, TRUE, FALSE);
  2556. if (temphr != DPNH_OK)
  2557. {
  2558. DPFX(DPFPREP, 0, "Failed unmapping registered port 0x%p on local HomeNet firewall (err = 0x%lx)! Ignoring.",
  2559. pRegisteredPort, temphr);
  2560. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  2561. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  2562. }
  2563. }
  2564. #endif // ! DPNBUILD_NOHNETFWAPI
  2565. if (pDevice != NULL)
  2566. {
  2567. pRegisteredPort->ClearDeviceOwner();
  2568. }
  2569. pRegisteredPort->ClearPrivateAddresses();
  2570. delete pRegisteredPort;
  2571. }
  2572. if (fHaveLock)
  2573. {
  2574. this->DropLock();
  2575. fHaveLock = FALSE;
  2576. }
  2577. goto Exit;
  2578. } // CNATHelpUPnP::RegisterPorts
  2579. #undef DPF_MODNAME
  2580. #define DPF_MODNAME "CNATHelpUPnP::GetRegisteredAddresses"
  2581. //=============================================================================
  2582. // CNATHelpUPnP::GetRegisteredAddresses
  2583. //-----------------------------------------------------------------------------
  2584. //
  2585. // Description: Returns the current public address mappings for a given
  2586. // registered port group. If there are no servers currently
  2587. // available, then DPNHERR_SERVERNOTAVAILABLE is returned. If the
  2588. // servers' public interfaces are not currently valid, then
  2589. // DPNHERR_NOMAPPING is returned, but appropriate values will
  2590. // still be placed in pdwAddressTypeFlags and
  2591. // pdwLeaseTimeRemaining.
  2592. //
  2593. // If the mapping was registered with the
  2594. // DPNHREGISTERPORTS_FIXEDPORTS flag, but at least one port is
  2595. // already in use on the gateway, then DPNHERR_PORTUNAVAILABLE is
  2596. // returned and appropriate flags will still be placed in
  2597. // pdwAddressTypeFlags.
  2598. //
  2599. // If the local machine has a cooperative firewall installed,
  2600. // the requested port is opened locally on the firewall before
  2601. // being mapped on the Internet gateway. Normally this function
  2602. // returns the public address on the Internet gateway address when
  2603. // both are present. Since some firewalls remap the port number
  2604. // when opening non-fixed ports, the
  2605. // DPNHGETREGISTEREDADDRESSES_LOCALFIREWALLREMAPONLY allows the
  2606. // caller to retrieve the locally remapped address, even if there
  2607. // is a mapping on an Internet gateway.
  2608. //
  2609. // Some gateway devices do not natively support ports that are
  2610. // not fixed, and may generate the DPNHERR_PORTUNAVAILABLE return
  2611. // code even when the DPNHREGISTERPORTS_FIXEDPORTS flag was not
  2612. // specified. The caller should de-register the port mapping
  2613. // handle, rebind the application to different ports, and call
  2614. // RegisterPorts again.
  2615. //
  2616. // If the buffer indicated by paPublicAddresses is too small,
  2617. // then the size required is returned in pdwPublicAddressesSize
  2618. // and DPNHERR_BUFFERTOOSMALL is returned. Otherwise the number of
  2619. // bytes written is returned in pdwPublicAddressesSize.
  2620. //
  2621. // Even though the addresses are returned as individual
  2622. // SOCKADDRs, all ports registered at the same time will share the
  2623. // same public address. Only the port components will vary.
  2624. //
  2625. // All buffers are optional and may be NULL, but if
  2626. // paPublicAddresses is specified, it must be accompanied by an
  2627. // appropriate size in pdwPublicAddressesSize.
  2628. //
  2629. // If GetCaps has not been previously called with the
  2630. // DPNHGETCAPS_UPDATESERVERSTATUS flag at least once, then the
  2631. // error code DPNHERR_UPDATESERVERSTATUS is returned.
  2632. //
  2633. // Arguments:
  2634. // DPNHHANDLE hRegisteredPorts - Handle for a specific binding returned by
  2635. // RegisterPorts.
  2636. // SOCKADDR * paPublicAddresses - Buffer to return assigned public realm
  2637. // address, or NULL if not desired.
  2638. // DWORD * pdwPublicAddressesSize - Pointer to size of paPublicAddresses
  2639. // buffer, or place to store size
  2640. // required/written. Cannot be NULL if
  2641. // paPublicAddresses is not NULL.
  2642. // DWORD * pdwAddressTypeFlags - Place to store flags describing the
  2643. // address types returned, or NULL if not
  2644. // desired.
  2645. // DWORD * pdwLeaseTimeRemaining - Place to store approximate number of
  2646. // milliseconds remaining in the port
  2647. // lease, or NULL if not desired. Call
  2648. // GetCaps to automatically extend leases
  2649. // about to expire.
  2650. // DWORD dwFlags - Unused, must be zero.
  2651. //
  2652. // Returns: HRESULT
  2653. // DPNH_OK - Information on the port mapping was found and
  2654. // the addresses were stored in
  2655. // paPublicAddresses.
  2656. // DPNHERR_BUFFERTOOSMALL - There was not enough room in the buffer to
  2657. // store the addresses.
  2658. // DPNHERR_GENERIC - An error occurred while retrieving the
  2659. // requested port mapping.
  2660. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  2661. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  2662. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  2663. // DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
  2664. // DPNHERR_NOMAPPING - The server(s) do not have valid public
  2665. // interfaces.
  2666. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  2667. // DPNHERR_OUTOFMEMORY - There is not enough memory to get the
  2668. // addresses.
  2669. // DPNHERR_PORTUNAVAILABLE - At least one of the ports is not available on
  2670. // the server.
  2671. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  2672. // thread.
  2673. // DPNHERR_SERVERNOTAVAILABLE - No servers are currently present.
  2674. // DPNHERR_UPDATESERVERSTATUS - GetCaps has not been called with the
  2675. // DPNHGETCAPS_UPDATESERVERSTATUS flag yet.
  2676. //=============================================================================
  2677. STDMETHODIMP CNATHelpUPnP::GetRegisteredAddresses(const DPNHHANDLE hRegisteredPorts,
  2678. SOCKADDR * const paPublicAddresses,
  2679. DWORD * const pdwPublicAddressesSize,
  2680. DWORD * const pdwAddressTypeFlags,
  2681. DWORD * const pdwLeaseTimeRemaining,
  2682. const DWORD dwFlags)
  2683. {
  2684. HRESULT hr;
  2685. CRegisteredPort * pRegisteredPort;
  2686. BOOL fHaveLock = FALSE;
  2687. BOOL fRegisteredWithServer = FALSE;
  2688. BOOL fFoundValidMapping = FALSE;
  2689. BOOL fPortIsUnavailable = FALSE;
  2690. DWORD dwSizeRequired;
  2691. DWORD dwAddressTypeFlags;
  2692. DWORD dwCurrentTime;
  2693. //DWORD dwTempLeaseTimeRemaining;
  2694. DWORD dwLeaseTimeRemaining = -1;
  2695. CDevice * pDevice;
  2696. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%p, 0x%p, 0x%lx)",
  2697. this, hRegisteredPorts, paPublicAddresses, pdwPublicAddressesSize,
  2698. pdwAddressTypeFlags, pdwLeaseTimeRemaining, dwFlags);
  2699. //
  2700. // Validate the object.
  2701. //
  2702. if (! this->IsValidObject())
  2703. {
  2704. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  2705. hr = DPNHERR_INVALIDOBJECT;
  2706. goto Failure;
  2707. }
  2708. //
  2709. // Validate the parameters.
  2710. //
  2711. pRegisteredPort = (CRegisteredPort*) hRegisteredPorts;
  2712. if (! pRegisteredPort->IsValidObject())
  2713. {
  2714. DPFX(DPFPREP, 0, "Invalid registered port mapping handle specified!");
  2715. hr = DPNHERR_INVALIDPARAM;
  2716. goto Failure;
  2717. }
  2718. if (paPublicAddresses != NULL)
  2719. {
  2720. if ((pdwPublicAddressesSize == NULL) ||
  2721. (IsBadWritePtr(pdwPublicAddressesSize, sizeof(DWORD))))
  2722. {
  2723. DPFX(DPFPREP, 0, "When specifying a public addresses buffer, a valid size must be given!");
  2724. hr = DPNHERR_INVALIDPOINTER;
  2725. goto Failure;
  2726. }
  2727. if (IsBadWritePtr(paPublicAddresses, (*pdwPublicAddressesSize)))
  2728. {
  2729. DPFX(DPFPREP, 0, "The public addresses buffer is invalid!");
  2730. hr = DPNHERR_INVALIDPOINTER;
  2731. goto Failure;
  2732. }
  2733. }
  2734. else
  2735. {
  2736. if ((pdwPublicAddressesSize != NULL) &&
  2737. (IsBadWritePtr(pdwPublicAddressesSize, sizeof(DWORD))))
  2738. {
  2739. DPFX(DPFPREP, 0, "Invalid pointer for size of public addresses buffer!");
  2740. hr = DPNHERR_INVALIDPOINTER;
  2741. goto Failure;
  2742. }
  2743. }
  2744. if ((pdwAddressTypeFlags != NULL) &&
  2745. (IsBadWritePtr(pdwAddressTypeFlags, sizeof(DWORD))))
  2746. {
  2747. DPFX(DPFPREP, 0, "Invalid pointer for address type flags!");
  2748. hr = DPNHERR_INVALIDPOINTER;
  2749. goto Failure;
  2750. }
  2751. if ((pdwLeaseTimeRemaining != NULL) &&
  2752. (IsBadWritePtr(pdwLeaseTimeRemaining, sizeof(DWORD))))
  2753. {
  2754. DPFX(DPFPREP, 0, "Invalid pointer for lease time remaining!");
  2755. hr = DPNHERR_INVALIDPOINTER;
  2756. goto Failure;
  2757. }
  2758. if (dwFlags & ~DPNHGETREGISTEREDADDRESSES_LOCALFIREWALLREMAPONLY)
  2759. {
  2760. DPFX(DPFPREP, 0, "Invalid flags specified!");
  2761. hr = DPNHERR_INVALIDFLAGS;
  2762. goto Failure;
  2763. }
  2764. //
  2765. // Start the flags off with the information regarding TCP vs. UDP.
  2766. //
  2767. if (pRegisteredPort->IsTCP())
  2768. {
  2769. dwAddressTypeFlags = DPNHADDRESSTYPE_TCP;
  2770. }
  2771. else
  2772. {
  2773. dwAddressTypeFlags = 0;
  2774. }
  2775. //
  2776. // Add in other flags we know already.
  2777. //
  2778. if (pRegisteredPort->IsFixedPort())
  2779. {
  2780. dwAddressTypeFlags |= DPNHADDRESSTYPE_FIXEDPORTS;
  2781. }
  2782. if (pRegisteredPort->IsSharedPort())
  2783. {
  2784. dwAddressTypeFlags |= DPNHADDRESSTYPE_SHAREDPORTS;
  2785. }
  2786. //
  2787. // Attempt to take the lock, but be prepared for the re-entrancy error.
  2788. //
  2789. hr = this->TakeLock();
  2790. if (hr != DPNH_OK)
  2791. {
  2792. DPFX(DPFPREP, 0, "Could not lock object!");
  2793. goto Failure;
  2794. }
  2795. fHaveLock = TRUE;
  2796. //
  2797. // Make sure object is in right state.
  2798. //
  2799. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  2800. {
  2801. DPFX(DPFPREP, 0, "Object not initialized!");
  2802. hr = DPNHERR_NOTINITIALIZED;
  2803. goto Failure;
  2804. }
  2805. if (this->m_dwLastUpdateServerStatusTime == 0)
  2806. {
  2807. DPFX(DPFPREP, 0, "GetCaps has not been called with UPDATESERVERSTATUS flag yet!");
  2808. hr = DPNHERR_UPDATESERVERSTATUS;
  2809. goto Failure;
  2810. }
  2811. //
  2812. // Get a shortcut pointer to the device (may not exist).
  2813. //
  2814. pDevice = pRegisteredPort->GetOwningDevice();
  2815. //
  2816. // Get the current time for both the remote and local lease
  2817. // calculations.
  2818. //
  2819. dwCurrentTime = GETTIMESTAMP();
  2820. if (! (dwFlags & DPNHGETREGISTEREDADDRESSES_LOCALFIREWALLREMAPONLY))
  2821. {
  2822. CUPnPDevice * pUPnPDevice;
  2823. //
  2824. // First check for a mapping on the UPnP device.
  2825. //
  2826. if (pRegisteredPort->HasUPnPPublicAddresses())
  2827. {
  2828. DNASSERT(pDevice != NULL);
  2829. pUPnPDevice = pDevice->GetUPnPDevice();
  2830. DNASSERT(pUPnPDevice != NULL);
  2831. fRegisteredWithServer = TRUE;
  2832. //
  2833. // Make sure the UPnP device currently has a valid external
  2834. // address. If so, hand the mapping out.
  2835. //
  2836. if (pUPnPDevice->GetExternalIPAddressV4() != 0)
  2837. {
  2838. if (pdwPublicAddressesSize != NULL)
  2839. {
  2840. dwSizeRequired = pRegisteredPort->GetAddressesSize();
  2841. if ((paPublicAddresses == NULL) ||
  2842. (dwSizeRequired > (*pdwPublicAddressesSize)))
  2843. {
  2844. //
  2845. // Not enough room in buffer, return the size required
  2846. // and the BUFFERTOOSMALL error code.
  2847. //
  2848. (*pdwPublicAddressesSize) = dwSizeRequired;
  2849. hr = DPNHERR_BUFFERTOOSMALL;
  2850. }
  2851. else
  2852. {
  2853. //
  2854. // Buffer was large enough, return the size written.
  2855. //
  2856. (*pdwPublicAddressesSize) = dwSizeRequired;
  2857. pRegisteredPort->CopyUPnPPublicAddresses((SOCKADDR_IN*) paPublicAddresses);
  2858. }
  2859. }
  2860. else
  2861. {
  2862. //
  2863. // Not using address buffer.
  2864. //
  2865. }
  2866. fFoundValidMapping = TRUE;
  2867. }
  2868. else
  2869. {
  2870. DPFX(DPFPREP, 8, "The UPnP Internet Gateway Device does not currently have a valid public address.");
  2871. }
  2872. //
  2873. // Add in the flag indicating that there's a UPnP device.
  2874. //
  2875. dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAY;
  2876. //
  2877. // See if the UPnP device is local.
  2878. //
  2879. if (pUPnPDevice->IsLocal())
  2880. {
  2881. dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAYISLOCAL;
  2882. }
  2883. //
  2884. // Get the relative UPnP lease time remaining, if it's not
  2885. // permanent.
  2886. //
  2887. if (! pRegisteredPort->HasPermanentUPnPLease())
  2888. {
  2889. dwLeaseTimeRemaining = pRegisteredPort->GetUPnPLeaseExpiration() - dwCurrentTime;
  2890. if (((int) dwLeaseTimeRemaining) < 0)
  2891. {
  2892. DPFX(DPFPREP, 1, "Registered port mapping's UPnP lease has already expired, returning 0 for lease time remaining.");
  2893. dwLeaseTimeRemaining = 0;
  2894. }
  2895. }
  2896. }
  2897. else if (pRegisteredPort->IsUPnPPortUnavailable())
  2898. {
  2899. DNASSERT(pDevice != NULL);
  2900. pUPnPDevice = pDevice->GetUPnPDevice();
  2901. DNASSERT(pUPnPDevice != NULL);
  2902. fRegisteredWithServer = TRUE;
  2903. fPortIsUnavailable = TRUE;
  2904. DPFX(DPFPREP, 8, "The UPnP device indicates the port(s) are unavailable.");
  2905. //
  2906. // Add in the flag indicating that there's a UPnP device.
  2907. //
  2908. dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAY;
  2909. //
  2910. // See if the UPnP device is local.
  2911. //
  2912. if (pUPnPDevice->IsLocal())
  2913. {
  2914. dwAddressTypeFlags |= DPNHADDRESSTYPE_GATEWAYISLOCAL;
  2915. }
  2916. }
  2917. }
  2918. else
  2919. {
  2920. //
  2921. // We're not allowed to return the UPnP mapping.
  2922. //
  2923. DPFX(DPFPREP, 8, "Ignoring any Internet gateway mappings, LOCALFIREWALLREMAPONLY was specified.");
  2924. }
  2925. #ifndef DPNBUILD_NOHNETFWAPI
  2926. //
  2927. // Finally, check for a mapping on a local firewall.
  2928. //
  2929. if (pRegisteredPort->IsMappedOnHNetFirewall())
  2930. {
  2931. DNASSERT(pDevice != NULL);
  2932. DNASSERT(pDevice->IsHNetFirewalled());
  2933. fRegisteredWithServer = TRUE;
  2934. //
  2935. // If we didn't already get a remote mapping, return this local one.
  2936. //
  2937. if (! fFoundValidMapping)
  2938. {
  2939. if (pdwPublicAddressesSize != NULL)
  2940. {
  2941. dwSizeRequired = pRegisteredPort->GetAddressesSize();
  2942. if ((paPublicAddresses == NULL) ||
  2943. (dwSizeRequired > (*pdwPublicAddressesSize)))
  2944. {
  2945. //
  2946. // Not enough room in buffer, return the size required
  2947. // and the BUFFERTOOSMALL error code.
  2948. //
  2949. (*pdwPublicAddressesSize) = dwSizeRequired;
  2950. hr = DPNHERR_BUFFERTOOSMALL;
  2951. }
  2952. else
  2953. {
  2954. SOCKADDR_IN * pasaddrinPrivate;
  2955. DWORD dwTemp;
  2956. //
  2957. // Buffer was large enough, return the size written.
  2958. //
  2959. (*pdwPublicAddressesSize) = dwSizeRequired;
  2960. //
  2961. // Note that the addresses mapped on the firewall are the
  2962. // same as the private addresses.
  2963. //
  2964. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  2965. DNASSERT(pasaddrinPrivate != NULL);
  2966. memcpy(paPublicAddresses, pasaddrinPrivate, dwSizeRequired);
  2967. //
  2968. // However, we don't want to ever return 0.0.0.0, so make
  2969. // sure they get the device address.
  2970. //
  2971. if (pasaddrinPrivate[0].sin_addr.S_un.S_addr == INADDR_ANY)
  2972. {
  2973. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  2974. {
  2975. ((SOCKADDR_IN*) paPublicAddresses)[dwTemp].sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  2976. }
  2977. DPFX(DPFPREP, 7, "Returning device address %u.%u.%u.%u instead of INADDR_ANY for firewalled port mapping 0x%p.",
  2978. ((SOCKADDR_IN*) paPublicAddresses)[0].sin_addr.S_un.S_un_b.s_b1,
  2979. ((SOCKADDR_IN*) paPublicAddresses)[0].sin_addr.S_un.S_un_b.s_b2,
  2980. ((SOCKADDR_IN*) paPublicAddresses)[0].sin_addr.S_un.S_un_b.s_b3,
  2981. ((SOCKADDR_IN*) paPublicAddresses)[0].sin_addr.S_un.S_un_b.s_b4,
  2982. pRegisteredPort);
  2983. }
  2984. }
  2985. }
  2986. else
  2987. {
  2988. //
  2989. // Not using address buffer.
  2990. //
  2991. }
  2992. fFoundValidMapping = TRUE;
  2993. }
  2994. else
  2995. {
  2996. DPFX(DPFPREP, 6, "Ignoring local HomeNet firewall mapping due to UPnP mapping.");
  2997. }
  2998. //
  2999. // Add in the flag indicating the local firewall.
  3000. //
  3001. dwAddressTypeFlags |= DPNHADDRESSTYPE_LOCALFIREWALL;
  3002. //
  3003. // The firewall API does not allow for lease times.
  3004. //
  3005. }
  3006. else
  3007. {
  3008. if (pRegisteredPort->IsHNetFirewallPortUnavailable())
  3009. {
  3010. DNASSERT(pDevice != NULL);
  3011. DNASSERT(pDevice->IsHNetFirewalled());
  3012. fRegisteredWithServer = TRUE;
  3013. fPortIsUnavailable = TRUE;
  3014. DPFX(DPFPREP, 8, "The local HomeNet firewall indicates the port(s) are unavailable.");
  3015. //
  3016. // Add in the flag indicating the local firewall.
  3017. //
  3018. dwAddressTypeFlags |= DPNHADDRESSTYPE_LOCALFIREWALL;
  3019. }
  3020. #ifdef DBG
  3021. else
  3022. {
  3023. //
  3024. // No local firewall or it's an unowned port.
  3025. //
  3026. if (pDevice != NULL)
  3027. {
  3028. DNASSERT(! pDevice->IsHNetFirewalled());
  3029. }
  3030. else
  3031. {
  3032. DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts));
  3033. }
  3034. }
  3035. #endif // DBG
  3036. }
  3037. #endif // ! DPNBUILD_NOHNETFWAPI
  3038. this->DropLock();
  3039. fHaveLock = FALSE;
  3040. if (fRegisteredWithServer)
  3041. {
  3042. DNASSERT(dwAddressTypeFlags & (DPNHADDRESSTYPE_LOCALFIREWALL | DPNHADDRESSTYPE_GATEWAY));
  3043. if (! fFoundValidMapping)
  3044. {
  3045. if (fPortIsUnavailable)
  3046. {
  3047. //
  3048. // The servers indicated that the ports were already in use.
  3049. // Return PORTUNAVAILABLE.
  3050. //
  3051. DPFX(DPFPREP, 1, "The Internet gateway(s) could not map the port, returning PORTUNAVAILABLE.");
  3052. hr = DPNHERR_PORTUNAVAILABLE;
  3053. }
  3054. else
  3055. {
  3056. //
  3057. // The servers didn't have public addresses. Return NOMAPPING.
  3058. //
  3059. DPFX(DPFPREP, 1, "The Internet gateway(s) did not offer valid public addresses, returning NOMAPPING.");
  3060. hr = DPNHERR_NOMAPPING;
  3061. }
  3062. }
  3063. else
  3064. {
  3065. //
  3066. // One of the servers had a public address.
  3067. //
  3068. DNASSERT((hr == DPNH_OK) || (hr == DPNHERR_BUFFERTOOSMALL));
  3069. }
  3070. }
  3071. else
  3072. {
  3073. //
  3074. // The ports aren't registered, because there aren't any gateways.
  3075. // Return SERVERNOTAVAILABLE.
  3076. //
  3077. DPFX(DPFPREP, 1, "No Internet gateways, returning SERVERNOTAVAILABLE.");
  3078. hr = DPNHERR_SERVERNOTAVAILABLE;
  3079. }
  3080. //
  3081. // If the caller wants information on the type of these addresses, return
  3082. // the flags we detected.
  3083. //
  3084. if (pdwAddressTypeFlags != NULL)
  3085. {
  3086. (*pdwAddressTypeFlags) = dwAddressTypeFlags;
  3087. }
  3088. //
  3089. // Return the minimum lease time remaining that we already calculated, if
  3090. // the caller wants it.
  3091. //
  3092. if (pdwLeaseTimeRemaining != NULL)
  3093. {
  3094. (*pdwLeaseTimeRemaining) = dwLeaseTimeRemaining;
  3095. }
  3096. #ifdef DBG
  3097. //
  3098. // If the port is unavailable or there aren't any servers, we better not
  3099. // have a lease time.
  3100. //
  3101. if ((hr == DPNHERR_PORTUNAVAILABLE) ||
  3102. (hr == DPNHERR_SERVERNOTAVAILABLE))
  3103. {
  3104. DNASSERT(dwLeaseTimeRemaining == -1);
  3105. }
  3106. //
  3107. // If there aren't any servers, we better not have server flags.
  3108. //
  3109. if (hr == DPNHERR_SERVERNOTAVAILABLE)
  3110. {
  3111. DNASSERT(! (dwAddressTypeFlags & (DPNHADDRESSTYPE_LOCALFIREWALL | DPNHADDRESSTYPE_GATEWAY)));
  3112. }
  3113. #endif // DBG
  3114. DPFX(DPFPREP, 5, "Registered port 0x%p addr type flags = 0x%lx, lease time remaining = %i.",
  3115. pRegisteredPort, dwAddressTypeFlags, (int) dwLeaseTimeRemaining);
  3116. Exit:
  3117. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  3118. return hr;
  3119. Failure:
  3120. if (fHaveLock)
  3121. {
  3122. this->DropLock();
  3123. fHaveLock = FALSE;
  3124. }
  3125. goto Exit;
  3126. } // CNATHelpUPnP::GetRegisteredAddresses
  3127. #undef DPF_MODNAME
  3128. #define DPF_MODNAME "CNATHelpUPnP::DeregisterPorts"
  3129. //=============================================================================
  3130. // CNATHelpUPnP::DeregisterPorts
  3131. //-----------------------------------------------------------------------------
  3132. //
  3133. // Description: Removes the lease record for the port group and informs the
  3134. // Internet gateway server that the binding is no longer needed.
  3135. // The port mapping handle must not be used after de-registering
  3136. // it.
  3137. //
  3138. // Arguments:
  3139. // DPNHHANDLE hRegisteredPorts - Handle for a specific binding returned by
  3140. // RegisterPorts.
  3141. // DWORD dwFlags - Unused, must be zero.
  3142. //
  3143. // Returns: HRESULT
  3144. // DPNH_OK - The binding was successfully released.
  3145. // DPNHERR_GENERIC - An error occurred that prevented the
  3146. // de-registration of the ports.
  3147. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  3148. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  3149. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  3150. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  3151. // DPNHERR_OUTOFMEMORY - There is not enough memory to de-register.
  3152. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  3153. // thread.
  3154. //=============================================================================
  3155. STDMETHODIMP CNATHelpUPnP::DeregisterPorts(const DPNHHANDLE hRegisteredPorts,
  3156. const DWORD dwFlags)
  3157. {
  3158. HRESULT hr;
  3159. CRegisteredPort * pRegisteredPort;
  3160. BOOL fHaveLock = FALSE;
  3161. LONG lResult;
  3162. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx)",
  3163. this, hRegisteredPorts, dwFlags);
  3164. //
  3165. // Validate the object.
  3166. //
  3167. if (! this->IsValidObject())
  3168. {
  3169. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  3170. hr = DPNHERR_INVALIDOBJECT;
  3171. goto Failure;
  3172. }
  3173. //
  3174. // Validate the parameters.
  3175. //
  3176. pRegisteredPort = (CRegisteredPort*) hRegisteredPorts;
  3177. if (! pRegisteredPort->IsValidObject())
  3178. {
  3179. DPFX(DPFPREP, 0, "Invalid registered port mapping handle specified!");
  3180. hr = DPNHERR_INVALIDPARAM;
  3181. goto Failure;
  3182. }
  3183. if (dwFlags != 0)
  3184. {
  3185. DPFX(DPFPREP, 0, "Invalid flags specified!");
  3186. hr = DPNHERR_INVALIDFLAGS;
  3187. goto Failure;
  3188. }
  3189. //
  3190. // Attempt to take the lock, but be prepared for the re-entrancy error.
  3191. //
  3192. hr = this->TakeLock();
  3193. if (hr != DPNH_OK)
  3194. {
  3195. DPFX(DPFPREP, 0, "Could not lock object!");
  3196. goto Failure;
  3197. }
  3198. fHaveLock = TRUE;
  3199. //
  3200. // Make sure object is in right state.
  3201. //
  3202. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  3203. {
  3204. DPFX(DPFPREP, 0, "Object not initialized!");
  3205. hr = DPNHERR_NOTINITIALIZED;
  3206. goto Failure;
  3207. }
  3208. //
  3209. // If this isn't the last user reference on the registered port, don't
  3210. // unmap it yet.
  3211. //
  3212. lResult = pRegisteredPort->DecUserRef();
  3213. if (lResult != 0)
  3214. {
  3215. DPFX(DPFPREP, 1, "Still %i references left on registered port 0x%p, not unmapping.",
  3216. lResult, pRegisteredPort);
  3217. goto Exit;
  3218. }
  3219. //
  3220. // First unmap from UPnP device, if necessary.
  3221. //
  3222. if (pRegisteredPort->HasUPnPPublicAddresses())
  3223. {
  3224. hr = this->UnmapUPnPPort(pRegisteredPort,
  3225. pRegisteredPort->GetNumAddresses(), // free all ports
  3226. TRUE);
  3227. if (hr != DPNH_OK)
  3228. {
  3229. DPFX(DPFPREP, 0, "Couldn't delete port mapping with UPnP device (0x%lx)! Ignoring.", hr);
  3230. //
  3231. // We'll treat this as non-fatal, but we have to dump the device.
  3232. //
  3233. this->ClearDevicesUPnPDevice(pRegisteredPort->GetOwningDevice());
  3234. hr = DPNH_OK;
  3235. }
  3236. }
  3237. #ifndef DPNBUILD_NOHNETFWAPI
  3238. //
  3239. // Then unmap from the local firewall, if necessary.
  3240. //
  3241. if (pRegisteredPort->IsMappedOnHNetFirewall())
  3242. {
  3243. //
  3244. // Unmap the port.
  3245. //
  3246. // Don't bother alerting user about address change, this is normal
  3247. // operation.
  3248. //
  3249. hr = this->UnmapPortOnLocalHNetFirewall(pRegisteredPort, TRUE, FALSE);
  3250. if (hr != DPNH_OK)
  3251. {
  3252. DPFX(DPFPREP, 0, "Failed unmapping registered port 0x%p on local HomeNet firewall (err = 0x%lx)! Ignoring.",
  3253. pRegisteredPort, hr);
  3254. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  3255. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  3256. hr = DPNH_OK;
  3257. }
  3258. }
  3259. #endif // ! DPNBUILD_NOHNETFWAPI
  3260. //
  3261. // Pull the item out of the lists.
  3262. // We have the appropriate lock.
  3263. //
  3264. DNASSERT(pRegisteredPort->m_blGlobalList.IsListMember(&this->m_blRegisteredPorts));
  3265. pRegisteredPort->m_blGlobalList.RemoveFromList();
  3266. if (pRegisteredPort->GetOwningDevice() != NULL)
  3267. {
  3268. DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&((pRegisteredPort->GetOwningDevice())->m_blOwnedRegPorts)));
  3269. pRegisteredPort->ClearDeviceOwner();
  3270. }
  3271. else
  3272. {
  3273. DNASSERT(pRegisteredPort->m_blDeviceList.IsListMember(&this->m_blUnownedPorts));
  3274. pRegisteredPort->m_blDeviceList.RemoveFromList();
  3275. }
  3276. pRegisteredPort->ClearPrivateAddresses();
  3277. delete pRegisteredPort;
  3278. Exit:
  3279. if (fHaveLock)
  3280. {
  3281. this->DropLock();
  3282. fHaveLock = FALSE;
  3283. }
  3284. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  3285. return hr;
  3286. Failure:
  3287. goto Exit;
  3288. } // CNATHelpUPnP::DeregisterPorts
  3289. #undef DPF_MODNAME
  3290. #define DPF_MODNAME "CNATHelpUPnP::QueryAddress"
  3291. //=============================================================================
  3292. // CNATHelpUPnP::QueryAddress
  3293. //-----------------------------------------------------------------------------
  3294. //
  3295. // Description: Some Internet gateways do not loopback if an attempt is made
  3296. // to connect to an address behind (on the same private side of)
  3297. // the public interface. QueryAddress is used to determine a
  3298. // possible private alias for a given public address.
  3299. //
  3300. // In most cases, this function is called prior to connecting
  3301. // to a new address. pSourceAddress should contain the address of
  3302. // the socket that will perform the connect. Similar to
  3303. // RegisterPorts, the address may be INADDR_ANY, in which case the
  3304. // "best" server will be used. Since the server chosen may not be
  3305. // optimal for a particular application, it is recommended that a
  3306. // specific network interface be used instead of INADDR_ANY, when
  3307. // possible.
  3308. //
  3309. // If no mapping for that address has been made by the gateway,
  3310. // the error code DPNHERR_NOMAPPING is returned. When the
  3311. // DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED flag is used, an
  3312. // extra effort is made to determine whether the address is behind
  3313. // the same Internet gateway without being mapped on the gateway.
  3314. // If that is the case, DPNHERR_NOMAPPINGBUTPRIVATE is returned.
  3315. // DPNHERR_NOMAPPING is still returned for addresses that are
  3316. // neither mapped nor private.
  3317. //
  3318. // pQueryAddress may not be INADDR_ANY or INADDR_BROADCAST.
  3319. // The port component may be zero if and only if the
  3320. // DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED flag is used. If
  3321. // the port is zero, a specific mapping cannot be verified, and
  3322. // only the DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED aspect of
  3323. // the address is tested.
  3324. //
  3325. // The resulting address (or lack thereof) can be cached for
  3326. // quick future retrieval using the DPNHQUERYADDRESS_CACHEFOUND
  3327. // and DPNHQUERYADDRESS_CACHENOTFOUND flags. The cached mappings
  3328. // will expire in 1 minute, or whenever the server's address
  3329. // changes.
  3330. //
  3331. // If the given source address is not currently connected to an
  3332. // Internet gateway, then the error DPNHERR_SERVERNOTAVAILABLE is
  3333. // returned.
  3334. //
  3335. // If GetCaps has not been previously called with the
  3336. // DPNHGETCAPS_UPDATESERVERSTATUS flag at least once, then the
  3337. // error code DPNHERR_UPDATESERVERSTATUS is returned.
  3338. //
  3339. // Arguments:
  3340. // SOCKADDR * pSourceAddress - Address for network interface that is using
  3341. // the address in question.
  3342. // SOCKADDR * pQueryAddress - Address to look up.
  3343. // SOCKADDR * pResponseAddress - Place to store public address, if one exists.
  3344. // int iAddressesSize - Size of the SOCKADDR structure used for the
  3345. // pSourceAddress, pQueryAddress and
  3346. // pResponseAddress buffers.
  3347. // DWORD dwFlags - Flags to use when querying
  3348. // (DPNHQUERYADDRESS_xxx).
  3349. //
  3350. // Returns: HRESULT
  3351. // DPNH_OK - The address was found and its mapping was
  3352. // stored in pResponseAddress.
  3353. // DPNHERR_GENERIC - An error occurred that prevented mapping the
  3354. // requested address.
  3355. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  3356. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  3357. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  3358. // DPNHERR_INVALIDPOINTER - An invalid pointer was specified.
  3359. // DPNHERR_NOMAPPING - The server indicated that no mapping for the
  3360. // requested address was found.
  3361. // DPNHERR_NOMAPPINGBUTPRIVATE - The server indicated that no mapping was
  3362. // found, but it is a private address.
  3363. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  3364. // DPNHERR_OUTOFMEMORY - There is not enough memory to query.
  3365. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  3366. // thread.
  3367. // DPNHERR_SERVERNOTAVAILABLE - There are no servers to query.
  3368. // DPNHERR_UPDATESERVERSTATUS - GetCaps has not been called with the
  3369. // DPNHGETCAPS_UPDATESERVERSTATUS flag yet.
  3370. //=============================================================================
  3371. STDMETHODIMP CNATHelpUPnP::QueryAddress(const SOCKADDR * const pSourceAddress,
  3372. const SOCKADDR * const pQueryAddress,
  3373. SOCKADDR * const pResponseAddress,
  3374. const int iAddressesSize,
  3375. const DWORD dwFlags)
  3376. {
  3377. HRESULT hr;
  3378. BOOL fHaveLock = FALSE;
  3379. CDevice * pDevice;
  3380. SOCKADDR_IN * psaddrinNextServerQueryAddress = NULL;
  3381. CUPnPDevice * pUPnPDevice = NULL;
  3382. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, %i, 0x%lx)",
  3383. this, pSourceAddress, pQueryAddress, pResponseAddress, iAddressesSize,
  3384. dwFlags);
  3385. //
  3386. // Validate the object.
  3387. //
  3388. if (! this->IsValidObject())
  3389. {
  3390. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  3391. hr = DPNHERR_INVALIDOBJECT;
  3392. goto Failure;
  3393. }
  3394. //
  3395. // Validate the parameters.
  3396. //
  3397. if (pSourceAddress == NULL)
  3398. {
  3399. DPFX(DPFPREP, 0, "Invalid source address specified!");
  3400. hr = DPNHERR_INVALIDPOINTER;
  3401. goto Failure;
  3402. }
  3403. if (pQueryAddress == NULL)
  3404. {
  3405. DPFX(DPFPREP, 0, "Invalid query address specified!");
  3406. hr = DPNHERR_INVALIDPOINTER;
  3407. goto Failure;
  3408. }
  3409. if (pResponseAddress == NULL)
  3410. {
  3411. DPFX(DPFPREP, 0, "Invalid response address specified!");
  3412. hr = DPNHERR_INVALIDPOINTER;
  3413. goto Failure;
  3414. }
  3415. if (iAddressesSize < sizeof(SOCKADDR_IN))
  3416. {
  3417. DPFX(DPFPREP, 0, "The address buffers must be at least %i bytes!",
  3418. sizeof(SOCKADDR_IN));
  3419. hr = DPNHERR_INVALIDPARAM;
  3420. goto Failure;
  3421. }
  3422. if (IsBadReadPtr(pSourceAddress, sizeof(SOCKADDR_IN)))
  3423. {
  3424. DPFX(DPFPREP, 0, "Invalid source address buffer used!");
  3425. hr = DPNHERR_INVALIDPOINTER;
  3426. goto Failure;
  3427. }
  3428. if (IsBadReadPtr(pQueryAddress, sizeof(SOCKADDR_IN)))
  3429. {
  3430. DPFX(DPFPREP, 0, "Invalid query address buffer used!");
  3431. hr = DPNHERR_INVALIDPOINTER;
  3432. goto Failure;
  3433. }
  3434. if (IsBadWritePtr(pResponseAddress, sizeof(SOCKADDR_IN)))
  3435. {
  3436. DPFX(DPFPREP, 0, "Invalid response address buffer used!");
  3437. hr = DPNHERR_INVALIDPOINTER;
  3438. goto Failure;
  3439. }
  3440. if ((((SOCKADDR_IN*) pSourceAddress)->sin_family != AF_INET) ||
  3441. (((SOCKADDR_IN*) pQueryAddress)->sin_family != AF_INET))
  3442. {
  3443. DPFX(DPFPREP, 0, "Source or query address is not AF_INET, only IPv4 addresses are supported!");
  3444. hr = DPNHERR_INVALIDPARAM;
  3445. goto Failure;
  3446. }
  3447. if (((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_addr == INADDR_BROADCAST)
  3448. {
  3449. DPFX(DPFPREP, 0, "Source address cannot be broadcast address!");
  3450. hr = DPNHERR_INVALIDPARAM;
  3451. goto Failure;
  3452. }
  3453. if ((((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_addr == INADDR_ANY) ||
  3454. (((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_addr == INADDR_BROADCAST))
  3455. {
  3456. //
  3457. // Don't use inet_ntoa because we may not be initialized yet.
  3458. //
  3459. DPFX(DPFPREP, 0, "Query address (%u.%u.%u.%u) is invalid, cannot be zero or broadcast!",
  3460. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b1,
  3461. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b2,
  3462. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b3,
  3463. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b4);
  3464. hr = DPNHERR_INVALIDPARAM;
  3465. goto Failure;
  3466. }
  3467. if (dwFlags & ~(DPNHQUERYADDRESS_TCP | DPNHQUERYADDRESS_CACHEFOUND | DPNHQUERYADDRESS_CACHENOTFOUND | DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED))
  3468. {
  3469. DPFX(DPFPREP, 0, "Invalid flags specified!");
  3470. hr = DPNHERR_INVALIDFLAGS;
  3471. goto Failure;
  3472. }
  3473. if ((((SOCKADDR_IN*) pQueryAddress)->sin_port == 0) &&
  3474. (! (dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED)))
  3475. {
  3476. DPFX(DPFPREP, 0, "Query address port cannot be zero unless CHECKFORPRIVATEBUTUNMAPPED is specified!");
  3477. hr = DPNHERR_INVALIDPARAM;
  3478. goto Failure;
  3479. }
  3480. //
  3481. // Attempt to take the lock, but be prepared for the re-entrancy error.
  3482. //
  3483. hr = this->TakeLock();
  3484. if (hr != DPNH_OK)
  3485. {
  3486. DPFX(DPFPREP, 0, "Could not lock object!");
  3487. goto Failure;
  3488. }
  3489. fHaveLock = TRUE;
  3490. //
  3491. // Make sure object is in right state.
  3492. //
  3493. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  3494. {
  3495. DPFX(DPFPREP, 0, "Object not initialized!");
  3496. hr = DPNHERR_NOTINITIALIZED;
  3497. goto Failure;
  3498. }
  3499. if (this->m_dwLastUpdateServerStatusTime == 0)
  3500. {
  3501. DPFX(DPFPREP, 0, "GetCaps has not been called with UPDATESERVERSTATUS flag yet!");
  3502. hr = DPNHERR_UPDATESERVERSTATUS;
  3503. goto Failure;
  3504. }
  3505. pDevice = this->FindMatchingDevice((SOCKADDR_IN*) pSourceAddress, TRUE);
  3506. if (pDevice == NULL)
  3507. {
  3508. DPFX(DPFPREP, 1, "Couldn't determine owning device for source %u.%u.%u.%u, returning SERVERNOTAVAILABLE for query %u.%u.%u.%u:%u.",
  3509. ((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_un_b.s_b1,
  3510. ((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_un_b.s_b2,
  3511. ((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_un_b.s_b3,
  3512. ((SOCKADDR_IN*) pSourceAddress)->sin_addr.S_un.S_un_b.s_b4,
  3513. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b1,
  3514. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b2,
  3515. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b3,
  3516. ((SOCKADDR_IN*) pQueryAddress)->sin_addr.S_un.S_un_b.s_b4,
  3517. NTOHS(((SOCKADDR_IN*) pQueryAddress)->sin_port));
  3518. hr = DPNHERR_SERVERNOTAVAILABLE;
  3519. goto Exit;
  3520. }
  3521. //
  3522. // Assume no servers are available. This will get overridden as
  3523. // appropriate.
  3524. //
  3525. hr = DPNHERR_SERVERNOTAVAILABLE;
  3526. //
  3527. // Start by querying the address passed in.
  3528. //
  3529. psaddrinNextServerQueryAddress = (SOCKADDR_IN*) pQueryAddress;
  3530. //
  3531. // If the port is zero, then we can't actually lookup a mapping. Just do
  3532. // the address locality check.
  3533. //
  3534. if (psaddrinNextServerQueryAddress->sin_port == 0)
  3535. {
  3536. //
  3537. // We should have caught this in parameter validation above, but I'm
  3538. // being paranoid.
  3539. //
  3540. DNASSERT(dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED);
  3541. //
  3542. // We don't cache these results, since there's no server (and thus, no
  3543. // network traffic) associated with it. No need to look anything up.
  3544. //
  3545. //
  3546. // If there aren't any Internet gateways, then no need to check.
  3547. //
  3548. #ifdef DPNBUILD_NOHNETFWAPI
  3549. if (pDevice->GetUPnPDevice() == NULL)
  3550. #else // ! DPNBUILD_NOHNETFWAPI
  3551. if ((pDevice->GetUPnPDevice() == NULL) &&
  3552. (! pDevice->IsHNetFirewalled()))
  3553. #endif // ! DPNBUILD_NOHNETFWAPI
  3554. {
  3555. DPFX(DPFPREP, 5, "No port queried and there aren't any gateways, returning SERVERNOTAVAILABLE.");
  3556. hr = DPNHERR_SERVERNOTAVAILABLE;
  3557. }
  3558. else
  3559. {
  3560. //
  3561. // There is an Internet gateway of some kind, our locality check
  3562. // would be meaningful.
  3563. //
  3564. if (this->IsAddressLocal(pDevice, psaddrinNextServerQueryAddress))
  3565. {
  3566. DPFX(DPFPREP, 5, "No port queried, but address appears to be local, returning NOMAPPINGBUTPRIVATE.");
  3567. hr = DPNHERR_NOMAPPINGBUTPRIVATE;
  3568. }
  3569. else
  3570. {
  3571. DPFX(DPFPREP, 5, "No port queried and address does not appear to be local, returning NOMAPPING.");
  3572. hr = DPNHERR_NOMAPPING;
  3573. }
  3574. }
  3575. //
  3576. // We've done all we can do.
  3577. //
  3578. goto Exit;
  3579. }
  3580. //
  3581. // Query the UPnP gateway, if there is one.
  3582. //
  3583. pUPnPDevice = pDevice->GetUPnPDevice();
  3584. if (pUPnPDevice != NULL)
  3585. {
  3586. //
  3587. // GetUPnPDevice did not add a reference to pUPnPDevice for us.
  3588. //
  3589. pUPnPDevice->AddRef();
  3590. DNASSERT(pUPnPDevice->IsReady());
  3591. //
  3592. // Actually query the device.
  3593. //
  3594. hr = this->InternalUPnPQueryAddress(pUPnPDevice,
  3595. psaddrinNextServerQueryAddress,
  3596. (SOCKADDR_IN*) pResponseAddress,
  3597. dwFlags);
  3598. switch (hr)
  3599. {
  3600. case DPNH_OK:
  3601. {
  3602. //
  3603. // There was a mapping.
  3604. //
  3605. //psaddrinNextServerQueryAddress = (SOCKADDR_IN*) pResponseAddress;
  3606. break;
  3607. }
  3608. case DPNHERR_NOMAPPING:
  3609. {
  3610. //
  3611. // There's no mapping.
  3612. //
  3613. break;
  3614. }
  3615. case DPNHERR_NOMAPPINGBUTPRIVATE:
  3616. {
  3617. //
  3618. // There's no mapping although the address is private.
  3619. //
  3620. break;
  3621. }
  3622. case DPNHERR_SERVERNOTRESPONDING:
  3623. {
  3624. //
  3625. // The device stopped responding, so we should get rid of it.
  3626. //
  3627. DPFX(DPFPREP, 1, "UPnP device stopped responding while querying port mapping, removing it.");
  3628. this->ClearDevicesUPnPDevice(pDevice);
  3629. //
  3630. // We also set the return code back to SERVERNOTAVAILABLE.
  3631. //
  3632. hr = DPNHERR_SERVERNOTAVAILABLE;
  3633. //
  3634. // Continue through to querying the HomeNet firewall.
  3635. //
  3636. break;
  3637. }
  3638. default:
  3639. {
  3640. DPFX(DPFPREP, 0, "Querying UPnP device for port mapping failed!");
  3641. goto Failure;
  3642. break;
  3643. }
  3644. }
  3645. pUPnPDevice->DecRef();
  3646. pUPnPDevice = NULL;
  3647. }
  3648. else
  3649. {
  3650. //
  3651. // No UPnP device.
  3652. //
  3653. }
  3654. #ifndef DPNBUILD_NOHNETFWAPI
  3655. //
  3656. // If there's a HomeNet firewall and we didn't already get a UPnP result,
  3657. // take the easy way out and return NOMAPPING instead of going through the
  3658. // trouble of looking up the mapping and returning success only if it maps
  3659. // to a local address.
  3660. //
  3661. // Note: we may want to look it up, but right now I'm not seeing any
  3662. // benefit to implementing that code.
  3663. //
  3664. if ((pDevice->IsHNetFirewalled()) && (hr == DPNHERR_SERVERNOTAVAILABLE))
  3665. {
  3666. DPFX(DPFPREP, 7, "Device is HomeNet firewalled, and no UPnP result obtained, returning NOMAPPING.");
  3667. hr = DPNHERR_NOMAPPING;
  3668. }
  3669. #endif // ! DPNBUILD_NOHNETFWAPI
  3670. //
  3671. // If we got here with hr still set to SERVERNOTAVAILABLE, that means
  3672. // there weren't any servers. The error code is appropriate, leave it
  3673. // alone.
  3674. //
  3675. #ifdef DBG
  3676. if (hr == DPNHERR_SERVERNOTAVAILABLE)
  3677. {
  3678. DPFX(DPFPREP, 1, "No Internet gateways, unable to query port mapping.");
  3679. }
  3680. #endif // DBG
  3681. Exit:
  3682. if (fHaveLock)
  3683. {
  3684. this->DropLock();
  3685. fHaveLock = FALSE;
  3686. }
  3687. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  3688. return hr;
  3689. Failure:
  3690. if (pUPnPDevice != NULL)
  3691. {
  3692. pUPnPDevice->DecRef();
  3693. }
  3694. goto Exit;
  3695. } // CNATHelpUPnP::QueryAddress
  3696. #undef DPF_MODNAME
  3697. #define DPF_MODNAME "CNATHelpUPnP::SetAlertEvent"
  3698. //=============================================================================
  3699. // CNATHelpUPnP::SetAlertEvent
  3700. //-----------------------------------------------------------------------------
  3701. //
  3702. // Description: This function allows the user to specify an event that will
  3703. // be set when some maintenance needs to be performed. The user
  3704. // should call GetCaps using the DPNHGETCAPS_UPDATESERVERSTATUS
  3705. // flag when the event is signalled.
  3706. //
  3707. // This function is not available on Windows 95 without WinSock
  3708. // 2, may only be called once, and cannot be used after
  3709. // SetAlertIOCompletionPort is called.
  3710. //
  3711. // Note that the event is used in addition to the regular
  3712. // polling of GetCaps, it simply allows the polling to be less
  3713. // frequent.
  3714. //
  3715. // Arguments:
  3716. // HANDLE hEvent - Handle to event to signal when GetCaps is to be called.
  3717. // DWORD dwFlags - Unused, must be zero.
  3718. //
  3719. // Returns: HRESULT
  3720. // DPNH_OK - The event was successfully registered.
  3721. // DPNHERR_GENERIC - An error occurred that prevented registering the
  3722. // event.
  3723. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  3724. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  3725. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  3726. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  3727. // DPNHERR_OUTOFMEMORY - There is not enough memory.
  3728. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  3729. // thread.
  3730. //=============================================================================
  3731. STDMETHODIMP CNATHelpUPnP::SetAlertEvent(const HANDLE hEvent,
  3732. const DWORD dwFlags)
  3733. {
  3734. #ifdef DPNBUILD_NOWINSOCK2
  3735. DPFX(DPFPREP, 0, "Cannot set alert event (0x%p)!", hEvent);
  3736. return E_NOTIMPL;
  3737. #else // ! DPNBUILD_NOWINSOCK2
  3738. HRESULT hr;
  3739. BOOL fHaveLock = FALSE;
  3740. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx)", this, hEvent, dwFlags);
  3741. //
  3742. // Validate the object.
  3743. //
  3744. if (! this->IsValidObject())
  3745. {
  3746. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  3747. hr = DPNHERR_INVALIDOBJECT;
  3748. goto Failure;
  3749. }
  3750. //
  3751. // Validate the parameters.
  3752. //
  3753. if (hEvent == NULL)
  3754. {
  3755. DPFX(DPFPREP, 0, "Invalid event handle specified!");
  3756. hr = DPNHERR_INVALIDPARAM;
  3757. goto Failure;
  3758. }
  3759. if (dwFlags != 0)
  3760. {
  3761. DPFX(DPFPREP, 0, "Invalid flags specified!");
  3762. hr = DPNHERR_INVALIDFLAGS;
  3763. goto Failure;
  3764. }
  3765. //
  3766. // Attempt to take the lock, but be prepared for the re-entrancy error.
  3767. //
  3768. hr = this->TakeLock();
  3769. if (hr != DPNH_OK)
  3770. {
  3771. DPFX(DPFPREP, 0, "Could not lock object!");
  3772. goto Failure;
  3773. }
  3774. fHaveLock = TRUE;
  3775. //
  3776. // Make sure object is in right state.
  3777. //
  3778. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED))
  3779. {
  3780. DPFX(DPFPREP, 0, "Object not initialized!");
  3781. hr = DPNHERR_NOTINITIALIZED;
  3782. goto Failure;
  3783. }
  3784. if (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1)
  3785. {
  3786. DPFX(DPFPREP, 0, "Cannot use alert mechanism on WinSock 1!");
  3787. hr = DPNHERR_GENERIC;
  3788. goto Failure;
  3789. }
  3790. if ((this->m_hAlertEvent != NULL) || (this->m_hAlertIOCompletionPort != NULL))
  3791. {
  3792. DPFX(DPFPREP, 0, "An alert event or I/O completion port has already been set!");
  3793. hr = DPNHERR_GENERIC;
  3794. goto Failure;
  3795. }
  3796. //
  3797. // Now save the event handle.
  3798. //
  3799. if (! DuplicateHandle(GetCurrentProcess(),
  3800. hEvent,
  3801. GetCurrentProcess(),
  3802. &this->m_hAlertEvent,
  3803. 0,
  3804. FALSE,
  3805. DUPLICATE_SAME_ACCESS))
  3806. {
  3807. #ifdef DBG
  3808. DWORD dwError;
  3809. dwError = GetLastError();
  3810. DPFX(DPFPREP, 0, "Couldn't duplicate event (error = %u)!", dwError);
  3811. #endif // DBG
  3812. DNASSERT(this->m_hAlertEvent == NULL);
  3813. hr = DPNHERR_INVALIDPARAM;
  3814. goto Failure;
  3815. }
  3816. //
  3817. // Create overlapped structure. Don't allocate it through DNMalloc,
  3818. // because we may have to leak it on purpose. We don't want those memory
  3819. // allocation asserts firing in that case.
  3820. //
  3821. this->m_polAddressListChange = (WSAOVERLAPPED*) HeapAlloc(GetProcessHeap(),
  3822. HEAP_ZERO_MEMORY,
  3823. sizeof(WSAOVERLAPPED));
  3824. if (this->m_polAddressListChange == NULL)
  3825. {
  3826. //
  3827. // Close the alert handle we set.
  3828. //
  3829. CloseHandle(this->m_hAlertEvent);
  3830. this->m_hAlertEvent = NULL;
  3831. hr = DPNHERR_OUTOFMEMORY;
  3832. goto Failure;
  3833. }
  3834. //
  3835. // Save the event in the address list change overlapped structure.
  3836. //
  3837. this->m_polAddressListChange->hEvent = this->m_hAlertEvent;
  3838. //
  3839. // Start getting notified of local address changes.
  3840. //
  3841. hr = this->RequestLocalAddressListChangeNotification();
  3842. if (hr != DPNH_OK)
  3843. {
  3844. DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
  3845. //
  3846. // Free the memory we allocated.
  3847. //
  3848. HeapFree(GetProcessHeap(), 0, this->m_polAddressListChange);
  3849. this->m_polAddressListChange = NULL;
  3850. //
  3851. // Close the alert handle we set.
  3852. //
  3853. CloseHandle(this->m_hAlertEvent);
  3854. this->m_hAlertEvent = NULL;
  3855. goto Failure;
  3856. }
  3857. Exit:
  3858. if (fHaveLock)
  3859. {
  3860. this->DropLock();
  3861. fHaveLock = FALSE;
  3862. }
  3863. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  3864. return hr;
  3865. Failure:
  3866. goto Exit;
  3867. #endif // ! DPNBUILD_NOWINSOCK2
  3868. } // CNATHelpUPnP::SetAlertEvent
  3869. #undef DPF_MODNAME
  3870. #define DPF_MODNAME "CNATHelpUPnP::SetAlertIOCompletionPort"
  3871. //=============================================================================
  3872. // CNATHelpUPnP::SetAlertIOCompletionPort
  3873. //-----------------------------------------------------------------------------
  3874. //
  3875. // Description: This function allows the user to specify an I/O completion
  3876. // port that will receive notification when some maintenance needs
  3877. // to be performed. The user should call GetCaps using the
  3878. // DPNHGETCAPS_UPDATESERVERSTATUS flag when the packet with the
  3879. // given completion key is dequeued.
  3880. //
  3881. // This function is only available on Windows NT, may only be
  3882. // called once, and cannot be used after SetAlertEvent is called.
  3883. //
  3884. // Note that the completion port is used in addition to the
  3885. // regular polling of GetCaps, it simply allows the polling to be
  3886. // less frequent.
  3887. //
  3888. // Arguments:
  3889. // HANDLE hIOCompletionPort - Handle to I/O completion port which will
  3890. // be used to signal when GetCaps is to be
  3891. // called.
  3892. // DWORD dwCompletionKey - Key to use when indicating I/O
  3893. // completion.
  3894. // DWORD dwNumConcurrentThreads - Number of concurrent threads allowed to
  3895. // process, or zero for default.
  3896. // DWORD dwFlags - Unused, must be zero.
  3897. //
  3898. // Returns: HRESULT
  3899. // DPNH_OK - The I/O completion port was successfully
  3900. // registered.
  3901. // DPNHERR_GENERIC - An error occurred that prevented registering the
  3902. // I/O completion port.
  3903. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  3904. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  3905. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  3906. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  3907. // DPNHERR_OUTOFMEMORY - There is not enough memory.
  3908. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  3909. // thread.
  3910. //=============================================================================
  3911. STDMETHODIMP CNATHelpUPnP::SetAlertIOCompletionPort(const HANDLE hIOCompletionPort,
  3912. const DWORD dwCompletionKey,
  3913. const DWORD dwNumConcurrentThreads,
  3914. const DWORD dwFlags)
  3915. {
  3916. #ifdef DPNBUILD_NOWINSOCK2
  3917. DPFX(DPFPREP, 0, "Cannot set alert I/O completion port (0x%p, %u, %u)!",
  3918. hIOCompletionPort, dwCompletionKey, dwNumConcurrentThreads);
  3919. return E_NOTIMPL;
  3920. #else // ! DPNBUILD_NOWINSOCK2
  3921. HRESULT hr;
  3922. BOOL fHaveLock = FALSE;
  3923. HANDLE hIOCompletionPortResult;
  3924. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, 0x%lx, %u, 0x%lx)",
  3925. this, hIOCompletionPort, dwCompletionKey, dwNumConcurrentThreads, dwFlags);
  3926. //
  3927. // Validate the object.
  3928. //
  3929. if (! this->IsValidObject())
  3930. {
  3931. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  3932. hr = DPNHERR_INVALIDOBJECT;
  3933. goto Failure;
  3934. }
  3935. //
  3936. // Validate the parameters.
  3937. //
  3938. if (hIOCompletionPort == NULL)
  3939. {
  3940. DPFX(DPFPREP, 0, "Invalid I/O completion port handle specified!");
  3941. hr = DPNHERR_INVALIDPARAM;
  3942. goto Failure;
  3943. }
  3944. if (dwFlags != 0)
  3945. {
  3946. DPFX(DPFPREP, 0, "Invalid flags specified!");
  3947. hr = DPNHERR_INVALIDFLAGS;
  3948. goto Failure;
  3949. }
  3950. //
  3951. // Attempt to take the lock, but be prepared for the re-entrancy error.
  3952. //
  3953. hr = this->TakeLock();
  3954. if (hr != DPNH_OK)
  3955. {
  3956. DPFX(DPFPREP, 0, "Could not lock object!");
  3957. goto Failure;
  3958. }
  3959. fHaveLock = TRUE;
  3960. //
  3961. // Make sure object is in right state.
  3962. //
  3963. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED))
  3964. {
  3965. DPFX(DPFPREP, 0, "Object not initialized!");
  3966. hr = DPNHERR_NOTINITIALIZED;
  3967. goto Failure;
  3968. }
  3969. if (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1)
  3970. {
  3971. DPFX(DPFPREP, 0, "Cannot use alert mechanism on WinSock 1!");
  3972. hr = DPNHERR_GENERIC;
  3973. goto Failure;
  3974. }
  3975. if ((this->m_hAlertEvent != NULL) || (this->m_hAlertIOCompletionPort != NULL))
  3976. {
  3977. DPFX(DPFPREP, 0, "An alert event or I/O completion port has already been set!");
  3978. hr = DPNHERR_GENERIC;
  3979. goto Failure;
  3980. }
  3981. //
  3982. // Now save the I/O completion port handle.
  3983. //
  3984. if (! DuplicateHandle(GetCurrentProcess(),
  3985. hIOCompletionPort,
  3986. GetCurrentProcess(),
  3987. &this->m_hAlertIOCompletionPort,
  3988. 0,
  3989. FALSE,
  3990. DUPLICATE_SAME_ACCESS))
  3991. {
  3992. #ifdef DBG
  3993. DWORD dwError;
  3994. dwError = GetLastError();
  3995. DPFX(DPFPREP, 0, "Couldn't duplicate I/O completion port (error = %u)!", dwError);
  3996. #endif // DBG
  3997. DNASSERT(this->m_hAlertIOCompletionPort == NULL);
  3998. hr = DPNHERR_INVALIDPARAM;
  3999. goto Failure;
  4000. }
  4001. this->m_dwAlertCompletionKey = dwCompletionKey;
  4002. //
  4003. // Associate our Ioctl socket with this IO completion port.
  4004. //
  4005. DNASSERT(this->m_sIoctls != INVALID_SOCKET);
  4006. hIOCompletionPortResult = CreateIoCompletionPort((HANDLE) this->m_sIoctls,
  4007. this->m_hAlertIOCompletionPort,
  4008. dwCompletionKey,
  4009. dwNumConcurrentThreads);
  4010. if (hIOCompletionPortResult == NULL)
  4011. {
  4012. #ifdef DBG
  4013. DWORD dwError;
  4014. dwError = GetLastError();
  4015. DPFX(DPFPREP, 0, "Couldn't associate I/O completion port with Ioctl socket (error = %u)!", dwError);
  4016. #endif // DBG
  4017. hr = DPNHERR_GENERIC;
  4018. goto Failure;
  4019. }
  4020. //
  4021. // We should have just gotten the same I/O completion port back.
  4022. //
  4023. DNASSERT(hIOCompletionPortResult == this->m_hAlertIOCompletionPort);
  4024. //
  4025. // Create overlapped structure. Don't allocate it through DNMalloc,
  4026. // because we may have to leak it on purpose. We don't want those memory
  4027. // allocation asserts firing in that case.
  4028. //
  4029. this->m_polAddressListChange = (WSAOVERLAPPED*) HeapAlloc(GetProcessHeap(),
  4030. HEAP_ZERO_MEMORY,
  4031. sizeof(WSAOVERLAPPED));
  4032. if (this->m_polAddressListChange == NULL)
  4033. {
  4034. //
  4035. // Close the alert IOCP we set.
  4036. //
  4037. CloseHandle(this->m_hAlertIOCompletionPort);
  4038. this->m_hAlertIOCompletionPort = NULL;
  4039. hr = DPNHERR_OUTOFMEMORY;
  4040. goto Failure;
  4041. }
  4042. //
  4043. // Start getting notified of local address changes.
  4044. //
  4045. hr = this->RequestLocalAddressListChangeNotification();
  4046. if (hr != DPNH_OK)
  4047. {
  4048. DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
  4049. //
  4050. // Free the memory we allocated.
  4051. //
  4052. HeapFree(GetProcessHeap(), 0, this->m_polAddressListChange);
  4053. this->m_polAddressListChange = NULL;
  4054. //
  4055. // Close the alert IOCP we set.
  4056. //
  4057. CloseHandle(this->m_hAlertIOCompletionPort);
  4058. this->m_hAlertIOCompletionPort = NULL;
  4059. goto Failure;
  4060. }
  4061. Exit:
  4062. if (fHaveLock)
  4063. {
  4064. this->DropLock();
  4065. fHaveLock = FALSE;
  4066. }
  4067. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  4068. return hr;
  4069. Failure:
  4070. goto Exit;
  4071. #endif // ! DPNBUILD_NOWINSOCK2
  4072. } // CNATHelpUPnP::SetAlertIOCompletionPort
  4073. #undef DPF_MODNAME
  4074. #define DPF_MODNAME "CNATHelpUPnP::ExtendRegisteredPortsLease"
  4075. //=============================================================================
  4076. // CNATHelpUPnP::ExtendRegisteredPortsLease
  4077. //-----------------------------------------------------------------------------
  4078. //
  4079. // Description: Manually extends the lease of the given registered port
  4080. // mapping by the requested time. The periodic calling of GetCaps
  4081. // can take care of this for the user, this function is only
  4082. // necessary to change the lease extension time or for finer
  4083. // control of individual mappings.
  4084. //
  4085. // The user should specify a requested lease extension time
  4086. // that the server will attempt to honor. It will be added to any
  4087. // time remaining in the existing lease, and the new total can be
  4088. // retrieved by calling GetRegisteredAddresses.
  4089. //
  4090. // Arguments:
  4091. // DPNHHANDLE hRegisteredPorts - Handle for a specific binding returned by
  4092. // RegisterPorts.
  4093. // DWORD dwLeaseTime - Requested time, in milliseconds, to
  4094. // extend the lease. If 0, the previous
  4095. // requested lease time is used.
  4096. // DWORD dwFlags - Unused, must be zero.
  4097. //
  4098. // Returns: HRESULT
  4099. // DPNH_OK - The lease was successfully extended.
  4100. // DPNHERR_GENERIC - An error occurred that prevented the extending
  4101. // the lease.
  4102. // DPNHERR_INVALIDFLAGS - Invalid flags were specified.
  4103. // DPNHERR_INVALIDOBJECT - The interface object is invalid.
  4104. // DPNHERR_INVALIDPARAM - An invalid parameter was specified.
  4105. // DPNHERR_NOTINITIALIZED - Initialize has not been called.
  4106. // DPNHERR_OUTOFMEMORY - There is not enough memory to extend the lease.
  4107. // DPNHERR_REENTRANT - The interface has been re-entered on the same
  4108. // thread.
  4109. //=============================================================================
  4110. STDMETHODIMP CNATHelpUPnP::ExtendRegisteredPortsLease(const DPNHHANDLE hRegisteredPorts,
  4111. const DWORD dwLeaseTime,
  4112. const DWORD dwFlags)
  4113. {
  4114. HRESULT hr;
  4115. CRegisteredPort * pRegisteredPort;
  4116. CDevice * pDevice;
  4117. BOOL fHaveLock = FALSE;
  4118. DPFX(DPFPREP, 2, "(0x%p) Parameters: (0x%p, %u, 0x%lx)",
  4119. this, hRegisteredPorts, dwLeaseTime, dwFlags);
  4120. //
  4121. // Validate the object.
  4122. //
  4123. if (! this->IsValidObject())
  4124. {
  4125. DPFX(DPFPREP, 0, "Invalid DirectPlay NAT Help object!");
  4126. hr = DPNHERR_INVALIDOBJECT;
  4127. goto Failure;
  4128. }
  4129. //
  4130. // Validate the parameters.
  4131. //
  4132. pRegisteredPort = (CRegisteredPort*) hRegisteredPorts;
  4133. if (! pRegisteredPort->IsValidObject())
  4134. {
  4135. DPFX(DPFPREP, 0, "Invalid registered port mapping handle specified!");
  4136. hr = DPNHERR_INVALIDPARAM;
  4137. goto Failure;
  4138. }
  4139. if (dwFlags != 0)
  4140. {
  4141. DPFX(DPFPREP, 0, "Invalid flags specified!");
  4142. hr = DPNHERR_INVALIDFLAGS;
  4143. goto Failure;
  4144. }
  4145. //
  4146. // Attempt to take the lock, but be prepared for the re-entrancy error.
  4147. //
  4148. hr = this->TakeLock();
  4149. if (hr != DPNH_OK)
  4150. {
  4151. DPFX(DPFPREP, 0, "Could not lock object!");
  4152. goto Failure;
  4153. }
  4154. fHaveLock = TRUE;
  4155. //
  4156. // Make sure object is in right state.
  4157. //
  4158. if (! (this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED) )
  4159. {
  4160. DPFX(DPFPREP, 0, "Object not initialized!");
  4161. hr = DPNHERR_NOTINITIALIZED;
  4162. goto Failure;
  4163. }
  4164. //
  4165. // If they wanted to change the lease time, update it.
  4166. //
  4167. if (dwLeaseTime != 0)
  4168. {
  4169. pRegisteredPort->UpdateRequestedLeaseTime(dwLeaseTime);
  4170. }
  4171. pDevice = pRegisteredPort->GetOwningDevice();
  4172. //
  4173. // If the port is registered with the UPnP device, extend that lease.
  4174. //
  4175. if (pRegisteredPort->HasUPnPPublicAddresses())
  4176. {
  4177. DNASSERT(pDevice != NULL);
  4178. hr = this->ExtendUPnPLease(pRegisteredPort);
  4179. if (hr != DPNH_OK)
  4180. {
  4181. DPFX(DPFPREP, 0, "Couldn't extend port mapping lease on UPnP device (0x%lx)! Ignoring.", hr);
  4182. //
  4183. // We'll treat this as non-fatal, but we have to dump the
  4184. // server. This may have already been done, but doing it
  4185. // twice shouldn't be harmful.
  4186. //
  4187. this->ClearDevicesUPnPDevice(pDevice);
  4188. hr = DPNH_OK;
  4189. }
  4190. }
  4191. else
  4192. {
  4193. DPFX(DPFPREP, 2, "Port mapping not registered with UPnP gateway device.");
  4194. }
  4195. //
  4196. // Firewall mappings never have lease times to extend.
  4197. //
  4198. this->DropLock();
  4199. fHaveLock = FALSE;
  4200. Exit:
  4201. DPFX(DPFPREP, 2, "(0x%p) Returning: [0x%lx]", this, hr);
  4202. return hr;
  4203. Failure:
  4204. if (fHaveLock)
  4205. {
  4206. this->DropLock();
  4207. fHaveLock = FALSE;
  4208. }
  4209. goto Exit;
  4210. } // CNATHelpUPnP::ExtendRegisteredPortsLease
  4211. #undef DPF_MODNAME
  4212. #define DPF_MODNAME "CNATHelpUPnP::InitializeObject"
  4213. //=============================================================================
  4214. // CNATHelpUPnP::InitializeObject
  4215. //-----------------------------------------------------------------------------
  4216. //
  4217. // Description: Sets up the object for use like the constructor, but may
  4218. // fail with OUTOFMEMORY. Should only be called by class factory
  4219. // creation routine.
  4220. //
  4221. // Arguments: None.
  4222. //
  4223. // Returns: HRESULT
  4224. // S_OK - Initialization was successful.
  4225. // E_OUTOFMEMORY - There is not enough memory to initialize.
  4226. //=============================================================================
  4227. HRESULT CNATHelpUPnP::InitializeObject(void)
  4228. {
  4229. HRESULT hr;
  4230. BOOL fInittedCriticalSection = FALSE;
  4231. DPFX(DPFPREP, 5, "(0x%p) Enter", this);
  4232. DNASSERT(this->IsValidObject());
  4233. //
  4234. // Create the lock.
  4235. //
  4236. if (! DNInitializeCriticalSection(&this->m_csLock))
  4237. {
  4238. hr = E_OUTOFMEMORY;
  4239. goto Failure;
  4240. }
  4241. fInittedCriticalSection = TRUE;
  4242. //
  4243. // Don't allow critical section reentry.
  4244. //
  4245. DebugSetCriticalSectionRecursionCount(&this->m_csLock, 0);
  4246. this->m_hLongLockSemaphore = DNCreateSemaphore(NULL,
  4247. 0,
  4248. MAX_LONG_LOCK_WAITING_THREADS,
  4249. NULL);
  4250. if (this->m_hLongLockSemaphore == NULL)
  4251. {
  4252. hr = DPNHERR_GENERIC;
  4253. goto Failure;
  4254. }
  4255. hr = S_OK;
  4256. Exit:
  4257. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  4258. return hr;
  4259. Failure:
  4260. if (fInittedCriticalSection)
  4261. {
  4262. DNDeleteCriticalSection(&this->m_csLock);
  4263. fInittedCriticalSection = FALSE;
  4264. }
  4265. goto Exit;
  4266. } // CNATHelpUPnP::InitializeObject
  4267. #undef DPF_MODNAME
  4268. #define DPF_MODNAME "CNATHelpUPnP::UninitializeObject"
  4269. //=============================================================================
  4270. // CNATHelpUPnP::UninitializeObject
  4271. //-----------------------------------------------------------------------------
  4272. //
  4273. // Description: Cleans up the object like the destructor, mostly to balance
  4274. // InitializeObject.
  4275. //
  4276. // Arguments: None.
  4277. //
  4278. // Returns: None.
  4279. //=============================================================================
  4280. void CNATHelpUPnP::UninitializeObject(void)
  4281. {
  4282. DPFX(DPFPREP, 5, "(0x%p) Enter", this);
  4283. DNASSERT(this->IsValidObject());
  4284. DNCloseHandle(this->m_hLongLockSemaphore);
  4285. this->m_hLongLockSemaphore = NULL;
  4286. DNDeleteCriticalSection(&this->m_csLock);
  4287. DPFX(DPFPREP, 5, "(0x%p) Leave", this);
  4288. } // CNATHelpUPnP::UninitializeObject
  4289. #undef DPF_MODNAME
  4290. #define DPF_MODNAME "CNATHelpUPnP::TakeLock"
  4291. //=============================================================================
  4292. // CNATHelpUPnP::TakeLock
  4293. //-----------------------------------------------------------------------------
  4294. //
  4295. // Description: Takes the main object lock. If some other thread is already
  4296. // holding the long lock, we wait for that first.
  4297. //
  4298. // Arguments: None.
  4299. //
  4300. // Returns: DPNH_OK if lock was taken successfully, DPNHERR_REENTRANT if lock
  4301. // was re-entered.
  4302. //=============================================================================
  4303. HRESULT CNATHelpUPnP::TakeLock(void)
  4304. {
  4305. HRESULT hr = DPNH_OK;
  4306. #ifdef DBG
  4307. DWORD dwStartTime;
  4308. dwStartTime = GETTIMESTAMP();
  4309. #endif // DBG
  4310. DNEnterCriticalSection(&this->m_csLock);
  4311. //
  4312. // If this same thread is already holding the lock, then bail.
  4313. //
  4314. if (this->m_dwLockThreadID == GetCurrentThreadId())
  4315. {
  4316. DPFX(DPFPREP, 0, "Thread re-entering!");
  4317. goto Failure;
  4318. }
  4319. //
  4320. // If someone is holding the long lock, we need to wait for that. Of
  4321. // course another thread could come in and take the long lock after the
  4322. // first one drops it and before we can take the main one. This algorithm
  4323. // does not attempt to be fair in this case. Theoretically we could wait
  4324. // forever if this continued to occur. That shouldn't happen in the real
  4325. // world.
  4326. // This whole mess of code is a huge... uh... workaround for stress hits
  4327. // involving critical section timeouts.
  4328. //
  4329. while (this->m_dwFlags & NATHELPUPNPOBJ_LONGLOCK)
  4330. {
  4331. DNASSERT(this->m_lNumLongLockWaitingThreads >= 0);
  4332. this->m_lNumLongLockWaitingThreads++;
  4333. //
  4334. // We need to keep looping until we do get the lock.
  4335. //
  4336. DNLeaveCriticalSection(&this->m_csLock);
  4337. DPFX(DPFPREP, 3, "Waiting for long lock to be released.");
  4338. DNWaitForSingleObject(this->m_hLongLockSemaphore, INFINITE);
  4339. DNEnterCriticalSection(&this->m_csLock);
  4340. //
  4341. // If this same thread is already holding the lock, then bail.
  4342. //
  4343. if (this->m_dwLockThreadID == GetCurrentThreadId())
  4344. {
  4345. DPFX(DPFPREP, 0, "Thread re-entering after waiting for long lock!");
  4346. goto Failure;
  4347. }
  4348. }
  4349. #ifdef DBG
  4350. DPFX(DPFPREP, 8, "Took main object lock, elapsed time = %u ms.",
  4351. (GETTIMESTAMP() - dwStartTime));
  4352. #endif // DBG
  4353. //
  4354. // Save this thread's ID so we know who's holding the lock.
  4355. //
  4356. this->m_dwLockThreadID = GetCurrentThreadId();
  4357. Exit:
  4358. return hr;
  4359. Failure:
  4360. //
  4361. // We're reentering. Drop the lock and return the failure.
  4362. //
  4363. DNLeaveCriticalSection(&this->m_csLock);
  4364. hr = DPNHERR_REENTRANT;
  4365. goto Exit;
  4366. } // CNATHelpUPnP::TakeLock
  4367. #undef DPF_MODNAME
  4368. #define DPF_MODNAME "CNATHelpUPnP::DropLock"
  4369. //=============================================================================
  4370. // CNATHelpUPnP::DropLock
  4371. //-----------------------------------------------------------------------------
  4372. //
  4373. // Description: Drops the main object lock.
  4374. //
  4375. // Arguments: None.
  4376. //
  4377. // Returns: None.
  4378. //=============================================================================
  4379. void CNATHelpUPnP::DropLock(void)
  4380. {
  4381. DNASSERT(! (this->m_dwFlags & NATHELPUPNPOBJ_LONGLOCK));
  4382. DNASSERT(this->m_lNumLongLockWaitingThreads == 0);
  4383. DNASSERT(this->m_dwLockThreadID == GetCurrentThreadId());
  4384. this->m_dwLockThreadID = 0;
  4385. DNLeaveCriticalSection(&this->m_csLock);
  4386. DPFX(DPFPREP, 8, "Dropped main object lock.");
  4387. } // CNATHelpUPnP::DropLock
  4388. #undef DPF_MODNAME
  4389. #define DPF_MODNAME "CNATHelpUPnP::SwitchToLongLock"
  4390. //=============================================================================
  4391. // CNATHelpUPnP::SwitchToLongLock
  4392. //-----------------------------------------------------------------------------
  4393. //
  4394. // Description: Switches from holding the main object lock to holding the
  4395. // long lock.
  4396. //
  4397. // Arguments: None.
  4398. //
  4399. // Returns: None.
  4400. //=============================================================================
  4401. void CNATHelpUPnP::SwitchToLongLock(void)
  4402. {
  4403. AssertCriticalSectionIsTakenByThisThread(&this->m_csLock, TRUE);
  4404. DNASSERT(! (this->m_dwFlags & NATHELPUPNPOBJ_LONGLOCK));
  4405. DNASSERT(this->m_lNumLongLockWaitingThreads == 0);
  4406. DPFX(DPFPREP, 8, "Switching to long lock.");
  4407. this->m_dwFlags |= NATHELPUPNPOBJ_LONGLOCK;
  4408. DNLeaveCriticalSection(&this->m_csLock);
  4409. } // CNATHelpUPnP::SwitchToLongLock
  4410. #undef DPF_MODNAME
  4411. #define DPF_MODNAME "CNATHelpUPnP::SwitchFromLongLock"
  4412. //=============================================================================
  4413. // CNATHelpUPnP::SwitchFromLongLock
  4414. //-----------------------------------------------------------------------------
  4415. //
  4416. // Description: Switches from holding the long lock back to holding the main
  4417. // object lock.
  4418. //
  4419. // Arguments: None.
  4420. //
  4421. // Returns: None.
  4422. //=============================================================================
  4423. void CNATHelpUPnP::SwitchFromLongLock(void)
  4424. {
  4425. DNEnterCriticalSection(&this->m_csLock);
  4426. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_LONGLOCK);
  4427. this->m_dwFlags &= ~NATHELPUPNPOBJ_LONGLOCK;
  4428. DPFX(DPFPREP, 8, "Switching from long lock, alerting %i threads.",
  4429. this->m_lNumLongLockWaitingThreads);
  4430. //
  4431. // This is non-optimal in that we release the semaphore but the waiting
  4432. // threads still won't actually be able to do anything since we now hold
  4433. // the main lock.
  4434. //
  4435. DNASSERT(this->m_lNumLongLockWaitingThreads >= 0);
  4436. DNReleaseSemaphore(this->m_hLongLockSemaphore,
  4437. this->m_lNumLongLockWaitingThreads,
  4438. NULL);
  4439. this->m_lNumLongLockWaitingThreads = 0;
  4440. } // CNATHelpUPnP::SwitchFromLongLock
  4441. #undef DPF_MODNAME
  4442. #define DPF_MODNAME "CNATHelpUPnP::LoadWinSockFunctionPointers"
  4443. //=============================================================================
  4444. // CNATHelpUPnP::LoadWinSockFunctionPointers
  4445. //-----------------------------------------------------------------------------
  4446. //
  4447. // Description: Loads pointers to all the functions that we use in WinSock.
  4448. //
  4449. // The object lock is assumed to be held.
  4450. //
  4451. // Arguments: None.
  4452. //
  4453. // Returns: HRESULT
  4454. // DPNH_OK - Loading was successful.
  4455. // DPNHERR_GENERIC - An error occurred.
  4456. //=============================================================================
  4457. HRESULT CNATHelpUPnP::LoadWinSockFunctionPointers(void)
  4458. {
  4459. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4460. #ifdef DBG
  4461. #define PRINTERRORIFDEBUG(name) \
  4462. {\
  4463. dwError = GetLastError();\
  4464. DPFX(DPFPREP, 0, "Couldn't get \"%hs\" function! 0x%lx", name, dwError);\
  4465. }
  4466. #else // ! DBG
  4467. #define PRINTERRORIFDEBUG(name)
  4468. #endif // ! DBG
  4469. #define LOADWINSOCKFUNCTION(var, proctype, name) \
  4470. {\
  4471. var = (##proctype) GetProcAddress(this->m_hWinSockDLL, _TWINCE(name));\
  4472. if (var == NULL)\
  4473. {\
  4474. PRINTERRORIFDEBUG(name);\
  4475. hr = DPNHERR_GENERIC;\
  4476. goto Failure;\
  4477. }\
  4478. }
  4479. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  4480. HRESULT hr = DPNH_OK;
  4481. #ifdef DBG
  4482. DWORD dwError;
  4483. #endif // DBG
  4484. LOADWINSOCKFUNCTION(this->m_pfnWSAStartup, LPFN_WSASTARTUP, "WSAStartup");
  4485. LOADWINSOCKFUNCTION(this->m_pfnWSACleanup, LPFN_WSACLEANUP, "WSACleanup");
  4486. #ifdef WINCE
  4487. this->m_pfnWSAGetLastError = (LPFN_WSAGETLASTERROR) GetLastError;
  4488. #else // ! WINCE
  4489. LOADWINSOCKFUNCTION(this->m_pfnWSAGetLastError, LPFN_WSAGETLASTERROR, "WSAGetLastError");
  4490. #endif // ! WINCE
  4491. LOADWINSOCKFUNCTION(this->m_pfnsocket, LPFN_SOCKET, "socket");
  4492. LOADWINSOCKFUNCTION(this->m_pfnclosesocket, LPFN_CLOSESOCKET, "closesocket");
  4493. LOADWINSOCKFUNCTION(this->m_pfnbind, LPFN_BIND, "bind");
  4494. LOADWINSOCKFUNCTION(this->m_pfnsetsockopt, LPFN_SETSOCKOPT, "setsockopt");
  4495. LOADWINSOCKFUNCTION(this->m_pfngetsockname, LPFN_GETSOCKNAME, "getsockname");
  4496. LOADWINSOCKFUNCTION(this->m_pfnselect, LPFN_SELECT, "select");
  4497. LOADWINSOCKFUNCTION(this->m_pfn__WSAFDIsSet, LPFN___WSAFDISSET, "__WSAFDIsSet");
  4498. LOADWINSOCKFUNCTION(this->m_pfnrecvfrom, LPFN_RECVFROM, "recvfrom");
  4499. LOADWINSOCKFUNCTION(this->m_pfnsendto, LPFN_SENDTO, "sendto");
  4500. LOADWINSOCKFUNCTION(this->m_pfngethostname, LPFN_GETHOSTNAME, "gethostname");
  4501. LOADWINSOCKFUNCTION(this->m_pfngethostbyname, LPFN_GETHOSTBYNAME, "gethostbyname");
  4502. LOADWINSOCKFUNCTION(this->m_pfninet_addr, LPFN_INET_ADDR, "inet_addr");
  4503. #ifndef DPNBUILD_NOWINSOCK2
  4504. if (! (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1))
  4505. {
  4506. LOADWINSOCKFUNCTION(this->m_pfnWSASocketA, LPFN_WSASOCKETA, "WSASocketA");
  4507. LOADWINSOCKFUNCTION(this->m_pfnWSAIoctl, LPFN_WSAIOCTL, "WSAIoctl");
  4508. LOADWINSOCKFUNCTION(this->m_pfnWSAGetOverlappedResult, LPFN_WSAGETOVERLAPPEDRESULT, "WSAGetOverlappedResult");
  4509. }
  4510. #endif // ! DPNBUILD_NOWINSOCK2
  4511. LOADWINSOCKFUNCTION(this->m_pfnioctlsocket, LPFN_IOCTLSOCKET, "ioctlsocket");
  4512. LOADWINSOCKFUNCTION(this->m_pfnconnect, LPFN_CONNECT, "connect");
  4513. LOADWINSOCKFUNCTION(this->m_pfnshutdown, LPFN_SHUTDOWN, "shutdown");
  4514. LOADWINSOCKFUNCTION(this->m_pfnsend, LPFN_SEND, "send");
  4515. LOADWINSOCKFUNCTION(this->m_pfnrecv, LPFN_RECV, "recv");
  4516. #ifdef DBG
  4517. LOADWINSOCKFUNCTION(this->m_pfngetsockopt, LPFN_GETSOCKOPT, "getsockopt");
  4518. #endif // DBG
  4519. Exit:
  4520. return hr;
  4521. Failure:
  4522. hr = DPNHERR_GENERIC;
  4523. goto Exit;
  4524. } // CNATHelpUPnP::LoadWinSockFunctionPointers
  4525. #undef DPF_MODNAME
  4526. #define DPF_MODNAME "CNATHelpUPnP::CheckForNewDevices"
  4527. //=============================================================================
  4528. // CNATHelpUPnP::CheckForNewDevices
  4529. //-----------------------------------------------------------------------------
  4530. //
  4531. // Description: Detects new IP capable devices that have been added and
  4532. // removes old ones no longer available.
  4533. //
  4534. // The object lock is assumed to be held.
  4535. //
  4536. // Arguments:
  4537. // BOOL * pfFoundNewDevices Pointer to boolean to set to TRUE if new
  4538. // devices were added, or NULL if don't care.
  4539. //
  4540. // Returns: HRESULT
  4541. // DPNH_OK - The check was successful.
  4542. // DPNHERR_GENERIC - An error occurred.
  4543. //=============================================================================
  4544. HRESULT CNATHelpUPnP::CheckForNewDevices(BOOL * const pfFoundNewDevices)
  4545. {
  4546. HRESULT hr = DPNH_OK;
  4547. #if ((defined(DBG)) || (! defined(DPNBUILD_NOWINSOCK2)))
  4548. DWORD dwError;
  4549. #endif // DBG or ! DPNBUILD_NOWINSOCK2
  4550. #ifndef DPNBUILD_NOWINSOCK2
  4551. int iReturn;
  4552. #endif // ! DPNBUILD_NOWINSOCK2
  4553. char szName[1000];
  4554. PHOSTENT phostent;
  4555. IN_ADDR ** ppinaddr;
  4556. DWORD dwAddressesSize = 0;
  4557. DWORD dwNumAddresses = 0;
  4558. IN_ADDR * painaddrAddresses = NULL;
  4559. CBilink * pBilinkDevice;
  4560. CDevice * pDevice = NULL; // NULL it for PREfix, even though fDeviceCreated guards it
  4561. BOOL fDeviceCreated = FALSE;
  4562. BOOL fFound;
  4563. CBilink * pBilinkRegPort;
  4564. CRegisteredPort * pRegisteredPort;
  4565. SOCKET sTemp = INVALID_SOCKET;
  4566. SOCKADDR_IN saddrinTemp;
  4567. DWORD dwTemp;
  4568. #ifndef DPNBUILD_NOWINSOCK2
  4569. SOCKET_ADDRESS * paSocketAddresses;
  4570. #endif // ! DPNBUILD_NOWINSOCK2
  4571. DPFX(DPFPREP, 5, "(0x%p) Parameters (0x%p)", this, pfFoundNewDevices);
  4572. #ifndef DPNBUILD_NOWINSOCK2
  4573. //
  4574. // Handle any address list change Ioctl completions that may have gotten us
  4575. // here.
  4576. //
  4577. if ((this->m_hAlertEvent != NULL) ||
  4578. (this->m_hAlertIOCompletionPort != NULL))
  4579. {
  4580. DNASSERT(this->m_sIoctls != INVALID_SOCKET);
  4581. DNASSERT(this->m_polAddressListChange != NULL);
  4582. if (this->m_pfnWSAGetOverlappedResult(this->m_sIoctls, //
  4583. this->m_polAddressListChange, //
  4584. &dwTemp, // ignore bytes transferred
  4585. FALSE, // don't wait
  4586. &dwTemp)) // ignore flags
  4587. {
  4588. DPFX(DPFPREP, 1, "Received address list change notification.");
  4589. //
  4590. // Overlapped result completed. Reissue it.
  4591. //
  4592. hr = this->RequestLocalAddressListChangeNotification();
  4593. if (hr != DPNH_OK)
  4594. {
  4595. DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
  4596. goto Failure;
  4597. }
  4598. }
  4599. else
  4600. {
  4601. //
  4602. // Figure out what error it was.
  4603. //
  4604. dwError = this->m_pfnWSAGetLastError();
  4605. switch (dwError)
  4606. {
  4607. case WSA_IO_INCOMPLETE:
  4608. {
  4609. //
  4610. // It hasn't completed yet.
  4611. //
  4612. break;
  4613. }
  4614. case ERROR_OPERATION_ABORTED:
  4615. {
  4616. //
  4617. // The thread that we originally submitted the Ioctl on
  4618. // went away and so the OS kindly cancelled the operation
  4619. // on us. How nice. Well, let's try resubmitting it.
  4620. //
  4621. DPFX(DPFPREP, 1, "Thread that submitted previous address list change notification went away, rerequesting.");
  4622. hr = this->RequestLocalAddressListChangeNotification();
  4623. if (hr != DPNH_OK)
  4624. {
  4625. DPFX(DPFPREP, 0, "Couldn't request local address list change notification!");
  4626. goto Failure;
  4627. }
  4628. break;
  4629. }
  4630. default:
  4631. {
  4632. DPFX(DPFPREP, 0, "Couldn't get overlapped result, error = %u! Ignoring.", dwError);
  4633. break;
  4634. }
  4635. } // end switch (on error)
  4636. }
  4637. }
  4638. //
  4639. // If we're on WinSock 2, let's try getting the address list with
  4640. // an Ioctl.
  4641. //
  4642. if (! (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1))
  4643. {
  4644. DNASSERT(this->m_sIoctls != INVALID_SOCKET);
  4645. DNASSERT(this->m_pfnWSAIoctl != NULL);
  4646. //
  4647. // Keep trying to get the address list until we have a large enough
  4648. // buffer. We use the IN_ADDR array pointer simply because it's
  4649. // already there. We know that IN_ADDRs are smaller than
  4650. // SOCKET_ADDRESSes, so we can reuse the same buffer.
  4651. //
  4652. do
  4653. {
  4654. iReturn = this->m_pfnWSAIoctl(this->m_sIoctls, // use the special Ioctl socket
  4655. SIO_ADDRESS_LIST_QUERY, //
  4656. NULL, // no input data
  4657. 0, // no input data
  4658. painaddrAddresses, // output buffer
  4659. dwAddressesSize, // output buffer size
  4660. &dwTemp, // bytes needed
  4661. NULL, // no overlapped structure
  4662. NULL); // no completion routine
  4663. if (iReturn != 0)
  4664. {
  4665. dwError = this->m_pfnWSAGetLastError();
  4666. //
  4667. // Free the previous buffer, no matter what error it was.
  4668. //
  4669. if (painaddrAddresses != NULL)
  4670. {
  4671. DNFree(painaddrAddresses);
  4672. painaddrAddresses = NULL;
  4673. }
  4674. if (dwError != WSAEFAULT)
  4675. {
  4676. DPFX(DPFPREP, 1, "Retrieving address list failed (err = %u), trying WinSock 1 method.", dwError);
  4677. //
  4678. // We'll try the old-fashioned WinSock 1 way.
  4679. //
  4680. break;
  4681. }
  4682. //
  4683. // Be absolutely sure WinSock isn't causing us trouble.
  4684. //
  4685. if (dwTemp < sizeof(SOCKET_ADDRESS_LIST))
  4686. {
  4687. DPFX(DPFPREP, 0, "Received an invalid buffer size (%u < %u)!",
  4688. dwTemp, sizeof(SOCKET_ADDRESS_LIST));
  4689. //
  4690. // We'll try the old-fashioned WinSock 1 way.
  4691. //
  4692. break;
  4693. }
  4694. //
  4695. // The buffer wasn't large enough. Try again.
  4696. //
  4697. painaddrAddresses = (IN_ADDR*) DNMalloc(dwTemp);
  4698. if (painaddrAddresses == NULL)
  4699. {
  4700. hr = DPNHERR_OUTOFMEMORY;
  4701. goto Failure;
  4702. }
  4703. dwAddressesSize = dwTemp;
  4704. }
  4705. else
  4706. {
  4707. //
  4708. // Success! We're going to being sneaky and reuse the buffer.
  4709. // We know that the SOCKET_ADDRESS_LIST returned will be larger
  4710. // than an array of IN_ADDRs, so we can save a malloc.
  4711. //
  4712. // But first, be absolutely sure WinSock isn't causing us
  4713. // trouble.
  4714. //
  4715. if (painaddrAddresses == NULL)
  4716. {
  4717. DPFX(DPFPREP, 0, "WinSock returned success with a NULL buffer!");
  4718. //
  4719. // We'll try the old-fashioned WinSock 1 way.
  4720. //
  4721. break;
  4722. }
  4723. dwNumAddresses = ((SOCKET_ADDRESS_LIST*) painaddrAddresses)->iAddressCount;
  4724. dwAddressesSize = 0;
  4725. //
  4726. // Make sure there are addresses.
  4727. //
  4728. if (dwNumAddresses > 0)
  4729. {
  4730. DPFX(DPFPREP, 7, "WinSock 2 Ioctl returned %u addresses:", dwNumAddresses);
  4731. paSocketAddresses = ((SOCKET_ADDRESS_LIST*) painaddrAddresses)->Address;
  4732. for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
  4733. {
  4734. DNASSERT(paSocketAddresses[dwTemp].iSockaddrLength == sizeof(SOCKADDR_IN));
  4735. DNASSERT(paSocketAddresses[dwTemp].lpSockaddr != NULL);
  4736. DNASSERT(paSocketAddresses[dwTemp].lpSockaddr->sa_family == AF_INET);
  4737. //
  4738. // Ignore 0.0.0.0 addresses.
  4739. //
  4740. if (((SOCKADDR_IN*) (paSocketAddresses[dwTemp].lpSockaddr))->sin_addr.S_un.S_addr != INADDR_NONE)
  4741. {
  4742. //
  4743. // Move the IN_ADDR component of this address
  4744. // toward the front of the buffer, into it's
  4745. // correct place in the array.
  4746. //
  4747. painaddrAddresses[dwTemp].S_un.S_addr = ((SOCKADDR_IN*) (paSocketAddresses[dwTemp].lpSockaddr))->sin_addr.S_un.S_addr;
  4748. DPFX(DPFPREP, 7, "\t%u- %u.%u.%u.%u",
  4749. dwTemp,
  4750. painaddrAddresses[dwTemp].S_un.S_un_b.s_b1,
  4751. painaddrAddresses[dwTemp].S_un.S_un_b.s_b2,
  4752. painaddrAddresses[dwTemp].S_un.S_un_b.s_b3,
  4753. painaddrAddresses[dwTemp].S_un.S_un_b.s_b4);
  4754. }
  4755. else
  4756. {
  4757. DPFX(DPFPREP, 1, "\t%u- Ignoring 0.0.0.0 address.", dwTemp);
  4758. dwAddressesSize++;
  4759. //
  4760. // The code should handle this fine, but why is
  4761. // WinSock doing this to us?
  4762. //
  4763. DNASSERT(FALSE);
  4764. }
  4765. }
  4766. //
  4767. // Subtract out any invalid addresses that we skipped.
  4768. //
  4769. dwNumAddresses -= dwAddressesSize;
  4770. if (dwNumAddresses == 0)
  4771. {
  4772. DPFX(DPFPREP, 1, "WinSock 2 reported only invalid addresses, hoping WinSock 1 method picks up the loopback address.");
  4773. DNFree(painaddrAddresses);
  4774. painaddrAddresses = NULL;
  4775. }
  4776. }
  4777. else
  4778. {
  4779. DPFX(DPFPREP, 1, "WinSock 2 Ioctl did not report any valid addresses, hoping WinSock 1 method picks up the loopback address.");
  4780. DNFree(painaddrAddresses);
  4781. painaddrAddresses = NULL;
  4782. }
  4783. //
  4784. // Get out of the loop.
  4785. //
  4786. break;
  4787. }
  4788. }
  4789. while (TRUE);
  4790. }
  4791. //
  4792. // Get the list of all available addresses from the WinSock 1 API if we
  4793. // don't already have them.
  4794. //
  4795. if (painaddrAddresses == NULL)
  4796. #endif // ! DPNBUILD_NOWINSOCK2
  4797. {
  4798. if (this->m_pfngethostname(szName, 1000) != 0)
  4799. {
  4800. #ifdef DBG
  4801. dwError = this->m_pfnWSAGetLastError();
  4802. DPFX(DPFPREP, 0, "Couldn't get host name, error = %u!", dwError);
  4803. #endif // DBG
  4804. hr = DPNHERR_GENERIC;
  4805. goto Failure;
  4806. }
  4807. phostent = this->m_pfngethostbyname(szName);
  4808. if (phostent == NULL)
  4809. {
  4810. #ifdef DBG
  4811. dwError = this->m_pfnWSAGetLastError();
  4812. DPFX(DPFPREP, 0, "Couldn't retrieve addresses, error = %u!", dwError);
  4813. #endif // DBG
  4814. hr = DPNHERR_GENERIC;
  4815. goto Failure;
  4816. }
  4817. //
  4818. // WinSock says that you need to copy this data before you make any
  4819. // other API calls. So first we count the number of entries we need to
  4820. // copy.
  4821. //
  4822. ppinaddr = (IN_ADDR**) phostent->h_addr_list;
  4823. while ((*ppinaddr) != NULL)
  4824. {
  4825. //
  4826. // Ignore 0.0.0.0 addresses.
  4827. //
  4828. if ((*ppinaddr)->S_un.S_addr != INADDR_NONE)
  4829. {
  4830. dwNumAddresses++;
  4831. }
  4832. else
  4833. {
  4834. DPFX(DPFPREP, 1, "Ignoring 0.0.0.0 address.");
  4835. //
  4836. // The code should handle this fine, but why is WinSock doing
  4837. // this to us?
  4838. //
  4839. DNASSERT(FALSE);
  4840. }
  4841. ppinaddr++;
  4842. }
  4843. //
  4844. // If there aren't any addresses, we must fail. WinSock 1 ought to
  4845. // report the loopback address at least.
  4846. //
  4847. if (dwNumAddresses == 0)
  4848. {
  4849. DPFX(DPFPREP, 0, "WinSock 1 did not report any valid addresses!");
  4850. hr = DPNHERR_GENERIC;
  4851. goto Failure;
  4852. }
  4853. DPFX(DPFPREP, 7, "WinSock 1 method returned %u valid addresses:", dwNumAddresses);
  4854. painaddrAddresses = (IN_ADDR*) DNMalloc(dwNumAddresses * sizeof(IN_ADDR));
  4855. if (painaddrAddresses == NULL)
  4856. {
  4857. hr = DPNHERR_OUTOFMEMORY;
  4858. goto Failure;
  4859. }
  4860. //
  4861. // Now copy all the addresses.
  4862. //
  4863. ppinaddr = (IN_ADDR**) phostent->h_addr_list;
  4864. dwTemp = 0;
  4865. while ((*ppinaddr) != NULL)
  4866. {
  4867. //
  4868. // Ignore 0.0.0.0 addresses again.
  4869. //
  4870. if ((*ppinaddr)->S_un.S_addr != INADDR_NONE)
  4871. {
  4872. painaddrAddresses[dwTemp].S_un.S_addr = (*ppinaddr)->S_un.S_addr;
  4873. DPFX(DPFPREP, 7, "\t%u- %u.%u.%u.%u",
  4874. dwTemp,
  4875. painaddrAddresses[dwTemp].S_un.S_un_b.s_b1,
  4876. painaddrAddresses[dwTemp].S_un.S_un_b.s_b2,
  4877. painaddrAddresses[dwTemp].S_un.S_un_b.s_b3,
  4878. painaddrAddresses[dwTemp].S_un.S_un_b.s_b4);
  4879. dwTemp++;
  4880. }
  4881. ppinaddr++;
  4882. }
  4883. DNASSERT(dwTemp == dwNumAddresses);
  4884. }
  4885. /*
  4886. else
  4887. {
  4888. //
  4889. // Already have addresses array.
  4890. //
  4891. }
  4892. */
  4893. //
  4894. // Make sure that all of the devices we currently know about are still
  4895. // around.
  4896. //
  4897. pBilinkDevice = this->m_blDevices.GetNext();
  4898. while (pBilinkDevice != &this->m_blDevices)
  4899. {
  4900. DNASSERT(! pBilinkDevice->IsEmpty());
  4901. pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
  4902. pBilinkDevice = pBilinkDevice->GetNext();
  4903. fFound = FALSE;
  4904. for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
  4905. {
  4906. if (painaddrAddresses[dwTemp].S_un.S_addr == pDevice->GetLocalAddressV4())
  4907. {
  4908. fFound = TRUE;
  4909. break;
  4910. }
  4911. }
  4912. if (fFound)
  4913. {
  4914. //
  4915. // It may be time for this device to use a different port...
  4916. //
  4917. dwTemp = pDevice->GetFirstUPnPDiscoveryTime();
  4918. if ((dwTemp != 0) && ((GETTIMESTAMP() - dwTemp) > g_dwReusePortTime))
  4919. {
  4920. ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
  4921. saddrinTemp.sin_family = AF_INET;
  4922. saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  4923. sTemp = this->CreateSocket(&saddrinTemp, SOCK_DGRAM, IPPROTO_UDP);
  4924. if (sTemp != INVALID_SOCKET)
  4925. {
  4926. //
  4927. // Sanity check that we didn't lose the device address.
  4928. //
  4929. DNASSERT(saddrinTemp.sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4());
  4930. DPFX(DPFPREP, 4, "Device 0x%p UPnP discovery socket 0x%p (%u.%u.%u.%u:%u) created to replace port %u.",
  4931. pDevice,
  4932. sTemp,
  4933. saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
  4934. saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
  4935. saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
  4936. saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
  4937. NTOHS(saddrinTemp.sin_port),
  4938. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  4939. #ifndef DPNBUILD_NOHNETFWAPI
  4940. //
  4941. // If we used the HomeNet firewall API to open a hole for UPnP
  4942. // discovery multicasts, close it.
  4943. //
  4944. if (pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall())
  4945. {
  4946. hr = this->CloseDevicesUPnPDiscoveryPort(pDevice, NULL);
  4947. if (hr != DPNH_OK)
  4948. {
  4949. DPFX(DPFPREP, 0, "Couldn't close device 0x%p's previous UPnP discovery socket's port on firewall (err = 0x%lx)! Ignoring.",
  4950. pDevice, hr);
  4951. //
  4952. // Continue...
  4953. //
  4954. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  4955. hr = DPNH_OK;
  4956. }
  4957. }
  4958. #endif // ! DPNBUILD_NOHNETFWAPI
  4959. pDevice->SetUPnPDiscoverySocketPort(saddrinTemp.sin_port);
  4960. pDevice->SetFirstUPnPDiscoveryTime(0);
  4961. //
  4962. // Close the existing socket.
  4963. //
  4964. this->m_pfnclosesocket(pDevice->GetUPnPDiscoverySocket());
  4965. //
  4966. // Transfer ownership of the new socket to the device.
  4967. //
  4968. pDevice->SetUPnPDiscoverySocket(sTemp);
  4969. sTemp = INVALID_SOCKET;
  4970. DPFX(DPFPREP, 8, "Device 0x%p got re-assigned UPnP socket 0x%p.",
  4971. pDevice, pDevice->GetUPnPDiscoverySocket());
  4972. //
  4973. // We'll let the normal "check for firewall" code detect
  4974. // the fact that the discovery socket is not mapped on the
  4975. // firewall and try to do so there (if it even needs to be
  4976. // mapped). See UpdateServerStatus.
  4977. //
  4978. }
  4979. else
  4980. {
  4981. DPFX(DPFPREP, 0, "Couldn't create a replacement UPnP discovery socket for device 0x%p! Using existing port %u.",
  4982. pDevice, NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  4983. }
  4984. }
  4985. }
  4986. else
  4987. {
  4988. //
  4989. // Didn't find this device in the returned list, forget about
  4990. // it.
  4991. //
  4992. #ifdef DBG
  4993. {
  4994. IN_ADDR inaddrTemp;
  4995. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  4996. DPFX(DPFPREP, 1, "Device 0x%p no longer exists, removing (address was %u.%u.%u.%u).",
  4997. pDevice,
  4998. inaddrTemp.S_un.S_un_b.s_b1,
  4999. inaddrTemp.S_un.S_un_b.s_b2,
  5000. inaddrTemp.S_un.S_un_b.s_b3,
  5001. inaddrTemp.S_un.S_un_b.s_b4);
  5002. }
  5003. this->m_dwNumDeviceRemoves++;
  5004. #endif // DBG
  5005. //
  5006. // Override the minimum UpdateServerStatus interval so that we can
  5007. // get information on any local public address changes due to the
  5008. // possible loss of a server on this interface.
  5009. //
  5010. this->m_dwFlags |= NATHELPUPNPOBJ_DEVICECHANGED;
  5011. //
  5012. // Since there was a change in the network, go back to polling
  5013. // relatively quickly.
  5014. //
  5015. this->ResetNextPollInterval();
  5016. //
  5017. // Forcefully mark the UPnP gateway device as disconnected.
  5018. //
  5019. if (pDevice->GetUPnPDevice() != NULL)
  5020. {
  5021. this->ClearDevicesUPnPDevice(pDevice);
  5022. }
  5023. //
  5024. // Mark all ports that were registered to this device as unowned
  5025. // by putting them into the wildcard list. First unmap them from
  5026. // the firewall.
  5027. //
  5028. pBilinkRegPort = pDevice->m_blOwnedRegPorts.GetNext();
  5029. while (pBilinkRegPort != &pDevice->m_blOwnedRegPorts)
  5030. {
  5031. DNASSERT(! pBilinkRegPort->IsEmpty());
  5032. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegPort);
  5033. pBilinkRegPort = pBilinkRegPort->GetNext();
  5034. DPFX(DPFPREP, 1, "Registered port 0x%p's device went away, marking as unowned.",
  5035. pRegisteredPort);
  5036. #ifndef DPNBUILD_NOHNETFWAPI
  5037. //
  5038. // Even though the device is gone, we can still remove the
  5039. // firewall mapping.
  5040. //
  5041. if (pRegisteredPort->IsMappedOnHNetFirewall())
  5042. {
  5043. //
  5044. // Unmap the port.
  5045. //
  5046. // Alert the user since this is unexpected.
  5047. //
  5048. hr = this->UnmapPortOnLocalHNetFirewall(pRegisteredPort,
  5049. TRUE,
  5050. TRUE);
  5051. if (hr != DPNH_OK)
  5052. {
  5053. DPFX(DPFPREP, 0, "Couldn't unmap registered port 0x%p from device 0x%p's firewall (err = 0x%lx)! Ignoring.",
  5054. pRegisteredPort, pDevice, hr);
  5055. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  5056. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  5057. //
  5058. // Continue anyway.
  5059. //
  5060. hr = DPNH_OK;
  5061. }
  5062. }
  5063. pRegisteredPort->NoteNotHNetFirewallPortUnavailable();
  5064. #endif // ! DPNBUILD_NOHNETFWAPI
  5065. DNASSERT(! pRegisteredPort->HasUPnPPublicAddresses());
  5066. DNASSERT(! pRegisteredPort->IsUPnPPortUnavailable());
  5067. pRegisteredPort->ClearDeviceOwner();
  5068. pRegisteredPort->m_blDeviceList.RemoveFromList();
  5069. pRegisteredPort->m_blDeviceList.InsertBefore(&this->m_blUnownedPorts);
  5070. //
  5071. // The user doesn't directly need to be informed. If the ports
  5072. // previously had public addresses, the ADDRESSESCHANGED flag
  5073. // would have already been set by ClearDevicesUPnPDevice. If
  5074. // they didn't have ports with public addresses, then the user
  5075. // won't see any difference and thus ADDRESSESCHANGED wouldn't
  5076. // need to be set.
  5077. //
  5078. }
  5079. #ifndef DPNBUILD_NOHNETFWAPI
  5080. //
  5081. // If we used the HomeNet firewall API to open a hole for UPnP
  5082. // discovery multicasts, close it.
  5083. //
  5084. if (pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall())
  5085. {
  5086. hr = this->CloseDevicesUPnPDiscoveryPort(pDevice, NULL);
  5087. if (hr != DPNH_OK)
  5088. {
  5089. DPFX(DPFPREP, 0, "Couldn't close device 0x%p's UPnP discovery socket's port on firewall (err = 0x%lx)! Ignoring.",
  5090. pDevice, hr);
  5091. //
  5092. // Continue...
  5093. //
  5094. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  5095. hr = DPNH_OK;
  5096. }
  5097. }
  5098. #endif // ! DPNBUILD_NOHNETFWAPI
  5099. pDevice->m_blList.RemoveFromList();
  5100. //
  5101. // Close the socket, if we had one.
  5102. //
  5103. if (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP)
  5104. {
  5105. this->m_pfnclosesocket(pDevice->GetUPnPDiscoverySocket());
  5106. pDevice->SetUPnPDiscoverySocket(INVALID_SOCKET);
  5107. }
  5108. delete pDevice;
  5109. }
  5110. }
  5111. //
  5112. // Search for all returned devices in our existing list, and add new
  5113. // entries for each one that we didn't already know about.
  5114. //
  5115. for(dwTemp = 0; dwTemp < dwNumAddresses; dwTemp++)
  5116. {
  5117. fFound = FALSE;
  5118. pBilinkDevice = this->m_blDevices.GetNext();
  5119. while (pBilinkDevice != &this->m_blDevices)
  5120. {
  5121. DNASSERT(! pBilinkDevice->IsEmpty());
  5122. pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
  5123. pBilinkDevice = pBilinkDevice->GetNext();
  5124. if (pDevice->GetLocalAddressV4() == painaddrAddresses[dwTemp].S_un.S_addr)
  5125. {
  5126. fFound = TRUE;
  5127. break;
  5128. }
  5129. }
  5130. if (! fFound)
  5131. {
  5132. //
  5133. // We didn't know about this device. Create a new object.
  5134. //
  5135. pDevice = new CDevice(painaddrAddresses[dwTemp].S_un.S_addr);
  5136. if (pDevice == NULL)
  5137. {
  5138. hr = DPNHERR_OUTOFMEMORY;
  5139. goto Failure;
  5140. }
  5141. fDeviceCreated = TRUE;
  5142. #ifdef DBG
  5143. DPFX(DPFPREP, 1, "Found new device %u.%u.%u.%u, (object = 0x%p).",
  5144. painaddrAddresses[dwTemp].S_un.S_un_b.s_b1,
  5145. painaddrAddresses[dwTemp].S_un.S_un_b.s_b2,
  5146. painaddrAddresses[dwTemp].S_un.S_un_b.s_b3,
  5147. painaddrAddresses[dwTemp].S_un.S_un_b.s_b4,
  5148. pDevice);
  5149. this->m_dwNumDeviceAdds++;
  5150. #endif // DBG
  5151. //
  5152. // Override the minimum UpdateServerStatus interval so that we can
  5153. // get information on this new device.
  5154. //
  5155. this->m_dwFlags |= NATHELPUPNPOBJ_DEVICECHANGED;
  5156. //
  5157. // Since there was a change in the network, go back to polling
  5158. // relatively quickly.
  5159. //
  5160. this->ResetNextPollInterval();
  5161. //
  5162. // Create the UPnP discovery socket, if we're allowed.
  5163. //
  5164. if (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP)
  5165. {
  5166. ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
  5167. saddrinTemp.sin_family = AF_INET;
  5168. saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  5169. sTemp = this->CreateSocket(&saddrinTemp, SOCK_DGRAM, IPPROTO_UDP);
  5170. if (sTemp == INVALID_SOCKET)
  5171. {
  5172. DPFX(DPFPREP, 0, "Couldn't create a UPnP discovery socket! Ignoring address (and destroying device 0x%p).",
  5173. pDevice);
  5174. //
  5175. // Get rid of the device.
  5176. //
  5177. delete pDevice;
  5178. pDevice = NULL;
  5179. //
  5180. // Forget about device in case of failure later.
  5181. //
  5182. fDeviceCreated = FALSE;
  5183. //
  5184. // Move to next address.
  5185. //
  5186. continue;
  5187. }
  5188. //
  5189. // Sanity check that we didn't lose the device address.
  5190. //
  5191. DNASSERT(saddrinTemp.sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4());
  5192. DPFX(DPFPREP, 4, "Device 0x%p UPnP discovery socket 0x%p (%u.%u.%u.%u:%u) created.",
  5193. pDevice,
  5194. sTemp,
  5195. saddrinTemp.sin_addr.S_un.S_un_b.s_b1,
  5196. saddrinTemp.sin_addr.S_un.S_un_b.s_b2,
  5197. saddrinTemp.sin_addr.S_un.S_un_b.s_b3,
  5198. saddrinTemp.sin_addr.S_un.S_un_b.s_b4,
  5199. NTOHS(saddrinTemp.sin_port));
  5200. pDevice->SetUPnPDiscoverySocketPort(saddrinTemp.sin_port);
  5201. //
  5202. // Transfer ownership of the socket to the device.
  5203. //
  5204. pDevice->SetUPnPDiscoverySocket(sTemp);
  5205. sTemp = INVALID_SOCKET;
  5206. DPFX(DPFPREP, 8, "Device 0x%p got assigned UPnP socket 0x%p.",
  5207. pDevice, pDevice->GetUPnPDiscoverySocket());
  5208. }
  5209. #ifndef DPNBUILD_NOHNETFWAPI
  5210. if (this->m_dwFlags & NATHELPUPNPOBJ_USEHNETFWAPI)
  5211. {
  5212. //
  5213. // Check if the local firewall is enabled.
  5214. //
  5215. hr = this->CheckForLocalHNetFirewallAndMapPorts(pDevice, NULL);
  5216. if (hr != DPNH_OK)
  5217. {
  5218. DPFX(DPFPREP, 0, "Couldn't check for local HNet firewall and map ports (err = 0x%lx)! Continuing.",
  5219. hr);
  5220. DNASSERT(! pDevice->IsHNetFirewalled());
  5221. hr = DPNH_OK;
  5222. }
  5223. }
  5224. else
  5225. {
  5226. //
  5227. // Not using firewall traversal.
  5228. //
  5229. }
  5230. #endif // ! DPNBUILD_NOHNETFWAPI
  5231. //
  5232. // Add the device to our known list.
  5233. //
  5234. pDevice->m_blList.InsertBefore(&this->m_blDevices);
  5235. //
  5236. // Inform the caller if they care.
  5237. //
  5238. if (pfFoundNewDevices != NULL)
  5239. {
  5240. (*pfFoundNewDevices) = TRUE;
  5241. }
  5242. //
  5243. // Forget about device in case of failure later.
  5244. //
  5245. fDeviceCreated = FALSE;
  5246. }
  5247. }
  5248. //
  5249. // If we got some very weird failures and ended up here without any
  5250. // devices, complain to management (or the caller of this function, that's
  5251. // probably more convenient).
  5252. //
  5253. if (this->m_blDevices.IsEmpty())
  5254. {
  5255. DPFX(DPFPREP, 0, "No usable devices, cannot proceed!", 0);
  5256. DNASSERTX(! "No usable devices!", 2);
  5257. hr = DPNHERR_GENERIC;
  5258. goto Failure;
  5259. }
  5260. Exit:
  5261. if (painaddrAddresses != NULL)
  5262. {
  5263. DNFree(painaddrAddresses);
  5264. painaddrAddresses = NULL;
  5265. }
  5266. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  5267. return hr;
  5268. Failure:
  5269. if (sTemp != INVALID_SOCKET)
  5270. {
  5271. this->m_pfnclosesocket(sTemp);
  5272. sTemp = INVALID_SOCKET;
  5273. }
  5274. if (fDeviceCreated)
  5275. {
  5276. delete pDevice;
  5277. }
  5278. goto Exit;
  5279. } // CNATHelpUPnP::CheckForNewDevices
  5280. #ifndef DPNBUILD_NOHNETFWAPI
  5281. #undef DPF_MODNAME
  5282. #define DPF_MODNAME "CNATHelpUPnP::CheckForLocalHNetFirewallAndMapPorts"
  5283. //=============================================================================
  5284. // CNATHelpUPnP::CheckForLocalHNetFirewallAndMapPorts
  5285. //-----------------------------------------------------------------------------
  5286. //
  5287. // Description: Looks for a local HomeNet API aware firewall, and ensures
  5288. // there are mappings for each of the device's registered ports,
  5289. // if a firewall is found.
  5290. //
  5291. // If any registered port (except pDontAlertRegisteredPort if
  5292. // not NULL) gets mapped, then it will trigger an address update
  5293. // alert the next time the user calls GetCaps.
  5294. //
  5295. // The main object lock is assumed to be held. It will be
  5296. // converted into the long lock for the duration of this function.
  5297. //
  5298. // Arguments:
  5299. // CDevice * pDevice - Pointer to device to check.
  5300. // CRegisteredPort * pDontAlertRegisteredPort - Pointer to registered port
  5301. // that should not trigger an
  5302. // address update alert, or
  5303. // NULL.
  5304. //
  5305. // Returns: HRESULT
  5306. // DPNH_OK - Search completed successfully. There may or may not
  5307. // be a firewall.
  5308. // DPNHERR_GENERIC - An error occurred.
  5309. //=============================================================================
  5310. HRESULT CNATHelpUPnP::CheckForLocalHNetFirewallAndMapPorts(CDevice * const pDevice,
  5311. CRegisteredPort * const pDontAlertRegisteredPort)
  5312. {
  5313. HRESULT hr = DPNH_OK;
  5314. BOOL fSwitchedToLongLock = FALSE;
  5315. BOOL fUninitializeCOM = FALSE;
  5316. IHNetCfgMgr * pHNetCfgMgr = NULL;
  5317. IHNetConnection * pHNetConnection = NULL;
  5318. CBilink * pBilink;
  5319. CRegisteredPort * pRegisteredPort;
  5320. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  5321. this, pDevice, pDontAlertRegisteredPort);
  5322. //
  5323. // If this is the loopback address, don't bother trying to map anything.
  5324. //
  5325. if (pDevice->GetLocalAddressV4() == NETWORKBYTEORDER_INADDR_LOOPBACK)
  5326. {
  5327. DPFX(DPFPREP, 7, "No firewall behavior necessary with loopback device 0x%p.",
  5328. pDevice);
  5329. goto Exit;
  5330. }
  5331. //
  5332. // If we don't have IPHLPAPI or RASAPI32, we can't do anything (and
  5333. // shouldn't need to).
  5334. //
  5335. if ((this->m_hIpHlpApiDLL == NULL) || (this->m_hRasApi32DLL == NULL))
  5336. {
  5337. DPFX(DPFPREP, 7, "Didn't load IPHLPAPI and/or RASAPI32, not getting HNet interfaces for device 0x%p.",
  5338. pDevice);
  5339. goto Exit;
  5340. }
  5341. //
  5342. // Using the HomeNet API (particularly the out-of-proc COM calls) during
  5343. // stress is really, really, painfully slow. Since we have one global lock
  5344. // the controls everything, other threads may be sitting for an equally
  5345. // long time... so long, in fact, that the critical section timeout fires
  5346. // and we get a false stress hit. So we have a sneaky workaround to
  5347. // prevent that from happening while still maintaining ownership of the
  5348. // object.
  5349. //
  5350. this->SwitchToLongLock();
  5351. fSwitchedToLongLock = TRUE;
  5352. //
  5353. // Try to initialize COM if we weren't instantiated through COM. It may
  5354. // have already been initialized in a different mode, which is okay. As
  5355. // long as it has been initialized somehow, we're fine.
  5356. //
  5357. if (this->m_dwFlags & NATHELPUPNPOBJ_NOTCREATEDWITHCOM)
  5358. {
  5359. hr = CoInitializeEx(NULL, (COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE));
  5360. switch (hr)
  5361. {
  5362. case S_OK:
  5363. {
  5364. //
  5365. // Success, that's good. Cleanup when we're done.
  5366. //
  5367. DPFX(DPFPREP, 8, "Successfully initialized COM.");
  5368. fUninitializeCOM = TRUE;
  5369. break;
  5370. }
  5371. case S_FALSE:
  5372. {
  5373. //
  5374. // Someone else already initialized COM, but that's okay.
  5375. // Cleanup when we're done.
  5376. //
  5377. DPFX(DPFPREP, 8, "Initialized COM (again).");
  5378. fUninitializeCOM = TRUE;
  5379. break;
  5380. }
  5381. case RPC_E_CHANGED_MODE:
  5382. {
  5383. //
  5384. // Someone else already initialized COM in a different mode.
  5385. // It should be okay, but we don't have to balance the CoInit
  5386. // call with a CoUninit.
  5387. //
  5388. DPFX(DPFPREP, 8, "Didn't initialize COM, already initialized in a different mode.");
  5389. break;
  5390. }
  5391. default:
  5392. {
  5393. //
  5394. // Hmm, something else is going on. We can't handle that.
  5395. //
  5396. DPFX(DPFPREP, 0, "Initializing COM failed (err = 0x%lx)!", hr);
  5397. goto Failure;
  5398. break;
  5399. }
  5400. }
  5401. }
  5402. else
  5403. {
  5404. DPFX(DPFPREP, 8, "Object was instantiated through COM, no need to initialize COM.");
  5405. }
  5406. //
  5407. // Try creating the main HNet manager object.
  5408. //
  5409. hr = CoCreateInstance(CLSID_HNetCfgMgr, NULL, CLSCTX_INPROC_SERVER,
  5410. IID_IHNetCfgMgr, (PVOID*) (&pHNetCfgMgr));
  5411. if (hr != S_OK)
  5412. {
  5413. DPFX(DPFPREP, 1, "Couldn't create IHNetCfgMgr interface for device 0x%p (err = 0x%lx), assuming firewall control interface unavailable.",
  5414. pDevice, hr);
  5415. hr = DPNH_OK;
  5416. goto Exit;
  5417. }
  5418. //
  5419. // We created the IHNetCfgMgr object as in-proc, so there's no proxy that
  5420. // requires security settings.
  5421. //
  5422. //SETDEFAULTPROXYBLANKET(pHNetCfgMgr);
  5423. //
  5424. // Get the HNetConnection object for this device.
  5425. //
  5426. hr = this->GetIHNetConnectionForDeviceIfFirewalled(pDevice,
  5427. pHNetCfgMgr,
  5428. &pHNetConnection);
  5429. if (hr != DPNH_OK)
  5430. {
  5431. DPFX(DPFPREP, 1, "Couldn't get IHNetConnection interface for device 0x%p (err = 0x%lx), assuming firewall not enabled.",
  5432. pDevice, hr);
  5433. //
  5434. // If the device was previously firewalled, we need to clear our info.
  5435. //
  5436. if (pDevice->IsHNetFirewalled())
  5437. {
  5438. DPFX(DPFPREP, 2, "Firewall is no longer enabled for device 0x%p.",
  5439. pDevice);
  5440. //
  5441. // Since there was a change in the network, go back to polling
  5442. // relatively quickly.
  5443. //
  5444. this->ResetNextPollInterval();
  5445. DNASSERT(pDevice->HasCheckedForFirewallAvailability());
  5446. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  5447. while (pBilink != &pDevice->m_blOwnedRegPorts)
  5448. {
  5449. DNASSERT(! pBilink->IsEmpty());
  5450. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  5451. //
  5452. // Unmap items mapped on the firewall.
  5453. //
  5454. if (pRegisteredPort->IsMappedOnHNetFirewall())
  5455. {
  5456. DPFX(DPFPREP, 1, "Unmapping registered port 0x%p from device 0x%p's disappearing firewall.",
  5457. pRegisteredPort, pDevice);
  5458. hr = this->UnmapPortOnLocalHNetFirewallInternal(pRegisteredPort,
  5459. TRUE,
  5460. pHNetCfgMgr);
  5461. if (hr != DPNH_OK)
  5462. {
  5463. DPFX(DPFPREP, 0, "Couldn't unmap registered port 0x%p from device 0x%p's firewall (err = 0x%lx)! Ignoring.",
  5464. pRegisteredPort, pDevice, hr);
  5465. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  5466. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  5467. //
  5468. // Continue anyway.
  5469. //
  5470. hr = DPNH_OK;
  5471. }
  5472. //
  5473. // Alert the user.
  5474. //
  5475. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  5476. }
  5477. else
  5478. {
  5479. DPFX(DPFPREP, 1, "Registered port 0x%p was not mapped on device 0x%p's disappearing firewall, assuming being called within RegisterPorts.",
  5480. pRegisteredPort, pDevice);
  5481. }
  5482. //
  5483. // Go to next port.
  5484. //
  5485. pBilink = pBilink->GetNext();
  5486. }
  5487. //
  5488. // If we used the HomeNet firewall API to open a hole for UPnP
  5489. // discovery multicasts, unmap that, too.
  5490. //
  5491. if (pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall())
  5492. {
  5493. DPFX(DPFPREP, 0, "Device 0x%p's UPnP discovery socket's forcefully unmapped from disappearing firewall.",
  5494. pDevice);
  5495. hr = this->CloseDevicesUPnPDiscoveryPort(pDevice, pHNetCfgMgr);
  5496. if (hr != DPNH_OK)
  5497. {
  5498. DPFX(DPFPREP, 0, "Couldn't close device 0x%p's UPnP discovery socket's port on firewall (err = 0x%lx)! Ignoring.",
  5499. pDevice, hr);
  5500. //
  5501. // Continue...
  5502. //
  5503. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  5504. hr = DPNH_OK;
  5505. }
  5506. }
  5507. //
  5508. // Turn off the flag now that all registered ports have been
  5509. // removed.
  5510. //
  5511. pDevice->NoteNotHNetFirewalled();
  5512. }
  5513. else
  5514. {
  5515. if (! pDevice->HasCheckedForFirewallAvailability())
  5516. {
  5517. //
  5518. // The firewall is not enabled.
  5519. //
  5520. DPFX(DPFPREP, 2, "Firewall is not enabled for device 0x%p.",
  5521. pDevice);
  5522. pDevice->NoteCheckedForFirewallAvailability();
  5523. //
  5524. // Since it is possible to remove mappings even without the
  5525. // firewall enabled, we can be courteous and unmap any stale
  5526. // entries left by previous app crashes when the firewall was
  5527. // still enabled.
  5528. //
  5529. //
  5530. // Pretend that it currently had been firewalled.
  5531. //
  5532. pDevice->NoteHNetFirewalled();
  5533. //
  5534. // Cleanup the mappings.
  5535. //
  5536. hr = this->CleanupInactiveFirewallMappings(pDevice, pHNetCfgMgr);
  5537. if (hr != DPNH_OK)
  5538. {
  5539. DPFX(DPFPREP, 0, "Failed cleaning up inactive firewall mappings with device 0x%p (firewall not initially enabled)!",
  5540. pDevice);
  5541. goto Failure;
  5542. }
  5543. //
  5544. // Turn off the flag we temporarily enabled while clearing
  5545. // the mappings.
  5546. //
  5547. pDevice->NoteNotHNetFirewalled();
  5548. }
  5549. else
  5550. {
  5551. //
  5552. // The firewall is still not enabled.
  5553. //
  5554. DPFX(DPFPREP, 2, "Firewall is still not enabled for device 0x%p.",
  5555. pDevice);
  5556. }
  5557. }
  5558. hr = DPNH_OK;
  5559. goto Exit;
  5560. }
  5561. //
  5562. // If firewalling is enabled now, and wasn't before, we need to map all the
  5563. // existing ports. If it had been, we're fine.
  5564. //
  5565. if (! pDevice->IsHNetFirewalled())
  5566. {
  5567. DPFX(DPFPREP, 2, "Firewall is now enabled for device 0x%p.",
  5568. pDevice);
  5569. pDevice->NoteCheckedForFirewallAvailability();
  5570. pDevice->NoteHNetFirewalled();
  5571. //
  5572. // Since there was a change in the network, go back to polling
  5573. // relatively quickly.
  5574. //
  5575. this->ResetNextPollInterval();
  5576. //
  5577. // If we're allowed, we need to try opening a hole so that we can
  5578. // receive responses from device discovery multicasts. We'll
  5579. // ignore failures, since this is only to support the funky case of
  5580. // enabling firewall behind a NAT.
  5581. //
  5582. if ((g_fMapUPnPDiscoverySocket) &&
  5583. (pDevice->GetLocalAddressV4() != NETWORKBYTEORDER_INADDR_LOOPBACK) &&
  5584. (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP))
  5585. {
  5586. hr = this->OpenDevicesUPnPDiscoveryPort(pDevice,
  5587. pHNetCfgMgr,
  5588. pHNetConnection);
  5589. if (hr != DPNH_OK)
  5590. {
  5591. 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.",
  5592. pDevice, hr);
  5593. hr = DPNH_OK;
  5594. //
  5595. // Continue...
  5596. //
  5597. }
  5598. }
  5599. else
  5600. {
  5601. DPFX(DPFPREP, 3, "Not opening device 0x%p's UPnP discovery port (domap = %i, loopback = %i, upnp = %i).",
  5602. pDevice,
  5603. g_fMapUPnPDiscoverySocket,
  5604. ((pDevice->GetLocalAddressV4() != NETWORKBYTEORDER_INADDR_LOOPBACK) ? FALSE : TRUE),
  5605. ((this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP) ? TRUE : FALSE));
  5606. }
  5607. //
  5608. // Try to remove any mappings that were not freed earlier because
  5609. // we crashed.
  5610. //
  5611. hr = this->CleanupInactiveFirewallMappings(pDevice, pHNetCfgMgr);
  5612. if (hr != DPNH_OK)
  5613. {
  5614. DPFX(DPFPREP, 0, "Failed cleaning up inactive firewall mappings with device 0x%p's new firewall!",
  5615. pDevice);
  5616. goto Failure;
  5617. }
  5618. }
  5619. else
  5620. {
  5621. DPFX(DPFPREP, 2, "Firewall is still enabled for device 0x%p.",
  5622. pDevice);
  5623. DNASSERT(pDevice->HasCheckedForFirewallAvailability());
  5624. //
  5625. // Try to map the discovery socket if it hasn't been (and we're allowed
  5626. // & supposed to).
  5627. //
  5628. if ((g_fMapUPnPDiscoverySocket) &&
  5629. (! pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall()) &&
  5630. (pDevice->GetLocalAddressV4() != NETWORKBYTEORDER_INADDR_LOOPBACK) &&
  5631. (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP))
  5632. {
  5633. hr = this->OpenDevicesUPnPDiscoveryPort(pDevice,
  5634. pHNetCfgMgr,
  5635. pHNetConnection);
  5636. if (hr != DPNH_OK)
  5637. {
  5638. 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.",
  5639. pDevice, hr);
  5640. hr = DPNH_OK;
  5641. //
  5642. // Continue...
  5643. //
  5644. }
  5645. }
  5646. else
  5647. {
  5648. DPFX(DPFPREP, 3, "Not opening device 0x%p's UPnP discovery port (domap = %i, already = %i, loopback = %i, upnp = %i).",
  5649. pDevice,
  5650. g_fMapUPnPDiscoverySocket,
  5651. pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall(),
  5652. ((pDevice->GetLocalAddressV4() != NETWORKBYTEORDER_INADDR_LOOPBACK) ? FALSE : TRUE),
  5653. ((this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP) ? TRUE : FALSE));
  5654. }
  5655. }
  5656. //
  5657. // Map all the ports that haven't been yet.
  5658. //
  5659. hr = this->MapUnmappedPortsOnLocalHNetFirewall(pDevice,
  5660. pHNetCfgMgr,
  5661. pHNetConnection,
  5662. pDontAlertRegisteredPort);
  5663. if (hr != DPNH_OK)
  5664. {
  5665. DPFX(DPFPREP, 0, "Couldn't map ports on device 0x%p's new firewall (err = 0x%lx)!",
  5666. pDevice, hr);
  5667. goto Failure;
  5668. }
  5669. DNASSERT(hr == DPNH_OK);
  5670. Exit:
  5671. if (pHNetConnection != NULL)
  5672. {
  5673. pHNetConnection->Release();
  5674. pHNetConnection = NULL;
  5675. }
  5676. if (pHNetCfgMgr != NULL)
  5677. {
  5678. pHNetCfgMgr->Release();
  5679. pHNetCfgMgr = NULL;
  5680. }
  5681. if (fUninitializeCOM)
  5682. {
  5683. DPFX(DPFPREP, 8, "Uninitializing COM.");
  5684. CoUninitialize();
  5685. fUninitializeCOM = FALSE;
  5686. }
  5687. if (fSwitchedToLongLock)
  5688. {
  5689. this->SwitchFromLongLock();
  5690. fSwitchedToLongLock = FALSE;
  5691. }
  5692. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  5693. return hr;
  5694. Failure:
  5695. //
  5696. // Ensure that the device is not considered to be firewalled.
  5697. //
  5698. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  5699. pDevice->NoteNotHNetFirewalled();
  5700. //
  5701. // Make sure no registered ports are marked as firewalled either.
  5702. //
  5703. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  5704. while (pBilink != &pDevice->m_blOwnedRegPorts)
  5705. {
  5706. DNASSERT(! pBilink->IsEmpty());
  5707. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  5708. if (pRegisteredPort->IsMappedOnHNetFirewall())
  5709. {
  5710. DPFX(DPFPREP, 1, "Registered port 0x%p forcefully marked as not mapped on HomeNet firewall.",
  5711. pRegisteredPort);
  5712. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  5713. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  5714. }
  5715. pBilink = pBilink->GetNext();
  5716. }
  5717. goto Exit;
  5718. } // CNATHelpUPnP::CheckForLocalHNetFirewallAndMapPorts
  5719. #undef DPF_MODNAME
  5720. #define DPF_MODNAME "CNATHelpUPnP::GetIHNetConnectionForDeviceIfFirewalled"
  5721. //=============================================================================
  5722. // CNATHelpUPnP::GetIHNetConnectionForDeviceIfFirewalled
  5723. //-----------------------------------------------------------------------------
  5724. //
  5725. // Description: Returns an IHNetConnection interface for the given device.
  5726. //
  5727. // COM is assumed to have been initialized.
  5728. //
  5729. // The object lock is assumed to be held.
  5730. //
  5731. // Arguments:
  5732. // CDevice * pDevice - Pointer to device whose
  5733. // IHNetConnection interface
  5734. // should be retrieved.
  5735. // IHNetCfgMgr * pHNetCfgMgr - IHNetCfgMgr interface to use.
  5736. // IHNetConnection ** ppHNetConnection - Place to store IHetConnection
  5737. // interface retrieved.
  5738. //
  5739. // Returns: HRESULT
  5740. // DPNH_OK - Interface retrieved successfully.
  5741. // DPNHERR_GENERIC - An error occurred.
  5742. //=============================================================================
  5743. HRESULT CNATHelpUPnP::GetIHNetConnectionForDeviceIfFirewalled(CDevice * const pDevice,
  5744. IHNetCfgMgr * const pHNetCfgMgr,
  5745. IHNetConnection ** const ppHNetConnection)
  5746. {
  5747. HRESULT hr;
  5748. DWORD dwError;
  5749. IHNetFirewallSettings * pHNetFirewallSettings = NULL;
  5750. IEnumHNetFirewalledConnections * pEnumHNetFirewalledConnections = NULL;
  5751. IHNetFirewalledConnection * pHNetFirewalledConnection = NULL;
  5752. ULONG ulNumFound;
  5753. IHNetConnection * pHNetConnection = NULL;
  5754. HNET_CONN_PROPERTIES * pHNetConnProperties;
  5755. BOOL fLanConnection;
  5756. IN_ADDR inaddrTemp;
  5757. TCHAR tszDeviceIPAddress[16]; // "nnn.nnn.nnn.nnn" + NULL termination
  5758. BOOL fHaveDeviceGUID = FALSE;
  5759. TCHAR tszGuidDevice[GUID_STRING_LENGTH + 1]; // include NULL termination
  5760. TCHAR tszGuidHNetConnection[GUID_STRING_LENGTH + 1]; // include NULL termination
  5761. GUID * pguidHNetConnection = NULL;
  5762. WCHAR * pwszPhonebookPath = NULL;
  5763. WCHAR * pwszName = NULL;
  5764. HRASCONN hrasconn;
  5765. RASPPPIP raspppip;
  5766. DWORD dwSize;
  5767. DPFX(DPFPREP, 6, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p)",
  5768. this, pDevice, pHNetCfgMgr, ppHNetConnection);
  5769. //
  5770. // Convert the IP address right away. We use it frequently so there's no
  5771. // sense in continually regenerating it.
  5772. //
  5773. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  5774. wsprintf(tszDeviceIPAddress, _T("%u.%u.%u.%u"),
  5775. inaddrTemp.S_un.S_un_b.s_b1,
  5776. inaddrTemp.S_un.S_un_b.s_b2,
  5777. inaddrTemp.S_un.S_un_b.s_b3,
  5778. inaddrTemp.S_un.S_un_b.s_b4);
  5779. //
  5780. // Here is what we're going to do in this function:
  5781. //
  5782. // IHNetCfgMgr::QueryInterface for IHNetFirewallSettings
  5783. // IHNetFirewallSettings::EnumFirewalledConnections
  5784. // IHNetFirewalledConnection::QueryInterface for IHNetConnection
  5785. // get the IHNetConnection's HNET_CONN_PROPERTIES
  5786. // if HNET_CONN_PROPERTIES.fLanConnection
  5787. // IHNetConnection::GetGuid()
  5788. // if GUID matches IPHLPAPI GUID
  5789. // We've got the one we want, we're done
  5790. // else
  5791. // Keep looping
  5792. // else
  5793. // IHNetConnection::GetRasPhonebookPath and IHNetConnection::GetName to pass into RasGetEntryHrasconnW as pszPhonebook and pszEntry, respectively
  5794. // if got HRASCONN
  5795. // RasGetProjectionInfo
  5796. // if IP matches the IP we're looking for
  5797. // We've got the one we want, we're done
  5798. // else
  5799. // Keep looping
  5800. // else
  5801. // RAS entry is not dialed, keep looping
  5802. // if didn't find object
  5803. // it's not firewalled
  5804. //
  5805. //
  5806. // Get the firewall settings object.
  5807. //
  5808. hr = pHNetCfgMgr->QueryInterface(IID_IHNetFirewallSettings,
  5809. (PVOID*) (&pHNetFirewallSettings));
  5810. if (hr != S_OK)
  5811. {
  5812. DPFX(DPFPREP, 0, "Couldn't query for IHNetFirewallSettings interface (err = 0x%lx)!",
  5813. hr);
  5814. goto Failure;
  5815. }
  5816. //
  5817. // The HNetxxx objects appear to not be proxied...
  5818. //
  5819. //SETDEFAULTPROXYBLANKET(pHNetFirewallSettings);
  5820. //
  5821. // Get the firewalled connections enumeration via IHNetFirewallSettings.
  5822. //
  5823. hr = pHNetFirewallSettings->EnumFirewalledConnections(&pEnumHNetFirewalledConnections);
  5824. if (hr != S_OK)
  5825. {
  5826. DPFX(DPFPREP, 0, "Couldn't query for IHNetFirewallSettings interface (err = 0x%lx)!",
  5827. hr);
  5828. //
  5829. // Make sure we don't try to release a bogus pointer in case it got
  5830. // set.
  5831. //
  5832. pEnumHNetFirewalledConnections = NULL;
  5833. goto Failure;
  5834. }
  5835. //
  5836. // The HNetxxx objects appear to not be proxied...
  5837. //
  5838. //SETDEFAULTPROXYBLANKET(pEnumHNetFirewalledConnections);
  5839. //
  5840. // Don't need the IHNetFirewallSettings interface anymore.
  5841. //
  5842. pHNetFirewallSettings->Release();
  5843. pHNetFirewallSettings = NULL;
  5844. //
  5845. // Keep looping until we find the item or run out of items.
  5846. //
  5847. do
  5848. {
  5849. hr = pEnumHNetFirewalledConnections->Next(1,
  5850. &pHNetFirewalledConnection,
  5851. &ulNumFound);
  5852. if (FAILED(hr))
  5853. {
  5854. DPFX(DPFPREP, 0, "Couldn't get next connection (err = 0x%lx)!",
  5855. hr);
  5856. goto Failure;
  5857. }
  5858. //
  5859. // If there aren't any more items, bail.
  5860. //
  5861. if (ulNumFound == 0)
  5862. {
  5863. //
  5864. // pEnumHNetFirewalledConnections->Next might have returned
  5865. // S_FALSE.
  5866. //
  5867. hr = DPNH_OK;
  5868. break;
  5869. }
  5870. //
  5871. // The HNetxxx objects appear to not be proxied...
  5872. //
  5873. //SETDEFAULTPROXYBLANKET(pHNetFirewalledConnection);
  5874. //
  5875. // Get the IHNetConnection interface.
  5876. //
  5877. hr = pHNetFirewalledConnection->QueryInterface(IID_IHNetConnection,
  5878. (PVOID*) (&pHNetConnection));
  5879. if (hr != S_OK)
  5880. {
  5881. DPFX(DPFPREP, 0, "Couldn't query for IHNetConnection interface (err = 0x%lx)!",
  5882. hr);
  5883. goto Failure;
  5884. }
  5885. //
  5886. // The HNetxxx objects appear to not be proxied...
  5887. //
  5888. //SETDEFAULTPROXYBLANKET(pHNetConnection);
  5889. //
  5890. // We don't need the firewalled connection object anymore.
  5891. //
  5892. pHNetFirewalledConnection->Release();
  5893. pHNetFirewalledConnection = NULL;
  5894. //
  5895. // Get the internal properties for this adapter.
  5896. //
  5897. hr = pHNetConnection->GetProperties(&pHNetConnProperties);
  5898. if (hr != S_OK)
  5899. {
  5900. DPFX(DPFPREP, 0, "Couldn't get home net connection properties (err = 0x%lx)!",
  5901. hr);
  5902. goto Failure;
  5903. }
  5904. //
  5905. // Be somewhat picky about whether adapters returned by
  5906. // IEnumHNetFirewalledConnections actually be firewalled.
  5907. //
  5908. DNASSERTX(pHNetConnProperties->fFirewalled, 2);
  5909. fLanConnection = pHNetConnProperties->fLanConnection;
  5910. //
  5911. // Free the properties buffer.
  5912. //
  5913. CoTaskMemFree(pHNetConnProperties);
  5914. //pHNetConnProperties = NULL;
  5915. //
  5916. // Now if it's a LAN connection, see if the GUID matches the one
  5917. // returned by IPHLPAPI.
  5918. // If it's a RAS connection, see if this phonebook entry is dialed and
  5919. // has the right IP address.
  5920. //
  5921. if (fLanConnection)
  5922. {
  5923. //
  5924. // LAN case. If we haven't already retrieved the device's GUID, do
  5925. // so now.
  5926. //
  5927. if (! fHaveDeviceGUID)
  5928. {
  5929. hr = this->GetIPAddressGuidString(tszDeviceIPAddress, tszGuidDevice);
  5930. if (hr != S_OK)
  5931. {
  5932. DPFX(DPFPREP, 0, "Couldn't get device 0x%p's GUID (err = 0x%lx)!",
  5933. hr);
  5934. goto Failure;
  5935. }
  5936. fHaveDeviceGUID = TRUE;
  5937. }
  5938. //
  5939. // Get the HNetConnection object's GUID.
  5940. //
  5941. hr = pHNetConnection->GetGuid(&pguidHNetConnection);
  5942. if (hr != S_OK)
  5943. {
  5944. DPFX(DPFPREP, 0, "Couldn't get HNetConnection 0x%p's GUID (err = 0x%lx)!",
  5945. pHNetConnection, hr);
  5946. goto Failure;
  5947. }
  5948. //
  5949. // Convert the GUID into a string.
  5950. //
  5951. wsprintf(tszGuidHNetConnection,
  5952. _T("{%-08.8X-%-04.4X-%-04.4X-%02.2X%02.2X-%02.2X%02.2X%02.2X%02.2X%02.2X%02.2X}"),
  5953. pguidHNetConnection->Data1,
  5954. pguidHNetConnection->Data2,
  5955. pguidHNetConnection->Data3,
  5956. pguidHNetConnection->Data4[0],
  5957. pguidHNetConnection->Data4[1],
  5958. pguidHNetConnection->Data4[2],
  5959. pguidHNetConnection->Data4[3],
  5960. pguidHNetConnection->Data4[4],
  5961. pguidHNetConnection->Data4[5],
  5962. pguidHNetConnection->Data4[6],
  5963. pguidHNetConnection->Data4[7]);
  5964. CoTaskMemFree(pguidHNetConnection);
  5965. pguidHNetConnection = NULL;
  5966. #ifdef DBG
  5967. //
  5968. // Attempt to get the HNetConnection object's name for debugging
  5969. // purposes.
  5970. //
  5971. hr = pHNetConnection->GetName(&pwszName);
  5972. if (hr != S_OK)
  5973. {
  5974. DPFX(DPFPREP, 0, "Couldn't get HNetConnection 0x%p name (err = 0x%lx)!",
  5975. pHNetConnection, hr);
  5976. goto Failure;
  5977. }
  5978. #endif // DBG
  5979. //
  5980. // See if we found the object we need.
  5981. //
  5982. if (_tcsicmp(tszGuidHNetConnection, tszGuidDevice) == 0)
  5983. {
  5984. DPFX(DPFPREP, 7, "Matched IHNetConnection object 0x%p \"%ls\" to device 0x%p (LAN GUID %s).",
  5985. pHNetConnection, pwszName, pDevice, tszGuidHNetConnection);
  5986. //
  5987. // Transfer reference to caller.
  5988. //
  5989. (*ppHNetConnection) = pHNetConnection;
  5990. pHNetConnection = NULL;
  5991. #ifdef DBG
  5992. CoTaskMemFree(pwszName);
  5993. pwszName = NULL;
  5994. #endif // DBG
  5995. //
  5996. // We're done here.
  5997. //
  5998. hr = DPNH_OK;
  5999. goto Exit;
  6000. }
  6001. DPFX(DPFPREP, 7, "Non-matching IHNetConnection 0x%p \"%ls\"",
  6002. pHNetConnection, pwszName);
  6003. DPFX(DPFPREP, 7, "\t(LAN GUID %s <> %s).",
  6004. tszGuidHNetConnection, tszGuidDevice);
  6005. #ifdef DBG
  6006. CoTaskMemFree(pwszName);
  6007. pwszName = NULL;
  6008. #endif // DBG
  6009. }
  6010. else
  6011. {
  6012. //
  6013. // RAS case.
  6014. //
  6015. DNASSERT(this->m_hRasApi32DLL != NULL);
  6016. //
  6017. // Get the HNetConnection object's phonebook path.
  6018. //
  6019. hr = pHNetConnection->GetRasPhonebookPath(&pwszPhonebookPath);
  6020. if (hr != S_OK)
  6021. {
  6022. DPFX(DPFPREP, 0, "Couldn't get HNetConnection's RAS phonebook path (err = 0x%lx)!",
  6023. hr);
  6024. goto Failure;
  6025. }
  6026. //
  6027. // Get the HNetConnection object's name.
  6028. //
  6029. hr = pHNetConnection->GetName(&pwszName);
  6030. if (hr != S_OK)
  6031. {
  6032. DPFX(DPFPREP, 0, "Couldn't get HNetConnection's name (err = 0x%lx)!",
  6033. hr);
  6034. goto Failure;
  6035. }
  6036. //
  6037. // Look for an active RAS connection from that phonebook with that
  6038. // name.
  6039. //
  6040. dwError = this->m_pfnRasGetEntryHrasconnW(pwszPhonebookPath, pwszName, &hrasconn);
  6041. if (dwError != 0)
  6042. {
  6043. //
  6044. // It's probably ERROR_NO_CONNECTION (668).
  6045. //
  6046. DPFX(DPFPREP, 1, "Couldn't get entry's active RAS connection (err = %u), assuming not dialed",
  6047. dwError);
  6048. DPFX(DPFPREP, 1, "\tname \"%ls\", phonebook \"%ls\".",
  6049. pwszName, pwszPhonebookPath);
  6050. }
  6051. else
  6052. {
  6053. //
  6054. // Get the IP address.
  6055. //
  6056. ZeroMemory(&raspppip, sizeof(raspppip));
  6057. raspppip.dwSize = sizeof(raspppip);
  6058. dwSize = sizeof(raspppip);
  6059. dwError = this->m_pfnRasGetProjectionInfo(hrasconn, RASP_PppIp, &raspppip, &dwSize);
  6060. if (dwError != 0)
  6061. {
  6062. DPFX(DPFPREP, 0, "Couldn't get RAS connection's IP information (err = %u)!",
  6063. dwError);
  6064. hr = DPNHERR_GENERIC;
  6065. goto Failure;
  6066. }
  6067. //
  6068. // See if we found the object we need.
  6069. //
  6070. if (_tcsicmp(raspppip.szIpAddress, tszDeviceIPAddress) == 0)
  6071. {
  6072. DPFX(DPFPREP, 7, "Matched IHNetConnection object 0x%p to device 0x%p (RAS IP %s)",
  6073. pHNetConnection, pDevice, raspppip.szIpAddress);
  6074. DPFX(DPFPREP, 7, "\tname \"%ls\", phonebook \"%ls\".",
  6075. pwszName, pwszPhonebookPath);
  6076. //
  6077. // Transfer reference to caller.
  6078. //
  6079. (*ppHNetConnection) = pHNetConnection;
  6080. pHNetConnection = NULL;
  6081. //
  6082. // We're done here.
  6083. //
  6084. hr = DPNH_OK;
  6085. goto Exit;
  6086. }
  6087. DPFX(DPFPREP, 7, "Non-matching IHNetConnection 0x%p (RAS IP %s != %s)",
  6088. pHNetConnection, raspppip.szIpAddress, tszDeviceIPAddress);
  6089. DPFX(DPFPREP, 7, "\tname \"%ls\", phonebook \"%ls\").",
  6090. pwszName, pwszPhonebookPath);
  6091. }
  6092. CoTaskMemFree(pwszPhonebookPath);
  6093. pwszPhonebookPath = NULL;
  6094. CoTaskMemFree(pwszName);
  6095. pwszName = NULL;
  6096. }
  6097. //
  6098. // If we're here, we pHNetConnection is not the one we're looking for.
  6099. //
  6100. pHNetConnection->Release();
  6101. pHNetConnection = NULL;
  6102. }
  6103. while (TRUE);
  6104. //
  6105. // If we're here, then we didn't find a matching firewall connection.
  6106. //
  6107. DPFX(DPFPREP, 3, "Didn't find device 0x%p in list of firewalled connections.",
  6108. pDevice);
  6109. hr = DPNHERR_GENERIC;
  6110. Exit:
  6111. if (pEnumHNetFirewalledConnections != NULL)
  6112. {
  6113. pEnumHNetFirewalledConnections->Release();
  6114. pEnumHNetFirewalledConnections = NULL;
  6115. }
  6116. DPFX(DPFPREP, 6, "(0x%p) Returning: [0x%lx]", this, hr);
  6117. return hr;
  6118. Failure:
  6119. if (pwszName != NULL)
  6120. {
  6121. CoTaskMemFree(pwszName);
  6122. pwszName = NULL;
  6123. }
  6124. if (pwszPhonebookPath == NULL)
  6125. {
  6126. CoTaskMemFree(pwszPhonebookPath);
  6127. pwszPhonebookPath = NULL;
  6128. }
  6129. if (pHNetConnection != NULL)
  6130. {
  6131. pHNetConnection->Release();
  6132. pHNetConnection = NULL;
  6133. }
  6134. if (pHNetFirewalledConnection != NULL)
  6135. {
  6136. pHNetFirewalledConnection->Release();
  6137. pHNetFirewalledConnection = NULL;
  6138. }
  6139. if (pHNetFirewallSettings != NULL)
  6140. {
  6141. pHNetFirewallSettings->Release();
  6142. pHNetFirewallSettings = NULL;
  6143. }
  6144. goto Exit;
  6145. } // CNATHelpUPnP::GetIHNetConnectionForDeviceIfFirewalled
  6146. #undef DPF_MODNAME
  6147. #define DPF_MODNAME "CNATHelpUPnP::GetIPAddressGuidString"
  6148. //=============================================================================
  6149. // CNATHelpUPnP::GetIPAddressGuidString
  6150. //-----------------------------------------------------------------------------
  6151. //
  6152. // Description: Retrieves the IPHLPAPI assigned GUID (in string format) for
  6153. // the given IP address string. ptszGuidString must be able to
  6154. // hold GUID_STRING_LENGTH + 1 characters.
  6155. //
  6156. // The object lock is assumed to be held.
  6157. //
  6158. // Arguments:
  6159. // TCHAR * tszDeviceIPAddress - IP address string to lookup.
  6160. // TCHAR * ptszGuidString - Place to store device's GUID string.
  6161. //
  6162. // Returns: HRESULT
  6163. // DPNH_OK - Interface retrieved successfully.
  6164. // DPNHERR_GENERIC - An error occurred.
  6165. //=============================================================================
  6166. HRESULT CNATHelpUPnP::GetIPAddressGuidString(const TCHAR * const tszDeviceIPAddress,
  6167. TCHAR * const ptszGuidString)
  6168. {
  6169. HRESULT hr = DPNH_OK;
  6170. DWORD dwError;
  6171. PIP_ADAPTER_INFO pAdaptersBuffer = NULL;
  6172. ULONG ulSize;
  6173. PIP_ADAPTER_INFO pAdapterInfo;
  6174. PIP_ADDR_STRING pIPAddrString;
  6175. char * pszAdapterGuid = NULL;
  6176. #ifdef UNICODE
  6177. char szDeviceIPAddress[16]; // "nnn.nnn.nnn.nnn" + NULL termination
  6178. #endif // UNICODE
  6179. #ifdef DBG
  6180. char szIPList[256];
  6181. char * pszCurrentIP;
  6182. #endif // DBG
  6183. DPFX(DPFPREP, 6, "(0x%p) Parameters: (\"%s\", 0x%p)",
  6184. this, tszDeviceIPAddress, ptszGuidString);
  6185. DNASSERT(this->m_hIpHlpApiDLL != NULL);
  6186. //
  6187. // Keep trying to get the list of adapters until we get ERROR_SUCCESS or a
  6188. // legitimate error (other than ERROR_BUFFER_OVERFLOW or
  6189. // ERROR_INSUFFICIENT_BUFFER).
  6190. //
  6191. ulSize = 0;
  6192. do
  6193. {
  6194. dwError = this->m_pfnGetAdaptersInfo(pAdaptersBuffer, &ulSize);
  6195. if (dwError == ERROR_SUCCESS)
  6196. {
  6197. //
  6198. // We succeeded, we should be set. But make sure there are
  6199. // adapters for us to use.
  6200. //
  6201. if (ulSize < sizeof(IP_ADAPTER_INFO))
  6202. {
  6203. DPFX(DPFPREP, 0, "Getting adapters info succeeded but didn't return any valid adapters (%u < %u)!",
  6204. ulSize, sizeof(IP_ADAPTER_INFO));
  6205. hr = DPNHERR_GENERIC;
  6206. goto Failure;
  6207. }
  6208. break;
  6209. }
  6210. if ((dwError != ERROR_BUFFER_OVERFLOW) &&
  6211. (dwError != ERROR_INSUFFICIENT_BUFFER))
  6212. {
  6213. DPFX(DPFPREP, 0, "Unable to get adapters info (error = 0x%lx)!",
  6214. dwError);
  6215. hr = DPNHERR_GENERIC;
  6216. goto Failure;
  6217. }
  6218. //
  6219. // We need more adapter space. Make sure there are adapters for us to
  6220. // use.
  6221. //
  6222. if (ulSize < sizeof(IP_ADAPTER_INFO))
  6223. {
  6224. DPFX(DPFPREP, 0, "Getting adapters info didn't return any valid adapters (%u < %u)!",
  6225. ulSize, sizeof(IP_ADAPTER_INFO));
  6226. hr = DPNHERR_GENERIC;
  6227. goto Failure;
  6228. }
  6229. //
  6230. // If we previously had a buffer, free it.
  6231. //
  6232. if (pAdaptersBuffer != NULL)
  6233. {
  6234. DNFree(pAdaptersBuffer);
  6235. }
  6236. //
  6237. // Allocate the buffer.
  6238. //
  6239. pAdaptersBuffer = (PIP_ADAPTER_INFO) DNMalloc(ulSize);
  6240. if (pAdaptersBuffer == NULL)
  6241. {
  6242. DPFX(DPFPREP, 0, "Unable to allocate memory for adapters info!");
  6243. hr = DPNHERR_OUTOFMEMORY;
  6244. goto Failure;
  6245. }
  6246. }
  6247. while (TRUE);
  6248. #ifdef UNICODE
  6249. STR_jkWideToAnsi(szDeviceIPAddress,
  6250. tszDeviceIPAddress,
  6251. 16);
  6252. szDeviceIPAddress[15] = 0; // ensure it's NULL terminated
  6253. #endif // UNICODE
  6254. //
  6255. // Now find the device in the adapter list returned. Loop through all
  6256. // adapters.
  6257. //
  6258. pAdapterInfo = pAdaptersBuffer;
  6259. while (pAdapterInfo != NULL)
  6260. {
  6261. #ifdef DBG
  6262. //
  6263. // Initialize IP address list string.
  6264. //
  6265. szIPList[0] = '\0';
  6266. pszCurrentIP = szIPList;
  6267. #endif // DBG
  6268. //
  6269. // Loop through all addresses for this adapter looking for the one for
  6270. // the device we have bound.
  6271. //
  6272. pIPAddrString = &pAdapterInfo->IpAddressList;
  6273. while (pIPAddrString != NULL)
  6274. {
  6275. #ifdef DBG
  6276. int iStrLen;
  6277. //
  6278. // Copy the IP address string (if there's enough room), then tack
  6279. // on a space and NULL terminator.
  6280. //
  6281. iStrLen = strlen(pIPAddrString->IpAddress.String);
  6282. if ((pszCurrentIP + iStrLen + 2) < (szIPList + sizeof(szIPList)))
  6283. {
  6284. memcpy(pszCurrentIP, pIPAddrString->IpAddress.String, iStrLen);
  6285. pszCurrentIP += iStrLen;
  6286. (*pszCurrentIP) = ' ';
  6287. pszCurrentIP++;
  6288. (*pszCurrentIP) = '\0';
  6289. pszCurrentIP++;
  6290. }
  6291. #endif // DBG
  6292. #ifdef UNICODE
  6293. if (strcmp(pIPAddrString->IpAddress.String, szDeviceIPAddress) == 0)
  6294. #else // ! UNICODE
  6295. if (strcmp(pIPAddrString->IpAddress.String, tszDeviceIPAddress) == 0)
  6296. #endif // ! UNICODE
  6297. {
  6298. DPFX(DPFPREP, 8, "Found %s under adapter index %u (\"%hs\").",
  6299. tszDeviceIPAddress, pAdapterInfo->Index, pAdapterInfo->Description);
  6300. DNASSERT(pszAdapterGuid == NULL);
  6301. pszAdapterGuid = pAdapterInfo->AdapterName;
  6302. //
  6303. // Drop out of the loop in retail, keep going in debug.
  6304. //
  6305. #ifndef DBG
  6306. break;
  6307. #endif // ! DBG
  6308. }
  6309. pIPAddrString = pIPAddrString->Next;
  6310. }
  6311. //
  6312. // Drop out of the loop in retail, print this entry and keep going in
  6313. // debug.
  6314. //
  6315. #ifdef DBG
  6316. DPFX(DPFPREP, 7, "Adapter index %u IPs = %hs, %hs, \"%hs\".",
  6317. pAdapterInfo->Index,
  6318. szIPList,
  6319. pAdapterInfo->AdapterName,
  6320. pAdapterInfo->Description);
  6321. #else // ! DBG
  6322. if (pszAdapterGuid != NULL)
  6323. {
  6324. break;
  6325. }
  6326. #endif // ! DBG
  6327. pAdapterInfo = pAdapterInfo->Next;
  6328. }
  6329. //
  6330. // pszAdapterGuid will be NULL if we never found the device.
  6331. //
  6332. if (pszAdapterGuid == NULL)
  6333. {
  6334. DPFX(DPFPREP, 0, "Did not find adapter with matching address for address %s!",
  6335. tszDeviceIPAddress);
  6336. hr = DPNHERR_GENERIC;
  6337. goto Failure;
  6338. }
  6339. //
  6340. // Copy the adapter GUID string to the buffer supplied.
  6341. //
  6342. #ifdef UNICODE
  6343. STR_jkAnsiToWide(ptszGuidString,
  6344. pszAdapterGuid,
  6345. (GUID_STRING_LENGTH + 1));
  6346. #else // ! UNICODE
  6347. strncpy(ptszGuidString, pszAdapterGuid, (GUID_STRING_LENGTH + 1));
  6348. #endif // ! UNICODE
  6349. ptszGuidString[GUID_STRING_LENGTH] = 0; // ensure it's NULL terminated
  6350. Exit:
  6351. if (pAdaptersBuffer != NULL)
  6352. {
  6353. DNFree(pAdaptersBuffer);
  6354. pAdaptersBuffer = NULL;
  6355. }
  6356. DPFX(DPFPREP, 6, "(0x%p) Returning: [0x%lx]", this, hr);
  6357. return hr;
  6358. Failure:
  6359. goto Exit;
  6360. } // CNATHelpUPnP::GetIPAddressGuidString
  6361. #undef DPF_MODNAME
  6362. #define DPF_MODNAME "CNATHelpUPnP::OpenDevicesUPnPDiscoveryPort"
  6363. //=============================================================================
  6364. // CNATHelpUPnP::OpenDevicesUPnPDiscoveryPort
  6365. //-----------------------------------------------------------------------------
  6366. //
  6367. // Description: Maps the UPnP discovery socket's port if a firewall is
  6368. // found.
  6369. //
  6370. // COM is assumed to have been initialized.
  6371. //
  6372. // The object lock is assumed to be held.
  6373. //
  6374. // Arguments:
  6375. // CDevice * pDevice - Pointer to device whose port should
  6376. // be opened.
  6377. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr interface to
  6378. // use.
  6379. // IHNetConnection * pHNetConnection - Pointer to IHNetConnection interface
  6380. // for the given device.
  6381. //
  6382. // Returns: HRESULT
  6383. // DPNH_OK - Mapping completed successfully. There may or may not
  6384. // be a firewall.
  6385. // DPNHERR_GENERIC - An error occurred.
  6386. //=============================================================================
  6387. HRESULT CNATHelpUPnP::OpenDevicesUPnPDiscoveryPort(CDevice * const pDevice,
  6388. IHNetCfgMgr * const pHNetCfgMgr,
  6389. IHNetConnection * const pHNetConnection)
  6390. {
  6391. HRESULT hr = DPNH_OK;
  6392. CRegisteredPort * pRegisteredPort = NULL;
  6393. SOCKADDR_IN saddrinTemp;
  6394. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p)",
  6395. this, pDevice, pHNetCfgMgr, pHNetConnection);
  6396. DNASSERT(pDevice->IsHNetFirewalled());
  6397. //
  6398. // Create a fake UDP registered port to map.
  6399. //
  6400. pRegisteredPort = new CRegisteredPort(0, 0);
  6401. if (pRegisteredPort == NULL)
  6402. {
  6403. hr = DPNHERR_OUTOFMEMORY;
  6404. goto Failure;
  6405. }
  6406. pRegisteredPort->MakeDeviceOwner(pDevice);
  6407. ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
  6408. saddrinTemp.sin_family = AF_INET;
  6409. saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  6410. saddrinTemp.sin_port = pDevice->GetUPnPDiscoverySocketPort();
  6411. DNASSERT(saddrinTemp.sin_port != 0);
  6412. hr = pRegisteredPort->SetPrivateAddresses(&saddrinTemp, 1);
  6413. if (hr != DPNH_OK)
  6414. {
  6415. DPFX(DPFPREP, 0, "Couldn't set registered port 0x%p's private addresses (err = 0x%lx)!",
  6416. pRegisteredPort, hr);
  6417. goto Failure;
  6418. }
  6419. //
  6420. // Map the port.
  6421. //
  6422. hr = this->MapPortOnLocalHNetFirewall(pRegisteredPort,
  6423. pHNetCfgMgr,
  6424. pHNetConnection,
  6425. FALSE);
  6426. if (hr != DPNH_OK)
  6427. {
  6428. DPFX(DPFPREP, 0, "Couldn't map UPnP discovery socket port (temp regport = 0x%p) on device 0x%p's initial firewall (err = 0x%lx)!",
  6429. pRegisteredPort, pDevice, hr);
  6430. goto Failure;
  6431. }
  6432. //
  6433. // If the port was unavailable, we have to give up on supporting the
  6434. // scenario (firewall enabled behind a NAT). Otherwise, remember the fact
  6435. // that we mapped the port, and then delete the registered port object. We
  6436. // will unmap it when we shut down the device.
  6437. //
  6438. if (! pRegisteredPort->IsHNetFirewallPortUnavailable())
  6439. {
  6440. DPFX(DPFPREP, 3, "Mapped UPnP discovery socket for device 0x%p on firewall (removing temp regport 0x%p).",
  6441. pDevice, pRegisteredPort);
  6442. pDevice->NoteUPnPDiscoverySocketMappedOnHNetFirewall();
  6443. //
  6444. // Clear this to prevent an assert.
  6445. //
  6446. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  6447. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  6448. }
  6449. else
  6450. {
  6451. DPFX(DPFPREP, 1, "Could not map UPnP discovery socket on firewall for device 0x%p, unable to support an upstream NAT.",
  6452. pDevice);
  6453. }
  6454. pRegisteredPort->ClearPrivateAddresses();
  6455. pRegisteredPort->ClearDeviceOwner();
  6456. //
  6457. // Delete the item.
  6458. //
  6459. delete pRegisteredPort;
  6460. pRegisteredPort = NULL;
  6461. Exit:
  6462. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  6463. return hr;
  6464. Failure:
  6465. if (pRegisteredPort != NULL)
  6466. {
  6467. //
  6468. // Clear any settings that might cause an assert.
  6469. //
  6470. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  6471. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  6472. pRegisteredPort->ClearPrivateAddresses();
  6473. pRegisteredPort->ClearDeviceOwner();
  6474. //
  6475. // Delete the item.
  6476. //
  6477. delete pRegisteredPort;
  6478. pRegisteredPort = NULL;
  6479. }
  6480. goto Exit;
  6481. } // CNATHelpUPnP::OpenDevicesUPnPDiscoveryPort
  6482. #undef DPF_MODNAME
  6483. #define DPF_MODNAME "CNATHelpUPnP::CloseDevicesUPnPDiscoveryPort"
  6484. //=============================================================================
  6485. // CNATHelpUPnP::CloseDevicesUPnPDiscoveryPort
  6486. //-----------------------------------------------------------------------------
  6487. //
  6488. // Description: Unmaps the UPnP discovery socket's port from the firewall.
  6489. // pHNetCfgMgr can be NULL if it has not previously been obtained.
  6490. //
  6491. // COM is assumed to have been initialized if pHNetCfgMgr is
  6492. // not NULL.
  6493. //
  6494. // The object lock is assumed to be held.
  6495. //
  6496. // Arguments:
  6497. // CDevice * pDevice - Pointer to device whose port should be
  6498. // close.
  6499. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr interface to use, or
  6500. // NULL if not previously obtained.
  6501. //
  6502. // Returns: HRESULT
  6503. // DPNH_OK - Unmapping completed successfully.
  6504. // DPNHERR_GENERIC - An error occurred.
  6505. //=============================================================================
  6506. HRESULT CNATHelpUPnP::CloseDevicesUPnPDiscoveryPort(CDevice * const pDevice,
  6507. IHNetCfgMgr * const pHNetCfgMgr)
  6508. {
  6509. HRESULT hr = DPNH_OK;
  6510. CRegisteredPort * pRegisteredPort = NULL;
  6511. SOCKADDR_IN saddrinTemp;
  6512. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  6513. this, pDevice, pHNetCfgMgr);
  6514. //
  6515. // Create a fake UDP registered port to unmap.
  6516. //
  6517. pRegisteredPort = new CRegisteredPort(0, 0);
  6518. if (pRegisteredPort == NULL)
  6519. {
  6520. hr = DPNHERR_OUTOFMEMORY;
  6521. goto Failure;
  6522. }
  6523. pRegisteredPort->MakeDeviceOwner(pDevice);
  6524. pRegisteredPort->NoteMappedOnHNetFirewall();
  6525. ZeroMemory(&saddrinTemp, sizeof(saddrinTemp));
  6526. saddrinTemp.sin_family = AF_INET;
  6527. saddrinTemp.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  6528. saddrinTemp.sin_port = pDevice->GetUPnPDiscoverySocketPort();
  6529. DNASSERT(saddrinTemp.sin_port != 0);
  6530. hr = pRegisteredPort->SetPrivateAddresses(&saddrinTemp, 1);
  6531. if (hr != DPNH_OK)
  6532. {
  6533. DPFX(DPFPREP, 0, "Couldn't set registered port 0x%p's private addresses (err = 0x%lx)!",
  6534. pRegisteredPort, hr);
  6535. goto Failure;
  6536. }
  6537. //
  6538. // Unmap the port using the internal method if possible.
  6539. //
  6540. if (pHNetCfgMgr != NULL)
  6541. {
  6542. hr = this->UnmapPortOnLocalHNetFirewallInternal(pRegisteredPort, TRUE, pHNetCfgMgr);
  6543. }
  6544. else
  6545. {
  6546. //
  6547. // Don't alert the user since he/she doesn't know about this port.
  6548. //
  6549. hr = this->UnmapPortOnLocalHNetFirewall(pRegisteredPort, TRUE, FALSE);
  6550. }
  6551. if (hr != DPNH_OK)
  6552. {
  6553. DPFX(DPFPREP, 0, "Couldn't unmap UPnP discovery socket port (temp regport = 0x%p) on device 0x%p's firewall (err = 0x%lx)!",
  6554. pRegisteredPort, pDevice, hr);
  6555. goto Failure;
  6556. }
  6557. //
  6558. // Destroy the registered port object (note that the port mapping still
  6559. // exists). We will unmap when we shut down the device.
  6560. //
  6561. pRegisteredPort->ClearPrivateAddresses();
  6562. pRegisteredPort->ClearDeviceOwner();
  6563. //
  6564. // Delete the item.
  6565. //
  6566. delete pRegisteredPort;
  6567. pRegisteredPort = NULL;
  6568. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  6569. Exit:
  6570. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  6571. return hr;
  6572. Failure:
  6573. if (pRegisteredPort != NULL)
  6574. {
  6575. //
  6576. // Clear any settings that might cause an assert.
  6577. //
  6578. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  6579. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  6580. pRegisteredPort->ClearPrivateAddresses();
  6581. pRegisteredPort->ClearDeviceOwner();
  6582. //
  6583. // Delete the item.
  6584. //
  6585. delete pRegisteredPort;
  6586. pRegisteredPort = NULL;
  6587. }
  6588. goto Exit;
  6589. } // CNATHelpUPnP::CloseDevicesUPnPDiscoveryPort
  6590. #undef DPF_MODNAME
  6591. #define DPF_MODNAME "CNATHelpUPnP::MapUnmappedPortsOnLocalHNetFirewall"
  6592. //=============================================================================
  6593. // CNATHelpUPnP::MapUnmappedPortsOnLocalHNetFirewall
  6594. //-----------------------------------------------------------------------------
  6595. //
  6596. // Description: Maps any ports associated with the given device that have
  6597. // not been mapped with the local firewall yet.
  6598. //
  6599. // COM is assumed to have been initialized.
  6600. //
  6601. // The object lock is assumed to be held.
  6602. //
  6603. // Arguments:
  6604. // CDevice * pDevice - Device with (new) firewall.
  6605. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr
  6606. // interface to use.
  6607. // IHNetConnection * pHNetConnection - Pointer to IHNetConnection
  6608. // interface for the given
  6609. // device.
  6610. // CRegisteredPort * pDontAlertRegisteredPort - Pointer to registered port
  6611. // that should not trigger an
  6612. // address update alert, or
  6613. // NULL.
  6614. //
  6615. // Returns: HRESULT
  6616. // DPNH_OK - Mapping completed successfully. Note that the ports
  6617. // may be marked as unavailable.
  6618. // DPNHERR_GENERIC - An error occurred.
  6619. //=============================================================================
  6620. HRESULT CNATHelpUPnP::MapUnmappedPortsOnLocalHNetFirewall(CDevice * const pDevice,
  6621. IHNetCfgMgr * const pHNetCfgMgr,
  6622. IHNetConnection * const pHNetConnection,
  6623. CRegisteredPort * const pDontAlertRegisteredPort)
  6624. {
  6625. HRESULT hr = DPNH_OK;
  6626. CBilink * pBilink;
  6627. CRegisteredPort * pRegisteredPort;
  6628. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%p)",
  6629. this, pDevice, pHNetCfgMgr, pHNetConnection, pDontAlertRegisteredPort);
  6630. DNASSERT(pDevice->IsHNetFirewalled());
  6631. //
  6632. // Loop through all the registered ports associated with the device.
  6633. //
  6634. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  6635. while (pBilink != &pDevice->m_blOwnedRegPorts)
  6636. {
  6637. DNASSERT(! pBilink->IsEmpty());
  6638. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  6639. pBilink = pBilink->GetNext();
  6640. //
  6641. // If this port has already been mapped, we can skip it.
  6642. //
  6643. if (pRegisteredPort->IsMappedOnHNetFirewall())
  6644. {
  6645. DPFX(DPFPREP, 7, "Registered port 0x%p has already been mapped on the firewall for device 0x%p.",
  6646. pRegisteredPort, pDevice);
  6647. continue;
  6648. }
  6649. //
  6650. // If this port has already been determined to be unavailable, we can
  6651. // skip it.
  6652. //
  6653. if (pRegisteredPort->IsHNetFirewallPortUnavailable())
  6654. {
  6655. DPFX(DPFPREP, 7, "Registered port 0x%p has already been determined to be unavailable on the firewall for device 0x%p.",
  6656. pRegisteredPort, pDevice);
  6657. continue;
  6658. }
  6659. DPFX(DPFPREP, 3, "Mapping registered port 0x%p on firewall for device 0x%p.",
  6660. pRegisteredPort, pDevice);
  6661. hr = this->MapPortOnLocalHNetFirewall(pRegisteredPort,
  6662. pHNetCfgMgr,
  6663. pHNetConnection,
  6664. ((pRegisteredPort == pDontAlertRegisteredPort) ? FALSE : TRUE));
  6665. if (hr != DPNH_OK)
  6666. {
  6667. DPFX(DPFPREP, 7, "Failed mapping registered port 0x%p on firewall for device 0x%p.",
  6668. pRegisteredPort, pDevice);
  6669. goto Failure;
  6670. }
  6671. //
  6672. // Go to next registered port.
  6673. //
  6674. }
  6675. Exit:
  6676. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  6677. return hr;
  6678. Failure:
  6679. goto Exit;
  6680. } // CNATHelpUPnP::MapUnmappedPortsOnLocalHNetFirewall
  6681. #undef DPF_MODNAME
  6682. #define DPF_MODNAME "CNATHelpUPnP::MapPortOnLocalHNetFirewall"
  6683. //=============================================================================
  6684. // CNATHelpUPnP::MapPortOnLocalHNetFirewall
  6685. //-----------------------------------------------------------------------------
  6686. //
  6687. // Description: Maps the given port with the corresponding firewall.
  6688. //
  6689. // COM is assumed to have been initialized.
  6690. //
  6691. // The object lock is assumed to be held.
  6692. //
  6693. // Arguments:
  6694. // CRegisteredPort * pRegisteredPort - Port to map.
  6695. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr interface to
  6696. // use.
  6697. // IHNetConnection * pHNetConnection - Pointer to IHNetConnection interface
  6698. // for the given device.
  6699. // BOOL fNoteAddressChange - Whether to alert the user of the
  6700. // address change or not.
  6701. //
  6702. // Returns: HRESULT
  6703. // DPNH_OK - Mapping completed successfully. Note that the ports
  6704. // may be marked as unavailable.
  6705. // DPNHERR_GENERIC - An error occurred.
  6706. //=============================================================================
  6707. HRESULT CNATHelpUPnP::MapPortOnLocalHNetFirewall(CRegisteredPort * const pRegisteredPort,
  6708. IHNetCfgMgr * const pHNetCfgMgr,
  6709. IHNetConnection * const pHNetConnection,
  6710. const BOOL fNoteAddressChange)
  6711. {
  6712. HRESULT hr = DPNH_OK;
  6713. CDevice * pDevice;
  6714. SOCKADDR_IN * pasaddrinPrivate;
  6715. UCHAR ucProtocolToMatch;
  6716. ULONG ulNumFound;
  6717. BOOLEAN fTemp;
  6718. IHNetProtocolSettings * pHNetProtocolSettings = NULL;
  6719. IEnumHNetPortMappingProtocols * pEnumHNetPortMappingProtocols = NULL;
  6720. IHNetPortMappingProtocol ** papHNetPortMappingProtocol = NULL;
  6721. DWORD dwTemp;
  6722. BOOL fCreatedCurrentPortMappingProtocol = FALSE;
  6723. IHNetPortMappingBinding * pHNetPortMappingBinding = NULL;
  6724. DWORD dwTargetAddressV4;
  6725. WORD wPort;
  6726. UCHAR ucProtocol;
  6727. DWORD dwDescriptionLength;
  6728. TCHAR tszPort[32];
  6729. CRegistry RegObject;
  6730. WCHAR wszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  6731. DPNHACTIVEFIREWALLMAPPING dpnhafm;
  6732. BOOLEAN fBuiltIn = FALSE;
  6733. WCHAR * pwszPortMappingProtocolName = NULL;
  6734. #ifdef UNICODE
  6735. TCHAR * ptszDescription = wszDescription;
  6736. #else // ! UNICODE
  6737. char szDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  6738. TCHAR * ptszDescription = szDescription;
  6739. #endif // ! UNICODE
  6740. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, %i)",
  6741. this, pRegisteredPort, pHNetCfgMgr, pHNetConnection, fNoteAddressChange);
  6742. DNASSERT(! pRegisteredPort->IsMappedOnHNetFirewall());
  6743. DNASSERT(! pRegisteredPort->IsHNetFirewallPortUnavailable());
  6744. pDevice = pRegisteredPort->GetOwningDevice();
  6745. DNASSERT(pDevice != NULL);
  6746. DNASSERT(pDevice->IsHNetFirewalled());
  6747. //
  6748. // Get a protocol settings interface.
  6749. //
  6750. hr = pHNetCfgMgr->QueryInterface(IID_IHNetProtocolSettings,
  6751. (PVOID*) (&pHNetProtocolSettings));
  6752. if (hr != S_OK)
  6753. {
  6754. DPFX(DPFPREP, 0, "Couldn't get IHNetProtocolSettings interface from IHNetCfgMgr 0x%p (err = 0x%lx)!",
  6755. pHNetCfgMgr, hr);
  6756. goto Failure;
  6757. }
  6758. //
  6759. // The HNetxxx objects appear to not be proxied...
  6760. //
  6761. //SETDEFAULTPROXYBLANKET(pHNetProtocolSettings);
  6762. //
  6763. // Get ready to enumerate the existing mappings.
  6764. //
  6765. hr = pHNetProtocolSettings->EnumPortMappingProtocols(&pEnumHNetPortMappingProtocols);
  6766. if (hr != S_OK)
  6767. {
  6768. DPFX(DPFPREP, 0, "Couldn't enumerate port mapping protocols (err = 0x%lx)!",
  6769. hr);
  6770. goto Failure;
  6771. }
  6772. //
  6773. // The HNetxxx objects appear to not be proxied...
  6774. //
  6775. //SETDEFAULTPROXYBLANKET(pEnumHNetPortMappingProtocols);
  6776. //
  6777. // Allocate an array to keep track of previous ports in case of failure.
  6778. //
  6779. papHNetPortMappingProtocol = (IHNetPortMappingProtocol**) DNMalloc(DPNH_MAX_SIMULTANEOUS_PORTS * sizeof(IHNetPortMappingProtocol*));
  6780. if (papHNetPortMappingProtocol == NULL)
  6781. {
  6782. hr = DPNHERR_OUTOFMEMORY;
  6783. goto Failure;
  6784. }
  6785. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  6786. if (pRegisteredPort->IsTCP())
  6787. {
  6788. ucProtocolToMatch = PORTMAPPINGPROTOCOL_TCP;
  6789. }
  6790. else
  6791. {
  6792. ucProtocolToMatch = PORTMAPPINGPROTOCOL_UDP;
  6793. }
  6794. //
  6795. // Map each individual address associated with the port.
  6796. //
  6797. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  6798. {
  6799. DNASSERT(pasaddrinPrivate[dwTemp].sin_port != 0);
  6800. //
  6801. // Loop until we find a duplicate item or run out of items.
  6802. //
  6803. do
  6804. {
  6805. hr = pEnumHNetPortMappingProtocols->Next(1,
  6806. &papHNetPortMappingProtocol[dwTemp],
  6807. &ulNumFound);
  6808. if (FAILED(hr))
  6809. {
  6810. DPFX(DPFPREP, 0, "Couldn't get next port mapping protocol (err = 0x%lx)!",
  6811. hr);
  6812. goto Failure;
  6813. }
  6814. //
  6815. // If there aren't any more items, bail.
  6816. //
  6817. if (ulNumFound == 0)
  6818. {
  6819. //
  6820. // pEnumHNetPortMappingProtocols->Next might have returned
  6821. // S_FALSE.
  6822. //
  6823. hr = DPNH_OK;
  6824. break;
  6825. }
  6826. //
  6827. // The HNetxxx objects appear to not be proxied...
  6828. //
  6829. //SETDEFAULTPROXYBLANKET(papHNetPortMappingProtocol[dwTemp]);
  6830. //
  6831. // Get the port.
  6832. //
  6833. hr = papHNetPortMappingProtocol[dwTemp]->GetPort(&wPort);
  6834. if (hr != S_OK)
  6835. {
  6836. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol 0x%p's port (err = 0x%lx)!",
  6837. papHNetPortMappingProtocol[dwTemp], hr);
  6838. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetPort!"), 2);
  6839. goto Failure;
  6840. }
  6841. //
  6842. // Get the protocol.
  6843. //
  6844. hr = papHNetPortMappingProtocol[dwTemp]->GetIPProtocol(&ucProtocol);
  6845. if (hr != S_OK)
  6846. {
  6847. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol 0x%p's IP protocol (err = 0x%lx)!",
  6848. papHNetPortMappingProtocol[dwTemp], hr);
  6849. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetIPProtocol!"), 2);
  6850. goto Failure;
  6851. }
  6852. #ifdef DBG
  6853. hr = papHNetPortMappingProtocol[dwTemp]->GetName(&pwszPortMappingProtocolName);
  6854. if (hr == S_OK)
  6855. {
  6856. DPFX(DPFPREP, 7, "Found %s port mapping protocol 0x%p (\"%ls\") for port %u.",
  6857. (((wPort == pasaddrinPrivate[dwTemp].sin_port) && (ucProtocol == ucProtocolToMatch)) ? _T("matching") : _T("non-matching")),
  6858. papHNetPortMappingProtocol[dwTemp],
  6859. pwszPortMappingProtocolName,
  6860. NTOHS(wPort));
  6861. CoTaskMemFree(pwszPortMappingProtocolName);
  6862. pwszPortMappingProtocolName = NULL;
  6863. }
  6864. else
  6865. {
  6866. DPFX(DPFPREP, 7, "Found %s port mapping protocol 0x%p for port %u, (unable to retrieve name, err = %0lx).",
  6867. (((wPort == pasaddrinPrivate[dwTemp].sin_port) && (ucProtocol == ucProtocolToMatch)) ? _T("matching") : _T("non-matching")),
  6868. NTOHS(wPort),
  6869. papHNetPortMappingProtocol[dwTemp],
  6870. hr);
  6871. }
  6872. #endif // DBG
  6873. //
  6874. // See if we found the object we need.
  6875. //
  6876. if ((wPort == pasaddrinPrivate[dwTemp].sin_port) &&
  6877. (ucProtocol == ucProtocolToMatch))
  6878. {
  6879. break;
  6880. }
  6881. //
  6882. // Get ready for the next object.
  6883. //
  6884. papHNetPortMappingProtocol[dwTemp]->Release();
  6885. papHNetPortMappingProtocol[dwTemp] = NULL;
  6886. }
  6887. while (TRUE);
  6888. //
  6889. // Generate a description for this mapping. The format is:
  6890. //
  6891. // [executable_name] nnnnn {"TCP" | "UDP"}
  6892. //
  6893. // unless it's shared, in which case it's
  6894. //
  6895. // [executable_name] (255.255.255.255:nnnnn) nnnnn {"TCP" | "UDP"}
  6896. //
  6897. // That way nothing needs to be localized.
  6898. //
  6899. wsprintf(tszPort, _T("%u"),
  6900. NTOHS(pasaddrinPrivate[dwTemp].sin_port));
  6901. dwDescriptionLength = GetModuleFileName(NULL,
  6902. ptszDescription,
  6903. (MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1));
  6904. if (dwDescriptionLength != 0)
  6905. {
  6906. //
  6907. // Be paranoid and make sure the description string is valid.
  6908. //
  6909. ptszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1] = 0;
  6910. //
  6911. // Get just the executable name from the path.
  6912. //
  6913. #ifdef WINCE
  6914. GetExeName(ptszDescription);
  6915. #else // ! WINCE
  6916. #ifdef UNICODE
  6917. _wsplitpath(ptszDescription, NULL, NULL, ptszDescription, NULL);
  6918. #else // ! UNICODE
  6919. _splitpath(ptszDescription, NULL, NULL, ptszDescription, NULL);
  6920. #endif // ! UNICODE
  6921. #endif // ! WINCE
  6922. if (pRegisteredPort->IsSharedPort())
  6923. {
  6924. dwDescriptionLength = _tcslen(ptszDescription) // executable name
  6925. + strlen(" (255.255.255.255:") // " (255.255.255.255:"
  6926. + _tcslen(tszPort) // port
  6927. + strlen(") ") // ") "
  6928. + _tcslen(tszPort) // port
  6929. + 4; // " TCP" | " UDP"
  6930. }
  6931. else
  6932. {
  6933. dwDescriptionLength = _tcslen(ptszDescription) // executable name
  6934. + 1 // " "
  6935. +_tcslen(tszPort) // port
  6936. + 4; // " TCP" | " UDP"
  6937. }
  6938. //
  6939. // Make sure the long string will fit. If not, use the
  6940. // abbreviated version.
  6941. //
  6942. if (dwDescriptionLength > MAX_UPNP_MAPPING_DESCRIPTION_SIZE)
  6943. {
  6944. dwDescriptionLength = 0;
  6945. }
  6946. }
  6947. if (dwDescriptionLength == 0)
  6948. {
  6949. //
  6950. // Use the abbreviated version we know will fit.
  6951. //
  6952. if (pRegisteredPort->IsSharedPort())
  6953. {
  6954. wsprintf(ptszDescription,
  6955. _T("(255.255.255.255:%s) %s %s"),
  6956. tszPort,
  6957. tszPort,
  6958. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  6959. }
  6960. else
  6961. {
  6962. wsprintf(ptszDescription,
  6963. _T("%s %s"),
  6964. tszPort,
  6965. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  6966. }
  6967. }
  6968. else
  6969. {
  6970. //
  6971. // There's enough room, tack on the rest of the description.
  6972. //
  6973. if (pRegisteredPort->IsSharedPort())
  6974. {
  6975. wsprintf((ptszDescription + _tcslen(ptszDescription)),
  6976. _T(" (255.255.255.255:%s) %s %s"),
  6977. tszPort,
  6978. tszPort,
  6979. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  6980. }
  6981. else
  6982. {
  6983. wsprintf((ptszDescription + _tcslen(ptszDescription)),
  6984. _T(" %s %s"),
  6985. tszPort,
  6986. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  6987. }
  6988. }
  6989. #ifndef UNICODE
  6990. dwDescriptionLength = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  6991. hr = STR_AnsiToWide(szDescription, -1, wszDescription, &dwDescriptionLength);
  6992. if (hr != S_OK)
  6993. {
  6994. DPFX(DPFPREP, 0, "Couldn't convert NAT mapping description to Unicode (err = 0x%lx)!",
  6995. hr);
  6996. goto Failure;
  6997. }
  6998. #endif // ! UNICODE
  6999. //
  7000. // If there wasn't a port mapping already, create it. Otherwise make
  7001. // sure it's not already in use by some other client.
  7002. //
  7003. if (papHNetPortMappingProtocol[dwTemp] == NULL)
  7004. {
  7005. DPFX(DPFPREP, 7, "Creating new port mapping protocol \"%ls\".",
  7006. wszDescription);
  7007. //
  7008. // Create a new port mapping protocol.
  7009. //
  7010. DPFX(DPFPREP, 9, "++ pHNetProtocolSettings(0x%p)->CreatePortMappingProtocol(\"%ls\", %u, 0x%lx, 0x%p)", pHNetProtocolSettings, wszDescription, ucProtocolToMatch, pasaddrinPrivate[dwTemp].sin_port, &papHNetPortMappingProtocol[dwTemp]);
  7011. hr = pHNetProtocolSettings->CreatePortMappingProtocol(wszDescription,
  7012. ucProtocolToMatch,
  7013. pasaddrinPrivate[dwTemp].sin_port,
  7014. &papHNetPortMappingProtocol[dwTemp]);
  7015. DPFX(DPFPREP, 9, "-- pHNetProtocolSettings(0x%p)->CreatePortMappingProtocol = 0x%lx", pHNetProtocolSettings, hr);
  7016. if (hr != S_OK)
  7017. {
  7018. //
  7019. // This might be WBEM_E_ACCESSDENIED (0x80041003), which means
  7020. // the current user doesn't have permissions to open holes in
  7021. // the firewall.
  7022. //
  7023. DPFX(DPFPREP, 0, "Couldn't create new port mapping protocol (err = 0x%lx)!",
  7024. hr);
  7025. goto Failure;
  7026. }
  7027. //
  7028. // The HNetxxx objects appear to not be proxied...
  7029. //
  7030. //SETDEFAULTPROXYBLANKET(papHNetPortMappingProtocol[dwTemp]);
  7031. fCreatedCurrentPortMappingProtocol = TRUE;
  7032. //
  7033. // Retrieve its binding.
  7034. //
  7035. DPFX(DPFPREP, 9, "++ pHNetConnection(0x%p)->GetBindingForPortMappingProtocol(0x%p, 0x%p)", pHNetConnection, papHNetPortMappingProtocol[dwTemp], &pHNetPortMappingBinding);
  7036. hr = pHNetConnection->GetBindingForPortMappingProtocol(papHNetPortMappingProtocol[dwTemp],
  7037. &pHNetPortMappingBinding);
  7038. DPFX(DPFPREP, 9, "-- pHNetConnection(0x%p)->GetBindingForPortMappingProtocol = 0x%lx", pHNetConnection, hr);
  7039. if (hr != S_OK)
  7040. {
  7041. DPFX(DPFPREP, 0, "Couldn't get binding for port mapping protocol 0x%p (err = 0x%lx)!",
  7042. papHNetPortMappingProtocol[dwTemp], hr);
  7043. goto Failure;
  7044. }
  7045. //
  7046. // The HNetxxx objects appear to not be proxied...
  7047. //
  7048. //SETDEFAULTPROXYBLANKET(pHNetPortMappingBinding);
  7049. //
  7050. // Make sure it refers to the local device (or the broadcast
  7051. // address, if shared). Although shared ports are a strange
  7052. // concept on a firewall, Microsoft's firewall implementation
  7053. // shares mappings with the NAT, so we'd rather be safe than sorry.
  7054. // Mapping it to the broadcast address makes it behave the same if
  7055. // the firewalled adapter also happens to be shared.
  7056. //
  7057. if (pRegisteredPort->IsSharedPort())
  7058. {
  7059. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress((broadcast) 0x%lx)", pHNetPortMappingBinding, INADDR_BROADCAST);
  7060. hr = pHNetPortMappingBinding->SetTargetComputerAddress(INADDR_BROADCAST);
  7061. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress = 0x%lx", pHNetPortMappingBinding, hr);
  7062. }
  7063. else
  7064. {
  7065. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress(0x%lx)", pHNetPortMappingBinding, pDevice->GetLocalAddressV4());
  7066. hr = pHNetPortMappingBinding->SetTargetComputerAddress(pDevice->GetLocalAddressV4());
  7067. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress = 0x%lx", pHNetPortMappingBinding, hr);
  7068. }
  7069. if (hr != S_OK)
  7070. {
  7071. DPFX(DPFPREP, 0, "Couldn't set binding 0x%p's target computer address (err = 0x%lx)!",
  7072. pHNetPortMappingBinding, hr);
  7073. goto Failure;
  7074. }
  7075. }
  7076. else
  7077. {
  7078. //
  7079. // Retrieve the existing binding.
  7080. //
  7081. DPFX(DPFPREP, 9, "++ pHNetConnection(0x%p)->GetBindingForPortMappingProtocol(0x%p, 0x%p)", pHNetConnection, papHNetPortMappingProtocol[dwTemp], &pHNetPortMappingBinding);
  7082. hr = pHNetConnection->GetBindingForPortMappingProtocol(papHNetPortMappingProtocol[dwTemp],
  7083. &pHNetPortMappingBinding);
  7084. DPFX(DPFPREP, 9, "-- pHNetConnection(0x%p)->GetBindingForPortMappingProtocol = 0x%lx", pHNetConnection, hr);
  7085. if (hr != S_OK)
  7086. {
  7087. DPFX(DPFPREP, 0, "Couldn't get binding for port mapping protocol 0x%p (err = 0x%lx)!",
  7088. papHNetPortMappingProtocol[dwTemp], hr);
  7089. goto Failure;
  7090. }
  7091. //
  7092. // The HNetxxx objects appear to not be proxied...
  7093. //
  7094. //SETDEFAULTPROXYBLANKET(pHNetPortMappingBinding);
  7095. //
  7096. // Find out where this mapping goes.
  7097. //
  7098. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->GetTargetComputerAddress(0x%p)", pHNetPortMappingBinding, &dwTargetAddressV4);
  7099. hr = pHNetPortMappingBinding->GetTargetComputerAddress(&dwTargetAddressV4);
  7100. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->GetTargetComputerAddress = 0x%lx", pHNetPortMappingBinding, hr);
  7101. if (hr != S_OK)
  7102. {
  7103. DPFX(DPFPREP, 0, "Couldn't get binding 0x%p's target computer address (err = 0x%lx)!",
  7104. pHNetPortMappingBinding, hr);
  7105. goto Failure;
  7106. }
  7107. //
  7108. // If it's not for the local device, we may have to leave it alone.
  7109. //
  7110. if ((dwTargetAddressV4 != pDevice->GetLocalAddressV4()) &&
  7111. ((! pRegisteredPort->IsSharedPort()) ||
  7112. (dwTargetAddressV4 != INADDR_BROADCAST)))
  7113. {
  7114. //
  7115. // Find out if it's turned on.
  7116. //
  7117. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->GetEnabled(0x%p)", pHNetPortMappingBinding, &fTemp);
  7118. hr = pHNetPortMappingBinding->GetEnabled(&fTemp);
  7119. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->GetEnabled = 0x%lx", pHNetPortMappingBinding, hr);
  7120. if (hr != S_OK)
  7121. {
  7122. DPFX(DPFPREP, 0, "Couldn't get binding 0x%p's target computer address (err = 0x%lx)!",
  7123. pHNetPortMappingBinding, hr);
  7124. goto Failure;
  7125. }
  7126. //
  7127. // If it's currently active, it's better to be safe than sorry.
  7128. // Don't attempt to replace it.
  7129. //
  7130. if (fTemp)
  7131. {
  7132. DPFX(DPFPREP, 1, "Existing active binding points to different target %u.%u.%u.%u, can't reuse for device 0x%p.",
  7133. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b1,
  7134. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b2,
  7135. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b3,
  7136. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b4,
  7137. pDevice);
  7138. //
  7139. // Mark this port as unavailable.
  7140. //
  7141. pRegisteredPort->NoteHNetFirewallPortUnavailable();
  7142. //
  7143. // Cleanup this port mapping.
  7144. //
  7145. pHNetPortMappingBinding->Release();
  7146. pHNetPortMappingBinding = NULL;
  7147. papHNetPortMappingProtocol[dwTemp]->Release();
  7148. papHNetPortMappingProtocol[dwTemp] = NULL;
  7149. //
  7150. // Reset for next port.
  7151. //
  7152. DPFX(DPFPREP, 9, "++ pEnumHNetPortMappingProtocols(0x%p)->Reset()", pEnumHNetPortMappingProtocols);
  7153. hr = pEnumHNetPortMappingProtocols->Reset();
  7154. DPFX(DPFPREP, 9, "-- pEnumHNetPortMappingProtocols(0x%p)->Reset = 0x%lx", pEnumHNetPortMappingProtocols, hr);
  7155. if (hr != S_OK)
  7156. {
  7157. DPFX(DPFPREP, 0, "Couldn't reset port mapping protocol enumeration 0x%p (err = 0x%lx)!",
  7158. pEnumHNetPortMappingProtocols, hr);
  7159. goto Failure;
  7160. }
  7161. //
  7162. // Get out of the loop.
  7163. //
  7164. break;
  7165. }
  7166. //
  7167. // It's inactive.
  7168. //
  7169. DPFX(DPFPREP, 7, "Modifying inactive port mapping protocol (target was %u.%u.%u.%u) for device 0x%p (new name = \"%ls\").",
  7170. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b1,
  7171. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b2,
  7172. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b3,
  7173. ((IN_ADDR*) (&dwTargetAddressV4))->S_un.S_un_b.s_b4,
  7174. pDevice,
  7175. wszDescription);
  7176. }
  7177. else
  7178. {
  7179. //
  7180. // It matches the local device, or we're mapping a shared port
  7181. // and the mapping pointed to the broadcast address.
  7182. // Assume it's okay to replace.
  7183. //
  7184. DPFX(DPFPREP, 7, "Modifying existing port mapping protocol (device = 0x%p, new name = \"%ls\" unless built-in).",
  7185. pDevice,
  7186. wszDescription);
  7187. }
  7188. //
  7189. // Otherwise, it's safe to change it.
  7190. //
  7191. //
  7192. // Make sure it refers to the local device (or the broadcast
  7193. // address, if shared). Although shared ports are a strange
  7194. // concept on a firewall, Microsoft's firewall implementation
  7195. // shares mappings with the NAT, so we'd rather be safe than sorry.
  7196. // Mapping it to the broadcast address makes it behave the same if
  7197. // the firewalled adapter also happens to be shared.
  7198. //
  7199. if (pRegisteredPort->IsSharedPort())
  7200. {
  7201. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress((broadcast) 0x%lx)", pHNetPortMappingBinding, INADDR_BROADCAST);
  7202. hr = pHNetPortMappingBinding->SetTargetComputerAddress(INADDR_BROADCAST);
  7203. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress = 0x%lx", pHNetPortMappingBinding, hr);
  7204. }
  7205. else
  7206. {
  7207. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress(0x%lx)", pHNetPortMappingBinding, pDevice->GetLocalAddressV4());
  7208. hr = pHNetPortMappingBinding->SetTargetComputerAddress(pDevice->GetLocalAddressV4());
  7209. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->SetTargetComputerAddress = 0x%lx", pHNetPortMappingBinding, hr);
  7210. }
  7211. if (hr != S_OK)
  7212. {
  7213. DPFX(DPFPREP, 0, "Couldn't set binding 0x%p's target computer address (err = 0x%lx)!",
  7214. pHNetPortMappingBinding, hr);
  7215. goto Failure;
  7216. }
  7217. //
  7218. // See if this protocol is built-in.
  7219. //
  7220. DPFX(DPFPREP, 9, "++ papHNetPortMappingProtocol[%u](0x%p)->GetBuiltIn(0x%p)", dwTemp, papHNetPortMappingProtocol[dwTemp], &fBuiltIn);
  7221. hr = papHNetPortMappingProtocol[dwTemp]->GetBuiltIn(&fBuiltIn);
  7222. DPFX(DPFPREP, 9, "-- papHNetPortMappingProtocol[%u](0x%p)->GetBuiltIn = 0x%lx", dwTemp, papHNetPortMappingProtocol[dwTemp], hr);
  7223. if (hr != S_OK)
  7224. {
  7225. DPFX(DPFPREP, 0, "Couldn't get protocol 0x%p's built-in status (err = 0x%lx)!",
  7226. papHNetPortMappingProtocol[dwTemp], hr);
  7227. goto Failure;
  7228. }
  7229. //
  7230. // If it's not built-in, we can change the name.
  7231. //
  7232. if (! fBuiltIn)
  7233. {
  7234. //
  7235. // Update the description.
  7236. //
  7237. DPFX(DPFPREP, 9, "++ papHNetPortMappingProtocol[%u](0x%p)->SetName(\"%ls\")", dwTemp, papHNetPortMappingProtocol[dwTemp], wszDescription);
  7238. hr = papHNetPortMappingProtocol[dwTemp]->SetName(wszDescription);
  7239. DPFX(DPFPREP, 9, "-- papHNetPortMappingProtocol[%u](0x%p)->SetName = 0x%lx", dwTemp, papHNetPortMappingProtocol[dwTemp], hr);
  7240. if (hr != S_OK)
  7241. {
  7242. //
  7243. // This might be WBEM_E_ACCESSDENIED (0x80041003), which
  7244. // means the current user doesn't truly have permissions to
  7245. // open holes in the firewall (even though the
  7246. // SetTargetComputerAddress call above succeeded).
  7247. //
  7248. DPFX(DPFPREP, 0, "Couldn't rename existing port mapping protocol 0x%p (err = 0x%lx)!",
  7249. papHNetPortMappingProtocol[dwTemp], hr);
  7250. goto Failure;
  7251. }
  7252. }
  7253. else
  7254. {
  7255. pRegisteredPort->NoteHNetFirewallMappingBuiltIn();
  7256. DPFX(DPFPREP, 9, "++ papHNetPortMappingProtocol[%u](0x%p)->GetName(0x%p)", dwTemp, papHNetPortMappingProtocol[dwTemp], &pwszPortMappingProtocolName);
  7257. hr = papHNetPortMappingProtocol[dwTemp]->GetName(&pwszPortMappingProtocolName);
  7258. DPFX(DPFPREP, 9, "-- papHNetPortMappingProtocol[%u](0x%p)->GetName = 0x%lx", dwTemp, papHNetPortMappingProtocol[dwTemp], hr);
  7259. if (hr != S_OK)
  7260. {
  7261. DPFX(DPFPREP, 0, "Couldn't get built-in port mapping protocol 0x%p's name (err = 0x%lx)!",
  7262. papHNetPortMappingProtocol[dwTemp], hr);
  7263. goto Failure;
  7264. }
  7265. DPFX(DPFPREP, 1, "Re-using built in port mapping protocol \"%ls\" (can't rename to \"%ls\").",
  7266. pwszPortMappingProtocolName, wszDescription);
  7267. }
  7268. } // end else (found port mapping protocol)
  7269. //
  7270. // Enable the binding.
  7271. //
  7272. DPFX(DPFPREP, 9, "++ pHNetPortMappingBinding(0x%p)->SetEnabled(TRUE)", pHNetPortMappingBinding);
  7273. hr = pHNetPortMappingBinding->SetEnabled(TRUE);
  7274. DPFX(DPFPREP, 9, "-- pHNetPortMappingBinding(0x%p)->SetEnabled = 0x%lx", pHNetPortMappingBinding, hr);
  7275. if (hr != S_OK)
  7276. {
  7277. //
  7278. // This might be WBEM_E_ACCESSDENIED (0x80041003), which means the
  7279. // current user doesn't truly have permissions to open holes in the
  7280. // firewall (even though the SetTargetComputerAddress call above
  7281. // succeeded).
  7282. //
  7283. DPFX(DPFPREP, 0, "Couldn't enable binding 0x%p (err = 0x%lx)!",
  7284. pHNetPortMappingBinding, hr);
  7285. goto Failure;
  7286. }
  7287. //
  7288. // Remember this firewall mapping, in case we crash before cleaning it
  7289. // up in this session. That we can clean it up next time we launch.
  7290. // Don't do this if the port is shared, since we can't tell when it's
  7291. // no longer in use.
  7292. //
  7293. if (! pRegisteredPort->IsSharedPort())
  7294. {
  7295. if (fBuiltIn)
  7296. {
  7297. DPFX(DPFPREP, 7, "Remembering built-in firewall mapping \"%ls\" (a.k.a. \"%ls\") in case of crash.",
  7298. pwszPortMappingProtocolName, wszDescription);
  7299. }
  7300. else
  7301. {
  7302. DPFX(DPFPREP, 7, "Remembering regular firewall mapping \"%ls\" in case of crash.",
  7303. wszDescription);
  7304. }
  7305. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  7306. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVEFIREWALLMAPPINGS,
  7307. FALSE,
  7308. TRUE,
  7309. TRUE,
  7310. DPN_KEY_ALL_ACCESS))
  7311. {
  7312. DPFX(DPFPREP, 0, "Couldn't open active firewall mapping key, unable to save in case of crash!");
  7313. }
  7314. else
  7315. {
  7316. DNASSERT(this->m_dwInstanceKey != 0);
  7317. ZeroMemory(&dpnhafm, sizeof(dpnhafm));
  7318. dpnhafm.dwVersion = ACTIVE_MAPPING_VERSION;
  7319. dpnhafm.dwInstanceKey = this->m_dwInstanceKey;
  7320. dpnhafm.dwFlags = pRegisteredPort->GetFlags();
  7321. dpnhafm.dwAddressV4 = pDevice->GetLocalAddressV4();
  7322. dpnhafm.wPort = pasaddrinPrivate[dwTemp].sin_port;
  7323. //
  7324. // If it's built-in, use its existing name since it couldn't be
  7325. // renamed. This allows the unmapping code to find it in the
  7326. // registry again. See UnmapPortOnLocalHNetFirewallInternal.
  7327. //
  7328. RegObject.WriteBlob(((fBuiltIn) ? pwszPortMappingProtocolName : wszDescription),
  7329. (LPBYTE) (&dpnhafm),
  7330. sizeof(dpnhafm));
  7331. RegObject.Close();
  7332. }
  7333. }
  7334. else
  7335. {
  7336. DPFX(DPFPREP, 7, "Not remembering shared port firewall mapping \"%ls\".",
  7337. wszDescription);
  7338. }
  7339. //
  7340. // Cleanup from this port mapping, and get ready for the next one.
  7341. //
  7342. if (fBuiltIn)
  7343. {
  7344. CoTaskMemFree(pwszPortMappingProtocolName);
  7345. pwszPortMappingProtocolName = NULL;
  7346. }
  7347. pHNetPortMappingBinding->Release();
  7348. pHNetPortMappingBinding = NULL;
  7349. fCreatedCurrentPortMappingProtocol = FALSE;
  7350. hr = pEnumHNetPortMappingProtocols->Reset();
  7351. if (hr != S_OK)
  7352. {
  7353. DPFX(DPFPREP, 0, "Couldn't reset port mapping protocol enumeration 0x%p (err = 0x%lx)!",
  7354. pEnumHNetPortMappingProtocols, hr);
  7355. goto Failure;
  7356. }
  7357. //
  7358. // Alert the user to the change the next time GetCaps is called, if
  7359. // requested.
  7360. //
  7361. if (fNoteAddressChange)
  7362. {
  7363. DPFX(DPFPREP, 8, "Noting that addresses changed (for registered port 0x%p).",
  7364. pRegisteredPort);
  7365. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  7366. }
  7367. //
  7368. // Go on to the next port.
  7369. //
  7370. }
  7371. //
  7372. // dwTemp == pRegisteredPort->GetNumAddresses() if everything succeeded, or
  7373. // or the index of the item that was unavailable if not.
  7374. //
  7375. //
  7376. // Free all the port mapping protocol objects. If we successfully bound
  7377. // all of them, that's all we need to do. If the port was unavailable, we
  7378. // have to unmap any ports that were successful up to the one that failed.
  7379. //
  7380. while (dwTemp > 0)
  7381. {
  7382. dwTemp--;
  7383. //
  7384. // If we failed to map all ports, delete this previous mapping.
  7385. //
  7386. if (pRegisteredPort->IsHNetFirewallPortUnavailable())
  7387. {
  7388. papHNetPortMappingProtocol[dwTemp]->Delete(); // ignore error
  7389. }
  7390. //
  7391. // Free the object.
  7392. //
  7393. papHNetPortMappingProtocol[dwTemp]->Release();
  7394. papHNetPortMappingProtocol[dwTemp] = NULL;
  7395. }
  7396. //
  7397. // If we succeeded, mark the registered port as mapped.
  7398. //
  7399. if (! pRegisteredPort->IsHNetFirewallPortUnavailable())
  7400. {
  7401. pRegisteredPort->NoteMappedOnHNetFirewall();
  7402. }
  7403. DNFree(papHNetPortMappingProtocol);
  7404. papHNetPortMappingProtocol = NULL;
  7405. DNASSERT(hr == DPNH_OK);
  7406. Exit:
  7407. if (pEnumHNetPortMappingProtocols != NULL)
  7408. {
  7409. pEnumHNetPortMappingProtocols->Release();
  7410. pEnumHNetPortMappingProtocols = NULL;
  7411. }
  7412. if (pHNetProtocolSettings != NULL)
  7413. {
  7414. pHNetProtocolSettings->Release();
  7415. pHNetProtocolSettings = NULL;
  7416. }
  7417. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  7418. return hr;
  7419. Failure:
  7420. if (pwszPortMappingProtocolName != NULL)
  7421. {
  7422. CoTaskMemFree(pwszPortMappingProtocolName);
  7423. pwszPortMappingProtocolName = NULL;
  7424. }
  7425. //
  7426. // If we have an array, then we need to clean it up. dwTemp will still
  7427. // hold the index of the item we were working on.
  7428. //
  7429. if (papHNetPortMappingProtocol != NULL)
  7430. {
  7431. //
  7432. // Delete the one we were working on, if we created it.
  7433. //
  7434. if (papHNetPortMappingProtocol[dwTemp] != NULL)
  7435. {
  7436. if (fCreatedCurrentPortMappingProtocol)
  7437. {
  7438. papHNetPortMappingProtocol[dwTemp]->Delete(); // ignore error
  7439. }
  7440. papHNetPortMappingProtocol[dwTemp]->Release();
  7441. papHNetPortMappingProtocol[dwTemp] = NULL;
  7442. }
  7443. //
  7444. // Delete all the mappings we successfully made up to the last one.
  7445. //
  7446. while (dwTemp > 0)
  7447. {
  7448. dwTemp--;
  7449. DNASSERT(papHNetPortMappingProtocol[dwTemp] != NULL);
  7450. papHNetPortMappingProtocol[dwTemp]->Delete(); // ignore error
  7451. papHNetPortMappingProtocol[dwTemp]->Release();
  7452. papHNetPortMappingProtocol[dwTemp] = NULL;
  7453. }
  7454. DNFree(papHNetPortMappingProtocol);
  7455. papHNetPortMappingProtocol = NULL;
  7456. }
  7457. if (pHNetPortMappingBinding != NULL)
  7458. {
  7459. pHNetPortMappingBinding->Release();
  7460. pHNetPortMappingBinding = NULL;
  7461. }
  7462. goto Exit;
  7463. } // CNATHelpUPnP::MapPortOnLocalHNetFirewall
  7464. #undef DPF_MODNAME
  7465. #define DPF_MODNAME "CNATHelpUPnP::UnmapPortOnLocalHNetFirewall"
  7466. //=============================================================================
  7467. // CNATHelpUPnP::UnmapPortOnLocalHNetFirewall
  7468. //-----------------------------------------------------------------------------
  7469. //
  7470. // Description: Removes the mappings for the given ports from the local
  7471. // firewall.
  7472. //
  7473. // The main object lock is assumed to be held. It will be
  7474. // converted into the long lock for the duration of this function.
  7475. //
  7476. // Arguments:
  7477. // CRegisteredPort * pRegisteredPort - Pointer to port to be opened on the
  7478. // firewall.
  7479. // BOOL fNeedToDeleteRegValue - Whether the corresponding crash
  7480. // recovery registry value needs to
  7481. // be deleted as well.
  7482. // BOOL fNoteAddressChange - Whether to alert the user of the
  7483. // address change or not.
  7484. //
  7485. // Returns: HRESULT
  7486. // DPNH_OK - Unmapping completed successfully.
  7487. // DPNHERR_GENERIC - An error occurred.
  7488. //=============================================================================
  7489. HRESULT CNATHelpUPnP::UnmapPortOnLocalHNetFirewall(CRegisteredPort * const pRegisteredPort,
  7490. const BOOL fNeedToDeleteRegValue,
  7491. const BOOL fNoteAddressChange)
  7492. {
  7493. HRESULT hr = DPNH_OK;
  7494. BOOL fSwitchedToLongLock = FALSE;
  7495. BOOL fUninitializeCOM = FALSE;
  7496. IHNetCfgMgr * pHNetCfgMgr = NULL;
  7497. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i, %i)",
  7498. this, pRegisteredPort, fNeedToDeleteRegValue, fNoteAddressChange);
  7499. DNASSERT(pRegisteredPort->IsMappedOnHNetFirewall());
  7500. //
  7501. // If the port is shared, leave it mapped since we can't tell when the
  7502. // last person using it is done with it.
  7503. //
  7504. if (pRegisteredPort->IsSharedPort())
  7505. {
  7506. DPFX(DPFPREP, 2, "Leaving shared registered port 0x%p mapped.",
  7507. pRegisteredPort);
  7508. //
  7509. // Pretend like we unmapped it, though.
  7510. //
  7511. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  7512. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  7513. goto Exit;
  7514. }
  7515. //
  7516. // Using the HomeNet API (particularly the out-of-proc COM calls) during
  7517. // stress is really, really, painfully slow. Since we have one global lock
  7518. // the controls everything, other threads may be sitting for an equally
  7519. // long time... so long, in fact, that the critical section timeout fires
  7520. // and we get a false stress hit. So we have a sneaky workaround to
  7521. // prevent that from happening while still maintaining ownership of the
  7522. // object.
  7523. //
  7524. this->SwitchToLongLock();
  7525. fSwitchedToLongLock = TRUE;
  7526. //
  7527. // Try to initialize COM if we weren't instantiated through COM. It may
  7528. // have already been initialized in a different mode, which is okay. As
  7529. // long as it has been initialized somehow, we're fine.
  7530. //
  7531. if (this->m_dwFlags & NATHELPUPNPOBJ_NOTCREATEDWITHCOM)
  7532. {
  7533. hr = CoInitializeEx(NULL, (COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE));
  7534. switch (hr)
  7535. {
  7536. case S_OK:
  7537. {
  7538. //
  7539. // Success, that's good. Cleanup when we're done.
  7540. //
  7541. DPFX(DPFPREP, 8, "Successfully initialized COM.");
  7542. fUninitializeCOM = TRUE;
  7543. break;
  7544. }
  7545. case S_FALSE:
  7546. {
  7547. //
  7548. // Someone else already initialized COM, but that's okay.
  7549. // Cleanup when we're done.
  7550. //
  7551. DPFX(DPFPREP, 8, "Initialized COM (again).");
  7552. fUninitializeCOM = TRUE;
  7553. break;
  7554. }
  7555. case RPC_E_CHANGED_MODE:
  7556. {
  7557. //
  7558. // Someone else already initialized COM in a different mode.
  7559. // It should be okay, but we don't have to balance the CoInit
  7560. // call with a CoUninit.
  7561. //
  7562. DPFX(DPFPREP, 8, "Didn't initialize COM, already initialized in a different mode.");
  7563. break;
  7564. }
  7565. default:
  7566. {
  7567. //
  7568. // Hmm, something else is going on. We can't handle that.
  7569. //
  7570. DPFX(DPFPREP, 0, "Initializing COM failed (err = 0x%lx)!", hr);
  7571. goto Failure;
  7572. break;
  7573. }
  7574. }
  7575. }
  7576. else
  7577. {
  7578. DPFX(DPFPREP, 8, "Object was instantiated through COM, no need to initialize COM.");
  7579. }
  7580. //
  7581. // Create the main HNet manager object.
  7582. //
  7583. hr = CoCreateInstance(CLSID_HNetCfgMgr, NULL, CLSCTX_INPROC_SERVER,
  7584. IID_IHNetCfgMgr, (PVOID*) (&pHNetCfgMgr));
  7585. if (hr != S_OK)
  7586. {
  7587. DPFX(DPFPREP, 1, "Couldn't create IHNetCfgMgr interface (err = 0x%lx)!",
  7588. hr);
  7589. goto Failure;
  7590. }
  7591. //
  7592. // We created the IHNetCfgMgr object as in-proc, so there's no proxy that
  7593. // requires security settings.
  7594. //
  7595. //SETDEFAULTPROXYBLANKET(pHNetCfgMgr);
  7596. //
  7597. // Actually unmap the port(s).
  7598. //
  7599. hr = this->UnmapPortOnLocalHNetFirewallInternal(pRegisteredPort,
  7600. fNeedToDeleteRegValue,
  7601. pHNetCfgMgr);
  7602. if (hr != DPNH_OK)
  7603. {
  7604. DPFX(DPFPREP, 0, "Couldn't unmap ports from local HNet firewall (err = 0x%lx)!",
  7605. hr);
  7606. goto Failure;
  7607. }
  7608. //
  7609. // Alert the user to the change the next time GetCaps is called, if requested.
  7610. //
  7611. if (fNoteAddressChange)
  7612. {
  7613. DPFX(DPFPREP, 8, "Noting that addresses changed (for registered port 0x%p).",
  7614. pRegisteredPort);
  7615. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  7616. }
  7617. Exit:
  7618. if (pHNetCfgMgr != NULL)
  7619. {
  7620. pHNetCfgMgr->Release();
  7621. pHNetCfgMgr = NULL;
  7622. }
  7623. if (fUninitializeCOM)
  7624. {
  7625. DPFX(DPFPREP, 8, "Uninitializing COM.");
  7626. CoUninitialize();
  7627. fUninitializeCOM = FALSE;
  7628. }
  7629. if (fSwitchedToLongLock)
  7630. {
  7631. this->SwitchFromLongLock();
  7632. fSwitchedToLongLock = FALSE;
  7633. }
  7634. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  7635. return hr;
  7636. Failure:
  7637. goto Exit;
  7638. } // CNATHelpUPnP::UnmapPortOnLocalHNetFirewall
  7639. #undef DPF_MODNAME
  7640. #define DPF_MODNAME "CNATHelpUPnP::UnmapPortOnLocalHNetFirewallInternal"
  7641. //=============================================================================
  7642. // CNATHelpUPnP::UnmapPortOnLocalHNetFirewallInternal
  7643. //-----------------------------------------------------------------------------
  7644. //
  7645. // Description: Removes the mappings for the given ports from the local
  7646. // firewall.
  7647. //
  7648. // COM is assumed to have been initialized.
  7649. //
  7650. // The object lock is assumed to be held.
  7651. //
  7652. // Arguments:
  7653. // CRegisteredPort * pRegisteredPort - Pointer to port to be opened on the
  7654. // firewall.
  7655. // BOOL fNeedToDeleteRegValue - Whether the corresponding crash
  7656. // recovery registry value needs to
  7657. // be deleted as well.
  7658. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr interface to
  7659. // use.
  7660. //
  7661. // Returns: HRESULT
  7662. // DPNH_OK - Unmapping completed successfully.
  7663. // DPNHERR_GENERIC - An error occurred.
  7664. //=============================================================================
  7665. HRESULT CNATHelpUPnP::UnmapPortOnLocalHNetFirewallInternal(CRegisteredPort * const pRegisteredPort,
  7666. const BOOL fNeedToDeleteRegValue,
  7667. IHNetCfgMgr * const pHNetCfgMgr)
  7668. {
  7669. HRESULT hr = DPNH_OK;
  7670. CDevice * pDevice;
  7671. DWORD dwAttempts = 0;
  7672. IHNetProtocolSettings * pHNetProtocolSettings = NULL;
  7673. IEnumHNetPortMappingProtocols * pEnumHNetPortMappingProtocols = NULL;
  7674. SOCKADDR_IN * pasaddrinPrivate;
  7675. UCHAR ucProtocolToMatch;
  7676. IHNetPortMappingProtocol * pHNetPortMappingProtocol = NULL;
  7677. DWORD dwStartingPort = 0;
  7678. DWORD dwTemp;
  7679. ULONG ulNumFound;
  7680. WORD wPort;
  7681. UCHAR ucProtocol;
  7682. WCHAR * pwszName = NULL;
  7683. BOOLEAN fBuiltIn;
  7684. CRegistry RegObject;
  7685. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i, 0x%p)",
  7686. this, pRegisteredPort, fNeedToDeleteRegValue, pHNetCfgMgr);
  7687. DNASSERT(pRegisteredPort->IsMappedOnHNetFirewall());
  7688. pDevice = pRegisteredPort->GetOwningDevice();
  7689. DNASSERT(pDevice != NULL);
  7690. DNASSERT(pDevice->IsHNetFirewalled());
  7691. DNASSERT(this->m_hIpHlpApiDLL != NULL);
  7692. Restart:
  7693. //
  7694. // Get a protocol settings interface.
  7695. //
  7696. hr = pHNetCfgMgr->QueryInterface(IID_IHNetProtocolSettings,
  7697. (PVOID*) (&pHNetProtocolSettings));
  7698. if (hr != S_OK)
  7699. {
  7700. DPFX(DPFPREP, 0, "Couldn't get IHNetProtocolSettings interface from IHNetCfgMgr 0x%p (err = 0x%lx)!",
  7701. pHNetCfgMgr, hr);
  7702. goto Failure;
  7703. }
  7704. //
  7705. // The HNetxxx objects appear to not be proxied...
  7706. //
  7707. //SETDEFAULTPROXYBLANKET(pHNetProtocolSettings);
  7708. //
  7709. // Get ready to enumerate the existing mappings.
  7710. //
  7711. hr = pHNetProtocolSettings->EnumPortMappingProtocols(&pEnumHNetPortMappingProtocols);
  7712. if (hr != S_OK)
  7713. {
  7714. DPFX(DPFPREP, 0, "Couldn't enumerate port mapping protocols (err = 0x%lx)!",
  7715. hr);
  7716. goto Failure;
  7717. }
  7718. //
  7719. // The HNetxxx objects appear to not be proxied...
  7720. //
  7721. //SETDEFAULTPROXYBLANKET(pEnumHNetPortMappingProtocols);
  7722. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  7723. if (pRegisteredPort->IsTCP())
  7724. {
  7725. ucProtocolToMatch = PORTMAPPINGPROTOCOL_TCP;
  7726. }
  7727. else
  7728. {
  7729. ucProtocolToMatch = PORTMAPPINGPROTOCOL_UDP;
  7730. }
  7731. //
  7732. // Loop through all the ports (that we haven't successfully unmapped yet).
  7733. //
  7734. for(dwTemp = dwStartingPort; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  7735. {
  7736. //
  7737. // Loop until we find a duplicate item or run out of items.
  7738. //
  7739. do
  7740. {
  7741. hr = pEnumHNetPortMappingProtocols->Next(1,
  7742. &pHNetPortMappingProtocol,
  7743. &ulNumFound);
  7744. if (FAILED(hr))
  7745. {
  7746. dwAttempts++;
  7747. if (dwAttempts < MAX_NUM_HOMENETUNMAP_ATTEMPTS)
  7748. {
  7749. DPFX(DPFPREP, 0, "Couldn't get next port mapping protocol (err = 0x%lx)! Trying again after %u ms.",
  7750. hr, (dwAttempts * HOMENETUNMAP_SLEEP_FACTOR));
  7751. //
  7752. // Dump the object pointers we currently have.
  7753. //
  7754. pEnumHNetPortMappingProtocols->Release();
  7755. pEnumHNetPortMappingProtocols = NULL;
  7756. pHNetProtocolSettings->Release();
  7757. pHNetProtocolSettings = NULL;
  7758. //
  7759. // Sleep, then go back to the top and try again.
  7760. //
  7761. Sleep(dwAttempts * HOMENETUNMAP_SLEEP_FACTOR);
  7762. goto Restart;
  7763. }
  7764. DPFX(DPFPREP, 0, "Couldn't get next port mapping protocol (err = 0x%lx)!",
  7765. hr);
  7766. goto Failure;
  7767. }
  7768. //
  7769. // If there aren't any more items, bail.
  7770. //
  7771. if (ulNumFound == 0)
  7772. {
  7773. //
  7774. // Be sure that IEnumHNetPortMappingProtocols::Next returned
  7775. // the right thing, for PREfix's sake.
  7776. //
  7777. if (pHNetPortMappingProtocol != NULL)
  7778. {
  7779. pHNetPortMappingProtocol->Release();
  7780. pHNetPortMappingProtocol = NULL;
  7781. }
  7782. //
  7783. // pEnumHNetPortMappingProtocols->Next might have returned
  7784. // S_FALSE.
  7785. //
  7786. hr = DPNH_OK;
  7787. break;
  7788. }
  7789. //
  7790. // The HNetxxx objects appear to not be proxied...
  7791. //
  7792. //SETDEFAULTPROXYBLANKET(pHNetPortMappingProtocol);
  7793. //
  7794. // Get the port.
  7795. //
  7796. hr = pHNetPortMappingProtocol->GetPort(&wPort);
  7797. if (hr != S_OK)
  7798. {
  7799. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetPort!"), 2);
  7800. //
  7801. // Dump the unusable mapping object.
  7802. //
  7803. pHNetPortMappingProtocol->Release();
  7804. pHNetPortMappingProtocol = NULL;
  7805. dwAttempts++;
  7806. if (dwAttempts < MAX_NUM_HOMENETUNMAP_ATTEMPTS)
  7807. {
  7808. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol port (err = 0x%lx)! Trying again after %u ms.",
  7809. hr, (dwAttempts * HOMENETUNMAP_SLEEP_FACTOR));
  7810. //
  7811. // Dump the object pointers we currently have.
  7812. //
  7813. pEnumHNetPortMappingProtocols->Release();
  7814. pEnumHNetPortMappingProtocols = NULL;
  7815. pHNetProtocolSettings->Release();
  7816. pHNetProtocolSettings = NULL;
  7817. //
  7818. // Sleep, then go back to the top and try again.
  7819. //
  7820. Sleep(dwAttempts * HOMENETUNMAP_SLEEP_FACTOR);
  7821. goto Restart;
  7822. }
  7823. //
  7824. // Break out of the search loop, but continue.
  7825. //
  7826. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol port (err = 0x%lx)!",
  7827. hr);
  7828. break;
  7829. }
  7830. //
  7831. // Get the protocol.
  7832. //
  7833. hr = pHNetPortMappingProtocol->GetIPProtocol(&ucProtocol);
  7834. if (hr != S_OK)
  7835. {
  7836. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetIPProtocol!"), 2);
  7837. //
  7838. // Dump the unusable mapping object.
  7839. //
  7840. pHNetPortMappingProtocol->Release();
  7841. pHNetPortMappingProtocol = NULL;
  7842. dwAttempts++;
  7843. if (dwAttempts < MAX_NUM_HOMENETUNMAP_ATTEMPTS)
  7844. {
  7845. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's IP protocol (err = 0x%lx)! Trying again after %u ms.",
  7846. hr, (dwAttempts * HOMENETUNMAP_SLEEP_FACTOR));
  7847. //
  7848. // Dump the object pointers we currently have.
  7849. //
  7850. pEnumHNetPortMappingProtocols->Release();
  7851. pEnumHNetPortMappingProtocols = NULL;
  7852. pHNetProtocolSettings->Release();
  7853. pHNetProtocolSettings = NULL;
  7854. //
  7855. // Sleep, then go back to the top and try again.
  7856. //
  7857. Sleep(dwAttempts * HOMENETUNMAP_SLEEP_FACTOR);
  7858. goto Restart;
  7859. }
  7860. //
  7861. // Break out of the search loop, but continue.
  7862. //
  7863. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's IP protocol (err = 0x%lx)!",
  7864. hr);
  7865. break;
  7866. }
  7867. //
  7868. // See if we found the object we need. Note that we don't verify
  7869. // the target address for simplicity (neither does UPnP).
  7870. //
  7871. if ((wPort == pasaddrinPrivate[dwTemp].sin_port) &&
  7872. (ucProtocol == ucProtocolToMatch))
  7873. {
  7874. //
  7875. // Retrieve the mapping name.
  7876. //
  7877. hr = pHNetPortMappingProtocol->GetName(&pwszName);
  7878. if (hr != S_OK)
  7879. {
  7880. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetName!"), 2);
  7881. //
  7882. // Dump the unusable mapping object.
  7883. //
  7884. pHNetPortMappingProtocol->Release();
  7885. pHNetPortMappingProtocol = NULL;
  7886. dwAttempts++;
  7887. if (dwAttempts < MAX_NUM_HOMENETUNMAP_ATTEMPTS)
  7888. {
  7889. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's name (err = 0x%lx)! Trying again after %u ms.",
  7890. hr, (dwAttempts * HOMENETUNMAP_SLEEP_FACTOR));
  7891. //
  7892. // Dump the object pointers we currently have.
  7893. //
  7894. pEnumHNetPortMappingProtocols->Release();
  7895. pEnumHNetPortMappingProtocols = NULL;
  7896. pHNetProtocolSettings->Release();
  7897. pHNetProtocolSettings = NULL;
  7898. //
  7899. // Sleep, then go back to the top and try again.
  7900. //
  7901. Sleep(dwAttempts * HOMENETUNMAP_SLEEP_FACTOR);
  7902. goto Restart;
  7903. }
  7904. //
  7905. // Break out of the search loop, but continue.
  7906. //
  7907. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's name (err = 0x%lx)!",
  7908. hr);
  7909. break;
  7910. }
  7911. DPFX(DPFPREP, 8, "Found port mapping protocol 0x%p (\"%ls\").",
  7912. pHNetPortMappingProtocol, pwszName);
  7913. //
  7914. // See if this protocol is built-in.
  7915. //
  7916. hr = pHNetPortMappingProtocol->GetBuiltIn(&fBuiltIn);
  7917. if (hr != S_OK)
  7918. {
  7919. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetBuiltIn!"), 2);
  7920. //
  7921. // Dump the unusable mapping object and its name.
  7922. //
  7923. pHNetPortMappingProtocol->Release();
  7924. pHNetPortMappingProtocol = NULL;
  7925. CoTaskMemFree(pwszName);
  7926. pwszName = NULL;
  7927. dwAttempts++;
  7928. if (dwAttempts < MAX_NUM_HOMENETUNMAP_ATTEMPTS)
  7929. {
  7930. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's built-in status (err = 0x%lx)! Trying again after %u ms.",
  7931. hr, (dwAttempts * HOMENETUNMAP_SLEEP_FACTOR));
  7932. //
  7933. // Dump the object pointers we currently have.
  7934. //
  7935. pEnumHNetPortMappingProtocols->Release();
  7936. pEnumHNetPortMappingProtocols = NULL;
  7937. pHNetProtocolSettings->Release();
  7938. pHNetProtocolSettings = NULL;
  7939. //
  7940. // Sleep, then go back to the top and try again.
  7941. //
  7942. Sleep(dwAttempts * HOMENETUNMAP_SLEEP_FACTOR);
  7943. goto Restart;
  7944. }
  7945. //
  7946. // Break out of the search loop, but continue.
  7947. //
  7948. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol's built-in status (err = 0x%lx)!",
  7949. hr);
  7950. break;
  7951. }
  7952. break;
  7953. }
  7954. #ifdef DBG
  7955. else
  7956. {
  7957. //
  7958. // Try to retrieve the mapping name for informational purposes.
  7959. //
  7960. hr = pHNetPortMappingProtocol->GetName(&pwszName);
  7961. if (hr != S_OK)
  7962. {
  7963. DPFX(DPFPREP, 0, "Couldn't get port mapping protocol 0x%p's name (err = 0x%lx)!",
  7964. pHNetPortMappingProtocol, hr);
  7965. DNASSERTX((! "Got unexpected error executing IHNetPortMappingProtocol::GetName!"), 2);
  7966. //
  7967. // Ignore error...
  7968. //
  7969. }
  7970. else
  7971. {
  7972. DPFX(DPFPREP, 7, "Skipping non-matching port mapping protocol 0x%p (\"%ls\").",
  7973. pHNetPortMappingProtocol, pwszName);
  7974. CoTaskMemFree(pwszName);
  7975. pwszName = NULL;
  7976. }
  7977. }
  7978. #endif // DBG
  7979. //
  7980. // Get ready for the next object.
  7981. //
  7982. pHNetPortMappingProtocol->Release();
  7983. pHNetPortMappingProtocol = NULL;
  7984. }
  7985. while (TRUE);
  7986. //
  7987. // Remove the mapping (if we found it).
  7988. //
  7989. if (pHNetPortMappingProtocol != NULL)
  7990. {
  7991. //
  7992. // If the mapping is built-in we can't delete it. Disabling it is
  7993. // the best we can do.
  7994. //
  7995. if (fBuiltIn)
  7996. {
  7997. DPFX(DPFPREP, 7, "Disabling built-in port mapping protocol \"%ls\".", pwszName);
  7998. DNASSERT(pRegisteredPort->IsHNetFirewallMappingBuiltIn());
  7999. hr = this->DisableAllBindingsForHNetPortMappingProtocol(pHNetPortMappingProtocol,
  8000. pHNetCfgMgr);
  8001. if (hr != DPNH_OK)
  8002. {
  8003. DPFX(DPFPREP, 0, "Couldn't disable all bindings for built-in port mapping protocol \"%ls\" (err = 0x%lx)!",
  8004. pwszName, hr);
  8005. goto Failure;
  8006. }
  8007. }
  8008. else
  8009. {
  8010. DPFX(DPFPREP, 7, "Deleting port mapping protocol \"%ls\".", pwszName);
  8011. DNASSERT(! pRegisteredPort->IsHNetFirewallMappingBuiltIn());
  8012. hr = pHNetPortMappingProtocol->Delete();
  8013. if (hr != S_OK)
  8014. {
  8015. //
  8016. // This might be WBEM_E_ACCESSDENIED (0x80041003), which
  8017. // means the current user doesn't have permissions to
  8018. // modify firewall mappings.
  8019. //
  8020. DPFX(DPFPREP, 0, "Couldn't delete port mapping protocol (err = 0x%lx)!",
  8021. hr);
  8022. goto Failure;
  8023. }
  8024. }
  8025. if (fNeedToDeleteRegValue)
  8026. {
  8027. //
  8028. // Delete the crash cleanup registry entry. The mapping
  8029. // description/name will match the registry key name even in
  8030. // the case of built-in mappings with names we didn't generate.
  8031. // See MapPortOnLocalHNetFirewall.
  8032. //
  8033. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  8034. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVEFIREWALLMAPPINGS,
  8035. FALSE,
  8036. TRUE,
  8037. TRUE,
  8038. DPN_KEY_ALL_ACCESS))
  8039. {
  8040. DPFX(DPFPREP, 0, "Couldn't open active firewall mapping key, unable to remove crash cleanup reference!");
  8041. }
  8042. else
  8043. {
  8044. BOOL fResult;
  8045. //
  8046. // Ignore error.
  8047. //
  8048. fResult = RegObject.DeleteValue(pwszName);
  8049. if (! fResult)
  8050. {
  8051. DPFX(DPFPREP, 0, "Couldn't delete firewall mapping value \"%ls\"! Continuing.",
  8052. pwszName);
  8053. }
  8054. RegObject.Close();
  8055. }
  8056. }
  8057. else
  8058. {
  8059. DPFX(DPFPREP, 6, "No need to delete firewall crash cleanup registry key \"%ls\".", pwszName);
  8060. }
  8061. //
  8062. // Cleanup pointers we accumulated.
  8063. //
  8064. CoTaskMemFree(pwszName);
  8065. pwszName = NULL;
  8066. pHNetPortMappingProtocol->Release();
  8067. pHNetPortMappingProtocol = NULL;
  8068. }
  8069. else
  8070. {
  8071. //
  8072. // We didn't find the mapping.
  8073. //
  8074. DPFX(DPFPREP, 0, "Didn't find port mapping protocol for port %u %s! Continuing.",
  8075. NTOHS(pasaddrinPrivate[dwTemp].sin_port),
  8076. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  8077. }
  8078. //
  8079. // Cleanup from this port mapping, and get ready for the next one.
  8080. //
  8081. hr = pEnumHNetPortMappingProtocols->Reset();
  8082. if (hr != S_OK)
  8083. {
  8084. DPFX(DPFPREP, 0, "Couldn't reset port mapping protocol enumeration 0x%p (err = 0x%lx)!",
  8085. pEnumHNetPortMappingProtocols, hr);
  8086. goto Failure;
  8087. }
  8088. //
  8089. // Go on to the next port, and update the starting counter in case we
  8090. // encounter a failure next time.
  8091. //
  8092. dwStartingPort++;
  8093. }
  8094. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  8095. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  8096. DNASSERT(hr == DPNH_OK);
  8097. Exit:
  8098. if (pHNetPortMappingProtocol != NULL)
  8099. {
  8100. pHNetPortMappingProtocol->Release();
  8101. pHNetPortMappingProtocol = NULL;
  8102. }
  8103. if (pEnumHNetPortMappingProtocols != NULL)
  8104. {
  8105. pEnumHNetPortMappingProtocols->Release();
  8106. pEnumHNetPortMappingProtocols = NULL;
  8107. }
  8108. if (pHNetProtocolSettings != NULL)
  8109. {
  8110. pHNetProtocolSettings->Release();
  8111. pHNetProtocolSettings = NULL;
  8112. }
  8113. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  8114. return hr;
  8115. Failure:
  8116. if (pwszName != NULL)
  8117. {
  8118. CoTaskMemFree(pwszName);
  8119. pwszName = NULL;
  8120. }
  8121. goto Exit;
  8122. } // CNATHelpUPnP::UnmapPortOnLocalHNetFirewallInternal
  8123. #undef DPF_MODNAME
  8124. #define DPF_MODNAME "CNATHelpUPnP::DisableAllBindingsForHNetPortMappingProtocol"
  8125. //=============================================================================
  8126. // CNATHelpUPnP::DisableAllBindingsForHNetPortMappingProtocol
  8127. //-----------------------------------------------------------------------------
  8128. //
  8129. // Description: Disables all HNetPortMappingBindings on all HNetConnection
  8130. // interfaces for the given port mapping protocol object.
  8131. //
  8132. // COM is assumed to have been initialized.
  8133. //
  8134. // The object lock is assumed to be held.
  8135. //
  8136. // Arguments:
  8137. // IHNetPortMappingProtocol * pHNetPortMappingProtocol - Pointer to port
  8138. // mapping
  8139. // protocol to
  8140. // disable on all
  8141. // connections.
  8142. // IHNetCfgMgr * pHNetCfgMgr - Pointer to
  8143. // IHNetCfgMgr
  8144. // interface to
  8145. // use.
  8146. //
  8147. // Returns: HRESULT
  8148. // DPNH_OK - Disabling was successful.
  8149. // DPNHERR_GENERIC - An error occurred.
  8150. //=============================================================================
  8151. HRESULT CNATHelpUPnP::DisableAllBindingsForHNetPortMappingProtocol(IHNetPortMappingProtocol * const pHNetPortMappingProtocol,
  8152. IHNetCfgMgr * const pHNetCfgMgr)
  8153. {
  8154. HRESULT hr;
  8155. INetConnectionManager * pNetConnectionManager = NULL;
  8156. IEnumNetConnection * pEnumNetConnections = NULL;
  8157. ULONG ulNumFound;
  8158. INetConnection * pNetConnection = NULL;
  8159. IHNetConnection * pHNetConnection = NULL;
  8160. IHNetPortMappingBinding * pHNetPortMappingBinding = NULL;
  8161. #ifdef DBG
  8162. WCHAR * pwszName = NULL;
  8163. #endif // DBG
  8164. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  8165. this, pHNetPortMappingProtocol, pHNetCfgMgr);
  8166. //
  8167. // Try creating the base connection object.
  8168. //
  8169. hr = CoCreateInstance(CLSID_ConnectionManager,
  8170. NULL,
  8171. CLSCTX_SERVER,
  8172. IID_INetConnectionManager,
  8173. (PVOID*) (&pNetConnectionManager));
  8174. if (hr != S_OK)
  8175. {
  8176. DPFX(DPFPREP, 0, "Couldn't create INetConnectionManager interface (err = 0x%lx)!",
  8177. hr);
  8178. goto Failure;
  8179. }
  8180. SETDEFAULTPROXYBLANKET(pNetConnectionManager);
  8181. DPFX(DPFPREP, 7, "Successfully created net connection manager object 0x%p.",
  8182. pNetConnectionManager);
  8183. //
  8184. // Get the net connection enumeration object.
  8185. //
  8186. hr = pNetConnectionManager->EnumConnections(NCME_DEFAULT, &pEnumNetConnections);
  8187. if (hr != S_OK)
  8188. {
  8189. DPFX(DPFPREP, 0, "Couldn't enum connections (err = 0x%lx)!",
  8190. hr);
  8191. goto Failure;
  8192. }
  8193. SETDEFAULTPROXYBLANKET(pEnumNetConnections);
  8194. //
  8195. // We don't need the base object anymore.
  8196. //
  8197. pNetConnectionManager->Release();
  8198. pNetConnectionManager = NULL;
  8199. //
  8200. // Keep looping until we find the item or run out of items.
  8201. //
  8202. do
  8203. {
  8204. hr = pEnumNetConnections->Next(1, &pNetConnection, &ulNumFound);
  8205. if (FAILED(hr))
  8206. {
  8207. DPFX(DPFPREP, 0, "Couldn't get next connection (err = 0x%lx)!",
  8208. hr);
  8209. goto Failure;
  8210. }
  8211. //
  8212. // If there aren't any more items, bail.
  8213. //
  8214. if (ulNumFound == 0)
  8215. {
  8216. //
  8217. // pEnumNetConnections->Next might have returned S_FALSE.
  8218. //
  8219. hr = DPNH_OK;
  8220. break;
  8221. }
  8222. SETDEFAULTPROXYBLANKET(pNetConnection);
  8223. //
  8224. // Get the HNetConnection object for this NetConnection.
  8225. //
  8226. hr = pHNetCfgMgr->GetIHNetConnectionForINetConnection(pNetConnection,
  8227. &pHNetConnection);
  8228. if (hr != S_OK)
  8229. {
  8230. DPFX(DPFPREP, 0, "Couldn't get IHNetConnection interface for INetConnection 0x%p (err = 0x%lx)!",
  8231. pNetConnection, hr);
  8232. goto Failure;
  8233. }
  8234. //
  8235. // The HNetxxx objects appear to not be proxied...
  8236. //
  8237. //SETDEFAULTPROXYBLANKET(pHNetConnection);
  8238. //
  8239. // Don't need the INetConnection interface anymore.
  8240. //
  8241. pNetConnection->Release();
  8242. pNetConnection = NULL;
  8243. #ifdef DBG
  8244. //
  8245. // Retrieve the connection name, for debug printing purposes.
  8246. //
  8247. hr = pHNetConnection->GetName(&pwszName);
  8248. if (hr != S_OK)
  8249. {
  8250. DPFX(DPFPREP, 0, "Couldn't get name of HNetConnection 0x%p (err = 0x%lx)!",
  8251. pHNetConnection, hr);
  8252. goto Failure;
  8253. }
  8254. #endif // DBG
  8255. //
  8256. // Retrieve the existing binding.
  8257. //
  8258. hr = pHNetConnection->GetBindingForPortMappingProtocol(pHNetPortMappingProtocol,
  8259. &pHNetPortMappingBinding);
  8260. if (hr != S_OK)
  8261. {
  8262. DPFX(DPFPREP, 0, "Couldn't get binding for port mapping protocol 0x%p (err = 0x%lx)!",
  8263. pHNetPortMappingProtocol, hr);
  8264. goto Failure;
  8265. }
  8266. //
  8267. // The HNetxxx objects appear to not be proxied...
  8268. //
  8269. //SETDEFAULTPROXYBLANKET(pHNetPortMappingBinding);
  8270. //
  8271. // Don't need the HomeNet Connection object anymore.
  8272. //
  8273. pHNetConnection->Release();
  8274. pHNetConnection = NULL;
  8275. DPFX(DPFPREP, 6, "Disabling binding 0x%p on connection \"%ls\".",
  8276. pHNetPortMappingBinding, pwszName);
  8277. //
  8278. // Disable it.
  8279. //
  8280. hr = pHNetPortMappingBinding->SetEnabled(FALSE);
  8281. if (hr != S_OK)
  8282. {
  8283. DPFX(DPFPREP, 0, "Couldn't disable port mapping binding 0x%p (err = 0x%lx)!",
  8284. pHNetPortMappingBinding, hr);
  8285. goto Failure;
  8286. }
  8287. pHNetPortMappingBinding->Release();
  8288. pHNetPortMappingBinding = NULL;
  8289. //
  8290. // Go to the next mapping.
  8291. //
  8292. #ifdef DBG
  8293. CoTaskMemFree(pwszName);
  8294. pwszName = NULL;
  8295. #endif // DBG
  8296. }
  8297. while (TRUE);
  8298. //
  8299. // If we're here, we made it through unscathed.
  8300. //
  8301. hr = DPNH_OK;
  8302. Exit:
  8303. if (pEnumNetConnections != NULL)
  8304. {
  8305. pEnumNetConnections->Release();
  8306. pEnumNetConnections = NULL;
  8307. }
  8308. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  8309. return hr;
  8310. Failure:
  8311. if (pHNetPortMappingBinding != NULL)
  8312. {
  8313. pHNetPortMappingBinding->Release();
  8314. pHNetPortMappingBinding = NULL;
  8315. }
  8316. #ifdef DBG
  8317. if (pwszName != NULL)
  8318. {
  8319. CoTaskMemFree(pwszName);
  8320. pwszName = NULL;
  8321. }
  8322. #endif // DBG
  8323. if (pHNetConnection != NULL)
  8324. {
  8325. pHNetConnection->Release();
  8326. pHNetConnection = NULL;
  8327. }
  8328. if (pNetConnection != NULL)
  8329. {
  8330. pNetConnection->Release();
  8331. pNetConnection = NULL;
  8332. }
  8333. if (pNetConnectionManager != NULL)
  8334. {
  8335. pNetConnectionManager->Release();
  8336. pNetConnectionManager = NULL;
  8337. }
  8338. goto Exit;
  8339. } // CNATHelpUPnP::DisableAllBindingsForHNetPortMappingProtocol
  8340. #undef DPF_MODNAME
  8341. #define DPF_MODNAME "CNATHelpUPnP::CleanupInactiveFirewallMappings"
  8342. //=============================================================================
  8343. // CNATHelpUPnP::CleanupInactiveFirewallMappings
  8344. //-----------------------------------------------------------------------------
  8345. //
  8346. // Description: Looks for any mappings previously made by other DPNATHLP
  8347. // instances that are no longer active (because of a crash), and
  8348. // unmaps them.
  8349. //
  8350. // COM is assumed to have been initialized.
  8351. //
  8352. // The object lock is assumed to be held.
  8353. //
  8354. // Arguments:
  8355. // CDevice * pDevice - Pointer to device to use.
  8356. // IHNetCfgMgr * pHNetCfgMgr - Pointer to IHNetCfgMgr interface to
  8357. // use.
  8358. //
  8359. // Returns: HRESULT
  8360. // DPNH_OK - The cleanup was successful.
  8361. // DPNHERR_GENERIC - An error occurred.
  8362. //=============================================================================
  8363. HRESULT CNATHelpUPnP::CleanupInactiveFirewallMappings(CDevice * const pDevice,
  8364. IHNetCfgMgr * const pHNetCfgMgr)
  8365. {
  8366. HRESULT hr = DPNH_OK;
  8367. CRegistry RegObject;
  8368. BOOL fOpenedRegistry = FALSE;
  8369. DWORD dwIndex;
  8370. WCHAR wszValueName[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  8371. DWORD dwValueNameSize;
  8372. DPNHACTIVEFIREWALLMAPPING dpnhafm;
  8373. DWORD dwValueSize;
  8374. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  8375. DNHANDLE hNamedObject = NULL;
  8376. CRegisteredPort * pRegisteredPort = NULL;
  8377. BOOL fSetPrivateAddresses = FALSE;
  8378. SOCKADDR_IN saddrinPrivate;
  8379. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  8380. this, pDevice, pHNetCfgMgr);
  8381. DNASSERT(pDevice != NULL);
  8382. DNASSERT(pDevice->IsHNetFirewalled());
  8383. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  8384. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVEFIREWALLMAPPINGS,
  8385. FALSE,
  8386. TRUE,
  8387. TRUE,
  8388. DPN_KEY_ALL_ACCESS))
  8389. {
  8390. DPFX(DPFPREP, 1, "Couldn't open active firewall mapping key, not performing crash cleanup.");
  8391. DNASSERT(hr == DPNH_OK);
  8392. goto Exit;
  8393. }
  8394. fOpenedRegistry = TRUE;
  8395. //
  8396. // Walk the list of active mappings.
  8397. //
  8398. dwIndex = 0;
  8399. do
  8400. {
  8401. dwValueNameSize = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  8402. if (! RegObject.EnumValues(wszValueName, &dwValueNameSize, dwIndex))
  8403. {
  8404. //
  8405. // There was an error or there aren't any more keys. We're done.
  8406. //
  8407. break;
  8408. }
  8409. //
  8410. // Try reading that mapping's data.
  8411. //
  8412. dwValueSize = sizeof(dpnhafm);
  8413. if (! RegObject.ReadBlob(wszValueName, (LPBYTE) (&dpnhafm), &dwValueSize))
  8414. {
  8415. //
  8416. // We don't have a lock protecting the registry, so some other
  8417. // instance could have deleted the key between when we enumerated
  8418. // it and now. We'll stop trying (and hopefully that other
  8419. // instance will cover the rest of the items).
  8420. //
  8421. DPFX(DPFPREP, 0, "Couldn't read \"%ls\" mapping value! Done with cleanup.",
  8422. wszValueName);
  8423. DNASSERT(hr == DPNH_OK);
  8424. goto Exit;
  8425. }
  8426. //
  8427. // Validate the data read.
  8428. //
  8429. if ((dwValueSize != sizeof(dpnhafm)) ||
  8430. (dpnhafm.dwVersion != ACTIVE_MAPPING_VERSION))
  8431. {
  8432. DPFX(DPFPREP, 0, "The \"%ls\" mapping value is invalid! Done with cleanup.",
  8433. wszValueName);
  8434. //
  8435. // Move to next item.
  8436. //
  8437. dwIndex++;
  8438. continue;
  8439. }
  8440. //
  8441. // See if that DPNHUPNP instance is still around.
  8442. //
  8443. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  8444. {
  8445. wsprintf(tszObjectName, _T("Global\\") INSTANCENAMEDOBJECT_FORMATSTRING, dpnhafm.dwInstanceKey);
  8446. }
  8447. else
  8448. {
  8449. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, dpnhafm.dwInstanceKey);
  8450. }
  8451. hNamedObject = DNOpenEvent(SYNCHRONIZE, FALSE, tszObjectName);
  8452. if (hNamedObject != NULL)
  8453. {
  8454. //
  8455. // This is still an active mapping.
  8456. //
  8457. DPFX(DPFPREP, 4, "Firewall mapping \"%ls\" belongs to instance %u, which is still active.",
  8458. wszValueName, dpnhafm.dwInstanceKey);
  8459. DNCloseHandle(hNamedObject);
  8460. hNamedObject = NULL;
  8461. //
  8462. // Move to next item.
  8463. //
  8464. dwIndex++;
  8465. continue;
  8466. }
  8467. DPFX(DPFPREP, 4, "Firewall mapping \"%ls\" belongs to instance %u, which no longer exists.",
  8468. wszValueName, dpnhafm.dwInstanceKey);
  8469. //
  8470. // Delete the value now that we have the information we need.
  8471. //
  8472. if (! RegObject.DeleteValue(wszValueName))
  8473. {
  8474. //
  8475. // See ReadBlob comments. Stop trying to cleanup.
  8476. //
  8477. DPFX(DPFPREP, 0, "Couldn't delete \"%ls\"! Done with cleanup.",
  8478. wszValueName);
  8479. DNASSERT(hr == DPNH_OK);
  8480. goto Exit;
  8481. }
  8482. //
  8483. // Create a fake registered port that we will deregister. Ignore the
  8484. // NAT state flags.
  8485. //
  8486. pRegisteredPort = new CRegisteredPort(0, (dpnhafm.dwFlags & REGPORTOBJMASK_HNETFWAPI));
  8487. if (pRegisteredPort == NULL)
  8488. {
  8489. hr = DPNHERR_OUTOFMEMORY;
  8490. goto Failure;
  8491. }
  8492. //
  8493. // Assert that the other information/state flags are correct.
  8494. //
  8495. DNASSERT(! pRegisteredPort->IsHNetFirewallPortUnavailable());
  8496. DNASSERT(! pRegisteredPort->IsRemovingUPnPLease());
  8497. //
  8498. // Temporarily associate the registered port with the device.
  8499. //
  8500. pRegisteredPort->MakeDeviceOwner(pDevice);
  8501. ZeroMemory(&saddrinPrivate, sizeof(saddrinPrivate));
  8502. saddrinPrivate.sin_family = AF_INET;
  8503. saddrinPrivate.sin_addr.S_un.S_addr = dpnhafm.dwAddressV4;
  8504. saddrinPrivate.sin_port = dpnhafm.wPort;
  8505. //
  8506. // Store the private address.
  8507. //
  8508. hr = pRegisteredPort->SetPrivateAddresses(&saddrinPrivate, 1);
  8509. if (hr != DPNH_OK)
  8510. {
  8511. DPFX(DPFPREP, 0, "Failed creating UPnP address array!");
  8512. goto Failure;
  8513. }
  8514. fSetPrivateAddresses = TRUE;
  8515. //
  8516. // Pretend it has been mapped on the local firewall. Note that this
  8517. // flag shouldn't have been set at the time it was stored in registry
  8518. // but we masked it out if it had been.
  8519. //
  8520. pRegisteredPort->NoteMappedOnHNetFirewall();
  8521. //
  8522. // Actually free the port.
  8523. //
  8524. hr = this->UnmapPortOnLocalHNetFirewallInternal(pRegisteredPort,
  8525. FALSE,
  8526. pHNetCfgMgr);
  8527. if (hr != DPNH_OK)
  8528. {
  8529. DPFX(DPFPREP, 0, "Failed deleting temporary HNet firewall port (err = 0x%lx)! Ignoring.",
  8530. hr);
  8531. //
  8532. // Jump to the failure cleanup case, but don't actually return a
  8533. // failure.
  8534. //
  8535. hr = DPNH_OK;
  8536. goto Failure;
  8537. }
  8538. pRegisteredPort->ClearPrivateAddresses();
  8539. fSetPrivateAddresses = FALSE;
  8540. pRegisteredPort->ClearDeviceOwner();
  8541. delete pRegisteredPort;
  8542. pRegisteredPort = NULL;
  8543. //
  8544. // Move to the next mapping. Don't increment index since we just
  8545. // deleted the previous entry and everything shifts down one.
  8546. //
  8547. }
  8548. while (TRUE);
  8549. Exit:
  8550. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  8551. return hr;
  8552. Failure:
  8553. if (pRegisteredPort != NULL)
  8554. {
  8555. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  8556. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  8557. if (fSetPrivateAddresses)
  8558. {
  8559. pRegisteredPort->ClearPrivateAddresses();
  8560. fSetPrivateAddresses = FALSE;
  8561. }
  8562. pRegisteredPort->ClearDeviceOwner();
  8563. delete pRegisteredPort;
  8564. pRegisteredPort = NULL;
  8565. }
  8566. if (fOpenedRegistry)
  8567. {
  8568. RegObject.Close();
  8569. }
  8570. goto Exit;
  8571. } // CNATHelpUPnP::CleanupInactiveFirewallMappings
  8572. #endif // ! DPNBUILD_NOHNETFWAPI
  8573. #undef DPF_MODNAME
  8574. #define DPF_MODNAME "CNATHelpUPnP::RemoveAllItems"
  8575. //=============================================================================
  8576. // CNATHelpUPnP::RemoveAllItems
  8577. //-----------------------------------------------------------------------------
  8578. //
  8579. // Description: Removes all devices (de-registering with Internet gateways
  8580. // if necessary). This removes all registered port mapping
  8581. // objects and UPnP device objects, as well.
  8582. //
  8583. // The object lock is assumed to be held.
  8584. //
  8585. // Arguments: None.
  8586. //
  8587. // Returns: None.
  8588. //=============================================================================
  8589. void CNATHelpUPnP::RemoveAllItems(void)
  8590. {
  8591. HRESULT hr;
  8592. CBilink * pBilinkDevice;
  8593. CDevice * pDevice;
  8594. CBilink * pBilinkRegisteredPort;
  8595. CRegisteredPort * pRegisteredPort;
  8596. CUPnPDevice * pUPnPDevice;
  8597. DPFX(DPFPREP, 7, "(0x%p) Enter", this);
  8598. pBilinkDevice = this->m_blDevices.GetNext();
  8599. while (pBilinkDevice != &this->m_blDevices)
  8600. {
  8601. DNASSERT(! pBilinkDevice->IsEmpty());
  8602. pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
  8603. pBilinkDevice = pBilinkDevice->GetNext();
  8604. DPFX(DPFPREP, 5, "Destroying device 0x%p.",
  8605. pDevice);
  8606. pDevice->m_blList.RemoveFromList();
  8607. //
  8608. // All of the device's registered ports are implicitly freed.
  8609. //
  8610. pBilinkRegisteredPort = pDevice->m_blOwnedRegPorts.GetNext();
  8611. while (pBilinkRegisteredPort != &pDevice->m_blOwnedRegPorts)
  8612. {
  8613. DNASSERT(! pBilinkRegisteredPort->IsEmpty());
  8614. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
  8615. pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
  8616. DPFX(DPFPREP, 5, "Destroying registered port 0x%p (under device 0x%p).",
  8617. pRegisteredPort, pDevice);
  8618. //
  8619. // Unmap on UPnP server if necessary.
  8620. //
  8621. if (pRegisteredPort->HasUPnPPublicAddresses())
  8622. {
  8623. hr = this->UnmapUPnPPort(pRegisteredPort,
  8624. pRegisteredPort->GetNumAddresses(), // free all ports
  8625. TRUE);
  8626. if (hr != DPNH_OK)
  8627. {
  8628. DPFX(DPFPREP, 0, "Couldn't delete UPnP registered port 0x%p mapping (err = 0x%lx)! Ignoring.",
  8629. pRegisteredPort, hr);
  8630. //
  8631. // Continue anyway, so we can finish cleaning up the object.
  8632. //
  8633. }
  8634. DNASSERT(! pRegisteredPort->HasUPnPPublicAddresses());
  8635. pRegisteredPort->NoteNotPermanentUPnPLease();
  8636. pRegisteredPort->NoteNotUPnPPortUnavailable();
  8637. }
  8638. #ifndef DPNBUILD_NOHNETFWAPI
  8639. //
  8640. // Then unmap from the local firewall, if necessary.
  8641. //
  8642. if (pRegisteredPort->IsMappedOnHNetFirewall())
  8643. {
  8644. //
  8645. // Unmap the port.
  8646. //
  8647. // Alert the user since this is unexpected.
  8648. //
  8649. hr = this->UnmapPortOnLocalHNetFirewall(pRegisteredPort,
  8650. TRUE,
  8651. TRUE);
  8652. if (hr != DPNH_OK)
  8653. {
  8654. DPFX(DPFPREP, 0, "Failed unmapping registered port 0x%p on local HomeNet firewall (err = 0x%lx)! Ignoring.",
  8655. pRegisteredPort, hr);
  8656. pRegisteredPort->NoteNotMappedOnHNetFirewall();
  8657. pRegisteredPort->NoteNotHNetFirewallMappingBuiltIn();
  8658. //
  8659. // Continue anyway, so we can finish cleaning up the object.
  8660. //
  8661. }
  8662. }
  8663. #endif // ! DPNBUILD_NOHNETFWAPI
  8664. pRegisteredPort->ClearDeviceOwner();
  8665. DNASSERT(pRegisteredPort->m_blGlobalList.IsListMember(&this->m_blRegisteredPorts));
  8666. pRegisteredPort->m_blGlobalList.RemoveFromList();
  8667. pRegisteredPort->ClearPrivateAddresses();
  8668. //
  8669. // The user implicitly released this port.
  8670. //
  8671. pRegisteredPort->ClearAllUserRefs();
  8672. delete pRegisteredPort;
  8673. }
  8674. //
  8675. // The device's UPnP gateway is implicitly removed.
  8676. //
  8677. pUPnPDevice = pDevice->GetUPnPDevice();
  8678. if (pUPnPDevice != NULL)
  8679. {
  8680. if ((pUPnPDevice->IsConnecting()) || (pUPnPDevice->IsConnected()))
  8681. {
  8682. if (this->m_pfnshutdown(pUPnPDevice->GetControlSocket(), 0) != 0)
  8683. {
  8684. #ifdef DBG
  8685. int iError;
  8686. iError = this->m_pfnWSAGetLastError();
  8687. DPFX(DPFPREP, 0, "Failed shutting down UPnP device 0x%p's control socket (err = %u)! Ignoring.",
  8688. pUPnPDevice, iError);
  8689. #endif // DBG
  8690. }
  8691. }
  8692. pUPnPDevice->ClearDeviceOwner();
  8693. DNASSERT(pUPnPDevice->m_blList.IsListMember(&this->m_blUPnPDevices));
  8694. pUPnPDevice->m_blList.RemoveFromList();
  8695. //
  8696. // Transfer list reference to our pointer, since GetUPnPDevice did
  8697. // not give us one.
  8698. //
  8699. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  8700. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  8701. pUPnPDevice->ClearLocationURL();
  8702. pUPnPDevice->ClearUSN();
  8703. pUPnPDevice->ClearServiceControlURL();
  8704. pUPnPDevice->DestroyReceiveBuffer();
  8705. pUPnPDevice->RemoveAllCachedMappings();
  8706. pUPnPDevice->DecRef();
  8707. pUPnPDevice = NULL;
  8708. }
  8709. #ifndef DPNBUILD_NOHNETFWAPI
  8710. //
  8711. // If we used the HomeNet firewall API to open a hole for UPnP
  8712. // discovery multicasts, close it.
  8713. //
  8714. if (pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall())
  8715. {
  8716. hr = this->CloseDevicesUPnPDiscoveryPort(pDevice, NULL);
  8717. if (hr != DPNH_OK)
  8718. {
  8719. DPFX(DPFPREP, 0, "Couldn't close device 0x%p's UPnP discovery socket's port on firewall (err = 0x%lx)! Ignoring.",
  8720. pDevice, hr);
  8721. //
  8722. // Continue...
  8723. //
  8724. pDevice->NoteNotUPnPDiscoverySocketMappedOnHNetFirewall();
  8725. hr = DPNH_OK;
  8726. }
  8727. }
  8728. #endif // ! DPNBUILD_NOHNETFWAPI
  8729. //
  8730. // Close the socket.
  8731. //
  8732. if (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP)
  8733. {
  8734. this->m_pfnclosesocket(pDevice->GetUPnPDiscoverySocket());
  8735. pDevice->SetUPnPDiscoverySocket(INVALID_SOCKET);
  8736. }
  8737. //
  8738. // Now we can dump the device object.
  8739. //
  8740. delete pDevice;
  8741. }
  8742. //
  8743. // Removing all the devices normally removes all the registered ports, but
  8744. // there may still be more wildcard ports that were never associated with
  8745. // any device.
  8746. //
  8747. pBilinkRegisteredPort = this->m_blUnownedPorts.GetNext();
  8748. while (pBilinkRegisteredPort != &this->m_blUnownedPorts)
  8749. {
  8750. DNASSERT(! pBilinkRegisteredPort->IsEmpty());
  8751. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
  8752. pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
  8753. DPFX(DPFPREP, 5, "Destroying unowned registered port 0x%p.",
  8754. pRegisteredPort);
  8755. pRegisteredPort->m_blDeviceList.RemoveFromList();
  8756. DNASSERT(pRegisteredPort->m_blGlobalList.IsListMember(&this->m_blRegisteredPorts));
  8757. pRegisteredPort->m_blGlobalList.RemoveFromList();
  8758. pRegisteredPort->ClearPrivateAddresses();
  8759. #ifndef DPNBUILD_NOHNETFWAPI
  8760. DNASSERT(! pRegisteredPort->IsMappedOnHNetFirewall());
  8761. DNASSERT(! pRegisteredPort->IsHNetFirewallPortUnavailable());
  8762. #endif // ! DPNBUILD_NOHNETFWAPI
  8763. DNASSERT(! pRegisteredPort->HasUPnPPublicAddresses());
  8764. DNASSERT(! pRegisteredPort->IsUPnPPortUnavailable());
  8765. //
  8766. // The user implicitly released this port.
  8767. //
  8768. pRegisteredPort->ClearAllUserRefs();
  8769. delete pRegisteredPort;
  8770. }
  8771. #ifdef DBG
  8772. DNASSERT(this->m_blRegisteredPorts.IsEmpty());
  8773. DNASSERT(this->m_blUPnPDevices.IsEmpty());
  8774. //
  8775. // Print all items still in the registry.
  8776. //
  8777. #ifndef DPNBUILD_NOHNETFWAPI
  8778. this->DebugPrintActiveFirewallMappings();
  8779. #endif // ! DPNBUILD_NOHNETFWAPI
  8780. this->DebugPrintActiveNATMappings();
  8781. #endif // DBG
  8782. DPFX(DPFPREP, 7, "(0x%p) Leave", this);
  8783. } // CNATHelpUPnP::RemoveAllItems
  8784. #undef DPF_MODNAME
  8785. #define DPF_MODNAME "CNATHelpUPnP::FindMatchingDevice"
  8786. //=============================================================================
  8787. // CNATHelpUPnP::FindMatchingDevice
  8788. //-----------------------------------------------------------------------------
  8789. //
  8790. // Description: Searches the list of devices for the object matching the
  8791. // given address, or NULL if one could not be found. If the
  8792. // address is INADDR_ANY, then the first device with a remote NAT
  8793. // is selected. If none exist, then the first device with a local
  8794. // firewall is selected.
  8795. //
  8796. // If fUseAllInfoSources is TRUE, the list of registered ports
  8797. // associated with devices is searched first for an exact match to
  8798. // the address passed in. If that fails, then devices are
  8799. // searched as above. In addition, if the address is INADDR_ANY,
  8800. /// the first device with a local NAT can be selected.
  8801. //
  8802. // The object lock is assumed to be held.
  8803. //
  8804. // Arguments:
  8805. // SOCKADDR_IN * psaddrinMatch - Pointer to address to look up.
  8806. // BOOL fUseAllInfoSources - Whether all possible sources of
  8807. // information should be considered.
  8808. //
  8809. // Returns: CDevice
  8810. // NULL if no match, valid object otherwise.
  8811. //=============================================================================
  8812. CDevice * CNATHelpUPnP::FindMatchingDevice(const SOCKADDR_IN * const psaddrinMatch,
  8813. const BOOL fUseAllInfoSources)
  8814. {
  8815. HRESULT hr;
  8816. BOOL fUpdatedDeviceList = FALSE;
  8817. CDevice * pDeviceUPnPGateway = NULL;
  8818. #ifndef DPNBUILD_NOHNETFWAPI
  8819. CDevice * pDeviceLocalHNetFirewall = NULL;
  8820. #endif // ! DPNBUILD_NOHNETFWAPI
  8821. SOCKADDR_IN * pasaddrinTemp;
  8822. CBilink * pBilink;
  8823. CRegisteredPort * pRegisteredPort;
  8824. CDevice * pDevice;
  8825. DWORD dwTemp;
  8826. do
  8827. {
  8828. //
  8829. // First, make sure there are devices to choose from.
  8830. //
  8831. if (this->m_blDevices.IsEmpty())
  8832. {
  8833. DPFX(DPFPREP, 0, "No devices, can't match address %u.%u.%u.%u!",
  8834. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  8835. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  8836. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  8837. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
  8838. pDevice = NULL;
  8839. goto Exit;
  8840. }
  8841. //
  8842. // It's possible that the address we're trying to match is an already
  8843. // registered port. Look through all owned port mappings for this
  8844. // address, if we're allowed.
  8845. //
  8846. if (fUseAllInfoSources)
  8847. {
  8848. pBilink = this->m_blRegisteredPorts.GetNext();
  8849. while (pBilink != &this->m_blRegisteredPorts)
  8850. {
  8851. DNASSERT(! pBilink->IsEmpty());
  8852. pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
  8853. //
  8854. // Only check this registered port if it has an owning device.
  8855. //
  8856. pDevice = pRegisteredPort->GetOwningDevice();
  8857. if (pDevice != NULL)
  8858. {
  8859. //
  8860. // Check each port in the array.
  8861. //
  8862. pasaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
  8863. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  8864. {
  8865. //
  8866. // If the address matches, we have a winner.
  8867. //
  8868. if ((pasaddrinTemp[dwTemp].sin_addr.S_un.S_addr == psaddrinMatch->sin_addr.S_un.S_addr) &&
  8869. (pasaddrinTemp[dwTemp].sin_port == psaddrinMatch->sin_port))
  8870. {
  8871. DPFX(DPFPREP, 7, "Registered port 0x%p index %u matches address %u.%u.%u.%u:%u, returning owning device 0x%p.",
  8872. pRegisteredPort,
  8873. dwTemp,
  8874. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  8875. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  8876. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  8877. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4,
  8878. NTOHS(psaddrinMatch->sin_port),
  8879. pDevice);
  8880. goto Exit;
  8881. }
  8882. }
  8883. }
  8884. pBilink = pBilink->GetNext();
  8885. }
  8886. }
  8887. //
  8888. // Darn, the address is not already registered. Well, match it up with
  8889. // a device as best as possible.
  8890. //
  8891. pBilink = this->m_blDevices.GetNext();
  8892. do
  8893. {
  8894. DNASSERT(! pBilink->IsEmpty());
  8895. pDevice = DEVICE_FROM_BILINK(pBilink);
  8896. if ((pDevice->GetLocalAddressV4() == psaddrinMatch->sin_addr.S_un.S_addr))
  8897. {
  8898. DPFX(DPFPREP, 7, "Device 0x%p matches address %u.%u.%u.%u.",
  8899. pDevice,
  8900. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  8901. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  8902. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  8903. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
  8904. goto Exit;
  8905. }
  8906. //
  8907. // Remember this device if it has the first remote UPnP gateway
  8908. // device we've seen.
  8909. //
  8910. if ((pDevice->GetUPnPDevice() != NULL) &&
  8911. ((! pDevice->GetUPnPDevice()->IsLocal()) || (fUseAllInfoSources)) &&
  8912. (pDeviceUPnPGateway == NULL))
  8913. {
  8914. pDeviceUPnPGateway = pDevice;
  8915. }
  8916. #ifndef DPNBUILD_NOHNETFWAPI
  8917. //
  8918. // Remember this device if it has the first HomeNet firewall we've
  8919. // seen.
  8920. //
  8921. if ((pDevice->IsHNetFirewalled()) &&
  8922. (pDeviceLocalHNetFirewall == NULL))
  8923. {
  8924. pDeviceLocalHNetFirewall = pDevice;
  8925. }
  8926. #endif // ! DPNBUILD_NOHNETFWAPI
  8927. DPFX(DPFPREP, 7, "Device 0x%p does not match address %u.%u.%u.%u.",
  8928. pDevice,
  8929. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  8930. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  8931. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  8932. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
  8933. pBilink = pBilink->GetNext();
  8934. }
  8935. while (pBilink != &this->m_blDevices);
  8936. //
  8937. // If we got here, there's no matching device. It might be because the
  8938. // caller detected an address change faster than we did. Try updating
  8939. // our device list and searching again (if we haven't already).
  8940. //
  8941. if (fUpdatedDeviceList)
  8942. {
  8943. break;
  8944. }
  8945. //
  8946. // Don't bother updating the list to match INADDR_ANY, we know that
  8947. // will never match anything.
  8948. //
  8949. if (psaddrinMatch->sin_addr.S_un.S_addr == INADDR_ANY)
  8950. {
  8951. DPFX(DPFPREP, 7, "Couldn't find matching device for INADDR_ANY, as expected.");
  8952. break;
  8953. }
  8954. DPFX(DPFPREP, 5, "Couldn't find matching device for %u.%u.%u.%u, updating device list and searching again.",
  8955. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  8956. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  8957. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  8958. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
  8959. hr = this->CheckForNewDevices(&fUpdatedDeviceList);
  8960. if (hr != DPNH_OK)
  8961. {
  8962. DPFX(DPFPREP, 0, "Couldn't check for new devices (0x%lx), continuing.",
  8963. hr);
  8964. //
  8965. // Hmm, we have to treat it as non-fatal. Don't search again,
  8966. // though.
  8967. //
  8968. break;
  8969. }
  8970. //
  8971. // If we didn't actually get any new devices, don't bother searching
  8972. // again.
  8973. //
  8974. if (! fUpdatedDeviceList)
  8975. {
  8976. break;
  8977. }
  8978. //
  8979. // fUpdatedDeviceList is set to TRUE so we'll only loop one more time.
  8980. //
  8981. }
  8982. while (TRUE);
  8983. //
  8984. // If we got here, there's still no matching device. If it's the wildcard
  8985. // value, that's to be expected, but we need to pick a device in the
  8986. // following order:
  8987. // 1. device has an Internet gateway
  8988. // 2. device has a firewall
  8989. // If none of those exists or it's not the wildcard value, we have to give
  8990. // up.
  8991. //
  8992. if (psaddrinMatch->sin_addr.S_un.S_addr == INADDR_ANY)
  8993. {
  8994. if (pDeviceUPnPGateway != NULL)
  8995. {
  8996. pDevice = pDeviceUPnPGateway;
  8997. DPFX(DPFPREP, 1, "Picking device 0x%p with UPnP gateway device to match INADDR_ANY.",
  8998. pDevice);
  8999. }
  9000. #ifndef DPNBUILD_NOHNETFWAPI
  9001. else if (pDeviceLocalHNetFirewall != NULL)
  9002. {
  9003. pDevice = pDeviceLocalHNetFirewall;
  9004. DPFX(DPFPREP, 1, "Picking device 0x%p with local HomeNet firewall to match INADDR_ANY.",
  9005. pDevice);
  9006. }
  9007. #endif // ! DPNBUILD_NOHNETFWAPI
  9008. else
  9009. {
  9010. pDevice = NULL;
  9011. DPFX(DPFPREP, 1, "No suitable device to match INADDR_ANY.");
  9012. }
  9013. }
  9014. else
  9015. {
  9016. pDevice = NULL;
  9017. DPFX(DPFPREP, 7, "No devices match address %u.%u.%u.%u.",
  9018. psaddrinMatch->sin_addr.S_un.S_un_b.s_b1,
  9019. psaddrinMatch->sin_addr.S_un.S_un_b.s_b2,
  9020. psaddrinMatch->sin_addr.S_un.S_un_b.s_b3,
  9021. psaddrinMatch->sin_addr.S_un.S_un_b.s_b4);
  9022. }
  9023. Exit:
  9024. return pDevice;
  9025. } // CNATHelpUPnP::FindMatchingDevice
  9026. #undef DPF_MODNAME
  9027. #define DPF_MODNAME "CNATHelpUPnP::ExtendAllExpiringLeases"
  9028. //=============================================================================
  9029. // CNATHelpUPnP::ExtendAllExpiringLeases
  9030. //-----------------------------------------------------------------------------
  9031. //
  9032. // Description: Renews any port leases that are close to expiring (within 2
  9033. // minutes of expiration time).
  9034. //
  9035. // The object lock is assumed to be held.
  9036. //
  9037. // Arguments: None.
  9038. //
  9039. // Returns: HRESULT
  9040. // DPNH_OK - Lease extension was successful.
  9041. // DPNHERR_GENERIC - An error occurred.
  9042. //=============================================================================
  9043. HRESULT CNATHelpUPnP::ExtendAllExpiringLeases(void)
  9044. {
  9045. HRESULT hr = DPNH_OK;
  9046. CBilink * pBilink;
  9047. CRegisteredPort * pRegisteredPort;
  9048. CDevice * pDevice;
  9049. DWORD dwLeaseTimeRemaining;
  9050. DPFX(DPFPREP, 5, "Enter");
  9051. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  9052. //
  9053. // Walk the list of all registered ports and check for leases that need to
  9054. // be extended.
  9055. // The lock is already held.
  9056. //
  9057. pBilink = this->m_blRegisteredPorts.GetNext();
  9058. while (pBilink != (&this->m_blRegisteredPorts))
  9059. {
  9060. DNASSERT(! pBilink->IsEmpty());
  9061. pRegisteredPort = REGPORT_FROM_GLOBAL_BILINK(pBilink);
  9062. pDevice = pRegisteredPort->GetOwningDevice();
  9063. //
  9064. // If the port is registered with the UPnP device, extend that lease,
  9065. // if necessary.
  9066. //
  9067. if ((pRegisteredPort->HasUPnPPublicAddresses()) &&
  9068. (! pRegisteredPort->HasPermanentUPnPLease()))
  9069. {
  9070. DNASSERT(pDevice != NULL);
  9071. dwLeaseTimeRemaining = pRegisteredPort->GetUPnPLeaseExpiration() - GETTIMESTAMP();
  9072. if (dwLeaseTimeRemaining < LEASE_RENEW_TIME)
  9073. {
  9074. hr = this->ExtendUPnPLease(pRegisteredPort);
  9075. if (hr != DPNH_OK)
  9076. {
  9077. DPFX(DPFPREP, 0, "Couldn't extend port mapping lease on remote UPnP device (0x%lx)! Ignoring.", hr);
  9078. //
  9079. // We'll treat this as non-fatal, but we have to dump the
  9080. // server. This may have already been done, but doing it
  9081. // twice shouldn't be harmful.
  9082. //
  9083. this->ClearDevicesUPnPDevice(pDevice);
  9084. hr = DPNH_OK;
  9085. }
  9086. }
  9087. }
  9088. //
  9089. // The local firewall never uses leases, no need to extend.
  9090. //
  9091. pBilink = pBilink->GetNext();
  9092. }
  9093. DNASSERT(hr == DPNH_OK);
  9094. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  9095. return hr;
  9096. } // CNATHelpUPnP::ExtendAllExpiringLeases
  9097. #undef DPF_MODNAME
  9098. #define DPF_MODNAME "CNATHelpUPnP::UpdateServerStatus"
  9099. //=============================================================================
  9100. // CNATHelpUPnP::UpdateServerStatus
  9101. //-----------------------------------------------------------------------------
  9102. //
  9103. // Description: Checks to see if any Internet gateways have stopped
  9104. // responding or are now available.
  9105. //
  9106. // The object lock is assumed to be held.
  9107. //
  9108. // Arguments: None.
  9109. //
  9110. // Returns: HRESULT
  9111. // DPNH_OK - The update was successful.
  9112. // DPNHERR_GENERIC - An error occurred.
  9113. //=============================================================================
  9114. HRESULT CNATHelpUPnP::UpdateServerStatus(void)
  9115. {
  9116. HRESULT hr = DPNH_OK;
  9117. DWORD dwMinUpdateServerStatusInterval;
  9118. DWORD dwCurrentTime;
  9119. CBilink * pBilink;
  9120. CDevice * pDevice;
  9121. CUPnPDevice * pUPnPDevice = NULL;
  9122. CDevice * pDeviceRemoteUPnPGateway = NULL;
  9123. #ifndef DPNBUILD_NOHNETFWAPI
  9124. CDevice * pDeviceLocalHNetFirewall = NULL;
  9125. #endif // ! DPNBUILD_NOHNETFWAPI
  9126. BOOL fSendRemoteGatewayDiscovery;
  9127. DPFX(DPFPREP, 5, "Enter");
  9128. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  9129. //
  9130. // Cache the current value of the global. This should be atomic so no need
  9131. // to take the globals lock.
  9132. //
  9133. dwMinUpdateServerStatusInterval = g_dwMinUpdateServerStatusInterval;
  9134. //
  9135. // Capture the current time.
  9136. //
  9137. dwCurrentTime = GETTIMESTAMP();
  9138. //
  9139. // If this isn't the first time to update server status, but it hasn't been
  9140. // very long since we last checked, don't. This will prevent unnecessary
  9141. // network traffic if GetCaps is called frequently (in response to many
  9142. // alert events, for example).
  9143. //
  9144. // However, if we just found a new device, update the status anyway.
  9145. //
  9146. if (this->m_dwLastUpdateServerStatusTime != 0)
  9147. {
  9148. if ((dwCurrentTime - this->m_dwLastUpdateServerStatusTime) < dwMinUpdateServerStatusInterval)
  9149. {
  9150. if (! (this->m_dwFlags & NATHELPUPNPOBJ_DEVICECHANGED))
  9151. {
  9152. DPFX(DPFPREP, 5, "Server status was just updated at %u, not updating again (time = %u, min interval = %u).",
  9153. this->m_dwLastUpdateServerStatusTime,
  9154. dwCurrentTime,
  9155. dwMinUpdateServerStatusInterval);
  9156. //
  9157. // hr == DPNH_OK
  9158. //
  9159. goto Exit;
  9160. }
  9161. 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.",
  9162. this->m_dwLastUpdateServerStatusTime,
  9163. dwCurrentTime,
  9164. dwMinUpdateServerStatusInterval);
  9165. //
  9166. // Continue...
  9167. //
  9168. }
  9169. //
  9170. // If we're allowed to keep polling for remote gateways after startup,
  9171. // do so. Otherwise, only do it if a device has changed or a port has
  9172. // been registered since our last check.
  9173. //
  9174. if ((g_fKeepPollingForRemoteGateway) ||
  9175. (this->m_dwFlags & NATHELPUPNPOBJ_DEVICECHANGED) ||
  9176. (this->m_dwFlags & NATHELPUPNPOBJ_PORTREGISTERED))
  9177. {
  9178. fSendRemoteGatewayDiscovery = TRUE;
  9179. }
  9180. else
  9181. {
  9182. fSendRemoteGatewayDiscovery = FALSE;
  9183. }
  9184. }
  9185. else
  9186. {
  9187. //
  9188. // We always poll for new remote gateways during startup.
  9189. //
  9190. fSendRemoteGatewayDiscovery = TRUE;
  9191. }
  9192. //
  9193. // Prevent the timer from landing exactly on 0.
  9194. //
  9195. if (dwCurrentTime == 0)
  9196. {
  9197. dwCurrentTime = 1;
  9198. }
  9199. this->m_dwLastUpdateServerStatusTime = dwCurrentTime;
  9200. //
  9201. // Turn off the 'device changed' and 'port registered' flags, if they were
  9202. // on.
  9203. //
  9204. this->m_dwFlags &= ~(NATHELPUPNPOBJ_DEVICECHANGED | NATHELPUPNPOBJ_PORTREGISTERED);
  9205. //
  9206. // Locate any new UPnP devices.
  9207. //
  9208. if (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP)
  9209. {
  9210. //
  9211. // We're not listening on the UPnP multicast address and can't hear
  9212. // unsolicited new device announcements. In order to detect new
  9213. // devices, we need to resend the discovery request periodically so
  9214. // that responses get sent directly to our listening socket.
  9215. //
  9216. hr = this->CheckForUPnPAnnouncements(g_dwUPnPAnnounceResponseWaitTime,
  9217. fSendRemoteGatewayDiscovery);
  9218. if (hr != DPNH_OK)
  9219. {
  9220. DPFX(DPFPREP, 0, "Couldn't check for UPnP announcements!");
  9221. goto Failure;
  9222. }
  9223. }
  9224. else
  9225. {
  9226. //
  9227. // Not using UPnP.
  9228. //
  9229. }
  9230. //
  9231. // Loop through all the devices.
  9232. //
  9233. pBilink = this->m_blDevices.GetNext();
  9234. while (pBilink != &this->m_blDevices)
  9235. {
  9236. DNASSERT(! pBilink->IsEmpty());
  9237. pDevice = DEVICE_FROM_BILINK(pBilink);
  9238. //
  9239. // This might be a new device, so register any ports with this address
  9240. // that were previously unowned (because this device's address was
  9241. // unknown at the time).
  9242. //
  9243. hr = this->RegisterPreviouslyUnownedPortsWithDevice(pDevice, FALSE);
  9244. if (hr != DPNH_OK)
  9245. {
  9246. DPFX(DPFPREP, 0, "Couldn't register previously unowned ports with device 0x%p!.",
  9247. pDevice);
  9248. goto Failure;
  9249. }
  9250. #ifndef DPNBUILD_NOHNETFWAPI
  9251. if (this->m_dwFlags & NATHELPUPNPOBJ_USEHNETFWAPI)
  9252. {
  9253. //
  9254. // See if the local firewall state has changed.
  9255. //
  9256. hr = this->CheckForLocalHNetFirewallAndMapPorts(pDevice, NULL);
  9257. if (hr != DPNH_OK)
  9258. {
  9259. DPFX(DPFPREP, 0, "Couldn't check for local HNet firewall and map ports (err = 0x%lx)! Ignoring.",
  9260. hr);
  9261. DNASSERT(! pDevice->IsHNetFirewalled());
  9262. hr = DPNH_OK;
  9263. }
  9264. //
  9265. // If there's a local firewall, remember the device if it's the
  9266. // first one we've found.
  9267. //
  9268. if ((pDevice->IsHNetFirewalled()) &&
  9269. (pDeviceLocalHNetFirewall == NULL))
  9270. {
  9271. pDeviceLocalHNetFirewall = pDevice;
  9272. }
  9273. }
  9274. else
  9275. {
  9276. //
  9277. // Not using firewall traversal.
  9278. //
  9279. }
  9280. #endif // ! DPNBUILD_NOHNETFWAPI
  9281. if (this->m_dwFlags & NATHELPUPNPOBJ_USEUPNP)
  9282. {
  9283. pUPnPDevice = pDevice->GetUPnPDevice();
  9284. if (pUPnPDevice != NULL)
  9285. {
  9286. //
  9287. // GetUPnPDevice did not add a reference to pUPnPDevice for us.
  9288. //
  9289. pUPnPDevice->AddRef();
  9290. //
  9291. // Update the public addresses for the UPnP device, if any.
  9292. //
  9293. hr = this->UpdateUPnPExternalAddress(pUPnPDevice, TRUE);
  9294. if (hr != DPNH_OK)
  9295. {
  9296. DPFX(DPFPREP, 0, "Failed updating UPnP device external address!");
  9297. //
  9298. // It may have been cleared already, but doing it twice
  9299. // shouldn't be harmful.
  9300. //
  9301. this->ClearDevicesUPnPDevice(pDevice);
  9302. hr = DPNH_OK;
  9303. }
  9304. else
  9305. {
  9306. //
  9307. // Save this UPnP device, if it's the first one we've
  9308. // found and it's not local.
  9309. //
  9310. if ((pDeviceRemoteUPnPGateway == NULL) &&
  9311. (! pUPnPDevice->IsLocal()))
  9312. {
  9313. pDeviceRemoteUPnPGateway = pDevice;
  9314. }
  9315. }
  9316. pUPnPDevice->DecRef();
  9317. pUPnPDevice = NULL;
  9318. }
  9319. else
  9320. {
  9321. //
  9322. // No UPnP device.
  9323. //
  9324. }
  9325. }
  9326. else
  9327. {
  9328. //
  9329. // Not using UPnP.
  9330. //
  9331. }
  9332. pBilink = pBilink->GetNext();
  9333. }
  9334. //
  9335. // Some new servers may have come online. If so, we can now map wildcard
  9336. // ports that were registered previously. Figure out which device that is.
  9337. //
  9338. if (pDeviceRemoteUPnPGateway != NULL)
  9339. {
  9340. pDevice = pDeviceRemoteUPnPGateway;
  9341. }
  9342. #ifndef DPNBUILD_NOHNETFWAPI
  9343. else if (pDeviceLocalHNetFirewall != NULL)
  9344. {
  9345. pDevice = pDeviceLocalHNetFirewall;
  9346. }
  9347. #endif // ! DPNBUILD_NOHNETFWAPI
  9348. else
  9349. {
  9350. pDevice = NULL;
  9351. }
  9352. if (pDevice != NULL)
  9353. {
  9354. //
  9355. // Register any wildcard ports that are unowned with this best device.
  9356. //
  9357. hr = this->RegisterPreviouslyUnownedPortsWithDevice(pDevice, TRUE);
  9358. if (hr != DPNH_OK)
  9359. {
  9360. DPFX(DPFPREP, 0, "Couldn't register unowned wildcard ports with device 0x%p!.",
  9361. pDevice);
  9362. goto Failure;
  9363. }
  9364. }
  9365. #ifdef DBG
  9366. else
  9367. {
  9368. DPFX(DPFPREP, 7, "No devices have a UPnP gateway device or a local HomeNet firewall.");
  9369. }
  9370. #endif // DBG
  9371. DPFX(DPFPREP, 7, "Spent %u ms updating server status, starting at %u.",
  9372. (GETTIMESTAMP() - dwCurrentTime), dwCurrentTime);
  9373. Exit:
  9374. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  9375. return hr;
  9376. Failure:
  9377. goto Exit;
  9378. } // CNATHelpUPnP::UpdateServerStatus
  9379. #undef DPF_MODNAME
  9380. #define DPF_MODNAME "CNATHelpUPnP::RegisterPreviouslyUnownedPortsWithDevice"
  9381. //=============================================================================
  9382. // CNATHelpUPnP::RegisterPreviouslyUnownedPortsWithDevice
  9383. //-----------------------------------------------------------------------------
  9384. //
  9385. // Description: Associates unknown ports with the given device, and
  9386. // registers them with the device's UPnP device or firewall.
  9387. //
  9388. // If fWildcardToo is FALSE, only previously unowned ports that
  9389. // match the device's address are associated. If TRUE, unowned
  9390. // INADDR_ANY ports are associated as well.
  9391. //
  9392. // The object lock is assumed to be held.
  9393. //
  9394. // Arguments:
  9395. // CDevice * pDevice - Pointer to device to take ownership of ports.
  9396. // BOOL fAll - Whether all ports should be associated.
  9397. //
  9398. // Returns: HRESULT
  9399. // DPNH_OK - The extension was successful.
  9400. // DPNHERR_GENERIC - An error occurred.
  9401. //=============================================================================
  9402. HRESULT CNATHelpUPnP::RegisterPreviouslyUnownedPortsWithDevice(CDevice * const pDevice,
  9403. const BOOL fWildcardToo)
  9404. {
  9405. HRESULT hr = DPNH_OK;
  9406. CBilink * pBilink;
  9407. CRegisteredPort * pRegisteredPort;
  9408. SOCKADDR_IN * pasaddrinPrivate;
  9409. CUPnPDevice * pUPnPDevice;
  9410. #ifdef DBG
  9411. BOOL fAssignedPort = FALSE;
  9412. IN_ADDR inaddrTemp;
  9413. #endif // DBG
  9414. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)",
  9415. this, pDevice, fWildcardToo);
  9416. //
  9417. // Loop through all unowned ports, assign them to the device if
  9418. // appropriate, then register them.
  9419. //
  9420. pBilink = this->m_blUnownedPorts.GetNext();
  9421. while (pBilink != &this->m_blUnownedPorts)
  9422. {
  9423. DNASSERT(! pBilink->IsEmpty());
  9424. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  9425. pBilink = pBilink->GetNext();
  9426. //
  9427. // The registered port must match the device's address in order to
  9428. // associate them. If wildcards are allowed, then INADDR_ANY
  9429. // registrations can be associated, too.
  9430. //
  9431. //
  9432. // All addresses should be same (if there are more than one), so just
  9433. // compare the first one in the array.
  9434. //
  9435. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  9436. if (pasaddrinPrivate[0].sin_addr.S_un.S_addr != pDevice->GetLocalAddressV4())
  9437. {
  9438. if (pasaddrinPrivate[0].sin_addr.S_un.S_addr != INADDR_ANY)
  9439. {
  9440. DPFX(DPFPREP, 7, "Unowned registered port 0x%p private address %u.%u.%u.%u doesn't match device 0x%p's, skipping.",
  9441. pRegisteredPort,
  9442. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b1,
  9443. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b2,
  9444. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b3,
  9445. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b4,
  9446. pDevice);
  9447. continue;
  9448. }
  9449. #ifdef DBG
  9450. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  9451. #endif // DBG
  9452. if (! fWildcardToo)
  9453. {
  9454. #ifdef DBG
  9455. 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.",
  9456. pRegisteredPort,
  9457. pDevice,
  9458. inaddrTemp.S_un.S_un_b.s_b1,
  9459. inaddrTemp.S_un.S_un_b.s_b2,
  9460. inaddrTemp.S_un.S_un_b.s_b3,
  9461. inaddrTemp.S_un.S_un_b.s_b4);
  9462. #endif // DBG
  9463. continue;
  9464. }
  9465. #ifdef DBG
  9466. DPFX(DPFPREP, 7, "Unowned registered port 0x%p (INADDR_ANY) becoming associated with device 0x%p (address %u.%u.%u.%u).",
  9467. pRegisteredPort,
  9468. pDevice,
  9469. inaddrTemp.S_un.S_un_b.s_b1,
  9470. inaddrTemp.S_un.S_un_b.s_b2,
  9471. inaddrTemp.S_un.S_un_b.s_b3,
  9472. inaddrTemp.S_un.S_un_b.s_b4);
  9473. #endif // DBG
  9474. }
  9475. else
  9476. {
  9477. DPFX(DPFPREP, 7, "Unowned registered port 0x%p private address %u.%u.%u.%u matches device 0x%p's, associating.",
  9478. pRegisteredPort,
  9479. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b1,
  9480. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b2,
  9481. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b3,
  9482. pasaddrinPrivate[0].sin_addr.S_un.S_un_b.s_b4,
  9483. pDevice);
  9484. //
  9485. // The way it's currently implemented, all non-wildcard ports
  9486. // should be registered before we even try to register the wildcard
  9487. // ones.
  9488. //
  9489. DNASSERT(! fWildcardToo);
  9490. }
  9491. //
  9492. // If we made it here, we can associate the port with the device.
  9493. //
  9494. pRegisteredPort->m_blDeviceList.RemoveFromList();
  9495. pRegisteredPort->MakeDeviceOwner(pDevice);
  9496. #ifndef DPNBUILD_NOHNETFWAPI
  9497. //
  9498. // Start by automatically mapping with the local firewall, if there is
  9499. // one and we're allowed.
  9500. //
  9501. if (this->m_dwFlags & NATHELPUPNPOBJ_USEHNETFWAPI)
  9502. {
  9503. hr = this->CheckForLocalHNetFirewallAndMapPorts(pDevice, NULL);
  9504. if (hr != DPNH_OK)
  9505. {
  9506. DPFX(DPFPREP, 0, "Couldn't check for local HNet firewall and map ports (err = 0x%lx)! Ignoring.",
  9507. hr);
  9508. DNASSERT(! pDevice->IsHNetFirewalled());
  9509. hr = DPNH_OK;
  9510. }
  9511. }
  9512. else
  9513. {
  9514. //
  9515. // Not using firewall traversal.
  9516. //
  9517. }
  9518. #endif // ! DPNBUILD_NOHNETFWAPI
  9519. //
  9520. // Attempt to automatically map it with the (new) UPnP gateway device,
  9521. // if present.
  9522. //
  9523. pUPnPDevice = pDevice->GetUPnPDevice();
  9524. if (pUPnPDevice != NULL)
  9525. {
  9526. //
  9527. // GetUPnPDevice did not add a reference to pUPnPDevice for us.
  9528. //
  9529. pUPnPDevice->AddRef();
  9530. DNASSERT(pUPnPDevice->IsReady());
  9531. hr = this->MapPortsOnUPnPDevice(pUPnPDevice, pRegisteredPort);
  9532. if (hr != DPNH_OK)
  9533. {
  9534. DPFX(DPFPREP, 0, "Couldn't map existing ports on UPnP device 0x%p!",
  9535. pUPnPDevice);
  9536. //
  9537. // It may have been cleared already, but doing it twice
  9538. // shouldn't be harmful.
  9539. //
  9540. this->ClearDevicesUPnPDevice(pDevice);
  9541. hr = DPNH_OK;
  9542. }
  9543. pUPnPDevice->DecRef();
  9544. pUPnPDevice = NULL;
  9545. }
  9546. }
  9547. #ifdef DBG
  9548. if (! fAssignedPort)
  9549. {
  9550. DPFX(DPFPREP, 1, "No unowned ports were bound to device object 0x%p.",
  9551. pDevice);
  9552. }
  9553. #endif // DBG
  9554. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  9555. return hr;
  9556. } // CNATHelpUPnP::RegisterPreviouslyUnownedPortsWithDevice
  9557. #undef DPF_MODNAME
  9558. #define DPF_MODNAME "CNATHelpUPnP::SendUPnPSearchMessagesForDevice"
  9559. //=============================================================================
  9560. // CNATHelpUPnP::SendUPnPSearchMessagesForDevice
  9561. //-----------------------------------------------------------------------------
  9562. //
  9563. // Description: Sends one UPnP search message via the given device locally
  9564. // and if fRemoteAllowed is TRUE, to the multicast or gateway
  9565. // address.
  9566. //
  9567. // The object lock is assumed to be held.
  9568. //
  9569. // Arguments:
  9570. // CDevice * pDevice - Pointer to device to use.
  9571. // BOOL fRemoteAllowed - Whether we can search remotely or not.
  9572. //
  9573. // Returns: HRESULT
  9574. // DPNH_OK - The messages were sent successfully.
  9575. // DPNHERR_GENERIC - An error occurred.
  9576. //=============================================================================
  9577. HRESULT CNATHelpUPnP::SendUPnPSearchMessagesForDevice(CDevice * const pDevice,
  9578. const BOOL fRemoteAllowed)
  9579. {
  9580. HRESULT hr = DPNH_OK;
  9581. SOCKADDR_IN saddrinRemote;
  9582. SOCKADDR_IN saddrinLocal;
  9583. BOOL fTryRemote;
  9584. int iWANIPConnectionMsgSize;
  9585. int iWANPPPConnectionMsgSize;
  9586. int iReturn;
  9587. SOCKET sTemp = INVALID_SOCKET;
  9588. #ifdef DBG
  9589. DWORD dwError;
  9590. #endif // DBG
  9591. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)", this, pDevice, fRemoteAllowed);
  9592. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  9593. DNASSERT(pDevice->GetUPnPDiscoverySocket() != INVALID_SOCKET);
  9594. ZeroMemory(&saddrinRemote, sizeof(saddrinRemote));
  9595. saddrinRemote.sin_family = AF_INET;
  9596. //saddrinRemote.sin_addr.S_un.S_addr = ?
  9597. saddrinRemote.sin_port = HTONS(UPNP_PORT);
  9598. //
  9599. // If we're allowed to try remotely, use the gateway's address, or the
  9600. // multicast address, as appropriate.
  9601. //
  9602. if ((fRemoteAllowed) && (! pDevice->GotRemoteUPnPDiscoveryConnReset()))
  9603. {
  9604. if (g_fUseMulticastUPnPDiscovery)
  9605. {
  9606. saddrinRemote.sin_addr.S_un.S_addr = this->m_pfninet_addr(UPNP_DISCOVERY_MULTICAST_ADDRESS);
  9607. fTryRemote = TRUE;
  9608. }
  9609. else
  9610. {
  9611. //
  9612. // Try to get the device's gateway's address. This might return FALSE
  9613. // if the device does not have a gateway. In that case, we will ignore
  9614. // the device. Otherwise the address should be filled in with the
  9615. // gateway or broadcast address.
  9616. //
  9617. fTryRemote = this->GetAddressToReachGateway(pDevice,
  9618. &saddrinRemote.sin_addr);
  9619. }
  9620. }
  9621. else
  9622. {
  9623. fTryRemote = FALSE;
  9624. }
  9625. ZeroMemory(&saddrinLocal, sizeof(saddrinLocal));
  9626. saddrinLocal.sin_family = AF_INET;
  9627. saddrinLocal.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  9628. saddrinLocal.sin_port = HTONS(UPNP_PORT);
  9629. //
  9630. // Note that these message strings contain:
  9631. //
  9632. // HOST: multicast_addr:port
  9633. //
  9634. // even though we send the messages to addresses other than the multicast
  9635. // address. It shouldn't matter.
  9636. //
  9637. iWANIPConnectionMsgSize = strlen(c_szUPnPMsg_Discover_Service_WANIPConnection);
  9638. iWANPPPConnectionMsgSize = strlen(c_szUPnPMsg_Discover_Service_WANPPPConnection);
  9639. #ifdef DBG
  9640. this->PrintUPnPTransactionToFile(c_szUPnPMsg_Discover_Service_WANIPConnection,
  9641. iWANIPConnectionMsgSize,
  9642. "Outbound WANIPConnection discovery messages",
  9643. pDevice);
  9644. this->PrintUPnPTransactionToFile(c_szUPnPMsg_Discover_Service_WANPPPConnection,
  9645. iWANPPPConnectionMsgSize,
  9646. "Outbound WANPPPConnection discovery messages",
  9647. pDevice);
  9648. #endif // DBG
  9649. DNASSERT(pDevice->GetUPnPDiscoverySocket() != INVALID_SOCKET);
  9650. //
  9651. // First, fire off messages to the remote gateway, if possible.
  9652. //
  9653. if (fTryRemote)
  9654. {
  9655. DPFX(DPFPREP, 7, "Sending UPnP discovery messages (WANIPConnection and WANPPPConnection) to gateway/multicast %u.%u.%u.%u:%u via device 0x%p.",
  9656. saddrinRemote.sin_addr.S_un.S_un_b.s_b1,
  9657. saddrinRemote.sin_addr.S_un.S_un_b.s_b2,
  9658. saddrinRemote.sin_addr.S_un.S_un_b.s_b3,
  9659. saddrinRemote.sin_addr.S_un.S_un_b.s_b4,
  9660. NTOHS(saddrinRemote.sin_port),
  9661. pDevice);
  9662. //
  9663. // Remember that we're trying remotely.
  9664. //
  9665. pDevice->NotePerformingRemoteUPnPDiscovery();
  9666. //
  9667. // Remember the current time, if this is the first thing we've sent
  9668. // from this port.
  9669. //
  9670. if (pDevice->GetFirstUPnPDiscoveryTime() == 0)
  9671. {
  9672. pDevice->SetFirstUPnPDiscoveryTime(GETTIMESTAMP());
  9673. }
  9674. //
  9675. // Multicast/send to gateway a WANIPConnection discovery message.
  9676. //
  9677. iReturn = this->m_pfnsendto(pDevice->GetUPnPDiscoverySocket(),
  9678. c_szUPnPMsg_Discover_Service_WANIPConnection,
  9679. iWANIPConnectionMsgSize,
  9680. 0,
  9681. (SOCKADDR*) (&saddrinRemote),
  9682. sizeof(saddrinRemote));
  9683. if (iReturn == SOCKET_ERROR)
  9684. {
  9685. #ifdef DBG
  9686. dwError = this->m_pfnWSAGetLastError();
  9687. DPFX(DPFPREP, 0, "Got sockets error %u when sending WANIPConnection discovery to UPnP gateway/multicast address on device 0x%p! Ignoring.",
  9688. dwError, pDevice);
  9689. #endif // DBG
  9690. //
  9691. // It's possible that we caught WinSock at a bad time,
  9692. // particularly with WSAEADDRNOTAVAIL (10049), which seems to
  9693. // occur if the address is going away (and we haven't detected
  9694. // it in CheckForNewDevices yet).
  9695. //
  9696. // Ignore the error, we can survive.
  9697. //
  9698. }
  9699. else
  9700. {
  9701. if (iReturn != iWANIPConnectionMsgSize)
  9702. {
  9703. DPFX(DPFPREP, 0, "Didn't multicast send entire WANIPConnection discovery datagram on device 0x%p (%i != %i)?!",
  9704. pDevice, iReturn, iWANIPConnectionMsgSize);
  9705. DNASSERT(FALSE);
  9706. hr = DPNHERR_GENERIC;
  9707. goto Failure;
  9708. }
  9709. }
  9710. //
  9711. // Multicast/send to gateway a WANPPPConnection discovery message.
  9712. //
  9713. iReturn = this->m_pfnsendto(pDevice->GetUPnPDiscoverySocket(),
  9714. c_szUPnPMsg_Discover_Service_WANPPPConnection,
  9715. iWANPPPConnectionMsgSize,
  9716. 0,
  9717. (SOCKADDR*) (&saddrinRemote),
  9718. sizeof(saddrinRemote));
  9719. if (iReturn == SOCKET_ERROR)
  9720. {
  9721. #ifdef DBG
  9722. dwError = this->m_pfnWSAGetLastError();
  9723. DPFX(DPFPREP, 0, "Got sockets error %u when sending WANPPPConnection discovery to UPnP multicast/gateway address on device 0x%p! Ignoring.",
  9724. dwError, pDevice);
  9725. #endif // DBG
  9726. //
  9727. // It's possible that we caught WinSock at a bad time,
  9728. // particularly with WSAEADDRNOTAVAIL (10049), which seems to
  9729. // occur if the address is going away (and we haven't detected
  9730. // it in CheckForNewDevices yet).
  9731. //
  9732. // Ignore the error, we can survive.
  9733. //
  9734. }
  9735. else
  9736. {
  9737. if (iReturn != iWANPPPConnectionMsgSize)
  9738. {
  9739. DPFX(DPFPREP, 0, "Didn't multicast send entire WANPPPConnection discovery datagram on device 0x%p (%i != %i)?!",
  9740. pDevice, iReturn, iWANPPPConnectionMsgSize);
  9741. DNASSERT(FALSE);
  9742. hr = DPNHERR_GENERIC;
  9743. goto Failure;
  9744. }
  9745. }
  9746. }
  9747. else
  9748. {
  9749. DPFX(DPFPREP, 2, "Device 0x%p should not attempt to reach a remote gateway.",
  9750. pDevice);
  9751. //
  9752. // Remember that we're not trying remotely.
  9753. //
  9754. pDevice->NoteNotPerformingRemoteUPnPDiscovery();
  9755. }
  9756. //
  9757. // If we didn't already get a CONNRESET from a previous attempt, try to
  9758. // bind a socket locally to the UPnP discovery port. If it's not in use,
  9759. // then we know nobody will be listening so there's no point in trying the
  9760. // local address. If it is in use, then this computer might be a UPnP
  9761. // gateway itself.
  9762. //
  9763. if (! pDevice->GotLocalUPnPDiscoveryConnReset())
  9764. {
  9765. sTemp = this->m_pfnsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
  9766. if (sTemp == INVALID_SOCKET)
  9767. {
  9768. #ifdef DBG
  9769. dwError = this->m_pfnWSAGetLastError();
  9770. DPFX(DPFPREP, 0, "Couldn't create temporary datagram socket, error = %u!", dwError);
  9771. #endif // DBG
  9772. hr = DPNHERR_GENERIC;
  9773. goto Failure;
  9774. }
  9775. if (this->m_pfnbind(sTemp,
  9776. (SOCKADDR *) (&saddrinLocal),
  9777. sizeof(saddrinLocal)) != 0)
  9778. {
  9779. #ifdef DBG
  9780. dwError = this->m_pfnWSAGetLastError();
  9781. DPFX(DPFPREP, 2, "Couldn't bind socket to UPnP discovery port (%u.%u.%u.%u:%u), assuming local device (error = %u).",
  9782. saddrinLocal.sin_addr.S_un.S_un_b.s_b1,
  9783. saddrinLocal.sin_addr.S_un.S_un_b.s_b2,
  9784. saddrinLocal.sin_addr.S_un.S_un_b.s_b3,
  9785. saddrinLocal.sin_addr.S_un.S_un_b.s_b4,
  9786. NTOHS(saddrinLocal.sin_port),
  9787. dwError);
  9788. #endif // DBG
  9789. //
  9790. // Remember that we're trying locally.
  9791. //
  9792. pDevice->NotePerformingLocalUPnPDiscovery();
  9793. //
  9794. // Remember the current time, if this is the first thing we've sent
  9795. // from this port.
  9796. //
  9797. if (pDevice->GetFirstUPnPDiscoveryTime() == 0)
  9798. {
  9799. pDevice->SetFirstUPnPDiscoveryTime(GETTIMESTAMP());
  9800. }
  9801. //
  9802. // Do WANIPConnection first.
  9803. //
  9804. DPFX(DPFPREP, 7, "Sending UPnP discovery messages (WANIPConnection and WANPPPConnection) locally to device 0x%p (address %u.%u.%u.%u:%u).",
  9805. pDevice,
  9806. saddrinLocal.sin_addr.S_un.S_un_b.s_b1,
  9807. saddrinLocal.sin_addr.S_un.S_un_b.s_b2,
  9808. saddrinLocal.sin_addr.S_un.S_un_b.s_b3,
  9809. saddrinLocal.sin_addr.S_un.S_un_b.s_b4,
  9810. NTOHS(saddrinLocal.sin_port));
  9811. iReturn = this->m_pfnsendto(pDevice->GetUPnPDiscoverySocket(),
  9812. c_szUPnPMsg_Discover_Service_WANIPConnection,
  9813. iWANIPConnectionMsgSize,
  9814. 0,
  9815. (SOCKADDR*) (&saddrinLocal),
  9816. sizeof(saddrinLocal));
  9817. if (iReturn == SOCKET_ERROR)
  9818. {
  9819. #ifdef DBG
  9820. dwError = this->m_pfnWSAGetLastError();
  9821. DPFX(DPFPREP, 0, "Got sockets error %u when sending WANIPConnection discovery to local address on device 0x%p! Ignoring.",
  9822. dwError, pDevice);
  9823. #endif // DBG
  9824. //
  9825. // It's possible that we caught WinSock at a bad time,
  9826. // particularly with WSAEADDRNOTAVAIL (10049), which seems to
  9827. // occur if the address is going away (and we haven't detected
  9828. // it in CheckForNewDevices yet).
  9829. //
  9830. // Ignore the error, we can survive.
  9831. //
  9832. }
  9833. else
  9834. {
  9835. if (iReturn != iWANIPConnectionMsgSize)
  9836. {
  9837. DPFX(DPFPREP, 0, "Didn't send entire WANIPConnection discovery datagram locally on device 0x%p (%i != %i)?!",
  9838. pDevice, iReturn, iWANIPConnectionMsgSize);
  9839. DNASSERT(FALSE);
  9840. hr = DPNHERR_GENERIC;
  9841. goto Failure;
  9842. }
  9843. }
  9844. //
  9845. // Now send WANPPPConnection discovery message locally.
  9846. //
  9847. iReturn = this->m_pfnsendto(pDevice->GetUPnPDiscoverySocket(),
  9848. c_szUPnPMsg_Discover_Service_WANPPPConnection,
  9849. iWANPPPConnectionMsgSize,
  9850. 0,
  9851. (SOCKADDR*) (&saddrinLocal),
  9852. sizeof(saddrinLocal));
  9853. if (iReturn == SOCKET_ERROR)
  9854. {
  9855. #ifdef DBG
  9856. dwError = this->m_pfnWSAGetLastError();
  9857. DPFX(DPFPREP, 0, "Got sockets error %u when sending WANPPPConnection discovery to local address on device 0x%p! Ignoring.",
  9858. dwError, pDevice);
  9859. #endif // DBG
  9860. //
  9861. // It's possible that we caught WinSock at a bad time,
  9862. // particularly with WSAEADDRNOTAVAIL (10049), which seems to
  9863. // occur if the address is going away (and we haven't detected
  9864. // it in CheckForNewDevices yet).
  9865. //
  9866. // Ignore the error, we can survive.
  9867. //
  9868. }
  9869. else
  9870. {
  9871. if (iReturn != iWANPPPConnectionMsgSize)
  9872. {
  9873. DPFX(DPFPREP, 0, "Didn't send entire WANPPPConnection discovery datagram locally on device 0x%p (%i != %i)?!",
  9874. pDevice, iReturn, iWANPPPConnectionMsgSize);
  9875. DNASSERT(FALSE);
  9876. hr = DPNHERR_GENERIC;
  9877. goto Failure;
  9878. }
  9879. }
  9880. }
  9881. else
  9882. {
  9883. DPFX(DPFPREP, 2, "Successfully bound socket to UPnP discovery port (%u.%u.%u.%u:%u), assuming no local UPnP device.",
  9884. saddrinLocal.sin_addr.S_un.S_un_b.s_b1,
  9885. saddrinLocal.sin_addr.S_un.S_un_b.s_b2,
  9886. saddrinLocal.sin_addr.S_un.S_un_b.s_b3,
  9887. saddrinLocal.sin_addr.S_un.S_un_b.s_b4,
  9888. NTOHS(saddrinLocal.sin_port));
  9889. //
  9890. // Remember that we're not trying locally.
  9891. //
  9892. pDevice->NoteNotPerformingLocalUPnPDiscovery();
  9893. }
  9894. }
  9895. else
  9896. {
  9897. //
  9898. // We got a CONNRESET last time.
  9899. //
  9900. }
  9901. Exit:
  9902. if (sTemp != INVALID_SOCKET)
  9903. {
  9904. this->m_pfnclosesocket(sTemp);
  9905. sTemp = INVALID_SOCKET;
  9906. }
  9907. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  9908. return hr;
  9909. Failure:
  9910. goto Exit;
  9911. } // CNATHelpUPnP::SendUPnPSearchMessagesForDevice
  9912. #undef DPF_MODNAME
  9913. #define DPF_MODNAME "CNATHelpUPnP::SendUPnPDescriptionRequest"
  9914. //=============================================================================
  9915. // CNATHelpUPnP::SendUPnPDescriptionRequest
  9916. //-----------------------------------------------------------------------------
  9917. //
  9918. // Description: Requests a description from the given UPnP device.
  9919. //
  9920. // The object lock is assumed to be held.
  9921. //
  9922. // Arguments:
  9923. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device to use.
  9924. //
  9925. // Returns: HRESULT
  9926. // DPNH_OK - The message was sent successfully.
  9927. // DPNHERR_GENERIC - An error occurred.
  9928. //=============================================================================
  9929. HRESULT CNATHelpUPnP::SendUPnPDescriptionRequest(CUPnPDevice * const pUPnPDevice)
  9930. {
  9931. HRESULT hr = DPNH_OK;
  9932. SOCKADDR_IN * psaddrinHost;
  9933. TCHAR tszHost[22]; // "xxx.xxx.xxx.xxx:xxxxx" + NULL termination
  9934. char * pszMessage = NULL;
  9935. int iMsgSize;
  9936. int iReturn;
  9937. #ifdef DBG
  9938. DWORD dwError;
  9939. #endif // DBG
  9940. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pUPnPDevice);
  9941. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  9942. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  9943. DNASSERT(pUPnPDevice->IsConnected());
  9944. DNASSERT(pUPnPDevice->GetLocationURL() != NULL);
  9945. psaddrinHost = pUPnPDevice->GetHostAddress();
  9946. wsprintf(tszHost, _T("%u.%u.%u.%u:%u"),
  9947. psaddrinHost->sin_addr.S_un.S_un_b.s_b1,
  9948. psaddrinHost->sin_addr.S_un.S_un_b.s_b2,
  9949. psaddrinHost->sin_addr.S_un.S_un_b.s_b3,
  9950. psaddrinHost->sin_addr.S_un.S_un_b.s_b4,
  9951. NTOHS(psaddrinHost->sin_port));
  9952. iMsgSize = strlen("GET ") + strlen(pUPnPDevice->GetLocationURL()) + strlen(" " HTTP_VERSION EOL)
  9953. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  9954. + strlen("ACCEPT-LANGUAGE: en" EOL)
  9955. + strlen(EOL);
  9956. pszMessage = (char*) DNMalloc(iMsgSize + 1); // include room for NULL termination that isn't actually sent
  9957. if (pszMessage == NULL)
  9958. {
  9959. hr = DPNHERR_OUTOFMEMORY;
  9960. goto Failure;
  9961. }
  9962. strcpy(pszMessage, "GET ");
  9963. strcat(pszMessage, pUPnPDevice->GetLocationURL());
  9964. strcat(pszMessage, " " HTTP_VERSION EOL);
  9965. strcat(pszMessage, "HOST: ");
  9966. #ifdef UNICODE
  9967. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  9968. tszHost,
  9969. (_tcslen(tszHost) + 1));
  9970. #else // ! UNICODE
  9971. strcat(pszMessage, tszHost);
  9972. #endif // ! UNICODE
  9973. strcat(pszMessage, EOL);
  9974. strcat(pszMessage, "ACCEPT-LANGUAGE: en" EOL);
  9975. strcat(pszMessage, EOL);
  9976. #ifdef DBG
  9977. this->PrintUPnPTransactionToFile(pszMessage,
  9978. iMsgSize,
  9979. "Outbound description request",
  9980. pUPnPDevice->GetOwningDevice());
  9981. #endif // DBG
  9982. iReturn = this->m_pfnsend(pUPnPDevice->GetControlSocket(),
  9983. pszMessage,
  9984. iMsgSize,
  9985. 0);
  9986. if (iReturn == SOCKET_ERROR)
  9987. {
  9988. #ifdef DBG
  9989. dwError = this->m_pfnWSAGetLastError();
  9990. DPFX(DPFPREP, 0, "Got sockets error %u when sending to UPnP device!", dwError);
  9991. #endif // DBG
  9992. hr = DPNHERR_GENERIC;
  9993. goto Failure;
  9994. }
  9995. if (iReturn != iMsgSize)
  9996. {
  9997. DPFX(DPFPREP, 0, "Didn't send entire message (%i != %i)?!", iReturn, iMsgSize);
  9998. DNASSERT(FALSE);
  9999. hr = DPNHERR_GENERIC;
  10000. goto Failure;
  10001. }
  10002. Exit:
  10003. if (pszMessage != NULL)
  10004. {
  10005. DNFree(pszMessage);
  10006. pszMessage = NULL;
  10007. }
  10008. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  10009. return hr;
  10010. Failure:
  10011. goto Exit;
  10012. } // CNATHelpUPnP::SendUPnPDescriptionRequest
  10013. #undef DPF_MODNAME
  10014. #define DPF_MODNAME "CNATHelpUPnP::UpdateUPnPExternalAddress"
  10015. //=============================================================================
  10016. // CNATHelpUPnP::UpdateUPnPExternalAddress
  10017. //-----------------------------------------------------------------------------
  10018. //
  10019. // Description: Retreives the current external address for the given UPnP
  10020. // Internet Gateway Device.
  10021. //
  10022. // The UPnP device may get removed from list if a failure
  10023. // occurs, the caller needs to have a reference.
  10024. //
  10025. // The object lock is assumed to be held.
  10026. //
  10027. // Arguments:
  10028. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device that should be
  10029. // updated.
  10030. // BOOL fUpdateRegisteredPorts - TRUE if existing registered ports should be
  10031. // updated to reflect the new address if it
  10032. // changed, FALSE if not.
  10033. //
  10034. // Returns: HRESULT
  10035. // DPNH_OK - The update was successful.
  10036. // DPNHERR_GENERIC - An error occurred.
  10037. // DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
  10038. // message.
  10039. //=============================================================================
  10040. HRESULT CNATHelpUPnP::UpdateUPnPExternalAddress(CUPnPDevice * const pUPnPDevice,
  10041. const BOOL fUpdateRegisteredPorts)
  10042. {
  10043. HRESULT hr;
  10044. BOOL fStartedWaitingForControlResponse = FALSE;
  10045. CDevice * pDevice;
  10046. SOCKADDR_IN * psaddrinTemp;
  10047. int iContentLength;
  10048. TCHAR tszContentLength[32];
  10049. TCHAR tszHost[22]; // "xxx.xxx.xxx.xxx:xxxxx" + NULL termination
  10050. char * pszMessage = NULL;
  10051. int iMsgSize;
  10052. int iPrevMsgSize = 0;
  10053. int iReturn;
  10054. UPNP_CONTROLRESPONSE_INFO RespInfo;
  10055. DWORD dwStartTime;
  10056. DWORD dwTimeout;
  10057. CBilink * pBilink;
  10058. CRegisteredPort * pRegisteredPort;
  10059. #ifdef DBG
  10060. DWORD dwError;
  10061. #endif // DBG
  10062. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i)",
  10063. this, pUPnPDevice, fUpdateRegisteredPorts);
  10064. DNASSERT(pUPnPDevice != NULL);
  10065. DNASSERT(pUPnPDevice->IsReady());
  10066. pDevice = pUPnPDevice->GetOwningDevice();
  10067. DNASSERT(pDevice != NULL);
  10068. DNASSERT(this->m_dwFlags & (NATHELPUPNPOBJ_INITIALIZED | NATHELPUPNPOBJ_USEUPNP));
  10069. DNASSERT(pUPnPDevice->GetServiceControlURL() != NULL);
  10070. //
  10071. // If the control socket got disconnected after the last message, then
  10072. // reconnect.
  10073. //
  10074. if (! pUPnPDevice->IsConnected())
  10075. {
  10076. hr = this->ReconnectUPnPControlSocket(pUPnPDevice);
  10077. if (hr != S_OK)
  10078. {
  10079. DPFX(DPFPREP, 0, "Couldn't reconnect UPnP control socket!");
  10080. goto Failure;
  10081. }
  10082. }
  10083. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  10084. psaddrinTemp = pUPnPDevice->GetHostAddress();
  10085. wsprintf(tszHost, _T("%u.%u.%u.%u:%u"),
  10086. psaddrinTemp->sin_addr.S_un.S_un_b.s_b1,
  10087. psaddrinTemp->sin_addr.S_un.S_un_b.s_b2,
  10088. psaddrinTemp->sin_addr.S_un.S_un_b.s_b3,
  10089. psaddrinTemp->sin_addr.S_un.S_un_b.s_b4,
  10090. NTOHS(psaddrinTemp->sin_port));
  10091. /*
  10092. iContentLength = strlen("<s:Envelope" EOL)
  10093. + strlen(" xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL)
  10094. + strlen(" s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL)
  10095. + strlen(" <s:Body>" EOL)
  10096. + strlen(" <u:" CONTROL_QUERYSTATEVARIABLE_A " xmlns:u=\"" URI_CONTROL_A "\">" EOL)
  10097. + strlen(" <u:" ARG_CONTROL_VARNAME_A ">" VAR_EXTERNALIPADDRESS_A "</u:" ARG_CONTROL_VARNAME_A ">" EOL)
  10098. + strlen(" </u:" CONTROL_QUERYSTATEVARIABLE_A ">" EOL)
  10099. + strlen(" </s:Body>" EOL)
  10100. + strlen("</s:Envelope>" EOL)
  10101. + strlen(EOL);
  10102. wsprintf(tszContentLength, _T("%i"), iContentLength);
  10103. iMsgSize = strlen("POST ") + strlen(pUPnPDevice->GetServiceControlURL()) + strlen(" " HTTP_VERSION EOL)
  10104. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  10105. + strlen("CONTENT-LENGTH: ") + strlen(szContentLength) + strlen(EOL)
  10106. + strlen("CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL)
  10107. + strlen("SOAPACTION: " URI_CONTROL_A "#" CONTROL_QUERYSTATEVARIABLE_A "" EOL)
  10108. + strlen(EOL)
  10109. + iContentLength;
  10110. */
  10111. iContentLength = strlen("<s:Envelope" EOL)
  10112. + strlen(" xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL)
  10113. + strlen(" s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL)
  10114. + strlen(" <s:Body>" EOL)
  10115. + strlen(" <u:" ACTION_GETEXTERNALIPADDRESS_A " xmlns:u=\"") + pUPnPDevice->GetStaticServiceURILength() + strlen("\">" EOL)
  10116. + strlen(" </u:" ACTION_GETEXTERNALIPADDRESS_A ">" EOL)
  10117. + strlen(" </s:Body>" EOL)
  10118. + strlen("</s:Envelope>" EOL)
  10119. + strlen(EOL);
  10120. wsprintf(tszContentLength, _T("%i"), iContentLength);
  10121. iMsgSize = strlen("POST ") + strlen(pUPnPDevice->GetServiceControlURL()) + strlen(" " HTTP_VERSION EOL)
  10122. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  10123. + strlen("CONTENT-LENGTH: ") + _tcslen(tszContentLength) + strlen(EOL)
  10124. + strlen("CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL)
  10125. + strlen("SOAPACTION: ") + pUPnPDevice->GetStaticServiceURILength() + strlen("#" ACTION_GETEXTERNALIPADDRESS_A EOL)
  10126. + strlen(EOL)
  10127. + iContentLength;
  10128. //
  10129. // Allocate (or reallocate) the message buffer.
  10130. //
  10131. if (iMsgSize > iPrevMsgSize)
  10132. {
  10133. if (pszMessage != NULL)
  10134. {
  10135. DNFree(pszMessage);
  10136. pszMessage = NULL;
  10137. }
  10138. pszMessage = (char*) DNMalloc(iMsgSize + 1); // include room for NULL termination that isn't actually sent
  10139. if (pszMessage == NULL)
  10140. {
  10141. hr = DPNHERR_OUTOFMEMORY;
  10142. goto Failure;
  10143. }
  10144. iPrevMsgSize = iMsgSize;
  10145. }
  10146. /*
  10147. strcpy(pszMessage, "POST ");
  10148. strcat(pszMessage, pUPnPDevice->GetServiceControlURL());
  10149. strcat(pszMessage, " " HTTP_VERSION EOL);
  10150. strcat(pszMessage, "HOST: ");
  10151. #ifdef UNICODE
  10152. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10153. tszHost,
  10154. (_tcslen(tszHost) + 1));
  10155. #else // ! UNICODE
  10156. strcat(pszMessage, tszHost);
  10157. #endif // ! UNICODE
  10158. strcat(pszMessage, EOL);
  10159. strcat(pszMessage, "CONTENT-LENGTH: ");
  10160. #ifdef UNICODE
  10161. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10162. tszContentLength,
  10163. (_tcslen(tszContentLength) + 1));
  10164. #else // ! UNICODE
  10165. strcat(pszMessage, tszContentLength);
  10166. #endif // ! UNICODE
  10167. strcat(pszMessage, EOL);
  10168. strcat(pszMessage, "CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL);
  10169. strcat(pszMessage, "SOAPACTION: " URI_CONTROL_A "#" CONTROL_QUERYSTATEVARIABLE_A "" EOL);
  10170. strcat(pszMessage, EOL);
  10171. strcat(pszMessage, "<s:Envelope" EOL);
  10172. strcat(pszMessage, " xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL);
  10173. strcat(pszMessage, " s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL);
  10174. strcat(pszMessage, " <s:Body>" EOL);
  10175. strcat(pszMessage, " <u:" CONTROL_QUERYSTATEVARIABLE_A " xmlns:u=\"" URI_CONTROL_A "\">" EOL);
  10176. strcat(pszMessage, " <u:" ARG_CONTROL_VARNAME_A ">" VAR_EXTERNALIPADDRESS_A "</u:" ARG_CONTROL_VARNAME_A ">" EOL);
  10177. strcat(pszMessage, " </u:" CONTROL_QUERYSTATEVARIABLE_A ">" EOL);
  10178. strcat(pszMessage, " </s:Body>" EOL);
  10179. strcat(pszMessage, "</s:Envelope>" EOL);
  10180. strcat(pszMessage, EOL);
  10181. */
  10182. strcpy(pszMessage, "POST ");
  10183. strcat(pszMessage, pUPnPDevice->GetServiceControlURL());
  10184. strcat(pszMessage, " " HTTP_VERSION EOL);
  10185. strcat(pszMessage, "HOST: ");
  10186. #ifdef UNICODE
  10187. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10188. tszHost,
  10189. (_tcslen(tszHost) + 1));
  10190. #else // ! UNICODE
  10191. strcat(pszMessage, tszHost);
  10192. #endif // ! UNICODE
  10193. strcat(pszMessage, EOL);
  10194. strcat(pszMessage, "CONTENT-LENGTH: ");
  10195. #ifdef UNICODE
  10196. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10197. tszContentLength,
  10198. (_tcslen(tszContentLength) + 1));
  10199. #else // ! UNICODE
  10200. strcat(pszMessage, tszContentLength);
  10201. #endif // ! UNICODE
  10202. strcat(pszMessage, EOL);
  10203. strcat(pszMessage, "CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL);
  10204. strcat(pszMessage, "SOAPACTION: ");
  10205. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  10206. strcat(pszMessage, "#" ACTION_GETEXTERNALIPADDRESS_A EOL);
  10207. strcat(pszMessage, EOL);
  10208. strcat(pszMessage, "<s:Envelope" EOL);
  10209. strcat(pszMessage, " xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL);
  10210. strcat(pszMessage, " s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL);
  10211. strcat(pszMessage, " <s:Body>" EOL);
  10212. strcat(pszMessage, " <u:" ACTION_GETEXTERNALIPADDRESS_A " xmlns:u=\"");
  10213. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  10214. strcat(pszMessage, "\">" EOL);
  10215. strcat(pszMessage, " </u:" ACTION_GETEXTERNALIPADDRESS_A ">" EOL);
  10216. strcat(pszMessage, " </s:Body>" EOL);
  10217. strcat(pszMessage, "</s:Envelope>" EOL);
  10218. strcat(pszMessage, EOL);
  10219. #ifdef DBG
  10220. this->PrintUPnPTransactionToFile(pszMessage,
  10221. iMsgSize,
  10222. //"Outbound query external IP address",
  10223. "Outbound get external IP address",
  10224. pDevice);
  10225. #endif // DBG
  10226. iReturn = this->m_pfnsend(pUPnPDevice->GetControlSocket(),
  10227. pszMessage,
  10228. iMsgSize,
  10229. 0);
  10230. if (iReturn == SOCKET_ERROR)
  10231. {
  10232. #ifdef DBG
  10233. dwError = this->m_pfnWSAGetLastError();
  10234. DPFX(DPFPREP, 0, "Got sockets error %u when sending control request to UPnP device!", dwError);
  10235. #endif // DBG
  10236. hr = DPNHERR_GENERIC;
  10237. goto Failure;
  10238. }
  10239. if (iReturn != iMsgSize)
  10240. {
  10241. DPFX(DPFPREP, 0, "Didn't send entire message (%i != %i)?!", iReturn, iMsgSize);
  10242. DNASSERT(FALSE);
  10243. hr = DPNHERR_GENERIC;
  10244. goto Failure;
  10245. }
  10246. //
  10247. // We have the lock so no one could have tried to receive data from the
  10248. // control socket yet. Mark the device as waiting for a response.
  10249. //
  10250. ZeroMemory(&RespInfo, sizeof(RespInfo));
  10251. //pUPnPDevice->StartWaitingForControlResponse(CONTROLRESPONSETYPE_QUERYSTATEVARIABLE_EXTERNALIPADDRESS,
  10252. pUPnPDevice->StartWaitingForControlResponse(CONTROLRESPONSETYPE_GETEXTERNALIPADDRESS,
  10253. &RespInfo);
  10254. fStartedWaitingForControlResponse = TRUE;
  10255. //
  10256. // Actually wait for the response.
  10257. //
  10258. dwStartTime = GETTIMESTAMP();
  10259. dwTimeout = g_dwUPnPResponseTimeout;
  10260. do
  10261. {
  10262. hr = this->CheckForReceivedUPnPMsgsOnDevice(pUPnPDevice, dwTimeout);
  10263. if (hr != DPNH_OK)
  10264. {
  10265. DPFX(DPFPREP, 0, "Failed receiving UPnP messages!");
  10266. goto Failure;
  10267. }
  10268. //
  10269. // We either timed out or got some data. Check if we got a
  10270. // response of some type.
  10271. //
  10272. if (! pUPnPDevice->IsWaitingForControlResponse())
  10273. {
  10274. break;
  10275. }
  10276. //
  10277. // Make sure our device is still connected.
  10278. //
  10279. if (! pUPnPDevice->IsConnected())
  10280. {
  10281. DPFX(DPFPREP, 0, "UPnP device 0x%p disconnected while retrieving external IP address!",
  10282. pUPnPDevice);
  10283. pUPnPDevice->StopWaitingForControlResponse();
  10284. hr = DPNHERR_SERVERNOTRESPONDING;
  10285. goto Failure;
  10286. }
  10287. //
  10288. // Calculate how long we have left to wait. If the calculation
  10289. // goes negative, it means we're done.
  10290. //
  10291. dwTimeout = g_dwUPnPResponseTimeout - (GETTIMESTAMP() - dwStartTime);
  10292. }
  10293. while (((int) dwTimeout > 0));
  10294. //
  10295. // If we never got the response, stop waiting for it.
  10296. //
  10297. if (pUPnPDevice->IsWaitingForControlResponse())
  10298. {
  10299. pUPnPDevice->StopWaitingForControlResponse();
  10300. DPFX(DPFPREP, 0, "Server didn't respond in time!");
  10301. hr = DPNHERR_SERVERNOTRESPONDING;
  10302. goto Failure;
  10303. }
  10304. //
  10305. // If we're here, then we've gotten a valid response from the server.
  10306. //
  10307. if (RespInfo.hrErrorCode != DPNH_OK)
  10308. {
  10309. DPFX(DPFPREP, 1, "Server returned failure response 0x%lx when retrieving external IP address.",
  10310. RespInfo.hrErrorCode);
  10311. hr = RespInfo.hrErrorCode;
  10312. goto Failure;
  10313. }
  10314. DPFX(DPFPREP, 1, "Server returned external IP address \"%u.%u.%u.%u\".",
  10315. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b1,
  10316. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b2,
  10317. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b3,
  10318. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b4);
  10319. //
  10320. // Convert the loopback address to the device address.
  10321. //
  10322. if (RespInfo.dwExternalIPAddressV4 == NETWORKBYTEORDER_INADDR_LOOPBACK)
  10323. {
  10324. RespInfo.dwExternalIPAddressV4 = pDevice->GetLocalAddressV4();
  10325. DPFX(DPFPREP, 0, "Converted private loopback address to device address (%u.%u.%u.%u)!",
  10326. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b1,
  10327. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b2,
  10328. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b3,
  10329. ((IN_ADDR*) (&RespInfo.dwExternalIPAddressV4))->S_un.S_un_b.s_b4);
  10330. DNASSERTX(! "Got loopback address as external IP address!", 2);
  10331. }
  10332. #ifdef DBG
  10333. //
  10334. // If this is a local UPnP gateway, print out the device corresponding to
  10335. // the public address.
  10336. //
  10337. if (pUPnPDevice->IsLocal())
  10338. {
  10339. if (RespInfo.dwExternalIPAddressV4 != 0)
  10340. {
  10341. CDevice * pPublicDevice;
  10342. //
  10343. // Loop through every device.
  10344. //
  10345. pBilink = this->m_blDevices.GetNext();
  10346. while (pBilink != &this->m_blDevices)
  10347. {
  10348. pPublicDevice = DEVICE_FROM_BILINK(pBilink);
  10349. if (pPublicDevice->GetLocalAddressV4() == RespInfo.dwExternalIPAddressV4)
  10350. {
  10351. DPFX(DPFPREP, 7, "Local UPnP gateway 0x%p for device 0x%p's public address is device 0x%p.",
  10352. pUPnPDevice, pDevice, pPublicDevice);
  10353. break;
  10354. }
  10355. pBilink = pBilink->GetNext();
  10356. }
  10357. //
  10358. // If we made it through the entire list without matching the device,
  10359. // that's odd. It's possible we're slow in detecting new devices, so
  10360. // don't get bent out of shape.
  10361. //
  10362. if (pBilink == &this->m_blDevices)
  10363. {
  10364. DPFX(DPFPREP, 0, "Couldn't match up local UPnP gateway 0x%p (device 0x%p)'s public address to a device!",
  10365. pUPnPDevice, pDevice);
  10366. DNASSERTX(! "Couldn't match up local UPnP gateway public address to a device!", 2);
  10367. }
  10368. }
  10369. else
  10370. {
  10371. DPFX(DPFPREP, 4, "Local UPnP gateway 0x%p (device 0x%p) does not have a valid public address.",
  10372. pUPnPDevice, pDevice);
  10373. }
  10374. }
  10375. #endif // DBG
  10376. //
  10377. // If the public address has changed, update all the existing mappings.
  10378. //
  10379. if (RespInfo.dwExternalIPAddressV4 != pUPnPDevice->GetExternalIPAddressV4())
  10380. {
  10381. DPFX(DPFPREP, 1, "UPnP Internet Gateway Device (0x%p) external address changed.",
  10382. pUPnPDevice);
  10383. //
  10384. // Since there was a change in the network, go back to polling
  10385. // relatively quickly.
  10386. //
  10387. this->ResetNextPollInterval();
  10388. //
  10389. // Loop through all the existing registered ports and update their
  10390. // public addresses, if allowed.
  10391. //
  10392. if (fUpdateRegisteredPorts)
  10393. {
  10394. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  10395. while (pBilink != &pDevice->m_blOwnedRegPorts)
  10396. {
  10397. DNASSERT(! pBilink->IsEmpty());
  10398. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  10399. if (! pRegisteredPort->IsUPnPPortUnavailable())
  10400. {
  10401. DPFX(DPFPREP, 7, "Updating registered port 0x%p's public address.",
  10402. pRegisteredPort);
  10403. pRegisteredPort->UpdateUPnPPublicV4Addresses(RespInfo.dwExternalIPAddressV4);
  10404. //
  10405. // The user should call GetCaps to detect the address change.
  10406. //
  10407. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  10408. }
  10409. else
  10410. {
  10411. DPFX(DPFPREP, 7, "Not updating registered port 0x%p's public address because the port is unavailable.",
  10412. pRegisteredPort);
  10413. }
  10414. pBilink = pBilink->GetNext();
  10415. }
  10416. }
  10417. //
  10418. // Store the new public address.
  10419. //
  10420. pUPnPDevice->SetExternalIPAddressV4(RespInfo.dwExternalIPAddressV4);
  10421. }
  10422. Exit:
  10423. if (pszMessage != NULL)
  10424. {
  10425. DNFree(pszMessage);
  10426. pszMessage = NULL;
  10427. }
  10428. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  10429. return hr;
  10430. Failure:
  10431. //
  10432. // If we started waiting for a response, clear that.
  10433. //
  10434. if (fStartedWaitingForControlResponse)
  10435. {
  10436. pUPnPDevice->StopWaitingForControlResponse();
  10437. }
  10438. goto Exit;
  10439. } // CNATHelpUPnP::UpdateUPnPExternalAddress
  10440. #undef DPF_MODNAME
  10441. #define DPF_MODNAME "CNATHelpUPnP::MapPortsOnUPnPDevice"
  10442. //=============================================================================
  10443. // CNATHelpUPnP::MapPortsOnUPnPDevice
  10444. //-----------------------------------------------------------------------------
  10445. //
  10446. // Description: Maps the given ports on the given UPnP device.
  10447. //
  10448. // The UPnP device may get removed from list if a failure
  10449. // occurs, the caller needs to have a reference.
  10450. //
  10451. // The object lock is assumed to be held.
  10452. //
  10453. // Arguments:
  10454. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device to use.
  10455. // CRegisteredPort * pRegisteredPort - Pointer to ports to register.
  10456. //
  10457. // Returns: HRESULT
  10458. // DPNH_OK - The message was sent successfully.
  10459. // DPNHERR_GENERIC - An error occurred.
  10460. // DPNHERR_SERVERNOTRESPONDING - A response didn't arrive in time.
  10461. //=============================================================================
  10462. HRESULT CNATHelpUPnP::MapPortsOnUPnPDevice(CUPnPDevice * const pUPnPDevice,
  10463. CRegisteredPort * const pRegisteredPort)
  10464. {
  10465. HRESULT hr = DPNH_OK;
  10466. HRESULT temphr;
  10467. BOOL fStartedWaitingForControlResponse = FALSE;
  10468. CDevice * pDevice;
  10469. DWORD dwLeaseExpiration;
  10470. IN_ADDR inaddrTemp;
  10471. SOCKADDR_IN * psaddrinTemp;
  10472. WORD wOriginalExternalPortHostOrder = 0;
  10473. WORD wExternalPortHostOrder;
  10474. TCHAR tszInternalPort[32];
  10475. TCHAR tszExternalPort[32];
  10476. TCHAR tszInternalClient[16]; // "xxx.xxx.xxx.xxx" + NULL termination
  10477. TCHAR tszLeaseDuration[32];
  10478. TCHAR tszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  10479. int iContentLength;
  10480. TCHAR tszContentLength[32];
  10481. TCHAR tszHost[22]; // "xxx.xxx.xxx.xxx:xxxxx" + NULL termination
  10482. char * pszMessage = NULL;
  10483. int iMsgSize;
  10484. int iPrevMsgSize = 0;
  10485. int iReturn;
  10486. DWORD dwTemp = 0;
  10487. UPNP_CONTROLRESPONSE_INFO RespInfo;
  10488. DWORD dwStartTime;
  10489. DWORD dwTimeout;
  10490. BOOL fFirstLease;
  10491. DWORD dwDescriptionLength;
  10492. #ifndef DPNBUILD_NOWINSOCK2
  10493. BOOL fResult;
  10494. #endif // ! DPNBUILD_NOWINSOCK2
  10495. #ifdef DBG
  10496. DWORD dwError;
  10497. #endif // DBG
  10498. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pUPnPDevice);
  10499. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  10500. DNASSERT(pUPnPDevice->IsReady());
  10501. pDevice = pRegisteredPort->GetOwningDevice();
  10502. DNASSERT(pDevice != NULL);
  10503. DNASSERT(pUPnPDevice->GetOwningDevice() == pDevice);
  10504. //
  10505. // Set up variables we'll need.
  10506. //
  10507. DNASSERT(pUPnPDevice->GetServiceControlURL() != NULL);
  10508. //
  10509. // If the port is shared, register the broadcast address instead of the
  10510. // local device address.
  10511. //
  10512. if (pRegisteredPort->IsSharedPort())
  10513. {
  10514. _tcscpy(tszInternalClient, _T("255.255.255.255"));
  10515. }
  10516. else
  10517. {
  10518. //
  10519. // Note that the device address is not necessarily the same as the
  10520. // address the user originally registered, particularly the 0.0.0.0
  10521. // wildcard address will get remapped.
  10522. //
  10523. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  10524. wsprintf(tszInternalClient, _T("%u.%u.%u.%u"),
  10525. inaddrTemp.S_un.S_un_b.s_b1,
  10526. inaddrTemp.S_un.S_un_b.s_b2,
  10527. inaddrTemp.S_un.S_un_b.s_b3,
  10528. inaddrTemp.S_un.S_un_b.s_b4);
  10529. }
  10530. //
  10531. // The current Microsoft UPnP Internet Gateway Device implementation--
  10532. // and most others-- do not support lease durations that are not set
  10533. // for INFINITE time, so we will almost certainly fail. Because of
  10534. // that, and since there's no server to test against anyway, we will
  10535. // not even attempt to use non-INFINITE leases. However, you can use
  10536. // the registry key to take a shot at it if you like living on the
  10537. // edge.
  10538. // Note that there's no way to detect whether a given UPnP
  10539. // implementation will allow non-INFINITE lease durations ahead of time,
  10540. // so we have to try it first, and fall back to the infinite lease
  10541. // behavior if it doesn't work. Ugh.
  10542. //
  10543. if ((! pUPnPDevice->DoesNotSupportLeaseDurations()) && (g_fUseLeaseDurations))
  10544. {
  10545. wsprintf(tszLeaseDuration, _T("%u"),
  10546. (pRegisteredPort->GetRequestedLeaseTime() / 1000));
  10547. }
  10548. else
  10549. {
  10550. _tcscpy(tszLeaseDuration, _T("0"));
  10551. pRegisteredPort->NotePermanentUPnPLease();
  10552. }
  10553. psaddrinTemp = pUPnPDevice->GetHostAddress();
  10554. wsprintf(tszHost, _T("%u.%u.%u.%u:%u"),
  10555. psaddrinTemp->sin_addr.S_un.S_un_b.s_b1,
  10556. psaddrinTemp->sin_addr.S_un.S_un_b.s_b2,
  10557. psaddrinTemp->sin_addr.S_un.S_un_b.s_b3,
  10558. psaddrinTemp->sin_addr.S_un.S_un_b.s_b4,
  10559. NTOHS(psaddrinTemp->sin_port));
  10560. //
  10561. // Create the array to hold the resulting public addresses.
  10562. //
  10563. hr = pRegisteredPort->CreateUPnPPublicAddressesArray();
  10564. if (hr != DPNH_OK)
  10565. {
  10566. DPFX(DPFPREP, 0, "Couldn't create UPnP public addresses array!");
  10567. goto Failure;
  10568. }
  10569. //
  10570. // Note whether this was the first lease or not.
  10571. //
  10572. fFirstLease = (this->m_dwNumLeases == 0) ? TRUE : FALSE;
  10573. this->m_dwNumLeases++;
  10574. DPFX(DPFPREP, 7, "UPnP lease for 0x%p added, total num leases = %u.",
  10575. pRegisteredPort, this->m_dwNumLeases);
  10576. //
  10577. // Assuming all goes well, the first port lease will expire approximately
  10578. // GetRequestedLeaseTime() ms from now.
  10579. // See above note about whether this lease will actually be used, though.
  10580. //
  10581. dwLeaseExpiration = GETTIMESTAMP() + pRegisteredPort->GetRequestedLeaseTime();
  10582. //
  10583. // Get a pointer to the addresses we're mapping. We don't have to worry
  10584. // about whether it's mapped on a local firewall since the HomeNet API will
  10585. // always map it to the same port.
  10586. //
  10587. psaddrinTemp = pRegisteredPort->GetPrivateAddressesArray();
  10588. //
  10589. // Loop through each port and map it.
  10590. //
  10591. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  10592. {
  10593. //
  10594. // Determine the public port number to register.
  10595. //
  10596. if (! pRegisteredPort->IsFixedPort())
  10597. {
  10598. //
  10599. // UPnP does not support wildcard ports (where the gateway
  10600. // device picks an unused public port number for us). We must
  10601. // select a port ahead of time to try mapping on the server.
  10602. //
  10603. // Worse, UPnP does not require the device support selecting a
  10604. // public port that is different from the client's private port
  10605. // (a.k.a. asymmetric, x to y, or floating port mappings).
  10606. // This means that even non fixed ports will act that way. To
  10607. // top it all off, there's no way to detect whether a given
  10608. // UPnP implementation will allow the ports to differ ahead of
  10609. // time, so we have to try it first, and fall back to the fixed
  10610. // port behavior if it doesn't work. Ugh.
  10611. //
  10612. if (pUPnPDevice->DoesNotSupportAsymmetricMappings())
  10613. {
  10614. //
  10615. // We are forced to use the client's private port.
  10616. //
  10617. wExternalPortHostOrder = NTOHS(psaddrinTemp[dwTemp].sin_port);
  10618. }
  10619. else
  10620. {
  10621. if (wOriginalExternalPortHostOrder == 0)
  10622. {
  10623. DNASSERT(dwTemp == 0);
  10624. //
  10625. // Ideally we would pick a random port that isn't in
  10626. // the reserved range (i.e. is greater than 1024).
  10627. // However, truly random ports cause problems with the
  10628. // Windows XP ICS implementation in a fairly obscure
  10629. // manner:
  10630. //
  10631. // 1. DPlay application starts hosting behind ICS on
  10632. // port 2302.
  10633. // 2. Port 2302 gets mapped to random port x.
  10634. // 3. External DPlay client is told to connect to x.
  10635. // 4. ICS detects inbound traffic to x, sees mapping
  10636. // for internal_ip:2302, and creates a virtual
  10637. // connection.
  10638. // 5. Internal client removes 2302<->x mapping and
  10639. // closes socket.
  10640. // 6. Internal DPlay application begins hosting on
  10641. // port 2302 again.
  10642. // 7. Port 2302 gets mapped to random port y.
  10643. // 8. External DPlay client is told to connect to y.
  10644. // 9. ICS detects inbound traffic to y, sees mapping
  10645. // for internal_ip:2302, but can't create virtual
  10646. // connection because the 2302<->x connection
  10647. // still exists.
  10648. //
  10649. // Windows XP ICS keeps the virtual connections around
  10650. // and cleans them up every 60 seconds. If the
  10651. // reconnect occurs within (up to) 2 minutes, the
  10652. // packets might get dropped due to the mapping
  10653. // collision.
  10654. //
  10655. // This causes all sorts of heartache with automated
  10656. // NAT tests that bring up and tear down connections
  10657. // between the same two machines across the NAT over
  10658. // and over.
  10659. //
  10660. // So, to avoid that, we need to make mappings
  10661. // deterministic, such that if the same internal client
  10662. // tries to map the same internal port, it should ask
  10663. // for the same external port every time. That way, if
  10664. // there happens to be a virtual connection hanging
  10665. // around from a previous attempt, we'll reuse it
  10666. // instead of clashing and getting the packet dropped.
  10667. //
  10668. // Our actual algorithm:
  10669. // 1. start with the internal client IP.
  10670. // 2. combine in the internal port being mapped.
  10671. // 3. add 1025 if necessary to get it out of the
  10672. // reserved range.
  10673. //
  10674. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  10675. wOriginalExternalPortHostOrder = inaddrTemp.S_un.S_un_w.s_w1;
  10676. wOriginalExternalPortHostOrder ^= inaddrTemp.S_un.S_un_w.s_w2;
  10677. wOriginalExternalPortHostOrder ^= psaddrinTemp[dwTemp].sin_port;
  10678. if (wOriginalExternalPortHostOrder <= MAX_RESERVED_PORT)
  10679. {
  10680. wOriginalExternalPortHostOrder += MAX_RESERVED_PORT + 1;
  10681. }
  10682. //
  10683. // This is the starting point, we'll increment by one
  10684. // as we go.
  10685. //
  10686. wExternalPortHostOrder = wOriginalExternalPortHostOrder;
  10687. }
  10688. else
  10689. {
  10690. //
  10691. // Go to the next sequential port. If we've wrapped
  10692. // around to 0, move to the first non reserved range
  10693. // port.
  10694. //
  10695. wExternalPortHostOrder++;
  10696. if (wExternalPortHostOrder == 0)
  10697. {
  10698. wExternalPortHostOrder = MAX_RESERVED_PORT + 1;
  10699. }
  10700. //
  10701. // If we wrapped all the way back around to the first
  10702. // port we tried, we have to fail.
  10703. //
  10704. if (wExternalPortHostOrder == wOriginalExternalPortHostOrder)
  10705. {
  10706. DPFX(DPFPREP, 0, "All ports were exhausted (before index %u), marking port as unavailable.",
  10707. dwTemp);
  10708. //
  10709. // Delete all mappings successfully made up until
  10710. // now.
  10711. //
  10712. DNASSERT(dwTemp > 0);
  10713. hr = this->UnmapUPnPPort(pRegisteredPort, dwTemp, TRUE);
  10714. if (hr != DPNH_OK)
  10715. {
  10716. DPFX(DPFPREP, 0, "Failed deleting %u previously mapped ports after getting failure response 0x%lx!",
  10717. dwTemp, RespInfo.hrErrorCode);
  10718. goto Failure;
  10719. }
  10720. //
  10721. // The port is unavailable.
  10722. //
  10723. pRegisteredPort->NoteUPnPPortUnavailable();
  10724. //
  10725. // We're done here. Ideally we would goto Exit, but
  10726. // we want to execute the public address array
  10727. // cleanup code. hr will == DPNH_OK.
  10728. //
  10729. goto Failure;
  10730. }
  10731. } // end if (haven't selected first port yet)
  10732. }
  10733. }
  10734. else
  10735. {
  10736. //
  10737. // Use the fixed port.
  10738. //
  10739. wExternalPortHostOrder = NTOHS(psaddrinTemp[dwTemp].sin_port);
  10740. }
  10741. wsprintf(tszInternalPort, _T("%u"),
  10742. NTOHS(psaddrinTemp[dwTemp].sin_port));
  10743. Retry:
  10744. //
  10745. // Because the UPnP spec allows a device to overwrite existing
  10746. // mappings if they are for the same client, we have to make sure
  10747. // that no local DPNHUPNP instances, including this one, have an
  10748. // active mapping for that public port.
  10749. // Rather than having the retry code in multiple places, I'll use
  10750. // the somewhat ugly 'goto' to jump straight to the existing port
  10751. // unavailable handling.
  10752. //
  10753. if (this->IsNATPublicPortInUseLocally(wExternalPortHostOrder))
  10754. {
  10755. DPFX(DPFPREP, 1, "Port %u is already in use locally.");
  10756. RespInfo.hrErrorCode = DPNHERR_PORTUNAVAILABLE;
  10757. goto PortUnavailable;
  10758. }
  10759. //
  10760. // If the control socket got disconnected after the last message,
  10761. // then reconnect.
  10762. //
  10763. if (! pUPnPDevice->IsConnected())
  10764. {
  10765. hr = this->ReconnectUPnPControlSocket(pUPnPDevice);
  10766. if (hr != S_OK)
  10767. {
  10768. DPFX(DPFPREP, 0, "Couldn't reconnect UPnP control socket!");
  10769. goto Failure;
  10770. }
  10771. }
  10772. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  10773. wsprintf(tszExternalPort, _T("%u"), wExternalPortHostOrder);
  10774. //
  10775. // Generate a description for this mapping. The format is:
  10776. //
  10777. // [executable_name] (nnn.nnn.nnn.nnn:nnnnn) nnnnn {"TCP" | "UDP"}
  10778. //
  10779. // That way nothing needs to be localized.
  10780. //
  10781. dwDescriptionLength = GetModuleFileName(NULL,
  10782. tszDescription,
  10783. (MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1));
  10784. if (dwDescriptionLength != 0)
  10785. {
  10786. //
  10787. // Be paranoid and make sure the description string is valid.
  10788. //
  10789. tszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1] = 0;
  10790. //
  10791. // Get just the executable name from the path.
  10792. //
  10793. #ifdef WINCE
  10794. GetExeName(tszDescription);
  10795. #else // ! WINCE
  10796. #ifdef UNICODE
  10797. _wsplitpath(tszDescription, NULL, NULL, tszDescription, NULL);
  10798. #else // ! UNICODE
  10799. _splitpath(tszDescription, NULL, NULL, tszDescription, NULL);
  10800. #endif // ! UNICODE
  10801. #endif // ! WINCE
  10802. dwDescriptionLength = _tcslen(tszDescription) // executable name
  10803. + 2 // " ("
  10804. + _tcslen(tszInternalClient) // private IP address
  10805. + 1 // ":"
  10806. + _tcslen(tszInternalPort) // private port
  10807. + 2 // ") "
  10808. + _tcslen(tszExternalPort) // public port
  10809. + 4; // " TCP" | " UDP"
  10810. //
  10811. // Make sure the long string will fit. If not, use the
  10812. // abbreviated version.
  10813. //
  10814. if (dwDescriptionLength > MAX_UPNP_MAPPING_DESCRIPTION_SIZE)
  10815. {
  10816. dwDescriptionLength = 0;
  10817. }
  10818. }
  10819. if (dwDescriptionLength == 0)
  10820. {
  10821. //
  10822. // Use the abbreviated version we know will fit.
  10823. //
  10824. wsprintf(tszDescription,
  10825. _T("(%s:%s) %s %s"),
  10826. tszInternalClient,
  10827. tszInternalPort,
  10828. tszExternalPort,
  10829. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  10830. }
  10831. else
  10832. {
  10833. //
  10834. // There's enough room, tack on the rest of the description.
  10835. //
  10836. wsprintf((tszDescription + _tcslen(tszDescription)),
  10837. _T(" (%s:%s) %s %s"),
  10838. tszInternalClient,
  10839. tszInternalPort,
  10840. tszExternalPort,
  10841. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  10842. }
  10843. DPFX(DPFPREP, 6, "Requesting mapping \"%s\".", tszDescription);
  10844. iContentLength = strlen("<s:Envelope" EOL)
  10845. + strlen(" xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL)
  10846. + strlen(" s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL)
  10847. + strlen(" <s:Body>" EOL)
  10848. + strlen(" <u:" ACTION_ADDPORTMAPPING_A " xmlns:u=\"") + pUPnPDevice->GetStaticServiceURILength() + strlen("\">" EOL)
  10849. + strlen(" <" ARG_ADDPORTMAPPING_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_ADDPORTMAPPING_NEWREMOTEHOST_A ">" EOL)
  10850. + strlen(" <" ARG_ADDPORTMAPPING_NEWEXTERNALPORT_A ">") + _tcslen(tszExternalPort) + strlen("</" ARG_ADDPORTMAPPING_NEWEXTERNALPORT_A ">" EOL)
  10851. + strlen(" <" ARG_ADDPORTMAPPING_NEWPROTOCOL_A ">") + 3 + strlen("</" ARG_ADDPORTMAPPING_NEWPROTOCOL_A ">" EOL)
  10852. + strlen(" <" ARG_ADDPORTMAPPING_NEWINTERNALPORT_A ">") + _tcslen(tszInternalPort) + strlen("</" ARG_ADDPORTMAPPING_NEWINTERNALPORT_A ">" EOL)
  10853. + strlen(" <" ARG_ADDPORTMAPPING_NEWINTERNALCLIENT_A ">") + _tcslen(tszInternalClient) + strlen("</" ARG_ADDPORTMAPPING_NEWINTERNALCLIENT_A ">" EOL)
  10854. + strlen(" <" ARG_ADDPORTMAPPING_NEWENABLED_A ">" UPNP_BOOLEAN_TRUE "</" ARG_ADDPORTMAPPING_NEWENABLED_A ">" EOL)
  10855. + strlen(" <" ARG_ADDPORTMAPPING_NEWPORTMAPPINGDESCRIPTION_A ">") + _tcslen(tszDescription) + strlen("</" ARG_ADDPORTMAPPING_NEWPORTMAPPINGDESCRIPTION_A ">" EOL)
  10856. + strlen(" <" ARG_ADDPORTMAPPING_NEWLEASEDURATION_A ">") + _tcslen(tszLeaseDuration) + strlen("</" ARG_ADDPORTMAPPING_NEWLEASEDURATION_A ">" EOL)
  10857. + strlen(" </u:" ACTION_ADDPORTMAPPING_A ">" EOL)
  10858. + strlen(" </s:Body>" EOL)
  10859. + strlen("</s:Envelope>" EOL)
  10860. + strlen(EOL);
  10861. wsprintf(tszContentLength, _T("%i"), iContentLength);
  10862. iMsgSize = strlen("POST ") + strlen(pUPnPDevice->GetServiceControlURL()) + strlen(" " HTTP_VERSION EOL)
  10863. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  10864. + strlen("CONTENT-LENGTH: ") + _tcslen(tszContentLength) + strlen(EOL)
  10865. + strlen("CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL)
  10866. + strlen("SOAPACTION: ") + pUPnPDevice->GetStaticServiceURILength() + strlen("#" ACTION_ADDPORTMAPPING_A EOL)
  10867. + strlen(EOL)
  10868. + iContentLength;
  10869. //
  10870. // Allocate (or reallocate) the message buffer.
  10871. //
  10872. if (iMsgSize > iPrevMsgSize)
  10873. {
  10874. if (pszMessage != NULL)
  10875. {
  10876. DNFree(pszMessage);
  10877. pszMessage = NULL;
  10878. }
  10879. pszMessage = (char*) DNMalloc(iMsgSize + 1); // include room for NULL termination that isn't actually sent
  10880. if (pszMessage == NULL)
  10881. {
  10882. hr = DPNHERR_OUTOFMEMORY;
  10883. goto Failure;
  10884. }
  10885. iPrevMsgSize = iMsgSize;
  10886. }
  10887. strcpy(pszMessage, "POST ");
  10888. strcat(pszMessage, pUPnPDevice->GetServiceControlURL());
  10889. strcat(pszMessage, " " HTTP_VERSION EOL);
  10890. strcat(pszMessage, "HOST: ");
  10891. #ifdef UNICODE
  10892. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10893. tszHost,
  10894. (_tcslen(tszHost) + 1));
  10895. #else // ! UNICODE
  10896. strcat(pszMessage, tszHost);
  10897. #endif // ! UNICODE
  10898. strcat(pszMessage, EOL);
  10899. strcat(pszMessage, "CONTENT-LENGTH: ");
  10900. #ifdef UNICODE
  10901. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10902. tszContentLength,
  10903. (_tcslen(tszContentLength) + 1));
  10904. #else // ! UNICODE
  10905. strcat(pszMessage, tszContentLength);
  10906. #endif // ! UNICODE
  10907. strcat(pszMessage, EOL);
  10908. strcat(pszMessage, "CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL);
  10909. strcat(pszMessage, "SOAPACTION: ");
  10910. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  10911. strcat(pszMessage, "#" ACTION_ADDPORTMAPPING_A EOL);
  10912. strcat(pszMessage, EOL);
  10913. strcat(pszMessage, "<s:Envelope" EOL);
  10914. strcat(pszMessage, " xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL);
  10915. strcat(pszMessage, " s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL);
  10916. strcat(pszMessage, " <s:Body>" EOL);
  10917. strcat(pszMessage, " <u:" ACTION_ADDPORTMAPPING_A " xmlns:u=\"");
  10918. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  10919. strcat(pszMessage, "\">" EOL);
  10920. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_ADDPORTMAPPING_NEWREMOTEHOST_A ">" EOL);
  10921. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWEXTERNALPORT_A ">");
  10922. #ifdef UNICODE
  10923. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10924. tszExternalPort,
  10925. (_tcslen(tszExternalPort) + 1));
  10926. #else // ! UNICODE
  10927. strcat(pszMessage, tszExternalPort);
  10928. #endif // ! UNICODE
  10929. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWEXTERNALPORT_A ">" EOL);
  10930. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWPROTOCOL_A ">");
  10931. strcat(pszMessage, ((pRegisteredPort->IsTCP()) ? "TCP" : "UDP"));
  10932. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWPROTOCOL_A ">" EOL);
  10933. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWINTERNALPORT_A ">");
  10934. #ifdef UNICODE
  10935. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10936. tszInternalPort,
  10937. (_tcslen(tszInternalPort) + 1));
  10938. #else // ! UNICODE
  10939. strcat(pszMessage, tszInternalPort);
  10940. #endif // ! UNICODE
  10941. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWINTERNALPORT_A ">" EOL);
  10942. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWINTERNALCLIENT_A ">");
  10943. #ifdef UNICODE
  10944. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10945. tszInternalClient,
  10946. (_tcslen(tszInternalClient) + 1));
  10947. #else // ! UNICODE
  10948. strcat(pszMessage, tszInternalClient);
  10949. #endif // ! UNICODE
  10950. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWINTERNALCLIENT_A ">" EOL);
  10951. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWENABLED_A ">" UPNP_BOOLEAN_TRUE "</" ARG_ADDPORTMAPPING_NEWENABLED_A ">" EOL);
  10952. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWPORTMAPPINGDESCRIPTION_A ">");
  10953. #ifdef UNICODE
  10954. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10955. tszDescription,
  10956. (_tcslen(tszDescription) + 1));
  10957. #else // ! UNICODE
  10958. strcat(pszMessage, tszDescription);
  10959. #endif // ! UNICODE
  10960. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWPORTMAPPINGDESCRIPTION_A ">" EOL);
  10961. strcat(pszMessage, " <" ARG_ADDPORTMAPPING_NEWLEASEDURATION_A ">");
  10962. #ifdef UNICODE
  10963. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  10964. tszLeaseDuration,
  10965. (_tcslen(tszLeaseDuration) + 1));
  10966. #else // ! UNICODE
  10967. strcat(pszMessage, tszLeaseDuration);
  10968. #endif // ! UNICODE
  10969. strcat(pszMessage, "</" ARG_ADDPORTMAPPING_NEWLEASEDURATION_A ">" EOL);
  10970. strcat(pszMessage, " </u:" ACTION_ADDPORTMAPPING_A ">" EOL);
  10971. strcat(pszMessage, " </s:Body>" EOL);
  10972. strcat(pszMessage, "</s:Envelope>" EOL);
  10973. strcat(pszMessage, EOL);
  10974. #ifdef DBG
  10975. this->PrintUPnPTransactionToFile(pszMessage,
  10976. iMsgSize,
  10977. "Outbound add port mapping request",
  10978. pDevice);
  10979. #endif // DBG
  10980. iReturn = this->m_pfnsend(pUPnPDevice->GetControlSocket(),
  10981. pszMessage,
  10982. iMsgSize,
  10983. 0);
  10984. if (iReturn == SOCKET_ERROR)
  10985. {
  10986. #ifdef DBG
  10987. dwError = this->m_pfnWSAGetLastError();
  10988. DPFX(DPFPREP, 0, "Got sockets error %u when sending control request to UPnP device!", dwError);
  10989. #endif // DBG
  10990. hr = DPNHERR_GENERIC;
  10991. goto Failure;
  10992. }
  10993. if (iReturn != iMsgSize)
  10994. {
  10995. DPFX(DPFPREP, 0, "Didn't send entire message (%i != %i)?!", iReturn, iMsgSize);
  10996. DNASSERT(FALSE);
  10997. hr = DPNHERR_GENERIC;
  10998. goto Failure;
  10999. }
  11000. //
  11001. // We have the lock so no one could have tried to receive data from
  11002. // the control socket yet. Mark the device as waiting for a
  11003. // response.
  11004. //
  11005. ZeroMemory(&RespInfo, sizeof(RespInfo));
  11006. pUPnPDevice->StartWaitingForControlResponse(CONTROLRESPONSETYPE_ADDPORTMAPPING,
  11007. &RespInfo);
  11008. fStartedWaitingForControlResponse = TRUE;
  11009. //
  11010. // Actually wait for the response.
  11011. //
  11012. dwStartTime = GETTIMESTAMP();
  11013. dwTimeout = g_dwUPnPResponseTimeout;
  11014. do
  11015. {
  11016. hr = this->CheckForReceivedUPnPMsgsOnDevice(pUPnPDevice, dwTimeout);
  11017. if (hr != DPNH_OK)
  11018. {
  11019. DPFX(DPFPREP, 0, "Failed receiving UPnP messages!");
  11020. goto Failure;
  11021. }
  11022. //
  11023. // We either timed out or got some data. Check if we got the
  11024. // response we need.
  11025. //
  11026. if (! pUPnPDevice->IsWaitingForControlResponse())
  11027. {
  11028. if (RespInfo.hrErrorCode != DPNH_OK)
  11029. {
  11030. //
  11031. // Make sure it's not the "I can't handle asymmetric
  11032. // mappings" error. If it is, note the fact that this
  11033. // device is going to force us to have worse behavior
  11034. // and try again.
  11035. //
  11036. if (RespInfo.hrErrorCode == (HRESULT) UPNPERR_IGD_SAMEPORTVALUESREQUIRED)
  11037. {
  11038. DPFX(DPFPREP, 1, "UPnP device 0x%p does not support asymmetric mappings.",
  11039. pUPnPDevice);
  11040. //
  11041. // Make sure we're not getting this error from a bad
  11042. // device. Otherwise we might get caught in a loop.
  11043. //
  11044. if ((! pUPnPDevice->DoesNotSupportAsymmetricMappings()) &&
  11045. (! pRegisteredPort->IsFixedPort()) &&
  11046. (dwTemp == 0))
  11047. {
  11048. //
  11049. // Remember that this device is unfriendly.
  11050. //
  11051. pUPnPDevice->NoteDoesNotSupportAsymmetricMappings();
  11052. //
  11053. // Use the same port externally next time.
  11054. //
  11055. DNASSERT(wExternalPortHostOrder != NTOHS(psaddrinTemp[dwTemp].sin_port));
  11056. wExternalPortHostOrder = NTOHS(psaddrinTemp[dwTemp].sin_port);
  11057. //
  11058. // Try again.
  11059. //
  11060. goto Retry;
  11061. }
  11062. DPFX(DPFPREP, 1, "DoesNotSupportAsymmetricMappings = %i, fixed port = %i, port index = %u",
  11063. pUPnPDevice->DoesNotSupportAsymmetricMappings(),
  11064. pRegisteredPort->IsFixedPort(),
  11065. dwTemp);
  11066. DNASSERTX(! "Getting UPNPERR_IGD_SAMEPORTVALUESREQUIRED from bad device!", 2);
  11067. //
  11068. // Continue through to failure case...
  11069. //
  11070. }
  11071. //
  11072. // Make sure it's not the "I can't handle lease times"
  11073. // error. If it is, note the fact that this device is
  11074. // going to force us to have worse behavior and try
  11075. // again.
  11076. //
  11077. if (RespInfo.hrErrorCode == (HRESULT) UPNPERR_IGD_ONLYPERMANENTLEASESSUPPORTED)
  11078. {
  11079. DPFX(DPFPREP, 1, "UPnP device 0x%p does not support non-INFINITE lease durations.",
  11080. pUPnPDevice);
  11081. //
  11082. // Make sure we're not getting this error from a bad
  11083. // device. Otherwise we might get caught in a loop.
  11084. //
  11085. if ((! pUPnPDevice->DoesNotSupportLeaseDurations()) &&
  11086. (dwTemp == 0))
  11087. {
  11088. //
  11089. // Remember that this device is unfriendly.
  11090. //
  11091. pUPnPDevice->NoteDoesNotSupportLeaseDurations();
  11092. //
  11093. // Use an INFINITE lease next time around.
  11094. //
  11095. DNASSERT(_tcscmp(tszLeaseDuration, _T("0")) != 0);
  11096. _tcscpy(tszLeaseDuration, _T("0"));
  11097. pRegisteredPort->NotePermanentUPnPLease();
  11098. //
  11099. // Try again.
  11100. //
  11101. goto Retry;
  11102. }
  11103. DPFX(DPFPREP, 1, "DoesNotSupportLeaseDurations = %i, port index = %u",
  11104. pUPnPDevice->DoesNotSupportLeaseDurations(), dwTemp);
  11105. DNASSERTX(! "Getting UPNPERR_IGD_ONLYPERMANENTLEASESSUPPORTED from bad device!", 2);
  11106. //
  11107. // Continue through to failure case...
  11108. //
  11109. }
  11110. #ifdef DBG
  11111. if (RespInfo.hrErrorCode == DPNHERR_PORTUNAVAILABLE)
  11112. {
  11113. DPFX(DPFPREP, 2, "Port %u (for address index %u) is reportedly unavailable.",
  11114. wExternalPortHostOrder, dwTemp);
  11115. }
  11116. #endif // DBG
  11117. PortUnavailable:
  11118. //
  11119. // If it's the port-unavailable error but we're able to
  11120. // try a different port, go for it.
  11121. //
  11122. if ((RespInfo.hrErrorCode == DPNHERR_PORTUNAVAILABLE) &&
  11123. (! pRegisteredPort->IsFixedPort()) &&
  11124. (! pUPnPDevice->DoesNotSupportAsymmetricMappings()))
  11125. {
  11126. //
  11127. // Go to the next sequential port. If we've wrapped
  11128. // around to 0, move to the first non reserved range
  11129. // port.
  11130. //
  11131. wExternalPortHostOrder++;
  11132. if (wExternalPortHostOrder == 0)
  11133. {
  11134. wExternalPortHostOrder = MAX_RESERVED_PORT + 1;
  11135. }
  11136. //
  11137. // If we haven't wrapped all the way back around to
  11138. // the first port we tried, try again.
  11139. //
  11140. if (wExternalPortHostOrder != wOriginalExternalPortHostOrder)
  11141. {
  11142. DPFX(DPFPREP, 2, "Retrying next port (%u) for index %u.",
  11143. wExternalPortHostOrder, dwTemp);
  11144. goto Retry;
  11145. }
  11146. DPFX(DPFPREP, 0, "All ports were exhausted (after index %u), marking port as unavailable.",
  11147. dwTemp);
  11148. }
  11149. //
  11150. // If it's not the port-unavailable error, it's
  11151. // serious. Bail.
  11152. //
  11153. if (RespInfo.hrErrorCode != DPNHERR_PORTUNAVAILABLE)
  11154. {
  11155. DPFX(DPFPREP, 1, "Port index %u got failure response 0x%lx.",
  11156. dwTemp, RespInfo.hrErrorCode);
  11157. hr = DPNHERR_SERVERNOTRESPONDING;
  11158. goto Failure;
  11159. }
  11160. //
  11161. // The port is unavailable.
  11162. //
  11163. pRegisteredPort->NoteUPnPPortUnavailable();
  11164. //
  11165. // We're done here. Ideally we would goto Exit, but we
  11166. // want to execute the public address array cleanup
  11167. // code. hr should == DPNH_OK.
  11168. //
  11169. DNASSERT(hr == DPNH_OK);
  11170. goto Failure;
  11171. }
  11172. //
  11173. // If we got here, we successfully registered this port.
  11174. //
  11175. //
  11176. // UPnP Internet Gateway Device mapping protocol doesn't
  11177. // hand you the external IP address in the success response
  11178. // message. We had to have known it through other means
  11179. // (querying the ExternalIPAddress variable).
  11180. //
  11181. DPFX(DPFPREP, 2, "Port index %u got success response.", dwTemp);
  11182. pRegisteredPort->SetUPnPPublicV4Address(dwTemp,
  11183. pUPnPDevice->GetExternalIPAddressV4(),
  11184. HTONS(wExternalPortHostOrder));
  11185. //
  11186. // If the lease is permanent and it's not for a shared
  11187. // port, we need to remember it, in case we crash before
  11188. // cleaning it up in this session. That we can clean it up
  11189. // next time we launch.
  11190. //
  11191. if ((pRegisteredPort->HasPermanentUPnPLease()) &&
  11192. (! pRegisteredPort->IsSharedPort()))
  11193. {
  11194. CRegistry RegObject;
  11195. DPNHACTIVENATMAPPING dpnhanm;
  11196. #ifndef UNICODE
  11197. WCHAR wszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  11198. #endif // ! UNICODE
  11199. DPFX(DPFPREP, 7, "Remembering NAT lease \"%s\" in case of crash.",
  11200. tszDescription);
  11201. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  11202. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVENATMAPPINGS,
  11203. FALSE,
  11204. TRUE,
  11205. TRUE,
  11206. DPN_KEY_ALL_ACCESS))
  11207. {
  11208. DPFX(DPFPREP, 0, "Couldn't open active NAT mapping key, unable to save in case of crash!");
  11209. }
  11210. else
  11211. {
  11212. #ifndef UNICODE
  11213. dwDescriptionLength = strlen(tszDescription) + 1;
  11214. hr = STR_jkAnsiToWide(wszDescription, tszDescription, dwDescriptionLength);
  11215. if (hr != S_OK)
  11216. {
  11217. DPFX(DPFPREP, 0, "Couldn't convert NAT mapping description to Unicode (err = 0x%lx), unable to save in case of crash!",
  11218. hr);
  11219. //
  11220. // Ignore error and continue.
  11221. //
  11222. hr = S_OK;
  11223. }
  11224. else
  11225. #endif // ! UNICODE
  11226. {
  11227. DNASSERT(this->m_dwInstanceKey != 0);
  11228. ZeroMemory(&dpnhanm, sizeof(dpnhanm));
  11229. dpnhanm.dwVersion = ACTIVE_MAPPING_VERSION;
  11230. dpnhanm.dwInstanceKey = this->m_dwInstanceKey;
  11231. dpnhanm.dwUPnPDeviceID = pUPnPDevice->GetID();
  11232. dpnhanm.dwFlags = pRegisteredPort->GetFlags();
  11233. dpnhanm.dwInternalAddressV4 = pDevice->GetLocalAddressV4();
  11234. dpnhanm.wInternalPort = psaddrinTemp[dwTemp].sin_port;
  11235. dpnhanm.dwExternalAddressV4 = pUPnPDevice->GetExternalIPAddressV4();
  11236. dpnhanm.wExternalPort = HTONS(wExternalPortHostOrder);
  11237. #ifdef UNICODE
  11238. RegObject.WriteBlob(tszDescription,
  11239. #else // ! UNICODE
  11240. RegObject.WriteBlob(wszDescription,
  11241. #endif // ! UNICODE
  11242. (LPBYTE) (&dpnhanm),
  11243. sizeof(dpnhanm));
  11244. }
  11245. RegObject.Close();
  11246. }
  11247. }
  11248. //
  11249. // Break out of the wait loop.
  11250. //
  11251. break;
  11252. }
  11253. //
  11254. // Make sure our device is still connected.
  11255. //
  11256. if (! pUPnPDevice->IsConnected())
  11257. {
  11258. DPFX(DPFPREP, 0, "UPnP device 0x%p disconnected while adding port index %u!",
  11259. pUPnPDevice, dwTemp);
  11260. pUPnPDevice->StopWaitingForControlResponse();
  11261. hr = DPNHERR_SERVERNOTRESPONDING;
  11262. goto Failure;
  11263. }
  11264. //
  11265. // Calculate how long we have left to wait. If the calculation
  11266. // goes negative, it means we're done.
  11267. //
  11268. dwTimeout = g_dwUPnPResponseTimeout - (GETTIMESTAMP() - dwStartTime);
  11269. }
  11270. while (((int) dwTimeout > 0));
  11271. //
  11272. // If we never got the response, stop waiting for it.
  11273. //
  11274. if (pUPnPDevice->IsWaitingForControlResponse())
  11275. {
  11276. pUPnPDevice->StopWaitingForControlResponse();
  11277. DPFX(DPFPREP, 0, "Port index %u didn't get response in time!", dwTemp);
  11278. hr = DPNHERR_SERVERNOTRESPONDING;
  11279. goto Failure;
  11280. }
  11281. //
  11282. // Continue on to the next port.
  11283. //
  11284. }
  11285. //
  11286. // If we're here, all ports were successfully registered.
  11287. //
  11288. if (pRegisteredPort->HasPermanentUPnPLease())
  11289. {
  11290. DPFX(DPFPREP, 3, "All %u ports successfully registered with UPnP device (no expiration).",
  11291. dwTemp);
  11292. }
  11293. else
  11294. {
  11295. pRegisteredPort->SetUPnPLeaseExpiration(dwLeaseExpiration);
  11296. DPFX(DPFPREP, 3, "All %u ports successfully registered with UPnP device, expiration = %u.",
  11297. dwTemp, dwLeaseExpiration);
  11298. //
  11299. // Remember this expiration time if it's the one that's going to expire
  11300. // soonest.
  11301. //
  11302. if ((fFirstLease) ||
  11303. ((int) (dwLeaseExpiration - this->m_dwEarliestLeaseExpirationTime) < 0))
  11304. {
  11305. if (fFirstLease)
  11306. {
  11307. DPFX(DPFPREP, 1, "Registered port 0x%p's UPnP lease is the first lease (expires at %u).",
  11308. pRegisteredPort, dwLeaseExpiration);
  11309. }
  11310. else
  11311. {
  11312. DPFX(DPFPREP, 1, "Registered port 0x%p's UPnP lease expires at %u which is earlier than the next earliest lease expiration (%u).",
  11313. pRegisteredPort,
  11314. dwLeaseExpiration,
  11315. this->m_dwEarliestLeaseExpirationTime);
  11316. }
  11317. this->m_dwEarliestLeaseExpirationTime = dwLeaseExpiration;
  11318. #ifndef DPNBUILD_NOWINSOCK2
  11319. //
  11320. // Ping the event if there is one so that the user's GetCaps
  11321. // interval doesn't miss this new, shorter lease.
  11322. //
  11323. if (this->m_hAlertEvent != NULL)
  11324. {
  11325. fResult = SetEvent(this->m_hAlertEvent);
  11326. #ifdef DBG
  11327. if (! fResult)
  11328. {
  11329. dwError = GetLastError();
  11330. DPFX(DPFPREP, 0, "Couldn't set alert event 0x%p! err = %u",
  11331. this->m_hAlertEvent, dwError);
  11332. //
  11333. // Ignore failure...
  11334. //
  11335. }
  11336. #endif // DBG
  11337. }
  11338. //
  11339. // Ping the I/O completion port if there is one so that the user's
  11340. // GetCaps interval doesn't miss this new, shorter lease.
  11341. //
  11342. if (this->m_hAlertIOCompletionPort != NULL)
  11343. {
  11344. fResult = PostQueuedCompletionStatus(this->m_hAlertIOCompletionPort,
  11345. 0,
  11346. this->m_dwAlertCompletionKey,
  11347. NULL);
  11348. #ifdef DBG
  11349. if (! fResult)
  11350. {
  11351. dwError = GetLastError();
  11352. DPFX(DPFPREP, 0, "Couldn't queue key %u on alert IO completion port 0x%p! err = %u",
  11353. this->m_dwAlertCompletionKey,
  11354. this->m_hAlertIOCompletionPort,
  11355. dwError);
  11356. //
  11357. // Ignore failure...
  11358. //
  11359. }
  11360. #endif // DBG
  11361. }
  11362. #endif // ! DPNBUILD_NOWINSOCK2
  11363. }
  11364. else
  11365. {
  11366. //
  11367. // Not first or shortest lease.
  11368. //
  11369. }
  11370. } // end if (not permanent UPnP lease)
  11371. Exit:
  11372. if (pszMessage != NULL)
  11373. {
  11374. DNFree(pszMessage);
  11375. pszMessage = NULL;
  11376. }
  11377. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  11378. return hr;
  11379. Failure:
  11380. //
  11381. // If we allocated the array, clean it up.
  11382. //
  11383. if (pRegisteredPort->HasUPnPPublicAddresses())
  11384. {
  11385. //
  11386. // If we started waiting for a response, clear that.
  11387. //
  11388. if (fStartedWaitingForControlResponse)
  11389. {
  11390. pUPnPDevice->StopWaitingForControlResponse();
  11391. }
  11392. //
  11393. // Delete all mappings successfully made up until now.
  11394. //
  11395. if (dwTemp > 0)
  11396. {
  11397. temphr = this->UnmapUPnPPort(pRegisteredPort, dwTemp, TRUE);
  11398. if (temphr != DPNH_OK)
  11399. {
  11400. DPFX(DPFPREP, 0, "Failed deleting %u previously mapped ports! Err = 0x%lx",
  11401. dwTemp, temphr);
  11402. if (hr == DPNH_OK)
  11403. {
  11404. hr = temphr;
  11405. }
  11406. }
  11407. }
  11408. else
  11409. {
  11410. //
  11411. // Remove the addresses array we created.
  11412. //
  11413. pRegisteredPort->DestroyUPnPPublicAddressesArray();
  11414. //
  11415. // Remove the lease counter.
  11416. //
  11417. DNASSERT(this->m_dwNumLeases > 0);
  11418. this->m_dwNumLeases--;
  11419. DPFX(DPFPREP, 7, "UPnP lease for 0x%p removed, total num leases = %u.",
  11420. pRegisteredPort, this->m_dwNumLeases);
  11421. }
  11422. }
  11423. //
  11424. // Turn off the permanent lease flag we may have turned on at the top of
  11425. // this function.
  11426. //
  11427. pRegisteredPort->NoteNotPermanentUPnPLease();
  11428. goto Exit;
  11429. } // CNATHelpUPnP::MapPortsOnUPnPDevice
  11430. #undef DPF_MODNAME
  11431. #define DPF_MODNAME "CNATHelpUPnP::InternalUPnPQueryAddress"
  11432. //=============================================================================
  11433. // CNATHelpUPnP::InternalUPnPQueryAddress
  11434. //-----------------------------------------------------------------------------
  11435. //
  11436. // Description: Queries a port mapping with a UPnP device.
  11437. //
  11438. // The UPnP device may get removed from list if a failure
  11439. // occurs, the caller needs to have a reference.
  11440. //
  11441. // The object lock is assumed to be held.
  11442. //
  11443. // Arguments:
  11444. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device that
  11445. // should be queried.
  11446. // SOCKADDR_IN * psaddrinQueryAddress - Address to look up.
  11447. // SOCKADDR_IN * psaddrinResponseAddress - Place to store public address, if
  11448. // one exists.
  11449. // DWORD dwFlags - Flags to use when querying.
  11450. //
  11451. // Returns: HRESULT
  11452. // DPNH_OK - The query was successful.
  11453. // DPNHERR_GENERIC - An error occurred.
  11454. // DPNHERR_NOMAPPING - The server did not have a mapping for the
  11455. // given address.
  11456. // DPNHERR_NOMAPPINGBUTPRIVATE - The server indicated that no mapping was
  11457. // found, but it is a private address.
  11458. // DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
  11459. // message.
  11460. //=============================================================================
  11461. HRESULT CNATHelpUPnP::InternalUPnPQueryAddress(CUPnPDevice * const pUPnPDevice,
  11462. const SOCKADDR_IN * const psaddrinQueryAddress,
  11463. SOCKADDR_IN * const psaddrinResponseAddress,
  11464. const DWORD dwFlags)
  11465. {
  11466. HRESULT hr;
  11467. BOOL fStartedWaitingForControlResponse = FALSE;
  11468. BOOL fNoPortMapping = FALSE;
  11469. CDevice * pDevice;
  11470. CBilink * pblCachedMaps;
  11471. DWORD dwCurrentTime;
  11472. CBilink * pBilink;
  11473. CCacheMap * pCacheMap;
  11474. SOCKADDR_IN * psaddrinTemp;
  11475. TCHAR tszExternalPort[32];
  11476. int iContentLength;
  11477. TCHAR tszContentLength[32];
  11478. TCHAR tszHost[22]; // "xxx.xxx.xxx.xxx:xxxxx" + NULL termination
  11479. char * pszMessage = NULL;
  11480. int iMsgSize;
  11481. int iPrevMsgSize = 0;
  11482. int iReturn;
  11483. UPNP_CONTROLRESPONSE_INFO RespInfo;
  11484. DWORD dwStartTime;
  11485. DWORD dwTimeout;
  11486. DWORD dwCacheMapFlags;
  11487. #ifdef DBG
  11488. DWORD dwError;
  11489. #endif // DBG
  11490. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%lx)",
  11491. this, pUPnPDevice, psaddrinQueryAddress, psaddrinResponseAddress, dwFlags);
  11492. DNASSERT(pUPnPDevice != NULL);
  11493. pDevice = pUPnPDevice->GetOwningDevice();
  11494. DNASSERT(pDevice != NULL);
  11495. DNASSERT(pUPnPDevice->IsReady());
  11496. DNASSERT(psaddrinQueryAddress != NULL);
  11497. DNASSERT(psaddrinResponseAddress != NULL);
  11498. DNASSERT(this->m_dwFlags & (NATHELPUPNPOBJ_INITIALIZED | NATHELPUPNPOBJ_USEUPNP));
  11499. DPFX(DPFPREP, 7, "Querying for address %u.%u.%u.%u:%u %hs.",
  11500. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b1,
  11501. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b2,
  11502. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b3,
  11503. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b4,
  11504. NTOHS(psaddrinQueryAddress->sin_port),
  11505. ((dwFlags & DPNHQUERYADDRESS_TCP) ? "TCP" : "UDP"));
  11506. //
  11507. // First, check if we've looked this address up recently and already have
  11508. // the result cached.
  11509. // The lock is already held.
  11510. //
  11511. pblCachedMaps = pUPnPDevice->GetCachedMaps();
  11512. dwCurrentTime = GETTIMESTAMP();
  11513. pBilink = pblCachedMaps->GetNext();
  11514. while (pBilink != pblCachedMaps)
  11515. {
  11516. DNASSERT(! pBilink->IsEmpty());
  11517. pCacheMap = CACHEMAP_FROM_BILINK(pBilink);
  11518. pBilink = pBilink->GetNext();
  11519. //
  11520. // Make sure this cached mapping hasn't expired.
  11521. //
  11522. if ((int) (pCacheMap->GetExpirationTime() - dwCurrentTime) < 0)
  11523. {
  11524. DPFX(DPFPREP, 5, "Cached mapping 0x%p has expired.", pCacheMap);
  11525. pCacheMap->m_blList.RemoveFromList();
  11526. delete pCacheMap;
  11527. }
  11528. else
  11529. {
  11530. //
  11531. // If this mapping is for the right address and type of address,
  11532. // then we've already got our answer.
  11533. //
  11534. if (pCacheMap->DoesMatchQuery(psaddrinQueryAddress, dwFlags))
  11535. {
  11536. if (pCacheMap->IsNotFound())
  11537. {
  11538. if ((dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED) &&
  11539. (pCacheMap->IsPrivateButUnmapped()))
  11540. {
  11541. DPFX(DPFPREP, 5, "Address was already determined to not have a mapping but still be private.");
  11542. hr = DPNHERR_NOMAPPINGBUTPRIVATE;
  11543. }
  11544. else
  11545. {
  11546. DPFX(DPFPREP, 5, "Address was already determined to not have a mapping.");
  11547. hr = DPNHERR_NOMAPPING;
  11548. }
  11549. }
  11550. else
  11551. {
  11552. pCacheMap->GetResponseAddressV4(psaddrinResponseAddress);
  11553. DPFX(DPFPREP, 5, "Address was already determined to have a mapping.");
  11554. hr = DPNH_OK;
  11555. }
  11556. goto Exit;
  11557. }
  11558. }
  11559. }
  11560. //
  11561. // If the address we're querying isn't the NAT's public address, it can't
  11562. // possibly be mapped. So only perform the actual query if it's
  11563. // appropriate.
  11564. //
  11565. if (psaddrinQueryAddress->sin_addr.S_un.S_addr == pUPnPDevice->GetExternalIPAddressV4())
  11566. {
  11567. //
  11568. // If we're here, we haven't already cached the answer. Query the UPnP
  11569. // device.
  11570. //
  11571. DNASSERT(pUPnPDevice->GetServiceControlURL() != NULL);
  11572. //
  11573. // If the control socket got disconnected after the last message,
  11574. // then reconnect.
  11575. //
  11576. if (! pUPnPDevice->IsConnected())
  11577. {
  11578. hr = this->ReconnectUPnPControlSocket(pUPnPDevice);
  11579. if (hr != S_OK)
  11580. {
  11581. DPFX(DPFPREP, 0, "Couldn't reconnect UPnP control socket!");
  11582. goto Failure;
  11583. }
  11584. }
  11585. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  11586. wsprintf(tszExternalPort, _T("%u"),
  11587. NTOHS(psaddrinQueryAddress->sin_port));
  11588. psaddrinTemp = pUPnPDevice->GetHostAddress();
  11589. wsprintf(tszHost, _T("%u.%u.%u.%u:%u"),
  11590. psaddrinTemp->sin_addr.S_un.S_un_b.s_b1,
  11591. psaddrinTemp->sin_addr.S_un.S_un_b.s_b2,
  11592. psaddrinTemp->sin_addr.S_un.S_un_b.s_b3,
  11593. psaddrinTemp->sin_addr.S_un.S_un_b.s_b4,
  11594. NTOHS(psaddrinTemp->sin_port));
  11595. iContentLength = strlen("<s:Envelope" EOL)
  11596. + strlen(" xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL)
  11597. + strlen(" s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL)
  11598. + strlen(" <s:Body>" EOL)
  11599. + strlen(" <u:" ACTION_GETSPECIFICPORTMAPPINGENTRY_A " xmlns:u=\"") + pUPnPDevice->GetStaticServiceURILength() + strlen("\">" EOL)
  11600. + strlen(" <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWREMOTEHOST_A ">" EOL)
  11601. + strlen(" <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWEXTERNALPORT_A ">") + _tcslen(tszExternalPort) + strlen("</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWEXTERNALPORT_A ">" EOL)
  11602. + strlen(" <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPROTOCOL_A ">") + 3 + strlen("</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPROTOCOL_A ">" EOL)
  11603. + strlen(" </u:" ACTION_GETSPECIFICPORTMAPPINGENTRY_A ">" EOL)
  11604. + strlen(" </s:Body>" EOL)
  11605. + strlen("</s:Envelope>" EOL)
  11606. + strlen(EOL);
  11607. wsprintf(tszContentLength, _T("%i"), iContentLength);
  11608. iMsgSize = strlen("POST ") + strlen(pUPnPDevice->GetServiceControlURL()) + strlen(" " HTTP_VERSION EOL)
  11609. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  11610. + strlen("CONTENT-LENGTH: ") + _tcslen(tszContentLength) + strlen(EOL)
  11611. + strlen("CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL)
  11612. + strlen("SOAPACTION: ") + pUPnPDevice->GetStaticServiceURILength() + strlen("#" ACTION_GETSPECIFICPORTMAPPINGENTRY_A EOL)
  11613. + strlen(EOL)
  11614. + iContentLength;
  11615. //
  11616. // Allocate (or reallocate) the message buffer.
  11617. //
  11618. if (iMsgSize > iPrevMsgSize)
  11619. {
  11620. if (pszMessage != NULL)
  11621. {
  11622. DNFree(pszMessage);
  11623. pszMessage = NULL;
  11624. }
  11625. pszMessage = (char*) DNMalloc(iMsgSize + 1); // include room for NULL termination that isn't actually sent
  11626. if (pszMessage == NULL)
  11627. {
  11628. hr = DPNHERR_OUTOFMEMORY;
  11629. goto Failure;
  11630. }
  11631. iPrevMsgSize = iMsgSize;
  11632. }
  11633. strcpy(pszMessage, "POST ");
  11634. strcat(pszMessage, pUPnPDevice->GetServiceControlURL());
  11635. strcat(pszMessage, " " HTTP_VERSION EOL);
  11636. strcat(pszMessage, "HOST: ");
  11637. #ifdef UNICODE
  11638. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  11639. tszHost,
  11640. (_tcslen(tszHost) + 1));
  11641. #else // ! UNICODE
  11642. strcat(pszMessage, tszHost);
  11643. #endif // ! UNICODE
  11644. strcat(pszMessage, EOL);
  11645. strcat(pszMessage, "CONTENT-LENGTH: ");
  11646. #ifdef UNICODE
  11647. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  11648. tszContentLength,
  11649. (_tcslen(tszContentLength) + 1));
  11650. #else // ! UNICODE
  11651. strcat(pszMessage, tszContentLength);
  11652. #endif // ! UNICODE
  11653. strcat(pszMessage, EOL);
  11654. strcat(pszMessage, "CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL);
  11655. strcat(pszMessage, "SOAPACTION: ");
  11656. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  11657. strcat(pszMessage, "#" ACTION_GETSPECIFICPORTMAPPINGENTRY_A EOL);
  11658. strcat(pszMessage, EOL);
  11659. strcat(pszMessage, "<s:Envelope" EOL);
  11660. strcat(pszMessage, " xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL);
  11661. strcat(pszMessage, " s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL);
  11662. strcat(pszMessage, " <s:Body>" EOL);
  11663. strcat(pszMessage, " <u:" ACTION_GETSPECIFICPORTMAPPINGENTRY_A " xmlns:u=\"");
  11664. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  11665. strcat(pszMessage, "\">" EOL);
  11666. strcat(pszMessage, " <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWREMOTEHOST_A ">" EOL);
  11667. strcat(pszMessage, " <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWEXTERNALPORT_A ">");
  11668. #ifdef UNICODE
  11669. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  11670. tszExternalPort,
  11671. (_tcslen(tszExternalPort) + 1));
  11672. #else // ! UNICODE
  11673. strcat(pszMessage, tszExternalPort);
  11674. #endif // ! UNICODE
  11675. strcat(pszMessage, "</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWEXTERNALPORT_A ">" EOL);
  11676. strcat(pszMessage, " <" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPROTOCOL_A ">");
  11677. strcat(pszMessage, ((dwFlags & DPNHQUERYADDRESS_TCP) ? "TCP" : "UDP"));
  11678. strcat(pszMessage, "</" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPROTOCOL_A ">" EOL);
  11679. strcat(pszMessage, " </u:" ACTION_GETSPECIFICPORTMAPPINGENTRY_A ">" EOL);
  11680. strcat(pszMessage, " </s:Body>" EOL);
  11681. strcat(pszMessage, "</s:Envelope>" EOL);
  11682. strcat(pszMessage, EOL);
  11683. #ifdef DBG
  11684. this->PrintUPnPTransactionToFile(pszMessage,
  11685. iMsgSize,
  11686. "Outbound get port mapping request",
  11687. pDevice);
  11688. #endif // DBG
  11689. iReturn = this->m_pfnsend(pUPnPDevice->GetControlSocket(),
  11690. pszMessage,
  11691. iMsgSize,
  11692. 0);
  11693. if (iReturn == SOCKET_ERROR)
  11694. {
  11695. #ifdef DBG
  11696. dwError = this->m_pfnWSAGetLastError();
  11697. DPFX(DPFPREP, 0, "Got sockets error %u when sending control request to UPnP device!", dwError);
  11698. #endif // DBG
  11699. hr = DPNHERR_GENERIC;
  11700. goto Failure;
  11701. }
  11702. if (iReturn != iMsgSize)
  11703. {
  11704. DPFX(DPFPREP, 0, "Didn't send entire message (%i != %i)?!", iReturn, iMsgSize);
  11705. DNASSERT(FALSE);
  11706. hr = DPNHERR_GENERIC;
  11707. goto Failure;
  11708. }
  11709. //
  11710. // We have the lock so no one could have tried to receive data from
  11711. // the control socket yet. Mark the device as waiting for a response.
  11712. //
  11713. ZeroMemory(&RespInfo, sizeof(RespInfo));
  11714. pUPnPDevice->StartWaitingForControlResponse(CONTROLRESPONSETYPE_GETSPECIFICPORTMAPPINGENTRY,
  11715. &RespInfo);
  11716. fStartedWaitingForControlResponse = TRUE;
  11717. //
  11718. // Actually wait for the response.
  11719. //
  11720. dwStartTime = GETTIMESTAMP();
  11721. dwTimeout = g_dwUPnPResponseTimeout;
  11722. do
  11723. {
  11724. hr = this->CheckForReceivedUPnPMsgsOnDevice(pUPnPDevice, dwTimeout);
  11725. if (hr != DPNH_OK)
  11726. {
  11727. DPFX(DPFPREP, 0, "Failed receiving UPnP messages!");
  11728. goto Failure;
  11729. }
  11730. //
  11731. // We either timed out or got some data. Check if we got a
  11732. // response of some type.
  11733. //
  11734. if (! pUPnPDevice->IsWaitingForControlResponse())
  11735. {
  11736. break;
  11737. }
  11738. //
  11739. // Make sure our device is still connected.
  11740. //
  11741. if (! pUPnPDevice->IsConnected())
  11742. {
  11743. DPFX(DPFPREP, 0, "UPnP device 0x%p disconnected while querying port!",
  11744. pUPnPDevice);
  11745. pUPnPDevice->StopWaitingForControlResponse();
  11746. hr = DPNHERR_SERVERNOTRESPONDING;
  11747. goto Failure;
  11748. }
  11749. //
  11750. // Calculate how long we have left to wait. If the calculation
  11751. // goes negative, it means we're done.
  11752. //
  11753. dwTimeout = g_dwUPnPResponseTimeout - (GETTIMESTAMP() - dwStartTime);
  11754. }
  11755. while (((int) dwTimeout > 0));
  11756. //
  11757. // If we never got the response, stop waiting for it.
  11758. //
  11759. if (pUPnPDevice->IsWaitingForControlResponse())
  11760. {
  11761. pUPnPDevice->StopWaitingForControlResponse();
  11762. DPFX(DPFPREP, 0, "Server didn't respond in time!");
  11763. hr = DPNHERR_SERVERNOTRESPONDING;
  11764. goto Failure;
  11765. }
  11766. //
  11767. // If we're here, then we've gotten a valid response from the server.
  11768. //
  11769. if (RespInfo.hrErrorCode != DPNH_OK)
  11770. {
  11771. DPFX(DPFPREP, 1, "Server returned failure response 0x%lx, assuming no port mapping.",
  11772. RespInfo.hrErrorCode);
  11773. fNoPortMapping = TRUE;
  11774. }
  11775. }
  11776. else
  11777. {
  11778. DPFX(DPFPREP, 1, "Address %u.%u.%u.%u doesn't match NAT's external IP address, not querying.",
  11779. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b1,
  11780. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b2,
  11781. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b3,
  11782. psaddrinQueryAddress->sin_addr.S_un.S_un_b.s_b4);
  11783. fNoPortMapping = TRUE;
  11784. }
  11785. dwCacheMapFlags = QUERYFLAGSMASK(dwFlags);
  11786. //
  11787. // Determine address locality (if requested) and cache the no-mapping
  11788. // result.
  11789. //
  11790. if (fNoPortMapping)
  11791. {
  11792. //
  11793. // Try determining if the address is local, if allowed.
  11794. //
  11795. if (dwFlags & DPNHQUERYADDRESS_CHECKFORPRIVATEBUTUNMAPPED)
  11796. {
  11797. if (this->IsAddressLocal(pDevice, psaddrinQueryAddress))
  11798. {
  11799. DPFX(DPFPREP, 5, "Address appears to be local, returning NOMAPPINGBUTPRIVATE.");
  11800. dwCacheMapFlags |= CACHEMAPOBJ_PRIVATEBUTUNMAPPED;
  11801. hr = DPNHERR_NOMAPPINGBUTPRIVATE;
  11802. }
  11803. else
  11804. {
  11805. DPFX(DPFPREP, 5, "Address does not appear to be local, returning NOMAPPING.");
  11806. hr = DPNHERR_NOMAPPING;
  11807. }
  11808. }
  11809. else
  11810. {
  11811. hr = DPNHERR_NOMAPPING;
  11812. }
  11813. //
  11814. // Cache the fact that we could not determine a mapping for that
  11815. // address, if allowed.
  11816. //
  11817. if (dwFlags & DPNHQUERYADDRESS_CACHENOTFOUND)
  11818. {
  11819. pCacheMap = new CCacheMap(psaddrinQueryAddress,
  11820. (GETTIMESTAMP() + g_dwCacheLifeNotFound),
  11821. (dwCacheMapFlags | CACHEMAPOBJ_NOTFOUND));
  11822. if (pCacheMap == NULL)
  11823. {
  11824. hr = DPNHERR_OUTOFMEMORY;
  11825. goto Failure;
  11826. }
  11827. pCacheMap->m_blList.InsertBefore(pblCachedMaps);
  11828. }
  11829. goto Failure;
  11830. }
  11831. DPFX(DPFPREP, 1, "Server returned a private mapping (%u.%u.%u.%u:%u).",
  11832. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b1,
  11833. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b2,
  11834. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b3,
  11835. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b4,
  11836. NTOHS(RespInfo.wInternalPort));
  11837. //
  11838. // Convert the loopback address to the device address.
  11839. //
  11840. if (RespInfo.dwInternalClientV4 == NETWORKBYTEORDER_INADDR_LOOPBACK)
  11841. {
  11842. RespInfo.dwInternalClientV4 = pDevice->GetLocalAddressV4();
  11843. DPFX(DPFPREP, 1, "Converted private loopback address to device address (%u.%u.%u.%u).",
  11844. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b1,
  11845. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b2,
  11846. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b3,
  11847. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b4);
  11848. }
  11849. //
  11850. // If the UPnP device doesn't support asymmetric mappings and thus didn't
  11851. // return a port, or it did return one but it's the bogus port 0, assume
  11852. // the internal port is the same port as the external one.
  11853. //
  11854. if (RespInfo.wInternalPort == 0)
  11855. {
  11856. RespInfo.wInternalPort = psaddrinQueryAddress->sin_port;
  11857. DPFX(DPFPREP, 2, "Converted invalid internal port to the query address public port (%u).",
  11858. NTOHS(psaddrinQueryAddress->sin_port));
  11859. }
  11860. //
  11861. // Ensure that we're not getting something bogus.
  11862. //
  11863. SOCKADDR_IN saddrinTemp;
  11864. saddrinTemp.sin_addr.S_un.S_addr = RespInfo.dwInternalClientV4;
  11865. if ((RespInfo.dwInternalClientV4 == 0) ||
  11866. (! this->IsAddressLocal(pDevice, &saddrinTemp)))
  11867. {
  11868. //
  11869. // If the returned address is the same as the NAT's public address,
  11870. // it's probably Windows ICS returning an ICF mapping. We still treat
  11871. // it as an invalid mapping, but we will cache the results since there
  11872. // are legimitate cases where we can see this.
  11873. //
  11874. if (RespInfo.dwInternalClientV4 == pUPnPDevice->GetExternalIPAddressV4())
  11875. {
  11876. 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.",
  11877. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b1,
  11878. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b2,
  11879. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b3,
  11880. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b4,
  11881. NTOHS(RespInfo.wInternalPort));
  11882. DNASSERTX(! "UPnP device returned public address as the private address!", 3);
  11883. //
  11884. // Cache the fact that we did not get a valid mapping for that
  11885. // address, if allowed.
  11886. //
  11887. if (dwFlags & DPNHQUERYADDRESS_CACHENOTFOUND)
  11888. {
  11889. pCacheMap = new CCacheMap(psaddrinQueryAddress,
  11890. (GETTIMESTAMP() + g_dwCacheLifeNotFound),
  11891. (dwCacheMapFlags | CACHEMAPOBJ_NOTFOUND));
  11892. if (pCacheMap == NULL)
  11893. {
  11894. hr = DPNHERR_OUTOFMEMORY;
  11895. goto Failure;
  11896. }
  11897. pCacheMap->m_blList.InsertBefore(pblCachedMaps);
  11898. }
  11899. }
  11900. else
  11901. {
  11902. DPFX(DPFPREP, 0, "UPnP device returned an invalid private address (%u.%u.%u.%u:%u)! Assuming no mapping.",
  11903. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b1,
  11904. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b2,
  11905. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b3,
  11906. ((IN_ADDR*) (&RespInfo.dwInternalClientV4))->S_un.S_un_b.s_b4,
  11907. NTOHS(RespInfo.wInternalPort));
  11908. DNASSERTX(! "Why is UPnP device returning invalid private address?", 2);
  11909. }
  11910. hr = DPNHERR_NOMAPPING;
  11911. goto Failure;
  11912. }
  11913. //
  11914. // Return the address mapping to our caller.
  11915. //
  11916. ZeroMemory(psaddrinResponseAddress, sizeof(SOCKADDR_IN));
  11917. psaddrinResponseAddress->sin_family = AF_INET;
  11918. psaddrinResponseAddress->sin_addr.s_addr = RespInfo.dwInternalClientV4;
  11919. psaddrinResponseAddress->sin_port = RespInfo.wInternalPort;
  11920. //
  11921. // Cache the fact that we found a mapping for that address, if allowed.
  11922. //
  11923. if (dwFlags & DPNHQUERYADDRESS_CACHEFOUND)
  11924. {
  11925. pCacheMap = new CCacheMap(psaddrinQueryAddress,
  11926. (GETTIMESTAMP() + g_dwCacheLifeFound),
  11927. dwCacheMapFlags);
  11928. if (pCacheMap == NULL)
  11929. {
  11930. hr = DPNHERR_OUTOFMEMORY;
  11931. goto Failure;
  11932. }
  11933. pCacheMap->SetResponseAddressV4(psaddrinResponseAddress->sin_addr.S_un.S_addr,
  11934. psaddrinResponseAddress->sin_port);
  11935. pCacheMap->m_blList.InsertBefore(pblCachedMaps);
  11936. }
  11937. Exit:
  11938. if (pszMessage != NULL)
  11939. {
  11940. DNFree(pszMessage);
  11941. pszMessage = NULL;
  11942. }
  11943. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  11944. return hr;
  11945. Failure:
  11946. //
  11947. // If we started waiting for a response, clear that.
  11948. //
  11949. if (fStartedWaitingForControlResponse)
  11950. {
  11951. pUPnPDevice->StopWaitingForControlResponse();
  11952. }
  11953. goto Exit;
  11954. } // CNATHelpUPnP::InternalUPnPQueryAddress
  11955. #undef DPF_MODNAME
  11956. #define DPF_MODNAME "CNATHelpUPnP::ExtendUPnPLease"
  11957. //=============================================================================
  11958. // CNATHelpUPnP::ExtendUPnPLease
  11959. //-----------------------------------------------------------------------------
  11960. //
  11961. // Description: Asks the UPnP server to extend a port mapping lease.
  11962. //
  11963. // The UPnP device may get removed from list if a failure
  11964. // occurs, the caller needs to have a reference if it's using the
  11965. // pointer.
  11966. //
  11967. // The object lock is assumed to be held.
  11968. //
  11969. // Arguments:
  11970. // CRegisteredPort * pRegisteredPort - Pointer to port object mapping to
  11971. // extend.
  11972. //
  11973. // Returns: HRESULT
  11974. // DPNH_OK - The extension was successful.
  11975. // DPNHERR_GENERIC - An error occurred.
  11976. // DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
  11977. // message.
  11978. //=============================================================================
  11979. HRESULT CNATHelpUPnP::ExtendUPnPLease(CRegisteredPort * const pRegisteredPort)
  11980. {
  11981. HRESULT hr;
  11982. CDevice * pDevice;
  11983. CUPnPDevice * pUPnPDevice;
  11984. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pRegisteredPort);
  11985. DNASSERT(pRegisteredPort != NULL);
  11986. pDevice = pRegisteredPort->GetOwningDevice();
  11987. DNASSERT(pDevice != NULL);
  11988. pUPnPDevice = pDevice->GetUPnPDevice();
  11989. DNASSERT(pUPnPDevice != NULL);
  11990. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  11991. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  11992. #ifdef DBG
  11993. if (pRegisteredPort->HasPermanentUPnPLease())
  11994. {
  11995. DPFX(DPFPREP, 1, "Extending already permanent UPnP lease for registered port 0x%p.",
  11996. pRegisteredPort);
  11997. }
  11998. #endif // DBG
  11999. //
  12000. // UPnP devices don't have port extension per se, you just reregister the
  12001. // mapping.
  12002. //
  12003. hr = this->MapPortsOnUPnPDevice(pUPnPDevice, pRegisteredPort);
  12004. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  12005. return hr;
  12006. } // CNATHelpUPnP::ExtendUPnPLease
  12007. #undef DPF_MODNAME
  12008. #define DPF_MODNAME "CNATHelpUPnP::UnmapUPnPPort"
  12009. //=============================================================================
  12010. // CNATHelpUPnP::UnmapUPnPPort
  12011. //-----------------------------------------------------------------------------
  12012. //
  12013. // Description: Asks the UPnP server to release a port mapping.
  12014. //
  12015. // The UPnP device may get removed from list if a failure
  12016. // occurs, the caller needs to have a reference if it's using the
  12017. // device.
  12018. //
  12019. // The object lock is assumed to be held.
  12020. //
  12021. // Arguments:
  12022. // CRegisteredPort * pRegisteredPort - Pointer to port object mapping to
  12023. // release.
  12024. // DWORD dwMaxValidPort - Highest address index in array to
  12025. // try freeing. This may be one more
  12026. // than the actual number to indicate
  12027. // all should be freed.
  12028. // BOOL fNeedToDeleteRegValue - Whether the corresponding crash
  12029. // recovery registry value needs to
  12030. // be deleted as well.
  12031. //
  12032. // Returns: HRESULT
  12033. // DPNH_OK - The extension was successful.
  12034. // DPNHERR_GENERIC - An error occurred.
  12035. // DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
  12036. // message.
  12037. //=============================================================================
  12038. HRESULT CNATHelpUPnP::UnmapUPnPPort(CRegisteredPort * const pRegisteredPort,
  12039. const DWORD dwMaxValidPort,
  12040. const BOOL fNeedToDeleteRegValue)
  12041. {
  12042. HRESULT hr = DPNH_OK;
  12043. BOOL fStartedWaitingForControlResponse = FALSE;
  12044. CDevice * pDevice;
  12045. CUPnPDevice * pUPnPDevice;
  12046. SOCKADDR_IN * psaddrinPublic;
  12047. SOCKADDR_IN * psaddrinPrivate;
  12048. TCHAR tszExternalPort[32];
  12049. int iContentLength;
  12050. TCHAR tszContentLength[32];
  12051. TCHAR tszHost[22]; // "xxx.xxx.xxx.xxx:xxxxx" + NULL termination
  12052. char * pszMessage = NULL;
  12053. int iMsgSize;
  12054. int iPrevMsgSize = 0;
  12055. int iReturn;
  12056. DWORD dwTemp;
  12057. UPNP_CONTROLRESPONSE_INFO RespInfo;
  12058. DWORD dwStartTime;
  12059. DWORD dwTimeout;
  12060. SOCKADDR_IN * psaddrinHostAddress;
  12061. #ifdef DBG
  12062. DWORD dwError;
  12063. #endif // DBG
  12064. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i, %i)",
  12065. this, pRegisteredPort, ((int) dwMaxValidPort), fNeedToDeleteRegValue);
  12066. DNASSERT(pRegisteredPort != NULL);
  12067. DNASSERT(dwMaxValidPort != 0);
  12068. DNASSERT(dwMaxValidPort <= pRegisteredPort->GetNumAddresses());
  12069. pDevice = pRegisteredPort->GetOwningDevice();
  12070. DNASSERT(pDevice != NULL);
  12071. pUPnPDevice = pDevice->GetUPnPDevice();
  12072. DNASSERT(pUPnPDevice != NULL);
  12073. //
  12074. // GetUPnPDevice did not add a reference to pUPnPDevice for us.
  12075. //
  12076. pUPnPDevice->AddRef();
  12077. DNASSERT(pUPnPDevice->IsReady());
  12078. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  12079. //
  12080. // Prevent trying to remove the lease twice.
  12081. //
  12082. pRegisteredPort->NoteRemovingUPnPLease();
  12083. if (dwMaxValidPort == pRegisteredPort->GetNumAddresses())
  12084. {
  12085. DPFX(DPFPREP, 7, "Unmapping all %u addresses for registered port 0x%p on UPnP device 0x%p.",
  12086. dwMaxValidPort, pRegisteredPort, pUPnPDevice);
  12087. }
  12088. else
  12089. {
  12090. DPFX(DPFPREP, 7, "Error cleanup code, only unmapping first %u addresses (of %u possible) for registered port 0x%p on UPnP device 0x%p.",
  12091. dwMaxValidPort, pRegisteredPort->GetNumAddresses(),
  12092. pRegisteredPort, pUPnPDevice);
  12093. }
  12094. //
  12095. // Set up variables we'll need.
  12096. //
  12097. DNASSERT(pUPnPDevice->GetServiceControlURL() != NULL);
  12098. psaddrinHostAddress = pUPnPDevice->GetHostAddress();
  12099. wsprintf(tszHost, _T("%u.%u.%u.%u:%u"),
  12100. psaddrinHostAddress->sin_addr.S_un.S_un_b.s_b1,
  12101. psaddrinHostAddress->sin_addr.S_un.S_un_b.s_b2,
  12102. psaddrinHostAddress->sin_addr.S_un.S_un_b.s_b3,
  12103. psaddrinHostAddress->sin_addr.S_un.S_un_b.s_b4,
  12104. NTOHS(psaddrinHostAddress->sin_port));
  12105. //
  12106. // Get the array of ports we're releasing.
  12107. //
  12108. psaddrinPublic = pRegisteredPort->GetUPnPPublicAddressesArray();
  12109. psaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  12110. //
  12111. // Loop through each port that we're unmapping.
  12112. //
  12113. for(dwTemp = 0; dwTemp < dwMaxValidPort; dwTemp++)
  12114. {
  12115. //
  12116. // The UPnP Internet Gateway Device spec does not require reference
  12117. // counting for mapped ports. If you register something that had
  12118. // already been registered, it will succeed silently.
  12119. //
  12120. // This means that we will never be able to tell which NAT client was
  12121. // the last person to use a given shared port. You could try detecting
  12122. // any other users at the app level (above DPNATHLP), but there is
  12123. // always a race condition. You could also have a concept of shared-
  12124. // port owner, but then you'd have to implement owner migration a la
  12125. // DPlay host migration. That is sooo not worth it.
  12126. //
  12127. // The other option is to just never unmap shared ports. You can
  12128. // probably imagine the implications of this solution, but it's what we
  12129. // have to do.
  12130. //
  12131. if (pRegisteredPort->IsSharedPort())
  12132. {
  12133. DPFX(DPFPREP, 1, "Registered port 0x%p address index %u (private address %u.%u.%u.%u:%u) is shared, not unmapping.",
  12134. pRegisteredPort, dwTemp,
  12135. psaddrinPublic[dwTemp].sin_addr.S_un.S_un_b.s_b1,
  12136. psaddrinPublic[dwTemp].sin_addr.S_un.S_un_b.s_b2,
  12137. psaddrinPublic[dwTemp].sin_addr.S_un.S_un_b.s_b3,
  12138. psaddrinPublic[dwTemp].sin_addr.S_un.S_un_b.s_b4,
  12139. NTOHS(psaddrinPublic[dwTemp].sin_port));
  12140. continue;
  12141. }
  12142. //
  12143. // If the control socket got disconnected after the last message,
  12144. // then reconnect.
  12145. //
  12146. if (! pUPnPDevice->IsConnected())
  12147. {
  12148. hr = this->ReconnectUPnPControlSocket(pUPnPDevice);
  12149. if (hr != S_OK)
  12150. {
  12151. DPFX(DPFPREP, 0, "Couldn't reconnect UPnP control socket!");
  12152. goto Failure;
  12153. }
  12154. }
  12155. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  12156. wsprintf(tszExternalPort, _T("%u"),
  12157. NTOHS(psaddrinPublic[dwTemp].sin_port));
  12158. iContentLength = strlen("<s:Envelope" EOL)
  12159. + strlen(" xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL)
  12160. + strlen(" s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL)
  12161. + strlen(" <s:Body>" EOL)
  12162. + strlen(" <u:" ACTION_DELETEPORTMAPPING_A " xmlns:u=\"") + pUPnPDevice->GetStaticServiceURILength() + strlen("\">" EOL)
  12163. + strlen(" <" ARG_DELETEPORTMAPPING_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_DELETEPORTMAPPING_NEWREMOTEHOST_A ">" EOL)
  12164. + strlen(" <" ARG_DELETEPORTMAPPING_NEWEXTERNALPORT_A ">") + _tcslen(tszExternalPort) + strlen("</" ARG_DELETEPORTMAPPING_NEWEXTERNALPORT_A ">" EOL)
  12165. + strlen(" <" ARG_DELETEPORTMAPPING_NEWPROTOCOL_A ">") + 3 + strlen("</" ARG_DELETEPORTMAPPING_NEWPROTOCOL_A ">" EOL)
  12166. + strlen(" </u:" ACTION_DELETEPORTMAPPING_A ">" EOL)
  12167. + strlen(" </s:Body>" EOL)
  12168. + strlen("</s:Envelope>" EOL)
  12169. + strlen(EOL);
  12170. wsprintf(tszContentLength, _T("%i"), iContentLength);
  12171. iMsgSize = strlen("POST ") + strlen(pUPnPDevice->GetServiceControlURL()) + strlen(" " HTTP_VERSION EOL)
  12172. + strlen("HOST: ") + _tcslen(tszHost) + strlen(EOL)
  12173. + strlen("CONTENT-LENGTH: ") + _tcslen(tszContentLength) + strlen(EOL)
  12174. + strlen("CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL)
  12175. + strlen("SOAPACTION: ") + pUPnPDevice->GetStaticServiceURILength() + strlen("#" ACTION_DELETEPORTMAPPING_A EOL)
  12176. + strlen(EOL)
  12177. + iContentLength;
  12178. //
  12179. // Allocate (or reallocate) the message buffer.
  12180. //
  12181. if (iMsgSize > iPrevMsgSize)
  12182. {
  12183. if (pszMessage != NULL)
  12184. {
  12185. DNFree(pszMessage);
  12186. pszMessage = NULL;
  12187. }
  12188. pszMessage = (char*) DNMalloc(iMsgSize + 1); // include room for NULL termination that isn't actually sent
  12189. if (pszMessage == NULL)
  12190. {
  12191. hr = DPNHERR_OUTOFMEMORY;
  12192. goto Failure;
  12193. }
  12194. iPrevMsgSize = iMsgSize;
  12195. }
  12196. strcpy(pszMessage, "POST ");
  12197. strcat(pszMessage, pUPnPDevice->GetServiceControlURL());
  12198. strcat(pszMessage, " " HTTP_VERSION EOL);
  12199. strcat(pszMessage, "HOST: ");
  12200. #ifdef UNICODE
  12201. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  12202. tszHost,
  12203. (_tcslen(tszHost) + 1));
  12204. #else // ! UNICODE
  12205. strcat(pszMessage, tszHost);
  12206. #endif // ! UNICODE
  12207. strcat(pszMessage, EOL);
  12208. strcat(pszMessage, "CONTENT-LENGTH: ");
  12209. #ifdef UNICODE
  12210. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  12211. tszContentLength,
  12212. (_tcslen(tszContentLength) + 1));
  12213. #else // ! UNICODE
  12214. strcat(pszMessage, tszContentLength);
  12215. #endif // ! UNICODE
  12216. strcat(pszMessage, EOL);
  12217. strcat(pszMessage, "CONTENT-TYPE: text/xml; charset=\"utf-8\"" EOL);
  12218. strcat(pszMessage, "SOAPACTION: ");
  12219. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  12220. strcat(pszMessage, "#" ACTION_DELETEPORTMAPPING_A EOL);
  12221. strcat(pszMessage, EOL);
  12222. strcat(pszMessage, "<s:Envelope" EOL);
  12223. strcat(pszMessage, " xmlns:s=\"" URL_SOAPENVELOPE_A "\"" EOL);
  12224. strcat(pszMessage, " s:encodingStyle=\"" URL_SOAPENCODING_A "\">" EOL);
  12225. strcat(pszMessage, " <s:Body>" EOL);
  12226. strcat(pszMessage, " <u:" ACTION_DELETEPORTMAPPING_A " xmlns:u=\"");
  12227. strcat(pszMessage, pUPnPDevice->GetStaticServiceURI());
  12228. strcat(pszMessage, "\">" EOL);
  12229. strcat(pszMessage, " <" ARG_DELETEPORTMAPPING_NEWREMOTEHOST_A ">" UPNP_WILDCARD "</" ARG_DELETEPORTMAPPING_NEWREMOTEHOST_A ">" EOL);
  12230. strcat(pszMessage, " <" ARG_DELETEPORTMAPPING_NEWEXTERNALPORT_A ">");
  12231. #ifdef UNICODE
  12232. STR_jkWideToAnsi((pszMessage + strlen(pszMessage)),
  12233. tszExternalPort,
  12234. (_tcslen(tszExternalPort) + 1));
  12235. #else // ! UNICODE
  12236. strcat(pszMessage, tszExternalPort);
  12237. #endif // ! UNICODE
  12238. strcat(pszMessage, "</" ARG_DELETEPORTMAPPING_NEWEXTERNALPORT_A ">" EOL);
  12239. strcat(pszMessage, " <" ARG_DELETEPORTMAPPING_NEWPROTOCOL_A ">");
  12240. strcat(pszMessage, ((pRegisteredPort->IsTCP()) ? "TCP" : "UDP"));
  12241. strcat(pszMessage, "</" ARG_DELETEPORTMAPPING_NEWPROTOCOL_A ">" EOL);
  12242. strcat(pszMessage, " </u:" ACTION_DELETEPORTMAPPING_A ">" EOL);
  12243. strcat(pszMessage, " </s:Body>" EOL);
  12244. strcat(pszMessage, "</s:Envelope>" EOL);
  12245. strcat(pszMessage, EOL);
  12246. #ifdef DBG
  12247. this->PrintUPnPTransactionToFile(pszMessage,
  12248. iMsgSize,
  12249. "Outbound delete port mapping request",
  12250. pDevice);
  12251. #endif // DBG
  12252. iReturn = this->m_pfnsend(pUPnPDevice->GetControlSocket(),
  12253. pszMessage,
  12254. iMsgSize,
  12255. 0);
  12256. if (iReturn == SOCKET_ERROR)
  12257. {
  12258. #ifdef DBG
  12259. dwError = this->m_pfnWSAGetLastError();
  12260. DPFX(DPFPREP, 0, "Got sockets error %u when sending control request to UPnP device!", dwError);
  12261. #endif // DBG
  12262. hr = DPNHERR_GENERIC;
  12263. goto Failure;
  12264. }
  12265. if (iReturn != iMsgSize)
  12266. {
  12267. DPFX(DPFPREP, 0, "Didn't send entire message (%i != %i)?!", iReturn, iMsgSize);
  12268. DNASSERT(FALSE);
  12269. hr = DPNHERR_GENERIC;
  12270. goto Failure;
  12271. }
  12272. //
  12273. // We have the lock so no one could have tried to receive data from
  12274. // the control socket yet. Mark the device as waiting for a
  12275. // response.
  12276. //
  12277. ZeroMemory(&RespInfo, sizeof(RespInfo));
  12278. pUPnPDevice->StartWaitingForControlResponse(CONTROLRESPONSETYPE_DELETEPORTMAPPING,
  12279. &RespInfo);
  12280. fStartedWaitingForControlResponse = TRUE;
  12281. //
  12282. // Actually wait for the response.
  12283. //
  12284. dwStartTime = GETTIMESTAMP();
  12285. dwTimeout = g_dwUPnPResponseTimeout;
  12286. do
  12287. {
  12288. hr = this->CheckForReceivedUPnPMsgsOnDevice(pUPnPDevice, dwTimeout);
  12289. if (hr != DPNH_OK)
  12290. {
  12291. DPFX(DPFPREP, 0, "Failed receiving UPnP messages!");
  12292. goto Failure;
  12293. }
  12294. //
  12295. // We either timed out or got some data. Check if we got a
  12296. // response of some type.
  12297. //
  12298. if (! pUPnPDevice->IsWaitingForControlResponse())
  12299. {
  12300. break;
  12301. }
  12302. //
  12303. // Make sure our device is still connected.
  12304. //
  12305. if (! pUPnPDevice->IsConnected())
  12306. {
  12307. DPFX(DPFPREP, 0, "UPnP device 0x%p disconnected while deleting port index %u!",
  12308. pUPnPDevice, dwTemp);
  12309. pUPnPDevice->StopWaitingForControlResponse();
  12310. hr = DPNHERR_SERVERNOTRESPONDING;
  12311. goto Failure;
  12312. }
  12313. //
  12314. // Calculate how long we have left to wait. If the calculation
  12315. // goes negative, it means we're done.
  12316. //
  12317. dwTimeout = g_dwUPnPResponseTimeout - (GETTIMESTAMP() - dwStartTime);
  12318. }
  12319. while (((int) dwTimeout > 0));
  12320. //
  12321. // If we never got the response, stop waiting for it.
  12322. //
  12323. if (pUPnPDevice->IsWaitingForControlResponse())
  12324. {
  12325. pUPnPDevice->StopWaitingForControlResponse();
  12326. DPFX(DPFPREP, 0, "Server didn't respond in time for port index %u!",
  12327. dwTemp);
  12328. hr = DPNHERR_SERVERNOTRESPONDING;
  12329. goto Failure;
  12330. }
  12331. //
  12332. // If we're here, then we've gotten a valid response (success or
  12333. // failure) from the server. If it's a failure, print out a note
  12334. // but continue.
  12335. //
  12336. #ifdef DBG
  12337. switch (RespInfo.hrErrorCode)
  12338. {
  12339. case DPNH_OK:
  12340. {
  12341. //
  12342. // Succeeded.
  12343. //
  12344. break;
  12345. }
  12346. case DPNHERR_NOMAPPING:
  12347. {
  12348. //
  12349. // UPnP device didn't know what we were talking about.
  12350. //
  12351. DPFX(DPFPREP, 1, "Server didn't recognize mapping for port index %u, continuing.",
  12352. dwTemp);
  12353. break;
  12354. }
  12355. default:
  12356. {
  12357. //
  12358. // Something else happened.
  12359. //
  12360. DPFX(DPFPREP, 0, "Server returned failure response 0x%lx for port index %u! Ignoring.",
  12361. RespInfo.hrErrorCode, dwTemp);
  12362. break;
  12363. }
  12364. }
  12365. #endif // DBG
  12366. //
  12367. // If the lease is permanent, we need to remove the reference from
  12368. // the registry.
  12369. //
  12370. if (pRegisteredPort->HasPermanentUPnPLease())
  12371. {
  12372. IN_ADDR inaddrTemp;
  12373. TCHAR tszInternalClient[16]; // "xxx.xxx.xxx.xxx" + NULL termination
  12374. TCHAR tszInternalPort[32];
  12375. DWORD dwDescriptionLength;
  12376. CRegistry RegObject;
  12377. WCHAR wszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  12378. #ifdef UNICODE
  12379. TCHAR * ptszDescription = wszDescription;
  12380. #else // ! UNICODE
  12381. char szDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  12382. TCHAR * ptszDescription = szDescription;
  12383. #endif // ! UNICODE
  12384. //
  12385. // Note that the device address is not necessarily the same as
  12386. // the address the user originally registered, particularly the
  12387. // 0.0.0.0 wildcard address will get remapped.
  12388. //
  12389. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  12390. wsprintf(tszInternalClient, _T("%u.%u.%u.%u"),
  12391. inaddrTemp.S_un.S_un_b.s_b1,
  12392. inaddrTemp.S_un.S_un_b.s_b2,
  12393. inaddrTemp.S_un.S_un_b.s_b3,
  12394. inaddrTemp.S_un.S_un_b.s_b4);
  12395. wsprintf(tszInternalPort, _T("%u"),
  12396. NTOHS(psaddrinPrivate[dwTemp].sin_port));
  12397. //
  12398. // Generate a description for this mapping. The format is:
  12399. //
  12400. // [executable_name] (nnn.nnn.nnn.nnn:nnnnn) nnnnn {"TCP" | "UDP"}
  12401. //
  12402. // That way nothing needs to be localized.
  12403. //
  12404. dwDescriptionLength = GetModuleFileName(NULL,
  12405. ptszDescription,
  12406. (MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1));
  12407. if (dwDescriptionLength != 0)
  12408. {
  12409. //
  12410. // Be paranoid and make sure the description string is valid.
  12411. //
  12412. ptszDescription[MAX_UPNP_MAPPING_DESCRIPTION_SIZE - 1] = 0;
  12413. //
  12414. // Get just the executable name from the path.
  12415. //
  12416. #ifdef WINCE
  12417. GetExeName(ptszDescription);
  12418. #else // ! WINCE
  12419. #ifdef UNICODE
  12420. _wsplitpath(ptszDescription, NULL, NULL, ptszDescription, NULL);
  12421. #else // ! UNICODE
  12422. _splitpath(ptszDescription, NULL, NULL, ptszDescription, NULL);
  12423. #endif // ! UNICODE
  12424. #endif // ! WINCE
  12425. dwDescriptionLength = _tcslen(ptszDescription) // executable name
  12426. + 2 // " ("
  12427. + _tcslen(tszInternalClient) // private IP address
  12428. + 1 // ":"
  12429. + _tcslen(tszInternalPort) // private port
  12430. + 2 // ") "
  12431. + _tcslen(tszExternalPort) // public port
  12432. + 4; // " TCP" | " UDP"
  12433. //
  12434. // Make sure the long string will fit. If not, use the
  12435. // abbreviated version.
  12436. //
  12437. if (dwDescriptionLength > MAX_UPNP_MAPPING_DESCRIPTION_SIZE)
  12438. {
  12439. dwDescriptionLength = 0;
  12440. }
  12441. }
  12442. if (dwDescriptionLength == 0)
  12443. {
  12444. //
  12445. // Use the abbreviated version we know will fit.
  12446. //
  12447. wsprintf(ptszDescription,
  12448. _T("(%s:%s) %s %s"),
  12449. tszInternalClient,
  12450. tszInternalPort,
  12451. tszExternalPort,
  12452. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  12453. }
  12454. else
  12455. {
  12456. //
  12457. // There's enough room, tack on the rest of the
  12458. // description.
  12459. //
  12460. wsprintf((ptszDescription + _tcslen(ptszDescription)),
  12461. _T(" (%s:%s) %s %s"),
  12462. tszInternalClient,
  12463. tszInternalPort,
  12464. tszExternalPort,
  12465. ((pRegisteredPort->IsTCP()) ? _T("TCP") : _T("UDP")));
  12466. }
  12467. if (fNeedToDeleteRegValue)
  12468. {
  12469. DPFX(DPFPREP, 7, "Removing NAT lease \"%s\" crash cleanup registry entry.",
  12470. ptszDescription);
  12471. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  12472. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVENATMAPPINGS,
  12473. FALSE,
  12474. TRUE,
  12475. TRUE,
  12476. DPN_KEY_ALL_ACCESS))
  12477. {
  12478. DPFX(DPFPREP, 0, "Couldn't open active NAT mapping key, unable to remove crash cleanup reference!");
  12479. }
  12480. else
  12481. {
  12482. #ifndef UNICODE
  12483. dwDescriptionLength = strlen(szDescription) + 1;
  12484. hr = STR_jkAnsiToWide(wszDescription, szDescription, dwDescriptionLength);
  12485. if (hr != S_OK)
  12486. {
  12487. DPFX(DPFPREP, 0, "Couldn't convert NAT mapping description to Unicode (err = 0x%lx), unable to remove crash cleanup reference!",
  12488. hr);
  12489. //
  12490. // Ignore error and continue.
  12491. //
  12492. hr = S_OK;
  12493. }
  12494. else
  12495. #endif // ! UNICODE
  12496. {
  12497. BOOL fResult;
  12498. //
  12499. // Ignore error.
  12500. //
  12501. fResult = RegObject.DeleteValue(wszDescription);
  12502. if (! fResult)
  12503. {
  12504. DPFX(DPFPREP, 0, "Couldn't delete NAT mapping value \"%s\"! Continuing.",
  12505. ptszDescription);
  12506. }
  12507. }
  12508. RegObject.Close();
  12509. }
  12510. }
  12511. else
  12512. {
  12513. DPFX(DPFPREP, 6, "No need to remove NAT lease \"%s\" crash cleanup registry entry.",
  12514. ptszDescription);
  12515. }
  12516. }
  12517. else
  12518. {
  12519. //
  12520. // Registered port doesn't have a permanent UPnP lease.
  12521. //
  12522. }
  12523. //
  12524. // Move on to next port.
  12525. //
  12526. }
  12527. //
  12528. // If we're here, everything was successful.
  12529. //
  12530. DPFX(DPFPREP, 8, "Registered port 0x%p mapping successfully deleted from UPnP device (0x%p).",
  12531. pRegisteredPort, pUPnPDevice);
  12532. Exit:
  12533. if (pszMessage != NULL)
  12534. {
  12535. DNFree(pszMessage);
  12536. pszMessage = NULL;
  12537. }
  12538. //
  12539. // No matter whether we succeeded or failed, remove the UPnP public addresses
  12540. // array and decrement the total lease count.
  12541. //
  12542. pRegisteredPort->DestroyUPnPPublicAddressesArray();
  12543. DNASSERT(this->m_dwNumLeases > 0);
  12544. this->m_dwNumLeases--;
  12545. DPFX(DPFPREP, 7, "UPnP lease for 0x%p removed, total num leases = %u.",
  12546. pRegisteredPort, this->m_dwNumLeases);
  12547. pRegisteredPort->NoteNotPermanentUPnPLease();
  12548. pRegisteredPort->NoteNotRemovingUPnPLease();
  12549. pUPnPDevice->DecRef();
  12550. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  12551. return hr;
  12552. Failure:
  12553. //
  12554. // If we started waiting for a response, clear that.
  12555. //
  12556. if (fStartedWaitingForControlResponse)
  12557. {
  12558. pUPnPDevice->StopWaitingForControlResponse();
  12559. }
  12560. goto Exit;
  12561. } // CNATHelpUPnP::UnmapUPnPPort
  12562. #undef DPF_MODNAME
  12563. #define DPF_MODNAME "CNATHelpUPnP::CleanupInactiveNATMappings"
  12564. //=============================================================================
  12565. // CNATHelpUPnP::CleanupInactiveNATMappings
  12566. //-----------------------------------------------------------------------------
  12567. //
  12568. // Description: Looks for any mappings previously made by other DPNHUPNP
  12569. // instances that are no longer active (because of a crash), and
  12570. // unmaps them.
  12571. //
  12572. // The UPnP device may get removed from list if a failure
  12573. // occurs, the caller needs to have a reference if it's using the
  12574. // device.
  12575. //
  12576. // The object lock is assumed to be held.
  12577. //
  12578. // Arguments:
  12579. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device to use.
  12580. //
  12581. // Returns: HRESULT
  12582. // DPNH_OK - The extension was successful.
  12583. // DPNHERR_GENERIC - An error occurred.
  12584. // DPNHERR_SERVERNOTRESPONDING - The server did not respond to the
  12585. // message.
  12586. //=============================================================================
  12587. HRESULT CNATHelpUPnP::CleanupInactiveNATMappings(CUPnPDevice * const pUPnPDevice)
  12588. {
  12589. HRESULT hr = DPNH_OK;
  12590. CDevice * pDevice;
  12591. CRegistry RegObject;
  12592. BOOL fOpenedRegistry = FALSE;
  12593. DWORD dwIndex;
  12594. WCHAR wszValueName[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  12595. DWORD dwValueNameSize;
  12596. DPNHACTIVENATMAPPING dpnhanm;
  12597. DWORD dwValueSize;
  12598. CBilink * pBilink;
  12599. CUPnPDevice * pUPnPDeviceTemp = NULL; // NULLed out because PREfast has been hassling me for a while, even though the code is safe
  12600. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  12601. DNHANDLE hNamedObject = NULL;
  12602. CRegisteredPort * pRegisteredPort = NULL;
  12603. BOOL fSetPrivateAddresses = FALSE;
  12604. SOCKADDR_IN saddrinPrivate;
  12605. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pUPnPDevice);
  12606. DNASSERT(pUPnPDevice != NULL);
  12607. pDevice = pUPnPDevice->GetOwningDevice();
  12608. DNASSERT(pDevice != NULL);
  12609. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  12610. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVENATMAPPINGS,
  12611. FALSE,
  12612. TRUE,
  12613. TRUE,
  12614. DPN_KEY_ALL_ACCESS))
  12615. {
  12616. DPFX(DPFPREP, 1, "Couldn't open active NAT mapping key, not performing crash cleanup.");
  12617. DNASSERT(hr == DPNH_OK);
  12618. goto Exit;
  12619. }
  12620. fOpenedRegistry = TRUE;
  12621. //
  12622. // Walk the list of active mappings.
  12623. //
  12624. dwIndex = 0;
  12625. do
  12626. {
  12627. dwValueNameSize = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  12628. if (! RegObject.EnumValues(wszValueName, &dwValueNameSize, dwIndex))
  12629. {
  12630. //
  12631. // There was an error or there aren't any more keys. We're done.
  12632. //
  12633. break;
  12634. }
  12635. //
  12636. // Try reading that mapping's data.
  12637. //
  12638. dwValueSize = sizeof(dpnhanm);
  12639. if (! RegObject.ReadBlob(wszValueName, (LPBYTE) (&dpnhanm), &dwValueSize))
  12640. {
  12641. //
  12642. // We don't have a lock protecting the registry, so some other
  12643. // instance could have deleted the key between when we enumerated
  12644. // it and now. We'll stop trying (and hopefully that other
  12645. // instance will cover the rest of the items).
  12646. //
  12647. DPFX(DPFPREP, 0, "Couldn't read \"%ls\" mapping value! Done with cleanup.",
  12648. wszValueName);
  12649. DNASSERT(hr == DPNH_OK);
  12650. goto Exit;
  12651. }
  12652. //
  12653. // Validate the data read.
  12654. //
  12655. if ((dwValueSize != sizeof(dpnhanm)) ||
  12656. (dpnhanm.dwVersion != ACTIVE_MAPPING_VERSION))
  12657. {
  12658. DPFX(DPFPREP, 0, "The \"%ls\" mapping value is invalid! Done with cleanup.",
  12659. wszValueName);
  12660. //
  12661. // Move to next item.
  12662. //
  12663. dwIndex++;
  12664. continue;
  12665. }
  12666. //
  12667. // See if it's owned by the local NATHelp instance.
  12668. //
  12669. if (dpnhanm.dwInstanceKey == this->m_dwInstanceKey)
  12670. {
  12671. //
  12672. // We own(ed) it. See if it was associated with a UPnP device
  12673. // that's now gone.
  12674. //
  12675. pBilink = this->m_blUPnPDevices.GetNext();
  12676. while (pBilink != &this->m_blUPnPDevices)
  12677. {
  12678. DNASSERT(! pBilink->IsEmpty());
  12679. pUPnPDeviceTemp = UPNPDEVICE_FROM_BILINK(pBilink);
  12680. if (pUPnPDeviceTemp->GetID() == dpnhanm.dwUPnPDeviceID)
  12681. {
  12682. //
  12683. // This mapping truly is active, leave it alone.
  12684. //
  12685. break;
  12686. }
  12687. pBilink = pBilink->GetNext();
  12688. }
  12689. //
  12690. // If we found the mapping, go on to the next one.
  12691. //
  12692. if (pBilink != &this->m_blUPnPDevices)
  12693. {
  12694. //
  12695. // Note that despite what PREfast v1.0.1195 says,
  12696. // pUPnPDeviceTemp will always be valid if we get here.
  12697. // However, I gave in and NULLed out the pointer up top.
  12698. //
  12699. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to current instance (%u)'s UPnP device 0x%p.",
  12700. wszValueName, dpnhanm.dwInstanceKey, pUPnPDeviceTemp);
  12701. //
  12702. // Move to next item.
  12703. //
  12704. dwIndex++;
  12705. continue;
  12706. }
  12707. //
  12708. // Otherwise, we gave up on this mapping earlier.
  12709. //
  12710. DNASSERT((this->m_dwNumDeviceRemoves > 0) || (this->m_dwNumServerFailures > 0));
  12711. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" was owned by current instance (%u)'s UPnP device ID %u that no longer exists.",
  12712. wszValueName, dpnhanm.dwInstanceKey, dpnhanm.dwUPnPDeviceID);
  12713. }
  12714. else
  12715. {
  12716. //
  12717. // See if that DPNHUPNP instance is still around.
  12718. //
  12719. #ifndef WINCE
  12720. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  12721. {
  12722. wsprintf(tszObjectName, _T( "Global\\" ) INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  12723. }
  12724. else
  12725. #endif // ! WINCE
  12726. {
  12727. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  12728. }
  12729. hNamedObject = DNOpenEvent(SYNCHRONIZE, FALSE, tszObjectName);
  12730. if (hNamedObject != NULL)
  12731. {
  12732. //
  12733. // This is still an active mapping.
  12734. //
  12735. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to instance %u, which is still active.",
  12736. wszValueName, dpnhanm.dwInstanceKey);
  12737. DNCloseHandle(hNamedObject);
  12738. hNamedObject = NULL;
  12739. //
  12740. // Move to next item.
  12741. //
  12742. dwIndex++;
  12743. continue;
  12744. }
  12745. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to instance %u, which no longer exists.",
  12746. wszValueName, dpnhanm.dwInstanceKey);
  12747. }
  12748. //
  12749. // Delete the value now that we have the information we need.
  12750. //
  12751. if (! RegObject.DeleteValue(wszValueName))
  12752. {
  12753. //
  12754. // See ReadBlob comments. Stop trying to cleanup.
  12755. //
  12756. DPFX(DPFPREP, 0, "Couldn't delete \"%ls\"! Done with cleanup.",
  12757. wszValueName);
  12758. DNASSERT(hr == DPNH_OK);
  12759. goto Exit;
  12760. }
  12761. //
  12762. // Create a fake registered port that we will deregister. Ignore the
  12763. // firewall state flags.
  12764. //
  12765. pRegisteredPort = new CRegisteredPort(0, (dpnhanm.dwFlags & REGPORTOBJMASK_UPNP));
  12766. if (pRegisteredPort == NULL)
  12767. {
  12768. hr = DPNHERR_OUTOFMEMORY;
  12769. goto Failure;
  12770. }
  12771. //
  12772. // Assert that the other UPnP information/state flags are not set.
  12773. //
  12774. DNASSERT(! pRegisteredPort->IsUPnPPortUnavailable());
  12775. DNASSERT(! pRegisteredPort->IsRemovingUPnPLease());
  12776. //
  12777. // Temporarily associate the registered port with the device.
  12778. //
  12779. pRegisteredPort->MakeDeviceOwner(pDevice);
  12780. ZeroMemory(&saddrinPrivate, sizeof(saddrinPrivate));
  12781. saddrinPrivate.sin_family = AF_INET;
  12782. saddrinPrivate.sin_addr.S_un.S_addr = dpnhanm.dwInternalAddressV4;
  12783. saddrinPrivate.sin_port = dpnhanm.wInternalPort;
  12784. //
  12785. // Store the private address.
  12786. //
  12787. hr = pRegisteredPort->SetPrivateAddresses(&saddrinPrivate, 1);
  12788. if (hr != DPNH_OK)
  12789. {
  12790. DPFX(DPFPREP, 0, "Failed creating UPnP address array!");
  12791. goto Failure;
  12792. }
  12793. fSetPrivateAddresses = TRUE;
  12794. //
  12795. // Create the public address array.
  12796. //
  12797. hr = pRegisteredPort->CreateUPnPPublicAddressesArray();
  12798. if (hr != DPNH_OK)
  12799. {
  12800. DPFX(DPFPREP, 0, "Failed creating UPnP address array!");
  12801. goto Failure;
  12802. }
  12803. //
  12804. // Fake increase the number of leases we have. It will just get
  12805. // decremented in UnmapUPnPPort.
  12806. //
  12807. DPFX(DPFPREP, 7, "Creating temporary UPnP lease 0x%p, total num leases = %u.",
  12808. pRegisteredPort, this->m_dwNumLeases);
  12809. this->m_dwNumLeases++;
  12810. //
  12811. // Store the public port.
  12812. //
  12813. pRegisteredPort->SetUPnPPublicV4Address(0,
  12814. dpnhanm.dwExternalAddressV4,
  12815. dpnhanm.wExternalPort);
  12816. //
  12817. // Actually free the port.
  12818. //
  12819. hr = this->UnmapUPnPPort(pRegisteredPort, 1, FALSE);
  12820. if (hr != DPNH_OK)
  12821. {
  12822. DPFX(DPFPREP, 0, "Failed deleting temporary UPnP port!");
  12823. goto Failure;
  12824. }
  12825. pRegisteredPort->ClearPrivateAddresses();
  12826. fSetPrivateAddresses = FALSE;
  12827. pRegisteredPort->ClearDeviceOwner();
  12828. delete pRegisteredPort;
  12829. pRegisteredPort = NULL;
  12830. //
  12831. // Move to the next mapping. Don't increment index since we just
  12832. // deleted the previous entry and everything shifts down one.
  12833. //
  12834. }
  12835. while (TRUE);
  12836. Exit:
  12837. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  12838. return hr;
  12839. Failure:
  12840. if (pRegisteredPort != NULL)
  12841. {
  12842. if (pRegisteredPort->HasUPnPPublicAddresses())
  12843. {
  12844. pRegisteredPort->DestroyUPnPPublicAddressesArray();
  12845. //
  12846. // Remove the lease counter.
  12847. //
  12848. DNASSERT(this->m_dwNumLeases > 0);
  12849. this->m_dwNumLeases--;
  12850. DPFX(DPFPREP, 7, "UPnP lease for 0x%p removed, total num leases = %u.",
  12851. pRegisteredPort, this->m_dwNumLeases);
  12852. }
  12853. if (fSetPrivateAddresses)
  12854. {
  12855. pRegisteredPort->ClearPrivateAddresses();
  12856. fSetPrivateAddresses = FALSE;
  12857. }
  12858. pRegisteredPort->ClearDeviceOwner();
  12859. delete pRegisteredPort;
  12860. pRegisteredPort = NULL;
  12861. }
  12862. if (fOpenedRegistry)
  12863. {
  12864. RegObject.Close();
  12865. }
  12866. goto Exit;
  12867. } // CNATHelpUPnP::CleanupInactiveNATMappings
  12868. #undef DPF_MODNAME
  12869. #define DPF_MODNAME "CNATHelpUPnP::IsNATPublicPortInUseLocally"
  12870. //=============================================================================
  12871. // CNATHelpUPnP::IsNATPublicPortInUseLocally
  12872. //-----------------------------------------------------------------------------
  12873. //
  12874. // Description: Looks for any mappings previously made by DPNHUPNP instances
  12875. // that are still active that use the given public port.
  12876. //
  12877. // The object lock is assumed to be held.
  12878. //
  12879. // Arguments:
  12880. // WORD wPortHostOrder - Port to check, in host byte order.
  12881. //
  12882. // Returns: BOOL
  12883. //=============================================================================
  12884. BOOL CNATHelpUPnP::IsNATPublicPortInUseLocally(const WORD wPortHostOrder)
  12885. {
  12886. BOOL fResult = FALSE;
  12887. WORD wExternalPort;
  12888. CRegistry RegObject;
  12889. BOOL fOpenedRegistry = FALSE;
  12890. DWORD dwIndex;
  12891. WCHAR wszValueName[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  12892. DWORD dwValueNameSize;
  12893. DPNHACTIVENATMAPPING dpnhanm;
  12894. CBilink * pBilink;
  12895. CUPnPDevice * pUPnPDevice = NULL; // NULLed out because PREfast has been hassling me for a while, even though the code is safe
  12896. DWORD dwValueSize;
  12897. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  12898. DNHANDLE hNamedObject = NULL;
  12899. DPFX(DPFPREP, 6, "(0x%p) Parameters: (%u)", this, wPortHostOrder);
  12900. wExternalPort = HTONS(wPortHostOrder);
  12901. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  12902. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVENATMAPPINGS,
  12903. FALSE,
  12904. TRUE,
  12905. TRUE,
  12906. DPN_KEY_ALL_ACCESS))
  12907. {
  12908. DPFX(DPFPREP, 1, "Couldn't open active NAT mapping key, assuming port not in use.");
  12909. goto Exit;
  12910. }
  12911. fOpenedRegistry = TRUE;
  12912. //
  12913. // Walk the list of active mappings.
  12914. //
  12915. dwIndex = 0;
  12916. do
  12917. {
  12918. dwValueNameSize = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  12919. if (! RegObject.EnumValues(wszValueName, &dwValueNameSize, dwIndex))
  12920. {
  12921. //
  12922. // There was an error or there aren't any more keys. We're done.
  12923. //
  12924. break;
  12925. }
  12926. //
  12927. // Try reading that mapping's data.
  12928. //
  12929. dwValueSize = sizeof(dpnhanm);
  12930. if (! RegObject.ReadBlob(wszValueName, (LPBYTE) (&dpnhanm), &dwValueSize))
  12931. {
  12932. //
  12933. // We don't have a lock protecting the registry, so some other
  12934. // instance could have deleted the key between when we enumerated
  12935. // it and now. We'll stop trying (and hopefully that other
  12936. // instance will cover the rest of the items).
  12937. //
  12938. DPFX(DPFPREP, 0, "Couldn't read \"%ls\" mapping value, assuming port not in use.",
  12939. wszValueName);
  12940. goto Exit;
  12941. }
  12942. //
  12943. // Validate the data read.
  12944. //
  12945. if ((dwValueSize != sizeof(dpnhanm)) ||
  12946. (dpnhanm.dwVersion != ACTIVE_MAPPING_VERSION))
  12947. {
  12948. DPFX(DPFPREP, 0, "The \"%ls\" mapping value is invalid, assuming port not in use.",
  12949. wszValueName);
  12950. //
  12951. // Move to next item.
  12952. //
  12953. dwIndex++;
  12954. continue;
  12955. }
  12956. //
  12957. // Is this the right port?
  12958. //
  12959. if (dpnhanm.wExternalPort == wExternalPort)
  12960. {
  12961. //
  12962. // See if it's owned by the local NATHelp instance.
  12963. //
  12964. if (dpnhanm.dwInstanceKey == this->m_dwInstanceKey)
  12965. {
  12966. //
  12967. // We own(ed) it. See if it was associated with a UPnP device
  12968. // that's now gone.
  12969. //
  12970. pBilink = this->m_blUPnPDevices.GetNext();
  12971. while (pBilink != &this->m_blUPnPDevices)
  12972. {
  12973. DNASSERT(! pBilink->IsEmpty());
  12974. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  12975. if (pUPnPDevice->GetID() == dpnhanm.dwUPnPDeviceID)
  12976. {
  12977. //
  12978. // This mapping truly still active.
  12979. //
  12980. fResult = TRUE;
  12981. break;
  12982. }
  12983. pBilink = pBilink->GetNext();
  12984. }
  12985. if (pBilink != &this->m_blUPnPDevices)
  12986. {
  12987. //
  12988. // Note that despite what PREfast v1.0.1195 says,
  12989. // pUPnPDevice will always be valid if we get here.
  12990. // However, I gave in and NULLed out the pointer up top.
  12991. //
  12992. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to current instance (%u)'s UPnP device 0x%p.",
  12993. wszValueName, dpnhanm.dwInstanceKey, pUPnPDevice);
  12994. }
  12995. else
  12996. {
  12997. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" was owned by current instance (%u)'s UPnP device ID %u that no longer exists.",
  12998. wszValueName, dpnhanm.dwInstanceKey, dpnhanm.dwUPnPDeviceID);
  12999. }
  13000. }
  13001. else
  13002. {
  13003. //
  13004. // See if that DPNHUPNP instance is still around.
  13005. //
  13006. #ifndef WINCE
  13007. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  13008. {
  13009. wsprintf(tszObjectName, _T( "Global\\" ) INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  13010. }
  13011. else
  13012. #endif // ! WINCE
  13013. {
  13014. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  13015. }
  13016. hNamedObject = DNOpenEvent(SYNCHRONIZE, FALSE, tszObjectName);
  13017. if (hNamedObject != NULL)
  13018. {
  13019. //
  13020. // This is still an active instance. Since we can't walk
  13021. // his list of UPnP devices, we have to assume the port is
  13022. // still in use.
  13023. //
  13024. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to instance %u, which is still active. Assuming port in use.",
  13025. wszValueName, dpnhanm.dwInstanceKey);
  13026. DNCloseHandle(hNamedObject);
  13027. hNamedObject = NULL;
  13028. fResult = TRUE;
  13029. }
  13030. else
  13031. {
  13032. DPFX(DPFPREP, 4, "NAT mapping \"%ls\" belongs to instance %u, which no longer exists.",
  13033. wszValueName, dpnhanm.dwInstanceKey);
  13034. }
  13035. }
  13036. //
  13037. // We found the mapping. We have our result now.
  13038. //
  13039. goto Exit;
  13040. }
  13041. //
  13042. // If we're here, this is not the external port we're looking for.
  13043. //
  13044. DPFX(DPFPREP, 8, "NAT mapping \"%ls\" does not use external port %u.",
  13045. wszValueName, wPortHostOrder);
  13046. //
  13047. // Move to the next mapping.
  13048. //
  13049. dwIndex++;
  13050. }
  13051. while (TRUE);
  13052. //
  13053. // If we're here, we didn't find the mapping.
  13054. //
  13055. DPFX(DPFPREP, 4, "Didn't find any local NAT mappings that use external port %u.",
  13056. wPortHostOrder);
  13057. Exit:
  13058. if (fOpenedRegistry)
  13059. {
  13060. RegObject.Close();
  13061. }
  13062. DPFX(DPFPREP, 6, "(0x%p) Returning: [%i]", this, fResult);
  13063. return fResult;
  13064. } // CNATHelpUPnP::IsNATPublicPortInUseLocally
  13065. #undef DPF_MODNAME
  13066. #define DPF_MODNAME "CNATHelpUPnP::CheckForUPnPAnnouncements"
  13067. //=============================================================================
  13068. // CNATHelpUPnP::CheckForUPnPAnnouncements
  13069. //-----------------------------------------------------------------------------
  13070. //
  13071. // Description: Receives any UPnP announcement messages sent to this control
  13072. // point. The entire timeout period will elapse, unless all
  13073. // devices get responses earlier.
  13074. //
  13075. // This will only send discovery requests for local devices
  13076. // unless fSendRemoteGatewayDiscovery is TRUE. However, we may
  13077. // still detect new ones if we got a straggling response from the
  13078. // last time we were allowed to send remotely.
  13079. //
  13080. // The object lock is assumed to be held.
  13081. //
  13082. // Arguments:
  13083. // DWORD dwTimeout - How long to wait for messages to
  13084. // arrive.
  13085. // BOOL fSendRemoteGatewayDiscovery - Whether we can search remotely or
  13086. // not.
  13087. //
  13088. // Returns: HRESULT
  13089. // DPNH_OK - Messages were received successfully.
  13090. // DPNHERR_GENERIC - An error occurred.
  13091. //=============================================================================
  13092. HRESULT CNATHelpUPnP::CheckForUPnPAnnouncements(const DWORD dwTimeout,
  13093. const BOOL fSendRemoteGatewayDiscovery)
  13094. {
  13095. HRESULT hr;
  13096. DWORD dwNumberOfTimes = 0;
  13097. DWORD dwCurrentTime;
  13098. DWORD dwEndTime;
  13099. DWORD dwNextSearchMessageTime;
  13100. FD_SET fdsRead;
  13101. DWORD dwNumDevicesSearchingForUPnPDevices;
  13102. timeval tv;
  13103. CBilink * pBilink;
  13104. CDevice * pDevice;
  13105. int iReturn;
  13106. int iRecvAddressSize;
  13107. char acBuffer[UPNP_DGRAM_RECV_BUFFER_SIZE];
  13108. SOCKADDR_IN saddrinRecvAddress;
  13109. DWORD dwError;
  13110. BOOL fInitiatedConnect = FALSE;
  13111. #ifdef DBG
  13112. BOOL fGotData = FALSE;
  13113. #endif // DBG
  13114. DPFX(DPFPREP, 5, "(0x%p) Parameters:(%u, %i)",
  13115. this, dwTimeout, fSendRemoteGatewayDiscovery);
  13116. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  13117. dwCurrentTime = GETTIMESTAMP();
  13118. dwEndTime = dwCurrentTime + dwTimeout;
  13119. dwNextSearchMessageTime = dwCurrentTime;
  13120. //
  13121. // Keep looping until the timeout elapses.
  13122. //
  13123. do
  13124. {
  13125. FD_ZERO(&fdsRead);
  13126. dwNumDevicesSearchingForUPnPDevices = 0;
  13127. //
  13128. // Build an FD_SET for all the sockets and send out search messages for
  13129. // all devices.
  13130. //
  13131. DNASSERT(! this->m_blDevices.IsEmpty());
  13132. pBilink = this->m_blDevices.GetNext();
  13133. while (pBilink != &this->m_blDevices)
  13134. {
  13135. DNASSERT(! pBilink->IsEmpty());
  13136. pDevice = DEVICE_FROM_BILINK(pBilink);
  13137. //
  13138. // We add it to the set whether we search or not, since if we're
  13139. // not searching, we're going to be clearing straggling messages.
  13140. //
  13141. DNASSERT(pDevice->GetUPnPDiscoverySocket() != INVALID_SOCKET);
  13142. FD_SET(pDevice->GetUPnPDiscoverySocket(), &fdsRead);
  13143. //
  13144. // Don't send search messages if we already have a UPnP device or
  13145. // this is the loopback adapter.
  13146. //
  13147. if ((pDevice->GetUPnPDevice() == NULL) &&
  13148. (pDevice->GetLocalAddressV4() != NETWORKBYTEORDER_INADDR_LOOPBACK))
  13149. {
  13150. //
  13151. // If this is the first time through the loop, clear the
  13152. // CONNRESET warning flags.
  13153. //
  13154. if (dwNumberOfTimes == 0)
  13155. {
  13156. pDevice->NoteNotGotRemoteUPnPDiscoveryConnReset();
  13157. pDevice->NoteNotGotLocalUPnPDiscoveryConnReset();
  13158. }
  13159. //
  13160. // Send out search messages if it's time.
  13161. //
  13162. if ((int) (dwNextSearchMessageTime - dwCurrentTime) <= 0)
  13163. {
  13164. hr = this->SendUPnPSearchMessagesForDevice(pDevice,
  13165. fSendRemoteGatewayDiscovery);
  13166. if (hr != DPNH_OK)
  13167. {
  13168. DPFX(DPFPREP, 0, "Couldn't send UPnP search messages via every device!");
  13169. goto Failure;
  13170. }
  13171. }
  13172. else
  13173. {
  13174. //
  13175. // Not time to send search messages.
  13176. //
  13177. }
  13178. //
  13179. // For subsequent times through the loop, make sure we didn't
  13180. // get a CONNRESET earlier telling us not to try again.
  13181. // The "attempt?" flags got set in
  13182. // SendUPnPSearchMessagesForDevice, and the CONNRESET flags
  13183. // were cleared the first time we entered here.
  13184. //
  13185. if ((pDevice->IsOKToPerformRemoteUPnPDiscovery()) ||
  13186. (pDevice->IsOKToPerformLocalUPnPDiscovery()))
  13187. {
  13188. //
  13189. // Remember that we're trying to detect an Internet Gateway
  13190. // for this device. See caveat immediately following, and
  13191. // below for this variable's usage.
  13192. //
  13193. dwNumDevicesSearchingForUPnPDevices++;
  13194. //
  13195. // Minor optimization:
  13196. //
  13197. // If we're only supposed to be trying locally, and we're
  13198. // the public address for a local gateway, assume that we
  13199. // actually shouldn't be trying locally. This is because
  13200. // Windows XP ICS keeps port 1900 open even on the public
  13201. // adapter, so we think we need to look for a local one
  13202. // even though we won't find one. So once the remote
  13203. // lookup comes back with a CONNRESET, we no longer need to
  13204. // bother trying.
  13205. //
  13206. // So first check if we're only trying locally.
  13207. //
  13208. if ((pDevice->IsOKToPerformLocalUPnPDiscovery()) &&
  13209. (! pDevice->IsOKToPerformRemoteUPnPDiscovery()))
  13210. {
  13211. CBilink * pBilinkPrivateDevice;
  13212. CDevice * pPrivateDevice;
  13213. CUPnPDevice * pUPnPDevice;
  13214. //
  13215. // Then loop through every device.
  13216. //
  13217. pBilinkPrivateDevice = this->m_blDevices.GetNext();
  13218. while (pBilinkPrivateDevice != &this->m_blDevices)
  13219. {
  13220. pPrivateDevice = DEVICE_FROM_BILINK(pBilinkPrivateDevice);
  13221. pUPnPDevice = pPrivateDevice->GetUPnPDevice();
  13222. //
  13223. // If it's not the device we're querying and it has
  13224. // a ready UPnP device, dig deeper.
  13225. //
  13226. if ((pPrivateDevice != pDevice) &&
  13227. (pUPnPDevice != NULL) &&
  13228. (pUPnPDevice->IsReady()))
  13229. {
  13230. //
  13231. // If its a local UPnP device and its public
  13232. // address is this device's address, we found a
  13233. // match.
  13234. //
  13235. if ((pUPnPDevice->IsLocal()) &&
  13236. (pUPnPDevice->GetExternalIPAddressV4() == pDevice->GetLocalAddressV4()))
  13237. {
  13238. DPFX(DPFPREP, 4, "Device 0x%p is the public address for device 0x%p's local UPnP device 0x%p, not including in search.",
  13239. pDevice, pPrivateDevice, pUPnPDevice);
  13240. //
  13241. // Remove the count we added above.
  13242. //
  13243. dwNumDevicesSearchingForUPnPDevices--;
  13244. //
  13245. // Stop searching.
  13246. //
  13247. break;
  13248. }
  13249. //
  13250. // Otherwise keep going.
  13251. //
  13252. 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.",
  13253. pPrivateDevice,
  13254. pUPnPDevice,
  13255. (! pUPnPDevice->IsLocal()),
  13256. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b1,
  13257. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b2,
  13258. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b3,
  13259. pUPnPDevice->GetControlAddress()->sin_addr.S_un.S_un_b.s_b4,
  13260. pDevice);
  13261. }
  13262. else
  13263. {
  13264. 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.",
  13265. pPrivateDevice, pDevice, pUPnPDevice);
  13266. }
  13267. //
  13268. // Go on to next device.
  13269. //
  13270. pBilinkPrivateDevice = pBilinkPrivateDevice->GetNext();
  13271. }
  13272. }
  13273. else
  13274. {
  13275. //
  13276. // Either not searching locally, or searching both
  13277. // locally and remotely.
  13278. //
  13279. DPFX(DPFPREP, 8, "Device 0x%p local search OK = %i, remote search OK = %i.",
  13280. pDevice,
  13281. pDevice->IsOKToPerformLocalUPnPDiscovery(),
  13282. pDevice->IsOKToPerformRemoteUPnPDiscovery());
  13283. }
  13284. }
  13285. else
  13286. {
  13287. DPFX(DPFPREP, 3, "Device 0x%p should not perform UPnP discovery.",
  13288. pDevice);
  13289. }
  13290. }
  13291. else
  13292. {
  13293. DPFX(DPFPREP, 8, "Device 0x%p already has UPnP device (0x%p) or is loopback address.",
  13294. pDevice, pDevice->GetUPnPDevice());
  13295. }
  13296. pBilink = pBilink->GetNext();
  13297. }
  13298. //
  13299. // Wait for any data, unless all devices already have an Internet
  13300. // Gateway, in which case we only want to clear the receive queue for
  13301. // the sockets.
  13302. //
  13303. if (dwNumDevicesSearchingForUPnPDevices == 0)
  13304. {
  13305. DPFX(DPFPREP, 7, "No devices need to search for UPnP devices, clearing straggling messages from sockets.");
  13306. tv.tv_usec = 0;
  13307. }
  13308. else
  13309. {
  13310. //
  13311. // Calculate the next time to send if we just sent search messages.
  13312. //
  13313. if ((int) (dwNextSearchMessageTime - dwCurrentTime) <= 0)
  13314. {
  13315. dwNextSearchMessageTime += UPNP_SEARCH_MESSAGE_INTERVAL;
  13316. //
  13317. // If we took way longer than expected in a previous loop
  13318. // (because of stress or Win9x errata), the next search time
  13319. // may have already passed. Just search right now if that's
  13320. // the case.
  13321. //
  13322. if ((int) (dwNextSearchMessageTime - dwCurrentTime) <= 0)
  13323. {
  13324. dwNextSearchMessageTime = dwCurrentTime;
  13325. }
  13326. }
  13327. //
  13328. // See how long we should wait for responses. Choose the total end
  13329. // time or the next search message time, whichever is shorter.
  13330. //
  13331. if ((int) (dwEndTime - dwNextSearchMessageTime) < 0)
  13332. {
  13333. DPFX(DPFPREP, 7, "Waiting %u ms for incoming responses.",
  13334. (dwEndTime - dwCurrentTime));
  13335. tv.tv_usec = (dwEndTime - dwCurrentTime) * 1000;
  13336. }
  13337. else
  13338. {
  13339. DPFX(DPFPREP, 7, "Waiting %u ms for incoming responses, and then might send search messages again.",
  13340. (dwNextSearchMessageTime - dwCurrentTime));
  13341. tv.tv_usec = (dwNextSearchMessageTime - dwCurrentTime) * 1000;
  13342. }
  13343. }
  13344. tv.tv_sec = 0;
  13345. iReturn = this->m_pfnselect(0, &fdsRead, NULL, NULL, &tv);
  13346. if (iReturn == SOCKET_ERROR)
  13347. {
  13348. #ifdef DBG
  13349. dwError = this->m_pfnWSAGetLastError();
  13350. DPFX(DPFPREP, 0, "Got sockets error %u trying to select on UPnP discovery sockets!", dwError);
  13351. #endif // DBG
  13352. hr = DPNHERR_GENERIC;
  13353. goto Failure;
  13354. }
  13355. //
  13356. // See if any sockets were selected.
  13357. //
  13358. if (iReturn > 0)
  13359. {
  13360. //
  13361. // Loop through all devices, looking for those that have data.
  13362. //
  13363. pBilink = this->m_blDevices.GetNext();
  13364. while (pBilink != &this->m_blDevices)
  13365. {
  13366. DNASSERT(! pBilink->IsEmpty());
  13367. pDevice = DEVICE_FROM_BILINK(pBilink);
  13368. //
  13369. // If this device's socket is set there's data to read.
  13370. //
  13371. //if (FD_ISSET(pDevice->GetUPnPDiscoverySocket(), &fdsRead))
  13372. if (this->m_pfn__WSAFDIsSet(pDevice->GetUPnPDiscoverySocket(), &fdsRead))
  13373. {
  13374. #ifdef DBG
  13375. fGotData = TRUE;
  13376. #endif // DBG
  13377. iRecvAddressSize = sizeof(saddrinRecvAddress);
  13378. iReturn = this->m_pfnrecvfrom(pDevice->GetUPnPDiscoverySocket(),
  13379. acBuffer,
  13380. (sizeof(acBuffer) - 1), // -1 to allow string termination
  13381. 0,
  13382. (SOCKADDR*) (&saddrinRecvAddress),
  13383. &iRecvAddressSize);
  13384. if ((iReturn == 0) || (iReturn == SOCKET_ERROR))
  13385. {
  13386. dwError = this->m_pfnWSAGetLastError();
  13387. //
  13388. // WSAENOBUFS means WinSock is out of memory.
  13389. //
  13390. if (dwError == WSAENOBUFS)
  13391. {
  13392. DPFX(DPFPREP, 0, "WinSock returned WSAENOBUFS while receiving discovery response!");
  13393. hr = DPNHERR_OUTOFMEMORY;
  13394. goto Failure;
  13395. }
  13396. //
  13397. // All other errors besides WSAECONNRESET are
  13398. // unexpected and mean we should bail.
  13399. //
  13400. if (dwError != WSAECONNRESET)
  13401. {
  13402. DPFX(DPFPREP, 0, "Got sockets error %u trying to receive on device 0x%p!",
  13403. dwError, pDevice);
  13404. hr = DPNHERR_GENERIC;
  13405. goto Failure;
  13406. }
  13407. //
  13408. // If we're here, it must be WSAECONNRESET. Correlate
  13409. // it with the outbound message that generated it so we
  13410. // don't bother waiting for a response from that
  13411. // location.
  13412. // Validate that it's for the port to which the message
  13413. // should have been sent.
  13414. //
  13415. if (saddrinRecvAddress.sin_port == HTONS(UPNP_PORT))
  13416. {
  13417. if (saddrinRecvAddress.sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4())
  13418. {
  13419. DPFX(DPFPREP, 1, "Got CONNRESET for local discovery attempt on device 0x%p (%u.%u.%u.%u:%u).",
  13420. pDevice,
  13421. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b1,
  13422. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b2,
  13423. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b3,
  13424. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b4,
  13425. NTOHS(saddrinRecvAddress.sin_port));
  13426. //
  13427. // Note the local error.
  13428. //
  13429. pDevice->NoteGotLocalUPnPDiscoveryConnReset();
  13430. }
  13431. else
  13432. {
  13433. if (! g_fUseMulticastUPnPDiscovery)
  13434. {
  13435. IN_ADDR inaddrGateway;
  13436. if ((! this->GetAddressToReachGateway(pDevice, &inaddrGateway)) ||
  13437. (inaddrGateway.S_un.S_addr == INADDR_BROADCAST) ||
  13438. (saddrinRecvAddress.sin_addr.S_un.S_addr == inaddrGateway.S_un.S_addr))
  13439. {
  13440. DPFX(DPFPREP, 2, "Got CONNRESET for remote discovery attempt on device 0x%p (%u.%u.%u.%u:%u).",
  13441. pDevice,
  13442. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b1,
  13443. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b2,
  13444. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b3,
  13445. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b4,
  13446. NTOHS(saddrinRecvAddress.sin_port));
  13447. //
  13448. // Note the remote error.
  13449. //
  13450. pDevice->NoteGotRemoteUPnPDiscoveryConnReset();
  13451. }
  13452. else
  13453. {
  13454. DPFX(DPFPREP, 1, "Ignoring CONNRESET on device 0x%p, sender %u.%u.%u.%u is not gateway %u.%u.%u.%u.",
  13455. pDevice,
  13456. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b1,
  13457. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b2,
  13458. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b3,
  13459. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b4,
  13460. inaddrGateway.S_un.S_un_b.s_b1,
  13461. inaddrGateway.S_un.S_un_b.s_b2,
  13462. inaddrGateway.S_un.S_un_b.s_b3,
  13463. inaddrGateway.S_un.S_un_b.s_b4);
  13464. }
  13465. }
  13466. else
  13467. {
  13468. DPFX(DPFPREP, 1, "Ignoring CONNRESET on device 0x%p from sender %u.%u.%u.%u, we are using multicast discovery.",
  13469. pDevice,
  13470. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b1,
  13471. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b2,
  13472. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b3,
  13473. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b4);
  13474. }
  13475. }
  13476. }
  13477. else
  13478. {
  13479. DPFX(DPFPREP, 1, "Ignoring CONNRESET on device 0x%p for invalid port (%u.%u.%u.%u:%u).",
  13480. pDevice,
  13481. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b1,
  13482. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b2,
  13483. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b3,
  13484. saddrinRecvAddress.sin_addr.S_un.S_un_b.s_b4,
  13485. NTOHS(saddrinRecvAddress.sin_port));
  13486. }
  13487. }
  13488. else
  13489. {
  13490. DNASSERT(iRecvAddressSize == sizeof(saddrinRecvAddress));
  13491. DNASSERT(iReturn < sizeof(acBuffer));
  13492. hr = this->HandleUPnPDiscoveryResponseMsg(pDevice,
  13493. &saddrinRecvAddress,
  13494. acBuffer,
  13495. iReturn,
  13496. &fInitiatedConnect);
  13497. if (hr != DPNH_OK)
  13498. {
  13499. DPFX(DPFPREP, 0, "Couldn't handle UPnP discovery response message (err = 0x%lx), ignoring.",
  13500. hr);
  13501. }
  13502. }
  13503. }
  13504. pBilink = pBilink->GetNext();
  13505. }
  13506. //
  13507. // Make sure we actually found a socket with data.
  13508. //
  13509. DNASSERT(fGotData);
  13510. }
  13511. else
  13512. {
  13513. //
  13514. // We timed out. If we were just clearing receive buffers for the
  13515. // socket(s), we're done.
  13516. //
  13517. if (dwNumDevicesSearchingForUPnPDevices == 0)
  13518. {
  13519. break;
  13520. }
  13521. }
  13522. //
  13523. // Increase the counter.
  13524. //
  13525. dwNumberOfTimes++;
  13526. //
  13527. // Get current time for figuring out how much longer to wait.
  13528. //
  13529. dwCurrentTime = GETTIMESTAMP();
  13530. }
  13531. while ((int) (dwEndTime - dwCurrentTime) > 0);
  13532. hr = DPNH_OK;
  13533. //
  13534. // If we initiated connections to any UPnP devices, wait for them to
  13535. // complete.
  13536. //
  13537. if (fInitiatedConnect)
  13538. {
  13539. hr = this->WaitForUPnPConnectCompletions();
  13540. if (hr != DPNH_OK)
  13541. {
  13542. DPFX(DPFPREP, 0, "Couldn't wait for UPnP connect completions!");
  13543. goto Failure;
  13544. }
  13545. }
  13546. Exit:
  13547. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  13548. return hr;
  13549. Failure:
  13550. goto Exit;
  13551. } // CNATHelpUPnP::CheckForUPnPAnnouncements
  13552. #undef DPF_MODNAME
  13553. #define DPF_MODNAME "CNATHelpUPnP::WaitForUPnPConnectCompletions"
  13554. //=============================================================================
  13555. // CNATHelpUPnP::WaitForUPnPConnectCompletions
  13556. //-----------------------------------------------------------------------------
  13557. //
  13558. // Description: Waits for completions for pending TCP connects to UPnP
  13559. // Internet gateway devices.
  13560. //
  13561. // UPnP devices may get removed from list if a failure occurs.
  13562. //
  13563. // The object lock is assumed to be held.
  13564. //
  13565. // Arguments: None.
  13566. //
  13567. // Returns: HRESULT
  13568. // DPNH_OK - Connects were handled successfully.
  13569. // DPNHERR_GENERIC - An error occurred.
  13570. //=============================================================================
  13571. HRESULT CNATHelpUPnP::WaitForUPnPConnectCompletions(void)
  13572. {
  13573. HRESULT hr;
  13574. int iNumSockets;
  13575. FD_SET fdsWrite;
  13576. FD_SET fdsExcept;
  13577. CBilink * pBilink;
  13578. CUPnPDevice * pUPnPDevice;
  13579. timeval tv;
  13580. int iReturn;
  13581. BOOL fRequestedDescription = FALSE;
  13582. DWORD dwStartTime;
  13583. DWORD dwTimeout;
  13584. CDevice * pDevice;
  13585. #ifdef DBG
  13586. BOOL fFoundCompletion;
  13587. DWORD dwError;
  13588. #endif // DBG
  13589. DPFX(DPFPREP, 5, "(0x%p) Enter", this);
  13590. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  13591. //
  13592. // Loop until all sockets are connected or there's a timeout.
  13593. //
  13594. do
  13595. {
  13596. //
  13597. // Check for any connect completions. Start by building two FD_SETs
  13598. // for all the sockets with pending connects.
  13599. //
  13600. FD_ZERO(&fdsWrite);
  13601. FD_ZERO(&fdsExcept);
  13602. iNumSockets = 0;
  13603. pBilink = this->m_blUPnPDevices.GetNext();
  13604. while (pBilink != &this->m_blUPnPDevices)
  13605. {
  13606. DNASSERT(! pBilink->IsEmpty());
  13607. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13608. if (pUPnPDevice->IsConnecting())
  13609. {
  13610. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  13611. FD_SET(pUPnPDevice->GetControlSocket(), &fdsWrite);
  13612. FD_SET(pUPnPDevice->GetControlSocket(), &fdsExcept);
  13613. iNumSockets++;
  13614. }
  13615. pBilink = pBilink->GetNext();
  13616. }
  13617. //
  13618. // If there weren't any sockets that had pending connections, then
  13619. // we're done here.
  13620. //
  13621. if (iNumSockets <= 0)
  13622. {
  13623. DPFX(DPFPREP, 7, "No more UPnP device control sockets with pending connections.");
  13624. break;
  13625. }
  13626. DPFX(DPFPREP, 7, "There are %i UPnP device control sockets with pending connections.",
  13627. iNumSockets);
  13628. //
  13629. // Wait for connect completions. We don't wait for the full TCP/IP
  13630. // timeout (which is why we made it non-blocking).
  13631. //
  13632. tv.tv_usec = 0;
  13633. tv.tv_sec = g_dwUPnPConnectTimeout;
  13634. iReturn = this->m_pfnselect(0, NULL, &fdsWrite, &fdsExcept, &tv);
  13635. if (iReturn == SOCKET_ERROR)
  13636. {
  13637. #ifdef DBG
  13638. dwError = this->m_pfnWSAGetLastError();
  13639. DPFX(DPFPREP, 0, "Got sockets error %u trying to select on UPnP device sockets!",
  13640. dwError);
  13641. #endif // DBG
  13642. hr = DPNHERR_GENERIC;
  13643. goto Failure;
  13644. }
  13645. //
  13646. // If no sockets were selected, that means the connections timed out.
  13647. // Remove all the devices that were waiting.
  13648. //
  13649. if (iReturn == 0)
  13650. {
  13651. DPFX(DPFPREP, 3, "Select for %u seconds timed out.", g_dwUPnPConnectTimeout);
  13652. pBilink = this->m_blUPnPDevices.GetNext();
  13653. while (pBilink != &this->m_blUPnPDevices)
  13654. {
  13655. DNASSERT(! pBilink->IsEmpty());
  13656. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13657. pBilink = pBilink->GetNext();
  13658. if (pUPnPDevice->IsConnecting())
  13659. {
  13660. DPFX(DPFPREP, 7, "UPnP device 0x%p is still connecting, removing.",
  13661. pUPnPDevice);
  13662. //
  13663. // Dump this unusable UPnP device and continue.
  13664. //
  13665. pDevice = pUPnPDevice->GetOwningDevice();
  13666. DNASSERT(pUPnPDevice->GetOwningDevice() != NULL);
  13667. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  13668. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  13669. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  13670. //
  13671. // This may cause our pUPnPDevice pointer to become
  13672. // invalid.
  13673. //
  13674. this->ClearDevicesUPnPDevice(pDevice);
  13675. #ifdef DBG
  13676. iNumSockets--;
  13677. #endif // DBG
  13678. }
  13679. else
  13680. {
  13681. DPFX(DPFPREP, 7, "UPnP device 0x%p is not trying to connect or has safely connected.",
  13682. pUPnPDevice);
  13683. }
  13684. }
  13685. //
  13686. // We should have destroyed the same number of devices that were
  13687. // waiting.
  13688. //
  13689. DNASSERT(iNumSockets == 0);
  13690. //
  13691. // Continue on to handling any sockets succeeded previously.
  13692. //
  13693. break;
  13694. }
  13695. //
  13696. // If we're here, some sockets were signalled.
  13697. //
  13698. #ifdef DBG
  13699. DPFX(DPFPREP, 7, "There are %i sockets with connect activity.", iReturn);
  13700. fFoundCompletion = FALSE;
  13701. #endif // DBG
  13702. pBilink = this->m_blUPnPDevices.GetNext();
  13703. while (pBilink != &this->m_blUPnPDevices)
  13704. {
  13705. DNASSERT(! pBilink->IsEmpty());
  13706. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13707. pBilink = pBilink->GetNext();
  13708. if (pUPnPDevice->IsConnecting())
  13709. {
  13710. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  13711. //
  13712. // If this UPnP device's socket is in the write set it
  13713. // connected successfully.
  13714. //
  13715. //if (FD_ISSET(pUPnPDevice->GetControlSocket(), &fdsWrite))
  13716. if (this->m_pfn__WSAFDIsSet(pUPnPDevice->GetControlSocket(), &fdsWrite))
  13717. {
  13718. pUPnPDevice->NoteConnected();
  13719. #ifdef DBG
  13720. fFoundCompletion = TRUE;
  13721. #endif // DBG
  13722. if (! pUPnPDevice->IsReady())
  13723. {
  13724. DPFX(DPFPREP, 2, "UPnP device object 0x%p now connected to Internet gateway device.",
  13725. pUPnPDevice);
  13726. hr = this->SendUPnPDescriptionRequest(pUPnPDevice);
  13727. if (hr != DPNH_OK)
  13728. {
  13729. DPFX(DPFPREP, 0, "Couldn't send UPnP description request to device object 0x%p! Disconnecting.",
  13730. pUPnPDevice);
  13731. //
  13732. // Dump this unusable UPnP device and continue.
  13733. //
  13734. pDevice = pUPnPDevice->GetOwningDevice();
  13735. DNASSERT(pUPnPDevice->GetOwningDevice() != NULL);
  13736. //
  13737. // This may cause our pUPnPDevice pointer to become
  13738. // invalid.
  13739. //
  13740. this->ClearDevicesUPnPDevice(pDevice);
  13741. }
  13742. else
  13743. {
  13744. fRequestedDescription = TRUE;
  13745. }
  13746. }
  13747. else
  13748. {
  13749. DPFX(DPFPREP, 2, "UPnP device object 0x%p successfully reconnected to Internet gateway device.",
  13750. pUPnPDevice);
  13751. }
  13752. }
  13753. else
  13754. {
  13755. //
  13756. // If this UPnP device's socket is in the except set it
  13757. // failed to connect.
  13758. //
  13759. //if (FD_ISSET(pUPnPDevice->GetControlSocket(), &fdsExcept))
  13760. if (this->m_pfn__WSAFDIsSet(pUPnPDevice->GetControlSocket(), &fdsExcept))
  13761. {
  13762. #ifdef DBG
  13763. int iError;
  13764. int iErrorSize;
  13765. fFoundCompletion = TRUE;
  13766. //
  13767. // Print out the reason why it couldn't connect.
  13768. // Ignore the direct return code from getsockopt.
  13769. //
  13770. iError = 0;
  13771. iErrorSize = sizeof(iError);
  13772. this->m_pfngetsockopt(pUPnPDevice->GetControlSocket(),
  13773. SOL_SOCKET,
  13774. SO_ERROR,
  13775. (char*) (&iError),
  13776. &iErrorSize);
  13777. DPFX(DPFPREP, 1, "Connecting to UPnP device object 0x%p failed with error %i, removing from list.",
  13778. pUPnPDevice, iError);
  13779. #endif // DBG
  13780. //
  13781. // This UPnP device is useless if it doesn't respond.
  13782. // Throw it out.
  13783. //
  13784. pDevice = pUPnPDevice->GetOwningDevice();
  13785. DNASSERT(pUPnPDevice->GetOwningDevice() != NULL);
  13786. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  13787. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  13788. //
  13789. // This may cause our pUPnPDevice pointer to become
  13790. // invalid.
  13791. //
  13792. this->ClearDevicesUPnPDevice(pDevice);
  13793. }
  13794. else
  13795. {
  13796. //
  13797. // Socket is still connecting.
  13798. //
  13799. }
  13800. }
  13801. }
  13802. else
  13803. {
  13804. //
  13805. // This socket is already connected.
  13806. //
  13807. }
  13808. }
  13809. //
  13810. // Make sure we actually found a socket with a connect completion this
  13811. // time through.
  13812. //
  13813. DNASSERT(fFoundCompletion);
  13814. }
  13815. while (TRUE);
  13816. //
  13817. // If we're here, all UPnP devices are connected or have since been
  13818. // destroyed.
  13819. //
  13820. if (fRequestedDescription)
  13821. {
  13822. //
  13823. // Wait for the description responses to come back.
  13824. //
  13825. dwStartTime = GETTIMESTAMP();
  13826. dwTimeout = g_dwUPnPResponseTimeout;
  13827. do
  13828. {
  13829. hr = this->CheckForReceivedUPnPMsgsOnAllDevices(dwTimeout);
  13830. if (hr != DPNH_OK)
  13831. {
  13832. DPFX(DPFPREP, 0, "Failed receiving UPnP messages!");
  13833. goto Failure;
  13834. }
  13835. //
  13836. // We either timed out or got some data. Check if we got the
  13837. // response(s) we need. Reuse the fRequestedDescription
  13838. // boolean.
  13839. //
  13840. fRequestedDescription = FALSE;
  13841. pBilink = this->m_blUPnPDevices.GetNext();
  13842. while (pBilink != &this->m_blUPnPDevices)
  13843. {
  13844. DNASSERT(! pBilink->IsEmpty());
  13845. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13846. if (! pUPnPDevice->IsReady())
  13847. {
  13848. if (pUPnPDevice->IsConnected())
  13849. {
  13850. DPFX(DPFPREP, 7, "UPnP device 0x%p is not ready yet.",
  13851. pUPnPDevice);
  13852. fRequestedDescription = TRUE;
  13853. }
  13854. else
  13855. {
  13856. DPFX(DPFPREP, 4, "UPnP device 0x%p got disconnected before receiving description response.",
  13857. pUPnPDevice);
  13858. }
  13859. break;
  13860. }
  13861. pBilink = pBilink->GetNext();
  13862. }
  13863. if (! fRequestedDescription)
  13864. {
  13865. DPFX(DPFPREP, 6, "All UPnP devices are ready or disconnected now.");
  13866. //
  13867. // Break out of the wait loop.
  13868. //
  13869. break;
  13870. }
  13871. //
  13872. // Calculate how long we have left to wait. If the calculation
  13873. // goes negative, it means we're done.
  13874. //
  13875. dwTimeout = g_dwUPnPResponseTimeout - (GETTIMESTAMP() - dwStartTime);
  13876. }
  13877. while (((int) dwTimeout > 0));
  13878. //
  13879. // Any devices that still aren't ready yet were either disconnected or
  13880. // are taking too long and should be removed.
  13881. //
  13882. pBilink = this->m_blUPnPDevices.GetNext();
  13883. while (pBilink != &this->m_blUPnPDevices)
  13884. {
  13885. DNASSERT(! pBilink->IsEmpty());
  13886. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13887. pBilink = pBilink->GetNext();
  13888. if (! pUPnPDevice->IsReady())
  13889. {
  13890. DPFX(DPFPREP, 1, "UPnP device 0x%p got disconnected or took too long to get ready, removing.",
  13891. pUPnPDevice);
  13892. pDevice = pUPnPDevice->GetOwningDevice();
  13893. DNASSERT(pUPnPDevice->GetOwningDevice() != NULL);
  13894. //
  13895. // This may cause our pUPnPDevice pointer to become
  13896. // invalid.
  13897. //
  13898. this->ClearDevicesUPnPDevice(pDevice);
  13899. }
  13900. }
  13901. }
  13902. else
  13903. {
  13904. DPFX(DPFPREP, 7, "Did not request any descriptions.");
  13905. }
  13906. //
  13907. // If we're here, everything is successful and we're done.
  13908. //
  13909. hr = DPNH_OK;
  13910. Exit:
  13911. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  13912. return hr;
  13913. Failure:
  13914. goto Exit;
  13915. } // CNATHelpUPnP::WaitForUPnPConnectCompletions
  13916. #undef DPF_MODNAME
  13917. #define DPF_MODNAME "CNATHelpUPnP::CheckForReceivedUPnPMsgsOnAllDevices"
  13918. //=============================================================================
  13919. // CNATHelpUPnP::CheckForReceivedUPnPMsgsOnAllDevices
  13920. //-----------------------------------------------------------------------------
  13921. //
  13922. // Description: Handles any incoming data on the TCP sockets connect to UPnP
  13923. // Internet gateway devices.
  13924. //
  13925. // UPnP devices with failures may get removed from the list.
  13926. //
  13927. // The object lock is assumed to be held.
  13928. //
  13929. // Arguments:
  13930. // DWORD dwTimeout - How long to wait for messages to arrive, or 0 to just
  13931. // poll.
  13932. //
  13933. // Returns: HRESULT
  13934. // DPNH_OK - Messages were handled successfully.
  13935. // DPNHERR_GENERIC - An error occurred.
  13936. //=============================================================================
  13937. HRESULT CNATHelpUPnP::CheckForReceivedUPnPMsgsOnAllDevices(const DWORD dwTimeout)
  13938. {
  13939. HRESULT hr;
  13940. int iNumSockets;
  13941. FD_SET fdsRead;
  13942. CBilink * pBilink;
  13943. CUPnPDevice * pUPnPDevice;
  13944. timeval tv;
  13945. int iReturn;
  13946. #ifdef DBG
  13947. BOOL fFoundData = FALSE;
  13948. DWORD dwError;
  13949. #endif // DBG
  13950. DPFX(DPFPREP, 5, "(0x%p) Parameters: (%u)", this, dwTimeout);
  13951. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  13952. Rewait:
  13953. iNumSockets = 0;
  13954. //
  13955. // Check for any data. Start by building an FD_SET for all the sockets
  13956. // with completed connections.
  13957. //
  13958. FD_ZERO(&fdsRead);
  13959. pBilink = this->m_blUPnPDevices.GetNext();
  13960. while (pBilink != &this->m_blUPnPDevices)
  13961. {
  13962. DNASSERT(! pBilink->IsEmpty());
  13963. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  13964. if (pUPnPDevice->IsConnected())
  13965. {
  13966. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  13967. FD_SET(pUPnPDevice->GetControlSocket(), &fdsRead);
  13968. iNumSockets++;
  13969. }
  13970. pBilink = pBilink->GetNext();
  13971. }
  13972. //
  13973. // If there weren't any sockets that were connected, then we're done here.
  13974. //
  13975. if (iNumSockets <= 0)
  13976. {
  13977. DPFX(DPFPREP, 7, "No connected UPnP device control sockets.");
  13978. hr = DPNH_OK;
  13979. goto Exit;
  13980. }
  13981. DPFX(DPFPREP, 7, "There are %i connected UPnP device control sockets.",
  13982. iNumSockets);
  13983. //
  13984. // Wait for received data.
  13985. //
  13986. tv.tv_usec = dwTimeout * 1000;
  13987. tv.tv_sec = 0;
  13988. iReturn = this->m_pfnselect(0, &fdsRead, NULL, NULL, &tv);
  13989. if (iReturn == SOCKET_ERROR)
  13990. {
  13991. #ifdef DBG
  13992. dwError = this->m_pfnWSAGetLastError();
  13993. DPFX(DPFPREP, 0, "Got sockets error %u trying to select on UPnP device sockets!",
  13994. dwError);
  13995. #endif // DBG
  13996. hr = DPNHERR_GENERIC;
  13997. goto Failure;
  13998. }
  13999. //
  14000. // If no sockets were selected, we're done.
  14001. //
  14002. if (iReturn == 0)
  14003. {
  14004. DPFX(DPFPREP, 7, "Timed out waiting for data on %i sockets.",
  14005. iNumSockets);
  14006. hr = DPNH_OK;
  14007. goto Exit;
  14008. }
  14009. //
  14010. // If we're here, some sockets were signalled.
  14011. //
  14012. pBilink = this->m_blUPnPDevices.GetNext();
  14013. while (pBilink != &this->m_blUPnPDevices)
  14014. {
  14015. DNASSERT(! pBilink->IsEmpty());
  14016. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilink);
  14017. pBilink = pBilink->GetNext();
  14018. if (pUPnPDevice->IsConnected())
  14019. {
  14020. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  14021. //
  14022. // If this UPnP device's socket is in the read set it has data.
  14023. //
  14024. //if (FD_ISSET(pUPnPDevice->GetControlSocket(), &fdsRead))
  14025. if (this->m_pfn__WSAFDIsSet(pUPnPDevice->GetControlSocket(), &fdsRead))
  14026. {
  14027. #ifdef DBG
  14028. fFoundData = TRUE;
  14029. #endif // DBG
  14030. //
  14031. // Grab a reference, since ReceiveUPnPDataStream may clear the
  14032. // device.
  14033. //
  14034. pUPnPDevice->AddRef();
  14035. hr = this->ReceiveUPnPDataStream(pUPnPDevice);
  14036. if (hr != DPNH_OK)
  14037. {
  14038. DPFX(DPFPREP, 0, "Couldn't receive UPnP stream from device object 0x%p (err = 0x%lx)! Disconnecting.",
  14039. pUPnPDevice, hr);
  14040. //
  14041. // Dump this unusable UPnP device and continue.
  14042. //
  14043. if (pUPnPDevice->GetOwningDevice() != NULL)
  14044. {
  14045. this->ClearDevicesUPnPDevice(pUPnPDevice->GetOwningDevice());
  14046. }
  14047. else
  14048. {
  14049. DPFX(DPFPREP, 1, "UPnP device 0x%p's has already been removed from owning device.",
  14050. pUPnPDevice);
  14051. }
  14052. hr = DPNH_OK;
  14053. }
  14054. //
  14055. // Remove the reference we added.
  14056. //
  14057. pUPnPDevice->DecRef();
  14058. }
  14059. else
  14060. {
  14061. //
  14062. // Socket does not have any data.
  14063. //
  14064. DPFX(DPFPREP, 8, "Skipping UPnP device 0x%p because it does not have any data.",
  14065. pUPnPDevice);
  14066. }
  14067. }
  14068. else
  14069. {
  14070. //
  14071. // This socket is not connected yet/anymore.
  14072. //
  14073. DPFX(DPFPREP, 7, "Skipping unconnected UPnP device 0x%p.", pUPnPDevice);
  14074. }
  14075. }
  14076. //
  14077. // Make sure we actually found a socket with data.
  14078. //
  14079. DNASSERT(fFoundData);
  14080. //
  14081. // We found data, so see if there's more. Connection should be closed
  14082. // after responses.
  14083. //
  14084. DPFX(DPFPREP, 7, "Waiting for more data on the sockets.");
  14085. goto Rewait;
  14086. Exit:
  14087. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  14088. return hr;
  14089. Failure:
  14090. goto Exit;
  14091. } // CNATHelpUPnP::CheckForReceivedUPnPMsgsOnAllDevices
  14092. #undef DPF_MODNAME
  14093. #define DPF_MODNAME "CNATHelpUPnP::CheckForReceivedUPnPMsgsOnDevice"
  14094. //=============================================================================
  14095. // CNATHelpUPnP::CheckForReceivedUPnPMsgsOnDevice
  14096. //-----------------------------------------------------------------------------
  14097. //
  14098. // Description: Handles any incoming data on the TCP socket for the given
  14099. // UPnP device.
  14100. //
  14101. // If the UPnP device encounters a failure, it may get removed
  14102. // from the list.
  14103. //
  14104. // The object lock is assumed to be held.
  14105. //
  14106. // Arguments:
  14107. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device to receive data.
  14108. // DWORD dwTimeout - How long to wait for messages to arrive, or 0
  14109. // to just poll.
  14110. //
  14111. // Returns: HRESULT
  14112. // DPNH_OK - Messages were handled successfully.
  14113. // DPNHERR_GENERIC - An error occurred.
  14114. //=============================================================================
  14115. HRESULT CNATHelpUPnP::CheckForReceivedUPnPMsgsOnDevice(CUPnPDevice * const pUPnPDevice,
  14116. const DWORD dwTimeout)
  14117. {
  14118. HRESULT hr;
  14119. FD_SET fdsRead;
  14120. timeval tv;
  14121. int iReturn;
  14122. #ifdef DBG
  14123. DWORD dwError;
  14124. #endif // DBG
  14125. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %u)", this, pUPnPDevice, dwTimeout);
  14126. DNASSERT(this->m_dwFlags & NATHELPUPNPOBJ_INITIALIZED);
  14127. DNASSERT(pUPnPDevice != NULL);
  14128. DNASSERT(pUPnPDevice->IsConnected());
  14129. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  14130. do
  14131. {
  14132. //
  14133. // Create an FD_SET for the socket in question.
  14134. //
  14135. FD_ZERO(&fdsRead);
  14136. FD_SET(pUPnPDevice->GetControlSocket(), &fdsRead);
  14137. //
  14138. // Wait for received data.
  14139. //
  14140. tv.tv_usec = dwTimeout * 1000;
  14141. tv.tv_sec = 0;
  14142. iReturn = this->m_pfnselect(0, &fdsRead, NULL, NULL, &tv);
  14143. if (iReturn == SOCKET_ERROR)
  14144. {
  14145. #ifdef DBG
  14146. dwError = this->m_pfnWSAGetLastError();
  14147. DPFX(DPFPREP, 0, "Got sockets error %u trying to select on UPnP device sockets!",
  14148. dwError);
  14149. #endif // DBG
  14150. hr = DPNHERR_GENERIC;
  14151. goto Failure;
  14152. }
  14153. //
  14154. // If no sockets were selected, we're done.
  14155. //
  14156. if (iReturn == 0)
  14157. {
  14158. DPFX(DPFPREP, 7, "Timed out waiting for data on UPnP device 0x%p's socket.",
  14159. pUPnPDevice);
  14160. break;
  14161. }
  14162. DNASSERT(iReturn == 1);
  14163. //DNASSERT(FD_ISSET(pUPnPDevice->GetControlSocket(), &fdsRead));
  14164. DNASSERT(this->m_pfn__WSAFDIsSet(pUPnPDevice->GetControlSocket(), &fdsRead));
  14165. hr = this->ReceiveUPnPDataStream(pUPnPDevice);
  14166. if (hr != DPNH_OK)
  14167. {
  14168. DPFX(DPFPREP, 0, "Couldn't receive UPnP stream from device object 0x%p! Disconnecting.",
  14169. pUPnPDevice);
  14170. //
  14171. // Dump this unusable UPnP device and continue.
  14172. //
  14173. if (pUPnPDevice->GetOwningDevice() != NULL)
  14174. {
  14175. this->ClearDevicesUPnPDevice(pUPnPDevice->GetOwningDevice());
  14176. }
  14177. else
  14178. {
  14179. DPFX(DPFPREP, 1, "UPnP device 0x%p's has already been removed from owning device.",
  14180. pUPnPDevice);
  14181. }
  14182. break;
  14183. }
  14184. //
  14185. // If the UPnP device is no longer connected, we're done.
  14186. //
  14187. if (! pUPnPDevice->IsConnected())
  14188. {
  14189. DPFX(DPFPREP, 7, "UPnP device 0x%p no longer connected.", pUPnPDevice);
  14190. break;
  14191. }
  14192. //
  14193. // We found data, so see if there's more. Connection should be closed
  14194. // after responses.
  14195. //
  14196. DPFX(DPFPREP, 7, "Waiting for more data on the UPnP device 0x%p's socket.", pUPnPDevice);
  14197. }
  14198. while (TRUE);
  14199. //
  14200. // If we're here, we're no worse for wear.
  14201. //
  14202. hr = DPNH_OK;
  14203. Exit:
  14204. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  14205. return hr;
  14206. Failure:
  14207. goto Exit;
  14208. } // CNATHelpUPnP::CheckForReceivedUPnPMsgsOnDevice
  14209. #undef DPF_MODNAME
  14210. #define DPF_MODNAME "CNATHelpUPnP::HandleUPnPDiscoveryResponseMsg"
  14211. //=============================================================================
  14212. // CNATHelpUPnP::HandleUPnPDiscoveryResponseMsg
  14213. //-----------------------------------------------------------------------------
  14214. //
  14215. // Description: Handles a UPnP discovery response message sent to this
  14216. // control point.
  14217. //
  14218. // The object lock is assumed to be held.
  14219. //
  14220. // Arguments:
  14221. // CDevice * pDevice - Pointer to device which received message.
  14222. // SOCKADDR_IN * psaddrinSource - Pointer to address that sent the response
  14223. // message.
  14224. // char * pcMsg - Pointer to buffer containing the UPnP
  14225. // message. It will be modified.
  14226. // int iMsgSize - Size of message buffer in bytes. There
  14227. // must be an extra byte after the end of
  14228. // the message.
  14229. // BOOL * pfInitiatedConnect - Pointer to boolean to set to TRUE if a
  14230. // new UPnP device was found and a
  14231. // connection to it was begun.
  14232. //
  14233. // Returns: HRESULT
  14234. // DPNH_OK - Message was handled successfully.
  14235. // DPNHERR_GENERIC - An error occurred.
  14236. //=============================================================================
  14237. HRESULT CNATHelpUPnP::HandleUPnPDiscoveryResponseMsg(CDevice * const pDevice,
  14238. const SOCKADDR_IN * const psaddrinSource,
  14239. char * const pcMsg,
  14240. const int iMsgSize,
  14241. BOOL * const pfInitiatedConnect)
  14242. {
  14243. HRESULT hr = DPNH_OK;
  14244. char * pszToken;
  14245. UPNP_HEADER_INFO HeaderInfo;
  14246. SOCKADDR_IN saddrinHost;
  14247. char * pszRelativePath;
  14248. SOCKET sTemp = INVALID_SOCKET;
  14249. SOCKADDR_IN saddrinLocal;
  14250. CUPnPDevice * pUPnPDevice = NULL;
  14251. DWORD dwError;
  14252. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, %i, 0x%p)",
  14253. this, pDevice, psaddrinSource, pcMsg, iMsgSize, pfInitiatedConnect);
  14254. #ifdef DBG
  14255. //
  14256. // Log the message.
  14257. //
  14258. this->PrintUPnPTransactionToFile(pcMsg,
  14259. iMsgSize,
  14260. "Inbound UPnP datagram headers",
  14261. pDevice);
  14262. #endif // DBG
  14263. //
  14264. // Any errors we get while analyzing the message will cause us to jump to
  14265. // the Exit label with hr == DPNH_OK. Once we start trying to connect to
  14266. // the UPnP device, that will change. See below.
  14267. //
  14268. //
  14269. // First of all, if this device already has a UPnP device, then we'll just
  14270. // ignore this response. Either it's a duplicate of an earlier response,
  14271. // a cache-refresh, or it's from a different device. Duplicates we should
  14272. // ignore. Cache-refresh is essentially a duplicate. We can't handle any
  14273. // information changes, so ignore those too. And finally, we don't handle
  14274. // multiple Internet gateway UPnP devices, so ignore those, too.
  14275. //
  14276. pUPnPDevice = pDevice->GetUPnPDevice();
  14277. if (pUPnPDevice != NULL)
  14278. {
  14279. DPFX(DPFPREP, 6, "Already have UPnP device (0x%p) ignoring message.",
  14280. pUPnPDevice);
  14281. //
  14282. // GetUPnPDevice did not add a reference to pUPnPDevice.
  14283. //
  14284. goto Exit;
  14285. }
  14286. //
  14287. // Make sure the sender of this response is valid. It should be either
  14288. // the local device address, or the address of the gateway. If we
  14289. // broadcasted or multicasted, we'll need to be more lenient. We'll just
  14290. // ensure that the response came from someone local (it doesn't make sense
  14291. // that in order to make mappings for our private network we would need to
  14292. // contact something outside).
  14293. //
  14294. if (psaddrinSource->sin_addr.S_un.S_addr != pDevice->GetLocalAddressV4())
  14295. {
  14296. if (g_fUseMulticastUPnPDiscovery)
  14297. {
  14298. if (! this->IsAddressLocal(pDevice, psaddrinSource))
  14299. {
  14300. DPFX(DPFPREP, 1, "Multicast search responding device (%u.%u.%u.%u:%u) is not local, ignoring message.",
  14301. psaddrinSource->sin_addr.S_un.S_un_b.s_b1,
  14302. psaddrinSource->sin_addr.S_un.S_un_b.s_b2,
  14303. psaddrinSource->sin_addr.S_un.S_un_b.s_b3,
  14304. psaddrinSource->sin_addr.S_un.S_un_b.s_b4,
  14305. NTOHS(psaddrinSource->sin_port));
  14306. goto Exit;
  14307. }
  14308. }
  14309. else
  14310. {
  14311. //
  14312. // Retrieve the gateway's address (using saddrinHost as a temporary
  14313. // variable. If that fails or returns the broadcast address, just
  14314. // make sure the address is local. Otherwise, expect an exact
  14315. // match.
  14316. //
  14317. if ((! this->GetAddressToReachGateway(pDevice, &saddrinHost.sin_addr)) ||
  14318. (saddrinHost.sin_addr.S_un.S_addr == INADDR_BROADCAST))
  14319. {
  14320. if (! this->IsAddressLocal(pDevice, psaddrinSource))
  14321. {
  14322. DPFX(DPFPREP, 1, "No gateway/broadcast search responding device (%u.%u.%u.%u:%u) is not local, ignoring message.",
  14323. psaddrinSource->sin_addr.S_un.S_un_b.s_b1,
  14324. psaddrinSource->sin_addr.S_un.S_un_b.s_b2,
  14325. psaddrinSource->sin_addr.S_un.S_un_b.s_b3,
  14326. psaddrinSource->sin_addr.S_un.S_un_b.s_b4,
  14327. NTOHS(psaddrinSource->sin_port));
  14328. goto Exit;
  14329. }
  14330. }
  14331. else
  14332. {
  14333. if (psaddrinSource->sin_addr.S_un.S_addr != saddrinHost.sin_addr.S_un.S_addr)
  14334. {
  14335. DPFX(DPFPREP, 1, "Unicast search responding device (%u.%u.%u.%u:%u) is not gateway (%u.%u.%u.%u), ignoring message.",
  14336. psaddrinSource->sin_addr.S_un.S_un_b.s_b1,
  14337. psaddrinSource->sin_addr.S_un.S_un_b.s_b2,
  14338. psaddrinSource->sin_addr.S_un.S_un_b.s_b3,
  14339. psaddrinSource->sin_addr.S_un.S_un_b.s_b4,
  14340. NTOHS(psaddrinSource->sin_port),
  14341. saddrinHost.sin_addr.S_un.S_un_b.s_b1,
  14342. saddrinHost.sin_addr.S_un.S_un_b.s_b2,
  14343. saddrinHost.sin_addr.S_un.S_un_b.s_b3,
  14344. saddrinHost.sin_addr.S_un.S_un_b.s_b4);
  14345. goto Exit;
  14346. }
  14347. }
  14348. } // end else (not multicasting search)
  14349. }
  14350. else
  14351. {
  14352. //
  14353. // Response was from the local device.
  14354. //
  14355. }
  14356. //
  14357. // Ensure the buffer is NULL terminated to prevent buffer overruns when
  14358. // using the string routines.
  14359. //
  14360. pcMsg[iMsgSize] = '\0';
  14361. //
  14362. // Find the version string.
  14363. //
  14364. pszToken = strtok(pcMsg, " \t\n");
  14365. if (pszToken == NULL)
  14366. {
  14367. DPFX(DPFPREP, 9, "Could not locate first white-space separator.");
  14368. goto Exit;
  14369. }
  14370. //
  14371. // Check the version string, case insensitive.
  14372. //
  14373. if ((_stricmp(pszToken, HTTP_VERSION) != 0) &&
  14374. (_stricmp(pszToken, HTTP_VERSION_ALT) != 0))
  14375. {
  14376. DPFX(DPFPREP, 1, "The version specified in the response message is not \"" HTTP_VERSION "\" or \"" HTTP_VERSION_ALT "\" (it's \"%hs\").",
  14377. pszToken);
  14378. goto Exit;
  14379. }
  14380. //
  14381. // Find the response code string.
  14382. //
  14383. pszToken = strtok(NULL, " ");
  14384. if (pszToken == NULL)
  14385. {
  14386. DPFX(DPFPREP, 1, "Could not find the response code space.");
  14387. goto Exit;
  14388. }
  14389. //
  14390. // Make sure it's the success result, case insensitive.
  14391. //
  14392. if (_stricmp(pszToken, "200") != 0)
  14393. {
  14394. DPFX(DPFPREP, 1, "The response code specified is not \"200\" (it's \"%hs\").",
  14395. pszToken);
  14396. goto Exit;
  14397. }
  14398. //
  14399. // Find the response code message.
  14400. //
  14401. pszToken = strtok(NULL, " \t\r");
  14402. if (pszToken == NULL)
  14403. {
  14404. DPFX(DPFPREP, 9, "Could not locate response code message white-space separator.");
  14405. goto Exit;
  14406. }
  14407. //
  14408. // Make sure it's the right string, case insensitive.
  14409. //
  14410. if (_stricmp(pszToken, "OK") != 0)
  14411. {
  14412. DPFX(DPFPREP, 1, "The response code message specified is not \"OK\" (it's \"%hs\").",
  14413. pszToken);
  14414. goto Exit;
  14415. }
  14416. //
  14417. // Parse the header information.
  14418. //
  14419. ZeroMemory(&HeaderInfo, sizeof(HeaderInfo));
  14420. this->ParseUPnPHeaders((pszToken + strlen(pszToken) + 1),
  14421. &HeaderInfo);
  14422. //
  14423. // Skip responses which don't include the required headers.
  14424. //
  14425. if ((HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CACHECONTROL] == NULL) ||
  14426. (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_EXT] == NULL) ||
  14427. (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_LOCATION] == NULL) ||
  14428. (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_SERVER] == NULL) ||
  14429. (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_ST] == NULL) ||
  14430. (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_USN] == NULL))
  14431. {
  14432. DPFX(DPFPREP, 1, "One of the expected headers was not specified, ignoring message.");
  14433. goto Exit;
  14434. }
  14435. //
  14436. // Make sure the service type is correct.
  14437. //
  14438. if ((_stricmp(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_ST], URI_SERVICE_WANIPCONNECTION_A) != 0) &&
  14439. (_stricmp(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_ST], URI_SERVICE_WANPPPCONNECTION_A) != 0))
  14440. {
  14441. DPFX(DPFPREP, 1, "Service type \"%hs\" is not desired \"" URI_SERVICE_WANIPCONNECTION_A "\" or \"" URI_SERVICE_WANPPPCONNECTION_A "\", ignoring message.",
  14442. HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_ST]);
  14443. goto Exit;
  14444. }
  14445. //
  14446. // Parse the location header into an address and port.
  14447. //
  14448. hr = this->GetAddressFromURL(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_LOCATION],
  14449. &saddrinHost,
  14450. &pszRelativePath);
  14451. if (hr != DPNH_OK)
  14452. {
  14453. DPFX(DPFPREP, 1, "Couldn't get address from URL (err = 0x%lx), ignoring message.",
  14454. hr);
  14455. hr = DPNH_OK;
  14456. goto Exit;
  14457. }
  14458. //
  14459. // Don't accept responses that refer to addresses other than the one that
  14460. // sent this response.
  14461. //
  14462. if (psaddrinSource->sin_addr.S_un.S_addr != saddrinHost.sin_addr.S_un.S_addr)
  14463. {
  14464. 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.",
  14465. saddrinHost.sin_addr.S_un.S_un_b.s_b1,
  14466. saddrinHost.sin_addr.S_un.S_un_b.s_b2,
  14467. saddrinHost.sin_addr.S_un.S_un_b.s_b3,
  14468. saddrinHost.sin_addr.S_un.S_un_b.s_b4,
  14469. NTOHS(saddrinHost.sin_port),
  14470. psaddrinSource->sin_addr.S_un.S_un_b.s_b1,
  14471. psaddrinSource->sin_addr.S_un.S_un_b.s_b2,
  14472. psaddrinSource->sin_addr.S_un.S_un_b.s_b3,
  14473. psaddrinSource->sin_addr.S_un.S_un_b.s_b4,
  14474. NTOHS(psaddrinSource->sin_port));
  14475. hr = DPNH_OK;
  14476. goto Exit;
  14477. }
  14478. //
  14479. // Don't accept responses that refer to ports in the reserved range (less
  14480. // than or equal to 1024), other than the standard HTTP port.
  14481. //
  14482. if ((NTOHS(saddrinHost.sin_port) <= MAX_RESERVED_PORT) &&
  14483. (saddrinHost.sin_port != HTONS(HTTP_PORT)))
  14484. {
  14485. DPFX(DPFPREP, 1, "Host address designated invalid port %u, ignoring message.",
  14486. NTOHS(saddrinHost.sin_port));
  14487. hr = DPNH_OK;
  14488. goto Exit;
  14489. }
  14490. //
  14491. // Any errors we get from here on out will cause us to jump to the Failure
  14492. // label, instead of going straight to Exit.
  14493. //
  14494. //
  14495. // Create a socket to connect to that address.
  14496. //
  14497. ZeroMemory(&saddrinLocal, sizeof(saddrinLocal));
  14498. saddrinLocal.sin_family = AF_INET;
  14499. saddrinLocal.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  14500. sTemp = this->CreateSocket(&saddrinLocal, SOCK_STREAM, 0);
  14501. if (sTemp == INVALID_SOCKET)
  14502. {
  14503. DPFX(DPFPREP, 0, "Couldn't create stream socket!");
  14504. hr = DPNHERR_GENERIC;
  14505. goto Failure;
  14506. }
  14507. //
  14508. // Initiate the connection to the UPnP device. It is expected that connect
  14509. // will return WSAEWOULDBLOCK.
  14510. //
  14511. if (this->m_pfnconnect(sTemp,
  14512. (SOCKADDR*) (&saddrinHost),
  14513. sizeof(saddrinHost)) != 0)
  14514. {
  14515. dwError = this->m_pfnWSAGetLastError();
  14516. if (dwError != WSAEWOULDBLOCK)
  14517. {
  14518. #ifdef DBG
  14519. DPFX(DPFPREP, 0, "Couldn't connect socket, error = %u!", dwError);
  14520. #endif // DBG
  14521. hr = DPNHERR_GENERIC;
  14522. goto Failure;
  14523. }
  14524. }
  14525. else
  14526. {
  14527. //
  14528. // connect() on non-blocking sockets is explicitly documented as
  14529. // always returning WSAEWOULDBLOCK, but CE seems to do it anyway.
  14530. //
  14531. DPFX(DPFPREP, 8, "Socket connected right away.");
  14532. }
  14533. //
  14534. // Create a new object to represent the UPnP device to which we're trying
  14535. // to connect.
  14536. //
  14537. pUPnPDevice = new CUPnPDevice(this->m_dwCurrentUPnPDeviceID++);
  14538. if (pUPnPDevice == NULL)
  14539. {
  14540. hr = DPNHERR_OUTOFMEMORY;
  14541. goto Failure;
  14542. }
  14543. hr = pUPnPDevice->SetLocationURL(pszRelativePath);
  14544. if (hr != DPNH_OK)
  14545. {
  14546. DPFX(DPFPREP, 0, "Couldn't set UPnP device's location URL!");
  14547. goto Failure;
  14548. }
  14549. pUPnPDevice->SetHostAddress(&saddrinHost);
  14550. hr = pUPnPDevice->SetUSN(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_USN]);
  14551. if (hr != DPNH_OK)
  14552. {
  14553. DPFX(DPFPREP, 0, "Couldn't set UPnP device's USN!");
  14554. goto Failure;
  14555. }
  14556. hr = pUPnPDevice->CreateReceiveBuffer(UPNP_STREAM_RECV_BUFFER_INITIAL_SIZE);
  14557. if (hr != DPNH_OK)
  14558. {
  14559. DPFX(DPFPREP, 0, "Couldn't create UPnP device's receive buffer!");
  14560. goto Failure;
  14561. }
  14562. DPFX(DPFPREP, 7, "Created new UPnP device object 0x%p ID %u.",
  14563. pUPnPDevice, pUPnPDevice->GetID());
  14564. //
  14565. // It's connecting...
  14566. //
  14567. pUPnPDevice->NoteConnecting();
  14568. //
  14569. // See if we need to avoid trying asymmetric port mappings.
  14570. //
  14571. if (g_fNoAsymmetricMappings)
  14572. {
  14573. DPFX(DPFPREP, 1, "Preventing asymmetric port mappings on new UPnP device 0x%p.",
  14574. pUPnPDevice);
  14575. pUPnPDevice->NoteDoesNotSupportAsymmetricMappings();
  14576. }
  14577. //
  14578. // Transfer ownership of the socket to the object.
  14579. //
  14580. pUPnPDevice->SetControlSocket(sTemp);
  14581. //
  14582. // Associate it with the device.
  14583. //
  14584. pUPnPDevice->MakeDeviceOwner(pDevice);
  14585. //
  14586. // Add it to the global list, and transfer ownership of the reference.
  14587. //
  14588. pUPnPDevice->m_blList.InsertBefore(&this->m_blUPnPDevices);
  14589. pUPnPDevice = NULL;
  14590. //
  14591. // Inform the caller that there's a new connection pending.
  14592. //
  14593. (*pfInitiatedConnect) = TRUE;
  14594. //
  14595. // Clear the device's discovery flags, now that we have a device, we're not
  14596. // going to be searching anymore.
  14597. //
  14598. pDevice->NoteNotPerformingRemoteUPnPDiscovery();
  14599. pDevice->NoteNotPerformingLocalUPnPDiscovery();
  14600. Exit:
  14601. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  14602. return hr;
  14603. Failure:
  14604. if (pUPnPDevice != NULL)
  14605. {
  14606. //pUPnPDevice->DestroyReceiveBuffer();
  14607. pUPnPDevice->ClearUSN();
  14608. pUPnPDevice->ClearLocationURL();
  14609. pUPnPDevice->DecRef();
  14610. }
  14611. if (sTemp != INVALID_SOCKET)
  14612. {
  14613. this->m_pfnclosesocket(sTemp);
  14614. sTemp = INVALID_SOCKET;
  14615. }
  14616. goto Exit;
  14617. } // CNATHelpUPnP::HandleUPnPDiscoveryResponseMsg
  14618. #undef DPF_MODNAME
  14619. #define DPF_MODNAME "CNATHelpUPnP::ReconnectUPnPControlSocket"
  14620. //=============================================================================
  14621. // CNATHelpUPnP::ReconnectUPnPControlSocket
  14622. //-----------------------------------------------------------------------------
  14623. //
  14624. // Description: Re-establishes a UPnP device TCP/IP connection.
  14625. //
  14626. // UPnP devices may get removed from list if a failure occurs.
  14627. //
  14628. // The object lock is assumed to be held.
  14629. //
  14630. // Arguments:
  14631. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device to reconnect.
  14632. //
  14633. // Returns: HRESULT
  14634. // DPNH_OK - Message was handled successfully.
  14635. // DPNHERR_GENERIC - An error occurred.
  14636. //=============================================================================
  14637. HRESULT CNATHelpUPnP::ReconnectUPnPControlSocket(CUPnPDevice * const pUPnPDevice)
  14638. {
  14639. HRESULT hr = DPNH_OK;
  14640. SOCKET sTemp = INVALID_SOCKET;
  14641. CDevice * pDevice;
  14642. SOCKADDR_IN saddrinLocal;
  14643. DWORD dwError;
  14644. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pUPnPDevice);
  14645. DNASSERT(pUPnPDevice->GetControlSocket() == INVALID_SOCKET);
  14646. //
  14647. // Create a socket to connect to that address.
  14648. //
  14649. pDevice = pUPnPDevice->GetOwningDevice();
  14650. DNASSERT(pDevice != NULL);
  14651. ZeroMemory(&saddrinLocal, sizeof(saddrinLocal));
  14652. saddrinLocal.sin_family = AF_INET;
  14653. saddrinLocal.sin_addr.S_un.S_addr = pDevice->GetLocalAddressV4();
  14654. sTemp = this->CreateSocket(&saddrinLocal, SOCK_STREAM, 0);
  14655. if (sTemp == INVALID_SOCKET)
  14656. {
  14657. DPFX(DPFPREP, 0, "Couldn't create stream socket!");
  14658. hr = DPNHERR_GENERIC;
  14659. goto Failure;
  14660. }
  14661. //
  14662. // Initiate the connection to the UPnP device. It is expected that connect
  14663. // will return WSAEWOULDBLOCK.
  14664. //
  14665. if (this->m_pfnconnect(sTemp,
  14666. (SOCKADDR*) (pUPnPDevice->GetControlAddress()),
  14667. sizeof(SOCKADDR_IN)) != 0)
  14668. {
  14669. dwError = this->m_pfnWSAGetLastError();
  14670. if (dwError != WSAEWOULDBLOCK)
  14671. {
  14672. #ifdef DBG
  14673. DPFX(DPFPREP, 0, "Couldn't connect socket, error = %u!", dwError);
  14674. #endif // DBG
  14675. hr = DPNHERR_GENERIC;
  14676. goto Failure;
  14677. }
  14678. }
  14679. else
  14680. {
  14681. //
  14682. // connect() on non-blocking sockets is explicitly documented as
  14683. // always returning WSAEWOULDBLOCK, but CE seems to do it anyway.
  14684. //
  14685. DPFX(DPFPREP, 8, "Socket connected right away.");
  14686. }
  14687. //
  14688. // It's reconnecting...
  14689. //
  14690. pUPnPDevice->NoteConnecting();
  14691. //
  14692. // Transfer ownership of the socket to the object.
  14693. //
  14694. pUPnPDevice->SetControlSocket(sTemp);
  14695. sTemp = INVALID_SOCKET;
  14696. //
  14697. // Wait for the connect to complete.
  14698. //
  14699. hr = this->WaitForUPnPConnectCompletions();
  14700. if (hr != DPNH_OK)
  14701. {
  14702. DPFX(DPFPREP, 0, "Couldn't wait for UPnP connect completions!");
  14703. goto Failure;
  14704. }
  14705. //
  14706. // Make sure the connect completed successfully.
  14707. //
  14708. if (! pUPnPDevice->IsConnected())
  14709. {
  14710. DPFX(DPFPREP, 0, "UPnP device 0x%p failed reconnecting!", pUPnPDevice);
  14711. //
  14712. // Note that the device is cleaned up and is not in any lists anymore.
  14713. //
  14714. hr = DPNHERR_SERVERNOTRESPONDING;
  14715. goto Exit;
  14716. }
  14717. Exit:
  14718. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  14719. return hr;
  14720. Failure:
  14721. if (sTemp != INVALID_SOCKET)
  14722. {
  14723. this->m_pfnclosesocket(sTemp);
  14724. sTemp = INVALID_SOCKET;
  14725. }
  14726. goto Exit;
  14727. } // CNATHelpUPnP::ReconnectUPnPControlSocket
  14728. #undef DPF_MODNAME
  14729. #define DPF_MODNAME "CNATHelpUPnP::ReceiveUPnPDataStream"
  14730. //=============================================================================
  14731. // CNATHelpUPnP::ReceiveUPnPDataStream
  14732. //-----------------------------------------------------------------------------
  14733. //
  14734. // Description: Receives incoming data from a UPnP TCP connection.
  14735. //
  14736. // The UPnP device may get removed from list if a failure
  14737. // occurs, the caller needs to have a reference.
  14738. //
  14739. // The object lock is assumed to be held.
  14740. //
  14741. // Arguments:
  14742. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device with data to receive.
  14743. //
  14744. // Returns: HRESULT
  14745. // DPNH_OK - Data was received successfully.
  14746. // DPNHERR_GENERIC - An error occurred.
  14747. //=============================================================================
  14748. HRESULT CNATHelpUPnP::ReceiveUPnPDataStream(CUPnPDevice * const pUPnPDevice)
  14749. {
  14750. HRESULT hr = DPNH_OK;
  14751. char * pszDeChunkedBuffer = NULL;
  14752. int iReturn;
  14753. DWORD dwError;
  14754. char * pszCurrent;
  14755. char * pszEndOfBuffer;
  14756. UPNP_HEADER_INFO HeaderInfo;
  14757. DWORD dwContentLength;
  14758. char * pszToken;
  14759. DWORD dwHTTPResponseCode;
  14760. int iHeaderLength;
  14761. DWORD dwBufferRemaining;
  14762. char * pszChunkData;
  14763. DWORD dwChunkSize;
  14764. char * pszDestination;
  14765. #ifdef DBG
  14766. char * pszPrintIfFailed = NULL;
  14767. #endif // DBG
  14768. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p)", this, pUPnPDevice);
  14769. do
  14770. {
  14771. //
  14772. // Make sure there's room in the buffer to actually get the data.
  14773. //
  14774. if (pUPnPDevice->GetRemainingReceiveBufferSize() == 0)
  14775. {
  14776. DPFX(DPFPREP, 7, "Increasing receive buffer size prior to receiving.");
  14777. hr = pUPnPDevice->IncreaseReceiveBufferSize();
  14778. if (hr != DPNH_OK)
  14779. {
  14780. DPFX(DPFPREP, 0, "Couldn't increase receive buffer size prior to receiving!");
  14781. goto Failure;
  14782. }
  14783. }
  14784. //
  14785. // Actually get the data that was indicated.
  14786. //
  14787. iReturn = this->m_pfnrecv(pUPnPDevice->GetControlSocket(),
  14788. pUPnPDevice->GetCurrentReceiveBufferPtr(),
  14789. pUPnPDevice->GetRemainingReceiveBufferSize(),
  14790. 0);
  14791. switch (iReturn)
  14792. {
  14793. case 0:
  14794. {
  14795. //
  14796. // Since the connection has been broken, shutdown the socket.
  14797. //
  14798. this->m_pfnshutdown(pUPnPDevice->GetControlSocket(), 0); // ignore error
  14799. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  14800. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  14801. //
  14802. // Mark the socket as not connected.
  14803. //
  14804. pUPnPDevice->NoteNotConnected();
  14805. //
  14806. // There may have been HTTP success/error information sent
  14807. // before the connection was closed.
  14808. //
  14809. if (pUPnPDevice->GetUsedReceiveBufferSize() == 0)
  14810. {
  14811. DPFX(DPFPREP, 3, "UPnP device 0x%p shut down connection (no more data).",
  14812. pUPnPDevice);
  14813. //
  14814. // Hopefully we got what we needed, but we're done now.
  14815. //
  14816. goto Exit;
  14817. }
  14818. DPFX(DPFPREP, 3, "UPnP device 0x%p gracefully closed connection after sending data.",
  14819. pUPnPDevice);
  14820. //
  14821. // Continue through and parse what data we have.
  14822. //
  14823. break;
  14824. }
  14825. case SOCKET_ERROR:
  14826. {
  14827. dwError = this->m_pfnWSAGetLastError();
  14828. switch (dwError)
  14829. {
  14830. case WSAEMSGSIZE:
  14831. {
  14832. //
  14833. // There's not enough room in the buffer. Double the
  14834. // buffer and try again.
  14835. //
  14836. DPFX(DPFPREP, 7, "Increasing receive buffer size after WSAEMSGSIZE error.");
  14837. hr = pUPnPDevice->IncreaseReceiveBufferSize();
  14838. if (hr != DPNH_OK)
  14839. {
  14840. DPFX(DPFPREP, 0, "Couldn't increase receive buffer size!");
  14841. goto Failure;
  14842. }
  14843. break;
  14844. }
  14845. case WSAECONNABORTED:
  14846. case WSAECONNRESET:
  14847. {
  14848. DPFX(DPFPREP, 1, "UPnP device shutdown connection (err = %u).", dwError);
  14849. //
  14850. // Our caller should remove this device.
  14851. //
  14852. hr = DPNHERR_GENERIC;
  14853. goto Failure;
  14854. break;
  14855. }
  14856. case WSAENOBUFS:
  14857. {
  14858. DPFX(DPFPREP, 0, "WinSock returned WSAENOBUFS while receiving!");
  14859. //
  14860. // Our caller should remove this device.
  14861. //
  14862. hr = DPNHERR_OUTOFMEMORY;
  14863. goto Failure;
  14864. break;
  14865. }
  14866. default:
  14867. {
  14868. DPFX(DPFPREP, 0, "Got unknown sockets error %u while receiving data!", dwError);
  14869. hr = DPNHERR_GENERIC;
  14870. goto Failure;
  14871. break;
  14872. }
  14873. }
  14874. break;
  14875. }
  14876. default:
  14877. {
  14878. DPFX(DPFPREP, 2, "Received %i bytes of data from UPnP device 0%p.",
  14879. iReturn, pUPnPDevice);
  14880. pUPnPDevice->UpdateUsedReceiveBufferSize(iReturn);
  14881. //
  14882. // We'll also break out of the do-while loop below.
  14883. //
  14884. break;
  14885. }
  14886. }
  14887. }
  14888. while (iReturn == SOCKET_ERROR);
  14889. //
  14890. // If we're here, we've gotten the data that has currently arrived.
  14891. //
  14892. //
  14893. // If we have all the headers, specifically the content length header, we
  14894. // can tell whether we have the whole message or not. If not, we can't do
  14895. // anything until the rest of the data comes in.
  14896. //
  14897. if (pUPnPDevice->IsWaitingForContent())
  14898. {
  14899. dwContentLength = pUPnPDevice->GetExpectedContentSize();
  14900. if (dwContentLength == -1)
  14901. {
  14902. //
  14903. // We have all the headers, but a faulty server implementation did
  14904. // not send a content-length header. We're going to wait until the
  14905. // socket is closed by the other side, and then consider all of the
  14906. // data received at that time to be the content. NOTE: It is
  14907. // expected that there will be a higher level timeout preventing us
  14908. // from waiting forever.
  14909. //
  14910. // So if the device is still connected, keep waiting.
  14911. //
  14912. // If we're using chunked transfer we won't get a content-length
  14913. // header or a total size legitimately. We will know the sizes of
  14914. // individual chunks, but that doesn't help us much. We basically
  14915. // need to scan for the "last chunk" indicator (or socket
  14916. // shutdown).
  14917. //
  14918. if (pUPnPDevice->IsConnected())
  14919. {
  14920. //
  14921. // If we're using chunked transfer, see if we have enough
  14922. // information already to determine if we're done.
  14923. //
  14924. if (pUPnPDevice->IsUsingChunkedTransferEncoding())
  14925. {
  14926. //
  14927. // Walk all of the chunks we have so far to see if we have
  14928. // the last one (the zero terminator).
  14929. //
  14930. pszCurrent = pUPnPDevice->GetReceiveBufferStart();
  14931. dwBufferRemaining = pUPnPDevice->GetUsedReceiveBufferSize();
  14932. do
  14933. {
  14934. if (! this->GetNextChunk(pszCurrent,
  14935. dwBufferRemaining,
  14936. &pszChunkData,
  14937. &dwChunkSize,
  14938. &pszCurrent,
  14939. &dwBufferRemaining))
  14940. {
  14941. DPFX(DPFPREP, 1, "Body contains invalid chunk (at offset %u)! Disconnecting.",
  14942. (DWORD_PTR) (pszCurrent - pUPnPDevice->GetReceiveBufferStart()));
  14943. goto Failure;
  14944. }
  14945. if (pszChunkData == NULL)
  14946. {
  14947. DPFX(DPFPREP, 1, "Did not receive end of chunked data (%u bytes received so far), continuing to waiting for data.",
  14948. pUPnPDevice->GetUsedReceiveBufferSize());
  14949. goto Exit;
  14950. }
  14951. }
  14952. while (dwChunkSize != 0);
  14953. }
  14954. else
  14955. {
  14956. DPFX(DPFPREP, 1, "Waiting for connection to be shutdown (%u bytes received).",
  14957. pUPnPDevice->GetUsedReceiveBufferSize());
  14958. goto Exit;
  14959. }
  14960. }
  14961. DPFX(DPFPREP, 1, "Socket closed with %u bytes received, parsing.",
  14962. pUPnPDevice->GetUsedReceiveBufferSize());
  14963. }
  14964. else
  14965. {
  14966. if (dwContentLength > pUPnPDevice->GetUsedReceiveBufferSize())
  14967. {
  14968. //
  14969. // We still haven't received all the data yet. Keep waiting
  14970. // (unless the socket is closed).
  14971. //
  14972. if (pUPnPDevice->IsConnected())
  14973. {
  14974. DPFX(DPFPREP, 1, "Still waiting for all content (%u bytes of %u total received).",
  14975. pUPnPDevice->GetUsedReceiveBufferSize(), dwContentLength);
  14976. goto Exit;
  14977. }
  14978. DPFX(DPFPREP, 1, "Socket closed before all content received (%u bytes of %u total), parsing anyway.",
  14979. pUPnPDevice->GetUsedReceiveBufferSize(), dwContentLength);
  14980. //
  14981. // Try parsing it anyway.
  14982. //
  14983. }
  14984. }
  14985. //
  14986. // Retrieve the HTTP response code stored earlier.
  14987. //
  14988. dwHTTPResponseCode = pUPnPDevice->GetHTTPResponseCode();
  14989. //
  14990. // All of the content that's going to arrive, has.
  14991. //
  14992. pUPnPDevice->NoteNotWaitingForContent();
  14993. //
  14994. // Make sure the buffer is NULL terminated. But first ensure the
  14995. // buffer can hold a new NULL termination character.
  14996. //
  14997. if (pUPnPDevice->GetRemainingReceiveBufferSize() == 0)
  14998. {
  14999. DPFX(DPFPREP, 7, "Increasing receive buffer size to hold NULL termination (for content).");
  15000. hr = pUPnPDevice->IncreaseReceiveBufferSize();
  15001. if (hr != DPNH_OK)
  15002. {
  15003. DPFX(DPFPREP, 0, "Couldn't increase receive buffer size to accommodate NULL termination (for content)!");
  15004. goto Failure;
  15005. }
  15006. }
  15007. //
  15008. // Move to the end of the buffer and NULL terminate it for string ops.
  15009. //
  15010. pszEndOfBuffer = pUPnPDevice->GetReceiveBufferStart()
  15011. + pUPnPDevice->GetUsedReceiveBufferSize();
  15012. (*pszEndOfBuffer) = '\0';
  15013. #ifdef DBG
  15014. //
  15015. // Print from the start of the buffer if we fail.
  15016. //
  15017. pszPrintIfFailed = pUPnPDevice->GetReceiveBufferStart();
  15018. #endif // DBG
  15019. //
  15020. // We now have all the data in a string buffer. Continue...
  15021. //
  15022. }
  15023. else
  15024. {
  15025. //
  15026. // We haven't already received the headers. The data we just got
  15027. // should be those headers.
  15028. //
  15029. pszCurrent = pUPnPDevice->GetReceiveBufferStart();
  15030. //
  15031. // Quick check to make sure the buffer starts with something reasonable
  15032. // in hopes of catching completely bogus responses earlier. Note that
  15033. // the buffer is not necessarily NULL terminated or completely
  15034. // available yet.
  15035. //
  15036. if ((pUPnPDevice->GetUsedReceiveBufferSize() >= strlen(HTTP_PREFIX)) &&
  15037. (_strnicmp(pszCurrent, HTTP_PREFIX, strlen(HTTP_PREFIX)) != 0))
  15038. {
  15039. DPFX(DPFPREP, 1, "Headers do not begin with \"" HTTP_PREFIX "\"! Disconnecting.");
  15040. goto Failure;
  15041. }
  15042. //
  15043. // We don't want to walk off the end of the buffer, so only search up
  15044. // to the last possible location for the sequence, which is the end of
  15045. // the buffer minus the double EOL sequence.
  15046. //
  15047. pszEndOfBuffer = pszCurrent
  15048. + pUPnPDevice->GetUsedReceiveBufferSize()
  15049. - strlen(EOL EOL);
  15050. while (pszCurrent < pszEndOfBuffer)
  15051. {
  15052. if (_strnicmp(pszCurrent, EOL EOL, strlen(EOL EOL)) == 0)
  15053. {
  15054. //
  15055. // Found end of headers.
  15056. //
  15057. //
  15058. // Possible loss of data on 64-bit is okay, we're just saving
  15059. // this for logging purposes.
  15060. //
  15061. iHeaderLength = (int) ((INT_PTR) (pszCurrent - pUPnPDevice->GetReceiveBufferStart()));
  15062. break;
  15063. }
  15064. pszCurrent++;
  15065. }
  15066. //
  15067. // If we didn't find the end of the headers, we're done (for now).
  15068. //
  15069. if (pszCurrent >= pszEndOfBuffer)
  15070. {
  15071. //
  15072. // We still haven't received all the data yet. Keep waiting
  15073. // (unless the socket is closed).
  15074. //
  15075. if (pUPnPDevice->IsConnected())
  15076. {
  15077. //
  15078. // Make sure the length is still within reason.
  15079. //
  15080. if (pUPnPDevice->GetUsedReceiveBufferSize() > MAX_UPNP_HEADER_LENGTH)
  15081. {
  15082. DPFX(DPFPREP, 1, "Headers are too large (%u > %u)! Disconnecting.",
  15083. pUPnPDevice->GetUsedReceiveBufferSize(), MAX_UPNP_HEADER_LENGTH);
  15084. goto Failure;
  15085. }
  15086. DPFX(DPFPREP, 1, "Have not detected end of headers yet (%u bytes received).",
  15087. pUPnPDevice->GetUsedReceiveBufferSize());
  15088. goto Exit;
  15089. }
  15090. DPFX(DPFPREP, 1, "Socket closed before end of headers detected (%u bytes received), parsing anyway.",
  15091. pUPnPDevice->GetUsedReceiveBufferSize());
  15092. //
  15093. // Consider the whole buffer the headers length.
  15094. //
  15095. iHeaderLength = pUPnPDevice->GetUsedReceiveBufferSize();
  15096. //
  15097. // Try parsing it anyway.
  15098. //
  15099. }
  15100. #ifdef DBG
  15101. //
  15102. // Log the headers.
  15103. //
  15104. this->PrintUPnPTransactionToFile(pUPnPDevice->GetReceiveBufferStart(),
  15105. iHeaderLength,
  15106. "Inbound UPnP stream headers",
  15107. pUPnPDevice->GetOwningDevice());
  15108. #endif // DBG
  15109. //
  15110. // Make sure the buffer is NULL terminated. But first ensure the
  15111. // buffer can hold a new NULL termination character.
  15112. //
  15113. if (pUPnPDevice->GetRemainingReceiveBufferSize() == 0)
  15114. {
  15115. DPFX(DPFPREP, 7, "Increasing receive buffer size to hold NULL termination (for headers).");
  15116. hr = pUPnPDevice->IncreaseReceiveBufferSize();
  15117. if (hr != DPNH_OK)
  15118. {
  15119. DPFX(DPFPREP, 0, "Couldn't increase receive buffer size to accommodate NULL termination (for headers)!");
  15120. goto Failure;
  15121. }
  15122. //
  15123. // Find the end of the new buffer, and make sure it's NULL
  15124. // terminated for string ops.
  15125. //
  15126. pszEndOfBuffer = pUPnPDevice->GetReceiveBufferStart()
  15127. + pUPnPDevice->GetUsedReceiveBufferSize();
  15128. }
  15129. else
  15130. {
  15131. //
  15132. // Move to the end of the buffer and NULL terminate it for string
  15133. // ops.
  15134. //
  15135. pszEndOfBuffer += strlen(EOL EOL);
  15136. }
  15137. (*pszEndOfBuffer) = '\0';
  15138. //
  15139. // Make sure the buffer is a valid response. Find the version string.
  15140. //
  15141. pszToken = strtok(pUPnPDevice->GetReceiveBufferStart(), " \t\n");
  15142. if (pszToken == NULL)
  15143. {
  15144. DPFX(DPFPREP, 1, "Could not locate first white-space separator! Disconnecting.");
  15145. goto Failure;
  15146. }
  15147. //
  15148. // Check the version string, case insensitive.
  15149. //
  15150. if ((_stricmp(pszToken, HTTP_VERSION) != 0) &&
  15151. (_stricmp(pszToken, HTTP_VERSION_ALT) != 0))
  15152. {
  15153. DPFX(DPFPREP, 1, "The version specified in the response message is not \"" HTTP_VERSION "\" or \"" HTTP_VERSION_ALT "\" (it's \"%hs\")! Disconnecting.",
  15154. pszToken);
  15155. goto Failure;
  15156. }
  15157. //
  15158. // Find the response code number string.
  15159. //
  15160. pszToken = strtok(NULL, " ");
  15161. if (pszToken == NULL)
  15162. {
  15163. DPFX(DPFPREP, 1, "Could not find the response code number space! Disconnecting.");
  15164. goto Failure;
  15165. }
  15166. //
  15167. // Retrieve the success/failure code value.
  15168. //
  15169. dwHTTPResponseCode = atoi(pszToken);
  15170. //
  15171. // Find the response code message.
  15172. //
  15173. pszToken = strtok(NULL, "\t\r");
  15174. if (pszToken == NULL)
  15175. {
  15176. DPFX(DPFPREP, 1, "Could not locate response code message white-space separator! Disconnecting.");
  15177. goto Failure;
  15178. }
  15179. DPFX(DPFPREP, 1, "Received HTTP response %u \"%hs\".",
  15180. dwHTTPResponseCode, pszToken);
  15181. //
  15182. // Try parsing the headers (after the response status line).
  15183. //
  15184. ZeroMemory(&HeaderInfo, sizeof(HeaderInfo));
  15185. this->ParseUPnPHeaders((pszToken + strlen(pszToken) + 1),
  15186. &HeaderInfo);
  15187. #ifdef DBG
  15188. //
  15189. // Print from the start of the message body if we fail.
  15190. //
  15191. pszPrintIfFailed = HeaderInfo.pszMsgBody;
  15192. #endif // DBG
  15193. if ((HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_TRANSFERENCODING] != NULL) &&
  15194. (_strnicmp(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_TRANSFERENCODING], "chunked", strlen("chunked")) == 0))
  15195. {
  15196. pUPnPDevice->NoteUsingChunkedTransferEncoding();
  15197. }
  15198. //
  15199. // We're pretty lenient about missing headers...
  15200. //
  15201. if (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTLENGTH] == NULL)
  15202. {
  15203. DPFX(DPFPREP, 1, "Content-length header was not specified in response (chunked = %i).",
  15204. pUPnPDevice->IsUsingChunkedTransferEncoding());
  15205. //
  15206. // May be because we're using chunked transfer encoding, or it
  15207. // could be a bad device. Either way, we'll continue...
  15208. //
  15209. dwContentLength = -1;
  15210. }
  15211. else
  15212. {
  15213. dwContentLength = atoi(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTLENGTH]);
  15214. #ifdef DBG
  15215. if (dwContentLength == 0)
  15216. {
  15217. DPFX(DPFPREP, 1, "Content length (\"%hs\") is zero.",
  15218. HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTLENGTH]);
  15219. }
  15220. #endif // DBG
  15221. if (HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTTYPE] == NULL)
  15222. {
  15223. DPFX(DPFPREP, 1, "Expected content-type header was not specified in response, continuing.");
  15224. }
  15225. else
  15226. {
  15227. if (_strnicmp(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTTYPE], "text/xml", strlen("text/xml")) != 0)
  15228. {
  15229. DPFX(DPFPREP, 1, "Content type does not start with \"text/xml\" (it's \"%hs\")! Disconnecting.",
  15230. HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTTYPE]);
  15231. goto Failure;
  15232. }
  15233. #ifdef DBG
  15234. //
  15235. // Note whether the content type is qualified with
  15236. // "charset=utf-8" or not.
  15237. //
  15238. if (_stricmp(HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTTYPE], "text/xml; charset=\"utf-8\"") != 0)
  15239. {
  15240. DPFX(DPFPREP, 1, "Content type is xml, but it's not \"text/xml; charset=\"utf-8\"\" (it's \"%hs\"), continuing.",
  15241. HeaderInfo.apszHeaderStrings[RESPONSEHEADERINDEX_CONTENTTYPE]);
  15242. //
  15243. // The check was just for information purposes, continue.
  15244. //
  15245. }
  15246. #endif // DBG
  15247. }
  15248. }
  15249. //
  15250. // Content length may be valid, or the special value -1 at this
  15251. // point.
  15252. //
  15253. DPFX(DPFPREP, 7, "Moving past 0x%p bytes of header.",
  15254. HeaderInfo.pszMsgBody - pUPnPDevice->GetReceiveBufferStart());
  15255. //
  15256. // Forget about all the headers, we only care about data now.
  15257. //
  15258. pUPnPDevice->UpdateReceiveBufferStart(HeaderInfo.pszMsgBody);
  15259. //
  15260. // The buffer has been destroyed up to the end of the headers (meaning
  15261. // that calling ParseUPnPHeaders won't work on the same buffer again),
  15262. // so if we don't have all the content yet, we need to save the
  15263. // response code, remember the fact that we're not done yet, and
  15264. // continue waiting for the rest of the data.
  15265. // Of course, if there wasn't a content-length header, we have to wait
  15266. // for the socket to shutdown before we can parse.
  15267. //
  15268. // Also, if we're using chunked transfer we won't get a content-length
  15269. // header or a total size legitimately. We will know the sizes of
  15270. // individual chunks, but that doesn't help us much. We basically need
  15271. // to scan for the "last chunk" indicator (or socket shutdown).
  15272. //
  15273. if (dwContentLength == -1)
  15274. {
  15275. if (pUPnPDevice->IsConnected())
  15276. {
  15277. //
  15278. // If we're using chunked transfer, see if we have enough
  15279. // information already to determine if we're done.
  15280. //
  15281. if (pUPnPDevice->IsUsingChunkedTransferEncoding())
  15282. {
  15283. //
  15284. // Walk all of the chunks we have so far to see if we have
  15285. // the last one (the zero terminator).
  15286. //
  15287. pszCurrent = pUPnPDevice->GetReceiveBufferStart();
  15288. dwBufferRemaining = pUPnPDevice->GetUsedReceiveBufferSize();
  15289. do
  15290. {
  15291. if (! this->GetNextChunk(pszCurrent,
  15292. dwBufferRemaining,
  15293. &pszChunkData,
  15294. &dwChunkSize,
  15295. &pszCurrent,
  15296. &dwBufferRemaining))
  15297. {
  15298. DPFX(DPFPREP, 1, "Body contains invalid chunk (at offset %u)! Disconnecting.",
  15299. (DWORD_PTR) (pszCurrent - pUPnPDevice->GetReceiveBufferStart()));
  15300. goto Failure;
  15301. }
  15302. if (pszChunkData == NULL)
  15303. {
  15304. DPFX(DPFPREP, 1, "Did not receive end of chunked data (%u bytes received so far), continuing to waiting for data.",
  15305. pUPnPDevice->GetUsedReceiveBufferSize());
  15306. pUPnPDevice->NoteWaitingForContent(dwContentLength, dwHTTPResponseCode);
  15307. goto Exit;
  15308. }
  15309. }
  15310. while (dwChunkSize != 0);
  15311. }
  15312. else
  15313. {
  15314. DPFX(DPFPREP, 1, "Unknown content length (%u bytes received so far), waiting for connection to close before parsing.",
  15315. pUPnPDevice->GetUsedReceiveBufferSize());
  15316. pUPnPDevice->NoteWaitingForContent(dwContentLength, dwHTTPResponseCode);
  15317. goto Exit;
  15318. }
  15319. }
  15320. }
  15321. else
  15322. {
  15323. if ((pUPnPDevice->IsConnected()) &&
  15324. (dwContentLength > pUPnPDevice->GetUsedReceiveBufferSize()))
  15325. {
  15326. DPFX(DPFPREP, 1, "Not all content has been received (%u bytes of %u total), waiting for remainder of message.",
  15327. pUPnPDevice->GetUsedReceiveBufferSize(), dwContentLength);
  15328. pUPnPDevice->NoteWaitingForContent(dwContentLength, dwHTTPResponseCode);
  15329. goto Exit;
  15330. }
  15331. }
  15332. //
  15333. // We have all the data already (and it's in string form).
  15334. // Continue...
  15335. //
  15336. }
  15337. //
  15338. // If we got here, it means we have all the data that we're expecting.
  15339. // Shutdown the socket if it hasn't been already.
  15340. //
  15341. if (pUPnPDevice->IsConnected())
  15342. {
  15343. DPFX(DPFPREP, 7, "Forcing UPnP device 0x%p socket disconnection.",
  15344. pUPnPDevice);
  15345. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  15346. this->m_pfnshutdown(pUPnPDevice->GetControlSocket(), 0); // ignore error
  15347. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  15348. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  15349. pUPnPDevice->NoteNotConnected();
  15350. }
  15351. else
  15352. {
  15353. DNASSERT(pUPnPDevice->GetControlSocket() == INVALID_SOCKET);
  15354. }
  15355. //
  15356. // If the sender used chunked-transfer encoding, copy each of the chunks
  15357. // into a contiguous "dechunked" buffer.
  15358. //
  15359. if (pUPnPDevice->IsUsingChunkedTransferEncoding())
  15360. {
  15361. //
  15362. // Prepare a dechunked buffer.
  15363. //
  15364. pszDeChunkedBuffer = (char*) DNMalloc(pUPnPDevice->GetUsedReceiveBufferSize());
  15365. if (pszDeChunkedBuffer == NULL)
  15366. {
  15367. hr = DPNHERR_OUTOFMEMORY;
  15368. goto Failure;
  15369. }
  15370. pszDestination = pszDeChunkedBuffer;
  15371. //
  15372. // Walk all of the chunks.
  15373. //
  15374. pszCurrent = pUPnPDevice->GetReceiveBufferStart();
  15375. dwBufferRemaining = pUPnPDevice->GetUsedReceiveBufferSize();
  15376. do
  15377. {
  15378. if (! this->GetNextChunk(pszCurrent,
  15379. dwBufferRemaining,
  15380. &pszChunkData,
  15381. &dwChunkSize,
  15382. &pszCurrent,
  15383. &dwBufferRemaining))
  15384. {
  15385. DPFX(DPFPREP, 1, "Body contains invalid chunk (at offset %u)!",
  15386. (DWORD_PTR) (pszCurrent - pUPnPDevice->GetReceiveBufferStart()));
  15387. goto Failure;
  15388. }
  15389. //
  15390. // If this chunk is unfinished, bail.
  15391. //
  15392. if (pszChunkData == NULL)
  15393. {
  15394. DPFX(DPFPREP, 1, "Did not receive complete chunked data!",
  15395. pUPnPDevice->GetUsedReceiveBufferSize());
  15396. goto Failure;
  15397. }
  15398. //
  15399. // If this is the last chunk, terminate the string here and stop.
  15400. //
  15401. if (dwChunkSize == 0)
  15402. {
  15403. (*pszDestination) = '\0';
  15404. break;
  15405. }
  15406. //
  15407. // Otherwise copy the chunk data to the dechunked buffer.
  15408. //
  15409. memcpy(pszDestination, pszChunkData, dwChunkSize);
  15410. pszDestination += dwChunkSize;
  15411. }
  15412. while (TRUE);
  15413. //
  15414. // Turn off the flag since it's no longer relevant.
  15415. //
  15416. pUPnPDevice->NoteNotUsingChunkedTransferEncoding();
  15417. //
  15418. // Parse the dechunked version of the message.
  15419. //
  15420. pszCurrent = pszDeChunkedBuffer;
  15421. }
  15422. else
  15423. {
  15424. //
  15425. // Get a pointer to the start of the message body.
  15426. //
  15427. pszCurrent = pUPnPDevice->GetReceiveBufferStart();
  15428. }
  15429. //
  15430. // Clear the buffer for the next message. Note that this does not
  15431. // invalidate the pszMessageBody pointer we just retrieved because
  15432. // ClearReceiveBuffer just resets the pointers back to the beginning (it
  15433. // does not zero out the buffer). We need to reset the buffer because the
  15434. // handler we're about to call may try to receive data as well. The buffer
  15435. // must be "empty" (reset) at that time. Of course, if the handler does do
  15436. // that, then it had better have saved off copies of any strings it needs
  15437. // because they will get overwritten once receiving starts.
  15438. //
  15439. // Content length of -1 means we never detected a valid CONTENT-LENGTH
  15440. // header, so we will assume the rest of the data that has been received up
  15441. // to now is (all of) the content.
  15442. //
  15443. #ifdef DBG
  15444. if ((dwContentLength != -1) &&
  15445. (dwContentLength < pUPnPDevice->GetUsedReceiveBufferSize()))
  15446. {
  15447. //
  15448. // The string was terminated before this data, so the handler will
  15449. // never even see it.
  15450. //
  15451. DPFX(DPFPREP, 1, "Ignoring %u bytes of extra data after response from UPnP device 0x%p.",
  15452. (pUPnPDevice->GetUsedReceiveBufferSize() - dwContentLength),
  15453. pUPnPDevice);
  15454. }
  15455. //
  15456. // HandleUPnPControlResponseBody or HandleUPnPDescriptionResponseBody might
  15457. // print out the body or overwrite the data, so we can't print it out if
  15458. // they fail.
  15459. //
  15460. pszPrintIfFailed = NULL;
  15461. #endif // DBG
  15462. pUPnPDevice->ClearReceiveBuffer();
  15463. if (pUPnPDevice->IsWaitingForControlResponse())
  15464. {
  15465. //
  15466. // It looks like it's a control response, because someone is waiting
  15467. // for one.
  15468. //
  15469. hr = this->HandleUPnPControlResponseBody(pUPnPDevice,
  15470. dwHTTPResponseCode,
  15471. pszCurrent);
  15472. if (hr != DPNH_OK)
  15473. {
  15474. DPFX(DPFPREP, 0, "Couldn't handle control response!", hr);
  15475. goto Failure;
  15476. }
  15477. }
  15478. else
  15479. {
  15480. //
  15481. // Not waiting for a control response, assume it's a description
  15482. // response.
  15483. //
  15484. hr = this->HandleUPnPDescriptionResponseBody(pUPnPDevice,
  15485. dwHTTPResponseCode,
  15486. pszCurrent);
  15487. if (hr != DPNH_OK)
  15488. {
  15489. //
  15490. // UPnP device may have been removed from list.
  15491. //
  15492. DPFX(DPFPREP, 0, "Couldn't handle description response!", hr);
  15493. goto Failure;
  15494. }
  15495. }
  15496. Exit:
  15497. if (pszDeChunkedBuffer != NULL)
  15498. {
  15499. DNFree(pszDeChunkedBuffer);
  15500. pszDeChunkedBuffer = NULL;
  15501. }
  15502. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  15503. return hr;
  15504. Failure:
  15505. //
  15506. // Something went wrong, break the connection if it exists.
  15507. //
  15508. if (pUPnPDevice->IsConnected())
  15509. {
  15510. this->m_pfnshutdown(pUPnPDevice->GetControlSocket(), 0); // ignore error
  15511. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  15512. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  15513. //
  15514. // Mark the socket as not connected.
  15515. //
  15516. pUPnPDevice->NoteNotConnected();
  15517. }
  15518. #ifdef DBG
  15519. if (pszPrintIfFailed != NULL)
  15520. {
  15521. this->PrintUPnPTransactionToFile(pszPrintIfFailed,
  15522. strlen(pszPrintIfFailed),
  15523. "Inbound ignored data",
  15524. pUPnPDevice->GetOwningDevice());
  15525. }
  15526. #endif // DBG
  15527. //
  15528. // Forget all data received.
  15529. //
  15530. pUPnPDevice->ClearReceiveBuffer();
  15531. goto Exit;
  15532. } // CNATHelpUPnP::ReceiveUPnPDataStream
  15533. #undef DPF_MODNAME
  15534. #define DPF_MODNAME "CNATHelpUPnP::ParseUPnPHeaders"
  15535. //=============================================================================
  15536. // CNATHelpUPnP::ParseUPnPHeaders
  15537. //-----------------------------------------------------------------------------
  15538. //
  15539. // Description: Parses UPnP header information out of a message buffer.
  15540. //
  15541. // Arguments:
  15542. // char * pszMsg - Pointer to string containing the UPnP
  15543. // message. It will be modified.
  15544. // UPNP_HEADER_INFO * pHeaderInfo - Structure used to return parsing results.
  15545. //
  15546. // Returns: None.
  15547. //=============================================================================
  15548. void CNATHelpUPnP::ParseUPnPHeaders(char * const pszMsg,
  15549. UPNP_HEADER_INFO * pHeaderInfo)
  15550. {
  15551. char * pszCurrent;
  15552. char * pszLineStart;
  15553. char * pszHeaderDelimiter;
  15554. int i;
  15555. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  15556. this, pszMsg, pHeaderInfo);
  15557. //
  15558. // Loop until we reach the last SSDP header (indicated by a blank line).
  15559. //
  15560. pszCurrent = pszMsg;
  15561. pszLineStart = pszMsg;
  15562. do
  15563. {
  15564. //
  15565. // Find the end of the current line (CR LF).
  15566. //
  15567. while ((*pszCurrent) != '\n')
  15568. {
  15569. if ((*pszCurrent) == '\0')
  15570. {
  15571. //
  15572. // We hit the end of the buffer. Bail.
  15573. //
  15574. DPFX(DPFPREP, 1, "Hit end of buffer, parsing terminated.");
  15575. return;
  15576. }
  15577. pszCurrent++;
  15578. }
  15579. //
  15580. // Assuming this is the last header line, update the message body
  15581. // pointer to be after this line.
  15582. //
  15583. pHeaderInfo->pszMsgBody = pszCurrent + 1;
  15584. //
  15585. // If it's a valid line, then a CR will precede the LF we just found.
  15586. // If so, truncate the string there. If not, we'll just continue. The
  15587. // wacky newline in the middle of nowhere will probably just be
  15588. // ignored.
  15589. //
  15590. if ((pszCurrent > (pszMsg + 1)) &&
  15591. (*(pszCurrent - 1)) == '\r')
  15592. {
  15593. //
  15594. // Truncate the string at the effective end of the line (i.e.
  15595. // replace the CR with NULL terminator).
  15596. //
  15597. *(pszCurrent - 1) = '\0';
  15598. //
  15599. // If this is the empty line denoting the end of the headers, we're
  15600. // done here.
  15601. //
  15602. if (strlen(pszLineStart) == 0)
  15603. {
  15604. //
  15605. // Stop looping.
  15606. //
  15607. break;
  15608. }
  15609. }
  15610. else
  15611. {
  15612. //
  15613. // Truncate the string here.
  15614. //
  15615. (*pszCurrent) = '\0';
  15616. DPFX(DPFPREP, 9, "Line has a newline in it (offset 0x%p) that isn't preceded by a carriage return.",
  15617. (pszCurrent - pszMsg));
  15618. }
  15619. //
  15620. // Whitespace means continuation of previous line, so if this line
  15621. // starts that way, erase the termination for the previous line (unless
  15622. // this is the first line).
  15623. //
  15624. if (((*pszLineStart) == ' ') || ((*pszLineStart) == '\t'))
  15625. {
  15626. if (pszLineStart >= (pszMsg + 2))
  15627. {
  15628. //
  15629. // The previous line should have ended with {CR, LF}, which
  15630. // gets modified to {NULL termination, LF}.
  15631. //
  15632. if ((*(pszLineStart - 2) != '\0') ||
  15633. (*(pszLineStart - 1) != '\n'))
  15634. {
  15635. DPFX(DPFPREP, 7, "Ignoring line \"%hs\" because previous character sequence was not {NULL terminator, LF}.",
  15636. pszLineStart);
  15637. }
  15638. else
  15639. {
  15640. DPFX(DPFPREP, 7, "Appending line \"%hs\" to previous line.",
  15641. pszLineStart);
  15642. //
  15643. // Replace the NULL terminator/LF pair with spaces so
  15644. // future parsing sees the previous line and this one as
  15645. // one string.
  15646. //
  15647. *(pszLineStart - 2) = ' ';
  15648. *(pszLineStart - 1) = ' ';
  15649. }
  15650. }
  15651. else
  15652. {
  15653. DPFX(DPFPREP, 7, "Ignoring initial line \"%hs\" that starts with whitespace.",
  15654. pszLineStart);
  15655. }
  15656. }
  15657. //
  15658. // Find the colon separating the header.
  15659. //
  15660. pszHeaderDelimiter = strchr(pszLineStart, ':');
  15661. if (pszHeaderDelimiter != NULL)
  15662. {
  15663. //
  15664. // Truncate the string at the end of the header.
  15665. //
  15666. (*pszHeaderDelimiter) = '\0';
  15667. //
  15668. // Remove the white space surrounding the header name.
  15669. //
  15670. strtrim(&pszLineStart);
  15671. //
  15672. // Parse the header type.
  15673. //
  15674. for(i = 0; i < NUM_RESPONSE_HEADERS; i++)
  15675. {
  15676. if (_stricmp(c_szResponseHeaders[i], pszLineStart) == 0)
  15677. {
  15678. //
  15679. // Found the header. Save it if it's not a duplicate.
  15680. //
  15681. if (pHeaderInfo->apszHeaderStrings[i] == NULL)
  15682. {
  15683. char * pszTrimmedValue;
  15684. //
  15685. // Skip leading and trailing whitespace in the value.
  15686. //
  15687. pszTrimmedValue = pszHeaderDelimiter + 1;
  15688. strtrim(&pszTrimmedValue);
  15689. pHeaderInfo->apszHeaderStrings[i] = pszTrimmedValue;
  15690. DPFX(DPFPREP, 7, "Recognized header %i:\"%hs\", data = \"%hs\".",
  15691. i, pszLineStart, pHeaderInfo->apszHeaderStrings[i]);
  15692. }
  15693. else
  15694. {
  15695. DPFX(DPFPREP, 7, "Ignoring duplicate header %i:\"%hs\", data = \"%hs\".",
  15696. i, pszLineStart, (pszHeaderDelimiter + 1));
  15697. }
  15698. break;
  15699. }
  15700. }
  15701. #ifdef DBG
  15702. //
  15703. // Print unrecognized headers.
  15704. //
  15705. if (i >= NUM_RESPONSE_HEADERS)
  15706. {
  15707. DPFX(DPFPREP, 7, "Ignoring unrecognized header \"%hs\", data = \"%hs\".",
  15708. pszLineStart, (pszHeaderDelimiter + 1));
  15709. }
  15710. #endif // DBG
  15711. }
  15712. else
  15713. {
  15714. DPFX(DPFPREP, 7, "Ignoring line \"%hs\", no header delimiter.",
  15715. pszLineStart);
  15716. }
  15717. //
  15718. // Go to the next UPnP header (if any).
  15719. //
  15720. pszCurrent++;
  15721. pszLineStart = pszCurrent;
  15722. }
  15723. while (TRUE);
  15724. //
  15725. // At this point pHeaderInfo->apszHeaderStrings should contain pointers to
  15726. // the data for all the headers that were found, and
  15727. // pHeaderInfo->pszMsgBody should point to the end of the headers.
  15728. //
  15729. } // CNATHelpUPnP::ParseUPnPHeaders
  15730. #undef DPF_MODNAME
  15731. #define DPF_MODNAME "CNATHelpUPnP::GetAddressFromURL"
  15732. //=============================================================================
  15733. // CNATHelpUPnP::GetAddressFromURL
  15734. //-----------------------------------------------------------------------------
  15735. //
  15736. // Description: Parses a UPnP URL into a SOCKADDR_IN structure. Only "http://"
  15737. // URLs are parsed. The string passed in may be temporarily
  15738. // modified.
  15739. //
  15740. // Arguments:
  15741. // char * pszLocation - Pointer to buffer containing the Location
  15742. // header. It will be modified.
  15743. // SOCKADDR_IN * psaddrinLocation - Place to store address contained in
  15744. // header string.
  15745. // char ** ppszRelativePath - Place to store pointer to rest of path
  15746. // (stuff after hostname and optional
  15747. // port).
  15748. //
  15749. // Returns: HRESULT
  15750. // DPNH_OK - String was parsed successfully.
  15751. // DPNHERR_GENERIC - An error occurred.
  15752. //=============================================================================
  15753. HRESULT CNATHelpUPnP::GetAddressFromURL(char * const pszLocation,
  15754. SOCKADDR_IN * psaddrinLocation,
  15755. char ** ppszRelativePath)
  15756. {
  15757. HRESULT hr;
  15758. BOOL fModifiedDelimiterChar = FALSE;
  15759. char * pszStart;
  15760. char * pszDelimiter;
  15761. char cTempChar;
  15762. PHOSTENT phostent;
  15763. //
  15764. // Initialize the address. Default to the standard HTTP port.
  15765. //
  15766. ZeroMemory(psaddrinLocation, sizeof(SOCKADDR_IN));
  15767. psaddrinLocation->sin_family = AF_INET;
  15768. psaddrinLocation->sin_port = HTONS(HTTP_PORT);
  15769. //
  15770. // Clear the relative path pointer.
  15771. //
  15772. (*ppszRelativePath) = NULL;
  15773. //
  15774. // Skip past "http://". If it's not "http://", then fail.
  15775. //
  15776. if (_strnicmp(pszLocation, "http://", strlen("http://")) != 0)
  15777. {
  15778. DPFX(DPFPREP, 1, "Location URL (\"%hs\") does not start with \"http://\".",
  15779. pszLocation);
  15780. hr = DPNHERR_GENERIC;
  15781. goto Exit;
  15782. }
  15783. pszStart = pszLocation + strlen("http://");
  15784. //
  15785. // See if there's a port specified or any extraneous junk after an IP
  15786. // address or hostname to figure out the string to use. Search from the
  15787. // start of the string until we hit the end of the string or a reserved URL
  15788. // character.
  15789. //
  15790. pszDelimiter = pszStart + 1;
  15791. while (((*pszDelimiter) != '\0') &&
  15792. ((*pszDelimiter) != '/') &&
  15793. ((*pszDelimiter) != '?') &&
  15794. ((*pszDelimiter) != '=') &&
  15795. ((*pszDelimiter) != '#'))
  15796. {
  15797. if ((*pszDelimiter) == ':')
  15798. {
  15799. char * pszPortEnd;
  15800. //
  15801. // We found the start of a port, search for the end. It must
  15802. // contain only numeric characters.
  15803. //
  15804. pszPortEnd = pszDelimiter + 1;
  15805. while (((*pszPortEnd) >= '0') && ((*pszPortEnd) <= '9'))
  15806. {
  15807. pszPortEnd++;
  15808. }
  15809. //
  15810. // Temporarily truncate the string.
  15811. //
  15812. cTempChar = (*pszPortEnd);
  15813. (*pszPortEnd) = '\0';
  15814. DPFX(DPFPREP, 7, "Found port \"%hs\".", (pszDelimiter + 1));
  15815. psaddrinLocation->sin_port = HTONS((u_short) atoi(pszDelimiter + 1));
  15816. //
  15817. // Restore the character.
  15818. //
  15819. (*pszPortEnd) = cTempChar;
  15820. //
  15821. // Save the relative path
  15822. //
  15823. (*ppszRelativePath) = pszPortEnd;
  15824. break;
  15825. }
  15826. pszDelimiter++;
  15827. }
  15828. //
  15829. // Remember the character that stopped the search, and then temporarily
  15830. // truncate the string.
  15831. //
  15832. cTempChar = (*pszDelimiter);
  15833. (*pszDelimiter) = '\0';
  15834. fModifiedDelimiterChar = TRUE;
  15835. //
  15836. // Save the relative path if we haven't already (because of a port).
  15837. //
  15838. if ((*ppszRelativePath) == NULL)
  15839. {
  15840. (*ppszRelativePath) = pszDelimiter;
  15841. }
  15842. DPFX(DPFPREP, 7, "Relative path = \"%hs\".", (*ppszRelativePath));
  15843. //
  15844. // Convert the hostname.
  15845. //
  15846. psaddrinLocation->sin_addr.S_un.S_addr = this->m_pfninet_addr(pszStart);
  15847. //
  15848. // If it's bogus, give up.
  15849. //
  15850. if (psaddrinLocation->sin_addr.S_un.S_addr == INADDR_ANY)
  15851. {
  15852. DPFX(DPFPREP, 0, "Host name \"%hs\" is invalid!",
  15853. pszStart);
  15854. hr = DPNHERR_GENERIC;
  15855. goto Exit;
  15856. }
  15857. if (psaddrinLocation->sin_addr.S_un.S_addr == INADDR_NONE)
  15858. {
  15859. //
  15860. // It's not a straight IP address. Lookup the hostname.
  15861. //
  15862. phostent = this->m_pfngethostbyname(pszStart);
  15863. if (phostent == NULL)
  15864. {
  15865. DPFX(DPFPREP, 0, "Couldn't lookup host name \"%hs\"!",
  15866. pszStart);
  15867. hr = DPNHERR_GENERIC;
  15868. goto Exit;
  15869. }
  15870. if (phostent->h_addr_list[0] == NULL)
  15871. {
  15872. DPFX(DPFPREP, 0, "Host name \"%hs\" has no address entries!",
  15873. pszStart);
  15874. hr = DPNHERR_GENERIC;
  15875. goto Exit;
  15876. }
  15877. //
  15878. // Pick the first address returned.
  15879. //
  15880. #ifdef DBG
  15881. {
  15882. IN_ADDR ** ppinaddr;
  15883. DWORD dwNumAddrs;
  15884. ppinaddr = (IN_ADDR**) phostent->h_addr_list;
  15885. dwNumAddrs = 0;
  15886. while ((*ppinaddr) != NULL)
  15887. {
  15888. ppinaddr++;
  15889. dwNumAddrs++;
  15890. }
  15891. DPFX(DPFPREP, 7, "Picking first (of %u IP addresses) for \"%hs\".",
  15892. dwNumAddrs, pszStart);
  15893. }
  15894. #endif // DBG
  15895. psaddrinLocation->sin_addr.S_un.S_addr = ((IN_ADDR*) phostent->h_addr_list[0])->S_un.S_addr;
  15896. }
  15897. else
  15898. {
  15899. DPFX(DPFPREP, 7, "Successfully converted IP address \"%hs\".", pszStart);
  15900. }
  15901. hr = DPNH_OK;
  15902. Exit:
  15903. //
  15904. // If we found a port, restore the string. If not, use the default port.
  15905. //
  15906. if (fModifiedDelimiterChar)
  15907. {
  15908. //
  15909. // Note that PREfast reported this as being used before being
  15910. // initialized for a while. For some reason it didn't notice that I
  15911. // key off of fModifiedDelimiterChar. This appeared to get fixed, but
  15912. // PREfast is still giving me a false hit for a similar reason
  15913. // elsewhere.
  15914. //
  15915. (*pszDelimiter) = cTempChar;
  15916. }
  15917. DPFX(DPFPREP, 8, "Returning %u.%u.%u.%u:%u, hr = 0x%lx.",
  15918. psaddrinLocation->sin_addr.S_un.S_un_b.s_b1,
  15919. psaddrinLocation->sin_addr.S_un.S_un_b.s_b2,
  15920. psaddrinLocation->sin_addr.S_un.S_un_b.s_b3,
  15921. psaddrinLocation->sin_addr.S_un.S_un_b.s_b4,
  15922. NTOHS(psaddrinLocation->sin_port),
  15923. hr);
  15924. return hr;
  15925. } // CNATHelpUPnP::GetAddressFromURL
  15926. #undef DPF_MODNAME
  15927. #define DPF_MODNAME "CNATHelpUPnP::HandleUPnPDescriptionResponseBody"
  15928. //=============================================================================
  15929. // CNATHelpUPnP::HandleUPnPDescriptionResponseBody
  15930. //-----------------------------------------------------------------------------
  15931. //
  15932. // Description: Handles a UPnP device description response. The string will
  15933. // be modified.
  15934. //
  15935. // The UPnP device may get removed from list if a failure
  15936. // occurs, the caller needs to have a reference.
  15937. //
  15938. // The object lock is assumed to be held.
  15939. //
  15940. // Arguments:
  15941. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device being described.
  15942. // DWORD dwHTTPResponseCode - HTTP header response code.
  15943. // char * pszDescriptionXML - UPnP device description XML string.
  15944. //
  15945. // Returns: HRESULT
  15946. // DPNH_OK - Description response was handled successfully.
  15947. // DPNHERR_GENERIC - An error occurred.
  15948. //=============================================================================
  15949. HRESULT CNATHelpUPnP::HandleUPnPDescriptionResponseBody(CUPnPDevice * const pUPnPDevice,
  15950. const DWORD dwHTTPResponseCode,
  15951. char * const pszDescriptionXML)
  15952. {
  15953. HRESULT hr;
  15954. PARSEXML_SUBELEMENT aSubElements[MAX_NUM_DESCRIPTION_XML_SUBELEMENTS];
  15955. PARSEXML_ELEMENT ParseElement;
  15956. CDevice * pDevice;
  15957. CBilink * pBilink;
  15958. CRegisteredPort * pRegisteredPort;
  15959. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %u, 0x%p)",
  15960. this, pUPnPDevice, dwHTTPResponseCode, pszDescriptionXML);
  15961. //
  15962. // Make sure it was the success result.
  15963. //
  15964. if (dwHTTPResponseCode != 200)
  15965. {
  15966. DPFX(DPFPREP, 0, "Got error response %u from UPnP description request!",
  15967. dwHTTPResponseCode);
  15968. hr = DPNHERR_GENERIC;
  15969. goto Failure;
  15970. }
  15971. ZeroMemory(aSubElements, sizeof(aSubElements));
  15972. ZeroMemory(&ParseElement, sizeof(ParseElement));
  15973. ParseElement.papszElementStack = (char**) (&c_szElementStack_service);
  15974. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_service) / sizeof(char*);
  15975. ParseElement.paSubElements = (PARSEXML_SUBELEMENT*) (aSubElements);
  15976. ParseElement.dwMaxNumSubElements = MAX_NUM_DESCRIPTION_XML_SUBELEMENTS;
  15977. //ParseElement.dwNumSubElements = 0;
  15978. //ParseElement.fFoundMatchingElement = FALSE;
  15979. hr = this->ParseXML(pszDescriptionXML,
  15980. &ParseElement,
  15981. PARSECALLBACK_DESCRIPTIONRESPONSE,
  15982. pUPnPDevice);
  15983. if (hr != DPNH_OK)
  15984. {
  15985. DPFX(DPFPREP, 0, "Couldn't parse XML!");
  15986. goto Failure;
  15987. }
  15988. //
  15989. // If we did not find a WANIPConnection or WANPPPConnection service, then
  15990. // this response was not valid.
  15991. //
  15992. if (pUPnPDevice->GetServiceControlURL() == NULL)
  15993. {
  15994. DPFX(DPFPREP, 0, "Couldn't find WANIPConnection or WANPPPConnection service in XML description!");
  15995. hr = DPNHERR_GENERIC;
  15996. goto Failure;
  15997. }
  15998. //
  15999. // The UPnP device is now controllable.
  16000. //
  16001. pUPnPDevice->NoteReady();
  16002. //
  16003. // Find out what the device's external IP address is. Note that calling
  16004. // UpdateUPnPExternalAddress will overwrite the buffer containing the
  16005. // pszDescriptionXML string. That's fine, because we've saved all the
  16006. // stuff in there that we need already.
  16007. //
  16008. hr = this->UpdateUPnPExternalAddress(pUPnPDevice, FALSE);
  16009. if (hr != DPNH_OK)
  16010. {
  16011. DPFX(DPFPREP, 0, "Couldn't update new UPnP device 0x%p's external address!",
  16012. pUPnPDevice);
  16013. goto Failure;
  16014. }
  16015. //
  16016. // Map existing registered ports with this new UPnP device.
  16017. //
  16018. pDevice = pUPnPDevice->GetOwningDevice();
  16019. DNASSERT(pDevice != NULL);
  16020. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  16021. while (pBilink != &pDevice->m_blOwnedRegPorts)
  16022. {
  16023. DNASSERT(! pBilink->IsEmpty());
  16024. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  16025. //
  16026. // Note that calling MapPortsOnUPnPDevice will overwrite the buffer
  16027. // containing the pszDescriptionXML string. That's fine, because
  16028. // we've saved all the stuff in there that we need already.
  16029. //
  16030. hr = this->MapPortsOnUPnPDevice(pUPnPDevice, pRegisteredPort);
  16031. if (hr != DPNH_OK)
  16032. {
  16033. DPFX(DPFPREP, 0, "Couldn't map existing ports on new UPnP device 0x%p!",
  16034. pUPnPDevice);
  16035. goto Failure;
  16036. }
  16037. //
  16038. // Let the user know the addresses changed next time GetCaps is
  16039. // called.
  16040. //
  16041. DPFX(DPFPREP, 8, "Noting that addresses changed (for registered port 0x%p).",
  16042. pRegisteredPort);
  16043. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  16044. pBilink = pBilink->GetNext();
  16045. }
  16046. //
  16047. // Try to remove any mappings that were not freed earlier because we
  16048. // crashed.
  16049. //
  16050. hr = this->CleanupInactiveNATMappings(pUPnPDevice);
  16051. if (hr != DPNH_OK)
  16052. {
  16053. DPFX(DPFPREP, 0, "Failed cleaning up inactive mappings with new UPnP device 0x%p!",
  16054. pUPnPDevice);
  16055. goto Failure;
  16056. }
  16057. Exit:
  16058. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  16059. return hr;
  16060. Failure:
  16061. goto Exit;
  16062. } // CNATHelpUPnP::HandleUPnPDescriptionResponseBody
  16063. #undef DPF_MODNAME
  16064. #define DPF_MODNAME "CNATHelpUPnP::HandleUPnPControlResponseBody"
  16065. //=============================================================================
  16066. // CNATHelpUPnP::HandleUPnPControlResponseBody
  16067. //-----------------------------------------------------------------------------
  16068. //
  16069. // Description: Handles a UPnP control response. The string will be
  16070. // modified.
  16071. //
  16072. // The object lock is assumed to be held.
  16073. //
  16074. // Arguments:
  16075. // CUPnPDevice * pUPnPDevice - Pointer to UPnP device being described.
  16076. // DWORD dwHTTPResponseCode - HTTP header response code.
  16077. // char * pszControlResponseSOAP - UPnP device response SOAP XML string.
  16078. //
  16079. // Returns: HRESULT
  16080. // DPNH_OK - Description response was handled successfully.
  16081. // DPNHERR_GENERIC - An error occurred.
  16082. //=============================================================================
  16083. HRESULT CNATHelpUPnP::HandleUPnPControlResponseBody(CUPnPDevice * const pUPnPDevice,
  16084. const DWORD dwHTTPResponseCode,
  16085. char * const pszControlResponseSOAP)
  16086. {
  16087. HRESULT hr = DPNH_OK;
  16088. CONTROLRESPONSEPARSECONTEXT crpc;
  16089. PARSEXML_SUBELEMENT aSubElements[MAX_NUM_UPNPCONTROLOUTARGS];
  16090. PARSEXML_ELEMENT ParseElement;
  16091. DNASSERT(pUPnPDevice->IsWaitingForControlResponse());
  16092. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %u, 0x%p)",
  16093. this, pUPnPDevice, dwHTTPResponseCode, pszControlResponseSOAP);
  16094. ZeroMemory(&crpc, sizeof(crpc));
  16095. crpc.ControlResponseType = pUPnPDevice->GetControlResponseType();
  16096. crpc.pUPnPDevice = pUPnPDevice;
  16097. crpc.dwHTTPResponseCode = dwHTTPResponseCode;
  16098. crpc.pControlResponseInfo = pUPnPDevice->GetControlResponseInfo();
  16099. ZeroMemory(aSubElements, sizeof(aSubElements));
  16100. ZeroMemory(&ParseElement, sizeof(ParseElement));
  16101. if (dwHTTPResponseCode == 200)
  16102. {
  16103. switch (crpc.ControlResponseType)
  16104. {
  16105. /*
  16106. case CONTROLRESPONSETYPE_QUERYSTATEVARIABLE_EXTERNALIPADDRESS:
  16107. {
  16108. ParseElement.papszElementStack = (char**) (&c_szElementStack_QueryStateVariableResponse);
  16109. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_QueryStateVariableResponse) / sizeof(char*);
  16110. break;
  16111. }
  16112. */
  16113. case CONTROLRESPONSETYPE_GETEXTERNALIPADDRESS:
  16114. {
  16115. ParseElement.papszElementStack = (char**) (&c_szElementStack_GetExternalIPAddressResponse);
  16116. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_GetExternalIPAddressResponse) / sizeof(char*);
  16117. break;
  16118. }
  16119. case CONTROLRESPONSETYPE_ADDPORTMAPPING:
  16120. {
  16121. ParseElement.papszElementStack = (char**) (&c_szElementStack_AddPortMappingResponse);
  16122. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_AddPortMappingResponse) / sizeof(char*);
  16123. break;
  16124. }
  16125. case CONTROLRESPONSETYPE_GETSPECIFICPORTMAPPINGENTRY:
  16126. {
  16127. ParseElement.papszElementStack = (char**) (&c_szElementStack_GetSpecificPortMappingEntryResponse);
  16128. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_GetSpecificPortMappingEntryResponse) / sizeof(char*);
  16129. break;
  16130. }
  16131. case CONTROLRESPONSETYPE_DELETEPORTMAPPING:
  16132. {
  16133. ParseElement.papszElementStack = (char**) (&c_szElementStack_DeletePortMappingResponse);
  16134. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_DeletePortMappingResponse) / sizeof(char*);
  16135. break;
  16136. }
  16137. default:
  16138. {
  16139. DNASSERT(FALSE);
  16140. hr = DPNHERR_GENERIC;
  16141. goto Failure;
  16142. break;
  16143. }
  16144. }
  16145. }
  16146. else
  16147. {
  16148. ParseElement.papszElementStack = (char**) (&c_szElementStack_ControlResponseFailure);
  16149. ParseElement.dwElementStackDepth = sizeof(c_szElementStack_ControlResponseFailure) / sizeof(char*);
  16150. }
  16151. ParseElement.paSubElements = (PARSEXML_SUBELEMENT*) (aSubElements);
  16152. ParseElement.dwMaxNumSubElements = MAX_NUM_UPNPCONTROLOUTARGS;
  16153. //ParseElement.dwNumSubElements = 0;
  16154. //ParseElement.fFoundMatchingElement = FALSE;
  16155. hr = this->ParseXML(pszControlResponseSOAP,
  16156. &ParseElement,
  16157. PARSECALLBACK_CONTROLRESPONSE,
  16158. &crpc);
  16159. if (hr != DPNH_OK)
  16160. {
  16161. DPFX(DPFPREP, 0, "Couldn't parse XML!");
  16162. goto Failure;
  16163. }
  16164. //
  16165. // If we didn't a matching item, map it to a generic failure.
  16166. //
  16167. if (! ParseElement.fFoundMatchingElement)
  16168. {
  16169. if (dwHTTPResponseCode == 200)
  16170. {
  16171. DPFX(DPFPREP, 1, "Didn't find XML items in success response, mapping to generic failure.");
  16172. }
  16173. else
  16174. {
  16175. DPFX(DPFPREP, 1, "Didn't find failure XML items, using generic failure.");
  16176. }
  16177. crpc.pControlResponseInfo->hrErrorCode = DPNHERR_GENERIC;
  16178. }
  16179. pUPnPDevice->StopWaitingForControlResponse();
  16180. Exit:
  16181. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  16182. return hr;
  16183. Failure:
  16184. goto Exit;
  16185. } // CNATHelpUPnP::HandleUPnPControlResponseBody
  16186. #undef DPF_MODNAME
  16187. #define DPF_MODNAME "CNATHelpUPnP::ParseXML"
  16188. //=============================================================================
  16189. // CNATHelpUPnP::ParseXML
  16190. //-----------------------------------------------------------------------------
  16191. //
  16192. // Description: Parses an XML string for a specific element, and calls a
  16193. // helper function for each instance found.
  16194. //
  16195. // Subelement values cannot themselves contain subelements. If
  16196. // they do, the sub-subelements will be ignored.
  16197. //
  16198. // The string buffer is modified.
  16199. //
  16200. // Arguments:
  16201. // char * pszXML - XML string to parse.
  16202. // PARSEXML_ELEMENT * pParseElement - Pointer to element whose sub element
  16203. // values should be retrieved.
  16204. // PARSECALLBACK ParseCallback - Enum indicating what helper function
  16205. // to use.
  16206. // PVOID pvContext - Pointer to context value to pass to
  16207. // helper function.
  16208. //
  16209. // Returns: HRESULT
  16210. // DPNH_OK - Description response was handled successfully.
  16211. // DPNHERR_GENERIC - An error occurred.
  16212. //=============================================================================
  16213. HRESULT CNATHelpUPnP::ParseXML(char * const pszXML,
  16214. PARSEXML_ELEMENT * const pParseElement,
  16215. const PARSECALLBACK ParseCallback,
  16216. PVOID pvContext)
  16217. {
  16218. HRESULT hr = DPNH_OK;
  16219. PARSEXML_STACKENTRY aElementStack[MAX_XMLELEMENT_DEPTH];
  16220. DWORD dwCurrentElementDepth = 0;
  16221. char * pszElementTagStart = NULL;
  16222. BOOL fInElement = FALSE;
  16223. BOOL fEmptyElement = FALSE;
  16224. char * pszCurrent;
  16225. DWORD dwStackDepth;
  16226. PARSEXML_SUBELEMENT * pSubElement;
  16227. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p)",
  16228. this, pszXML, pParseElement);
  16229. //
  16230. // Need room for entire stack + at least one subelement level.
  16231. //
  16232. DNASSERT(pParseElement->dwElementStackDepth < MAX_XMLELEMENT_DEPTH);
  16233. #ifdef DBG
  16234. this->PrintUPnPTransactionToFile(pszXML,
  16235. strlen(pszXML),
  16236. "Inbound XML Body",
  16237. NULL);
  16238. #endif // DBG
  16239. //
  16240. // Loop through the XML looking for the given elements.
  16241. //
  16242. pszCurrent = pszXML;
  16243. while ((*pszCurrent) != '\0')
  16244. {
  16245. switch (*pszCurrent)
  16246. {
  16247. case '<':
  16248. {
  16249. //
  16250. // If we're in an element tag already, this is bogus XML or a
  16251. // CDATA section (which we don't handle). Fail.
  16252. //
  16253. if (pszElementTagStart != NULL)
  16254. {
  16255. DPFX(DPFPREP, 0, "Encountered '<' character in element tag, XML parsing failed.");
  16256. goto Failure;
  16257. }
  16258. //
  16259. // Truncate the string here in case this is the start of an
  16260. // element end tag. This delimits a value string.
  16261. //
  16262. (*pszCurrent) = '\0';
  16263. pszElementTagStart = pszCurrent + 1;
  16264. if ((*pszElementTagStart) == '\0')
  16265. {
  16266. DPFX(DPFPREP, 0, "Encountered '<' character at end of string, XML parsing failed.");
  16267. goto Failure;
  16268. }
  16269. break;
  16270. }
  16271. case '>':
  16272. {
  16273. //
  16274. // If we're not in an element tag, this is bogus XML or a CDATA
  16275. // section (which we don't handle). Fail.
  16276. //
  16277. if (pszElementTagStart == NULL)
  16278. {
  16279. DPFX(DPFPREP, 0, "Encountered '>' character outside of element tag, XML parsing failed.");
  16280. goto Failure;
  16281. }
  16282. //
  16283. // Truncate the string here.
  16284. //
  16285. (*pszCurrent) = '\0';
  16286. //
  16287. // This could either be a start or an end tag. If the first
  16288. // character of the tag is '/', then it's an end tag.
  16289. //
  16290. // Note that empty element tags begin by being parsed like a
  16291. // start tag, but then jump into the end tag clause.
  16292. //
  16293. if ((*pszElementTagStart) == '/')
  16294. {
  16295. pszElementTagStart++;
  16296. //
  16297. // Make sure the element tag stack is valid. The name of
  16298. // this end tag should match the start tag at the top of
  16299. // the stack. XML elements are case sensitive.
  16300. //
  16301. if (dwCurrentElementDepth == 0)
  16302. {
  16303. DPFX(DPFPREP, 0, "Encountered extra element end tag \"%hs\", XML parsing failed.",
  16304. pszElementTagStart);
  16305. goto Failure;
  16306. }
  16307. if (strcmp(pszElementTagStart, aElementStack[dwCurrentElementDepth - 1].pszName) != 0)
  16308. {
  16309. DPFX(DPFPREP, 0, "Encountered non-matching element end tag (\"%hs\" != \"%hs\"), XML parsing failed.",
  16310. pszElementTagStart,
  16311. aElementStack[dwCurrentElementDepth - 1].pszName);
  16312. goto Failure;
  16313. }
  16314. TagEnd:
  16315. //
  16316. // If we're here, then we have a complete element. If we
  16317. // were in the element, then it's either:
  16318. // the end of a sub-sub element,
  16319. // the end of a sub element, or
  16320. // the end of the element itself.
  16321. //
  16322. if (fInElement)
  16323. {
  16324. switch (dwCurrentElementDepth - pParseElement->dwElementStackDepth)
  16325. {
  16326. case 0:
  16327. {
  16328. //
  16329. // It's the end of the element. Call the
  16330. // helper function. Reuse fInElement as the
  16331. // fContinueParsing BOOL.
  16332. //
  16333. switch (ParseCallback)
  16334. {
  16335. case PARSECALLBACK_DESCRIPTIONRESPONSE:
  16336. {
  16337. hr = this->ParseXMLCallback_DescriptionResponse(pParseElement,
  16338. pvContext,
  16339. aElementStack,
  16340. &fInElement);
  16341. if (hr != DPNH_OK)
  16342. {
  16343. DPFX(DPFPREP, 0, "Description response parse helper function failed!");
  16344. goto Failure;
  16345. }
  16346. break;
  16347. }
  16348. case PARSECALLBACK_CONTROLRESPONSE:
  16349. {
  16350. hr = this->ParseXMLCallback_ControlResponse(pParseElement,
  16351. pvContext,
  16352. aElementStack,
  16353. &fInElement);
  16354. if (hr != DPNH_OK)
  16355. {
  16356. DPFX(DPFPREP, 0, "Control response parse helper function failed!");
  16357. goto Failure;
  16358. }
  16359. break;
  16360. }
  16361. default:
  16362. {
  16363. DNASSERT(FALSE);
  16364. break;
  16365. }
  16366. }
  16367. if (! fInElement)
  16368. {
  16369. DPFX(DPFPREP, 1, "Parse callback function discontinued parsing.");
  16370. goto Exit;
  16371. }
  16372. //
  16373. // Keep parsing, but we're no longer in the
  16374. // element. Reset the sub element counter in
  16375. // case we found entries.
  16376. //
  16377. fInElement = FALSE;
  16378. pParseElement->dwNumSubElements = 0;
  16379. break;
  16380. }
  16381. case 1:
  16382. {
  16383. //
  16384. // It's the end of a subelement. Complete this
  16385. // instance, if there's room.
  16386. //
  16387. if (pParseElement->dwNumSubElements < pParseElement->dwMaxNumSubElements)
  16388. {
  16389. pSubElement = &pParseElement->paSubElements[pParseElement->dwNumSubElements];
  16390. pSubElement->pszNameFound = pszElementTagStart;
  16391. pSubElement->dwNumAttributes = aElementStack[dwCurrentElementDepth - 1].dwNumAttributes;
  16392. if (pSubElement->dwNumAttributes > 0)
  16393. {
  16394. memcpy(pSubElement->apszAttributeNames,
  16395. aElementStack[dwCurrentElementDepth - 1].apszAttributeNames,
  16396. (pSubElement->dwNumAttributes * sizeof(char*)));
  16397. memcpy(pSubElement->apszAttributeValues,
  16398. aElementStack[dwCurrentElementDepth - 1].apszAttributeValues,
  16399. (pSubElement->dwNumAttributes * sizeof(char*)));
  16400. }
  16401. pSubElement->pszValueFound = aElementStack[dwCurrentElementDepth - 1].pszValue;
  16402. pParseElement->dwNumSubElements++;
  16403. DPFX(DPFPREP, 7, "Completed subelement instance #%u, name = \"%hs\", %u attributes, value = \"%hs\".",
  16404. pParseElement->dwNumSubElements,
  16405. pSubElement->pszNameFound,
  16406. pSubElement->dwNumAttributes,
  16407. pSubElement->pszValueFound);
  16408. }
  16409. else
  16410. {
  16411. DPFX(DPFPREP, 0, "Ignoring subelement instance \"%hs\" (%u attributes, value = \"%hs\"), no room in array.",
  16412. pszElementTagStart,
  16413. aElementStack[dwCurrentElementDepth - 1].dwNumAttributes,
  16414. aElementStack[dwCurrentElementDepth - 1].pszValue);
  16415. }
  16416. break;
  16417. }
  16418. default:
  16419. {
  16420. //
  16421. // It's the end of a sub-subelement.
  16422. //
  16423. DPFX(DPFPREP, 1, "Ignoring sub-sub element \"%hs\" (%u attributes, value = \"%hs\").",
  16424. pszElementTagStart,
  16425. aElementStack[dwCurrentElementDepth - 1].dwNumAttributes,
  16426. aElementStack[dwCurrentElementDepth - 1].pszValue);
  16427. break;
  16428. }
  16429. }
  16430. }
  16431. //
  16432. // Pop the element off the stack.
  16433. //
  16434. dwCurrentElementDepth--;
  16435. }
  16436. else
  16437. {
  16438. //
  16439. // It's not an end tag, but it might be an empty element
  16440. // (i.e. "<tag/>").
  16441. //
  16442. if (*(pszCurrent - 1) == '/')
  16443. {
  16444. //
  16445. // Truncate the string early.
  16446. //
  16447. *(pszCurrent - 1) = '\0';
  16448. //
  16449. // Remember this state so we can parse it properly.
  16450. //
  16451. fEmptyElement = TRUE;
  16452. DPFX(DPFPREP, 7, "XML element \"%hs\" is empty (i.e. is both a start and end tag).",
  16453. pszElementTagStart);
  16454. }
  16455. //
  16456. // Push the element on the tag stack, if there's room.
  16457. //
  16458. if (dwCurrentElementDepth >= MAX_XMLELEMENT_DEPTH)
  16459. {
  16460. DPFX(DPFPREP, 0, "Too many nested element tags (%u), XML parsing failed.",
  16461. dwCurrentElementDepth);
  16462. goto Failure;
  16463. }
  16464. aElementStack[dwCurrentElementDepth].pszName = pszElementTagStart;
  16465. //
  16466. // If there are attributes to this element, separate them
  16467. // into a different array. They will not be parsed,
  16468. // though.
  16469. // Attributes are delimited by whitespace.
  16470. //
  16471. while ((*pszElementTagStart) != '\0')
  16472. {
  16473. pszElementTagStart++;
  16474. //
  16475. // If it's whitespace, that's the end of the element
  16476. // name. Truncate the string and break out of the
  16477. // loops.
  16478. //
  16479. if (((*pszElementTagStart) == ' ') ||
  16480. ((*pszElementTagStart) == '\t') ||
  16481. ((*pszElementTagStart) == '\r') ||
  16482. ((*pszElementTagStart) == '\n'))
  16483. {
  16484. (*pszElementTagStart) = '\0';
  16485. pszElementTagStart++;
  16486. DPFX(DPFPREP, 8, "Attribute whitespace found at offset 0x%p, string length = %i.",
  16487. (pszElementTagStart - aElementStack[dwCurrentElementDepth].pszName),
  16488. strlen(pszElementTagStart));
  16489. break;
  16490. }
  16491. }
  16492. //
  16493. // If there weren't any attributes, pszElementTagStart will
  16494. // just point to an empty (but not NULL) string.
  16495. //
  16496. // So save the start of the value string.
  16497. //
  16498. aElementStack[dwCurrentElementDepth].pszValue = pszElementTagStart + strlen(pszElementTagStart) + 1;
  16499. //
  16500. // Then parse out the attributes.
  16501. //
  16502. this->ParseXMLAttributes(pszElementTagStart,
  16503. aElementStack[dwCurrentElementDepth].apszAttributeNames,
  16504. aElementStack[dwCurrentElementDepth].apszAttributeValues,
  16505. MAX_XMLNAMESPACES_PER_ELEMENT,
  16506. &(aElementStack[dwCurrentElementDepth].dwNumAttributes));
  16507. //
  16508. // The <?xml> tag is considered optional by this parser,
  16509. // and will be ignored.
  16510. //
  16511. if (_stricmp(aElementStack[dwCurrentElementDepth].pszName, "?xml") != 0)
  16512. {
  16513. //
  16514. // Bump the stack pointer.
  16515. //
  16516. dwCurrentElementDepth++;
  16517. //
  16518. // See if this the right element. If the stack depth
  16519. // isn't right, it can't be the desired item.
  16520. // Otherwise, make sure the stack matches.
  16521. //
  16522. if (dwCurrentElementDepth == pParseElement->dwElementStackDepth)
  16523. {
  16524. //
  16525. // Work through the entire element stack, making
  16526. // sure each name matches.
  16527. //
  16528. for(dwStackDepth = 0; dwStackDepth < dwCurrentElementDepth; dwStackDepth++)
  16529. {
  16530. if (! this->MatchesXMLStringWithoutNamespace(aElementStack[dwStackDepth].pszName,
  16531. pParseElement->papszElementStack[dwStackDepth],
  16532. aElementStack,
  16533. NULL,
  16534. (dwStackDepth + 1)))
  16535. {
  16536. //
  16537. // It didn't match. Stop looping.
  16538. //
  16539. break;
  16540. }
  16541. }
  16542. //
  16543. // If they all matched, we found the value desired.
  16544. //
  16545. if (dwStackDepth == dwCurrentElementDepth)
  16546. {
  16547. fInElement = TRUE;
  16548. DPFX(DPFPREP, 7, "Found requested element \"%hs\" at depth %u, has %u attributes.",
  16549. aElementStack[dwCurrentElementDepth - 1].pszName,
  16550. dwCurrentElementDepth,
  16551. aElementStack[dwCurrentElementDepth - 1].dwNumAttributes);
  16552. }
  16553. }
  16554. }
  16555. else
  16556. {
  16557. DPFX(DPFPREP, 7, "Ignoring element \"%hs\" at depth %u that has %u attributes.",
  16558. aElementStack[dwCurrentElementDepth].pszName,
  16559. dwCurrentElementDepth,
  16560. aElementStack[dwCurrentElementDepth].dwNumAttributes);
  16561. //
  16562. // If this assertion fails and it is an empty element,
  16563. // dwCurrentElementDepth will be off by -1 when we jump
  16564. // to TagEnd.
  16565. //
  16566. DNASSERT(! fInElement);
  16567. }
  16568. //
  16569. // If this is an empty element, go immediately to handling
  16570. // the tag closure.
  16571. //
  16572. if (fEmptyElement)
  16573. {
  16574. fEmptyElement = FALSE;
  16575. goto TagEnd;
  16576. }
  16577. }
  16578. //
  16579. // Search for another element tag.
  16580. //
  16581. pszElementTagStart = NULL;
  16582. break;
  16583. }
  16584. default:
  16585. {
  16586. //
  16587. // Ordinary character, continue.
  16588. //
  16589. break;
  16590. }
  16591. }
  16592. //
  16593. // Move to the next character
  16594. //
  16595. pszCurrent++;
  16596. }
  16597. Exit:
  16598. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  16599. return hr;
  16600. Failure:
  16601. hr = DPNHERR_GENERIC;
  16602. goto Exit;
  16603. } // CNATHelpUPnP::ParseXML
  16604. #undef DPF_MODNAME
  16605. #define DPF_MODNAME "CNATHelpUPnP::ParseXMLAttributes"
  16606. //=============================================================================
  16607. // CNATHelpUPnP::ParseXMLAttributes
  16608. //-----------------------------------------------------------------------------
  16609. //
  16610. // Description: Parses any XML attributes out of the given string. The input
  16611. // string buffer will be modified.
  16612. //
  16613. // Arguments:
  16614. // char * pszString - Pointer to attributes string to parse.
  16615. // This will be modified.
  16616. // char ** apszAttributeNames - Array in which to store attribute name
  16617. // string pointers.
  16618. // char ** apszAttributeValues - Matching array in which to store cor-
  16619. // responding attribute value strings.
  16620. // DWORD dwMaxNumAttributes - Maximum number of entries allowed in
  16621. // previous arrays.
  16622. // DWORD * pdwNumAttributes - Place to store number of attributes that
  16623. // were found.
  16624. //
  16625. // Returns: HRESULT
  16626. // DPNH_OK - Description response was handled successfully.
  16627. // DPNHERR_GENERIC - An error occurred.
  16628. //=============================================================================
  16629. void CNATHelpUPnP::ParseXMLAttributes(char * const pszString,
  16630. char ** const apszAttributeNames,
  16631. char ** const apszAttributeValues,
  16632. const DWORD dwMaxNumAttributes,
  16633. DWORD * const pdwNumAttributes)
  16634. {
  16635. char * pszStart;
  16636. char * pszCurrent;
  16637. char * pszEndOfString;
  16638. BOOL fInValueString = FALSE;
  16639. BOOL fInQuotes = FALSE;
  16640. BOOL fEmptyString = FALSE;
  16641. #ifdef EXTRA_PARSING_SPEW
  16642. DPFX(DPFPREP, 8, "(0x%p) Parameters: (\"%hs\", 0x%p, 0x%p, %u, 0x%p)",
  16643. this, pszString, apszAttributeNames, apszAttributeValues,
  16644. dwMaxNumAttributes, pdwNumAttributes);
  16645. #endif // EXTRA_PARSING_SPEW
  16646. //
  16647. // Start at the beginning with no entries.
  16648. //
  16649. (*pdwNumAttributes) = 0;
  16650. pszStart = pszString;
  16651. pszCurrent = pszStart;
  16652. pszEndOfString = pszString + strlen(pszString);
  16653. //
  16654. // Skip empty strings.
  16655. //
  16656. if (pszEndOfString == pszStart)
  16657. {
  16658. return;
  16659. }
  16660. //
  16661. // Loop through the entire string.
  16662. //
  16663. while (pszCurrent <= pszEndOfString)
  16664. {
  16665. switch (*pszCurrent)
  16666. {
  16667. case '=':
  16668. {
  16669. //
  16670. // If we're not in quotes or a value string, this is the end of
  16671. // the attribute name and the start of a value string.
  16672. //
  16673. if ((! fInQuotes) && (! fInValueString))
  16674. {
  16675. (*pszCurrent) = '\0';
  16676. apszAttributeNames[(*pdwNumAttributes)] = pszStart;
  16677. pszStart = pszCurrent + 1;
  16678. fInValueString = TRUE;
  16679. }
  16680. break;
  16681. }
  16682. case '\0':
  16683. case ' ':
  16684. case '\t':
  16685. case '\r':
  16686. case '\n':
  16687. {
  16688. //
  16689. // Whitespace or the end of the string. If we're not in
  16690. // quotes, that means it's the end of an attribute. Of course
  16691. // if it's the end of the string, then we force the end of the
  16692. // attribute/value.
  16693. //
  16694. if ((! fInQuotes) || ((*pszCurrent) == '\0'))
  16695. {
  16696. (*pszCurrent) = '\0';
  16697. if (fInValueString)
  16698. {
  16699. //
  16700. // End of the value string.
  16701. //
  16702. apszAttributeValues[(*pdwNumAttributes)] = pszStart;
  16703. fInValueString = FALSE;
  16704. DPFX(DPFPREP, 7, "Found attribute \"%hs\" with value \"%hs\".",
  16705. apszAttributeNames[(*pdwNumAttributes)],
  16706. apszAttributeValues[(*pdwNumAttributes)]);
  16707. }
  16708. else
  16709. {
  16710. //
  16711. // This may be another whitespace character immediately
  16712. // following a previous one. If so, ignore it. If
  16713. // not, save the attribute.
  16714. //
  16715. if (pszCurrent == pszStart)
  16716. {
  16717. fEmptyString = TRUE;
  16718. #ifdef EXTRA_PARSING_SPEW
  16719. DPFX(DPFPREP, 9, "Ignoring extra whitespace at offset 0x%p.",
  16720. (pszCurrent - pszString));
  16721. #endif // EXTRA_PARSING_SPEW
  16722. }
  16723. else
  16724. {
  16725. //
  16726. // End of the attribute. Force an empty value string.
  16727. //
  16728. apszAttributeNames[(*pdwNumAttributes)] = pszStart;
  16729. apszAttributeValues[(*pdwNumAttributes)] = pszCurrent;
  16730. DPFX(DPFPREP, 7, "Found attribute \"%hs\" with no value string.",
  16731. apszAttributeNames[(*pdwNumAttributes)]);
  16732. }
  16733. }
  16734. //
  16735. // Update the pointer for the start of the next attribute.
  16736. //
  16737. pszStart = pszCurrent + 1;
  16738. //
  16739. // Move to next attribute storage location, if this is not
  16740. // an empty string. If that was the last storage slot,
  16741. // we're done here.
  16742. //
  16743. if (fEmptyString)
  16744. {
  16745. fEmptyString = FALSE;
  16746. }
  16747. else
  16748. {
  16749. (*pdwNumAttributes)++;
  16750. if ((*pdwNumAttributes) >= dwMaxNumAttributes)
  16751. {
  16752. DPFX(DPFPREP, 1, "Maximum number of attributes reached, discontinuing attribute parsing.");
  16753. pszCurrent = pszEndOfString;
  16754. }
  16755. }
  16756. }
  16757. break;
  16758. }
  16759. case '"':
  16760. {
  16761. //
  16762. // Make sure it's not an escaped quote character.
  16763. //
  16764. if ((pszCurrent == pszString) || (*(pszCurrent - 1) != '\\'))
  16765. {
  16766. //
  16767. // Toggle the quote state.
  16768. //
  16769. if (fInQuotes)
  16770. {
  16771. //
  16772. // Force the string to terminate here so we skip the
  16773. // trailing quote character.
  16774. //
  16775. fInQuotes = FALSE;
  16776. (*pszCurrent) = '\0';
  16777. }
  16778. else
  16779. {
  16780. fInQuotes = TRUE;
  16781. //
  16782. // This should be the start of a (value) string. Skip
  16783. // this quote character.
  16784. //
  16785. if (pszCurrent == pszStart)
  16786. {
  16787. pszStart++;
  16788. }
  16789. else
  16790. {
  16791. DPFX(DPFPREP, 1, "Found starting quote that wasn't at the beginning of the string! Continuing.");
  16792. }
  16793. }
  16794. }
  16795. else
  16796. {
  16797. //
  16798. // It's an escaped quote character.
  16799. //
  16800. }
  16801. break;
  16802. }
  16803. default:
  16804. {
  16805. //
  16806. // Ignore the character and move on.
  16807. //
  16808. break;
  16809. }
  16810. }
  16811. //
  16812. // Move to next character.
  16813. //
  16814. pszCurrent++;
  16815. }
  16816. #ifdef EXTRA_PARSING_SPEW
  16817. DPFX(DPFPREP, 8, "(0x%p) Leave (found %u items)", this, (*pdwNumAttributes));
  16818. #endif // EXTRA_PARSING_SPEW
  16819. } // CNATHelpUPnP::ParseXMLNamespaceAttributes
  16820. #undef DPF_MODNAME
  16821. #define DPF_MODNAME "CNATHelpUPnP::MatchesXMLStringWithoutNamespace"
  16822. //=============================================================================
  16823. // CNATHelpUPnP::MatchesXMLStringWithoutNamespace
  16824. //-----------------------------------------------------------------------------
  16825. //
  16826. // Description: Determines whether the szCompareString matches szMatchString
  16827. // when all namespace prefixes in szCompareString are ignored.
  16828. // TRUE is returned if they do match, FALSE if not.
  16829. //
  16830. // Arguments:
  16831. // char * szCompareString - String that may contain namespace
  16832. // prefixes to be ignored.
  16833. // char * szMatchString - Shortened string without any
  16834. // namespace prefixes to be matched.
  16835. // PARSEXML_STACKENTRY * aElementStack - Array of nested elements whose
  16836. // attributes may define XML namespace
  16837. // aliases.
  16838. // PARSEXML_SUBELEMENT * pSubElement - Optional subelement entry with
  16839. // additional attributes to check.
  16840. // DWORD dwElementStackDepth - Number of entries in previous array.
  16841. //
  16842. // Returns: BOOL
  16843. //=============================================================================
  16844. BOOL CNATHelpUPnP::MatchesXMLStringWithoutNamespace(const char * const szCompareString,
  16845. const char * const szMatchString,
  16846. const PARSEXML_STACKENTRY * const aElementStack,
  16847. const PARSEXML_SUBELEMENT * const pSubElement,
  16848. const DWORD dwElementStackDepth)
  16849. {
  16850. BOOL fResult;
  16851. char * pszCompareStringNoNamespace;
  16852. //
  16853. // First, do a straight-forward string comparison.
  16854. //
  16855. if (_stricmp(szCompareString, szMatchString) == 0)
  16856. {
  16857. DPFX(DPFPREP, 7, "\"%hs\" exactly matches the short string.",
  16858. szCompareString);
  16859. fResult = TRUE;
  16860. }
  16861. else
  16862. {
  16863. //
  16864. // Skip past any namespace prefixes.
  16865. //
  16866. pszCompareStringNoNamespace = this->GetStringWithoutNamespacePrefix(szCompareString,
  16867. aElementStack,
  16868. pSubElement,
  16869. dwElementStackDepth);
  16870. DNASSERT((pszCompareStringNoNamespace >= szCompareString) && (pszCompareStringNoNamespace <= (szCompareString + strlen(szCompareString))));
  16871. //
  16872. // Now try comparing again, if we found any prefixes.
  16873. //
  16874. if (pszCompareStringNoNamespace > szCompareString)
  16875. {
  16876. if (_stricmp(pszCompareStringNoNamespace, szMatchString) == 0)
  16877. {
  16878. DPFX(DPFPREP, 7, "\"%hs\" matches the short string \"%hs\" starting at offset 0x%p.",
  16879. szCompareString, szMatchString,
  16880. (pszCompareStringNoNamespace - szCompareString));
  16881. fResult = TRUE;
  16882. }
  16883. else
  16884. {
  16885. DPFX(DPFPREP, 8, "\"%hs\" does not match the short string \"%hs\" (starting at offset 0x%p).",
  16886. szCompareString, szMatchString,
  16887. (pszCompareStringNoNamespace - szCompareString));
  16888. fResult = FALSE;
  16889. }
  16890. }
  16891. else
  16892. {
  16893. DPFX(DPFPREP, 8, "\"%hs\" does not have any namespace prefixes and does not match \"%hs\".",
  16894. szCompareString, szMatchString);
  16895. fResult = FALSE;
  16896. }
  16897. }
  16898. return fResult;
  16899. } // CNATHelpUPnP::MatchesXMLStringWithoutNamespace
  16900. #undef DPF_MODNAME
  16901. #define DPF_MODNAME "CNATHelpUPnP::GetStringWithoutNamespacePrefix"
  16902. //=============================================================================
  16903. // CNATHelpUPnP::GetStringWithoutNamespacePrefix
  16904. //-----------------------------------------------------------------------------
  16905. //
  16906. // Description: Returns a pointer to the first part of the string after any
  16907. // prefixes found. This will be the start of the string if none
  16908. // are found.
  16909. //
  16910. // Arguments:
  16911. // char * szString - String that may contain namespace
  16912. // prefixes to be skipped.
  16913. // PARSEXML_STACKENTRY * aElementStack - Array of nested elements whose
  16914. // attributes may define XML namespace
  16915. // aliases.
  16916. // PARSEXML_SUBELEMENT * pSubElement - Optional subelement entry with
  16917. // additional attributes to check.
  16918. // DWORD dwElementStackDepth - Number of entries in previous array.
  16919. //
  16920. // Returns: char *
  16921. //=============================================================================
  16922. char * CNATHelpUPnP::GetStringWithoutNamespacePrefix(const char * const szString,
  16923. const PARSEXML_STACKENTRY * const aElementStack,
  16924. const PARSEXML_SUBELEMENT * const pSubElement,
  16925. const DWORD dwElementStackDepth)
  16926. {
  16927. char * pszResult;
  16928. UINT uiXMLNSPrefixLength;
  16929. DWORD dwAttribute;
  16930. UINT uiNamespaceNameLength;
  16931. UINT uiNamespaceValueLength;
  16932. DWORD dwStackDepth;
  16933. //
  16934. // Store the prefix value since we use it frequently in this function.
  16935. //
  16936. uiXMLNSPrefixLength = strlen(XML_NAMESPACEDEFINITIONPREFIX);
  16937. //
  16938. // Search the stack for an XML namespace definition that matches. Start at
  16939. // the bottom and work up, since later definitions take precendence over
  16940. // earlier ones.
  16941. //
  16942. // In fact, if this is a subelement, there are attributes associated with
  16943. // this item that need to be checked, too. Do that first.
  16944. //
  16945. if (pSubElement != NULL)
  16946. {
  16947. //
  16948. // Search each attribute
  16949. //
  16950. for(dwAttribute = 0; dwAttribute < pSubElement->dwNumAttributes; dwAttribute++)
  16951. {
  16952. //
  16953. // Work with this attribute if it's a valid XML namespace
  16954. // definition. It needs to start with the prefix, plus have one
  16955. // extra character for the actual name.
  16956. //
  16957. uiNamespaceNameLength = strlen(pSubElement->apszAttributeNames[dwAttribute]);
  16958. if ((uiNamespaceNameLength >= (uiXMLNSPrefixLength + 1)) &&
  16959. (_strnicmp(pSubElement->apszAttributeNames[dwAttribute], XML_NAMESPACEDEFINITIONPREFIX, uiXMLNSPrefixLength) == 0))
  16960. {
  16961. uiNamespaceNameLength -= uiXMLNSPrefixLength;
  16962. //
  16963. // Only work with the item's value if it's valid.
  16964. //
  16965. uiNamespaceValueLength = strlen(pSubElement->apszAttributeValues[dwAttribute]);
  16966. if (uiNamespaceValueLength > 0)
  16967. {
  16968. //
  16969. // Okay, here's an item. See if the name prefixes the
  16970. // passed in string.
  16971. //
  16972. if (_strnicmp(szString, (pSubElement->apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength), uiNamespaceNameLength) == 0)
  16973. {
  16974. DPFX(DPFPREP, 8, "\"%hs\" begins with prefix \"%hs\" (subelement).",
  16975. szString,
  16976. (pSubElement->apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength));
  16977. //
  16978. // Cast to lose the const.
  16979. //
  16980. pszResult = ((char*) szString) + uiNamespaceNameLength;
  16981. //
  16982. // Skip the colon delimiter.
  16983. //
  16984. if ((*pszResult) == ':')
  16985. {
  16986. pszResult++;
  16987. }
  16988. else
  16989. {
  16990. DPFX(DPFPREP, 1, "\"%hs\" begins with prefix \"%hs\" but does not have colon separator (subelement)! Continuing.",
  16991. szString,
  16992. (pSubElement->apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength));
  16993. }
  16994. goto Exit;
  16995. }
  16996. //
  16997. // Namespace doesn't match
  16998. //
  16999. #ifdef EXTRA_PARSING_SPEW
  17000. DPFX(DPFPREP, 9, "\"%hs\" does not begin with prefix \"%hs\" (subelement).",
  17001. szString,
  17002. (pSubElement->apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength));
  17003. #endif // EXTRA_PARSING_SPEW
  17004. }
  17005. else
  17006. {
  17007. //
  17008. // Namespace value is bogus, ignore it.
  17009. //
  17010. DPFX(DPFPREP, 1, "Ignoring namespace definition \"%hs\" with empty value string (subelement).",
  17011. pSubElement->apszAttributeNames[dwAttribute]);
  17012. }
  17013. }
  17014. else
  17015. {
  17016. //
  17017. // Not an XML namespace definition.
  17018. //
  17019. #ifdef EXTRA_PARSING_SPEW
  17020. DPFX(DPFPREP, 9, "Attribute \"%hs\" is not a valid namespace definition (subelement).",
  17021. pSubElement->apszAttributeNames[dwAttribute]);
  17022. #endif // EXTRA_PARSING_SPEW
  17023. }
  17024. } // end for (each attribute)
  17025. }
  17026. else
  17027. {
  17028. //
  17029. // No subelement to check.
  17030. //
  17031. }
  17032. //
  17033. // Do the same thing for the all items above this one.
  17034. //
  17035. dwStackDepth = dwElementStackDepth;
  17036. while (dwStackDepth > 0)
  17037. {
  17038. dwStackDepth--;
  17039. //
  17040. // Search each attribute
  17041. //
  17042. for(dwAttribute = 0; dwAttribute < aElementStack[dwStackDepth].dwNumAttributes; dwAttribute++)
  17043. {
  17044. //
  17045. // Work with this attribute if it's a valid XML namespace
  17046. // definition. It needs to start with the prefix, plus have one
  17047. // extra character for the actual name.
  17048. //
  17049. uiNamespaceNameLength = strlen(aElementStack[dwStackDepth].apszAttributeNames[dwAttribute]);
  17050. if ((uiNamespaceNameLength >= (uiXMLNSPrefixLength + 1)) &&
  17051. (_strnicmp(aElementStack[dwStackDepth].apszAttributeNames[dwAttribute], XML_NAMESPACEDEFINITIONPREFIX, uiXMLNSPrefixLength) == 0))
  17052. {
  17053. uiNamespaceNameLength -= uiXMLNSPrefixLength;
  17054. //
  17055. // Only work with the item's value if it's valid.
  17056. //
  17057. uiNamespaceValueLength = strlen(aElementStack[dwStackDepth].apszAttributeValues[dwAttribute]);
  17058. if (uiNamespaceValueLength > 0)
  17059. {
  17060. //
  17061. // Okay, here's an item. See if the value prefixes the
  17062. // passed in string.
  17063. //
  17064. if (_strnicmp(szString, (aElementStack[dwStackDepth].apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength), uiNamespaceNameLength) == 0)
  17065. {
  17066. DPFX(DPFPREP, 8, "\"%hs\" begins with prefix \"%hs\" (stack depth %u).",
  17067. szString,
  17068. (aElementStack[dwStackDepth].apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength),
  17069. dwStackDepth);
  17070. //
  17071. // Cast to lose the const.
  17072. //
  17073. pszResult = ((char*) szString) + uiNamespaceNameLength;
  17074. //
  17075. // Skip the colon delimiter.
  17076. //
  17077. if ((*pszResult) == ':')
  17078. {
  17079. pszResult++;
  17080. }
  17081. else
  17082. {
  17083. DPFX(DPFPREP, 1, "\"%hs\" begins with prefix \"%hs\" but does not have colon separator (stack depth %u)! Continuing.",
  17084. szString,
  17085. (aElementStack[dwStackDepth].apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength),
  17086. dwStackDepth);
  17087. }
  17088. goto Exit;
  17089. }
  17090. //
  17091. // Namespace doesn't match
  17092. //
  17093. #ifdef EXTRA_PARSING_SPEW
  17094. DPFX(DPFPREP, 9, "\"%hs\" does not begin with prefix \"%hs\" (stack depth %u).",
  17095. szString,
  17096. (aElementStack[dwStackDepth].apszAttributeNames[dwAttribute] + uiXMLNSPrefixLength),
  17097. dwStackDepth);
  17098. #endif // EXTRA_PARSING_SPEW
  17099. }
  17100. else
  17101. {
  17102. //
  17103. // Namespace value is bogus, ignore it.
  17104. //
  17105. DPFX(DPFPREP, 1, "Ignoring namespace definition \"%hs\" with empty value string (stack depth %u).",
  17106. aElementStack[dwStackDepth].apszAttributeNames[dwAttribute],
  17107. dwStackDepth);
  17108. }
  17109. }
  17110. else
  17111. {
  17112. //
  17113. // Not an XML namespace definition.
  17114. //
  17115. #ifdef EXTRA_PARSING_SPEW
  17116. DPFX(DPFPREP, 9, "Attribute \"%hs\" is not a valid namespace definition (stack depth %u).",
  17117. aElementStack[dwStackDepth].apszAttributeNames[dwAttribute],
  17118. dwStackDepth);
  17119. #endif // EXTRA_PARSING_SPEW
  17120. }
  17121. } // end for (each attribute)
  17122. }
  17123. //
  17124. // If we're here, it didn't match, even with namespace expansion.
  17125. //
  17126. DPFX(DPFPREP, 8, "\"%hs\" does not contain any namespace prefixes.",
  17127. szString);
  17128. //
  17129. // Cast to lose the const.
  17130. //
  17131. pszResult = (char*) szString;
  17132. Exit:
  17133. return pszResult;
  17134. } // CNATHelpUPnP::GetStringWithoutNamespacePrefix
  17135. #undef DPF_MODNAME
  17136. #define DPF_MODNAME "CNATHelpUPnP::GetNextChunk"
  17137. //=============================================================================
  17138. // CNATHelpUPnP::GetNextChunk
  17139. //-----------------------------------------------------------------------------
  17140. //
  17141. // Description: Attempts to parse the next chunk out of a message buffer.
  17142. // If invalid data is encountered, this function returns FALSE.
  17143. // If not enough data has been received to complete the chunk,
  17144. // TRUE is returned, but NULL is returned in ppszChunkData.
  17145. // Otherwise, a pointer to the start of the chunk data is placed
  17146. // in ppszChunkData, the size of the chunk is placed in
  17147. // pdwChunkSize, ppszBufferRemaining is set to the start of the
  17148. // next potential chunk, and pdwBufferSizeRemaining is set to the
  17149. // amount of the buffer remaining starting at the returned
  17150. // ppszBufferRemaining value.
  17151. //
  17152. // Note that the chunk size may be zero, in which case the
  17153. // pointer in ppszChunkData & ppszBufferRemaining will be non-
  17154. // NULL, but useless.
  17155. //
  17156. // Arguments:
  17157. // char * pszBuffer - Pointer to string containing the message
  17158. // received so far.
  17159. // DWORD dwBufferSize - Size of the message buffer.
  17160. // char ** ppszChunkData - Place to store pointer to chunk data, or
  17161. // NULL if full chunk is not available.
  17162. // DWORD * pdwChunkSize - Place to store size of chunk.
  17163. // char ** ppszBufferRemaining - Place to store pointer to end of chunk
  17164. // if full chunk is available.
  17165. // DWORD * pdwBufferSizeRemaining - Place to store size of buffer remaining
  17166. // after returned chunk.
  17167. //
  17168. // Returns: None.
  17169. //=============================================================================
  17170. BOOL CNATHelpUPnP::GetNextChunk(char * const pszBuffer,
  17171. const DWORD dwBufferSize,
  17172. char ** const ppszChunkData,
  17173. DWORD * const pdwChunkSize,
  17174. char ** const ppszBufferRemaining,
  17175. DWORD * const pdwBufferSizeRemaining)
  17176. {
  17177. BOOL fReturn = TRUE;
  17178. char * pszCurrent;
  17179. char * pszEndOfBuffer;
  17180. BOOL fFoundChunkSizeEnd;
  17181. BOOL fExtensions;
  17182. BOOL fInQuotedString;
  17183. DPFX(DPFPREP, 8, "(0x%p) Parameters: (0x%p, %u, 0x%p, 0x%p, 0x%p, 0x%p)",
  17184. this, pszBuffer, dwBufferSize, ppszChunkData, pdwChunkSize,
  17185. ppszBufferRemaining, pdwBufferSizeRemaining);
  17186. pszCurrent = pszBuffer;
  17187. pszEndOfBuffer = pszCurrent + dwBufferSize;
  17188. fFoundChunkSizeEnd = FALSE;
  17189. fExtensions = FALSE;
  17190. fInQuotedString = FALSE;
  17191. (*ppszChunkData) = NULL;
  17192. (*pdwChunkSize) = 0;
  17193. //
  17194. // The buffer must be large enough to hold 1 hex digit, CR LF chunk size
  17195. // terminator, and CR LF chunk trailer.
  17196. //
  17197. if (dwBufferSize < 5)
  17198. {
  17199. DPFX(DPFPREP, 3, "Buffer is not large enough (%u bytes) to hold one valid chunk.",
  17200. dwBufferSize);
  17201. goto Exit;
  17202. }
  17203. //
  17204. // Be paranoid to make sure we're not going to having wrap problems.
  17205. //
  17206. if (pszEndOfBuffer < pszCurrent)
  17207. {
  17208. DPFX(DPFPREP, 0, "Buffer pointer 0x%p cannot have size %u!",
  17209. pszBuffer, dwBufferSize);
  17210. goto Failure;
  17211. }
  17212. while (pszCurrent < pszEndOfBuffer)
  17213. {
  17214. //
  17215. // Make sure we have a valid hex chunk size string and convert it
  17216. // as we go.
  17217. //
  17218. if (((*pszCurrent) >= '0') && ((*pszCurrent) <= '9'))
  17219. {
  17220. (*pdwChunkSize) = ((*pdwChunkSize) * 16) + ((*pszCurrent) - '0');
  17221. }
  17222. else if (((*pszCurrent) >= 'a') && ((*pszCurrent) <= 'f'))
  17223. {
  17224. (*pdwChunkSize) = ((*pdwChunkSize) * 16) + ((*pszCurrent) - 'a' + 10);
  17225. }
  17226. else if (((*pszCurrent) >= 'A') && ((*pszCurrent) <= 'F'))
  17227. {
  17228. (*pdwChunkSize) = ((*pdwChunkSize) * 16) + ((*pszCurrent) - 'A' + 10);
  17229. }
  17230. else if ((*pszCurrent) == '\r')
  17231. {
  17232. //
  17233. // This should be the end of the chunk size string.
  17234. //
  17235. fFoundChunkSizeEnd = TRUE;
  17236. break;
  17237. }
  17238. else if ((*pszCurrent) == ';')
  17239. {
  17240. //
  17241. // This should be the end of the chunk size string, and the
  17242. // beginning of extensions. Loop until we find the true end.
  17243. //
  17244. while ((*pszCurrent) != '\r')
  17245. {
  17246. pszCurrent++;
  17247. if (pszCurrent >= pszEndOfBuffer)
  17248. {
  17249. DPFX(DPFPREP, 5, "Buffer stops in middle of chunk extension.");
  17250. goto Exit;
  17251. }
  17252. //
  17253. // We do not support quoted extension value strings that
  17254. // theoretically contain CR characters...
  17255. //
  17256. }
  17257. fFoundChunkSizeEnd = TRUE;
  17258. break;
  17259. }
  17260. else
  17261. {
  17262. //
  17263. // There's a bogus character. This can't be a validly encoded
  17264. // message.
  17265. //
  17266. DPFX(DPFPREP, 1, "Chunk size string contains invalid character 0x%x at offset %u!",
  17267. (*pszCurrent), (DWORD_PTR) (pszCurrent - pszBuffer));
  17268. goto Failure;
  17269. }
  17270. //
  17271. // Validate the chunk size we have so far.
  17272. //
  17273. if ((*pdwChunkSize) > MAX_RECEIVE_BUFFER_SIZE)
  17274. {
  17275. DPFX(DPFPREP, 1, "Chunk size %u is too large!",
  17276. (*pdwChunkSize));
  17277. goto Failure;
  17278. }
  17279. pszCurrent++;
  17280. }
  17281. //
  17282. // If we're here, see if we found the end of the chunk size string.
  17283. // Make sure we've received enough data, and then validate that the CR
  17284. // stopping character is followed by the LF character.
  17285. //
  17286. if (fFoundChunkSizeEnd)
  17287. {
  17288. pszCurrent++;
  17289. if (pszCurrent < pszEndOfBuffer)
  17290. {
  17291. if ((*pszCurrent) != '\n')
  17292. {
  17293. DPFX(DPFPREP, 1, "Chunk size string did not end with CRLF sequence (offset %u)!",
  17294. (DWORD_PTR) (pszCurrent - pszBuffer));
  17295. goto Failure;
  17296. }
  17297. //
  17298. // Otherwise we got a complete chunk size string.
  17299. //
  17300. pszCurrent++;
  17301. //
  17302. // If we have received all of the chunk already, make sure we have
  17303. // the trailing CR LF sequence before returning the pointer to the
  17304. // caller.
  17305. if (((*pdwChunkSize) + 2) <= ((DWORD_PTR) (pszEndOfBuffer - pszCurrent)))
  17306. {
  17307. if ((*(pszCurrent + (*pdwChunkSize)) != '\r') ||
  17308. (*(pszCurrent + (*pdwChunkSize) + 1) != '\n'))
  17309. {
  17310. DPFX(DPFPREP, 1, "Chunk data did not end with CRLF sequence (offset %u)!",
  17311. (DWORD_PTR) (pszCurrent - pszBuffer + (*pdwChunkSize)));
  17312. goto Failure;
  17313. }
  17314. //
  17315. // Return the data pointers to the caller. In the case of the
  17316. // zero size terminating chunk, the ppszChunkData pointer will
  17317. // actually be useless, but the caller should recognize that.
  17318. //
  17319. (*ppszChunkData) = pszCurrent;
  17320. (*ppszBufferRemaining) = pszCurrent + ((*pdwChunkSize) + 2);
  17321. (*pdwBufferSizeRemaining) = (DWORD) ((DWORD_PTR) (pszEndOfBuffer - (*ppszBufferRemaining)));
  17322. }
  17323. }
  17324. }
  17325. //
  17326. // If we're here, we didn't encounter invalid data.
  17327. //
  17328. Exit:
  17329. DPFX(DPFPREP, 8, "(0x%p) Returning: [%i]", this, fReturn);
  17330. return fReturn;
  17331. Failure:
  17332. fReturn = FALSE;
  17333. goto Exit;
  17334. } // CNATHelpUPnP::GetNextChunk
  17335. #undef DPF_MODNAME
  17336. #define DPF_MODNAME "CNATHelpUPnP::ParseXMLCallback_DescriptionResponse"
  17337. //=============================================================================
  17338. // CNATHelpUPnP::ParseXMLCallback_DescriptionResponse
  17339. //-----------------------------------------------------------------------------
  17340. //
  17341. // Description: Handles a completed parsed element in the description response
  17342. // XML.
  17343. //
  17344. // Arguments:
  17345. // PARSEXML_ELEMENT * pParseElement - Pointer to element which was found.
  17346. // PVOID pvContext - Pointer to parsing context.
  17347. // PARSEXML_STACKENTRY * aElementStack - Array of parent elements containing
  17348. // the completed element.
  17349. // BOOL * pfContinueParsing - Pointer to BOOL that should be set
  17350. // to FALSE if the calling function
  17351. // should stop parsing the XML.
  17352. //
  17353. // Returns: HRESULT
  17354. // DPNH_OK - Description response was handled successfully.
  17355. // DPNHERR_GENERIC - An error occurred.
  17356. //=============================================================================
  17357. HRESULT CNATHelpUPnP::ParseXMLCallback_DescriptionResponse(PARSEXML_ELEMENT * const pParseElement,
  17358. PVOID pvContext,
  17359. PARSEXML_STACKENTRY * const aElementStack,
  17360. BOOL * const pfContinueParsing)
  17361. {
  17362. HRESULT hr = DPNH_OK;
  17363. CUPnPDevice * pUPnPDevice;
  17364. char * pszServiceType = NULL;
  17365. char * pszServiceId = NULL;
  17366. char * pszControlURL = NULL;
  17367. DWORD dwSubElement;
  17368. PARSEXML_SUBELEMENT * pSubElement;
  17369. SOCKADDR_IN saddrinControl;
  17370. SOCKADDR_IN * psaddrinHost;
  17371. char * pszRelativePath;
  17372. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%p)",
  17373. this, pParseElement, pvContext, aElementStack, pfContinueParsing);
  17374. pUPnPDevice = (CUPnPDevice *) pvContext;
  17375. DNASSERT(pUPnPDevice != NULL);
  17376. DNASSERT(pParseElement->papszElementStack == (char **) (&c_szElementStack_service));
  17377. //
  17378. // Look for the subelements we want.
  17379. //
  17380. for(dwSubElement = 0; dwSubElement < pParseElement->dwNumSubElements; dwSubElement++)
  17381. {
  17382. pSubElement = &pParseElement->paSubElements[dwSubElement];
  17383. if (_stricmp(pSubElement->pszNameFound, XML_DEVICEDESCRIPTION_SERVICETYPE) == 0)
  17384. {
  17385. if (pszServiceType == NULL)
  17386. {
  17387. pszServiceType = pSubElement->pszValueFound;
  17388. }
  17389. else
  17390. {
  17391. DPFX(DPFPREP, 7, "Ignoring duplicate \"" XML_DEVICEDESCRIPTION_SERVICETYPE "\" subelement (value = \"%hs\").",
  17392. pSubElement->pszValueFound);
  17393. }
  17394. }
  17395. else if (_stricmp(pSubElement->pszNameFound, XML_DEVICEDESCRIPTION_SERVICEID) == 0)
  17396. {
  17397. if (pszServiceId == NULL)
  17398. {
  17399. pszServiceId = pSubElement->pszValueFound;
  17400. }
  17401. else
  17402. {
  17403. DPFX(DPFPREP, 7, "Ignoring duplicate \"" XML_DEVICEDESCRIPTION_SERVICEID "\" subelement (value = \"%hs\").",
  17404. pSubElement->pszValueFound);
  17405. }
  17406. }
  17407. else if (_stricmp(pSubElement->pszNameFound, XML_DEVICEDESCRIPTION_CONTROLURL) == 0)
  17408. {
  17409. if (pszControlURL == NULL)
  17410. {
  17411. pszControlURL = pSubElement->pszValueFound;
  17412. }
  17413. else
  17414. {
  17415. DPFX(DPFPREP, 7, "Ignoring duplicate \"" XML_DEVICEDESCRIPTION_CONTROLURL "\" subelement (value = \"%hs\").",
  17416. pSubElement->pszValueFound);
  17417. }
  17418. }
  17419. else
  17420. {
  17421. DPFX(DPFPREP, 7, "Ignoring subelement \"%hs\" (value = \"%hs\").",
  17422. pSubElement->pszNameFound, pSubElement->pszValueFound);
  17423. }
  17424. }
  17425. //
  17426. // If one of those elements was not specified, then this element is not
  17427. // helpful.
  17428. //
  17429. if ((pszServiceType == NULL) || (pszServiceId == NULL) || (pszControlURL == NULL))
  17430. {
  17431. DPFX(DPFPREP, 1, "Couldn't find either \"" XML_DEVICEDESCRIPTION_SERVICETYPE "\", \"" XML_DEVICEDESCRIPTION_SERVICEID "\", or \"" XML_DEVICEDESCRIPTION_CONTROLURL "\" in XML description, ignoring element.");
  17432. goto Exit;
  17433. }
  17434. //
  17435. // If the service type is not one of the ones we want, ignore the element.
  17436. //
  17437. if (_stricmp(pszServiceType, URI_SERVICE_WANIPCONNECTION_A) == 0)
  17438. {
  17439. DPFX(DPFPREP, 7, "Found \"" URI_SERVICE_WANIPCONNECTION_A "\".");
  17440. DNASSERT(! pUPnPDevice->IsWANPPPConnection());
  17441. }
  17442. else if (_stricmp(pszServiceType, URI_SERVICE_WANPPPCONNECTION_A) == 0)
  17443. {
  17444. DPFX(DPFPREP, 7, "Found \"" URI_SERVICE_WANPPPCONNECTION_A "\".");
  17445. pUPnPDevice->NoteWANPPPConnection();
  17446. }
  17447. else
  17448. {
  17449. DPFX(DPFPREP, 1, "Ignoring unknown service type \"%hs\".", pszServiceType);
  17450. goto Exit;
  17451. }
  17452. pParseElement->fFoundMatchingElement = TRUE;
  17453. (*pfContinueParsing) = FALSE;
  17454. //
  17455. // Validate and store the service control URL.
  17456. //
  17457. hr = this->GetAddressFromURL(pszControlURL,
  17458. &saddrinControl,
  17459. &pszRelativePath);
  17460. if (hr != DPNH_OK)
  17461. {
  17462. psaddrinHost = pUPnPDevice->GetHostAddress();
  17463. DPFX(DPFPREP, 1, "No control address in URL, using host address %u.%u.%u.%u:%u and full URL as relative path (\"%hs\").",
  17464. psaddrinHost->sin_addr.S_un.S_un_b.s_b1,
  17465. psaddrinHost->sin_addr.S_un.S_un_b.s_b2,
  17466. psaddrinHost->sin_addr.S_un.S_un_b.s_b3,
  17467. psaddrinHost->sin_addr.S_un.S_un_b.s_b4,
  17468. NTOHS(psaddrinHost->sin_port),
  17469. pszControlURL);
  17470. memcpy(&saddrinControl, psaddrinHost, sizeof(SOCKADDR_IN));
  17471. pszRelativePath = pszControlURL;
  17472. }
  17473. else
  17474. {
  17475. #if 0
  17476. //
  17477. // Ensure that the address to use is local. It doesn't make sense that
  17478. // in order to make mappings for our private network we would need to
  17479. // contact something outside.
  17480. //
  17481. if (! this->IsAddressLocal(pUPnPDevice->GetOwningDevice(), &saddrinControl))
  17482. {
  17483. DPFX(DPFPREP, 1, "Control address designated (%u.%u.%u.%u:%u) is not local, ignoring message.",
  17484. saddrinControl.sin_addr.S_un.S_un_b.s_b1,
  17485. saddrinControl.sin_addr.S_un.S_un_b.s_b2,
  17486. saddrinControl.sin_addr.S_un.S_un_b.s_b3,
  17487. saddrinControl.sin_addr.S_un.S_un_b.s_b4,
  17488. NTOHS(saddrinControl.sin_port));
  17489. goto Exit;
  17490. }
  17491. #else
  17492. psaddrinHost = pUPnPDevice->GetHostAddress();
  17493. //
  17494. // Don't accept responses that refer to addresses other than the one
  17495. // that sent this response.
  17496. //
  17497. if (saddrinControl.sin_addr.S_un.S_addr != psaddrinHost->sin_addr.S_un.S_addr)
  17498. {
  17499. 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.",
  17500. saddrinControl.sin_addr.S_un.S_un_b.s_b1,
  17501. saddrinControl.sin_addr.S_un.S_un_b.s_b2,
  17502. saddrinControl.sin_addr.S_un.S_un_b.s_b3,
  17503. saddrinControl.sin_addr.S_un.S_un_b.s_b4,
  17504. NTOHS(saddrinControl.sin_port),
  17505. psaddrinHost->sin_addr.S_un.S_un_b.s_b1,
  17506. psaddrinHost->sin_addr.S_un.S_un_b.s_b2,
  17507. psaddrinHost->sin_addr.S_un.S_un_b.s_b3,
  17508. psaddrinHost->sin_addr.S_un.S_un_b.s_b4,
  17509. NTOHS(psaddrinHost->sin_port));
  17510. goto Exit;
  17511. }
  17512. #endif
  17513. //
  17514. // Don't accept responses that refer to ports in the reserved range
  17515. // (less than or equal to 1024) other than the standard HTTP port.
  17516. //
  17517. if ((NTOHS(saddrinControl.sin_port) <= MAX_RESERVED_PORT) &&
  17518. (saddrinControl.sin_port != HTONS(HTTP_PORT)))
  17519. {
  17520. DPFX(DPFPREP, 1, "Control address designated invalid port %u, ignoring message.",
  17521. NTOHS(saddrinControl.sin_port));
  17522. goto Exit;
  17523. }
  17524. }
  17525. pUPnPDevice->SetControlAddress(&saddrinControl);
  17526. //
  17527. // Save the service control URL.
  17528. //
  17529. hr = pUPnPDevice->SetServiceControlURL(pszRelativePath);
  17530. if (hr != DPNH_OK)
  17531. {
  17532. DPFX(DPFPREP, 0, "Couldn't store service control URL!");
  17533. goto Failure;
  17534. }
  17535. Exit:
  17536. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  17537. return hr;
  17538. Failure:
  17539. hr = DPNHERR_GENERIC;
  17540. goto Exit;
  17541. } // CNATHelpUPnP::ParseXMLCallback_DescriptionResponse
  17542. #undef DPF_MODNAME
  17543. #define DPF_MODNAME "CNATHelpUPnP::ParseXMLCallback_ControlResponse"
  17544. //=============================================================================
  17545. // CNATHelpUPnP::ParseXMLCallback_ControlResponse
  17546. //-----------------------------------------------------------------------------
  17547. //
  17548. // Description: Handles a completed parsed element in a control SOAP response.
  17549. //
  17550. // Arguments:
  17551. // PARSEXML_ELEMENT * pParseElement - Pointer to element which was found.
  17552. // PVOID pvContext - Pointer to parsing context.
  17553. // PARSEXML_STACKENTRY * aElementStack - Array of parent elements containing
  17554. // the completed element.
  17555. // BOOL * pfContinueParsing - Pointer to BOOL that should be set
  17556. // to FALSE if the calling function
  17557. // should stop parsing the XML.
  17558. //
  17559. // Returns: HRESULT
  17560. // DPNH_OK - Description response was handled successfully.
  17561. // DPNHERR_GENERIC - An error occurred.
  17562. //=============================================================================
  17563. HRESULT CNATHelpUPnP::ParseXMLCallback_ControlResponse(PARSEXML_ELEMENT * const pParseElement,
  17564. PVOID pvContext,
  17565. PARSEXML_STACKENTRY * const aElementStack,
  17566. BOOL * const pfContinueParsing)
  17567. {
  17568. HRESULT hr = DPNH_OK;
  17569. PCONTROLRESPONSEPARSECONTEXT pContext;
  17570. //char * pszReturn = NULL;
  17571. char * pszExternalIPAddress = NULL;
  17572. char * pszInternalPort = NULL;
  17573. char * pszInternalClient = NULL;
  17574. char * pszEnabled = NULL;
  17575. char * pszPortMappingDescription = NULL;
  17576. char * pszLeaseDuration = NULL;
  17577. char * pszErrorCode = NULL;
  17578. char * pszErrorDescription = NULL;
  17579. DWORD dwSubElement;
  17580. PARSEXML_SUBELEMENT * pSubElement;
  17581. int iErrorCode;
  17582. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, 0x%p, 0x%p, 0x%p)",
  17583. this, pParseElement, pvContext, aElementStack, pfContinueParsing);
  17584. pContext = (PCONTROLRESPONSEPARSECONTEXT) pvContext;
  17585. DNASSERT(pContext != NULL);
  17586. DNASSERT(pContext->pUPnPDevice != NULL);
  17587. //
  17588. // Look for the subelements we want.
  17589. //
  17590. for(dwSubElement = 0; dwSubElement < pParseElement->dwNumSubElements; dwSubElement++)
  17591. {
  17592. pSubElement = &pParseElement->paSubElements[dwSubElement];
  17593. /*
  17594. if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17595. ARG_CONTROL_RETURN_A,
  17596. aElementStack,
  17597. pSubElement,
  17598. pParseElement->dwElementStackDepth))
  17599. {
  17600. if (pszReturn == NULL)
  17601. {
  17602. pszReturn = pSubElement->pszValueFound;
  17603. }
  17604. else
  17605. {
  17606. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_CONTROL_RETURN_A "\" subelement (value = \"%hs\").",
  17607. pSubElement->pszValueFound);
  17608. }
  17609. }
  17610. */
  17611. if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17612. ARG_GETEXTERNALIPADDRESS_NEWEXTERNALIPADDRESS_A,
  17613. aElementStack,
  17614. pSubElement,
  17615. pParseElement->dwElementStackDepth))
  17616. {
  17617. if (pszExternalIPAddress == NULL)
  17618. {
  17619. pszExternalIPAddress = pSubElement->pszValueFound;
  17620. }
  17621. else
  17622. {
  17623. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETEXTERNALIPADDRESS_NEWEXTERNALIPADDRESS_A "\" subelement (value = \"%hs\").",
  17624. pSubElement->pszValueFound);
  17625. }
  17626. }
  17627. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17628. ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALPORT_A,
  17629. aElementStack,
  17630. pSubElement,
  17631. pParseElement->dwElementStackDepth))
  17632. {
  17633. if (pszInternalPort == NULL)
  17634. {
  17635. pszInternalPort = pSubElement->pszValueFound;
  17636. }
  17637. else
  17638. {
  17639. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALPORT_A "\" subelement (value = \"%hs\").",
  17640. pSubElement->pszValueFound);
  17641. }
  17642. }
  17643. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17644. ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALCLIENT_A,
  17645. aElementStack,
  17646. pSubElement,
  17647. pParseElement->dwElementStackDepth))
  17648. {
  17649. if (pszInternalClient == NULL)
  17650. {
  17651. pszInternalClient = pSubElement->pszValueFound;
  17652. }
  17653. else
  17654. {
  17655. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALCLIENT_A "\" subelement (value = \"%hs\").",
  17656. pSubElement->pszValueFound);
  17657. }
  17658. }
  17659. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17660. ARG_GETSPECIFICPORTMAPPINGENTRY_NEWENABLED_A,
  17661. aElementStack,
  17662. pSubElement,
  17663. pParseElement->dwElementStackDepth))
  17664. {
  17665. if (pszEnabled == NULL)
  17666. {
  17667. pszEnabled = pSubElement->pszValueFound;
  17668. }
  17669. else
  17670. {
  17671. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWENABLED_A "\" subelement (value = \"%hs\").",
  17672. pSubElement->pszValueFound);
  17673. }
  17674. }
  17675. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17676. ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPORTMAPPINGDESCRIPTION_A,
  17677. aElementStack,
  17678. pSubElement,
  17679. pParseElement->dwElementStackDepth))
  17680. {
  17681. if (pszPortMappingDescription == NULL)
  17682. {
  17683. pszPortMappingDescription = pSubElement->pszValueFound;
  17684. }
  17685. else
  17686. {
  17687. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWPORTMAPPINGDESCRIPTION_A "\" subelement (value = \"%hs\").",
  17688. pSubElement->pszValueFound);
  17689. }
  17690. }
  17691. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17692. ARG_GETSPECIFICPORTMAPPINGENTRY_NEWLEASEDURATION_A,
  17693. aElementStack,
  17694. pSubElement,
  17695. pParseElement->dwElementStackDepth))
  17696. {
  17697. if (pszLeaseDuration == NULL)
  17698. {
  17699. pszLeaseDuration = pSubElement->pszValueFound;
  17700. }
  17701. else
  17702. {
  17703. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWLEASEDURATION_A "\" subelement (value = \"%hs\").",
  17704. pSubElement->pszValueFound);
  17705. }
  17706. }
  17707. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17708. ARG_CONTROL_ERROR_ERRORCODE_A,
  17709. aElementStack,
  17710. pSubElement,
  17711. pParseElement->dwElementStackDepth))
  17712. {
  17713. if (pszErrorCode == NULL)
  17714. {
  17715. pszErrorCode = pSubElement->pszValueFound;
  17716. }
  17717. else
  17718. {
  17719. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_CONTROL_ERROR_ERRORCODE_A "\" subelement (value = \"%hs\").",
  17720. pSubElement->pszValueFound);
  17721. }
  17722. }
  17723. else if (this->MatchesXMLStringWithoutNamespace(pSubElement->pszNameFound,
  17724. ARG_CONTROL_ERROR_ERRORDESCRIPTION_A,
  17725. aElementStack,
  17726. pSubElement,
  17727. pParseElement->dwElementStackDepth))
  17728. {
  17729. if (pszErrorDescription == NULL)
  17730. {
  17731. pszErrorDescription = pSubElement->pszValueFound;
  17732. }
  17733. else
  17734. {
  17735. DPFX(DPFPREP, 7, "Ignoring duplicate \"" ARG_CONTROL_ERROR_ERRORDESCRIPTION_A "\" subelement (value = \"%hs\").",
  17736. pSubElement->pszValueFound);
  17737. }
  17738. }
  17739. else
  17740. {
  17741. DPFX(DPFPREP, 7, "Ignoring subelement \"%hs\" (value = \"%hs\").",
  17742. pSubElement->pszNameFound, pSubElement->pszValueFound);
  17743. }
  17744. } // end for (each sub element)
  17745. if (pContext->dwHTTPResponseCode == 200)
  17746. {
  17747. //
  17748. // The action succeeded.
  17749. //
  17750. switch (pContext->ControlResponseType)
  17751. {
  17752. /*
  17753. case CONTROLRESPONSETYPE_QUERYSTATEVARIABLE_EXTERNALIPADDRESS:
  17754. {
  17755. if (pszReturn == NULL)
  17756. {
  17757. DPFX(DPFPREP, 1, "Couldn't find \"" ARG_CONTROL_RETURN_A "\" in SOAP response, ignoring element.");
  17758. goto Exit;
  17759. }
  17760. DPFX(DPFPREP, 2, "QueryStateVariable returned \"%hs\".",
  17761. pszReturn);
  17762. / *
  17763. //
  17764. // Key off of the variable we were querying.
  17765. //
  17766. switch (pContext->ControlResponseType)
  17767. {
  17768. case CONTROLRESPONSETYPE_QUERYSTATEVARIABLE_EXTERNALIPADDRESS:
  17769. {
  17770. * /
  17771. pContext->pControlResponseInfo->dwExternalIPAddressV4 = this->m_pfninet_addr(pszReturn);
  17772. if (pContext->pControlResponseInfo->dwExternalIPAddressV4 == INADDR_NONE)
  17773. {
  17774. DPFX(DPFPREP, 1, "External IP address string \"%hs\" is invalid, using INADDR_ANY.");
  17775. pContext->pControlResponseInfo->dwExternalIPAddressV4 = INADDR_ANY;
  17776. }
  17777. / *
  17778. break;
  17779. }
  17780. }
  17781. * /
  17782. break;
  17783. }
  17784. */
  17785. case CONTROLRESPONSETYPE_GETEXTERNALIPADDRESS:
  17786. {
  17787. if (pszExternalIPAddress == NULL)
  17788. {
  17789. DPFX(DPFPREP, 1, "Couldn't find \"" ARG_GETEXTERNALIPADDRESS_NEWEXTERNALIPADDRESS_A "\" in SOAP response, ignoring element.");
  17790. goto Exit;
  17791. }
  17792. DPFX(DPFPREP, 2, "GetExternalIPAddress returned \"%hs\".",
  17793. pszExternalIPAddress);
  17794. pContext->pControlResponseInfo->dwExternalIPAddressV4 = this->m_pfninet_addr(pszExternalIPAddress);
  17795. if ((pContext->pControlResponseInfo->dwExternalIPAddressV4 == INADDR_NONE) ||
  17796. (IS_CLASSD_IPV4_ADDRESS(pContext->pControlResponseInfo->dwExternalIPAddressV4)))
  17797. {
  17798. DPFX(DPFPREP, 1, "External IP address string \"%hs\" is invalid, using INADDR_ANY.");
  17799. pContext->pControlResponseInfo->dwExternalIPAddressV4 = INADDR_ANY;
  17800. }
  17801. break;
  17802. }
  17803. case CONTROLRESPONSETYPE_ADDPORTMAPPING:
  17804. {
  17805. DPFX(DPFPREP, 2, "AddPortMapping got success response.");
  17806. break;
  17807. }
  17808. case CONTROLRESPONSETYPE_GETSPECIFICPORTMAPPINGENTRY:
  17809. {
  17810. /*
  17811. if ((pszInternalPort == NULL) ||
  17812. (pszInternalClient == NULL) ||
  17813. (pszEnabled == NULL) ||
  17814. (pszPortMappingDescription == NULL) ||
  17815. (pszLeaseDuration == NULL))
  17816. */
  17817. if (pszInternalClient == NULL)
  17818. {
  17819. DPFX(DPFPREP, 1, "Couldn't find \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALCLIENT_A "\" in SOAP response, ignoring element.");
  17820. goto Exit;
  17821. }
  17822. if (pszInternalPort == NULL)
  17823. {
  17824. DPFX(DPFPREP, 1, "Couldn't find \"" ARG_GETSPECIFICPORTMAPPINGENTRY_NEWINTERNALPORT_A "\" in SOAP response, assuming asymmetric mappings are not supported.");
  17825. pszInternalPort = "0";
  17826. }
  17827. DPFX(DPFPREP, 2, "GetPortMappingPrivateIP returned \"%hs:%hs\".",
  17828. pszInternalClient, pszInternalPort);
  17829. pContext->pControlResponseInfo->dwInternalClientV4 = this->m_pfninet_addr(pszInternalClient);
  17830. if (pContext->pControlResponseInfo->dwInternalClientV4 == INADDR_ANY)
  17831. {
  17832. DPFX(DPFPREP, 0, "Internal client address is INADDR_ANY!");
  17833. hr = DPNHERR_GENERIC;
  17834. goto Failure;
  17835. }
  17836. pContext->pControlResponseInfo->wInternalPort = HTONS((WORD) atoi(pszInternalPort));
  17837. break;
  17838. }
  17839. case CONTROLRESPONSETYPE_DELETEPORTMAPPING:
  17840. {
  17841. DPFX(DPFPREP, 2, "DeletePortMapping got success response.");
  17842. break;
  17843. }
  17844. default:
  17845. {
  17846. DNASSERT(FALSE);
  17847. hr = DPNHERR_GENERIC;
  17848. goto Failure;
  17849. break;
  17850. }
  17851. }
  17852. //
  17853. // The action completed successfully.
  17854. //
  17855. pContext->pControlResponseInfo->hrErrorCode = DPNH_OK;
  17856. }
  17857. else
  17858. {
  17859. //
  17860. // The action failed.
  17861. //
  17862. //
  17863. // See if we found an error description that we can print for
  17864. // informational purposes.
  17865. //
  17866. if ((pszErrorCode != NULL) && (pszErrorDescription != NULL))
  17867. {
  17868. iErrorCode = atoi(pszErrorCode);
  17869. switch (iErrorCode)
  17870. {
  17871. case UPNPERR_IGD_NOSUCHENTRYINARRAY:
  17872. {
  17873. DPFX(DPFPREP, 1, "Control action was rejected with NoSuchEntryInArray error %hs (description = \"%hs\").",
  17874. pszErrorCode, pszErrorDescription);
  17875. pContext->pControlResponseInfo->hrErrorCode = DPNHERR_NOMAPPING;
  17876. break;
  17877. }
  17878. case UPNPERR_IGD_CONFLICTINMAPPINGENTRY:
  17879. {
  17880. DPFX(DPFPREP, 1, "Control action was rejected with ConflictInMappingEntry error %hs (description = \"%hs\").",
  17881. pszErrorCode, pszErrorDescription);
  17882. pContext->pControlResponseInfo->hrErrorCode = DPNHERR_PORTUNAVAILABLE;
  17883. break;
  17884. }
  17885. case UPNPERR_IGD_SAMEPORTVALUESREQUIRED:
  17886. {
  17887. DPFX(DPFPREP, 1, "Control action was rejected with SamePortValuesRequired error %hs (description = \"%hs\").",
  17888. pszErrorCode, pszErrorDescription);
  17889. pContext->pControlResponseInfo->hrErrorCode = (HRESULT) UPNPERR_IGD_SAMEPORTVALUESREQUIRED;
  17890. break;
  17891. }
  17892. case UPNPERR_IGD_ONLYPERMANENTLEASESSUPPORTED:
  17893. {
  17894. DPFX(DPFPREP, 1, "Control action was rejected with OnlyPermanentLeasesSupported error %hs (description = \"%hs\").",
  17895. pszErrorCode, pszErrorDescription);
  17896. pContext->pControlResponseInfo->hrErrorCode = (HRESULT) UPNPERR_IGD_ONLYPERMANENTLEASESSUPPORTED;
  17897. break;
  17898. }
  17899. default:
  17900. {
  17901. DPFX(DPFPREP, 1, "Control action was rejected with unknown error \"%hs\", \"%hs\", assuming generic failure.",
  17902. pszErrorCode, pszErrorDescription);
  17903. pContext->pControlResponseInfo->hrErrorCode = DPNHERR_GENERIC;
  17904. break;
  17905. }
  17906. }
  17907. }
  17908. else
  17909. {
  17910. DPFX(DPFPREP, 1, "Couldn't find either \"" ARG_CONTROL_ERROR_ERRORCODE_A "\", or \"" ARG_CONTROL_ERROR_ERRORDESCRIPTION_A "\" in SOAP response, assuming generic failure.");
  17911. pContext->pControlResponseInfo->hrErrorCode = DPNHERR_GENERIC;
  17912. }
  17913. }
  17914. //
  17915. // If we got here, we got the information we needed.
  17916. //
  17917. pParseElement->fFoundMatchingElement = TRUE;
  17918. (*pfContinueParsing) = FALSE;
  17919. Exit:
  17920. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  17921. return hr;
  17922. Failure:
  17923. goto Exit;
  17924. } // CNATHelpUPnP::ParseXMLCallback_ControlResponse
  17925. #undef DPF_MODNAME
  17926. #define DPF_MODNAME "CNATHelpUPnP::ClearDevicesUPnPDevice"
  17927. //=============================================================================
  17928. // CNATHelpUPnP::ClearDevicesUPnPDevice
  17929. //-----------------------------------------------------------------------------
  17930. //
  17931. // Description: Forcefully simulates de-registration with a UPnP device
  17932. /// without actually going to the network. This clears all bind
  17933. // IDs, public addresses, and cached mappings for a given device's
  17934. // local or remote server, and should only be called after the
  17935. // server appears to have died.
  17936. //
  17937. // The object lock is assumed to be held.
  17938. //
  17939. // Arguments:
  17940. // CDevice * pDevice - Pointer to device whose UPnP device should be
  17941. // removed.
  17942. //
  17943. // Returns: None.
  17944. //=============================================================================
  17945. void CNATHelpUPnP::ClearDevicesUPnPDevice(CDevice * const pDevice)
  17946. {
  17947. CUPnPDevice * pUPnPDevice;
  17948. DNASSERT(pDevice != NULL);
  17949. pUPnPDevice = pDevice->GetUPnPDevice();
  17950. if (pUPnPDevice != NULL)
  17951. {
  17952. #ifdef DBG
  17953. DPFX(DPFPREP, 1, "Clearing device 0x%p's UPnP device (0x%p).",
  17954. pDevice, pUPnPDevice);
  17955. pDevice->IncrementUPnPDeviceFailures();
  17956. this->m_dwNumServerFailures++;
  17957. #endif // DBG
  17958. //
  17959. // Since there was a change in the network, go back to polling
  17960. // relatively quickly.
  17961. //
  17962. this->ResetNextPollInterval();
  17963. pUPnPDevice->ClearDeviceOwner();
  17964. DNASSERT(pUPnPDevice->m_blList.IsListMember(&this->m_blUPnPDevices));
  17965. pUPnPDevice->m_blList.RemoveFromList();
  17966. //
  17967. // Transfer list reference to our pointer, since GetUPnPDevice did not give
  17968. // us one.
  17969. //
  17970. if (pUPnPDevice->IsConnected())
  17971. {
  17972. DNASSERT(pUPnPDevice->GetControlSocket() != INVALID_SOCKET);
  17973. this->m_pfnshutdown(pUPnPDevice->GetControlSocket(), 0); // ignore error
  17974. this->m_pfnclosesocket(pUPnPDevice->GetControlSocket());
  17975. pUPnPDevice->SetControlSocket(INVALID_SOCKET);
  17976. }
  17977. else
  17978. {
  17979. DNASSERT(pUPnPDevice->GetControlSocket() == INVALID_SOCKET);
  17980. }
  17981. this->ClearAllUPnPRegisteredPorts(pDevice);
  17982. pUPnPDevice->ClearLocationURL();
  17983. pUPnPDevice->ClearUSN();
  17984. pUPnPDevice->ClearServiceControlURL();
  17985. pUPnPDevice->DestroyReceiveBuffer();
  17986. pUPnPDevice->RemoveAllCachedMappings();
  17987. pUPnPDevice->DecRef();
  17988. }
  17989. else
  17990. {
  17991. DPFX(DPFPREP, 1, "Can't clear device 0x%p's UPnP device, it doesn't exist.",
  17992. pDevice);
  17993. }
  17994. } // CNATHelpUPnP::ClearDevicesUPnPDevice
  17995. #undef DPF_MODNAME
  17996. #define DPF_MODNAME "CNATHelpUPnP::ClearAllUPnPRegisteredPorts"
  17997. //=============================================================================
  17998. // CNATHelpUPnP::ClearAllUPnPRegisteredPorts
  17999. //-----------------------------------------------------------------------------
  18000. //
  18001. // Description: Clears all bind IDs and public addresses for a given
  18002. // device's UPnP Internet gateway. This should only be called
  18003. // after the UPnP device dies.
  18004. //
  18005. // The object lock is assumed to be held.
  18006. //
  18007. // Arguments:
  18008. // CDevice * pDevice - Pointer to device whose ports should be unbound.
  18009. // BOOL fRemote - TRUE if clearing remote server, FALSE if clearing
  18010. // local server.
  18011. //
  18012. // Returns: None.
  18013. //=============================================================================
  18014. void CNATHelpUPnP::ClearAllUPnPRegisteredPorts(CDevice * const pDevice)
  18015. {
  18016. CBilink * pBilink;
  18017. CRegisteredPort * pRegisteredPort;
  18018. DNASSERT(pDevice != NULL);
  18019. pBilink = pDevice->m_blOwnedRegPorts.GetNext();
  18020. while (pBilink != &pDevice->m_blOwnedRegPorts)
  18021. {
  18022. DNASSERT(! pBilink->IsEmpty());
  18023. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilink);
  18024. if (pRegisteredPort->HasUPnPPublicAddresses())
  18025. {
  18026. if (! pRegisteredPort->IsRemovingUPnPLease())
  18027. {
  18028. DPFX(DPFPREP, 1, "Registered port 0x%p losing UPnP public address.",
  18029. pRegisteredPort);
  18030. //
  18031. // Let the user know the next time GetCaps is called.
  18032. //
  18033. this->m_dwFlags |= NATHELPUPNPOBJ_ADDRESSESCHANGED;
  18034. //
  18035. // Note that this means the crash recovery entry will be left
  18036. // in the registry for the next person to come along and clean
  18037. // up. That should be okay, since we should only be doing this
  18038. // if we had a problem talking to the UPnP device (either it
  18039. // went AWOL, or we lost the local network interface). In
  18040. // either case, we can't really clean it up now, so we have to
  18041. // leave it for someone else to do.
  18042. //
  18043. pRegisteredPort->DestroyUPnPPublicAddressesArray();
  18044. pRegisteredPort->NoteNotPermanentUPnPLease();
  18045. DNASSERT(this->m_dwNumLeases > 0);
  18046. this->m_dwNumLeases--;
  18047. DPFX(DPFPREP, 7, "UPnP lease for 0x%p cleared, total num leases = %u.",
  18048. pRegisteredPort, this->m_dwNumLeases);
  18049. }
  18050. else
  18051. {
  18052. DPFX(DPFPREP, 1, "Registered port 0x%p already has had UPnP public address removed, skipping.",
  18053. pRegisteredPort);
  18054. }
  18055. }
  18056. else
  18057. {
  18058. //
  18059. // Port no longer unavailable (if it had been).
  18060. //
  18061. pRegisteredPort->NoteNotUPnPPortUnavailable();
  18062. }
  18063. pBilink = pBilink->GetNext();
  18064. }
  18065. } // CNATHelpUPnP::ClearAllUPnPRegisteredPorts
  18066. #ifndef DPNBUILD_NOWINSOCK2
  18067. #undef DPF_MODNAME
  18068. #define DPF_MODNAME "CNATHelpUPnP::RequestLocalAddressListChangeNotification"
  18069. //=============================================================================
  18070. // CNATHelpUPnP::RequestLocalAddressListChangeNotification
  18071. //-----------------------------------------------------------------------------
  18072. //
  18073. // Description: Attempts to request asynchronous notification (via the
  18074. // user's alert event or I/O completion port) when the local
  18075. // address list changes.
  18076. //
  18077. // The object lock is assumed to be held.
  18078. //
  18079. // Arguments: None.
  18080. //
  18081. // Returns: HRESULT
  18082. // DPNH_OK - The notification request was successfully submitted.
  18083. // DPNHERR_GENERIC - An error occurred.
  18084. //=============================================================================
  18085. HRESULT CNATHelpUPnP::RequestLocalAddressListChangeNotification(void)
  18086. {
  18087. HRESULT hr;
  18088. DWORD dwTemp;
  18089. int iReturn;
  18090. DPFX(DPFPREP, 5, "(0x%p) Enter", this);
  18091. DNASSERT(! (this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1));
  18092. DNASSERT(this->m_sIoctls != INVALID_SOCKET);
  18093. DNASSERT(this->m_pfnWSAIoctl != NULL);
  18094. DNASSERT((this->m_hAlertEvent != NULL) || (this->m_hAlertIOCompletionPort != NULL));
  18095. DNASSERT(this->m_polAddressListChange != NULL);
  18096. do
  18097. {
  18098. iReturn = this->m_pfnWSAIoctl(this->m_sIoctls, // use the special Ioctl socket
  18099. SIO_ADDRESS_LIST_CHANGE, //
  18100. NULL, // no input data
  18101. 0, // no input data
  18102. NULL, // no output data
  18103. 0, // no output data
  18104. &dwTemp, // ignore bytes returned
  18105. this->m_polAddressListChange, // overlapped structure
  18106. NULL); // no completion routine
  18107. if (iReturn != 0)
  18108. {
  18109. dwTemp = this->m_pfnWSAGetLastError();
  18110. if (dwTemp != WSA_IO_PENDING)
  18111. {
  18112. DPFX(DPFPREP, 0, "Submitting address list change notification request failed (err = %u)!", dwTemp);
  18113. hr = DPNHERR_GENERIC;
  18114. goto Failure;
  18115. }
  18116. //
  18117. // Pending is what we want, we're set.
  18118. //
  18119. hr = DPNH_OK;
  18120. break;
  18121. }
  18122. //
  18123. // Address list changed right away?
  18124. //
  18125. DPFX(DPFPREP, 1, "Address list changed right away somehow, submitting again.");
  18126. }
  18127. while (TRUE);
  18128. Exit:
  18129. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%lx]", this, hr);
  18130. return hr;
  18131. Failure:
  18132. goto Exit;
  18133. } // CNATHelpUPnP::RequestLocalAddressListChangeNotification
  18134. #endif // ! DPNBUILD_NOWINSOCK2
  18135. #undef DPF_MODNAME
  18136. #define DPF_MODNAME "CNATHelpUPnP::CreateSocket"
  18137. //=============================================================================
  18138. // CNATHelpUPnP::CreateSocket
  18139. //-----------------------------------------------------------------------------
  18140. //
  18141. // Description: Creates a UPnP discovery socket bound to a new random port
  18142. // on the specified IP interface. Completely random (but non-
  18143. // reserved) port numbers are chosen first, but if those ports are
  18144. // in use, WinSock is allowed to choose. The port actually
  18145. // selected will be returned in psaddrinAddress.
  18146. //
  18147. // Arguments:
  18148. // SOCKADDR_IN * psaddrinAddress - Pointer to base address to use when
  18149. // binding. The port will be modified.
  18150. //
  18151. // Returns: SOCKET
  18152. //=============================================================================
  18153. SOCKET CNATHelpUPnP::CreateSocket(SOCKADDR_IN * const psaddrinAddress,
  18154. int iType,
  18155. int iProtocol)
  18156. {
  18157. SOCKET sTemp;
  18158. DWORD dwTry;
  18159. int iTemp;
  18160. BOOL fTemp;
  18161. ULONG ulEnable;
  18162. #ifdef DBG
  18163. DWORD dwError;
  18164. #endif // DBG
  18165. DPFX(DPFPREP, 5, "(0x%p) Parameters: (0x%p, %i, %i)",
  18166. this, psaddrinAddress, iType, iProtocol);
  18167. //
  18168. // Create the socket.
  18169. //
  18170. sTemp = this->m_pfnsocket(AF_INET, iType, iProtocol);
  18171. if (sTemp == INVALID_SOCKET)
  18172. {
  18173. #ifdef DBG
  18174. dwError = this->m_pfnWSAGetLastError();
  18175. DPFX(DPFPREP, 0, "Couldn't create datagram socket, error = %u!", dwError);
  18176. #endif // DBG
  18177. goto Failure;
  18178. }
  18179. //
  18180. // Try binding the socket to a completely random port a few times.
  18181. //
  18182. for(dwTry = 0; dwTry < MAX_NUM_RANDOM_PORT_TRIES; dwTry++)
  18183. {
  18184. //
  18185. // Pick a completely random port. For the moment, the value is stored
  18186. // in host byte order while we make sure it's not a reserved value.
  18187. //
  18188. do
  18189. {
  18190. psaddrinAddress->sin_port = (WORD) GetGlobalRand();
  18191. }
  18192. while ((psaddrinAddress->sin_port <= MAX_RESERVED_PORT) ||
  18193. (psaddrinAddress->sin_port == 1900) || // SSDP
  18194. (psaddrinAddress->sin_port == 2234) || // PAST
  18195. (psaddrinAddress->sin_port == 6073) || // DPNSVR
  18196. (psaddrinAddress->sin_port == 47624)); // DPLAYSVR
  18197. //
  18198. // Now try binding to the port (in network byte order).
  18199. //
  18200. psaddrinAddress->sin_port = HTONS(psaddrinAddress->sin_port);
  18201. if (this->m_pfnbind(sTemp, (SOCKADDR*) psaddrinAddress, sizeof(SOCKADDR_IN)) == 0)
  18202. {
  18203. //
  18204. // We successfully bound to the port.
  18205. //
  18206. break;
  18207. }
  18208. //
  18209. // Assume that the port is in use.
  18210. //
  18211. #ifdef DBG
  18212. dwError = this->m_pfnWSAGetLastError();
  18213. DPFX(DPFPREP, 2, "Couldn't bind to port %u (err = %u), continuing.",
  18214. NTOHS(psaddrinAddress->sin_port), dwError);
  18215. #endif // DBG
  18216. psaddrinAddress->sin_port = 0;
  18217. }
  18218. //
  18219. // If we ran out of completely random port attempts, just let WinSock
  18220. // choose it.
  18221. //
  18222. if (psaddrinAddress->sin_port == 0)
  18223. {
  18224. if (this->m_pfnbind(sTemp, (SOCKADDR*) psaddrinAddress, sizeof(SOCKADDR_IN)) != 0)
  18225. {
  18226. #ifdef DBG
  18227. dwError = this->m_pfnWSAGetLastError();
  18228. DPFX(DPFPREP, 0, "Failed binding to any port (err = %u)!",
  18229. dwError);
  18230. #endif // DBG
  18231. goto Failure;
  18232. }
  18233. //
  18234. // Find out what port WinSock chose.
  18235. //
  18236. iTemp = sizeof(SOCKADDR_IN);
  18237. if (this->m_pfngetsockname(sTemp,
  18238. (SOCKADDR *) psaddrinAddress,
  18239. &iTemp) != 0)
  18240. {
  18241. #ifdef DBG
  18242. dwError = this->m_pfnWSAGetLastError();
  18243. DPFX(DPFPREP, 0, "Couldn't get the socket's address, error = %u!",
  18244. dwError);
  18245. #endif // DBG
  18246. goto Failure;
  18247. }
  18248. DNASSERT(psaddrinAddress->sin_port != 0);
  18249. }
  18250. //
  18251. // Set the unicast TTL, if requested. Use the appropriate constant for the
  18252. // version of WinSock we're using.
  18253. //
  18254. if (g_iUnicastTTL != 0)
  18255. {
  18256. iTemp = this->m_pfnsetsockopt(sTemp,
  18257. IPPROTO_IP,
  18258. #ifdef DPNBUILD_NOWINSOCK2
  18259. IP_TTL,
  18260. #else // ! DPNBUILD_NOWINSOCK2
  18261. ((this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1) ? IP_TTL_WINSOCK1 : IP_TTL),
  18262. #endif // ! DPNBUILD_NOWINSOCK2
  18263. (char *) (&g_iUnicastTTL),
  18264. sizeof(g_iUnicastTTL));
  18265. if (iTemp != 0)
  18266. {
  18267. #ifdef DBG
  18268. dwError = this->m_pfnWSAGetLastError();
  18269. DPFX(DPFPREP, 0, "Couldn't set unicast TTL socket option, error = %u! Ignoring.",
  18270. dwError);
  18271. #endif // DBG
  18272. //
  18273. // Continue...
  18274. //
  18275. }
  18276. }
  18277. if (iType == SOCK_DGRAM)
  18278. {
  18279. if (g_fUseMulticastUPnPDiscovery)
  18280. {
  18281. //
  18282. // Set the multicast interface. Use the appropriate constant for
  18283. // the version of WinSock we're using.
  18284. //
  18285. iTemp = this->m_pfnsetsockopt(sTemp,
  18286. IPPROTO_IP,
  18287. #ifdef DPNBUILD_NOWINSOCK2
  18288. IP_MULTICAST_IF,
  18289. #else // ! DPNBUILD_NOWINSOCK2
  18290. ((this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1) ? IP_MULTICAST_IF_WINSOCK1 : IP_MULTICAST_IF),
  18291. #endif // ! DPNBUILD_NOWINSOCK2
  18292. (char *) (&psaddrinAddress->sin_addr.S_un.S_addr),
  18293. sizeof(psaddrinAddress->sin_addr.S_un.S_addr));
  18294. if (iTemp != 0)
  18295. {
  18296. #ifdef DBG
  18297. dwError = this->m_pfnWSAGetLastError();
  18298. DPFX(DPFPREP, 1, "Couldn't set multicast interface socket option, error = %u, ignoring.",
  18299. dwError);
  18300. #endif // DBG
  18301. //
  18302. // Continue...
  18303. //
  18304. }
  18305. //
  18306. // Set the multicast TTL, if requested. Use the appropriate
  18307. // constant for the version of WinSock we're using.
  18308. //
  18309. if (g_iMulticastTTL != 0)
  18310. {
  18311. iTemp = this->m_pfnsetsockopt(sTemp,
  18312. IPPROTO_IP,
  18313. #ifdef DPNBUILD_NOWINSOCK2
  18314. IP_MULTICAST_TTL,
  18315. #else // ! DPNBUILD_NOWINSOCK2
  18316. ((this->m_dwFlags & NATHELPUPNPOBJ_WINSOCK1) ? IP_MULTICAST_TTL_WINSOCK1 : IP_MULTICAST_TTL),
  18317. #endif // ! DPNBUILD_NOWINSOCK2
  18318. (char *) (&g_iMulticastTTL),
  18319. sizeof(g_iMulticastTTL));
  18320. if (iTemp != 0)
  18321. {
  18322. #ifdef DBG
  18323. dwError = this->m_pfnWSAGetLastError();
  18324. DPFX(DPFPREP, 0, "Couldn't set multicast TTL socket option, error = %u! Ignoring.",
  18325. dwError);
  18326. #endif // DBG
  18327. //
  18328. // Continue...
  18329. //
  18330. }
  18331. }
  18332. }
  18333. else
  18334. {
  18335. //
  18336. // Not using multicast. Set the socket up to allow broadcasts in
  18337. // case we can't determine the gateway.
  18338. //
  18339. fTemp = TRUE;
  18340. if (this->m_pfnsetsockopt(sTemp,
  18341. SOL_SOCKET,
  18342. SO_BROADCAST,
  18343. (char *) (&fTemp),
  18344. sizeof(fTemp)) != 0)
  18345. {
  18346. #ifdef DBG
  18347. dwError = this->m_pfnWSAGetLastError();
  18348. DPFX(DPFPREP, 0, "Couldn't set broadcast socket option, error = %u!", dwError);
  18349. #endif // DBG
  18350. goto Failure;
  18351. }
  18352. }
  18353. }
  18354. else
  18355. {
  18356. //
  18357. // Make the socket non-blocking.
  18358. //
  18359. ulEnable = 1;
  18360. if (this->m_pfnioctlsocket(sTemp, FIONBIO, &ulEnable) != 0)
  18361. {
  18362. #ifdef DBG
  18363. dwError = this->m_pfnWSAGetLastError();
  18364. DPFX(DPFPREP, 0, "Couldn't make socket non-blocking, error = %u!", dwError);
  18365. #endif // DBG
  18366. goto Failure;
  18367. }
  18368. }
  18369. Exit:
  18370. DPFX(DPFPREP, 5, "(0x%p) Returning: [0x%x]", this, sTemp);
  18371. return sTemp;
  18372. Failure:
  18373. if (sTemp != INVALID_SOCKET)
  18374. {
  18375. this->m_pfnclosesocket(sTemp);
  18376. sTemp = INVALID_SOCKET;
  18377. }
  18378. goto Exit;
  18379. } // CNATHelpUPnP::CreateSocket
  18380. #undef DPF_MODNAME
  18381. #define DPF_MODNAME "CNATHelpUPnP::GetAddressToReachGateway"
  18382. //=============================================================================
  18383. // CNATHelpUPnP::GetAddressToReachGateway
  18384. //-----------------------------------------------------------------------------
  18385. //
  18386. // Description: Retrieves the address of the gateway for the given device,
  18387. // or the broadcast address if unable to be determined.
  18388. //
  18389. // This will return TRUE if the gateway's address was found, or
  18390. // the IPHLPAPI DLL could not be used (Win95). FALSE is returned
  18391. // if IPHLPAPI reported that there was no gateway (ICS private
  18392. // side adapter).
  18393. //
  18394. // Arguments:
  18395. // CDevice * pDevice - Pointer to device whose gateway should be retrieved.
  18396. // IN_ADDR * pinaddr - Place to store gateway or broadcast address.
  18397. //
  18398. // Returns: BOOL
  18399. // TRUE - Gateway address was found or had to use broadcast.
  18400. // FALSE - There is no gateway, do not attempt to use the address.
  18401. //=============================================================================
  18402. BOOL CNATHelpUPnP::GetAddressToReachGateway(CDevice * const pDevice,
  18403. IN_ADDR * const pinaddr)
  18404. {
  18405. #ifdef DPNBUILD_NOWINSOCK2
  18406. //
  18407. // Fill in the default address. This should be atomic, so don't worry
  18408. // about locking the globals.
  18409. //
  18410. pinaddr->S_un.S_addr = g_dwDefaultGatewayV4;
  18411. return TRUE;
  18412. #else // ! DPNBUILD_NOWINSOCK2
  18413. DWORD dwError;
  18414. BOOL fResult = TRUE;
  18415. ULONG ulSize;
  18416. PIP_ADAPTER_INFO pAdaptersBuffer = NULL;
  18417. PIP_ADAPTER_INFO pAdapterInfo;
  18418. PIP_ADDR_STRING pIPAddrString;
  18419. DWORD dwAdapterIndex;
  18420. PMIB_IPFORWARDTABLE pIPForwardTableBuffer = NULL;
  18421. DWORD dwTemp;
  18422. PMIB_IPFORWARDROW pIPForwardRow;
  18423. //
  18424. // Fill in the default address. This should be atomic, so don't worry
  18425. // about locking the globals.
  18426. //
  18427. pinaddr->S_un.S_addr = g_dwDefaultGatewayV4;
  18428. #ifdef DBG
  18429. pDevice->ClearGatewayFlags();
  18430. #endif // DBG
  18431. //
  18432. // If this is the loopback address, then don't bother looking for a
  18433. // gateway, we won't find one.
  18434. //
  18435. if (pDevice->GetLocalAddressV4() == NETWORKBYTEORDER_INADDR_LOOPBACK)
  18436. {
  18437. DPFX(DPFPREP, 8, "No gateway for loopback address (device = 0x%p).",
  18438. pDevice);
  18439. //
  18440. // No gateway.
  18441. //
  18442. #ifdef DBG
  18443. pDevice->NoteNoGateway();
  18444. #endif // DBG
  18445. fResult = FALSE;
  18446. goto Exit;
  18447. }
  18448. //
  18449. // If we didn't load the IP helper DLL, we can't do our fancy gateway
  18450. // tricks.
  18451. //
  18452. if (this->m_hIpHlpApiDLL == NULL)
  18453. {
  18454. DPFX(DPFPREP, 4, "Didn't load \"iphlpapi.dll\", returning default address for device 0x%p.",
  18455. pDevice);
  18456. goto Exit;
  18457. }
  18458. //
  18459. // Keep trying to get the list of adapters until we get ERROR_SUCCESS or a
  18460. // legitimate error (other than ERROR_BUFFER_OVERFLOW or
  18461. // ERROR_INSUFFICIENT_BUFFER).
  18462. //
  18463. ulSize = 0;
  18464. do
  18465. {
  18466. dwError = this->m_pfnGetAdaptersInfo(pAdaptersBuffer, &ulSize);
  18467. if (dwError == ERROR_SUCCESS)
  18468. {
  18469. //
  18470. // We succeeded, we should be set. But make sure there are
  18471. // adapters for us to use.
  18472. //
  18473. if (ulSize < sizeof(IP_ADAPTER_INFO))
  18474. {
  18475. DPFX(DPFPREP, 0, "Getting adapters info succeeded but didn't return any valid adapters (%u < %u), returning default address for device 0x%p.",
  18476. ulSize, sizeof(IP_ADAPTER_INFO), pDevice);
  18477. goto Exit;
  18478. }
  18479. break;
  18480. }
  18481. if ((dwError != ERROR_BUFFER_OVERFLOW) &&
  18482. (dwError != ERROR_INSUFFICIENT_BUFFER))
  18483. {
  18484. DPFX(DPFPREP, 0, "Unable to get adapters info (error = 0x%lx), returning default address for device 0x%p.",
  18485. dwError, pDevice);
  18486. goto Exit;
  18487. }
  18488. //
  18489. // We need more adapter space. Make sure there are adapters for us to
  18490. // use.
  18491. //
  18492. if (ulSize < sizeof(IP_ADAPTER_INFO))
  18493. {
  18494. DPFX(DPFPREP, 0, "Getting adapters info didn't return any valid adapters (%u < %u), returning default address for device 0x%p.",
  18495. ulSize, sizeof(IP_ADAPTER_INFO), pDevice);
  18496. goto Exit;
  18497. }
  18498. //
  18499. // If we previously had a buffer, free it.
  18500. //
  18501. if (pAdaptersBuffer != NULL)
  18502. {
  18503. DNFree(pAdaptersBuffer);
  18504. }
  18505. //
  18506. // Allocate the buffer.
  18507. //
  18508. pAdaptersBuffer = (PIP_ADAPTER_INFO) DNMalloc(ulSize);
  18509. if (pAdaptersBuffer == NULL)
  18510. {
  18511. DPFX(DPFPREP, 0, "Unable to allocate memory for adapters info, returning default address for device 0x%p.",
  18512. pDevice);
  18513. goto Exit;
  18514. }
  18515. }
  18516. while (TRUE);
  18517. //
  18518. // Now find the device in the adapter list returned. Loop through all
  18519. // adapters.
  18520. //
  18521. pAdapterInfo = pAdaptersBuffer;
  18522. while (pAdapterInfo != NULL)
  18523. {
  18524. //
  18525. // Loop through all addresses for this adapter looking for the one for
  18526. // the device we have bound.
  18527. //
  18528. pIPAddrString = &pAdapterInfo->IpAddressList;
  18529. while (pIPAddrString != NULL)
  18530. {
  18531. if (this->m_pfninet_addr(pIPAddrString->IpAddress.String) == pDevice->GetLocalAddressV4())
  18532. {
  18533. pinaddr->S_un.S_addr = this->m_pfninet_addr(pAdapterInfo->GatewayList.IpAddress.String);
  18534. if ((pinaddr->S_un.S_addr == INADDR_ANY) ||
  18535. (pinaddr->S_un.S_addr == INADDR_NONE))
  18536. {
  18537. DPFX(DPFPREP, 8, "Found address for device 0x%p under adapter index %u (\"%hs\") but there is no gateway.",
  18538. pDevice, pAdapterInfo->Index, pAdapterInfo->Description);
  18539. //
  18540. // Although this isn't reporting a gateway, we may still
  18541. // want to use this adapter. That's because this could be
  18542. // a multihomed machine with multiple NICs on the same
  18543. // network, where this one isn't the "default" adapter.
  18544. // So save the index so we can search for it later.
  18545. //
  18546. dwAdapterIndex = pAdapterInfo->Index;
  18547. goto CheckRouteTable;
  18548. }
  18549. //
  18550. // Make sure the address doesn't match the local device.
  18551. //
  18552. if (pinaddr->S_un.S_addr == pDevice->GetLocalAddressV4())
  18553. {
  18554. DPFX(DPFPREP, 1, "Gateway address for device 0x%p (adapter index %u, \"%hs\") matches device IP address %hs! Forcing no gateway.",
  18555. pDevice, pAdapterInfo->Index, pAdapterInfo->Description,
  18556. pAdapterInfo->GatewayList.IpAddress.String);
  18557. //
  18558. // Pretend there's no gateway, since the one we received is
  18559. // bogus.
  18560. //
  18561. #ifdef DBG
  18562. pDevice->NoteNoGateway();
  18563. #endif // DBG
  18564. fResult = FALSE;
  18565. }
  18566. else
  18567. {
  18568. DPFX(DPFPREP, 7, "Found address for device 0x%p under adapter index %u (\"%hs\"), gateway = %hs.",
  18569. pDevice, pAdapterInfo->Index, pAdapterInfo->Description,
  18570. pAdapterInfo->GatewayList.IpAddress.String);
  18571. #ifdef DBG
  18572. pDevice->NotePrimaryDevice();
  18573. #endif // DBG
  18574. }
  18575. goto Exit;
  18576. }
  18577. pIPAddrString = pIPAddrString->Next;
  18578. }
  18579. if (! fResult)
  18580. {
  18581. break;
  18582. }
  18583. pAdapterInfo = pAdapterInfo->Next;
  18584. }
  18585. //
  18586. // If we got here, then we didn't find the address. fResult will still be
  18587. // TRUE.
  18588. //
  18589. DPFX(DPFPREP, 0, "Did not find adapter with matching address, returning default address for device 0x%p.",
  18590. pDevice);
  18591. goto Exit;
  18592. CheckRouteTable:
  18593. //
  18594. // The adapter info structure said that the device doesn't have a gateway.
  18595. // However for some reason the gateway is only reported for the "default"
  18596. // device when multiple NICs can reach the same network. Check the routing
  18597. // table to determine if there's a gateway for secondary devices.
  18598. //
  18599. //
  18600. // Keep trying to get the routing table until we get ERROR_SUCCESS or a
  18601. // legitimate error (other than ERROR_BUFFER_OVERFLOW or
  18602. // ERROR_INSUFFICIENT_BUFFER).
  18603. //
  18604. ulSize = 0;
  18605. do
  18606. {
  18607. dwError = this->m_pfnGetIpForwardTable(pIPForwardTableBuffer, &ulSize, TRUE);
  18608. if (dwError == ERROR_SUCCESS)
  18609. {
  18610. //
  18611. // We succeeded, we should be set. But make sure the size is
  18612. // valid.
  18613. //
  18614. if (ulSize < sizeof(MIB_IPFORWARDTABLE))
  18615. {
  18616. 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.",
  18617. ulSize, sizeof(MIB_IPFORWARDTABLE), pDevice);
  18618. fResult = FALSE;
  18619. goto Exit;
  18620. }
  18621. break;
  18622. }
  18623. if ((dwError != ERROR_BUFFER_OVERFLOW) &&
  18624. (dwError != ERROR_INSUFFICIENT_BUFFER))
  18625. {
  18626. DPFX(DPFPREP, 0, "Unable to get IP forward table (error = 0x%lx), returning \"no gateway\" indication for device 0x%p.",
  18627. dwError, pDevice);
  18628. fResult = FALSE;
  18629. goto Exit;
  18630. }
  18631. //
  18632. // We need more table space. Make sure there are adapters for us to
  18633. // use.
  18634. //
  18635. if (ulSize < sizeof(MIB_IPFORWARDTABLE))
  18636. {
  18637. DPFX(DPFPREP, 0, "Getting IP forward table didn't return any valid adapters (%u < %u), returning \"no gateway\" indication for device 0x%p.",
  18638. ulSize, sizeof(MIB_IPFORWARDTABLE), pDevice);
  18639. fResult = FALSE;
  18640. goto Exit;
  18641. }
  18642. //
  18643. // If we previously had a buffer, free it.
  18644. //
  18645. if (pIPForwardTableBuffer != NULL)
  18646. {
  18647. DNFree(pIPForwardTableBuffer);
  18648. }
  18649. //
  18650. // Allocate the buffer.
  18651. //
  18652. pIPForwardTableBuffer = (PMIB_IPFORWARDTABLE) DNMalloc(ulSize);
  18653. if (pIPForwardTableBuffer == NULL)
  18654. {
  18655. DPFX(DPFPREP, 0, "Unable to allocate memory for IP forward table, returning \"no gateway\" indication for device 0x%p.",
  18656. pDevice);
  18657. fResult = FALSE;
  18658. goto Exit;
  18659. }
  18660. }
  18661. while (TRUE);
  18662. //
  18663. // Now find the interface. Note that we don't look it up as a destination
  18664. // address. Instead, we look for it as the interface to use for a 0.0.0.0
  18665. // network destination.
  18666. //
  18667. // We're looking for a route entry:
  18668. //
  18669. // Network Destination Netmask Gateway Interface Metric
  18670. // 0.0.0.0 0.0.0.0 xxx.xxx.xxx.xxx yyy.yyy.yyy.yyy 1
  18671. //
  18672. // We have yyy.yyy.yyy.yyy, we're trying to get xxx.xxx.xxx.xxx
  18673. //
  18674. pIPForwardRow = pIPForwardTableBuffer->table;
  18675. for(dwTemp = 0; dwTemp < pIPForwardTableBuffer->dwNumEntries; dwTemp++)
  18676. {
  18677. //
  18678. // Is this a 0.0.0.0 network destination?
  18679. //
  18680. if (pIPForwardRow->dwForwardDest == INADDR_ANY)
  18681. {
  18682. DNASSERT(pIPForwardRow->dwForwardMask == INADDR_ANY);
  18683. //
  18684. // Is this the right interface?
  18685. //
  18686. if (pIPForwardRow->dwForwardIfIndex == dwAdapterIndex)
  18687. {
  18688. if (pIPForwardRow->dwForwardNextHop == INADDR_ANY)
  18689. {
  18690. DPFX(DPFPREP, 8, "Found route table entry, but it didn't have a gateway (device = 0x%p).",
  18691. pDevice);
  18692. //
  18693. // No gateway.
  18694. //
  18695. #ifdef DBG
  18696. pDevice->NoteNoGateway();
  18697. #endif // DBG
  18698. fResult = FALSE;
  18699. }
  18700. else
  18701. {
  18702. //
  18703. // Make sure the address doesn't match the local device.
  18704. //
  18705. if (pinaddr->S_un.S_addr == pDevice->GetLocalAddressV4())
  18706. {
  18707. DPFX(DPFPREP, 1, "Route table gateway for device 0x%p matches device's IP address %u.%u.%u.%u! Forcing no gateway.",
  18708. pDevice,
  18709. pinaddr->S_un.S_un_b.s_b1,
  18710. pinaddr->S_un.S_un_b.s_b2,
  18711. pinaddr->S_un.S_un_b.s_b3,
  18712. pinaddr->S_un.S_un_b.s_b4);
  18713. //
  18714. // Pretend there's no gateway, since the one we
  18715. // received is bogus.
  18716. //
  18717. #ifdef DBG
  18718. pDevice->NoteNoGateway();
  18719. #endif // DBG
  18720. fResult = FALSE;
  18721. }
  18722. else
  18723. {
  18724. pinaddr->S_un.S_addr = pIPForwardRow->dwForwardNextHop;
  18725. DPFX(DPFPREP, 8, "Found route table entry, gateway = %u.%u.%u.%u (device = 0x%p).",
  18726. pinaddr->S_un.S_un_b.s_b1,
  18727. pinaddr->S_un.S_un_b.s_b2,
  18728. pinaddr->S_un.S_un_b.s_b3,
  18729. pinaddr->S_un.S_un_b.s_b4,
  18730. pDevice);
  18731. //
  18732. // We found a gateway after all, fResult == TRUE.
  18733. //
  18734. #ifdef DBG
  18735. pDevice->NoteSecondaryDevice();
  18736. #endif // DBG
  18737. }
  18738. }
  18739. //
  18740. // We're done here.
  18741. //
  18742. goto Exit;
  18743. }
  18744. }
  18745. //
  18746. // Move to next row.
  18747. //
  18748. pIPForwardRow++;
  18749. }
  18750. //
  18751. // If we got here, then we couldn't find an appropriate entry in the
  18752. // routing table.
  18753. //
  18754. DPFX(DPFPREP, 1, "Did not find adapter in routing table, returning \"no gateway\" indication for device 0x%p.",
  18755. pDevice);
  18756. #ifdef DBG
  18757. pDevice->NoteNoGateway();
  18758. #endif // DBG
  18759. fResult = FALSE;
  18760. Exit:
  18761. if (pAdaptersBuffer != NULL)
  18762. {
  18763. DNFree(pAdaptersBuffer);
  18764. pAdaptersBuffer = NULL;
  18765. }
  18766. if (pIPForwardTableBuffer != NULL)
  18767. {
  18768. DNFree(pIPForwardTableBuffer);
  18769. pIPForwardTableBuffer = NULL;
  18770. }
  18771. return fResult;
  18772. #endif // ! DPNBUILD_NOWINSOCK2
  18773. } // CNATHelpUPnP::GetAddressToReachGateway
  18774. #undef DPF_MODNAME
  18775. #define DPF_MODNAME "CNATHelpUPnP::IsAddressLocal"
  18776. //=============================================================================
  18777. // CNATHelpUPnP::IsAddressLocal
  18778. //-----------------------------------------------------------------------------
  18779. //
  18780. // Description: Returns TRUE if the given address is local to the given
  18781. // device; that is, if the device can send to the address directly
  18782. // without having to go through the gateway.
  18783. //
  18784. // Note that if IPHLPAPI is not available (Win95), this
  18785. // function will make an educated guess using a reasonable subnet
  18786. // mask.
  18787. //
  18788. // Arguments:
  18789. // CDevice * pDevice - Pointer to device to use.
  18790. // SOCKADDR_IN * psaddrinAddress - Address whose locality is in question.
  18791. //
  18792. // Returns: BOOL
  18793. // TRUE - Address is behind the same gateway as the device.
  18794. // FALSE - Address is not behind the same gateway as the device.
  18795. //=============================================================================
  18796. BOOL CNATHelpUPnP::IsAddressLocal(CDevice * const pDevice,
  18797. const SOCKADDR_IN * const psaddrinAddress)
  18798. {
  18799. BOOL fResult;
  18800. DWORD dwSubnetMaskV4;
  18801. #ifndef DPNBUILD_NOWINSOCK2
  18802. DWORD dwError;
  18803. MIB_IPFORWARDROW IPForwardRow;
  18804. #endif // ! DPNBUILD_NOWINSOCK2
  18805. //
  18806. // If the address to query matches the device's local address exactly, then
  18807. // of course it's local.
  18808. //
  18809. if (psaddrinAddress->sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4())
  18810. {
  18811. DPFX(DPFPREP, 6, "The address %u.%u.%u.%u matches device 0x%p's local address exactly.",
  18812. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18813. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18814. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18815. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  18816. pDevice);
  18817. fResult = TRUE;
  18818. goto Exit;
  18819. }
  18820. //
  18821. // If it's a multicast address, then it should not be considered local.
  18822. //
  18823. if (IS_CLASSD_IPV4_ADDRESS(psaddrinAddress->sin_addr.S_un.S_addr))
  18824. {
  18825. DPFX(DPFPREP, 6, "Address %u.%u.%u.%u is multicast, not considered local for device 0x%p.",
  18826. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18827. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18828. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18829. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  18830. pDevice);
  18831. fResult = FALSE;
  18832. goto Exit;
  18833. }
  18834. #ifndef DPNBUILD_NOWINSOCK2
  18835. //
  18836. // If we didn't load the IP helper DLL, we will have to guess.
  18837. //
  18838. if (this->m_hIpHlpApiDLL == NULL)
  18839. {
  18840. goto EducatedGuess;
  18841. }
  18842. //
  18843. // Figure out what IPHLPAPI says about how to get there.
  18844. //
  18845. ZeroMemory(&IPForwardRow, sizeof(IPForwardRow));
  18846. dwError = this->m_pfnGetBestRoute(psaddrinAddress->sin_addr.S_un.S_addr,
  18847. pDevice->GetLocalAddressV4(),
  18848. &IPForwardRow);
  18849. if (dwError != ERROR_SUCCESS)
  18850. {
  18851. DPFX(DPFPREP, 0, "Unable to get best route to %u.%u.%u.%u via device 0x%p (error = 0x%lx)! Using subnet mask.",
  18852. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18853. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18854. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18855. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  18856. pDevice,
  18857. dwError);
  18858. goto EducatedGuess;
  18859. }
  18860. //
  18861. // Key off what IPHLPAPI returned.
  18862. //
  18863. switch (IPForwardRow.dwForwardType)
  18864. {
  18865. case 1:
  18866. {
  18867. //
  18868. // Other.
  18869. //
  18870. DPFX(DPFPREP, 6, "The route from device 0x%p to %u.%u.%u.%u is unknown.",
  18871. pDevice,
  18872. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18873. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18874. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18875. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
  18876. fResult = FALSE;
  18877. break;
  18878. }
  18879. case 2:
  18880. {
  18881. //
  18882. // The route is invalid.
  18883. //
  18884. DPFX(DPFPREP, 6, "The route from device 0x%p to %u.%u.%u.%u is invalid.",
  18885. pDevice,
  18886. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18887. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18888. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18889. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
  18890. fResult = FALSE;
  18891. break;
  18892. }
  18893. case 3:
  18894. {
  18895. //
  18896. // The next hop is the final destination (local route).
  18897. // Unfortunately, on multi-NIC machines querying an address
  18898. // reachable by another device returns success... not sure why, but
  18899. // if that's the case we need to further qualify this result. We
  18900. // do that by making sure the next hop address is actually the
  18901. // device with which we're querying.
  18902. //
  18903. if (IPForwardRow.dwForwardNextHop == pDevice->GetLocalAddressV4())
  18904. {
  18905. DPFX(DPFPREP, 6, "Device 0x%p can reach %u.%u.%u.%u directly, it's local.",
  18906. pDevice,
  18907. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18908. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18909. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18910. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
  18911. fResult = TRUE;
  18912. }
  18913. else
  18914. {
  18915. DPFX(DPFPREP, 6, "Device 0x%p can reach %u.%u.%u.%u but it would be routed via another device (%u.%u.%u.%u).",
  18916. pDevice,
  18917. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18918. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18919. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18920. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  18921. ((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b1,
  18922. ((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b2,
  18923. ((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b3,
  18924. ((IN_ADDR*) (&IPForwardRow.dwForwardNextHop))->S_un.S_un_b.s_b4);
  18925. fResult = FALSE;
  18926. }
  18927. break;
  18928. }
  18929. case 4:
  18930. {
  18931. //
  18932. // The next hop is not the final destination (remote route).
  18933. //
  18934. DPFX(DPFPREP, 6, "Device 0x%p cannot reach %u.%u.%u.%u directly.",
  18935. pDevice,
  18936. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18937. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18938. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18939. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
  18940. fResult = FALSE;
  18941. break;
  18942. }
  18943. default:
  18944. {
  18945. //
  18946. // What?
  18947. //
  18948. DPFX(DPFPREP, 0, "Unexpected forward type %u for device 0x%p and address %u.%u.%u.%u!",
  18949. IPForwardRow.dwForwardType,
  18950. pDevice,
  18951. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18952. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18953. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18954. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4);
  18955. fResult = FALSE;
  18956. break;
  18957. }
  18958. }
  18959. goto Exit;
  18960. EducatedGuess:
  18961. #endif // ! DPNBUILD_NOWINSOCK2
  18962. //
  18963. // This should be atomic, so don't worry about locking.
  18964. //
  18965. dwSubnetMaskV4 = g_dwSubnetMaskV4;
  18966. if ((pDevice->GetLocalAddressV4() & dwSubnetMaskV4) == (psaddrinAddress->sin_addr.S_un.S_addr & dwSubnetMaskV4))
  18967. {
  18968. DPFX(DPFPREP, 4, "Didn't load \"iphlpapi.dll\", guessing that device 0x%p can reach %u.%u.%u.%u (using subnet mask 0x%08x).",
  18969. pDevice,
  18970. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18971. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18972. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18973. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  18974. dwSubnetMaskV4);
  18975. fResult = TRUE;
  18976. }
  18977. else
  18978. {
  18979. DPFX(DPFPREP, 4, "Didn't load \"iphlpapi.dll\", guessing that device 0x%p cannot reach %u.%u.%u.%u (using subnet mask 0x%08x).",
  18980. pDevice,
  18981. psaddrinAddress->sin_addr.S_un.S_un_b.s_b1,
  18982. psaddrinAddress->sin_addr.S_un.S_un_b.s_b2,
  18983. psaddrinAddress->sin_addr.S_un.S_un_b.s_b3,
  18984. psaddrinAddress->sin_addr.S_un.S_un_b.s_b4,
  18985. dwSubnetMaskV4);
  18986. fResult = FALSE;
  18987. }
  18988. Exit:
  18989. return fResult;
  18990. } // CNATHelpUPnP::IsAddressLocal
  18991. #undef DPF_MODNAME
  18992. #define DPF_MODNAME "CNATHelpUPnP::ExpireOldCachedMappings"
  18993. //=============================================================================
  18994. // CNATHelpUPnP::ExpireOldCachedMappings
  18995. //-----------------------------------------------------------------------------
  18996. //
  18997. // Description: Removes any cached mappings for any device which has
  18998. // expired.
  18999. //
  19000. // The object lock is assumed to be held.
  19001. //
  19002. // Arguments: None.
  19003. //
  19004. // Returns: None.
  19005. //=============================================================================
  19006. void CNATHelpUPnP::ExpireOldCachedMappings(void)
  19007. {
  19008. DWORD dwCurrentTime;
  19009. CBilink * pBilinkUPnPDevice;
  19010. CBilink * pCachedMaps;
  19011. CBilink * pBilinkCacheMap;
  19012. CCacheMap * pCacheMap;
  19013. CUPnPDevice * pUPnPDevice;
  19014. DPFX(DPFPREP, 7, "(0x%p) Enter", this);
  19015. dwCurrentTime = GETTIMESTAMP();
  19016. //
  19017. // Check the UPnP device cached mappings.
  19018. //
  19019. pBilinkUPnPDevice = this->m_blUPnPDevices.GetNext();
  19020. while (pBilinkUPnPDevice != &this->m_blUPnPDevices)
  19021. {
  19022. DNASSERT(! pBilinkUPnPDevice->IsEmpty());
  19023. pUPnPDevice = UPNPDEVICE_FROM_BILINK(pBilinkUPnPDevice);
  19024. //
  19025. // Check the actual cached mappings.
  19026. //
  19027. pCachedMaps = pUPnPDevice->GetCachedMaps();
  19028. pBilinkCacheMap = pCachedMaps->GetNext();
  19029. while (pBilinkCacheMap != pCachedMaps)
  19030. {
  19031. DNASSERT(! pBilinkCacheMap->IsEmpty());
  19032. pCacheMap = CACHEMAP_FROM_BILINK(pBilinkCacheMap);
  19033. pBilinkCacheMap = pBilinkCacheMap->GetNext();
  19034. if ((int) (pCacheMap->GetExpirationTime() - dwCurrentTime) < 0)
  19035. {
  19036. DPFX(DPFPREP, 5, "UPnP device 0x%p cached mapping 0x%p has expired.",
  19037. pUPnPDevice, pCacheMap);
  19038. pCacheMap->m_blList.RemoveFromList();
  19039. delete pCacheMap;
  19040. }
  19041. }
  19042. pBilinkUPnPDevice = pBilinkUPnPDevice->GetNext();
  19043. }
  19044. DPFX(DPFPREP, 7, "(0x%p) Leave", this);
  19045. } // CNATHelpUPnP::ExpireOldCachedMappings
  19046. #ifdef WINNT
  19047. #undef DPF_MODNAME
  19048. #define DPF_MODNAME "CNATHelpUPnP::IsUPnPServiceDisabled"
  19049. //=============================================================================
  19050. // CNATHelpUPnP::IsUPnPServiceDisabled
  19051. //-----------------------------------------------------------------------------
  19052. //
  19053. // Description: Returns TRUE if at least one UPnP related service is
  19054. // disabled, FALSE if no UPnP related services are disabled.
  19055. //
  19056. // Arguments:
  19057. // char * szString - Pointer to string to print.
  19058. // int iStringLength - Length of string to print.
  19059. // char * szDescription - Description header for the transaction.
  19060. // CDevice * pDevice - Device handling transaction, or NULL if not
  19061. // known.
  19062. //
  19063. // Returns: BOOL
  19064. // TRUE - A UPnP related service was disabled.
  19065. // FALSE - No UPnP related services were disabled.
  19066. //=============================================================================
  19067. BOOL CNATHelpUPnP::IsUPnPServiceDisabled(void)
  19068. {
  19069. BOOL fResult = FALSE;
  19070. SC_HANDLE schSCManager = NULL;
  19071. DWORD dwTemp;
  19072. SC_HANDLE schService = NULL;
  19073. QUERY_SERVICE_CONFIG * pQueryServiceConfig = NULL;
  19074. DWORD dwQueryServiceConfigSize = 0;
  19075. DWORD dwError;
  19076. schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  19077. if (schSCManager == NULL)
  19078. {
  19079. #ifdef DBG
  19080. dwError = GetLastError();
  19081. DPFX(DPFPREP, 0, "Couldn't open SC Manager (err = %u)!", dwError);
  19082. #endif // DBG
  19083. goto Exit;
  19084. }
  19085. //
  19086. // Loop through each relevant service.
  19087. //
  19088. for(dwTemp = 0; dwTemp < (sizeof(c_tszUPnPServices) / sizeof(TCHAR*)); dwTemp++)
  19089. {
  19090. schService = OpenService(schSCManager, c_tszUPnPServices[dwTemp], SERVICE_QUERY_CONFIG);
  19091. if (schService != NULL)
  19092. {
  19093. do
  19094. {
  19095. if (QueryServiceConfig(schService,
  19096. pQueryServiceConfig,
  19097. dwQueryServiceConfigSize,
  19098. &dwQueryServiceConfigSize))
  19099. {
  19100. //
  19101. // Make sure the size written is valid.
  19102. //
  19103. if (dwQueryServiceConfigSize < sizeof(QUERY_SERVICE_CONFIG))
  19104. {
  19105. DPFX(DPFPREP, 0, "Got invalid service config size for \"%s\" (%u < %u)!",
  19106. c_tszUPnPServices[dwTemp], dwQueryServiceConfigSize, sizeof(QUERY_SERVICE_CONFIG));
  19107. goto Exit;
  19108. }
  19109. break;
  19110. }
  19111. //
  19112. // Otherwise, we failed. Make sure it's because our buffer was
  19113. // too small.
  19114. //
  19115. dwError = GetLastError();
  19116. if (dwError != ERROR_INSUFFICIENT_BUFFER)
  19117. {
  19118. DPFX(DPFPREP, 0, "Couldn't query \"%s\" service config (err = %u)!",
  19119. c_tszUPnPServices[dwTemp], dwError);
  19120. goto Exit;
  19121. }
  19122. //
  19123. // Make sure the size needed is valid.
  19124. //
  19125. if (dwQueryServiceConfigSize < sizeof(QUERY_SERVICE_CONFIG))
  19126. {
  19127. DPFX(DPFPREP, 0, "Got invalid service config size for \"%s\" (%u < %u)!",
  19128. c_tszUPnPServices[dwTemp], dwQueryServiceConfigSize, sizeof(QUERY_SERVICE_CONFIG));
  19129. goto Exit;
  19130. }
  19131. //
  19132. // (Re)-allocate the buffer.
  19133. //
  19134. if (pQueryServiceConfig != NULL)
  19135. {
  19136. DNFree(pQueryServiceConfig);
  19137. }
  19138. pQueryServiceConfig = (QUERY_SERVICE_CONFIG*) DNMalloc(dwQueryServiceConfigSize);
  19139. if (pQueryServiceConfig == NULL)
  19140. {
  19141. DPFX(DPFPREP, 0, "Couldn't allocate memory to query service config.");
  19142. goto Exit;
  19143. }
  19144. }
  19145. while (TRUE);
  19146. //
  19147. // If the service was disabled, we're done here.
  19148. //
  19149. if (pQueryServiceConfig->dwStartType == SERVICE_DISABLED)
  19150. {
  19151. DPFX(DPFPREP, 1, "The \"%s\" service has been disabled.",
  19152. c_tszUPnPServices[dwTemp]);
  19153. fResult = TRUE;
  19154. goto Exit;
  19155. }
  19156. DPFX(DPFPREP, 7, "The \"%s\" service is not disabled (start type = %u).",
  19157. c_tszUPnPServices[dwTemp], pQueryServiceConfig->dwStartType);
  19158. }
  19159. else
  19160. {
  19161. //
  19162. // Win2K doesn't have these services, so it will always fail.
  19163. //
  19164. #ifdef DBG
  19165. dwError = GetLastError();
  19166. DPFX(DPFPREP, 1, "Couldn't open \"%s\" service (err = %u), continuing.",
  19167. c_tszUPnPServices[dwTemp], dwError);
  19168. #endif // DBG
  19169. }
  19170. }
  19171. Exit:
  19172. if (pQueryServiceConfig != NULL)
  19173. {
  19174. DNFree(pQueryServiceConfig);
  19175. pQueryServiceConfig = NULL;
  19176. }
  19177. if (schService != NULL)
  19178. {
  19179. CloseServiceHandle(schService);
  19180. schService = NULL;
  19181. }
  19182. if (schSCManager != NULL)
  19183. {
  19184. CloseServiceHandle(schSCManager);
  19185. schSCManager = NULL;
  19186. }
  19187. return fResult;
  19188. } // CNATHelpUPnP::IsUPnPServiceDisabled
  19189. #endif // WINNT
  19190. #ifdef DBG
  19191. #undef DPF_MODNAME
  19192. #define DPF_MODNAME "CNATHelpUPnP::PrintUPnPTransactionToFile"
  19193. //=============================================================================
  19194. // CNATHelpUPnP::PrintUPnPTransactionToFile
  19195. //-----------------------------------------------------------------------------
  19196. //
  19197. // Description: Prints a given UPnP transaction to the file if logging is
  19198. // enabled.
  19199. //
  19200. // The object lock is assumed to be held.
  19201. //
  19202. // Arguments:
  19203. // char * szString - Pointer to string to print.
  19204. // int iStringLength - Length of string to print.
  19205. // char * szDescription - Description header for the transaction.
  19206. // CDevice * pDevice - Device handling transaction, or NULL if not
  19207. // known.
  19208. //
  19209. // Returns: None.
  19210. //=============================================================================
  19211. void CNATHelpUPnP::PrintUPnPTransactionToFile(const char * const szString,
  19212. const int iStringLength,
  19213. const char * const szDescription,
  19214. CDevice * const pDevice)
  19215. {
  19216. DNHANDLE hFile;
  19217. DWORD dwNumBytesWritten;
  19218. TCHAR tszHeaderPrefix[256];
  19219. DWORD dwError;
  19220. #ifdef UNICODE
  19221. char szHeaderPrefix[256];
  19222. #endif // UNICODE
  19223. //
  19224. // Lock the globals so nobody touches the string while we use it.
  19225. //
  19226. DNEnterCriticalSection(&g_csGlobalsLock);
  19227. //
  19228. // Only print it if UPnP transaction logging is turned on.
  19229. //
  19230. if (wcslen(g_wszUPnPTransactionLog) > 0)
  19231. {
  19232. #ifndef UNICODE
  19233. HRESULT hr;
  19234. char szUPnPTransactionLog[sizeof(g_wszUPnPTransactionLog) / sizeof(WCHAR)];
  19235. DWORD dwLength;
  19236. //
  19237. // Convert the Unicode file name/path into ANSI.
  19238. //
  19239. dwLength = sizeof(szUPnPTransactionLog) / sizeof(char);
  19240. hr = STR_WideToAnsi(g_wszUPnPTransactionLog,
  19241. -1, // NULL terminated
  19242. szUPnPTransactionLog,
  19243. &dwLength);
  19244. if (hr != S_OK)
  19245. {
  19246. DPFX(DPFPREP, 0, "Couldn't convert UPnP transaction log file string from Unicode to ANSI (err = 0x%lx)!",
  19247. hr);
  19248. hFile = DNINVALID_HANDLE_VALUE;
  19249. }
  19250. else
  19251. {
  19252. //
  19253. // Open the file if it exists, or create a new one if it doesn't.
  19254. //
  19255. hFile = DNCreateFile(szUPnPTransactionLog,
  19256. (GENERIC_READ | GENERIC_WRITE),
  19257. FILE_SHARE_READ,
  19258. NULL,
  19259. OPEN_ALWAYS,
  19260. 0,
  19261. NULL);
  19262. }
  19263. #else // UNICODE
  19264. //
  19265. // Open the file if it exists, or create a new one if it doesn't.
  19266. //
  19267. hFile = DNCreateFile(g_wszUPnPTransactionLog,
  19268. (GENERIC_READ | GENERIC_WRITE),
  19269. FILE_SHARE_READ,
  19270. NULL,
  19271. OPEN_ALWAYS,
  19272. 0,
  19273. NULL);
  19274. #endif // UNICODE
  19275. if (hFile == DNINVALID_HANDLE_VALUE)
  19276. {
  19277. dwError = GetLastError();
  19278. DPFX(DPFPREP, 0, "Couldn't open UPnP transaction log file, err = %u!",
  19279. dwError);
  19280. }
  19281. else
  19282. {
  19283. //
  19284. // Move the write pointer to the end of the file unless the file
  19285. // has exceeded the maximum size, in which case just start over.
  19286. // Ignore error.
  19287. //
  19288. if (GetFileSize(HANDLE_FROM_DNHANDLE(hFile), NULL) >= MAX_TRANSACTION_LOG_SIZE)
  19289. {
  19290. DPFX(DPFPREP, 0, "Transaction log maximum size exceeded, overwriting existing contents!");
  19291. SetFilePointer(HANDLE_FROM_DNHANDLE(hFile), 0, NULL, FILE_BEGIN);
  19292. }
  19293. else
  19294. {
  19295. SetFilePointer(HANDLE_FROM_DNHANDLE(hFile), 0, NULL, FILE_END);
  19296. }
  19297. //
  19298. // Write the descriptive header. Ignore errors.
  19299. //
  19300. if (pDevice != NULL)
  19301. {
  19302. IN_ADDR inaddr;
  19303. inaddr.S_un.S_addr = pDevice->GetLocalAddressV4();
  19304. wsprintf(tszHeaderPrefix,
  19305. _T("%u\t0x%lx\t0x%lx\t(0x%p, %u.%u.%u.%u) UPnP transaction \""),
  19306. GETTIMESTAMP(),
  19307. GetCurrentProcessId(),
  19308. GetCurrentThreadId(),
  19309. pDevice,
  19310. inaddr.S_un.S_un_b.s_b1,
  19311. inaddr.S_un.S_un_b.s_b2,
  19312. inaddr.S_un.S_un_b.s_b3,
  19313. inaddr.S_un.S_un_b.s_b4);
  19314. }
  19315. else
  19316. {
  19317. wsprintf(tszHeaderPrefix,
  19318. _T("%u\t0x%lx\t0x%lx\t(no device) UPnP transaction \""),
  19319. GETTIMESTAMP(),
  19320. GetCurrentProcessId(),
  19321. GetCurrentThreadId());
  19322. }
  19323. #ifdef UNICODE
  19324. STR_jkWideToAnsi(szHeaderPrefix,
  19325. tszHeaderPrefix,
  19326. (_tcslen(tszHeaderPrefix) + 1));
  19327. WriteFile(HANDLE_FROM_DNHANDLE(hFile), szHeaderPrefix, strlen(szHeaderPrefix), &dwNumBytesWritten, NULL);
  19328. #else // ! UNICODE
  19329. WriteFile(HANDLE_FROM_DNHANDLE(hFile), tszHeaderPrefix, _tcslen(tszHeaderPrefix), &dwNumBytesWritten, NULL);
  19330. #endif // ! UNICODE
  19331. WriteFile(HANDLE_FROM_DNHANDLE(hFile), szDescription, strlen(szDescription), &dwNumBytesWritten, NULL);
  19332. WriteFile(HANDLE_FROM_DNHANDLE(hFile), "\"\r\n", strlen("\"\r\n"), &dwNumBytesWritten, NULL);
  19333. //
  19334. // Write the transaction. Ignore error.
  19335. //
  19336. WriteFile(HANDLE_FROM_DNHANDLE(hFile), szString, iStringLength, &dwNumBytesWritten, NULL);
  19337. //
  19338. // Add blank space. Ignore error.
  19339. //
  19340. WriteFile(HANDLE_FROM_DNHANDLE(hFile), "\r\n\r\n", strlen("\r\n\r\n"), &dwNumBytesWritten, NULL);
  19341. //
  19342. // Truncate the log at this point in case we are overwriting
  19343. // existing contents. Ignore error.
  19344. //
  19345. SetEndOfFile(HANDLE_FROM_DNHANDLE(hFile));
  19346. //
  19347. // Close the file.
  19348. //
  19349. DNCloseHandle(hFile);
  19350. }
  19351. }
  19352. //
  19353. // Drop the globals lock.
  19354. //
  19355. DNLeaveCriticalSection(&g_csGlobalsLock);
  19356. } // CNATHelpUPnP::PrintUPnPTransactionToFile
  19357. #undef DPF_MODNAME
  19358. #define DPF_MODNAME "CNATHelpUPnP::DebugPrintCurrentStatus"
  19359. //=============================================================================
  19360. // CNATHelpUPnP::DebugPrintCurrentStatus
  19361. //-----------------------------------------------------------------------------
  19362. //
  19363. // Description: Prints all the devices and mappings to the debug log
  19364. // routines.
  19365. //
  19366. // The object lock is assumed to be held.
  19367. //
  19368. // Arguments: None.
  19369. //
  19370. // Returns: None.
  19371. //=============================================================================
  19372. void CNATHelpUPnP::DebugPrintCurrentStatus(void)
  19373. {
  19374. CBilink * pBilinkDevice;
  19375. CBilink * pBilinkRegisteredPort;
  19376. CDevice * pDevice;
  19377. CRegisteredPort * pRegisteredPort;
  19378. IN_ADDR inaddrTemp;
  19379. DWORD dwTemp;
  19380. SOCKADDR_IN * pasaddrinTemp;
  19381. SOCKADDR_IN * pasaddrinPrivate;
  19382. CUPnPDevice * pUPnPDevice;
  19383. SOCKADDR_IN * pasaddrinUPnPPublic;
  19384. DPFX(DPFPREP, 3, "Object flags = 0x%08x", this->m_dwFlags);
  19385. pBilinkDevice = this->m_blDevices.GetNext();
  19386. while (pBilinkDevice != &this->m_blDevices)
  19387. {
  19388. DNASSERT(! pBilinkDevice->IsEmpty());
  19389. pDevice = DEVICE_FROM_BILINK(pBilinkDevice);
  19390. inaddrTemp.S_un.S_addr = pDevice->GetLocalAddressV4();
  19391. DPFX(DPFPREP, 3, "Device 0x%p (%u.%u.%u.%u):",
  19392. pDevice,
  19393. inaddrTemp.S_un.S_un_b.s_b1,
  19394. inaddrTemp.S_un.S_un_b.s_b2,
  19395. inaddrTemp.S_un.S_un_b.s_b3,
  19396. inaddrTemp.S_un.S_un_b.s_b4);
  19397. //
  19398. // Print the search information. We should have detected it by now.
  19399. //
  19400. if (pDevice->IsPerformingRemoteUPnPDiscovery())
  19401. {
  19402. if (pDevice->GotRemoteUPnPDiscoveryConnReset())
  19403. {
  19404. DPFX(DPFPREP, 3, " Performed remote UPnP discovery (from port %u), but got conn reset.",
  19405. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19406. }
  19407. else
  19408. {
  19409. DPFX(DPFPREP, 3, " Performed remote UPnP discovery (from port %u).",
  19410. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19411. }
  19412. }
  19413. else
  19414. {
  19415. //DPFX(DPFPREP, 3, " Didn't perform remote UPnP discovery (from port %u).",
  19416. // NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19417. }
  19418. if (pDevice->IsPerformingLocalUPnPDiscovery())
  19419. {
  19420. if (pDevice->GotLocalUPnPDiscoveryConnReset())
  19421. {
  19422. DPFX(DPFPREP, 3, " Performed local UPnP discovery (from port %u), but got conn reset.",
  19423. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19424. }
  19425. else
  19426. {
  19427. DPFX(DPFPREP, 3, " Performed local UPnP discovery (from port %u).",
  19428. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19429. }
  19430. }
  19431. else
  19432. {
  19433. //DPFX(DPFPREP, 3, " Didn't perform local UPnP discovery (from port %u).",
  19434. // NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19435. }
  19436. #ifndef DPNBUILD_NOWINSOCK2
  19437. //
  19438. // Print the gateway information. We may not have detected it yet,
  19439. // that's okay.
  19440. //
  19441. if (pDevice->IsPrimaryDevice())
  19442. {
  19443. DPFX(DPFPREP, 3, " Primary device.");
  19444. }
  19445. else if (pDevice->IsSecondaryDevice())
  19446. {
  19447. DPFX(DPFPREP, 3, " Secondary device.");
  19448. }
  19449. else if (pDevice->HasNoGateway())
  19450. {
  19451. DPFX(DPFPREP, 3, " Has no gateway.");
  19452. }
  19453. else
  19454. {
  19455. DPFX(DPFPREP, 3, " No gateway information known.");
  19456. }
  19457. #endif // ! DPNBUILD_NOWINSOCK2
  19458. #ifndef DPNBUILD_NOHNETFWAPI
  19459. if (pDevice->IsHNetFirewalled())
  19460. {
  19461. DPFX(DPFPREP, 3, " HNet firewalled.");
  19462. }
  19463. else
  19464. {
  19465. DNASSERT(! pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall());
  19466. }
  19467. if (pDevice->IsUPnPDiscoverySocketMappedOnHNetFirewall())
  19468. {
  19469. DNASSERT(pDevice->IsHNetFirewalled());
  19470. DPFX(DPFPREP, 3, " UPnP discovery socket (port %u) mapped on HNet firewall.",
  19471. NTOHS(pDevice->GetUPnPDiscoverySocketPort()));
  19472. }
  19473. #endif // ! DPNBUILD_NOHNETFWAPI
  19474. pUPnPDevice = pDevice->GetUPnPDevice();
  19475. if (pUPnPDevice != NULL)
  19476. {
  19477. pasaddrinTemp = pUPnPDevice->GetControlAddress();
  19478. DPFX(DPFPREP, 3, " UPnP device (0x%p, ID = %u, control = %u.%u.%u.%u:%u).",
  19479. pUPnPDevice, pUPnPDevice->GetID(),
  19480. pasaddrinTemp->sin_addr.S_un.S_un_b.s_b1,
  19481. pasaddrinTemp->sin_addr.S_un.S_un_b.s_b2,
  19482. pasaddrinTemp->sin_addr.S_un.S_un_b.s_b3,
  19483. pasaddrinTemp->sin_addr.S_un.S_un_b.s_b4,
  19484. NTOHS(pasaddrinTemp->sin_port));
  19485. if (pasaddrinTemp->sin_addr.S_un.S_addr == pDevice->GetLocalAddressV4())
  19486. {
  19487. DPFX(DPFPREP, 3, " Is local.");
  19488. }
  19489. DNASSERT(pUPnPDevice->IsReady());
  19490. if (pUPnPDevice->IsConnected())
  19491. {
  19492. DPFX(DPFPREP, 3, " Is connected.");
  19493. }
  19494. if (pUPnPDevice->DoesNotSupportAsymmetricMappings())
  19495. {
  19496. DPFX(DPFPREP, 3, " Does not support asymmetric mappings.");
  19497. }
  19498. if (pUPnPDevice->DoesNotSupportLeaseDurations())
  19499. {
  19500. DPFX(DPFPREP, 3, " Does not support lease durations.");
  19501. }
  19502. inaddrTemp.S_un.S_addr = pUPnPDevice->GetExternalIPAddressV4();
  19503. if (pUPnPDevice->GetExternalIPAddressV4() == 0)
  19504. {
  19505. DPFX(DPFPREP, 3, " Does not have a valid external IP address.");
  19506. }
  19507. else
  19508. {
  19509. DPFX(DPFPREP, 3, " Has external IP %u.%u.%u.%u.",
  19510. inaddrTemp.S_un.S_un_b.s_b1,
  19511. inaddrTemp.S_un.S_un_b.s_b2,
  19512. inaddrTemp.S_un.S_un_b.s_b3,
  19513. inaddrTemp.S_un.S_un_b.s_b4);
  19514. }
  19515. }
  19516. if (pDevice->m_blOwnedRegPorts.IsEmpty())
  19517. {
  19518. DPFX(DPFPREP, 3, " No registered port mappings.");
  19519. }
  19520. else
  19521. {
  19522. DPFX(DPFPREP, 3, " Registered port mappings:");
  19523. pBilinkRegisteredPort = pDevice->m_blOwnedRegPorts.GetNext();
  19524. while (pBilinkRegisteredPort != &pDevice->m_blOwnedRegPorts)
  19525. {
  19526. DNASSERT(! pBilinkRegisteredPort->IsEmpty());
  19527. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
  19528. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  19529. if ((pDevice->GetUPnPDevice() != NULL) &&
  19530. (! pRegisteredPort->IsUPnPPortUnavailable()))
  19531. {
  19532. if (pRegisteredPort->HasUPnPPublicAddresses())
  19533. {
  19534. pasaddrinUPnPPublic = pRegisteredPort->GetUPnPPublicAddressesArray();
  19535. }
  19536. else
  19537. {
  19538. pasaddrinUPnPPublic = NULL;
  19539. }
  19540. }
  19541. else
  19542. {
  19543. pasaddrinUPnPPublic = NULL;
  19544. }
  19545. DPFX(DPFPREP, 3, " Registered port 0x%p:",
  19546. pRegisteredPort);
  19547. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  19548. {
  19549. //
  19550. // Print private address.
  19551. //
  19552. DPFX(DPFPREP, 3, " %u-\tPrivate = %u.%u.%u.%u:%u",
  19553. dwTemp,
  19554. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b1,
  19555. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b2,
  19556. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b3,
  19557. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b4,
  19558. NTOHS(pasaddrinPrivate[dwTemp].sin_port));
  19559. //
  19560. // Print flags.
  19561. //
  19562. DPFX(DPFPREP, 3, " \tFlags = 0x%lx",
  19563. pRegisteredPort->GetFlags());
  19564. //
  19565. // Print UPnP information.
  19566. //
  19567. if (pasaddrinUPnPPublic != NULL)
  19568. {
  19569. if (pRegisteredPort->HasPermanentUPnPLease())
  19570. {
  19571. DPFX(DPFPREP, 3, " \tUPnP = %u.%u.%u.%u:%u, permanently leased",
  19572. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b1,
  19573. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b2,
  19574. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b3,
  19575. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b4,
  19576. NTOHS(pasaddrinUPnPPublic[dwTemp].sin_port));
  19577. }
  19578. else
  19579. {
  19580. DPFX(DPFPREP, 3, " \tUPnP = %u.%u.%u.%u:%u, lease expires at %u",
  19581. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b1,
  19582. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b2,
  19583. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b3,
  19584. pasaddrinUPnPPublic[dwTemp].sin_addr.S_un.S_un_b.s_b4,
  19585. NTOHS(pasaddrinUPnPPublic[dwTemp].sin_port),
  19586. pRegisteredPort->GetUPnPLeaseExpiration());
  19587. }
  19588. }
  19589. else if (pRegisteredPort->IsUPnPPortUnavailable())
  19590. {
  19591. DPFX(DPFPREP, 3, " \tUPnP = port unavailable");
  19592. }
  19593. else if (pDevice->GetUPnPDevice() != NULL)
  19594. {
  19595. DPFX(DPFPREP, 3, " \tUPnP = not registered");
  19596. }
  19597. else
  19598. {
  19599. //
  19600. // No UPnP gateway device.
  19601. //
  19602. }
  19603. #ifndef DPNBUILD_NOHNETFWAPI
  19604. //
  19605. // Print firewall status.
  19606. //
  19607. if (pRegisteredPort->IsMappedOnHNetFirewall())
  19608. {
  19609. DNASSERT(pDevice->IsHNetFirewalled());
  19610. if (pRegisteredPort->IsHNetFirewallMappingBuiltIn())
  19611. {
  19612. DPFX(DPFPREP, 3, " \tHNet firewall = built-in mapping");
  19613. }
  19614. else
  19615. {
  19616. DPFX(DPFPREP, 3, " \tHNet firewall = mapped");
  19617. }
  19618. }
  19619. else if (pRegisteredPort->IsHNetFirewallPortUnavailable())
  19620. {
  19621. DNASSERT(! pRegisteredPort->IsMappedOnHNetFirewall());
  19622. DPFX(DPFPREP, 3, " \tHNet firewall = port unavailable");
  19623. }
  19624. else
  19625. {
  19626. //
  19627. // It is not mapped on the firewall.
  19628. //
  19629. DNASSERT(! pDevice->IsHNetFirewalled());
  19630. DNASSERT(! pRegisteredPort->IsMappedOnHNetFirewall());
  19631. DNASSERT(! pRegisteredPort->IsHNetFirewallMappingBuiltIn());
  19632. }
  19633. #endif // ! DPNBUILD_NOHNETFWAPI
  19634. }
  19635. pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
  19636. }
  19637. }
  19638. pBilinkDevice = pBilinkDevice->GetNext();
  19639. }
  19640. if (this->m_blUnownedPorts.IsEmpty())
  19641. {
  19642. DPFX(DPFPREP, 3, "No unowned registered port mappings.");
  19643. }
  19644. else
  19645. {
  19646. DPFX(DPFPREP, 3, "Unowned registered port mappings:");
  19647. pBilinkRegisteredPort = this->m_blUnownedPorts.GetNext();
  19648. while (pBilinkRegisteredPort != &this->m_blUnownedPorts)
  19649. {
  19650. DNASSERT(! pBilinkRegisteredPort->IsEmpty());
  19651. pRegisteredPort = REGPORT_FROM_DEVICE_BILINK(pBilinkRegisteredPort);
  19652. pasaddrinPrivate = pRegisteredPort->GetPrivateAddressesArray();
  19653. DNASSERT(pRegisteredPort->GetOwningDevice() == NULL);
  19654. DNASSERT(! (pRegisteredPort->HasUPnPPublicAddresses()));
  19655. #ifndef DPNBUILD_NOHNETFWAPI
  19656. DNASSERT(! (pRegisteredPort->IsMappedOnHNetFirewall()));
  19657. #endif // ! DPNBUILD_NOHNETFWAPI
  19658. DPFX(DPFPREP, 3, " Registered port 0x%p:", pRegisteredPort);
  19659. for(dwTemp = 0; dwTemp < pRegisteredPort->GetNumAddresses(); dwTemp++)
  19660. {
  19661. //
  19662. // Print private address.
  19663. //
  19664. DPFX(DPFPREP, 3, " %u-\tPrivate = %u.%u.%u.%u:%u",
  19665. dwTemp,
  19666. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b1,
  19667. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b2,
  19668. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b3,
  19669. pasaddrinPrivate[dwTemp].sin_addr.S_un.S_un_b.s_b4,
  19670. NTOHS(pasaddrinPrivate[dwTemp].sin_port));
  19671. //
  19672. // Print flags.
  19673. //
  19674. DPFX(DPFPREP, 3, " \tFlags = 0x%lx",
  19675. pRegisteredPort->GetFlags());
  19676. }
  19677. pBilinkRegisteredPort = pBilinkRegisteredPort->GetNext();
  19678. }
  19679. }
  19680. } // CNATHelpUPnP::DebugPrintCurrentStatus
  19681. #ifndef DPNBUILD_NOHNETFWAPI
  19682. #undef DPF_MODNAME
  19683. #define DPF_MODNAME "CNATHelpUPnP::DebugPrintActiveFirewallMappings"
  19684. //=============================================================================
  19685. // CNATHelpUPnP::DebugPrintActiveFirewallMappings
  19686. //-----------------------------------------------------------------------------
  19687. //
  19688. // Description: Prints all the active firewall mapping registry entries to
  19689. // the debug log routines.
  19690. //
  19691. // Arguments: None.
  19692. //
  19693. // Returns: None.
  19694. //=============================================================================
  19695. void CNATHelpUPnP::DebugPrintActiveFirewallMappings(void)
  19696. {
  19697. HRESULT hr = DPNH_OK;
  19698. CRegistry RegObject;
  19699. DWORD dwIndex;
  19700. WCHAR wszValueName[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  19701. DWORD dwValueNameSize;
  19702. DPNHACTIVEFIREWALLMAPPING dpnhafm;
  19703. DWORD dwValueSize;
  19704. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  19705. DNHANDLE hNamedObject = NULL;
  19706. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  19707. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVEFIREWALLMAPPINGS,
  19708. FALSE,
  19709. TRUE,
  19710. TRUE,
  19711. DPN_KEY_ALL_ACCESS))
  19712. {
  19713. DPFX(DPFPREP, 1, "Couldn't open active firewall mapping key, not dumping entries (local instance = %u).",
  19714. this->m_dwInstanceKey);
  19715. }
  19716. else
  19717. {
  19718. //
  19719. // Walk the list of active mappings.
  19720. //
  19721. dwIndex = 0;
  19722. do
  19723. {
  19724. dwValueNameSize = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  19725. if (! RegObject.EnumValues(wszValueName, &dwValueNameSize, dwIndex))
  19726. {
  19727. //
  19728. // There was an error or there aren't any more keys. We're done.
  19729. //
  19730. break;
  19731. }
  19732. //
  19733. // Try reading that mapping's data.
  19734. //
  19735. dwValueSize = sizeof(dpnhafm);
  19736. if ((! RegObject.ReadBlob(wszValueName, (LPBYTE) (&dpnhafm), &dwValueSize)) ||
  19737. (dwValueSize != sizeof(dpnhafm)) ||
  19738. (dpnhafm.dwVersion != ACTIVE_MAPPING_VERSION))
  19739. {
  19740. DPFX(DPFPREP, 1, "Couldn't read \"%ls\" mapping value (index %u) or it was invalid! Ignoring.",
  19741. wszValueName, dwIndex);
  19742. }
  19743. else
  19744. {
  19745. //
  19746. // See if that DPNHUPNP instance is still around.
  19747. //
  19748. #ifndef WINCE
  19749. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  19750. {
  19751. wsprintf(tszObjectName, _T( "Global\\" ) INSTANCENAMEDOBJECT_FORMATSTRING, dpnhafm.dwInstanceKey);
  19752. }
  19753. else
  19754. #endif // ! WINCE
  19755. {
  19756. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, dpnhafm.dwInstanceKey);
  19757. }
  19758. hNamedObject = DNOpenEvent(SYNCHRONIZE, FALSE, tszObjectName);
  19759. if (hNamedObject != NULL)
  19760. {
  19761. //
  19762. // This is still an active mapping.
  19763. //
  19764. DPFX(DPFPREP, 5, "%u: Firewall mapping \"%ls\" belongs to instance %u (local instance = %u), which is still active.",
  19765. dwIndex, wszValueName, dpnhafm.dwInstanceKey,
  19766. this->m_dwInstanceKey);
  19767. DNCloseHandle(hNamedObject);
  19768. hNamedObject = NULL;
  19769. }
  19770. else
  19771. {
  19772. DPFX(DPFPREP, 5, "%u: Firewall mapping \"%ls\" belongs to instance %u (local instance = %u), which no longer exists.",
  19773. dwIndex, wszValueName, dpnhafm.dwInstanceKey,
  19774. this->m_dwInstanceKey);
  19775. }
  19776. }
  19777. //
  19778. // Move to next item.
  19779. //
  19780. dwIndex++;
  19781. }
  19782. while (TRUE);
  19783. //
  19784. // Close the registry object.
  19785. //
  19786. RegObject.Close();
  19787. DPFX(DPFPREP, 5, "Done reading %u registry entries (local instance = %u).",
  19788. dwIndex, this->m_dwInstanceKey);
  19789. }
  19790. } // CNATHelpUPnP::DebugPrintActiveFirewallMappings
  19791. #endif // ! DPNBUILD_NOHNETFWAPI
  19792. #undef DPF_MODNAME
  19793. #define DPF_MODNAME "CNATHelpUPnP::DebugPrintActiveNATMappings"
  19794. //=============================================================================
  19795. // CNATHelpUPnP::DebugPrintActiveNATMappings
  19796. //-----------------------------------------------------------------------------
  19797. //
  19798. // Description: Prints all the active NAT mapping registry entries to the
  19799. // debug log routines.
  19800. //
  19801. // Arguments: None.
  19802. //
  19803. // Returns: None.
  19804. //=============================================================================
  19805. void CNATHelpUPnP::DebugPrintActiveNATMappings(void)
  19806. {
  19807. HRESULT hr = DPNH_OK;
  19808. CRegistry RegObject;
  19809. DWORD dwIndex;
  19810. WCHAR wszValueName[MAX_UPNP_MAPPING_DESCRIPTION_SIZE];
  19811. DWORD dwValueNameSize;
  19812. DPNHACTIVENATMAPPING dpnhanm;
  19813. DWORD dwValueSize;
  19814. TCHAR tszObjectName[MAX_INSTANCENAMEDOBJECT_SIZE];
  19815. DNHANDLE hNamedObject = NULL;
  19816. if (! RegObject.Open(HKEY_LOCAL_MACHINE,
  19817. DIRECTPLAYNATHELP_REGKEY L"\\" REGKEY_COMPONENTSUBKEY L"\\" REGKEY_ACTIVENATMAPPINGS,
  19818. FALSE,
  19819. TRUE,
  19820. TRUE,
  19821. DPN_KEY_ALL_ACCESS))
  19822. {
  19823. DPFX(DPFPREP, 1, "Couldn't open active NAT mapping key, not dumping entries (local instance = %u).",
  19824. this->m_dwInstanceKey);
  19825. }
  19826. else
  19827. {
  19828. //
  19829. // Walk the list of active mappings.
  19830. //
  19831. dwIndex = 0;
  19832. do
  19833. {
  19834. dwValueNameSize = MAX_UPNP_MAPPING_DESCRIPTION_SIZE;
  19835. if (! RegObject.EnumValues(wszValueName, &dwValueNameSize, dwIndex))
  19836. {
  19837. //
  19838. // There was an error or there aren't any more keys. We're done.
  19839. //
  19840. break;
  19841. }
  19842. //
  19843. // Try reading that mapping's data.
  19844. //
  19845. dwValueSize = sizeof(dpnhanm);
  19846. if ((! RegObject.ReadBlob(wszValueName, (LPBYTE) (&dpnhanm), &dwValueSize)) ||
  19847. (dwValueSize != sizeof(dpnhanm)) ||
  19848. (dpnhanm.dwVersion != ACTIVE_MAPPING_VERSION))
  19849. {
  19850. DPFX(DPFPREP, 1, "Couldn't read \"%ls\" mapping value (index %u) or it was invalid! Ignoring.",
  19851. wszValueName, dwIndex);
  19852. }
  19853. else
  19854. {
  19855. //
  19856. // See if that DPNHUPNP instance is still around.
  19857. //
  19858. #ifndef WINCE
  19859. if (this->m_dwFlags & NATHELPUPNPOBJ_USEGLOBALNAMESPACEPREFIX)
  19860. {
  19861. wsprintf(tszObjectName, _T( "Global\\" ) INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  19862. }
  19863. else
  19864. #endif // ! WINCE
  19865. {
  19866. wsprintf(tszObjectName, INSTANCENAMEDOBJECT_FORMATSTRING, dpnhanm.dwInstanceKey);
  19867. }
  19868. hNamedObject = DNOpenEvent(SYNCHRONIZE, FALSE, tszObjectName);
  19869. if (hNamedObject != NULL)
  19870. {
  19871. //
  19872. // This is still an active mapping.
  19873. //
  19874. DPFX(DPFPREP, 5, "%u: NAT mapping \"%ls\" belongs to instance %u UPnP device %u (local instance = %u), which is still active.",
  19875. dwIndex, wszValueName, dpnhanm.dwInstanceKey,
  19876. dpnhanm.dwUPnPDeviceID, this->m_dwInstanceKey);
  19877. DNCloseHandle(hNamedObject);
  19878. hNamedObject = NULL;
  19879. }
  19880. else
  19881. {
  19882. DPFX(DPFPREP, 5, "%u: NAT mapping \"%ls\" belongs to instance %u UPnP device %u (local instance = %u), which no longer exists.",
  19883. dwIndex, wszValueName, dpnhanm.dwInstanceKey,
  19884. dpnhanm.dwUPnPDeviceID, this->m_dwInstanceKey);
  19885. }
  19886. }
  19887. //
  19888. // Move to next item.
  19889. //
  19890. dwIndex++;
  19891. }
  19892. while (TRUE);
  19893. //
  19894. // Close the registry object.
  19895. //
  19896. RegObject.Close();
  19897. DPFX(DPFPREP, 5, "Done reading %u registry entries (local instance = %u).",
  19898. dwIndex, this->m_dwInstanceKey);
  19899. }
  19900. } // CNATHelpUPnP::DebugPrintActiveNATMappings
  19901. #endif // DBG
  19902. #undef DPF_MODNAME
  19903. #define DPF_MODNAME "strtrim"
  19904. //=============================================================================
  19905. // strtrim
  19906. //-----------------------------------------------------------------------------
  19907. //
  19908. // Description: Removes surrounding white space from the given string. Taken
  19909. // from \nt\net\upnp\ssdp\common\ssdpparser\parser.cpp (author
  19910. // TingCai).
  19911. //
  19912. // Arguments:
  19913. // CHAR ** pszStr - Pointer to input string, and place to store resulting
  19914. // pointer.
  19915. //
  19916. // Returns: None.
  19917. //=============================================================================
  19918. VOID strtrim(CHAR ** pszStr)
  19919. {
  19920. CHAR *end;
  19921. CHAR *begin;
  19922. // Empty string. Nothing to do.
  19923. //
  19924. if (!(**pszStr))
  19925. {
  19926. return;
  19927. }
  19928. begin = *pszStr;
  19929. end = begin + strlen(*pszStr) - 1;
  19930. while (*begin == ' ' || *begin == '\t')
  19931. {
  19932. begin++;
  19933. }
  19934. *pszStr = begin;
  19935. while (*end == ' ' || *end == '\t')
  19936. {
  19937. end--;
  19938. }
  19939. *(end+1) = '\0';
  19940. } // strtrim
  19941. #ifdef WINCE
  19942. #undef DPF_MODNAME
  19943. #define DPF_MODNAME "GetExeName"
  19944. //=============================================================================
  19945. // GetExeName
  19946. //-----------------------------------------------------------------------------
  19947. //
  19948. // Description: Updates a path string to hold only the executable name
  19949. // contained in the path.
  19950. //
  19951. // Arguments:
  19952. // WCHAR * wszPath - Input path string, and place to store resulting
  19953. // string.
  19954. //
  19955. // Returns: None.
  19956. //=============================================================================
  19957. void GetExeName(WCHAR * wszPath)
  19958. {
  19959. WCHAR * pCurrent;
  19960. pCurrent = wszPath + wcslen(wszPath);
  19961. while (pCurrent > wszPath)
  19962. {
  19963. if ((*pCurrent) == L'\\')
  19964. {
  19965. break;
  19966. }
  19967. pCurrent--;
  19968. }
  19969. if (pCurrent != wszPath)
  19970. {
  19971. memcpy(wszPath, (pCurrent + 1), ((wcslen(pCurrent) + 1) * sizeof(WCHAR)));
  19972. }
  19973. } // GetExeName
  19974. #endif // WINCE