420 lines
10 KiB
C++
420 lines
10 KiB
C++
/* Copyright(C) 2007-2018 VoIP objects (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 "ICERelaying.h"
|
|
#include "ICEStunAttributes.h"
|
|
#include "ICETime.h"
|
|
#include "ICEMD5.h"
|
|
#include "ICELog.h"
|
|
#include "ICEStream.h"
|
|
|
|
#include <assert.h>
|
|
|
|
using namespace ice;
|
|
|
|
#define LOG_SUBSYSTEM "ice"
|
|
// ------------------ ClientAllocate -----------------
|
|
|
|
ClientAllocate::ClientAllocate(unsigned int lifetime)
|
|
:AuthTransaction(), mLifetime(lifetime), mWireFamily(AF_INET), mAllocFamily(AF_INET)
|
|
{
|
|
assert(lifetime > 60 || !lifetime);
|
|
setComment("ClientAllocate");
|
|
addLongTermCredentials(true);
|
|
}
|
|
|
|
ClientAllocate::~ClientAllocate()
|
|
{
|
|
}
|
|
|
|
NetworkAddress& ClientAllocate::relayedAddress()
|
|
{
|
|
return mRelayedAddr;
|
|
}
|
|
|
|
NetworkAddress& ClientAllocate::reflexiveAddress()
|
|
{
|
|
return mReflexiveAddr;
|
|
}
|
|
|
|
NetworkAddress& ClientAllocate::responseAddress()
|
|
{
|
|
return mResponseAddr;
|
|
}
|
|
|
|
void ClientAllocate::setInitialRequest(StunMessage& msg)
|
|
{
|
|
if (keepalive())
|
|
{
|
|
// Build refresh request
|
|
msg.setMessageType(ice::StunMessage::Refresh);
|
|
msg.setMessageClass(StunMessage::RequestClass);
|
|
}
|
|
else
|
|
{
|
|
// Build allocate request
|
|
msg.setMessageClass(StunMessage::RequestClass);
|
|
msg.setMessageType(StunMessage::Allocate);
|
|
|
|
// Add UDP transport attribute (RequestedTransport is UDP by default)
|
|
msg.setAttribute(new RequestedTransport());
|
|
|
|
// Add request family attribute
|
|
if (mAllocFamily != mWireFamily)
|
|
msg.setAttribute(new RequestedAddressFamily(mAllocFamily == AF_INET ? IPv4 : IPv6));
|
|
}
|
|
}
|
|
|
|
void ClientAllocate::setAuthenticatedRequest(StunMessage& msg)
|
|
{
|
|
if (keepalive())
|
|
{
|
|
// Build refresh request
|
|
msg.setMessageType(ice::StunMessage::Refresh);
|
|
msg.setMessageClass(StunMessage::RequestClass);
|
|
msg.lifetimeAttr().setLifetime(mLifetime);
|
|
}
|
|
else
|
|
{
|
|
msg.setMessageClass(StunMessage::RequestClass);
|
|
msg.setMessageType(StunMessage::Allocate);
|
|
|
|
// Add UDP transport attribute
|
|
msg.setAttribute(new RequestedTransport());
|
|
|
|
// Add LIFETIME
|
|
msg.lifetimeAttr().setLifetime(mLifetime);
|
|
|
|
// Add request family attribute
|
|
if (mAllocFamily != mWireFamily)
|
|
msg.setAttribute(new RequestedAddressFamily(mAllocFamily == AF_INET ? IPv4 : IPv6));
|
|
}
|
|
}
|
|
|
|
void ClientAllocate::processSuccessMessage(StunMessage& msg, NetworkAddress& sourceAddress)
|
|
{
|
|
if (msg.hasAttribute(StunAttribute::XorMappedAddress))
|
|
mReflexiveAddr = msg.xorMappedAddressAttr().address();
|
|
|
|
if (msg.hasAttribute(StunAttribute::XorRelayedAddress))
|
|
mRelayedAddr = msg.xorRelayedAddressAttr().address();
|
|
|
|
if (msg.hasAttribute(StunAttribute::Lifetime))
|
|
mLifetime = msg.lifetimeAttr().lifetime();
|
|
|
|
mResponseAddr = sourceAddress;
|
|
ICELogDebug(<< "Allocated for " << (int)mLifetime << " seconds.");
|
|
}
|
|
|
|
int ClientAllocate::lifetime()
|
|
{
|
|
return mLifetime;
|
|
}
|
|
|
|
void ClientAllocate::setWireFamily(int family)
|
|
{
|
|
mWireFamily = family;
|
|
}
|
|
|
|
int ClientAllocate::getWireFamily() const
|
|
{
|
|
return mWireFamily;
|
|
}
|
|
|
|
void ClientAllocate::setAllocFamily(int family)
|
|
{
|
|
mAllocFamily = family;
|
|
}
|
|
|
|
int ClientAllocate::getAllocFamily() const
|
|
{
|
|
return mAllocFamily;
|
|
}
|
|
|
|
|
|
//------------------- ClientRefresh -------------------------------
|
|
ClientRefresh::ClientRefresh(unsigned int lifetime, Stream* stream, ClientAllocate* allocate)
|
|
:AuthTransaction(), mLifetime(lifetime), mStream(stream)
|
|
{
|
|
assert(stream);
|
|
setComment("ClientRefresh");
|
|
addLongTermCredentials(true);
|
|
|
|
// Copy data from Allocate transaction
|
|
if (allocate)
|
|
{
|
|
setDestination( allocate->destination() );
|
|
setPassword( stream->mConfig.mTurnPassword );
|
|
setUsername( stream->mConfig.mTurnUsername );
|
|
setComponent( allocate->component() );
|
|
#ifdef ICE_CACHE_REALM_NONCE
|
|
setRealm( allocate->realm() );
|
|
setNonce( allocate->nonce() );
|
|
#endif
|
|
mRelayed = allocate->relayedAddress();
|
|
mReflexive = allocate->reflexiveAddress();
|
|
}
|
|
}
|
|
|
|
ClientRefresh::~ClientRefresh()
|
|
{
|
|
}
|
|
|
|
void ClientRefresh::setInitialRequest(StunMessage& msg)
|
|
{
|
|
ICELogDebug(<< "Prepare to run ClientRefresh on TURN server with lifetime " << mLifetime);
|
|
msg.setMessageType(ice::StunMessage::Refresh);
|
|
msg.setMessageClass(ice::StunMessage::RequestClass);
|
|
}
|
|
|
|
void ClientRefresh::setAuthenticatedRequest(StunMessage& msg)
|
|
{
|
|
msg.setMessageType(StunMessage::Refresh);
|
|
msg.setMessageClass(StunMessage::RequestClass);
|
|
msg.lifetimeAttr().setLifetime(mLifetime);
|
|
}
|
|
|
|
void ClientRefresh::processSuccessMessage(StunMessage& /*msg*/, NetworkAddress& /*sourceAddress*/)
|
|
{
|
|
if (mLifetime)
|
|
{
|
|
ICELogDebug(<< "TURN allocation refreshed for " << (int)mLifetime << " seconds.");
|
|
}
|
|
else
|
|
{
|
|
if (mStream)
|
|
{
|
|
if (mStream->mTurnAllocated > 0)
|
|
mStream->mTurnAllocated--;
|
|
}
|
|
ICELogDebug(<< "TURN allocation is deleted.");
|
|
}
|
|
}
|
|
|
|
void ClientRefresh::processError()
|
|
{
|
|
if (mStream)
|
|
{
|
|
if (!mLifetime)
|
|
{
|
|
if (mStream->mTurnAllocated > 0)
|
|
mStream->mTurnAllocated--;
|
|
ICELogError(<< "TURN allocation is not deleted due to error " << errorCode() << " " << errorResponse());
|
|
}
|
|
else
|
|
ICELogError(<< "ClientRefresh failed due to error " << errorCode() << " " << errorResponse());
|
|
}
|
|
}
|
|
|
|
NetworkAddress ClientRefresh::relayedAddress()
|
|
{
|
|
return mRelayed;
|
|
}
|
|
|
|
NetworkAddress ClientRefresh::reflexiveAddress()
|
|
{
|
|
return mReflexive;
|
|
}
|
|
|
|
//------------------- ClientChannelBind ----------------------------
|
|
|
|
static TurnPrefix GPrefix = 0;
|
|
static Mutex GPrefixGuard;
|
|
static TurnPrefix obtainNewPrefix()
|
|
{
|
|
Lock l(GPrefixGuard);
|
|
|
|
// Generate initial value if needed
|
|
if (!GPrefix)
|
|
GPrefix = (rand() % (0x7FFE - 0x4000)) + 0x4000;
|
|
|
|
// Avoid logical overflow
|
|
if (GPrefix == 0x7FFE)
|
|
GPrefix = 0x4000;
|
|
|
|
return ++GPrefix;
|
|
}
|
|
|
|
ClientChannelBind::ClientChannelBind(const NetworkAddress& address)
|
|
:AuthTransaction(), mPeerAddress(address)
|
|
{
|
|
setComment( "ClientChannelBind" );
|
|
// Compute prefix
|
|
mChannelPrefix = obtainNewPrefix();
|
|
ICELogInfo(<< "Channel prefix for TURN channel bind is " << mChannelPrefix);
|
|
addLongTermCredentials(true);
|
|
}
|
|
|
|
ClientChannelBind::~ClientChannelBind()
|
|
{
|
|
}
|
|
|
|
void ClientChannelBind::setInitialRequest(StunMessage& msg)
|
|
{
|
|
msg.setMessageClass(StunMessage::RequestClass);
|
|
msg.setMessageType(StunMessage::ChannelBind);
|
|
|
|
msg.channelNumberAttr().setChannelNumber(mChannelPrefix);
|
|
msg.xorPeerAddressAttr().address() = mPeerAddress;
|
|
}
|
|
|
|
void ClientChannelBind::setAuthenticatedRequest(StunMessage& msg)
|
|
{
|
|
setInitialRequest(msg);
|
|
}
|
|
|
|
unsigned short ClientChannelBind::channelPrefix()
|
|
{
|
|
return mChannelPrefix;
|
|
}
|
|
|
|
bool ClientChannelBind::processData(StunMessage& msg, NetworkAddress& address)
|
|
{
|
|
return AuthTransaction::processData(msg, address);
|
|
}
|
|
|
|
void ClientChannelBind::processSuccessMessage(StunMessage& msg, NetworkAddress& sourceAddress)
|
|
{
|
|
ICELogDebug(<< mPeerAddress.toStdString() << " is bound to " << mChannelPrefix);
|
|
// Make this transaction keepalive
|
|
setKeepalive( true );
|
|
setType( KeepAlive );
|
|
setInterval( ICE_PERMISSIONS_REFRESH_INTERVAL );
|
|
setTimestamp( ICETimeHelper::timestamp() );
|
|
}
|
|
|
|
void ClientChannelBind::processError()
|
|
{
|
|
ICELogCritical(<< "Failed to bind channel with error " << errorCode());
|
|
}
|
|
|
|
NetworkAddress ClientChannelBind::peerAddress()
|
|
{
|
|
return mPeerAddress;
|
|
}
|
|
//----------------- ClientCreatePermission ------------------------
|
|
|
|
ClientCreatePermission::ClientCreatePermission()
|
|
:AuthTransaction()
|
|
{
|
|
setComment("ClientCreatePermission");
|
|
addLongTermCredentials(true);
|
|
}
|
|
|
|
ClientCreatePermission::~ClientCreatePermission()
|
|
{
|
|
;
|
|
}
|
|
|
|
void ClientCreatePermission::addIpAddress(const NetworkAddress& ip)
|
|
{
|
|
// Skip loopback / empty / LAN / IPv6 addresses addresses
|
|
if (!ip.isLoopback() && !ip.isEmpty() && !ip.isLAN() && ip.family() == AF_INET)
|
|
{
|
|
for (unsigned i=0; i<mIPAddressList.size(); i++)
|
|
if (mIPAddressList[i].ip() == ip.ip())
|
|
return;
|
|
|
|
ICELogInfo( << "Permission is to be installed for " << ip.toStdString());
|
|
mIPAddressList.push_back(ip);
|
|
}
|
|
}
|
|
|
|
void ClientCreatePermission::processSuccessMessage(StunMessage& /*msg*/, NetworkAddress& /*sourceAddress*/)
|
|
{
|
|
ICELogDebug(<< "Set permissions ok.");
|
|
setKeepalive( true );
|
|
setType( KeepAlive );
|
|
setInterval( ICE_PERMISSIONS_REFRESH_INTERVAL );
|
|
setTimestamp( ICETimeHelper::timestamp() );
|
|
}
|
|
|
|
void ClientCreatePermission::setInitialRequest(StunMessage& msg)
|
|
{
|
|
msg.setMessageClass(StunMessage::RequestClass);
|
|
msg.setMessageType(StunMessage::CreatePermission);
|
|
|
|
for (unsigned i=0; i<mIPAddressList.size(); i++)
|
|
{
|
|
XorPeerAddress* xpa = new XorPeerAddress();
|
|
xpa->address() = mIPAddressList[i];
|
|
msg.addAttribute(xpa);
|
|
}
|
|
}
|
|
|
|
void
|
|
ClientCreatePermission::setAuthenticatedRequest(StunMessage& msg)
|
|
{
|
|
setInitialRequest(msg);
|
|
}
|
|
|
|
ClientCreatePermission::IpList& ClientCreatePermission::ipList()
|
|
{
|
|
return mIPAddressList;
|
|
}
|
|
|
|
// ------------------ SendIndication ----------------------------------
|
|
SendIndication::SendIndication()
|
|
{
|
|
}
|
|
|
|
SendIndication::~SendIndication()
|
|
{
|
|
}
|
|
|
|
void SendIndication::setTarget(NetworkAddress& addr)
|
|
{
|
|
mTarget = addr;
|
|
}
|
|
|
|
NetworkAddress& SendIndication::target()
|
|
{
|
|
return mTarget;
|
|
}
|
|
|
|
void SendIndication::setPlainData(ByteBuffer& plain)
|
|
{
|
|
mPlainData = plain;
|
|
}
|
|
|
|
ByteBuffer& SendIndication::plainData()
|
|
{
|
|
return mPlainData;
|
|
}
|
|
|
|
ByteBuffer* SendIndication::buildPacket()
|
|
{
|
|
StunMessage m;
|
|
m.setMessageClass(StunMessage::IndicationClass);
|
|
m.setMessageType(StunMessage::Send);
|
|
|
|
XorPeerAddress* xpa = new XorPeerAddress();
|
|
xpa->address() = mTarget;
|
|
|
|
DataAttribute* da = new DataAttribute();
|
|
da->setData(mPlainData);
|
|
|
|
m.setAttribute(xpa);
|
|
m.setAttribute(da);
|
|
|
|
ByteBuffer* result = new ByteBuffer();
|
|
m.buildPacket(*result, "");
|
|
|
|
return result;
|
|
}
|
|
|
|
ByteBuffer* SendIndication::buildPacket(NetworkAddress& target, ByteBuffer& data, NetworkAddress& relay, int component)
|
|
{
|
|
SendIndication si;
|
|
si.setTarget( target );
|
|
si.setPlainData( data );
|
|
|
|
ByteBuffer* result = si.buildPacket();
|
|
result->setComponent( component );
|
|
result->setRemoteAddress( relay );
|
|
|
|
return result;
|
|
}
|