/** * 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-2013 Null Team * Author: Marian Podgoreanu * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. * * 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. */ #include #include // For memcpy() #include // For sprintf() using namespace TelEngine; static inline void setStringFromInteger(String& dest, u_int32_t value, u_int8_t length) { char tmp[11]; switch (length) { case 1: sprintf(tmp,"0x%02x",(u_int8_t)value); break; case 2: sprintf(tmp,"0x%04x",(u_int16_t)value); break; default: sprintf(tmp,"0x%08x",value); break; } dest = tmp; } class IAXTrunkFrameTrans : public GenObject { public: inline IAXTrunkFrameTrans(u_int16_t callNo) : m_callNo(callNo) {} static IAXTrunkFrameTrans* get(ObjList& list, u_int16_t rCallNo); u_int16_t m_callNo; ObjList m_blocks; }; IAXTrunkFrameTrans* IAXTrunkFrameTrans::get(ObjList& list, u_int16_t callNo) { for (ObjList* o = list.skipNull(); o; o = o->skipNext()) { IAXTrunkFrameTrans* t = static_cast(o->get()); if (t->m_callNo == callNo) return t; } IAXTrunkFrameTrans* t = new IAXTrunkFrameTrans(callNo); list.append(t); return t; } /* * IAXInfoElement */ const TokenDict IAXInfoElement::s_causeName[] = { {"unallocated", 1}, // Unallocated (unassigned) number {"noroute-to-network", 2}, // No route to specified transit network {"noroute", 3}, // No route to destination {"channel-unacceptable", 6}, // Channel unacceptable {"call-delivered", 7}, // Call awarded and being delivered in an established channel {"normal-clearing", 16}, // Normal Clearing {"busy", 17}, // User busy {"noresponse", 18}, // No user responding {"noanswer", 19}, // No answer from user (user alerted) {"rejected", 21}, // Call Rejected {"moved", 22}, // Number changed {"out-of-order", 27}, // Destination out of order {"invalid-number", 28}, // Invalid number format {"facility-rejected", 29}, // Facility rejected {"status-enquiry-rsp", 30}, // Response to STATUS ENQUIRY {"normal", 31}, // Normal, unspecified {"congestion", 34}, // No circuit/channel available {"channel-congestion", 34}, {"net-out-of-order", 38}, // Network out of order {"noconn", 38}, {"temporary-failure", 41}, // Temporary failure {"congestion", 42}, // Switching equipment congestion {"switch-congestion", 42}, {"access-info-discarded", 43}, // Access information discarded {"channel-unavailable", 44}, // Requested channel not available {"preempted", 45}, // Preempted {"noresource", 47}, // Resource unavailable, unspecified {"facility-not-subscribed", 50}, // Requested facility not subscribed {"barred-out", 52}, // Outgoing call barred {"barred-in", 54}, // Incoming call barred {"bearer-cap-not-auth", 57}, // Bearer capability not authorized {"bearer-cap-not-available", 58}, // Bearer capability not presently available {"nomedia", 58}, {"service-unavailable", 63}, // Service or option not available {"bearer-cap-not-implemented", 65}, // Bearer capability not implemented {"channel-type-not-implemented", 66}, // Channel type not implemented {"facility-not-implemented", 69}, // Requested facility not implemented {"restrict-bearer-cap-avail", 70}, // Only restricted digital information bearer capability is available {"service-not-implemented", 79}, // Service or option not implemented, unspecified {"invalid-callref", 81}, // Invalid call reference value {"unknown-channel", 82}, // Identified channel does not exist {"unknown-callid", 83}, // A suspended call exists, but this call identity does not {"duplicate-callid", 84}, // Call identity in use {"no-call-suspended", 85}, // No call suspended {"suspended-call-cleared", 86}, // Call having the requested call identity has been cleared {"incompatible-dest", 88}, // Incompatible destination {"invalid-transit-net", 91}, // Invalid transit network selection {"invalid-message", 95}, // Invalid message, unspecified {"missing-mandatory-ie", 96}, // Mandatory information element is missing {"unknown-message", 97}, // Message type non-existent or not implemented {"wrong-message", 98}, // Message not compatible with call state, non-existent or not implemented {"unknown-ie", 99}, // Information element non-existent or not implemented {"invalid-ie", 100}, // Invalid information element contents {"wrong-state-message", 101}, // Message not compatible with call state {"timeout", 102}, // Recovery on timer expiry {"mandatory-ie-len", 103}, // Mandatory information element length error {"protocol-error", 111}, // Protocol error, unspecified {"interworking", 127}, // Interworking, unspecified {0,0} }; const TokenDict IAXInfoElement::s_typeOfNumber[] = { {"unknown", 0x00}, // Unknown {"international", 0x10}, // International number {"national", 0x20}, // National number {"net-specific", 0x30}, // Network specific number {"subscriber", 0x40}, // Subscriber number {"abbreviated", 0x60}, // Abbreviated number {"reserved", 0x70}, // Reserved for extension {0,0} }; const TokenDict IAXInfoElement::s_presentation[] = { {"allowed", 0x00}, // Presentation allowed {"restricted", 0x20}, // Presentation restricted {"unavailable", 0x40}, // Number not available due to interworking // Aliases for presentation=... {"yes", 0x00}, {"true", 0x00}, {"no", 0x20}, {"false", 0x20}, {0,0} }; const TokenDict IAXInfoElement::s_screening[] = { {"user-provided", 0x00}, // User-provided, not screened {"user-provided-passed", 0x01}, // User-provided, verified and passed {"user-provided-failed", 0x02}, // User-provided, verified and failed {"network-provided", 0x03}, // Network provided // Aliases for screening=... {"yes", 0x01}, // User-provided, verified and passed {"true", 0x01}, {"no", 0x00}, // User-provided, not screened {"false", 0x00}, {0,0} }; const 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}, {"CALLTOKEN", CALLTOKEN}, {"CAPABILITY2", CAPABILITY2}, {"FORMAT2", FORMAT2}, {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) { if (!m_data.length()) return; String tmp; tmp.hexify(m_data.data(),m_data.length(),' '); buf << tmp; } 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 */ IAXIEList::IAXIEList() : m_invalidIEList(false) { XDebug(DebugInfo,"IAXIEList::IAXIEList() [%p]",this); } IAXIEList::IAXIEList(const IAXFullFrame* frame, bool incoming) : m_invalidIEList(false) { XDebug(DebugInfo,"IAXIEList::IAXIEList(%p,%u) [%p]",frame,incoming,this); if (frame) createFromFrame(frame,incoming); } IAXIEList::~IAXIEList() { XDebug(DebugInfo,"IAXIEList::~IAXIEList() [%p]",this); } void IAXIEList::insertVersion() { if (!getIE(IAXInfoElement::VERSION)) 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 || frame->type() == IAXFrame::Video) 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: case IAXInfoElement::CALLTOKEN: 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; case IAXInfoElement::CAPABILITY2: case IAXInfoElement::FORMAT2: // Binary: 1 byte version + array of bytes (media format flags) 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(DebugInfo,"IAX Frame(%u,%u) with unknown IE identifier %u [%p]", frame->type(),frame->subclass(),data[i-1],frame); appendBinary((IAXInfoElement::Type)data[i-1],data+i+1,data[i]); i += data[i] + 1; } 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); } } static inline void addNumericName(String& buf, IAXInfoElement* ie, const TokenDict* dict, const char* prefix = " (", const char* suffix = ")") { int val = (static_cast(ie))->data(); const char* s = lookup(val,dict); if (s) buf << prefix << s << suffix; } static inline void addCallingPres(String& buf, IAXInfoElement* ie) { int val = (static_cast(ie))->data(); const char* pres = lookup(val & 0xf0,IAXInfoElement::s_presentation); const char* screen = lookup(val & 0x0f,IAXInfoElement::s_screening); if (!(pres || screen)) return; buf << " ("; if (pres) buf << pres; if (screen) buf << (pres ? "," : "") << screen; buf << ")"; } static inline void addValPadded0(String& buf, unsigned int val, const char* prefix) { buf << prefix; if (val < 10) buf << "0"; buf << val; } static void addDateTime(String& buf, IAXInfoElement* ie) { u_int32_t val = (static_cast(ie))->data(); unsigned int y,mon,d,h,min,s; IAXEngine::decodeDateTime(val,y,mon,d,h,min,s); buf << " (" << y; addValPadded0(buf,mon,"."); addValPadded0(buf,d,"."); addValPadded0(buf,h," "); addValPadded0(buf,min,":"); addValPadded0(buf,s,":"); buf << ")"; } 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; } const char* name = IAXInfoElement::ieText(ie->type()); if (name) dest << name; else { u_int8_t t = ie->type(); String tmp; tmp.hexify(&t,1); dest << "0x" << tmp; } if (ie->type() != IAXInfoElement::AUTOANSWER) dest << ": "; switch (ie->type()) { // 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: case IAXInfoElement::CALLTOKEN: 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: ie->toString(dest); addDateTime(dest,ie); 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::ENCRYPTION: //TODO: print more data ie->toString(dest); break; case IAXInfoElement::CALLINGPRES: ie->toString(dest); addCallingPres(dest,ie); break; case IAXInfoElement::CALLINGTON: ie->toString(dest); addNumericName(dest,ie,IAXInfoElement::s_typeOfNumber); break; case IAXInfoElement::CAUSECODE: ie->toString(dest); addNumericName(dest,ie,IAXInfoElement::s_causeName); 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: ; ie->toString(dest); } } } IAXInfoElement* IAXIEList::getIE(IAXInfoElement::Type type, bool remove) { for (ObjList* l = m_list.skipNull(); l; l = l->skipNext()) { IAXInfoElement* ie = static_cast(l->get()); if (ie->type() != type) continue; if (remove) l->remove(false); 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; } } /* * IAXFormatDesc */ // Set the format void IAXFormatDesc::setFormat(u_int32_t fmt, int type) { m_format = IAXFormat::mask(fmt,type); if (!m_format) { m_multiplier = 1; return; } if (type == IAXFormat::Audio) { switch (m_format) { case IAXFormat::G722: // 16kHz samplig rate m_multiplier = 16; break; default: // Assume 8kHz sampling rate m_multiplier = 8; } } else if (type == IAXFormat::Video) // Assume 90kHz sampling rate for video m_multiplier = 90; else m_multiplier = 1; } /* * IAXFormat */ const TokenDict IAXFormat::s_formats[] = { {"G.723.1", G723_1}, {"GSM", GSM}, {"G.711 mu-law", ULAW}, {"G.711 a-law", ALAW}, {"G.726", G726}, {"IMA ADPCM", ADPCM}, {"SLIN", SLIN}, {"LPC10", LPC10}, {"G.729", G729}, {"SPEEX", SPEEX}, {"ILBC", ILBC}, {"G.726 AAL2", G726AAL2}, {"G.722", G722}, {"AMR", AMR}, {"GSM_HR", GSM_HR}, {"JPEG", JPEG}, {"PNG", PNG}, {"H261", H261}, {"H263", H263}, {"H263p", H263p}, {"H264", H264}, {0,0} }; const TokenDict IAXFormat::s_types[] = { {"audio", Audio}, {"video", Video}, {"image", Image}, {0,0} }; const String IAXFormat::s_typesList[IAXFormat::TypeCount] = { "audio", "video", "image" }; // Set format void IAXFormat::set(u_int32_t* fmt, u_int32_t* fmtIn, u_int32_t* fmtOut) { if (fmt) m_format.setFormat(*fmt,m_type); if (fmtIn) m_formatIn.setFormat(*fmtIn,m_type); if (fmtOut) m_formatOut.setFormat(*fmtOut,m_type); } void IAXFormat::formatList(String& dest, u_int32_t formats, const TokenDict* dict, const char* sep) { if (!dict) dict = s_formats; for (; dict->value; dict++) if (0 != (dict->value & formats)) dest.append(dict->token,sep); } // Pick a format from a list of capabilities u_int32_t IAXFormat::pickFormat(u_int32_t formats, u_int32_t format) { if (0 != (format & formats)) return format; if (!formats) return 0; format = 1; for (unsigned int i = 0; i < (8 * sizeof(u_int32_t)); i++, format = format << 1) if (0 != (format & formats)) return format; return 0; } // Encode a formats list u_int32_t IAXFormat::encode(const String& formats, const TokenDict* dict, char sep) { if (!dict) return 0; u_int32_t mask = 0; ObjList* list = formats.split(sep,false); for (ObjList* o = list->skipNull(); o; o = o->skipNext()) { int fmt = lookup(o->get()->toString(),dict); mask |= fmt; } TelEngine::destruct(list); return mask; } /* * 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}, {"CALLTOKEN", CallToken}, {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, bool mark) : m_data((char*)buf,len,true), m_retrans(retrans), m_type(type), m_sCallNo(sCallNo), m_tStamp(tStamp), m_mark(mark) { // 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 = 0; bool mark = false; unsigned char type = buf[10]; if (type != IAXFrame::Video) { // Hack: Control StopSounds is sent with subclass 255 if (type != IAXFrame::Control || buf[11] != 255) sc = IAXFrame::unpackSubclass(buf[11]); else sc = IAXFullFrame::StopSounds; } else { mark = 0 != (buf[11] & 0x40); // Clear the mark flag sc = IAXFrame::unpackSubclass(buf[11] & 0xbf); } u_int32_t ts = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; return new IAXFullFrame((IAXFrame::Type)type,sc,scn,dcn,buf[8],buf[9],ts,retrans,buf+12,len-12,mark); } // Meta frame ? if (scn == 0) { if (dcn & 0x8000) { // Meta video if (len < 6) return 0; // Timestamp: lowest 15 bits of transmiter timestamp scn = ((buf[4] & 0x7f) << 8) | buf[5]; bool mark = (0 != (buf[4] & 0x80)); return new IAXFrame(IAXFrame::Video,dcn & 0x7fff,scn & 0x7fff,false,buf+6,len-6,mark); } // 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; if (tstamps) { // Trunk timestamps (mini frames) buf += 8; len -= 8; 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 u_int32_t ts = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]; buf += 8; len -= 8; Time now; ObjList list; while (len >= 4) { u_int16_t dlen = (buf[2] << 8) | buf[3]; if ((unsigned int)(dlen + 4) > len) break; scn = (buf[0] << 8) | buf[1]; bool retrans = false; if (scn & 0x8000) { retrans = true; scn &= 0x7fff; } IAXTrunkFrameTrans* t = IAXTrunkFrameTrans::get(list,scn); t->m_blocks.append(new DataBlock((void*)(buf+4),dlen)); dlen += 4; buf += dlen; len -= dlen; } for (ObjList* o = list.skipNull(); o; o = o->skipNext()) { IAXTrunkFrameTrans* t = static_cast(o->get()); IAXTransaction* tr = engine->findTransaction(*addr,t->m_callNo); if (!tr) continue; tr->processMiniNoTs(ts,t->m_blocks,now); TelEngine::destruct(tr); } } return 0; } // Mini frame return new IAXFrame(IAXFrame::Voice,scn,dcn,false,buf+4,len-4); } // Build a video meta frame buffer void IAXFrame::buildVideoMetaFrame(DataBlock& dest, u_int16_t sCallNo, u_int32_t tStamp, bool mark, void* data, unsigned int len) { unsigned char header[6] = {0,0}; header[2] = 0x80 | ((sCallNo >> 8) & 0x7f); header[3] = (unsigned char)sCallNo; header[4] = (tStamp >> 8) & 0x7f; if (mark) header[4] |= 0x80; header[5] = tStamp; dest.assign(header,6); dest.append(data,len); } 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)) { Debug(DebugMild,"IAXFrame nonstandard pack %u",value); return 0; } // 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%x)",value,value); return 0; } u_int32_t IAXFrame::unpackSubclass(u_int8_t value) { if (value > 0x9f) { DDebug(DebugMild,"IAXFrame nonstandard unpack %u",value); return 0; } if (value & 0x80) return 1 << (value & 0x7f); return value; } IAXFullFrame* IAXFrame::fullFrame() { 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}, {"SRCUPDATE", SrcUpdate}, {"STOPSOUNDS", StopSounds}, {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, bool mark) : IAXFrame(type,sCallNo,tStamp,retrans,buf,len,mark), m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass), m_ieList(0) { XDebug(DebugAll, "IAXFullFrame() incoming type=%u subclass=%u callno=(%u,%u) seq=(%u,%u) ts=%u retrans=%u [%p]", type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,retrans,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, bool mark) : IAXFrame(type,sCallNo,tStamp,false,0,0,mark), m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass), m_ieList(0) { XDebug(DebugAll, "IAXFullFrame() outgoing type=%u subclass=%u callno=(%u,%u) seq=(%u,%u) ts=%u [%p]", type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,this); setDataHeader(); if (buf) m_data.append((void*)buf,(unsigned int)len); } // Constructor. Constructs an outgoing full frame IAXFullFrame::IAXFullFrame(Type type, u_int32_t subclass, u_int16_t sCallNo, u_int16_t dCallNo, unsigned char oSeqNo, unsigned char iSeqNo, u_int32_t tStamp, IAXIEList* ieList, u_int16_t maxlen, bool mark) : IAXFrame(type,sCallNo,tStamp,false,0,0,mark), m_dCallNo(dCallNo), m_oSeqNo(oSeqNo), m_iSeqNo(iSeqNo), m_subclass(subclass), m_ieList(ieList) { XDebug(DebugAll, "IAXFullFrame() outgoing type=%u subclass=%u callno=(%u,%u) seq=(%u,%u) ts=%u [%p]", type,subclass,sCallNo,dCallNo,oSeqNo,iSeqNo,tStamp,this); updateBuffer(maxlen); } void IAXFullFrame::toString(String& dest, const SocketAddr& local, const SocketAddr& remote, bool incoming) { #define STARTLINE(indent) "\r\n" << indent const char* enclose = "\r\n-----"; dest << enclose; String stmp; setStringFromInteger(stmp,type(),1); dest << STARTLINE("") << typeText(type()) << " (" << stmp << ")"; String extra; // Subclass String subc; switch (type()) { case IAXFrame::IAX: case IAXFrame::Control: subc = (type() == IAXFrame::IAX ? IAXControl::typeText(subclass()) : controlTypeText(subclass())); break; case IAXFrame::DTMF: subc << (char)subclass(); break; case IAXFrame::Video: extra << "Mark: " << String::boolText(mark()); // fallthrough case IAXFrame::Voice: case IAXFrame::Image: IAXFormat::formatList(subc,subclass()); 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: ; } setStringFromInteger(stmp,subclass(),4); dest << " - " << (subc ? subc.c_str() : "???") << " (" << 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(); if (extra) dest << STARTLINE(" ") << extra; // IEs updateIEList(incoming); if (!m_ieList->empty()) { String aux; aux << STARTLINE(" "); m_ieList->toString(dest,aux); } if (m_ieList->empty()) { dest << STARTLINE(" "); if (m_ieList->invalidIEList()) dest << "Error parsing Information Element(s)"; else dest << "No Information Element(s)"; } dest << enclose; #undef STARTLINE } // Rebuild frame buffer from the list of IEs void IAXFullFrame::updateBuffer(u_int16_t maxlen) { setDataHeader(); if (!m_ieList) return; DataBlock tmp; m_ieList->toBuffer(tmp); if (tmp.length() <= maxlen) m_data += tmp; else Debug(DebugNote,"Frame(%u,%u) buffer too long (%u > %u) [%p]", type(),subclass(),tmp.length(),maxlen,this); } // Update IE list from buffer if not already done bool IAXFullFrame::updateIEList(bool incoming) { if (!m_ieList) m_ieList = new IAXIEList(this,incoming); return !m_ieList->invalidIEList(); } // Remove the IE list IAXIEList* IAXFullFrame::removeIEList(bool delObj) { if (!m_ieList) return 0; IAXIEList* old = m_ieList; m_ieList = 0; if (delObj) { delete old; old = 0; } return old; } IAXFullFrame::~IAXFullFrame() { XDebug(DebugAll,"IAXFullFrame::~IAXFullFrame(%u,%u) [%p]",type(),m_subclass,this); } IAXFullFrame* IAXFullFrame::fullFrame() { return this; } // Destroyed notification. Clear data void IAXFullFrame::destroyed() { removeIEList(); IAXFrame::destroyed(); } // Build frame buffer header void IAXFullFrame::setDataHeader() { unsigned char header[12]; // Full frame flag + Source call number header[0] = 0x80 | (unsigned char)(sourceCallNo() >> 8); header[1] = (unsigned char)(sourceCallNo()); // Retrans + Destination call number header[2] = (unsigned char)(destCallNo() >> 8); // retrans is false: bit 7 is 0 header[3] = (unsigned char)destCallNo(); // Timestamp header[4] = (unsigned char)(timeStamp() >> 24); header[5] = (unsigned char)(timeStamp() >> 16); header[6] = (unsigned char)(timeStamp() >> 8); header[7] = (unsigned char)timeStamp(); // oSeqNo + iSeqNo header[8] = m_oSeqNo; header[9] = m_iSeqNo; // Type header[10] = type(); // Subclass header[11] = packSubclass(m_subclass); if (mark()) header[11] |= 0x40; // Set data m_data.assign(header,sizeof(header)); } /* * IAXTrunkInfo */ // Init all data from parameters void IAXTrunkInfo::init(const NamedList& params, const String& prefix, const IAXTrunkInfo* def) { m_retransCount = params.getIntValue(prefix + "retrans_count", def ? def->m_retransCount : IAX2_RETRANS_COUNT_DEF, IAX2_RETRANS_COUNT_MIN,IAX2_RETRANS_COUNT_MAX); m_retransInterval = params.getIntValue(prefix + "retrans_interval", def ? def->m_retransInterval : IAX2_RETRANS_INTERVAL_DEF, IAX2_RETRANS_INTERVAL_MIN,IAX2_RETRANS_INTERVAL_MAX); m_pingInterval = params.getIntValue(prefix + "ping_interval", def ? def->m_pingInterval : IAX2_PING_INTERVAL_DEF, IAX2_PING_INTERVAL_MIN); } // Init from parameters void IAXTrunkInfo::initTrunking(const NamedList& params, const String& prefix, const IAXTrunkInfo* def, bool out, bool in) { if (out) { m_timestamps = params.getBoolValue(prefix + "timestamps", !def || def->m_timestamps); m_sendInterval = params.getIntValue(prefix + "sendinterval", def ? def->m_sendInterval : IAX2_TRUNKFRAME_SEND_DEF,IAX2_TRUNKFRAME_SEND_MIN); m_maxLen = params.getIntValue(prefix + "maxlen", def ? def->m_maxLen : IAX2_TRUNKFRAME_LEN_DEF,IAX2_TRUNKFRAME_LEN_MIN); m_efficientUse = params.getBoolValue(prefix + "efficient_use", def && def->m_efficientUse); } if (in) { m_trunkInSyncUsingTs = params.getBoolValue(prefix + "nominits_sync_use_ts", !def || def->m_trunkInSyncUsingTs); m_trunkInTsDiffRestart = params.getIntValue(prefix + "nominits_ts_diff_restart", def ? m_trunkInTsDiffRestart : 5000,1000); } } // Update trunking from parameters. Don't change values not present in list void IAXTrunkInfo::updateTrunking(const NamedList& params, const String& prefix, bool out, bool in) { if (out) { m_timestamps = params.getBoolValue(prefix + "timestamps",m_timestamps); m_sendInterval = params.getIntValue(prefix + "sendinterval", m_sendInterval,IAX2_TRUNKFRAME_SEND_MIN); m_maxLen = params.getIntValue(prefix + "maxlen",m_maxLen,IAX2_TRUNKFRAME_LEN_MIN); m_efficientUse = params.getBoolValue(prefix + "efficient_use",m_efficientUse); } if (in) { m_trunkInSyncUsingTs = params.getBoolValue(prefix + "nominits_sync_use_ts", m_trunkInSyncUsingTs); m_trunkInTsDiffRestart = params.getIntValue(prefix + "nominits_ts_diff_restart", m_trunkInTsDiffRestart,1000); } } // Dump info void IAXTrunkInfo::dump(String& buf, const char* sep, bool out, bool in, bool other) { if (out) { buf.append("timestamps=",sep) << String::boolText(m_timestamps); buf << sep << "sendinterval=" << m_sendInterval; buf << sep << "maxlen=" << m_maxLen; buf << sep << "efficient_use=" << String::boolText(m_efficientUse); } if (in) { buf.append("nominits_sync_use_ts=",sep) << String::boolText(m_trunkInSyncUsingTs); buf << sep << "nominits_ts_diff_restart=" << m_trunkInTsDiffRestart; } if (other) { buf.append("retrans_count=",sep) << m_retransCount; buf << sep << "retrans_interval=" << m_retransInterval; buf << sep << "ping_interval=" << m_pingInterval; } } /* * IAXMetaTrunkFrame */ IAXMetaTrunkFrame::IAXMetaTrunkFrame(IAXEngine* engine, const SocketAddr& addr, bool timestamps, unsigned int maxLen, unsigned int sendInterval) : Mutex(false,"IAXMetaTrunkFrame"), m_calls(0), m_data(0), m_dataAddIdx(IAX2_TRUNKFRAME_HEADERLENGTH), m_timeStamp(0), m_send(0), m_lastSentTs(0), m_sendInterval(sendInterval), m_engine(engine), m_addr(addr), m_trunkTimestamps(timestamps), m_maxLen(maxLen), m_maxDataLen(0), m_miniHdrLen(timestamps ? 6 : 4) { // Make sure we don't receive invalid values if (m_maxLen < IAX2_TRUNKFRAME_LEN_MIN) m_maxLen = IAX2_TRUNKFRAME_LEN_MIN; m_data = new u_int8_t[m_maxLen]; // Audio data length can't be greater the remaining space // Also make sure we can put it in 2 bytes m_maxDataLen = m_maxLen - IAX2_TRUNKFRAME_HEADERLENGTH - m_miniHdrLen; if (m_maxDataLen > 0xffff) m_maxDataLen = 0xffff; // Meta indicator *(u_int16_t*)m_data = 0; // Meta command & Command data (use timestamps) m_data[2] = 1; m_data[3] = m_trunkTimestamps ? 1 : 0; XDebug(m_engine,DebugAll,"Trunk frame '%s:%d' created [%p]", m_addr.host().c_str(),m_addr.port(),this); } IAXMetaTrunkFrame::~IAXMetaTrunkFrame() { if (!m_calls) XDebug(m_engine,DebugAll,"Trunk frame '%s:%d' destroyed [%p]", m_addr.host().c_str(),m_addr.port(),this); else Debug(m_engine,DebugMild,"Trunk frame '%s:%d' destroyed with %u calls [%p]", m_addr.host().c_str(),m_addr.port(),m_calls,this); delete[] m_data; } unsigned int IAXMetaTrunkFrame::add(u_int16_t sCallNo, const DataBlock& data, u_int32_t tStamp) { // Do we have data ? if (!data.length()) return 0; // Avoid buffer overflow if (data.length() > m_maxDataLen) { Debug(m_engine,DebugGoOn, "Trunk frame '%s:%d' can't add %u bytes (max=%u) for call %u [%p]", m_addr.host().c_str(),m_addr.port(),data.length(),m_maxDataLen,sCallNo,this); return 0; } Lock lck(this); if (!m_timeStamp) setTimestamp(Time::now()); // If no more room, send it if (m_dataAddIdx + data.length() + m_miniHdrLen > m_maxLen) doSend(); XDebug(m_engine,DebugAll,"Trunk frame '%s:%d' adding %u payload bytes call=%u [%p]", m_addr.host().c_str(),m_addr.port(),data.length(),sCallNo,this); // Add the mini frame if (m_trunkTimestamps) { // data length + call no + timestamp 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; } else { // call no + 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)(data.length() >> 8); m_data[m_dataAddIdx++] = (u_int8_t)data.length(); } memcpy(m_data + m_dataAddIdx,data.data(),data.length()); m_dataAddIdx += data.length(); return data.length(); } // Send this frame to remote peer bool IAXMetaTrunkFrame::doSend(const Time& now, bool onTime) { #define IAX2_TRUNKDATA_DELTA 160 bool dontSend = (m_dataAddIdx <= IAX2_TRUNKFRAME_HEADERLENGTH); u_int64_t ellapsed = (now - m_timeStamp) / 1000; if (ellapsed <= 0xffffffff) { // Sent on time: set timestamp from send interval // Sent on buffer full: set timestamp from ellapsed time u_int32_t ts = 0; if (onTime) { setSendTime(now); ts = m_lastSentTs + m_sendInterval; if (ts != ellapsed) { // Adjust timestamp if (ts > ellapsed) { if ((ts - ellapsed) >= IAX2_TRUNKDATA_DELTA) ts = (u_int32_t)ellapsed; } else if ((ellapsed - ts) >= IAX2_TRUNKDATA_DELTA) ts = (u_int32_t)ellapsed; } } else ts = (u_int32_t)ellapsed; if (ts > m_lastSentTs || dontSend) m_lastSentTs = ts; else m_lastSentTs++; } else { // Timestamp wraparound: reset setTimestamp(now); m_lastSentTs = 0; } if (dontSend) return false; XDebug(m_engine,DebugAll,"Trunk frame '%s:%d' sending %u tStamp=%u calls=%u [%p]", m_addr.host().c_str(),m_addr.port(),m_dataAddIdx,m_lastSentTs,m_calls,this); setTimestamp(m_lastSentTs); bool b = m_engine->writeSocket(m_data,m_dataAddIdx,m_addr); m_dataAddIdx = IAX2_TRUNKFRAME_HEADERLENGTH; return b; } /* vi: set ts=8 sw=4 sts=4 noet: */