From 9a73bc5f3d48f6ccaef02871ad62284dd61eff38 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 8 May 2023 20:29:39 -0700 Subject: [PATCH] QDnsLookup/Windows: use DnsQueryEx so IPv6 servers are supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar test with link-local server on the same network: $ $objdir/tests/manual/qdnslookup/qdnslookup.exe @fe80::1e1b:dff:fee2:49e6%Ethernet ; <<>> QDnsLookup 6.6.0 <<>> qdnslookup @fe80::1e1b:dff:fee2:49e6%Ethernet ;; status: NoError ;; QUESTION: ;qt-project.org IN A ;; ANSWER: qt-project.org 3532 IN A 52.18.144.254 ;; Query time: 17 ms ;; SERVER: fe80::1e1b:dff:fee2:49e6%Ethernet#53 Server's dnsmasq log shows it was queried. We don't know why we must set the port to 0. It works for me the regular way, but not for everyone who tested this patch. [ChangeLog][QtNetwork][QDnsLookup] setNameserver() now supports IPv6 servers with on Apple systems, AIX, FreeBSD, NetBSD, Solaris, and Windows. (AIX, NetBSD and Solaris not directly tested, but their docs online show they have res_setservers()) Change-Id: I3e3bfef633af4130a03afffd175d5bfd4e7922b9 Reviewed-by: MÃ¥rten Nordheim --- src/network/kernel/qdnslookup_win.cpp | 79 ++++++++++++++++++++------- 1 file changed, 60 insertions(+), 19 deletions(-) diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp index 796ff9c206..7a09035774 100644 --- a/src/network/kernel/qdnslookup_win.cpp +++ b/src/network/kernel/qdnslookup_win.cpp @@ -5,37 +5,78 @@ #include "qdnslookup_p.h" #include +#include #include #include #include #include +#ifndef DNS_ADDR_MAX_SOCKADDR_LENGTH +// MinGW headers are missing almost all of this +typedef struct Qt_DnsAddr { + CHAR MaxSa[32]; + DWORD DnsAddrUserDword[8]; +} DNS_ADDR, *PDNS_ADDR; +typedef struct Qt_DnsAddrArray { + DWORD MaxCount; + DWORD AddrCount; + DWORD Tag; + WORD Family; + WORD WordReserved; + DWORD Flags; + DWORD MatchFlag; + DWORD Reserved1; + DWORD Reserved2; + DNS_ADDR AddrArray[]; +} DNS_ADDR_ARRAY, *PDNS_ADDR_ARRAY; +typedef struct Qt_DNS_QUERY_REQUEST { + ULONG Version; + PCWSTR QueryName; + WORD QueryType; + ULONG64 QueryOptions; + PDNS_ADDR_ARRAY pDnsServerList; + ULONG InterfaceIndex; + PDNS_QUERY_COMPLETION_ROUTINE pQueryCompletionCallback; + PVOID pQueryContext; +} DNS_QUERY_REQUEST, *PDNS_QUERY_REQUEST; + +typedef void *PDNS_QUERY_CANCEL; // not really, but we don't need it +extern "C" { +DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest, + PDNS_QUERY_RESULT pQueryResults, + PDNS_QUERY_CANCEL pCancelHandle); +} +#endif + QT_BEGIN_NAMESPACE void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply) { // Perform DNS query. - PDNS_RECORD dns_records = 0; const QString requestNameUtf16 = QString::fromUtf8(requestName.data(), requestName.size()); - IP4_ARRAY srvList; - memset(&srvList, 0, sizeof(IP4_ARRAY)); + alignas(DNS_ADDR_ARRAY) uchar dnsAddresses[sizeof(DNS_ADDR_ARRAY) + sizeof(DNS_ADDR)]; + DNS_QUERY_REQUEST request = {}; + request.Version = 1; + request.QueryName = reinterpret_cast(requestNameUtf16.constData()); + request.QueryType = requestType; + request.QueryOptions = DNS_QUERY_STANDARD; + if (!nameserver.isNull()) { - if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) { - // The below code is referenced from: http://support.microsoft.com/kb/831226 - srvList.AddrCount = 1; - srvList.AddrArray[0] = htonl(nameserver.toIPv4Address()); - } else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) { - // For supoprting IPv6 nameserver addresses, we'll need to switch - // from DnsQuey() to DnsQueryEx() as it supports passing an IPv6 - // address in the nameserver list - qWarning("%s", QDnsLookupPrivate::msgNoIpV6NameServerAdresses); - reply->error = QDnsLookup::ResolverError; - reply->errorString = tr(QDnsLookupPrivate::msgNoIpV6NameServerAdresses); - return; - } + memset(dnsAddresses, 0, sizeof(dnsAddresses)); + request.pDnsServerList = new (dnsAddresses) DNS_ADDR_ARRAY; + auto addr = new (request.pDnsServerList->AddrArray) DNS_ADDR[1]; + auto sa = new (addr[0].MaxSa) sockaddr; + request.pDnsServerList->MaxCount = sizeof(dnsAddresses); + request.pDnsServerList->AddrCount = 1; + // ### setting port 53 seems to cause some systems to fail + setSockaddr(sa, nameserver, 0); + request.pDnsServerList->Family = sa->sa_family; } - const DNS_STATUS status = DnsQuery_W(reinterpret_cast(requestNameUtf16.utf16()), requestType, DNS_QUERY_STANDARD, &srvList, &dns_records, NULL); + + DNS_QUERY_RESULT results = {}; + results.Version = 1; + const DNS_STATUS status = DnsQueryEx(&request, &results, nullptr); switch (status) { case ERROR_SUCCESS: break; @@ -63,7 +104,7 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN } // Extract results. - for (PDNS_RECORD ptr = dns_records; ptr != NULL; ptr = ptr->pNext) { + for (PDNS_RECORD ptr = results.pQueryRecords; ptr != NULL; ptr = ptr->pNext) { const QString name = QUrl::fromAce( QString::fromWCharArray( ptr->pName ).toLatin1() ); if (ptr->wType == QDnsLookup::A) { QDnsHostAddressRecord record; @@ -125,7 +166,7 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN } } - DnsRecordListFree(dns_records, DnsFreeRecordList); + DnsRecordListFree(results.pQueryRecords, DnsFreeRecordList); } QT_END_NAMESPACE