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