Bug 1815741 - implement DNS-over-Oblivious-HTTP r=valentin,necko-reviewers

Differential Revision: https://phabricator.services.mozilla.com/D169218
This commit is contained in:
Dana Keeler 2023-02-23 02:51:18 +00:00
Родитель 47428da2d3
Коммит 66f11ff04c
14 изменённых файлов: 620 добавлений и 53 удалений

Просмотреть файл

@ -11967,6 +11967,24 @@
value: "canary"
mirror: never
# Use Oblivious HTTP when making TRR requests.
- name: network.trr.use_ohttp
type: RelaxedAtomicBool
value: false
mirror: always
# Oblivious HTTP relay URI for TRR requests.
- name: network.trr.ohttp.relay_uri
type: String
value: ""
mirror: never
# URI from which to fetch the configuration for the Oblivious HTTP gateway for TRR requests.
- name: network.trr.ohttp.config_uri
type: String
value: ""
mirror: never
# Allow the network changed event to get sent when a network topology or setup
# change is noticed while running.
- name: network.notify.changed

Просмотреть файл

@ -269,7 +269,7 @@ nsresult ODoHService::UpdateODoHConfigFromURI() {
return rv;
}
channel->SetLoadFlags(
rv = channel->SetLoadFlags(
nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER);
NS_ENSURE_SUCCESS(rv, rv);

Просмотреть файл

@ -14,6 +14,7 @@
#include "nsIHttpChannelInternal.h"
#include "nsIIOService.h"
#include "nsIInputStream.h"
#include "nsIObliviousHttp.h"
#include "nsISupports.h"
#include "nsISupportsUtils.h"
#include "nsITimedChannel.h"
@ -25,6 +26,7 @@
#include "nsThreadUtils.h"
#include "nsURLHelper.h"
#include "ODoH.h"
#include "ObliviousHttpChannel.h"
#include "TRR.h"
#include "TRRService.h"
#include "TRRServiceChannel.h"
@ -261,7 +263,27 @@ nsresult TRR::SendHTTPRequest() {
}
nsCOMPtr<nsIChannel> channel;
rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel));
bool useOHTTP = StaticPrefs::network_trr_use_ohttp();
if (useOHTTP) {
nsCOMPtr<nsIObliviousHttpService> ohttpService(
do_GetService("@mozilla.org/network/oblivious-http-service;1"));
if (!ohttpService) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIURI> relayURI;
nsTArray<uint8_t> encodedConfig;
rv = ohttpService->GetTRRSettings(getter_AddRefs(relayURI), encodedConfig);
if (NS_FAILED(rv)) {
return rv;
}
if (!relayURI) {
return NS_ERROR_FAILURE;
}
rv = ohttpService->NewChannel(relayURI, dnsURI, encodedConfig,
getter_AddRefs(channel));
} else {
rv = DNSUtils::CreateChannelHelper(dnsURI, getter_AddRefs(channel));
}
if (NS_FAILED(rv) || !channel) {
LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
return rv;
@ -1040,8 +1062,17 @@ TRR::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aInputStream,
}
void TRR::Cancel(nsresult aStatus) {
RefPtr<TRRServiceChannel> trrServiceChannel = do_QueryObject(mChannel);
if (trrServiceChannel && !XRE_IsSocketProcess()) {
bool isTRRServiceChannel = false;
nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal(
do_QueryInterface(mChannel));
if (httpChannelInternal) {
nsresult rv =
httpChannelInternal->GetIsTRRServiceChannel(&isTRRServiceChannel);
if (NS_FAILED(rv)) {
isTRRServiceChannel = false;
}
}
if (isTRRServiceChannel && !XRE_IsSocketProcess()) {
if (TRRService::Get()) {
nsCOMPtr<nsIThread> thread = TRRService::Get()->TRRThread();
if (thread && !thread->IsOnCurrentThread()) {

Просмотреть файл

@ -21,6 +21,7 @@
#include "nsDNSPrefetch.h"
#include "nsThreadUtils.h"
#include "nsIProtocolProxyService.h"
#include "nsIObliviousHttp.h"
#include "prsystem.h"
#include "prnetdb.h"
#include "prmon.h"
@ -879,6 +880,9 @@ nsDNSService::Init() {
RegisterWeakMemoryReporter(this);
nsCOMPtr<nsIObliviousHttpService> ohttpService(
do_GetService("@mozilla.org/network/oblivious-http-service;1"));
mTrrService = new TRRService();
if (NS_FAILED(mTrrService->Init())) {
mTrrService = nullptr;

Просмотреть файл

@ -16,18 +16,23 @@
namespace mozilla::net {
NS_IMPL_ISUPPORTS(ObliviousHttpChannel, nsIHttpChannel, nsIIdentChannel,
nsIChannel, nsIRequest, nsIRequestObserver, nsIStreamListener,
nsIUploadChannel2)
NS_IMPL_ISUPPORTS(ObliviousHttpChannel, nsIChannel, nsIHttpChannel,
nsIHttpChannelInternal, nsIIdentChannel, nsIRequest,
nsIRequestObserver, nsIStreamListener, nsIUploadChannel2,
nsITimedChannel)
ObliviousHttpChannel::ObliviousHttpChannel(
nsIURI* targetURI, const nsTArray<uint8_t>& encodedConfig,
nsIHttpChannel* innerChannel)
: mTargetURI(targetURI),
mEncodedConfig(encodedConfig.Clone()),
mInnerChannel(innerChannel) {
mInnerChannel(innerChannel),
mInnerChannelInternal(do_QueryInterface(innerChannel)),
mInnerChannelTimed(do_QueryInterface(innerChannel)) {
LOG(("ObliviousHttpChannel ctor [this=%p]", this));
MOZ_ASSERT(mInnerChannel);
MOZ_ASSERT(mInnerChannelInternal);
MOZ_ASSERT(mInnerChannelTimed);
}
ObliviousHttpChannel::~ObliviousHttpChannel() {
@ -60,17 +65,17 @@ ObliviousHttpChannel::SetTopBrowsingContextId(uint64_t aId) {
NS_IMETHODIMP
ObliviousHttpChannel::GetTransferSize(uint64_t* aTransferSize) {
return NS_ERROR_NOT_IMPLEMENTED;
return mInnerChannel->GetTransferSize(aTransferSize);
}
NS_IMETHODIMP
ObliviousHttpChannel::GetRequestSize(uint64_t* aRequestSize) {
return NS_ERROR_NOT_IMPLEMENTED;
return mInnerChannel->GetRequestSize(aRequestSize);
}
NS_IMETHODIMP
ObliviousHttpChannel::GetDecodedBodySize(uint64_t* aDecodedBodySize) {
return NS_ERROR_NOT_IMPLEMENTED;
return mInnerChannel->GetDecodedBodySize(aDecodedBodySize);
}
NS_IMETHODIMP
@ -198,6 +203,8 @@ ObliviousHttpChannel::GetResponseStatus(uint32_t* aResponseStatus) {
NS_IMETHODIMP
ObliviousHttpChannel::GetResponseStatusText(nsACString& aResponseStatusText) {
LOG(("ObliviousHttpChannel::GetResponseStatusText NOT IMPLEMENTED [this=%p]",
this));
return NS_ERROR_NOT_IMPLEMENTED;
}
@ -252,6 +259,8 @@ ObliviousHttpChannel::GetResponseHeader(const nsACString& header,
NS_IMETHODIMP
ObliviousHttpChannel::SetResponseHeader(const nsACString& header,
const nsACString& value, bool merge) {
LOG(("ObliviousHttpChannel::SetResponseHeader NOT IMPLEMENTED [this=%p]",
this));
return NS_ERROR_NOT_IMPLEMENTED;
}
@ -290,37 +299,56 @@ ObliviousHttpChannel::GetOriginalResponseHeader(
NS_IMETHODIMP
ObliviousHttpChannel::VisitOriginalResponseHeaders(
nsIHttpHeaderVisitor* aVisitor) {
LOG(
("ObliviousHttpChannel::VisitOriginalResponseHeaders NOT IMPLEMENTED "
"[this=%p]",
this));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ObliviousHttpChannel::ShouldStripRequestBodyHeader(const nsACString& aMethod,
bool* aResult) {
LOG(
("ObliviousHttpChannel::ShouldStripRequestBodyHeader NOT IMPLEMENTED "
"[this=%p]",
this));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ObliviousHttpChannel::IsNoStoreResponse(bool* _retval) {
LOG(("ObliviousHttpChannel::IsNoStoreResponse NOT IMPLEMENTED [this=%p]",
this));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ObliviousHttpChannel::IsNoCacheResponse(bool* _retval) {
LOG(("ObliviousHttpChannel::IsNoCacheResponse NOT IMPLEMENTED [this=%p]",
this));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ObliviousHttpChannel::IsPrivateResponse(bool* _retval) {
LOG(("ObliviousHttpChannel::IsPrivateResponse NOT IMPLEMENTED [this=%p]",
this));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ObliviousHttpChannel::RedirectTo(nsIURI* aNewURI) {
LOG(("ObliviousHttpChannel::RedirectTo NOT IMPLEMENTED [this=%p]", this));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ObliviousHttpChannel::UpgradeToSecure() { return NS_ERROR_NOT_IMPLEMENTED; }
ObliviousHttpChannel::UpgradeToSecure() {
LOG(("ObliviousHttpChannel::UpgradeToSecure NOT IMPLEMENTED [this=%p]",
this));
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
ObliviousHttpChannel::GetRequestContextID(uint64_t* _retval) {
@ -339,6 +367,8 @@ ObliviousHttpChannel::GetProtocolVersion(nsACString& aProtocolVersion) {
NS_IMETHODIMP
ObliviousHttpChannel::GetEncodedBodySize(uint64_t* aEncodedBodySize) {
LOG(("ObliviousHttpChannel::GetEncodedBodySize NOT IMPLEMENTED [this=%p]",
this));
return NS_ERROR_NOT_IMPLEMENTED;
}
@ -358,9 +388,55 @@ ObliviousHttpChannel::LogMimeTypeMismatch(const nsACString& aMessageName,
void ObliviousHttpChannel::SetSource(
mozilla::UniquePtr<mozilla::ProfileChunkedBuffer> aSource) {
LOG(("ObliviousHttpChannel::SetSource NOT IMPLEMENTED [this=%p]", this));
// NS_ERROR_NOT_IMPLEMENTED
}
void ObliviousHttpChannel::SetConnectionInfo(nsHttpConnectionInfo* aCi) {
if (mInnerChannelInternal) {
mInnerChannelInternal->SetConnectionInfo(aCi);
}
}
void ObliviousHttpChannel::DoDiagnosticAssertWhenOnStopNotCalledOnDestroy() {
if (mInnerChannelInternal) {
mInnerChannelInternal->DoDiagnosticAssertWhenOnStopNotCalledOnDestroy();
}
}
void ObliviousHttpChannel::SetIPv6Disabled() {
if (mInnerChannelInternal) {
mInnerChannelInternal->SetIPv6Disabled();
}
}
void ObliviousHttpChannel::SetIPv4Disabled() {
if (mInnerChannelInternal) {
mInnerChannelInternal->SetIPv4Disabled();
}
}
void ObliviousHttpChannel::DisableAltDataCache() {
if (mInnerChannelInternal) {
mInnerChannelInternal->DisableAltDataCache();
}
}
void ObliviousHttpChannel::SetAltDataForChild(bool aIsForChild) {
if (mInnerChannelInternal) {
mInnerChannelInternal->SetAltDataForChild(aIsForChild);
}
}
void ObliviousHttpChannel::SetCorsPreflightParameters(
nsTArray<nsTString<char>> const& aUnsafeHeaders,
bool aShouldStripRequestBodyHeader) {
if (mInnerChannelInternal) {
mInnerChannelInternal->SetCorsPreflightParameters(
aUnsafeHeaders, aShouldStripRequestBodyHeader);
}
}
//-----------------------------------------------------------------------------
// ObliviousHttpChannel::nsIChannel
//-----------------------------------------------------------------------------
@ -411,7 +487,7 @@ ObliviousHttpChannel::GetSecurityInfo(
NS_IMETHODIMP
ObliviousHttpChannel::GetContentType(nsACString& aContentType) {
return mInnerChannel->GetContentType(aContentType);
return GetResponseHeader("content-type"_ns, aContentType);
}
NS_IMETHODIMP
@ -441,6 +517,7 @@ ObliviousHttpChannel::SetContentLength(int64_t aContentLength) {
NS_IMETHODIMP
ObliviousHttpChannel::Open(nsIInputStream** aStream) {
LOG(("ObliviousHttpChannel::Open NOT IMPLEMENTED [this=%p]", this));
return NS_ERROR_NOT_IMPLEMENTED;
}
@ -459,6 +536,9 @@ ObliviousHttpChannel::AsyncOpen(nsIStreamListener* aListener) {
return rv;
}
nsAutoCString scheme;
if (!mTargetURI) {
return NS_ERROR_NULL_POINTER;
}
rv = mTargetURI->GetScheme(scheme);
if (NS_FAILED(rv)) {
return rv;
@ -615,15 +695,13 @@ NS_IMETHODIMP
ObliviousHttpChannel::OnStartRequest(nsIRequest* aRequest) {
LOG(("ObliviousHttpChannel::OnStartRequest [this=%p, request=%p]", this,
aRequest));
return mStreamListener->OnStartRequest(aRequest);
return NS_OK;
}
NS_IMETHODIMP
ObliviousHttpChannel::OnStopRequest(nsIRequest* aRequest,
nsresult aStatusCode) {
LOG(("ObliviousHttpChannel::OnStopRequest [this=%p, request=%p, status=%u]",
this, aRequest, (uint32_t)aStatusCode));
nsresult ObliviousHttpChannel::ProcessOnStopRequest() {
if (mRawResponse.IsEmpty()) {
return NS_OK;
}
nsCOMPtr<nsIObliviousHttp> obliviousHttp(
do_GetService("@mozilla.org/network/oblivious-http;1"));
if (!obliviousHttp) {
@ -644,39 +722,57 @@ ObliviousHttpChannel::OnStopRequest(nsIRequest* aRequest,
if (!bhttp) {
return NS_ERROR_FAILURE;
}
rv = bhttp->DecodeResponse(decapsulated, getter_AddRefs(mBinaryHttpResponse));
if (NS_FAILED(rv)) {
return rv;
return bhttp->DecodeResponse(decapsulated,
getter_AddRefs(mBinaryHttpResponse));
}
void ObliviousHttpChannel::EmitOnDataAvailable() {
if (!mBinaryHttpResponse) {
return;
}
nsTArray<uint8_t> content;
rv = mBinaryHttpResponse->GetContent(content);
nsresult rv = mBinaryHttpResponse->GetContent(content);
if (NS_FAILED(rv)) {
return rv;
return;
}
if (content.IsEmpty()) {
return;
}
if (content.Length() > std::numeric_limits<uint32_t>::max()) {
return NS_ERROR_FAILURE;
return;
}
uint32_t contentLength = (uint32_t)content.Length();
if (contentLength > 0) {
nsCOMPtr<nsIInputStream> contentStream;
rv = NS_NewByteInputStream(getter_AddRefs(contentStream),
std::move(content));
if (NS_FAILED(rv)) {
return rv;
}
rv = mStreamListener->OnDataAvailable(aRequest, contentStream, 0,
contentLength);
if (NS_FAILED(rv)) {
return rv;
}
}
rv = mStreamListener->OnStopRequest(aRequest, aStatusCode);
nsCOMPtr<nsIInputStream> contentStream;
rv = NS_NewByteInputStream(getter_AddRefs(contentStream), std::move(content));
if (NS_FAILED(rv)) {
return rv;
return;
}
mInnerChannel = nullptr;
mEncapsulatedRequest = nullptr;
mStreamListener = nullptr;
rv = mStreamListener->OnDataAvailable(this, contentStream, 0, contentLength);
Unused << rv;
}
NS_IMETHODIMP
ObliviousHttpChannel::OnStopRequest(nsIRequest* aRequest,
nsresult aStatusCode) {
LOG(("ObliviousHttpChannel::OnStopRequest [this=%p, request=%p, status=%u]",
this, aRequest, (uint32_t)aStatusCode));
auto releaseStreamListener = MakeScopeExit(
[self = RefPtr{this}]() mutable { self->mStreamListener = nullptr; });
if (NS_SUCCEEDED(aStatusCode)) {
bool requestSucceeded;
nsresult rv = mInnerChannel->GetRequestSucceeded(&requestSucceeded);
if (NS_SUCCEEDED(rv) && requestSucceeded) {
aStatusCode = ProcessOnStopRequest();
}
}
Unused << mStreamListener->OnStartRequest(this);
if (NS_SUCCEEDED(aStatusCode)) {
EmitOnDataAvailable();
}
Unused << mStreamListener->OnStopRequest(this, aStatusCode);
return NS_OK;
}
@ -687,6 +783,10 @@ ObliviousHttpChannel::OnStopRequest(nsIRequest* aRequest,
NS_IMETHODIMP ObliviousHttpChannel::ExplicitSetUploadStream(
nsIInputStream* aStream, const nsACString& aContentType,
int64_t aContentLength, const nsACString& aMethod, bool aStreamHasHeaders) {
// This function should only be called before AsyncOpen.
if (mStreamListener) {
return NS_ERROR_IN_PROGRESS;
}
if (aMethod != "POST"_ns || aStreamHasHeaders) {
return NS_ERROR_INVALID_ARG;
}
@ -726,6 +826,8 @@ NS_IMETHODIMP ObliviousHttpChannel::GetUploadStreamHasHeaders(
NS_IMETHODIMP ObliviousHttpChannel::CloneUploadStream(
int64_t* aContentLength, nsIInputStream** _retval) {
LOG(("ObliviousHttpChannel::CloneUploadStream NOT IMPLEMENTED [this=%p]",
this));
return NS_ERROR_NOT_IMPLEMENTED;
}

Просмотреть файл

@ -10,16 +10,20 @@
#include "nsIBinaryHttp.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIObliviousHttp.h"
#include "nsIStreamListener.h"
#include "nsITimedChannel.h"
#include "nsIUploadChannel2.h"
#include "nsTHashMap.h"
namespace mozilla::net {
class ObliviousHttpChannel final : public nsIHttpChannel,
public nsIHttpChannelInternal,
public nsIStreamListener,
public nsIUploadChannel2 {
public nsIUploadChannel2,
public nsITimedChannel {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSICHANNEL
@ -32,12 +36,17 @@ class ObliviousHttpChannel final : public nsIHttpChannel,
const nsTArray<uint8_t>& encodedConfig,
nsIHttpChannel* innerChannel);
NS_FORWARD_NSIREQUEST(mInnerChannel->)
NS_FORWARD_NSIIDENTCHANNEL(mInnerChannel->)
NS_FORWARD_SAFE_NSIREQUEST(mInnerChannel)
NS_FORWARD_SAFE_NSIIDENTCHANNEL(mInnerChannel)
NS_FORWARD_SAFE_NSIHTTPCHANNELINTERNAL(mInnerChannelInternal)
NS_FORWARD_SAFE_NSITIMEDCHANNEL(mInnerChannelTimed)
protected:
~ObliviousHttpChannel();
nsresult ProcessOnStopRequest();
void EmitOnDataAvailable();
nsCOMPtr<nsIURI> mTargetURI;
nsTArray<uint8_t> mEncodedConfig;
@ -46,6 +55,8 @@ class ObliviousHttpChannel final : public nsIHttpChannel,
nsTArray<uint8_t> mContent;
nsCOMPtr<nsIHttpChannel> mInnerChannel;
nsCOMPtr<nsIHttpChannelInternal> mInnerChannelInternal;
nsCOMPtr<nsITimedChannel> mInnerChannelTimed;
nsCOMPtr<nsIObliviousHttpClientRequest> mEncapsulatedRequest;
nsTArray<uint8_t> mRawResponse;
nsCOMPtr<nsIBinaryHttpResponse> mBinaryHttpResponse;

Просмотреть файл

@ -9,10 +9,91 @@
#include "DNSUtils.h"
#include "ObliviousHttpChannel.h"
#include "mozilla/Base64.h"
#include "nsIObserverService.h"
#include "nsIPrefService.h"
#include "nsNetUtil.h"
namespace mozilla::net {
NS_IMPL_ISUPPORTS(ObliviousHttpService, nsIObliviousHttpService)
NS_IMPL_ISUPPORTS(ObliviousHttpService, nsIObliviousHttpService, nsIObserver,
nsIStreamLoaderObserver)
ObliviousHttpService::ObliviousHttpService()
: mTRRConfig(ObliviousHttpConfig(), "ObliviousHttpService::mTRRConfig") {
nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
if (prefBranch) {
prefBranch->AddObserver("network.trr.ohttp", this, false);
}
MOZ_ASSERT(NS_IsMainThread());
ReadPrefs("*"_ns);
}
void ObliviousHttpService::ReadPrefs(const nsACString& whichPref) {
nsAutoCString relayURIPref("network.trr.ohttp.relay_uri");
if (whichPref.Equals(relayURIPref) || whichPref.EqualsLiteral("*")) {
nsAutoCString relayURIString;
nsresult rv = Preferences::GetCString(relayURIPref.get(), relayURIString);
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIURI> relayURI;
rv = NS_NewURI(getter_AddRefs(relayURI), relayURIString);
if (NS_FAILED(rv)) {
return;
}
auto trrConfig = mTRRConfig.Lock();
trrConfig->mRelayURI = relayURI;
}
nsAutoCString configURIPref("network.trr.ohttp.config_uri");
if (whichPref.Equals(configURIPref) || whichPref.EqualsLiteral("*")) {
nsAutoCString configURIString;
nsresult rv = Preferences::GetCString(configURIPref.get(), configURIString);
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIURI> configURI;
rv = NS_NewURI(getter_AddRefs(configURI), configURIString);
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIChannel> channel;
rv = DNSUtils::CreateChannelHelper(configURI, getter_AddRefs(channel));
if (NS_FAILED(rv)) {
return;
}
rv = channel->SetLoadFlags(
nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER);
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
if (!httpChannel) {
return;
}
// This connection should not use TRR
rv = httpChannel->SetTRRMode(nsIRequest::TRR_DISABLED_MODE);
if (NS_FAILED(rv)) {
return;
}
nsCOMPtr<nsIStreamLoader> loader;
rv = NS_NewStreamLoader(getter_AddRefs(loader), this);
if (NS_FAILED(rv)) {
return;
}
rv = httpChannel->AsyncOpen(loader);
if (NS_FAILED(rv)) {
return;
}
auto trrConfig = mTRRConfig.Lock();
trrConfig->mEncodedConfig.Clear();
}
}
// nsIObliviousHttpService
NS_IMETHODIMP
ObliviousHttpService::NewChannel(nsIURI* relayURI, nsIURI* targetURI,
@ -34,4 +115,51 @@ ObliviousHttpService::NewChannel(nsIURI* relayURI, nsIURI* targetURI,
return NS_OK;
}
NS_IMETHODIMP
ObliviousHttpService::GetTRRSettings(nsIURI** relayURI,
nsTArray<uint8_t>& encodedConfig) {
auto trrConfig = mTRRConfig.Lock();
*relayURI = do_AddRef(trrConfig->mRelayURI).take();
encodedConfig.Assign(trrConfig->mEncodedConfig.Clone());
return NS_OK;
}
// nsIObserver
NS_IMETHODIMP
ObliviousHttpService::Observe(nsISupports* subject, const char* topic,
const char16_t* data) {
if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
ReadPrefs(NS_ConvertUTF16toUTF8(data));
}
return NS_OK;
}
// nsIStreamLoaderObserver
NS_IMETHODIMP
ObliviousHttpService::OnStreamComplete(nsIStreamLoader* aLoader,
nsISupports* aContext, nsresult aStatus,
uint32_t aLength,
const uint8_t* aContent) {
if (NS_SUCCEEDED(aStatus)) {
auto trrConfig = mTRRConfig.Lock();
trrConfig->mEncodedConfig.Clear();
trrConfig->mEncodedConfig.AppendElements(aContent, aLength);
}
nsCOMPtr<nsIObserverService> observerService(
mozilla::services::GetObserverService());
if (!observerService) {
return NS_ERROR_FAILURE;
}
nsresult rv = observerService->NotifyObservers(
nullptr, "ohttp-service-config-loaded", nullptr);
if (NS_FAILED(rv)) {
return rv;
}
return NS_OK;
}
} // namespace mozilla::net

Просмотреть файл

@ -8,17 +8,36 @@
#ifndef mozilla_net_ObliviousHttpService_h
#define mozilla_net_ObliviousHttpService_h
#include "mozilla/DataMutex.h"
#include "nsCOMPtr.h"
#include "nsIObliviousHttp.h"
#include "nsIObserver.h"
#include "nsIStreamLoader.h"
namespace mozilla::net {
class ObliviousHttpService final : public nsIObliviousHttpService {
class ObliviousHttpConfig {
public:
nsCOMPtr<nsIURI> mRelayURI;
nsTArray<uint8_t> mEncodedConfig;
};
class ObliviousHttpService final : public nsIObliviousHttpService,
nsIObserver,
nsIStreamLoaderObserver {
public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIOBLIVIOUSHTTPSERVICE
NS_DECL_NSIOBSERVER
NS_DECL_NSISTREAMLOADEROBSERVER
ObliviousHttpService();
private:
~ObliviousHttpService() = default;
void ReadPrefs(const nsACString& whichPref);
DataMutex<ObliviousHttpConfig> mTRRConfig;
};
} // namespace mozilla::net

Просмотреть файл

@ -83,6 +83,7 @@ NS_INTERFACE_MAP_BEGIN(TRRServiceChannel)
NS_INTERFACE_MAP_ENTRY(nsITransportEventSink)
NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIUploadChannel2)
NS_INTERFACE_MAP_ENTRY_CONCRETE(TRRServiceChannel)
NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)

Просмотреть файл

@ -70,4 +70,6 @@ interface nsIObliviousHttp : nsISupports
interface nsIObliviousHttpService : nsISupports
{
nsIChannel newChannel(in nsIURI relayURI, in nsIURI targetURI, in Array<octet> encodedConfig);
void getTRRSettings(out nsIURI relayURI, out Array<octet> encodedConfig);
};

Просмотреть файл

@ -473,6 +473,10 @@ BinaryHttpResponse.prototype = {
QueryInterface: ChromeUtils.generateQI(["nsIBinaryHttpResponse"]),
};
function bytesToString(bytes) {
return String.fromCharCode.apply(null, bytes);
}
function check_http_info(request, expected_httpVersion, expected_proxy) {
let httpVersion = "";
try {

Просмотреть файл

@ -0,0 +1,247 @@
/* Any copyright is dedicated to the Public Domain.
* https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from trr_common.js */
Cu.importGlobalProperties(["fetch"]);
const { setTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
let httpServer;
let ohttpServer;
let ohttpEncodedConfig = "not a valid config";
// Decapsulate the request, send it to the actual TRR, receive the response,
// encapsulate it, and send it back through `response`.
async function forwardToTRR(request, response) {
let inputStream = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(
Ci.nsIScriptableInputStream
);
inputStream.init(request.bodyInputStream);
let requestBody = inputStream.readBytes(inputStream.available());
let ohttpResponse = ohttpServer.decapsulate(stringToBytes(requestBody));
let bhttp = Cc["@mozilla.org/network/binary-http;1"].getService(
Ci.nsIBinaryHttp
);
let decodedRequest = bhttp.decodeRequest(ohttpResponse.request);
let headers = {};
for (
let i = 0;
i < decodedRequest.headerNames.length && decodedRequest.headerValues.length;
i++
) {
headers[decodedRequest.headerNames[i]] = decodedRequest.headerValues[i];
}
let uri = `${decodedRequest.scheme}://${decodedRequest.authority}${decodedRequest.path}`;
let body = new Uint8Array(decodedRequest.content.length);
for (let i = 0; i < decodedRequest.content.length; i++) {
body[i] = decodedRequest.content[i];
}
try {
// Timeout after 10 seconds.
let fetchInProgress = true;
let controller = new AbortController();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
if (fetchInProgress) {
controller.abort();
}
}, 10000);
let trrResponse = await fetch(uri, {
method: decodedRequest.method,
headers,
body: decodedRequest.method == "POST" ? body : undefined,
credentials: "omit",
signal: controller.signal,
});
fetchInProgress = false;
let data = new Uint8Array(await trrResponse.arrayBuffer());
let trrResponseContent = [];
for (let i = 0; i < data.length; i++) {
trrResponseContent.push(data[i]);
}
let trrResponseHeaderNames = [];
let trrResponseHeaderValues = [];
for (let header of trrResponse.headers) {
trrResponseHeaderNames.push(header[0]);
trrResponseHeaderValues.push(header[1]);
}
let binaryResponse = new BinaryHttpResponse(
trrResponse.status,
trrResponseHeaderNames,
trrResponseHeaderValues,
trrResponseContent
);
let responseBytes = bhttp.encodeResponse(binaryResponse);
let encResponse = ohttpResponse.encapsulate(responseBytes);
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "message/ohttp-res", false);
response.write(bytesToString(encResponse));
} catch (e) {
// Some tests involve the responder either timing out or closing the
// connection unexpectedly.
}
}
add_setup(async function setup() {
h2Port = trr_test_setup();
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);
let ohttp = Cc["@mozilla.org/network/oblivious-http;1"].getService(
Ci.nsIObliviousHttp
);
ohttpServer = ohttp.server();
httpServer = new HttpServer();
httpServer.registerPathHandler("/relay", function(request, response) {
response.processAsync();
forwardToTRR(request, response).then(() => {
response.finish();
});
});
httpServer.registerPathHandler("/config", function(request, response) {
response.setStatusLine(request.httpVersion, 200, "OK");
response.setHeader("Content-Type", "application/ohttp-keys", false);
response.write(ohttpEncodedConfig);
});
httpServer.start(-1);
Services.prefs.setBoolPref("network.trr.use_ohttp", true);
registerCleanupFunction(async () => {
trr_clear_prefs();
Services.prefs.clearUserPref("network.trr.use_ohttp");
Services.prefs.clearUserPref("network.trr.ohttp.config_uri");
Services.prefs.clearUserPref("network.trr.ohttp.relay_uri");
await new Promise((resolve, reject) => {
httpServer.stop(resolve);
});
});
});
// Test that if DNS-over-OHTTP isn't configured, the implementation falls back
// to platform resolution.
add_task(async function test_ohttp_not_configured() {
Services.dns.clearCache(true);
setModeAndURI(2, "doh?responseIP=2.2.2.2");
await new TRRDNSListener("example.com", "127.0.0.1");
});
add_task(async function set_ohttp_invalid_prefs() {
Services.prefs.setCharPref(
"network.trr.ohttp.relay_uri",
"http://nonexistent.test"
);
Services.prefs.setCharPref(
"network.trr.ohttp.config_uri",
"http://nonexistent.test"
);
Cc["@mozilla.org/network/oblivious-http-service;1"].getService(
Ci.nsIObliviousHttpService
);
await TestUtils.topicObserved("ohttp-service-config-loaded");
});
// Test that if DNS-over-OHTTP has an invalid configuration, the implementation
// falls back to platform resolution.
add_task(async function test_ohttp_invalid_prefs_fallback() {
Services.dns.clearCache(true);
setModeAndURI(2, "doh?responseIP=2.2.2.2");
await new TRRDNSListener("example.com", "127.0.0.1");
});
add_task(async function set_ohttp_prefs_500_error() {
Services.prefs.setCharPref(
"network.trr.ohttp.relay_uri",
`http://localhost:${httpServer.identity.primaryPort}/relay`
);
Services.prefs.setCharPref(
"network.trr.ohttp.config_uri",
`http://localhost:${httpServer.identity.primaryPort}/500error`
);
await TestUtils.topicObserved("ohttp-service-config-loaded");
});
// Test that if DNS-over-OHTTP has an invalid configuration, the implementation
// falls back to platform resolution.
add_task(async function test_ohttp_500_error_fallback() {
Services.dns.clearCache(true);
setModeAndURI(2, "doh?responseIP=2.2.2.2");
await new TRRDNSListener("example.com", "127.0.0.1");
});
add_task(async function set_ohttp_prefs_valid() {
ohttpEncodedConfig = bytesToString(ohttpServer.encodedConfig);
Services.prefs.setCharPref(
"network.trr.ohttp.config_uri",
`http://localhost:${httpServer.identity.primaryPort}/config`
);
await TestUtils.topicObserved("ohttp-service-config-loaded");
});
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);
// This test doesn't work with having a JS httpd server as a relay.
// 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);

Просмотреть файл

@ -5,10 +5,6 @@
const { HttpServer } = ChromeUtils.import("resource://testing-common/httpd.js");
function bytesToString(bytes) {
return String.fromCharCode.apply(null, bytes);
}
class ObliviousHttpTestRequest {
constructor(method, uri, headers, content) {
this.method = method;

Просмотреть файл

@ -178,6 +178,10 @@ skip-if = bits != 32
[test_udpsocket.js]
[test_udpsocket_offline.js]
[test_doomentry.js]
[test_dooh.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 = socketprocess_networking
[test_cacheflags.js]
skip-if =
os == "win" && os_version == "6.1" # Skip on Azure - frequent failure