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