void PeerConnectionMedia::UpdateSinkIdentity_m(nsIPrincipal* aPrincipal, const PeerIdentity* aSinkIdentity) { ASSERT_ON_THREAD(mMainThread); for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) { mLocalSourceStreams[u]->UpdateSinkIdentity_m(aPrincipal, aSinkIdentity); } }
int TestNrSocket::sendto(const void *msg, size_t len, int flags, nr_transport_addr *to) { MOZ_ASSERT(my_addr_.protocol != IPPROTO_TCP); ASSERT_ON_THREAD(ststhread_); if (!nat_->enabled_ || nat_->is_an_internal_tuple(*to)) { return NrSocket::sendto(msg, len, flags, to); } destroy_stale_port_mappings(); if (to->protocol == IPPROTO_UDP && nat_->block_udp_) { // Silently eat the packet return 0; } // Choose our port mapping based on our most selective criteria PortMapping *port_mapping = get_port_mapping(*to, std::max(nat_->filtering_type_, nat_->mapping_type_)); if (!port_mapping) { // See if we have already made the external socket we need to use. PortMapping *similar_port_mapping = get_port_mapping(*to, nat_->mapping_type_); nsRefPtr<NrSocket> external_socket; if (similar_port_mapping) { external_socket = similar_port_mapping->external_socket_; } else { external_socket = create_external_socket(*to); if (!external_socket) { MOZ_ASSERT(false); return R_INTERNAL; } } port_mapping = create_port_mapping(*to, external_socket); port_mappings_.push_back(port_mapping); if (poll_flags() & PR_POLL_READ) { // Make sure the new port mapping is ready to receive traffic if the // TestNrSocket is already waiting. port_mapping->async_wait(NR_ASYNC_WAIT_READ, port_mapping_readable_callback, this, (char*)__FUNCTION__, __LINE__); } } // We probably don't want to propagate the flags, since this is a simulated // external IP address. return port_mapping->sendto(msg, len, *to); }
LocalSourceStreamInfo* PeerConnectionMedia::GetLocalStreamByIndex(int aIndex) { ASSERT_ON_THREAD(mMainThread); if(aIndex < 0 || aIndex >= (int) mLocalSourceStreams.Length()) { return nullptr; } MOZ_ASSERT(mLocalSourceStreams[aIndex]); return mLocalSourceStreams[aIndex]; }
void SourceStreamInfo::DetachMedia_m() { ASSERT_ON_THREAD(mParent->GetMainThread()); // walk through all the MediaPipelines and call the shutdown // media functions. Must be on the main thread. for (auto it = mPipelines.begin(); it != mPipelines.end(); ++it) { it->second->ShutdownMedia_m(); } mMediaStream = nullptr; }
void PeerConnectionMedia::UpdateSinkIdentity_m(const MediaStreamTrack* aTrack, nsIPrincipal* aPrincipal, const PeerIdentity* aSinkIdentity) { ASSERT_ON_THREAD(mMainThread); for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) { transceiver->UpdateSinkIdentity(aTrack, aPrincipal, aSinkIdentity); } }
void PeerConnectionMedia::PerformOrEnqueueIceCtxOperation(nsIRunnable* runnable) { ASSERT_ON_THREAD(mMainThread); if (IsIceCtxReady()) { GetSTSThread()->Dispatch(runnable, NS_DISPATCH_NORMAL); } else { mQueuedIceCtxOperations.push_back(runnable); } }
void RemoteSourceStreamInfo::DetachTransport_s() { ASSERT_ON_THREAD(mParent->GetSTSThread()); // walk through all the MediaPipelines and call the shutdown // transport functions. Must be on the STS thread. for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it = mPipelines.begin(); it != mPipelines.end(); ++it) { it->second->ShutdownTransport_s(); } }
void LocalSourceStreamInfo::DetachMedia_m() { ASSERT_ON_THREAD(mParent->GetMainThread()); // walk through all the MediaPipelines and call the shutdown // functions. Must be on the main thread. for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it = mPipelines.begin(); it != mPipelines.end(); ++it) { it->second->ShutdownMedia_m(); } }
// callback while UDP packet is sent NS_IMETHODIMP NrSocketIpc::CallListenerSent(const nsACString &type, nsresult result) { ASSERT_ON_THREAD(main_thread_); MOZ_ASSERT(type.EqualsLiteral("onsent")); if (NS_FAILED(result)) { ReentrantMonitorAutoEnter mon(monitor_); err_ = true; } return NS_OK; }
int NrSocketIpc::getaddr(nr_transport_addr *addrp) { ASSERT_ON_THREAD(sts_thread_); ReentrantMonitorAutoEnter mon(monitor_); if (state_ != NR_CONNECTED) { return R_INTERNAL; } return nr_transport_addr_copy(addrp, &my_addr_); }
void PeerConnectionMedia::GatherIfReady() { ASSERT_ON_THREAD(mMainThread); nsCOMPtr<nsIRunnable> runnable(WrapRunnable( RefPtr<PeerConnectionMedia>(this), &PeerConnectionMedia::EnsureIceGathering_s, GetPrefDefaultAddressOnly(), GetPrefProxyOnly())); PerformOrEnqueueIceCtxOperation(runnable); }
void PeerConnectionMedia::FlushIceCtxOperationQueueIfReady() { ASSERT_ON_THREAD(mMainThread); if (IsIceCtxReady()) { for (auto& mQueuedIceCtxOperation : mQueuedIceCtxOperations) { GetSTSThread()->Dispatch(mQueuedIceCtxOperation, NS_DISPATCH_NORMAL); } mQueuedIceCtxOperations.clear(); } }
void PeerConnectionMedia::SelfDestruct_m() { CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__); ASSERT_ON_THREAD(mMainThread); mMainThread = nullptr; // Final self-destruct. this->Release(); }
int NrIceResolver::resolve(nr_resolver_resource *resource, int (*cb)(void *cb_arg, nr_transport_addr *addr), void *cb_arg, void **handle) { int _status; MOZ_ASSERT(allocated_resolvers_ > 0); ASSERT_ON_THREAD(sts_thread_); RefPtr<PendingResolution> pr; uint32_t resolve_flags = 0; OriginAttributes attrs; if (resource->transport_protocol != IPPROTO_UDP && resource->transport_protocol != IPPROTO_TCP) { MOZ_MTLOG(ML_ERROR, "Only UDP and TCP are supported."); ABORT(R_NOT_FOUND); } pr = new PendingResolution(sts_thread_, resource->port? resource->port : 3478, resource->transport_protocol ? resource->transport_protocol : IPPROTO_UDP, cb, cb_arg); switch(resource->address_family) { case AF_INET: resolve_flags |= nsIDNSService::RESOLVE_DISABLE_IPV6; break; case AF_INET6: resolve_flags |= nsIDNSService::RESOLVE_DISABLE_IPV4; break; default: ABORT(R_BAD_ARGS); } if (NS_FAILED(dns_->AsyncResolveNative(nsAutoCString(resource->domain_name), resolve_flags, pr, sts_thread_, attrs, getter_AddRefs(pr->request_)))) { MOZ_MTLOG(ML_ERROR, "AsyncResolve failed."); ABORT(R_NOT_FOUND); } // Because the C API offers no "finished" method to release the handle we // return, we cannot return the request we got from AsyncResolve directly. // // Instead, we return an addref'ed reference to PendingResolution itself, // which in turn holds the request and coordinates between cancel and // OnLookupComplete to release it only once. pr.forget(handle); _status=0; abort: return _status; }
void PeerConnectionMedia::SelfDestruct_m() { CSFLogDebug(logTag, "%s: ", __FUNCTION__); ASSERT_ON_THREAD(mMainThread); mLocalSourceStreams.Clear(); mRemoteSourceStreams.Clear(); // Final self-destruct. this->Release(); }
RemoteSourceStreamInfo* PeerConnectionMedia::GetRemoteStreamByTrackId(const std::string& id) { ASSERT_ON_THREAD(mMainThread); for (RefPtr<RemoteSourceStreamInfo>& info : mRemoteSourceStreams) { if (info->HasTrack(id)) { return info; } } return nullptr; }
RemoteSourceStreamInfo* PeerConnectionMedia::GetRemoteStreamById(const std::string& id) { ASSERT_ON_THREAD(mMainThread); for (size_t i = 0; i < mRemoteSourceStreams.Length(); ++i) { if (id == mRemoteSourceStreams[i]->GetId()) { return mRemoteSourceStreams[i]; } } return nullptr; }
// callback for state update after every socket operation NS_IMETHODIMP NrSocketIpc::UpdateReadyState(const nsACString &readyState) { ASSERT_ON_THREAD(main_thread_); ReentrantMonitorAutoEnter mon(monitor_); if (readyState.EqualsLiteral("closed")) { MOZ_ASSERT(state_ == NR_CONNECTED || state_ == NR_CLOSING); state_ = NR_CLOSED; } return NS_OK; }
// This should be called on the STS thread. int NrSocket::sendto(const void *msg, size_t len, int flags, nr_transport_addr *to) { ASSERT_ON_THREAD(ststhread_); int r,_status; PRNetAddr naddr; int32_t status; if ((r=nr_transport_addr_to_praddr(to, &naddr))) ABORT(r); if(fd_==nullptr) ABORT(R_EOD); if (nr_is_stun_request_message((UCHAR*)msg, len)) { // Global rate limiting for stun requests, to mitigate the ice hammer DoS // (see http://tools.ietf.org/html/draft-thomson-mmusic-ice-webrtc) // Tolerate rate of 8k/sec, for one second. static SimpleTokenBucket burst(8192*1, 8192); // Tolerate rate of 3.6k/sec over twenty seconds. static SimpleTokenBucket sustained(3686*20, 3686); // Check number of tokens in each bucket. if (burst.getTokens(UINT32_MAX) < len || sustained.getTokens(UINT32_MAX) < len) { r_log(LOG_GENERIC, LOG_ERR, "Global rate limit for STUN requests exceeded."); MOZ_ASSERT("Global rate limit for STUN requests exceeded. Go bug " "[email protected] if you weren't intentionally spamming " "ICE candidates, or don't know what that means."); ABORT(R_WOULDBLOCK); } // Take len tokens from both buckets. // (not threadsafe, but no problem since this is only called from STS) burst.getTokens(len); sustained.getTokens(len); } // TODO: Convert flags? status = PR_SendTo(fd_, msg, len, flags, &naddr, PR_INTERVAL_NO_WAIT); if (status < 0 || (size_t)status != len) { if (PR_GetError() == PR_WOULD_BLOCK_ERROR) ABORT(R_WOULDBLOCK); r_log(LOG_GENERIC, LOG_INFO, "Error in sendto %s", to->as_string); ABORT(R_IO_ERROR); } _status=0; abort: return(_status); }
void PeerConnectionMedia::BeginIceRestart_s(RefPtr<NrIceCtx> new_ctx) { ASSERT_ON_THREAD(mSTSThread); // hold the original context so we can disconnect signals if needed RefPtr<NrIceCtx> originalCtx = mIceCtxHdlr->ctx(); if (mIceCtxHdlr->BeginIceRestart(new_ctx)) { ConnectSignals(mIceCtxHdlr->ctx().get(), originalCtx.get()); } }
// callback while UDP socket is opened NS_IMETHODIMP NrSocketIpc::CallListenerOpened() { ASSERT_ON_THREAD(main_thread_); ReentrantMonitorAutoEnter mon(monitor_); uint16_t port; if (NS_FAILED(socket_child_->GetLocalPort(&port))) { err_ = true; MOZ_ASSERT(false, "Failed to get local port"); return NS_OK; } nsAutoCString address; if(NS_FAILED(socket_child_->GetLocalAddress(address))) { err_ = true; MOZ_ASSERT(false, "Failed to get local address"); return NS_OK; } PRNetAddr praddr; if (PR_SUCCESS != PR_InitializeNetAddr(PR_IpAddrAny, port, &praddr)) { err_ = true; MOZ_ASSERT(false, "Failed to set port in PRNetAddr"); return NS_OK; } if (PR_SUCCESS != PR_StringToNetAddr(address.BeginReading(), &praddr)) { err_ = true; MOZ_ASSERT(false, "Failed to convert local host to PRNetAddr"); return NS_OK; } nr_transport_addr expected_addr; if(nr_transport_addr_copy(&expected_addr, &my_addr_)) { err_ = true; MOZ_ASSERT(false, "Failed to copy my_addr_"); } if (nr_praddr_to_transport_addr(&praddr, &my_addr_, IPPROTO_UDP, 1)) { err_ = true; MOZ_ASSERT(false, "Failed to copy local host to my_addr_"); } if (nr_transport_addr_cmp(&expected_addr, &my_addr_, NR_TRANSPORT_ADDR_CMP_MODE_ADDR)) { err_ = true; MOZ_ASSERT(false, "Address of opened socket is not expected"); } mon.NotifyAll(); return NS_OK; }
int TestNrSocket::recvfrom(void *buf, size_t maxlen, size_t *len, int flags, nr_transport_addr *from) { MOZ_ASSERT(my_addr_.protocol != IPPROTO_TCP); ASSERT_ON_THREAD(ststhread_); int r; bool ingress_allowed = false; if (readable_socket_) { // If any of the external sockets got data, see if it will be passed through r = readable_socket_->recvfrom(buf, maxlen, len, 0, from); readable_socket_ = nullptr; if (!r) { PortMapping *port_mapping_used; ingress_allowed = allow_ingress(*from, &port_mapping_used); if (ingress_allowed && nat_->refresh_on_ingress_ && port_mapping_used) { port_mapping_used->last_used_ = PR_IntervalNow(); } } } else { // If no external socket has data, see if there's any data that was sent // directly to the TestNrSocket, and eat it if it isn't supposed to get // through. r = NrSocket::recvfrom(buf, maxlen, len, flags, from); if (!r) { // We do not use allow_ingress() here because that only handles traffic // landing on an external port. ingress_allowed = (!nat_->enabled_ || nat_->is_an_internal_tuple(*from)); if (!ingress_allowed) { r_log(LOG_GENERIC, LOG_INFO, "TestNrSocket %s denying ingress from %s: " "Not behind the same NAT", my_addr_.as_string, from->as_string); } } } // Kinda lame that we are forced to give the app a readable callback and then // say "Oh, never mind...", but the alternative is to totally decouple the // callbacks from STS and the callbacks the app sets. On the bright side, this // speeds up unit tests where we are verifying that ingress is forbidden, // since they'll get a readable callback and then an error, instead of having // to wait for a timeout. if (!ingress_allowed) { *len = 0; r = R_WOULDBLOCK; } return r; }
nsresult PeerConnectionMedia::AddRemoteStream(nsRefPtr<RemoteSourceStreamInfo> aInfo, int *aIndex) { ASSERT_ON_THREAD(mMainThread); MOZ_ASSERT(aIndex); *aIndex = mRemoteSourceStreams.Length(); mRemoteSourceStreams.AppendElement(aInfo); return NS_OK; }
void NrSocketIpc::sendto_m(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) { ASSERT_ON_THREAD(main_thread_); MOZ_ASSERT(socket_child_); ReentrantMonitorAutoEnter mon(monitor_); if (NS_FAILED(socket_child_->SendWithAddress(&addr, buf->data(), buf->len()))) { err_ = true; } }
/** * Tells you if any local track is isolated to a specific peer identity. * Obviously, we want all the tracks to be isolated equally so that they can * all be sent or not. We check once when we are setting a local description * and that determines if we flip the "privacy requested" bit on. Once the bit * is on, all media originating from this peer connection is isolated. * * @returns true if any track has a peerIdentity set on it */ bool PeerConnectionMedia::AnyLocalTrackHasPeerIdentity() const { ASSERT_ON_THREAD(mMainThread); for (const RefPtr<TransceiverImpl>& transceiver : mTransceivers) { if (transceiver->GetSendTrack() && transceiver->GetSendTrack()->GetPeerIdentity()) { return true; } } return false; }
bool PeerConnectionMedia::GetPrefDefaultAddressOnly() const { ASSERT_ON_THREAD(mMainThread); // will crash on STS thread #if !defined(MOZILLA_EXTERNAL_LINKAGE) bool default_address_only = Preferences::GetBool( "media.peerconnection.ice.default_address_only", false); #else bool default_address_only = false; #endif return default_address_only; }
bool PeerConnectionMedia::GetPrefDefaultAddressOnly() const { ASSERT_ON_THREAD(mMainThread); // will crash on STS thread uint64_t winId = mParent->GetWindow()->WindowID(); bool default_address_only = Preferences::GetBool( "media.peerconnection.ice.default_address_only", false); default_address_only |= !MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId); return default_address_only; }
// IUDPSocketInternal interfaces // callback while error happened in UDP socket operation NS_IMETHODIMP NrSocketIpc::CallListenerError(const nsACString &message, const nsACString &filename, uint32_t line_number) { ASSERT_ON_THREAD(main_thread_); r_log(LOG_GENERIC, LOG_ERR, "UDP socket error:%s at %s:%d", message.BeginReading(), filename.BeginReading(), line_number ); ReentrantMonitorAutoEnter mon(monitor_); err_ = true; monitor_.NotifyAll(); return NS_OK; }
// nr_socket public APIs int NrSocketIpc::create(nr_transport_addr *addr) { ASSERT_ON_THREAD(sts_thread_); int r, _status; nsresult rv; int32_t port; nsCString host; ReentrantMonitorAutoEnter mon(monitor_); if (state_ != NR_INIT) { ABORT(R_INTERNAL); } sts_thread_ = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) { MOZ_ASSERT(false, "Failed to get STS thread"); ABORT(R_INTERNAL); } if ((r=nr_transport_addr_get_addrstring_and_port(addr, &host, &port))) { ABORT(r); } // wildcard address will be resolved at NrSocketIpc::CallListenerVoid if ((r=nr_transport_addr_copy(&my_addr_, addr))) { ABORT(r); } state_ = NR_CONNECTING; RUN_ON_THREAD(main_thread_, mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this), &NrSocketIpc::create_m, host, static_cast<uint16_t>(port)), NS_DISPATCH_NORMAL); // Wait until socket creation complete. mon.Wait(); if (err_) { ABORT(R_INTERNAL); } state_ = NR_CONNECTED; _status = 0; abort: return(_status); }
/** * Tells you if any local track is isolated to a specific peer identity. * Obviously, we want all the tracks to be isolated equally so that they can * all be sent or not. We check once when we are setting a local description * and that determines if we flip the "privacy requested" bit on. Once the bit * is on, all media originating from this peer connection is isolated. * * @returns true if any track has a peerIdentity set on it */ bool PeerConnectionMedia::AnyLocalTrackHasPeerIdentity() const { ASSERT_ON_THREAD(mMainThread); for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) { for (auto pair : mLocalSourceStreams[u]->GetMediaStreamTracks()) { if (pair.second->GetPeerIdentity() != nullptr) { return true; } } } return false; }