From 5aea6ddbff8e94f9d61626144df2d617d28927fe Mon Sep 17 00:00:00 2001 From: "Byron Campen [:bwc]" Date: Tue, 17 Sep 2019 18:15:23 +0000 Subject: [PATCH] Bug 1569183: Teach WebrtcProxyChannel to handle non-proxied TCP connections, and SOCKS proxying because that was easy to do also. r=mjf Differential Revision: https://phabricator.services.mozilla.com/D45097 --HG-- extra : moz-landing-system : lando --- media/mtransport/ipc/WebrtcProxyChannel.cpp | 192 +++++++++++++++++--- media/mtransport/ipc/WebrtcProxyChannel.h | 17 +- 2 files changed, 182 insertions(+), 27 deletions(-) diff --git a/media/mtransport/ipc/WebrtcProxyChannel.cpp b/media/mtransport/ipc/WebrtcProxyChannel.cpp index e2e6d329029d..b32a2925c025 100644 --- a/media/mtransport/ipc/WebrtcProxyChannel.cpp +++ b/media/mtransport/ipc/WebrtcProxyChannel.cpp @@ -19,6 +19,7 @@ #include "nsString.h" #include "mozilla/dom/ContentProcessManager.h" #include "mozilla/dom/BrowserParent.h" +#include "nsISocketTransportService.h" #include "WebrtcProxyChannelCallback.h" #include "WebrtcProxyLog.h" @@ -43,7 +44,8 @@ class WebrtcProxyData { NS_IMPL_ISUPPORTS(WebrtcProxyChannel, nsIAuthPromptProvider, nsIHttpUpgradeListener, nsIInputStreamCallback, nsIInterfaceRequestor, nsIOutputStreamCallback, - nsIRequestObserver, nsIStreamListener) + nsIRequestObserver, nsIStreamListener, + nsIProtocolProxyCallback) WebrtcProxyChannel::WebrtcProxyChannel(WebrtcProxyChannelCallback* aCallbacks) : mProxyCallbacks(aCallbacks), @@ -137,45 +139,171 @@ void WebrtcProxyChannel::CloseWithReason(nsresult aReason) { nsresult WebrtcProxyChannel::Open(const nsCString& aHost, const int& aPort, const net::LoadInfoArgs& aArgs, const nsCString& aAlpn) { - LOG(("WebrtcProxyChannel::AsyncOpen %p\n", this)); + LOG(("WebrtcProxyChannel::Open %p\n", this)); - if (mOpened) { + if (NS_WARN_IF(mOpened)) { LOG(("WebrtcProxyChannel %p: proxy channel already open\n", this)); CloseWithReason(NS_ERROR_FAILURE); return NS_ERROR_FAILURE; } mOpened = true; - - nsresult rv; nsCString spec = NS_LITERAL_CSTRING("http://") + aHost; - nsCOMPtr uri; - rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) - .SetSpec(spec) - .SetPort(aPort) - .Finalize(uri); + nsresult rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID) + .SetSpec(spec) + .SetPort(aPort) + .Finalize(mURI); - if (NS_FAILED(rv)) { - LOG(("WebrtcProxyChannel %p: bad proxy connect uri set\n", this)); - CloseWithReason(rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(NS_ERROR_FAILURE); + return NS_ERROR_FAILURE; + } + + mLoadInfoArgs = aArgs; + mAlpn = aAlpn; + + // We need to figure out whether a proxy needs to be used for mURI before we + // can start on establishing a connection. + rv = DoProxyConfigLookup(); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(NS_ERROR_FAILURE); + } + + return rv; +} + +nsresult WebrtcProxyChannel::DoProxyConfigLookup() { + nsresult rv; + nsCOMPtr pps = + do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv); + if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } + nsCOMPtr channel; + rv = NS_NewChannel(getter_AddRefs(channel), mURI, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, + nsIContentPolicy::TYPE_OTHER); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr target = + SystemGroup::EventTargetFor(TaskCategory::Network); + nsCOMPtr proxyRequest; + + rv = pps->AsyncResolve(channel, + nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY | + nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL, + this, target, getter_AddRefs(proxyRequest)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + // We pick back up in OnProxyAvailable + + return NS_OK; +} + +NS_IMETHODIMP WebrtcProxyChannel::OnProxyAvailable(nsICancelable* aRequest, + nsIChannel* aChannel, + nsIProxyInfo* aProxyinfo, + nsresult aResult) { + nsresult rv; + + if (NS_SUCCEEDED(aResult) && aProxyinfo) { + nsCString proxyType; + rv = aProxyinfo->GetType(proxyType); + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + return rv; + } + + if (proxyType == "http" || proxyType == "https") { + rv = OpenWithHttpProxy(); + } else if (proxyType == "socks" || proxyType == "socks4" || + proxyType == "direct") { + rv = OpenWithoutHttpProxy(aProxyinfo); + } + } else { + rv = OpenWithoutHttpProxy(nullptr); + } + + if (NS_WARN_IF(NS_FAILED(rv))) { + CloseWithReason(rv); + } + + return rv; +} + +nsresult WebrtcProxyChannel::OpenWithoutHttpProxy( + nsIProxyInfo* aSocksProxyInfo) { + nsCString host; + int32_t port; + + nsresult rv = mURI->GetHost(host); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + rv = mURI->GetPort(&port); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + AutoTArray socketTypes; + if (mSsl) { + socketTypes.AppendElement(NS_LITERAL_CSTRING("ssl")); + } + + nsCOMPtr sts = + do_GetService("@mozilla.org/network/socket-transport-service;1"); + rv = sts->CreateTransport(socketTypes, host, port, aSocksProxyInfo, + getter_AddRefs(mTransport)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + nsCOMPtr socketIn; + rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(socketIn)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + mSocketIn = do_QueryInterface(socketIn); + if (NS_WARN_IF(!mSocketIn)) { + return NS_ERROR_NULL_POINTER; + } + + nsCOMPtr socketOut; + rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0, + getter_AddRefs(socketOut)); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + mSocketOut = do_QueryInterface(socketOut); + if (NS_WARN_IF(!mSocketOut)) { + return NS_ERROR_NULL_POINTER; + } + + return FinishOpen(); +} + +nsresult WebrtcProxyChannel::OpenWithHttpProxy() { + nsresult rv; nsCOMPtr ioService; ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) { LOG(("WebrtcProxyChannel %p: io service missing\n", this)); - CloseWithReason(rv); return rv; } nsCOMPtr loadInfo; - Maybe loadInfoArgs = Some(aArgs); + Maybe loadInfoArgs = Some(mLoadInfoArgs); rv = LoadInfoArgsToLoadInfo(loadInfoArgs, getter_AddRefs(loadInfo)); if (NS_FAILED(rv)) { LOG(("WebrtcProxyChannel %p: could not init load info\n", this)); - CloseWithReason(rv); return rv; } @@ -186,7 +314,7 @@ nsresult WebrtcProxyChannel::Open(const nsCString& aHost, const int& aPort, // introduce new behavior. can't follow redirects on connect anyway. nsCOMPtr localChannel; rv = ioService->NewChannelFromURIWithProxyFlags( - uri, nullptr, + mURI, nullptr, // Proxy flags are overridden by SetConnectOnly() 0, loadInfo->LoadingNode(), loadInfo->LoadingPrincipal(), loadInfo->TriggeringPrincipal(), @@ -197,7 +325,6 @@ nsresult WebrtcProxyChannel::Open(const nsCString& aHost, const int& aPort, nsIContentPolicy::TYPE_OTHER, getter_AddRefs(localChannel)); if (NS_FAILED(rv)) { LOG(("WebrtcProxyChannel %p: bad open channel\n", this)); - CloseWithReason(rv); return rv; } @@ -206,7 +333,6 @@ nsresult WebrtcProxyChannel::Open(const nsCString& aHost, const int& aPort, if (!httpChannel) { LOG(("WebrtcProxyChannel %p: not an http channel\n", this)); - CloseWithReason(NS_ERROR_FAILURE); return NS_ERROR_FAILURE; } @@ -220,23 +346,29 @@ nsresult WebrtcProxyChannel::Open(const nsCString& aHost, const int& aPort, nsIClassOfService::DontThrottle); } else { LOG(("WebrtcProxyChannel %p: could not set class of service\n", this)); - CloseWithReason(NS_ERROR_FAILURE); return NS_ERROR_FAILURE; } - rv = httpChannel->HTTPUpgrade(aAlpn, this); - NS_ENSURE_SUCCESS(rv, rv); + rv = httpChannel->HTTPUpgrade(mAlpn, this); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } rv = httpChannel->SetConnectOnly(); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } rv = NS_MaybeOpenChannelUsingAsyncOpen(httpChannel, this); if (NS_FAILED(rv)) { LOG(("WebrtcProxyChannel %p: cannot async open\n", this)); - CloseWithReason(rv); return rv; } + // This picks back up in OnTransportAvailable once we have connected to the + // proxy, and performed the http upgrade to switch the proxy into passthrough + // mode. + return NS_OK; } @@ -305,6 +437,9 @@ NS_IMETHODIMP WebrtcProxyChannel::OnTransportAvailable(nsISocketTransport* aTransport, nsIAsyncInputStream* aSocketIn, nsIAsyncOutputStream* aSocketOut) { + // This is called only in the http proxy case, once we have connected to the + // http proxy and performed the http upgrade to switch it over to passthrough + // mode. That process is started async by OpenWithHttpProxy. LOG(("WebrtcProxyChannel::OnTransportAvailable %p\n", this)); MOZ_ASSERT(OnSocketThread(), "not on socket thread"); MOZ_ASSERT(!mTransport, "already called transport available on webrtc proxy"); @@ -338,6 +473,15 @@ WebrtcProxyChannel::OnTransportAvailable(nsISocketTransport* aTransport, return rv; } + return FinishOpen(); +} + +nsresult WebrtcProxyChannel::FinishOpen() { + // mTransport, mSocketIn, and mSocketOut are all set. We may have set them in + // OnTransportAvailable (in the http/https proxy case), or in + // OpenWithoutHttpProxy. From here on out, this class functions the same for + // these two cases. + mSocketIn->AsyncWait(this, 0, 0, nullptr); InvokeOnConnected(); diff --git a/media/mtransport/ipc/WebrtcProxyChannel.h b/media/mtransport/ipc/WebrtcProxyChannel.h index 1b08a29eb99a..e14914f7448c 100644 --- a/media/mtransport/ipc/WebrtcProxyChannel.h +++ b/media/mtransport/ipc/WebrtcProxyChannel.h @@ -18,13 +18,14 @@ #include "nsIStreamListener.h" #include "nsStringFwd.h" #include "nsTArray.h" -#include "mozilla/dom/ipc/IdType.h" // TabId +#include "mozilla/dom/ipc/IdType.h" // TabId +#include "mozilla/dom/PContentChild.h" // LoadInfoArgs +#include "nsIProtocolProxyCallback.h" class nsISocketTransport; namespace mozilla { namespace net { -class LoadInfoArgs; class WebrtcProxyChannelCallback; class WebrtcProxyData; @@ -34,7 +35,8 @@ class WebrtcProxyChannel : public nsIHttpUpgradeListener, public nsIInputStreamCallback, public nsIOutputStreamCallback, public nsIInterfaceRequestor, - public nsIAuthPromptProvider { + public nsIAuthPromptProvider, + public nsIProtocolProxyCallback { public: NS_DECL_NSIHTTPUPGRADELISTENER NS_DECL_NSIINPUTSTREAMCALLBACK @@ -44,6 +46,7 @@ class WebrtcProxyChannel : public nsIHttpUpgradeListener, NS_DECL_NSISTREAMLISTENER NS_DECL_THREADSAFE_ISUPPORTS NS_FORWARD_SAFE_NSIAUTHPROMPTPROVIDER(mAuthProvider) + NS_DECL_NSIPROTOCOLPROXYCALLBACK explicit WebrtcProxyChannel(WebrtcProxyChannelCallback* aCallbacks); @@ -68,7 +71,15 @@ class WebrtcProxyChannel : public nsIHttpUpgradeListener, private: bool mClosed; bool mOpened; + nsCOMPtr mURI; + net::LoadInfoArgs mLoadInfoArgs; + nsCString mAlpn; + bool mSsl = false; + nsresult DoProxyConfigLookup(); + nsresult OpenWithHttpProxy(); + nsresult OpenWithoutHttpProxy(nsIProxyInfo* aSocksProxyInfo); + nsresult FinishOpen(); void EnqueueWrite_s(nsTArray&& aWriteData); void CloseWithReason(nsresult aReason);