854 lines
21 KiB
C++
854 lines
21 KiB
C++
/* Copyright(C) 2007-2014 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 "ICEPlatform.h"
|
|
#include "ICEStunMessage.h"
|
|
#include "ICECRC32.h"
|
|
#include "ICESHA1.h"
|
|
#include "ICEStunAttributes.h"
|
|
#include "ICEError.h"
|
|
#include "ICELog.h"
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <algorithm>
|
|
|
|
#ifndef _WIN32
|
|
# include <arpa/inet.h>
|
|
# include <alloca.h>
|
|
# include <stdexcept>
|
|
#endif
|
|
|
|
#define STUN_HEADER_SIZE 20
|
|
#define HMAC_DIGEST_SIZE 20
|
|
#define LOG_SUBSYSTEM "ICE"
|
|
|
|
using namespace ice;
|
|
|
|
bool StunMessage::TransactionID::operator == (const StunMessage::TransactionID& rhs)
|
|
{
|
|
return memcmp(mValue, rhs.mValue, 12) == 0;
|
|
}
|
|
|
|
bool StunMessage::TransactionID::operator != (const TransactionID& rhs)
|
|
{
|
|
return memcmp(mValue, rhs.mValue, 12) != 0;
|
|
}
|
|
|
|
StunMessage::TransactionID::TransactionID()
|
|
{
|
|
for (int i=0; i<12; i++)
|
|
mValue[i] = rand() & 0xFF;
|
|
|
|
//memset(mValue, 0, 12);
|
|
}
|
|
|
|
std::string StunMessage::TransactionID::toStdString()
|
|
{
|
|
char hex[3];
|
|
std::string result;
|
|
for (size_t i=0; i<12; i++)
|
|
{
|
|
sprintf(hex, "%2x", mValue[i]);
|
|
result += hex;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
StunMessage::TransactionID StunMessage::TransactionID::generateNew()
|
|
{
|
|
TransactionID result;
|
|
for (size_t i=0; i<12; i++)
|
|
result.mValue[i] = (unsigned char)rand();
|
|
|
|
return result;
|
|
}
|
|
|
|
//--------------------------- StunAttribute ----------------------
|
|
StunAttribute::StunAttribute()
|
|
{
|
|
mLength = 0;
|
|
mType = 0;
|
|
mDataOffset = 0;
|
|
}
|
|
|
|
StunAttribute::~StunAttribute()
|
|
{
|
|
}
|
|
|
|
void StunAttribute::setDataOffset(size_t offset)
|
|
{
|
|
mDataOffset = offset;
|
|
}
|
|
|
|
size_t StunAttribute::dataOffset() const
|
|
{
|
|
return mDataOffset;
|
|
}
|
|
|
|
//---------------------------- StunMessage -----------------------
|
|
StunMessage::StunMessage()
|
|
:mMagicCookie(0x2112A442)
|
|
{
|
|
|
|
}
|
|
|
|
StunMessage::~StunMessage()
|
|
{
|
|
// Iterate map to delete all attributes
|
|
for (AttributeMap::iterator ait=mAttrMap.begin(); ait != mAttrMap.end(); ++ait)
|
|
delete ait->second;
|
|
}
|
|
|
|
std::string StunMessage::comment()
|
|
{
|
|
return mComment;
|
|
}
|
|
|
|
void StunMessage::setComment(std::string comment)
|
|
{
|
|
mComment = comment;
|
|
}
|
|
|
|
|
|
void StunMessage::setMessageType(Type type)
|
|
{
|
|
mType = type;
|
|
}
|
|
|
|
|
|
StunMessage::Type StunMessage::messageType()
|
|
{
|
|
return mType;
|
|
}
|
|
|
|
void StunMessage::setMessageClass(Class value)
|
|
{
|
|
mClass = value;
|
|
}
|
|
|
|
StunMessage::Class StunMessage::messageClass()
|
|
{
|
|
return mClass;
|
|
}
|
|
|
|
unsigned int StunMessage::magicCookie()
|
|
{
|
|
return mMagicCookie;
|
|
}
|
|
|
|
bool StunMessage::isMagicCookieValid()
|
|
{
|
|
return mMagicCookie == 0x2112A442;
|
|
}
|
|
|
|
void StunMessage::setTransactionId(TransactionID id)
|
|
{
|
|
mTransactionID = id;
|
|
}
|
|
|
|
StunMessage::TransactionID StunMessage::transactionId()
|
|
{
|
|
return mTransactionID;
|
|
}
|
|
|
|
static void EmbedAttrBuffer(StunAttribute& attr, BufferWriter& writer)
|
|
{
|
|
ByteBuffer attrBuffer; attrBuffer.resize(512);
|
|
BufferWriter attrWriter(attrBuffer);
|
|
attr.buildPacket(attrWriter);
|
|
attrBuffer.resize(attrWriter.offset());
|
|
|
|
// Enqueue attribute type
|
|
writer.writeUShort(attr.type());
|
|
|
|
// Enqueue length
|
|
writer.writeUShort((uint16_t)attrBuffer.size());
|
|
|
|
// Save data offset in generated buffer
|
|
attr.setDataOffset(writer.offset() + 2); // 2 is for first 16 bits written by bitstream
|
|
|
|
// Enqueue attribute's value
|
|
if (attrBuffer.size() > 0)
|
|
writer.writeBuffer(attrBuffer.data(), attrBuffer.size());
|
|
|
|
if (attrBuffer.size() & 0x3)
|
|
{
|
|
size_t paddingLength = 4 - (attrBuffer.size() & 0x3);
|
|
char paddingBytes[4] = { 0, 0, 0, 0 };
|
|
writer.writeBuffer(paddingBytes, paddingLength);
|
|
}
|
|
}
|
|
|
|
void StunMessage::buildPacket(ByteBuffer& buffer, const std::string& password)
|
|
{
|
|
// Make enough space for packet
|
|
buffer.resize(1024);
|
|
|
|
// Write bits
|
|
BitWriter bitstream(buffer);
|
|
|
|
bitstream.writeBit(0)
|
|
.writeBit(0);
|
|
|
|
unsigned int msgtype = mType & 0xFFF;
|
|
unsigned int msgclass = mClass & 0x3;
|
|
|
|
// Enqueue last 5 bits of mtype
|
|
for (size_t i=0; i<5; i++)
|
|
bitstream.writeBit(bit(msgtype, 11 - i));
|
|
|
|
// Enqueue last bit of msgclass
|
|
bitstream.writeBit(bit(msgclass, 1));
|
|
|
|
// Enqueue 3 bits of msgtype
|
|
for (size_t i=0; i<3; i++)
|
|
bitstream.writeBit(bit(msgtype, 6 - i));
|
|
|
|
// Enqueue first bit of msgclass
|
|
bitstream.writeBit(bit(msgclass, 0));
|
|
|
|
// Enqueue 4 bits of msgtype
|
|
for (size_t i=0; i<4; i++)
|
|
bitstream.writeBit(bit(msgtype, 3-i));
|
|
|
|
// Enqueue 2 bytes of length - now it is zero
|
|
BufferWriter stream(buffer.mutableData() + bitstream.count() / 8);
|
|
stream.writeUShort(0);
|
|
|
|
// Enqueue magic cookie value
|
|
unsigned int cookie = htonl(mMagicCookie);
|
|
stream.writeBuffer(&cookie, 4);
|
|
|
|
// Enqueue transaction ID
|
|
//memset(mTransactionID.mValue, 0, sizeof(mTransactionID.mValue)); // For debugging only
|
|
stream.writeBuffer(mTransactionID.mValue, 12);
|
|
|
|
// Iterate attributes
|
|
AttributeMap::iterator attrIter;
|
|
for (attrIter = mAttrMap.begin(); attrIter != mAttrMap.end(); ++attrIter)
|
|
{
|
|
StunAttribute& attr = *attrIter->second;
|
|
|
|
// Check if it is MessageIntegrity or Fingerprint - they comes last and skip them for now
|
|
if (attr.type() == StunAttribute::MessageIntegrity || attr.type() == StunAttribute::Fingerprint)
|
|
continue;
|
|
|
|
EmbedAttrBuffer(attr, stream);
|
|
}
|
|
|
|
// Append MessageIntegrity attribute if exists
|
|
AttributeMap::iterator miIter = mAttrMap.find(StunAttribute::MessageIntegrity);
|
|
if ( miIter != mAttrMap.end())
|
|
{
|
|
EmbedAttrBuffer(*miIter->second, stream);
|
|
}
|
|
|
|
int lengthWithoutFingerprint = stream.offset() + 2;
|
|
|
|
// Append Fingerprint attribute if exists
|
|
AttributeMap::iterator fpIter = mAttrMap.find(StunAttribute::Fingerprint);
|
|
if (fpIter != mAttrMap.end())
|
|
{
|
|
EmbedAttrBuffer(*fpIter->second, stream);
|
|
}
|
|
|
|
// Check for message integrity attribute
|
|
miIter = mAttrMap.find(StunAttribute::MessageIntegrity);
|
|
if (miIter != mAttrMap.end())
|
|
{
|
|
// Update length in header
|
|
*((unsigned short*)buffer.mutableData() + 1) = htons(lengthWithoutFingerprint - STUN_HEADER_SIZE);
|
|
|
|
// Prepare HMAC digest buffer
|
|
unsigned char digest[HMAC_DIGEST_SIZE];
|
|
|
|
// Get data offset for output digest
|
|
size_t dataOffset = miIter->second->dataOffset();
|
|
|
|
// Get digest
|
|
hmacSha1Digest(buffer.data(), dataOffset - 4, digest, password.c_str(), password.length());
|
|
|
|
// Copy digest to proper place
|
|
memcpy((unsigned char*)buffer.data() + dataOffset, digest, HMAC_DIGEST_SIZE);
|
|
}
|
|
|
|
// Resize resulting buffer
|
|
buffer.resize(stream.offset() + 2);
|
|
|
|
// Put length in header
|
|
*((unsigned short*)buffer.mutableData() + 1) = (uint16_t)(htons(buffer.size() - STUN_HEADER_SIZE));
|
|
|
|
// Check for fingerprint attribute
|
|
fpIter = mAttrMap.find(StunAttribute::Fingerprint);
|
|
if (fpIter != mAttrMap.end())
|
|
{
|
|
// Get data offset for fingeprint attribute
|
|
size_t dataOffset = fpIter->second->dataOffset();
|
|
|
|
// Find CRC32
|
|
CRC32 crc32;
|
|
unsigned int crc = crc32.fullCrc((unsigned char*)buffer.data(), dataOffset - 4);
|
|
|
|
// Update attribute value with CRC32 value
|
|
memcpy((unsigned char*)buffer.data() + dataOffset, &crc, 4);
|
|
}
|
|
|
|
}
|
|
|
|
#include "ICEStunAttributes.h"
|
|
|
|
bool StunMessage::parsePacket(ByteBuffer& buffer)
|
|
{
|
|
// Save incoming packet
|
|
mPacket2Parse = buffer;
|
|
|
|
// Clear attribute list
|
|
mAttrMap.clear();
|
|
|
|
BufferReader stream(buffer);
|
|
|
|
uint8_t firstByte = stream.readUChar();
|
|
uint8_t secondByte = stream.readUChar();
|
|
|
|
if (firstByte & ~0x3F)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int c1 = firstByte & 0x1;
|
|
int m11 = firstByte & 0x20 ? 1 : 0;
|
|
int m10 = firstByte & 0x10 ? 1 : 0;
|
|
int m9 = firstByte & 0x8 ? 1 : 0;
|
|
int m8 = firstByte & 0x4 ? 1 : 0;
|
|
int m7 = firstByte & 0x2 ? 1 : 0;
|
|
|
|
int m6 = secondByte & 0x80 ? 1 : 0;
|
|
int m5 = secondByte & 0x40 ? 1 : 0;
|
|
int m4 = secondByte & 0x20 ? 1 : 0;
|
|
int c0 = secondByte & 0x10 ? 1 : 0;
|
|
int m3 = secondByte & 0x8 ? 1 : 0;
|
|
int m2 = secondByte & 0x4 ? 1 : 0;
|
|
int m1 = secondByte & 0x2 ? 1 : 0;
|
|
int m0 = secondByte & 0x1 ? 1 : 0;
|
|
|
|
mType = (StunMessage::Type)((m11 << 11) + (m10 << 10) + (m9 << 9) + (m8 << 8) + (m7 << 7) + (m6 << 6) + (m5 << 5) + (m4 << 4) + (m3 << 3) + (m2 << 2) + (m1 << 1) + m0);
|
|
mClass = (StunMessage::Class) ((c1 << 1) + c0);
|
|
|
|
unsigned short length = stream.readUShort();
|
|
if (length & 0x3)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Dequeue magic cookie
|
|
mMagicCookie = stream.readUInt();
|
|
if (!isMagicCookieValid())
|
|
return false;
|
|
|
|
// Dequeue transaction id
|
|
char id[12];
|
|
stream.readBuffer(id, 12);
|
|
|
|
memcpy(mTransactionID.mValue, id, 12);
|
|
|
|
// Dequeue attributes
|
|
while (stream.count() < buffer.size())
|
|
{
|
|
int attrType = stream.readUShort();
|
|
StunAttribute* attr = NULL;
|
|
switch (attrType)
|
|
{
|
|
case StunAttribute::Data:
|
|
attr = new DataAttribute();
|
|
break;
|
|
|
|
case StunAttribute::MappedAddress:
|
|
attr = new MappedAddress();
|
|
break;
|
|
|
|
case StunAttribute::XorMappedAddress:
|
|
attr = new XorMappedAddress();
|
|
break;
|
|
|
|
case StunAttribute::ErrorCode:
|
|
attr = new ErrorCode();
|
|
break;
|
|
|
|
case StunAttribute::AlternateServer:
|
|
attr = new AlternateServer();
|
|
break;
|
|
|
|
case StunAttribute::UnknownAttributes:
|
|
attr = new UnknownAttributes();
|
|
break;
|
|
|
|
case StunAttribute::Fingerprint:
|
|
attr = new Fingerprint();
|
|
break;
|
|
|
|
case StunAttribute::MessageIntegrity:
|
|
attr = new MessageIntegrity();
|
|
break;
|
|
|
|
case StunAttribute::Nonce:
|
|
attr = new Nonce();
|
|
break;
|
|
|
|
case StunAttribute::Realm:
|
|
attr = new Realm();
|
|
break;
|
|
|
|
case StunAttribute::Server:
|
|
attr = new Server();
|
|
break;
|
|
|
|
case StunAttribute::Username:
|
|
attr = new Username();
|
|
break;
|
|
|
|
case StunAttribute::ICEPriority:
|
|
attr = new ICEPriority();
|
|
break;
|
|
|
|
case StunAttribute::ControlledAttr:
|
|
attr = new ControlledAttr();
|
|
break;
|
|
|
|
case StunAttribute::ControllingAttr:
|
|
attr = new ControllingAttr();
|
|
break;
|
|
|
|
case StunAttribute::ICEUseCandidate:
|
|
attr = new ICEUseCandidate();
|
|
break;
|
|
|
|
case StunAttribute::XorRelayedAddress:
|
|
attr = new XorRelayedAddress();
|
|
break;
|
|
|
|
case StunAttribute::XorPeerAddress:
|
|
attr = new XorPeerAddress();
|
|
break;
|
|
|
|
case StunAttribute::Lifetime:
|
|
attr = new Lifetime();
|
|
break;
|
|
|
|
case StunAttribute::RequestedTransport:
|
|
attr = new RequestedTransport();
|
|
break;
|
|
|
|
case StunAttribute::RequestedAddressFamily:
|
|
attr = new RequestedAddressFamily();
|
|
break;
|
|
|
|
default:
|
|
attr = NULL;
|
|
}
|
|
|
|
// Get attribute value length
|
|
int attrLen = stream.readUShort();
|
|
|
|
unsigned int dataOffset = stream.count();
|
|
|
|
// Get attribute buffer
|
|
ByteBuffer attrBuffer;
|
|
if (attrLen > 0)
|
|
{
|
|
attrBuffer.resize(attrLen);
|
|
stream.readBuffer(attrBuffer.mutableData(), attrLen);
|
|
}
|
|
|
|
// Skip padding bytes
|
|
if (attrLen & 0x3)
|
|
{
|
|
size_t paddingLength = 4 - (attrLen & 0x3);
|
|
char paddingBytes[4];
|
|
stream.readBuffer(paddingBytes, paddingLength);
|
|
}
|
|
|
|
// Parse attribute value
|
|
if (attr)
|
|
{
|
|
// Parse attribute
|
|
BufferReader attrReader(attrBuffer);
|
|
if (!attr->parsePacket(attrReader))
|
|
return false;
|
|
|
|
// Set data offset
|
|
attr->setDataOffset(dataOffset);
|
|
|
|
// Add attribute
|
|
addAttribute(attr);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool StunMessage::validatePacket(std::string key)
|
|
{
|
|
// Iterate attributes
|
|
if (hasAttribute(StunAttribute::MessageIntegrity))
|
|
{
|
|
// Get HMAC digest
|
|
MessageIntegrity& mi = dynamic_cast<MessageIntegrity&>(attribute(StunAttribute::MessageIntegrity));
|
|
|
|
unsigned char digest[HMAC_DIGEST_SIZE];
|
|
|
|
// Reset length to length of packet without Fingerprint
|
|
unsigned short* lengthPtr = ((unsigned short*)mPacket2Parse.mutableData() + 1);
|
|
|
|
// Save old value - it is for backup purposes only. So there is no call to ntohs().
|
|
unsigned short len = *lengthPtr;
|
|
|
|
// Set fake length including MessageIntegrity attribute but excluding any attributes behind of it and excluding STUN header size
|
|
*lengthPtr = (uint16_t)(htons(mi.dataOffset() + HMAC_DIGEST_SIZE - STUN_HEADER_SIZE));
|
|
|
|
// Find HMAC-SHA1 again
|
|
hmacSha1Digest(mPacket2Parse.data(), mi.dataOffset() - 4, digest, key.c_str(), key.size());
|
|
|
|
// And restore old value
|
|
*lengthPtr = len;
|
|
|
|
if (memcmp(mi.value(), digest, HMAC_DIGEST_SIZE) != 0)
|
|
{
|
|
ICELogCritical(<< "Bad MessageIntegrity in STUN message");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (hasAttribute(StunAttribute::Fingerprint))
|
|
{
|
|
// Get fingerpring attribute
|
|
Fingerprint& fp = dynamic_cast<Fingerprint&>(attribute(StunAttribute::Fingerprint));
|
|
|
|
// Find CRC32
|
|
CRC32 crcObj;
|
|
unsigned long crcValue = crcObj.fullCrc((const unsigned char*)mPacket2Parse.data(), fp.dataOffset() - 4);
|
|
|
|
// Compare with saved one
|
|
if (crcValue != fp.crc32())
|
|
{
|
|
ICELogCritical(<< "Bad CRC32 value in STUN message");
|
|
return false;
|
|
}
|
|
}*/
|
|
|
|
return true;
|
|
}
|
|
|
|
inline char StunMessage::bit(unsigned int value, size_t index)
|
|
{
|
|
unsigned int mask = (1 << index);
|
|
|
|
return value & mask ? 1 : 0;
|
|
}
|
|
|
|
inline void StunMessage::setBit(unsigned int& result, size_t index, char value)
|
|
{
|
|
unsigned int mask = (1 << index);
|
|
if (value != 0)
|
|
result |= mask;
|
|
else
|
|
result &= ~mask;
|
|
}
|
|
|
|
void StunMessage::addAttribute(StunAttribute* attr)
|
|
{
|
|
assert(NULL != attr);
|
|
mAttrMap.insert(std::pair<int, StunAttribute*>(attr->type(), attr));
|
|
}
|
|
|
|
void StunMessage::setAttribute(StunAttribute *attr)
|
|
{
|
|
assert(NULL != attr);
|
|
|
|
AttributeMap::iterator attrIter = mAttrMap.find(attr->type());
|
|
if (attrIter != mAttrMap.end())
|
|
{
|
|
delete attrIter->second;
|
|
attrIter->second = attr;
|
|
}
|
|
else
|
|
mAttrMap.insert(std::pair<int, StunAttribute*>(attr->type(), attr));
|
|
}
|
|
|
|
bool StunMessage::hasAttribute(int attrType) const
|
|
{
|
|
return (mAttrMap.find(attrType) != mAttrMap.end());
|
|
}
|
|
|
|
|
|
StunAttribute& StunMessage::attribute(int attrType)
|
|
{
|
|
AttributeMap::iterator attrIter = mAttrMap.find(attrType);
|
|
if (attrIter != mAttrMap.end())
|
|
return *attrIter->second;
|
|
|
|
throw Exception(CANNOT_FIND_ATTRIBUTE, attrType);
|
|
}
|
|
|
|
const StunAttribute&
|
|
StunMessage::operator [] (int attribute) const
|
|
{
|
|
AttributeMap::iterator attrIter = mAttrMap.find(attribute);
|
|
if (attrIter != mAttrMap.end())
|
|
return *attrIter->second;
|
|
|
|
throw Exception(CANNOT_FIND_ATTRIBUTE, attribute);
|
|
}
|
|
|
|
StunAttribute&
|
|
StunMessage::operator[] (int attribute)
|
|
{
|
|
AttributeMap::iterator attrIter = mAttrMap.find(attribute);
|
|
if (attrIter != mAttrMap.end())
|
|
return *attrIter->second;
|
|
|
|
// Create attribute
|
|
StunAttribute* attr = NULL;
|
|
|
|
switch (attribute)
|
|
{
|
|
case StunAttribute::MappedAddress:
|
|
attr = new MappedAddress();
|
|
break;
|
|
|
|
case StunAttribute::XorMappedAddress:
|
|
attr = new XorMappedAddress();
|
|
break;
|
|
|
|
case StunAttribute::ErrorCode:
|
|
attr = new ErrorCode();
|
|
break;
|
|
|
|
case StunAttribute::AlternateServer:
|
|
attr = new AlternateServer();
|
|
break;
|
|
|
|
case StunAttribute::UnknownAttributes:
|
|
attr = new UnknownAttributes();
|
|
break;
|
|
|
|
case StunAttribute::Fingerprint:
|
|
attr = new Fingerprint();
|
|
break;
|
|
|
|
case StunAttribute::MessageIntegrity:
|
|
attr = new MessageIntegrity();
|
|
break;
|
|
|
|
case StunAttribute::Nonce:
|
|
attr = new Nonce();
|
|
break;
|
|
|
|
case StunAttribute::Realm:
|
|
attr = new Realm();
|
|
break;
|
|
|
|
case StunAttribute::Server:
|
|
attr = new Server();
|
|
break;
|
|
|
|
case StunAttribute::Username:
|
|
attr = new Username();
|
|
break;
|
|
|
|
case StunAttribute::ICEPriority:
|
|
attr = new ICEPriority();
|
|
break;
|
|
|
|
case StunAttribute::ControlledAttr:
|
|
attr = new ControlledAttr();
|
|
break;
|
|
|
|
case StunAttribute::ControllingAttr:
|
|
attr = new ControllingAttr();
|
|
break;
|
|
|
|
case StunAttribute::ICEUseCandidate:
|
|
attr = new ICEUseCandidate();
|
|
break;
|
|
|
|
case StunAttribute::XorRelayedAddress:
|
|
attr = new XorRelayedAddress();
|
|
break;
|
|
|
|
case StunAttribute::Lifetime:
|
|
attr = new Lifetime();
|
|
break;
|
|
|
|
default:
|
|
attr = NULL;
|
|
}
|
|
|
|
if (!attr)
|
|
throw Exception(UNKNOWN_ATTRIBUTE, attribute);
|
|
|
|
mAttrMap.insert(std::pair<int, StunAttribute*>(attribute, attr));
|
|
return *attr;
|
|
}
|
|
|
|
Username&
|
|
StunMessage::usernameAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<Username&>(self[StunAttribute::Username]);
|
|
}
|
|
|
|
Realm&
|
|
StunMessage::realmAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<Realm&>(self[StunAttribute::Realm]);
|
|
}
|
|
|
|
ErrorCode&
|
|
StunMessage::errorCodeAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<ErrorCode&>(self[StunAttribute::ErrorCode]);
|
|
}
|
|
|
|
Nonce&
|
|
StunMessage::nonceAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<Nonce&>(self[StunAttribute::Nonce]);
|
|
}
|
|
|
|
MessageIntegrity&
|
|
StunMessage::messageIntegrityAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<MessageIntegrity&>(self[StunAttribute::MessageIntegrity]);
|
|
}
|
|
|
|
MappedAddress&
|
|
StunMessage::mappedAddressAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<MappedAddress&>(self[StunAttribute::MappedAddress]);
|
|
}
|
|
|
|
XorMappedAddress&
|
|
StunMessage::xorMappedAddressAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<XorMappedAddress&>(self[StunAttribute::XorMappedAddress]);
|
|
}
|
|
|
|
ControlledAttr&
|
|
StunMessage::iceControlledAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<ControlledAttr&>(self[StunAttribute::ControlledAttr]);
|
|
}
|
|
|
|
ControllingAttr&
|
|
StunMessage::iceControllingAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<ControllingAttr&>(self[StunAttribute::ControllingAttr]);
|
|
}
|
|
|
|
ICEPriority&
|
|
StunMessage::icePriorityAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<ICEPriority&>(self[StunAttribute::ICEPriority]);
|
|
}
|
|
|
|
Lifetime&
|
|
StunMessage::lifetimeAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<Lifetime&>(self[StunAttribute::Lifetime]);
|
|
}
|
|
|
|
XorRelayedAddress&
|
|
StunMessage::xorRelayedAddressAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
return dynamic_cast<XorRelayedAddress&>(self[StunAttribute::XorRelayedAddress]);
|
|
}
|
|
|
|
ChannelNumber&
|
|
StunMessage::channelNumberAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
if (!self.hasAttribute(StunAttribute::ChannelNumber))
|
|
self.addAttribute(new ChannelNumber());
|
|
|
|
return dynamic_cast<ChannelNumber&>(self[StunAttribute::ChannelNumber]);
|
|
}
|
|
|
|
XorPeerAddress&
|
|
StunMessage::xorPeerAddressAttr()
|
|
{
|
|
StunMessage& self = *this;
|
|
if (!self.hasAttribute(StunAttribute::XorPeerAddress))
|
|
self.addAttribute(new XorPeerAddress());
|
|
return dynamic_cast<XorPeerAddress&>(self[StunAttribute::XorPeerAddress]);
|
|
}
|
|
|
|
class CompareAttributesByOffsetFunctor
|
|
{
|
|
public:
|
|
bool operator () (const StunAttribute* attr1, const StunAttribute* attr2)
|
|
{
|
|
return attr1->dataOffset() < attr2->dataOffset();
|
|
}
|
|
};
|
|
|
|
void StunMessage::dump(std::ostream& output)
|
|
{
|
|
switch (messageClass())
|
|
{
|
|
case RequestClass: output << "Request"; break;
|
|
case IndicationClass: output << "Indication"; break;
|
|
case SuccessClass: output << "Success"; break;
|
|
case ErrorClass: output << "Error"; break;
|
|
default:
|
|
output << "Invalid";
|
|
break;
|
|
}
|
|
|
|
output << " / ";
|
|
switch (messageType())
|
|
{
|
|
case Binding: output << "Binding"; break;
|
|
case Allocate: output << "Allocate"; break;
|
|
case Refresh: output << "Refresh"; break;
|
|
case Send: output << "Send"; break;
|
|
case Data: output << "Data"; break;
|
|
case CreatePermission: output << "CreatePermission"; break;
|
|
case ChannelBind: output << "ChannelBind"; break;
|
|
default:
|
|
output << "Invalid";
|
|
break;
|
|
}
|
|
|
|
output << std::endl;
|
|
|
|
// Sort attribytes by data offset
|
|
std::vector<StunAttribute*> attrList;
|
|
for (AttributeMap::iterator attrIter = mAttrMap.begin(); attrIter != mAttrMap.end(); attrIter++)
|
|
attrList.push_back(attrIter->second);
|
|
|
|
std::sort(attrList.begin(), attrList.end(), CompareAttributesByOffsetFunctor());
|
|
|
|
for (unsigned i=0; i<attrList.size(); i++)
|
|
{
|
|
output << "Offset " << attrList[i]->dataOffset() <<": ";
|
|
attrList[i]->dump(output);
|
|
output << std::endl;
|
|
}
|
|
}
|