From 8503442db4075cfbc9c2de65ec22576b251e87a5 Mon Sep 17 00:00:00 2001 From: marian Date: Mon, 11 Jul 2011 12:46:14 +0000 Subject: [PATCH] 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 --- conf.d/yiaxchan.conf.sample | 24 ++++ libs/yiax/engine.cpp | 191 +++++++++++++++++++++++--- libs/yiax/frame.cpp | 181 +++++++++++++++++++------ libs/yiax/transaction.cpp | 261 +++++++++++++++++++++--------------- libs/yiax/yateiax.h | 200 ++++++++++++++++++++++++--- modules/yiaxchan.cpp | 14 +- 6 files changed, 677 insertions(+), 194 deletions(-) diff --git a/conf.d/yiaxchan.conf.sample b/conf.d/yiaxchan.conf.sample index 2aea3f92..6ef3da48 100644 --- a/conf.d/yiaxchan.conf.sample +++ b/conf.d/yiaxchan.conf.sample @@ -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 diff --git a/libs/yiax/engine.cpp b/libs/yiax/engine.cpp index 098ab153..192e030c 100644 --- a/libs/yiax/engine.cpp +++ b/libs/yiax/engine.cpp @@ -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(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 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 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 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(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: */ diff --git a/libs/yiax/frame.cpp b/libs/yiax/frame.cpp index 96c6f287..9eeadda8 100644 --- a/libs/yiax/frame.cpp +++ b/libs/yiax/frame.cpp @@ -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 */ diff --git a/libs/yiax/transaction.cpp b/libs/yiax/transaction.cpp index b0ffaf64..8da8961e 100644 --- a/libs/yiax/transaction.cpp +++ b/libs/yiax/transaction.cpp @@ -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(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(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: */ diff --git a/libs/yiax/yateiax.h b/libs/yiax/yateiax.h index 8c473934..d0febcce 100644 --- a/libs/yiax/yateiax.h +++ b/libs/yiax/yateiax.h @@ -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 diff --git a/modules/yiaxchan.cpp b/modules/yiaxchan.cpp index 74f726aa..8cb41aca 100644 --- a/modules/yiaxchan.cpp +++ b/modules/yiaxchan.cpp @@ -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))