Bug 1619584 - P2: Implement redirection r=valentin,dragana

Differential Revision: https://phabricator.services.mozilla.com/D66773

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Kershaw Chang 2020-03-17 15:51:01 +00:00
Родитель 4a22dca83d
Коммит 1a69c8800a
7 изменённых файлов: 264 добавлений и 28 удалений

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

@ -7,7 +7,6 @@
#include "DNS.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsContentUtils.h"
#include "nsHostResolver.h"
#include "nsHttpHandler.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
@ -24,6 +23,7 @@
#include "nsURLHelper.h"
#include "TRR.h"
#include "TRRService.h"
#include "TRRLoadInfo.h"
#include "mozilla/Base64.h"
#include "mozilla/DebugOnly.h"
@ -219,11 +219,13 @@ nsresult TRR::CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult) {
return NS_ERROR_UNEXPECTED;
}
RefPtr<TRRLoadInfo> loadInfo =
new TRRLoadInfo(aUri, nsIContentPolicy::TYPE_OTHER);
return gHttpHandler->CreateTRRServiceChannel(aUri,
nullptr, // givenProxyInfo
0, // proxyResolveFlags
nullptr, // proxyURI
nullptr, // aLoadInfo
nullptr, // givenProxyInfo
0, // proxyResolveFlags
nullptr, // proxyURI
loadInfo, // aLoadInfo
aResult);
}
@ -299,7 +301,7 @@ nsresult TRR::SendHTTPRequest() {
if (query.IsEmpty()) {
query.Assign(NS_LITERAL_CSTRING("?dns="));
} else {
} else {
query.Append(NS_LITERAL_CSTRING("&dns="));
}
query.Append(body);
@ -323,21 +325,22 @@ nsresult TRR::SendHTTPRequest() {
return rv;
}
rv = CreateChannelHelper(dnsURI, getter_AddRefs(mChannel));
if (NS_FAILED(rv)) {
nsCOMPtr<nsIChannel> channel;
rv = CreateChannelHelper(dnsURI, getter_AddRefs(channel));
if (NS_FAILED(rv) || !channel) {
LOG(("TRR:SendHTTPRequest: NewChannel failed!\n"));
return rv;
}
mChannel->SetLoadFlags(
channel->SetLoadFlags(
nsIRequest::LOAD_ANONYMOUS | nsIRequest::INHIBIT_CACHING |
nsIRequest::LOAD_BYPASS_CACHE | nsIChannel::LOAD_BYPASS_URL_CLASSIFIER);
NS_ENSURE_SUCCESS(rv, rv);
rv = mChannel->SetNotificationCallbacks(this);
rv = channel->SetNotificationCallbacks(this);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
if (!httpChannel) {
return NS_ERROR_UNEXPECTED;
}
@ -361,8 +364,7 @@ nsresult TRR::SendHTTPRequest() {
NS_ENSURE_SUCCESS(rv, rv);
}
nsCOMPtr<nsIHttpChannelInternal> internalChannel =
do_QueryInterface(mChannel);
nsCOMPtr<nsIHttpChannelInternal> internalChannel = do_QueryInterface(channel);
if (!internalChannel) {
return NS_ERROR_UNEXPECTED;
}
@ -380,9 +382,6 @@ nsresult TRR::SendHTTPRequest() {
rv = httpChannel->SetRequestMethod(NS_LITERAL_CSTRING("GET"));
NS_ENSURE_SUCCESS(rv, rv);
} else {
rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Cache-Control"),
NS_LITERAL_CSTRING("no-store"), false);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIUploadChannel2> uploadChannel = do_QueryInterface(httpChannel);
if (!uploadChannel) {
return NS_ERROR_UNEXPECTED;
@ -399,6 +398,37 @@ nsresult TRR::SendHTTPRequest() {
NS_ENSURE_SUCCESS(rv, rv);
}
rv = SetupTRRServiceChannelInternal(httpChannel, useGet);
if (NS_FAILED(rv)) {
return rv;
}
rv = httpChannel->AsyncOpen(this);
if (NS_FAILED(rv)) {
return rv;
}
NS_NewTimerWithCallback(getter_AddRefs(mTimeout), this,
gTRRService->GetRequestTimeout(),
nsITimer::TYPE_ONE_SHOT);
mChannel = channel;
return NS_OK;
}
// static
nsresult TRR::SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
bool aUseGet) {
nsCOMPtr<nsIHttpChannel> httpChannel = aChannel;
MOZ_ASSERT(httpChannel);
nsresult rv = NS_OK;
if (!aUseGet) {
rv = httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Cache-Control"),
NS_LITERAL_CSTRING("no-store"), false);
NS_ENSURE_SUCCESS(rv, rv);
}
// Sanitize the request by removing the Accept-Language header so we minimize
// the amount of fingerprintable information we send to the server.
if (!StaticPrefs::network_trr_send_accept_language_headers()) {
@ -423,20 +453,15 @@ nsresult TRR::SendHTTPRequest() {
// set the *default* response content type
if (NS_FAILED(httpChannel->SetContentType(
NS_LITERAL_CSTRING("application/dns-message")))) {
LOG(("TRR::SendHTTPRequest: couldn't set content-type!\n"));
LOG(("TRR::SetupTRRServiceChannelInternal: couldn't set content-type!\n"));
}
nsCOMPtr<nsITimedChannel> timedChan(do_QueryInterface(httpChannel));
timedChan->SetTimingEnabled(true);
if (NS_SUCCEEDED(httpChannel->AsyncOpen(this))) {
NS_NewTimerWithCallback(getter_AddRefs(mTimeout), this,
gTRRService->GetRequestTimeout(),
nsITimer::TYPE_ONE_SHOT);
return NS_OK;
if (timedChan) {
timedChan->SetTimingEnabled(true);
}
mChannel = nullptr;
return NS_ERROR_UNEXPECTED;
return NS_OK;
}
NS_IMETHODIMP

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

@ -12,6 +12,7 @@
#include "nsIHttpPushListener.h"
#include "nsIInterfaceRequestor.h"
#include "nsIStreamListener.h"
#include "nsHostResolver.h"
#include "nsXULAppAPI.h"
namespace mozilla {
@ -33,6 +34,7 @@ class DOHaddr : public LinkedListElement<DOHaddr> {
};
class TRRService;
class TRRServiceChannel;
extern TRRService* gTRRService;
class DOHresp {
@ -167,6 +169,10 @@ class TRR : public Runnable,
nsresult CreateChannelHelper(nsIURI* aUri, nsIChannel** aResult);
friend class TRRServiceChannel;
static nsresult SetupTRRServiceChannelInternal(nsIHttpChannel* aChannel,
bool aUseGet);
nsCOMPtr<nsIChannel> mChannel;
enum TrrType mType;
TimeStamp mStartTime;

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

@ -18,7 +18,9 @@
#include "nsIOService.h"
#include "nsISeekableStream.h"
#include "nsURLHelper.h"
#include "TRRLoadInfo.h"
#include "ReferrerInfo.h"
#include "TRR.h"
namespace mozilla {
namespace net {
@ -675,9 +677,11 @@ nsresult TRRServiceChannel::OnPush(uint32_t aPushedStreamId,
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsILoadInfo> loadInfo =
static_cast<TRRLoadInfo*>(mLoadInfo.get())->Clone();
nsCOMPtr<nsIChannel> pushHttpChannel;
rv = gHttpHandler->CreateTRRServiceChannel(pushResource, nullptr, 0, nullptr,
nullptr,
loadInfo,
getter_AddRefs(pushHttpChannel));
NS_ENSURE_SUCCESS(rv, rv);
@ -945,6 +949,18 @@ TRRServiceChannel::OnStartRequest(nsIRequest* request) {
if ((httpStatus < 500) && (httpStatus != 421) && (httpStatus != 407)) {
ProcessAltService();
}
if (httpStatus == 300 || httpStatus == 301 || httpStatus == 302 ||
httpStatus == 303 || httpStatus == 307 || httpStatus == 308) {
nsresult rv = SyncProcessRedirection(httpStatus);
if (NS_SUCCEEDED(rv)) {
return rv;
}
mStatus = rv;
DoNotifyListener();
return rv;
}
} else {
NS_WARNING("No response head in OnStartRequest");
}
@ -959,6 +975,124 @@ TRRServiceChannel::OnStartRequest(nsIRequest* request) {
return CallOnStartRequest();
}
nsresult TRRServiceChannel::SyncProcessRedirection(uint32_t aHttpStatus) {
nsAutoCString location;
// if a location header was not given, then we can't perform the redirect,
// so just carry on as though this were a normal response.
if (NS_FAILED(mResponseHead->GetHeader(nsHttp::Location, location))) {
return NS_ERROR_FAILURE;
}
// make sure non-ASCII characters in the location header are escaped.
nsAutoCString locationBuf;
if (NS_EscapeURL(location.get(), -1, esc_OnlyNonASCII | esc_Spaces,
locationBuf)) {
location = locationBuf;
}
LOG(("redirecting to: %s [redirection-limit=%u]\n", location.get(),
uint32_t(mRedirectionLimit)));
nsCOMPtr<nsIURI> redirectURI;
nsresult rv = NS_NewURI(getter_AddRefs(redirectURI), location);
if (NS_FAILED(rv)) {
LOG(("Invalid URI for redirect: Location: %s\n", location.get()));
return NS_ERROR_CORRUPTED_CONTENT;
}
// move the reference of the old location to the new one if the new
// one has none.
PropagateReferenceIfNeeded(mURI, redirectURI);
bool rewriteToGET =
ShouldRewriteRedirectToGET(aHttpStatus, mRequestHead.ParsedMethod());
// Let's not rewrite the method to GET for TRR requests.
if (rewriteToGET) {
return NS_ERROR_FAILURE;
}
// If the method is not safe (such as POST, PUT, DELETE, ...)
if (!mRequestHead.IsSafeMethod()) {
LOG(("TRRServiceChannel: unsafe redirect to:%s\n", location.get()));
}
uint32_t redirectFlags;
if (nsHttp::IsPermanentRedirect(aHttpStatus)) {
redirectFlags = nsIChannelEventSink::REDIRECT_PERMANENT;
} else {
redirectFlags = nsIChannelEventSink::REDIRECT_TEMPORARY;
}
nsCOMPtr<nsIChannel> newChannel;
nsCOMPtr<nsILoadInfo> redirectLoadInfo =
static_cast<TRRLoadInfo*>(mLoadInfo.get())->Clone();
rv = gHttpHandler->CreateTRRServiceChannel(redirectURI, nullptr, 0, nullptr,
redirectLoadInfo,
getter_AddRefs(newChannel));
if (NS_FAILED(rv)) {
return rv;
}
rv = SetupReplacementChannel(redirectURI, newChannel, !rewriteToGET,
redirectFlags);
if (NS_FAILED(rv)) {
return rv;
}
// Make sure to do this after we received redirect veto answer,
// i.e. after all sinks had been notified
newChannel->SetOriginalURI(mOriginalURI);
rv = newChannel->AsyncOpen(mListener);
LOG((" new channel AsyncOpen returned %" PRIX32, static_cast<uint32_t>(rv)));
// close down this channel
Cancel(NS_BINDING_REDIRECTED);
ReleaseListeners();
return NS_OK;
}
nsresult TRRServiceChannel::SetupReplacementChannel(nsIURI* aNewURI,
nsIChannel* aNewChannel,
bool aPreserveMethod,
uint32_t aRedirectFlags) {
LOG(
("TRRServiceChannel::SetupReplacementChannel "
"[this=%p newChannel=%p preserveMethod=%d]",
this, aNewChannel, aPreserveMethod));
nsresult rv = HttpBaseChannel::SetupReplacementChannel(
aNewURI, aNewChannel, aPreserveMethod, aRedirectFlags);
if (NS_FAILED(rv)) {
return rv;
}
rv = CheckRedirectLimit(aRedirectFlags);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aNewChannel);
if (!httpChannel) {
MOZ_ASSERT(false);
return NS_ERROR_FAILURE;
}
// convey the mApplyConversion flag (bug 91862)
nsCOMPtr<nsIEncodedChannel> encodedChannel = do_QueryInterface(httpChannel);
if (encodedChannel) {
encodedChannel->SetApplyConversion(mApplyConversion);
}
// Apply TRR specific settings.
return TRR::SetupTRRServiceChannelInternal(
httpChannel,
mRequestHead.ParsedMethod() == nsHttpRequestHead::kMethod_Get);
}
NS_IMETHODIMP
TRRServiceChannel::OnDataAvailable(nsIRequest* request, nsIInputStream* input,
uint64_t offset, uint32_t count) {
@ -1306,5 +1440,16 @@ TRRServiceChannel::GetResponseEnd(TimeStamp* _retval) {
return NS_OK;
}
NS_IMETHODIMP TRRServiceChannel::SetLoadGroup(nsILoadGroup* aLoadGroup) {
return NS_OK;
}
NS_IMETHODIMP
TRRServiceChannel::TimingAllowCheck(nsIPrincipal* aOrigin, bool* aResult) {
NS_ENSURE_ARG_POINTER(aResult);
*aResult = true;
return NS_OK;
}
} // namespace net
} // namespace mozilla

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

@ -101,6 +101,8 @@ class TRRServiceChannel : public HttpBaseChannel,
NS_IMETHOD GetRequestStart(mozilla::TimeStamp* aRequestStart) override;
NS_IMETHOD GetResponseStart(mozilla::TimeStamp* aResponseStart) override;
NS_IMETHOD GetResponseEnd(mozilla::TimeStamp* aResponseEnd) override;
NS_IMETHOD SetLoadGroup(nsILoadGroup* aLoadGroup) override;
NS_IMETHOD TimingAllowCheck(nsIPrincipal* aOrigin, bool* aResult) override;
protected:
TRRServiceChannel();
@ -125,6 +127,10 @@ class TRRServiceChannel : public HttpBaseChannel,
nsresult ResolveProxy();
void AfterApplyContentConversions(nsresult aResult,
nsIStreamListener* aListener);
nsresult SyncProcessRedirection(uint32_t aHttpStatus);
virtual MOZ_MUST_USE nsresult SetupReplacementChannel(
nsIURI* aNewURI, nsIChannel* aNewChannel, bool aPreserveMethod,
uint32_t aRedirectFlags) override;
// True only when we have computed the value of the top window origin.
bool mTopWindowOriginComputed;

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

@ -154,6 +154,7 @@ LOCAL_INCLUDES += [
'/extensions/auth',
'/netwerk/base',
'/netwerk/cookie',
'/netwerk/dns',
'/netwerk/ipc',
'/netwerk/socket/neqo_glue',
'/netwerk/url-classifier',

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

@ -1686,6 +1686,32 @@ add_task(async function test_content_encoding_gzip() {
await new DNSListener("bar.example.com", "2.2.2.2");
});
add_task(async function test_redirect_get() {
dns.clearCache(true);
Services.prefs.setIntPref("network.trr.mode", 3); // TRR-only
Services.prefs.setCharPref(
"network.trr.uri",
`https://foo.example.com:${h2Port}/doh?redirect=4.4.4.4{&dns}`
);
Services.prefs.clearUserPref("network.trr.allow-rfc1918");
Services.prefs.setBoolPref("network.trr.useGET", true);
Services.prefs.setBoolPref("network.trr.disable-ECS", true);
await new DNSListener("ecs.example.com", "4.4.4.4");
});
// test redirect
add_task(async function test_redirect_post() {
dns.clearCache(true);
Services.prefs.setIntPref("network.trr.mode", 3);
Services.prefs.setBoolPref("network.trr.useGET", false);
Services.prefs.setCharPref(
"network.trr.uri",
`https://foo.example.com:${h2Port}/doh?redirect=4.4.4.4`
);
await new DNSListener("bar.example.com", "4.4.4.4");
});
// confirmationNS set without confirmed NS yet
// checks that we properly fall back to DNS is confirmation is not ready yet
add_task(async function test_resolve_not_confirmed() {

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

@ -580,6 +580,30 @@ function handleRequest(req, res) {
responseIP = "5.5.5.5";
}
let redirect = u.query.redirect;
if (redirect) {
responseIP = redirect;
if (u.query.dns) {
res.setHeader(
"Location",
"https://localhost:" +
serverPort +
"/doh?responseIP=" +
responseIP +
"&dns=" +
u.query.dns
);
} else {
res.setHeader(
"Location",
"https://localhost:" + serverPort + "/doh?responseIP=" + responseIP
);
}
res.writeHead(307);
res.end("");
return;
}
if (u.query.auth) {
// There's a Set-Cookie: header in the response for "/dns" , which this
// request subsequently would include if the http channel wasn't
@ -758,7 +782,10 @@ function handleRequest(req, res) {
payload = Buffer.concat([payload, chunk]);
});
req.on("end", function finishedData() {
emitResponse(res, payload);
// parload is empty when we send redirect response.
if (payload.length) {
emitResponse(res, payload);
}
});
return;
} else if (u.pathname === "/dns-cname-a") {