diff options
Diffstat (limited to 'src/network/kernel/qdnslookup_win.cpp')
-rw-r--r-- | src/network/kernel/qdnslookup_win.cpp | 119 |
1 files changed, 107 insertions, 12 deletions
diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp index 9ca8f25bbf..1b07776db9 100644 --- a/src/network/kernel/qdnslookup_win.cpp +++ b/src/network/kernel/qdnslookup_win.cpp @@ -5,9 +5,11 @@ #include <winsock2.h> #include "qdnslookup_p.h" -#include <qurl.h> +#include <qendian.h> #include <private/qnativesocketengine_p.h> #include <private/qsystemerror_p.h> +#include <qurl.h> +#include <qspan.h> #include <qt_windows.h> #include <windns.h> @@ -63,18 +65,69 @@ DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest, QT_BEGIN_NAMESPACE +static DNS_STATUS sendAlternate(QDnsLookupRunnable *self, QDnsLookupReply *reply, + PDNS_QUERY_REQUEST request, PDNS_QUERY_RESULT results) +{ + // WinDNS wants MTU - IP Header - UDP header for some reason, in spite + // of never needing that much + QVarLengthArray<unsigned char, 1472> query(1472); + + auto dnsBuffer = new (query.data()) DNS_MESSAGE_BUFFER; + DWORD dnsBufferSize = query.size(); + WORD xid = 0; + bool recursionDesired = true; + + SetLastError(ERROR_SUCCESS); + + // MinGW winheaders incorrectly declare the third parameter as LPWSTR + if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize, + const_cast<LPWSTR>(request->QueryName), request->QueryType, + xid, recursionDesired)) { + // let's try reallocating + query.resize(dnsBufferSize); + if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize, + const_cast<LPWSTR>(request->QueryName), request->QueryType, + xid, recursionDesired)) { + return GetLastError(); + } + } + + // set AD bit: we want to trust this server + dnsBuffer->MessageHead.AuthenticatedData = true; + + QDnsLookupRunnable::ReplyBuffer replyBuffer; + if (!self->sendDnsOverTls(reply, { query.data(), qsizetype(dnsBufferSize) }, replyBuffer)) + return DNS_STATUS(-1); // error set in reply + + // interpret the RCODE in the reply + auto response = reinterpret_cast<PDNS_MESSAGE_BUFFER>(replyBuffer.data()); + DNS_HEADER *header = &response->MessageHead; + if (!header->IsResponse) + return DNS_ERROR_BAD_PACKET; // not a reply + + // Convert the byte order for the 16-bit quantities in the header, so + // DnsExtractRecordsFromMessage can parse the contents. + //header->Xid = qFromBigEndian(header->Xid); + header->QuestionCount = qFromBigEndian(header->QuestionCount); + header->AnswerCount = qFromBigEndian(header->AnswerCount); + header->NameServerCount = qFromBigEndian(header->NameServerCount); + header->AdditionalCount = qFromBigEndian(header->AdditionalCount); + + results->QueryOptions = request->QueryOptions; + return DnsExtractRecordsFromMessage_W(response, replyBuffer.size(), &results->pQueryRecords); +} + 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; - if (!nameserver.isNull()) { + if (protocol == QDnsLookup::Standard && !nameserver.isNull()) { memset(dnsAddresses, 0, sizeof(dnsAddresses)); request.pDnsServerList = new (dnsAddresses) DNS_ADDR_ARRAY; auto addr = new (request.pDnsServerList->AddrArray) DNS_ADDR[1]; @@ -88,7 +141,18 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply) DNS_QUERY_RESULT results = {}; results.Version = 1; - const DNS_STATUS status = DnsQueryEx(&request, &results, nullptr); + DNS_STATUS status = ERROR_INVALID_PARAMETER; + switch (protocol) { + case QDnsLookup::Standard: + status = DnsQueryEx(&request, &results, nullptr); + break; + case QDnsLookup::DnsOverTls: + status = sendAlternate(this, reply, &request, &results); + break; + } + + if (status == DNS_STATUS(-1)) + return; // error already set in reply if (status >= DNS_ERROR_RCODE_FORMAT_ERROR && status <= DNS_ERROR_RCODE_LAST) return reply->makeDnsRcodeError(status - DNS_ERROR_RCODE_FORMAT_ERROR + 1); else if (status == ERROR_TIMEOUT) @@ -96,9 +160,21 @@ void QDnsLookupRunnable::query(QDnsLookupReply *reply) else if (status != ERROR_SUCCESS) return reply->makeResolverSystemError(status); + QStringView lastEncodedName; + QString cachedDecodedName; + auto extractAndCacheHost = [&](QStringView name) -> const QString & { + lastEncodedName = name; + cachedDecodedName = decodeLabel(name); + return cachedDecodedName; + }; + auto extractMaybeCachedHost = [&](QStringView name) -> const QString & { + return lastEncodedName == name ? cachedDecodedName : extractAndCacheHost(name); + }; + // Extract results. for (PDNS_RECORD ptr = results.pQueryRecords; ptr != NULL; ptr = ptr->pNext) { - const QString name = QUrl::fromAce( QString::fromWCharArray( ptr->pName ).toLatin1() ); + // warning: always assign name to the record before calling extractXxxHost() again + const QString &name = extractMaybeCachedHost(ptr->pName); if (ptr->wType == QDnsLookup::A) { QDnsHostAddressRecord record; record.d->name = name; @@ -118,12 +194,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 = extractAndCacheHost(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,29 +207,48 @@ 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; record.d->weight = ptr->Data.Srv.wWeight; reply->serviceRecords.append(record); + } else if (ptr->wType == QDnsLookup::TLSA) { + // Note: untested, because the DNS_RECORD reply appears to contain + // no records relating to TLSA. Maybe WinDNS filters them out of + // zones without DNSSEC. + QDnsTlsAssociationRecord record; + record.d->name = name; + record.d->timeToLive = ptr->dwTtl; + + const auto &tlsa = ptr->Data.Tlsa; + const quint8 usage = tlsa.bCertUsage; + const quint8 selector = tlsa.bSelector; + const quint8 matchType = tlsa.bMatchingType; + + record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage); + record.d->selector = QDnsTlsAssociationRecord::Selector(selector); + record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType); + record.d->value.assign(tlsa.bCertificateAssociationData, + tlsa.bCertificateAssociationData + tlsa.bCertificateAssociationDataLength); + reply->tlsAssociationRecords.append(std::move(record)); } else if (ptr->wType == QDnsLookup::TXT) { QDnsTextRecord record; 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); } |