blob: 74a9ab6218567fb2dc878d8971ca3129f61350f5 [file] [log] [blame]
Zhi Huange818b6e2018-02-22 23:26:271/*
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/jseptransportcontroller.h"
12
13#include <algorithm>
14#include <memory>
15#include <utility>
16
17#include "p2p/base/port.h"
Piotr (Peter) Slatala9f956252018-10-31 15:25:2618#include "pc/srtpfilter.h"
Zhi Huange818b6e2018-02-22 23:26:2719#include "rtc_base/bind.h"
20#include "rtc_base/checks.h"
Piotr (Peter) Slatala9f956252018-10-31 15:25:2621#include "rtc_base/key_derivation.h"
Zhi Huange818b6e2018-02-22 23:26:2722#include "rtc_base/thread.h"
23
24using webrtc::SdpType;
25
26namespace {
27
Zhi Huange818b6e2018-02-22 23:26:2728webrtc::RTCError VerifyCandidate(const cricket::Candidate& cand) {
29 // No address zero.
30 if (cand.address().IsNil() || cand.address().IsAnyIP()) {
31 return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
32 "candidate has address of zero");
33 }
34
35 // Disallow all ports below 1024, except for 80 and 443 on public addresses.
36 int port = cand.address().port();
37 if (cand.protocol() == cricket::TCP_PROTOCOL_NAME &&
38 (cand.tcptype() == cricket::TCPTYPE_ACTIVE_STR || port == 0)) {
39 // Expected for active-only candidates per
40 // http://tools.ietf.org/html/rfc6544#section-4.5 so no error.
41 // Libjingle clients emit port 0, in "active" mode.
42 return webrtc::RTCError::OK();
43 }
44 if (port < 1024) {
45 if ((port != 80) && (port != 443)) {
46 return webrtc::RTCError(
47 webrtc::RTCErrorType::INVALID_PARAMETER,
48 "candidate has port below 1024, but not 80 or 443");
49 }
50
51 if (cand.address().IsPrivateIP()) {
52 return webrtc::RTCError(
53 webrtc::RTCErrorType::INVALID_PARAMETER,
54 "candidate has port of 80 or 443 with private IP address");
55 }
56 }
57
58 return webrtc::RTCError::OK();
59}
60
61webrtc::RTCError VerifyCandidates(const cricket::Candidates& candidates) {
62 for (const cricket::Candidate& candidate : candidates) {
63 webrtc::RTCError error = VerifyCandidate(candidate);
64 if (!error.ok()) {
65 return error;
66 }
67 }
68 return webrtc::RTCError::OK();
69}
70
71} // namespace
72
73namespace webrtc {
74
75JsepTransportController::JsepTransportController(
76 rtc::Thread* signaling_thread,
77 rtc::Thread* network_thread,
78 cricket::PortAllocator* port_allocator,
Zach Steine20867f2018-08-02 20:20:1579 AsyncResolverFactory* async_resolver_factory,
Zhi Huange818b6e2018-02-22 23:26:2780 Config config)
81 : signaling_thread_(signaling_thread),
82 network_thread_(network_thread),
83 port_allocator_(port_allocator),
Zach Steine20867f2018-08-02 20:20:1584 async_resolver_factory_(async_resolver_factory),
Zhi Huang365381f2018-04-13 23:44:3485 config_(config) {
86 // The |transport_observer| is assumed to be non-null.
87 RTC_DCHECK(config_.transport_observer);
88}
Zhi Huange818b6e2018-02-22 23:26:2789
90JsepTransportController::~JsepTransportController() {
91 // Channel destructors may try to send packets, so this needs to happen on
92 // the network thread.
93 network_thread_->Invoke<void>(
94 RTC_FROM_HERE,
95 rtc::Bind(&JsepTransportController::DestroyAllJsepTransports_n, this));
96}
97
98RTCError JsepTransportController::SetLocalDescription(
99 SdpType type,
100 const cricket::SessionDescription* description) {
101 if (!network_thread_->IsCurrent()) {
102 return network_thread_->Invoke<RTCError>(
103 RTC_FROM_HERE, [=] { return SetLocalDescription(type, description); });
104 }
105
106 if (!initial_offerer_.has_value()) {
107 initial_offerer_.emplace(type == SdpType::kOffer);
108 if (*initial_offerer_) {
109 SetIceRole_n(cricket::ICEROLE_CONTROLLING);
110 } else {
111 SetIceRole_n(cricket::ICEROLE_CONTROLLED);
112 }
113 }
114 return ApplyDescription_n(/*local=*/true, type, description);
115}
116
117RTCError JsepTransportController::SetRemoteDescription(
118 SdpType type,
119 const cricket::SessionDescription* description) {
120 if (!network_thread_->IsCurrent()) {
121 return network_thread_->Invoke<RTCError>(
122 RTC_FROM_HERE, [=] { return SetRemoteDescription(type, description); });
123 }
124
125 return ApplyDescription_n(/*local=*/false, type, description);
126}
127
128RtpTransportInternal* JsepTransportController::GetRtpTransport(
129 const std::string& mid) const {
Zhi Huange830e682018-03-30 17:48:35130 auto jsep_transport = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 23:26:27131 if (!jsep_transport) {
132 return nullptr;
133 }
134 return jsep_transport->rtp_transport();
135}
136
Anton Sukhanov7940da02018-10-10 17:34:49137MediaTransportInterface* JsepTransportController::GetMediaTransport(
138 const std::string& mid) const {
139 auto jsep_transport = GetJsepTransportForMid(mid);
140 if (!jsep_transport) {
141 return nullptr;
142 }
143 return jsep_transport->media_transport();
144}
145
Bjorn Mellem175aa2e2018-11-08 19:23:22146MediaTransportState JsepTransportController::GetMediaTransportState(
147 const std::string& mid) const {
148 auto jsep_transport = GetJsepTransportForMid(mid);
149 if (!jsep_transport) {
150 return MediaTransportState::kPending;
151 }
152 return jsep_transport->media_transport_state();
153}
154
Zhi Huange818b6e2018-02-22 23:26:27155cricket::DtlsTransportInternal* JsepTransportController::GetDtlsTransport(
156 const std::string& mid) const {
Zhi Huange830e682018-03-30 17:48:35157 auto jsep_transport = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 23:26:27158 if (!jsep_transport) {
159 return nullptr;
160 }
161 return jsep_transport->rtp_dtls_transport();
162}
163
164cricket::DtlsTransportInternal* JsepTransportController::GetRtcpDtlsTransport(
165 const std::string& mid) const {
Zhi Huange830e682018-03-30 17:48:35166 auto jsep_transport = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 23:26:27167 if (!jsep_transport) {
168 return nullptr;
169 }
170 return jsep_transport->rtcp_dtls_transport();
171}
172
173void JsepTransportController::SetIceConfig(const cricket::IceConfig& config) {
174 if (!network_thread_->IsCurrent()) {
175 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] { SetIceConfig(config); });
176 return;
177 }
178
179 ice_config_ = config;
180 for (auto& dtls : GetDtlsTransports()) {
181 dtls->ice_transport()->SetIceConfig(ice_config_);
182 }
183}
184
185void JsepTransportController::SetNeedsIceRestartFlag() {
Zhi Huange830e682018-03-30 17:48:35186 for (auto& kv : jsep_transports_by_name_) {
Zhi Huange818b6e2018-02-22 23:26:27187 kv.second->SetNeedsIceRestartFlag();
188 }
189}
190
191bool JsepTransportController::NeedsIceRestart(
192 const std::string& transport_name) const {
Zhi Huang365381f2018-04-13 23:44:34193 const cricket::JsepTransport* transport =
Zhi Huange830e682018-03-30 17:48:35194 GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 23:26:27195 if (!transport) {
196 return false;
197 }
198 return transport->needs_ice_restart();
199}
200
Danil Chapovalov66cadcc2018-06-19 14:47:43201absl::optional<rtc::SSLRole> JsepTransportController::GetDtlsRole(
Zhi Huange830e682018-03-30 17:48:35202 const std::string& mid) const {
Zhi Huange818b6e2018-02-22 23:26:27203 if (!network_thread_->IsCurrent()) {
Danil Chapovalov66cadcc2018-06-19 14:47:43204 return network_thread_->Invoke<absl::optional<rtc::SSLRole>>(
Zhi Huange830e682018-03-30 17:48:35205 RTC_FROM_HERE, [&] { return GetDtlsRole(mid); });
Zhi Huange818b6e2018-02-22 23:26:27206 }
207
Zhi Huang365381f2018-04-13 23:44:34208 const cricket::JsepTransport* t = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 23:26:27209 if (!t) {
Danil Chapovalov66cadcc2018-06-19 14:47:43210 return absl::optional<rtc::SSLRole>();
Zhi Huange818b6e2018-02-22 23:26:27211 }
212 return t->GetDtlsRole();
213}
214
215bool JsepTransportController::SetLocalCertificate(
216 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
217 if (!network_thread_->IsCurrent()) {
218 return network_thread_->Invoke<bool>(
219 RTC_FROM_HERE, [&] { return SetLocalCertificate(certificate); });
220 }
221
222 // Can't change a certificate, or set a null certificate.
223 if (certificate_ || !certificate) {
224 return false;
225 }
226 certificate_ = certificate;
227
228 // Set certificate for JsepTransport, which verifies it matches the
229 // fingerprint in SDP, and DTLS transport.
230 // Fallback from DTLS to SDES is not supported.
Zhi Huange830e682018-03-30 17:48:35231 for (auto& kv : jsep_transports_by_name_) {
Zhi Huange818b6e2018-02-22 23:26:27232 kv.second->SetLocalCertificate(certificate_);
233 }
234 for (auto& dtls : GetDtlsTransports()) {
235 bool set_cert_success = dtls->SetLocalCertificate(certificate_);
236 RTC_DCHECK(set_cert_success);
237 }
238 return true;
239}
240
241rtc::scoped_refptr<rtc::RTCCertificate>
242JsepTransportController::GetLocalCertificate(
243 const std::string& transport_name) const {
244 if (!network_thread_->IsCurrent()) {
245 return network_thread_->Invoke<rtc::scoped_refptr<rtc::RTCCertificate>>(
246 RTC_FROM_HERE, [&] { return GetLocalCertificate(transport_name); });
247 }
248
Zhi Huang365381f2018-04-13 23:44:34249 const cricket::JsepTransport* t = GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 23:26:27250 if (!t) {
251 return nullptr;
252 }
253 return t->GetLocalCertificate();
254}
255
Taylor Brandstetterc3928662018-02-23 21:04:51256std::unique_ptr<rtc::SSLCertChain>
257JsepTransportController::GetRemoteSSLCertChain(
Zhi Huange818b6e2018-02-22 23:26:27258 const std::string& transport_name) const {
259 if (!network_thread_->IsCurrent()) {
Taylor Brandstetterc3928662018-02-23 21:04:51260 return network_thread_->Invoke<std::unique_ptr<rtc::SSLCertChain>>(
261 RTC_FROM_HERE, [&] { return GetRemoteSSLCertChain(transport_name); });
Zhi Huange818b6e2018-02-22 23:26:27262 }
263
Zhi Huange830e682018-03-30 17:48:35264 // Get the certificate from the RTP transport's DTLS handshake. Should be
265 // identical to the RTCP transport's, since they were given the same remote
Zhi Huange818b6e2018-02-22 23:26:27266 // fingerprint.
Zhi Huange830e682018-03-30 17:48:35267 auto jsep_transport = GetJsepTransportByName(transport_name);
268 if (!jsep_transport) {
269 return nullptr;
270 }
271 auto dtls = jsep_transport->rtp_dtls_transport();
Zhi Huange818b6e2018-02-22 23:26:27272 if (!dtls) {
273 return nullptr;
274 }
275
Taylor Brandstetterc3928662018-02-23 21:04:51276 return dtls->GetRemoteSSLCertChain();
Zhi Huange818b6e2018-02-22 23:26:27277}
278
279void JsepTransportController::MaybeStartGathering() {
280 if (!network_thread_->IsCurrent()) {
281 network_thread_->Invoke<void>(RTC_FROM_HERE,
282 [&] { MaybeStartGathering(); });
283 return;
284 }
285
286 for (auto& dtls : GetDtlsTransports()) {
287 dtls->ice_transport()->MaybeStartGathering();
288 }
289}
290
291RTCError JsepTransportController::AddRemoteCandidates(
292 const std::string& transport_name,
293 const cricket::Candidates& candidates) {
294 if (!network_thread_->IsCurrent()) {
295 return network_thread_->Invoke<RTCError>(RTC_FROM_HERE, [&] {
296 return AddRemoteCandidates(transport_name, candidates);
297 });
298 }
299
300 // Verify each candidate before passing down to the transport layer.
301 RTCError error = VerifyCandidates(candidates);
302 if (!error.ok()) {
303 return error;
304 }
Zhi Huange830e682018-03-30 17:48:35305 auto jsep_transport = GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 23:26:27306 if (!jsep_transport) {
Zhi Huange830e682018-03-30 17:48:35307 RTC_LOG(LS_WARNING) << "Not adding candidate because the JsepTransport "
308 "doesn't exist. Ignore it.";
309 return RTCError::OK();
Zhi Huange818b6e2018-02-22 23:26:27310 }
Zhi Huange818b6e2018-02-22 23:26:27311 return jsep_transport->AddRemoteCandidates(candidates);
312}
313
314RTCError JsepTransportController::RemoveRemoteCandidates(
315 const cricket::Candidates& candidates) {
316 if (!network_thread_->IsCurrent()) {
317 return network_thread_->Invoke<RTCError>(
318 RTC_FROM_HERE, [&] { return RemoveRemoteCandidates(candidates); });
319 }
320
321 // Verify each candidate before passing down to the transport layer.
322 RTCError error = VerifyCandidates(candidates);
323 if (!error.ok()) {
324 return error;
325 }
326
327 std::map<std::string, cricket::Candidates> candidates_by_transport_name;
328 for (const cricket::Candidate& cand : candidates) {
329 if (!cand.transport_name().empty()) {
330 candidates_by_transport_name[cand.transport_name()].push_back(cand);
331 } else {
332 RTC_LOG(LS_ERROR) << "Not removing candidate because it does not have a "
333 "transport name set: "
334 << cand.ToString();
335 }
336 }
337
338 for (const auto& kv : candidates_by_transport_name) {
339 const std::string& transport_name = kv.first;
340 const cricket::Candidates& candidates = kv.second;
Zhi Huang365381f2018-04-13 23:44:34341 cricket::JsepTransport* jsep_transport =
Zhi Huange830e682018-03-30 17:48:35342 GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 23:26:27343 if (!jsep_transport) {
Zhi Huange830e682018-03-30 17:48:35344 RTC_LOG(LS_WARNING)
345 << "Not removing candidate because the JsepTransport doesn't exist.";
346 continue;
Zhi Huange818b6e2018-02-22 23:26:27347 }
348 for (const cricket::Candidate& candidate : candidates) {
349 auto dtls = candidate.component() == cricket::ICE_CANDIDATE_COMPONENT_RTP
350 ? jsep_transport->rtp_dtls_transport()
351 : jsep_transport->rtcp_dtls_transport();
352 if (dtls) {
353 dtls->ice_transport()->RemoveRemoteCandidate(candidate);
354 }
355 }
356 }
357 return RTCError::OK();
358}
359
360bool JsepTransportController::GetStats(const std::string& transport_name,
361 cricket::TransportStats* stats) {
362 if (!network_thread_->IsCurrent()) {
363 return network_thread_->Invoke<bool>(
364 RTC_FROM_HERE, [=] { return GetStats(transport_name, stats); });
365 }
366
Zhi Huang365381f2018-04-13 23:44:34367 cricket::JsepTransport* transport = GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 23:26:27368 if (!transport) {
369 return false;
370 }
371 return transport->GetStats(stats);
372}
373
Zhi Huangb57e1692018-06-12 18:41:11374void JsepTransportController::SetActiveResetSrtpParams(
375 bool active_reset_srtp_params) {
376 if (!network_thread_->IsCurrent()) {
377 network_thread_->Invoke<void>(RTC_FROM_HERE, [=] {
378 SetActiveResetSrtpParams(active_reset_srtp_params);
379 });
380 return;
381 }
382
383 RTC_LOG(INFO)
384 << "Updating the active_reset_srtp_params for JsepTransportController: "
385 << active_reset_srtp_params;
386 config_.active_reset_srtp_params = active_reset_srtp_params;
387 for (auto& kv : jsep_transports_by_name_) {
388 kv.second->SetActiveResetSrtpParams(active_reset_srtp_params);
389 }
390}
391
Piotr (Peter) Slatala97fc11f2018-10-18 19:57:59392void JsepTransportController::SetMediaTransportFactory(
393 MediaTransportFactory* media_transport_factory) {
394 RTC_DCHECK(media_transport_factory == config_.media_transport_factory ||
395 jsep_transports_by_name_.empty())
396 << "You can only call SetMediaTransportFactory before "
397 "JsepTransportController created its first transport.";
398 config_.media_transport_factory = media_transport_factory;
399}
400
Zhi Huange818b6e2018-02-22 23:26:27401std::unique_ptr<cricket::DtlsTransportInternal>
402JsepTransportController::CreateDtlsTransport(const std::string& transport_name,
403 bool rtcp) {
404 RTC_DCHECK(network_thread_->IsCurrent());
405 int component = rtcp ? cricket::ICE_CANDIDATE_COMPONENT_RTCP
406 : cricket::ICE_CANDIDATE_COMPONENT_RTP;
407
408 std::unique_ptr<cricket::DtlsTransportInternal> dtls;
409 if (config_.external_transport_factory) {
410 auto ice = config_.external_transport_factory->CreateIceTransport(
411 transport_name, component);
412 dtls = config_.external_transport_factory->CreateDtlsTransport(
413 std::move(ice), config_.crypto_options);
414 } else {
Karl Wiberg918f50c2018-07-05 09:40:33415 auto ice = absl::make_unique<cricket::P2PTransportChannel>(
Zach Steine20867f2018-08-02 20:20:15416 transport_name, component, port_allocator_, async_resolver_factory_,
417 config_.event_log);
Karl Wiberg918f50c2018-07-05 09:40:33418 dtls = absl::make_unique<cricket::DtlsTransport>(std::move(ice),
419 config_.crypto_options);
Zhi Huange818b6e2018-02-22 23:26:27420 }
421
422 RTC_DCHECK(dtls);
423 dtls->SetSslMaxProtocolVersion(config_.ssl_max_version);
Zhi Huange818b6e2018-02-22 23:26:27424 dtls->ice_transport()->SetIceRole(ice_role_);
425 dtls->ice_transport()->SetIceTiebreaker(ice_tiebreaker_);
426 dtls->ice_transport()->SetIceConfig(ice_config_);
427 if (certificate_) {
428 bool set_cert_success = dtls->SetLocalCertificate(certificate_);
429 RTC_DCHECK(set_cert_success);
430 }
431
432 // Connect to signals offered by the DTLS and ICE transport.
433 dtls->SignalWritableState.connect(
434 this, &JsepTransportController::OnTransportWritableState_n);
435 dtls->SignalReceivingState.connect(
436 this, &JsepTransportController::OnTransportReceivingState_n);
437 dtls->SignalDtlsHandshakeError.connect(
438 this, &JsepTransportController::OnDtlsHandshakeError);
439 dtls->ice_transport()->SignalGatheringState.connect(
440 this, &JsepTransportController::OnTransportGatheringState_n);
441 dtls->ice_transport()->SignalCandidateGathered.connect(
442 this, &JsepTransportController::OnTransportCandidateGathered_n);
443 dtls->ice_transport()->SignalCandidatesRemoved.connect(
444 this, &JsepTransportController::OnTransportCandidatesRemoved_n);
445 dtls->ice_transport()->SignalRoleConflict.connect(
446 this, &JsepTransportController::OnTransportRoleConflict_n);
447 dtls->ice_transport()->SignalStateChanged.connect(
448 this, &JsepTransportController::OnTransportStateChanged_n);
449 return dtls;
450}
451
452std::unique_ptr<webrtc::RtpTransport>
453JsepTransportController::CreateUnencryptedRtpTransport(
454 const std::string& transport_name,
455 rtc::PacketTransportInternal* rtp_packet_transport,
456 rtc::PacketTransportInternal* rtcp_packet_transport) {
457 RTC_DCHECK(network_thread_->IsCurrent());
Zhi Huange830e682018-03-30 17:48:35458 auto unencrypted_rtp_transport =
Karl Wiberg918f50c2018-07-05 09:40:33459 absl::make_unique<RtpTransport>(rtcp_packet_transport == nullptr);
Zhi Huange830e682018-03-30 17:48:35460 unencrypted_rtp_transport->SetRtpPacketTransport(rtp_packet_transport);
461 if (rtcp_packet_transport) {
462 unencrypted_rtp_transport->SetRtcpPacketTransport(rtcp_packet_transport);
463 }
464 return unencrypted_rtp_transport;
Zhi Huange818b6e2018-02-22 23:26:27465}
466
467std::unique_ptr<webrtc::SrtpTransport>
468JsepTransportController::CreateSdesTransport(
469 const std::string& transport_name,
Zhi Huange830e682018-03-30 17:48:35470 cricket::DtlsTransportInternal* rtp_dtls_transport,
471 cricket::DtlsTransportInternal* rtcp_dtls_transport) {
Zhi Huange818b6e2018-02-22 23:26:27472 RTC_DCHECK(network_thread_->IsCurrent());
Zhi Huange818b6e2018-02-22 23:26:27473 auto srtp_transport =
Karl Wiberg918f50c2018-07-05 09:40:33474 absl::make_unique<webrtc::SrtpTransport>(rtcp_dtls_transport == nullptr);
Zhi Huange830e682018-03-30 17:48:35475 RTC_DCHECK(rtp_dtls_transport);
476 srtp_transport->SetRtpPacketTransport(rtp_dtls_transport);
477 if (rtcp_dtls_transport) {
478 srtp_transport->SetRtcpPacketTransport(rtcp_dtls_transport);
Zhi Huange818b6e2018-02-22 23:26:27479 }
480 if (config_.enable_external_auth) {
481 srtp_transport->EnableExternalAuth();
482 }
483 return srtp_transport;
484}
485
486std::unique_ptr<webrtc::DtlsSrtpTransport>
487JsepTransportController::CreateDtlsSrtpTransport(
488 const std::string& transport_name,
489 cricket::DtlsTransportInternal* rtp_dtls_transport,
490 cricket::DtlsTransportInternal* rtcp_dtls_transport) {
491 RTC_DCHECK(network_thread_->IsCurrent());
Karl Wiberg918f50c2018-07-05 09:40:33492 auto dtls_srtp_transport = absl::make_unique<webrtc::DtlsSrtpTransport>(
Zhi Huang365381f2018-04-13 23:44:34493 rtcp_dtls_transport == nullptr);
Zhi Huang27f3bf52018-03-27 04:37:23494 if (config_.enable_external_auth) {
Zhi Huang365381f2018-04-13 23:44:34495 dtls_srtp_transport->EnableExternalAuth();
Zhi Huang27f3bf52018-03-27 04:37:23496 }
Zhi Huang97d5e5b2018-03-27 00:09:01497
Zhi Huange818b6e2018-02-22 23:26:27498 dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport,
499 rtcp_dtls_transport);
Zhi Huangb57e1692018-06-12 18:41:11500 dtls_srtp_transport->SetActiveResetSrtpParams(
501 config_.active_reset_srtp_params);
Jonas Olsson635474e2018-10-18 13:58:17502 dtls_srtp_transport->SignalDtlsStateChange.connect(
503 this, &JsepTransportController::UpdateAggregateStates_n);
Zhi Huange818b6e2018-02-22 23:26:27504 return dtls_srtp_transport;
505}
506
507std::vector<cricket::DtlsTransportInternal*>
508JsepTransportController::GetDtlsTransports() {
509 std::vector<cricket::DtlsTransportInternal*> dtls_transports;
Zhi Huange830e682018-03-30 17:48:35510 for (auto it = jsep_transports_by_name_.begin();
511 it != jsep_transports_by_name_.end(); ++it) {
Zhi Huange818b6e2018-02-22 23:26:27512 auto jsep_transport = it->second.get();
513 RTC_DCHECK(jsep_transport);
514 if (jsep_transport->rtp_dtls_transport()) {
515 dtls_transports.push_back(jsep_transport->rtp_dtls_transport());
516 }
517
518 if (jsep_transport->rtcp_dtls_transport()) {
519 dtls_transports.push_back(jsep_transport->rtcp_dtls_transport());
520 }
521 }
522 return dtls_transports;
523}
524
Zhi Huange818b6e2018-02-22 23:26:27525RTCError JsepTransportController::ApplyDescription_n(
526 bool local,
527 SdpType type,
528 const cricket::SessionDescription* description) {
529 RTC_DCHECK(network_thread_->IsCurrent());
530 RTC_DCHECK(description);
531
532 if (local) {
533 local_desc_ = description;
534 } else {
535 remote_desc_ = description;
536 }
537
Zhi Huange830e682018-03-30 17:48:35538 RTCError error;
Zhi Huangd2248f82018-04-10 21:41:03539 error = ValidateAndMaybeUpdateBundleGroup(local, type, description);
Zhi Huange830e682018-03-30 17:48:35540 if (!error.ok()) {
541 return error;
Zhi Huange818b6e2018-02-22 23:26:27542 }
543
544 std::vector<int> merged_encrypted_extension_ids;
545 if (bundle_group_) {
546 merged_encrypted_extension_ids =
547 MergeEncryptedHeaderExtensionIdsForBundle(description);
548 }
549
550 for (const cricket::ContentInfo& content_info : description->contents()) {
551 // Don't create transports for rejected m-lines and bundled m-lines."
552 if (content_info.rejected ||
553 (IsBundled(content_info.name) && content_info.name != *bundled_mid())) {
554 continue;
555 }
Anton Sukhanov7940da02018-10-10 17:34:49556 error = MaybeCreateJsepTransport(local, content_info);
Zhi Huange830e682018-03-30 17:48:35557 if (!error.ok()) {
558 return error;
559 }
Zhi Huange818b6e2018-02-22 23:26:27560 }
561
562 RTC_DCHECK(description->contents().size() ==
563 description->transport_infos().size());
564 for (size_t i = 0; i < description->contents().size(); ++i) {
565 const cricket::ContentInfo& content_info = description->contents()[i];
566 const cricket::TransportInfo& transport_info =
567 description->transport_infos()[i];
568 if (content_info.rejected) {
Taylor Brandstettercbaa2542018-04-16 23:42:14569 HandleRejectedContent(content_info, description);
Zhi Huange818b6e2018-02-22 23:26:27570 continue;
571 }
572
573 if (IsBundled(content_info.name) && content_info.name != *bundled_mid()) {
Zhi Huang365381f2018-04-13 23:44:34574 if (!HandleBundledContent(content_info)) {
575 return RTCError(RTCErrorType::INVALID_PARAMETER,
576 "Failed to process the bundled m= section.");
577 }
Zhi Huange818b6e2018-02-22 23:26:27578 continue;
579 }
580
Zhi Huange830e682018-03-30 17:48:35581 error = ValidateContent(content_info);
582 if (!error.ok()) {
583 return error;
584 }
585
Zhi Huange818b6e2018-02-22 23:26:27586 std::vector<int> extension_ids;
Taylor Brandstetter0ab56512018-04-12 17:30:48587 if (bundled_mid() && content_info.name == *bundled_mid()) {
Zhi Huange818b6e2018-02-22 23:26:27588 extension_ids = merged_encrypted_extension_ids;
589 } else {
590 extension_ids = GetEncryptedHeaderExtensionIds(content_info);
591 }
592
Zhi Huange830e682018-03-30 17:48:35593 int rtp_abs_sendtime_extn_id =
594 GetRtpAbsSendTimeHeaderExtensionId(content_info);
595
Zhi Huang365381f2018-04-13 23:44:34596 cricket::JsepTransport* transport =
Zhi Huange830e682018-03-30 17:48:35597 GetJsepTransportForMid(content_info.name);
Zhi Huange818b6e2018-02-22 23:26:27598 RTC_DCHECK(transport);
599
600 SetIceRole_n(DetermineIceRole(transport, transport_info, type, local));
601
Zhi Huange818b6e2018-02-22 23:26:27602 cricket::JsepTransportDescription jsep_description =
603 CreateJsepTransportDescription(content_info, transport_info,
Zhi Huange830e682018-03-30 17:48:35604 extension_ids, rtp_abs_sendtime_extn_id);
Zhi Huange818b6e2018-02-22 23:26:27605 if (local) {
606 error =
607 transport->SetLocalJsepTransportDescription(jsep_description, type);
608 } else {
609 error =
610 transport->SetRemoteJsepTransportDescription(jsep_description, type);
611 }
612
613 if (!error.ok()) {
614 LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_PARAMETER,
615 "Failed to apply the description for " +
616 content_info.name + ": " + error.message());
617 }
618 }
619 return RTCError::OK();
620}
621
Zhi Huangd2248f82018-04-10 21:41:03622RTCError JsepTransportController::ValidateAndMaybeUpdateBundleGroup(
623 bool local,
624 SdpType type,
Zhi Huange830e682018-03-30 17:48:35625 const cricket::SessionDescription* description) {
626 RTC_DCHECK(description);
Zhi Huangd2248f82018-04-10 21:41:03627 const cricket::ContentGroup* new_bundle_group =
628 description->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
629
630 // The BUNDLE group containing a MID that no m= section has is invalid.
631 if (new_bundle_group) {
632 for (auto content_name : new_bundle_group->content_names()) {
633 if (!description->GetContentByName(content_name)) {
634 return RTCError(RTCErrorType::INVALID_PARAMETER,
635 "The BUNDLE group contains MID:" + content_name +
636 " matching no m= section.");
637 }
638 }
639 }
640
641 if (type == SdpType::kAnswer) {
642 const cricket::ContentGroup* offered_bundle_group =
643 local ? remote_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE)
644 : local_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
645
646 if (new_bundle_group) {
647 // The BUNDLE group in answer should be a subset of offered group.
648 for (auto content_name : new_bundle_group->content_names()) {
649 if (!offered_bundle_group ||
650 !offered_bundle_group->HasContentName(content_name)) {
651 return RTCError(RTCErrorType::INVALID_PARAMETER,
652 "The BUNDLE group in answer contains a MID that was "
653 "not in the offered group.");
654 }
655 }
656 }
657
658 if (bundle_group_) {
659 for (auto content_name : bundle_group_->content_names()) {
660 // An answer that removes m= sections from pre-negotiated BUNDLE group
661 // without rejecting it, is invalid.
662 if (!new_bundle_group ||
663 !new_bundle_group->HasContentName(content_name)) {
664 auto* content_info = description->GetContentByName(content_name);
665 if (!content_info || !content_info->rejected) {
666 return RTCError(RTCErrorType::INVALID_PARAMETER,
667 "Answer cannot remove m= section " + content_name +
668 " from already-established BUNDLE group.");
669 }
670 }
671 }
672 }
673 }
674
675 if (config_.bundle_policy ==
676 PeerConnectionInterface::kBundlePolicyMaxBundle &&
677 !description->HasGroup(cricket::GROUP_TYPE_BUNDLE)) {
678 return RTCError(RTCErrorType::INVALID_PARAMETER,
679 "max-bundle is used but no bundle group found.");
680 }
681
682 if (ShouldUpdateBundleGroup(type, description)) {
Taylor Brandstetter0ab56512018-04-12 17:30:48683 const std::string* new_bundled_mid = new_bundle_group->FirstContentName();
684 if (bundled_mid() && new_bundled_mid &&
685 *bundled_mid() != *new_bundled_mid) {
Zhi Huangd2248f82018-04-10 21:41:03686 return RTCError(RTCErrorType::UNSUPPORTED_OPERATION,
687 "Changing the negotiated BUNDLE-tag is not supported.");
688 }
689
690 bundle_group_ = *new_bundle_group;
691 }
Zhi Huange830e682018-03-30 17:48:35692
693 if (!bundled_mid()) {
694 return RTCError::OK();
695 }
696
697 auto bundled_content = description->GetContentByName(*bundled_mid());
698 if (!bundled_content) {
699 return RTCError(
700 RTCErrorType::INVALID_PARAMETER,
701 "An m= section associated with the BUNDLE-tag doesn't exist.");
702 }
703
704 // If the |bundled_content| is rejected, other contents in the bundle group
705 // should be rejected.
706 if (bundled_content->rejected) {
707 for (auto content_name : bundle_group_->content_names()) {
708 auto other_content = description->GetContentByName(content_name);
709 if (!other_content->rejected) {
710 return RTCError(
711 RTCErrorType::INVALID_PARAMETER,
712 "The m= section:" + content_name + " should be rejected.");
713 }
714 }
715 }
716
717 return RTCError::OK();
718}
719
720RTCError JsepTransportController::ValidateContent(
721 const cricket::ContentInfo& content_info) {
722 if (config_.rtcp_mux_policy ==
723 PeerConnectionInterface::kRtcpMuxPolicyRequire &&
724 content_info.type == cricket::MediaProtocolType::kRtp &&
725 !content_info.media_description()->rtcp_mux()) {
726 return RTCError(RTCErrorType::INVALID_PARAMETER,
727 "The m= section:" + content_info.name +
728 " is invalid. RTCP-MUX is not "
729 "enabled when it is required.");
730 }
731 return RTCError::OK();
732}
733
Taylor Brandstettercbaa2542018-04-16 23:42:14734void JsepTransportController::HandleRejectedContent(
Zhi Huangd2248f82018-04-10 21:41:03735 const cricket::ContentInfo& content_info,
736 const cricket::SessionDescription* description) {
Zhi Huange818b6e2018-02-22 23:26:27737 // If the content is rejected, let the
738 // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first,
Zhi Huang365381f2018-04-13 23:44:34739 // then destroy the cricket::JsepTransport.
Taylor Brandstettercbaa2542018-04-16 23:42:14740 RemoveTransportForMid(content_info.name);
Zhi Huange830e682018-03-30 17:48:35741 if (content_info.name == bundled_mid()) {
742 for (auto content_name : bundle_group_->content_names()) {
Taylor Brandstettercbaa2542018-04-16 23:42:14743 RemoveTransportForMid(content_name);
Zhi Huange830e682018-03-30 17:48:35744 }
745 bundle_group_.reset();
746 } else if (IsBundled(content_info.name)) {
747 // Remove the rejected content from the |bundle_group_|.
Zhi Huange818b6e2018-02-22 23:26:27748 bundle_group_->RemoveContentName(content_info.name);
Zhi Huange830e682018-03-30 17:48:35749 // Reset the bundle group if nothing left.
750 if (!bundle_group_->FirstContentName()) {
751 bundle_group_.reset();
752 }
Zhi Huange818b6e2018-02-22 23:26:27753 }
Taylor Brandstettercbaa2542018-04-16 23:42:14754 MaybeDestroyJsepTransport(content_info.name);
Zhi Huange818b6e2018-02-22 23:26:27755}
756
Zhi Huang365381f2018-04-13 23:44:34757bool JsepTransportController::HandleBundledContent(
Zhi Huange818b6e2018-02-22 23:26:27758 const cricket::ContentInfo& content_info) {
Zhi Huangd2248f82018-04-10 21:41:03759 auto jsep_transport = GetJsepTransportByName(*bundled_mid());
760 RTC_DCHECK(jsep_transport);
Zhi Huange818b6e2018-02-22 23:26:27761 // If the content is bundled, let the
762 // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first,
Zhi Huang365381f2018-04-13 23:44:34763 // then destroy the cricket::JsepTransport.
Taylor Brandstettercbaa2542018-04-16 23:42:14764 if (SetTransportForMid(content_info.name, jsep_transport)) {
Piotr (Peter) Slatala10aeb2a2018-11-14 18:57:24765 // TODO(bugs.webrtc.org/9719) For media transport this is far from ideal,
766 // because it means that we first create media transport and start
767 // connecting it, and then we destroy it. We will need to address it before
768 // video path is enabled.
Zhi Huang365381f2018-04-13 23:44:34769 MaybeDestroyJsepTransport(content_info.name);
770 return true;
771 }
772 return false;
Zhi Huange818b6e2018-02-22 23:26:27773}
774
Zhi Huang365381f2018-04-13 23:44:34775bool JsepTransportController::SetTransportForMid(
Zhi Huangd2248f82018-04-10 21:41:03776 const std::string& mid,
Taylor Brandstettercbaa2542018-04-16 23:42:14777 cricket::JsepTransport* jsep_transport) {
Zhi Huang365381f2018-04-13 23:44:34778 RTC_DCHECK(jsep_transport);
Zhi Huangd2248f82018-04-10 21:41:03779 if (mid_to_transport_[mid] == jsep_transport) {
Zhi Huang365381f2018-04-13 23:44:34780 return true;
Zhi Huangd2248f82018-04-10 21:41:03781 }
782
783 mid_to_transport_[mid] = jsep_transport;
Taylor Brandstettercbaa2542018-04-16 23:42:14784 return config_.transport_observer->OnTransportChanged(
785 mid, jsep_transport->rtp_transport(),
Piotr (Peter) Slatalacc8e8bb2018-11-15 16:26:19786 jsep_transport->rtp_dtls_transport(), jsep_transport->media_transport());
Zhi Huangd2248f82018-04-10 21:41:03787}
788
Taylor Brandstettercbaa2542018-04-16 23:42:14789void JsepTransportController::RemoveTransportForMid(const std::string& mid) {
Piotr (Peter) Slatalacc8e8bb2018-11-15 16:26:19790 bool ret = config_.transport_observer->OnTransportChanged(mid, nullptr,
791 nullptr, nullptr);
Taylor Brandstettercbaa2542018-04-16 23:42:14792 // Calling OnTransportChanged with nullptr should always succeed, since it is
793 // only expected to fail when adding media to a transport (not removing).
794 RTC_DCHECK(ret);
Zhi Huangd2248f82018-04-10 21:41:03795 mid_to_transport_.erase(mid);
796}
797
Zhi Huange818b6e2018-02-22 23:26:27798cricket::JsepTransportDescription
799JsepTransportController::CreateJsepTransportDescription(
800 cricket::ContentInfo content_info,
801 cricket::TransportInfo transport_info,
Zhi Huange830e682018-03-30 17:48:35802 const std::vector<int>& encrypted_extension_ids,
803 int rtp_abs_sendtime_extn_id) {
Zhi Huange818b6e2018-02-22 23:26:27804 const cricket::MediaContentDescription* content_desc =
805 static_cast<const cricket::MediaContentDescription*>(
806 content_info.description);
807 RTC_DCHECK(content_desc);
808 bool rtcp_mux_enabled = content_info.type == cricket::MediaProtocolType::kSctp
809 ? true
810 : content_desc->rtcp_mux();
811
812 return cricket::JsepTransportDescription(
813 rtcp_mux_enabled, content_desc->cryptos(), encrypted_extension_ids,
Zhi Huange830e682018-03-30 17:48:35814 rtp_abs_sendtime_extn_id, transport_info.description);
Zhi Huange818b6e2018-02-22 23:26:27815}
816
817bool JsepTransportController::ShouldUpdateBundleGroup(
818 SdpType type,
819 const cricket::SessionDescription* description) {
820 if (config_.bundle_policy ==
821 PeerConnectionInterface::kBundlePolicyMaxBundle) {
822 return true;
823 }
824
825 if (type != SdpType::kAnswer) {
826 return false;
827 }
828
829 RTC_DCHECK(local_desc_ && remote_desc_);
830 const cricket::ContentGroup* local_bundle =
831 local_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
832 const cricket::ContentGroup* remote_bundle =
833 remote_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
834 return local_bundle && remote_bundle;
835}
836
837std::vector<int> JsepTransportController::GetEncryptedHeaderExtensionIds(
838 const cricket::ContentInfo& content_info) {
839 const cricket::MediaContentDescription* content_desc =
840 static_cast<const cricket::MediaContentDescription*>(
841 content_info.description);
842
Benjamin Wrighta54daf12018-10-11 22:33:17843 if (!config_.crypto_options.srtp.enable_encrypted_rtp_header_extensions) {
Zhi Huange818b6e2018-02-22 23:26:27844 return std::vector<int>();
845 }
846
847 std::vector<int> encrypted_header_extension_ids;
848 for (auto extension : content_desc->rtp_header_extensions()) {
849 if (!extension.encrypt) {
850 continue;
851 }
852 auto it = std::find(encrypted_header_extension_ids.begin(),
853 encrypted_header_extension_ids.end(), extension.id);
854 if (it == encrypted_header_extension_ids.end()) {
855 encrypted_header_extension_ids.push_back(extension.id);
856 }
857 }
858 return encrypted_header_extension_ids;
859}
860
861std::vector<int>
862JsepTransportController::MergeEncryptedHeaderExtensionIdsForBundle(
863 const cricket::SessionDescription* description) {
864 RTC_DCHECK(description);
865 RTC_DCHECK(bundle_group_);
866
867 std::vector<int> merged_ids;
868 // Union the encrypted header IDs in the group when bundle is enabled.
869 for (const cricket::ContentInfo& content_info : description->contents()) {
870 if (bundle_group_->HasContentName(content_info.name)) {
871 std::vector<int> extension_ids =
872 GetEncryptedHeaderExtensionIds(content_info);
873 for (int id : extension_ids) {
874 auto it = std::find(merged_ids.begin(), merged_ids.end(), id);
875 if (it == merged_ids.end()) {
876 merged_ids.push_back(id);
877 }
878 }
879 }
880 }
881 return merged_ids;
882}
883
Zhi Huange830e682018-03-30 17:48:35884int JsepTransportController::GetRtpAbsSendTimeHeaderExtensionId(
Zhi Huange818b6e2018-02-22 23:26:27885 const cricket::ContentInfo& content_info) {
Zhi Huange830e682018-03-30 17:48:35886 if (!config_.enable_external_auth) {
887 return -1;
Zhi Huange818b6e2018-02-22 23:26:27888 }
889
890 const cricket::MediaContentDescription* content_desc =
891 static_cast<const cricket::MediaContentDescription*>(
892 content_info.description);
Zhi Huange830e682018-03-30 17:48:35893
894 const webrtc::RtpExtension* send_time_extension =
895 webrtc::RtpExtension::FindHeaderExtensionByUri(
896 content_desc->rtp_header_extensions(),
897 webrtc::RtpExtension::kAbsSendTimeUri);
898 return send_time_extension ? send_time_extension->id : -1;
899}
900
Zhi Huang365381f2018-04-13 23:44:34901const cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid(
Zhi Huange830e682018-03-30 17:48:35902 const std::string& mid) const {
Zhi Huangd2248f82018-04-10 21:41:03903 auto it = mid_to_transport_.find(mid);
904 return it == mid_to_transport_.end() ? nullptr : it->second;
Zhi Huange830e682018-03-30 17:48:35905}
906
Zhi Huang365381f2018-04-13 23:44:34907cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid(
Zhi Huange830e682018-03-30 17:48:35908 const std::string& mid) {
Zhi Huangd2248f82018-04-10 21:41:03909 auto it = mid_to_transport_.find(mid);
910 return it == mid_to_transport_.end() ? nullptr : it->second;
Zhi Huange830e682018-03-30 17:48:35911}
912
Zhi Huang365381f2018-04-13 23:44:34913const cricket::JsepTransport* JsepTransportController::GetJsepTransportByName(
Zhi Huange830e682018-03-30 17:48:35914 const std::string& transport_name) const {
915 auto it = jsep_transports_by_name_.find(transport_name);
916 return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
917}
918
Zhi Huang365381f2018-04-13 23:44:34919cricket::JsepTransport* JsepTransportController::GetJsepTransportByName(
Zhi Huange830e682018-03-30 17:48:35920 const std::string& transport_name) {
921 auto it = jsep_transports_by_name_.find(transport_name);
922 return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
923}
924
925RTCError JsepTransportController::MaybeCreateJsepTransport(
Anton Sukhanov7940da02018-10-10 17:34:49926 bool local,
Zhi Huange830e682018-03-30 17:48:35927 const cricket::ContentInfo& content_info) {
928 RTC_DCHECK(network_thread_->IsCurrent());
Zhi Huang365381f2018-04-13 23:44:34929 cricket::JsepTransport* transport = GetJsepTransportByName(content_info.name);
Zhi Huange830e682018-03-30 17:48:35930 if (transport) {
931 return RTCError::OK();
932 }
933
934 const cricket::MediaContentDescription* content_desc =
935 static_cast<const cricket::MediaContentDescription*>(
936 content_info.description);
937 if (certificate_ && !content_desc->cryptos().empty()) {
938 return RTCError(RTCErrorType::INVALID_PARAMETER,
939 "SDES and DTLS-SRTP cannot be enabled at the same time.");
940 }
941
Zhi Huange818b6e2018-02-22 23:26:27942 std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
Zhi Huangd2248f82018-04-10 21:41:03943 CreateDtlsTransport(content_info.name, /*rtcp =*/false);
Anton Sukhanov7940da02018-10-10 17:34:49944
Zhi Huange818b6e2018-02-22 23:26:27945 std::unique_ptr<cricket::DtlsTransportInternal> rtcp_dtls_transport;
Anton Sukhanov7940da02018-10-10 17:34:49946 std::unique_ptr<RtpTransport> unencrypted_rtp_transport;
947 std::unique_ptr<SrtpTransport> sdes_transport;
948 std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport;
949 std::unique_ptr<MediaTransportInterface> media_transport;
950
Zhi Huange830e682018-03-30 17:48:35951 if (config_.rtcp_mux_policy !=
952 PeerConnectionInterface::kRtcpMuxPolicyRequire &&
953 content_info.type == cricket::MediaProtocolType::kRtp) {
Zhi Huangd2248f82018-04-10 21:41:03954 rtcp_dtls_transport =
955 CreateDtlsTransport(content_info.name, /*rtcp =*/true);
Zhi Huange818b6e2018-02-22 23:26:27956 }
957
Piotr (Peter) Slatala9f956252018-10-31 15:25:26958 absl::optional<cricket::CryptoParams> selected_crypto_for_media_transport;
959 if (content_info.media_description() &&
960 !content_info.media_description()->cryptos().empty()) {
961 // Order of cryptos is deterministic (rfc4568, 5.1.1), so we just select the
962 // first one (in fact the first one should be the most preferred one.) We
963 // ignore the HMAC size, as media transport crypto settings currently don't
964 // expose HMAC size, nor crypto protocol for that matter.
965 selected_crypto_for_media_transport =
966 content_info.media_description()->cryptos()[0];
967 }
968
Anton Sukhanov7940da02018-10-10 17:34:49969 if (config_.media_transport_factory != nullptr) {
Piotr (Peter) Slatala9f956252018-10-31 15:25:26970 if (!selected_crypto_for_media_transport.has_value()) {
971 RTC_LOG(LS_WARNING) << "a=cryto line was not found in the offer. Most "
972 "likely you did not enable SDES. "
973 "Make sure to pass config.enable_dtls_srtp=false "
974 "to RTCConfiguration. "
975 "Cannot continue with media transport. Falling "
976 "back to RTP. is_local="
977 << local;
Anton Sukhanov7940da02018-10-10 17:34:49978
Piotr (Peter) Slatala9f956252018-10-31 15:25:26979 // Remove media_transport_factory from config, because we don't want to
980 // use it on the subsequent call (for the other side of the offer).
981 config_.media_transport_factory = nullptr;
982 } else {
983 // Note that we ignore here lifetime and length.
984 // In fact we take those bits (inline, lifetime and length) and keep it as
985 // part of key derivation.
986 //
987 // Technically, we are also not following rfc4568, which requires us to
988 // send and answer with the key that we chose. In practice, for media
989 // transport, the current approach should be sufficient (we take the key
990 // that sender offered, and caller assumes we will use it. We are not
991 // signaling back that we indeed used it.)
992 std::unique_ptr<rtc::KeyDerivation> key_derivation =
993 rtc::KeyDerivation::Create(rtc::KeyDerivationAlgorithm::HKDF_SHA256);
994 const std::string label = "MediaTransportLabel";
995 constexpr int kDerivedKeyByteSize = 32;
Anton Sukhanov7940da02018-10-10 17:34:49996
Piotr (Peter) Slatala9f956252018-10-31 15:25:26997 int key_len, salt_len;
998 if (!rtc::GetSrtpKeyAndSaltLengths(
999 rtc::SrtpCryptoSuiteFromName(
1000 selected_crypto_for_media_transport.value().cipher_suite),
1001 &key_len, &salt_len)) {
1002 RTC_CHECK(false) << "Cannot set up secure media transport";
1003 }
1004 rtc::ZeroOnFreeBuffer<uint8_t> raw_key(key_len + salt_len);
1005
1006 cricket::SrtpFilter::ParseKeyParams(
1007 selected_crypto_for_media_transport.value().key_params,
1008 raw_key.data(), raw_key.size());
1009 absl::optional<rtc::ZeroOnFreeBuffer<uint8_t>> key =
1010 key_derivation->DeriveKey(
1011 raw_key,
1012 /*salt=*/nullptr,
1013 rtc::ArrayView<const uint8_t>(
1014 reinterpret_cast<const uint8_t*>(label.data()), label.size()),
1015 kDerivedKeyByteSize);
1016
1017 // We want to crash the app if we don't have a key, and not silently fall
1018 // back to the unsecure communication.
1019 RTC_CHECK(key.has_value());
1020 MediaTransportSettings settings;
1021 settings.is_caller = local;
1022 settings.pre_shared_key =
1023 std::string(reinterpret_cast<const char*>(key.value().data()),
1024 key.value().size());
1025 auto media_transport_result =
1026 config_.media_transport_factory->CreateMediaTransport(
1027 rtp_dtls_transport->ice_transport(), network_thread_, settings);
1028
1029 // TODO(sukhanov): Proper error handling.
1030 RTC_CHECK(media_transport_result.ok());
1031
Piotr (Peter) Slatalacc8e8bb2018-11-15 16:26:191032 RTC_DCHECK(media_transport == nullptr);
Piotr (Peter) Slatala9f956252018-10-31 15:25:261033 media_transport = std::move(media_transport_result.value());
1034 }
Anton Sukhanov7940da02018-10-10 17:34:491035 }
1036
1037 // TODO(sukhanov): Do not create RTP/RTCP transports if media transport is
1038 // used.
Zhi Huange818b6e2018-02-22 23:26:271039 if (config_.disable_encryption) {
1040 unencrypted_rtp_transport = CreateUnencryptedRtpTransport(
Zhi Huangd2248f82018-04-10 21:41:031041 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 23:26:271042 } else if (!content_desc->cryptos().empty()) {
Zhi Huangd2248f82018-04-10 21:41:031043 sdes_transport = CreateSdesTransport(
1044 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 23:26:271045 } else {
Zhi Huangd2248f82018-04-10 21:41:031046 dtls_srtp_transport = CreateDtlsSrtpTransport(
1047 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 23:26:271048 }
1049
Zhi Huang365381f2018-04-13 23:44:341050 std::unique_ptr<cricket::JsepTransport> jsep_transport =
Karl Wiberg918f50c2018-07-05 09:40:331051 absl::make_unique<cricket::JsepTransport>(
Zhi Huangd2248f82018-04-10 21:41:031052 content_info.name, certificate_, std::move(unencrypted_rtp_transport),
Zhi Huange818b6e2018-02-22 23:26:271053 std::move(sdes_transport), std::move(dtls_srtp_transport),
Anton Sukhanov7940da02018-10-10 17:34:491054 std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport),
1055 std::move(media_transport));
Zhi Huange818b6e2018-02-22 23:26:271056 jsep_transport->SignalRtcpMuxActive.connect(
1057 this, &JsepTransportController::UpdateAggregateStates_n);
Piotr (Peter) Slatala4eb41122018-11-01 14:26:031058 jsep_transport->SignalMediaTransportStateChanged.connect(
Bjorn Mellem175aa2e2018-11-08 19:23:221059 this, &JsepTransportController::OnMediaTransportStateChanged_n);
Taylor Brandstettercbaa2542018-04-16 23:42:141060 SetTransportForMid(content_info.name, jsep_transport.get());
Zhi Huange830e682018-03-30 17:48:351061
Zhi Huangd2248f82018-04-10 21:41:031062 jsep_transports_by_name_[content_info.name] = std::move(jsep_transport);
1063 UpdateAggregateStates_n();
Zhi Huange830e682018-03-30 17:48:351064 return RTCError::OK();
Zhi Huange818b6e2018-02-22 23:26:271065}
1066
1067void JsepTransportController::MaybeDestroyJsepTransport(
1068 const std::string& mid) {
Zhi Huangd2248f82018-04-10 21:41:031069 auto jsep_transport = GetJsepTransportByName(mid);
1070 if (!jsep_transport) {
1071 return;
1072 }
1073
1074 // Don't destroy the JsepTransport if there are still media sections referring
1075 // to it.
1076 for (const auto& kv : mid_to_transport_) {
1077 if (kv.second == jsep_transport) {
1078 return;
1079 }
1080 }
Piotr (Peter) Slatalacc8e8bb2018-11-15 16:26:191081
Zhi Huange830e682018-03-30 17:48:351082 jsep_transports_by_name_.erase(mid);
Zhi Huange818b6e2018-02-22 23:26:271083 UpdateAggregateStates_n();
1084}
1085
1086void JsepTransportController::DestroyAllJsepTransports_n() {
1087 RTC_DCHECK(network_thread_->IsCurrent());
Piotr (Peter) Slatalacc8e8bb2018-11-15 16:26:191088
1089 for (const auto& jsep_transport : jsep_transports_by_name_) {
1090 config_.transport_observer->OnTransportChanged(jsep_transport.first,
1091 nullptr, nullptr, nullptr);
1092 }
1093
Zhi Huange830e682018-03-30 17:48:351094 jsep_transports_by_name_.clear();
Zhi Huange818b6e2018-02-22 23:26:271095}
1096
1097void JsepTransportController::SetIceRole_n(cricket::IceRole ice_role) {
1098 RTC_DCHECK(network_thread_->IsCurrent());
1099
1100 ice_role_ = ice_role;
1101 for (auto& dtls : GetDtlsTransports()) {
1102 dtls->ice_transport()->SetIceRole(ice_role_);
1103 }
1104}
1105
1106cricket::IceRole JsepTransportController::DetermineIceRole(
Zhi Huang365381f2018-04-13 23:44:341107 cricket::JsepTransport* jsep_transport,
Zhi Huange818b6e2018-02-22 23:26:271108 const cricket::TransportInfo& transport_info,
1109 SdpType type,
1110 bool local) {
1111 cricket::IceRole ice_role = ice_role_;
1112 auto tdesc = transport_info.description;
1113 if (local) {
1114 // The initial offer side may use ICE Lite, in which case, per RFC5245
1115 // Section 5.1.1, the answer side should take the controlling role if it is
1116 // in the full ICE mode.
1117 //
1118 // When both sides use ICE Lite, the initial offer side must take the
1119 // controlling role, and this is the default logic implemented in
1120 // SetLocalDescription in JsepTransportController.
1121 if (jsep_transport->remote_description() &&
1122 jsep_transport->remote_description()->transport_desc.ice_mode ==
1123 cricket::ICEMODE_LITE &&
1124 ice_role_ == cricket::ICEROLE_CONTROLLED &&
1125 tdesc.ice_mode == cricket::ICEMODE_FULL) {
1126 ice_role = cricket::ICEROLE_CONTROLLING;
1127 }
1128
1129 // Older versions of Chrome expect the ICE role to be re-determined when an
1130 // ICE restart occurs, and also don't perform conflict resolution correctly,
1131 // so for now we can't safely stop doing this, unless the application opts
1132 // in by setting |config_.redetermine_role_on_ice_restart_| to false. See:
1133 // https://bugs.chromium.org/p/chromium/issues/detail?id=628676
1134 // TODO(deadbeef): Remove this when these old versions of Chrome reach a low
1135 // enough population.
1136 if (config_.redetermine_role_on_ice_restart &&
1137 jsep_transport->local_description() &&
1138 cricket::IceCredentialsChanged(
1139 jsep_transport->local_description()->transport_desc.ice_ufrag,
1140 jsep_transport->local_description()->transport_desc.ice_pwd,
1141 tdesc.ice_ufrag, tdesc.ice_pwd) &&
1142 // Don't change the ICE role if the remote endpoint is ICE lite; we
1143 // should always be controlling in that case.
1144 (!jsep_transport->remote_description() ||
1145 jsep_transport->remote_description()->transport_desc.ice_mode !=
1146 cricket::ICEMODE_LITE)) {
1147 ice_role = (type == SdpType::kOffer) ? cricket::ICEROLE_CONTROLLING
1148 : cricket::ICEROLE_CONTROLLED;
1149 }
1150 } else {
1151 // If our role is cricket::ICEROLE_CONTROLLED and the remote endpoint
1152 // supports only ice_lite, this local endpoint should take the CONTROLLING
1153 // role.
1154 // TODO(deadbeef): This is a session-level attribute, so it really shouldn't
1155 // be in a TransportDescription in the first place...
1156 if (ice_role_ == cricket::ICEROLE_CONTROLLED &&
1157 tdesc.ice_mode == cricket::ICEMODE_LITE) {
1158 ice_role = cricket::ICEROLE_CONTROLLING;
1159 }
1160
1161 // If we use ICE Lite and the remote endpoint uses the full implementation
1162 // of ICE, the local endpoint must take the controlled role, and the other
1163 // side must be the controlling role.
1164 if (jsep_transport->local_description() &&
1165 jsep_transport->local_description()->transport_desc.ice_mode ==
1166 cricket::ICEMODE_LITE &&
1167 ice_role_ == cricket::ICEROLE_CONTROLLING &&
Zhi Huange830e682018-03-30 17:48:351168 tdesc.ice_mode == cricket::ICEMODE_FULL) {
Zhi Huange818b6e2018-02-22 23:26:271169 ice_role = cricket::ICEROLE_CONTROLLED;
1170 }
1171 }
1172
1173 return ice_role;
1174}
1175
1176void JsepTransportController::OnTransportWritableState_n(
1177 rtc::PacketTransportInternal* transport) {
1178 RTC_DCHECK(network_thread_->IsCurrent());
1179 RTC_LOG(LS_INFO) << " Transport " << transport->transport_name()
1180 << " writability changed to " << transport->writable()
1181 << ".";
1182 UpdateAggregateStates_n();
1183}
1184
1185void JsepTransportController::OnTransportReceivingState_n(
1186 rtc::PacketTransportInternal* transport) {
1187 RTC_DCHECK(network_thread_->IsCurrent());
1188 UpdateAggregateStates_n();
1189}
1190
1191void JsepTransportController::OnTransportGatheringState_n(
1192 cricket::IceTransportInternal* transport) {
1193 RTC_DCHECK(network_thread_->IsCurrent());
1194 UpdateAggregateStates_n();
1195}
1196
1197void JsepTransportController::OnTransportCandidateGathered_n(
1198 cricket::IceTransportInternal* transport,
1199 const cricket::Candidate& candidate) {
1200 RTC_DCHECK(network_thread_->IsCurrent());
1201
1202 // We should never signal peer-reflexive candidates.
1203 if (candidate.type() == cricket::PRFLX_PORT_TYPE) {
1204 RTC_NOTREACHED();
1205 return;
1206 }
Steve Antond25828a2018-08-31 20:06:051207 std::string transport_name = transport->transport_name();
1208 invoker_.AsyncInvoke<void>(
1209 RTC_FROM_HERE, signaling_thread_, [this, transport_name, candidate] {
1210 SignalIceCandidatesGathered(transport_name, {candidate});
1211 });
Zhi Huange818b6e2018-02-22 23:26:271212}
1213
1214void JsepTransportController::OnTransportCandidatesRemoved_n(
1215 cricket::IceTransportInternal* transport,
1216 const cricket::Candidates& candidates) {
1217 invoker_.AsyncInvoke<void>(
1218 RTC_FROM_HERE, signaling_thread_,
Steve Antond25828a2018-08-31 20:06:051219 [this, candidates] { SignalIceCandidatesRemoved(candidates); });
Zhi Huange818b6e2018-02-22 23:26:271220}
1221
1222void JsepTransportController::OnTransportRoleConflict_n(
1223 cricket::IceTransportInternal* transport) {
1224 RTC_DCHECK(network_thread_->IsCurrent());
1225 // Note: since the role conflict is handled entirely on the network thread,
1226 // we don't need to worry about role conflicts occurring on two ports at
1227 // once. The first one encountered should immediately reverse the role.
1228 cricket::IceRole reversed_role = (ice_role_ == cricket::ICEROLE_CONTROLLING)
1229 ? cricket::ICEROLE_CONTROLLED
1230 : cricket::ICEROLE_CONTROLLING;
1231 RTC_LOG(LS_INFO) << "Got role conflict; switching to "
1232 << (reversed_role == cricket::ICEROLE_CONTROLLING
1233 ? "controlling"
1234 : "controlled")
1235 << " role.";
1236 SetIceRole_n(reversed_role);
1237}
1238
1239void JsepTransportController::OnTransportStateChanged_n(
1240 cricket::IceTransportInternal* transport) {
1241 RTC_DCHECK(network_thread_->IsCurrent());
1242 RTC_LOG(LS_INFO) << transport->transport_name() << " Transport "
1243 << transport->component()
1244 << " state changed. Check if state is complete.";
1245 UpdateAggregateStates_n();
1246}
1247
Bjorn Mellem175aa2e2018-11-08 19:23:221248void JsepTransportController::OnMediaTransportStateChanged_n() {
1249 SignalMediaTransportStateChanged();
1250 UpdateAggregateStates_n();
1251}
1252
Zhi Huange818b6e2018-02-22 23:26:271253void JsepTransportController::UpdateAggregateStates_n() {
1254 RTC_DCHECK(network_thread_->IsCurrent());
1255
1256 auto dtls_transports = GetDtlsTransports();
1257 cricket::IceConnectionState new_connection_state =
1258 cricket::kIceConnectionConnecting;
Jonas Olsson635474e2018-10-18 13:58:171259 PeerConnectionInterface::IceConnectionState new_ice_connection_state =
1260 PeerConnectionInterface::IceConnectionState::kIceConnectionNew;
1261 PeerConnectionInterface::PeerConnectionState new_combined_state =
1262 PeerConnectionInterface::PeerConnectionState::kNew;
Zhi Huange818b6e2018-02-22 23:26:271263 cricket::IceGatheringState new_gathering_state = cricket::kIceGatheringNew;
1264 bool any_failed = false;
Piotr (Peter) Slatala4eb41122018-11-01 14:26:031265
1266 // TODO(http://bugs.webrtc.org/9719) If(when) media_transport disables
1267 // dtls_transports entirely, the below line will have to be changed to account
1268 // for the fact that dtls transports might be absent.
Zhi Huange818b6e2018-02-22 23:26:271269 bool all_connected = !dtls_transports.empty();
1270 bool all_completed = !dtls_transports.empty();
1271 bool any_gathering = false;
1272 bool all_done_gathering = !dtls_transports.empty();
Jonas Olsson635474e2018-10-18 13:58:171273
1274 std::map<IceTransportState, int> ice_state_counts;
1275 std::map<cricket::DtlsTransportState, int> dtls_state_counts;
1276
Zhi Huange818b6e2018-02-22 23:26:271277 for (const auto& dtls : dtls_transports) {
1278 any_failed = any_failed || dtls->ice_transport()->GetState() ==
1279 cricket::IceTransportState::STATE_FAILED;
1280 all_connected = all_connected && dtls->writable();
1281 all_completed =
1282 all_completed && dtls->writable() &&
1283 dtls->ice_transport()->GetState() ==
1284 cricket::IceTransportState::STATE_COMPLETED &&
1285 dtls->ice_transport()->GetIceRole() == cricket::ICEROLE_CONTROLLING &&
1286 dtls->ice_transport()->gathering_state() ==
1287 cricket::kIceGatheringComplete;
1288 any_gathering = any_gathering || dtls->ice_transport()->gathering_state() !=
1289 cricket::kIceGatheringNew;
1290 all_done_gathering =
1291 all_done_gathering && dtls->ice_transport()->gathering_state() ==
1292 cricket::kIceGatheringComplete;
Jonas Olsson635474e2018-10-18 13:58:171293
1294 dtls_state_counts[dtls->dtls_state()]++;
1295 ice_state_counts[dtls->ice_transport()->GetIceTransportState()]++;
Zhi Huange818b6e2018-02-22 23:26:271296 }
Piotr (Peter) Slatala4eb41122018-11-01 14:26:031297
1298 for (auto it = jsep_transports_by_name_.begin();
1299 it != jsep_transports_by_name_.end(); ++it) {
1300 auto jsep_transport = it->second.get();
1301 if (!jsep_transport->media_transport()) {
1302 continue;
1303 }
1304
1305 // There is no 'kIceConnectionDisconnected', so we only need to handle
1306 // connected and completed.
1307 // We treat kClosed as failed, because if it happens before shutting down
1308 // media transports it means that there was a failure.
1309 // MediaTransportInterface allows to flip back and forth between kWritable
1310 // and kPending, but there does not exist an implementation that does that,
1311 // and the contract of jsep transport controller doesn't quite expect that.
1312 // When this happens, we would go from connected to connecting state, but
1313 // this may change in future.
1314 any_failed |= jsep_transport->media_transport_state() ==
1315 webrtc::MediaTransportState::kClosed;
1316 all_completed &= jsep_transport->media_transport_state() ==
1317 webrtc::MediaTransportState::kWritable;
1318 all_connected &= jsep_transport->media_transport_state() ==
1319 webrtc::MediaTransportState::kWritable;
1320 }
1321
Zhi Huange818b6e2018-02-22 23:26:271322 if (any_failed) {
1323 new_connection_state = cricket::kIceConnectionFailed;
1324 } else if (all_completed) {
1325 new_connection_state = cricket::kIceConnectionCompleted;
1326 } else if (all_connected) {
1327 new_connection_state = cricket::kIceConnectionConnected;
1328 }
1329 if (ice_connection_state_ != new_connection_state) {
1330 ice_connection_state_ = new_connection_state;
Steve Antond25828a2018-08-31 20:06:051331 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1332 [this, new_connection_state] {
1333 SignalIceConnectionState(new_connection_state);
1334 });
Zhi Huange818b6e2018-02-22 23:26:271335 }
1336
Jonas Olsson635474e2018-10-18 13:58:171337 // Compute the current RTCIceConnectionState as described in
1338 // https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate.
1339 // The PeerConnection is responsible for handling the "closed" state.
1340 int total_ice_checking = ice_state_counts[IceTransportState::kChecking];
1341 int total_ice_connected = ice_state_counts[IceTransportState::kConnected];
1342 int total_ice_completed = ice_state_counts[IceTransportState::kCompleted];
1343 int total_ice_failed = ice_state_counts[IceTransportState::kFailed];
1344 int total_ice_disconnected =
1345 ice_state_counts[IceTransportState::kDisconnected];
1346 int total_ice_closed = ice_state_counts[IceTransportState::kClosed];
1347 int total_ice_new = ice_state_counts[IceTransportState::kNew];
1348 int total_ice = dtls_transports.size();
1349
1350 if (total_ice_failed > 0) {
1351 // Any of the RTCIceTransports are in the "failed" state.
1352 new_ice_connection_state = PeerConnectionInterface::kIceConnectionFailed;
1353 } else if (total_ice_disconnected > 0) {
1354 // Any of the RTCIceTransports are in the "disconnected" state and none of
1355 // them are in the "failed" state.
1356 new_ice_connection_state =
1357 PeerConnectionInterface::kIceConnectionDisconnected;
1358 } else if (total_ice_checking > 0) {
1359 // Any of the RTCIceTransports are in the "checking" state and none of them
1360 // are in the "disconnected" or "failed" state.
1361 new_ice_connection_state = PeerConnectionInterface::kIceConnectionChecking;
1362 } else if (total_ice_completed + total_ice_closed == total_ice &&
1363 total_ice_completed > 0) {
1364 // All RTCIceTransports are in the "completed" or "closed" state and at
1365 // least one of them is in the "completed" state.
1366 new_ice_connection_state = PeerConnectionInterface::kIceConnectionCompleted;
1367 } else if (total_ice_connected + total_ice_completed + total_ice_closed ==
1368 total_ice &&
1369 total_ice_connected > 0) {
1370 // All RTCIceTransports are in the "connected", "completed" or "closed"
1371 // state and at least one of them is in the "connected" state.
1372 new_ice_connection_state = PeerConnectionInterface::kIceConnectionConnected;
1373 } else if ((total_ice_new > 0 &&
1374 total_ice_checking + total_ice_disconnected + total_ice_failed ==
1375 0) ||
1376 total_ice == total_ice_closed) {
1377 // Any of the RTCIceTransports are in the "new" state and none of them are
1378 // in the "checking", "disconnected" or "failed" state, or all
1379 // RTCIceTransports are in the "closed" state, or there are no transports.
1380 new_ice_connection_state = PeerConnectionInterface::kIceConnectionNew;
1381 } else {
1382 RTC_NOTREACHED();
1383 }
1384
1385 if (standardized_ice_connection_state_ != new_ice_connection_state) {
1386 standardized_ice_connection_state_ = new_ice_connection_state;
1387 invoker_.AsyncInvoke<void>(
1388 RTC_FROM_HERE, signaling_thread_, [this, new_ice_connection_state] {
1389 SignalStandardizedIceConnectionState(new_ice_connection_state);
1390 });
1391 }
1392
1393 // Compute the current RTCPeerConnectionState as described in
1394 // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnectionstate.
1395 // The PeerConnection is responsible for handling the "closed" state.
1396 // Note that "connecting" is only a valid state for DTLS transports while
1397 // "checking", "completed" and "disconnected" are only valid for ICE
1398 // transports.
1399 int total_connected = total_ice_connected +
1400 dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTED];
1401 int total_dtls_connecting =
1402 dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTING];
1403 int total_failed =
1404 total_ice_failed + dtls_state_counts[cricket::DTLS_TRANSPORT_FAILED];
1405 int total_closed =
1406 total_ice_closed + dtls_state_counts[cricket::DTLS_TRANSPORT_CLOSED];
1407 int total_new =
1408 total_ice_new + dtls_state_counts[cricket::DTLS_TRANSPORT_NEW];
1409 int total_transports = total_ice * 2;
1410
1411 if (total_failed > 0) {
1412 // Any of the RTCIceTransports or RTCDtlsTransports are in a "failed" state.
1413 new_combined_state = PeerConnectionInterface::PeerConnectionState::kFailed;
1414 } else if (total_ice_disconnected > 0 &&
1415 total_dtls_connecting + total_ice_checking == 0) {
1416 // Any of the RTCIceTransports or RTCDtlsTransports are in the
1417 // "disconnected" state and none of them are in the "failed" or "connecting"
1418 // or "checking" state.
1419 new_combined_state =
1420 PeerConnectionInterface::PeerConnectionState::kDisconnected;
1421 } else if (total_dtls_connecting + total_ice_checking > 0) {
1422 // Any of the RTCIceTransports or RTCDtlsTransports are in the "connecting"
1423 // or "checking" state and none of them is in the "failed" state.
1424 new_combined_state =
1425 PeerConnectionInterface::PeerConnectionState::kConnecting;
1426 } else if (total_connected + total_ice_completed + total_closed ==
1427 total_transports &&
1428 total_connected + total_ice_completed > 0) {
1429 // All RTCIceTransports and RTCDtlsTransports are in the "connected",
1430 // "completed" or "closed" state and at least one of them is in the
1431 // "connected" or "completed" state.
1432 new_combined_state =
1433 PeerConnectionInterface::PeerConnectionState::kConnected;
1434 } else if ((total_new > 0 && total_dtls_connecting + total_ice_checking +
1435 total_failed + total_ice_disconnected ==
1436 0) ||
1437 total_transports == total_closed) {
1438 // Any of the RTCIceTransports or RTCDtlsTransports are in the "new" state
1439 // and none of the transports are in the "connecting", "checking", "failed"
1440 // or "disconnected" state, or all transports are in the "closed" state, or
1441 // there are no transports.
1442 //
1443 // Note that if none of the other conditions hold this is guaranteed to be
1444 // true.
1445 new_combined_state = PeerConnectionInterface::PeerConnectionState::kNew;
1446 } else {
1447 RTC_NOTREACHED();
1448 }
1449
1450 if (combined_connection_state_ != new_combined_state) {
1451 combined_connection_state_ = new_combined_state;
1452 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1453 [this, new_combined_state] {
1454 SignalConnectionState(new_combined_state);
1455 });
1456 }
1457
Zhi Huange818b6e2018-02-22 23:26:271458 if (all_done_gathering) {
1459 new_gathering_state = cricket::kIceGatheringComplete;
1460 } else if (any_gathering) {
1461 new_gathering_state = cricket::kIceGatheringGathering;
1462 }
1463 if (ice_gathering_state_ != new_gathering_state) {
1464 ice_gathering_state_ = new_gathering_state;
Steve Antond25828a2018-08-31 20:06:051465 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1466 [this, new_gathering_state] {
1467 SignalIceGatheringState(new_gathering_state);
1468 });
Zhi Huange818b6e2018-02-22 23:26:271469 }
1470}
1471
1472void JsepTransportController::OnDtlsHandshakeError(
1473 rtc::SSLHandshakeError error) {
1474 SignalDtlsHandshakeError(error);
1475}
1476
1477} // namespace webrtc