/** * transaction.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 using namespace TelEngine; String IAXTransaction::s_iax_modNoAuthMethod("Unsupported or missing authentication method or missing challenge"); String IAXTransaction::s_iax_modNoMediaFormat("Unsupported or missing media format or capability"); String IAXTransaction::s_iax_modInvalidAuth("Invalid authentication request, response or challenge"); String IAXTransaction::s_iax_modNoUsername("Username is missing"); unsigned char IAXTransaction::m_maxInFrames = 100; IAXTransaction::IAXTransaction(IAXEngine* engine, IAXFullFrame* frame, u_int16_t lcallno, const SocketAddr& addr, void* data) : Mutex(true), m_localInitTrans(false), m_localReqEnd(false), m_type(Incorrect), m_state(Unknown), m_timeStamp(Time::msecNow() - 1), m_timeout(0), m_addr(addr), m_lCallNo(lcallno), m_rCallNo(frame->sourceCallNo()), m_oSeqNo(0), m_iSeqNo(0), m_engine(engine), m_userdata(data), m_lastFullFrameOut(0), m_lastMiniFrameOut(0xFFFF), m_lastMiniFrameIn(0), m_lastAck(0xFFFF), m_mutexInMedia(true), m_pendingEvent(0), m_currentEvent(0), m_retransCount(5), m_retransInterval(500), m_pingInterval(20000), m_timeToNextPing(0), m_inTotalFramesCount(1), m_inOutOfOrderFrames(0), m_inDroppedFrames(0), m_authmethod(IAXAuthMethod::MD5), m_expire(60), m_format(0), m_formatIn(0), m_formatOut(0), m_capability(0), m_trunkFrame(0) { XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) incoming [%p]", localCallNo(),remoteCallNo(),this); // Setup transaction m_retransCount = engine->retransCount(); m_retransInterval = engine->retransInterval(); m_timeToNextPing = m_timeStamp + m_pingInterval; switch (frame->subclass()) { case IAXControl::New: m_type = New; break; case IAXControl::RegReq: m_type = RegReq; break; case IAXControl::RegRel: m_type = RegRel; break; case IAXControl::Poke: m_type = Poke; break; default: XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) incoming [%p]. Unsupported type: %u", localCallNo(),remoteCallNo(),this,frame->subclass()); return; } // Append frame to incoming list Lock lock(this); m_inFrames.append(frame); incrementSeqNo(frame,true); } IAXTransaction::IAXTransaction(IAXEngine* engine, Type type, u_int16_t lcallno, const SocketAddr& addr, IAXIEList& ieList, void* data) : Mutex(true), m_localInitTrans(true), m_localReqEnd(false), m_type(type), m_state(Unknown), m_timeStamp(Time::msecNow() - 1), m_timeout(0), m_addr(addr), m_lCallNo(lcallno), m_rCallNo(0), m_oSeqNo(0), m_iSeqNo(0), m_engine(engine), m_userdata(data), m_lastFullFrameOut(0), m_lastMiniFrameOut(0xFFFF), m_lastMiniFrameIn(0), m_lastAck(0xFFFF), m_mutexInMedia(true), m_pendingEvent(0), m_currentEvent(0), m_retransCount(5), m_retransInterval(500), m_pingInterval(20000), m_timeToNextPing(0), m_inTotalFramesCount(0), m_inOutOfOrderFrames(0), m_inDroppedFrames(0), m_authmethod(IAXAuthMethod::MD5), m_expire(60), m_format(0), m_formatIn(0), m_formatOut(0), m_capability(0), m_trunkFrame(0) { XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) outgoing. [%p]", localCallNo(),remoteCallNo(),this); // Init data members if (!m_addr.port()) { XDebug(m_engine,DebugAll, "IAXTransaction::IAXTransaction(%u,%u). No remote port. Set to default. [%p]", localCallNo(),remoteCallNo(),this); m_addr.port(4569); } m_retransCount = engine->retransCount(); m_retransInterval = engine->retransInterval(); m_timeToNextPing = m_timeStamp + m_pingInterval; init(ieList); ieList.clear(); IAXControl::Type frametype; // 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); frametype = IAXControl::New; break; case RegReq: case RegRel: ieList.appendString(IAXInfoElement::USERNAME,m_username); ieList.appendNumeric(IAXInfoElement::REFRESH,m_expire,2); frametype = (type == RegReq ? IAXControl::RegReq : IAXControl::RegRel); break; case Poke: frametype = IAXControl::Poke; break; default: XDebug(m_engine,DebugAll,"IAXTransaction::IAXTransaction(%u,%u) outgoing. Unsupported type: %u. [%p]", localCallNo(),remoteCallNo(),m_type,this); 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()); changeState(NewLocalInvite); } IAXTransaction::~IAXTransaction() { #ifdef XDEBUG print(); #endif if (m_trunkFrame) m_trunkFrame->deref(); if (state() != Terminating && state() != Terminated) sendReject("Server shutdown"); XDebug(m_engine,DebugAll,"IAXTransaction::~IAXTransaction(%u,%u). [%p]", localCallNo(),remoteCallNo(),this); } 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) return tr; tr->deref(); return 0; } 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) return tr; tr->deref(); return 0; } IAXTransaction* IAXTransaction::processFrame(IAXFrame* frame) { if (!frame) return 0; if (state() == Terminated) { sendInval(); return 0; } if (state() == Terminating) // Local terminate: Accept only Ack. Remote terminate: Accept none. if (m_localReqEnd && frame->fullFrame()) { if (!(frame->type() == IAXFrame::IAX && frame->fullFrame()->subclass() == IAXControl::Ack)) return 0; } else 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->fullFrame()->subclass() == IAXControl::VNAK) return retransmitOnVNAK(frame->fullFrame()->iSeqNo()); // Do we have enough space to keep this frame ? if (m_inFrames.count() == m_maxInFrames) { Debug(DebugWarn,"Transaction(%u,%u). processFrame. Buffer overrun! (MAX=%u)",localCallNo(),remoteCallNo(),m_maxInFrames); m_inDroppedFrames++; return 0; } bool fAck = frame->type() == IAXFrame::IAX && frame->fullFrame()->subclass() == IAXControl::Ack; if (!fAck && !isFrameAcceptable(frame->fullFrame())) return 0; incrementSeqNo(frame->fullFrame(),true); // Voice full frame: process voice data & format if (frame->type() == IAXFrame::Voice && type() == New) { if (!processVoiceFrame(frame->fullFrame())) return 0; // Frame accepted: process voice data lock.drop(); return processMedia(frame->data(),frame->timeStamp(),true); } // Process incoming Ping if (frame->type() == IAXFrame::IAX && frame->fullFrame()->subclass() == IAXControl::Ping) { DDebug(m_engine,DebugAll,"Transaction(%u,%u) received Ping iseq=%u oseq=%u stamp=%u [%p]", localCallNo(),remoteCallNo(),frame->fullFrame()->iSeqNo(),frame->fullFrame()->oSeqNo(), frame->timeStamp(),this); postFrame(IAXFrame::IAX,IAXControl::Pong,0,0,frame->timeStamp(),true); return 0; } // Append frame to incoming frame list m_inFrames.append(frame); 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; } IAXTransaction* IAXTransaction::processMedia(DataBlock& data, u_int32_t tStamp, bool voice) { Lock lock(&m_mutexInMedia); if (!(voice || (tStamp & 0xffff0000))) { // Miniframe timestamp int16_t delta = (int16_t)(tStamp - m_lastMiniFrameIn); if (delta < 0) return 0; // add upper bits from last frame tStamp |= m_lastMiniFrameIn & 0xffff0000; // check if timestamp wrapped around by a miniframe, adjust if so if ((tStamp & 0xffff) < (m_lastMiniFrameIn & 0xffff)) { DDebug(m_engine,DebugAll,"Timestamp wraparound, ts=%u last=%u [%p]",tStamp & 0xffff,m_lastMiniFrameIn,this); tStamp += 0x10000; } } int32_t interval = (int32_t)tStamp - m_lastMiniFrameIn; if (interval <= 0) return 0; if (voice || interval < 32767) { m_lastMiniFrameIn = tStamp; // New frame is newer then the last one m_engine->processMedia(this,data,tStamp); } return 0; } IAXTransaction* IAXTransaction::sendMedia(const DataBlock& data, u_int32_t format) { if (!data.length()) return 0; u_int32_t ts = (u_int32_t)timeStamp(); // Avoid to send the same timestamp twice if ((u_int16_t)ts == m_lastMiniFrameOut) ts++; // Format changed or timestamp wrapped around? Send Voice full frame if ((u_int16_t)ts < m_lastMiniFrameOut || m_formatOut != format ) { if (m_formatOut != format) { DDebug(m_engine,DebugNote,"Outgoing format changed (New: %u, Old: %u). Send VOICE. [%p]", format,m_formatOut,this); m_formatOut = format; } else DDebug(m_engine,DebugNote,"Transaction(%u,%u) Time to send VOICE: ts=%u last=%u [%p]", localCallNo(),remoteCallNo(),ts,m_lastMiniFrameOut,this); m_lastMiniFrameOut = (u_int16_t)ts; postFrame(IAXFrame::Voice,m_formatOut,data.data(),data.length(),ts,true); return this; } // Send mini frame m_lastMiniFrameOut = (u_int16_t)ts; 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 = 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; // Waiting on remote cleanup ? if (state() == Terminating && !m_localReqEnd) return getEventTerminating(time); // Do we have a pending event ? if (m_pendingEvent) { ev = m_pendingEvent; m_pendingEvent = 0; return keepEvent(ev); } // Time to Ping remote peer ? if (time > m_timeToNextPing && state() != Terminating) { postFrame(IAXFrame::IAX,IAXControl::Ping,0,0,(u_int32_t)timeStamp(),false); m_timeToNextPing = time + m_pingInterval; } // Process outgoing frames ListIterator lout(m_outFrames); IAXFrameOut* lastFrameAck = 0; delFrame = false; for (; (obj = lout.get());) { IAXFrameOut* frame = static_cast(obj); ev = getEventResponse(frame,delFrame); // Frame received ACK or other response ? if (frame->ack() || delFrame) { frame->setAck(); lastFrameAck = frame; // Frame received non ACK response if (ev || delFrame) break; if (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); // No response. Timeout ? if (frame->timeout()) { if (m_state == Terminating) // Client already notified: Terminate transaction ev = terminate(IAXEvent::Timeout,true); else // Client not notified: Notify it and terminate transaction 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 { 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(obj); if (frame == lastFrameAck) { if (ev || delFrame || frame->ackOnly()) { 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());) { IAXFullFrame* frame = static_cast(obj); // If frame is ACK, ignore it if (frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::Ack) continue; DDebug(m_engine,DebugAll,"Transaction(%u,%u) dequeued Frame(%u,%u) iseq=%u oseq=%u stamp=%u [%p]", localCallNo(),remoteCallNo(),frame->type(),frame->subclass(),frame->iSeqNo(),frame->oSeqNo(),frame->timeStamp(),this); if (m_state == IAXTransaction::Unknown) ev = getEventStartTrans(frame,delFrame); // New transaction else ev = getEventRequest(frame,delFrame); if (delFrame) m_inFrames.remove(frame,true); // frame is no longer needded if (ev) return keepEvent(ev); } // No pending outgoing frames. No valid requests. Clear incoming frames queue. //m_inDroppedFrames += m_inFrames.count(); m_inFrames.clear(); return 0; } bool IAXTransaction::sendAccept() { Lock lock(this); if (!((type() == New && (state() == NewRemoteInvite || state() == NewRemoteInvite_RepRecv)) || (type() == RegReq && state() == NewRemoteInvite) || ((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); 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); changeState(Terminating); m_localReqEnd = true; } return true; } 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); changeState(Terminating); m_localReqEnd = true; Debug(m_engine,DebugAll,"Transaction(%u,%u). Hangup call. Cause: '%s'",localCallNo(),remoteCallNo(),cause); return true; } 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; IAXControl::Type frametype; switch (type()) { case New: frametype = IAXControl::Reject; break; case RegReq: case RegRel: frametype = IAXControl::RegRej; break; case Poke: 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); Debug(m_engine,DebugAll,"Transaction(%u,%u). Reject. Cause: '%s'",localCallNo(),remoteCallNo(),cause); changeState(Terminating); m_localReqEnd = true; return true; } bool IAXTransaction::sendAuth() { Lock lock(this); if (!((type() == New || type() == RegReq || type() == RegRel) && state() == NewRemoteInvite)) return false; switch (m_authmethod) { case IAXAuthMethod::MD5: m_challenge = (int)random(); break; case IAXAuthMethod::RSA: case IAXAuthMethod::Text: 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); switch (type()) { case New: postFrame(IAXFrame::IAX,IAXControl::AuthReq,data.data(),data.length(),0,false); break; case RegReq: case RegRel: postFrame(IAXFrame::IAX,IAXControl::RegAuth,data.data(),data.length(),0,false); break; default: ; } changeState(NewRemoteInvite_AuthSent); return true; } bool IAXTransaction::sendAuthReply(const String& response) { Lock lock(this); if (state() != NewLocalInvite_AuthRecv) return false; m_authdata = response; IAXIEList ieList; IAXControl::Type subclass; switch (type()) { case New: subclass = IAXControl::AuthRep; break; case RegReq: subclass = IAXControl::RegReq; ieList.appendString(IAXInfoElement::USERNAME,m_username); break; case RegRel: subclass = IAXControl::RegRel; ieList.appendString(IAXInfoElement::USERNAME,m_username); break; default: return false; } if (m_authmethod != IAXAuthMethod::MD5) return false; ieList.appendString(IAXInfoElement::MD5_RESULT,response); DataBlock data; ieList.toBuffer(data); postFrame(IAXFrame::IAX,subclass,data.data(),data.length(),0,false); changeState(NewLocalInvite_RepSent); return true; } bool IAXTransaction::sendText(const char* text) { Lock lock(this); if (state() != Connected) return false; String s(text); postFrame(IAXFrame::Text,0,(void*)s.c_str(),s.length(),0,true); return true; } unsigned char IAXTransaction::getMaxFrameList() { return m_maxInFrames; } bool IAXTransaction::setMaxFrameList(unsigned char value) { if (value < IAX2_MAX_TRANSINFRAMELIST) { m_maxInFrames = value; return true; } m_maxInFrames = IAX2_MAX_TRANSINFRAMELIST; return false; } bool IAXTransaction::abortReg() { if (!(type() == RegReq || type() == RegRel) || state() == Terminating || state() == Terminated) return false; m_userdata = 0; sendReject(); 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() { static SocketAddr addr; ObjList* l; String frames; frames << "\r\nOutgoing frames: " << m_outFrames.count(); for(l = m_outFrames.skipNull(); l; l = l->skipNext()) { IAXFrameOut* frame = static_cast(l->get()); frame->toString(frames,addr,remoteAddr(),false); } frames << "\r\nIncoming frames: " << m_inFrames.count(); for(l = m_inFrames.skipNull(); l; l = l->skipNext()) { IAXFrameOut* frame = static_cast(l->get()); frame->toString(frames,addr,remoteAddr(),true); } Debug(m_engine,DebugInfo,"Transaction(%u,%u). Remote address: %s:%u. Type: %u. State: %u. Timestamp: " FMT64U " [%p]%s", localCallNo(),remoteCallNo(),remoteAddr().host().c_str(),remoteAddr().port(), type(),state(),(u_int64_t)timeStamp(),this,frames.c_str()); } void IAXTransaction::init(IAXIEList& ieList) { switch (type()) { case New: ieList.getString(IAXInfoElement::USERNAME,m_username); ieList.getString(IAXInfoElement::CALLING_NUMBER,m_callingNo); ieList.getString(IAXInfoElement::CALLING_NAME,m_callingName); ieList.getString(IAXInfoElement::CALLED_NUMBER,m_calledNo); ieList.getString(IAXInfoElement::CALLED_CONTEXT,m_calledContext); ieList.getNumeric(IAXInfoElement::FORMAT,m_format); ieList.getNumeric(IAXInfoElement::CAPABILITY,m_capability); if (outgoing()) m_formatOut = m_format; else m_formatIn = m_format; break; case RegReq: case RegRel: ieList.getString(IAXInfoElement::USERNAME,m_username); ieList.getNumeric(IAXInfoElement::REFRESH,m_expire); break; case Poke: default: ; } } bool IAXTransaction::incrementSeqNo(const IAXFullFrame* frame, bool inbound) { if (frame->type() == IAXFrame::IAX) switch (frame->subclass()) { case IAXControl::Ack: case IAXControl::VNAK: case IAXControl::TxAcc: case IAXControl::TxCnt: case IAXControl::Inval: return false; default: ; } if (inbound) m_iSeqNo++; else m_oSeqNo++; XDebug(m_engine,DebugAll,"Transaction(%u,%u). Incremented %s=%u for Frame(%u,%u) iseq=%u oseq=%u [%p]", localCallNo(),remoteCallNo(),inbound ? "iseq" : "oseq", inbound ? m_iSeqNo : m_oSeqNo, frame->type(),frame->subclass(),frame->iSeqNo(),frame->oSeqNo(),this); return true; } bool IAXTransaction::isFrameAcceptable(const IAXFullFrame* frame) { int64_t delta = frame->oSeqNo() - m_iSeqNo; if (!delta) return true; if (delta > 0) { // We missed some frames before this one: Send VNAK 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,"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; } bool IAXTransaction::changeState(State newState) { if (state() == newState) return true; switch (state()) { case Terminated: return false; case Terminating: if (newState == Terminated) break; return false; default: ; } XDebug(m_engine,DebugInfo,"Transaction(%u,%u). State change: %u --> %u [%p]",localCallNo(),remoteCallNo(),m_state,newState,this); m_state = newState; return true; } IAXEvent* IAXTransaction::terminate(u_int8_t evType, bool local, const IAXFullFrame* frame, bool createIEList) { IAXEvent* ev; if (createIEList) ev = new IAXEvent((IAXEvent::Type)evType,local,true,this,frame); else if (frame) ev = new IAXEvent((IAXEvent::Type)evType,local,true,this,frame->type(),frame->subclass()); else ev = new IAXEvent((IAXEvent::Type)evType,local,true,this,0,0); Debug(m_engine,DebugAll,"Transaction(%u,%u). Terminated. Event: %u, Frame(%u,%u)", localCallNo(),remoteCallNo(),evType,ev->frameType(),ev->subclass()); changeState(Terminated); deref(); return ev; } IAXEvent* IAXTransaction::waitForTerminate(u_int8_t evType, bool local, const 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)", localCallNo(),remoteCallNo(),evType,ev->frameType(),ev->subclass()); changeState(Terminating); m_timeout = (m_engine->transactionTimeout() + Time::secNow()) * 1000; return ev; } void IAXTransaction::postFrame(IAXFrame::Type type, u_int32_t subclass, void* data, u_int16_t len, u_int32_t tStamp, bool ackOnly) { 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; } 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); } bool IAXTransaction::sendFrame(IAXFrameOut* frame, bool vnak) { if (!frame) return false; bool b = m_engine->writeSocket(frame->data().data(),frame->data().length(),remoteAddr(),frame); // Don't modify timeout if transmitted as a response to a VNAK if (!vnak) { if (frame->retrans()) // Retransmission frame->transmitted(); else // First transmission frame->setRetrans(); } return b; } IAXEvent* IAXTransaction::createEvent(u_int8_t evType, bool local, const IAXFullFrame* frame, State newState) { IAXEvent* ev; changeState(newState); switch (m_state) { case Terminating: ev = waitForTerminate((IAXEvent::Type)evType,local,frame); break; case Terminated: ev = terminate((IAXEvent::Type)evType,local,frame); break; default: ev = new IAXEvent((IAXEvent::Type)evType,local,false,this,frame); } if (ev && ev->getList().invalidIEList()) { sendInval(); delete ev; ev = waitForTerminate(IAXEvent::Invalid,local,frame); } return ev; } IAXEvent* IAXTransaction::createResponse(IAXFrameOut* frame, u_int8_t findType, u_int8_t findSubclass, u_int8_t evType, bool local, State newState) { IAXFullFrame* ffind = findInFrame((IAXFrame::Type)findType,findSubclass); if (ffind) { frame->setAck(); IAXEvent* ev = createEvent(evType,local,ffind,newState); m_inFrames.remove(ffind,true); return ev; } return 0; } IAXEvent* IAXTransaction::getEventResponse(IAXFrameOut* frame, bool& delFrame) { delFrame = false; if (findInFrameAck(frame)) { frame->setAck(); // Terminating frame sent if (m_state == Terminating) return terminate(IAXEvent::Terminated,true); // Frame only need ACK if (frame->ackOnly()) return 0; } // Frame only need ACK. Didn't found it. Return if (frame->ackOnly()) return 0; delFrame = true; switch (type()) { case New: return getEventResponse_New(frame,delFrame); case RegReq: case RegRel: return getEventResponse_Reg(frame,delFrame); case Poke: IAXEvent* event; if (m_state == NewLocalInvite && frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::Poke && 0 != (event = createResponse(frame,IAXFrame::IAX,IAXControl::Pong,IAXEvent::Terminated,false,Terminating))) { return event; } break; default: ; } delFrame = false; // Internal stuff return processInternalOutgoingRequest(frame,delFrame); } IAXEvent* IAXTransaction::getEventResponse_New(IAXFrameOut* frame, bool& delFrame) { IAXEvent* ev; delFrame = true; switch (m_state) { case Connected: break; case NewLocalInvite: if (!(frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::New)) break; // Frame is NEW: AUTHREQ, ACCEPT, REJECT, HANGUP ? if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::AuthReq,IAXEvent::AuthReq,false,NewLocalInvite_AuthRecv))) return processAuthReq(ev); if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::Accept,IAXEvent::Accept,false,Connected))) return processAccept(ev); if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::Reject,IAXEvent::Reject,false,Terminating))) return ev; if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::Hangup,IAXEvent::Hangup,false,Terminating))) return ev; break; case NewLocalInvite_RepSent: if (!(frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::AuthRep)) break; // Frame is AUTHREP: ACCEPT, REJECT, HANGUP ? if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::Accept,IAXEvent::Accept,false,Connected))) return processAccept(ev); if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::Reject,IAXEvent::Reject,false,Terminating))) return ev; if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::Hangup,IAXEvent::Hangup,false,Terminating))) return ev; break; case NewRemoteInvite_AuthSent: if (!(frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::AuthReq)) break; // Frame is AUTHREQ: AUTHREP, REJECT, HANGUP ? if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::AuthRep, IAXEvent::AuthRep,false,NewRemoteInvite_RepRecv))) return processAuthRep(ev); if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::Reject,IAXEvent::Reject,false,Terminating))) return ev; if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::Hangup,IAXEvent::Hangup,false,Terminating))) return ev; break; default: ; } delFrame = false; // Internal stuff return processInternalOutgoingRequest(frame,delFrame); } IAXEvent* IAXTransaction::processAuthReq(IAXEvent* event) { if (event->type() != IAXEvent::AuthReq) return event; Debug(m_engine,DebugAll,"Transaction(%u,%u). AuthReq received",localCallNo(),remoteCallNo()); // Valid authmethod & challenge ? u_int32_t authmethod; bool bAuthMethod = event->getList().getNumeric(IAXInfoElement::AUTHMETHODS,authmethod) && (authmethod & m_authmethod); bool bChallenge = event->getList().getString(IAXInfoElement::CHALLENGE,m_challenge); if (bAuthMethod && bChallenge) return event; delete event; return internalReject(s_iax_modNoAuthMethod); } IAXEvent* IAXTransaction::processAccept(IAXEvent* event) { if (event->type() != IAXEvent::Accept) return event; Debug(m_engine,DebugAll,"Transaction(%u,%u). Accept received",localCallNo(),remoteCallNo()); // We might have a format received with a Voice frame if (m_formatIn) return event; m_format = 0; event->getList().getNumeric(IAXInfoElement::FORMAT,m_format); if (m_engine->acceptFormatAndCapability(this)) return event; delete event; return internalReject(s_iax_modNoMediaFormat); } IAXEvent* IAXTransaction::processAuthRep(IAXEvent* event) { if (event->type() != IAXEvent::AuthRep) return event; Debug(m_engine,DebugAll,"Transaction(%u,%u). Auth Reply received",localCallNo(),remoteCallNo()); event->getList().getString(IAXInfoElement::MD5_RESULT,m_authdata); return event; } IAXEvent* IAXTransaction::getEventResponse_Reg(IAXFrameOut* frame, bool& delFrame) { IAXEvent* ev; delFrame = true; switch (m_state) { case NewLocalInvite: if (!(frame->type() == IAXFrame::IAX && (frame->subclass() == IAXControl::RegReq || frame->subclass() == IAXControl::RegRel))) break; // Frame is REGREQ ? Find REGACK. Else: Find REGAUTH if (frame->subclass() == IAXControl::RegReq) if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::RegAck,IAXEvent::Accept,false,Terminating))) return processRegAck(ev); if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::RegAuth,IAXEvent::AuthReq,false,NewLocalInvite_AuthRecv))) return processAuthReq(ev); // REGREJ ? if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::RegRej,IAXEvent::Reject,false,Terminating))) return ev; break; case NewLocalInvite_RepSent: if (!(frame->type() == IAXFrame::IAX && (frame->subclass() == IAXControl::RegReq || frame->subclass() == IAXControl::RegRel))) break; // Frame is REGREQ/REGREL. Find REGACK, REGREJ if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::RegAck,IAXEvent::Accept,false,Terminating))) return processRegAck(ev); if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::RegRej,IAXEvent::Reject,false,Terminating))) return ev; break; case NewRemoteInvite_AuthSent: if (!(frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::RegAuth)) break; // Frame is REGAUTH. Find REGREQ/REGREL, REGREJ if (type() == RegReq) { if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::RegReq,IAXEvent::AuthRep,false,NewRemoteInvite_RepRecv))) return processAuthRep(ev); } else { if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::RegRel,IAXEvent::AuthRep,false,NewRemoteInvite_RepRecv))) return processAuthRep(ev); } if (0 != (ev = createResponse(frame,IAXFrame::IAX,IAXControl::RegRej,IAXEvent::Reject,false,Terminating))) return ev; break; default: ; } delFrame = false; return processInternalOutgoingRequest(frame,delFrame); } IAXEvent* IAXTransaction::processRegAck(IAXEvent* event) { event->getList().getNumeric(IAXInfoElement::REFRESH,m_expire); event->getList().getString(IAXInfoElement::CALLING_NAME,m_callingName); event->getList().getString(IAXInfoElement::CALLING_NUMBER,m_callingNo); return event; } IAXEvent* IAXTransaction::getEventStartTrans(IAXFullFrame* frame, bool& delFrame) { IAXEvent* ev; delFrame = true; switch (type()) { case New: if (!(frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::New)) break; ev = createEvent(IAXEvent::New,false,frame,NewRemoteInvite); if (ev) { // Check version if (!ev->getList().validVersion()) { delete ev; sendReject("Unsupported or missing protocol version"); return 0; } init(ev->getList()); // Check username if (!m_username) return internalReject(s_iax_modNoUsername); } return ev; case RegReq: case RegRel: if (!(frame->type() == IAXFrame::IAX && (frame->subclass() == IAXControl::RegReq || frame->subclass() == IAXControl::RegRel))) break; ev = createEvent(IAXEvent::New,false,frame,NewRemoteInvite); init(ev->getList()); // Check username if (!m_username) return internalReject(s_iax_modNoUsername); return ev; case Poke: if (!(frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::Poke)) break; // Send PONG postFrame(IAXFrame::IAX,IAXControl::Pong,0,0,frame->timeStamp(),true); return createEvent(IAXEvent::Terminated,false,0,Terminating); default: ; } delFrame = false; return 0; } IAXEvent* IAXTransaction::getEventRequest(IAXFullFrame* frame, bool& delFrame) { IAXEvent* ev; delFrame = true; // INVAL ? if (frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::Inval) { Debug(m_engine,DebugAll,"IAXTransaction(%u,%u). Received INVAL. Terminate [%p]", localCallNo(),remoteCallNo(),this); return createEvent(IAXEvent::Invalid,false,frame,Terminated); } switch (type()) { case New: return getEventRequest_New(frame,delFrame); case RegReq: case RegRel: switch (m_state) { case NewLocalInvite_AuthRecv: case NewRemoteInvite: case NewRemoteInvite_RepRecv: if (0 != (ev = remoteRejectCall(frame,delFrame))) return ev; break; default: ; } break; default: ; } delFrame = false; return processInternalIncomingRequest(frame,delFrame); } IAXEvent* IAXTransaction::getEventRequest_New(IAXFullFrame* frame, bool& delFrame) { IAXEvent* ev; delFrame = true; switch (m_state) { case Connected: switch (frame->type()) { case IAXFrame::Control: return processMidCallControl(frame,delFrame); case IAXFrame::IAX: return processMidCallIAXControl(frame,delFrame); case IAXFrame::DTMF: return createEvent(IAXEvent::Dtmf,false,frame,m_state); case IAXFrame::Text: return createEvent(IAXEvent::Text,false,frame,m_state); case IAXFrame::Noise: return createEvent(IAXEvent::Noise,false,frame,m_state); // NOT IMPLEMENTED case IAXFrame::Video: case IAXFrame::Image: case IAXFrame::HTML: return createEvent(IAXEvent::NotImplemented,false,frame,m_state); default: ; } break; case NewLocalInvite_AuthRecv: case NewRemoteInvite: case NewRemoteInvite_RepRecv: if (0 != (ev = remoteRejectCall(frame,delFrame))) return ev; break; default: ; } delFrame = false; return processInternalIncomingRequest(frame,delFrame); } IAXFullFrame* IAXTransaction::findInFrame(IAXFrame::Type type, u_int32_t subclass) { for (ObjList* l = m_inFrames.skipNull(); l; l = l->next()) { IAXFullFrame* frame = static_cast(l->get()); if (frame && frame->type() == type && frame->subclass() == subclass) return frame; } return 0; } bool IAXTransaction::findInFrameTimestamp(const IAXFullFrame* frameOut, IAXFrame::Type type, u_int32_t subclass) { IAXFullFrame* frame = 0; // Loose timestamp check for Ping/Pong // Received timestamp can be greater then the sent one bool looseTimestamp = type == IAXFrame::IAX && subclass == IAXControl::Pong; for (ObjList* l = m_inFrames.skipNull(); l; l = l->skipNext()) { frame = static_cast(l->get()); if (frame->type() == type && frame->subclass() == subclass) { bool match = looseTimestamp ? frame->timeStamp() >= frameOut->timeStamp() : frame->timeStamp() == frameOut->timeStamp(); if (match) break; } } if (frame) { m_inFrames.remove(frame,true); return true; } return false; } bool IAXTransaction::findInFrameAck(const IAXFullFrame* frameOut) { IAXFullFrame* frame = 0; for (ObjList* l = m_inFrames.skipNull(); l; l = l->next()) { frame = static_cast(l->get()); if (frame && frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::Ack && frame->timeStamp() == frameOut->timeStamp() && frame->oSeqNo() == frameOut->iSeqNo()) break; frame = 0; } if (frame) { m_inFrames.remove(frame,true); return true; } return false; } void IAXTransaction::ackInFrames() { IAXFullFrame* ack = 0; for (ObjList* l = m_inFrames.skipNull(); l; l = l->next()) { IAXFullFrame* frame = static_cast(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) return false; postFrame(frametype,subclass,0,0,0,true); return true; } void IAXTransaction::sendAck(const IAXFullFrame* frame) { if (!frame) return; IAXFullFrame* f = new IAXFullFrame(IAXFrame::IAX,IAXControl::Ack,localCallNo(), remoteCallNo(),frame->iSeqNo(),m_iSeqNo,frame->timeStamp()); 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(f->data().data(),f->data().length(),remoteAddr(),f); f->deref(); } void IAXTransaction::sendInval() { IAXFullFrame* f = new IAXFullFrame(IAXFrame::IAX,IAXControl::Inval,localCallNo(), remoteCallNo(),m_oSeqNo++,m_iSeqNo,(u_int32_t)timeStamp()); m_engine->writeSocket(f->data().data(),f->data().length(),remoteAddr(),f); f->deref(); } void IAXTransaction::sendVNAK() { IAXFullFrame* f = new IAXFullFrame(IAXFrame::IAX,IAXControl::VNAK,localCallNo(), remoteCallNo(),m_oSeqNo,m_iSeqNo,(u_int32_t)timeStamp()); m_engine->writeSocket(f->data().data(),f->data().length(),remoteAddr(),f); f->deref(); } 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); } IAXEvent* IAXTransaction::processInternalOutgoingRequest(IAXFrameOut* frame, bool& delFrame) { delFrame = false; if (frame->type() != IAXFrame::IAX) return 0; delFrame = true; switch (frame->subclass()) { case IAXControl::Ping: if (findInFrameTimestamp(frame,IAXFrame::IAX,IAXControl::Pong)) return 0; break; case IAXControl::LagRq: if (findInFrameTimestamp(frame,IAXFrame::IAX,IAXControl::LagRp)) return 0; break; default: ; } delFrame = false; return 0; } IAXEvent* IAXTransaction::processInternalIncomingRequest(const IAXFullFrame* frame, bool& delFrame) { delFrame = false; if (frame->type() != IAXFrame::IAX) return 0; if (frame->subclass() == IAXControl::LagRq) { postFrame(IAXFrame::IAX,IAXControl::LagRp,0,0,frame->timeStamp(),true); delFrame = true; } return 0; } IAXEvent* IAXTransaction::processMidCallControl(const IAXFullFrame* frame, bool& delFrame) { delFrame = true; switch (frame->subclass()) { case IAXFullFrame::Hangup: return createEvent(IAXEvent::Hangup,false,frame,Terminating); case IAXFullFrame::Busy: return createEvent(IAXEvent::Busy,false,frame,Terminating); case IAXFullFrame::Ringing: return createEvent(IAXEvent::Ringing,false,frame,m_state); case IAXFullFrame::Answer: return createEvent(IAXEvent::Answer,false,frame,Connected); case IAXFullFrame::Progressing: case IAXFullFrame::Proceeding: return createEvent(IAXEvent::Progressing,false,frame,m_state); case IAXFullFrame::Hold: case IAXFullFrame::Unhold: case IAXFullFrame::Congestion: case IAXFullFrame::FlashHook: case IAXFullFrame::Option: case IAXFullFrame::KeyRadio: case IAXFullFrame::UnkeyRadio: case IAXFullFrame::VidUpdate: return createEvent(IAXEvent::NotImplemented,false,frame,m_state); default: ; } delFrame = false; return 0; } IAXEvent* IAXTransaction::processMidCallIAXControl(const IAXFullFrame* frame, bool& delFrame) { delFrame = true; switch (frame->subclass()) { case IAXControl::Ping: case IAXControl::LagRq: case IAXControl::Pong: case IAXControl::LagRp: case IAXControl::VNAK: return processInternalIncomingRequest(frame,delFrame); case IAXControl::Quelch: return createEvent(IAXEvent::Quelch,false,frame,m_state); case IAXControl::Unquelch: return createEvent(IAXEvent::Unquelch,false,frame,m_state); case IAXControl::Hangup: case IAXControl::Reject: return createEvent(IAXEvent::Hangup,false,frame,Terminating); case IAXControl::New: case IAXControl::Accept: case IAXControl::AuthReq: case IAXControl::AuthRep: // Already received: Ignore return 0; case IAXControl::Inval: return createEvent(IAXEvent::Invalid,false,frame,Terminated); case IAXControl::Unsupport: return 0; case IAXControl::Transfer: case IAXControl::TxReady: sendUnsupport(frame->subclass()); return createEvent(IAXEvent::NotImplemented,false,frame,Terminating); case IAXControl::DpReq: case IAXControl::DpRep: case IAXControl::Dial: case IAXControl::TxReq: case IAXControl::TxCnt: case IAXControl::TxAcc: case IAXControl::TxRel: case IAXControl::TxRej: case IAXControl::MWI: case IAXControl::Provision: case IAXControl::FwData: sendUnsupport(frame->subclass()); return createEvent(IAXEvent::NotImplemented,false,frame,state()); default: ; } delFrame = false; return 0; } IAXEvent* IAXTransaction::remoteRejectCall(const IAXFullFrame* frame, bool& delFrame) { delFrame = true; switch (type()) { case New: if ((frame->type() == IAXFrame::IAX && (frame->subclass() == IAXControl::Hangup || frame->subclass() == IAXControl::Reject)) || (frame->type() == IAXFrame::Control && frame->subclass() == IAXFullFrame::Hangup)) return createEvent(IAXEvent::Reject,false,frame,Terminating); break; case RegReq: case RegRel: if (frame->type() == IAXFrame::IAX && frame->subclass() == IAXControl::RegRej) return createEvent(IAXEvent::Reject,false,frame,Terminating); break; default: ; } delFrame = false; return 0; } IAXEvent* IAXTransaction::getEventTerminating(u_int64_t time) { if (time > m_timeout) { Debug(m_engine,DebugAll,"Transaction(%u,%u) - Cleanup on remote request. Timestamp: " FMT64U, localCallNo(),remoteCallNo(),timeStamp()); return terminate(IAXEvent::Timeout,false); } 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->iSeqNo(),frame->oSeqNo(),frame->timeStamp(),this); sendAck(frame); // We might have an incoming media format received with an Accept 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(); 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; } } } else { m_format = frame->subclass(); if (!m_engine->acceptFormatAndCapability(this)) { m_pendingEvent = internalReject(s_iax_modNoMediaFormat); return 0; } } return this; } IAXTransaction* IAXTransaction::retransmitOnVNAK(u_int16_t seqNo) { int c = 0; for (ObjList* l = m_outFrames.skipNull(); l; l = l->next()) { IAXFrameOut* frame = static_cast(l->get()); if (frame && frame->oSeqNo() >= seqNo) { sendFrame(frame,true); c++; } } DDebug(m_engine,DebugNote,"Transaction(%u,%u). Retransmitted %d frames on VNAK(%u)", localCallNo(),remoteCallNo(),c,seqNo); return 0; } IAXEvent* IAXTransaction::internalAccept() { Debug(m_engine,DebugAll,"Transaction(%u,%u). Internal accept",localCallNo(),remoteCallNo()); sendAccept(); return new IAXEvent(IAXEvent::Accept,true,true,this,IAXFrame::IAX,IAXControl::Accept); } IAXEvent* IAXTransaction::internalReject(String& reason) { Debug(m_engine,DebugAll,"Transaction(%u,%u). Internal reject: '%s'",localCallNo(),remoteCallNo(),reason.c_str()); sendReject(reason); IAXEvent* event = new IAXEvent(IAXEvent::Reject,true,true,this,IAXFrame::IAX,IAXControl::Reject); event->getList().appendString(IAXInfoElement::CAUSE,reason); m_localReqEnd = true; return event; } void IAXTransaction::eventTerminated(IAXEvent* event) { Lock lock(this); if (event && event == m_currentEvent) { XDebug(m_engine,DebugAll,"Transaction(%u,%u). Event (%p) terminated. [%p]", localCallNo(),remoteCallNo(),event,this); m_currentEvent = 0; } } /* vi: set ts=8 sw=4 sts=4 noet: */