summaryrefslogtreecommitdiffstats
path: root/tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp')
-rw-r--r--tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp627
1 files changed, 377 insertions, 250 deletions
diff --git a/tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp b/tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp
index 20ecf8a6ab..af3a74a498 100644
--- a/tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp
+++ b/tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp
@@ -13,6 +13,13 @@
#include <QtNetwork/QNetworkDatagram>
#include <QtNetwork/QUdpSocket>
+#if QT_CONFIG(networkproxy)
+# include <QtNetwork/QNetworkProxyFactory>
+#endif
+#if QT_CONFIG(ssl)
+# include <QtNetwork/QSslSocket>
+#endif
+
#ifdef Q_OS_UNIX
# include <QtCore/QFile>
#else
@@ -35,12 +42,21 @@ class tst_QDnsLookup: public QObject
QString domainName(const QString &input);
QString domainNameList(const QString &input);
QStringList domainNameListAlternatives(const QString &input);
+
+ std::unique_ptr<QDnsLookup> lookupCommon(QDnsLookup::Type type, const QString &domain,
+ const QHostAddress &server = {}, quint16 port = 0,
+ QDnsLookup::Protocol protocol = QDnsLookup::Standard);
+ QStringList formatReply(const QDnsLookup *lookup) const;
+
+ void setNameserver_helper(QDnsLookup::Protocol protocol);
public slots:
void initTestCase();
private slots:
void lookupLocalhost();
void lookupRoot();
+ void lookupNxDomain_data();
+ void lookupNxDomain();
void lookup_data();
void lookup();
void lookupIdn_data() { lookup_data(); }
@@ -51,6 +67,8 @@ private slots:
void setNameserverLoopback();
void setNameserver_data();
void setNameserver();
+ void dnsOverTls_data();
+ void dnsOverTls();
void bindingsAndProperties();
void automatedBindings();
};
@@ -68,9 +86,11 @@ static const char preparedDnsQuery[] =
"\x00\x00\x06\x00\x01" // <root domain> IN SOA
;
-static QList<QHostAddress> systemNameservers()
+static QList<QHostAddress> systemNameservers(QDnsLookup::Protocol protocol)
{
QList<QHostAddress> result;
+ if (protocol != QDnsLookup::Standard)
+ return result;
#ifdef Q_OS_WIN
ULONG infosize = 0;
@@ -85,25 +105,29 @@ static QList<QHostAddress> systemNameservers()
}
}
#else
- QFile f("/etc/resolv.conf");
- if (!f.open(QIODevice::ReadOnly))
- return result;
-
- while (!f.atEnd()) {
- static const char command[] = "nameserver";
- QByteArray line = f.readLine().simplified();
- if (!line.startsWith(command))
- continue;
-
- QString addr = QLatin1StringView(line).mid(sizeof(command));
- result.emplaceBack(addr);
- }
+ auto parseFile = [&](QLatin1StringView path) {
+ QFile f(path);
+ if (!f.open(QIODevice::ReadOnly))
+ return;
+
+ while (!f.atEnd()) {
+ static const char command[] = "nameserver";
+ QByteArray line = f.readLine().simplified();
+ if (!line.startsWith(command))
+ continue;
+
+ QString addr = QLatin1StringView(line).mid(sizeof(command));
+ result.emplaceBack(addr);
+ }
+ };
+ parseFile("/etc/resolv.conf"_L1);
+ parseFile("/run/systemd/resolve/resolv.conf"_L1);
#endif
return result;
}
-static QList<QHostAddress> globalPublicNameservers()
+static QList<QHostAddress> globalPublicNameservers(QDnsLookup::Protocol proto)
{
const char *const candidates[] = {
// Google's dns.google
@@ -118,6 +142,56 @@ static QList<QHostAddress> globalPublicNameservers()
//"9.9.9.9", "2620:fe::9",
};
+ auto udpSendAndReceive = [](const QHostAddress &addr, QByteArray &data) {
+ QUdpSocket socket;
+ socket.connectToHost(addr, 53);
+ if (socket.waitForConnected(1))
+ socket.write(data);
+
+ if (!socket.waitForReadyRead(1000))
+ return socket.errorString();
+
+ QNetworkDatagram dgram = socket.receiveDatagram();
+ if (!dgram.isValid())
+ return socket.errorString();
+
+ data = dgram.data();
+ return QString();
+ };
+
+ auto tlsSendAndReceive = [](const QHostAddress &addr, QByteArray &data) {
+#if QT_CONFIG(ssl)
+ QSslSocket socket;
+ QDeadlineTimer timeout(2000);
+ socket.connectToHostEncrypted(addr.toString(), 853);
+ if (!socket.waitForEncrypted(2000))
+ return socket.errorString();
+
+ quint16 size = qToBigEndian<quint16>(data.size());
+ socket.write(reinterpret_cast<char *>(&size), sizeof(size));
+ socket.write(data);
+
+ if (!socket.waitForReadyRead(timeout.remainingTime()))
+ return socket.errorString();
+ if (socket.bytesAvailable() < 2)
+ return u"protocol error"_s;
+
+ socket.read(reinterpret_cast<char *>(&size), sizeof(size));
+ size = qFromBigEndian(size);
+
+ while (socket.bytesAvailable() < size) {
+ int remaining = timeout.remainingTime();
+ if (remaining < 0 || !socket.waitForReadyRead(remaining))
+ return socket.errorString();
+ }
+
+ data = socket.readAll();
+ return QString();
+#else
+ return u"SSL/TLS support not compiled in"_s;
+#endif
+ };
+
QList<QHostAddress> result;
QRandomGenerator &rng = *QRandomGenerator::system();
for (auto name : candidates) {
@@ -128,23 +202,18 @@ static QList<QHostAddress> globalPublicNameservers()
char *ptr = data.data();
qToBigEndian(id, ptr);
- QUdpSocket socket;
- socket.connectToHost(addr, 53);
- if (socket.waitForConnected(1))
- socket.write(data);
-
- if (!socket.waitForReadyRead(1000)) {
- qDebug() << addr << "discarded:" << socket.errorString();
- continue;
- }
-
- QNetworkDatagram dgram = socket.receiveDatagram();
- if (!dgram.isValid()) {
- qDebug() << addr << "discarded:" << socket.errorString();
+ QString errorString = [&] {
+ switch (proto) {
+ case QDnsLookup::Standard: return udpSendAndReceive(addr, data);
+ case QDnsLookup::DnsOverTls: return tlsSendAndReceive(addr, data);
+ }
+ Q_UNREACHABLE();
+ }();
+ if (!errorString.isEmpty()) {
+ qDebug() << addr << "discarded:" << errorString;
continue;
}
- data = dgram.data();
ptr = data.data();
if (data.size() < HeaderSize) {
qDebug() << addr << "discarded: reply too small";
@@ -171,6 +240,11 @@ void tst_QDnsLookup::initTestCase()
{
if (qgetenv("QTEST_ENVIRONMENT") == "ci")
dnsServersMustWork = true;
+
+#if QT_CONFIG(networkproxy)
+ // for DNS-over-TLS
+ QNetworkProxyFactory::setUseSystemConfiguration(true);
+#endif
}
QString tst_QDnsLookup::domainName(const QString &input)
@@ -209,17 +283,119 @@ QStringList tst_QDnsLookup::domainNameListAlternatives(const QString &input)
return alternatives;
}
+std::unique_ptr<QDnsLookup>
+tst_QDnsLookup::lookupCommon(QDnsLookup::Type type, const QString &domain,
+ const QHostAddress &server, quint16 port,
+ QDnsLookup::Protocol protocol)
+{
+ auto lookup = std::make_unique<QDnsLookup>(type, domainName(domain), protocol, server, port);
+ QObject::connect(lookup.get(), &QDnsLookup::finished,
+ &QTestEventLoop::instance(), &QTestEventLoop::exitLoop);
+ lookup->lookup();
+ QTestEventLoop::instance().enterLoopMSecs(Timeout);
+
+ QDnsLookup::Error error = lookup->error();
+ if (QTestEventLoop::instance().timeout())
+ error = QDnsLookup::TimeoutError;
+
+ if (!dnsServersMustWork && (error == QDnsLookup::ServerFailureError
+ || error == QDnsLookup::ServerRefusedError
+ || error == QDnsLookup::TimeoutError)) {
+ // It's not a QDnsLookup problem if the server refuses to answer the query.
+ // This happens for queries of type ANY through Dnsmasq, for example.
+ [&] {
+ auto me = QMetaEnum::fromType<QDnsLookup::Type>();
+ QString msg = u"Server refused or was unable to answer query; %1 type %3: %2"_s
+ .arg(domain, lookup->errorString(), QString(me.valueToKey(int(type))));
+ QSKIP(msg.toLocal8Bit());
+ }();
+ return {};
+ }
+
+ return lookup;
+}
+
+QStringList tst_QDnsLookup::formatReply(const QDnsLookup *lookup) const
+{
+ QStringList result;
+ QString domain = lookup->name();
+
+ auto shorter = [this](QString value) {
+ const QString &ending = usingIdnDomain ? idnDomain : normalDomain;
+ if (value.endsWith(ending))
+ value.chop(ending.size());
+ else
+ value += u'.';
+ return value;
+ };
+
+ for (const QDnsMailExchangeRecord &rr : lookup->mailExchangeRecords()) {
+ QString entry = u"MX %1 %2"_s.arg(rr.preference(), 5).arg(shorter(rr.exchange()));
+ if (rr.name() != domain)
+ entry = "MX unexpected label to "_L1 + rr.name();
+ result.append(std::move(entry));
+ }
+
+ for (const QDnsServiceRecord &rr : lookup->serviceRecords()) {
+ QString entry = u"SRV %1 %2 %3 %4"_s.arg(rr.priority(), 5).arg(rr.weight())
+ .arg(rr.port()).arg(shorter(rr.target()));
+ if (rr.name() != domain)
+ entry = "SRV unexpected label to "_L1 + rr.name();
+ result.append(std::move(entry));
+ }
+
+ auto addNameRecords = [&](QLatin1StringView rrtype, const QList<QDnsDomainNameRecord> &rrset) {
+ for (const QDnsDomainNameRecord &rr : rrset) {
+ QString entry = u"%1 %2"_s.arg(rrtype, shorter(rr.value()));
+ if (rr.name() != domain)
+ entry = rrtype + " unexpected label to "_L1 + rr.name();
+ result.append(std::move(entry));
+ }
+ };
+ addNameRecords("NS"_L1, lookup->nameServerRecords());
+ addNameRecords("PTR"_L1, lookup->pointerRecords());
+ addNameRecords("CNAME"_L1, lookup->canonicalNameRecords());
+
+ for (const QDnsHostAddressRecord &rr : lookup->hostAddressRecords()) {
+ if (rr.name() != domain)
+ continue; // A and AAAA may appear as extra records in the answer section
+ QHostAddress addr = rr.value();
+ result.append(u"%1 %2"_s
+ .arg(addr.protocol() == QHostAddress::IPv6Protocol ? "AAAA" : "A",
+ addr.toString()));
+ }
+
+ for (const QDnsTextRecord &rr : lookup->textRecords()) {
+ QString entry = "TXT"_L1;
+ for (const QByteArray &data : rr.values()) {
+ entry += u' ';
+ entry += QDebug::toString(data);
+ }
+ result.append(std::move(entry));
+ }
+
+ for (const QDnsTlsAssociationRecord &rr : lookup->tlsAssociationRecords()) {
+ QString entry = u"TLSA %1 %2 %3 %4"_s.arg(int(rr.usage())).arg(int(rr.selector()))
+ .arg(int(rr.matchType())).arg(rr.value().toHex().toUpper());
+ if (rr.name() != domain)
+ entry = "TLSA unexpected label to "_L1 + rr.name();
+ result.append(std::move(entry));
+ }
+
+ result.sort();
+ return result;
+}
+
void tst_QDnsLookup::lookupLocalhost()
{
- QDnsLookup lookup(QDnsLookup::Type::A, u"localhost"_s);
- lookup.lookup();
- QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
- QCOMPARE(lookup.error(), QDnsLookup::NoError);
+ auto lookup = lookupCommon(QDnsLookup::Type::A, u"localhost."_s);
+ QVERIFY(lookup);
+ QCOMPARE(lookup->error(), QDnsLookup::NoError);
- QList<QDnsHostAddressRecord> hosts = lookup.hostAddressRecords();
+ QList<QDnsHostAddressRecord> hosts = lookup->hostAddressRecords();
QCOMPARE(hosts.size(), 1);
QCOMPARE(hosts.at(0).value(), QHostAddress::LocalHost);
- QVERIFY2(hosts.at(0).name().startsWith(lookup.name()),
+ QVERIFY2(hosts.at(0).name().startsWith(lookup->name()),
qPrintable(hosts.at(0).name()));
}
@@ -228,12 +404,12 @@ void tst_QDnsLookup::lookupRoot()
#ifdef Q_OS_WIN
QSKIP("This test fails on Windows as it seems to treat the lookup as a local one.");
#else
- QDnsLookup lookup(QDnsLookup::Type::NS, u""_s);
- lookup.lookup();
- QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
- QCOMPARE(lookup.error(), QDnsLookup::NoError);
+ auto lookup = lookupCommon(QDnsLookup::Type::NS, u""_s);
+ if (!lookup)
+ return;
+ QCOMPARE(lookup->error(), QDnsLookup::NoError);
- const QList<QDnsDomainNameRecord> servers = lookup.nameServerRecords();
+ const QList<QDnsDomainNameRecord> servers = lookup->nameServerRecords();
QVERIFY(!servers.isEmpty());
for (const QDnsDomainNameRecord &ns : servers) {
QCOMPARE(ns.name(), QString());
@@ -242,215 +418,142 @@ void tst_QDnsLookup::lookupRoot()
#endif
}
+void tst_QDnsLookup::lookupNxDomain_data()
+{
+ QTest::addColumn<QDnsLookup::Type>("type");
+ QTest::addColumn<QString>("domain");
+
+ QTest::newRow("a") << QDnsLookup::A << "invalid.invalid";
+ QTest::newRow("aaaa") << QDnsLookup::AAAA << "invalid.invalid";
+ QTest::newRow("any") << QDnsLookup::ANY << "invalid.invalid";
+ QTest::newRow("mx") << QDnsLookup::MX << "invalid.invalid";
+ QTest::newRow("ns") << QDnsLookup::NS << "invalid.invalid";
+ QTest::newRow("ptr") << QDnsLookup::PTR << "invalid.invalid";
+ QTest::newRow("srv") << QDnsLookup::SRV << "invalid.invalid";
+ QTest::newRow("txt") << QDnsLookup::TXT << "invalid.invalid";
+}
+
+void tst_QDnsLookup::lookupNxDomain()
+{
+ QFETCH(QDnsLookup::Type, type);
+ QFETCH(QString, domain);
+
+ auto lookup = lookupCommon(type, domain);
+ if (!lookup)
+ return;
+ QCOMPARE(lookup->name(), domainName(domain));
+ QCOMPARE(lookup->type(), type);
+ QCOMPARE(lookup->error(), QDnsLookup::NotFoundError);
+}
+
void tst_QDnsLookup::lookup_data()
{
- QTest::addColumn<int>("type");
+ QTest::addColumn<QDnsLookup::Type>("type");
QTest::addColumn<QString>("domain");
- QTest::addColumn<int>("error");
- QTest::addColumn<QString>("cname");
- QTest::addColumn<QString>("host");
- QTest::addColumn<QString>("mx");
- QTest::addColumn<QString>("ns");
- QTest::addColumn<QString>("ptr");
- QTest::addColumn<QString>("srv");
- QTest::addColumn<QString>("txt");
-
- QTest::newRow("a-notfound") << int(QDnsLookup::A) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
- QTest::newRow("a-single") << int(QDnsLookup::A) << "a-single" << int(QDnsLookup::NoError) << "" << "192.0.2.1" << "" << "" << "" << "" << "";
- QTest::newRow("a-multi") << int(QDnsLookup::A) << "a-multi" << int(QDnsLookup::NoError) << "" << "192.0.2.1;192.0.2.2;192.0.2.3" << "" << "" << "" << "" << "";
- QTest::newRow("aaaa-notfound") << int(QDnsLookup::AAAA) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
- QTest::newRow("aaaa-single") << int(QDnsLookup::AAAA) << "aaaa-single" << int(QDnsLookup::NoError) << "" << "2001:db8::1" << "" << "" << "" << "" << "";
- QTest::newRow("aaaa-multi") << int(QDnsLookup::AAAA) << "aaaa-multi" << int(QDnsLookup::NoError) << "" << "2001:db8::1;2001:db8::2;2001:db8::3" << "" << "" << "" << "" << "";
-
- QTest::newRow("any-notfound") << int(QDnsLookup::ANY) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
- QTest::newRow("any-a-single") << int(QDnsLookup::ANY) << "a-single" << int(QDnsLookup::NoError) << "" << "192.0.2.1" << "" << "" << "" << "" << "";
- QTest::newRow("any-a-plus-aaaa") << int(QDnsLookup::ANY) << "a-plus-aaaa" << int(QDnsLookup::NoError) << "" << "198.51.100.1;2001:db8::1:1" << "" << "" << "" << "" << "";
- QTest::newRow("any-multi") << int(QDnsLookup::ANY) << "multi" << int(QDnsLookup::NoError) << "" << "198.51.100.1;198.51.100.2;198.51.100.3;2001:db8::1:1;2001:db8::1:2" << "" << "" << "" << "" << "";
-
- QTest::newRow("mx-notfound") << int(QDnsLookup::MX) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
- QTest::newRow("mx-single") << int(QDnsLookup::MX) << "mx-single" << int(QDnsLookup::NoError) << "" << "" << "10 multi" << "" << "" << "" << "";
- QTest::newRow("mx-single-cname") << int(QDnsLookup::MX) << "mx-single-cname" << int(QDnsLookup::NoError) << "" << "" << "10 cname" << "" << "" << "" << "";
- QTest::newRow("mx-multi") << int(QDnsLookup::MX) << "mx-multi" << int(QDnsLookup::NoError) << "" << "" << "10 multi;20 a-single" << "" << "" << "" << "";
- QTest::newRow("mx-multi-sameprio") << int(QDnsLookup::MX) << "mx-multi-sameprio" << int(QDnsLookup::NoError) << "" << ""
- << "10 multi;10 a-single|"
- "10 a-single;10 multi" << "" << "" << "" << "";
-
- QTest::newRow("ns-notfound") << int(QDnsLookup::NS) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
- QTest::newRow("ns-single") << int(QDnsLookup::NS) << "ns-single" << int(QDnsLookup::NoError) << "" << "" << "" << "ns11.cloudns.net." << "" << "" << "";
- QTest::newRow("ns-multi") << int(QDnsLookup::NS) << "ns-multi" << int(QDnsLookup::NoError) << "" << "" << "" << "ns11.cloudns.net.;ns12.cloudns.net." << "" << "" << "";
-
- QTest::newRow("ptr-notfound") << int(QDnsLookup::PTR) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("a-single") << QDnsLookup::A << "a-single"
+ << "A 192.0.2.1";
+ QTest::newRow("a-multi") << QDnsLookup::A << "a-multi"
+ << "A 192.0.2.1;A 192.0.2.2;A 192.0.2.3";
+ QTest::newRow("aaaa-single") << QDnsLookup::AAAA << "aaaa-single"
+ << "AAAA 2001:db8::1";
+ QTest::newRow("aaaa-multi") << QDnsLookup::AAAA << "aaaa-multi"
+ << "AAAA 2001:db8::1;AAAA 2001:db8::2;AAAA 2001:db8::3";
+
+ QTest::newRow("any-a-single") << QDnsLookup::ANY << "a-single"
+ << "A 192.0.2.1";
+ QTest::newRow("any-a-plus-aaaa") << QDnsLookup::ANY << "a-plus-aaaa"
+ << "A 198.51.100.1;AAAA 2001:db8::1:1";
+ QTest::newRow("any-multi") << QDnsLookup::ANY << "multi"
+ << "A 198.51.100.1;A 198.51.100.2;A 198.51.100.3;"
+ "AAAA 2001:db8::1:1;AAAA 2001:db8::1:2" ;
+
+ QTest::newRow("mx-single") << QDnsLookup::MX << "mx-single"
+ << "MX 10 multi";
+ QTest::newRow("mx-single-cname") << QDnsLookup::MX << "mx-single-cname"
+ << "MX 10 cname";
+ QTest::newRow("mx-multi") << QDnsLookup::MX << "mx-multi"
+ << "MX 10 multi;MX 20 a-single";
+ QTest::newRow("mx-multi-sameprio") << QDnsLookup::MX << "mx-multi-sameprio"
+ << "MX 10 a-single;MX 10 multi";
+
+ QTest::newRow("ns-single") << QDnsLookup::NS << "ns-single"
+ << "NS ns11.cloudns.net.";
+ QTest::newRow("ns-multi") << QDnsLookup::NS << "ns-multi"
+ << "NS ns11.cloudns.net.;NS ns12.cloudns.net.";
+
#if 0
// temporarily disabled since the new hosting provider can't insert
// PTR records outside of the in-addr.arpa zone
- QTest::newRow("ptr-single") << int(QDnsLookup::PTR) << "ptr-single" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "a-single" << "" << "";
+ QTest::newRow("ptr-single") << QDnsLookup::PTR << "ptr-single"
+ << "PTR a-single";
#endif
-
- QTest::newRow("srv-notfound") << int(QDnsLookup::SRV) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
- QTest::newRow("srv-single") << int(QDnsLookup::SRV) << "_echo._tcp.srv-single" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "" << "5 0 7 multi" << "";
- QTest::newRow("srv-prio") << int(QDnsLookup::SRV) << "_echo._tcp.srv-prio" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "" << "1 0 7 multi;2 0 7 a-plus-aaaa" << "";
- QTest::newRow("srv-weighted") << int(QDnsLookup::SRV) << "_echo._tcp.srv-weighted" << int(QDnsLookup::NoError) << "" << "" << "" << "" << ""
- << "5 75 7 multi;5 25 7 a-plus-aaaa|"
- "5 25 7 a-plus-aaaa;5 75 7 multi" << "";
- QTest::newRow("srv-multi") << int(QDnsLookup::SRV) << "_echo._tcp.srv-multi" << int(QDnsLookup::NoError) << "" << "" << "" << "" << ""
- << "1 50 7 multi;2 50 7 a-single;2 50 7 aaaa-single;3 50 7 a-multi|"
- "1 50 7 multi;2 50 7 aaaa-single;2 50 7 a-single;3 50 7 a-multi" << "";
-
- QTest::newRow("txt-notfound") << int(QDnsLookup::TXT) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
- QTest::newRow("txt-single") << int(QDnsLookup::TXT) << "txt-single" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "" << "" << "Hello";
- QTest::newRow("txt-multi-onerr") << int(QDnsLookup::TXT) << "txt-multi-onerr" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "" << ""
- << QString::fromLatin1("Hello\0World", sizeof("Hello\0World") - 1);
- QTest::newRow("txt-multi-multirr") << int(QDnsLookup::TXT) << "txt-multi-multirr" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "" << "" << "Hello;World";
+ QTest::newRow("ptr-1.1.1.1") << QDnsLookup::PTR << "1.1.1.1.in-addr.arpa."
+ << "PTR one.one.one.one.";
+ QTest::newRow("ptr-8.8.8.8") << QDnsLookup::PTR << "8.8.8.8.in-addr.arpa."
+ << "PTR dns.google.";
+ QTest::newRow("ptr-2001:4860:4860::8888")
+ << QDnsLookup::PTR << "8.8.8.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.6.8.4.0.6.8.4.1.0.0.2.ip6.arpa."
+ << "PTR dns.google.";
+ QTest::newRow("ptr-2606:4700:4700::1111")
+ << QDnsLookup::PTR << "1.1.1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.7.4.0.0.7.4.6.0.6.2.ip6.arpa."
+ << "PTR one.one.one.one.";
+
+ QTest::newRow("srv-single") << QDnsLookup::SRV << "_echo._tcp.srv-single"
+ << "SRV 5 0 7 multi";
+ QTest::newRow("srv-prio") << QDnsLookup::SRV << "_echo._tcp.srv-prio"
+ << "SRV 1 0 7 multi;SRV 2 0 7 a-plus-aaaa";
+ QTest::newRow("srv-weighted") << QDnsLookup::SRV << "_echo._tcp.srv-weighted"
+ << "SRV 5 25 7 a-plus-aaaa;SRV 5 75 7 multi";
+ QTest::newRow("srv-multi") << QDnsLookup::SRV << "_echo._tcp.srv-multi"
+ << "SRV 1 50 7 multi;"
+ "SRV 2 50 7 a-single;"
+ "SRV 2 50 7 aaaa-single;"
+ "SRV 3 50 7 a-multi";
+
+ QTest::newRow("tlsa") << QDnsLookup::Type::TLSA << "_25._tcp.multi"
+ << "TLSA 3 1 1 0123456789ABCDEFFEDCBA9876543210"
+ "0123456789ABCDEFFEDCBA9876543210";
+
+ QTest::newRow("txt-single") << QDnsLookup::TXT << "txt-single"
+ << "TXT \"Hello\"";
+ QTest::newRow("txt-multi-onerr") << QDnsLookup::TXT << "txt-multi-onerr"
+ << "TXT \"Hello\" \"World\"";
+ QTest::newRow("txt-multi-multirr") << QDnsLookup::TXT << "txt-multi-multirr"
+ << "TXT \"Hello\";TXT \"World\"";
}
void tst_QDnsLookup::lookup()
{
- QFETCH(int, type);
+ QFETCH(QDnsLookup::Type, type);
QFETCH(QString, domain);
- QFETCH(int, error);
- QFETCH(QString, cname);
- QFETCH(QString, host);
- QFETCH(QString, mx);
- QFETCH(QString, ns);
- QFETCH(QString, ptr);
- QFETCH(QString, srv);
- QFETCH(QString, txt);
-
- // transform the inputs
- domain = domainName(domain);
- cname = domainName(cname);
- ns = domainNameList(ns);
- ptr = domainNameList(ptr);
-
- // SRV and MX have reply entries that can change order
- // and we can't sort
- QStringList mx_alternatives = domainNameListAlternatives(mx);
- QStringList srv_alternatives = domainNameListAlternatives(srv);
+ QFETCH(QString, expected);
- QDnsLookup lookup;
- lookup.setType(static_cast<QDnsLookup::Type>(type));
- lookup.setName(domain);
- lookup.lookup();
- QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
-
- auto extraErrorMsg = [&] () {
- QString result;
- QTextStream str(&result);
- str << "Actual error: " << lookup.error();
- if (QString errorString = lookup.errorString(); !errorString.isEmpty())
- str << " (" << errorString << ')';
- str << ", expected: " << error;
- str << ", domain: " << domain;
- if (!cname.isEmpty())
- str << ", cname: " << cname;
- str << ", host: " << host;
- if (!srv.isEmpty())
- str << " server: " << srv;
- if (!mx.isEmpty())
- str << " mx: " << mx;
- if (!ns.isEmpty())
- str << " ns: " << ns;
- if (!ptr.isEmpty())
- str << " ptr: " << ptr;
- return result.toLocal8Bit();
- };
-
- if (!dnsServersMustWork && (lookup.error() == QDnsLookup::ServerFailureError
- || lookup.error() == QDnsLookup::ServerRefusedError
- || lookup.error() == QDnsLookup::TimeoutError)) {
- // It's not a QDnsLookup problem if the server refuses to answer the query.
- // This happens for queries of type ANY through Dnsmasq, for example.
- qWarning("Server refused or was unable to answer query; %s", extraErrorMsg().constData());
+ std::unique_ptr<QDnsLookup> lookup = lookupCommon(type, domain);
+ if (!lookup)
return;
- }
-
- QVERIFY2(int(lookup.error()) == error, extraErrorMsg());
- if (error == QDnsLookup::NoError)
- QVERIFY(lookup.errorString().isEmpty());
- QCOMPARE(int(lookup.type()), type);
- QCOMPARE(lookup.name(), domain);
-
- // canonical names
- if (!cname.isEmpty()) {
- QVERIFY(!lookup.canonicalNameRecords().isEmpty());
- const QDnsDomainNameRecord cnameRecord = lookup.canonicalNameRecords().first();
- QCOMPARE(cnameRecord.name(), domain);
- QCOMPARE(cnameRecord.value(), cname);
- } else {
- QVERIFY(lookup.canonicalNameRecords().isEmpty());
- }
-
- // host addresses
- const QString hostName = cname.isEmpty() ? domain : cname;
- QStringList addresses;
- const auto records = lookup.hostAddressRecords();
- for (const QDnsHostAddressRecord &record : records) {
- //reply may include A & AAAA records for nameservers, ignore them and only look at records matching the query
- if (record.name() == hostName)
- addresses << record.value().toString().toLower();
- }
- addresses.sort();
- QCOMPARE(addresses.join(';'), host);
-
- // mail exchanges
- QStringList mailExchanges;
- const auto mailRecords = lookup.mailExchangeRecords();
- for (const QDnsMailExchangeRecord &record : mailRecords) {
- QCOMPARE(record.name(), domain);
- mailExchanges << QString::number(record.preference()) + QLatin1Char(' ') + record.exchange();
- }
- QVERIFY2(mx_alternatives.contains(mailExchanges.join(';')),
- qPrintable("Actual: " + mailExchanges.join(';') + "\nExpected one of:\n" + mx_alternatives.join('\n')));
-
- // name servers
- QStringList nameServers;
- const auto nameServerRecords = lookup.nameServerRecords();
- for (const QDnsDomainNameRecord &record : nameServerRecords) {
- //reply may include NS records for authoritative nameservers, ignore them and only look at records matching the query
- if (record.name() == domain)
- nameServers << record.value();
- }
- nameServers.sort();
- QCOMPARE(nameServers.join(';'), ns);
-
- // pointers
- if (!ptr.isEmpty()) {
- QVERIFY(!lookup.pointerRecords().isEmpty());
- const QDnsDomainNameRecord ptrRecord = lookup.pointerRecords().first();
- QCOMPARE(ptrRecord.name(), domain);
- QCOMPARE(ptrRecord.value(), ptr);
- } else {
- QVERIFY(lookup.pointerRecords().isEmpty());
- }
- // services
- QStringList services;
- const auto serviceRecords = lookup.serviceRecords();
- for (const QDnsServiceRecord &record : serviceRecords) {
- QCOMPARE(record.name(), domain);
- services << (QString::number(record.priority()) + QLatin1Char(' ')
- + QString::number(record.weight()) + QLatin1Char(' ')
- + QString::number(record.port()) + QLatin1Char(' ') + record.target());
- }
- QVERIFY2(srv_alternatives.contains(services.join(';')),
- qPrintable("Actual: " + services.join(';') + "\nExpected one of:\n" + srv_alternatives.join('\n')));
-
- // text
- QStringList texts;
- const auto textRecords = lookup.textRecords();
- for (const QDnsTextRecord &record : textRecords) {
- QCOMPARE(record.name(), domain);
- QString text;
- const auto values = record.values();
- for (const QByteArray &ba : values) {
- if (!text.isEmpty())
- text += '\0';
- text += QString::fromLatin1(ba);
- }
- texts << text;
- }
- texts.sort();
- QCOMPARE(texts.join(';'), txt);
+#ifdef Q_OS_WIN
+ if (QTest::currentDataTag() == "tlsa"_L1)
+ QSKIP("WinDNS doesn't work properly with TLSA records and we don't know why");
+#endif
+ QCOMPARE(lookup->errorString(), QString());
+ QCOMPARE(lookup->error(), QDnsLookup::NoError);
+ QCOMPARE(lookup->type(), type);
+ QCOMPARE(lookup->name(), domainName(domain));
+
+ QString result = formatReply(lookup.get()).join(u';');
+ QCOMPARE(result, expected);
+
+ // confirm that MX and SRV records are properly sorted
+ const QList<QDnsMailExchangeRecord> mx = lookup->mailExchangeRecords();
+ for (qsizetype i = 1; i < mx.size(); ++i)
+ QCOMPARE_GE(mx[i].preference(), mx[i - 1].preference());
+
+ const QList<QDnsServiceRecord> srv = lookup->serviceRecords();
+ for (qsizetype i = 1; i < srv.size(); ++i)
+ QCOMPARE_GE(srv[i].priority(), srv[i - 1].priority());
}
void tst_QDnsLookup::lookupIdn()
@@ -556,9 +659,10 @@ void tst_QDnsLookup::setNameserverLoopback()
// send an NXDOMAIN reply to release the lookup thread
QByteArray reply = data;
- reply[2] = 0x80; // header->qr = true;
+ reply[2] = 0x80U; // header->qr = true;
reply[3] = 3; // header->rcode = NXDOMAIN;
- server.writeDatagram(dgram.makeReply(reply));
+ server.writeDatagram(reply.constData(), reply.size(), dgram.senderAddress(),
+ dgram.senderPort());
server.close();
// now check that the QDnsLookup finished
@@ -568,34 +672,57 @@ void tst_QDnsLookup::setNameserverLoopback()
QCOMPARE(lookup.error(), QDnsLookup::NotFoundError);
}
-void tst_QDnsLookup::setNameserver_data()
+template <QDnsLookup::Protocol Protocol>
+static void setNameserver_data_helper(const QByteArray &protoName)
{
- static QList<QHostAddress> servers = systemNameservers() + globalPublicNameservers();
+ if (!QDnsLookup::isProtocolSupported(Protocol))
+ QSKIP(protoName + " not supported");
+
+ static QList<QHostAddress> servers = systemNameservers(Protocol)
+ + globalPublicNameservers(Protocol);
QTest::addColumn<QHostAddress>("server");
if (servers.isEmpty()) {
- QSKIP("No reachable DNS servers were found");
+ QSKIP("No reachable " + protoName + " servers were found");
} else {
for (const QHostAddress &h : std::as_const(servers))
QTest::addRow("%s", qUtf8Printable(h.toString())) << h;
}
}
-void tst_QDnsLookup::setNameserver()
+void tst_QDnsLookup::setNameserver_data()
+{
+ setNameserver_data_helper<QDnsLookup::Standard>("DNS");
+}
+
+void tst_QDnsLookup::setNameserver_helper(QDnsLookup::Protocol protocol)
{
QFETCH(QHostAddress, server);
- QDnsLookup lookup;
- lookup.setNameserver(server);
+ QElapsedTimer timer;
+ timer.start();
+ std::unique_ptr<QDnsLookup> lookup =
+ lookupCommon(QDnsLookup::Type::A, "a-single", server, 0, protocol);
+ if (!lookup)
+ return;
+ qDebug() << "Lookup took" << timer.elapsed() << "ms";
+ QCOMPARE(lookup->error(), QDnsLookup::NoError);
+ QString result = formatReply(lookup.get()).join(';');
+ QCOMPARE(result, "A 192.0.2.1");
+}
- lookup.setType(QDnsLookup::Type::A);
- lookup.setName(domainName("a-single"));
- lookup.lookup();
+void tst_QDnsLookup::setNameserver()
+{
+ setNameserver_helper(QDnsLookup::Standard);
+}
- QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
- QCOMPARE(int(lookup.error()), int(QDnsLookup::NoError));
- QVERIFY(!lookup.hostAddressRecords().isEmpty());
- QCOMPARE(lookup.hostAddressRecords().first().name(), domainName("a-single"));
- QCOMPARE(lookup.hostAddressRecords().first().value(), QHostAddress("192.0.2.1"));
+void tst_QDnsLookup::dnsOverTls_data()
+{
+ setNameserver_data_helper<QDnsLookup::DnsOverTls>("DNS-over-TLS");
+}
+
+void tst_QDnsLookup::dnsOverTls()
+{
+ setNameserver_helper(QDnsLookup::DnsOverTls);
}
void tst_QDnsLookup::bindingsAndProperties()