📄 announce.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
#include <bldver.h>
#include "ipsupport.h"
#define BUF_SIZE 40
static LIST_ENTRY listAnnounce;
static CRITICAL_SECTION CSListAnnounce;
static CHAR *AliveHeader = "ssdp:alive";
static CHAR *ByebyeHeader = "ssdp:byebye";
static CHAR *MulticastUri = "*";
static const CHAR c_szHttpLocalHost[] = "http://localhost";
static const CHAR c_szServerVersion[] = "Microsoft-WinCE/%d.%02d UPnP/1.0 UPnP-Device-Host/1.0";
static SSDP_HEADER AliveHeaders[] = {SSDP_HOST, SSDP_CACHECONTROL, SSDP_LOCATION, SSDP_NT, SSDP_NTS, SSDP_SERVER, SSDP_USN, SSDP_LAST, SSDP_LAST, SSDP_LAST};
static SSDP_HEADER ByeByeHeaders[] = {SSDP_HOST, SSDP_NT, SSDP_NTS, SSDP_USN, SSDP_LAST, SSDP_LAST, SSDP_LAST};
static SSDP_HEADER ResponseHeaders[] = {SSDP_CACHECONTROL, SSDP_EXT, SSDP_LOCATION, SSDP_SERVER, SSDP_ST, SSDP_USN, SSDP_LAST, SSDP_LAST, SSDP_LAST};
extern LONG_PTR bShutdown;
extern CHAR g_pszExtensionURI[500];
extern CHAR g_pszHeaderPrefix[10];
extern CHAR g_lpszNls[100];
SVSThreadPool* g_pThreadPool;
BOOL InitializeSsdpRequestFromMessage(SSDP_REQUEST *pRequest,
const SSDP_MESSAGE *pssdpSrc);
VOID PrintList(LIST_ENTRY *pListHead);
VOID PrintSSDPService (const SSDP_SERVICE *pSSDPService);
VOID InitializeListAnnounce()
{
InitializeCriticalSection(&CSListAnnounce);
EnterCriticalSection(&CSListAnnounce);
InitializeListHead(&listAnnounce);
LeaveCriticalSection(&CSListAnnounce);
}
BOOL SendAnnouncementOrResponse(
SSDP_SERVICE *pssdpService,
SOCKET sock, // socket to send response (INVALID_SOCKET if multicast announcement)
PSOCKADDR_STORAGE pSockAddr) // destination addr (NULL if mulicast announcement)
{
PSSDPNetwork pNet = NULL;
CHAR *pszOrigLocation = pssdpService->SsdpRequest.Headers[SSDP_LOCATION];
CHAR *pszBytes = NULL;
ce::istring strLocation;
ce::istring::size_type nReplaceAt, nReplaceLen, nReplaceAddrAt;
Assert(pszOrigLocation);
Assert(pssdpService->SsdpRequest.Headers[SSDP_NLS] == NULL);
Assert(pssdpService->SsdpRequest.Headers[SSDP_HOST] == NULL);
Assert(pssdpService->SsdpRequest.Headers[SSDP_OPT] == NULL);
strLocation = pszOrigLocation;
nReplaceAt = strLocation.find(c_szReplaceGuid);
nReplaceLen = strlen(c_szReplaceGuid);
nReplaceAddrAt = strLocation.find(c_szReplaceAddrGuid);
assert(nReplaceLen == strlen(c_szReplaceAddrGuid));
// set OPT header
if(*g_pszHeaderPrefix && *g_lpszNls)
{
if(pssdpService->SsdpRequest.Headers[SSDP_OPT] = (CHAR*)malloc(strlen(g_pszExtensionURI) + strlen(g_pszHeaderPrefix) + sizeof("; ns=") + 1))
sprintf(pssdpService->SsdpRequest.Headers[SSDP_OPT], "%s; ns=%s", g_pszExtensionURI, g_pszHeaderPrefix);
}
GetNetworkLock();
while(pNet = GetNextNetwork(pNet))
{
if(sock != INVALID_SOCKET && pNet->socket != sock)
continue;
if(nReplaceAt != ce::istring::npos)
{
strLocation = pszOrigLocation;
// Repalce c_szReplaceGuid with IP address for this network
strLocation.replace(nReplaceAt, nReplaceLen, pNet->pszIPString);
pssdpService->SsdpRequest.Headers[SSDP_LOCATION] = const_cast<LPSTR>(static_cast<LPCSTR>(strLocation));
}
else if(nReplaceAddrAt != ce::istring::npos)
{
strLocation = pszOrigLocation;
// Repalce c_szReplaceAddrGuid with address (IP and port) for this network
strLocation.replace(nReplaceAddrAt, nReplaceLen, pNet->pszAddressString);
pssdpService->SsdpRequest.Headers[SSDP_LOCATION] = const_cast<LPSTR>(static_cast<LPCSTR>(strLocation));
}
// set NLS header
if(*g_lpszNls)
pssdpService->SsdpRequest.Headers[SSDP_NLS] = g_lpszNls;
if(pSockAddr)
{
// ResponseHeaders has space for 3 optional headers: AL, OPT and NLS
int nHeaders = sizeof(ResponseHeaders)/sizeof(*ResponseHeaders) - 3;
// add optional AL header if specified
if(pssdpService->SsdpRequest.Headers[SSDP_AL])
{
PREFAST_SUPPRESS(394, "optional header");
ResponseHeaders[nHeaders++] = SSDP_AL;
}
// add optional OPT header if specified
if(pssdpService->SsdpRequest.Headers[SSDP_OPT])
{
PREFAST_SUPPRESS(394, "optional header");
ResponseHeaders[nHeaders++] = SSDP_OPT;
}
// add optional NLS header if specified
if(pssdpService->SsdpRequest.Headers[SSDP_NLS])
{
PREFAST_SUPPRESS(394, "optional header");
ResponseHeaders[nHeaders++] = SSDP_NLS;
}
// compose response message
ComposeSsdpResponse(&pssdpService->SsdpRequest, ResponseHeaders, nHeaders, &pszBytes);
// send unicast response
SocketSend(pszBytes, pNet->socket, (PSOCKADDR)pSockAddr);
}
else
{
int nHeaders;
SSDP_HEADER *pIncludedHeaders;
// set HOST header
pssdpService->SsdpRequest.Headers[SSDP_HOST] = pNet->pszMulticastAddr;
// set list of headers to be included in the message
if(pssdpService->SsdpRequest.Headers[SSDP_NTS] == ByebyeHeader)
{
pIncludedHeaders = ByeByeHeaders;
nHeaders = sizeof(ByeByeHeaders)/sizeof(*ByeByeHeaders);
}
else
{
assert(pssdpService->SsdpRequest.Headers[SSDP_NTS] == AliveHeader);
pIncludedHeaders = AliveHeaders;
nHeaders = sizeof(AliveHeaders)/sizeof(*AliveHeaders);
}
// AliveHeaders and ByeByeHeaders has space for 3 optional headers: AL, OPT and NLS
nHeaders -= 3;
// add optional AL header if specified
if(pssdpService->SsdpRequest.Headers[SSDP_AL])
pIncludedHeaders[nHeaders++] = SSDP_AL;
// add optional OPT header if specified
if(pssdpService->SsdpRequest.Headers[SSDP_OPT])
pIncludedHeaders[nHeaders++] = SSDP_OPT;
// add optional NLS header if specified
if(pssdpService->SsdpRequest.Headers[SSDP_NLS])
pIncludedHeaders[nHeaders++] = SSDP_NLS;
// compose request message
ComposeSsdpRequest(&pssdpService->SsdpRequest, pIncludedHeaders, nHeaders, &pszBytes);
// send multicast announcement
SocketSend(pszBytes, pNet->socket, pNet->pMulticastAddr);
}
if (pszBytes)
{
free(pszBytes);
pszBytes = NULL;
}
}
FreeNetworkLock();
// restore LOCATION header
pssdpService->SsdpRequest.Headers[SSDP_LOCATION] = pszOrigLocation;
// restore NLS header
pssdpService->SsdpRequest.Headers[SSDP_NLS] = NULL;
// restore HOST header
pssdpService->SsdpRequest.Headers[SSDP_HOST] = NULL;
// free OPT header
if(pssdpService->SsdpRequest.Headers[SSDP_OPT])
{
free(pssdpService->SsdpRequest.Headers[SSDP_OPT]);
pssdpService->SsdpRequest.Headers[SSDP_OPT] = NULL;
}
return TRUE;
}
// Announce all registered services on all active networks
// This is called when there is a network change, for example,
// an IP address change or a new network card
// This is implemented as a synchronous call, since it is called
// infrequently.
VOID SendAllAnnouncements()
{
PLIST_ENTRY p;
PLIST_ENTRY pListHead = &listAnnounce;
TraceTag(ttidSsdpAnnounce, "SendAllAnnouncements entered");
EnterCriticalSection(&CSListAnnounce);
for (p = pListHead->Flink; p != pListHead; p = p->Flink)
{
SSDP_SERVICE *pService;
pService = CONTAINING_RECORD (p, SSDP_SERVICE, linkage);
EnterCriticalSection(&pService->CSService);
pService->SsdpRequest.Headers[SSDP_NTS] = AliveHeader;
SendAnnouncement(pService);
LeaveCriticalSection(&pService->CSService);
}
TraceTag(ttidSsdpAnnounce, "SendAllAnnouncements done");
LeaveCriticalSection(&CSListAnnounce);
}
DWORD AnnounceTimerProc (VOID *Arg)
{
SSDP_SERVICE *pssdpService = (SSDP_SERVICE *) Arg;
unsigned long Timeout;
EnterCriticalSection(&(pssdpService->CSService));
TraceTag(ttidSsdpTimer, "Announcement timer of %x expired with count = %d",
pssdpService,pssdpService->iRetryCount );
if (pssdpService->state == SERVICE_NO_MASTER_CLEANUP)
{
SetEvent(pssdpService->CleanupEvent);
LeaveCriticalSection(&(pssdpService->CSService));
return 0;
}
pssdpService->SsdpRequest.Headers[SSDP_NTS] = AliveHeader;
SendAnnouncement(pssdpService);
pssdpService->iRetryCount--;
if (pssdpService->iRetryCount == 0)
{
// 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 ...
pssdpService->iRetryCount = NUM_RETRIES;
Timeout = (pssdpService->iLifeTime - ANNOUNCE_MARGIN) * 1000;
if(!(pssdpService->dwTimerCookie = g_pThreadPool->StartTimer(AnnounceTimerProc, pssdpService, Timeout)))
{
TraceTag(ttidError, "Failed to start cache timer for %x.",
pssdpService);
}
else
{
TraceTag(ttidSsdpTimer, "Started cache timer.");
}
}
else
{
Timeout = RETRY_INTERVAL;
if(!(pssdpService->dwTimerCookie = g_pThreadPool->StartTimer(AnnounceTimerProc, pssdpService, Timeout)))
{
TraceTag(ttidError, "Failed to start retry timer.");
}
else
{
TraceTag(ttidSsdpTimer, "Started retry timer.");
}
}
LeaveCriticalSection(&(pssdpService->CSService));
return 0;
}
DWORD ByebyeTimerProc (VOID *Arg)
{
SSDP_SERVICE *pssdpService = (SSDP_SERVICE *) Arg;
unsigned long Timeout;
if (InterlockedExchange(&bShutdown, bShutdown) != 0)
{
FreeSSDPService(pssdpService);
return 0;
}
//GetNetworkLock();
EnterCriticalSection(&(pssdpService->CSService));
TraceTag(ttidSsdpTimer, "Byebye timer of %x expired with count = %d",
pssdpService,pssdpService->iRetryCount );
pssdpService->SsdpRequest.Headers[SSDP_NTS] = ByebyeHeader;
SendAnnouncement(pssdpService);
//FreeNetworkLock();
pssdpService->iRetryCount--;
if (pssdpService->iRetryCount == 0)
{
TraceTag(ttidSsdpAnnounce, "Done with sending byebyes.");
LeaveCriticalSection(&(pssdpService->CSService));
FreeSSDPService(pssdpService);
}
else
{
Timeout = RETRY_INTERVAL;
if(!(pssdpService->dwTimerCookie = g_pThreadPool->StartTimer(ByebyeTimerProc, pssdpService, Timeout)))
{
TraceTag(ttidError, "Failed to start byebye retry timer.");
}
else
{
TraceTag(ttidSsdpTimer, "Started byebye retry timer.");
}
LeaveCriticalSection(&(pssdpService->CSService));
}
return 0;
}
VOID StartAnnounceTimer(PSSDP_SERVICE pssdpSvc, LPTHREAD_START_ROUTINE pCallback)
{
EnterCriticalSection(&(pssdpSvc->CSService));
// Assume send will be called once before start the timer
pssdpSvc->iRetryCount = NUM_RETRIES-1;
if(!(pssdpSvc->dwTimerCookie = g_pThreadPool->StartTimer(pCallback, pssdpSvc, RETRY_INTERVAL)))
{
TraceTag(ttidError, "Announcement timer failed to start "
"for service %x", pssdpSvc);
}
else
{
TraceTag(ttidSsdpTimer, "Announcement timer started for service "
"%x", pssdpSvc);
}
LeaveCriticalSection(&(pssdpSvc->CSService));
}
VOID StopAnnounceTimer(SSDP_SERVICE *pSSDPSvc)
{
EnterCriticalSection(&(pSSDPSvc->CSService));
Assert(pSSDPSvc->state == SERVICE_NO_MASTER_CLEANUP);
TraceTag(ttidSsdpTimer, "Stopping Announcement timer for service %x", pSSDPSvc);
if (g_pThreadPool->StopTimer(pSSDPSvc->dwTimerCookie))
{
TraceTag(ttidSsdpTimer, "Announcement timer stopped for service %x", pSSDPSvc);
LeaveCriticalSection(&(pSSDPSvc->CSService));
}
else
{
// Timer is running, wait for CleanupEvent
TraceTag(ttidSsdpAnnounce, "Announcement timer is running, wait ...%x", pSSDPSvc);
LeaveCriticalSection(&(pSSDPSvc->CSService));
WaitForSingleObject(pSSDPSvc->CleanupEvent, INFINITE);
}
}
PSSDP_SERVICE AddToListAnnounce(SSDP_MESSAGE *pssdpMsg, DWORD flags, PCONTEXT_HANDLE_TYPE *pphContext)
{
SSDP_SERVICE *pssdpService;
// To-Do: Check for duplicates.
// To-Do: QueryState
// Create SSDPService from SSDP_MESSAGE
pssdpService = (SSDP_SERVICE *) malloc (sizeof(SSDP_SERVICE));
if (pssdpService == NULL)
{
return NULL;
}
pssdpService->Type = SSDP_SERVICE_SIGNATURE;
if (InitializeSsdpRequestFromMessage(&(pssdpService->SsdpRequest),
pssdpMsg) == FALSE)
{
free(pssdpService);
return NULL;
};
pssdpService->iLifeTime = pssdpMsg->iLifeTime;
pssdpService->iRetryCount = 0;
pssdpService->dwTimerCookie = 0;
pssdpService->CleanupEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (pssdpService->CleanupEvent == NULL)
{
free(pssdpService);
return NULL;
}
pssdpService->flags = flags;
pssdpService->RpcContextHandle = pphContext;
// Important: Set to NULL to prevent freeing memory.
pssdpService->SsdpRequest.Headers[SSDP_NTS] = NULL;
pssdpService->SsdpRequest.RequestUri = MulticastUri; // for notify
// To-Do: Query Per Network State Matrix state
pssdpService->state = SERVICE_ACTIVE_NO_MASTER;
InitializeCriticalSection(&(pssdpService->CSService));
InitializeListHead(&(pssdpService->listSearchResponse));
// ALWAYS get the list lock before the service lock.
EnterCriticalSection(&CSListAnnounce);
// read the LIST_ENTRY
EnterCriticalSection(&(pssdpService->CSService));
InsertHeadList(&listAnnounce, &(pssdpService->linkage));
LeaveCriticalSection(&(pssdpService->CSService));
//PrintList(&listAnnounce);
TraceTag(ttidSsdpAnnounce, "New SSDP service announcement:\n");
#ifdef DEBUG
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -