qt5base-lts/tests/manual/qdnslookup/main.cpp
Thiago Macieira bce7009f55 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>
2023-05-23 21:23:42 -07:00

185 lines
5.9 KiB
C++

// Copyright (C) 2023 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <QtCore/QElapsedTimer>
#include <QtCore/QMetaEnum>
#include <QtCore/QTimer>
#include <QtCore/QUrl>
#include <QtNetwork/QHostAddress>
#include <QtNetwork/QHostInfo>
#include <QtNetwork/QDnsLookup>
#include <stdlib.h>
#include <stdio.h>
#include <chrono>
using namespace Qt::StringLiterals;
using namespace std::chrono;
using namespace std::chrono_literals;
static QDnsLookup::Type typeFromString(QString str)
{
// we can use the meta object
QMetaEnum me = QMetaEnum::fromType<QDnsLookup::Type>();
bool ok;
int value = me.keyToValue(str.toUpper().toLatin1(), &ok);
if (!ok)
return QDnsLookup::Type(0);
return QDnsLookup::Type(value);
}
static int showHelp(const char *argv0, int exitcode)
{
// like dig
printf("%s [@global-server] [domain] [query-type]\n", argv0);
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");
static auto printRecordCommon = [](const auto &rr, const char *rrtype) {
printf("%-23s %6d IN %s\t", qPrintable(rr.name()), rr.timeToLive(), rrtype);
};
auto printNameRecords = [](const char *rrtype, const QList<QDnsDomainNameRecord> list) {
for (const QDnsDomainNameRecord &rr : list) {
printRecordCommon(rr, rrtype);
printf("%s\n", qPrintable(rr.value()));
}
};
for (const QDnsMailExchangeRecord &rr : lookup.mailExchangeRecords()) {
printRecordCommon(rr, "MX");
printf("%d %s\n", rr.preference(), qPrintable(rr.exchange()));
}
for (const QDnsServiceRecord &rr : lookup.serviceRecords()) {
printRecordCommon(rr, "SRV");
printf("%d %d %d %s\n", rr.priority(), rr.weight(), rr.port(),
qPrintable(rr.target()));
}
printNameRecords("NS", lookup.nameServerRecords());
printNameRecords("PTR", lookup.pointerRecords());
printNameRecords("CNAME", lookup.canonicalNameRecords());
for (const QDnsHostAddressRecord &rr : lookup.hostAddressRecords()) {
QHostAddress addr = rr.value();
printRecordCommon(rr, addr.protocol() == QHostAddress::IPv6Protocol ? "AAAA" : "A");
printf("%s\n", qPrintable(addr.toString()));
}
for (const QDnsTextRecord &rr : lookup.textRecords()) {
printRecordCommon(rr, "TXT");
for (const QByteArray &data : rr.values())
printf("%s ", qPrintable(QDebug::toString(data)));
puts("");
}
}
static void printResults(const QDnsLookup &lookup, QElapsedTimer::Duration duration)
{
if (QDnsLookup::Error error = lookup.error())
printf(";; status: %s (%s)\n", QMetaEnum::fromType<QDnsLookup::Error>().valueToKey(error),
qPrintable(lookup.errorString()));
else
printf(";; status: NoError\n");
QMetaEnum me = QMetaEnum::fromType<QDnsLookup::Type>();
printf(";; QUESTION:\n");
printf(";%-30s IN %s\n", qPrintable(lookup.name()),
me.valueToKey(lookup.type()));
if (lookup.error() == QDnsLookup::NoError)
printAnswers(lookup);
printf("\n;; Query time: %lld ms\n", qint64(duration_cast<milliseconds>(duration).count()));
if (QHostAddress server = lookup.nameserver(); !server.isNull())
printf(";; SERVER: %s#%d\n", qPrintable(server.toString()), lookup.nameserverPort());
}
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QDnsLookup::Type type = {};
QString domain, server;
const QStringList args = QCoreApplication::arguments().sliced(1);
for (const QString &arg : args) {
if (arg.startsWith(u'@')) {
server = arg.mid(1);
continue;
}
if (arg == u"-h")
return showHelp(argv[0], EXIT_SUCCESS);
if (domain.isNull()) {
domain = arg;
continue;
}
if (type != QDnsLookup::Type{})
return showHelp(argv[0], EXIT_FAILURE);
type = typeFromString(arg);
if (type == QDnsLookup::Type{}) {
fprintf(stderr, "%s: unknown DNS record type '%s'. Valid types are:\n",
argv[0], qPrintable(arg));
QMetaEnum me = QMetaEnum::fromType<QDnsLookup::Type>();
for (int i = 0; i < me.keyCount(); ++i)
fprintf(stderr, " %s\n", me.key(i));
return EXIT_FAILURE;
}
}
if (domain.isEmpty())
domain = u"qt-project.org"_s;
if (type == QDnsLookup::Type{})
type = QDnsLookup::A;
QDnsLookup lookup(type, domain);
if (!server.isEmpty()) {
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.address);
if (addr.port > 0)
lookup.setNameserverPort(addr.port);
}
// execute the lookup
QObject::connect(&lookup, &QDnsLookup::finished, qApp, &QCoreApplication::quit);
QTimer::singleShot(15s, []() { qApp->exit(EXIT_FAILURE); });
QElapsedTimer timer;
timer.start();
lookup.lookup();
if (a.exec() == EXIT_FAILURE)
return EXIT_FAILURE;
printf("; <<>> QDnsLookup " QT_VERSION_STR " <<>> %s %s\n",
qPrintable(QCoreApplication::applicationName()), qPrintable(args.join(u' ')));
printResults(lookup, timer.durationElapsed());
return 0;
}