blob: cb99686a9844aa8f08331afe8a1e981f73642d0b [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)) {
Zhi Huang365381f2018-04-13 23:44:34765 MaybeDestroyJsepTransport(content_info.name);
766 return true;
767 }
768 return false;
Zhi Huange818b6e2018-02-22 23:26:27769}
770
Zhi Huang365381f2018-04-13 23:44:34771bool JsepTransportController::SetTransportForMid(
Zhi Huangd2248f82018-04-10 21:41:03772 const std::string& mid,
Taylor Brandstettercbaa2542018-04-16 23:42:14773 cricket::JsepTransport* jsep_transport) {
Zhi Huang365381f2018-04-13 23:44:34774 RTC_DCHECK(jsep_transport);
Zhi Huangd2248f82018-04-10 21:41:03775 if (mid_to_transport_[mid] == jsep_transport) {
Zhi Huang365381f2018-04-13 23:44:34776 return true;
Zhi Huangd2248f82018-04-10 21:41:03777 }
778
779 mid_to_transport_[mid] = jsep_transport;
Taylor Brandstettercbaa2542018-04-16 23:42:14780 return config_.transport_observer->OnTransportChanged(
781 mid, jsep_transport->rtp_transport(),
782 jsep_transport->rtp_dtls_transport());
Zhi Huangd2248f82018-04-10 21:41:03783}
784
Taylor Brandstettercbaa2542018-04-16 23:42:14785void JsepTransportController::RemoveTransportForMid(const std::string& mid) {
786 bool ret =
787 config_.transport_observer->OnTransportChanged(mid, nullptr, nullptr);
788 // Calling OnTransportChanged with nullptr should always succeed, since it is
789 // only expected to fail when adding media to a transport (not removing).
790 RTC_DCHECK(ret);
Zhi Huangd2248f82018-04-10 21:41:03791 mid_to_transport_.erase(mid);
792}
793
Zhi Huange818b6e2018-02-22 23:26:27794cricket::JsepTransportDescription
795JsepTransportController::CreateJsepTransportDescription(
796 cricket::ContentInfo content_info,
797 cricket::TransportInfo transport_info,
Zhi Huange830e682018-03-30 17:48:35798 const std::vector<int>& encrypted_extension_ids,
799 int rtp_abs_sendtime_extn_id) {
Zhi Huange818b6e2018-02-22 23:26:27800 const cricket::MediaContentDescription* content_desc =
801 static_cast<const cricket::MediaContentDescription*>(
802 content_info.description);
803 RTC_DCHECK(content_desc);
804 bool rtcp_mux_enabled = content_info.type == cricket::MediaProtocolType::kSctp
805 ? true
806 : content_desc->rtcp_mux();
807
808 return cricket::JsepTransportDescription(
809 rtcp_mux_enabled, content_desc->cryptos(), encrypted_extension_ids,
Zhi Huange830e682018-03-30 17:48:35810 rtp_abs_sendtime_extn_id, transport_info.description);
Zhi Huange818b6e2018-02-22 23:26:27811}
812
813bool JsepTransportController::ShouldUpdateBundleGroup(
814 SdpType type,
815 const cricket::SessionDescription* description) {
816 if (config_.bundle_policy ==
817 PeerConnectionInterface::kBundlePolicyMaxBundle) {
818 return true;
819 }
820
821 if (type != SdpType::kAnswer) {
822 return false;
823 }
824
825 RTC_DCHECK(local_desc_ && remote_desc_);
826 const cricket::ContentGroup* local_bundle =
827 local_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
828 const cricket::ContentGroup* remote_bundle =
829 remote_desc_->GetGroupByName(cricket::GROUP_TYPE_BUNDLE);
830 return local_bundle && remote_bundle;
831}
832
833std::vector<int> JsepTransportController::GetEncryptedHeaderExtensionIds(
834 const cricket::ContentInfo& content_info) {
835 const cricket::MediaContentDescription* content_desc =
836 static_cast<const cricket::MediaContentDescription*>(
837 content_info.description);
838
Benjamin Wrighta54daf12018-10-11 22:33:17839 if (!config_.crypto_options.srtp.enable_encrypted_rtp_header_extensions) {
Zhi Huange818b6e2018-02-22 23:26:27840 return std::vector<int>();
841 }
842
843 std::vector<int> encrypted_header_extension_ids;
844 for (auto extension : content_desc->rtp_header_extensions()) {
845 if (!extension.encrypt) {
846 continue;
847 }
848 auto it = std::find(encrypted_header_extension_ids.begin(),
849 encrypted_header_extension_ids.end(), extension.id);
850 if (it == encrypted_header_extension_ids.end()) {
851 encrypted_header_extension_ids.push_back(extension.id);
852 }
853 }
854 return encrypted_header_extension_ids;
855}
856
857std::vector<int>
858JsepTransportController::MergeEncryptedHeaderExtensionIdsForBundle(
859 const cricket::SessionDescription* description) {
860 RTC_DCHECK(description);
861 RTC_DCHECK(bundle_group_);
862
863 std::vector<int> merged_ids;
864 // Union the encrypted header IDs in the group when bundle is enabled.
865 for (const cricket::ContentInfo& content_info : description->contents()) {
866 if (bundle_group_->HasContentName(content_info.name)) {
867 std::vector<int> extension_ids =
868 GetEncryptedHeaderExtensionIds(content_info);
869 for (int id : extension_ids) {
870 auto it = std::find(merged_ids.begin(), merged_ids.end(), id);
871 if (it == merged_ids.end()) {
872 merged_ids.push_back(id);
873 }
874 }
875 }
876 }
877 return merged_ids;
878}
879
Zhi Huange830e682018-03-30 17:48:35880int JsepTransportController::GetRtpAbsSendTimeHeaderExtensionId(
Zhi Huange818b6e2018-02-22 23:26:27881 const cricket::ContentInfo& content_info) {
Zhi Huange830e682018-03-30 17:48:35882 if (!config_.enable_external_auth) {
883 return -1;
Zhi Huange818b6e2018-02-22 23:26:27884 }
885
886 const cricket::MediaContentDescription* content_desc =
887 static_cast<const cricket::MediaContentDescription*>(
888 content_info.description);
Zhi Huange830e682018-03-30 17:48:35889
890 const webrtc::RtpExtension* send_time_extension =
891 webrtc::RtpExtension::FindHeaderExtensionByUri(
892 content_desc->rtp_header_extensions(),
893 webrtc::RtpExtension::kAbsSendTimeUri);
894 return send_time_extension ? send_time_extension->id : -1;
895}
896
Zhi Huang365381f2018-04-13 23:44:34897const cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid(
Zhi Huange830e682018-03-30 17:48:35898 const std::string& mid) const {
Zhi Huangd2248f82018-04-10 21:41:03899 auto it = mid_to_transport_.find(mid);
900 return it == mid_to_transport_.end() ? nullptr : it->second;
Zhi Huange830e682018-03-30 17:48:35901}
902
Zhi Huang365381f2018-04-13 23:44:34903cricket::JsepTransport* JsepTransportController::GetJsepTransportForMid(
Zhi Huange830e682018-03-30 17:48:35904 const std::string& mid) {
Zhi Huangd2248f82018-04-10 21:41:03905 auto it = mid_to_transport_.find(mid);
906 return it == mid_to_transport_.end() ? nullptr : it->second;
Zhi Huange830e682018-03-30 17:48:35907}
908
Zhi Huang365381f2018-04-13 23:44:34909const cricket::JsepTransport* JsepTransportController::GetJsepTransportByName(
Zhi Huange830e682018-03-30 17:48:35910 const std::string& transport_name) const {
911 auto it = jsep_transports_by_name_.find(transport_name);
912 return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
913}
914
Zhi Huang365381f2018-04-13 23:44:34915cricket::JsepTransport* JsepTransportController::GetJsepTransportByName(
Zhi Huange830e682018-03-30 17:48:35916 const std::string& transport_name) {
917 auto it = jsep_transports_by_name_.find(transport_name);
918 return (it == jsep_transports_by_name_.end()) ? nullptr : it->second.get();
919}
920
921RTCError JsepTransportController::MaybeCreateJsepTransport(
Anton Sukhanov7940da02018-10-10 17:34:49922 bool local,
Zhi Huange830e682018-03-30 17:48:35923 const cricket::ContentInfo& content_info) {
924 RTC_DCHECK(network_thread_->IsCurrent());
Zhi Huang365381f2018-04-13 23:44:34925 cricket::JsepTransport* transport = GetJsepTransportByName(content_info.name);
Zhi Huange830e682018-03-30 17:48:35926 if (transport) {
927 return RTCError::OK();
928 }
929
930 const cricket::MediaContentDescription* content_desc =
931 static_cast<const cricket::MediaContentDescription*>(
932 content_info.description);
933 if (certificate_ && !content_desc->cryptos().empty()) {
934 return RTCError(RTCErrorType::INVALID_PARAMETER,
935 "SDES and DTLS-SRTP cannot be enabled at the same time.");
936 }
937
Zhi Huange818b6e2018-02-22 23:26:27938 std::unique_ptr<cricket::DtlsTransportInternal> rtp_dtls_transport =
Zhi Huangd2248f82018-04-10 21:41:03939 CreateDtlsTransport(content_info.name, /*rtcp =*/false);
Anton Sukhanov7940da02018-10-10 17:34:49940
Zhi Huange818b6e2018-02-22 23:26:27941 std::unique_ptr<cricket::DtlsTransportInternal> rtcp_dtls_transport;
Anton Sukhanov7940da02018-10-10 17:34:49942 std::unique_ptr<RtpTransport> unencrypted_rtp_transport;
943 std::unique_ptr<SrtpTransport> sdes_transport;
944 std::unique_ptr<DtlsSrtpTransport> dtls_srtp_transport;
945 std::unique_ptr<MediaTransportInterface> media_transport;
946
Zhi Huange830e682018-03-30 17:48:35947 if (config_.rtcp_mux_policy !=
948 PeerConnectionInterface::kRtcpMuxPolicyRequire &&
949 content_info.type == cricket::MediaProtocolType::kRtp) {
Zhi Huangd2248f82018-04-10 21:41:03950 rtcp_dtls_transport =
951 CreateDtlsTransport(content_info.name, /*rtcp =*/true);
Zhi Huange818b6e2018-02-22 23:26:27952 }
953
Piotr (Peter) Slatala9f956252018-10-31 15:25:26954 absl::optional<cricket::CryptoParams> selected_crypto_for_media_transport;
955 if (content_info.media_description() &&
956 !content_info.media_description()->cryptos().empty()) {
957 // Order of cryptos is deterministic (rfc4568, 5.1.1), so we just select the
958 // first one (in fact the first one should be the most preferred one.) We
959 // ignore the HMAC size, as media transport crypto settings currently don't
960 // expose HMAC size, nor crypto protocol for that matter.
961 selected_crypto_for_media_transport =
962 content_info.media_description()->cryptos()[0];
963 }
964
Anton Sukhanov7940da02018-10-10 17:34:49965 if (config_.media_transport_factory != nullptr) {
Piotr (Peter) Slatala9f956252018-10-31 15:25:26966 if (!selected_crypto_for_media_transport.has_value()) {
967 RTC_LOG(LS_WARNING) << "a=cryto line was not found in the offer. Most "
968 "likely you did not enable SDES. "
969 "Make sure to pass config.enable_dtls_srtp=false "
970 "to RTCConfiguration. "
971 "Cannot continue with media transport. Falling "
972 "back to RTP. is_local="
973 << local;
Anton Sukhanov7940da02018-10-10 17:34:49974
Piotr (Peter) Slatala9f956252018-10-31 15:25:26975 // Remove media_transport_factory from config, because we don't want to
976 // use it on the subsequent call (for the other side of the offer).
977 config_.media_transport_factory = nullptr;
978 } else {
979 // Note that we ignore here lifetime and length.
980 // In fact we take those bits (inline, lifetime and length) and keep it as
981 // part of key derivation.
982 //
983 // Technically, we are also not following rfc4568, which requires us to
984 // send and answer with the key that we chose. In practice, for media
985 // transport, the current approach should be sufficient (we take the key
986 // that sender offered, and caller assumes we will use it. We are not
987 // signaling back that we indeed used it.)
988 std::unique_ptr<rtc::KeyDerivation> key_derivation =
989 rtc::KeyDerivation::Create(rtc::KeyDerivationAlgorithm::HKDF_SHA256);
990 const std::string label = "MediaTransportLabel";
991 constexpr int kDerivedKeyByteSize = 32;
Anton Sukhanov7940da02018-10-10 17:34:49992
Piotr (Peter) Slatala9f956252018-10-31 15:25:26993 int key_len, salt_len;
994 if (!rtc::GetSrtpKeyAndSaltLengths(
995 rtc::SrtpCryptoSuiteFromName(
996 selected_crypto_for_media_transport.value().cipher_suite),
997 &key_len, &salt_len)) {
998 RTC_CHECK(false) << "Cannot set up secure media transport";
999 }
1000 rtc::ZeroOnFreeBuffer<uint8_t> raw_key(key_len + salt_len);
1001
1002 cricket::SrtpFilter::ParseKeyParams(
1003 selected_crypto_for_media_transport.value().key_params,
1004 raw_key.data(), raw_key.size());
1005 absl::optional<rtc::ZeroOnFreeBuffer<uint8_t>> key =
1006 key_derivation->DeriveKey(
1007 raw_key,
1008 /*salt=*/nullptr,
1009 rtc::ArrayView<const uint8_t>(
1010 reinterpret_cast<const uint8_t*>(label.data()), label.size()),
1011 kDerivedKeyByteSize);
1012
1013 // We want to crash the app if we don't have a key, and not silently fall
1014 // back to the unsecure communication.
1015 RTC_CHECK(key.has_value());
1016 MediaTransportSettings settings;
1017 settings.is_caller = local;
1018 settings.pre_shared_key =
1019 std::string(reinterpret_cast<const char*>(key.value().data()),
1020 key.value().size());
1021 auto media_transport_result =
1022 config_.media_transport_factory->CreateMediaTransport(
1023 rtp_dtls_transport->ice_transport(), network_thread_, settings);
1024
1025 // TODO(sukhanov): Proper error handling.
1026 RTC_CHECK(media_transport_result.ok());
1027
1028 media_transport = std::move(media_transport_result.value());
1029 }
Anton Sukhanov7940da02018-10-10 17:34:491030 }
1031
1032 // TODO(sukhanov): Do not create RTP/RTCP transports if media transport is
1033 // used.
Zhi Huange818b6e2018-02-22 23:26:271034 if (config_.disable_encryption) {
1035 unencrypted_rtp_transport = CreateUnencryptedRtpTransport(
Zhi Huangd2248f82018-04-10 21:41:031036 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 23:26:271037 } else if (!content_desc->cryptos().empty()) {
Zhi Huangd2248f82018-04-10 21:41:031038 sdes_transport = CreateSdesTransport(
1039 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 23:26:271040 } else {
Zhi Huangd2248f82018-04-10 21:41:031041 dtls_srtp_transport = CreateDtlsSrtpTransport(
1042 content_info.name, rtp_dtls_transport.get(), rtcp_dtls_transport.get());
Zhi Huange818b6e2018-02-22 23:26:271043 }
1044
Zhi Huang365381f2018-04-13 23:44:341045 std::unique_ptr<cricket::JsepTransport> jsep_transport =
Karl Wiberg918f50c2018-07-05 09:40:331046 absl::make_unique<cricket::JsepTransport>(
Zhi Huangd2248f82018-04-10 21:41:031047 content_info.name, certificate_, std::move(unencrypted_rtp_transport),
Zhi Huange818b6e2018-02-22 23:26:271048 std::move(sdes_transport), std::move(dtls_srtp_transport),
Anton Sukhanov7940da02018-10-10 17:34:491049 std::move(rtp_dtls_transport), std::move(rtcp_dtls_transport),
1050 std::move(media_transport));
Zhi Huange818b6e2018-02-22 23:26:271051 jsep_transport->SignalRtcpMuxActive.connect(
1052 this, &JsepTransportController::UpdateAggregateStates_n);
Piotr (Peter) Slatala4eb41122018-11-01 14:26:031053 jsep_transport->SignalMediaTransportStateChanged.connect(
Bjorn Mellem175aa2e2018-11-08 19:23:221054 this, &JsepTransportController::OnMediaTransportStateChanged_n);
Taylor Brandstettercbaa2542018-04-16 23:42:141055 SetTransportForMid(content_info.name, jsep_transport.get());
Zhi Huange830e682018-03-30 17:48:351056
Zhi Huangd2248f82018-04-10 21:41:031057 jsep_transports_by_name_[content_info.name] = std::move(jsep_transport);
1058 UpdateAggregateStates_n();
Zhi Huange830e682018-03-30 17:48:351059 return RTCError::OK();
Zhi Huange818b6e2018-02-22 23:26:271060}
1061
1062void JsepTransportController::MaybeDestroyJsepTransport(
1063 const std::string& mid) {
Zhi Huangd2248f82018-04-10 21:41:031064 auto jsep_transport = GetJsepTransportByName(mid);
1065 if (!jsep_transport) {
1066 return;
1067 }
1068
1069 // Don't destroy the JsepTransport if there are still media sections referring
1070 // to it.
1071 for (const auto& kv : mid_to_transport_) {
1072 if (kv.second == jsep_transport) {
1073 return;
1074 }
1075 }
Zhi Huange830e682018-03-30 17:48:351076 jsep_transports_by_name_.erase(mid);
Zhi Huange818b6e2018-02-22 23:26:271077 UpdateAggregateStates_n();
1078}
1079
1080void JsepTransportController::DestroyAllJsepTransports_n() {
1081 RTC_DCHECK(network_thread_->IsCurrent());
Zhi Huange830e682018-03-30 17:48:351082 jsep_transports_by_name_.clear();
Zhi Huange818b6e2018-02-22 23:26:271083}
1084
1085void JsepTransportController::SetIceRole_n(cricket::IceRole ice_role) {
1086 RTC_DCHECK(network_thread_->IsCurrent());
1087
1088 ice_role_ = ice_role;
1089 for (auto& dtls : GetDtlsTransports()) {
1090 dtls->ice_transport()->SetIceRole(ice_role_);
1091 }
1092}
1093
1094cricket::IceRole JsepTransportController::DetermineIceRole(
Zhi Huang365381f2018-04-13 23:44:341095 cricket::JsepTransport* jsep_transport,
Zhi Huange818b6e2018-02-22 23:26:271096 const cricket::TransportInfo& transport_info,
1097 SdpType type,
1098 bool local) {
1099 cricket::IceRole ice_role = ice_role_;
1100 auto tdesc = transport_info.description;
1101 if (local) {
1102 // The initial offer side may use ICE Lite, in which case, per RFC5245
1103 // Section 5.1.1, the answer side should take the controlling role if it is
1104 // in the full ICE mode.
1105 //
1106 // When both sides use ICE Lite, the initial offer side must take the
1107 // controlling role, and this is the default logic implemented in
1108 // SetLocalDescription in JsepTransportController.
1109 if (jsep_transport->remote_description() &&
1110 jsep_transport->remote_description()->transport_desc.ice_mode ==
1111 cricket::ICEMODE_LITE &&
1112 ice_role_ == cricket::ICEROLE_CONTROLLED &&
1113 tdesc.ice_mode == cricket::ICEMODE_FULL) {
1114 ice_role = cricket::ICEROLE_CONTROLLING;
1115 }
1116
1117 // Older versions of Chrome expect the ICE role to be re-determined when an
1118 // ICE restart occurs, and also don't perform conflict resolution correctly,
1119 // so for now we can't safely stop doing this, unless the application opts
1120 // in by setting |config_.redetermine_role_on_ice_restart_| to false. See:
1121 // https://bugs.chromium.org/p/chromium/issues/detail?id=628676
1122 // TODO(deadbeef): Remove this when these old versions of Chrome reach a low
1123 // enough population.
1124 if (config_.redetermine_role_on_ice_restart &&
1125 jsep_transport->local_description() &&
1126 cricket::IceCredentialsChanged(
1127 jsep_transport->local_description()->transport_desc.ice_ufrag,
1128 jsep_transport->local_description()->transport_desc.ice_pwd,
1129 tdesc.ice_ufrag, tdesc.ice_pwd) &&
1130 // Don't change the ICE role if the remote endpoint is ICE lite; we
1131 // should always be controlling in that case.
1132 (!jsep_transport->remote_description() ||
1133 jsep_transport->remote_description()->transport_desc.ice_mode !=
1134 cricket::ICEMODE_LITE)) {
1135 ice_role = (type == SdpType::kOffer) ? cricket::ICEROLE_CONTROLLING
1136 : cricket::ICEROLE_CONTROLLED;
1137 }
1138 } else {
1139 // If our role is cricket::ICEROLE_CONTROLLED and the remote endpoint
1140 // supports only ice_lite, this local endpoint should take the CONTROLLING
1141 // role.
1142 // TODO(deadbeef): This is a session-level attribute, so it really shouldn't
1143 // be in a TransportDescription in the first place...
1144 if (ice_role_ == cricket::ICEROLE_CONTROLLED &&
1145 tdesc.ice_mode == cricket::ICEMODE_LITE) {
1146 ice_role = cricket::ICEROLE_CONTROLLING;
1147 }
1148
1149 // If we use ICE Lite and the remote endpoint uses the full implementation
1150 // of ICE, the local endpoint must take the controlled role, and the other
1151 // side must be the controlling role.
1152 if (jsep_transport->local_description() &&
1153 jsep_transport->local_description()->transport_desc.ice_mode ==
1154 cricket::ICEMODE_LITE &&
1155 ice_role_ == cricket::ICEROLE_CONTROLLING &&
Zhi Huange830e682018-03-30 17:48:351156 tdesc.ice_mode == cricket::ICEMODE_FULL) {
Zhi Huange818b6e2018-02-22 23:26:271157 ice_role = cricket::ICEROLE_CONTROLLED;
1158 }
1159 }
1160
1161 return ice_role;
1162}
1163
1164void JsepTransportController::OnTransportWritableState_n(
1165 rtc::PacketTransportInternal* transport) {
1166 RTC_DCHECK(network_thread_->IsCurrent());
1167 RTC_LOG(LS_INFO) << " Transport " << transport->transport_name()
1168 << " writability changed to " << transport->writable()
1169 << ".";
1170 UpdateAggregateStates_n();
1171}
1172
1173void JsepTransportController::OnTransportReceivingState_n(
1174 rtc::PacketTransportInternal* transport) {
1175 RTC_DCHECK(network_thread_->IsCurrent());
1176 UpdateAggregateStates_n();
1177}
1178
1179void JsepTransportController::OnTransportGatheringState_n(
1180 cricket::IceTransportInternal* transport) {
1181 RTC_DCHECK(network_thread_->IsCurrent());
1182 UpdateAggregateStates_n();
1183}
1184
1185void JsepTransportController::OnTransportCandidateGathered_n(
1186 cricket::IceTransportInternal* transport,
1187 const cricket::Candidate& candidate) {
1188 RTC_DCHECK(network_thread_->IsCurrent());
1189
1190 // We should never signal peer-reflexive candidates.
1191 if (candidate.type() == cricket::PRFLX_PORT_TYPE) {
1192 RTC_NOTREACHED();
1193 return;
1194 }
Steve Antond25828a2018-08-31 20:06:051195 std::string transport_name = transport->transport_name();
1196 invoker_.AsyncInvoke<void>(
1197 RTC_FROM_HERE, signaling_thread_, [this, transport_name, candidate] {
1198 SignalIceCandidatesGathered(transport_name, {candidate});
1199 });
Zhi Huange818b6e2018-02-22 23:26:271200}
1201
1202void JsepTransportController::OnTransportCandidatesRemoved_n(
1203 cricket::IceTransportInternal* transport,
1204 const cricket::Candidates& candidates) {
1205 invoker_.AsyncInvoke<void>(
1206 RTC_FROM_HERE, signaling_thread_,
Steve Antond25828a2018-08-31 20:06:051207 [this, candidates] { SignalIceCandidatesRemoved(candidates); });
Zhi Huange818b6e2018-02-22 23:26:271208}
1209
1210void JsepTransportController::OnTransportRoleConflict_n(
1211 cricket::IceTransportInternal* transport) {
1212 RTC_DCHECK(network_thread_->IsCurrent());
1213 // Note: since the role conflict is handled entirely on the network thread,
1214 // we don't need to worry about role conflicts occurring on two ports at
1215 // once. The first one encountered should immediately reverse the role.
1216 cricket::IceRole reversed_role = (ice_role_ == cricket::ICEROLE_CONTROLLING)
1217 ? cricket::ICEROLE_CONTROLLED
1218 : cricket::ICEROLE_CONTROLLING;
1219 RTC_LOG(LS_INFO) << "Got role conflict; switching to "
1220 << (reversed_role == cricket::ICEROLE_CONTROLLING
1221 ? "controlling"
1222 : "controlled")
1223 << " role.";
1224 SetIceRole_n(reversed_role);
1225}
1226
1227void JsepTransportController::OnTransportStateChanged_n(
1228 cricket::IceTransportInternal* transport) {
1229 RTC_DCHECK(network_thread_->IsCurrent());
1230 RTC_LOG(LS_INFO) << transport->transport_name() << " Transport "
1231 << transport->component()
1232 << " state changed. Check if state is complete.";
1233 UpdateAggregateStates_n();
1234}
1235
Bjorn Mellem175aa2e2018-11-08 19:23:221236void JsepTransportController::OnMediaTransportStateChanged_n() {
1237 SignalMediaTransportStateChanged();
1238 UpdateAggregateStates_n();
1239}
1240
Zhi Huange818b6e2018-02-22 23:26:271241void JsepTransportController::UpdateAggregateStates_n() {
1242 RTC_DCHECK(network_thread_->IsCurrent());
1243
1244 auto dtls_transports = GetDtlsTransports();
1245 cricket::IceConnectionState new_connection_state =
1246 cricket::kIceConnectionConnecting;
Jonas Olsson635474e2018-10-18 13:58:171247 PeerConnectionInterface::IceConnectionState new_ice_connection_state =
1248 PeerConnectionInterface::IceConnectionState::kIceConnectionNew;
1249 PeerConnectionInterface::PeerConnectionState new_combined_state =
1250 PeerConnectionInterface::PeerConnectionState::kNew;
Zhi Huange818b6e2018-02-22 23:26:271251 cricket::IceGatheringState new_gathering_state = cricket::kIceGatheringNew;
1252 bool any_failed = false;
Piotr (Peter) Slatala4eb41122018-11-01 14:26:031253
1254 // TODO(http://bugs.webrtc.org/9719) If(when) media_transport disables
1255 // dtls_transports entirely, the below line will have to be changed to account
1256 // for the fact that dtls transports might be absent.
Zhi Huange818b6e2018-02-22 23:26:271257 bool all_connected = !dtls_transports.empty();
1258 bool all_completed = !dtls_transports.empty();
1259 bool any_gathering = false;
1260 bool all_done_gathering = !dtls_transports.empty();
Jonas Olsson635474e2018-10-18 13:58:171261
1262 std::map<IceTransportState, int> ice_state_counts;
1263 std::map<cricket::DtlsTransportState, int> dtls_state_counts;
1264
Zhi Huange818b6e2018-02-22 23:26:271265 for (const auto& dtls : dtls_transports) {
1266 any_failed = any_failed || dtls->ice_transport()->GetState() ==
1267 cricket::IceTransportState::STATE_FAILED;
1268 all_connected = all_connected && dtls->writable();
1269 all_completed =
1270 all_completed && dtls->writable() &&
1271 dtls->ice_transport()->GetState() ==
1272 cricket::IceTransportState::STATE_COMPLETED &&
1273 dtls->ice_transport()->GetIceRole() == cricket::ICEROLE_CONTROLLING &&
1274 dtls->ice_transport()->gathering_state() ==
1275 cricket::kIceGatheringComplete;
1276 any_gathering = any_gathering || dtls->ice_transport()->gathering_state() !=
1277 cricket::kIceGatheringNew;
1278 all_done_gathering =
1279 all_done_gathering && dtls->ice_transport()->gathering_state() ==
1280 cricket::kIceGatheringComplete;
Jonas Olsson635474e2018-10-18 13:58:171281
1282 dtls_state_counts[dtls->dtls_state()]++;
1283 ice_state_counts[dtls->ice_transport()->GetIceTransportState()]++;
Zhi Huange818b6e2018-02-22 23:26:271284 }
Piotr (Peter) Slatala4eb41122018-11-01 14:26:031285
1286 for (auto it = jsep_transports_by_name_.begin();
1287 it != jsep_transports_by_name_.end(); ++it) {
1288 auto jsep_transport = it->second.get();
1289 if (!jsep_transport->media_transport()) {
1290 continue;
1291 }
1292
1293 // There is no 'kIceConnectionDisconnected', so we only need to handle
1294 // connected and completed.
1295 // We treat kClosed as failed, because if it happens before shutting down
1296 // media transports it means that there was a failure.
1297 // MediaTransportInterface allows to flip back and forth between kWritable
1298 // and kPending, but there does not exist an implementation that does that,
1299 // and the contract of jsep transport controller doesn't quite expect that.
1300 // When this happens, we would go from connected to connecting state, but
1301 // this may change in future.
1302 any_failed |= jsep_transport->media_transport_state() ==
1303 webrtc::MediaTransportState::kClosed;
1304 all_completed &= jsep_transport->media_transport_state() ==
1305 webrtc::MediaTransportState::kWritable;
1306 all_connected &= jsep_transport->media_transport_state() ==
1307 webrtc::MediaTransportState::kWritable;
1308 }
1309
Zhi Huange818b6e2018-02-22 23:26:271310 if (any_failed) {
1311 new_connection_state = cricket::kIceConnectionFailed;
1312 } else if (all_completed) {
1313 new_connection_state = cricket::kIceConnectionCompleted;
1314 } else if (all_connected) {
1315 new_connection_state = cricket::kIceConnectionConnected;
1316 }
1317 if (ice_connection_state_ != new_connection_state) {
1318 ice_connection_state_ = new_connection_state;
Steve Antond25828a2018-08-31 20:06:051319 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1320 [this, new_connection_state] {
1321 SignalIceConnectionState(new_connection_state);
1322 });
Zhi Huange818b6e2018-02-22 23:26:271323 }
1324
Jonas Olsson635474e2018-10-18 13:58:171325 // Compute the current RTCIceConnectionState as described in
1326 // https://www.w3.org/TR/webrtc/#dom-rtciceconnectionstate.
1327 // The PeerConnection is responsible for handling the "closed" state.
1328 int total_ice_checking = ice_state_counts[IceTransportState::kChecking];
1329 int total_ice_connected = ice_state_counts[IceTransportState::kConnected];
1330 int total_ice_completed = ice_state_counts[IceTransportState::kCompleted];
1331 int total_ice_failed = ice_state_counts[IceTransportState::kFailed];
1332 int total_ice_disconnected =
1333 ice_state_counts[IceTransportState::kDisconnected];
1334 int total_ice_closed = ice_state_counts[IceTransportState::kClosed];
1335 int total_ice_new = ice_state_counts[IceTransportState::kNew];
1336 int total_ice = dtls_transports.size();
1337
1338 if (total_ice_failed > 0) {
1339 // Any of the RTCIceTransports are in the "failed" state.
1340 new_ice_connection_state = PeerConnectionInterface::kIceConnectionFailed;
1341 } else if (total_ice_disconnected > 0) {
1342 // Any of the RTCIceTransports are in the "disconnected" state and none of
1343 // them are in the "failed" state.
1344 new_ice_connection_state =
1345 PeerConnectionInterface::kIceConnectionDisconnected;
1346 } else if (total_ice_checking > 0) {
1347 // Any of the RTCIceTransports are in the "checking" state and none of them
1348 // are in the "disconnected" or "failed" state.
1349 new_ice_connection_state = PeerConnectionInterface::kIceConnectionChecking;
1350 } else if (total_ice_completed + total_ice_closed == total_ice &&
1351 total_ice_completed > 0) {
1352 // All RTCIceTransports are in the "completed" or "closed" state and at
1353 // least one of them is in the "completed" state.
1354 new_ice_connection_state = PeerConnectionInterface::kIceConnectionCompleted;
1355 } else if (total_ice_connected + total_ice_completed + total_ice_closed ==
1356 total_ice &&
1357 total_ice_connected > 0) {
1358 // All RTCIceTransports are in the "connected", "completed" or "closed"
1359 // state and at least one of them is in the "connected" state.
1360 new_ice_connection_state = PeerConnectionInterface::kIceConnectionConnected;
1361 } else if ((total_ice_new > 0 &&
1362 total_ice_checking + total_ice_disconnected + total_ice_failed ==
1363 0) ||
1364 total_ice == total_ice_closed) {
1365 // Any of the RTCIceTransports are in the "new" state and none of them are
1366 // in the "checking", "disconnected" or "failed" state, or all
1367 // RTCIceTransports are in the "closed" state, or there are no transports.
1368 new_ice_connection_state = PeerConnectionInterface::kIceConnectionNew;
1369 } else {
1370 RTC_NOTREACHED();
1371 }
1372
1373 if (standardized_ice_connection_state_ != new_ice_connection_state) {
1374 standardized_ice_connection_state_ = new_ice_connection_state;
1375 invoker_.AsyncInvoke<void>(
1376 RTC_FROM_HERE, signaling_thread_, [this, new_ice_connection_state] {
1377 SignalStandardizedIceConnectionState(new_ice_connection_state);
1378 });
1379 }
1380
1381 // Compute the current RTCPeerConnectionState as described in
1382 // https://www.w3.org/TR/webrtc/#dom-rtcpeerconnectionstate.
1383 // The PeerConnection is responsible for handling the "closed" state.
1384 // Note that "connecting" is only a valid state for DTLS transports while
1385 // "checking", "completed" and "disconnected" are only valid for ICE
1386 // transports.
1387 int total_connected = total_ice_connected +
1388 dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTED];
1389 int total_dtls_connecting =
1390 dtls_state_counts[cricket::DTLS_TRANSPORT_CONNECTING];
1391 int total_failed =
1392 total_ice_failed + dtls_state_counts[cricket::DTLS_TRANSPORT_FAILED];
1393 int total_closed =
1394 total_ice_closed + dtls_state_counts[cricket::DTLS_TRANSPORT_CLOSED];
1395 int total_new =
1396 total_ice_new + dtls_state_counts[cricket::DTLS_TRANSPORT_NEW];
1397 int total_transports = total_ice * 2;
1398
1399 if (total_failed > 0) {
1400 // Any of the RTCIceTransports or RTCDtlsTransports are in a "failed" state.
1401 new_combined_state = PeerConnectionInterface::PeerConnectionState::kFailed;
1402 } else if (total_ice_disconnected > 0 &&
1403 total_dtls_connecting + total_ice_checking == 0) {
1404 // Any of the RTCIceTransports or RTCDtlsTransports are in the
1405 // "disconnected" state and none of them are in the "failed" or "connecting"
1406 // or "checking" state.
1407 new_combined_state =
1408 PeerConnectionInterface::PeerConnectionState::kDisconnected;
1409 } else if (total_dtls_connecting + total_ice_checking > 0) {
1410 // Any of the RTCIceTransports or RTCDtlsTransports are in the "connecting"
1411 // or "checking" state and none of them is in the "failed" state.
1412 new_combined_state =
1413 PeerConnectionInterface::PeerConnectionState::kConnecting;
1414 } else if (total_connected + total_ice_completed + total_closed ==
1415 total_transports &&
1416 total_connected + total_ice_completed > 0) {
1417 // All RTCIceTransports and RTCDtlsTransports are in the "connected",
1418 // "completed" or "closed" state and at least one of them is in the
1419 // "connected" or "completed" state.
1420 new_combined_state =
1421 PeerConnectionInterface::PeerConnectionState::kConnected;
1422 } else if ((total_new > 0 && total_dtls_connecting + total_ice_checking +
1423 total_failed + total_ice_disconnected ==
1424 0) ||
1425 total_transports == total_closed) {
1426 // Any of the RTCIceTransports or RTCDtlsTransports are in the "new" state
1427 // and none of the transports are in the "connecting", "checking", "failed"
1428 // or "disconnected" state, or all transports are in the "closed" state, or
1429 // there are no transports.
1430 //
1431 // Note that if none of the other conditions hold this is guaranteed to be
1432 // true.
1433 new_combined_state = PeerConnectionInterface::PeerConnectionState::kNew;
1434 } else {
1435 RTC_NOTREACHED();
1436 }
1437
1438 if (combined_connection_state_ != new_combined_state) {
1439 combined_connection_state_ = new_combined_state;
1440 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1441 [this, new_combined_state] {
1442 SignalConnectionState(new_combined_state);
1443 });
1444 }
1445
Zhi Huange818b6e2018-02-22 23:26:271446 if (all_done_gathering) {
1447 new_gathering_state = cricket::kIceGatheringComplete;
1448 } else if (any_gathering) {
1449 new_gathering_state = cricket::kIceGatheringGathering;
1450 }
1451 if (ice_gathering_state_ != new_gathering_state) {
1452 ice_gathering_state_ = new_gathering_state;
Steve Antond25828a2018-08-31 20:06:051453 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_,
1454 [this, new_gathering_state] {
1455 SignalIceGatheringState(new_gathering_state);
1456 });
Zhi Huange818b6e2018-02-22 23:26:271457 }
1458}
1459
1460void JsepTransportController::OnDtlsHandshakeError(
1461 rtc::SSLHandshakeError error) {
1462 SignalDtlsHandshakeError(error);
1463}
1464
1465} // namespace webrtc