1 /*
2 * Copyright 2017 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "pc/jsep_transport_controller.h"
12
13 #include <memory>
14 #include <utility>
15
16 #include "absl/algorithm/container.h"
17 #include "api/ice_transport_factory.h"
18 #include "p2p/base/ice_transport_internal.h"
19 #include "p2p/base/port.h"
20 #include "pc/srtp_filter.h"
21 #include "rtc_base/bind.h"
22 #include "rtc_base/checks.h"
23 #include "rtc_base/thread.h"
24
25 using webrtc::SdpType;
26
27 namespace {
28
VerifyCandidate(const cricket::Candidate & cand)29 webrtc::RTCError VerifyCandidate(const cricket::Candidate& cand) {
30 // No address zero.
31 if (cand.address().IsNil() || cand.address().IsAnyIP()) {
32 return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
33 "candidate has address of zero");
34 }
35
36 // Disallow all ports below 1024, except for 80 and 443 on public addresses.
37 int port = cand.address().port();
38 if (cand.protocol() == cricket::TCP_PROTOCOL_NAME &&
39 (cand.tcptype() == cricket::TCPTYPE_ACTIVE_STR || port == 0)) {
40 // Expected for active-only candidates per
41 // http://tools.ietf.org/html/rfc6544#section-4.5 so no error.
42 // Libjingle clients emit port 0, in "active" mode.
43 return webrtc::RTCError::OK();
44 }
45 if (port < 1024) {
46 if ((port != 80) && (port != 443)) {
47 return webrtc::RTCError(
48 webrtc::RTCErrorType::INVALID_PARAMETER,
49 "candidate has port below 1024, but not 80 or 443");
50 }
51
52 if (cand.address().IsPrivateIP()) {
53 return webrtc::RTCError(
54 webrtc::RTCErrorType::INVALID_PARAMETER,
55 "candidate has port of 80 or 443 with private IP address");
56 }
57 }
58
59 return webrtc::RTCError::OK();
60 }
61
VerifyCandidates(const cricket::Candidates & candidates)62 webrtc::RTCError VerifyCandidates(const cricket::Candidates& candidates) {
63 for (const cricket::Candidate& candidate : candidates) {
64 webrtc::RTCError error = VerifyCandidate(candidate);
65 if (!error.ok()) {
66 return error;
67 }
68 }
69 return webrtc::RTCError::OK();
70 }
71
72 } // namespace
73
74 namespace webrtc {
75
JsepTransportController(rtc::Thread * signaling_thread,rtc::Thread * network_thread,cricket::PortAllocator * port_allocator,AsyncResolverFactory * async_resolver_factory,Config config)76 JsepTransportController::JsepTransportController(
77 rtc::Thread* signaling_thread,
78 rtc::Thread* network_thread,
79 cricket::PortAllocator* port_allocator,
80 AsyncResolverFactory* async_resolver_factory,
81 Config config)
82 : signaling_thread_(signaling_thread),
83 network_thread_(network_thread),
84 port_allocator_(port_allocator),
85 async_resolver_factory_(async_resolver_factory),
86 config_(config) {
87 // The |transport_observer| is assumed to be non-null.
88 RTC_DCHECK(config_.transport_observer);
89 RTC_DCHECK(config_.rtcp_handler);
90 RTC_DCHECK(config_.ice_transport_factory);
91 }
92
~JsepTransportController()93 JsepTransportController::~JsepTransportController() {
94 // Channel destructors may try to send packets, so this needs to happen on
95 // the network thread.
96 network_thread_->Invoke<void>(
97 RTC_FROM_HERE,
98 rtc::Bind(&JsepTransportController::DestroyAllJsepTransports_n, this));
99 }
100
SetLocalDescription(SdpType type,const cricket::SessionDescription * description)101 RTCError JsepTransportController::SetLocalDescription(
102 SdpType type,
103 const cricket::SessionDescription* description) {
104 if (!network_thread_->IsCurrent()) {
105 return network_thread_->Invoke<RTCError>(
106 RTC_FROM_HERE, [=] { return SetLocalDescription(type, description); });
107 }
108
109 if (!initial_offerer_.has_value()) {
110 initial_offerer_.emplace(type == SdpType::kOffer);
111 if (*initial_offerer_) {
112 SetIceRole_n(cricket::ICEROLE_CONTROLLING);
113 } else {
114 SetIceRole_n(cricket::ICEROLE_CONTROLLED);
115 }
116 }
117 return ApplyDescription_n(/*local=*/true, type, description);
118 }
119
SetRemoteDescription(SdpType type,const cricket::SessionDescription * description)120 RTCError JsepTransportController::SetRemoteDescription(
121 SdpType type,
122 const cricket::SessionDescription* description) {
123 if (!network_thread_->IsCurrent()) {
124 return network_thread_->Invoke<RTCError>(
125 RTC_FROM_HERE, [=] { return SetRemoteDescription(type, description); });
126 }
127
128 return ApplyDescription_n(/*local=*/false, type, description);
129 }
130
GetRtpTransport(const std::string & mid) const131 RtpTransportInternal* JsepTransportController::GetRtpTransport(
132 const std::string& mid) const {
133 auto jsep_transport = GetJsepTransportForMid(mid);
134 if (!jsep_transport) {
135 return nullptr;
136 }
137 return jsep_transport->rtp_transport();
138 }
139
GetDataChannelTransport(const std::string & mid) const140 DataChannelTransportInterface* JsepTransportController::GetDataChannelTransport(
141 const std::string& mid) const {
142 auto jsep_transport = GetJsepTransportForMid(mid);
143 if (!jsep_transport) {
144 return nullptr;
145 }
146 return jsep_transport->data_channel_transport();
147 }
148
GetDtlsTransport(const std::string & mid)149 cricket::DtlsTransportInternal* JsepTransportController::GetDtlsTransport(
150 const std::string& mid) {
151 auto jsep_transport = GetJsepTransportForMid(mid);
152 if (!jsep_transport) {
153 return nullptr;
154 }
155 return jsep_transport->rtp_dtls_transport();
156 }
157
158 const cricket::DtlsTransportInternal*
GetRtcpDtlsTransport(const std::string & mid) const159 JsepTransportController::GetRtcpDtlsTransport(const std::string& mid) const {
160 auto jsep_transport = GetJsepTransportForMid(mid);
161 if (!jsep_transport) {
162 return nullptr;
163 }
164 return jsep_transport->rtcp_dtls_transport();
165 }
166
167 rtc::scoped_refptr<webrtc::DtlsTransport>
LookupDtlsTransportByMid(const std::string & mid)168 JsepTransportController::LookupDtlsTransportByMid(const std::string& mid) {
169 auto jsep_transport = GetJsepTransportForMid(mid);
170 if (!jsep_transport) {
171 return nullptr;
172 }
173 return jsep_transport->RtpDtlsTransport();
174 }
175
GetSctpTransport(const std::string & mid) const176 rtc::scoped_refptr<SctpTransport> JsepTransportController::GetSctpTransport(
177 const std::string& mid) const {
178 auto jsep_transport = GetJsepTransportForMid(mid);
179 if (!jsep_transport) {
180 return nullptr;
181 }
182 return jsep_transport->SctpTransport();
183 }
184
SetIceConfig(const cricket::IceConfig & config)185 void JsepTransportController::SetIceConfig(const cricket::IceConfig& config) {
186 if (!network_thread_->IsCurrent()) {
187 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] { SetIceConfig(config); });
188 return;
189 }
190
191 ice_config_ = config;
192 for (auto& dtls : GetDtlsTransports()) {
193 dtls->ice_transport()->SetIceConfig(ice_config_);
194 }
195 }
196
SetNeedsIceRestartFlag()197 void JsepTransportController::SetNeedsIceRestartFlag() {
198 for (auto& kv : jsep_transports_by_name_) {
199 kv.second->SetNeedsIceRestartFlag();
200 }
201 }
202
NeedsIceRestart(const std::string & transport_name) const203 bool JsepTransportController::NeedsIceRestart(
204 const std::string& transport_name) const {
205 const cricket::JsepTransport* transport =
206 GetJsepTransportByName(transport_name);
207 if (!transport) {
208 return false;
209 }
210 return transport->needs_ice_restart();
211 }
212
GetDtlsRole(const std::string & mid) const213 absl::optional<rtc::SSLRole> JsepTransportController::GetDtlsRole(
214 const std::string& mid) const {
215 if (!network_thread_->IsCurrent()) {
216 return network_thread_->Invoke<absl::optional<rtc::SSLRole>>(
217 RTC_FROM_HERE, [&] { return GetDtlsRole(mid); });
218 }
219
220 const cricket::JsepTransport* t = GetJsepTransportForMid(mid);
221 if (!t) {
222 return absl::optional<rtc::SSLRole>();
223 }
224 return t->GetDtlsRole();
225 }
226
SetLocalCertificate(const rtc::scoped_refptr<rtc::RTCCertificate> & certificate)227 bool JsepTransportController::SetLocalCertificate(
228 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
229 if (!network_thread_->IsCurrent()) {
230 return network_thread_->Invoke<bool>(
231 RTC_FROM_HERE, [&] { return SetLocalCertificate(certificate); });
232 }
233
234 // Can't change a certificate, or set a null certificate.
235 if (certificate_ || !certificate) {
236 return false;
237 }
238 certificate_ = certificate;
239
240 // Set certificate for JsepTransport, which verifies it matches the
241 // fingerprint in SDP, and DTLS transport.
242 // Fallback from DTLS to SDES is not supported.
243 for (auto& kv : jsep_transports_by_name_) {
244 kv.second->SetLocalCertificate(certificate_);
245 }
246 for (auto& dtls : GetDtlsTransports()) {
247 bool set_cert_success = dtls->SetLocalCertificate(certificate_);
248 RTC_DCHECK(set_cert_success);
249 }
250 return true;
251 }
252
253 rtc::scoped_refptr<rtc::RTCCertificate>
GetLocalCertificate(const std::string & transport_name) const254 JsepTransportController::GetLocalCertificate(
255 const std::string& transport_name) const {
256 if (!network_thread_->IsCurrent()) {
257 return network_thread_->Invoke<rtc::scoped_refptr<rtc::RTCCertificate>>(
258 RTC_FROM_HERE, [&] { return GetLocalCertificate(transport_name); });
259 }
260
261 const cricket::JsepTransport* t = GetJsepTransportByName(transport_name);
262 if (!t) {
263 return nullptr;
264 }
265 return t->GetLocalCertificate();
266 }
267
268 std::unique_ptr<rtc::SSLCertChain>
GetRemoteSSLCertChain(const std::string & transport_name) const269 JsepTransportController::GetRemoteSSLCertChain(
270 const std::string& transport_name) const {
271 if (!network_thread_->IsCurrent()) {
272 return network_thread_->Invoke<std::unique_ptr<rtc::SSLCertChain>>(
273 RTC_FROM_HERE, [&] { return GetRemoteSSLCertChain(transport_name); });
274 }
275
276 // Get the certificate from the RTP transport's DTLS handshake. Should be
277 // identical to the RTCP transport's, since they were given the same remote
278 // fingerprint.
279 auto jsep_transport = GetJsepTransportByName(transport_name);
280 if (!jsep_transport) {
281 return nullptr;
282 }
283 auto dtls = jsep_transport->rtp_dtls_transport();
284 if (!dtls) {
285 return nullptr;
286 }
287
288 return dtls->GetRemoteSSLCertChain();
289 }
290
MaybeStartGathering()291 void JsepTransportController::MaybeStartGathering() {
292 if (!network_thread_->IsCurrent()) {
293 network_thread_->Invoke<void>(RTC_FROM_HERE,
294 [&] { MaybeStartGathering(); });
295 return;
296 }
297
298 for (auto& dtls : GetDtlsTransports()) {
299 dtls->ice_transport()->MaybeStartGathering();
300 }
301 }
302
AddRemoteCandidates(const std::string & transport_name,const cricket::Candidates & candidates)303 RTCError JsepTransportController::AddRemoteCandidates(
304 const std::string& transport_name,
305 const cricket::Candidates& candidates) {
306 if (!network_thread_->IsCurrent()) {
307 return network_thread_->Invoke<RTCError>(RTC_FROM_HERE, [&] {
308 return AddRemoteCandidates(transport_name, candidates);
309 });
310 }
311
312 // Verify each candidate before passing down to the transport layer.
313 RTCError error = VerifyCandidates(candidates);
314 if (!error.ok()) {
315 return error;
316 }
317 auto jsep_transport = GetJsepTransportByName(transport_name);
318 if (!jsep_transport) {
319 RTC_LOG(LS_WARNING) << "Not adding candidate because the JsepTransport "
320 "doesn't exist. Ignore it.";
321 return RTCError::OK();
322 }
323 return jsep_transport->AddRemoteCandidates(candidates);
324 }
325
RemoveRemoteCandidates(const cricket::Candidates & candidates)326 RTCError JsepTransportController::RemoveRemoteCandidates(
327 const cricket::Candidates& candidates) {
328 if (!network_thread_->IsCurrent()) {
329 return network_thread_->Invoke<RTCError>(
330 RTC_FROM_HERE, [&] { return RemoveRemoteCandidates(candidates); });
331 }
332
333 // Verify each candidate before passing down to the transport layer.
334 RTCError error = VerifyCandidates(candidates);
335 if (!error.ok()) {
336 return error;
337 }
338
339 std::map<std::string, cricket::Candidates> candidates_by_transport_name;
340 for (const cricket::Candidate& cand : candidates) {
341 if (!cand.transport_name().empty()) {
342 candidates_by_transport_name[cand.transport_name()].push_back(cand);
343 } else {
344 RTC_LOG(LS_ERROR) << "Not removing candidate because it does not have a "
345 "transport name set: "
346 << cand.ToSensitiveString();
347 }
348 }
349
350 for (const auto& kv : candidates_by_transport_name) {
351 const std::string& transport_name = kv.first;
352 const cricket::Candidates& candidates = kv.second;
353 cricket::JsepTransport* jsep_transport =
354 GetJsepTransportByName(transport_name);
355 if (!jsep_transport) {
356 RTC_LOG(LS_WARNING)
357 << "Not removing candidate because the JsepTransport doesn't exist.";
358 continue;
359 }
360 for (const cricket::Candidate& candidate : candidates) {
361 cricket::DtlsTransportInternal* dtls =
362 candidate.component() == cricket::ICE_CANDIDATE_COMPONENT_RTP
363 ? jsep_transport->rtp_dtls_transport()
364 : jsep_transport->rtcp_dtls_transport();
365 if (dtls) {
366 dtls->ice_transport()->RemoveRemoteCandidate(candidate);
367 }
368 }
369 }
370 return RTCError::OK();
371 }
372
GetStats(const std::string & transport_name,cricket::TransportStats * stats)373 bool JsepTransportController::GetStats(const std::string& transport_name,
374 cricket::TransportStats* stats) {
375 if (!network_thread_->IsCurrent()) {
376 return network_thread_->Invoke<bool>(
377 RTC_FROM_HERE, [=] { return GetStats(transport_name, stats); });
378 }
379
380 cricket::JsepTransport* transport = GetJsepTransportByName(transport_name);
381 if (!transport) {
382 return false;
383 }
384 return transport->GetStats(stats);
385 }
386
SetActiveResetSrtpParams(bool active_reset_srtp_params)387 void JsepTransportController::SetActiveResetSrtpParams(
388 bool active_reset_srtp_params) {
389 if (!network_thread_->IsCurrent()) {
390 network_thread_->Invoke<void>(RTC_FROM_HERE, [=] {
391 SetActiveResetSrtpParams(active_reset_srtp_params);
392 });
393 return;
394 }
395
396 RTC_LOG(INFO)
397 << "Updating the active_reset_srtp_params for JsepTransportController: "
398 << active_reset_srtp_params;
399 config_.active_reset_srtp_params = active_reset_srtp_params;
400 for (auto& kv : jsep_transports_by_name_) {
401 kv.second->SetActiveResetSrtpParams(active_reset_srtp_params);
402 }
403 }
404
RollbackTransports()405 void JsepTransportController::RollbackTransports() {
406 if (!network_thread_->IsCurrent()) {
407 network_thread_->Invoke<void>(RTC_FROM_HERE, [=] { RollbackTransports(); });
408 return;
409 }
410 RTC_DCHECK_RUN_ON(network_thread_);
411 for (auto&& mid : pending_mids_) {
412 RemoveTransportForMid(mid);
413 }
414 for (auto&& mid : pending_mids_) {
415 MaybeDestroyJsepTransport(mid);
416 }
417 pending_mids_.clear();
418 }
419
420 rtc::scoped_refptr<webrtc::IceTransportInterface>
CreateIceTransport(const std::string & transport_name,bool rtcp)421 JsepTransportController::CreateIceTransport(const std::string& transport_name,
422 bool rtcp) {
423 int component = rtcp ? cricket::ICE_CANDIDATE_COMPONENT_RTCP
424 : cricket::ICE_CANDIDATE_COMPONENT_RTP;
425
426 IceTransportInit init;
427 init.set_port_allocator(port_allocator_);
428 init.set_async_resolver_factory(async_resolver_factory_);
429 init.set_event_log(config_.event_log);
430 return config_.ice_transport_factory->CreateIceTransport(
431 transport_name, component, std::move(init));
432 }
433
434 std::unique_ptr<cricket::DtlsTransportInternal>
CreateDtlsTransport(const cricket::ContentInfo & content_info,cricket::IceTransportInternal * ice)435 JsepTransportController::CreateDtlsTransport(
436 const cricket::ContentInfo& content_info,
437 cricket::IceTransportInternal* ice) {
438 RTC_DCHECK(network_thread_->IsCurrent());
439
440 std::unique_ptr<cricket::DtlsTransportInternal> dtls;
441
442 if (config_.dtls_transport_factory) {
443 dtls = config_.dtls_transport_factory->CreateDtlsTransport(
444 ice, config_.crypto_options);
445 } else {
446 dtls = std::make_unique<cricket::DtlsTransport>(ice, config_.crypto_options,
447 config_.event_log);
448 }
449
450 RTC_DCHECK(dtls);
451 dtls->SetSslMaxProtocolVersion(config_.ssl_max_version);
452 dtls->ice_transport()->SetIceRole(ice_role_);
453 dtls->ice_transport()->SetIceTiebreaker(ice_tiebreaker_);
454 dtls->ice_transport()->SetIceConfig(ice_config_);
455 if (certificate_) {
456 bool set_cert_success = dtls->SetLocalCertificate(certificate_);
457 RTC_DCHECK(set_cert_success);
458 }
459
460 // Connect to signals offered by the DTLS and ICE transport.
461 dtls->SignalWritableState.connect(
462 this, &JsepTransportController::OnTransportWritableState_n);
463 dtls->SignalReceivingState.connect(
464 this, &JsepTransportController::OnTransportReceivingState_n);
465 dtls->SignalDtlsHandshakeError.connect(
466 this, &JsepTransportController::OnDtlsHandshakeError);
467 dtls->ice_transport()->SignalGatheringState.connect(
468 this, &JsepTransportController::OnTransportGatheringState_n);
469 dtls->ice_transport()->SignalCandidateGathered.connect(
470 this, &JsepTransportController::OnTransportCandidateGathered_n);
471 dtls->ice_transport()->SignalCandidateError.connect(
472 this, &JsepTransportController::OnTransportCandidateError_n);
473 dtls->ice_transport()->SignalCandidatesRemoved.connect(
474 this, &JsepTransportController::OnTransportCandidatesRemoved_n);
475 dtls->ice_transport()->SignalRoleConflict.connect(
476 this, &JsepTransportController::OnTransportRoleConflict_n);
477 dtls->ice_transport()->SignalStateChanged.connect(
478 this, &JsepTransportController::OnTransportStateChanged_n);
479 dtls->ice_transport()->SignalIceTransportStateChanged.connect(
480 this, &JsepTransportController::OnTransportStateChanged_n);
481 dtls->ice_transport()->SignalCandidatePairChanged.connect(
482 this, &JsepTransportController::OnTransportCandidatePairChanged_n);
483 return dtls;
484 }
485
486 std::unique_ptr<webrtc::RtpTransport>
CreateUnencryptedRtpTransport(const std::string & transport_name,rtc::PacketTransportInternal * rtp_packet_transport,rtc::PacketTransportInternal * rtcp_packet_transport)487 JsepTransportController::CreateUnencryptedRtpTransport(
488 const std::string& transport_name,
489 rtc::PacketTransportInternal* rtp_packet_transport,
490 rtc::PacketTransportInternal* rtcp_packet_transport) {
491 RTC_DCHECK(network_thread_->IsCurrent());
492 auto unencrypted_rtp_transport =
493 std::make_unique<RtpTransport>(rtcp_packet_transport == nullptr);
494 unencrypted_rtp_transport->SetRtpPacketTransport(rtp_packet_transport);
495 if (rtcp_packet_transport) {
496 unencrypted_rtp_transport->SetRtcpPacketTransport(rtcp_packet_transport);
497 }
498 return unencrypted_rtp_transport;
499 }
500
501 std::unique_ptr<webrtc::SrtpTransport>
CreateSdesTransport(const std::string & transport_name,cricket::DtlsTransportInternal * rtp_dtls_transport,cricket::DtlsTransportInternal * rtcp_dtls_transport)502 JsepTransportController::CreateSdesTransport(
503 const std::string& transport_name,
504 cricket::DtlsTransportInternal* rtp_dtls_transport,
505 cricket::DtlsTransportInternal* rtcp_dtls_transport) {
506 RTC_DCHECK(network_thread_->IsCurrent());
507 auto srtp_transport =
508 std::make_unique<webrtc::SrtpTransport>(rtcp_dtls_transport == nullptr);
509 RTC_DCHECK(rtp_dtls_transport);
510 srtp_transport->SetRtpPacketTransport(rtp_dtls_transport);
511 if (rtcp_dtls_transport) {
512 srtp_transport->SetRtcpPacketTransport(rtcp_dtls_transport);
513 }
514 if (config_.enable_external_auth) {
515 srtp_transport->EnableExternalAuth();
516 }
517 return srtp_transport;
518 }
519
520 std::unique_ptr<webrtc::DtlsSrtpTransport>
CreateDtlsSrtpTransport(const std::string & transport_name,cricket::DtlsTransportInternal * rtp_dtls_transport,cricket::DtlsTransportInternal * rtcp_dtls_transport)521 JsepTransportController::CreateDtlsSrtpTransport(
522 const std::string& transport_name,
523 cricket::DtlsTransportInternal* rtp_dtls_transport,
524 cricket::DtlsTransportInternal* rtcp_dtls_transport) {
525 RTC_DCHECK(network_thread_->IsCurrent());
526 auto dtls_srtp_transport = std::make_unique<webrtc::DtlsSrtpTransport>(
527 rtcp_dtls_transport == nullptr);
528 if (config_.enable_external_auth) {
529 dtls_srtp_transport->EnableExternalAuth();
530 }
531
532 dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport,
533 rtcp_dtls_transport);
534 dtls_srtp_transport->SetActiveResetSrtpParams(
535 config_.active_reset_srtp_params);
536 dtls_srtp_transport->SignalDtlsStateChange.connect(
537 this, &JsepTransportController::UpdateAggregateStates_n);
538 return dtls_srtp_transport;
539 }
540
541 std::vector<cricket::DtlsTransportInternal*>
GetDtlsTransports()542 JsepTransportController::GetDtlsTransports() {
543 std::vector<cricket::DtlsTransportInternal*> dtls_transports;
544 for (auto it = jsep_transports_by_name_.begin();
545 it != jsep_transports_by_name_.end(); ++it) {
546 auto jsep_transport = it->second.get();
547 RTC_DCHECK(jsep_transport);
548 if (jsep_transport->rtp_dtls_transport()) {
549 dtls_transports.push_back(jsep_transport->rtp_dtls_transport());
550 }
551
552 if (jsep_transport->rtcp_dtls_transport()) {
553 dtls_transports.push_back(jsep_transport->rtcp_dtls_transport());
554 }
555 }
556 return dtls_transports;
557 }
558
ApplyDescription_n(bool local,SdpType type,const cricket::SessionDescription * description)559 RTCError JsepTransportController::ApplyDescription_n(
560 bool local,
561 SdpType type,
562 const cricket::SessionDescription* description) {
563 RTC_DCHECK_RUN_ON(network_thread_);
564 RTC_DCHECK(description);
565
566 if (local) {
567 local_desc_ = description;
568 } else {
569 remote_desc_ = description;
570 }
571
572 RTCError error;
573 error = ValidateAndMaybeUpdateBundleGroup(local, type, description);
574 if (!error.ok()) {
575 return error;
576 }
577
578 std::vector<int> merged_encrypted_extension_ids;
579 if (bundle_group_) {
580 merged_encrypted_extension_ids =
581 MergeEncryptedHeaderExtensionIdsForBundle(description);
582 }
583
584 for (const cricket::ContentInfo& content_info : description->contents()) {
585 // Don't create transports for rejected m-lines and bundled m-lines."
586 if (content_info.rejected ||
587 (IsBundled(content_info.name) && content_info.name != *bundled_mid())) {
588 continue;
589 }
590 error = MaybeCreateJsepTransport(local, content_info, *description);
591 if (!error.ok()) {
592 return error;
593 }
594 }
595
596 RTC_DCHECK(description->contents().size() ==
597 description->transport_infos().size());
598 for (size_t i = 0; i < description->contents().size(); ++i) {
599 const cricket::ContentInfo& content_info = description->contents()[i];
600 const cricket::TransportInfo& transport_info =
601 description->transport_infos()[i];
602 if (content_info.rejected) {
603 HandleRejectedContent(content_info, description);
604 continue;
605 }
606
607 if (IsBundled(content_info.name) && content_info.name != *bundled_mid()) {
608 if (!HandleBundledContent(content_info)) {
609 return RTCError(RTCErrorType::INVALID_PARAMETER,
610 "Failed to process the bundled m= section with mid='" +
611 content_info.name + "'.");
612 }
613 continue;
614 }
615
616 error = ValidateContent(content_info);
617 if (!error.ok()) {
618 return error;
619 }
620
621 std::vector<int> extension_ids;
622 if (bundled_mid() && content_info.name == *bundled_mid()) {
623 extension_ids = merged_encrypted_extension_ids;
624 } else {
625 extension_ids = GetEncryptedHeaderExtensionIds(content_info);
626 }
627
628 int rtp_abs_sendtime_extn_id =
629 GetRtpAbsSendTimeHeaderExtensionId(content_info);
630
631 cricket::JsepTransport* transport =
632 GetJsepTransportForMid(content_info.name);
633 RTC_DCHECK(transport);
634
635 SetIceRole_n(DetermineIceRole(transport, transport_info, type, local));
636
637 cricket::JsepTransportDescription jsep_description =
638 CreateJsepTransportDescription(content_info, transport_info,
639 extension_ids, rtp_abs_sendtime_extn_id);
640 if (local) {
641 error =
642 transport->SetLocalJsepTransportDescription(jsep_description, type);
643 } else {
644 error =
645 transport->SetRemoteJsepTransportDescription(jsep_description, type);
646 }
647
648 if (!error.ok()) {
649 LOG_AND_RETURN_ERROR(
650 RTCErrorType::INVALID_PARAMETER,
651 "Failed to apply the description for m= section with mid='" +
652 content_info.name + "': " + error.message());
653 }
654 }
655 if (type == SdpType::kAnswer) {
656 pending_mids_.clear();
657 }
658 return RTCError::OK();
659 }
660
ValidateAndMaybeUpdateBundleGroup(bool local,SdpType type,const cricket::SessionDescription * description)661 RTCError JsepTransportController::ValidateAndMaybeUpdateBundleGroup(
662 bool local,
663 SdpType type,
664 const cricket::SessionDescription* description) {
665 RTC_DCHECK(description);
666 const cricket::ContentGroup* new_bundle_group =
667 description->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
668
669 // The BUNDLE group containing a MID that no m= section has is invalid.
670 if (new_bundle_group) {
671 for (const std::string& content_name : new_bundle_group->content_names()) {
672 if (!description->GetContentByName(content_name)) {
673 return RTCError(RTCErrorType::INVALID_PARAMETER,
674 "The BUNDLE group contains MID='" + content_name +
675 "' matching no m= section.");
676 }
677 }
678 }
679
680 if (type == SdpType::kAnswer) {
681 const cricket::ContentGroup* offered_bundle_group =
682 local ? remote_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE)
683 : local_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
684
685 if (new_bundle_group) {
686 // The BUNDLE group in answer should be a subset of offered group.
687 for (const std::string& content_name :
688 new_bundle_group->content_names()) {
689 if (!offered_bundle_group ||
690 !offered_bundle_group->HasContentName(content_name)) {
691 return RTCError(RTCErrorType::INVALID_PARAMETER,
692 "The BUNDLE group in answer contains a MID='" +
693 content_name +
694 "' that was "
695 "not in the offered group.");
696 }
697 }
698 }
699
700 if (bundle_group_) {
701 for (const std::string& content_name : bundle_group_->content_names()) {
702 // An answer that removes m= sections from pre-negotiated BUNDLE group
703 // without rejecting it, is invalid.
704 if (!new_bundle_group ||
705 !new_bundle_group->HasContentName(content_name)) {
706 auto* content_info = description->GetContentByName(content_name);
707 if (!content_info || !content_info->rejected) {
708 return RTCError(RTCErrorType::INVALID_PARAMETER,
709 "Answer cannot remove m= section with mid='" +
710 content_name +
711 "' from already-established BUNDLE group.");
712 }
713 }
714 }
715 }
716 }
717
718 if (config_.bundle_policy ==
719 PeerConnectionInterface::kBundlePolicyMaxBundle &&
720 !description->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
721 return RTCError(RTCErrorType::INVALID_PARAMETER,
722 "max-bundle is used but no bundle group found.");
723 }
724
725 if (ShouldUpdateBundleGroup(type, description)) {
726 bundle_group_ = *new_bundle_group;
727 }
728
729 if (!bundled_mid()) {
730 return RTCError::OK();
731 }
732
733 auto bundled_content = description->GetContentByName(*bundled_mid());
734 if (!bundled_content) {
735 return RTCError(
736 RTCErrorType::INVALID_PARAMETER,
737 "An m= section associated with the BUNDLE-tag doesn't exist.");
738 }
739
740 // If the |bundled_content| is rejected, other contents in the bundle group
741 // should be rejected.
742 if (bundled_content->rejected) {
743 for (const auto& content_name : bundle_group_->content_names()) {
744 auto other_content = description->GetContentByName(content_name);
745 if (!other_content->rejected) {
746 return RTCError(RTCErrorType::INVALID_PARAMETER,
747 "The m= section with mid='" + content_name +
748 "' should be rejected.");
749 }
750 }
751 }
752
753 return RTCError::OK();
754 }
755
ValidateContent(const cricket::ContentInfo & content_info)756 RTCError JsepTransportController::ValidateContent(
757 const cricket::ContentInfo& content_info) {
758 if (config_.rtcp_mux_policy ==
759 PeerConnectionInterface::kRtcpMuxPolicyRequire &&
760 content_info.type == cricket::MediaProtocolType::kRtp &&
761 !content_info.media_description()->rtcp_mux()) {
762 return RTCError(RTCErrorType::INVALID_PARAMETER,
763 "The m= section with mid='" + content_info.name +
764 "' is invalid. RTCP-MUX is not "
765 "enabled when it is required.");
766 }
767 return RTCError::OK();
768 }
769
HandleRejectedContent(const cricket::ContentInfo & content_info,const cricket::SessionDescription * description)770 void JsepTransportController::HandleRejectedContent(
771 const cricket::ContentInfo& content_info,
772 const cricket::SessionDescription* description) {
773 // If the content is rejected, let the
774 // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first,
775 // then destroy the cricket::JsepTransport.
776 RemoveTransportForMid(content_info.name);
777 if (content_info.name == bundled_mid()) {
778 for (const auto& content_name : bundle_group_->content_names()) {
779 RemoveTransportForMid(content_name);
780 }
781 bundle_group_.reset();
782 } else if (IsBundled(content_info.name)) {
783 // Remove the rejected content from the |bundle_group_|.
784 bundle_group_->RemoveContentName(content_info.name);
785 // Reset the bundle group if nothing left.
786 if (!bundle_group_->FirstContentName()) {
787 bundle_group_.reset();
788 }
789 }
790 MaybeDestroyJsepTransport(content_info.name);
791 }
792
HandleBundledContent(const cricket::ContentInfo & content_info)793 bool JsepTransportController::HandleBundledContent(
794 const cricket::ContentInfo& content_info) {
795 auto jsep_transport = GetJsepTransportByName(*bundled_mid());
796 RTC_DCHECK(jsep_transport);
797 // If the content is bundled, let the
798 // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first,
799 // then destroy the cricket::JsepTransport.
800 if (SetTransportForMid(content_info.name, jsep_transport)) {
801 // TODO(bugs.webrtc.org/9719) For media transport this is far from ideal,
802 // because it means that we first create media transport and start
803 // connecting it, and then we destroy it. We will need to address it before
804 // video path is enabled.
805 MaybeDestroyJsepTransport(content_info.name);
806 return true;
807 }
808 return false;
809 }
810
SetTransportForMid(const std::string & mid,cricket::JsepTransport * jsep_transport)811 bool JsepTransportController::SetTransportForMid(
812 const std::string& mid,
813 cricket::JsepTransport* jsep_transport) {
814 RTC_DCHECK(jsep_transport);
815 if (mid_to_transport_[mid] == jsep_transport) {
816 return true;
817 }
818 RTC_DCHECK_RUN_ON(network_thread_);
819 pending_mids_.push_back(mid);
820 mid_to_transport_[mid] = jsep_transport;
821 return config_.transport_observer->OnTransportChanged(
822 mid, jsep_transport->rtp_transport(), jsep_transport->RtpDtlsTransport(),
823 jsep_transport->data_channel_transport());
824 }
825
RemoveTransportForMid(const std::string & mid)826 void JsepTransportController::RemoveTransportForMid(const std::string& mid) {
827 bool ret = config_.transport_observer->OnTransportChanged(mid, nullptr,
828 nullptr, nullptr);
829 // Calling OnTransportChanged with nullptr should always succeed, since it is
830 // only expected to fail when adding media to a transport (not removing).
831 RTC_DCHECK(ret);
832 mid_to_transport_.erase(mid);
833 }
834
835 cricket::JsepTransportDescription
CreateJsepTransportDescription(const cricket::ContentInfo & content_info,const cricket::TransportInfo & transport_info,const std::vector<int> & encrypted_extension_ids,int rtp_abs_sendtime_extn_id)836 JsepTransportController::CreateJsepTransportDescription(
837 const cricket::ContentInfo& content_info,
838 const cricket::TransportInfo& transport_info,
839 const std::vector<int>& encrypted_extension_ids,
840 int rtp_abs_sendtime_extn_id) {
841 const cricket::MediaContentDescription* content_desc =
842 content_info.media_description();
843 RTC_DCHECK(content_desc);
844 bool rtcp_mux_enabled = content_info.type == cricket::MediaProtocolType::kSctp
845 ? true
846 : content_desc->rtcp_mux();
847
848 return cricket::JsepTransportDescription(
849 rtcp_mux_enabled, content_desc->cryptos(), encrypted_extension_ids,
850 rtp_abs_sendtime_extn_id, transport_info.description);
851 }
852
ShouldUpdateBundleGroup(SdpType type,const cricket::SessionDescription * description)853 bool JsepTransportController::ShouldUpdateBundleGroup(
854 SdpType type,
855 const cricket::SessionDescription* description) {
856 if (config_.bundle_policy ==
857 PeerConnectionInterface::kBundlePolicyMaxBundle) {
858 return true;
859 }
860
861 if (type != SdpType::kAnswer) {
862 return false;
863 }
864
865 RTC_DCHECK(local_desc_ && remote_desc_);
866 const cricket::ContentGroup* local_bundle =
867 local_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
868 const cricket::ContentGroup* remote_bundle =
869 remote_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
870 return local_bundle && remote_bundle;
871 }
872
GetEncryptedHeaderExtensionIds(const cricket::ContentInfo & content_info)873 std::vector<int> JsepTransportController::GetEncryptedHeaderExtensionIds(
874 const cricket::ContentInfo& content_info) {
875 const cricket::MediaContentDescription* content_desc =
876 content_info.media_description();
877
878 if (!config_.crypto_options.srtp.enable_encrypted_rtp_header_extensions) {
879 return std::vector<int>();
880 }
881
882 std::vector<int> encrypted_header_extension_ids;
883 for (const auto& extension : content_desc->rtp_header_extensions()) {
884 if (!extension.encrypt) {
885 continue;
886 }
887 if (!absl::c_linear_search(encrypted_header_extension_ids, extension.id)) {
888 encrypted_header_extension_ids.push_back(extension.id);
889 }
890 }
891 return encrypted_header_extension_ids;
892 }
893
894 std::vector<int>
MergeEncryptedHeaderExtensionIdsForBundle(const cricket::SessionDescription * description)895 JsepTransportController::MergeEncryptedHeaderExtensionIdsForBundle(
896 const cricket::SessionDescription* description) {
897 RTC_DCHECK(description);
898 RTC_DCHECK(bundle_group_);
899
900 std::vector<int> merged_ids;
901 // Union the encrypted header IDs in the group when bundle is enabled.
902 for (const cricket::ContentInfo& content_info : description->contents()) {
903 if (bundle_group_->HasContentName(content_info.name)) {
904 std::vector<int> extension_ids =
905 GetEncryptedHeaderExtensionIds(content_info);
906 for (int id : extension_ids) {
907 if (!absl::c_linear_search(merged_ids, id)) {
908 merged_ids.push_back(id);
909 }
910 }
911 }
912 }
913 return merged_ids;
914 }
915
GetRtpAbsSendTimeHeaderExtensionId(const cricket::ContentInfo & content_info)916 int JsepTransportController::GetRtpAbsSendTimeHeaderExtensionId(
917 const cricket::ContentInfo& content_info) {
918 if (!config_.enable_external_auth) {
919 return -1;
920 }
921
922 const cricket::MediaContentDescription* content_desc =
923 content_info.media_description();
924
925 const webrtc::RtpExtension* send_time_extension =
926 webrtc::RtpExtension::FindHeaderExtensionByUri(
927 content_desc->rtp_header_extensions(),
928 webrtc::RtpExtension::kAbsSendTimeUri);
929 return send_time_extension ? send_time_extension->id : -1;
930 }
931
GetJsepTransportForMid(const std::string & mid) const932 const cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid(
933 const std::string& mid) const {
934 auto it = mid_to_transport_.find(mid);
935 return it == mid_to_transport_.end() ? nullptr : it->second;
936 }
937
GetJsepTransportForMid(const std::string & mid)938 cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid(
939 const std::string& mid) {
940 auto it = mid_to_transport_.find(mid);
941 return it == mid_to_transport_.end() ? nullptr : it->second;
942 }
943
GetJsepTransportByName(const std::string & transport_name) const944 const cricket::JsepTransport* JsepTransportController::GetJsepTransportByName(
945 const std::string& transport_name) const {
946 auto it = jsep_transports_by_name_.find(transport_name);
947 return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
948 }
949
GetJsepTransportByName(const std::string & transport_name)950 cricket::JsepTransport* JsepTransportController::GetJsepTransportByName(
951 const std::string& transport_name) {
952 auto it = jsep_transports_by_name_.find(transport_name);
953 return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
954 }
955
MaybeCreateJsepTransport(bool local,const cricket::ContentInfo & content_info,const cricket::SessionDescription & description)956 RTCError JsepTransportController::MaybeCreateJsepTransport(
957 bool local,
958 const cricket::ContentInfo& content_info,
959 const cricket::SessionDescription& description) {
960 RTC_DCHECK(network_thread_->IsCurrent());
961 cricket::JsepTransport* transport = GetJsepTransportByName(content_info.name);
962 if (transport) {
963 return RTCError::OK();
964 }
965
966 const cricket::MediaContentDescription* content_desc =
967 content_info.media_description();
968 if (certificate_ && !content_desc->cryptos().empty()) {
969 return RTCError(RTCErrorType::INVALID_PARAMETER,
970 "SDES and DTLS-SRTP cannot be enabled at the same time.");
971 }
972
973 rtc::scoped_refptr<webrtc::IceTransportInterface> ice =
974 CreateIceTransport(content_info.name, /*rtcp=*/false);
975 RTC_DCHECK(ice);
976
977 std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
978 CreateDtlsTransport(content_info, ice->internal());
979
980 std::unique_ptr<cricket::DtlsTransportInternal> rtcp_dtls_transport;
981 std::unique_ptr<RtpTransport> unencrypted_rtp_transport;
982 std::unique_ptr<SrtpTransport> sdes_transport;
983 std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport;
984 std::unique_ptr<RtpTransportInternal> datagram_rtp_transport;
985
986 rtc::scoped_refptr<webrtc::IceTransportInterface> rtcp_ice;
987 if (config_.rtcp_mux_policy !=
988 PeerConnectionInterface::kRtcpMuxPolicyRequire &&
989 content_info.type == cricket::MediaProtocolType::kRtp) {
990 rtcp_ice = CreateIceTransport(content_info.name, /*rtcp=*/true);
991 rtcp_dtls_transport =
992 CreateDtlsTransport(content_info, rtcp_ice->internal());
993 }
994
995 if (config_.disable_encryption) {
996 RTC_LOG(LS_INFO)
997 << "Creating UnencryptedRtpTransport, becayse encryption is disabled.";
998 unencrypted_rtp_transport = CreateUnencryptedRtpTransport(
999 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
1000 } else if (!content_desc->cryptos().empty()) {
1001 sdes_transport = CreateSdesTransport(
1002 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
1003 RTC_LOG(LS_INFO) << "Creating SdesTransport.";
1004 } else {
1005 RTC_LOG(LS_INFO) << "Creating DtlsSrtpTransport.";
1006 dtls_srtp_transport = CreateDtlsSrtpTransport(
1007 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
1008 }
1009
1010 std::unique_ptr<cricket::SctpTransportInternal> sctp_transport;
1011 if (config_.sctp_factory) {
1012 sctp_transport =
1013 config_.sctp_factory->CreateSctpTransport(rtp_dtls_transport.get());
1014 }
1015
1016 std::unique_ptr<cricket::JsepTransport> jsep_transport =
1017 std::make_unique<cricket::JsepTransport>(
1018 content_info.name, certificate_, std::move(ice), std::move(rtcp_ice),
1019 std::move(unencrypted_rtp_transport), std::move(sdes_transport),
1020 std::move(dtls_srtp_transport), std::move(datagram_rtp_transport),
1021 std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport),
1022 std::move(sctp_transport));
1023
1024 jsep_transport->rtp_transport()->SignalRtcpPacketReceived.connect(
1025 this, &JsepTransportController::OnRtcpPacketReceived_n);
1026
1027 jsep_transport->SignalRtcpMuxActive.connect(
1028 this, &JsepTransportController::UpdateAggregateStates_n);
1029 SetTransportForMid(content_info.name, jsep_transport.get());
1030
1031 jsep_transports_by_name_[content_info.name] = std::move(jsep_transport);
1032 UpdateAggregateStates_n();
1033 return RTCError::OK();
1034 }
1035
MaybeDestroyJsepTransport(const std::string & mid)1036 void JsepTransportController::MaybeDestroyJsepTransport(
1037 const std::string& mid) {
1038 auto jsep_transport = GetJsepTransportByName(mid);
1039 if (!jsep_transport) {
1040 return;
1041 }
1042
1043 // Don't destroy the JsepTransport if there are still media sections referring
1044 // to it.
1045 for (const auto& kv : mid_to_transport_) {
1046 if (kv.second == jsep_transport) {
1047 return;
1048 }
1049 }
1050
1051 jsep_transports_by_name_.erase(mid);
1052 UpdateAggregateStates_n();
1053 }
1054
DestroyAllJsepTransports_n()1055 void JsepTransportController::DestroyAllJsepTransports_n() {
1056 RTC_DCHECK(network_thread_->IsCurrent());
1057
1058 for (const auto& jsep_transport : jsep_transports_by_name_) {
1059 config_.transport_observer->OnTransportChanged(jsep_transport.first,
1060 nullptr, nullptr, nullptr);
1061 }
1062
1063 jsep_transports_by_name_.clear();
1064 }
1065
SetIceRole_n(cricket::IceRole ice_role)1066 void JsepTransportController::SetIceRole_n(cricket::IceRole ice_role) {
1067 RTC_DCHECK(network_thread_->IsCurrent());
1068
1069 ice_role_ = ice_role;
1070 for (auto& dtls : GetDtlsTransports()) {
1071 dtls->ice_transport()->SetIceRole(ice_role_);
1072 }
1073 }
1074
DetermineIceRole(cricket::JsepTransport * jsep_transport,const cricket::TransportInfo & transport_info,SdpType type,bool local)1075 cricket::IceRole JsepTransportController::DetermineIceRole(
1076 cricket::JsepTransport* jsep_transport,
1077 const cricket::TransportInfo& transport_info,
1078 SdpType type,
1079 bool local) {
1080 cricket::IceRole ice_role = ice_role_;
1081 auto tdesc = transport_info.description;
1082 if (local) {
1083 // The initial offer side may use ICE Lite, in which case, per RFC5245
1084 // Section 5.1.1, the answer side should take the controlling role if it is
1085 // in the full ICE mode.
1086 //
1087 // When both sides use ICE Lite, the initial offer side must take the
1088 // controlling role, and this is the default logic implemented in
1089 // SetLocalDescription in JsepTransportController.
1090 if (jsep_transport->remote_description() &&
1091 jsep_transport->remote_description()->transport_desc.ice_mode ==
1092 cricket::ICEMODE_LITE &&
1093 ice_role_ == cricket::ICEROLE_CONTROLLED &&
1094 tdesc.ice_mode == cricket::ICEMODE_FULL) {
1095 ice_role = cricket::ICEROLE_CONTROLLING;
1096 }
1097 } else {
1098 // If our role is cricket::ICEROLE_CONTROLLED and the remote endpoint
1099 // supports only ice_lite, this local endpoint should take the CONTROLLING
1100 // role.
1101 // TODO(deadbeef): This is a session-level attribute, so it really shouldn't
1102 // be in a TransportDescription in the first place...
1103 if (ice_role_ == cricket::ICEROLE_CONTROLLED &&
1104 tdesc.ice_mode == cricket::ICEMODE_LITE) {
1105 ice_role = cricket::ICEROLE_CONTROLLING;
1106 }
1107
1108 // If we use ICE Lite and the remote endpoint uses the full implementation
1109 // of ICE, the local endpoint must take the controlled role, and the other
1110 // side must be the controlling role.
1111 if (jsep_transport->local_description() &&
1112 jsep_transport->local_description()->transport_desc.ice_mode ==
1113 cricket::ICEMODE_LITE &&
1114 ice_role_ == cricket::ICEROLE_CONTROLLING &&
1115 tdesc.ice_mode == cricket::ICEMODE_FULL) {
1116 ice_role = cricket::ICEROLE_CONTROLLED;
1117 }
1118 }
1119
1120 return ice_role;
1121 }
1122
OnTransportWritableState_n(rtc::PacketTransportInternal * transport)1123 void JsepTransportController::OnTransportWritableState_n(
1124 rtc::PacketTransportInternal* transport) {
1125 RTC_DCHECK(network_thread_->IsCurrent());
1126 RTC_LOG(LS_INFO) << " Transport " << transport->transport_name()
1127 << " writability changed to " << transport->writable()
1128 << ".";
1129 UpdateAggregateStates_n();
1130 }
1131
OnTransportReceivingState_n(rtc::PacketTransportInternal * transport)1132 void JsepTransportController::OnTransportReceivingState_n(
1133 rtc::PacketTransportInternal* transport) {
1134 RTC_DCHECK(network_thread_->IsCurrent());
1135 UpdateAggregateStates_n();
1136 }
1137
OnTransportGatheringState_n(cricket::IceTransportInternal * transport)1138 void JsepTransportController::OnTransportGatheringState_n(
1139 cricket::IceTransportInternal* transport) {
1140 RTC_DCHECK(network_thread_->IsCurrent());
1141 UpdateAggregateStates_n();
1142 }
1143
OnTransportCandidateGathered_n(cricket::IceTransportInternal * transport,const cricket::Candidate & candidate)1144 void JsepTransportController::OnTransportCandidateGathered_n(
1145 cricket::IceTransportInternal* transport,
1146 const cricket::Candidate& candidate) {
1147 RTC_DCHECK(network_thread_->IsCurrent());
1148
1149 // We should never signal peer-reflexive candidates.
1150 if (candidate.type() == cricket::PRFLX_PORT_TYPE) {
1151 RTC_NOTREACHED();
1152 return;
1153 }
1154 std::string transport_name = transport->transport_name();
1155 invoker_.AsyncInvoke<void>(
1156 RTC_FROM_HERE, signaling_thread_, [this, transport_name, candidate] {
1157 SignalIceCandidatesGathered(transport_name, {candidate});
1158 });
1159 }
1160
OnTransportCandidateError_n(cricket::IceTransportInternal * transport,const cricket::IceCandidateErrorEvent & event)1161 void JsepTransportController::OnTransportCandidateError_n(
1162 cricket::IceTransportInternal* transport,
1163 const cricket::IceCandidateErrorEvent& event) {
1164 RTC_DCHECK(network_thread_->IsCurrent());
1165
1166 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1167 [this, event] { SignalIceCandidateError(event); });
1168 }
OnTransportCandidatesRemoved_n(cricket::IceTransportInternal * transport,const cricket::Candidates & candidates)1169 void JsepTransportController::OnTransportCandidatesRemoved_n(
1170 cricket::IceTransportInternal* transport,
1171 const cricket::Candidates& candidates) {
1172 invoker_.AsyncInvoke<void>(
1173 RTC_FROM_HERE, signaling_thread_,
1174 [this, candidates] { SignalIceCandidatesRemoved(candidates); });
1175 }
OnTransportCandidatePairChanged_n(const cricket::CandidatePairChangeEvent & event)1176 void JsepTransportController::OnTransportCandidatePairChanged_n(
1177 const cricket::CandidatePairChangeEvent& event) {
1178 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_, [this, event] {
1179 SignalIceCandidatePairChanged(event);
1180 });
1181 }
1182
OnTransportRoleConflict_n(cricket::IceTransportInternal * transport)1183 void JsepTransportController::OnTransportRoleConflict_n(
1184 cricket::IceTransportInternal* transport) {
1185 RTC_DCHECK(network_thread_->IsCurrent());
1186 // Note: since the role conflict is handled entirely on the network thread,
1187 // we don't need to worry about role conflicts occurring on two ports at
1188 // once. The first one encountered should immediately reverse the role.
1189 cricket::IceRole reversed_role = (ice_role_ == cricket::ICEROLE_CONTROLLING)
1190 ? cricket::ICEROLE_CONTROLLED
1191 : cricket::ICEROLE_CONTROLLING;
1192 RTC_LOG(LS_INFO) << "Got role conflict; switching to "
1193 << (reversed_role == cricket::ICEROLE_CONTROLLING
1194 ? "controlling"
1195 : "controlled")
1196 << " role.";
1197 SetIceRole_n(reversed_role);
1198 }
1199
OnTransportStateChanged_n(cricket::IceTransportInternal * transport)1200 void JsepTransportController::OnTransportStateChanged_n(
1201 cricket::IceTransportInternal* transport) {
1202 RTC_DCHECK(network_thread_->IsCurrent());
1203 RTC_LOG(LS_INFO) << transport->transport_name() << " Transport "
1204 << transport->component()
1205 << " state changed. Check if state is complete.";
1206 UpdateAggregateStates_n();
1207 }
1208
UpdateAggregateStates_n()1209 void JsepTransportController::UpdateAggregateStates_n() {
1210 RTC_DCHECK(network_thread_->IsCurrent());
1211
1212 auto dtls_transports = GetDtlsTransports();
1213 cricket::IceConnectionState new_connection_state =
1214 cricket::kIceConnectionConnecting;
1215 PeerConnectionInterface::IceConnectionState new_ice_connection_state =
1216 PeerConnectionInterface::IceConnectionState::kIceConnectionNew;
1217 PeerConnectionInterface::PeerConnectionState new_combined_state =
1218 PeerConnectionInterface::PeerConnectionState::kNew;
1219 cricket::IceGatheringState new_gathering_state = cricket::kIceGatheringNew;
1220 bool any_failed = false;
1221 bool all_connected = !dtls_transports.empty();
1222 bool all_completed = !dtls_transports.empty();
1223 bool any_gathering = false;
1224 bool all_done_gathering = !dtls_transports.empty();
1225
1226 std::map<IceTransportState, int> ice_state_counts;
1227 std::map<cricket::DtlsTransportState, int> dtls_state_counts;
1228
1229 for (const auto& dtls : dtls_transports) {
1230 any_failed = any_failed || dtls->ice_transport()->GetState() ==
1231 cricket::IceTransportState::STATE_FAILED;
1232 all_connected = all_connected && dtls->writable();
1233 all_completed =
1234 all_completed && dtls->writable() &&
1235 dtls->ice_transport()->GetState() ==
1236 cricket::IceTransportState::STATE_COMPLETED &&
1237 dtls->ice_transport()->GetIceRole() == cricket::ICEROLE_CONTROLLING &&
1238 dtls->ice_transport()->gathering_state() ==
1239 cricket::kIceGatheringComplete;
1240 any_gathering = any_gathering || dtls->ice_transport()->gathering_state() !=
1241 cricket::kIceGatheringNew;
1242 all_done_gathering =
1243 all_done_gathering && dtls->ice_transport()->gathering_state() ==
1244 cricket::kIceGatheringComplete;
1245
1246 dtls_state_counts[dtls->dtls_state()]++;
1247 ice_state_counts[dtls->ice_transport()->GetIceTransportState()]++;
1248 }
1249
1250 if (any_failed) {
1251 new_connection_state = cricket::kIceConnectionFailed;
1252 } else if (all_completed) {
1253 new_connection_state = cricket::kIceConnectionCompleted;
1254 } else if (all_connected) {
1255 new_connection_state = cricket::kIceConnectionConnected;
1256 }
1257 if (ice_connection_state_ != new_connection_state) {
1258 ice_connection_state_ = new_connection_state;
1259 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1260 [this, new_connection_state] {
1261 SignalIceConnectionState(new_connection_state);
1262 });
1263 }
1264
1265 // Compute the current RTCIceConnectionState as described in
1266 // https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate.
1267 // The PeerConnection is responsible for handling the "closed" state.
1268 int total_ice_checking = ice_state_counts[IceTransportState::kChecking];
1269 int total_ice_connected = ice_state_counts[IceTransportState::kConnected];
1270 int total_ice_completed = ice_state_counts[IceTransportState::kCompleted];
1271 int total_ice_failed = ice_state_counts[IceTransportState::kFailed];
1272 int total_ice_disconnected =
1273 ice_state_counts[IceTransportState::kDisconnected];
1274 int total_ice_closed = ice_state_counts[IceTransportState::kClosed];
1275 int total_ice_new = ice_state_counts[IceTransportState::kNew];
1276 int total_ice = dtls_transports.size();
1277
1278 if (total_ice_failed > 0) {
1279 // Any RTCIceTransports are in the "failed" state.
1280 new_ice_connection_state = PeerConnectionInterface::kIceConnectionFailed;
1281 } else if (total_ice_disconnected > 0) {
1282 // None of the previous states apply and any RTCIceTransports are in the
1283 // "disconnected" state.
1284 new_ice_connection_state =
1285 PeerConnectionInterface::kIceConnectionDisconnected;
1286 } else if (total_ice_new + total_ice_closed == total_ice) {
1287 // None of the previous states apply and all RTCIceTransports are in the
1288 // "new" or "closed" state, or there are no transports.
1289 new_ice_connection_state = PeerConnectionInterface::kIceConnectionNew;
1290 } else if (total_ice_new + total_ice_checking > 0) {
1291 // None of the previous states apply and any RTCIceTransports are in the
1292 // "new" or "checking" state.
1293 new_ice_connection_state = PeerConnectionInterface::kIceConnectionChecking;
1294 } else if (total_ice_completed + total_ice_closed == total_ice ||
1295 all_completed) {
1296 // None of the previous states apply and all RTCIceTransports are in the
1297 // "completed" or "closed" state.
1298 //
1299 // TODO(https://bugs.webrtc.org/10356): The all_completed condition is added
1300 // to mimic the behavior of the old ICE connection state, and should be
1301 // removed once we get end-of-candidates signaling in place.
1302 new_ice_connection_state = PeerConnectionInterface::kIceConnectionCompleted;
1303 } else if (total_ice_connected + total_ice_completed + total_ice_closed ==
1304 total_ice) {
1305 // None of the previous states apply and all RTCIceTransports are in the
1306 // "connected", "completed" or "closed" state.
1307 new_ice_connection_state = PeerConnectionInterface::kIceConnectionConnected;
1308 } else {
1309 RTC_NOTREACHED();
1310 }
1311
1312 if (standardized_ice_connection_state_ != new_ice_connection_state) {
1313 if (standardized_ice_connection_state_ ==
1314 PeerConnectionInterface::kIceConnectionChecking &&
1315 new_ice_connection_state ==
1316 PeerConnectionInterface::kIceConnectionCompleted) {
1317 // Ensure that we never skip over the "connected" state.
1318 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_, [this] {
1319 SignalStandardizedIceConnectionState(
1320 PeerConnectionInterface::kIceConnectionConnected);
1321 });
1322 }
1323 standardized_ice_connection_state_ = new_ice_connection_state;
1324 invoker_.AsyncInvoke<void>(
1325 RTC_FROM_HERE, signaling_thread_, [this, new_ice_connection_state] {
1326 SignalStandardizedIceConnectionState(new_ice_connection_state);
1327 });
1328 }
1329
1330 // Compute the current RTCPeerConnectionState as described in
1331 // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnectionstate.
1332 // The PeerConnection is responsible for handling the "closed" state.
1333 // Note that "connecting" is only a valid state for DTLS transports while
1334 // "checking", "completed" and "disconnected" are only valid for ICE
1335 // transports.
1336 int total_connected = total_ice_connected +
1337 dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTED];
1338 int total_dtls_connecting =
1339 dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTING];
1340 int total_failed =
1341 total_ice_failed + dtls_state_counts[cricket::DTLS_TRANSPORT_FAILED];
1342 int total_closed =
1343 total_ice_closed + dtls_state_counts[cricket::DTLS_TRANSPORT_CLOSED];
1344 int total_new =
1345 total_ice_new + dtls_state_counts[cricket::DTLS_TRANSPORT_NEW];
1346 int total_transports = total_ice * 2;
1347
1348 if (total_failed > 0) {
1349 // Any of the RTCIceTransports or RTCDtlsTransports are in a "failed" state.
1350 new_combined_state = PeerConnectionInterface::PeerConnectionState::kFailed;
1351 } else if (total_ice_disconnected > 0) {
1352 // None of the previous states apply and any RTCIceTransports or
1353 // RTCDtlsTransports are in the "disconnected" state.
1354 new_combined_state =
1355 PeerConnectionInterface::PeerConnectionState::kDisconnected;
1356 } else if (total_new + total_closed == total_transports) {
1357 // None of the previous states apply and all RTCIceTransports and
1358 // RTCDtlsTransports are in the "new" or "closed" state, or there are no
1359 // transports.
1360 new_combined_state = PeerConnectionInterface::PeerConnectionState::kNew;
1361 } else if (total_new + total_dtls_connecting + total_ice_checking > 0) {
1362 // None of the previous states apply and all RTCIceTransports or
1363 // RTCDtlsTransports are in the "new", "connecting" or "checking" state.
1364 new_combined_state =
1365 PeerConnectionInterface::PeerConnectionState::kConnecting;
1366 } else if (total_connected + total_ice_completed + total_closed ==
1367 total_transports) {
1368 // None of the previous states apply and all RTCIceTransports and
1369 // RTCDtlsTransports are in the "connected", "completed" or "closed" state.
1370 new_combined_state =
1371 PeerConnectionInterface::PeerConnectionState::kConnected;
1372 } else {
1373 RTC_NOTREACHED();
1374 }
1375
1376 if (combined_connection_state_ != new_combined_state) {
1377 combined_connection_state_ = new_combined_state;
1378 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1379 [this, new_combined_state] {
1380 SignalConnectionState(new_combined_state);
1381 });
1382 }
1383
1384 if (all_done_gathering) {
1385 new_gathering_state = cricket::kIceGatheringComplete;
1386 } else if (any_gathering) {
1387 new_gathering_state = cricket::kIceGatheringGathering;
1388 }
1389 if (ice_gathering_state_ != new_gathering_state) {
1390 ice_gathering_state_ = new_gathering_state;
1391 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1392 [this, new_gathering_state] {
1393 SignalIceGatheringState(new_gathering_state);
1394 });
1395 }
1396 }
1397
OnRtcpPacketReceived_n(rtc::CopyOnWriteBuffer * packet,int64_t packet_time_us)1398 void JsepTransportController::OnRtcpPacketReceived_n(
1399 rtc::CopyOnWriteBuffer* packet,
1400 int64_t packet_time_us) {
1401 RTC_DCHECK(config_.rtcp_handler);
1402 config_.rtcp_handler(*packet, packet_time_us);
1403 }
1404
OnDtlsHandshakeError(rtc::SSLHandshakeError error)1405 void JsepTransportController::OnDtlsHandshakeError(
1406 rtc::SSLHandshakeError error) {
1407 SignalDtlsHandshakeError(error);
1408 }
1409
1410 } // namespace webrtc
1411