📄 cache.cpp
字号:
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#include <ssdppch.h>
#pragma hdrstop
#define BUF_SIZE 100
#define CACHE_RESULT_SIZE 2
// To-Do: Set cache size limit and clean up cache when it reaches the limit.
// USN is the key to the list, no duplicate USNs.
static LIST_ENTRY listCache;
static CRITICAL_SECTION CSListCache;
static CTETimer timerCleanup;
static PSSDP_CACHE_ENTRY pCacheEntryFirst;
#ifndef UNDER_CE
static LPCTSTR g_szSystemDir = TEXT("windir");
#else
static const TCHAR g_szWinDir[] = TEXT("\\windows");
#endif
static LPCTSTR g_szCacheFileName = TEXT("\\ssdpcache.txt");
BOOL CacheEntryExpired(SSDP_CACHE_ENTRY *CacheEntry);
VOID FreeSsdpCacheEntry(PSSDP_CACHE_ENTRY CacheEntry);
VOID PrintListCache();
VOID RemoveFromListCache(PSSDP_CACHE_ENTRY CacheEntry, PSSDP_REQUEST pSsdpRequest);
VOID RemoveEntryWithNotification(PSSDP_CACHE_ENTRY CacheEntry, PSSDP_REQUEST pSsdpRequest);
VOID FileTimeToString(FILETIME FileTime, CHAR *szBuf, INT BufSize);
VOID CacheTimerProc (CTETimer *Timer, VOID *Arg)
{
PSSDP_CACHE_ENTRY pCacheEntryExpired = (PSSDP_CACHE_ENTRY) Arg;
TraceTag(ttidSsdpCache, "+Enter CacheTimerProc %x", pCacheEntryExpired);
EnterCriticalSection(&CSListCache);
ConvertToByebyeNotify(&pCacheEntryExpired->SsdpRequest);
if (pCacheEntryExpired == pCacheEntryFirst)
{
TraceTag(ttidSsdpCache, "+Expired entry %x is the current ealiest %x",
pCacheEntryExpired, pCacheEntryFirst);
RemoveFromListCache(pCacheEntryExpired, &pCacheEntryExpired->SsdpRequest);
} else
{
// This can only happen if update occurred while we are in this proc,
// but before EnterCriticalSection. The update was treated as adding
// a new item and a cleanup timer is already restarted as a result.
TraceTag(ttidSsdpCache, "+Expired entry %x is not the current ealiest %x",
pCacheEntryExpired, pCacheEntryFirst);
RemoveEntryWithNotification(pCacheEntryExpired, &pCacheEntryExpired->SsdpRequest);
}
LeaveCriticalSection(&CSListCache);
TraceTag(ttidSsdpCache, "-Leave CacheTimerProc %x", pCacheEntryExpired);
}
VOID InitializeListCache()
{
InitializeCriticalSection(&CSListCache);
EnterCriticalSection(&CSListCache);
InitializeListHead(&listCache);
CTEInitTimer(&timerCleanup);
pCacheEntryFirst = NULL;
LeaveCriticalSection(&CSListCache);
}
// This should only be called during service shut down.
// Call CleanupListCache to clean up the cache while service is running.
VOID DestroyListCache()
{
CTEStopTimer(&timerCleanup);
CleanupListCache();
DeleteCriticalSection(&CSListCache);
}
#if 0
VOID CheckListCacheForNotification(PSSDP_NOTIFY_REQUEST pNotifyRequest)
{
PLIST_ENTRY p;
PLIST_ENTRY pListHead = &listCache;
TraceTag(ttidSsdpCache, "----- Check List Cache for Notification -----");
EnterCriticalSection(&CSListCache);
for (p = pListHead->Flink; p != pListHead;)
{
PSSDP_CACHE_ENTRY CacheEntry;
CacheEntry = CONTAINING_RECORD (p, SSDP_CACHE_ENTRY, linkage);
p = p->Flink;
if (IsMatchingAliveByebye(pNotifyRequest, &CacheEntry->SsdpRequest) == TRUE)
{
QueuePendingNotification(pNotifyRequest, &CacheEntry->SsdpRequest);
}
}
LeaveCriticalSection(&CSListCache);
}
#endif
VOID CleanupListCache()
{
PLIST_ENTRY p;
PLIST_ENTRY pListHead = &listCache;
TraceTag(ttidSsdpCache, "----- Cleanup SSDP Cache List -----");
EnterCriticalSection(&CSListCache);
for (p = pListHead->Flink; p != pListHead;)
{
PSSDP_CACHE_ENTRY CacheEntry;
CacheEntry = CONTAINING_RECORD (p, SSDP_CACHE_ENTRY, linkage);
p = p->Flink;
RemoveEntryList(&(CacheEntry->linkage));
FreeSsdpCacheEntry(CacheEntry);
}
LeaveCriticalSection(&CSListCache);
// To-Do: Stop the cache cleanup timer.
}
BOOL UpdateExpireTime(PSSDP_CACHE_ENTRY CacheEntry, SSDP_REQUEST *SsdpRequest)
{
INT Temp;
FILETIME TempTime;
#ifdef DBG
CHAR szBuf[BUF_SIZE];
#endif // DBG
Temp = GetMaxAgeFromCacheControl(SsdpRequest->Headers[SSDP_CACHECONTROL]);
if (Temp <= 0)
{
TraceTag(ttidSsdpCache, "Negative value of max_age %s",
SsdpRequest->Headers[SSDP_CACHECONTROL]);
return FALSE;
}
GetSystemTimeAsFileTime(&TempTime);
// FILETIME is in 100 nano seconds, max-age is in seconds.
CacheEntry->ExpireTime = ULONGLONG_FROM_FILETIME(TempTime) + (((ULONGLONG) Temp) * 10000000);
#ifdef DBG
FileTimeToString(TempTime, szBuf, BUF_SIZE);
TraceTag(ttidSsdpCache, "Current Time is %s", szBuf);
FileTimeToString(FILETIME_FROM_ULONGLONG(CacheEntry->ExpireTime), szBuf, BUF_SIZE);
TraceTag(ttidSsdpCache, "Expire Time is %s", szBuf);
#endif //DBG
return TRUE;
}
SSDP_CACHE_ENTRY *CreateCacheEntryFromRequest(SSDP_REQUEST *SsdpRequest, ULONGLONG ExpireTime)
{
PSSDP_CACHE_ENTRY CacheEntry;
INT Size;
Size = sizeof(SSDP_CACHE_ENTRY);
CacheEntry = (PSSDP_CACHE_ENTRY) malloc (Size);
if (CacheEntry == NULL)
{
TraceTag(ttidSsdpCache, "Couldn't allocate memory for CacheEntry "
"of %s", SsdpRequest->Headers[SSDP_NT]);
return NULL;
}
CacheEntry->Type = SSDP_CACHE_ENTRY_SIGNATURE;
CacheEntry->Size = Size;
if (ExpireTime != 0)
{
CacheEntry->ExpireTime = ExpireTime;
} else {
if (UpdateExpireTime(CacheEntry, SsdpRequest) == FALSE)
{
free(CacheEntry);
return NULL;
}
}
CacheEntry->SsdpRequest = (* SsdpRequest);
return CacheEntry;
}
VOID FreeSsdpCacheEntry(PSSDP_CACHE_ENTRY CacheEntry)
{
Assert(CacheEntry);
FreeSsdpRequest(&CacheEntry->SsdpRequest);
free(CacheEntry);
}
// Pre-Condition: The lock for cache list is held.
BOOL UpdateCacheEntry(PSSDP_CACHE_ENTRY pCacheEntry, SSDP_REQUEST *pSsdpRequest)
{
if (UpdateExpireTime(pCacheEntry, pSsdpRequest) == FALSE)
{
return FALSE;
}
if (CompareSsdpRequest(&pCacheEntry->SsdpRequest, pSsdpRequest) == FALSE)
{
CheckListNotifyForAliveByebye(pSsdpRequest);
}
FreeSsdpRequest(&pCacheEntry->SsdpRequest);
pCacheEntry->SsdpRequest = (* pSsdpRequest);
return TRUE;
}
VOID AddToListCache(PSSDP_CACHE_ENTRY pCacheEntry)
{
TraceTag(ttidSsdpCache, "+Enter AddToListCache %x", pCacheEntry);
EnterCriticalSection(&CSListCache);
if (IsListEmpty(&listCache))
{
ULONGLONG Timeout;
FILETIME ftCurrent;
VOID *pTimer;
#ifdef DBG
CHAR szBuf[BUF_SIZE];
#endif
TraceTag(ttidSsdpCache, "The list is empty when adding %x", pCacheEntry);
GetSystemTimeAsFileTime(&ftCurrent);
#ifdef DBG
FileTimeToString(ftCurrent, szBuf, BUF_SIZE);
TraceTag(ttidSsdpCache, "Current Time is %s", szBuf);
FileTimeToString(FILETIME_FROM_ULONGLONG(pCacheEntry->ExpireTime), szBuf, BUF_SIZE);
TraceTag(ttidSsdpCache, "Expire Time is %s", szBuf);
#endif
if (pCacheEntry->ExpireTime > ULONGLONG_FROM_FILETIME(ftCurrent))
{
TraceTag(ttidSsdpCache, "%x has not expired, insert to list", pCacheEntry);
InsertHeadList(&listCache, &(pCacheEntry->linkage));
Timeout = (pCacheEntry->ExpireTime - ULONGLONG_FROM_FILETIME(ftCurrent))/10000;
pCacheEntryFirst = pCacheEntry;
// currently CTEStartTimer only takes 32 bit timeouts
pTimer = CTEStartTimer(&timerCleanup, (ULONG)Timeout, CacheTimerProc,
(VOID *) pCacheEntry);
// Check return value of CTEStartTimer.
// Queue notifications
CheckListNotifyForAliveByebye(&pCacheEntry->SsdpRequest);
} else {
TraceTag(ttidSsdpCache, "%x already expired, no need to add", pCacheEntry);
}
} else
{
TraceTag(ttidSsdpCache, "The list is NOT empty when adding %x", pCacheEntry);
if (pCacheEntry->ExpireTime >= pCacheEntryFirst->ExpireTime)
{
TraceTag(ttidSsdpCache, "%x is not ealier than the current earliest, just insert", pCacheEntry);
InsertHeadList(&listCache, &(pCacheEntry->linkage));
CheckListNotifyForAliveByebye(&pCacheEntry->SsdpRequest);
} else {
TraceTag(ttidSsdpCache, "%x is earlier than the current earliest", pCacheEntry);
if (CTEStopTimer(&timerCleanup))
{
ULONGLONG Timeout;
FILETIME ftCurrent;
VOID *pTimer;
TraceTag(ttidSsdpCache, "Successfully stopped the cleanup timer", pCacheEntry);
InsertHeadList(&listCache, &(pCacheEntry->linkage));
CheckListNotifyForAliveByebye(&pCacheEntry->SsdpRequest);
GetSystemTimeAsFileTime(&ftCurrent);
if (pCacheEntry->ExpireTime > ULONGLONG_FROM_FILETIME(ftCurrent))
{
Timeout = (pCacheEntry->ExpireTime - ULONGLONG_FROM_FILETIME(ftCurrent))/10000;
} else
{
Timeout = 0;
}
pCacheEntryFirst = pCacheEntry;
// currently CTEStartTimer only takes 32 bit timeouts
pTimer = CTEStartTimer(&timerCleanup, (ULONG)Timeout, CacheTimerProc,
(VOID *) pCacheEntry);
} else {
// The timer proc is running and I am ealier than the expired entry,
// so I am expired as well, thus ignore
TraceTag(ttidSsdpCache, "Failed to stop the cleanup timer for %x, skip add", pCacheEntry);
}
}
}
LeaveCriticalSection(&CSListCache);
TraceTag(ttidSsdpCache, "-Leave AddToListCache %x.", pCacheEntry);
}
VOID RemoveEntryWithNotification(PSSDP_CACHE_ENTRY CacheEntry, PSSDP_REQUEST pSsdpRequest)
{
RemoveEntryList(&(CacheEntry->linkage));
CheckListNotifyForAliveByebye(pSsdpRequest);
FreeSsdpCacheEntry(CacheEntry);
}
VOID StartCacheCleanupTimer()
{
EnterCriticalSection(&CSListCache);
if (IsListEmpty(&listCache)) {
TraceTag(ttidSsdpCache, "No more cache entry left.");
} else
{
ULONGLONG ullFirst = _UI64_MAX;
PLIST_ENTRY p;
PLIST_ENTRY pListHead = &listCache;
TraceTag(ttidSsdpCache, "Cache list is not empty after removing");
for (p = pListHead->Flink; p != pListHead;)
{
PSSDP_CACHE_ENTRY CacheEntry;
CacheEntry = CONTAINING_RECORD (p, SSDP_CACHE_ENTRY, linkage);
p = p->Flink;
if (CacheEntry->ExpireTime <= ullFirst) {
ullFirst = CacheEntry->ExpireTime;
pCacheEntryFirst = CacheEntry;
}
}
TraceTag(ttidSsdpCache, "The new earliest cache entry is %x", pCacheEntryFirst);
Assert(pCacheEntryFirst != NULL);
ULONGLONG Timeout;
FILETIME ftCurrent;
VOID *pTimer;
GetSystemTimeAsFileTime(&ftCurrent);
if (ullFirst > ULONGLONG_FROM_FILETIME(ftCurrent))
{
Timeout = (ullFirst - ULONGLONG_FROM_FILETIME(ftCurrent))/10000;
} else {
// Expired
Timeout = 0;
}
// currently CTEStartTimer only takes 32 bit timeouts
pTimer = CTEStartTimer(&timerCleanup, (ULONG)Timeout,CacheTimerProc,
(VOID *) pCacheEntryFirst);
}
LeaveCriticalSection(&CSListCache);
}
VOID RemoveFromListCache(PSSDP_CACHE_ENTRY CacheEntry, PSSDP_REQUEST pSsdpRequest)
{
TraceTag(ttidSsdpCache, "+Enter RemoveFromListCache %x", CacheEntry);
EnterCriticalSection(&CSListCache);
RemoveEntryWithNotification(CacheEntry, pSsdpRequest);
pCacheEntryFirst = NULL;
StartCacheCleanupTimer();
LeaveCriticalSection(&CSListCache);
}
//+---------------------------------------------------------------------------
//
// Function: UpdateListCache
//
// Purpose: Update the cache with ssdp:alive messagesand M-SEARCH results
//
// Arguments:
//
// Returns: TRUE if listCache takes ownership of the memory within
// SsdpRequest; FALSE to have caller free the memory.
//
// Author: tingcai 22 Nov 1999
//
// Notes:
//
// If no CacheControl header, don't cache.
// If exists in cache, update if alive or search result, remove if byebye,
// If not in cache, add to the cache if subscribed and it is alive
// Don't do bRetVal = FALSE, in case you have to, make sure you don't
// override a TRUE;
BOOL UpdateListCache(PSSDP_REQUEST pSsdpRequest, BOOL IsSubscribed)
{
PLIST_ENTRY p;
LIST_ENTRY *pListHead = &listCache;
BOOL found = FALSE;
BOOL IsByebye;
BOOL bRetVal = FALSE;
if (pSsdpRequest->Headers[SSDP_NTS] != NULL &&
strcmp(pSsdpRequest->Headers[SSDP_NTS], "ssdp:byebye") == 0)
{
IsByebye = TRUE;
}
else
{
IsByebye = FALSE;
}
if (pSsdpRequest->Headers[SSDP_CACHECONTROL] == NULL)
{
// To-Do:
TraceTag(ttidSsdpCache, "Couldn't find cache control header, not cachable");
return bRetVal;
}
EnterCriticalSection(&CSListCache);
for (p = pListHead->Flink; p != pListHead; )
{
SSDP_CACHE_ENTRY *CacheEntry;
CacheEntry = CONTAINING_RECORD (p, SSDP_CACHE_ENTRY, linkage);
p = p->Flink;
if (strcmp(CacheEntry->SsdpRequest.Headers[SSDP_USN],
pSsdpRequest->Headers[SSDP_USN]) == 0)
{
found = TRUE; // even if update fails.
TraceTag(ttidSsdpCache, "Found matching cache entry %x", CacheEntry);
if (IsByebye)
{
// byebye
if (CacheEntry == pCacheEntryFirst)
{
TraceTag(ttidSsdpCache, "Byebye is for the current earliest %x", CacheEntry);
if (CTEStopTimer(&timerCleanup))
{
TraceTag(ttidSsdpCache, "Stopped cleanup timer for %x", CacheEntry);
RemoveFromListCache(CacheEntry, pSsdpRequest);
} else {
TraceTag(ttidSsdpCache, "Received byebye for cache entry %x, timer \
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -