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/. */
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"
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)
37 for (const auto& err
: noFallbackErrors
) {
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"));
51 mStatus
= NS_ERROR_UNEXPECTED
;
55 mBodySize
= response_length
;
59 nsresult
DNSPacket::ParseSvcParam(unsigned int svcbIndex
, uint16_t key
,
60 SvcFieldValue
& field
, uint16_t length
,
61 const unsigned char* aBuffer
) {
63 case SvcParamKeyMandatory
: {
64 if (length
% 2 != 0) {
65 // This key should encode a list of uint16_t
66 return NS_ERROR_UNEXPECTED
;
69 uint16_t mandatoryKey
= get16bit(aBuffer
, svcbIndex
);
73 if (!IsValidSvcParamKey(mandatoryKey
)) {
74 LOG(("The mandatory field includes a key we don't support %u",
76 return NS_ERROR_UNEXPECTED
;
81 case SvcParamKeyAlpn
: {
82 field
.mValue
= AsVariant(SvcParamAlpn());
83 auto& alpnArray
= field
.mValue
.as
<SvcParamAlpn
>().mValue
;
85 uint8_t alpnIdLength
= aBuffer
[svcbIndex
++];
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
;
98 case SvcParamKeyNoDefaultAlpn
: {
100 // This key should not contain a value
101 return NS_ERROR_UNEXPECTED
;
103 field
.mValue
= AsVariant(SvcParamNoDefaultAlpn
{});
106 case SvcParamKeyPort
: {
108 // This key should only encode a uint16_t
109 return NS_ERROR_UNEXPECTED
;
112 AsVariant(SvcParamPort
{.mValue
= get16bit(aBuffer
, svcbIndex
)});
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
;
125 addr
.inet
.family
= AF_INET
;
127 addr
.inet
.ip
= ntohl(get32bit(aBuffer
, svcbIndex
));
128 ipv4array
.AppendElement(addr
);
134 case SvcParamKeyEchConfig
: {
135 field
.mValue
= AsVariant(SvcParamEchConfig
{
136 .mValue
= nsCString((const char*)(&aBuffer
[svcbIndex
]), length
)});
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
;
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
);
158 // no need to increase svcbIndex - we did it in the for above.
162 case SvcParamKeyODoHConfig
: {
163 field
.mValue
= AsVariant(SvcParamODoHConfig
{
164 .mValue
= nsCString((const char*)(&aBuffer
[svcbIndex
]), length
)});
168 // Unespected type. We'll just ignore it.
176 nsresult
DNSPacket::PassQName(unsigned int& index
,
177 const unsigned char* aBuffer
) {
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
;
194 LOG(("TRR: illegal label length byte (%x) at index %d\n", length
, index
));
195 return NS_ERROR_ILLEGAL_VALUE
;
198 if (mBodySize
< (index
+ 1 + length
)) {
199 LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__
, index
));
200 return NS_ERROR_ILLEGAL_VALUE
;
207 // GetQname: retrieves the qname (stores in 'aQname') and stores the index
208 // after qname was parsed into the 'aIndex'.
210 nsresult
DNSPacket::GetQname(nsACString
& aQname
, unsigned int& aIndex
,
211 const unsigned char* aBuffer
,
212 unsigned int aBodySize
) {
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
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];
231 // only update on the first "jump"
232 endindex
= cindex
+ 2;
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
;
246 if (!aQname
.IsEmpty()) {
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
);
258 LOG(("DNSPacket::DohDecode pointer loop error\n"));
259 return NS_ERROR_ILLEGAL_VALUE
;
262 // there was no "jump"
269 nsresult
DOHresp::Add(uint32_t TTL
, unsigned char const* dns
,
270 unsigned int index
, uint16_t len
, bool aLocalAllowed
) {
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
) {
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
];
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
303 addr
.ToStringBuffer(buf
, sizeof(buf
));
304 LOG(("DOHresp:Add %s\n", buf
));
306 mAddresses
.AppendElement(addr
);
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
;
319 aInputStream
->Read((char*)mResponse
+ mBodySize
, aCount
, &count
);
323 MOZ_ASSERT(count
== aCount
);
328 const uint8_t kDNS_CLASS_IN
= 1;
330 nsresult
DNSPacket::EncodeRequest(nsCString
& aBody
, const nsACString
& aHost
,
331 uint16_t aType
, bool aDisableECS
) {
335 aBody
+= '\0'; // 16 bit id
336 aBody
+= 0x01; // |QR| Opcode |AA|TC|RD| Set the RD bit
337 aBody
+= '\0'; // |RA| Z | RCODE |
339 aBody
+= 1; // QDCOUNT (number of entries in the question section)
341 aBody
+= '\0'; // ANCOUNT
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)
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
361 bool dotFound
= false;
363 index
= aHost
.FindChar('.', offset
);
364 if (kNotFound
!= index
) {
366 labelLength
= index
- offset
;
368 labelLength
= aHost
.Length() - offset
;
370 if (labelLength
> 63) {
372 return NS_ERROR_ILLEGAL_VALUE
;
374 if (labelLength
> 0) {
375 aBody
+= static_cast<unsigned char>(labelLength
);
376 nsDependentCSubstring label
= Substring(aHost
, offset
, labelLength
);
380 aBody
+= '\0'; // terminate with a final zero
383 offset
+= labelLength
+ 1; // move over label and dot
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) |
395 aBody
+= 41; // TYPE | u_int16_t | OPT (41) |
396 aBody
+= 16; // CLASS | u_int16_t | requestor's UDP payload size |
398 '\0'; // advertise 4K (high-byte: 16 | low-byte: 0), ignored by DoH
399 aBody
+= '\0'; // TTL | u_int32_t | extended RCODE and flags |
404 // calculate padding length
405 unsigned int paddingLen
= 0;
406 unsigned int rdlen
= 0;
407 bool padding
= StaticPrefs::network_trr_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;
415 // 8 bytes for disabling ecs
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
427 paddingLen
= (padTo
- (packetLen
% padTo
)) % padTo
;
429 // padding header + padding length
430 rdlen
+= 4 + paddingLen
;
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
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 |
457 // ADDRESS, minimum number of octets == nothing because zero bits
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
++) {
477 nsresult
DNSPacket::ParseHTTPS(uint16_t aRDLen
, struct SVCB
& aParsed
,
479 const unsigned char* aBuffer
,
480 unsigned int aBodySize
,
481 const nsACString
& aOriginHost
) {
482 int32_t lastSvcParamKey
= -1;
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
489 if (available
.value() < 3) {
490 return NS_ERROR_UNEXPECTED
;
493 aParsed
.mSvcFieldPriority
= get16bit(aBuffer
, svcbIndex
);
496 rv
= GetQname(aParsed
.mSvcDomainName
, svcbIndex
, aBuffer
, aBodySize
);
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.
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
);
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
);
540 available
-= 4 + len
;
541 if (!available
.isValid()) {
542 return NS_ERROR_UNEXPECTED
;
545 rv
= ParseSvcParam(svcbIndex
, key
, value
, len
, aBuffer
);
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
)) {
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
);
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:
603 // abcd 8180 0001 0001 0000 0000
605 // 0377 7777 0765 7861 6d70 6c65 0363 6f6d 0000 0100 01
607 // 03 7777 7707 6578 616d 706c 6503 636f 6d00 0001 0001
608 // 0000 0080 0004 5db8 d822
610 unsigned int index
= 12;
614 uint16_t extendedError
= UINT16_MAX
;
616 LOG(("doh decode %s %d bytes\n", aHost
.get(), aLen
));
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
) {
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
]);
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
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
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
) {
670 rv
= GetQname(qname
, index
, aBuffer
, mBodySize
);
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
);
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
;
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
);
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
);
711 if (aLen
< (index
+ RDLENGTH
)) {
712 LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__
,
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
,
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
) {
736 // - A (TYPE 1): 4 bytes
737 // - AAAA (TYPE 28): 16 bytes
738 // - NS (TYPE 2): N bytes
743 LOG(("TRR bad length for A (%u)\n", RDLENGTH
));
744 return NS_ERROR_UNEXPECTED
;
746 rv
= aResp
.Add(TTL
, aBuffer
, index
, RDLENGTH
, aAllowRFC1918
);
749 ("TRR:DohDecode failed: local IP addresses or unknown IP "
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
);
761 LOG(("TRR got unique/local IPv6 address!\n"));
769 if (aCname
.IsEmpty()) {
771 unsigned int qnameindex
= index
;
772 rv
= GetQname(qname
, qnameindex
, aBuffer
, mBodySize
);
776 if (!qname
.IsEmpty()) {
779 LOG(("DNSPacket::DohDecode CNAME host %s => %s\n", host
.get(),
782 LOG(("DNSPacket::DohDecode empty CNAME for host %s!\n",
786 LOG(("DNSPacket::DohDecode CNAME - ignoring another entry\n"));
790 // TXT record RRDATA sections are a series of character-strings
791 // each character string is a length byte followed by that many data
794 unsigned int txtIndex
= index
;
795 uint16_t available
= RDLENGTH
;
797 while (available
> 0) {
798 uint8_t characterStringLen
= aBuffer
[txtIndex
++];
800 if (characterStringLen
> available
) {
801 LOG(("DNSPacket::DohDecode MALFORMED TXT RECORD\n"));
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
);
820 LOG(("DNSPacket::DohDecode TXT host %s => %s\n", host
.get(),
825 case TRRTYPE_HTTPSSVC
: {
828 if (aType
!= TRRTYPE_HTTPSSVC
) {
829 // Ignore the entry that we just parsed if we didn't ask for it.
833 rv
= ParseHTTPS(RDLENGTH
, parsed
, index
, aBuffer
, mBodySize
,
834 mOriginHost
? *mOriginHost
: qname
);
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.
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());
856 LOG(("DNSPacket::DohDecode HTTPSSVC AliasForm host %s => %s\n",
857 host
.get(), aCname
.get()));
863 if (!aTypeResult
.is
<TypeRecordHTTPSSVC
>()) {
864 aTypeResult
= mozilla::AsVariant(CopyableTArray
<SVCB
>());
867 auto& results
= aTypeResult
.as
<TypeRecordHTTPSSVC
>();
868 results
.AppendElement(parsed
);
874 // skip unknown record types
875 LOG(("TRR unsupported TYPE (%u) RDLENGTH %u\n", TYPE
, RDLENGTH
));
879 LOG(("TRR asked for %s data but got %s\n", aHost
.get(), qname
.get()));
883 LOG(("done with record type %u len %u index now %u of %u\n", TYPE
, RDLENGTH
,
889 uint16_t nsRecords
= get16bit(aBuffer
, 8);
890 LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords
, aLen
));
892 rv
= PassQName(index
, aBuffer
);
897 if (aLen
< (index
+ 8)) {
898 return NS_ERROR_ILLEGAL_VALUE
;
905 if (aLen
< (index
+ 2)) {
906 return NS_ERROR_ILLEGAL_VALUE
;
908 uint16_t RDLENGTH
= get16bit(aBuffer
, index
);
910 if (aLen
< (index
+ RDLENGTH
)) {
911 return NS_ERROR_ILLEGAL_VALUE
;
914 LOG(("done with nsRecord now %u of %u\n", index
, aLen
));
918 // additional resource records
919 uint16_t arRecords
= get16bit(aBuffer
, 10);
920 LOG(("TRR Decode: %d additional resource records (%u bytes body)\n",
925 rv
= GetQname(qname
, index
, aBuffer
, mBodySize
);
927 LOG(("Bad qname for additional record"));
931 if (aLen
< (index
+ 8)) {
932 return NS_ERROR_ILLEGAL_VALUE
;
934 uint16_t type
= get16bit(aBuffer
, index
);
936 // The next two bytes encode class
937 // (or udpPayloadSize when type is TRRTYPE_OPT)
938 uint16_t cls
= get16bit(aBuffer
, index
);
940 // The next 4 bytes encode TTL
941 // (or extRCode + ednsVersion + flags when type is TRRTYPE_OPT)
942 uint32_t ttl
= get32bit(aBuffer
, index
);
944 // cls and ttl are unused when type is TRRTYPE_OPT
947 if (aLen
< (index
+ 2)) {
948 LOG(("Record too small"));
949 return NS_ERROR_ILLEGAL_VALUE
;
952 uint16_t rdlength
= get16bit(aBuffer
, index
);
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
);
965 if (kDNS_CLASS_IN
!= cls
) {
966 LOG(("NOT IN - returning"));
970 LOG(("TRR bad length for A (%u)\n", rdlength
));
973 rv
= entry
->Add(ttl
, aBuffer
, index
, rdlength
, aAllowRFC1918
);
976 ("TRR:DohDecode failed: local IP addresses or unknown IP "
982 if (kDNS_CLASS_IN
!= cls
) {
983 LOG(("NOT IN - returning"));
986 if (rdlength
!= 16) {
987 LOG(("TRR bad length for AAAA (%u)\n", rdlength
));
990 rv
= entry
->Add(ttl
, aBuffer
, index
, rdlength
, aAllowRFC1918
);
992 LOG(("TRR got unique/local IPv6 address!\n"));
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
));
1003 if (offset
+ 2 > rdlength
) {
1006 uint16_t optLen
= get16bit(aBuffer
, index
+ offset
);
1007 LOG(("optLen: %u", optLen
));
1009 if (offset
+ optLen
> rdlength
) {
1010 LOG(("offset: %u, optLen: %u, rdlen: %u", offset
, optLen
,
1015 LOG(("OPT: code: %u len:%u", optCode
, optLen
));
1017 if (optCode
!= 15) {
1022 // optCode == 15; Extended DNS error
1024 if (offset
+ 2 > rdlength
|| optLen
< 2) {
1027 extendedError
= get16bit(aBuffer
, index
+ offset
);
1029 LOG(("Extended error code: %u message: %s", extendedError
,
1030 nsAutoCString((char*)aBuffer
+ index
+ offset
+ 2, optLen
- 2)
1044 LOG(("done with additional rr now %u of %u\n", index
, aLen
));
1048 if (index
!= aLen
) {
1049 LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n",
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
1076 if (aTypeResult
.is
<TypeRecordHTTPSSVC
>()) {
1077 auto& results
= aTypeResult
.as
<TypeRecordHTTPSSVC
>();
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
,
1093 DecodeInternal(aHost
, aType
, aCname
, aAllowRFC1918
, aResp
, aTypeResult
,
1094 aAdditionalRecords
, aTTL
, mResponse
, mBodySize
);
1100 } // namespace mozilla