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) {
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
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 "<instance-name>._<service-type>._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-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.data.ptr.name = createMdnsString(self.serviceInstance);
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.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();

View file

@ -13,30 +13,32 @@
#include <QString>
#include <QVector>
/*
#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 "<instance-name>._<service-type>._tcp.local."
uint16_t port;
QHostAddress address;
QHostAddress address; // An IPv4 address (IPv6 addresses are ignored)
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 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<QSocketNotifier *> 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-name>.<_service-type>._tcp.local."
QByteArray serviceType; // ie: "_<service-type>._tcp.local."
QByteArray serviceInstance; // ie: "<instance-name>._<service-type>._tcp.local."
QByteArray hostname; // ie: "<hostname>.local."
QVector<QHostAddress> addressesV4;
QVector<QHostAddress> addressesV6;
@ -63,8 +65,8 @@ public:
QHash<QByteArray, QByteArray> txtRecords;
};
// serviceType must be of the form "_<name>._<tcp/udp>.local"
Announcer(const QString &serviceName, const QString &serviceType, uint16_t port);
// serviceType must be of the form "_<service-type>._<tcp/udp>.local"
Announcer(const QString &instanceName, const QString &serviceType, uint16_t port);
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("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;

View file

@ -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})

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"