521 lines
12 KiB
C++
521 lines
12 KiB
C++
/* Copyright(C) 2007-2017 VoIPobjects (voipobjects.com)
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "ICEBinding.h"
|
|
#include "ICEStunAttributes.h"
|
|
#include "ICETime.h"
|
|
#include "ICELog.h"
|
|
#include <memory>
|
|
|
|
using namespace ice;
|
|
#define LOG_SUBSYSTEM "ICE"
|
|
|
|
//----------------------- AuthClientBinding -----------------
|
|
|
|
ConnectivityCheck::ConnectivityCheck()
|
|
{
|
|
setComment("ConnectivityCheck");
|
|
mErrorCode = 0;
|
|
}
|
|
|
|
ConnectivityCheck::~ConnectivityCheck()
|
|
{
|
|
}
|
|
|
|
ByteBuffer* ConnectivityCheck::generateData(bool force)
|
|
{
|
|
if (!mComposed)
|
|
{
|
|
StunMessage msg;
|
|
msg.setMessageClass(StunMessage::RequestClass);
|
|
msg.setMessageType(StunMessage::Binding);
|
|
msg.setTransactionId(mTransactionID);
|
|
|
|
if (mUseCandidate)
|
|
msg.setAttribute(new ICEUseCandidate());
|
|
|
|
// Use message integrity attribute
|
|
mMessageIntegrity = true;
|
|
|
|
// Use fingerprint attribute
|
|
mFingerprint = true;
|
|
|
|
if (mEnablePriority)
|
|
msg.icePriorityAttr().setPriority(mPriority);
|
|
|
|
// Copy comment to message object
|
|
msg.setComment(mComment);
|
|
|
|
enqueueMessage(msg);
|
|
|
|
mComposed = true;
|
|
}
|
|
|
|
return Transaction::generateData(force);
|
|
}
|
|
|
|
void ConnectivityCheck::confirmTransaction(NetworkAddress& mapped)
|
|
{
|
|
// Save resolved address&ip
|
|
mMappedAddress = mapped;
|
|
|
|
// Mark transaction as succeeded
|
|
mState = Transaction::Success;
|
|
|
|
// Save source IP and port
|
|
mResponseAddress = mapped;
|
|
}
|
|
|
|
bool ConnectivityCheck::processData(StunMessage& msg, NetworkAddress& address)
|
|
{
|
|
#ifdef ICE_TEST_VERYAGGRESSIVE
|
|
return false;
|
|
#endif
|
|
|
|
// Check if it is response
|
|
if (msg.messageClass() != StunMessage::ErrorClass &&
|
|
msg.messageClass() != StunMessage::SuccessClass)
|
|
return false;
|
|
|
|
if (msg.transactionId() != mTransactionID)
|
|
return false;
|
|
|
|
// Validate againgst password used to encrypt
|
|
if (!msg.validatePacket(mPassword))
|
|
return false;
|
|
|
|
// Check for ErrorCode attribute
|
|
if (msg.hasAttribute(StunAttribute::ErrorCode))
|
|
{
|
|
ErrorCode& ec = dynamic_cast<ErrorCode&>(msg.attribute(StunAttribute::ErrorCode));
|
|
|
|
//save error code and response
|
|
mErrorCode = ec.errorCode();
|
|
mErrorResponse = ec.errorPhrase();
|
|
|
|
//mark transaction as failed
|
|
mState = Transaction::Failed;
|
|
|
|
return true;
|
|
}
|
|
|
|
//check if received empty ErrorClass response for poor servers
|
|
if (msg.messageClass() == StunMessage::ErrorClass)
|
|
{
|
|
//mark transaction as failed
|
|
mState = Transaction::Failed;
|
|
|
|
return true;
|
|
}
|
|
|
|
//check for mapped address attribute
|
|
if (msg.hasAttribute(StunAttribute::MappedAddress))
|
|
{
|
|
// Save resolved address&ip
|
|
mMappedAddress = msg.mappedAddressAttr().address();
|
|
|
|
// Mark transaction as succeeded
|
|
mState = Transaction::Success;
|
|
|
|
// Save source IP and port
|
|
mResponseAddress = address;
|
|
}
|
|
|
|
//check for xor'ed mapped address attribute
|
|
if (msg.hasAttribute(StunAttribute::XorMappedAddress))
|
|
{
|
|
// Save resolved IP and port
|
|
mMappedAddress = msg.xorMappedAddressAttr().address();
|
|
|
|
// Mark transaction as succeeded
|
|
mState = Transaction::Success;
|
|
|
|
// Save source IP and port
|
|
mResponseAddress = address;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
NetworkAddress& ConnectivityCheck::mappedAddress()
|
|
{
|
|
return mMappedAddress;
|
|
}
|
|
NetworkAddress& ConnectivityCheck::responseAddress()
|
|
{
|
|
return mResponseAddress;
|
|
}
|
|
void ConnectivityCheck::addUseCandidate()
|
|
{
|
|
mUseCandidate = true;
|
|
}
|
|
int ConnectivityCheck::errorCode()
|
|
{
|
|
return mErrorCode;
|
|
}
|
|
int ConnectivityCheck::resultError()
|
|
{
|
|
return mErrorCode;
|
|
}
|
|
NetworkAddress& ConnectivityCheck::resultSource()
|
|
{
|
|
return mResponseAddress;
|
|
}
|
|
Transaction::State ConnectivityCheck::resultState()
|
|
{
|
|
return state();
|
|
}
|
|
NetworkAddress& ConnectivityCheck::resultLocal()
|
|
{
|
|
return mMappedAddress;
|
|
}
|
|
void ConnectivityCheck::useCandidate()
|
|
{
|
|
addUseCandidate();
|
|
}
|
|
unsigned ConnectivityCheck::resultPriority()
|
|
{
|
|
return priorityValue();
|
|
}
|
|
//---------------------------- ClientBindingTransaction ----------------------------
|
|
|
|
ClientBinding::ClientBinding()
|
|
:Transaction(), mErrorCode(0), mUseCandidate(false)
|
|
{
|
|
setComment("ClientBinding");
|
|
}
|
|
|
|
ClientBinding::~ClientBinding()
|
|
{
|
|
}
|
|
|
|
int ClientBinding::errorCode()
|
|
{
|
|
return mErrorCode;
|
|
}
|
|
|
|
std::string ClientBinding::errorResponse()
|
|
{
|
|
return mErrorResponse;
|
|
}
|
|
|
|
NetworkAddress& ClientBinding::mappedAddress()
|
|
{
|
|
return mMappedAddress;
|
|
}
|
|
|
|
NetworkAddress& ClientBinding::responseAddress()
|
|
{
|
|
return mResponseAddress;
|
|
}
|
|
|
|
bool ClientBinding::processData(StunMessage& msg, NetworkAddress& address)
|
|
{
|
|
// Check if it is response
|
|
if (msg.messageClass() != StunMessage::ErrorClass &&
|
|
msg.messageClass() != StunMessage::SuccessClass)
|
|
return false;
|
|
|
|
if (msg.transactionId() != this->mTransactionID)
|
|
return false;
|
|
|
|
// Check for ErrorCode attribute
|
|
if (msg.hasAttribute(StunAttribute::ErrorCode))
|
|
{
|
|
// Save error code and response
|
|
mErrorCode = msg.errorCodeAttr().errorCode();
|
|
mErrorResponse = msg.errorCodeAttr().errorPhrase();
|
|
|
|
// Mark transaction as failed
|
|
mState = Transaction::Failed;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Check if received empty ErrorClass response for poor servers
|
|
if (msg.messageClass() == StunMessage::ErrorClass)
|
|
{
|
|
// Mark transaction as failed
|
|
mState = Transaction::Failed;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Check for mapped address attribute
|
|
if (msg.hasAttribute(StunAttribute::MappedAddress))
|
|
{
|
|
// Save resolved address&ip
|
|
mMappedAddress = msg.mappedAddressAttr().address();
|
|
|
|
// mMark transaction as succeeded
|
|
mState = Transaction::Success;
|
|
|
|
// Save source IP and port
|
|
mResponseAddress = address;
|
|
}
|
|
|
|
//check for xor'ed mapped address attribute
|
|
if (msg.hasAttribute(StunAttribute::XorMappedAddress))
|
|
{
|
|
// Save resolved IP and port
|
|
mMappedAddress = msg.xorMappedAddressAttr().address();
|
|
|
|
// Mark transaction as succeeded
|
|
mState = Transaction::Success;
|
|
|
|
// Save source IP and port
|
|
mResponseAddress = address;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
ByteBuffer* ClientBinding::generateData(bool force)
|
|
{
|
|
if (!mComposed)
|
|
{
|
|
StunMessage msg;
|
|
msg.setMessageType(ice::StunMessage::Binding);
|
|
msg.setMessageClass(ice::StunMessage::RequestClass);
|
|
msg.setTransactionId(mTransactionID);
|
|
|
|
if (mUseCandidate)
|
|
msg.setAttribute(new ICEUseCandidate());
|
|
|
|
// Copy comment to message object
|
|
msg.setComment(mComment);
|
|
|
|
enqueueMessage(msg);
|
|
|
|
mComposed = true;
|
|
}
|
|
|
|
return Transaction::generateData(force);
|
|
}
|
|
|
|
void ClientBinding::addUseCandidate()
|
|
{
|
|
mUseCandidate = true;
|
|
}
|
|
|
|
|
|
//------------------------------ ServerBindingTransaction ---------------------------
|
|
ServerBinding::ServerBinding()
|
|
{
|
|
setComment("ServerBinding");
|
|
|
|
mGenerate400 = false;
|
|
mGenerate487 = false;
|
|
mRole = 0;
|
|
}
|
|
|
|
ServerBinding::~ServerBinding()
|
|
{
|
|
}
|
|
void ServerBinding::setLocalTieBreaker(std::string tieBreaker)
|
|
{
|
|
mLocalTieBreaker = tieBreaker;
|
|
}
|
|
|
|
bool ServerBinding::processData(StunMessage& msg, NetworkAddress& address)
|
|
{
|
|
if (msg.messageType() != StunMessage::Binding || msg.messageClass() != StunMessage::RequestClass)
|
|
return false;
|
|
ICELogDebug(<< "Received Binding request from " << address.toStdString().c_str());
|
|
|
|
// Save visible address
|
|
mSourceAddress = address;
|
|
|
|
// Save transaction ID
|
|
mTransactionID = msg.transactionId();
|
|
|
|
// Save Priority value
|
|
if (msg.hasAttribute(StunAttribute::ICEPriority))
|
|
{
|
|
mEnablePriority = true;
|
|
mPriority = msg.icePriorityAttr().priority();
|
|
}
|
|
|
|
// Save UseCandidate flag
|
|
mUseCandidate = msg.hasAttribute(StunAttribute::ICEUseCandidate);
|
|
|
|
// Check if auth credentials are needed
|
|
mGenerate400 = false; //do not send 400 response by default
|
|
mGenerate487 = false;
|
|
|
|
if (!msg.hasAttribute(StunAttribute::Username) || !msg.hasAttribute(StunAttribute::MessageIntegrity))
|
|
{
|
|
ICELogCritical(<< "There is no Username or MessageIntegrity attributes in Binding required. Error 400 will be generated.");
|
|
// Send 400 error
|
|
mGenerate400 = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
// Check for role
|
|
if (msg.hasAttribute(StunAttribute::ControlledAttr))
|
|
{
|
|
mRemoteTieBreaker = msg.iceControlledAttr().tieBreaker();
|
|
mRole = 1; // Session::Controlled;
|
|
}
|
|
else
|
|
if (msg.hasAttribute(StunAttribute::ControllingAttr))
|
|
{
|
|
mRemoteTieBreaker = msg.iceControllingAttr().tieBreaker();
|
|
mRole = 2;// Session::Controlling;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
ByteBuffer* ServerBinding::generateData(bool force)
|
|
{
|
|
// Restart timer
|
|
mRTOTimer.stop();
|
|
mRTOTimer.start();
|
|
|
|
// See if remote password / username are set
|
|
if (mPassword.empty() || mUsername.empty())
|
|
return NULL;
|
|
|
|
StunMessage msg;
|
|
|
|
// Set transaction ID the same as processed incoming message
|
|
msg.setTransactionId(mTransactionID);
|
|
msg.setMessageType(StunMessage::Binding);
|
|
|
|
if (mGenerate400)
|
|
{
|
|
// Generate bad request error
|
|
msg.setMessageClass(StunMessage::ErrorClass);
|
|
|
|
msg.errorCodeAttr().setErrorCode(400);
|
|
msg.errorCodeAttr().setErrorPhrase("Bad request");
|
|
}
|
|
else
|
|
if (mGenerate487)
|
|
{
|
|
// Generate 487 error
|
|
msg.setMessageClass(StunMessage::ErrorClass);
|
|
msg.errorCodeAttr().setErrorCode(487);
|
|
msg.errorCodeAttr().setErrorPhrase("Role conflict");
|
|
}
|
|
else
|
|
{
|
|
msg.setMessageClass(StunMessage::SuccessClass);
|
|
|
|
msg.mappedAddressAttr().address() = mSourceAddress;
|
|
msg.xorMappedAddressAttr().address() = mSourceAddress;
|
|
}
|
|
|
|
// Build message
|
|
|
|
// Clear outgoing buffer
|
|
mOutgoingData.clear();
|
|
|
|
// Check if message should be secured by message integrity
|
|
//std::string password;
|
|
if (!mGenerate400 && !mGenerate487)
|
|
{
|
|
// Do not create username attribute here - response does not need it
|
|
// msg.usernameAttr().setValue(mUsername);
|
|
|
|
//ICELogCritical(<< "Using password " << mPassword);
|
|
|
|
// Add message integrity attribute
|
|
msg.setAttribute(new MessageIntegrity());
|
|
|
|
if (mFingerprint)
|
|
msg.setAttribute(new Fingerprint());
|
|
|
|
// Add ICE-CONTROLLED attribute if needed
|
|
if (mEnableControlled)
|
|
msg.iceControlledAttr().setTieBreaker(mLocalTieBreaker);
|
|
|
|
// Add ICE-CONTROLLING attribute if needed
|
|
if (mEnableControlling)
|
|
msg.iceControllingAttr().setTieBreaker(mLocalTieBreaker);
|
|
|
|
// Add ICE-PRIORITY attribute if needed
|
|
if (mEnablePriority)
|
|
msg.icePriorityAttr().setPriority(mPriority);
|
|
}
|
|
|
|
// Build packet
|
|
msg.buildPacket(mOutgoingData, mPassword);
|
|
|
|
// Copy comment
|
|
msg.setComment(mComment);
|
|
|
|
mComposed = true;
|
|
|
|
return new ByteBuffer(mOutgoingData);
|
|
}
|
|
|
|
bool ServerBinding::hasUseCandidate()
|
|
{
|
|
return mUseCandidate;
|
|
}
|
|
|
|
bool ServerBinding::gotRequest()
|
|
{
|
|
return !mGenerate400 && !mGenerate487;
|
|
}
|
|
|
|
int ServerBinding::role()
|
|
{
|
|
return mRole;
|
|
}
|
|
|
|
void ServerBinding::generate487()
|
|
{
|
|
mGenerate487 = true;
|
|
}
|
|
|
|
std::string ServerBinding::remoteTieBreaker()
|
|
{
|
|
return mRemoteTieBreaker;
|
|
}
|
|
|
|
void ServerBinding::restart()
|
|
{
|
|
}
|
|
|
|
//-------------------- BindingIndication ---------------
|
|
BindingIndication::BindingIndication(unsigned int interval)
|
|
:mInterval(interval)
|
|
{
|
|
setComment("BindingIndication");
|
|
}
|
|
|
|
BindingIndication::~BindingIndication()
|
|
{
|
|
}
|
|
|
|
bool BindingIndication::processData(StunMessage& msg, NetworkAddress& address)
|
|
{
|
|
return (msg.messageClass() == StunMessage::IndicationClass &&
|
|
msg.messageType() == StunMessage::Binding);
|
|
}
|
|
|
|
|
|
ByteBuffer* BindingIndication::generateData(bool force)
|
|
{
|
|
if (!mComposed)
|
|
{
|
|
StunMessage msg;
|
|
msg.setMessageClass(StunMessage::IndicationClass);
|
|
msg.setMessageType(StunMessage::Binding);
|
|
msg.setTransactionId(mTransactionID);
|
|
msg.setComment(mComment);
|
|
enqueueMessage(msg);
|
|
mComposed = true;
|
|
}
|
|
|
|
return Transaction::generateData(force);
|
|
}
|
|
|