blob: 9457ed7893328b7b96f8c2b890788bd286d867a1 [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
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 08:25:26 -070018#include "pc/srtpfilter.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080019#include "rtc_base/bind.h"
20#include "rtc_base/checks.h"
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -070021#include "rtc_base/key_derivation.h"
Zhi Huange818b6e2018-02-22 15:26:27 -080022#include "rtc_base/thread.h"
23
24using webrtc::SdpType;
25
26namespace {
27
Zhi Huange818b6e2018-02-22 15:26:27 -080028webrtc::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 13:20:15 -070079 AsyncResolverFactory* async_resolver_factory,
Zhi Huange818b6e2018-02-22 15:26:27 -080080 Config config)
81 : signaling_thread_(signaling_thread),
82 network_thread_(network_thread),
83 port_allocator_(port_allocator),
Zach Steine20867f2018-08-02 13:20:15 -070084 async_resolver_factory_(async_resolver_factory),
Zhi Huang365381f2018-04-13 16:44:34 -070085 config_(config) {
86 // The |transport_observer| is assumed to be non-null.
87 RTC_DCHECK(config_.transport_observer);
88}
Zhi Huange818b6e2018-02-22 15:26:27 -080089
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 10:48:35 -0700130 auto jsep_transport = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 15:26:27 -0800131 if (!jsep_transport) {
132 return nullptr;
133 }
134 return jsep_transport->rtp_transport();
135}
136
Anton Sukhanov7940da02018-10-10 10:34:49 -0700137MediaTransportInterface* 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 11:23:22 -0800146MediaTransportState 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 15:26:27 -0800155cricket::DtlsTransportInternal* JsepTransportController::GetDtlsTransport(
156 const std::string& mid) const {
Zhi Huange830e682018-03-30 10:48:35 -0700157 auto jsep_transport = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 15:26:27 -0800158 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 10:48:35 -0700166 auto jsep_transport = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 15:26:27 -0800167 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 10:48:35 -0700186 for (auto& kv : jsep_transports_by_name_) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800187 kv.second->SetNeedsIceRestartFlag();
188 }
189}
190
191bool JsepTransportController::NeedsIceRestart(
192 const std::string& transport_name) const {
Zhi Huang365381f2018-04-13 16:44:34 -0700193 const cricket::JsepTransport* transport =
Zhi Huange830e682018-03-30 10:48:35 -0700194 GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800195 if (!transport) {
196 return false;
197 }
198 return transport->needs_ice_restart();
199}
200
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200201absl::optional<rtc::SSLRole> JsepTransportController::GetDtlsRole(
Zhi Huange830e682018-03-30 10:48:35 -0700202 const std::string& mid) const {
Zhi Huange818b6e2018-02-22 15:26:27 -0800203 if (!network_thread_->IsCurrent()) {
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200204 return network_thread_->Invoke<absl::optional<rtc::SSLRole>>(
Zhi Huange830e682018-03-30 10:48:35 -0700205 RTC_FROM_HERE, [&] { return GetDtlsRole(mid); });
Zhi Huange818b6e2018-02-22 15:26:27 -0800206 }
207
Zhi Huang365381f2018-04-13 16:44:34 -0700208 const cricket::JsepTransport* t = GetJsepTransportForMid(mid);
Zhi Huange818b6e2018-02-22 15:26:27 -0800209 if (!t) {
Danil Chapovalov66cadcc2018-06-19 16:47:43 +0200210 return absl::optional<rtc::SSLRole>();
Zhi Huange818b6e2018-02-22 15:26:27 -0800211 }
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 10:48:35 -0700231 for (auto& kv : jsep_transports_by_name_) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800232 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 16:44:34 -0700249 const cricket::JsepTransport* t = GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800250 if (!t) {
251 return nullptr;
252 }
253 return t->GetLocalCertificate();
254}
255
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800256std::unique_ptr<rtc::SSLCertChain>
257JsepTransportController::GetRemoteSSLCertChain(
Zhi Huange818b6e2018-02-22 15:26:27 -0800258 const std::string& transport_name) const {
259 if (!network_thread_->IsCurrent()) {
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800260 return network_thread_->Invoke<std::unique_ptr<rtc::SSLCertChain>>(
261 RTC_FROM_HERE, [&] { return GetRemoteSSLCertChain(transport_name); });
Zhi Huange818b6e2018-02-22 15:26:27 -0800262 }
263
Zhi Huange830e682018-03-30 10:48:35 -0700264 // 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 15:26:27 -0800266 // fingerprint.
Zhi Huange830e682018-03-30 10:48:35 -0700267 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 15:26:27 -0800272 if (!dtls) {
273 return nullptr;
274 }
275
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800276 return dtls->GetRemoteSSLCertChain();
Zhi Huange818b6e2018-02-22 15:26:27 -0800277}
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 10:48:35 -0700305 auto jsep_transport = GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800306 if (!jsep_transport) {
Zhi Huange830e682018-03-30 10:48:35 -0700307 RTC_LOG(LS_WARNING) << "Not adding candidate because the JsepTransport "
308 "doesn't exist. Ignore it.";
309 return RTCError::OK();
Zhi Huange818b6e2018-02-22 15:26:27 -0800310 }
Zhi Huange818b6e2018-02-22 15:26:27 -0800311 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 16:44:34 -0700341 cricket::JsepTransport* jsep_transport =
Zhi Huange830e682018-03-30 10:48:35 -0700342 GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800343 if (!jsep_transport) {
Zhi Huange830e682018-03-30 10:48:35 -0700344 RTC_LOG(LS_WARNING)
345 << "Not removing candidate because the JsepTransport doesn't exist.";
346 continue;
Zhi Huange818b6e2018-02-22 15:26:27 -0800347 }
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 16:44:34 -0700367 cricket::JsepTransport* transport = GetJsepTransportByName(transport_name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800368 if (!transport) {
369 return false;
370 }
371 return transport->GetStats(stats);
372}
373
Zhi Huangb57e1692018-06-12 11:41:11 -0700374void 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 12:57:59 -0700392void 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 15:26:27 -0800401std::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 11:40:33 +0200415 auto ice = absl::make_unique<cricket::P2PTransportChannel>(
Zach Steine20867f2018-08-02 13:20:15 -0700416 transport_name, component, port_allocator_, async_resolver_factory_,
417 config_.event_log);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200418 dtls = absl::make_unique<cricket::DtlsTransport>(std::move(ice),
419 config_.crypto_options);
Zhi Huange818b6e2018-02-22 15:26:27 -0800420 }
421
422 RTC_DCHECK(dtls);
423 dtls->SetSslMaxProtocolVersion(config_.ssl_max_version);
Zhi Huange818b6e2018-02-22 15:26:27 -0800424 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 10:48:35 -0700458 auto unencrypted_rtp_transport =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200459 absl::make_unique<RtpTransport>(rtcp_packet_transport == nullptr);
Zhi Huange830e682018-03-30 10:48:35 -0700460 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 15:26:27 -0800465}
466
467std::unique_ptr<webrtc::SrtpTransport>
468JsepTransportController::CreateSdesTransport(
469 const std::string& transport_name,
Zhi Huange830e682018-03-30 10:48:35 -0700470 cricket::DtlsTransportInternal* rtp_dtls_transport,
471 cricket::DtlsTransportInternal* rtcp_dtls_transport) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800472 RTC_DCHECK(network_thread_->IsCurrent());
Zhi Huange818b6e2018-02-22 15:26:27 -0800473 auto srtp_transport =
Karl Wiberg918f50c2018-07-05 11:40:33 +0200474 absl::make_unique<webrtc::SrtpTransport>(rtcp_dtls_transport == nullptr);
Zhi Huange830e682018-03-30 10:48:35 -0700475 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 15:26:27 -0800479 }
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 11:40:33 +0200492 auto dtls_srtp_transport = absl::make_unique<webrtc::DtlsSrtpTransport>(
Zhi Huang365381f2018-04-13 16:44:34 -0700493 rtcp_dtls_transport == nullptr);
Zhi Huang27f3bf52018-03-26 21:37:23 -0700494 if (config_.enable_external_auth) {
Zhi Huang365381f2018-04-13 16:44:34 -0700495 dtls_srtp_transport->EnableExternalAuth();
Zhi Huang27f3bf52018-03-26 21:37:23 -0700496 }
Zhi Huang97d5e5b2018-03-27 00:09:01 +0000497
Zhi Huange818b6e2018-02-22 15:26:27 -0800498 dtls_srtp_transport->SetDtlsTransports(rtp_dtls_transport,
499 rtcp_dtls_transport);
Zhi Huangb57e1692018-06-12 11:41:11 -0700500 dtls_srtp_transport->SetActiveResetSrtpParams(
501 config_.active_reset_srtp_params);
Jonas Olsson635474e2018-10-18 15:58:17 +0200502 dtls_srtp_transport->SignalDtlsStateChange.connect(
503 this, &JsepTransportController::UpdateAggregateStates_n);
Zhi Huange818b6e2018-02-22 15:26:27 -0800504 return dtls_srtp_transport;
505}
506
507std::vector<cricket::DtlsTransportInternal*>
508JsepTransportController::GetDtlsTransports() {
509 std::vector<cricket::DtlsTransportInternal*> dtls_transports;
Zhi Huange830e682018-03-30 10:48:35 -0700510 for (auto it = jsep_transports_by_name_.begin();
511 it != jsep_transports_by_name_.end(); ++it) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800512 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 15:26:27 -0800525RTCError 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 10:48:35 -0700538 RTCError error;
Zhi Huangd2248f82018-04-10 14:41:03 -0700539 error = ValidateAndMaybeUpdateBundleGroup(local, type, description);
Zhi Huange830e682018-03-30 10:48:35 -0700540 if (!error.ok()) {
541 return error;
Zhi Huange818b6e2018-02-22 15:26:27 -0800542 }
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 10:34:49 -0700556 error = MaybeCreateJsepTransport(local, content_info);
Zhi Huange830e682018-03-30 10:48:35 -0700557 if (!error.ok()) {
558 return error;
559 }
Zhi Huange818b6e2018-02-22 15:26:27 -0800560 }
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 16:42:14 -0700569 HandleRejectedContent(content_info, description);
Zhi Huange818b6e2018-02-22 15:26:27 -0800570 continue;
571 }
572
573 if (IsBundled(content_info.name) && content_info.name != *bundled_mid()) {
Zhi Huang365381f2018-04-13 16:44:34 -0700574 if (!HandleBundledContent(content_info)) {
575 return RTCError(RTCErrorType::INVALID_PARAMETER,
576 "Failed to process the bundled m= section.");
577 }
Zhi Huange818b6e2018-02-22 15:26:27 -0800578 continue;
579 }
580
Zhi Huange830e682018-03-30 10:48:35 -0700581 error = ValidateContent(content_info);
582 if (!error.ok()) {
583 return error;
584 }
585
Zhi Huange818b6e2018-02-22 15:26:27 -0800586 std::vector<int> extension_ids;
Taylor Brandstetter0ab56512018-04-12 10:30:48 -0700587 if (bundled_mid() && content_info.name == *bundled_mid()) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800588 extension_ids = merged_encrypted_extension_ids;
589 } else {
590 extension_ids = GetEncryptedHeaderExtensionIds(content_info);
591 }
592
Zhi Huange830e682018-03-30 10:48:35 -0700593 int rtp_abs_sendtime_extn_id =
594 GetRtpAbsSendTimeHeaderExtensionId(content_info);
595
Zhi Huang365381f2018-04-13 16:44:34 -0700596 cricket::JsepTransport* transport =
Zhi Huange830e682018-03-30 10:48:35 -0700597 GetJsepTransportForMid(content_info.name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800598 RTC_DCHECK(transport);
599
600 SetIceRole_n(DetermineIceRole(transport, transport_info, type, local));
601
Zhi Huange818b6e2018-02-22 15:26:27 -0800602 cricket::JsepTransportDescription jsep_description =
603 CreateJsepTransportDescription(content_info, transport_info,
Zhi Huange830e682018-03-30 10:48:35 -0700604 extension_ids, rtp_abs_sendtime_extn_id);
Zhi Huange818b6e2018-02-22 15:26:27 -0800605 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 14:41:03 -0700622RTCError JsepTransportController::ValidateAndMaybeUpdateBundleGroup(
623 bool local,
624 SdpType type,
Zhi Huange830e682018-03-30 10:48:35 -0700625 const cricket::SessionDescription* description) {
626 RTC_DCHECK(description);
Zhi Huangd2248f82018-04-10 14:41:03 -0700627 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 10:30:48 -0700683 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 14:41:03 -0700686 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 10:48:35 -0700692
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 16:42:14 -0700734void JsepTransportController::HandleRejectedContent(
Zhi Huangd2248f82018-04-10 14:41:03 -0700735 const cricket::ContentInfo& content_info,
736 const cricket::SessionDescription* description) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800737 // If the content is rejected, let the
738 // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first,
Zhi Huang365381f2018-04-13 16:44:34 -0700739 // then destroy the cricket::JsepTransport.
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700740 RemoveTransportForMid(content_info.name);
Zhi Huange830e682018-03-30 10:48:35 -0700741 if (content_info.name == bundled_mid()) {
742 for (auto content_name : bundle_group_->content_names()) {
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700743 RemoveTransportForMid(content_name);
Zhi Huange830e682018-03-30 10:48:35 -0700744 }
745 bundle_group_.reset();
746 } else if (IsBundled(content_info.name)) {
747 // Remove the rejected content from the |bundle_group_|.
Zhi Huange818b6e2018-02-22 15:26:27 -0800748 bundle_group_->RemoveContentName(content_info.name);
Zhi Huange830e682018-03-30 10:48:35 -0700749 // Reset the bundle group if nothing left.
750 if (!bundle_group_->FirstContentName()) {
751 bundle_group_.reset();
752 }
Zhi Huange818b6e2018-02-22 15:26:27 -0800753 }
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700754 MaybeDestroyJsepTransport(content_info.name);
Zhi Huange818b6e2018-02-22 15:26:27 -0800755}
756
Zhi Huang365381f2018-04-13 16:44:34 -0700757bool JsepTransportController::HandleBundledContent(
Zhi Huange818b6e2018-02-22 15:26:27 -0800758 const cricket::ContentInfo& content_info) {
Zhi Huangd2248f82018-04-10 14:41:03 -0700759 auto jsep_transport = GetJsepTransportByName(*bundled_mid());
760 RTC_DCHECK(jsep_transport);
Zhi Huange818b6e2018-02-22 15:26:27 -0800761 // If the content is bundled, let the
762 // BaseChannel/SctpTransport change the RtpTransport/DtlsTransport first,
Zhi Huang365381f2018-04-13 16:44:34 -0700763 // then destroy the cricket::JsepTransport.
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700764 if (SetTransportForMid(content_info.name, jsep_transport)) {
Piotr (Peter) Slatala10aeb2a2018-11-14 10:57:24 -0800765 // 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 16:44:34 -0700769 MaybeDestroyJsepTransport(content_info.name);
770 return true;
771 }
772 return false;
Zhi Huange818b6e2018-02-22 15:26:27 -0800773}
774
Zhi Huang365381f2018-04-13 16:44:34 -0700775bool JsepTransportController::SetTransportForMid(
Zhi Huangd2248f82018-04-10 14:41:03 -0700776 const std::string& mid,
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700777 cricket::JsepTransport* jsep_transport) {
Zhi Huang365381f2018-04-13 16:44:34 -0700778 RTC_DCHECK(jsep_transport);
Zhi Huangd2248f82018-04-10 14:41:03 -0700779 if (mid_to_transport_[mid] == jsep_transport) {
Zhi Huang365381f2018-04-13 16:44:34 -0700780 return true;
Zhi Huangd2248f82018-04-10 14:41:03 -0700781 }
782
783 mid_to_transport_[mid] = jsep_transport;
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700784 return config_.transport_observer->OnTransportChanged(
785 mid, jsep_transport->rtp_transport(),
786 jsep_transport->rtp_dtls_transport());
Zhi Huangd2248f82018-04-10 14:41:03 -0700787}
788
Taylor Brandstettercbaa2542018-04-16 16:42:14 -0700789void JsepTransportController::RemoveTransportForMid(const std::string& mid) {
790 bool ret =
791 config_.transport_observer->OnTransportChanged(mid, nullptr, nullptr);
792 // 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 14:41:03 -0700795 mid_to_transport_.erase(mid);
796}
797
Zhi Huange818b6e2018-02-22 15:26:27 -0800798cricket::JsepTransportDescription
799JsepTransportController::CreateJsepTransportDescription(
800 cricket::ContentInfo content_info,
801 cricket::TransportInfo transport_info,
Zhi Huange830e682018-03-30 10:48:35 -0700802 const std::vector<int>& encrypted_extension_ids,
803 int rtp_abs_sendtime_extn_id) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800804 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 10:48:35 -0700814 rtp_abs_sendtime_extn_id, transport_info.description);
Zhi Huange818b6e2018-02-22 15:26:27 -0800815}
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 15:33:17 -0700843 if (!config_.crypto_options.srtp.enable_encrypted_rtp_header_extensions) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800844 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 10:48:35 -0700884int JsepTransportController::GetRtpAbsSendTimeHeaderExtensionId(
Zhi Huange818b6e2018-02-22 15:26:27 -0800885 const cricket::ContentInfo& content_info) {
Zhi Huange830e682018-03-30 10:48:35 -0700886 if (!config_.enable_external_auth) {
887 return -1;
Zhi Huange818b6e2018-02-22 15:26:27 -0800888 }
889
890 const cricket::MediaContentDescription* content_desc =
891 static_cast<const cricket::MediaContentDescription*>(
892 content_info.description);
Zhi Huange830e682018-03-30 10:48:35 -0700893
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 16:44:34 -0700901const cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid(
Zhi Huange830e682018-03-30 10:48:35 -0700902 const std::string& mid) const {
Zhi Huangd2248f82018-04-10 14:41:03 -0700903 auto it = mid_to_transport_.find(mid);
904 return it == mid_to_transport_.end() ? nullptr : it->second;
Zhi Huange830e682018-03-30 10:48:35 -0700905}
906
Zhi Huang365381f2018-04-13 16:44:34 -0700907cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid(
Zhi Huange830e682018-03-30 10:48:35 -0700908 const std::string& mid) {
Zhi Huangd2248f82018-04-10 14:41:03 -0700909 auto it = mid_to_transport_.find(mid);
910 return it == mid_to_transport_.end() ? nullptr : it->second;
Zhi Huange830e682018-03-30 10:48:35 -0700911}
912
Zhi Huang365381f2018-04-13 16:44:34 -0700913const cricket::JsepTransport* JsepTransportController::GetJsepTransportByName(
Zhi Huange830e682018-03-30 10:48:35 -0700914 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 16:44:34 -0700919cricket::JsepTransport* JsepTransportController::GetJsepTransportByName(
Zhi Huange830e682018-03-30 10:48:35 -0700920 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 10:34:49 -0700926 bool local,
Zhi Huange830e682018-03-30 10:48:35 -0700927 const cricket::ContentInfo& content_info) {
928 RTC_DCHECK(network_thread_->IsCurrent());
Zhi Huang365381f2018-04-13 16:44:34 -0700929 cricket::JsepTransport* transport = GetJsepTransportByName(content_info.name);
Zhi Huange830e682018-03-30 10:48:35 -0700930 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 15:26:27 -0800942 std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
Zhi Huangd2248f82018-04-10 14:41:03 -0700943 CreateDtlsTransport(content_info.name, /*rtcp =*/false);
Anton Sukhanov7940da02018-10-10 10:34:49 -0700944
Zhi Huange818b6e2018-02-22 15:26:27 -0800945 std::unique_ptr<cricket::DtlsTransportInternal> rtcp_dtls_transport;
Anton Sukhanov7940da02018-10-10 10:34:49 -0700946 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 10:48:35 -0700951 if (config_.rtcp_mux_policy !=
952 PeerConnectionInterface::kRtcpMuxPolicyRequire &&
953 content_info.type == cricket::MediaProtocolType::kRtp) {
Zhi Huangd2248f82018-04-10 14:41:03 -0700954 rtcp_dtls_transport =
955 CreateDtlsTransport(content_info.name, /*rtcp =*/true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800956 }
957
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700958 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 10:34:49 -0700969 if (config_.media_transport_factory != nullptr) {
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700970 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 10:34:49 -0700978
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700979 // 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 10:34:49 -0700996
Piotr (Peter) Slatala9f956252018-10-31 08:25:26 -0700997 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
1032 media_transport = std::move(media_transport_result.value());
1033 }
Anton Sukhanov7940da02018-10-10 10:34:49 -07001034 }
1035
1036 // TODO(sukhanov): Do not create RTP/RTCP transports if media transport is
1037 // used.
Zhi Huange818b6e2018-02-22 15:26:27 -08001038 if (config_.disable_encryption) {
1039 unencrypted_rtp_transport = CreateUnencryptedRtpTransport(
Zhi Huangd2248f82018-04-10 14:41:03 -07001040 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 15:26:27 -08001041 } else if (!content_desc->cryptos().empty()) {
Zhi Huangd2248f82018-04-10 14:41:03 -07001042 sdes_transport = CreateSdesTransport(
1043 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 15:26:27 -08001044 } else {
Zhi Huangd2248f82018-04-10 14:41:03 -07001045 dtls_srtp_transport = CreateDtlsSrtpTransport(
1046 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 15:26:27 -08001047 }
1048
Zhi Huang365381f2018-04-13 16:44:34 -07001049 std::unique_ptr<cricket::JsepTransport> jsep_transport =
Karl Wiberg918f50c2018-07-05 11:40:33 +02001050 absl::make_unique<cricket::JsepTransport>(
Zhi Huangd2248f82018-04-10 14:41:03 -07001051 content_info.name, certificate_, std::move(unencrypted_rtp_transport),
Zhi Huange818b6e2018-02-22 15:26:27 -08001052 std::move(sdes_transport), std::move(dtls_srtp_transport),
Anton Sukhanov7940da02018-10-10 10:34:49 -07001053 std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport),
1054 std::move(media_transport));
Zhi Huange818b6e2018-02-22 15:26:27 -08001055 jsep_transport->SignalRtcpMuxActive.connect(
1056 this, &JsepTransportController::UpdateAggregateStates_n);
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -07001057 jsep_transport->SignalMediaTransportStateChanged.connect(
Bjorn Mellem175aa2e2018-11-08 11:23:22 -08001058 this, &JsepTransportController::OnMediaTransportStateChanged_n);
Taylor Brandstettercbaa2542018-04-16 16:42:14 -07001059 SetTransportForMid(content_info.name, jsep_transport.get());
Zhi Huange830e682018-03-30 10:48:35 -07001060
Zhi Huangd2248f82018-04-10 14:41:03 -07001061 jsep_transports_by_name_[content_info.name] = std::move(jsep_transport);
1062 UpdateAggregateStates_n();
Zhi Huange830e682018-03-30 10:48:35 -07001063 return RTCError::OK();
Zhi Huange818b6e2018-02-22 15:26:27 -08001064}
1065
1066void JsepTransportController::MaybeDestroyJsepTransport(
1067 const std::string& mid) {
Zhi Huangd2248f82018-04-10 14:41:03 -07001068 auto jsep_transport = GetJsepTransportByName(mid);
1069 if (!jsep_transport) {
1070 return;
1071 }
1072
1073 // Don't destroy the JsepTransport if there are still media sections referring
1074 // to it.
1075 for (const auto& kv : mid_to_transport_) {
1076 if (kv.second == jsep_transport) {
1077 return;
1078 }
1079 }
Zhi Huange830e682018-03-30 10:48:35 -07001080 jsep_transports_by_name_.erase(mid);
Zhi Huange818b6e2018-02-22 15:26:27 -08001081 UpdateAggregateStates_n();
1082}
1083
1084void JsepTransportController::DestroyAllJsepTransports_n() {
1085 RTC_DCHECK(network_thread_->IsCurrent());
Zhi Huange830e682018-03-30 10:48:35 -07001086 jsep_transports_by_name_.clear();
Zhi Huange818b6e2018-02-22 15:26:27 -08001087}
1088
1089void JsepTransportController::SetIceRole_n(cricket::IceRole ice_role) {
1090 RTC_DCHECK(network_thread_->IsCurrent());
1091
1092 ice_role_ = ice_role;
1093 for (auto& dtls : GetDtlsTransports()) {
1094 dtls->ice_transport()->SetIceRole(ice_role_);
1095 }
1096}
1097
1098cricket::IceRole JsepTransportController::DetermineIceRole(
Zhi Huang365381f2018-04-13 16:44:34 -07001099 cricket::JsepTransport* jsep_transport,
Zhi Huange818b6e2018-02-22 15:26:27 -08001100 const cricket::TransportInfo& transport_info,
1101 SdpType type,
1102 bool local) {
1103 cricket::IceRole ice_role = ice_role_;
1104 auto tdesc = transport_info.description;
1105 if (local) {
1106 // The initial offer side may use ICE Lite, in which case, per RFC5245
1107 // Section 5.1.1, the answer side should take the controlling role if it is
1108 // in the full ICE mode.
1109 //
1110 // When both sides use ICE Lite, the initial offer side must take the
1111 // controlling role, and this is the default logic implemented in
1112 // SetLocalDescription in JsepTransportController.
1113 if (jsep_transport->remote_description() &&
1114 jsep_transport->remote_description()->transport_desc.ice_mode ==
1115 cricket::ICEMODE_LITE &&
1116 ice_role_ == cricket::ICEROLE_CONTROLLED &&
1117 tdesc.ice_mode == cricket::ICEMODE_FULL) {
1118 ice_role = cricket::ICEROLE_CONTROLLING;
1119 }
1120
1121 // Older versions of Chrome expect the ICE role to be re-determined when an
1122 // ICE restart occurs, and also don't perform conflict resolution correctly,
1123 // so for now we can't safely stop doing this, unless the application opts
1124 // in by setting |config_.redetermine_role_on_ice_restart_| to false. See:
1125 // https://bugs.chromium.org/p/chromium/issues/detail?id=628676
1126 // TODO(deadbeef): Remove this when these old versions of Chrome reach a low
1127 // enough population.
1128 if (config_.redetermine_role_on_ice_restart &&
1129 jsep_transport->local_description() &&
1130 cricket::IceCredentialsChanged(
1131 jsep_transport->local_description()->transport_desc.ice_ufrag,
1132 jsep_transport->local_description()->transport_desc.ice_pwd,
1133 tdesc.ice_ufrag, tdesc.ice_pwd) &&
1134 // Don't change the ICE role if the remote endpoint is ICE lite; we
1135 // should always be controlling in that case.
1136 (!jsep_transport->remote_description() ||
1137 jsep_transport->remote_description()->transport_desc.ice_mode !=
1138 cricket::ICEMODE_LITE)) {
1139 ice_role = (type == SdpType::kOffer) ? cricket::ICEROLE_CONTROLLING
1140 : cricket::ICEROLE_CONTROLLED;
1141 }
1142 } else {
1143 // If our role is cricket::ICEROLE_CONTROLLED and the remote endpoint
1144 // supports only ice_lite, this local endpoint should take the CONTROLLING
1145 // role.
1146 // TODO(deadbeef): This is a session-level attribute, so it really shouldn't
1147 // be in a TransportDescription in the first place...
1148 if (ice_role_ == cricket::ICEROLE_CONTROLLED &&
1149 tdesc.ice_mode == cricket::ICEMODE_LITE) {
1150 ice_role = cricket::ICEROLE_CONTROLLING;
1151 }
1152
1153 // If we use ICE Lite and the remote endpoint uses the full implementation
1154 // of ICE, the local endpoint must take the controlled role, and the other
1155 // side must be the controlling role.
1156 if (jsep_transport->local_description() &&
1157 jsep_transport->local_description()->transport_desc.ice_mode ==
1158 cricket::ICEMODE_LITE &&
1159 ice_role_ == cricket::ICEROLE_CONTROLLING &&
Zhi Huange830e682018-03-30 10:48:35 -07001160 tdesc.ice_mode == cricket::ICEMODE_FULL) {
Zhi Huange818b6e2018-02-22 15:26:27 -08001161 ice_role = cricket::ICEROLE_CONTROLLED;
1162 }
1163 }
1164
1165 return ice_role;
1166}
1167
1168void JsepTransportController::OnTransportWritableState_n(
1169 rtc::PacketTransportInternal* transport) {
1170 RTC_DCHECK(network_thread_->IsCurrent());
1171 RTC_LOG(LS_INFO) << " Transport " << transport->transport_name()
1172 << " writability changed to " << transport->writable()
1173 << ".";
1174 UpdateAggregateStates_n();
1175}
1176
1177void JsepTransportController::OnTransportReceivingState_n(
1178 rtc::PacketTransportInternal* transport) {
1179 RTC_DCHECK(network_thread_->IsCurrent());
1180 UpdateAggregateStates_n();
1181}
1182
1183void JsepTransportController::OnTransportGatheringState_n(
1184 cricket::IceTransportInternal* transport) {
1185 RTC_DCHECK(network_thread_->IsCurrent());
1186 UpdateAggregateStates_n();
1187}
1188
1189void JsepTransportController::OnTransportCandidateGathered_n(
1190 cricket::IceTransportInternal* transport,
1191 const cricket::Candidate& candidate) {
1192 RTC_DCHECK(network_thread_->IsCurrent());
1193
1194 // We should never signal peer-reflexive candidates.
1195 if (candidate.type() == cricket::PRFLX_PORT_TYPE) {
1196 RTC_NOTREACHED();
1197 return;
1198 }
Steve Antond25828a2018-08-31 13:06:05 -07001199 std::string transport_name = transport->transport_name();
1200 invoker_.AsyncInvoke<void>(
1201 RTC_FROM_HERE, signaling_thread_, [this, transport_name, candidate] {
1202 SignalIceCandidatesGathered(transport_name, {candidate});
1203 });
Zhi Huange818b6e2018-02-22 15:26:27 -08001204}
1205
1206void JsepTransportController::OnTransportCandidatesRemoved_n(
1207 cricket::IceTransportInternal* transport,
1208 const cricket::Candidates& candidates) {
1209 invoker_.AsyncInvoke<void>(
1210 RTC_FROM_HERE, signaling_thread_,
Steve Antond25828a2018-08-31 13:06:05 -07001211 [this, candidates] { SignalIceCandidatesRemoved(candidates); });
Zhi Huange818b6e2018-02-22 15:26:27 -08001212}
1213
1214void JsepTransportController::OnTransportRoleConflict_n(
1215 cricket::IceTransportInternal* transport) {
1216 RTC_DCHECK(network_thread_->IsCurrent());
1217 // Note: since the role conflict is handled entirely on the network thread,
1218 // we don't need to worry about role conflicts occurring on two ports at
1219 // once. The first one encountered should immediately reverse the role.
1220 cricket::IceRole reversed_role = (ice_role_ == cricket::ICEROLE_CONTROLLING)
1221 ? cricket::ICEROLE_CONTROLLED
1222 : cricket::ICEROLE_CONTROLLING;
1223 RTC_LOG(LS_INFO) << "Got role conflict; switching to "
1224 << (reversed_role == cricket::ICEROLE_CONTROLLING
1225 ? "controlling"
1226 : "controlled")
1227 << " role.";
1228 SetIceRole_n(reversed_role);
1229}
1230
1231void JsepTransportController::OnTransportStateChanged_n(
1232 cricket::IceTransportInternal* transport) {
1233 RTC_DCHECK(network_thread_->IsCurrent());
1234 RTC_LOG(LS_INFO) << transport->transport_name() << " Transport "
1235 << transport->component()
1236 << " state changed. Check if state is complete.";
1237 UpdateAggregateStates_n();
1238}
1239
Bjorn Mellem175aa2e2018-11-08 11:23:22 -08001240void JsepTransportController::OnMediaTransportStateChanged_n() {
1241 SignalMediaTransportStateChanged();
1242 UpdateAggregateStates_n();
1243}
1244
Zhi Huange818b6e2018-02-22 15:26:27 -08001245void JsepTransportController::UpdateAggregateStates_n() {
1246 RTC_DCHECK(network_thread_->IsCurrent());
1247
1248 auto dtls_transports = GetDtlsTransports();
1249 cricket::IceConnectionState new_connection_state =
1250 cricket::kIceConnectionConnecting;
Jonas Olsson635474e2018-10-18 15:58:17 +02001251 PeerConnectionInterface::IceConnectionState new_ice_connection_state =
1252 PeerConnectionInterface::IceConnectionState::kIceConnectionNew;
1253 PeerConnectionInterface::PeerConnectionState new_combined_state =
1254 PeerConnectionInterface::PeerConnectionState::kNew;
Zhi Huange818b6e2018-02-22 15:26:27 -08001255 cricket::IceGatheringState new_gathering_state = cricket::kIceGatheringNew;
1256 bool any_failed = false;
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -07001257
1258 // TODO(http://bugs.webrtc.org/9719) If(when) media_transport disables
1259 // dtls_transports entirely, the below line will have to be changed to account
1260 // for the fact that dtls transports might be absent.
Zhi Huange818b6e2018-02-22 15:26:27 -08001261 bool all_connected = !dtls_transports.empty();
1262 bool all_completed = !dtls_transports.empty();
1263 bool any_gathering = false;
1264 bool all_done_gathering = !dtls_transports.empty();
Jonas Olsson635474e2018-10-18 15:58:17 +02001265
1266 std::map<IceTransportState, int> ice_state_counts;
1267 std::map<cricket::DtlsTransportState, int> dtls_state_counts;
1268
Zhi Huange818b6e2018-02-22 15:26:27 -08001269 for (const auto& dtls : dtls_transports) {
1270 any_failed = any_failed || dtls->ice_transport()->GetState() ==
1271 cricket::IceTransportState::STATE_FAILED;
1272 all_connected = all_connected && dtls->writable();
1273 all_completed =
1274 all_completed && dtls->writable() &&
1275 dtls->ice_transport()->GetState() ==
1276 cricket::IceTransportState::STATE_COMPLETED &&
1277 dtls->ice_transport()->GetIceRole() == cricket::ICEROLE_CONTROLLING &&
1278 dtls->ice_transport()->gathering_state() ==
1279 cricket::kIceGatheringComplete;
1280 any_gathering = any_gathering || dtls->ice_transport()->gathering_state() !=
1281 cricket::kIceGatheringNew;
1282 all_done_gathering =
1283 all_done_gathering && dtls->ice_transport()->gathering_state() ==
1284 cricket::kIceGatheringComplete;
Jonas Olsson635474e2018-10-18 15:58:17 +02001285
1286 dtls_state_counts[dtls->dtls_state()]++;
1287 ice_state_counts[dtls->ice_transport()->GetIceTransportState()]++;
Zhi Huange818b6e2018-02-22 15:26:27 -08001288 }
Piotr (Peter) Slatala4eb41122018-11-01 07:26:03 -07001289
1290 for (auto it = jsep_transports_by_name_.begin();
1291 it != jsep_transports_by_name_.end(); ++it) {
1292 auto jsep_transport = it->second.get();
1293 if (!jsep_transport->media_transport()) {
1294 continue;
1295 }
1296
1297 // There is no 'kIceConnectionDisconnected', so we only need to handle
1298 // connected and completed.
1299 // We treat kClosed as failed, because if it happens before shutting down
1300 // media transports it means that there was a failure.
1301 // MediaTransportInterface allows to flip back and forth between kWritable
1302 // and kPending, but there does not exist an implementation that does that,
1303 // and the contract of jsep transport controller doesn't quite expect that.
1304 // When this happens, we would go from connected to connecting state, but
1305 // this may change in future.
1306 any_failed |= jsep_transport->media_transport_state() ==
1307 webrtc::MediaTransportState::kClosed;
1308 all_completed &= jsep_transport->media_transport_state() ==
1309 webrtc::MediaTransportState::kWritable;
1310 all_connected &= jsep_transport->media_transport_state() ==
1311 webrtc::MediaTransportState::kWritable;
1312 }
1313
Zhi Huange818b6e2018-02-22 15:26:27 -08001314 if (any_failed) {
1315 new_connection_state = cricket::kIceConnectionFailed;
1316 } else if (all_completed) {
1317 new_connection_state = cricket::kIceConnectionCompleted;
1318 } else if (all_connected) {
1319 new_connection_state = cricket::kIceConnectionConnected;
1320 }
1321 if (ice_connection_state_ != new_connection_state) {
1322 ice_connection_state_ = new_connection_state;
Steve Antond25828a2018-08-31 13:06:05 -07001323 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1324 [this, new_connection_state] {
1325 SignalIceConnectionState(new_connection_state);
1326 });
Zhi Huange818b6e2018-02-22 15:26:27 -08001327 }
1328
Jonas Olsson635474e2018-10-18 15:58:17 +02001329 // Compute the current RTCIceConnectionState as described in
1330 // https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate.
1331 // The PeerConnection is responsible for handling the "closed" state.
1332 int total_ice_checking = ice_state_counts[IceTransportState::kChecking];
1333 int total_ice_connected = ice_state_counts[IceTransportState::kConnected];
1334 int total_ice_completed = ice_state_counts[IceTransportState::kCompleted];
1335 int total_ice_failed = ice_state_counts[IceTransportState::kFailed];
1336 int total_ice_disconnected =
1337 ice_state_counts[IceTransportState::kDisconnected];
1338 int total_ice_closed = ice_state_counts[IceTransportState::kClosed];
1339 int total_ice_new = ice_state_counts[IceTransportState::kNew];
1340 int total_ice = dtls_transports.size();
1341
1342 if (total_ice_failed > 0) {
1343 // Any of the RTCIceTransports are in the "failed" state.
1344 new_ice_connection_state = PeerConnectionInterface::kIceConnectionFailed;
1345 } else if (total_ice_disconnected > 0) {
1346 // Any of the RTCIceTransports are in the "disconnected" state and none of
1347 // them are in the "failed" state.
1348 new_ice_connection_state =
1349 PeerConnectionInterface::kIceConnectionDisconnected;
1350 } else if (total_ice_checking > 0) {
1351 // Any of the RTCIceTransports are in the "checking" state and none of them
1352 // are in the "disconnected" or "failed" state.
1353 new_ice_connection_state = PeerConnectionInterface::kIceConnectionChecking;
1354 } else if (total_ice_completed + total_ice_closed == total_ice &&
1355 total_ice_completed > 0) {
1356 // All RTCIceTransports are in the "completed" or "closed" state and at
1357 // least one of them is in the "completed" state.
1358 new_ice_connection_state = PeerConnectionInterface::kIceConnectionCompleted;
1359 } else if (total_ice_connected + total_ice_completed + total_ice_closed ==
1360 total_ice &&
1361 total_ice_connected > 0) {
1362 // All RTCIceTransports are in the "connected", "completed" or "closed"
1363 // state and at least one of them is in the "connected" state.
1364 new_ice_connection_state = PeerConnectionInterface::kIceConnectionConnected;
1365 } else if ((total_ice_new > 0 &&
1366 total_ice_checking + total_ice_disconnected + total_ice_failed ==
1367 0) ||
1368 total_ice == total_ice_closed) {
1369 // Any of the RTCIceTransports are in the "new" state and none of them are
1370 // in the "checking", "disconnected" or "failed" state, or all
1371 // RTCIceTransports are in the "closed" state, or there are no transports.
1372 new_ice_connection_state = PeerConnectionInterface::kIceConnectionNew;
1373 } else {
1374 RTC_NOTREACHED();
1375 }
1376
1377 if (standardized_ice_connection_state_ != new_ice_connection_state) {
1378 standardized_ice_connection_state_ = new_ice_connection_state;
1379 invoker_.AsyncInvoke<void>(
1380 RTC_FROM_HERE, signaling_thread_, [this, new_ice_connection_state] {
1381 SignalStandardizedIceConnectionState(new_ice_connection_state);
1382 });
1383 }
1384
1385 // Compute the current RTCPeerConnectionState as described in
1386 // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnectionstate.
1387 // The PeerConnection is responsible for handling the "closed" state.
1388 // Note that "connecting" is only a valid state for DTLS transports while
1389 // "checking", "completed" and "disconnected" are only valid for ICE
1390 // transports.
1391 int total_connected = total_ice_connected +
1392 dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTED];
1393 int total_dtls_connecting =
1394 dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTING];
1395 int total_failed =
1396 total_ice_failed + dtls_state_counts[cricket::DTLS_TRANSPORT_FAILED];
1397 int total_closed =
1398 total_ice_closed + dtls_state_counts[cricket::DTLS_TRANSPORT_CLOSED];
1399 int total_new =
1400 total_ice_new + dtls_state_counts[cricket::DTLS_TRANSPORT_NEW];
1401 int total_transports = total_ice * 2;
1402
1403 if (total_failed > 0) {
1404 // Any of the RTCIceTransports or RTCDtlsTransports are in a "failed" state.
1405 new_combined_state = PeerConnectionInterface::PeerConnectionState::kFailed;
1406 } else if (total_ice_disconnected > 0 &&
1407 total_dtls_connecting + total_ice_checking == 0) {
1408 // Any of the RTCIceTransports or RTCDtlsTransports are in the
1409 // "disconnected" state and none of them are in the "failed" or "connecting"
1410 // or "checking" state.
1411 new_combined_state =
1412 PeerConnectionInterface::PeerConnectionState::kDisconnected;
1413 } else if (total_dtls_connecting + total_ice_checking > 0) {
1414 // Any of the RTCIceTransports or RTCDtlsTransports are in the "connecting"
1415 // or "checking" state and none of them is in the "failed" state.
1416 new_combined_state =
1417 PeerConnectionInterface::PeerConnectionState::kConnecting;
1418 } else if (total_connected + total_ice_completed + total_closed ==
1419 total_transports &&
1420 total_connected + total_ice_completed > 0) {
1421 // All RTCIceTransports and RTCDtlsTransports are in the "connected",
1422 // "completed" or "closed" state and at least one of them is in the
1423 // "connected" or "completed" state.
1424 new_combined_state =
1425 PeerConnectionInterface::PeerConnectionState::kConnected;
1426 } else if ((total_new > 0 && total_dtls_connecting + total_ice_checking +
1427 total_failed + total_ice_disconnected ==
1428 0) ||
1429 total_transports == total_closed) {
1430 // Any of the RTCIceTransports or RTCDtlsTransports are in the "new" state
1431 // and none of the transports are in the "connecting", "checking", "failed"
1432 // or "disconnected" state, or all transports are in the "closed" state, or
1433 // there are no transports.
1434 //
1435 // Note that if none of the other conditions hold this is guaranteed to be
1436 // true.
1437 new_combined_state = PeerConnectionInterface::PeerConnectionState::kNew;
1438 } else {
1439 RTC_NOTREACHED();
1440 }
1441
1442 if (combined_connection_state_ != new_combined_state) {
1443 combined_connection_state_ = new_combined_state;
1444 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1445 [this, new_combined_state] {
1446 SignalConnectionState(new_combined_state);
1447 });
1448 }
1449
Zhi Huange818b6e2018-02-22 15:26:27 -08001450 if (all_done_gathering) {
1451 new_gathering_state = cricket::kIceGatheringComplete;
1452 } else if (any_gathering) {
1453 new_gathering_state = cricket::kIceGatheringGathering;
1454 }
1455 if (ice_gathering_state_ != new_gathering_state) {
1456 ice_gathering_state_ = new_gathering_state;
Steve Antond25828a2018-08-31 13:06:05 -07001457 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1458 [this, new_gathering_state] {
1459 SignalIceGatheringState(new_gathering_state);
1460 });
Zhi Huange818b6e2018-02-22 15:26:27 -08001461 }
1462}
1463
1464void JsepTransportController::OnDtlsHandshakeError(
1465 rtc::SSLHandshakeError error) {
1466 SignalDtlsHandshakeError(error);
1467}
1468
1469} // namespace webrtc