📄 cbtarget.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: CALLBACK.CPP
//
// Contents: Application LPC callback processing for UPnP device notifications
//
// Notes:
// Plays the role of a LPC server, although we are running in client (application) space.
// The LPC client in this case is the UPNPSVC service.
//
// Author:
//
//----------------------------------------------------------------------------
#include <windows.h>
#include <ssdppch.h>
#include <upnpdevapi.h>
#include <ssdpapi.h>
#include <sax.h>
#include <string.hxx>
#include <psl_marshaler.hxx>
#include <UpnpInvokeRequest.hpp>
struct CoGate
{
CoGate(DWORD dwCoInit = COINIT_MULTITHREADED)
{CoInitializeEx(NULL, dwCoInit); }
~CoGate()
{CoUninitialize(); }
};
extern CRITICAL_SECTION g_csUPNPAPI;
class SOAPHandler : ce::SAXContentHandler
{
friend BOOL WINAPI UpnpSetRawControlResponse(UPNPSERVICECONTROL *pSvcCtl, DWORD dwHttpStatus, PCWSTR pszResp);
private:
DWORD m_hSource; // handle to the request object in the UPNPSVC server
PCWSTR m_pszUDN;
PCWSTR m_pszServiceId; // UPnP service id (memory not alloced)
PCWSTR m_pszRequestXML; // request body (memory not alloced)
PWSTR m_pszAction; // action name
DWORD m_cParams; // number of input parameters
DWORD m_cArgsMax; // size of the m_pParams array
UPNPPARAM *m_pParams; // input parameters (parsed from request body)
PWSTR m_pszServiceType; // service type(from the UPNP device description)
BOOL m_fResponse; // TRUE if SetResponse has been called
// the UPNPSERVICECONTROL struct is used for callbacks into the device implementation.
// It contains mapped aliases to the actionName, args etc and should not be freed or generally
// touched.
UPNPSERVICECONTROL m_SvcCtl;
ce::wstring m_strArgumentValue;
ce::wstring m_strArgumentElement;
ce::wstring m_strActionElement;
bool m_bParsingBody;
bool m_bParsingAction;
bool m_bParsingArgument;
public:
SOAPHandler(DWORD hSource, PCWSTR pszUDN, PCWSTR pszServiceId, PCWSTR pszRequestXML);
~SOAPHandler();
BOOL Parse();
UPNPSERVICECONTROL *GetServiceControl() { return &m_SvcCtl; }
BOOL SetResponse(DWORD dwHttpStatus, PCWSTR pszResp);
BOOL Done(BOOL fRet);
static ce::SAXReader* m_pReader;
// ISAXContentHandler
private:
virtual HRESULT STDMETHODCALLTYPE startElement(
/* [in] */ const wchar_t __RPC_FAR *pwchNamespaceUri,
/* [in] */ int cchNamespaceUri,
/* [in] */ const wchar_t __RPC_FAR *pwchLocalName,
/* [in] */ int cchLocalName,
/* [in] */ const wchar_t __RPC_FAR *pwchQName,
/* [in] */ int cchQName,
/* [in] */ ISAXAttributes __RPC_FAR *pAttributes);
virtual HRESULT STDMETHODCALLTYPE endElement(
/* [in] */ const wchar_t __RPC_FAR *pwchNamespaceUri,
/* [in] */ int cchNamespaceUri,
/* [in] */ const wchar_t __RPC_FAR *pwchLocalName,
/* [in] */ int cchLocalName,
/* [in] */ const wchar_t __RPC_FAR *pwchQName,
/* [in] */ int cchQName);
virtual HRESULT STDMETHODCALLTYPE characters(
/* [in] */ const wchar_t __RPC_FAR *pwchChars,
/* [in] */ int cchChars);
};
ce::SAXReader* SOAPHandler::m_pReader;
class CallbackTarget
{
private:
LIST_ENTRY m_link;
PWSTR m_pszName;
PUPNPCALLBACK m_pfCallback;
PVOID m_pvUserContext;
DWORD m_Id;
public:
CallbackTarget(PCWSTR pszName, PUPNPCALLBACK pfCallback, PVOID pvUserContext)
: m_pfCallback(pfCallback),
m_pvUserContext(pvUserContext)
{
InitializeListHead(&m_link);
m_pszName = new WCHAR [wcslen(pszName)+1];
m_Id = InterlockedIncrement(&lastId);
if (m_pszName)
wcscpy(m_pszName, pszName);
}
~CallbackTarget()
{
// should be unlinked by now
if (m_pszName)
delete [] m_pszName;
}
PCWSTR Name() { return m_pszName; }
DWORD Handle() { return m_Id; }
DWORD DoCallback(UPNPCB_ID, PVOID pvPara);
// since there is only one list, the list manipulation methods are added to this
// class as statics.
static LIST_ENTRY list;
static LONG lastId;
static CallbackTarget *SearchByName(PCWSTR pszName);
static CallbackTarget *SearchByHandle(DWORD handle);
static void Link(CallbackTarget *pCallback) // add to list
{
InsertHeadList(&list, &pCallback->m_link);
}
static void Unlink(CallbackTarget *pCallback) // remove from list
{
RemoveEntryList(&pCallback->m_link);
}
static void LockList(void)
{
EnterCriticalSection(&g_csUPNPAPI);
}
static void UnlockList(void)
{
LeaveCriticalSection(&g_csUPNPAPI);
}
static BOOL CleanupList();
};
extern ce::psl_proxy<> proxy;
HANDLE g_hLPCThread;
static BOOL g_Stopped;
static DWORD g_dwLPCThreadId;
static UpnpInvokeRequest *g_pReq;
static DWORD g_cbReqSize;
// TODO: move to common library
static PWSTR
StrDupW(LPCWSTR pwszSource)
{
PWSTR pwsz;
size_t nBytes;
if (!pwszSource)
return NULL;
nBytes = wcslen(pwszSource)+1;
if (pwsz = new WCHAR [nBytes])
memcpy(pwsz,pwszSource,nBytes*sizeof(WCHAR));
return pwsz;
}
LIST_ENTRY CallbackTarget::list = {&CallbackTarget::list, &CallbackTarget::list};
LONG CallbackTarget::lastId;
CallbackTarget *
CallbackTarget::SearchByName(PCWSTR pszName)
{
LIST_ENTRY *pLink = CallbackTarget::list.Flink;
CallbackTarget *pCallback;
// assume lock is held
while (pLink != &CallbackTarget::list)
{
pCallback = CONTAINING_RECORD(pLink, CallbackTarget, m_link);
if (wcscmp(pszName, pCallback->m_pszName) == 0)
return pCallback;
pLink = pLink->Flink;
}
return NULL;
}
CallbackTarget *
CallbackTarget::SearchByHandle(DWORD handle)
{
LIST_ENTRY *pLink = CallbackTarget::list.Flink;
CallbackTarget *pCallback;
// assume lock is held
while (pLink != &CallbackTarget::list)
{
pCallback = CONTAINING_RECORD(pLink, CallbackTarget, m_link);
if (pCallback->Handle() == handle)
return pCallback;
pLink = pLink->Flink;
}
return NULL;
}
BOOL
CallbackTarget::CleanupList()
{
CallbackTarget *pCallback;
while (CallbackTarget::list.Flink != &CallbackTarget::list)
{
pCallback = CONTAINING_RECORD(CallbackTarget::list.Flink, CallbackTarget, m_link);
if (pCallback)
{
CallbackTarget::Unlink(pCallback); // remove from list
delete pCallback;
}
}
return TRUE;
}
DWORD
CallbackTarget::DoCallback(UPNPCB_ID cbId, PVOID pvPara)
{
DWORD dwRet = 0;
__try {
dwRet = (*m_pfCallback)(cbId, m_pvUserContext, pvPara);
}
__except (EXCEPTION_EXECUTE_HANDLER)
{
dwRet = ERROR_GEN_FAILURE;
}
return dwRet;
}
static BOOL
UnmarshalCallbackRequest(PBYTE pbReq, DWORD cbReq, UpnpRequest **ppUpnpReq, PWSTR *ppszUDN, PWSTR *ppszServiceId, PWSTR *ppszReqXML)
{
UpnpInvokeRequestContainer_t upnpInvokeRequestContainer;
if(upnpInvokeRequestContainer.CreateUpnpInvokeRequest(pbReq, cbReq) == FALSE) {
return FALSE;
}
*ppUpnpReq = upnpInvokeRequestContainer.GetUpnpRequest();
*ppszServiceId = static_cast<PWSTR>(const_cast<WCHAR*>(upnpInvokeRequestContainer.GetServiceID()));
*ppszUDN = static_cast<PWSTR>(const_cast<WCHAR*>(upnpInvokeRequestContainer.GetUDN()));
*ppszReqXML = static_cast<PWSTR>(const_cast<WCHAR*>(upnpInvokeRequestContainer.GetRequestXML()));
return TRUE;
}
static DWORD CALLBACK UPNPCallback(PVOID pvContext, PBYTE pbInBuf, DWORD cbInBuf)
{
UpnpRequest *pReq = NULL;
CallbackTarget *pCallback;
BOOL fRet = FALSE;
PWSTR pszServiceId = NULL;
PWSTR pszReqXML = NULL;
PWSTR pszUDN = NULL;
TraceTag(ttidControl, "UPNPCallback: ");
if (cbInBuf < sizeof(UpnpRequest))
return FALSE;
if (UnmarshalCallbackRequest( pbInBuf, cbInBuf, &pReq, &pszUDN, &pszServiceId, &pszReqXML) && pReq)
{
TraceTag(ttidControl, "UPNPCallback: CB_ID(%d), service %S, hTarget(%x)", pReq->cbId, (pszServiceId ? pszServiceId : L"NULL"), pReq->hTarget);
// which of the callback targets is this msg directed to?
CallbackTarget::LockList();
pCallback = CallbackTarget::SearchByHandle(pReq->hTarget);
if (pCallback)
{
switch (pReq->cbId)
{
case UPNPCB_INIT:
case UPNPCB_SHUTDOWN:
fRet = pCallback->DoCallback(pReq->cbId, NULL);
break;
case UPNPCB_SUBSCRIBING:
case UPNPCB_UNSUBSCRIBING:
{
UPNPSUBSCRIPTION UPnPSubscription;
UPnPSubscription.pszSID = pszServiceId;
UPnPSubscription.pszUDN = pszUDN;
fRet = pCallback->DoCallback(pReq->cbId, &UPnPSubscription);
}
break;
case UPNPCB_CONTROL:
{
SOAPHandler soapHandler(pReq->hSource, pszUDN, pszServiceId, pszReqXML);
if (soapHandler.Parse())
{
fRet = pCallback->DoCallback(pReq->cbId, soapHandler.GetServiceControl());
soapHandler.Done(fRet);
}
break;
}
}
}
CallbackTarget::UnlockList();
}
return fRet;
}
static DWORD WINAPI LPCInvokeRequestHandlerThread(PVOID pvPara)
{
CoGate co;
BOOL fRet = TRUE;
DWORD cbWrittenReqSize;
DWORD errCode = ERROR_SUCCESS;
TraceTag(ttidControl, "%s: Beginning Invoke Request Handler Thread\n", __FUNCTION__);
Assert(!g_pReq);
g_cbReqSize = MAXIMUM_UPNP_MESSAGE_LENGTH;
g_pReq = static_cast<UpnpInvokeRequest*>(VirtualAlloc(NULL,g_cbReqSize,MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE));
if(!g_pReq)
{
errCode = ERROR_OUTOFMEMORY;
TraceTag(ttidError, "%s: OOM allocating request buffer [%d]\n", __FUNCTION__, errCode);
goto Finish;
}
SOAPHandler::m_pReader = new ce::SAXReader;
if(!SOAPHandler::m_pReader)
{
errCode = ERROR_OUTOFMEMORY;
TraceTag(ttidError, "%s: Invoke Request Handler Thread error OOM m_pReader [%d]\n", __FUNCTION__, errCode);
goto Finish;
}
{
while(g_Stopped == FALSE)
{
SetSentinel(g_pReq);
fRet = (ERROR_SUCCESS == proxy.call(UPNP_IOCTL_RECV_INVOKE_REQUEST, errCode, ce::psl_buffer(g_pReq, g_cbReqSize), &cbWrittenReqSize));
errCode = ERROR_SUCCESS;
TraceTag(ttidControl, "%s: Received Next Invoke Request [%S]\n", __FUNCTION__, fRet ? L"TRUE" : L"FALSE");
Assert((!SentinelSet(g_pReq) && fRet) || (SentinelSet(g_pReq) && !fRet));
if(g_Stopped == TRUE)
break;
if(fRet)
{
fRet = UPNPCallback(NULL, (PBYTE) g_pReq, cbWrittenReqSize);
if(!fRet)
{
errCode = GetLastError();
if(errCode == ERROR_SUCCESS)
{
errCode = E_FAIL;
}
TraceTag(ttidError,"%s: Error processing handler Callback[%d]\n", __FUNCTION__, errCode);
}
}
else
{
errCode = GetLastError();
if(errCode == ERROR_SUCCESS)
{
errCode = E_FAIL;
}
TraceTag(ttidError,"%s: Error Receiving Next Invoke Request [%d]\n", __FUNCTION__, errCode);
}
}
}
Finish:
if(SOAPHandler::m_pReader)
{
delete SOAPHandler::m_pReader;
SOAPHandler::m_pReader = NULL;
}
if(g_pReq)
{
VirtualFree(g_pReq, g_cbReqSize, MEM_DECOMMIT);
VirtualFree(g_pReq, 0, MEM_RELEASE);
g_pReq = NULL;
}
TraceTag(ttidError,"%s: Exiting Receive Invoke Request Thread [%d]\n", __FUNCTION__, errCode);
if(errCode != ERROR_SUCCESS)
SetLastError(errCode);
return fRet;
}
static BOOL
LPCInit()
{
BOOL fRet = TRUE;
TraceTag(ttidDevice,"%s: Initializing LPCInvokeRequestHandler Thread\n", __FUNCTION__);
// create an empty list of callback targets
InitializeListHead(&CallbackTarget::list);
Assert(g_hLPCThread == NULL);
g_Stopped = FALSE;
// initialize the service for UPNP message requests
fRet = (ERROR_SUCCESS == proxy.call(UPNP_IOCTL_INIT_RECV_INVOKE_REQUEST));
Assert(fRet);
// create a thread to listen on the port
g_hLPCThread = CreateThread(NULL,0, LPCInvokeRequestHandlerThread, NULL, 0, &g_dwLPCThreadId);
return ((g_hLPCThread != NULL) && fRet);
}
static BOOL
CancelCallbacks()
{
TraceTag(ttidDevice,"%s: Cancelling UPnP Service Callbacks\n", __FUNCTION__);
return (ERROR_SUCCESS == proxy.call(UPNP_IOCTL_CANCEL_RECV_INVOKE_REQUEST));
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -