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/draft-ietf-dnsop-extended-error-16#section-4
29 // This is a list of errors for which we should not fallback to Do53.
30 // These are normally DNSSEC failures or explicit filtering performed by the
31 // recursive resolver.
32 bool hardFail(uint16_t code
) {
33 const uint16_t noFallbackErrors
[] = {
34 4, // Forged answer (malware filtering)
36 7, // Signature expired
37 8, // Signature not yet valid
40 11, // No ZONE Key Bit set
45 for (const auto& err
: noFallbackErrors
) {
54 nsresult
DNSPacket::ParseSvcParam(unsigned int svcbIndex
, uint16_t key
,
55 SvcFieldValue
& field
, uint16_t length
,
56 const unsigned char* aBuffer
) {
58 case SvcParamKeyMandatory
: {
59 if (length
% 2 != 0) {
60 // This key should encode a list of uint16_t
61 return NS_ERROR_UNEXPECTED
;
64 uint16_t mandatoryKey
= get16bit(aBuffer
, svcbIndex
);
68 if (!IsValidSvcParamKey(mandatoryKey
)) {
69 LOG(("The mandatory field includes a key we don't support %u",
71 return NS_ERROR_UNEXPECTED
;
76 case SvcParamKeyAlpn
: {
77 field
.mValue
= AsVariant(SvcParamAlpn());
78 auto& alpnArray
= field
.mValue
.as
<SvcParamAlpn
>().mValue
;
80 uint8_t alpnIdLength
= aBuffer
[svcbIndex
++];
82 if (alpnIdLength
> length
) {
83 return NS_ERROR_UNEXPECTED
;
86 alpnArray
.AppendElement(
87 nsCString((const char*)&aBuffer
[svcbIndex
], alpnIdLength
));
88 length
-= alpnIdLength
;
89 svcbIndex
+= alpnIdLength
;
93 case SvcParamKeyNoDefaultAlpn
: {
95 // This key should not contain a value
96 return NS_ERROR_UNEXPECTED
;
98 field
.mValue
= AsVariant(SvcParamNoDefaultAlpn
{});
101 case SvcParamKeyPort
: {
103 // This key should only encode a uint16_t
104 return NS_ERROR_UNEXPECTED
;
107 AsVariant(SvcParamPort
{.mValue
= get16bit(aBuffer
, svcbIndex
)});
110 case SvcParamKeyIpv4Hint
: {
111 if (length
% 4 != 0) {
112 // This key should only encode IPv4 addresses
113 return NS_ERROR_UNEXPECTED
;
116 field
.mValue
= AsVariant(SvcParamIpv4Hint());
117 auto& ipv4array
= field
.mValue
.as
<SvcParamIpv4Hint
>().mValue
;
120 addr
.inet
.family
= AF_INET
;
122 addr
.inet
.ip
= ntohl(get32bit(aBuffer
, svcbIndex
));
123 ipv4array
.AppendElement(addr
);
129 case SvcParamKeyEchConfig
: {
130 field
.mValue
= AsVariant(SvcParamEchConfig
{
131 .mValue
= nsCString((const char*)(&aBuffer
[svcbIndex
]), length
)});
134 case SvcParamKeyIpv6Hint
: {
135 if (length
% 16 != 0) {
136 // This key should only encode IPv6 addresses
137 return NS_ERROR_UNEXPECTED
;
140 field
.mValue
= AsVariant(SvcParamIpv6Hint());
141 auto& ipv6array
= field
.mValue
.as
<SvcParamIpv6Hint
>().mValue
;
144 addr
.inet6
.family
= AF_INET6
;
145 addr
.inet6
.port
= 0; // unknown
146 addr
.inet6
.flowinfo
= 0; // unknown
147 addr
.inet6
.scope_id
= 0; // unknown
148 for (int i
= 0; i
< 16; i
++, svcbIndex
++) {
149 addr
.inet6
.ip
.u8
[i
] = aBuffer
[svcbIndex
];
151 ipv6array
.AppendElement(addr
);
153 // no need to increase svcbIndex - we did it in the for above.
157 case SvcParamKeyODoHConfig
: {
158 field
.mValue
= AsVariant(SvcParamODoHConfig
{
159 .mValue
= nsCString((const char*)(&aBuffer
[svcbIndex
]), length
)});
163 // Unespected type. We'll just ignore it.
171 nsresult
DNSPacket::PassQName(unsigned int& index
,
172 const unsigned char* aBuffer
) {
175 if (mBodySize
< (index
+ 1)) {
176 LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__
, index
));
177 return NS_ERROR_ILLEGAL_VALUE
;
179 length
= static_cast<uint8_t>(aBuffer
[index
]);
180 if ((length
& 0xc0) == 0xc0) {
181 // name pointer, advance over it and be done
182 if (mBodySize
< (index
+ 2)) {
183 return NS_ERROR_ILLEGAL_VALUE
;
189 LOG(("TRR: illegal label length byte (%x) at index %d\n", length
, index
));
190 return NS_ERROR_ILLEGAL_VALUE
;
193 if (mBodySize
< (index
+ 1 + length
)) {
194 LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__
, index
));
195 return NS_ERROR_ILLEGAL_VALUE
;
202 // GetQname: retrieves the qname (stores in 'aQname') and stores the index
203 // after qname was parsed into the 'aIndex'.
204 nsresult
DNSPacket::GetQname(nsACString
& aQname
, unsigned int& aIndex
,
205 const unsigned char* aBuffer
) {
207 unsigned int cindex
= aIndex
;
208 unsigned int loop
= 128; // a valid DNS name can never loop this much
209 unsigned int endindex
= 0; // index position after this data
211 if (cindex
>= mBodySize
) {
212 LOG(("TRR: bad Qname packet\n"));
213 return NS_ERROR_ILLEGAL_VALUE
;
215 clength
= static_cast<uint8_t>(aBuffer
[cindex
]);
216 if ((clength
& 0xc0) == 0xc0) {
217 // name pointer, get the new offset (14 bits)
218 if ((cindex
+ 1) >= mBodySize
) {
219 return NS_ERROR_ILLEGAL_VALUE
;
221 // extract the new index position for the next label
222 uint16_t newpos
= (clength
& 0x3f) << 8 | aBuffer
[cindex
+ 1];
224 // only update on the first "jump"
225 endindex
= cindex
+ 2;
230 if (clength
& 0xc0) {
231 // any of those bits set individually is an error
232 LOG(("TRR: bad Qname packet\n"));
233 return NS_ERROR_ILLEGAL_VALUE
;
239 if (!aQname
.IsEmpty()) {
242 if ((cindex
+ clength
) > mBodySize
) {
243 return NS_ERROR_ILLEGAL_VALUE
;
245 aQname
.Append((const char*)(&aBuffer
[cindex
]), clength
);
246 cindex
+= clength
; // skip label
248 } while (clength
&& --loop
);
251 LOG(("DNSPacket::DohDecode pointer loop error\n"));
252 return NS_ERROR_ILLEGAL_VALUE
;
255 // there was no "jump"
262 nsresult
DOHresp::Add(uint32_t TTL
, unsigned char const* dns
,
263 unsigned int index
, uint16_t len
, bool aLocalAllowed
) {
267 addr
.inet
.family
= AF_INET
;
268 addr
.inet
.port
= 0; // unknown
269 addr
.inet
.ip
= ntohl(get32bit(dns
, index
));
270 } else if (16 == len
) {
272 addr
.inet6
.family
= AF_INET6
;
273 addr
.inet6
.port
= 0; // unknown
274 addr
.inet6
.flowinfo
= 0; // unknown
275 addr
.inet6
.scope_id
= 0; // unknown
276 for (int i
= 0; i
< 16; i
++, index
++) {
277 addr
.inet6
.ip
.u8
[i
] = dns
[index
];
280 return NS_ERROR_UNEXPECTED
;
283 if (addr
.IsIPAddrLocal() && !aLocalAllowed
) {
284 return NS_ERROR_FAILURE
;
287 // While the DNS packet might return individual TTLs for each address,
288 // we can only return one value in the AddrInfo class so pick the
296 addr
.ToStringBuffer(buf
, sizeof(buf
));
297 LOG(("DOHresp:Add %s\n", buf
));
299 mAddresses
.AppendElement(addr
);
303 nsresult
DNSPacket::OnDataAvailable(nsIRequest
* aRequest
,
304 nsIInputStream
* aInputStream
,
305 uint64_t aOffset
, const uint32_t aCount
) {
306 if (aCount
+ mBodySize
> MAX_SIZE
) {
307 LOG(("DNSPacket::OnDataAvailable:%d fail\n", __LINE__
));
308 return NS_ERROR_FAILURE
;
312 aInputStream
->Read((char*)mResponse
+ mBodySize
, aCount
, &count
);
316 MOZ_ASSERT(count
== aCount
);
321 const uint8_t kDNS_CLASS_IN
= 1;
323 nsresult
DNSPacket::EncodeRequest(nsCString
& aBody
, const nsACString
& aHost
,
324 uint16_t aType
, bool aDisableECS
) {
328 aBody
+= '\0'; // 16 bit id
329 aBody
+= 0x01; // |QR| Opcode |AA|TC|RD| Set the RD bit
330 aBody
+= '\0'; // |RA| Z | RCODE |
332 aBody
+= 1; // QDCOUNT (number of entries in the question section)
334 aBody
+= '\0'; // ANCOUNT
336 aBody
+= '\0'; // NSCOUNT
338 char additionalRecords
=
339 (aDisableECS
|| StaticPrefs::network_trr_padding()) ? 1 : 0;
340 aBody
+= '\0'; // ARCOUNT
341 aBody
+= additionalRecords
; // ARCOUNT low byte for EDNS(0)
345 // The input host name should be converted to a sequence of labels, where
346 // each label consists of a length octet followed by that number of
347 // octets. The domain name terminates with the zero length octet for the
348 // null label of the root.
349 // Followed by 16 bit QTYPE and 16 bit QCLASS
354 bool dotFound
= false;
356 index
= aHost
.FindChar('.', offset
);
357 if (kNotFound
!= index
) {
359 labelLength
= index
- offset
;
361 labelLength
= aHost
.Length() - offset
;
363 if (labelLength
> 63) {
365 SetDNSPacketStatus(DNSPacketStatus::EncodeError
);
366 return NS_ERROR_ILLEGAL_VALUE
;
368 if (labelLength
> 0) {
369 aBody
+= static_cast<unsigned char>(labelLength
);
370 nsDependentCSubstring label
= Substring(aHost
, offset
, labelLength
);
374 aBody
+= '\0'; // terminate with a final zero
377 offset
+= labelLength
+ 1; // move over label and dot
380 aBody
+= static_cast<uint8_t>(aType
>> 8); // upper 8 bit TYPE
381 aBody
+= static_cast<uint8_t>(aType
);
382 aBody
+= '\0'; // upper 8 bit CLASS
383 aBody
+= kDNS_CLASS_IN
; // IN - "the Internet"
385 if (additionalRecords
) {
386 // EDNS(0) is RFC 6891, ECS is RFC 7871
387 aBody
+= '\0'; // NAME | domain name | MUST be 0 (root domain) |
389 aBody
+= 41; // TYPE | u_int16_t | OPT (41) |
390 aBody
+= 16; // CLASS | u_int16_t | requestor's UDP payload size |
392 '\0'; // advertise 4K (high-byte: 16 | low-byte: 0), ignored by DoH
393 aBody
+= '\0'; // TTL | u_int32_t | extended RCODE and flags |
398 // calculate padding length
399 unsigned int paddingLen
= 0;
400 unsigned int rdlen
= 0;
401 bool padding
= StaticPrefs::network_trr_padding();
403 // always add padding specified in rfc 7830 when this config is enabled
404 // to allow the reponse to be padded as well
406 // two bytes RDLEN, 4 bytes padding header
407 unsigned int packetLen
= aBody
.Length() + 2 + 4;
409 // 8 bytes for disabling ecs
413 // clamp the padding length, because the padding extension only allows up
414 // to 2^16 - 1 bytes padding and adding too much padding wastes resources
415 uint32_t padTo
= std::clamp
<uint32_t>(
416 StaticPrefs::network_trr_padding_length(), 0, 1024);
418 // Calculate number of padding bytes. The second '%'-operator is necessary
419 // because we prefer to add 0 bytes padding rather than padTo bytes
421 paddingLen
= (padTo
- (packetLen
% padTo
)) % padTo
;
423 // padding header + padding length
424 rdlen
+= 4 + paddingLen
;
430 // RDLEN | u_int16_t | length of all RDATA |
431 aBody
+= (char)((rdlen
>> 8) & 0xff); // upper 8 bit RDLEN
432 aBody
+= (char)(rdlen
& 0xff);
434 // RDATA | octet stream | {attribute,value} pairs |
435 // The RDATA is just the ECS option setting zero subnet prefix
438 aBody
+= '\0'; // upper 8 bit OPTION-CODE ECS
439 aBody
+= 8; // OPTION-CODE, 2 octets, for ECS is 8
441 aBody
+= '\0'; // upper 8 bit OPTION-LENGTH
442 aBody
+= 4; // OPTION-LENGTH, 2 octets, contains the length of the
443 // payload after OPTION-LENGTH
444 aBody
+= '\0'; // upper 8 bit FAMILY. IANA Address Family Numbers
445 // registry, not the AF_* constants!
446 aBody
+= 1; // FAMILY (Ipv4), 2 octets
448 aBody
+= '\0'; // SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH |
451 // ADDRESS, minimum number of octets == nothing because zero bits
455 aBody
+= '\0'; // upper 8 bit option OPTION-CODE PADDING
456 aBody
+= 12; // OPTION-CODE, 2 octets, for PADDING is 12
458 // OPTION-LENGTH, 2 octets
459 aBody
+= (char)((paddingLen
>> 8) & 0xff);
460 aBody
+= (char)(paddingLen
& 0xff);
461 for (unsigned int i
= 0; i
< paddingLen
; i
++) {
467 SetDNSPacketStatus(DNSPacketStatus::Success
);
471 Result
<uint8_t, nsresult
> DNSPacket::GetRCode() const {
472 if (mBodySize
< 12) {
473 LOG(("DNSPacket::GetRCode - packet too small"));
474 return Err(NS_ERROR_ILLEGAL_VALUE
);
477 return mResponse
[3] & 0x0F;
480 Result
<bool, nsresult
> DNSPacket::RecursionAvailable() const {
481 if (mBodySize
< 12) {
482 LOG(("DNSPacket::GetRCode - packet too small"));
483 return Err(NS_ERROR_ILLEGAL_VALUE
);
486 return mResponse
[3] & 0x80;
489 nsresult
DNSPacket::DecodeInternal(
490 nsCString
& aHost
, enum TrrType aType
, nsCString
& aCname
, bool aAllowRFC1918
,
491 DOHresp
& aResp
, TypeRecordResultType
& aTypeResult
,
492 nsClassHashtable
<nsCStringHashKey
, DOHresp
>& aAdditionalRecords
,
493 uint32_t& aTTL
, const unsigned char* aBuffer
, uint32_t aLen
) {
494 // The response has a 12 byte header followed by the question (returned)
495 // and then the answer. The answer section itself contains the name, type
496 // and class again and THEN the record data.
498 // www.example.com response:
500 // abcd 8180 0001 0001 0000 0000
502 // 0377 7777 0765 7861 6d70 6c65 0363 6f6d 0000 0100 01
504 // 03 7777 7707 6578 616d 706c 6503 636f 6d00 0001 0001
505 // 0000 0080 0004 5db8 d822
507 unsigned int index
= 12;
511 uint16_t extendedError
= UINT16_MAX
;
513 LOG(("doh decode %s %d bytes\n", aHost
.get(), aLen
));
517 if (aLen
< 12 || aBuffer
[0] || aBuffer
[1]) {
518 LOG(("TRR bad incoming DOH, eject!\n"));
519 return NS_ERROR_ILLEGAL_VALUE
;
521 uint8_t rcode
= mResponse
[3] & 0x0F;
522 LOG(("TRR Decode %s RCODE %d\n", PromiseFlatCString(aHost
).get(), rcode
));
524 uint16_t questionRecords
= get16bit(aBuffer
, 4); // qdcount
525 // iterate over the single(?) host name in question
526 while (questionRecords
) {
528 if (aLen
< (index
+ 1)) {
529 LOG(("TRR Decode 1 index: %u size: %u", index
, aLen
));
530 return NS_ERROR_ILLEGAL_VALUE
;
532 length
= static_cast<uint8_t>(aBuffer
[index
]);
537 if (aLen
< (index
+ 1 + length
)) {
538 LOG(("TRR Decode 2 index: %u size: %u len: %u", index
, aLen
, length
));
539 return NS_ERROR_ILLEGAL_VALUE
;
541 host
.Append(((char*)aBuffer
) + index
+ 1, length
);
543 index
+= 1 + length
; // skip length byte + label
545 if (aLen
< (index
+ 4)) {
546 LOG(("TRR Decode 3 index: %u size: %u", index
, aLen
));
547 return NS_ERROR_ILLEGAL_VALUE
;
549 index
+= 4; // skip question's type, class
553 // Figure out the number of answer records from ANCOUNT
554 uint16_t answerRecords
= get16bit(aBuffer
, 6);
556 LOG(("TRR Decode: %d answer records (%u bytes body) %s index=%u\n",
557 answerRecords
, aLen
, host
.get(), index
));
559 while (answerRecords
) {
561 rv
= GetQname(qname
, index
, aBuffer
);
566 if (aLen
< (index
+ 2)) {
567 LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__
, index
+ 2));
568 return NS_ERROR_ILLEGAL_VALUE
;
570 uint16_t TYPE
= get16bit(aBuffer
, index
);
575 if (aLen
< (index
+ 2)) {
576 LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__
, index
+ 2));
577 return NS_ERROR_ILLEGAL_VALUE
;
579 uint16_t CLASS
= get16bit(aBuffer
, index
);
580 if (kDNS_CLASS_IN
!= CLASS
) {
581 LOG(("TRR bad CLASS (%u) at index %d\n", CLASS
, index
));
582 return NS_ERROR_UNEXPECTED
;
586 // 32 bit TTL (seconds)
587 if (aLen
< (index
+ 4)) {
588 LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__
, index
));
589 return NS_ERROR_ILLEGAL_VALUE
;
591 uint32_t TTL
= get32bit(aBuffer
, index
);
595 if (aLen
< (index
+ 2)) {
596 LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__
, index
));
597 return NS_ERROR_ILLEGAL_VALUE
;
599 uint16_t RDLENGTH
= get16bit(aBuffer
, index
);
602 if (aLen
< (index
+ RDLENGTH
)) {
603 LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__
,
605 return NS_ERROR_ILLEGAL_VALUE
;
608 if ((TYPE
!= TRRTYPE_CNAME
) && (TYPE
!= TRRTYPE_HTTPSSVC
) &&
609 (TYPE
!= static_cast<uint16_t>(aType
))) {
610 // Not the same type as was asked for nor CNAME
611 LOG(("TRR: Dohdecode:%d asked for type %d got %d\n", __LINE__
, aType
,
618 // We check if the qname is a case-insensitive match for the host or the
619 // FQDN version of the host
620 bool responseMatchesQuestion
=
621 (qname
.Length() == aHost
.Length() ||
622 (aHost
.Length() == qname
.Length() + 1 && aHost
.Last() == '.')) &&
623 StringBeginsWith(aHost
, qname
, nsCaseInsensitiveCStringComparator
);
625 if (responseMatchesQuestion
) {
627 // - A (TYPE 1): 4 bytes
628 // - AAAA (TYPE 28): 16 bytes
629 // - NS (TYPE 2): N bytes
634 LOG(("TRR bad length for A (%u)\n", RDLENGTH
));
635 return NS_ERROR_UNEXPECTED
;
637 rv
= aResp
.Add(TTL
, aBuffer
, index
, RDLENGTH
, aAllowRFC1918
);
640 ("TRR:DohDecode failed: local IP addresses or unknown IP "
646 if (RDLENGTH
!= 16) {
647 LOG(("TRR bad length for AAAA (%u)\n", RDLENGTH
));
648 return NS_ERROR_UNEXPECTED
;
650 rv
= aResp
.Add(TTL
, aBuffer
, index
, RDLENGTH
, aAllowRFC1918
);
652 LOG(("TRR got unique/local IPv6 address!\n"));
660 if (aCname
.IsEmpty()) {
662 unsigned int qnameindex
= index
;
663 rv
= GetQname(qname
, qnameindex
, aBuffer
);
667 if (!qname
.IsEmpty()) {
670 LOG(("DNSPacket::DohDecode CNAME host %s => %s\n", host
.get(),
673 LOG(("DNSPacket::DohDecode empty CNAME for host %s!\n",
677 LOG(("DNSPacket::DohDecode CNAME - ignoring another entry\n"));
681 // TXT record RRDATA sections are a series of character-strings
682 // each character string is a length byte followed by that many data
685 unsigned int txtIndex
= index
;
686 uint16_t available
= RDLENGTH
;
688 while (available
> 0) {
689 uint8_t characterStringLen
= aBuffer
[txtIndex
++];
691 if (characterStringLen
> available
) {
692 LOG(("DNSPacket::DohDecode MALFORMED TXT RECORD\n"));
695 txt
.Append((const char*)(&aBuffer
[txtIndex
]), characterStringLen
);
696 txtIndex
+= characterStringLen
;
697 available
-= characterStringLen
;
700 if (!aTypeResult
.is
<TypeRecordTxt
>()) {
701 aTypeResult
= AsVariant(CopyableTArray
<nsCString
>());
705 auto& results
= aTypeResult
.as
<TypeRecordTxt
>();
706 results
.AppendElement(txt
);
711 LOG(("DNSPacket::DohDecode TXT host %s => %s\n", host
.get(),
716 case TRRTYPE_HTTPSSVC
: {
718 int32_t lastSvcParamKey
= -1;
720 unsigned int svcbIndex
= index
;
721 CheckedInt
<uint16_t> available
= RDLENGTH
;
723 // Should have at least 2 bytes for the priority and one for the
725 if (available
.value() < 3) {
726 return NS_ERROR_UNEXPECTED
;
729 parsed
.mSvcFieldPriority
= get16bit(aBuffer
, svcbIndex
);
732 rv
= GetQname(parsed
.mSvcDomainName
, svcbIndex
, aBuffer
);
737 if (parsed
.mSvcDomainName
.IsEmpty()) {
738 if (parsed
.mSvcFieldPriority
== 0) {
739 // For AliasMode SVCB RRs, a TargetName of "." indicates that the
740 // service is not available or does not exist.
744 // For ServiceMode SVCB RRs, if TargetName has the value ".",
745 // then the owner name of this record MUST be used as
746 // the effective TargetName.
747 // When the qname is port prefix name, we need to use the
748 // original host name as TargetName.
750 parsed
.mSvcDomainName
= *mOriginHost
;
752 parsed
.mSvcDomainName
= qname
;
756 available
-= (svcbIndex
- index
);
757 if (!available
.isValid()) {
758 return NS_ERROR_UNEXPECTED
;
761 while (available
.value() >= 4) {
762 // Every SvcFieldValues must have at least 4 bytes for the
763 // SvcParamKey (2 bytes) and length of SvcParamValue (2 bytes)
764 // If the length ever goes above the available data, meaning if
765 // available ever underflows, then that is an error.
766 struct SvcFieldValue value
;
767 uint16_t key
= get16bit(aBuffer
, svcbIndex
);
770 // 2.2 Clients MUST consider an RR malformed if SvcParamKeys are
771 // not in strictly increasing numeric order.
772 if (key
<= lastSvcParamKey
) {
773 LOG(("SvcParamKeys not in increasing order"));
774 return NS_ERROR_UNEXPECTED
;
776 lastSvcParamKey
= key
;
778 uint16_t len
= get16bit(aBuffer
, svcbIndex
);
781 available
-= 4 + len
;
782 if (!available
.isValid()) {
783 return NS_ERROR_UNEXPECTED
;
786 rv
= ParseSvcParam(svcbIndex
, key
, value
, len
, aBuffer
);
792 // If this is an unknown key, we will simply ignore it.
793 // We also don't need to record SvcParamKeyMandatory
794 if (key
== SvcParamKeyMandatory
|| !IsValidSvcParamKey(key
)) {
798 if (value
.mValue
.is
<SvcParamIpv4Hint
>() ||
799 value
.mValue
.is
<SvcParamIpv6Hint
>()) {
800 parsed
.mHasIPHints
= true;
802 if (value
.mValue
.is
<SvcParamEchConfig
>()) {
803 parsed
.mHasEchConfig
= true;
804 parsed
.mEchConfig
= value
.mValue
.as
<SvcParamEchConfig
>().mValue
;
806 if (value
.mValue
.is
<SvcParamODoHConfig
>()) {
807 parsed
.mODoHConfig
= value
.mValue
.as
<SvcParamODoHConfig
>().mValue
;
809 parsed
.mSvcFieldValue
.AppendElement(value
);
812 if (aType
!= TRRTYPE_HTTPSSVC
) {
813 // Ignore the entry that we just parsed if we didn't ask for it.
817 // Check for AliasForm
818 if (aCname
.IsEmpty() && parsed
.mSvcFieldPriority
== 0) {
819 // Alias form SvcDomainName must not have the "." value (empty)
820 if (parsed
.mSvcDomainName
.IsEmpty()) {
821 return NS_ERROR_UNEXPECTED
;
823 aCname
= parsed
.mSvcDomainName
;
824 // If aliasForm is present, Service form must be ignored.
825 aTypeResult
= mozilla::AsVariant(Nothing());
827 LOG(("DNSPacket::DohDecode HTTPSSVC AliasForm host %s => %s\n",
828 host
.get(), aCname
.get()));
832 if (!aTypeResult
.is
<TypeRecordHTTPSSVC
>()) {
833 aTypeResult
= mozilla::AsVariant(CopyableTArray
<SVCB
>());
836 auto& results
= aTypeResult
.as
<TypeRecordHTTPSSVC
>();
837 results
.AppendElement(parsed
);
843 // skip unknown record types
844 LOG(("TRR unsupported TYPE (%u) RDLENGTH %u\n", TYPE
, RDLENGTH
));
848 LOG(("TRR asked for %s data but got %s\n", aHost
.get(), qname
.get()));
852 LOG(("done with record type %u len %u index now %u of %u\n", TYPE
, RDLENGTH
,
858 uint16_t nsRecords
= get16bit(aBuffer
, 8);
859 LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords
, aLen
));
861 rv
= PassQName(index
, aBuffer
);
866 if (aLen
< (index
+ 8)) {
867 return NS_ERROR_ILLEGAL_VALUE
;
874 if (aLen
< (index
+ 2)) {
875 return NS_ERROR_ILLEGAL_VALUE
;
877 uint16_t RDLENGTH
= get16bit(aBuffer
, index
);
879 if (aLen
< (index
+ RDLENGTH
)) {
880 return NS_ERROR_ILLEGAL_VALUE
;
883 LOG(("done with nsRecord now %u of %u\n", index
, aLen
));
887 // additional resource records
888 uint16_t arRecords
= get16bit(aBuffer
, 10);
889 LOG(("TRR Decode: %d additional resource records (%u bytes body)\n",
894 rv
= GetQname(qname
, index
, aBuffer
);
896 LOG(("Bad qname for additional record"));
900 if (aLen
< (index
+ 8)) {
901 return NS_ERROR_ILLEGAL_VALUE
;
903 uint16_t type
= get16bit(aBuffer
, index
);
905 // The next two bytes encode class
906 // (or udpPayloadSize when type is TRRTYPE_OPT)
907 uint16_t cls
= get16bit(aBuffer
, index
);
909 // The next 4 bytes encode TTL
910 // (or extRCode + ednsVersion + flags when type is TRRTYPE_OPT)
911 uint32_t ttl
= get32bit(aBuffer
, index
);
913 // cls and ttl are unused when type is TRRTYPE_OPT
916 if (aLen
< (index
+ 2)) {
917 LOG(("Record too small"));
918 return NS_ERROR_ILLEGAL_VALUE
;
921 uint16_t rdlength
= get16bit(aBuffer
, index
);
923 if (aLen
< (index
+ rdlength
)) {
924 LOG(("rdlength too big"));
925 return NS_ERROR_ILLEGAL_VALUE
;
928 auto parseRecord
= [&]() {
929 LOG(("Parsing additional record type: %u", type
));
930 auto* entry
= aAdditionalRecords
.GetOrInsertNew(qname
);
934 if (kDNS_CLASS_IN
!= cls
) {
935 LOG(("NOT IN - returning"));
939 LOG(("TRR bad length for A (%u)\n", rdlength
));
942 rv
= entry
->Add(ttl
, aBuffer
, index
, rdlength
, aAllowRFC1918
);
945 ("TRR:DohDecode failed: local IP addresses or unknown IP "
951 if (kDNS_CLASS_IN
!= cls
) {
952 LOG(("NOT IN - returning"));
955 if (rdlength
!= 16) {
956 LOG(("TRR bad length for AAAA (%u)\n", rdlength
));
959 rv
= entry
->Add(ttl
, aBuffer
, index
, rdlength
, aAllowRFC1918
);
961 LOG(("TRR got unique/local IPv6 address!\n"));
965 case TRRTYPE_OPT
: { // OPT
966 LOG(("Parsing opt rdlen: %u", rdlength
));
967 unsigned int offset
= 0;
968 while (offset
+ 2 <= rdlength
) {
969 uint16_t optCode
= get16bit(aBuffer
, index
+ offset
);
970 LOG(("optCode: %u", optCode
));
972 if (offset
+ 2 > rdlength
) {
975 uint16_t optLen
= get16bit(aBuffer
, index
+ offset
);
976 LOG(("optLen: %u", optLen
));
978 if (offset
+ optLen
> rdlength
) {
979 LOG(("offset: %u, optLen: %u, rdlen: %u", offset
, optLen
,
984 LOG(("OPT: code: %u len:%u", optCode
, optLen
));
991 // optCode == 15; Extended DNS error
993 if (offset
+ 2 > rdlength
|| optLen
< 2) {
996 extendedError
= get16bit(aBuffer
, index
+ offset
);
998 LOG(("Extended error code: %u message: %s", extendedError
,
999 nsAutoCString((char*)aBuffer
+ index
+ offset
+ 2, optLen
- 2)
1013 LOG(("done with additional rr now %u of %u\n", index
, aLen
));
1017 if (index
!= aLen
) {
1018 LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n",
1020 // failed to parse 100%, do not continue
1021 return NS_ERROR_ILLEGAL_VALUE
;
1024 if (aType
== TRRTYPE_NS
&& rcode
!= 0) {
1025 return NS_ERROR_UNKNOWN_HOST
;
1028 if ((aType
!= TRRTYPE_NS
) && aCname
.IsEmpty() && aResp
.mAddresses
.IsEmpty() &&
1029 aTypeResult
.is
<TypeRecordEmpty
>()) {
1030 // no entries were stored!
1031 LOG(("TRR: No entries were stored!\n"));
1033 if (extendedError
!= UINT16_MAX
&& hardFail(extendedError
)) {
1034 return NS_ERROR_DEFINITIVE_UNKNOWN_HOST
;
1036 return NS_ERROR_UNKNOWN_HOST
;
1039 // https://tools.ietf.org/html/draft-ietf-dnsop-svcb-httpssvc-03#page-14
1040 // If one or more SVCB records of ServiceForm SvcRecordType are returned for
1041 // HOST, clients should select the highest-priority option with acceptable
1043 if (aTypeResult
.is
<TypeRecordHTTPSSVC
>()) {
1044 auto& results
= aTypeResult
.as
<TypeRecordHTTPSSVC
>();
1052 // DohDecode() collects the TTL and the IP addresses in the response
1054 nsresult
DNSPacket::Decode(
1055 nsCString
& aHost
, enum TrrType aType
, nsCString
& aCname
, bool aAllowRFC1918
,
1056 DOHresp
& aResp
, TypeRecordResultType
& aTypeResult
,
1057 nsClassHashtable
<nsCStringHashKey
, DOHresp
>& aAdditionalRecords
,
1060 DecodeInternal(aHost
, aType
, aCname
, aAllowRFC1918
, aResp
, aTypeResult
,
1061 aAdditionalRecords
, aTTL
, mResponse
, mBodySize
);
1062 SetDNSPacketStatus(NS_SUCCEEDED(rv
) ? DNSPacketStatus::Success
1063 : DNSPacketStatus::DecodeError
);
1068 } // namespace mozilla