2018-02-01 12:20:49 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
2018-11-30 18:39:55 +03:00
|
|
|
/* vim:set ts=4 sw=2 sts=2 et cin: */
|
2018-02-01 12:20:49 +03:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
#include "DNS.h"
|
|
|
|
#include "nsCharSeparatedTokenizer.h"
|
|
|
|
#include "nsContentUtils.h"
|
2020-03-04 19:11:16 +03:00
|
|
|
#include "nsHttpHandler.h"
|
2018-02-01 12:20:49 +03:00
|
|
|
#include "nsIHttpChannel.h"
|
|
|
|
#include "nsIHttpChannelInternal.h"
|
|
|
|
#include "nsIIOService.h"
|
|
|
|
#include "nsIInputStream.h"
|
|
|
|
#include "nsISupportsBase.h"
|
|
|
|
#include "nsISupportsUtils.h"
|
2020-01-14 15:11:46 +03:00
|
|
|
#include "nsITimedChannel.h"
|
2018-02-01 12:20:49 +03:00
|
|
|
#include "nsIUploadChannel2.h"
|
2020-03-18 18:12:52 +03:00
|
|
|
#include "nsIURIMutator.h"
|
2018-02-01 12:20:49 +03:00
|
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "nsStringStream.h"
|
|
|
|
#include "nsThreadUtils.h"
|
|
|
|
#include "nsURLHelper.h"
|
|
|
|
#include "TRR.h"
|
|
|
|
#include "TRRService.h"
|
2020-06-22 14:10:21 +03:00
|
|
|
#include "TRRServiceChannel.h"
|
2020-03-18 18:13:09 +03:00
|
|
|
#include "TRRLoadInfo.h"
|
2018-02-01 12:20:49 +03:00
|
|
|
|
|
|
|
#include "mozilla/Base64.h"
|
|
|
|
#include "mozilla/DebugOnly.h"
|
|
|
|
#include "mozilla/Logging.h"
|
|
|
|
#include "mozilla/Preferences.h"
|
2019-11-05 13:45:21 +03:00
|
|
|
#include "mozilla/StaticPrefs_network.h"
|
2020-03-04 19:11:16 +03:00
|
|
|
#include "mozilla/SyncRunnable.h"
|
2018-02-01 12:20:49 +03:00
|
|
|
#include "mozilla/Telemetry.h"
|
|
|
|
#include "mozilla/TimeStamp.h"
|
|
|
|
#include "mozilla/Tokenizer.h"
|
2020-02-11 19:20:08 +03:00
|
|
|
#include "mozilla/UniquePtr.h"
|
2018-02-01 12:20:49 +03:00
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace net {
|
|
|
|
|
|
|
|
#undef LOG
|
|
|
|
extern mozilla::LazyLogModule gHostResolverLog;
|
|
|
|
#define LOG(args) MOZ_LOG(gHostResolverLog, mozilla::LogLevel::Debug, args)
|
|
|
|
#define LOG_ENABLED() \
|
|
|
|
MOZ_LOG_TEST(mozilla::net::gHostResolverLog, mozilla::LogLevel::Debug)
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS(TRR, nsIHttpPushListener, nsIInterfaceRequestor,
|
|
|
|
nsIStreamListener, nsIRunnable)
|
|
|
|
|
|
|
|
const uint8_t kDNS_CLASS_IN = 1;
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TRR::Notify(nsITimer* aTimer) {
|
|
|
|
if (aTimer == mTimeout) {
|
|
|
|
mTimeout = nullptr;
|
|
|
|
Cancel();
|
|
|
|
} else {
|
|
|
|
MOZ_CRASH("Unknown timer");
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert a given host request to a DOH 'body'
|
|
|
|
//
|
2018-08-13 18:45:15 +03:00
|
|
|
nsresult TRR::DohEncode(nsCString& aBody, bool aDisableECS) {
|
2018-02-01 12:20:49 +03:00
|
|
|
aBody.Truncate();
|
|
|
|
// Header
|
|
|
|
aBody += '\0';
|
|
|
|
aBody += '\0'; // 16 bit id
|
2018-03-05 10:47:52 +03:00
|
|
|
aBody += 0x01; // |QR| Opcode |AA|TC|RD| Set the RD bit
|
2018-02-01 12:20:49 +03:00
|
|
|
aBody += '\0'; // |RA| Z | RCODE |
|
|
|
|
aBody += '\0';
|
|
|
|
aBody += 1; // QDCOUNT (number of entries in the question section)
|
|
|
|
aBody += '\0';
|
|
|
|
aBody += '\0'; // ANCOUNT
|
|
|
|
aBody += '\0';
|
|
|
|
aBody += '\0'; // NSCOUNT
|
2018-08-13 18:45:15 +03:00
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
aBody += '\0'; // ARCOUNT
|
2018-08-13 18:45:15 +03:00
|
|
|
aBody += aDisableECS ? 1 : '\0'; // ARCOUNT low byte for EDNS(0)
|
2018-02-01 12:20:49 +03:00
|
|
|
|
|
|
|
// Question
|
|
|
|
|
|
|
|
// The input host name should be converted to a sequence of labels, where
|
|
|
|
// each label consists of a length octet followed by that number of
|
|
|
|
// octets. The domain name terminates with the zero length octet for the
|
|
|
|
// null label of the root.
|
|
|
|
// Followed by 16 bit QTYPE and 16 bit QCLASS
|
|
|
|
|
|
|
|
int32_t index = 0;
|
|
|
|
int32_t offset = 0;
|
|
|
|
do {
|
|
|
|
bool dotFound = false;
|
|
|
|
int32_t labelLength;
|
|
|
|
index = mHost.FindChar('.', offset);
|
|
|
|
if (kNotFound != index) {
|
|
|
|
dotFound = true;
|
|
|
|
labelLength = index - offset;
|
|
|
|
} else {
|
|
|
|
labelLength = mHost.Length() - offset;
|
|
|
|
}
|
|
|
|
if (labelLength > 63) {
|
|
|
|
// too long label!
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
2020-03-27 17:22:53 +03:00
|
|
|
if (labelLength > 0) {
|
|
|
|
aBody += static_cast<unsigned char>(labelLength);
|
|
|
|
nsDependentCSubstring label = Substring(mHost, offset, labelLength);
|
|
|
|
aBody.Append(label);
|
|
|
|
}
|
2018-02-01 12:20:49 +03:00
|
|
|
if (!dotFound) {
|
|
|
|
aBody += '\0'; // terminate with a final zero
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
offset += labelLength + 1; // move over label and dot
|
2018-04-30 19:46:04 +03:00
|
|
|
} while (true);
|
2018-02-01 12:20:49 +03:00
|
|
|
|
2020-05-04 22:56:51 +03:00
|
|
|
aBody += static_cast<uint8_t>(mType >> 8); // upper 8 bit TYPE
|
2018-02-01 12:20:49 +03:00
|
|
|
aBody += static_cast<uint8_t>(mType);
|
|
|
|
aBody += '\0'; // upper 8 bit CLASS
|
|
|
|
aBody += kDNS_CLASS_IN; // IN - "the Internet"
|
|
|
|
|
2018-08-13 18:45:15 +03:00
|
|
|
if (aDisableECS) {
|
|
|
|
// EDNS(0) is RFC 6891, ECS is RFC 7871
|
|
|
|
aBody += '\0'; // NAME | domain name | MUST be 0 (root domain) |
|
|
|
|
aBody += '\0';
|
|
|
|
aBody += 41; // TYPE | u_int16_t | OPT (41) |
|
|
|
|
aBody += 16; // CLASS | u_int16_t | requestor's UDP payload size |
|
|
|
|
aBody +=
|
|
|
|
'\0'; // advertise 4K (high-byte: 16 | low-byte: 0), ignored by DoH
|
|
|
|
aBody += '\0'; // TTL | u_int32_t | extended RCODE and flags |
|
|
|
|
aBody += '\0';
|
|
|
|
aBody += '\0';
|
|
|
|
aBody += '\0';
|
|
|
|
|
|
|
|
aBody += '\0'; // upper 8 bit RDLEN
|
|
|
|
aBody += 8; // RDLEN | u_int16_t | length of all RDATA |
|
|
|
|
|
|
|
|
// RDATA | octet stream | {attribute,value} pairs |
|
|
|
|
// The RDATA is just the ECS option setting zero subnet prefix
|
|
|
|
|
|
|
|
aBody += '\0'; // upper 8 bit OPTION-CODE ECS
|
|
|
|
aBody += 8; // OPTION-CODE, 2 octets, for ECS is 8
|
|
|
|
|
|
|
|
aBody += '\0'; // upper 8 bit OPTION-LENGTH
|
|
|
|
aBody += 4; // OPTION-LENGTH, 2 octets, contains the length of the payload
|
|
|
|
// after OPTION-LENGTH
|
2018-09-03 18:02:03 +03:00
|
|
|
aBody += '\0'; // upper 8 bit FAMILY. IANA Address Family Numbers registry,
|
|
|
|
// not the AF_* constants!
|
|
|
|
aBody += 1; // FAMILY (Ipv4), 2 octets
|
2018-08-13 18:45:15 +03:00
|
|
|
|
|
|
|
aBody += '\0'; // SOURCE PREFIX-LENGTH | SCOPE PREFIX-LENGTH |
|
|
|
|
aBody += '\0';
|
|
|
|
|
|
|
|
// ADDRESS, minimum number of octets == nothing because zero bits
|
|
|
|
}
|
2018-02-01 12:20:49 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TRR::Run() {
|
2020-06-22 14:10:21 +03:00
|
|
|
MOZ_ASSERT_IF(XRE_IsParentProcess() && gTRRService,
|
|
|
|
NS_IsMainThread() || gTRRService->IsOnTRRThread());
|
|
|
|
MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
|
2020-03-04 19:11:16 +03:00
|
|
|
|
2018-02-20 00:54:14 +03:00
|
|
|
if ((gTRRService == nullptr) || NS_FAILED(SendHTTPRequest())) {
|
2020-07-11 22:32:05 +03:00
|
|
|
RecordReason(nsHostRecord::TRR_SEND_FAILED);
|
2018-06-20 12:00:19 +03:00
|
|
|
FailData(NS_ERROR_FAILURE);
|
2018-02-01 12:20:49 +03:00
|
|
|
// The dtor will now be run
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-03-04 19:11:16 +03:00
|
|
|
static void InitHttpHandler() {
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIProtocolHandler> handler;
|
|
|
|
rv = ios->GetProtocolHandler("http", getter_AddRefs(handler));
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult TRR::CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult) {
|
|
|
|
*aResult = nullptr;
|
|
|
|
|
2020-05-18 23:18:08 +03:00
|
|
|
if (NS_IsMainThread() && !XRE_IsSocketProcess()) {
|
2020-03-04 19:11:16 +03:00
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsIIOService> ios(do_GetIOService(&rv));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
return NS_NewChannel(aResult, aUri, nsContentUtils::GetSystemPrincipal(),
|
|
|
|
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
|
|
|
|
nsIContentPolicy::TYPE_OTHER,
|
|
|
|
nullptr, // nsICookieJarSettings
|
|
|
|
nullptr, // PerformanceStorage
|
|
|
|
nullptr, // aLoadGroup
|
|
|
|
nullptr, // aCallbacks
|
|
|
|
nsIRequest::LOAD_NORMAL, ios);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unfortunately, we can only initialize gHttpHandler on main thread.
|
|
|
|
if (!gHttpHandler) {
|
|
|
|
nsCOMPtr<nsIEventTarget> main = GetMainThreadEventTarget();
|
|
|
|
if (main) {
|
|
|
|
// Forward to the main thread synchronously.
|
|
|
|
SyncRunnable::DispatchToThread(
|
|
|
|
main, new SyncRunnable(NS_NewRunnableFunction(
|
|
|
|
"InitHttpHandler", []() { InitHttpHandler(); })));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!gHttpHandler) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
2020-03-18 18:13:09 +03:00
|
|
|
RefPtr<TRRLoadInfo> loadInfo =
|
|
|
|
new TRRLoadInfo(aUri, nsIContentPolicy::TYPE_OTHER);
|
2020-03-05 21:13:32 +03:00
|
|
|
return gHttpHandler->CreateTRRServiceChannel(aUri,
|
2020-03-18 18:13:09 +03:00
|
|
|
nullptr, // givenProxyInfo
|
|
|
|
0, // proxyResolveFlags
|
|
|
|
nullptr, // proxyURI
|
|
|
|
loadInfo, // aLoadInfo
|
2020-03-04 19:11:16 +03:00
|
|
|
aResult);
|
|
|
|
}
|
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
nsresult TRR::SendHTTPRequest() {
|
|
|
|
// This is essentially the "run" method - created from nsHostResolver
|
|
|
|
|
2018-09-22 23:54:11 +03:00
|
|
|
if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
|
2020-05-04 22:56:51 +03:00
|
|
|
(mType != TRRTYPE_NS) && (mType != TRRTYPE_TXT) &&
|
|
|
|
(mType != TRRTYPE_HTTPSSVC)) {
|
2018-02-01 12:20:49 +03:00
|
|
|
// limit the calling interface because nsHostResolver has explicit slots for
|
|
|
|
// these types
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2020-01-07 23:20:38 +03:00
|
|
|
if (((mType == TRRTYPE_A) || (mType == TRRTYPE_AAAA)) &&
|
2020-02-07 12:42:00 +03:00
|
|
|
mRec->mEffectiveTRRMode != nsIRequest::TRR_ONLY_MODE) {
|
2020-07-11 22:32:27 +03:00
|
|
|
// let NS resolves skip the blocklist check
|
|
|
|
// we also don't check the blocklist for TRR only requests
|
2018-10-22 18:38:18 +03:00
|
|
|
MOZ_ASSERT(mRec);
|
|
|
|
|
2020-02-11 19:20:08 +03:00
|
|
|
if (UseDefaultServer() &&
|
2020-07-11 22:32:27 +03:00
|
|
|
gTRRService->IsTemporarilyBlocked(mHost, mOriginSuffix, mPB, true)) {
|
2018-04-30 10:08:07 +03:00
|
|
|
if (mType == TRRTYPE_A) {
|
2020-07-11 22:32:27 +03:00
|
|
|
// count only blocklist for A records to avoid double counts
|
2020-05-30 11:39:02 +03:00
|
|
|
Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED2,
|
|
|
|
TRRService::AutoDetectedKey(), true);
|
2018-04-30 10:08:07 +03:00
|
|
|
}
|
2020-07-11 22:32:05 +03:00
|
|
|
|
|
|
|
RecordReason(nsHostRecord::TRR_HOST_BLOCKED_TEMPORARY);
|
2018-04-30 10:08:07 +03:00
|
|
|
// not really an error but no TRR is issued
|
|
|
|
return NS_ERROR_UNKNOWN_HOST;
|
2020-07-11 22:32:57 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (gTRRService->IsExcludedFromTRR(mHost)) {
|
|
|
|
RecordReason(nsHostRecord::TRR_EXCLUDED);
|
|
|
|
return NS_ERROR_UNKNOWN_HOST;
|
2020-05-04 22:56:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (UseDefaultServer() && (mType == TRRTYPE_A)) {
|
2020-05-30 11:39:02 +03:00
|
|
|
Telemetry::Accumulate(Telemetry::DNS_TRR_BLACKLISTED2,
|
|
|
|
TRRService::AutoDetectedKey(), false);
|
2018-04-30 10:08:07 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-20 00:54:14 +03:00
|
|
|
bool useGet = gTRRService->UseGET();
|
2018-02-01 12:20:49 +03:00
|
|
|
nsAutoCString body;
|
|
|
|
nsCOMPtr<nsIURI> dnsURI;
|
2018-08-13 18:45:15 +03:00
|
|
|
bool disableECS = gTRRService->DisableECS();
|
2020-03-04 19:11:16 +03:00
|
|
|
nsresult rv;
|
2018-02-01 12:20:49 +03:00
|
|
|
|
|
|
|
LOG(("TRR::SendHTTPRequest resolve %s type %u\n", mHost.get(), mType));
|
|
|
|
|
|
|
|
if (useGet) {
|
|
|
|
nsAutoCString tmp;
|
2018-08-13 18:45:15 +03:00
|
|
|
rv = DohEncode(tmp, disableECS);
|
2018-02-01 12:20:49 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
/* For GET requests, the outgoing packet needs to be Base64url-encoded and
|
|
|
|
then appended to the end of the URI. */
|
|
|
|
rv = Base64URLEncode(tmp.Length(),
|
|
|
|
reinterpret_cast<const unsigned char*>(tmp.get()),
|
|
|
|
Base64URLEncodePaddingPolicy::Omit, body);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsAutoCString uri;
|
2020-02-06 21:09:38 +03:00
|
|
|
if (UseDefaultServer()) {
|
2020-02-04 22:35:34 +03:00
|
|
|
gTRRService->GetURI(uri);
|
|
|
|
} else {
|
|
|
|
uri = mRec->mTrrServer;
|
|
|
|
}
|
2020-03-18 18:12:52 +03:00
|
|
|
|
2020-03-17 23:55:52 +03:00
|
|
|
rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
|
2020-03-18 18:12:52 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsAutoCString query;
|
|
|
|
rv = dnsURI->GetQuery(query);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (query.IsEmpty()) {
|
2020-07-01 11:29:29 +03:00
|
|
|
query.Assign("?dns="_ns);
|
2020-03-18 18:13:09 +03:00
|
|
|
} else {
|
2020-07-01 11:29:29 +03:00
|
|
|
query.Append("&dns="_ns);
|
2020-03-18 18:12:52 +03:00
|
|
|
}
|
|
|
|
query.Append(body);
|
|
|
|
|
|
|
|
rv = NS_MutateURI(dnsURI).SetQuery(query).Finalize(dnsURI);
|
|
|
|
LOG(("TRR::SendHTTPRequest GET dns=%s\n", body.get()));
|
2018-02-01 12:20:49 +03:00
|
|
|
} else {
|
2018-08-13 18:45:15 +03:00
|
|
|
rv = DohEncode(body, disableECS);
|
2018-02-01 12:20:49 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsAutoCString uri;
|
2020-02-06 21:09:38 +03:00
|
|
|
if (UseDefaultServer()) {
|
2020-02-04 22:35:34 +03:00
|
|
|
gTRRService->GetURI(uri);
|
|
|
|
} else {
|
|
|
|
uri = mRec->mTrrServer;
|
|
|
|
}
|
2018-02-01 12:20:49 +03:00
|
|
|
rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
|
|
|
|
}
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOG(("TRR:SendHTTPRequest: NewURI failed!\n"));
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2020-03-18 18:13:09 +03:00
|
|
|
nsCOMPtr<nsIChannel> channel;
|
|
|
|
rv = CreateChannelHelper(dnsURI, getter_AddRefs(channel));
|
|
|
|
if (NS_FAILED(rv) || !channel) {
|
2018-02-01 12:20:49 +03:00
|
|
|
LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2020-03-18 18:13:09 +03:00
|
|
|
channel->SetLoadFlags(
|
2020-03-04 19:11:16 +03:00
|
|
|
nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
|
|
|
|
nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2020-03-18 18:13:09 +03:00
|
|
|
rv = channel->SetNotificationCallbacks(this);
|
2020-03-04 19:11:16 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2020-03-18 18:13:09 +03:00
|
|
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
|
2018-02-01 12:20:49 +03:00
|
|
|
if (!httpChannel) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
2020-01-07 23:20:38 +03:00
|
|
|
// This connection should not use TRR
|
|
|
|
rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2020-07-01 11:29:29 +03:00
|
|
|
rv = httpChannel->SetRequestHeader("Accept"_ns, "application/dns-message"_ns,
|
|
|
|
false);
|
2018-02-01 12:20:49 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsAutoCString cred;
|
2020-02-06 21:09:38 +03:00
|
|
|
if (UseDefaultServer()) {
|
2020-02-04 22:35:34 +03:00
|
|
|
gTRRService->GetCredentials(cred);
|
|
|
|
}
|
2018-02-01 12:20:49 +03:00
|
|
|
if (!cred.IsEmpty()) {
|
2020-07-01 11:29:29 +03:00
|
|
|
rv = httpChannel->SetRequestHeader("Authorization"_ns, cred, false);
|
2018-02-01 12:20:49 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
2020-03-18 18:13:09 +03:00
|
|
|
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel);
|
2018-02-01 12:20:49 +03:00
|
|
|
if (!internalChannel) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
// setting a small stream window means the h2 stack won't pipeline a window
|
|
|
|
// update with each HEADERS or reply to a DATA with a WINDOW UPDATE
|
|
|
|
rv = internalChannel->SetInitialRwin(127 * 1024);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2019-05-06 19:37:01 +03:00
|
|
|
rv = internalChannel->SetIsTRRServiceChannel(true);
|
2018-02-01 12:20:49 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2018-02-20 00:54:14 +03:00
|
|
|
mAllowRFC1918 = gTRRService->AllowRFC1918();
|
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
if (useGet) {
|
2020-07-01 11:29:29 +03:00
|
|
|
rv = httpChannel->SetRequestMethod("GET"_ns);
|
2018-02-01 12:20:49 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} else {
|
|
|
|
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
|
|
|
|
if (!uploadChannel) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
2018-04-11 17:06:17 +03:00
|
|
|
uint32_t streamLength = body.Length();
|
2018-02-01 12:20:49 +03:00
|
|
|
nsCOMPtr<nsIInputStream> uploadStream;
|
2018-05-30 22:15:35 +03:00
|
|
|
rv =
|
|
|
|
NS_NewCStringInputStream(getter_AddRefs(uploadStream), std::move(body));
|
2018-02-01 12:20:49 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
2020-07-01 11:29:29 +03:00
|
|
|
rv = uploadChannel->ExplicitSetUploadStream(uploadStream,
|
|
|
|
"application/dns-message"_ns,
|
|
|
|
streamLength, "POST"_ns, false);
|
2018-02-01 12:20:49 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
2020-03-18 18:13:09 +03:00
|
|
|
rv = SetupTRRServiceChannelInternal(httpChannel, useGet);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = httpChannel->AsyncOpen(this);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2020-07-07 13:08:28 +03:00
|
|
|
// If the asyncOpen succeeded we can say that we actually attempted to
|
|
|
|
// use the TRR connection.
|
|
|
|
RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRec);
|
|
|
|
if (addrRec) {
|
|
|
|
addrRec->mTRRUsed = true;
|
|
|
|
}
|
|
|
|
|
2020-03-18 18:13:09 +03:00
|
|
|
NS_NewTimerWithCallback(getter_AddRefs(mTimeout), this,
|
|
|
|
gTRRService->GetRequestTimeout(),
|
|
|
|
nsITimer::TYPE_ONE_SHOT);
|
|
|
|
|
|
|
|
mChannel = channel;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// static
|
|
|
|
nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
|
|
|
|
bool aUseGet) {
|
|
|
|
nsCOMPtr<nsIHttpChannel> httpChannel = aChannel;
|
|
|
|
MOZ_ASSERT(httpChannel);
|
|
|
|
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
if (!aUseGet) {
|
2020-07-01 11:29:29 +03:00
|
|
|
rv =
|
|
|
|
httpChannel->SetRequestHeader("Cache-Control"_ns, "no-store"_ns, false);
|
2020-03-18 18:13:09 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
2019-11-05 13:45:21 +03:00
|
|
|
// Sanitize the request by removing the Accept-Language header so we minimize
|
|
|
|
// the amount of fingerprintable information we send to the server.
|
|
|
|
if (!StaticPrefs::network_trr_send_accept_language_headers()) {
|
2020-07-01 11:29:29 +03:00
|
|
|
rv = httpChannel->SetRequestHeader("Accept-Language"_ns, EmptyCString(),
|
|
|
|
false);
|
2019-11-05 13:45:21 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
2019-12-05 22:17:28 +03:00
|
|
|
// Sanitize the request by removing the User-Agent
|
|
|
|
if (!StaticPrefs::network_trr_send_user_agent_headers()) {
|
2020-07-01 11:29:29 +03:00
|
|
|
rv = httpChannel->SetRequestHeader("User-Agent"_ns, EmptyCString(), false);
|
2019-12-05 22:17:28 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
2020-03-10 17:28:27 +03:00
|
|
|
if (StaticPrefs::network_trr_send_empty_accept_encoding_headers()) {
|
2020-07-01 11:29:29 +03:00
|
|
|
rv = httpChannel->SetEmptyRequestHeader("Accept-Encoding"_ns);
|
2020-03-10 17:28:27 +03:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
// set the *default* response content type
|
2020-07-01 11:29:29 +03:00
|
|
|
if (NS_FAILED(httpChannel->SetContentType("application/dns-message"_ns))) {
|
2020-03-18 18:13:09 +03:00
|
|
|
LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n"));
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
2020-01-14 15:11:46 +03:00
|
|
|
|
|
|
|
nsCOMPtr<nsITimedChannel> timedChan(do_QueryInterface(httpChannel));
|
2020-03-18 18:13:09 +03:00
|
|
|
if (timedChan) {
|
|
|
|
timedChan->SetTimingEnabled(true);
|
2020-03-17 23:55:52 +03:00
|
|
|
}
|
2020-03-18 18:13:09 +03:00
|
|
|
|
|
|
|
return NS_OK;
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TRR::GetInterface(const nsIID& iid, void** result) {
|
|
|
|
if (!iid.Equals(NS_GET_IID(nsIHttpPushListener))) {
|
|
|
|
return NS_ERROR_NO_INTERFACE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIHttpPushListener> copy(this);
|
|
|
|
*result = copy.forget().take();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult TRR::DohDecodeQuery(const nsCString& query, nsCString& host,
|
|
|
|
enum TrrType& type) {
|
|
|
|
FallibleTArray<uint8_t> binary;
|
|
|
|
bool found_dns = false;
|
|
|
|
LOG(("TRR::DohDecodeQuery %s!\n", query.get()));
|
|
|
|
|
|
|
|
// extract "dns=" from the query string
|
|
|
|
nsCCharSeparatedTokenizer tokenizer(query, '&');
|
|
|
|
nsAutoCString data;
|
|
|
|
while (tokenizer.hasMoreTokens()) {
|
|
|
|
const nsACString& token = tokenizer.nextToken();
|
|
|
|
nsDependentCSubstring dns = Substring(token, 0, 4);
|
|
|
|
nsAutoCString check(dns);
|
|
|
|
if (check.Equals("dns=")) {
|
|
|
|
nsDependentCSubstring q = Substring(token, 4, -1);
|
|
|
|
data = q;
|
|
|
|
found_dns = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found_dns) {
|
|
|
|
LOG(("TRR::DohDecodeQuery no dns= in pushed URI query string\n"));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv =
|
|
|
|
Base64URLDecode(data, Base64URLDecodePaddingPolicy::Ignore, binary);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
uint32_t avail = binary.Length();
|
|
|
|
if (avail < 12) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
// check the query bit and the opcode
|
|
|
|
if ((binary[2] & 0xf8) != 0) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
uint32_t qdcount = (binary[4] << 8) + binary[5];
|
|
|
|
if (!qdcount) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t index = 12;
|
|
|
|
uint32_t length = 0;
|
|
|
|
host.Truncate();
|
|
|
|
do {
|
|
|
|
if (avail < (index + 1)) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
length = binary[index];
|
|
|
|
if (length) {
|
|
|
|
if (host.Length()) {
|
|
|
|
host.Append(".");
|
|
|
|
}
|
|
|
|
if (avail < (index + 1 + length)) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
host.Append((const char*)(&binary[0]) + index + 1, length);
|
|
|
|
}
|
|
|
|
index += 1 + length; // skip length byte + label
|
|
|
|
} while (length);
|
|
|
|
|
|
|
|
LOG(("TRR::DohDecodeQuery host %s\n", host.get()));
|
|
|
|
|
|
|
|
if (avail < (index + 2)) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
uint16_t i16 = 0;
|
|
|
|
i16 += binary[index] << 8;
|
|
|
|
i16 += binary[index + 1];
|
|
|
|
type = (enum TrrType)i16;
|
|
|
|
|
|
|
|
LOG(("TRR::DohDecodeQuery type %d\n", (int)type));
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult TRR::ReceivePush(nsIHttpChannel* pushed, nsHostRecord* pushedRec) {
|
|
|
|
if (!mHostResolver) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(("TRR::ReceivePush: PUSH incoming!\n"));
|
|
|
|
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
pushed->GetURI(getter_AddRefs(uri));
|
|
|
|
nsAutoCString query;
|
|
|
|
if (uri) {
|
|
|
|
uri->GetQuery(query);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRNetAddr tempAddr;
|
|
|
|
if (NS_FAILED(DohDecodeQuery(query, mHost, mType)) ||
|
|
|
|
(PR_StringToNetAddr(mHost.get(), &tempAddr) == PR_SUCCESS)) { // literal
|
|
|
|
LOG(("TRR::ReceivePush failed to decode %s\n", mHost.get()));
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
2018-11-15 16:10:54 +03:00
|
|
|
if ((mType != TRRTYPE_A) && (mType != TRRTYPE_AAAA) &&
|
2020-05-04 22:56:51 +03:00
|
|
|
(mType != TRRTYPE_TXT) && (mType != TRRTYPE_HTTPSSVC)) {
|
2018-11-15 16:10:54 +03:00
|
|
|
LOG(("TRR::ReceivePush unknown type %d\n", mType));
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
2019-11-21 13:02:33 +03:00
|
|
|
if (gTRRService->IsExcludedFromTRR(mHost)) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2020-05-04 22:56:51 +03:00
|
|
|
uint32_t type = nsIDNSService::RESOLVE_TYPE_DEFAULT;
|
|
|
|
if (mType == TRRTYPE_TXT) {
|
|
|
|
type = nsIDNSService::RESOLVE_TYPE_TXT;
|
|
|
|
} else if (mType == TRRTYPE_HTTPSSVC) {
|
|
|
|
type = nsIDNSService::RESOLVE_TYPE_HTTPSSVC;
|
|
|
|
}
|
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
RefPtr<nsHostRecord> hostRecord;
|
|
|
|
nsresult rv;
|
2018-06-15 02:15:13 +03:00
|
|
|
rv = mHostResolver->GetHostRecord(
|
2020-05-04 22:56:51 +03:00
|
|
|
mHost, EmptyCString(), type, pushedRec->flags, pushedRec->af,
|
|
|
|
pushedRec->pb, pushedRec->originSuffix, getter_AddRefs(hostRecord));
|
2018-02-01 12:20:49 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2020-02-07 12:42:00 +03:00
|
|
|
// Since we don't ever call nsHostResolver::NameLookup for this record,
|
|
|
|
// we need to copy the trr mode from the previous record
|
|
|
|
if (hostRecord->mEffectiveTRRMode == nsIRequest::TRR_DEFAULT_MODE) {
|
|
|
|
hostRecord->mEffectiveTRRMode = pushedRec->mEffectiveTRRMode;
|
|
|
|
}
|
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
rv = mHostResolver->TrrLookup_unlocked(hostRecord, this);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2019-02-12 19:08:25 +03:00
|
|
|
rv = pushed->AsyncOpen(this);
|
2018-02-01 12:20:49 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
// OK!
|
|
|
|
mChannel = pushed;
|
|
|
|
mRec.swap(hostRecord);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
TRR::OnPush(nsIHttpChannel* associated, nsIHttpChannel* pushed) {
|
|
|
|
LOG(("TRR::OnPush entry\n"));
|
|
|
|
MOZ_ASSERT(associated == mChannel);
|
|
|
|
if (!mRec) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2020-02-06 21:09:38 +03:00
|
|
|
if (!UseDefaultServer()) {
|
2020-02-04 22:35:34 +03:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2018-02-01 12:20:49 +03:00
|
|
|
|
|
|
|
RefPtr<TRR> trr = new TRR(mHostResolver, mPB);
|
|
|
|
return trr->ReceivePush(pushed, mRec);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2019-02-28 02:41:04 +03:00
|
|
|
TRR::OnStartRequest(nsIRequest* aRequest) {
|
2018-02-01 12:20:49 +03:00
|
|
|
LOG(("TRR::OnStartRequest %p %s %d\n", this, mHost.get(), mType));
|
2020-07-11 22:32:05 +03:00
|
|
|
|
|
|
|
nsresult status = NS_OK;
|
|
|
|
aRequest->GetStatus(&status);
|
|
|
|
|
|
|
|
if (NS_FAILED(status)) {
|
|
|
|
if (NS_IsOffline()) {
|
|
|
|
RecordReason(nsHostRecord::TRR_IS_OFFLINE);
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (status) {
|
|
|
|
case NS_ERROR_UNKNOWN_HOST:
|
|
|
|
RecordReason(nsHostRecord::TRR_CHANNEL_DNS_FAIL);
|
|
|
|
break;
|
|
|
|
case NS_ERROR_OFFLINE:
|
|
|
|
RecordReason(nsHostRecord::TRR_IS_OFFLINE);
|
|
|
|
break;
|
|
|
|
case NS_ERROR_NET_RESET:
|
|
|
|
RecordReason(nsHostRecord::TRR_NET_RESET);
|
|
|
|
break;
|
|
|
|
case NS_ERROR_NET_TIMEOUT:
|
|
|
|
RecordReason(nsHostRecord::TRR_NET_TIMEOUT);
|
|
|
|
break;
|
|
|
|
case NS_ERROR_PROXY_CONNECTION_REFUSED:
|
|
|
|
RecordReason(nsHostRecord::TRR_NET_REFUSED);
|
|
|
|
break;
|
|
|
|
case NS_ERROR_NET_INTERRUPT:
|
|
|
|
RecordReason(nsHostRecord::TRR_NET_INTERRUPT);
|
|
|
|
break;
|
|
|
|
case NS_ERROR_NET_INADEQUATE_SECURITY:
|
|
|
|
RecordReason(nsHostRecord::TRR_NET_INADEQ_SEQURITY);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
RecordReason(nsHostRecord::TRR_UNKNOWN_CHANNEL_FAILURE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-05-04 22:56:51 +03:00
|
|
|
static uint16_t get16bit(const unsigned char* aData, unsigned int index) {
|
2018-02-01 12:20:49 +03:00
|
|
|
return ((aData[index] << 8) | aData[index + 1]);
|
|
|
|
}
|
|
|
|
|
2020-05-04 22:56:51 +03:00
|
|
|
static uint32_t get32bit(const unsigned char* aData, unsigned int index) {
|
2018-02-01 12:20:49 +03:00
|
|
|
return (aData[index] << 24) | (aData[index + 1] << 16) |
|
|
|
|
(aData[index + 2] << 8) | aData[index + 3];
|
|
|
|
}
|
|
|
|
|
2018-04-06 00:28:26 +03:00
|
|
|
nsresult TRR::PassQName(unsigned int& index) {
|
|
|
|
uint8_t length;
|
|
|
|
do {
|
|
|
|
if (mBodySize < (index + 1)) {
|
|
|
|
LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
length = static_cast<uint8_t>(mResponse[index]);
|
|
|
|
if ((length & 0xc0) == 0xc0) {
|
|
|
|
// name pointer, advance over it and be done
|
|
|
|
if (mBodySize < (index + 2)) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
index += 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (length & 0xc0) {
|
|
|
|
LOG(("TRR: illegal label length byte (%x) at index %d\n", length, index));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
// pass label
|
|
|
|
if (mBodySize < (index + 1 + length)) {
|
|
|
|
LOG(("TRR: PassQName:%d fail at index %d\n", __LINE__, index));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
index += 1 + length;
|
|
|
|
} while (length);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-05-08 20:30:07 +03:00
|
|
|
// GetQname: retrieves the qname (stores in 'aQname') and stores the index
|
|
|
|
// after qname was parsed into the 'aIndex'.
|
|
|
|
|
2020-05-04 22:56:51 +03:00
|
|
|
nsresult TRR::GetQname(nsACString& aQname, unsigned int& aIndex) {
|
2018-05-08 20:30:07 +03:00
|
|
|
uint8_t clength = 0;
|
|
|
|
unsigned int cindex = aIndex;
|
|
|
|
unsigned int loop = 128; // a valid DNS name can never loop this much
|
|
|
|
unsigned int endindex = 0; // index position after this data
|
|
|
|
do {
|
|
|
|
if (cindex >= mBodySize) {
|
2020-05-04 22:56:51 +03:00
|
|
|
LOG(("TRR: bad Qname packet\n"));
|
2018-05-08 20:30:07 +03:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
clength = static_cast<uint8_t>(mResponse[cindex]);
|
|
|
|
if ((clength & 0xc0) == 0xc0) {
|
|
|
|
// name pointer, get the new offset (14 bits)
|
|
|
|
if ((cindex + 1) >= mBodySize) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
// extract the new index position for the next label
|
|
|
|
uint16_t newpos = (clength & 0x3f) << 8 | mResponse[cindex + 1];
|
|
|
|
if (!endindex) {
|
|
|
|
// only update on the first "jump"
|
|
|
|
endindex = cindex + 2;
|
|
|
|
}
|
|
|
|
cindex = newpos;
|
|
|
|
continue;
|
2020-05-04 22:56:51 +03:00
|
|
|
}
|
|
|
|
if (clength & 0xc0) {
|
2018-05-08 20:30:07 +03:00
|
|
|
// any of those bits set individually is an error
|
2020-05-04 22:56:51 +03:00
|
|
|
LOG(("TRR: bad Qname packet\n"));
|
2018-05-08 20:30:07 +03:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
2020-05-04 22:56:51 +03:00
|
|
|
|
|
|
|
cindex++;
|
|
|
|
|
2018-05-08 20:30:07 +03:00
|
|
|
if (clength) {
|
|
|
|
if (!aQname.IsEmpty()) {
|
|
|
|
aQname.Append(".");
|
|
|
|
}
|
|
|
|
if ((cindex + clength) > mBodySize) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
aQname.Append((const char*)(&mResponse[cindex]), clength);
|
|
|
|
cindex += clength; // skip label
|
|
|
|
}
|
|
|
|
} while (clength && --loop);
|
|
|
|
|
|
|
|
if (!loop) {
|
|
|
|
LOG(("TRR::DohDecode pointer loop error\n"));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
if (!endindex) {
|
|
|
|
// there was no "jump"
|
|
|
|
endindex = cindex;
|
|
|
|
}
|
|
|
|
aIndex = endindex;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
//
|
|
|
|
// DohDecode() collects the TTL and the IP addresses in the response
|
|
|
|
//
|
2018-05-08 20:30:07 +03:00
|
|
|
nsresult TRR::DohDecode(nsCString& aHost) {
|
2018-02-01 12:20:49 +03:00
|
|
|
// The response has a 12 byte header followed by the question (returned)
|
|
|
|
// and then the answer. The answer section itself contains the name, type
|
|
|
|
// and class again and THEN the record data.
|
|
|
|
|
|
|
|
// www.example.com response:
|
|
|
|
// header:
|
|
|
|
// abcd 8180 0001 0001 0000 0000
|
|
|
|
// the question:
|
|
|
|
// 0377 7777 0765 7861 6d70 6c65 0363 6f6d 0000 0100 01
|
|
|
|
// the answer:
|
|
|
|
// 03 7777 7707 6578 616d 706c 6503 636f 6d00 0001 0001
|
|
|
|
// 0000 0080 0004 5db8 d822
|
|
|
|
|
|
|
|
unsigned int index = 12;
|
|
|
|
uint8_t length;
|
|
|
|
nsAutoCString host;
|
2018-04-06 00:28:26 +03:00
|
|
|
nsresult rv;
|
2018-02-01 12:20:49 +03:00
|
|
|
|
2018-05-08 20:30:07 +03:00
|
|
|
LOG(("doh decode %s %d bytes\n", aHost.get(), mBodySize));
|
2018-02-01 12:20:49 +03:00
|
|
|
|
|
|
|
mCname.Truncate();
|
|
|
|
|
|
|
|
if (mBodySize < 12 || mResponse[0] || mResponse[1]) {
|
|
|
|
LOG(("TRR bad incoming DOH, eject!\n"));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
uint8_t rcode = mResponse[3] & 0x0F;
|
|
|
|
if (rcode) {
|
2018-05-08 20:30:07 +03:00
|
|
|
LOG(("TRR Decode %s RCODE %d\n", aHost.get(), rcode));
|
2018-02-01 12:20:49 +03:00
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16_t questionRecords = get16bit(mResponse, 4); // qdcount
|
|
|
|
// iterate over the single(?) host name in question
|
|
|
|
while (questionRecords) {
|
|
|
|
do {
|
|
|
|
if (mBodySize < (index + 1)) {
|
2020-05-04 22:56:51 +03:00
|
|
|
LOG(("TRR Decode 1 index: %u size: %u", index, mBodySize));
|
2018-02-01 12:20:49 +03:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
length = static_cast<uint8_t>(mResponse[index]);
|
|
|
|
if (length) {
|
|
|
|
if (host.Length()) {
|
|
|
|
host.Append(".");
|
|
|
|
}
|
|
|
|
if (mBodySize < (index + 1 + length)) {
|
2020-05-04 22:56:51 +03:00
|
|
|
LOG(("TRR Decode 2 index: %u size: %u len: %u", index, mBodySize,
|
|
|
|
length));
|
2018-02-01 12:20:49 +03:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
host.Append(((char*)mResponse) + index + 1, length);
|
|
|
|
}
|
|
|
|
index += 1 + length; // skip length byte + label
|
|
|
|
} while (length);
|
|
|
|
if (mBodySize < (index + 4)) {
|
2020-05-04 22:56:51 +03:00
|
|
|
LOG(("TRR Decode 3 index: %u size: %u", index, mBodySize));
|
2018-02-01 12:20:49 +03:00
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
index += 4; // skip question's type, class
|
|
|
|
questionRecords--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Figure out the number of answer records from ANCOUNT
|
|
|
|
uint16_t answerRecords = get16bit(mResponse, 6);
|
|
|
|
|
|
|
|
LOG(("TRR Decode: %d answer records (%u bytes body) %s index=%u\n",
|
|
|
|
answerRecords, mBodySize, host.get(), index));
|
|
|
|
|
|
|
|
while (answerRecords) {
|
2018-05-08 20:30:07 +03:00
|
|
|
nsAutoCString qname;
|
|
|
|
rv = GetQname(qname, index);
|
2018-04-06 00:28:26 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
|
|
|
// 16 bit TYPE
|
|
|
|
if (mBodySize < (index + 2)) {
|
|
|
|
LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
uint16_t TYPE = get16bit(mResponse, index);
|
|
|
|
|
2020-06-30 15:27:09 +03:00
|
|
|
if ((TYPE != TRRTYPE_CNAME) && (TYPE != TRRTYPE_HTTPSSVC) &&
|
|
|
|
(TYPE != static_cast<uint16_t>(mType))) {
|
2018-02-01 12:20:49 +03:00
|
|
|
// Not the same type as was asked for nor CNAME
|
|
|
|
LOG(("TRR: Dohdecode:%d asked for type %d got %d\n", __LINE__, mType,
|
|
|
|
TYPE));
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
index += 2;
|
|
|
|
|
|
|
|
// 16 bit class
|
|
|
|
if (mBodySize < (index + 2)) {
|
|
|
|
LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index + 2));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
uint16_t CLASS = get16bit(mResponse, index);
|
|
|
|
if (kDNS_CLASS_IN != CLASS) {
|
|
|
|
LOG(("TRR bad CLASS (%u) at index %d\n", CLASS, index));
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
index += 2;
|
|
|
|
|
|
|
|
// 32 bit TTL (seconds)
|
|
|
|
if (mBodySize < (index + 4)) {
|
|
|
|
LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
uint32_t TTL = get32bit(mResponse, index);
|
|
|
|
index += 4;
|
|
|
|
|
|
|
|
// 16 bit RDLENGTH
|
|
|
|
if (mBodySize < (index + 2)) {
|
|
|
|
LOG(("TRR: Dohdecode:%d fail at index %d\n", __LINE__, index));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
uint16_t RDLENGTH = get16bit(mResponse, index);
|
|
|
|
index += 2;
|
|
|
|
|
|
|
|
if (mBodySize < (index + RDLENGTH)) {
|
|
|
|
LOG(("TRR: Dohdecode:%d fail RDLENGTH=%d at index %d\n", __LINE__,
|
|
|
|
RDLENGTH, index));
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
2020-05-25 16:06:36 +03:00
|
|
|
// We check if the qname is a case-insensitive match for the host or the
|
|
|
|
// FQDN version of the host
|
|
|
|
bool responseMatchesQuestion =
|
|
|
|
(qname.Length() == aHost.Length() ||
|
|
|
|
(aHost.Length() == qname.Length() + 1 && aHost.Last() == '.')) &&
|
|
|
|
qname.Compare(aHost.BeginReading(), true, qname.Length()) == 0;
|
|
|
|
|
|
|
|
if (responseMatchesQuestion) {
|
2018-05-08 20:30:07 +03:00
|
|
|
// RDATA
|
|
|
|
// - A (TYPE 1): 4 bytes
|
|
|
|
// - AAAA (TYPE 28): 16 bytes
|
|
|
|
// - NS (TYPE 2): N bytes
|
2018-02-01 12:20:49 +03:00
|
|
|
|
2018-05-08 20:30:07 +03:00
|
|
|
switch (TYPE) {
|
|
|
|
case TRRTYPE_A:
|
|
|
|
if (RDLENGTH != 4) {
|
|
|
|
LOG(("TRR bad length for A (%u)\n", RDLENGTH));
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
rv = mDNS.Add(TTL, mResponse, index, RDLENGTH, mAllowRFC1918);
|
|
|
|
if (NS_FAILED(rv)) {
|
2018-11-30 13:46:48 +03:00
|
|
|
LOG(
|
2018-05-08 20:30:07 +03:00
|
|
|
("TRR:DohDecode failed: local IP addresses or unknown IP "
|
|
|
|
"family\n"));
|
|
|
|
return rv;
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
break;
|
2018-05-08 20:30:07 +03:00
|
|
|
case TRRTYPE_AAAA:
|
|
|
|
if (RDLENGTH != 16) {
|
|
|
|
LOG(("TRR bad length for AAAA (%u)\n", RDLENGTH));
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2018-05-08 20:30:07 +03:00
|
|
|
rv = mDNS.Add(TTL, mResponse, index, RDLENGTH, mAllowRFC1918);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOG(("TRR got unique/local IPv6 address!\n"));
|
|
|
|
return rv;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2018-05-08 20:30:07 +03:00
|
|
|
case TRRTYPE_NS:
|
2018-11-30 13:46:48 +03:00
|
|
|
break;
|
2018-05-08 20:30:07 +03:00
|
|
|
case TRRTYPE_CNAME:
|
|
|
|
if (mCname.IsEmpty()) {
|
|
|
|
nsAutoCString qname;
|
|
|
|
unsigned int qnameindex = index;
|
|
|
|
rv = GetQname(qname, qnameindex);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2018-05-08 20:30:07 +03:00
|
|
|
if (!qname.IsEmpty()) {
|
2020-05-25 16:06:23 +03:00
|
|
|
ToLowerCase(qname);
|
2018-05-08 20:30:07 +03:00
|
|
|
mCname = qname;
|
|
|
|
LOG(("TRR::DohDecode CNAME host %s => %s\n", host.get(),
|
|
|
|
mCname.get()));
|
2018-11-30 13:46:48 +03:00
|
|
|
} else {
|
2018-05-08 20:30:07 +03:00
|
|
|
LOG(("TRR::DohDecode empty CNAME for host %s!\n", host.get()));
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2018-02-01 12:20:49 +03:00
|
|
|
} else {
|
2018-05-08 20:30:07 +03:00
|
|
|
LOG(("TRR::DohDecode CNAME - ignoring another entry\n"));
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
2018-05-08 20:30:07 +03:00
|
|
|
break;
|
2018-09-22 23:54:11 +03:00
|
|
|
case TRRTYPE_TXT: {
|
|
|
|
// TXT record RRDATA sections are a series of character-strings
|
|
|
|
// each character string is a length byte followed by that many data
|
|
|
|
// bytes
|
|
|
|
nsAutoCString txt;
|
|
|
|
unsigned int txtIndex = index;
|
|
|
|
uint16_t available = RDLENGTH;
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2018-09-22 23:54:11 +03:00
|
|
|
while (available > 0) {
|
|
|
|
uint8_t characterStringLen = mResponse[txtIndex++];
|
|
|
|
available--;
|
|
|
|
if (characterStringLen > available) {
|
|
|
|
LOG(("TRR::DohDecode MALFORMED TXT RECORD\n"));
|
|
|
|
break;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2018-09-22 23:54:11 +03:00
|
|
|
txt.Append((const char*)(&mResponse[txtIndex]), characterStringLen);
|
|
|
|
txtIndex += characterStringLen;
|
|
|
|
available -= characterStringLen;
|
|
|
|
}
|
|
|
|
|
2020-05-04 22:56:51 +03:00
|
|
|
if (!mResult.is<TypeRecordTxt>()) {
|
2020-05-05 13:40:41 +03:00
|
|
|
mResult = AsVariant(CopyableTArray<nsCString>());
|
2020-05-04 22:56:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
auto& results = mResult.as<TypeRecordTxt>();
|
|
|
|
results.AppendElement(txt);
|
|
|
|
}
|
|
|
|
if (mTTL > TTL) {
|
|
|
|
mTTL = TTL;
|
2018-11-30 13:46:48 +03:00
|
|
|
}
|
2018-02-01 12:20:49 +03:00
|
|
|
LOG(("TRR::DohDecode TXT host %s => %s\n", host.get(), txt.get()));
|
2020-05-04 22:56:51 +03:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TRRTYPE_HTTPSSVC: {
|
|
|
|
struct SVCB parsed;
|
|
|
|
|
|
|
|
unsigned int svcbIndex = index;
|
|
|
|
CheckedInt<uint16_t> available = RDLENGTH;
|
|
|
|
|
|
|
|
// Should have at least 2 bytes for the priority and one for the
|
|
|
|
// qname length.
|
|
|
|
if (available.value() < 3) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
parsed.mSvcFieldPriority = get16bit(mResponse, svcbIndex);
|
|
|
|
svcbIndex += 2;
|
|
|
|
|
|
|
|
rv = GetQname(parsed.mSvcDomainName, svcbIndex);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
available -= (svcbIndex - index);
|
|
|
|
if (!available.isValid()) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
while (available.value() >= 4) {
|
|
|
|
// Every SvcFieldValues must have at least 4 bytes for the
|
|
|
|
// SvcParamKey (2 bytes) and length of SvcParamValue (2 bytes)
|
|
|
|
// If the length ever goes above the available data, meaning if
|
|
|
|
// available ever underflows, then that is an error.
|
|
|
|
struct SvcFieldValue value;
|
|
|
|
uint16_t key = get16bit(mResponse, svcbIndex);
|
|
|
|
svcbIndex += 2;
|
|
|
|
|
|
|
|
uint16_t len = get16bit(mResponse, svcbIndex);
|
|
|
|
svcbIndex += 2;
|
|
|
|
|
|
|
|
available -= 4 + len;
|
|
|
|
if (!available.isValid()) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
rv = ParseSvcParam(svcbIndex, key, value, len);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
svcbIndex += len;
|
|
|
|
|
|
|
|
// If this is an unknown key, we will simply ignore it.
|
|
|
|
if (key == SvcParamKeyNone || key > SvcParamKeyLast) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
parsed.mSvcFieldValue.AppendElement(value);
|
|
|
|
}
|
|
|
|
|
2020-06-30 15:27:09 +03:00
|
|
|
// Check for AliasForm
|
|
|
|
if (mCname.IsEmpty() && parsed.mSvcFieldPriority == 0) {
|
|
|
|
// Alias form SvcDomainName must not have the "." value (empty)
|
|
|
|
if (parsed.mSvcDomainName.IsEmpty()) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
mCname = parsed.mSvcDomainName;
|
|
|
|
ToLowerCase(mCname);
|
|
|
|
LOG(("TRR::DohDecode HTTPSSVC AliasForm host %s => %s\n",
|
|
|
|
host.get(), mCname.get()));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mType != TRRTYPE_HTTPSSVC) {
|
|
|
|
// Ignore the entry that we just parsed if we didn't ask for it.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-05-04 22:56:51 +03:00
|
|
|
if (!mResult.is<TypeRecordHTTPSSVC>()) {
|
2020-05-05 13:40:41 +03:00
|
|
|
mResult = mozilla::AsVariant(CopyableTArray<SVCB>());
|
2020-05-04 22:56:51 +03:00
|
|
|
}
|
|
|
|
{
|
|
|
|
auto& results = mResult.as<TypeRecordHTTPSSVC>();
|
|
|
|
results.AppendElement(parsed);
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
break;
|
2018-09-22 23:54:11 +03:00
|
|
|
}
|
2018-05-08 20:30:07 +03:00
|
|
|
default:
|
|
|
|
// skip unknown record types
|
|
|
|
LOG(("TRR unsupported TYPE (%u) RDLENGTH %u\n", TYPE, RDLENGTH));
|
|
|
|
break;
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
2018-05-08 20:30:07 +03:00
|
|
|
} else {
|
|
|
|
LOG(("TRR asked for %s data but got %s\n", aHost.get(), qname.get()));
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
index += RDLENGTH;
|
|
|
|
LOG(("done with record type %u len %u index now %u of %u\n", TYPE, RDLENGTH,
|
|
|
|
index, mBodySize));
|
|
|
|
answerRecords--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NSCOUNT
|
|
|
|
uint16_t nsRecords = get16bit(mResponse, 8);
|
|
|
|
LOG(("TRR Decode: %d ns records (%u bytes body)\n", nsRecords, mBodySize));
|
|
|
|
while (nsRecords) {
|
2018-04-06 00:28:26 +03:00
|
|
|
rv = PassQName(index);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mBodySize < (index + 8)) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
index += 2; // type
|
|
|
|
index += 2; // class
|
|
|
|
index += 4; // ttl
|
|
|
|
|
|
|
|
// 16 bit RDLENGTH
|
|
|
|
if (mBodySize < (index + 2)) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
uint16_t RDLENGTH = get16bit(mResponse, index);
|
|
|
|
index += 2;
|
|
|
|
if (mBodySize < (index + RDLENGTH)) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
index += RDLENGTH;
|
|
|
|
LOG(("done with nsRecord now %u of %u\n", index, mBodySize));
|
|
|
|
nsRecords--;
|
|
|
|
}
|
|
|
|
|
|
|
|
// additional resource records
|
|
|
|
uint16_t arRecords = get16bit(mResponse, 10);
|
|
|
|
LOG(("TRR Decode: %d additional resource records (%u bytes body)\n",
|
|
|
|
arRecords, mBodySize));
|
|
|
|
while (arRecords) {
|
2018-04-06 00:28:26 +03:00
|
|
|
rv = PassQName(index);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
return rv;
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mBodySize < (index + 8)) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
index += 2; // type
|
|
|
|
index += 2; // class
|
|
|
|
index += 4; // ttl
|
|
|
|
|
|
|
|
// 16 bit RDLENGTH
|
|
|
|
if (mBodySize < (index + 2)) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
uint16_t RDLENGTH = get16bit(mResponse, index);
|
|
|
|
index += 2;
|
|
|
|
if (mBodySize < (index + RDLENGTH)) {
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
index += RDLENGTH;
|
|
|
|
LOG(("done with additional rr now %u of %u\n", index, mBodySize));
|
|
|
|
arRecords--;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index != mBodySize) {
|
|
|
|
LOG(("DohDecode failed to parse entire response body, %u out of %u bytes\n",
|
|
|
|
index, mBodySize));
|
|
|
|
// failed to parse 100%, do not continue
|
|
|
|
return NS_ERROR_ILLEGAL_VALUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((mType != TRRTYPE_NS) && mCname.IsEmpty() &&
|
2020-05-04 22:56:51 +03:00
|
|
|
!mDNS.mAddresses.getFirst() && mResult.is<TypeRecordEmpty>()) {
|
2018-02-01 12:20:49 +03:00
|
|
|
// no entries were stored!
|
|
|
|
LOG(("TRR: No entries were stored!\n"));
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-05-04 22:56:51 +03:00
|
|
|
nsresult TRR::ParseSvcParam(unsigned int svcbIndex, uint16_t key,
|
|
|
|
SvcFieldValue& field, uint16_t length) {
|
|
|
|
switch (key) {
|
|
|
|
case SvcParamKeyAlpn: {
|
|
|
|
field.mValue = AsVariant(SvcParamAlpn{
|
|
|
|
.mValue = nsCString((const char*)(&mResponse[svcbIndex]), length)});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SvcParamKeyNoDefaultAlpn: {
|
|
|
|
if (length != 0) {
|
|
|
|
// This key should not contain a value
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
field.mValue = AsVariant(SvcParamNoDefaultAlpn{});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SvcParamKeyPort: {
|
|
|
|
if (length != 2) {
|
|
|
|
// This key should only encode a uint16_t
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
field.mValue =
|
|
|
|
AsVariant(SvcParamPort{.mValue = get16bit(mResponse, svcbIndex)});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SvcParamKeyIpv4Hint: {
|
|
|
|
if (length % 4 != 0) {
|
|
|
|
// This key should only encode IPv4 addresses
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
field.mValue = AsVariant(SvcParamIpv4Hint());
|
|
|
|
auto& ipv4array = field.mValue.as<SvcParamIpv4Hint>().mValue;
|
|
|
|
while (length > 0) {
|
|
|
|
NetAddr addr = {.inet = {.family = AF_INET,
|
|
|
|
.port = 0,
|
|
|
|
.ip = ntohl(get32bit(mResponse, svcbIndex))}};
|
|
|
|
ipv4array.AppendElement(addr);
|
|
|
|
length -= 4;
|
|
|
|
svcbIndex += 4;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SvcParamKeyEsniConfig: {
|
|
|
|
field.mValue = AsVariant(SvcParamEsniConfig{
|
|
|
|
.mValue = nsCString((const char*)(&mResponse[svcbIndex]), length)});
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SvcParamKeyIpv6Hint: {
|
|
|
|
if (length % 16 != 0) {
|
|
|
|
// This key should only encode IPv6 addresses
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
field.mValue = AsVariant(SvcParamIpv6Hint());
|
|
|
|
auto& ipv6array = field.mValue.as<SvcParamIpv6Hint>().mValue;
|
|
|
|
while (length > 0) {
|
|
|
|
NetAddr addr = {{.family = 0, .data = {0}}};
|
|
|
|
addr.inet6.family = AF_INET6;
|
|
|
|
addr.inet6.port = 0; // unknown
|
|
|
|
addr.inet6.flowinfo = 0; // unknown
|
|
|
|
addr.inet6.scope_id = 0; // unknown
|
|
|
|
for (int i = 0; i < 16; i++, svcbIndex++) {
|
|
|
|
addr.inet6.ip.u8[i] = mResponse[svcbIndex];
|
|
|
|
}
|
|
|
|
ipv6array.AppendElement(addr);
|
|
|
|
length -= 16;
|
|
|
|
// no need to increase svcbIndex - we did it in the for above.
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
// Unespected type. We'll just ignore it.
|
|
|
|
return NS_OK;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-02-04 14:09:11 +03:00
|
|
|
nsresult TRR::ReturnData(nsIChannel* aChannel) {
|
2020-05-04 22:56:51 +03:00
|
|
|
if (mType != TRRTYPE_TXT && mType != TRRTYPE_HTTPSSVC) {
|
2018-09-22 23:54:11 +03:00
|
|
|
// create and populate an AddrInfo instance to pass on
|
2019-03-19 15:22:12 +03:00
|
|
|
RefPtr<AddrInfo> ai(new AddrInfo(mHost, mType));
|
2018-09-22 23:54:11 +03:00
|
|
|
DOHaddr* item;
|
|
|
|
uint32_t ttl = AddrInfo::NO_TTL_DATA;
|
|
|
|
while ((item = static_cast<DOHaddr*>(mDNS.mAddresses.popFirst()))) {
|
|
|
|
PRNetAddr prAddr;
|
|
|
|
NetAddrToPRNetAddr(&item->mNet, &prAddr);
|
|
|
|
auto* addrElement = new NetAddrElement(&prAddr);
|
|
|
|
ai->AddAddress(addrElement);
|
|
|
|
if (item->mTtl < ttl) {
|
|
|
|
// While the DNS packet might return individual TTLs for each address,
|
|
|
|
// we can only return one value in the AddrInfo class so pick the
|
|
|
|
// lowest number.
|
|
|
|
ttl = item->mTtl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ai->ttl = ttl;
|
2020-02-04 14:09:11 +03:00
|
|
|
|
|
|
|
// Set timings.
|
|
|
|
nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
|
|
|
|
if (timedChan) {
|
|
|
|
TimeStamp asyncOpen, start, end;
|
2020-02-07 12:42:00 +03:00
|
|
|
if (NS_SUCCEEDED(timedChan->GetAsyncOpen(&asyncOpen)) &&
|
|
|
|
!asyncOpen.IsNull()) {
|
|
|
|
ai->SetTrrFetchDuration(
|
|
|
|
(TimeStamp::Now() - asyncOpen).ToMilliseconds());
|
2020-02-04 14:09:11 +03:00
|
|
|
}
|
|
|
|
if (NS_SUCCEEDED(timedChan->GetRequestStart(&start)) &&
|
2020-02-07 12:42:00 +03:00
|
|
|
NS_SUCCEEDED(timedChan->GetResponseEnd(&end)) && !start.IsNull() &&
|
|
|
|
!end.IsNull()) {
|
2020-02-04 14:09:11 +03:00
|
|
|
ai->SetTrrFetchDurationNetworkOnly((end - start).ToMilliseconds());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-22 23:54:11 +03:00
|
|
|
if (!mHostResolver) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
2020-07-11 22:32:05 +03:00
|
|
|
(void)mHostResolver->CompleteLookup(mRec, NS_OK, ai, mPB, mOriginSuffix,
|
|
|
|
mTRRSkippedReason);
|
2018-09-22 23:54:11 +03:00
|
|
|
mHostResolver = nullptr;
|
|
|
|
mRec = nullptr;
|
|
|
|
} else {
|
2020-05-04 22:56:51 +03:00
|
|
|
(void)mHostResolver->CompleteLookupByType(mRec, NS_OK, mResult, mTTL, mPB);
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2018-06-20 12:00:19 +03:00
|
|
|
nsresult TRR::FailData(nsresult error) {
|
2018-02-01 12:20:49 +03:00
|
|
|
if (!mHostResolver) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2020-07-11 22:32:05 +03:00
|
|
|
// If we didn't record a reason until now, record a default one.
|
|
|
|
RecordReason(nsHostRecord::TRR_FAILED);
|
|
|
|
|
2020-05-04 22:56:51 +03:00
|
|
|
if (mType == TRRTYPE_TXT || mType == TRRTYPE_HTTPSSVC) {
|
|
|
|
TypeRecordResultType empty(Nothing{});
|
|
|
|
(void)mHostResolver->CompleteLookupByType(mRec, error, empty, 0, mPB);
|
2018-09-22 23:54:11 +03:00
|
|
|
} else {
|
|
|
|
// create and populate an TRR AddrInfo instance to pass on to signal that
|
|
|
|
// this comes from TRR
|
2019-03-19 15:22:12 +03:00
|
|
|
RefPtr<AddrInfo> ai = new AddrInfo(mHost, mType);
|
2018-09-22 23:54:11 +03:00
|
|
|
|
2020-07-11 22:32:05 +03:00
|
|
|
(void)mHostResolver->CompleteLookup(mRec, error, ai, mPB, mOriginSuffix,
|
|
|
|
mTRRSkippedReason);
|
2018-09-22 23:54:11 +03:00
|
|
|
}
|
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
mHostResolver = nullptr;
|
|
|
|
mRec = nullptr;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-07-01 15:34:15 +03:00
|
|
|
nsresult TRR::FollowCname(nsIChannel* aChannel) {
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
nsAutoCString cname;
|
|
|
|
while (NS_SUCCEEDED(rv) && !mDNS.mAddresses.getFirst() && !mCname.IsEmpty() &&
|
|
|
|
mCnameLoop > 0) {
|
|
|
|
mCnameLoop--;
|
|
|
|
LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
|
|
|
|
mCnameLoop));
|
|
|
|
cname = mCname;
|
|
|
|
mCname = EmptyCString();
|
|
|
|
|
|
|
|
LOG(("TRR: check for CNAME record for %s within previous response\n",
|
|
|
|
cname.get()));
|
|
|
|
rv = DohDecode(cname);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// restore mCname as DohDecode() change it
|
|
|
|
mCname = cname;
|
|
|
|
if (NS_SUCCEEDED(rv) && mDNS.mAddresses.getFirst()) {
|
|
|
|
ReturnData(aChannel);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mCnameLoop) {
|
|
|
|
LOG(("TRR::On200Response CNAME loop, eject!\n"));
|
|
|
|
return NS_ERROR_REDIRECT_LOOP;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
|
|
|
|
mCnameLoop));
|
|
|
|
RefPtr<TRR> trr =
|
|
|
|
new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB);
|
|
|
|
if (!gTRRService) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
return gTRRService->DispatchTRRRequest(trr);
|
|
|
|
}
|
|
|
|
|
2020-02-04 14:09:11 +03:00
|
|
|
nsresult TRR::On200Response(nsIChannel* aChannel) {
|
2018-02-01 12:20:49 +03:00
|
|
|
// decode body and create an AddrInfo struct for the response
|
2018-05-08 20:30:07 +03:00
|
|
|
nsresult rv = DohDecode(mHost);
|
2018-02-01 12:20:49 +03:00
|
|
|
|
2020-07-01 15:34:15 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
2018-02-01 12:20:49 +03:00
|
|
|
LOG(("TRR::On200Response DohDecode %x\n", (int)rv));
|
2020-07-11 22:32:05 +03:00
|
|
|
RecordReason(nsHostRecord::TRR_DECODE_FAILED);
|
2020-07-01 15:34:15 +03:00
|
|
|
return NS_ERROR_FAILURE;
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
2020-07-01 15:34:15 +03:00
|
|
|
|
|
|
|
if (mDNS.mAddresses.getFirst() || mType == TRRTYPE_TXT || mCname.IsEmpty()) {
|
|
|
|
// pass back the response data
|
|
|
|
ReturnData(aChannel);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(("TRR::On200Response trying CNAME %s", mCname.get()));
|
|
|
|
return FollowCname(aChannel);
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
|
|
|
|
2020-01-14 15:11:46 +03:00
|
|
|
static void RecordProcessingTime(nsIChannel* aChannel) {
|
|
|
|
// This method records the time it took from the last received byte of the
|
|
|
|
// DoH response until we've notified the consumer with a host record.
|
|
|
|
nsCOMPtr<nsITimedChannel> timedChan = do_QueryInterface(aChannel);
|
|
|
|
if (!timedChan) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
TimeStamp end;
|
|
|
|
if (NS_FAILED(timedChan->GetResponseEnd(&end))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (end.IsNull()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Telemetry::AccumulateTimeDelta(Telemetry::DNS_TRR_PROCESSING_TIME, end);
|
|
|
|
|
|
|
|
LOG(("Processing DoH response took %f ms",
|
|
|
|
(TimeStamp::Now() - end).ToMilliseconds()));
|
|
|
|
}
|
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
NS_IMETHODIMP
|
2019-02-28 02:41:31 +03:00
|
|
|
TRR::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
|
2018-02-01 12:20:49 +03:00
|
|
|
// The dtor will be run after the function returns
|
|
|
|
LOG(("TRR:OnStopRequest %p %s %d failed=%d code=%X\n", this, mHost.get(),
|
|
|
|
mType, mFailed, (unsigned int)aStatusCode));
|
|
|
|
nsCOMPtr<nsIChannel> channel;
|
|
|
|
channel.swap(mChannel);
|
|
|
|
|
2020-05-07 00:58:37 +03:00
|
|
|
{
|
|
|
|
// Cancel the timer since we don't need it anymore.
|
|
|
|
nsCOMPtr<nsITimer> timer;
|
|
|
|
mTimeout.swap(timer);
|
|
|
|
if (timer) {
|
|
|
|
timer->Cancel();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-06 21:09:38 +03:00
|
|
|
if (UseDefaultServer()) {
|
2020-02-04 22:35:34 +03:00
|
|
|
// Bad content is still considered "okay" if the HTTP response is okay
|
|
|
|
gTRRService->TRRIsOkay(NS_SUCCEEDED(aStatusCode) ? TRRService::OKAY_NORMAL
|
|
|
|
: TRRService::OKAY_BAD);
|
|
|
|
}
|
2018-10-03 14:53:46 +03:00
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
// if status was "fine", parse the response and pass on the answer
|
|
|
|
if (!mFailed && NS_SUCCEEDED(aStatusCode)) {
|
|
|
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest);
|
|
|
|
if (!httpChannel) {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
nsAutoCString contentType;
|
|
|
|
httpChannel->GetContentType(contentType);
|
|
|
|
if (contentType.Length() &&
|
2018-08-08 18:01:05 +03:00
|
|
|
!contentType.LowerCaseEqualsLiteral("application/dns-message")) {
|
|
|
|
LOG(("TRR:OnStopRequest %p %s %d wrong content type %s\n", this,
|
2018-02-01 12:20:49 +03:00
|
|
|
mHost.get(), mType, contentType.get()));
|
2018-06-20 12:00:19 +03:00
|
|
|
FailData(NS_ERROR_UNEXPECTED);
|
2018-02-01 12:20:49 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t httpStatus;
|
|
|
|
rv = httpChannel->GetResponseStatus(&httpStatus);
|
|
|
|
if (NS_SUCCEEDED(rv) && httpStatus == 200) {
|
2020-02-04 14:09:11 +03:00
|
|
|
rv = On200Response(channel);
|
2020-02-06 21:09:38 +03:00
|
|
|
if (NS_SUCCEEDED(rv) && UseDefaultServer()) {
|
2020-07-11 22:32:05 +03:00
|
|
|
RecordReason(nsHostRecord::TRR_OK);
|
2020-01-14 15:11:46 +03:00
|
|
|
RecordProcessingTime(channel);
|
2018-02-01 12:20:49 +03:00
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
} else {
|
2020-07-11 22:32:05 +03:00
|
|
|
RecordReason(nsHostRecord::TRR_SERVER_RESPONSE_ERR);
|
2018-02-01 12:20:49 +03:00
|
|
|
LOG(("TRR:OnStopRequest:%d %p rv %x httpStatus %d\n", __LINE__, this,
|
|
|
|
(int)rv, httpStatus));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(("TRR:OnStopRequest %p status %x mFailed %d\n", this, (int)aStatusCode,
|
|
|
|
mFailed));
|
2018-06-20 12:00:19 +03:00
|
|
|
FailData(NS_ERROR_UNKNOWN_HOST);
|
2018-02-01 12:20:49 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2019-02-28 02:42:27 +03:00
|
|
|
TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
|
2018-02-01 12:20:49 +03:00
|
|
|
uint64_t aOffset, const uint32_t aCount) {
|
|
|
|
LOG(("TRR:OnDataAvailable %p %s %d failed=%d aCount=%u\n", this, mHost.get(),
|
|
|
|
mType, mFailed, (unsigned int)aCount));
|
|
|
|
// receive DNS response into the local buffer
|
|
|
|
if (mFailed) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aCount + mBodySize > kMaxSize) {
|
|
|
|
LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__));
|
|
|
|
mFailed = true;
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t count;
|
|
|
|
nsresult rv =
|
|
|
|
aInputStream->Read((char*)mResponse + mBodySize, aCount, &count);
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
LOG(("TRR::OnDataAvailable:%d fail\n", __LINE__));
|
|
|
|
mFailed = true;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
MOZ_ASSERT(count == aCount);
|
|
|
|
mBodySize += aCount;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2020-05-04 22:56:51 +03:00
|
|
|
nsresult DOHresp::Add(uint32_t TTL, unsigned char* dns, unsigned int index,
|
|
|
|
uint16_t len, bool aLocalAllowed) {
|
2020-02-11 19:20:08 +03:00
|
|
|
auto doh = MakeUnique<DOHaddr>();
|
2018-02-01 12:20:49 +03:00
|
|
|
NetAddr* addr = &doh->mNet;
|
|
|
|
if (4 == len) {
|
|
|
|
// IPv4
|
|
|
|
addr->inet.family = AF_INET;
|
|
|
|
addr->inet.port = 0; // unknown
|
|
|
|
addr->inet.ip = ntohl(get32bit(dns, index));
|
|
|
|
} else if (16 == len) {
|
|
|
|
// IPv6
|
|
|
|
addr->inet6.family = AF_INET6;
|
|
|
|
addr->inet6.port = 0; // unknown
|
|
|
|
addr->inet6.flowinfo = 0; // unknown
|
|
|
|
addr->inet6.scope_id = 0; // unknown
|
|
|
|
for (int i = 0; i < 16; i++, index++) {
|
|
|
|
addr->inet6.ip.u8[i] = dns[index];
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsIPAddrLocal(addr) && !aLocalAllowed) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
doh->mTtl = TTL;
|
|
|
|
|
|
|
|
if (LOG_ENABLED()) {
|
|
|
|
char buf[128];
|
|
|
|
NetAddrToString(addr, buf, sizeof(buf));
|
|
|
|
LOG(("DOHresp:Add %s\n", buf));
|
|
|
|
}
|
2020-02-11 19:20:08 +03:00
|
|
|
mAddresses.insertBack(doh.release());
|
2018-02-01 12:20:49 +03:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
class ProxyCancel : public Runnable {
|
|
|
|
public:
|
|
|
|
explicit ProxyCancel(TRR* aTRR) : Runnable("proxyTrrCancel"), mTRR(aTRR) {}
|
|
|
|
|
|
|
|
NS_IMETHOD Run() override {
|
|
|
|
mTRR->Cancel();
|
|
|
|
mTRR = nullptr;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
RefPtr<TRR> mTRR;
|
|
|
|
};
|
|
|
|
|
|
|
|
void TRR::Cancel() {
|
2020-06-22 14:10:21 +03:00
|
|
|
RefPtr<TRRServiceChannel> trrServiceChannel = do_QueryObject(mChannel);
|
|
|
|
if (trrServiceChannel && !XRE_IsSocketProcess()) {
|
2020-03-04 19:11:16 +03:00
|
|
|
if (gTRRService) {
|
|
|
|
nsCOMPtr<nsIThread> thread = gTRRService->TRRThread();
|
|
|
|
if (thread && !thread->IsOnCurrentThread()) {
|
|
|
|
nsCOMPtr<nsIRunnable> r = new ProxyCancel(this);
|
|
|
|
thread->Dispatch(r.forget());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!NS_IsMainThread()) {
|
|
|
|
NS_DispatchToMainThread(new ProxyCancel(this));
|
|
|
|
return;
|
|
|
|
}
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
2020-03-04 19:11:16 +03:00
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
if (mChannel) {
|
2020-07-11 22:32:05 +03:00
|
|
|
RecordReason(nsHostRecord::TRR_TIMEOUT);
|
2018-02-01 12:20:49 +03:00
|
|
|
LOG(("TRR: %p canceling Channel %p %s %d\n", this, mChannel.get(),
|
|
|
|
mHost.get(), mType));
|
|
|
|
mChannel->Cancel(NS_ERROR_ABORT);
|
2020-02-06 21:09:38 +03:00
|
|
|
if (UseDefaultServer()) {
|
|
|
|
gTRRService->TRRIsOkay(TRRService::OKAY_TIMEOUT);
|
|
|
|
}
|
2018-02-01 12:20:49 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-11 19:20:08 +03:00
|
|
|
bool TRR::UseDefaultServer() { return !mRec || mRec->mTrrServer.IsEmpty(); }
|
2020-02-06 21:09:38 +03:00
|
|
|
|
2018-02-01 12:20:49 +03:00
|
|
|
#undef LOG
|
|
|
|
|
|
|
|
// namespace
|
|
|
|
} // namespace net
|
|
|
|
} // namespace mozilla
|