1 /*
2 * Copyright 2013 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/webrtc_session_description_factory.h"
12
13 #include <stddef.h>
14 #include <algorithm>
15 #include <memory>
16 #include <string>
17 #include <type_traits>
18 #include <utility>
19 #include <vector>
20
21 #include "absl/algorithm/container.h"
22 #include "absl/types/optional.h"
23 #include "api/jsep.h"
24 #include "api/jsep_session_description.h"
25 #include "api/rtc_error.h"
26 #include "pc/sdp_state_provider.h"
27 #include "pc/session_description.h"
28 #include "rtc_base/checks.h"
29 #include "rtc_base/location.h"
30 #include "rtc_base/logging.h"
31 #include "rtc_base/ref_counted_object.h"
32 #include "rtc_base/ssl_identity.h"
33 #include "rtc_base/ssl_stream_adapter.h"
34 #include "rtc_base/string_encode.h"
35
36 using cricket::MediaSessionOptions;
37 using rtc::UniqueRandomIdGenerator;
38
39 namespace webrtc {
40 namespace {
41 static const char kFailedDueToIdentityFailed[] =
42 " failed because DTLS identity request failed";
43 static const char kFailedDueToSessionShutdown[] =
44 " failed because the session was shut down";
45
46 static const uint64_t kInitSessionVersion = 2;
47
48 // Check that each sender has a unique ID.
ValidMediaSessionOptions(const cricket::MediaSessionOptions & session_options)49 static bool ValidMediaSessionOptions(
50 const cricket::MediaSessionOptions& session_options) {
51 std::vector<cricket::SenderOptions> sorted_senders;
52 for (const cricket::MediaDescriptionOptions& media_description_options :
53 session_options.media_description_options) {
54 sorted_senders.insert(sorted_senders.end(),
55 media_description_options.sender_options.begin(),
56 media_description_options.sender_options.end());
57 }
58 absl::c_sort(sorted_senders, [](const cricket::SenderOptions& sender1,
59 const cricket::SenderOptions& sender2) {
60 return sender1.track_id < sender2.track_id;
61 });
62 return absl::c_adjacent_find(sorted_senders,
63 [](const cricket::SenderOptions& sender1,
64 const cricket::SenderOptions& sender2) {
65 return sender1.track_id == sender2.track_id;
66 }) == sorted_senders.end();
67 }
68
69 enum {
70 MSG_CREATE_SESSIONDESCRIPTION_SUCCESS,
71 MSG_CREATE_SESSIONDESCRIPTION_FAILED,
72 MSG_USE_CONSTRUCTOR_CERTIFICATE
73 };
74
75 struct CreateSessionDescriptionMsg : public rtc::MessageData {
CreateSessionDescriptionMsgwebrtc::__anon392417ff0111::CreateSessionDescriptionMsg76 explicit CreateSessionDescriptionMsg(
77 webrtc::CreateSessionDescriptionObserver* observer,
78 RTCError error_in)
79 : observer(observer), error(std::move(error_in)) {}
80
81 rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserver> observer;
82 RTCError error;
83 std::unique_ptr<webrtc::SessionDescriptionInterface> description;
84 };
85 } // namespace
86
OnFailure()87 void WebRtcCertificateGeneratorCallback::OnFailure() {
88 SignalRequestFailed();
89 }
90
OnSuccess(const rtc::scoped_refptr<rtc::RTCCertificate> & certificate)91 void WebRtcCertificateGeneratorCallback::OnSuccess(
92 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
93 SignalCertificateReady(certificate);
94 }
95
96 // static
CopyCandidatesFromSessionDescription(const SessionDescriptionInterface * source_desc,const std::string & content_name,SessionDescriptionInterface * dest_desc)97 void WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
98 const SessionDescriptionInterface* source_desc,
99 const std::string& content_name,
100 SessionDescriptionInterface* dest_desc) {
101 if (!source_desc) {
102 return;
103 }
104 const cricket::ContentInfos& contents =
105 source_desc->description()->contents();
106 const cricket::ContentInfo* cinfo =
107 source_desc->description()->GetContentByName(content_name);
108 if (!cinfo) {
109 return;
110 }
111 size_t mediasection_index = static_cast<int>(cinfo - &contents[0]);
112 const IceCandidateCollection* source_candidates =
113 source_desc->candidates(mediasection_index);
114 const IceCandidateCollection* dest_candidates =
115 dest_desc->candidates(mediasection_index);
116 if (!source_candidates || !dest_candidates) {
117 return;
118 }
119 for (size_t n = 0; n < source_candidates->count(); ++n) {
120 const IceCandidateInterface* new_candidate = source_candidates->at(n);
121 if (!dest_candidates->HasCandidate(new_candidate)) {
122 dest_desc->AddCandidate(source_candidates->at(n));
123 }
124 }
125 }
126
WebRtcSessionDescriptionFactory(rtc::Thread * signaling_thread,cricket::ChannelManager * channel_manager,const SdpStateProvider * sdp_info,const std::string & session_id,bool dtls_enabled,std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,const rtc::scoped_refptr<rtc::RTCCertificate> & certificate,UniqueRandomIdGenerator * ssrc_generator,std::function<void (const rtc::scoped_refptr<rtc::RTCCertificate> &)> on_certificate_ready)127 WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
128 rtc::Thread* signaling_thread,
129 cricket::ChannelManager* channel_manager,
130 const SdpStateProvider* sdp_info,
131 const std::string& session_id,
132 bool dtls_enabled,
133 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
134 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate,
135 UniqueRandomIdGenerator* ssrc_generator,
136 std::function<void(const rtc::scoped_refptr<rtc::RTCCertificate>&)>
137 on_certificate_ready)
138 : signaling_thread_(signaling_thread),
139 session_desc_factory_(channel_manager,
140 &transport_desc_factory_,
141 ssrc_generator),
142 // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
143 // as the session id and session version. To simplify, it should be fine
144 // to just use a random number as session id and start version from
145 // |kInitSessionVersion|.
146 session_version_(kInitSessionVersion),
147 cert_generator_(dtls_enabled ? std::move(cert_generator) : nullptr),
148 sdp_info_(sdp_info),
149 session_id_(session_id),
150 certificate_request_state_(CERTIFICATE_NOT_NEEDED),
151 on_certificate_ready_(on_certificate_ready) {
152 RTC_DCHECK(signaling_thread_);
153
154 if (!dtls_enabled) {
155 SetSdesPolicy(cricket::SEC_REQUIRED);
156 RTC_LOG(LS_VERBOSE) << "DTLS-SRTP disabled.";
157 return;
158 }
159
160 // SRTP-SDES is disabled if DTLS is on.
161 SetSdesPolicy(cricket::SEC_DISABLED);
162 if (certificate) {
163 // Use |certificate|.
164 certificate_request_state_ = CERTIFICATE_WAITING;
165
166 RTC_LOG(LS_VERBOSE) << "DTLS-SRTP enabled; has certificate parameter.";
167 // We already have a certificate but we wait to do |SetIdentity|; if we do
168 // it in the constructor then the caller has not had a chance to connect to
169 // |SignalCertificateReady|.
170 signaling_thread_->Post(
171 RTC_FROM_HERE, this, MSG_USE_CONSTRUCTOR_CERTIFICATE,
172 new rtc::ScopedRefMessageData<rtc::RTCCertificate>(certificate));
173 } else {
174 // Generate certificate.
175 certificate_request_state_ = CERTIFICATE_WAITING;
176
177 rtc::scoped_refptr<WebRtcCertificateGeneratorCallback> callback(
178 new rtc::RefCountedObject<WebRtcCertificateGeneratorCallback>());
179 callback->SignalRequestFailed.connect(
180 this, &WebRtcSessionDescriptionFactory::OnCertificateRequestFailed);
181 callback->SignalCertificateReady.connect(
182 this, &WebRtcSessionDescriptionFactory::SetCertificate);
183
184 rtc::KeyParams key_params = rtc::KeyParams();
185 RTC_LOG(LS_VERBOSE)
186 << "DTLS-SRTP enabled; sending DTLS identity request (key type: "
187 << key_params.type() << ").";
188
189 // Request certificate. This happens asynchronously, so that the caller gets
190 // a chance to connect to |SignalCertificateReady|.
191 cert_generator_->GenerateCertificateAsync(key_params, absl::nullopt,
192 callback);
193 }
194 }
195
~WebRtcSessionDescriptionFactory()196 WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() {
197 RTC_DCHECK_RUN_ON(signaling_thread_);
198
199 // Fail any requests that were asked for before identity generation completed.
200 FailPendingRequests(kFailedDueToSessionShutdown);
201
202 // Process all pending notifications in the message queue. If we don't do
203 // this, requests will linger and not know they succeeded or failed.
204 rtc::MessageList list;
205 signaling_thread_->Clear(this, rtc::MQID_ANY, &list);
206 for (auto& msg : list) {
207 if (msg.message_id != MSG_USE_CONSTRUCTOR_CERTIFICATE) {
208 OnMessage(&msg);
209 } else {
210 // Skip MSG_USE_CONSTRUCTOR_CERTIFICATE because we don't want to trigger
211 // SetIdentity-related callbacks in the destructor. This can be a problem
212 // when WebRtcSession listens to the callback but it was the WebRtcSession
213 // destructor that caused WebRtcSessionDescriptionFactory's destruction.
214 // The callback is then ignored, leaking memory allocated by OnMessage for
215 // MSG_USE_CONSTRUCTOR_CERTIFICATE.
216 delete msg.pdata;
217 }
218 }
219 }
220
CreateOffer(CreateSessionDescriptionObserver * observer,const PeerConnectionInterface::RTCOfferAnswerOptions & options,const cricket::MediaSessionOptions & session_options)221 void WebRtcSessionDescriptionFactory::CreateOffer(
222 CreateSessionDescriptionObserver* observer,
223 const PeerConnectionInterface::RTCOfferAnswerOptions& options,
224 const cricket::MediaSessionOptions& session_options) {
225 RTC_DCHECK_RUN_ON(signaling_thread_);
226 std::string error = "CreateOffer";
227 if (certificate_request_state_ == CERTIFICATE_FAILED) {
228 error += kFailedDueToIdentityFailed;
229 RTC_LOG(LS_ERROR) << error;
230 PostCreateSessionDescriptionFailed(observer, error);
231 return;
232 }
233
234 if (!ValidMediaSessionOptions(session_options)) {
235 error += " called with invalid session options";
236 RTC_LOG(LS_ERROR) << error;
237 PostCreateSessionDescriptionFailed(observer, error);
238 return;
239 }
240
241 CreateSessionDescriptionRequest request(
242 CreateSessionDescriptionRequest::kOffer, observer, session_options);
243 if (certificate_request_state_ == CERTIFICATE_WAITING) {
244 create_session_description_requests_.push(request);
245 } else {
246 RTC_DCHECK(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
247 certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
248 InternalCreateOffer(request);
249 }
250 }
251
CreateAnswer(CreateSessionDescriptionObserver * observer,const cricket::MediaSessionOptions & session_options)252 void WebRtcSessionDescriptionFactory::CreateAnswer(
253 CreateSessionDescriptionObserver* observer,
254 const cricket::MediaSessionOptions& session_options) {
255 std::string error = "CreateAnswer";
256 if (certificate_request_state_ == CERTIFICATE_FAILED) {
257 error += kFailedDueToIdentityFailed;
258 RTC_LOG(LS_ERROR) << error;
259 PostCreateSessionDescriptionFailed(observer, error);
260 return;
261 }
262 if (!sdp_info_->remote_description()) {
263 error += " can't be called before SetRemoteDescription.";
264 RTC_LOG(LS_ERROR) << error;
265 PostCreateSessionDescriptionFailed(observer, error);
266 return;
267 }
268 if (sdp_info_->remote_description()->GetType() != SdpType::kOffer) {
269 error += " failed because remote_description is not an offer.";
270 RTC_LOG(LS_ERROR) << error;
271 PostCreateSessionDescriptionFailed(observer, error);
272 return;
273 }
274
275 if (!ValidMediaSessionOptions(session_options)) {
276 error += " called with invalid session options.";
277 RTC_LOG(LS_ERROR) << error;
278 PostCreateSessionDescriptionFailed(observer, error);
279 return;
280 }
281
282 CreateSessionDescriptionRequest request(
283 CreateSessionDescriptionRequest::kAnswer, observer, session_options);
284 if (certificate_request_state_ == CERTIFICATE_WAITING) {
285 create_session_description_requests_.push(request);
286 } else {
287 RTC_DCHECK(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
288 certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
289 InternalCreateAnswer(request);
290 }
291 }
292
SetSdesPolicy(cricket::SecurePolicy secure_policy)293 void WebRtcSessionDescriptionFactory::SetSdesPolicy(
294 cricket::SecurePolicy secure_policy) {
295 session_desc_factory_.set_secure(secure_policy);
296 }
297
SdesPolicy() const298 cricket::SecurePolicy WebRtcSessionDescriptionFactory::SdesPolicy() const {
299 return session_desc_factory_.secure();
300 }
301
OnMessage(rtc::Message * msg)302 void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) {
303 switch (msg->message_id) {
304 case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
305 CreateSessionDescriptionMsg* param =
306 static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
307 param->observer->OnSuccess(param->description.release());
308 delete param;
309 break;
310 }
311 case MSG_CREATE_SESSIONDESCRIPTION_FAILED: {
312 CreateSessionDescriptionMsg* param =
313 static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
314 param->observer->OnFailure(std::move(param->error));
315 delete param;
316 break;
317 }
318 case MSG_USE_CONSTRUCTOR_CERTIFICATE: {
319 rtc::ScopedRefMessageData<rtc::RTCCertificate>* param =
320 static_cast<rtc::ScopedRefMessageData<rtc::RTCCertificate>*>(
321 msg->pdata);
322 RTC_LOG(LS_INFO) << "Using certificate supplied to the constructor.";
323 SetCertificate(param->data());
324 delete param;
325 break;
326 }
327 default:
328 RTC_NOTREACHED();
329 break;
330 }
331 }
332
InternalCreateOffer(CreateSessionDescriptionRequest request)333 void WebRtcSessionDescriptionFactory::InternalCreateOffer(
334 CreateSessionDescriptionRequest request) {
335 if (sdp_info_->local_description()) {
336 // If the needs-ice-restart flag is set as described by JSEP, we should
337 // generate an offer with a new ufrag/password to trigger an ICE restart.
338 for (cricket::MediaDescriptionOptions& options :
339 request.options.media_description_options) {
340 if (sdp_info_->NeedsIceRestart(options.mid)) {
341 options.transport_options.ice_restart = true;
342 }
343 }
344 }
345
346 std::unique_ptr<cricket::SessionDescription> desc =
347 session_desc_factory_.CreateOffer(
348 request.options, sdp_info_->local_description()
349 ? sdp_info_->local_description()->description()
350 : nullptr);
351 if (!desc) {
352 PostCreateSessionDescriptionFailed(request.observer,
353 "Failed to initialize the offer.");
354 return;
355 }
356
357 // RFC 3264
358 // When issuing an offer that modifies the session,
359 // the "o=" line of the new SDP MUST be identical to that in the
360 // previous SDP, except that the version in the origin field MUST
361 // increment by one from the previous SDP.
362
363 // Just increase the version number by one each time when a new offer
364 // is created regardless if it's identical to the previous one or not.
365 // The |session_version_| is a uint64_t, the wrap around should not happen.
366 RTC_DCHECK(session_version_ + 1 > session_version_);
367 auto offer = std::make_unique<JsepSessionDescription>(
368 SdpType::kOffer, std::move(desc), session_id_,
369 rtc::ToString(session_version_++));
370 if (sdp_info_->local_description()) {
371 for (const cricket::MediaDescriptionOptions& options :
372 request.options.media_description_options) {
373 if (!options.transport_options.ice_restart) {
374 CopyCandidatesFromSessionDescription(sdp_info_->local_description(),
375 options.mid, offer.get());
376 }
377 }
378 }
379 PostCreateSessionDescriptionSucceeded(request.observer, std::move(offer));
380 }
381
InternalCreateAnswer(CreateSessionDescriptionRequest request)382 void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
383 CreateSessionDescriptionRequest request) {
384 if (sdp_info_->remote_description()) {
385 for (cricket::MediaDescriptionOptions& options :
386 request.options.media_description_options) {
387 // According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
388 // an answer should also contain new ICE ufrag and password if an offer
389 // has been received with new ufrag and password.
390 options.transport_options.ice_restart =
391 sdp_info_->IceRestartPending(options.mid);
392 // We should pass the current DTLS role to the transport description
393 // factory, if there is already an existing ongoing session.
394 absl::optional<rtc::SSLRole> dtls_role =
395 sdp_info_->GetDtlsRole(options.mid);
396 if (dtls_role) {
397 options.transport_options.prefer_passive_role =
398 (rtc::SSL_SERVER == *dtls_role);
399 }
400 }
401 }
402
403 std::unique_ptr<cricket::SessionDescription> desc =
404 session_desc_factory_.CreateAnswer(
405 sdp_info_->remote_description()
406 ? sdp_info_->remote_description()->description()
407 : nullptr,
408 request.options,
409 sdp_info_->local_description()
410 ? sdp_info_->local_description()->description()
411 : nullptr);
412 if (!desc) {
413 PostCreateSessionDescriptionFailed(request.observer,
414 "Failed to initialize the answer.");
415 return;
416 }
417
418 // RFC 3264
419 // If the answer is different from the offer in any way (different IP
420 // addresses, ports, etc.), the origin line MUST be different in the answer.
421 // In that case, the version number in the "o=" line of the answer is
422 // unrelated to the version number in the o line of the offer.
423 // Get a new version number by increasing the |session_version_answer_|.
424 // The |session_version_| is a uint64_t, the wrap around should not happen.
425 RTC_DCHECK(session_version_ + 1 > session_version_);
426 auto answer = std::make_unique<JsepSessionDescription>(
427 SdpType::kAnswer, std::move(desc), session_id_,
428 rtc::ToString(session_version_++));
429 if (sdp_info_->local_description()) {
430 // Include all local ICE candidates in the SessionDescription unless
431 // the remote peer has requested an ICE restart.
432 for (const cricket::MediaDescriptionOptions& options :
433 request.options.media_description_options) {
434 if (!options.transport_options.ice_restart) {
435 CopyCandidatesFromSessionDescription(sdp_info_->local_description(),
436 options.mid, answer.get());
437 }
438 }
439 }
440 PostCreateSessionDescriptionSucceeded(request.observer, std::move(answer));
441 }
442
FailPendingRequests(const std::string & reason)443 void WebRtcSessionDescriptionFactory::FailPendingRequests(
444 const std::string& reason) {
445 RTC_DCHECK_RUN_ON(signaling_thread_);
446 while (!create_session_description_requests_.empty()) {
447 const CreateSessionDescriptionRequest& request =
448 create_session_description_requests_.front();
449 PostCreateSessionDescriptionFailed(
450 request.observer,
451 ((request.type == CreateSessionDescriptionRequest::kOffer)
452 ? "CreateOffer"
453 : "CreateAnswer") +
454 reason);
455 create_session_description_requests_.pop();
456 }
457 }
458
PostCreateSessionDescriptionFailed(CreateSessionDescriptionObserver * observer,const std::string & error)459 void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed(
460 CreateSessionDescriptionObserver* observer,
461 const std::string& error) {
462 CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(
463 observer, RTCError(RTCErrorType::INTERNAL_ERROR, std::string(error)));
464 signaling_thread_->Post(RTC_FROM_HERE, this,
465 MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
466 RTC_LOG(LS_ERROR) << "Create SDP failed: " << error;
467 }
468
PostCreateSessionDescriptionSucceeded(CreateSessionDescriptionObserver * observer,std::unique_ptr<SessionDescriptionInterface> description)469 void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded(
470 CreateSessionDescriptionObserver* observer,
471 std::unique_ptr<SessionDescriptionInterface> description) {
472 CreateSessionDescriptionMsg* msg =
473 new CreateSessionDescriptionMsg(observer, RTCError::OK());
474 msg->description = std::move(description);
475 signaling_thread_->Post(RTC_FROM_HERE, this,
476 MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
477 }
478
OnCertificateRequestFailed()479 void WebRtcSessionDescriptionFactory::OnCertificateRequestFailed() {
480 RTC_DCHECK_RUN_ON(signaling_thread_);
481
482 RTC_LOG(LS_ERROR) << "Asynchronous certificate generation request failed.";
483 certificate_request_state_ = CERTIFICATE_FAILED;
484
485 FailPendingRequests(kFailedDueToIdentityFailed);
486 }
487
SetCertificate(const rtc::scoped_refptr<rtc::RTCCertificate> & certificate)488 void WebRtcSessionDescriptionFactory::SetCertificate(
489 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
490 RTC_DCHECK(certificate);
491 RTC_LOG(LS_VERBOSE) << "Setting new certificate.";
492
493 certificate_request_state_ = CERTIFICATE_SUCCEEDED;
494
495 on_certificate_ready_(certificate);
496
497 transport_desc_factory_.set_certificate(certificate);
498 transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
499
500 while (!create_session_description_requests_.empty()) {
501 if (create_session_description_requests_.front().type ==
502 CreateSessionDescriptionRequest::kOffer) {
503 InternalCreateOffer(create_session_description_requests_.front());
504 } else {
505 InternalCreateAnswer(create_session_description_requests_.front());
506 }
507 create_session_description_requests_.pop();
508 }
509 }
510 } // namespace webrtc
511