зеркало из https://github.com/mozilla/gecko-dev.git
Bug 902346 - Support socks proxy in TCPSocket. r=mixedpuppy,necko-reviewers
- Use nsIProtocolProxyService to look up proxy - Pass the found proxy to CreateTransport Differential Revision: https://phabricator.services.mozilla.com/D104357
This commit is contained in:
Родитель
ff3000edd1
Коммит
b2fa673570
|
@ -36,7 +36,14 @@
|
|||
#include "nsIOutputStream.h"
|
||||
#include "nsINSSErrorsService.h"
|
||||
#include "nsISSLSocketControl.h"
|
||||
#include "nsIProtocolProxyService.h"
|
||||
#include "nsICancelable.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsIURIMutator.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsString.h"
|
||||
#include "nsStringStream.h"
|
||||
#include "secerr.h"
|
||||
#include "sslerr.h"
|
||||
|
@ -131,6 +138,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocket)
|
|||
NS_INTERFACE_MAP_ENTRY(nsIObserver)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_ENTRY(nsITCPSocketCallback)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
TCPSocket::TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost,
|
||||
|
@ -221,7 +229,7 @@ nsresult TCPSocket::InitWithUnconnectedTransport(
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult TCPSocket::Init() {
|
||||
nsresult TCPSocket::Init(nsIProxyInfo* aProxyInfo) {
|
||||
nsCOMPtr<nsIObserverService> obs =
|
||||
do_GetService("@mozilla.org/observer-service;1");
|
||||
if (obs) {
|
||||
|
@ -254,7 +262,7 @@ nsresult TCPSocket::Init() {
|
|||
nsCOMPtr<nsISocketTransport> transport;
|
||||
nsresult rv =
|
||||
sts->CreateTransport(socketTypes, NS_ConvertUTF16toUTF8(mHost), mPort,
|
||||
nullptr, nullptr, getter_AddRefs(transport));
|
||||
aProxyInfo, nullptr, getter_AddRefs(transport));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return InitWithUnconnectedTransport(transport);
|
||||
|
@ -708,6 +716,11 @@ void TCPSocket::CloseHelper(bool waitForUnsentData) {
|
|||
|
||||
mReadyState = TCPReadyState::Closing;
|
||||
|
||||
if (mProxyRequest) {
|
||||
mProxyRequest->Cancel(NS_BINDING_ABORTED);
|
||||
mProxyRequest = nullptr;
|
||||
}
|
||||
|
||||
if (mSocketBridgeChild) {
|
||||
mSocketBridgeChild->SendClose();
|
||||
return;
|
||||
|
@ -829,9 +842,8 @@ TCPReadyState TCPSocket::ReadyState() { return mReadyState; }
|
|||
TCPSocketBinaryType TCPSocket::BinaryType() {
|
||||
if (mUseArrayBuffers) {
|
||||
return TCPSocketBinaryType::Arraybuffer;
|
||||
} else {
|
||||
return TCPSocketBinaryType::String;
|
||||
}
|
||||
return TCPSocketBinaryType::String;
|
||||
}
|
||||
|
||||
already_AddRefed<TCPSocket> TCPSocket::CreateAcceptedSocket(
|
||||
|
@ -859,15 +871,70 @@ already_AddRefed<TCPSocket> TCPSocket::Constructor(
|
|||
RefPtr<TCPSocket> socket =
|
||||
new TCPSocket(global, aHost, aPort, aOptions.mUseSecureTransport,
|
||||
aOptions.mBinaryType == TCPSocketBinaryType::Arraybuffer);
|
||||
nsresult rv = socket->Init();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
socket->ResolveProxy();
|
||||
|
||||
return socket.forget();
|
||||
}
|
||||
|
||||
nsresult TCPSocket::ResolveProxy() {
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIProtocolProxyService> pps =
|
||||
do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsCString spec = mSsl ? "https://"_ns : "http://"_ns;
|
||||
if (!AppendUTF16toUTF8(mHost, spec, fallible)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
|
||||
.SetSpec(spec)
|
||||
.SetPort(mPort)
|
||||
.Finalize(uri);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel), uri,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
rv = pps->AsyncResolve(channel,
|
||||
nsIProtocolProxyService::RESOLVE_PREFER_SOCKS_PROXY,
|
||||
this, nullptr, getter_AddRefs(mProxyRequest));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
TCPSocket::OnProxyAvailable(nsICancelable* aRequest, nsIChannel* aChannel,
|
||||
nsIProxyInfo* aProxyInfo, nsresult aResult) {
|
||||
mProxyRequest = nullptr;
|
||||
if (NS_SUCCEEDED(aResult) && aProxyInfo) {
|
||||
nsCString proxyType;
|
||||
nsresult rv = aProxyInfo->GetType(proxyType);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Close();
|
||||
return rv;
|
||||
}
|
||||
// Only supports SOCKS proxy for now.
|
||||
if (proxyType == "socks" || proxyType == "socks4") {
|
||||
return Init(aProxyInfo);
|
||||
}
|
||||
}
|
||||
return Init(nullptr);
|
||||
}
|
||||
|
||||
nsresult TCPSocket::CreateInputStreamPump() {
|
||||
if (!mSocketInputStream) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "mozilla/dom/TCPSocketBinding.h"
|
||||
#include "mozilla/dom/TypedArray.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "nsIProxyInfo.h"
|
||||
#include "nsITransport.h"
|
||||
#include "nsIStreamListener.h"
|
||||
#include "nsIAsyncInputStream.h"
|
||||
|
@ -17,6 +18,7 @@
|
|||
#include "nsIObserver.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsITCPSocketCallback.h"
|
||||
#include "nsIProtocolProxyCallback.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
class nsISocketTransport;
|
||||
|
@ -69,7 +71,8 @@ class TCPSocket final : public DOMEventTargetHelper,
|
|||
public nsIInputStreamCallback,
|
||||
public nsIObserver,
|
||||
public nsSupportsWeakReference,
|
||||
public nsITCPSocketCallback {
|
||||
public nsITCPSocketCallback,
|
||||
public nsIProtocolProxyCallback {
|
||||
public:
|
||||
TCPSocket(nsIGlobalObject* aGlobal, const nsAString& aHost, uint16_t aPort,
|
||||
bool aSsl, bool aUseArrayBuffers);
|
||||
|
@ -83,6 +86,7 @@ class TCPSocket final : public DOMEventTargetHelper,
|
|||
NS_DECL_NSIINPUTSTREAMCALLBACK
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSITCPSOCKETCALLBACK
|
||||
NS_DECL_NSIPROTOCOLPROXYCALLBACK
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
@ -120,8 +124,7 @@ class TCPSocket final : public DOMEventTargetHelper,
|
|||
// Used by the TCPServerSocketChild implementation when a new connection is
|
||||
// accepted.
|
||||
static already_AddRefed<TCPSocket> CreateAcceptedSocket(
|
||||
nsIGlobalObject* aGlobal, TCPSocketChild* aSocketBridge,
|
||||
bool aUseArrayBuffers);
|
||||
nsIGlobalObject* aGlobal, TCPSocketChild* aBridge, bool aUseArrayBuffers);
|
||||
|
||||
// Initialize this socket's associated IPC actor in the parent process.
|
||||
void SetSocketBridgeParent(TCPSocketParent* aBridgeParent);
|
||||
|
@ -134,7 +137,7 @@ class TCPSocket final : public DOMEventTargetHelper,
|
|||
IMPL_EVENT_HANDLER(error);
|
||||
IMPL_EVENT_HANDLER(close);
|
||||
|
||||
nsresult Init();
|
||||
nsresult Init(nsIProxyInfo* aProxyInfo);
|
||||
|
||||
// Inform this socket that a buffered send() has completed sending.
|
||||
void NotifyCopyComplete(nsresult aStatus);
|
||||
|
@ -147,7 +150,7 @@ class TCPSocket final : public DOMEventTargetHelper,
|
|||
~TCPSocket();
|
||||
|
||||
// Initialize this socket with an existing IPC actor.
|
||||
void InitWithSocketChild(TCPSocketChild* aBridge);
|
||||
void InitWithSocketChild(TCPSocketChild* aSocketBridge);
|
||||
// Initialize this socket from an existing low-level connection.
|
||||
nsresult InitWithTransport(nsISocketTransport* aTransport);
|
||||
// Initialize the input/output streams for this socket object.
|
||||
|
@ -170,6 +173,8 @@ class TCPSocket final : public DOMEventTargetHelper,
|
|||
// Helper for Close/CloseImmediately
|
||||
void CloseHelper(bool waitForUnsentData);
|
||||
|
||||
nsresult ResolveProxy();
|
||||
|
||||
TCPReadyState mReadyState;
|
||||
// Whether to use strings or array buffers for the "data" event.
|
||||
bool mUseArrayBuffers;
|
||||
|
@ -188,6 +193,8 @@ class TCPSocket final : public DOMEventTargetHelper,
|
|||
nsCOMPtr<nsIInputStream> mSocketInputStream;
|
||||
nsCOMPtr<nsIOutputStream> mSocketOutputStream;
|
||||
|
||||
nsCOMPtr<nsICancelable> mProxyRequest;
|
||||
|
||||
// Input stream machinery
|
||||
nsCOMPtr<nsIInputStreamPump> mInputStreamPump;
|
||||
nsCOMPtr<nsIScriptableInputStream> mInputStreamScriptable;
|
||||
|
|
|
@ -80,7 +80,7 @@ mozilla::ipc::IPCResult TCPSocketParent::RecvOpen(
|
|||
const bool& aUseArrayBuffers) {
|
||||
mSocket = new TCPSocket(nullptr, aHost, aPort, aUseSSL, aUseArrayBuffers);
|
||||
mSocket->SetSocketBridgeParent(this);
|
||||
NS_ENSURE_SUCCESS(mSocket->Init(), IPC_OK());
|
||||
NS_ENSURE_SUCCESS(mSocket->Init(nullptr), IPC_OK());
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
|
|
@ -360,7 +360,7 @@ class SocksTestServer {
|
|||
|
||||
/**
|
||||
* Tests the basic socks logic using a simple socket connection and the
|
||||
* protocol proxy service. It seems TCPSocket has no way to tie proxy
|
||||
* protocol proxy service. Before 902346, TCPSocket has no way to tie proxy
|
||||
* data to it, so we go old school here.
|
||||
*/
|
||||
class SocksTestClient {
|
||||
|
@ -481,6 +481,67 @@ add_task(async function test_socks_server() {
|
|||
});
|
||||
});
|
||||
|
||||
// Register a proxy to be used by TCPSocket connections later.
|
||||
function registerProxy(socks) {
|
||||
let pps = Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(
|
||||
Ci.nsIProtocolProxyService
|
||||
);
|
||||
let filter = {
|
||||
QueryInterface: ChromeUtils.generateQI(["nsIProtocolProxyFilter"]),
|
||||
applyFilter(uri, proxyInfo, callback) {
|
||||
callback.onProxyFilterResult(
|
||||
pps.newProxyInfoWithAuth(
|
||||
socks.version,
|
||||
socks.host,
|
||||
socks.port,
|
||||
socks.username,
|
||||
socks.password,
|
||||
"",
|
||||
"",
|
||||
socks.dns == "remote"
|
||||
? Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST
|
||||
: 0,
|
||||
-1,
|
||||
null
|
||||
)
|
||||
);
|
||||
},
|
||||
};
|
||||
pps.registerFilter(filter, 0);
|
||||
registerCleanupFunction(() => {
|
||||
pps.unregisterFilter(filter);
|
||||
});
|
||||
}
|
||||
|
||||
// A simple ping/pong to test the socks server with TCPSocket.
|
||||
add_task(async function test_tcpsocket_proxy() {
|
||||
let socks = {
|
||||
version: "socks",
|
||||
host: "127.0.0.1",
|
||||
port: socksServer.listener.localPort,
|
||||
username: "foo",
|
||||
password: "bar",
|
||||
dns: false,
|
||||
};
|
||||
let dest = {
|
||||
host: "localhost",
|
||||
port: 8888,
|
||||
};
|
||||
|
||||
registerProxy(socks);
|
||||
await new Promise((resolve, reject) => {
|
||||
let client = new TCPSocket(dest.host, dest.port);
|
||||
client.onopen = () => {
|
||||
client.send("PING!");
|
||||
};
|
||||
client.ondata = e => {
|
||||
equal("PONG!", e.data, "socks test ok");
|
||||
resolve();
|
||||
};
|
||||
client.onerror = () => reject();
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_webRequest_socks_proxy() {
|
||||
async function background(port) {
|
||||
function checkProxyData(details) {
|
||||
|
@ -555,3 +616,45 @@ add_task(async function test_webRequest_socks_proxy() {
|
|||
await contentPage.close();
|
||||
await handlingExt.unload();
|
||||
});
|
||||
|
||||
add_task(async function test_onRequest_tcpsocket_proxy() {
|
||||
async function background(port) {
|
||||
browser.proxy.onRequest.addListener(
|
||||
() => {
|
||||
return [
|
||||
{
|
||||
type: "socks",
|
||||
host: "127.0.0.1",
|
||||
port,
|
||||
username: "foo",
|
||||
password: "bar",
|
||||
},
|
||||
];
|
||||
},
|
||||
{ urls: ["<all_urls>"] }
|
||||
);
|
||||
}
|
||||
|
||||
let handlingExt = ExtensionTestUtils.loadExtension({
|
||||
manifest: {
|
||||
permissions: ["proxy", "webRequest", "webRequestBlocking", "<all_urls>"],
|
||||
},
|
||||
background: `(${background})(${socksServer.listener.localPort})`,
|
||||
});
|
||||
|
||||
await handlingExt.startup();
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
let client = new TCPSocket("localhost", 8888);
|
||||
client.onopen = () => {
|
||||
client.send("PING!");
|
||||
};
|
||||
client.ondata = e => {
|
||||
equal("PONG!", e.data, "socks test ok");
|
||||
resolve();
|
||||
};
|
||||
client.onerror = () => reject();
|
||||
});
|
||||
|
||||
await handlingExt.unload();
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче