d83f4eff84
BUG=crbug/464995 R=pthatcher@webrtc.org Committed: https://code.google.com/p/webrtc/source/detail?r=8689 Committed: https://code.google.com/p/webrtc/source/detail?r=8701 Committed: https://code.google.com/p/webrtc/source/detail?r=8706 Review URL: https://webrtc-codereview.appspot.com/42659004 Cr-Commit-Position: refs/heads/master@{#8722} git-svn-id: http://webrtc.googlecode.com/svn/trunk@8722 4adac7df-926f-26a2-2b94-8c16560cd09d
215 lines
6.9 KiB
C++
215 lines
6.9 KiB
C++
/*
|
|
* libjingle
|
|
* Copyright 2015 Google Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "talk/app/webrtc/dtlsidentitystore.h"
|
|
|
|
#include "talk/app/webrtc/webrtcsessiondescriptionfactory.h"
|
|
#include "webrtc/base/logging.h"
|
|
|
|
using webrtc::DTLSIdentityRequestObserver;
|
|
using webrtc::WebRtcSessionDescriptionFactory;
|
|
|
|
namespace webrtc {
|
|
|
|
namespace {
|
|
|
|
enum {
|
|
MSG_GENERATE_IDENTITY,
|
|
MSG_GENERATE_IDENTITY_RESULT,
|
|
MSG_RETURN_FREE_IDENTITY
|
|
};
|
|
|
|
typedef rtc::ScopedMessageData<rtc::SSLIdentity> IdentityResultMessageData;
|
|
|
|
} // namespace
|
|
|
|
// This class runs on the worker thread to generate the identity. It's necessary
|
|
// to separate this class from DtlsIdentityStore so that it can live on the
|
|
// worker thread after DtlsIdentityStore is destroyed.
|
|
class DtlsIdentityStore::WorkerTask : public sigslot::has_slots<>,
|
|
public rtc::MessageHandler {
|
|
public:
|
|
explicit WorkerTask(DtlsIdentityStore* store) : store_(store) {
|
|
store_->SignalDestroyed.connect(this, &WorkerTask::OnStoreDestroyed);
|
|
};
|
|
|
|
virtual ~WorkerTask() {}
|
|
|
|
void GenerateIdentity() {
|
|
rtc::scoped_ptr<rtc::SSLIdentity> identity(
|
|
rtc::SSLIdentity::Generate(DtlsIdentityStore::kIdentityName));
|
|
|
|
{
|
|
rtc::CritScope cs(&cs_);
|
|
if (store_) {
|
|
store_->PostGenerateIdentityResult_w(identity.Pass());
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnMessage(rtc::Message* msg) override {
|
|
DCHECK(msg->message_id == MSG_GENERATE_IDENTITY);
|
|
GenerateIdentity();
|
|
// Deleting msg->pdata will destroy the WorkerTask.
|
|
delete msg->pdata;
|
|
}
|
|
|
|
private:
|
|
void OnStoreDestroyed() {
|
|
rtc::CritScope cs(&cs_);
|
|
store_ = NULL;
|
|
}
|
|
|
|
rtc::CriticalSection cs_;
|
|
DtlsIdentityStore* store_;
|
|
};
|
|
|
|
// Arbitrary constant used as common name for the identity.
|
|
// Chosen to make the certificates more readable.
|
|
const char DtlsIdentityStore::kIdentityName[] = "WebRTC";
|
|
|
|
DtlsIdentityStore::DtlsIdentityStore(rtc::Thread* signaling_thread,
|
|
rtc::Thread* worker_thread)
|
|
: signaling_thread_(signaling_thread),
|
|
worker_thread_(worker_thread),
|
|
pending_jobs_(0) {}
|
|
|
|
DtlsIdentityStore::~DtlsIdentityStore() {
|
|
SignalDestroyed();
|
|
}
|
|
|
|
void DtlsIdentityStore::Initialize() {
|
|
// Do not aggressively generate the free identity if the worker thread and the
|
|
// signaling thread are the same.
|
|
if (worker_thread_ != signaling_thread_) {
|
|
GenerateIdentity();
|
|
}
|
|
}
|
|
|
|
void DtlsIdentityStore::RequestIdentity(DTLSIdentityRequestObserver* observer) {
|
|
DCHECK(rtc::Thread::Current() == signaling_thread_);
|
|
DCHECK(observer);
|
|
|
|
// Must return the free identity async.
|
|
if (free_identity_.get()) {
|
|
IdentityResultMessageData* msg =
|
|
new IdentityResultMessageData(free_identity_.release());
|
|
signaling_thread_->Post(this, MSG_RETURN_FREE_IDENTITY, msg);
|
|
}
|
|
|
|
pending_observers_.push(observer);
|
|
GenerateIdentity();
|
|
}
|
|
|
|
void DtlsIdentityStore::OnMessage(rtc::Message* msg) {
|
|
switch (msg->message_id) {
|
|
case MSG_GENERATE_IDENTITY_RESULT: {
|
|
rtc::scoped_ptr<IdentityResultMessageData> pdata(
|
|
static_cast<IdentityResultMessageData*>(msg->pdata));
|
|
OnIdentityGenerated(pdata->data().Pass());
|
|
break;
|
|
}
|
|
case MSG_RETURN_FREE_IDENTITY: {
|
|
rtc::scoped_ptr<IdentityResultMessageData> pdata(
|
|
static_cast<IdentityResultMessageData*>(msg->pdata));
|
|
ReturnIdentity(pdata->data().Pass());
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool DtlsIdentityStore::HasFreeIdentityForTesting() const {
|
|
return free_identity_.get();
|
|
}
|
|
|
|
void DtlsIdentityStore::GenerateIdentity() {
|
|
pending_jobs_++;
|
|
LOG(LS_VERBOSE) << "New DTLS identity generation is posted, "
|
|
<< "pending_identities=" << pending_jobs_;
|
|
|
|
WorkerTask* task = new WorkerTask(this);
|
|
// The WorkerTask is owned by the message data to make sure it will not be
|
|
// leaked even if the task does not get run.
|
|
IdentityTaskMessageData* msg = new IdentityTaskMessageData(task);
|
|
worker_thread_->Post(task, MSG_GENERATE_IDENTITY, msg);
|
|
}
|
|
|
|
void DtlsIdentityStore::OnIdentityGenerated(
|
|
rtc::scoped_ptr<rtc::SSLIdentity> identity) {
|
|
DCHECK(rtc::Thread::Current() == signaling_thread_);
|
|
|
|
pending_jobs_--;
|
|
LOG(LS_VERBOSE) << "A DTLS identity generation job returned, "
|
|
<< "pending_identities=" << pending_jobs_;
|
|
|
|
if (pending_observers_.empty()) {
|
|
if (!free_identity_.get()) {
|
|
free_identity_.reset(identity.release());
|
|
LOG(LS_VERBOSE) << "A free DTLS identity is saved";
|
|
}
|
|
return;
|
|
}
|
|
ReturnIdentity(identity.Pass());
|
|
}
|
|
|
|
void DtlsIdentityStore::ReturnIdentity(
|
|
rtc::scoped_ptr<rtc::SSLIdentity> identity) {
|
|
DCHECK(!free_identity_.get());
|
|
DCHECK(!pending_observers_.empty());
|
|
|
|
rtc::scoped_refptr<DTLSIdentityRequestObserver> observer =
|
|
pending_observers_.front();
|
|
pending_observers_.pop();
|
|
|
|
if (identity.get()) {
|
|
observer->OnSuccessWithIdentityObj(identity.Pass());
|
|
} else {
|
|
// Pass an arbitrary error code.
|
|
observer->OnFailure(0);
|
|
LOG(LS_WARNING) << "Failed to generate SSL identity";
|
|
}
|
|
|
|
// Do not aggressively generate the free identity if the worker thread and the
|
|
// signaling thread are the same.
|
|
if (worker_thread_ != signaling_thread_ &&
|
|
pending_observers_.empty() &&
|
|
pending_jobs_ == 0) {
|
|
// Generate a free identity in the background.
|
|
GenerateIdentity();
|
|
}
|
|
}
|
|
|
|
void DtlsIdentityStore::PostGenerateIdentityResult_w(
|
|
rtc::scoped_ptr<rtc::SSLIdentity> identity) {
|
|
DCHECK(rtc::Thread::Current() == worker_thread_);
|
|
|
|
IdentityResultMessageData* msg =
|
|
new IdentityResultMessageData(identity.release());
|
|
signaling_thread_->Post(this, MSG_GENERATE_IDENTITY_RESULT, msg);
|
|
}
|
|
} // namespace webrtc
|