📄 connectionpoint.cpp
字号:
}
// GetBestIP
// gets HTTP URL and returns best IP address that can be used by device at this URL to call us back
static DWORD GetBestAddress(LPCSTR pszUrl, unsigned nPort, LPSTR pszAddress, int nSize)
{
char pszHost[INTERNET_MAX_HOST_NAME_LENGTH];
URL_COMPONENTSA urlComp = {0};
DWORD result;
urlComp.dwStructSize = sizeof(URL_COMPONENTS);
urlComp.lpszHostName = pszHost;
urlComp.dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
if(InternetCrackUrlA(pszUrl, 0, ICU_DECODE, &urlComp))
{
struct addrinfo hints = {0}, *ai = NULL;
DWORD dwIndex;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
if(ERROR_SUCCESS == (result = getaddrinfo(pszHost, "", &hints, &ai)) &&
ERROR_SUCCESS == (result = GetBestInterfaceEx(ai->ai_addr, &dwIndex)))
{
PIP_ADAPTER_ADDRESSES pAdapterAddresses = NULL;
DWORD ulBufSize;
// Find out size of returned buffer
result = GetAdaptersAddresses(
ai->ai_addr->sa_family,
GAA_FLAG_SKIP_ANYCAST |GAA_FLAG_SKIP_DNS_SERVER,
NULL,
pAdapterAddresses,
&ulBufSize
);
if(ulBufSize)
{
// Allocate sufficient Space
pAdapterAddresses = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(malloc(ulBufSize));
if(pAdapterAddresses !=NULL)
{
// Get Adapter List
result = GetAdaptersAddresses(
ai->ai_addr->sa_family,
GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_MULTICAST,
NULL,
pAdapterAddresses,
&ulBufSize
);
if(result == ERROR_SUCCESS)
{
for(PIP_ADAPTER_ADDRESSES pAdapterIter = pAdapterAddresses; pAdapterIter; pAdapterIter = pAdapterIter->Next)
{
if(ai->ai_addr->sa_family == AF_INET6 && pAdapterIter->Ipv6IfIndex != dwIndex ||
ai->ai_addr->sa_family == AF_INET && pAdapterIter->IfIndex != dwIndex)
continue;
PIP_ADAPTER_UNICAST_ADDRESS pUnicastAddress;
// found the adapter - get best address
for(pUnicastAddress = pAdapterIter->FirstUnicastAddress; pUnicastAddress; pUnicastAddress = pUnicastAddress->Next)
{
if(ai->ai_addr->sa_family == AF_INET)
{
if(pUnicastAddress->Address.lpSockaddr->sa_family == AF_INET)
// if device address is IPv4 use first IPv4 address for subscription callback
break;
}
else
{
ASSERT(ai->ai_addr->sa_family == AF_INET6);
if(IN6_IS_ADDR_SITELOCAL(&((PSOCKADDR_IN6)ai->ai_addr)->sin6_addr))
if(IN6_IS_ADDR_SITELOCAL(&((PSOCKADDR_IN6)pUnicastAddress->Address.lpSockaddr)->sin6_addr))
// if device address is site-local use first site-local address for subscription callback
break;
else;
else
if(IN6_IS_ADDR_LINKLOCAL(&((PSOCKADDR_IN6)pUnicastAddress->Address.lpSockaddr)->sin6_addr))
// if device address is other than site-local use first link-local address for subscription callback
break;
}
}
if(pUnicastAddress)
{
wchar_t pwszAddress[256];
if(ai->ai_addr->sa_family == AF_INET6)
{
ASSERT(pUnicastAddress->Address.lpSockaddr->sa_family == AF_INET6);
((PSOCKADDR_IN6)pUnicastAddress->Address.lpSockaddr)->sin6_port = htons(nPort);
((PSOCKADDR_IN6)pUnicastAddress->Address.lpSockaddr)->sin6_scope_id = 0;
}
if(ai->ai_addr->sa_family == AF_INET)
{
ASSERT(pUnicastAddress->Address.lpSockaddr->sa_family == AF_INET);
((PSOCKADDR_IN)pUnicastAddress->Address.lpSockaddr)->sin_port = htons(nPort);
}
result = WSAAddressToString((LPSOCKADDR)pUnicastAddress->Address.lpSockaddr, pUnicastAddress->Address.iSockaddrLength, NULL, pwszAddress, &(ulBufSize = sizeof(pwszAddress)));
wcstombs(pszAddress, pwszAddress, nSize);
}
else
{
// couldn't find proper address - should never happen
result = ERROR_INVALID_DATA;
}
break;
}
}
free(pAdapterAddresses);
}
else
{
result = ERROR_OUTOFMEMORY;
}
}
}
if(ai)
freeaddrinfo(ai);
}
else
{
result = GetLastError();
}
return result;
}
// subscribe
HRESULT ConnectionPoint::sink::subscribe(LPCSTR pszEventsURL /*= NULL*/)
{
assert(m_strSID.empty());
HttpRequest request;
char pszCallback[INTERNET_MAX_URL_LENGTH];
char pszIP[256];
DWORD dw, result;
if(pszEventsURL)
m_strEventsURL = pszEventsURL;
m_bInitialEventReceived = false;
// format callback URL
if(ERROR_SUCCESS != (result = GetBestAddress(m_strEventsURL, upnp_config::port(), pszIP, sizeof(pszIP))))
return HRESULT_FROM_WIN32(result);
sprintf(pszCallback, "<http://%s/upnpisapi?%s>", pszIP, m_pchQueryString);
// prepare SUBSCRIBE request
if(!request.Open("SUBSCRIBE", m_strEventsURL, "HTTP/1.1"))
return request.GetHresult();
request.AddHeader("CALLBACK", pszCallback);
request.AddHeader("NT", "upnp:event");
request.AddHeader("TIMEOUT", "Second-1800"); // request subscription for 30 minutes
// send request
if(!request.Send())
return request.GetHresult();
if(HTTP_STATUS_OK != request.GetStatus())
return E_FAIL; // TO DO: error
char pszBuffer[50];
if(!request.GetHeader("TIMEOUT", pszBuffer, &(dw = sizeof(pszBuffer)/sizeof(*pszBuffer))))
return request.GetHresult();
if(1 != sscanf(pszBuffer, "Second-%d", &m_dwTimeoutSeconds))
return E_FAIL; // TO DO: error
m_strSID.reserve(10);
if(!request.GetHeader("SID", m_strSID.get_buffer(), &(dw = m_strSID.capacity())))
{
m_strSID.reserve(dw);
if(!request.GetHeader("SID", m_strSID.get_buffer(), &(dw = m_strSID.capacity())))
return request.GetHresult();
}
// leave 7% margin
m_timerResubscribe.start(m_dwTimeoutSeconds * 930, this);
return S_OK;
}
// resubscribe
HRESULT ConnectionPoint::sink::resubscribe()
{
assert(!m_strSID.empty());
HttpRequest request;
// prepare SUBSCRIBE request
if(!request.Open("SUBSCRIBE", m_strEventsURL, "HTTP/1.1"))
return request.GetHresult();
// add SID header
request.AddHeader("SID", m_strSID);
request.AddHeader("TIMEOUT", "Second-1800"); // request subscription for 30 minutes
// send request
if(!request.Send())
return request.GetHresult();
if(HTTP_STATUS_PRECOND_FAILED == request.GetStatus())
{
// looks like we lost our subscription
m_strSID.resize(0);
return subscribe();
}
if(HTTP_STATUS_OK != request.GetStatus())
return E_FAIL; // TO DO: error
DWORD dw;
char pszBuffer[50];
if(!request.GetHeader("TIMEOUT", pszBuffer, &(dw = sizeof(pszBuffer)/sizeof(*pszBuffer))))
return request.GetHresult();
if(1 != sscanf(pszBuffer, "Second-%d", &m_dwTimeoutSeconds))
return E_FAIL; // TO DO: error
// leave 7% margin
m_timerResubscribe.start(m_dwTimeoutSeconds * 930, this);
return S_OK;
}
// unsubscribe
HRESULT ConnectionPoint::sink::unsubscribe()
{
if(!m_strSID.empty())
{
HttpRequest request;
ce::string strSID = m_strSID;
m_timerResubscribe.stop();
m_strSID.resize(0);
// prepare UNSUBSCRIBE request
if(!request.Open("UNSUBSCRIBE", m_strEventsURL, "HTTP/1.1"))
return request.GetHresult();
// add SID header
request.AddHeader("SID", strSID);
// send request
if(!request.Send())
return request.GetHresult();
if(HTTP_STATUS_OK != request.GetStatus())
return E_FAIL; // TO DO: error
}
assert(m_strSID.empty());
return S_OK;
}
// ResubscribeTimerProc
DWORD WINAPI ConnectionPoint::sink::ResubscribeTimerProc(void* pvContext)
{
sink* pThis = reinterpret_cast<sink*>(pvContext);
if(FAILED(pThis->resubscribe()))
{
// "repair" subscription
pThis->unsubscribe();
if(FAILED(pThis->subscribe()))
// notify application that service is not available
pThis->byebye(pThis->m_strUSN);
}
return 0;
}
// event
void ConnectionPoint::sink::event(LPCWSTR pwszEventMessage, DWORD dwEventSEQ)
{
if(dwEventSEQ == 0)
{
// initial event
m_dwEventSEQ = 0;
m_bInitialEventReceived = true;
}
else
{
m_dwEventSEQ++;
// on overflow wrap to 1
if(m_dwEventSEQ == 0)
m_dwEventSEQ = 1;
}
// not consecutive event SEQ number -> we lost some events
if(dwEventSEQ != m_dwEventSEQ || !m_bInitialEventReceived)
{
// "repair" subscription
unsubscribe();
if(FAILED(subscribe()))
// notify application that service is not available
byebye(m_strUSN);
}
else
// parse event document
if(m_pReader->valid())
{
(*m_pReader)->putContentHandler(this);
HRESULT hr = (*m_pReader)->parse(ce::variant(pwszEventMessage));
if(FAILED(hr))
TraceTag(ttidError, "SAXXMLReader::parse returned error 0x%08x", hr);
(*m_pReader)->putContentHandler(NULL);
}
}
// alive
void ConnectionPoint::sink::alive(LPCWSTR pwszUSN, LPCWSTR pwszLocation, LPCWSTR pwszNLS, DWORD dwLifeTime)
{
if(dwLifeTime)
{
m_timerAlive.stop();
m_timerAlive.start(dwLifeTime * 1000, this);
m_pCallback->AliveNotification(pwszUSN, pwszLocation, pwszNLS, dwLifeTime);
}
}
// AliveTimerProc
DWORD WINAPI ConnectionPoint::sink::AliveTimerProc(VOID *pvContext)
{
sink* pThis = reinterpret_cast<sink*>(pvContext);
pThis->byebye(pThis->m_strUSN);
return 0;
}
// byebye
void ConnectionPoint::sink::byebye(LPCWSTR pwszUSN)
{
stop_timers();
m_pCallback->ServiceInstanceDied(pwszUSN);
}
// startDocument
HRESULT STDMETHODCALLTYPE ConnectionPoint::sink::startDocument(void)
{
SAXContentHandler::startDocument();
m_bParsingVariable = false;
m_bParsingProperty = false;
return S_OK;
}
// startElement
HRESULT STDMETHODCALLTYPE ConnectionPoint::sink::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)
{
static wchar_t* pwszPropertyElement =
L"<urn:schemas-upnp-org:event-1-0>"
L"<propertyset>"
L"<urn:schemas-upnp-org:event-1-0>"
L"<property>";
SAXContentHandler::startElement(pwchNamespaceUri, cchNamespaceUri, pwchLocalName, cchLocalName, pwchQName, cchQName, pAttributes);
if(m_bParsingProperty)
{
m_strName.assign(pwchLocalName, cchLocalName);
m_strValue.resize(0);
m_bParsingVariable = true;
m_bParsingProperty = false;
}
if(pwszPropertyElement == m_strFullElementName)
{
assert(!m_bParsingVariable);
m_bParsingProperty = true;
}
return S_OK;
}
// endElement
HRESULT STDMETHODCALLTYPE ConnectionPoint::sink::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)
{
if(m_bParsingVariable)
{
assert(!m_bParsingProperty);
assert(m_strName.size());
m_bParsingVariable = false;
m_strValue.trim(L"\n\r\t ");
m_pCallback->StateVariableChanged(m_strName, m_strValue);
}
return SAXContentHandler::endElement(pwchNamespaceUri, cchNamespaceUri, pwchLocalName, cchLocalName, pwchQName, cchQName);
}
// characters
HRESULT STDMETHODCALLTYPE ConnectionPoint::sink::characters(
/* [in] */ const wchar_t __RPC_FAR *pwchChars,
/* [in] */ int cchChars)
{
if(m_bParsingVariable)
m_strValue.append(pwchChars, cchChars);
return S_OK;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -