/** * frame.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 // For memcpy() #include // For sprintf() using namespace TelEngine; inline void setStringFromInteger(String& dest, u_int32_t value, u_int8_t length) { char tmp[11]; switch (length) { case 1: sprintf(tmp,"%0#4x",(u_int8_t)value); break; case 2: sprintf(tmp,"%0#6x",(u_int16_t)value); break; default: sprintf(tmp,"%0#10x",value); break; } dest = tmp; } /* * IAXInfoElement */ TokenDict IAXInfoElement::s_ieData[] = { {"CALLED_NUMBER", CALLED_NUMBER}, {"CALLING_NUMBER", CALLING_NUMBER}, {"CALLING_ANI", CALLING_ANI}, {"CALLING_NAME", CALLING_NAME}, {"CALLED_CONTEXT", CALLED_CONTEXT}, {"USERNAME", USERNAME}, {"PASSWORD", PASSWORD}, {"CAPABILITY", CAPABILITY}, {"FORMAT", FORMAT}, {"LANGUAGE", LANGUAGE}, {"VERSION", VERSION}, {"ADSICPE", ADSICPE}, {"DNID", DNID}, {"AUTHMETHODS", AUTHMETHODS}, {"CHALLENGE", CHALLENGE}, {"MD5_RESULT", MD5_RESULT}, {"RSA_RESULT", RSA_RESULT}, {"APPARENT_ADDR", APPARENT_ADDR}, {"REFRESH", REFRESH}, {"DPSTATUS", DPSTATUS}, {"CALLNO", CALLNO}, {"CAUSE", CAUSE}, {"IAX_UNKNOWN", IAX_UNKNOWN}, {"MSGCOUNT", MSGCOUNT}, {"AUTOANSWER", AUTOANSWER}, {"MUSICONHOLD", MUSICONHOLD}, {"TRANSFERID", TRANSFERID}, {"RDNIS", RDNIS}, {"PROVISIONING", PROVISIONING}, {"AESPROVISIONING", AESPROVISIONING}, {"DATETIME", DATETIME}, {"DEVICETYPE", DEVICETYPE}, {"SERVICEIDENT", SERVICEIDENT}, {"FIRMWAREVER", FIRMWAREVER}, {"FWBLOCKDESC", FWBLOCKDESC}, {"FWBLOCKDATA", FWBLOCKDATA}, {"PROVVER", PROVVER}, {"CALLINGPRES", CALLINGPRES}, {"CALLINGTON", CALLINGTON}, {"CALLINGTNS", CALLINGTNS}, {"SAMPLINGRATE", SAMPLINGRATE}, {"CAUSECODE", CAUSECODE}, {"ENCRYPTION", ENCRYPTION}, {"ENKEY", ENKEY}, {"CODEC_PREFS", CODEC_PREFS}, {"RR_JITTER", RR_JITTER}, {"RR_LOSS", RR_LOSS}, {"RR_PKTS", RR_PKTS}, {"RR_DELAY", RR_DELAY}, {"RR_DROPPED", RR_DROPPED}, {"RR_OOO", RR_OOO}, {0,0} }; void IAXInfoElement::toBuffer(DataBlock& buf) { unsigned char d[2] = {m_type,0}; buf.assign(d,sizeof(d)); } void IAXInfoElement::toString(String& buf) { buf << ""; } /* * IAXInfoElementString */ void IAXInfoElementString::toBuffer(DataBlock& buf) { unsigned char d[2] = {type(),m_strData.length()}; buf.assign(d,sizeof(d)); buf.append(data()); } /* * IAXInfoElementNumeric */ IAXInfoElementNumeric::IAXInfoElementNumeric(Type type, u_int32_t val, u_int8_t len) : IAXInfoElement(type), m_length(len) { switch (m_length) { case 4: m_numericData = (u_int32_t)val; break; case 2: m_numericData = (u_int16_t)val; break; case 1: m_numericData = (u_int8_t)val; break; } } void IAXInfoElementNumeric::toBuffer(DataBlock& buf) { unsigned char d[6] = {type(),m_length}; switch (m_length) { case 1: d[2] = (unsigned char)m_numericData; break; case 2: d[2] = (unsigned char)(m_numericData >> 8); d[3] = (unsigned char)m_numericData; break; case 4: d[2] = (unsigned char)(m_numericData >> 24); d[3] = (unsigned char)(m_numericData >> 16); d[4] = (unsigned char)(m_numericData >> 8); d[5] = (unsigned char)m_numericData; break; } buf.assign(d,2 + m_length); } void IAXInfoElementNumeric::toString(String& buf) { String tmp; setStringFromInteger(tmp,m_numericData,m_length); buf << tmp; } /* * IAXInfoElementBinary */ void IAXInfoElementBinary::toBuffer(DataBlock& buf) { unsigned char d[2] = {type(),m_data.length()}; buf.assign(d,sizeof(d)); buf += m_data; } void IAXInfoElementBinary::toString(String& buf) { buf << "Binary data"; } IAXInfoElementBinary* IAXInfoElementBinary::packIP(const SocketAddr& addr) { return new IAXInfoElementBinary(IAXInfoElement::APPARENT_ADDR,(unsigned char*)(addr.address()),addr.length()); } bool IAXInfoElementBinary::unpackIP(SocketAddr& addr, IAXInfoElementBinary* ie) { addr.clear(); if (!ie) return false; addr.assign((struct sockaddr*)(ie->data().data()),ie->data().length()); return true; } /* * IAXIEList */ void IAXIEList::insertVersion() { if (!getIE(IAXInfoElement::VERSION)) m_list.insert(new IAXInfoElementNumeric(IAXInfoElement::VERSION,IAX_PROTOCOL_VERSION,2)); } bool IAXIEList::createFromFrame(const IAXFullFrame* frame, bool incoming) { m_invalidIEList = false; m_list.clear(); if (!frame) return true; if (frame->type() == IAXFrame::Voice) return true; unsigned char* data = (unsigned char*)(((IAXFullFrame*)frame)->data().data()); unsigned int len = ((IAXFullFrame*)frame)->data().length(); // Skip header for outgoing frames if (!incoming) { data += 12; len -= 12; } u_int16_t i; // Current index of IE buffer u_int32_t value; if (frame->type() == IAXFrame::Text) { // Create even if text is empty appendString(IAXInfoElement::textframe,data,len); return true; } if (len < 2) { m_invalidIEList = len ? true : false; return !m_invalidIEList; } for (i = 1; i < len;) { if (i + (unsigned int)data[i] >= len) { i = 0xFFFF; break; } switch (data[i-1]) { // Text case IAXInfoElement::CALLED_NUMBER: case IAXInfoElement::CALLING_NUMBER: case IAXInfoElement::CALLING_ANI: case IAXInfoElement::CALLING_NAME: case IAXInfoElement::CALLED_CONTEXT: case IAXInfoElement::USERNAME: case IAXInfoElement::PASSWORD: case IAXInfoElement::LANGUAGE: case IAXInfoElement::DNID: case IAXInfoElement::CHALLENGE: case IAXInfoElement::MD5_RESULT: case IAXInfoElement::RSA_RESULT: case IAXInfoElement::CAUSE: case IAXInfoElement::MUSICONHOLD: // Optional case IAXInfoElement::RDNIS: case IAXInfoElement::DEVICETYPE: if (data[i]) appendString((IAXInfoElement::Type)data[i-1],data+i+1,data[i]); else appendString((IAXInfoElement::Type)data[i-1],0,0); i += data[i] + 1; break; // Binary case IAXInfoElement::CODEC_PREFS: // LIST of strings if (data[i]) appendBinary((IAXInfoElement::Type)data[i-1],data+i+1,data[i]); else appendBinary((IAXInfoElement::Type)data[i-1],0,0); i += data[i] + 1; break; case IAXInfoElement::APPARENT_ADDR: case IAXInfoElement::PROVISIONING: case IAXInfoElement::AESPROVISIONING: case IAXInfoElement::SERVICEIDENT: // Length must be 6 case IAXInfoElement::FWBLOCKDATA: // Length can be 0 case IAXInfoElement::ENKEY: if (data[i-1] == IAXInfoElement::SERVICEIDENT && data[i] != 6) { i = 0xFFFF; break; } appendBinary((IAXInfoElement::Type)data[i-1],data+i+1,data[i]); i += data[i] + 1; break; // 4 bytes case IAXInfoElement::CAPABILITY: case IAXInfoElement::FORMAT: case IAXInfoElement::TRANSFERID: case IAXInfoElement::DATETIME: case IAXInfoElement::PROVVER: case IAXInfoElement::FWBLOCKDESC: case IAXInfoElement::SAMPLINGRATE: case IAXInfoElement::RR_JITTER: case IAXInfoElement::RR_LOSS: case IAXInfoElement::RR_PKTS: case IAXInfoElement::RR_DROPPED: case IAXInfoElement::RR_OOO: if (data[i] != 4) { i = 0xFFFF; break; } value = (data[i+1] << 24) | (data[i+2] << 16) | (data[i+3] << 8) | data[i+4]; appendNumeric((IAXInfoElement::Type)data[i-1],value,4); i += 5; break; // 2 bytes case IAXInfoElement::VERSION: case IAXInfoElement::ADSICPE: case IAXInfoElement::AUTHMETHODS: case IAXInfoElement::REFRESH: case IAXInfoElement::DPSTATUS: case IAXInfoElement::CALLNO: case IAXInfoElement::MSGCOUNT: case IAXInfoElement::CALLINGTNS: case IAXInfoElement::FIRMWAREVER: case IAXInfoElement::RR_DELAY: if (data[i] != 2) { i = 0xFFFF; break; } value = (data[i+1] << 8) | data[i+2]; appendNumeric((IAXInfoElement::Type)data[i-1],value,2); i += 3; break; // 1 byte case IAXInfoElement::IAX_UNKNOWN: case IAXInfoElement::CALLINGPRES: case IAXInfoElement::CALLINGTON: case IAXInfoElement::CAUSECODE: case IAXInfoElement::ENCRYPTION: if (data[i] != 1) { i = 0xFFFF; break; } value = data[i+1]; appendNumeric((IAXInfoElement::Type)data[i-1],value,1); i += 2; break; // None case IAXInfoElement::AUTOANSWER: if (data[i]) { i = 0xFFFF; break; } appendNull(IAXInfoElement::AUTOANSWER); i += 1; break; default: Debug(DebugWarn,"IAXIEList::createFromFrame. Frame(%u,%u) with unknown IE identifier %u [%p]", frame->type(),frame->subclass(),data[i-1],frame); i = 0xFFFF; } if (i == 0xFFFF) break; if (i == len -1) i = 0xFFFF; else i++; } m_invalidIEList = i == 0xFFFF; if (!m_invalidIEList) return true; Debug(DebugWarn,"IAXIEList::createFromFrame. Frame(%u,%u) with invalid IE [%p]",frame->type(),frame->subclass(),frame); return false; } void IAXIEList::toBuffer(DataBlock& buf) { DataBlock data; buf.clear(); ObjList* obj = m_list.skipNull(); for (; obj; obj = obj->skipNext()) { IAXInfoElement* ie = static_cast(obj->get()); ie->toBuffer(data); buf.append(data); } } void IAXIEList::toString(String& dest, const char* indent) { ObjList* obj = m_list.skipNull(); for (; obj; obj = obj->skipNext()) { IAXInfoElement* ie = static_cast(obj->get()); dest << indent; if (ie->type() == IAXInfoElement::textframe) { ie->toString(dest); continue; } dest << IAXInfoElement::ieText(ie->type()); if (ie->type() != IAXInfoElement::AUTOANSWER) dest << ": "; switch (ie->type()) { // Text case IAXInfoElement::CALLED_NUMBER: case IAXInfoElement::CALLING_NUMBER: case IAXInfoElement::CALLING_ANI: case IAXInfoElement::CALLING_NAME: case IAXInfoElement::CALLED_CONTEXT: case IAXInfoElement::USERNAME: case IAXInfoElement::PASSWORD: case IAXInfoElement::LANGUAGE: case IAXInfoElement::DNID: case IAXInfoElement::CHALLENGE: case IAXInfoElement::MD5_RESULT: case IAXInfoElement::RSA_RESULT: case IAXInfoElement::CAUSE: case IAXInfoElement::MUSICONHOLD: case IAXInfoElement::RDNIS: case IAXInfoElement::DEVICETYPE: ie->toString(dest); break; case IAXInfoElement::CODEC_PREFS: //TODO: LIST of strings ? { const char* tmp = (const char*)((static_cast(ie))->data().data()); String s(tmp,(static_cast(ie))->length()); dest << s; } break; // Binary case IAXInfoElement::APPARENT_ADDR: { SocketAddr addr; IAXInfoElementBinary::unpackIP(addr,static_cast(ie)); dest << addr.host() << ':' << addr.port(); } break; case IAXInfoElement::PROVISIONING: case IAXInfoElement::AESPROVISIONING: case IAXInfoElement::SERVICEIDENT: case IAXInfoElement::FWBLOCKDATA: case IAXInfoElement::ENKEY: ie->toString(dest); break; // 4 bytes case IAXInfoElement::CAPABILITY: case IAXInfoElement::FORMAT: case IAXInfoElement::AUTHMETHODS: { ie->toString(dest); u_int32_t val = (static_cast(ie))->data(); String tmp; if (ie->type() == IAXInfoElement::AUTHMETHODS) IAXAuthMethod::authList(tmp,val,','); else IAXFormat::formatList(tmp,val,','); dest << " (" << tmp << ')'; } break; case IAXInfoElement::DATETIME: //TODO: print more data ie->toString(dest); break; case IAXInfoElement::SAMPLINGRATE: dest << (unsigned int)((static_cast(ie))->data()) << " Hz"; break; case IAXInfoElement::RR_LOSS: { u_int32_t val = (static_cast(ie))->data(); unsigned int percent = (unsigned int)(val & 0xFF000000); unsigned int count = (unsigned int)(val & 0x00FFFFFF); dest << count << " (" << percent << "%)"; } break; case IAXInfoElement::RR_JITTER: case IAXInfoElement::RR_PKTS: case IAXInfoElement::RR_DROPPED: case IAXInfoElement::RR_OOO: case IAXInfoElement::RR_DELAY: dest << (unsigned int)((static_cast(ie))->data()); break; case IAXInfoElement::TRANSFERID: case IAXInfoElement::PROVVER: case IAXInfoElement::FWBLOCKDESC: ie->toString(dest); break; // 2 bytes case IAXInfoElement::REFRESH: dest << (unsigned int)((static_cast(ie))->data()); dest << " second(s)"; break; case IAXInfoElement::ADSICPE: case IAXInfoElement::DPSTATUS: //TODO: print more data case IAXInfoElement::CALLNO: case IAXInfoElement::CALLINGTNS: //TODO: print more data case IAXInfoElement::FIRMWAREVER: case IAXInfoElement::VERSION: ie->toString(dest); break; // 1 byte case IAXInfoElement::IAX_UNKNOWN: case IAXInfoElement::CALLINGPRES: //TODO: print more data case IAXInfoElement::CALLINGTON: //TODO: print more data case IAXInfoElement::CAUSECODE: //TODO: print more data case IAXInfoElement::ENCRYPTION: //TODO: print more data ie->toString(dest); break; case IAXInfoElement::MSGCOUNT: { u_int16_t val = (static_cast(ie))->data(); dest << (int)((u_int8_t)val) << "new. " << (int)(val >> 8) << "old"; } break; // None case IAXInfoElement::AUTOANSWER: break; default: ; } } } IAXInfoElement* IAXIEList::getIE(IAXInfoElement::Type type) { for (ObjList* l = m_list.skipNull(); l; l = l->next()) { IAXInfoElement* ie = static_cast(l->get()); if (ie && ie->type() == type) return ie; } return 0; } bool IAXIEList::getString(IAXInfoElement::Type type, String& dest) { IAXInfoElementString* ie = static_cast(getIE(type)); dest.clear(); if (!ie) return false; dest = ie->data(); return true; } bool IAXIEList::getNumeric(IAXInfoElement::Type type, u_int32_t& dest) { IAXInfoElementNumeric* ie = static_cast(getIE(type)); if (!ie) return false; dest = ie->data(); return true; } bool IAXIEList::getBinary(IAXInfoElement::Type type, DataBlock& dest) { IAXInfoElementBinary* ie = static_cast(getIE(type)); dest.clear(); if (!ie) return false; dest = ie->data(); return true; } /* * IAXAuthMethod */ TokenDict IAXAuthMethod::s_texts[] = { {"Text", Text}, {"MD5", MD5}, {"RSA", RSA}, {0,0} }; void IAXAuthMethod::authList(String& dest, u_int16_t auth, char sep) { dest = ""; bool first = true; for (u_int16_t i = 0; s_texts[i].value; i++) { if (0 == (s_texts[i].value & auth)) continue; if (first) first = false; else dest += sep; dest += s_texts[i].token; } } /* * IAXFormat */ TokenDict IAXFormat::audioData[] = { {"G.723.1", G723_1}, {"GSM", GSM}, {"G.711 mu-law", ULAW}, {"G.711 a-law", ALAW}, {"G.726", MP3}, {"IMA ADPCM", ADPCM}, {"SLIN", SLIN}, {"LPC10", LPC10}, {"G729", G729A}, {"SPEEX", SPEEX}, {"ILBC", ILBC}, {0,0} }; TokenDict IAXFormat::videoData[] = { {"JPEG", IAXFormat::JPEG}, {"PNG", IAXFormat::PNG}, {"H261", IAXFormat::H261}, {"H263", IAXFormat::H263}, {0,0} }; void IAXFormat::formatList(String& dest, u_int32_t formats, char sep) { String s = sep; u_int32_t i; for (i = 0; audioData[i].value; i++) { if (0 == (audioData[i].value & formats)) continue; dest.append(audioData[i].token,s); } for (i = 0; videoData[i].value; i++) { if (0 == (videoData[i].value & formats)) continue; dest.append(videoData[i].token,s); } } /* * IAXControl */ TokenDict IAXControl::s_types[] = { {"NEW", New}, {"PING", Ping}, {"PONG", Pong}, {"ACK", Ack}, {"HANGUP", Hangup}, {"REJECT", Reject}, {"ACCEPT", Accept}, {"AUTHREQ", AuthReq}, {"AUTHREP", AuthRep}, {"INVAL", Inval}, {"LAGRQ", LagRq}, {"LAGRP", LagRp}, {"REGREQ", RegReq}, {"REGAUTH", RegAuth}, {"REGACK", RegAck}, {"REGREJ", RegRej}, {"REGREL", RegRel}, {"VNAK", VNAK}, {"DPREQ", DpReq}, {"DPREP", DpRep}, {"DIAL", Dial}, {"TXREQ", TxReq}, {"TXCNT", TxCnt}, {"TXACC", TxAcc}, {"TXREADY", TxReady}, {"TXREL", TxRel}, {"TXREJ", TxRej}, {"QUELCH", Quelch}, {"UNQUELCH", Unquelch}, {"POKE", Poke}, {"MWI", MWI}, {"UNSUPPORT", Unsupport}, {"TRANSFER", Transfer}, {"PROVISION", Provision}, {"FWDOWNL", FwDownl}, {"FWDATA", FwData}, {0,0} }; /* * IAXFrame */ TokenDict IAXFrame::s_types[] = { {"DTMF", DTMF}, {"VOICE", Voice}, {"VIDEO", Video}, {"CONTROL", Control}, {"NULL", Null}, {"IAX", IAX}, {"TEXT", Text}, {"IMAGE", Image}, {"HTML", HTML}, {"NOISE", Noise}, {0,0} }; IAXFrame::IAXFrame(Type type, u_int16_t sCallNo, u_int32_t tStamp, bool retrans, const unsigned char* buf, unsigned int len) : m_data((char*)buf,len,true), m_retrans(retrans), m_type(type), m_sCallNo(sCallNo), m_tStamp(tStamp) { // XDebug(DebugAll,"IAXFrame::IAXFrame(%u) [%p]",type,this); } IAXFrame::~IAXFrame() { // XDebug(DebugAll,"IAXFrame::~IAXFrame() [%p]",this); } IAXFrame* IAXFrame::parse(const unsigned char* buf, unsigned int len, IAXEngine* engine, const SocketAddr* addr) { if (len < 4) return 0; u_int16_t scn = (buf[0] << 8) | buf[1]; u_int16_t dcn = (buf[2] << 8) | buf[3]; // Full frame ? if (scn & 0x8000) { if (len < 12) return 0; scn &= 0x7fff; bool retrans = false; if (dcn & 0x8000) { retrans = true; dcn &= 0x7fff; } u_int32_t sc = IAXFrame::unpackSubclass(buf[11]); u_int32_t ts = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; return new IAXFullFrame((IAXFrame::Type)buf[10],sc,scn,dcn,buf[8],buf[9],ts,retrans,buf+12,len-12); } // Meta frame ? if (scn == 0) { if (dcn & 0x8000) { // Meta video if (len < 6) return 0; scn = (buf[4] << 8) | buf[5]; bool retrans = false; if (scn & 0x8000) { retrans = true; scn &= 0x7fff; } return new IAXFrame(IAXFrame::Video,dcn & 0x7fff,scn,retrans,buf+6,len-6); } // Meta trunk frame - we need to push chunks into the engine if (!(engine && addr)) return 0; if (len < 8) return 0; // "meta command" should be 1 if (buf[2] != 1) return 0; bool tstamps = (buf[3] & 1) != 0; // u_int32_t ts = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; buf += 8; len -= 8; if (tstamps) { // Trunk timestamps (mini frames) while (len >= 6) { u_int16_t dlen = (buf[0] << 8) | buf[1]; if ((unsigned int)(dlen + 6) > len) return 0; scn = (buf[2] << 8) | buf[3]; bool retrans = false; if (scn & 0x8000) { retrans = true; scn &= 0x7fff; } dcn = (buf[4] << 8) | buf[5]; IAXFrame* frame = new IAXFrame(IAXFrame::Voice,scn,dcn,retrans,buf+6,dlen); engine->addFrame(*addr,frame); frame->deref(); dlen += 6; buf += dlen; len -= dlen; } } else { // No trunk timestamps while (len >= 4) { u_int16_t dlen = (buf[2] << 8) | buf[3]; if ((unsigned int)(dlen + 4) > len) return 0; scn = (buf[0] << 8) | buf[1]; bool retrans = false; if (scn & 0x8000) { retrans = true; scn &= 0x7fff; } IAXFrame* frame = new IAXFrame(IAXFrame::Voice,scn,0,retrans,buf+4,dlen); engine->addFrame(*addr,frame); frame->deref(); dlen += 4; buf += dlen; len -= dlen; } } return 0; } // Mini frame return new IAXFrame(IAXFrame::Voice,scn,dcn,false,buf+4,len-4); } u_int8_t IAXFrame::packSubclass(u_int32_t value) { if (value < 0x80) return (u_int8_t)value; if (value == 0x80) return 0x87; if ((value > 0x9f) && (value <= 0xff)) { DDebug(DebugMild,"IAXFrame nonstandard pack %u",value); return (u_int8_t)value; } // 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) return i | 0x80; v <<= 1; } Debug(DebugGoOn,"IAXFrame could not pack subclass %u (0x%08x)",value,value); return 0; } u_int32_t IAXFrame::unpackSubclass(u_int8_t value) { if (value > 0x9f) { DDebug(DebugMild,"IAXFrame nonstandard unpack %u",value); return value; } if (value & 0x80) return 1 << value & 0x7f; return value; } const IAXFullFrame* IAXFrame::fullFrame() const { return 0; } /* * IAXFullFrame */ TokenDict IAXFullFrame::s_controlTypes[] = { {"HANGUP", Hangup}, // {"RING", Ring}, {"RINGING", Ringing}, {"ANSWER", Answer}, {"BUSY", Busy}, {"CONGESTION", Congestion}, {"FLASHHOOK", FlashHook}, {"OPTION", Option}, {"KEYRADIO", KeyRadio}, {"UNKEYRADIO", UnkeyRadio}, {"PROGRESSING", Progressing}, {"PROCEEDING", Proceeding}, {"HOLD", Hold}, {"UNHOLD", Unhold}, {"VIDUPDATE", VidUpdate}, {0,0} }; 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_subclass(subclass) { // XDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]", // type,subclass,this); } 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_subclass(subclass) { // XDebug(DebugAll,"IAXFullFrame::IAXFullFrame(%u,%u) [%p]", // type,subclass,this); unsigned char header[12]; DataBlock ie; // Full frame flag + Source call number header[0] = 0x80 | (unsigned char)(sourceCallNo() >> 8); header[1] = (unsigned char)(sourceCallNo()); // Retrans + Destination call number header[2] = (unsigned char)(destCallNo() >> 8); // retrans is false: bit 7 is 0 header[3] = (unsigned char)destCallNo(); // Timestamp header[4] = (unsigned char)(timeStamp() >> 24); header[5] = (unsigned char)(timeStamp() >> 16); header[6] = (unsigned char)(timeStamp() >> 8); header[7] = (unsigned char)timeStamp(); // oSeqNo + iSeqNo header[8] = m_oSeqNo; header[9] = m_iSeqNo; // Type header[10] = type; // Subclass header[11] = packSubclass(m_subclass); // Set data m_data.assign(header,sizeof(header)); if (buf) { ie.assign((void*)buf,(unsigned int)len); m_data += ie; } } void IAXFullFrame::toString(String& dest, const SocketAddr& local, const SocketAddr& remote, bool incoming) const { #define STARTLINE(indent) "\r\n" << indent #define TMP_TEXT (tmp ? tmp : unk) const char* enclose = "\r\n-----"; const char* unk = "???"; const char* tmp = 0; dest << enclose; String stmp; setStringFromInteger(stmp,type(),1); tmp = typeText(type()); dest << STARTLINE("") << TMP_TEXT << " (" << stmp << ")"; // Subclass String subc; switch (type()) { case IAXFrame::IAX: case IAXFrame::Control: tmp = (type() == IAXFrame::IAX ? IAXControl::typeText(subclass()) : controlTypeText(subclass())); subc = TMP_TEXT; break; case IAXFrame::DTMF: subc << (char)subclass(); break; case IAXFrame::Voice: case IAXFrame::Video: case IAXFrame::Image: tmp = (type() == IAXFrame::Voice ? IAXFormat::audioText(subclass()) : IAXFormat::videoText(subclass())); subc = TMP_TEXT; break; case IAXFrame::Null: subc = "Subclass: "; break; case IAXFrame::Text: subc = "Subclass: "; break; case IAXFrame::HTML: subc = "Subclass: "; break; case IAXFrame::Noise: subc << (unsigned int)(subclass()) << " -dBov"; break; default: subc = unk; } setStringFromInteger(stmp,subclass(),4); dest << " - " << subc << " (" << stmp << ")"; // Addresses if (incoming) dest << STARTLINE(" ") << "Incoming from "; else dest << STARTLINE(" ") << "Outgoing to "; dest << remote.host() << ':' << remote.port(); dest << " (Local address: "; dest << local.host() << ':' << local.port() << ')'; // Transaction numbers dest << STARTLINE(" ") << "Call (Local:Remote): "; if (incoming) dest << destCallNo(); else dest << sourceCallNo(); dest << ':'; if (incoming) dest << sourceCallNo(); else dest << destCallNo(); // Info dest << ". Timestamp: " << (unsigned int)(timeStamp()); dest << ". Retrans: " << String::boolText(retrans()); dest << ". Sequence numbers: Out: " << oSeqNo() << " In: " << iSeqNo(); // IEs IAXIEList ieList; bool hasIE = ieList.createFromFrame(this,incoming); if (hasIE) { String aux; aux << STARTLINE(" "); ieList.toString(dest,aux); } if (!hasIE) { dest << STARTLINE(" "); if (ieList.invalidIEList()) dest << "Error parsing Information Element(s)"; else dest << "No Information Element(s)"; } dest << enclose; #undef TMP_TEXT #undef STARTLINE } IAXFullFrame::~IAXFullFrame() { // XDebug(DebugAll,"IAXFullFrame::~IAXFullFrame(%u,%u) [%p]", // type(),m_subclass,this); } const IAXFullFrame* IAXFullFrame::fullFrame() const { return this; } /* * 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; } } void IAXFrameOut::adjustAuthTimeout(u_int64_t nextTransTime) { if (!(type() == IAXFrame::IAX && (subclass() == IAXControl::AuthReq || subclass() ==IAXControl::RegAuth))) return; m_retransCount = 1; m_nextTransTime = nextTransTime; } /* * IAXMetaTrunkFrame */ #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((u_int32_t)Time::msecNow()); } IAXMetaTrunkFrame::~IAXMetaTrunkFrame() { m_engine->removeTrunkFrame(this); delete[] m_data; } void IAXMetaTrunkFrame::setTimestamp(u_int32_t tStamp) { m_timestamp = tStamp; m_data[4] = (u_int8_t)(tStamp >> 24); m_data[5] = (u_int8_t)(tStamp >> 16); m_data[6] = (u_int8_t)(tStamp >> 8); m_data[7] = (u_int8_t)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((u_int32_t)Time::msecNow()); // Is the first mini frame ? if (m_dataAddIdx == IAX2_METATRUNK_HEADERLENGTH) m_timestamp = (u_int32_t)Time::msecNow(); // Add the mini frame m_data[m_dataAddIdx++] = (u_int8_t)(data.length() >> 8); m_data[m_dataAddIdx++] = (u_int8_t)data.length(); m_data[m_dataAddIdx++] = (u_int8_t)(sCallNo >> 8); m_data[m_dataAddIdx++] = (u_int8_t)sCallNo; m_data[m_dataAddIdx++] = (u_int8_t)(tStamp >> 8); m_data[m_dataAddIdx++] = (u_int8_t)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: */