Added support for call token IAX extension. The frame is now keeping its own IE list to avoid parsing it again.

git-svn-id: http://voip.null.ro/svn/yate@4480 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
marian 2011-07-11 12:46:14 +00:00
parent 253c8006af
commit 8503442db4
6 changed files with 677 additions and 194 deletions

View File

@ -11,6 +11,25 @@
; Defaults to yes
;force_bind=yes
; calltoken_in: boolean: Use call token ip address authentication on incoming calls
; Note: If the caller don't support the call token IAX extension the call request
; will be ignored anyway
; This parameter is applied on reload
; Defaults to no
;calltoken_in=no
; calltoken_out: boolean: Offer call token ip address authentication on outgoing calls
; This parameter is applied on reload and can be overridden from routing
; Defaults to yes
;calltoken_out=yes
; calltoken_rejectmissing: boolean: Reject incoming calls without call token support
; when calltoken_in is enabled
; If disabled the requests will be ignored (dropped)
; This parameter is applied on reload
; Defaults to yes
;calltoken_rejectmissing=yes
; tos: keyword: Type Of Service to set in outgoing UDP packets
; numeric TOS value or: lowdelay, throughput, reliability, mincost
;tos=0
@ -29,6 +48,11 @@
; It is a bad idea to set a low priority for anything but testing
;thread=normal
; printmsg: boolean: Print sent/received frames to output if the module's debug
; level is at least 9
; This parameter is applied on reload
; Defaults to yes
;printmsg=yes
[formats]
; This section allows to individually enable or disable the codecs

View File

@ -30,6 +30,25 @@
using namespace TelEngine;
// Local call number to set when rejecting calls with missing call token
#define IAX2_CALLTOKEN_REJ_CALLNO 1
// Local call number to set when sending call token message
#define IAX2_CALLTOKEN_CALLNO 1
// Minimum value for local call numbers
#define IAX2_MIN_CALLNO 2
// Build an MD5 digest from secret, address, integer value and engine run id
// MD5(addr.host() + secret + addr.port() + t)
static void buildSecretDigest(String& buf, const String& secret, unsigned int t,
const SocketAddr& addr)
{
String tmp;
tmp << addr.host() << secret << addr.port() << t;
MD5 md5(tmp);
buf << md5.hexDigest();
}
IAXEngine::IAXEngine(const char* iface, int port, u_int16_t transListCount, u_int16_t retransCount, u_int16_t retransInterval,
u_int16_t authTimeout, u_int16_t transTimeout, u_int16_t maxFullFrameDataLen,
u_int32_t format, u_int32_t capab, u_int32_t trunkSendInterval, bool authRequired,
@ -44,6 +63,10 @@ IAXEngine::IAXEngine(const char* iface, int port, u_int16_t transListCount, u_in
m_retransInterval(retransInterval),
m_authTimeout(authTimeout),
m_transTimeout(transTimeout),
m_callToken(false),
m_callTokenAge(10),
m_showCallTokenFailures(false),
m_printMsg(true),
m_format(format),
m_capability(capab),
m_mutexTrunk(true,"IAXEngine::Trunk"),
@ -65,6 +88,11 @@ IAXEngine::IAXEngine(const char* iface, int port, u_int16_t transListCount, u_in
m_transListCount = transListCount;
for(i = 0; i <= IAX2_MAX_CALLNO; i++)
m_lUsedCallNo[i] = false;
if (params)
m_callTokenSecret = params->getValue("calltoken_secret");
if (!m_callTokenSecret)
for (i = 0; i < 3; i++)
m_callTokenSecret << (int)(Random::random() ^ Time::now());
m_socket.create(AF_INET,SOCK_DGRAM);
SocketAddr addr(AF_INET);
addr.host(iface);
@ -93,6 +121,12 @@ IAXEngine::IAXEngine(const char* iface, int port, u_int16_t transListCount, u_in
if (ok)
Debug(this,DebugInfo,"Bound on '%s:%d'",addr.host().c_str(),addr.port());
m_startLocalCallNo = 1 + (u_int16_t)(Random::random() % IAX2_MAX_CALLNO);
if (m_startLocalCallNo < IAX2_MIN_CALLNO)
m_startLocalCallNo = IAX2_MIN_CALLNO;
if (params)
initialize(*params);
else
initialize(NamedList::empty());
}
IAXEngine::~IAXEngine()
@ -111,12 +145,29 @@ IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame)
Lock lock(this);
// Transaction exists for this frame?
// Incomplete transactions. They MUST receive a full frame
if (frame->fullFrame()) {
IAXFullFrame* fullFrame = frame->fullFrame();
if (fullFrame) {
l = m_incompleteTransList.skipNull();
for (; l; l = l->next()) {
tr = static_cast<IAXTransaction*>(l->get());
if (!(tr && tr->localCallNo() == frame->fullFrame()->destCallNo() && addr == tr->remoteAddr()))
if (!(tr && tr->localCallNo() == fullFrame->destCallNo() && addr == tr->remoteAddr()))
continue;
// Incomplete outgoing receiving call token
if (fullFrame->type() == IAXFrame::IAX &&
fullFrame->subclass() == IAXControl::CallToken) {
RefPointer<IAXTransaction> t = tr;
lock.drop();
if (!t)
return 0;
fullFrame->updateIEList(true);
IAXIEList* list = fullFrame->ieList();
DataBlock db;
if (list)
list->getBinary(IAXInfoElement::CALLTOKEN,db);
t->processCallToken(db);
t = 0;
return 0;
}
// Complete transaction
if (tr->processFrame(frame)) {
tr->m_rCallNo = frame->sourceCallNo();
@ -136,7 +187,7 @@ IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame)
if (!(tr && tr->remoteCallNo() == frame->sourceCallNo()))
continue;
// Mini frame
if (!frame->fullFrame()) {
if (!fullFrame) {
if (addr == tr->remoteAddr()) {
// keep transaction referenced but unlock the engine
RefPointer<IAXTransaction> t = tr;
@ -147,7 +198,7 @@ IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame)
}
// Full frame
// Has a local number assigned? If not, test socket
if ((frame->fullFrame())->destCallNo() || addr == tr->remoteAddr()) {
if (fullFrame->destCallNo() || addr == tr->remoteAddr()) {
// keep transaction referenced but unlock the engine
RefPointer<IAXTransaction> t = tr;
lock.drop();
@ -156,10 +207,12 @@ IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame)
}
// Frame doesn't belong to an existing transaction
// Test if it is a full frame with an IAX control message that needs a new transaction
if (!frame->fullFrame() || frame->type() != IAXFrame::IAX)
if (!fullFrame || frame->type() != IAXFrame::IAX)
return 0;
switch (frame->fullFrame()->subclass()) {
switch (fullFrame->subclass()) {
case IAXControl::New:
if (!checkCallToken(addr,*fullFrame))
return 0;
case IAXControl::RegReq:
case IAXControl::RegRel:
case IAXControl::Poke:
@ -170,14 +223,14 @@ IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame)
case IAXControl::FwDownl:
default:
#ifdef DEBUG
if (frame->fullFrame()) {
if (frame->fullFrame()->destCallNo() == 0)
if (fullFrame) {
if (fullFrame->destCallNo() == 0)
Debug(this,DebugAll,"Unsupported incoming transaction Frame(%u,%u). Source call no: %u",
frame->type(),frame->fullFrame()->subclass(),frame->fullFrame()->sourceCallNo());
frame->type(),fullFrame->subclass(),fullFrame->sourceCallNo());
#ifdef XDEBUG
else
Debug(this,DebugAll,"Unmatched Frame(%u,%u) for (%u,%u)",
frame->type(),frame->fullFrame()->subclass(),frame->fullFrame()->destCallNo(),frame->fullFrame()->sourceCallNo());
frame->type(),fullFrame->subclass(),fullFrame->destCallNo(),fullFrame->sourceCallNo());
#endif
}
#endif
@ -188,7 +241,7 @@ IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame)
if (!lcn)
return 0;
// Create and add transaction
tr = IAXTransaction::factoryIn(this,(IAXFullFrame*)frame->fullFrame(),lcn,addr);
tr = IAXTransaction::factoryIn(this,fullFrame,lcn,addr);
if (tr)
m_transList[frame->sourceCallNo() % m_transListCount]->append(tr);
else
@ -201,7 +254,7 @@ IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, const unsigned char*
IAXFrame* frame = IAXFrame::parse(buf,len,this,&addr);
if (!frame)
return 0;
if (frame->fullFrame() && debugAt(DebugAll)) {
if (m_printMsg && frame->fullFrame() && debugAt(DebugInfo)) {
String s;
SocketAddr local;
m_socket.getSockName(local);
@ -233,6 +286,20 @@ bool IAXEngine::process()
return ok;
}
// (Re)Initialize the engine
void IAXEngine::initialize(const NamedList& params)
{
m_callToken = params.getBoolValue("calltoken_in");
int callTokenAge = params.getIntValue("calltoken_age",10);
if (callTokenAge > 1 && callTokenAge < 25)
m_callTokenAge = callTokenAge;
else
m_callTokenAge = 10;
m_showCallTokenFailures = params.getBoolValue("calltoken_printfailure");
m_rejectMissingCallToken = params.getBoolValue("calltoken_rejectmissing",true);
m_printMsg = params.getBoolValue("printmsg",true);
}
void IAXEngine::readSocket(SocketAddr& addr)
{
unsigned char buf[1500];
@ -255,7 +322,7 @@ void IAXEngine::readSocket(SocketAddr& addr)
bool IAXEngine::writeSocket(const void* buf, int len, const SocketAddr& addr, IAXFullFrame* frame)
{
if (frame && debugAt(DebugAll)) {
if (m_printMsg && frame && debugAt(DebugInfo)) {
String s;
SocketAddr local;
m_socket.getSockName(local);
@ -412,13 +479,13 @@ u_int16_t IAXEngine::generateCallNo()
m_startLocalCallNo++;
if (m_startLocalCallNo > IAX2_MAX_CALLNO)
m_startLocalCallNo = 1;
m_startLocalCallNo = IAX2_MIN_CALLNO;
for (i = m_startLocalCallNo; i <= IAX2_MAX_CALLNO; i++)
if (!m_lUsedCallNo[i]) {
m_lUsedCallNo[i] = true;
return i;
}
for (i = 1; i < m_startLocalCallNo; i++)
for (i = IAX2_MIN_CALLNO; i < m_startLocalCallNo; i++)
if (!m_lUsedCallNo[i]) {
m_lUsedCallNo[i] = true;
return i;
@ -449,6 +516,63 @@ IAXTransaction* IAXEngine::startLocalTransaction(IAXTransaction::Type type, cons
return tr;
}
// Check call token on incoming call requests.
bool IAXEngine::checkCallToken(const SocketAddr& addr, IAXFullFrame& frame)
{
XDebug(this,DebugAll,"IAXEngine::checkCallToken('%s:%d') calltoken=%u",
addr.host().c_str(),addr.port(),m_callToken);
if (!m_callToken)
return true;
frame.updateIEList(true);
IAXIEList* list = frame.ieList();
IAXInfoElementBinary* ct = 0;
if (list)
ct = static_cast<IAXInfoElementBinary*>(list->getIE(IAXInfoElement::CALLTOKEN));
// No call token support
if (!ct) {
if (m_showCallTokenFailures)
Debug(this,DebugNote,
"Missing required %s parameter in call request %u from '%s:%d'",
IAXInfoElement::ieText(IAXInfoElement::CALLTOKEN),frame.sourceCallNo(),
addr.host().c_str(),addr.port());
if (m_rejectMissingCallToken) {
IAXIEList* ies = new IAXIEList;
ies->appendString(IAXInfoElement::CAUSE,"CALLTOKEN support required");
IAXFullFrame* rsp = new IAXFullFrame(IAXFrame::IAX,IAXControl::Reject,
IAX2_CALLTOKEN_REJ_CALLNO,frame.sourceCallNo(),0,1,2,
ies,maxFullFrameDataLen());
writeSocket(addr,rsp);
TelEngine::destruct(rsp);
}
return false;
}
// Request with call token
if (ct->data().length()) {
String tmp((char*)ct->data().data(),ct->data().length());
int age = addrSecretAge(tmp,m_callTokenSecret,addr);
XDebug(this,DebugAll,"Call request %u from '%s:%d' with call token age=%d",
frame.sourceCallNo(),addr.host().c_str(),addr.port(),age);
if (age >= 0 && age <= m_callTokenAge)
return true;
if (m_showCallTokenFailures)
Debug(this,DebugNote,
"Ignoring call request %u from '%s:%d' with %s call token age=%d",
frame.sourceCallNo(),addr.host().c_str(),addr.port(),
(age > 0) ? "old" : "invalid",age);
return false;
}
// Request with empty call token: send one
String tmp;
buildAddrSecret(tmp,m_callTokenSecret,addr);
IAXIEList* ies = new IAXIEList;
ies->appendBinary(IAXInfoElement::CALLTOKEN,(unsigned char*)tmp.c_str(),tmp.length());
IAXFullFrame* rsp = new IAXFullFrame(IAXFrame::IAX,IAXControl::CallToken,
IAX2_CALLTOKEN_CALLNO,frame.sourceCallNo(),0,1,1,ies,maxFullFrameDataLen());
writeSocket(addr,rsp);
TelEngine::destruct(rsp);
return false;
}
bool IAXEngine::acceptFormatAndCapability(IAXTransaction* trans)
{
if (!trans)
@ -567,27 +691,52 @@ bool IAXEngine::isMD5ChallengeCorrect(const String& md5data, const String& chall
return md5data == md5.hexDigest();
}
// Build a time signed secret used to authenticate an IP address
void IAXEngine::buildAddrSecret(String& buf, const String& secret, const SocketAddr& addr)
{
unsigned int t = Time::secNow();
buildSecretDigest(buf,secret,t,addr);
buf << "." << t;
}
// Decode a secret built using buildAddrSecret()
int IAXEngine::addrSecretAge(const String& buf, const String& secret, const SocketAddr& addr)
{
int pos = buf.find('.');
if (pos < 1)
return -1;
int t = buf.substr(pos + 1).toInteger();
String tmp;
buildSecretDigest(tmp,secret,t,addr);
return (tmp == buf.substr(0,pos)) ? (Time::secNow() - t) : -1;
}
/*
* IAXEvent
*/
IAXEvent::IAXEvent(Type type, bool local, bool final, IAXTransaction* transaction, u_int8_t frameType, u_int32_t subclass)
: m_type(type), m_frameType(frameType), m_subClass(subclass), m_local(local), m_final(final), m_transaction(0)
: m_type(type), m_frameType(frameType), m_subClass(subclass),
m_local(local), m_final(final), m_transaction(0), m_ieList(0)
{
if (transaction && transaction->ref())
m_transaction = transaction;
m_ieList = new IAXIEList;
}
IAXEvent::IAXEvent(Type type, bool local, bool final, IAXTransaction* transaction, const IAXFullFrame* frame)
: m_type(type), m_frameType(0), m_subClass(0), m_local(local), m_final(final), m_transaction(0), m_ieList(frame)
IAXEvent::IAXEvent(Type type, bool local, bool final, IAXTransaction* transaction, IAXFullFrame* frame)
: m_type(type), m_frameType(0), m_subClass(0), m_local(local),
m_final(final), m_transaction(0), m_ieList(0)
{
if (transaction && transaction->ref())
m_transaction = transaction;
if (frame) {
m_frameType = frame->type();
m_subClass = frame->subclass();
frame->updateIEList(true);
m_ieList = frame->removeIEList(false);
}
if (!m_ieList)
m_ieList = new IAXIEList;
}
IAXEvent::~IAXEvent()
@ -598,6 +747,8 @@ IAXEvent::~IAXEvent()
m_transaction->eventTerminated(this);
m_transaction->deref();
}
if (m_ieList)
delete m_ieList;
}
/* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -101,6 +101,7 @@ TokenDict IAXInfoElement::s_ieData[] = {
{"RR_DELAY", RR_DELAY},
{"RR_DROPPED", RR_DROPPED},
{"RR_OOO", RR_OOO},
{"CALLTOKEN", CALLTOKEN},
{0,0}
};
@ -186,7 +187,11 @@ void IAXInfoElementBinary::toBuffer(DataBlock& buf)
void IAXInfoElementBinary::toString(String& buf)
{
buf << "Binary data";
if (!m_data.length())
return;
String tmp;
tmp.hexify(m_data.data(),m_data.length(),' ');
buf << tmp;
}
IAXInfoElementBinary* IAXInfoElementBinary::packIP(const SocketAddr& addr)
@ -206,6 +211,25 @@ bool IAXInfoElementBinary::unpackIP(SocketAddr& addr, IAXInfoElementBinary* ie)
/*
* IAXIEList
*/
IAXIEList::IAXIEList()
: m_invalidIEList(false)
{
XDebug(DebugInfo,"IAXIEList::IAXIEList() [%p]",this);
}
IAXIEList::IAXIEList(const IAXFullFrame* frame, bool incoming)
: m_invalidIEList(false)
{
XDebug(DebugInfo,"IAXIEList::IAXIEList(%p,%u) [%p]",frame,incoming,this);
if (frame)
createFromFrame(frame,incoming);
}
IAXIEList::~IAXIEList()
{
XDebug(DebugInfo,"IAXIEList::~IAXIEList() [%p]",this);
}
void IAXIEList::insertVersion()
{
if (!getIE(IAXInfoElement::VERSION))
@ -282,6 +306,7 @@ bool IAXIEList::createFromFrame(const IAXFullFrame* frame, bool incoming)
case IAXInfoElement::SERVICEIDENT: // Length must be 6
case IAXInfoElement::FWBLOCKDATA: // Length can be 0
case IAXInfoElement::ENKEY:
case IAXInfoElement::CALLTOKEN:
if (data[i-1] == IAXInfoElement::SERVICEIDENT && data[i] != 6) {
i = 0xFFFF;
break;
@ -353,9 +378,10 @@ bool IAXIEList::createFromFrame(const IAXFullFrame* frame, bool incoming)
i += 1;
break;
default:
Debug(DebugWarn,"IAXIEList::createFromFrame. Frame(%u,%u) with unknown IE identifier %u [%p]",
Debug(DebugInfo,"IAX Frame(%u,%u) with unknown IE identifier %u [%p]",
frame->type(),frame->subclass(),data[i-1],frame);
i = 0xFFFF;
appendBinary((IAXInfoElement::Type)data[i-1],data+i+1,data[i]);
i += data[i] + 1;
}
if (i == 0xFFFF)
break;
@ -393,7 +419,15 @@ void IAXIEList::toString(String& dest, const char* indent)
ie->toString(dest);
continue;
}
dest << IAXInfoElement::ieText(ie->type());
const char* name = IAXInfoElement::ieText(ie->type());
if (name)
dest << name;
else {
u_int8_t t = ie->type();
String tmp;
tmp.hexify(&t,1);
dest << "0x" << tmp;
}
if (ie->type() != IAXInfoElement::AUTOANSWER)
dest << ": ";
switch (ie->type()) {
@ -436,6 +470,7 @@ void IAXIEList::toString(String& dest, const char* indent)
case IAXInfoElement::SERVICEIDENT:
case IAXInfoElement::FWBLOCKDATA:
case IAXInfoElement::ENKEY:
case IAXInfoElement::CALLTOKEN:
ie->toString(dest);
break;
// 4 bytes
@ -510,6 +545,7 @@ void IAXIEList::toString(String& dest, const char* indent)
case IAXInfoElement::AUTOANSWER:
break;
default: ;
ie->toString(dest);
}
}
}
@ -660,6 +696,7 @@ TokenDict IAXControl::s_types[] = {
{"PROVISION", Provision},
{"FWDOWNL", FwDownl},
{"FWDATA", FwData},
{"CALLTOKEN", CallToken},
{0,0}
};
@ -818,7 +855,7 @@ u_int32_t IAXFrame::unpackSubclass(u_int8_t value)
return value;
}
const IAXFullFrame* IAXFrame::fullFrame() const
IAXFullFrame* IAXFrame::fullFrame()
{
return 0;
}
@ -850,7 +887,8 @@ IAXFullFrame::IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_i
u_int32_t tStamp, bool retrans,
const unsigned char* buf, unsigned int len)
: IAXFrame(type,sCallNo,tStamp,retrans,buf,len),
m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass)
m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass),
m_ieList(0)
{
// XDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]",
// type,subclass,this);
@ -861,42 +899,31 @@ IAXFullFrame::IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_i
u_int32_t tStamp,
const unsigned char* buf, unsigned int len)
: IAXFrame(type,sCallNo,tStamp,false,0,0),
m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass)
m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass),
m_ieList(0)
{
// XDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]",
// type,subclass,this);
setDataHeader();
if (buf)
m_data.append((void*)buf,(unsigned int)len);
}
unsigned char header[12];
DataBlock ie;
// Full frame flag + Source call number
header[0] = 0x80 | (unsigned char)(sourceCallNo() >> 8);
header[1] = (unsigned char)(sourceCallNo());
// Retrans + Destination call number
header[2] = (unsigned char)(destCallNo() >> 8); // retrans is false: bit 7 is 0
header[3] = (unsigned char)destCallNo();
// Timestamp
header[4] = (unsigned char)(timeStamp() >> 24);
header[5] = (unsigned char)(timeStamp() >> 16);
header[6] = (unsigned char)(timeStamp() >> 8);
header[7] = (unsigned char)timeStamp();
// oSeqNo + iSeqNo
header[8] = m_oSeqNo;
header[9] = m_iSeqNo;
// Type
header[10] = type;
// Subclass
header[11] = packSubclass(m_subclass);
// Set data
m_data.assign(header,sizeof(header));
if (buf) {
ie.assign((void*)buf,(unsigned int)len);
m_data += ie;
}
// Constructor. Constructs an outgoing full frame
IAXFullFrame::IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo,
u_int32_t tStamp, IAXIEList* ieList, u_int16_t maxlen)
: IAXFrame(type,sCallNo,tStamp,false,0,0),
m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass),
m_ieList(ieList)
{
// XDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]",
// type,subclass,this);
updateBuffer(maxlen);
}
void IAXFullFrame::toString(String& dest, const SocketAddr& local,
const SocketAddr& remote, bool incoming) const
const SocketAddr& remote, bool incoming)
{
#define STARTLINE(indent) "\r\n" << indent
#define TMP_TEXT (tmp ? tmp : unk)
@ -941,7 +968,6 @@ void IAXFullFrame::toString(String& dest, const SocketAddr& local,
break;
default:
subc = unk;
}
setStringFromInteger(stmp,subclass(),4);
dest << " - " << subc << " (" << stmp << ")";
@ -969,16 +995,15 @@ void IAXFullFrame::toString(String& dest, const SocketAddr& local,
dest << ". Retrans: " << String::boolText(retrans());
dest << ". Sequence numbers: Out: " << oSeqNo() << " In: " << iSeqNo();
// IEs
IAXIEList ieList;
bool hasIE = ieList.createFromFrame(this,incoming);
if (hasIE) {
updateIEList(incoming);
if (!m_ieList->empty()) {
String aux;
aux << STARTLINE(" ");
ieList.toString(dest,aux);
m_ieList->toString(dest,aux);
}
if (!hasIE) {
if (m_ieList->empty()) {
dest << STARTLINE(" ");
if (ieList.invalidIEList())
if (m_ieList->invalidIEList())
dest << "Error parsing Information Element(s)";
else
dest << "No Information Element(s)";
@ -988,17 +1013,87 @@ void IAXFullFrame::toString(String& dest, const SocketAddr& local,
#undef STARTLINE
}
// Rebuild frame buffer from the list of IEs
void IAXFullFrame::updateBuffer(u_int16_t maxlen)
{
setDataHeader();
if (!m_ieList)
return;
DataBlock tmp;
m_ieList->toBuffer(tmp);
if (tmp.length() <= maxlen)
m_data += tmp;
else
Debug(DebugNote,"Frame(%u,%u) buffer too long (%u > %u) [%p]",
type(),subclass(),tmp.length(),maxlen,this);
}
// Update IE list from buffer if not already done
bool IAXFullFrame::updateIEList(bool incoming)
{
if (!m_ieList)
m_ieList = new IAXIEList(this,incoming);
return !m_ieList->invalidIEList();
}
// Remove the IE list
IAXIEList* IAXFullFrame::removeIEList(bool delObj)
{
if (!m_ieList)
return 0;
IAXIEList* old = m_ieList;
m_ieList = 0;
if (delObj) {
delete old;
old = 0;
}
return old;
}
IAXFullFrame::~IAXFullFrame()
{
// XDebug(DebugAll,"IAXFullFrame::~IAXFullFrame(%u,%u) [%p]",
// type(),m_subclass,this);
}
const IAXFullFrame* IAXFullFrame::fullFrame() const
IAXFullFrame* IAXFullFrame::fullFrame()
{
return this;
}
// Destroyed notification. Clear data
void IAXFullFrame::destroyed()
{
removeIEList();
IAXFrame::destroyed();
}
// Build frame buffer header
void IAXFullFrame::setDataHeader()
{
unsigned char header[12];
// Full frame flag + Source call number
header[0] = 0x80 | (unsigned char)(sourceCallNo() >> 8);
header[1] = (unsigned char)(sourceCallNo());
// Retrans + Destination call number
header[2] = (unsigned char)(destCallNo() >> 8); // retrans is false: bit 7 is 0
header[3] = (unsigned char)destCallNo();
// Timestamp
header[4] = (unsigned char)(timeStamp() >> 24);
header[5] = (unsigned char)(timeStamp() >> 16);
header[6] = (unsigned char)(timeStamp() >> 8);
header[7] = (unsigned char)timeStamp();
// oSeqNo + iSeqNo
header[8] = m_oSeqNo;
header[9] = m_iSeqNo;
// Type
header[10] = type();
// Subclass
header[11] = packSubclass(m_subclass);
// Set data
m_data.assign(header,sizeof(header));
}
/*
* IAXFrameOut
*/

View File

@ -69,7 +69,7 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t
m_format(0),
m_formatIn(0),
m_formatOut(0),
m_capability(0),
m_capability(0), m_callToken(false),
m_trunkFrame(0)
{
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) incoming [%p]",
@ -137,7 +137,7 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
m_format(0),
m_formatIn(0),
m_formatOut(0),
m_capability(0),
m_capability(0), m_callToken(false),
m_trunkFrame(0)
{
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) outgoing. [%p]",
@ -153,25 +153,27 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
m_retransInterval = engine->retransInterval();
m_timeToNextPing = m_timeStamp + m_pingInterval;
init(ieList);
ieList.clear();
IAXControl::Type frametype;
IAXIEList* ies = new IAXIEList;
// Create IE list to send
switch (type) {
case New:
ieList.insertVersion();
ieList.appendString(IAXInfoElement::USERNAME,m_username);
ieList.appendString(IAXInfoElement::CALLING_NUMBER,m_callingNo);
ieList.appendString(IAXInfoElement::CALLING_NAME,m_callingName);
ieList.appendString(IAXInfoElement::CALLED_NUMBER,m_calledNo);
ieList.appendString(IAXInfoElement::CALLED_CONTEXT,m_calledContext);
ieList.appendNumeric(IAXInfoElement::FORMAT,m_format,4);
ieList.appendNumeric(IAXInfoElement::CAPABILITY,m_capability,4);
ies->insertVersion();
ies->appendString(IAXInfoElement::USERNAME,m_username);
ies->appendString(IAXInfoElement::CALLING_NUMBER,m_callingNo);
ies->appendString(IAXInfoElement::CALLING_NAME,m_callingName);
ies->appendString(IAXInfoElement::CALLED_NUMBER,m_calledNo);
ies->appendString(IAXInfoElement::CALLED_CONTEXT,m_calledContext);
ies->appendNumeric(IAXInfoElement::FORMAT,m_format,4);
ies->appendNumeric(IAXInfoElement::CAPABILITY,m_capability,4);
if (m_callToken)
ies->appendBinary(IAXInfoElement::CALLTOKEN,0,0);
frametype = IAXControl::New;
break;
case RegReq:
case RegRel:
ieList.appendString(IAXInfoElement::USERNAME,m_username);
ieList.appendNumeric(IAXInfoElement::REFRESH,m_expire,2);
ies->appendString(IAXInfoElement::USERNAME,m_username);
ies->appendNumeric(IAXInfoElement::REFRESH,m_expire,2);
frametype = (type == RegReq ? IAXControl::RegReq : IAXControl::RegRel);
break;
case Poke:
@ -180,17 +182,11 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
default:
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) outgoing. Unsupported type: %u. [%p]",
localCallNo(),remoteCallNo(),m_type,this);
delete ies;
m_type = Incorrect;
return;
}
DataBlock d;
ieList.toBuffer(d);
if (d.length() > (unsigned int)m_engine->maxFullFrameDataLen()) {
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u). Buffer too long (%u > %u). [%p]",
localCallNo(),remoteCallNo(),d.length(),(unsigned int)m_engine->maxFullFrameDataLen(),this);
d.clear();
}
postFrame(IAXFrame::IAX,frametype,(void*)(d.data()),d.length());
postFrameIes(IAXFrame::IAX,frametype,ies);
changeState(NewLocalInvite);
}
@ -475,19 +471,18 @@ bool IAXTransaction::sendAccept()
((type() == RegReq || type() == RegRel) && state() == NewRemoteInvite_RepRecv)))
return false;
if (type() == New) {
unsigned char d[12] = {IAXInfoElement::FORMAT,4,m_format >> 24,m_format >> 16,m_format >> 8,m_format,
IAXInfoElement::CAPABILITY,4,m_capability >> 24,m_capability >> 16,m_capability >> 8,m_capability};
postFrame(IAXFrame::IAX,IAXControl::Accept,d,sizeof(d),0,true);
IAXIEList* ies = new IAXIEList;
ies->appendNumeric(IAXInfoElement::FORMAT,m_format,4);
ies->appendNumeric(IAXInfoElement::CAPABILITY,m_capability,4);
postFrameIes(IAXFrame::IAX,IAXControl::Accept,ies,0,true);
changeState(Connected);
}
else {
IAXIEList ieList;
ieList.appendString(IAXInfoElement::USERNAME,m_username);
ieList.appendNumeric(IAXInfoElement::REFRESH,m_expire,2);
ieList.appendIE(IAXInfoElementBinary::packIP(remoteAddr()));
DataBlock data;
ieList.toBuffer(data);
postFrame(IAXFrame::IAX,IAXControl::RegAck,data.data(),data.length(),0,true);
IAXIEList* ies = new IAXIEList;
ies->appendString(IAXInfoElement::USERNAME,m_username);
ies->appendNumeric(IAXInfoElement::REFRESH,m_expire,2);
ies->appendIE(IAXInfoElementBinary::packIP(remoteAddr()));
postFrameIes(IAXFrame::IAX,IAXControl::RegAck,ies,0,true);
changeState(Terminating);
m_localReqEnd = true;
}
@ -496,27 +491,15 @@ bool IAXTransaction::sendAccept()
bool IAXTransaction::sendHangup(const char* cause, u_int8_t code)
{
String s(cause);
unsigned char d[3];
DataBlock data,aux;
Lock lock(this);
if (type() != New || state() == Terminated || state() == Terminating)
return false;
if (cause) {
d[0] = IAXInfoElement::CAUSE;
d[1] = s.length();
data.assign(d,2);
data.append(s);
}
if (code) {
d[0] = IAXInfoElement::CAUSECODE;
d[1] = 1;
d[2] = code;
aux.assign(d,3);
data += aux;
}
postFrame(IAXFrame::IAX,IAXControl::Hangup,data.data(),data.length(),0,true);
IAXIEList* ies = new IAXIEList;
if (!TelEngine::null(cause))
ies->appendString(IAXInfoElement::CAUSE,cause);
if (code)
ies->appendNumeric(IAXInfoElement::CAUSECODE,code,1);
postFrameIes(IAXFrame::IAX,IAXControl::Hangup,ies,0,true);
changeState(Terminating);
m_localReqEnd = true;
Debug(m_engine,DebugAll,"Transaction(%u,%u). Hangup call. Cause: '%s'",localCallNo(),remoteCallNo(),cause);
@ -525,10 +508,6 @@ bool IAXTransaction::sendHangup(const char* cause, u_int8_t code)
bool IAXTransaction::sendReject(const char* cause, u_int8_t code)
{
String s(cause);
unsigned char d[3];
DataBlock data,aux;
Lock lock(this);
if (state() == Terminated || state() == Terminating)
return false;
@ -545,20 +524,12 @@ bool IAXTransaction::sendReject(const char* cause, u_int8_t code)
default:
return false;
}
if (cause) {
d[0] = IAXInfoElement::CAUSE;
d[1] = s.length();
data.assign(d,2);
data.append(s);
}
if (code) {
d[0] = IAXInfoElement::CAUSECODE;
d[1] = 1;
d[2] = code;
aux.assign(d,3);
data += aux;
}
postFrame(IAXFrame::IAX,frametype,data.data(),data.length(),0,true);
IAXIEList* ies = new IAXIEList;
if (!TelEngine::null(cause))
ies->appendString(IAXInfoElement::CAUSE,cause);
if (code)
ies->appendNumeric(IAXInfoElement::CAUSECODE,code,1);
postFrameIes(IAXFrame::IAX,frametype,ies,0,true);
Debug(m_engine,DebugAll,"Transaction(%u,%u). Reject. Cause: '%s'",localCallNo(),remoteCallNo(),cause);
changeState(Terminating);
m_localReqEnd = true;
@ -579,22 +550,24 @@ bool IAXTransaction::sendAuth()
default:
return false;
}
IAXIEList ieList;
ieList.appendString(IAXInfoElement::USERNAME,m_username);
ieList.appendNumeric(IAXInfoElement::AUTHMETHODS,m_authmethod,2);
ieList.appendString(IAXInfoElement::CHALLENGE,m_challenge);
DataBlock data;
ieList.toBuffer(data);
IAXControl::Type t = IAXControl::Unsupport;
switch (type()) {
case New:
postFrame(IAXFrame::IAX,IAXControl::AuthReq,data.data(),data.length(),0,false);
t = IAXControl::AuthReq;
break;
case RegReq:
case RegRel:
postFrame(IAXFrame::IAX,IAXControl::RegAuth,data.data(),data.length(),0,false);
t = IAXControl::RegAuth;
break;
default: ;
}
if (t != IAXControl::Unsupport) {
IAXIEList* ies = new IAXIEList;
ies->appendString(IAXInfoElement::USERNAME,m_username);
ies->appendNumeric(IAXInfoElement::AUTHMETHODS,m_authmethod,2);
ies->appendString(IAXInfoElement::CHALLENGE,m_challenge);
postFrameIes(IAXFrame::IAX,t,ies);
}
changeState(NewRemoteInvite_AuthSent);
return true;
}
@ -605,7 +578,7 @@ bool IAXTransaction::sendAuthReply(const String& response)
if (state() != NewLocalInvite_AuthRecv)
return false;
m_authdata = response;
IAXIEList ieList;
IAXIEList* ies = new IAXIEList;
IAXControl::Type subclass;
switch (type()) {
case New:
@ -613,21 +586,22 @@ bool IAXTransaction::sendAuthReply(const String& response)
break;
case RegReq:
subclass = IAXControl::RegReq;
ieList.appendString(IAXInfoElement::USERNAME,m_username);
ies->appendString(IAXInfoElement::USERNAME,m_username);
break;
case RegRel:
subclass = IAXControl::RegRel;
ieList.appendString(IAXInfoElement::USERNAME,m_username);
ies->appendString(IAXInfoElement::USERNAME,m_username);
break;
default:
delete ies;
return false;
}
if (m_authmethod != IAXAuthMethod::MD5)
if (m_authmethod != IAXAuthMethod::MD5) {
delete ies;
return false;
ieList.appendString(IAXInfoElement::MD5_RESULT,response);
DataBlock data;
ieList.toBuffer(data);
postFrame(IAXFrame::IAX,subclass,data.data(),data.length(),0,false);
}
ies->appendString(IAXInfoElement::MD5_RESULT,response);
postFrameIes(IAXFrame::IAX,subclass,ies);
changeState(NewLocalInvite_RepSent);
return true;
}
@ -681,6 +655,41 @@ bool IAXTransaction::enableTrunking(IAXMetaTrunkFrame* trunkFrame)
return true;
}
// Process a received call token
void IAXTransaction::processCallToken(const DataBlock& callToken)
{
Lock lock(this);
IAXFrameOut* frame = 0;
if (state() == NewLocalInvite && m_callToken) {
ObjList* o = m_outFrames.skipNull();
frame = o ? static_cast<IAXFrameOut*>(o->get()) : 0;
if (frame && frame->type() != IAXFrame::IAX && frame->subclass() != IAXControl::New)
frame = 0;
}
m_callToken = false;
if (!frame) {
Debug(m_engine,DebugNote,
"Transaction(%u,%u). Received call token in invalid state [%p]",
localCallNo(),remoteCallNo(),this);
return;
}
frame->updateIEList(false);
IAXIEList* ies = frame->ieList();
if (!ies) {
Debug(m_engine,DebugNote,
"Transaction(%u,%u). No IE list in first frame [%p]",
localCallNo(),remoteCallNo(),this);
return;
}
IAXInfoElementBinary* ct = static_cast<IAXInfoElementBinary*>(ies->getIE(IAXInfoElement::CALLTOKEN));
if (ct)
ct->setData(callToken.data(),callToken.length());
else
ies->appendBinary(IAXInfoElement::CALLTOKEN,(unsigned char*)callToken.data(),callToken.length());
frame->updateBuffer(m_engine->maxFullFrameDataLen());
sendFrame(frame);
}
void IAXTransaction::print()
{
static SocketAddr addr;
@ -712,8 +721,10 @@ void IAXTransaction::init(IAXIEList& ieList)
ieList.getString(IAXInfoElement::CALLED_CONTEXT,m_calledContext);
ieList.getNumeric(IAXInfoElement::FORMAT,m_format);
ieList.getNumeric(IAXInfoElement::CAPABILITY,m_capability);
if (outgoing())
if (outgoing()) {
m_formatOut = m_format;
m_callToken = (0 != ieList.getIE(IAXInfoElement::CALLTOKEN));
}
else
m_formatIn = m_format;
break;
@ -788,7 +799,7 @@ bool IAXTransaction::changeState(State newState)
return true;
}
IAXEvent* IAXTransaction::terminate(u_int8_t evType, bool local, const IAXFullFrame* frame, bool createIEList)
IAXEvent* IAXTransaction::terminate(u_int8_t evType, bool local, IAXFullFrame* frame, bool createIEList)
{
IAXEvent* ev;
if (createIEList)
@ -805,7 +816,7 @@ IAXEvent* IAXTransaction::terminate(u_int8_t evType, bool local, const IAXFullFr
return ev;
}
IAXEvent* IAXTransaction::waitForTerminate(u_int8_t evType, bool local, const IAXFullFrame* frame)
IAXEvent* IAXTransaction::waitForTerminate(u_int8_t evType, bool local, IAXFullFrame* frame)
{
IAXEvent* ev = new IAXEvent((IAXEvent::Type)evType,local,true,this,frame);
Debug(m_engine,DebugAll,"Transaction(%u,%u). Terminating. Event: %u, Frame(%u,%u)",
@ -820,23 +831,24 @@ void IAXTransaction::postFrame(IAXFrame::Type type, u_int32_t subclass, void* da
Lock lock(this);
if (state() == Terminated)
return;
if (!tStamp) {
tStamp = (u_int32_t)timeStamp();
if (m_lastFullFrameOut) {
// adjust timestamp to be different from the last sent
int32_t delta = tStamp - m_lastFullFrameOut;
if (delta <= 0)
tStamp = m_lastFullFrameOut + 1;
}
m_lastFullFrameOut = tStamp;
}
adjustTStamp(tStamp);
IAXFrameOut* frame = new IAXFrameOut(type,subclass,m_lCallNo,m_rCallNo,m_oSeqNo,m_iSeqNo,tStamp,
(unsigned char*)data,len,m_retransCount,m_retransInterval,ackOnly);
DDebug(m_engine,DebugAll,"Transaction(%u,%u) posting Frame(%u,%u) oseq=%u iseq=%u stamp=%u [%p]",
localCallNo(),remoteCallNo(),type,subclass,m_oSeqNo,m_iSeqNo,tStamp,this);
incrementSeqNo(frame,false);
m_outFrames.append(frame);
sendFrame(frame);
postFrame(frame);
}
// Constructs an IAXFrameOut frame, send it to remote peer and put it in the transmission list
void IAXTransaction::postFrameIes(IAXFrame::Type type, u_int32_t subclass, IAXIEList* ies,
u_int32_t tStamp, bool ackOnly)
{
Lock lock(this);
if (state() == Terminated)
return;
adjustTStamp(tStamp);
IAXFrameOut* frame = new IAXFrameOut(type,subclass,m_lCallNo,m_rCallNo,m_oSeqNo,
m_iSeqNo,tStamp,ies,m_engine->maxFullFrameDataLen(),m_retransCount,
m_retransInterval,ackOnly);
postFrame(frame);
}
bool IAXTransaction::sendFrame(IAXFrameOut* frame, bool vnak)
@ -854,7 +866,7 @@ bool IAXTransaction::sendFrame(IAXFrameOut* frame, bool vnak)
return b;
}
IAXEvent* IAXTransaction::createEvent(u_int8_t evType, bool local, const IAXFullFrame* frame, State newState)
IAXEvent* IAXTransaction::createEvent(u_int8_t evType, bool local, IAXFullFrame* frame, State newState)
{
IAXEvent* ev;
changeState(newState);
@ -1257,7 +1269,7 @@ bool IAXTransaction::sendConnected(IAXFullFrame::ControlType subclass, IAXFrame:
{
if (state() != Connected)
return false;
postFrame(frametype,subclass,0,0,0,true);
postFrameIes(frametype,subclass,0,0,true);
return true;
}
@ -1291,8 +1303,10 @@ void IAXTransaction::sendVNAK()
void IAXTransaction::sendUnsupport(u_int32_t subclass)
{
unsigned char d[3] = {IAXInfoElement::IAX_UNKNOWN,1,IAXFrame::packSubclass(subclass)};
postFrame(IAXFrame::IAX,IAXControl::Unsupport,d,sizeof(d),0,true);
IAXIEList* ies = new IAXIEList;
u_int8_t val = IAXFrame::packSubclass(subclass);
ies->appendNumeric(IAXInfoElement::IAX_UNKNOWN,val,1);
postFrameIes(IAXFrame::IAX,IAXControl::Unsupport,ies,0,true);
}
IAXEvent* IAXTransaction::processInternalOutgoingRequest(IAXFrameOut* frame, bool& delFrame)
@ -1328,7 +1342,7 @@ IAXEvent* IAXTransaction::processInternalIncomingRequest(const IAXFullFrame* fra
return 0;
}
IAXEvent* IAXTransaction::processMidCallControl(const IAXFullFrame* frame, bool& delFrame)
IAXEvent* IAXTransaction::processMidCallControl(IAXFullFrame* frame, bool& delFrame)
{
delFrame = true;
switch (frame->subclass()) {
@ -1358,7 +1372,7 @@ IAXEvent* IAXTransaction::processMidCallControl(const IAXFullFrame* frame, bool&
return 0;
}
IAXEvent* IAXTransaction::processMidCallIAXControl(const IAXFullFrame* frame, bool& delFrame)
IAXEvent* IAXTransaction::processMidCallIAXControl(IAXFullFrame* frame, bool& delFrame)
{
delFrame = true;
switch (frame->subclass()) {
@ -1408,7 +1422,7 @@ IAXEvent* IAXTransaction::processMidCallIAXControl(const IAXFullFrame* frame, bo
return 0;
}
IAXEvent* IAXTransaction::remoteRejectCall(const IAXFullFrame* frame, bool& delFrame)
IAXEvent* IAXTransaction::remoteRejectCall(IAXFullFrame* frame, bool& delFrame)
{
delFrame = true;
switch (type()) {
@ -1449,8 +1463,8 @@ IAXTransaction* IAXTransaction::processVoiceFrame(const IAXFullFrame* frame)
if (m_formatIn) {
if (frame->subclass() && frame->subclass() != m_formatIn) {
// Format changed.
if (m_engine->voiceFormatChanged(this,frame->fullFrame()->subclass()))
m_formatIn = frame->fullFrame()->subclass();
if (m_engine->voiceFormatChanged(this,frame->subclass()))
m_formatIn = frame->subclass();
else {
DDebug(m_engine,DebugAll,"IAXTransaction(%u,%u). Process Voice Frame. Media format (%u) change rejected!",
localCallNo(),remoteCallNo(),m_format);
@ -1511,4 +1525,31 @@ void IAXTransaction::eventTerminated(IAXEvent* event)
}
}
void IAXTransaction::adjustTStamp(u_int32_t& tStamp)
{
if (tStamp)
return;
tStamp = (u_int32_t)timeStamp();
if (m_lastFullFrameOut) {
// adjust timestamp to be different from the last sent
int32_t delta = tStamp - m_lastFullFrameOut;
if (delta <= 0)
tStamp = m_lastFullFrameOut + 1;
}
m_lastFullFrameOut = tStamp;
}
void IAXTransaction::postFrame(IAXFrameOut* frame)
{
if (!frame)
return;
DDebug(m_engine,DebugAll,
"Transaction(%u,%u) posting Frame(%u,%u) oseq=%u iseq=%u stamp=%u [%p]",
localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),
m_oSeqNo,m_iSeqNo,frame->timeStamp(),this);
incrementSeqNo(frame,false);
m_outFrames.append(frame);
sendFrame(frame);
}
/* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -123,6 +123,7 @@ public:
RR_DELAY = 0x31, // W
RR_DROPPED = 0x32, // DW
RR_OOO = 0x33, // DW
CALLTOKEN = 0X36, // BIN
};
/**
@ -307,6 +308,14 @@ public:
inline DataBlock& data()
{ return m_data; }
/**
* Set the data
* @param buf Source buffer to construct this IE
* @param len Buffer length
*/
inline void setData(void* buf, unsigned len)
{ m_data.assign(buf,len); }
/**
* Constructs a buffer containing this Information Element
* @param buf Destination buffer
@ -348,22 +357,19 @@ public:
/**
* Constructor
*/
inline IAXIEList() : m_invalidIEList(false)
{}
IAXIEList();
/**
* Constructor. Construct the list from an IAXFullFrame object
* @param frame Source object
* @param incoming True if it is an incoming frame
*/
inline IAXIEList(const IAXFullFrame* frame, bool incoming = true) : m_invalidIEList(false)
{ createFromFrame(frame,incoming); }
IAXIEList(const IAXFullFrame* frame, bool incoming = true);
/**
* Destructor
*/
inline ~IAXIEList()
{}
~IAXIEList();
/**
* Get the invalid IE list flag
@ -378,6 +384,13 @@ public:
inline void clear()
{ m_list.clear(); }
/**
* Check if the list is empty
* @return True if the list is empty
*/
inline bool empty()
{ return 0 == m_list.skipNull(); }
/**
* Insert a VERSION Information Element in the list if not already done
*/
@ -644,6 +657,7 @@ public:
Provision = 0x23,
FwDownl = 0x24,
FwData = 0x25,
CallToken = 0x28,
};
/**
@ -737,7 +751,7 @@ public:
* Get a pointer to this frame if it is a full frame
* @return A pointer to this frame if it is a full frame or 0
*/
virtual const IAXFullFrame* fullFrame() const;
virtual IAXFullFrame* fullFrame();
/**
* Parse a received buffer and returns a IAXFrame pointer if valid
@ -852,6 +866,22 @@ public:
u_int32_t tStamp,
const unsigned char* buf = 0, unsigned int len = 0);
/**
* Constructor. Constructs an outgoing full frame
* @param type Frame type
* @param subclass Frame subclass
* @param sCallNo Source (remote) call number
* @param dCallNo Destination (local) call number
* @param oSeqNo Outgoing sequence number
* @param iSeqNo Incoming (expected) sequence number
* @param tStamp Frame timestamp
* @param ieList List of frame IEs
* @param maxlen Max frame data length
*/
IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo,
u_int32_t tStamp, IAXIEList* ieList, u_int16_t maxlen);
/**
* Destructor
*/
@ -889,7 +919,34 @@ public:
* Get a pointer to this frame if it is a full frame
* @return A pointer to this frame
*/
virtual const IAXFullFrame* fullFrame() const;
virtual IAXFullFrame* fullFrame();
/**
* Rebuild frame buffer from the list of IEs
* @param maxlen Max frame data length
*/
void updateBuffer(u_int16_t maxlen);
/**
* Retrieve the IE list
* @return IAXIEList pointer or NULL
*/
inline IAXIEList* ieList()
{ return m_ieList; }
/**
* Update IE list from buffer if not already done
* @param incoming True if this is an incoming frame
* @return True if the list is valid
*/
bool updateIEList(bool incoming);
/**
* Remove the IE list
* @param delObj True to delete it
* @return IAXIEList pointer or NULL if requested to delete it or already NULL
*/
IAXIEList* removeIEList(bool delObj = true);
/**
* Fill a string with this frame
@ -899,7 +956,7 @@ public:
* @param incoming True if it is an incoming frame
*/
void toString(String& dest, const SocketAddr& local, const SocketAddr& remote,
bool incoming) const;
bool incoming);
/**
* Get the string associated with the given IAX control type
@ -909,12 +966,21 @@ public:
static inline const char* controlTypeText(int type)
{ return lookup(type,s_controlTypes,0); }
protected:
/**
* Destroyed notification. Clear data
*/
virtual void destroyed();
private:
// Build frame buffer header
void setDataHeader();
static TokenDict s_controlTypes[]; // Keep the association between control types and their names
u_int16_t m_dCallNo; // Destination call number
unsigned char m_oSeqNo; // Out sequence number
unsigned char m_iSeqNo; // In sequence number
u_int32_t m_subclass; // Subclass
IAXIEList* m_ieList; // List of IEs
};
/**
@ -947,6 +1013,30 @@ public:
m_nextTransTime(Time::msecNow() + m_retransTimeInterval)
{}
/**
* Constructor. Constructs an outgoing full frame
* @param type Frame type
* @param subclass Frame subclass
* @param sCallNo Source (remote) call number
* @param dCallNo Destination (local) call number
* @param oSeqNo Outgoing sequence number
* @param iSeqNo Incoming (expected) sequence number
* @param tStamp Frame timestamp
* @param ieList List of frame IEs
* @param maxlen Max frame data length
* @param retransCount Retransmission counter
* @param retransInterval Time interval to the next retransmission
* @param ackOnly Acknoledge only flag. If true, the frame only expects an ACK
*/
inline IAXFrameOut(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo,
unsigned char oSeqNo, unsigned char iSeqNo, u_int32_t tStamp,
IAXIEList* ieList, u_int16_t maxlen,
u_int16_t retransCount, u_int32_t retransInterval, bool ackOnly)
: IAXFullFrame(type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,ieList,maxlen),
m_ack(false), m_ackOnly(ackOnly), m_retransCount(retransCount), m_retransTimeInterval(retransInterval),
m_nextTransTime(Time::msecNow() + m_retransTimeInterval)
{}
/**
* Destructor
*/
@ -1448,6 +1538,13 @@ public:
*/
bool enableTrunking(IAXMetaTrunkFrame* trunkFrame);
/**
* Process a received call token
* This method is thread safe
* @param callToken Received call token
*/
void processCallToken(const DataBlock& callToken);
/**
* Print transaction data on stdin
*/
@ -1534,7 +1631,7 @@ protected:
* @param createIEList If true create IE list in the generated event
* @return Pointer to a valid IAXEvent
*/
IAXEvent* terminate(u_int8_t evType, bool local, const IAXFullFrame* frame = 0, bool createIEList = true);
IAXEvent* terminate(u_int8_t evType, bool local, IAXFullFrame* frame = 0, bool createIEList = true);
/**
* Wait for ACK to terminate the transaction. No more events will be generated
@ -1543,7 +1640,7 @@ protected:
* @param frame Frame to build event from
* @return Pointer to a valid IAXEvent
*/
IAXEvent* waitForTerminate(u_int8_t evType, bool local, const IAXFullFrame* frame);
IAXEvent* waitForTerminate(u_int8_t evType, bool local, IAXFullFrame* frame);
/**
* Constructs an IAXFrameOut frame, send it to remote peer and put it in the transmission list
@ -1558,6 +1655,18 @@ protected:
void postFrame(IAXFrame::Type type, u_int32_t subclass, void* data = 0, u_int16_t len = 0, u_int32_t tStamp = 0,
bool ackOnly = false);
/**
* Constructs an IAXFrameOut frame, send it to remote peer and put it in the transmission list
* This method is thread safe
* @param type Frame type
* @param subclass Frame subclass
* @param ies Frame IE list
* @param tStamp Frame timestamp. If 0 the transaction timestamp will be used
* @param ackOnly Frame's acknoledge only flag
*/
void postFrameIes(IAXFrame::Type type, u_int32_t subclass, IAXIEList* ies, u_int32_t tStamp = 0,
bool ackOnly = false);
/**
* Send a full frame to remote peer
* @param frame Frame to send
@ -1574,7 +1683,7 @@ protected:
* @param newState The transaction new state
* @return Pointer to an IAXEvent or 0 (invalid IE list)
*/
IAXEvent* createEvent(u_int8_t evType, bool local, const IAXFullFrame* frame, State newState);
IAXEvent* createEvent(u_int8_t evType, bool local, IAXFullFrame* frame, State newState);
/**
* Create an event from a received frame that is a response to a sent frame and
@ -1748,7 +1857,7 @@ protected:
* @param delFrame Delete frame flag. If true on exit, a request was found
* @return A valid IAXEvent or 0
*/
IAXEvent* processMidCallControl(const IAXFullFrame* frame, bool& delFrame);
IAXEvent* processMidCallControl(IAXFullFrame* frame, bool& delFrame);
/**
* Process mid call IAX control frames
@ -1756,7 +1865,7 @@ protected:
* @param delFrame Delete frame flag. If true on exit, a request was found
* @return A valid IAXEvent or 0
*/
IAXEvent* processMidCallIAXControl(const IAXFullFrame* frame, bool& delFrame);
IAXEvent* processMidCallIAXControl(IAXFullFrame* frame, bool& delFrame);
/**
* Test if frame is a Reject/RegRej frame
@ -1764,7 +1873,7 @@ protected:
* @param delFrame Delete frame flag. If true on exit, a request was found
* @return A valid IAXEvent or 0.
*/
IAXEvent* remoteRejectCall(const IAXFullFrame* frame, bool& delFrame);
IAXEvent* remoteRejectCall(IAXFullFrame* frame, bool& delFrame);
/**
* Terminate the transaction if state is Terminating on a remote request
@ -1818,6 +1927,9 @@ protected:
}
private:
void adjustTStamp(u_int32_t& tStamp);
void postFrame(IAXFrameOut* frame);
// Params
bool m_localInitTrans; // True: local initiated transaction
bool m_localReqEnd; // Local client requested terminate
@ -1867,6 +1979,7 @@ private:
u_int32_t m_formatIn; // Incoming media format
u_int32_t m_formatOut; // Outgoing media format
u_int32_t m_capability; // Media capability of this transaction
bool m_callToken; // Call token supported/expected
// Meta trunking
IAXMetaTrunkFrame* m_trunkFrame; // Reference to a trunk frame if trunking is enabled for this transaction
};
@ -1979,7 +2092,7 @@ public:
* @return IE list reference
*/
inline IAXIEList& getList()
{ return m_ieList; }
{ return *m_ieList; }
protected:
/**
@ -2001,7 +2114,7 @@ protected:
* @param transaction IAX transaction that generated the event
* @param frame The frame that generated the event
*/
IAXEvent(Type type, bool local, bool final, IAXTransaction* transaction, const IAXFullFrame* frame = 0);
IAXEvent(Type type, bool local, bool final, IAXTransaction* transaction, IAXFullFrame* frame = 0);
private:
inline IAXEvent() {} // Default constructor
@ -2012,7 +2125,7 @@ private:
bool m_local; // If true the event is generated locally, the receiver MUST not respond
bool m_final; // Final event flag
IAXTransaction* m_transaction; // Transaction that generated this event
IAXIEList m_ieList; // IAXInfoElement list
IAXIEList* m_ieList; // IAXInfoElement list
};
/**
@ -2138,6 +2251,12 @@ public:
inline u_int32_t capability() const
{ return m_capability; }
/**
* (Re)Initialize the engine
* @param params Parameter list
*/
void initialize(const NamedList& params);
/**
* Read data from socket
* @param addr Socket to read from
@ -2149,11 +2268,20 @@ public:
* @param buf Data to write
* @param len Data length
* @param addr Socket to write to
* @param frame Optional frame to be printed if debug is DebugAll
* @param frame Optional frame to be printed
* @return True on success
*/
bool writeSocket(const void* buf, int len, const SocketAddr& addr, IAXFullFrame* frame = 0);
/**
* Write a full frame to socket
* @param addr Socket to write to
* @param frame Frame to write
* @return True on success
*/
inline bool writeSocket(const SocketAddr& addr, IAXFullFrame* frame)
{ return !frame || writeSocket(frame->data().data(),frame->data().length(),addr,frame); }
/**
* Read events
*/
@ -2188,6 +2316,15 @@ public:
virtual bool voiceFormatChanged(IAXTransaction* trans, u_int32_t format)
{ return false; }
/**
* Check call token on incoming call requests.
* This method is called by the engine when processing an incoming call request
* @param addr The address from where the call request was received
* @param frame Received frame
* @return True if accepted, false to ignore the call
*/
virtual bool checkCallToken(const SocketAddr& addr, IAXFullFrame& frame);
/**
* Process the initial received format and capability. If accepted on exit will set the transaction format and capability
* @param trans Transaction that received the new format
@ -2241,6 +2378,25 @@ public:
*/
static bool isMD5ChallengeCorrect(const String& md5data, const String& challenge, const String& password);
/**
* Build a time signed secret used to authenticate an IP address
* @param buf Destination buffer
* @param secret Extra secret to add to MD5 sum
* @param addr Socket address
*/
static void buildAddrSecret(String& buf, const String& secret,
const SocketAddr& addr);
/**
* Decode a secret built using buildAddrSecret()
* @param buf Input buffer
* @param secret Extra secret to check
* @param addr Socket address
* @return Secret age, negative if invalid
*/
static int addrSecretAge(const String& buf, const String& secret,
const SocketAddr& addr);
protected:
/**
* Process all trunk meta frames in the queue
@ -2303,6 +2459,12 @@ private:
u_int16_t m_authTimeout; // Timeout (in seconds) of acknoledged auth frames sent
u_int32_t m_transTimeout; // Timeout (in seconds) on remote request of transactions
// belonging to this engine
bool m_callToken; // Call token required on incoming calls
String m_callTokenSecret; // Secret used to generate call tokens
int m_callTokenAge; // Max allowed call token age
bool m_showCallTokenFailures; // Print incoming call token failures to output
bool m_rejectMissingCallToken; // Reject/ignore incoming calls without call token if mandatory
bool m_printMsg; // Print frame to output
// Media
u_int32_t m_format; // The default media format
u_int32_t m_capability; // The media capability

View File

@ -540,6 +540,7 @@ private:
static Configuration s_cfg; // Configuration file
static YIAXLineContainer s_lines; // Lines
static Thread::Priority s_priority = Thread::Normal; // Threads priority
static bool s_callTokenOut = true; // Send an empty call token on outgoing calls
static YIAXDriver iplugin; // Init the driver
static String s_statusCmd = "status";
@ -1031,6 +1032,8 @@ IAXTransaction* YIAXEngine::call(SocketAddr& addr, NamedList& params)
}
ieList.appendNumeric(IAXInfoElement::FORMAT,format,4);
ieList.appendNumeric(IAXInfoElement::CAPABILITY,codecs,4);
if (params.getBoolValue("calltoken_out",s_callTokenOut))
ieList.appendBinary(IAXInfoElement::CALLTOKEN,0,0);
return startLocalTransaction(IAXTransaction::New,addr,ieList);
}
@ -1206,6 +1209,11 @@ void YIAXDriver::initialize()
// Load configuration
s_cfg = Engine::configFile("yiaxchan");
s_cfg.load();
NamedList* gen = s_cfg.getSection("general");
NamedList dummy("general");
if (!gen)
gen = &dummy;
s_callTokenOut = gen->getBoolValue("calltoken_out",true);
// Codec capabilities
m_defaultCodec = 0;
m_codecs = 0;
@ -1231,8 +1239,10 @@ void YIAXDriver::initialize()
m_defaultCodec = fallback;
unlock();
// Setup driver if this is the first call
if (m_iaxEngine)
if (m_iaxEngine) {
m_iaxEngine->initialize(*gen);
return;
}
setup();
installRelay(Halt);
installRelay(Route);
@ -1251,7 +1261,7 @@ void YIAXDriver::initialize()
String iface = s_cfg.getValue("general","addr");
bool authReq = s_cfg.getBoolValue("registrar","auth_required",true);
m_iaxEngine = new YIAXEngine(iface,m_port,transListCount,retransCount,retransInterval,authTimeout,
transTimeout,maxFullFrameDataLen,trunkSendInterval,authReq,s_cfg.getSection("general"));
transTimeout,maxFullFrameDataLen,trunkSendInterval,authReq,gen);
m_iaxEngine->debugChain(this);
int tos = s_cfg.getIntValue("general","tos",dict_tos,0);
if (tos && !m_iaxEngine->socket().setTOS(tos))