1 /*
2  * libjingle
3  * Copyright 2013 Google Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *  1. Redistributions of source code must retain the above copyright notice,
9  *     this list of conditions and the following disclaimer.
10  *  2. Redistributions in binary form must reproduce the above copyright notice,
11  *     this list of conditions and the following disclaimer in the documentation
12  *     and/or other materials provided with the distribution.
13  *  3. The name of the author may not be used to endorse or promote products
14  *     derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "talk/app/webrtc/webrtcsessiondescriptionfactory.h"
29 
30 #include <utility>
31 
32 #include "talk/app/webrtc/dtlsidentitystore.h"
33 #include "talk/app/webrtc/jsep.h"
34 #include "talk/app/webrtc/jsepsessiondescription.h"
35 #include "talk/app/webrtc/mediaconstraintsinterface.h"
36 #include "talk/app/webrtc/webrtcsession.h"
37 #include "webrtc/base/sslidentity.h"
38 
39 using cricket::MediaSessionOptions;
40 
41 namespace webrtc {
42 namespace {
43 static const char kFailedDueToIdentityFailed[] =
44     " failed because DTLS identity request failed";
45 static const char kFailedDueToSessionShutdown[] =
46     " failed because the session was shut down";
47 
48 static const uint64_t kInitSessionVersion = 2;
49 
CompareStream(const MediaSessionOptions::Stream & stream1,const MediaSessionOptions::Stream & stream2)50 static bool CompareStream(const MediaSessionOptions::Stream& stream1,
51                           const MediaSessionOptions::Stream& stream2) {
52   return stream1.id < stream2.id;
53 }
54 
SameId(const MediaSessionOptions::Stream & stream1,const MediaSessionOptions::Stream & stream2)55 static bool SameId(const MediaSessionOptions::Stream& stream1,
56                    const MediaSessionOptions::Stream& stream2) {
57   return stream1.id == stream2.id;
58 }
59 
60 // Checks if each Stream within the |streams| has unique id.
ValidStreams(const MediaSessionOptions::Streams & streams)61 static bool ValidStreams(const MediaSessionOptions::Streams& streams) {
62   MediaSessionOptions::Streams sorted_streams = streams;
63   std::sort(sorted_streams.begin(), sorted_streams.end(), CompareStream);
64   MediaSessionOptions::Streams::iterator it =
65       std::adjacent_find(sorted_streams.begin(), sorted_streams.end(),
66                          SameId);
67   return it == sorted_streams.end();
68 }
69 
70 enum {
71   MSG_CREATE_SESSIONDESCRIPTION_SUCCESS,
72   MSG_CREATE_SESSIONDESCRIPTION_FAILED,
73   MSG_USE_CONSTRUCTOR_CERTIFICATE
74 };
75 
76 struct CreateSessionDescriptionMsg : public rtc::MessageData {
CreateSessionDescriptionMsgwebrtc::__anon1cf350830111::CreateSessionDescriptionMsg77   explicit CreateSessionDescriptionMsg(
78       webrtc::CreateSessionDescriptionObserver* observer)
79       : observer(observer) {
80   }
81 
82   rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserver> observer;
83   std::string error;
84   rtc::scoped_ptr<webrtc::SessionDescriptionInterface> description;
85 };
86 }  // namespace
87 
OnFailure(int error)88 void WebRtcIdentityRequestObserver::OnFailure(int error) {
89   SignalRequestFailed(error);
90 }
91 
OnSuccess(const std::string & der_cert,const std::string & der_private_key)92 void WebRtcIdentityRequestObserver::OnSuccess(
93     const std::string& der_cert, const std::string& der_private_key) {
94   std::string pem_cert = rtc::SSLIdentity::DerToPem(
95       rtc::kPemTypeCertificate,
96       reinterpret_cast<const unsigned char*>(der_cert.data()),
97       der_cert.length());
98   std::string pem_key = rtc::SSLIdentity::DerToPem(
99       rtc::kPemTypeRsaPrivateKey,
100       reinterpret_cast<const unsigned char*>(der_private_key.data()),
101       der_private_key.length());
102   rtc::scoped_ptr<rtc::SSLIdentity> identity(
103       rtc::SSLIdentity::FromPEMStrings(pem_key, pem_cert));
104   SignalCertificateReady(rtc::RTCCertificate::Create(std::move(identity)));
105 }
106 
OnSuccess(rtc::scoped_ptr<rtc::SSLIdentity> identity)107 void WebRtcIdentityRequestObserver::OnSuccess(
108     rtc::scoped_ptr<rtc::SSLIdentity> identity) {
109   SignalCertificateReady(rtc::RTCCertificate::Create(std::move(identity)));
110 }
111 
112 // static
CopyCandidatesFromSessionDescription(const SessionDescriptionInterface * source_desc,SessionDescriptionInterface * dest_desc)113 void WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
114     const SessionDescriptionInterface* source_desc,
115     SessionDescriptionInterface* dest_desc) {
116   if (!source_desc)
117     return;
118   for (size_t m = 0; m < source_desc->number_of_mediasections() &&
119                      m < dest_desc->number_of_mediasections(); ++m) {
120     const IceCandidateCollection* source_candidates =
121         source_desc->candidates(m);
122     const IceCandidateCollection* dest_candidates = dest_desc->candidates(m);
123     for  (size_t n = 0; n < source_candidates->count(); ++n) {
124       const IceCandidateInterface* new_candidate = source_candidates->at(n);
125       if (!dest_candidates->HasCandidate(new_candidate))
126         dest_desc->AddCandidate(source_candidates->at(n));
127     }
128   }
129 }
130 
131 // Private constructor called by other constructors.
WebRtcSessionDescriptionFactory(rtc::Thread * signaling_thread,cricket::ChannelManager * channel_manager,rtc::scoped_ptr<DtlsIdentityStoreInterface> dtls_identity_store,const rtc::scoped_refptr<WebRtcIdentityRequestObserver> & identity_request_observer,WebRtcSession * session,const std::string & session_id,bool dtls_enabled)132 WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
133     rtc::Thread* signaling_thread,
134     cricket::ChannelManager* channel_manager,
135     rtc::scoped_ptr<DtlsIdentityStoreInterface> dtls_identity_store,
136     const rtc::scoped_refptr<WebRtcIdentityRequestObserver>&
137         identity_request_observer,
138     WebRtcSession* session,
139     const std::string& session_id,
140     bool dtls_enabled)
141     : signaling_thread_(signaling_thread),
142       session_desc_factory_(channel_manager, &transport_desc_factory_),
143       // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
144       // as the session id and session version. To simplify, it should be fine
145       // to just use a random number as session id and start version from
146       // |kInitSessionVersion|.
147       session_version_(kInitSessionVersion),
148       dtls_identity_store_(std::move(dtls_identity_store)),
149       identity_request_observer_(identity_request_observer),
150       session_(session),
151       session_id_(session_id),
152       certificate_request_state_(CERTIFICATE_NOT_NEEDED) {
153   session_desc_factory_.set_add_legacy_streams(false);
154   // SRTP-SDES is disabled if DTLS is on.
155   SetSdesPolicy(dtls_enabled ? cricket::SEC_DISABLED : cricket::SEC_REQUIRED);
156 }
157 
WebRtcSessionDescriptionFactory(rtc::Thread * signaling_thread,cricket::ChannelManager * channel_manager,WebRtcSession * session,const std::string & session_id)158 WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
159     rtc::Thread* signaling_thread,
160     cricket::ChannelManager* channel_manager,
161     WebRtcSession* session,
162     const std::string& session_id)
163     : WebRtcSessionDescriptionFactory(signaling_thread,
164                                       channel_manager,
165                                       nullptr,
166                                       nullptr,
167                                       session,
168                                       session_id,
169                                       false) {
170   LOG(LS_VERBOSE) << "DTLS-SRTP disabled.";
171 }
172 
WebRtcSessionDescriptionFactory(rtc::Thread * signaling_thread,cricket::ChannelManager * channel_manager,rtc::scoped_ptr<DtlsIdentityStoreInterface> dtls_identity_store,WebRtcSession * session,const std::string & session_id)173 WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
174     rtc::Thread* signaling_thread,
175     cricket::ChannelManager* channel_manager,
176     rtc::scoped_ptr<DtlsIdentityStoreInterface> dtls_identity_store,
177     WebRtcSession* session,
178     const std::string& session_id)
179     : WebRtcSessionDescriptionFactory(
180           signaling_thread,
181           channel_manager,
182           std::move(dtls_identity_store),
183           new rtc::RefCountedObject<WebRtcIdentityRequestObserver>(),
184           session,
185           session_id,
186           true) {
187   RTC_DCHECK(dtls_identity_store_);
188 
189   certificate_request_state_ = CERTIFICATE_WAITING;
190 
191   identity_request_observer_->SignalRequestFailed.connect(
192       this, &WebRtcSessionDescriptionFactory::OnIdentityRequestFailed);
193   identity_request_observer_->SignalCertificateReady.connect(
194       this, &WebRtcSessionDescriptionFactory::SetCertificate);
195 
196   rtc::KeyType key_type = rtc::KT_DEFAULT;
197   LOG(LS_VERBOSE) << "DTLS-SRTP enabled; sending DTLS identity request (key "
198                   << "type: " << key_type << ").";
199 
200   // Request identity. This happens asynchronously, so the caller will have a
201   // chance to connect to SignalIdentityReady.
202   dtls_identity_store_->RequestIdentity(key_type, identity_request_observer_);
203 }
204 
WebRtcSessionDescriptionFactory(rtc::Thread * signaling_thread,cricket::ChannelManager * channel_manager,const rtc::scoped_refptr<rtc::RTCCertificate> & certificate,WebRtcSession * session,const std::string & session_id)205 WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
206     rtc::Thread* signaling_thread,
207     cricket::ChannelManager* channel_manager,
208     const rtc::scoped_refptr<rtc::RTCCertificate>& certificate,
209     WebRtcSession* session,
210     const std::string& session_id)
211     : WebRtcSessionDescriptionFactory(signaling_thread,
212                                       channel_manager,
213                                       nullptr,
214                                       nullptr,
215                                       session,
216                                       session_id,
217                                       true) {
218   RTC_DCHECK(certificate);
219 
220   certificate_request_state_ = CERTIFICATE_WAITING;
221 
222   LOG(LS_VERBOSE) << "DTLS-SRTP enabled; has certificate parameter.";
223   // We already have a certificate but we wait to do SetIdentity; if we do
224   // it in the constructor then the caller has not had a chance to connect to
225   // SignalIdentityReady.
226   signaling_thread_->Post(
227       this, MSG_USE_CONSTRUCTOR_CERTIFICATE,
228       new rtc::ScopedRefMessageData<rtc::RTCCertificate>(certificate));
229 }
230 
~WebRtcSessionDescriptionFactory()231 WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() {
232   ASSERT(signaling_thread_->IsCurrent());
233 
234   // Fail any requests that were asked for before identity generation completed.
235   FailPendingRequests(kFailedDueToSessionShutdown);
236 
237   // Process all pending notifications in the message queue.  If we don't do
238   // this, requests will linger and not know they succeeded or failed.
239   rtc::MessageList list;
240   signaling_thread_->Clear(this, rtc::MQID_ANY, &list);
241   for (auto& msg : list) {
242     if (msg.message_id != MSG_USE_CONSTRUCTOR_CERTIFICATE) {
243       OnMessage(&msg);
244     } else {
245       // Skip MSG_USE_CONSTRUCTOR_CERTIFICATE because we don't want to trigger
246       // SetIdentity-related callbacks in the destructor. This can be a problem
247       // when WebRtcSession listens to the callback but it was the WebRtcSession
248       // destructor that caused WebRtcSessionDescriptionFactory's destruction.
249       // The callback is then ignored, leaking memory allocated by OnMessage for
250       // MSG_USE_CONSTRUCTOR_CERTIFICATE.
251       delete msg.pdata;
252     }
253   }
254 }
255 
CreateOffer(CreateSessionDescriptionObserver * observer,const PeerConnectionInterface::RTCOfferAnswerOptions & options,const cricket::MediaSessionOptions & session_options)256 void WebRtcSessionDescriptionFactory::CreateOffer(
257     CreateSessionDescriptionObserver* observer,
258     const PeerConnectionInterface::RTCOfferAnswerOptions& options,
259     const cricket::MediaSessionOptions& session_options) {
260   std::string error = "CreateOffer";
261   if (certificate_request_state_ == CERTIFICATE_FAILED) {
262     error += kFailedDueToIdentityFailed;
263     LOG(LS_ERROR) << error;
264     PostCreateSessionDescriptionFailed(observer, error);
265     return;
266   }
267 
268   if (!ValidStreams(session_options.streams)) {
269     error += " called with invalid media streams.";
270     LOG(LS_ERROR) << error;
271     PostCreateSessionDescriptionFailed(observer, error);
272     return;
273   }
274 
275   CreateSessionDescriptionRequest request(
276       CreateSessionDescriptionRequest::kOffer, observer, session_options);
277   if (certificate_request_state_ == CERTIFICATE_WAITING) {
278     create_session_description_requests_.push(request);
279   } else {
280     ASSERT(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
281            certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
282     InternalCreateOffer(request);
283   }
284 }
285 
CreateAnswer(CreateSessionDescriptionObserver * observer,const MediaConstraintsInterface * constraints,const cricket::MediaSessionOptions & session_options)286 void WebRtcSessionDescriptionFactory::CreateAnswer(
287     CreateSessionDescriptionObserver* observer,
288     const MediaConstraintsInterface* constraints,
289     const cricket::MediaSessionOptions& session_options) {
290   std::string error = "CreateAnswer";
291   if (certificate_request_state_ == CERTIFICATE_FAILED) {
292     error += kFailedDueToIdentityFailed;
293     LOG(LS_ERROR) << error;
294     PostCreateSessionDescriptionFailed(observer, error);
295     return;
296   }
297   if (!session_->remote_description()) {
298     error += " can't be called before SetRemoteDescription.";
299     LOG(LS_ERROR) << error;
300     PostCreateSessionDescriptionFailed(observer, error);
301     return;
302   }
303   if (session_->remote_description()->type() !=
304       JsepSessionDescription::kOffer) {
305     error += " failed because remote_description is not an offer.";
306     LOG(LS_ERROR) << error;
307     PostCreateSessionDescriptionFailed(observer, error);
308     return;
309   }
310 
311   if (!ValidStreams(session_options.streams)) {
312     error += " called with invalid media streams.";
313     LOG(LS_ERROR) << error;
314     PostCreateSessionDescriptionFailed(observer, error);
315     return;
316   }
317 
318   CreateSessionDescriptionRequest request(
319       CreateSessionDescriptionRequest::kAnswer, observer, session_options);
320   if (certificate_request_state_ == CERTIFICATE_WAITING) {
321     create_session_description_requests_.push(request);
322   } else {
323     ASSERT(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
324            certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
325     InternalCreateAnswer(request);
326   }
327 }
328 
SetSdesPolicy(cricket::SecurePolicy secure_policy)329 void WebRtcSessionDescriptionFactory::SetSdesPolicy(
330     cricket::SecurePolicy secure_policy) {
331   session_desc_factory_.set_secure(secure_policy);
332 }
333 
SdesPolicy() const334 cricket::SecurePolicy WebRtcSessionDescriptionFactory::SdesPolicy() const {
335   return session_desc_factory_.secure();
336 }
337 
OnMessage(rtc::Message * msg)338 void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) {
339   switch (msg->message_id) {
340     case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
341       CreateSessionDescriptionMsg* param =
342           static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
343       param->observer->OnSuccess(param->description.release());
344       delete param;
345       break;
346     }
347     case MSG_CREATE_SESSIONDESCRIPTION_FAILED: {
348       CreateSessionDescriptionMsg* param =
349           static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
350       param->observer->OnFailure(param->error);
351       delete param;
352       break;
353     }
354     case MSG_USE_CONSTRUCTOR_CERTIFICATE: {
355       rtc::ScopedRefMessageData<rtc::RTCCertificate>* param =
356           static_cast<rtc::ScopedRefMessageData<rtc::RTCCertificate>*>(
357               msg->pdata);
358       LOG(LS_INFO) << "Using certificate supplied to the constructor.";
359       SetCertificate(param->data());
360       delete param;
361       break;
362     }
363     default:
364       ASSERT(false);
365       break;
366   }
367 }
368 
InternalCreateOffer(CreateSessionDescriptionRequest request)369 void WebRtcSessionDescriptionFactory::InternalCreateOffer(
370     CreateSessionDescriptionRequest request) {
371   cricket::SessionDescription* desc(session_desc_factory_.CreateOffer(
372       request.options, session_->local_description()
373                            ? session_->local_description()->description()
374                            : nullptr));
375   // RFC 3264
376   // When issuing an offer that modifies the session,
377   // the "o=" line of the new SDP MUST be identical to that in the
378   // previous SDP, except that the version in the origin field MUST
379   // increment by one from the previous SDP.
380 
381   // Just increase the version number by one each time when a new offer
382   // is created regardless if it's identical to the previous one or not.
383   // The |session_version_| is a uint64_t, the wrap around should not happen.
384   ASSERT(session_version_ + 1 > session_version_);
385   JsepSessionDescription* offer(new JsepSessionDescription(
386       JsepSessionDescription::kOffer));
387   if (!offer->Initialize(desc, session_id_,
388                          rtc::ToString(session_version_++))) {
389     delete offer;
390     PostCreateSessionDescriptionFailed(request.observer,
391                                        "Failed to initialize the offer.");
392     return;
393   }
394   if (session_->local_description() &&
395       !request.options.audio_transport_options.ice_restart &&
396       !request.options.video_transport_options.ice_restart &&
397       !request.options.data_transport_options.ice_restart) {
398     // Include all local ice candidates in the SessionDescription unless
399     // the an ice restart has been requested.
400     CopyCandidatesFromSessionDescription(session_->local_description(), offer);
401   }
402   PostCreateSessionDescriptionSucceeded(request.observer, offer);
403 }
404 
InternalCreateAnswer(CreateSessionDescriptionRequest request)405 void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
406     CreateSessionDescriptionRequest request) {
407   // According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
408   // an answer should also contain new ice ufrag and password if an offer has
409   // been received with new ufrag and password.
410   request.options.audio_transport_options.ice_restart =
411       session_->IceRestartPending();
412   request.options.video_transport_options.ice_restart =
413       session_->IceRestartPending();
414   request.options.data_transport_options.ice_restart =
415       session_->IceRestartPending();
416   // We should pass current ssl role to the transport description factory, if
417   // there is already an existing ongoing session.
418   rtc::SSLRole ssl_role;
419   if (session_->GetSslRole(session_->voice_channel(), &ssl_role)) {
420     request.options.audio_transport_options.prefer_passive_role =
421         (rtc::SSL_SERVER == ssl_role);
422   }
423   if (session_->GetSslRole(session_->video_channel(), &ssl_role)) {
424     request.options.video_transport_options.prefer_passive_role =
425         (rtc::SSL_SERVER == ssl_role);
426   }
427   if (session_->GetSslRole(session_->data_channel(), &ssl_role)) {
428     request.options.data_transport_options.prefer_passive_role =
429         (rtc::SSL_SERVER == ssl_role);
430   }
431 
432   cricket::SessionDescription* desc(session_desc_factory_.CreateAnswer(
433       session_->remote_description()
434           ? session_->remote_description()->description()
435           : nullptr,
436       request.options, session_->local_description()
437                            ? session_->local_description()->description()
438                            : nullptr));
439   // RFC 3264
440   // If the answer is different from the offer in any way (different IP
441   // addresses, ports, etc.), the origin line MUST be different in the answer.
442   // In that case, the version number in the "o=" line of the answer is
443   // unrelated to the version number in the o line of the offer.
444   // Get a new version number by increasing the |session_version_answer_|.
445   // The |session_version_| is a uint64_t, the wrap around should not happen.
446   ASSERT(session_version_ + 1 > session_version_);
447   JsepSessionDescription* answer(new JsepSessionDescription(
448       JsepSessionDescription::kAnswer));
449   if (!answer->Initialize(desc, session_id_,
450                           rtc::ToString(session_version_++))) {
451     delete answer;
452     PostCreateSessionDescriptionFailed(request.observer,
453                                        "Failed to initialize the answer.");
454     return;
455   }
456   if (session_->local_description() &&
457       !request.options.audio_transport_options.ice_restart &&
458       !request.options.video_transport_options.ice_restart &&
459       !request.options.data_transport_options.ice_restart) {
460     // Include all local ice candidates in the SessionDescription unless
461     // the remote peer has requested an ice restart.
462     CopyCandidatesFromSessionDescription(session_->local_description(), answer);
463   }
464   session_->ResetIceRestartLatch();
465   PostCreateSessionDescriptionSucceeded(request.observer, answer);
466 }
467 
FailPendingRequests(const std::string & reason)468 void WebRtcSessionDescriptionFactory::FailPendingRequests(
469     const std::string& reason) {
470   ASSERT(signaling_thread_->IsCurrent());
471   while (!create_session_description_requests_.empty()) {
472     const CreateSessionDescriptionRequest& request =
473         create_session_description_requests_.front();
474     PostCreateSessionDescriptionFailed(request.observer,
475         ((request.type == CreateSessionDescriptionRequest::kOffer) ?
476             "CreateOffer" : "CreateAnswer") + reason);
477     create_session_description_requests_.pop();
478   }
479 }
480 
PostCreateSessionDescriptionFailed(CreateSessionDescriptionObserver * observer,const std::string & error)481 void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed(
482     CreateSessionDescriptionObserver* observer, const std::string& error) {
483   CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
484   msg->error = error;
485   signaling_thread_->Post(this, MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
486   LOG(LS_ERROR) << "Create SDP failed: " << error;
487 }
488 
PostCreateSessionDescriptionSucceeded(CreateSessionDescriptionObserver * observer,SessionDescriptionInterface * description)489 void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded(
490     CreateSessionDescriptionObserver* observer,
491     SessionDescriptionInterface* description) {
492   CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
493   msg->description.reset(description);
494   signaling_thread_->Post(this, MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
495 }
496 
OnIdentityRequestFailed(int error)497 void WebRtcSessionDescriptionFactory::OnIdentityRequestFailed(int error) {
498   ASSERT(signaling_thread_->IsCurrent());
499 
500   LOG(LS_ERROR) << "Async identity request failed: error = " << error;
501   certificate_request_state_ = CERTIFICATE_FAILED;
502 
503   FailPendingRequests(kFailedDueToIdentityFailed);
504 }
505 
SetCertificate(const rtc::scoped_refptr<rtc::RTCCertificate> & certificate)506 void WebRtcSessionDescriptionFactory::SetCertificate(
507     const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
508   RTC_DCHECK(certificate);
509   LOG(LS_VERBOSE) << "Setting new certificate";
510 
511   certificate_request_state_ = CERTIFICATE_SUCCEEDED;
512   SignalCertificateReady(certificate);
513 
514   transport_desc_factory_.set_certificate(certificate);
515   transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
516 
517   while (!create_session_description_requests_.empty()) {
518     if (create_session_description_requests_.front().type ==
519         CreateSessionDescriptionRequest::kOffer) {
520       InternalCreateOffer(create_session_description_requests_.front());
521     } else {
522       InternalCreateAnswer(create_session_description_requests_.front());
523     }
524     create_session_description_requests_.pop();
525   }
526 }
527 }  // namespace webrtc
528