/** * engine.cpp * Yet Another IAX2 Stack * This file is part of the YATE Project http://YATE.null.ro * * Yet Another Telephony Engine - a fully featured software PBX and IVR * Copyright (C) 2004-2006 Null Team * Author: Marian Podgoreanu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include using namespace TelEngine; 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) : Mutex(true), m_lastGetEvIndex(0), m_authRequired(authRequired), m_maxFullFrameDataLen(maxFullFrameDataLen), m_startLocalCallNo(0), m_transListCount(0), m_retransCount(retransCount), m_retransInterval(retransInterval), m_authTimeout(authTimeout), m_transTimeout(transTimeout), m_format(format), m_capability(capab), m_mutexTrunk(true), m_trunkSendInterval(trunkSendInterval) { debugName("iaxengine"); Debug(this,DebugAll,"Automatically request authentication set to '%s'.", (authRequired?"YES":"NO")); if ((port <= 0) || port > 65535) port = 4569; if (transListCount < 4) transListCount = 4; else if (transListCount > 256) transListCount = 256; m_transList = new ObjList*[transListCount]; int i; for (i = 0; i < transListCount; i++) m_transList[i] = new ObjList; m_transListCount = transListCount; for(i = 0; i <= IAX2_MAX_CALLNO; i++) m_lUsedCallNo[i] = false; m_socket.create(AF_INET,SOCK_DGRAM); SocketAddr addr(AF_INET); addr.host(iface); addr.port(port); m_socket.setBlocking(false); if (!m_socket.bind(addr)) Debug(this,DebugWarn,"Failed to bind socket to %s:%d",c_safe(iface),port); m_startLocalCallNo = 1 + (u_int16_t)(random() % IAX2_MAX_CALLNO); } IAXEngine::~IAXEngine() { for (int i = 0; i < m_transListCount; i++) TelEngine::destruct(m_transList[i]); delete[] m_transList; } IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, IAXFrame* frame) { if (!frame) return 0; IAXTransaction* tr; ObjList* l; Lock lock(this); // Transaction exists for this frame? // Incomplete transactions. They MUST receive a full frame if (frame->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())) continue; // Complete transaction if (tr->processFrame(frame)) { 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)", tr->localCallNo(),tr->remoteCallNo()); return tr; } break; } } // Complete transactions l = m_transList[frame->sourceCallNo() % m_transListCount]; for (; l; l = l->next()) { tr = static_cast(l->get()); if (!(tr && tr->remoteCallNo() == frame->sourceCallNo())) continue; // Mini frame if (!frame->fullFrame()) { if (addr == tr->remoteAddr()) { // keep transaction referenced but unlock the engine RefPointer 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()) { // keep transaction referenced but unlock the engine RefPointer 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->fullFrame()->subclass()) { case IAXControl::New: case IAXControl::RegReq: case IAXControl::RegRel: case IAXControl::Poke: break; case IAXControl::Inval: // These are often used as keepalives return 0; case IAXControl::FwDownl: default: if (frame->fullFrame()) { if (frame->fullFrame()->destCallNo()) XDebug(this,DebugAll,"Unmatched Frame(%u,%u) for (%u,%u)", frame->type(),frame->fullFrame()->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 u_int16_t lcn = generateCallNo(); if (!lcn) return 0; // Create and add transaction tr = IAXTransaction::factoryIn(this,(IAXFullFrame*)frame->fullFrame(),lcn,addr); if (tr) m_transList[frame->sourceCallNo() % m_transListCount]->append(tr); else releaseCallNo(lcn); return tr; } IAXTransaction* IAXEngine::addFrame(const SocketAddr& addr, const unsigned char* buf, unsigned int len) { IAXFrame* frame = IAXFrame::parse(buf,len,this,&addr); if (!frame) return 0; if (frame->fullFrame() && debugAt(DebugAll)) { String s; SocketAddr local; m_socket.getSockName(local); frame->fullFrame()->toString(s,local,addr,true); Debug(this,DebugInfo,"Received frame.%s",s.c_str()); } IAXTransaction* tr = addFrame(addr,frame); if (!tr) frame->deref(); return tr; } bool IAXEngine::process() { bool ok = false; for (;;) { IAXEvent* event = getEvent(Time::msecNow()); if (!event) break; ok = true; if ((event->final() && !event->frameType()) || !event->getTransaction()) { XDebug(this,DebugAll,"Deleting internal event type %u Frame(%u,%u)", event->type(),event->frameType(),event->subclass()); delete event; continue; } processEvent(event); } return ok; } void IAXEngine::readSocket(SocketAddr& addr) { unsigned char buf[1500]; while (1) { int len = m_socket.recvFrom(buf,sizeof(buf),addr); if (len == Socket::socketError()) { if (!m_socket.canRetry()) Debug(this,DebugWarn,"Socket read error: %s (%d)", ::strerror(m_socket.error()),m_socket.error()); Thread::msleep(1,true); continue; } addFrame(addr,buf,len); } } bool IAXEngine::writeSocket(const void* buf, int len, const SocketAddr& addr, IAXFullFrame* frame) { if (frame && debugAt(DebugAll)) { String s; SocketAddr local; m_socket.getSockName(local); frame->toString(s,local,addr,false); Debug(this,DebugInfo,"Sending frame.%s",s.c_str()); } len = m_socket.sendTo(buf,len,addr); if (len == Socket::socketError()) { 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; } void IAXEngine::runGetEvents() { while (1) { if (!process()) { Thread::msleep(2,true); continue; } } } void IAXEngine::removeTransaction(IAXTransaction* transaction) { if (!transaction) return; Lock lock(this); releaseCallNo(transaction->localCallNo()); if (!m_incompleteTransList.remove(transaction,false)) { if (m_transList[transaction->remoteCallNo() % m_transListCount]->remove(transaction,false)) { DDebug(this,DebugAll,"Transaction(%u,%u) removed", transaction->localCallNo(),transaction->remoteCallNo()); } else { DDebug(this,DebugAll,"Trying to remove transaction(%u,%u) but does not exist", transaction->localCallNo(),transaction->remoteCallNo()); } } else { DDebug(this,DebugAll,"Transaction(%u,%u) (incomplete outgoing) removed", transaction->localCallNo(),transaction->remoteCallNo()); } } u_int32_t IAXEngine::transactionCount() { u_int32_t n = 0; Lock lock(this); // Incomplete transactions n += m_incompleteTransList.count(); // Complete transactions for (int i = 0; i < m_transListCount; i++) n += m_transList[i]->count(); return n; } void IAXEngine::keepAlive(SocketAddr& addr) { #if 0 unsigned char buf[12] = {0x80,0,0,0,0,0,0,0,0,0,IAXFrame::IAX,IAXControl::Inval}; writeSocket(buf,sizeof(buf),addr); #endif IAXFullFrame* f = new IAXFullFrame(IAXFrame::IAX,IAXControl::Inval,0,0,0,0,0); writeSocket(f->data().data(),f->data().length(),addr,f); f->deref(); } 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(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", event,event->subclass()); delete event; } IAXEvent* IAXEngine::getEvent(u_int64_t time) { IAXTransaction* tr; IAXEvent* ev; ObjList* l; lock(); // Find for incomplete transactions l = m_incompleteTransList.skipNull(); for (; l; l = l->next()) { tr = static_cast(l->get()); if (tr && 0 != (ev = tr->getEvent(time))) { unlock(); return ev; } continue; } // 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(iter.get()); // end of iteration? if (!tr) break; RefPointer t = tr; // dead pointer? if (!t) continue; unlock(); if (0 != (ev = t->getEvent(time))) return ev; lock(); } } m_lastGetEvIndex = 0; unlock(); return 0; } //TODO: Optimize generateCallNo & releaseCallNo u_int16_t IAXEngine::generateCallNo() { u_int16_t i; m_startLocalCallNo++; if (m_startLocalCallNo > IAX2_MAX_CALLNO) m_startLocalCallNo = 1; 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++) if (!m_lUsedCallNo[i]) { m_lUsedCallNo[i] = true; return i; } Debug(this,DebugWarn,"Unable to generate call number. Transaction count: %u",transactionCount()); return 0; } void IAXEngine::releaseCallNo(u_int16_t lcallno) { m_lUsedCallNo[lcallno] = false; } 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) { m_incompleteTransList.append(tr); if (trunking) enableTrunking(tr); } else releaseCallNo(lcn); return tr; } bool IAXEngine::acceptFormatAndCapability(IAXTransaction* trans) { if (!trans) return false; u_int32_t format = trans->format(); u_int32_t capability = m_capability & trans->capability(); #ifdef XDEBUG if (!trans->outgoing()) { String rec; IAXFormat::formatList(rec,m_capability); Debug(this,DebugAll,"acceptFormatAndCapability. Local: Format: %u(%s). Capabilities: '%s'.", m_format,IAXFormat::audioText(m_format),rec.c_str()); IAXFormat::formatList(rec,trans->capability()); Debug(this,DebugAll,"acceptFormatAndCapability. Received: Format: %u(%s). Capabilities: '%s'.", format,IAXFormat::audioText(format),rec.c_str()); } #endif //XDEBUG // Valid capability ? if (!capability) { if (!trans->outgoing()) XDebug(this,DebugAll,"acceptFormatAndCapability. No capabilities received."); return false; } for (;;) { // Received format is valid ? if (0 != (format & capability) && IAXFormat::audioText(format)) break; // Local format is valid ? format = m_format; if (0 != (m_format & capability) && IAXFormat::audioText(format)) break; // No valid format: choose one from capability format = 0; u_int32_t i = 0; for (; IAXFormat::audioData[i].value; i++) if (0 != (capability & IAXFormat::audioData[i].value)) break; if (IAXFormat::audioData[i].value) { format = IAXFormat::audioData[i].value; break; } XDebug(this,DebugAll,"acceptFormatAndCapability. Unable to choose a format."); return false; } XDebug(this,DebugAll,"acceptFormatAndCapability. Format %u: '%s'.", format,IAXFormat::audioText(format)); trans->m_format = format; trans->m_capability = capability; trans->m_formatIn = format; if (!trans->outgoing()) trans->m_formatOut = format; return true; } void IAXEngine::defaultEventHandler(IAXEvent* event) { DDebug(this,DebugAll,"defaultEventHandler - Event type: %u. Frame - Type: %u Subclass: %u", event->type(),event->frameType(),event->subclass()); IAXTransaction* tr = event->getTransaction(); switch (event->type()) { case IAXEvent::New: tr->sendReject("Feature not implemented or unsupported"); break; default: ; } } void IAXEngine::enableTrunking(IAXTransaction* trans) { if (!trans || trans->type() != IAXTransaction::New) return; Lock lock(&m_mutexTrunk); IAXMetaTrunkFrame* frame; // Already enabled ? for (ObjList* l = m_trunkList.skipNull(); l; l = l->next()) { frame = static_cast(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::getMD5FromChallenge(String& md5data, const String& challenge, const String& password) { MD5 md5; md5 << challenge << password; md5data = md5.hexDigest(); } bool IAXEngine::isMD5ChallengeCorrect(const String& md5data, const String& challenge, const String& password) { MD5 md5; md5 << challenge << password; return md5data == md5.hexDigest(); } /* * 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) { if (transaction && transaction->ref()) m_transaction = transaction; } 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) { if (transaction && transaction->ref()) m_transaction = transaction; if (frame) { m_frameType = frame->type(); m_subClass = frame->subclass(); } } IAXEvent::~IAXEvent() { 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(); } } /* vi: set ts=8 sw=4 sts=4 noet: */