blob: 014539f8e8843c4ff3678d3d9c8645395990204e [file] [log] [blame]
/*
* libjingle
* Copyright 2011, 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/roapsession.h"
#include "talk/app/webrtc/roapmessages.h"
#include "talk/base/helpers.h"
#include "talk/base/logging.h"
namespace webrtc {
static const uint32 kMaxTieBreaker = 0xFFFFFFFE;
static std::string CreateLocalId(const std::string& remote_id) {
std::string local_id;
do {
talk_base::CreateRandomString(32, &local_id);
ASSERT(!local_id.empty());
} while (local_id == remote_id);
return local_id;
}
RoapSession::RoapSession()
: seq_(0),
waiting_for_answer_(false),
received_seq_(0) {
}
std::string RoapSession::CreateOffer(
const SessionDescription* desc,
const std::vector<Candidate>& candidates) {
if (local_id_.empty()) {
local_id_ = CreateLocalId(remote_id_);
}
do {
local_tie_breaker_ = talk_base::CreateRandomNonZeroId();
} while (local_tie_breaker_ > kMaxTieBreaker);
RoapOffer offer(local_id_, remote_id_, session_token_, ++seq_,
local_tie_breaker_, desc, candidates);
waiting_for_answer_ = true;
return offer.Serialize();
}
std::string RoapSession::CreateAnswer(
const SessionDescription* desc,
const std::vector<Candidate>& candidates) {
ASSERT(!remote_id_.empty());
if (local_id_.empty()) {
local_id_ = CreateLocalId(remote_id_);
}
RoapAnswer answer(remote_id_, local_id_, session_token_, response_token_,
seq_, desc, candidates);
response_token_.clear();
return answer.Serialize();
}
std::string RoapSession::CreateOk() {
ASSERT(!remote_id_.empty());
if (local_id_.empty()) {
local_id_ = CreateLocalId(remote_id_);
}
RoapOk ok(remote_id_, local_id_, session_token_, response_token_, seq_);
response_token_.clear();
return ok.Serialize();
}
std::string RoapSession::CreateShutDown() {
if (local_id_.empty()) {
local_id_ = CreateLocalId(remote_id_);
}
RoapShutdown shutdown(local_id_, remote_id_, session_token_, ++seq_);
return shutdown.Serialize();
}
std::string RoapSession::CreateErrorMessage(RoapErrorCode error) {
if (local_id_.empty()) {
local_id_ = CreateLocalId(remote_id_);
}
RoapError message(received_offer_id_, local_id_, session_token_,
response_token_, received_seq_, error);
response_token_.clear();
return message.Serialize();
}
RoapSession::ParseResult RoapSession::Parse(
const std::string& msg) {
RoapMessageBase message;
if (!message.Parse(msg)) {
LOG(LS_ERROR) << "Parse failed. Invalid Roap message?";
return kInvalidMessage;
}
received_offer_id_ = message.offer_session_id();
received_answer_id_ = message.answer_session_id();
received_seq_ = message.seq();
session_token_ = message.session_token();
response_token_ = message.response_token();
ParseResult result = kInvalidMessage;
switch (message.type()) {
case RoapMessageBase::kOffer: {
RoapOffer offer(message);
if (!offer.Parse()) {
LOG(LS_ERROR) << "Parse failed. Invalid Offer message?";
return kInvalidMessage;
}
result = ValidateOffer(&offer);
break;
}
case RoapMessageBase::kAnswer: {
RoapAnswer answer(message);
if (!answer.Parse()) {
LOG(LS_ERROR) << "Parse failed. Invalid Answer message?";
result = kInvalidMessage;
} else {
result = ValidateAnswer(&answer);
}
break;
}
case RoapMessageBase::kOk: {
result = ValidateOk(message);
break;
}
case RoapMessageBase::kShutdown: {
// Always accept shutdown messages.
if (remote_id_.empty()) {
remote_id_ = message.offer_session_id();
}
seq_ = message.seq();
result = kShutDown;
break;
}
case RoapMessageBase::kError: {
RoapError error(message);
if (!error.Parse()) {
LOG(LS_ERROR) << "Parse failed. Invalid Error message?";
result = kInvalidMessage;
} else if (ValidateError(error) == kError) {
result = kError;
} // else ignore this error message.
break;
}
default: {
ASSERT(!"Unknown message type.");
LOG(LS_ERROR) << "Received unknown message.";
result = kInvalidMessage;
break;
}
}
return result;
}
RoapSession::ParseResult RoapSession::ValidateOffer(
RoapOffer* received_offer) {
/* Check if the incoming OFFER has a answererSessionId, if not it is
an initial offer. If the outstanding OFFER also is an initial
OFFER there is an Error. */
if (received_offer->answer_session_id().empty() &&
remote_id_.empty() && waiting_for_answer_) {
return kInvalidMessage;
}
if (remote_id_.empty()) {
remote_id_ = received_offer->offer_session_id();
}
// Check the message belong to this session.
bool result =
received_offer->offer_session_id() == remote_id_ &&
received_offer->answer_session_id() == local_id_;
if (!result || received_offer->seq() < seq_) {
return kInvalidMessage; // Old seq.
}
if (waiting_for_answer_) {
if (received_offer->seq() != seq_) {
return kInvalidMessage;
}
// Glare.
if (received_offer->tie_breaker() < local_tie_breaker_) {
return kConflict;
} else if (received_offer->tie_breaker() == local_tie_breaker_) {
return kDoubleConflict;
}
}
// seq ok or remote offer won the glare resolution.
seq_ = received_offer->seq();
remote_desc_.reset(received_offer->ReleaseSessionDescription());
remote_candidates_ = received_offer->candidates();
return kOffer;
}
RoapSession::ParseResult RoapSession::ValidateAnswer(
RoapAnswer* received_answer) {
if (remote_id_.empty()) {
remote_id_ = received_answer->answer_session_id();
}
bool result =
received_answer->offer_session_id() == local_id_ &&
received_answer->seq() == seq_ &&
received_answer->answer_session_id() == remote_id_;
if (!result) {
return kInvalidMessage;
}
remote_desc_.reset(received_answer->ReleaseSessionDescription());
remote_candidates_ = received_answer->candidates();
if (received_answer->more_coming()) {
return kAnswerMoreComing;
}
waiting_for_answer_ = false;
return kAnswer;
}
RoapSession::ParseResult RoapSession::ValidateOk(
const RoapMessageBase& message) {
if (remote_id_.empty()) {
remote_id_ = message.answer_session_id();
}
bool result =
message.offer_session_id() == local_id_ &&
message.seq() == seq_ &&
message.answer_session_id() == remote_id_;
if (!result) {
return kInvalidMessage;
}
return kOk;
}
RoapSession::ParseResult RoapSession::ValidateError(
const RoapError& message) {
bool result =
message.offer_session_id() == local_id_ && message.seq() == seq_;
if (!result) {
return kInvalidMessage;
}
waiting_for_answer_ = false;
remote_error_ = message.error();
return kError;
}
SessionDescription* RoapSession::ReleaseRemoteDescription() {
return remote_desc_.release();
}
const std::vector<Candidate>& RoapSession::RemoteCandidates() {
return remote_candidates_;
}
RoapErrorCode RoapSession::RemoteError() {
return remote_error_;
}
} // namespace webrtc