QDnsLookup: merge some of the domain label expansion code

Use qt_ACE_do directly from QtCore, to avoid going through Latin1
(US-ASCII) multiple times.

Drive-by reduce the size of the buffers from PACKETSZ (512) to the
maximum name a label can be (255 plus 1 for the null, just in case).

Drive-by replace the last QString::fromWCharArray with QStringView,
saving an unnecessary memory allocation before calling
QtPrivate::convertToLatin1().

Change-Id: I3e3bfef633af4130a03afffd175d8be1feb5d74b
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Thiago Macieira 2023-05-09 11:07:18 -07:00
parent eda71105ff
commit 01dfcfcecf
5 changed files with 66 additions and 45 deletions

View File

@ -29,8 +29,8 @@ extern Q_AUTOTEST_EXPORT qsizetype qt_urlRecode(QString &appendTo, QStringView u
// in qurlidna.cpp
enum AceLeadingDot { AllowLeadingDot, ForbidLeadingDot };
enum AceOperation { ToAceOnly, NormalizeAce };
extern QString qt_ACE_do(const QString &domain, AceOperation op, AceLeadingDot dot,
QUrl::AceProcessingOptions options);
QString Q_CORE_EXPORT qt_ACE_do(const QString &domain, AceOperation op, AceLeadingDot dot,
QUrl::AceProcessingOptions options = {});
extern Q_AUTOTEST_EXPORT void qt_punycodeEncoder(QStringView in, QString *output);
extern Q_AUTOTEST_EXPORT QString qt_punycodeDecoder(const QString &pc);

View File

@ -1048,8 +1048,18 @@ QDnsTextRecord &QDnsTextRecord::operator=(const QDnsTextRecord &other)
very fast and never fails.
*/
static QDnsLookupRunnable::EncodedLabel encodeLabel(const QString &label)
{
QString encodedLabel = qt_ACE_do(label, ToAceOnly, ForbidLeadingDot);
#ifdef Q_OS_WIN
return encodedLabel;
#else
return std::move(encodedLabel).toLatin1();
#endif
}
inline QDnsLookupRunnable::QDnsLookupRunnable(const QDnsLookupPrivate *d)
: requestName(QUrl::toAce(d->name)),
: requestName(encodeLabel(d->name)),
nameserver(d->nameserver),
requestType(d->type),
port(d->port)

View File

@ -25,6 +25,7 @@
#include "QtNetwork/qdnslookup.h"
#include "QtNetwork/qhostaddress.h"
#include "private/qobject_p.h"
#include "private/qurl_p.h"
QT_REQUIRE_CONFIG(dnslookup);
@ -174,6 +175,12 @@ class QDnsLookupRunnable : public QObject, public QRunnable
Q_OBJECT
public:
#ifdef Q_OS_WIN
using EncodedLabel = QString;
#else
using EncodedLabel = QByteArray;
#endif
QDnsLookupRunnable(const QDnsLookupPrivate *d);
void run() override;
@ -181,8 +188,13 @@ signals:
void finished(const QDnsLookupReply &reply);
private:
template <typename T> static QString decodeLabel(T encodedLabel)
{
return qt_ACE_do(encodedLabel.toString(), NormalizeAce, ForbidLeadingDot);
}
void query(QDnsLookupReply *reply);
QByteArray requestName;
EncodedLabel requestName;
QHostAddress nameserver;
QDnsLookup::Type requestType;
quint16 port;

View File

@ -219,18 +219,26 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
if (header->rcode)
return reply->makeDnsRcodeError(header->rcode);
char host[PACKETSZ], answer[PACKETSZ];
qptrdiff offset = sizeof(HEADER);
unsigned char *response = buffer.data();
int status;
auto expandHost = [&](qptrdiff offset) {
char host[MAXCDNAME + 1];
status = dn_expand(response, response + responseLength, response + offset, host, sizeof(host));
if (status >= 0)
return decodeLabel(QLatin1StringView(host));
// failed
reply->makeInvalidReplyError(QDnsLookup::tr("Could not expand domain name"));
return QString();
};
if (ntohs(header->qdcount) == 1) {
// 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->makeInvalidReplyError(QDnsLookup::tr("Could not expand domain name"));
expandHost(offset);
if (status < 0)
return;
}
if (offset + status + 4 >= responseLength)
header->qdcount = 0xffff; // invalid reply below
else
@ -243,12 +251,9 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
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->makeInvalidReplyError(QDnsLookup::tr("Could not expand domain name"));
const QString name = expandHost(offset);
if (status < 0)
return;
}
const QString name = QUrl::fromAce(host);
offset += status;
if (offset + RRFIXEDSZ > responseLength) {
@ -283,57 +288,52 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
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));
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
record.d->value = expandHost(offset);
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));
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
record.d->value = expandHost(offset);
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)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid pointer record"));
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
record.d->value = QUrl::fromAce(answer);
record.d->value = expandHost(offset);
if (status < 0)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid pointer record"));
reply->pointerRecords.append(record);
} 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)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid mail exchange record"));
QDnsMailExchangeRecord record;
record.d->exchange = QUrl::fromAce(answer);
record.d->exchange = expandHost(offset + 2);
record.d->name = name;
record.d->preference = preference;
record.d->timeToLive = ttl;
if (status < 0)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid mail exchange record"));
reply->mailExchangeRecords.append(record);
} else if (type == QDnsLookup::SRV) {
const quint16 priority = qFromBigEndian<quint16>(response + offset);
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)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
QDnsServiceRecord record;
record.d->name = name;
record.d->target = QUrl::fromAce(answer);
record.d->target = expandHost(offset + 6);
record.d->port = port;
record.d->priority = priority;
record.d->timeToLive = ttl;
record.d->weight = weight;
if (status < 0)
return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
reply->serviceRecords.append(record);
} else if (type == QDnsLookup::TXT) {
QDnsTextRecord record;

View File

@ -66,11 +66,10 @@ QT_BEGIN_NAMESPACE
void QDnsLookupRunnable::query(QDnsLookupReply *reply)
{
// Perform DNS query.
const QString requestNameUtf16 = QString::fromUtf8(requestName.data(), requestName.size());
alignas(DNS_ADDR_ARRAY) uchar dnsAddresses[sizeof(DNS_ADDR_ARRAY) + sizeof(DNS_ADDR)];
DNS_QUERY_REQUEST request = {};
request.Version = 1;
request.QueryName = reinterpret_cast<const wchar_t *>(requestNameUtf16.constData());
request.QueryName = reinterpret_cast<const wchar_t *>(requestName.constData());
request.QueryType = requestType;
request.QueryOptions = DNS_QUERY_STANDARD | DNS_QUERY_TREAT_AS_FQDN;
@ -98,7 +97,7 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
// Extract results.
for (PDNS_RECORD ptr = results.pQueryRecords; ptr != NULL; ptr = ptr->pNext) {
const QString name = QUrl::fromAce( QString::fromWCharArray( ptr->pName ).toLatin1() );
const QString name = decodeLabel(ptr->pName);
if (ptr->wType == QDnsLookup::A) {
QDnsHostAddressRecord record;
record.d->name = name;
@ -118,12 +117,12 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Cname.pNameHost).toLatin1());
record.d->value = decodeLabel(ptr->Data.Cname.pNameHost);
reply->canonicalNameRecords.append(record);
} else if (ptr->wType == QDnsLookup::MX) {
QDnsMailExchangeRecord record;
record.d->name = name;
record.d->exchange = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Mx.pNameExchange).toLatin1());
record.d->exchange = decodeLabel(QStringView(ptr->Data.Mx.pNameExchange));
record.d->preference = ptr->Data.Mx.wPreference;
record.d->timeToLive = ptr->dwTtl;
reply->mailExchangeRecords.append(record);
@ -131,18 +130,18 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ns.pNameHost).toLatin1());
record.d->value = decodeLabel(QStringView(ptr->Data.Ns.pNameHost));
reply->nameServerRecords.append(record);
} else if (ptr->wType == QDnsLookup::PTR) {
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ptr.pNameHost).toLatin1());
record.d->value = decodeLabel(QStringView(ptr->Data.Ptr.pNameHost));
reply->pointerRecords.append(record);
} else if (ptr->wType == QDnsLookup::SRV) {
QDnsServiceRecord record;
record.d->name = name;
record.d->target = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Srv.pNameTarget).toLatin1());
record.d->target = decodeLabel(QStringView(ptr->Data.Srv.pNameTarget));
record.d->port = ptr->Data.Srv.wPort;
record.d->priority = ptr->Data.Srv.wPriority;
record.d->timeToLive = ptr->dwTtl;
@ -153,7 +152,7 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply)
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
for (unsigned int i = 0; i < ptr->Data.Txt.dwStringCount; ++i) {
record.d->values << QString::fromWCharArray((ptr->Data.Txt.pStringArray[i])).toLatin1();;
record.d->values << QStringView(ptr->Data.Txt.pStringArray[i]).toLatin1();
}
reply->textRecords.append(record);
}