Commited changes by Marian Podgoreanu.

git-svn-id: http://yate.null.ro/svn/yate/trunk@920 acf43c95-373e-0410-b603-e72c3f656dc1
This commit is contained in:
paulc 2006-07-02 20:11:46 +00:00
parent 3ab82e9ea2
commit 9dc980c5a9
5 changed files with 1626 additions and 829 deletions

View File

@ -31,7 +31,8 @@
using namespace TelEngine;
IAXEngine::IAXEngine(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_int16_t authTimeout, u_int16_t transTimeout, u_int16_t maxFullFrameDataLen,
u_int32_t format, u_int32_t capab, u_int32_t trunkSendInterval)
: Mutex(true),
m_lastGetEvIndex(0),
m_maxFullFrameDataLen(maxFullFrameDataLen),
@ -42,7 +43,11 @@ IAXEngine::IAXEngine(int port, u_int16_t transListCount, u_int16_t retransCount,
m_authTimeout(authTimeout),
m_transTimeout(transTimeout),
m_format(format),
m_capability(capab)
m_capability(capab),
m_mutexTrunk(true),
m_trunkSendInterval(trunkSendInterval),
m_writeCommands(0),
m_writeCommandsFail(0)
{
debugName("iaxengine");
if ((port <= 0) || port > 65535)
@ -72,6 +77,10 @@ IAXEngine::~IAXEngine()
for (int i = 0; i < m_transListCount; i++)
delete m_transList[i];
delete[] m_transList;
print();
}
IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame)
@ -91,7 +100,7 @@ IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame)
continue;
// Complete transaction
if (tr->processFrame(frame)) {
tr->m_rCallNo = frame->sourceCallNo();
tr->m_rCallNo = frame->sourceCallNo();
m_incompleteTransList.remove(tr,false);
m_transList[frame->sourceCallNo() % m_transListCount]->append(tr);
XDebug(this,DebugAll,"New incomplete outgoing transaction completed (%u,%u)",
@ -109,20 +118,28 @@ IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame)
continue;
// Mini frame
if (!frame->fullFrame()) {
if (addr == tr->remoteAddr())
return tr->processFrame(frame);
if (addr == tr->remoteAddr()) {
// keep transaction referenced but unlock the engine
RefPointer<IAXTransaction> t = tr;
lock.drop();
return t ? t->processFrame(frame) : 0;
}
continue;
}
// Full frame
// Has a local number assigned? If not, test socket
if ((frame->fullFrame())->destCallNo() || addr == tr->remoteAddr())
return tr->processFrame(frame);
if ((frame->fullFrame())->destCallNo() || addr == tr->remoteAddr()) {
// keep transaction referenced but unlock the engine
RefPointer<IAXTransaction> t = tr;
lock.drop();
return t ? t->processFrame(frame) : 0;
}
}
// 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)
return 0;
switch (frame->subclass()) {
switch (frame->fullFrame()->subclass()) {
case IAXControl::New:
case IAXControl::RegReq:
case IAXControl::RegRel:
@ -133,8 +150,14 @@ IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame)
return 0;
case IAXControl::FwDownl:
default:
DDebug(this,DebugAll,"Unsupported incoming transaction Frame(%u,%u)",
frame->type(),frame->subclass());
if (frame->fullFrame()) {
if (frame->fullFrame()->destCallNo())
XDebug(this,DebugAll,"Unmatched Frame(%u,%u) for (%u,%u)",
frame->type(),frame->subclass(),frame->fullFrame()->destCallNo(),frame->fullFrame()->sourceCallNo());
else
DDebug(this,DebugAll,"Unsupported incoming transaction Frame(%u,%u). Source call no: %u",
frame->type(),frame->fullFrame()->subclass(),frame->fullFrame()->sourceCallNo());
}
return 0;
}
// Generate local number
@ -200,10 +223,15 @@ void IAXEngine::readSocket(SocketAddr& addr)
bool IAXEngine::writeSocket(const void* buf, int len, const SocketAddr& addr)
{
len = m_socket.sendTo(buf,len,addr);
m_writeCommands++;
if (len == Socket::socketError()) {
m_writeCommandsFail++;
if (!m_socket.canRetry())
Debug(this,DebugWarn,"Socket write error: %s (%d)",
::strerror(m_socket.error()),m_socket.error());
else
DDebug(this,DebugMild,"Socket temporary unavailable: %s (%d)",
::strerror(m_socket.error()),m_socket.error());
return false;
}
return true;
@ -260,6 +288,25 @@ void IAXEngine::keepAlive(SocketAddr& addr)
writeSocket(buf,sizeof(buf),addr);
}
bool IAXEngine::processTrunkFrames(u_int32_t time)
{
Lock lock(&m_mutexTrunk);
bool sent = false;
for (ObjList* l = m_trunkList.skipNull(); l; l = l->next()) {
IAXMetaTrunkFrame* frame = static_cast<IAXMetaTrunkFrame*>(l->get());
// Frame has mini frame(s) ?
if (!frame->timestamp())
continue;
int32_t interval = time - frame->timestamp();
if (!interval || (interval && (u_int32_t)interval < m_trunkSendInterval))
continue;
// If the time wrapped around, send it. Worst case: we'll send an empty frame
frame->send(time);
sent = true;
}
return sent;
}
void IAXEngine::processEvent(IAXEvent* event)
{
XDebug(this,DebugAll,"Default processing - deleting event %p Subclass %u",
@ -273,26 +320,40 @@ IAXEvent* IAXEngine::getEvent(u_int64_t time)
IAXEvent* ev;
ObjList* l;
Lock lock(this);
lock();
// Find for incomplete transactions
l = m_incompleteTransList.skipNull();
for (; l; l = l->next()) {
tr = static_cast<IAXTransaction*>(l->get());
if (tr && 0 != (ev = tr->getEvent(time)))
if (tr && 0 != (ev = tr->getEvent(time))) {
unlock();
return ev;
}
continue;
}
// Find for complete transactions
for (; m_lastGetEvIndex < m_transListCount; m_lastGetEvIndex++) {
l = m_transList[m_lastGetEvIndex]->skipNull();
for (; l; l = l->next()) {
tr = static_cast<IAXTransaction*>(l->get());
if (tr && 0 != (ev = tr->getEvent(time)))
return ev;
// Find for complete transactions, start with current index
while (m_lastGetEvIndex < m_transListCount) {
l = m_transList[m_lastGetEvIndex++]->skipNull();
if (!l)
continue;
ListIterator iter(*l);
for (;;) {
tr = static_cast<IAXTransaction*>(iter.get());
// end of iteration?
if (!tr)
break;
RefPointer<IAXTransaction> t = tr;
// dead pointer?
if (!t)
continue;
unlock();
if (0 != (ev = t->getEvent(time)))
return ev;
lock();
}
}
m_lastGetEvIndex = 0;
unlock();
return 0;
}
@ -323,15 +384,18 @@ void IAXEngine::releaseCallNo(u_int16_t lcallno)
m_lUsedCallNo[lcallno] = false;
}
IAXTransaction* IAXEngine::startLocalTransaction(IAXTransaction::Type type, const SocketAddr& addr, IAXIEList& ieList)
IAXTransaction* IAXEngine::startLocalTransaction(IAXTransaction::Type type, const SocketAddr& addr, IAXIEList& ieList, bool trunking)
{
Lock lock(this);
u_int16_t lcn = generateCallNo();
if (!lcn)
return 0;
IAXTransaction* tr = IAXTransaction::factoryOut(this,type,lcn,addr,ieList);
if (tr)
if (tr) {
m_incompleteTransList.append(tr);
if (trunking)
enableTrunking(tr);
}
else
releaseCallNo(lcn);
return tr;
@ -386,6 +450,47 @@ void IAXEngine::defaultEventHandler(IAXEvent* event)
}
}
void IAXEngine::enableTrunking(IAXTransaction* trans)
{
Lock lock(&m_mutexTrunk);
IAXMetaTrunkFrame* frame;
// Already enabled ?
for (ObjList* l = m_trunkList.skipNull(); l; l = l->next()) {
frame = static_cast<IAXMetaTrunkFrame*>(l->get());
if (frame && frame->addr() == trans->remoteAddr()) {
trans->enableTrunking(frame);
return;
}
}
frame = new IAXMetaTrunkFrame(this,trans->remoteAddr());
if (trans->enableTrunking(frame))
m_trunkList.append(frame);
// Deref frame: Only transactions are allowed to keep references for it
frame->deref();
}
void IAXEngine::removeTrunkFrame(IAXMetaTrunkFrame* trunkFrame)
{
Lock lock(&m_mutexTrunk);
m_trunkList.remove(trunkFrame,false);
}
void IAXEngine::runProcessTrunkFrames()
{
while (1) {
processTrunkFrames();
Thread::msleep(2,true);
}
}
void IAXEngine::print()
{
Debug(this,DebugInfo,"IAXEngine - START PRINT [%p]",this);
Output("Write commands: " FMT64U,m_writeCommands);
Output("Write commands failed: " FMT64U,m_writeCommandsFail);
Debug(this,DebugInfo,"IAXEngine - END PRINT [%p]",this);
}
void IAXEngine::getMD5FromChallenge(String& md5data, const String& challenge, const String& password)
{
MD5 md5;
@ -425,9 +530,10 @@ IAXEvent::IAXEvent(Type type, bool local, bool final, IAXTransaction* transactio
IAXEvent::~IAXEvent()
{
if (m_final && m_transaction && m_transaction->state() == IAXTransaction::Terminated) {
m_transaction->getEngine()->removeTransaction(m_transaction);
}
// Moved to transaction destructor
// if (m_final && m_transaction && m_transaction->state() == IAXTransaction::Terminated) {
// m_transaction->getEngine()->removeTransaction(m_transaction);
// }
if (m_transaction) {
m_transaction->eventTerminated(this);
m_transaction->deref();

View File

@ -24,6 +24,8 @@
#include <yateiax.h>
#include <string.h> // For memcpy()
using namespace TelEngine;
/**
@ -421,13 +423,13 @@ const char* IAXFormat::videoText(u_int8_t video)
return lookup(video,videoData);
}
/**
* IAXFrame
*/
/*
* IAXFrame
*/
IAXFrame::IAXFrame(Type type, u_int16_t sCallNo, u_int32_t tStamp, bool retrans,
const unsigned char* buf, unsigned int len)
: m_type(type), m_data((char*)buf,len,true), m_retrans(retrans),
m_sCallNo(sCallNo), m_tStamp(tStamp), m_subclass(0)
m_sCallNo(sCallNo), m_tStamp(tStamp)
{
XDebug(DebugAll,"IAXFrame::IAXFrame(%u) [%p]",type,this);
}
@ -540,7 +542,7 @@ u_int8_t IAXFrame::packSubclass(u_int32_t value)
DDebug(DebugMild,"IAXFrame nonstandard pack %u",value);
return value;
}
// no need to start from zero, we already know it's >= 2^8
// No need to start from zero, we already know it's >= 2^8
u_int32_t v = 0x100;
for (u_int8_t i = 8; i < 32; i++) {
if (v == value)
@ -570,32 +572,30 @@ const IAXFullFrame* IAXFrame::fullFrame() const
/**
* IAXFullFrame
*/
IAXFullFrame::IAXFullFrame(Type type, u_int32_t subClass, u_int16_t sCallNo, u_int16_t dCallNo,
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, 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_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass)
{
DDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]",
XDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]",
type,subClass,this);
m_subclass = subClass;
}
IAXFullFrame::IAXFullFrame(Type type, u_int32_t subClass, u_int16_t sCallNo, u_int16_t dCallNo,
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,
const unsigned char* buf, unsigned int len)
: IAXFrame(type,sCallNo,tStamp,false,0,0),
m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo)
m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass)
{
DDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]",
XDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]",
type,subClass,this);
unsigned char header[12];
DataBlock ie;
m_subclass = subClass;
// Full frame flag + Source call number
header[0] = 0x80 | (unsigned char)(m_sCallNo >> 8);
header[1] = (unsigned char)(m_sCallNo);
@ -624,7 +624,7 @@ IAXFullFrame::IAXFullFrame(Type type, u_int32_t subClass, u_int16_t sCallNo, u_i
IAXFullFrame::~IAXFullFrame()
{
DDebug(DebugAll,"IAXFullFrame::~IAXFullFrame(%u,%u) [%p]",
XDebug(DebugAll,"IAXFullFrame::~IAXFullFrame(%u,%u) [%p]",
m_type,m_subclass,this);
}
@ -636,16 +636,20 @@ const IAXFullFrame* IAXFullFrame::fullFrame() const
/**
* IAXFrameOut
*/
void IAXFrameOut::setRetrans()
{
if (!m_retrans) {
m_retrans = true;
((unsigned char*)m_data.data())[2] |= 0x80;
}
}
void IAXFrameOut::transmitted()
{
if (m_retransCount) {
m_retransCount--;
m_retransTimeInterval *= 2;
m_nextTransTime += m_retransTimeInterval;
if (!m_retrans) {
m_retrans = true;
((unsigned char*)m_data.data())[2] |= 0x80;
}
}
}
@ -657,5 +661,74 @@ void IAXFrameOut::adjustAuthTimeout(u_int64_t nextTransTime)
m_nextTransTime = nextTransTime;
}
/**
* IAXFrameOut
*/
#define IAX2_METATRUNK_HEADERLENGTH 8
#define IAX2_MINIFRAME_HEADERLENGTH 6
IAXMetaTrunkFrame::IAXMetaTrunkFrame(IAXEngine* engine, const SocketAddr& addr)
: Mutex(true), m_data(0), m_dataAddIdx(IAX2_METATRUNK_HEADERLENGTH), m_engine(engine), m_addr(addr)
{
m_data = new u_int8_t[m_engine->maxFullFrameDataLen()];
// Meta indicator
*(u_int16_t*)m_data = 0;
// Meta command & Command data (use timestamps)
m_data[2] = 1;
m_data[3] = 1;
// Frame timestamp
setTimestamp(Time::msecNow());
}
IAXMetaTrunkFrame::~IAXMetaTrunkFrame()
{
m_engine->removeTrunkFrame(this);
delete[] m_data;
}
void IAXMetaTrunkFrame::setTimestamp(u_int32_t tStamp)
{
m_timestamp = tStamp;
m_data[4] = tStamp >> 24;
m_data[5] = tStamp >> 16;
m_data[6] = tStamp >> 8;
m_data[7] = tStamp;
}
bool IAXMetaTrunkFrame::add(u_int16_t sCallNo, const DataBlock& data, u_int32_t tStamp)
{
Lock lock(this);
bool b = true;
// Do we have data ?
if (!data.length())
return b;
// If no more room, send it
if (m_dataAddIdx + data.length() + IAX2_MINIFRAME_HEADERLENGTH > m_engine->maxFullFrameDataLen())
b = send(Time::msecNow());
// Is the first mini frame ?
if (m_dataAddIdx == IAX2_METATRUNK_HEADERLENGTH)
m_timestamp = Time::msecNow();
// Add the mini frame
m_data[m_dataAddIdx++] = data.length() >> 8;
m_data[m_dataAddIdx++] = data.length();
m_data[m_dataAddIdx++] = sCallNo >> 8;
m_data[m_dataAddIdx++] = sCallNo;
m_data[m_dataAddIdx++] = tStamp >> 8;
m_data[m_dataAddIdx++] = tStamp;
memcpy(m_data + m_dataAddIdx,data.data(),data.length());
m_dataAddIdx += data.length();
return b;
}
bool IAXMetaTrunkFrame::send(u_int32_t tStamp)
{
Lock lock(this);
setTimestamp(tStamp);
bool b = m_engine->writeSocket(m_data,m_dataAddIdx,m_addr);
// Reset index & timestamp
m_dataAddIdx = IAX2_METATRUNK_HEADERLENGTH;
m_timestamp = 0;
return b;
}
/* vi: set ts=8 sw=4 sts=4 noet: */

View File

@ -33,8 +33,8 @@ String IAXTransaction::s_iax_modInvalidAuth("Invalid authentication request, res
unsigned char IAXTransaction::m_maxInFrames = 100;
IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame,
u_int16_t lcallno, const SocketAddr& addr, void* data)
IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t lcallno,
const SocketAddr& addr, void* data)
: Mutex(true),
m_localInitTrans(false),
m_localReqEnd(false),
@ -52,6 +52,7 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame,
m_lastFullFrameOut(0),
m_lastMiniFrameOut(0xFFFF),
m_lastMiniFrameIn(0),
m_lastAck(0xFFFF),
m_mutexInMedia(true),
m_pendingEvent(0),
m_currentEvent(0),
@ -67,7 +68,8 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame,
m_format(0),
m_formatIn(0),
m_formatOut(0),
m_capability(0)
m_capability(0),
m_trunkFrame(0)
{
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) incoming [%p]",
localCallNo(),remoteCallNo(),this);
@ -88,7 +90,6 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame,
case IAXControl::Poke:
m_type = Poke;
break;
case IAXControl::FwDownl:
default:
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) incoming [%p]. Unsupported type: %u",
localCallNo(),remoteCallNo(),this,frame->subclass());
@ -98,7 +99,6 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame,
Lock lock(this);
m_inFrames.append(frame);
incrementSeqNo(frame,true);
sendAck(frame->fullFrame());
}
IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno, const SocketAddr& addr,
@ -120,6 +120,7 @@ IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno,
m_lastFullFrameOut(0),
m_lastMiniFrameOut(0xFFFF),
m_lastMiniFrameIn(0),
m_lastAck(0xFFFF),
m_mutexInMedia(true),
m_pendingEvent(0),
m_currentEvent(0),
@ -135,7 +136,8 @@ 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_trunkFrame(0)
{
XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) outgoing [%p]",
localCallNo(),remoteCallNo(),this);
@ -190,12 +192,15 @@ IAXTransaction::~IAXTransaction()
#ifdef XDEBUG
print();
#endif
if (m_trunkFrame)
m_trunkFrame->deref();
m_engine->removeTransaction(this);
if (state() != Terminating && state() != Terminated)
sendReject("Server shutdown");
}
IAXTransaction* IAXTransaction::factoryIn(IAXEngine* engine, IAXFullFrame* frame, u_int16_t lcallno, const SocketAddr& addr,
void* data)
IAXTransaction* IAXTransaction::factoryIn(IAXEngine* engine, IAXFullFrame* frame, u_int16_t lcallno,
const SocketAddr& addr, void* data)
{
IAXTransaction* tr = new IAXTransaction(engine,frame,lcallno,addr,data);
if (tr->type() != Incorrect)
@ -204,8 +209,8 @@ IAXTransaction* IAXTransaction::factoryIn(IAXEngine* engine, IAXFullFrame* frame
return 0;
}
IAXTransaction* IAXTransaction::factoryOut(IAXEngine* engine, Type type, u_int16_t lcallno, const SocketAddr& addr,
IAXIEList& ieList, void* data)
IAXTransaction* IAXTransaction::factoryOut(IAXEngine* engine, Type type, u_int16_t lcallno,
const SocketAddr& addr, IAXIEList& ieList, void* data)
{
IAXTransaction* tr = new IAXTransaction(engine,type,lcallno,addr,ieList,data);
if (tr->type() != Incorrect)
@ -218,79 +223,49 @@ IAXTransaction* IAXTransaction::processFrame(IAXFrame* frame)
{
if (!frame)
return 0;
// if (frame->fullFrame())
// Output("Transaction(%u,%u) received Frame(%u,%u) (%u,%u) stamp=%u",
// localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->fullFrame()->destCallNo(),frame->sourceCallNo(),frame->timeStamp());
if (state() == Terminated) {
// Output(" State is Terminated. DROP");
sendInval();
return 0;
}
if (state() == Terminating)
// Local terminate: Accept only Ack. Remote terminate: Accept none.
if (m_localReqEnd) {
if (!(frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::Ack))
{
// if (frame->fullFrame())
// Output(" State is Terminating and frame is not ACK. DROP");
if (m_localReqEnd && frame->fullFrame()) {
if (!(frame->type() == IAXFrame::IAX && frame->fullFrame()->subclass() == IAXControl::Ack))
return 0;
}
}
else
{
// if (frame->fullFrame()) Output(" State is Terminating on remote request. DROP");
return 0;
}
// Mini frame
if (!frame->fullFrame())
return processMedia(frame->data(),frame->timeStamp());
Lock lock(this);
m_inTotalFramesCount++;
// Frame is VNAK ?
if (frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::VNAK)
if (frame->type() == IAXFrame::IAX && frame->fullFrame()->subclass() == IAXControl::VNAK)
return retransmitOnVNAK(frame->fullFrame()->iSeqNo());
// Do we have space?
// Do we have enough space to keep this frame ?
if (m_inFrames.count() == m_maxInFrames) {
// Output(" Buffer overrun. DROP");
Debug(DebugWarn,"IAXTransaction(%u,%u) - processFrame. Buffer overrun!",localCallNo(),remoteCallNo());
Debug(DebugWarn,"Transaction(%u,%u) - processFrame. Buffer overrun!",localCallNo(),remoteCallNo());
m_inDroppedFrames++;
return 0;
}
bool fAck = frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::Ack;
bool fAck = frame->type() == IAXFrame::IAX && frame->fullFrame()->subclass() == IAXControl::Ack;
if (!fAck && !isFrameAcceptable(frame->fullFrame()))
{
// Output(" Not acceptable. DROP");
return 0;
}
incrementSeqNo(frame->fullFrame(),true);
if (!fAck)
sendAck(frame->fullFrame());
// Voice full frame: process voice data & format
if (frame->type() == IAXFrame::Voice && type() == New) {
if (frame->fullFrame()->subclass() && frame->fullFrame()->subclass() != m_formatIn) {
// Format changed.
if (m_engine->voiceFormatChanged(this,frame->fullFrame()->subclass()))
m_formatIn = frame->fullFrame()->subclass();
else {
// Output(" Media format change rejected. DROP");
DDebug(m_engine,DebugAll,"IAXTransaction(%u,%u) - processFrame. Media format (%u) change rejected!",
localCallNo(),remoteCallNo(),m_format);
m_pendingEvent = internalReject(s_iax_modNoMediaFormat);
return 0;
}
}
processMedia(frame->data(),frame->timeStamp(),true);
frame->data().clear();
if (frame->type() == IAXFrame::Voice && type() == New)
return processVoiceFrame(frame->fullFrame());
// Process incoming Ping
if (frame->type() == IAXFrame::IAX && frame->fullFrame()->subclass() == IAXControl::Ping) {
postFrame(IAXFrame::IAX,IAXControl::Pong,0,0,frame->timeStamp(),true);
return 0;
}
// Append frame to incoming frame list
m_inFrames.append(frame);
// Output("Transaction(%u,%u) enqueued Frame(%u,%u) stamp=%u",localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->timeStamp());
DDebug(m_engine,DebugAll,"Transaction(%u,%u) enqueued Frame(%u,%u) stamp=%u [%p]",
localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->timeStamp(),this);
DDebug(m_engine,DebugAll,"Transaction(%u,%u) enqueued Frame(%u,%u) iseq=%u oseq=%u stamp=%u [%p]",
localCallNo(),remoteCallNo(),frame->type(),frame->fullFrame()->subclass(),
frame->fullFrame()->iSeqNo(),frame->fullFrame()->oSeqNo(),frame->timeStamp(),this);
return this;
}
@ -325,7 +300,7 @@ IAXTransaction* IAXTransaction::processMedia(DataBlock& data, u_int32_t tStamp,
return 0;
}
IAXTransaction* IAXTransaction::sendMedia(const DataBlock& data, u_int8_t format)
IAXTransaction* IAXTransaction::sendMedia(const DataBlock& data, u_int32_t format)
{
if (!data.length())
return 0;
@ -346,25 +321,31 @@ IAXTransaction* IAXTransaction::sendMedia(const DataBlock& data, u_int8_t format
}
// Send mini frame
m_lastMiniFrameOut = (u_int16_t)ts;
unsigned char b[4] = {localCallNo() >> 8,localCallNo(),m_lastMiniFrameOut >> 8,m_lastMiniFrameOut};
DataBlock buf(b,4);
buf += data;
m_engine->writeSocket(buf.data(),buf.length(),remoteAddr());
if (m_trunkFrame)
m_trunkFrame->add(localCallNo(),data,m_lastMiniFrameOut);
else {
unsigned char b[4] = {localCallNo() >> 8,localCallNo(),m_lastMiniFrameOut >> 8,m_lastMiniFrameOut};
DataBlock buf(b,4);
buf += data;
m_engine->writeSocket(buf.data(),buf.length(),remoteAddr());
}
return this;
}
IAXEvent* IAXTransaction::getEvent(u_int64_t time)
{
IAXEvent* ev;
IAXEvent* ev = 0;
GenObject* obj;
bool delFrame;
Lock lock(this);
if (state() == Terminated)
return 0;
// Send ack for received frames
ackInFrames();
// Do we have a generated event ?
if (m_currentEvent)
return 0;
Lock lock(this);
// Waiting on remote cleanup ?
if (state() == Terminating && !m_localReqEnd)
return getEventTerminating(time);
@ -382,22 +363,18 @@ IAXEvent* IAXTransaction::getEvent(u_int64_t time)
}
// Process outgoing frames
ListIterator lout(m_outFrames);
IAXFrameOut* lastFrameAck = 0;
for (; (obj = lout.get());) {
IAXFrameOut* frame = static_cast<IAXFrameOut*>(obj);
ev = getEventResponse(frame,delFrame);
if((frame->ack() && frame->ackOnly()) || delFrame) {
//if (frame->type() == IAXFrame::Voice)
// Output("***********************Transaction(%u,%u) VOICE frame removed. ACK: %s",localCallNo(),remoteCallNo(),frame->ack()?"YES":"NO");
m_outFrames.remove(frame,true);
if (ev)
return keepEvent(ev);
continue;
}
if (delFrame)
frame->setAck();
if(frame->ack())
lastFrameAck = frame;
if (ev)
return keepEvent(ev);
break;
if(frame->ack() && frame->ackOnly())
continue;
// Adjust timeout for acknoledged auth frames sent with no auth response
if (state() == NewRemoteInvite_AuthSent && frame->ack())
frame->adjustAuthTimeout(time + m_engine->authTimeout() * 1000);
@ -405,24 +382,46 @@ IAXEvent* IAXTransaction::getEvent(u_int64_t time)
if (frame->timeout()) {
if (m_state == Terminating)
// Client already notified: Terminate transaction
return keepEvent(terminate(IAXEvent::Timeout,true));
ev = terminate(IAXEvent::Timeout,true);
else
// Client not notified: Notify it and terminate transaction
return keepEvent(terminate(IAXEvent::Timeout,true,frame,false));
ev = terminate(IAXEvent::Timeout,true,frame,false);
break;
}
// Retransmit ?
if (frame->timeForRetrans(time)) {
if (frame->ack())
frame->transmitted(); // Frame acknoledged: just update retransmission info
else {
// Output("Transaction(%u,%u) resending Frame(%u,%u) oseq=%u iseq=%u stamp=%u [%p]",
// localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->oSeqNo(),frame->iSeqNo(),frame->timeStamp(),this);
Debug(m_engine,DebugNote,"Transaction(%u,%u) resending Frame(%u,%u) oseq=%u iseq=%u stamp=%u [%p]",
localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->oSeqNo(),frame->iSeqNo(),frame->timeStamp(),this);
sendFrame(frame); // Retransmission
}
}
}
// Set the ACK flag for each frame before lastFrameAck and delete it if it must
if (lastFrameAck) {
lout.reset();
for (; (obj = lout.get());) {
IAXFrameOut* frame = static_cast<IAXFrameOut*>(obj);
if (frame == lastFrameAck) {
DDebug(m_engine,DebugAll,"Transaction(%u,%u) removing outgoing frame(%u,%u) oseq=%u iseq=%u stamp=%u [%p]",
localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->oSeqNo(),
frame->iSeqNo(),frame->timeStamp(),this);
m_outFrames.remove(frame,true);
break;
}
frame->setAck();
if (frame->ackOnly()) {
DDebug(m_engine,DebugAll,"Transaction(%u,%u) removing outgoing frame(%u,%u) with implicit ACK(%u) oseq=%u iseq=%u stamp=%u [%p]",
localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),lastFrameAck->oSeqNo(),
frame->oSeqNo(),frame->iSeqNo(),frame->timeStamp(),this);
m_outFrames.remove(frame,true);
}
}
}
if (ev)
return keepEvent(ev);
// Process incoming frames
ListIterator lin(m_inFrames);
for (; (obj = lin.get());) {
@ -519,7 +518,6 @@ bool IAXTransaction::sendReject(const char* cause, u_int8_t code)
case RegRel:
frametype = IAXControl::RegRej;
break;
case FwDownl:
case Poke:
default:
return false;
@ -648,9 +646,20 @@ bool IAXTransaction::abortReg()
return true;
}
bool IAXTransaction::enableTrunking(IAXMetaTrunkFrame* trunkFrame)
{
if (m_trunkFrame)
return false;
// Get a reference to the trunk frame
if (!(trunkFrame && trunkFrame->ref()))
return false;
m_trunkFrame = trunkFrame;
return true;
}
void IAXTransaction::print()
{
Debug(m_engine,DebugAll,"IAXTransaction - START PRINT [%p]",this);
Debug(m_engine,DebugInfo,"Transaction - START PRINT [%p]",this);
Output("Local call no: %u\nRemote call no: %u\nType: %u\nState: %u\nTimestamp: %llu",
localCallNo(),remoteCallNo(),type(),state(),(long unsigned long)timeStamp());
Output("Queues:\nOutgoing: %u",m_outFrames.count());
@ -669,7 +678,7 @@ void IAXTransaction::print()
Output(" %5u Type: %3u Subclass: %3u Out: %5u In: %5u Timestamp: %5u",
i+1,frame->type(),frame->subclass(),frame->oSeqNo(),frame->iSeqNo(), frame->timeStamp());
}
Debug(m_engine,DebugAll,"IAXTransaction - END PRINT [%p]",this);
Debug(m_engine,DebugInfo,"Transaction - END PRINT [%p]",this);
}
void IAXTransaction::init(IAXIEList& ieList)
@ -693,7 +702,6 @@ void IAXTransaction::init(IAXIEList& ieList)
ieList.getNumeric(IAXInfoElement::REFRESH,m_expire);
break;
case Poke:
case FwDownl:
default: ;
}
}
@ -728,13 +736,15 @@ bool IAXTransaction::isFrameAcceptable(const IAXFullFrame* frame)
return true;
if (delta > 0) {
// We missed some frames before this one: Send VNAK
Debug(m_engine,DebugInfo,"IAXTransaction(%u,%u) - received frame out of order! oseq=%u expecting %u. Send VNAK",
localCallNo(),remoteCallNo(),frame->oSeqNo(),m_iSeqNo);
Debug(m_engine,DebugInfo,"Transaction(%u,%u) - received Frame(%u,%u) out of order! oseq=%u expecting %u. Send VNAK",
localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->oSeqNo(),m_iSeqNo);
sendVNAK();
m_inOutOfOrderFrames++;
return false;
}
DDebug(m_engine,DebugInfo,"IAXTransaction(%u,%u) - received late frame with oseq=%u expecting %u [%p]",
localCallNo(),remoteCallNo(),frame->oSeqNo(),m_iSeqNo,this);
DDebug(m_engine,DebugInfo,"Transaction(%u,%u) - received late Frame(%u,%u) with oseq=%u expecting %u [%p]",
localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->oSeqNo(),m_iSeqNo,this);
sendAck(frame);
return false;
}
@ -794,14 +804,14 @@ void IAXTransaction::postFrame(IAXFrame::Type type, u_int32_t subclass, void* da
// adjust timestamp to be different from the last sent
int32_t delta = tStamp - m_lastFullFrameOut;
if (delta <= 0)
tStamp = m_lastFullFrameOut+1;
tStamp = m_lastFullFrameOut + 1;
}
m_lastFullFrameOut = 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 posting Frame(%u,%u) oseq=%u iseq=%u stamp=%u [%p]",
type,subclass,m_oSeqNo,m_iSeqNo,tStamp,this);
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);
@ -813,8 +823,12 @@ bool IAXTransaction::sendFrame(IAXFrameOut* frame, bool vnak)
return false;
bool b = m_engine->writeSocket(frame->data().data(),frame->data().length(),remoteAddr());
// Don't modify timeout if transmitted as a response to a VNAK
if (!vnak)
frame->transmitted();
if (!vnak) {
if (frame->retrans()) // Retransmission
frame->transmitted();
else // First transmission
frame->setRetrans();
}
return b;
}
@ -855,27 +869,18 @@ IAXEvent* IAXTransaction::createResponse(IAXFrameOut* frame, u_int8_t findType,
IAXEvent* IAXTransaction::getEventResponse(IAXFrameOut* frame, bool& delFrame)
{
delFrame = true;
delFrame = false;
if (findInFrameAck(frame)) {
//Output("************* Transaction(%u,%u) Found ACK for frame(%u,%u) stamp=%u",localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->timeStamp());
frame->setAck();
// Terminating frame sent
if (m_state == Terminating)
return terminate(IAXEvent::Terminated,true);
// Frame only need ACK
if (frame->ackOnly())
{
//Output(" ******* Frame only need ACK");
return 0;
}
}
// Frame only need ACK. Didn't found it. Return
delFrame = true;
if (frame->ackOnly()) {
delFrame = false;
return 0;
@ -1117,7 +1122,6 @@ IAXEvent* IAXTransaction::getEventRequest(IAXFullFrame* frame, bool& delFrame)
postFrame(IAXFrame::IAX,IAXControl::Pong,0,0,frame->timeStamp());
changeState(Terminating);
return 0;
case FwDownl:
default: ;
}
delFrame = false;
@ -1204,6 +1208,24 @@ bool IAXTransaction::findInFrameAck(const IAXFullFrame* frameOut)
return false;
}
void IAXTransaction::ackInFrames()
{
IAXFullFrame* ack = 0;
for (ObjList* l = m_inFrames.skipNull(); l; l = l->next()) {
IAXFullFrame* frame = static_cast<IAXFullFrame*>(l->get());
if (frame && frame->type() == IAXFrame::IAX && frame->subclass() != IAXControl::Ack)
ack = frame;
}
if (ack) {
int32_t interval = (int32_t)ack->oSeqNo() - m_lastAck;
if (interval > 32767 || (interval > -32767 && interval <= 0))
// Frame is older then the last ack'd
return;
m_lastAck = ack->oSeqNo();
sendAck(ack);
}
}
bool IAXTransaction::sendConnected(IAXFullFrame::ControlType subclass, IAXFrame::Type frametype)
{
if (state() != Connected)
@ -1219,6 +1241,8 @@ void IAXTransaction::sendAck(const IAXFullFrame* frame)
unsigned char buf[12] = {0x80 | localCallNo() >> 8,localCallNo(),remoteCallNo() >> 8,remoteCallNo(),
frame->timeStamp() >> 24,frame->timeStamp() >> 16,frame->timeStamp() >> 8,frame->timeStamp(),
frame->iSeqNo(),m_iSeqNo,IAXFrame::IAX,IAXControl::Ack};
DDebug(m_engine,DebugInfo,"Transaction(%u,%u) - Send ACK for Frame(%u,%u) oseq: %u iseq: %u",
localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->oSeqNo(),frame->iSeqNo());
m_engine->writeSocket(buf,12,remoteAddr());
}
@ -1242,6 +1266,7 @@ void IAXTransaction::sendVNAK()
IAXEvent* IAXTransaction::processInternalOutgoingRequest(IAXFrameOut* frame, bool& delFrame)
{
delFrame = false;
if (frame->type() != IAXFrame::IAX)
return 0;
delFrame = true;
@ -1266,19 +1291,13 @@ IAXEvent* IAXTransaction::processInternalOutgoingRequest(IAXFrameOut* frame, boo
IAXEvent* IAXTransaction::processInternalIncomingRequest(const IAXFullFrame* frame, bool& delFrame)
{
delFrame = false;
if (frame->type() != IAXFrame::IAX)
return 0;
delFrame = true;
switch (frame->subclass()) {
case IAXControl::Ping:
postFrame(IAXFrame::IAX,IAXControl::Pong,0,0,frame->timeStamp(),true);
return 0;
case IAXControl::LagRq:
postFrame(IAXFrame::IAX,IAXControl::LagRp,0,0,frame->timeStamp(),true);
return 0;
default: ;
if (frame->subclass() == IAXControl::LagRq) {
postFrame(IAXFrame::IAX,IAXControl::LagRp,0,0,frame->timeStamp(),true);
delFrame = true;
}
delFrame = false;
return 0;
}
@ -1374,6 +1393,29 @@ IAXEvent* IAXTransaction::getEventTerminating(u_int64_t time)
return 0;
}
IAXTransaction* IAXTransaction::processVoiceFrame(const IAXFullFrame* frame)
{
// Process format
DDebug(m_engine,DebugAll,"Transaction(%u,%u) - Received Voice Frame(%u,%u) iseq=%u oseq=%u stamp=%u [%p]",
localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),
frame->fullFrame()->iSeqNo(),frame->fullFrame()->oSeqNo(),frame->timeStamp(),this);
sendAck(frame);
if (frame->fullFrame()->subclass() && frame->fullFrame()->subclass() != m_formatIn) {
// Format changed.
if (m_engine->voiceFormatChanged(this,frame->fullFrame()->subclass()))
m_formatIn = frame->fullFrame()->subclass();
else {
DDebug(m_engine,DebugAll,"IAXTransaction(%u,%u). Process Voice Frame. Media format (%u) change rejected!",
localCallNo(),remoteCallNo(),m_format);
m_pendingEvent = internalReject(s_iax_modNoMediaFormat);
return 0;
}
}
// Process voice data
processMedia(((IAXFullFrame*)frame)->data(),frame->timeStamp(),true);
return 0;
}
IAXTransaction* IAXTransaction::retransmitOnVNAK(u_int16_t seqNo)
{
int c = 0;

File diff suppressed because it is too large Load Diff

View File

@ -45,12 +45,13 @@ static TokenDict dict_tos[] = {
{ 0, 0 }
};
static Configuration s_cfg;
class YIAXLineContainer;
class YIAXEngine;
class IAXURI;
/*
* Keep a single registration line
*/
class YIAXLine : public String
{
friend class YIAXLineContainer;
@ -61,10 +62,8 @@ public:
Registering,
Unregistering,
};
YIAXLine(const String& name);
virtual ~YIAXLine();
inline State state() const
{ return m_state; }
inline bool registered() const
@ -87,27 +86,26 @@ public:
{ return m_localPort; }
inline int remotePort() const
{ return m_remotePort; }
private:
State m_state;
String m_username; /* Username */
String m_password; /* Password */
String m_callingNo; /* Calling number */
String m_callingName; /* Calling name */
u_int16_t m_expire; /* Expire time */
String m_username; // Username
String m_password; // Password
String m_callingNo; // Calling number
String m_callingName; // Calling name
u_int16_t m_expire; // Expire time
String m_localAddr;
String m_remoteAddr;
int m_localPort;
int m_remotePort;
u_int32_t m_nextReg; /* Time to next registration */
u_int32_t m_nextKeepAlive; /* Time to next keep alive signal */
bool m_registered;
bool m_register; /* Operation flag: True - register */
u_int32_t m_nextReg; // Time to next registration
u_int32_t m_nextKeepAlive; // Time to next keep alive signal
bool m_registered; // Registered flag. If true the line is registered
bool m_register; // Operation flag: True - register
IAXTransaction* m_transaction;
};
/**
* YIAXLineContainer
/*
* Line container: Add/Delete/Update/Register/Unregister lines
*/
class YIAXLineContainer : public Mutex
{
@ -115,12 +113,12 @@ public:
inline YIAXLineContainer() : Mutex(true) {}
inline ~YIAXLineContainer() {}
/**
/*
* Logout and remove all lines
*/
void clear();
/**
/*
* Update a line from a message
* This method is thread safe
* @param msg Received message
@ -128,33 +126,33 @@ public:
*/
bool updateLine(Message &msg);
/**
/*
* Event handler for a registration.
* @param event The event.
*/
void handleEvent(IAXEvent* event);
/**
/*
* Terminate notification of a Register/Unregister operation
* This method is thread safe
* @param event The event (result)
*/
void regTerminate(IAXEvent* event);
/**
/*
* Timer notification
* This method is thread safe
* @param time Time
*/
void evTimer(Time& time);
/**
/*
* Fill a named list from a line
* This method is thread safe
*/
bool fillList(String& name, NamedList& dest, SocketAddr& addr, bool& registered);
/**
/*
* Check if a line exists
*/
inline bool hasLine(const String& line)
@ -171,7 +169,7 @@ private:
ObjList m_lines;
};
/**
/*
* Thread class for reading data from socket for the specified IAX engine
*/
class YIAX_API YIAXListener : public Thread
@ -185,7 +183,7 @@ protected:
YIAXEngine* m_engine;
};
/**
/*
* Thread class for reading events for the specified IAX engine
*/
class YIAX_API YIAXGetEvent : public Thread
@ -199,13 +197,27 @@ protected:
YIAXEngine* m_engine;
};
/**
* YIAXEngine
/*
* Thread class for sending trunked mini frames for the specified IAX engine
*/
class YIAX_API YIAXTrunking : public Thread
{
public:
inline YIAXTrunking(YIAXEngine* engine, const char* name = 0, Priority prio = Normal)
: Thread(name,prio), m_engine(engine)
{}
virtual void run();
protected:
YIAXEngine* m_engine;
};
/*
* The IAX engine for this driver
*/
class YIAXEngine : public IAXEngine
{
public:
/**
/*
* Constructor
* @param port UDP port to use
* @param transListCount Number of entries in the transaction hash table
@ -214,17 +226,16 @@ public:
* @param authTimeout Timeout (in seconds) of acknoledged auth frames sent
* @param transTimeout Timeout (in seconds) on remote request of transactions belonging to this engine
* @param maxFullFrameDataLen Max full frame IE list (buffer) length
* @param trunkSendInterval Send trunk meta frame interval
*/
YIAXEngine(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_int16_t authTimeout, u_int16_t transTimeout,
u_int16_t maxFullFrameDataLen, u_int32_t trunkSendInterval);
/**
* Destructor
*/
virtual ~YIAXEngine()
{}
/**
/*
* Process media from remote peer.
* @param transaction IAXTransaction that owns the call leg
* @param data Media data.
@ -232,7 +243,7 @@ public:
*/
virtual void processMedia(IAXTransaction* transaction, DataBlock& data, u_int32_t tStamp);
/**
/*
* Initiate an outgoing registration (release) request.
* @param line YIAXLine pointer to use for registration.
* @param regreq Registration request flag. If false a registration release will take place.
@ -240,7 +251,7 @@ public:
*/
IAXTransaction* reg(YIAXLine* line, bool regreq = true);
/**
/*
* Initiate an aoutgoing call.
* @param addr Address to poke.
* @param params Call parameters.
@ -248,42 +259,43 @@ public:
*/
IAXTransaction* call(SocketAddr& addr, NamedList& params);
/**
/*
* Initiate a test of existence of a remote IAX peer.
* @param addr Address to poke.
* @return IAXTransaction pointer on success.
*/
IAXTransaction* poke(SocketAddr& addr);
/**
/*
* Start thread members
* @param listenThreadCount Reading socket thread count.
* @param eventThreadCount Reading event thread count.
* @param trunkingThreadCount Trunking thread count.
*/
void start(u_int16_t listenThreadCount, u_int16_t eventThreadCount);
void start(u_int16_t listenThreadCount, u_int16_t eventThreadCount,u_int16_t trunkingThreadCount);
protected:
/**
/*
* Event handler for transaction with a connection.
*/
virtual void processEvent(IAXEvent* event);
/**
/*
* Event handler for incoming registration transactions.
*/
void processRemoteReg(IAXEvent* event);
/**
/*
* Send Register/Unregister messages to Engine
*/
bool userreg(IAXTransaction* tr, bool regrel = true);
private:
bool m_threadsCreated; /* True if reading and get events threads were created */
bool m_threadsCreated; // True if reading and get events threads were created
};
/**
/*
* YIAXRegDataHandler
*/
class YIAXRegDataHandler : public MessageHandler
@ -295,7 +307,7 @@ public:
virtual bool received(Message &msg);
};
/**
/*
* YIAXDriver
*/
class YIAXDriver : public Driver
@ -304,11 +316,8 @@ public:
YIAXDriver();
virtual ~YIAXDriver();
virtual void initialize();
/* Create an outgoing call */
virtual bool msgExecute(Message& msg, String& dest);
virtual bool msgRoute(Message& msg);
virtual bool received(Message& msg, int id);
inline u_int32_t defaultCodec() const
@ -325,15 +334,15 @@ public:
protected:
YIAXEngine* m_iaxEngine;
u_int32_t m_defaultCodec; /* Default codec */
u_int32_t m_codecs; /* Capability */
int m_port; /* Default UDP port */
u_int32_t m_defaultCodec;
u_int32_t m_codecs;
int m_port; // Default UDP port
};
class YIAXConnection;
/**
* YIAXConsumer
/*
* Connection's data consumer
*/
class YIAXConsumer : public DataConsumer
{
@ -347,8 +356,8 @@ private:
u_int32_t m_format;
};
/**
* YIAXSource
/*
* Connection's data source
*/
class YIAXSource : public DataSource
{
@ -362,8 +371,8 @@ private:
u_int32_t m_format;
};
/**
* YIAXConnection
/*
* The connection
*/
class YIAXConnection : public Channel
{
@ -391,28 +400,20 @@ public:
void handleEvent(IAXEvent* event);
/* Start router */
bool route(bool authenticated = false);
protected:
/* Hangup */
void hangup(const char* reason = 0, bool reject = false);
/* Hangup */
inline void hangup(IAXEvent* event, const char* reason = 0, bool reject = false) {
event->setFinal();
hangup(reason,reject);
}
/* Start consumer */
void startAudioIn();
/* Start source */
void startAudioOut();
/* Events */
void evAuthRep(IAXEvent* event);
// Safe deref the connection if the reference counter was increased during registration
void safeDeref();
bool safeRefIncrease();
private:
YIAXEngine* m_iaxEngine; // IAX engine owning the transaction
IAXTransaction* m_transaction; // IAX transaction
@ -421,11 +422,12 @@ private:
bool m_mutedOut; // No local media accepted
String m_reason; // Call end reason text
bool m_hangup; // Need to send chan.hangup message
Mutex m_mutexTrans; // Safe m_transaction operations
Mutex m_mutexRefIncreased; // Safe ref/deref connection
bool m_refIncreased; // If true, the reference counter was increased
};
/**
/*
* IAXURI
* [iax[2]:][username@]host[:port][/called_number[@called_context]]
*/
@ -461,19 +463,18 @@ private:
};
/**
/*
* Local data
*/
/* Init the driver */
static YIAXDriver iplugin;
/* Lines */
static YIAXLineContainer s_lines;
static Configuration s_cfg; // Configuration file
static YIAXDriver iplugin; // Init the driver
static YIAXLineContainer s_lines; // Lines
/**
/*
* Class definitions
*/
/**
/*
* YIAXLine
*/
YIAXLine::YIAXLine(const String& name)
@ -489,7 +490,7 @@ YIAXLine::~YIAXLine()
{
}
/**
/*
* YIAXLineContainer
*/
bool YIAXLineContainer::updateLine(Message& msg)
@ -568,8 +569,10 @@ void YIAXLineContainer::evTimer(Time& time)
Lock lock(this);
for (ObjList* l = m_lines.skipNull(); l; l = l->next()) {
YIAXLine* line = static_cast<YIAXLine*>(l->get());
// Line exists and is idle ?
if (!line || line->state() != YIAXLine::Idle)
continue;
// Time to keep alive
if (sec > line->m_nextKeepAlive) {
line->m_nextKeepAlive = sec + 25;
SocketAddr addr(AF_INET);
@ -577,6 +580,7 @@ void YIAXLineContainer::evTimer(Time& time)
addr.port(line->remotePort());
iplugin.getEngine()->keepAlive(addr);
}
// Time to reg/unreg
if (sec > line->m_nextReg) {
line->m_nextReg += line->expire();
if (line->m_register)
@ -732,13 +736,22 @@ void YIAXGetEvent::run()
m_engine->runGetEvents();
}
/**
* YIAXTrunking
*/
void YIAXTrunking::run()
{
Debug(m_engine,DebugAll,"%s started",currentName());
m_engine->runProcessTrunkFrames();
}
/**
* YIAXEngine
*/
YIAXEngine::YIAXEngine(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_int16_t authTimeout, u_int16_t transTimeout, u_int16_t maxFullFrameDataLen, u_int32_t trunkSendInterval)
: IAXEngine(port,transListCount,retransCount,retransInterval,authTimeout,transTimeout,
maxFullFrameDataLen,iplugin.defaultCodec(),iplugin.codecs()),
maxFullFrameDataLen,iplugin.defaultCodec(),iplugin.codecs(),trunkSendInterval),
m_threadsCreated(false)
{
}
@ -806,7 +819,7 @@ IAXTransaction* YIAXEngine::poke(SocketAddr& addr)
return startLocalTransaction(IAXTransaction::Poke,addr,ieList);
}
void YIAXEngine::start(u_int16_t listenThreadCount, u_int16_t eventThreadCount)
void YIAXEngine::start(u_int16_t listenThreadCount, u_int16_t eventThreadCount, u_int16_t trunkThreadCount)
{
if (m_threadsCreated)
return;
@ -814,18 +827,19 @@ void YIAXEngine::start(u_int16_t listenThreadCount, u_int16_t eventThreadCount)
Debug(DebugWarn,"YIAXEngine - start. No reading socket threads(s)!.");
if (!eventThreadCount)
Debug(DebugWarn,"YIAXEngine - start. No reading event threads(s)!.");
if (!trunkThreadCount)
Debug(DebugWarn,"YIAXEngine - start. No trunking threads(s)!.");
for (; listenThreadCount; listenThreadCount--)
(new YIAXListener(this,"YIAXListener thread"))->startup();
for (; eventThreadCount; eventThreadCount--)
(new YIAXGetEvent(this,"YIAXGetEvent thread"))->startup();
for (; trunkThreadCount; trunkThreadCount--)
(new YIAXTrunking(this,"YIAXTrunking thread"))->startup();
m_threadsCreated = true;
}
void YIAXEngine::processEvent(IAXEvent* event)
{
#if 0
static u_int16_t hp = 0, rej = 0, final = 0;
#endif
YIAXConnection* connection = 0;
switch (event->getTransaction()->type()) {
case IAXTransaction::New:
@ -834,21 +848,6 @@ static u_int16_t hp = 0, rej = 0, final = 0;
// We already have a channel for this call
connection->handleEvent(event);
if (event->final()) {
#if 0
final++;
switch (event->type()) {
case IAXEvent::Hangup: hp++; break;
case IAXEvent::Reject: rej++; break;
case IAXEvent::Timeout:
Output("**************************** Engine HALT: Channels: %u ****************************",iplugin.channels().count());
Output("Final events: %u Hangup: %u, Reject: %u",final,hp,rej);
event->getTransaction()->print();
Engine::halt(0xFF);
break;
default:
break;
}
#endif
// Final event: disconnect
Debug(this,DebugAll,"YIAXEngine::processEvent - Disconnect connection [%p]",connection);
connection->disconnect();
@ -861,6 +860,8 @@ switch (event->type()) {
event->getTransaction()->setUserData(connection);
if (!connection->route())
event->getTransaction()->setUserData(0);
else
enableTrunking(event->getTransaction());
}
}
break;
@ -1001,15 +1002,17 @@ void YIAXDriver::initialize()
installRelay(Halt);
installRelay(Route);
// Init IAX engine
u_int16_t transListCount = 16;
u_int16_t transListCount = 64;
u_int16_t retransCount = 5;
u_int16_t retransInterval = 500;
u_int16_t authTimeout = 30;
u_int16_t transTimeout = 10;
u_int16_t maxFullFrameDataLen = 1400;
u_int32_t trunkSendInterval = 10;
if (!m_iaxEngine) {
Engine::install(new YIAXRegDataHandler);
m_iaxEngine = new YIAXEngine(m_port,transListCount,retransCount,retransInterval,authTimeout,transTimeout,maxFullFrameDataLen);
m_iaxEngine = new YIAXEngine(m_port,transListCount,retransCount,retransInterval,authTimeout,
transTimeout,maxFullFrameDataLen,trunkSendInterval);
m_iaxEngine->debugChain(this);
int tos = s_cfg.getIntValue("general","tos",dict_tos,0);
if (tos) {
@ -1019,7 +1022,8 @@ void YIAXDriver::initialize()
}
int readThreadCount = 3;
int eventThreadCount = 3;
m_iaxEngine->start(readThreadCount,eventThreadCount);
int trunkingThreadCount = 1;
m_iaxEngine->start(readThreadCount,eventThreadCount,trunkingThreadCount);
}
bool YIAXDriver::msgRoute(Message& msg)
@ -1149,7 +1153,7 @@ void YIAXSource::Forward(const DataBlock& data, unsigned long tStamp)
YIAXConnection::YIAXConnection(YIAXEngine* iaxEngine, IAXTransaction* transaction, Message* msg)
: Channel(&iplugin,0,transaction->outgoing()),
m_iaxEngine(iaxEngine), m_transaction(transaction), m_mutedIn(false), m_mutedOut(false),
m_hangup(true), m_mutexRefIncreased(true), m_refIncreased(false)
m_hangup(true), m_mutexTrans(true), m_mutexRefIncreased(true), m_refIncreased(false)
{
DDebug(this,DebugAll,"YIAXConnection::YIAXConnection [%p]",this);
setMaxcall(msg);
@ -1179,8 +1183,10 @@ YIAXConnection::~YIAXConnection()
void YIAXConnection::callAccept(Message& msg)
{
DDebug(this,DebugAll,"callAccept [%p]",this);
m_mutexTrans.lock();
if (m_transaction)
m_transaction->sendAccept();
m_mutexTrans.unlock();
Channel::callAccept(msg);
}
@ -1194,11 +1200,14 @@ void YIAXConnection::callRejected(const char* error, const char* reason, const M
reason = error;
DDebug(this,DebugInfo,"callRejected [%p]. Error: '%s'",this,error);
String s(error);
m_mutexTrans.lock();
if (m_transaction && (s == "noauth") && safeRefIncrease()) {
Debug(this,DebugAll,"callRejected [%p]. Requesting authentication",this);
m_transaction->sendAuth(m_password);
m_mutexTrans.unlock();
return;
}
m_mutexTrans.unlock();
hangup(reason,true);
}
@ -1214,6 +1223,7 @@ bool YIAXConnection::callRouted(Message& msg)
bool YIAXConnection::msgRinging(Message& msg)
{
Lock lock(&m_mutexTrans);
if (m_transaction) {
m_transaction->sendRinging();
startAudioOut();
@ -1224,6 +1234,7 @@ bool YIAXConnection::msgRinging(Message& msg)
bool YIAXConnection::msgAnswered(Message& msg)
{
Lock lock(&m_mutexTrans);
if (m_transaction) {
m_transaction->sendAnswer();
startAudioIn();
@ -1235,6 +1246,7 @@ bool YIAXConnection::msgAnswered(Message& msg)
bool YIAXConnection::msgTone(Message& msg, const char* tone)
{
Lock lock(&m_mutexTrans);
if (m_transaction) {
while (tone && *tone)
m_transaction->sendDtmf(*tone++);
@ -1245,6 +1257,7 @@ bool YIAXConnection::msgTone(Message& msg, const char* tone)
bool YIAXConnection::msgText(Message& msg, const char* text)
{
Lock lock(&m_mutexTrans);
if (m_transaction) {
m_transaction->sendText(text);
return true;
@ -1329,7 +1342,7 @@ void YIAXConnection::handleEvent(IAXEvent* event)
break;
case IAXEvent::Timeout:
DDebug(this,DebugNote,"YIAXConnection - TIMEOUT. Transaction: %u,%u, Frame: %u,%u ",
m_transaction->localCallNo(),m_transaction->remoteCallNo(),event->frameType(),event->subclass());
event->getTransaction()->localCallNo(),event->getTransaction()->remoteCallNo(),event->frameType(),event->subclass());
m_reason = "Timeout";
break;
case IAXEvent::Busy:
@ -1350,7 +1363,7 @@ void YIAXConnection::handleEvent(IAXEvent* event)
}
}
void YIAXConnection::hangup(const char *reason, bool reject)
void YIAXConnection::hangup(const char* reason, bool reject)
{
if (!m_hangup)
// Already done
@ -1360,6 +1373,7 @@ void YIAXConnection::hangup(const char *reason, bool reject)
reason = m_reason;
if (!reason)
reason = Engine::exiting() ? "Server shutdown" : "Unexpected problem";
m_mutexTrans.lock();
if (m_transaction) {
m_transaction->setUserData(0);
if (reject)
@ -1368,6 +1382,7 @@ void YIAXConnection::hangup(const char *reason, bool reject)
m_transaction->sendHangup(reason);
m_transaction = 0;
}
m_mutexTrans.unlock();
Message* m = message("chan.hangup",true);
m->setParam("status","hangup");
m->setParam("reason",reason);
@ -1380,6 +1395,7 @@ bool YIAXConnection::route(bool authenticated)
if (!m_transaction)
return false;
Message* m = message("call.preroute",false,true);
Lock lock(&m_mutexTrans);
if (authenticated) {
DDebug(this,DebugAll,"Route pass 2: Password accepted.");
m_refIncreased = false;
@ -1407,8 +1423,10 @@ void YIAXConnection::startAudioIn()
if (getSource())
return;
u_int32_t format = 0;
m_mutexTrans.lock();
if (m_transaction)
format = m_transaction->formatIn();
m_mutexTrans.unlock();
const char* formatText = IAXFormat::audioText(format);
setSource(new YIAXSource(this,format,formatText));
getSource()->deref();
@ -1420,8 +1438,10 @@ void YIAXConnection::startAudioOut()
if (getConsumer())
return;
u_int32_t format = 0;
m_mutexTrans.lock();
if (m_transaction)
format = m_transaction->formatOut();
m_mutexTrans.unlock();
const char* formatText = (char*)IAXFormat::audioText(format);
setConsumer(new YIAXConsumer(this,format,formatText));
getConsumer()->deref();