Bug 1890277: part 4) Add CSPParser support for the `trusted-types` directive, guarded...
[gecko.git] / netwerk / dns / DNSPacket.cpp
blob6aef801d63c01d653c516b2be4d1b63b93ceb4d9
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "DNSPacket.h"
7 #include "DNS.h"
8 #include "mozilla/EndianUtils.h"
9 #include "mozilla/ScopeExit.h"
10 #include "mozilla/StaticPrefs_network.h"
11 // Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
12 #include "DNSLogging.h"
14 #include "nsIInputStream.h"
16 namespace mozilla {
17 namespace net {
19 static uint16_t get16bit(const unsigned char* aData, unsigned int index) {
20 return ((aData[index] << 8) | aData[index + 1]);
23 static uint32_t get32bit(const unsigned char* aData, unsigned int index) {
24 return (aData[index] << 24) | (aData[index + 1] << 16) |
25 (aData[index + 2] << 8) | aData[index + 3];
28 // https://datatracker.ietf.org/doc/html/rfc8914#name-defined-extended-dns-errors
29 // This is a list of errors for which we should not fallback to Do53.
30 // These are normally explicit filtering performed by the recursive resolver.
31 bool hardFail(uint16_t code) {
32 const uint16_t noFallbackErrors[] = {
33 4, // Forged answer (malware filtering)
34 17, // Filtered
37 for (const auto& err : noFallbackErrors) {
38 if (code == err) {
39 return true;
42 return false;
45 nsresult DNSPacket::FillBuffer(
46 std::function<int(unsigned char response[MAX_SIZE])>&& aPredicate) {
47 int response_length = aPredicate(mResponse);
48 if (response_length < 0) {
49 LOG(("FillBuffer response len < 0"));
50 mBodySize = 0;
51 mStatus = NS_ERROR_UNEXPECTED;
52 return mStatus;
55 mBodySize = response_length;
56 return NS_OK;
58 // static
59 nsresult DNSPacket::ParseSvcParam(unsigned int svcbIndex, uint16_t key,
60 SvcFieldValue& field, uint16_t length,
61 const unsigned char* aBuffer) {
62 switch (key) {
63 case SvcParamKeyMandatory: {
64 if (length % 2 != 0) {
65 // This key should encode a list of uint16_t
66 return NS_ERROR_UNEXPECTED;
68 while (length > 0) {
69 uint16_t mandatoryKey = get16bit(aBuffer, svcbIndex);
70 length -= 2;
71 svcbIndex += 2;
73 if (!IsValidSvcParamKey(mandatoryKey)) {
74 LOG(("The mandatory field includes a key we don't support %u",
75 mandatoryKey));
76 return NS_ERROR_UNEXPECTED;
79 break;
81 case SvcParamKeyAlpn: {
82 field.mValue = AsVariant(SvcParamAlpn());
83 auto& alpnArray = field.mValue.as<SvcParamAlpn>().mValue;
84 while (length > 0) {
85 uint8_t alpnIdLength = aBuffer[svcbIndex++];
86 length -= 1;
87 if (alpnIdLength > length) {
88 return NS_ERROR_UNEXPECTED;
91 alpnArray.AppendElement(
92 nsCString((const char*)&aBuffer[svcbIndex], alpnIdLength));
93 length -= alpnIdLength;
94 svcbIndex += alpnIdLength;
96 break;
98 case SvcParamKeyNoDefaultAlpn: {
99 if (length != 0) {
100 // This key should not contain a value
101 return NS_ERROR_UNEXPECTED;
103 field.mValue = AsVariant(SvcParamNoDefaultAlpn{});
104 break;
106 case SvcParamKeyPort: {
107 if (length != 2) {
108 // This key should only encode a uint16_t
109 return NS_ERROR_UNEXPECTED;
111 field.mValue =
112 AsVariant(SvcParamPort{.mValue = get16bit(aBuffer, svcbIndex)});
113 break;
115 case SvcParamKeyIpv4Hint: {
116 if (length % 4 != 0) {
117 // This key should only encode IPv4 addresses
118 return NS_ERROR_UNEXPECTED;
121 field.mValue = AsVariant(SvcParamIpv4Hint());
122 auto& ipv4array = field.mValue.as<SvcParamIpv4Hint>().mValue;
123 while (length > 0) {
124 NetAddr addr;
125 addr.inet.family = AF_INET;
126 addr.inet.port = 0;
127 addr.inet.ip = ntohl(get32bit(aBuffer, svcbIndex));
128 ipv4array.AppendElement(addr);
129 length -= 4;
130 svcbIndex += 4;
132 break;
134 case SvcParamKeyEchConfig: {
135 field.mValue = AsVariant(SvcParamEchConfig{
136 .mValue = nsCString((const char*)(&aBuffer[svcbIndex]), length)});
137 break;
139 case SvcParamKeyIpv6Hint: {
140 if (length % 16 != 0) {
141 // This key should only encode IPv6 addresses
142 return NS_ERROR_UNEXPECTED;
145 field.mValue = AsVariant(SvcParamIpv6Hint());
146 auto& ipv6array = field.mValue.as<SvcParamIpv6Hint>().mValue;
147 while (length > 0) {
148 NetAddr addr;
149 addr.inet6.family = AF_INET6;
150 addr.inet6.port = 0; // unknown
151 addr.inet6.flowinfo = 0; // unknown
152 addr.inet6.scope_id = 0; // unknown
153 for (int i = 0; i < 16; i++, svcbIndex++) {
154 addr.inet6.ip.u8[i] = aBuffer[svcbIndex];
156 ipv6array.AppendElement(addr);
157 length -= 16;
158 // no need to increase svcbIndex - we did it in the for above.
160 break;
162 case SvcParamKeyODoHConfig: {
163 field.mValue = AsVariant(SvcParamODoHConfig{
164 .mValue = nsCString((const char*)(&aBuffer[svcbIndex]), length)});
165 break;
167 default: {
168 // Unespected type. We'll just ignore it.
169 return NS_OK;
170 break;
173 return NS_OK;
176 nsresult DNSPacket::PassQName(unsigned int& index,
177 const unsigned char* aBuffer) {
178 uint8_t length;
179 do {
180 if (mBodySize < (index + 1)) {
181 LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index));
182 return NS_ERROR_ILLEGAL_VALUE;
184 length = static_cast<uint8_t>(aBuffer[index]);
185 if ((length & 0xc0) == 0xc0) {
186 // name pointer, advance over it and be done
187 if (mBodySize < (index + 2)) {
188 return NS_ERROR_ILLEGAL_VALUE;
190 index += 2;
191 break;
193 if (length & 0xc0) {
194 LOG(("TRR: illegal label length byte (%x) at index %d\n", length, index));
195 return NS_ERROR_ILLEGAL_VALUE;
197 // pass label
198 if (mBodySize < (index + 1 + length)) {
199 LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index));
200 return NS_ERROR_ILLEGAL_VALUE;
202 index += 1 + length;
203 } while (length);
204 return NS_OK;
207 // GetQname: retrieves the qname (stores in 'aQname') and stores the index
208 // after qname was parsed into the 'aIndex'.
209 // static
210 nsresult DNSPacket::GetQname(nsACString& aQname, unsigned int& aIndex,
211 const unsigned char* aBuffer,
212 unsigned int aBodySize) {
213 uint8_t clength = 0;
214 unsigned int cindex = aIndex;
215 unsigned int loop = 128; // a valid DNS name can never loop this much
216 unsigned int endindex = 0; // index position after this data
217 do {
218 if (cindex >= aBodySize) {
219 LOG(("TRR: bad Qname packet\n"));
220 return NS_ERROR_ILLEGAL_VALUE;
222 clength = static_cast<uint8_t>(aBuffer[cindex]);
223 if ((clength & 0xc0) == 0xc0) {
224 // name pointer, get the new offset (14 bits)
225 if ((cindex + 1) >= aBodySize) {
226 return NS_ERROR_ILLEGAL_VALUE;
228 // extract the new index position for the next label
229 uint16_t newpos = (clength & 0x3f) << 8 | aBuffer[cindex + 1];
230 if (!endindex) {
231 // only update on the first "jump"
232 endindex = cindex + 2;
234 cindex = newpos;
235 continue;
237 if (clength & 0xc0) {
238 // any of those bits set individually is an error
239 LOG(("TRR: bad Qname packet\n"));
240 return NS_ERROR_ILLEGAL_VALUE;
243 cindex++;
245 if (clength) {
246 if (!aQname.IsEmpty()) {
247 aQname.Append(".");
249 if ((cindex + clength) > aBodySize) {
250 return NS_ERROR_ILLEGAL_VALUE;
252 aQname.Append((const char*)(&aBuffer[cindex]), clength);
253 cindex += clength; // skip label
255 } while (clength && --loop);
257 if (!loop) {
258 LOG(("DNSPacket::DohDecode pointer loop error\n"));
259 return NS_ERROR_ILLEGAL_VALUE;
261 if (!endindex) {
262 // there was no "jump"
263 endindex = cindex;
265 aIndex = endindex;
266 return NS_OK;
269 nsresult DOHresp::Add(uint32_t TTL, unsigned char const* dns,
270 unsigned int index, uint16_t len, bool aLocalAllowed) {
271 NetAddr addr;
272 if (4 == len) {
273 // IPv4
274 addr.inet.family = AF_INET;
275 addr.inet.port = 0; // unknown
276 addr.inet.ip = ntohl(get32bit(dns, index));
277 } else if (16 == len) {
278 // IPv6
279 addr.inet6.family = AF_INET6;
280 addr.inet6.port = 0; // unknown
281 addr.inet6.flowinfo = 0; // unknown
282 addr.inet6.scope_id = 0; // unknown
283 for (int i = 0; i < 16; i++, index++) {
284 addr.inet6.ip.u8[i] = dns[index];
286 } else {
287 return NS_ERROR_UNEXPECTED;
290 if (addr.IsIPAddrLocal() && !aLocalAllowed) {
291 return NS_ERROR_FAILURE;
294 // While the DNS packet might return individual TTLs for each address,
295 // we can only return one value in the AddrInfo class so pick the
296 // lowest number.
297 if (mTtl < TTL) {
298 mTtl = TTL;
301 if (LOG_ENABLED()) {
302 char buf[128];
303 addr.ToStringBuffer(buf, sizeof(buf));
304 LOG(("DOHresp:Add %s\n", buf));
306 mAddresses.AppendElement(addr);
307 return NS_OK;
310 nsresult DNSPacket::OnDataAvailable(nsIRequest* aRequest,
311 nsIInputStream* aInputStream,
312 uint64_t aOffset, const uint32_t aCount) {
313 if (aCount + mBodySize > MAX_SIZE) {
314 LOG(("DNSPacket::OnDataAvailable:%d fail\n", __LINE__));
315 return NS_ERROR_FAILURE;
317 uint32_t count;
318 nsresult rv =
319 aInputStream->Read((char*)mResponse + mBodySize, aCount, &count);
320 if (NS_FAILED(rv)) {
321 return rv;
323 MOZ_ASSERT(count == aCount);
324 mBodySize += aCount;
325 return NS_OK;
328 const uint8_t kDNS_CLASS_IN = 1;
330 nsresult DNSPacket::EncodeRequest(nsCString& aBody, const nsACString& aHost,
331 uint16_t aType, bool aDisableECS) {
332 aBody.Truncate();
333 // Header
334 aBody += '\0';
335 aBody += '\0'; // 16 bit id
336 aBody += 0x01; // |QR| Opcode |AA|TC|RD| Set the RD bit
337 aBody += '\0'; // |RA| Z | RCODE |
338 aBody += '\0';
339 aBody += 1; // QDCOUNT (number of entries in the question section)
340 aBody += '\0';
341 aBody += '\0'; // ANCOUNT
342 aBody += '\0';
343 aBody += '\0'; // NSCOUNT
345 char additionalRecords =
346 (aDisableECS || StaticPrefs::network_trr_padding()) ? 1 : 0;
347 aBody += '\0'; // ARCOUNT
348 aBody += additionalRecords; // ARCOUNT low byte for EDNS(0)
350 // Question
352 // The input host name should be converted to a sequence of labels, where
353 // each label consists of a length octet followed by that number of
354 // octets. The domain name terminates with the zero length octet for the
355 // null label of the root.
356 // Followed by 16 bit QTYPE and 16 bit QCLASS
358 int32_t index = 0;
359 int32_t offset = 0;
360 do {
361 bool dotFound = false;
362 int32_t labelLength;
363 index = aHost.FindChar('.', offset);
364 if (kNotFound != index) {
365 dotFound = true;
366 labelLength = index - offset;
367 } else {
368 labelLength = aHost.Length() - offset;
370 if (labelLength > 63) {
371 // too long label!
372 return NS_ERROR_ILLEGAL_VALUE;
374 if (labelLength > 0) {
375 aBody += static_cast<unsigned char>(labelLength);
376 nsDependentCSubstring label = Substring(aHost, offset, labelLength);
377 aBody.Append(label);
379 if (!dotFound) {
380 aBody += '\0'; // terminate with a final zero
381 break;
383 offset += labelLength + 1; // move over label and dot
384 } while (true);
386 aBody += static_cast<uint8_t>(aType >> 8); // upper 8 bit TYPE
387 aBody += static_cast<uint8_t>(aType);
388 aBody += '\0'; // upper 8 bit CLASS
389 aBody += kDNS_CLASS_IN; // IN - "the Internet"
391 if (additionalRecords) {
392 // EDNS(0) is RFC 6891, ECS is RFC 7871
393 aBody += '\0'; // NAME | domain name | MUST be 0 (root domain) |
394 aBody += '\0';
395 aBody += 41; // TYPE | u_int16_t | OPT (41) |
396 aBody += 16; // CLASS | u_int16_t | requestor's UDP payload size |
397 aBody +=
398 '\0'; // advertise 4K (high-byte: 16 | low-byte: 0), ignored by DoH
399 aBody += '\0'; // TTL | u_int32_t | extended RCODE and flags |
400 aBody += '\0';
401 aBody += '\0';
402 aBody += '\0';
404 // calculate padding length
405 unsigned int paddingLen = 0;
406 unsigned int rdlen = 0;
407 bool padding = StaticPrefs::network_trr_padding();
408 if (padding) {
409 // always add padding specified in rfc 7830 when this config is enabled
410 // to allow the reponse to be padded as well
412 // two bytes RDLEN, 4 bytes padding header
413 unsigned int packetLen = aBody.Length() + 2 + 4;
414 if (aDisableECS) {
415 // 8 bytes for disabling ecs
416 packetLen += 8;
419 // clamp the padding length, because the padding extension only allows up
420 // to 2^16 - 1 bytes padding and adding too much padding wastes resources
421 uint32_t padTo = std::clamp<uint32_t>(
422 StaticPrefs::network_trr_padding_length(), 0, 1024);
424 // Calculate number of padding bytes. The second '%'-operator is necessary
425 // because we prefer to add 0 bytes padding rather than padTo bytes
426 if (padTo > 0) {
427 paddingLen = (padTo - (packetLen % padTo)) % padTo;
429 // padding header + padding length
430 rdlen += 4 + paddingLen;
432 if (aDisableECS) {
433 rdlen += 8;
436 // RDLEN | u_int16_t | length of all RDATA |
437 aBody += (char)((rdlen >> 8) & 0xff); // upper 8 bit RDLEN
438 aBody += (char)(rdlen & 0xff);
440 // RDATA | octet stream | {attribute,value} pairs |
441 // The RDATA is just the ECS option setting zero subnet prefix
443 if (aDisableECS) {
444 aBody += '\0'; // upper 8 bit OPTION-CODE ECS
445 aBody += 8; // OPTION-CODE, 2 octets, for ECS is 8
447 aBody += '\0'; // upper 8 bit OPTION-LENGTH
448 aBody += 4; // OPTION-LENGTH, 2 octets, contains the length of the
449 // payload after OPTION-LENGTH
450 aBody += '\0'; // upper 8 bit FAMILY. IANA Address Family Numbers
451 // registry, not the AF_* constants!
452 aBody += 1; // FAMILY (Ipv4), 2 octets
454 aBody += '\0'; // SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH |
455 aBody += '\0';
457 // ADDRESS, minimum number of octets == nothing because zero bits
460 if (padding) {
461 aBody += '\0'; // upper 8 bit option OPTION-CODE PADDING
462 aBody += 12; // OPTION-CODE, 2 octets, for PADDING is 12
464 // OPTION-LENGTH, 2 octets
465 aBody += (char)((paddingLen >> 8) & 0xff);
466 aBody += (char)(paddingLen & 0xff);
467 for (unsigned int i = 0; i < paddingLen; i++) {
468 aBody += '\0';
473 return NS_OK;
476 // static
477 nsresult DNSPacket::ParseHTTPS(uint16_t aRDLen, struct SVCB& aParsed,
478 unsigned int aIndex,
479 const unsigned char* aBuffer,
480 unsigned int aBodySize,
481 const nsACString& aOriginHost) {
482 int32_t lastSvcParamKey = -1;
483 nsresult rv = NS_OK;
484 unsigned int svcbIndex = aIndex;
485 CheckedInt<uint16_t> available = aRDLen;
487 // Should have at least 2 bytes for the priority and one for the
488 // qname length.
489 if (available.value() < 3) {
490 return NS_ERROR_UNEXPECTED;
493 aParsed.mSvcFieldPriority = get16bit(aBuffer, svcbIndex);
494 svcbIndex += 2;
496 rv = GetQname(aParsed.mSvcDomainName, svcbIndex, aBuffer, aBodySize);
497 if (NS_FAILED(rv)) {
498 return rv;
501 if (aParsed.mSvcDomainName.IsEmpty()) {
502 if (aParsed.mSvcFieldPriority == 0) {
503 // For AliasMode SVCB RRs, a TargetName of "." indicates that
504 // the service is not available or does not exist.
505 return NS_OK;
508 // For ServiceMode SVCB RRs, if TargetName has the value ".",
509 // then the owner name of this record MUST be used as
510 // the effective TargetName.
511 // When the qname is port prefix name, we need to use the
512 // original host name as TargetName.
513 aParsed.mSvcDomainName = aOriginHost;
516 available -= (svcbIndex - aIndex);
517 if (!available.isValid()) {
518 return NS_ERROR_UNEXPECTED;
520 while (available.value() >= 4) {
521 // Every SvcFieldValues must have at least 4 bytes for the
522 // SvcParamKey (2 bytes) and length of SvcParamValue (2 bytes)
523 // If the length ever goes above the available data, meaning if
524 // available ever underflows, then that is an error.
525 struct SvcFieldValue value;
526 uint16_t key = get16bit(aBuffer, svcbIndex);
527 svcbIndex += 2;
529 // 2.2 Clients MUST consider an RR malformed if SvcParamKeys are
530 // not in strictly increasing numeric order.
531 if (key <= lastSvcParamKey) {
532 LOG(("SvcParamKeys not in increasing order"));
533 return NS_ERROR_UNEXPECTED;
535 lastSvcParamKey = key;
537 uint16_t len = get16bit(aBuffer, svcbIndex);
538 svcbIndex += 2;
540 available -= 4 + len;
541 if (!available.isValid()) {
542 return NS_ERROR_UNEXPECTED;
545 rv = ParseSvcParam(svcbIndex, key, value, len, aBuffer);
546 if (NS_FAILED(rv)) {
547 return rv;
549 svcbIndex += len;
551 // If this is an unknown key, we will simply ignore it.
552 // We also don't need to record SvcParamKeyMandatory
553 if (key == SvcParamKeyMandatory || !IsValidSvcParamKey(key)) {
554 continue;
557 if (value.mValue.is<SvcParamIpv4Hint>() ||
558 value.mValue.is<SvcParamIpv6Hint>()) {
559 aParsed.mHasIPHints = true;
561 if (value.mValue.is<SvcParamEchConfig>()) {
562 aParsed.mHasEchConfig = true;
563 aParsed.mEchConfig = value.mValue.as<SvcParamEchConfig>().mValue;
565 if (value.mValue.is<SvcParamODoHConfig>()) {
566 aParsed.mODoHConfig = value.mValue.as<SvcParamODoHConfig>().mValue;
568 aParsed.mSvcFieldValue.AppendElement(value);
571 return NS_OK;
574 Result<uint8_t, nsresult> DNSPacket::GetRCode() const {
575 if (mBodySize < 12) {
576 LOG(("DNSPacket::GetRCode - packet too small"));
577 return Err(NS_ERROR_ILLEGAL_VALUE);
580 return mResponse[3] & 0x0F;
583 Result<bool, nsresult> DNSPacket::RecursionAvailable() const {
584 if (mBodySize < 12) {
585 LOG(("DNSPacket::GetRCode - packet too small"));
586 return Err(NS_ERROR_ILLEGAL_VALUE);
589 return mResponse[3] & 0x80;
592 nsresult DNSPacket::DecodeInternal(
593 nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918,
594 DOHresp& aResp, TypeRecordResultType& aTypeResult,
595 nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
596 uint32_t& aTTL, const unsigned char* aBuffer, uint32_t aLen) {
597 // The response has a 12 byte header followed by the question (returned)
598 // and then the answer. The answer section itself contains the name, type
599 // and class again and THEN the record data.
601 // www.example.com response:
602 // header:
603 // abcd 8180 0001 0001 0000 0000
604 // the question:
605 // 0377 7777 0765 7861 6d70 6c65 0363 6f6d 0000 0100 01
606 // the answer:
607 // 03 7777 7707 6578 616d 706c 6503 636f 6d00 0001 0001
608 // 0000 0080 0004 5db8 d822
610 unsigned int index = 12;
611 uint8_t length;
612 nsAutoCString host;
613 nsresult rv;
614 uint16_t extendedError = UINT16_MAX;
616 LOG(("doh decode %s %d bytes\n", aHost.get(), aLen));
618 aCname.Truncate();
620 if (aLen < 12) {
621 LOG(("TRR bad incoming DOH, eject!\n"));
622 return NS_ERROR_ILLEGAL_VALUE;
625 if (!mNativePacket && (aBuffer[0] || aBuffer[1])) {
626 LOG(("Packet ID is unexpectedly non-zero"));
627 return NS_ERROR_ILLEGAL_VALUE;
630 uint8_t rcode = mResponse[3] & 0x0F;
631 LOG(("TRR Decode %s RCODE %d\n", PromiseFlatCString(aHost).get(), rcode));
633 uint16_t questionRecords = get16bit(aBuffer, 4); // qdcount
634 // iterate over the single(?) host name in question
635 while (questionRecords) {
636 do {
637 if (aLen < (index + 1)) {
638 LOG(("TRR Decode 1 index: %u size: %u", index, aLen));
639 return NS_ERROR_ILLEGAL_VALUE;
641 length = static_cast<uint8_t>(aBuffer[index]);
642 if (length) {
643 if (host.Length()) {
644 host.Append(".");
646 if (aLen < (index + 1 + length)) {
647 LOG(("TRR Decode 2 index: %u size: %u len: %u", index, aLen, length));
648 return NS_ERROR_ILLEGAL_VALUE;
650 host.Append(((char*)aBuffer) + index + 1, length);
652 index += 1 + length; // skip length byte + label
653 } while (length);
654 if (aLen < (index + 4)) {
655 LOG(("TRR Decode 3 index: %u size: %u", index, aLen));
656 return NS_ERROR_ILLEGAL_VALUE;
658 index += 4; // skip question's type, class
659 questionRecords--;
662 // Figure out the number of answer records from ANCOUNT
663 uint16_t answerRecords = get16bit(aBuffer, 6);
665 LOG(("TRR Decode: %d answer records (%u bytes body) %s index=%u\n",
666 answerRecords, aLen, host.get(), index));
668 while (answerRecords) {
669 nsAutoCString qname;
670 rv = GetQname(qname, index, aBuffer, mBodySize);
671 if (NS_FAILED(rv)) {
672 return rv;
674 // 16 bit TYPE
675 if (aLen < (index + 2)) {
676 LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2));
677 return NS_ERROR_ILLEGAL_VALUE;
679 uint16_t TYPE = get16bit(aBuffer, index);
681 index += 2;
683 // 16 bit class
684 if (aLen < (index + 2)) {
685 LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2));
686 return NS_ERROR_ILLEGAL_VALUE;
688 uint16_t CLASS = get16bit(aBuffer, index);
689 if (kDNS_CLASS_IN != CLASS) {
690 LOG(("TRR bad CLASS (%u) at index %d\n", CLASS, index));
691 return NS_ERROR_UNEXPECTED;
693 index += 2;
695 // 32 bit TTL (seconds)
696 if (aLen < (index + 4)) {
697 LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index));
698 return NS_ERROR_ILLEGAL_VALUE;
700 uint32_t TTL = get32bit(aBuffer, index);
701 index += 4;
703 // 16 bit RDLENGTH
704 if (aLen < (index + 2)) {
705 LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index));
706 return NS_ERROR_ILLEGAL_VALUE;
708 uint16_t RDLENGTH = get16bit(aBuffer, index);
709 index += 2;
711 if (aLen < (index + RDLENGTH)) {
712 LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__,
713 RDLENGTH, index));
714 return NS_ERROR_ILLEGAL_VALUE;
717 if ((TYPE != TRRTYPE_CNAME) && (TYPE != TRRTYPE_HTTPSSVC) &&
718 (TYPE != static_cast<uint16_t>(aType))) {
719 // Not the same type as was asked for nor CNAME
720 LOG(("TRR: Dohdecode:%d asked for type %d got %d\n", __LINE__, aType,
721 TYPE));
722 index += RDLENGTH;
723 answerRecords--;
724 continue;
727 // We check if the qname is a case-insensitive match for the host or the
728 // FQDN version of the host
729 bool responseMatchesQuestion =
730 (qname.Length() == aHost.Length() ||
731 (aHost.Length() == qname.Length() + 1 && aHost.Last() == '.')) &&
732 StringBeginsWith(aHost, qname, nsCaseInsensitiveCStringComparator);
734 if (responseMatchesQuestion) {
735 // RDATA
736 // - A (TYPE 1): 4 bytes
737 // - AAAA (TYPE 28): 16 bytes
738 // - NS (TYPE 2): N bytes
740 switch (TYPE) {
741 case TRRTYPE_A:
742 if (RDLENGTH != 4) {
743 LOG(("TRR bad length for A (%u)\n", RDLENGTH));
744 return NS_ERROR_UNEXPECTED;
746 rv = aResp.Add(TTL, aBuffer, index, RDLENGTH, aAllowRFC1918);
747 if (NS_FAILED(rv)) {
748 LOG(
749 ("TRR:DohDecode failed: local IP addresses or unknown IP "
750 "family\n"));
751 return rv;
753 break;
754 case TRRTYPE_AAAA:
755 if (RDLENGTH != 16) {
756 LOG(("TRR bad length for AAAA (%u)\n", RDLENGTH));
757 return NS_ERROR_UNEXPECTED;
759 rv = aResp.Add(TTL, aBuffer, index, RDLENGTH, aAllowRFC1918);
760 if (NS_FAILED(rv)) {
761 LOG(("TRR got unique/local IPv6 address!\n"));
762 return rv;
764 break;
766 case TRRTYPE_NS:
767 break;
768 case TRRTYPE_CNAME:
769 if (aCname.IsEmpty()) {
770 nsAutoCString qname;
771 unsigned int qnameindex = index;
772 rv = GetQname(qname, qnameindex, aBuffer, mBodySize);
773 if (NS_FAILED(rv)) {
774 return rv;
776 if (!qname.IsEmpty()) {
777 ToLowerCase(qname);
778 aCname = qname;
779 LOG(("DNSPacket::DohDecode CNAME host %s => %s\n", host.get(),
780 aCname.get()));
781 } else {
782 LOG(("DNSPacket::DohDecode empty CNAME for host %s!\n",
783 host.get()));
785 } else {
786 LOG(("DNSPacket::DohDecode CNAME - ignoring another entry\n"));
788 break;
789 case TRRTYPE_TXT: {
790 // TXT record RRDATA sections are a series of character-strings
791 // each character string is a length byte followed by that many data
792 // bytes
793 nsAutoCString txt;
794 unsigned int txtIndex = index;
795 uint16_t available = RDLENGTH;
797 while (available > 0) {
798 uint8_t characterStringLen = aBuffer[txtIndex++];
799 available--;
800 if (characterStringLen > available) {
801 LOG(("DNSPacket::DohDecode MALFORMED TXT RECORD\n"));
802 break;
804 txt.Append((const char*)(&aBuffer[txtIndex]), characterStringLen);
805 txtIndex += characterStringLen;
806 available -= characterStringLen;
809 if (!aTypeResult.is<TypeRecordTxt>()) {
810 aTypeResult = AsVariant(CopyableTArray<nsCString>());
814 auto& results = aTypeResult.as<TypeRecordTxt>();
815 results.AppendElement(txt);
817 if (aTTL > TTL) {
818 aTTL = TTL;
820 LOG(("DNSPacket::DohDecode TXT host %s => %s\n", host.get(),
821 txt.get()));
823 break;
825 case TRRTYPE_HTTPSSVC: {
826 struct SVCB parsed;
828 if (aType != TRRTYPE_HTTPSSVC) {
829 // Ignore the entry that we just parsed if we didn't ask for it.
830 break;
833 rv = ParseHTTPS(RDLENGTH, parsed, index, aBuffer, mBodySize,
834 mOriginHost ? *mOriginHost : qname);
835 if (NS_FAILED(rv)) {
836 return rv;
839 if (parsed.mSvcDomainName.IsEmpty() &&
840 parsed.mSvcFieldPriority == 0) {
841 // For AliasMode SVCB RRs, a TargetName of "." indicates that the
842 // service is not available or does not exist.
843 continue;
846 // Check for AliasForm
847 if (aCname.IsEmpty() && parsed.mSvcFieldPriority == 0) {
848 // Alias form SvcDomainName must not have the "." value (empty)
849 if (parsed.mSvcDomainName.IsEmpty()) {
850 return NS_ERROR_UNEXPECTED;
852 aCname = parsed.mSvcDomainName;
853 // If aliasForm is present, Service form must be ignored.
854 aTypeResult = mozilla::AsVariant(Nothing());
855 ToLowerCase(aCname);
856 LOG(("DNSPacket::DohDecode HTTPSSVC AliasForm host %s => %s\n",
857 host.get(), aCname.get()));
858 break;
861 aTTL = TTL;
863 if (!aTypeResult.is<TypeRecordHTTPSSVC>()) {
864 aTypeResult = mozilla::AsVariant(CopyableTArray<SVCB>());
867 auto& results = aTypeResult.as<TypeRecordHTTPSSVC>();
868 results.AppendElement(parsed);
871 break;
873 default:
874 // skip unknown record types
875 LOG(("TRR unsupported TYPE (%u) RDLENGTH %u\n", TYPE, RDLENGTH));
876 break;
878 } else {
879 LOG(("TRR asked for %s data but got %s\n", aHost.get(), qname.get()));
882 index += RDLENGTH;
883 LOG(("done with record type %u len %u index now %u of %u\n", TYPE, RDLENGTH,
884 index, aLen));
885 answerRecords--;
888 // NSCOUNT
889 uint16_t nsRecords = get16bit(aBuffer, 8);
890 LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords, aLen));
891 while (nsRecords) {
892 rv = PassQName(index, aBuffer);
893 if (NS_FAILED(rv)) {
894 return rv;
897 if (aLen < (index + 8)) {
898 return NS_ERROR_ILLEGAL_VALUE;
900 index += 2; // type
901 index += 2; // class
902 index += 4; // ttl
904 // 16 bit RDLENGTH
905 if (aLen < (index + 2)) {
906 return NS_ERROR_ILLEGAL_VALUE;
908 uint16_t RDLENGTH = get16bit(aBuffer, index);
909 index += 2;
910 if (aLen < (index + RDLENGTH)) {
911 return NS_ERROR_ILLEGAL_VALUE;
913 index += RDLENGTH;
914 LOG(("done with nsRecord now %u of %u\n", index, aLen));
915 nsRecords--;
918 // additional resource records
919 uint16_t arRecords = get16bit(aBuffer, 10);
920 LOG(("TRR Decode: %d additional resource records (%u bytes body)\n",
921 arRecords, aLen));
923 while (arRecords) {
924 nsAutoCString qname;
925 rv = GetQname(qname, index, aBuffer, mBodySize);
926 if (NS_FAILED(rv)) {
927 LOG(("Bad qname for additional record"));
928 return rv;
931 if (aLen < (index + 8)) {
932 return NS_ERROR_ILLEGAL_VALUE;
934 uint16_t type = get16bit(aBuffer, index);
935 index += 2;
936 // The next two bytes encode class
937 // (or udpPayloadSize when type is TRRTYPE_OPT)
938 uint16_t cls = get16bit(aBuffer, index);
939 index += 2;
940 // The next 4 bytes encode TTL
941 // (or extRCode + ednsVersion + flags when type is TRRTYPE_OPT)
942 uint32_t ttl = get32bit(aBuffer, index);
943 index += 4;
944 // cls and ttl are unused when type is TRRTYPE_OPT
946 // 16 bit RDLENGTH
947 if (aLen < (index + 2)) {
948 LOG(("Record too small"));
949 return NS_ERROR_ILLEGAL_VALUE;
952 uint16_t rdlength = get16bit(aBuffer, index);
953 index += 2;
954 if (aLen < (index + rdlength)) {
955 LOG(("rdlength too big"));
956 return NS_ERROR_ILLEGAL_VALUE;
959 auto parseRecord = [&]() {
960 LOG(("Parsing additional record type: %u", type));
961 auto* entry = aAdditionalRecords.GetOrInsertNew(qname);
963 switch (type) {
964 case TRRTYPE_A:
965 if (kDNS_CLASS_IN != cls) {
966 LOG(("NOT IN - returning"));
967 return;
969 if (rdlength != 4) {
970 LOG(("TRR bad length for A (%u)\n", rdlength));
971 return;
973 rv = entry->Add(ttl, aBuffer, index, rdlength, aAllowRFC1918);
974 if (NS_FAILED(rv)) {
975 LOG(
976 ("TRR:DohDecode failed: local IP addresses or unknown IP "
977 "family\n"));
978 return;
980 break;
981 case TRRTYPE_AAAA:
982 if (kDNS_CLASS_IN != cls) {
983 LOG(("NOT IN - returning"));
984 return;
986 if (rdlength != 16) {
987 LOG(("TRR bad length for AAAA (%u)\n", rdlength));
988 return;
990 rv = entry->Add(ttl, aBuffer, index, rdlength, aAllowRFC1918);
991 if (NS_FAILED(rv)) {
992 LOG(("TRR got unique/local IPv6 address!\n"));
993 return;
995 break;
996 case TRRTYPE_OPT: { // OPT
997 LOG(("Parsing opt rdlen: %u", rdlength));
998 unsigned int offset = 0;
999 while (offset + 2 <= rdlength) {
1000 uint16_t optCode = get16bit(aBuffer, index + offset);
1001 LOG(("optCode: %u", optCode));
1002 offset += 2;
1003 if (offset + 2 > rdlength) {
1004 break;
1006 uint16_t optLen = get16bit(aBuffer, index + offset);
1007 LOG(("optLen: %u", optLen));
1008 offset += 2;
1009 if (offset + optLen > rdlength) {
1010 LOG(("offset: %u, optLen: %u, rdlen: %u", offset, optLen,
1011 rdlength));
1012 break;
1015 LOG(("OPT: code: %u len:%u", optCode, optLen));
1017 if (optCode != 15) {
1018 offset += optLen;
1019 continue;
1022 // optCode == 15; Extended DNS error
1024 if (offset + 2 > rdlength || optLen < 2) {
1025 break;
1027 extendedError = get16bit(aBuffer, index + offset);
1029 LOG(("Extended error code: %u message: %s", extendedError,
1030 nsAutoCString((char*)aBuffer + index + offset + 2, optLen - 2)
1031 .get()));
1032 offset += optLen;
1034 break;
1036 default:
1037 break;
1041 parseRecord();
1043 index += rdlength;
1044 LOG(("done with additional rr now %u of %u\n", index, aLen));
1045 arRecords--;
1048 if (index != aLen) {
1049 LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n",
1050 index, aLen));
1051 // failed to parse 100%, do not continue
1052 return NS_ERROR_ILLEGAL_VALUE;
1055 if (aType == TRRTYPE_NS && rcode != 0) {
1056 return NS_ERROR_UNKNOWN_HOST;
1059 if ((aType != TRRTYPE_NS) && aCname.IsEmpty() && aResp.mAddresses.IsEmpty() &&
1060 aTypeResult.is<TypeRecordEmpty>()) {
1061 // no entries were stored!
1062 LOG(("TRR: No entries were stored!\n"));
1064 if (extendedError != UINT16_MAX &&
1065 StaticPrefs::network_trr_hard_fail_on_extended_error() &&
1066 hardFail(extendedError)) {
1067 return NS_ERROR_DEFINITIVE_UNKNOWN_HOST;
1069 return NS_ERROR_UNKNOWN_HOST;
1072 // https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03#page-14
1073 // If one or more SVCB records of ServiceForm SvcRecordType are returned for
1074 // HOST, clients should select the highest-priority option with acceptable
1075 // parameters.
1076 if (aTypeResult.is<TypeRecordHTTPSSVC>()) {
1077 auto& results = aTypeResult.as<TypeRecordHTTPSSVC>();
1078 results.Sort();
1081 return NS_OK;
1085 // DohDecode() collects the TTL and the IP addresses in the response
1087 nsresult DNSPacket::Decode(
1088 nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918,
1089 DOHresp& aResp, TypeRecordResultType& aTypeResult,
1090 nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
1091 uint32_t& aTTL) {
1092 nsresult rv =
1093 DecodeInternal(aHost, aType, aCname, aAllowRFC1918, aResp, aTypeResult,
1094 aAdditionalRecords, aTTL, mResponse, mBodySize);
1095 mStatus = rv;
1096 return rv;
1099 } // namespace net
1100 } // namespace mozilla