KDNSSD

avahi-servicebrowser.cpp
1/*
2 This file is part of the KDE project
3
4 SPDX-FileCopyrightText: 2004 Jakub Stachowski <qbast@go2.pl>
5 SPDX-FileCopyrightText: 2018 Harald Sitter <sitter@kde.org>
6
7 SPDX-License-Identifier: LGPL-2.0-or-later
8*/
9
10#include "avahi-servicebrowser_p.h"
11#include "avahi_server_interface.h"
12#include "avahi_servicebrowser_interface.h"
13#include "servicebrowser.h"
14#include <QHash>
15#include <QHostAddress>
16#include <QStringList>
17
18namespace KDNSSD
19{
20ServiceBrowser::ServiceBrowser(const QString &type, bool autoResolve, const QString &domain, const QString &subtype)
21 : d(new ServiceBrowserPrivate(this))
22{
24 d->m_type = type;
25 d->m_subtype = subtype;
26 d->m_autoResolve = autoResolve;
27 d->m_domain = domain;
28 d->m_timer.setSingleShot(true);
29}
30
32{
33 org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus());
34 QDBusReply<int> rep = s.GetState();
35 return (rep.isValid() && rep.value() == 2) ? Working : Stopped;
36}
37
38ServiceBrowser::~ServiceBrowser() = default;
39
41{
42 Q_D(const ServiceBrowser);
43 return d->m_autoResolve;
44}
45
47{
49 if (d->m_running) {
50 return;
51 }
52
53 // Do not race!
54 // https://github.com/lathiat/avahi/issues/9
55 // Avahi's DBus API is incredibly racey with signals getting fired
56 // immediately after a request was made even though we may not yet be
57 // listening. In lieu of a proper upstream fix for this we'll unfortunately
58 // have to resort to this hack:
59 // We register to all signals regardless of path and then filter them once
60 // we know what "our" path is. This is much more fragile than a proper
61 // QDBusInterface assisted signal connection but unfortunately the only way
62 // we can reliably prevent signals getting lost in the race.
63 // This uses a fancy trick whereby using QDBusMessage as last argument will
64 // give us the correct signal argument types as well as the underlying
65 // message so that we may check the message path.
66 QDBusConnection::systemBus().connect("org.freedesktop.Avahi",
67 "",
68 "org.freedesktop.Avahi.ServiceBrowser",
69 "ItemNew",
70 d,
71 SLOT(gotGlobalItemNew(int, int, QString, QString, QString, uint, QDBusMessage)));
72 QDBusConnection::systemBus().connect("org.freedesktop.Avahi",
73 "",
74 "org.freedesktop.Avahi.ServiceBrowser",
75 "ItemRemove",
76 d,
77 SLOT(gotGlobalItemRemove(int, int, QString, QString, QString, uint, QDBusMessage)));
79 .connect("org.freedesktop.Avahi", "", "org.freedesktop.Avahi.ServiceBrowser", "AllForNow", d, SLOT(gotGlobalAllForNow(QDBusMessage)));
80 d->m_dbusObjectPath.clear();
81
82 org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus());
83
84 QString fullType = d->m_type;
85 if (!d->m_subtype.isEmpty()) {
86 fullType = d->m_subtype + QStringLiteral("._sub.") + d->m_type;
87 }
88 QDBusReply<QDBusObjectPath> rep = s.ServiceBrowserNew(-1, -1, fullType, domainToDNS(d->m_domain), 0);
89 if (!rep.isValid()) {
91 return;
92 }
93
94 d->m_dbusObjectPath = rep.value().path();
95 d->m_running = true;
96 d->m_browserFinished = true;
97
98 // This is held because we need to explicitly Free it!
99 d->m_browser = new org::freedesktop::Avahi::ServiceBrowser(s.service(), d->m_dbusObjectPath, s.connection());
100
101 connect(&d->m_timer, &QTimer::timeout, d, &ServiceBrowserPrivate::browserFinished);
102 d->m_timer.start(domainIsLocal(d->m_domain) ? TIMEOUT_LAST_SERVICE : TIMEOUT_START_WAN);
103}
104
105void ServiceBrowserPrivate::serviceResolved(bool success)
106{
107 QObject *sender_obj = const_cast<QObject *>(sender());
108 RemoteService *svr = static_cast<RemoteService *>(sender_obj);
109 disconnect(svr, SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool)));
110 QList<RemoteService::Ptr>::Iterator it = m_duringResolve.begin();
111 QList<RemoteService::Ptr>::Iterator itEnd = m_duringResolve.end();
112 while (it != itEnd && svr != (*it).data()) {
113 ++it;
114 }
115 if (it != itEnd) {
116 if (success) {
117 m_services += (*it);
118 Q_EMIT m_parent->serviceAdded(RemoteService::Ptr(svr));
119 }
120 m_duringResolve.erase(it);
121 queryFinished();
122 }
123}
124
125void ServiceBrowserPrivate::gotGlobalItemNew(int interface,
126 int protocol,
127 const QString &name,
128 const QString &type,
129 const QString &domain,
130 uint flags,
131 QDBusMessage msg)
132{
133 if (!isOurMsg(msg)) {
134 return;
135 }
136 gotNewService(interface, protocol, name, type, domain, flags);
137}
138
139void ServiceBrowserPrivate::gotGlobalItemRemove(int interface,
140 int protocol,
141 const QString &name,
142 const QString &type,
143 const QString &domain,
144 uint flags,
145 QDBusMessage msg)
146{
147 if (!isOurMsg(msg)) {
148 return;
149 }
150 gotRemoveService(interface, protocol, name, type, domain, flags);
151}
152
153void ServiceBrowserPrivate::gotGlobalAllForNow(QDBusMessage msg)
154{
155 if (!isOurMsg(msg)) {
156 return;
157 }
158 browserFinished();
159}
160
161RemoteService::Ptr ServiceBrowserPrivate::find(RemoteService::Ptr s, const QList<RemoteService::Ptr> &where) const
162{
163 for (const RemoteService::Ptr &i : where)
164 if (*s == *i) {
165 return i;
166 }
167 return RemoteService::Ptr();
168}
169
170void ServiceBrowserPrivate::gotNewService(int, int, const QString &name, const QString &type, const QString &domain, uint)
171{
172 m_timer.start(TIMEOUT_LAST_SERVICE);
173 RemoteService::Ptr svr(new RemoteService(name, type, domain));
174 if (m_autoResolve) {
175 connect(svr.data(), SIGNAL(resolved(bool)), this, SLOT(serviceResolved(bool)));
176 m_duringResolve += svr;
177 svr->resolveAsync();
178 } else {
179 m_services += svr;
180 Q_EMIT m_parent->serviceAdded(svr);
181 }
182}
183
184void ServiceBrowserPrivate::gotRemoveService(int, int, const QString &name, const QString &type, const QString &domain, uint)
185{
186 m_timer.start(TIMEOUT_LAST_SERVICE);
187 RemoteService::Ptr tmpl(new RemoteService(name, type, domain));
188 RemoteService::Ptr found = find(tmpl, m_duringResolve);
189 if (found) {
190 m_duringResolve.removeAll(found);
191 return;
192 }
193 found = find(tmpl, m_services);
194 if (!found) {
195 return;
196 }
197
198 Q_EMIT m_parent->serviceRemoved(found);
199 m_services.removeAll(found);
200}
201void ServiceBrowserPrivate::browserFinished()
202{
203 m_timer.stop();
204 m_browserFinished = true;
205 queryFinished();
206}
207
208void ServiceBrowserPrivate::queryFinished()
209{
210 if (!m_duringResolve.count() && m_browserFinished) {
211 Q_EMIT m_parent->finished();
212 }
213}
214
216{
217 Q_D(const ServiceBrowser);
218 return d->m_services;
219}
220
221void ServiceBrowser::virtual_hook(int, void *)
222{
223}
224
226{
227 org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus());
228
229 int protocol = 0;
230 QString name;
231 int aprotocol = 0;
232 QString address;
233 uint flags = 0;
234
235 QDBusReply<int> reply = s.ResolveHostName(-1, -1, hostname, 0, (unsigned int)0, protocol, name, aprotocol, address, flags);
236
237 if (reply.isValid()) {
238 return QHostAddress(address);
239 } else {
240 return QHostAddress();
241 }
242}
243
245{
246 org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus());
247
248 QDBusReply<QString> reply = s.GetHostName();
249
250 if (reply.isValid()) {
251 return reply.value();
252 } else {
253 return QString();
254 }
255}
256
257}
258
259#include "moc_avahi-servicebrowser_p.cpp"
260#include "moc_servicebrowser.cpp"
Describes a service published over DNS-SD, typically on a remote machine.
void resolveAsync()
Resolves the host name and port of service asynchronously.
Browses for network services advertised over DNS-SD.
static QHostAddress resolveHostName(const QString &hostname)
Resolves an mDNS hostname into an IP address.
bool isAutoResolving() const
Whether discovered services are resolved before being reported.
virtual void startBrowse()
Starts browsing for services.
QList< RemoteService::Ptr > services() const
The currently known services of the specified type.
State
Availability of DNS-SD services.
@ Working
the service is available
@ Stopped
not available because mDnsd or Avahi daemon is not running
void finished()
Emitted when the list of published services has settled.
static State isAvailable()
Checks availability of DNS-SD services.
ServiceBrowser(const QString &type, bool autoResolve=false, const QString &domain=QString(), const QString &subtype=QString())
Create a ServiceBrowser for a particular service type.
static QString getLocalHostName()
The mDNS hostname of the local machine.
const QList< QKeySequence > & find()
QDBusConnection connection() const const
QString service() const const
bool connect(const QString &service, const QString &path, const QString &interface, const QString &name, QObject *receiver, const char *slot)
QDBusConnection systemBus()
bool isValid() const const
iterator begin()
iterator end()
iterator erase(const_iterator begin, const_iterator end)
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void timeout()
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:17:48 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.