QDnsLookup: add support for setting the port number of the server
I couldn't make my Windows 10 or 11 query a non-standard port. It kept complaining about "The parameter is incorrect.", so as a result the unit test doesn't actually test the new feature there. I can't find a single example of this on the Internet; my speculation is that the backend API that DnsQueryEx uses does not support setting port numbers (DnsQuery_{A,W} didn't offer that option). [ChangeLog][QtNetwork][QDnsLookup] Added setNameserverPort(). Change-Id: I3e3bfef633af4130a03afffd175d60a581cc0a9c Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
029e0bf552
commit
bce7009f55
@ -244,16 +244,39 @@ QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
|
||||
/*!
|
||||
\fn QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
|
||||
\since 5.4
|
||||
Constructs a QDnsLookup object for the given \a type, \a name and
|
||||
\a nameserver and sets \a parent as the parent object.
|
||||
|
||||
Constructs a QDnsLookup object to issue a query for \a name of record type
|
||||
\a type, using the DNS server \a nameserver running on the default DNS port,
|
||||
and sets \a parent as the parent object.
|
||||
*/
|
||||
|
||||
QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
|
||||
: QDnsLookup(type, name, nameserver, DnsPort, parent)
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
\fn QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port, QObject *parent)
|
||||
\since 6.6
|
||||
|
||||
Constructs a QDnsLookup object to issue a query for \a name of record type
|
||||
\a type, using the DNS server \a nameserver running on port \a port, and
|
||||
sets \a parent as the parent object.
|
||||
|
||||
//! [nameserver-port]
|
||||
\note Setting the port number to any value other than the default (53) can
|
||||
cause the name resolution to fail, depending on the operating system
|
||||
limitations and firewalls. Notably, the Windows API used by QDnsLookup is
|
||||
unable to handle alternate port numbers.
|
||||
//! [nameserver-port]
|
||||
*/
|
||||
QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port, QObject *parent)
|
||||
: QObject(*new QDnsLookupPrivate, parent)
|
||||
{
|
||||
Q_D(QDnsLookup);
|
||||
d->name = name;
|
||||
d->type = type;
|
||||
d->port = port;
|
||||
d->nameserver = nameserver;
|
||||
}
|
||||
|
||||
@ -366,6 +389,46 @@ QBindable<QHostAddress> QDnsLookup::bindableNameserver()
|
||||
return &d->nameserver;
|
||||
}
|
||||
|
||||
/*!
|
||||
\property QDnsLookup::nameserverPort
|
||||
\since 6.6
|
||||
\brief the port number of nameserver to use for DNS lookup.
|
||||
\include qdnslookup.cpp nameserver-port
|
||||
*/
|
||||
|
||||
quint16 QDnsLookup::nameserverPort() const
|
||||
{
|
||||
return d_func()->port;
|
||||
}
|
||||
|
||||
void QDnsLookup::setNameserverPort(quint16 nameserverPort)
|
||||
{
|
||||
Q_D(QDnsLookup);
|
||||
d->port = nameserverPort;
|
||||
}
|
||||
|
||||
QBindable<quint16> QDnsLookup::bindableNameserverPort()
|
||||
{
|
||||
Q_D(QDnsLookup);
|
||||
return &d->port;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 6.6
|
||||
Sets the nameserver to \a nameserver and the port to \a port.
|
||||
|
||||
\include qdnslookup.cpp nameserver-port
|
||||
|
||||
\sa QDnsLookup::nameserver, QDnsLookup::nameserverPort
|
||||
*/
|
||||
void QDnsLookup::setNameserver(const QHostAddress &nameserver, quint16 port)
|
||||
{
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
setNameserver(nameserver);
|
||||
setNameserverPort(port);
|
||||
Qt::endPropertyUpdateGroup();
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the list of canonical name records associated with this lookup.
|
||||
*/
|
||||
@ -965,7 +1028,8 @@ void QDnsLookupPrivate::_q_lookupFinished(const QDnsLookupReply &_reply)
|
||||
inline QDnsLookupRunnable::QDnsLookupRunnable(const QDnsLookupPrivate *d)
|
||||
: requestName(QUrl::toAce(d->name)),
|
||||
nameserver(d->nameserver),
|
||||
requestType(d->type)
|
||||
requestType(d->type),
|
||||
port(d->port)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -146,6 +146,8 @@ class Q_NETWORK_EXPORT QDnsLookup : public QObject
|
||||
Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged BINDABLE bindableType)
|
||||
Q_PROPERTY(QHostAddress nameserver READ nameserver WRITE setNameserver NOTIFY nameserverChanged
|
||||
BINDABLE bindableNameserver)
|
||||
Q_PROPERTY(quint16 nameserverPort READ nameserverPort WRITE setNameserverPort
|
||||
NOTIFY nameserverPortChanged BINDABLE bindableNameserverPort)
|
||||
|
||||
public:
|
||||
enum Error
|
||||
@ -178,6 +180,8 @@ public:
|
||||
explicit QDnsLookup(QObject *parent = nullptr);
|
||||
QDnsLookup(Type type, const QString &name, QObject *parent = nullptr);
|
||||
QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent = nullptr);
|
||||
QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port,
|
||||
QObject *parent = nullptr);
|
||||
~QDnsLookup();
|
||||
|
||||
Error error() const;
|
||||
@ -195,6 +199,10 @@ public:
|
||||
QHostAddress nameserver() const;
|
||||
void setNameserver(const QHostAddress &nameserver);
|
||||
QBindable<QHostAddress> bindableNameserver();
|
||||
quint16 nameserverPort() const;
|
||||
void setNameserverPort(quint16 port);
|
||||
QBindable<quint16> bindableNameserverPort();
|
||||
void setNameserver(const QHostAddress &nameserver, quint16 port);
|
||||
|
||||
QList<QDnsDomainNameRecord> canonicalNameRecords() const;
|
||||
QList<QDnsHostAddressRecord> hostAddressRecords() const;
|
||||
@ -214,6 +222,7 @@ Q_SIGNALS:
|
||||
void nameChanged(const QString &name);
|
||||
void typeChanged(Type type);
|
||||
void nameserverChanged(const QHostAddress &nameserver);
|
||||
void nameserverPortChanged(quint16 port);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(QDnsLookup)
|
||||
|
@ -120,15 +120,12 @@ class QDnsLookupPrivate : public QObjectPrivate
|
||||
{
|
||||
public:
|
||||
QDnsLookupPrivate()
|
||||
: isFinished(false)
|
||||
, type(QDnsLookup::A)
|
||||
, runnable(nullptr)
|
||||
: type(QDnsLookup::A)
|
||||
, port(DnsPort)
|
||||
{ }
|
||||
void _q_lookupFinished(const QDnsLookupReply &reply);
|
||||
|
||||
|
||||
bool isFinished;
|
||||
|
||||
void nameChanged()
|
||||
{
|
||||
emit q_func()->nameChanged(name);
|
||||
@ -136,6 +133,13 @@ public:
|
||||
Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QString, name,
|
||||
&QDnsLookupPrivate::nameChanged);
|
||||
|
||||
void nameserverChanged()
|
||||
{
|
||||
emit q_func()->nameserverChanged(nameserver);
|
||||
}
|
||||
Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QHostAddress, nameserver,
|
||||
&QDnsLookupPrivate::nameserverChanged);
|
||||
|
||||
void typeChanged()
|
||||
{
|
||||
emit q_func()->typeChanged(type);
|
||||
@ -144,15 +148,18 @@ public:
|
||||
Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QDnsLookup::Type,
|
||||
type, &QDnsLookupPrivate::typeChanged);
|
||||
|
||||
void nameserverChanged()
|
||||
void nameserverPortChanged()
|
||||
{
|
||||
emit q_func()->nameserverChanged(nameserver);
|
||||
emit q_func()->nameserverPortChanged(port);
|
||||
}
|
||||
Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QHostAddress, nameserver,
|
||||
&QDnsLookupPrivate::nameserverChanged);
|
||||
|
||||
Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, quint16,
|
||||
port, &QDnsLookupPrivate::nameserverPortChanged);
|
||||
|
||||
|
||||
QDnsLookupReply reply;
|
||||
QDnsLookupRunnable *runnable;
|
||||
QDnsLookupRunnable *runnable = nullptr;
|
||||
bool isFinished = false;
|
||||
|
||||
Q_DECLARE_PUBLIC(QDnsLookup)
|
||||
};
|
||||
@ -173,6 +180,7 @@ private:
|
||||
QByteArray requestName;
|
||||
QHostAddress nameserver;
|
||||
QDnsLookup::Type requestType;
|
||||
quint16 port;
|
||||
};
|
||||
|
||||
class QDnsLookupThreadPool : public QThreadPool
|
||||
|
@ -109,7 +109,7 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
|
||||
auto guard = qScopeGuard([&] { res_nclose(&state); });
|
||||
|
||||
//Check if a nameserver was set. If so, use it
|
||||
if (!applyNameServer(&state, nameserver, DnsPort)) {
|
||||
if (!applyNameServer(&state, nameserver, port)) {
|
||||
qWarning("QDnsLookup: %s", "IPv6 nameservers are currently not supported on this OS");
|
||||
return reply->setError(QDnsLookup::ResolverError,
|
||||
QDnsLookup::tr("IPv6 nameservers are currently not supported on this OS"));
|
||||
|
@ -82,7 +82,7 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
|
||||
request.pDnsServerList->MaxCount = sizeof(dnsAddresses);
|
||||
request.pDnsServerList->AddrCount = 1;
|
||||
// ### setting port 53 seems to cause some systems to fail
|
||||
setSockaddr(sa, nameserver, 0);
|
||||
setSockaddr(sa, nameserver, port == DnsPort ? 0 : port);
|
||||
request.pDnsServerList->Family = sa->sa_family;
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,11 @@
|
||||
#include <QSignalSpy>
|
||||
|
||||
#include <QtNetwork/QDnsLookup>
|
||||
|
||||
#include <QtCore/QRandomGenerator>
|
||||
#include <QtNetwork/QHostAddress>
|
||||
#include <QtNetwork/QNetworkDatagram>
|
||||
#include <QtNetwork/QUdpSocket>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
static const int Timeout = 15000; // 15s
|
||||
@ -35,6 +39,7 @@ private slots:
|
||||
|
||||
void lookupReuse();
|
||||
void lookupAbortRetry();
|
||||
void setNameserverLoopback();
|
||||
void bindingsAndProperties();
|
||||
};
|
||||
|
||||
@ -351,6 +356,62 @@ void tst_QDnsLookup::lookupAbortRetry()
|
||||
QCOMPARE(lookup.hostAddressRecords().first().value(), QHostAddress("2001:db8::1"));
|
||||
}
|
||||
|
||||
void tst_QDnsLookup::setNameserverLoopback()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
// Windows doesn't like sending DNS requests to ports other than 53, so
|
||||
// let's try it first.
|
||||
constexpr quint16 DesiredPort = 53;
|
||||
#else
|
||||
// Trying to bind to port 53 will fail on Unix systems unless this test is
|
||||
// run as root, so we try mDNS's port (to help decoding in a packet capture).
|
||||
constexpr quint16 DesiredPort = 5353; // mDNS
|
||||
#endif
|
||||
// random loopback address so multiple copies of this test can run
|
||||
QHostAddress desiredAddress(0x7f000000 | QRandomGenerator::system()->bounded(0xffffff));
|
||||
|
||||
QUdpSocket server;
|
||||
if (!server.bind(desiredAddress, DesiredPort)) {
|
||||
// port in use, try a random one
|
||||
server.bind(QHostAddress::LocalHost, 0);
|
||||
}
|
||||
QCOMPARE(server.state(), QUdpSocket::BoundState);
|
||||
|
||||
QDnsLookup lookup(QDnsLookup::Type::A, u"somelabel.somedomain"_s);
|
||||
QSignalSpy spy(&lookup, SIGNAL(finished()));
|
||||
lookup.setNameserver(server.localAddress(), server.localPort());
|
||||
|
||||
// QDnsLookup is threaded, so we can answer on the main thread
|
||||
QObject::connect(&server, &QUdpSocket::readyRead,
|
||||
&QTestEventLoop::instance(), &QTestEventLoop::exitLoop);
|
||||
QObject::connect(&lookup, &QDnsLookup::finished,
|
||||
&QTestEventLoop::instance(), &QTestEventLoop::exitLoop);
|
||||
lookup.lookup();
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QVERIFY2(spy.isEmpty(), qPrintable(lookup.errorString()));
|
||||
|
||||
QNetworkDatagram dgram = server.receiveDatagram();
|
||||
QByteArray data = dgram.data();
|
||||
QCOMPARE_GT(data.size(), HeaderSize);
|
||||
|
||||
quint8 opcode = (quint8(data.at(3)) >> 4) & 0xF;
|
||||
QCOMPARE(opcode, 0); // standard query
|
||||
|
||||
// send an NXDOMAIN reply to release the lookup thread
|
||||
QByteArray reply = data;
|
||||
reply[2] = 0x80; // header->qr = true;
|
||||
reply[3] = 3; // header->rcode = NXDOMAIN;
|
||||
server.writeDatagram(dgram.makeReply(reply));
|
||||
server.close();
|
||||
|
||||
// now check that the QDnsLookup finished
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QCOMPARE(spy.size(), 1);
|
||||
QCOMPARE(lookup.error(), QDnsLookup::NotFoundError);
|
||||
}
|
||||
|
||||
void tst_QDnsLookup::bindingsAndProperties()
|
||||
{
|
||||
QDnsLookup lookup;
|
||||
@ -383,14 +444,23 @@ void tst_QDnsLookup::bindingsAndProperties()
|
||||
QProperty<QHostAddress> nameserverProp;
|
||||
lookup.bindableNameserver().setBinding(Qt::makePropertyBinding(nameserverProp));
|
||||
const QSignalSpy nameserverChangeSpy(&lookup, &QDnsLookup::nameserverChanged);
|
||||
const QSignalSpy nameserverPortChangeSpy(&lookup, &QDnsLookup::nameserverPortChanged);
|
||||
|
||||
nameserverProp = QHostAddress::LocalHost;
|
||||
QCOMPARE(nameserverChangeSpy.size(), 1);
|
||||
QCOMPARE(nameserverPortChangeSpy.size(), 0);
|
||||
QCOMPARE(lookup.nameserver(), QHostAddress::LocalHost);
|
||||
|
||||
nameserverProp.setBinding(lookup.bindableNameserver().makeBinding());
|
||||
lookup.setNameserver(QHostAddress::Any);
|
||||
QCOMPARE(nameserverProp.value(), QHostAddress::Any);
|
||||
QCOMPARE(nameserverChangeSpy.size(), 2);
|
||||
QCOMPARE(nameserverPortChangeSpy.size(), 0);
|
||||
|
||||
lookup.setNameserver(QHostAddress::LocalHostIPv6, 10053);
|
||||
QCOMPARE(nameserverProp.value(), QHostAddress::LocalHostIPv6);
|
||||
QCOMPARE(nameserverChangeSpy.size(), 3);
|
||||
QCOMPARE(nameserverPortChangeSpy.size(), 1);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QDnsLookup)
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <QtCore/QElapsedTimer>
|
||||
#include <QtCore/QMetaEnum>
|
||||
#include <QtCore/QTimer>
|
||||
#include <QtCore/QUrl>
|
||||
#include <QtNetwork/QHostAddress>
|
||||
#include <QtNetwork/QHostInfo>
|
||||
#include <QtNetwork/QDnsLookup>
|
||||
@ -38,6 +39,24 @@ static int showHelp(const char *argv0, int exitcode)
|
||||
return exitcode;
|
||||
}
|
||||
|
||||
static auto parseServerAddress(QString server)
|
||||
{
|
||||
struct R {
|
||||
QHostAddress address;
|
||||
int port = -1;
|
||||
} r;
|
||||
|
||||
// let's use QUrl to help us
|
||||
QUrl url;
|
||||
url.setAuthority(server);
|
||||
if (!url.isValid() || !url.userInfo().isNull())
|
||||
return r; // failed
|
||||
|
||||
r.port = url.port();
|
||||
r.address.setAddress(url.host());
|
||||
return r;
|
||||
}
|
||||
|
||||
static void printAnswers(const QDnsLookup &lookup)
|
||||
{
|
||||
printf("\n;; ANSWER:\n");
|
||||
@ -96,7 +115,7 @@ static void printResults(const QDnsLookup &lookup, QElapsedTimer::Duration durat
|
||||
|
||||
printf("\n;; Query time: %lld ms\n", qint64(duration_cast<milliseconds>(duration).count()));
|
||||
if (QHostAddress server = lookup.nameserver(); !server.isNull())
|
||||
printf(";; SERVER: %s#53\n", qPrintable(server.toString()));
|
||||
printf(";; SERVER: %s#%d\n", qPrintable(server.toString()), lookup.nameserverPort());
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@ -138,15 +157,15 @@ int main(int argc, char *argv[])
|
||||
|
||||
QDnsLookup lookup(type, domain);
|
||||
if (!server.isEmpty()) {
|
||||
QHostAddress addr(server);
|
||||
if (addr.isNull())
|
||||
addr = QHostInfo::fromName(server).addresses().value(0);
|
||||
if (addr.isNull()) {
|
||||
auto addr = parseServerAddress(server);
|
||||
if (addr.address.isNull()) {
|
||||
fprintf(stderr, "%s: could not parse name server address '%s'\n",
|
||||
argv[0], qPrintable(server));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
lookup.setNameserver(addr);
|
||||
lookup.setNameserver(addr.address);
|
||||
if (addr.port > 0)
|
||||
lookup.setNameserverPort(addr.port);
|
||||
}
|
||||
|
||||
// execute the lookup
|
||||
|
Loading…
Reference in New Issue
Block a user