Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qdnslookup_win.cpp
Go to the documentation of this file.
1// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
2// Copyright (C) 2023 Intel Corporation.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include <winsock2.h>
6#include "qdnslookup_p.h"
7
8#include <qendian.h>
9#include <private/qnativesocketengine_p.h>
10#include <private/qsystemerror_p.h>
11#include <qurl.h>
12#include <qspan.h>
13
14#include <qt_windows.h>
15#include <windns.h>
16#include <memory.h>
17
18#ifndef DNS_ADDR_MAX_SOCKADDR_LENGTH
19// MinGW headers are missing almost all of this
20typedef struct Qt_DnsAddr {
21 CHAR MaxSa[32];
36# ifndef DNS_QUERY_RESULTS_VERSION1
44typedef VOID WINAPI DNS_QUERY_COMPLETION_ROUTINE(PVOID pQueryContext,PDNS_QUERY_RESULT pQueryResults);
46# endif
57
58typedef void *PDNS_QUERY_CANCEL; // not really, but we don't need it
59extern "C" {
60DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest,
61 PDNS_QUERY_RESULT pQueryResults,
62 PDNS_QUERY_CANCEL pCancelHandle);
63}
64#endif
65
67
70{
71 // WinDNS wants MTU - IP Header - UDP header for some reason, in spite
72 // of never needing that much
73 QVarLengthArray<unsigned char, 1472> query(1472);
74
75 auto dnsBuffer = new (query.data()) DNS_MESSAGE_BUFFER;
76 DWORD dnsBufferSize = query.size();
77 WORD xid = 0;
78 bool recursionDesired = true;
79
80 SetLastError(ERROR_SUCCESS);
81
82 // MinGW winheaders incorrectly declare the third parameter as LPWSTR
83 if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
84 const_cast<LPWSTR>(request->QueryName), request->QueryType,
85 xid, recursionDesired)) {
86 // let's try reallocating
87 query.resize(dnsBufferSize);
88 if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
89 const_cast<LPWSTR>(request->QueryName), request->QueryType,
90 xid, recursionDesired)) {
91 return GetLastError();
92 }
93 }
94
95 // set AD bit: we want to trust this server
96 dnsBuffer->MessageHead.AuthenticatedData = true;
97
99 if (!self->sendDnsOverTls(reply, { query.data(), qsizetype(dnsBufferSize) }, replyBuffer))
100 return DNS_STATUS(-1); // error set in reply
101
102 // interpret the RCODE in the reply
103 auto response = reinterpret_cast<PDNS_MESSAGE_BUFFER>(replyBuffer.data());
104 DNS_HEADER *header = &response->MessageHead;
105 if (!header->IsResponse)
106 return DNS_ERROR_BAD_PACKET; // not a reply
107
108 // Convert the byte order for the 16-bit quantities in the header, so
109 // DnsExtractRecordsFromMessage can parse the contents.
110 //header->Xid = qFromBigEndian(header->Xid);
111 header->QuestionCount = qFromBigEndian(header->QuestionCount);
112 header->AnswerCount = qFromBigEndian(header->AnswerCount);
113 header->NameServerCount = qFromBigEndian(header->NameServerCount);
114 header->AdditionalCount = qFromBigEndian(header->AdditionalCount);
115
116 results->QueryOptions = request->QueryOptions;
117 return DnsExtractRecordsFromMessage_W(response, replyBuffer.size(), &results->pQueryRecords);
118}
119
120void QDnsLookupRunnable::query(QDnsLookupReply *reply)
121{
122 // Perform DNS query.
123 alignas(DNS_ADDR_ARRAY) uchar dnsAddresses[sizeof(DNS_ADDR_ARRAY) + sizeof(DNS_ADDR)];
125 request.Version = 1;
126 request.QueryName = reinterpret_cast<const wchar_t *>(requestName.constData());
127 request.QueryType = requestType;
128 request.QueryOptions = DNS_QUERY_STANDARD | DNS_QUERY_TREAT_AS_FQDN;
129
130 if (protocol == QDnsLookup::Standard && !nameserver.isNull()) {
131 memset(dnsAddresses, 0, sizeof(dnsAddresses));
132 request.pDnsServerList = new (dnsAddresses) DNS_ADDR_ARRAY;
133 auto addr = new (request.pDnsServerList->AddrArray) DNS_ADDR[1];
134 auto sa = new (addr[0].MaxSa) sockaddr;
135 request.pDnsServerList->MaxCount = sizeof(dnsAddresses);
136 request.pDnsServerList->AddrCount = 1;
137 // ### setting port 53 seems to cause some systems to fail
138 setSockaddr(sa, nameserver, port == DnsPort ? 0 : port);
139 request.pDnsServerList->Family = sa->sa_family;
140 }
141
143 results.Version = 1;
144 DNS_STATUS status = ERROR_INVALID_PARAMETER;
145 switch (protocol) {
147 status = DnsQueryEx(&request, &results, nullptr);
148 break;
150 status = sendAlternate(this, reply, &request, &results);
151 break;
152 }
153
154 if (status == DNS_STATUS(-1))
155 return; // error already set in reply
156 if (status >= DNS_ERROR_RCODE_FORMAT_ERROR && status <= DNS_ERROR_RCODE_LAST)
157 return reply->makeDnsRcodeError(status - DNS_ERROR_RCODE_FORMAT_ERROR + 1);
158 else if (status == ERROR_TIMEOUT)
159 return reply->makeTimeoutError();
160 else if (status != ERROR_SUCCESS)
161 return reply->makeResolverSystemError(status);
162
163 QStringView lastEncodedName;
164 QString cachedDecodedName;
165 auto extractAndCacheHost = [&](QStringView name) -> const QString & {
166 lastEncodedName = name;
167 cachedDecodedName = decodeLabel(name);
168 return cachedDecodedName;
169 };
170 auto extractMaybeCachedHost = [&](QStringView name) -> const QString & {
171 return lastEncodedName == name ? cachedDecodedName : extractAndCacheHost(name);
172 };
173
174 // Extract results.
175 for (PDNS_RECORD ptr = results.pQueryRecords; ptr != NULL; ptr = ptr->pNext) {
176 // warning: always assign name to the record before calling extractXxxHost() again
177 const QString &name = extractMaybeCachedHost(ptr->pName);
178 if (ptr->wType == QDnsLookup::A) {
180 record.d->name = name;
181 record.d->timeToLive = ptr->dwTtl;
182 record.d->value = QHostAddress(ntohl(ptr->Data.A.IpAddress));
183 reply->hostAddressRecords.append(record);
184 } else if (ptr->wType == QDnsLookup::AAAA) {
186 memcpy(&addr, &ptr->Data.AAAA.Ip6Address, sizeof(Q_IPV6ADDR));
187
189 record.d->name = name;
190 record.d->timeToLive = ptr->dwTtl;
191 record.d->value = QHostAddress(addr);
192 reply->hostAddressRecords.append(record);
193 } else if (ptr->wType == QDnsLookup::CNAME) {
195 record.d->name = name;
196 record.d->timeToLive = ptr->dwTtl;
197 record.d->value = extractAndCacheHost(ptr->Data.Cname.pNameHost);
198 reply->canonicalNameRecords.append(record);
199 } else if (ptr->wType == QDnsLookup::MX) {
201 record.d->name = name;
202 record.d->exchange = decodeLabel(QStringView(ptr->Data.Mx.pNameExchange));
203 record.d->preference = ptr->Data.Mx.wPreference;
204 record.d->timeToLive = ptr->dwTtl;
205 reply->mailExchangeRecords.append(record);
206 } else if (ptr->wType == QDnsLookup::NS) {
208 record.d->name = name;
209 record.d->timeToLive = ptr->dwTtl;
210 record.d->value = decodeLabel(QStringView(ptr->Data.Ns.pNameHost));
211 reply->nameServerRecords.append(record);
212 } else if (ptr->wType == QDnsLookup::PTR) {
214 record.d->name = name;
215 record.d->timeToLive = ptr->dwTtl;
216 record.d->value = decodeLabel(QStringView(ptr->Data.Ptr.pNameHost));
217 reply->pointerRecords.append(record);
218 } else if (ptr->wType == QDnsLookup::SRV) {
220 record.d->name = name;
221 record.d->target = decodeLabel(QStringView(ptr->Data.Srv.pNameTarget));
222 record.d->port = ptr->Data.Srv.wPort;
223 record.d->priority = ptr->Data.Srv.wPriority;
224 record.d->timeToLive = ptr->dwTtl;
225 record.d->weight = ptr->Data.Srv.wWeight;
226 reply->serviceRecords.append(record);
227 } else if (ptr->wType == QDnsLookup::TLSA) {
228 // Note: untested, because the DNS_RECORD reply appears to contain
229 // no records relating to TLSA. Maybe WinDNS filters them out of
230 // zones without DNSSEC.
232 record.d->name = name;
233 record.d->timeToLive = ptr->dwTtl;
234
235 const auto &tlsa = ptr->Data.Tlsa;
236 const quint8 usage = tlsa.bCertUsage;
237 const quint8 selector = tlsa.bSelector;
238 const quint8 matchType = tlsa.bMatchingType;
239
242 record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType);
243 record.d->value.assign(tlsa.bCertificateAssociationData,
244 tlsa.bCertificateAssociationData + tlsa.bCertificateAssociationDataLength);
245 reply->tlsAssociationRecords.append(std::move(record));
246 } else if (ptr->wType == QDnsLookup::TXT) {
248 record.d->name = name;
249 record.d->timeToLive = ptr->dwTtl;
250 for (unsigned int i = 0; i < ptr->Data.Txt.dwStringCount; ++i) {
251 record.d->values << QStringView(ptr->Data.Txt.pStringArray[i]).toLatin1();
252 }
253 reply->textRecords.append(record);
254 }
255 }
256
257 DnsRecordListFree(results.pQueryRecords, DnsFreeRecordList);
258}
259
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
The QDnsDomainNameRecord class stores information about a domain name record.
Definition qdnslookup.h:31
The QDnsHostAddressRecord class stores information about a host address record.
Definition qdnslookup.h:53
The QDnsMailExchangeRecord class stores information about a DNS MX record.
Definition qdnslookup.h:75
The QDnsServiceRecord class stores information about a DNS SRV record.
Definition qdnslookup.h:98
The QDnsTextRecord class stores information about a DNS TXT record.
Definition qdnslookup.h:123
The QDnsTlsAssociationRecord class stores information about a DNS TLSA record.
Definition qdnslookup.h:145
CertificateUsage
This enumeration contains valid values for the certificate usage field of TLS Association queries.
Definition qdnslookup.h:148
Selector
This enumeration contains valid values for the selector field of TLS Association queries.
Definition qdnslookup.h:166
MatchingType
This enumeration contains valid values for the matching type field of TLS Association queries.
Definition qdnslookup.h:180
The QHostAddress class provides an IP address.
bool isNull() const
Returns true if this host address is not valid for any host or interface.
\inmodule QtCore
Definition qstringview.h:78
QByteArray toLatin1() const
Returns a Latin-1 representation of the string as a QByteArray.
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
Combined button and popup list for selecting options.
QT_SOCKLEN_T setSockaddr(sockaddr_in *sin, const QHostAddress &addr, quint16 port=0)
static QString header(const QString &name)
constexpr quint16 DnsPort
struct Qt_DnsAddrArray * PDNS_ADDR_ARRAY
struct Qt_DnsAddr DNS_ADDR
struct Qt_DnsAddrArray DNS_ADDR_ARRAY
VOID WINAPI DNS_QUERY_COMPLETION_ROUTINE(PVOID pQueryContext, PDNS_QUERY_RESULT pQueryResults)
void * PDNS_QUERY_CANCEL
DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest, PDNS_QUERY_RESULT pQueryResults, PDNS_QUERY_CANCEL pCancelHandle)
struct Qt_DNS_QUERY_RESULT DNS_QUERY_RESULT
static QT_BEGIN_NAMESPACE DNS_STATUS sendAlternate(QDnsLookupRunnable *self, QDnsLookupReply *reply, PDNS_QUERY_REQUEST request, PDNS_QUERY_RESULT results)
struct Qt_DnsAddr * PDNS_ADDR
struct Qt_DNS_QUERY_RESULT * PDNS_QUERY_RESULT
struct Qt_DNS_QUERY_REQUEST * PDNS_QUERY_REQUEST
DNS_QUERY_COMPLETION_ROUTINE * PDNS_QUERY_COMPLETION_ROUTINE
struct Qt_DNS_QUERY_REQUEST DNS_QUERY_REQUEST
EGLOutputPortEXT port
constexpr T qFromBigEndian(T source)
Definition qendian.h:174
QIPv6Address Q_IPV6ADDR
static ControlElement< T > * ptr(QWidget *widget)
GLuint name
GLenum query
GLenum const void * addr
GLsizeiptr const void GLenum usage
Definition qopenglext.h:543
unsigned char uchar
Definition qtypes.h:32
unsigned char quint8
Definition qtypes.h:46
QFileSelector selector
[1]
MyRecord record(int row) const
[0]
QNetworkRequest request(url)
QNetworkReply * reply
PDNS_ADDR_ARRAY pDnsServerList
PDNS_QUERY_COMPLETION_ROUTINE pQueryCompletionCallback
DWORD DnsAddrUserDword[8]