File indexing completed on 2024-04-28 15:22:06

0001 /*
0002     This file is part of the KDE project
0003 
0004     SPDX-FileCopyrightText: 2004 Jakub Stachowski <qbast@go2.pl>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "domainbrowser.h"
0010 #include "mdnsd-responder.h"
0011 #include "mdnsd-sdevent.h"
0012 #include "mdnsd-servicebrowser_p.h"
0013 #include "remoteservice.h"
0014 #include "servicebrowser.h"
0015 #include <QCoreApplication>
0016 #include <QHash>
0017 #include <QHostInfo>
0018 #include <QStringList>
0019 #include <QTimer>
0020 #include <dns_sd.h>
0021 
0022 #define TIMEOUT_WAN 2000
0023 #define TIMEOUT_LAN 200
0024 
0025 namespace KDNSSD
0026 {
0027 void query_callback(DNSServiceRef,
0028                     DNSServiceFlags flags,
0029                     uint32_t,
0030                     DNSServiceErrorType errorCode,
0031                     const char *serviceName,
0032                     const char *regtype,
0033                     const char *replyDomain,
0034                     void *context);
0035 
0036 ServiceBrowser::ServiceBrowser(const QString &type, bool autoResolve, const QString &domain, const QString &subtype)
0037     : d(new ServiceBrowserPrivate(this))
0038 {
0039     Q_D(ServiceBrowser);
0040     d->m_type = type;
0041     d->m_autoResolve = autoResolve;
0042     d->m_domain = domain;
0043     d->m_subtype = subtype;
0044     d->timeout.setSingleShot(true);
0045     connect(&d->timeout, SIGNAL(timeout()), d, SLOT(onTimeout()));
0046 }
0047 
0048 ServiceBrowser::State ServiceBrowser::isAvailable()
0049 {
0050     //  DNSServiceRef ref;
0051     //  bool ok (DNSServiceCreateConnection(&ref)==kDNSServiceErr_NoError);
0052     //  if (ok) DNSServiceRefDeallocate(ref);
0053     //  return (ok) ? Working : Stopped;
0054     return Working;
0055 }
0056 
0057 ServiceBrowser::~ServiceBrowser() = default;
0058 
0059 bool ServiceBrowser::isAutoResolving() const
0060 {
0061     Q_D(const ServiceBrowser);
0062     return d->m_autoResolve;
0063 }
0064 
0065 void ServiceBrowserPrivate::serviceResolved(bool success)
0066 {
0067     QObject *sender_obj = const_cast<QObject *>(sender());
0068     RemoteService *svr = static_cast<RemoteService *>(sender_obj);
0069     disconnect(svr, SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool)));
0070     QList<RemoteService::Ptr>::Iterator it = m_duringResolve.begin();
0071     QList<RemoteService::Ptr>::Iterator itEnd = m_duringResolve.end();
0072     while (it != itEnd && svr != (*it).data()) {
0073         ++it;
0074     }
0075     if (it != itEnd) {
0076         if (success) {
0077             m_services += (*it);
0078             Q_EMIT m_parent->serviceAdded(RemoteService::Ptr(svr));
0079         }
0080         m_duringResolve.erase(it);
0081         queryFinished();
0082     }
0083 }
0084 
0085 void ServiceBrowser::startBrowse()
0086 {
0087     Q_D(ServiceBrowser);
0088     if (d->isRunning()) {
0089         return;
0090     }
0091     d->m_finished = false;
0092     DNSServiceRef ref;
0093     QString fullType = d->m_type;
0094     if (!d->m_subtype.isEmpty()) {
0095         fullType = d->m_subtype + "._sub." + d->m_type;
0096     }
0097     if (DNSServiceBrowse(&ref, 0, 0, fullType.toLatin1().constData(), domainToDNS(d->m_domain).constData(), query_callback, reinterpret_cast<void *>(d))
0098         == kDNSServiceErr_NoError) {
0099         d->setRef(ref);
0100     }
0101     if (!d->isRunning()) {
0102         Q_EMIT finished();
0103     } else {
0104         d->timeout.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAN : TIMEOUT_WAN);
0105     }
0106 }
0107 
0108 void ServiceBrowserPrivate::queryFinished()
0109 {
0110     if (!m_duringResolve.count() && m_finished) {
0111         Q_EMIT m_parent->finished();
0112     }
0113 }
0114 
0115 QList<RemoteService::Ptr> ServiceBrowser::services() const
0116 {
0117     Q_D(const ServiceBrowser);
0118     return d->m_services;
0119 }
0120 
0121 void ServiceBrowser::virtual_hook(int, void *)
0122 {
0123 }
0124 
0125 RemoteService::Ptr ServiceBrowserPrivate::find(RemoteService::Ptr s, const QList<RemoteService::Ptr> &where) const
0126 {
0127     for (const RemoteService::Ptr &i : where)
0128         if (*s == *i) {
0129             return i;
0130         }
0131     return RemoteService::Ptr();
0132 }
0133 
0134 void ServiceBrowserPrivate::customEvent(QEvent *event)
0135 {
0136     if (event->type() == QEvent::User + SD_ERROR) {
0137         stop();
0138         m_finished = false;
0139         queryFinished();
0140     }
0141     if (event->type() == QEvent::User + SD_ADDREMOVE) {
0142         AddRemoveEvent *aev = static_cast<AddRemoveEvent *>(event);
0143         // m_type has useless trailing dot
0144         RemoteService::Ptr svr(new RemoteService(aev->m_name, aev->m_type.left(aev->m_type.length() - 1), aev->m_domain));
0145         if (aev->m_op == AddRemoveEvent::Add) {
0146             if (m_autoResolve) {
0147                 connect(svr.data(), SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool)));
0148                 m_duringResolve += svr;
0149                 svr->resolveAsync();
0150             } else {
0151                 m_services += svr;
0152                 Q_EMIT m_parent->serviceAdded(svr);
0153             }
0154         } else {
0155             RemoteService::Ptr found = find(svr, m_duringResolve);
0156             if (found) {
0157                 m_duringResolve.removeAll(found);
0158             } else {
0159                 found = find(svr, m_services);
0160                 if (found) {
0161                     Q_EMIT m_parent->serviceRemoved(found);
0162                     m_services.removeAll(found);
0163                 }
0164             }
0165         }
0166         m_finished = aev->m_last;
0167         if (m_finished) {
0168             queryFinished();
0169         }
0170     }
0171 }
0172 
0173 void ServiceBrowserPrivate::onTimeout()
0174 {
0175     m_finished = true;
0176     queryFinished();
0177 }
0178 
0179 void query_callback(DNSServiceRef,
0180                     DNSServiceFlags flags,
0181                     uint32_t,
0182                     DNSServiceErrorType errorCode,
0183                     const char *serviceName,
0184                     const char *regtype,
0185                     const char *replyDomain,
0186                     void *context)
0187 {
0188     QObject *obj = reinterpret_cast<QObject *>(context);
0189     if (errorCode != kDNSServiceErr_NoError) {
0190         ErrorEvent err;
0191         QCoreApplication::sendEvent(obj, &err);
0192     } else {
0193         AddRemoveEvent arev((flags & kDNSServiceFlagsAdd) ? AddRemoveEvent::Add : AddRemoveEvent::Remove,
0194                             QString::fromUtf8(serviceName),
0195                             regtype,
0196                             DNSToDomain(replyDomain),
0197                             !(flags & kDNSServiceFlagsMoreComing));
0198         QCoreApplication::sendEvent(obj, &arev);
0199     }
0200 }
0201 
0202 // TODO: Please Implement Me - Using a KResolver (if not natively)
0203 QHostAddress ServiceBrowser::resolveHostName(const QString &hostname)
0204 {
0205     return QHostAddress();
0206 }
0207 
0208 QString ServiceBrowser::getLocalHostName()
0209 {
0210     return QHostInfo::localHostName();
0211 }
0212 
0213 }
0214 
0215 #include "moc_mdnsd-servicebrowser_p.cpp"
0216 #include "moc_servicebrowser.cpp"