Bug 1838739 - Initialize result of SetAsGPUOutOfMemoryError. r=webgpu-reviewers,nical
[gecko.git] / netwerk / dns / DNSPacket.cpp
blob157effad317ab0c175529a942c94a953f0d58e1d
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/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)
35 6, // DNSSEC Boggus
36 7, // Signature expired
37 8, // Signature not yet valid
38 9, // DNSKEY Missing
39 10, // RRSIG missing
40 11, // No ZONE Key Bit set
41 12, // NSEC Missing
42 17, // Filtered
45 for (const auto& err : noFallbackErrors) {
46 if (code == err) {
47 return true;
50 return false;
53 // static
54 nsresult DNSPacket::ParseSvcParam(unsigned int svcbIndex, uint16_t key,
55 SvcFieldValue& field, uint16_t length,
56 const unsigned char* aBuffer) {
57 switch (key) {
58 case SvcParamKeyMandatory: {
59 if (length % 2 != 0) {
60 // This key should encode a list of uint16_t
61 return NS_ERROR_UNEXPECTED;
63 while (length > 0) {
64 uint16_t mandatoryKey = get16bit(aBuffer, svcbIndex);
65 length -= 2;
66 svcbIndex += 2;
68 if (!IsValidSvcParamKey(mandatoryKey)) {
69 LOG(("The mandatory field includes a key we don't support %u",
70 mandatoryKey));
71 return NS_ERROR_UNEXPECTED;
74 break;
76 case SvcParamKeyAlpn: {
77 field.mValue = AsVariant(SvcParamAlpn());
78 auto& alpnArray = field.mValue.as<SvcParamAlpn>().mValue;
79 while (length > 0) {
80 uint8_t alpnIdLength = aBuffer[svcbIndex++];
81 length -= 1;
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;
91 break;
93 case SvcParamKeyNoDefaultAlpn: {
94 if (length != 0) {
95 // This key should not contain a value
96 return NS_ERROR_UNEXPECTED;
98 field.mValue = AsVariant(SvcParamNoDefaultAlpn{});
99 break;
101 case SvcParamKeyPort: {
102 if (length != 2) {
103 // This key should only encode a uint16_t
104 return NS_ERROR_UNEXPECTED;
106 field.mValue =
107 AsVariant(SvcParamPort{.mValue = get16bit(aBuffer, svcbIndex)});
108 break;
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;
118 while (length > 0) {
119 NetAddr addr;
120 addr.inet.family = AF_INET;
121 addr.inet.port = 0;
122 addr.inet.ip = ntohl(get32bit(aBuffer, svcbIndex));
123 ipv4array.AppendElement(addr);
124 length -= 4;
125 svcbIndex += 4;
127 break;
129 case SvcParamKeyEchConfig: {
130 field.mValue = AsVariant(SvcParamEchConfig{
131 .mValue = nsCString((const char*)(&aBuffer[svcbIndex]), length)});
132 break;
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;
142 while (length > 0) {
143 NetAddr addr;
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);
152 length -= 16;
153 // no need to increase svcbIndex - we did it in the for above.
155 break;
157 case SvcParamKeyODoHConfig: {
158 field.mValue = AsVariant(SvcParamODoHConfig{
159 .mValue = nsCString((const char*)(&aBuffer[svcbIndex]), length)});
160 break;
162 default: {
163 // Unespected type. We'll just ignore it.
164 return NS_OK;
165 break;
168 return NS_OK;
171 nsresult DNSPacket::PassQName(unsigned int& index,
172 const unsigned char* aBuffer) {
173 uint8_t length;
174 do {
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;
185 index += 2;
186 break;
188 if (length & 0xc0) {
189 LOG(("TRR: illegal label length byte (%x) at index %d\n", length, index));
190 return NS_ERROR_ILLEGAL_VALUE;
192 // pass label
193 if (mBodySize < (index + 1 + length)) {
194 LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index));
195 return NS_ERROR_ILLEGAL_VALUE;
197 index += 1 + length;
198 } while (length);
199 return NS_OK;
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) {
206 uint8_t clength = 0;
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
210 do {
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];
223 if (!endindex) {
224 // only update on the first "jump"
225 endindex = cindex + 2;
227 cindex = newpos;
228 continue;
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;
236 cindex++;
238 if (clength) {
239 if (!aQname.IsEmpty()) {
240 aQname.Append(".");
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);
250 if (!loop) {
251 LOG(("DNSPacket::DohDecode pointer loop error\n"));
252 return NS_ERROR_ILLEGAL_VALUE;
254 if (!endindex) {
255 // there was no "jump"
256 endindex = cindex;
258 aIndex = endindex;
259 return NS_OK;
262 nsresult DOHresp::Add(uint32_t TTL, unsigned char const* dns,
263 unsigned int index, uint16_t len, bool aLocalAllowed) {
264 NetAddr addr;
265 if (4 == len) {
266 // IPv4
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) {
271 // IPv6
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];
279 } else {
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
289 // lowest number.
290 if (mTtl < TTL) {
291 mTtl = TTL;
294 if (LOG_ENABLED()) {
295 char buf[128];
296 addr.ToStringBuffer(buf, sizeof(buf));
297 LOG(("DOHresp:Add %s\n", buf));
299 mAddresses.AppendElement(addr);
300 return NS_OK;
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;
310 uint32_t count;
311 nsresult rv =
312 aInputStream->Read((char*)mResponse + mBodySize, aCount, &count);
313 if (NS_FAILED(rv)) {
314 return rv;
316 MOZ_ASSERT(count == aCount);
317 mBodySize += aCount;
318 return NS_OK;
321 const uint8_t kDNS_CLASS_IN = 1;
323 nsresult DNSPacket::EncodeRequest(nsCString& aBody, const nsACString& aHost,
324 uint16_t aType, bool aDisableECS) {
325 aBody.Truncate();
326 // Header
327 aBody += '\0';
328 aBody += '\0'; // 16 bit id
329 aBody += 0x01; // |QR| Opcode |AA|TC|RD| Set the RD bit
330 aBody += '\0'; // |RA| Z | RCODE |
331 aBody += '\0';
332 aBody += 1; // QDCOUNT (number of entries in the question section)
333 aBody += '\0';
334 aBody += '\0'; // ANCOUNT
335 aBody += '\0';
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)
343 // Question
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
351 int32_t index = 0;
352 int32_t offset = 0;
353 do {
354 bool dotFound = false;
355 int32_t labelLength;
356 index = aHost.FindChar('.', offset);
357 if (kNotFound != index) {
358 dotFound = true;
359 labelLength = index - offset;
360 } else {
361 labelLength = aHost.Length() - offset;
363 if (labelLength > 63) {
364 // too long label!
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);
371 aBody.Append(label);
373 if (!dotFound) {
374 aBody += '\0'; // terminate with a final zero
375 break;
377 offset += labelLength + 1; // move over label and dot
378 } while (true);
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) |
388 aBody += '\0';
389 aBody += 41; // TYPE | u_int16_t | OPT (41) |
390 aBody += 16; // CLASS | u_int16_t | requestor's UDP payload size |
391 aBody +=
392 '\0'; // advertise 4K (high-byte: 16 | low-byte: 0), ignored by DoH
393 aBody += '\0'; // TTL | u_int32_t | extended RCODE and flags |
394 aBody += '\0';
395 aBody += '\0';
396 aBody += '\0';
398 // calculate padding length
399 unsigned int paddingLen = 0;
400 unsigned int rdlen = 0;
401 bool padding = StaticPrefs::network_trr_padding();
402 if (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;
408 if (aDisableECS) {
409 // 8 bytes for disabling ecs
410 packetLen += 8;
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
420 if (padTo > 0) {
421 paddingLen = (padTo - (packetLen % padTo)) % padTo;
423 // padding header + padding length
424 rdlen += 4 + paddingLen;
426 if (aDisableECS) {
427 rdlen += 8;
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
437 if (aDisableECS) {
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 |
449 aBody += '\0';
451 // ADDRESS, minimum number of octets == nothing because zero bits
454 if (padding) {
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++) {
462 aBody += '\0';
467 SetDNSPacketStatus(DNSPacketStatus::Success);
468 return NS_OK;
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:
499 // header:
500 // abcd 8180 0001 0001 0000 0000
501 // the question:
502 // 0377 7777 0765 7861 6d70 6c65 0363 6f6d 0000 0100 01
503 // the answer:
504 // 03 7777 7707 6578 616d 706c 6503 636f 6d00 0001 0001
505 // 0000 0080 0004 5db8 d822
507 unsigned int index = 12;
508 uint8_t length;
509 nsAutoCString host;
510 nsresult rv;
511 uint16_t extendedError = UINT16_MAX;
513 LOG(("doh decode %s %d bytes\n", aHost.get(), aLen));
515 aCname.Truncate();
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) {
527 do {
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]);
533 if (length) {
534 if (host.Length()) {
535 host.Append(".");
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
544 } while (length);
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
550 questionRecords--;
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) {
560 nsAutoCString qname;
561 rv = GetQname(qname, index, aBuffer);
562 if (NS_FAILED(rv)) {
563 return rv;
565 // 16 bit TYPE
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);
572 index += 2;
574 // 16 bit class
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;
584 index += 2;
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);
592 index += 4;
594 // 16 bit RDLENGTH
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);
600 index += 2;
602 if (aLen < (index + RDLENGTH)) {
603 LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__,
604 RDLENGTH, index));
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,
612 TYPE));
613 index += RDLENGTH;
614 answerRecords--;
615 continue;
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) {
626 // RDATA
627 // - A (TYPE 1): 4 bytes
628 // - AAAA (TYPE 28): 16 bytes
629 // - NS (TYPE 2): N bytes
631 switch (TYPE) {
632 case TRRTYPE_A:
633 if (RDLENGTH != 4) {
634 LOG(("TRR bad length for A (%u)\n", RDLENGTH));
635 return NS_ERROR_UNEXPECTED;
637 rv = aResp.Add(TTL, aBuffer, index, RDLENGTH, aAllowRFC1918);
638 if (NS_FAILED(rv)) {
639 LOG(
640 ("TRR:DohDecode failed: local IP addresses or unknown IP "
641 "family\n"));
642 return rv;
644 break;
645 case TRRTYPE_AAAA:
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);
651 if (NS_FAILED(rv)) {
652 LOG(("TRR got unique/local IPv6 address!\n"));
653 return rv;
655 break;
657 case TRRTYPE_NS:
658 break;
659 case TRRTYPE_CNAME:
660 if (aCname.IsEmpty()) {
661 nsAutoCString qname;
662 unsigned int qnameindex = index;
663 rv = GetQname(qname, qnameindex, aBuffer);
664 if (NS_FAILED(rv)) {
665 return rv;
667 if (!qname.IsEmpty()) {
668 ToLowerCase(qname);
669 aCname = qname;
670 LOG(("DNSPacket::DohDecode CNAME host %s => %s\n", host.get(),
671 aCname.get()));
672 } else {
673 LOG(("DNSPacket::DohDecode empty CNAME for host %s!\n",
674 host.get()));
676 } else {
677 LOG(("DNSPacket::DohDecode CNAME - ignoring another entry\n"));
679 break;
680 case TRRTYPE_TXT: {
681 // TXT record RRDATA sections are a series of character-strings
682 // each character string is a length byte followed by that many data
683 // bytes
684 nsAutoCString txt;
685 unsigned int txtIndex = index;
686 uint16_t available = RDLENGTH;
688 while (available > 0) {
689 uint8_t characterStringLen = aBuffer[txtIndex++];
690 available--;
691 if (characterStringLen > available) {
692 LOG(("DNSPacket::DohDecode MALFORMED TXT RECORD\n"));
693 break;
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);
708 if (aTTL > TTL) {
709 aTTL = TTL;
711 LOG(("DNSPacket::DohDecode TXT host %s => %s\n", host.get(),
712 txt.get()));
714 break;
716 case TRRTYPE_HTTPSSVC: {
717 struct SVCB parsed;
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
724 // qname length.
725 if (available.value() < 3) {
726 return NS_ERROR_UNEXPECTED;
729 parsed.mSvcFieldPriority = get16bit(aBuffer, svcbIndex);
730 svcbIndex += 2;
732 rv = GetQname(parsed.mSvcDomainName, svcbIndex, aBuffer);
733 if (NS_FAILED(rv)) {
734 return rv;
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.
741 continue;
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.
749 if (mOriginHost) {
750 parsed.mSvcDomainName = *mOriginHost;
751 } else {
752 parsed.mSvcDomainName = qname;
756 available -= (svcbIndex - index);
757 if (!available.isValid()) {
758 return NS_ERROR_UNEXPECTED;
760 aTTL = TTL;
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);
768 svcbIndex += 2;
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);
779 svcbIndex += 2;
781 available -= 4 + len;
782 if (!available.isValid()) {
783 return NS_ERROR_UNEXPECTED;
786 rv = ParseSvcParam(svcbIndex, key, value, len, aBuffer);
787 if (NS_FAILED(rv)) {
788 return rv;
790 svcbIndex += len;
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)) {
795 continue;
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.
814 break;
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());
826 ToLowerCase(aCname);
827 LOG(("DNSPacket::DohDecode HTTPSSVC AliasForm host %s => %s\n",
828 host.get(), aCname.get()));
829 break;
832 if (!aTypeResult.is<TypeRecordHTTPSSVC>()) {
833 aTypeResult = mozilla::AsVariant(CopyableTArray<SVCB>());
836 auto& results = aTypeResult.as<TypeRecordHTTPSSVC>();
837 results.AppendElement(parsed);
840 break;
842 default:
843 // skip unknown record types
844 LOG(("TRR unsupported TYPE (%u) RDLENGTH %u\n", TYPE, RDLENGTH));
845 break;
847 } else {
848 LOG(("TRR asked for %s data but got %s\n", aHost.get(), qname.get()));
851 index += RDLENGTH;
852 LOG(("done with record type %u len %u index now %u of %u\n", TYPE, RDLENGTH,
853 index, aLen));
854 answerRecords--;
857 // NSCOUNT
858 uint16_t nsRecords = get16bit(aBuffer, 8);
859 LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords, aLen));
860 while (nsRecords) {
861 rv = PassQName(index, aBuffer);
862 if (NS_FAILED(rv)) {
863 return rv;
866 if (aLen < (index + 8)) {
867 return NS_ERROR_ILLEGAL_VALUE;
869 index += 2; // type
870 index += 2; // class
871 index += 4; // ttl
873 // 16 bit RDLENGTH
874 if (aLen < (index + 2)) {
875 return NS_ERROR_ILLEGAL_VALUE;
877 uint16_t RDLENGTH = get16bit(aBuffer, index);
878 index += 2;
879 if (aLen < (index + RDLENGTH)) {
880 return NS_ERROR_ILLEGAL_VALUE;
882 index += RDLENGTH;
883 LOG(("done with nsRecord now %u of %u\n", index, aLen));
884 nsRecords--;
887 // additional resource records
888 uint16_t arRecords = get16bit(aBuffer, 10);
889 LOG(("TRR Decode: %d additional resource records (%u bytes body)\n",
890 arRecords, aLen));
892 while (arRecords) {
893 nsAutoCString qname;
894 rv = GetQname(qname, index, aBuffer);
895 if (NS_FAILED(rv)) {
896 LOG(("Bad qname for additional record"));
897 return rv;
900 if (aLen < (index + 8)) {
901 return NS_ERROR_ILLEGAL_VALUE;
903 uint16_t type = get16bit(aBuffer, index);
904 index += 2;
905 // The next two bytes encode class
906 // (or udpPayloadSize when type is TRRTYPE_OPT)
907 uint16_t cls = get16bit(aBuffer, index);
908 index += 2;
909 // The next 4 bytes encode TTL
910 // (or extRCode + ednsVersion + flags when type is TRRTYPE_OPT)
911 uint32_t ttl = get32bit(aBuffer, index);
912 index += 4;
913 // cls and ttl are unused when type is TRRTYPE_OPT
915 // 16 bit RDLENGTH
916 if (aLen < (index + 2)) {
917 LOG(("Record too small"));
918 return NS_ERROR_ILLEGAL_VALUE;
921 uint16_t rdlength = get16bit(aBuffer, index);
922 index += 2;
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);
932 switch (type) {
933 case TRRTYPE_A:
934 if (kDNS_CLASS_IN != cls) {
935 LOG(("NOT IN - returning"));
936 return;
938 if (rdlength != 4) {
939 LOG(("TRR bad length for A (%u)\n", rdlength));
940 return;
942 rv = entry->Add(ttl, aBuffer, index, rdlength, aAllowRFC1918);
943 if (NS_FAILED(rv)) {
944 LOG(
945 ("TRR:DohDecode failed: local IP addresses or unknown IP "
946 "family\n"));
947 return;
949 break;
950 case TRRTYPE_AAAA:
951 if (kDNS_CLASS_IN != cls) {
952 LOG(("NOT IN - returning"));
953 return;
955 if (rdlength != 16) {
956 LOG(("TRR bad length for AAAA (%u)\n", rdlength));
957 return;
959 rv = entry->Add(ttl, aBuffer, index, rdlength, aAllowRFC1918);
960 if (NS_FAILED(rv)) {
961 LOG(("TRR got unique/local IPv6 address!\n"));
962 return;
964 break;
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));
971 offset += 2;
972 if (offset + 2 > rdlength) {
973 break;
975 uint16_t optLen = get16bit(aBuffer, index + offset);
976 LOG(("optLen: %u", optLen));
977 offset += 2;
978 if (offset + optLen > rdlength) {
979 LOG(("offset: %u, optLen: %u, rdlen: %u", offset, optLen,
980 rdlength));
981 break;
984 LOG(("OPT: code: %u len:%u", optCode, optLen));
986 if (optCode != 15) {
987 offset += optLen;
988 continue;
991 // optCode == 15; Extended DNS error
993 if (offset + 2 > rdlength || optLen < 2) {
994 break;
996 extendedError = get16bit(aBuffer, index + offset);
998 LOG(("Extended error code: %u message: %s", extendedError,
999 nsAutoCString((char*)aBuffer + index + offset + 2, optLen - 2)
1000 .get()));
1001 offset += optLen;
1003 break;
1005 default:
1006 break;
1010 parseRecord();
1012 index += rdlength;
1013 LOG(("done with additional rr now %u of %u\n", index, aLen));
1014 arRecords--;
1017 if (index != aLen) {
1018 LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n",
1019 index, aLen));
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
1042 // parameters.
1043 if (aTypeResult.is<TypeRecordHTTPSSVC>()) {
1044 auto& results = aTypeResult.as<TypeRecordHTTPSSVC>();
1045 results.Sort();
1048 return NS_OK;
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,
1058 uint32_t& aTTL) {
1059 nsresult rv =
1060 DecodeInternal(aHost, aType, aCname, aAllowRFC1918, aResp, aTypeResult,
1061 aAdditionalRecords, aTTL, mResponse, mBodySize);
1062 SetDNSPacketStatus(NS_SUCCEEDED(rv) ? DNSPacketStatus::Success
1063 : DNSPacketStatus::DecodeError);
1064 return rv;
1067 } // namespace net
1068 } // namespace mozilla