diff --git a/src/avahi-remoteservice.cpp b/src/avahi-remoteservice.cpp index bcdacad..ef1b22c 100644 --- a/src/avahi-remoteservice.cpp +++ b/src/avahi-remoteservice.cpp @@ -1,6 +1,7 @@ /* This file is part of the KDE project * * Copyright (C) 2004, 2005 Jakub Stachowski + * Copyright (C) 2018 Harald Sitter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -56,22 +57,53 @@ void RemoteService::resolveAsync() } d->m_resolved = false; registerTypes(); + + // Do not race! + // https://github.com/lathiat/avahi/issues/9 + // Avahi's DBus API is incredibly racey with signals getting fired + // immediately after a request was made even though we may not yet be + // listening. In lieu of a proper upstream fix for this we'll unfortunately + // have to resort to this hack: + // We register to all signals regardless of path and then filter them once + // we know what "our" wath is. This is much more fragile than a proper + // QDBusInterface assisted signal connection but unfortunately the only way + // we can reliably prevent signals getting lost in the race. + // This uses a fancy trick whereby using QDBusMessage as last argument will + // give us the correct signal argument types as well as the underlying + // message so that we may check the message path. + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.ServiceResolver", + "Found", + d, + SLOT(gotGlobalFound(int,int,QString,QString,QString,QString, + int,QString,ushort,QList, + uint,QDBusMessage))); + QDBusConnection::systemBus() + .connect("org.freedesktop.Avahi", + "", + "org.freedesktop.Avahi.ServiceResolver", + "Failure", + d, + SLOT(gotGlobalError(QDBusMessage))); + + d->m_resolverPath.clear(); + //qDebug() << this << ":Starting resolve of : " << d->m_serviceName << " " << d->m_type << " " << d->m_domain << "\n"; org::freedesktop::Avahi::Server s(QStringLiteral("org.freedesktop.Avahi"), QStringLiteral("/"), QDBusConnection::systemBus()); //FIXME: don't use LOOKUP_NO_ADDRESS if NSS unavailable QDBusReply rep = s.ServiceResolverNew(-1, -1, d->m_serviceName, d->m_type, domainToDNS(d->m_domain), -1, 8 /*AVAHI_LOOKUP_NO_ADDRESS*/); + if (!rep.isValid()) { emit resolved(false); return; } - org::freedesktop::Avahi::ServiceResolver *b = new org::freedesktop::Avahi::ServiceResolver("org.freedesktop.Avahi", rep.value().path(), - QDBusConnection::systemBus()); - connect(b, SIGNAL(Found(int, int, const QString &, const QString &, const QString &, const QString &, int, const QString &, ushort, - const QList &, uint)), d, SLOT(gotFound(int, int, const QString &, const QString &, const QString &, const QString &, - int, const QString &, ushort, const QList &, uint))); - connect(b, SIGNAL(Failure(QString)), d, SLOT(gotError())); + // We cannot race up until here since our eventloop will be blocked. + // At least until we return. + d->m_resolverPath = rep.value().path(); d->m_running = true; } @@ -89,6 +121,35 @@ void RemoteServicePrivate::gotError() emit m_parent->resolved(false); } +void RemoteServicePrivate::gotGlobalFound(int interface, + int protocol, + const QString &name, + const QString &type, + const QString &domain, + const QString &host, + int aprotocol, + const QString &address, + ushort port, + const QList &txt, + QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + qDebug() << msg; + gotFound(interface, protocol, name, type, domain, host, aprotocol, address, + port, txt, flags); +} + +void RemoteServicePrivate::gotGlobalError(QDBusMessage msg) +{ + if (!isOurMsg(msg)) { + return; + } + qDebug() << msg; + gotError(); +} + void RemoteServicePrivate::gotFound(int, int, const QString &name, const QString &, const QString &domain, const QString &host, int, const QString &, ushort port, const QList &txt, uint) { m_serviceName = name; @@ -117,6 +178,16 @@ void RemoteServicePrivate::stop() m_running = false; } +bool RemoteServicePrivate::isOurMsg(const QDBusMessage &msg) const +{ + if (m_resolverPath.isEmpty() || m_resolverPath != msg.path()) { +#warning FIXME this needs to be dropped. due to the nature of the global listener we'll have lots of unexpected on async resolve calls + qWarning() << "UNEXPECTED MSG" << msg; + return false; + } + return true; +} + void RemoteService::virtual_hook(int, void *) { // BASE::virtual_hook(int, void*);