Skip to content

Commit

Permalink
QDnsLookup: add a number of functions to simply setting error conditions
Browse files Browse the repository at this point in the history
This also updates a few messages and transfers the translation context
from QDnsLookupRunnable to QDnsLookup.

Change-Id: I3e3bfef633af4130a03afffd175e86715e4a25e3
Reviewed-by: Mårten Nordheim <[email protected]>
  • Loading branch information
thiagomacieira committed May 24, 2023
1 parent c5c7712 commit 029e0bf
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 142 deletions.
5 changes: 1 addition & 4 deletions src/network/kernel/qdnslookup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,6 @@ static void qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> &records)
}
}

const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
QT_TRANSLATE_NOOP("QDnsLookupRunnable", "IPv6 addresses for nameservers are currently not supported");

/*!
\class QDnsLookup
\brief The QDnsLookup class represents a DNS lookup.
Expand Down Expand Up @@ -979,7 +976,7 @@ void QDnsLookupRunnable::run()
// Validate input.
if (qsizetype n = requestName.size(); n > MaxDomainNameLength || n == 0) {
reply.error = QDnsLookup::InvalidRequestError;
reply.errorString = tr("Invalid domain name");
reply.errorString = QDnsLookup::tr("Invalid domain name");
emit finished(reply);
if (n)
qWarning("QDnsLookup: domain name being looked up is too long (%lld bytes)", n);
Expand Down
73 changes: 66 additions & 7 deletions src/network/kernel/qdnslookup_p.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (C) 2012 Jeremy Lainé <[email protected]>
// Copyright (C) 2023 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only

#ifndef QDNSLOOKUP_P_H
Expand Down Expand Up @@ -39,11 +40,7 @@ class QDnsLookupRunnable;
class QDnsLookupReply
{
public:
QDnsLookupReply()
: error(QDnsLookup::NoError)
{ }

QDnsLookup::Error error;
QDnsLookup::Error error = QDnsLookup::NoError;
QString errorString;

QList<QDnsDomainNameRecord> canonicalNameRecords;
Expand All @@ -53,6 +50,70 @@ class QDnsLookupReply
QList<QDnsDomainNameRecord> pointerRecords;
QList<QDnsServiceRecord> serviceRecords;
QList<QDnsTextRecord> textRecords;

// helper methods
void setError(QDnsLookup::Error err, QString &&msg)
{
error = err;
errorString = std::move(msg);
}

void makeResolverSystemError(int code = -1)
{
Q_ASSERT(allAreEmpty());
setError(QDnsLookup::ResolverError, qt_error_string(code));
}

void makeDnsRcodeError(quint8 rcode)
{
Q_ASSERT(allAreEmpty());
switch (rcode) {
case 1: // FORMERR
error = QDnsLookup::InvalidRequestError;
errorString = QDnsLookup::tr("Server could not process query");
return;
case 2: // SERVFAIL
case 4: // NOTIMP
error = QDnsLookup::ServerFailureError;
errorString = QDnsLookup::tr("Server failure");
return;
case 3: // NXDOMAIN
error = QDnsLookup::NotFoundError;
errorString = QDnsLookup::tr("Non existent domain");
return;
case 5: // REFUSED
error = QDnsLookup::ServerRefusedError;
errorString = QDnsLookup::tr("Server refused to answer");
return;
default:
error = QDnsLookup::InvalidReplyError;
errorString = QDnsLookup::tr("Invalid reply received (rcode %1)")
.arg(rcode);
return;
}
}

void makeInvalidReplyError(QString &&msg = QString())
{
if (msg.isEmpty())
msg = QDnsLookup::tr("Invalid reply received");
else
msg = QDnsLookup::tr("Invalid reply received (%1)").arg(std::move(msg));
*this = QDnsLookupReply(); // empty our lists
setError(QDnsLookup::InvalidReplyError, std::move(msg));
}

private:
bool allAreEmpty() const
{
return canonicalNameRecords.isEmpty()
&& hostAddressRecords.isEmpty()
&& mailExchangeRecords.isEmpty()
&& nameServerRecords.isEmpty()
&& pointerRecords.isEmpty()
&& serviceRecords.isEmpty()
&& textRecords.isEmpty();
}
};

class QDnsLookupPrivate : public QObjectPrivate
Expand All @@ -63,10 +124,8 @@ class QDnsLookupPrivate : public QObjectPrivate
, type(QDnsLookup::A)
, runnable(nullptr)
{ }

void _q_lookupFinished(const QDnsLookupReply &reply);

static const char *msgNoIpV6NameServerAdresses;

bool isFinished;

Expand Down
152 changes: 46 additions & 106 deletions src/network/kernel/qdnslookup_unix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ using namespace Qt::StringLiterals;
#if QT_CONFIG(res_setservers)
// https://www.ibm.com/docs/en/i/7.3?topic=ssw_ibm_i_73/apis/ressetservers.html
// https://docs.oracle.com/cd/E86824_01/html/E54774/res-setservers-3resolv.html
static const char *applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
{
if (nameserver.isNull())
return nullptr;
union res_sockaddr_union u;
setSockaddr(reinterpret_cast<sockaddr *>(&u.sin), nameserver, port);
res_setservers(state, &u, 1);
return nullptr;
if (!nameserver.isNull()) {
union res_sockaddr_union u;
setSockaddr(reinterpret_cast<sockaddr *>(&u.sin), nameserver, port);
res_setservers(state, &u, 1);
}
return true;
}
#else
template <typename T> void setNsMap(T &ext, std::enable_if_t<sizeof(T::nsmap) != 0, uint16_t> v)
Expand Down Expand Up @@ -83,20 +83,17 @@ template <typename State> bool setIpv6NameServer(State *, const void *, quint16)
return false;
}

static const char *applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
{
if (nameserver.isNull())
return nullptr;
return true;

state->nscount = 1;
state->nsaddr_list[0].sin_family = AF_UNSPEC;
if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
if (!setIpv6NameServer(state, &nameserver, port))
return QDnsLookupPrivate::msgNoIpV6NameServerAdresses;
} else {
setSockaddr(&state->nsaddr_list[0], nameserver, port);
}
return nullptr;
if (nameserver.protocol() == QAbstractSocket::IPv6Protocol)
return setIpv6NameServer(state, &nameserver, port);
setSockaddr(&state->nsaddr_list[0], nameserver, port);
return true;
}
#endif // !QT_CONFIG(res_setservers)

Expand All @@ -105,18 +102,17 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
// Initialize state.
std::remove_pointer_t<res_state> state = {};
if (res_ninit(&state) < 0) {
reply->error = QDnsLookup::ResolverError;
reply->errorString = tr("Resolver initialization failed");
return;
int error = errno;
qErrnoWarning(error, "QDnsLookup: Resolver initialization failed");
return reply->makeResolverSystemError(error);
}
auto guard = qScopeGuard([&] { res_nclose(&state); });

//Check if a nameserver was set. If so, use it
if (const char *msg = applyNameServer(&state, nameserver, DnsPort)) {
qWarning("%s", msg);
reply->error = QDnsLookup::ResolverError;
reply->errorString = tr(msg);
return;
if (!applyNameServer(&state, nameserver, DnsPort)) {
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"));
}
#ifdef QDNSLOOKUP_DEBUG
state.options |= RES_DEBUG;
Expand All @@ -132,9 +128,8 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
responseLength = res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
if (Q_UNLIKELY(responseLength > buffer.size())) {
// Ok, we give up.
reply->error = QDnsLookup::ResolverError;
reply->errorString.clear(); // We cannot be more specific, alas.
return;
return reply->setError(QDnsLookup::ResolverError,
QDnsLookup::tr("Reply was too large"));
}
}

Expand All @@ -143,38 +138,12 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
// responseLength in case of error, we still can extract the
// exact error code from the response.
HEADER *header = (HEADER*)response;
switch (header->rcode) {
case NOERROR:
break;
case FORMERR:
reply->error = QDnsLookup::InvalidRequestError;
reply->errorString = tr("Server could not process query");
return;
case SERVFAIL:
case NOTIMP:
reply->error = QDnsLookup::ServerFailureError;
reply->errorString = tr("Server failure");
return;
case NXDOMAIN:
reply->error = QDnsLookup::NotFoundError;
reply->errorString = tr("Non existent domain");
return;
case REFUSED:
reply->error = QDnsLookup::ServerRefusedError;
reply->errorString = tr("Server refused to answer");
return;
default:
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid reply received");
return;
}
if (header->rcode)
return reply->makeDnsRcodeError(header->rcode);

// Check the reply is valid.
if (responseLength < int(sizeof(HEADER))) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid reply received");
return;
}
if (responseLength < int(sizeof(HEADER)))
return reply->makeInvalidReplyError();

char host[PACKETSZ], answer[PACKETSZ];
qptrdiff offset = sizeof(HEADER);
Expand All @@ -184,29 +153,24 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
// Skip the query host, type (2 bytes) and class (2 bytes).
status = dn_expand(response, response + responseLength, response + offset, host, sizeof(host));
if (status < 0) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Could not expand domain name");
reply->makeInvalidReplyError(QDnsLookup::tr("Could not expand domain name"));
return;
}
if (offset + status + 4 >= responseLength)
header->qdcount = 0xffff; // invalid reply below
else
offset += status + 4;
}
if (ntohs(header->qdcount) > 1) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid reply received");
return;
}
if (ntohs(header->qdcount) > 1)
return reply->makeInvalidReplyError();

// Extract results.
const int answerCount = ntohs(header->ancount);
int answerIndex = 0;
while ((offset < responseLength) && (answerIndex < answerCount)) {
status = dn_expand(response, response + responseLength, response + offset, host, sizeof(host));
if (status < 0) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Could not expand domain name");
reply->makeInvalidReplyError(QDnsLookup::tr("Could not expand domain name"));
return;
}
const QString name = QUrl::fromAce(host);
Expand All @@ -227,59 +191,44 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
continue;

if (type == QDnsLookup::A) {
if (size != 4) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid IPv4 address record");
return;
}
if (size != 4)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid IPv4 address record"));
const quint32 addr = qFromBigEndian<quint32>(response + offset);
QDnsHostAddressRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
record.d->value = QHostAddress(addr);
reply->hostAddressRecords.append(record);
} else if (type == QDnsLookup::AAAA) {
if (size != 16) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid IPv6 address record");
return;
}
if (size != 16)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid IPv6 address record"));
QDnsHostAddressRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
record.d->value = QHostAddress(response + offset);
reply->hostAddressRecords.append(record);
} else if (type == QDnsLookup::CNAME) {
status = dn_expand(response, response + responseLength, response + offset, answer, sizeof(answer));
if (status < 0) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid canonical name record");
return;
}
if (status < 0)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid canonical name record"));
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
record.d->value = QUrl::fromAce(answer);
reply->canonicalNameRecords.append(record);
} else if (type == QDnsLookup::NS) {
status = dn_expand(response, response + responseLength, response + offset, answer, sizeof(answer));
if (status < 0) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid name server record");
return;
}
if (status < 0)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid name server record"));
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
record.d->value = QUrl::fromAce(answer);
reply->nameServerRecords.append(record);
} else if (type == QDnsLookup::PTR) {
status = dn_expand(response, response + responseLength, response + offset, answer, sizeof(answer));
if (status < 0) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid pointer record");
return;
}
if (status < 0)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid pointer record"));
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
Expand All @@ -288,11 +237,8 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
} else if (type == QDnsLookup::MX) {
const quint16 preference = qFromBigEndian<quint16>(response + offset);
status = dn_expand(response, response + responseLength, response + offset + 2, answer, sizeof(answer));
if (status < 0) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid mail exchange record");
return;
}
if (status < 0)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid mail exchange record"));
QDnsMailExchangeRecord record;
record.d->exchange = QUrl::fromAce(answer);
record.d->name = name;
Expand All @@ -304,11 +250,8 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
const quint16 weight = qFromBigEndian<quint16>(response + offset + 2);
const quint16 port = qFromBigEndian<quint16>(response + offset + 4);
status = dn_expand(response, response + responseLength, response + offset + 6, answer, sizeof(answer));
if (status < 0) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid service record");
return;
}
if (status < 0)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
QDnsServiceRecord record;
record.d->name = name;
record.d->target = QUrl::fromAce(answer);
Expand All @@ -325,11 +268,8 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
while (txt < offset + size) {
const unsigned char length = response[txt];
txt++;
if (txt + length > offset + size) {
reply->error = QDnsLookup::InvalidReplyError;
reply->errorString = tr("Invalid text record");
return;
}
if (txt + length > offset + size)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid text record"));
record.d->values << QByteArrayView(response + txt, length).toByteArray();
txt += length;
}
Expand Down
Loading

0 comments on commit 029e0bf

Please sign in to comment.