Add MDNS e2e test

This commit is contained in:
Albert Vaca Cintora 2023-08-06 12:02:04 +02:00
parent 80c34b28ac
commit 5e225a23cb
5 changed files with 87 additions and 22 deletions

View file

@ -98,12 +98,12 @@ static int query_callback(int sock, const struct sockaddr* from, size_t addrlen,
switch (record_type) { switch (record_type) {
case MDNS_RECORDTYPE_PTR: { 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-name>.<_service-type>._tcp.local." string // We don't use mdns_record_parse_ptr() because we want to extract just the service name instead of the full "<instance-name>._<service-type>._tcp.local." string
mdns_string_pair_t serviceNamePos = mdns_get_next_substring(data, size, record_offset); mdns_string_pair_t instanceNamePos = mdns_get_next_substring(data, size, record_offset);
discoveredService->name = QString::fromLatin1((char *)data + serviceNamePos.offset, serviceNamePos.length); discoveredService->name = QString::fromLatin1((char *)data + instanceNamePos.offset, instanceNamePos.length);
//static char serviceNameBuffer[256]; //static char instanceNameBuffer[256];
//mdns_string_t serviceName = mdns_record_parse_ptr(data, size, record_offset, record_length, serviceNameBuffer, sizeof(serviceNameBuffer)); //mdns_string_t instanceName = mdns_record_parse_ptr(data, size, record_offset, record_length, instanceNameBuffer, sizeof(instanceNameBuffer));
//discoveredService->name = QString::fromLatin1(serviceName.str, serviceName.length); //discoveredService->name = QString::fromLatin1(instanceName.str, instanceName.length);
if (discoveredService->address == QHostAddress::Null) { if (discoveredService->address == QHostAddress::Null) {
discoveredService->address = QHostAddress(from); // In case we don't receive a A record, use from as address 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.rclass = 0;
answer.ttl = 0; answer.ttl = 0;
switch (record_type) { switch (record_type) {
case MDNS_RECORDTYPE_PTR: // maps "<_service-type>._tcp.local." to "<service-name>.<_service-type>._tcp.local." case MDNS_RECORDTYPE_PTR: // maps "_<service-type>._tcp.local." to "<instance-name>._<service-type>._tcp.local."
answer.name = createMdnsString(self.serviceType); answer.name = createMdnsString(self.serviceType);
answer.data.ptr.name = createMdnsString(self.serviceInstance); answer.data.ptr.name = createMdnsString(self.serviceInstance);
break; break;
case MDNS_RECORDTYPE_SRV: // maps "<service-name>.<_service-type>._tcp.local." to "<hostname>.local." and port case MDNS_RECORDTYPE_SRV: // maps "<instance-name>._<service-type>._tcp.local." to "<hostname>.local." and port
answer.name = createMdnsString(self.serviceInstance); answer.name = createMdnsString(self.serviceInstance);
answer.data.srv.name = createMdnsString(self.hostname); answer.data.srv.name = createMdnsString(self.hostname);
answer.data.srv.port = self.port; answer.data.srv.port = self.port;
@ -544,14 +544,14 @@ int Announcer::listenForQueries()
return numSockets; 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(); self.serviceType = serviceType.toLatin1();
if (!self.serviceType.endsWith('.')) { if (!self.serviceType.endsWith('.')) {
// mdns.h needs all the qualified names to end with dot for some reason // mdns.h needs all the qualified names to end with dot for some reason
self.serviceType.append('.'); self.serviceType.append('.');
} }
self.serviceInstance = serviceName.toLatin1() + '.' + self.serviceType; self.serviceInstance = instanceName.toLatin1() + '.' + self.serviceType;
self.hostname = QHostInfo::localHostName().toLatin1() + ".local."; self.hostname = QHostInfo::localHostName().toLatin1() + ".local.";
self.port = port; self.port = port;
detectHostAddresses(); detectHostAddresses();

View file

@ -13,30 +13,32 @@
#include <QString> #include <QString>
#include <QVector> #include <QVector>
/* #include "kdeconnectcore_export.h"
/**
* A Qt wrapper for the mdns.h header-only library * A Qt wrapper for the mdns.h header-only library
* from https://github.com/mjansson/mdns * from https://github.com/mjansson/mdns
*/ */
namespace MdnsWrapper namespace MdnsWrapper
{ {
class Discoverer : public QObject class KDECONNECTCORE_EXPORT Discoverer : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
struct MdnsService { struct MdnsService {
QString name; QString name; // The instance-name part in "<instance-name>._<service-type>._tcp.local."
uint16_t port; uint16_t port;
QHostAddress address; QHostAddress address; // An IPv4 address (IPv6 addresses are ignored)
QMap<QString, QString> txtRecords; QMap<QString, QString> txtRecords;
}; };
// serviceType must be of the form "_<name>._<tcp/udp>.local" // serviceType must be of the form "_<service-type>._<tcp/udp>.local"
void startDiscovering(const QString &serviceType); void startDiscovering(const QString &serviceType);
void stopDiscovering(); void stopDiscovering();
void sendQuery(const QString &serviceName); void sendQuery(const QString &serviceType);
Q_SIGNALS: Q_SIGNALS:
void serviceFound(const MdnsWrapper::Discoverer::MdnsService &service); void serviceFound(const MdnsWrapper::Discoverer::MdnsService &service);
@ -48,14 +50,14 @@ private:
QVector<QSocketNotifier *> responseSocketNotifiers; QVector<QSocketNotifier *> responseSocketNotifiers;
}; };
class Announcer : public QObject class KDECONNECTCORE_EXPORT Announcer : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
struct AnnouncedInfo { struct AnnouncedInfo {
QByteArray serviceType; // ie: "<_service-type>._tcp.local." QByteArray serviceType; // ie: "_<service-type>._tcp.local."
QByteArray serviceInstance; // ie: "<service-name>.<_service-type>._tcp.local." QByteArray serviceInstance; // ie: "<instance-name>._<service-type>._tcp.local."
QByteArray hostname; // ie: "<hostname>.local." QByteArray hostname; // ie: "<hostname>.local."
QVector<QHostAddress> addressesV4; QVector<QHostAddress> addressesV4;
QVector<QHostAddress> addressesV6; QVector<QHostAddress> addressesV6;
@ -63,8 +65,8 @@ public:
QHash<QByteArray, QByteArray> txtRecords; QHash<QByteArray, QByteArray> txtRecords;
}; };
// serviceType must be of the form "_<name>._<tcp/udp>.local" // serviceType must be of the form "_<service-type>._<tcp/udp>.local"
Announcer(const QString &serviceName, const QString &serviceType, uint16_t port); Announcer(const QString &instanceName, const QString &serviceType, uint16_t port);
void putTxtRecord(const QString &key, const QString &value) void putTxtRecord(const QString &key, const QString &value)
{ {

View file

@ -23,7 +23,7 @@ MdnsDiscovery::MdnsDiscovery(LanLinkProvider *lanLinkProvider)
mdnsAnnouncer.putTxtRecord(QStringLiteral("type"), config.deviceType().toString()); mdnsAnnouncer.putTxtRecord(QStringLiteral("type"), config.deviceType().toString());
mdnsAnnouncer.putTxtRecord(QStringLiteral("protocol"), QString::number(NetworkPacket::s_protocolVersion)); 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) { if (KdeConnectConfig::instance().deviceId() == service.name) {
qCDebug(KDECONNECT_CORE) << "Discovered myself, ignoring"; qCDebug(KDECONNECT_CORE) << "Discovered myself, ignoring";
return; return;

View file

@ -12,3 +12,4 @@ set(kdeconnect_libraries
ecm_add_test(pluginloadtest.cpp LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(pluginloadtest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
ecm_add_test(sendfiletest.cpp LINK_LIBRARIES ${kdeconnect_libraries}) ecm_add_test(sendfiletest.cpp LINK_LIBRARIES ${kdeconnect_libraries})
ecm_add_test(mdnstest.cpp LINK_LIBRARIES ${kdeconnect_libraries})

62
tests/mdnstest.cpp Normal file
View file

@ -0,0 +1,62 @@
/**
* SPDX-FileCopyrightText: 2023 Albert Vaca Cintora <albertvaka@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
*/
#include <QCoreApplication>
#include <QObject>
#include <QSignalSpy>
#include <QSocketNotifier>
#include <QStandardPaths>
#include <QTest>
#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"