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.

696 lines
18 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 2000.
  5. //
  6. // File: A N N O U N C E . C P P
  7. //
  8. // Contents: SSDP Announcement list
  9. //
  10. // Notes:
  11. //
  12. // Author: mbend 8 Nov 2000
  13. //
  14. //----------------------------------------------------------------------------
  15. #include <pch.h>
  16. #pragma hdrstop
  17. #include "announce.h"
  18. #include "search.h"
  19. #include "ssdpfunc.h"
  20. #include "ssdpnetwork.h"
  21. #include "ncbase.h"
  22. #include "upthread.h"
  23. #define BUF_SIZE 40
  24. HRESULT HrInitializeSsdpRequestFromMessage(
  25. SSDP_REQUEST * pRequest,
  26. const SSDP_MESSAGE *pMsg);
  27. static LIST_ENTRY listAnnounce;
  28. CRITICAL_SECTION CSListAnnounce;
  29. static CHAR *AliveHeader = "ssdp:alive";
  30. static CHAR *ByebyeHeader = "ssdp:byebye";
  31. static CHAR *MulticastUri = "*";
  32. static const DWORD c_dwUPnPMajorVersion = 1;
  33. static const DWORD c_dwUPnPMinorVersion = 0;
  34. static const DWORD c_dwHostMajorVersion = 1;
  35. static const DWORD c_dwHostMinorVersion = 0;
  36. CSsdpService::CSsdpService() : m_timer(*this)
  37. {
  38. ZeroMemory(&m_ssdpRequest, sizeof(m_ssdpRequest));
  39. }
  40. CSsdpService::~CSsdpService()
  41. {
  42. FreeSsdpRequest(&m_ssdpRequest);
  43. }
  44. PCONTEXT_HANDLE_TYPE * CSsdpService::GetContext()
  45. {
  46. return m_ppContextHandle;
  47. }
  48. BOOL CSsdpService::FIsUsn(const char * szUsn)
  49. {
  50. BOOL bMatch = FALSE;
  51. if(m_ssdpRequest.Headers[SSDP_USN])
  52. {
  53. if(0 == lstrcmpA(m_ssdpRequest.Headers[SSDP_USN], szUsn))
  54. {
  55. bMatch = TRUE;
  56. }
  57. }
  58. return bMatch;
  59. }
  60. void CSsdpService::OnRundown(CSsdpService * pService)
  61. {
  62. CSsdpServiceManager::Instance().HrServiceRundown(pService);
  63. }
  64. void CSsdpService::TimerFired()
  65. {
  66. HRESULT hr = S_OK;
  67. char * szAlive = NULL;
  68. hr = m_strAlive.HrGetMultiByteWithAlloc(&szAlive);
  69. if(SUCCEEDED(hr))
  70. {
  71. GetNetworkLock();
  72. SendOnAllNetworks(szAlive, NULL);
  73. FreeNetworkLock();
  74. delete [] szAlive;
  75. // Repeat three times
  76. --m_nRetryCount;
  77. if(0 == m_nRetryCount)
  78. {
  79. // To-Do: The current limit on life time is 49.7 days. (32 bits in milliseconds)
  80. // 32 bit in seconds should be enough.
  81. // Need to add field remaining time ...
  82. m_nRetryCount = 3;
  83. long nTimeout = m_nLifetime * 1000/2 - 2 * (RETRY_INTERVAL * (NUM_RETRIES - 1));
  84. hr = m_timer.HrSetTimerInFired(nTimeout);
  85. }
  86. else
  87. {
  88. hr = m_timer.HrSetTimerInFired(RETRY_INTERVAL);
  89. }
  90. }
  91. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::TimerFired");
  92. }
  93. BOOL CSsdpService::TimerTryToLock()
  94. {
  95. return m_critSec.FTryEnter();
  96. }
  97. void CSsdpService::TimerUnlock()
  98. {
  99. m_critSec.Leave();
  100. }
  101. HRESULT CSsdpService::HrInitialize(
  102. SSDP_MESSAGE * pssdpMsg,
  103. DWORD dwFlags,
  104. PCONTEXT_HANDLE_TYPE * ppContextHandle)
  105. {
  106. HRESULT hr = S_OK;
  107. hr = HrInitializeSsdpRequestFromMessage(&m_ssdpRequest, pssdpMsg);
  108. if(SUCCEEDED(hr))
  109. {
  110. m_nLifetime = pssdpMsg->iLifeTime;
  111. m_nRetryCount = 3;
  112. m_dwFlags = dwFlags;
  113. m_ppContextHandle = ppContextHandle;
  114. *m_ppContextHandle = NULL;
  115. m_ssdpRequest.RequestUri = MulticastUri;
  116. OSVERSIONINFO osvi = {0};
  117. osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  118. if(!GetVersionEx(&osvi))
  119. {
  120. hr = HrFromLastWin32Error();
  121. }
  122. if(SUCCEEDED(hr))
  123. {
  124. // Yes this is a constant but it is just used temporarily
  125. m_ssdpRequest.Headers[SSDP_SERVER] = "Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0";
  126. char * szAlive = NULL;
  127. if(SUCCEEDED(hr))
  128. {
  129. m_ssdpRequest.Headers[SSDP_NTS] = AliveHeader;
  130. if (!ComposeSsdpRequest(&m_ssdpRequest, &szAlive))
  131. {
  132. hr = E_FAIL;
  133. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrInitialize - ComposeSsdpRequest failed!");
  134. }
  135. else
  136. {
  137. hr = m_strAlive.HrAssign(szAlive);
  138. delete [] szAlive;
  139. }
  140. }
  141. if(SUCCEEDED(hr))
  142. {
  143. char * szByebye = NULL;
  144. if(SUCCEEDED(hr))
  145. {
  146. m_ssdpRequest.Headers[SSDP_NTS] = ByebyeHeader;
  147. if(!ComposeSsdpRequest(&m_ssdpRequest, &szByebye))
  148. {
  149. hr = E_FAIL;
  150. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrInitialize - ComposeSsdpRequest failed!");
  151. }
  152. else
  153. {
  154. hr = m_strByebye.HrAssign(szByebye);
  155. delete [] szByebye;
  156. }
  157. }
  158. }
  159. if(SUCCEEDED(hr))
  160. {
  161. // Generate an empty Ext header on the response to M-SEARCH
  162. m_ssdpRequest.Headers[SSDP_EXT] = "";
  163. char * szResponse = NULL;
  164. if (!ComposeSsdpResponse(&m_ssdpRequest, &szResponse))
  165. {
  166. hr = E_FAIL;
  167. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrInitialize - ComposeSsdpResponse failed!");
  168. }
  169. else
  170. {
  171. hr = m_strResponse.HrAssign(szResponse);
  172. delete [] szResponse;
  173. }
  174. }
  175. }
  176. }
  177. // Important: Set to NULL to prevent freeing memory.
  178. m_ssdpRequest.Headers[SSDP_NTS] = NULL;
  179. m_ssdpRequest.RequestUri = NULL;
  180. // Important: Set to NULL to prevent freeing memory.
  181. m_ssdpRequest.Headers[SSDP_EXT] = NULL;
  182. m_ssdpRequest.Headers[SSDP_SERVER] = NULL;
  183. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrInitialize");
  184. return hr;
  185. }
  186. HRESULT CSsdpService::HrStartTimer()
  187. {
  188. HRESULT hr = S_OK;
  189. CLock lock(m_critSec);
  190. hr = m_timer.HrSetTimer(0);
  191. if(SUCCEEDED(hr))
  192. {
  193. *m_ppContextHandle = this;
  194. }
  195. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrStartTimer");
  196. return hr;
  197. }
  198. long CalcMX (SSDP_REQUEST * pSsdpRequest)
  199. {
  200. LONG lMin, lMX;
  201. #define MINIMUM_MX_UUID 1
  202. #define MINIMUM_MX_DST 2
  203. #define MINIMUM_MX_ROOT 3
  204. #define MAXIMUM_MX 12
  205. #define UUID_COLON "uuid:"
  206. #define URN_SERVICE "urn:schemas-upnp-org:service:"
  207. #define URN_DEVICE "urn:schemas-upnp-org:device:"
  208. Assert (pSsdpRequest != NULL);
  209. if (strncmp (pSsdpRequest->Headers[SSDP_ST], UUID_COLON, sizeof(UUID_COLON) - 1) == 0)
  210. lMin = MINIMUM_MX_UUID;
  211. else if (strcmp(pSsdpRequest->Headers[SSDP_ST], "upnp:rootdevice") == 0)
  212. lMin = MINIMUM_MX_ROOT;
  213. else if (strcmp(pSsdpRequest->Headers[SSDP_ST], "ssdp:all") == 0)
  214. lMin = MINIMUM_MX_ROOT;
  215. else if (
  216. strncmp (pSsdpRequest->Headers[SSDP_ST], URN_SERVICE, sizeof(URN_SERVICE) - 1) == 0 ||
  217. strncmp (pSsdpRequest->Headers[SSDP_ST], URN_DEVICE, sizeof(URN_DEVICE) - 1) == 0 )
  218. lMin = MINIMUM_MX_DST;
  219. else
  220. lMin = MINIMUM_MX_UUID;
  221. lMX = atol(pSsdpRequest->Headers[SSDP_MX]);
  222. if (lMX > MAXIMUM_MX) lMX = MAXIMUM_MX;
  223. else if (lMX < lMin) lMX = lMin;
  224. return lMX;
  225. }
  226. HRESULT CSsdpService::HrAddSearchResponse(SSDP_REQUEST * pSsdpRequest,
  227. SOCKET * pSocket,
  228. SOCKADDR_IN * pRemoteSocket)
  229. {
  230. HRESULT hr = S_OK;
  231. CLock lock(m_critSec);
  232. BOOL bSendRequest = FALSE;
  233. if(pSsdpRequest->Headers[SSDP_ST])
  234. {
  235. if(0 == lstrcmpA(pSsdpRequest->Headers[SSDP_ST], "ssdp:all"))
  236. {
  237. bSendRequest = TRUE;
  238. }
  239. if(!bSendRequest && 0 == lstrcmpA(pSsdpRequest->Headers[SSDP_ST], m_ssdpRequest.Headers[SSDP_NT]))
  240. {
  241. bSendRequest = TRUE;
  242. }
  243. }
  244. if(bSendRequest)
  245. {
  246. TraceTag(ttidSsdpAnnounce, "Adding search response for %s", m_ssdpRequest.Headers[SSDP_NT]);
  247. char * szResponse = NULL;
  248. hr = m_strResponse.HrGetMultiByteWithAlloc(&szResponse);
  249. if(SUCCEEDED(hr))
  250. {
  251. // Takes ownership of string
  252. hr = CSsdpSearchResponse::HrCreate(pSocket, pRemoteSocket,
  253. szResponse,
  254. CalcMX(pSsdpRequest));
  255. }
  256. }
  257. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrAddSearchResponse");
  258. return hr;
  259. }
  260. class CSsdpByebye : public CWorkItem
  261. {
  262. public:
  263. static HRESULT Create(char * szByebye);
  264. void TimerFired();
  265. BOOL TimerTryToLock()
  266. {
  267. return m_critSec.FTryEnter();
  268. }
  269. void TimerUnlock()
  270. {
  271. m_critSec.Leave();
  272. }
  273. private:
  274. CSsdpByebye(char * szByebye);
  275. ~CSsdpByebye();
  276. CSsdpByebye(const CSsdpByebye &);
  277. CSsdpByebye & operator=(const CSsdpByebye &);
  278. DWORD DwRun();
  279. char * m_szByebye;
  280. long m_nRetryCount;
  281. CTimer<CSsdpByebye> m_timer;
  282. CUCriticalSection m_critSec;
  283. };
  284. CSsdpByebye::CSsdpByebye(char * szByebye)
  285. : m_szByebye(szByebye), m_nRetryCount(3), m_timer(*this)
  286. {
  287. }
  288. CSsdpByebye::~CSsdpByebye()
  289. {
  290. delete [] m_szByebye;
  291. }
  292. HRESULT CSsdpByebye::Create(char * szByebye)
  293. {
  294. HRESULT hr = S_OK;
  295. CSsdpByebye * pByebye = new CSsdpByebye(szByebye);
  296. if(!pByebye)
  297. {
  298. hr = E_OUTOFMEMORY;
  299. delete [] szByebye;
  300. }
  301. if(SUCCEEDED(hr))
  302. {
  303. hr = pByebye->m_timer.HrSetTimer(0);
  304. if(FAILED(hr))
  305. {
  306. delete pByebye;
  307. }
  308. }
  309. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpByebye::Create");
  310. return hr;
  311. }
  312. void CSsdpByebye::TimerFired()
  313. {
  314. HRESULT hr = S_OK;
  315. GetNetworkLock();
  316. SendOnAllNetworks(m_szByebye, NULL);
  317. FreeNetworkLock();
  318. --m_nRetryCount;
  319. // We do this three times and then we kill ourselves
  320. if(0 == m_nRetryCount)
  321. {
  322. // This will queue ourselves to autodelete
  323. HrStart(true);
  324. }
  325. else
  326. {
  327. hr = m_timer.HrSetTimerInFired(RETRY_INTERVAL);
  328. if(FAILED(hr))
  329. {
  330. // This will queue ourselves to autodelete
  331. HrStart(true);
  332. }
  333. }
  334. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpByebye::TimerFired");
  335. }
  336. DWORD CSsdpByebye::DwRun()
  337. {
  338. // Wait for timer to exit before allowing this function to return (which causes delete this to happen)
  339. CLock lock(m_critSec);
  340. m_timer.HrDelete(INVALID_HANDLE_VALUE);
  341. return 0;
  342. }
  343. HRESULT CSsdpService::HrCleanup(BOOL bByebye)
  344. {
  345. HRESULT hr = S_OK;
  346. CLock lock(m_critSec);
  347. hr = m_timer.HrDelete(INVALID_HANDLE_VALUE);
  348. if(SUCCEEDED(hr))
  349. {
  350. if(bByebye)
  351. {
  352. char * szByebye = NULL;
  353. hr = m_strByebye.HrGetMultiByteWithAlloc(&szByebye);
  354. if(SUCCEEDED(hr))
  355. {
  356. // This takes ownership of memory
  357. CSsdpByebye::Create(szByebye);
  358. }
  359. }
  360. }
  361. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrCleanup");
  362. return hr;
  363. }
  364. BOOL CSsdpService::FIsPersistent()
  365. {
  366. return (SSDP_SERVICE_PERSISTENT & m_dwFlags) != 0;
  367. }
  368. CSsdpServiceManager CSsdpServiceManager::s_instance;
  369. CSsdpServiceManager::CSsdpServiceManager()
  370. {
  371. }
  372. CSsdpServiceManager::~CSsdpServiceManager()
  373. {
  374. }
  375. CSsdpServiceManager & CSsdpServiceManager::Instance()
  376. {
  377. return s_instance;
  378. }
  379. HRESULT CSsdpServiceManager::HrAddService(
  380. SSDP_MESSAGE * pssdpMsg,
  381. DWORD dwFlags,
  382. PCONTEXT_HANDLE_TYPE * ppContextHandle)
  383. {
  384. HRESULT hr = S_OK;
  385. CLock lock(m_critSec);
  386. if(pssdpMsg && FindServiceByUsn(pssdpMsg->szUSN))
  387. {
  388. hr = E_INVALIDARG;
  389. TraceTag(ttidSsdpAnnounce, "CSsdpServiceManager::HrAddService - attempting to add duplicate USN");
  390. }
  391. CSsdpService * pService = NULL;
  392. if(SUCCEEDED(hr))
  393. {
  394. ServiceList serviceList;
  395. hr = serviceList.HrPushFrontDefault();
  396. if(SUCCEEDED(hr))
  397. {
  398. hr = serviceList.Front().HrInitialize(pssdpMsg, dwFlags, ppContextHandle);
  399. if(SUCCEEDED(hr))
  400. {
  401. hr = serviceList.Front().HrStartTimer();
  402. if(SUCCEEDED(hr))
  403. {
  404. m_serviceList.Prepend(serviceList);
  405. pService = &m_serviceList.Front();
  406. }
  407. }
  408. }
  409. }
  410. if(pService)
  411. {
  412. hr = CSsdpRundownSupport::Instance().HrAddRundownItem(pService);
  413. }
  414. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrAddService");
  415. return hr;
  416. }
  417. CSsdpService * CSsdpServiceManager::FindServiceByUsn(char * szUSN)
  418. {
  419. HRESULT hr = S_OK;
  420. CLock lock(m_critSec);
  421. CSsdpService * pService = NULL;
  422. ServiceList::Iterator iter;
  423. if(S_OK == m_serviceList.GetIterator(iter))
  424. {
  425. CSsdpService * pServiceIter = NULL;
  426. while(S_OK == iter.HrGetItem(&pServiceIter))
  427. {
  428. if(pServiceIter->FIsUsn(szUSN))
  429. {
  430. pService = pServiceIter;
  431. break;
  432. }
  433. if(S_OK != iter.HrNext())
  434. {
  435. break;
  436. }
  437. }
  438. }
  439. if(!pService)
  440. {
  441. TraceTag(ttidSsdpAnnounce, "CSsdpServiceManager::FindServiceByUsn(%s) - not found!", szUSN);
  442. }
  443. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::FindServiceByUsn");
  444. return pService;
  445. }
  446. HRESULT CSsdpServiceManager::HrAddSearchResponse(SSDP_REQUEST * pSsdpRequest,
  447. SOCKET * pSocket,
  448. SOCKADDR_IN * pRemoteSocket)
  449. {
  450. HRESULT hr = S_OK;
  451. CLock lock(m_critSec);
  452. ServiceList::Iterator iter;
  453. if(S_OK == m_serviceList.GetIterator(iter))
  454. {
  455. CSsdpService * pService = NULL;
  456. while(S_OK == iter.HrGetItem(&pService))
  457. {
  458. pService->HrAddSearchResponse(pSsdpRequest, pSocket, pRemoteSocket);
  459. if(S_OK != iter.HrNext())
  460. {
  461. break;
  462. }
  463. }
  464. }
  465. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrAddSearchResponse");
  466. return hr;
  467. }
  468. HRESULT CSsdpServiceManager::HrRemoveService(CSsdpService * pService, BOOL bByebye)
  469. {
  470. HRESULT hr = S_OK;
  471. hr = HrRemoveServiceInternal(pService, bByebye, FALSE);
  472. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrRemoveService");
  473. return hr;
  474. }
  475. HRESULT CSsdpServiceManager::HrServiceRundown(CSsdpService * pService)
  476. {
  477. HRESULT hr = S_OK;
  478. hr = HrRemoveServiceInternal(pService, TRUE, TRUE);
  479. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrCleanupAnouncements");
  480. return hr;
  481. }
  482. HRESULT CSsdpServiceManager::HrCleanupAnouncements()
  483. {
  484. HRESULT hr = S_OK;
  485. CLock lock(m_critSec);
  486. // TODO: Implement this!!!!!!!!!!!!!!!!!!!!!!
  487. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrCleanupAnouncements");
  488. return hr;
  489. }
  490. HRESULT CSsdpServiceManager::HrRemoveServiceInternal(CSsdpService * pService, BOOL bByebye, BOOL bRundown)
  491. {
  492. HRESULT hr = S_OK;
  493. BOOL bFound = FALSE;
  494. ServiceList serviceList;
  495. {
  496. CLock lock(m_critSec);
  497. ServiceList::Iterator iter;
  498. if(S_OK == m_serviceList.GetIterator(iter))
  499. {
  500. CSsdpService * pServiceIter = NULL;
  501. while(S_OK == iter.HrGetItem(&pServiceIter))
  502. {
  503. if(pServiceIter == pService)
  504. {
  505. if(!bRundown || !pService->FIsPersistent())
  506. {
  507. iter.HrMoveToList(serviceList);
  508. bFound = TRUE;
  509. }
  510. break;
  511. }
  512. if(S_OK != iter.HrNext())
  513. {
  514. break;
  515. }
  516. }
  517. }
  518. }
  519. if(bFound)
  520. {
  521. ServiceList::Iterator iter;
  522. if(S_OK == serviceList.GetIterator(iter))
  523. {
  524. CSsdpService * pServiceIter = NULL;
  525. while(S_OK == iter.HrGetItem(&pServiceIter))
  526. {
  527. if(!bRundown)
  528. {
  529. CSsdpRundownSupport::Instance().RemoveRundownItem(pServiceIter);
  530. }
  531. hr = pServiceIter->HrCleanup(bByebye);
  532. if(S_OK != iter.HrNext())
  533. {
  534. break;
  535. }
  536. }
  537. }
  538. }
  539. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrRemoveServiceInternal");
  540. return hr;
  541. }
  542. HRESULT HrInitializeSsdpRequestFromMessage(
  543. SSDP_REQUEST * pRequest,
  544. const SSDP_MESSAGE *pMsg)
  545. {
  546. HRESULT hr = S_OK;
  547. ZeroMemory(pRequest, sizeof(*pRequest));
  548. pRequest->Method = SSDP_NOTIFY;
  549. pRequest->Headers[SSDP_HOST] = reinterpret_cast<char*>(
  550. midl_user_allocate(sizeof(CHAR) * (
  551. lstrlenA(SSDP_ADDR) +
  552. 1 + // colon
  553. 6 + // port number
  554. 1)));
  555. if (!pRequest->Headers[SSDP_HOST])
  556. {
  557. hr = E_OUTOFMEMORY;
  558. }
  559. if(SUCCEEDED(hr))
  560. {
  561. wsprintfA(pRequest->Headers[SSDP_HOST], "%s:%d", SSDP_ADDR, SSDP_PORT);
  562. hr = HrCopyString(pMsg->szType, &pRequest->Headers[SSDP_NT]);
  563. if(SUCCEEDED(hr) || !pMsg->szType)
  564. {
  565. hr = HrCopyString(pMsg->szUSN, &pRequest->Headers[SSDP_USN]);
  566. if(SUCCEEDED(hr) || !pMsg->szUSN)
  567. {
  568. hr = HrCopyString(pMsg->szAltHeaders, &pRequest->Headers[SSDP_AL]);
  569. if(SUCCEEDED(hr) || !pMsg->szAltHeaders)
  570. {
  571. hr = HrCopyString(pMsg->szLocHeader, &pRequest->Headers[SSDP_LOCATION]);
  572. if(SUCCEEDED(hr) || pMsg->szLocHeader)
  573. {
  574. char szBuf[256];
  575. wsprintfA(szBuf, "max-age=%d", pMsg->iLifeTime);
  576. hr = HrCopyString(szBuf, &pRequest->Headers[SSDP_CACHECONTROL]);
  577. }
  578. }
  579. }
  580. }
  581. }
  582. if(FAILED(hr))
  583. {
  584. FreeSsdpRequest(pRequest);
  585. ZeroMemory(pRequest, sizeof(*pRequest));
  586. }
  587. TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "HrInitializeSsdpRequestFromMessage");
  588. return hr;
  589. }