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

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 2000.
//
// File: A N N O U N C E . C P P
//
// Contents: SSDP Announcement list
//
// Notes:
//
// Author: mbend 8 Nov 2000
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#include "announce.h"
#include "search.h"
#include "ssdpfunc.h"
#include "ssdpnetwork.h"
#include "ncbase.h"
#include "upthread.h"
#define BUF_SIZE 40
HRESULT HrInitializeSsdpRequestFromMessage(
SSDP_REQUEST * pRequest,
const SSDP_MESSAGE *pMsg);
static LIST_ENTRY listAnnounce;
CRITICAL_SECTION CSListAnnounce;
static CHAR *AliveHeader = "ssdp:alive";
static CHAR *ByebyeHeader = "ssdp:byebye";
static CHAR *MulticastUri = "*";
static const DWORD c_dwUPnPMajorVersion = 1;
static const DWORD c_dwUPnPMinorVersion = 0;
static const DWORD c_dwHostMajorVersion = 1;
static const DWORD c_dwHostMinorVersion = 0;
CSsdpService::CSsdpService() : m_timer(*this)
{
ZeroMemory(&m_ssdpRequest, sizeof(m_ssdpRequest));
}
CSsdpService::~CSsdpService()
{
FreeSsdpRequest(&m_ssdpRequest);
}
PCONTEXT_HANDLE_TYPE * CSsdpService::GetContext()
{
return m_ppContextHandle;
}
BOOL CSsdpService::FIsUsn(const char * szUsn)
{
BOOL bMatch = FALSE;
if(m_ssdpRequest.Headers[SSDP_USN])
{
if(0 == lstrcmpA(m_ssdpRequest.Headers[SSDP_USN], szUsn))
{
bMatch = TRUE;
}
}
return bMatch;
}
void CSsdpService::OnRundown(CSsdpService * pService)
{
CSsdpServiceManager::Instance().HrServiceRundown(pService);
}
void CSsdpService::TimerFired()
{
HRESULT hr = S_OK;
char * szAlive = NULL;
hr = m_strAlive.HrGetMultiByteWithAlloc(&szAlive);
if(SUCCEEDED(hr))
{
GetNetworkLock();
SendOnAllNetworks(szAlive, NULL);
FreeNetworkLock();
delete [] szAlive;
// Repeat three times
--m_nRetryCount;
if(0 == m_nRetryCount)
{
// To-Do: The current limit on life time is 49.7 days. (32 bits in milliseconds)
// 32 bit in seconds should be enough.
// Need to add field remaining time ...
m_nRetryCount = 3;
long nTimeout = m_nLifetime * 1000/2 - 2 * (RETRY_INTERVAL * (NUM_RETRIES - 1));
hr = m_timer.HrSetTimerInFired(nTimeout);
}
else
{
hr = m_timer.HrSetTimerInFired(RETRY_INTERVAL);
}
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::TimerFired");
}
BOOL CSsdpService::TimerTryToLock()
{
return m_critSec.FTryEnter();
}
void CSsdpService::TimerUnlock()
{
m_critSec.Leave();
}
HRESULT CSsdpService::HrInitialize(
SSDP_MESSAGE * pssdpMsg,
DWORD dwFlags,
PCONTEXT_HANDLE_TYPE * ppContextHandle)
{
HRESULT hr = S_OK;
hr = HrInitializeSsdpRequestFromMessage(&m_ssdpRequest, pssdpMsg);
if(SUCCEEDED(hr))
{
m_nLifetime = pssdpMsg->iLifeTime;
m_nRetryCount = 3;
m_dwFlags = dwFlags;
m_ppContextHandle = ppContextHandle;
*m_ppContextHandle = NULL;
m_ssdpRequest.RequestUri = MulticastUri;
OSVERSIONINFO osvi = {0};
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
if(!GetVersionEx(&osvi))
{
hr = HrFromLastWin32Error();
}
if(SUCCEEDED(hr))
{
// Yes this is a constant but it is just used temporarily
m_ssdpRequest.Headers[SSDP_SERVER] = "Microsoft-Windows-NT/5.1 UPnP/1.0 UPnP-Device-Host/1.0";
char * szAlive = NULL;
if(SUCCEEDED(hr))
{
m_ssdpRequest.Headers[SSDP_NTS] = AliveHeader;
if (!ComposeSsdpRequest(&m_ssdpRequest, &szAlive))
{
hr = E_FAIL;
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrInitialize - ComposeSsdpRequest failed!");
}
else
{
hr = m_strAlive.HrAssign(szAlive);
delete [] szAlive;
}
}
if(SUCCEEDED(hr))
{
char * szByebye = NULL;
if(SUCCEEDED(hr))
{
m_ssdpRequest.Headers[SSDP_NTS] = ByebyeHeader;
if(!ComposeSsdpRequest(&m_ssdpRequest, &szByebye))
{
hr = E_FAIL;
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrInitialize - ComposeSsdpRequest failed!");
}
else
{
hr = m_strByebye.HrAssign(szByebye);
delete [] szByebye;
}
}
}
if(SUCCEEDED(hr))
{
// Generate an empty Ext header on the response to M-SEARCH
m_ssdpRequest.Headers[SSDP_EXT] = "";
char * szResponse = NULL;
if (!ComposeSsdpResponse(&m_ssdpRequest, &szResponse))
{
hr = E_FAIL;
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrInitialize - ComposeSsdpResponse failed!");
}
else
{
hr = m_strResponse.HrAssign(szResponse);
delete [] szResponse;
}
}
}
}
// Important: Set to NULL to prevent freeing memory.
m_ssdpRequest.Headers[SSDP_NTS] = NULL;
m_ssdpRequest.RequestUri = NULL;
// Important: Set to NULL to prevent freeing memory.
m_ssdpRequest.Headers[SSDP_EXT] = NULL;
m_ssdpRequest.Headers[SSDP_SERVER] = NULL;
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrInitialize");
return hr;
}
HRESULT CSsdpService::HrStartTimer()
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
hr = m_timer.HrSetTimer(0);
if(SUCCEEDED(hr))
{
*m_ppContextHandle = this;
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrStartTimer");
return hr;
}
long CalcMX (SSDP_REQUEST * pSsdpRequest)
{
LONG lMin, lMX;
#define MINIMUM_MX_UUID 1
#define MINIMUM_MX_DST 2
#define MINIMUM_MX_ROOT 3
#define MAXIMUM_MX 12
#define UUID_COLON "uuid:"
#define URN_SERVICE "urn:schemas-upnp-org:service:"
#define URN_DEVICE "urn:schemas-upnp-org:device:"
Assert (pSsdpRequest != NULL);
if (strncmp (pSsdpRequest->Headers[SSDP_ST], UUID_COLON, sizeof(UUID_COLON) - 1) == 0)
lMin = MINIMUM_MX_UUID;
else if (strcmp(pSsdpRequest->Headers[SSDP_ST], "upnp:rootdevice") == 0)
lMin = MINIMUM_MX_ROOT;
else if (strcmp(pSsdpRequest->Headers[SSDP_ST], "ssdp:all") == 0)
lMin = MINIMUM_MX_ROOT;
else if (
strncmp (pSsdpRequest->Headers[SSDP_ST], URN_SERVICE, sizeof(URN_SERVICE) - 1) == 0 ||
strncmp (pSsdpRequest->Headers[SSDP_ST], URN_DEVICE, sizeof(URN_DEVICE) - 1) == 0 )
lMin = MINIMUM_MX_DST;
else
lMin = MINIMUM_MX_UUID;
lMX = atol(pSsdpRequest->Headers[SSDP_MX]);
if (lMX > MAXIMUM_MX) lMX = MAXIMUM_MX;
else if (lMX < lMin) lMX = lMin;
return lMX;
}
HRESULT CSsdpService::HrAddSearchResponse(SSDP_REQUEST * pSsdpRequest,
SOCKET * pSocket,
SOCKADDR_IN * pRemoteSocket)
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
BOOL bSendRequest = FALSE;
if(pSsdpRequest->Headers[SSDP_ST])
{
if(0 == lstrcmpA(pSsdpRequest->Headers[SSDP_ST], "ssdp:all"))
{
bSendRequest = TRUE;
}
if(!bSendRequest && 0 == lstrcmpA(pSsdpRequest->Headers[SSDP_ST], m_ssdpRequest.Headers[SSDP_NT]))
{
bSendRequest = TRUE;
}
}
if(bSendRequest)
{
TraceTag(ttidSsdpAnnounce, "Adding search response for %s", m_ssdpRequest.Headers[SSDP_NT]);
char * szResponse = NULL;
hr = m_strResponse.HrGetMultiByteWithAlloc(&szResponse);
if(SUCCEEDED(hr))
{
// Takes ownership of string
hr = CSsdpSearchResponse::HrCreate(pSocket, pRemoteSocket,
szResponse,
CalcMX(pSsdpRequest));
}
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrAddSearchResponse");
return hr;
}
class CSsdpByebye : public CWorkItem
{
public:
static HRESULT Create(char * szByebye);
void TimerFired();
BOOL TimerTryToLock()
{
return m_critSec.FTryEnter();
}
void TimerUnlock()
{
m_critSec.Leave();
}
private:
CSsdpByebye(char * szByebye);
~CSsdpByebye();
CSsdpByebye(const CSsdpByebye &);
CSsdpByebye & operator=(const CSsdpByebye &);
DWORD DwRun();
char * m_szByebye;
long m_nRetryCount;
CTimer<CSsdpByebye> m_timer;
CUCriticalSection m_critSec;
};
CSsdpByebye::CSsdpByebye(char * szByebye)
: m_szByebye(szByebye), m_nRetryCount(3), m_timer(*this)
{
}
CSsdpByebye::~CSsdpByebye()
{
delete [] m_szByebye;
}
HRESULT CSsdpByebye::Create(char * szByebye)
{
HRESULT hr = S_OK;
CSsdpByebye * pByebye = new CSsdpByebye(szByebye);
if(!pByebye)
{
hr = E_OUTOFMEMORY;
delete [] szByebye;
}
if(SUCCEEDED(hr))
{
hr = pByebye->m_timer.HrSetTimer(0);
if(FAILED(hr))
{
delete pByebye;
}
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpByebye::Create");
return hr;
}
void CSsdpByebye::TimerFired()
{
HRESULT hr = S_OK;
GetNetworkLock();
SendOnAllNetworks(m_szByebye, NULL);
FreeNetworkLock();
--m_nRetryCount;
// We do this three times and then we kill ourselves
if(0 == m_nRetryCount)
{
// This will queue ourselves to autodelete
HrStart(true);
}
else
{
hr = m_timer.HrSetTimerInFired(RETRY_INTERVAL);
if(FAILED(hr))
{
// This will queue ourselves to autodelete
HrStart(true);
}
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpByebye::TimerFired");
}
DWORD CSsdpByebye::DwRun()
{
// Wait for timer to exit before allowing this function to return (which causes delete this to happen)
CLock lock(m_critSec);
m_timer.HrDelete(INVALID_HANDLE_VALUE);
return 0;
}
HRESULT CSsdpService::HrCleanup(BOOL bByebye)
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
hr = m_timer.HrDelete(INVALID_HANDLE_VALUE);
if(SUCCEEDED(hr))
{
if(bByebye)
{
char * szByebye = NULL;
hr = m_strByebye.HrGetMultiByteWithAlloc(&szByebye);
if(SUCCEEDED(hr))
{
// This takes ownership of memory
CSsdpByebye::Create(szByebye);
}
}
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpService::HrCleanup");
return hr;
}
BOOL CSsdpService::FIsPersistent()
{
return (SSDP_SERVICE_PERSISTENT & m_dwFlags) != 0;
}
CSsdpServiceManager CSsdpServiceManager::s_instance;
CSsdpServiceManager::CSsdpServiceManager()
{
}
CSsdpServiceManager::~CSsdpServiceManager()
{
}
CSsdpServiceManager & CSsdpServiceManager::Instance()
{
return s_instance;
}
HRESULT CSsdpServiceManager::HrAddService(
SSDP_MESSAGE * pssdpMsg,
DWORD dwFlags,
PCONTEXT_HANDLE_TYPE * ppContextHandle)
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
if(pssdpMsg && FindServiceByUsn(pssdpMsg->szUSN))
{
hr = E_INVALIDARG;
TraceTag(ttidSsdpAnnounce, "CSsdpServiceManager::HrAddService - attempting to add duplicate USN");
}
CSsdpService * pService = NULL;
if(SUCCEEDED(hr))
{
ServiceList serviceList;
hr = serviceList.HrPushFrontDefault();
if(SUCCEEDED(hr))
{
hr = serviceList.Front().HrInitialize(pssdpMsg, dwFlags, ppContextHandle);
if(SUCCEEDED(hr))
{
hr = serviceList.Front().HrStartTimer();
if(SUCCEEDED(hr))
{
m_serviceList.Prepend(serviceList);
pService = &m_serviceList.Front();
}
}
}
}
if(pService)
{
hr = CSsdpRundownSupport::Instance().HrAddRundownItem(pService);
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrAddService");
return hr;
}
CSsdpService * CSsdpServiceManager::FindServiceByUsn(char * szUSN)
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
CSsdpService * pService = NULL;
ServiceList::Iterator iter;
if(S_OK == m_serviceList.GetIterator(iter))
{
CSsdpService * pServiceIter = NULL;
while(S_OK == iter.HrGetItem(&pServiceIter))
{
if(pServiceIter->FIsUsn(szUSN))
{
pService = pServiceIter;
break;
}
if(S_OK != iter.HrNext())
{
break;
}
}
}
if(!pService)
{
TraceTag(ttidSsdpAnnounce, "CSsdpServiceManager::FindServiceByUsn(%s) - not found!", szUSN);
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::FindServiceByUsn");
return pService;
}
HRESULT CSsdpServiceManager::HrAddSearchResponse(SSDP_REQUEST * pSsdpRequest,
SOCKET * pSocket,
SOCKADDR_IN * pRemoteSocket)
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
ServiceList::Iterator iter;
if(S_OK == m_serviceList.GetIterator(iter))
{
CSsdpService * pService = NULL;
while(S_OK == iter.HrGetItem(&pService))
{
pService->HrAddSearchResponse(pSsdpRequest, pSocket, pRemoteSocket);
if(S_OK != iter.HrNext())
{
break;
}
}
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrAddSearchResponse");
return hr;
}
HRESULT CSsdpServiceManager::HrRemoveService(CSsdpService * pService, BOOL bByebye)
{
HRESULT hr = S_OK;
hr = HrRemoveServiceInternal(pService, bByebye, FALSE);
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrRemoveService");
return hr;
}
HRESULT CSsdpServiceManager::HrServiceRundown(CSsdpService * pService)
{
HRESULT hr = S_OK;
hr = HrRemoveServiceInternal(pService, TRUE, TRUE);
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrCleanupAnouncements");
return hr;
}
HRESULT CSsdpServiceManager::HrCleanupAnouncements()
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
// TODO: Implement this!!!!!!!!!!!!!!!!!!!!!!
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrCleanupAnouncements");
return hr;
}
HRESULT CSsdpServiceManager::HrRemoveServiceInternal(CSsdpService * pService, BOOL bByebye, BOOL bRundown)
{
HRESULT hr = S_OK;
BOOL bFound = FALSE;
ServiceList serviceList;
{
CLock lock(m_critSec);
ServiceList::Iterator iter;
if(S_OK == m_serviceList.GetIterator(iter))
{
CSsdpService * pServiceIter = NULL;
while(S_OK == iter.HrGetItem(&pServiceIter))
{
if(pServiceIter == pService)
{
if(!bRundown || !pService->FIsPersistent())
{
iter.HrMoveToList(serviceList);
bFound = TRUE;
}
break;
}
if(S_OK != iter.HrNext())
{
break;
}
}
}
}
if(bFound)
{
ServiceList::Iterator iter;
if(S_OK == serviceList.GetIterator(iter))
{
CSsdpService * pServiceIter = NULL;
while(S_OK == iter.HrGetItem(&pServiceIter))
{
if(!bRundown)
{
CSsdpRundownSupport::Instance().RemoveRundownItem(pServiceIter);
}
hr = pServiceIter->HrCleanup(bByebye);
if(S_OK != iter.HrNext())
{
break;
}
}
}
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "CSsdpServiceManager::HrRemoveServiceInternal");
return hr;
}
HRESULT HrInitializeSsdpRequestFromMessage(
SSDP_REQUEST * pRequest,
const SSDP_MESSAGE *pMsg)
{
HRESULT hr = S_OK;
ZeroMemory(pRequest, sizeof(*pRequest));
pRequest->Method = SSDP_NOTIFY;
pRequest->Headers[SSDP_HOST] = reinterpret_cast<char*>(
midl_user_allocate(sizeof(CHAR) * (
lstrlenA(SSDP_ADDR) +
1 + // colon
6 + // port number
1)));
if (!pRequest->Headers[SSDP_HOST])
{
hr = E_OUTOFMEMORY;
}
if(SUCCEEDED(hr))
{
wsprintfA(pRequest->Headers[SSDP_HOST], "%s:%d", SSDP_ADDR, SSDP_PORT);
hr = HrCopyString(pMsg->szType, &pRequest->Headers[SSDP_NT]);
if(SUCCEEDED(hr) || !pMsg->szType)
{
hr = HrCopyString(pMsg->szUSN, &pRequest->Headers[SSDP_USN]);
if(SUCCEEDED(hr) || !pMsg->szUSN)
{
hr = HrCopyString(pMsg->szAltHeaders, &pRequest->Headers[SSDP_AL]);
if(SUCCEEDED(hr) || !pMsg->szAltHeaders)
{
hr = HrCopyString(pMsg->szLocHeader, &pRequest->Headers[SSDP_LOCATION]);
if(SUCCEEDED(hr) || pMsg->szLocHeader)
{
char szBuf[256];
wsprintfA(szBuf, "max-age=%d", pMsg->iLifeTime);
hr = HrCopyString(szBuf, &pRequest->Headers[SSDP_CACHECONTROL]);
}
}
}
}
}
if(FAILED(hr))
{
FreeSsdpRequest(pRequest);
ZeroMemory(pRequest, sizeof(*pRequest));
}
TraceHr(ttidSsdpAnnounce, FAL, hr, FALSE, "HrInitializeSsdpRequestFromMessage");
return hr;
}