blob: 97f6bdde46fc0604ba9bee1b604e96c6f99982a6 [file] [log] [blame]
Zhi Huange818b6e2018-02-22 15:26:27 -08001/*
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
Steve Anton10542f22019-01-11 09:11:00 -080011#include "pc/jsep_transport_controller.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080012
Zhi Huange818b6e2018-02-22 15:26:27 -080013#include <memory>
14#include <utility>
15
Steve Anton64b626b2019-01-28 17:25:26 -080016#include "absl/algorithm/container.h"
Zach Steinc64078f2018-11-27 15:53:01 -080017#include "absl/memory/memory.h"
Piotr (Peter) Slatala2b5baee2019-01-16 08:25:21 -080018#include "p2p/base/ice_transport_internal.h"
19#include "p2p/base/no_op_dtls_transport.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080020#include "p2p/base/port.h"
Steve Anton10542f22019-01-11 09:11:00 -080021#include "pc/srtp_filter.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080022#include "rtc_base/bind.h"
23#include "rtc_base/checks.h"
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -070024#include "rtc_base/key_derivation.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080025#include "rtc_base/thread.h"
26
27using webrtc::SdpType;
28
29namespace {
30
Zhi Huange818b6e2018-02-22 15:26:27 -080031webrtc::RTCError VerifyCandidate(const cricket::Candidate& cand) {
32 // No address zero.
33 if (cand.address().IsNil() || cand.address().IsAnyIP()) {
34 return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
35 "candidate has address of zero");
36 }
37
38 // Disallow all ports below 1024, except for 80 and 443 on public addresses.
39 int port = cand.address().port();
40 if (cand.protocol() == cricket::TCP_PROTOCOL_NAME &&
41 (cand.tcptype() == cricket::TCPTYPE_ACTIVE_STR || port == 0)) {
42 // Expected for active-only candidates per
43 // http://tools.ietf.org/html/rfc6544#section-4.5 so no error.
44 // Libjingle clients emit port 0, in "active" mode.
45 return webrtc::RTCError::OK();
46 }
47 if (port < 1024) {
48 if ((port != 80) && (port != 443)) {
49 return webrtc::RTCError(
50 webrtc::RTCErrorType::INVALID_PARAMETER,
51 "candidate has port below 1024, but not 80 or 443");
52 }
53
54 if (cand.address().IsPrivateIP()) {
55 return webrtc::RTCError(
56 webrtc::RTCErrorType::INVALID_PARAMETER,
57 "candidate has port of 80 or 443 with private IP address");
58 }
59 }
60
61 return webrtc::RTCError::OK();
62}
63
64webrtc::RTCError VerifyCandidates(const cricket::Candidates& candidates) {
65 for (const cricket::Candidate& candidate : candidates) {
66 webrtc::RTCError error = VerifyCandidate(candidate);
67 if (!error.ok()) {
68 return error;
69 }
70 }
71 return webrtc::RTCError::OK();
72}
73
74} // namespace
75
76namespace webrtc {
77
78JsepTransportController::JsepTransportController(
79 rtc::Thread* signaling_thread,
80 rtc::Thread* network_thread,
81 cricket::PortAllocator* port_allocator,
Zach Steine20867f2018-08-02 13:20:15 -070082 AsyncResolverFactory* async_resolver_factory,
Zhi Huange818b6e2018-02-22 15:26:27 -080083 Config config)
84 : signaling_thread_(signaling_thread),
85 network_thread_(network_thread),
86 port_allocator_(port_allocator),
Zach Steine20867f2018-08-02 13:20:15 -070087 async_resolver_factory_(async_resolver_factory),
Zhi Huang365381f2018-04-13 16:44:34 -070088 config_(config) {
89 // The |transport_observer| is assumed to be non-null.
90 RTC_DCHECK(config_.transport_observer);
91}
Zhi Huange818b6e2018-02-22 15:26:27 -080092
93JsepTransportController::~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
101RTCError 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
120RTCError 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
131RtpTransportInternal* JsepTransportController::GetRtpTransport(
132 const std::string& mid) const {
Zhi Huange830e682018-03-30 10:48:35 -0700133 auto jsep_transport = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 15:26:27 -0800134 if (!jsep_transport) {
135 return nullptr;
136 }
137 return jsep_transport->rtp_transport();
138}
139
Anton Sukhanov7940da02018-10-10 10:34:49 -0700140MediaTransportInterface* JsepTransportController::GetMediaTransport(
141 const std::string& mid) const {
142 auto jsep_transport = GetJsepTransportForMid(mid);
143 if (!jsep_transport) {
144 return nullptr;
145 }
146 return jsep_transport->media_transport();
147}
148
Bjorn Mellem175aa2e2018-11-08 11:23:22 -0800149MediaTransportState JsepTransportController::GetMediaTransportState(
150 const std::string& mid) const {
151 auto jsep_transport = GetJsepTransportForMid(mid);
152 if (!jsep_transport) {
153 return MediaTransportState::kPending;
154 }
155 return jsep_transport->media_transport_state();
156}
157
Zhi Huange818b6e2018-02-22 15:26:27 -0800158cricket::DtlsTransportInternal* JsepTransportController::GetDtlsTransport(
Harald Alvestrandad88c882018-11-28 16:47:46 +0100159 const std::string& mid) {
Zhi Huange830e682018-03-30 10:48:35 -0700160 auto jsep_transport = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 15:26:27 -0800161 if (!jsep_transport) {
162 return nullptr;
163 }
164 return jsep_transport->rtp_dtls_transport();
165}
166
Harald Alvestrandad88c882018-11-28 16:47:46 +0100167const cricket::DtlsTransportInternal*
168JsepTransportController::GetRtcpDtlsTransport(const std::string& mid) const {
Zhi Huange830e682018-03-30 10:48:35 -0700169 auto jsep_transport = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 15:26:27 -0800170 if (!jsep_transport) {
171 return nullptr;
172 }
173 return jsep_transport->rtcp_dtls_transport();
174}
175
Harald Alvestrand4a7b3ac2019-01-17 10:39:40 +0100176rtc::scoped_refptr<webrtc::DtlsTransport>
Harald Alvestrandad88c882018-11-28 16:47:46 +0100177JsepTransportController::LookupDtlsTransportByMid(const std::string& mid) {
178 auto jsep_transport = GetJsepTransportForMid(mid);
179 if (!jsep_transport) {
180 return nullptr;
181 }
182 return jsep_transport->RtpDtlsTransport();
183}
184
Zhi Huange818b6e2018-02-22 15:26:27 -0800185void 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
197void JsepTransportController::SetNeedsIceRestartFlag() {
Zhi Huange830e682018-03-30 10:48:35 -0700198 for (auto& kv : jsep_transports_by_name_) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800199 kv.second->SetNeedsIceRestartFlag();
200 }
201}
202
203bool JsepTransportController::NeedsIceRestart(
204 const std::string& transport_name) const {
Zhi Huang365381f2018-04-13 16:44:34 -0700205 const cricket::JsepTransport* transport =
Zhi Huange830e682018-03-30 10:48:35 -0700206 GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800207 if (!transport) {
208 return false;
209 }
210 return transport->needs_ice_restart();
211}
212
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200213absl::optional<rtc::SSLRole> JsepTransportController::GetDtlsRole(
Zhi Huange830e682018-03-30 10:48:35 -0700214 const std::string& mid) const {
Zhi Huange818b6e2018-02-22 15:26:27 -0800215 if (!network_thread_->IsCurrent()) {
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200216 return network_thread_->Invoke<absl::optional<rtc::SSLRole>>(
Zhi Huange830e682018-03-30 10:48:35 -0700217 RTC_FROM_HERE, [&] { return GetDtlsRole(mid); });
Zhi Huange818b6e2018-02-22 15:26:27 -0800218 }
219
Zhi Huang365381f2018-04-13 16:44:34 -0700220 const cricket::JsepTransport* t = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 15:26:27 -0800221 if (!t) {
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200222 return absl::optional<rtc::SSLRole>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800223 }
224 return t->GetDtlsRole();
225}
226
227bool 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.
Zhi Huange830e682018-03-30 10:48:35 -0700243 for (auto& kv : jsep_transports_by_name_) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800244 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
253rtc::scoped_refptr<rtc::RTCCertificate>
254JsepTransportController::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
Zhi Huang365381f2018-04-13 16:44:34 -0700261 const cricket::JsepTransport* t = GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800262 if (!t) {
263 return nullptr;
264 }
265 return t->GetLocalCertificate();
266}
267
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800268std::unique_ptr<rtc::SSLCertChain>
269JsepTransportController::GetRemoteSSLCertChain(
Zhi Huange818b6e2018-02-22 15:26:27 -0800270 const std::string& transport_name) const {
271 if (!network_thread_->IsCurrent()) {
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800272 return network_thread_->Invoke<std::unique_ptr<rtc::SSLCertChain>>(
273 RTC_FROM_HERE, [&] { return GetRemoteSSLCertChain(transport_name); });
Zhi Huange818b6e2018-02-22 15:26:27 -0800274 }
275
Zhi Huange830e682018-03-30 10:48:35 -0700276 // 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
Zhi Huange818b6e2018-02-22 15:26:27 -0800278 // fingerprint.
Zhi Huange830e682018-03-30 10:48:35 -0700279 auto jsep_transport = GetJsepTransportByName(transport_name);
280 if (!jsep_transport) {
281 return nullptr;
282 }
283 auto dtls = jsep_transport->rtp_dtls_transport();
Zhi Huange818b6e2018-02-22 15:26:27 -0800284 if (!dtls) {
285 return nullptr;
286 }
287
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800288 return dtls->GetRemoteSSLCertChain();
Zhi Huange818b6e2018-02-22 15:26:27 -0800289}
290
291void 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
303RTCError 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 }
Zhi Huange830e682018-03-30 10:48:35 -0700317 auto jsep_transport = GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800318 if (!jsep_transport) {
Zhi Huange830e682018-03-30 10:48:35 -0700319 RTC_LOG(LS_WARNING) << "Not adding candidate because the JsepTransport "
320 "doesn't exist. Ignore it.";
321 return RTCError::OK();
Zhi Huange818b6e2018-02-22 15:26:27 -0800322 }
Zhi Huange818b6e2018-02-22 15:26:27 -0800323 return jsep_transport->AddRemoteCandidates(candidates);
324}
325
326RTCError 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.ToString();
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;
Zhi Huang365381f2018-04-13 16:44:34 -0700353 cricket::JsepTransport* jsep_transport =
Zhi Huange830e682018-03-30 10:48:35 -0700354 GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800355 if (!jsep_transport) {
Zhi Huange830e682018-03-30 10:48:35 -0700356 RTC_LOG(LS_WARNING)
357 << "Not removing candidate because the JsepTransport doesn't exist.";
358 continue;
Zhi Huange818b6e2018-02-22 15:26:27 -0800359 }
360 for (const cricket::Candidate& candidate : candidates) {
Harald Alvestrandad88c882018-11-28 16:47:46 +0100361 cricket::DtlsTransportInternal* dtls =
362 candidate.component() == cricket::ICE_CANDIDATE_COMPONENT_RTP
363 ? jsep_transport->rtp_dtls_transport()
364 : jsep_transport->rtcp_dtls_transport();
Zhi Huange818b6e2018-02-22 15:26:27 -0800365 if (dtls) {
366 dtls->ice_transport()->RemoveRemoteCandidate(candidate);
367 }
368 }
369 }
370 return RTCError::OK();
371}
372
373bool 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
Zhi Huang365381f2018-04-13 16:44:34 -0700380 cricket::JsepTransport* transport = GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800381 if (!transport) {
382 return false;
383 }
384 return transport->GetStats(stats);
385}
386
Zhi Huangb57e1692018-06-12 11:41:11 -0700387void 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
Piotr (Peter) Slatala55b91b92019-01-25 13:31:15 -0800405void JsepTransportController::SetMediaTransportSettings(
406 bool use_media_transport_for_media,
407 bool use_media_transport_for_data_channels) {
408 RTC_DCHECK(use_media_transport_for_media ==
409 config_.use_media_transport_for_media ||
Piotr (Peter) Slatala97fc11f2018-10-18 12:57:59 -0700410 jsep_transports_by_name_.empty())
Piotr (Peter) Slatala55b91b92019-01-25 13:31:15 -0800411 << "You can only change media transport configuration before creating "
412 "the first transport.";
413
414 RTC_DCHECK(use_media_transport_for_data_channels ==
415 config_.use_media_transport_for_data_channels ||
416 jsep_transports_by_name_.empty())
417 << "You can only change media transport configuration before creating "
418 "the first transport.";
419
420 config_.use_media_transport_for_media = use_media_transport_for_media;
421 config_.use_media_transport_for_data_channels =
422 use_media_transport_for_data_channels;
Piotr (Peter) Slatala97fc11f2018-10-18 12:57:59 -0700423}
424
Piotr (Peter) Slatala2b5baee2019-01-16 08:25:21 -0800425std::unique_ptr<cricket::IceTransportInternal>
426JsepTransportController::CreateIceTransport(const std::string transport_name,
427 bool rtcp) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800428 int component = rtcp ? cricket::ICE_CANDIDATE_COMPONENT_RTCP
429 : cricket::ICE_CANDIDATE_COMPONENT_RTP;
430
Zhi Huange818b6e2018-02-22 15:26:27 -0800431 if (config_.external_transport_factory) {
Piotr (Peter) Slatala2b5baee2019-01-16 08:25:21 -0800432 return config_.external_transport_factory->CreateIceTransport(
Zhi Huange818b6e2018-02-22 15:26:27 -0800433 transport_name, component);
Piotr (Peter) Slatala2b5baee2019-01-16 08:25:21 -0800434 } else {
435 return absl::make_unique<cricket::P2PTransportChannel>(
436 transport_name, component, port_allocator_, async_resolver_factory_,
437 config_.event_log);
438 }
439}
440
441std::unique_ptr<cricket::DtlsTransportInternal>
442JsepTransportController::CreateDtlsTransport(
443 std::unique_ptr<cricket::IceTransportInternal> ice) {
444 RTC_DCHECK(network_thread_->IsCurrent());
445
446 std::unique_ptr<cricket::DtlsTransportInternal> dtls;
Piotr (Peter) Slatala55b91b92019-01-25 13:31:15 -0800447 // If media transport is used for both media and data channels,
448 // then we don't need to create DTLS.
449 // Otherwise, DTLS is still created.
450 if (is_media_transport_factory_enabled_ && config_.media_transport_factory &&
451 config_.use_media_transport_for_media &&
452 config_.use_media_transport_for_data_channels) {
Piotr (Peter) Slatala2b5baee2019-01-16 08:25:21 -0800453 dtls = absl::make_unique<cricket::NoOpDtlsTransport>(
454 std::move(ice), config_.crypto_options);
455 } else if (config_.external_transport_factory) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800456 dtls = config_.external_transport_factory->CreateDtlsTransport(
457 std::move(ice), config_.crypto_options);
458 } else {
Zach Steinc64078f2018-11-27 15:53:01 -0800459 dtls = absl::make_unique<cricket::DtlsTransport>(
460 std::move(ice), config_.crypto_options, config_.event_log);
Zhi Huange818b6e2018-02-22 15:26:27 -0800461 }
462
463 RTC_DCHECK(dtls);
464 dtls->SetSslMaxProtocolVersion(config_.ssl_max_version);
Zhi Huange818b6e2018-02-22 15:26:27 -0800465 dtls->ice_transport()->SetIceRole(ice_role_);
466 dtls->ice_transport()->SetIceTiebreaker(ice_tiebreaker_);
467 dtls->ice_transport()->SetIceConfig(ice_config_);
468 if (certificate_) {
469 bool set_cert_success = dtls->SetLocalCertificate(certificate_);
470 RTC_DCHECK(set_cert_success);
471 }
472
473 // Connect to signals offered by the DTLS and ICE transport.
474 dtls->SignalWritableState.connect(
475 this, &JsepTransportController::OnTransportWritableState_n);
476 dtls->SignalReceivingState.connect(
477 this, &JsepTransportController::OnTransportReceivingState_n);
478 dtls->SignalDtlsHandshakeError.connect(
479 this, &JsepTransportController::OnDtlsHandshakeError);
480 dtls->ice_transport()->SignalGatheringState.connect(
481 this, &JsepTransportController::OnTransportGatheringState_n);
482 dtls->ice_transport()->SignalCandidateGathered.connect(
483 this, &JsepTransportController::OnTransportCandidateGathered_n);
484 dtls->ice_transport()->SignalCandidatesRemoved.connect(
485 this, &JsepTransportController::OnTransportCandidatesRemoved_n);
486 dtls->ice_transport()->SignalRoleConflict.connect(
487 this, &JsepTransportController::OnTransportRoleConflict_n);
488 dtls->ice_transport()->SignalStateChanged.connect(
489 this, &JsepTransportController::OnTransportStateChanged_n);
Jonas Olsson7a6739e2019-01-15 16:31:55 +0100490 dtls->ice_transport()->SignalIceTransportStateChanged.connect(
491 this, &JsepTransportController::OnTransportStateChanged_n);
Zhi Huange818b6e2018-02-22 15:26:27 -0800492 return dtls;
493}
494
495std::unique_ptr<webrtc::RtpTransport>
496JsepTransportController::CreateUnencryptedRtpTransport(
497 const std::string& transport_name,
498 rtc::PacketTransportInternal* rtp_packet_transport,
499 rtc::PacketTransportInternal* rtcp_packet_transport) {
500 RTC_DCHECK(network_thread_->IsCurrent());
Zhi Huange830e682018-03-30 10:48:35 -0700501 auto unencrypted_rtp_transport =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200502 absl::make_unique<RtpTransport>(rtcp_packet_transport == nullptr);
Zhi Huange830e682018-03-30 10:48:35 -0700503 unencrypted_rtp_transport->SetRtpPacketTransport(rtp_packet_transport);
504 if (rtcp_packet_transport) {
505 unencrypted_rtp_transport->SetRtcpPacketTransport(rtcp_packet_transport);
506 }
507 return unencrypted_rtp_transport;
Zhi Huange818b6e2018-02-22 15:26:27 -0800508}
509
510std::unique_ptr<webrtc::SrtpTransport>
511JsepTransportController::CreateSdesTransport(
512 const std::string& transport_name,
Zhi Huange830e682018-03-30 10:48:35 -0700513 cricket::DtlsTransportInternal* rtp_dtls_transport,
514 cricket::DtlsTransportInternal* rtcp_dtls_transport) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800515 RTC_DCHECK(network_thread_->IsCurrent());
Zhi Huange818b6e2018-02-22 15:26:27 -0800516 auto srtp_transport =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200517 absl::make_unique<webrtc::SrtpTransport>(rtcp_dtls_transport == nullptr);
Zhi Huange830e682018-03-30 10:48:35 -0700518 RTC_DCHECK(rtp_dtls_transport);
519 srtp_transport->SetRtpPacketTransport(rtp_dtls_transport);
520 if (rtcp_dtls_transport) {
521 srtp_transport->SetRtcpPacketTransport(rtcp_dtls_transport);
Zhi Huange818b6e2018-02-22 15:26:27 -0800522 }
523 if (config_.enable_external_auth) {
524 srtp_transport->EnableExternalAuth();
525 }
526 return srtp_transport;
527}
528
529std::unique_ptr<webrtc::DtlsSrtpTransport>
530JsepTransportController::CreateDtlsSrtpTransport(
531 const std::string& transport_name,
532 cricket::DtlsTransportInternal* rtp_dtls_transport,
533 cricket::DtlsTransportInternal* rtcp_dtls_transport) {
534 RTC_DCHECK(network_thread_->IsCurrent());
Karl Wiberg918f50c2018-07-05 11:40:33 +0200535 auto dtls_srtp_transport = absl::make_unique<webrtc::DtlsSrtpTransport>(
Zhi Huang365381f2018-04-13 16:44:34 -0700536 rtcp_dtls_transport == nullptr);
Zhi Huang27f3bf52018-03-26 21:37:23 -0700537 if (config_.enable_external_auth) {
Zhi Huang365381f2018-04-13 16:44:34 -0700538 dtls_srtp_transport->EnableExternalAuth();
Zhi Huang27f3bf52018-03-26 21:37:23 -0700539 }
Zhi Huang97d5e5b2018-03-27 00:09:01 +0000540
Zhi Huange818b6e2018-02-22 15:26:27 -0800541 dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport,
542 rtcp_dtls_transport);
Zhi Huangb57e1692018-06-12 11:41:11 -0700543 dtls_srtp_transport->SetActiveResetSrtpParams(
544 config_.active_reset_srtp_params);
Jonas Olsson635474e2018-10-18 15:58:17 +0200545 dtls_srtp_transport->SignalDtlsStateChange.connect(
546 this, &JsepTransportController::UpdateAggregateStates_n);
Zhi Huange818b6e2018-02-22 15:26:27 -0800547 return dtls_srtp_transport;
548}
549
550std::vector<cricket::DtlsTransportInternal*>
551JsepTransportController::GetDtlsTransports() {
552 std::vector<cricket::DtlsTransportInternal*> dtls_transports;
Zhi Huange830e682018-03-30 10:48:35 -0700553 for (auto it = jsep_transports_by_name_.begin();
554 it != jsep_transports_by_name_.end(); ++it) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800555 auto jsep_transport = it->second.get();
556 RTC_DCHECK(jsep_transport);
557 if (jsep_transport->rtp_dtls_transport()) {
558 dtls_transports.push_back(jsep_transport->rtp_dtls_transport());
559 }
560
561 if (jsep_transport->rtcp_dtls_transport()) {
562 dtls_transports.push_back(jsep_transport->rtcp_dtls_transport());
563 }
564 }
565 return dtls_transports;
566}
567
Zhi Huange818b6e2018-02-22 15:26:27 -0800568RTCError JsepTransportController::ApplyDescription_n(
569 bool local,
570 SdpType type,
571 const cricket::SessionDescription* description) {
572 RTC_DCHECK(network_thread_->IsCurrent());
573 RTC_DCHECK(description);
574
575 if (local) {
576 local_desc_ = description;
577 } else {
578 remote_desc_ = description;
579 }
580
Zhi Huange830e682018-03-30 10:48:35 -0700581 RTCError error;
Zhi Huangd2248f82018-04-10 14:41:03 -0700582 error = ValidateAndMaybeUpdateBundleGroup(local, type, description);
Zhi Huange830e682018-03-30 10:48:35 -0700583 if (!error.ok()) {
584 return error;
Zhi Huange818b6e2018-02-22 15:26:27 -0800585 }
586
587 std::vector<int> merged_encrypted_extension_ids;
588 if (bundle_group_) {
589 merged_encrypted_extension_ids =
590 MergeEncryptedHeaderExtensionIdsForBundle(description);
591 }
592
593 for (const cricket::ContentInfo& content_info : description->contents()) {
594 // Don't create transports for rejected m-lines and bundled m-lines."
595 if (content_info.rejected ||
596 (IsBundled(content_info.name) && content_info.name != *bundled_mid())) {
597 continue;
598 }
Piotr (Peter) Slatala105ded32019-02-27 14:26:15 -0800599 error = MaybeCreateJsepTransport(local, content_info, *description);
Zhi Huange830e682018-03-30 10:48:35 -0700600 if (!error.ok()) {
601 return error;
602 }
Zhi Huange818b6e2018-02-22 15:26:27 -0800603 }
604
605 RTC_DCHECK(description->contents().size() ==
606 description->transport_infos().size());
607 for (size_t i = 0; i < description->contents().size(); ++i) {
608 const cricket::ContentInfo& content_info = description->contents()[i];
609 const cricket::TransportInfo& transport_info =
610 description->transport_infos()[i];
611 if (content_info.rejected) {
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700612 HandleRejectedContent(content_info, description);
Zhi Huange818b6e2018-02-22 15:26:27 -0800613 continue;
614 }
615
616 if (IsBundled(content_info.name) && content_info.name != *bundled_mid()) {
Zhi Huang365381f2018-04-13 16:44:34 -0700617 if (!HandleBundledContent(content_info)) {
618 return RTCError(RTCErrorType::INVALID_PARAMETER,
619 "Failed to process the bundled m= section.");
620 }
Zhi Huange818b6e2018-02-22 15:26:27 -0800621 continue;
622 }
623
Zhi Huange830e682018-03-30 10:48:35 -0700624 error = ValidateContent(content_info);
625 if (!error.ok()) {
626 return error;
627 }
628
Zhi Huange818b6e2018-02-22 15:26:27 -0800629 std::vector<int> extension_ids;
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700630 if (bundled_mid() && content_info.name == *bundled_mid()) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800631 extension_ids = merged_encrypted_extension_ids;
632 } else {
633 extension_ids = GetEncryptedHeaderExtensionIds(content_info);
634 }
635
Zhi Huange830e682018-03-30 10:48:35 -0700636 int rtp_abs_sendtime_extn_id =
637 GetRtpAbsSendTimeHeaderExtensionId(content_info);
638
Zhi Huang365381f2018-04-13 16:44:34 -0700639 cricket::JsepTransport* transport =
Zhi Huange830e682018-03-30 10:48:35 -0700640 GetJsepTransportForMid(content_info.name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800641 RTC_DCHECK(transport);
642
643 SetIceRole_n(DetermineIceRole(transport, transport_info, type, local));
644
Zhi Huange818b6e2018-02-22 15:26:27 -0800645 cricket::JsepTransportDescription jsep_description =
646 CreateJsepTransportDescription(content_info, transport_info,
Zhi Huange830e682018-03-30 10:48:35 -0700647 extension_ids, rtp_abs_sendtime_extn_id);
Zhi Huange818b6e2018-02-22 15:26:27 -0800648 if (local) {
649 error =
650 transport->SetLocalJsepTransportDescription(jsep_description, type);
651 } else {
652 error =
653 transport->SetRemoteJsepTransportDescription(jsep_description, type);
654 }
655
656 if (!error.ok()) {
657 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
658 "Failed to apply the description for " +
659 content_info.name + ": " + error.message());
660 }
661 }
662 return RTCError::OK();
663}
664
Zhi Huangd2248f82018-04-10 14:41:03 -0700665RTCError JsepTransportController::ValidateAndMaybeUpdateBundleGroup(
666 bool local,
667 SdpType type,
Zhi Huange830e682018-03-30 10:48:35 -0700668 const cricket::SessionDescription* description) {
669 RTC_DCHECK(description);
Zhi Huangd2248f82018-04-10 14:41:03 -0700670 const cricket::ContentGroup* new_bundle_group =
671 description->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
672
673 // The BUNDLE group containing a MID that no m= section has is invalid.
674 if (new_bundle_group) {
Mirko Bonadei739baf02019-01-27 17:29:42 +0100675 for (const auto& content_name : new_bundle_group->content_names()) {
Zhi Huangd2248f82018-04-10 14:41:03 -0700676 if (!description->GetContentByName(content_name)) {
677 return RTCError(RTCErrorType::INVALID_PARAMETER,
678 "The BUNDLE group contains MID:" + content_name +
679 " matching no m= section.");
680 }
681 }
682 }
683
684 if (type == SdpType::kAnswer) {
685 const cricket::ContentGroup* offered_bundle_group =
686 local ? remote_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE)
687 : local_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
688
689 if (new_bundle_group) {
690 // The BUNDLE group in answer should be a subset of offered group.
Mirko Bonadei739baf02019-01-27 17:29:42 +0100691 for (const auto& content_name : new_bundle_group->content_names()) {
Zhi Huangd2248f82018-04-10 14:41:03 -0700692 if (!offered_bundle_group ||
693 !offered_bundle_group->HasContentName(content_name)) {
694 return RTCError(RTCErrorType::INVALID_PARAMETER,
695 "The BUNDLE group in answer contains a MID that was "
696 "not in the offered group.");
697 }
698 }
699 }
700
701 if (bundle_group_) {
Mirko Bonadei739baf02019-01-27 17:29:42 +0100702 for (const auto& content_name : bundle_group_->content_names()) {
Zhi Huangd2248f82018-04-10 14:41:03 -0700703 // An answer that removes m= sections from pre-negotiated BUNDLE group
704 // without rejecting it, is invalid.
705 if (!new_bundle_group ||
706 !new_bundle_group->HasContentName(content_name)) {
707 auto* content_info = description->GetContentByName(content_name);
708 if (!content_info || !content_info->rejected) {
709 return RTCError(RTCErrorType::INVALID_PARAMETER,
710 "Answer cannot remove m= section " + 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)) {
Zhi Huangd2248f82018-04-10 14:41:03 -0700726 bundle_group_ = *new_bundle_group;
727 }
Zhi Huange830e682018-03-30 10:48:35 -0700728
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) {
Mirko Bonadei739baf02019-01-27 17:29:42 +0100743 for (const auto& content_name : bundle_group_->content_names()) {
Zhi Huange830e682018-03-30 10:48:35 -0700744 auto other_content = description->GetContentByName(content_name);
745 if (!other_content->rejected) {
746 return RTCError(
747 RTCErrorType::INVALID_PARAMETER,
748 "The m= section:" + content_name + " should be rejected.");
749 }
750 }
751 }
752
753 return RTCError::OK();
754}
755
756RTCError 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:" + content_info.name +
764 " is invalid. RTCP-MUX is not "
765 "enabled when it is required.");
766 }
767 return RTCError::OK();
768}
769
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700770void JsepTransportController::HandleRejectedContent(
Zhi Huangd2248f82018-04-10 14:41:03 -0700771 const cricket::ContentInfo& content_info,
772 const cricket::SessionDescription* description) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800773 // If the content is rejected, let the
774 // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first,
Zhi Huang365381f2018-04-13 16:44:34 -0700775 // then destroy the cricket::JsepTransport.
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700776 RemoveTransportForMid(content_info.name);
Zhi Huange830e682018-03-30 10:48:35 -0700777 if (content_info.name == bundled_mid()) {
Mirko Bonadei739baf02019-01-27 17:29:42 +0100778 for (const auto& content_name : bundle_group_->content_names()) {
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700779 RemoveTransportForMid(content_name);
Zhi Huange830e682018-03-30 10:48:35 -0700780 }
781 bundle_group_.reset();
782 } else if (IsBundled(content_info.name)) {
783 // Remove the rejected content from the |bundle_group_|.
Zhi Huange818b6e2018-02-22 15:26:27 -0800784 bundle_group_->RemoveContentName(content_info.name);
Zhi Huange830e682018-03-30 10:48:35 -0700785 // Reset the bundle group if nothing left.
786 if (!bundle_group_->FirstContentName()) {
787 bundle_group_.reset();
788 }
Zhi Huange818b6e2018-02-22 15:26:27 -0800789 }
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700790 MaybeDestroyJsepTransport(content_info.name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800791}
792
Zhi Huang365381f2018-04-13 16:44:34 -0700793bool JsepTransportController::HandleBundledContent(
Zhi Huange818b6e2018-02-22 15:26:27 -0800794 const cricket::ContentInfo& content_info) {
Zhi Huangd2248f82018-04-10 14:41:03 -0700795 auto jsep_transport = GetJsepTransportByName(*bundled_mid());
796 RTC_DCHECK(jsep_transport);
Zhi Huange818b6e2018-02-22 15:26:27 -0800797 // If the content is bundled, let the
798 // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first,
Zhi Huang365381f2018-04-13 16:44:34 -0700799 // then destroy the cricket::JsepTransport.
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700800 if (SetTransportForMid(content_info.name, jsep_transport)) {
Piotr (Peter) Slatala10aeb2a2018-11-14 10:57:24 -0800801 // 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.
Zhi Huang365381f2018-04-13 16:44:34 -0700805 MaybeDestroyJsepTransport(content_info.name);
806 return true;
807 }
808 return false;
Zhi Huange818b6e2018-02-22 15:26:27 -0800809}
810
Zhi Huang365381f2018-04-13 16:44:34 -0700811bool JsepTransportController::SetTransportForMid(
Zhi Huangd2248f82018-04-10 14:41:03 -0700812 const std::string& mid,
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700813 cricket::JsepTransport* jsep_transport) {
Zhi Huang365381f2018-04-13 16:44:34 -0700814 RTC_DCHECK(jsep_transport);
Zhi Huangd2248f82018-04-10 14:41:03 -0700815 if (mid_to_transport_[mid] == jsep_transport) {
Zhi Huang365381f2018-04-13 16:44:34 -0700816 return true;
Zhi Huangd2248f82018-04-10 14:41:03 -0700817 }
818
819 mid_to_transport_[mid] = jsep_transport;
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700820 return config_.transport_observer->OnTransportChanged(
Harald Alvestrandc85328f2019-02-28 07:51:00 +0100821 mid, jsep_transport->rtp_transport(), jsep_transport->RtpDtlsTransport(),
822 jsep_transport->media_transport());
Zhi Huangd2248f82018-04-10 14:41:03 -0700823}
824
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700825void JsepTransportController::RemoveTransportForMid(const std::string& mid) {
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -0800826 bool ret = config_.transport_observer->OnTransportChanged(mid, nullptr,
827 nullptr, nullptr);
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700828 // Calling OnTransportChanged with nullptr should always succeed, since it is
829 // only expected to fail when adding media to a transport (not removing).
830 RTC_DCHECK(ret);
Zhi Huangd2248f82018-04-10 14:41:03 -0700831 mid_to_transport_.erase(mid);
832}
833
Zhi Huange818b6e2018-02-22 15:26:27 -0800834cricket::JsepTransportDescription
835JsepTransportController::CreateJsepTransportDescription(
836 cricket::ContentInfo content_info,
837 cricket::TransportInfo transport_info,
Zhi Huange830e682018-03-30 10:48:35 -0700838 const std::vector<int>& encrypted_extension_ids,
839 int rtp_abs_sendtime_extn_id) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800840 const cricket::MediaContentDescription* content_desc =
841 static_cast<const cricket::MediaContentDescription*>(
842 content_info.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,
Zhi Huange830e682018-03-30 10:48:35 -0700850 rtp_abs_sendtime_extn_id, transport_info.description);
Zhi Huange818b6e2018-02-22 15:26:27 -0800851}
852
853bool 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
873std::vector<int> JsepTransportController::GetEncryptedHeaderExtensionIds(
874 const cricket::ContentInfo& content_info) {
875 const cricket::MediaContentDescription* content_desc =
876 static_cast<const cricket::MediaContentDescription*>(
877 content_info.description);
878
Benjamin Wrighta54daf12018-10-11 15:33:17 -0700879 if (!config_.crypto_options.srtp.enable_encrypted_rtp_header_extensions) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800880 return std::vector<int>();
881 }
882
883 std::vector<int> encrypted_header_extension_ids;
Mirko Bonadei739baf02019-01-27 17:29:42 +0100884 for (const auto& extension : content_desc->rtp_header_extensions()) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800885 if (!extension.encrypt) {
886 continue;
887 }
Steve Anton64b626b2019-01-28 17:25:26 -0800888 if (!absl::c_linear_search(encrypted_header_extension_ids, extension.id)) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800889 encrypted_header_extension_ids.push_back(extension.id);
890 }
891 }
892 return encrypted_header_extension_ids;
893}
894
895std::vector<int>
896JsepTransportController::MergeEncryptedHeaderExtensionIdsForBundle(
897 const cricket::SessionDescription* description) {
898 RTC_DCHECK(description);
899 RTC_DCHECK(bundle_group_);
900
901 std::vector<int> merged_ids;
902 // Union the encrypted header IDs in the group when bundle is enabled.
903 for (const cricket::ContentInfo& content_info : description->contents()) {
904 if (bundle_group_->HasContentName(content_info.name)) {
905 std::vector<int> extension_ids =
906 GetEncryptedHeaderExtensionIds(content_info);
907 for (int id : extension_ids) {
Steve Anton64b626b2019-01-28 17:25:26 -0800908 if (!absl::c_linear_search(merged_ids, id)) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800909 merged_ids.push_back(id);
910 }
911 }
912 }
913 }
914 return merged_ids;
915}
916
Zhi Huange830e682018-03-30 10:48:35 -0700917int JsepTransportController::GetRtpAbsSendTimeHeaderExtensionId(
Zhi Huange818b6e2018-02-22 15:26:27 -0800918 const cricket::ContentInfo& content_info) {
Zhi Huange830e682018-03-30 10:48:35 -0700919 if (!config_.enable_external_auth) {
920 return -1;
Zhi Huange818b6e2018-02-22 15:26:27 -0800921 }
922
923 const cricket::MediaContentDescription* content_desc =
924 static_cast<const cricket::MediaContentDescription*>(
925 content_info.description);
Zhi Huange830e682018-03-30 10:48:35 -0700926
927 const webrtc::RtpExtension* send_time_extension =
928 webrtc::RtpExtension::FindHeaderExtensionByUri(
929 content_desc->rtp_header_extensions(),
930 webrtc::RtpExtension::kAbsSendTimeUri);
931 return send_time_extension ? send_time_extension->id : -1;
932}
933
Zhi Huang365381f2018-04-13 16:44:34 -0700934const cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid(
Zhi Huange830e682018-03-30 10:48:35 -0700935 const std::string& mid) const {
Zhi Huangd2248f82018-04-10 14:41:03 -0700936 auto it = mid_to_transport_.find(mid);
937 return it == mid_to_transport_.end() ? nullptr : it->second;
Zhi Huange830e682018-03-30 10:48:35 -0700938}
939
Zhi Huang365381f2018-04-13 16:44:34 -0700940cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid(
Zhi Huange830e682018-03-30 10:48:35 -0700941 const std::string& mid) {
Zhi Huangd2248f82018-04-10 14:41:03 -0700942 auto it = mid_to_transport_.find(mid);
943 return it == mid_to_transport_.end() ? nullptr : it->second;
Zhi Huange830e682018-03-30 10:48:35 -0700944}
945
Zhi Huang365381f2018-04-13 16:44:34 -0700946const cricket::JsepTransport* JsepTransportController::GetJsepTransportByName(
Zhi Huange830e682018-03-30 10:48:35 -0700947 const std::string& transport_name) const {
948 auto it = jsep_transports_by_name_.find(transport_name);
949 return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
950}
951
Zhi Huang365381f2018-04-13 16:44:34 -0700952cricket::JsepTransport* JsepTransportController::GetJsepTransportByName(
Zhi Huange830e682018-03-30 10:48:35 -0700953 const std::string& transport_name) {
954 auto it = jsep_transports_by_name_.find(transport_name);
955 return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
956}
957
Piotr (Peter) Slatala47dfdca2018-11-16 14:13:58 -0800958std::unique_ptr<webrtc::MediaTransportInterface>
959JsepTransportController::MaybeCreateMediaTransport(
960 const cricket::ContentInfo& content_info,
Piotr (Peter) Slatala105ded32019-02-27 14:26:15 -0800961 const cricket::SessionDescription& description,
Anton Sukhanov7940da02018-10-10 10:34:49 -0700962 bool local,
Piotr (Peter) Slatala47dfdca2018-11-16 14:13:58 -0800963 cricket::IceTransportInternal* ice_transport) {
Piotr (Peter) Slatala63a176b2019-01-25 08:25:33 -0800964 if (!is_media_transport_factory_enabled_) {
965 return nullptr;
966 }
967 if (config_.media_transport_factory == nullptr) {
968 return nullptr;
969 }
970
Piotr (Peter) Slatala55b91b92019-01-25 13:31:15 -0800971 if (!config_.use_media_transport_for_media &&
972 !config_.use_media_transport_for_data_channels) {
973 return nullptr;
974 }
975
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700976 absl::optional<cricket::CryptoParams> selected_crypto_for_media_transport;
977 if (content_info.media_description() &&
978 !content_info.media_description()->cryptos().empty()) {
979 // Order of cryptos is deterministic (rfc4568, 5.1.1), so we just select the
980 // first one (in fact the first one should be the most preferred one.) We
981 // ignore the HMAC size, as media transport crypto settings currently don't
982 // expose HMAC size, nor crypto protocol for that matter.
983 selected_crypto_for_media_transport =
984 content_info.media_description()->cryptos()[0];
985 }
986
Piotr (Peter) Slatala63a176b2019-01-25 08:25:33 -0800987 if (!selected_crypto_for_media_transport.has_value()) {
988 RTC_LOG(LS_WARNING) << "a=cryto line was not found in the offer. Most "
989 "likely you did not enable SDES. "
990 "Make sure to pass config.enable_dtls_srtp=false "
991 "to RTCConfiguration. "
992 "Cannot continue with media transport. Falling "
993 "back to RTP. is_local="
994 << local;
Anton Sukhanov7940da02018-10-10 10:34:49 -0700995
Piotr (Peter) Slatala63a176b2019-01-25 08:25:33 -0800996 // Remove media_transport_factory from config, because we don't want to
997 // use it on the subsequent call (for the other side of the offer).
998 is_media_transport_factory_enabled_ = false;
999 return nullptr;
Anton Sukhanov7940da02018-10-10 10:34:49 -07001000 }
1001
Piotr (Peter) Slatala105ded32019-02-27 14:26:15 -08001002 // TODO(psla): This code will be removed in favor of media transport settings.
Piotr (Peter) Slatala63a176b2019-01-25 08:25:33 -08001003 // Note that we ignore here lifetime and length.
1004 // In fact we take those bits (inline, lifetime and length) and keep it as
1005 // part of key derivation.
1006 //
1007 // Technically, we are also not following rfc4568, which requires us to
1008 // send and answer with the key that we chose. In practice, for media
1009 // transport, the current approach should be sufficient (we take the key
1010 // that sender offered, and caller assumes we will use it. We are not
1011 // signaling back that we indeed used it.)
1012 std::unique_ptr<rtc::KeyDerivation> key_derivation =
1013 rtc::KeyDerivation::Create(rtc::KeyDerivationAlgorithm::HKDF_SHA256);
1014 const std::string label = "MediaTransportLabel";
1015 constexpr int kDerivedKeyByteSize = 32;
1016
1017 int key_len, salt_len;
1018 if (!rtc::GetSrtpKeyAndSaltLengths(
1019 rtc::SrtpCryptoSuiteFromName(
1020 selected_crypto_for_media_transport.value().cipher_suite),
1021 &key_len, &salt_len)) {
1022 RTC_CHECK(false) << "Cannot set up secure media transport";
1023 }
1024 rtc::ZeroOnFreeBuffer<uint8_t> raw_key(key_len + salt_len);
1025
1026 cricket::SrtpFilter::ParseKeyParams(
1027 selected_crypto_for_media_transport.value().key_params, raw_key.data(),
1028 raw_key.size());
1029 absl::optional<rtc::ZeroOnFreeBuffer<uint8_t>> key =
1030 key_derivation->DeriveKey(
1031 raw_key,
1032 /*salt=*/nullptr,
1033 rtc::ArrayView<const uint8_t>(
1034 reinterpret_cast<const uint8_t*>(label.data()), label.size()),
1035 kDerivedKeyByteSize);
Piotr (Peter) Slatala105ded32019-02-27 14:26:15 -08001036 // TODO(psla): End of the code to be removed.
Piotr (Peter) Slatala63a176b2019-01-25 08:25:33 -08001037
1038 // We want to crash the app if we don't have a key, and not silently fall
1039 // back to the unsecure communication.
1040 RTC_CHECK(key.has_value());
1041 MediaTransportSettings settings;
1042 settings.is_caller = local;
1043 settings.pre_shared_key = std::string(
1044 reinterpret_cast<const char*>(key.value().data()), key.value().size());
Piotr (Peter) Slatala01fe3092019-02-15 12:05:50 -08001045 if (config_.use_media_transport_for_media) {
1046 settings.event_log = config_.event_log;
1047 }
Piotr (Peter) Slatala105ded32019-02-27 14:26:15 -08001048
1049 // Assume there is only one media transport (or if more, use the first one).
1050 if (!local && !description.MediaTransportSettings().empty() &&
1051 config_.media_transport_factory->GetTransportName() ==
1052 description.MediaTransportSettings()[0].transport_name) {
1053 settings.remote_transport_parameters =
1054 description.MediaTransportSettings()[0].transport_setting;
1055 }
1056
Piotr (Peter) Slatala63a176b2019-01-25 08:25:33 -08001057 auto media_transport_result =
1058 config_.media_transport_factory->CreateMediaTransport(
1059 ice_transport, network_thread_, settings);
1060
1061 // TODO(sukhanov): Proper error handling.
1062 RTC_CHECK(media_transport_result.ok());
1063
1064 return media_transport_result.MoveValue();
Piotr (Peter) Slatala47dfdca2018-11-16 14:13:58 -08001065}
1066
1067RTCError JsepTransportController::MaybeCreateJsepTransport(
1068 bool local,
Piotr (Peter) Slatala105ded32019-02-27 14:26:15 -08001069 const cricket::ContentInfo& content_info,
1070 const cricket::SessionDescription& description) {
Piotr (Peter) Slatala47dfdca2018-11-16 14:13:58 -08001071 RTC_DCHECK(network_thread_->IsCurrent());
1072 cricket::JsepTransport* transport = GetJsepTransportByName(content_info.name);
1073 if (transport) {
1074 return RTCError::OK();
1075 }
1076
1077 const cricket::MediaContentDescription* content_desc =
1078 static_cast<const cricket::MediaContentDescription*>(
1079 content_info.description);
1080 if (certificate_ && !content_desc->cryptos().empty()) {
1081 return RTCError(RTCErrorType::INVALID_PARAMETER,
1082 "SDES and DTLS-SRTP cannot be enabled at the same time.");
1083 }
1084
Piotr (Peter) Slatala2b5baee2019-01-16 08:25:21 -08001085 std::unique_ptr<cricket::IceTransportInternal> ice =
1086 CreateIceTransport(content_info.name, /*rtcp=*/false);
1087
1088 std::unique_ptr<MediaTransportInterface> media_transport =
Piotr (Peter) Slatala105ded32019-02-27 14:26:15 -08001089 MaybeCreateMediaTransport(content_info, description, local, ice.get());
Piotr (Peter) Slatala2b5baee2019-01-16 08:25:21 -08001090
Piotr (Peter) Slatala47dfdca2018-11-16 14:13:58 -08001091 std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
Piotr (Peter) Slatala2b5baee2019-01-16 08:25:21 -08001092 CreateDtlsTransport(std::move(ice));
Piotr (Peter) Slatala47dfdca2018-11-16 14:13:58 -08001093
1094 std::unique_ptr<cricket::DtlsTransportInternal> rtcp_dtls_transport;
1095 std::unique_ptr<RtpTransport> unencrypted_rtp_transport;
1096 std::unique_ptr<SrtpTransport> sdes_transport;
1097 std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport;
Piotr (Peter) Slatala47dfdca2018-11-16 14:13:58 -08001098
1099 if (config_.rtcp_mux_policy !=
1100 PeerConnectionInterface::kRtcpMuxPolicyRequire &&
1101 content_info.type == cricket::MediaProtocolType::kRtp) {
Piotr (Peter) Slatala2b5baee2019-01-16 08:25:21 -08001102 RTC_DCHECK(media_transport == nullptr);
1103 rtcp_dtls_transport = CreateDtlsTransport(
1104 CreateIceTransport(content_info.name, /*rtcp=*/true));
Piotr (Peter) Slatala47dfdca2018-11-16 14:13:58 -08001105 }
Piotr (Peter) Slatala47dfdca2018-11-16 14:13:58 -08001106
Anton Sukhanov7940da02018-10-10 10:34:49 -07001107 // TODO(sukhanov): Do not create RTP/RTCP transports if media transport is
Piotr (Peter) Slatala2b5baee2019-01-16 08:25:21 -08001108 // used, and remove the no-op dtls transport when that's done.
Zhi Huange818b6e2018-02-22 15:26:27 -08001109 if (config_.disable_encryption) {
1110 unencrypted_rtp_transport = CreateUnencryptedRtpTransport(
Zhi Huangd2248f82018-04-10 14:41:03 -07001111 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 15:26:27 -08001112 } else if (!content_desc->cryptos().empty()) {
Zhi Huangd2248f82018-04-10 14:41:03 -07001113 sdes_transport = CreateSdesTransport(
1114 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 15:26:27 -08001115 } else {
Zhi Huangd2248f82018-04-10 14:41:03 -07001116 dtls_srtp_transport = CreateDtlsSrtpTransport(
1117 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 15:26:27 -08001118 }
1119
Zhi Huang365381f2018-04-13 16:44:34 -07001120 std::unique_ptr<cricket::JsepTransport> jsep_transport =
Karl Wiberg918f50c2018-07-05 11:40:33 +02001121 absl::make_unique<cricket::JsepTransport>(
Zhi Huangd2248f82018-04-10 14:41:03 -07001122 content_info.name, certificate_, std::move(unencrypted_rtp_transport),
Zhi Huange818b6e2018-02-22 15:26:27 -08001123 std::move(sdes_transport), std::move(dtls_srtp_transport),
Anton Sukhanov7940da02018-10-10 10:34:49 -07001124 std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport),
1125 std::move(media_transport));
Zhi Huange818b6e2018-02-22 15:26:27 -08001126 jsep_transport->SignalRtcpMuxActive.connect(
1127 this, &JsepTransportController::UpdateAggregateStates_n);
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -07001128 jsep_transport->SignalMediaTransportStateChanged.connect(
Bjorn Mellem175aa2e2018-11-08 11:23:22 -08001129 this, &JsepTransportController::OnMediaTransportStateChanged_n);
Taylor Brandstettercbaa2542018-04-16 16:42:14 -07001130 SetTransportForMid(content_info.name, jsep_transport.get());
Zhi Huange830e682018-03-30 10:48:35 -07001131
Zhi Huangd2248f82018-04-10 14:41:03 -07001132 jsep_transports_by_name_[content_info.name] = std::move(jsep_transport);
1133 UpdateAggregateStates_n();
Zhi Huange830e682018-03-30 10:48:35 -07001134 return RTCError::OK();
Zhi Huange818b6e2018-02-22 15:26:27 -08001135}
1136
1137void JsepTransportController::MaybeDestroyJsepTransport(
1138 const std::string& mid) {
Zhi Huangd2248f82018-04-10 14:41:03 -07001139 auto jsep_transport = GetJsepTransportByName(mid);
1140 if (!jsep_transport) {
1141 return;
1142 }
1143
1144 // Don't destroy the JsepTransport if there are still media sections referring
1145 // to it.
1146 for (const auto& kv : mid_to_transport_) {
1147 if (kv.second == jsep_transport) {
1148 return;
1149 }
1150 }
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -08001151
Zhi Huange830e682018-03-30 10:48:35 -07001152 jsep_transports_by_name_.erase(mid);
Zhi Huange818b6e2018-02-22 15:26:27 -08001153 UpdateAggregateStates_n();
1154}
1155
1156void JsepTransportController::DestroyAllJsepTransports_n() {
1157 RTC_DCHECK(network_thread_->IsCurrent());
Piotr (Peter) Slatalacc8e8bb2018-11-15 08:26:19 -08001158
1159 for (const auto& jsep_transport : jsep_transports_by_name_) {
1160 config_.transport_observer->OnTransportChanged(jsep_transport.first,
1161 nullptr, nullptr, nullptr);
1162 }
1163
Zhi Huange830e682018-03-30 10:48:35 -07001164 jsep_transports_by_name_.clear();
Zhi Huange818b6e2018-02-22 15:26:27 -08001165}
1166
1167void JsepTransportController::SetIceRole_n(cricket::IceRole ice_role) {
1168 RTC_DCHECK(network_thread_->IsCurrent());
1169
1170 ice_role_ = ice_role;
1171 for (auto& dtls : GetDtlsTransports()) {
1172 dtls->ice_transport()->SetIceRole(ice_role_);
1173 }
1174}
1175
1176cricket::IceRole JsepTransportController::DetermineIceRole(
Zhi Huang365381f2018-04-13 16:44:34 -07001177 cricket::JsepTransport* jsep_transport,
Zhi Huange818b6e2018-02-22 15:26:27 -08001178 const cricket::TransportInfo& transport_info,
1179 SdpType type,
1180 bool local) {
1181 cricket::IceRole ice_role = ice_role_;
1182 auto tdesc = transport_info.description;
1183 if (local) {
1184 // The initial offer side may use ICE Lite, in which case, per RFC5245
1185 // Section 5.1.1, the answer side should take the controlling role if it is
1186 // in the full ICE mode.
1187 //
1188 // When both sides use ICE Lite, the initial offer side must take the
1189 // controlling role, and this is the default logic implemented in
1190 // SetLocalDescription in JsepTransportController.
1191 if (jsep_transport->remote_description() &&
1192 jsep_transport->remote_description()->transport_desc.ice_mode ==
1193 cricket::ICEMODE_LITE &&
1194 ice_role_ == cricket::ICEROLE_CONTROLLED &&
1195 tdesc.ice_mode == cricket::ICEMODE_FULL) {
1196 ice_role = cricket::ICEROLE_CONTROLLING;
1197 }
1198
1199 // Older versions of Chrome expect the ICE role to be re-determined when an
1200 // ICE restart occurs, and also don't perform conflict resolution correctly,
1201 // so for now we can't safely stop doing this, unless the application opts
1202 // in by setting |config_.redetermine_role_on_ice_restart_| to false. See:
1203 // https://bugs.chromium.org/p/chromium/issues/detail?id=628676
1204 // TODO(deadbeef): Remove this when these old versions of Chrome reach a low
1205 // enough population.
1206 if (config_.redetermine_role_on_ice_restart &&
1207 jsep_transport->local_description() &&
1208 cricket::IceCredentialsChanged(
1209 jsep_transport->local_description()->transport_desc.ice_ufrag,
1210 jsep_transport->local_description()->transport_desc.ice_pwd,
1211 tdesc.ice_ufrag, tdesc.ice_pwd) &&
1212 // Don't change the ICE role if the remote endpoint is ICE lite; we
1213 // should always be controlling in that case.
1214 (!jsep_transport->remote_description() ||
1215 jsep_transport->remote_description()->transport_desc.ice_mode !=
1216 cricket::ICEMODE_LITE)) {
1217 ice_role = (type == SdpType::kOffer) ? cricket::ICEROLE_CONTROLLING
1218 : cricket::ICEROLE_CONTROLLED;
1219 }
1220 } else {
1221 // If our role is cricket::ICEROLE_CONTROLLED and the remote endpoint
1222 // supports only ice_lite, this local endpoint should take the CONTROLLING
1223 // role.
1224 // TODO(deadbeef): This is a session-level attribute, so it really shouldn't
1225 // be in a TransportDescription in the first place...
1226 if (ice_role_ == cricket::ICEROLE_CONTROLLED &&
1227 tdesc.ice_mode == cricket::ICEMODE_LITE) {
1228 ice_role = cricket::ICEROLE_CONTROLLING;
1229 }
1230
1231 // If we use ICE Lite and the remote endpoint uses the full implementation
1232 // of ICE, the local endpoint must take the controlled role, and the other
1233 // side must be the controlling role.
1234 if (jsep_transport->local_description() &&
1235 jsep_transport->local_description()->transport_desc.ice_mode ==
1236 cricket::ICEMODE_LITE &&
1237 ice_role_ == cricket::ICEROLE_CONTROLLING &&
Zhi Huange830e682018-03-30 10:48:35 -07001238 tdesc.ice_mode == cricket::ICEMODE_FULL) {
Zhi Huange818b6e2018-02-22 15:26:27 -08001239 ice_role = cricket::ICEROLE_CONTROLLED;
1240 }
1241 }
1242
1243 return ice_role;
1244}
1245
1246void JsepTransportController::OnTransportWritableState_n(
1247 rtc::PacketTransportInternal* transport) {
1248 RTC_DCHECK(network_thread_->IsCurrent());
1249 RTC_LOG(LS_INFO) << " Transport " << transport->transport_name()
1250 << " writability changed to " << transport->writable()
1251 << ".";
1252 UpdateAggregateStates_n();
1253}
1254
1255void JsepTransportController::OnTransportReceivingState_n(
1256 rtc::PacketTransportInternal* transport) {
1257 RTC_DCHECK(network_thread_->IsCurrent());
1258 UpdateAggregateStates_n();
1259}
1260
1261void JsepTransportController::OnTransportGatheringState_n(
1262 cricket::IceTransportInternal* transport) {
1263 RTC_DCHECK(network_thread_->IsCurrent());
1264 UpdateAggregateStates_n();
1265}
1266
1267void JsepTransportController::OnTransportCandidateGathered_n(
1268 cricket::IceTransportInternal* transport,
1269 const cricket::Candidate& candidate) {
1270 RTC_DCHECK(network_thread_->IsCurrent());
1271
1272 // We should never signal peer-reflexive candidates.
1273 if (candidate.type() == cricket::PRFLX_PORT_TYPE) {
1274 RTC_NOTREACHED();
1275 return;
1276 }
Steve Antond25828a2018-08-31 13:06:05 -07001277 std::string transport_name = transport->transport_name();
1278 invoker_.AsyncInvoke<void>(
1279 RTC_FROM_HERE, signaling_thread_, [this, transport_name, candidate] {
1280 SignalIceCandidatesGathered(transport_name, {candidate});
1281 });
Zhi Huange818b6e2018-02-22 15:26:27 -08001282}
1283
1284void JsepTransportController::OnTransportCandidatesRemoved_n(
1285 cricket::IceTransportInternal* transport,
1286 const cricket::Candidates& candidates) {
1287 invoker_.AsyncInvoke<void>(
1288 RTC_FROM_HERE, signaling_thread_,
Steve Antond25828a2018-08-31 13:06:05 -07001289 [this, candidates] { SignalIceCandidatesRemoved(candidates); });
Zhi Huange818b6e2018-02-22 15:26:27 -08001290}
1291
1292void JsepTransportController::OnTransportRoleConflict_n(
1293 cricket::IceTransportInternal* transport) {
1294 RTC_DCHECK(network_thread_->IsCurrent());
1295 // Note: since the role conflict is handled entirely on the network thread,
1296 // we don't need to worry about role conflicts occurring on two ports at
1297 // once. The first one encountered should immediately reverse the role.
1298 cricket::IceRole reversed_role = (ice_role_ == cricket::ICEROLE_CONTROLLING)
1299 ? cricket::ICEROLE_CONTROLLED
1300 : cricket::ICEROLE_CONTROLLING;
1301 RTC_LOG(LS_INFO) << "Got role conflict; switching to "
1302 << (reversed_role == cricket::ICEROLE_CONTROLLING
1303 ? "controlling"
1304 : "controlled")
1305 << " role.";
1306 SetIceRole_n(reversed_role);
1307}
1308
1309void JsepTransportController::OnTransportStateChanged_n(
1310 cricket::IceTransportInternal* transport) {
1311 RTC_DCHECK(network_thread_->IsCurrent());
1312 RTC_LOG(LS_INFO) << transport->transport_name() << " Transport "
1313 << transport->component()
1314 << " state changed. Check if state is complete.";
1315 UpdateAggregateStates_n();
1316}
1317
Bjorn Mellem175aa2e2018-11-08 11:23:22 -08001318void JsepTransportController::OnMediaTransportStateChanged_n() {
1319 SignalMediaTransportStateChanged();
1320 UpdateAggregateStates_n();
1321}
1322
Zhi Huange818b6e2018-02-22 15:26:27 -08001323void JsepTransportController::UpdateAggregateStates_n() {
1324 RTC_DCHECK(network_thread_->IsCurrent());
1325
1326 auto dtls_transports = GetDtlsTransports();
Alex Loiko9289eda2018-11-23 16:18:59 +00001327 cricket::IceConnectionState new_connection_state =
1328 cricket::kIceConnectionConnecting;
Jonas Olsson635474e2018-10-18 15:58:17 +02001329 PeerConnectionInterface::IceConnectionState new_ice_connection_state =
1330 PeerConnectionInterface::IceConnectionState::kIceConnectionNew;
1331 PeerConnectionInterface::PeerConnectionState new_combined_state =
1332 PeerConnectionInterface::PeerConnectionState::kNew;
Zhi Huange818b6e2018-02-22 15:26:27 -08001333 cricket::IceGatheringState new_gathering_state = cricket::kIceGatheringNew;
Alex Loiko9289eda2018-11-23 16:18:59 +00001334 bool any_failed = false;
1335
1336 // TODO(http://bugs.webrtc.org/9719) If(when) media_transport disables
1337 // dtls_transports entirely, the below line will have to be changed to account
1338 // for the fact that dtls transports might be absent.
1339 bool all_connected = !dtls_transports.empty();
1340 bool all_completed = !dtls_transports.empty();
Zhi Huange818b6e2018-02-22 15:26:27 -08001341 bool any_gathering = false;
1342 bool all_done_gathering = !dtls_transports.empty();
Jonas Olsson635474e2018-10-18 15:58:17 +02001343
1344 std::map<IceTransportState, int> ice_state_counts;
1345 std::map<cricket::DtlsTransportState, int> dtls_state_counts;
1346
Zhi Huange818b6e2018-02-22 15:26:27 -08001347 for (const auto& dtls : dtls_transports) {
Alex Loiko9289eda2018-11-23 16:18:59 +00001348 any_failed = any_failed || dtls->ice_transport()->GetState() ==
1349 cricket::IceTransportState::STATE_FAILED;
1350 all_connected = all_connected && dtls->writable();
1351 all_completed =
1352 all_completed && dtls->writable() &&
1353 dtls->ice_transport()->GetState() ==
1354 cricket::IceTransportState::STATE_COMPLETED &&
1355 dtls->ice_transport()->GetIceRole() == cricket::ICEROLE_CONTROLLING &&
1356 dtls->ice_transport()->gathering_state() ==
1357 cricket::kIceGatheringComplete;
Zhi Huange818b6e2018-02-22 15:26:27 -08001358 any_gathering = any_gathering || dtls->ice_transport()->gathering_state() !=
1359 cricket::kIceGatheringNew;
1360 all_done_gathering =
1361 all_done_gathering && dtls->ice_transport()->gathering_state() ==
1362 cricket::kIceGatheringComplete;
Jonas Olsson635474e2018-10-18 15:58:17 +02001363
1364 dtls_state_counts[dtls->dtls_state()]++;
1365 ice_state_counts[dtls->ice_transport()->GetIceTransportState()]++;
Zhi Huange818b6e2018-02-22 15:26:27 -08001366 }
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -07001367
Alex Loiko9289eda2018-11-23 16:18:59 +00001368 for (auto it = jsep_transports_by_name_.begin();
1369 it != jsep_transports_by_name_.end(); ++it) {
1370 auto jsep_transport = it->second.get();
1371 if (!jsep_transport->media_transport()) {
1372 continue;
1373 }
1374
1375 // There is no 'kIceConnectionDisconnected', so we only need to handle
1376 // connected and completed.
1377 // We treat kClosed as failed, because if it happens before shutting down
1378 // media transports it means that there was a failure.
1379 // MediaTransportInterface allows to flip back and forth between kWritable
1380 // and kPending, but there does not exist an implementation that does that,
1381 // and the contract of jsep transport controller doesn't quite expect that.
1382 // When this happens, we would go from connected to connecting state, but
1383 // this may change in future.
1384 any_failed |= jsep_transport->media_transport_state() ==
1385 webrtc::MediaTransportState::kClosed;
1386 all_completed &= jsep_transport->media_transport_state() ==
1387 webrtc::MediaTransportState::kWritable;
1388 all_connected &= jsep_transport->media_transport_state() ==
1389 webrtc::MediaTransportState::kWritable;
1390 }
1391
1392 if (any_failed) {
1393 new_connection_state = cricket::kIceConnectionFailed;
1394 } else if (all_completed) {
1395 new_connection_state = cricket::kIceConnectionCompleted;
1396 } else if (all_connected) {
1397 new_connection_state = cricket::kIceConnectionConnected;
1398 }
1399 if (ice_connection_state_ != new_connection_state) {
1400 ice_connection_state_ = new_connection_state;
1401 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1402 [this, new_connection_state] {
1403 SignalIceConnectionState(new_connection_state);
1404 });
1405 }
1406
Jonas Olsson635474e2018-10-18 15:58:17 +02001407 // Compute the current RTCIceConnectionState as described in
1408 // https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate.
1409 // The PeerConnection is responsible for handling the "closed" state.
1410 int total_ice_checking = ice_state_counts[IceTransportState::kChecking];
1411 int total_ice_connected = ice_state_counts[IceTransportState::kConnected];
1412 int total_ice_completed = ice_state_counts[IceTransportState::kCompleted];
1413 int total_ice_failed = ice_state_counts[IceTransportState::kFailed];
1414 int total_ice_disconnected =
1415 ice_state_counts[IceTransportState::kDisconnected];
1416 int total_ice_closed = ice_state_counts[IceTransportState::kClosed];
1417 int total_ice_new = ice_state_counts[IceTransportState::kNew];
1418 int total_ice = dtls_transports.size();
1419
1420 if (total_ice_failed > 0) {
Jonas Olsson6a8727b2018-12-07 13:11:44 +01001421 // Any RTCIceTransports are in the "failed" state.
Jonas Olsson635474e2018-10-18 15:58:17 +02001422 new_ice_connection_state = PeerConnectionInterface::kIceConnectionFailed;
Alex Loiko9289eda2018-11-23 16:18:59 +00001423 } else if (total_ice_disconnected > 0) {
Jonas Olsson6a8727b2018-12-07 13:11:44 +01001424 // None of the previous states apply and any RTCIceTransports are in the
1425 // "disconnected" state.
Jonas Olsson635474e2018-10-18 15:58:17 +02001426 new_ice_connection_state =
1427 PeerConnectionInterface::kIceConnectionDisconnected;
Jonas Olsson6a8727b2018-12-07 13:11:44 +01001428 } else if (total_ice_new + total_ice_closed == total_ice) {
1429 // None of the previous states apply and all RTCIceTransports are in the
1430 // "new" or "closed" state, or there are no transports.
1431 new_ice_connection_state = PeerConnectionInterface::kIceConnectionNew;
1432 } else if (total_ice_new + total_ice_checking > 0) {
1433 // None of the previous states apply and any RTCIceTransports are in the
1434 // "new" or "checking" state.
Jonas Olsson635474e2018-10-18 15:58:17 +02001435 new_ice_connection_state = PeerConnectionInterface::kIceConnectionChecking;
Jonas Olsson6a8727b2018-12-07 13:11:44 +01001436 } else if (total_ice_completed + total_ice_closed == total_ice) {
1437 // None of the previous states apply and all RTCIceTransports are in the
1438 // "completed" or "closed" state.
Jonas Olsson635474e2018-10-18 15:58:17 +02001439 new_ice_connection_state = PeerConnectionInterface::kIceConnectionCompleted;
1440 } else if (total_ice_connected + total_ice_completed + total_ice_closed ==
Jonas Olsson6a8727b2018-12-07 13:11:44 +01001441 total_ice) {
1442 // None of the previous states apply and all RTCIceTransports are in the
1443 // "connected", "completed" or "closed" state.
Jonas Olsson635474e2018-10-18 15:58:17 +02001444 new_ice_connection_state = PeerConnectionInterface::kIceConnectionConnected;
Jonas Olsson635474e2018-10-18 15:58:17 +02001445 } else {
1446 RTC_NOTREACHED();
1447 }
1448
Alex Loiko9289eda2018-11-23 16:18:59 +00001449 if (standardized_ice_connection_state_ != new_ice_connection_state) {
1450 standardized_ice_connection_state_ = new_ice_connection_state;
Jonas Olsson635474e2018-10-18 15:58:17 +02001451 invoker_.AsyncInvoke<void>(
1452 RTC_FROM_HERE, signaling_thread_, [this, new_ice_connection_state] {
Alex Loiko9289eda2018-11-23 16:18:59 +00001453 SignalStandardizedIceConnectionState(new_ice_connection_state);
Jonas Olsson635474e2018-10-18 15:58:17 +02001454 });
1455 }
1456
1457 // Compute the current RTCPeerConnectionState as described in
1458 // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnectionstate.
1459 // The PeerConnection is responsible for handling the "closed" state.
1460 // Note that "connecting" is only a valid state for DTLS transports while
1461 // "checking", "completed" and "disconnected" are only valid for ICE
1462 // transports.
1463 int total_connected = total_ice_connected +
1464 dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTED];
1465 int total_dtls_connecting =
1466 dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTING];
1467 int total_failed =
1468 total_ice_failed + dtls_state_counts[cricket::DTLS_TRANSPORT_FAILED];
1469 int total_closed =
1470 total_ice_closed + dtls_state_counts[cricket::DTLS_TRANSPORT_CLOSED];
1471 int total_new =
1472 total_ice_new + dtls_state_counts[cricket::DTLS_TRANSPORT_NEW];
1473 int total_transports = total_ice * 2;
1474
1475 if (total_failed > 0) {
1476 // Any of the RTCIceTransports or RTCDtlsTransports are in a "failed" state.
1477 new_combined_state = PeerConnectionInterface::PeerConnectionState::kFailed;
Jonas Olsson6a8727b2018-12-07 13:11:44 +01001478 } else if (total_ice_disconnected > 0) {
1479 // None of the previous states apply and any RTCIceTransports or
1480 // RTCDtlsTransports are in the "disconnected" state.
Jonas Olsson635474e2018-10-18 15:58:17 +02001481 new_combined_state =
1482 PeerConnectionInterface::PeerConnectionState::kDisconnected;
Jonas Olsson6a8727b2018-12-07 13:11:44 +01001483 } else if (total_new + total_closed == total_transports) {
1484 // None of the previous states apply and all RTCIceTransports and
1485 // RTCDtlsTransports are in the "new" or "closed" state, or there are no
1486 // transports.
1487 new_combined_state = PeerConnectionInterface::PeerConnectionState::kNew;
1488 } else if (total_new + total_dtls_connecting + total_ice_checking > 0) {
1489 // None of the previous states apply and all RTCIceTransports or
1490 // RTCDtlsTransports are in the "new", "connecting" or "checking" state.
Jonas Olsson635474e2018-10-18 15:58:17 +02001491 new_combined_state =
1492 PeerConnectionInterface::PeerConnectionState::kConnecting;
1493 } else if (total_connected + total_ice_completed + total_closed ==
Jonas Olsson6a8727b2018-12-07 13:11:44 +01001494 total_transports) {
1495 // None of the previous states apply and all RTCIceTransports and
1496 // RTCDtlsTransports are in the "connected", "completed" or "closed" state.
Jonas Olsson635474e2018-10-18 15:58:17 +02001497 new_combined_state =
1498 PeerConnectionInterface::PeerConnectionState::kConnected;
Jonas Olsson635474e2018-10-18 15:58:17 +02001499 } else {
1500 RTC_NOTREACHED();
1501 }
1502
1503 if (combined_connection_state_ != new_combined_state) {
1504 combined_connection_state_ = new_combined_state;
1505 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1506 [this, new_combined_state] {
1507 SignalConnectionState(new_combined_state);
1508 });
1509 }
1510
Zhi Huange818b6e2018-02-22 15:26:27 -08001511 if (all_done_gathering) {
1512 new_gathering_state = cricket::kIceGatheringComplete;
1513 } else if (any_gathering) {
1514 new_gathering_state = cricket::kIceGatheringGathering;
1515 }
1516 if (ice_gathering_state_ != new_gathering_state) {
1517 ice_gathering_state_ = new_gathering_state;
Steve Antond25828a2018-08-31 13:06:05 -07001518 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1519 [this, new_gathering_state] {
1520 SignalIceGatheringState(new_gathering_state);
1521 });
Zhi Huange818b6e2018-02-22 15:26:27 -08001522 }
1523}
1524
1525void JsepTransportController::OnDtlsHandshakeError(
1526 rtc::SSLHandshakeError error) {
1527 SignalDtlsHandshakeError(error);
1528}
1529
1530} // namespace webrtc