summaryrefslogtreecommitdiffstats
path: root/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/network/mdns/ZeroconfBrowserMDNS.cpp
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/network/mdns/ZeroconfBrowserMDNS.cpp')
-rw-r--r--xbmc/network/mdns/ZeroconfBrowserMDNS.cpp420
1 files changed, 420 insertions, 0 deletions
diff --git a/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp b/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp
new file mode 100644
index 0000000..c4a1c1e
--- /dev/null
+++ b/xbmc/network/mdns/ZeroconfBrowserMDNS.cpp
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2012-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "ZeroconfBrowserMDNS.h"
+
+#include "GUIUserMessages.h"
+#include "ServiceBroker.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIMessage.h"
+#include "guilib/GUIWindowManager.h"
+#include "network/DNSNameCache.h"
+#include "utils/log.h"
+
+#include <mutex>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#if defined(TARGET_WINDOWS)
+#include "platform/win32/WIN32Util.h"
+#endif //TARGET_WINDOWS
+
+using namespace std::chrono_literals;
+
+extern HWND g_hWnd;
+
+CZeroconfBrowserMDNS::CZeroconfBrowserMDNS()
+{
+ m_browser = NULL;
+}
+
+CZeroconfBrowserMDNS::~CZeroconfBrowserMDNS()
+{
+ std::unique_lock<CCriticalSection> lock(m_data_guard);
+ //make sure there are no browsers anymore
+ for (const auto& it : m_service_browsers)
+ doRemoveServiceType(it.first);
+
+#if defined(TARGET_WINDOWS_DESKTOP)
+ WSAAsyncSelect( (SOCKET) DNSServiceRefSockFD( m_browser ), g_hWnd, BONJOUR_BROWSER_EVENT, 0 );
+#elif defined(TARGET_WINDOWS_STORE)
+ // need to modify this code to use WSAEventSelect since WSAAsyncSelect is not supported
+ CLog::Log(LOGDEBUG, "{} is not implemented for TARGET_WINDOWS_STORE", __FUNCTION__);
+#endif //TARGET_WINDOWS
+
+ if (m_browser)
+ DNSServiceRefDeallocate(m_browser);
+ m_browser = NULL;
+}
+
+void DNSSD_API CZeroconfBrowserMDNS::BrowserCallback(DNSServiceRef browser,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *serviceName,
+ const char *regtype,
+ const char *replyDomain,
+ void *context)
+{
+
+ if (errorCode == kDNSServiceErr_NoError)
+ {
+ //get our instance
+ CZeroconfBrowserMDNS* p_this = reinterpret_cast<CZeroconfBrowserMDNS*>(context);
+ //store the service
+ ZeroconfService s(serviceName, regtype, replyDomain);
+
+ if (flags & kDNSServiceFlagsAdd)
+ {
+ CLog::Log(
+ LOGDEBUG,
+ "ZeroconfBrowserMDNS::BrowserCallback found service named: {}, type: {}, domain: {}",
+ s.GetName(), s.GetType(), s.GetDomain());
+ p_this->addDiscoveredService(browser, s);
+ }
+ else
+ {
+ CLog::Log(LOGDEBUG,
+ "ZeroconfBrowserMDNS::BrowserCallback service named: {}, type: {}, domain: {} "
+ "disappeared",
+ s.GetName(), s.GetType(), s.GetDomain());
+ p_this->removeDiscoveredService(browser, s);
+ }
+ if(! (flags & kDNSServiceFlagsMoreComing) )
+ {
+ CGUIMessage message(GUI_MSG_NOTIFY_ALL, 0, 0, GUI_MSG_UPDATE_PATH);
+ message.SetStringParam("zeroconf://");
+ CServiceBroker::GetGUI()->GetWindowManager().SendThreadMessage(message);
+ CLog::Log(LOGDEBUG, "ZeroconfBrowserMDNS::BrowserCallback sent gui update for path zeroconf://");
+ }
+ }
+ else
+ {
+ CLog::Log(LOGERROR, "ZeroconfBrowserMDNS::BrowserCallback returned (error = {})",
+ (int)errorCode);
+ }
+}
+
+void DNSSD_API CZeroconfBrowserMDNS::GetAddrInfoCallback(DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *hostname,
+ const struct sockaddr *address,
+ uint32_t ttl,
+ void *context
+ )
+{
+
+ if (errorCode)
+ {
+ CLog::Log(LOGERROR, "ZeroconfBrowserMDNS: GetAddrInfoCallback failed with error = {}",
+ (int)errorCode);
+ return;
+ }
+
+ std::string strIP;
+ CZeroconfBrowserMDNS* p_instance = static_cast<CZeroconfBrowserMDNS*> ( context );
+
+ if (address->sa_family == AF_INET)
+ strIP = inet_ntoa(((const struct sockaddr_in *)address)->sin_addr);
+
+ p_instance->m_resolving_service.SetIP(strIP);
+ p_instance->m_addrinfo_event.Set();
+}
+
+void DNSSD_API CZeroconfBrowserMDNS::ResolveCallback(DNSServiceRef sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode,
+ const char *fullname,
+ const char *hosttarget,
+ uint16_t port, /* In network byte order */
+ uint16_t txtLen,
+ const unsigned char *txtRecord,
+ void *context
+ )
+{
+
+ if (errorCode)
+ {
+ CLog::Log(LOGERROR, "ZeroconfBrowserMDNS: ResolveCallback failed with error = {}",
+ (int)errorCode);
+ return;
+ }
+
+ DNSServiceErrorType err;
+ CZeroconfBrowser::ZeroconfService::tTxtRecordMap recordMap;
+ std::string strIP;
+ CZeroconfBrowserMDNS* p_instance = static_cast<CZeroconfBrowserMDNS*> ( context );
+
+ p_instance->m_resolving_service.SetHostname(hosttarget);
+
+ for(uint16_t i = 0; i < TXTRecordGetCount(txtLen, txtRecord); ++i)
+ {
+ char key[256];
+ uint8_t valueLen;
+ const void *value;
+ std::string strvalue;
+ err = TXTRecordGetItemAtIndex(txtLen, txtRecord,i ,sizeof(key) , key, &valueLen, &value);
+ if(err != kDNSServiceErr_NoError)
+ continue;
+
+ if(value != NULL && valueLen > 0)
+ strvalue.append((const char *)value, valueLen);
+
+ recordMap.insert(std::make_pair(key, strvalue));
+ }
+ p_instance->m_resolving_service.SetTxtRecords(recordMap);
+ p_instance->m_resolving_service.SetPort(ntohs(port));
+ p_instance->m_resolved_event.Set();
+}
+
+/// adds the service to list of found services
+void CZeroconfBrowserMDNS::addDiscoveredService(DNSServiceRef browser, CZeroconfBrowser::ZeroconfService const& fcr_service)
+{
+ std::unique_lock<CCriticalSection> lock(m_data_guard);
+ tDiscoveredServicesMap::iterator browserIt = m_discovered_services.find(browser);
+ if(browserIt == m_discovered_services.end())
+ {
+ //first service by this browser
+ browserIt = m_discovered_services.insert(make_pair(browser, std::vector<std::pair<ZeroconfService, unsigned int> >())).first;
+ }
+ //search this service
+ std::vector<std::pair<ZeroconfService, unsigned int> >& services = browserIt->second;
+ std::vector<std::pair<ZeroconfService, unsigned int> >::iterator serviceIt = services.begin();
+ for( ; serviceIt != services.end(); ++serviceIt)
+ {
+ if(serviceIt->first == fcr_service)
+ break;
+ }
+ if(serviceIt == services.end())
+ services.push_back(std::make_pair(fcr_service, 1));
+ else
+ ++serviceIt->second;
+}
+
+void CZeroconfBrowserMDNS::removeDiscoveredService(DNSServiceRef browser, CZeroconfBrowser::ZeroconfService const& fcr_service)
+{
+ std::unique_lock<CCriticalSection> lock(m_data_guard);
+ tDiscoveredServicesMap::iterator browserIt = m_discovered_services.find(browser);
+ //search this service
+ std::vector<std::pair<ZeroconfService, unsigned int> >& services = browserIt->second;
+ std::vector<std::pair<ZeroconfService, unsigned int> >::iterator serviceIt = services.begin();
+ for( ; serviceIt != services.end(); ++serviceIt)
+ if(serviceIt->first == fcr_service)
+ break;
+ if(serviceIt != services.end())
+ {
+ //decrease refCount
+ --serviceIt->second;
+ if(!serviceIt->second)
+ {
+ //eventually remove the service
+ services.erase(serviceIt);
+ }
+ } else
+ {
+ //looks like we missed the announce, no problem though..
+ }
+}
+
+
+bool CZeroconfBrowserMDNS::doAddServiceType(const std::string& fcr_service_type)
+{
+ DNSServiceErrorType err;
+ DNSServiceRef browser = NULL;
+
+#if !defined(HAS_MDNS_EMBEDDED)
+ if(m_browser == NULL)
+ {
+ err = DNSServiceCreateConnection(&m_browser);
+ if (err != kDNSServiceErr_NoError)
+ {
+ CLog::Log(LOGERROR, "ZeroconfBrowserMDNS: DNSServiceCreateConnection failed with error = {}",
+ (int)err);
+ return false;
+ }
+#if defined(TARGET_WINDOWS_DESKTOP)
+ err = WSAAsyncSelect( (SOCKET) DNSServiceRefSockFD( m_browser ), g_hWnd, BONJOUR_BROWSER_EVENT, FD_READ | FD_CLOSE );
+ if (err != kDNSServiceErr_NoError)
+ CLog::Log(LOGERROR, "ZeroconfBrowserMDNS: WSAAsyncSelect failed with error = {}", (int)err);
+#elif defined(TARGET_WINDOWS_STORE)
+ // need to modify this code to use WSAEventSelect since WSAAsyncSelect is not supported
+ CLog::Log(LOGERROR, "{} is not implemented for TARGET_WINDOWS_STORE", __FUNCTION__);
+#endif // TARGET_WINDOWS_STORE
+ }
+#endif //!HAS_MDNS_EMBEDDED
+
+ {
+ std::unique_lock<CCriticalSection> lock(m_data_guard);
+ browser = m_browser;
+ err = DNSServiceBrowse(&browser, kDNSServiceFlagsShareConnection, kDNSServiceInterfaceIndexAny, fcr_service_type.c_str(), NULL, BrowserCallback, this);
+ }
+
+ if( err != kDNSServiceErr_NoError )
+ {
+ if (browser)
+ DNSServiceRefDeallocate(browser);
+
+ CLog::Log(LOGERROR, "ZeroconfBrowserMDNS: DNSServiceBrowse returned (error = {})", (int)err);
+ return false;
+ }
+
+ //store the browser
+ {
+ std::unique_lock<CCriticalSection> lock(m_data_guard);
+ m_service_browsers.insert(std::make_pair(fcr_service_type, browser));
+ }
+
+ return true;
+}
+
+bool CZeroconfBrowserMDNS::doRemoveServiceType(const std::string& fcr_service_type)
+{
+ //search for this browser and remove it from the map
+ DNSServiceRef browser = 0;
+ {
+ std::unique_lock<CCriticalSection> lock(m_data_guard);
+ tBrowserMap::iterator it = m_service_browsers.find(fcr_service_type);
+ if(it == m_service_browsers.end())
+ {
+ return false;
+ }
+ browser = it->second;
+ m_service_browsers.erase(it);
+ }
+
+ //remove the services of this browser
+ {
+ std::unique_lock<CCriticalSection> lock(m_data_guard);
+ tDiscoveredServicesMap::iterator it = m_discovered_services.find(browser);
+ if(it != m_discovered_services.end())
+ m_discovered_services.erase(it);
+ }
+
+ if (browser)
+ DNSServiceRefDeallocate(browser);
+
+ return true;
+}
+
+std::vector<CZeroconfBrowser::ZeroconfService> CZeroconfBrowserMDNS::doGetFoundServices()
+{
+ std::vector<CZeroconfBrowser::ZeroconfService> ret;
+ std::unique_lock<CCriticalSection> lock(m_data_guard);
+ for (const auto& it : m_discovered_services)
+ {
+ auto& services = it.second;
+ for(unsigned int i = 0; i < services.size(); ++i)
+ {
+ ret.push_back(services[i].first);
+ }
+ }
+ return ret;
+}
+
+bool CZeroconfBrowserMDNS::doResolveService(CZeroconfBrowser::ZeroconfService& fr_service, double f_timeout)
+{
+ DNSServiceErrorType err;
+ DNSServiceRef sdRef = NULL;
+
+ //start resolving
+ m_resolving_service = fr_service;
+ m_resolved_event.Reset();
+
+ err = DNSServiceResolve(&sdRef, 0, kDNSServiceInterfaceIndexAny, fr_service.GetName().c_str(), fr_service.GetType().c_str(), fr_service.GetDomain().c_str(), ResolveCallback, this);
+
+ if( err != kDNSServiceErr_NoError )
+ {
+ if (sdRef)
+ DNSServiceRefDeallocate(sdRef);
+
+ CLog::Log(LOGERROR, "ZeroconfBrowserMDNS: DNSServiceResolve returned (error = {})", (int)err);
+ return false;
+ }
+
+ err = DNSServiceProcessResult(sdRef);
+
+ if (err != kDNSServiceErr_NoError)
+ CLog::Log(LOGERROR,
+ "ZeroconfBrowserMDNS::doResolveService DNSServiceProcessResult returned (error = {})",
+ (int)err);
+
+#if defined(HAS_MDNS_EMBEDDED)
+ // when using the embedded mdns service the call to DNSServiceProcessResult
+ // above will not block until the resolving was finished - instead we have to
+ // wait for resolve to return or timeout
+ m_resolved_event.Wait(std::chrono::duration<double, std::milli>(f_timeout * 1000));
+#endif //HAS_MDNS_EMBEDDED
+ fr_service = m_resolving_service;
+
+ if (sdRef)
+ DNSServiceRefDeallocate(sdRef);
+
+ // resolve the hostname
+ if (!fr_service.GetHostname().empty())
+ {
+ std::string strIP;
+
+ // use mdns resolving
+ m_addrinfo_event.Reset();
+ sdRef = NULL;
+
+ err = DNSServiceGetAddrInfo(&sdRef, 0, kDNSServiceInterfaceIndexAny, kDNSServiceProtocol_IPv4, fr_service.GetHostname().c_str(), GetAddrInfoCallback, this);
+
+ if (err != kDNSServiceErr_NoError)
+ CLog::Log(LOGERROR, "ZeroconfBrowserMDNS: DNSServiceGetAddrInfo returned (error = {})",
+ (int)err);
+
+ err = DNSServiceProcessResult(sdRef);
+
+ if (err != kDNSServiceErr_NoError)
+ CLog::Log(
+ LOGERROR,
+ "ZeroconfBrowserMDNS::doResolveService DNSServiceProcessResult returned (error = {})",
+ (int)err);
+
+#if defined(HAS_MDNS_EMBEDDED)
+ // when using the embedded mdns service the call to DNSServiceProcessResult
+ // above will not block until the resolving was finished - instead we have to
+ // wait for resolve to return or timeout
+ // give it 2 secs for resolving (resolving in mdns is cached and queued
+ // in timeslices off 1 sec
+ m_addrinfo_event.Wait(2000ms);
+#endif //HAS_MDNS_EMBEDDED
+ fr_service = m_resolving_service;
+
+ if (sdRef)
+ DNSServiceRefDeallocate(sdRef);
+
+ // fall back to our resolver
+ if (fr_service.GetIP().empty())
+ {
+ CLog::Log(LOGWARNING,
+ "ZeroconfBrowserMDNS: Could not resolve hostname {} falling back to CDNSNameCache",
+ fr_service.GetHostname());
+ if (CDNSNameCache::Lookup(fr_service.GetHostname(), strIP))
+ fr_service.SetIP(strIP);
+ else
+ CLog::Log(LOGERROR, "ZeroconfBrowserMDNS: Could not resolve hostname {}",
+ fr_service.GetHostname());
+ }
+ }
+
+ return (!fr_service.GetIP().empty());
+}
+
+void CZeroconfBrowserMDNS::ProcessResults()
+{
+ std::unique_lock<CCriticalSection> lock(m_data_guard);
+ DNSServiceErrorType err = DNSServiceProcessResult(m_browser);
+ if (err != kDNSServiceErr_NoError)
+ CLog::Log(LOGERROR, "ZeroconfWIN: DNSServiceProcessResult returned (error = {})", (int)err);
+}