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_unix.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// Copyright (C) 2026 Linus Jahn <lnj@kaidan.im>
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5// Qt-Security score:critical reason:data-parser
6
7#include "qdnslookup_p.h"
8
9#include <qendian.h>
10#include <qrandom.h>
11#include <qspan.h>
12#include <qurl.h>
13#include <qvarlengtharray.h>
14#include <private/qnativesocketengine_p.h> // for setSockAddr
15#include <private/qtnetwork-config_p.h>
16
17// Three backends: libresolv on Unix, Android's DnsResolver (bionic has no
18// libresolv) and HarmonyOS' stateless resolver (musl has no res_n* functions).
19// They share everything but the transport.
20#if !QT_CONFIG(libresolv) && !QT_CONFIG(android_dnsresolver)
21 && !QT_CONFIG(harmony_dnsresolver)
22# error "This file requires libresolv, Android's DnsResolver or the HarmonyOS stateless resolver"
23#endif
24
25#include <sys/types.h>
26#include <netinet/in.h>
27#include <arpa/nameser.h>
28#if __has_include(<arpa/nameser_compat.h>)
29# include <arpa/nameser_compat.h>
30#endif
31#include <errno.h>
32#include <resolv.h>
33
34#if QT_CONFIG(android_dnsresolver)
35# include <android/multinetwork.h>
36#endif
37
38#include <array>
39
40#ifndef T_OPT
41// the older arpa/nameser_compat.h wasn't updated between 1999 and 2016 in glibc
42# define T_OPT ns_t_opt
43#endif
44
45QT_BEGIN_NAMESPACE
46
47using namespace Qt::StringLiterals;
48using ReplyBuffer = QDnsLookupRunnable::ReplyBuffer;
49
50// https://www.rfc-editor.org/rfc/rfc6891
51static constexpr unsigned char Edns0Record[] = {
52 0x00, // root label
53 T_OPT >> 8, T_OPT & 0xff, // type OPT
54 ReplyBuffer::PreallocatedSize >> 8, ReplyBuffer::PreallocatedSize & 0xff, // payload size
55 NOERROR, // extended rcode
56 0, // version
57 0x00, 0x00, // flags
58 0x00, 0x00, // option length
59};
60
61// maximum length of a EDNS0 query with a 255-character domain (rounded up to 16)
62static constexpr qsizetype QueryBufferSize =
63 HFIXEDSZ + QFIXEDSZ + MAXCDNAME + 1 + sizeof(Edns0Record);
64using QueryBuffer = std::array<unsigned char, (QueryBufferSize + 15) / 16 * 16>;
65
66namespace {
67struct QDnsCachedName
68{
69 QString name;
70 int code = 0;
71 QDnsCachedName(const QString &name, int code) : name(name), code(code) {}
72};
73}
75using Cache = QList<QDnsCachedName>; // QHash or QMap are overkill
76
77#if QT_CONFIG(libresolv)
78#if QT_CONFIG(res_setservers)
79// https://www.ibm.com/docs/en/i/7.3?topic=ssw_ibm_i_73/apis/ressetservers.html
80// https://docs.oracle.com/cd/E86824_01/html/E54774/res-setservers-3resolv.html
81static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
82{
83 union res_sockaddr_union u;
84 setSockaddr(reinterpret_cast<sockaddr *>(&u.sin), nameserver, port);
85 res_setservers(state, &u, 1);
86 return true;
87}
88#else
89template <typename T> void setNsMap(T &ext, std::enable_if_t<sizeof(T::nsmap) != 0, uint16_t> v)
90{
91 // Set nsmap[] to indicate that nsaddrs[0] is an IPv6 address
92 // See: https://sourceware.org/ml/libc-hacker/2002-05/msg00035.html
93 // Unneeded since glibc 2.22 (2015), but doesn't hurt to set it
94 // See: https://sourceware.org/git/?p=glibc.git;a=commit;h=2212c1420c92a33b0e0bd9a34938c9814a56c0f7
95 ext.nsmap[0] = v;
96}
97template <typename T> void setNsMap(T &, ...)
98{
99 // fallback
100}
101
102template <bool Condition>
103using EnableIfIPv6 = std::enable_if_t<Condition, const QHostAddress *>;
104
105template <typename State>
106bool setIpv6NameServer(State *state,
107 EnableIfIPv6<sizeof(std::declval<State>()._u._ext.nsaddrs) != 0> addr,
108 quint16 port)
109{
110 // glibc-like API to set IPv6 name servers
111 struct sockaddr_in6 *ns = state->_u._ext.nsaddrs[0];
112
113 // nsaddrs will be NULL if no nameserver is set in /etc/resolv.conf
114 if (!ns) {
115 // Memory allocated here will be free()'d in res_close() as we
116 // have done res_init() above.
117 ns = static_cast<struct sockaddr_in6*>(calloc(1, sizeof(struct sockaddr_in6)));
118 Q_CHECK_PTR(ns);
119 state->_u._ext.nsaddrs[0] = ns;
120 }
121
122 setNsMap(state->_u._ext, MAXNS + 1);
123 state->_u._ext.nscount6 = 1;
124 setSockaddr(ns, *addr, port);
125 return true;
126}
127
128template <typename State> bool setIpv6NameServer(State *, const void *, quint16)
129{
130 // fallback
131 return false;
132}
133
134static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
135{
136 state->nscount = 1;
137 state->nsaddr_list[0].sin_family = AF_UNSPEC;
138 if (nameserver.protocol() == QAbstractSocket::IPv6Protocol)
139 return setIpv6NameServer(state, &nameserver, port);
140 setSockaddr(&state->nsaddr_list[0], nameserver, port);
141 return true;
142}
143#endif // !QT_CONFIG(res_setservers)
144
145static int
146prepareQueryBuffer(res_state state, QueryBuffer &buffer, const char *label, ns_rcode type)
147{
148 // Create header and our query
149 int queryLength = res_nmkquery(state, QUERY, label, C_IN, type, nullptr, 0, nullptr,
150 buffer.data(), buffer.size());
151 Q_ASSERT(queryLength < int(buffer.size()));
152 if (Q_UNLIKELY(queryLength < 0))
153 return queryLength;
154
155 // Append EDNS0 record and set the number of additional RRs to 1
156 Q_ASSERT(queryLength + sizeof(Edns0Record) < buffer.size());
157 std::copy_n(std::begin(Edns0Record), sizeof(Edns0Record), buffer.begin() + queryLength);
158 reinterpret_cast<HEADER *>(buffer.data())->arcount = qToBigEndian<quint16>(1);
159
160 return queryLength + sizeof(Edns0Record);
161}
162
163static int sendStandardDns(QDnsLookupReply *reply, res_state state, QSpan<unsigned char> qbuffer,
164 ReplyBuffer &buffer, const QHostAddress &nameserver, quint16 port)
165{
166 // Check if a nameserver was set. If so, use it.
167 if (!nameserver.isNull()) {
168 if (!applyNameServer(state, nameserver, port)) {
169 reply->setError(QDnsLookup::ResolverError,
170 QDnsLookup::tr("IPv6 nameservers are currently not supported on this OS"));
171 return -1;
172 }
173
174 // Request the name server attempt to authenticate the reply.
175 reinterpret_cast<HEADER *>(buffer.data())->ad = true;
176
177#ifdef RES_TRUSTAD
178 // Need to set this option even though we set the AD bit, otherwise
179 // glibc turns it off.
180 state->options |= RES_TRUSTAD;
181#endif
182 }
183
184 auto attemptToSend = [&]() {
185 std::memset(buffer.data(), 0, HFIXEDSZ); // the header is enough
186 int responseLength = res_nsend(state, qbuffer.data(), qbuffer.size(), buffer.data(), buffer.size());
187 if (responseLength >= 0)
188 return responseLength; // success
189
190 // libresolv uses ETIMEDOUT for resolver errors ("no answer")
191 if (errno == ECONNREFUSED)
192 reply->setError(QDnsLookup::ServerRefusedError, qt_error_string());
193 else if (errno != ETIMEDOUT)
194 reply->makeResolverSystemError(); // some other error
195
196 auto query = reinterpret_cast<HEADER *>(qbuffer.data());
197 auto header = reinterpret_cast<HEADER *>(buffer.data());
198 if (query->id == header->id && header->qr)
199 reply->makeDnsRcodeError(header->rcode);
200 else
201 reply->makeTimeoutError(); // must really be a timeout
202 return -1;
203 };
204
205 // strictly use UDP, we'll deal with truncated replies ourselves
206 state->options |= RES_IGNTC;
207 int responseLength = attemptToSend();
208 if (responseLength < 0)
209 return responseLength;
210
211 // check if we need to use the virtual circuit (TCP)
212 auto header = reinterpret_cast<HEADER *>(buffer.data());
213 if (header->rcode == NOERROR && header->tc) {
214 // yes, increase our buffer size
215 buffer.resize(std::numeric_limits<quint16>::max());
216 header = reinterpret_cast<HEADER *>(buffer.data());
217
218 // remove the EDNS record in the query
219 reinterpret_cast<HEADER *>(qbuffer.data())->arcount = 0;
220 qbuffer = qbuffer.first(qbuffer.size() - sizeof(Edns0Record));
221
222 // send using the virtual circuit
223 state->options |= RES_USEVC;
224 responseLength = attemptToSend();
225 if (Q_UNLIKELY(responseLength > buffer.size())) {
226 // Ok, we give up.
227 reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr("Reply was too large"));
228 return -1;
229 }
230 }
231
232 // We only trust the AD bit in the reply if we're querying a custom name
233 // server or if we can tell the system administrator configured the resolver
234 // to trust replies.
235#ifndef RES_TRUSTAD
236 if (nameserver.isNull())
237 header->ad = false;
238#endif
239 reply->authenticData = header->ad;
240
241 return responseLength;
242}
243
244void QDnsLookupRunnable::query(QDnsLookupReply *reply)
245{
246 // Initialize state.
247 std::remove_pointer_t<res_state> state = {};
248 if (res_ninit(&state) < 0) {
249 int error = errno;
250 qErrnoWarning(error, "QDnsLookup: Resolver initialization failed");
251 return reply->makeResolverSystemError(error);
252 }
253 auto guard = qScopeGuard([&] { res_nclose(&state); });
254
255#ifdef QDNSLOOKUP_DEBUG
256 state.options |= RES_DEBUG;
257#endif
258
259 // Prepare the DNS query.
260 QueryBuffer qbuffer;
261 int queryLength = prepareQueryBuffer(&state, qbuffer, requestName.constData(), ns_rcode(requestType));
262 if (Q_UNLIKELY(queryLength < 0))
263 return reply->makeResolverSystemError();
264
265 // Perform DNS query.
266 QSpan query(qbuffer.data(), queryLength);
267 ReplyBuffer buffer(ReplyBufferSize);
268 int responseLength = -1;
269 switch (protocol) {
270 case QDnsLookup::Standard:
271 responseLength = sendStandardDns(reply, &state, query, buffer, nameserver, port);
272 break;
273 case QDnsLookup::DnsOverTls:
274 if (!sendDnsOverTls(reply, query, buffer))
275 return;
276 responseLength = buffer.size();
277 break;
278 }
279
280 if (responseLength < 0)
281 return;
282
283 parseResponse(reply, buffer, responseLength);
284}
285
286#elif QT_CONFIG(android_dnsresolver) || QT_CONFIG(harmony_dnsresolver)
287
288// Neither bionic nor musl exports res_nmkquery(), so we assemble the query
289// ourselves.
290// See https://www.rfc-editor.org/rfc/rfc1035#section-4.1.
291static int prepareQueryBuffer(QueryBuffer &buffer, const char *label, ns_type type)
292{
293 std::memset(buffer.data(), 0, HFIXEDSZ);
294 auto header = new (buffer.data()) HEADER;
295 header->id = QRandomGenerator::system()->generate();
296 header->opcode = QUERY;
297 header->rd = true; // recursion desired
298 header->qdcount = qToBigEndian<quint16>(1);
299 header->arcount = qToBigEndian<quint16>(1); // EDNS0 record
300
301 // the question: QNAME, QTYPE, QCLASS; a single-question query has nothing
302 // to compress against, so dn_comp() gets no compression pointers
303 unsigned char *ptr = buffer.data() + HFIXEDSZ;
304 int labelLength = dn_comp(label, ptr,
305 int(buffer.size() - HFIXEDSZ - QFIXEDSZ - sizeof(Edns0Record)),
306 nullptr, nullptr);
307 if (Q_UNLIKELY(labelLength < 0))
308 return labelLength;
309 ptr += labelLength;
310 qToBigEndian<quint16>(type, ptr);
311 qToBigEndian<quint16>(C_IN, ptr + sizeof(quint16));
312 ptr += QFIXEDSZ;
313
314 Q_ASSERT(ptr + sizeof(Edns0Record) <= buffer.data() + buffer.size());
315 ptr = std::copy_n(std::begin(Edns0Record), sizeof(Edns0Record), ptr);
316
317 return ptr - buffer.data();
318}
319
320#if QT_CONFIG(android_dnsresolver)
321// These are API level 29, while Qt still supports 28, where the NDK headers
322// mark them unavailable; weakrefs are resolved to null if they are missing.
323static int local_res_nsend(net_handle_t network, const uint8_t *msg, size_t msglen, uint32_t flags)
324__attribute__((weakref("android_res_nsend")));
325
326static int local_res_nresult(int fd, int *rcode, uint8_t *answer, size_t anslen)
327__attribute__((weakref("android_res_nresult")));
328
329bool qt_androidDnsResolverAvailable()
330{
331 return local_res_nsend && local_res_nresult;
332}
333
334static int sendStandardDns(QDnsLookupReply *reply, QSpan<unsigned char> qbuffer,
335 ReplyBuffer &buffer)
336{
337 if (!qt_androidDnsResolverAvailable()) {
338 reply->setError(QDnsLookup::ResolverError,
339 QDnsLookup::tr("DNS lookups require Android 10 or later"));
340 return -1;
341 }
342
343 // unused: the rcode is also in the reply header, where parseResponse() finds it
344 int rcode = 0;
345 auto attemptToSend = [&]() {
346 int fd = local_res_nsend(NETWORK_UNSPECIFIED, qbuffer.data(), qbuffer.size(), 0);
347 if (fd < 0)
348 return fd;
349 // android_res_nresult() closes the file descriptor in all cases
350 return local_res_nresult(fd, &rcode, buffer.data(), buffer.size());
351 };
352
353 int responseLength = attemptToSend();
354 if (responseLength == -EMSGSIZE) {
355 // The reply did not fit (was truncated) and the resolver closed the
356 // socket, so ask again with a buffer that any DNS message fits into.
357 // The repeated query will normally be answered from the resolver's cache.
358 buffer.resize(std::numeric_limits<quint16>::max());
359 responseLength = attemptToSend();
360 }
361 if (responseLength < 0) {
362 if (responseLength == -ETIMEDOUT)
363 reply->makeTimeoutError();
364 else
365 reply->makeResolverSystemError(-responseLength);
366 return -1;
367 }
368
369 return responseLength;
370}
371
372#endif // QT_CONFIG(android_dnsresolver)
373
374#if QT_CONFIG(harmony_dnsresolver)
375
376static int sendStandardDns(QDnsLookupReply *reply, QSpan<unsigned char> qbuffer,
377 ReplyBuffer &buffer)
378{
379 auto attemptToSend = [&]() {
380 std::memset(buffer.data(), 0, HFIXEDSZ);
381 int responseLength = res_send(qbuffer.data(), qbuffer.size(), buffer.data(), buffer.size());
382 if (responseLength >= 0)
383 return responseLength;
384
385 if (errno == ECONNREFUSED)
386 reply->setError(QDnsLookup::ServerRefusedError, qt_error_string());
387 else if (errno != EAGAIN && errno != ETIMEDOUT)
388 reply->makeResolverSystemError();
389
390 auto query = reinterpret_cast<const HEADER *>(qbuffer.data());
391 auto header = reinterpret_cast<const HEADER *>(buffer.data());
392 if (query->id == header->id && header->qr)
393 reply->makeDnsRcodeError(header->rcode);
394 else
395 reply->makeTimeoutError();
396 return -1;
397 };
398
399 int responseLength = attemptToSend();
400 if (responseLength > buffer.size()) {
401 buffer.resize(std::numeric_limits<quint16>::max());
402 responseLength = attemptToSend();
403 if (Q_UNLIKELY(responseLength > buffer.size())) {
404 reply->setError(QDnsLookup::ResolverError, QDnsLookup::tr("Reply was too large"));
405 return -1;
406 }
407 }
408
409 return responseLength;
410}
411
412#endif // QT_CONFIG(harmony_dnsresolver)
413
414void QDnsLookupRunnable::query(QDnsLookupReply *reply)
415{
416 // Prepare the DNS query.
417 QueryBuffer qbuffer;
418 int queryLength = prepareQueryBuffer(qbuffer, requestName.constData(), ns_type(requestType));
419 if (Q_UNLIKELY(queryLength < 0))
420 return reply->setError(QDnsLookup::InvalidRequestError,
421 QDnsLookup::tr("Invalid domain name"));
422
423 // Perform DNS query.
424 QSpan query(qbuffer.data(), queryLength);
425 ReplyBuffer buffer(ReplyBufferSize);
426 int responseLength = -1;
427 switch (protocol) {
428 case QDnsLookup::Standard:
429 if (!nameserver.isNull()) {
430 reply->setError(QDnsLookup::ResolverError,
431 QDnsLookup::tr("Setting a nameserver is currently not supported on this OS"));
432 return;
433 }
434 responseLength = sendStandardDns(reply, query, buffer);
435
436 // We can't tell whether the system resolver validated the reply, so don't
437 // pass on the AD bit (see the RES_TRUSTAD handling above).
438 if (responseLength >= int(sizeof(HEADER)))
439 reinterpret_cast<HEADER *>(buffer.data())->ad = false;
440 break;
441 case QDnsLookup::DnsOverTls:
442 if (!sendDnsOverTls(reply, query, buffer))
443 return;
444 responseLength = buffer.size();
445 break;
446 }
447
448 if (responseLength < 0)
449 return;
450
451 parseResponse(reply, buffer, responseLength);
452}
453
454#endif // resolver backend selection
455
456void QDnsLookupRunnable::parseResponse(QDnsLookupReply *reply, ReplyBuffer &buffer,
457 int responseLength)
458{
459 // Check the reply is valid.
460 if (responseLength < int(sizeof(HEADER)))
461 return reply->makeInvalidReplyError();
462
463 // Parse the reply.
464 auto header = reinterpret_cast<HEADER *>(buffer.data());
465 if (header->rcode)
466 return reply->makeDnsRcodeError(header->rcode);
467
468 qptrdiff offset = sizeof(HEADER);
469 unsigned char *response = buffer.data();
470 int status;
471
472 auto expandHost = [&, cache = Cache{}](qptrdiff offset) mutable {
473 if (uchar n = response[offset]; n & NS_CMPRSFLGS) {
474 // compressed name, see if we already have it cached
475 if (offset + 1 < responseLength) {
476 int id = ((n & ~NS_CMPRSFLGS) << 8) | response[offset + 1];
477 auto it = std::find_if(cache.constBegin(), cache.constEnd(),
478 [id](const QDnsCachedName &n) { return n.code == id; });
479 if (it != cache.constEnd()) {
480 status = 2;
481 return it->name;
482 }
483 }
484 }
485
486 // uncached, expand it
487 char host[MAXCDNAME + 1];
488 status = dn_expand(response, response + responseLength, response + offset,
489 host, sizeof(host));
490 if (status >= 0)
491 return cache.emplaceBack(decodeLabel(QLatin1StringView(host)), offset).name;
492
493 // failed
494 reply->makeInvalidReplyError(QDnsLookup::tr("Could not expand domain name"));
495 return QString();
496 };
497
498 if (ntohs(header->qdcount) == 1) {
499 // Skip the query host, type (2 bytes) and class (2 bytes).
500 expandHost(offset);
501 if (status < 0)
502 return;
503 if (offset + status + 4 > responseLength)
504 header->qdcount = 0xffff; // invalid reply below
505 else
506 offset += status + 4;
507 }
508 if (ntohs(header->qdcount) > 1)
509 return reply->makeInvalidReplyError();
510
511 // Extract results.
512 const int answerCount = ntohs(header->ancount);
513 int answerIndex = 0;
514 while ((offset < responseLength) && (answerIndex < answerCount)) {
515 const QString name = expandHost(offset);
516 if (status < 0)
517 return;
518
519 offset += status;
520 if (offset + RRFIXEDSZ > responseLength) {
521 // probably just a truncated reply, return what we have
522 return;
523 }
524 const quint16 type = qFromBigEndian<quint16>(response + offset);
525 const qint16 rrclass = qFromBigEndian<quint16>(response + offset + 2);
526 const quint32 ttl = qFromBigEndian<quint32>(response + offset + 4);
527 const quint16 size = qFromBigEndian<quint16>(response + offset + 8);
528 offset += RRFIXEDSZ;
529 if (offset + size > responseLength)
530 return; // truncated
531 if (rrclass != C_IN)
532 continue;
533
534 if (type == QDnsLookup::A) {
535 if (size != 4)
536 return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid IPv4 address record"));
537 const quint32 addr = qFromBigEndian<quint32>(response + offset);
538 QDnsHostAddressRecord record;
539 record.d->name = name;
540 record.d->timeToLive = ttl;
541 record.d->value = QHostAddress(addr);
542 reply->hostAddressRecords.append(record);
543 } else if (type == QDnsLookup::AAAA) {
544 if (size != 16)
545 return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid IPv6 address record"));
546 QDnsHostAddressRecord record;
547 record.d->name = name;
548 record.d->timeToLive = ttl;
549 record.d->value = QHostAddress(response + offset);
550 reply->hostAddressRecords.append(record);
551 } else if (type == QDnsLookup::CNAME) {
552 QDnsDomainNameRecord record;
553 record.d->name = name;
554 record.d->timeToLive = ttl;
555 record.d->value = expandHost(offset);
556 if (status < 0)
557 return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid canonical name record"));
558 reply->canonicalNameRecords.append(record);
559 } else if (type == QDnsLookup::NS) {
560 QDnsDomainNameRecord record;
561 record.d->name = name;
562 record.d->timeToLive = ttl;
563 record.d->value = expandHost(offset);
564 if (status < 0)
565 return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid name server record"));
566 reply->nameServerRecords.append(record);
567 } else if (type == QDnsLookup::PTR) {
568 QDnsDomainNameRecord record;
569 record.d->name = name;
570 record.d->timeToLive = ttl;
571 record.d->value = expandHost(offset);
572 if (status < 0)
573 return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid pointer record"));
574 reply->pointerRecords.append(record);
575 } else if (type == QDnsLookup::MX) {
576 const quint16 preference = qFromBigEndian<quint16>(response + offset);
577 QDnsMailExchangeRecord record;
578 record.d->exchange = expandHost(offset + 2);
579 record.d->name = name;
580 record.d->preference = preference;
581 record.d->timeToLive = ttl;
582 if (status < 0)
583 return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid mail exchange record"));
584 reply->mailExchangeRecords.append(record);
585 } else if (type == QDnsLookup::SRV) {
586 if (size < 7)
587 return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
588 const quint16 priority = qFromBigEndian<quint16>(response + offset);
589 const quint16 weight = qFromBigEndian<quint16>(response + offset + 2);
590 const quint16 port = qFromBigEndian<quint16>(response + offset + 4);
591 QDnsServiceRecord record;
592 record.d->name = name;
593 record.d->target = expandHost(offset + 6);
594 record.d->port = port;
595 record.d->priority = priority;
596 record.d->timeToLive = ttl;
597 record.d->weight = weight;
598 if (status < 0)
599 return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
600 reply->serviceRecords.append(record);
601 } else if (type == QDnsLookup::TLSA) {
602 // https://datatracker.ietf.org/doc/html/rfc6698#section-2.1
603 if (size < 3)
604 return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid TLS association record"));
605
606 const quint8 usage = response[offset];
607 const quint8 selector = response[offset + 1];
608 const quint8 matchType = response[offset + 2];
609
610 QDnsTlsAssociationRecord record;
611 record.d->name = name;
612 record.d->timeToLive = ttl;
613 record.d->usage = QDnsTlsAssociationRecord::CertificateUsage(usage);
614 record.d->selector = QDnsTlsAssociationRecord::Selector(selector);
615 record.d->matchType = QDnsTlsAssociationRecord::MatchingType(matchType);
616 record.d->value.assign(response + offset + 3, response + offset + size);
617 reply->tlsAssociationRecords.append(std::move(record));
618 } else if (type == QDnsLookup::TXT) {
619 QDnsTextRecord record;
620 record.d->name = name;
621 record.d->timeToLive = ttl;
622 qptrdiff txt = offset;
623 while (txt < offset + size) {
624 const unsigned char length = response[txt];
625 txt++;
626 if (txt + length > offset + size)
627 return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid text record"));
628 record.d->values << QByteArrayView(response + txt, length).toByteArray();
629 txt += length;
630 }
631 reply->textRecords.append(record);
632 }
633 offset += size;
634 answerIndex++;
635 }
636}
637
638QT_END_NAMESPACE
#define __has_include(x)
static constexpr unsigned char Edns0Record[]
#define T_OPT
Q_DECLARE_TYPEINFO(QDnsCachedName, Q_RELOCATABLE_TYPE)
static constexpr qsizetype QueryBufferSize