|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 2000.
//
// File: S E A R C H C . C P P
//
// Contents: Client side searching
//
// Notes:
//
// Author: mbend 2 Dec 2000
//
//----------------------------------------------------------------------------
#include <pch.h>
#pragma hdrstop
#include <rpcasync.h> // I_RpcExceptionFilter
#include "searchc.h"
#include "list.h"
#include "ssdpparser.h"
#include "ssdpfuncc.h"
#include "ssdpapi.h"
#include "common.h"
#include "ncbase.h"
#include "ncdefine.h"
#include "ncdebug.h"
#include "nccom.h"
#include "InterfaceTable.h"
#include "iphlpapi.h"
static CHAR *SearchHeader = "\"ssdp:discover\""; static CHAR *MulticastUri = "*"; static CHAR *MX = "3";
extern LONG cInitialized;
#define MX_VALUE 3000
#define SELECT_TIMEOUT 60
#define NUM_OF_RETRY 2 // 3-1
#define LOOPBACK_ADDR_TIMEOUT 120000 // 2 minutes
CSsdpSearchThread::CSsdpSearchThread(CSsdpSearchRequest * pRequest) : m_pRequest(pRequest) { }
CSsdpSearchThread::~CSsdpSearchThread() { }
DWORD CSsdpSearchThread::DwRun() { return m_pRequest->DwThreadFunc(); }
CSsdpSearchRequest::CSsdpSearchRequest() : m_searchState(SEARCH_START), m_pfnCallback(NULL), m_pvContext(NULL), m_searchThread(this), m_nNumOfRetry(NUM_OF_RETRY), m_timer(*this), m_bHitWire(FALSE), m_hEventDone(NULL), m_bShutdown(FALSE), m_bOnlyLoopBack(TRUE), m_bDeletedTimer(FALSE) {
}
CSsdpSearchRequest::~CSsdpSearchRequest() { CloseHandle(m_hEventDone);
long nCount = m_socketList.GetCount(); for(long n = 0; n < nCount; ++n) { closesocket(m_socketList[n].m_socket); } m_socketList.Clear();
ResponseMessageList::Iterator iter; if(S_OK == m_responseMessageList.GetIterator(iter)) { SSDP_MESSAGE * pMsg = NULL; while(S_OK == iter.HrGetItem(&pMsg)) { delete [] pMsg->szAltHeaders; delete [] pMsg->szContent; delete [] pMsg->szLocHeader; delete [] pMsg->szSid; delete [] pMsg->szType; delete [] pMsg->szUSN; if(S_OK != iter.HrNext()) { break; } } } m_responseMessageList.Clear(); }
HRESULT CSsdpSearchRequest::HrInitialize(char * szType) { HRESULT hr = S_OK;
if(!szType) { return E_INVALIDARG; }
hr = m_strType.HrAssign(szType); if(SUCCEEDED(hr)) { m_hEventDone = CreateEvent(NULL, TRUE, FALSE, NULL); if(!m_hEventDone) { hr = HrFromLastWin32Error(); } if(SUCCEEDED(hr)) { SSDP_REQUEST request; ZeroMemory(&request, sizeof(request));
hr = HrInitializeSsdpSearchRequest(&request, szType); if(SUCCEEDED(hr)) { char * szSearch = NULL; if(!ComposeSsdpRequest(&request, &szSearch)) { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { hr = m_strSearch.HrAssign(szSearch); delete [] szSearch; } // Presumably these point to constants and should not be freed
request.Headers[SSDP_MAN] = NULL; request.Headers[SSDP_MX] = NULL; request.RequestUri = NULL;
FreeSsdpRequest(&request); } } }
if(SUCCEEDED(hr)) { hr = HrBuildSocketList(); }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrInitialize"); return hr; }
HRESULT CSsdpSearchRequest::HrBuildSocketList() { HRESULT hr = S_OK;
CLock lock(m_critSec);
SOCKET sockInfo = INVALID_SOCKET; SOCKADDR_IN sockAddrInfo; sockAddrInfo.sin_family = AF_INET; sockAddrInfo.sin_addr.s_addr = INADDR_ANY; sockAddrInfo.sin_port = 0;
m_bOnlyLoopBack = TRUE ; // Open a socket to query interface list with
sockInfo = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(INVALID_SOCKET == sockInfo) { hr = E_FAIL; TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrBuildSocketList - socket() failed"); }
if(SUCCEEDED(hr)) { if(SOCKET_ERROR == bind(sockInfo, reinterpret_cast<sockaddr*>(&sockAddrInfo), sizeof(sockAddrInfo))) { hr = E_FAIL; TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrBuildSocketList - bind() failed"); } }
if(SUCCEEDED(hr)) { DWORD cbSocketAddressList = 0;
// Fetch size
WSAIoctl(sockInfo, SIO_ADDRESS_LIST_QUERY, NULL, 0, NULL, 0, &cbSocketAddressList, NULL, NULL);
if(cbSocketAddressList) { SOCKET_ADDRESS_LIST * pSocketAddressList = reinterpret_cast<SOCKET_ADDRESS_LIST*>(new char[cbSocketAddressList]); if(!pSocketAddressList) { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { // Fetch list of interfaces
if(SOCKET_ERROR == WSAIoctl(sockInfo, SIO_ADDRESS_LIST_QUERY, NULL, 0, pSocketAddressList, cbSocketAddressList, &cbSocketAddressList, NULL, NULL)) { hr = E_FAIL; TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrBuildSocketList - WSAIoctl() failed"); } if(SUCCEEDED(hr)) { GUID guidInterface; CInterfaceTable interfaceTable; hr = interfaceTable.HrInitialize(); if(SUCCEEDED(hr)) { TraceTag(ttidSsdpSearchResp, "CSsdpSearchRequest::BuildSocketList() No of Interface %d ",pSocketAddressList->iAddressCount); // Insert each interface into the list
for(long n = 0; n < pSocketAddressList->iAddressCount && SUCCEEDED(hr); ++n) { SOCKET_ADDRESS * pSockAddr = &pSocketAddressList->Address[n]; SOCKADDR_IN * pSockAddrIn = reinterpret_cast<SOCKADDR_IN*>(pSockAddr->lpSockaddr);
BOOL bBad = pSockAddr->iSockaddrLength == 0 || pSockAddr->lpSockaddr == NULL || pSockAddr->lpSockaddr->sa_family != AF_INET || pSockAddrIn->sin_addr.s_addr == 0; if(!bBad) { hr = interfaceTable.HrMapIpAddressToGuid(pSockAddrIn->sin_addr.S_un.S_addr, guidInterface); if(SUCCEEDED(hr)) { SOCKET socket = INVALID_SOCKET; pSockAddrIn->sin_port = 0; if(!SocketOpen(&socket, pSockAddrIn, pSockAddrIn->sin_addr.S_un.S_addr, FALSE)) { hr = E_FAIL; TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrBuildSocketList - SocketOpen() failed"); } if(SUCCEEDED(hr)) { SocketInfo socketInfo; socketInfo.m_socket = socket; socketInfo.m_guidInterface = guidInterface; hr = m_socketList.HrPushBack(socketInfo); m_bOnlyLoopBack = FALSE; TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::BuildSocketList() Loopbackonly(%d)", m_bOnlyLoopBack); } } } } } }
delete [] reinterpret_cast<char*>(pSocketAddressList); } } }
if(SUCCEEDED(hr)) { // Bind to loopback address if nothing else
SOCKADDR_IN sockAddrLoopback;
sockAddrLoopback.sin_family = AF_INET; sockAddrLoopback.sin_addr.s_addr = inet_addr("127.0.0.1"); sockAddrLoopback.sin_port = 0;
SOCKET socketLoopback = INVALID_SOCKET; if(!SocketOpen(&socketLoopback, &sockAddrLoopback, sockAddrLoopback.sin_addr.s_addr, FALSE)) { hr = E_FAIL; TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrBuildSocketList - SocketOpen() failed"); } if(SUCCEEDED(hr)) { SocketInfo socketInfo; socketInfo.m_socket = socketLoopback; ZeroMemory(&socketInfo.m_guidInterface, sizeof(socketInfo.m_guidInterface)); hr = m_socketList.HrPushBack(socketInfo); } }
if(FAILED(hr) && m_socketList.GetCount()) { long nCount = m_socketList.GetCount(); for(long n = 0; n < nCount; ++n) { closesocket(m_socketList[n].m_socket); } m_socketList.Clear(); }
if(INVALID_SOCKET != sockInfo) { closesocket(sockInfo); }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrBuildSocketList"); return hr; }
HRESULT CSsdpSearchRequest::HrReBuildSocketList() { HRESULT hr = S_OK;
{ CLock lock(m_critSec); long nCount = m_socketList.GetCount();
for(long n = 0; n < nCount; ++n) { closesocket(m_socketList[n].m_socket); } m_socketList.Clear();
hr = HrBuildSocketList(); }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrReBuildSocketList"); return hr;
}
HRESULT CSsdpSearchRequest::HrSocketSend(const char * szData) { HRESULT hr = S_OK;
CLock lock(m_critSec);
long nCount = m_socketList.GetCount(); for(long n = 0; n < nCount; ++n) { SocketSend(szData, m_socketList[n].m_socket, NULL); }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrSocketSend"); return hr; }
HRESULT CSsdpSearchRequest::HrProcessLoopbackAddrOnly() { HRESULT hr = S_OK; HANDLE hNotify = NULL; DWORD dwStatus; DWORD dwWaitStatus; HANDLE hInterfaceChangeEvent = NULL;
ZeroMemory(&m_ovInterfaceChange, sizeof(m_ovInterfaceChange));
hInterfaceChangeEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (hInterfaceChangeEvent) { m_ovInterfaceChange.hEvent = hInterfaceChangeEvent;
// NotifyAddrChange will be cancelled when the calling thread dies
// CSsdpSearchRequest, and therefore m_ovInterfaceChange, has a longer lifetime than the thread
dwStatus = NotifyAddrChange(&hNotify, &m_ovInterfaceChange); if (dwStatus != ERROR_SUCCESS && dwStatus != ERROR_IO_PENDING) { TraceTag(ttidSsdpCSearch, "NotifyAddrChange returned %d",dwStatus); hr = E_FAIL; } else { TraceTag(ttidSsdpCSearch, "NotifyAddrChange succeeded",dwStatus); HANDLE hHandles[2] = {hInterfaceChangeEvent, m_hEventDone};
dwWaitStatus = WaitForMultipleObjects(2, hHandles, FALSE, LOOPBACK_ADDR_TIMEOUT); switch(dwWaitStatus) { case WAIT_OBJECT_0: TraceTag(ttidSsdpCSearch, "Wait Object - IP Addr change notified"); hr = HrReBuildSocketList(); TraceTag(ttidSsdpCSearch, "Rebuilding Socket List - List Count - %d",m_socketList.GetCount()); // falling through intentionally
case WAIT_TIMEOUT: TraceTag(ttidSsdpCSearch, "AutoIP Time out"); if(SUCCEEDED(hr)) { char *szSearch = NULL; hr = m_strSearch.HrGetMultiByteWithAlloc(&szSearch); if(SUCCEEDED(hr)) { hr = HrSocketSend(szSearch); delete [] szSearch; if(SUCCEEDED(hr)) { hr = m_timer.HrSetTimer(MX_VALUE); } } }
break; case WAIT_OBJECT_0 + 1: TraceTag(ttidSsdpCSearch, "Exiting"); hr = E_FAIL; break;
default: hr = E_FAIL; break; } } } else { hr = E_FAIL; }
if(hInterfaceChangeEvent != NULL) CloseHandle(hInterfaceChangeEvent);
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrProcessLoopbackAddrOnly()"); return hr ; }
HRESULT CSsdpSearchRequest::HrStartAsync( BOOL bForceSearch, SERVICE_CALLBACK_FUNC pfnCallback, VOID * pvContext) { HRESULT hr = S_OK;
SOCKADDR_IN sockAddrIn; int nSockAddrInSize = sizeof(sockAddrIn);
if (!cInitialized) { return HRESULT_FROM_WIN32(ERROR_NOT_READY); }
if(!pfnCallback) { return E_INVALIDARG; }
m_pfnCallback = pfnCallback; m_pvContext = pvContext; m_searchState = SEARCH_DISCOVERING;
if(!m_bOnlyLoopBack) hr = HrGetCacheResult();
if(SUCCEEDED(hr)) { if(bForceSearch || !FIsInListNotify(m_strType)) { if(!m_bOnlyLoopBack) {
m_bHitWire = TRUE;
char * szSearch = NULL; hr = m_strSearch.HrGetMultiByteWithAlloc(&szSearch); if(SUCCEEDED(hr)) { // Make sure we have some IO before we returned the handle, so getsockname will succeed.
hr = HrSocketSend(szSearch); delete [] szSearch;
if(SUCCEEDED(hr)) { hr = m_timer.HrSetTimer(MX_VALUE); if(SUCCEEDED(hr)) { { CLock lock(m_critSec); hr = m_searchThread.HrStart(FALSE, FALSE); } if(FAILED(hr)) { HrDeleteTimer(); } } } } } else { hr = m_searchThread.HrStart(FALSE, FALSE); } } }
if(FAILED(hr)) { (*pfnCallback)(SSDP_DONE, NULL, pvContext); }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrStartAsync"); return hr; }
HRESULT CSsdpSearchRequest::HrStartSync(BOOL bForceSearch) { HRESULT hr = S_OK;
if (!cInitialized) { return HRESULT_FROM_WIN32(ERROR_NOT_READY); }
m_searchState = SEARCH_DISCOVERING;
hr = HrGetCacheResult(); if(SUCCEEDED(hr)) { if(bForceSearch || !FIsInListNotify(m_strType)) { m_bHitWire = TRUE;
char * szSearch = NULL; hr = m_strSearch.HrGetMultiByteWithAlloc(&szSearch); if(SUCCEEDED(hr)) { hr = HrSocketSend(szSearch); delete [] szSearch;
if(SUCCEEDED(hr)) { hr = m_timer.HrSetTimer(MX_VALUE);
if(SUCCEEDED(hr)) { hr = DwThreadFunc(); if(SUCCEEDED(hr)) { if(m_responseMessageList.IsEmpty()) { hr = HRESULT_FROM_WIN32(ERROR_NO_MORE_SERVICES); } } HrDeleteTimer(); } } } } }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrStartSync"); return hr; }
HRESULT CSsdpSearchRequest::HrShutdown() { HRESULT hr = S_OK;
{ CLock lock(m_critSec); m_searchState = SEARCH_COMPLETED; }
if(m_pfnCallback) { hr = HrCancelCallback(); if(SUCCEEDED(hr)) { DWORD dwResult;
if (m_hEventDone) SetEvent(m_hEventDone);
HANDLE h = m_searchThread.GetThreadHandle(); hr = HrMyWaitForMultipleHandles(0, INFINITE, 1, &h, &dwResult); TraceError("CSsdpSearchRequest::HrShutdown: HrMyWaitForMultipleHandles", hr); } }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrShutdown"); return hr; }
HRESULT CSsdpSearchRequest::HrCancelCallback() { HRESULT hr = S_OK;
CLock lock(m_critSec);
m_bShutdown = TRUE;
hr = HrDeleteTimer();
WakeupSelect();
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrCancelCallback"); return hr; }
void CSsdpSearchRequest::WakeupSelect() { SOCKADDR_IN sockAddrIn; int nAddrInSize = sizeof(sockAddrIn);
long nCount = m_socketList.GetCount(); for(long n = 0; n < nCount; ++n) { if(SOCKET_ERROR != getsockname(m_socketList[n].m_socket, reinterpret_cast<sockaddr*>(&sockAddrIn), &nAddrInSize)) { sockAddrIn.sin_addr.s_addr = inet_addr("127.0.0.1"); TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::WakeupSelect() - sending to 127.0.0.1:%d", ntohs(sockAddrIn.sin_port)); // Will select get this if the socket is not bound to ADDR_ANY?
SocketSend("Q", m_socketList[n].m_socket, &sockAddrIn); } else { TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::WakeupSelect() - failed to send loopback packet. (%d)", GetLastError()); // select will eventually timeout, just slower.
} } }
HRESULT CSsdpSearchRequest::HrGetCacheResult() { HRESULT hr = S_OK;
CLock lock(m_critSec);
MessageList * pMessageList = NULL;
char * szType = NULL; hr = m_strType.HrGetMultiByteWithAlloc(&szType); if(SUCCEEDED(hr)) { RpcTryExcept { LookupCacheRpc(szType, &pMessageList); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { hr = HRESULT_FROM_WIN32(RpcExceptionCode()); TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::HrGetCacheResult - LookupCacheRpc failed (%x)", RpcExceptionCode()); } RpcEndExcept
delete [] szType;
if(pMessageList) { for(long n = 0; n < pMessageList->size; ++n) { hr = HrAddRequestToResponseMessageList(&pMessageList->list[n]); if(FAILED(hr)) { break; } } for(long n = 0; n < pMessageList->size; ++n) { FreeSsdpRequest(&pMessageList->list[n]); } delete [] pMessageList->list; delete pMessageList;
// Make the callbacks
ResponseMessageList::Iterator iter; if(S_OK == m_responseMessageList.GetIterator(iter)) { SSDP_MESSAGE * pSsdpMessage = NULL; while(S_OK == iter.HrGetItem(&pSsdpMessage)) { if(m_pfnCallback) { (*m_pfnCallback)(SSDP_FOUND, pSsdpMessage, m_pvContext); } if(S_OK != iter.HrNext()) { break; } } } } }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrGetCacheResult"); return hr; }
BOOL CSsdpSearchRequest::FIsInListNotify(const CUString & strType) { HRESULT hr = S_OK;
CLock lock(m_critSec);
char * szType = NULL; hr = strType.HrGetMultiByteWithAlloc(&szType); if(SUCCEEDED(hr)) { if(!IsInListNotify(szType)) { hr = S_FALSE; }
delete [] szType; }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::FIsInListNotify"); return S_OK == hr; }
HRESULT CSsdpSearchRequest::HrAddRequestToResponseMessageList(SSDP_REQUEST * pRequest) { HRESULT hr = S_OK;
CLock lock(m_critSec);
// Build a temporary item in a list and then transfer
ResponseMessageList responseMessageList; hr = responseMessageList.HrPushFrontDefault(); if(SUCCEEDED(hr)) { if(InitializeSsdpMessageFromRequest(&responseMessageList.Front(), pRequest)) { m_responseMessageList.Prepend(responseMessageList); } }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrAddRequestToResponseMessageList"); return hr; }
BOOL CSsdpSearchRequest::FIsInResponseMessageList(const char * szUSN) { CLock lock(m_critSec);
BOOL bRet = FALSE;
ResponseMessageList::Iterator iter; if(S_OK == m_responseMessageList.GetIterator(iter)) { SSDP_MESSAGE * pSsdpMessage = NULL; while(S_OK == iter.HrGetItem(&pSsdpMessage)) { if(!lstrcmpA(szUSN, pSsdpMessage->szUSN)) { bRet = TRUE; break; } if(S_OK != iter.HrNext()) { break; } } } return bRet; }
DWORD CSsdpSearchRequest::DwThreadFunc() { HRESULT hr = S_OK;
fd_set setReadSocket; timeval tvSelectTimeOut;
tvSelectTimeOut.tv_sec = SELECT_TIMEOUT; tvSelectTimeOut.tv_usec = 0;
long nRet;
if(m_bOnlyLoopBack) { hr = HrProcessLoopbackAddrOnly(); } if(FAILED(hr)) { m_bShutdown = TRUE; SetEvent(m_hEventDone); } while(!m_bShutdown) { char * szRecvBuf = NULL; u_long ulBytesReceived; SOCKADDR_IN sockAddrInRemoteSocket; SSDP_REQUEST ssdpRequestResponse; u_long ulBufferSize; int nSockAddrInSize = sizeof(sockAddrInRemoteSocket); long n; long nCount;
FD_ZERO(&setReadSocket); nCount = m_socketList.GetCount(); for(n = 0; n < nCount; ++n) { FD_SET(m_socketList[n].m_socket, &setReadSocket); }
TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::DwThreadFunc(this=%x) - about to call select", this); nRet = select(-1, &setReadSocket, NULL, NULL, &tvSelectTimeOut);
if(SOCKET_ERROR == nRet) { TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::DwThreadFunc - select failed(%d)", nRet); break; }
if(!nRet) { TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::DwThreadFunc !!! select timed out !!!, where is loopback packet? "); hr = HrDeleteTimer(); break; }
BOOL bBreak = FALSE;
for(n = 0; n < nCount; ++n) {
if(FD_ISSET(m_socketList[n].m_socket, &setReadSocket)) { ioctlsocket(m_socketList[n].m_socket, FIONREAD, &ulBufferSize);
szRecvBuf = new char[ulBufferSize + 1]; if(!szRecvBuf) { hr = E_OUTOFMEMORY; }
if(FAILED(hr)) { hr = HrDeleteTimer(); bBreak = TRUE; break; } if(SUCCEEDED(hr)) { TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::DwThreadFunc(this=%x) - about to call recvfrom", this); ulBytesReceived = recvfrom(m_socketList[n].m_socket, szRecvBuf, ulBufferSize, 0, reinterpret_cast<sockaddr*>(&sockAddrInRemoteSocket), &nSockAddrInSize); if(SOCKET_ERROR == ulBytesReceived) { CLock lock(m_critSec); delete [] szRecvBuf; DWORD dwError = WSAGetLastError(); if(WSAECONNRESET == dwError) { TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::DwThreadFunc - recvfrom failed as this socket has received port unreachable"); continue; } TraceTag(ttidError, "CSsdpSearchRequest::DwThreadFunc - recvfrom failed(%d)", dwError); hr = HrDeleteTimer(); bBreak = TRUE; break; } else { szRecvBuf[ulBytesReceived] = 0; TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::DwThreadFunc(this=%x) - recvfrom returned: %s", this, szRecvBuf); } }
if(SUCCEEDED(hr)) { if(!InitializeSsdpRequest(&ssdpRequestResponse)) { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { if(ParseSsdpResponse(szRecvBuf, &ssdpRequestResponse)) { // Set the interface on which we received this
ssdpRequestResponse.guidInterface = m_socketList[n].m_guidInterface;
// preserve source address if possible.
// hack here where we use szSID to hold address
// this should not be used for search response normally.
if (ssdpRequestResponse.Headers[GENA_SID] == NULL) { char* pszIp = GetSourceAddress(sockAddrInRemoteSocket); ssdpRequestResponse.Headers[GENA_SID] = (CHAR *) midl_user_allocate( sizeof(CHAR) * (strlen(pszIp) + 1)); if (ssdpRequestResponse.Headers[GENA_SID]) { strcpy(ssdpRequestResponse.Headers[GENA_SID], pszIp); } }
// Check duplicate
if(!FIsInResponseMessageList(ssdpRequestResponse.Headers[SSDP_USN])) { // Build a temporary item in a list and then transfer
ResponseMessageList responseMessageList; hr = responseMessageList.HrPushFrontDefault(); if(SUCCEEDED(hr)) { if(InitializeSsdpMessageFromRequest(&responseMessageList.Front(), &ssdpRequestResponse)) { if(m_pfnCallback) { (*m_pfnCallback)(SSDP_FOUND, &responseMessageList.Front(), m_pvContext); } CLock lock(m_critSec); m_responseMessageList.Prepend(responseMessageList); } } RpcTryExcept { TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::DwThreadFunc(this=%x) - about to call UpdateCacheRpc", this); UpdateCacheRpc(&ssdpRequestResponse); } RpcExcept(I_RpcExceptionFilter(RpcExceptionCode())) { hr = HRESULT_FROM_WIN32(RpcExceptionCode()); TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::DwThreadFunc - UpdateCacheRpc failed (%x)", RpcExceptionCode()); } RpcEndExcept
} FreeSsdpRequest(&ssdpRequestResponse); } } if(FAILED(hr)) { TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::DwThreadFunc(this=%x) - processing of select failed", this); } }
delete [] szRecvBuf; szRecvBuf = NULL; } if(bBreak) { break; } } }
// FindServices ideally should Get Cache at the of search to the most up-to-date info.
// FindServicesCallback ideally should Get Cache first to give faster responses.
// Get the cache at the beginning to make the code common.
{ CLock lock(m_critSec); long nCount = m_socketList.GetCount(); for(long n = 0; n < nCount; ++n) { closesocket(m_socketList[n].m_socket); } m_socketList.Clear(); }
if(m_pfnCallback) { (*m_pfnCallback)(SSDP_DONE, NULL, m_pvContext); }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::DwThreadFunc"); return hr; }
void CSsdpSearchRequest::TimerFired() { HRESULT hr = S_OK;
TraceTag(ttidSsdpCSearch, "CSsdpSearchRequest::TimerFired(this=%x, count=%d)", this, m_nNumOfRetry);
if(m_bShutdown) { SetEvent(m_hEventDone); return; }
_try { if(0 == m_nNumOfRetry) { TraceTag(ttidSsdpSearchResp, "CSsdpSearchRequest::TimerFired(this=%x) - timed out", this); m_bShutdown = TRUE; WakeupSelect(); SetEvent(m_hEventDone); } else { char * szSearch = NULL; hr = m_strSearch.HrGetMultiByteWithAlloc(&szSearch); if(SUCCEEDED(hr)) { hr = HrSocketSend(szSearch); delete [] szSearch;
if(SUCCEEDED(hr)) { --m_nNumOfRetry; hr = m_timer.HrSetTimerInFired(MX_VALUE); TraceTag(ttidSsdpSearchResp, "CSsdpSearchRequest::TimerFired(this=%x) - Num of Retry %d", this,m_nNumOfRetry); } if(FAILED(hr)) { TraceHr(ttidSsdpSearchResp, FAL, hr, FALSE, "CSsdpSearchRequest::TimerFired - failed to restart timer"); m_bShutdown = TRUE; WakeupSelect(); SetEvent(m_hEventDone); } } } } _finally { } }
BOOL CSsdpSearchRequest::TimerTryToLock() { return m_critSec.FTryEnter(); }
void CSsdpSearchRequest::TimerUnlock() { m_critSec.Leave(); }
HRESULT CSsdpSearchRequest::HrInitializeSsdpSearchRequest(SSDP_REQUEST * pRequest,char * szType) { HRESULT hr = S_OK;
ZeroMemory(pRequest, sizeof(SSDP_REQUEST));
pRequest->Method = SSDP_M_SEARCH; pRequest->RequestUri = MulticastUri; pRequest->Headers[SSDP_MAN] = SearchHeader; pRequest->Headers[SSDP_MX] = MX;
pRequest->Headers[SSDP_HOST] = new char[sizeof(CHAR) * (strlen(SSDP_ADDR) + 1 + // colon
6 + // port number
1)]; if(!pRequest->Headers[SSDP_HOST]) { hr = E_OUTOFMEMORY; } if(SUCCEEDED(hr)) { wsprintfA(pRequest->Headers[SSDP_HOST], "%s:%d", SSDP_ADDR, SSDP_PORT); hr = HrCopyString(szType, &pRequest->Headers[SSDP_ST]); }
if(FAILED(hr)) { delete [] pRequest->Headers[SSDP_HOST]; delete [] pRequest->Headers[SSDP_ST]; ZeroMemory(pRequest, sizeof(SSDP_REQUEST)); }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrInitializeSsdpSearchRequest"); return hr; }
HRESULT CSsdpSearchRequest::HrGetFirstService(SSDP_MESSAGE ** ppSsdpMessage) { HRESULT hr = S_OK;
CLock lock(m_critSec);
hr = m_responseMessageList.GetIterator(m_iterCurrentResponse); if(S_OK == hr) { m_iterCurrentResponse.HrGetItem(ppSsdpMessage); m_iterCurrentResponse.HrNext(); }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrGetFirstService"); return hr; }
HRESULT CSsdpSearchRequest::HrGetNextService(SSDP_MESSAGE ** ppSsdpMessage) { HRESULT hr = S_OK;
CLock lock(m_critSec);
if(!m_iterCurrentResponse.FIsItem()) { hr = S_FALSE; } if(S_OK == hr) { m_iterCurrentResponse.HrGetItem(ppSsdpMessage); m_iterCurrentResponse.HrNext(); }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrGetNextService"); return hr; }
HRESULT CSsdpSearchRequest::HrDeleteTimer() { HRESULT hr = S_OK;
if (!InterlockedExchange((LONG*)&m_bDeletedTimer, TRUE)) { hr = m_timer.HrDelete(INVALID_HANDLE_VALUE); }
TraceHr(ttidSsdpNotify, FAL, hr, FALSE, "CSsdpSearchRequest::HrDeleteTimer"); return hr; }
CSsdpSearchRequestManager CSsdpSearchRequestManager::s_instance;
CSsdpSearchRequestManager::CSsdpSearchRequestManager() { }
CSsdpSearchRequestManager::~CSsdpSearchRequestManager() { }
CSsdpSearchRequestManager & CSsdpSearchRequestManager::Instance() { return s_instance; }
HRESULT CSsdpSearchRequestManager::HrCleanup() { HRESULT hr = S_OK;
CLock lock(m_critSec);
SearchRequestList::Iterator iter; if(S_OK == m_searchRequestList.GetIterator(iter)) { CSsdpSearchRequest * pRequestIter = NULL; while(S_OK == iter.HrGetItem(&pRequestIter)) { hr = pRequestIter->HrShutdown();
if(S_OK != iter.HrNext()) { break; } } } m_searchRequestList.Clear();
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequestManager::HrCleanup"); return hr; }
HRESULT CSsdpSearchRequestManager::HrCreateAsync( char * szType, BOOL bForceSearch, SERVICE_CALLBACK_FUNC pfnCallback, VOID * pvContext, CSsdpSearchRequest ** ppSearchRequest) { HRESULT hr = S_OK;
if(!ppSearchRequest) { return E_POINTER; }
if(!cInitialized) { return HRESULT_FROM_WIN32(ERROR_NOT_READY); }
if(!szType || !pfnCallback) { return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); }
SearchRequestList searchRequestList; hr = searchRequestList.HrPushFrontDefault(); if(SUCCEEDED(hr)) { hr = searchRequestList.Front().HrInitialize(szType); if(SUCCEEDED(hr)) { hr = searchRequestList.Front().HrStartAsync(bForceSearch, pfnCallback, pvContext); if(SUCCEEDED(hr)) { CLock lock(m_critSec); m_searchRequestList.Prepend(searchRequestList); *ppSearchRequest = &m_searchRequestList.Front(); } } }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequestManager::HrCreateAsync"); return hr; }
HRESULT CSsdpSearchRequestManager::HrCreateSync( char * szType, BOOL bForceSearch, CSsdpSearchRequest ** ppSearchRequest) { HRESULT hr = S_OK;
if(!ppSearchRequest) { return E_POINTER; }
if(!cInitialized) { return HRESULT_FROM_WIN32(ERROR_NOT_READY); }
if(!szType) { return HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER); }
SearchRequestList searchRequestList; hr = searchRequestList.HrPushFrontDefault(); if(SUCCEEDED(hr)) { hr = searchRequestList.Front().HrInitialize(szType); if(SUCCEEDED(hr)) { hr = searchRequestList.Front().HrStartSync(bForceSearch); if(SUCCEEDED(hr)) { CLock lock(m_critSec); m_searchRequestList.Prepend(searchRequestList); *ppSearchRequest = &m_searchRequestList.Front(); } } }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequestManager::HrCreateSync"); return hr; }
HRESULT CSsdpSearchRequestManager::HrRemove(CSsdpSearchRequest * pSearchRequest) { HRESULT hr = S_FALSE;
SearchRequestList searchRequestList;
{ CLock lock(m_critSec); SearchRequestList::Iterator iter; if(S_OK == m_searchRequestList.GetIterator(iter)) { CSsdpSearchRequest * pRequestIter = NULL; while(S_OK == iter.HrGetItem(&pRequestIter)) { if(pSearchRequest == pRequestIter) { iter.HrMoveToList(searchRequestList); break; }
if(S_OK != iter.HrNext()) { break; } } } }
// Delete outside of lock
SearchRequestList::Iterator iter; if(S_OK == searchRequestList.GetIterator(iter)) { CSsdpSearchRequest * pRequestIter = NULL; while(S_OK == iter.HrGetItem(&pRequestIter)) { hr = pRequestIter->HrShutdown(); if(S_OK != iter.HrNext()) { break; } } }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequestManager::HrRemove"); return hr; }
HRESULT CSsdpSearchRequestManager::HrGetFirstService(CSsdpSearchRequest * pSearchRequest, SSDP_MESSAGE ** ppSsdpMessage) { HRESULT hr = S_FALSE;
SearchRequestList::Iterator iter; if(S_OK == m_searchRequestList.GetIterator(iter)) { CSsdpSearchRequest * pRequestIter = NULL; while(S_OK == iter.HrGetItem(&pRequestIter)) { if(pSearchRequest == pRequestIter) { hr = pRequestIter->HrGetFirstService(ppSsdpMessage); break; }
if(S_OK != iter.HrNext()) { break; } } }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequestManager::HrGetFirstService"); return hr; }
HRESULT CSsdpSearchRequestManager::HrGetNextService(CSsdpSearchRequest * pSearchRequest, SSDP_MESSAGE ** ppSsdpMessage) { HRESULT hr = S_FALSE;
SearchRequestList::Iterator iter; if(S_OK == m_searchRequestList.GetIterator(iter)) { CSsdpSearchRequest * pRequestIter = NULL; while(S_OK == iter.HrGetItem(&pRequestIter)) { if(pSearchRequest == pRequestIter) { hr = pRequestIter->HrGetNextService(ppSsdpMessage); break; }
if(S_OK != iter.HrNext()) { break; } } }
TraceHr(ttidSsdpCNotify, FAL, hr, FALSE, "CSsdpSearchRequestManager::HrGetNextService"); return hr; }
BOOL InitializeListSearch() { return TRUE; }
VOID CleanupListSearch() { CSsdpSearchRequestManager::Instance().HrCleanup(); }
HANDLE WINAPI FindServicesCallback (CHAR * szType, VOID * pReserved , BOOL fForceSearch, SERVICE_CALLBACK_FUNC fnCallback, VOID *Context) { if (!cInitialized) { SetLastError(ERROR_NOT_READY); return INVALID_HANDLE_VALUE; }
if (szType == NULL || !*szType || fnCallback == NULL || pReserved != NULL) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; }
CSsdpSearchRequest * pSearchRequest = NULL; HRESULT hr = CSsdpSearchRequestManager::Instance().HrCreateAsync(szType, fForceSearch, fnCallback, Context, &pSearchRequest);
if(FAILED(hr)) { SetLastError(DwWin32ErrorFromHr(hr)); return INVALID_HANDLE_VALUE; }
return pSearchRequest; }
BOOL WINAPI FindServicesCancel(HANDLE SearchHandle) { CSsdpSearchRequest * pRequest = reinterpret_cast<CSsdpSearchRequest*>(SearchHandle); HRESULT hr = pRequest->HrCancelCallback(); if(FAILED(hr)) { SetLastError(DwWin32ErrorFromHr(hr)); } return S_OK == hr; }
BOOL WINAPI FindServicesClose(HANDLE SearchHandle) { CSsdpSearchRequest * pRequest = reinterpret_cast<CSsdpSearchRequest*>(SearchHandle); HRESULT hr = CSsdpSearchRequestManager::Instance().HrRemove(pRequest); if(FAILED(hr)) { SetLastError(DwWin32ErrorFromHr(hr)); } return S_OK == hr; }
HANDLE WINAPI FindServices(CHAR* szType, VOID *pReserved , BOOL fForceSearch) { if (!cInitialized) { SetLastError(ERROR_NOT_READY); return INVALID_HANDLE_VALUE; }
if (szType == NULL || !*szType || pReserved != NULL) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; }
CSsdpSearchRequest * pSearchRequest = NULL; HRESULT hr = CSsdpSearchRequestManager::Instance().HrCreateSync(szType, fForceSearch, &pSearchRequest);
if(FAILED(hr)) { SetLastError(DwWin32ErrorFromHr(hr)); return INVALID_HANDLE_VALUE; }
return pSearchRequest; }
BOOL WINAPI GetFirstService(HANDLE hFindServices, PSSDP_MESSAGE *ppSsdpService) { if (!cInitialized) { SetLastError(ERROR_NOT_READY); return FALSE; }
if (!ppSsdpService || !hFindServices || hFindServices == INVALID_HANDLE_VALUE) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
CSsdpSearchRequest * pRequest = reinterpret_cast<CSsdpSearchRequest*>(hFindServices); HRESULT hr = CSsdpSearchRequestManager::Instance().HrGetFirstService(pRequest, ppSsdpService); if(FAILED(hr)) { SetLastError(DwWin32ErrorFromHr(hr)); } return S_OK == hr; }
BOOL WINAPI GetNextService(HANDLE hFindServices, PSSDP_MESSAGE *ppSsdpService) { if (!cInitialized) { SetLastError(ERROR_NOT_READY); return FALSE; }
if (!ppSsdpService || !hFindServices || hFindServices == INVALID_HANDLE_VALUE) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; }
CSsdpSearchRequest * pRequest = reinterpret_cast<CSsdpSearchRequest*>(hFindServices); HRESULT hr = CSsdpSearchRequestManager::Instance().HrGetNextService(pRequest, ppSsdpService); if(FAILED(hr)) { SetLastError(DwWin32ErrorFromHr(hr)); } return S_OK == hr; }
|