forked from mirrors/gecko-dev
Backed out changeset af645c9220f1 (bug 1835805) for causing bustage on DNSPacket.cpp. CLOSED TREE
This commit is contained in:
parent
9691ab4a5f
commit
448ccac0b9
52 changed files with 2883 additions and 35 deletions
|
|
@ -1424,6 +1424,7 @@ testing/xpcshell/dns-packet/
|
||||||
testing/xpcshell/node_ip/
|
testing/xpcshell/node_ip/
|
||||||
testing/xpcshell/node-http2/
|
testing/xpcshell/node-http2/
|
||||||
testing/xpcshell/node-ws/
|
testing/xpcshell/node-ws/
|
||||||
|
testing/xpcshell/odoh-wasm/
|
||||||
third_party/
|
third_party/
|
||||||
toolkit/components/certviewer/content/vendor/
|
toolkit/components/certviewer/content/vendor/
|
||||||
toolkit/components/jsoncpp/
|
toolkit/components/jsoncpp/
|
||||||
|
|
|
||||||
|
|
@ -12199,6 +12199,42 @@
|
||||||
value: true
|
value: true
|
||||||
mirror: always
|
mirror: always
|
||||||
|
|
||||||
|
# Whether to enable odoh.
|
||||||
|
- name: network.trr.odoh.enabled
|
||||||
|
type: RelaxedAtomicBool
|
||||||
|
value: false
|
||||||
|
mirror: always
|
||||||
|
|
||||||
|
# The uri of Oblivious Proxy.
|
||||||
|
- name: network.trr.odoh.proxy_uri
|
||||||
|
type: String
|
||||||
|
value: ""
|
||||||
|
mirror: never
|
||||||
|
|
||||||
|
# The host name of Oblivious Target.
|
||||||
|
- name: network.trr.odoh.target_host
|
||||||
|
type: String
|
||||||
|
value: ""
|
||||||
|
mirror: never
|
||||||
|
|
||||||
|
# The uri path of the odoh uri.
|
||||||
|
- name: network.trr.odoh.target_path
|
||||||
|
type: String
|
||||||
|
value: ""
|
||||||
|
mirror: never
|
||||||
|
|
||||||
|
# The minimum ttl of the DNS record that contains ODoHConfigs.
|
||||||
|
- name: network.trr.odoh.min_ttl
|
||||||
|
type: RelaxedAtomicUint32
|
||||||
|
value: 60
|
||||||
|
mirror: always
|
||||||
|
|
||||||
|
# The uri indicates where to get ODoHConfigs.
|
||||||
|
- name: network.trr.odoh.configs_uri
|
||||||
|
type: String
|
||||||
|
value: ""
|
||||||
|
mirror: never
|
||||||
|
|
||||||
# Whether to add padding in the doh dns queries (rfc 7830)
|
# Whether to add padding in the doh dns queries (rfc 7830)
|
||||||
- name: network.trr.padding
|
- name: network.trr.padding
|
||||||
type: RelaxedAtomicBool
|
type: RelaxedAtomicBool
|
||||||
|
|
|
||||||
|
|
@ -1001,6 +1001,23 @@ void nsLoadGroup::TelemetryReportChannel(nsITimedChannel* aTimedChannel,
|
||||||
asyncOpen, requestStart);
|
asyncOpen, requestStart);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StaticPrefs::network_trr_odoh_enabled() && !domainLookupStart.IsNull() &&
|
||||||
|
!domainLookupEnd.IsNull()) {
|
||||||
|
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
|
||||||
|
bool ODoHActivated = false;
|
||||||
|
if (dns && NS_SUCCEEDED(dns->GetODoHActivated(&ODoHActivated)) &&
|
||||||
|
ODoHActivated) {
|
||||||
|
if (aDefaultRequest) {
|
||||||
|
Telemetry::AccumulateTimeDelta(
|
||||||
|
Telemetry::HTTP_PAGE_DNS_ODOH_LOOKUP_TIME, domainLookupStart,
|
||||||
|
domainLookupEnd);
|
||||||
|
} else {
|
||||||
|
Telemetry::AccumulateTimeDelta(Telemetry::HTTP_SUB_DNS_ODOH_LOOKUP_TIME,
|
||||||
|
domainLookupStart, domainLookupEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#undef HTTP_REQUEST_HISTOGRAMS
|
#undef HTTP_REQUEST_HISTOGRAMS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -386,6 +386,14 @@ ChildDNSService::GetMyHostName(nsACString& result) {
|
||||||
return NS_ERROR_NOT_AVAILABLE;
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ChildDNSService::GetODoHActivated(bool* aResult) {
|
||||||
|
NS_ENSURE_ARG(aResult);
|
||||||
|
|
||||||
|
*aResult = mODoHActivated;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
void ChildDNSService::NotifyRequestDone(DNSRequestSender* aDnsRequest) {
|
void ChildDNSService::NotifyRequestDone(DNSRequestSender* aDnsRequest) {
|
||||||
// We need the original flags and listener for the pending requests hash.
|
// We need the original flags and listener for the pending requests hash.
|
||||||
nsIDNSService::DNSFlags originalFlags =
|
nsIDNSService::DNSFlags originalFlags =
|
||||||
|
|
@ -430,6 +438,12 @@ nsresult ChildDNSService::Init() {
|
||||||
AddPrefObserver(prefs);
|
AddPrefObserver(prefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIObserverService> observerService =
|
||||||
|
mozilla::services::GetObserverService();
|
||||||
|
if (observerService) {
|
||||||
|
observerService->AddObserver(this, "odoh-service-activated", false);
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -475,7 +489,10 @@ ChildDNSService::Observe(nsISupports* subject, const char* topic,
|
||||||
if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
||||||
// Reread prefs
|
// Reread prefs
|
||||||
ReadPrefs(NS_ConvertUTF16toUTF8(data).get());
|
ReadPrefs(NS_ConvertUTF16toUTF8(data).get());
|
||||||
|
} else if (!strcmp(topic, "odoh-service-activated")) {
|
||||||
|
mODoHActivated = u"true"_ns.Equals(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ class ChildDNSService final : public DNSServiceBase, public nsPIDNSService {
|
||||||
nsIDNSListener* aListener, nsresult aReason,
|
nsIDNSListener* aListener, nsresult aReason,
|
||||||
const OriginAttributes& aOriginAttributes);
|
const OriginAttributes& aOriginAttributes);
|
||||||
|
|
||||||
|
bool mODoHActivated = false;
|
||||||
|
|
||||||
// We need to remember pending dns requests to be able to cancel them.
|
// We need to remember pending dns requests to be able to cancel them.
|
||||||
nsClassHashtable<nsCStringHashKey, nsTArray<RefPtr<DNSRequestSender>>>
|
nsClassHashtable<nsCStringHashKey, nsTArray<RefPtr<DNSRequestSender>>>
|
||||||
mPendingRequests;
|
mPendingRequests;
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,39 @@ union NetAddr {
|
||||||
nsCString ToString() const;
|
nsCString ToString() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class DNSResolverType : uint32_t { Native = 0, TRR };
|
#define ODOH_VERSION 0x0001
|
||||||
|
static const char kODoHQuery[] = "odoh query";
|
||||||
|
static const char hODoHConfigID[] = "odoh key id";
|
||||||
|
static const char kODoHResponse[] = "odoh response";
|
||||||
|
static const char kODoHKey[] = "odoh key";
|
||||||
|
static const char kODoHNonce[] = "odoh nonce";
|
||||||
|
|
||||||
|
struct ObliviousDoHConfigContents {
|
||||||
|
uint16_t mKemId{};
|
||||||
|
uint16_t mKdfId{};
|
||||||
|
uint16_t mAeadId{};
|
||||||
|
nsTArray<uint8_t> mPublicKey;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObliviousDoHConfig {
|
||||||
|
uint16_t mVersion{};
|
||||||
|
uint16_t mLength{};
|
||||||
|
ObliviousDoHConfigContents mContents;
|
||||||
|
nsTArray<uint8_t> mConfigId;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ObliviousDoHMessageType : uint8_t {
|
||||||
|
ODOH_QUERY = 1,
|
||||||
|
ODOH_RESPONSE = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ObliviousDoHMessage {
|
||||||
|
ObliviousDoHMessageType mType{ODOH_QUERY};
|
||||||
|
nsTArray<uint8_t> mKeyId;
|
||||||
|
nsTArray<uint8_t> mEncryptedMessage;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DNSResolverType : uint32_t { Native = 0, TRR, ODoH };
|
||||||
|
|
||||||
class AddrInfo {
|
class AddrInfo {
|
||||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AddrInfo)
|
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AddrInfo)
|
||||||
|
|
@ -184,7 +216,10 @@ class AddrInfo {
|
||||||
|
|
||||||
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||||
|
|
||||||
bool IsTRR() const { return mResolverType == DNSResolverType::TRR; }
|
bool IsTRROrODoH() const {
|
||||||
|
return mResolverType == DNSResolverType::TRR ||
|
||||||
|
mResolverType == DNSResolverType::ODoH;
|
||||||
|
}
|
||||||
DNSResolverType ResolverType() const { return mResolverType; }
|
DNSResolverType ResolverType() const { return mResolverType; }
|
||||||
unsigned int TRRType() { return mTRRType; }
|
unsigned int TRRType() { return mTRRType; }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
#include "mozilla/EndianUtils.h"
|
#include "mozilla/EndianUtils.h"
|
||||||
#include "mozilla/ScopeExit.h"
|
#include "mozilla/ScopeExit.h"
|
||||||
#include "mozilla/StaticPrefs_network.h"
|
#include "mozilla/StaticPrefs_network.h"
|
||||||
|
#include "ODoHService.h"
|
||||||
// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
|
// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
|
||||||
#include "DNSLogging.h"
|
#include "DNSLogging.h"
|
||||||
|
|
||||||
|
|
@ -1076,5 +1077,552 @@ nsresult DNSPacket::Decode(
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SECItem* CreateRawConfig(const ObliviousDoHConfig& aConfig) {
|
||||||
|
SECItem* item(::SECITEM_AllocItem(nullptr, nullptr,
|
||||||
|
8 + aConfig.mContents.mPublicKey.Length()));
|
||||||
|
if (!item) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t index = 0;
|
||||||
|
NetworkEndian::writeUint16(&item->data[index], aConfig.mContents.mKemId);
|
||||||
|
index += 2;
|
||||||
|
NetworkEndian::writeUint16(&item->data[index], aConfig.mContents.mKdfId);
|
||||||
|
index += 2;
|
||||||
|
NetworkEndian::writeUint16(&item->data[index], aConfig.mContents.mAeadId);
|
||||||
|
index += 2;
|
||||||
|
uint16_t keyLength = aConfig.mContents.mPublicKey.Length();
|
||||||
|
NetworkEndian::writeUint16(&item->data[index], keyLength);
|
||||||
|
index += 2;
|
||||||
|
memcpy(&item->data[index], aConfig.mContents.mPublicKey.Elements(),
|
||||||
|
aConfig.mContents.mPublicKey.Length());
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateConfigId(ObliviousDoHConfig& aConfig) {
|
||||||
|
SECStatus rv;
|
||||||
|
CK_HKDF_PARAMS params = {0};
|
||||||
|
SECItem paramsi = {siBuffer, (unsigned char*)¶ms, sizeof(params)};
|
||||||
|
|
||||||
|
UniquePK11SlotInfo slot(PK11_GetInternalSlot());
|
||||||
|
if (!slot) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueSECItem rawConfig(CreateRawConfig(aConfig));
|
||||||
|
if (!rawConfig) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniquePK11SymKey configKey(PK11_ImportDataKey(slot.get(), CKM_HKDF_DATA,
|
||||||
|
PK11_OriginUnwrap, CKA_DERIVE,
|
||||||
|
rawConfig.get(), nullptr));
|
||||||
|
if (!configKey) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
params.bExtract = CK_TRUE;
|
||||||
|
params.bExpand = CK_TRUE;
|
||||||
|
params.prfHashMechanism = CKM_SHA256;
|
||||||
|
params.ulSaltType = CKF_HKDF_SALT_NULL;
|
||||||
|
params.pInfo = (unsigned char*)&hODoHConfigID[0];
|
||||||
|
params.ulInfoLen = strlen(hODoHConfigID);
|
||||||
|
UniquePK11SymKey derived(PK11_DeriveWithFlags(
|
||||||
|
configKey.get(), CKM_HKDF_DATA, ¶msi, CKM_HKDF_DERIVE, CKA_DERIVE,
|
||||||
|
SHA256_LENGTH, CKF_SIGN | CKF_VERIFY));
|
||||||
|
|
||||||
|
rv = PK11_ExtractKeyValue(derived.get());
|
||||||
|
if (rv != SECSuccess) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECItem* derivedItem = PK11_GetKeyData(derived.get());
|
||||||
|
if (!derivedItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (derivedItem->len != SHA256_LENGTH) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aConfig.mConfigId.AppendElements(derivedItem->data, derivedItem->len);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
bool ODoHDNSPacket::ParseODoHConfigs(Span<const uint8_t> aData,
|
||||||
|
nsTArray<ObliviousDoHConfig>& aOut) {
|
||||||
|
// struct {
|
||||||
|
// uint16 kem_id;
|
||||||
|
// uint16 kdf_id;
|
||||||
|
// uint16 aead_id;
|
||||||
|
// opaque public_key<1..2^16-1>;
|
||||||
|
// } ObliviousDoHConfigContents;
|
||||||
|
//
|
||||||
|
// struct {
|
||||||
|
// uint16 version;
|
||||||
|
// uint16 length;
|
||||||
|
// select (ObliviousDoHConfig.version) {
|
||||||
|
// case 0xff03: ObliviousDoHConfigContents contents;
|
||||||
|
// }
|
||||||
|
// } ObliviousDoHConfig;
|
||||||
|
//
|
||||||
|
// ObliviousDoHConfig ObliviousDoHConfigs<1..2^16-1>;
|
||||||
|
|
||||||
|
Span<const uint8_t>::const_iterator it = aData.begin();
|
||||||
|
uint16_t length = 0;
|
||||||
|
if (!get16bit(aData, it, length)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length != aData.Length() - 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsTArray<ObliviousDoHConfig> result;
|
||||||
|
static const int kMinimumConfigContentLength = 12;
|
||||||
|
while (std::distance(it, aData.cend()) > kMinimumConfigContentLength) {
|
||||||
|
ObliviousDoHConfig config;
|
||||||
|
if (!get16bit(aData, it, config.mVersion)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!get16bit(aData, it, config.mLength)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::distance(it, aData.cend()) < config.mLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!get16bit(aData, it, config.mContents.mKemId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!get16bit(aData, it, config.mContents.mKdfId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!get16bit(aData, it, config.mContents.mAeadId)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t keyLength = 0;
|
||||||
|
if (!get16bit(aData, it, keyLength)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!keyLength || std::distance(it, aData.cend()) < keyLength) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
config.mContents.mPublicKey.AppendElements(Span(it, it + keyLength));
|
||||||
|
it += keyLength;
|
||||||
|
|
||||||
|
CreateConfigId(config);
|
||||||
|
|
||||||
|
// Check if the version of the config is supported and validate its content.
|
||||||
|
if (config.mVersion == ODOH_VERSION &&
|
||||||
|
PK11_HPKE_ValidateParameters(
|
||||||
|
static_cast<HpkeKemId>(config.mContents.mKemId),
|
||||||
|
static_cast<HpkeKdfId>(config.mContents.mKdfId),
|
||||||
|
static_cast<HpkeAeadId>(config.mContents.mAeadId)) == SECSuccess) {
|
||||||
|
result.AppendElement(std::move(config));
|
||||||
|
} else {
|
||||||
|
LOG(("ODoHDNSPacket::ParseODoHConfigs got an invalid config"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
aOut = std::move(result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ODoHDNSPacket::~ODoHDNSPacket() { PK11_HPKE_DestroyContext(mContext, true); }
|
||||||
|
|
||||||
|
nsresult ODoHDNSPacket::EncodeRequest(nsCString& aBody, const nsACString& aHost,
|
||||||
|
uint16_t aType, bool aDisableECS) {
|
||||||
|
nsAutoCString queryBody;
|
||||||
|
nsresult rv = DNSPacket::EncodeRequest(queryBody, aHost, aType, aDisableECS);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
SetDNSPacketStatus(DNSPacketStatus::EncodeError);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gODoHService->ODoHConfigs()) {
|
||||||
|
SetDNSPacketStatus(DNSPacketStatus::KeyNotAvailable);
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gODoHService->ODoHConfigs()->IsEmpty()) {
|
||||||
|
SetDNSPacketStatus(DNSPacketStatus::KeyNotUsable);
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only use the first ODoHConfig.
|
||||||
|
const ObliviousDoHConfig& config = (*gODoHService->ODoHConfigs())[0];
|
||||||
|
|
||||||
|
ObliviousDoHMessage message;
|
||||||
|
// The spec didn't recommand padding length for encryption, let's use 0 here.
|
||||||
|
if (!EncryptDNSQuery(queryBody, 0, config, message)) {
|
||||||
|
SetDNSPacketStatus(DNSPacketStatus::EncryptError);
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
aBody.Truncate();
|
||||||
|
aBody += message.mType;
|
||||||
|
uint16_t keyIdLength = message.mKeyId.Length();
|
||||||
|
aBody += static_cast<uint8_t>(keyIdLength >> 8);
|
||||||
|
aBody += static_cast<uint8_t>(keyIdLength);
|
||||||
|
aBody.Append(reinterpret_cast<const char*>(message.mKeyId.Elements()),
|
||||||
|
keyIdLength);
|
||||||
|
uint16_t messageLen = message.mEncryptedMessage.Length();
|
||||||
|
aBody += static_cast<uint8_t>(messageLen >> 8);
|
||||||
|
aBody += static_cast<uint8_t>(messageLen);
|
||||||
|
aBody.Append(
|
||||||
|
reinterpret_cast<const char*>(message.mEncryptedMessage.Elements()),
|
||||||
|
messageLen);
|
||||||
|
|
||||||
|
SetDNSPacketStatus(DNSPacketStatus::Success);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* def encrypt_query_body(pkR, key_id, Q_plain):
|
||||||
|
* enc, context = SetupBaseS(pkR, "odoh query")
|
||||||
|
* aad = 0x01 || len(key_id) || key_id
|
||||||
|
* ct = context.Seal(aad, Q_plain)
|
||||||
|
* Q_encrypted = enc || ct
|
||||||
|
* return Q_encrypted
|
||||||
|
*/
|
||||||
|
bool ODoHDNSPacket::EncryptDNSQuery(const nsACString& aQuery,
|
||||||
|
uint16_t aPaddingLen,
|
||||||
|
const ObliviousDoHConfig& aConfig,
|
||||||
|
ObliviousDoHMessage& aOut) {
|
||||||
|
mContext = PK11_HPKE_NewContext(
|
||||||
|
static_cast<HpkeKemId>(aConfig.mContents.mKemId),
|
||||||
|
static_cast<HpkeKdfId>(aConfig.mContents.mKdfId),
|
||||||
|
static_cast<HpkeAeadId>(aConfig.mContents.mAeadId), nullptr, nullptr);
|
||||||
|
if (!mContext) {
|
||||||
|
LOG(("ODoHDNSPacket::EncryptDNSQuery create context failed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECKEYPublicKey* pkR;
|
||||||
|
SECStatus rv =
|
||||||
|
PK11_HPKE_Deserialize(mContext, aConfig.mContents.mPublicKey.Elements(),
|
||||||
|
aConfig.mContents.mPublicKey.Length(), &pkR);
|
||||||
|
if (rv != SECSuccess) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueSECItem hpkeInfo(
|
||||||
|
::SECITEM_AllocItem(nullptr, nullptr, strlen(kODoHQuery)));
|
||||||
|
if (!hpkeInfo) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(hpkeInfo->data, kODoHQuery, strlen(kODoHQuery));
|
||||||
|
|
||||||
|
rv = PK11_HPKE_SetupS(mContext, nullptr, nullptr, pkR, hpkeInfo.get());
|
||||||
|
if (rv != SECSuccess) {
|
||||||
|
LOG(("ODoHDNSPacket::EncryptDNSQuery setupS failed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SECItem* hpkeEnc = PK11_HPKE_GetEncapPubKey(mContext);
|
||||||
|
if (!hpkeEnc) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// aad = 0x01 || len(key_id) || key_id
|
||||||
|
UniqueSECItem aad(::SECITEM_AllocItem(nullptr, nullptr,
|
||||||
|
1 + 2 + aConfig.mConfigId.Length()));
|
||||||
|
if (!aad) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aad->data[0] = ODOH_QUERY;
|
||||||
|
NetworkEndian::writeUint16(&aad->data[1], aConfig.mConfigId.Length());
|
||||||
|
memcpy(&aad->data[3], aConfig.mConfigId.Elements(),
|
||||||
|
aConfig.mConfigId.Length());
|
||||||
|
|
||||||
|
// struct {
|
||||||
|
// opaque dns_message<1..2^16-1>;
|
||||||
|
// opaque padding<0..2^16-1>;
|
||||||
|
// } ObliviousDoHMessagePlaintext;
|
||||||
|
SECItem* odohPlainText(::SECITEM_AllocItem(
|
||||||
|
nullptr, nullptr, 2 + aQuery.Length() + 2 + aPaddingLen));
|
||||||
|
if (!odohPlainText) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPlainQuery.reset(odohPlainText);
|
||||||
|
memset(mPlainQuery->data, 0, mPlainQuery->len);
|
||||||
|
NetworkEndian::writeUint16(&mPlainQuery->data[0], aQuery.Length());
|
||||||
|
memcpy(&mPlainQuery->data[2], aQuery.BeginReading(), aQuery.Length());
|
||||||
|
NetworkEndian::writeUint16(&mPlainQuery->data[2 + aQuery.Length()],
|
||||||
|
aPaddingLen);
|
||||||
|
|
||||||
|
SECItem* chCt = nullptr;
|
||||||
|
rv = PK11_HPKE_Seal(mContext, aad.get(), mPlainQuery.get(), &chCt);
|
||||||
|
if (rv != SECSuccess) {
|
||||||
|
LOG(("ODoHDNSPacket::EncryptDNSQuery seal failed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniqueSECItem ct(chCt);
|
||||||
|
|
||||||
|
aOut.mType = ODOH_QUERY;
|
||||||
|
aOut.mKeyId.AppendElements(aConfig.mConfigId);
|
||||||
|
aOut.mEncryptedMessage.AppendElements(Span(hpkeEnc->data, hpkeEnc->len));
|
||||||
|
aOut.mEncryptedMessage.AppendElements(Span(ct->data, ct->len));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult ODoHDNSPacket::Decode(
|
||||||
|
nsCString& aHost, enum TrrType aType, nsCString& aCname, bool aAllowRFC1918,
|
||||||
|
DOHresp& aResp, TypeRecordResultType& aTypeResult,
|
||||||
|
nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
|
||||||
|
uint32_t& aTTL) {
|
||||||
|
// This function could be called multiple times when we are checking CNAME
|
||||||
|
// records, but we only need to decrypt the response once.
|
||||||
|
if (!mDecryptedResponseRange) {
|
||||||
|
if (!DecryptDNSResponse()) {
|
||||||
|
SetDNSPacketStatus(DNSPacketStatus::DecryptError);
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t index = 0;
|
||||||
|
uint16_t responseLength = get16bit(mResponse, index);
|
||||||
|
index += 2;
|
||||||
|
|
||||||
|
if (mBodySize < (index + responseLength)) {
|
||||||
|
SetDNSPacketStatus(DNSPacketStatus::DecryptError);
|
||||||
|
return NS_ERROR_ILLEGAL_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
DecryptedResponseRange range;
|
||||||
|
range.mStart = index;
|
||||||
|
range.mLength = responseLength;
|
||||||
|
|
||||||
|
index += responseLength;
|
||||||
|
uint16_t paddingLen = get16bit(mResponse, index);
|
||||||
|
|
||||||
|
if (static_cast<unsigned int>(4 + responseLength + paddingLen) !=
|
||||||
|
mBodySize) {
|
||||||
|
SetDNSPacketStatus(DNSPacketStatus::DecryptError);
|
||||||
|
return NS_ERROR_ILLEGAL_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDecryptedResponseRange.emplace(range);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv = DecodeInternal(aHost, aType, aCname, aAllowRFC1918, aResp,
|
||||||
|
aTypeResult, aAdditionalRecords, aTTL,
|
||||||
|
&mResponse[mDecryptedResponseRange->mStart],
|
||||||
|
mDecryptedResponseRange->mLength);
|
||||||
|
SetDNSPacketStatus(NS_SUCCEEDED(rv) ? DNSPacketStatus::Success
|
||||||
|
: DNSPacketStatus::DecodeError);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool CreateObliviousDoHMessage(const unsigned char* aData,
|
||||||
|
unsigned int aLength,
|
||||||
|
ObliviousDoHMessage& aOut) {
|
||||||
|
if (aLength < 5) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int index = 0;
|
||||||
|
aOut.mType = static_cast<ObliviousDoHMessageType>(aData[index++]);
|
||||||
|
|
||||||
|
uint16_t keyIdLength = get16bit(aData, index);
|
||||||
|
index += 2;
|
||||||
|
if (aLength < (index + keyIdLength)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aOut.mKeyId.AppendElements(Span(aData + index, keyIdLength));
|
||||||
|
index += keyIdLength;
|
||||||
|
|
||||||
|
uint16_t messageLen = get16bit(aData, index);
|
||||||
|
index += 2;
|
||||||
|
if (aLength < (index + messageLen)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
aOut.mEncryptedMessage.AppendElements(Span(aData + index, messageLen));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SECStatus HKDFExtract(SECItem* aSalt, PK11SymKey* aIkm,
|
||||||
|
UniquePK11SymKey& aOutKey) {
|
||||||
|
CK_HKDF_PARAMS params = {0};
|
||||||
|
SECItem paramsItem = {siBuffer, (unsigned char*)¶ms, sizeof(params)};
|
||||||
|
|
||||||
|
params.bExtract = CK_TRUE;
|
||||||
|
params.bExpand = CK_FALSE;
|
||||||
|
params.prfHashMechanism = CKM_SHA256;
|
||||||
|
params.ulSaltType = aSalt ? CKF_HKDF_SALT_DATA : CKF_HKDF_SALT_NULL;
|
||||||
|
params.pSalt = aSalt ? (CK_BYTE_PTR)aSalt->data : nullptr;
|
||||||
|
params.ulSaltLen = aSalt ? aSalt->len : 0;
|
||||||
|
|
||||||
|
UniquePK11SymKey prk(PK11_Derive(aIkm, CKM_HKDF_DERIVE, ¶msItem,
|
||||||
|
CKM_HKDF_DERIVE, CKA_DERIVE, 0));
|
||||||
|
if (!prk) {
|
||||||
|
return SECFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
aOutKey.swap(prk);
|
||||||
|
return SECSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SECStatus HKDFExpand(PK11SymKey* aPrk, const SECItem* aInfo, int aLen,
|
||||||
|
bool aKey, UniquePK11SymKey& aOutKey) {
|
||||||
|
CK_HKDF_PARAMS params = {0};
|
||||||
|
SECItem paramsItem = {siBuffer, (unsigned char*)¶ms, sizeof(params)};
|
||||||
|
|
||||||
|
params.bExtract = CK_FALSE;
|
||||||
|
params.bExpand = CK_TRUE;
|
||||||
|
params.prfHashMechanism = CKM_SHA256;
|
||||||
|
params.ulSaltType = CKF_HKDF_SALT_NULL;
|
||||||
|
params.pInfo = (CK_BYTE_PTR)aInfo->data;
|
||||||
|
params.ulInfoLen = aInfo->len;
|
||||||
|
CK_MECHANISM_TYPE deriveMech = CKM_HKDF_DERIVE;
|
||||||
|
CK_MECHANISM_TYPE keyMech = aKey ? CKM_AES_GCM : CKM_HKDF_DERIVE;
|
||||||
|
|
||||||
|
UniquePK11SymKey derivedKey(
|
||||||
|
PK11_Derive(aPrk, deriveMech, ¶msItem, keyMech, CKA_DERIVE, aLen));
|
||||||
|
if (!derivedKey) {
|
||||||
|
return SECFailure;
|
||||||
|
}
|
||||||
|
|
||||||
|
aOutKey.swap(derivedKey);
|
||||||
|
return SECSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* def decrypt_response_body(context, Q_plain, R_encrypted, response_nonce):
|
||||||
|
* aead_key, aead_nonce = derive_secrets(context, Q_plain, response_nonce)
|
||||||
|
* aad = 0x02 || len(response_nonce) || response_nonce
|
||||||
|
* R_plain, error = Open(key, nonce, aad, R_encrypted)
|
||||||
|
* return R_plain, error
|
||||||
|
*/
|
||||||
|
bool ODoHDNSPacket::DecryptDNSResponse() {
|
||||||
|
ObliviousDoHMessage message;
|
||||||
|
if (!CreateObliviousDoHMessage(mResponse, mBodySize, message)) {
|
||||||
|
LOG(("ODoHDNSPacket::DecryptDNSResponse invalid response"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.mType != ODOH_RESPONSE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unsigned int kResponseNonceLen = 16;
|
||||||
|
// KeyId is actually response_nonce
|
||||||
|
if (message.mKeyId.Length() != kResponseNonceLen) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// def derive_secrets(context, Q_plain, response_nonce):
|
||||||
|
// secret = context.Export("odoh response", Nk)
|
||||||
|
// salt = Q_plain || len(response_nonce) || response_nonce
|
||||||
|
// prk = Extract(salt, secret)
|
||||||
|
// key = Expand(odoh_prk, "odoh key", Nk)
|
||||||
|
// nonce = Expand(odoh_prk, "odoh nonce", Nn)
|
||||||
|
// return key, nonce
|
||||||
|
const SECItem kODoHResponsetInfoItem = {
|
||||||
|
siBuffer, (unsigned char*)kODoHResponse,
|
||||||
|
static_cast<unsigned int>(strlen(kODoHResponse))};
|
||||||
|
const unsigned int kAes128GcmKeyLen = 16;
|
||||||
|
const unsigned int kAes128GcmNonceLen = 12;
|
||||||
|
PK11SymKey* tmp = nullptr;
|
||||||
|
SECStatus rv = PK11_HPKE_ExportSecret(mContext, &kODoHResponsetInfoItem,
|
||||||
|
kAes128GcmKeyLen, &tmp);
|
||||||
|
if (rv != SECSuccess) {
|
||||||
|
LOG(("ODoHDNSPacket::DecryptDNSResponse export secret failed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
UniquePK11SymKey odohSecret(tmp);
|
||||||
|
|
||||||
|
SECItem* salt(::SECITEM_AllocItem(nullptr, nullptr,
|
||||||
|
mPlainQuery->len + 2 + kResponseNonceLen));
|
||||||
|
memcpy(salt->data, mPlainQuery->data, mPlainQuery->len);
|
||||||
|
NetworkEndian::writeUint16(&salt->data[mPlainQuery->len], kResponseNonceLen);
|
||||||
|
memcpy(salt->data + mPlainQuery->len + 2, message.mKeyId.Elements(),
|
||||||
|
kResponseNonceLen);
|
||||||
|
UniqueSECItem st(salt);
|
||||||
|
UniquePK11SymKey odohPrk;
|
||||||
|
rv = HKDFExtract(salt, odohSecret.get(), odohPrk);
|
||||||
|
if (rv != SECSuccess) {
|
||||||
|
LOG(("ODoHDNSPacket::DecryptDNSResponse extract failed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECItem keyInfoItem = {siBuffer, (unsigned char*)&kODoHKey[0],
|
||||||
|
static_cast<unsigned int>(strlen(kODoHKey))};
|
||||||
|
UniquePK11SymKey key;
|
||||||
|
rv = HKDFExpand(odohPrk.get(), &keyInfoItem, kAes128GcmKeyLen, true, key);
|
||||||
|
if (rv != SECSuccess) {
|
||||||
|
LOG(("ODoHDNSPacket::DecryptDNSResponse expand key failed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECItem nonceInfoItem = {siBuffer, (unsigned char*)&kODoHNonce[0],
|
||||||
|
static_cast<unsigned int>(strlen(kODoHNonce))};
|
||||||
|
UniquePK11SymKey nonce;
|
||||||
|
rv = HKDFExpand(odohPrk.get(), &nonceInfoItem, kAes128GcmNonceLen, false,
|
||||||
|
nonce);
|
||||||
|
if (rv != SECSuccess) {
|
||||||
|
LOG(("ODoHDNSPacket::DecryptDNSResponse expand nonce failed"));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = PK11_ExtractKeyValue(nonce.get());
|
||||||
|
if (rv != SECSuccess) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SECItem* derivedItem = PK11_GetKeyData(nonce.get());
|
||||||
|
if (!derivedItem) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// aad = 0x02 || len(response_nonce) || response_nonce
|
||||||
|
SECItem* aadItem(
|
||||||
|
::SECITEM_AllocItem(nullptr, nullptr, 1 + 2 + kResponseNonceLen));
|
||||||
|
aadItem->data[0] = ODOH_RESPONSE;
|
||||||
|
NetworkEndian::writeUint16(&aadItem->data[1], kResponseNonceLen);
|
||||||
|
memcpy(&aadItem->data[3], message.mKeyId.Elements(), kResponseNonceLen);
|
||||||
|
UniqueSECItem aad(aadItem);
|
||||||
|
|
||||||
|
SECItem paramItem;
|
||||||
|
CK_GCM_PARAMS param;
|
||||||
|
param.pIv = derivedItem->data;
|
||||||
|
param.ulIvLen = derivedItem->len;
|
||||||
|
param.ulIvBits = param.ulIvLen * 8;
|
||||||
|
param.ulTagBits = 16 * 8;
|
||||||
|
param.pAAD = (CK_BYTE_PTR)aad->data;
|
||||||
|
param.ulAADLen = aad->len;
|
||||||
|
|
||||||
|
paramItem.type = siBuffer;
|
||||||
|
paramItem.data = (unsigned char*)(¶m);
|
||||||
|
paramItem.len = sizeof(CK_GCM_PARAMS);
|
||||||
|
|
||||||
|
memset(mResponse, 0, mBodySize);
|
||||||
|
rv = PK11_Decrypt(key.get(), CKM_AES_GCM, ¶mItem, mResponse, &mBodySize,
|
||||||
|
MAX_SIZE, message.mEncryptedMessage.Elements(),
|
||||||
|
message.mEncryptedMessage.Length());
|
||||||
|
if (rv != SECSuccess) {
|
||||||
|
LOG(("ODoHDNSPacket::DecryptDNSResponse decrypt failed %d",
|
||||||
|
PORT_GetError()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace net
|
} // namespace net
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,39 @@ class DNSPacket {
|
||||||
Maybe<nsCString> mOriginHost;
|
Maybe<nsCString> mOriginHost;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ODoHDNSPacket final : public DNSPacket {
|
||||||
|
public:
|
||||||
|
ODoHDNSPacket() = default;
|
||||||
|
virtual ~ODoHDNSPacket();
|
||||||
|
|
||||||
|
static bool ParseODoHConfigs(Span<const uint8_t> aData,
|
||||||
|
nsTArray<ObliviousDoHConfig>& aOut);
|
||||||
|
|
||||||
|
virtual nsresult EncodeRequest(nsCString& aBody, const nsACString& aHost,
|
||||||
|
uint16_t aType, bool aDisableECS) override;
|
||||||
|
|
||||||
|
virtual nsresult Decode(
|
||||||
|
nsCString& aHost, enum TrrType aType, nsCString& aCname,
|
||||||
|
bool aAllowRFC1918, DOHresp& aResp, TypeRecordResultType& aTypeResult,
|
||||||
|
nsClassHashtable<nsCStringHashKey, DOHresp>& aAdditionalRecords,
|
||||||
|
uint32_t& aTTL) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool EncryptDNSQuery(const nsACString& aQuery, uint16_t aPaddingLen,
|
||||||
|
const ObliviousDoHConfig& aConfig,
|
||||||
|
ObliviousDoHMessage& aOut);
|
||||||
|
bool DecryptDNSResponse();
|
||||||
|
|
||||||
|
HpkeContext* mContext = nullptr;
|
||||||
|
UniqueSECItem mPlainQuery;
|
||||||
|
// This struct indicates the range of decrypted responses stored in mResponse.
|
||||||
|
struct DecryptedResponseRange {
|
||||||
|
uint16_t mStart = 0;
|
||||||
|
uint16_t mLength = 0;
|
||||||
|
};
|
||||||
|
Maybe<DecryptedResponseRange> mDecryptedResponseRange;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace net
|
} // namespace net
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,13 @@ namespace mozilla {
|
||||||
namespace net {
|
namespace net {
|
||||||
|
|
||||||
class NetworkConnectivityService;
|
class NetworkConnectivityService;
|
||||||
|
class ODoHService;
|
||||||
class TRR;
|
class TRR;
|
||||||
|
|
||||||
class DNSUtils final {
|
class DNSUtils final {
|
||||||
private:
|
private:
|
||||||
friend class NetworkConnectivityService;
|
friend class NetworkConnectivityService;
|
||||||
|
friend class ODoHService;
|
||||||
friend class ObliviousHttpService;
|
friend class ObliviousHttpService;
|
||||||
friend class TRR;
|
friend class TRR;
|
||||||
static nsresult CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult);
|
static nsresult CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult);
|
||||||
|
|
|
||||||
112
netwerk/dns/ODoH.cpp
Normal file
112
netwerk/dns/ODoH.cpp
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||||
|
/* 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 "ODoH.h"
|
||||||
|
|
||||||
|
#include "mozilla/Base64.h"
|
||||||
|
#include "nsIURIMutator.h"
|
||||||
|
#include "ODoHService.h"
|
||||||
|
#include "TRRService.h"
|
||||||
|
// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
|
||||||
|
#include "DNSLogging.h"
|
||||||
|
#include "nsNetUtil.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace net {
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ODoH::Run() {
|
||||||
|
if (!gODoHService) {
|
||||||
|
RecordReason(TRRSkippedReason::TRR_SEND_FAILED);
|
||||||
|
FailData(NS_ERROR_FAILURE);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!gODoHService->ODoHConfigs()) {
|
||||||
|
LOG((
|
||||||
|
"ODoH::Run ODoHConfigs is not available mTriedDownloadODoHConfigs=%d\n",
|
||||||
|
mTriedDownloadODoHConfigs));
|
||||||
|
// Make this lookup fail if we don't have a valid ODoHConfig and we already
|
||||||
|
// tried before.
|
||||||
|
if (NS_SUCCEEDED(gODoHService->UpdateODoHConfig()) &&
|
||||||
|
!mTriedDownloadODoHConfigs) {
|
||||||
|
gODoHService->AppendPendingODoHRequest(this);
|
||||||
|
mTriedDownloadODoHConfigs = true;
|
||||||
|
} else {
|
||||||
|
RecordReason(TRRSkippedReason::ODOH_UPDATE_KEY_FAILED);
|
||||||
|
FailData(NS_ERROR_FAILURE);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRR::Run();
|
||||||
|
}
|
||||||
|
|
||||||
|
DNSPacket* ODoH::GetOrCreateDNSPacket() {
|
||||||
|
if (!mPacket) {
|
||||||
|
mPacket = MakeUnique<ODoHDNSPacket>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return mPacket.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult ODoH::CreateQueryURI(nsIURI** aOutURI) {
|
||||||
|
nsAutoCString uri;
|
||||||
|
nsCOMPtr<nsIURI> dnsURI;
|
||||||
|
gODoHService->GetRequestURI(uri);
|
||||||
|
|
||||||
|
nsresult rv = NS_NewURI(getter_AddRefs(dnsURI), uri);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
dnsURI.forget(aOutURI);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODoH::HandleTimeout() {
|
||||||
|
// If this request is still in the pending queue, it means we can't get the
|
||||||
|
// ODoHConfigs within the timeout.
|
||||||
|
if (gODoHService->RemovePendingODoHRequest(this)) {
|
||||||
|
RecordReason(TRRSkippedReason::ODOH_KEY_NOT_AVAILABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRR::HandleTimeout();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODoH::HandleEncodeError(nsresult aStatusCode) {
|
||||||
|
MOZ_ASSERT(NS_FAILED(aStatusCode));
|
||||||
|
|
||||||
|
DNSPacketStatus status = mPacket->PacketStatus();
|
||||||
|
MOZ_ASSERT(status != DNSPacketStatus::Success);
|
||||||
|
|
||||||
|
if (status == DNSPacketStatus::KeyNotAvailable) {
|
||||||
|
RecordReason(TRRSkippedReason::ODOH_KEY_NOT_AVAILABLE);
|
||||||
|
} else if (status == DNSPacketStatus::KeyNotUsable) {
|
||||||
|
RecordReason(TRRSkippedReason::ODOH_KEY_NOT_USABLE);
|
||||||
|
} else if (status == DNSPacketStatus::EncryptError) {
|
||||||
|
RecordReason(TRRSkippedReason::ODOH_ENCRYPTION_FAILED);
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT_UNREACHABLE("Unexpected status code.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODoH::HandleDecodeError(nsresult aStatusCode) {
|
||||||
|
MOZ_ASSERT(NS_FAILED(aStatusCode));
|
||||||
|
|
||||||
|
DNSPacketStatus status = mPacket->PacketStatus();
|
||||||
|
MOZ_ASSERT(status != DNSPacketStatus::Success);
|
||||||
|
|
||||||
|
if (status == DNSPacketStatus::DecryptError) {
|
||||||
|
RecordReason(TRRSkippedReason::ODOH_DECRYPTION_FAILED);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRR::HandleDecodeError(aStatusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace net
|
||||||
|
} // namespace mozilla
|
||||||
60
netwerk/dns/ODoH.h
Normal file
60
netwerk/dns/ODoH.h
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set sw=2 ts=8 et tw=80 : */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_net_ODoH_h
|
||||||
|
#define mozilla_net_ODoH_h
|
||||||
|
|
||||||
|
#include "TRR.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace net {
|
||||||
|
|
||||||
|
class ODoH final : public TRR {
|
||||||
|
public:
|
||||||
|
explicit ODoH(AHostResolver* aResolver, nsHostRecord* aRec,
|
||||||
|
enum TrrType aType)
|
||||||
|
: TRR(aResolver, aRec, aType) {}
|
||||||
|
// when following CNAMEs
|
||||||
|
explicit ODoH(AHostResolver* aResolver, nsHostRecord* aRec, nsCString& aHost,
|
||||||
|
enum TrrType& aType, unsigned int aLoopCount, bool aPB)
|
||||||
|
: TRR(aResolver, aRec, aHost, aType, aLoopCount, aPB) {}
|
||||||
|
NS_IMETHOD Run() override;
|
||||||
|
// ODoH should not support push.
|
||||||
|
NS_IMETHOD GetInterface(const nsIID&, void**) override {
|
||||||
|
return NS_ERROR_NO_INTERFACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~ODoH() = default;
|
||||||
|
virtual DNSPacket* GetOrCreateDNSPacket() override;
|
||||||
|
virtual nsresult CreateQueryURI(nsIURI** aOutURI) override;
|
||||||
|
virtual const char* ContentType() const override {
|
||||||
|
return "application/oblivious-dns-message";
|
||||||
|
}
|
||||||
|
virtual DNSResolverType ResolverType() const override {
|
||||||
|
return DNSResolverType::ODoH;
|
||||||
|
}
|
||||||
|
virtual bool MaybeBlockRequest() override {
|
||||||
|
// TODO: check excluded list
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
virtual void RecordProcessingTime(nsIChannel* aChannel) override {
|
||||||
|
// TODO: record processing time for ODoH.
|
||||||
|
}
|
||||||
|
virtual void ReportStatus(nsresult aStatusCode) override {
|
||||||
|
// TODO: record status in ODoHService.
|
||||||
|
}
|
||||||
|
virtual void HandleTimeout() override;
|
||||||
|
virtual void HandleEncodeError(nsresult aStatusCode) override;
|
||||||
|
virtual void HandleDecodeError(nsresult aStatusCode) override;
|
||||||
|
|
||||||
|
bool mTriedDownloadODoHConfigs = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace net
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif
|
||||||
521
netwerk/dns/ODoHService.cpp
Normal file
521
netwerk/dns/ODoHService.cpp
Normal file
|
|
@ -0,0 +1,521 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* 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 "ODoHService.h"
|
||||||
|
|
||||||
|
#include "DNSUtils.h"
|
||||||
|
#include "mozilla/net/SocketProcessChild.h"
|
||||||
|
#include "mozilla/Preferences.h"
|
||||||
|
#include "mozilla/ScopeExit.h"
|
||||||
|
#include "mozilla/StaticPrefs_network.h"
|
||||||
|
#include "nsICancelable.h"
|
||||||
|
#include "nsIDNSAdditionalInfo.h"
|
||||||
|
#include "nsIDNSService.h"
|
||||||
|
#include "nsIDNSByTypeRecord.h"
|
||||||
|
#include "nsIOService.h"
|
||||||
|
#include "nsIObserverService.h"
|
||||||
|
#include "nsNetUtil.h"
|
||||||
|
#include "ODoH.h"
|
||||||
|
#include "TRRService.h"
|
||||||
|
#include "nsURLHelper.h"
|
||||||
|
// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
|
||||||
|
#include "DNSLogging.h"
|
||||||
|
|
||||||
|
static const char kODoHProxyURIPref[] = "network.trr.odoh.proxy_uri";
|
||||||
|
static const char kODoHTargetHostPref[] = "network.trr.odoh.target_host";
|
||||||
|
static const char kODoHTargetPathPref[] = "network.trr.odoh.target_path";
|
||||||
|
static const char kODoHConfigsUriPref[] = "network.trr.odoh.configs_uri";
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace net {
|
||||||
|
|
||||||
|
ODoHService* gODoHService = nullptr;
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS(ODoHService, nsIDNSListener, nsIObserver,
|
||||||
|
nsISupportsWeakReference, nsITimerCallback, nsINamed,
|
||||||
|
nsIStreamLoaderObserver)
|
||||||
|
|
||||||
|
ODoHService::ODoHService()
|
||||||
|
: mLock("net::ODoHService"), mQueryODoHConfigInProgress(false) {
|
||||||
|
gODoHService = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ODoHService::~ODoHService() { gODoHService = nullptr; }
|
||||||
|
|
||||||
|
bool ODoHService::Init() {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
|
||||||
|
|
||||||
|
nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
|
||||||
|
if (!prefBranch) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
prefBranch->AddObserver(kODoHProxyURIPref, this, true);
|
||||||
|
prefBranch->AddObserver(kODoHTargetHostPref, this, true);
|
||||||
|
prefBranch->AddObserver(kODoHTargetPathPref, this, true);
|
||||||
|
prefBranch->AddObserver(kODoHConfigsUriPref, this, true);
|
||||||
|
|
||||||
|
ReadPrefs(nullptr);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIObserverService> observerService =
|
||||||
|
mozilla::services::GetObserverService();
|
||||||
|
if (observerService) {
|
||||||
|
observerService->AddObserver(this, "xpcom-shutdown-threads", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ODoHService::Enabled() const {
|
||||||
|
return StaticPrefs::network_trr_odoh_enabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ODoHService::Observe(nsISupports* aSubject, const char* aTopic,
|
||||||
|
const char16_t* aData) {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread(), "wrong thread");
|
||||||
|
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
||||||
|
ReadPrefs(NS_ConvertUTF16toUTF8(aData).get());
|
||||||
|
} else if (!strcmp(aTopic, "xpcom-shutdown-threads")) {
|
||||||
|
if (mTTLTimer) {
|
||||||
|
mTTLTimer->Cancel();
|
||||||
|
mTTLTimer = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult ODoHService::ReadPrefs(const char* aName) {
|
||||||
|
if (!aName || !strcmp(aName, kODoHConfigsUriPref)) {
|
||||||
|
OnODohConfigsURIChanged();
|
||||||
|
}
|
||||||
|
if (!aName || !strcmp(aName, kODoHProxyURIPref) ||
|
||||||
|
!strcmp(aName, kODoHTargetHostPref) ||
|
||||||
|
!strcmp(aName, kODoHTargetPathPref)) {
|
||||||
|
OnODoHPrefsChange(aName == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODoHService::OnODohConfigsURIChanged() {
|
||||||
|
nsAutoCString uri;
|
||||||
|
Preferences::GetCString(kODoHConfigsUriPref, uri);
|
||||||
|
|
||||||
|
bool updateConfig = false;
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
if (!mODoHConfigsUri.Equals(uri)) {
|
||||||
|
mODoHConfigsUri = uri;
|
||||||
|
updateConfig = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateConfig) {
|
||||||
|
UpdateODoHConfigFromURI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODoHService::OnODoHPrefsChange(bool aInit) {
|
||||||
|
nsAutoCString proxyURI;
|
||||||
|
Preferences::GetCString(kODoHProxyURIPref, proxyURI);
|
||||||
|
nsAutoCString targetHost;
|
||||||
|
Preferences::GetCString(kODoHTargetHostPref, targetHost);
|
||||||
|
nsAutoCString targetPath;
|
||||||
|
Preferences::GetCString(kODoHTargetPathPref, targetPath);
|
||||||
|
|
||||||
|
bool updateODoHConfig = false;
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
mODoHProxyURI = proxyURI;
|
||||||
|
// Only update ODoHConfig when the host is really changed.
|
||||||
|
if (!mODoHTargetHost.Equals(targetHost) && mODoHConfigsUri.IsEmpty()) {
|
||||||
|
updateODoHConfig = true;
|
||||||
|
}
|
||||||
|
mODoHTargetHost = targetHost;
|
||||||
|
mODoHTargetPath = targetPath;
|
||||||
|
|
||||||
|
BuildODoHRequestURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateODoHConfig) {
|
||||||
|
// When this function is called from ODoHService::Init(), it's on the same
|
||||||
|
// call stack as nsDNSService is inited. In this case, we need to dispatch
|
||||||
|
// UpdateODoHConfigFromHTTPSRR(), since recursively getting DNS service is
|
||||||
|
// not allowed.
|
||||||
|
auto task = []() { gODoHService->UpdateODoHConfigFromHTTPSRR(); };
|
||||||
|
if (aInit) {
|
||||||
|
NS_DispatchToMainThread(NS_NewRunnableFunction(
|
||||||
|
"ODoHService::UpdateODoHConfigFromHTTPSRR", std::move(task)));
|
||||||
|
} else {
|
||||||
|
task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static nsresult ExtractHostAndPort(const nsACString& aURI, nsCString& aResult,
|
||||||
|
int32_t& aOutPort) {
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!uri->SchemeIs("https")) {
|
||||||
|
LOG(("ODoHService host uri is not https"));
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = uri->GetPort(&aOutPort);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uri->GetAsciiHost(aResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODoHService::BuildODoHRequestURI() {
|
||||||
|
mLock.AssertCurrentThreadOwns();
|
||||||
|
|
||||||
|
mODoHRequestURI.Truncate();
|
||||||
|
if (mODoHTargetHost.IsEmpty() || mODoHTargetPath.IsEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mODoHProxyURI.IsEmpty()) {
|
||||||
|
mODoHRequestURI.Append(mODoHTargetHost);
|
||||||
|
mODoHRequestURI.AppendLiteral("/");
|
||||||
|
mODoHRequestURI.Append(mODoHTargetPath);
|
||||||
|
} else {
|
||||||
|
nsAutoCString hostStr;
|
||||||
|
int32_t port = -1;
|
||||||
|
if (NS_FAILED(ExtractHostAndPort(mODoHTargetHost, hostStr, port))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mODoHRequestURI.Append(mODoHProxyURI);
|
||||||
|
mODoHRequestURI.AppendLiteral("?targethost=");
|
||||||
|
mODoHRequestURI.Append(hostStr);
|
||||||
|
mODoHRequestURI.AppendLiteral("&targetpath=/");
|
||||||
|
mODoHRequestURI.Append(mODoHTargetPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODoHService::GetRequestURI(nsACString& aResult) {
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
aResult = mODoHRequestURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult ODoHService::UpdateODoHConfig() {
|
||||||
|
LOG(("ODoHService::UpdateODoHConfig"));
|
||||||
|
if (mQueryODoHConfigInProgress) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_SUCCEEDED(UpdateODoHConfigFromURI())) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UpdateODoHConfigFromHTTPSRR();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult ODoHService::UpdateODoHConfigFromURI() {
|
||||||
|
LOG(("ODoHService::UpdateODoHConfigFromURI"));
|
||||||
|
|
||||||
|
nsAutoCString configUri;
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
configUri = mODoHConfigsUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (configUri.IsEmpty() || !StringBeginsWith(configUri, "https://"_ns)) {
|
||||||
|
LOG(("ODoHService::UpdateODoHConfigFromURI: uri is invalid"));
|
||||||
|
return UpdateODoHConfigFromHTTPSRR();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIEventTarget> target = TRRService::Get()->MainThreadOrTRRThread();
|
||||||
|
if (!target) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target->IsOnCurrentThread()) {
|
||||||
|
nsresult rv = target->Dispatch(NS_NewRunnableFunction(
|
||||||
|
"ODoHService::UpdateODoHConfigFromURI",
|
||||||
|
[]() { gODoHService->UpdateODoHConfigFromURI(); }));
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
// Set mQueryODoHConfigInProgress to true to avoid updating ODoHConfigs
|
||||||
|
// when waiting the runnable to be executed.
|
||||||
|
mQueryODoHConfigInProgress = true;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// In case any error happens, we should reset mQueryODoHConfigInProgress.
|
||||||
|
auto guard = MakeScopeExit([&]() { mQueryODoHConfigInProgress = false; });
|
||||||
|
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
nsresult rv = NS_NewURI(getter_AddRefs(uri), configUri);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIChannel> channel;
|
||||||
|
rv = DNSUtils::CreateChannelHelper(uri, getter_AddRefs(channel));
|
||||||
|
if (NS_FAILED(rv) || !channel) {
|
||||||
|
LOG(("NewChannel failed!"));
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = channel->SetLoadFlags(
|
||||||
|
nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
|
||||||
|
nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
|
||||||
|
if (!httpChannel) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This connection should not use TRR
|
||||||
|
rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
|
||||||
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIStreamLoader> loader;
|
||||||
|
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = httpChannel->AsyncOpen(loader);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// AsyncOpen succeeded, dismiss the guard.
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
guard.release();
|
||||||
|
mLoader.swap(loader);
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult ODoHService::UpdateODoHConfigFromHTTPSRR() {
|
||||||
|
LOG(("ODoHService::UpdateODoHConfigFromHTTPSRR"));
|
||||||
|
|
||||||
|
nsAutoCString uri;
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
uri = mODoHTargetHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDNSService> dns(
|
||||||
|
do_GetService("@mozilla.org/network/dns-service;1"));
|
||||||
|
if (!dns) {
|
||||||
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TRRService::Get()) {
|
||||||
|
return NS_ERROR_NOT_AVAILABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsAutoCString hostStr;
|
||||||
|
int32_t port = -1;
|
||||||
|
nsresult rv = ExtractHostAndPort(uri, hostStr, port);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsICancelable> tmpOutstanding;
|
||||||
|
nsCOMPtr<nsIEventTarget> target = TRRService::Get()->MainThreadOrTRRThread();
|
||||||
|
// We'd like to bypass the DNS cache, since ODoHConfigs will be updated
|
||||||
|
// manually by ODoHService.
|
||||||
|
nsIDNSService::DNSFlags flags =
|
||||||
|
nsIDNSService::RESOLVE_DISABLE_ODOH | nsIDNSService::RESOLVE_BYPASS_CACHE;
|
||||||
|
nsCOMPtr<nsIDNSAdditionalInfo> info;
|
||||||
|
if (port != -1) {
|
||||||
|
Unused << dns->NewAdditionalInfo(""_ns, port, getter_AddRefs(info));
|
||||||
|
}
|
||||||
|
rv = dns->AsyncResolveNative(hostStr, nsIDNSService::RESOLVE_TYPE_HTTPSSVC,
|
||||||
|
flags, info, this, target, OriginAttributes(),
|
||||||
|
getter_AddRefs(tmpOutstanding));
|
||||||
|
LOG(("ODoHService::UpdateODoHConfig [host=%s rv=%" PRIx32 "]", hostStr.get(),
|
||||||
|
static_cast<uint32_t>(rv)));
|
||||||
|
|
||||||
|
if (NS_SUCCEEDED(rv)) {
|
||||||
|
mQueryODoHConfigInProgress = true;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODoHService::StartTTLTimer(uint32_t aTTL) {
|
||||||
|
if (mTTLTimer) {
|
||||||
|
mTTLTimer->Cancel();
|
||||||
|
mTTLTimer = nullptr;
|
||||||
|
}
|
||||||
|
LOG(("ODoHService::StartTTLTimer ttl=%d(s)", aTTL));
|
||||||
|
NS_NewTimerWithCallback(getter_AddRefs(mTTLTimer), this, aTTL * 1000,
|
||||||
|
nsITimer::TYPE_ONE_SHOT);
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ODoHService::Notify(nsITimer* aTimer) {
|
||||||
|
MOZ_ASSERT(aTimer == mTTLTimer);
|
||||||
|
UpdateODoHConfig();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ODoHService::GetName(nsACString& aName) {
|
||||||
|
aName.AssignLiteral("ODoHService");
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODoHService::ODoHConfigUpdateDone(uint32_t aTTL,
|
||||||
|
Span<const uint8_t> aRawConfig) {
|
||||||
|
MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
|
||||||
|
NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
|
||||||
|
MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
|
||||||
|
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
mQueryODoHConfigInProgress = false;
|
||||||
|
mODoHConfigs.reset();
|
||||||
|
|
||||||
|
nsTArray<ObliviousDoHConfig> configs;
|
||||||
|
if (ODoHDNSPacket::ParseODoHConfigs(aRawConfig, configs)) {
|
||||||
|
mODoHConfigs.emplace(std::move(configs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let observers know whether ODoHService is activated or not.
|
||||||
|
bool hasODoHConfigs = mODoHConfigs && !mODoHConfigs->IsEmpty();
|
||||||
|
if (aTTL < StaticPrefs::network_trr_odoh_min_ttl()) {
|
||||||
|
aTTL = StaticPrefs::network_trr_odoh_min_ttl();
|
||||||
|
}
|
||||||
|
auto task = [hasODoHConfigs, aTTL]() {
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
if (XRE_IsSocketProcess()) {
|
||||||
|
SocketProcessChild::GetSingleton()->SendODoHServiceActivated(
|
||||||
|
hasODoHConfigs);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIObserverService> observerService =
|
||||||
|
mozilla::services::GetObserverService();
|
||||||
|
|
||||||
|
if (observerService) {
|
||||||
|
observerService->NotifyObservers(nullptr, "odoh-service-activated",
|
||||||
|
hasODoHConfigs ? u"true" : u"false");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aTTL) {
|
||||||
|
gODoHService->StartTTLTimer(aTTL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (NS_IsMainThread()) {
|
||||||
|
task();
|
||||||
|
} else {
|
||||||
|
NS_DispatchToMainThread(
|
||||||
|
NS_NewRunnableFunction("ODoHService::Activated", std::move(task)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mPendingRequests.IsEmpty()) {
|
||||||
|
nsTArray<RefPtr<ODoH>> requests = std::move(mPendingRequests);
|
||||||
|
nsCOMPtr<nsIEventTarget> target =
|
||||||
|
TRRService::Get()->MainThreadOrTRRThread();
|
||||||
|
for (auto& query : requests) {
|
||||||
|
target->Dispatch(query.forget());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ODoHService::OnLookupComplete(nsICancelable* aRequest, nsIDNSRecord* aRec,
|
||||||
|
nsresult aStatus) {
|
||||||
|
MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
|
||||||
|
NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
|
||||||
|
MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDNSHTTPSSVCRecord> httpsRecord;
|
||||||
|
nsCString rawODoHConfig;
|
||||||
|
auto notifyDone = MakeScopeExit([&]() {
|
||||||
|
uint32_t ttl = 0;
|
||||||
|
if (httpsRecord) {
|
||||||
|
Unused << httpsRecord->GetTtl(&ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
ODoHConfigUpdateDone(
|
||||||
|
ttl,
|
||||||
|
Span(reinterpret_cast<const uint8_t*>(rawODoHConfig.BeginReading()),
|
||||||
|
rawODoHConfig.Length()));
|
||||||
|
});
|
||||||
|
|
||||||
|
LOG(("ODoHService::OnLookupComplete [aStatus=%" PRIx32 "]",
|
||||||
|
static_cast<uint32_t>(aStatus)));
|
||||||
|
if (NS_FAILED(aStatus)) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
httpsRecord = do_QueryInterface(aRec);
|
||||||
|
if (!httpsRecord) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsTArray<RefPtr<nsISVCBRecord>> svcbRecords;
|
||||||
|
httpsRecord->GetRecords(svcbRecords);
|
||||||
|
for (const auto& record : svcbRecords) {
|
||||||
|
Unused << record->GetODoHConfig(rawODoHConfig);
|
||||||
|
if (!rawODoHConfig.IsEmpty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
ODoHService::OnStreamComplete(nsIStreamLoader* aLoader, nsISupports* aContext,
|
||||||
|
nsresult aStatus, uint32_t aLength,
|
||||||
|
const uint8_t* aContent) {
|
||||||
|
MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
|
||||||
|
NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
|
||||||
|
MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
|
||||||
|
LOG(("ODoHService::OnStreamComplete aLength=%d\n", aLength));
|
||||||
|
|
||||||
|
{
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
mLoader = nullptr;
|
||||||
|
}
|
||||||
|
ODoHConfigUpdateDone(0, Span(aContent, aLength));
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Maybe<nsTArray<ObliviousDoHConfig>>& ODoHService::ODoHConfigs() {
|
||||||
|
MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
|
||||||
|
NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
|
||||||
|
MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
|
||||||
|
|
||||||
|
return mODoHConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ODoHService::AppendPendingODoHRequest(ODoH* aRequest) {
|
||||||
|
LOG(("ODoHService::AppendPendingODoHQuery\n"));
|
||||||
|
MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
|
||||||
|
NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
|
||||||
|
MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
|
||||||
|
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
mPendingRequests.AppendElement(aRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ODoHService::RemovePendingODoHRequest(ODoH* aRequest) {
|
||||||
|
MOZ_ASSERT_IF(XRE_IsParentProcess() && TRRService::Get(),
|
||||||
|
NS_IsMainThread() || TRRService::Get()->IsOnTRRThread());
|
||||||
|
MOZ_ASSERT_IF(XRE_IsSocketProcess(), NS_IsMainThread());
|
||||||
|
|
||||||
|
MutexAutoLock lock(mLock);
|
||||||
|
return mPendingRequests.RemoveElement(aRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace net
|
||||||
|
} // namespace mozilla
|
||||||
80
netwerk/dns/ODoHService.h
Normal file
80
netwerk/dns/ODoHService.h
Normal file
|
|
@ -0,0 +1,80 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef ODoHService_h_
|
||||||
|
#define ODoHService_h_
|
||||||
|
|
||||||
|
#include "DNS.h"
|
||||||
|
#include "mozilla/Atomics.h"
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
|
#include "mozilla/Mutex.h"
|
||||||
|
#include "nsString.h"
|
||||||
|
#include "nsIDNSListener.h"
|
||||||
|
#include "nsIObserver.h"
|
||||||
|
#include "nsIStreamLoader.h"
|
||||||
|
#include "nsITimer.h"
|
||||||
|
#include "nsWeakReference.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace net {
|
||||||
|
|
||||||
|
class ODoH;
|
||||||
|
|
||||||
|
class ODoHService : public nsIDNSListener,
|
||||||
|
public nsIObserver,
|
||||||
|
public nsSupportsWeakReference,
|
||||||
|
public nsITimerCallback,
|
||||||
|
public nsINamed,
|
||||||
|
public nsIStreamLoaderObserver {
|
||||||
|
public:
|
||||||
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
|
NS_DECL_NSIDNSLISTENER
|
||||||
|
NS_DECL_NSIOBSERVER
|
||||||
|
NS_DECL_NSITIMERCALLBACK
|
||||||
|
NS_DECL_NSINAMED
|
||||||
|
NS_DECL_NSISTREAMLOADEROBSERVER
|
||||||
|
|
||||||
|
ODoHService();
|
||||||
|
bool Init();
|
||||||
|
bool Enabled() const;
|
||||||
|
|
||||||
|
const Maybe<nsTArray<ObliviousDoHConfig>>& ODoHConfigs();
|
||||||
|
void AppendPendingODoHRequest(ODoH* aRequest);
|
||||||
|
bool RemovePendingODoHRequest(ODoH* aRequest);
|
||||||
|
void GetRequestURI(nsACString& aResult);
|
||||||
|
// Send a DNS query to reterive the ODoHConfig.
|
||||||
|
nsresult UpdateODoHConfig();
|
||||||
|
|
||||||
|
private:
|
||||||
|
virtual ~ODoHService();
|
||||||
|
nsresult ReadPrefs(const char* aName);
|
||||||
|
void OnODoHPrefsChange(bool aInit);
|
||||||
|
void BuildODoHRequestURI();
|
||||||
|
void StartTTLTimer(uint32_t aTTL);
|
||||||
|
void OnODohConfigsURIChanged();
|
||||||
|
void ODoHConfigUpdateDone(uint32_t aTTL, Span<const uint8_t> aRawConfig);
|
||||||
|
nsresult UpdateODoHConfigFromHTTPSRR();
|
||||||
|
nsresult UpdateODoHConfigFromURI();
|
||||||
|
|
||||||
|
mozilla::Mutex mLock;
|
||||||
|
Atomic<bool, Relaxed> mQueryODoHConfigInProgress;
|
||||||
|
nsCString mODoHProxyURI MOZ_GUARDED_BY(mLock);
|
||||||
|
nsCString mODoHTargetHost MOZ_GUARDED_BY(mLock);
|
||||||
|
nsCString mODoHTargetPath MOZ_GUARDED_BY(mLock);
|
||||||
|
nsCString mODoHRequestURI MOZ_GUARDED_BY(mLock);
|
||||||
|
nsCString mODoHConfigsUri MOZ_GUARDED_BY(mLock);
|
||||||
|
Maybe<nsTArray<ObliviousDoHConfig>> mODoHConfigs MOZ_GUARDED_BY(mLock);
|
||||||
|
nsTArray<RefPtr<ODoH>> mPendingRequests MOZ_GUARDED_BY(mLock);
|
||||||
|
// This timer is always touched on main thread to avoid race conditions.
|
||||||
|
nsCOMPtr<nsITimer> mTTLTimer;
|
||||||
|
nsCOMPtr<nsIStreamLoader> mLoader MOZ_GUARDED_BY(mLock);
|
||||||
|
};
|
||||||
|
|
||||||
|
extern ODoHService* gODoHService;
|
||||||
|
|
||||||
|
} // namespace net
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include "nsStringStream.h"
|
#include "nsStringStream.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
#include "nsURLHelper.h"
|
#include "nsURLHelper.h"
|
||||||
|
#include "ODoH.h"
|
||||||
#include "ObliviousHttpChannel.h"
|
#include "ObliviousHttpChannel.h"
|
||||||
#include "TRR.h"
|
#include "TRR.h"
|
||||||
#include "TRRService.h"
|
#include "TRRService.h"
|
||||||
|
|
@ -880,7 +881,9 @@ nsresult TRR::FollowCname(nsIChannel* aChannel) {
|
||||||
LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
|
LOG(("TRR::On200Response CNAME %s => %s (%u)\n", mHost.get(), mCname.get(),
|
||||||
mCnameLoop));
|
mCnameLoop));
|
||||||
RefPtr<TRR> trr =
|
RefPtr<TRR> trr =
|
||||||
new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB);
|
ResolverType() == DNSResolverType::ODoH
|
||||||
|
? new ODoH(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB)
|
||||||
|
: new TRR(mHostResolver, mRec, mCname, mType, mCnameLoop, mPB);
|
||||||
if (!TRRService::Get()) {
|
if (!TRRService::Get()) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "nsQueryObject.h"
|
#include "nsQueryObject.h"
|
||||||
#include "TRR.h"
|
#include "TRR.h"
|
||||||
#include "TRRService.h"
|
#include "TRRService.h"
|
||||||
|
#include "ODoH.h"
|
||||||
// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
|
// Put DNSLogging.h at the end to avoid LOG being overwritten by other headers.
|
||||||
#include "DNSLogging.h"
|
#include "DNSLogging.h"
|
||||||
|
|
||||||
|
|
@ -64,10 +65,15 @@ void TRRQuery::MarkSendingTRR(TRR* trr, enum TrrType rectype, MutexAutoLock&) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TRRQuery::PrepareQuery(enum TrrType aRecType,
|
void TRRQuery::PrepareQuery(bool aUseODoH, enum TrrType aRecType,
|
||||||
nsTArray<RefPtr<TRR>>& aRequestsToSend) {
|
nsTArray<RefPtr<TRR>>& aRequestsToSend) {
|
||||||
LOG(("TRR Resolve %s type %d\n", mRecord->host.get(), (int)aRecType));
|
LOG(("TRR Resolve %s type %d\n", mRecord->host.get(), (int)aRecType));
|
||||||
RefPtr<TRR> trr = new TRR(this, mRecord, aRecType);
|
RefPtr<TRR> trr;
|
||||||
|
if (aUseODoH) {
|
||||||
|
trr = new ODoH(this, mRecord, aRecType);
|
||||||
|
} else {
|
||||||
|
trr = new TRR(this, mRecord, aRecType);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
MutexAutoLock trrlock(mTrrLock);
|
MutexAutoLock trrlock(mTrrLock);
|
||||||
|
|
@ -99,9 +105,14 @@ bool TRRQuery::SendQueries(nsTArray<RefPtr<TRR>>& aRequestsToSend) {
|
||||||
return madeQuery;
|
return madeQuery;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult TRRQuery::DispatchLookup(TRR* pushedTRR) {
|
nsresult TRRQuery::DispatchLookup(TRR* pushedTRR, bool aUseODoH) {
|
||||||
|
if (aUseODoH && pushedTRR) {
|
||||||
|
MOZ_ASSERT(false, "ODoH should not support push");
|
||||||
|
return NS_ERROR_UNKNOWN_HOST;
|
||||||
|
}
|
||||||
|
|
||||||
if (!mRecord->IsAddrRecord()) {
|
if (!mRecord->IsAddrRecord()) {
|
||||||
return DispatchByTypeLookup(pushedTRR);
|
return DispatchByTypeLookup(pushedTRR, aUseODoH);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRecord);
|
RefPtr<AddrHostRecord> addrRec = do_QueryObject(mRecord);
|
||||||
|
|
@ -131,20 +142,21 @@ nsresult TRRQuery::DispatchLookup(TRR* pushedTRR) {
|
||||||
// same time.
|
// same time.
|
||||||
nsTArray<RefPtr<TRR>> requestsToSend;
|
nsTArray<RefPtr<TRR>> requestsToSend;
|
||||||
if ((mRecord->af == AF_UNSPEC || mRecord->af == AF_INET6)) {
|
if ((mRecord->af == AF_UNSPEC || mRecord->af == AF_INET6)) {
|
||||||
PrepareQuery(TRRTYPE_AAAA, requestsToSend);
|
PrepareQuery(aUseODoH, TRRTYPE_AAAA, requestsToSend);
|
||||||
}
|
}
|
||||||
if (mRecord->af == AF_UNSPEC || mRecord->af == AF_INET) {
|
if (mRecord->af == AF_UNSPEC || mRecord->af == AF_INET) {
|
||||||
PrepareQuery(TRRTYPE_A, requestsToSend);
|
PrepareQuery(aUseODoH, TRRTYPE_A, requestsToSend);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SendQueries(requestsToSend)) {
|
if (SendQueries(requestsToSend)) {
|
||||||
|
mUsingODoH = aUseODoH;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_ERROR_UNKNOWN_HOST;
|
return NS_ERROR_UNKNOWN_HOST;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult TRRQuery::DispatchByTypeLookup(TRR* pushedTRR) {
|
nsresult TRRQuery::DispatchByTypeLookup(TRR* pushedTRR, bool aUseODoH) {
|
||||||
RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRecord);
|
RefPtr<TypeHostRecord> typeRec = do_QueryObject(mRecord);
|
||||||
MOZ_ASSERT(typeRec);
|
MOZ_ASSERT(typeRec);
|
||||||
if (!typeRec) {
|
if (!typeRec) {
|
||||||
|
|
@ -167,7 +179,12 @@ nsresult TRRQuery::DispatchByTypeLookup(TRR* pushedTRR) {
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(("TRR Resolve %s type %d\n", typeRec->host.get(), (int)rectype));
|
LOG(("TRR Resolve %s type %d\n", typeRec->host.get(), (int)rectype));
|
||||||
RefPtr<TRR> trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype);
|
RefPtr<TRR> trr;
|
||||||
|
if (aUseODoH) {
|
||||||
|
trr = new ODoH(this, mRecord, rectype);
|
||||||
|
} else {
|
||||||
|
trr = pushedTRR ? pushedTRR : new TRR(this, mRecord, rectype);
|
||||||
|
}
|
||||||
|
|
||||||
if (pushedTRR || NS_SUCCEEDED(TRRService::Get()->DispatchTRRRequest(trr))) {
|
if (pushedTRR || NS_SUCCEEDED(TRRService::Get()->DispatchTRRRequest(trr))) {
|
||||||
MutexAutoLock trrlock(mTrrLock);
|
MutexAutoLock trrlock(mTrrLock);
|
||||||
|
|
@ -268,7 +285,7 @@ AHostResolver::LookupStatus TRRQuery::CompleteLookup(
|
||||||
if (newRRSet->TRRType() == TRRTYPE_A) {
|
if (newRRSet->TRRType() == TRRTYPE_A) {
|
||||||
LOG(("A lookup failed. Checking if AAAA record exists"));
|
LOG(("A lookup failed. Checking if AAAA record exists"));
|
||||||
nsTArray<RefPtr<TRR>> requestsToSend;
|
nsTArray<RefPtr<TRR>> requestsToSend;
|
||||||
PrepareQuery(TRRTYPE_AAAA, requestsToSend);
|
PrepareQuery(mUsingODoH, TRRTYPE_AAAA, requestsToSend);
|
||||||
if (SendQueries(requestsToSend)) {
|
if (SendQueries(requestsToSend)) {
|
||||||
LOG(("Sent AAAA request"));
|
LOG(("Sent AAAA request"));
|
||||||
return LOOKUP_OK;
|
return LOOKUP_OK;
|
||||||
|
|
@ -276,7 +293,7 @@ AHostResolver::LookupStatus TRRQuery::CompleteLookup(
|
||||||
} else if (newRRSet->TRRType() == TRRTYPE_AAAA) {
|
} else if (newRRSet->TRRType() == TRRTYPE_AAAA) {
|
||||||
LOG(("AAAA lookup failed. Checking if A record exists"));
|
LOG(("AAAA lookup failed. Checking if A record exists"));
|
||||||
nsTArray<RefPtr<TRR>> requestsToSend;
|
nsTArray<RefPtr<TRR>> requestsToSend;
|
||||||
PrepareQuery(TRRTYPE_A, requestsToSend);
|
PrepareQuery(mUsingODoH, TRRTYPE_A, requestsToSend);
|
||||||
if (SendQueries(requestsToSend)) {
|
if (SendQueries(requestsToSend)) {
|
||||||
LOG(("Sent A request"));
|
LOG(("Sent A request"));
|
||||||
return LOOKUP_OK;
|
return LOOKUP_OK;
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ class TRRQuery : public AHostResolver {
|
||||||
mRecord(aHostRecord),
|
mRecord(aHostRecord),
|
||||||
mTrrLock("TRRQuery.mTrrLock") {}
|
mTrrLock("TRRQuery.mTrrLock") {}
|
||||||
|
|
||||||
nsresult DispatchLookup(TRR* pushedTRR = nullptr);
|
nsresult DispatchLookup(TRR* pushedTRR = nullptr, bool aUseODoHProxy = false);
|
||||||
|
|
||||||
void Cancel(nsresult aStatus);
|
void Cancel(nsresult aStatus);
|
||||||
|
|
||||||
|
|
@ -69,13 +69,15 @@ class TRRQuery : public AHostResolver {
|
||||||
mozilla::TimeDuration Duration() { return mTrrDuration; }
|
mozilla::TimeDuration Duration() { return mTrrDuration; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
nsresult DispatchByTypeLookup(TRR* pushedTRR = nullptr);
|
nsresult DispatchByTypeLookup(TRR* pushedTRR = nullptr,
|
||||||
|
bool aUseODoHProxy = false);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~TRRQuery() = default;
|
~TRRQuery() = default;
|
||||||
|
|
||||||
void MarkSendingTRR(TRR* trr, TrrType rectype, MutexAutoLock&);
|
void MarkSendingTRR(TRR* trr, TrrType rectype, MutexAutoLock&);
|
||||||
void PrepareQuery(TrrType aRecType, nsTArray<RefPtr<TRR>>& aRequestsToSend);
|
void PrepareQuery(bool aUseODoH, TrrType aRecType,
|
||||||
|
nsTArray<RefPtr<TRR>>& aRequestsToSend);
|
||||||
bool SendQueries(nsTArray<RefPtr<TRR>>& aRequestsToSend);
|
bool SendQueries(nsTArray<RefPtr<TRR>>& aRequestsToSend);
|
||||||
|
|
||||||
RefPtr<nsHostResolver> mHostResolver;
|
RefPtr<nsHostResolver> mHostResolver;
|
||||||
|
|
@ -94,6 +96,7 @@ class TRRQuery : public AHostResolver {
|
||||||
Atomic<uint32_t> mTRRRequestCounter{0};
|
Atomic<uint32_t> mTRRRequestCounter{0};
|
||||||
|
|
||||||
uint8_t mTRRSuccess = 0; // number of successful TRR responses
|
uint8_t mTRRSuccess = 0; // number of successful TRR responses
|
||||||
|
bool mUsingODoH = false;
|
||||||
bool mCalledCompleteLookup = false;
|
bool mCalledCompleteLookup = false;
|
||||||
|
|
||||||
mozilla::TimeDuration mTrrDuration;
|
mozilla::TimeDuration mTrrDuration;
|
||||||
|
|
|
||||||
|
|
@ -216,6 +216,11 @@ nsresult TRRService::Init() {
|
||||||
sTRRBackgroundThread = thread;
|
sTRRBackgroundThread = thread;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mODoHService = new ODoHService();
|
||||||
|
if (!mODoHService->Init()) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
Preferences::RegisterCallbackAndCall(
|
Preferences::RegisterCallbackAndCall(
|
||||||
EventTelemetryPrefChanged,
|
EventTelemetryPrefChanged,
|
||||||
"network.trr.confirmation_telemetry_enabled"_ns);
|
"network.trr.confirmation_telemetry_enabled"_ns);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include "nsIObserver.h"
|
#include "nsIObserver.h"
|
||||||
#include "nsITimer.h"
|
#include "nsITimer.h"
|
||||||
#include "nsWeakReference.h"
|
#include "nsWeakReference.h"
|
||||||
|
#include "ODoHService.h"
|
||||||
#include "TRRServiceBase.h"
|
#include "TRRServiceBase.h"
|
||||||
#include "nsICaptivePortalService.h"
|
#include "nsICaptivePortalService.h"
|
||||||
#include "nsTHashSet.h"
|
#include "nsTHashSet.h"
|
||||||
|
|
@ -108,6 +109,7 @@ class TRRService : public TRRServiceBase,
|
||||||
|
|
||||||
friend class TRRServiceChild;
|
friend class TRRServiceChild;
|
||||||
friend class TRRServiceParent;
|
friend class TRRServiceParent;
|
||||||
|
friend class ODoHService;
|
||||||
static void AddObserver(nsIObserver* aObserver,
|
static void AddObserver(nsIObserver* aObserver,
|
||||||
nsIObserverService* aObserverService = nullptr);
|
nsIObserverService* aObserverService = nullptr);
|
||||||
static bool CheckCaptivePortalIsPassed();
|
static bool CheckCaptivePortalIsPassed();
|
||||||
|
|
@ -376,6 +378,7 @@ class TRRService : public TRRServiceBase,
|
||||||
// This is used to track whether a confirmation was triggered by a URI change,
|
// This is used to track whether a confirmation was triggered by a URI change,
|
||||||
// so we don't trigger another one just because other prefs have changed.
|
// so we don't trigger another one just because other prefs have changed.
|
||||||
bool mConfirmationTriggered{false};
|
bool mConfirmationTriggered{false};
|
||||||
|
RefPtr<ODoHService> mODoHService;
|
||||||
nsCOMPtr<nsINetworkLinkService> mLinkService;
|
nsCOMPtr<nsINetworkLinkService> mLinkService;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,8 @@ UNIFIED_SOURCES += [
|
||||||
"nsHostRecord.cpp",
|
"nsHostRecord.cpp",
|
||||||
"nsHostResolver.cpp",
|
"nsHostResolver.cpp",
|
||||||
"nsIDNService.cpp",
|
"nsIDNService.cpp",
|
||||||
|
"ODoH.cpp",
|
||||||
|
"ODoHService.cpp",
|
||||||
"punycode.c",
|
"punycode.c",
|
||||||
"TRR.cpp",
|
"TRR.cpp",
|
||||||
"TRRQuery.cpp",
|
"TRRQuery.cpp",
|
||||||
|
|
|
||||||
|
|
@ -128,7 +128,9 @@ NS_IMETHODIMP
|
||||||
nsDNSRecord::IsTRR(bool* retval) {
|
nsDNSRecord::IsTRR(bool* retval) {
|
||||||
MutexAutoLock lock(mHostRecord->addr_info_lock);
|
MutexAutoLock lock(mHostRecord->addr_info_lock);
|
||||||
if (mHostRecord->addr_info) {
|
if (mHostRecord->addr_info) {
|
||||||
*retval = mHostRecord->addr_info->IsTRR();
|
// TODO: Let the consumers of nsIDNSRecord be unaware of the difference of
|
||||||
|
// TRR and ODoH. Will let them know the truth when needed.
|
||||||
|
*retval = mHostRecord->addr_info->IsTRROrODoH();
|
||||||
} else {
|
} else {
|
||||||
*retval = false;
|
*retval = false;
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +146,7 @@ nsDNSRecord::ResolvedInSocketProcess(bool* retval) {
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsDNSRecord::GetTrrFetchDuration(double* aTime) {
|
nsDNSRecord::GetTrrFetchDuration(double* aTime) {
|
||||||
MutexAutoLock lock(mHostRecord->addr_info_lock);
|
MutexAutoLock lock(mHostRecord->addr_info_lock);
|
||||||
if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRR()) {
|
if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRROrODoH()) {
|
||||||
*aTime = mHostRecord->addr_info->GetTrrFetchDuration();
|
*aTime = mHostRecord->addr_info->GetTrrFetchDuration();
|
||||||
} else {
|
} else {
|
||||||
*aTime = 0;
|
*aTime = 0;
|
||||||
|
|
@ -155,7 +157,7 @@ nsDNSRecord::GetTrrFetchDuration(double* aTime) {
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime) {
|
nsDNSRecord::GetTrrFetchDurationNetworkOnly(double* aTime) {
|
||||||
MutexAutoLock lock(mHostRecord->addr_info_lock);
|
MutexAutoLock lock(mHostRecord->addr_info_lock);
|
||||||
if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRR()) {
|
if (mHostRecord->addr_info && mHostRecord->addr_info->IsTRROrODoH()) {
|
||||||
*aTime = mHostRecord->addr_info->GetTrrFetchDurationNetworkOnly();
|
*aTime = mHostRecord->addr_info->GetTrrFetchDurationNetworkOnly();
|
||||||
} else {
|
} else {
|
||||||
*aTime = 0;
|
*aTime = 0;
|
||||||
|
|
@ -845,6 +847,7 @@ nsDNSService::Init() {
|
||||||
observerService->AddObserver(this, "last-pb-context-exited", false);
|
observerService->AddObserver(this, "last-pb-context-exited", false);
|
||||||
observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
|
observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, false);
|
||||||
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||||
|
observerService->AddObserver(this, "odoh-service-activated", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<nsHostResolver> res;
|
RefPtr<nsHostResolver> res;
|
||||||
|
|
@ -1300,6 +1303,14 @@ nsDNSService::GetMyHostName(nsACString& result) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsDNSService::GetODoHActivated(bool* aResult) {
|
||||||
|
NS_ENSURE_ARG(aResult);
|
||||||
|
|
||||||
|
*aResult = mODoHActivated;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsDNSService::Observe(nsISupports* subject, const char* topic,
|
nsDNSService::Observe(nsISupports* subject, const char* topic,
|
||||||
const char16_t* data) {
|
const char16_t* data) {
|
||||||
|
|
@ -1322,6 +1333,8 @@ nsDNSService::Observe(nsISupports* subject, const char* topic,
|
||||||
}
|
}
|
||||||
} else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
} else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||||
Shutdown();
|
Shutdown();
|
||||||
|
} else if (!strcmp(topic, "odoh-service-activated")) {
|
||||||
|
mODoHActivated = u"true"_ns.Equals(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flushCache && resolver) {
|
if (flushCache && resolver) {
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@ class nsDNSService final : public mozilla::net::DNSServiceBase,
|
||||||
uint32_t mResCacheExpiration = 0;
|
uint32_t mResCacheExpiration = 0;
|
||||||
uint32_t mResCacheGrace = 0;
|
uint32_t mResCacheGrace = 0;
|
||||||
bool mResolverPrefsUpdated = false;
|
bool mResolverPrefsUpdated = false;
|
||||||
|
bool mODoHActivated = false;
|
||||||
nsClassHashtable<nsCStringHashKey, nsTArray<nsCString>> mFailedSVCDomainNames;
|
nsClassHashtable<nsCStringHashKey, nsTArray<nsCString>> mFailedSVCDomainNames;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -245,7 +245,7 @@ bool AddrHostRecord::HasUsableResultInternal(
|
||||||
bool AddrHostRecord::RemoveOrRefresh(bool aTrrToo) {
|
bool AddrHostRecord::RemoveOrRefresh(bool aTrrToo) {
|
||||||
// no need to flush TRRed names, they're not resolved "locally"
|
// no need to flush TRRed names, they're not resolved "locally"
|
||||||
MutexAutoLock lock(addr_info_lock);
|
MutexAutoLock lock(addr_info_lock);
|
||||||
if (addr_info && !aTrrToo && addr_info->IsTRR()) {
|
if (addr_info && !aTrrToo && addr_info->IsTRROrODoH()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (LoadNative()) {
|
if (LoadNative()) {
|
||||||
|
|
@ -284,6 +284,21 @@ void AddrHostRecord::ResolveComplete() {
|
||||||
: Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::osFail);
|
: Telemetry::LABELS_DNS_LOOKUP_DISPOSITION3::osFail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mResolverType == DNSResolverType::ODoH) {
|
||||||
|
// XXX(kershaw): Consider adding the failed host name into a blocklist.
|
||||||
|
if (mTRRSuccess) {
|
||||||
|
uint32_t millis = static_cast<uint32_t>(mTrrDuration.ToMilliseconds());
|
||||||
|
Telemetry::Accumulate(Telemetry::DNS_ODOH_LOOKUP_TIME, millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nsHostResolver::Mode() == nsIDNSService::MODE_TRRFIRST) {
|
||||||
|
Telemetry::Accumulate(Telemetry::ODOH_SKIP_REASON_ODOH_FIRST,
|
||||||
|
static_cast<uint32_t>(mTRRSkippedReason));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (mResolverType == DNSResolverType::TRR) {
|
if (mResolverType == DNSResolverType::TRR) {
|
||||||
if (mTRRSuccess) {
|
if (mTRRSuccess) {
|
||||||
MOZ_DIAGNOSTIC_ASSERT(mTRRSkippedReason ==
|
MOZ_DIAGNOSTIC_ASSERT(mTRRSkippedReason ==
|
||||||
|
|
|
||||||
|
|
@ -321,7 +321,7 @@ class AddrHostRecord final : public nsHostRecord {
|
||||||
mozilla::TimeDuration mTrrDuration;
|
mozilla::TimeDuration mTrrDuration;
|
||||||
mozilla::TimeDuration mNativeDuration;
|
mozilla::TimeDuration mNativeDuration;
|
||||||
|
|
||||||
// TRR was used on this record
|
// TRR or ODoH was used on this record
|
||||||
mozilla::Atomic<DNSResolverType> mResolverType{DNSResolverType::Native};
|
mozilla::Atomic<DNSResolverType> mResolverType{DNSResolverType::Native};
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
#include "TRR.h"
|
#include "TRR.h"
|
||||||
#include "TRRQuery.h"
|
#include "TRRQuery.h"
|
||||||
#include "TRRService.h"
|
#include "TRRService.h"
|
||||||
|
#include "ODoHService.h"
|
||||||
|
|
||||||
#include "mozilla/Atomics.h"
|
#include "mozilla/Atomics.h"
|
||||||
#include "mozilla/HashFunctions.h"
|
#include "mozilla/HashFunctions.h"
|
||||||
|
|
@ -932,7 +933,9 @@ nsresult nsHostResolver::TrrLookup(nsHostRecord* aRec,
|
||||||
MaybeRenewHostRecordLocked(rec, aLock);
|
MaybeRenewHostRecordLocked(rec, aLock);
|
||||||
|
|
||||||
RefPtr<TRRQuery> query = new TRRQuery(this, rec);
|
RefPtr<TRRQuery> query = new TRRQuery(this, rec);
|
||||||
nsresult rv = query->DispatchLookup(pushedTRR);
|
bool useODoH = gODoHService->Enabled() &&
|
||||||
|
!((rec->flags & nsIDNSService::RESOLVE_DISABLE_ODOH));
|
||||||
|
nsresult rv = query->DispatchLookup(pushedTRR, useODoH);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
rec->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY);
|
rec->RecordReason(TRRSkippedReason::TRR_DID_NOT_MAKE_QUERY);
|
||||||
return rv;
|
return rv;
|
||||||
|
|
@ -1289,7 +1292,7 @@ void nsHostResolver::PrepareRecordExpirationAddrRecord(
|
||||||
unsigned int grace = mDefaultGracePeriod;
|
unsigned int grace = mDefaultGracePeriod;
|
||||||
|
|
||||||
unsigned int ttl = mDefaultCacheLifetime;
|
unsigned int ttl = mDefaultCacheLifetime;
|
||||||
if (sGetTtlEnabled || rec->addr_info->IsTRR()) {
|
if (sGetTtlEnabled || rec->addr_info->IsTRROrODoH()) {
|
||||||
if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) {
|
if (rec->addr_info && rec->addr_info->TTL() != AddrInfo::NO_TTL_DATA) {
|
||||||
ttl = rec->addr_info->TTL();
|
ttl = rec->addr_info->TTL();
|
||||||
}
|
}
|
||||||
|
|
@ -1601,7 +1604,7 @@ nsHostResolver::LookupStatus nsHostResolver::CompleteLookupLocked(
|
||||||
bool hasNativeResult = false;
|
bool hasNativeResult = false;
|
||||||
{
|
{
|
||||||
MutexAutoLock lock(addrRec->addr_info_lock);
|
MutexAutoLock lock(addrRec->addr_info_lock);
|
||||||
if (addrRec->addr_info && !addrRec->addr_info->IsTRR()) {
|
if (addrRec->addr_info && !addrRec->addr_info->IsTRROrODoH()) {
|
||||||
hasNativeResult = true;
|
hasNativeResult = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1896,7 +1899,7 @@ void nsHostResolver::GetDNSCacheEntries(nsTArray<DNSCacheEntries>* args) {
|
||||||
info.hostaddr.AppendElement(buf);
|
info.hostaddr.AppendElement(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info.TRR = addrRec->addr_info->IsTRR();
|
info.TRR = addrRec->addr_info->IsTRROrODoH();
|
||||||
}
|
}
|
||||||
|
|
||||||
info.originAttributesSuffix = recordEntry.GetKey().originSuffix;
|
info.originAttributesSuffix = recordEntry.GetKey().originSuffix;
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ interface nsIDNSService : nsISupports
|
||||||
RESOLVE_IGNORE_SOCKS_DNS = (1 << 13),
|
RESOLVE_IGNORE_SOCKS_DNS = (1 << 13),
|
||||||
// If set, only cached IP hint addresses will be returned from resolve/asyncResolve.
|
// If set, only cached IP hint addresses will be returned from resolve/asyncResolve.
|
||||||
RESOLVE_IP_HINT = (1 << 14),
|
RESOLVE_IP_HINT = (1 << 14),
|
||||||
|
// If set, do not use ODoH for resolving the host name.
|
||||||
|
RESOLVE_DISABLE_ODOH = (1 << 15),
|
||||||
// If set, the DNS service will pass a DNS record to
|
// If set, the DNS service will pass a DNS record to
|
||||||
// OnLookupComplete even when there was a resolution error.
|
// OnLookupComplete even when there was a resolution error.
|
||||||
RESOLVE_WANT_RECORD_ON_ERROR = (1 << 16),
|
RESOLVE_WANT_RECORD_ON_ERROR = (1 << 16),
|
||||||
|
|
@ -347,6 +349,12 @@ interface nsIDNSService : nsISupports
|
||||||
*/
|
*/
|
||||||
readonly attribute ACString TRRDomainKey;
|
readonly attribute ACString TRRDomainKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true when we have valid ODoHConfigs to encrypt/decrypt oblivious
|
||||||
|
* DNS packets.
|
||||||
|
*/
|
||||||
|
readonly attribute boolean ODoHActivated;
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* Listed below are the various flags that may be OR'd together to form
|
* Listed below are the various flags that may be OR'd together to form
|
||||||
* the aFlags parameter passed to asyncResolve() and resolve().
|
* the aFlags parameter passed to asyncResolve() and resolve().
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,7 @@ parent:
|
||||||
OriginAttributes aOriginAttributes,
|
OriginAttributes aOriginAttributes,
|
||||||
nsCString aRequestString)
|
nsCString aRequestString)
|
||||||
returns (bool aAccepted);
|
returns (bool aAccepted);
|
||||||
|
async ODoHServiceActivated(bool aActivated);
|
||||||
|
|
||||||
async ExcludeHttp2OrHttp3(HttpConnectionInfoCloneArgs aArgs);
|
async ExcludeHttp2OrHttp3(HttpConnectionInfoCloneArgs aArgs);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -309,6 +309,18 @@ void SocketProcessParent::Destroy(RefPtr<SocketProcessParent>&& aParent) {
|
||||||
new DeferredDeleteSocketProcessParent(std::move(aParent)));
|
new DeferredDeleteSocketProcessParent(std::move(aParent)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult SocketProcessParent::RecvODoHServiceActivated(
|
||||||
|
const bool& aActivated) {
|
||||||
|
nsCOMPtr<nsIObserverService> observerService =
|
||||||
|
mozilla::services::GetObserverService();
|
||||||
|
|
||||||
|
if (observerService) {
|
||||||
|
observerService->NotifyObservers(nullptr, "odoh-service-activated",
|
||||||
|
aActivated ? u"true" : u"false");
|
||||||
|
}
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::ipc::IPCResult SocketProcessParent::RecvExcludeHttp2OrHttp3(
|
mozilla::ipc::IPCResult SocketProcessParent::RecvExcludeHttp2OrHttp3(
|
||||||
const HttpConnectionInfoCloneArgs& aArgs) {
|
const HttpConnectionInfoCloneArgs& aArgs) {
|
||||||
RefPtr<nsHttpConnectionInfo> cinfo =
|
RefPtr<nsHttpConnectionInfo> cinfo =
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,8 @@ class SocketProcessParent final
|
||||||
nsIURI* aPushedURL, OriginAttributes&& aOriginAttributes,
|
nsIURI* aPushedURL, OriginAttributes&& aOriginAttributes,
|
||||||
nsCString&& aRequestString, CachePushCheckResolver&& aResolver);
|
nsCString&& aRequestString, CachePushCheckResolver&& aResolver);
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult RecvODoHServiceActivated(const bool& aActivated);
|
||||||
|
|
||||||
mozilla::ipc::IPCResult RecvExcludeHttp2OrHttp3(
|
mozilla::ipc::IPCResult RecvExcludeHttp2OrHttp3(
|
||||||
const HttpConnectionInfoCloneArgs& aArgs);
|
const HttpConnectionInfoCloneArgs& aArgs);
|
||||||
mozilla::ipc::IPCResult RecvOnConsoleMessage(const nsString& aMessage);
|
mozilla::ipc::IPCResult RecvOnConsoleMessage(const nsString& aMessage);
|
||||||
|
|
|
||||||
|
|
@ -1180,15 +1180,18 @@ nsresult TRRServiceChannel::SetupReplacementChannel(nsIURI* aNewURI,
|
||||||
encodedChannel->SetApplyConversion(LoadApplyConversion());
|
encodedChannel->SetApplyConversion(LoadApplyConversion());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mContentTypeHint is empty when this channel is used to download
|
||||||
|
// ODoHConfigs.
|
||||||
if (mContentTypeHint.IsEmpty()) {
|
if (mContentTypeHint.IsEmpty()) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we set content-type on the old channel properly.
|
// Make sure we set content-type on the old channel properly.
|
||||||
MOZ_ASSERT(mContentTypeHint.Equals("application/dns-message"));
|
MOZ_ASSERT(mContentTypeHint.Equals("application/dns-message") ||
|
||||||
|
mContentTypeHint.Equals("application/oblivious-dns-message"));
|
||||||
|
|
||||||
// Apply TRR specific settings. Note that we already know mContentTypeHint is
|
// Apply TRR specific settings. Note that we already know mContentTypeHint is
|
||||||
// "application/dns-message" here.
|
// "application/dns-message" or "application/oblivious-dns-message" here.
|
||||||
return TRR::SetupTRRServiceChannelInternal(
|
return TRR::SetupTRRServiceChannelInternal(
|
||||||
httpChannel,
|
httpChannel,
|
||||||
mRequestHead.ParsedMethod() == nsHttpRequestHead::kMethod_Get,
|
mRequestHead.ParsedMethod() == nsHttpRequestHead::kMethod_Get,
|
||||||
|
|
|
||||||
|
|
@ -7029,7 +7029,17 @@ static void RecordOnStartTelemetry(nsresult aStatus,
|
||||||
Others = 2,
|
Others = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (TRRService::Get() && TRRService::Get()->IsConfirmed()) {
|
if (StaticPrefs::network_trr_odoh_enabled()) {
|
||||||
|
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
|
||||||
|
if (!dns) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bool ODoHActivated = false;
|
||||||
|
if (NS_SUCCEEDED(dns->GetODoHActivated(&ODoHActivated)) && ODoHActivated) {
|
||||||
|
Telemetry::Accumulate(Telemetry::HTTP_CHANNEL_ONSTART_SUCCESS_ODOH,
|
||||||
|
NS_SUCCEEDED(aStatus));
|
||||||
|
}
|
||||||
|
} else if (TRRService::Get() && TRRService::Get()->IsConfirmed()) {
|
||||||
// Note this telemetry probe is not working when DNS resolution is done in
|
// Note this telemetry probe is not working when DNS resolution is done in
|
||||||
// the socket process.
|
// the socket process.
|
||||||
HttpOnStartState state = HttpOnStartState::Others;
|
HttpOnStartState state = HttpOnStartState::Others;
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ interface nsISocketProvider : nsISupports
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If set, indicates that the connection used a privacy-preserving DNS
|
* If set, indicates that the connection used a privacy-preserving DNS
|
||||||
* transport such as DoH, DoQ or similar. Currently this field is
|
* transport such as DoH, DoQ, ODOH or similar. Currently this field is
|
||||||
* set only when DoH is used via the TRR.
|
* set only when DoH is used via the TRR.
|
||||||
*/
|
*/
|
||||||
const unsigned long USED_PRIVATE_DNS = (1 << 12);
|
const unsigned long USED_PRIVATE_DNS = (1 << 12);
|
||||||
|
|
|
||||||
295
netwerk/test/unit/test_odoh.js
Normal file
295
netwerk/test/unit/test_odoh.js
Normal file
|
|
@ -0,0 +1,295 @@
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Most of tests in this file are reused from test_trr.js, except tests listed
|
||||||
|
* below. Since ODoH doesn't support push and customerized DNS resolver, some
|
||||||
|
* related tests are skipped.
|
||||||
|
*
|
||||||
|
* test_trr_flags
|
||||||
|
* test_push
|
||||||
|
* test_clearCacheOnURIChange // TODO: Clear DNS cache when ODoH prefs changed.
|
||||||
|
* test_dnsSuffix
|
||||||
|
* test_async_resolve_with_trr_server
|
||||||
|
* test_content_encoding_gzip
|
||||||
|
* test_redirect
|
||||||
|
* test_confirmation
|
||||||
|
* test_detected_uri
|
||||||
|
* test_pref_changes
|
||||||
|
* test_dohrollout_mode
|
||||||
|
* test_purge_trr_cache_on_mode_change
|
||||||
|
* test_old_bootstrap_pref
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/* import-globals-from trr_common.js */
|
||||||
|
|
||||||
|
const certOverrideService = Cc[
|
||||||
|
"@mozilla.org/security/certoverride;1"
|
||||||
|
].getService(Ci.nsICertOverrideService);
|
||||||
|
|
||||||
|
add_setup(async function setup() {
|
||||||
|
h2Port = trr_test_setup();
|
||||||
|
runningODoHTests = true;
|
||||||
|
|
||||||
|
// This is for skiping the security check for the odoh host.
|
||||||
|
certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
registerCleanupFunction(() => {
|
||||||
|
trr_clear_prefs();
|
||||||
|
Services.prefs.clearUserPref("network.trr.odoh.enabled");
|
||||||
|
Services.prefs.clearUserPref("network.trr.odoh.target_path");
|
||||||
|
Services.prefs.clearUserPref("network.trr.odoh.configs_uri");
|
||||||
|
certOverrideService.setDisableAllSecurityChecksAndLetAttackersInterceptMyData(
|
||||||
|
false
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mozinfo.socketprocess_networking) {
|
||||||
|
Services.dns; // Needed to trigger socket process.
|
||||||
|
await TestUtils.waitForCondition(() => Services.io.socketProcessLaunched);
|
||||||
|
}
|
||||||
|
|
||||||
|
Services.prefs.setIntPref("network.trr.mode", Ci.nsIDNSService.MODE_TRRONLY);
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig() {
|
||||||
|
// use the h2 server as DOH provider
|
||||||
|
Services.prefs.setCharPref(
|
||||||
|
"network.trr.uri",
|
||||||
|
"https://foo.example.com:" + h2Port + "/odohconfig"
|
||||||
|
);
|
||||||
|
|
||||||
|
let { inRecord } = await new TRRDNSListener("odoh_host.example.com", {
|
||||||
|
type: Ci.nsIDNSService.RESOLVE_TYPE_HTTPSSVC,
|
||||||
|
});
|
||||||
|
let answer = inRecord.QueryInterface(Ci.nsIDNSHTTPSSVCRecord).records;
|
||||||
|
Assert.equal(answer[0].priority, 1);
|
||||||
|
Assert.equal(answer[0].name, "odoh_host.example.com");
|
||||||
|
Assert.equal(answer[0].values.length, 1);
|
||||||
|
let odohconfig = answer[0].values[0].QueryInterface(
|
||||||
|
Ci.nsISVCParamODoHConfig
|
||||||
|
).ODoHConfig;
|
||||||
|
Assert.equal(odohconfig.length, 46);
|
||||||
|
});
|
||||||
|
|
||||||
|
let testIndex = 0;
|
||||||
|
|
||||||
|
async function ODoHConfigTest(query, ODoHHost, expectedResult = false) {
|
||||||
|
Services.prefs.setCharPref(
|
||||||
|
"network.trr.uri",
|
||||||
|
"https://foo.example.com:" + h2Port + "/odohconfig?" + query
|
||||||
|
);
|
||||||
|
|
||||||
|
// Setting the pref "network.trr.odoh.target_host" will trigger the reload of
|
||||||
|
// the ODoHConfigs.
|
||||||
|
if (ODoHHost != undefined) {
|
||||||
|
Services.prefs.setCharPref("network.trr.odoh.target_host", ODoHHost);
|
||||||
|
} else {
|
||||||
|
Services.prefs.setCharPref(
|
||||||
|
"network.trr.odoh.target_host",
|
||||||
|
`https://odoh_host_${testIndex++}.com`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await topicObserved("odoh-service-activated");
|
||||||
|
Assert.equal(Services.dns.ODoHActivated, expectedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig1() {
|
||||||
|
await ODoHConfigTest("invalid=empty");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig2() {
|
||||||
|
await ODoHConfigTest("invalid=version");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig3() {
|
||||||
|
await ODoHConfigTest("invalid=configLength");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig4() {
|
||||||
|
await ODoHConfigTest("invalid=totalLength");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig5() {
|
||||||
|
await ODoHConfigTest("invalid=kemId");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig6() {
|
||||||
|
// Use a very short TTL.
|
||||||
|
Services.prefs.setIntPref("network.trr.odoh.min_ttl", 1);
|
||||||
|
await ODoHConfigTest("invalid=kemId&ttl=1");
|
||||||
|
|
||||||
|
// This is triggered by the expiration of the TTL.
|
||||||
|
await topicObserved("odoh-service-activated");
|
||||||
|
Assert.ok(!Services.dns.ODoHActivated);
|
||||||
|
Services.prefs.clearUserPref("network.trr.odoh.min_ttl");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig7() {
|
||||||
|
Services.dns.clearCache(true);
|
||||||
|
Services.prefs.setIntPref("network.trr.mode", 2); // TRR-first
|
||||||
|
Services.prefs.setBoolPref("network.trr.odoh.enabled", true);
|
||||||
|
// At this point, we've queried the ODoHConfig, but there is no usable config
|
||||||
|
// (kemId is not supported). So, we should see the DNS result is from the
|
||||||
|
// native resolver.
|
||||||
|
await new TRRDNSListener("bar.example.com", "127.0.0.1");
|
||||||
|
});
|
||||||
|
|
||||||
|
async function ODoHConfigTestHTTP(configUri, expectedResult) {
|
||||||
|
// Setting the pref "network.trr.odoh.configs_uri" will trigger the reload of
|
||||||
|
// the ODoHConfigs.
|
||||||
|
Services.prefs.setCharPref("network.trr.odoh.configs_uri", configUri);
|
||||||
|
|
||||||
|
await topicObserved("odoh-service-activated");
|
||||||
|
Assert.equal(Services.dns.ODoHActivated, expectedResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig8() {
|
||||||
|
Services.dns.clearCache(true);
|
||||||
|
Services.prefs.setCharPref("network.trr.uri", "");
|
||||||
|
|
||||||
|
await ODoHConfigTestHTTP(
|
||||||
|
`https://foo.example.com:${h2Port}/odohconfig?downloadFrom=http`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig9() {
|
||||||
|
// Reset the prefs back to the correct values, so we will get valid
|
||||||
|
// ODoHConfigs when "network.trr.odoh.configs_uri" is cleared below.
|
||||||
|
Services.prefs.setCharPref(
|
||||||
|
"network.trr.uri",
|
||||||
|
`https://foo.example.com:${h2Port}/odohconfig`
|
||||||
|
);
|
||||||
|
Services.prefs.setCharPref(
|
||||||
|
"network.trr.odoh.target_host",
|
||||||
|
`https://odoh_host.example.com:${h2Port}`
|
||||||
|
);
|
||||||
|
|
||||||
|
// Use a very short TTL.
|
||||||
|
Services.prefs.setIntPref("network.trr.odoh.min_ttl", 1);
|
||||||
|
// This will trigger to download ODoHConfigs from HTTPS RR.
|
||||||
|
Services.prefs.clearUserPref("network.trr.odoh.configs_uri");
|
||||||
|
|
||||||
|
await topicObserved("odoh-service-activated");
|
||||||
|
Assert.ok(Services.dns.ODoHActivated);
|
||||||
|
|
||||||
|
await ODoHConfigTestHTTP(
|
||||||
|
`https://foo.example.com:${h2Port}/odohconfig?downloadFrom=http`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
// This is triggered by the expiration of the TTL.
|
||||||
|
await topicObserved("odoh-service-activated");
|
||||||
|
Assert.ok(Services.dns.ODoHActivated);
|
||||||
|
Services.prefs.clearUserPref("network.trr.odoh.min_ttl");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testODoHConfig10() {
|
||||||
|
// We can still get ODoHConfigs from HTTPS RR.
|
||||||
|
await ODoHConfigTestHTTP("http://invalid_odoh_config.com", true);
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function ensureODoHConfig() {
|
||||||
|
Services.prefs.setBoolPref("network.trr.odoh.enabled", true);
|
||||||
|
// Make sure we can connect to ODOH target successfully.
|
||||||
|
Services.prefs.setCharPref(
|
||||||
|
"network.dns.localDomains",
|
||||||
|
"odoh_host.example.com"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Make sure we have an usable ODoHConfig to use.
|
||||||
|
await ODoHConfigTestHTTP(
|
||||||
|
`https://foo.example.com:${h2Port}/odohconfig?downloadFrom=http`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(test_A_record);
|
||||||
|
|
||||||
|
add_task(test_AAAA_records);
|
||||||
|
|
||||||
|
add_task(test_RFC1918);
|
||||||
|
|
||||||
|
add_task(test_GET_ECS);
|
||||||
|
|
||||||
|
add_task(test_timeout_mode3);
|
||||||
|
|
||||||
|
add_task(test_strict_native_fallback);
|
||||||
|
|
||||||
|
add_task(test_no_answers_fallback);
|
||||||
|
|
||||||
|
add_task(test_404_fallback);
|
||||||
|
|
||||||
|
add_task(test_mode_1_and_4);
|
||||||
|
|
||||||
|
add_task(test_CNAME);
|
||||||
|
|
||||||
|
add_task(test_name_mismatch);
|
||||||
|
|
||||||
|
add_task(test_mode_2);
|
||||||
|
|
||||||
|
add_task(test_excluded_domains);
|
||||||
|
|
||||||
|
add_task(test_captiveportal_canonicalURL);
|
||||||
|
|
||||||
|
add_task(test_parentalcontrols);
|
||||||
|
|
||||||
|
// TRR-first check that DNS result is used if domain is part of the builtin-excluded-domains pref
|
||||||
|
add_task(test_builtin_excluded_domains);
|
||||||
|
|
||||||
|
add_task(test_excluded_domains_mode3);
|
||||||
|
|
||||||
|
add_task(test25e);
|
||||||
|
|
||||||
|
add_task(test_parentalcontrols_mode3);
|
||||||
|
|
||||||
|
add_task(test_builtin_excluded_domains_mode3);
|
||||||
|
|
||||||
|
add_task(count_cookies);
|
||||||
|
|
||||||
|
add_task(test_connection_closed);
|
||||||
|
|
||||||
|
add_task(test_fetch_time);
|
||||||
|
|
||||||
|
add_task(test_fqdn);
|
||||||
|
|
||||||
|
add_task(test_ipv6_trr_fallback);
|
||||||
|
|
||||||
|
add_task(test_ipv4_trr_fallback);
|
||||||
|
|
||||||
|
add_task(test_no_retry_without_doh);
|
||||||
|
|
||||||
|
add_task(test_connection_reuse_and_cycling).skip(); // Bug 1742743
|
||||||
|
|
||||||
|
add_task(async function testODoHConfigNotAvailableInMode3() {
|
||||||
|
Services.dns.clearCache(true);
|
||||||
|
Services.prefs.setIntPref("network.trr.mode", 3);
|
||||||
|
Services.prefs.setCharPref("network.trr.uri", "");
|
||||||
|
|
||||||
|
await ODoHConfigTestHTTP("https://failed_odoh_config.com", false);
|
||||||
|
|
||||||
|
// In mode 3, the DNS lookup should fail.
|
||||||
|
let { inStatus } = await new TRRDNSListener("test.example.com", {
|
||||||
|
expectedSuccess: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.equal(inStatus, Cr.NS_ERROR_UNKNOWN_HOST);
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function testODoHConfigNotAvailableInMode2() {
|
||||||
|
Services.dns.clearCache(true);
|
||||||
|
Services.prefs.setIntPref("network.trr.mode", 2);
|
||||||
|
Services.prefs.setCharPref("network.trr.uri", "");
|
||||||
|
|
||||||
|
await ODoHConfigTestHTTP("https://failed_odoh_config_1.com", false);
|
||||||
|
|
||||||
|
// In mode 2, we fallback to native.
|
||||||
|
await new TRRDNSListener("test.example.com", "127.0.0.1");
|
||||||
|
});
|
||||||
|
|
@ -8,6 +8,7 @@ SetParentalControlEnabled(false);
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
h2Port = trr_test_setup();
|
h2Port = trr_test_setup();
|
||||||
|
runningODoHTests = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
setup();
|
setup();
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ const { TelemetryTestUtils } = ChromeUtils.importESModule(
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
h2Port = trr_test_setup();
|
h2Port = trr_test_setup();
|
||||||
|
runningODoHTests = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let TRR_OK = 1;
|
let TRR_OK = 1;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ const pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService();
|
||||||
|
|
||||||
function setup() {
|
function setup() {
|
||||||
h2Port = trr_test_setup();
|
h2Port = trr_test_setup();
|
||||||
|
runningODoHTests = false;
|
||||||
SetParentalControlEnabled(false);
|
SetParentalControlEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ async function SetParentalControlEnabled(aEnabled) {
|
||||||
MockRegistrar.unregister(cid);
|
MockRegistrar.unregister(cid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let runningODoHTests = false;
|
||||||
let runningOHTTPTests = false;
|
let runningOHTTPTests = false;
|
||||||
let h2Port;
|
let h2Port;
|
||||||
|
|
||||||
|
|
@ -68,6 +69,8 @@ function setModeAndURIForOHTTP(mode, path, domain) {
|
||||||
function setModeAndURI(mode, path, domain) {
|
function setModeAndURI(mode, path, domain) {
|
||||||
if (runningOHTTPTests) {
|
if (runningOHTTPTests) {
|
||||||
setModeAndURIForOHTTP(mode, path, domain);
|
setModeAndURIForOHTTP(mode, path, domain);
|
||||||
|
} else if (runningODoHTests) {
|
||||||
|
setModeAndURIForODoH(mode, path);
|
||||||
} else {
|
} else {
|
||||||
Services.prefs.setIntPref("network.trr.mode", mode);
|
Services.prefs.setIntPref("network.trr.mode", mode);
|
||||||
if (domain) {
|
if (domain) {
|
||||||
|
|
@ -194,7 +197,11 @@ async function test_GET_ECS() {
|
||||||
info("Verifying resolution via GET with ECS disabled");
|
info("Verifying resolution via GET with ECS disabled");
|
||||||
Services.dns.clearCache(true);
|
Services.dns.clearCache(true);
|
||||||
// The template part should be discarded
|
// The template part should be discarded
|
||||||
|
if (runningODoHTests) {
|
||||||
|
setModeAndURI(3, "odoh");
|
||||||
|
} else {
|
||||||
setModeAndURI(3, "doh{?dns}");
|
setModeAndURI(3, "doh{?dns}");
|
||||||
|
}
|
||||||
Services.prefs.setBoolPref("network.trr.useGET", true);
|
Services.prefs.setBoolPref("network.trr.useGET", true);
|
||||||
Services.prefs.setBoolPref("network.trr.disable-ECS", true);
|
Services.prefs.setBoolPref("network.trr.disable-ECS", true);
|
||||||
|
|
||||||
|
|
@ -202,7 +209,11 @@ async function test_GET_ECS() {
|
||||||
|
|
||||||
info("Verifying resolution via GET with ECS enabled");
|
info("Verifying resolution via GET with ECS enabled");
|
||||||
Services.dns.clearCache(true);
|
Services.dns.clearCache(true);
|
||||||
|
if (runningODoHTests) {
|
||||||
|
setModeAndURI(3, "odoh");
|
||||||
|
} else {
|
||||||
setModeAndURI(3, "doh");
|
setModeAndURI(3, "doh");
|
||||||
|
}
|
||||||
Services.prefs.setBoolPref("network.trr.disable-ECS", false);
|
Services.prefs.setBoolPref("network.trr.disable-ECS", false);
|
||||||
|
|
||||||
await new TRRDNSListener("get.example.com", "5.5.5.5");
|
await new TRRDNSListener("get.example.com", "5.5.5.5");
|
||||||
|
|
@ -353,6 +364,14 @@ async function test_strict_native_fallback() {
|
||||||
info("Now with confirmation failed - should fallback");
|
info("Now with confirmation failed - should fallback");
|
||||||
Services.dns.clearCache(true);
|
Services.dns.clearCache(true);
|
||||||
setModeAndURI(2, "doh?responseIP=2.2.2.2&corruptedAnswer=true");
|
setModeAndURI(2, "doh?responseIP=2.2.2.2&corruptedAnswer=true");
|
||||||
|
if (runningODoHTests) {
|
||||||
|
Services.prefs.setCharPref(
|
||||||
|
"network.trr.uri",
|
||||||
|
"https://foo.example.com:" +
|
||||||
|
h2Port +
|
||||||
|
"/odohconfig?failConfirmation=true"
|
||||||
|
);
|
||||||
|
}
|
||||||
Services.prefs.setCharPref("network.trr.confirmationNS", "example.com");
|
Services.prefs.setCharPref("network.trr.confirmationNS", "example.com");
|
||||||
await TestUtils.waitForCondition(
|
await TestUtils.waitForCondition(
|
||||||
// 3 => CONFIRM_FAILED, 4 => CONFIRM_TRYING_FAILED
|
// 3 => CONFIRM_FAILED, 4 => CONFIRM_TRYING_FAILED
|
||||||
|
|
@ -371,6 +390,12 @@ async function test_strict_native_fallback() {
|
||||||
setModeAndURI(2, "doh?responseIP=2.2.2.2");
|
setModeAndURI(2, "doh?responseIP=2.2.2.2");
|
||||||
if (!mozinfo.socketprocess_networking) {
|
if (!mozinfo.socketprocess_networking) {
|
||||||
// Only need to reset confirmation state if we messed with it before.
|
// Only need to reset confirmation state if we messed with it before.
|
||||||
|
if (runningODoHTests) {
|
||||||
|
Services.prefs.setCharPref(
|
||||||
|
"network.trr.uri",
|
||||||
|
"https://foo.example.com:" + h2Port + "/odohconfig"
|
||||||
|
);
|
||||||
|
}
|
||||||
Services.prefs.setCharPref("network.trr.confirmationNS", "skip");
|
Services.prefs.setCharPref("network.trr.confirmationNS", "skip");
|
||||||
await TestUtils.waitForCondition(
|
await TestUtils.waitForCondition(
|
||||||
// 5 => CONFIRM_DISABLED
|
// 5 => CONFIRM_DISABLED
|
||||||
|
|
@ -471,14 +496,22 @@ async function test_CNAME() {
|
||||||
// The dns-cname path alternates between sending us a CNAME pointing to
|
// The dns-cname path alternates between sending us a CNAME pointing to
|
||||||
// another domain, and an A record. If we follow the cname correctly, doing
|
// another domain, and an A record. If we follow the cname correctly, doing
|
||||||
// a lookup with this path as the DoH URI should resolve to that A record.
|
// a lookup with this path as the DoH URI should resolve to that A record.
|
||||||
|
if (runningODoHTests) {
|
||||||
|
setModeAndURI(3, "odoh?cname=content");
|
||||||
|
} else {
|
||||||
setModeAndURI(3, "dns-cname");
|
setModeAndURI(3, "dns-cname");
|
||||||
|
}
|
||||||
|
|
||||||
await new TRRDNSListener("cname.example.com", "99.88.77.66");
|
await new TRRDNSListener("cname.example.com", "99.88.77.66");
|
||||||
|
|
||||||
info("Verifying that we bail out when we're thrown into a CNAME loop");
|
info("Verifying that we bail out when we're thrown into a CNAME loop");
|
||||||
Services.dns.clearCache(true);
|
Services.dns.clearCache(true);
|
||||||
// First mode 3.
|
// First mode 3.
|
||||||
|
if (runningODoHTests) {
|
||||||
|
setModeAndURI(3, "odoh?responseIP=none&cnameloop=true");
|
||||||
|
} else {
|
||||||
setModeAndURI(3, "doh?responseIP=none&cnameloop=true");
|
setModeAndURI(3, "doh?responseIP=none&cnameloop=true");
|
||||||
|
}
|
||||||
|
|
||||||
let { inStatus } = await new TRRDNSListener(
|
let { inStatus } = await new TRRDNSListener(
|
||||||
"test18.example.com",
|
"test18.example.com",
|
||||||
|
|
@ -492,14 +525,22 @@ async function test_CNAME() {
|
||||||
|
|
||||||
// Now mode 2.
|
// Now mode 2.
|
||||||
Services.dns.clearCache(true);
|
Services.dns.clearCache(true);
|
||||||
|
if (runningODoHTests) {
|
||||||
|
setModeAndURI(2, "ododoh?responseIP=none&cnameloop=trueoh");
|
||||||
|
} else {
|
||||||
setModeAndURI(2, "doh?responseIP=none&cnameloop=true");
|
setModeAndURI(2, "doh?responseIP=none&cnameloop=true");
|
||||||
|
}
|
||||||
|
|
||||||
await new TRRDNSListener("test20.example.com", "127.0.0.1"); // Should fallback
|
await new TRRDNSListener("test20.example.com", "127.0.0.1"); // Should fallback
|
||||||
|
|
||||||
info("Check that we correctly handle CNAME bundled with an A record");
|
info("Check that we correctly handle CNAME bundled with an A record");
|
||||||
Services.dns.clearCache(true);
|
Services.dns.clearCache(true);
|
||||||
// "dns-cname-a" path causes server to send a CNAME as well as an A record
|
// "dns-cname-a" path causes server to send a CNAME as well as an A record
|
||||||
|
if (runningODoHTests) {
|
||||||
|
setModeAndURI(3, "odoh?cname=ARecord");
|
||||||
|
} else {
|
||||||
setModeAndURI(3, "dns-cname-a");
|
setModeAndURI(3, "dns-cname-a");
|
||||||
|
}
|
||||||
|
|
||||||
await new TRRDNSListener("cname-a.example.com", "9.8.7.6");
|
await new TRRDNSListener("cname-a.example.com", "9.8.7.6");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -611,6 +611,11 @@ skip-if =
|
||||||
!debug
|
!debug
|
||||||
(os == "win" && socketprocess_networking)
|
(os == "win" && socketprocess_networking)
|
||||||
[test_SuperfluousAuth.js]
|
[test_SuperfluousAuth.js]
|
||||||
|
[test_odoh.js]
|
||||||
|
head = head_channels.js head_cache.js head_cookies.js head_trr.js head_http3.js trr_common.js
|
||||||
|
run-sequentially = node server exceptions dont replay well
|
||||||
|
skip-if =
|
||||||
|
os == "win" && os_version == "6.1" # Skip on Azure - frequent failure
|
||||||
[test_trr_confirmation.js]
|
[test_trr_confirmation.js]
|
||||||
skip-if =
|
skip-if =
|
||||||
socketprocess_networking # confirmation state isn't passed cross-process
|
socketprocess_networking # confirmation state isn't passed cross-process
|
||||||
|
|
|
||||||
|
|
@ -544,6 +544,7 @@ ARCHIVE_FILES = {
|
||||||
"node_ip/**",
|
"node_ip/**",
|
||||||
"node-ws/**",
|
"node-ws/**",
|
||||||
"dns-packet/**",
|
"dns-packet/**",
|
||||||
|
"odoh-wasm/**",
|
||||||
"remotexpcshelltests.py",
|
"remotexpcshelltests.py",
|
||||||
"runxpcshelltests.py",
|
"runxpcshelltests.py",
|
||||||
"selftest.py",
|
"selftest.py",
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ const ip = require(`${node_http2_root}/../node_ip`);
|
||||||
const { fork } = require("child_process");
|
const { fork } = require("child_process");
|
||||||
const path = require("path");
|
const path = require("path");
|
||||||
const zlib = require("zlib");
|
const zlib = require("zlib");
|
||||||
|
const odoh = require(`${node_http2_root}/../odoh-wasm/pkg`);
|
||||||
|
|
||||||
// Hook into the decompression code to log the decompressed name-value pairs
|
// Hook into the decompression code to log the decompressed name-value pairs
|
||||||
var compression_module = node_http2_root + "/lib/protocol/compressor";
|
var compression_module = node_http2_root + "/lib/protocol/compressor";
|
||||||
|
|
@ -1120,6 +1121,174 @@ function handleRequest(req, res) {
|
||||||
res.end("");
|
res.end("");
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
} else if (u.pathname === "/odohconfig") {
|
||||||
|
let payload = Buffer.from("");
|
||||||
|
req.on("data", function receiveData(chunk) {
|
||||||
|
payload = Buffer.concat([payload, chunk]);
|
||||||
|
});
|
||||||
|
req.on("end", function finishedData() {
|
||||||
|
let answers = [];
|
||||||
|
let odohconfig;
|
||||||
|
if (u.query.invalid) {
|
||||||
|
if (u.query.invalid === "empty") {
|
||||||
|
odohconfig = Buffer.from("");
|
||||||
|
} else if (u.query.invalid === "version") {
|
||||||
|
odohconfig = Buffer.from(
|
||||||
|
"002cff030028002000010001002021c8c16355091b28d521cb196627297955c1b607a3dcf1f136534578460d077d",
|
||||||
|
"hex"
|
||||||
|
);
|
||||||
|
} else if (u.query.invalid === "configLength") {
|
||||||
|
odohconfig = Buffer.from(
|
||||||
|
"002cff040028002000010001002021c8c16355091b28d521cb196627297955c1b607a3dcf1f136534578460d07",
|
||||||
|
"hex"
|
||||||
|
);
|
||||||
|
} else if (u.query.invalid === "totalLength") {
|
||||||
|
odohconfig = Buffer.from(
|
||||||
|
"012cff030028002000010001002021c8c16355091b28d521cb196627297955c1b607a3dcf1f136534578460d077d",
|
||||||
|
"hex"
|
||||||
|
);
|
||||||
|
} else if (u.query.invalid === "kemId") {
|
||||||
|
odohconfig = Buffer.from(
|
||||||
|
"002cff040028002100010001002021c8c16355091b28d521cb196627297955c1b607a3dcf1f136534578460d077d",
|
||||||
|
"hex"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
odohconfig = odoh.get_odoh_config();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.query.downloadFrom === "http") {
|
||||||
|
res.writeHead(200);
|
||||||
|
res.write(odohconfig);
|
||||||
|
res.end("");
|
||||||
|
} else {
|
||||||
|
var b64encoded = Buffer.from(odohconfig).toString("base64");
|
||||||
|
let packet = dnsPacket.decode(payload);
|
||||||
|
if (
|
||||||
|
u.query.failConfirmation == "true" &&
|
||||||
|
packet.questions[0].type == "NS" &&
|
||||||
|
packet.questions[0].name == "example.com"
|
||||||
|
) {
|
||||||
|
res.writeHead(200);
|
||||||
|
res.write("<12bytes");
|
||||||
|
res.end("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (packet.questions[0].type == "HTTPS") {
|
||||||
|
answers.push({
|
||||||
|
name: packet.questions[0].name,
|
||||||
|
type: packet.questions[0].type,
|
||||||
|
ttl: u.query.ttl ? u.query.ttl : 55,
|
||||||
|
class: "IN",
|
||||||
|
flush: false,
|
||||||
|
data: {
|
||||||
|
priority: 1,
|
||||||
|
name: packet.questions[0].name,
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
key: "odoh",
|
||||||
|
value: b64encoded,
|
||||||
|
needBase64Decode: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let buf = dnsPacket.encode({
|
||||||
|
type: "response",
|
||||||
|
id: packet.id,
|
||||||
|
flags: dnsPacket.RECURSION_DESIRED,
|
||||||
|
questions: packet.questions,
|
||||||
|
answers,
|
||||||
|
});
|
||||||
|
|
||||||
|
res.setHeader("Content-Type", "application/dns-message");
|
||||||
|
res.setHeader("Content-Length", buf.length);
|
||||||
|
res.writeHead(200);
|
||||||
|
res.write(buf);
|
||||||
|
res.end("");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
} else if (u.pathname === "/odoh") {
|
||||||
|
let responseIP = u.query.responseIP;
|
||||||
|
if (!responseIP) {
|
||||||
|
responseIP = "5.5.5.5";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.query.auth) {
|
||||||
|
if (!handleAuth()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.query.noResponse) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload = Buffer.from("");
|
||||||
|
|
||||||
|
function emitResponse(response, requestPayload) {
|
||||||
|
let decryptedQuery = odoh.decrypt_query(requestPayload);
|
||||||
|
let packet = dnsPacket.decode(Buffer.from(decryptedQuery.buffer));
|
||||||
|
let answer = createDNSAnswer(
|
||||||
|
response,
|
||||||
|
packet,
|
||||||
|
responseIP,
|
||||||
|
requestPayload
|
||||||
|
);
|
||||||
|
if (!answer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let encryptedResponse = odoh.create_response(answer);
|
||||||
|
writeDNSResponse(
|
||||||
|
response,
|
||||||
|
encryptedResponse,
|
||||||
|
getDelayFromPacket(packet, responseType(packet, responseIP)),
|
||||||
|
"application/oblivious-dns-message"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.query.dns) {
|
||||||
|
payload = Buffer.from(u.query.dns, "base64");
|
||||||
|
emitResponse(res, payload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
req.on("data", function receiveData(chunk) {
|
||||||
|
payload = Buffer.concat([payload, chunk]);
|
||||||
|
});
|
||||||
|
req.on("end", function finishedData() {
|
||||||
|
if (u.query.httpError) {
|
||||||
|
res.writeHead(404);
|
||||||
|
res.end("Not Found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (u.query.cname) {
|
||||||
|
let decryptedQuery = odoh.decrypt_query(payload);
|
||||||
|
let rContent;
|
||||||
|
if (u.query.cname === "ARecord") {
|
||||||
|
rContent = createCNameARecord();
|
||||||
|
} else {
|
||||||
|
rContent = createCNameContent(Buffer.from(decryptedQuery.buffer));
|
||||||
|
}
|
||||||
|
let encryptedResponse = odoh.create_response(rContent);
|
||||||
|
res.setHeader("Content-Type", "application/oblivious-dns-message");
|
||||||
|
res.setHeader("Content-Length", encryptedResponse.length);
|
||||||
|
res.writeHead(200);
|
||||||
|
res.write(encryptedResponse);
|
||||||
|
res.end("");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// parload is empty when we send redirect response.
|
||||||
|
if (payload.length) {
|
||||||
|
emitResponse(res, payload);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
} else if (u.pathname === "/httpssvc_as_altsvc") {
|
} else if (u.pathname === "/httpssvc_as_altsvc") {
|
||||||
let payload = Buffer.from("");
|
let payload = Buffer.from("");
|
||||||
req.on("data", function receiveData(chunk) {
|
req.on("data", function receiveData(chunk) {
|
||||||
|
|
|
||||||
42
testing/xpcshell/odoh-wasm/Cargo.toml
Normal file
42
testing/xpcshell/odoh-wasm/Cargo.toml
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
[package]
|
||||||
|
name = "odoh-wasm"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Kershaw Chang <kershaw@mozilla.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["console_error_panic_hook"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
wasm-bindgen = "0.2.63"
|
||||||
|
odoh-rs = "=0.1.10"
|
||||||
|
hpke = "=0.5.0"
|
||||||
|
js-sys = "0.3"
|
||||||
|
hex = "0.4"
|
||||||
|
futures = "0.3.1"
|
||||||
|
rand = "=0.7"
|
||||||
|
|
||||||
|
# The `console_error_panic_hook` crate provides better debugging of panics by
|
||||||
|
# logging them with `console.error`. This is great for development, but requires
|
||||||
|
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
|
||||||
|
# code size when deploying.
|
||||||
|
console_error_panic_hook = { version = "0.1.6", optional = true }
|
||||||
|
|
||||||
|
# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
|
||||||
|
# compared to the default allocator's ~10K. It is slower than the default
|
||||||
|
# allocator, however.
|
||||||
|
#
|
||||||
|
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
|
||||||
|
wee_alloc = { version = "0.4.5", optional = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
wasm-bindgen-test = "0.3.13"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
# Tell `rustc` to optimize for small code size.
|
||||||
|
opt-level = "s"
|
||||||
176
testing/xpcshell/odoh-wasm/LICENSE_APACHE
Normal file
176
testing/xpcshell/odoh-wasm/LICENSE_APACHE
Normal file
|
|
@ -0,0 +1,176 @@
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
25
testing/xpcshell/odoh-wasm/LICENSE_MIT
Normal file
25
testing/xpcshell/odoh-wasm/LICENSE_MIT
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
Copyright (c) 2018 Kershaw Chang <kershaw@mozilla.com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any
|
||||||
|
person obtaining a copy of this software and associated
|
||||||
|
documentation files (the "Software"), to deal in the
|
||||||
|
Software without restriction, including without
|
||||||
|
limitation the rights to use, copy, modify, merge,
|
||||||
|
publish, distribute, sublicense, and/or sell copies of
|
||||||
|
the Software, and to permit persons to whom the Software
|
||||||
|
is furnished to do so, subject to the following
|
||||||
|
conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice
|
||||||
|
shall be included in all copies or substantial portions
|
||||||
|
of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||||
|
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||||
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||||
|
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||||
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||||
|
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||||
|
DEALINGS IN THE SOFTWARE.
|
||||||
75
testing/xpcshell/odoh-wasm/README.md
Normal file
75
testing/xpcshell/odoh-wasm/README.md
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
<h1><code>wasm-pack-template</code></h1>
|
||||||
|
|
||||||
|
<strong>A template for kick starting a Rust and WebAssembly project using <a href="https://github.com/rustwasm/wasm-pack">wasm-pack</a>.</strong>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="https://travis-ci.org/rustwasm/wasm-pack-template"><img src="https://img.shields.io/travis/rustwasm/wasm-pack-template.svg?style=flat-square" alt="Build Status" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
<a href="https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html">Tutorial</a>
|
||||||
|
<span> | </span>
|
||||||
|
<a href="https://discordapp.com/channels/442252698964721669/443151097398296587">Chat</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<sub>Built with 🦀🕸 by <a href="https://rustwasm.github.io/">The Rust and WebAssembly Working Group</a></sub>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
[**📚 Read this template tutorial! 📚**][template-docs]
|
||||||
|
|
||||||
|
This template is designed for compiling Rust libraries into WebAssembly and
|
||||||
|
publishing the resulting package to NPM.
|
||||||
|
|
||||||
|
Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other
|
||||||
|
templates and usages of `wasm-pack`.
|
||||||
|
|
||||||
|
[tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html
|
||||||
|
[template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html
|
||||||
|
|
||||||
|
## 🚴 Usage
|
||||||
|
|
||||||
|
### 🐑 Use `cargo generate` to Clone this Template
|
||||||
|
|
||||||
|
[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate)
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project
|
||||||
|
cd my-project
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🛠️ Build with `wasm-pack build`
|
||||||
|
|
||||||
|
```
|
||||||
|
wasm-pack build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🛠️ Build a module that can be used for nodejs
|
||||||
|
|
||||||
|
```
|
||||||
|
wasm-pack build --target nodejs
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔬 Test in Headless Browsers with `wasm-pack test`
|
||||||
|
|
||||||
|
```
|
||||||
|
wasm-pack test --headless --firefox
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🎁 Publish to NPM with `wasm-pack publish`
|
||||||
|
|
||||||
|
```
|
||||||
|
wasm-pack publish
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔋 Batteries Included
|
||||||
|
|
||||||
|
* [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating
|
||||||
|
between WebAssembly and JavaScript.
|
||||||
|
* [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook)
|
||||||
|
for logging panic messages to the developer console.
|
||||||
|
* [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized
|
||||||
|
for small code size.
|
||||||
69
testing/xpcshell/odoh-wasm/pkg/README.md
Normal file
69
testing/xpcshell/odoh-wasm/pkg/README.md
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
<h1><code>wasm-pack-template</code></h1>
|
||||||
|
|
||||||
|
<strong>A template for kick starting a Rust and WebAssembly project using <a href="https://github.com/rustwasm/wasm-pack">wasm-pack</a>.</strong>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<a href="https://travis-ci.org/rustwasm/wasm-pack-template"><img src="https://img.shields.io/travis/rustwasm/wasm-pack-template.svg?style=flat-square" alt="Build Status" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<h3>
|
||||||
|
<a href="https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html">Tutorial</a>
|
||||||
|
<span> | </span>
|
||||||
|
<a href="https://discordapp.com/channels/442252698964721669/443151097398296587">Chat</a>
|
||||||
|
</h3>
|
||||||
|
|
||||||
|
<sub>Built with 🦀🕸 by <a href="https://rustwasm.github.io/">The Rust and WebAssembly Working Group</a></sub>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
[**📚 Read this template tutorial! 📚**][template-docs]
|
||||||
|
|
||||||
|
This template is designed for compiling Rust libraries into WebAssembly and
|
||||||
|
publishing the resulting package to NPM.
|
||||||
|
|
||||||
|
Be sure to check out [other `wasm-pack` tutorials online][tutorials] for other
|
||||||
|
templates and usages of `wasm-pack`.
|
||||||
|
|
||||||
|
[tutorials]: https://rustwasm.github.io/docs/wasm-pack/tutorials/index.html
|
||||||
|
[template-docs]: https://rustwasm.github.io/docs/wasm-pack/tutorials/npm-browser-packages/index.html
|
||||||
|
|
||||||
|
## 🚴 Usage
|
||||||
|
|
||||||
|
### 🐑 Use `cargo generate` to Clone this Template
|
||||||
|
|
||||||
|
[Learn more about `cargo generate` here.](https://github.com/ashleygwilliams/cargo-generate)
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo generate --git https://github.com/rustwasm/wasm-pack-template.git --name my-project
|
||||||
|
cd my-project
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🛠️ Build with `wasm-pack build`
|
||||||
|
|
||||||
|
```
|
||||||
|
wasm-pack build
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🔬 Test in Headless Browsers with `wasm-pack test`
|
||||||
|
|
||||||
|
```
|
||||||
|
wasm-pack test --headless --firefox
|
||||||
|
```
|
||||||
|
|
||||||
|
### 🎁 Publish to NPM with `wasm-pack publish`
|
||||||
|
|
||||||
|
```
|
||||||
|
wasm-pack publish
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔋 Batteries Included
|
||||||
|
|
||||||
|
* [`wasm-bindgen`](https://github.com/rustwasm/wasm-bindgen) for communicating
|
||||||
|
between WebAssembly and JavaScript.
|
||||||
|
* [`console_error_panic_hook`](https://github.com/rustwasm/console_error_panic_hook)
|
||||||
|
for logging panic messages to the developer console.
|
||||||
|
* [`wee_alloc`](https://github.com/rustwasm/wee_alloc), an allocator optimized
|
||||||
|
for small code size.
|
||||||
16
testing/xpcshell/odoh-wasm/pkg/odoh_wasm.d.ts
vendored
Normal file
16
testing/xpcshell/odoh-wasm/pkg/odoh_wasm.d.ts
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* @returns {Uint8Array}
|
||||||
|
*/
|
||||||
|
export function get_odoh_config(): Uint8Array;
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} odoh_encrypted_query_msg
|
||||||
|
* @returns {Uint8Array}
|
||||||
|
*/
|
||||||
|
export function decrypt_query(odoh_encrypted_query_msg: Uint8Array): Uint8Array;
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} response
|
||||||
|
* @returns {Uint8Array}
|
||||||
|
*/
|
||||||
|
export function create_response(response: Uint8Array): Uint8Array;
|
||||||
132
testing/xpcshell/odoh-wasm/pkg/odoh_wasm.js
Normal file
132
testing/xpcshell/odoh-wasm/pkg/odoh_wasm.js
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
let imports = {};
|
||||||
|
imports['__wbindgen_placeholder__'] = module.exports;
|
||||||
|
let wasm;
|
||||||
|
const { TextDecoder } = require(`util`);
|
||||||
|
|
||||||
|
const heap = new Array(32).fill(undefined);
|
||||||
|
|
||||||
|
heap.push(undefined, null, true, false);
|
||||||
|
|
||||||
|
function getObject(idx) { return heap[idx]; }
|
||||||
|
|
||||||
|
let heap_next = heap.length;
|
||||||
|
|
||||||
|
function dropObject(idx) {
|
||||||
|
if (idx < 36) return;
|
||||||
|
heap[idx] = heap_next;
|
||||||
|
heap_next = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
function takeObject(idx) {
|
||||||
|
const ret = getObject(idx);
|
||||||
|
dropObject(idx);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
||||||
|
|
||||||
|
cachedTextDecoder.decode();
|
||||||
|
|
||||||
|
let cachegetUint8Memory0 = null;
|
||||||
|
function getUint8Memory0() {
|
||||||
|
if (cachegetUint8Memory0 === null || cachegetUint8Memory0.buffer !== wasm.memory.buffer) {
|
||||||
|
cachegetUint8Memory0 = new Uint8Array(wasm.memory.buffer);
|
||||||
|
}
|
||||||
|
return cachegetUint8Memory0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStringFromWasm0(ptr, len) {
|
||||||
|
return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr, ptr + len));
|
||||||
|
}
|
||||||
|
|
||||||
|
function addHeapObject(obj) {
|
||||||
|
if (heap_next === heap.length) heap.push(heap.length + 1);
|
||||||
|
const idx = heap_next;
|
||||||
|
heap_next = heap[idx];
|
||||||
|
|
||||||
|
heap[idx] = obj;
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @returns {Uint8Array}
|
||||||
|
*/
|
||||||
|
module.exports.get_odoh_config = function() {
|
||||||
|
var ret = wasm.get_odoh_config();
|
||||||
|
return takeObject(ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
let WASM_VECTOR_LEN = 0;
|
||||||
|
|
||||||
|
function passArray8ToWasm0(arg, malloc) {
|
||||||
|
const ptr = malloc(arg.length * 1);
|
||||||
|
getUint8Memory0().set(arg, ptr / 1);
|
||||||
|
WASM_VECTOR_LEN = arg.length;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} odoh_encrypted_query_msg
|
||||||
|
* @returns {Uint8Array}
|
||||||
|
*/
|
||||||
|
module.exports.decrypt_query = function(odoh_encrypted_query_msg) {
|
||||||
|
var ptr0 = passArray8ToWasm0(odoh_encrypted_query_msg, wasm.__wbindgen_malloc);
|
||||||
|
var len0 = WASM_VECTOR_LEN;
|
||||||
|
var ret = wasm.decrypt_query(ptr0, len0);
|
||||||
|
return takeObject(ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} response
|
||||||
|
* @returns {Uint8Array}
|
||||||
|
*/
|
||||||
|
module.exports.create_response = function(response) {
|
||||||
|
var ptr0 = passArray8ToWasm0(response, wasm.__wbindgen_malloc);
|
||||||
|
var len0 = WASM_VECTOR_LEN;
|
||||||
|
var ret = wasm.create_response(ptr0, len0);
|
||||||
|
return takeObject(ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.__wbindgen_object_drop_ref = function(arg0) {
|
||||||
|
takeObject(arg0);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.__wbg_log_b3f203d9e6882397 = function(arg0, arg1) {
|
||||||
|
console.log(getStringFromWasm0(arg0, arg1));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.__wbg_buffer_eb2155f17856c20b = function(arg0) {
|
||||||
|
var ret = getObject(arg0).buffer;
|
||||||
|
return addHeapObject(ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.__wbg_newwithbyteoffsetandlength_7d07f77c6d0d8e26 = function(arg0, arg1, arg2) {
|
||||||
|
var ret = new Uint8Array(getObject(arg0), arg1 >>> 0, arg2 >>> 0);
|
||||||
|
return addHeapObject(ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.__wbg_new_ff8b26f7b2d7e2fb = function(arg0) {
|
||||||
|
var ret = new Uint8Array(getObject(arg0));
|
||||||
|
return addHeapObject(ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.__wbg_newwithlength_a49b32b2030b93c3 = function(arg0) {
|
||||||
|
var ret = new Uint8Array(arg0 >>> 0);
|
||||||
|
return addHeapObject(ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.__wbindgen_throw = function(arg0, arg1) {
|
||||||
|
throw new Error(getStringFromWasm0(arg0, arg1));
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.__wbindgen_memory = function() {
|
||||||
|
var ret = wasm.memory;
|
||||||
|
return addHeapObject(ret);
|
||||||
|
};
|
||||||
|
|
||||||
|
const path = require('path').join(__dirname, 'odoh_wasm_bg.wasm');
|
||||||
|
const bytes = require('fs').readFileSync(path);
|
||||||
|
|
||||||
|
const wasmModule = new WebAssembly.Module(bytes);
|
||||||
|
const wasmInstance = new WebAssembly.Instance(wasmModule, imports);
|
||||||
|
wasm = wasmInstance.exports;
|
||||||
|
module.exports.__wasm = wasm;
|
||||||
|
|
||||||
BIN
testing/xpcshell/odoh-wasm/pkg/odoh_wasm_bg.wasm
Normal file
BIN
testing/xpcshell/odoh-wasm/pkg/odoh_wasm_bg.wasm
Normal file
Binary file not shown.
7
testing/xpcshell/odoh-wasm/pkg/odoh_wasm_bg.wasm.d.ts
vendored
Normal file
7
testing/xpcshell/odoh-wasm/pkg/odoh_wasm_bg.wasm.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
/* tslint:disable */
|
||||||
|
/* eslint-disable */
|
||||||
|
export const memory: WebAssembly.Memory;
|
||||||
|
export function get_odoh_config(): number;
|
||||||
|
export function decrypt_query(a: number, b: number): number;
|
||||||
|
export function create_response(a: number, b: number): number;
|
||||||
|
export function __wbindgen_malloc(a: number): number;
|
||||||
15
testing/xpcshell/odoh-wasm/pkg/package.json
Normal file
15
testing/xpcshell/odoh-wasm/pkg/package.json
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "odoh-wasm",
|
||||||
|
"collaborators": [
|
||||||
|
"Kershaw Chang <kershaw@mozilla.com>"
|
||||||
|
],
|
||||||
|
"version": "0.1.0",
|
||||||
|
"files": [
|
||||||
|
"odoh_wasm_bg.wasm",
|
||||||
|
"odoh_wasm.js",
|
||||||
|
"odoh_wasm_bg.js",
|
||||||
|
"odoh_wasm.d.ts"
|
||||||
|
],
|
||||||
|
"main": "odoh_wasm.js",
|
||||||
|
"types": "odoh_wasm.d.ts"
|
||||||
|
}
|
||||||
158
testing/xpcshell/odoh-wasm/src/lib.rs
Normal file
158
testing/xpcshell/odoh-wasm/src/lib.rs
Normal file
|
|
@ -0,0 +1,158 @@
|
||||||
|
use hpke::{
|
||||||
|
kem::X25519HkdfSha256,
|
||||||
|
Kem as KemTrait, Serializable,
|
||||||
|
};
|
||||||
|
|
||||||
|
use odoh_rs::protocol::{
|
||||||
|
create_response_msg, parse_received_query,
|
||||||
|
ObliviousDoHConfigContents, ObliviousDoHKeyPair,
|
||||||
|
ObliviousDoHQueryBody,
|
||||||
|
};
|
||||||
|
|
||||||
|
use futures::executor;
|
||||||
|
use hex;
|
||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
pub type Kem = X25519HkdfSha256;
|
||||||
|
|
||||||
|
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
|
||||||
|
// allocator.
|
||||||
|
#[cfg(feature = "wee_alloc")]
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
|
||||||
|
|
||||||
|
pub const ODOH_VERSION: u16 = 0x0001;
|
||||||
|
const KEM_ID: u16 = 0x0020;
|
||||||
|
const KDF_ID: u16 = 0x0001;
|
||||||
|
const AEAD_ID: u16 = 0x0001;
|
||||||
|
|
||||||
|
// random bytes, should be 32 bytes for X25519 keys
|
||||||
|
pub const IKM: &str = "871389a8727130974e3eb3ee528d440a871389a8727130974e3eb3ee528d440a";
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
// Use `js_namespace` here to bind `console.log(..)` instead of just
|
||||||
|
// `log(..)`
|
||||||
|
#[wasm_bindgen(js_namespace = console)]
|
||||||
|
fn log(s: &str);
|
||||||
|
|
||||||
|
// The `console.log` is quite polymorphic, so we can bind it with multiple
|
||||||
|
// signatures. Note that we need to use `js_name` to ensure we always call
|
||||||
|
// `log` in JS.
|
||||||
|
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||||
|
fn log_u32(a: u32);
|
||||||
|
|
||||||
|
// Multiple arguments too!
|
||||||
|
#[wasm_bindgen(js_namespace = console, js_name = log)]
|
||||||
|
fn log_many(a: &str, b: &str);
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! console_log {
|
||||||
|
// Note that this is using the `log` function imported above during
|
||||||
|
// `bare_bones`
|
||||||
|
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_key_pair() -> ObliviousDoHKeyPair {
|
||||||
|
let ikm_bytes = hex::decode(IKM).unwrap();
|
||||||
|
let (secret_key, public_key) = Kem::derive_keypair(&ikm_bytes);
|
||||||
|
let public_key_bytes = public_key.to_bytes().to_vec();
|
||||||
|
let odoh_public_key = ObliviousDoHConfigContents {
|
||||||
|
kem_id: KEM_ID,
|
||||||
|
kdf_id: KDF_ID,
|
||||||
|
aead_id: AEAD_ID,
|
||||||
|
public_key: public_key_bytes,
|
||||||
|
};
|
||||||
|
ObliviousDoHKeyPair {
|
||||||
|
private_key: secret_key,
|
||||||
|
public_key: odoh_public_key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn get_odoh_config() -> js_sys::Uint8Array {
|
||||||
|
let key_pair = generate_key_pair();
|
||||||
|
let public_key_bytes = key_pair.public_key.public_key;
|
||||||
|
let length_bytes = (public_key_bytes.len() as u16).to_be_bytes();
|
||||||
|
let odoh_config_length = 12 + public_key_bytes.len();
|
||||||
|
let version = ODOH_VERSION;
|
||||||
|
let odoh_contents_length = 8 + public_key_bytes.len();
|
||||||
|
let kem_id = KEM_ID; // DHKEM(X25519, HKDF-SHA256)
|
||||||
|
let kdf_id = KDF_ID; // KDF(SHA-256)
|
||||||
|
let aead_id = AEAD_ID; // AEAD(AES-GCM-128)
|
||||||
|
let mut result = vec![];
|
||||||
|
result.extend(&((odoh_config_length as u16).to_be_bytes()));
|
||||||
|
result.extend(&((version as u16).to_be_bytes()));
|
||||||
|
result.extend(&((odoh_contents_length as u16).to_be_bytes()));
|
||||||
|
result.extend(&((kem_id as u16).to_be_bytes()));
|
||||||
|
result.extend(&((kdf_id as u16).to_be_bytes()));
|
||||||
|
result.extend(&((aead_id as u16).to_be_bytes()));
|
||||||
|
result.extend(&length_bytes);
|
||||||
|
result.extend(&public_key_bytes);
|
||||||
|
return js_sys::Uint8Array::from(&result[..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut QUERY_BODY: Option<ObliviousDoHQueryBody> = None;
|
||||||
|
static mut SERVER_SECRET: Option<Vec<u8>> = None;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn decrypt_query(
|
||||||
|
odoh_encrypted_query_msg: &[u8],
|
||||||
|
) -> js_sys::Uint8Array {
|
||||||
|
let mut result = vec![];
|
||||||
|
unsafe {
|
||||||
|
let key_pair = generate_key_pair();
|
||||||
|
let parsed_res =
|
||||||
|
executor::block_on(parse_received_query(&key_pair, &odoh_encrypted_query_msg));
|
||||||
|
let (parsed_query, secret) = match parsed_res {
|
||||||
|
Ok(t) => (t.0, t.1),
|
||||||
|
Err(_) => {
|
||||||
|
console_log!("parse_received_query failed!");
|
||||||
|
return js_sys::Uint8Array::new_with_length(0)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
result.extend(&parsed_query.dns_msg);
|
||||||
|
|
||||||
|
QUERY_BODY = Some(parsed_query);
|
||||||
|
SERVER_SECRET = Some(secret);
|
||||||
|
}
|
||||||
|
|
||||||
|
return js_sys::Uint8Array::from(&result[..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn create_response(
|
||||||
|
response: &[u8],
|
||||||
|
) -> js_sys::Uint8Array {
|
||||||
|
unsafe {
|
||||||
|
if let Some(body) = &QUERY_BODY {
|
||||||
|
if let Some(secret) = &SERVER_SECRET {
|
||||||
|
// random bytes
|
||||||
|
let nonce = vec![0x1b, 0xff, 0xfd, 0xff, 0x1a, 0xff, 0xff, 0xff,
|
||||||
|
0xff, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff, 0xe];
|
||||||
|
let result = executor::block_on(create_response_msg(
|
||||||
|
&secret,
|
||||||
|
&response,
|
||||||
|
None,
|
||||||
|
Some(nonce),
|
||||||
|
&body,
|
||||||
|
));
|
||||||
|
let generated_response = match result {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_) => {
|
||||||
|
console_log!("create_response_msg failed!");
|
||||||
|
return js_sys::Uint8Array::new_with_length(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
QUERY_BODY = None;
|
||||||
|
SERVER_SECRET = None;
|
||||||
|
return js_sys::Uint8Array::from(&generated_response[..]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console_log!("create_response_msg failed!");
|
||||||
|
return js_sys::Uint8Array::new_with_length(0);
|
||||||
|
}
|
||||||
|
|
@ -4174,6 +4174,15 @@
|
||||||
"bug_numbers": [1682552],
|
"bug_numbers": [1682552],
|
||||||
"alert_emails": ["necko@mozilla.com", "kershaw@mozilla.com"]
|
"alert_emails": ["necko@mozilla.com", "kershaw@mozilla.com"]
|
||||||
},
|
},
|
||||||
|
"HTTP_CHANNEL_ONSTART_SUCCESS_ODOH": {
|
||||||
|
"record_in_processes": ["main"],
|
||||||
|
"products": ["firefox"],
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "boolean",
|
||||||
|
"description": "Successfully started HTTP channels when ODoH is enabled",
|
||||||
|
"bug_numbers": [1689987],
|
||||||
|
"alert_emails": ["necko@mozilla.com", "kershaw@mozilla.com"]
|
||||||
|
},
|
||||||
"HTTP_CONNECTION_ENTRY_CACHE_HIT_1": {
|
"HTTP_CONNECTION_ENTRY_CACHE_HIT_1": {
|
||||||
"record_in_processes": ["main", "socket"],
|
"record_in_processes": ["main", "socket"],
|
||||||
"products": ["firefox", "fennec"],
|
"products": ["firefox", "fennec"],
|
||||||
|
|
@ -17894,6 +17903,50 @@
|
||||||
"kind": "boolean",
|
"kind": "boolean",
|
||||||
"description": "Whether an HTTP request gets upgraded to HTTPS because HTTPS RR is presented"
|
"description": "Whether an HTTP request gets upgraded to HTTPS because HTTPS RR is presented"
|
||||||
},
|
},
|
||||||
|
"DNS_ODOH_LOOKUP_TIME": {
|
||||||
|
"record_in_processes": ["main", "socket"],
|
||||||
|
"products": ["firefox"],
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "exponential",
|
||||||
|
"high": 60000,
|
||||||
|
"alert_emails": ["necko@mozilla.com", "kershaw@mozilla.com"],
|
||||||
|
"bug_numbers": [1689987],
|
||||||
|
"n_buckets": 50,
|
||||||
|
"description": "Time for a completed ODoH resolution (msec)"
|
||||||
|
},
|
||||||
|
"ODOH_SKIP_REASON_ODOH_FIRST": {
|
||||||
|
"record_in_processes": ["main", "socket"],
|
||||||
|
"products": ["firefox"],
|
||||||
|
"alert_emails": ["necko@mozilla.com", "kershaw@mozilla.com"],
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "enumerated",
|
||||||
|
"n_values": 50,
|
||||||
|
"bug_numbers": [1689987],
|
||||||
|
"releaseChannelCollection": "opt-out",
|
||||||
|
"description": "When in ODoH-first mode, it lists the reason we may have skipped ODoH"
|
||||||
|
},
|
||||||
|
"HTTP_PAGE_DNS_ODOH_LOOKUP_TIME": {
|
||||||
|
"record_in_processes": ["main", "content"],
|
||||||
|
"products": ["firefox"],
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"alert_emails": ["necko@mozilla.com", "kershaw@mozilla.com"],
|
||||||
|
"kind": "exponential",
|
||||||
|
"high": 30000,
|
||||||
|
"n_buckets": 50,
|
||||||
|
"description": "HTTP page channel: ODoH lookup time (ms)",
|
||||||
|
"bug_numbers": [1689987]
|
||||||
|
},
|
||||||
|
"HTTP_SUB_DNS_ODOH_LOOKUP_TIME": {
|
||||||
|
"record_in_processes": ["main", "content"],
|
||||||
|
"products": ["firefox"],
|
||||||
|
"expires_in_version": "never",
|
||||||
|
"kind": "exponential",
|
||||||
|
"high": 30000,
|
||||||
|
"n_buckets": 50,
|
||||||
|
"bug_numbers": [1689987],
|
||||||
|
"alert_emails": ["necko@mozilla.com", "kershaw@mozilla.com"],
|
||||||
|
"description": "HTTP subitem channel: ODoH lookup time (ms)"
|
||||||
|
},
|
||||||
"HTTP_PRELOAD_IMAGE_STARTREQUEST_DELAY": {
|
"HTTP_PRELOAD_IMAGE_STARTREQUEST_DELAY": {
|
||||||
"record_in_processes": ["content"],
|
"record_in_processes": ["content"],
|
||||||
"products": ["firefox"],
|
"products": ["firefox"],
|
||||||
|
|
|
||||||
|
|
@ -171,6 +171,7 @@ testing/xpcshell/dns-packet/
|
||||||
testing/xpcshell/node_ip/
|
testing/xpcshell/node_ip/
|
||||||
testing/xpcshell/node-http2/
|
testing/xpcshell/node-http2/
|
||||||
testing/xpcshell/node-ws/
|
testing/xpcshell/node-ws/
|
||||||
|
testing/xpcshell/odoh-wasm/
|
||||||
third_party/
|
third_party/
|
||||||
toolkit/components/certviewer/content/vendor/
|
toolkit/components/certviewer/content/vendor/
|
||||||
toolkit/components/jsoncpp/
|
toolkit/components/jsoncpp/
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue