From fd25318a2060db69b7c34e135f8e241a76c10624 Mon Sep 17 00:00:00 2001 From: Albert Vaca Cintora Date: Sun, 6 Aug 2023 12:02:04 +0200 Subject: [PATCH] Add MDNS e2e test --- core/backends/lan/mdns_wrapper.cpp | 20 +++++----- core/backends/lan/mdns_wrapper.h | 24 ++++++----- core/backends/lan/mdnsdiscovery.cpp | 2 +- tests/CMakeLists.txt | 1 + tests/mdnstest.cpp | 62 +++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+), 22 deletions(-) create mode 100644 tests/mdnstest.cpp diff --git a/core/backends/lan/mdns_wrapper.cpp b/core/backends/lan/mdns_wrapper.cpp index c4104f4af..db944c4c9 100644 --- a/core/backends/lan/mdns_wrapper.cpp +++ b/core/backends/lan/mdns_wrapper.cpp @@ -98,12 +98,12 @@ static int query_callback(int sock, const struct sockaddr* from, size_t addrlen, switch (record_type) { case MDNS_RECORDTYPE_PTR: { - // We don't use mdns_record_parse_ptr() because we want to extract just the service name instead of the full ".<_service-type>._tcp.local." string - mdns_string_pair_t serviceNamePos = mdns_get_next_substring(data, size, record_offset); - discoveredService->name = QString::fromLatin1((char *)data + serviceNamePos.offset, serviceNamePos.length); - //static char serviceNameBuffer[256]; - //mdns_string_t serviceName = mdns_record_parse_ptr(data, size, record_offset, record_length, serviceNameBuffer, sizeof(serviceNameBuffer)); - //discoveredService->name = QString::fromLatin1(serviceName.str, serviceName.length); + // We don't use mdns_record_parse_ptr() because we want to extract just the service name instead of the full "._._tcp.local." string + mdns_string_pair_t instanceNamePos = mdns_get_next_substring(data, size, record_offset); + discoveredService->name = QString::fromLatin1((char *)data + instanceNamePos.offset, instanceNamePos.length); + //static char instanceNameBuffer[256]; + //mdns_string_t instanceName = mdns_record_parse_ptr(data, size, record_offset, record_length, instanceNameBuffer, sizeof(instanceNameBuffer)); + //discoveredService->name = QString::fromLatin1(instanceName.str, instanceName.length); if (discoveredService->address == QHostAddress::Null) { discoveredService->address = QHostAddress(from); // In case we don't receive a A record, use from as address } @@ -309,11 +309,11 @@ static mdns_record_t createMdnsRecord(const Announcer::AnnouncedInfo &self, answer.rclass = 0; answer.ttl = 0; switch (record_type) { - case MDNS_RECORDTYPE_PTR: // maps "<_service-type>._tcp.local." to ".<_service-type>._tcp.local." + case MDNS_RECORDTYPE_PTR: // maps "_._tcp.local." to "._._tcp.local." answer.name = createMdnsString(self.serviceType); answer.data.ptr.name = createMdnsString(self.serviceInstance); break; - case MDNS_RECORDTYPE_SRV: // maps ".<_service-type>._tcp.local." to ".local." and port + case MDNS_RECORDTYPE_SRV: // maps "._._tcp.local." to ".local." and port answer.name = createMdnsString(self.serviceInstance); answer.data.srv.name = createMdnsString(self.hostname); answer.data.srv.port = self.port; @@ -544,14 +544,14 @@ int Announcer::listenForQueries() return numSockets; } -Announcer::Announcer(const QString &serviceName, const QString &serviceType, uint16_t port) +Announcer::Announcer(const QString &instanceName, const QString &serviceType, uint16_t port) { self.serviceType = serviceType.toLatin1(); if (!self.serviceType.endsWith('.')) { // mdns.h needs all the qualified names to end with dot for some reason self.serviceType.append('.'); } - self.serviceInstance = serviceName.toLatin1() + '.' + self.serviceType; + self.serviceInstance = instanceName.toLatin1() + '.' + self.serviceType; self.hostname = QHostInfo::localHostName().toLatin1() + ".local."; self.port = port; detectHostAddresses(); diff --git a/core/backends/lan/mdns_wrapper.h b/core/backends/lan/mdns_wrapper.h index 1af2148a6..4070ab498 100644 --- a/core/backends/lan/mdns_wrapper.h +++ b/core/backends/lan/mdns_wrapper.h @@ -13,30 +13,32 @@ #include #include -/* +#include "kdeconnectcore_export.h" + +/** * A Qt wrapper for the mdns.h header-only library * from https://github.com/mjansson/mdns */ namespace MdnsWrapper { -class Discoverer : public QObject +class KDECONNECTCORE_EXPORT Discoverer : public QObject { Q_OBJECT public: struct MdnsService { - QString name; + QString name; // The instance-name part in "._._tcp.local." uint16_t port; - QHostAddress address; + QHostAddress address; // An IPv4 address (IPv6 addresses are ignored) QMap txtRecords; }; - // serviceType must be of the form "_._.local" + // serviceType must be of the form "_._.local" void startDiscovering(const QString &serviceType); void stopDiscovering(); - void sendQuery(const QString &serviceName); + void sendQuery(const QString &serviceType); Q_SIGNALS: void serviceFound(const MdnsWrapper::Discoverer::MdnsService &service); @@ -48,14 +50,14 @@ private: QVector responseSocketNotifiers; }; -class Announcer : public QObject +class KDECONNECTCORE_EXPORT Announcer : public QObject { Q_OBJECT public: struct AnnouncedInfo { - QByteArray serviceType; // ie: "<_service-type>._tcp.local." - QByteArray serviceInstance; // ie: ".<_service-type>._tcp.local." + QByteArray serviceType; // ie: "_._tcp.local." + QByteArray serviceInstance; // ie: "._._tcp.local." QByteArray hostname; // ie: ".local." QVector addressesV4; QVector addressesV6; @@ -63,8 +65,8 @@ public: QHash txtRecords; }; - // serviceType must be of the form "_._.local" - Announcer(const QString &serviceName, const QString &serviceType, uint16_t port); + // serviceType must be of the form "_._.local" + Announcer(const QString &instanceName, const QString &serviceType, uint16_t port); void putTxtRecord(const QString &key, const QString &value) { diff --git a/core/backends/lan/mdnsdiscovery.cpp b/core/backends/lan/mdnsdiscovery.cpp index 44bd6a014..43acb5b41 100644 --- a/core/backends/lan/mdnsdiscovery.cpp +++ b/core/backends/lan/mdnsdiscovery.cpp @@ -23,7 +23,7 @@ MdnsDiscovery::MdnsDiscovery(LanLinkProvider *lanLinkProvider) mdnsAnnouncer.putTxtRecord(QStringLiteral("type"), config.deviceType().toString()); mdnsAnnouncer.putTxtRecord(QStringLiteral("protocol"), QString::number(NetworkPacket::s_protocolVersion)); - connect(&mdnsDiscoverer, &MdnsWrapper::Discoverer::serviceFound, [lanLinkProvider](const MdnsWrapper::Discoverer::MdnsService &service) { + connect(&mdnsDiscoverer, &MdnsWrapper::Discoverer::serviceFound, this, [lanLinkProvider](const MdnsWrapper::Discoverer::MdnsService &service) { if (KdeConnectConfig::instance().deviceId() == service.name) { qCDebug(KDECONNECT_CORE) << "Discovered myself, ignoring"; return; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f833b098b..b0453d6aa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -12,3 +12,4 @@ set(kdeconnect_libraries ecm_add_test(pluginloadtest.cpp LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(sendfiletest.cpp LINK_LIBRARIES ${kdeconnect_libraries}) +ecm_add_test(mdnstest.cpp LINK_LIBRARIES ${kdeconnect_libraries}) diff --git a/tests/mdnstest.cpp b/tests/mdnstest.cpp new file mode 100644 index 000000000..7c075e0cc --- /dev/null +++ b/tests/mdnstest.cpp @@ -0,0 +1,62 @@ +/** + * SPDX-FileCopyrightText: 2023 Albert Vaca Cintora + * + * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL + */ + +#include +#include +#include +#include +#include +#include + +#include "core/backends/lan/mdns_wrapper.h" +#include "kdeconnect-version.h" + +class TestMdns : public QObject +{ + Q_OBJECT +public: + TestMdns() + { + } + +private Q_SLOTS: + void testAnounceAndDiscover() + { + QString instanceName = QStringLiteral("myinstance"); + uint16_t instancePort = 1716; + QString serviceType = QStringLiteral("_test._udp.local"); + QString txtKey = QStringLiteral("keyerino"); + QString txtValue = QStringLiteral("valuerino"); + + MdnsWrapper::Announcer announcer(instanceName, serviceType, instancePort); + announcer.putTxtRecord(txtKey, txtValue); + + MdnsWrapper::Discoverer discoverer; + + QSignalSpy spy(&discoverer, &MdnsWrapper::Discoverer::serviceFound); + + connect(&discoverer, &MdnsWrapper::Discoverer::serviceFound, this, [instanceName, instancePort, txtKey, txtValue](const MdnsWrapper::Discoverer::MdnsService &service) { + QCOMPARE(instanceName, service.name); + QCOMPARE(instancePort, service.port); + QVERIFY(service.txtRecords.size() == 1); + QVERIFY(service.txtRecords.contains(txtKey)); + QCOMPARE(txtValue, service.txtRecords.value(txtKey)); + }); + + announcer.startAnnouncing(); + discoverer.startDiscovering(serviceType); + + QVERIFY(spy.wait(2000)); + QVERIFY(spy.count() > 0); + + discoverer.stopDiscovering(); + announcer.stopAnnouncing(); + } +}; + +QTEST_MAIN(TestMdns); + +#include "mdnstest.moc"