//+--------------------------------------------------------------------------- // // 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 #pragma hdrstop #include // 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(&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(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(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(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(&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(&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(SearchHandle); HRESULT hr = pRequest->HrCancelCallback(); if(FAILED(hr)) { SetLastError(DwWin32ErrorFromHr(hr)); } return S_OK == hr; } BOOL WINAPI FindServicesClose(HANDLE SearchHandle) { CSsdpSearchRequest * pRequest = reinterpret_cast(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(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(hFindServices); HRESULT hr = CSsdpSearchRequestManager::Instance().HrGetNextService(pRequest, ppSsdpService); if(FAILED(hr)) { SetLastError(DwWin32ErrorFromHr(hr)); } return S_OK == hr; }