// Copyright (C) 2016 Jeremy Lainé // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "dnslookup.h" #include #include #include #include #include #include #include #include using namespace Qt::StringLiterals; static std::optional typeFromParameter(QStringView type) { if (type.compare(u"a", Qt::CaseInsensitive) == 0) return QDnsLookup::A; if (type.compare(u"aaaa", Qt::CaseInsensitive) == 0) return QDnsLookup::AAAA; if (type.compare(u"any", Qt::CaseInsensitive) == 0) return QDnsLookup::ANY; if (type.compare(u"cname", Qt::CaseInsensitive) == 0) return QDnsLookup::CNAME; if (type.compare(u"mx", Qt::CaseInsensitive) == 0) return QDnsLookup::MX; if (type.compare(u"ns", Qt::CaseInsensitive) == 0) return QDnsLookup::NS; if (type.compare(u"ptr", Qt::CaseInsensitive) == 0) return QDnsLookup::PTR; if (type.compare(u"srv", Qt::CaseInsensitive) == 0) return QDnsLookup::SRV; if (type.compare(u"txt", Qt::CaseInsensitive) == 0) return QDnsLookup::TXT; return std::nullopt; } //! [0] struct CommandLineParseResult { enum class Status { Ok, Error, VersionRequested, HelpRequested }; Status statusCode = Status::Ok; std::optional errorString = std::nullopt; }; CommandLineParseResult parseCommandLine(QCommandLineParser &parser, DnsQuery *query) { using Status = CommandLineParseResult::Status; parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); const QCommandLineOption nameServerOption("n", "The name server to use.", "nameserver"); parser.addOption(nameServerOption); const QCommandLineOption typeOption("t", "The lookup type.", "type"); parser.addOption(typeOption); parser.addPositionalArgument("name", "The name to look up."); const QCommandLineOption helpOption = parser.addHelpOption(); const QCommandLineOption versionOption = parser.addVersionOption(); if (!parser.parse(QCoreApplication::arguments())) return { Status::Error, parser.errorText() }; if (parser.isSet(versionOption)) return { Status::VersionRequested }; if (parser.isSet(helpOption)) return { Status::HelpRequested }; if (parser.isSet(nameServerOption)) { const QString nameserver = parser.value(nameServerOption); query->nameServer = QHostAddress(nameserver); if (query->nameServer.isNull() || query->nameServer.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol) { return { Status::Error, u"Bad nameserver address: %1"_s.arg(nameserver) }; } } if (parser.isSet(typeOption)) { const QString typeParameter = parser.value(typeOption); if (std::optional type = typeFromParameter(typeParameter)) query->type = *type; else return { Status::Error, u"Bad record type: %1"_s.arg(typeParameter) }; } const QStringList positionalArguments = parser.positionalArguments(); if (positionalArguments.isEmpty()) return { Status::Error, u"Argument 'name' missing."_s }; if (positionalArguments.size() > 1) return { Status::Error, u"Several 'name' arguments specified."_s }; query->name = positionalArguments.first(); return { Status::Ok }; } //! [0] DnsManager::DnsManager() : dns(new QDnsLookup(this)) { connect(dns, &QDnsLookup::finished, this, &DnsManager::showResults); } void DnsManager::execute() { // lookup type dns->setType(query.type); if (!query.nameServer.isNull()) dns->setNameserver(query.nameServer); dns->setName(query.name); dns->lookup(); } void DnsManager::showResults() { if (dns->error() != QDnsLookup::NoError) std::printf("Error: %i (%s)\n", dns->error(), qPrintable(dns->errorString())); // CNAME records const QList cnameRecords = dns->canonicalNameRecords(); for (const QDnsDomainNameRecord &record : cnameRecords) { std::printf("%s\t%i\tIN\tCNAME\t%s\n", qPrintable(record.name()), record.timeToLive(), qPrintable(record.value())); } // A and AAAA records const QList aRecords = dns->hostAddressRecords(); for (const QDnsHostAddressRecord &record : aRecords) { const char *type = (record.value().protocol() == QAbstractSocket::IPv6Protocol) ? "AAAA" : "A"; std::printf("%s\t%i\tIN\t%s\t%s\n", qPrintable(record.name()), record.timeToLive(), type, qPrintable(record.value().toString())); } // MX records const QList mxRecords = dns->mailExchangeRecords(); for (const QDnsMailExchangeRecord &record : mxRecords) { std::printf("%s\t%i\tIN\tMX\t%u %s\n", qPrintable(record.name()), record.timeToLive(), record.preference(), qPrintable(record.exchange())); } // NS records const QList nsRecords = dns->nameServerRecords(); for (const QDnsDomainNameRecord &record : nsRecords) { std::printf("%s\t%i\tIN\tNS\t%s\n", qPrintable(record.name()), record.timeToLive(), qPrintable(record.value())); } // PTR records const QList ptrRecords = dns->pointerRecords(); for (const QDnsDomainNameRecord &record : ptrRecords) { std::printf("%s\t%i\tIN\tPTR\t%s\n", qPrintable(record.name()), record.timeToLive(), qPrintable(record.value())); } // SRV records const QList srvRecords = dns->serviceRecords(); for (const QDnsServiceRecord &record : srvRecords) { std::printf("%s\t%i\tIN\tSRV\t%u %u %u %s\n", qPrintable(record.name()), record.timeToLive(), record.priority(), record.weight(), record.port(), qPrintable(record.target())); } // TXT records const QList txtRecords = dns->textRecords(); for (const QDnsTextRecord &record : txtRecords) { QStringList values; const QList dnsRecords = record.values(); for (const QByteArray &ba : dnsRecords) values << "\"" + QString::fromLatin1(ba) + "\""; std::printf("%s\t%i\tIN\tTXT\t%s\n", qPrintable(record.name()), record.timeToLive(), qPrintable(values.join(' '))); } QCoreApplication::instance()->quit(); } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); //! [1] QCoreApplication::setApplicationVersion(QT_VERSION_STR); QCoreApplication::setApplicationName(QCoreApplication::translate("QDnsLookupExample", "DNS Lookup Example")); QCommandLineParser parser; parser.setApplicationDescription(QCoreApplication::translate("QDnsLookupExample", "An example demonstrating the " "class QDnsLookup.")); DnsQuery query; using Status = CommandLineParseResult::Status; CommandLineParseResult parseResult = parseCommandLine(parser, &query); switch (parseResult.statusCode) { case Status::Ok: break; case Status::Error: std::fputs(qPrintable(parseResult.errorString.value_or(u"Unknown error occurred"_s)), stderr); std::fputs("\n\n", stderr); std::fputs(qPrintable(parser.helpText()), stderr); return 1; case Status::VersionRequested: parser.showVersion(); Q_UNREACHABLE_RETURN(0); case Status::HelpRequested: parser.showHelp(); Q_UNREACHABLE_RETURN(0); } //! [1] DnsManager manager; manager.setQuery(query); QTimer::singleShot(0, &manager, &DnsManager::execute); return app.exec(); }