From 7247b7c8f049ac9bb49ec7ba0d8545da80b17708 Mon Sep 17 00:00:00 2001 From: marian Date: Wed, 30 Jan 2008 16:26:41 +0000 Subject: [PATCH] Added switch type and behaviour flags to ISDN call controller. git-svn-id: http://voip.null.ro/svn/yate@1704 acf43c95-373e-0410-b603-e72c3f656dc1 --- libs/ysig/engine.cpp | 17 +- libs/ysig/q931.cpp | 366 +++++++++++++++++++++++++++++-------------- libs/ysig/yatesig.h | 148 +++++++++++++---- 3 files changed, 385 insertions(+), 146 deletions(-) diff --git a/libs/ysig/engine.cpp b/libs/ysig/engine.cpp index 87bda6da..1c5e43e7 100644 --- a/libs/ysig/engine.cpp +++ b/libs/ysig/engine.cpp @@ -444,12 +444,27 @@ TokenDict* SignallingUtils::s_dictCCITT[5] = { bool SignallingUtils::hasFlag(const NamedList& list, const char* param, const char* flag) { String s = list.getValue(param); - ObjList* obj = list.split(',',false); + ObjList* obj = s.split(',',false); bool found = (obj->find(flag) != 0); TelEngine::destruct(obj); return found; } +// Remove a flag from a comma separated list of flags +bool SignallingUtils::removeFlag(String& flags, const char* flag) +{ + ObjList* obj = flags.split(',',false); + ObjList* found = obj->find(flag); + if (found) { + obj->remove(found,true); + flags = ""; + for (ObjList* o = obj->skipNull(); o; o = o->skipNext()) + flags.append(*static_cast(o->get()),","); + } + TelEngine::destruct(obj); + return (found != 0); +} + // Add string (keyword) if found or integer parameter to a named list void SignallingUtils::addKeyword(NamedList& list, const char* param, const TokenDict* tokens, unsigned int val) { diff --git a/libs/ysig/q931.cpp b/libs/ysig/q931.cpp index 077e80e2..b2a5ebc1 100644 --- a/libs/ysig/q931.cpp +++ b/libs/ysig/q931.cpp @@ -147,7 +147,7 @@ class Q931Parser { public: inline Q931Parser(ISDNQ931ParserData& data) - : m_settings(&data), m_msg(0), m_codeset(0), m_activeCodeset(0) + : m_settings(&data), m_msg(0), m_codeset(0), m_activeCodeset(0), m_skip(false) {} // Decode received data. @@ -281,7 +281,7 @@ private: 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 Connectet number has the same layout as the Calling number IE + // 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 @@ -303,6 +303,7 @@ private: 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 }; @@ -310,14 +311,14 @@ private: * ISDNQ931IEData */ ISDNQ931IEData::ISDNQ931IEData() - : m_charsetDisplay(255), - m_bri(false), + : m_bri(false), m_channelMandatory(true), m_channelByNumber(true) { } -bool ISDNQ931IEData::processBearerCaps(ISDNQ931Message* msg, bool add) +bool ISDNQ931IEData::processBearerCaps(ISDNQ931Message* msg, bool add, + ISDNQ931ParserData* data) { if (!msg) return false; @@ -349,7 +350,8 @@ bool ISDNQ931IEData::processBearerCaps(ISDNQ931Message* msg, bool add) return true; } -bool ISDNQ931IEData::processChannelID(ISDNQ931Message* msg, bool add) +bool ISDNQ931IEData::processChannelID(ISDNQ931Message* msg, bool add, + ISDNQ931ParserData* data) { if (!msg) return false; @@ -387,23 +389,33 @@ bool ISDNQ931IEData::processChannelID(ISDNQ931Message* msg, bool add) return true; } -bool ISDNQ931IEData::processProgress(ISDNQ931Message* msg, bool add) +bool ISDNQ931IEData::processProgress(ISDNQ931Message* msg, bool add, + ISDNQ931ParserData* data) { if (!msg) return false; if (add) { - // TODO: Add more IEs if repeated - msg->appendIEValue(ISDNQ931IE::Progress,"description",m_progress); - return true; + // Remove non-isdn-source/non-isdn-destination + if (data) { + if (!data->flag(ISDNQ931::SendNonIsdnSource)) + SignallingUtils::removeFlag(m_progress,"non-isdn-source"); + if (data->flag(ISDNQ931::IgnoreNonIsdnDest)) + SignallingUtils::removeFlag(m_progress,"non-isdn-destination"); + } + if (!m_progress.null()) + msg->appendIEValue(ISDNQ931IE::Progress,"description",m_progress); + } + else { + // Progress may repeat + ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::Progress); + for (; ie; ie = msg->getIE(ISDNQ931IE::Progress,ie)) + m_progress.append(ie->getValue("description"),","); } - // Progress may repeat - ISDNQ931IE* ie = msg->getIE(ISDNQ931IE::Progress); - for (; ie; ie = msg->getIE(ISDNQ931IE::Progress,ie)) - m_progress.append(ie->getValue("description"),","); return !m_progress.null(); } -bool ISDNQ931IEData::processRestart(ISDNQ931Message* msg, bool add) +bool ISDNQ931IEData::processRestart(ISDNQ931Message* msg, bool add, + ISDNQ931ParserData* data) { if (!msg) return false; @@ -415,7 +427,8 @@ bool ISDNQ931IEData::processRestart(ISDNQ931Message* msg, bool add) return !m_restart.null(); } -bool ISDNQ931IEData::processNotification(ISDNQ931Message* msg, bool add) +bool ISDNQ931IEData::processNotification(ISDNQ931Message* msg, bool add, + ISDNQ931ParserData* data) { if (!msg) return false; @@ -427,7 +440,8 @@ bool ISDNQ931IEData::processNotification(ISDNQ931Message* msg, bool add) return !m_notification.null(); } -bool ISDNQ931IEData::processCalledNo(ISDNQ931Message* msg, bool add) +bool ISDNQ931IEData::processCalledNo(ISDNQ931Message* msg, bool add, + ISDNQ931ParserData* data) { if (!msg) return false; @@ -452,7 +466,8 @@ bool ISDNQ931IEData::processCalledNo(ISDNQ931Message* msg, bool add) return true; } -bool ISDNQ931IEData::processCallingNo(ISDNQ931Message* msg, bool add) +bool ISDNQ931IEData::processCallingNo(ISDNQ931Message* msg, bool add, + ISDNQ931ParserData* data) { if (!msg) return false; @@ -481,7 +496,8 @@ bool ISDNQ931IEData::processCallingNo(ISDNQ931Message* msg, bool add) return true; } -bool ISDNQ931IEData::processCause(ISDNQ931Message* msg, bool add) +bool ISDNQ931IEData::processCause(ISDNQ931Message* msg, bool add, + ISDNQ931ParserData* data) { if (!msg) return false; @@ -493,23 +509,23 @@ bool ISDNQ931IEData::processCause(ISDNQ931Message* msg, bool add) return !m_reason.null(); } -bool ISDNQ931IEData::processDisplay(ISDNQ931Message* msg, bool add) +bool ISDNQ931IEData::processDisplay(ISDNQ931Message* msg, bool add, + ISDNQ931ParserData* data) { if (!msg) return false; if (add) { - if (m_display.null()) + if (m_display.null() || !data || data->flag(ISDNQ931::NoDisplayIE)) return false; - ISDNQ931IE* ie = msg->appendIEValue(ISDNQ931IE::Display,"display",m_display); - if (m_charsetDisplay <= 127) - ie->addParam("charset",String(m_charsetDisplay)); + 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) +bool ISDNQ931IEData::processKeypad(ISDNQ931Message* msg, bool add, + ISDNQ931ParserData* data) { if (!msg) return false; @@ -681,7 +697,7 @@ ISDNQ931Call::ISDNQ931Call(ISDNQ931* controller, bool outgoing, m_terminate(false), m_destroy(false) { - DDebug(q931(),DebugAll,"Call(%u,%u). Created %s [%p]", + Debug(q931(),DebugAll,"Call(%u,%u). direction=%s [%p]", Q931_CALL_ID,(outgoing ? "outgoing" : "incoming"),this); if (!controller) { Debug(DebugWarn,"ISDNQ931Call(%u,%u). No call controller. Terminate [%p]", @@ -690,7 +706,6 @@ ISDNQ931Call::ISDNQ931Call(ISDNQ931* controller, bool outgoing, m_data.m_reason = "temporary-failure"; return; } - m_data.m_charsetDisplay = q931()->parserData().m_charsetDisplay; // Init timers q931()->setInterval(m_discTimer,305); q931()->setInterval(m_relTimer,308); @@ -1050,6 +1065,10 @@ SignallingEvent* ISDNQ931Call::processMsgConnect(ISDNQ931Message* msg) 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); @@ -1217,7 +1236,7 @@ SignallingEvent* ISDNQ931Call::processMsgStatus(ISDNQ931Message* msg) if (!m_data.processCause(msg,false)) m_data.m_reason = "unknown"; DDebug(q931(),DebugInfo, - "Call(%u,%u). Received '%s'. Our state: '%s'. Peer state: '%s'. Cause: '%s' [%p]", + "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); @@ -1319,11 +1338,12 @@ SignallingEvent* ISDNQ931Call::processMsgStatusEnquiry(ISDNQ931Message* msg) // Check if the state allows to send a message #define MSG_CHECK_SEND(type) \ - if (!checkStateSend(type)) { \ + if (!(q931() && checkStateSend(type))) { \ DDebug(q931(),DebugNote, \ - "Call(%u,%u). Denying request to send '%s' in invalid state '%s'. [%p]", \ + "Call(%u,%u). Can't send msg='%s' in state='%s'. %s [%p]", \ Q931_CALL_ID,ISDNQ931Message::typeName(type), \ - stateName(state()),this); \ + stateName(state()),(q931()?"Invalid state":"No call controller"),\ + this); \ return false; \ } @@ -1369,21 +1389,28 @@ bool ISDNQ931Call::sendCallProceeding(SignallingMessage* sigMsg) } // Send CONNECT. See Q.931 3.1.3 -// IE: BearerCaps, ChannelID, Progress, Display, DateTime, Signal, LoLayerCompat, HiLayerCompat +// 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 - changeState(ConnectReq); + 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); + m_data.processBearerCaps(msg,true,&q931()->parserData()); m_rspBearerCaps = false; } if (!m_channelIDSent) { - m_data.processChannelID(msg,true); + m_data.processChannelID(msg,true,&q931()->parserData()); m_channelIDSent = true; } + // Progress indicator + m_data.m_progress = sigMsg->params().getValue("call-progress"); + m_data.processProgress(msg,true,&q931()->parserData()); m_conTimer.start(); return q931()->sendMessage(msg); } @@ -1396,6 +1423,13 @@ bool ISDNQ931Call::sendConnectAck(SignallingMessage* sigMsg) // Change state, send message changeState(Active); ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::ConnectAck,this); + // Progress indicator + if (sigMsg) { + m_data.m_progress = sigMsg->params().getValue("call-progress"); + m_data.processProgress(msg,true,&q931()->parserData()); + } + else + m_data.m_progress = ""; return q931()->sendMessage(msg); } @@ -1426,8 +1460,7 @@ bool ISDNQ931Call::sendInfo(SignallingMessage* sigMsg) if (sigMsg->params().getBoolValue("complete")) msg->appendSafe(new ISDNQ931IE(ISDNQ931IE::SendComplete)); m_data.m_display = sigMsg->params().getValue("display"); - if (!m_data.m_display.null()) - m_data.processDisplay(msg,true); + m_data.processDisplay(msg,true,&q931()->parserData()); // Check tones or ringing const char* tone = sigMsg->params().getValue("tone"); if (tone) @@ -1470,7 +1503,7 @@ bool ISDNQ931Call::sendRelease(const char* reason, SignallingMessage* sigMsg) // Send RELEASE COMPLETE. See Q.931 3.1.10 // IE: Cause, Display, Signal -bool ISDNQ931Call::sendReleaseComplete(const char* reason) +bool ISDNQ931Call::sendReleaseComplete(const char* reason, const char* diag) { m_relTimer.stop(); if (state() == Null) @@ -1480,7 +1513,7 @@ bool ISDNQ931Call::sendReleaseComplete(const char* reason) m_terminate = m_destroy = true; changeState(Null); q931()->releaseCircuit(m_circuit); - return q931()->sendRelease(this,false,m_data.m_reason); + return q931()->sendRelease(this,false,m_data.m_reason,diag); } // Send SETUP. See Q.931 3.1.14 @@ -1494,6 +1527,9 @@ bool ISDNQ931Call::sendSetup(SignallingMessage* sigMsg) MSG_CHECK_SEND(ISDNQ931Message::Setup) ISDNQ931Message* msg = new ISDNQ931Message(ISDNQ931Message::Setup,this); while (true) { + // TODO: fix it (don't send?) if overlapp dialing is used + if (q931()->parserData().flag(ISDNQ931::ForceSendComplete)) + msg->appendSafe(new ISDNQ931IE(ISDNQ931IE::SendComplete)); // BearerCaps m_data.m_transferCapability = "speech"; m_data.m_transferMode = "circuit"; @@ -1513,9 +1549,12 @@ bool ISDNQ931Call::sendSetup(SignallingMessage* sigMsg) m_data.m_channelSelect = "present"; m_data.m_channels = m_circuit->code(); m_data.processChannelID(msg,true); + // Progress indicator + m_data.m_progress = sigMsg->params().getValue("call-progress"); + m_data.processProgress(msg,true,&q931()->parserData()); // Display m_data.m_display = sigMsg->params().getValue("callername"); - m_data.processDisplay(msg,true); + m_data.processDisplay(msg,true,&q931()->parserData()); // CallingNo m_data.m_callerType = sigMsg->params().getValue("callernumtype",q931()->numType()); m_data.m_callerPlan = sigMsg->params().getValue("callernumplan",q931()->numPlan()); @@ -1552,7 +1591,7 @@ bool ISDNQ931Call::sendSuspendRej(const char* reason, SignallingMessage* sigMsg) return q931()->sendMessage(msg); } -SignallingEvent* ISDNQ931Call::releaseComplete(const char* reason) +SignallingEvent* ISDNQ931Call::releaseComplete(const char* reason, const char* diag) { Lock lock(m_callMutex); if (reason) @@ -1560,7 +1599,7 @@ SignallingEvent* ISDNQ931Call::releaseComplete(const char* 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); + sendReleaseComplete(reason,diag); // Cleanup q931()->releaseCircuit(m_circuit); changeState(Null); @@ -1653,8 +1692,12 @@ SignallingEvent* ISDNQ931Call::errorNoIE(ISDNQ931Message* msg, Debug(q931(),DebugNote, "Call(%u,%u). Received '%s' without mandatory IE '%s' [%p]", Q931_CALL_ID,msg->name(),ISDNQ931IE::typeName(type),this); - if (release) - return releaseComplete("missing-mandatory-ie"); + if (release) { + unsigned char c = (unsigned char)type; + String tmp; + tmp.hexify(&c,1); + return releaseComplete("missing-mandatory-ie",tmp); + } return 0; } @@ -1666,8 +1709,12 @@ SignallingEvent* ISDNQ931Call::errorWrongIE(ISDNQ931Message* msg, 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) - return releaseComplete("invalid-ie"); + if (release) { + unsigned char c = (unsigned char)type; + String tmp; + tmp.hexify(&c,1); + return releaseComplete("invalid-ie",tmp); + } return 0; } @@ -1676,9 +1723,13 @@ void ISDNQ931Call::changeState(State newState) { if (state() == newState) return; - DDebug(q931(),DebugInfo, - "Call(%u,%u). Changing state from '%s' to '%s' [%p]", +#ifdef DEBUG + Debug(q931(),DebugAll,"Call(%u,%u). Changing state '%s' --> '%s' [%p]", Q931_CALL_ID,stateName(state()),stateName(newState),this); +#else + Debug(q931(),DebugAll,"Call(%u,%u). Changing state %u --> %u [%p]", + Q931_CALL_ID,state(),newState,this); +#endif m_state = newState; } @@ -1705,7 +1756,8 @@ ISDNQ931CallMonitor::ISDNQ931CallMonitor(ISDNQ931Monitor* controller, u_int32_t m_terminate(false), m_terminator("engine") { - DDebug(q931(),DebugAll,"Monitor(%u). Created [%p]",m_callRef,this); + 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); @@ -1713,7 +1765,6 @@ ISDNQ931CallMonitor::ISDNQ931CallMonitor(ISDNQ931Monitor* controller, u_int32_t m_data.m_reason = "temporary-failure"; return; } - m_data.m_charsetDisplay = q931()->m_parserData.m_charsetDisplay; } ISDNQ931CallMonitor::~ISDNQ931CallMonitor() @@ -2062,22 +2113,62 @@ ISDNQ931Monitor* ISDNQ931CallMonitor::q931() /** * ISDNQ931ParserData */ -ISDNQ931ParserData::ISDNQ931ParserData(DebugEnabler* dbg, const NamedList& params) +ISDNQ931ParserData::ISDNQ931ParserData(const NamedList& params, DebugEnabler* dbg) : m_dbg(dbg), m_maxMsgLen(0) { m_allowSegment = params.getBoolValue("allowsegmentation",false); m_maxSegments = params.getIntValue("maxsegments",8); - m_charsetDisplay = params.getIntValue("display-charset",255); m_maxDisplay = params.getIntValue("max-display",34); if (m_maxDisplay != 34 && m_maxDisplay != 82) m_maxDisplay = 34; m_extendedDebug = params.getBoolValue("extended-debug",false); + // Set flags + String flags = params.getValue("switchtype"); + m_flagsOrig = lookup(flags,ISDNQ931::s_swType); + // Check if the parameter is a list of flags + if (!m_flagsOrig) { + ObjList* list = flags.split(',',false); + for (ObjList* o = list->skipNull(); o; o = o->skipNext()) { + String* s = static_cast(o->get()); + m_flagsOrig |= lookup(*s,ISDNQ931::s_flags); + } + TelEngine::destruct(list); + } + m_flags = m_flagsOrig; } /** * ISDNQ931 */ +TokenDict ISDNQ931::s_flags[] = { + {"sendnonisdnsource", SendNonIsdnSource}, + {"ignorenonisdndest", IgnoreNonIsdnDest}, + {"setpresnetprov", SetPresNetProv}, + {"translate31kaudio", Translate31kAudio}, + {"urditransfercapsonly", URDITransferCapsOnly}, + {"nolayer1caps", NoLayer1Caps}, + {"ignorenonlockedie", IgnoreNonLockedIE}, + {"nodisplay", NoDisplayIE}, + {"nodisplaycharset", NoDisplayCharset}, + {"forcesendcomplete", ForceSendComplete}, + {"checknotifyind", CheckNotifyInd}, + {"noactiveonconnect", NoActiveOnConnect}, + {0,0}, + }; + +TokenDict ISDNQ931::s_swType[] = { + {"euro-isdn-e1", EuroIsdnE1}, + {"euro-isdn-t1", EuroIsdnT1}, + {"national-isdn", NationalIsdn}, + {"dms100", Dms100}, + {"lucent5e", Lucent5e}, + {"att4ess", Att4ess}, + {"qsig", QSIG}, + {"unknown", Unknown}, + {0,0} + }; + ISDNQ931::ISDNQ931(const NamedList& params, const char* name) : SignallingCallControl(params,"isdn."), ISDNLayer3(name), @@ -2089,7 +2180,7 @@ ISDNQ931::ISDNQ931(const NamedList& params, const char* name) m_callRef(1), m_callRefLen(2), m_callRefMask(0), - m_parserData(this,params), + m_parserData(params), m_l2DownTimer(0), m_recvSgmTimer(0), m_syncCicTimer(0), @@ -2108,6 +2199,7 @@ ISDNQ931::ISDNQ931(const NamedList& params, const char* name) m_flagQ921Invalid(false) { setName(params.getValue("debugname",name)); + m_parserData.m_dbg = this; //m_primaryRate = params.getBoolValue("pri",true); m_callRefLen = params.getIntValue("callreflen",2); if (m_callRefLen < 1 || m_callRefLen > 4) @@ -2140,26 +2232,31 @@ ISDNQ931::ISDNQ931(const NamedList& params, const char* name) // Debug setDebug(params.getBoolValue("print-messages",false), params.getBoolValue("extended-debug",false)); +#ifdef DEBUG if (debugAt(DebugInfo)) { String s; s << "\r\nType: " << (m_primaryRate ? "pri" : "bri"); s << "\r\nCall reference length: " << (unsigned int)m_callRefLen; - s << "\r\nNumber plan/type/pres/screen: " << m_numPlan << "/" << m_numType << "/" - << m_numPresentation << "/" << m_numScreening; + s << "\r\nNumber plan/type/pres/screen: " << m_numPlan << "/" << + m_numType << "/" << m_numPresentation << "/" << m_numScreening; s << "\r\nFormat: " << m_format; - s << "\r\nDisplay charset: " << (unsigned int)m_parserData.m_charsetDisplay; s << "\r\nMax Display length: " << (unsigned int)m_parserData.m_maxDisplay; - s << "\r\nAllow segmentation: " << String::boolText(m_parserData.m_allowSegment); - s << "\r\nMax segments: " << (unsigned int)m_parserData.m_maxSegments; - s << "\r\nCIC allocation strategy: " << strategy(); - if (debugAt(DebugAll)) - s << "\r\nTimers: 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 << "\r\nAllocation strategy: " << + lookup(strategy(),SignallingCircuitGroup::s_strategy); + s << "\r\nTimers: channelsync/l2Down/recvSgm/syncCic = " << + (unsigned int)m_syncGroupTimer.interval() << "/" << + (unsigned int)m_l2DownTimer.interval() << "/" << + (unsigned int)m_recvSgmTimer.interval() << "/" << + (unsigned int)m_syncCicTimer.interval(); Debug(this,DebugInfo,"Initialized: [%p]%s",this,s.c_str()); + s << "\r\nAllow segmentation: " << + String::boolText(m_parserData.m_allowSegment); + s << "\r\nMax segments: " << (unsigned int)m_parserData.m_maxSegments; } - else - DDebug(this,DebugAll,"Initialized [%p]",this); +#else + Debug(this,DebugAll,"ISDN call controller type=%s format=%s [%p]", + (m_primaryRate?"pri":"bri"),m_format.c_str(),this); +#endif m_syncGroupTimer.start(); } @@ -2363,11 +2460,22 @@ void ISDNQ931::attach(ISDNLayer2* q921) if (m_syncCicTimer.interval() <= min) m_syncCicTimer.interval(min + 1000); } - // Adjust message length limit + // Adjust parser data message length limit m_parserData.m_maxMsgLen = m_q921->maxUserData(); + // Adjust some parser flags if the switch type is not a custom one + bool notCustom = (0 != lookup(m_parserData.m_flagsOrig,s_swType)); + if (notCustom) { + 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 + else { + // Reset parser data if no layer 2 m_parserData.m_maxMsgLen = 0; + m_parserData.m_flags = m_parserData.m_flagsOrig; + } lock.drop(); if (tmp) { const char* name = 0; @@ -2379,7 +2487,8 @@ void ISDNQ931::attach(ISDNLayer2* q921) } if (!q921) return; - Debug(this,DebugAll,"Attached L2 (%p,'%s') [%p]",q921,q921->toString().safe(),this); + Debug(this,DebugAll,"Attached L2 '%s' (%p,'%s') [%p]", + (q921->network()?"NET":"CPE"),q921,q921->toString().safe(),this); insert(q921); q921->attach(this); } @@ -2895,13 +3004,16 @@ bool ISDNQ931::sendStatus(const char* cause, u_int8_t callRefLen, u_int32_t call // Send RELEASE (See Q.931 3.1.9) or RELEASE COMPLETE (See Q.931 3.1.10) // IE: Cause, Display, Signal bool ISDNQ931::sendRelease(bool release, u_int8_t callRefLen, u_int32_t callRef, - bool initiator, const char* cause, const char* display, const char* signal) + 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 - msg->appendIEValue(ISDNQ931IE::Cause,"cause",cause); + ISDNQ931IE* ie = msg->appendIEValue(ISDNQ931IE::Cause,"cause",cause); + if (diag) + ie->addParam("diagnostic",diag); if (display) msg->appendIEValue(ISDNQ931IE::Display,"display",display); if (signal) @@ -2920,24 +3032,18 @@ ISDNQ931Monitor::ISDNQ931Monitor(const NamedList& params, const char* name) m_q921Cpe(0), m_cicNet(0), m_cicCpe(0), - m_parserData(this,params), + m_parserData(params), m_printMsg(true), m_extendedDebug(false) { setName(params.getValue("debugname",name)); - // Accept maximum data length + // Set parser data. Accept maximum data length m_parserData.m_maxMsgLen = 0xffffffff; + m_parserData.m_dbg = this; // Debug setDebug(params.getBoolValue("print-messages",true), params.getBoolValue("extended-debug",false)); - if (debugAt(DebugInfo)) { - String s; - s << "\r\nDisplay charset: " << (unsigned int)m_parserData.m_charsetDisplay; - s << "\r\nMax Display length: " << (unsigned int)m_parserData.m_maxDisplay; - Debug(this,DebugInfo,"Initialized: [%p]%s",this,s.c_str()); - } - else - DDebug(this,DebugAll,"Initialized [%p]",this); + Debug(this,DebugAll,"ISDN monitor created [%p]",this); } ISDNQ931Monitor::~ISDNQ931Monitor() @@ -2955,6 +3061,7 @@ ISDNQ931Monitor::~ISDNQ931Monitor() void ISDNQ931Monitor::dataLinkState(bool cmd, bool value, ISDNLayer2* layer2) { Lock lock(m_layer); +#ifdef DEBUG if (debugAt(DebugInfo)) { String tmp; if (cmd) @@ -2964,6 +3071,7 @@ void ISDNQ931Monitor::dataLinkState(bool cmd, bool value, ISDNLayer2* layer2) DDebug(this,DebugInfo,"Captured %s from '%s'. Clearing monitors", tmp.c_str(),layer2->debugName()); } +#endif terminateMonitor(0,"net-out-of-order"); } @@ -4033,8 +4141,22 @@ ISDNQ931Message* Q931Parser::decode(const DataBlock& buffer, DataBlock* segData) for (;;) { // Append IE if any if (ie) { - XDebug(m_settings->m_dbg,DebugAll, - "Adding IE '%s'. %u bytes consumed [%p]",ie->c_str(),consumed,m_msg); + // Skip non-locked IEs if told to do so + if (m_settings->flag(ISDNQ931::IgnoreNonLockedIE)) { + bool ignore = false; + if (ie->type() == ISDNQ931IE::Shift) + ignore = m_skip = !ie->getBoolValue("lock",false); + else if (m_skip) { + ignore = true; + m_skip = false; + } + if (ignore) { + String* s = static_cast(ie); + *s = String("ignored-") + *s; + } + } + XDebug(m_settings->m_dbg,DebugAll,"Adding IE '%s'. %u bytes consumed [%p]", + ie->c_str(),consumed,m_msg); if (m_settings->m_extendedDebug) ie->m_buffer.assign(data,consumed); m_msg->append(ie); @@ -4551,9 +4673,15 @@ ISDNQ931IE* Q931Parser::decodeBearerCaps(ISDNQ931IE* ie, const u_int8_t* data, #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]); // Information transfer capability + 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) @@ -4658,11 +4786,11 @@ ISDNQ931IE* Q931Parser::decodeChannelID(ISDNQ931IE* ie, const u_int8_t* data, 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 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 + // 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 @@ -4818,7 +4946,7 @@ ISDNQ931IE* Q931Parser::decodeDisplay(ISDNQ931IE* ie, const u_int8_t* data, if (!len) return errorParseIE(ie,s_errorNoData,0,0); // Check charset - if (m_settings->m_charsetDisplay <= 127 && (data[0] & 0x80)) { + if (0 != (data[0] & 0x80)) { s_ie_ieDisplay[0].addIntParam(ie,data[0]); data++; len--; @@ -5178,40 +5306,44 @@ void Q931Parser::decodeLayer3(ISDNQ931IE* ie, const u_int8_t* data, u_int32_t le 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, Information transfer capability (bit 0-4) + // 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) + // 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 - int layer = 0; - while (true) { - int tmp; -#define Q931_ENC_SETLAYER(l,idx) \ - layer = l; \ - tmp = s_ie_ieBearerCaps[idx].getValue(ie,false,-1); \ - if (tmp == -1) \ - break; \ - data[1]++; \ - data[data[1] + 1] = 0x80 | ((u_int8_t)layer << 5) | ((u_int8_t)tmp & s_ie_ieBearerCaps[idx].mask); - Q931_ENC_SETLAYER(1,4) - Q931_ENC_SETLAYER(2,6) - Q931_ENC_SETLAYER(3,7) -#undef Q931_ENC_SETLAYER - layer = 4; - break; + for (unsigned int idx = 0; layer < 4; layer++, idx++) { + int tmp = s_ie_ieBearerCaps[idx].getValue(ie,false,-1); + if (tmp == -1) { + DDebug(m_settings->m_dbg,DebugAll, + "Stop encoding '%s' IE. No user information layer %d protocol [%p]", + ie->c_str(),layer,m_msg); + break; + } + data[1]++; + data[data[1] + 1] = 0x80 | ((u_int8_t)layer << 5) | + ((u_int8_t)tmp & s_ie_ieBearerCaps[idx].mask); } - if (layer < 4) - DDebug(m_settings->m_dbg,DebugAll, - "Stop encoding '%s' IE. No user information layer %d protocol [%p]", - ie->c_str(),layer,m_msg); CHECK_IE_LENGTH(data[1] + 2,Q931_MAX_BEARERCAPS_LEN) buffer.assign(data,data[1] + 2); return true; @@ -5345,20 +5477,20 @@ 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->m_charsetDisplay <= 127) { + if (!m_settings->flag(ISDNQ931::NoDisplayCharset)) { headerLen++; header[1] = 1; - header[2] |= m_settings->m_charsetDisplay; + header[2] |= 0x31; } // Process display String display = ie->getValue(s_ie_ieDisplay[1].name); - // Check size - if (display.length() > (unsigned int)(m_settings->m_maxDisplay - headerLen)) { + // 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(), - (unsigned int)(m_settings->m_maxDisplay - sizeof(header)),m_msg); - display = display.substr(0,m_settings->m_maxDisplay - headerLen); + ie->c_str(),display.length(),maxlen,m_msg); + display = display.substr(0,maxlen); } header[1] += display.length(); clearBit7(display.c_str(),display.length()); diff --git a/libs/ysig/yatesig.h b/libs/ysig/yatesig.h index d058e2ae..5a57ced3 100644 --- a/libs/ysig/yatesig.h +++ b/libs/ysig/yatesig.h @@ -1557,6 +1557,11 @@ public: static int str2strategy(const char* name, int def = Increment) { return lookup(name,s_strategy,def); } + /** + * Keep the strategy names + */ + static TokenDict s_strategy[]; + protected: /** * Get the circuit list @@ -1564,11 +1569,6 @@ protected: inline ObjList& circuits() { return m_circuits; } - /** - * Keep the strategy names - */ - static TokenDict s_strategy[]; - private: unsigned int advance(unsigned int n, int strategy); @@ -1895,6 +1895,14 @@ public: */ static bool hasFlag(const NamedList& list, const char* param, const char* flag); + /** + * Remove a flag from a comma separated list of flags + * @param flags The list of flags + * @param flag The flag to remove + * @return True if the given flag was found and removed + */ + static bool removeFlag(String& flags, const char* flag); + /** * Add string (keyword) if found in a dictionary or integer parameter to a named list * @param list Destination list @@ -6420,20 +6428,19 @@ private: // Process received IEs // If add is true, append an IE to the message // If add is false, extract data from message. Set data to default values if IE is missing - // @return False if the IE is missing when decoding. True on success - bool processBearerCaps(ISDNQ931Message* msg, bool add); - bool processCause(ISDNQ931Message* msg, bool add); - bool processDisplay(ISDNQ931Message* msg, bool add); - bool processKeypad(ISDNQ931Message* msg, bool add); - bool processChannelID(ISDNQ931Message* msg, bool add); - bool processProgress(ISDNQ931Message* msg, bool add); - bool processRestart(ISDNQ931Message* msg, bool add); - bool processNotification(ISDNQ931Message* msg, bool add); - bool processCalledNo(ISDNQ931Message* msg, bool add); - bool processCallingNo(ISDNQ931Message* msg, bool add); + // @return False if the IE is missing when decoding or the IE wasn't added + bool processBearerCaps(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0); + bool processCause(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0); + bool processDisplay(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0); + bool processKeypad(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0); + bool processChannelID(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0); + bool processProgress(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0); + bool processRestart(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0); + bool processNotification(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0); + bool processCalledNo(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0); + bool processCallingNo(ISDNQ931Message* msg, bool add, ISDNQ931ParserData* data = 0); // IE parameters - u_int8_t m_charsetDisplay; // Display: The charset String m_display; // Display: The data String m_callerNo; // CallingNo: Number String m_callerType; // CallingNo: Number type @@ -6632,9 +6639,10 @@ protected: * Clear all call data. * Remove from controller's queue. Decrease the object's refence count * @param reason Optional release reason. If missing, the last reason is used + * @param diag Optional hexified string for the cause diagnostic * @return Pointer to an SignallingEvent of type Release, with no message */ - SignallingEvent* releaseComplete(const char* reason = 0); + SignallingEvent* releaseComplete(const char* reason = 0, const char* diag = 0); /** * Get an event from the circuit reserved for this call @@ -6679,7 +6687,7 @@ private: bool sendInfo(SignallingMessage* sigMsg); bool sendProgress(SignallingMessage* sigMsg); bool sendRelease(const char* reason = 0, SignallingMessage* sigMsg = 0); - bool sendReleaseComplete(const char* reason = 0); + bool sendReleaseComplete(const char* reason = 0, const char* diag = 0); bool sendSetup(SignallingMessage* sigMsg); bool sendSuspendRej(const char* reason = 0, SignallingMessage* sigMsg = 0); // Errors on processing received messages @@ -6815,14 +6823,23 @@ class YSIG_API ISDNQ931ParserData public: /** * Constructor - * @param dbg The debug enabler owning this object * @param params Parser settings + * @param dbg The debug enabler used for output */ - ISDNQ931ParserData(DebugEnabler* dbg, const NamedList& params); + ISDNQ931ParserData(const NamedList& params, DebugEnabler* dbg = 0); - DebugEnabler* m_dbg; // The owner of this parser + /** + * Check the state of a flag + * @param mask The flag to check + * @return True if the given flag is set + */ + inline bool flag(int mask) + { return (0 != (m_flags & mask)); } + + DebugEnabler* m_dbg; // The debug enabler used for output u_int32_t m_maxMsgLen; // Maximum length of outgoing messages (or message segments) - u_int8_t m_charsetDisplay; // Charset for Display IE + int m_flags; // The current behaviour flags + int m_flagsOrig; // The original behaviour flags u_int8_t m_maxDisplay; // Max Display IE size bool m_allowSegment; // True if message segmentation is allowed u_int8_t m_maxSegments; // Maximum allowed segments for outgoing messages @@ -6837,6 +6854,70 @@ class YSIG_API ISDNQ931 : public SignallingCallControl, public ISDNLayer3 { friend class ISDNQ931Call; public: + /** + * Enumeration flags defining the behaviour of the ISDN call controller and + * any active calls managed by it + */ + enum BehaviourFlags { + // Append the progress indicator 'non-isdn-source' if present when sending SETUP + // If this flag is not set, the indicator will be removed from the message + SendNonIsdnSource = 0x00000001, + // Ignore (don't send) the progress indicator 'non-isdn-destination' if present + // when sending SETUP ACKNOWLEDGE or CONNECT + IgnoreNonIsdnDest = 0x00000002, + // Set presentation='allowed' and screening='network-provided' if presentation + // is not 'allowed' or screening is not 'network-provided' when sending SETUP + // Used for Calling Party Number and Redirecting Number IEs + // This is done only if the calling number or redirecting number are not empty + SetPresNetProv = 0x00000004, + // Translate '3.1khz-audio' transfer capability code 0x10 to/from 0x08 + Translate31kAudio = 0x00000008, + // Send only tranfer mode and rate when sending the Bearer Capability IE + // with transfer capability 'udi' or 'rdi' (unrestricted/restricted + // digital information) + URDITransferCapsOnly = 0x00000010, + // Don't send Layer 1 capabilities (data format) with the + // Bearer Capability IE when in circuit switch mode + NoLayer1Caps = 0x00000020, + // Don't parse incoming IEs found after a temporary (non-locking) shift + IgnoreNonLockedIE = 0x00000040, + // Don't send the Display IE + // This flag is internally set for EuroIsdnE1 type when the call + // controller is the CPE side of the link + NoDisplayIE = 0x00000080, + // Don't append a charset byte 0xb1 before Display data + NoDisplayCharset = 0x00000100, + // Send a Sending Complete IE even if no overlap dialing + ForceSendComplete = 0x00000200, + // Check the validity of the notification indicator when sending a NOTIFY message + CheckNotifyInd = 0x00000400, + // Don't change call state to Active instead of ConnectRequest after sending CONNECT + // This flag is internally set when the call controller is the CPE side of the data link + NoActiveOnConnect = 0x00000800, + }; + + /** + * Call controller switch type. Each value is a mask of behaviour flags + */ + enum SwitchType { + Unknown = 0, + // Standard Euro ISDN (CTR4, ETSI 300-102) + EuroIsdnE1 = ForceSendComplete|CheckNotifyInd|NoDisplayCharset|URDITransferCapsOnly, + // T1 Euro ISDN variant (ETSI 300-102) + EuroIsdnT1 = ForceSendComplete|CheckNotifyInd, + // National ISDN + NationalIsdn = SendNonIsdnSource, + // DMS 100 + Dms100 = SetPresNetProv|IgnoreNonIsdnDest, + // Lucent 5E + Lucent5e = IgnoreNonLockedIE, + // AT&T 4ESS + Att4ess = SetPresNetProv|IgnoreNonLockedIE|Translate31kAudio|NoLayer1Caps, + // QSIG Switch + QSIG = NoActiveOnConnect|NoDisplayIE|NoDisplayCharset + }; + + /** * Constructor * Initialize this object and the component @@ -6990,16 +7071,17 @@ public: * @param call The call requesting the operation * @param release True to send RELEASE, false to send RELEASE COMPLETE * @param cause Value for Cause IE + * @param diag Optional hexified string for cause dignostic * @param display Optional value for Display IE * @param signal Optional value for Signal IE * @return The result of the operation (true if succesfully sent) */ inline bool sendRelease(ISDNQ931Call* call, bool release, const char* cause, - const char* display = 0, const char* signal = 0) { + const char* diag = 0, const char* display = 0, const char* signal = 0) { if (!call) return false; return sendRelease(release,call->callRefLen(),call->callRef(), - call->outgoing(),cause,display,signal); + call->outgoing(),cause,diag,display,signal); } /** @@ -7044,6 +7126,16 @@ public: m_parserData.m_extendedDebug = m_extendedDebug; } + /** + * The list of behaviour flag names + */ + static TokenDict s_flags[]; + + /** + * The list of switch type names + */ + static TokenDict s_swType[]; + protected: /** * Method called periodically to check timeouts @@ -7155,14 +7247,14 @@ protected: * @param callRef The call reference * @param initiator The call initiator flag * @param cause Value for Cause IE + * @param diag Optional hexified string for cause dignostic * @param display Optional value for Display IE * @param signal Optional value for Signal IE * @return The result of the operation (true if succesfully sent) */ bool sendRelease(bool release, u_int8_t callRefLen, u_int32_t callRef, - bool initiator, const char* cause, const char* display = 0, - const char* signal = 0); - + bool initiator, const char* cause, const char* diag = 0, + const char* display = 0, const char* signal = 0); private: Mutex m_layer; // Lock layer operation ISDNLayer2* m_q921; // The attached layer 2