/** * 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-2014 Null Team * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include "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 const TokenDict s_dict_congestion[]; static const TokenDict s_dict_bearerTransCap[]; static const TokenDict s_dict_bearerTransMode[]; static const TokenDict s_dict_bearerTransRate[]; static const TokenDict s_dict_bearerProto1[]; static const TokenDict s_dict_bearerProto2[]; static const TokenDict s_dict_bearerProto3[]; static const TokenDict s_dict_typeOfNumber[]; static const TokenDict s_dict_numPlan[]; static const TokenDict s_dict_presentation[]; static const TokenDict s_dict_screening[]; static const TokenDict s_dict_subaddrType[]; static const TokenDict s_dict_channelIDSelect_BRI[]; static const TokenDict s_dict_channelIDSelect_PRI[]; static const TokenDict s_dict_channelIDUnits[]; static const TokenDict s_dict_loLayerProto2[]; static const TokenDict s_dict_loLayerProto3[]; static const TokenDict s_dict_networkIdType[]; static const TokenDict s_dict_networkIdPlan[]; static const TokenDict s_dict_notification[]; static const TokenDict s_dict_progressDescr[]; static const TokenDict s_dict_restartClass[]; static const 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* 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 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); bool encodeHighLayerCap(ISDNQ931IE* ie, DataBlock& buffer); bool encodeUserUser(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(bool bri) : m_bri(bri), 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(YSTRING("transfer-cap")); m_transferMode = ie->getValue(YSTRING("transfer-mode")); m_transferRate = ie->getValue(YSTRING("transfer-rate")); m_format = ie->getValue(YSTRING("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_channelMandatory = m_channelByNumber = false; return false; } m_bri = ie->getBoolValue(YSTRING("interface-bri"),m_bri); m_channelMandatory = ie->getBoolValue(YSTRING("channel-exclusive")); m_channelByNumber = ie->getBoolValue(YSTRING("channel-by-number")); m_channelType = ie->getValue(YSTRING("type")); m_channelSelect = ie->getValue(YSTRING("channel-select")); if (m_bri && m_channelSelect) { m_channelByNumber = true; if (m_channelSelect == "b1") m_channels = "1"; else if (m_channelSelect == "b2") m_channels = "2"; else return false; } // ChannelID IE may repeat if channel is given by number if (m_channelByNumber) { unsigned int n = ie->length(); for (unsigned int i = 0; i < n; i++) { NamedString* ns = ie->getParam(i); if (ns && (ns->name() == YSTRING("channels"))) m_channels.append(*ns,","); } } else m_channels = ie->getValue(YSTRING("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(YSTRING("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(YSTRING("number")); m_calledType = ie->getValue(YSTRING("type")); m_calledPlan = ie->getValue(YSTRING("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(YSTRING("number")); m_callerType = ie->getValue(YSTRING("type")); m_callerPlan = ie->getValue(YSTRING("plan")); m_callerPres = ie->getValue(YSTRING("presentation")); m_callerScreening = ie->getValue(YSTRING("screening")); return true; } bool ISDNQ931IEData::processCause(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data) { if (!msg) return false; if (add) { msg->appendIEValue(ISDNQ931IE::Cause,0,m_reason?m_reason:"normal-clearing"); return true; } m_reason = msg->getIEValue(ISDNQ931IE::Cause,0); 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 */ const 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: case OverlapSend: 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: case OverlapSend: 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, u_int8_t tei) : SignallingCall(controller,outgoing), m_callRef(callRef), m_callRefLen(callRefLen), m_tei(tei), m_circuit(0), m_circuitChange(false), m_channelIDSent(false), m_rspBearerCaps(false), m_inbandAvailable(false), m_net(false), m_data(controller && !controller->primaryRate()), m_discTimer(0), m_relTimer(0), m_conTimer(0), m_overlapSendTimer(0), m_overlapRecvTimer(0), m_retransSetupTimer(0), m_terminate(false), m_destroy(false), m_destroyed(false) { Debug(q931(),DebugAll,"Call(%u,%u) direction=%s TEI=%u [%p]", Q931_CALL_ID,(outgoing ? "outgoing" : "incoming"),tei,this); for (u_int8_t i = 0; i < 127; i++) m_broadcast[i] = false; 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; } m_net = q931() && q931()->network(); // Init timers q931()->setInterval(m_discTimer,305); q931()->setInterval(m_relTimer,308); q931()->setInterval(m_conTimer,313); m_overlapSendTimer.interval(10000); m_overlapRecvTimer.interval(20000); m_retransSetupTimer.interval(1000); if (outgoing) reserveCircuit(); } 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 mylock(this); if (m_destroyed) return; 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 mylock(this); DDebug(q931(),DebugAll,"Call(%u,%u). sendEvent(%s) state=%s [%p]", Q931_CALL_ID,event->name(),stateName(state()),this); if (m_terminate || state() == CallAbort) { mylock.drop(); delete event; return false; } bool retVal = false; switch (event->type()) { case SignallingEvent::Progress: retVal = sendProgress(event->message()); break; case SignallingEvent::Ringing: retVal = sendAlerting(event->message()); break; case SignallingEvent::Accept: if (m_overlap) { sendSetupAck(); m_overlap = false; break; } changeState(CallPresent); retVal = sendCallProceeding(event->message()); break; case SignallingEvent::Answer: changeState(CallPresent); 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; mylock.drop(); delete event; return false; default: m_terminate = m_destroy = true; retVal = sendReleaseComplete(event->message() ? event->message()->params().getValue(YSTRING("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); } mylock.drop(); 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 mylock(this); // Check for last event or destroyed/aborting if (m_lastEvent || m_destroyed || state() == CallAbort) return 0; while (true) { // 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); break; } 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); TelEngine::destruct(msg); m_lastEvent = releaseComplete("missing-mandatory-ie"); break; } 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",callTei()); break; case ISDNQ931Message::SuspendAck: case ISDNQ931Message::SuspendRej: case ISDNQ931Message::ResumeAck: case ISDNQ931Message::ResumeRej: q931()->sendStatus(this,"wrong-state-message",callTei()); 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",callTei()); // Fall through to destruct the message and check timeouts } TelEngine::destruct(msg); if (!m_lastEvent) m_lastEvent = checkTimeout(when.msec()); if (!m_lastEvent) m_lastEvent = getCircuitEvent(when); break; } if (!m_lastEvent) return 0; XDebug(q931(),DebugInfo,"Call(%u,%u). Raising event '%s' state=%s [%p]", Q931_CALL_ID,m_lastEvent->name(),stateName(state()),this); return m_lastEvent; } // Get reserved circuit or this object void* ISDNQ931Call::getObject(const String& name) const { if (name == YSTRING("SignallingCircuit")) return m_circuit; if (name == YSTRING("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 mylock(this); // 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",callTei()); } // Process termination flags or requests (messages) SignallingEvent* ISDNQ931Call::processTerminate(ISDNQ931Message* msg) { XDebug(q931(),DebugAll,"Call(%u,%u). processTerminate(%s) state=%s [%p]", Q931_CALL_ID,msg?msg->name():"",stateName(state()),this); 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("normal-clearing"); return 0; } // Check message timeout for Connect, Disconnect, Release, Setup 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; case CallInitiated: if (!m_retransSetupTimer.timeout(time)) break; CALL_TIMEOUT_DEBUG("Setup") m_retransSetupTimer.stop(); m_data.m_reason = reason; return releaseComplete(reason); case OverlapSend: if (!m_overlapSendTimer.timeout(time)) { m_overlapSendTimer.stop(); m_overlapSendTimer.start(); } 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",callTei()); } 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); // Check if inband ringback is available if (m_data.processProgress(msg,false)) m_inbandAvailable = m_inbandAvailable || SignallingUtils::hasFlag(m_data.m_progress,"in-band-info"); msg->params().addParam("earlymedia",String::boolText(m_inbandAvailable)); 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) { m_retransSetupTimer.stop(); 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) { if (state() == DisconnectReq) { // Disconnect requested concurrently from both sides sendRelease(); return 0; } if (!checkMsgRecv(msg,false)) return 0; m_discTimer.stop(); 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) { m_lastEvent = checkTimeout(10000); // 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)) m_inbandAvailable = m_inbandAvailable || SignallingUtils::hasFlag(m_data.m_progress,"in-band-info"); msg->params().addParam("earlymedia",String::boolText(m_inbandAvailable)); 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) { if (!msg) return 0; 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 on PRI if (msg->getIE(ISDNQ931IE::ChannelID)) m_data.processChannelID(msg,false); else if (q931() && q931()->primaryRate()) return errorNoIE(msg,ISDNQ931IE::ChannelID,true); // Check if channel contains valid PRI/BRI flag if (q931() && (m_data.m_bri == q931()->primaryRate())) { Debug(q931(),DebugWarn, "Call(%u,%u). Invalid interface type. Releasing call [%p]", Q931_CALL_ID,this); return errorWrongIE(msg,ISDNQ931IE::ChannelID,true); } // Get a circuit from controller if (reserveCircuit()) m_circuit->updateFormat(m_data.m_format,0); else if (q931() && q931()->primaryRate()) return releaseComplete("congestion"); // *** CalledNo /CallingNo m_overlap = !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); msg->params().setParam("overlapped",String::boolText(m_overlap)); msg->params().setParam("transfer-cap",m_data.m_transferCapability); 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) 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; } else if (peerState == Active) { Debug(q931(),DebugNote,"Call(%u,%u). Recovering from STATUS, cause='%s' [%p]", Q931_CALL_ID,m_data.m_reason.c_str(),this); 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: ; } TelEngine::destruct(sigMsg); 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",callTei()); 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 = 0; if (sigMsg) { format = sigMsg->params().getValue(YSTRING("format")); m_inbandAvailable = m_inbandAvailable || sigMsg->params().getBoolValue(YSTRING("earlymedia"),false); if (m_inbandAvailable) SignallingUtils::appendFlag(m_data.m_progress,"in-band-info"); } 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) { if (!q931()->primaryRate()) { m_data.m_channelType = "B"; if (m_circuit) m_data.m_channelSelect = lookup(m_circuit->code(),Q931Parser::s_dict_channelIDSelect_BRI); if (!m_data.m_channelSelect) { TelEngine::destruct(msg); return sendReleaseComplete("congestion"); } } if (q931()->network()) m_data.m_channelMandatory = true; m_data.processChannelID(msg,true,&q931()->parserData()); m_channelIDSent = true; } m_data.processProgress(msg,true); return q931()->sendMessage(msg,callTei()); } // 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) { if (q931()->network()) m_data.m_channelMandatory = true; m_data.processChannelID(msg,true); m_channelIDSent = true; } return q931()->sendMessage(msg,callTei()); } // 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) { if (!q931()->primaryRate()) { m_data.m_channelType = "B"; m_data.m_channelByNumber = true; m_data.m_channelSelect = lookup(m_circuit->code(),Q931Parser::s_dict_channelIDSelect_BRI); } if (q931()->network()) m_data.m_channelMandatory = true; m_data.processChannelID(msg,true,&q931()->parserData()); m_channelIDSent = true; } // Progress indicator if (sigMsg) { m_data.m_progress = sigMsg->params().getValue(YSTRING("call-progress")); m_data.processProgress(msg,true,&q931()->parserData()); } m_conTimer.start(); return q931()->sendMessage(msg,callTei()); } // 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(YSTRING("call-progress")); m_data.processProgress(msg,true,&q931()->parserData()); } else m_data.m_progress = ""; return q931()->sendMessage(msg,callTei()); } // 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(YSTRING("reason")); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Disconnect,this); m_data.processCause(msg,true); changeState(DisconnectReq); m_discTimer.start(); return q931()->sendMessage(msg,callTei()); } // 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(YSTRING("complete"))) msg->appendSafe(new ISDNQ931IE(ISDNQ931IE::SendComplete)); m_data.m_display = sigMsg->params().getValue(YSTRING("display")); m_data.processDisplay(msg,true,&q931()->parserData()); // Check tones or ringing const char* tone = sigMsg->params().getValue(YSTRING("tone")); if (tone) msg->appendIEValue(ISDNQ931IE::Keypad,"keypad",tone); return q931()->sendMessage(msg,callTei()); } // 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(YSTRING("progress")); m_inbandAvailable = m_inbandAvailable || sigMsg->params().getBoolValue(YSTRING("earlymedia"),false); if (m_inbandAvailable) SignallingUtils::appendFlag(m_data.m_progress,"in-band-info"); } ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Progress,this); m_data.processProgress(msg,true); return q931()->sendMessage(msg,callTei()); } // 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(YSTRING("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,callTei()); } // Send RELEASE COMPLETE. See Q.931 3.1.10 // IE: Cause, Display, Signal bool ISDNQ931Call::sendReleaseComplete(const char* reason, const char* diag, u_int8_t tei) { m_relTimer.stop(); if ((state() == Null) && (0 == tei)) return false; if (reason) m_data.m_reason = reason; m_terminate = m_destroy = true; changeState(Null); q931()->releaseCircuit(m_circuit); if (callTei() >= 127) { for (u_int8_t i = 0; i < 127; i++) if (m_broadcast[i]) return q931()->sendRelease(this,false,m_data.m_reason,i,diag); return true; } if (0 == tei) tei = callTei(); return q931()->sendRelease(this,false,m_data.m_reason,tei,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 = sigMsg->params().getValue(YSTRING("transfer-cap"), "speech"); m_data.m_transferMode = "circuit"; m_data.m_transferRate = "64kbit"; m_data.m_format = sigMsg->params().getValue(YSTRING("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 (!m_circuit) break; if (m_net || q931()->primaryRate()) { // Reserving a circuit attempted only on PRI or if we are NET if (!reserveCircuit()) { m_data.m_reason = "network-busy"; break; } m_circuit->updateFormat(m_data.m_format,0); m_data.m_channelMandatory = sigMsg->params().getBoolValue(YSTRING("channel-exclusive"), q931()->parserData().flag(ISDNQ931::ChannelExclusive)); m_data.m_channelByNumber = true; m_data.m_channelType = "B"; if (m_data.m_bri) { if (m_circuit->code() > 0 && m_circuit->code() < 3) m_data.m_channelSelect = lookup(m_circuit->code(),Q931Parser::s_dict_channelIDSelect_BRI); if (!m_data.m_channelSelect) { m_data.m_reason = "network-busy"; break; } } else { 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(YSTRING("call-progress")); m_data.processProgress(msg,true,&q931()->parserData()); // Display m_data.m_display = sigMsg->params().getValue(YSTRING("callername")); m_data.processDisplay(msg,true,&q931()->parserData()); // CallingNo m_data.m_callerType = sigMsg->params().getValue(YSTRING("callernumtype"),q931()->numType()); m_data.m_callerPlan = sigMsg->params().getValue(YSTRING("callernumplan"),q931()->numPlan()); m_data.m_callerPres = sigMsg->params().getValue(YSTRING("callerpres"),q931()->numPresentation()); m_data.m_callerScreening = sigMsg->params().getValue(YSTRING("callerscreening"),q931()->numScreening()); m_data.m_callerNo = sigMsg->params().getValue(YSTRING("caller")); m_data.processCallingNo(msg,true); // CalledNo m_data.m_calledType = sigMsg->params().getValue(YSTRING("callednumtype")); m_data.m_calledPlan = sigMsg->params().getValue(YSTRING("callednumplan")); m_data.m_calledNo = sigMsg->params().getValue(YSTRING("called")); m_data.processCalledNo(msg,true); // Send changeState(CallInitiated); if (m_net && !q931()->primaryRate()) { m_tei = 127; m_retransSetupTimer.start(); } if (q931()->sendMessage(msg,callTei(),&m_data.m_reason)) return true; msg = 0; break; } TelEngine::destruct(msg); 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(YSTRING("reason")); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::SuspendRej,this); msg->appendIEValue(ISDNQ931IE::Cause,0,reason); return q931()->sendMessage(msg,callTei()); } bool ISDNQ931Call::sendSetupAck() { MSG_CHECK_SEND(ISDNQ931Message::SetupAck) ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::SetupAck,this); if (!m_channelIDSent) { m_data.m_channelType = "B"; if (m_data.m_bri && m_circuit) m_data.m_channelSelect = lookup(m_circuit->code(),Q931Parser::s_dict_channelIDSelect_BRI); if (!m_data.m_channelSelect) { Debug(q931(),DebugNote,"Call(%u,%u). No voice channel available [%p]", Q931_CALL_ID,this); return sendReleaseComplete("congestion"); } if (q931()->network()) m_data.m_channelMandatory = true; m_data.processChannelID(msg,true,&q931()->parserData()); m_channelIDSent = true; } return q931()->sendMessage(msg,callTei()); } SignallingEvent* ISDNQ931Call::releaseComplete(const char* reason, const char* diag) { Lock mylock(this); if (m_destroyed) return 0; 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); TelEngine::destruct(msg); deref(); m_destroyed = m_terminate = m_destroy = true; 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(YSTRING("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); TelEngine::destruct(msg); } 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 BRI calls we reserve the circuit only one time (at SETUP) if (!(outgoing() || q931()->primaryRate())) { // Check if we are a BRI NET and we should assign any channel int briChan = lookup(m_data.m_channelSelect,Q931Parser::s_dict_channelIDSelect_BRI,3); if (m_net && (briChan == 3)) anyCircuit = true; else m_data.m_channels = briChan; break; } // Outgoing calls if (!m_data.m_channelByNumber) { m_data.m_reason = "service-not-implemented"; return false; } int reqCircuit = m_data.m_channels.toInteger(-1); // Check if we don't have a circuit reserved if (!m_circuit) { anyCircuit = (outgoing() || (reqCircuit < 0 && !m_data.m_channelMandatory)) && (m_net || q931()->primaryRate()); break; } // Check the received circuit if any if ((int)m_circuit->code() == reqCircuit) 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,0,-1,&m_data.m_channels,m_data.m_channelMandatory,true); if (m_circuit) { m_data.m_channels = m_circuit->code(); u_int64_t t = Time::msecNow(); if (!m_circuit->connect(m_data.m_format) && !m_net && (state() != ISDNQ931State::CallPresent)) { Debug(q931(),DebugNote, "Call(%u,%u). Failed to connect circuit [%p]",Q931_CALL_ID,this); return false; } t = Time::msecNow() - t; if (t > 100) { int level = DebugInfo; if (t > 300) level = DebugMild; else if (t > 200) level = DebugNote; Debug(q931(),level,"Call(%u,%u). Connected to circuit %u in %u ms [%p]", Q931_CALL_ID,m_circuit->code(),(unsigned int)t,this); } #ifdef DEBUG else Debug(q931(),DebugAll,"Call(%u,%u). Connected to circuit %u in %u ms [%p]", Q931_CALL_ID,m_circuit->code(),(unsigned int)t,this); #endif return true; } DDebug(q931(),DebugNote, "Call(%u,%u). Can't reserve%s circuit [%p]", Q931_CALL_ID,(anyCircuit ? " any" : ""),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; Debug(q931(),DebugAll,"Call(%u,%u). State '%s' --> '%s' [%p]", Q931_CALL_ID,stateName(state()),stateName(newState),this); 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 mylock(this); // 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 destruct the message and check timeouts } TelEngine::destruct(msg); if (!m_lastEvent) m_lastEvent = getCircuitEvent(when); return m_lastEvent; } // Set termination flag void ISDNQ931CallMonitor::setTerminate(const char* reason) { Lock mylock(this); 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 == YSTRING("SignallingCircuitCaller")) return m_callerCircuit; if (name == YSTRING("SignallingCircuitCalled")) return m_calledCircuit; if (name == YSTRING("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 mylock(this); if (state() == Null) return 0; 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); TelEngine::destruct(msg); 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(YSTRING("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); TelEngine::destruct(msg); } 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); TelEngine::destruct(m_callerCircuit); } if (m_calledCircuit) { q931()->releaseCircuit(m_calledCircuit); TelEngine::destruct(m_calledCircuit); } } // 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(YSTRING("allowsegmentation"),false); m_maxSegments = params.getIntValue(YSTRING("maxsegments"),8); m_maxDisplay = params.getIntValue(YSTRING("max-display"),34); if (m_maxDisplay != 34 && m_maxDisplay != 82) m_maxDisplay = 34; m_extendedDebug = params.getBoolValue(YSTRING("extended-debug"),false); // Set flags String flags = params.getValue(YSTRING("switchtype")); SignallingUtils::encodeFlags(0,m_flagsOrig,flags,ISDNQ931::s_swType); SignallingUtils::encodeFlags(0,m_flagsOrig,flags,ISDNQ931::s_flags); m_flags = m_flagsOrig; } /** * ISDNQ931 */ const 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}, {"channelexclusive", ChannelExclusive}, {0,0}, }; const 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) : SignallingComponent(name,¶ms,"isdn-q931"), SignallingCallControl(params,"isdn."), SignallingDumpable(SignallingDumper::Q931), ISDNLayer3(name), m_q921(0), m_q921Up(false), m_networkHint(true), 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) { #ifdef DEBUG if (debugAt(DebugAll)) { String tmp; params.dump(tmp,"\r\n ",'\'',true); Debug(this,DebugAll,"ISDNQ931::ISDNQ931(%p,'%s') [%p]%s", ¶ms,name,this,tmp.c_str()); } #endif m_parserData.m_dbg = this; m_networkHint = params.getBoolValue(YSTRING("network"),m_networkHint); m_data.m_bri = !(m_primaryRate = params.getBoolValue(YSTRING("primary"),m_primaryRate)); m_callRefLen = params.getIntValue(YSTRING("callreflen"),m_primaryRate ? 2 : 1); 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 >> (8 * (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",60,300,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_cpeNumber = params.getValue(YSTRING("number")); m_numPlan = params.getValue(YSTRING("numplan")); if (0xffff == lookup(m_numPlan,Q931Parser::s_dict_numPlan,0xffff)) m_numPlan = "unknown"; m_numType = params.getValue(YSTRING("numtype")); if (0xffff == lookup(m_numType,Q931Parser::s_dict_typeOfNumber,0xffff)) m_numType = "unknown"; m_numPresentation = params.getValue(YSTRING("presentation")); if (0xffff == lookup(m_numPresentation,Q931Parser::s_dict_presentation,0xffff)) m_numPresentation = "allowed"; m_numScreening = params.getValue(YSTRING("screening")); if (0xffff == lookup(m_numScreening,Q931Parser::s_dict_screening,0xffff)) m_numScreening = "user-provided"; m_format = params.getValue(YSTRING("format")); if (0xffff == lookup(m_format,Q931Parser::s_dict_bearerProto1,0xffff)) m_format = "alaw"; // Debug setDebug(params.getBoolValue(YSTRING("print-messages"),false), params.getBoolValue(YSTRING("extended-debug"),false)); if (debugAt(DebugInfo)) { String s(network() ? "NET" : "CPE"); #ifdef DEBUG s << " type=" << lookup(m_parserData.m_flags,s_swType,"Custom"); String t; for (const 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(YSTRING("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); } setDumper(params.getValue(YSTRING("layer3dump"))); m_syncGroupTimer.start(); } ISDNQ931::~ISDNQ931() { if (m_calls.count()) { cleanup(); m_calls.clear(); } TelEngine::destruct(attach((ISDNLayer2*)0)); TelEngine::destruct(SignallingCallControl::attach(0)); DDebug(this,DebugAll,"ISDN Call Controller destroyed [%p]",this); } // Initialize Q.931 and attach a layer 2 bool ISDNQ931::initialize(const NamedList* config) { #ifdef DEBUG String tmp; if (config && debugAt(DebugAll)) config->dump(tmp,"\r\n ",'\'',true); Debug(this,DebugInfo,"ISDNQ931::initialize(%p) [%p]%s",config,this,tmp.c_str()); #endif if (config) { debugLevel(config->getIntValue(YSTRING("debuglevel_q931"), config->getIntValue(YSTRING("debuglevel"),-1))); setDebug(config->getBoolValue(YSTRING("print-messages"),false), config->getBoolValue(YSTRING("extended-debug"),false)); } if (config && !layer2()) { const String* name = config->getParam(YSTRING("sig")); if (!name) name = config; if (!TelEngine::null(name)) { NamedPointer* ptr = YOBJECT(NamedPointer,name); NamedList* linkConfig = ptr ? YOBJECT(NamedList,ptr->userData()) : 0; NamedList params(*name + "/Q921"); params.addParam("basename",*name); params.addParam("primary",String::boolText(primaryRate())); params.addParam("network",String::boolText(network())); if (linkConfig) params.copyParams(*linkConfig); else { if (config->hasSubParams(*name + ".")) params.copySubParams(*config,*name + "."); else { params.addParam("local-config","true"); params.copyParams(*config); } linkConfig = ¶ms; } params.clearParam(YSTRING("debugname")); ISDNLayer2* l2 = YSIGCREATE(ISDNLayer2,¶ms); if (!l2) { Debug(this,DebugWarn,"Could not create ISDN Layer 2 '%s' [%p]",name->c_str(),this); return false; } attach(l2); if (!l2->initialize(linkConfig)) TelEngine::destruct(attach((ISDNLayer2*)0)); } } return 0 != layer2(); } const char* ISDNQ931::statusName() const { if (exiting()) return "Exiting"; if (!m_q921) return "Layer 2 missing"; if (!m_q921Up) return "Layer 2 down"; return "Operational"; } // Check if layer 2 may be up bool ISDNQ931::q921Up() const { if (!m_q921) return false; if (m_q921Up) return true; // Assume BRI NET is always up return !primaryRate() && network(); } // Send a message to layer 2 bool ISDNQ931::sendMessage(ISDNQ931Message* msg, u_int8_t tei, String* reason) { if (!msg) { if (reason) *reason = "wrong-message"; return false; } Lock lock(l3Mutex()); if (!q921Up()) { if (!m_flagQ921Invalid) Debug(this,DebugNote, "Refusing to send message. Layer 2 is missing or down"); m_flagQ921Invalid = true; TelEngine::destruct(msg); 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()); } TelEngine::destruct(msg); 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,tei,true)) { if (reason) *reason = "net-out-of-order"; return false; } } return true; } // Data link up notification from layer 2 // Notify calls void ISDNQ931::multipleFrameEstablished(u_int8_t tei, bool confirmation, bool timeout, ISDNLayer2* layer2) { l3Mutex().lock(); bool q921Tmp = m_q921Up; m_q921Up = true; if (m_q921Up != q921Tmp) { NamedList p(""); p.addParam("type","isdn-q921"); p.addParam("operational",String::boolText(m_q921Up)); p.addParam("from",m_q921->toString()); engine()->notify(this,p); } DDebug(this,DebugNote,"'Established' %s TEI %u", confirmation ? "confirmation" :"indication",tei); endReceiveSegment("Data link is up"); m_l2DownTimer.stop(); m_flagQ921Down = false; l3Mutex().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 notification from layer 2 // Notify calls void ISDNQ931::multipleFrameReleased(u_int8_t tei, bool confirmation, bool timeout, ISDNLayer2* layer2) { Lock lockLayer(l3Mutex()); bool q921Tmp = m_q921Up; m_q921Up = false; if (m_q921Up != q921Tmp) { NamedList p(""); p.addParam("type","isdn-q921"); p.addParam("operational",String::boolText(m_q921Up)); p.addParam("from",m_q921->toString()); engine()->notify(this,p); } DDebug(this,DebugNote,"'Released' %s TEI %u. Timeout: %s", confirmation ? "confirmation" :"indication",tei,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(tei,true,false); } if (confirmation) return; if (primaryRate() && !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, u_int8_t tei, ISDNLayer2* layer2) { XDebug(this,DebugAll,"Received data. Length: %u, TEI: %u",data.length(),tei); Lock lock(l3Mutex()); ISDNQ931Message* msg = getMsg(data); if (!msg) return; // Dummy call reference if (msg->dummyCallRef()) { sendStatus("service-not-implemented",0,tei); TelEngine::destruct(msg); 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,tei); TelEngine::destruct(msg); return; } bool doMore = true; // This is an incoming message: // if initiator is true, the message is for an incoming call ISDNQ931Call* call = findCall(msg->callRef(),!msg->initiator(),tei); if (call && (call->callTei() == 127) && (call->callRef() == msg->callRef())) { // Call was or still is Point-to-Multipoint int i; switch (msg->type()) { case ISDNQ931Message::Disconnect: case ISDNQ931Message::ReleaseComplete: if ((tei < 127) && call->m_broadcast[tei]) call->m_broadcast[tei] = false; else doMore = false; if (call->m_retransSetupTimer.timeout()) { call->m_retransSetupTimer.stop(); for (i = 0; i < 127; i++) { if (call->m_broadcast[i]) { doMore = false; break; } } } if ((msg->type() != ISDNQ931Message::ReleaseComplete) && !doMore) sendRelease(false,msg->callRefLen(),msg->callRef(), tei,!msg->initiator()); break; case ISDNQ931Message::Connect: if (tei >= 127) break; call->m_tei = tei; call->m_broadcast[tei] = false; // All other pending calls are to be aborted for (i = 0; i < 127; i++) { if (call->m_broadcast[i]) { sendRelease(true,msg->callRefLen(),msg->callRef(), i,!msg->initiator(),"answered"); call->m_broadcast[i] = false; break; } } break; default: if (tei < 127) call->m_broadcast[tei] = true; } } while (doMore) { if (call) { if (msg->type() != ISDNQ931Message::Setup && (call->callTei() == 127 || call->callTei() == tei)) { call->enqueue(msg); msg = 0; } else if (msg->type() != ISDNQ931Message::ReleaseComplete) { sendRelease((msg->type() != ISDNQ931Message::Release), msg->callRefLen(),msg->callRef(),tei, !msg->initiator(),"invalid-callref"); } break; } // Check if it is a new incoming call if (msg->initiator() && msg->type() == ISDNQ931Message::Setup) { if (!primaryRate() && m_cpeNumber && !network()) { // We are a BRI CPE with a number - check the called party field ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::CalledNo); if (ie) { const String* number = ie->getParam(YSTRING("number")); if (number && !number->startsWith(m_cpeNumber)) { DDebug(this,DebugInfo,"Setup was for '%s', not us.",number->c_str()); break; } } } // 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(),tei); m_calls.append(call); call->enqueue(msg); msg = 0; call = 0; } else sendRelease(false,msg->callRefLen(),msg->callRef(),tei, !msg->initiator(),reason); break; } processInvalidMsg(msg,tei); break; } TelEngine::destruct(call); TelEngine::destruct(msg); } // Attach layer 2 // Update some data from the attached object ISDNLayer2* ISDNQ931::attach(ISDNLayer2* q921) { Lock lock(l3Mutex()); if (m_q921 == q921) return 0; cleanup(q921 ? "layer 2 attach" : "layer 2 detach"); ISDNLayer2* tmp = m_q921; m_q921 = q921; if (m_q921) { ISDNQ921* q = YOBJECT(ISDNQ921,m_q921); // 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) { m_primaryRate = true; m_data.m_bri = false; 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 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 if (YOBJECT(ISDNQ921Management,m_q921)) { m_primaryRate = false; m_data.m_bri = true; m_callRefLen = 1; m_callRefMask = 0x7f; m_callRef &= m_callRefMask; } // Adjust parser data message length limit m_parserData.m_maxMsgLen = m_q921->maxUserData(); } 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) { if (tmp->layer3() == this) { Debug(this,DebugAll,"Detaching L2 (%p,'%s') [%p]", tmp,tmp->toString().safe(),this); tmp->attach(0); } else { Debug(this,DebugNote,"Layer 2 (%p,'%s') was not attached to us [%p]", tmp,tmp->toString().safe(),this); tmp = 0; } } if (!q921) return tmp; Debug(this,DebugAll,"Attached L2 '%s' (%p,'%s') [%p]", (q921->network() ? "NET" : "CPE"), q921,q921->toString().safe(),this); insert(q921); q921->attach(this); return tmp; } // Make an outgoing call from a given message SignallingCall* ISDNQ931::call(SignallingMessage* msg, String& reason) { if (!msg) { reason = "invalid-parameter"; return 0; } Lock lock(l3Mutex()); if (!acceptNewCall(true,reason)) { TelEngine::destruct(msg); return 0; } ISDNQ931Call* call = new ISDNQ931Call(this,true,m_callRef,m_callRefLen); if (!call->circuit()) { reason = "congestion"; TelEngine::destruct(call); return 0; } 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); TelEngine::destruct(msg); 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; default: Debug(this,DebugWarn,"Unknown interval %d",id); } } // Check timeouts for segmented messages, layer 2 down state, restart circuits void ISDNQ931::timerTick(const Time& when) { Lock mylock(l3Mutex(),SignallingEngine::maxLockWait()); if (!mylock.locked()) return; // 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, u_int8_t tei) { 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()) { if (!primaryRate() && (call->callTei() != tei) && (call->callTei() != 127)) return 0; 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); TelEngine::destruct(call); 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() || !q921Up()) { Debug(this,DebugInfo,"Denying %s call request, reason: %s.", outgoing ? "outgoing" : "incoming", exiting() ? "exiting" : "link down"); reason = "net-out-of-order"; return false; } return true; } // 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); TelEngine::destruct(msg); return 0; } // Parse received data // Create a message from it. Validate it. Process segmented messages ISDNQ931Message* ISDNQ931::getMsg(const DataBlock& data) { Lock lock(l3Mutex()); 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(YSTRING("first")); if (!ns) break; first = ns->toBoolean(); remaining = (u_int8_t)ie->getIntValue(YSTRING("remaining"),0xff); type = (u_int8_t)ie->getIntValue(YSTRING("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()); TelEngine::destruct(msg); // 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"); } TelEngine::destruct(msg); // 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(l3Mutex()); m_recvSgmTimer.stop(); if (!m_segmented) return 0; // Clear some data TelEngine::destruct(m_segmented); 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, u_int8_t tei) { 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,tei); return; #else DDebug(this,DebugNote,"(%p): '%s' without global call reference", msg,msg->name()); #endif } if (msg->type() == ISDNQ931Message::Restart) { processMsgRestart(msg,tei); 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,tei); return; case ISDNQ931Message::Status: break; default: Debug(this,DebugNote,"Dropping (%p): '%s' with global call reference", msg,msg->name()); sendStatus("invalid-callref",m_callRefLen,tei); 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,0,"Unknown/missing")); } // Process restart requests // See Q.931 5.5 void ISDNQ931::processMsgRestart(ISDNQ931Message* msg, u_int8_t tei) { m_data.processRestart(msg,false); m_data.processChannelID(msg,false); m_data.m_reason = ""; ObjList* list = m_data.m_channels.split(',',false); unsigned char buf = 0; DDebug(this,DebugInfo,"Received '%s' class=%s circuits=%s", msg->name(),m_data.m_restart.c_str(),m_data.m_channels.c_str()); while (true) { if (m_data.m_restart == YSTRING("channels")) { if (list->count() > 0) terminateCalls(list,"resource-unavailable"); else { m_data.m_reason = "invalid-ie"; buf = ISDNQ931IE::ChannelID; } break; } bool single = (m_data.m_restart == YSTRING("interface")); bool all = !single && (m_data.m_restart == YSTRING("all-interfaces")); // If all interfaces is specified, ChannelID must not be present // If ChannelID is present and allowed, it must contain a single channel code if (!(single || all) || (all && list->count() > 0) || (single && list->count() > 1)) { m_data.m_reason = "invalid-ie"; buf = ISDNQ931IE::Restart; break; } // Terminate all calls if class is 'all-interfaces' if (all) { terminateCalls(0,"resource-unavailable"); break; } // Done if no circuits if (!circuits()) break; // Identify the span containing the D-channel SignallingCircuitSpan* span = 0; if (list->count()) { unsigned int code = static_cast(list->get())->toInteger(0); SignallingCircuit* cic = circuits()->find(code); if (cic) span = cic->span(); } else { // FIXME: Make a proper implementation: identify the span containing the active D-channel // Use the first span ObjList* o = circuits()->m_spans.skipNull(); if (o) span = static_cast(o->get()); } if (span) { // Fill a list with all circuit codes used to reset and terminate calls ObjList m_terminate; for (ObjList* o = circuits()->circuits().skipNull(); o; o = o->skipNext()) { SignallingCircuit* cic = static_cast(o->get()); if (span == cic->span()) m_terminate.append(new String(cic->code())); } terminateCalls(&m_terminate,"resource-unavailable"); } else Debug(this,DebugNote, "Unable to identify span containing D-channel for '%s' request class=%s circuit=%s", msg->name(),m_data.m_restart.c_str(),m_data.m_channels.c_str()); break; } TelEngine::destruct(list); // ACK if no error if (m_data.m_reason.null()) { ISDNQ931Message* m = new ISDNQ931Message(ISDNQ931Message::RestartAck, false,0,m_callRefLen); m->append(msg->removeIE(ISDNQ931IE::ChannelID)); m->append(msg->removeIE(ISDNQ931IE::Restart)); sendMessage(m,tei); return; } String diagnostic; if (buf) diagnostic.hexify(&buf,1); Debug(this,DebugNote, "Invalid '%s' request class=%s circuits=%s reason='%s' diagnostic=%s", msg->name(),m_data.m_restart.c_str(),m_data.m_channels.c_str(), m_data.m_reason.c_str(),diagnostic.c_str()); sendStatus(m_data.m_reason,m_callRefLen,tei,0,false,ISDNQ931Call::Null,0,diagnostic); } // Process messages with invalid call reference. See Q.931 5.8 void ISDNQ931::processInvalidMsg(ISDNQ931Message* msg, u_int8_t tei) { if (!msg) return; DDebug(this,DebugNote,"Received (%p): '%s' with invalid call reference %u [%p]", msg,msg->name(),msg->callRef(),this); switch (msg->type()) { case ISDNQ931Message::Resume: case ISDNQ931Message::Setup: case ISDNQ931Message::ReleaseComplete: break; case ISDNQ931Message::Release: sendRelease(false,msg->callRefLen(),msg->callRef(), tei,!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(), tei,!msg->initiator(),"wrong-state-message"); } break; case ISDNQ931Message::StatusEnquiry: sendStatus("status-enquiry-rsp",msg->callRefLen(),msg->callRef(), tei,!msg->initiator(),ISDNQ931Call::Null); break; default: sendRelease(true,msg->callRefLen(),msg->callRef(), tei,!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(l3Mutex()); m_syncCicTimer.stop(); if (!primaryRate()) return; if (m_restartCic) { if (!retrans) return; } else { unsigned int count = circuits() ? circuits()->count() : 0; for (m_lastRestart++; m_lastRestart <= count; m_lastRestart++) { String tmp(m_lastRestart); if (reserveCircuit(m_restartCic,0,-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,DebugNote,"%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,0); } // 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(l3Mutex()); 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, u_int8_t tei, bool initiator, ISDNQ931Call::State state, const char* display, const char* diagnostic) { if (!primaryRate()) return false; // Create message ISDNQ931Message* msg = 0; 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 ISDNQ931IE* ie = msg->appendIEValue(ISDNQ931IE::Cause,0,cause); // We always send status about the local network ie->addParamPrefix("location","LN"); if (diagnostic && ie) ie->addParamPrefix("diagnostic",diagnostic); msg->appendIEValue(ISDNQ931IE::CallState,"state",ISDNQ931Call::stateName(state)); if (display) msg->appendIEValue(ISDNQ931IE::Display,"display",display); return sendMessage(msg,tei); } // 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, u_int8_t tei, 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 if (cause) { ISDNQ931IE* ie = msg->appendIEValue(ISDNQ931IE::Cause,0,cause); if (diag) ie->addParamPrefix("diagnostic",diag); } if (display) msg->appendIEValue(ISDNQ931IE::Display,"display",display); if (signal) msg->appendIEValue(ISDNQ931IE::Signal,"signal",signal); return sendMessage(msg,tei); } /** * ISDNQ931Monitor */ ISDNQ931Monitor::ISDNQ931Monitor(const NamedList& params, const char* name) : SignallingComponent(name,¶ms,"isdn-q931-mon"), SignallingCallControl(params,"isdn."), ISDNLayer3(name), m_q921Net(0), m_q921Cpe(0), m_cicNet(0), m_cicCpe(0), m_parserData(params), m_printMsg(true), m_extendedDebug(false) { #ifdef DEBUG if (debugAt(DebugAll)) { String tmp; params.dump(tmp,"\r\n ",'\'',true); Debug(this,DebugAll,"ISDNQ931Monitor::ISDNQ931Monitor(%p,'%s') [%p]%s", ¶ms,name,this,tmp.c_str()); } #endif // Set parser data. Accept maximum data length m_parserData.m_maxMsgLen = 0xffffffff; m_parserData.m_dbg = this; // Debug setDebug(params.getBoolValue(YSTRING("print-messages"),true), params.getBoolValue(YSTRING("extended-debug"),false)); } ISDNQ931Monitor::~ISDNQ931Monitor() { terminateMonitor(0,0); TelEngine::destruct(attach((ISDNQ921Passive*)0,true)); TelEngine::destruct(attach((ISDNQ921Passive*)0,false)); TelEngine::destruct(attach((SignallingCircuitGroup*)0,true)); TelEngine::destruct(attach((SignallingCircuitGroup*)0,false)); m_calls.clear(); DDebug(this,DebugAll,"ISDN Monitor destroyed [%p]",this); } // Initialize the monitor and attach both passive layer 2 bool ISDNQ931Monitor::initialize(const NamedList* config) { #ifdef DEBUG String tmp; if (config && debugAt(DebugAll)) config->dump(tmp,"\r\n ",'\'',true); Debug(this,DebugInfo,"ISDNQ931Monitor::initialize(%p) [%p]%s",config,this,tmp.c_str()); #endif if (config) { debugLevel(config->getIntValue(YSTRING("debuglevel_q931"), config->getIntValue(YSTRING("debuglevel"),-1))); setDebug(config->getBoolValue(YSTRING("print-messages"),false), config->getBoolValue(YSTRING("extended-debug"),false)); for (int i = 0; i <= 1; i++) { bool net = (0 == i); if (net && m_q921Net) continue; if (!net && m_q921Cpe) continue; NamedString* name = config->getParam(net ? "sig-net" : "sig-cpe"); if (name) { NamedPointer* ptr = YOBJECT(NamedPointer,name); NamedList* linkConfig = ptr ? YOBJECT(NamedList,ptr->userData()) : 0; NamedList params(name->c_str()); params.addParam("basename",*name); if (linkConfig) params.copyParams(*linkConfig); else { params.copySubParams(*config,*name + "."); linkConfig = ¶ms; } ISDNQ921Passive* l2 = YSIGCREATE(ISDNQ921Passive,¶ms); if (!l2) return false; attach(l2,net); if (!l2->initialize(linkConfig)) TelEngine::destruct(attach((ISDNQ921Passive*)0,net)); } } } return m_q921Net && m_q921Cpe; } const char* ISDNQ931Monitor::statusName() const { if (exiting()) return "Exiting"; if (!(m_q921Net && m_q921Cpe)) return "Layer 2 missing"; return "Operational"; } // Notification from layer 2 of data link set/release command or response void ISDNQ931Monitor::dataLinkState(u_int8_t tei, bool cmd, bool value, ISDNLayer2* layer2) { #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) { 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, u_int8_t tei, ISDNLayer2* layer2) { XDebug(this,DebugAll,"Received data. Length: %u, TEI: %u",data.length(),tei); //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()); TelEngine::destruct(msg); 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) { lock(); ISDNQ931CallMonitor* newMon = new ISDNQ931CallMonitor(this,msg->callRef(),m_q921Net == layer2); m_calls.append(newMon); unlock(); newMon->enqueue(msg); 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; } TelEngine::destruct(mon); TelEngine::destruct(msg); } // Attach ISDN Q.921 pasive transport that monitors one side of the link ISDNQ921Passive* ISDNQ931Monitor::attach(ISDNQ921Passive* q921, bool net) { Lock lock(l3Mutex()); // Yes, this is a reference to a pointer ISDNQ921Passive*& which = net ? m_q921Net : m_q921Cpe; // Make no change if same transport if (which == q921) return 0; terminateMonitor(0,q921 ? "layer 2 attach" : "layer 2 detach"); ISDNQ921Passive* tmp = which; which = q921; lock.drop(); const char* type = net ? "NET" : "CPE"; if (tmp) { if (tmp->layer3() == this) { Debug(this,DebugAll,"Detaching L2 %s (%p,'%s') [%p]", type,tmp,tmp->toString().safe(),this); static_cast(tmp)->attach(0); } else { Debug(this,DebugNote,"Layer 2 %s (%p,'%s') was not attached to us [%p]", type,tmp,tmp->toString().safe(),this); tmp = 0; } } if (!q921) return tmp; Debug(this,DebugAll,"Attached L2 %s (%p,'%s') [%p]", type,q921,q921->toString().safe(),this); insert(q921); q921->ISDNLayer2::attach(this); return tmp; } // Attach a circuit group to this call controller SignallingCircuitGroup* ISDNQ931Monitor::attach(SignallingCircuitGroup* circuits, bool net) { Lock lock(l3Mutex()); // Yes, this is a reference to a pointer SignallingCircuitGroup*& which = net ? m_cicNet : m_cicCpe; SignallingCircuitGroup* tmp = which; // Don't attach if it's the same object if (tmp == circuits) return 0; 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()); } #ifdef DEBUG else if (circuits) Debug(this,DebugAll,"Circuit group (%p) '%s' attached",circuits,circuits->debugName()); else Debug(this,DebugAll,"Circuit group (%p) '%s' detached",tmp,tmp->debugName()); #endif which = circuits; return tmp; } // 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(l3Mutex()); 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(l3Mutex()); 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,"resource-unavailable"); TelEngine::destruct(mon); } } 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 */ const 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 */ const 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 == YSTRING("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_errorUnsuppCoding = "unsupported coding standard"; // // IE descriptions // // *** Fixed (1 byte length) IEs // 4.5.14 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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.13: Channel identification // Q.931 4.5.13. Channel id selection for BRI interface: Bits 0,1 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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 const 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(YSTRING("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: { DataBlock tmp; if (SignallingUtils::encodeCause( static_cast(m_settings->m_dbg), tmp,*ie,ISDNQ931IE::typeName(ie->type()),false)) { unsigned char id = ISDNQ931IE::Cause; buffer.assign(&id,1); buffer += tmp; return true; } return false; } 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); case ISDNQ931IE::HiLayerCompat: return encodeHighLayerCap(ie,buffer); case ISDNQ931IE::UserUser: return encodeUserUser(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); TelEngine::destruct(m_msg); return reset(); } u_int32_t consumed = 0; ISDNQ931IE* ie = getIE(data,len,consumed); if (!ie) { TelEngine::destruct(m_msg); 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; TelEngine::destruct(m_msg); 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]); } // Get type u_int16_t type = ((u_int16_t)m_activeCodeset << 8) | 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]); XDebug(m_settings->m_dbg,DebugAll,"Decoding IE %u=%s len=%u [%p]", type,ISDNQ931IE::typeName(type,"Unknown"),ieLen,m_msg); 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; // 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::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 case ISDNQ931IE::Cause: { ISDNQ931IE* ie = new ISDNQ931IE(type); if (SignallingUtils::decodeCause( static_cast(m_settings->m_dbg), *ie,ieData,ieLen,ie->c_str(),false)) return ie; TelEngine::destruct(ie); return 0; } 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(YSTRING("lock"),false); int value = ie->getIntValue(YSTRING("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.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 // Layer 1 data is at index 4 in s_ie_ieBearerCaps // Layer 2 data is at index 6 in s_ie_ieBearerCaps // Layer 3 data is at index 7 in s_ie_ieBearerCaps for (unsigned int idx = 4; layer < 4; 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); if (layer == 1) layer += 2; else layer++; } CHECK_IE_LENGTH(data[1] + 2,Q931_MAX_BEARERCAPS_LEN) buffer.assign(data,data[1] + 2); 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; } bool Q931Parser::encodeHighLayerCap(ISDNQ931IE* ie, DataBlock& buffer) { // **coding standard ** //octet 1:information element identifier 7d // 2:the length of contents // 3:bit -8 extension set to 1 // -7-6 coding standad // -5-4-3 interpretation // -2-1 presentation method of protocol profile // 4:bit -8 extension set to 0 // -7-1 high layer caracteristics identification // TODO: implement it! u_int8_t tmp[4]; tmp[0]=0x7d; tmp[1]=0x02; tmp[2]=0x91; tmp[3]=0x81; buffer.assign(tmp,sizeof(tmp)); return true; } bool Q931Parser::encodeUserUser(ISDNQ931IE* ie, DataBlock& buffer) { // TODO: implement it! u_int8_t tmp[10]; tmp[0]=0x7e;tmp[1]=0x08;tmp[2]=0x04;tmp[3]=0x30;tmp[4]=0x39; tmp[5]=0x32;tmp[6]=0x21;tmp[7]=0x30;tmp[8]=0x39;tmp[9]=0x32; buffer.assign(tmp,sizeof(tmp)); return true; } /* vi: set ts=8 sw=4 sts=4 noet: */