diff options
28 files changed, 368 insertions, 34 deletions
diff --git a/dom/media/PeerConnection.js b/dom/media/PeerConnection.js index d2f098b1fa..77b7a58980 100644 --- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -588,7 +588,7 @@ RTCPeerConnection.prototype = { throw new this._win.DOMException(msg + " - improper scheme: " + url.scheme, "SyntaxError"); } - if (url.scheme in { stuns:1, turns:1 }) { + if (url.scheme in { stuns:1 }) { this.logWarning(url.scheme.toUpperCase() + " is not yet supported."); } }); diff --git a/dom/media/tests/mochitest/addTurnsSelfsignedCert.js b/dom/media/tests/mochitest/addTurnsSelfsignedCert.js new file mode 100644 index 0000000000..cad3d04465 --- /dev/null +++ b/dom/media/tests/mochitest/addTurnsSelfsignedCert.js @@ -0,0 +1,26 @@ +/* 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/. */ + +"use strict"; + +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +// This is only usable from the parent process, even for doing simple stuff like +// serializing a cert. +var gCertMaker = Cc["@mozilla.org/security/x509certdb;1"]. + getService(Ci.nsIX509CertDB); + +var gCertOverrides = Cc["@mozilla.org/security/certoverride;1"]. + getService(Ci.nsICertOverrideService); + + +addMessageListener('add-turns-certs', certs => { + var port = 5349; + certs.forEach(certDescription => { + var cert = gCertMaker.constructX509FromBase64(certDescription.cert); + gCertOverrides.rememberValidityOverride(certDescription.hostname, port, + cert, Ci.nsICertOverrideService.ERROR_UNTRUSTED, false); + }); + sendAsyncMessage('certs-added'); +}); diff --git a/dom/media/tests/mochitest/mochitest.ini b/dom/media/tests/mochitest/mochitest.ini index 22006ffa2f..948d938a30 100644 --- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -13,6 +13,7 @@ support-files = blacksilence.js turnConfig.js sdpUtils.js + addTurnsSelfsignedCert.js !/dom/canvas/test/captureStream_common.js !/dom/canvas/test/webgl-mochitest/webgl-util.js !/dom/media/test/manifest.js @@ -98,6 +99,8 @@ skip-if = toolkit == 'android' # websockets don't work on android (bug 1266217) skip-if = toolkit == 'android' # websockets don't work on android (bug 1266217) [test_peerConnection_basicAudioNATRelayTCP.html] skip-if = toolkit == 'android' # websockets don't work on android (bug 1266217) +[test_peerConnection_basicAudioNATRelayTLS.html] +skip-if = true # need pyopenssl on builders, see bug 1323439 [test_peerConnection_basicAudioRequireEOC.html] skip-if = (android_version == '18' && debug) # android(Bug 1189784, timeouts on 4.3 emulator) [test_peerConnection_basicAudioPcmaPcmuOnly.html] diff --git a/dom/media/tests/mochitest/pc.js b/dom/media/tests/mochitest/pc.js index a9383358f9..2e9c7c63f1 100644 --- a/dom/media/tests/mochitest/pc.js +++ b/dom/media/tests/mochitest/pc.js @@ -1822,6 +1822,33 @@ function createHTML(options) { var iceServerWebsocket; var iceServersArray = []; +var addTurnsSelfsignedCerts = () => { + var gUrl = SimpleTest.getTestFileURL('addTurnsSelfsignedCert.js'); + var gScript = SpecialPowers.loadChromeScript(gUrl); + var certs = []; + // If the ICE server is running TURNS, and includes a "cert" attribute in + // its JSON, we set up an override that will forgive things like + // self-signed for it. + iceServersArray.forEach(iceServer => { + if (iceServer.hasOwnProperty("cert")) { + iceServer.urls.forEach(url => { + if (url.startsWith("turns:")) { + // Assumes no port or params! + certs.push({"cert": iceServer.cert, "hostname": url.substr(6)}); + } + }); + } + }); + + return new Promise((resolve, reject) => { + gScript.addMessageListener('certs-added', () => { + resolve(); + }); + + gScript.sendAsyncMessage('add-turns-certs', certs); + }); +}; + var setupIceServerConfig = useIceServer => { // We disable ICE support for HTTP proxy when using a TURN server, because // mochitest uses a fake HTTP proxy to serve content, which will eat our STUN @@ -1863,7 +1890,8 @@ var setupIceServerConfig = useIceServer => { return enableHttpProxy(false) .then(spawnIceServer) - .then(iceServersStr => { iceServersArray = JSON.parse(iceServersStr); }); + .then(iceServersStr => { iceServersArray = JSON.parse(iceServersStr); }) + .then(addTurnsSelfsignedCerts); }; function runNetworkTest(testFunction, fixtureOptions) { diff --git a/dom/media/tests/mochitest/test_peerConnection_basicAudioNATRelayTLS.html b/dom/media/tests/mochitest/test_peerConnection_basicAudioNATRelayTLS.html new file mode 100644 index 0000000000..c295955d81 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_basicAudioNATRelayTLS.html @@ -0,0 +1,38 @@ +<!DOCTYPE HTML> +<html> +<head> + <script type="application/javascript" src="pc.js"></script> +</head> +<body> +<pre id="test"> +<script type="application/javascript"> + createHTML({ + bug: "1231975", + title: "Basic audio-only peer connection with port dependent NAT that blocks STUN" + }); + + var test; + runNetworkTest(options => { + SpecialPowers.pushPrefEnv( + { + 'set': [ + ['media.peerconnection.nat_simulator.filtering_type', 'PORT_DEPENDENT'], + ['media.peerconnection.nat_simulator.mapping_type', 'PORT_DEPENDENT'], + ['media.peerconnection.nat_simulator.block_udp', true], + ['media.peerconnection.nat_simulator.block_tcp', true] + ] + }, function (options) { + options = options || {}; + options.expectedLocalCandidateType = "relayed-tcp"; + options.expectedRemoteCandidateType = "relayed-tcp"; + // No reason to wait for gathering to complete like the other NAT tests, + // since relayed-tcp is the only thing that can work. + test = new PeerConnectionTest(options); + test.setMediaConstraints([{audio: true}], [{audio: true}]); + test.run(); + }) + }, { useIceServer: true }); +</script> +</pre> +</body> +</html> diff --git a/dom/network/PTCPSocket.ipdl b/dom/network/PTCPSocket.ipdl index aa7c51e45f..ea00856029 100644 --- a/dom/network/PTCPSocket.ipdl +++ b/dom/network/PTCPSocket.ipdl @@ -44,7 +44,8 @@ parent: // address specified in |localAddr| and |localPort|. async OpenBind(nsCString host, uint16_t port, nsCString localAddr, uint16_t localPort, - bool useSSL, bool aUseArrayBuffers, nsCString aFilter); + bool useSSL, bool reuseAddrPort, + bool aUseArrayBuffers, nsCString aFilter); // When child's send() is called, this message requrests parent to send // data and update it's trackingNumber. diff --git a/dom/network/TCPSocketChild.cpp b/dom/network/TCPSocketChild.cpp index 9e1dce2769..b2b8db78a3 100644 --- a/dom/network/TCPSocketChild.cpp +++ b/dom/network/TCPSocketChild.cpp @@ -108,7 +108,7 @@ void TCPSocketChild::SendWindowlessOpenBind(nsITCPSocketCallback* aSocket, const nsACString& aRemoteHost, uint16_t aRemotePort, const nsACString& aLocalHost, uint16_t aLocalPort, - bool aUseSSL) + bool aUseSSL, bool aReuseAddrPort) { mSocket = aSocket; AddIPDLReference(); @@ -117,7 +117,8 @@ TCPSocketChild::SendWindowlessOpenBind(nsITCPSocketCallback* aSocket, aRemotePort); PTCPSocketChild::SendOpenBind(nsCString(aRemoteHost), aRemotePort, nsCString(aLocalHost), aLocalPort, - aUseSSL, true, mFilterName); + aUseSSL, aReuseAddrPort, + true, mFilterName); } void diff --git a/dom/network/TCPSocketChild.h b/dom/network/TCPSocketChild.h index 7e9b59e8b5..46e9b0a90f 100644 --- a/dom/network/TCPSocketChild.h +++ b/dom/network/TCPSocketChild.h @@ -55,7 +55,7 @@ public: void SendWindowlessOpenBind(nsITCPSocketCallback* aSocket, const nsACString& aRemoteHost, uint16_t aRemotePort, const nsACString& aLocalHost, uint16_t aLocalPort, - bool aUseSSL); + bool aUseSSL, bool aUseRealtimeOptions); NS_IMETHOD SendSendArray(nsTArray<uint8_t>& aArray, uint32_t aTrackingNumber); void SendSend(const nsACString& aData, uint32_t aTrackingNumber); diff --git a/dom/network/TCPSocketParent.cpp b/dom/network/TCPSocketParent.cpp index 313d13f759..96eab44510 100644 --- a/dom/network/TCPSocketParent.cpp +++ b/dom/network/TCPSocketParent.cpp @@ -149,6 +149,7 @@ TCPSocketParent::RecvOpenBind(const nsCString& aRemoteHost, const nsCString& aLocalAddr, const uint16_t& aLocalPort, const bool& aUseSSL, + const bool& aReuseAddrPort, const bool& aUseArrayBuffers, const nsCString& aFilter) { @@ -167,14 +168,27 @@ TCPSocketParent::RecvOpenBind(const nsCString& aRemoteHost, } nsCOMPtr<nsISocketTransport> socketTransport; - rv = sts->CreateTransport(nullptr, 0, - aRemoteHost, aRemotePort, - nullptr, getter_AddRefs(socketTransport)); + if (aUseSSL) { + const char* socketTypes[1]; + socketTypes[0] = "ssl"; + rv = sts->CreateTransport(socketTypes, 1, + aRemoteHost, aRemotePort, + nullptr, getter_AddRefs(socketTransport)); + } else { + rv = sts->CreateTransport(nullptr, 0, + aRemoteHost, aRemotePort, + nullptr, getter_AddRefs(socketTransport)); + } + if (NS_FAILED(rv)) { FireInteralError(this, __LINE__); return true; } + // in most cases aReuseAddrPort is false, but ICE TCP needs + // sockets options set that allow addr/port reuse + socketTransport->SetReuseAddrPort(aReuseAddrPort); + PRNetAddr prAddr; if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr)) { FireInteralError(this, __LINE__); diff --git a/dom/network/TCPSocketParent.h b/dom/network/TCPSocketParent.h index 07112f1e2d..e0f987b17f 100644 --- a/dom/network/TCPSocketParent.h +++ b/dom/network/TCPSocketParent.h @@ -54,6 +54,7 @@ public: const nsCString& aLocalAddr, const uint16_t& aLocalPort, const bool& aUseSSL, + const bool& aReuseAddrPort, const bool& aUseArrayBuffers, const nsCString& aFilter) override; diff --git a/media/mtransport/nr_socket_prsock.cpp b/media/mtransport/nr_socket_prsock.cpp index 80e5ef51e4..3fd223349c 100644 --- a/media/mtransport/nr_socket_prsock.cpp +++ b/media/mtransport/nr_socket_prsock.cpp @@ -858,6 +858,10 @@ int NrSocket::connect(nr_transport_addr *addr) { PRNetAddr naddr; int32_t connect_status, getsockname_status; + // TODO: Add TLS layer with nsISocketProviderService? + if (addr->tls_host[0] != '\0') + ABORT(R_INTERNAL); + if ((r=nr_transport_addr_to_praddr(addr, &naddr))) ABORT(r); @@ -1840,7 +1844,7 @@ void NrTcpSocketIpc::close() { } int NrTcpSocketIpc::connect(nr_transport_addr *addr) { - nsCString remote_addr, local_addr; + nsCString remote_addr, local_addr, tls_host; int32_t remote_port, local_port; int r, _status; if ((r=nr_transport_addr_get_addrstring_and_port(addr, @@ -1856,6 +1860,8 @@ int NrTcpSocketIpc::connect(nr_transport_addr *addr) { ABORT(r); } + tls_host = addr->tls_host; + state_ = mirror_state_ = NR_CONNECTING; RUN_ON_THREAD(io_thread_, mozilla::WrapRunnable(RefPtr<NrTcpSocketIpc>(this), @@ -1863,7 +1869,8 @@ int NrTcpSocketIpc::connect(nr_transport_addr *addr) { remote_addr, static_cast<uint16_t>(remote_port), local_addr, - static_cast<uint16_t>(local_port)), + static_cast<uint16_t>(local_port), + tls_host), NS_DISPATCH_NORMAL); // Make caller wait for ready to write. @@ -1939,7 +1946,8 @@ int NrTcpSocketIpc::accept(nr_transport_addr *addrp, nr_socket **sockp) { void NrTcpSocketIpc::connect_i(const nsACString &remote_addr, uint16_t remote_port, const nsACString &local_addr, - uint16_t local_port) { + uint16_t local_port, + const nsACString &tls_host) { ASSERT_ON_THREAD(io_thread_); mirror_state_ = NR_CONNECTING; @@ -1948,11 +1956,21 @@ void NrTcpSocketIpc::connect_i(const nsACString &remote_addr, // Bug 1285330: put filtering back in here - // XXX remove remote! - socket_child_->SendWindowlessOpenBind(this, - remote_addr, remote_port, - local_addr, local_port, - /* use ssl */ false); + if (tls_host.IsEmpty()) { + // XXX remove remote! + socket_child_->SendWindowlessOpenBind(this, + remote_addr, remote_port, + local_addr, local_port, + /* use ssl */ false, + /* reuse addr port */ true); + } else { + // XXX remove remote! + socket_child_->SendWindowlessOpenBind(this, + tls_host, remote_port, + local_addr, local_port, + /* use ssl */ true, + /* reuse addr port */ true); + } } void NrTcpSocketIpc::write_i(nsAutoPtr<InfallibleTArray<uint8_t>> arr, diff --git a/media/mtransport/nr_socket_prsock.h b/media/mtransport/nr_socket_prsock.h index 5ed9a6a219..7d372ccb16 100644 --- a/media/mtransport/nr_socket_prsock.h +++ b/media/mtransport/nr_socket_prsock.h @@ -369,7 +369,8 @@ private: void connect_i(const nsACString &remote_addr, uint16_t remote_port, const nsACString &local_addr, - uint16_t local_port); + uint16_t local_port, + const nsACString &tls_host); void write_i(nsAutoPtr<InfallibleTArray<uint8_t>> buf, uint32_t tracking_number); void close_i(); diff --git a/media/mtransport/nricectx.cpp b/media/mtransport/nricectx.cpp index be50f50169..75d3d2519d 100644 --- a/media/mtransport/nricectx.cpp +++ b/media/mtransport/nricectx.cpp @@ -108,6 +108,7 @@ MOZ_MTLOG_MODULE("mtransport") const char kNrIceTransportUdp[] = "udp"; const char kNrIceTransportTcp[] = "tcp"; +const char kNrIceTransportTls[] = "tls"; static bool initialized = false; @@ -210,6 +211,9 @@ nsresult NrIceStunServer::ToNicerStunStruct(nr_ice_stun_server *server) const { server->transport = IPPROTO_UDP; } else if (transport_ == kNrIceTransportTcp) { server->transport = IPPROTO_TCP; + } else if (transport_ == kNrIceTransportTls) { + server->transport = IPPROTO_TCP; + server->tls = 1; } else { MOZ_MTLOG(ML_ERROR, "Unsupported STUN server transport: " << transport_); return NS_ERROR_FAILURE; @@ -586,6 +590,7 @@ NrIceCtx::Initialize(const std::string& ufrag, nsCString mapping_type; nsCString filtering_type; bool block_udp = false; + bool block_tcp = false; nsresult rv; nsCOMPtr<nsIPrefService> pref_service = @@ -604,6 +609,9 @@ NrIceCtx::Initialize(const std::string& ufrag, rv = pref_branch->GetBoolPref( "media.peerconnection.nat_simulator.block_udp", &block_udp); + rv = pref_branch->GetBoolPref( + "media.peerconnection.nat_simulator.block_tcp", + &block_tcp); } } @@ -614,6 +622,7 @@ NrIceCtx::Initialize(const std::string& ufrag, test_nat->filtering_type_ = TestNat::ToNatBehavior(filtering_type.get()); test_nat->mapping_type_ = TestNat::ToNatBehavior(mapping_type.get()); test_nat->block_udp_ = block_udp; + test_nat->block_tcp_ = block_tcp; test_nat->enabled_ = true; SetNat(test_nat); } diff --git a/media/mtransport/nricectx.h b/media/mtransport/nricectx.h index ce104ac317..87122121e5 100644 --- a/media/mtransport/nricectx.h +++ b/media/mtransport/nricectx.h @@ -90,6 +90,7 @@ class NrIceMediaStream; extern const char kNrIceTransportUdp[]; extern const char kNrIceTransportTcp[]; +extern const char kNrIceTransportTls[]; class NrIceStunServer { public: diff --git a/media/mtransport/test_nr_socket.cpp b/media/mtransport/test_nr_socket.cpp index e7dad79cd1..fb2de3096e 100644 --- a/media/mtransport/test_nr_socket.cpp +++ b/media/mtransport/test_nr_socket.cpp @@ -200,6 +200,7 @@ int TestNat::create_socket_factory(nr_socket_factory **factorypp) { TestNrSocket::TestNrSocket(TestNat *nat) : nat_(nat), + tls_(false), timer_handle_(nullptr) { nat_->insert_socket(this); } @@ -473,6 +474,10 @@ int TestNrSocket::connect(nr_transport_addr *addr) { return R_INTERNAL; } + if (addr->tls_host[0] != '\0') { + tls_ = true; + } + if (!nat_->enabled_ || addr->protocol==IPPROTO_UDP // Horrible hack to allow default address // discovery to work. Only works because @@ -508,6 +513,24 @@ int TestNrSocket::connect(nr_transport_addr *addr) { } int TestNrSocket::write(const void *msg, size_t len, size_t *written) { + UCHAR *buf = static_cast<UCHAR*>(const_cast<void*>(msg)); + if (nat_->block_stun_ && nr_is_stun_message(buf, len)) { + // Should cause this socket to be abandoned + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket %s dropping outgoing TCP " + "because it is configured to drop STUN", + my_addr().as_string); + return R_INTERNAL; + } + + if (nat_->block_tcp_ && !tls_) { + // Should cause this socket to be abandoned + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket %s dropping outgoing TCP " + "because it is configured to drop TCP", + my_addr().as_string); + return R_INTERNAL; + } if (port_mappings_.empty()) { // The no-nat case, just pass call through. @@ -518,7 +541,11 @@ int TestNrSocket::write(const void *msg, size_t len, size_t *written) { } else { destroy_stale_port_mappings(); if (port_mappings_.empty()) { - return -1; + r_log(LOG_GENERIC, LOG_DEBUG, + "TestNrSocket %s dropping outgoing TCP " + "because the port mapping was stale", + my_addr().as_string); + return R_INTERNAL; } // This is TCP only MOZ_ASSERT(port_mappings_.size() == 1); @@ -533,18 +560,34 @@ int TestNrSocket::write(const void *msg, size_t len, size_t *written) { } int TestNrSocket::read(void *buf, size_t maxlen, size_t *len) { + int r; if (port_mappings_.empty()) { - return internal_socket_->read(buf, maxlen, len); + r = internal_socket_->read(buf, maxlen, len); } else { MOZ_ASSERT(port_mappings_.size() == 1); - int bytesRead = - port_mappings_.front()->external_socket_->read(buf, maxlen, len); - if (bytesRead > 0 && nat_->refresh_on_ingress_) { + r = port_mappings_.front()->external_socket_->read(buf, maxlen, len); + if (!r && nat_->refresh_on_ingress_) { port_mappings_.front()->last_used_ = PR_IntervalNow(); } - return bytesRead; } + + if (r) { + return r; + } + + if (nat_->block_tcp_ && !tls_) { + // Should cause this socket to be abandoned + return R_INTERNAL; + } + + UCHAR *cbuf = static_cast<UCHAR*>(const_cast<void*>(buf)); + if (nat_->block_stun_ && nr_is_stun_message(cbuf, *len)) { + // Should cause this socket to be abandoned + return R_INTERNAL; + } + + return r; } int TestNrSocket::async_wait(int how, NR_async_cb cb, void *cb_arg, diff --git a/media/mtransport/test_nr_socket.h b/media/mtransport/test_nr_socket.h index 91c9030f17..a47a88beb9 100644 --- a/media/mtransport/test_nr_socket.h +++ b/media/mtransport/test_nr_socket.h @@ -137,6 +137,7 @@ class TestNat { refresh_on_ingress_(false), block_udp_(false), block_stun_(false), + block_tcp_(false), delay_stun_resp_ms_(0), sockets_() {} @@ -168,6 +169,7 @@ class TestNat { bool refresh_on_ingress_; bool block_udp_; bool block_stun_; + bool block_tcp_; /* Note: this can only delay a single response so far (bug 1253657) */ uint32_t delay_stun_resp_ms_; @@ -319,6 +321,7 @@ class TestNrSocket : public NrSocketBase { // same nat. RefPtr<NrSocketBase> internal_socket_; RefPtr<TestNat> nat_; + bool tls_; // Since our comparison logic is different depending on what kind of NAT // we simulate, and the STL does not make it very easy to switch out the // comparison function at runtime, and these lists are going to be very diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c index e48b7b5e1a..879b77933e 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c +++ b/media/mtransport/third_party/nICEr/src/ice/ice_candidate.c @@ -666,6 +666,14 @@ static int nr_ice_candidate_resolved_cb(void *cb_arg, nr_transport_addr *addr) if(r=nr_transport_addr_copy(&cand->stun_server_addr,addr)) ABORT(r); + if (cand->stun_server->tls) { + /* Copy over the DNS name; needed for TLS. There is already a null at the + * end of the buffer, leave it there. */ + strncpy(cand->stun_server_addr.tls_host, + cand->stun_server->u.dnsname.host, + sizeof(cand->stun_server_addr.tls_host) - 1); + } + if (cand->tcp_type == TCP_TYPE_PASSIVE || cand->tcp_type == TCP_TYPE_SO){ if (r=nr_socket_multi_tcp_stun_server_connect(cand->osock, addr)) ABORT(r); diff --git a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h index cc79304f08..1e815ac2ac 100644 --- a/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h +++ b/media/mtransport/third_party/nICEr/src/ice/ice_ctx.h @@ -63,6 +63,7 @@ typedef struct nr_ice_stun_server_ { } u; int id; int transport; + int tls; /* Whether to use TLS or not */ } nr_ice_stun_server; typedef struct nr_ice_turn_server_ { diff --git a/media/mtransport/third_party/nICEr/src/net/transport_addr.h b/media/mtransport/third_party/nICEr/src/net/transport_addr.h index dfec633291..06c8b8c9c5 100644 --- a/media/mtransport/third_party/nICEr/src/net/transport_addr.h +++ b/media/mtransport/third_party/nICEr/src/net/transport_addr.h @@ -66,6 +66,7 @@ typedef struct nr_transport_addr_ { /* A string version. 56 = 5 ("IP6:[") + 39 (ipv6 address) + 2 ("]:") + 5 (port) + 4 (/UDP) + 1 (null) */ char as_string[56]; + char tls_host[256]; } nr_transport_addr; typedef struct nr_transport_addr_mask_ { diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 3b4363a13c..ad87fa1f91 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -335,6 +335,7 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal) , mSTSThread(nullptr) , mAllowIceLoopback(false) , mAllowIceLinkLocal(false) + , mForceIceTcp(false) , mMedia(nullptr) , mUuidGen(MakeUnique<PCUuidGenerator>()) , mNumAudioStreams(0) @@ -365,6 +366,8 @@ PeerConnectionImpl::PeerConnectionImpl(const GlobalObject* aGlobal) "media.peerconnection.ice.loopback", false); mAllowIceLinkLocal = Preferences::GetBool( "media.peerconnection.ice.link_local", false); + mForceIceTcp = Preferences::GetBool( + "media.peerconnection.ice.force_ice_tcp", false); #endif memset(mMaxReceiving, 0, sizeof(mMaxReceiving)); memset(mMaxSending, 0, sizeof(mMaxSending)); @@ -526,8 +529,8 @@ PeerConnectionConfiguration::AddIceServer(const RTCIceServer &aServer) if (!(isStun || isStuns || isTurn || isTurns)) { return NS_ERROR_FAILURE; } - if (isTurns || isStuns) { - continue; // TODO: Support TURNS and STUNS (Bug 1056934) + if (isStuns) { + continue; // TODO: Support STUNS (Bug 1056934) } nsAutoCString spec; rv = url->GetSpec(spec); @@ -576,6 +579,11 @@ PeerConnectionConfiguration::AddIceServer(const RTCIceServer &aServer) if (port == -1) port = (isStuns || isTurns)? 5349 : 3478; + if (isStuns || isTurns) { + // Should we barf if transport is set to udp or something? + transport = "tls"; + } + // First check the known good ports for webrtc bool knownGoodPort = false; for (int i = 0; !knownGoodPort && gGoodWebrtcPortList[i]; i++) { @@ -2255,6 +2263,11 @@ NS_IMETHODIMP PeerConnectionImpl::AddIceCandidate(const char* aCandidate, const char* aMid, unsigned short aLevel) { PC_AUTO_ENTER_API_CALL(true); + if (mForceIceTcp && std::string::npos != std::string(aCandidate).find(" UDP ")) { + CSFLogError(logTag, "Blocking remote UDP candidate: %s", aCandidate); + return NS_OK; + } + JSErrorResult rv; RefPtr<PeerConnectionObserver> pco = do_QueryObjectReferent(mPCObserver); if (!pco) { @@ -3106,7 +3119,7 @@ PeerConnectionImpl::SetSignalingState_m(PCImplSignalingState aSignalingState, mNegotiationNeeded = false; // If we're rolling back a local offer, we might need to remove some // transports, but nothing further needs to be done. - mMedia->ActivateOrRemoveTransports(*mJsepSession); + mMedia->ActivateOrRemoveTransports(*mJsepSession, mForceIceTcp); if (!rollback) { mMedia->UpdateMediaPipelines(*mJsepSession); InitializeDataChannel(); @@ -3268,6 +3281,11 @@ PeerConnectionImpl::CandidateReady(const std::string& candidate, uint16_t level) { PC_AUTO_ENTER_API_CALL_VOID_RETURN(false); + if (mForceIceTcp && std::string::npos != candidate.find(" UDP ")) { + CSFLogError(logTag, "Blocking local UDP candidate: %s", candidate.c_str()); + return; + } + std::string mid; bool skipped = false; nsresult res = mJsepSession->AddLocalIceCandidate(candidate, diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h index 7b53ea1160..098b34249a 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -803,6 +803,7 @@ private: bool mAllowIceLoopback; bool mAllowIceLinkLocal; + bool mForceIceTcp; RefPtr<PeerConnectionMedia> mMedia; // The JSEP negotiation session. diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp index 0d388a8f49..0306b57904 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -457,7 +457,8 @@ PeerConnectionMedia::EnsureTransport_s(size_t aLevel, size_t aComponentCount) } void -PeerConnectionMedia::ActivateOrRemoveTransports(const JsepSession& aSession) +PeerConnectionMedia::ActivateOrRemoveTransports(const JsepSession& aSession, + const bool forceIceTcp) { auto transports = aSession.GetTransports(); for (size_t i = 0; i < transports.size(); ++i) { @@ -480,6 +481,14 @@ PeerConnectionMedia::ActivateOrRemoveTransports(const JsepSession& aSession) RemoveTransportFlow(i, true); } + if (forceIceTcp) { + candidates.erase(std::remove_if(candidates.begin(), + candidates.end(), + [](const std::string & s) { + return s.find(" UDP "); }), + candidates.end()); + } + RUN_ON_THREAD( GetSTSThread(), WrapRunnable(RefPtr<PeerConnectionMedia>(this), diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h index c0001a5e54..8908e51546 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h @@ -270,7 +270,8 @@ class PeerConnectionMedia : public sigslot::has_slots<> { // Activate or remove ICE transports at the conclusion of offer/answer, // or when rollback occurs. - void ActivateOrRemoveTransports(const JsepSession& aSession); + void ActivateOrRemoveTransports(const JsepSession& aSession, + const bool forceIceTcp); // Start ICE checks. void StartIceChecks(const JsepSession& session); diff --git a/netwerk/base/nsISocketTransport.idl b/netwerk/base/nsISocketTransport.idl index 9b5bc23fb7..3525aad5d9 100644 --- a/netwerk/base/nsISocketTransport.idl +++ b/netwerk/base/nsISocketTransport.idl @@ -130,6 +130,11 @@ interface nsISocketTransport : nsITransport void setTimeout(in unsigned long aType, in unsigned long aValue); /** + * True to set addr and port reuse socket options. + */ + void setReuseAddrPort(in bool reuseAddrPort); + + /** * Values for the aType parameter passed to get/setTimeout. */ const unsigned long TIMEOUT_CONNECT = 0; diff --git a/netwerk/base/nsSocketTransport2.cpp b/netwerk/base/nsSocketTransport2.cpp index ff5fc3070d..ab20737443 100644 --- a/netwerk/base/nsSocketTransport2.cpp +++ b/netwerk/base/nsSocketTransport2.cpp @@ -737,6 +737,7 @@ nsSocketTransport::nsSocketTransport() , mProxyTransparentResolvesHost(false) , mHttpsProxy(false) , mConnectionFlags(0) + , mReuseAddrPort(false) , mState(STATE_CLOSED) , mAttached(false) , mInputClosed(true) @@ -1354,6 +1355,32 @@ nsSocketTransport::InitiateSocket() status = PR_SetSocketOption(fd, &opt); NS_ASSERTION(status == PR_SUCCESS, "unable to make socket non-blocking"); + if (mReuseAddrPort) { + SOCKET_LOG((" Setting port/addr reuse socket options\n")); + + // Set ReuseAddr for TCP sockets to enable having several + // sockets bound to same local IP and port + PRSocketOptionData opt_reuseaddr; + opt_reuseaddr.option = PR_SockOpt_Reuseaddr; + opt_reuseaddr.value.reuse_addr = PR_TRUE; + status = PR_SetSocketOption(fd, &opt_reuseaddr); + if (status != PR_SUCCESS) { + SOCKET_LOG((" Couldn't set reuse addr socket option: %d\n", + status)); + } + + // And also set ReusePort for platforms supporting this socket option + PRSocketOptionData opt_reuseport; + opt_reuseport.option = PR_SockOpt_Reuseport; + opt_reuseport.value.reuse_port = PR_TRUE; + status = PR_SetSocketOption(fd, &opt_reuseport); + if (status != PR_SUCCESS + && PR_GetError() != PR_OPERATION_NOT_SUPPORTED_ERROR) { + SOCKET_LOG((" Couldn't set reuse port socket option: %d\n", + status)); + } + } + // disable the nagle algorithm - if we rely on it to coalesce writes into // full packets the final packet of a multi segment POST/PUT or pipeline // sequence is delayed a full rtt @@ -2469,6 +2496,13 @@ nsSocketTransport::SetTimeout(uint32_t type, uint32_t value) } NS_IMETHODIMP +nsSocketTransport::SetReuseAddrPort(bool reuseAddrPort) +{ + mReuseAddrPort = reuseAddrPort; + return NS_OK; +} + +NS_IMETHODIMP nsSocketTransport::SetQoSBits(uint8_t aQoSBits) { // Don't do any checking here of bits. Why? Because as of RFC-4594 diff --git a/netwerk/base/nsSocketTransport2.h b/netwerk/base/nsSocketTransport2.h index 89b75efa57..a61e432b48 100644 --- a/netwerk/base/nsSocketTransport2.h +++ b/netwerk/base/nsSocketTransport2.h @@ -295,6 +295,7 @@ private: bool mProxyTransparentResolvesHost; bool mHttpsProxy; uint32_t mConnectionFlags; + bool mReuseAddrPort; // The origin attributes are used to create sockets. The first party domain // will eventually be used to isolate OCSP cache and is only non-empty when diff --git a/netwerk/protocol/http/TunnelUtils.cpp b/netwerk/protocol/http/TunnelUtils.cpp index eeaf57f55c..01075d2c0c 100644 --- a/netwerk/protocol/http/TunnelUtils.cpp +++ b/netwerk/protocol/http/TunnelUtils.cpp @@ -1686,6 +1686,12 @@ SocketTransportShim::SetTimeout(uint32_t aType, uint32_t aValue) } NS_IMETHODIMP +SocketTransportShim::SetReuseAddrPort(bool aReuseAddrPort) +{ + return mWrapped->SetReuseAddrPort(aReuseAddrPort); +} + +NS_IMETHODIMP SocketTransportShim::GetQoSBits(uint8_t *aQoSBits) { return mWrapped->GetQoSBits(aQoSBits); diff --git a/testing/tools/iceserver/iceserver.py b/testing/tools/iceserver/iceserver.py index 2089301af8..85d0f3ee54 100644 --- a/testing/tools/iceserver/iceserver.py +++ b/testing/tools/iceserver/iceserver.py @@ -10,7 +10,9 @@ import passlib.utils # for saslprep import copy import random import operator +import os import platform +import string import time from string import Template from twisted.internet import reactor, protocol @@ -715,6 +717,38 @@ def prune_allocations(): del allocations[key] allocation.close() +CERT_FILE = "selfsigned.crt" +KEY_FILE = "private.key" + +def create_self_signed_cert(name): + from OpenSSL import crypto + if os.path.isfile(CERT_FILE) and os.path.isfile(KEY_FILE): + return + + # create a key pair + k = crypto.PKey() + k.generate_key(crypto.TYPE_RSA, 1024) + + # create a self-signed cert + cert = crypto.X509() + cert.get_subject().C = "US" + cert.get_subject().ST = "TX" + cert.get_subject().L = "Dallas" + cert.get_subject().O = "Mozilla test iceserver" + cert.get_subject().OU = "Mozilla test iceserver" + cert.get_subject().CN = name + cert.set_serial_number(1000) + cert.gmtime_adj_notBefore(0) + cert.gmtime_adj_notAfter(10*365*24*60*60) + cert.set_issuer(cert.get_subject()) + cert.set_pubkey(k) + cert.sign(k, 'sha1') + + open(CERT_FILE, "wt").write( + crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) + open(KEY_FILE, "wt").write( + crypto.dump_privatekey(crypto.FILETYPE_PEM, k)) + if __name__ == "__main__": random.seed() @@ -739,20 +773,48 @@ if __name__ == "__main__": except: pass + try: + from twisted.internet import ssl + from OpenSSL import SSL + create_self_signed_cert(hostname) + tls_context_factory = ssl.DefaultOpenSSLContextFactory(KEY_FILE, CERT_FILE, SSL.TLSv1_2_METHOD) + reactor.listenSSL(5349, TcpStunHandlerFactory(), tls_context_factory, interface=interface_4) + + try: + reactor.listenSSL(5349, TcpStunHandlerFactory(), tls_context_factory, interface=interface_6) + except: + pass + + f = open(CERT_FILE, 'r'); + lines = f.readlines(); + lines.pop(0); # Remove BEGIN CERTIFICATE + lines.pop(); # Remove END CERTIFICATE + lines = map(string.strip, lines); + certbase64 = string.join(lines, ''); + + turns_url = ', "turns:' + hostname + '"' + cert_prop = ', "cert":"' + certbase64 + '"' + except: + turns_url = '' + cert_prop = '' + pass + allocation_pruner = LoopingCall(prune_allocations) allocation_pruner.start(1) template = Template( '[\ -{"url":"stun:$hostname"}, \ -{"url":"stun:$hostname?transport=tcp"}, \ -{"username":"$user","credential":"$pwd","url":"turn:$hostname"}, \ -{"username":"$user","credential":"$pwd","url":"turn:$hostname?transport=tcp"}]' +{"urls":["stun:$hostname", "stun:$hostname?transport=tcp"]}, \ +{"username":"$user","credential":"$pwd","urls": \ +["turn:$hostname", "turn:$hostname?transport=tcp" $turns_url] \ +$cert_prop}]' # Hack to make it easier to override cert checks ) print(template.substitute(user=turn_user, pwd=turn_pass, - hostname=hostname)) + hostname=hostname, + turns_url=turns_url, + cert_prop=cert_prop)) reactor.run() |