/** * tcap.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 "yateasn.h" using namespace TelEngine; #ifdef DEBUG static void dumpData(int debugLevel, SS7TCAP* tcap, String message, void* obj, NamedList& params, DataBlock data = DataBlock::empty()) { if (tcap) { String tmp; params.dump(tmp,"\r\n ",'\'',true); String str; str.hexify(data.data(),data.length(),' '); Debug(tcap,debugLevel,"%s [%p] - \r\nparams='%s',\r\ndata='%s'", message.safe(),obj,tmp.c_str(),str.c_str()); } } #endif TCAPUser::~TCAPUser() { Debug(this,DebugAll,"TCAPUser::~TCAPUser() [%p] - tcap user destroyed",this); } void TCAPUser::destroyed() { Debug(this,DebugAll,"TCAPUser::destroyed() [%p]",this); Lock lock(m_tcapMtx); if (m_tcap) { // notify SCCP OutOfService NamedList p(""); m_tcap->updateUserStatus(this,SCCPManagement::UserOutOfService,p); m_tcap->detach(this); Debug(this,DebugAll,"TCAPUser::~TCAPUser() [%p] - Detached from TCAP (%p,%s)",this,m_tcap,m_tcap->toString().safe()); m_tcap->deref(); m_tcap = 0; } lock.drop(); SignallingComponent::destroyed(); } void TCAPUser::attach(SS7TCAP* tcap) { Lock lock(m_tcapMtx); if (m_tcap == tcap) return; SS7TCAP* tmp = m_tcap; m_tcap = tcap; lock.drop(); DDebug(this,DebugAll,"TCAPUser::attach(tcap=%s [%p], replacing tcap=%s [%p] [%p]",(m_tcap ? m_tcap->toString().safe() : ""),m_tcap, (tmp ? tmp->toString().c_str() : ""),tmp,this); if (tmp) { tmp->detach(this); Debug(this,DebugAll,"TCAPUser::attach() - Detached from TCAP (%p,%s) [%p]",tmp,tmp->toString().safe(),this); tmp->deref(); tmp = 0; } if (!tcap) return; tcap->attach(this); tcap->ref(); Debug(this,DebugAll,"Attached to TCAP (%p,%s) [%p] tcapRefCount=%d",tcap,tcap->toString().safe(),this,tcap->refcount()); } bool TCAPUser::tcapIndication(NamedList& params) { Debug(this,DebugStub,"Please implement TCAPUser::tcapIndication()"); return false; } bool TCAPUser::managementNotify(SCCP::Type type, NamedList& params) { Debug(this,DebugStub,"Please implement TCAPUser::managementNotify()"); return false; } int TCAPUser::managementState() { return SCCPManagement::UserOutOfService; } struct PrimitiveMapping { int primitive; int mappedTo; }; static bool s_extendedDbg = false; static bool s_printMsgs = false; static const String s_checkAddr = "tcap.checkAddress"; static const String s_localPC = "LocalPC"; static const String s_remotePC = "RemotePC"; static const String s_callingPA = "CallingPartyAddress"; static const String s_callingSSN = "CallingPartyAddress.ssn"; static const String s_callingRoute = "CallingPartyAddress.route"; static const String s_calledPA = "CalledPartyAddress"; static const String s_calledSSN = "CalledPartyAddress.ssn"; static const String s_HopCounter = "HopCounter"; // TCAP message parameters static const String s_tcapUser = "tcap.user"; static const String s_tcapBasicTerm = "tcap.transaction.terminationBasic"; static const String s_tcapEndNow = "tcap.transaction.endNow"; static const String s_tcapRequest = "tcap.request.type"; static const String s_tcapRequestError = "tcap.request.error"; static const String s_tcapTransPrefix = "tcap.transaction"; static const String s_tcapMsgType = "tcap.transaction.messageType"; static const String s_tcapLocalTID = "tcap.transaction.localTID"; static const String s_tcapRemoteTID = "tcap.transaction.remoteTID"; static const String s_tcapAbortCause = "tcap.transaction.abort.cause"; static const String s_tcapAbortInfo = "tcap.transaction.abort.information"; static const String s_tcapDialogPrefix = "tcap.dialogPDU"; static const String s_tcapProtoVers = "tcap.dialogPDU.protocol-version"; static const String s_tcapIntAppID = "tcap.dialogPDU.integerApplicationId"; static const String s_tcapObjAppID = "tcap.dialogPDU.objectApplicationId"; static const String s_tcapIntSecID = "tcap.dialogPDU.integerSecurityId"; static const String s_tcapObjSecID = "tcap.dialogPDU.objectSecurityId"; static const String s_tcapIntConfidID = "tcap.dialogPDU.integerConfidentialityId"; static const String s_tcapObjConfidID = "tcap.dialogPDU.objectConfidentialityId"; static const String s_tcapReference = "tcap.dialogPDU.userInformation.direct-reference"; static const String s_tcapDataDesc = "tcap.dialogPDU.userInformation.data-descriptor"; static const String s_tcapEncodingContent = "tcap.dialogPDU.userInformation.encoding-contents"; static const String s_tcapEncodingType = "tcap.dialogPDU.userInformation.encoding-type"; static const String s_tcapCompCount = "tcap.component.count"; static const String s_tcapCompPrefix = "tcap.component"; static const String s_tcapLocalCID = "localCID"; static const String s_tcapRemoteCID = "remoteCID"; static const String s_tcapCompType = "componentType"; static const String s_tcapOpCodeType = "operationCodeType"; static const String s_tcapOpCode = "operationCode"; static const String s_tcapErrCodeType = "errorCodeType"; static const String s_tcapErrCode = "errorCode"; static const String s_tcapProblemCode = "problemCode"; static const String s_tcapPayload = "payload"; static void populateSCCPAddress(NamedList& localAddr, NamedList& remoteAddr, NamedList& initParams, bool initLocal, bool keepPrefix = false) { String localParam(initLocal ? s_callingPA : s_calledPA); String remoteParam(initLocal ? s_calledPA : s_callingPA); NamedList aux(""); aux.copySubParams(initParams,localParam + "."); if (keepPrefix) { for (unsigned int i = 0; i < aux.count(); i++) { NamedString* p = aux.getParam(i); if (!TelEngine::null(p)) { localAddr.setParam(remoteParam + "." + p->name(),*p); } } } else localAddr.copyParams(aux); if (!TelEngine::null(initParams.getParam(s_localPC))) localAddr.copyParam(initParams,s_localPC); aux.clearParams(); aux.copySubParams(initParams,remoteParam + "."); if (keepPrefix) { for (unsigned int i = 0; i < aux.count(); i++) { NamedString* p = aux.getParam(i); if (!TelEngine::null(p)) { remoteAddr.setParam(localParam + "." + p->name(),*p); } } } else remoteAddr.copyParams(aux); if (!TelEngine::null(initParams.getParam(s_remotePC))) remoteAddr.copyParam(initParams,s_remotePC); } static void compPrefix(String& prefix, unsigned int index, bool endSep = false) { prefix = s_tcapCompPrefix; prefix << "." << index << (endSep ? "." : ""); } /** * SS7TCAP implementation */ const TokenDict SS7TCAP::s_tcapVersion[] = { {"UnknownTCAP", SS7TCAP::UnknownTCAP}, {"ITU-T TCAP", SS7TCAP::ITUTCAP}, {"ANSI TCAP", SS7TCAP::ANSITCAP}, {0,-1}, }; const TokenDict SS7TCAP::s_compPrimitives[] = { {"Invoke", SS7TCAP::TC_Invoke}, {"ResultLast", SS7TCAP::TC_ResultLast}, {"U_Error", SS7TCAP::TC_U_Error}, {"U_Reject", SS7TCAP::TC_U_Reject}, {"R_Reject", SS7TCAP::TC_R_Reject}, {"L_Reject", SS7TCAP::TC_L_Reject}, {"InvokeNotLast", SS7TCAP::TC_InvokeNotLast}, {"ResultNotLast", SS7TCAP::TC_ResultNotLast}, {"L_Cancel", SS7TCAP::TC_L_Cancel}, {"U_Cancel", SS7TCAP::TC_U_Cancel}, {"TimerReset", SS7TCAP::TC_TimerReset}, {0,0}, }; const TokenDict SS7TCAP::s_transPrimitives[] = { {"Unidirectional", SS7TCAP::TC_Unidirectional}, {"Begin", SS7TCAP::TC_Begin}, {"QueryWithPerm", SS7TCAP::TC_QueryWithPerm}, {"QueryWithoutPerm", SS7TCAP::TC_QueryWithoutPerm}, {"Continue", SS7TCAP::TC_Continue}, {"ConversationWithPerm", SS7TCAP::TC_ConversationWithPerm}, {"ConversationWithoutPerm", SS7TCAP::TC_ConversationWithoutPerm}, {"End", SS7TCAP::TC_End}, {"Response", SS7TCAP::TC_Response}, {"U_Abort", SS7TCAP::TC_U_Abort}, {"P_Abort", SS7TCAP::TC_P_Abort}, {"Notice", SS7TCAP::TC_Notice}, {"Unknown", SS7TCAP::TC_Unknown}, {0,0}, }; const TokenDict SS7TCAP::s_compOperClasses[] = { {"reportAll", SS7TCAP::SuccessOrFailureReport}, {"reportFail", SS7TCAP::FailureOnlyReport}, {"reportSuccess", SS7TCAP::SuccessOnlyReport}, {"reportNone", SS7TCAP::NoReport}, }; SS7TCAP::SS7TCAP(const NamedList& params) : SCCPUser(params), m_usersMtx(true,"TCAPUsers"), m_inQueueMtx(true,"TCAPPendingMsg"), m_SSN(0), m_defaultRemoteSSN(0), m_defaultHopCounter(0), m_defaultRemotePC(0), m_remoteTypePC(SS7PointCode::Other), m_trTimeout(300), m_transactionsMtx(true,"TCAPTransactions"), m_tcapType(UnknownTCAP), m_idsPool(0) { Debug(this,DebugAll,"SS7TCAP::SS7TCAP() [%p] created",this); m_recvMsgs = m_sentMsgs = m_discardMsgs = m_normalMsgs = m_abnormalMsgs = 0; m_ssnStatus = SCCPManagement::UserOutOfService; } SS7TCAP::~SS7TCAP() { Debug(this,DebugAll,"SS7TCAP::~SS7TCAP() [%p] destroyed, refCount=%d, usersCount=%d",this,refcount(),m_users.count()); if (m_users.count()) { Debug(this,DebugCrit,"SS7TCAP destroyed while having %d user(s) still attached [%p]",m_users.count(),this); ListIterator iter(m_users); for (;;) { TCAPUser* user = static_cast(iter.get()); // End of iteration? if (!user) break; if(user->tcap()) user->setTCAP(0); } m_users.setDelete(false); } m_transactions.clear(); m_inQueue.clear(); } bool SS7TCAP::initialize(const NamedList* config) { #ifdef DEBUG if(config && debugAt(DebugAll)) { String tmp; config->dump(tmp,"\r\n ",'\'',true); Debug(this,DebugAll,"SS7TCAP::initialize([%p]) [%p] for configuration '%s'",config,this,tmp.c_str()); } #endif if (config) { // read local point code and default remote point code m_SSN = config->getIntValue(YSTRING("local_SSN"),-1); m_defaultRemoteSSN = config->getIntValue(YSTRING("default_remote_SSN"),-1); m_defaultHopCounter = config->getIntValue(YSTRING("default_hopcounter"),0); if (m_defaultHopCounter > 15 || config->getBoolValue(YSTRING("default_hopcounter"))) m_defaultHopCounter = 15; const char* code = config->getValue(YSTRING("default_remote_pointcode")); m_remoteTypePC = SS7PointCode::lookup(config->getValue(YSTRING("pointcodetype"),"")); if (!(m_defaultRemotePC.assign(code,m_remoteTypePC) && m_defaultRemotePC.pack(m_remoteTypePC))) { int codeInt = config->getIntValue(YSTRING("default_remote_pointcode")); if (!m_defaultRemotePC.unpack(m_remoteTypePC,codeInt)) Debug(this,DebugMild,"SS7TCAP::initialize([%p]) [%p] - Invalid default_remote_pointcode=%s value configured", config,this,code); } m_trTimeout = config->getIntValue(YSTRING("transact_timeout"),m_trTimeout / 1000) * 1000; // seconds to miliseconds s_printMsgs = config->getBoolValue(YSTRING("print-messages"),false); s_extendedDbg = config->getBoolValue(YSTRING("extended-debug"),false); } bool ok = SCCPUser::initialize(config); if (ok) { NamedList p(""); sendSCCPNotify(p); Debug(this,DebugInfo,"SSN=%d has status='%s'[%p]",m_SSN,lookup(m_ssnStatus,SCCPManagement::broadcastType(),""),this); } return ok; } bool SS7TCAP::sendData(DataBlock& data, NamedList& params) { if (params.getBoolValue(s_callingSSN)) params.setParam(s_callingSSN,String(m_SSN)); if (params.getBoolValue(s_checkAddr,true)) { String dpc = params.getValue(s_remotePC,""); unsigned int pc = m_defaultRemotePC.pack(m_remoteTypePC); if (dpc.null() && pc) params.addParam(s_remotePC,String(pc)); int ssn = params.getIntValue(s_calledSSN,-1); if (ssn < 0 && m_defaultRemoteSSN <= 255) params.setParam(s_calledSSN,String(m_defaultRemoteSSN)); ssn = params.getIntValue(s_callingSSN,-1); if (ssn < 0 && m_SSN <= 255) { params.setParam(s_callingSSN,String(m_SSN)); if (!params.getParam(s_callingRoute)) params.addParam(s_callingRoute,"ssn"); } if (m_defaultHopCounter && !params.getParam(s_HopCounter)) params.addParam(s_HopCounter,String(m_defaultHopCounter)); } #ifdef DEBUG if (s_printMsgs && debugAt(DebugInfo)) dumpData(DebugInfo,this,"Sending to SCCP : ",this,params,data); #endif return SCCPUser::sendData(data,params); } HandledMSU SS7TCAP::receivedData(DataBlock& data, NamedList& params) { HandledMSU result; if (!data.length()) return result; #ifdef DEBUG if (s_printMsgs && debugAt(DebugInfo)) dumpData(DebugInfo,this,"Received from SCCP: ",this,params,data); #endif unsigned int cpaSSN = params.getIntValue(s_calledSSN,0); unsigned int ssn = params.getIntValue("ssn",0); if (m_SSN != cpaSSN && m_SSN != ssn) return result; enqueue(new SS7TCAPMessage(params,data)); result = HandledMSU::Accepted; return result; } HandledMSU SS7TCAP::notifyData(DataBlock& data, NamedList& params) { HandledMSU result; #ifdef DEBUG if (s_printMsgs && debugAt(DebugInfo)) dumpData(DebugInfo,this,"Received notify from SCCP: ",this,params,data); #endif enqueue(new SS7TCAPMessage(params,data,true)); return result; } bool SS7TCAP::managementNotify(SCCP::Type type, NamedList& params) { Lock lock(m_usersMtx); ListIterator iter(m_users); bool ok = false; if (type == SCCP::SubsystemStatus && m_SSN != (unsigned int)params.getIntValue("ssn")) { params.setParam("subsystem-status","UserOutOfService"); return true; } bool inService = false; for (;;) { TCAPUser* user = static_cast(iter.get()); // End of iteration? if (!user) break; if (user->managementNotify(type,params)) ok = true; if (user->managementState() == (int) SCCPManagement::UserInService) inService = true; } if (type == SCCP::SubsystemStatus) params.setParam("subsystem-status",(inService ? "UserInService" : "UserOutOfService")); return ok; } void SS7TCAP::updateUserStatus(TCAPUser* user, SCCPManagement::LocalBroadcast status, NamedList& params) { if (!user) return; DDebug(this,DebugAll,"SS7TCAP::updateUserStatus(user=%s[%p],status=%d) [%p]",user->toString().c_str(),user,status,this); bool notify = false; Lock l(m_usersMtx); SCCPManagement::LocalBroadcast tmp = m_ssnStatus; switch (m_ssnStatus) { case SCCPManagement::UserOutOfService: if (status == SCCPManagement::UserInService) { m_ssnStatus = SCCPManagement::UserInService; notify = true; } break; case SCCPManagement::UserInService: if (status == SCCPManagement::UserOutOfService) { ListIterator it(m_users); for (;;) { TCAPUser* usr = static_cast(it.get()); // End of iteration? if (!usr) { m_ssnStatus = SCCPManagement::UserOutOfService; notify = true; break; } if (usr->managementState() == (int) SCCPManagement::UserInService) break; } } default: break; } if (notify) { sendSCCPNotify(params); // it always returns false, so no point in checking result Debug(this,DebugInfo,"SSN=%d changed status from '%s' to '%s' [%p]",m_SSN, lookup(tmp,SCCPManagement::broadcastType(),""),lookup(m_ssnStatus,SCCPManagement::broadcastType(),""),this); } } bool SS7TCAP::sendSCCPNotify(NamedList& params) { params.setParam(YSTRING("subsystem-status"),lookup(m_ssnStatus,SCCPManagement::broadcastType(),"")); params.setParam(YSTRING("ssn"),String(m_SSN)); if (!params.getParam(YSTRING("smi"))) params.setParam("smi","0"); return sccpNotify(SCCP::StatusRequest,params); } void SS7TCAP::attach(TCAPUser* user) { if (!user) return; DDebug(this,DebugAll,"SS7TCAP::attach(user=%s [%p]) [%p]",user->toString().safe(),user,this); Lock l(m_usersMtx); if (m_users.find(user)) return; m_users.append(user); Debug(this,DebugAll,"SS7TCAP '%s'[%p] attached user=%s [%p]",toString().safe(),this,user->toString().safe(),user); } void SS7TCAP::detach(TCAPUser* user) { if (!user) return; DDebug(this,DebugAll,"SS7TCAP::detach(user=%s [%p]) [%p], refCount=%d",user->toString().safe(),user,this,refcount()); Lock l(m_usersMtx); if (m_users.find(user)) { m_users.remove(user,false); Debug(this,DebugAll,"SS7TCAP '%s'[%p] detached user=%s [%p], refCount=%d",toString().safe(),this,user->toString().c_str(),user,refcount()); } } void SS7TCAP::enqueue(SS7TCAPMessage* msg) { if (!msg) return; Lock lock(m_inQueueMtx); m_inQueue.append(msg); XDebug(this,DebugAll,"SS7TCAP::enqueue(). Enqueued transaction wrapper (%p) [%p]",msg,this); } SS7TCAPMessage* SS7TCAP::dequeue() { Lock lock(m_inQueueMtx,SignallingEngine::maxLockWait()); if (!lock.locked()) return 0; ObjList* obj = m_inQueue.skipNull(); if (!obj) return 0; SS7TCAPMessage* msg = static_cast(obj->get()); m_inQueue.remove(msg,false); XDebug(this,DebugAll,"SS7TCAP::dequeue(). Dequeued transaction wrapper (%p) [%p]",msg,this); return msg; } void SS7TCAP::allocTransactionID(String& str) { u_int32_t tmp = m_idsPool; m_idsPool++; unsigned char buff[sizeof(tmp)]; int len = sizeof(tmp); for (int index = len - 1; index >= 0; index--) { buff[index] = tmp & 0xff; tmp >>= 8; } str.hexify(buff,len,' '); XDebug(this,DebugAll,"SS7TCAP::allocTransactionID() - allocated new transaction ID=%s [%p]",str.c_str(),this); } const String SS7TCAP::allocTransactionID() { String str; allocTransactionID(str); return str; } bool SS7TCAP::sendToUser(NamedList& params) { String userName = params.getValue(s_tcapUser,""); // if it has a specified user, send it to that user Lock lock(m_usersMtx); if (!userName.null()) { ObjList* obj = m_users.find(userName); if (!obj) { Debug(this,DebugInfo,"SS7TCAP::sendToUser() [%p] - failed to send message with id=%s to user=%s," " no such application",this,params.getValue(s_tcapLocalTID),userName.c_str()); return false; } TCAPUser* user = static_cast(obj->get()); if (!user) { Debug(this,DebugInfo,"SS7TCAP::sendToUser() [%p] - failed to send message with id=%s to user,%s" " no such application",this,params.getValue(s_tcapLocalTID),userName.c_str()); return false; } #ifdef DEBUG if (s_printMsgs && debugAt(DebugInfo)) dumpData(DebugInfo,this,"Sent to TCAP user: ",this,params); #endif return user->tcapIndication(params); } else { ListIterator iter(m_users); for (;;) { TCAPUser* user = static_cast(iter.get()); // End of iteration? if (!user) { Debug(this,DebugInfo,"SS7TCAP::sendToUser() [%p] - failed to send message with id=%s to any user", this,params.getValue(s_tcapLocalTID)); return false; } if (user->tcapIndication(params)) { params.setParam(s_tcapUser,user->toString()); // set the user for this transaction #ifdef DEBUG if (s_printMsgs && debugAt(DebugInfo)) dumpData(DebugInfo,this,"Sent to TCAP user: ",this,params); #endif break; } } } return true; } void SS7TCAP::status(NamedList& status) { status.setParam("totalIncoming",String(m_recvMsgs)); status.setParam("totalOutgoing",String(m_sentMsgs)); status.setParam("totalDiscarded",String(m_discardMsgs)); status.setParam("totalNormal",String(m_normalMsgs)); status.setParam("totalAbnormal",String(m_abnormalMsgs)); } void SS7TCAP::userStatus(NamedList& status) { Debug(this,DebugStub,"Please implement SS7TCAP::userStatus()"); } SS7TCAPTransaction* SS7TCAP::getTransaction(const String& tid) { SS7TCAPTransaction* tr = 0; Lock lock(m_transactionsMtx); ObjList* o = m_transactions.find(tid); if (o) tr = static_cast(o->get()); if (tr && tr->ref()) return tr; return 0; } void SS7TCAP::removeTransaction(SS7TCAPTransaction* tr) { Lock lock(m_transactionsMtx); m_transactions.remove(tr); } void SS7TCAP::timerTick(const Time& when) { // first check pending received messages SS7TCAPMessage* msg = dequeue(); while (msg) { processSCCPData(msg); TelEngine::destruct(msg); //break; msg = dequeue(); } // update/handle rest of transactions Lock lock(m_transactionsMtx); ListIterator iter(m_transactions); for (;;) { SS7TCAPTransaction* tr = static_cast(iter.get()); // End of iteration? if (!tr) break; if (!tr->ref()) continue; lock.drop(); NamedList params(""); DataBlock data; if (tr->transactionState() != SS7TCAPTransaction::Idle) tr->checkComponents(); if (tr->endNow()) tr->setState(SS7TCAPTransaction::Idle); if (tr->timedOut()) { DDebug(this,DebugInfo,"SS7TCAP::timerTick() - transaction with id=%s(%p) timed out [%p]",tr->toString().c_str(),tr,this); tr->updateToEnd(); buildSCCPData(params,tr); if (!tr->basicEnd()) tr->transactionData(params); sendToUser(params); tr->setState(SS7TCAPTransaction::Idle); } if (tr->transactionState() == SS7TCAPTransaction::Idle) removeTransaction(tr); TelEngine::destruct(tr); if (!lock.acquire(m_transactionsMtx)) break; } } HandledMSU SS7TCAP::processSCCPData(SS7TCAPMessage* msg) { HandledMSU result; if (!msg) return result; XDebug(this,DebugAll,"SS7TCAP::processSCCPData(msg=[%p]) [%p]",msg,this); NamedList& msgParams = msg->msgParams(); DataBlock& msgData = msg->msgData(); SS7TCAPError transactError = decodeTransactionPart(msgParams,msgData); if (transactError.error() != SS7TCAPError::NoError) return handleError(transactError,msgParams,msgData); NamedString* trID = msgParams.getParam(s_tcapLocalTID); String trType = msgParams.getValue(s_tcapRequest); SS7TCAP::TCAPUserTransActions type = (SS7TCAP::TCAPUserTransActions)trType.toInteger(SS7TCAP::s_transPrimitives); // check if it's a notice from SCCP, switch the ids if so if (msg->isNotice()) { trID = msgParams.getParam(s_tcapRemoteTID); msgParams.setParam(s_tcapRemoteTID,msgParams.getValue(s_tcapLocalTID)); msgParams.setParam(s_tcapLocalTID,(TelEngine::null(trID) ? "" : *trID)); type = TC_Notice; msgParams.setParam(s_tcapRequest,lookup(type,SS7TCAP::s_transPrimitives,"Notice")); } else incCounter(SS7TCAP::IncomingMsgs); SS7TCAPTransaction* tr = 0; switch (type) { case SS7TCAP::TC_Unidirectional: case SS7TCAP::TC_Begin: case SS7TCAP::TC_QueryWithPerm: case SS7TCAP::TC_QueryWithoutPerm: // if there isn't a destination ID, allocate a new one and build a transaction if (TelEngine::null(trID)) { String newID; allocTransactionID(newID); tr = buildTransaction(type,newID,msgParams,false); tr->ref(); m_transactionsMtx.lock(); m_transactions.append(tr); m_transactionsMtx.unlock(); msgParams.setParam(s_tcapLocalTID,newID); } break; case SS7TCAP::TC_Continue: case SS7TCAP::TC_ConversationWithPerm: case SS7TCAP::TC_ConversationWithoutPerm: case SS7TCAP::TC_End: case SS7TCAP::TC_Response: case SS7TCAP::TC_P_Abort: case SS7TCAP::TC_U_Abort: case SS7TCAP::TC_Notice: if (TelEngine::null(trID)) { transactError.setError(SS7TCAPError::Transact_UnassignedTransactionID); return handleError(transactError,msgParams,msgData); } tr = getTransaction(*trID); if (!tr) { transactError.setError(SS7TCAPError::Transact_UnassignedTransactionID); return handleError(transactError,msgParams,msgData); } transactError = tr->update((SS7TCAP::TCAPUserTransActions)type,msgParams,false); if (transactError.error() != SS7TCAPError::NoError) { result = handleError(transactError,msgParams,msgData,tr); TelEngine::destruct(tr); return result; } break; default: incCounter(SS7TCAP::DiscardedMsgs); return result; } if (tr) { transactError = tr->handleData(msgParams,msgData); if (transactError.error() != SS7TCAPError::NoError) { result = handleError(transactError,msgParams,msgData,tr); TelEngine::destruct(tr); return result; } tr->addSCCPAddressing(msgParams,true); tr->updateState(false); if (sendToUser(msgParams)) { tr->setUserName(msgParams.getValue(s_tcapUser)); tr->endNow(msgParams.getBoolValue(s_tcapEndNow,false)); if (tr->transactionType() == SS7TCAP::TC_Unidirectional || tr->transactionType() == SS7TCAP::TC_U_Abort || tr->transactionType() == SS7TCAP::TC_P_Abort || tr->transactionType() == SS7TCAP::TC_End || tr->transactionType() == SS7TCAP::TC_Response) tr->setState(SS7TCAPTransaction::Idle); else tr->setTransmitState(SS7TCAPTransaction::Transmitted); } else if (type != SS7TCAP::TC_Notice) { tr->update(SS7TCAP::TC_U_Abort,msgParams,false); buildSCCPData(msgParams,tr); tr->setTransmitState(SS7TCAPTransaction::Transmitted); tr->updateState(false); } else tr->setState(SS7TCAPTransaction::Idle); TelEngine::destruct(tr); } result = HandledMSU::Accepted; incCounter(SS7TCAP::NormalMsgs); return result; } SS7TCAPError SS7TCAP::userRequest(NamedList& params) { #ifdef DEBUG if (s_printMsgs && debugAt(DebugInfo)) dumpData(DebugInfo,this,"SS7TCAP::userRequest() - received request ",this,params); #endif NamedString* req = params.getParam(s_tcapRequest); NamedString* otid = params.getParam(s_tcapLocalTID); NamedString* user = params.getParam(s_tcapUser); SS7TCAPError error(m_tcapType); if (TelEngine::null(req)) { Debug(this,DebugInfo,"SS7TCAP::userRequest()[%p] - received a transaction request from user=%s with originating ID=%s" " without request type, rejecting it",this,(user ? user->c_str() : ""),(otid ? otid->c_str() : "")); params.setParam(s_tcapRequestError,"missing_primitive"); error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); return error; } SS7TCAPTransaction* tr = 0; if (!TelEngine::null(req)) { int type = req->toInteger(SS7TCAP::s_transPrimitives); switch (type) { case SS7TCAP::TC_Unidirectional: case SS7TCAP::TC_Begin: case SS7TCAP::TC_QueryWithPerm: case SS7TCAP::TC_QueryWithoutPerm: // if otid not set, alloc one and set it if (TelEngine::null(otid)) { params.setParam(s_tcapLocalTID,allocTransactionID()); otid = params.getParam(s_tcapLocalTID); } else { // if set, check if we already have it tr = getTransaction(*otid); if (tr) { Debug(this,DebugInfo,"SS7TCAP::userRequest()[%p] - received a new transaction request from user=%s with originating ID=%s which is the ID " "of an already existing transaction, rejecting the request",this,(user ? user->c_str() : ""),otid->c_str()); params.setParam(s_tcapRequestError,"allocated_id"); error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); TelEngine::destruct(tr); return error; } } // create transaction tr = buildTransaction((SS7TCAP::TCAPUserTransActions)type,otid,params,true); if (!TelEngine::null(user)) tr->setUserName(user); tr->ref(); m_transactionsMtx.lock(); m_transactions.append(tr); m_transactionsMtx.unlock(); break; case SS7TCAP::TC_Continue: case SS7TCAP::TC_ConversationWithPerm: case SS7TCAP::TC_ConversationWithoutPerm: case SS7TCAP::TC_End: case SS7TCAP::TC_Response: case SS7TCAP::TC_U_Abort: // find transaction and update if (!TelEngine::null(otid)) { tr = getTransaction(*otid); if (!tr) { params.setParam(s_tcapRequestError,"unknown_transaction"); error.setError(SS7TCAPError::Transact_UnassignedTransactionID); return error; } error = tr->update((SS7TCAP::TCAPUserTransActions)type,params); if (error.error() != SS7TCAPError::NoError) { TelEngine::destruct(tr); return error; } } else { params.setParam(s_tcapRequestError,"need_transaction_id"); error.setError(SS7TCAPError::Transact_UnassignedTransactionID); return error; } break; case SS7TCAP::TC_Unknown: if (!TelEngine::null(otid)) tr = getTransaction(*otid); break; case SS7TCAP::TC_P_Abort: case SS7TCAP::TC_Notice: default: Debug(this,DebugAll,"SS7TCAP::userRequest() - received user request with unsuited primitive='%s' [%p]",req->c_str(),this); params.setParam(s_tcapRequestError,"wrong_primitive"); error.setError(SS7TCAPError::Transact_UnrecognizedPackageType); return error; } } if (tr) { error = tr->handleDialogPortion(params,true); if (error.error() != SS7TCAPError::NoError) { TelEngine::destruct(tr); return error; } error = tr->handleComponents(params,true); if (error.error() != SS7TCAPError::NoError) { TelEngine::destruct(tr); return error; } if (tr->transmitState() == SS7TCAPTransaction::PendingTransmit) { tr->updateState(true); buildSCCPData(params,tr); tr->setTransmitState(SS7TCAPTransaction::Transmitted); } else if (tr->transmitState() == SS7TCAPTransaction::NoTransmit) removeTransaction(tr); TelEngine::destruct(tr); } return error; } void SS7TCAP::buildSCCPData(NamedList& params, SS7TCAPTransaction* tr) { if (!tr) return; DDebug(this,DebugAll,"SS7TCAP::buildSCCPData(tr=%p) for local transaction ID=%s [%p]",tr,tr->toString().c_str(),this); Lock l(tr); bool sendOk = true; int type = tr->transactionType(); if (type == SS7TCAP::TC_End || type == TC_Response) { if (!tr->basicEnd()) { sendOk = false; // prearranged end, don't send to remote Transaction End message Debug(this,DebugAll,"SS7TCAP::buildSCCPData(tr=%p) [%p] - transaction with id=%s has set prearranged end, won't be" " sending anything to SCCP",tr,this,tr->toString().c_str()); } } if (sendOk) { DataBlock data; tr->requestContent(params,data); tr->addSCCPAddressing(params,false); encodeTransactionPart(params,data); if (!sendData(data,params)) { params.setParam("ReturnCause","Network failure"); enqueue(new SS7TCAPMessage(params,data,true)); Debug(this,DebugInfo,"SS7TCAP::buildSCCPData(tr=%p) [%p] - message for transaction with id=%s failed to be sent", tr,this,tr->toString().c_str()); return; }; incCounter(SS7TCAP::OutgoingMsgs); } } HandledMSU SS7TCAP::handleError(SS7TCAPError& error, NamedList& params, DataBlock& data, SS7TCAPTransaction* tr) { Debug(this,DebugInfo,"SS7TCAP::handleError(error=%s) for transaction with id=%s(%p) [%p]",error.errorName().c_str(), (tr ? tr->toString().c_str() : "unknown"),tr,this); HandledMSU result = HandledMSU::Accepted; int type = lookup(params.getValue(s_tcapRequest,""),SS7TCAP::s_transPrimitives); NamedString* rtid = params.getParam(s_tcapRemoteTID); NamedString* ltid = params.getParam(s_tcapLocalTID); bool buildRemAbort = false; bool buildLocAbort = false; switch (type) { case SS7TCAP::TC_Unidirectional: incCounter(SS7TCAP::DiscardedMsgs); return result; // return with rejected, meaning Discarded case SS7TCAP::TC_Begin: case SS7TCAP::TC_QueryWithPerm: case SS7TCAP::TC_QueryWithoutPerm: if (!TelEngine::null(rtid)) buildRemAbort = true; else { // no originating ID, we don't know to whom to send the Abort, meaning we'll discard the message incCounter(SS7TCAP::DiscardedMsgs); return result; } break; case SS7TCAP::TC_Continue: case SS7TCAP::TC_ConversationWithPerm: case SS7TCAP::TC_ConversationWithoutPerm: if (TelEngine::null(rtid) && TelEngine::null(ltid)) { incCounter(SS7TCAP::DiscardedMsgs); return result; } if (!TelEngine::null(rtid)) { buildRemAbort = true; if (!TelEngine::null(ltid)) buildLocAbort = true; } break; case SS7TCAP::TC_End: case SS7TCAP::TC_Response: case SS7TCAP::TC_P_Abort: case SS7TCAP::TC_U_Abort: if (TelEngine::null(ltid)) { incCounter(SS7TCAP::DiscardedMsgs); return result; } else buildLocAbort = true; break; default: if (!TelEngine::null(rtid)) { buildRemAbort = true; if (!TelEngine::null(ltid)) buildLocAbort = true; } else { incCounter(SS7TCAP::DiscardedMsgs); return result; } break; } if (buildLocAbort && !TelEngine::null(ltid)) { // notify user of the abort params.setParam(s_tcapRequest,lookup(SS7TCAP::TC_P_Abort,SS7TCAP:: s_transPrimitives)); params.setParam(s_tcapAbortCause,"pAbort"); params.setParam(s_tcapAbortInfo,String(error.error())); if (tr) { tr->update(SS7TCAP::TC_P_Abort,params,false); tr->updateState(); } sendToUser(params); } if (buildRemAbort) { // clean dataBlock data.clear(); if (!TelEngine::null(rtid)) { // we have the remote ID, notify of abort NamedList addr(""); populateSCCPAddress(addr,addr,params,false,true); params.copyParams(addr); if (error.error() != SS7TCAPError::Dialog_Abnormal) { params.setParam(s_tcapRequest,lookup(SS7TCAP::TC_P_Abort,SS7TCAP::s_transPrimitives)); params.setParam(s_tcapAbortCause,"pAbort"); params.setParam(s_tcapAbortInfo,String(error.error())); } else if (tr) tr->abnormalDialogInfo(params); if (tcapType() == ANSITCAP) SS7TCAPTransactionANSI::encodePAbort(tr,params,data); else if (tcapType() == ITUTCAP) SS7TCAPTransactionITU::encodePAbort(tr,params,data); encodeTransactionPart(params,data); sendData(data,params); } } if (buildRemAbort || buildLocAbort) { incCounter(SS7TCAP::AbnormalMsgs); result = HandledMSU::Accepted; } return result; } /** * SS7TCAPError implementation */ struct TCAPError { SS7TCAPError::ErrorType errorType; u_int16_t errorCode; }; static const TCAPError s_ansiErrorDefs[] = { // error fullcode { SS7TCAPError::Transact_UnrecognizedPackageType, 0x01}, { SS7TCAPError::Transact_IncorrectTransactionPortion, 0x02}, { SS7TCAPError::Transact_BadlyStructuredTransaction, 0x03}, { SS7TCAPError::Transact_UnassignedTransactionID, 0x04}, { SS7TCAPError::Transact_PermissionToReleaseProblem, 0x05}, { SS7TCAPError::Transact_ResourceUnavailable, 0x06}, { SS7TCAPError::Dialog_UnrecognizedDialoguePortionID, 0x07}, { SS7TCAPError::Dialog_BadlyStructuredDialoguePortion, 0x08}, { SS7TCAPError::Dialog_MissingDialoguePortion, 0x09}, { SS7TCAPError::Dialog_InconsistentDialoguePortion, 0x0a}, // GeneralProblem { SS7TCAPError::General_UnrecognizedComponentType, 0x101}, { SS7TCAPError::General_IncorrectComponentPortion, 0x102}, { SS7TCAPError::General_BadlyStructuredCompPortion, 0x103}, { SS7TCAPError::General_IncorrectComponentCoding, 0x104}, // InvokeProblem { SS7TCAPError::Invoke_DuplicateInvokeID, 0x201}, { SS7TCAPError::Invoke_UnrecognizedOperationCode, 0x202}, { SS7TCAPError::Invoke_IncorrectParameter, 0x203}, { SS7TCAPError::Invoke_UnrecognizedCorrelationID, 0x204}, // ReturnResult { SS7TCAPError::Result_UnrecognisedCorrelationID, 0x301}, { SS7TCAPError::Result_UnexpectedReturnResult, 0x302}, { SS7TCAPError::Result_IncorrectParameter, 0x303}, // ReturnError { SS7TCAPError::Error_UnrecognisedCorrelationID, 0x401}, { SS7TCAPError::Error_UnexpectedReturnError, 0x402}, { SS7TCAPError::Error_UnrecognisedError, 0x403}, { SS7TCAPError::Error_UnexpectedError, 0x404}, { SS7TCAPError::Error_IncorrectParameter, 0x405}, { SS7TCAPError::NoError, 0xfff}, }; static const TCAPError s_ituErrorDefs[] = { // error fullcode { SS7TCAPError::Transact_UnrecognizedPackageType, 0x00}, { SS7TCAPError::Transact_UnassignedTransactionID, 0x01}, { SS7TCAPError::Transact_BadlyStructuredTransaction, 0x02}, { SS7TCAPError::Transact_IncorrectTransactionPortion, 0x03}, { SS7TCAPError::Transact_ResourceUnavailable, 0x04}, { SS7TCAPError::Dialog_Abnormal, 0x7000}, // GeneralProblem { SS7TCAPError::General_UnrecognizedComponentType, 0x8000}, { SS7TCAPError::General_IncorrectComponentPortion, 0x8001}, { SS7TCAPError::General_BadlyStructuredCompPortion, 0x8002}, // InvokeProblem { SS7TCAPError::Invoke_DuplicateInvokeID, 0x8100}, { SS7TCAPError::Invoke_UnrecognizedOperationCode, 0x8101}, { SS7TCAPError::Invoke_IncorrectParameter, 0x8102}, { SS7TCAPError::Invoke_UnrecognizedCorrelationID, 0x8105}, { SS7TCAPError::Invoke_ResourceLimitation, 0x8103}, { SS7TCAPError::Invoke_InitiatingRelease, 0x8104}, { SS7TCAPError::Invoke_LinkedResponseUnexpected, 0x8106}, { SS7TCAPError::Invoke_UnexpectedLinkedOperation, 0x8107}, // ReturnResult { SS7TCAPError::Result_UnrecognizedInvokeID, 0x8200}, { SS7TCAPError::Result_UnexpectedReturnResult, 0x8201}, { SS7TCAPError::Result_IncorrectParameter, 0x8202}, // ReturnError { SS7TCAPError::Error_UnrecognizedInvokeID, 0x8300}, { SS7TCAPError::Error_UnexpectedReturnError, 0x8301}, { SS7TCAPError::Error_UnrecognisedError, 0x8302}, { SS7TCAPError::Error_UnexpectedError, 0x8303}, { SS7TCAPError::Error_IncorrectParameter, 0x8304}, { SS7TCAPError::NoError, 0xffff}, }; const TokenDict SS7TCAPError::s_errorTypes[] = { {"Transact-UnrecognizedPackageType", SS7TCAPError::Transact_UnrecognizedPackageType}, {"Transact-IncorrectTransactionPortion", SS7TCAPError::Transact_IncorrectTransactionPortion}, {"Transact-BadlyStructuredTransaction", SS7TCAPError::Transact_BadlyStructuredTransaction}, {"Transact-UnassignedTransactionID", SS7TCAPError::Transact_UnassignedTransactionID }, {"Transact-PermissionToReleaseProblem", SS7TCAPError::Transact_PermissionToReleaseProblem}, {"Transact-ResourceUnavailable", SS7TCAPError::Transact_ResourceUnavailable}, {"Dialog-UnrecognizedDialoguePortionID", SS7TCAPError::Dialog_UnrecognizedDialoguePortionID}, {"Dialog-BadlyStructuredDialoguePortion", SS7TCAPError::Dialog_BadlyStructuredDialoguePortion}, {"Dialog-MissingDialoguePortion", SS7TCAPError::Dialog_MissingDialoguePortion}, {"Dialog-InconsistentDialoguePortion", SS7TCAPError::Dialog_InconsistentDialoguePortion}, {"Dialog-Abnormal", SS7TCAPError::Dialog_Abnormal}, {"General-UnrecognizedComponentType", SS7TCAPError::General_UnrecognizedComponentType}, {"General-IncorrectComponentPortion", SS7TCAPError::General_IncorrectComponentPortion}, {"General-BadlyStructuredCompPortion", SS7TCAPError::General_BadlyStructuredCompPortion}, {"General-IncorrectComponentCoding", SS7TCAPError::General_IncorrectComponentCoding}, {"Invoke-DuplicateInvokeID", SS7TCAPError::Invoke_DuplicateInvokeID}, {"Invoke-UnrecognizedOperationCode", SS7TCAPError::Invoke_UnrecognizedOperationCode}, {"Invoke-IncorrectParameter", SS7TCAPError::Invoke_IncorrectParameter}, {"Invoke-UnrecognizedCorrelationID", SS7TCAPError::Invoke_UnrecognizedCorrelationID}, {"Invoke-ResourceLimitation", SS7TCAPError::Invoke_ResourceLimitation}, {"Invoke-InitiatingRelease", SS7TCAPError::Invoke_InitiatingRelease}, {"Invoke-LinkedResponseUnexpected", SS7TCAPError::Invoke_LinkedResponseUnexpected}, {"Invoke-UnexpectedLinkedOperation", SS7TCAPError::Invoke_UnexpectedLinkedOperation}, {"Result-UnrecognizedInvokeID", SS7TCAPError::Result_UnrecognizedInvokeID}, {"Result-UnrecognisedCorrelationID", SS7TCAPError::Result_UnrecognisedCorrelationID}, {"Result-UnexpectedReturnResult", SS7TCAPError::Result_UnexpectedReturnResult}, {"Result-IncorrectParameter", SS7TCAPError::Result_IncorrectParameter}, {"Error-UnrecognizedInvokeID", SS7TCAPError::Error_UnrecognizedInvokeID}, {"Error-UnrecognisedCorrelationID", SS7TCAPError::Error_UnrecognisedCorrelationID}, {"Error-UnexpectedReturnError", SS7TCAPError::Error_UnexpectedReturnError}, {"Error-UnrecognisedError", SS7TCAPError::Error_UnrecognisedError}, {"Error-UnexpectedError", SS7TCAPError::Error_UnexpectedError}, {"Error-IncorrectParameter", SS7TCAPError::Error_IncorrectParameter}, {"NoError", SS7TCAPError::NoError}, {0,0}, }; SS7TCAPError::SS7TCAPError(SS7TCAP::TCAPType tcapType) : m_tcapType(tcapType), m_error(SS7TCAPError::NoError) { } SS7TCAPError::SS7TCAPError(SS7TCAP::TCAPType tcapType, ErrorType error) : m_tcapType(tcapType), m_error(error) { XDebug(DebugAll,"SS7TCAPError created TCAP=%s with error=%s [%p]",lookup(tcapType,SS7TCAP::s_tcapVersion), lookup(error,s_errorTypes),this); } SS7TCAPError::~SS7TCAPError() { } const String SS7TCAPError::errorName() { return lookup(m_error,s_errorTypes,"NoError"); } u_int16_t SS7TCAPError::errorCode() { const TCAPError* errDef = (m_tcapType == SS7TCAP::ANSITCAP ? s_ansiErrorDefs : s_ituErrorDefs); for (;errDef && errDef->errorType != SS7TCAPError::NoError; errDef++) { if (errDef->errorType == m_error) break; } return errDef->errorCode; } int SS7TCAPError::errorFromCode(SS7TCAP::TCAPType tcapType, u_int16_t code) { const TCAPError* errDef = (tcapType == SS7TCAP::ANSITCAP ? s_ansiErrorDefs : s_ituErrorDefs); for (;errDef && errDef->errorType != SS7TCAPError::NoError; errDef++) { if (errDef->errorCode == code) break; } return errDef->errorType; } u_int16_t SS7TCAPError::codeFromError(SS7TCAP::TCAPType tcapType, int err) { const TCAPError* errDef = (tcapType == SS7TCAP::ANSITCAP ? s_ansiErrorDefs : s_ituErrorDefs); for (;errDef && errDef->errorType != SS7TCAPError::NoError; errDef++) { if (errDef->errorType == err) break; } return errDef->errorCode; } /** * SS7TCAPTransaction */ SS7TCAPTransaction::SS7TCAPTransaction(SS7TCAP* tcap, SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params, u_int64_t timeout, bool initLocal) : Mutex(true,"TcapTransaction"), m_tcap(tcap), m_tcapType(SS7TCAP::UnknownTCAP), m_userName(""), m_localID(transactID), m_type(type), m_localSCCPAddr(""), m_remoteSCCPAddr(""), m_basicEnd(true), m_endNow(false), m_timeout(timeout) { DDebug(m_tcap,DebugAll,"SS7TCAPTransaction(tcap = '%s' [%p], transactID = %s) created [%p]", m_tcap->toString().c_str(),tcap,transactID.c_str(),this); m_remoteID = params.getValue(s_tcapRemoteTID); populateSCCPAddress(m_localSCCPAddr,m_remoteSCCPAddr,params,initLocal,false); m_endNow = params.getBoolValue(s_tcapEndNow,false); if (initLocal) setState(PackageSent); else setState(PackageReceived); } SS7TCAPTransaction::~SS7TCAPTransaction() { DDebug(tcap(),DebugAll,"Transaction with ID=%s of user=%s destroyed [%p]", m_localID.c_str(),m_userName.c_str(),this); m_components.clear(); m_tcap = 0; } SS7TCAPComponent* SS7TCAPTransaction::findComponent(const String& id) { SS7TCAPComponent* comp = 0; ObjList* o = m_components.find(id); if (o) comp = static_cast(o->get()); return comp; } SS7TCAPError SS7TCAPTransaction::update(SS7TCAP::TCAPUserTransActions type, NamedList& params, bool updateByUser) { DDebug(tcap(),DebugStub,"SS7TCAPTransaction::update() [%p], localID=%s - stub",this,m_localID.c_str()); SS7TCAPError error(m_tcapType); return error; } SS7TCAPError SS7TCAPTransaction::buildComponentError(SS7TCAPError& error, NamedList& params, DataBlock& data) { if (error.error() == SS7TCAPError::NoError) return error; Debug(tcap(),DebugInfo,"SS7TCAPTransaction::buildComponentError(error=%s) for transaction with id=%s [%p]",error.errorName().c_str(), toString().c_str(),this); int compCount = params.getIntValue(s_tcapCompCount,1); if (!compCount) return error; String param; compPrefix(param,compCount,true); bool buildRej = false; NamedString* typeStr = params.getParam(param + s_tcapCompType); if (TelEngine::null(typeStr)) buildRej = true; else { int type = typeStr->toInteger(SS7TCAP::s_compPrimitives); NamedString* invokeID = params.getParam(param + s_tcapRemoteCID); switch (type) { case SS7TCAP::TC_ResultLast: case SS7TCAP::TC_ResultNotLast: case SS7TCAP::TC_U_Error: if (!TelEngine::null(invokeID)) { SS7TCAPComponent* comp = findComponent(*invokeID); if (comp) m_components.remove(comp); } break; case SS7TCAP::TC_Invoke: case SS7TCAP::TC_R_Reject: default: break; } buildRej = true; } params.setParam(param + s_tcapCompType,lookup(SS7TCAP::TC_L_Reject,SS7TCAP::s_compPrimitives,"L_Reject")); params.setParam(param + s_tcapProblemCode,String(error.error())); if (buildRej) { SS7TCAPComponent* comp = SS7TCAPComponent::componentFromNamedList(m_tcapType,this,params,compCount); if (comp) m_components.append(comp); } return error; } SS7TCAPError SS7TCAPTransaction::handleComponents(NamedList& params, bool updateByUser) { XDebug(tcap(),DebugAll,"SS7TCAPTransaction::handleComponents(updateByUser=%s) [%p]",String::boolText(updateByUser),this); int count = params.getIntValue(s_tcapCompCount,0); SS7TCAPError error(m_tcapType); if (!count) return error; int index = 0; Lock l(this); while (index < count) { index++; String paramRoot; compPrefix(paramRoot,index,true); NamedString* localCID = params.getParam(paramRoot + s_tcapLocalCID); NamedString* typeStr = params.getParam(paramRoot + s_tcapCompType); if (TelEngine::null(typeStr)) continue; int type = typeStr->toInteger(SS7TCAP::s_compPrimitives); switch (type) { case SS7TCAP::TC_Invoke: case SS7TCAP::TC_InvokeNotLast: if (!updateByUser) { if (!TelEngine::null(localCID)) { // we have a linked/correlation ID, check the state of that component SS7TCAPComponent* linkedTo = findComponent(*localCID); if (!linkedTo) { type = SS7TCAP::TC_L_Reject; params.setParam(paramRoot + s_tcapProblemCode,String(SS7TCAPError::Invoke_UnrecognizedCorrelationID)); } else { if (linkedTo->state() != SS7TCAPComponent::OperationSent) { type = SS7TCAP::TC_L_Reject; params.setParam(paramRoot + s_tcapProblemCode,String(SS7TCAPError::Invoke_UnexpectedLinkedOperation)); } } } if (type == SS7TCAP::TC_L_Reject) { params.setParam(paramRoot + s_tcapCompType,lookup(type,SS7TCAP::s_compPrimitives,"L_Reject")); SS7TCAPComponent* comp = SS7TCAPComponent::componentFromNamedList(m_tcapType,this,params,index); if (comp) m_components.append(comp); } } else { if (!TelEngine::null(localCID)) { if (findComponent(*localCID)) { error.setError(SS7TCAPError::Invoke_DuplicateInvokeID); return error; } else { SS7TCAPComponent* comp = SS7TCAPComponent::componentFromNamedList(m_tcapType,this,params,index); if (comp) { m_components.append(comp); comp->setState(SS7TCAPComponent::OperationSent); } } } } break; case SS7TCAP::TC_ResultLast: case SS7TCAP::TC_ResultNotLast: case SS7TCAP::TC_U_Error: if (!updateByUser) { if (!TelEngine::null(localCID)) { SS7TCAPComponent* comp = findComponent(*localCID); if (comp) comp->update(params,index); else { params.setParam(paramRoot + s_tcapCompType,lookup(SS7TCAP::TC_L_Reject,SS7TCAP::s_compPrimitives,"L_Reject")); params.setParam(paramRoot + s_tcapProblemCode,String(SS7TCAPError::Invoke_UnexpectedLinkedOperation)); SS7TCAPComponent* comp = SS7TCAPComponent::componentFromNamedList(m_tcapType,this,params,index); if (comp) m_components.append(comp); } } } break; case SS7TCAP::TC_R_Reject: case SS7TCAP::TC_U_Reject: if (!updateByUser) { params.setParam(paramRoot + s_tcapCompType,lookup(SS7TCAP::TC_R_Reject,SS7TCAP::s_compPrimitives,"R_Reject")); if (!TelEngine::null(localCID)) { SS7TCAPComponent* comp = findComponent(*localCID); if (comp) m_components.remove(comp); } } else if (!TelEngine::null(localCID)) { m_components.remove(*localCID); } break; case SS7TCAP::TC_L_Reject: case SS7TCAP::TC_U_Cancel: if (updateByUser) { if (!TelEngine::null(localCID)) m_components.remove(*localCID); } break; case SS7TCAP::TC_TimerReset: if (updateByUser && !TelEngine::null(localCID) && m_tcapType == SS7TCAP::ITUTCAP) { SS7TCAPComponent* comp = findComponent(*localCID); if (comp) comp->resetTimer(params,index); } break; case SS7TCAP::TC_L_Cancel: default: break; } } DDebug(tcap(),DebugAll,"SS7TCAPTransaction::handleComponents() - transaction with localID=%s handled %d components [%p]", m_localID.c_str(),index,this); return error; } void SS7TCAPTransaction::requestComponents(NamedList& params, DataBlock& data) { Lock(this); unsigned int index = params.getIntValue(s_tcapCompCount); for (ObjList* o = m_components.skipNull(); o; o = o->skipNext()) { SS7TCAPComponent* comp = static_cast(o->get()); if (comp && comp->state() == SS7TCAPComponent::OperationPending) { index++; comp->fill(index,params); } } #ifdef DEBUG if (tcap() && s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"SS7TCAPTransaction::requestComponents() preparing to encode components:",this,params,data); #endif params.setParam(s_tcapCompCount,String(index)); encodeComponents(params,data); #ifdef DEBUG if (tcap() && s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"SS7TCAPTransaction::requestComponents() encoded components'",this,params,data); #endif } void SS7TCAPTransaction::transactionData(NamedList& params) { Lock l(this); params.setParam(s_tcapRequest,lookup(m_type,SS7TCAP::s_transPrimitives)); params.setParam(s_tcapLocalTID,m_localID); params.setParam(s_tcapRemoteTID,m_remoteID); #ifdef DEBUG if (s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"SS7TCAPTransaction::transactionData() - added transaction data",this,params); #endif } void SS7TCAPTransaction::checkComponents() { NamedList params(""); int index = 0; Lock l(this); ListIterator iter(m_components); for (;;) { SS7TCAPComponent* comp = static_cast(iter.get()); if (!comp) break; if (comp->timedOut()) { XDebug(tcap(),DebugInfo,"SS7TCAPTransaction::checkComponents() - component with local ID = %s timed out in" " transaction with local ID = %s [%p]",comp->toString().c_str(),toString().c_str(),this); SS7TCAP::TCAPUserCompActions type = comp->type(); String paramRoot = ""; switch (type) { case SS7TCAP::TC_Invoke: case SS7TCAP::TC_InvokeNotLast: if (comp->operationClass() != SS7TCAP::NoReport) { index++; comp->setType(SS7TCAP::TC_L_Cancel); comp->fill(index,params); } comp->setState(SS7TCAPComponent::Idle); break; case SS7TCAP::TC_ResultLast: case SS7TCAP::TC_U_Error: comp->setState(SS7TCAPComponent::Idle); break; case SS7TCAP::TC_ResultNotLast: case SS7TCAP::TC_U_Reject: case SS7TCAP::TC_L_Reject: case SS7TCAP::TC_R_Reject: case SS7TCAP::TC_L_Cancel: case SS7TCAP::TC_U_Cancel: case SS7TCAP::TC_TimerReset: default: break; } } if (comp->state() == SS7TCAPComponent::Idle) m_components.remove(comp); } if (params.count()) { params.setParam(s_tcapCompCount,String(index)); transactionData(params); params.clearParam(s_tcapRequest); tcap()->sendToUser(params); } if (m_components.count() == 0) {// we don't have any more components if (!m_timeout.started()) { m_timeout.start(); XDebug(tcap(),DebugInfo,"SS7TCAPTransaction::checkComponents() - timer for transaction with localID=%s has been started [%p]", toString().c_str(),this); } } } void SS7TCAPTransaction::setTransmitState(TransactionTransmit state) { Lock l(this); m_transmit = state; if (m_transmit == Transmitted) { switch (m_type) { case SS7TCAP::TC_Unidirectional: case SS7TCAP::TC_P_Abort: case SS7TCAP::TC_U_Abort: case SS7TCAP::TC_Response: case SS7TCAP::TC_End: m_state = Idle; break; case SS7TCAP::TC_Notice: case SS7TCAP::TC_Begin: case SS7TCAP::TC_QueryWithPerm: case SS7TCAP::TC_QueryWithoutPerm: case SS7TCAP::TC_Continue: case SS7TCAP::TC_ConversationWithPerm: case SS7TCAP::TC_ConversationWithoutPerm: default: break; } } } void SS7TCAPTransaction::addSCCPAddressing(NamedList& fillParams, bool local) { String localParam(local ? s_calledPA : s_callingPA); String remoteParam(local ? s_callingPA : s_calledPA); fillParams.clearParam(s_calledPA,'.'); fillParams.clearParam(s_callingPA,'.'); Lock l(this); fillParams.copyParam(m_localSCCPAddr,s_localPC); for (unsigned int i = 0; i < m_localSCCPAddr.count(); i++) { NamedString* ns = m_localSCCPAddr.getParam(i); if (ns && *ns && !(*ns).null()) { const String& name = ns->name(); if (name != s_localPC) fillParams.setParam(localParam + "." + name,*ns); } } fillParams.copyParam(m_remoteSCCPAddr,s_remotePC); for (unsigned int i = 0; i < m_remoteSCCPAddr.count(); i++) { NamedString* ns = m_remoteSCCPAddr.getParam(i); if (ns && *ns && !(*ns).null()) { const String& name = ns->name(); if (name != s_remotePC) fillParams.setParam(remoteParam + "." + name,*ns); } } } SS7TCAPError SS7TCAPTransaction::handleData(NamedList& params, DataBlock& data) { DDebug(tcap(),DebugAll,"SS7TCAPTransaction::handleData() transactionID=%s data length=%u [%p]",m_localID.c_str(), data.length(),this); Lock lock(this); // in case of Abort message, check Cause Information SS7TCAPError error(m_tcapType); return error; } void SS7TCAPTransaction::updateToEnd() { } void SS7TCAPTransaction::abnormalDialogInfo(NamedList& params) { Debug(tcap(),DebugAll,"SS7TCAPTransaction::abnormalDialogInfo() [%p]",this); } /** * SS7TCAPComponent */ const TokenDict SS7TCAPComponent::s_compStates[] = { {"Idle", SS7TCAPComponent::Idle}, {"OperationPending", SS7TCAPComponent::OperationPending}, {"OperationSent", SS7TCAPComponent::OperationSent}, {"WaitForReject", SS7TCAPComponent::WaitForReject}, }; SS7TCAPComponent::SS7TCAPComponent(SS7TCAP::TCAPType type, SS7TCAPTransaction* trans, NamedList& params, unsigned int index) : m_transact(trans), m_state(Idle), m_id(""), m_corrID(""), m_opClass(SS7TCAP::SuccessOrFailureReport), m_opTimer(0), m_error(type) { String paramRoot; compPrefix(paramRoot,index,true); m_type = (SS7TCAP::TCAPUserCompActions) lookup(params.getValue(paramRoot + s_tcapCompType),SS7TCAP::s_compPrimitives); m_id = params.getValue(paramRoot + s_tcapLocalCID); m_corrID = params.getValue(paramRoot + s_tcapRemoteCID); setState(OperationPending); m_opType = params.getValue(paramRoot + s_tcapOpCodeType,""); m_opCode = params.getValue(paramRoot + s_tcapOpCode,""); NamedString* opClass = params.getParam(paramRoot + "operationClass"); if (!TelEngine::null(opClass)) m_opClass = (SS7TCAP::TCAPComponentOperationClass) opClass->toInteger(SS7TCAP::s_compOperClasses,SS7TCAP::SuccessOrFailureReport); m_opTimer.interval(params.getIntValue(paramRoot + "timeout",5) * 1000); m_error.setError((SS7TCAPError::ErrorType)params.getIntValue(paramRoot + s_tcapProblemCode)); DDebug(m_transact->tcap(),DebugAll,"SS7TCAPComponent() [%p] created for transaction='%s' [%p] with localID=%s, remoteID=%s," " type=%s, class=%s",this, (m_transact ? m_transact->toString().c_str() :""),m_transact,m_id.c_str(), m_corrID.c_str(),lookup(m_type,SS7TCAP::s_compPrimitives),lookup(m_opClass,SS7TCAP::s_compOperClasses)); } SS7TCAPComponent::~SS7TCAPComponent() { DDebug(m_transact->tcap(),DebugAll,"SS7TCAPComponent::~SS7TCAPComponent() - component [%p] destroyed",this); m_transact = 0; } void SS7TCAPComponent::update(NamedList& params, unsigned int index) { String paramRoot; compPrefix(paramRoot,index,false); DDebug(m_transact->tcap(),DebugAll,"SS7TCAPComponent::update() - update component with localID=%s [%p]",m_id.c_str(),this); m_type = (SS7TCAP::TCAPUserCompActions) lookup(params.getValue(paramRoot + "." + s_tcapCompType),SS7TCAP::s_compPrimitives); switch (m_type) { case SS7TCAP::TC_ResultLast: if (m_opClass == SS7TCAP::SuccessOrFailureReport || m_opClass == SS7TCAP::SuccessOnlyReport) setState(WaitForReject); else if (m_opClass == SS7TCAP::FailureOnlyReport || m_opClass == SS7TCAP::NoReport) { // build reject component m_type = SS7TCAP::TC_L_Reject; params.setParam(paramRoot + "." + s_tcapCompType,lookup(SS7TCAP::TC_L_Reject,SS7TCAP::s_compPrimitives)); params.setParam(paramRoot + "." + s_tcapProblemCode,String(SS7TCAPError::Result_UnexpectedReturnResult)); m_error.setError(SS7TCAPError::Result_UnexpectedReturnResult); setState(OperationPending); return; } break; case SS7TCAP::TC_ResultNotLast: if (m_opClass == SS7TCAP::FailureOnlyReport || m_opClass == SS7TCAP::NoReport) { // build reject component m_type = SS7TCAP::TC_L_Reject; params.setParam(paramRoot + "." + s_tcapCompType,lookup(SS7TCAP::TC_L_Reject,SS7TCAP::s_compPrimitives)); params.setParam(paramRoot + "." + s_tcapProblemCode,String(SS7TCAPError::Result_UnexpectedReturnResult)); m_error.setError(SS7TCAPError::Result_UnexpectedReturnResult); setState(OperationPending); return; } else if (m_opClass == SS7TCAP::SuccessOnlyReport) setState(WaitForReject); break; case SS7TCAP::TC_U_Error: if (m_opClass == SS7TCAP::FailureOnlyReport) setState(WaitForReject); else if (m_opClass == SS7TCAP::SuccessOnlyReport || m_opClass == SS7TCAP::NoReport) { m_type = SS7TCAP::TC_L_Reject; params.setParam(paramRoot + "." + s_tcapCompType,lookup(SS7TCAP::TC_L_Reject,SS7TCAP::s_compPrimitives)); params.setParam(paramRoot + "." + s_tcapProblemCode,String(SS7TCAPError::Error_UnexpectedReturnError)); m_error.setError(SS7TCAPError::Error_UnexpectedReturnError); setState(OperationPending); return; } break; case SS7TCAP::TC_TimerReset: default: break; } if (TelEngine::null(params.getParam(paramRoot + "." + s_tcapOpCode))) { params.setParam(paramRoot + "." + s_tcapOpCode,m_opCode); params.setParam(paramRoot + "." + s_tcapOpCodeType,m_opType); } } SS7TCAPComponent* SS7TCAPComponent::componentFromNamedList(SS7TCAP::TCAPType tcapType, SS7TCAPTransaction* tr, NamedList& params, unsigned int index) { if (!tr) return 0; String paramRoot; compPrefix(paramRoot,index,true); NamedString* str = params.getParam(paramRoot + s_tcapLocalCID); if (TelEngine::null(str)) str = params.getParam(paramRoot + s_tcapRemoteCID); if (TelEngine::null(str)) return 0; int type = lookup(params.getValue(paramRoot + s_tcapCompType),SS7TCAP::s_compPrimitives); // we allow building Reject components that have been built by Component layer or Invokes requested by local user if (type != SS7TCAP::TC_Invoke && type != SS7TCAP::TC_InvokeNotLast && type != SS7TCAP::TC_L_Reject && type != SS7TCAP::TC_U_Reject && type != SS7TCAP::TC_R_Reject) return 0; SS7TCAPComponent* comp = new SS7TCAPComponent(tcapType,tr,params,index); return comp; } void SS7TCAPComponent::setState(TCAPComponentState state) { #ifdef DEBUG if (m_transact && m_transact->tcap() && s_extendedDbg) DDebug(m_transact->tcap(),DebugAll,"SS7TCAPComponent::setState(%s), locaID=%s remoteID=%s [%p]",lookup(state,s_compStates), m_id.c_str(),m_corrID.c_str(),this); #endif m_state = state; m_opTimer.stop(); if (!(state == Idle || state == OperationPending)) m_opTimer.start(); } void SS7TCAPComponent::fill(unsigned int index, NamedList& fillIn) { #ifdef DEBUG if (m_transact && m_transact->tcap() && s_printMsgs && s_extendedDbg && debugAt(DebugAll)) DDebug(m_transact->tcap(),DebugAll,"SS7TCAPComponent::fill() - component with localID=%s,remoteID=%s of transaction=%s " "fill for index=%u [%p]",m_id.c_str(),m_corrID.c_str(),m_transact->toString().c_str(),index,this); #endif String paramRoot; compPrefix(paramRoot,index,true); fillIn.setParam(paramRoot + s_tcapLocalCID,m_id); fillIn.setParam(paramRoot + s_tcapRemoteCID,m_corrID); fillIn.setParam(paramRoot + s_tcapCompType,lookup(m_type,SS7TCAP::s_compPrimitives,"Unknown")); if (m_error.error() != SS7TCAPError::NoError) { if (m_type == SS7TCAP::TC_U_Error) fillIn.setParam(paramRoot + s_tcapErrCode,String(m_error.error())); else if (m_type == SS7TCAP::TC_L_Reject || m_type == SS7TCAP::TC_U_Reject || m_type == SS7TCAP::TC_R_Reject) fillIn.setParam(paramRoot + s_tcapProblemCode,String(m_error.error())); } if (m_type == SS7TCAP::TC_L_Cancel) { fillIn.setParam(paramRoot + s_tcapOpCode,m_opCode); fillIn.setParam(paramRoot + s_tcapOpCodeType,m_opType); } if (m_type == SS7TCAP::TC_U_Reject || m_type == SS7TCAP::TC_R_Reject || m_type == SS7TCAP::TC_L_Reject) setState(Idle); } void SS7TCAPComponent::resetTimer(NamedList& params, unsigned int index) { DDebug(m_transact->tcap(),DebugInfo,"SS7TCAPComponent::resetTimer() [%p]",this); String paramRoot; compPrefix(paramRoot,index,false); if (state() == OperationSent) m_opTimer.start(); params.clearParam(paramRoot,'.'); } // class SS7TCAPANSI static u_int8_t s_tcapProtoVersion = 0x04; static const PrimitiveMapping s_componentsANSIMap[] = { {SS7TCAP::TC_Invoke, SS7TCAPTransactionANSI::InvokeLast}, {SS7TCAP::TC_ResultLast, SS7TCAPTransactionANSI::ReturnResultLast}, {SS7TCAP::TC_U_Error, SS7TCAPTransactionANSI::ReturnError}, {SS7TCAP::TC_U_Reject, SS7TCAPTransactionANSI::Reject}, {SS7TCAP::TC_R_Reject, SS7TCAPTransactionANSI::Reject}, {SS7TCAP::TC_L_Reject, SS7TCAPTransactionANSI::Reject}, {SS7TCAP::TC_InvokeNotLast, SS7TCAPTransactionANSI::InvokeNotLast}, {SS7TCAP::TC_ResultNotLast, SS7TCAPTransactionANSI::ReturnResultNotLast}, {SS7TCAP::TC_L_Cancel, SS7TCAPTransactionANSI::Local}, {SS7TCAP::TC_U_Cancel, SS7TCAPTransactionANSI::Local}, {SS7TCAP::TC_TimerReset, SS7TCAPTransactionANSI::Local}, }; static const PrimitiveMapping s_transANSIMap[] = { {SS7TCAP::TC_Unidirectional, SS7TCAPTransactionANSI::Unidirectional}, {SS7TCAP::TC_QueryWithPerm, SS7TCAPTransactionANSI::QueryWithPermission}, {SS7TCAP::TC_QueryWithoutPerm, SS7TCAPTransactionANSI::QueryWithoutPermission}, {SS7TCAP::TC_Begin, SS7TCAPTransactionANSI::QueryWithPermission}, // on receiving a ITU_T Begin, we'll map it to ANSI QueryWithPermission {SS7TCAP::TC_ConversationWithPerm, SS7TCAPTransactionANSI::ConversationWithPermission}, {SS7TCAP::TC_ConversationWithoutPerm, SS7TCAPTransactionANSI::ConversationWithoutPermission}, {SS7TCAP::TC_Continue, SS7TCAPTransactionANSI::ConversationWithPermission}, {SS7TCAP::TC_Response, SS7TCAPTransactionANSI::Response}, {SS7TCAP::TC_End, SS7TCAPTransactionANSI::Response}, {SS7TCAP::TC_U_Abort, SS7TCAPTransactionANSI::Abort}, {SS7TCAP::TC_P_Abort, SS7TCAPTransactionANSI::Abort}, {SS7TCAP::TC_Notice, SS7TCAPTransactionANSI::Unknown}, {SS7TCAP::TC_Unknown, SS7TCAPTransactionANSI::Unknown}, }; static const PrimitiveMapping* mapCompPrimitivesANSI(int primitive, int comp = -1) { const PrimitiveMapping* map = s_componentsANSIMap; for (; map->primitive != SS7TCAP::TC_Unknown; map++) { if (primitive != -1) { if (map->primitive == primitive ) break; } else if (comp != -1) if (map->mappedTo == comp) break; } return map; } static const PrimitiveMapping* mapTransPrimitivesANSI(int primitive, int trans = -1) { const PrimitiveMapping* map = s_transANSIMap; for (; map->primitive != SS7TCAP::TC_Unknown; map++) { if (primitive != -1) { if (map->primitive == primitive ) break; } else if (trans != -1) if (map->mappedTo == trans) break; } return map; } static const SS7TCAPTransactionANSI::ANSITransactionType primitiveToTransactANSI(String primitive, SS7TCAP::TCAPUserTransActions primitiveType = SS7TCAP::TC_Unknown) { SS7TCAPTransactionANSI::ANSITransactionType type = SS7TCAPTransactionANSI::Unknown; if (!primitive.null()) primitiveType = (SS7TCAP::TCAPUserTransActions)primitive.toInteger(SS7TCAP::s_transPrimitives); const PrimitiveMapping* map = mapTransPrimitivesANSI(primitiveType); if (map) type = (SS7TCAPTransactionANSI::ANSITransactionType)map->mappedTo; return type; } SS7TCAPANSI::SS7TCAPANSI(const NamedList& params) : SignallingComponent(params.safe("SS7TCAPANSI"),¶ms,"ss7-tcap-ansi"), SS7TCAP(params) { String tmp; params.dump(tmp,"\r\n ",'\'',true); DDebug(this,DebugAll,"SS7TCAPANSI::SS7TCAPANSI(%s)",tmp.c_str()); setTCAPType(SS7TCAP::ANSITCAP); } SS7TCAPANSI::~SS7TCAPANSI() { DDebug(this,DebugAll,"SS7TCAPANSI::~SS7TCAPANSI() [%p] destroyed with %d transactions, refCount=%d", this,m_transactions.count(),refcount()); } SS7TCAPTransaction* SS7TCAPANSI::buildTransaction(SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params, bool initLocal) { return new SS7TCAPTransactionANSI(this,type,transactID,params,m_trTimeout,initLocal); } SS7TCAPError SS7TCAPANSI::decodeTransactionPart(NamedList& params, DataBlock& data) { SS7TCAPError error(SS7TCAP::ANSITCAP); if (data.length() < 2) // should find out which is the minimal TCAP message length return error; // decode message type u_int8_t msgType = data[0]; data.cut(-1); const PrimitiveMapping* map = mapTransPrimitivesANSI(-1,msgType); if (map) { String type = lookup(map->primitive,SS7TCAP::s_transPrimitives,"Unknown"); params.setParam(s_tcapRequest,type); } // decode message length unsigned int len = ASNLib::decodeLength(data); if (len != data.length()) return error; // decode transaction IDs, start with Transaction Identifier u_int8_t tag = data[0]; if (tag != TransactionIDTag) {// 0xc7 error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); return error; // check it } data.cut(-1); // if we'll detect an error, it should be a BadlyStructuredTransaction error error.setError(SS7TCAPError::Transact_BadlyStructuredTransaction); len = ASNLib::decodeLength(data); if (len > data.length() || data.length() < len || (len != 0 && len != 4 && len != 8)) return error; // transaction IDs shall be decoded according to message type String tid1, tid2; if (len > 0 ) { tid1.hexify(data.data(),4,' '); data.cut(-4); if (len == 8) { tid2.hexify(data.data(),4,' '); data.cut(-4); } } switch (msgType) { case SS7TCAPTransactionANSI::Unidirectional: if (len != 0) return error; break; case SS7TCAPTransactionANSI::QueryWithPermission: case SS7TCAPTransactionANSI::QueryWithoutPermission: if (len != 4) return error; params.setParam(s_tcapRemoteTID,tid1); break; case SS7TCAPTransactionANSI::Response: case SS7TCAPTransactionANSI::Abort: if (len != 4) return error; params.setParam(s_tcapLocalTID,tid1); break; case SS7TCAPTransactionANSI::ConversationWithPermission: case SS7TCAPTransactionANSI::ConversationWithoutPermission: if (len != 8) return error; params.setParam(s_tcapRemoteTID,tid1); params.setParam(s_tcapLocalTID,tid2); break; default: error.setError(SS7TCAPError::Transact_UnrecognizedPackageType); return error; }; #ifdef DEBUG if (s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,this,"SS7TCAPANSI::decodeTransactionPart() finished",this,params,data); #endif error.setError(SS7TCAPError::NoError); return error; } void SS7TCAPANSI::encodeTransactionPart(NamedList& params, DataBlock& data) { #ifdef DEBUG if (s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,this,"SS7TCAPANSI::encodeTransactionPart() - to be encoded",this,params,data); #endif int msgType = primitiveToTransactANSI(params.getValue(s_tcapRequest)); const String& otid = params[s_tcapLocalTID]; const String& dtid = params[s_tcapRemoteTID]; String ids; switch (msgType) { case SS7TCAPTransactionANSI::Unidirectional: break; case SS7TCAPTransactionANSI::QueryWithPermission: case SS7TCAPTransactionANSI::QueryWithoutPermission: ids = otid; break; case SS7TCAPTransactionANSI::Response: case SS7TCAPTransactionANSI::Abort: ids = dtid; break; case SS7TCAPTransactionANSI::ConversationWithPermission: case SS7TCAPTransactionANSI::ConversationWithoutPermission: ids << otid << " " << dtid; break; default: break; }; DataBlock db; db.unHexify(ids.c_str(),ids.length(),' '); db.insert(ASNLib::buildLength(db)); int tag = TransactionIDTag; db.insert(DataBlock(&tag,1)); data.insert(db); data.insert(ASNLib::buildLength(data)); data.insert(DataBlock(&msgType,1)); } /** * SS7TCAPTransactionANSI implementation */ const TokenDict SS7TCAPTransactionANSI::s_ansiTransactTypes[] = { {"Unidirectional", SS7TCAPTransactionANSI::Unidirectional}, {"QueryWithPermission", SS7TCAPTransactionANSI::QueryWithPermission}, {"QueryWithoutPermission", SS7TCAPTransactionANSI::QueryWithoutPermission}, {"Response", SS7TCAPTransactionANSI::Response}, {"ConversationWithPermission", SS7TCAPTransactionANSI::ConversationWithPermission}, {"ConversationWithoutPermission", SS7TCAPTransactionANSI::ConversationWithoutPermission}, {"Abort", SS7TCAPTransactionANSI::Abort}, {0,0}, }; SS7TCAPTransactionANSI::SS7TCAPTransactionANSI(SS7TCAP* tcap, SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params, u_int64_t timeout, bool initLocal) : SS7TCAPTransaction(tcap,type,transactID,params,timeout,initLocal), m_prevType(type) { DDebug(tcap,DebugAll,"SS7TCAPTransactionANSI[%p] created with type='%s' and localID='%s'",this, lookup(type,SS7TCAP::s_transPrimitives),m_localID.c_str()); } SS7TCAPTransactionANSI::~SS7TCAPTransactionANSI() { DDebug(tcap(),DebugAll,"Transaction with ID=%s of user=%s destroyed, TCAP refcount=%d [%p]", m_localID.c_str(),m_userName.c_str(),tcap()->refcount(),this); } SS7TCAPError SS7TCAPTransactionANSI::handleData(NamedList& params, DataBlock& data) { XDebug(tcap(),DebugAll,"SS7TCAPTransactionANSI::handleData() transactionID=%s data length=%u [%p]",m_localID.c_str(), data.length(),this); Lock lock(this); // decode DialogPortion SS7TCAPError error = decodeDialogPortion(params,data); if (error.error() != SS7TCAPError::NoError) return error; error = handleDialogPortion(params,false); if (error.error() != SS7TCAPError::NoError) return error; // in case of Abort message, check Cause Information String msg = params.getValue(s_tcapMsgType); if (msg.toInteger(s_ansiTransactTypes) == Abort) { error = decodePAbort(this,params,data); if (error.error() != SS7TCAPError::NoError) return error; } // decodeComponents error = decodeComponents(params,data); if (error.error() != SS7TCAPError::NoError) buildComponentError(error,params,data); error = handleComponents(params,false); return error; } SS7TCAPError SS7TCAPTransactionANSI::update(SS7TCAP::TCAPUserTransActions type, NamedList& params, bool updateByUser) { DDebug(tcap(),DebugAll,"SS7TCAPTransactionANSI::update() [%p], localID=%s - update to type=%s initiated by %s",this,m_localID.c_str(), lookup(type,SS7TCAP::s_transPrimitives,"Unknown"), (updateByUser ? "user" : "remote")); #ifdef DEBUG if (s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"SS7TCAPTransactionANSI::update() with",this,params); #endif Lock l(this); SS7TCAPError error(SS7TCAP::ANSITCAP); switch (type) { case SS7TCAP::TC_Begin: case SS7TCAP::TC_QueryWithPerm: case SS7TCAP::TC_QueryWithoutPerm: case SS7TCAP::TC_Unidirectional: Debug(tcap(),DebugInfo,"SS7TCAPTransactionANSI::update() [%p], localID=%s - invalid update: trying to update from type=%s to type=%s", this,m_localID.c_str(),lookup(m_type,SS7TCAP::s_transPrimitives,"Unknown"), lookup(type,SS7TCAP::s_transPrimitives,"Unknown")); params.setParam(s_tcapRequestError,"invalid_update"); params.setParam("tcap.request.error.currentState",lookup(m_type,SS7TCAP::s_transPrimitives,"Unknown")); error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); return error; case SS7TCAP::TC_End: case SS7TCAP::TC_Response: if (m_type == SS7TCAP::TC_QueryWithoutPerm || m_type == SS7TCAP::TC_ConversationWithoutPerm) { params.setParam(s_tcapRequestError,"invalid_update"); params.setParam("tcap.request.error.currentState",lookup(m_type,SS7TCAP::s_transPrimitives,"Unknown")); error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); return error; } else { if (!m_basicEnd) // prearranged end, no need to transmit to remote end m_transmit = NoTransmit; else m_transmit = PendingTransmit; m_type = type; } break; case SS7TCAP::TC_Continue: case SS7TCAP::TC_ConversationWithPerm: case SS7TCAP::TC_ConversationWithoutPerm: if (m_type == SS7TCAP::TC_End || m_type == SS7TCAP::TC_Response) { params.setParam(s_tcapRequestError,"invalid_update"); params.setParam("tcap.request.error.currentState",lookup(m_type,SS7TCAP::s_transPrimitives,"Unknown")); error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); return error; } else { m_remoteID = params.getValue(s_tcapRemoteTID); m_type = type; m_transmit = PendingTransmit; } break; case SS7TCAP::TC_Notice: case SS7TCAP::TC_P_Abort: if (updateByUser) { Debug(tcap(),DebugInfo,"SS7TCAPTransactionANSI::update() [%p], localID=%s - invalid update: trying to update from type=%s to type=%s", this,m_localID.c_str(),lookup(m_type,SS7TCAP::s_transPrimitives,"Unknown"), lookup(type,SS7TCAP::s_transPrimitives,"Unknown")); params.setParam(s_tcapRequestError,"invalid_update"); params.setParam("tcap.request.error.currentState",lookup(m_type,SS7TCAP::s_transPrimitives,"Unknown")); error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); return error; } case SS7TCAP::TC_U_Abort: if (!updateByUser && String("pAbort") == params.getValue(s_tcapAbortCause)) m_type = SS7TCAP::TC_P_Abort; else m_type = type; m_transmit = PendingTransmit; break; default: break; } populateSCCPAddress(m_localSCCPAddr,m_remoteSCCPAddr,params,updateByUser); if (updateByUser) { setState(PackageSent); m_basicEnd = params.getBoolValue(s_tcapBasicTerm,true); m_endNow = params.getBoolValue(s_tcapEndNow,false); } else setState(PackageReceived); if (m_timeout.started()) { m_timeout.stop(); XDebug(tcap(),DebugInfo,"SS7TCAPTransactionANSI::update() [%p], localID=%s - timeout timer has been stopped",this,m_localID.c_str()); } return error; } SS7TCAPError SS7TCAPTransactionANSI::decodeDialogPortion(NamedList& params, DataBlock& data) { XDebug(tcap(),DebugAll,"SS7TCAPTransactionANSI::decodeDialogPortion() for transaction with localID=%s [%p]", m_localID.c_str(),this); SS7TCAPError error(SS7TCAP::ANSITCAP); u_int8_t tag = data[0]; // dialog is not present if (tag != SS7TCAPANSI::DialogPortionTag) // 0xf9 return error; data.cut(-1); // dialog portion is present, decode dialog length int len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } tag = data[0]; // check for protocol version if (data[0] == SS7TCAPANSI::ProtocolVersionTag) { //0xda data.cut(-1); // decode protocol version u_int8_t proto; len = ASNLib::decodeUINT8(data,&proto,false); if (len != 1) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapProtoVers,String(proto)); } tag = data[0]; // check for Application Context if (tag == SS7TCAPANSI::IntApplicationContextTag || tag == SS7TCAPANSI::OIDApplicationContextTag) { // 0xdb , 0xdc data.cut(-1); if (tag == SS7TCAPANSI::IntApplicationContextTag) { //0xdb u_int64_t val = 0; len = ASNLib::decodeInteger(data,val,sizeof(int),false); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapIntAppID,String((int)val)); } if (tag == SS7TCAPANSI::OIDApplicationContextTag) { // oxdc ASNObjId oid; len = ASNLib::decodeOID(data,&oid,false); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapObjAppID,oid.toString()); } } // check for user information tag = data[0]; if (tag == SS7TCAPANSI::UserInformationTag) {// 0xfd data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } tag = data[0]; if (tag != SS7TCAPANSI::ExternalTag) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } // direct Reference tag = data[0]; if (tag == SS7TCAPANSI::DirectReferenceTag) { // 0x06 data.cut(-1); ASNObjId oid; len = ASNLib::decodeOID(data,&oid,false); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapReference,oid.toString()); } // data Descriptor tag = data[0]; if (tag == SS7TCAPANSI::DataDescriptorTag) { // 0x07 data.cut(-1); String str; int type; len = ASNLib::decodeString(data,&str,&type,false); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapDataDesc,str); } // encoding tag = data[0]; if (tag == SS7TCAPANSI::SingleASNTypePEncTag || tag == SS7TCAPANSI::SingleASNTypeCEncTag || tag == SS7TCAPANSI::OctetAlignEncTag || tag == SS7TCAPANSI::ArbitraryEncTag) { data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } DataBlock d((void*)data.data(0,len),len); data.cut(-len); // put encoding context in hexified form String dataHexified; dataHexified.hexify(d.data(),d.length(),' '); params.setParam(s_tcapEncodingContent,dataHexified); // put encoding identifier switch (tag) { case SS7TCAPANSI::SingleASNTypePEncTag: // 0x80 params.setParam(s_tcapEncodingType,"single-ASN1-type-primitive"); break; case SS7TCAPANSI::SingleASNTypeCEncTag: // 0xa0 params.setParam(s_tcapEncodingType,"single-ASN1-type-contructor"); break; case SS7TCAPANSI::OctetAlignEncTag: // 0x81 params.setParam(s_tcapEncodingType,"octet-aligned"); break; case SS7TCAPANSI::ArbitraryEncTag: // 0x82 params.setParam(s_tcapEncodingType,"arbitrary"); break; default: break; } } } // check for security context tag = data[0]; if (tag == SS7TCAPANSI::IntSecurityContextTag || tag == SS7TCAPANSI::OIDSecurityContextTag) { data.cut(-1); if (tag == SS7TCAPANSI::IntSecurityContextTag) { //0x80 int val = 0; len = ASNLib::decodeINT32(data,&val,false); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapIntSecID,String(val)); } if (tag == SS7TCAPANSI::OIDSecurityContextTag) { // 0x81 ASNObjId oid; len = ASNLib::decodeOID(data,&oid,false); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapObjSecID,oid.toString()); } } // check for Confidentiality information tag = data[0]; if (tag == SS7TCAPANSI::ConfidentialityTag) { // 0xa2 data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } tag = data[0]; if (tag == SS7TCAPANSI::IntSecurityContextTag || tag == SS7TCAPANSI::OIDSecurityContextTag) { data.cut(-1); if (tag == SS7TCAPANSI::IntSecurityContextTag) { //0x80 int val = 0; len = ASNLib::decodeINT32(data,&val,false); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapIntConfidID,String(val)); } if (tag == SS7TCAPANSI::OIDSecurityContextTag) { // 0x81 ASNObjId oid; len = ASNLib::decodeOID(data,&oid,false); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapObjConfidID,oid.toString()); } } } #ifdef DEBUG if (s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"SS7TCAPTransactionANSI::decodeDialogPortion() - decoded dialog portion",this,params,data); #endif return error; } void SS7TCAPTransactionANSI::encodeDialogPortion(NamedList& params, DataBlock& data) { XDebug(tcap(),DebugAll,"SS7TCAPTransactionANSI::encodeDialogPortion() for transaction with localID=%s [%p]",m_localID.c_str(),this); DataBlock dialogData; int tag; // encode confidentiality information NamedString* val = params.getParam(s_tcapIntConfidID); NamedString* oidStr = params.getParam(s_tcapObjConfidID); ASNObjId oid; if (!TelEngine::null(val) && !TelEngine::null(oidStr)) { // parameter error, encoding of this portion skipped Debug(tcap(),DebugInfo,"SS7TCAPTransactionANSI::encodeDialogPortion() - skipping encoding of Confidentiality Information," " both IntegerConfidentialityAlgorithmID=%s and ObjectIDConfidentialityID=%s specified, can't pick one", val->c_str(),oidStr->c_str()); } else { if (!TelEngine::null(val)) { DataBlock db = ASNLib::encodeInteger(val->toInteger(),false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPANSI::IntSecurityContextTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } else if (!TelEngine::null(oidStr)) { oid = *oidStr; DataBlock db = ASNLib::encodeOID(oid,false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPANSI::OIDSecurityContextTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } if (dialogData.length()) { dialogData.insert(ASNLib::buildLength(dialogData)); tag = SS7TCAPANSI::ConfidentialityTag; dialogData.insert(DataBlock(&tag,1)); } } // encode security information val = params.getParam(s_tcapIntSecID); oidStr = params.getParam(s_tcapObjSecID); if (!TelEngine::null(val) && !TelEngine::null(oidStr)) { // parameter error, encoding of this portion skipped Debug(tcap(),DebugInfo,"SS7TCAPTransactionANSI::encodeDialogPortion() - skipping encoding of Security Context Information," " both IntegerSecurityContext=%s and ObjectIDSecurityContext=%s specified, can't pick one", val->c_str(),oid.toString().c_str()); } else if (!TelEngine::null(val)) { DataBlock db = ASNLib::encodeInteger(val->toInteger(),false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPANSI::IntSecurityContextTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } else if (!TelEngine::null(oidStr)) { oid = *oidStr; DataBlock db = ASNLib::encodeOID(oid,false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPANSI::OIDSecurityContextTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } // encode user information DataBlock userInfo; val = params.getParam(s_tcapEncodingType); if (!TelEngine::null(val)) { if (*val == "single-ASN1-type-primitive") tag = SS7TCAPANSI::SingleASNTypePEncTag; else if (*val == "single-ASN1-type-contructor") tag = SS7TCAPANSI::SingleASNTypeCEncTag; else if (*val == "octet-aligned") tag = SS7TCAPANSI::OctetAlignEncTag; else if (*val == "arbitrary") tag = SS7TCAPANSI::ArbitraryEncTag; val = params.getParam(s_tcapEncodingContent); if (val) { DataBlock db; db.unHexify(val->c_str(),val->length(),' '); db.insert(ASNLib::buildLength(db)); db.insert(DataBlock(&tag,1)); userInfo.insert(db); } } val = params.getParam(s_tcapDataDesc); if (!TelEngine::null(val)) { DataBlock db = ASNLib::encodeString(*val,ASNLib::PRINTABLE_STR,false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPANSI::DataDescriptorTag; db.insert(DataBlock(&tag,1)); userInfo.insert(db); } val = params.getParam(s_tcapReference); if (!TelEngine::null(val)) { oid = *val; DataBlock db = ASNLib::encodeOID(oid,false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPANSI::DirectReferenceTag; db.insert(DataBlock(&tag,1)); userInfo.insert(db); } if (userInfo.length()) { userInfo.insert(ASNLib::buildLength(userInfo)); tag = SS7TCAPANSI::ExternalTag; userInfo.insert(DataBlock(&tag,1)); userInfo.insert(ASNLib::buildLength(userInfo)); tag = SS7TCAPANSI::UserInformationTag; userInfo.insert(DataBlock(&tag,1)); dialogData.insert(userInfo); } // Aplication context val = params.getParam(s_tcapIntAppID); oidStr = params.getParam(s_tcapObjAppID); if (!TelEngine::null(val) && !TelEngine::null(oidStr)) { // parameter error, encoding of this portion skipped Debug(tcap(),DebugInfo,"SS7TCAPTransactionANSI::encodeDialogPortion() - skipping encoding of Application Context Information," " both IntegerApplicationID=%s and ObjectApplicationID=%s specified, can't pick one",val->c_str(),oid.toString().c_str()); } else if (!TelEngine::null(val)) { DataBlock db = ASNLib::encodeInteger(val->toInteger(),false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPANSI::IntApplicationContextTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } else if (!TelEngine::null(oidStr)) { oid = *oidStr; DataBlock db = ASNLib::encodeOID(oid,false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPANSI::OIDApplicationContextTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } val = params.getParam(s_tcapProtoVers); if (!TelEngine::null(val)) { u_int8_t proto = val->toInteger(); DataBlock db = ASNLib::encodeInteger(proto,false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPANSI::ProtocolVersionTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } if (dialogData.length()) { dialogData.insert(ASNLib::buildLength(dialogData)); tag = SS7TCAPANSI::DialogPortionTag; dialogData.insert(DataBlock(&tag,1)); } data.insert(dialogData); params.clearParam(s_tcapDialogPrefix,'.'); #ifdef DEBUG if (s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"SS7TCAPTransactionANSI::encodeDialogPortion() - encoded dialog portion",this,params,data); #endif } SS7TCAPError SS7TCAPTransactionANSI::decodePAbort(SS7TCAPTransaction* tr, NamedList& params, DataBlock& data) { u_int8_t tag = data[0]; SS7TCAPError error(SS7TCAP::ANSITCAP); if (tag == SS7TCAPANSI::PCauseTag || tag == SS7TCAPANSI::UserAbortPTag || tag == SS7TCAPANSI::UserAbortCTag) { SS7TCAPError error(SS7TCAP::ANSITCAP); data.cut(-1); if (tag == SS7TCAPANSI::PCauseTag) { u_int8_t pCode = 0; int len = ASNLib::decodeUINT8(data,&pCode,false); if (len != 1) { error.setError(SS7TCAPError::Transact_BadlyStructuredTransaction); return error; } params.setParam(s_tcapAbortCause,"pAbort"); params.setParam(s_tcapAbortInfo,String(SS7TCAPError::errorFromCode(SS7TCAP::ANSITCAP,pCode))); } else { int len = ASNLib::decodeLength(data); if (len < 0) { error.setError(SS7TCAPError::Transact_BadlyStructuredTransaction); return error; } String str; str.hexify(data.data(0,len),len,' '); data.cut(-len); params.setParam(s_tcapAbortCause,(tag == SS7TCAPANSI::UserAbortPTag ? "userAbortP" : "userAbortC")); params.setParam(s_tcapAbortInfo,str); if (tr) tr->setTransactionType(SS7TCAP::TC_U_Abort); } #ifdef DEBUG if (tr && tr->tcap() && s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tr->tcap(),"SS7TCAPTransactionANSI::decodePAbort() - decoded Abort info",tr,params,data); #endif } return error; } void SS7TCAPTransactionANSI::encodePAbort(SS7TCAPTransaction* tr, NamedList& params, DataBlock& data) { NamedString* pAbortCause = params.getParam(s_tcapAbortCause); DataBlock db; if (!TelEngine::null(pAbortCause)) { int tag = 0; if (*pAbortCause == "pAbort") { tag = SS7TCAPANSI::PCauseTag; u_int16_t pCode = SS7TCAPError::codeFromError(SS7TCAP::ANSITCAP,params.getIntValue(s_tcapAbortInfo)); if (pCode) { db.append(ASNLib::encodeInteger(pCode,false)); db.insert(ASNLib::buildLength(db)); } } else if (*pAbortCause == "userAbortP" || *pAbortCause == "userAbortC") { NamedString* info = params.getParam(s_tcapAbortInfo); if (!TelEngine::null(info)) db.unHexify(info->c_str(),info->length(),' '); db.insert(ASNLib::buildLength(db)); if (*pAbortCause == "userAbortP") tag = SS7TCAPANSI::UserAbortPTag; else tag = SS7TCAPANSI::UserAbortCTag; } if (db.length()) db.insert(DataBlock(&tag,1)); } if (db.length()) { data.insert(db); params.clearParam(s_tcapAbortCause); params.clearParam(s_tcapAbortInfo); } #ifdef DEBUG if (tr && tr->tcap() && s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tr->tcap(),"SS7TCAPTransactionANSI::encodePAbort() - encoded Abort info",tr,params,data); #endif } void SS7TCAPTransactionANSI::requestContent(NamedList& params, DataBlock& data) { #ifdef DEBUG if (s_extendedDbg) DDebug(tcap(),DebugAll,"SS7TCAPTransactionANSI::requestContent() for transaction with id=%s [%p]",m_localID.c_str(),this); #endif if (m_type == SS7TCAP::TC_P_Abort || m_type == SS7TCAP::TC_U_Abort) encodePAbort(this,params,data); else requestComponents(params,data); encodeDialogPortion(params,data); transactionData(params); } void SS7TCAPTransactionANSI::updateToEnd() { if (transactionType() == SS7TCAP::TC_QueryWithoutPerm || transactionType() == SS7TCAP::TC_ConversationWithoutPerm) setTransactionType(SS7TCAP::TC_U_Abort); else setTransactionType(SS7TCAP::TC_Response); } SS7TCAPError SS7TCAPTransactionANSI::decodeComponents(NamedList& params, DataBlock& data) { XDebug(tcap(),DebugAll,"SS7TCAPTransactionANSI::decodeComponents() [%p] - data length=%u",this,data.length()); SS7TCAPError error(SS7TCAP::ANSITCAP); if (!data.length()) { params.setParam(s_tcapCompCount,"0"); return error; } u_int8_t tag = data[0]; if (tag != SS7TCAPANSI::ComponentPortionTag) { // 0xe8 error.setError(SS7TCAPError::General_IncorrectComponentPortion); return error; } data.cut(-1); // decode length of component portion int len = ASNLib::decodeLength(data); bool checkEoC = (len == ASNLib::IndefiniteForm); if (!checkEoC && (len < 0 || len != (int)data.length())) { // the length of the remaining data should be the same as the decoded length() error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); return error; } unsigned int compCount = 0; while (data.length()) { if (checkEoC && ASNLib::matchEOC(data) > 0) break; compCount++; // decode component type u_int8_t compType = data[0]; data.cut(-1); // verify component length len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } // decode component IDs, start with ComponentsIDs identifier tag = data[0]; if (tag != SS7TCAPANSI::ComponentsIDsTag) {// 0xcf error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } data.cut(-1); // obtain component ID(s) u_int16_t compIDs; len = ASNLib::decodeUINT16(data,&compIDs,false); if (len < 0) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } String compParam; compPrefix(compParam,compCount,false); // comp IDs shall be decoded according to component type switch (compType) { case InvokeLast: case InvokeNotLast: if (len == 1) params.setParam(compParam + "." + s_tcapRemoteCID,String(compIDs)); else if (len == 2) { params.setParam(compParam + "." + s_tcapRemoteCID,String(compIDs >> 8)); params.setParam(compParam + "." + s_tcapLocalCID,String((u_int8_t)compIDs)); } else { params.setParam(compParam + "." + s_tcapRemoteCID,""); params.setParam(compParam + "." + s_tcapLocalCID,""); } break; case ReturnResultLast: case ReturnError: case Reject: case ReturnResultNotLast: if (len != 1) error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); else params.setParam(compParam + "." + s_tcapLocalCID,String(compIDs)); break; default: error.setError(SS7TCAPError::General_UnrecognizedComponentType); break; } const PrimitiveMapping* map = mapCompPrimitivesANSI(-1,compType); if (!map) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } params.setParam(compParam + "." + s_tcapCompType,lookup(map->primitive,SS7TCAP::s_compPrimitives,"Unknown")); if (error.error() != SS7TCAPError::NoError) break; // decode Operation Code tag = data[0]; if (tag == SS7TCAPANSI::OperationNationalTag || tag == SS7TCAPANSI::OperationPrivateTag) { data.cut(-1); int opCode = 0; len = ASNLib::decodeINT32(data,&opCode,false); if (tag == SS7TCAPANSI::OperationNationalTag) { if (len != 2) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } params.setParam(compParam + "." + s_tcapOpCodeType,"national"); } if (tag == SS7TCAPANSI::OperationPrivateTag) params.setParam(compParam + "." + s_tcapOpCodeType,"private"); params.setParam(compParam + "." + s_tcapOpCode,String(opCode)); } // decode Error Code tag = data[0]; if (tag == SS7TCAPANSI::ErrorNationalTag || tag == SS7TCAPANSI::ErrorPrivateTag) { // 0xd3, 0xd4 data.cut(-1); int errCode = 0; len = ASNLib::decodeINT32(data,&errCode,false); if (len < 0 || (tag == SS7TCAPANSI::ErrorNationalTag && len != 1)) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } if (tag == SS7TCAPANSI::ErrorNationalTag) params.setParam(compParam + "." + s_tcapErrCodeType,"national"); else params.setParam(compParam + "." + s_tcapErrCodeType,"private"); params.setParam(compParam + "." + s_tcapErrCode,String(errCode)); } // decode Problem tag = data[0]; if (tag == SS7TCAPANSI::ProblemCodeTag) { // 0xd5 data.cut(-1); u_int16_t problemCode = 0; len = ASNLib::decodeUINT16(data,&problemCode,false); if (len != 2) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } params.setParam(compParam + "." + s_tcapProblemCode,String(SS7TCAPError::errorFromCode(tcap()->tcapType(),problemCode))); } // decode Parameters (Set or Sequence) as payload tag = data[0]; String dataHexified = ""; if (tag == SS7TCAPANSI::ParameterSetTag || tag == SS7TCAPANSI::ParameterSeqTag) { // 0xf2 0x30 data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } DataBlock d((void*)data.data(0,len),len); data.cut(-len); d.insert(ASNLib::buildLength(d)); d.insert(DataBlock(&tag,1)); dataHexified.hexify(d.data(),d.length(),' '); } params.setParam(compParam,dataHexified); } params.setParam(s_tcapCompCount,String(compCount)); #ifdef DEBUG if (tcap() && s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"Finished decoding message",this,params,data); #endif return error; } void SS7TCAPTransactionANSI::encodeComponents(NamedList& params, DataBlock& data) { XDebug(tcap(),DebugAll,"SS7TCAPTransactionANSI::encodeComponents() for transaction with localID=%s [%p]",m_localID.c_str(),this); int componentCount = params.getIntValue(s_tcapCompCount,0); DataBlock compData; if (componentCount) { int index = componentCount + 1; while (--index) { DataBlock codedComp; // encode parameters String compParam; compPrefix(compParam,index,false); // Component Type NamedString* value = params.getParam(compParam + "." + s_tcapCompType); if (TelEngine::null(value)) continue; int compPrimitive = lookup(*value,SS7TCAP::s_compPrimitives); const PrimitiveMapping* map = mapCompPrimitivesANSI(compPrimitive,-1); if (!map) continue; int compType = map->mappedTo; String payloadHex = params.getValue(compParam,""); if (!payloadHex.null()) { DataBlock payload; payload.unHexify(payloadHex.c_str(),payloadHex.length(),' '); //payload.insert(ASNLib::buildLength(payload)); codedComp.insert(payload); } // encode Problem only if Reject if (compType == Reject) { value = params.getParam(compParam + "." + s_tcapProblemCode); if (!TelEngine::null(value)) { u_int16_t code = SS7TCAPError::codeFromError(tcap()->tcapType(),value->toInteger()); DataBlock db = ASNLib::encodeInteger(code,false); // should check that encoded length is 2 if (db.length() < 2) { code = 0; db.insert(DataBlock(&code,1)); } db.insert(ASNLib::buildLength(db)); int tag = SS7TCAPANSI::ProblemCodeTag; db.insert(DataBlock(&tag,1)); codedComp.insert(db); } } // encode Error Code only if ReturnError if (compType == ReturnError) { value = params.getParam(compParam + "." + s_tcapErrCodeType); if (!TelEngine::null(value)) { int errCode = params.getIntValue(compParam + "." + s_tcapErrCode,0); DataBlock db = ASNLib::encodeInteger(errCode,false); db.insert(ASNLib::buildLength(db)); int tag = 0; if (*value == "national") tag = SS7TCAPANSI::ErrorNationalTag; else if (*value == "private") tag = SS7TCAPANSI::ErrorPrivateTag; db.insert(DataBlock(&tag,1)); codedComp.insert(db); } } // encode Operation Code only if Invoke if (compType == InvokeLast || compType == InvokeNotLast) { value = params.getParam(compParam + "." + s_tcapOpCodeType); if (!TelEngine::null(value)) { int opCode = params.getIntValue(compParam + "." + s_tcapOpCode,0); DataBlock db = ASNLib::encodeInteger(opCode,false); int tag = 0; if (*value == "national") { tag = SS7TCAPANSI::OperationNationalTag; if (db.length() < 2) { opCode = 0; db.insert(DataBlock(&opCode,1)); } } else if (*value == "private") tag = SS7TCAPANSI::OperationPrivateTag; db.insert(ASNLib::buildLength(db)); db.insert(DataBlock(&tag,1)); codedComp.insert(db); } } NamedString* invID = params.getParam(compParam + "." + s_tcapLocalCID); NamedString* corrID = params.getParam(compParam + "." + s_tcapRemoteCID); DataBlock db; u_int8_t val = 0; switch (compType) { case InvokeLast: case InvokeNotLast: if (!TelEngine::null(invID)) { val = invID->toInteger(); db.append(&val,sizeof(u_int8_t)); if (!TelEngine::null(corrID)) { val = corrID->toInteger(); db.append(&val,sizeof(u_int8_t)); } } else { if (!TelEngine::null(corrID)) { val = corrID->toInteger(); db.append(&val,sizeof(u_int8_t)); } } break; case ReturnResultLast: case ReturnError: case Reject: case ReturnResultNotLast: val = corrID->toInteger(); db.append(&val,sizeof(u_int8_t)); break; default: break; } db.insert(ASNLib::buildLength(db)); int tag = SS7TCAPANSI::ComponentsIDsTag; db.insert(DataBlock(&tag,1)); codedComp.insert(db); codedComp.insert(ASNLib::buildLength(codedComp)); codedComp.insert(DataBlock(&compType,1)); params.clearParam(compParam,'.'); // clear all params for this component compData.insert(codedComp); } } compData.insert(ASNLib::buildLength(compData)); int tag = SS7TCAPANSI::ComponentPortionTag; compData.insert(DataBlock(&tag,1)); data.insert(compData); params.clearParam(s_tcapCompPrefix,'.'); } SS7TCAPError SS7TCAPTransactionANSI::handleDialogPortion(NamedList& params, bool byUser) { XDebug(tcap(),DebugAll,"SS7TCAPTransactionANSI::handleDialogPortion() [%p]",this); SS7TCAPError err(SS7TCAP::ANSITCAP); NamedList dialog(""); Lock l(this); switch (m_type) { case SS7TCAP::TC_Begin: case SS7TCAP::TC_QueryWithPerm: case SS7TCAP::TC_QueryWithoutPerm: case SS7TCAP::TC_Unidirectional: if (!byUser) { int protoVersion = params.getIntValue(s_tcapProtoVers); if (protoVersion) { // there is a Dialog portion if ((protoVersion & s_tcapProtoVersion ) != s_tcapProtoVersion) params.setParam(s_tcapProtoVers,String(s_tcapProtoVersion)); } } else { dialog.copyParams(params,s_tcapDialogPrefix,'.'); if (dialog.count()) params.setParam(s_tcapProtoVers,String(s_tcapProtoVersion)); } return err; case SS7TCAP::TC_End: case SS7TCAP::TC_Response: dialog.copyParams(params,s_tcapDialogPrefix,'.'); if (dialog.count() && m_prevType != SS7TCAP::TC_Begin && m_prevType != SS7TCAP::TC_QueryWithPerm) { err.setError(SS7TCAPError::Dialog_InconsistentDialoguePortion); return err; } break; case SS7TCAP::TC_Continue: case SS7TCAP::TC_ConversationWithPerm: case SS7TCAP::TC_ConversationWithoutPerm: dialog.copyParams(params,s_tcapDialogPrefix,'.'); if (dialog.count() && m_prevType != SS7TCAP::TC_Begin && m_prevType != SS7TCAP::TC_QueryWithPerm && m_prevType != SS7TCAP::TC_QueryWithoutPerm) { err.setError(SS7TCAPError::Dialog_InconsistentDialoguePortion); return err; } break; case SS7TCAP::TC_Notice: case SS7TCAP::TC_P_Abort: break; case SS7TCAP::TC_U_Abort: break; default: break; } return err; } void SS7TCAPTransactionANSI::updateState(bool byUser) { switch (m_type) { case SS7TCAP::TC_Begin: case SS7TCAP::TC_QueryWithPerm: case SS7TCAP::TC_QueryWithoutPerm: case SS7TCAP::TC_Continue: case SS7TCAP::TC_ConversationWithPerm: case SS7TCAP::TC_ConversationWithoutPerm: (byUser ? setState(SS7TCAPTransaction::PackageSent) : setState(SS7TCAPTransaction::PackageReceived)); break; case SS7TCAP::TC_End: case SS7TCAP::TC_U_Abort: case SS7TCAP::TC_P_Abort: case SS7TCAP::TC_Response: case SS7TCAP::TC_Unidirectional: setState(Idle); break; case SS7TCAP::TC_Notice: case SS7TCAP::TC_Unknown: default: break; } } /** * ITU-T SS7 TCAP implementation */ static const int s_ituTCAPProto = 1; static const String s_tcapDialogueID = "tcap.dialogPDU.dialog-as-id"; static const String s_tcapDialogueAppCtxt = "tcap.dialogPDU.application-context-name"; static const String s_tcapDialoguePduType = "tcap.dialogPDU.dialog-pdu-type"; static const String s_tcapDialogueAbrtSrc = "tcap.dialogPDU.abort-source"; static const String s_tcapDialogueResult = "tcap.dialogPDU.result"; static const String s_tcapDialogueDiag = "tcap.dialogPDU.result-source-diagnostic"; static const String s_unstructDialogueOID = "0.0.17.773.1.2.1"; static const String s_structDialogueOID = "0.0.17.773.1.1.1"; static const PrimitiveMapping s_componentsITUMap[] = { {SS7TCAP::TC_Invoke, SS7TCAPTransactionITU::Invoke}, {SS7TCAP::TC_ResultLast, SS7TCAPTransactionITU::ReturnResultLast}, {SS7TCAP::TC_U_Error, SS7TCAPTransactionITU::ReturnError}, {SS7TCAP::TC_U_Reject, SS7TCAPTransactionITU::Reject}, {SS7TCAP::TC_R_Reject, SS7TCAPTransactionITU::Reject}, {SS7TCAP::TC_L_Reject, SS7TCAPTransactionITU::Reject}, {SS7TCAP::TC_InvokeNotLast, SS7TCAPTransactionITU::Invoke}, {SS7TCAP::TC_ResultNotLast, SS7TCAPTransactionITU::ReturnResultNotLast}, {SS7TCAP::TC_L_Cancel, SS7TCAPTransactionITU::Local}, {SS7TCAP::TC_U_Cancel, SS7TCAPTransactionITU::Local}, {SS7TCAP::TC_TimerReset, SS7TCAPTransactionITU::Local}, {SS7TCAP::TC_Unknown, SS7TCAPTransactionITU::Unknown}, }; static const PrimitiveMapping s_transITUMap[] = { {SS7TCAP::TC_Unidirectional, SS7TCAPTransactionITU::Unidirectional}, {SS7TCAP::TC_Begin, SS7TCAPTransactionITU::Begin}, {SS7TCAP::TC_QueryWithPerm, SS7TCAPTransactionITU::Begin}, {SS7TCAP::TC_QueryWithoutPerm, SS7TCAPTransactionITU::Begin}, {SS7TCAP::TC_Continue, SS7TCAPTransactionITU::Continue}, {SS7TCAP::TC_ConversationWithPerm, SS7TCAPTransactionITU::Continue}, {SS7TCAP::TC_ConversationWithoutPerm, SS7TCAPTransactionITU::Continue}, {SS7TCAP::TC_End, SS7TCAPTransactionITU::End}, {SS7TCAP::TC_Response, SS7TCAPTransactionITU::End}, {SS7TCAP::TC_U_Abort, SS7TCAPTransactionITU::Abort}, {SS7TCAP::TC_P_Abort, SS7TCAPTransactionITU::Abort}, {SS7TCAP::TC_Notice, SS7TCAPTransactionITU::Unknown}, {SS7TCAP::TC_Unknown, SS7TCAPTransactionITU::Unknown}, }; static const PrimitiveMapping* mapCompPrimitivesITU(int primitive, int comp = -1) { const PrimitiveMapping* map = s_componentsITUMap; for (; map->primitive != SS7TCAP::TC_Unknown; map++) { if (primitive != -1) { if (map->primitive == primitive ) break; } else if (comp != -1) if (map->mappedTo == comp) break; } return map; } static const PrimitiveMapping* mapTransPrimitivesITU(int primitive, int trans = -1) { const PrimitiveMapping* map = s_transITUMap; for (; map->primitive != SS7TCAP::TC_Unknown; map++) { if (primitive != -1) { if (map->primitive == primitive ) break; } else if (trans != -1) if (map->mappedTo == trans) break; } return map; } SS7TCAPITU::SS7TCAPITU(const NamedList& params) : SignallingComponent(params.safe("SS7TCAPITU"),¶ms,"ss7-tcap-itu"), SS7TCAP(params) { String tmp; params.dump(tmp,"\r\n ",'\'',true); DDebug(this,DebugAll,"SS7TCAPITU::SS7TCAPITU(%s)",tmp.c_str()); setTCAPType(SS7TCAP::ITUTCAP); } SS7TCAPITU::~SS7TCAPITU() { DDebug(this,DebugAll,"SS7TCAPITU::~SS7TCAPITU() [%p] destroyed with %d transactions, refCount=%d", this,m_transactions.count(),refcount()); } SS7TCAPTransaction* SS7TCAPITU::buildTransaction(SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params, bool initLocal) { return new SS7TCAPTransactionITU(this,type,transactID,params,m_trTimeout,initLocal); } SS7TCAPError SS7TCAPITU::decodeTransactionPart(NamedList& params, DataBlock& data) { SS7TCAPError error(SS7TCAP::ITUTCAP); if (data.length() < 2) return error; // decode message type u_int8_t msgType = data[0]; data.cut(-1); const PrimitiveMapping* map = mapTransPrimitivesITU(-1,msgType); if (map) { String type = lookup(map->primitive,SS7TCAP::s_transPrimitives,"Unknown"); params.setParam(s_tcapRequest,type); } // decode message length int len = ASNLib::decodeLength(data); if (len != (int)data.length()) { error.setError(SS7TCAPError::Transact_BadlyStructuredTransaction); return error; } // decode transaction ids bool decodeOTID = false; bool decodeDTID = false; switch (map->mappedTo) { case SS7TCAPTransactionITU::Unidirectional: return error; case SS7TCAPTransactionITU::Begin: decodeOTID = true; break; case SS7TCAPTransactionITU::End: case SS7TCAPTransactionITU::Abort: decodeDTID = true; break; case SS7TCAPTransactionITU::Continue: decodeOTID = true; decodeDTID = true; break; default: error.setError(SS7TCAPError::Transact_UnrecognizedPackageType); return error; } u_int8_t tag = data[0]; String str; if (decodeOTID) { // check for originating ID if (tag != OriginatingIDTag) {// 0x48 error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); return error; } data.cut(-1); len = ASNLib::decodeLength(data); if (len < 1 || len > 4 || len > (int)data.length()) { error.setError(SS7TCAPError::Transact_BadlyStructuredTransaction); return error; } str.hexify(data.data(),len,' '); data.cut(-len); params.setParam(s_tcapRemoteTID,str); } tag = data[0]; if (decodeDTID) { // check for originating ID if (tag != DestinationIDTag) {// 0x49 error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); return error; } data.cut(-1); len = ASNLib::decodeLength(data); if (len < 1 || len > 4 || len > (int)data.length()) { error.setError(SS7TCAPError::Transact_BadlyStructuredTransaction); return error; } str.hexify(data.data(),len,' '); data.cut(-len); params.setParam(s_tcapLocalTID,str); } #ifdef DEBUG if (s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,this,"SS7TCAPITU::decodeTransactionPart() finished",this,params,data); #endif error.setError(SS7TCAPError::NoError); return error; } void SS7TCAPITU::encodeTransactionPart(NamedList& params, DataBlock& data) { String msg = params.getValue(s_tcapRequest); const PrimitiveMapping* map = mapTransPrimitivesITU(msg.toInteger(SS7TCAP::s_transPrimitives),-1); if (!map) return; u_int8_t msgType = map->mappedTo; NamedString* val = 0; u_int8_t tag; bool encDTID = false; bool encOTID = false; switch (msgType) { case SS7TCAPTransactionITU::Unidirectional: break; case SS7TCAPTransactionITU::Begin: encOTID = true; break; case SS7TCAPTransactionITU::End: case SS7TCAPTransactionITU::Abort: encDTID = true; break; case SS7TCAPTransactionITU::Continue: encOTID = true; encDTID = true; break; default: break; } if (encDTID) { val = params.getParam(s_tcapRemoteTID); if (!TelEngine::null(val)) { // destination TID DataBlock db; db.unHexify(val->c_str(),val->length(),' '); db.insert(ASNLib::buildLength(db)); tag = DestinationIDTag; db.insert(DataBlock(&tag,1)); data.insert(db); } } if (encOTID) { val = params.getParam(s_tcapLocalTID); if (!TelEngine::null(val)) { // origination id DataBlock db; db.unHexify(val->c_str(),val->length(),' '); db.insert(ASNLib::buildLength(db)); tag = OriginatingIDTag; db.insert(DataBlock(&tag,1)); data.insert(db); } } data.insert(ASNLib::buildLength(data)); data.insert(DataBlock(&msgType,1)); } /** * ITU-T SS7 TCAP transaction implementation */ const TokenDict SS7TCAPTransactionITU::s_dialogPDUs[] = { {"AARQ", SS7TCAPTransactionITU::AARQDialogTag}, {"AARE", SS7TCAPTransactionITU::AAREDialogTag}, {"ABRT", SS7TCAPTransactionITU::ABRTDialogTag}, {0,0}, }; const TokenDict SS7TCAPTransactionITU::s_resultPDUValues[] = { {"accepted", SS7TCAPTransactionITU::ResultAccepted}, {"reject-permanent", SS7TCAPTransactionITU::ResultRejected}, {"user-null", SS7TCAPTransactionITU::DiagnosticUserNull}, {"user-no-reason-given", SS7TCAPTransactionITU::DiagnosticUserNoReason}, {"user-application-context-name-not-supported", SS7TCAPTransactionITU::DiagnosticUserAppCtxtNotSupported}, {"provider-null", SS7TCAPTransactionITU::DiagnosticProviderNull}, {"provider-no-reason-given", SS7TCAPTransactionITU::DiagnosticProviderNoReason}, {"provider-no-common-dialogue-portion", SS7TCAPTransactionITU::DiagnosticProviderNoCommonDialog}, {"dialogue-service-user", SS7TCAPTransactionITU::AbortSourceUser}, {"dialogue-service-provider", SS7TCAPTransactionITU::AbortSourceProvider}, {0,-1}, }; SS7TCAPTransactionITU::SS7TCAPTransactionITU(SS7TCAP* tcap, SS7TCAP::TCAPUserTransActions type, const String& transactID, NamedList& params, u_int64_t timeout, bool initLocal) : SS7TCAPTransaction(tcap,type,transactID,params,timeout,initLocal) { DDebug(m_tcap,DebugAll,"SS7TCAPTransactionITU(tcap = '%s' [%p], transactID = %s, timeout=" FMT64 " ) created [%p]", m_tcap->toString().c_str(),tcap,transactID.c_str(),timeout,this); } SS7TCAPTransactionITU::~SS7TCAPTransactionITU() { DDebug(tcap(),DebugAll,"Transaction with ID=%s of user=%s destroyed [%p]", m_localID.c_str(),m_userName.c_str(),this); } SS7TCAPError SS7TCAPTransactionITU::handleData(NamedList& params, DataBlock& data) { DDebug(tcap(),DebugAll,"SS7TCAPTransactionITU::handleData() transactionID=%s data length=%u [%p]",m_localID.c_str(), data.length(),this); Lock lock(this); // in case of Abort message, check Cause Information SS7TCAPError error(SS7TCAP::ITUTCAP); if (m_type == SS7TCAP::TC_P_Abort) { error = decodePAbort(this,params,data); if (error.error() != SS7TCAPError::NoError) return error; } else if (testForDialog(data)) { // decode DialogPortion error = decodeDialogPortion(params,data); if (error.error() != SS7TCAPError::NoError) return error; } error = handleDialogPortion(params,false); if (error.error() != SS7TCAPError::NoError) return error; // decodeComponents error = decodeComponents(params,data); if (error.error() != SS7TCAPError::NoError) buildComponentError(error,params,data); error = handleComponents(params,false); return error; } bool SS7TCAPTransactionITU::testForDialog(DataBlock& data) { return (data.length() && data[0] == SS7TCAPITU::DialogPortionTag); } void SS7TCAPTransactionITU::updateState(bool byUser) { switch (m_type) { case SS7TCAP::TC_Begin: case SS7TCAP::TC_QueryWithPerm: case SS7TCAP::TC_QueryWithoutPerm: break; case SS7TCAP::TC_Continue: case SS7TCAP::TC_ConversationWithPerm: case SS7TCAP::TC_ConversationWithoutPerm: setState(Active); break; case SS7TCAP::TC_End: case SS7TCAP::TC_U_Abort: case SS7TCAP::TC_P_Abort: case SS7TCAP::TC_Response: case SS7TCAP::TC_Unidirectional: setState(Idle); break; case SS7TCAP::TC_Notice: case SS7TCAP::TC_Unknown: default: break; } } SS7TCAPError SS7TCAPTransactionITU::update(SS7TCAP::TCAPUserTransActions type, NamedList& params, bool updateByUser) { DDebug(tcap(),DebugAll,"SS7TCAPTransactionITU::update() [%p], localID=%s - update to type=%s by %s",this,m_localID.c_str(), lookup(type,SS7TCAP::s_transPrimitives,"Unknown"), (updateByUser ? "user" : "remote")); #ifdef DEBUG if (s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"SS7TCAPTransactionITU::update() with",this,params); #endif Lock l(this); SS7TCAPError error(SS7TCAP::ITUTCAP); switch (type) { case SS7TCAP::TC_Begin: case SS7TCAP::TC_QueryWithPerm: case SS7TCAP::TC_QueryWithoutPerm: case SS7TCAP::TC_Unidirectional: Debug(tcap(),DebugInfo,"SS7TCAPTransactionITU::update() [%p], localID=%s - invalid update: trying to update from type=%s to type=%s", this,m_localID.c_str(),lookup(m_type,SS7TCAP::s_transPrimitives,"Unknown"), lookup(type,SS7TCAP::s_transPrimitives,"Unknown")); params.setParam(s_tcapRequestError,"invalid_update"); params.setParam("tcap.request.error.currentState",lookup(m_type,SS7TCAP::s_transPrimitives,"Unknown")); error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); return error; case SS7TCAP::TC_End: case SS7TCAP::TC_Response: m_type = type; if (m_state == PackageReceived) { m_basicEnd = params.getBoolValue(s_tcapBasicTerm,m_basicEnd); if (!m_basicEnd) // prearranged end, no need to transmit to remote end m_transmit = NoTransmit; else m_transmit = PendingTransmit; } else if (m_state == PackageSent) { if(!updateByUser) m_transmit = PendingTransmit; else m_transmit = NoTransmit; } else if (m_state == Active) { if(!updateByUser) m_transmit = PendingTransmit; else { m_basicEnd = params.getBoolValue(s_tcapBasicTerm,m_basicEnd); if (!m_basicEnd) // prearranged end, no need to transmit to remote end m_transmit = NoTransmit; else m_transmit = PendingTransmit; } } break; case SS7TCAP::TC_Continue: case SS7TCAP::TC_ConversationWithPerm: case SS7TCAP::TC_ConversationWithoutPerm: if (m_state == PackageSent) m_remoteID = params.getValue(s_tcapRemoteTID); m_type = type; m_transmit = PendingTransmit; break; case SS7TCAP::TC_Notice: m_type = type; if (updateByUser) { Debug(tcap(),DebugInfo,"SS7TCAPTransactionITU::update() [%p], localID=%s - invalid update: trying to update from type=%s to type=%s", this,m_localID.c_str(),lookup(m_type,SS7TCAP::s_transPrimitives,"Unknown"), lookup(type,SS7TCAP::s_transPrimitives,"Unknown")); params.setParam(s_tcapRequestError,"invalid_update"); params.setParam("tcap.request.error.currentState",lookup(m_type,SS7TCAP::s_transPrimitives,"Unknown")); error.setError(SS7TCAPError::Transact_IncorrectTransactionPortion); return error; } break; case SS7TCAP::TC_P_Abort: case SS7TCAP::TC_U_Abort: m_type = type; if (m_state == PackageReceived) m_transmit = PendingTransmit; else if (m_state == PackageSent) { if (!updateByUser) { if (String("pAbort") == params.getValue(s_tcapAbortCause)) m_type = SS7TCAP::TC_P_Abort; else m_type = SS7TCAP::TC_P_Abort; m_transmit = PendingTransmit; } else m_transmit = NoTransmit; } else if (m_state == Active) { if (!updateByUser) { if (String("pAbort") == params.getValue(s_tcapAbortCause)) m_type = SS7TCAP::TC_P_Abort; else m_type = SS7TCAP::TC_P_Abort; } m_transmit = PendingTransmit; } break; default: break; } populateSCCPAddress(m_localSCCPAddr,m_remoteSCCPAddr,params,updateByUser); m_basicEnd = params.getBoolValue(s_tcapBasicTerm,true); m_endNow = params.getBoolValue(s_tcapEndNow,m_endNow); if (m_timeout.started()) { m_timeout.stop(); XDebug(tcap(),DebugInfo,"SS7TCAPTransactionITU::update() [%p], localID=%s - timeout timer has been stopped",this,m_localID.c_str()); } return error; } SS7TCAPError SS7TCAPTransactionITU::handleDialogPortion(NamedList& params, bool byUser) { DDebug(tcap(),DebugAll,"SS7TCAPTransactionITU::handleDialogPortion() [%p]",this); SS7TCAPError error(SS7TCAP::ITUTCAP); NamedString* diagPDU = params.getParam(s_tcapDialoguePduType); NamedString* appCtxt = params.getParam(s_tcapDialogueAppCtxt); int protoVers = params.getIntValue(s_tcapProtoVers,s_ituTCAPProto,0); Lock l(this); switch (m_type) { case SS7TCAP::TC_Unidirectional: if (byUser) { // check for context name, if not present no AUDT if (TelEngine::null(appCtxt)) return error; m_appCtxt = *appCtxt; // build AUDT. params.setParam(s_tcapDialogueID,s_unstructDialogueOID.toString()); if (protoVers) params.setParam(s_tcapProtoVers,String(protoVers)); params.setParam(s_tcapDialoguePduType,lookup(AARQDialogTag,s_dialogPDUs)); } else { // check to be AUDT if (TelEngine::null(diagPDU) || !protoVers) return error; if (diagPDU->toInteger(s_dialogPDUs) != AARQDialogTag || s_ituTCAPProto != protoVers) error.setError(SS7TCAPError::Dialog_Abnormal); } break; case SS7TCAP::TC_Begin: case SS7TCAP::TC_QueryWithPerm: case SS7TCAP::TC_QueryWithoutPerm: if (byUser) { if (TelEngine::null(appCtxt)) break; m_appCtxt = *appCtxt; // build AARQ params.setParam(s_tcapDialogueID,s_structDialogueOID); if (protoVers) params.setParam(s_tcapProtoVers,String(protoVers)); params.setParam(s_tcapDialoguePduType,lookup(AARQDialogTag,s_dialogPDUs)); } else { if (TelEngine::null(diagPDU) || !protoVers) break; // check to be AARQ and that it has context if (diagPDU->toInteger(s_dialogPDUs) != AARQDialogTag || TelEngine::null(appCtxt)) { error.setError(SS7TCAPError::Dialog_Abnormal); break; } // check proto version, if not 1, build AARE - no common dialogue version, return err to build abort if (s_ituTCAPProto != protoVers) { params.clearParam(s_tcapDialogPrefix,'.'); params.setParam(s_tcapDialogueID,s_structDialogueOID); params.setParam(s_tcapDialoguePduType,lookup(AAREDialogTag,s_dialogPDUs)); params.setParam(s_tcapDialogueResult,lookup(ResultRejected,s_resultPDUValues)); params.setParam(s_tcapDialogueDiag,lookup(DiagnosticProviderNoCommonDialog,s_resultPDUValues)); error.setError(SS7TCAPError::Dialog_Abnormal); break; } m_appCtxt = *appCtxt; } break; case SS7TCAP::TC_End: case SS7TCAP::TC_Response: if (byUser) { if (!basicEnd() || transactionState() != PackageReceived || m_appCtxt.null()) { params.clearParam(s_tcapDialogPrefix,'.'); break; } if (TelEngine::null(appCtxt)) params.setParam(s_tcapDialogueAppCtxt,m_appCtxt); // build AARE with result=accepted, result-source-diagnostic=null / dialog-service-user(null) params.setParam(s_tcapDialogueID,s_structDialogueOID); if (protoVers) params.setParam(s_tcapProtoVers,String(protoVers)); params.setParam(s_tcapDialoguePduType,lookup(AAREDialogTag,s_dialogPDUs)); params.setParam(s_tcapDialogueResult,lookup(ResultAccepted,s_resultPDUValues)); if (!params.getParam(s_tcapDialogueDiag)) params.addParam(s_tcapDialogueDiag,lookup(DiagnosticUserNoReason,s_resultPDUValues)); } else { // page 51 q.774 // dialog info ? // => yes => AC MODE ? = no, discard components. TC-p-abort to TCU , terminate transaction // = yes, check correct AARE, incorrect => TC-P-Abort to user, send TC_END to user otherwise // => no =? AC MODE ? = no, send TC_END to user (continue processing) // = yes, TC-p-abort to TCU , terminate transaction if (transactionState() != PackageSent && !TelEngine::null(diagPDU)) { error.setError(SS7TCAPError::Dialog_Abnormal); break; } if (!TelEngine::null(appCtxt)) { if (m_appCtxt.null()) error.setError(SS7TCAPError::Dialog_Abnormal); else { if (TelEngine::null(diagPDU) || diagPDU->toInteger(s_dialogPDUs) != AAREDialogTag) error.setError(SS7TCAPError::Dialog_Abnormal); } } else { if (!m_appCtxt.null() && transactionState() != Active) error.setError(SS7TCAPError::Dialog_Abnormal); } } break; case SS7TCAP::TC_Continue: case SS7TCAP::TC_ConversationWithPerm: case SS7TCAP::TC_ConversationWithoutPerm: if (byUser) { if (transactionState() != PackageReceived || TelEngine::null(appCtxt)) { params.clearParam(s_tcapDialogPrefix,'.'); break; } // build AARE m_appCtxt = *appCtxt; params.setParam(s_tcapDialogueID,s_structDialogueOID); if (protoVers) params.setParam(s_tcapProtoVers,String(protoVers)); params.setParam(s_tcapDialoguePduType,lookup(AAREDialogTag,s_dialogPDUs)); params.setParam(s_tcapDialogueResult,lookup(ResultAccepted,s_resultPDUValues)); if (!params.getParam(s_tcapDialogueDiag)) params.addParam(s_tcapDialogueDiag,lookup(DiagnosticUserNoReason,s_resultPDUValues)); } else { // dialog info? // yes => AC MODE? => yes, Check AARE // = > no, discard, build P Abort with ABRT apdu // no => AC MODE? => no, send to user / continue processing // => yes, build U_Abort with ABRT apdu if (transactionState() == PackageReceived) break; if (!TelEngine::null(appCtxt)) { if (m_appCtxt.null()) error.setError(SS7TCAPError::Dialog_Abnormal); else { if (TelEngine::null(diagPDU) || diagPDU->toInteger(s_dialogPDUs) != AAREDialogTag) error.setError(SS7TCAPError::Dialog_Abnormal); } } else { if (!m_appCtxt.null() && transactionState() == PackageSent) error.setError(SS7TCAPError::Dialog_Abnormal); } } break; case SS7TCAP::TC_Notice: case SS7TCAP::TC_P_Abort: break; case SS7TCAP::TC_U_Abort: if (byUser) { if (m_appCtxt.null()) break; params.setParam(s_tcapDialogueID,s_structDialogueOID); if (protoVers) params.setParam(s_tcapProtoVers,String(protoVers)); if (transactionState() == PackageReceived ) { NamedString* abrtReason = params.getParam(s_tcapDialogueDiag); if (!TelEngine::null(abrtReason) && (abrtReason->toInteger(s_resultPDUValues) == DiagnosticUserAppCtxtNotSupported || abrtReason->toInteger(s_resultPDUValues) == DiagnosticProviderNoCommonDialog)) { // build AARE if (TelEngine::null(appCtxt)) params.setParam(s_tcapDialogueAppCtxt,m_appCtxt); params.setParam(s_tcapDialoguePduType,lookup(AAREDialogTag,s_dialogPDUs)); params.setParam(s_tcapDialogueResult,lookup(ResultRejected,s_resultPDUValues)); } else { // build ABRT params.setParam(s_tcapDialoguePduType,lookup(ABRTDialogTag,s_dialogPDUs)); params.setParam(s_tcapDialogueAbrtSrc,lookup(AbortSourceUser,s_resultPDUValues)); } } else if (transactionState() == Active) { if (TelEngine::null(params.getParam(s_tcapDialogueAbrtSrc))) params.setParam(s_tcapDialogueAbrtSrc,lookup(AbortSourceUser,s_resultPDUValues)); params.setParam(s_tcapDialoguePduType,lookup(ABRTDialogTag,s_dialogPDUs)); } } else { // state initsent/active if (!TelEngine::null(m_appCtxt)) { NamedString* diagID = params.getParam(s_tcapDialogueID); NamedString* pdu = params.getParam(s_tcapDialoguePduType); if (!TelEngine::null(diagID) && !TelEngine::null(pdu)) { if (s_structDialogueOID == *diagID && (pdu->toInteger(s_dialogPDUs) == AAREDialogTag || pdu->toInteger(s_dialogPDUs) == ABRTDialogTag)) { if (pdu->toInteger() == AAREDialogTag) { NamedString* diag = params.getParam(s_tcapDialogueDiag); if (transactionState() == PackageSent && !TelEngine::null(diag) && diag->toInteger(s_resultPDUValues) != DiagnosticProviderNoCommonDialog) { params.setParam(s_tcapRequest,lookup(SS7TCAP::TC_P_Abort,SS7TCAP::s_transPrimitives)); params.setParam(s_tcapAbortCause,"pAbort"); m_transmit = PendingTransmit; } else error.setError(SS7TCAPError::Dialog_Abnormal); } else { NamedString* src = params.getParam(s_tcapDialogueAbrtSrc); if (!TelEngine::null(src) && src->toInteger(s_resultPDUValues) != AbortSourceUser) error.setError(SS7TCAPError::Dialog_Abnormal); } } else error.setError(SS7TCAPError::Dialog_Abnormal); } else error.setError(SS7TCAPError::Dialog_Abnormal); } else { if (!TelEngine::null(appCtxt)) error.setError(SS7TCAPError::Dialog_Abnormal); } } break; default: break; } #ifdef DEBUG if (tcap() && s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"SS7TCAPTransactionITU::handleDialogPortion()",this,params,DataBlock::empty()); #endif return error; } void SS7TCAPTransactionITU::abnormalDialogInfo(NamedList& params) { params.setParam(s_tcapRequest,lookup(SS7TCAP::TC_U_Abort,SS7TCAP::s_transPrimitives)); params.setParam(s_tcapAbortCause,"uAbort"); params.setParam(s_tcapDialogueID,s_structDialogueOID); params.setParam(s_tcapDialoguePduType, lookup(SS7TCAPTransactionITU::ABRTDialogTag,SS7TCAPTransactionITU::s_dialogPDUs)); params.setParam(s_tcapDialogueAbrtSrc, lookup(SS7TCAPTransactionITU::AbortSourceProvider,SS7TCAPTransactionITU::s_resultPDUValues)); } void SS7TCAPTransactionITU::encodePAbort(SS7TCAPTransaction* tr, NamedList& params, DataBlock& data) { NamedString* pAbortCause = params.getParam(s_tcapAbortCause); DataBlock db; if (!TelEngine::null(pAbortCause)) { int tag = 0; if (*pAbortCause == "pAbort") { tag = SS7TCAPITU::PCauseTag; u_int8_t pCode = SS7TCAPError::codeFromError(SS7TCAP::ITUTCAP,params.getIntValue(s_tcapAbortInfo)); if (pCode) { db.append(ASNLib::encodeInteger(pCode,false)); db.insert(ASNLib::buildLength(db)); db.insert(DataBlock(&tag,1)); } } else if (*pAbortCause == "uAbort") { if (tr) tr->encodeDialogPortion(params,data); } } if (db.length()) data.insert(db); #ifdef DEBUG if (tr && tr->tcap() && s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tr->tcap(),"SS7TCAPTransactionITU::encodePAbort() - encoded Abort info",tr,params,data); #endif } SS7TCAPError SS7TCAPTransactionITU::decodePAbort(SS7TCAPTransaction* tr, NamedList& params, DataBlock& data) { u_int8_t tag = data[0]; SS7TCAPError error(SS7TCAP::ITUTCAP); if (!tr) return error; SS7TCAPTransactionITU* tri = static_cast(tr); if (!tri) return error; if (tag == SS7TCAPITU::PCauseTag) { data.cut(-1); u_int8_t pCode = 0; int len = ASNLib::decodeUINT8(data,&pCode,false); if (len != 1) { error.setError(SS7TCAPError::Transact_BadlyStructuredTransaction); return error; } params.setParam(s_tcapAbortCause,"pAbort"); params.setParam(s_tcapAbortInfo,String(SS7TCAPError::errorFromCode(SS7TCAP::ITUTCAP,pCode))); } else if (tri->testForDialog(data)) { error = tri->decodeDialogPortion(params,data); if (error.error() != SS7TCAPError::NoError) return error; params.setParam(s_tcapAbortCause,"uAbort"); } #ifdef DEBUG if (tr && tr->tcap() && s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tr->tcap(),"SS7TCAPTransactionITU::decodePAbort() - decoded Abort info",tr,params,data); #endif return error; } void SS7TCAPTransactionITU::updateToEnd() { setTransactionType(SS7TCAP::TC_End); if (transactionState() == PackageSent) m_basicEnd = false; } SS7TCAPError SS7TCAPTransactionITU::decodeDialogPortion(NamedList& params, DataBlock& data) { DDebug(tcap(),DebugAll,"SS7TCAPTransactionITU::decodeDialogPortion() for transaction with localID=%s [%p]", m_localID.c_str(),this); SS7TCAPError error(SS7TCAP::ITUTCAP); u_int8_t tag = data[0]; // dialog is not present if (tag != SS7TCAPITU::DialogPortionTag) // 0x6b return error; data.cut(-1); // dialog portion is present, decode dialog length int len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } tag = data[0]; if (tag != SS7TCAPITU::ExternalTag) {// 0x28 error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } // decode dialog-as-id ASNObjId oid; len = ASNLib::decodeOID(data,&oid,true); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapDialogueID,oid.toString()); // remove Encoding Tag tag = data[0]; if (tag != SS7TCAPITU::SingleASNTypeCEncTag) {// 0xa0 error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } int dialogPDU = data[0]; // should be DialoguePDU type tag if (dialogPDU != AARQDialogTag && dialogPDU != AAREDialogTag && dialogPDU != ABRTDialogTag) {// 0x60 0x61 0x64 error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } data.cut(-1); params.setParam(s_tcapDialoguePduType,lookup(dialogPDU,s_dialogPDUs)); len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } // check for protocol version or abort-source if (data[0] == SS7TCAPITU::ProtocolVersionTag) { //0x80 bitstring data.cut(-1); if (dialogPDU != ABRTDialogTag) { // decode protocol version String proto; len = ASNLib::decodeBitString(data,&proto,false); if (len != 1) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapProtoVers,proto); } else { u_int8_t abrtSrc = 0xff; len = ASNLib::decodeUINT8(data,&abrtSrc,false); int code = 0x30 | abrtSrc; params.setParam(s_tcapDialogueAbrtSrc,lookup(code,s_resultPDUValues)); } } // check for Application Context Tag length OID tag length if (data[0] == SS7TCAPITU::ApplicationContextTag) { // 0xa1 data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } ASNObjId oid; len = ASNLib::decodeOID(data,&oid,true); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapDialogueAppCtxt,oid.toString()); } if (data[0] == ResultTag) { data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } u_int8_t res = 0xff; len = ASNLib::decodeUINT8(data,&res,true); params.setParam(s_tcapDialogueResult,lookup(res,s_resultPDUValues)); } if (data[0] == ResultDiagnosticTag) { data.cut(-1); len = ASNLib::decodeLength(data); if (data[0] == ResultDiagnosticUserTag || data[0]== ResultDiagnosticProviderTag) { tag = data[0]; data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } u_int8_t res = 0xff; len = ASNLib::decodeUINT8(data,&res,true); if (tag == ResultDiagnosticUserTag) { int code = 0x10 | res; params.setParam(s_tcapDialogueDiag,lookup(code,s_resultPDUValues)); } else { int code = 0x20 | res; params.setParam(s_tcapDialogueDiag,lookup(code,s_resultPDUValues)); } } } // check for user information if (data[0] == SS7TCAPITU::UserInformationTag) {// 0xfd data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } tag = data[0]; if (tag != SS7TCAPITU::ExternalTag) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } // direct Reference tag = data[0]; if (tag == SS7TCAPITU::DirectReferenceTag) { // 0x06 data.cut(-1); ASNObjId oid; len = ASNLib::decodeOID(data,&oid,false); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapReference,oid.toString()); } // data Descriptor tag = data[0]; if (tag == SS7TCAPITU::DataDescriptorTag) { // 0x07 data.cut(-1); String str; int type; len = ASNLib::decodeString(data,&str,&type,false); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } params.setParam(s_tcapDataDesc,str); } // encoding tag = data[0]; if (tag == SS7TCAPITU::SingleASNTypePEncTag || tag == SS7TCAPITU::SingleASNTypeCEncTag || tag == SS7TCAPITU::OctetAlignEncTag || tag == SS7TCAPITU::ArbitraryEncTag) { data.cut(-1); len = ASNLib::decodeLength(data); if (len < 0) { error.setError(SS7TCAPError::Dialog_BadlyStructuredDialoguePortion); return error; } DataBlock d((void*)data.data(0,len),len); data.cut(-len); // put encoding context in hexified form String dataHexified; dataHexified.hexify(d.data(),d.length(),' '); params.setParam(s_tcapEncodingContent,dataHexified); // put encoding identifier switch (tag) { case SS7TCAPITU::SingleASNTypePEncTag: // 0x80 params.setParam(s_tcapEncodingType,"single-ASN1-type-primitive"); break; case SS7TCAPITU::SingleASNTypeCEncTag: // 0xa0 params.setParam(s_tcapEncodingType,"single-ASN1-type-contructor"); break; case SS7TCAPITU::OctetAlignEncTag: // 0x81 params.setParam(s_tcapEncodingType,"octet-aligned"); break; case SS7TCAPITU::ArbitraryEncTag: // 0x82 params.setParam(s_tcapEncodingType,"arbitrary"); break; default: break; } } } #ifdef DEBUG if (s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"SS7TCAPTransactionITU::decodeDialogPortion() - decoded dialog portion",this,params,data); #endif return error; } void SS7TCAPTransactionITU::encodeDialogPortion(NamedList& params, DataBlock& data) { DDebug(tcap(),DebugAll,"SS7TCAPTransactionITU::encodeDialogPortion() for transaction with localID=%s [%p]", m_localID.c_str(),this); DataBlock dialogData; int tag; NamedString* typeStr = params.getParam(s_tcapDialoguePduType); if (TelEngine::null(typeStr)) return; u_int8_t pduType = typeStr->toInteger(s_dialogPDUs); // encode user information DataBlock userInfo; NamedString* val = params.getParam(s_tcapEncodingType); if (!TelEngine::null(val)) { if (*val == "single-ASN1-type-primitive") tag = SS7TCAPITU::SingleASNTypePEncTag; else if (*val == "single-ASN1-type-contructor") tag = SS7TCAPITU::SingleASNTypeCEncTag; else if (*val == "octet-aligned") tag = SS7TCAPITU::OctetAlignEncTag; else if (*val == "arbitrary") tag = SS7TCAPITU::ArbitraryEncTag; val = params.getParam(s_tcapEncodingContent); if (val) { DataBlock db; db.unHexify(val->c_str(),val->length(),' '); db.insert(ASNLib::buildLength(db)); db.insert(DataBlock(&tag,1)); userInfo.insert(db); } } val = params.getParam(s_tcapDataDesc); if (!TelEngine::null(val)) { DataBlock db = ASNLib::encodeString(*val,ASNLib::PRINTABLE_STR,false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPITU::DataDescriptorTag; db.insert(DataBlock(&tag,1)); userInfo.insert(db); } val = params.getParam(s_tcapReference); if (!TelEngine::null(val)) { ASNObjId oid = *val; DataBlock db = ASNLib::encodeOID(oid,false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPITU::DirectReferenceTag; db.insert(DataBlock(&tag,1)); userInfo.insert(db); } if (userInfo.length()) { userInfo.insert(ASNLib::buildLength(userInfo)); tag = SS7TCAPITU::ExternalTag; userInfo.insert(DataBlock(&tag,1)); userInfo.insert(ASNLib::buildLength(userInfo)); tag = SS7TCAPITU::UserInformationTag; userInfo.insert(DataBlock(&tag,1)); dialogData.insert(userInfo); } switch (pduType) { case AAREDialogTag: val = params.getParam(s_tcapDialogueDiag); if (!TelEngine::null(val)) { u_int16_t code = val->toInteger(s_resultPDUValues); DataBlock db = ASNLib::encodeInteger(code % 0x10,true); db.insert(ASNLib::buildLength(db)); if ((code & 0x10) == 0x10) tag = ResultDiagnosticUserTag; else tag = ResultDiagnosticProviderTag; db.insert(DataBlock(&tag,1)); db.insert(ASNLib::buildLength(db)); tag = ResultDiagnosticTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } val = params.getParam(s_tcapDialogueResult); if (!TelEngine::null(val)) { u_int8_t res = val->toInteger(s_resultPDUValues); DataBlock db = ASNLib::encodeInteger(res,true); db.insert(ASNLib::buildLength(db)); tag = ResultTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } case AARQDialogTag: // Application context val = params.getParam(s_tcapDialogueAppCtxt); if (!TelEngine::null(val)) { ASNObjId oid = *val; DataBlock db = ASNLib::encodeOID(oid,true); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPITU::ApplicationContextTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } val = params.getParam(s_tcapProtoVers); if (!TelEngine::null(val) && (val->toInteger() > 0)) { DataBlock db = ASNLib::encodeBitString(*val,false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPITU::ProtocolVersionTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } break; case ABRTDialogTag: val = params.getParam(s_tcapDialogueAbrtSrc); if (!TelEngine::null(val)) { u_int8_t code = val->toInteger(s_resultPDUValues) % 0x30; DataBlock db = ASNLib::encodeInteger(code,false); db.insert(ASNLib::buildLength(db)); tag = SS7TCAPITU::ProtocolVersionTag; db.insert(DataBlock(&tag,1)); dialogData.insert(db); } break; default: return; } dialogData.insert(ASNLib::buildLength(dialogData)); dialogData.insert(DataBlock(&pduType,1)); dialogData.insert(ASNLib::buildLength(dialogData)); tag = SS7TCAPITU::SingleASNTypeCEncTag; dialogData.insert(DataBlock(&tag,1)); val = params.getParam(s_tcapDialogueID); if (TelEngine::null(val)) return; ASNObjId oid = *val; dialogData.insert(ASNLib::encodeOID(oid,true)); dialogData.insert(ASNLib::buildLength(dialogData)); tag = SS7TCAPITU::ExternalTag; dialogData.insert(DataBlock(&tag,1)); dialogData.insert(ASNLib::buildLength(dialogData)); tag = SS7TCAPITU::DialogPortionTag; dialogData.insert(DataBlock(&tag,1)); data.insert(dialogData); params.clearParam(s_tcapDialogPrefix,'.'); #ifdef DEBUG if (s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"SS7TCAPTransactionITU::encodeDialogPortion() - encoded dialog portion",this,params,data); #endif } SS7TCAPError SS7TCAPTransactionITU::decodeComponents(NamedList& params, DataBlock& data) { XDebug(tcap(),DebugAll,"SS7TCAPTransactionITU::decodeComponents() [%p] - data length=%u",this,data.length()); SS7TCAPError error(SS7TCAP::ITUTCAP); if (!data.length()) { params.setParam(s_tcapCompCount,"0"); return error; } u_int8_t tag = data[0]; if (tag != SS7TCAPITU::ComponentPortionTag) { // 0x6c error.setError(SS7TCAPError::General_IncorrectComponentPortion); return error; } data.cut(-1); // decode length of component portion int len = ASNLib::decodeLength(data); bool checkEoC = (len == ASNLib::IndefiniteForm); if (!checkEoC && (len < 0 || len != (int)data.length())) { // the length of the remaining data should be the same as the decoded length error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); return error; } unsigned int compCount = 0; while (data.length()) { if (checkEoC && ASNLib::matchEOC(data) > 0) break; compCount++; // decode component type u_int8_t compType = data[0]; data.cut(-1); // verify component length len = ASNLib::decodeLength(data); if (len < 0 || len > (int)data.length()) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } unsigned int initLength = data.length(); unsigned int compLength = len; // decode invoke id u_int16_t compID; tag = data[0]; bool notDeriv = false; if (tag != SS7TCAPITU::LocalTag) {// 0x02 if (compType == Reject) { ASNLib::decodeNull(data,true); notDeriv = true; } else { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } } else { data.cut(-1); // obtain component ID(s) len = ASNLib::decodeUINT16(data,&compID,false); if (len < 0) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } } String compParam; compPrefix(compParam,compCount,false); // comp IDs shall be decoded according to component type switch (compType) { case Invoke: params.setParam(compParam + "." + s_tcapRemoteCID,String(compID)); if (data[0] == SS7TCAPITU::LinkedIDTag) { data.cut(-1); u_int16_t linkID; len = ASNLib::decodeUINT16(data,&linkID,false); if (len < 0) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } params.setParam(compParam + "." + s_tcapLocalCID,String(compID)); } break; case ReturnResultLast: case ReturnError: case Reject: case ReturnResultNotLast: if (notDeriv) params.setParam(compParam + "." + s_tcapLocalCID,""); else params.setParam(compParam + "." + s_tcapLocalCID,String(compID)); break; default: error.setError(SS7TCAPError::General_UnrecognizedComponentType); break; } const PrimitiveMapping* map = mapCompPrimitivesITU(-1,compType); if (!map) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } params.setParam(compParam + "." + s_tcapCompType,lookup(map->primitive,SS7TCAP::s_compPrimitives,"Unknown")); if (error.error() != SS7TCAPError::NoError) { break; } // decode Operation Code if (compType == Invoke || compType == ReturnResultLast || compType == ReturnResultNotLast) { tag = data[0]; if (tag == SS7TCAPITU::ParameterSeqTag) { data.cut(-1); len = ASNLib::decodeLength(data); } tag = data[0]; if (tag == SS7TCAPITU::LocalTag) { data.cut(-1); int opCode = 0; len = ASNLib::decodeINT32(data,&opCode,false); params.setParam(compParam +"." + s_tcapOpCodeType,"local"); params.setParam(compParam + "." + s_tcapOpCode,String(opCode)); } else if (tag == SS7TCAPITU::GlobalTag) { data.cut(-1); ASNObjId obj; len = ASNLib::decodeOID(data,&obj,false); params.setParam(compParam + "." + s_tcapOpCodeType,"global"); params.setParam(compParam + "." + s_tcapOpCode,obj.toString()); } else if (compType == Invoke) { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } } // decode Error Code if (compType == ReturnError) { tag = data[0]; if (tag == SS7TCAPITU::LocalTag) { data.cut(-1); int opCode = 0; len = ASNLib::decodeINT32(data,&opCode,false); params.setParam(compParam + "." + s_tcapErrCodeType,"local"); params.setParam(compParam + "." + s_tcapErrCode,String(opCode)); } else if (tag == SS7TCAPITU::GlobalTag) { data.cut(-1); ASNObjId obj; len = ASNLib::decodeOID(data,&obj,false); params.setParam(compParam + "." + s_tcapErrCodeType,"global"); params.setParam(compParam + "." + s_tcapErrCode,obj.toString()); } else { error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } } // decode Problem if (compType == Reject) { tag = data[0]; data.cut(-1); u_int16_t problemCode = 0x0 | (tag << 8); u_int8_t code = 0; len = ASNLib::decodeUINT8(data,&code,false); problemCode |= code; params.setParam(compParam + "." + s_tcapProblemCode,String(SS7TCAPError::errorFromCode(tcap()->tcapType(),problemCode))); } else { // decode Parameters (Set or Sequence) as payload int payloadLen = data.length() - (initLength - compLength); DataBlock d((void*)data.data(0,payloadLen),payloadLen); data.cut(-payloadLen); String dataHexified = ""; dataHexified.hexify(d.data(),d.length(),' '); params.setParam(compParam,dataHexified); } if (initLength - data.length() != compLength) { // check we consumed the announced component length error.setError(SS7TCAPError::General_BadlyStructuredCompPortion); break; } } params.setParam(s_tcapCompCount,String(compCount)); #ifdef DEBUG if (tcap() && s_printMsgs && s_extendedDbg && debugAt(DebugAll)) dumpData(DebugAll,tcap(),"Finished decoding message",this,params,data); #endif return error; } void SS7TCAPTransactionITU::encodeComponents(NamedList& params, DataBlock& data) { XDebug(tcap(),DebugAll,"SS7TCAPTransactionITU::encodeComponents() for transaction with localID=%s [%p]",m_localID.c_str(),this); int componentCount = params.getIntValue(s_tcapCompCount,0); DataBlock compData; if (componentCount) { int index = componentCount + 1; while (--index) { DataBlock codedComp; // encode parameters String compParam; compPrefix(compParam,index,false); // Component Type int compPrimitive = lookup(params.getValue(compParam + "." + s_tcapCompType,"Unknown"),SS7TCAP::s_compPrimitives); const PrimitiveMapping* map = mapCompPrimitivesITU(compPrimitive,-1); if (!map) continue; int compType = map->mappedTo; NamedString* value = 0; bool hasPayload = false; if (compType == Reject) { value = params.getParam(compParam + "." + s_tcapProblemCode); if (!TelEngine::null(value)) { u_int16_t codeErr = SS7TCAPError::codeFromError(tcap()->tcapType(),(SS7TCAPError::ErrorType)value->toInteger()); u_int8_t problemTag = (codeErr & 0xff00) >> 8; u_int8_t code = codeErr & 0x000f; DataBlock db(DataBlock(&code,1)); db.insert(ASNLib::buildLength(db)); db.insert(DataBlock(&problemTag,1)); codedComp.insert(db); } else { Debug(tcap(),DebugWarn,"Missing mandatory 'problemCode' information for component with index='%d' from transaction " "with localID=%s [%p]",index,m_localID.c_str(),this); continue; } } else { NamedString* payloadHex = params.getParam(compParam); if (!TelEngine::null(payloadHex)) { DataBlock payload; payload.unHexify(payloadHex->c_str(),payloadHex->length(),' '); codedComp.insert(payload); hasPayload = true; } } // encode Error Code only if ReturnError if (compType == ReturnError) { value = params.getParam(compParam + "." + s_tcapErrCodeType); if (!TelEngine::null(value)) { int tag = 0; DataBlock db; if (*value == "local") { tag = SS7TCAPITU::LocalTag; int errCode = params.getIntValue(compParam + "." + s_tcapErrCode,0); db = ASNLib::encodeInteger(errCode,false); db.insert(ASNLib::buildLength(db)); } else if (*value == "global") { tag = SS7TCAPITU::GlobalTag; ASNObjId oid = String(params.getValue(compParam + "." + s_tcapErrCode)); db = ASNLib::encodeOID(oid,false); db.insert(ASNLib::buildLength(db)); } db.insert(DataBlock(&tag,1)); codedComp.insert(db); } else { Debug(tcap(),DebugWarn,"Missing mandatory 'errorCodeType' information for component with index='%d' from transaction " "with localID=%s [%p]",index,m_localID.c_str(),this); continue; } } // encode Operation Code only if Invoke if (compType == Invoke || compType == ReturnResultNotLast || compType == ReturnResultLast) { value = params.getParam(compParam + "." + s_tcapOpCodeType); if (!TelEngine::null(value)) { int tag = 0; DataBlock db; if (*value == "local") { int opCode = params.getIntValue(compParam + "." + s_tcapOpCode,0); db = ASNLib::encodeInteger(opCode,true); } else if (*value == "global") { ASNObjId oid(params.getValue(compParam + "." + s_tcapOpCode)); db = ASNLib::encodeOID(oid,true); //db.insert(ASNLib::buildLength(db)); } codedComp.insert(db); if (compType != Invoke) { tag = SS7TCAPITU::ParameterSeqTag; codedComp.insert(ASNLib::buildLength(codedComp)); codedComp.insert(DataBlock(&tag,1)); } } else { if (compType == Invoke || hasPayload) { Debug(tcap(),DebugWarn,"Missing mandatory 'operationCodeType' information for component with index='%d' from transaction " "with localID=%s [%p]",index,m_localID.c_str(),this); continue; } } } NamedString* invID = params.getParam(compParam + "." + s_tcapLocalCID); NamedString* linkID = params.getParam(compParam + "." + s_tcapRemoteCID); DataBlock db; u_int8_t val = 0; switch (compType) { case Invoke: if (!TelEngine::null(linkID)) { val = linkID->toInteger(); DataBlock db1; db1.append(&val,sizeof(u_int8_t)); db1.insert(ASNLib::buildLength(db1)); val = SS7TCAPITU::LinkedIDTag; db1.insert(DataBlock(&val,1)); codedComp.insert(db1); } if (!TelEngine::null(invID)) { val = invID->toInteger(); db.append(&val,sizeof(u_int8_t)); db.insert(ASNLib::buildLength(db)); val = SS7TCAPITU::LocalTag; db.insert(DataBlock(&val,1)); } else { Debug(tcap(),DebugWarn,"Missing mandatory 'localCID' information for component with index='%d' from transaction " "with localID=%s [%p]",index,m_localID.c_str(),this); continue; } break; case ReturnResultLast: case ReturnError: case ReturnResultNotLast: if (!TelEngine::null(linkID)) { val = linkID->toInteger(); db.append(&val,sizeof(u_int8_t)); db.insert(ASNLib::buildLength(db)); val = SS7TCAPITU::LocalTag; db.insert(DataBlock(&val,1)); } else { Debug(tcap(),DebugWarn,"Missing mandatory 'remoteCID' information for component with index='%d' from transaction " "with localID=%s [%p]",index,m_localID.c_str(),this); continue; } break; case Reject: if (TelEngine::null(linkID)) linkID = invID; if (!TelEngine::null(linkID)) { val = linkID->toInteger(); db.append(&val,sizeof(u_int8_t)); db.insert(ASNLib::buildLength(db)); val = SS7TCAPITU::LocalTag; db.insert(DataBlock(&val,1)); } else db.insert(ASNLib::encodeNull(true)); break; default: break; } codedComp.insert(db); if(codedComp.length()) { codedComp.insert(ASNLib::buildLength(codedComp)); codedComp.insert(DataBlock(&compType,1)); } params.clearParam(compParam,'.'); // clear all params for this component compData.insert(codedComp); } if (compData.length()) { compData.insert(ASNLib::buildLength(compData)); int tag = SS7TCAPITU::ComponentPortionTag; compData.insert(DataBlock(&tag,1)); data.insert(compData); } } params.clearParam(s_tcapCompPrefix,'.'); } void SS7TCAPTransactionITU::requestContent(NamedList& params, DataBlock &data) { #ifdef DEBUG DDebug(tcap(),DebugAll,"SS7TCAPTransactionITU::requestContent() - for id=%s [%p]",m_localID.c_str(),this); #endif if (m_type == SS7TCAP::TC_P_Abort || m_type == SS7TCAP::TC_U_Abort) encodePAbort(this,params,data); else { requestComponents(params,data); if (dialogPresent()) { if (TelEngine::null(params.getParam(s_tcapDialoguePduType))) handleDialogPortion(params,true); encodeDialogPortion(params,data); } } transactionData(params); } /* vi: set ts=8 sw=4 sts=4 noet: */