blob: 4cc348d0c38e14fcdd9cac0ab38c11f28aef6c82 [file] [log] [blame]
/*
* 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);
};
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";
}
if (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