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.
 
 
 
 
 
 

775 lines
21 KiB

#include <pch.h>
#pragma hdrstop
#include <limits.h>
#include "ssdpfunc.h"
#include "ssdpnetwork.h"
#include "ncbase.h"
#include "cache.h"
#include "upthread.h"
#include "notify.h"
#define BUF_SIZE 100
#define CACHE_RESULT_SIZE 2
static const int g_cMaxCacheDefault = 1000; // default max size of SSDP cache
static const int g_cMaxCacheMin = 10; // min allowed max size of SSDP cache
static const int g_cMaxCacheMax = 30000; // max allowed max size of SSDP cache
CSsdpCacheEntry::CSsdpCacheEntry() : m_timer(*this), m_bTimerFired(FALSE)
{
ZeroMemory(&m_ssdpRequest, sizeof(m_ssdpRequest));
}
CSsdpCacheEntry::~CSsdpCacheEntry()
{
FreeSsdpRequest(&m_ssdpRequest);
}
class CSsdpCacheEntryByebye : public CWorkItem
{
public:
static HRESULT HrCreate(CSsdpCacheEntry * pEntry);
private:
CSsdpCacheEntryByebye(CSsdpCacheEntry * pEntry);
~CSsdpCacheEntryByebye();
CSsdpCacheEntryByebye(const CSsdpCacheEntryByebye &);
CSsdpCacheEntryByebye & operator=(const CSsdpCacheEntryByebye &);
DWORD DwRun();
CSsdpCacheEntry * m_pEntry;
};
CSsdpCacheEntryByebye::CSsdpCacheEntryByebye(CSsdpCacheEntry * pEntry)
: m_pEntry(pEntry)
{
}
CSsdpCacheEntryByebye::~CSsdpCacheEntryByebye()
{
}
HRESULT CSsdpCacheEntryByebye::HrCreate(CSsdpCacheEntry * pEntry)
{
HRESULT hr = S_OK;
CSsdpCacheEntryByebye * pByebye = new CSsdpCacheEntryByebye(pEntry);
if(!pByebye)
{
hr = E_OUTOFMEMORY;
}
if(SUCCEEDED(hr))
{
hr = pByebye->HrStart(TRUE);
if(FAILED(hr))
{
delete pByebye;
}
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntryByebye::HrCreate");
return hr;
}
DWORD CSsdpCacheEntryByebye::DwRun()
{
HRESULT hr = S_OK;
hr = CSsdpCacheEntryManager::Instance().HrRemoveEntry(m_pEntry);
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntryByebye::DwRun");
return 0;
}
void CSsdpCacheEntry::TimerFired()
{
HRESULT hr = S_OK;
if(!ConvertToByebyeNotify(&m_ssdpRequest))
{
hr = E_FAIL;
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntry::TimerFired - ConvertToByebyeNotify failed!");
}
// Mark that the timer has fired in case a renew comes in
m_bTimerFired = TRUE;
if(SUCCEEDED(hr))
{
// Queue the delete to happen later
hr = CSsdpCacheEntryByebye::HrCreate(this);
}
}
BOOL CSsdpCacheEntry::TimerTryToLock()
{
return m_critSec.FTryEnter();
}
void CSsdpCacheEntry::TimerUnlock()
{
m_critSec.Leave();
}
HRESULT CSsdpCacheEntry::HrInitialize(const SSDP_REQUEST * pRequest)
{
HRESULT hr = S_OK;
if(!CopySsdpRequest(&m_ssdpRequest, pRequest))
{
hr = E_FAIL;
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntry::HrInitialize - CopySsdpRequest failed!");
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntry::HrInitialize");
return hr;
}
HRESULT CSsdpCacheEntry::HrStartTimer(const SSDP_REQUEST * pRequest)
{
HRESULT hr = S_OK;
hr = HrUpdateExpireTime(pRequest);
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntry::HrStartTimer");
return hr;
}
BOOL CSsdpCacheEntry::FTimerFired()
{
CLock lock(m_critSec);
return m_bTimerFired;
}
HRESULT CSsdpCacheEntry::HrShutdown(BOOL bExpired)
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
Assert(FImplies(bExpired, m_bTimerFired));
hr = m_timer.HrDelete(INVALID_HANDLE_VALUE);
if(bExpired)
{
hr = CSsdpNotifyRequestManager::Instance().HrCheckListNotifyForAliveOrByebye(&m_ssdpRequest);
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntry::HrShutdown");
return hr;
}
HRESULT CSsdpCacheEntry::HrUpdateExpireTime(const SSDP_REQUEST * pRequest)
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
if(!pRequest->Headers[SSDP_CACHECONTROL])
{
hr = E_INVALIDARG;
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntry::HrUpdateExpireTime - No cache-control header");
}
if(SUCCEEDED(hr))
{
DWORD dwTimeout = GetMaxAgeFromCacheControl(pRequest->Headers[SSDP_CACHECONTROL]);
hr = m_timer.HrResetTimer(dwTimeout * 1000);
m_bTimerFired = FALSE;
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntry::HrUpdateExpireTime");
return hr;
}
HRESULT CSsdpCacheEntry::HrUpdateEntry(const SSDP_REQUEST * pRequest, BOOL * pbCheckListNotify)
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
hr = HrUpdateExpireTime(pRequest);
if(S_OK == hr)
{
if(!CompareSsdpRequest(&m_ssdpRequest, pRequest))
{
*pbCheckListNotify = TRUE;
// Requests don't match. Check location headers. If different,
// then we need to fake a byebye then allow the new alive to be
// notified
if (lstrcmpi(m_ssdpRequest.Headers[SSDP_LOCATION],
pRequest->Headers[SSDP_LOCATION]))
{
if(!ConvertToByebyeNotify(&m_ssdpRequest))
{
hr = E_FAIL;
TraceHr(ttidSsdpCache, FAL, hr, FALSE,
"CSsdpCacheEntry::HrUpdateEntry - "
"ConvertToByebyeNotify failed!");
}
else
{
hr = CSsdpNotifyRequestManager::Instance().HrCheckListNotifyForAliveOrByebye(&m_ssdpRequest);
}
}
}
FreeSsdpRequest(&m_ssdpRequest);
if(!CopySsdpRequest(&m_ssdpRequest, pRequest))
{
hr = E_OUTOFMEMORY;
}
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntry::HrUpdateEntry");
return hr;
}
BOOL CSsdpCacheEntry::FIsMatchUSN(const char * szUSN)
{
CLock lock(m_critSec);
return 0 == lstrcmpA(m_ssdpRequest.Headers[SSDP_USN], szUSN);
}
BOOL CSsdpCacheEntry::FIsSearchMatch(const char * szType)
{
CLock lock(m_critSec);
if(0 == lstrcmpiA(szType, "ssdp:all"))
{
return TRUE;
}
if(m_ssdpRequest.Headers[SSDP_NT] &&
0 == lstrcmpiA(m_ssdpRequest.Headers[SSDP_NT], szType))
{
return TRUE;
}
if(m_ssdpRequest.Headers[SSDP_ST] &&
0 == lstrcmpiA(m_ssdpRequest.Headers[SSDP_ST], szType))
{
return TRUE;
}
return FALSE;
}
HRESULT CSsdpCacheEntry::HrGetRequest(SSDP_REQUEST * pRequest)
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
if(!CopySsdpRequest(pRequest, &m_ssdpRequest))
{
hr = E_OUTOFMEMORY;
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntry::HrCacheEntryExpired");
return hr;
}
void CSsdpCacheEntry::PrintItem()
{
TraceTag(ttidSsdpCache, " %s - %s - %s",
m_ssdpRequest.Headers[SSDP_USN],
m_ssdpRequest.Headers[SSDP_NT],
m_ssdpRequest.Headers[SSDP_LOCATION]);
}
BOOL CSsdpCacheEntry::FCheckForDirtyInterfaceGuids(long nCount, GUID * arGuidInterfaces)
{
BOOL bDirty = FALSE;
HRESULT hr = S_OK;
CLock lock(m_critSec);
for(long n = 0; n < nCount; ++n)
{
if(arGuidInterfaces[n] == m_ssdpRequest.guidInterface)
{
bDirty = TRUE;
if(ConvertToByebyeNotify(&m_ssdpRequest))
{
hr = CSsdpNotifyRequestManager::Instance().HrCheckListNotifyForAliveOrByebye(&m_ssdpRequest);
}
else
{
TraceTag(ttidError, "CSsdpCacheEntry::FCheckForDirtyInterfaceGuids - ConvertToByebyeNotify failed");
}
break;
}
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntry::FCheckForDirtyInterfaceGuids");
return bDirty;
}
CSsdpCacheEntryManager CSsdpCacheEntryManager::s_instance;
CSsdpCacheEntryManager::CSsdpCacheEntryManager():m_cCacheEntries(0), m_cMaxCacheEntries(1000)
{
}
CSsdpCacheEntryManager::~CSsdpCacheEntryManager()
{
}
HRESULT CSsdpCacheEntryManager::HrInitialize()
{
HRESULT hr = S_OK;
HKEY hkey;
DWORD dwMaxCache = g_cMaxCacheDefault;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
"SYSTEM\\CurrentControlSet\\Services"
"\\SSDPSRV\\Parameters", 0,
KEY_READ, &hkey))
{
DWORD cbSize = sizeof(DWORD);
// ignore failure. In that case, we'll use default
(VOID) RegQueryValueEx(hkey, "MaxCache", NULL, NULL, (BYTE *)&dwMaxCache, &cbSize);
RegCloseKey(hkey);
}
dwMaxCache = max(dwMaxCache, g_cMaxCacheMin);
dwMaxCache = min(dwMaxCache, g_cMaxCacheMax);
m_cMaxCacheEntries = dwMaxCache;
m_cCacheEntries = 0;
TraceTag(ttidSsdpCache, "CSsdpCacheEntryManager::HrInitialize - Max Cache %d",m_cMaxCacheEntries);
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntryManager::HrInitialize");
return hr;
}
CSsdpCacheEntryManager & CSsdpCacheEntryManager::Instance()
{
return s_instance;
}
BOOL CSsdpCacheEntryManager::IsCacheListNotFull()
{
CLock lock(m_critSec);
if (m_cCacheEntries < m_cMaxCacheEntries)
return TRUE;
else
return FALSE;
}
HRESULT CSsdpCacheEntryManager::HrShutdown()
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
CacheEntryList::Iterator iter;
if(S_OK == m_cacheEntryList.GetIterator(iter))
{
CSsdpCacheEntry * pEntryIter = NULL;
while(S_OK == iter.HrGetItem(&pEntryIter))
{
hr = pEntryIter->HrShutdown(FALSE);
if(S_OK != iter.HrErase())
{
break;
}
}
}
m_cCacheEntries = 0;
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntryManager::HrShutdown");
return hr;
}
HRESULT CSsdpCacheEntryManager::HrRemoveEntry(CSsdpCacheEntry * pEntry)
{
HRESULT hr = S_OK;
CacheEntryList cacheEntryListRemove;
{
CLock lock(m_critSec);
CacheEntryList::Iterator iter;
if(S_OK == m_cacheEntryList.GetIterator(iter))
{
CSsdpCacheEntry * pEntryIter = NULL;
while(S_OK == iter.HrGetItem(&pEntryIter))
{
if(pEntryIter == pEntry)
{
if(pEntry->FTimerFired())
{
iter.HrMoveToList(cacheEntryListRemove);
m_cCacheEntries--;
TraceTag(ttidSsdpCache, "CSsdpCacheEntryManager::HrRemoveEntry - Cache Entries %d", m_cCacheEntries);
}
break;
}
if(S_OK != iter.HrNext())
{
break;
}
}
}
}
// Remove outside of lock
CacheEntryList::Iterator iter;
if(S_OK == cacheEntryListRemove.GetIterator(iter))
{
CSsdpCacheEntry * pEntryIter = NULL;
while(S_OK == iter.HrGetItem(&pEntryIter))
{
hr = pEntryIter->HrShutdown(TRUE);
if(S_OK != iter.HrNext())
{
break;
}
}
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntryManager::HrRemoveEntry");
return hr;
}
#ifdef DBG
#define CACHECONTENTTRACE DBG
#endif // DBG
HRESULT CSsdpCacheEntryManager::HrUpdateCacheList(const SSDP_REQUEST * pRequest, BOOL bIsSubscribed)
{
HRESULT hr = S_OK;
BOOL bIsByebye = FALSE;
BOOL bFound = FALSE;
// Debugging only
#if CACHECONTENTTRACE
BOOL bAdded = FALSE;
BOOL bRemoved = FALSE;
#endif //CACHECONTENTTRACE
BOOL bCheckListNotify = FALSE;
CacheEntryList cacheEntryListRemove;
if(pRequest->Headers[SSDP_NTS] &&
0 == lstrcmpA(pRequest->Headers[SSDP_NTS], "ssdp:byebye"))
{
bIsByebye = TRUE;
}
if(!bIsByebye && !pRequest->Headers[SSDP_CACHECONTROL])
{
// Not cacheable
hr = S_FALSE;
if(S_OK == hr)
{
if(!bIsByebye && !pRequest->Headers[SSDP_SERVER])
{
// No server header
hr = S_FALSE;
}
}
}
{
CLock lock(m_critSec);
if(S_OK == hr)
{
CacheEntryList::Iterator iter;
if(S_OK == m_cacheEntryList.GetIterator(iter))
{
CSsdpCacheEntry * pEntryIter = NULL;
while(S_OK == iter.HrGetItem(&pEntryIter))
{
if(pEntryIter->FIsMatchUSN(pRequest->Headers[SSDP_USN]))
{
bFound = TRUE;
if(bIsByebye)
{
bCheckListNotify = TRUE;
iter.HrMoveToList(cacheEntryListRemove);
m_cCacheEntries--;
TraceTag(ttidSsdpCache, "CSsdpCacheEntryManager::HrUpdateCacheList - Bye Bye - Cache Entries %d", m_cCacheEntries);
#if CACHECONTENTTRACE
bRemoved = TRUE;
#endif //CACHECONTENTTRACE
}
else
{
hr = pEntryIter->HrUpdateEntry(pRequest, &bCheckListNotify);
}
break;
}
if(S_OK != iter.HrNext())
{
break;
}
}
}
}
if(S_OK == hr && !bFound && ! bIsByebye && bIsSubscribed)
{
if(IsCacheListNotFull())
{
CacheEntryList cacheEntryList;
hr = cacheEntryList.HrPushFrontDefault();
if(SUCCEEDED(hr))
{
hr = cacheEntryList.Front().HrInitialize(pRequest);
if(SUCCEEDED(hr))
{
bCheckListNotify = TRUE;
hr = cacheEntryList.Front().HrStartTimer(pRequest);
if(SUCCEEDED(hr))
{
m_cacheEntryList.Prepend(cacheEntryList);
m_cCacheEntries++;
TraceTag(ttidSsdpCache, "CSsdpCacheEntryManager::HrUpdateCacheList - Cache Entries %d", m_cCacheEntries);
#if CACHECONTENTTRACE
bAdded = TRUE;
#endif //CACHECONTENTTRACE
}
}
}
}
else
{
TraceTag(ttidSsdpCache, "CSsdpCacheEntryManager::HrUpdateCacheList - Cache Entries Reached MAX");
}
}
#if CACHECONTENTTRACE
if(bAdded || bRemoved)
{
TraceTag(ttidSsdpCache, "CSsdpCacheEntryManager::HrUpdateCacheList - Cache Contents");
long nCount = 0;
CacheEntryList::Iterator iter;
if(S_OK == m_cacheEntryList.GetIterator(iter))
{
CSsdpCacheEntry * pEntryIter = NULL;
while(S_OK == iter.HrGetItem(&pEntryIter))
{
pEntryIter->PrintItem();
++nCount;
if(S_OK != iter.HrNext())
{
break;
}
}
}
TraceTag(ttidSsdpCache, "CSsdpCacheEntryManager::HrUpdateCacheList - Cache Content Count: %d", nCount);
if(bAdded)
{
TraceTag(ttidSsdpCache, "CSsdpCacheEntryManager::HrUpdateCacheList - Added: %s", pRequest->Headers[SSDP_USN]);
}
if(bRemoved)
{
TraceTag(ttidSsdpCache, "CSsdpCacheEntryManager::HrUpdateCacheList - Removed: %s", pRequest->Headers[SSDP_USN]);
}
}
#endif // CACHECONTENTTRACE
}
if(bCheckListNotify)
{
CSsdpNotifyRequestManager::Instance().HrCheckListNotifyForAliveOrByebye(pRequest);
}
// Remove outside of lock
CacheEntryList::Iterator iter;
if(S_OK == cacheEntryListRemove.GetIterator(iter))
{
CSsdpCacheEntry * pEntryIter = NULL;
while(S_OK == iter.HrGetItem(&pEntryIter))
{
hr = pEntryIter->HrShutdown(FALSE);
if(S_OK != iter.HrNext())
{
break;
}
}
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntryManager::HrUpdateCacheList");
return hr;
}
HRESULT CSsdpCacheEntryManager::HrSearchListCache(char * szType, MessageList ** ppSvcList)
{
HRESULT hr = S_OK;
CLock lock(m_critSec);
*ppSvcList = NULL;
typedef CUList<SSDP_REQUEST> RequestList;
RequestList requestList;
SSDP_REQUEST ssdpRequest;
long nItems = 0;
CacheEntryList::Iterator iter;
if(S_OK == m_cacheEntryList.GetIterator(iter))
{
CSsdpCacheEntry * pEntryIter = NULL;
while(S_OK == iter.HrGetItem(&pEntryIter))
{
if(pEntryIter->FIsSearchMatch(szType))
{
hr = pEntryIter->HrGetRequest(&ssdpRequest);
if(SUCCEEDED(hr))
{
hr = requestList.HrPushFront(ssdpRequest);
if(SUCCEEDED(hr))
{
++nItems;
}
if(FAILED(hr))
{
FreeSsdpRequest(&ssdpRequest);
}
}
if(FAILED(hr))
{
break;
}
}
if(S_OK != iter.HrNext())
{
break;
}
}
}
if(SUCCEEDED(hr))
{
MessageList * pSvcList = new MessageList;
if(!pSvcList)
{
hr = E_OUTOFMEMORY;
}
if(SUCCEEDED(hr))
{
if(nItems)
{
pSvcList->list = new SSDP_REQUEST[nItems];
if(!pSvcList->list)
{
hr = E_OUTOFMEMORY;
}
else
{
ZeroMemory(pSvcList->list, sizeof(SSDP_REQUEST) * nItems);
}
}
else
{
pSvcList->list = NULL;
}
pSvcList->size = nItems;
if(SUCCEEDED(hr))
{
RequestList::Iterator iter;
if(S_OK == requestList.GetIterator(iter))
{
SSDP_REQUEST * pRequestDst = pSvcList->list;
SSDP_REQUEST * pRequest = NULL;
while(S_OK == iter.HrGetItem(&pRequest))
{
CopyMemory(pRequestDst, pRequest, sizeof(SSDP_REQUEST));
++pRequestDst;
if(S_OK != iter.HrNext())
{
break;
}
}
}
*ppSvcList = pSvcList;
}
}
}
if(FAILED(hr))
{
RequestList::Iterator iter;
if(S_OK == requestList.GetIterator(iter))
{
SSDP_REQUEST * pRequest = NULL;
while(S_OK == iter.HrGetItem(&pRequest))
{
FreeSsdpRequest(pRequest);
if(S_OK != iter.HrNext())
{
break;
}
}
}
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntryManager::HrSearchListCache");
return hr;
}
HRESULT CSsdpCacheEntryManager::HrClearDirtyInterfaceGuids(long nCount, GUID * arGuidInterfaces)
{
HRESULT hr = S_OK;
CacheEntryList cacheEntryListRemove;
{
CLock lock(m_critSec);
CacheEntryList::Iterator iter;
if(S_OK == m_cacheEntryList.GetIterator(iter))
{
CSsdpCacheEntry * pEntryIter = NULL;
while(S_OK == iter.HrGetItem(&pEntryIter))
{
if(pEntryIter->FCheckForDirtyInterfaceGuids(nCount, arGuidInterfaces))
{
m_cCacheEntries--;
TraceTag(ttidSsdpCache, "CSsdpCacheEntryManager::HrClearDirtyInterfaceGuids - Cache Entries %d", m_cCacheEntries);
if(S_OK != iter.HrMoveToList(cacheEntryListRemove))
{
break;
}
}
if(S_OK != iter.HrNext())
{
break;
}
}
}
}
// Remove outside of lock
CacheEntryList::Iterator iter;
if(S_OK == cacheEntryListRemove.GetIterator(iter))
{
CSsdpCacheEntry * pEntryIter = NULL;
while(S_OK == iter.HrGetItem(&pEntryIter))
{
hr = pEntryIter->HrShutdown(FALSE);
if(S_OK != iter.HrNext())
{
break;
}
}
}
TraceHr(ttidSsdpCache, FAL, hr, FALSE, "CSsdpCacheEntryManager::HrClearDirtyInterfaceGuids");
return hr;
}