/** * q931.cpp * This file is part of the YATE Project http://YATE.null.ro * * Yet Another Signalling Stack - implements the support for SS7, ISDN and PSTN * * Yet Another Telephony Engine - a fully featured software PBX and IVR * Copyright (C) 2004-2006 Null Team * * 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 "yatesig.h" #include using namespace TelEngine; /** * DEFINEs controlling Q.931 implementation * Q931_ACCEPT_RESTART * Controls acceptance of RESTART and RESTART ACK messages even if they don't have the global call reference * Yes: Accept anyway * No: Don't accept these messages if they don't have the global call reference */ #ifndef Q931_ACCEPT_RESTART // #define Q931_ACCEPT_RESTART #endif #define Q931_MSG_PROTOQ931 0x08 // Q.931 protocol discriminator in the message header // Clear the bit 7 for each byte in a buffer static inline void clearBit7(const void* buffer, u_int32_t len) { u_int8_t* data = (u_int8_t*)buffer; for (u_int32_t i = 0; i < len; i++) data[i] &= 0x7f; } // Dump data to a given parameter of a named list. Clear bit 7 if requested static inline void dumpDataBit7(NamedList* dest, const void* data, u_int32_t len, bool keepBit7 = true, const char* name = "unparsed-data") { String tmp((const char*)data,len); if (!keepBit7) clearBit7(tmp.c_str(),tmp.length()); dest->addParam(name,tmp); } // Fill a message header. header parameter must be large enough to store message header // Return header length static inline u_int8_t fillHeader(u_int8_t* header, ISDNQ931Message* msg, DebugEnabler* dbg) { header[0] = Q931_MSG_PROTOQ931; // Dummy call reference ? if (msg->dummyCallRef()) { header[1] = 0; header[2] = msg->type() & 0x7f; // Message type. Bit 7 must be 0 return 3; } // Check message's call reference length if (!msg->callRefLen() || msg->callRefLen() > 4) { Debug(dbg,DebugNote, "Can't encode message (%p) with call reference length %u", msg,msg->callRefLen()); return 0; } // Call reference length header[1] = 0x0f & msg->callRefLen(); // Set call reference field // For the initiator, bit 7 of the first byte of call reference must be 0 header[2] = msg->initiator() ? 0 : 0x80; u_int8_t len = 2; u_int8_t shift = msg->callRefLen() * 8; do { shift -= 8; header[len++] |= (u_int8_t)(msg->callRef() >> shift); } while (shift); // Set message type. Bit 7 must be 0 header[len++] = msg->type() & 0x7f; return len; } /** * IEParam * Q.931 message IE parameter description */ struct IEParam { public: inline const char* addParam(NamedList* dest, u_int8_t data, const char* defVal = 0) const { const char* tmp = lookup(data & mask,values,defVal); if (tmp) dest->addParam(name,tmp); return tmp; } inline bool addBoolParam(NamedList* dest, u_int8_t data, bool toggle) const { bool result = toggle ^ ((data & mask) != 0); dest->addParam(name,String::boolText(result)); return result; } inline void addIntParam(NamedList* dest, u_int8_t data) const { if (!addParam(dest,data)) dest->addParam(name,String((unsigned int)(data & mask))); } inline void dumpData(NamedList* dest, const u_int8_t* data, u_int32_t len) const { SignallingUtils::dumpData(0,*dest,name,data,len); } inline void dumpDataBit7(NamedList* dest, const u_int8_t* data, u_int32_t len, bool keepBit7) const { ::dumpDataBit7(dest,(const void*)data,len,keepBit7,name); } inline int getValue(NamedList* ns, bool applyMask = true, int defVal = 0) const { int tmp = lookup(ns->getValue(name),values,defVal); if (applyMask) tmp &= mask; return tmp; } const char* name; u_int8_t mask; const TokenDict* values; }; /** * Q931Parser * Q.931 message encoder/decoder */ class Q931Parser { public: inline Q931Parser(ISDNQ931ParserData& data) : m_settings(&data), m_msg(0), m_codeset(0), m_activeCodeset(0), m_skip(false) {} // Decode received data. // If the message is a SEGMENT decode only the header and the first IE. // If valid, fill the buffer with the rest of the message. If segData is 0, drop the message. // @param segData Segment message data // @return Valid ISDNQ931Message pointer on success or 0. ISDNQ931Message* decode(const DataBlock& buffer, DataBlock* segData); // Encode a message. // If the message is longer then max allowed and segmentation is allowed, split it into SEGMENT messages // Failure reasons: // Message too long and segmentation not allowed // Message too long, segmentation allowed, but too many segments // @param msg The message to encode. // @param dest List of DataBlock with the message segments. // @return The number of segments on success or 0 on failure. u_int8_t encode(ISDNQ931Message* msg, ObjList& dest); // Field names static TokenDict s_dict_congestion[]; static TokenDict s_dict_bearerTransCap[]; static TokenDict s_dict_bearerTransMode[]; static TokenDict s_dict_bearerTransRate[]; static TokenDict s_dict_bearerProto1[]; static TokenDict s_dict_bearerProto2[]; static TokenDict s_dict_bearerProto3[]; static TokenDict s_dict_typeOfNumber[]; static TokenDict s_dict_numPlan[]; static TokenDict s_dict_presentation[]; static TokenDict s_dict_screening[]; static TokenDict s_dict_subaddrType[]; static TokenDict s_dict_causeClass[]; static TokenDict s_dict_causeCause[]; static TokenDict s_dict_channelIDSelect_BRI[]; static TokenDict s_dict_channelIDSelect_PRI[]; static TokenDict s_dict_channelIDUnits[]; static TokenDict s_dict_loLayerProto2[]; static TokenDict s_dict_loLayerProto3[]; static TokenDict s_dict_networkIdType[]; static TokenDict s_dict_networkIdPlan[]; static TokenDict s_dict_notification[]; static TokenDict s_dict_progressDescr[]; static TokenDict s_dict_restartClass[]; static TokenDict s_dict_signalValue[]; private: // Encode a full message. Parameter ieEncoded is true if the IEs buffers are already filled // Check if the message fits the maximum length // Return 1 on success and 0 on failure u_int8_t encodeMessage(ObjList& dest, bool ieEncoded, u_int8_t* header, u_int8_t headerLen); // Encode each IE into it's buffer // Check if the largest buffer fits the maximum message length bool encodeIEList(bool& segmented, u_int8_t headerLen); // Append a segment buffer to a list. Increase the segment counter // Check if the counter is valid (don't exceed the maximum segments count) bool appendSegment(ObjList& dest, DataBlock* segment, u_int8_t& count); // Reset data. Returns the message inline ISDNQ931Message* reset() { ISDNQ931Message* msg = m_msg; m_msg = 0; m_activeCodeset = m_codeset = 0; return msg; } // Reset data. Returns the value inline u_int8_t reset(u_int8_t val) { m_msg = 0; m_activeCodeset = m_codeset = 0; return val; } // Encode an IE to a buffer // Return false on failure bool encodeIE(ISDNQ931IE* ie, DataBlock& buffer); // Add an error parameter to a given IE ISDNQ931IE* errorParseIE(ISDNQ931IE* ie, const char* reason, const u_int8_t* data, u_int32_t len); // Check the encoding a given IE. Before checking apply a 0x60 mask // Add a parameter if the check fails bool checkCoding(u_int8_t value, u_int8_t expected, ISDNQ931IE* ie); // Skip data until an element with bit 7 (0/1 ext) set is found. Skip this one too // Parameter 'crt' is modified in the process. On exit points to the element which won't be skipped // Return the number of element to skip u_int8_t skipExt(const u_int8_t* data, u_int8_t len, u_int8_t& crt); // Parse the received data to get the message header. Create message on success // @return False to stop the parser bool createMessage(u_int8_t* data, u_int32_t len); // Process received Segment message ISDNQ931Message* processSegment(const u_int8_t* data, u_int32_t len, DataBlock* segData); // Parse the received data to get an IE // @param data The data to parse // @param len Data length // @param consumed The number of bytes consumed by the IE // @return Pointer to a valid IE or 0 to stop the parser ISDNQ931IE* getIE(const u_int8_t* data, u_int32_t len, u_int32_t& consumed); // Constructs a fixed (1 byte) length IE // @param data The data // @return Valid ISDNQ931IE pointer ISDNQ931IE* getFixedIE(u_int8_t data); // Shift the codeset while parsing // @param ie Pointer to a valid ISDNQ931IE of type Shift void shiftCodeset(const ISDNQ931IE* ie); // Common methods for decoding Bearer capabilities and Low layer compatibility void decodeLayer1(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len, u_int8_t& crt, const IEParam* ieParam, u_int8_t ieParamIdx); void decodeLayer2(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len, u_int8_t& crt, const IEParam* ieParam, u_int8_t ieParamIdx); void decodeLayer3(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len, u_int8_t& crt, const IEParam* ieParam, u_int8_t ieParamIdx); // Decode the corresponding variable IE ISDNQ931IE* decodeBearerCaps(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeCause(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeCallIdentity(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeCallState(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeChannelID(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeProgress(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeNetFacility(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeNotification(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeDisplay(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeDateTime(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeKeypad(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeSignal(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeCallingNo(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeCallingSubAddr(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeCalledNo(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeCalledSubAddr(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeRestart(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeSegmented(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeNetTransit(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeLoLayerCompat(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeHiLayerCompat(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); ISDNQ931IE* decodeUserUser(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len); // It seems that the Connected number has the same layout as the Calling number IE inline ISDNQ931IE* decodeConnectedNo(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { return decodeCallingNo(ie,data,len); } // Encode the corresponding variable IE bool encodeBearerCaps(ISDNQ931IE* ie, DataBlock& buffer); bool encodeCause(ISDNQ931IE* ie, DataBlock& buffer); bool encodeCallState(ISDNQ931IE* ie, DataBlock& buffer); bool encodeChannelID(ISDNQ931IE* ie, DataBlock& buffer); bool encodeDisplay(ISDNQ931IE* ie, DataBlock& buffer); bool encodeCallingNo(ISDNQ931IE* ie, DataBlock& buffer); bool encodeCalledNo(ISDNQ931IE* ie, DataBlock& buffer); bool encodeProgress(ISDNQ931IE* ie, DataBlock& buffer); bool encodeNotification(ISDNQ931IE* ie, DataBlock& buffer); bool encodeKeypad(ISDNQ931IE* ie, DataBlock& buffer); bool encodeSignal(ISDNQ931IE* ie, DataBlock& buffer); bool encodeRestart(ISDNQ931IE* ie, DataBlock& buffer); bool encodeSendComplete(ISDNQ931IE* ie, DataBlock& buffer); ISDNQ931ParserData* m_settings; // Settings ISDNQ931Message* m_msg; // Current encoded/decoded message u_int8_t m_codeset; // Current codeset u_int8_t m_activeCodeset; // Active codeset bool m_skip; // Skip current IE }; /** * ISDNQ931IEData */ ISDNQ931IEData::ISDNQ931IEData() : m_bri(false), m_channelMandatory(true), m_channelByNumber(true) { } bool ISDNQ931IEData::processBearerCaps(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { ISDNQ931IE* ie = new ISDNQ931IE(ISDNQ931IE::BearerCaps); ie->addParam("transfer-cap",m_transferCapability); ie->addParam("transfer-mode",m_transferMode); ie->addParam("transfer-rate",m_transferRate); ie->addParam("layer1-protocol",m_format); // Q.931 Table 4.6: Send Layer 2/3 only in 'packet switching' (0x40) mode if (m_transferMode == lookup(0x40,Q931Parser::s_dict_bearerTransMode)) { ie->addParam("layer2-protocol","q921"); ie->addParam("layer3-protocol","q931"); } msg->appendSafe(ie); return true; } ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::BearerCaps); if (!ie) { m_transferCapability = ""; m_transferMode = ""; m_transferRate = ""; return false; } m_transferCapability = ie->getValue("transfer-cap"); m_transferMode = ie->getValue("transfer-mode"); m_transferRate = ie->getValue("transfer-rate"); m_format = ie->getValue("layer1-protocol"); return true; } bool ISDNQ931IEData::processChannelID(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { ISDNQ931IE* ie = new ISDNQ931IE(ISDNQ931IE::ChannelID); ie->addParam("interface-bri",String::boolText(m_bri)); ie->addParam("channel-exclusive",String::boolText(m_channelMandatory)); ie->addParam("channel-select",m_channelSelect); ie->addParam("type",m_channelType); ie->addParam("channel-by-number",String::boolText(true)); ie->addParam("channels",m_channels); msg->appendSafe(ie); return true; } ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::ChannelID); m_channels = ""; if (!ie) { m_bri = m_channelMandatory = m_channelByNumber = false; return false; } m_bri = ie->getBoolValue("interface-bri"); // Done for basic rate interface if (m_bri) return true; m_channelMandatory = ie->getBoolValue("channel-exclusive"); m_channelByNumber = ie->getBoolValue("channel-by-number"); m_channelType = ie->getValue("type"); m_channelSelect = ie->getValue("channel-select"); // ChannelID IE may repeat if channel is given by number if (m_channelByNumber) for (; ie; ie = msg->getIE(ISDNQ931IE::ChannelID,ie)) m_channels.append(ie->getValue("channels"),","); else m_channels = ie->getValue("slot-map"); return true; } bool ISDNQ931IEData::processProgress(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { // Remove non-isdn-source/non-isdn-destination if (data) { if (!data->flag(ISDNQ931::SendNonIsdnSource)) SignallingUtils::removeFlag(m_progress,"non-isdn-source"); if (data->flag(ISDNQ931::IgnoreNonIsdnDest)) SignallingUtils::removeFlag(m_progress,"non-isdn-destination"); } if (!m_progress.null()) msg->appendIEValue(ISDNQ931IE::Progress,"description",m_progress); } else { // Progress may repeat ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::Progress); for (; ie; ie = msg->getIE(ISDNQ931IE::Progress,ie)) m_progress.append(ie->getValue("description"),","); } return !m_progress.null(); } bool ISDNQ931IEData::processRestart(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { msg->appendIEValue(ISDNQ931IE::Restart,"class",m_restart); return true; } m_restart = msg->getIEValue(ISDNQ931IE::Restart,"class"); return !m_restart.null(); } bool ISDNQ931IEData::processNotification(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { if (data && data->flag(ISDNQ931::CheckNotifyInd)) { int val = lookup(m_notification,Q931Parser::s_dict_notification,-1); if (val < 0 && val > 2) return false; } msg->appendIEValue(ISDNQ931IE::Notification,"notification",m_notification); return true; } m_notification = msg->getIEValue(ISDNQ931IE::Notification,"notification"); return !m_notification.null(); } bool ISDNQ931IEData::processCalledNo(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { ISDNQ931IE* ie = new ISDNQ931IE(ISDNQ931IE::CalledNo); ie->addParam("number",m_calledNo); if (!m_callerType.null()) ie->addParam("type",m_calledType); if (!m_callerPlan.null()) ie->addParam("plan",m_calledPlan); msg->appendSafe(ie); return true; } ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::CalledNo); if (!ie) { m_calledNo = ""; return false; } m_calledNo = ie->getValue("number"); m_calledType = ie->getValue("type"); m_calledPlan = ie->getValue("plan"); return true; } bool ISDNQ931IEData::processCallingNo(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { if (!m_callerNo) return false; ISDNQ931IE* ie = new ISDNQ931IE(ISDNQ931IE::CallingNo); ie->addParam("number",m_callerNo); if (!m_callerType.null()) ie->addParam("type",m_callerType); if (!m_callerPlan.null()) ie->addParam("plan",m_callerPlan); if (data && data->flag(ISDNQ931::ForcePresNetProv)) { ie->addParam("presentation",lookup(0x00,Q931Parser::s_dict_presentation)); ie->addParam("screening",lookup(0x03,Q931Parser::s_dict_screening)); } else { ie->addParam("presentation",m_callerPres); ie->addParam("screening",m_callerScreening); } msg->appendSafe(ie); return true; } ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::CallingNo); if (!ie) { m_callerNo = ""; return false; } m_callerNo = ie->getValue("number"); m_callerType = ie->getValue("type"); m_callerPlan = ie->getValue("plan"); m_callerPres = ie->getValue("presentation"); m_callerScreening = ie->getValue("screening"); return true; } bool ISDNQ931IEData::processCause(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { msg->appendIEValue(ISDNQ931IE::Cause,"cause",m_reason); return true; } m_reason = msg->getIEValue(ISDNQ931IE::Cause,"cause"); return !m_reason.null(); } bool ISDNQ931IEData::processDisplay(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { if (m_display.null() || !data || data->flag(ISDNQ931::NoDisplayIE)) return false; msg->appendIEValue(ISDNQ931IE::Display,"display",m_display); return true; } m_display = msg->getIEValue(ISDNQ931IE::Display,"display"); return !m_display.null(); } bool ISDNQ931IEData::processKeypad(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { msg->appendIEValue(ISDNQ931IE::Keypad,"keypad",m_keypad); return true; } m_keypad = msg->getIEValue(ISDNQ931IE::Keypad,"keypad"); return !m_keypad.null(); } /** * ISDNQ931State */ TokenDict ISDNQ931State::s_states[] = { {"Null", Null}, {"CallInitiated", CallInitiated}, {"OverlapSend", OverlapSend}, {"OutgoingProceeding", OutgoingProceeding}, {"CallDelivered", CallDelivered}, {"CallPresent", CallPresent}, {"CallReceived", CallReceived}, {"ConnectReq", ConnectReq}, {"IncomingProceeding", IncomingProceeding}, {"Active", Active}, {"DisconnectReq", DisconnectReq}, {"DisconnectIndication", DisconnectIndication}, {"SuspendReq", SuspendReq}, {"ResumeReq", ResumeReq}, {"ReleaseReq", ReleaseReq}, {"CallAbort", CallAbort}, {"OverlapRecv", OverlapRecv}, {"RestartReq", RestartReq}, {"Restart", Restart}, {0,0} }; bool ISDNQ931State::checkStateRecv(int type, bool* retrans) { #define STATE_CHECK_RETRANS(st) \ if (state() == st) { \ if (retrans) \ *retrans = true; \ return false; \ } switch (type) { case ISDNQ931Message::Setup: STATE_CHECK_RETRANS(CallPresent) if (state() != Null) break; return true; case ISDNQ931Message::SetupAck: STATE_CHECK_RETRANS(OverlapSend) if (state() != CallInitiated) break; return true; case ISDNQ931Message::Proceeding: STATE_CHECK_RETRANS(OutgoingProceeding) if (state() != CallInitiated && state() != OverlapSend) break; return true; case ISDNQ931Message::Alerting: STATE_CHECK_RETRANS(CallDelivered) if (state() != CallInitiated && state() != OutgoingProceeding) break; return true; case ISDNQ931Message::Connect: STATE_CHECK_RETRANS(Active) if (state() != CallInitiated && state() != OutgoingProceeding && state() != CallDelivered) break; return true; case ISDNQ931Message::ConnectAck: STATE_CHECK_RETRANS(Active) if (state() != ConnectReq && state() != Active) break; return true; case ISDNQ931Message::Disconnect: STATE_CHECK_RETRANS(DisconnectIndication) switch (state()) { case CallInitiated: case OutgoingProceeding: case CallDelivered: case CallPresent: case CallReceived: case ConnectReq: case IncomingProceeding: case Active: return true; default: ; } break; default: if (state() == Null) break; return true; } return false; #undef STATE_CHECK_RETRANS } bool ISDNQ931State::checkStateSend(int type) { switch (type) { case ISDNQ931Message::Setup: if (state() != Null) break; return true; case ISDNQ931Message::SetupAck: if (state() != CallPresent) break; return true; case ISDNQ931Message::Proceeding: if (state() != CallPresent && state() != OverlapRecv) break; return true; case ISDNQ931Message::Alerting: if (state() != CallPresent && state() != IncomingProceeding) break; return true; case ISDNQ931Message::Connect: if (state() != CallPresent && state() != IncomingProceeding && state() != CallReceived) break; return true; case ISDNQ931Message::Disconnect: switch (state()) { case OutgoingProceeding: case CallDelivered: case CallPresent: case CallReceived: case ConnectReq: case IncomingProceeding: case Active: return true; default: ; } break; case ISDNQ931Message::Progress: if (state() != CallPresent && state() != CallReceived && state() != IncomingProceeding) break; return true; default: if (state() == Null) break; return true; } return false; } /** * ISDNQ931Call */ #define Q931_CALL_ID this->outgoing(),this->callRef() ISDNQ931Call::ISDNQ931Call(ISDNQ931* controller, bool outgoing, u_int32_t callRef, u_int8_t callRefLen) : SignallingCall(controller,outgoing), m_callRef(callRef), m_callRefLen(callRefLen), m_circuit(0), m_circuitChange(false), m_channelIDSent(false), m_rspBearerCaps(false), m_discTimer(0), m_relTimer(0), m_conTimer(0), m_terminate(false), m_destroy(false) { Debug(q931(),DebugAll,"Call(%u,%u) direction=%s [%p]", Q931_CALL_ID,(outgoing ? "outgoing" : "incoming"),this); if (!controller) { Debug(DebugWarn,"ISDNQ931Call(%u,%u). No call controller. Terminate [%p]", Q931_CALL_ID,this); m_terminate = m_destroy = true; m_data.m_reason = "temporary-failure"; return; } // Init timers q931()->setInterval(m_discTimer,305); q931()->setInterval(m_relTimer,308); q931()->setInterval(m_conTimer,313); } ISDNQ931Call::~ISDNQ931Call() { q931()->releaseCircuit(m_circuit); if (state() != Null) sendReleaseComplete("temporary-failure"); Debug(q931(),DebugAll,"Call(%u,%u) destroyed with reason '%s' [%p]", Q931_CALL_ID,m_data.m_reason.c_str(),this); } // Set terminate flags and reason void ISDNQ931Call::setTerminate(bool destroy, const char* reason) { Lock lock(m_callMutex); if (state() == CallAbort) changeState(Null); // Check terminate & destroy flags if (m_terminate && destroy == m_destroy) return; m_terminate = true; m_destroy = destroy; if (m_data.m_reason.null()) m_data.m_reason = reason; DDebug(q931(),DebugInfo, "Call(%u,%u). Set terminate. Destroy: %s [%p]", Q931_CALL_ID,String::boolText(m_destroy),this); } // Send an event bool ISDNQ931Call::sendEvent(SignallingEvent* event) { if (!event) return false; Lock lock(m_callMutex); bool retVal = false; if (m_terminate || state() == CallAbort) { delete event; return false; } switch (event->type()) { case SignallingEvent::Progress: retVal = sendProgress(event->message()); break; case SignallingEvent::Ringing: retVal = sendAlerting(event->message()); break; case SignallingEvent::Accept: retVal = sendCallProceeding(event->message()); break; case SignallingEvent::Answer: retVal = sendConnect(event->message()); break; case SignallingEvent::Release: switch (state()) { case DisconnectIndication: retVal = sendRelease(0,event->message()); break; case OutgoingProceeding: case CallDelivered: case CallPresent: case CallReceived: case ConnectReq: case IncomingProceeding: case Active: retVal = sendDisconnect(event->message()); break; case Null: case ReleaseReq: case CallAbort: // Schedule destroy m_terminate = m_destroy = true; delete event; return false; default: m_terminate = m_destroy = true; retVal = sendReleaseComplete(event->message() ? event->message()->params().getValue("reason") : 0); break; } break; case SignallingEvent::Info: retVal = sendInfo(event->message()); break; case SignallingEvent::NewCall: retVal = sendSetup(event->message()); break; default: Debug(q931(),DebugStub, "Call(%u,%u). sendEvent not implemented for event '%s' [%p]", Q931_CALL_ID,event->name(),this); } delete event; return retVal; } // Process received messages. Generate events from them // Get events from reserved circuit when no call event SignallingEvent* ISDNQ931Call::getEvent(const Time& when) { Lock lock(m_callMutex); // Check for last event or aborting if (m_lastEvent || state() == CallAbort) return 0; // Check for incoming messages ISDNQ931Message* msg = static_cast(dequeue()); // No message: check terminate and timeouts. Try to get a circuit event if (!msg) { if (m_terminate) m_lastEvent = processTerminate(); if (!m_lastEvent) m_lastEvent = checkTimeout(when.msec()); if (!m_lastEvent) m_lastEvent = getCircuitEvent(when); return m_lastEvent; } XDebug(q931(),DebugAll, "Call(%u,%u). Dequeued message (%p): '%s' in state '%s' [%p]", Q931_CALL_ID,msg,msg->name(),stateName(state()),this); // Check for unknown madatory IE. See Q.931 7.8.7.1 if (msg->unknownMandatory()) { Debug(q931(),DebugWarn, "Call(%u,%u). Received message (%p): '%s' with unknown mandatory IE [%p]", Q931_CALL_ID,msg,msg->name(),this); msg->deref(); m_lastEvent = releaseComplete("missing-mandatory-ie"); return m_lastEvent; } switch (msg->type()) { #define Q931_CALL_PROCESS_MSG(type,method) \ case type: \ m_lastEvent = !m_terminate ? method(msg) : processTerminate(msg); \ break; Q931_CALL_PROCESS_MSG(ISDNQ931Message::Alerting,processMsgAlerting) Q931_CALL_PROCESS_MSG(ISDNQ931Message::Proceeding,processMsgCallProceeding) Q931_CALL_PROCESS_MSG(ISDNQ931Message::Connect,processMsgConnect) Q931_CALL_PROCESS_MSG(ISDNQ931Message::ConnectAck,processMsgConnectAck) Q931_CALL_PROCESS_MSG(ISDNQ931Message::Disconnect,processMsgDisconnect) Q931_CALL_PROCESS_MSG(ISDNQ931Message::Info,processMsgInfo) Q931_CALL_PROCESS_MSG(ISDNQ931Message::Notify,processMsgNotify) Q931_CALL_PROCESS_MSG(ISDNQ931Message::Progress,processMsgProgress) Q931_CALL_PROCESS_MSG(ISDNQ931Message::Release,processMsgRelease) Q931_CALL_PROCESS_MSG(ISDNQ931Message::ReleaseComplete,processMsgRelease) Q931_CALL_PROCESS_MSG(ISDNQ931Message::Setup,processMsgSetup) Q931_CALL_PROCESS_MSG(ISDNQ931Message::SetupAck,processMsgSetupAck) Q931_CALL_PROCESS_MSG(ISDNQ931Message::Status,processMsgStatus) Q931_CALL_PROCESS_MSG(ISDNQ931Message::StatusEnquiry,processMsgStatusEnquiry) #undef Q931_CALL_PROCESS_MSG case ISDNQ931Message::Suspend: sendSuspendRej("service-not-implemented",0); break; case ISDNQ931Message::Resume: q931()->sendStatus(this,"no-call-suspended"); break; case ISDNQ931Message::SuspendAck: case ISDNQ931Message::SuspendRej: case ISDNQ931Message::ResumeAck: case ISDNQ931Message::ResumeRej: q931()->sendStatus(this,"wrong-state-message"); break; default: DDebug(q931(),DebugNote, "Call(%u,%u). Received unknown/not implemented message '%s'. Sending status [%p]", Q931_CALL_ID,msg->name(),this); q931()->sendStatus(this,"unknown-message"); // Fall through to deref the message and check timeouts } msg->deref(); if (!m_lastEvent) m_lastEvent = checkTimeout(when.msec()); if (!m_lastEvent) m_lastEvent = getCircuitEvent(when); return m_lastEvent; } // Get reserved circuit or this object void* ISDNQ931Call::getObject(const String& name) const { if (name == "SignallingCircuit") return m_circuit; if (name == "ISDNQ931Call") return (void*)this; return SignallingCall::getObject(name); } // Data link change state notification from call controller // Set termination flag. Send status if link is up void ISDNQ931Call::dataLinkState(bool up) { Lock lock(m_callMutex); // Q.931 5.8.9. Terminate if not up and not in the active state if (!up) { if (state() != ISDNQ931Call::Active) setTerminate(true,"net-out-of-order"); return; } // Q.931 5.8.8 Terminate in state OverlapSend and OverlapRecv if (state() == ISDNQ931Call::OverlapSend || state() == ISDNQ931Call::OverlapRecv) { setTerminate(true,"temporary-failure"); } q931()->sendStatus(this,"normal"); } // Process termination flags or requests (messages) SignallingEvent* ISDNQ931Call::processTerminate(ISDNQ931Message* msg) { bool complete = m_destroy; // We don't have to destroy and not send/received Release: Send Release if (!m_destroy && state() != ReleaseReq && state() != DisconnectReq) complete = false; // Message is Release/ReleaseComplete: terminate if (msg) { if (msg->type() == ISDNQ931Message::Release || msg->type() == ISDNQ931Message::ReleaseComplete) { changeState(Null); m_data.processCause(msg,false); complete = true; } else DDebug(q931(),DebugNote, "Call(%u,%u). Dropping received message '%s' while terminating [%p]", Q931_CALL_ID,msg->name(),this); } if (complete) return releaseComplete(); sendRelease(); return 0; } // Check message timeout for Connect, Disconnect, Release SignallingEvent* ISDNQ931Call::checkTimeout(u_int64_t time) { #define CALL_TIMEOUT_DEBUG(info) \ DDebug(q931(),DebugNote, \ "Call(%u,%u). %s request timed out in state '%s' [%p]", \ Q931_CALL_ID,info,stateName(state()),this); static const char* reason = "timeout"; switch (state()) { case DisconnectReq: if (!m_discTimer.timeout(time)) break; CALL_TIMEOUT_DEBUG("Disconnect") m_discTimer.stop(); sendRelease(reason); break; case ReleaseReq: if (!m_relTimer.timeout(time)) break; CALL_TIMEOUT_DEBUG("Release") m_relTimer.stop(); changeState(Null); return releaseComplete(reason); case ConnectReq: if (!m_conTimer.timeout(time)) break; CALL_TIMEOUT_DEBUG("Connect") m_conTimer.stop(); m_data.m_reason = reason; sendDisconnect(0); break; default: ; } return 0; #undef CALL_TIMEOUT_DEBUG } // Check received messages for appropriate state or retransmission // Send status if not accepted and requested by the caller bool ISDNQ931Call::checkMsgRecv(ISDNQ931Message* msg, bool status) { bool retrans = false; if (checkStateRecv(msg->type(),&retrans)) return true; if (retrans) XDebug(q931(),DebugAll, "Call(%u,%u). Dropping '%s' retransmission in state '%s' [%p]", Q931_CALL_ID,msg->name(),stateName(state()),this); else { Debug(q931(),DebugNote, "Call(%u,%u). Received '%s'. Invalid in state '%s'. Drop [%p]", Q931_CALL_ID,msg->name(),stateName(state()),this); if (status && state() != Null) q931()->sendStatus(this,"wrong-state-message"); } return false; } // Process ALERTING. See Q.931 3.1.1 // IE: BearerCaps, ChannelID, Progress, Display, Signal, HiLayerCompat SignallingEvent* ISDNQ931Call::processMsgAlerting(ISDNQ931Message* msg) { if (!checkMsgRecv(msg,true)) return 0; if (m_data.processChannelID(msg,false) && !reserveCircuit()) return releaseComplete(); // Notify format and circuit change if (m_circuitChange) { m_circuitChange = false; msg->params().setParam("circuit-change",String::boolText(true)); } if (m_data.processBearerCaps(msg,false) && !m_data.m_format.null()) msg->params().setParam("format",m_data.m_format); changeState(CallDelivered); return new SignallingEvent(SignallingEvent::Ringing,msg,this); } // Process CALL PROCEEDING. See Q.931 3.1.2 // IE: BearerCaps, ChannelID, Progress, Display, HiLayerCompat SignallingEvent* ISDNQ931Call::processMsgCallProceeding(ISDNQ931Message* msg) { if (!checkMsgRecv(msg,true)) return 0; if (m_data.processChannelID(msg,false) && !reserveCircuit()) return releaseComplete(); // Notify format and circuit change if (m_circuitChange) { m_circuitChange = false; msg->params().setParam("circuit-change",String::boolText(true)); } if (m_data.processBearerCaps(msg,false) && !m_data.m_format.null()) msg->params().setParam("format",m_data.m_format); changeState(OutgoingProceeding); return new SignallingEvent(SignallingEvent::Accept,msg,this); } // Process CONNECT. See Q.931 3.1.3 // IE: BearerCaps, ChannelID, Progress, Display, DateTime, Signal, LoLayerCompat, HiLayerCompat SignallingEvent* ISDNQ931Call::processMsgConnect(ISDNQ931Message* msg) { if (!checkMsgRecv(msg,true)) return 0; if (m_data.processChannelID(msg,false) && !reserveCircuit()) return releaseComplete(); // This is the last time we can receive a circuit. Check if we reserved one if (!m_circuit) return releaseComplete("invalid-message"); // Notify format and circuit change if (m_circuitChange) { m_circuitChange = false; msg->params().setParam("circuit-change",String::boolText(true)); } if (m_data.processBearerCaps(msg,false) && !m_data.m_format.null()) msg->params().setParam("format",m_data.m_format); changeState(ConnectReq); SignallingEvent* event = new SignallingEvent(SignallingEvent::Answer,msg,this); sendConnectAck(0); return event; } // Process CONNECT ACK. See Q.931 3.1.4 // IE: Display, Signal SignallingEvent* ISDNQ931Call::processMsgConnectAck(ISDNQ931Message* msg) { m_conTimer.stop(); // Check if we've changed state to Active when sent Connect bool yes = q931() && !q931()->parserData().flag(ISDNQ931::NoActiveOnConnect); if (yes && state() == Active) return 0; if (!checkMsgRecv(msg,false)) return 0; changeState(Active); return 0; } // Process DISCONNECT. See Q.931 3.1.5 // IE: Cause, Progress, Display, Signal SignallingEvent* ISDNQ931Call::processMsgDisconnect(ISDNQ931Message* msg) { m_discTimer.stop(); if (!checkMsgRecv(msg,false)) return 0; changeState(DisconnectIndication); if (m_data.processCause(msg,false)) msg->params().setParam("reason",m_data.m_reason); return new SignallingEvent(SignallingEvent::Release,msg,this); } // Process INFORMATION. See Q.931 3.1.6 // IE: SendComplete, Display, Keypad, Signal, CalledNo SignallingEvent* ISDNQ931Call::processMsgInfo(ISDNQ931Message* msg) { // Check complete bool complete = (0 != msg->getIE(ISDNQ931IE::SendComplete)); msg->params().addParam("complete",String::boolText(complete)); // Display m_data.processDisplay(msg,false); // Check tones const char* tone = msg->getIEValue(ISDNQ931IE::CalledNo,"number"); if (!tone) tone = msg->getIEValue(ISDNQ931IE::Keypad,"keypad"); if (tone) msg->params().addParam("tone",tone); return new SignallingEvent(SignallingEvent::Info,msg,this); } // Process NOTIFY. See Q.931 3.1.7 // IE: BearerCaps, Notification, Display SignallingEvent* ISDNQ931Call::processMsgNotify(ISDNQ931Message* msg) { m_data.processNotification(msg,false); DDebug(q931(),DebugNote, "Call(%u,%u). Received '%s' with '%s'='%s' [%p]", Q931_CALL_ID,msg->name(),ISDNQ931IE::typeName(ISDNQ931IE::Notification), m_data.m_notification.c_str(),this); return 0; } // Process PROGRESS. See Q.931 3.1.8 // IE: BearerCaps, Cause, Progress (mandatory), Display, HiLayerCompat SignallingEvent* ISDNQ931Call::processMsgProgress(ISDNQ931Message* msg) { // Q.931 says that we should ignore the message. We don't if (m_data.processProgress(msg,false)) { bool media = (-1 != m_data.m_progress.find("in-band-info")); msg->params().addParam("media",String::boolText(media)); } if (m_data.processCause(msg,false)) msg->params().setParam("reason",m_data.m_reason); if (m_data.processDisplay(msg,false)) msg->params().setParam("callername",m_data.m_display); return new SignallingEvent(SignallingEvent::Progress,msg,this);; } // Process RELEASE and RELEASE COMPLETE. See Q.931 3.1.9/3.1.10 // IE: Cause, Display, Signal SignallingEvent* ISDNQ931Call::processMsgRelease(ISDNQ931Message* msg) { m_discTimer.stop(); m_relTimer.stop(); m_conTimer.stop(); if (!checkMsgRecv(msg,false)) return 0; m_data.processCause(msg,false); if (m_data.m_reason.null()) m_data.m_reason = "normal-clearing"; msg->params().setParam("reason",m_data.m_reason); if (state() != ReleaseReq && msg->type() == ISDNQ931Message::Release) changeState(ReleaseReq); else changeState(Null); return releaseComplete(); } // Process SETUP. See Q.931 3.1.14 // IE: Repeat, BearerCaps, ChannelID, Progress, NetFacility, Display, // Keypad, Signal, CallingNo, CallingSubAddr, CalledNo, CalledSubAddr, // NetTransit, Repeat, LoLayerCompat, HiLayerCompat SignallingEvent* ISDNQ931Call::processMsgSetup(ISDNQ931Message* msg) { if (!checkMsgRecv(msg,true)) return 0; changeState(CallPresent); // *** BearerCaps. Mandatory if (!m_data.processBearerCaps(msg,false)) return errorNoIE(msg,ISDNQ931IE::BearerCaps,true); // Check for multiple BearerCaps ISDNQ931IE* bc = msg->getIE(ISDNQ931IE::BearerCaps); if (bc && msg->getIE(ISDNQ931IE::BearerCaps,bc)) m_rspBearerCaps = true; // Check if transfer mode is 'circuit' if (m_data.m_transferMode != "circuit") { Debug(q931(),DebugWarn, "Call(%u,%u). Invalid or missing transfer mode '%s'. Releasing call [%p]", Q931_CALL_ID,m_data.m_transferMode.c_str(),this); return errorWrongIE(msg,ISDNQ931IE::BearerCaps,true); } // *** ChannelID. Mandatory if (!msg->getIE(ISDNQ931IE::ChannelID)) return errorNoIE(msg,ISDNQ931IE::ChannelID,true); m_data.processChannelID(msg,false); // Check if channel contains valid BRI flag // TODO: Support BRI: check consistency with q931()->primaryRate() if (m_data.m_bri) { Debug(q931(),DebugWarn, "Call(%u,%u). Invalid interface (BRI not implemented). Releasing call [%p]", Q931_CALL_ID,this); return errorWrongIE(msg,ISDNQ931IE::ChannelID,true); } // Get a circuit from controller if (!reserveCircuit()) return releaseComplete(); m_circuit->updateFormat(m_data.m_format,0); // *** CalledNo /CallingNo m_data.processCalledNo(msg,false); m_data.processCallingNo(msg,false); // *** Display m_data.processDisplay(msg,false); // Set message parameters msg->params().setParam("caller",m_data.m_callerNo); msg->params().setParam("called",m_data.m_calledNo); msg->params().setParam("format",m_data.m_format); msg->params().setParam("callername",m_data.m_display); msg->params().setParam("callernumtype",m_data.m_callerType); msg->params().setParam("callernumplan",m_data.m_callerPlan); msg->params().setParam("callerpres",m_data.m_callerPres); msg->params().setParam("callerscreening",m_data.m_callerScreening); msg->params().setParam("callednumtype",m_data.m_calledType); msg->params().setParam("callednumplan",m_data.m_calledPlan); return new SignallingEvent(SignallingEvent::NewCall,msg,this); } // Process SETUP ACKNOLEDGE. See Q.931 3.1.14 // IE: ChannelID, Progress, Display, Signal SignallingEvent* ISDNQ931Call::processMsgSetupAck(ISDNQ931Message* msg) { if (!checkMsgRecv(msg,true)) return 0; if (!m_data.processChannelID(msg,false)) return errorWrongIE(msg,ISDNQ931IE::ChannelID,true); // We don't implement overlap sending. So, just complete the number sending SignallingMessage* m = new SignallingMessage; m->params().addParam("complete",String::boolText(true)); sendInfo(m); return 0; } // Process STATUS. See Q.931 3.1.15, 5.8.11 // Try to recover (retransmit) messages based on received status // IE: Cause, CallState, Display SignallingEvent* ISDNQ931Call::processMsgStatus(ISDNQ931Message* msg) { const char* s = msg->getIEValue(ISDNQ931IE::CallState,"state"); if (!m_data.processCause(msg,false)) m_data.m_reason = "unknown"; DDebug(q931(),DebugInfo, "Call(%u,%u). Received '%s' state='%s' peer-state='%s' cause='%s' [%p]", Q931_CALL_ID,msg->name(), stateName(state()),s,m_data.m_reason.c_str(),this); u_int8_t peerState = (u_int8_t)lookup(s,s_states,255); // Check for valid state if (peerState == 255) return 0; // Check for Null states (our's and peer's Null state) if (state() == Null) { if (peerState != Null) { // Change state to allow sending RELEASE COMPLETE changeState(CallAbort); sendReleaseComplete("wrong-state-message"); } return 0; } if (peerState == Null) { changeState(Null); return releaseComplete(); } // Check peer wrong states (these are states associated with dummy call reference) if (peerState == Restart || peerState == RestartReq) return releaseComplete("wrong-state-message"); // Check if we are releasing the call // Release the call, even if peer's state is a compatible one switch (state()) { case DisconnectReq: case DisconnectIndication: case SuspendReq: case ResumeReq: case ReleaseReq: case CallAbort: return releaseComplete("wrong-state-message"); default: ; } // Try to recover // This can be done only if we assume that the peer didn't saw our last message SignallingMessage* sigMsg = new SignallingMessage; bool recover = false; switch (state()) { case CallReceived: // Sent Alerting // Can recover if peer's state is OutgoingProceeding if (peerState == OutgoingProceeding) { changeState(IncomingProceeding); sendAlerting(sigMsg); recover = true; } break; case ConnectReq: // Sent Connect // Can recover if peer's state is OutgoingProceeding or CallDelivered // (saw our Alerting or Proceeding) if (peerState == OutgoingProceeding || peerState == CallDelivered) { changeState(CallReceived); sendConnect(sigMsg); recover = true; } break; case IncomingProceeding: // Sent Proceeding // Can recover if peer's state is CallInitiated // TODO: if overlap implemented: check if we received a Setup with full called number if (peerState == CallInitiated) { changeState(CallPresent); sendCallProceeding(sigMsg); recover = true; } break; case Active: // Incoming: received ConnectAck. Nothing to be done // Outgoing: Sent ConnectAck. Recover only if peer's state is ConnectReq if (outgoing() && peerState == ConnectReq) { changeState(ConnectReq); sendConnectAck(sigMsg); recover = true; } case CallInitiated: // We've sent Setup. Can't recover: something went wrong case OverlapSend: case OverlapRecv: // TODO: implement if overlap send/recv is implemented case CallDelivered: // Received Alerting. Sent nothing. Can't recover case CallPresent: // Received Setup. Sent nothing. Can't recover case OutgoingProceeding: // Received Proceeding. Sent nothing. Can't recover break; default: ; } sigMsg->deref(); if (!recover) return releaseComplete("wrong-state-message"); return 0; } // Process STATUS ENQUIRY. See Q.931 3.1.16, 5.8.10 // IE: Display SignallingEvent* ISDNQ931Call::processMsgStatusEnquiry(ISDNQ931Message* msg) { q931()->sendStatus(this,"status-enquiry-rsp"); return 0; } // Check if the state allows to send a message #define MSG_CHECK_SEND(type) \ if (!(q931() && checkStateSend(type))) { \ DDebug(q931(),DebugNote, \ "Call(%u,%u). Can't send msg='%s' in state='%s'. %s [%p]", \ Q931_CALL_ID,ISDNQ931Message::typeName(type), \ stateName(state()),(q931()?"Invalid state":"No call controller"),\ this); \ return false; \ } // Send ALERTING. See Q.931 3.1.1 // IE: BearerCaps, ChannelID, Progress, Display, Signal, HiLayerCompat bool ISDNQ931Call::sendAlerting(SignallingMessage* sigMsg) { MSG_CHECK_SEND(ISDNQ931Message::Alerting) const char* format = sigMsg ? sigMsg->params().getValue("format") : 0; if (format) m_data.m_format = format; // Change state, send message changeState(CallReceived); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Alerting,this); if (m_rspBearerCaps) { m_data.processBearerCaps(msg,true); m_rspBearerCaps = false; } if (!m_channelIDSent) { m_data.processChannelID(msg,true); m_channelIDSent = true; } return q931()->sendMessage(msg); } // Send CALL PROCEEDING. See Q.931 3.1.2 // IE: BearerCaps, ChannelID, Progress, Display, HiLayerCompat bool ISDNQ931Call::sendCallProceeding(SignallingMessage* sigMsg) { MSG_CHECK_SEND(ISDNQ931Message::Proceeding) // Change state, send message changeState(IncomingProceeding); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Proceeding,this); if (m_rspBearerCaps) { m_data.processBearerCaps(msg,true); m_rspBearerCaps = false; } if (!m_channelIDSent) { m_data.processChannelID(msg,true); m_channelIDSent = true; } return q931()->sendMessage(msg); } // Send CONNECT. See Q.931 3.1.3 // IE: BearerCaps, ChannelID, Progress, Display, DateTime, Signal, // LoLayerCompat, HiLayerCompat bool ISDNQ931Call::sendConnect(SignallingMessage* sigMsg) { MSG_CHECK_SEND(ISDNQ931Message::Connect) // Change state, start timer, send message if (q931()->parserData().flag(ISDNQ931::NoActiveOnConnect)) changeState(ConnectReq); else changeState(Active); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Connect,this); if (m_rspBearerCaps) { m_data.processBearerCaps(msg,true,&q931()->parserData()); m_rspBearerCaps = false; } if (!m_channelIDSent) { m_data.processChannelID(msg,true,&q931()->parserData()); m_channelIDSent = true; } // Progress indicator m_data.m_progress = sigMsg->params().getValue("call-progress"); m_data.processProgress(msg,true,&q931()->parserData()); m_conTimer.start(); return q931()->sendMessage(msg); } // Send CONNECT ACK. See Q.931 3.1.4 // IE: Display, Signal bool ISDNQ931Call::sendConnectAck(SignallingMessage* sigMsg) { MSG_CHECK_SEND(ISDNQ931Message::ConnectAck) // Change state, send message changeState(Active); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::ConnectAck,this); // Progress indicator if (sigMsg) { m_data.m_progress = sigMsg->params().getValue("call-progress"); m_data.processProgress(msg,true,&q931()->parserData()); } else m_data.m_progress = ""; return q931()->sendMessage(msg); } // Send DISCONNECT. See Q.931 3.1.5 // IE: Cause, Progress, Display, Signal bool ISDNQ931Call::sendDisconnect(SignallingMessage* sigMsg) { MSG_CHECK_SEND(ISDNQ931Message::Disconnect) m_data.m_reason = ""; if (sigMsg) m_data.m_reason = sigMsg->params().getValue("reason"); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Disconnect,this); m_data.processCause(msg,true); changeState(DisconnectReq); m_discTimer.start(); return q931()->sendMessage(msg); } // Send INFORMATION. See Q.931 3.1.6 // IE: SendComplete, Display, Keypad, Signal, CalledNo bool ISDNQ931Call::sendInfo(SignallingMessage* sigMsg) { if (!sigMsg) return false; MSG_CHECK_SEND(ISDNQ931Message::Info) ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Info,this); // Check send complete complete if (sigMsg->params().getBoolValue("complete")) msg->appendSafe(new ISDNQ931IE(ISDNQ931IE::SendComplete)); m_data.m_display = sigMsg->params().getValue("display"); m_data.processDisplay(msg,true,&q931()->parserData()); // Check tones or ringing const char* tone = sigMsg->params().getValue("tone"); if (tone) msg->appendIEValue(ISDNQ931IE::Keypad,"keypad",tone); return q931()->sendMessage(msg); } // Send PROGRESS. See Q.931 3.1.8 // IE: BearerCaps, Cause, Progress (mandatory), Display, HiLayerCompat bool ISDNQ931Call::sendProgress(SignallingMessage* sigMsg) { MSG_CHECK_SEND(ISDNQ931Message::Progress) if (sigMsg) { m_data.m_progress = sigMsg->params().getValue("progress"); if (sigMsg->params().getBoolValue("media",false) && -1 == m_data.m_progress.find("in-band-info")) m_data.m_progress.append("in-band-info",","); } ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Progress,this); m_data.processProgress(msg,true); return q931()->sendMessage(msg); } // Send RELEASE. See Q.931 3.1.9 // IE: Cause, Display, Signal bool ISDNQ931Call::sendRelease(const char* reason, SignallingMessage* sigMsg) { if (state() == ReleaseReq || state() == Null) return false; // Get reason if (!reason && sigMsg) reason = sigMsg->params().getValue("reason",0); if (reason) m_data.m_reason = reason; m_terminate = true; changeState(ReleaseReq); m_relTimer.start(); return q931()->sendRelease(this,true,m_data.m_reason); } // Send RELEASE COMPLETE. See Q.931 3.1.10 // IE: Cause, Display, Signal bool ISDNQ931Call::sendReleaseComplete(const char* reason, const char* diag) { m_relTimer.stop(); if (state() == Null) return false; if (reason) m_data.m_reason = reason; m_terminate = m_destroy = true; changeState(Null); q931()->releaseCircuit(m_circuit); return q931()->sendRelease(this,false,m_data.m_reason,diag); } // Send SETUP. See Q.931 3.1.14 // IE: Repeat, BearerCaps, ChannelID, Progress, NetFacility, Display, // Keypad, Signal, CallingNo, CallingSubAddr, CalledNo, CalledSubAddr, // NetTransit, Repeat, LoLayerCompat, HiLayerCompat bool ISDNQ931Call::sendSetup(SignallingMessage* sigMsg) { if (!sigMsg) return false; MSG_CHECK_SEND(ISDNQ931Message::Setup) ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Setup,this); while (true) { // TODO: fix it (don't send?) if overlapp dialing is used if (q931()->parserData().flag(ISDNQ931::ForceSendComplete)) msg->appendSafe(new ISDNQ931IE(ISDNQ931IE::SendComplete)); // BearerCaps m_data.m_transferCapability = "speech"; m_data.m_transferMode = "circuit"; m_data.m_transferRate = "64kbit"; m_data.m_format = sigMsg->params().getValue("format",q931()->format()); if (0xffff == lookup(m_data.m_format,Q931Parser::s_dict_bearerProto1,0xffff)) m_data.m_format = "alaw"; m_data.processBearerCaps(msg,true); // ChannelID if (!reserveCircuit()) break; m_circuit->updateFormat(m_data.m_format,0); m_data.m_bri = false; m_data.m_channelMandatory = false; m_data.m_channelByNumber = true; m_data.m_channelType = "B"; m_data.m_channelSelect = "present"; m_data.m_channels = m_circuit->code(); m_data.processChannelID(msg,true); // Progress indicator m_data.m_progress = sigMsg->params().getValue("call-progress"); m_data.processProgress(msg,true,&q931()->parserData()); // Display m_data.m_display = sigMsg->params().getValue("callername"); m_data.processDisplay(msg,true,&q931()->parserData()); // CallingNo m_data.m_callerType = sigMsg->params().getValue("callernumtype",q931()->numType()); m_data.m_callerPlan = sigMsg->params().getValue("callernumplan",q931()->numPlan()); m_data.m_callerPres = sigMsg->params().getValue("callerpres",q931()->numPresentation()); m_data.m_callerScreening = sigMsg->params().getValue("callerscreening",q931()->numScreening()); m_data.m_callerNo = sigMsg->params().getValue("caller"); m_data.processCallingNo(msg,true); // CalledNo m_data.m_calledType = sigMsg->params().getValue("callednumtype"); m_data.m_calledPlan = sigMsg->params().getValue("callednumplan"); m_data.m_calledNo = sigMsg->params().getValue("called"); m_data.processCalledNo(msg,true); // Send changeState(CallInitiated); if (q931()->sendMessage(msg,&m_data.m_reason)) return true; msg = 0; break; } if (msg) msg->deref(); setTerminate(true,0); return false; } // Send SUSPEND REJECT. See Q.931 3.1.20 // IE: Cause, Display bool ISDNQ931Call::sendSuspendRej(const char* reason, SignallingMessage* sigMsg) { if (!reason && sigMsg) reason = sigMsg->params().getValue("reason"); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::SuspendRej,this); msg->appendIEValue(ISDNQ931IE::Cause,"cause",reason); return q931()->sendMessage(msg); } SignallingEvent* ISDNQ931Call::releaseComplete(const char* reason, const char* diag) { Lock lock(m_callMutex); if (reason) m_data.m_reason = reason; DDebug(q931(),DebugInfo, "Call(%u,%u). Call release in state '%s'. Reason: '%s' [%p]", Q931_CALL_ID,stateName(state()),m_data.m_reason.c_str(),this); sendReleaseComplete(reason,diag); // Cleanup q931()->releaseCircuit(m_circuit); changeState(Null); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::ReleaseComplete,this); msg->params().addParam("reason",m_data.m_reason); SignallingEvent* event = new SignallingEvent(SignallingEvent::Release,msg,this); msg->deref(); deref(); return event; } // Get an event from the reserved circuit SignallingEvent* ISDNQ931Call::getCircuitEvent(const Time& when) { if (!m_circuit) return 0; SignallingCircuitEvent* ev = m_circuit->getEvent(when); if (!ev) return 0; SignallingEvent* event = 0; switch (ev->type()) { case SignallingCircuitEvent::Dtmf: { const char* tone = ev->getValue("tone"); if (!(tone && *tone)) break; ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Info,this); msg->params().addParam("tone",tone); msg->params().addParam("inband",String::boolText(true)); event = new SignallingEvent(SignallingEvent::Info,msg,this); msg->deref(); } break; default: ; } delete ev; return event; } // Reserve and connect a circuit. Change the reserved one if it must to bool ISDNQ931Call::reserveCircuit() { m_circuitChange = false; bool anyCircuit = false; while (true) { // For incoming calls we reserve the circuit only one time (at SETUP) if (!outgoing()) break; // Outgoing calls if (!m_data.m_channelByNumber) { m_data.m_reason = "service-not-implemented"; return false; } // Check if we don't have a circuit reserved if (!m_circuit) { anyCircuit = true; break; } // Check the received circuit if any if (!m_data.m_channels.null() && m_data.m_channels.toInteger() == (int)m_circuit->code()) return true; // We already have a circuit and received a different one: force mandatory m_data.m_channelMandatory = true; break; } // Reserve the circuit m_circuitChange = true; if (anyCircuit) q931()->reserveCircuit(m_circuit); else q931()->reserveCircuit(m_circuit,-1,&m_data.m_channels,m_data.m_channelMandatory,true); if (m_circuit) { m_data.m_channels = m_circuit->code(); if (!m_circuit->connect(m_data.m_format)) Debug(q931(),DebugNote, "Call(%u,%u). Failed to connect circuit [%p]",Q931_CALL_ID,this); return true; } DDebug(q931(),DebugNote, "Call(%u,%u). Can't reserve circuit [%p]",Q931_CALL_ID,this); m_data.m_reason = anyCircuit ? "congestion" : "channel-unacceptable"; return false; } // Print debug message on missing IE // Generate a Release event if requested by caller SignallingEvent* ISDNQ931Call::errorNoIE(ISDNQ931Message* msg, ISDNQ931IE::Type type, bool release) { Debug(q931(),DebugNote, "Call(%u,%u). Received '%s' without mandatory IE '%s' [%p]", Q931_CALL_ID,msg->name(),ISDNQ931IE::typeName(type),this); if (release) { unsigned char c = (unsigned char)type; String tmp; tmp.hexify(&c,1); return releaseComplete("missing-mandatory-ie",tmp); } return 0; } // Print debug message on wrong IE // Generate a Release event if requested by caller SignallingEvent* ISDNQ931Call::errorWrongIE(ISDNQ931Message* msg, ISDNQ931IE::Type type, bool release) { Debug(q931(),DebugNote, "Call(%u,%u). Received '%s' containing IE '%s' with wrong data [%p]", Q931_CALL_ID,msg->name(),ISDNQ931IE::typeName(type),this); if (release) { unsigned char c = (unsigned char)type; String tmp; tmp.hexify(&c,1); return releaseComplete("invalid-ie",tmp); } return 0; } // Change call state void ISDNQ931Call::changeState(State newState) { if (state() == newState) return; #ifdef DEBUG Debug(q931(),DebugAll,"Call(%u,%u). Changing state '%s' --> '%s' [%p]", Q931_CALL_ID,stateName(state()),stateName(newState),this); #else Debug(q931(),DebugAll,"Call(%u,%u). Changing state %u --> %u [%p]", Q931_CALL_ID,state(),newState,this); #endif m_state = newState; } ISDNQ931* ISDNQ931Call::q931() { return static_cast(SignallingCall::controller()); } #undef Q931_CALL_ID #undef MSG_CHECK_SEND /** * ISDNQ931CallMonitor */ ISDNQ931CallMonitor::ISDNQ931CallMonitor(ISDNQ931Monitor* controller, u_int32_t callRef, bool netInit) : SignallingCall(controller,true), m_callRef(callRef), m_callerCircuit(0), m_calledCircuit(0), m_eventCircuit(0), m_netInit(netInit), m_circuitChange(false), m_terminate(false), m_terminator("engine") { Debug(q931(),DebugAll,"Monitor(%u) netInit=%s [%p]", m_callRef,String::boolText(netInit),this); if (!controller) { Debug(DebugWarn,"Monitor(%u). No monitor controller. Terminate [%p]", m_callRef,this); m_terminate = true; m_data.m_reason = "temporary-failure"; return; } } ISDNQ931CallMonitor::~ISDNQ931CallMonitor() { releaseCircuit(); DDebug(q931(),DebugAll,"Monitor(%u). Destroyed with reason '%s' [%p]", m_callRef,m_data.m_reason.c_str(),this); } // Get an event from this monitor SignallingEvent* ISDNQ931CallMonitor::getEvent(const Time& when) { Lock lock(m_callMutex); // Check for last event or aborting if (m_lastEvent || state() == CallAbort) return 0; if (m_terminate) return (m_lastEvent = releaseComplete()); // Check for incoming messages ISDNQ931Message* msg = static_cast(dequeue()); // No message: check terminate if (!msg) return (m_lastEvent = getCircuitEvent(when)); XDebug(q931(),DebugAll, "Monitor(%u). Dequeued message (%p): '%s' in state '%s' [%p]", m_callRef,msg,msg->name(),stateName(state()),this); switch (msg->type()) { case ISDNQ931Message::Setup: m_lastEvent = processMsgSetup(msg); break; case ISDNQ931Message::Proceeding: case ISDNQ931Message::Alerting: case ISDNQ931Message::Connect: m_lastEvent = processMsgResponse(msg); break; case ISDNQ931Message::Disconnect: case ISDNQ931Message::Release: case ISDNQ931Message::ReleaseComplete: m_lastEvent = processMsgTerminate(msg); break; case ISDNQ931Message::Info: m_lastEvent = processMsgInfo(msg); break; case ISDNQ931Message::Notify: case ISDNQ931Message::Progress: case ISDNQ931Message::SetupAck: case ISDNQ931Message::ConnectAck: case ISDNQ931Message::Status: case ISDNQ931Message::StatusEnquiry: case ISDNQ931Message::Suspend: case ISDNQ931Message::Resume: case ISDNQ931Message::SuspendAck: case ISDNQ931Message::SuspendRej: case ISDNQ931Message::ResumeAck: case ISDNQ931Message::ResumeRej: XDebug(q931(),DebugAll,"Monitor(%u). Ignoring '%s' message [%p]", m_callRef,msg->name(),this); break; default: DDebug(q931(),DebugNote,"Monitor(%u). Unknown message '%s' [%p]", m_callRef,msg->name(),this); // Fall through to deref the message and check timeouts } msg->deref(); if (!m_lastEvent) m_lastEvent = getCircuitEvent(when); return m_lastEvent; } // Set termination flag void ISDNQ931CallMonitor::setTerminate(const char* reason) { Lock lock(m_callMutex); if (state() == CallAbort) changeState(Null); // Check terminate & destroy flags if (m_terminate) return; m_terminate = true; if (reason) m_data.m_reason = reason; DDebug(q931(),DebugInfo, "Monitor(%u). Set terminate [%p]",m_callRef,this); } // Get caller's and called's circuit or this object void* ISDNQ931CallMonitor::getObject(const String& name) const { if (name == "SignallingCircuitCaller") return m_callerCircuit; if (name == "SignallingCircuitCalled") return m_calledCircuit; if (name == "ISDNQ931CallMonitor") return (void*)this; return SignallingCall::getObject(name); } // Process SETUP. See Q.931 3.1.14 // IE: Repeat, BearerCaps, ChannelID, Progress, NetFacility, Display, // Keypad, Signal, CallingNo, CallingSubAddr, CalledNo, CalledSubAddr, // NetTransit, Repeat, LoLayerCompat, HiLayerCompat SignallingEvent* ISDNQ931CallMonitor::processMsgSetup(ISDNQ931Message* msg) { // These message should come from the call initiator if (!msg->initiator()) return 0; changeState(CallPresent); // Process IEs m_data.processBearerCaps(msg,false); m_circuitChange = false; if (m_data.processChannelID(msg,false) && reserveCircuit() && m_circuitChange) { m_circuitChange = false; msg->params().setParam("circuit-change",String::boolText(true)); } m_data.processCalledNo(msg,false); m_data.processCallingNo(msg,false); m_data.processDisplay(msg,false); // Get circuits from controller. Connect the caller's circuit if (reserveCircuit()) connectCircuit(true); // Set message parameters msg->params().setParam("caller",m_data.m_callerNo); msg->params().setParam("called",m_data.m_calledNo); msg->params().setParam("format",m_data.m_format); msg->params().setParam("callername",m_data.m_display); msg->params().setParam("callernumtype",m_data.m_callerType); msg->params().setParam("callernumplan",m_data.m_callerPlan); msg->params().setParam("callerpres",m_data.m_callerPres); msg->params().setParam("callerscreening",m_data.m_callerScreening); msg->params().setParam("callednumtype",m_data.m_calledType); msg->params().setParam("callednumplan",m_data.m_calledPlan); return new SignallingEvent(SignallingEvent::NewCall,msg,this); } // Process CALL PROCEEDING. See Q.931 3.1.2 // IE: BearerCaps, ChannelID, Progress, Display, HiLayerCompat // Process ALERTING. See Q.931 3.1.1 // IE: BearerCaps, ChannelID, Progress, Display, Signal, HiLayerCompat // Process CONNECT. See Q.931 3.1.3 // IE: BearerCaps, ChannelID, Progress, Display, DateTime, Signal, LoLayerCompat, HiLayerCompat // All we need is BearerCaps (for data format) and ChannelID (for channel change) SignallingEvent* ISDNQ931CallMonitor::processMsgResponse(ISDNQ931Message* msg) { SignallingEvent::Type type; // These responses should never come from the call initiator if (msg->initiator()) return 0; switch (msg->type()) { case ISDNQ931Message::Proceeding: if (state() == OutgoingProceeding) return 0; changeState(OutgoingProceeding); type = SignallingEvent::Accept; break; case ISDNQ931Message::Alerting: if (state() == CallDelivered) return 0; changeState(CallDelivered); type = SignallingEvent::Ringing; break; case ISDNQ931Message::Connect: if (state() == Active) return 0; changeState(Active); type = SignallingEvent::Answer; break; default: return 0; } m_circuitChange = false; if (m_data.processChannelID(msg,false) && reserveCircuit() && m_circuitChange) { m_circuitChange = false; msg->params().setParam("circuit-change",String::boolText(true)); } if (m_data.processBearerCaps(msg,false) && !m_data.m_format.null()) msg->params().setParam("format",m_data.m_format); connectCircuit(true); connectCircuit(false); return new SignallingEvent(type,msg,this); } // Process termination messages Disconnect, Release, ReleaseComplete SignallingEvent* ISDNQ931CallMonitor::processMsgTerminate(ISDNQ931Message* msg) { if (!msg) return 0; // Set terminator // Usually Disconnect and ReleaseComplete come from the termination initiator switch (msg->type()) { case ISDNQ931Message::Disconnect: case ISDNQ931Message::ReleaseComplete: m_terminator = msg->initiator() ? m_data.m_callerNo : m_data.m_calledNo; break; case ISDNQ931Message::Release: m_terminator = msg->initiator() ? m_data.m_calledNo : m_data.m_callerNo; break; default: return 0; } m_data.processCause(msg,false); return releaseComplete(); } // Process INFORMATION. See Q.931 3.1.6 // IE: SendComplete, Display, Keypad, Signal, CalledNo SignallingEvent* ISDNQ931CallMonitor::processMsgInfo(ISDNQ931Message* msg) { // Check complete bool complete = (0 != msg->getIE(ISDNQ931IE::SendComplete)); if (complete) msg->params().addParam("complete",String::boolText(true)); // Display m_data.processDisplay(msg,false); // Try to get digits const char* tone = msg->getIEValue(ISDNQ931IE::CalledNo,"number"); if (!tone) tone = msg->getIEValue(ISDNQ931IE::Keypad,"keypad"); if (tone) msg->params().addParam("tone",tone); msg->params().setParam("fromcaller",String::boolText(msg->initiator())); return new SignallingEvent(SignallingEvent::Info,msg,this); } // Release monitor SignallingEvent* ISDNQ931CallMonitor::releaseComplete(const char* reason) { Lock lock(m_callMutex); if (reason) m_data.m_reason = reason; DDebug(q931(),DebugInfo, "Monitor(%u). Monitor release in state '%s'. Reason: '%s' [%p]", m_callRef,stateName(state()),m_data.m_reason.c_str(),this); // Cleanup releaseCircuit(); changeState(Null); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::ReleaseComplete, true,m_callRef,2); msg->params().addParam("reason",m_data.m_reason); msg->params().addParam("terminator",m_terminator); SignallingEvent* event = new SignallingEvent(SignallingEvent::Release,msg,this); msg->deref(); deref(); return event; } // Get an event from the reserved circuit SignallingEvent* ISDNQ931CallMonitor::getCircuitEvent(const Time& when) { bool fromCaller = true; // Select circuit to get event from if (m_eventCircuit) if (m_eventCircuit == m_callerCircuit) { m_eventCircuit = m_calledCircuit; fromCaller = false; } else m_eventCircuit = m_callerCircuit; else m_eventCircuit = m_callerCircuit; SignallingCircuitEvent* ev = m_eventCircuit ? m_eventCircuit->getEvent(when) : 0; if (!ev) return 0; SignallingEvent* event = 0; switch (ev->type()) { case SignallingCircuitEvent::Dtmf: { const char* tone = ev->getValue("tone"); if (!(tone && *tone)) break; ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Info, !fromCaller,m_callRef,2); msg->params().addParam("tone",tone); msg->params().addParam("inband",String::boolText(true)); msg->params().addParam("fromcaller",String::boolText(fromCaller)); event = new SignallingEvent(SignallingEvent::Info,msg,this); msg->deref(); } break; default: ; } delete ev; return event; } // Reserve circuit for caller and called // Reserve both circuits or none bool ISDNQ931CallMonitor::reserveCircuit() { m_circuitChange = false; if (!m_data.m_channelByNumber) return false; // Check the received circuit if any unsigned int code = (unsigned int)m_data.m_channels.toInteger(-1); if (m_data.m_channels.null()) return 0 != m_callerCircuit; else if (m_callerCircuit && (code == m_callerCircuit->code())) return true; // Reserve the circuit m_circuitChange = true; releaseCircuit(); if (q931()->reserveCircuit(code,m_netInit,&m_callerCircuit,&m_calledCircuit)) return true; DDebug(q931(),DebugNote, "Monitor(%u). Can't reserve circuit [%p]",m_callRef,this); return false; } // Release both reserved circuits void ISDNQ931CallMonitor::releaseCircuit() { if (m_callerCircuit) { q931()->releaseCircuit(m_callerCircuit); m_callerCircuit->deref(); m_callerCircuit = 0; } if (m_calledCircuit) { q931()->releaseCircuit(m_calledCircuit); m_calledCircuit->deref(); m_calledCircuit = 0; } } // Connect a reserved circuit bool ISDNQ931CallMonitor::connectCircuit(bool caller) { if (caller) { if (m_callerCircuit && m_callerCircuit->connect(m_data.m_format)) return true; } else if (m_calledCircuit && m_calledCircuit->connect(m_data.m_format)) return true; DDebug(q931(),DebugNote, "Monitor(%u). Can't connect circuit for calle%s [%p]", m_callRef,caller?"r":"d",this); return false; } // Change monitor state void ISDNQ931CallMonitor::changeState(State newState) { if (state() == newState) return; DDebug(q931(),DebugInfo, "Monitor(%u). Changing state from '%s' to '%s' [%p]", m_callRef,stateName(state()),stateName(newState),this); m_state = newState; } ISDNQ931Monitor* ISDNQ931CallMonitor::q931() { return static_cast(SignallingCall::controller()); } /** * ISDNQ931ParserData */ ISDNQ931ParserData::ISDNQ931ParserData(const NamedList& params, DebugEnabler* dbg) : m_dbg(dbg), m_maxMsgLen(0), m_flags(0), m_flagsOrig(0) { m_allowSegment = params.getBoolValue("allowsegmentation",false); m_maxSegments = params.getIntValue("maxsegments",8); m_maxDisplay = params.getIntValue("max-display",34); if (m_maxDisplay != 34 && m_maxDisplay != 82) m_maxDisplay = 34; m_extendedDebug = params.getBoolValue("extended-debug",false); // Set flags String flags = params.getValue("switchtype"); SignallingUtils::encodeFlags(0,m_flagsOrig,flags,ISDNQ931::s_swType); SignallingUtils::encodeFlags(0,m_flagsOrig,flags,ISDNQ931::s_flags); m_flags = m_flagsOrig; } /** * ISDNQ931 */ TokenDict ISDNQ931::s_flags[] = { {"sendnonisdnsource", SendNonIsdnSource}, {"ignorenonisdndest", IgnoreNonIsdnDest}, {"forcepresnetprov", ForcePresNetProv}, {"translate31kaudio", Translate31kAudio}, {"urditransfercapsonly", URDITransferCapsOnly}, {"nolayer1caps", NoLayer1Caps}, {"ignorenonlockedie", IgnoreNonLockedIE}, {"nodisplay", NoDisplayIE}, {"nodisplaycharset", NoDisplayCharset}, {"forcesendcomplete", ForceSendComplete}, {"noactiveonconnect", NoActiveOnConnect}, {"checknotifyind", CheckNotifyInd}, {0,0}, }; TokenDict ISDNQ931::s_swType[] = { {"euro-isdn-e1", EuroIsdnE1}, {"euro-isdn-t1", EuroIsdnT1}, {"national-isdn", NationalIsdn}, {"dms100", Dms100}, {"lucent5e", Lucent5e}, {"att4ess", Att4ess}, {"qsig", QSIG}, {"unknown", Unknown}, {0,0} }; ISDNQ931::ISDNQ931(const NamedList& params, const char* name) : SignallingCallControl(params,"isdn."), ISDNLayer3(name), m_layer(true), m_q921(0), m_q921Up(false), m_primaryRate(true), m_transferModeCircuit(true), m_callRef(1), m_callRefLen(2), m_callRefMask(0), m_parserData(params), m_l2DownTimer(0), m_recvSgmTimer(0), m_syncCicTimer(0), m_syncCicCounter(2), m_callDiscTimer(0), m_callRelTimer(0), m_callConTimer(0), m_restartCic(0), m_lastRestart(0), m_syncGroupTimer(0), m_segmented(0), m_remaining(0), m_printMsg(true), m_extendedDebug(false), m_flagQ921Down(false), m_flagQ921Invalid(false) { setName(params.getValue("debugname",name)); m_parserData.m_dbg = this; //m_primaryRate = params.getBoolValue("pri",true); m_callRefLen = params.getIntValue("callreflen",2); if (m_callRefLen < 1 || m_callRefLen > 4) m_callRefLen = 2; // Set mask. Bit 7 of the first byte of the message header it's used for initiator flag m_callRefMask = 0x7fffffff >> (4 - m_callRefLen); // Timers m_l2DownTimer.interval(params,"t309",60000,90000,false); m_recvSgmTimer.interval(params,"t314",3000,4000,false); m_syncCicTimer.interval(params,"t316",4000,5000,false); m_syncGroupTimer.interval(params,"channelsync",500,1000,true,true); m_callDiscTimer.interval(params,"t305",0,5000,false); m_callRelTimer.interval(params,"t308",0,5000,false); m_callConTimer.interval(params,"t313",0,5000,false); m_numPlan = params.getValue("numplan"); if (0xffff == lookup(m_numPlan,Q931Parser::s_dict_numPlan,0xffff)) m_numPlan = "unknown"; m_numType = params.getValue("numtype"); if (0xffff == lookup(m_numType,Q931Parser::s_dict_typeOfNumber,0xffff)) m_numType = "unknown"; m_numPresentation = params.getValue("presentation"); if (0xffff == lookup(m_numPresentation,Q931Parser::s_dict_presentation,0xffff)) m_numPresentation = "allowed"; m_numScreening = params.getValue("screening"); if (0xffff == lookup(m_numScreening,Q931Parser::s_dict_screening,0xffff)) m_numScreening = "user-provided"; m_format = params.getValue("format"); if (0xffff == lookup(m_format,Q931Parser::s_dict_bearerProto1,0xffff)) m_format = "alaw"; // Debug setDebug(params.getBoolValue("print-messages",false), params.getBoolValue("extended-debug",false)); if (debugAt(DebugInfo)) { String s; #ifdef DEBUG s << "type=" << lookup(m_parserData.m_flags,s_swType,"Custom"); String t; for (TokenDict* p = s_flags; p->token; p++) if (m_parserData.flag(p->value)) t.append(p->token,","); if (!t.null()) s << " (" << t << ")"; s << " pri=" << String::boolText(m_primaryRate); s << " format=" << m_format; s << " callref-len=" << (unsigned int)m_callRefLen; s << " plan/type/pres/screen=" << m_numPlan << "/" << m_numType << "/" << m_numPresentation << "/" << m_numScreening; s << " strategy=" << lookup(strategy(),SignallingCircuitGroup::s_strategy); s << " channelsync/l2Down/recvSgm/syncCic=" << (unsigned int)m_syncGroupTimer.interval() << "/" << (unsigned int)m_l2DownTimer.interval() << "/" << (unsigned int)m_recvSgmTimer.interval() << "/" << (unsigned int)m_syncCicTimer.interval(); s << " segmentation=" << String::boolText(m_parserData.m_allowSegment); s << " max-segments=" << (unsigned int)m_parserData.m_maxSegments; #else s << "type=" << params.getValue("switchtype"); s << " pri=" << String::boolText(m_primaryRate); s << " format=" << m_format; s << " channelsync=" << String::boolText(0 != m_syncGroupTimer.interval()); #endif Debug(this,DebugInfo,"ISDN Call Controller %s [%p]",s.c_str(),this); } m_syncGroupTimer.start(); } ISDNQ931::~ISDNQ931() { if (m_calls.count()) { cleanup(); m_calls.clear(); } attach((ISDNLayer2*)0); DDebug(this,DebugAll,"ISDN Call Controller destroyed [%p]",this); } // Send a message to layer 2 bool ISDNQ931::sendMessage(ISDNQ931Message* msg, String* reason) { if (!msg) { if (reason) *reason = "wrong-message"; return false; } Lock lock(m_layer); if (!(m_q921 && m_q921Up)) { if (!m_flagQ921Invalid) Debug(this,DebugNote, "Refusing to send message. Layer 2 is missing or down"); m_flagQ921Invalid = true; msg->deref(); if (reason) *reason = "net-out-of-order"; return false; } m_flagQ921Invalid = false; // Print message after running encoder to view dumped data ObjList segments; u_int8_t count = msg->encode(m_parserData,segments); if (debugAt(DebugInfo) && m_printMsg) { String tmp; msg->toString(tmp,m_extendedDebug); Debug(this,DebugInfo,"Sending message (%p)%s",msg,tmp.c_str()); } msg->deref(); ObjList* obj = segments.skipNull(); if (!(count && obj)) { Debug(this,DebugNote,"Failed to send message (%p). Parser failure",msg); if (reason) *reason = "wrong-message"; return false; } if (count != 1) DDebug(this,DebugNote,"Message (%p) was segmented in %u parts",msg,count); for (; obj; obj = obj->skipNext()) { DataBlock* buffer = static_cast(obj->get()); dump(*buffer,true); if (!m_q921->sendData(*buffer,true)) { if (reason) *reason = "net-out-of-order"; return false; } } return true; } // Data link up notificatin from layer 2 // Notify calls void ISDNQ931::multipleFrameEstablished(bool confirmation, bool timeout, ISDNLayer2* layer2) { m_layer.lock(); m_q921Up = true; DDebug(this,DebugNote,"'Established' %s", confirmation ? "confirmation" :"indication"); endReceiveSegment("Data link is up"); m_l2DownTimer.stop(); m_flagQ921Down = false; m_layer.unlock(); if (confirmation) return; // Notify calls Lock lock(this); for (ObjList* obj = m_calls.skipNull(); obj; obj = obj->skipNext()) (static_cast(obj->get()))->dataLinkState(true); } // Data link down notificatin from layer 2 // Notify calls void ISDNQ931::multipleFrameReleased(bool confirmation, bool timeout, ISDNLayer2* layer2) { Lock lockLayer(m_layer); m_q921Up = false; DDebug(this,DebugNote,"'Released' %s. Timeout: %s", confirmation ? "confirmation" :"indication",String::boolText(timeout)); endReceiveSegment("Data link is down"); // Re-establish if layer 2 doesn't have an automatically re-establish procedure if (m_q921 && !m_q921->autoRestart()) { DDebug(this,DebugNote,"Re-establish layer 2."); m_q921->multipleFrame(true,false); } if (confirmation) return; if (!m_l2DownTimer.started()) { XDebug(this,DebugAll,"Starting T309 (layer 2 down)"); m_l2DownTimer.start(); } lockLayer.drop(); // Notify calls Lock lockCalls(this); for (ObjList* obj = m_calls.skipNull(); obj; obj = obj->skipNext()) (static_cast(obj->get()))->dataLinkState(false); } // Receive and parse data from layer 2 // Process the message void ISDNQ931::receiveData(const DataBlock& data, bool ack, ISDNLayer2* layer2) { XDebug(this,DebugAll,"Received data. Length: %u",data.length()); if (!ack) { Debug(this,DebugNote,"Received unacknoledged data. Drop"); return; } Lock lock(m_layer); ISDNQ931Message* msg = getMsg(data); if (!msg) return; // Dummy call reference if (msg->dummyCallRef()) { sendStatus("service-not-implemented",0); msg->deref(); return; } // Global call reference or a message that should have a dummy call reference if (!msg->callRef() || msg->type() == ISDNQ931Message::Restart || msg->type() == ISDNQ931Message::RestartAck) { processGlobalMsg(msg); msg->deref(); return; } // This is an incoming message: // if initiator is true, the message is for an incoming call ISDNQ931Call* call = findCall(msg->callRef(),!msg->initiator()); while (true) { if (call) { if (msg->type() != ISDNQ931Message::Setup) { call->enqueue(msg); msg = 0; } else sendRelease(false,msg->callRefLen(),msg->callRef(),!msg->initiator(), "invalid-callref"); break; } // Check if it is a new incoming call if (msg->initiator() && msg->type() == ISDNQ931Message::Setup) { // Accept new calls only if no channel is restarting and not exiting String reason; if (acceptNewCall(false,reason)) { call = new ISDNQ931Call(this,false,msg->callRef(),msg->callRefLen()); m_calls.append(call); call->enqueue(msg); msg = 0; call = 0; } else sendRelease(false,msg->callRefLen(),msg->callRef(), !msg->initiator(),reason); break; } processInvalidMsg(msg); break; } if (call) call->deref(); if (msg) msg->deref(); } // Attach layer 2 // Update some data from the attached object void ISDNQ931::attach(ISDNLayer2* q921) { Lock lock(m_layer); if (m_q921 == q921) return; cleanup(q921 ? "layer 2 attach" : "layer 2 detach"); ISDNLayer2* tmp = m_q921; m_q921 = q921; if (m_q921) { ISDNQ921* q = static_cast(m_q921->getObject("ISDNQ921")); // Adjust timers from the new lower layer // Add 1000 ms to minimum value to allow the lower layer to re-establish // the data link before we make a retransmission if (q) { u_int64_t min = q->dataTimeout(); if (m_callDiscTimer.interval() <= min) m_callDiscTimer.interval(min + 1000); if (m_callRelTimer.interval() <= min) m_callRelTimer.interval(min + 1000); if (m_callConTimer.interval() <= min) m_callConTimer.interval(min + 1000); if (m_l2DownTimer.interval() <= min) m_l2DownTimer.interval(min + 1000); if (m_syncCicTimer.interval() <= min) m_syncCicTimer.interval(min + 1000); } // Adjust parser data message length limit m_parserData.m_maxMsgLen = m_q921->maxUserData(); // Adjust some parser flags if (m_parserData.m_flagsOrig == EuroIsdnE1 && !q->network()) m_parserData.m_flags |= NoDisplayIE; if (m_parserData.m_flagsOrig != QSIG && !q->network()) m_parserData.m_flags |= NoActiveOnConnect; } else { // Reset parser data if no layer 2 m_parserData.m_maxMsgLen = 0; m_parserData.m_flags = m_parserData.m_flagsOrig; } lock.drop(); if (tmp) { const char* name = 0; if (engine() && engine()->find(tmp)) { name = tmp->toString().safe(); tmp->attach(0); } Debug(this,DebugAll,"Detached L2 (%p,'%s') [%p]",tmp,name,this); } if (!q921) return; Debug(this,DebugAll,"Attached L2 '%s' (%p,'%s') [%p]", (q921->network()?"NET":"CPE"),q921,q921->toString().safe(),this); insert(q921); q921->attach(this); } // Make an outgoing call from a given message SignallingCall* ISDNQ931::call(SignallingMessage* msg, String& reason) { if (!msg) { reason = "invalid-parameter"; return 0; } Lock lock(m_layer); if (!acceptNewCall(true,reason)) { msg->deref(); return 0; } ISDNQ931Call* call = new ISDNQ931Call(this,true,m_callRef,m_callRefLen); call->ref(); // Adjust m_callRef. Avoid to use 0 m_callRef = (m_callRef + 1) & m_callRefMask; if (!m_callRef) m_callRef = 1; m_calls.append(call); SignallingEvent* event = new SignallingEvent(SignallingEvent::NewCall,msg,call); msg->deref(); call->sendEvent(event); return call; } // Reset data. Terminate calls and pending operations void ISDNQ931::cleanup(const char* reason) { DDebug(this,DebugAll,"Cleanup. Reason: '%s'",reason); terminateCalls(0,reason); endReceiveSegment(reason); endRestart(false,0); } // Set the interval for a given timer void ISDNQ931::setInterval(SignallingTimer& timer, int id) { switch (id) { case 305: timer.interval(m_callDiscTimer.interval()); break; case 308: timer.interval(m_callRelTimer.interval()); break; case 313: timer.interval(m_callConTimer.interval()); break; } timer.interval(0); } void* ISDNQ931::getObject(const String& name) const { if (name == "ISDNQ931") return (void*)this; return SignallingComponent::getObject(name); } // Check timeouts for segmented messages, layer 2 down state, restart circuits void ISDNQ931::timerTick(const Time& when) { Lock lock(m_layer); // Check segmented message if (m_recvSgmTimer.timeout(when.msec())) endReceiveSegment("timeout"); // Terminate all calls if T309 (layer 2 down) timed out if (m_l2DownTimer.timeout(when.msec())) { m_l2DownTimer.stop(); if (!m_flagQ921Down) Debug(this,DebugWarn,"Layer 2 was down for " FMT64 " ms",m_l2DownTimer.interval()); m_flagQ921Down = true; cleanup("dest-out-of-order"); } // Restart circuits if (!m_syncGroupTimer.interval()) return; if (m_syncGroupTimer.started()) { if (m_syncGroupTimer.timeout(when.msec())) { m_syncGroupTimer.stop(); sendRestart(when.msec(),false); } return; } if (!m_syncCicTimer.started()) { m_lastRestart = 0; m_syncGroupTimer.start(when.msec()); return; } // Terminate restart procedure if timeout if (m_syncCicTimer.timeout(when.msec())) { m_syncCicTimer.stop(); m_syncCicCounter.inc(); if (m_syncCicCounter.full()) endRestart(true,when.msec(),true); else sendRestart(when.msec(),true); } } // Find a call by call reference and direction ISDNQ931Call* ISDNQ931::findCall(u_int32_t callRef, bool outgoing) { Lock lock(this); ObjList* obj = m_calls.skipNull(); for (; obj; obj = obj->skipNext()) { ISDNQ931Call* call = static_cast(obj->get()); if (callRef == call->callRef() && outgoing == call->outgoing()) return (call->ref() ? call : 0); } return 0; } // Find a call by reserved circuit ISDNQ931Call* ISDNQ931::findCall(unsigned int circuit) { Lock lock(this); ObjList* obj = m_calls.skipNull(); for (; obj; obj = obj->skipNext()) { ISDNQ931Call* call = static_cast(obj->get()); if (!call->circuit() || call->circuit()->code() != circuit) continue; return (call->ref() ? call : 0); } return 0; } // Terminate a call or all of them void ISDNQ931::terminateCalls(ObjList* list, const char* reason) { Lock lock(this); // Terminate all calls if no list if (!list) { ObjList* obj = m_calls.skipNull(); for (; obj; obj = obj->skipNext()) { ISDNQ931Call* call = static_cast(obj->get()); call->setTerminate(true,reason); } return; } // Terminate calls from list for (ObjList* obj = list->skipNull(); obj; obj = obj->skipNext()) { int circuit = (static_cast(obj->get()))->toInteger(-1); if (circuit == -1) continue; ISDNQ931Call* call = findCall(circuit); if (call) { call->setTerminate(true,reason); call->deref(); continue; } // No call for this circuit. Release the circuit releaseCircuit(circuit); } } // Check if new calls are acceptable bool ISDNQ931::acceptNewCall(bool outgoing, String& reason) { if (!exiting()) return true; Debug(this,DebugNote,"Denying %s call request. We are exiting", outgoing ? "outgoing" : "incoming"); reason = "net-out-of-order"; return false; } // Helper function called in ISDNQ931::receive() static inline ISDNQ931Message* dropSegMsg(ISDNQ931* q931, ISDNQ931Message* msg, const char* reason) { if (reason) Debug(q931,DebugNote,"Dropping message segment (%p): '%s'. %s", msg,msg->name(),reason); msg->deref(); return 0; } // Parse received data // Create a message from it. Validate it. Process segmented messages ISDNQ931Message* ISDNQ931::getMsg(const DataBlock& data) { Lock lock(m_layer); DataBlock segData; ISDNQ931Message* msg = ISDNQ931Message::parse(m_parserData,data,&segData); if (!msg) return 0; // Print received message if (debugAt(DebugInfo) && m_printMsg) { String tmp; msg->toString(tmp,m_extendedDebug); Debug(this,DebugInfo,"Received message (%p)%s",msg,tmp.c_str()); } dump(data,false); // Not a segment if (msg->type() != ISDNQ931Message::Segment) { // We was waiting for a segment: Drop waiting if (m_segmented) endReceiveSegment("Received non-segmented message"); return msg; } // This is a message segment. Start timer. Get it's parameters m_recvSgmTimer.start(); bool first; u_int8_t remaining = 0xff, type = 0xff; // Get parameters bool valid = false; while (true) { ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::Segmented); if (!ie) break; NamedString* ns = ie->getParam("first"); if (!ns) break; first = ns->toBoolean(); remaining = (u_int8_t)ie->getIntValue("remaining",0xff); type = (u_int8_t)ie->getIntValue("message",0xff); valid = true; break; } if (!valid || type == 0xff || remaining == 0xff) return dropSegMsg(this,msg,"Invalid or missing segmented IE"); // Check segmented message type if (!ISDNQ931Message::typeName(type)) return dropSegMsg(this,msg,"Unknown segmented message type"); // SEGMENT message can't be segmented if (type == ISDNQ931Message::Segment) return dropSegMsg(this,msg,"Segmented message can't be a segment"); // Check if this is a new one if (!m_segmented) { // Should be the first segment with a valid call reference if (!first || !msg->callRef()) return dropSegMsg(this,msg,"Invalid message segment"); // Create message XDebug(this,DebugAll,"Start receiving message segments"); m_segmented = new ISDNQ931Message((ISDNQ931Message::Type)type, msg->initiator(),msg->callRef(),msg->callRefLen()); msg->deref(); // Put the message header in the buffer u_int8_t header[7]; m_segmentData.assign(header,fillHeader(header,m_segmented,this)); m_remaining = remaining; m_segmentData += segData; // Strange case: Segmented message in 1 segment if (!remaining) return endReceiveSegment(); return 0; } // Sould be a segment for the message we already have // Check call identification if (m_segmented->initiator() != msg->initiator() || m_segmented->callRef() != msg->callRef()) { dropSegMsg(this,msg,"Invalid call identification");; return endReceiveSegment("Segment with invalid call identification"); } // Check segment parameters if (first || m_remaining <= remaining || m_remaining - remaining != 1) { dropSegMsg(this,msg,"Invalid Segmented IE parameters");; return endReceiveSegment("Segment with invalid parameters"); } msg->deref(); // Update data m_remaining--; m_segmentData += segData; // End receiving ? if (!m_remaining) return endReceiveSegment(); return 0; } // Terminate receiving segmented message ISDNQ931Message* ISDNQ931::endReceiveSegment(const char* reason) { Lock lock(m_layer); m_recvSgmTimer.stop(); if (!m_segmented) return 0; // Clear some data m_segmented->deref(); m_segmented = 0; m_remaining = 0; // Drop ? if (reason) { Debug(this,DebugNote,"Drop receiving message segment. %s",reason); m_segmentData.clear(); return 0; } // Received all message: reassembly XDebug(this,DebugNote,"Reassambly message segment(s)"); ISDNQ931Message* msg = ISDNQ931Message::parse(m_parserData,m_segmentData,0); m_segmentData.clear(); if (msg && debugAt(DebugInfo) && m_printMsg) { String tmp; msg->toString(tmp,m_extendedDebug); Debug(this,DebugInfo,"Completed segmented message. (%p)%s",msg,tmp.c_str()); } return msg; } // Process messages with global call reference and messages that should have it void ISDNQ931::processGlobalMsg(ISDNQ931Message* msg) { if (!msg) return; switch (msg->type()) { case ISDNQ931Message::Restart: case ISDNQ931Message::RestartAck: // These messages must have a global call reference if (msg->callRef()) { #ifndef Q931_ACCEPT_RESTART Debug(this,DebugNote, "Dropping (%p): '%s' without global call reference", msg,msg->name()); sendStatus("invalid-message",m_callRefLen); return; #else DDebug(this,DebugNote,"(%p): '%s' without global call reference", msg,msg->name()); #endif } if (msg->type() == ISDNQ931Message::Restart) { processMsgRestart(msg); return; } if (m_restartCic) { String tmp = msg->getIEValue(ISDNQ931IE::ChannelID,"channels"); if (m_restartCic->code() == (unsigned int)tmp.toInteger(-1)) endRestart(true,0); else Debug(this,DebugWarn, "'%s' with invalid circuit(s) '%s'. We've requested '%u'", msg->name(),tmp.c_str(),m_restartCic->code()); } else sendStatus("wrong-state-message",m_callRefLen); return; case ISDNQ931Message::Status: break; default: Debug(this,DebugNote,"Dropping (%p): '%s' with global call reference", msg,msg->name()); sendStatus("invalid-callref",m_callRefLen); return; } // Message is a STATUS one DDebug(this,m_restartCic ? DebugWarn : DebugInfo, "'%s' with global call reference. State: '%s'. Cause: '%s'", msg->name(), msg->getIEValue(ISDNQ931IE::CallState,"state","Unknown/missing"), msg->getIEValue(ISDNQ931IE::Cause,"cause","Unknown/missing")); } // Process restart requests void ISDNQ931::processMsgRestart(ISDNQ931Message* msg) { m_data.processRestart(msg,false); m_data.processChannelID(msg,false); m_data.m_reason = ""; ObjList* list = 0; if (m_data.m_restart == "channels") { list = m_data.m_channels.split(',',false); if (!list) m_data.m_reason = "invalid-ie"; } else if (m_data.m_restart == "all-interfaces" || m_data.m_restart == "interface") m_data.m_reason = "service-not-implemented"; else m_data.m_reason = "invalid-ie"; if (!m_data.m_reason.null()) { Debug(this,DebugNote, "Incorrect/unsupported '%s' request. Class: '%s'. Circuit(s): '%s'. Reason: '%s'", msg->name(),m_data.m_restart.c_str(),m_data.m_channels.c_str(),m_data.m_reason.c_str()); sendStatus(m_data.m_reason,m_callRefLen); return; } if (!m_printMsg) DDebug(this,DebugInfo,"Received '%s' request for circuit(s) '%s'", msg->name(),m_data.m_channels.c_str()); // Terminate calls and ACK terminateCalls(list,"temporary-failure"); delete list; ISDNQ931Message* m = new ISDNQ931Message(ISDNQ931Message::RestartAck, false,0,m_callRefLen); m->append(msg->removeIE(ISDNQ931IE::ChannelID)); m->append(msg->removeIE(ISDNQ931IE::Restart)); sendMessage(m); } // Process messages with invalid call reference. See Q.931 5.8 void ISDNQ931::processInvalidMsg(ISDNQ931Message* msg) { if (!msg) return; DDebug(this,DebugNote,"Received (%p): '%s' with invalid call reference [%p]", msg,msg->name(),this); switch (msg->type()) { case ISDNQ931Message::Resume: case ISDNQ931Message::Setup: case ISDNQ931Message::ReleaseComplete: break; case ISDNQ931Message::Release: sendRelease(false,msg->callRefLen(),msg->callRef(), !msg->initiator(),"invalid-callref"); break; case ISDNQ931Message::Status: // Assume our call state to be Null. See Q.931 5.8.11 // Ignore the message if the reported state is Null { String s = msg->getIEValue(ISDNQ931IE::CallState,"state"); if (s != ISDNQ931Call::stateName(ISDNQ931Call::Null)) sendRelease(false,msg->callRefLen(),msg->callRef(), !msg->initiator(),"wrong-state-message"); } break; case ISDNQ931Message::StatusEnquiry: sendStatus("status-enquiry-rsp",msg->callRefLen(),msg->callRef(), !msg->initiator(),ISDNQ931Call::Null); break; default: sendRelease(true,msg->callRefLen(),msg->callRef(), !msg->initiator(),"invalid-callref"); return; } } // Try to reserve a circuit if none. Send a restart request on it's behalf // Start counting the restart interval if no circuit reserved void ISDNQ931::sendRestart(u_int64_t time, bool retrans) { Lock lock(m_layer); m_syncCicTimer.stop(); if (m_restartCic) { if (!retrans) return; } else { unsigned int count = circuitCount(); for (m_lastRestart++; m_lastRestart <= count; m_lastRestart++) { String tmp = m_lastRestart; if (reserveCircuit(m_restartCic,-1,&tmp,true)) break; } if (!m_restartCic) { m_lastRestart = 0; m_syncGroupTimer.start(time ? time : Time::msecNow()); return; } } String s = m_restartCic->code(); DDebug(this,DebugInfo,"%s restart for circuit(s) '%s'", !retrans ? "Sending" : "Retransmitting",s.c_str()); // Create the message ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Restart,true, 0,m_callRefLen); // Don't add 'interface' parameter. We always send the channels, not the interface ISDNQ931IE* ie = new ISDNQ931IE(ISDNQ931IE::ChannelID); ie->addParam("interface-bri",String::boolText(!primaryRate())); ie->addParam("channel-exclusive",String::boolText(true)); ie->addParam("channel-select","present"); ie->addParam("type","B"); ie->addParam("channel-by-number",String::boolText(true)); ie->addParam("channels",s); msg->appendSafe(ie); msg->appendIEValue(ISDNQ931IE::Restart,"class","channels"); m_syncCicTimer.start(time ? time : Time::msecNow()); sendMessage(msg); } // End our restart requests // Release reserved circuit. Continue restarting circuits if requested void ISDNQ931::endRestart(bool restart, u_int64_t time, bool timeout) { Lock lock(m_layer); m_syncCicTimer.stop(); m_syncCicCounter.reset(); if (m_restartCic) { if (!timeout) XDebug(this,DebugInfo,"Ending restart for circuit(s) '%u'",m_restartCic->code()); else Debug(this,DebugInfo,"Restart timed out for circuit(s) '%u'", m_restartCic->code()); releaseCircuit(m_restartCic); m_restartCic = 0; } if (restart) sendRestart(time,false); else { m_lastRestart = 0; m_syncGroupTimer.start(time ? time : Time::msecNow()); } } // Send STATUS. See Q.931 3.1.16 // IE: Cause, CallState, Display bool ISDNQ931::sendStatus(const char* cause, u_int8_t callRefLen, u_int32_t callRef, bool initiator, ISDNQ931Call::State state, const char* display) { // Create message ISDNQ931Message* msg; if (callRefLen) msg = new ISDNQ931Message(ISDNQ931Message::Status,initiator,callRef,callRefLen); else msg = new ISDNQ931Message(ISDNQ931Message::Status); // Set our state for dummy or global call references if (!(callRef && callRefLen)) state = m_restartCic ? ISDNQ931Call::RestartReq : ISDNQ931Call::Null; // Add IEs msg->appendIEValue(ISDNQ931IE::Cause,"cause",cause); msg->appendIEValue(ISDNQ931IE::CallState,"state",ISDNQ931Call::stateName(state)); if (display) msg->appendIEValue(ISDNQ931IE::Display,"display",display); return sendMessage(msg); } // Send RELEASE (See Q.931 3.1.9) or RELEASE COMPLETE (See Q.931 3.1.10) // IE: Cause, Display, Signal bool ISDNQ931::sendRelease(bool release, u_int8_t callRefLen, u_int32_t callRef, bool initiator, const char* cause, const char* diag, const char* display, const char* signal) { // Create message ISDNQ931Message::Type t = release ? ISDNQ931Message::Release : ISDNQ931Message::ReleaseComplete; ISDNQ931Message* msg = new ISDNQ931Message(t,initiator,callRef,callRefLen); // Add IEs ISDNQ931IE* ie = msg->appendIEValue(ISDNQ931IE::Cause,"cause",cause); if (diag) ie->addParam("diagnostic",diag); if (display) msg->appendIEValue(ISDNQ931IE::Display,"display",display); if (signal) msg->appendIEValue(ISDNQ931IE::Signal,"signal",signal); return sendMessage(msg); } /** * ISDNQ931Monitor */ ISDNQ931Monitor::ISDNQ931Monitor(const NamedList& params, const char* name) : SignallingCallControl(params,"isdn."), ISDNLayer3(name), m_layer(true), m_q921Net(0), m_q921Cpe(0), m_cicNet(0), m_cicCpe(0), m_parserData(params), m_printMsg(true), m_extendedDebug(false) { setName(params.getValue("debugname",name)); // Set parser data. Accept maximum data length m_parserData.m_maxMsgLen = 0xffffffff; m_parserData.m_dbg = this; // Debug setDebug(params.getBoolValue("print-messages",true), params.getBoolValue("extended-debug",false)); Debug(this,DebugAll,"ISDN Monitor created [%p]",this); } ISDNQ931Monitor::~ISDNQ931Monitor() { terminateMonitor(0,0); attach((ISDNQ921Pasive*)0,true); attach((ISDNQ921Pasive*)0,false); attach((SignallingCircuitGroup*)0,true); attach((SignallingCircuitGroup*)0,false); m_calls.clear(); DDebug(this,DebugAll,"ISDN Monitor destroyed [%p]",this); } // Notification from layer 2 of data link set/release command or response void ISDNQ931Monitor::dataLinkState(bool cmd, bool value, ISDNLayer2* layer2) { Lock lock(m_layer); #ifdef DEBUG if (debugAt(DebugInfo)) { String tmp; if (cmd) tmp << "'" << (value ? "Establish" : "Release") << "' request"; else tmp << "'" << (value ? "YES" : "NO") << "' response"; DDebug(this,DebugInfo,"Captured %s from '%s'. Clearing monitors", tmp.c_str(),layer2->debugName()); } #endif terminateMonitor(0,"net-out-of-order"); } // Notification from layer 2 of data link idle timeout void ISDNQ931Monitor::idleTimeout(ISDNLayer2* layer2) { Lock lock(m_layer); DDebug(this,DebugInfo,"Idle timeout from '%s'. Clearing monitors", layer2->debugName()); terminateMonitor(0,"net-out-of-order"); } // Receive data void ISDNQ931Monitor::receiveData(const DataBlock& data, bool ack, ISDNLayer2* layer2) { if (!layer2) return; XDebug(this,DebugAll,"Received data. Length: %u",data.length()); if (!ack) { Debug(this,DebugNote,"Received unacknoledged data. Drop"); return; } Lock lock(m_layer); //TODO: Implement segmentation ISDNQ931Message* msg = ISDNQ931Message::parse(m_parserData,data,0); if (!msg) return; msg->params().setParam("monitor-sender",layer2->debugName()); // Print received message if (debugAt(DebugInfo) && m_printMsg) { String tmp; msg->toString(tmp,m_extendedDebug); Debug(this,DebugInfo,"Captured message from '%s' (%p)%s", layer2->debugName(),msg,tmp.c_str()); } else DDebug(this,DebugInfo,"Captured '%s' (call ref: %u) from '%s'", msg->name(),msg->callRef(),layer2->debugName()); // Drop some messages if (dropMessage(msg)) { if (msg->type() == ISDNQ931Message::Restart || msg->type() == ISDNQ931Message::RestartAck) processMsgRestart(msg); else DDebug(this,DebugInfo,"Dropping message message (%p): '%s' from '%s'", msg,msg->name(),layer2->debugName()); msg->deref(); return; } // Find a monitor for this message or create a new one ISDNQ931CallMonitor* mon = findMonitor(msg->callRef(),true); while (true) { if (mon) { mon->enqueue(msg); msg = 0; break; } // Check if it is a new incoming call if (msg->initiator() && msg->type() == ISDNQ931Message::Setup) { mon = new ISDNQ931CallMonitor(this,msg->callRef(),m_q921Net == layer2); m_calls.append(mon); mon->enqueue(msg); mon = 0; msg = 0; break; } DDebug(this,DebugInfo, "Dropping message message (%p): '%s' from '%s'. Missing monitor for call %u", msg,msg->name(),layer2->debugName(),msg->callRef()); break; } if (mon) mon->deref(); if (msg) msg->deref(); } // Attach ISDN Q.921 pasive transport that monitors one side of the link void ISDNQ931Monitor::attach(ISDNQ921Pasive* q921, bool net) { Lock lock(m_layer); ISDNQ921Pasive* which = net ? m_q921Net : m_q921Cpe; if (which == q921) return; terminateMonitor(0,q921 ? "layer 2 attach" : "layer 2 detach"); ISDNQ921Pasive* tmp = which; if (net) m_q921Net = q921; else m_q921Cpe = q921; lock.drop(); if (tmp) { const char* name = 0; if (engine() && engine()->find(tmp)) { name = tmp->toString().safe(); tmp->ISDNLayer2::attach(0); } Debug(this,DebugAll,"Detached L2 %s (%p,'%s') [%p]",net?"NET":"CPE",tmp,name,this); } if (!q921) return; Debug(this,DebugAll,"Attached L2 %s (%p,'%s') [%p]", net?"NET":"CPE",q921,q921->toString().safe(),this); insert(q921); q921->ISDNLayer2::attach(this); } // Attach a circuit group to this call controller void ISDNQ931Monitor::attach(SignallingCircuitGroup* circuits, bool net) { Lock lock(m_layer); // Don't attach if it's the same object SignallingCircuitGroup* tmp = net ? m_cicNet : m_cicCpe; if (tmp == circuits) return; terminateMonitor(0,circuits ? "circuit group attach" : "circuit group detach"); if (tmp && circuits) Debug(this,DebugNote, "Attached circuit group (%p) '%s' while we already have one (%p) '%s'", circuits,circuits->debugName(),tmp,tmp->debugName()); else if (circuits && !tmp) DDebug(this,DebugAll,"Circuit group (%p) '%s' attached",circuits,circuits->debugName()); else if (tmp && !circuits) DDebug(this,DebugAll,"Circuit group (%p) '%s' detached",tmp,tmp->debugName()); if (net) m_cicNet = circuits; else m_cicCpe = circuits; } // Get a pointer to this call controller void* ISDNQ931Monitor::getObject(const String& name) const { if (name == "ISDNQ931Monitor") return (void*)this; return SignallingComponent::getObject(name); } // Method called periodically to check timeouts void ISDNQ931Monitor::timerTick(const Time& when) { } // Reserve the same circuit code from both circuit groups // This is an atomic operation: if one circuit fails to be reserved, both of them will fail bool ISDNQ931Monitor::reserveCircuit(unsigned int code, bool netInit, SignallingCircuit** caller, SignallingCircuit** called) { Lock lock(m_layer); if (!(m_cicNet && m_cicCpe)) return false; String cic = code; if (netInit) { *caller = m_cicNet->reserve(cic,true); *called = m_cicCpe->reserve(cic,true); } else { *caller = m_cicCpe->reserve(cic,true); *called = m_cicNet->reserve(cic,true); } if (*caller && *called) return true; releaseCircuit(*caller); releaseCircuit(*called); return false; } // Release a circuit from both groups bool ISDNQ931Monitor::releaseCircuit(SignallingCircuit* circuit) { Lock lock(m_layer); if (!circuit) return false; if (m_cicNet == circuit->group()) return m_cicNet->release(circuit,true); if (m_cicCpe == circuit->group()) return m_cicCpe->release(circuit,true); return false; } // Process a restart or restart acknoledge message // Terminate the monitor having the circuit given in restart message void ISDNQ931Monitor::processMsgRestart(ISDNQ931Message* msg) { if (msg->type() == ISDNQ931Message::Restart) { m_data.processRestart(msg,false); if (m_data.m_restart != "channels") { DDebug(this,DebugNote,"Unsupported '%s' request (class: '%s')", msg->name(),m_data.m_restart.c_str()); return; } } m_data.processChannelID(msg,false); ObjList* list = m_data.m_channels.split(',',false); if (!list) { DDebug(this,DebugNote,"Incorrect '%s' message (circuit(s): '%s')", msg->name(),m_data.m_channels.c_str()); return; } if (!m_printMsg) DDebug(this,DebugInfo,"Received '%s' message for circuit(s) '%s'", msg->name(),m_data.m_channels.c_str()); // Terminate monitor(s) for (ObjList* o = list->skipNull(); o; o = o->skipNext()) { String* s = static_cast(o->get()); ISDNQ931CallMonitor* mon = findMonitor(s->toInteger(-1),false); if (mon) { terminateMonitor(mon,"temporary-failure"); mon->deref(); } } delete list; } // Find a call monitor by call reference or reserved circuit ISDNQ931CallMonitor* ISDNQ931Monitor::findMonitor(unsigned int value, bool byCallRef) { Lock lock(this); ObjList* obj = m_calls.skipNull(); if (byCallRef) { for (; obj; obj = obj->skipNext()) { ISDNQ931CallMonitor* mon = static_cast(obj->get()); if (value == mon->m_callRef) return (mon->ref() ? mon : 0); } return 0; } // Find by reserved circuit for (; obj; obj = obj->skipNext()) { ISDNQ931CallMonitor* mon = static_cast(obj->get()); if (mon->m_callerCircuit && value == mon->m_callerCircuit->code()) return (mon->ref() ? mon : 0); } return 0; } // Drop some messages bool ISDNQ931Monitor::dropMessage(const ISDNQ931Message* msg) { if (msg->dummyCallRef()) return true; // Global call reference or a message that should have a dummy call reference if (!msg->callRef() || msg->type() == ISDNQ931Message::Restart || msg->type() == ISDNQ931Message::RestartAck) return true; return false; } // Terminate all monitors or only one void ISDNQ931Monitor::terminateMonitor(ISDNQ931CallMonitor* mon, const char* reason) { Lock lock(this); if (mon) { mon->setTerminate(reason); return; } // Terminate all monitors ObjList* obj = m_calls.skipNull(); for (; obj; obj = obj->skipNext()) { mon = static_cast(obj->get()); mon->setTerminate(reason); } } /** * ISDNQ931IE */ TokenDict ISDNQ931IE::s_type[] = { {"Shift", Shift}, {"More data", MoreData}, {"Sending complete", SendComplete}, {"Congestion level", Congestion}, {"Repeat indicator", Repeat}, {"Segmented", Segmented}, {"Bearer capability", BearerCaps}, {"Cause", Cause}, {"Call identity", CallIdentity}, {"Call state", CallState}, {"Channel identification", ChannelID}, {"Progress indicator", Progress}, {"Network-specific facilities", NetFacility}, {"Notification indicator", Notification}, {"Display", Display}, {"Date/time", DateTime}, {"Keypad facility", Keypad}, {"Signal", Signal}, {"Connected number", ConnectedNo}, {"Calling number", CallingNo}, {"Calling party subaddress", CallingSubAddr}, {"Called number", CalledNo}, {"Called party subaddress", CalledSubAddr}, {"Transit network selection", NetTransit}, {"Restart indicator", Restart}, {"Low layer compatibility", LoLayerCompat}, {"High layer compatibility", HiLayerCompat}, // Not used {"User-user", UserUser}, {"Escape", Escape}, {0,0} }; ISDNQ931IE::ISDNQ931IE(u_int16_t type) : NamedList(""), m_type(type) { *(String*)this = typeName(m_type,"Unknown"); } ISDNQ931IE::~ISDNQ931IE() { } void ISDNQ931IE::toString(String& dest, bool extendedDebug, const char* before) { dest << before; dest << *((NamedList*)this); // Append content ? if (extendedDebug) { // Add condeset and value dest << " (codeset=" << (m_type >> 8) << " type=" << (u_int8_t)m_type << ')'; String tmp; // Dump data if (m_buffer.length()) { tmp.hexify(m_buffer.data(),m_buffer.length(),' '); dest << " " << tmp; } // Show fields tmp = before; tmp << " "; for (unsigned int i = 0; ; i++) { NamedString* param = getParam(i); if (!param) break; dest << tmp << param->name() << '=' << *param; } } } /** * ISDNQ931Message */ TokenDict ISDNQ931Message::s_type[] = { {"ALERTING", Alerting}, {"CALL PROCEEDING", Proceeding}, {"CONNECT", Connect}, {"CONNECT ACK", ConnectAck}, {"PROGRESS", Progress}, {"SETUP", Setup}, {"SETUP ACK", SetupAck}, {"RESUME", Resume}, {"RESUME ACK", ResumeAck}, {"RESUME REJECT", ResumeRej}, {"SUSPEND", Suspend}, {"SUSPEND ACK", SuspendAck}, {"SUSPEND REJECT", SuspendRej}, {"USER INFO", UserInfo}, {"DISCONNECT", Disconnect}, {"RELEASE", Release}, {"RELEASE COMPLETE", ReleaseComplete}, {"RESTART", Restart}, {"RESTART ACK", RestartAck}, {"SEGMENT", Segment}, {"CONGESTION CONTROL", CongestionCtrl}, {"INFORMATION", Info}, {"NOTIFY", Notify}, {"STATUS", Status}, {"STATUS ENQUIRY", StatusEnquiry}, {0,0} }; ISDNQ931Message::ISDNQ931Message(Type type, bool initiator, u_int32_t callRef, u_int8_t callRefLen) : SignallingMessage(typeName(type)), m_type(type), m_initiator(initiator), m_callRef(callRef), m_callRefLen(callRefLen), m_unkMandatory(false), m_dummy(false) { } ISDNQ931Message::ISDNQ931Message(Type type) : SignallingMessage(typeName(type)), m_type(type), m_initiator(false), m_callRef(0), m_callRefLen(0), m_unkMandatory(false), m_dummy(true) { } ISDNQ931Message::ISDNQ931Message(Type type, ISDNQ931Call* call) : SignallingMessage(typeName(type)), m_type(type), m_initiator(false), m_callRef(0), m_callRefLen(0), m_unkMandatory(false), m_dummy(false) { if (!call) return; m_initiator = call->outgoing(); m_callRef = call->callRef(); m_callRefLen = call->callRefLen(); } ISDNQ931Message::~ISDNQ931Message() { } // Get an IE from list starting from the begining or from a given point ISDNQ931IE* ISDNQ931Message::getIE(ISDNQ931IE::Type type, ISDNQ931IE* base) { ObjList* obj = m_ie.skipNull(); // Set start point after base if non 0 if (base) { for (; obj; obj = obj->skipNext()) if (base == obj->get()) { obj = obj->skipNext(); break; } } for (; obj; obj = obj->skipNext()) { ISDNQ931IE* ie = static_cast(obj->get()); if (ie->type() == type) return ie; } return 0; } // Remove an IE from list and returns it ISDNQ931IE* ISDNQ931Message::removeIE(ISDNQ931IE::Type type, ISDNQ931IE* base) { ObjList* obj = m_ie.skipNull(); // Set start point after base if non 0 if (base) { for (; obj; obj = obj->skipNext()) if (base == obj->get()) { obj = obj->skipNext(); break; } } ISDNQ931IE* ie = 0; for (; obj; obj = obj->skipNext()) { ie = static_cast(obj->get()); if (ie->type() == type) break; ie = 0; } if (ie) m_ie.remove(ie,false); return ie; } // Safely appends an IE to the list bool ISDNQ931Message::appendSafe(ISDNQ931IE* ie) { if (!ie) return false; // Special care for some IEs: // Don't append Shift or Segment. Don't accept Repeat for now switch (ie->type()) { case ISDNQ931IE::Shift: case ISDNQ931IE::Segmented: case ISDNQ931IE::Repeat: delete ie; return false; } // This is not a safe way, but is good for now // TODO: Insert the IE in the proper order. Insert Shift if nedded. Special care for Repeat IE append(ie); return true; } void ISDNQ931Message::toString(String& dest, bool extendedDebug, const char* indent) const { #define STARTLINE(indent) "\r\n" << indent const char* enclose = "-----"; String ind = indent; ind << " "; dest << STARTLINE(indent) << enclose; dest << STARTLINE(indent) << name() << STARTLINE(ind); if (!m_dummy) { dest << "[From initiator=" << String::boolText(m_initiator); dest << " CallRef=" << (unsigned int)m_callRef << ']'; } else dest << "[Dummy call reference]"; // Dump message header if (extendedDebug && m_buffer.length()) { String s; s.hexify(m_buffer.data(),m_buffer.length(),' '); dest << " " << s; } // Add IEs String ieBefore; ieBefore << STARTLINE(ind); ObjList* obj = m_ie.skipNull(); for (; obj; obj = obj->skipNext()) { ISDNQ931IE* ie = static_cast(obj->get()); ie->toString(dest,extendedDebug,ieBefore); } dest << STARTLINE(indent) << enclose; #undef STARTLINE } void* ISDNQ931Message::getObject(const String& name) const { if (name == "ISDNQ931Message") return (void*)this; return SignallingMessage::getObject(name); } u_int8_t ISDNQ931Message::encode(ISDNQ931ParserData& parserData, ObjList& dest) { Q931Parser parser(parserData); return parser.encode(this,dest); } ISDNQ931Message* ISDNQ931Message::parse(ISDNQ931ParserData& parserData, const DataBlock& buffer, DataBlock* segData) { Q931Parser parser(parserData); return parser.decode(buffer,segData); } /** * Q931Parser */ #define Q931_MSG_PROTOQ931 0x08 // Q.931 protocol discriminator // Get bit 7 to check if the current byte is extended to the next one // Used to parse message IE #define Q931_EXT_FINAL(val) ((val & 0x80) != 0) // Max values for some IEs #define Q931_MAX_BEARERCAPS_LEN 12 #define Q931_MAX_SEGMENTED_LEN 4 #define Q931_MAX_CAUSE_LEN 32 #define Q931_MAX_CHANNELID_LEN 255 #define Q931_MAX_CALLINGNO_LEN 255 #define Q931_MAX_CALLEDNO_LEN 255 #define Q931_MAX_KEYPAD_LEN 34 // Parse errors static const char* s_errorNoData = "no data"; static const char* s_errorWrongData = "inconsistent data"; static const char* s_errorNoCause = "no cause value"; static const char* s_errorUnsuppRecommen = "unsupported recommendation"; static const char* s_errorUnsuppCoding = "unsupported coding standard"; // // IE descriptions // // *** Fixed (1 byte length) IEs // 4.5.14 TokenDict Q931Parser::s_dict_congestion[] = { {"recv-ready", 0x00}, // Receiver ready {"recv-not-ready", 0x0f}, // Receiver not ready // aliases for level=... {"yes", 0x00}, {"true", 0x00}, {"no", 0x0f}, {"false", 0x0f}, {0,0} }; static const IEParam s_ie_ieFixed[] = { {"lock", 0x08, 0}, // Shift {"codeset", 0x07, 0}, // Shift {"level", 0x0f, Q931Parser::s_dict_congestion}, // Congestion {"indication", 0x0f, 0}, // Repeat {0,0,0} }; // *** Q.931 4.5.5: Bearer capability // Q.931 4.5.5. Information transfer capability: Bits 0-4 TokenDict Q931Parser::s_dict_bearerTransCap[] = { {"speech", 0x00}, // Speech {"udi", 0x08}, // Unrestricted digital information {"rdi", 0x09}, // Restricted digital information {"3.1khz-audio", 0x10}, // 3.1 khz audio {"udi-ta", 0x11}, // Unrestricted digital information with tone/announcements {"video", 0x18}, // Video {0,0} }; // Q.931 4.5.5. Transfer mode: Bits 5,6 TokenDict Q931Parser::s_dict_bearerTransMode[] = { {"circuit", 0x00}, // Circuit switch mode {"packet", 0x40}, // Packet mode {0,0} }; // Q.931 4.5.5. Transfer rate: Bits 0-4 TokenDict Q931Parser::s_dict_bearerTransRate[] = { {"packet", 0x00}, // Packet mode use {"64kbit", 0x10}, // 64 kbit/s {"2x64kbit", 0x11}, // 2x64 kbit/s {"384kbit", 0x13}, // 384 kbit/s {"1536kbit", 0x15}, // 1536 kbit/s {"1920kbit", 0x17}, // 1920 kbit/s {"multirate", 0x18}, // Multirate (64 kbit/s base rate) {0,0} }; // Q.931 4.5.5. User information Layer 1 protocol: Bits 0-4 TokenDict Q931Parser::s_dict_bearerProto1[] = { {"v110", 0x01}, // Recomendation V.110 and X.30 {"mulaw", 0x02}, // Recomendation G.711 mu-law {"alaw", 0x03}, // Recomendation G.711 A-law {"g721", 0x04}, // Recomendation G.721 32kbit/s ADPCM and I.460 {"h221", 0x05}, // Recomendation H.221 and H.242 {"non-CCITT", 0x07}, // Non CCITT standardized rate adaption {"v120", 0x08}, // Recomendation V.120 {"x31", 0x09}, // Recomendation X.31 HDLC flag stuffing {0,0} }; // Q.931 4.5.5. User information Layer 2 protocol: Bits 0-4 TokenDict Q931Parser::s_dict_bearerProto2[] = { {"q921", 0x02}, // Recommendation Q.921 or I441 {"x25", 0x06}, // Recommendation X.25 link layer {0,0} }; // Q.931 4.5.5. User information Layer 3 protocol: Bits 0-4 TokenDict Q931Parser::s_dict_bearerProto3[] = { {"q931", 0x02}, // Recommendation Q.931 or I451 {"x25", 0x06}, // Recommendation X.25 packet layer {0,0} }; // IE description static const IEParam s_ie_ieBearerCaps[] = { {"transfer-cap", 0x1f, Q931Parser::s_dict_bearerTransCap}, // Tranfer capability {"transfer-mode", 0x60, Q931Parser::s_dict_bearerTransMode}, // Transfer mode {"transfer-rate", 0x1f, Q931Parser::s_dict_bearerTransRate}, // Transfer rate {"rate-multiplier", 0x7f, 0}, // Rate multiplier {"layer1-protocol", 0x1f, Q931Parser::s_dict_bearerProto1}, // Layer 1 protocol {"layer1-data", 0xff, 0}, // Unparsed layer 1 data (for modems) {"layer2-protocol", 0x1f, Q931Parser::s_dict_bearerProto2}, // Layer 2 protocol {"layer3-protocol", 0x1f, Q931Parser::s_dict_bearerProto3}, // Layer 3 protocol {0,0,0} }; // *** Q.931 4.5.6: Call identity // IE description static const IEParam s_ie_ieCallIdentity[] = { {"identity", 0, 0}, // Call identity data {0,0,0} }; // *** Q.931 4.5.7: Call state // Call state values: bit 0-5 // IE description static const IEParam s_ie_ieCallState[] = { {"state", 0x3f, ISDNQ931Call::s_states}, {0,0,0} }; // *** Q.931 4.5.8: Called party number // Q.931 4.5.10: Calling party number // Q.931 4.5.10 Type of number: Bits 4-6 TokenDict Q931Parser::s_dict_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} }; // Q.931 4.5.10 Numbering plan: Bits 0-3. Apply only for type 0,1,2,4 TokenDict Q931Parser::s_dict_numPlan[] = { {"unknown", 0x00}, // Unknown {"isdn", 0x01}, // ISDN/telephoby numbering plan {"data", 0x03}, // Data numbering plan {"telex", 0x04}, // Telex numbering plan {"national", 0x08}, // National numbering plan {"private", 0x09}, // Private numbering plan {"reserved", 0x0f}, // Reserved for extension {0,0} }; // Q.931 4.5.10 Presentation indicator: Bits 5,6 TokenDict Q931Parser::s_dict_presentation[] = { {"allowed", 0x00}, // Presentation allowed {"restricted", 0x20}, // Presentation restricted {"unavailable", 0x40}, // Number not available due to interworking {"reserved", 0x50}, // Reserved // Aliases for presentation=... {"yes", 0x00}, {"true", 0x00}, {"no", 0x20}, {"false", 0x20}, {0,0} }; // Q.931 4.5.10 Presentation indicator: Bits 0,1 TokenDict Q931Parser::s_dict_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} }; // IE description static const IEParam s_ie_ieNumber[] = { {"type", 0x70, Q931Parser::s_dict_typeOfNumber}, // Type of number {"plan", 0x0f, Q931Parser::s_dict_numPlan}, // Numbering plan {"presentation", 0x60, Q931Parser::s_dict_presentation}, // Presentation {"screening", 0x03, Q931Parser::s_dict_screening}, // Screening {"number", 0x7f, 0}, // The number {0,0,0} }; // *** Q.931 4.5.9: Called party subaddress // Q.931 4.5.11: Calling party subaddress // Q.931 4.5.9 Type of subaddress: Bits 5-6 TokenDict Q931Parser::s_dict_subaddrType[] = { {"nsap", 0x00}, // NSAP (CCITT Rec. X.213/ISO 8348 AD2) {"user", 0x20}, // User-specified {0,0} }; // IE description static const IEParam s_ie_ieSubAddress[] = { {"type", 0x60, Q931Parser::s_dict_subaddrType}, // Type of subaddress {"odd", 0x10, 0}, // Odd/even indicator of number of address signals {"subaddress", 0xff, 0}, // Subaddress information {0,0,0} }; // *** Q.931 4.5.12: Cause // IE description static const IEParam s_ie_ieCause[] = { {"location", 0x0f, SignallingUtils::locations()}, {"cause", 0x7f, SignallingUtils::dict(0,0)}, {"diagnostic", 0xff, 0}, {0,0,0} }; // *** Q.931 4.5.13: Channel identification // Q.931 4.5.13. Channel id selection for BRI interface: Bits 0,1 TokenDict Q931Parser::s_dict_channelIDSelect_BRI[] = { {"none", 0x00}, // No channel {"b1", 0x01}, // B1 channel {"b2", 0x02}, // B2 channel {"any", 0x03}, // Any channel {0,0} }; // Q.931 4.5.13. Channel id selection for PRI interface: Bits 0,1 TokenDict Q931Parser::s_dict_channelIDSelect_PRI[] = { {"none", 0x00}, // No channel {"present", 0x01}, // Defined by the following bytes {"reserved", 0x02}, // Reserved value {"any", 0x03}, // Any channel {0,0} }; // Q.931 4.5.13. Channel type: Bits 0-3 TokenDict Q931Parser::s_dict_channelIDUnits[] = { {"B", 0x03}, // B-channel {"H0", 0x06}, // H0-channel {"H11", 0x08}, // H11-channel {"H12", 0x09}, // H12-channel {0,0} }; // IE description static const IEParam s_ie_ieChannelID[] = { {"interface-bri", 0x20, 0}, // Interface it's a basic rate one {"channel-exclusive", 0x08, 0}, // The indicated B channel is exclusive/preferred {"d-channel", 0x04, 0}, // The channel identified is the D-channel {"channel-select", 0x03, Q931Parser::s_dict_channelIDSelect_BRI}, // Channel select for BRI interface {"channel-select", 0x03, Q931Parser::s_dict_channelIDSelect_PRI}, // Channel select for PRI interface {"interface", 0x7f, 0}, // Interface identifier {"channel-by-number", 0x10, 0}, // Channel is given by number or slot map {"type", 0x0f, Q931Parser::s_dict_channelIDUnits}, // Channel type {"channels", 0x7f, 0}, // Channel number(s) {"slot-map", 0xff, 0}, // Slot-map {0,0,0} }; // *** Q.931 4.5.15: Date/time // IE description static const IEParam s_ie_ieDateTime[] = { {"year", 0xff, 0}, // Integer value {"month", 0xff, 0}, {"day", 0xff, 0}, {"hour", 0xff, 0}, {"minute", 0xff, 0}, {"second", 0xff, 0}, {0,0,0} }; // *** Q.931 4.5.16: Display // IE description static const IEParam s_ie_ieDisplay[] = { {"charset", 0x7f, 0}, // Charset, if any {"display", 0x7f, 0}, // IA5 characters {0,0,0} }; // *** Q.931 4.5.17: High layer compatibility // IE description static const IEParam s_ie_ieHiLayerCompat[] = { {"interpretation", 0x1c, 0}, // Interpretation {"presentation", 0x03, 0}, // Presentation method or protocol profile {"layer", 0x7f, 0}, // High layer characteristics identification if presentation is 0x01 {"layer", 0x7f, 0}, // High layer characteristics identification for other values of interpretation {"layer-ext", 0x7f, 0}, // Extended high layer characteristics identification if presentation is 0x01 {"layer-ext", 0x7f, 0}, // Extended high layer characteristics identification for other values of interpretation {0,0,0} }; // *** Q.931 4.5.18: Keypad facility // IE description static const IEParam s_ie_ieKeypad[] = { {"keypad", 0, 0}, // IA5 characters {0,0,0} }; // *** Q.931 4.5.19: Low layer compatibility // Q.931 4.5.19. User information Layer 2 protocol: Bits 0-4 TokenDict Q931Parser::s_dict_loLayerProto2[] = { {"iso1745", 0x01}, // Basic mode ISO 1745 {"q921", 0x02}, // Recommendation Q.921 or I441 {"x25", 0x06}, // Recommendation X.25 link layer {"x25-multilink", 0x0f}, // Recommendation X.25 multilink {"lapb", 0x08}, // Extended LAPB; for half duplex operation {"hdlc-arm", 0x09}, // HDLC ARM (ISO 4335) {"hdlc-nrm", 0x0a}, // HDLC NRM (ISO 4335) {"hdlc-abm", 0x0b}, // HDLC ABM (ISO 4335) {"lan", 0x0c}, // LAN logical link control {"x75", 0x0d}, // Recommendation X.75. Single Link Procedure (SLP) {"q922", 0x0e}, // Recommendation Q.922 {"q922-core", 0x0f}, // Core aspects of Recommendation Q.922 {"user", 0x10}, // User specified {"iso7776", 0x11}, // ISO 7776 DTE-DTE operation {0,0} }; // Q.931 4.5.19. User information Layer 3 protocol: Bits 0-4 TokenDict Q931Parser::s_dict_loLayerProto3[] = { {"q931", 0x02}, // Recommendation Q.931 or I451 {"x25", 0x06}, // Recommendation X.25 packet layer {"iso8208", 0x07}, // ISO/IEC 8208 (X.25 packet level protocol for data terminal equipment) {"x223", 0x08}, // CCITT Rec. X.223|ISO 8878 {"iso8473", 0x09}, // ISO/IEC 8473 (OSI connectionless mode protocol) {"t70", 0x0a}, // Recommendation T.70 minimum network layer {"iso-tr-9577", 0x0b}, // ISO/IEC TR 9577 (Protocol identification in the network layer) {"user", 0x10}, // User specified {0,0} }; // IE description static const IEParam s_ie_ieLoLayerCompat[] = { {"transfer-cap", 0x1f, Q931Parser::s_dict_bearerTransCap}, // Tranfer capability {"out-band", 0x40, 0}, // Outband negotiation possible or not {"transfer-mode", 0x60, Q931Parser::s_dict_bearerTransMode}, // Transfer mode {"transfer-rate", 0x1f, Q931Parser::s_dict_bearerTransRate}, // Transfer rate {"rate-multiplier", 0x7f, 0}, // Rate multiplier {"layer1-protocol", 0x1f, Q931Parser::s_dict_bearerProto1}, // Layer 1 protocol {"layer1-data", 0xff, 0}, // Unparsed layer 1 data {"layer2-protocol", 0x1f, Q931Parser::s_dict_loLayerProto2}, // Layer 2 protocol {"layer2-data", 0xff, 0}, // Unparsed layer 2 data {"layer2-window-size",0x1f, 0}, // Window size (k) {"layer3-protocol", 0x1f, Q931Parser::s_dict_loLayerProto3}, // Layer 3 protocol {"layer3-mode", 0x60, 0}, // Mode for CCITT 'layer3-protocol' values {"layer3-user-data", 0x7f, 0}, // Optional Layer 3 user data {"layer3-7a", 0x7f, 0}, // Unparsed octet 7a {"layer3-def-size", 0x1f, 0}, // Default packet size {"layer3-packet-size",0x7f, 0}, // Packet window size {0,0,0} }; // *** Q.931 4.5.21: Network-specific facilities // Q.931 4.5.29: Transit network selection // Q.931 4.5.21. Type of network identification: Bits 4-6 TokenDict Q931Parser::s_dict_networkIdType[] = { {"user", 0x00}, // User specified {"national", 0x20}, // National network identification {"international", 0x30}, // International network identification {0,0} }; // Q.931 4.5.21. Network identification plan: Bits 0-3 TokenDict Q931Parser::s_dict_networkIdPlan[] = { {"unknown", 0x00}, // Unknown {"carrier", 0x01}, // Carrier identification code {"data", 0x03}, // Data network identification code (Recommendation X.121) {0,0} }; // Q.931 4.5.21: Network-specific facilities static const IEParam s_ie_ieNetFacility[] = { {"type", 0x70, Q931Parser::s_dict_networkIdType}, // Type of network identification {"plan", 0x0f, Q931Parser::s_dict_networkIdPlan}, // Network identification plan {"id", 0xff, 0}, // Network identification {"facility", 0xff, 0}, // Network-specific facility {0,0,0} }; // Q.931 4.5.29: Transit network selection static const IEParam s_ie_ieNetTransit[] = { {"type", 0x70, Q931Parser::s_dict_networkIdType}, // Type of network identification {"plan", 0x0f, Q931Parser::s_dict_networkIdPlan}, // Network identification plan {"id", 0xff, 0}, // Network identification {0,0,0} }; // *** Q.931 4.5.22: Notification TokenDict Q931Parser::s_dict_notification[] = { {"suspended", 0x00}, {"resumed", 0x01}, {"bearer-service-change", 0x02}, {0,0} }; // IE description static const IEParam s_ie_ieNotification[] = { {"notification", 0x7f, Q931Parser::s_dict_notification}, {0,0,0} }; // *** Q.931 4.5.23: Progress indication // Progress description: Bits 0-6 TokenDict Q931Parser::s_dict_progressDescr[] = { {"non-isdn", 0x01}, // Call is not end-to-end ISDN, further call progress info may be present in-band {"non-isdn-destination", 0x02}, // Destination address is non ISDN {"non-isdn-source", 0x03}, // Source address is non ISDN {"return-to-isdn", 0x04}, // Call has returned to the ISDN {"interworking", 0x05}, // Interworking has occurred and has resulted in a telecommunication change {"in-band-info", 0x08}, // In-band info or an appropriate pattern is now available {0,0} }; // IE description static const IEParam s_ie_ieProgress[] = { {"location", 0x0f, SignallingUtils::locations()}, {"description", 0x7f, Q931Parser::s_dict_progressDescr}, {0,0,0} }; // *** Q.931 4.5.25: Restart indicator // Class: Bits 0-2 TokenDict Q931Parser::s_dict_restartClass[] = { {"channels", 0x00}, // Indicated channels {"interface", 0x06}, // Single interface {"all-interfaces", 0x07}, // All interfaces {0,0} }; // IE description static const IEParam s_ie_ieRestart[] = { {"class", 0x07, Q931Parser::s_dict_restartClass}, {0,0,0} }; // *** Q.931 4.5.26: Segmented message // IE description static const IEParam s_ie_ieSegmented[] = { {"first", 0x80, 0}, // First/subsequent segment {"remaining", 0x7f, 0}, // Number of segments remaining {"message", 0x7f, 0}, // Segmented message type {0,0,0} }; // *** Q.931 4.5.28: Signal // Q.931 4.5.28 Signal values: first byte TokenDict Q931Parser::s_dict_signalValue[] = { {"dial", 0x00}, // Dial tone on {"ring", 0x01}, // Ring back tone on {"intercept", 0x02}, // Intercept tone on {"congestion", 0x03}, // Network congestion tone on {"busy", 0x04}, // Busy tone on {"confirm", 0x05}, // Confirm tone on {"answer", 0x06}, // Answer tone on {"call-waiting", 0x07}, // Call waiting tone on {"off-hook", 0x08}, // Off-hook tone on {"preemption", 0x09}, // Preemption tone on {"tones-off", 0x3f}, // Tones off {"patern0", 0x40}, // Alering on - patern 0 {"patern1", 0x41}, // Alering on - patern 1 {"patern2", 0x42}, // Alering on - patern 2 {"patern3", 0x43}, // Alering on - patern 3 {"patern4", 0x44}, // Alering on - patern 4 {"patern5", 0x45}, // Alering on - patern 5 {"patern6", 0x46}, // Alering on - patern 6 {"patern7", 0x47}, // Alering on - patern 7 {"alerting-off", 0x4f}, // Alerting off {0,0} }; // IE description static const IEParam s_ie_ieSignal[] = { {"signal", 0xff, Q931Parser::s_dict_signalValue}, // Signal value {0,0,0} }; // *** Q.931 4.5.30: User-user // IE description static const IEParam s_ie_ieUserUser[] = { {"protocol", 0xff, 0}, // Protocol discriminator {"information", 0xff, 0}, // User information {0,0,0} }; // Decode received buffer ISDNQ931Message* Q931Parser::decode(const DataBlock& buffer, DataBlock* segData) { XDebug(m_settings->m_dbg,DebugAll,"Start parse %u bytes",buffer.length()); // Set data u_int8_t* data = (u_int8_t*)buffer.data(); u_int32_t len = buffer.length(); // Parse header. Create message if (!createMessage(data,len)) return reset(); // Skip header bytes: // 3: protocol discriminator, call reference length, message type // n: call reference u_int32_t consumed = 3 + m_msg->callRefLen(); ISDNQ931IE* ie = 0; // Parse SEGMENT if (m_msg->type() == ISDNQ931Message::Segment) { len -= consumed; data += consumed; return processSegment(data,len,segData); } // Parse IEs m_activeCodeset = m_codeset = 0; for (;;) { // Append IE if any if (ie) { // Skip non-locked IEs if told to do so if (m_settings->flag(ISDNQ931::IgnoreNonLockedIE)) { bool ignore = false; if (ie->type() == ISDNQ931IE::Shift) ignore = m_skip = !ie->getBoolValue("lock",false); else if (m_skip) { ignore = true; m_skip = false; } if (ignore) { String* s = static_cast(ie); *s = String("ignored-") + *s; } } XDebug(m_settings->m_dbg,DebugAll,"Adding IE '%s'. %u bytes consumed [%p]", ie->c_str(),consumed,m_msg); if (m_settings->m_extendedDebug) ie->m_buffer.assign(data,consumed); m_msg->append(ie); } // Reset the active codeset m_activeCodeset = m_codeset; // End of data ? if (consumed >= len) break; len -= consumed; data += consumed; consumed = 0; ie = getIE(data,len,consumed); if (!ie) break; // Check shift if (ie->type() == ISDNQ931IE::Shift) shiftCodeset(ie); } return reset(); } // Encode a message to a buffer. If buffer is too long, split it into segments if allowed u_int8_t Q931Parser::encode(ISDNQ931Message* msg, ObjList& dest) { if (!msg) return 0; m_msg = msg; // Set message header buffer // Proto discriminator (1) + call reference length (1) + call reference (max 4) + type (1) + [Segmented IE] u_int8_t header[7 + Q931_MAX_SEGMENTED_LEN]; ::memset(header,0,sizeof(header)); u_int8_t headerLen = fillHeader(header,m_msg,m_settings->m_dbg); if (!headerLen) { reset(); return 0; } if (m_settings->m_extendedDebug) msg->m_buffer.assign(header,headerLen); // We assume that at this point the IE list is ready to be encoded as it is // Check if segmentation is allowed if (!m_settings->m_allowSegment) return encodeMessage(dest,false,header,headerLen); // Segmentation is allowed bool segmented = false; // Encode each IE into it's buffer. Check if the largest IE will fit in a message if (!encodeIEList(segmented,headerLen)) return reset(0); // Check if the message is segmented if (!segmented) return encodeMessage(dest,true,header,headerLen); // Message will be segmented. Change the header // Change the message type to Segment. Append Segmented IE u_int8_t msgType = header[headerLen - 1]; // Message type it's the last byte of the header header[headerLen - 1] = 0x7f & (u_int8_t)ISDNQ931Message::Segment; header[headerLen++] = 0x7f & (u_int8_t)ISDNQ931IE::Segmented; header[headerLen++] = 2; // IE information length after IE header u_int8_t remainingIdx = headerLen; // Remember the index to write the remaining segments count header[headerLen++] = 0; // Reserved space for remaining segments header[headerLen++] = msgType; // Message type // Create message segments ObjList* obj = m_msg->ieList()->skipNull(); u_int8_t count = 0; DataBlock* segment = 0; while (true) { ISDNQ931IE* ie = static_cast(obj->get()); DataBlock* data = &(ie->m_buffer); obj = obj->skipNext(); // Force append when done with the list bool append = (bool)(!obj); if (!segment) segment = new DataBlock(header,headerLen); // Add data to buffer if we have enough place // Force append if new data excceeds the segment length if (segment->length() + data->length() <= m_settings->m_maxMsgLen) { *segment += *data; data = 0; } else append = true; // Append segment to list if (append) { if (!appendSegment(dest,segment,count)) { count = 0; break; } segment = 0; } // Append data to segment if not already added if (data) { if (!segment) segment = new DataBlock(header,headerLen); *segment += *data; } // Keep going if we have more IEs if (obj) continue; // No more IEs. Check if last one was added to segment if (segment && !appendSegment(dest,segment,count)) { count = 0; break; } break; } if (!count) { dest.clear(); return reset(0); } u_int8_t remaining = count; bool first = true; obj = dest.skipNull(); for (; obj; obj = obj->skipNext()) { segment = static_cast(obj->get()); u_int8_t* data = (u_int8_t*)(segment->data()); if (!first) data[remainingIdx] = --remaining; else { data[remainingIdx] = 0x80 | --remaining; first = false; } } return reset(count); } // Create message segments if segmented u_int8_t Q931Parser::encodeMessage(ObjList& dest, bool ieEncoded, u_int8_t* header, u_int8_t headerLen) { DataBlock* buf = new DataBlock(header,headerLen); ObjList* obj = m_msg->ieList()->skipNull(); for (; obj; obj = obj->skipNext()) { ISDNQ931IE* ie = static_cast(obj->get()); // Encode current IE if not already encoded if (!ieEncoded && !encodeIE(ie,ie->m_buffer)) { delete buf; return reset(0); } // Check for valid data length if (buf->length() + ie->m_buffer.length() > m_settings->m_maxMsgLen) { Debug(m_settings->m_dbg,DebugWarn, "Can't encode message. Length %u exceeds limit %u [%p]", buf->length() + ie->m_buffer.length(),m_settings->m_maxMsgLen,m_msg); delete buf; return reset(0); } *buf += ie->m_buffer; } dest.append(buf); return reset(1); } // Encode a list of IEs bool Q931Parser::encodeIEList(bool& segmented, u_int8_t headerLen) { segmented = false; ObjList* obj = m_msg->ieList()->skipNull(); // Empty message if (!obj) return true; // Encode each IE into it's buffer u_int32_t dataLen = headerLen; ISDNQ931IE* ieMax = 0; for (; obj; obj = obj->skipNext()) { // Encode current IE ISDNQ931IE* ie = static_cast(obj->get()); if (!encodeIE(ie,ie->m_buffer)) return false; // Check if the message will be segmented if (!segmented) { dataLen += ie->m_buffer.length(); if (dataLen > m_settings->m_maxMsgLen) segmented = true; } // Keep the IE with the largest buffer if (!ieMax || ieMax->m_buffer.length() < ie->m_buffer.length()) ieMax = ie; } // Check if the largest IE buffer fits a message if (ieMax && ieMax->m_buffer.length() > m_settings->m_maxMsgLen - headerLen) { Debug(m_settings->m_dbg,DebugWarn, "Can't encode message. IE '%s' with length %u won't fit limit %u [%p]", ieMax->c_str(),ieMax->m_buffer.length(),m_settings->m_maxMsgLen,m_msg); return false; } return true; } // Append a segment to a given list bool Q931Parser::appendSegment(ObjList& dest, DataBlock* segment, u_int8_t& count) { count++; // We can't split a message in more then 128 segments (see Q.931 4.5.26) if (count <= m_settings->m_maxSegments) { dest.append(segment); return true; } delete segment; Debug(m_settings->m_dbg,DebugWarn, "Can't encode message. Too many segments [%p]",m_msg); return false; } // Encode a single IE bool Q931Parser::encodeIE(ISDNQ931IE* ie, DataBlock& buffer) { switch (ie->type()) { case ISDNQ931IE::BearerCaps: return encodeBearerCaps(ie,buffer); case ISDNQ931IE::Cause: return encodeCause(ie,buffer); case ISDNQ931IE::Display: return encodeDisplay(ie,buffer);; case ISDNQ931IE::CallingNo: return encodeCallingNo(ie,buffer); case ISDNQ931IE::CalledNo: return encodeCalledNo(ie,buffer); case ISDNQ931IE::CallState: return encodeCallState(ie,buffer); case ISDNQ931IE::ChannelID: return encodeChannelID(ie,buffer); case ISDNQ931IE::Progress: return encodeProgress(ie,buffer); case ISDNQ931IE::Notification: return encodeNotification(ie,buffer); case ISDNQ931IE::Keypad: return encodeKeypad(ie,buffer); case ISDNQ931IE::Signal: return encodeSignal(ie,buffer); case ISDNQ931IE::Restart: return encodeRestart(ie,buffer); case ISDNQ931IE::SendComplete: return encodeSendComplete(ie,buffer); } Debug(m_settings->m_dbg,DebugMild,"Encoding not implemented for IE '%s' [%p]", ie->c_str(),m_msg); // Encode anyway. Only type with length=0 u_int8_t header[2] = {(u_int8_t)ie->type(),0}; buffer.assign(header,sizeof(header)); return true; } ISDNQ931IE* Q931Parser::errorParseIE(ISDNQ931IE* ie, const char* reason, const u_int8_t* data, u_int32_t len) { Debug(m_settings->m_dbg,DebugNote,"Error parse IE ('%s'): %s [%p]", ie->c_str(),reason,m_msg); ie->addParam("error",reason); if (len) SignallingUtils::dumpData(0,*ie,"error-data",data,len); return ie; } // Check the coding standard of an IE bool Q931Parser::checkCoding(u_int8_t value, u_int8_t expected, ISDNQ931IE* ie) { value &= 0x60; if (value == expected) return true; String s = lookup(value,SignallingUtils::codings(),0); if (s.null()) s = (unsigned int)value; ie->addParam("coding",s.c_str()); return false; } // Skip extended bytes until a byte with bit 0 is reached u_int8_t Q931Parser::skipExt(const u_int8_t* data, u_int8_t len, u_int8_t& crt) { u_int8_t skip = 0; for (; crt < len && !Q931_EXT_FINAL(data[crt]); crt++, skip++) ; if (crt < len) { crt++; skip++; } return skip; } // Create a message from received data (parse message header) // See Q.931 5.8.1, 5.8.2, 5.8.3.1 for protocol discriminator, message length // and call reference length errors bool Q931Parser::createMessage(u_int8_t* data, u_int32_t len) { bool initiator = false; u_int32_t callRef = 0; u_int8_t callRefLen = 0; // We should have at least 3 bytes: // 1 for protocol discriminator, 1 for call reference and 1 for message type if (!data || len < 3) { Debug(m_settings->m_dbg,DebugWarn, "Not enough data (%u) for message header",len); return false; } // Check protocol discriminator if (data[0] != Q931_MSG_PROTOQ931) { Debug(m_settings->m_dbg,DebugWarn,"Unknown protocol discriminator %u",data[0]); return false; } // Check for dummy call reference if (data[1]) { // Call id length: bits 4-7 of the 2nd byte should be 0 if (data[1] & 0xf0) { Debug(m_settings->m_dbg,DebugWarn, "Call reference length %u is incorrect",data[1]); return false; } // Call id length: bits 0-3 of the 2nd byte callRefLen = data[1] & 0x0f; // Initiator flag: bit 7 of the 3rd byte - 0: From initiator. 1: To initiator initiator = (data[2] & 0x80) == 0; // We should have at least (callRefLen + 3) bytes: // 1 for protocol discriminator, 1 for call reference length, // 1 for message type and the call reference if ((unsigned int)(callRefLen + 3) > len) { Debug(m_settings->m_dbg,DebugWarn, "Call reference length %u greater then data length %u", callRefLen,len); return false; } // Call reference switch (callRefLen) { case 4: callRef = (data[2] & 0x7f) << 24 | data[3] << 16 | data[4] << 8 | data[5]; break; case 3: callRef = (data[2] & 0x7f) << 16 | data[3] << 8 | data[4]; break; case 2: callRef = (data[2] & 0x7f) << 8 | data[3]; break; case 1: callRef = data[2] & 0x7f; break; default: Debug(m_settings->m_dbg,DebugWarn, "Unsupported call reference length %u",callRefLen); return false; } } // Message type: bits 0-6 of the 1st byte after the call reference u_int8_t t = data[callRefLen + 2] & 0x7f; if (!ISDNQ931Message::typeName(t)) { Debug(m_settings->m_dbg,DebugNote,"Unknown message type %u",t); return false; } if (callRefLen) m_msg = new ISDNQ931Message((ISDNQ931Message::Type)t,initiator,callRef, callRefLen); else m_msg = new ISDNQ931Message((ISDNQ931Message::Type)t); if (m_settings->m_extendedDebug) m_msg->m_buffer.assign(data,callRefLen + 3); XDebug(m_settings->m_dbg,DebugAll,"Created message (%p): '%s'", m_msg,m_msg->name()); return true; } // Process received Segment message ISDNQ931Message* Q931Parser::processSegment(const u_int8_t* data, u_int32_t len, DataBlock* segData) { if (!segData) { Debug(m_settings->m_dbg,DebugNote, "Dropping segment message. Not allowed [%p]",m_msg); m_msg->deref(); m_msg = 0; return reset(); } u_int32_t consumed = 0; ISDNQ931IE* ie = getIE(data,len,consumed); if (!ie) { m_msg->deref(); m_msg = 0; return reset(); } if (ie->type() != ISDNQ931IE::Segmented || consumed > len) { Debug(m_settings->m_dbg,DebugNote, "Dropping segment message with missing or invalid Segmented IE [%p]", m_msg); delete ie; m_msg->deref(); m_msg = 0; return reset(); } m_msg->append(ie); segData->assign((void*)(data + consumed),len - consumed); return reset(); } // Get a single IE from a buffer ISDNQ931IE* Q931Parser::getIE(const u_int8_t* data, u_int32_t len, u_int32_t& consumed) { consumed = 0; if (!(data && len)) return 0; // Check if this is a fixed (1 byte length) or variable length IE // Fixed: Bit 7 is 1. See Q.931 4.5.1 if ((data[0] >> 7)) { consumed = 1; return getFixedIE(data[0]); } // Variable length // Check/Get length. Byte 2 is the length of the rest of the IE u_int8_t ieLen = ((len == 1) ? 1 : data[1]); if (len == 1 || ieLen > len - 2) { Debug(m_settings->m_dbg,DebugNote, "Invalid variable IE length %u. Remaing data: %u [%p]", ieLen,len,m_msg); consumed = len; return 0; } consumed = 2 + ieLen; // Get type u_int16_t type = ((u_int16_t)m_activeCodeset << 8) | data[0]; // Skip type and length u_int8_t* ieData = (u_int8_t*)data + 2; switch (type) { #define CASE_DECODE_IE(id,method) case id: return method(new ISDNQ931IE(id),ieData,ieLen); CASE_DECODE_IE(ISDNQ931IE::BearerCaps,decodeBearerCaps) CASE_DECODE_IE(ISDNQ931IE::Cause,decodeCause) CASE_DECODE_IE(ISDNQ931IE::Display,decodeDisplay) CASE_DECODE_IE(ISDNQ931IE::CallingNo,decodeCallingNo) CASE_DECODE_IE(ISDNQ931IE::CalledNo,decodeCalledNo) CASE_DECODE_IE(ISDNQ931IE::CallIdentity,decodeCallIdentity) CASE_DECODE_IE(ISDNQ931IE::CallState,decodeCallState) CASE_DECODE_IE(ISDNQ931IE::ChannelID,decodeChannelID) CASE_DECODE_IE(ISDNQ931IE::Progress,decodeProgress) CASE_DECODE_IE(ISDNQ931IE::NetFacility,decodeNetFacility) CASE_DECODE_IE(ISDNQ931IE::Notification,decodeNotification) CASE_DECODE_IE(ISDNQ931IE::DateTime,decodeDateTime) CASE_DECODE_IE(ISDNQ931IE::Keypad,decodeKeypad) CASE_DECODE_IE(ISDNQ931IE::Signal,decodeSignal) CASE_DECODE_IE(ISDNQ931IE::ConnectedNo,decodeConnectedNo) CASE_DECODE_IE(ISDNQ931IE::CallingSubAddr,decodeCallingSubAddr) CASE_DECODE_IE(ISDNQ931IE::CalledSubAddr,decodeCalledSubAddr) CASE_DECODE_IE(ISDNQ931IE::Restart,decodeRestart) CASE_DECODE_IE(ISDNQ931IE::Segmented,decodeSegmented) CASE_DECODE_IE(ISDNQ931IE::NetTransit,decodeNetTransit) CASE_DECODE_IE(ISDNQ931IE::LoLayerCompat,decodeLoLayerCompat) CASE_DECODE_IE(ISDNQ931IE::HiLayerCompat,decodeHiLayerCompat) CASE_DECODE_IE(ISDNQ931IE::UserUser,decodeUserUser) #undef CASE_DECODE_IE default: ; } // Unknown or unhandled IE // Check bits 4-7: If 0: the value MUST be a known one (See Q.931, Table 4-3, Note 5) if ((data[0] >> 4) == 0) { Debug(m_settings->m_dbg,DebugMild, "Found unknown mandatory IE: %u [%p]",type,m_msg); m_msg->setUnknownMandatory(); } ISDNQ931IE* ie = new ISDNQ931IE(type); SignallingUtils::dumpData(0,*ie,"dumped-data",ieData,ieLen); return ie; } // Check Shift IE. Change current codeset void Q931Parser::shiftCodeset(const ISDNQ931IE* ie) { bool locking = ie->getBoolValue("lock",false); int value = ie->getIntValue("codeset",0); XDebug(m_settings->m_dbg,DebugAll, "Process %s shift with codeset %u [%p]", locking?"locking":"non locking",value,m_msg); // Values 1,2,3 are reserved if (value && value < 4) { Debug(m_settings->m_dbg,DebugNote, "Ignoring shift with reserved codeset [%p]",m_msg); return; } // Non locking shift if (!locking) { DDebug(m_settings->m_dbg,DebugNote, "Non locking shift. Set active codeset to %u [%p]", value,m_msg); m_activeCodeset = value; return; } // Locking shift. MUST not be lower then the current one if (value < m_codeset) { Debug(m_settings->m_dbg,DebugNote, "Ignoring locking shift with lower value %u then the current one %u [%p]", value,m_codeset,m_msg); return; } m_activeCodeset = m_codeset = value; DDebug(m_settings->m_dbg,DebugNote, "Locking shift. Codeset set to %u [%p]",m_codeset,m_msg); } // Parse a single fixed length IE ISDNQ931IE* Q931Parser::getFixedIE(u_int8_t data) { // Type1: bits 7-4 define the IE type. Bits 3-0 contain the value // Type2: bits 7-4 are 1010. The type is the whole byte u_int16_t type = data & 0xf0; if (type == 0xa0) type = data; type |= (u_int16_t)m_activeCodeset << 8; ISDNQ931IE* ie = new ISDNQ931IE(type); switch (type) { // Type 1 case ISDNQ931IE::Shift: s_ie_ieFixed[0].addBoolParam(ie,data,true); s_ie_ieFixed[1].addIntParam(ie,data); break; case ISDNQ931IE::Congestion: s_ie_ieFixed[2].addIntParam(ie,data); break; case ISDNQ931IE::Repeat: s_ie_ieFixed[3].addIntParam(ie,data); break; // Type 2 case ISDNQ931IE::MoreData: case ISDNQ931IE::SendComplete: break; default: SignallingUtils::dumpData(0,*ie,"Unknown fixed IE",&data,1); } return ie; } // Q.931 4.5.5 ISDNQ931IE* Q931Parser::decodeBearerCaps(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { #define CHECK_INDEX(idx) {if (idx >= len) return errorParseIE(ie,len ? s_errorWrongData : s_errorNoData,0,0);} CHECK_INDEX(0) // Byte 0: Coding standard (bit 5,6), Information transfer capability (bit 0-4) // Translate transfer cap 0x08 to 0x10 if (!checkCoding(data[0],0,ie)) // Check coding standard (CCITT: 0) return errorParseIE(ie,s_errorUnsuppCoding,data,len); s_ie_ieBearerCaps[0].addIntParam(ie,data[0]); if (m_settings->flag(ISDNQ931::Translate31kAudio)) { NamedString* ns = ie->getParam(s_ie_ieBearerCaps[0].name); if (ns && *ns == lookup(0x08,s_ie_ieBearerCaps[0].values)) *ns = lookup(0x10,s_ie_ieBearerCaps[0].values); } // End of data ? CHECK_INDEX(1) // Byte 1: Transfer mode (bit 5,6), Transfer rate (bit 0-4) s_ie_ieBearerCaps[1].addIntParam(ie,data[1]); // Transfer mode s_ie_ieBearerCaps[2].addIntParam(ie,data[1]); // Transfer rate u_int8_t crt = 2; // Figure 4.11 Note 1: Next byte is the rate multiplier if the transfer rate is 'multirate' (0x18) if ((data[1] & 0x1f) == 0x18) { CHECK_INDEX(2) s_ie_ieBearerCaps[3].addIntParam(ie,data[2]); crt = 3; } // Get user information layer data u_int8_t crtLayer = 0; while (true) { // End of data ? if (crt >= len) return ie; // Get and check layer (must be greater than the current one) u_int8_t layer = (data[crt] & 0x60) >> 5; if (layer <= crtLayer || layer > 3) return errorParseIE(ie,s_errorWrongData,data + crt,len - crt); crtLayer = layer; // Process layer information switch (crtLayer) { case 1: decodeLayer1(ie,data,len,crt,s_ie_ieBearerCaps,4); continue; case 2: decodeLayer2(ie,data,len,crt,s_ie_ieBearerCaps,6); continue; case 3: decodeLayer3(ie,data,len,crt,s_ie_ieBearerCaps,7); } break; } // Dump any remaining data if (crt < len) SignallingUtils::dumpData(0,*ie,"garbage",data+crt,len-crt); return ie; #undef CHECK_INDEX } // Q.850. Section 2 ISDNQ931IE* Q931Parser::decodeCause(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // Byte 0: Coding standard (bit 5,6), location (bit 0-3) if (!checkCoding(data[0],0,ie)) // Check coding standard (CCITT: 0) return errorParseIE(ie,s_errorUnsuppCoding,data,len); s_ie_ieCause[0].addParam(ie,data[0]); // Location // End of data ? if (len == 1) return errorParseIE(ie,s_errorNoCause,0,0); // Optional byte 1: Recommendation: If exists, must be 0: Q.931 u_int32_t crt = Q931_EXT_FINAL(data[0]) ? 1 : 2; if (crt == 2 && 0 != (data[1] & 0x7f)) return errorParseIE(ie,s_errorUnsuppRecommen,data + 1,len - 1); // End of data ? if (crt >= len) return errorParseIE(ie,s_errorNoCause,0,0); // byte: Cause value s_ie_ieCause[1].addParam(ie,data[crt]); // Rest of data: diagnostic crt++; if (crt < len) s_ie_ieCause[2].dumpDataBit7(ie,data + crt,len - crt,true); return ie; } // Q.931 4.5.6 ISDNQ931IE* Q931Parser::decodeCallIdentity(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); s_ie_ieCallIdentity[0].dumpData(ie,data,len); return ie; } // Q.931 4.5.7 ISDNQ931IE* Q931Parser::decodeCallState(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); if (!checkCoding(data[0],0,ie)) // Check coding standard (CCITT: 0) return errorParseIE(ie,s_errorUnsuppCoding,data,len); s_ie_ieCallState[0].addIntParam(ie,data[0]); if (len > 1) SignallingUtils::dumpData(0,*ie,"garbage",data + 1,len - 1); return ie; } // Q.931 4.5.13 ISDNQ931IE* Q931Parser::decodeChannelID(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { #define DUMP_DATA_AND_EXIT {if (crt < len) SignallingUtils::dumpData(0,*ie,"garbage",data+crt,len-crt); return ie;} if (!len) return errorParseIE(ie,s_errorNoData,0,0); // Byte 0 // Bit 6 - Interface identifier 0: implicit 1: identified by the next byte(s) // Bit 5 - Interface type 0: basic 1: other (e.g. primary rate) // Bit 3 - Preferred/exclusive channel 0: indicated channel is preferred 1: only indicated channel is acceptable // Bit 2 - Identified channel is a D-channel or not // Bit 0,1 - Channel selection bool briInterface = s_ie_ieChannelID[0].addBoolParam(ie,data[0],true); // Interface type s_ie_ieChannelID[1].addBoolParam(ie,data[0],false); // Preferred/Exclusive B channel s_ie_ieChannelID[2].addBoolParam(ie,data[0],false); // D-channel flag // Channel selection if (briInterface) s_ie_ieChannelID[3].addParam(ie,data[0]); // Channel select for BRI interface else s_ie_ieChannelID[4].addParam(ie,data[0]); // Channel select for PRI interface // Optional Byte 1: Interface identifier if present u_int8_t crt = 1; bool interfaceIDExplicit = (data[0] & 0x40) != 0; if (interfaceIDExplicit) { if (len == 1) return errorParseIE(ie,s_errorWrongData,0,0); // Calculate length of the interface ID for (; crt < len && !Q931_EXT_FINAL(data[crt]); crt++); s_ie_ieChannelID[5].dumpData(ie,data + 1,crt - 1); crt++; } // See Q.931 Figure 4.18, Note 2 and 5. Terminate if it's a BRI interface or the interface is explicitely given // If not a BRI interface or the interface is not explicit: // check channel selection. If 1: the channel is indicated by the following bytes = continue if (briInterface || interfaceIDExplicit || 1 != (data[0] & 0x03)) DUMP_DATA_AND_EXIT // Optional Byte: Coding standard (bit 5,6), Channel indication (bit 4), Channel type (bit 0-3) // Check coding standard (CCITT: 0) if (crt >= len) return ie; if (!checkCoding(data[crt],0,ie)) return errorParseIE(ie,s_errorUnsuppCoding,data + crt,len - crt); bool byNumber = s_ie_ieChannelID[6].addBoolParam(ie,data[crt],true); // Channel is indicated by number/slot-map s_ie_ieChannelID[7].addIntParam(ie,data[crt]); // Channel type crt++; // Optional Byte: Channel number or slot map // The rest of the data is a list of channels or the slot map if (crt >= len) return ie; u_int8_t idx = byNumber ? 8 : 9; String param; for (; crt < len; crt++) { String tmp = (unsigned int)(data[crt] & s_ie_ieChannelID[idx].mask); param.append(tmp,","); // Bit 7 is used to end channel numbers. See Q.931 Figure 4.18 Note 3 if (byNumber && Q931_EXT_FINAL(data[crt])) { crt++; break; } } ie->addParam(s_ie_ieChannelID[idx].name,param); DUMP_DATA_AND_EXIT #undef DUMP_DATA_AND_EXIT } // Q.931 4.5.23 ISDNQ931IE* Q931Parser::decodeProgress(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // data[0]: Bits 5,6: coding standard // Bits 0-3: Location if (!checkCoding(data[0],0,ie)) // Check coding standard (CCITT: 0) return errorParseIE(ie,s_errorUnsuppCoding,data,len); s_ie_ieProgress[0].addIntParam(ie,data[0]); // data[1]: Progress indication if (len == 1) return errorParseIE(ie,s_errorWrongData,0,0); s_ie_ieProgress[1].addIntParam(ie,data[1]); // Dump any remaining data if (len > 2) SignallingUtils::dumpData(0,*ie,"garbage",data + 2,len - 2); return ie; } // Q.931 4.5.21 ISDNQ931IE* Q931Parser::decodeNetFacility(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // data[0]: Length of network identification (0 bytes or at least 2 bytes) // If non 0: data[1]: Bits 4-6: Type of network identification. Bits 0-3: Network identification plan // data[2] --> data[crt-1]: Network identification // data[crt]: Network specific facilities // Start of 'Network specific facilities' u_int8_t crt = data[0] + 1; // Check if the indicated length is correct if (crt >= len) return errorParseIE(ie,s_errorWrongData,data,len); // Network identification exists if (crt > 1) { // Mandatory: data[1], data[2] if (crt < 3) return errorParseIE(ie,s_errorWrongData,data + 1,1); s_ie_ieNetFacility[0].addIntParam(ie,data[1]); s_ie_ieNetFacility[1].addIntParam(ie,data[1]); s_ie_ieNetFacility[2].dumpDataBit7(ie,data + 2,crt - 2,true); } // Network specific facilities s_ie_ieNetFacility[3].addIntParam(ie,data[crt]); // Dump any remaining data crt++; if (crt < len) SignallingUtils::dumpData(0,*ie,"garbage",data + crt,len - crt); return ie; } // Q.931 4.5.22 ISDNQ931IE* Q931Parser::decodeNotification(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); s_ie_ieNotification[0].addIntParam(ie,data[0]); // Dump any remaining data if (len > 1) SignallingUtils::dumpData(0,*ie,"garbage",data + 1,len - 1); return ie; } // Q.931 4.5.15 ISDNQ931IE* Q931Parser::decodeDateTime(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { #define DATETIME_SET(crt) \ if (crt >= len) return errorParseIE(ie,s_errorWrongData,0,0);\ s_ie_ieDateTime[crt].addIntParam(ie,data[crt]); #define DATETIME_SET_OPT(crt) \ if (crt >= len) return ie;\ s_ie_ieDateTime[crt].addIntParam(ie,data[crt]); \ crt++; if (!len) return errorParseIE(ie,s_errorNoData,0,0); DATETIME_SET(0) DATETIME_SET(1) DATETIME_SET(2) u_int8_t crt = 3; DATETIME_SET_OPT(crt) DATETIME_SET_OPT(crt) DATETIME_SET_OPT(crt) // Dump any remaining data if (crt < len) SignallingUtils::dumpData(0,*ie,"garbage",data + crt,len - crt); return ie; #undef DATETIME_SET #undef DATETIME_SET_OPT } // Q.931 4.5.16 ISDNQ931IE* Q931Parser::decodeDisplay(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // Check charset if (0 != (data[0] & 0x80)) { s_ie_ieDisplay[0].addIntParam(ie,data[0]); data++; len--; } s_ie_ieDisplay[1].dumpDataBit7(ie,data,len,false); return ie; } // Q.931 4.5.18 ISDNQ931IE* Q931Parser::decodeKeypad(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); s_ie_ieKeypad[0].dumpDataBit7(ie,data,len,false); return ie; } // Q.931 4.5.28 ISDNQ931IE* Q931Parser::decodeSignal(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); s_ie_ieSignal[0].addIntParam(ie,data[0]); // Dump any remaining data if (len > 1) SignallingUtils::dumpData(0,*ie,"garbage",data + 1,len - 1); return ie; } // Q.931 4.5.10 ISDNQ931IE* Q931Parser::decodeCallingNo(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // Byte 0: Type of number (bit 4-6), Numbering plan (bit 0-3) // Number type s_ie_ieNumber[0].addParam(ie,data[0]); // Type of number switch (data[0] & 0x70) { // Numbering plan (applicable only if type is 0,1,2,4) case 0x00: case 0x10: case 0x20: case 0x40: s_ie_ieNumber[1].addParam(ie,data[0]); } // End of data ? if (len == 1) return ie; // Optional Byte 1: Presentation indicator (bit 5,6), Screening (bit 0,1) // Presentation exists ? u_int8_t crt = Q931_EXT_FINAL(data[0]) ? 1 : 2; if (crt == 2) { s_ie_ieNumber[2].addParam(ie,data[1]); s_ie_ieNumber[3].addParam(ie,data[1]); } // Rest of data: The number if (crt < len) s_ie_ieNumber[4].dumpDataBit7(ie,data + crt,len - crt,false); return ie; } // Q.931 4.5.11 ISDNQ931IE* Q931Parser::decodeCallingSubAddr(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); s_ie_ieSubAddress[0].addIntParam(ie,data[0]); s_ie_ieSubAddress[1].addBoolParam(ie,data[0],false); if (len == 1) return errorParseIE(ie,s_errorNoData,0,0); s_ie_ieSubAddress[2].dumpData(ie,data + 1,len - 1); return ie; } // Q.931 4.5.8 ISDNQ931IE* Q931Parser::decodeCalledNo(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // Byte 0: Type of number (bit 4-6), Numbering plan (bit 0-3) s_ie_ieNumber[0].addParam(ie,data[0]); // Type of number switch (data[0] & 0x70) { // Numbering plan (applicable only if type is 0,1,2,4) case 0x00: case 0x10: case 0x20: case 0x40: s_ie_ieNumber[1].addParam(ie,data[0]); } // Rest of data: The number if (len > 1) s_ie_ieNumber[4].dumpDataBit7(ie,data + 1,len - 1,false); return ie; } // Q.931 4.5.9 ISDNQ931IE* Q931Parser::decodeCalledSubAddr(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); s_ie_ieSubAddress[0].addIntParam(ie,data[0]); s_ie_ieSubAddress[1].addBoolParam(ie,data[0],false); if (len == 1) return errorParseIE(ie,s_errorNoData,0,0); s_ie_ieSubAddress[2].dumpData(ie,data + 1,len - 1); return ie; } // Q.931 4.5.25 ISDNQ931IE* Q931Parser::decodeRestart(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // data[0]: Bits 0-2: Restart class s_ie_ieRestart[0].addIntParam(ie,data[0]); // Dump any remaining data if (len > 1) SignallingUtils::dumpData(0,*ie,"garbage",data + 1,len - 1); return ie; } // Q.931 4.5.26 ISDNQ931IE* Q931Parser::decodeSegmented(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // data[0]: bit 7: First/subsequent segment. bits 0-6: number of segments remaining s_ie_ieSegmented[0].addBoolParam(ie,data[0],false); s_ie_ieSegmented[1].addIntParam(ie,data[0]); // Segmented message type if (len == 1) return errorParseIE(ie,s_errorWrongData,0,0); s_ie_ieSegmented[2].addIntParam(ie,data[1]); // Dump any remaining data if (len > 2) SignallingUtils::dumpData(0,*ie,"garbage",data + 2,len - 2); return ie; } // Q.931 4.5.29 ISDNQ931IE* Q931Parser::decodeNetTransit(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // data[0]: Bits 4-6: Type of network identification. Bits 0-3: Network identification plan s_ie_ieNetTransit[0].addIntParam(ie,data[0]); s_ie_ieNetTransit[1].addIntParam(ie,data[0]); // Network identification if (len == 1) return errorParseIE(ie,s_errorNoData,0,0); s_ie_ieNetTransit[2].dumpDataBit7(ie,data + 1,len - 1,false); return ie; } // Q.931 4.5.19 ISDNQ931IE* Q931Parser::decodeLoLayerCompat(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { #define CHECK_INDEX(idx) {if (idx >= len) return errorParseIE(ie,len ? s_errorWrongData : s_errorNoData,0,0);} CHECK_INDEX(0) // data[0]: Bits 5,6: coding standard. Bits 0-4: transfer capability if (!checkCoding(data[0],0,ie)) // Check coding standard (CCITT: 0) return errorParseIE(ie,s_errorUnsuppCoding,data,len); s_ie_ieLoLayerCompat[0].addIntParam(ie,data[0]); u_int8_t crt = 1; // Out-band negotiation is present only if data[0] has bit 7 not set if (!Q931_EXT_FINAL(data[0])) { CHECK_INDEX(1) s_ie_ieLoLayerCompat[1].addBoolParam(ie,data[1],false); crt = 2; } CHECK_INDEX(crt) // Transfer mode and transfer rate s_ie_ieLoLayerCompat[2].addIntParam(ie,data[1]); s_ie_ieLoLayerCompat[3].addIntParam(ie,data[1]); crt++; // Rate multiplier. Only if transfer rate is 'multirate' if ((data[crt-1] & 0x1f) == 0x18) { CHECK_INDEX(crt) s_ie_ieLoLayerCompat[4].addIntParam(ie,data[1]); crt++; } // Get user information layer data u_int8_t crtLayer = 0; while (true) { // End of data ? if (crt >= len) return ie; // Get and check layer (must be greater than the current one) u_int8_t layer = (data[crt] & 0x60) >> 5; if (layer <= crtLayer || layer > 3) return errorParseIE(ie,s_errorWrongData,data + crt,len - crt); crtLayer = layer; // Process layer information switch (crtLayer) { case 1: decodeLayer1(ie,data,len,crt,s_ie_ieLoLayerCompat,5); continue; case 2: decodeLayer2(ie,data,len,crt,s_ie_ieLoLayerCompat,7); continue; case 3: decodeLayer3(ie,data,len,crt,s_ie_ieLoLayerCompat,10); } break; } // Dump any remaining data if (crt < len) SignallingUtils::dumpData(0,*ie,"garbage",data + crt,len - crt); return ie; #undef CHECK_INDEX } // Q.931 4.5.17 ISDNQ931IE* Q931Parser::decodeHiLayerCompat(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // data[0]: Bits 5,6: coding standard // Bits 2-4: interpretation // Bits 0,1: presenttion if (!checkCoding(data[0],0,ie)) // Check coding standard (CCITT: 0) return errorParseIE(ie,s_errorUnsuppCoding,data,len); s_ie_ieHiLayerCompat[0].addIntParam(ie,data[0]); // interpretation s_ie_ieHiLayerCompat[1].addIntParam(ie,data[0]); // presentation if (len == 1) return errorParseIE(ie,s_errorWrongData,0,0); u_int8_t crt = 2; u_int8_t presIndex = ((data[0] & 0x03) == 0x01) ? 2 : 4; // High layer characteristics identification s_ie_ieHiLayerCompat[presIndex].addIntParam(ie,data[1]); // Extended high layer characteristics identification if (!Q931_EXT_FINAL(data[1])) { if (len == 2) return errorParseIE(ie,s_errorWrongData,0,0); s_ie_ieHiLayerCompat[presIndex+1].addIntParam(ie,data[2]); crt = 3; } // Dump any remaining data if (crt < len) SignallingUtils::dumpData(0,*ie,"garbage",data + crt,len - crt); return ie; } // Q.931 4.5.30 ISDNQ931IE* Q931Parser::decodeUserUser(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len) { if (!len) return errorParseIE(ie,s_errorNoData,0,0); // data[0]: Protocol discriminator s_ie_ieUserUser[0].addIntParam(ie,data[0]); if (len == 1) return errorParseIE(ie,s_errorWrongData,0,0); // Remaining data: user information s_ie_ieUserUser[1].dumpData(ie,data + 1,len - 1); return ie; } void Q931Parser::decodeLayer1(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len, u_int8_t& crt, const IEParam* ieParam, u_int8_t ieParamIdx) { ieParam[ieParamIdx].addIntParam(ie,data[crt]); crt++; // Done with layer 1 data ? if (Q931_EXT_FINAL(data[crt-1])) return; // Skip data up to (and including) the first byte with bit 7 set u_int8_t skip = skipExt(data,len,crt); if (skip) ieParam[ieParamIdx+1].dumpData(ie,data + crt - skip,skip); } void Q931Parser::decodeLayer2(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len, u_int8_t& crt, const IEParam* ieParam, u_int8_t ieParamIdx) { #define CHECK_INDEX \ if (Q931_EXT_FINAL(data[crt-1])) \ return; \ if (crt >= len) { \ errorParseIE(ie,s_errorWrongData,0,0); \ return; \ } ieParam[ieParamIdx].addIntParam(ie,data[crt]); crt++; // This is all for bearer capabilities if (ie->type() == ISDNQ931IE::BearerCaps) return; // IE is 'Low layer compatibility' // Skip data: see Q.931 Table 4-16 description for octet 6a CHECK_INDEX ieParam[ieParamIdx+1].addIntParam(ie,data[crt]); crt++; // This byte should be the window size CHECK_INDEX ieParam[ieParamIdx+2].addIntParam(ie,data[crt]); crt++; #undef CHECK_INDEX } void Q931Parser::decodeLayer3(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t len, u_int8_t& crt, const IEParam* ieParam, u_int8_t ieParamIdx) { #define CHECK_INDEX \ if (Q931_EXT_FINAL(data[crt-1])) \ return; \ if (crt >= len) { \ errorParseIE(ie,s_errorWrongData,0,0); \ return; \ } ieParam[ieParamIdx].addIntParam(ie,data[crt]); crt++; // This is all for bearer capabilities if (ie->type() == ISDNQ931IE::BearerCaps) return; // IE is 'Low layer compatibility' CHECK_INDEX // See Q.931 Figure 4-25 Notes 7,8 bool advance = false; switch (data[crt-1] & 0x1f) { // x25, iso8208, x223 case 0x06: case 0x07: case 0x08: ieParam[ieParamIdx+1].addIntParam(ie,data[crt]); advance = true; break; // User specified case 0x10: ieParam[ieParamIdx+2].addIntParam(ie,data[crt]); break; default: ieParam[ieParamIdx+3].addIntParam(ie,data[crt]); } crt++; if (!advance) return; // Default packet size CHECK_INDEX ieParam[ieParamIdx+4].addIntParam(ie,data[crt]); crt++; // Packet window size CHECK_INDEX ieParam[ieParamIdx+5].addIntParam(ie,data[crt]); crt++; #undef CHECK_INDEX } #define CHECK_IE_LENGTH(len,maxlen) \ if (len > maxlen) { \ Debug(m_settings->m_dbg,DebugNote, \ "Can't encode '%s' IE. Length %lu exceeds maximum allowed %u [%p]", \ ie->c_str(),(long unsigned int)len,maxlen,m_msg); \ return false; \ } bool Q931Parser::encodeBearerCaps(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[8] = {(u_int8_t)ie->type(),2,0x80,0x80}; // 2: Coding standard (bit 5,6) 0:CCITT, Transfer capability (bit 0-4) // Translate '3.1khz-audio' (0x10) to 0x08 data[2] |= s_ie_ieBearerCaps[0].getValue(ie); u_int8_t transCap = data[2] & 0x1f; if (m_settings->flag(ISDNQ931::Translate31kAudio) && (0x10 == transCap)) { transCap = 0x08; data[2] = (data[2] & 0xd0) | 0x08; } // 3: Transfer mode (bit 5,6), Transfer rate (bit 0-4) data[3] |= s_ie_ieBearerCaps[1].getValue(ie); // Figure 4.11 Note 1: Next byte is the rate multiplier if the transfer // rate is 'multirate' (0x18) u_int8_t transRate = s_ie_ieBearerCaps[2].getValue(ie); data[3] |= transRate; if (transRate == 0x18) { data[1] = 3; data[4] = 0x80 | s_ie_ieBearerCaps[3].getValue(ie); } // Check if this all data we'll send with Bearer Capability unsigned int layer = 1; if (m_settings->flag(ISDNQ931::NoLayer1Caps) || (m_settings->flag(ISDNQ931::URDITransferCapsOnly) && (transCap == 0x08 || transCap == 0x09))) layer = 4; // User information layer data // Bit 7 = 1, Bits 5,6 = layer, Bits 0-4: the value for (unsigned int idx = 0; layer < 4; layer++, idx++) { int tmp = s_ie_ieBearerCaps[idx].getValue(ie,false,-1); if (tmp == -1) { DDebug(m_settings->m_dbg,DebugAll, "Stop encoding '%s' IE. No user information layer %d protocol [%p]", ie->c_str(),layer,m_msg); break; } data[1]++; data[data[1] + 1] = 0x80 | ((u_int8_t)layer << 5) | ((u_int8_t)tmp & s_ie_ieBearerCaps[idx].mask); } CHECK_IE_LENGTH(data[1] + 2,Q931_MAX_BEARERCAPS_LEN) buffer.assign(data,data[1] + 2); return true; } bool Q931Parser::encodeCause(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[4] = {(u_int8_t)ie->type(),2,0x80,0x80}; // Coding standard (0: CCITT) + location. If no location, set it to 0x01: "LPN" data[2] |= (u_int8_t)s_ie_ieCause[0].getValue(ie,true,0x01); // Cause. If no cause, set to 0x1f: "Normal, unspecified" data[3] |= (u_int8_t)s_ie_ieCause[1].getValue(ie,true,0x1f); // Diagnostic DataBlock diagnostic; const char* tmp = ie->getValue(s_ie_ieCause[2].name); if (tmp) { if (!diagnostic.unHexify(tmp,strlen(tmp),' ')) Debug(m_settings->m_dbg,DebugMild, "Error encoding '%s' IE. Field %s=%s is incorrect [%p]", ie->c_str(),s_ie_ieCause[3].name,tmp,m_msg); data[1] += (u_int8_t)diagnostic.length(); } CHECK_IE_LENGTH(diagnostic.length() + sizeof(data),Q931_MAX_CAUSE_LEN) // Set data buffer.assign(data,sizeof(data)); buffer += diagnostic; return true; } bool Q931Parser::encodeCallState(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[3] = {(u_int8_t)ie->type(),1,0}; u_int8_t callstate = (u_int8_t)s_ie_ieCallState[0].getValue(ie,false,255); if (callstate == 255) { const char* name = s_ie_ieCallState[0].name; Debug(m_settings->m_dbg,DebugNote, "Can't encode '%s' IE with unknown or missing field %s=%s [%p]", ie->c_str(),name,ie->getValue(name),m_msg); return false; } data[2] |= callstate & s_ie_ieCallState[0].mask; buffer.assign(data,sizeof(data)); return true; } bool Q931Parser::encodeChannelID(ISDNQ931IE* ie, DataBlock& buffer) { DataBlock dataBuffer; u_int8_t tmp; // *** Byte 0 // Bit 6 - Interface identifier 0: implicit 1: identified by the next byte(s) // Bit 5 - Interface type 0: basic 1: other (e.g. primary rate) // Bit 3 - Preferred/exclusive channel 0: indicated channel is preferred 1: only indicated channel is acceptable // Bit 2 - Identified channel is a D-channel or not // Bit 0,1 - Channel selection tmp = 0x80; String interfaceID = ie->getValue(s_ie_ieChannelID[5].name); if (!interfaceID.null()) { Debug(m_settings->m_dbg,DebugWarn, "Can't encode '%s' IE. Interface identifier encoding not implemeted [%p]", ie->c_str(),m_msg); return false; //TODO: tmp |= 0x40; } // BRI flag is 0 is briInterface is true bool briInterface = ie->getBoolValue(s_ie_ieChannelID[0].name); if (!briInterface) tmp |= s_ie_ieChannelID[0].mask; if (ie->getBoolValue(s_ie_ieChannelID[1].name)) // Preferred/Exclusive B channel tmp |= s_ie_ieChannelID[1].mask; if (ie->getBoolValue(s_ie_ieChannelID[2].name)) // D-channel flag tmp |= s_ie_ieChannelID[2].mask; // Channel selection if (briInterface) tmp |= (u_int8_t)s_ie_ieChannelID[3].getValue(ie); else tmp |= (u_int8_t)s_ie_ieChannelID[4].getValue(ie); dataBuffer.assign(&tmp,1); // Optional Byte 1: Interface identifier if present if (!interfaceID.null()) { if (!interfaceID.length() || interfaceID.length() > 254) { Debug(m_settings->m_dbg,DebugNote, "Can't encode '%s' IE with incorrect interface identifier length %u [%p]", ie->c_str(),interfaceID.length(),m_msg); return false; } //TODO: Encode interface identifier. Add to dataBuffer } // See Q.931 Figure 4.18, Note 2 and 5. Terminate if it's a BRI interface or the interface is explicitely given // If not a BRI interface or the interface is not explicit: // check channel selection. If 1: the channel is indicated by the following bytes if (!(briInterface || !interfaceID.null() || 1 != (tmp & 0x03))) { tmp = 0x80; // Coding standard 0: CCITT // Channel is indicated by number/slot-map flag is 0 for number bool byNumber = ie->getBoolValue(s_ie_ieChannelID[6].name); if (!byNumber) tmp |= s_ie_ieChannelID[6].mask; tmp |= s_ie_ieChannelID[7].getValue(ie); // Channel type dataBuffer += DataBlock(&tmp,1); String s; if (byNumber) s = ie->getValue(s_ie_ieChannelID[8].name); else s = ie->getValue(s_ie_ieChannelID[9].name); ObjList* list = s.split(',',false); ObjList* obj = list->skipNull(); unsigned int count = list->count(); for (; obj; obj = obj->skipNext(), count--) { tmp = (static_cast(obj->get()))->toInteger(255); if (tmp == 255) continue; // Last octet must have bit 7 set to 1 if (count == 1) tmp |= 0x80; else tmp &= 0x7f; dataBuffer += DataBlock(&tmp,1); } delete list; } // Create buffer u_int8_t header[2] = {(u_int8_t)ie->type(),(u_int8_t)dataBuffer.length()}; CHECK_IE_LENGTH(dataBuffer.length() + sizeof(header),Q931_MAX_CHANNELID_LEN) buffer.assign(header,sizeof(header)); buffer += dataBuffer; return true; } bool Q931Parser::encodeDisplay(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t header[3] = {(u_int8_t)ie->type(),0,0x80}; u_int8_t headerLen = 2; // Check charset if (!m_settings->flag(ISDNQ931::NoDisplayCharset)) { headerLen++; header[1] = 1; header[2] |= 0x31; } // Process display String display = ie->getValue(s_ie_ieDisplay[1].name); // Check size (the charset will steel a char from display) unsigned int maxlen = m_settings->m_maxDisplay - headerLen; if (display.length() > maxlen) { Debug(m_settings->m_dbg,DebugMild, "Truncating '%s' IE. Size %u greater then %u [%p]", ie->c_str(),display.length(),maxlen,m_msg); display = display.substr(0,maxlen); } header[1] += display.length(); clearBit7(display.c_str(),display.length()); // Encode CHECK_IE_LENGTH(display.length() + headerLen,m_settings->m_maxDisplay) buffer.assign(header,headerLen); buffer.append(display); return true; } bool Q931Parser::encodeCallingNo(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[4] = {(u_int8_t)ie->type(),1,0x80,0x80}; // Byte 2: Type of number (bit 4-6), Numbering plan (bit 0-3) u_int8_t tmp = s_ie_ieNumber[0].getValue(ie); // Type of number data[2] |= tmp; switch (tmp) { // Numbering plan (applicable only if type is 0,1,2,4) case 0x00: case 0x10: case 0x20: case 0x40: data[2] |= s_ie_ieNumber[1].getValue(ie); } // Optional: Presentation indicator (bit 5,6), Screening (bit 0,1) String s = ie->getValue(s_ie_ieNumber[2].name); if (!s.null()) { data[1] = 2; // Set length data[2] &= 0x7f; // Clear bit 7 to signal the presence of the next octet data[3] |= s_ie_ieNumber[2].getValue(ie); data[3] |= s_ie_ieNumber[3].getValue(ie); } // Rest of data: The number String number = ie->getValue(s_ie_ieNumber[4].name); clearBit7(number.c_str(),number.length()); u_int8_t dataLen = data[1] + 2; CHECK_IE_LENGTH(number.length() + dataLen,Q931_MAX_CALLINGNO_LEN) data[1] += number.length(); buffer.assign(data,dataLen); buffer += number; return true; } bool Q931Parser::encodeCalledNo(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[3] = {(u_int8_t)ie->type(),1,0x80}; // Byte 2: Type of number (bit 4-6), Numbering plan (bit 0-3) u_int8_t tmp = s_ie_ieNumber[0].getValue(ie); // Type of number data[2] |= tmp; switch (tmp) { // Numbering plan (applicable only if type is 0,1,2,4) case 0x00: case 0x10: case 0x20: case 0x40: data[2] |= s_ie_ieNumber[1].getValue(ie); } // Rest of data: The number String number = ie->getValue(s_ie_ieNumber[4].name); clearBit7(number.c_str(),number.length()); CHECK_IE_LENGTH(number.length() + sizeof(data),Q931_MAX_CALLEDNO_LEN) data[1] += number.length(); buffer.assign(data,sizeof(data)); buffer += number; return true; } bool Q931Parser::encodeProgress(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[4] = {(u_int8_t)ie->type(),2,0x80,0x80}; // data[2]: Bits 5,6: coding standard // Bits 0-3: Location // Coding standard (0: CCITT). If no location, set it to 0x01: "LPN" data[2] |= s_ie_ieProgress[0].getValue(ie,true,0x01); // data[3]: Progress indicatior data[3] |= s_ie_ieProgress[1].getValue(ie); buffer.assign(data,sizeof(data)); return true; } bool Q931Parser::encodeNotification(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[3] = {(u_int8_t)ie->type(),1,0x80}; data[2] |= s_ie_ieNotification[0].getValue(ie,true,0xff); buffer.assign(data,sizeof(data)); return true; } bool Q931Parser::encodeKeypad(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[2] = {(u_int8_t)ie->type(),0}; // Process keypad String keypad = ie->getValue(s_ie_ieKeypad[0].name); CHECK_IE_LENGTH(keypad.length() + sizeof(data),Q931_MAX_KEYPAD_LEN) data[1] = keypad.length(); clearBit7(keypad.c_str(),keypad.length()); // Encode buffer.assign(data,sizeof(data)); buffer.append(keypad); return true; } bool Q931Parser::encodeSignal(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[3] = {(u_int8_t)ie->type(),1,0}; data[2] = s_ie_ieSignal[0].getValue(ie,true,0xff); buffer.assign(data,sizeof(data)); return true; } bool Q931Parser::encodeRestart(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[3] = {(u_int8_t)ie->type(),1,0x80}; data[2] = s_ie_ieRestart[0].getValue(ie,true,0xff); buffer.assign(data,sizeof(data)); return true; } bool Q931Parser::encodeSendComplete(ISDNQ931IE* ie, DataBlock& buffer) { u_int8_t data[1] = {(u_int8_t)ie->type()}; buffer.assign(data,sizeof(data)); return true; } /* vi: set ts=8 sw=4 sts=4 noet: */