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