From e7f7deadc389558ea4be1faf15dc1e4f09ccb358 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Thu, 11 Apr 2019 23:50:55 +0200 Subject: [PATCH 1/2] Workaround zeroconf breaking because of broken multicast hardware --- .../hardware/network/avahi/qtavahiservice.cpp | 45 ++++++++++++++++--- .../hardware/network/avahi/qtavahiservice.h | 8 ++++ 2 files changed, 48 insertions(+), 5 deletions(-) diff --git a/libnymea-core/hardware/network/avahi/qtavahiservice.cpp b/libnymea-core/hardware/network/avahi/qtavahiservice.cpp index 5d74cc0f..022193e4 100644 --- a/libnymea-core/hardware/network/avahi/qtavahiservice.cpp +++ b/libnymea-core/hardware/network/avahi/qtavahiservice.cpp @@ -53,6 +53,7 @@ #include "loggingcategories.h" #include +#include namespace nymeaserver { @@ -66,6 +67,14 @@ QtAvahiService::QtAvahiService(QObject *parent) : d_ptr->client = new QtAvahiClient(this); d_ptr->client->start(); + + m_reregisterTimer.setInterval(60000); + m_reregisterTimer.setSingleShot(true); + connect(&m_reregisterTimer, &QTimer::timeout, this, [this](){ + qCDebug(dcAvahi()) << "Re-registering services."; + resetService(); + registerService(m_name, m_hostAddress, m_port, m_serviceType, m_txtRecords); + }); } /*! Destructs this \l{QtAvahiService}. */ @@ -124,6 +133,14 @@ bool QtAvahiService::registerService(const QString &name, const QHostAddress &ho return false; } + // Cache all values locally at first + m_name = name; + m_hostAddress = hostAddress; + m_port = port; + m_serviceType = serviceType; + m_txtRecords = txtRecords; + + // Now set up avahi d_ptr->name = name; d_ptr->hostAddress = hostAddress; d_ptr->port = port; @@ -190,6 +207,10 @@ bool QtAvahiService::registerService(const QString &name, const QHostAddress &ho return false; } + // Reregister every minute in order to work around low quality network hardware which + // doesn't properly keep multicast sessions alive. + // https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=736641 + m_reregisterTimer.start(); return true; } @@ -204,6 +225,7 @@ void QtAvahiService::resetService() d_ptr->serviceList = nullptr; } avahi_entry_group_reset(d_ptr->group); + m_reregisterTimer.stop(); } /*! Update the TXT record of this service. Returns true of the record could be updated. */ @@ -212,11 +234,26 @@ bool QtAvahiService::updateTxtRecord(const QHash &txtRecords) if (!d_ptr->group) return false; + m_txtRecords = txtRecords; + // Add the service + AvahiIfIndex ifIndex = AVAHI_IF_UNSPEC; + if (d_ptr->hostAddress != QHostAddress("0.0.0.0")) { + foreach (const QNetworkInterface &interface, QNetworkInterface::allInterfaces()) { + foreach (const QNetworkAddressEntry &addressEntry, interface.addressEntries()) { + QPair subnet = QHostAddress::parseSubnet(addressEntry.ip().toString() + "/" + addressEntry.netmask().toString()); + if (d_ptr->hostAddress.isInSubnet(subnet.first, subnet.second)) { + ifIndex = interface.index(); + break; + } + } + } + } + d_ptr->serviceList = QtAvahiServicePrivate::createTxtList(txtRecords); d_ptr->error = avahi_entry_group_update_service_txt_strlst(d_ptr->group, - AVAHI_IF_UNSPEC, - AVAHI_PROTO_INET, + ifIndex, + d_ptr->hostAddress.protocol() == QAbstractSocket::IPv6Protocol ? AVAHI_PROTO_INET6 : AVAHI_PROTO_INET, (AvahiPublishFlags) 0, d_ptr->name.toLatin1().data(), d_ptr->type.toLatin1().data(), @@ -254,7 +291,7 @@ bool QtAvahiService::handlCollision() char* alt = avahi_alternative_service_name(name().toStdString().data()); QString alternativeServiceName = QLatin1String(alt); free(alt); - qCDebug(dcAvahi()) << "Service name colision. Picking alternative service name" << alternativeServiceName; + qCDebug(dcAvahi()) << "Service name collision. Picking alternative service name" << alternativeServiceName; resetService(); return registerService(alternativeServiceName, hostAddress(), port(), serviceType(), txtRecords()); @@ -284,8 +321,6 @@ void QtAvahiService::onStateChanged(const QtAvahiServiceState &state) case QtAvahiServiceStateFailure: qCWarning(dcAvahi()) << this << "failure: " << errorString(); break; - default: - break; } } diff --git a/libnymea-core/hardware/network/avahi/qtavahiservice.h b/libnymea-core/hardware/network/avahi/qtavahiservice.h index 6af0634c..339d48bd 100644 --- a/libnymea-core/hardware/network/avahi/qtavahiservice.h +++ b/libnymea-core/hardware/network/avahi/qtavahiservice.h @@ -27,6 +27,7 @@ #include #include #include +#include namespace nymeaserver { @@ -78,6 +79,13 @@ private: QtAvahiServiceState m_state; Q_DECLARE_PRIVATE(QtAvahiService) + QTimer m_reregisterTimer; + + QString m_name; + QHostAddress m_hostAddress; + quint16 m_port; + QString m_serviceType; + QHash m_txtRecords; }; QDebug operator <<(QDebug dbg, QtAvahiService *service); From b23d7c820ef52dc754fe1aa9b9d963e4316a88b2 Mon Sep 17 00:00:00 2001 From: Michael Zanetti Date: Fri, 12 Apr 2019 11:59:28 +0200 Subject: [PATCH 2/2] Clean up avahi debug messages --- .../hardware/network/avahi/qtavahiservice.cpp | 40 +++++++++++-------- .../hardware/network/avahi/qtavahiservice.h | 9 +++-- .../network/avahi/qtavahiservice_p.cpp | 4 +- .../qtavahiservicebrowserimplementation.cpp | 4 +- .../qtavahiservicebrowserimplementation_p.cpp | 2 +- libnymea/loggingcategories.cpp | 1 + libnymea/loggingcategories.h | 1 + server/main.cpp | 1 + 8 files changed, 36 insertions(+), 26 deletions(-) diff --git a/libnymea-core/hardware/network/avahi/qtavahiservice.cpp b/libnymea-core/hardware/network/avahi/qtavahiservice.cpp index 022193e4..70ebe537 100644 --- a/libnymea-core/hardware/network/avahi/qtavahiservice.cpp +++ b/libnymea-core/hardware/network/avahi/qtavahiservice.cpp @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2016 Simon Stürz * + * Copyright (C) 2019 Michael Zanetti * * * * This file is part of nymea. * * * @@ -61,7 +62,7 @@ namespace nymeaserver { QtAvahiService::QtAvahiService(QObject *parent) : QObject(parent), d_ptr(new QtAvahiServicePrivate), - m_state(QtAvahiServiceStateUncomitted) + m_state(QtAvahiServiceStateUncommitted) { connect(this, &QtAvahiService::serviceStateChanged, this, &QtAvahiService::onStateChanged); @@ -71,9 +72,9 @@ QtAvahiService::QtAvahiService(QObject *parent) : m_reregisterTimer.setInterval(60000); m_reregisterTimer.setSingleShot(true); connect(&m_reregisterTimer, &QTimer::timeout, this, [this](){ - qCDebug(dcAvahi()) << "Re-registering services."; - resetService(); - registerService(m_name, m_hostAddress, m_port, m_serviceType, m_txtRecords); + qCDebug(dcAvahiDebug()) << "Re-registering service" << this; + resetService(true); + registerService(m_name, m_hostAddress, m_port, m_serviceType, m_txtRecords, true); }); } @@ -125,7 +126,7 @@ QtAvahiService::QtAvahiServiceState QtAvahiService::state() const } /*! Register a new \l{QtAvahiService} with the given \a name and \a port. The service type can be specified with the \a serviceType string. The \a txtRecords records inform about additional information. Returns true if the service could be registered. */ -bool QtAvahiService::registerService(const QString &name, const QHostAddress &hostAddress, const quint16 &port, const QString &serviceType, const QHash &txtRecords) +bool QtAvahiService::registerService(const QString &name, const QHostAddress &hostAddress, const quint16 &port, const QString &serviceType, const QHash &txtRecords, bool silent) { // Check if the client is running if (!d_ptr->client->m_client || AVAHI_CLIENT_S_RUNNING != avahi_client_get_state(d_ptr->client->m_client)) { @@ -166,7 +167,8 @@ bool QtAvahiService::registerService(const QString &name, const QHostAddress &ho } } } - qCDebug(dcAvahi()) << "Registering avahi service" << name << hostAddress.toString() << port << serviceType << "on interface" << ifIndex; + + qCDebug((silent ? dcAvahiDebug() : dcAvahi())) << "Registering avahi service" << name << hostAddress.toString() << port << serviceType << "on interface" << ifIndex; d_ptr->serviceList = QtAvahiServicePrivate::createTxtList(txtRecords); d_ptr->error = avahi_entry_group_add_service_strlst(d_ptr->group, @@ -184,7 +186,7 @@ bool QtAvahiService::registerService(const QString &name, const QHostAddress &ho if (d_ptr->error) { if (d_ptr->error == AVAHI_ERR_COLLISION) { - if (!handlCollision()) { + if (!handleCollision()) { qCWarning(dcAvahi()) << this << "error:" << avahi_strerror(d_ptr->error); return false; } @@ -215,10 +217,14 @@ bool QtAvahiService::registerService(const QString &name, const QHostAddress &ho } /*! Remove this service from the local network. This \l{QtAvahiService} can be reused to register a new avahi service. */ -void QtAvahiService::resetService() +void QtAvahiService::resetService(bool silent) { - if (!d_ptr->group) + if (!d_ptr->group) { + qCWarning(dcAvahi()) << "Cannot unregister service. Service Group not existing."; return; + } + + qCDebug((silent ? dcAvahiDebug() : dcAvahi())) << "Unregistering service" << this; if (d_ptr->serviceList) { avahi_string_list_free(d_ptr->serviceList); @@ -286,7 +292,7 @@ QString QtAvahiService::errorString() const return avahi_strerror(avahi_client_errno(d_ptr->client->m_client)); } -bool QtAvahiService::handlCollision() +bool QtAvahiService::handleCollision() { char* alt = avahi_alternative_service_name(name().toStdString().data()); QString alternativeServiceName = QLatin1String(alt); @@ -305,21 +311,21 @@ void QtAvahiService::onStateChanged(const QtAvahiServiceState &state) m_state = state; switch (m_state) { - case QtAvahiServiceStateUncomitted: - qCDebug(dcAvahi()) << this << "state changed: uncomitted"; + case QtAvahiServiceStateUncommitted: + qCDebug(dcAvahiDebug()) << "Service state changed to Uncommitted:" << this; break; case QtAvahiServiceStateRegistering: - qCDebug(dcAvahi()) << this << "state changed: registering..."; + qCDebug(dcAvahiDebug()) << "Service state changed to Registering:" << this; break; case QtAvahiServiceStateEstablished: - qCDebug(dcAvahi()) << this << "state changed: established"; + qCDebug(dcAvahiDebug()) << "Service state changed to Established:" << this; break; case QtAvahiServiceStateCollision: - qCDebug(dcAvahi()) << this << "state changed: collision"; - handlCollision(); + qCDebug(dcAvahiDebug()) << "Service state changed to Collision:" << this; + handleCollision(); break; case QtAvahiServiceStateFailure: - qCWarning(dcAvahi()) << this << "failure: " << errorString(); + qCDebug(dcAvahiDebug()) << "Service state changed to Failure:" << this; break; } diff --git a/libnymea-core/hardware/network/avahi/qtavahiservice.h b/libnymea-core/hardware/network/avahi/qtavahiservice.h index 339d48bd..962db1fc 100644 --- a/libnymea-core/hardware/network/avahi/qtavahiservice.h +++ b/libnymea-core/hardware/network/avahi/qtavahiservice.h @@ -1,6 +1,7 @@ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Copyright (C) 2016 Simon Stürz * + * Copyright (C) 2019 Michael Zanetti * * * * This file is part of nymea. * * * @@ -39,7 +40,7 @@ class QtAvahiService : public QObject public: enum QtAvahiServiceState { - QtAvahiServiceStateUncomitted = 0, + QtAvahiServiceStateUncommitted = 0, QtAvahiServiceStateRegistering = 1, QtAvahiServiceStateEstablished = 2, QtAvahiServiceStateCollision = 3, @@ -57,8 +58,8 @@ public: QHash txtRecords() const; QtAvahiServiceState state() const; - bool registerService(const QString &name, const QHostAddress &hostAddress, const quint16 &port, const QString &serviceType = "_http._tcp", const QHash &txtRecords = QHash()); - void resetService(); + bool registerService(const QString &name, const QHostAddress &hostAddress, const quint16 &port, const QString &serviceType = "_http._tcp", const QHash &txtRecords = QHash(), bool silent = false); + void resetService(bool silent = false); bool updateTxtRecord(const QHash &txtRecords); @@ -72,7 +73,7 @@ protected: QtAvahiServicePrivate *d_ptr; private slots: - bool handlCollision(); + bool handleCollision(); void onStateChanged(const QtAvahiServiceState &state); private: diff --git a/libnymea-core/hardware/network/avahi/qtavahiservice_p.cpp b/libnymea-core/hardware/network/avahi/qtavahiservice_p.cpp index a88f4412..431e0494 100644 --- a/libnymea-core/hardware/network/avahi/qtavahiservice_p.cpp +++ b/libnymea-core/hardware/network/avahi/qtavahiservice_p.cpp @@ -50,7 +50,7 @@ void QtAvahiServicePrivate::callback(AvahiEntryGroup *group, AvahiEntryGroupStat switch (state) { case AVAHI_ENTRY_GROUP_UNCOMMITED: - emit service->serviceStateChanged(QtAvahiService::QtAvahiServiceStateUncomitted); + emit service->serviceStateChanged(QtAvahiService::QtAvahiServiceStateUncommitted); break; case AVAHI_ENTRY_GROUP_REGISTERING: emit service->serviceStateChanged(QtAvahiService::QtAvahiServiceStateRegistering); @@ -69,7 +69,7 @@ void QtAvahiServicePrivate::callback(AvahiEntryGroup *group, AvahiEntryGroupStat AvahiStringList *QtAvahiServicePrivate::createTxtList(const QHash &txt) { - AvahiStringList *list = NULL; + AvahiStringList *list = nullptr; if (txt.isEmpty()) return list; diff --git a/libnymea-core/hardware/network/avahi/qtavahiservicebrowserimplementation.cpp b/libnymea-core/hardware/network/avahi/qtavahiservicebrowserimplementation.cpp index cc1c6f86..a92560eb 100644 --- a/libnymea-core/hardware/network/avahi/qtavahiservicebrowserimplementation.cpp +++ b/libnymea-core/hardware/network/avahi/qtavahiservicebrowserimplementation.cpp @@ -46,7 +46,7 @@ QtAvahiServiceBrowserImplementation::QtAvahiServiceBrowserImplementation(QObject connect(d_ptr->client, &QtAvahiClient::clientStateChanged, this, &QtAvahiServiceBrowserImplementation::onClientStateChanged); - qCDebug(dcAvahi()) << "-->" << name() << "created successfully."; + qCDebug(dcAvahiDebug()) << "-->" << name() << "created successfully."; } /*! Destructs this \l{QtAvahiServiceBrowserImplementation}. */ @@ -86,7 +86,7 @@ bool QtAvahiServiceBrowserImplementation::enabled() const void QtAvahiServiceBrowserImplementation::onClientStateChanged(const QtAvahiClient::QtAvahiClientState &state) { if (state == QtAvahiClient::QtAvahiClientStateRunning) { - qCDebug(dcAvahi()) << "Service browser client connected."; + qCDebug(dcAvahiDebug()) << "Service browser client connected."; // Return if we already have a service type browser if (d_ptr->serviceTypeBrowser) return; diff --git a/libnymea-core/hardware/network/avahi/qtavahiservicebrowserimplementation_p.cpp b/libnymea-core/hardware/network/avahi/qtavahiservicebrowserimplementation_p.cpp index 6a25267a..26614345 100644 --- a/libnymea-core/hardware/network/avahi/qtavahiservicebrowserimplementation_p.cpp +++ b/libnymea-core/hardware/network/avahi/qtavahiservicebrowserimplementation_p.cpp @@ -60,7 +60,7 @@ void QtAvahiServiceBrowserImplementationPrivate::callbackServiceTypeBrowser(Avah case AVAHI_BROWSER_NEW: if (!serviceBrowser->m_serviceTypes.contains(type)) { serviceBrowser->m_serviceTypes.append(type); - qCDebug(dcAvahi()) << "[+] Service browser" << type; + qCDebug(dcAvahiDebug()) << "[+] Service browser" << type; serviceBrowser->createServiceBrowser(type); } break; diff --git a/libnymea/loggingcategories.cpp b/libnymea/loggingcategories.cpp index d93ddb5e..7eb94460 100644 --- a/libnymea/loggingcategories.cpp +++ b/libnymea/loggingcategories.cpp @@ -42,6 +42,7 @@ Q_LOGGING_CATEGORY(dcJsonRpcTraffic, "JsonRpcTraffic") Q_LOGGING_CATEGORY(dcRest, "Rest") Q_LOGGING_CATEGORY(dcOAuth2, "OAuth2") Q_LOGGING_CATEGORY(dcAvahi, "Avahi") +Q_LOGGING_CATEGORY(dcAvahiDebug, "AvahiDebug") Q_LOGGING_CATEGORY(dcUpnp, "UPnP") Q_LOGGING_CATEGORY(dcBluetooth, "Bluetooth") Q_LOGGING_CATEGORY(dcCloud, "Cloud") diff --git a/libnymea/loggingcategories.h b/libnymea/loggingcategories.h index 4d1a8d21..30366dc7 100644 --- a/libnymea/loggingcategories.h +++ b/libnymea/loggingcategories.h @@ -50,6 +50,7 @@ Q_DECLARE_LOGGING_CATEGORY(dcJsonRpcTraffic) Q_DECLARE_LOGGING_CATEGORY(dcRest) Q_DECLARE_LOGGING_CATEGORY(dcOAuth2) Q_DECLARE_LOGGING_CATEGORY(dcAvahi) +Q_DECLARE_LOGGING_CATEGORY(dcAvahiDebug) Q_DECLARE_LOGGING_CATEGORY(dcUpnp) Q_DECLARE_LOGGING_CATEGORY(dcBluetooth) Q_DECLARE_LOGGING_CATEGORY(dcCloud) diff --git a/server/main.cpp b/server/main.cpp index 5b26f6bd..56ef5639 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -122,6 +122,7 @@ int main(int argc, char *argv[]) "TimeManager", "Coap", "Avahi", + "AvahiDebug", "UPnP", "Cloud", "CloudTraffic",