2013-07-10 02:45:36 +02:00
|
|
|
/*
|
|
|
|
* libjingle
|
|
|
|
* Copyright 2011, Google Inc.
|
|
|
|
* Copyright 2011, RTFM, 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/p2p/base/dtlstransportchannel.h"
|
|
|
|
|
|
|
|
#include "talk/base/buffer.h"
|
2013-09-23 22:34:45 +02:00
|
|
|
#include "talk/base/dscp.h"
|
2013-07-10 02:45:36 +02:00
|
|
|
#include "talk/base/messagequeue.h"
|
|
|
|
#include "talk/base/stream.h"
|
|
|
|
#include "talk/base/sslstreamadapter.h"
|
|
|
|
#include "talk/base/thread.h"
|
|
|
|
#include "talk/p2p/base/common.h"
|
|
|
|
|
|
|
|
namespace cricket {
|
|
|
|
|
|
|
|
// We don't pull the RTP constants from rtputils.h, to avoid a layer violation.
|
|
|
|
static const size_t kDtlsRecordHeaderLen = 13;
|
|
|
|
static const size_t kMaxDtlsPacketLen = 2048;
|
|
|
|
static const size_t kMinRtpPacketLen = 12;
|
|
|
|
|
|
|
|
static bool IsDtlsPacket(const char* data, size_t len) {
|
|
|
|
const uint8* u = reinterpret_cast<const uint8*>(data);
|
|
|
|
return (len >= kDtlsRecordHeaderLen && (u[0] > 19 && u[0] < 64));
|
|
|
|
}
|
|
|
|
static bool IsRtpPacket(const char* data, size_t len) {
|
|
|
|
const uint8* u = reinterpret_cast<const uint8*>(data);
|
|
|
|
return (len >= kMinRtpPacketLen && (u[0] & 0xC0) == 0x80);
|
|
|
|
}
|
|
|
|
|
|
|
|
talk_base::StreamResult StreamInterfaceChannel::Read(void* buffer,
|
|
|
|
size_t buffer_len,
|
|
|
|
size_t* read,
|
|
|
|
int* error) {
|
|
|
|
if (state_ == talk_base::SS_CLOSED)
|
|
|
|
return talk_base::SR_EOS;
|
|
|
|
if (state_ == talk_base::SS_OPENING)
|
|
|
|
return talk_base::SR_BLOCK;
|
|
|
|
|
|
|
|
return fifo_.Read(buffer, buffer_len, read, error);
|
|
|
|
}
|
|
|
|
|
|
|
|
talk_base::StreamResult StreamInterfaceChannel::Write(const void* data,
|
|
|
|
size_t data_len,
|
|
|
|
size_t* written,
|
|
|
|
int* error) {
|
|
|
|
// Always succeeds, since this is an unreliable transport anyway.
|
|
|
|
// TODO: Should this block if channel_'s temporarily unwritable?
|
2014-02-14 01:56:12 +01:00
|
|
|
talk_base::PacketOptions packet_options;
|
|
|
|
channel_->SendPacket(static_cast<const char*>(data), data_len,
|
|
|
|
packet_options);
|
2013-07-10 02:45:36 +02:00
|
|
|
if (written) {
|
|
|
|
*written = data_len;
|
|
|
|
}
|
|
|
|
return talk_base::SR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool StreamInterfaceChannel::OnPacketReceived(const char* data, size_t size) {
|
|
|
|
// We force a read event here to ensure that we don't overflow our FIFO.
|
|
|
|
// Under high packet rate this can occur if we wait for the FIFO to post its
|
|
|
|
// own SE_READ.
|
|
|
|
bool ret = (fifo_.WriteAll(data, size, NULL, NULL) == talk_base::SR_SUCCESS);
|
|
|
|
if (ret) {
|
|
|
|
SignalEvent(this, talk_base::SE_READ, 0);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void StreamInterfaceChannel::OnEvent(talk_base::StreamInterface* stream,
|
|
|
|
int sig, int err) {
|
|
|
|
SignalEvent(this, sig, err);
|
|
|
|
}
|
|
|
|
|
|
|
|
DtlsTransportChannelWrapper::DtlsTransportChannelWrapper(
|
|
|
|
Transport* transport,
|
|
|
|
TransportChannelImpl* channel)
|
|
|
|
: TransportChannelImpl(channel->content_name(), channel->component()),
|
|
|
|
transport_(transport),
|
|
|
|
worker_thread_(talk_base::Thread::Current()),
|
|
|
|
channel_(channel),
|
|
|
|
downward_(NULL),
|
|
|
|
dtls_state_(STATE_NONE),
|
|
|
|
local_identity_(NULL),
|
2013-08-24 01:21:25 +02:00
|
|
|
ssl_role_(talk_base::SSL_CLIENT) {
|
2013-07-10 02:45:36 +02:00
|
|
|
channel_->SignalReadableState.connect(this,
|
|
|
|
&DtlsTransportChannelWrapper::OnReadableState);
|
|
|
|
channel_->SignalWritableState.connect(this,
|
|
|
|
&DtlsTransportChannelWrapper::OnWritableState);
|
|
|
|
channel_->SignalReadPacket.connect(this,
|
|
|
|
&DtlsTransportChannelWrapper::OnReadPacket);
|
|
|
|
channel_->SignalReadyToSend.connect(this,
|
|
|
|
&DtlsTransportChannelWrapper::OnReadyToSend);
|
|
|
|
channel_->SignalRequestSignaling.connect(this,
|
|
|
|
&DtlsTransportChannelWrapper::OnRequestSignaling);
|
|
|
|
channel_->SignalCandidateReady.connect(this,
|
|
|
|
&DtlsTransportChannelWrapper::OnCandidateReady);
|
|
|
|
channel_->SignalCandidatesAllocationDone.connect(this,
|
|
|
|
&DtlsTransportChannelWrapper::OnCandidatesAllocationDone);
|
|
|
|
channel_->SignalRoleConflict.connect(this,
|
|
|
|
&DtlsTransportChannelWrapper::OnRoleConflict);
|
|
|
|
channel_->SignalRouteChange.connect(this,
|
|
|
|
&DtlsTransportChannelWrapper::OnRouteChange);
|
2014-02-14 01:56:12 +01:00
|
|
|
channel_->SignalConnectionRemoved.connect(this,
|
|
|
|
&DtlsTransportChannelWrapper::OnConnectionRemoved);
|
2013-07-10 02:45:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
DtlsTransportChannelWrapper::~DtlsTransportChannelWrapper() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void DtlsTransportChannelWrapper::Connect() {
|
|
|
|
// We should only get a single call to Connect.
|
|
|
|
ASSERT(dtls_state_ == STATE_NONE ||
|
|
|
|
dtls_state_ == STATE_OFFERED ||
|
|
|
|
dtls_state_ == STATE_ACCEPTED);
|
|
|
|
channel_->Connect();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DtlsTransportChannelWrapper::Reset() {
|
|
|
|
channel_->Reset();
|
|
|
|
set_writable(false);
|
|
|
|
set_readable(false);
|
|
|
|
|
|
|
|
// Re-call SetupDtls()
|
|
|
|
if (!SetupDtls()) {
|
|
|
|
LOG_J(LS_ERROR, this) << "Error re-initializing DTLS";
|
|
|
|
dtls_state_ = STATE_CLOSED;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
dtls_state_ = STATE_ACCEPTED;
|
|
|
|
}
|
|
|
|
|
2013-08-12 23:18:15 +02:00
|
|
|
bool DtlsTransportChannelWrapper::SetLocalIdentity(
|
|
|
|
talk_base::SSLIdentity* identity) {
|
|
|
|
if (dtls_state_ == STATE_OPEN && identity == local_identity_) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-10 02:45:36 +02:00
|
|
|
// TODO(ekr@rtfm.com): Forbid this if Connect() has been called.
|
|
|
|
if (dtls_state_ != STATE_NONE) {
|
|
|
|
LOG_J(LS_ERROR, this) << "Can't set DTLS local identity in this state";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (identity) {
|
|
|
|
local_identity_ = identity;
|
|
|
|
dtls_state_ = STATE_OFFERED;
|
|
|
|
} else {
|
|
|
|
LOG_J(LS_INFO, this) << "NULL DTLS identity supplied. Not doing DTLS";
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-09 17:37:36 +02:00
|
|
|
bool DtlsTransportChannelWrapper::GetLocalIdentity(
|
|
|
|
talk_base::SSLIdentity** identity) const {
|
|
|
|
if (!local_identity_)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
*identity = local_identity_->GetReference();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-08-24 01:21:25 +02:00
|
|
|
bool DtlsTransportChannelWrapper::SetSslRole(talk_base::SSLRole role) {
|
|
|
|
if (dtls_state_ == STATE_OPEN) {
|
|
|
|
if (ssl_role_ != role) {
|
|
|
|
LOG(LS_ERROR) << "SSL Role can't be reversed after the session is setup.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2013-07-10 02:45:36 +02:00
|
|
|
|
2013-08-24 01:21:25 +02:00
|
|
|
ssl_role_ = role;
|
|
|
|
return true;
|
|
|
|
}
|
2013-07-10 02:45:36 +02:00
|
|
|
|
2013-08-24 01:21:25 +02:00
|
|
|
bool DtlsTransportChannelWrapper::GetSslRole(talk_base::SSLRole* role) const {
|
|
|
|
*role = ssl_role_;
|
|
|
|
return true;
|
2013-07-10 02:45:36 +02:00
|
|
|
}
|
|
|
|
|
2013-08-12 23:18:15 +02:00
|
|
|
bool DtlsTransportChannelWrapper::SetRemoteFingerprint(
|
|
|
|
const std::string& digest_alg,
|
|
|
|
const uint8* digest,
|
|
|
|
size_t digest_len) {
|
|
|
|
|
|
|
|
talk_base::Buffer remote_fingerprint_value(digest, digest_len);
|
|
|
|
|
|
|
|
if ((dtls_state_ == STATE_OPEN) &&
|
|
|
|
(remote_fingerprint_value_ == remote_fingerprint_value)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-10 02:45:36 +02:00
|
|
|
// Allow SetRemoteFingerprint with a NULL digest even if SetLocalIdentity
|
|
|
|
// hasn't been called.
|
|
|
|
if (dtls_state_ > STATE_OFFERED ||
|
|
|
|
(dtls_state_ == STATE_NONE && !digest_alg.empty())) {
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_ERROR, this) << "Can't set DTLS remote settings in this state.";
|
2013-07-10 02:45:36 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (digest_alg.empty()) {
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_INFO, this) << "Other side didn't support DTLS.";
|
2013-07-10 02:45:36 +02:00
|
|
|
dtls_state_ = STATE_NONE;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// At this point we know we are doing DTLS
|
2013-08-12 23:18:15 +02:00
|
|
|
remote_fingerprint_value.TransferTo(&remote_fingerprint_value_);
|
2013-07-10 02:45:36 +02:00
|
|
|
remote_fingerprint_algorithm_ = digest_alg;
|
|
|
|
|
|
|
|
if (!SetupDtls()) {
|
|
|
|
dtls_state_ = STATE_CLOSED;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
dtls_state_ = STATE_ACCEPTED;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-09 17:37:36 +02:00
|
|
|
bool DtlsTransportChannelWrapper::GetRemoteCertificate(
|
|
|
|
talk_base::SSLCertificate** cert) const {
|
|
|
|
if (!dtls_)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return dtls_->GetPeerCertificate(cert);
|
|
|
|
}
|
|
|
|
|
2013-07-10 02:45:36 +02:00
|
|
|
bool DtlsTransportChannelWrapper::SetupDtls() {
|
|
|
|
StreamInterfaceChannel* downward =
|
|
|
|
new StreamInterfaceChannel(worker_thread_, channel_);
|
|
|
|
|
|
|
|
dtls_.reset(talk_base::SSLStreamAdapter::Create(downward));
|
|
|
|
if (!dtls_) {
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_ERROR, this) << "Failed to create DTLS adapter.";
|
2013-07-10 02:45:36 +02:00
|
|
|
delete downward;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
downward_ = downward;
|
|
|
|
|
|
|
|
dtls_->SetIdentity(local_identity_->GetReference());
|
|
|
|
dtls_->SetMode(talk_base::SSL_MODE_DTLS);
|
2013-08-24 01:21:25 +02:00
|
|
|
dtls_->SetServerRole(ssl_role_);
|
2013-07-10 02:45:36 +02:00
|
|
|
dtls_->SignalEvent.connect(this, &DtlsTransportChannelWrapper::OnDtlsEvent);
|
|
|
|
if (!dtls_->SetPeerCertificateDigest(
|
|
|
|
remote_fingerprint_algorithm_,
|
|
|
|
reinterpret_cast<unsigned char *>(remote_fingerprint_value_.data()),
|
|
|
|
remote_fingerprint_value_.length())) {
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_ERROR, this) << "Couldn't set DTLS certificate digest.";
|
2013-07-10 02:45:36 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set up DTLS-SRTP, if it's been enabled.
|
|
|
|
if (!srtp_ciphers_.empty()) {
|
|
|
|
if (!dtls_->SetDtlsSrtpCiphers(srtp_ciphers_)) {
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_ERROR, this) << "Couldn't set DTLS-SRTP ciphers.";
|
2013-07-10 02:45:36 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_INFO, this) << "Not using DTLS.";
|
2013-07-10 02:45:36 +02:00
|
|
|
}
|
|
|
|
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_INFO, this) << "DTLS setup complete.";
|
2013-07-10 02:45:36 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-13 19:18:27 +02:00
|
|
|
bool DtlsTransportChannelWrapper::SetSrtpCiphers(
|
|
|
|
const std::vector<std::string>& ciphers) {
|
|
|
|
if (srtp_ciphers_ == ciphers)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (dtls_state_ == STATE_OPEN) {
|
|
|
|
// We don't support DTLS renegotiation currently. If new set of srtp ciphers
|
|
|
|
// are different than what's being used currently, we will not use it.
|
|
|
|
// So for now, let's be happy (or sad) with a warning message.
|
|
|
|
std::string current_srtp_cipher;
|
|
|
|
if (!dtls_->GetDtlsSrtpCipher(¤t_srtp_cipher)) {
|
|
|
|
LOG(LS_ERROR) << "Failed to get the current SRTP cipher for DTLS channel";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
const std::vector<std::string>::const_iterator iter =
|
|
|
|
std::find(ciphers.begin(), ciphers.end(), current_srtp_cipher);
|
|
|
|
if (iter == ciphers.end()) {
|
|
|
|
std::string requested_str;
|
|
|
|
for (size_t i = 0; i < ciphers.size(); ++i) {
|
|
|
|
requested_str.append(" ");
|
|
|
|
requested_str.append(ciphers[i]);
|
|
|
|
requested_str.append(" ");
|
|
|
|
}
|
|
|
|
LOG(LS_WARNING) << "Ignoring new set of SRTP ciphers, as DTLS "
|
|
|
|
<< "renegotiation is not supported currently "
|
|
|
|
<< "current cipher = " << current_srtp_cipher << " and "
|
|
|
|
<< "requested = " << "[" << requested_str << "]";
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-07-10 02:45:36 +02:00
|
|
|
if (dtls_state_ != STATE_NONE &&
|
|
|
|
dtls_state_ != STATE_OFFERED &&
|
|
|
|
dtls_state_ != STATE_ACCEPTED) {
|
|
|
|
ASSERT(false);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
srtp_ciphers_ = ciphers;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DtlsTransportChannelWrapper::GetSrtpCipher(std::string* cipher) {
|
|
|
|
if (dtls_state_ != STATE_OPEN) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return dtls_->GetDtlsSrtpCipher(cipher);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Called from upper layers to send a media packet.
|
2014-02-14 01:56:12 +01:00
|
|
|
int DtlsTransportChannelWrapper::SendPacket(
|
|
|
|
const char* data, size_t size,
|
|
|
|
const talk_base::PacketOptions& options, int flags) {
|
2013-07-10 02:45:36 +02:00
|
|
|
int result = -1;
|
|
|
|
|
|
|
|
switch (dtls_state_) {
|
|
|
|
case STATE_OFFERED:
|
|
|
|
// We don't know if we are doing DTLS yet, so we can't send a packet.
|
|
|
|
// TODO(ekr@rtfm.com): assert here?
|
|
|
|
result = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_STARTED:
|
|
|
|
case STATE_ACCEPTED:
|
|
|
|
// Can't send data until the connection is active
|
|
|
|
result = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_OPEN:
|
|
|
|
if (flags & PF_SRTP_BYPASS) {
|
|
|
|
ASSERT(!srtp_ciphers_.empty());
|
|
|
|
if (!IsRtpPacket(data, size)) {
|
|
|
|
result = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2014-02-14 01:56:12 +01:00
|
|
|
result = channel_->SendPacket(data, size, options);
|
2013-07-10 02:45:36 +02:00
|
|
|
} else {
|
|
|
|
result = (dtls_->WriteAll(data, size, NULL, NULL) ==
|
|
|
|
talk_base::SR_SUCCESS) ? static_cast<int>(size) : -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
// Not doing DTLS.
|
|
|
|
case STATE_NONE:
|
2014-02-14 01:56:12 +01:00
|
|
|
result = channel_->SendPacket(data, size, options);
|
2013-07-10 02:45:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_CLOSED: // Can't send anything when we're closed.
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The state transition logic here is as follows:
|
|
|
|
// (1) If we're not doing DTLS-SRTP, then the state is just the
|
|
|
|
// state of the underlying impl()
|
|
|
|
// (2) If we're doing DTLS-SRTP:
|
|
|
|
// - Prior to the DTLS handshake, the state is neither readable or
|
|
|
|
// writable
|
|
|
|
// - When the impl goes writable for the first time we
|
|
|
|
// start the DTLS handshake
|
|
|
|
// - Once the DTLS handshake completes, the state is that of the
|
|
|
|
// impl again
|
|
|
|
void DtlsTransportChannelWrapper::OnReadableState(TransportChannel* channel) {
|
|
|
|
ASSERT(talk_base::Thread::Current() == worker_thread_);
|
|
|
|
ASSERT(channel == channel_);
|
|
|
|
LOG_J(LS_VERBOSE, this)
|
2013-08-24 01:21:25 +02:00
|
|
|
<< "DTLSTransportChannelWrapper: channel readable state changed.";
|
2013-07-10 02:45:36 +02:00
|
|
|
|
|
|
|
if (dtls_state_ == STATE_NONE || dtls_state_ == STATE_OPEN) {
|
|
|
|
set_readable(channel_->readable());
|
|
|
|
// Note: SignalReadableState fired by set_readable.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DtlsTransportChannelWrapper::OnWritableState(TransportChannel* channel) {
|
|
|
|
ASSERT(talk_base::Thread::Current() == worker_thread_);
|
|
|
|
ASSERT(channel == channel_);
|
|
|
|
LOG_J(LS_VERBOSE, this)
|
2013-08-24 01:21:25 +02:00
|
|
|
<< "DTLSTransportChannelWrapper: channel writable state changed.";
|
2013-07-10 02:45:36 +02:00
|
|
|
|
|
|
|
switch (dtls_state_) {
|
|
|
|
case STATE_NONE:
|
|
|
|
case STATE_OPEN:
|
|
|
|
set_writable(channel_->writable());
|
|
|
|
// Note: SignalWritableState fired by set_writable.
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_OFFERED:
|
|
|
|
// Do nothing
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_ACCEPTED:
|
|
|
|
if (!MaybeStartDtls()) {
|
|
|
|
// This should never happen:
|
|
|
|
// Because we are operating in a nonblocking mode and all
|
|
|
|
// incoming packets come in via OnReadPacket(), which rejects
|
|
|
|
// packets in this state, the incoming queue must be empty. We
|
|
|
|
// ignore write errors, thus any errors must be because of
|
|
|
|
// configuration and therefore are our fault.
|
|
|
|
// Note that in non-debug configurations, failure in
|
|
|
|
// MaybeStartDtls() changes the state to STATE_CLOSED.
|
|
|
|
ASSERT(false);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_STARTED:
|
|
|
|
// Do nothing
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_CLOSED:
|
|
|
|
// Should not happen. Do nothing
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-13 01:21:03 +01:00
|
|
|
void DtlsTransportChannelWrapper::OnReadPacket(
|
|
|
|
TransportChannel* channel, const char* data, size_t size,
|
|
|
|
const talk_base::PacketTime& packet_time, int flags) {
|
2013-07-10 02:45:36 +02:00
|
|
|
ASSERT(talk_base::Thread::Current() == worker_thread_);
|
|
|
|
ASSERT(channel == channel_);
|
|
|
|
ASSERT(flags == 0);
|
|
|
|
|
|
|
|
switch (dtls_state_) {
|
|
|
|
case STATE_NONE:
|
|
|
|
// We are not doing DTLS
|
2013-12-13 01:21:03 +01:00
|
|
|
SignalReadPacket(this, data, size, packet_time, 0);
|
2013-07-10 02:45:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_OFFERED:
|
|
|
|
// Currently drop the packet, but we might in future
|
|
|
|
// decide to take this as evidence that the other
|
|
|
|
// side is ready to do DTLS and start the handshake
|
|
|
|
// on our end
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_WARNING, this) << "Received packet before we know if we are "
|
|
|
|
<< "doing DTLS or not; dropping.";
|
2013-07-10 02:45:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_ACCEPTED:
|
|
|
|
// Drop packets received before DTLS has actually started
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_INFO, this) << "Dropping packet received before DTLS started.";
|
2013-07-10 02:45:36 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_STARTED:
|
|
|
|
case STATE_OPEN:
|
|
|
|
// We should only get DTLS or SRTP packets; STUN's already been demuxed.
|
|
|
|
// Is this potentially a DTLS packet?
|
|
|
|
if (IsDtlsPacket(data, size)) {
|
|
|
|
if (!HandleDtlsPacket(data, size)) {
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_ERROR, this) << "Failed to handle DTLS packet.";
|
2013-07-10 02:45:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Not a DTLS packet; our handshake should be complete by now.
|
|
|
|
if (dtls_state_ != STATE_OPEN) {
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_ERROR, this) << "Received non-DTLS packet before DTLS "
|
|
|
|
<< "complete.";
|
2013-07-10 02:45:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// And it had better be a SRTP packet.
|
|
|
|
if (!IsRtpPacket(data, size)) {
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_ERROR, this) << "Received unexpected non-DTLS packet.";
|
2013-07-10 02:45:36 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sanity check.
|
|
|
|
ASSERT(!srtp_ciphers_.empty());
|
|
|
|
|
|
|
|
// Signal this upwards as a bypass packet.
|
2013-12-13 01:21:03 +01:00
|
|
|
SignalReadPacket(this, data, size, packet_time, PF_SRTP_BYPASS);
|
2013-07-10 02:45:36 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case STATE_CLOSED:
|
|
|
|
// This shouldn't be happening. Drop the packet
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DtlsTransportChannelWrapper::OnReadyToSend(TransportChannel* channel) {
|
|
|
|
if (writable()) {
|
|
|
|
SignalReadyToSend(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DtlsTransportChannelWrapper::OnDtlsEvent(talk_base::StreamInterface* dtls,
|
|
|
|
int sig, int err) {
|
|
|
|
ASSERT(talk_base::Thread::Current() == worker_thread_);
|
|
|
|
ASSERT(dtls == dtls_.get());
|
|
|
|
if (sig & talk_base::SE_OPEN) {
|
|
|
|
// This is the first time.
|
2013-08-24 01:21:25 +02:00
|
|
|
LOG_J(LS_INFO, this) << "DTLS handshake complete.";
|
2013-07-10 02:45:36 +02:00
|
|
|
if (dtls_->GetState() == talk_base::SS_OPEN) {
|
|
|
|
// The check for OPEN shouldn't be necessary but let's make
|
|
|
|
// sure we don't accidentally frob the state if it's closed.
|
|
|
|
dtls_state_ = STATE_OPEN;
|
|
|
|
|
|
|
|
set_readable(true);
|
|
|
|
set_writable(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sig & talk_base::SE_READ) {
|
|
|
|
char buf[kMaxDtlsPacketLen];
|
|
|
|
size_t read;
|
|
|
|
if (dtls_->Read(buf, sizeof(buf), &read, NULL) == talk_base::SR_SUCCESS) {
|
2013-12-13 01:21:03 +01:00
|
|
|
SignalReadPacket(this, buf, read, talk_base::CreatePacketTime(0), 0);
|
2013-07-10 02:45:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (sig & talk_base::SE_CLOSE) {
|
|
|
|
ASSERT(sig == talk_base::SE_CLOSE); // SE_CLOSE should be by itself.
|
|
|
|
if (!err) {
|
|
|
|
LOG_J(LS_INFO, this) << "DTLS channel closed";
|
|
|
|
} else {
|
|
|
|
LOG_J(LS_INFO, this) << "DTLS channel error, code=" << err;
|
|
|
|
}
|
|
|
|
|
|
|
|
set_readable(false);
|
|
|
|
set_writable(false);
|
|
|
|
dtls_state_ = STATE_CLOSED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool DtlsTransportChannelWrapper::MaybeStartDtls() {
|
|
|
|
if (channel_->writable()) {
|
|
|
|
if (dtls_->StartSSLWithPeer()) {
|
|
|
|
LOG_J(LS_ERROR, this) << "Couldn't start DTLS handshake";
|
|
|
|
dtls_state_ = STATE_CLOSED;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
LOG_J(LS_INFO, this)
|
|
|
|
<< "DtlsTransportChannelWrapper: Started DTLS handshake";
|
|
|
|
|
|
|
|
dtls_state_ = STATE_STARTED;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Called from OnReadPacket when a DTLS packet is received.
|
|
|
|
bool DtlsTransportChannelWrapper::HandleDtlsPacket(const char* data,
|
|
|
|
size_t size) {
|
|
|
|
// Sanity check we're not passing junk that
|
|
|
|
// just looks like DTLS.
|
|
|
|
const uint8* tmp_data = reinterpret_cast<const uint8* >(data);
|
|
|
|
size_t tmp_size = size;
|
|
|
|
while (tmp_size > 0) {
|
|
|
|
if (tmp_size < kDtlsRecordHeaderLen)
|
|
|
|
return false; // Too short for the header
|
|
|
|
|
|
|
|
size_t record_len = (tmp_data[11] << 8) | (tmp_data[12]);
|
|
|
|
if ((record_len + kDtlsRecordHeaderLen) > tmp_size)
|
|
|
|
return false; // Body too short
|
|
|
|
|
|
|
|
tmp_data += record_len + kDtlsRecordHeaderLen;
|
|
|
|
tmp_size -= record_len + kDtlsRecordHeaderLen;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Looks good. Pass to the SIC which ends up being passed to
|
|
|
|
// the DTLS stack.
|
|
|
|
return downward_->OnPacketReceived(data, size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DtlsTransportChannelWrapper::OnRequestSignaling(
|
|
|
|
TransportChannelImpl* channel) {
|
|
|
|
ASSERT(channel == channel_);
|
|
|
|
SignalRequestSignaling(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DtlsTransportChannelWrapper::OnCandidateReady(
|
|
|
|
TransportChannelImpl* channel, const Candidate& c) {
|
|
|
|
ASSERT(channel == channel_);
|
|
|
|
SignalCandidateReady(this, c);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DtlsTransportChannelWrapper::OnCandidatesAllocationDone(
|
|
|
|
TransportChannelImpl* channel) {
|
|
|
|
ASSERT(channel == channel_);
|
|
|
|
SignalCandidatesAllocationDone(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DtlsTransportChannelWrapper::OnRoleConflict(
|
|
|
|
TransportChannelImpl* channel) {
|
|
|
|
ASSERT(channel == channel_);
|
|
|
|
SignalRoleConflict(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void DtlsTransportChannelWrapper::OnRouteChange(
|
|
|
|
TransportChannel* channel, const Candidate& candidate) {
|
|
|
|
ASSERT(channel == channel_);
|
|
|
|
SignalRouteChange(this, candidate);
|
|
|
|
}
|
|
|
|
|
2014-02-14 01:56:12 +01:00
|
|
|
void DtlsTransportChannelWrapper::OnConnectionRemoved(
|
|
|
|
TransportChannelImpl* channel) {
|
|
|
|
ASSERT(channel == channel_);
|
|
|
|
SignalConnectionRemoved(this);
|
|
|
|
}
|
|
|
|
|
2013-07-10 02:45:36 +02:00
|
|
|
} // namespace cricket
|