summaryrefslogtreecommitdiffstats
path: root/dom/media/webrtc/transport/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/webrtc/transport/ipc')
-rw-r--r--dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h54
-rw-r--r--dom/media/webrtc/transport/ipc/PStunAddrsParams.h33
-rw-r--r--dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl35
-rw-r--r--dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl42
-rw-r--r--dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp45
-rw-r--r--dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h64
-rw-r--r--dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp262
-rw-r--r--dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h82
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh23
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp785
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h104
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h28
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp96
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h47
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp11
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h20
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp122
-rw-r--r--dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h59
-rw-r--r--dom/media/webrtc/transport/ipc/moz.build54
19 files changed, 1966 insertions, 0 deletions
diff --git a/dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h b/dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h
new file mode 100644
index 0000000000..34ec46726e
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/NrIceStunAddrMessageUtils.h
@@ -0,0 +1,54 @@
+/* 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/. */
+
+#ifndef mozilla_net_NrIceStunAddrMessageUtils_h
+#define mozilla_net_NrIceStunAddrMessageUtils_h
+
+// forward declare NrIceStunAddr for --disable-webrtc builds where
+// the header will not be available.
+namespace mozilla {
+class NrIceStunAddr;
+} // namespace mozilla
+
+#include "ipc/IPCMessageUtils.h"
+#ifdef MOZ_WEBRTC
+# include "transport/nricestunaddr.h"
+#endif
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::NrIceStunAddr> {
+ static void Write(MessageWriter* aWriter,
+ const mozilla::NrIceStunAddr& aParam) {
+#ifdef MOZ_WEBRTC
+ const size_t bufSize = aParam.SerializationBufferSize();
+ char* buffer = new char[bufSize];
+ aParam.Serialize(buffer, bufSize);
+ aWriter->WriteBytes((void*)buffer, bufSize);
+ delete[] buffer;
+#endif
+ }
+
+ static bool Read(MessageReader* aReader, mozilla::NrIceStunAddr* aResult) {
+#ifdef MOZ_WEBRTC
+ const size_t bufSize = aResult->SerializationBufferSize();
+ char* buffer = new char[bufSize];
+ bool result = aReader->ReadBytesInto((void*)buffer, bufSize);
+
+ if (result) {
+ result = result && (NS_OK == aResult->Deserialize(buffer, bufSize));
+ }
+ delete[] buffer;
+
+ return result;
+#else
+ return false;
+#endif
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_net_NrIceStunAddrMessageUtils_h
diff --git a/dom/media/webrtc/transport/ipc/PStunAddrsParams.h b/dom/media/webrtc/transport/ipc/PStunAddrsParams.h
new file mode 100644
index 0000000000..315925609d
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/PStunAddrsParams.h
@@ -0,0 +1,33 @@
+/* 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/. */
+
+#ifndef PStunAddrsParams_h
+#define PStunAddrsParams_h
+
+#include "mozilla/Maybe.h"
+#include "nsTArray.h"
+
+#ifdef MOZ_WEBRTC
+# include "transport/nricestunaddr.h"
+#endif
+
+namespace mozilla {
+namespace net {
+
+// Need to define typedef in .h file--can't seem to in ipdl.h file?
+#ifdef MOZ_WEBRTC
+typedef nsTArray<NrIceStunAddr> NrIceStunAddrArray;
+#else
+// a "dummy" typedef for --disabled-webrtc builds when the definition
+// for NrIceStunAddr is not available (otherwise we get complaints
+// about missing definitions for contructor and destructor)
+typedef nsTArray<int> NrIceStunAddrArray;
+#endif
+
+typedef Maybe<nsCString> MaybeNsCString;
+
+} // namespace net
+} // namespace mozilla
+
+#endif // PStunAddrsParams_h
diff --git a/dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl b/dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl
new file mode 100644
index 0000000000..c775f73f99
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/PStunAddrsRequest.ipdl
@@ -0,0 +1,35 @@
+/* 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 protocol PNecko;
+
+using mozilla::net::NrIceStunAddrArray from "mozilla/net/PStunAddrsParams.h";
+using mozilla::net::MaybeNsCString from "mozilla/net/PStunAddrsParams.h";
+
+include "mozilla/net/NrIceStunAddrMessageUtils.h";
+
+namespace mozilla {
+namespace net {
+
+[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual]
+async protocol PStunAddrsRequest
+{
+ manager PNecko;
+
+parent:
+ async GetStunAddrs();
+
+ async RegisterMDNSHostname(nsCString hostname, nsCString address);
+ async QueryMDNSHostname(nsCString hostname);
+ async UnregisterMDNSHostname(nsCString hostname);
+
+ async __delete__();
+
+child:
+ async OnMDNSQueryComplete(nsCString hostname, MaybeNsCString address);
+ async OnStunAddrsAvailable(NrIceStunAddrArray iceStunAddrs);
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl b/dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl
new file mode 100644
index 0000000000..3bc2a89828
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/PWebrtcTCPSocket.ipdl
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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 protocol PNecko;
+include protocol PSocketProcess;
+
+include WebrtcProxyConfig;
+
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+
+namespace mozilla {
+namespace net {
+
+[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual]
+async protocol PWebrtcTCPSocket
+{
+ manager PNecko or PSocketProcess;
+
+parent:
+ async AsyncOpen(nsCString aHost,
+ int32_t aPort,
+ nsCString aLocalAddress,
+ int32_t aLocalPort,
+ bool aUseTls,
+ WebrtcProxyConfig? aProxyConfig);
+ async Write(uint8_t[] aWriteData);
+ async Close();
+
+child:
+ async OnClose(nsresult aReason);
+ async OnConnected(nsCString aProxyType);
+ async OnRead(uint8_t[] aReadData);
+
+ async __delete__();
+};
+
+} // namespace net
+} // namespace mozilla
diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp
new file mode 100644
index 0000000000..057fcbd330
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.cpp
@@ -0,0 +1,45 @@
+/* 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 "StunAddrsRequestChild.h"
+
+#include "mozilla/net/NeckoChild.h"
+#include "nsISerialEventTarget.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla::net {
+
+StunAddrsRequestChild::StunAddrsRequestChild(StunAddrsListener* listener)
+ : mListener(listener) {
+ gNeckoChild->SendPStunAddrsRequestConstructor(this);
+ // IPDL holds a reference until IPDL channel gets destroyed
+ AddIPDLReference();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestChild::RecvOnMDNSQueryComplete(
+ const nsACString& hostname, const Maybe<nsCString>& address) {
+ if (mListener) {
+ mListener->OnMDNSQueryComplete(PromiseFlatCString(hostname), address);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestChild::RecvOnStunAddrsAvailable(
+ const NrIceStunAddrArray& addrs) {
+ if (mListener) {
+ mListener->OnStunAddrsAvailable(addrs);
+ }
+ return IPC_OK();
+}
+
+void StunAddrsRequestChild::Cancel() { mListener = nullptr; }
+
+NS_IMPL_ADDREF(StunAddrsRequestChild)
+NS_IMPL_RELEASE(StunAddrsRequestChild)
+
+NS_IMPL_ADDREF(StunAddrsListener)
+NS_IMPL_RELEASE(StunAddrsListener)
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h
new file mode 100644
index 0000000000..f487f52baf
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestChild.h
@@ -0,0 +1,64 @@
+/* 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/. */
+
+#ifndef mozilla_net_StunAddrsRequestChild_h
+#define mozilla_net_StunAddrsRequestChild_h
+
+#include "mozilla/net/PStunAddrsRequestChild.h"
+
+class nsISerialEventTarget;
+
+namespace mozilla::net {
+
+class StunAddrsListener {
+ public:
+ virtual void OnMDNSQueryComplete(const nsCString& hostname,
+ const Maybe<nsCString>& address) = 0;
+ virtual void OnStunAddrsAvailable(const NrIceStunAddrArray& addrs) = 0;
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+
+ protected:
+ virtual ~StunAddrsListener() = default;
+
+ ThreadSafeAutoRefCnt mRefCnt;
+ NS_DECL_OWNINGTHREAD
+};
+
+class StunAddrsRequestChild final : public PStunAddrsRequestChild {
+ friend class PStunAddrsRequestChild;
+
+ public:
+ explicit StunAddrsRequestChild(StunAddrsListener* listener);
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+
+ // Not sure why AddIPDLReference & ReleaseIPDLReference don't come
+ // from PStunAddrsRequestChild since the IPC plumbing seem to
+ // expect this.
+ void AddIPDLReference() { AddRef(); }
+ void ReleaseIPDLReference() { Release(); }
+
+ void Cancel();
+
+ protected:
+ virtual ~StunAddrsRequestChild() = default;
+
+ virtual mozilla::ipc::IPCResult RecvOnMDNSQueryComplete(
+ const nsACString& aHostname, const Maybe<nsCString>& aAddress) override;
+
+ virtual mozilla::ipc::IPCResult RecvOnStunAddrsAvailable(
+ const NrIceStunAddrArray& addrs) override;
+
+ RefPtr<StunAddrsListener> mListener;
+
+ ThreadSafeAutoRefCnt mRefCnt;
+ NS_DECL_OWNINGTHREAD
+};
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_StunAddrsRequestChild_h
diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp
new file mode 100644
index 0000000000..23ef6dea73
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.cpp
@@ -0,0 +1,262 @@
+/* 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 "StunAddrsRequestParent.h"
+
+#include "../runnable_utils.h"
+#include "mozilla/StaticPtr.h"
+#include "nsIThread.h"
+#include "nsNetUtil.h"
+
+#include "transport/nricectx.h"
+#include "transport/nricemediastream.h" // needed only for including nricectx.h
+#include "transport/nricestunaddr.h"
+
+#include "../mdns_service/mdns_service.h"
+
+extern "C" {
+#include "local_addr.h"
+}
+
+using namespace mozilla::ipc;
+
+namespace mozilla::net {
+
+static void mdns_service_resolved(void* cb, const char* hostname,
+ const char* addr) {
+ StunAddrsRequestParent* self = static_cast<StunAddrsRequestParent*>(cb);
+ self->OnQueryComplete(nsCString(hostname), Some(nsCString(addr)));
+}
+
+void mdns_service_timedout(void* cb, const char* hostname) {
+ StunAddrsRequestParent* self = static_cast<StunAddrsRequestParent*>(cb);
+ self->OnQueryComplete(nsCString(hostname), Nothing());
+}
+
+StunAddrsRequestParent::StunAddrsRequestParent() : mIPCClosed(false) {
+ NS_GetMainThread(getter_AddRefs(mMainThread));
+
+ nsresult res;
+ mSTSThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &res);
+ MOZ_ASSERT(mSTSThread);
+}
+
+StunAddrsRequestParent::~StunAddrsRequestParent() {
+ ASSERT_ON_THREAD(mMainThread);
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestParent::RecvGetStunAddrs() {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ return IPC_OK();
+ }
+
+ RUN_ON_THREAD(mSTSThread,
+ WrapRunnable(RefPtr<StunAddrsRequestParent>(this),
+ &StunAddrsRequestParent::GetStunAddrs_s),
+ NS_DISPATCH_NORMAL);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestParent::RecvRegisterMDNSHostname(
+ const nsACString& aHostname, const nsACString& aAddress) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ return IPC_OK();
+ }
+
+ if (mSharedMDNSService) {
+ mSharedMDNSService->RegisterHostname(aHostname.BeginReading(),
+ aAddress.BeginReading());
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestParent::RecvQueryMDNSHostname(
+ const nsACString& aHostname) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ return IPC_OK();
+ }
+
+ if (mSharedMDNSService) {
+ mSharedMDNSService->QueryHostname(this, aHostname.BeginReading());
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestParent::RecvUnregisterMDNSHostname(
+ const nsACString& aHostname) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ return IPC_OK();
+ }
+
+ if (mSharedMDNSService) {
+ mSharedMDNSService->UnregisterHostname(aHostname.BeginReading());
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult StunAddrsRequestParent::Recv__delete__() {
+ // see note below in ActorDestroy
+ mIPCClosed = true;
+ return IPC_OK();
+}
+
+void StunAddrsRequestParent::OnQueryComplete(const nsACString& hostname,
+ const Maybe<nsCString>& address) {
+ RUN_ON_THREAD(mMainThread,
+ WrapRunnable(RefPtr<StunAddrsRequestParent>(this),
+ &StunAddrsRequestParent::OnQueryComplete_m,
+ nsCString(hostname), address),
+ NS_DISPATCH_NORMAL);
+}
+
+void StunAddrsRequestParent::ActorDestroy(ActorDestroyReason why) {
+ // We may still have refcount>0 if we haven't made it through
+ // GetStunAddrs_s and SendStunAddrs_m yet, but child process
+ // has crashed. We must not send any more msgs to child, or
+ // IPDL will kill chrome process, too.
+ mIPCClosed = true;
+
+ // We need to stop the mDNS service here to ensure that we don't
+ // end up with any messages queued for the main thread after the
+ // destructors run. Because of Bug 1569311, all of the
+ // StunAddrsRequestParent instances end up being destroyed one
+ // after the other, so it is ok to free the shared service when
+ // the first one is destroyed rather than waiting for the last one.
+ // If this behaviour changes, we would potentially end up starting
+ // and stopping instances repeatedly and should add a refcount and
+ // a way of cancelling pending queries to avoid churn in that case.
+ if (mSharedMDNSService) {
+ mSharedMDNSService = nullptr;
+ }
+}
+
+void StunAddrsRequestParent::GetStunAddrs_s() {
+ ASSERT_ON_THREAD(mSTSThread);
+
+ // get the stun addresses while on STS thread
+ NrIceStunAddrArray addrs = NrIceCtx::GetStunAddrs();
+
+ if (mIPCClosed) {
+ return;
+ }
+
+ // in order to return the result over IPC, we need to be on main thread
+ RUN_ON_THREAD(
+ mMainThread,
+ WrapRunnable(RefPtr<StunAddrsRequestParent>(this),
+ &StunAddrsRequestParent::SendStunAddrs_m, std::move(addrs)),
+ NS_DISPATCH_NORMAL);
+}
+
+void StunAddrsRequestParent::SendStunAddrs_m(const NrIceStunAddrArray& addrs) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ // nothing to do: child probably crashed
+ return;
+ }
+
+ // This means that the mDNS service will continue running until shutdown
+ // once started. The StunAddrsRequestParent destructor does not run until
+ // shutdown anyway (see Bug 1569311), so there is not much we can do about
+ // this here. One option would be to add a check if there are no hostnames
+ // registered after UnregisterHostname is called, and if so, stop the mDNS
+ // service at that time (see Bug 1569955.)
+ if (!mSharedMDNSService) {
+ std::ostringstream o;
+ char buffer[16];
+ for (auto& addr : addrs) {
+ if (addr.localAddr().addr.ip_version == NR_IPV4 &&
+ !nr_transport_addr_is_loopback(&addr.localAddr().addr)) {
+ nr_transport_addr_get_addrstring(&addr.localAddr().addr, buffer, 16);
+ o << buffer << ";";
+ }
+ }
+ std::string addrstring = o.str();
+ if (!addrstring.empty()) {
+ mSharedMDNSService = new MDNSServiceWrapper(addrstring);
+ }
+ }
+
+ // send the new addresses back to the child
+ Unused << SendOnStunAddrsAvailable(addrs);
+}
+
+void StunAddrsRequestParent::OnQueryComplete_m(
+ const nsACString& hostname, const Maybe<nsCString>& address) {
+ ASSERT_ON_THREAD(mMainThread);
+
+ if (mIPCClosed) {
+ // nothing to do: child probably crashed
+ return;
+ }
+
+ // send the hostname and address back to the child
+ Unused << SendOnMDNSQueryComplete(hostname, address);
+}
+
+StaticRefPtr<StunAddrsRequestParent::MDNSServiceWrapper>
+ StunAddrsRequestParent::mSharedMDNSService;
+
+NS_IMPL_ADDREF(StunAddrsRequestParent)
+NS_IMPL_RELEASE(StunAddrsRequestParent)
+
+StunAddrsRequestParent::MDNSServiceWrapper::MDNSServiceWrapper(
+ const std::string& ifaddr)
+ : ifaddr(ifaddr) {}
+
+void StunAddrsRequestParent::MDNSServiceWrapper::RegisterHostname(
+ const char* hostname, const char* address) {
+ StartIfRequired();
+ if (mMDNSService) {
+ mdns_service_register_hostname(mMDNSService, hostname, address);
+ }
+}
+
+void StunAddrsRequestParent::MDNSServiceWrapper::QueryHostname(
+ void* data, const char* hostname) {
+ StartIfRequired();
+ if (mMDNSService) {
+ mdns_service_query_hostname(mMDNSService, data, mdns_service_resolved,
+ mdns_service_timedout, hostname);
+ }
+}
+
+void StunAddrsRequestParent::MDNSServiceWrapper::UnregisterHostname(
+ const char* hostname) {
+ StartIfRequired();
+ if (mMDNSService) {
+ mdns_service_unregister_hostname(mMDNSService, hostname);
+ }
+}
+
+StunAddrsRequestParent::MDNSServiceWrapper::~MDNSServiceWrapper() {
+ if (mMDNSService) {
+ mdns_service_stop(mMDNSService);
+ mMDNSService = nullptr;
+ }
+}
+
+void StunAddrsRequestParent::MDNSServiceWrapper::StartIfRequired() {
+ if (!mMDNSService) {
+ mMDNSService = mdns_service_start(ifaddr.c_str());
+ }
+}
+
+NS_IMPL_ADDREF(StunAddrsRequestParent::MDNSServiceWrapper)
+NS_IMPL_RELEASE(StunAddrsRequestParent::MDNSServiceWrapper)
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h
new file mode 100644
index 0000000000..33e71abbc7
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/StunAddrsRequestParent.h
@@ -0,0 +1,82 @@
+/* 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/. */
+
+#ifndef mozilla_net_StunAddrsRequestParent_h
+#define mozilla_net_StunAddrsRequestParent_h
+
+#include "mozilla/net/PStunAddrsRequestParent.h"
+
+struct MDNSService;
+
+namespace mozilla::net {
+
+class StunAddrsRequestParent : public PStunAddrsRequestParent {
+ friend class PStunAddrsRequestParent;
+
+ public:
+ StunAddrsRequestParent();
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+
+ mozilla::ipc::IPCResult Recv__delete__() override;
+
+ void OnQueryComplete(const nsACString& hostname,
+ const Maybe<nsCString>& address);
+
+ protected:
+ virtual ~StunAddrsRequestParent();
+
+ virtual mozilla::ipc::IPCResult RecvGetStunAddrs() override;
+ virtual mozilla::ipc::IPCResult RecvRegisterMDNSHostname(
+ const nsACString& hostname, const nsACString& address) override;
+ virtual mozilla::ipc::IPCResult RecvQueryMDNSHostname(
+ const nsACString& hostname) override;
+ virtual mozilla::ipc::IPCResult RecvUnregisterMDNSHostname(
+ const nsACString& hostname) override;
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ nsCOMPtr<nsIThread> mMainThread;
+ nsCOMPtr<nsISerialEventTarget> mSTSThread;
+
+ void GetStunAddrs_s();
+ void SendStunAddrs_m(const NrIceStunAddrArray& addrs);
+
+ void OnQueryComplete_m(const nsACString& hostname,
+ const Maybe<nsCString>& address);
+
+ ThreadSafeAutoRefCnt mRefCnt;
+ NS_DECL_OWNINGTHREAD
+
+ private:
+ bool mIPCClosed; // true if IPDL channel has been closed (child crash)
+
+ class MDNSServiceWrapper {
+ public:
+ explicit MDNSServiceWrapper(const std::string& ifaddr);
+ void RegisterHostname(const char* hostname, const char* address);
+ void QueryHostname(void* data, const char* hostname);
+ void UnregisterHostname(const char* hostname);
+
+ NS_IMETHOD_(MozExternalRefCountType) AddRef();
+ NS_IMETHOD_(MozExternalRefCountType) Release();
+
+ protected:
+ ThreadSafeAutoRefCnt mRefCnt;
+ NS_DECL_OWNINGTHREAD
+
+ private:
+ virtual ~MDNSServiceWrapper();
+ void StartIfRequired();
+
+ std::string ifaddr;
+ MDNSService* mMDNSService = nullptr;
+ };
+
+ static StaticRefPtr<MDNSServiceWrapper> mSharedMDNSService;
+};
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_StunAddrsRequestParent_h
diff --git a/dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh b/dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh
new file mode 100644
index 0000000000..e93e82a6a3
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcProxyConfig.ipdlh
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* 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/. */
+
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+include NeckoChannelParams;
+
+namespace mozilla {
+namespace net {
+
+struct WebrtcProxyConfig {
+ TabId tabId;
+ nsCString alpn;
+ LoadInfoArgs loadInfoArgs;
+ bool forceProxy;
+};
+
+} // namespace net
+} // namespace mozilla
+
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp
new file mode 100644
index 0000000000..447b4cc741
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.cpp
@@ -0,0 +1,785 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 "WebrtcTCPSocket.h"
+
+#include "nsHttpChannel.h"
+#include "nsIChannel.h"
+#include "nsIClassOfService.h"
+#include "nsIContentPolicy.h"
+#include "nsIIOService.h"
+#include "nsILoadInfo.h"
+#include "nsIProtocolProxyService.h"
+#include "nsIURIMutator.h"
+#include "nsICookieJarSettings.h"
+#include "nsProxyRelease.h"
+#include "nsString.h"
+#include "mozilla/dom/ContentProcessManager.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "nsISocketTransportService.h"
+#include "nsICancelable.h"
+#include "nsSocketTransportService2.h"
+
+#include "WebrtcTCPSocketCallback.h"
+#include "WebrtcTCPSocketLog.h"
+
+namespace mozilla::net {
+
+class WebrtcTCPData {
+ public:
+ explicit WebrtcTCPData(nsTArray<uint8_t>&& aData) : mData(std::move(aData)) {
+ MOZ_COUNT_CTOR(WebrtcTCPData);
+ }
+
+ MOZ_COUNTED_DTOR(WebrtcTCPData)
+
+ const nsTArray<uint8_t>& GetData() const { return mData; }
+
+ private:
+ nsTArray<uint8_t> mData;
+};
+
+NS_IMPL_ISUPPORTS(WebrtcTCPSocket, nsIAuthPromptProvider,
+ nsIHttpUpgradeListener, nsIInputStreamCallback,
+ nsIInterfaceRequestor, nsIOutputStreamCallback,
+ nsIRequestObserver, nsIStreamListener,
+ nsIProtocolProxyCallback)
+
+WebrtcTCPSocket::WebrtcTCPSocket(WebrtcTCPSocketCallback* aCallbacks)
+ : mProxyCallbacks(aCallbacks),
+ mClosed(false),
+ mOpened(false),
+ mWriteOffset(0),
+ mAuthProvider(nullptr),
+ mTransport(nullptr),
+ mSocketIn(nullptr),
+ mSocketOut(nullptr) {
+ LOG(("WebrtcTCPSocket::WebrtcTCPSocket %p\n", this));
+ mMainThread = GetMainThreadSerialEventTarget();
+ mSocketThread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
+ MOZ_RELEASE_ASSERT(mMainThread, "no main thread");
+ MOZ_RELEASE_ASSERT(mSocketThread, "no socket thread");
+}
+
+WebrtcTCPSocket::~WebrtcTCPSocket() {
+ LOG(("WebrtcTCPSocket::~WebrtcTCPSocket %p\n", this));
+
+ NS_ProxyRelease("WebrtcTCPSocket::CleanUpAuthProvider", mMainThread,
+ mAuthProvider.forget());
+}
+
+void WebrtcTCPSocket::SetTabId(dom::TabId aTabId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ dom::ContentProcessManager* cpm = dom::ContentProcessManager::GetSingleton();
+ if (cpm) {
+ dom::ContentParentId cpId = cpm->GetTabProcessId(aTabId);
+ mAuthProvider = cpm->GetBrowserParentByProcessAndTabId(cpId, aTabId);
+ }
+}
+
+nsresult WebrtcTCPSocket::Write(nsTArray<uint8_t>&& aWriteData) {
+ LOG(("WebrtcTCPSocket::Write %p\n", this));
+ MOZ_ASSERT(NS_IsMainThread());
+ nsresult rv = mSocketThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>(
+ "WebrtcTCPSocket::Write", this, &WebrtcTCPSocket::EnqueueWrite_s,
+ std::move(aWriteData)));
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS");
+
+ return rv;
+}
+
+nsresult WebrtcTCPSocket::Close() {
+ LOG(("WebrtcTCPSocket::Close %p\n", this));
+
+ CloseWithReason(NS_OK);
+
+ return NS_OK;
+}
+
+void WebrtcTCPSocket::CloseWithReason(nsresult aReason) {
+ LOG(("WebrtcTCPSocket::CloseWithReason %p reason=%u\n", this,
+ static_cast<uint32_t>(aReason)));
+
+ if (!OnSocketThread()) {
+ MOZ_ASSERT(NS_IsMainThread(), "not on main thread");
+
+ // Let's pretend we got an open even if we didn't to prevent an Open later.
+ mOpened = true;
+
+ DebugOnly<nsresult> rv =
+ mSocketThread->Dispatch(NewRunnableMethod<nsresult>(
+ "WebrtcTCPSocket::CloseWithReason", this,
+ &WebrtcTCPSocket::CloseWithReason, aReason));
+
+ // This was MOZ_ALWAYS_SUCCEEDS, but that now uses NS_WARNING_ASSERTION.
+ // In order to convert this back to MOZ_ALWAYS_SUCCEEDS we would need
+ // OnSocketThread to return true if we're shutting down and doing the
+ // "running all of STS's queued events on main" thing.
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS");
+
+ return;
+ }
+
+ if (mClosed) {
+ return;
+ }
+
+ mClosed = true;
+
+ if (mSocketIn) {
+ mSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
+ mSocketIn = nullptr;
+ }
+
+ if (mSocketOut) {
+ mSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
+ mSocketOut = nullptr;
+ }
+
+ if (mTransport) {
+ mTransport->Close(NS_BASE_STREAM_CLOSED);
+ mTransport = nullptr;
+ }
+
+ NS_ProxyRelease("WebrtcTCPSocket::CleanUpAuthProvider", mMainThread,
+ mAuthProvider.forget());
+ InvokeOnClose(aReason);
+}
+
+nsresult WebrtcTCPSocket::Open(
+ const nsACString& aHost, const int& aPort, const nsACString& aLocalAddress,
+ const int& aLocalPort, bool aUseTls,
+ const Maybe<net::WebrtcProxyConfig>& aProxyConfig) {
+ LOG(("WebrtcTCPSocket::Open %p remote-host=%s local-addr=%s local-port=%d",
+ this, PromiseFlatCString(aHost).get(),
+ PromiseFlatCString(aLocalAddress).get(), aLocalPort));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (NS_WARN_IF(mOpened)) {
+ LOG(("WebrtcTCPSocket %p: TCP socket already open\n", this));
+ CloseWithReason(NS_ERROR_FAILURE);
+ return NS_ERROR_FAILURE;
+ }
+
+ mOpened = true;
+ const nsLiteralCString schemePrefix = aUseTls ? "https://"_ns : "http://"_ns;
+ nsAutoCString spec(schemePrefix);
+
+ bool ipv6Literal = aHost.Find(":") != kNotFound;
+ if (ipv6Literal) {
+ spec += "[";
+ spec += aHost;
+ spec += "]";
+ } else {
+ spec += aHost;
+ }
+
+ nsresult rv = NS_MutateURI(NS_STANDARDURLMUTATOR_CONTRACTID)
+ .SetSpec(spec)
+ .SetPort(aPort)
+ .Finalize(mURI);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(NS_ERROR_FAILURE);
+ return NS_ERROR_FAILURE;
+ }
+
+ mTls = aUseTls;
+ mLocalAddress = aLocalAddress;
+ mLocalPort = aLocalPort;
+ mProxyConfig = aProxyConfig;
+
+ if (!mProxyConfig.isSome()) {
+ OpenWithoutHttpProxy(nullptr);
+ return NS_OK;
+ }
+
+ // 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(rv);
+ }
+
+ return rv;
+}
+
+nsresult WebrtcTCPSocket::DoProxyConfigLookup() {
+ MOZ_ASSERT(NS_IsMainThread());
+ nsresult rv;
+ nsCOMPtr<nsIProtocolProxyService> pps =
+ do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_NewChannel(getter_AddRefs(channel), mURI,
+ 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_HTTPS_PROXY |
+ nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
+ this, nullptr, getter_AddRefs(mProxyRequest));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // We pick back up in OnProxyAvailable
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP WebrtcTCPSocket::OnProxyAvailable(nsICancelable* aRequest,
+ nsIChannel* aChannel,
+ nsIProxyInfo* aProxyinfo,
+ nsresult aResult) {
+ MOZ_ASSERT(NS_IsMainThread());
+ mProxyRequest = nullptr;
+
+ if (NS_SUCCEEDED(aResult) && aProxyinfo) {
+ nsresult rv = aProxyinfo->GetType(mProxyType);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return rv;
+ }
+
+ if (mProxyType == "http" || mProxyType == "https") {
+ rv = OpenWithHttpProxy();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ }
+ return rv;
+ }
+
+ if (mProxyType == "socks" || mProxyType == "socks4" ||
+ mProxyType == "direct") {
+ OpenWithoutHttpProxy(aProxyinfo);
+ return NS_OK;
+ }
+ }
+
+ OpenWithoutHttpProxy(nullptr);
+
+ return NS_OK;
+}
+
+void WebrtcTCPSocket::OpenWithoutHttpProxy(nsIProxyInfo* aSocksProxyInfo) {
+ if (!OnSocketThread()) {
+ DebugOnly<nsresult> rv =
+ mSocketThread->Dispatch(NewRunnableMethod<nsCOMPtr<nsIProxyInfo>>(
+ "WebrtcTCPSocket::OpenWithoutHttpProxy", this,
+ &WebrtcTCPSocket::OpenWithoutHttpProxy, aSocksProxyInfo));
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to dispatch to STS");
+ return;
+ }
+
+ LOG(("WebrtcTCPSocket::OpenWithoutHttpProxy %p\n", this));
+
+ if (mClosed) {
+ return;
+ }
+
+ if (NS_WARN_IF(mProxyConfig.isSome() && mProxyConfig->forceProxy() &&
+ !aSocksProxyInfo)) {
+ CloseWithReason(NS_ERROR_FAILURE);
+ return;
+ }
+
+ nsCString host;
+ int32_t port;
+
+ nsresult rv = mURI->GetHost(host);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+
+ rv = mURI->GetPort(&port);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+
+ AutoTArray<nsCString, 1> socketTypes;
+ if (mTls) {
+ socketTypes.AppendElement("ssl"_ns);
+ }
+
+ nsCOMPtr<nsISocketTransportService> sts =
+ do_GetService("@mozilla.org/network/socket-transport-service;1");
+ rv = sts->CreateTransport(socketTypes, host, port, aSocksProxyInfo, nullptr,
+ getter_AddRefs(mTransport));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+
+ mTransport->SetReuseAddrPort(true);
+
+ PRNetAddr prAddr;
+ if (NS_WARN_IF(PR_SUCCESS !=
+ PR_InitializeNetAddr(PR_IpAddrAny, mLocalPort, &prAddr))) {
+ CloseWithReason(NS_ERROR_FAILURE);
+ return;
+ }
+
+ if (NS_WARN_IF(PR_SUCCESS !=
+ PR_StringToNetAddr(mLocalAddress.BeginReading(), &prAddr))) {
+ CloseWithReason(NS_ERROR_FAILURE);
+ return;
+ }
+
+ mozilla::net::NetAddr addr(&prAddr);
+ rv = mTransport->Bind(&addr);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+
+ // Binding to a V4 address is not sufficient to cause this socket to use
+ // V4, and the same goes for V6. So, we disable as needed here.
+ uint32_t flags = 0;
+ if (addr.raw.family == AF_INET) {
+ flags |= nsISocketTransport::DISABLE_IPV6;
+ } else if (addr.raw.family == AF_INET6) {
+ flags |= nsISocketTransport::DISABLE_IPV4;
+ } else {
+ MOZ_CRASH();
+ }
+
+ mTransport->SetConnectionFlags(flags);
+
+ nsCOMPtr<nsIInputStream> socketIn;
+ rv = mTransport->OpenInputStream(0, 0, 0, getter_AddRefs(socketIn));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+ mSocketIn = do_QueryInterface(socketIn);
+ if (NS_WARN_IF(!mSocketIn)) {
+ CloseWithReason(NS_ERROR_NULL_POINTER);
+ return;
+ }
+
+ nsCOMPtr<nsIOutputStream> socketOut;
+ rv = mTransport->OpenOutputStream(nsITransport::OPEN_UNBUFFERED, 0, 0,
+ getter_AddRefs(socketOut));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ CloseWithReason(rv);
+ return;
+ }
+ mSocketOut = do_QueryInterface(socketOut);
+ if (NS_WARN_IF(!mSocketOut)) {
+ CloseWithReason(NS_ERROR_NULL_POINTER);
+ return;
+ }
+
+ FinishOpen();
+}
+
+nsresult WebrtcTCPSocket::OpenWithHttpProxy() {
+ MOZ_ASSERT(NS_IsMainThread(), "not on main thread");
+ LOG(("WebrtcTCPSocket::OpenWithHttpProxy %p\n", this));
+ nsresult rv;
+ nsCOMPtr<nsIIOService> ioService;
+ ioService = do_GetService(NS_IOSERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket %p: io service missing\n", this));
+ return rv;
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ Maybe<net::LoadInfoArgs> loadInfoArgs = Some(mProxyConfig->loadInfoArgs());
+
+ // FIXME: We don't know the remote type of the process which provided these
+ // LoadInfoArgs. Pass in `NOT_REMOTE_TYPE` as the origin process to blindly
+ // accept whatever value was passed by the other side for now, as we aren't
+ // using it for security checks here.
+ // If this code ever starts checking the triggering remote type, this needs to
+ // be changed.
+ rv = ipc::LoadInfoArgsToLoadInfo(loadInfoArgs, NOT_REMOTE_TYPE,
+ getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket %p: could not init load info\n", this));
+ return rv;
+ }
+
+ // -need to always tunnel since we're using a proxy
+ // -there shouldn't be an opportunity to send cookies, but explicitly disallow
+ // them anyway.
+ // -the previous proxy tunnel didn't support redirects e.g. 307. don't need to
+ // introduce new behavior. can't follow redirects on connect anyway.
+ nsCOMPtr<nsIChannel> localChannel;
+ rv = ioService->NewChannelFromURIWithProxyFlags(
+ mURI, nullptr,
+ // Proxy flags are overridden by SetConnectOnly()
+ 0, loadInfo->LoadingNode(), loadInfo->GetLoadingPrincipal(),
+ loadInfo->TriggeringPrincipal(),
+ nsILoadInfo::SEC_COOKIES_OMIT |
+ // We need this flag to allow loads from any origin since this channel
+ // is being used to CONNECT to an HTTP proxy.
+ nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
+ nsIContentPolicy::TYPE_PROXIED_WEBRTC_MEDIA,
+ getter_AddRefs(localChannel));
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket %p: bad open channel\n", this));
+ return rv;
+ }
+
+ nsCOMPtr<nsILoadInfo> channelLoadInfo = localChannel->LoadInfo();
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
+ loadInfo->GetCookieJarSettings(getter_AddRefs(cookieJarSettings));
+ channelLoadInfo->SetCookieJarSettings(cookieJarSettings);
+
+ RefPtr<nsHttpChannel> httpChannel;
+ CallQueryInterface(localChannel, httpChannel.StartAssignment());
+
+ if (!httpChannel) {
+ LOG(("WebrtcTCPSocket %p: not an http channel\n", this));
+ return NS_ERROR_FAILURE;
+ }
+
+ httpChannel->SetNotificationCallbacks(this);
+
+ // don't block webrtc proxy setup with other requests
+ // often more than one of these channels will be created all at once by ICE
+ nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(localChannel);
+ if (cos) {
+ cos->AddClassFlags(nsIClassOfService::Unblocked |
+ nsIClassOfService::DontThrottle);
+ } else {
+ LOG(("WebrtcTCPSocket %p: could not set class of service\n", this));
+ return NS_ERROR_FAILURE;
+ }
+
+ rv = httpChannel->HTTPUpgrade(mProxyConfig->alpn(), this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ rv = httpChannel->SetConnectOnly();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ rv = httpChannel->AsyncOpen(this);
+
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket %p: cannot async open\n", this));
+ 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;
+}
+
+void WebrtcTCPSocket::EnqueueWrite_s(nsTArray<uint8_t>&& aWriteData) {
+ LOG(("WebrtcTCPSocket::EnqueueWrite %p\n", this));
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+
+ if (mClosed) {
+ return;
+ }
+
+ mWriteQueue.emplace_back(std::move(aWriteData));
+
+ if (mSocketOut) {
+ mSocketOut->AsyncWait(this, 0, 0, nullptr);
+ }
+}
+
+void WebrtcTCPSocket::InvokeOnClose(nsresult aReason) {
+ LOG(("WebrtcTCPSocket::InvokeOnClose %p\n", this));
+
+ if (!NS_IsMainThread()) {
+ MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch(
+ NewRunnableMethod<nsresult>("WebrtcTCPSocket::InvokeOnClose", this,
+ &WebrtcTCPSocket::InvokeOnClose, aReason)));
+ return;
+ }
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null");
+
+ if (mProxyRequest) {
+ mProxyRequest->Cancel(aReason);
+ mProxyRequest = nullptr;
+ }
+
+ mProxyCallbacks->OnClose(aReason);
+ mProxyCallbacks = nullptr;
+}
+
+void WebrtcTCPSocket::InvokeOnConnected() {
+ LOG(("WebrtcTCPSocket::InvokeOnConnected %p\n", this));
+
+ if (!NS_IsMainThread()) {
+ MOZ_ALWAYS_SUCCEEDS(mMainThread->Dispatch(
+ NewRunnableMethod("WebrtcTCPSocket::InvokeOnConnected", this,
+ &WebrtcTCPSocket::InvokeOnConnected)));
+ return;
+ }
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null");
+
+ mProxyCallbacks->OnConnected(mProxyType);
+}
+
+void WebrtcTCPSocket::InvokeOnRead(nsTArray<uint8_t>&& aReadData) {
+ LOG(("WebrtcTCPSocket::InvokeOnRead %p count=%zu\n", this,
+ aReadData.Length()));
+
+ if (!NS_IsMainThread()) {
+ MOZ_ALWAYS_SUCCEEDS(
+ mMainThread->Dispatch(NewRunnableMethod<nsTArray<uint8_t>&&>(
+ "WebrtcTCPSocket::InvokeOnRead", this,
+ &WebrtcTCPSocket::InvokeOnRead, std::move(aReadData))));
+ return;
+ }
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callback should be non-null");
+
+ mProxyCallbacks->OnRead(std::move(aReadData));
+}
+
+// nsIHttpUpgradeListener
+NS_IMETHODIMP
+WebrtcTCPSocket::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(("WebrtcTCPSocket::OnTransportAvailable %p\n", this));
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ MOZ_ASSERT(!mTransport,
+ "already called transport available on webrtc TCP socket");
+
+ // Cancel any pending callbacks. The caller doesn't always cancel these
+ // awaits. We need to make sure they don't get them.
+ aSocketIn->AsyncWait(nullptr, 0, 0, nullptr);
+ aSocketOut->AsyncWait(nullptr, 0, 0, nullptr);
+
+ if (mClosed) {
+ LOG(("WebrtcTCPSocket::OnTransportAvailable %p closed\n", this));
+ return NS_OK;
+ }
+
+ mTransport = aTransport;
+ mSocketIn = aSocketIn;
+ mSocketOut = aSocketOut;
+
+ // pulled from nr_socket_prsock.cpp
+ uint32_t minBufferSize = 256 * 1024;
+ nsresult rv = mTransport->SetSendBufferSize(minBufferSize);
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcProxyChannel::OnTransportAvailable %p send failed\n", this));
+ CloseWithReason(rv);
+ return rv;
+ }
+ rv = mTransport->SetRecvBufferSize(minBufferSize);
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcProxyChannel::OnTransportAvailable %p recv failed\n", this));
+ CloseWithReason(rv);
+ return rv;
+ }
+
+ FinishOpen();
+ return NS_OK;
+}
+
+void WebrtcTCPSocket::FinishOpen() {
+ MOZ_ASSERT(OnSocketThread());
+ // 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();
+}
+
+NS_IMETHODIMP
+WebrtcTCPSocket::OnUpgradeFailed(nsresult aErrorCode) {
+ LOG(("WebrtcTCPSocket::OnUpgradeFailed %p\n", this));
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ MOZ_ASSERT(!mTransport,
+ "already called transport available on webrtc TCP socket");
+
+ if (mClosed) {
+ LOG(("WebrtcTCPSocket::OnUpgradeFailed %p closed\n", this));
+ return NS_OK;
+ }
+
+ CloseWithReason(aErrorCode);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebrtcTCPSocket::OnWebSocketConnectionAvailable(
+ WebSocketConnectionBase* aConnection) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIRequestObserver (from nsIStreamListener)
+NS_IMETHODIMP
+WebrtcTCPSocket::OnStartRequest(nsIRequest* aRequest) {
+ LOG(("WebrtcTCPSocket::OnStartRequest %p\n", this));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+WebrtcTCPSocket::OnStopRequest(nsIRequest* aRequest, nsresult aStatusCode) {
+ LOG(("WebrtcTCPSocket::OnStopRequest %p status=%u\n", this,
+ static_cast<uint32_t>(aStatusCode)));
+
+ // see nsHttpChannel::ProcessFailedProxyConnect for most error codes
+ if (NS_FAILED(aStatusCode)) {
+ CloseWithReason(aStatusCode);
+ return aStatusCode;
+ }
+
+ return NS_OK;
+}
+
+// nsIStreamListener
+NS_IMETHODIMP
+WebrtcTCPSocket::OnDataAvailable(nsIRequest* aRequest,
+ nsIInputStream* aInputStream, uint64_t aOffset,
+ uint32_t aCount) {
+ LOG(("WebrtcTCPSocket::OnDataAvailable %p count=%u\n", this, aCount));
+ MOZ_ASSERT(0, "unreachable data available");
+ return NS_OK;
+}
+
+// nsIInputStreamCallback
+NS_IMETHODIMP
+WebrtcTCPSocket::OnInputStreamReady(nsIAsyncInputStream* in) {
+ LOG(("WebrtcTCPSocket::OnInputStreamReady %p unwritten=%zu\n", this,
+ CountUnwrittenBytes()));
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ MOZ_ASSERT(!mClosed, "webrtc TCP socket closed");
+ MOZ_ASSERT(mTransport, "webrtc TCP socket not connected");
+ MOZ_ASSERT(mSocketIn == in, "wrong input stream");
+
+ char buffer[9216];
+ uint32_t remainingCapacity = sizeof(buffer);
+ uint32_t read = 0;
+
+ while (remainingCapacity > 0) {
+ uint32_t count = 0;
+ nsresult rv = mSocketIn->Read(buffer + read, remainingCapacity, &count);
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ break;
+ }
+
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket::OnInputStreamReady %p failed %u\n", this,
+ static_cast<uint32_t>(rv)));
+ CloseWithReason(rv);
+ return rv;
+ }
+
+ // base stream closed
+ if (count == 0) {
+ LOG(("WebrtcTCPSocket::OnInputStreamReady %p connection closed\n", this));
+ CloseWithReason(NS_ERROR_FAILURE);
+ return NS_OK;
+ }
+
+ remainingCapacity -= count;
+ read += count;
+ }
+
+ if (read > 0) {
+ nsTArray<uint8_t> array(read);
+ array.AppendElements(buffer, read);
+
+ InvokeOnRead(std::move(array));
+ }
+
+ mSocketIn->AsyncWait(this, 0, 0, nullptr);
+
+ return NS_OK;
+}
+
+// nsIOutputStreamCallback
+NS_IMETHODIMP
+WebrtcTCPSocket::OnOutputStreamReady(nsIAsyncOutputStream* out) {
+ LOG(("WebrtcTCPSocket::OnOutputStreamReady %p unwritten=%zu\n", this,
+ CountUnwrittenBytes()));
+ MOZ_ASSERT(OnSocketThread(), "not on socket thread");
+ MOZ_ASSERT(!mClosed, "webrtc TCP socket closed");
+ MOZ_ASSERT(mTransport, "webrtc TCP socket not connected");
+ MOZ_ASSERT(mSocketOut == out, "wrong output stream");
+
+ while (!mWriteQueue.empty()) {
+ const WebrtcTCPData& data = mWriteQueue.front();
+
+ char* buffer = reinterpret_cast<char*>(
+ const_cast<uint8_t*>(data.GetData().Elements())) +
+ mWriteOffset;
+ uint32_t toWrite = data.GetData().Length() - mWriteOffset;
+
+ uint32_t wrote = 0;
+ nsresult rv = mSocketOut->Write(buffer, toWrite, &wrote);
+ if (rv == NS_BASE_STREAM_WOULD_BLOCK) {
+ mSocketOut->AsyncWait(this, 0, 0, nullptr);
+ return NS_OK;
+ }
+
+ if (NS_FAILED(rv)) {
+ LOG(("WebrtcTCPSocket::OnOutputStreamReady %p failed %u\n", this,
+ static_cast<uint32_t>(rv)));
+ CloseWithReason(rv);
+ return NS_OK;
+ }
+
+ mWriteOffset += wrote;
+
+ if (toWrite == wrote) {
+ mWriteOffset = 0;
+ mWriteQueue.pop_front();
+ }
+ }
+
+ return NS_OK;
+}
+
+// nsIInterfaceRequestor
+NS_IMETHODIMP
+WebrtcTCPSocket::GetInterface(const nsIID& iid, void** result) {
+ LOG(("WebrtcTCPSocket::GetInterface %p\n", this));
+
+ return QueryInterface(iid, result);
+}
+
+size_t WebrtcTCPSocket::CountUnwrittenBytes() const {
+ size_t count = 0;
+
+ for (const WebrtcTCPData& data : mWriteQueue) {
+ count += data.GetData().Length();
+ }
+
+ MOZ_ASSERT(count >= mWriteOffset, "offset exceeds write buffer length");
+
+ count -= mWriteOffset;
+
+ return count;
+}
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h
new file mode 100644
index 0000000000..632ba47d32
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocket.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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/. */
+
+#ifndef webrtc_tcp_socket_h__
+#define webrtc_tcp_socket_h__
+
+#include <list>
+
+#include "nsCOMPtr.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIAuthPromptProvider.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIStreamListener.h"
+#include "nsStringFwd.h"
+#include "nsTArray.h"
+#include "nsIProtocolProxyCallback.h"
+#include "mozilla/net/WebrtcProxyConfig.h"
+
+class nsISocketTransport;
+
+namespace mozilla::net {
+
+class WebrtcTCPSocketCallback;
+class WebrtcTCPData;
+
+class WebrtcTCPSocket : public nsIHttpUpgradeListener,
+ public nsIStreamListener,
+ public nsIInputStreamCallback,
+ public nsIOutputStreamCallback,
+ public nsIInterfaceRequestor,
+ public nsIAuthPromptProvider,
+ public nsIProtocolProxyCallback {
+ public:
+ NS_DECL_NSIHTTPUPGRADELISTENER
+ NS_DECL_NSIINPUTSTREAMCALLBACK
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIOUTPUTSTREAMCALLBACK
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_FORWARD_SAFE_NSIAUTHPROMPTPROVIDER(mAuthProvider)
+ NS_DECL_NSIPROTOCOLPROXYCALLBACK
+
+ explicit WebrtcTCPSocket(WebrtcTCPSocketCallback* aCallbacks);
+
+ void SetTabId(dom::TabId aTabId);
+ nsresult Open(const nsACString& aHost, const int& aPort,
+ const nsACString& aLocalAddress, const int& aLocalPort,
+ bool aUseTls,
+ const Maybe<net::WebrtcProxyConfig>& aProxyConfig);
+ nsresult Write(nsTArray<uint8_t>&& aBytes);
+ nsresult Close();
+
+ size_t CountUnwrittenBytes() const;
+
+ protected:
+ virtual ~WebrtcTCPSocket();
+
+ // protected for gtests
+ virtual void InvokeOnClose(nsresult aReason);
+ virtual void InvokeOnConnected();
+ virtual void InvokeOnRead(nsTArray<uint8_t>&& aReadData);
+
+ RefPtr<WebrtcTCPSocketCallback> mProxyCallbacks;
+
+ private:
+ bool mClosed;
+ bool mOpened;
+ nsCOMPtr<nsIURI> mURI;
+ bool mTls = false;
+ Maybe<WebrtcProxyConfig> mProxyConfig;
+ nsCString mLocalAddress;
+ uint16_t mLocalPort = 0;
+ nsCString mProxyType;
+
+ nsresult DoProxyConfigLookup();
+ nsresult OpenWithHttpProxy();
+ void OpenWithoutHttpProxy(nsIProxyInfo* aSocksProxyInfo);
+ void FinishOpen();
+ void EnqueueWrite_s(nsTArray<uint8_t>&& aWriteData);
+
+ void CloseWithReason(nsresult aReason);
+
+ size_t mWriteOffset;
+ std::list<WebrtcTCPData> mWriteQueue;
+ nsCOMPtr<nsIAuthPromptProvider> mAuthProvider;
+
+ // Indicates that the channel is CONNECTed
+ nsCOMPtr<nsISocketTransport> mTransport;
+ nsCOMPtr<nsIAsyncInputStream> mSocketIn;
+ nsCOMPtr<nsIAsyncOutputStream> mSocketOut;
+ nsCOMPtr<nsIEventTarget> mMainThread;
+ nsCOMPtr<nsIEventTarget> mSocketThread;
+ nsCOMPtr<nsICancelable> mProxyRequest;
+};
+
+} // namespace mozilla::net
+
+#endif // webrtc_tcp_socket_h__
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h
new file mode 100644
index 0000000000..1929e55ac2
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketCallback.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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/. */
+
+#ifndef webrtc_tcp_socket_callback_h__
+#define webrtc_tcp_socket_callback_h__
+
+#include "nsTArray.h"
+
+namespace mozilla::net {
+
+class WebrtcTCPSocketCallback {
+ public:
+ NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+
+ virtual void OnClose(nsresult aReason) = 0;
+ virtual void OnConnected(const nsACString& aProxyType) = 0;
+ virtual void OnRead(nsTArray<uint8_t>&& aReadData) = 0;
+
+ protected:
+ virtual ~WebrtcTCPSocketCallback() = default;
+};
+
+} // namespace mozilla::net
+
+#endif // webrtc_tcp_socket_callback_h__
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp
new file mode 100644
index 0000000000..52d1ba8ab2
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.cpp
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 "WebrtcTCPSocketChild.h"
+
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/SocketProcessChild.h"
+
+#include "LoadInfo.h"
+
+#include "WebrtcTCPSocketLog.h"
+#include "WebrtcTCPSocketCallback.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla::net {
+
+mozilla::ipc::IPCResult WebrtcTCPSocketChild::RecvOnClose(
+ const nsresult& aReason) {
+ LOG(("WebrtcTCPSocketChild::RecvOnClose %p\n", this));
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null");
+ mProxyCallbacks->OnClose(aReason);
+ mProxyCallbacks = nullptr;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WebrtcTCPSocketChild::RecvOnConnected(
+ const nsACString& aProxyType) {
+ LOG(("WebrtcTCPSocketChild::RecvOnConnected %p\n", this));
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null");
+ mProxyCallbacks->OnConnected(aProxyType);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WebrtcTCPSocketChild::RecvOnRead(
+ nsTArray<uint8_t>&& aReadData) {
+ LOG(("WebrtcTCPSocketChild::RecvOnRead %p\n", this));
+
+ MOZ_ASSERT(mProxyCallbacks, "webrtc TCP callbacks should be non-null");
+ mProxyCallbacks->OnRead(std::move(aReadData));
+
+ return IPC_OK();
+}
+
+WebrtcTCPSocketChild::WebrtcTCPSocketChild(
+ WebrtcTCPSocketCallback* aProxyCallbacks)
+ : mProxyCallbacks(aProxyCallbacks) {
+ MOZ_COUNT_CTOR(WebrtcTCPSocketChild);
+
+ LOG(("WebrtcTCPSocketChild::WebrtcTCPSocketChild %p\n", this));
+}
+
+WebrtcTCPSocketChild::~WebrtcTCPSocketChild() {
+ MOZ_COUNT_DTOR(WebrtcTCPSocketChild);
+
+ LOG(("WebrtcTCPSocketChild::~WebrtcTCPSocketChild %p\n", this));
+}
+
+void WebrtcTCPSocketChild::AsyncOpen(
+ const nsACString& aHost, const int& aPort, const nsACString& aLocalAddress,
+ const int& aLocalPort, bool aUseTls,
+ const std::shared_ptr<NrSocketProxyConfig>& aProxyConfig) {
+ LOG(("WebrtcTCPSocketChild::AsyncOpen %p %s:%d\n", this,
+ PromiseFlatCString(aHost).get(), aPort));
+
+ MOZ_ASSERT(NS_IsMainThread(), "not main thread");
+
+ AddIPDLReference();
+
+ Maybe<net::WebrtcProxyConfig> proxyConfig;
+ Maybe<dom::TabId> tabId;
+ if (aProxyConfig) {
+ proxyConfig = Some(aProxyConfig->GetConfig());
+ tabId = Some(proxyConfig->tabId());
+ }
+
+ if (IsNeckoChild()) {
+ // We're on a content process
+ gNeckoChild->SendPWebrtcTCPSocketConstructor(this, tabId);
+ } else if (IsSocketProcessChild()) {
+ // We're on a socket process
+ SocketProcessChild::GetSingleton()->SendPWebrtcTCPSocketConstructor(this,
+ tabId);
+ }
+
+ SendAsyncOpen(aHost, aPort, aLocalAddress, aLocalPort, aUseTls, proxyConfig);
+}
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h
new file mode 100644
index 0000000000..638ffcaac3
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketChild.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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/. */
+
+#ifndef mozilla_net_WebrtcTCPSocketChild_h
+#define mozilla_net_WebrtcTCPSocketChild_h
+
+#include "mozilla/net/PWebrtcTCPSocketChild.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "transport/nr_socket_proxy_config.h"
+
+namespace mozilla::net {
+
+class WebrtcTCPSocketCallback;
+
+class WebrtcTCPSocketChild : public PWebrtcTCPSocketChild {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcTCPSocketChild)
+
+ mozilla::ipc::IPCResult RecvOnClose(const nsresult& aReason) override;
+
+ mozilla::ipc::IPCResult RecvOnConnected(
+ const nsACString& aProxyType) override;
+
+ mozilla::ipc::IPCResult RecvOnRead(nsTArray<uint8_t>&& aReadData) override;
+
+ explicit WebrtcTCPSocketChild(WebrtcTCPSocketCallback* aProxyCallbacks);
+
+ void AsyncOpen(const nsACString& aHost, const int& aPort,
+ const nsACString& aLocalAddress, const int& aLocalPort,
+ bool aUseTls,
+ const std::shared_ptr<NrSocketProxyConfig>& aProxyConfig);
+
+ void AddIPDLReference() { AddRef(); }
+ void ReleaseIPDLReference() { Release(); }
+
+ protected:
+ virtual ~WebrtcTCPSocketChild();
+
+ RefPtr<WebrtcTCPSocketCallback> mProxyCallbacks;
+};
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_WebrtcTCPSocketChild_h
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp
new file mode 100644
index 0000000000..a6b0dcdb75
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.cpp
@@ -0,0 +1,11 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 "WebrtcTCPSocketLog.h"
+
+namespace mozilla::net {
+LazyLogModule webrtcTCPSocketLog("WebrtcTCPSocket");
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h
new file mode 100644
index 0000000000..72d3d0064b
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketLog.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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/. */
+
+#ifndef webrtc_tcp_socket_log_h__
+#define webrtc_tcp_socket_log_h__
+
+#include "mozilla/Logging.h"
+
+namespace mozilla::net {
+extern LazyLogModule webrtcTCPSocketLog;
+} // namespace mozilla::net
+
+#undef LOG
+#define LOG(args) \
+ MOZ_LOG(mozilla::net::webrtcTCPSocketLog, mozilla::LogLevel::Debug, args)
+
+#endif // webrtc_tcp_socket_log_h__
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp
new file mode 100644
index 0000000000..0df8962757
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.cpp
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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 "WebrtcTCPSocketParent.h"
+
+#include "mozilla/net/NeckoParent.h"
+
+#include "WebrtcTCPSocket.h"
+#include "WebrtcTCPSocketLog.h"
+
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+namespace mozilla::net {
+
+mozilla::ipc::IPCResult WebrtcTCPSocketParent::RecvAsyncOpen(
+ const nsACString& aHost, const int& aPort, const nsACString& aLocalAddress,
+ const int& aLocalPort, const bool& aUseTls,
+ const Maybe<WebrtcProxyConfig>& aProxyConfig) {
+ LOG(("WebrtcTCPSocketParent::RecvAsyncOpen %p to %s:%d\n", this,
+ PromiseFlatCString(aHost).get(), aPort));
+
+ MOZ_ASSERT(mChannel, "webrtc TCP socket should be non-null");
+ if (!mChannel) {
+ return IPC_FAIL(this, "Called with null channel.");
+ }
+
+ mChannel->Open(aHost, aPort, aLocalAddress, aLocalPort, aUseTls,
+ aProxyConfig);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WebrtcTCPSocketParent::RecvWrite(
+ nsTArray<uint8_t>&& aWriteData) {
+ LOG(("WebrtcTCPSocketParent::RecvWrite %p for %zu\n", this,
+ aWriteData.Length()));
+
+ // Need to check this here in case there are Writes in the queue after OnClose
+ if (mChannel) {
+ mChannel->Write(std::move(aWriteData));
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WebrtcTCPSocketParent::RecvClose() {
+ LOG(("WebrtcTCPSocketParent::RecvClose %p\n", this));
+
+ CleanupChannel();
+
+ IProtocol* mgr = Manager();
+ if (!Send__delete__(this)) {
+ return IPC_FAIL_NO_REASON(mgr);
+ }
+
+ return IPC_OK();
+}
+
+void WebrtcTCPSocketParent::ActorDestroy(ActorDestroyReason aWhy) {
+ LOG(("WebrtcTCPSocketParent::ActorDestroy %p for %d\n", this, aWhy));
+
+ CleanupChannel();
+}
+
+WebrtcTCPSocketParent::WebrtcTCPSocketParent(const Maybe<dom::TabId>& aTabId) {
+ MOZ_COUNT_CTOR(WebrtcTCPSocketParent);
+
+ LOG(("WebrtcTCPSocketParent::WebrtcTCPSocketParent %p\n", this));
+
+ mChannel = new WebrtcTCPSocket(this);
+ if (aTabId.isSome()) {
+ mChannel->SetTabId(*aTabId);
+ }
+}
+
+WebrtcTCPSocketParent::~WebrtcTCPSocketParent() {
+ MOZ_COUNT_DTOR(WebrtcTCPSocketParent);
+
+ LOG(("WebrtcTCPSocketParent::~WebrtcTCPSocketParent %p\n", this));
+
+ CleanupChannel();
+}
+
+// WebrtcTCPSocketCallback
+void WebrtcTCPSocketParent::OnClose(nsresult aReason) {
+ LOG(("WebrtcTCPSocketParent::OnClose %p\n", this));
+
+ if (mChannel) {
+ Unused << SendOnClose(aReason);
+ }
+
+ CleanupChannel();
+}
+
+void WebrtcTCPSocketParent::OnRead(nsTArray<uint8_t>&& aReadData) {
+ LOG(("WebrtcTCPSocketParent::OnRead %p %zu\n", this, aReadData.Length()));
+
+ if (mChannel && !SendOnRead(std::move(aReadData))) {
+ CleanupChannel();
+ }
+}
+
+void WebrtcTCPSocketParent::OnConnected(const nsACString& aProxyType) {
+ LOG(("WebrtcTCPSocketParent::OnConnected %p\n", this));
+
+ if (mChannel && !SendOnConnected(aProxyType)) {
+ CleanupChannel();
+ }
+}
+
+void WebrtcTCPSocketParent::CleanupChannel() {
+ if (mChannel) {
+ mChannel->Close();
+ mChannel = nullptr;
+ }
+}
+
+} // namespace mozilla::net
diff --git a/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h
new file mode 100644
index 0000000000..df4462609d
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/WebrtcTCPSocketParent.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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/. */
+
+#ifndef mozilla_net_WebrtcTCPSocketParent_h
+#define mozilla_net_WebrtcTCPSocketParent_h
+
+#include "mozilla/net/PWebrtcTCPSocketParent.h"
+
+#include "WebrtcTCPSocketCallback.h"
+
+class nsIAuthPromptProvider;
+
+namespace mozilla::net {
+
+class WebrtcTCPSocket;
+
+class WebrtcTCPSocketParent : public PWebrtcTCPSocketParent,
+ public WebrtcTCPSocketCallback {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebrtcTCPSocketParent, override)
+
+ mozilla::ipc::IPCResult RecvAsyncOpen(
+ const nsACString& aHost, const int& aPort,
+ const nsACString& aLocalAddress, const int& aLocalPort,
+ const bool& aUseTls,
+ const Maybe<WebrtcProxyConfig>& aProxyConfig) override;
+
+ mozilla::ipc::IPCResult RecvWrite(nsTArray<uint8_t>&& aWriteData) override;
+
+ mozilla::ipc::IPCResult RecvClose() override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ explicit WebrtcTCPSocketParent(const Maybe<dom::TabId>& aTabId);
+
+ // WebrtcTCPSocketCallback
+ void OnClose(nsresult aReason) override;
+ void OnConnected(const nsACString& aProxyType) override;
+ void OnRead(nsTArray<uint8_t>&& bytes) override;
+
+ void AddIPDLReference() { AddRef(); }
+ void ReleaseIPDLReference() { Release(); }
+
+ protected:
+ virtual ~WebrtcTCPSocketParent();
+
+ private:
+ void CleanupChannel();
+
+ // Indicates that IPC is open.
+ RefPtr<WebrtcTCPSocket> mChannel;
+};
+
+} // namespace mozilla::net
+
+#endif // mozilla_net_WebrtcTCPSocketParent_h
diff --git a/dom/media/webrtc/transport/ipc/moz.build b/dom/media/webrtc/transport/ipc/moz.build
new file mode 100644
index 0000000000..a2a72bb624
--- /dev/null
+++ b/dom/media/webrtc/transport/ipc/moz.build
@@ -0,0 +1,54 @@
+# 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/.
+
+EXPORTS.mozilla.net += [
+ "NrIceStunAddrMessageUtils.h",
+ "PStunAddrsParams.h",
+ "StunAddrsRequestChild.h",
+ "StunAddrsRequestParent.h",
+ "WebrtcTCPSocket.h",
+ "WebrtcTCPSocketCallback.h",
+ "WebrtcTCPSocketChild.h",
+ "WebrtcTCPSocketParent.h",
+]
+
+UNIFIED_SOURCES += [
+ "StunAddrsRequestChild.cpp",
+ "StunAddrsRequestParent.cpp",
+ "WebrtcTCPSocket.cpp",
+ "WebrtcTCPSocketChild.cpp",
+ "WebrtcTCPSocketLog.cpp",
+ "WebrtcTCPSocketParent.cpp",
+]
+
+IPDL_SOURCES += [
+ "PStunAddrsRequest.ipdl",
+ "PWebrtcTCPSocket.ipdl",
+ "WebrtcProxyConfig.ipdlh",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+DEFINES["R_DEFINED_INT2"] = "int16_t"
+DEFINES["R_DEFINED_UINT2"] = "uint16_t"
+DEFINES["R_DEFINED_INT4"] = "int32_t"
+DEFINES["R_DEFINED_UINT4"] = "uint32_t"
+# These are defined to avoid a conflict between typedefs in winsock2.h and
+# r_types.h. This is safe because these types are unused by the code here,
+# but still deeply unfortunate. There is similar code in the win32 version of
+# csi_platform.h, but that trick does not work here, even if that file is
+# directly included.
+DEFINES["R_DEFINED_INT8"] = "int8_t"
+DEFINES["R_DEFINED_UINT8"] = "uint8_t"
+
+LOCAL_INCLUDES += [
+ "/dom/media/webrtc/jsapi",
+ "/dom/media/webrtc/transport/third_party/nICEr/src/net",
+ "/dom/media/webrtc/transport/third_party/nrappkit/src/util/libekr",
+ "/media/webrtc",
+ "/netwerk/base",
+ "/netwerk/protocol/http",
+]