Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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// Qt-Security score:critical reason:data-parser
5
6#include <winsock2.h>
7#include "qdnslookup_p.h"
8
9#include <qendian.h>
10#include <private/qnativesocketengine_p.h>
11#include <private/qsystemerror_p.h>
12#include <qurl.h>
13#include <qspan.h>
14
15#include <qt_windows.h>
16#include <windns.h>
17#include <memory.h>
18
19#ifndef DNS_ADDR_MAX_SOCKADDR_LENGTH
20// MinGW headers are missing almost all of this
21typedef struct Qt_DnsAddr {
24} DNS_ADDR, *PDNS_ADDR;
37# ifndef DNS_QUERY_RESULTS_VERSION1
45typedef VOID WINAPI DNS_QUERY_COMPLETION_ROUTINE(PVOID pQueryContext,PDNS_QUERY_RESULT pQueryResults);
47# endif
58
59typedef void *PDNS_QUERY_CANCEL; // not really, but we don't need it
60extern "C" {
61DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest,
62 PDNS_QUERY_RESULT pQueryResults,
63 PDNS_QUERY_CANCEL pCancelHandle);
64}
65#endif
66
67QT_BEGIN_NAMESPACE
68
69static DNS_STATUS sendAlternate(QDnsLookupRunnable *self, QDnsLookupReply *reply,
70 PDNS_QUERY_REQUEST request, PDNS_QUERY_RESULT results)
71{
72 // WinDNS wants MTU - IP Header - UDP header for some reason, in spite
73 // of never needing that much
74 QVarLengthArray<unsigned char, 1472> query(1472);
75
76 auto dnsBuffer = new (query.data()) DNS_MESSAGE_BUFFER;
77 DWORD dnsBufferSize = query.size();
78 WORD xid = 0;
79 bool recursionDesired = true;
80
81 SetLastError(ERROR_SUCCESS);
82
83 // MinGW winheaders incorrectly declare the third parameter as LPWSTR
84 if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
85 const_cast<LPWSTR>(request->QueryName), request->QueryType,
86 xid, recursionDesired)) {
87 // let's try reallocating
88 query.resize(dnsBufferSize);
89 if (!DnsWriteQuestionToBuffer_W(dnsBuffer, &dnsBufferSize,
90 const_cast<LPWSTR>(request->QueryName), request->QueryType,
91 xid, recursionDesired)) {
92 return GetLastError();
93 }
94 }
95
96 // set AD bit: we want to trust this server
97 dnsBuffer->MessageHead.AuthenticatedData = true;
98
99 QDnsLookupRunnable::ReplyBuffer replyBuffer;
100 if (!self->sendDnsOverTls(reply, { query.data(), qsizetype(dnsBufferSize) }, replyBuffer))
101 return DNS_STATUS(-1); // error set in reply
102
103 // interpret the RCODE in the reply
104 auto response = reinterpret_cast<PDNS_MESSAGE_BUFFER>(replyBuffer.data());
105 DNS_HEADER *header = &response->MessageHead;
106 if (!header->IsResponse)
107 return DNS_ERROR_BAD_PACKET; // not a reply
108
109 // Convert the byte order for the 16-bit quantities in the header, so
110 // DnsExtractRecordsFromMessage can parse the contents.
111 //header->Xid = qFromBigEndian(header->Xid);
112 header->QuestionCount = qFromBigEndian(header->QuestionCount);
113 header->AnswerCount = qFromBigEndian(header->AnswerCount);
114 header->NameServerCount = qFromBigEndian(header->NameServerCount);
115 header->AdditionalCount = qFromBigEndian(header->AdditionalCount);
116
117 results->QueryOptions = request->QueryOptions;
118 return DnsExtractRecordsFromMessage_W(response, replyBuffer.size(), &results->pQueryRecords);
119}
120
121void QDnsLookupRunnable::query(QDnsLookupReply *reply)
122{
123 // Perform DNS query.
124 alignas(DNS_ADDR_ARRAY) uchar dnsAddresses[sizeof(DNS_ADDR_ARRAY) + sizeof(DNS_ADDR)];
125 DNS_QUERY_REQUEST request = {};
126 request.Version = 1;
127 request.QueryName = reinterpret_cast<const wchar_t *>(requestName.constData());
128 request.QueryType = requestType;
129 request.QueryOptions = DNS_QUERY_STANDARD | DNS_QUERY_TREAT_AS_FQDN;
130
131 if (protocol == QDnsLookup::Standard && !nameserver.isNull()) {
132 memset(dnsAddresses, 0, sizeof(dnsAddresses));
133 request.pDnsServerList = new (dnsAddresses) DNS_ADDR_ARRAY;
134 auto addr = new (request.pDnsServerList->AddrArray) DNS_ADDR[1];
135 auto sa = new (addr[0].MaxSa) sockaddr;
136 request.pDnsServerList->MaxCount = sizeof(dnsAddresses);
137 request.pDnsServerList->AddrCount = 1;
138 // ### setting port 53 seems to cause some systems to fail
139 setSockaddr(sa, nameserver, port == DnsPort ? 0 : port);
140 request.pDnsServerList->Family = sa->sa_family;
141 }
142
143 DNS_QUERY_RESULT results = {};
144 results.Version = 1;
145 DNS_STATUS status = ERROR_INVALID_PARAMETER;
146 switch (protocol) {
147 case QDnsLookup::Standard:
148 status = DnsQueryEx(&request, &results, nullptr);
149 break;
150 case QDnsLookup::DnsOverTls:
151 status = sendAlternate(this, reply, &request, &results);
152 break;
153 }
154
155 if (status == DNS_STATUS(-1))
156 return; // error already set in reply
157 if (status >= DNS_ERROR_RCODE_FORMAT_ERROR && status <= DNS_ERROR_RCODE_LAST)
158 return reply->makeDnsRcodeError(status - DNS_ERROR_RCODE_FORMAT_ERROR + 1);
159 else if (status == ERROR_TIMEOUT)
160 return reply->makeTimeoutError();
161 else if (status != ERROR_SUCCESS)
162 return reply->makeResolverSystemError(status);
163
164 QStringView lastEncodedName;
165 QString cachedDecodedName;
166 auto extractAndCacheHost = [&](QStringView name) -> const QString & {
167 lastEncodedName = name;
168 cachedDecodedName = decodeLabel(name);
169 return cachedDecodedName;
170 };
171 auto extractMaybeCachedHost = [&](QStringView name) -> const QString & {
172 return lastEncodedName == name ? cachedDecodedName : extractAndCacheHost(name);
173 };
174
175 // Extract results.
176 for (PDNS_RECORD ptr = results.pQueryRecords; ptr != NULL; ptr = ptr->pNext) {
177 // warning: always assign name to the record before calling extractXxxHost() again
178 const QString &name = extractMaybeCachedHost(ptr->pName);
179 if (ptr->wType == QDnsLookup::A) {
180 QDnsHostAddressRecord record;
181 record.d->name = name;
182 record.d->timeToLive = ptr->dwTtl;
183 record.d->value = QHostAddress(ntohl(ptr->Data.A.IpAddress));
184 reply->hostAddressRecords.append(record);
185 } else if (ptr->wType == QDnsLookup::AAAA) {
186 Q_IPV6ADDR addr;
187 memcpy(&addr, &ptr->Data.AAAA.Ip6Address, sizeof(Q_IPV6ADDR));
188
189 QDnsHostAddressRecord record;
190 record.d->name = name;
191 record.d->timeToLive = ptr->dwTtl;
192 record.d->value = QHostAddress(addr);
193 reply->hostAddressRecords.append(record);
194 } else if (ptr->wType == QDnsLookup::CNAME) {
195 QDnsDomainNameRecord record;
196 record.d->name = name;
197 record.d->timeToLive = ptr->dwTtl;
198 record.d->value = extractAndCacheHost(ptr->Data.Cname.pNameHost);
199 reply->canonicalNameRecords.append(record);
200 } else if (ptr->wType == QDnsLookup::MX) {
201 QDnsMailExchangeRecord record;
202 record.d->name = name;
203 record.d->exchange = decodeLabel(QStringView(ptr->Data.Mx.pNameExchange));
204 record.d->preference = ptr->Data.Mx.wPreference;
205 record.d->timeToLive = ptr->dwTtl;
206 reply->mailExchangeRecords.append(record);
207 } else if (ptr->wType == QDnsLookup::NS) {
208 QDnsDomainNameRecord record;
209 record.d->name = name;
210 record.d->timeToLive = ptr->dwTtl;
211 record.d->value = decodeLabel(QStringView(ptr->Data.Ns.pNameHost));
212 reply->nameServerRecords.append(record);
213 } else if (ptr->wType == QDnsLookup::PTR) {
214 QDnsDomainNameRecord record;
215 record.d->name = name;
216 record.d->timeToLive = ptr->dwTtl;
217 record.d->value = decodeLabel(QStringView(ptr->Data.Ptr.pNameHost));
218 reply->pointerRecords.append(record);
219 } else if (ptr->wType == QDnsLookup::SRV) {
220 QDnsServiceRecord record;
221 record.d->name = name;
222 record.d->target = decodeLabel(QStringView(ptr->Data.Srv.pNameTarget));
223 record.d->port = ptr->Data.Srv.wPort;
224 record.d->priority = ptr->Data.Srv.wPriority;
225 record.d->timeToLive = ptr->dwTtl;
226 record.d->weight = ptr->Data.Srv.wWeight;
227 reply->serviceRecords.append(record);
228 } else if (ptr->wType == QDnsLookup::TLSA) {
229 // Note: untested, because the DNS_RECORD reply appears to contain
230 // no records relating to TLSA. Maybe WinDNS filters them out of
231 // zones without DNSSEC.
232 QDnsTlsAssociationRecord record;
233 record.d->name = name;
234 record.d->timeToLive = ptr->dwTtl;
235
236 const auto &tlsa = ptr->Data.Tlsa;
237 const quint8 usage = tlsa.bCertUsage;
238 const quint8 selector = tlsa.bSelector;
239 const quint8 matchType = tlsa.bMatchingType;
240
241 record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage);
242 record.d->selector = QDnsTlsAssociationRecord::Selector(selector);
243 record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType);
244 record.d->value.assign(tlsa.bCertificateAssociationData,
245 tlsa.bCertificateAssociationData + tlsa.bCertificateAssociationDataLength);
246 reply->tlsAssociationRecords.append(std::move(record));
247 } else if (ptr->wType == QDnsLookup::TXT) {
248 QDnsTextRecord record;
249 record.d->name = name;
250 record.d->timeToLive = ptr->dwTtl;
251 for (unsigned int i = 0; i < ptr->Data.Txt.dwStringCount; ++i) {
252 record.d->values << QStringView(ptr->Data.Txt.pStringArray[i]).toLatin1();
253 }
254 reply->textRecords.append(record);
255 }
256 }
257
258 DnsRecordListFree(results.pQueryRecords, DnsFreeRecordList);
259}
260
261QT_END_NAMESPACE
void makeTimeoutError()
void * PDNS_QUERY_CANCEL
DNS_QUERY_COMPLETION_ROUTINE * PDNS_QUERY_COMPLETION_ROUTINE
PDNS_ADDR_ARRAY pDnsServerList
PDNS_QUERY_COMPLETION_ROUTINE pQueryCompletionCallback