📄 subs.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.
//
//+---------------------------------------------------------------------------
//
// Microsoft Windows
//
// File: S U B S . C P P
//
// Contents: Functions to send and receive (process) subscription requests
//
// Notes:
//
// Author: danielwe 7 Oct 1999
//
//----------------------------------------------------------------------------
#include <ssdppch.h>
#pragma hdrstop
#include <httpext.h>
#include "ssdpparser.h"
#include "ssdpnetwork.h"
#include "http_status.h"
#include "url_verifier.h"
#include "upnp_config.h"
extern url_verifier* g_pURL_Verifier;
const CHAR c_szUrlPrefix[] = "http://";
const DWORD g_cDefaultSubTimeout = 1800;
const DWORD g_cDefaultMaxSubTimeout = 3600;
const DWORD g_cMaxSubTimeout = 0xFFFFFFFF / 1000;
LONG g_nSubscribers = 0;
#define DEFAULT_HEADERS_SIZE 512
// SubscriptionExtensionProc
DWORD SubscriptionExtensionProc(LPEXTENSION_CONTROL_BLOCK pecb, DWORD dwIndex)
{
BOOL fRet;
HSE_SEND_HEADER_EX_INFO hse = {0};
CHAR szHeaders[DEFAULT_HEADERS_SIZE];
CHAR *pszHeaders = &szHeaders[0];
DWORD cbHeaders = sizeof(szHeaders);
PSTR pszResponse = NULL;
DWORD cbResponse;
SSDP_REQUEST* pSsdpReq;
BOOL fNotifyNeeded = FALSE;
if(pSsdpReq = new SSDP_REQUEST)
{
SSDP_REQUEST& ssdpReq = *pSsdpReq;
InitializeSsdpRequest(&ssdpReq);
ssdpReq.status = HTTP_STATUS_OK;
if (VerifySsdpMethod(pecb->lpszMethod, &ssdpReq)
&& (ssdpReq.Method == GENA_SUBSCRIBE || ssdpReq.Method == GENA_UNSUBSCRIBE))
{
// Get request URI
if (pecb->lpszQueryString)
ssdpReq.RequestUri = SsdpDup(pecb->lpszQueryString);
fRet = pecb->GetServerVariable(pecb->ConnID, "ALL_RAW", pszHeaders, &cbHeaders);
if (!fRet && (ERROR_INSUFFICIENT_BUFFER == GetLastError()))
{
pszHeaders = (CHAR *)SsdpAlloc(cbHeaders);
if (pszHeaders)
{
fRet = pecb->GetServerVariable(pecb->ConnID, "ALL_RAW", pszHeaders, &cbHeaders);
}
}
// no body for SUBSCRIBE and UNSUBSCRIBE
ASSERT(pecb->cbTotalBytes == 0);
ASSERT(pecb->cbAvailable == 0);
ssdpReq.ContentLength = 0;
if (fRet)
if(ParseHeaders(pszHeaders, &ssdpReq))
{
fRet = FProcessSubscribeRequest(&ssdpReq, &pszResponse, &fNotifyNeeded, dwIndex);
}
else
{
if (ssdpReq.status == HTTP_STATUS_OK)
ssdpReq.status = HTTP_STATUS_BAD_REQUEST;
}
if (!fRet && ssdpReq.status == HTTP_STATUS_OK)
ssdpReq.status = HTTP_STATUS_SERVER_ERROR;
}
else
{
ssdpReq.status = HTTP_STATUS_BAD_METHOD;
}
pecb->dwHttpStatusCode = ssdpReq.status;
}
else
{
pecb->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
}
cbResponse = pszResponse ? strlen(pszResponse) : 0;
hse.pszStatus = ce::http_status_string(pecb->dwHttpStatusCode);
hse.cchStatus = strlen(hse.pszStatus);
hse.pszHeader = pszResponse; // Should be empty for HTTP errors.
hse.cchHeader = cbResponse;
hse.fKeepConn = FALSE;
pecb->ServerSupportFunction(pecb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER_EX, &hse, NULL, NULL);
if (fNotifyNeeded)
{
assert(pSsdpReq);
// The first event has to be sent *after* subscription response.
// BUGBUG - web server doesn't currently have a way to close the connection before returning
// from ISAPI extension proc; as a work-around we schedule to send the first event in 1s.
g_pThreadPool->StartTimer((LPTHREAD_START_ROUTINE)SendInitialEventNotification, pSsdpReq, 1000);
}
else
{
if(pSsdpReq)
{
FreeSsdpRequest(pSsdpReq);
delete pSsdpReq;
}
}
if (pszHeaders != &szHeaders[0])
SsdpFree(pszHeaders);
if (pszResponse)
SsdpFree(pszResponse);
return HSE_STATUS_SUCCESS;
}
// optional function that is called when a subscribe request is received
BOOL (*pfSubscribeCallback)(BOOL fSubscribe, PSTR pszUri);
//+---------------------------------------------------------------------------
//
// Function: FProcessSubscribeRequest
//
// Purpose: Given an SSDP_REQUEST, determines whether it is a subscribe,
// re-subscribe, or unsubscribe and passes it on for further
// processing.
//
// Arguments:
// socket [in] Socket to send response to
// pRequest [in] Request to be processed
//
// Returns: TRUE if successful, FALSE if not. GetLastError() has error
// code.
//
// Author: danielwe 13 Oct 1999
//
// Notes:
//
BOOL FProcessSubscribeRequest(SSDP_REQUEST * pRequest, PSTR *ppszResponse, BOOL *pfNotifyNeeded, DWORD dwIndex)
{
BOOL fResult = FALSE;
*pfNotifyNeeded = FALSE;
AssertSz((pRequest->Method == GENA_SUBSCRIBE) ||
(pRequest->Method == GENA_UNSUBSCRIBE), "I thought you told me "
"this was a subscription request!");
if (pRequest->Method == GENA_SUBSCRIBE)
{
// Figure out if this is a re-subscribe request or not
if (pRequest->Headers[GENA_SID])
{
if (!pRequest->Headers[GENA_CALLBACK] &&
!pRequest->Headers[SSDP_NT])
{
// Having a SID header means this is supposed to be a
// re-subscribe request
fResult = FProcessResubscribeRequest( pRequest, ppszResponse);
}
else
{
// but, they included an NT or Callback header so this is
// confusing and invalid.
//
SetLastError(ERROR_HTTP_INVALID_HEADER);
pRequest->status = HTTP_STATUS_BAD_REQUEST;
}
}
else
{
// No SID header on a SUBSCRIBE request means this must be an
// initial subscribe.
//
if(g_nSubscribers < upnp_config::max_subscribers())
{
fResult = FAddSubscriberFromRequest(pRequest, ppszResponse, dwIndex);
if (fResult)
{
// need to send initial notification AFTER sending the response
*pfNotifyNeeded = TRUE;
}
}
else
{
pRequest->status = HTTP_STATUS_SERVICE_UNAVAIL;
fResult = FALSE;
}
}
}
else
{
fResult = FRemoveSubscriberFromRequest(pRequest, ppszResponse);
}
TraceResult("FProcessSubscribeRequest", fResult);
return fResult;
}
PSTR
ComposeSubscribeResponse(UPNP_SUBSCRIBER* pSub)
{
// DATE: when response was generated
// SERVER: OS/version UPnP/1.0 product/version
// SID: uuid:subscription-UUID
// TIMEOUT: Second-actual subscription duration
CHAR szResp[512]; // adequate
sprintf(szResp, "SID:%s\r\n"
"TIMEOUT:Second-%d\r\n"
"\r\n",
pSub->szSid,pSub->csecTimeout);
return _strdup(szResp);
}
//+---------------------------------------------------------------------------
//
// Function: FProcessResubscribeRequest
//
// Purpose: Given an SSDP_REQUEST that has been determined to be a re-
// subscribe request, processes the message.
//
// Arguments:
// pRequest [in] Request to be processed
// ppResp [out] Response
//
// Returns: TRUE if successful, FALSE if not. GetLastError() has error
// code.
//
// Author: danielwe 13 Oct 1999
//
// Notes:
//
BOOL FProcessResubscribeRequest(SSDP_REQUEST * pRequest, PSTR *ppResp)
{
BOOL fResult = FALSE;
UPNP_EVENT_SOURCE * pes;
UPNP_SUBSCRIBER * pSub = NULL;
PLIST_ENTRY pListHead;
PLIST_ENTRY p;
AssertSz(pRequest->Method == GENA_SUBSCRIBE, "I thought you told me "
"this was a subscribe request!");
AssertSz(pRequest->Headers[GENA_SID], "Why don't I have a SID header??!?!");
if (!pRequest->RequestUri || !(*pRequest->RequestUri))
{
TraceTag(ttidEvents, "Didn't get a Reqeust-Uri!");
pRequest->status = HTTP_STATUS_BAD_REQUEST;
SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
goto cleanup;
}
EnterCriticalSection(&g_csListEventSource);
// Find the event source based on the URI
//
pes = PesFindEventSource(pRequest->RequestUri);
if (!pes)
{
TraceTag(ttidEvents, "Re-subscribe sent to unknown Request URI: %s ",
pRequest->RequestUri);
// No event source registered matches this Request URI. We'll
// retrun 404 Not Found.
//
pRequest->status = HTTP_STATUS_SERVER_ERROR;
SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
goto cleanup;
}
// Found the event source this SUBSCRIBE was sent to. Now find the
// subscriber.
//
//EnterCriticalSection(&pes->cs);
pListHead = &pes->listSubs;
for (p = pListHead->Flink; p != pListHead; p = p->Flink)
{
pSub = CONTAINING_RECORD (p, UPNP_SUBSCRIBER, linkage);
if (!_stricmp(pSub->szSid, pRequest->Headers[GENA_SID]))
{
// Found the subscriber they were looking for!
break;
}
}
if (p == pListHead)
{
TraceTag(ttidEvents, "Re-subscribe sent to unknown SID: %s. Maybe "
"its timer ran out?",
pRequest->Headers[GENA_SID]);
// Didn't find the subscriber. We should return 412 precondition
// failed
//
pRequest->status = HTTP_STATUS_PRECOND_FAILED;
SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
fResult = FALSE;
}
else
{
// Reset the timeout on the subscription
//
SetSubscriptionTimeout(pSub, pRequest->Headers[GENA_TIMEOUT]);
*ppResp = ComposeSubscribeResponse(pSub);
if (*ppResp)
{
// Restart the subscription timer
StopResubscribeTimer(pSub);
StartResubscribeTimer(pSub);
TraceTag(ttidEvents, "Re-subscribe for %s successful.",
pRequest->Headers[GENA_SID]);
fResult = TRUE;
}
}
//LeaveCriticalSection(&pes->cs);
cleanup:
LeaveCriticalSection(&g_csListEventSource);
TraceResult("FProcessResubscribeRequest", fResult);
return fResult;
}
//+---------------------------------------------------------------------------
//
// Function: SetSubscriptionTimeout
//
// Purpose: Sets the internal timeout for a subscription.
//
// Arguments:
// pSub [in] Subscription to set
// szTimeoutHeader [in] Timeout header from SSDP_REQUEST message
//
// Returns: Nothing
//
// Author: danielwe 13 Oct 1999
//
// Notes:
//
VOID SetSubscriptionTimeout(UPNP_SUBSCRIBER * pSub, LPCSTR szTimeoutHeader)
{
// Parse the Timeout header and convert it to seconds
//
pSub->csecTimeout = DwParseTime(szTimeoutHeader);
}
//+---------------------------------------------------------------------------
//
// Function: FAddSubscriberFromRequest
//
// Purpose: Given a SUBSCRIBE request, adds a subscriber to the event
// source the request indicates.
//
// Arguments:
//
// pRequest [in] Raw SUBSCRIBE message.
// ppResp [out] Response
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -