summaryrefslogtreecommitdiffstats
path: root/dom/network/TCPSocketParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/network/TCPSocketParent.cpp')
-rw-r--r--dom/network/TCPSocketParent.cpp427
1 files changed, 427 insertions, 0 deletions
diff --git a/dom/network/TCPSocketParent.cpp b/dom/network/TCPSocketParent.cpp
new file mode 100644
index 000000000..54167234e
--- /dev/null
+++ b/dom/network/TCPSocketParent.cpp
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 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 "TCPSocketParent.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "nsJSUtils.h"
+#include "mozilla/Unused.h"
+#include "mozilla/AppProcessChecker.h"
+#include "mozilla/net/NeckoCommon.h"
+#include "mozilla/net/PNeckoParent.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/HoldDropJSObjects.h"
+#include "nsISocketTransportService.h"
+#include "nsISocketTransport.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsNetUtil.h"
+
+namespace IPC {
+
+//Defined in TCPSocketChild.cpp
+extern bool
+DeserializeArrayBuffer(JSContext* aCx,
+ const InfallibleTArray<uint8_t>& aBuffer,
+ JS::MutableHandle<JS::Value> aVal);
+
+} // namespace IPC
+
+namespace mozilla {
+
+namespace net {
+//
+// set MOZ_LOG=TCPSocket:5
+//
+extern LazyLogModule gTCPSocketLog;
+#define TCPSOCKET_LOG(args) MOZ_LOG(gTCPSocketLog, LogLevel::Debug, args)
+#define TCPSOCKET_LOG_ENABLED() MOZ_LOG_TEST(gTCPSocketLog, LogLevel::Debug)
+} // namespace net
+
+namespace dom {
+
+static void
+FireInteralError(mozilla::net::PTCPSocketParent* aActor, uint32_t aLineNo)
+{
+ mozilla::Unused <<
+ aActor->SendCallback(NS_LITERAL_STRING("onerror"),
+ TCPError(NS_LITERAL_STRING("InvalidStateError"), NS_LITERAL_STRING("Internal error")),
+ static_cast<uint32_t>(TCPReadyState::Connecting));
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TCPSocketParentBase)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION(TCPSocketParentBase, mSocket)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TCPSocketParentBase)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TCPSocketParentBase)
+
+TCPSocketParentBase::TCPSocketParentBase()
+: mIPCOpen(false)
+{
+}
+
+TCPSocketParentBase::~TCPSocketParentBase()
+{
+}
+
+uint32_t
+TCPSocketParent::GetAppId()
+{
+ const PContentParent *content = Manager()->Manager();
+ if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
+ TabParent *tab = TabParent::GetFrom(browser);
+ return tab->OwnAppId();
+ } else {
+ return nsIScriptSecurityManager::UNKNOWN_APP_ID;
+ }
+};
+
+bool
+TCPSocketParent::GetInIsolatedMozBrowser()
+{
+ const PContentParent *content = Manager()->Manager();
+ if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
+ TabParent *tab = TabParent::GetFrom(browser);
+ return tab->IsIsolatedMozBrowserElement();
+ } else {
+ return false;
+ }
+}
+
+void
+TCPSocketParentBase::ReleaseIPDLReference()
+{
+ MOZ_ASSERT(mIPCOpen);
+ mIPCOpen = false;
+ this->Release();
+}
+
+void
+TCPSocketParentBase::AddIPDLReference()
+{
+ MOZ_ASSERT(!mIPCOpen);
+ mIPCOpen = true;
+ this->AddRef();
+}
+
+NS_IMETHODIMP_(MozExternalRefCountType) TCPSocketParent::Release(void)
+{
+ nsrefcnt refcnt = TCPSocketParentBase::Release();
+ if (refcnt == 1 && mIPCOpen) {
+ mozilla::Unused << PTCPSocketParent::SendRequestDelete();
+ return 1;
+ }
+ return refcnt;
+}
+
+bool
+TCPSocketParent::RecvOpen(const nsString& aHost, const uint16_t& aPort, const bool& aUseSSL,
+ const bool& aUseArrayBuffers)
+{
+ // We don't have browser actors in xpcshell, and hence can't run automated
+ // tests without this loophole.
+ if (net::UsingNeckoIPCSecurity() &&
+ !AssertAppProcessPermission(Manager()->Manager(), "tcp-socket")) {
+ FireInteralError(this, __LINE__);
+ return true;
+ }
+
+ // Obtain App ID
+ uint32_t appId = GetAppId();
+ bool inIsolatedMozBrowser = GetInIsolatedMozBrowser();
+
+ mSocket = new TCPSocket(nullptr, aHost, aPort, aUseSSL, aUseArrayBuffers);
+ mSocket->SetAppIdAndBrowser(appId, inIsolatedMozBrowser);
+ mSocket->SetSocketBridgeParent(this);
+ NS_ENSURE_SUCCESS(mSocket->Init(), true);
+ return true;
+}
+
+bool
+TCPSocketParent::RecvOpenBind(const nsCString& aRemoteHost,
+ const uint16_t& aRemotePort,
+ const nsCString& aLocalAddr,
+ const uint16_t& aLocalPort,
+ const bool& aUseSSL,
+ const bool& aUseArrayBuffers,
+ const nsCString& aFilter)
+{
+ if (net::UsingNeckoIPCSecurity() &&
+ !AssertAppProcessPermission(Manager()->Manager(), "tcp-socket")) {
+ FireInteralError(this, __LINE__);
+ return true;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsISocketTransportService> sts =
+ do_GetService("@mozilla.org/network/socket-transport-service;1", &rv);
+ if (NS_FAILED(rv)) {
+ FireInteralError(this, __LINE__);
+ return true;
+ }
+
+ nsCOMPtr<nsISocketTransport> socketTransport;
+ rv = sts->CreateTransport(nullptr, 0,
+ aRemoteHost, aRemotePort,
+ nullptr, getter_AddRefs(socketTransport));
+ if (NS_FAILED(rv)) {
+ FireInteralError(this, __LINE__);
+ return true;
+ }
+
+ PRNetAddr prAddr;
+ if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr)) {
+ FireInteralError(this, __LINE__);
+ return true;
+ }
+ if (PR_SUCCESS != PR_StringToNetAddr(aLocalAddr.BeginReading(), &prAddr)) {
+ FireInteralError(this, __LINE__);
+ return true;
+ }
+
+ mozilla::net::NetAddr addr;
+ PRNetAddrToNetAddr(&prAddr, &addr);
+ rv = socketTransport->Bind(&addr);
+ if (NS_FAILED(rv)) {
+ FireInteralError(this, __LINE__);
+ return true;
+ }
+
+ if (!aFilter.IsEmpty()) {
+ nsAutoCString contractId(NS_NETWORK_TCP_SOCKET_FILTER_HANDLER_PREFIX);
+ contractId.Append(aFilter);
+ nsCOMPtr<nsISocketFilterHandler> filterHandler =
+ do_GetService(contractId.get());
+ if (!filterHandler) {
+ NS_ERROR("Content doesn't have a valid filter");
+ FireInteralError(this, __LINE__);
+ return true;
+ }
+ rv = filterHandler->NewFilter(getter_AddRefs(mFilter));
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Cannot create filter that content specified");
+ FireInteralError(this, __LINE__);
+ return true;
+ }
+ }
+
+ // Obtain App ID
+ uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
+ bool inIsolatedMozBrowser = false;
+ const PContentParent *content = Manager()->Manager();
+ if (PBrowserParent* browser = SingleManagedOrNull(content->ManagedPBrowserParent())) {
+ // appId's are for B2G only currently, where managees.Count() == 1
+ // This is not guaranteed currently in Desktop, so skip this there.
+ TabParent *tab = TabParent::GetFrom(browser);
+ appId = tab->OwnAppId();
+ inIsolatedMozBrowser = tab->IsIsolatedMozBrowserElement();
+ }
+
+ mSocket = new TCPSocket(nullptr, NS_ConvertUTF8toUTF16(aRemoteHost), aRemotePort, aUseSSL, aUseArrayBuffers);
+ mSocket->SetAppIdAndBrowser(appId, inIsolatedMozBrowser);
+ mSocket->SetSocketBridgeParent(this);
+ rv = mSocket->InitWithUnconnectedTransport(socketTransport);
+ NS_ENSURE_SUCCESS(rv, true);
+ return true;
+}
+
+bool
+TCPSocketParent::RecvStartTLS()
+{
+ NS_ENSURE_TRUE(mSocket, true);
+ ErrorResult rv;
+ mSocket->UpgradeToSecure(rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ }
+
+ return true;
+}
+
+bool
+TCPSocketParent::RecvSuspend()
+{
+ NS_ENSURE_TRUE(mSocket, true);
+ mSocket->Suspend();
+ return true;
+}
+
+bool
+TCPSocketParent::RecvResume()
+{
+ NS_ENSURE_TRUE(mSocket, true);
+ ErrorResult rv;
+ mSocket->Resume(rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ }
+
+ return true;
+}
+
+bool
+TCPSocketParent::RecvData(const SendableData& aData,
+ const uint32_t& aTrackingNumber)
+{
+ ErrorResult rv;
+
+ if (mFilter) {
+ mozilla::net::NetAddr addr; // dummy value
+ bool allowed;
+ MOZ_ASSERT(aData.type() == SendableData::TArrayOfuint8_t,
+ "Unsupported data type for filtering");
+ const InfallibleTArray<uint8_t>& data(aData.get_ArrayOfuint8_t());
+ nsresult nsrv = mFilter->FilterPacket(&addr, data.Elements(),
+ data.Length(),
+ nsISocketFilter::SF_OUTGOING,
+ &allowed);
+
+ // Reject sending of unallowed data
+ if (NS_WARN_IF(NS_FAILED(nsrv)) || !allowed) {
+ TCPSOCKET_LOG(("%s: Dropping outgoing TCP packet", __FUNCTION__));
+ return false;
+ }
+ }
+
+ switch (aData.type()) {
+ case SendableData::TArrayOfuint8_t: {
+ AutoSafeJSContext autoCx;
+ JS::Rooted<JS::Value> val(autoCx);
+ const nsTArray<uint8_t>& buffer = aData.get_ArrayOfuint8_t();
+ bool ok = IPC::DeserializeArrayBuffer(autoCx, buffer, &val);
+ NS_ENSURE_TRUE(ok, true);
+ RootedTypedArray<ArrayBuffer> data(autoCx);
+ data.Init(&val.toObject());
+ Optional<uint32_t> byteLength(buffer.Length());
+ mSocket->SendWithTrackingNumber(autoCx, data, 0, byteLength, aTrackingNumber, rv);
+ break;
+ }
+
+ case SendableData::TnsCString: {
+ const nsCString& strData = aData.get_nsCString();
+ mSocket->SendWithTrackingNumber(strData, aTrackingNumber, rv);
+ break;
+ }
+
+ default:
+ MOZ_CRASH("unexpected SendableData type");
+ }
+ NS_ENSURE_SUCCESS(rv.StealNSResult(), true);
+ return true;
+}
+
+bool
+TCPSocketParent::RecvClose()
+{
+ NS_ENSURE_TRUE(mSocket, true);
+ mSocket->Close();
+ return true;
+}
+
+void
+TCPSocketParent::FireErrorEvent(const nsAString& aName, const nsAString& aType, TCPReadyState aReadyState)
+{
+ SendEvent(NS_LITERAL_STRING("error"), TCPError(nsString(aName), nsString(aType)), aReadyState);
+}
+
+void
+TCPSocketParent::FireEvent(const nsAString& aType, TCPReadyState aReadyState)
+{
+ return SendEvent(aType, mozilla::void_t(), aReadyState);
+}
+
+void
+TCPSocketParent::FireArrayBufferDataEvent(nsTArray<uint8_t>& aBuffer, TCPReadyState aReadyState)
+{
+ InfallibleTArray<uint8_t> arr;
+ arr.SwapElements(aBuffer);
+
+ if (mFilter) {
+ bool allowed;
+ mozilla::net::NetAddr addr;
+ nsresult nsrv = mFilter->FilterPacket(&addr, arr.Elements(), arr.Length(),
+ nsISocketFilter::SF_INCOMING,
+ &allowed);
+ // receiving unallowed data, drop it.
+ if (NS_WARN_IF(NS_FAILED(nsrv)) || !allowed) {
+ TCPSOCKET_LOG(("%s: Dropping incoming TCP packet", __FUNCTION__));
+ return;
+ }
+ }
+
+ SendableData data(arr);
+ SendEvent(NS_LITERAL_STRING("data"), data, aReadyState);
+}
+
+void
+TCPSocketParent::FireStringDataEvent(const nsACString& aData, TCPReadyState aReadyState)
+{
+ SendableData data((nsCString(aData)));
+
+ MOZ_ASSERT(!mFilter, "Socket filtering doesn't support nsCString");
+
+ SendEvent(NS_LITERAL_STRING("data"), data, aReadyState);
+}
+
+void
+TCPSocketParent::SendEvent(const nsAString& aType, CallbackData aData, TCPReadyState aReadyState)
+{
+ if (mIPCOpen) {
+ mozilla::Unused << PTCPSocketParent::SendCallback(nsString(aType),
+ aData,
+ static_cast<uint32_t>(aReadyState));
+ }
+}
+
+void
+TCPSocketParent::SetSocket(TCPSocket *socket)
+{
+ mSocket = socket;
+}
+
+nsresult
+TCPSocketParent::GetHost(nsAString& aHost)
+{
+ if (!mSocket) {
+ NS_ERROR("No internal socket instance mSocket!");
+ return NS_ERROR_FAILURE;
+ }
+ mSocket->GetHost(aHost);
+ return NS_OK;
+}
+
+nsresult
+TCPSocketParent::GetPort(uint16_t* aPort)
+{
+ if (!mSocket) {
+ NS_ERROR("No internal socket instance mSocket!");
+ return NS_ERROR_FAILURE;
+ }
+ *aPort = mSocket->Port();
+ return NS_OK;
+}
+
+void
+TCPSocketParent::ActorDestroy(ActorDestroyReason why)
+{
+ if (mSocket) {
+ mSocket->Close();
+ }
+ mSocket = nullptr;
+}
+
+bool
+TCPSocketParent::RecvRequestDelete()
+{
+ mozilla::Unused << Send__delete__(this);
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla