All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.restcomm.protocols.ss7.sccp.impl.SccpRoutingControl Maven / Gradle / Ivy

The newest version!
/*
 * TeleStax, Open Source Cloud Communications  Copyright 2012.
 * and individual contributors
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.restcomm.protocols.ss7.sccp.impl;

import javolution.util.FastMap;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.restcomm.protocols.ss7.indicator.RoutingIndicator;
import org.restcomm.protocols.ss7.mtp.Mtp3;
import org.restcomm.protocols.ss7.mtp.Mtp3TransferPrimitive;
import org.restcomm.protocols.ss7.mtp.Mtp3TransferPrimitiveFactory;
import org.restcomm.protocols.ss7.mtp.Mtp3UserPart;
import org.restcomm.protocols.ss7.sccp.LongMessageRule;
import org.restcomm.protocols.ss7.sccp.LongMessageRuleType;
import org.restcomm.protocols.ss7.sccp.Mtp3ServiceAccessPoint;
import org.restcomm.protocols.ss7.sccp.RemoteSignalingPointCode;
import org.restcomm.protocols.ss7.sccp.RemoteSubSystem;
import org.restcomm.protocols.ss7.sccp.SccpConnection;
import org.restcomm.protocols.ss7.sccp.SccpListener;
import org.restcomm.protocols.ss7.sccp.impl.message.EncodingResultData;
import org.restcomm.protocols.ss7.sccp.impl.message.MessageFactoryImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpAddressedMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnAkMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnCcMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnCrMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnCrefMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnDt1MessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnDt2MessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnErrMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnItMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnReferencedMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnRlcMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnRlsdMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnRscMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnRsrMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpConnSegmentableMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpDataMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.message.SccpNoticeMessageImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.ErrorCauseImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.ImportanceImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.ParameterFactoryImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.RefusalCauseImpl;
import org.restcomm.protocols.ss7.sccp.impl.parameter.ReleaseCauseImpl;
import org.restcomm.protocols.ss7.sccp.message.SccpConnMessage;
import org.restcomm.protocols.ss7.sccp.message.SccpDataMessage;
import org.restcomm.protocols.ss7.sccp.message.SccpMessage;
import org.restcomm.protocols.ss7.sccp.message.SccpNoticeMessage;
import org.restcomm.protocols.ss7.sccp.parameter.GlobalTitle;
import org.restcomm.protocols.ss7.sccp.parameter.LocalReference;
import org.restcomm.protocols.ss7.sccp.parameter.RefusalCauseValue;
import org.restcomm.protocols.ss7.sccp.parameter.ReleaseCauseValue;
import org.restcomm.protocols.ss7.sccp.parameter.ReturnCause;
import org.restcomm.protocols.ss7.sccp.parameter.ReturnCauseValue;
import org.restcomm.protocols.ss7.sccp.parameter.SccpAddress;

import java.io.IOException;
import java.util.Map;

import static org.restcomm.protocols.ss7.sccp.impl.message.MessageUtil.getDln;
import static org.restcomm.protocols.ss7.sccp.impl.message.MessageUtil.getSln;
import static org.restcomm.protocols.ss7.sccp.parameter.ErrorCauseValue.LRN_MISMATCH_INCONSISTENT_SOURCE_LRN;
import static org.restcomm.protocols.ss7.sccp.parameter.ErrorCauseValue.SERVICE_CLASS_MISMATCH;
import static org.restcomm.protocols.ss7.sccp.parameter.ReleaseCauseValue.SCCP_FAILURE;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *
 * @author amit bhayani
 * @author sergey vetyutnev
 *
 */
public class SccpRoutingControl implements SccpRoutingCtxInterface {
    private final Logger logger;

    private SccpStackImpl sccpStackImpl = null;
    private SccpProviderImpl sccpProviderImpl = null;

    private SccpManagement sccpManagement = null;

    private MessageFactoryImpl messageFactory;
    private ConcurrentHashMap opcSscCounters = new ConcurrentHashMap();

    public SccpRoutingControl(SccpProviderImpl sccpProviderImpl, SccpStackImpl sccpStackImpl) {
        this.messageFactory = sccpStackImpl.messageFactory;
        this.sccpProviderImpl = sccpProviderImpl;
        this.sccpStackImpl = sccpStackImpl;
        this.logger = Logger.getLogger(SccpRoutingControl.class.getCanonicalName() + "-" + this.sccpStackImpl.name);
    }

    public SccpManagement getSccpManagement() {
        return sccpManagement;
    }

    public void setSccpManagement(SccpManagement sccpManagement) {
        this.sccpManagement = sccpManagement;
    }

    public void start() {
        // NOP for now

    }

    public void stop() {
        // NOP for now

    }

    protected void routeMssgFromMtp(SccpAddressedMessageImpl msg) throws Exception {
        if (this.sccpStackImpl.isPreviewMode()) {
            // we route all incoming message (except management messages) to all registered SccpListeners

            int ssn = msg.getCalledPartyAddress().getSubsystemNumber();
            if (ssn == 1) {
                return;
            }

            FastMap lstListn = this.sccpProviderImpl.getAllSccpListeners();
            for (Map.Entry val : lstListn.entrySet()) {
                SccpListener listener = val.getValue();
                if (msg instanceof SccpDataMessage) {
//                    SccpDataMessage dataMsg = (SccpDataMessage) msg;
//                    listener.onMessage(dataMsg);
                    deliverMessageToSccpUser(listener, (SccpDataMessage) msg);
                }
            }

            return;
        }

        //if the local SCCP or node is in an overload condition, SCRC
        // shall inform SCMG
        int subsystemNumber = msg.getCalledPartyAddress().getSubsystemNumber();
        Integer congestionLevel = this.sccpProviderImpl.getCongestionSsn().get(subsystemNumber);
        if(congestionLevel != null) {
            int inOpc = msg.getIncomingOpc();
            if(congestionLevel > 0) {
                if(subsystemNumber != 1)
                    if(msg instanceof SccpDataMessage) {
                        AtomicInteger aCounter = opcSscCounters.get(inOpc);
                        if(aCounter == null) {
                            opcSscCounters.put(inOpc, new AtomicInteger(0));
                            this.sccpManagement.receivedForCongestionUser(msg, subsystemNumber, congestionLevel);
                        } else if((aCounter.incrementAndGet() % 8) == 0) {
                            this.sccpManagement.receivedForCongestionUser(msg, subsystemNumber, congestionLevel);
                            aCounter.compareAndSet(Integer.MAX_VALUE, 0);
                        }
                    }
            } else if (congestionLevel == 0) {
                AtomicInteger aCounter = opcSscCounters.get(inOpc);
                if(aCounter!=null)
                    aCounter.set(0);
            }
        }

        SccpAddress calledPartyAddress = msg.getCalledPartyAddress();
        RoutingIndicator ri = calledPartyAddress.getAddressIndicator().getRoutingIndicator();
        switch (ri) {
            case ROUTING_BASED_ON_DPC_AND_SSN:
                int ssn = msg.getCalledPartyAddress().getSubsystemNumber();
                if (ssn == 1) {
                    // This is for management
                    if (msg instanceof SccpDataMessage) {
                        this.sccpManagement.onManagementMessage((SccpDataMessage) msg);
                    }
                    return;
                }

                SccpListener listener = this.sccpProviderImpl.getSccpListener(ssn);
                if (listener == null) {
                    // SCCP user with received SSN is not available - Notify Management
                    this.sccpManagement.recdMsgForProhibitedSsn(msg, ssn);

                    if (logger.isEnabledFor(Level.WARN)) {
                        logger.warn(String.format(
                                "Received SccpMessage=%s from MTP but the SSN is not available for local routing", msg));
                    }
                    this.sendSccpError(msg, ReturnCauseValue.SUBSYSTEM_FAILURE, RefusalCauseValue.SUBSYSTEM_FAILURE);
                    return;
                }

                // Notify Listener
                try {
                    if (msg instanceof SccpDataMessage) {
                        if (logger.isDebugEnabled()) {
                            logger.debug(String.format("Local deliver : SCCP Data Message=%s", msg.toString()));
                        }
//                        listener.onMessage((SccpDataMessage) msg);
                        deliverMessageToSccpUser(listener, (SccpDataMessage) msg);
                    } else if (msg instanceof SccpNoticeMessage) {
                        if (logger.isDebugEnabled()) {
                            logger.debug(String.format("Local deliver : SCCP Notice Message=%s", msg.toString()));
                        }
                        listener.onNotice((SccpNoticeMessage) msg);
                    } else if (msg instanceof SccpConnCrMessageImpl) {
                        this.processIncCR(msg);
                    }

                } catch (Exception e) {
                    if (logger.isEnabledFor(Level.WARN)) {
                        logger.warn(String.format(
                                "Exception from the listener side when delivering SccpData to ssn=%d: Message=%s",
                                msg.getOriginLocalSsn(), msg), e);
                    }
                }
                break;
            case ROUTING_BASED_ON_GLOBAL_TITLE:
                sccpStackImpl.ss7ExtSccpDetailedInterface.translationFunction(this, msg);
                break;
            default:
                // This can never happen
                logger.error(String.format("Invalid Routing Indicator received for message=%s from MTP3", msg));
                break;
        }
    }

    protected void routeMssgFromMtpConn(SccpConnMessage msg) throws Exception {
        LocalReference ref = getDln(msg);
        SccpConnectionImpl conn = sccpStackImpl.getConnection(ref);
        if (conn == null) {
            return;
        }

        int ssn = conn.getLocalSsn();

        SccpListener listener = conn.getListener();
        if (listener == null && !conn.isCouplingEnabled() && !(msg instanceof SccpConnRlsdMessageImpl || msg instanceof SccpConnRlcMessageImpl)) {
            // SCCP user with received SSN is not available - Notify Management
            this.sccpManagement.recdMsgForProhibitedSsn(msg, ssn);

            if (logger.isEnabledFor(Level.WARN)) {
                logger.warn(String.format(
                        "Received SccpMessage=%s from MTP but the SSN is not available for local routing", msg));
            }
            this.sendSccpErrorConn(msg, ReleaseCauseValue.SUBSYSTEM_FAILURE);
            return;
        }

        // Notify Listener
        if (logger.isDebugEnabled()) {
            logger.debug(String.format("Local deliver : SCCP Message=%s", msg.toString()));
        }
        this.processCoMessages(msg, conn, listener);
    }

    protected void routeMssgFromSccpUser(SccpAddressedMessageImpl msg) throws Exception {
        if (this.sccpStackImpl.isPreviewMode()) {
            // we drop off local originated message in pereviewMode
            return;
        }

        if (msg instanceof SccpAddressedMessageImpl) {
            this.routeAddressed(msg);
        } else {
            this.routeConn((SccpConnMessage)msg);  // not needed - SccpAddressedMessageImpl is not SccpConnMessage ............
        }
    }

    public void routeMssgFromSccpUserConn(SccpConnMessage msg) throws Exception {
        if (this.sccpStackImpl.isPreviewMode()) {
            // we drop off local originated message in pereviewMode
            return;
        }

        if (msg instanceof SccpAddressedMessageImpl) {
            this.routeAddressed((SccpAddressedMessageImpl)msg);
        } else {
            this.routeConn(msg);
        }
    }

    private long lastCongAnnounseTime;

    protected void send(SccpAddressedMessageImpl message) throws Exception {

        int dpc = message.getOutgoingDpc();
        int sls = message.getSls();

        // outgoing congestion control
        RemoteSignalingPointCode remoteSpc = this.sccpStackImpl.getSccpResource().getRemoteSpcByPC(dpc);
        int currentRestrictionLevel = remoteSpc.getCurrentRestrictionLevel();
        int msgImportance = 8;
        if (message instanceof SccpDataMessageImpl) {
            // UDT, XUDT, LUDT
            msgImportance = 5;
        }
        if (message instanceof SccpNoticeMessageImpl) {
            // UDTS, XUDTS, LUDTS
            msgImportance = 3;
        }
        if (this.sccpStackImpl.isCongControl_blockingOutgoungSccpMessages() && msgImportance < currentRestrictionLevel) {
            // we are dropping a message because of outgoing congestion

            long curTime = System.currentTimeMillis();
            if (lastCongAnnounseTime + 1000 < curTime) {
                lastCongAnnounseTime = curTime;
                logger.warn(String.format("Outgoing congestion control: SCCP: SccpMessage for sending=%s was dropped because of congestion level %d to dpc %d",
                        message, currentRestrictionLevel, dpc));
            }

            this.sendSccpError(message, ReturnCauseValue.NETWORK_CONGESTION, RefusalCauseValue.SUBSYSTEM_CONGESTION);
            return;
        }

        Mtp3ServiceAccessPoint sap = this.sccpStackImpl.router.findMtp3ServiceAccessPoint(dpc, sls, message.getNetworkId());
        if (sap == null) {
            if (logger.isEnabledFor(Level.WARN)) {
                logger.warn(String.format("SccpMessage for sending=%s but no matching dpc=%d & sls=%d SAP found", message, dpc,
                        sls));
            }
            this.sendSccpError(message, ReturnCauseValue.SCCP_FAILURE, RefusalCauseValue.SCCP_FAILURE);
            return;
        }

        Mtp3UserPart mup = this.sccpStackImpl.getMtp3UserPart(sap.getMtp3Id());
        if (mup == null) {
            if (logger.isEnabledFor(Level.WARN)) {
                logger.warn(String.format("SccpMessage for sending=%s but no matching Mtp3UserPart found for Id=%d", message,
                        sap.getMtp3Id()));
            }
            this.sendSccpError(message, ReturnCauseValue.SCCP_FAILURE, RefusalCauseValue.SCCP_FAILURE);
            return;
        }

        LongMessageRule lmr = this.sccpStackImpl.router.findLongMessageRule(dpc);
        LongMessageRuleType lmrt = LongMessageRuleType.LONG_MESSAGE_FORBBIDEN;
        if (lmr != null)
            lmrt = lmr.getLongMessageRuleType();
        EncodingResultData erd = message.encode(sccpStackImpl, lmrt, mup.getMaxUserDataLength(dpc), logger, this.sccpStackImpl.isRemoveSpc(),
                this.sccpStackImpl.getSccpProtocolVersion());
        switch (erd.getEncodingResult()) {
            case Success:
                Mtp3TransferPrimitiveFactory factory = mup.getMtp3TransferPrimitiveFactory();
                if (erd.getSolidData() != null) {
                    // nonsegmented data
                    Mtp3TransferPrimitive msg = factory.createMtp3TransferPrimitive(Mtp3._SI_SERVICE_SCCP, sap.getNi(), 0,
                            sap.getOpc(), dpc, sls, erd.getSolidData());
                    mup.sendMessage(msg);
                } else {
                    // segmented data
                    for (byte[] bf : erd.getSegementedData()) {
                        Mtp3TransferPrimitive msg = factory.createMtp3TransferPrimitive(Mtp3._SI_SERVICE_SCCP, sap.getNi(), 0,
                                sap.getOpc(), dpc, sls, bf);
                        mup.sendMessage(msg);
                    }
                }
                return;

            case ReturnFailure:
                this.sendSccpError(message, erd.getReturnCause(), RefusalCauseValue.SUBSYSTEM_FAILURE);
                return;

            default:
                String em = String.format("Error %s when encoding a SccpMessage\n%s", erd.getEncodingResult().toString(),
                        message.toString());
                if (logger.isEnabledFor(Level.WARN)) {
                    logger.warn(em);
                }
                throw new IOException(em);
        }
    }

    public ReleaseCauseValue sendConn(SccpConnMessage connMessage) throws Exception {
        SccpMessageImpl message = (SccpMessageImpl)connMessage;
        int dpc = message.getOutgoingDpc();
        int sls = message.getSls();

        // outgoing congestion control
        RemoteSignalingPointCode remoteSpc = this.sccpStackImpl.getSccpResource().getRemoteSpcByPC(dpc);
        int currentRestrictionLevel = remoteSpc.getCurrentRestrictionLevel();
        int msgImportance = updateImportance(connMessage);

        if (this.sccpStackImpl.isCongControl_blockingOutgoungSccpMessages() && msgImportance < currentRestrictionLevel) {
            // we are dropping a message because of outgoing congestion

            long curTime = System.currentTimeMillis();
            if (lastCongAnnounseTime + 1000 < curTime) {
                lastCongAnnounseTime = curTime;
                logger.warn(String.format("Outgoing congestion control: SCCP: SccpMessage for sending=%s was dropped because of congestion level %d to dpc %d",
                        message, currentRestrictionLevel, dpc));
            }
            return ReleaseCauseValue.SUBSYSTEM_CONGESTION;
        }

        Mtp3ServiceAccessPoint sap = this.sccpStackImpl.router.findMtp3ServiceAccessPoint(dpc, sls, message.getNetworkId());
        if (sap == null) {
            if (logger.isEnabledFor(Level.WARN)) {
                logger.warn(String.format("SccpMessage for sending=%s but no matching dpc=%d & sls=%d SAP found", message, dpc,
                        sls));
            }
            return SCCP_FAILURE;
        }

        Mtp3UserPart mup = this.sccpStackImpl.getMtp3UserPart(sap.getMtp3Id());
        if (mup == null) {
            if (logger.isEnabledFor(Level.WARN)) {
                logger.warn(String.format("SccpMessage for sending=%s but no matching Mtp3UserPart found for Id=%d", message,
                        sap.getMtp3Id()));
            }
            return SCCP_FAILURE;
        }

        LongMessageRuleType lmrt = LongMessageRuleType.LONG_MESSAGE_FORBBIDEN;
        EncodingResultData erd = message.encode(sccpStackImpl, lmrt, mup.getMaxUserDataLength(dpc), logger, this.sccpStackImpl.isRemoveSpc(),
                this.sccpStackImpl.getSccpProtocolVersion());
        switch (erd.getEncodingResult()) {
            case Success:
                Mtp3TransferPrimitiveFactory factory = mup.getMtp3TransferPrimitiveFactory();
                Mtp3TransferPrimitive msg = factory.createMtp3TransferPrimitive(Mtp3._SI_SERVICE_SCCP, sap.getNi(), 0,
                        sap.getOpc(), dpc, sls, erd.getSolidData());
                mup.sendMessage(msg);

//                if (erd.getSolidData() != null) {
//                    // nonsegmented data
//                    Mtp3TransferPrimitive msg = factory.createMtp3TransferPrimitive(Mtp3._SI_SERVICE_SCCP, sap.getNi(), 0,
//                            sap.getOpc(), dpc, sls, erd.getSolidData());
//                    mup.sendMessage(msg);
//                } else {
//                    // segmented data
//                    for (byte[] bf : erd.getSegementedData()) {
//                        Mtp3TransferPrimitive msg = factory.createMtp3TransferPrimitive(Mtp3._SI_SERVICE_SCCP, sap.getNi(), 0,
//                                sap.getOpc(), dpc, sls, bf);
//                        mup.sendMessage(msg);
//                    }
//                }
                return null;

            case ReturnFailure:
//                return erd.getReturnCause();
                return ReleaseCauseValue.SUBSYSTEM_FAILURE;

            default:
                String em = String.format("Error %s when encoding a SccpMessage\n%s", erd.getEncodingResult().toString(),
                        message.toString());
                if (logger.isEnabledFor(Level.WARN)) {
                    logger.warn(em);
                }
                throw new IOException(em);
        }
    }

    private int updateImportance(SccpConnMessage connMessage) {
        if (connMessage instanceof SccpConnCrMessageImpl) {
            SccpConnCrMessageImpl cr = (SccpConnCrMessageImpl) connMessage;
            cr.setImportance(new ImportanceImpl((byte)3));
            return cr.getImportance().getValue();
        } else if (connMessage instanceof SccpConnSegmentableMessageImpl) {
            return 5;
        } else {
            return 8;
        }
    }

    protected ReturnCauseValue sendManagementMessage(SccpDataMessageImpl message) throws Exception {
        int dpc = message.getOutgoingDpc();

        Mtp3ServiceAccessPoint sap = this.sccpStackImpl.router.findMtp3ServiceAccessPoint(dpc, 0);
        if (sap == null) {
            if (logger.isEnabledFor(Level.WARN)) {
                logger.warn(String.format("Sccp management message for sending=%s but no matching dpc=%d SAP found", message,
                        dpc));
            }
            return ReturnCauseValue.SCCP_FAILURE;
        }

        Mtp3UserPart mup = this.sccpStackImpl.getMtp3UserPart(sap.getMtp3Id());
        if (mup == null) {
            if (logger.isEnabledFor(Level.WARN)) {
                logger.warn(String.format("Sccp management message for sending=%s but no matching Mtp3UserPart found for Id=%d", message,
                        sap.getMtp3Id()));
            }
            return ReturnCauseValue.SCCP_FAILURE;
        }

        LongMessageRuleType lmrt = LongMessageRuleType.LONG_MESSAGE_FORBBIDEN;
        EncodingResultData erd = message.encode(sccpStackImpl, lmrt, mup.getMaxUserDataLength(dpc), logger, this.sccpStackImpl.isRemoveSpc(),
                this.sccpStackImpl.getSccpProtocolVersion());
        switch (erd.getEncodingResult()) {
            case Success:
                Mtp3TransferPrimitiveFactory factory = mup.getMtp3TransferPrimitiveFactory();
                if (erd.getSolidData() != null) {
                    // nonsegmented data
                    Mtp3TransferPrimitive msg = factory.createMtp3TransferPrimitive(Mtp3._SI_SERVICE_SCCP, sap.getNi(), 0,
                            sap.getOpc(), dpc, 0, erd.getSolidData());
                    mup.sendMessage(msg);
                } else {
                    // segmented data - not possible for a management message
                    if (logger.isEnabledFor(Level.WARN)) {
                        logger.warn(String.format(
                                "Sccp management message for sending=%s was encoded with segments, it is forbidded", message));
                    }
                    return ReturnCauseValue.SCCP_FAILURE;
                }
                return null;

            case ReturnFailure:
                return erd.getReturnCause();

            default:
                String em = String.format("Error %s when encoding a SccpMessage\n%s", erd.getEncodingResult().toString(),
                        message.toString());
                if (logger.isEnabledFor(Level.WARN)) {
                    logger.warn(em);
                }
                throw new IOException(em);
        }
    }

    public void routeAddressed(SccpAddressedMessageImpl msg) throws Exception {
        SccpAddress calledPartyAddress = msg.getCalledPartyAddress();

        int dpc = calledPartyAddress.getSignalingPointCode();
        int ssn = calledPartyAddress.getSubsystemNumber();
        GlobalTitle gt = calledPartyAddress.getGlobalTitle();

        if (calledPartyAddress.getAddressIndicator().isPCPresent()) {
            // DPC present

            if (this.sccpStackImpl.router.spcIsLocal(dpc)) {
                // This message is for local routing

                if (ssn > 0) {
                    // if a non-zero SSN is present but not the GT (case 2 a) of
                    // 2.2.2), then the message is passed based on the message
                    // type to either connection-oriented control or
                    // connectionless control and based on the availability of
                    // the subsystem;
                    if (ssn == 1) {
                        // This is for management
                        if (msg instanceof SccpDataMessage) {
                            this.sccpManagement.onManagementMessage((SccpDataMessage) msg);
                        }
                        return;
                    }

                    SccpListener listener = this.sccpProviderImpl.getSccpListener(ssn);
                    if (listener == null) {
                        if (logger.isEnabledFor(Level.WARN)) {
                            logger.warn(String.format(
                                    "Received SccpMessage=%s for routing but the SSN is not available for local routing", msg));
                        }
                        this.sendSccpError(msg, ReturnCauseValue.SUBSYSTEM_FAILURE, RefusalCauseValue.SUBSYSTEM_FAILURE);
                        return;
                    }

                    // Notify Listener
                    try {
                        // JIC: user may behave bad and throw something here.
                        if (msg instanceof SccpDataMessage) {
                            if (logger.isDebugEnabled()) {
                                logger.debug(String.format("Local deliver : SCCP Data Message=%s", msg.toString()));
                            }
//                            listener.onMessage((SccpDataMessage) msg);
                            deliverMessageToSccpUser(listener, (SccpDataMessage) msg);
                        } else if (msg instanceof SccpNoticeMessage) {
                            if (logger.isDebugEnabled()) {
                                logger.debug(String.format("Local deliver : SCCP Notice Message=%s", msg.toString()));
                            }
                            listener.onNotice((SccpNoticeMessage) msg);
                        } else if (msg instanceof SccpConnCrMessageImpl) {
                            msg.setIsIncoming(true);
                            this.processIncCR(msg);

                            // handling of CR messages after address translation
//                            SccpConnCrMessageImpl cr = (SccpConnCrMessageImpl) msg;
//                            LocalReference ref = (!msg.getIsMtpOriginated()) ? getSln(cr) : getDln(cr);
//                            SccpConnectionImpl conn = sccpStackImpl.getConnection(ref);
//                            conn.receiveMessage(cr);
                        }
                    } catch (Exception e) {
                        if (logger.isEnabledFor(Level.WARN)) {
                            logger.warn(String.format(
                                    "Exception from the listener side when delivering SccpData to ssn=%d: Message=%s",
                                    msg.getOriginLocalSsn(), msg), e);
                        }
                    }
                } else if (gt != null) {
                    // if the GT is present but no SSN or a zero SSN is present
                    // (case 2 b) of 2.2.2), then the message is passed to the
                    // translation function;

                    if (calledPartyAddress.isTranslated()) {
                        // Called address already translated once. This is loop
                        // condition and error
                        logger.error(String
                                .format("Droping message. Received SCCPMessage=%s for routing but CalledPartyAddress is already translated once",
                                        msg));
                        this.sendSccpError(msg, ReturnCauseValue.SCCP_FAILURE, RefusalCauseValue.SCCP_FAILURE);
                        return;
                    }

                    sccpStackImpl.ss7ExtSccpDetailedInterface.translationFunction(this, msg);

                } else {
                    // if an SSN equal to zero is present but not a GT (case 2
                    // d) of 2.2.2), then the address information is incomplete
                    // and the message shall be discarded. This abnormality is
                    // similar to the one described in 3.8.3.3, item 1) b6.

                    logger.error(String.format("Received SCCPMessage=%s for routing, but neither SSN nor GT present", msg));
                    this.sendSccpError(msg, ReturnCauseValue.NO_TRANSLATION_FOR_NATURE , RefusalCauseValue.NO_TRANSLATION_FOR_AN_ADDRESS_OF_SUCH_NATURE);
                }

            } else {
                // DPC present but its not local pointcode. This message should be Tx to MTP

                // Check if the DPC is not prohibited
                RemoteSignalingPointCode remoteSpc = this.sccpStackImpl.getSccpResource().getRemoteSpcByPC(dpc);
                if (remoteSpc == null) {
                    if (logger.isEnabledFor(Level.WARN)) {
                        logger.warn(String.format(
                                "Received SccpMessage=%s for routing but no Remote Signaling Pointcode = %d resource defined ",
                                msg, dpc));
                    }
                    this.sendSccpError(msg, ReturnCauseValue.SCCP_FAILURE, RefusalCauseValue.SCCP_FAILURE);
                    return;
                }
                if (remoteSpc.isRemoteSpcProhibited()) {
                    if (logger.isEnabledFor(Level.WARN)) {
                        logger.warn(String.format(
                                "Received SccpMessage=%s for routing but Remote Signaling Pointcode = %d is prohibited", msg,
                                dpc));
                    }
                    this.sendSccpError(msg, ReturnCauseValue.MTP_FAILURE, RefusalCauseValue.DESTINATION_INACCESSIBLE);
                    return;
                }

                if (ssn > 1) { // was: ssn > 1 ???
                    if (calledPartyAddress.getAddressIndicator().getRoutingIndicator() == RoutingIndicator.ROUTING_BASED_ON_DPC_AND_SSN) {
                        // if a non-zero SSN is present but not the GT (case 2a) of 2.2.2),
                        // then the called party address provided shall
                        // contain this SSN and the routing indicator shall be set
                        // to "Route on SSN"; See 2.2.2.1 point 2 of ITU-T Q.714
                        // If routing based on SSN, check remote SSN is available
                        RemoteSubSystem remoteSsn = this.sccpStackImpl.getSccpResource().getRemoteSsn(dpc,
                                calledPartyAddress.getSubsystemNumber());
                        if (remoteSsn == null) {
                            if (logger.isEnabledFor(Level.WARN)) {
                                logger.warn(String.format(
                                        "Received SCCPMessage=%s for routing, but no Remote SubSystem = %d resource defined ",
                                        msg, calledPartyAddress.getSubsystemNumber()));
                            }
                            // Routing failed return error
                            this.sendSccpError(msg, ReturnCauseValue.SCCP_FAILURE, RefusalCauseValue.SCCP_FAILURE);
                            return;
                        }

                        if (remoteSsn.isRemoteSsnProhibited()) {
                            if (logger.isEnabledFor(Level.WARN)) {
                                logger.warn(String.format(
                                        "Routing of Sccp Message=%s failed as Remote SubSystem = %d is prohibited ", msg,
                                        calledPartyAddress.getSubsystemNumber()));
                            }
                            this.sendSccpError(msg, ReturnCauseValue.SUBSYSTEM_FAILURE, RefusalCauseValue.SUBSYSTEM_FAILURE);
                            return;
                        }
                    }

                    // send to MTP
                    if (logger.isDebugEnabled()) {
                        logger.debug(String.format("Tx : SCCP Message=%s", msg.toString()));
                    }
                    this.sendMessageToMtp(msg);
                } else if (gt != null) {

                    // if the GT is present but no SSN or a zero SSN is present
                    // (case 2 b) of 2.2.2), then the DPC identifies where the
                    // global title translation occurs. The called party address
                    // provided shall contain this GT and the routing indicator
                    // shall be set to "Route on GT"; See 2.2.2.1 point 3 of
                    // ITU-T Q.714

                    // send to MTP
                    if (logger.isDebugEnabled()) {
                        logger.debug(String.format("Tx : SCCP Message=%s", msg.toString()));
                    }
                    this.sendMessageToMtp(msg);
                } else {

                    logger.error(String.format("Received SCCPMessage=%s for routing, but neither SSN nor GT present", msg));
                    this.sendSccpError(msg, ReturnCauseValue.NO_TRANSLATION_FOR_NATURE, RefusalCauseValue.NO_TRANSLATION_FOR_AN_ADDRESS_OF_SUCH_NATURE);
                }
            }
        } else {
            // DPC not present

            // If the DPC is not present, (case 3 of 2.2.2), then a global title
            // translation is required before the message can be sent out.
            // Translation results in a DPC and possibly a new SSN or new GT or
            // both.

            if (gt == null) {
                // No DPC, and no GT. This is insufficient information
                if (logger.isEnabledFor(Level.WARN)) {
                    logger.warn(String
                            .format("Received SccpMessage=%s for routing from local SCCP user part but no pointcode and no GT or SSN included",
                                    msg, dpc));
                }
                this.sendSccpError(msg, ReturnCauseValue.NO_TRANSLATION_FOR_NATURE, RefusalCauseValue.NO_TRANSLATION_FOR_AN_ADDRESS_OF_SUCH_NATURE);
                return;
            }

            if (calledPartyAddress.isTranslated()) {
                // Called address already translated once. This is loop
                // condition and error
                logger.error(String
                        .format("Droping message. Received SCCPMessage=%s for Routing , but CalledPartyAddress is already translated once",
                                msg));
                this.sendSccpError(msg, ReturnCauseValue.SCCP_FAILURE, RefusalCauseValue.SCCP_FAILURE);
                return;
            }

            sccpStackImpl.ss7ExtSccpDetailedInterface.translationFunction(this, msg);
        }
    }

    private void routeConn(SccpConnMessage msg) throws Exception {
        // we have only local originated message here
        LocalReference ref = getSln(msg);

        SccpConnectionImpl conn = sccpStackImpl.getConnection(ref);
        if (conn == null) {
            logger.error(String
                    .format("Dropping message. Received SCCPMessage=%s for routing but can't find connection by local reference %s in this message",
                            msg, ref));
            return;
        }

        Integer dpc = conn.getRemoteDpc();
        if (dpc == null) {
            logger.error(String
                    .format("Dropping message. Received SCCPMessage=%s for routing but can't find remote DPC",
                            msg));
            return;
        }

        if (this.sccpStackImpl.router.spcIsLocal(dpc)) {
            // Notify Listener
            ((SccpMessageImpl)msg).setIsIncoming(true);
            deliverMessageToSccpUser(null, msg);

//            try {
//                if (msg instanceof SccpConnCcMessageImpl) {
//                    conn.receiveMessage(msg);
//                    listener.onConnectConfirm(conn, ((SccpConnCcMessageImpl) msg).getUserData());
//
//                } else if (msg instanceof SccpConnRlsdMessageImpl) {
//                    SccpConnRlsdMessageImpl rlsd = (SccpConnRlsdMessageImpl) msg;
//
//                    if (rlsd.getSourceLocalReferenceNumber().getValue() == conn.getRemoteReference().getValue()) {
//                        listener.onDisconnectIndication(conn, rlsd.getReleaseCause(), rlsd.getUserData());
//                        sccpStackImpl.removeConnection(ref);
//
//                        SccpConnRlcMessageImpl rlc = new SccpConnRlcMessageImpl(conn.getSls(), conn.getLocalSsn());
//                        rlc.setSourceLocalReferenceNumber(conn.getLocalReference());
//                        rlc.setDestinationLocalReferenceNumber(conn.getRemoteReference());
//                        rlc.setOutgoingDpc(conn.getRemoteDpc());
//                        sendConn(rlc);
//
//                    } else {
//                        conn.sendErr(new ErrorCauseImpl(LRN_MISMATCH_INCONSISTENT_SOURCE_LRN));
//                    }
//                } else if (msg instanceof SccpConnRlcMessageImpl) {
//                    sccpStackImpl.removeConnection(ref);
//                    listener.onDisconnectConfirm(conn);
//
//                } else if (msg instanceof SccpConnCrefMessageImpl) {
//                    SccpConnCrefMessageImpl cref = (SccpConnCrefMessageImpl) msg;
//                    if (!conn.isCouplingEnabled()) {
//                        sccpStackImpl.removeConnection(ref);
//                        listener.onDisconnectIndication(conn, cref.getRefusalCause(), cref.getUserData());
//                    } else {
//                        conn.receiveMessage(msg);
//                    }
//
//                } else if (msg instanceof SccpConnRsrMessageImpl) {
//                    if (logger.isDebugEnabled()) {
//                        logger.debug(String.format("Local deliver : SCCP RSR Message=%s", msg.toString()));
//                    }
//
//                    SccpConnRsrMessageImpl rsr = (SccpConnRsrMessageImpl) msg;
//                    conn.receiveMessage(rsr);
//                    listener.onResetIndication(conn, rsr.getResetCause());
//
//                } else if (msg instanceof SccpConnRscMessageImpl) {
//                    if (logger.isDebugEnabled()) {
//                        logger.debug(String.format("Local deliver : SCCP RSC Message=%s", msg.toString()));
//                    }
//                    conn.receiveMessage(msg);
//                    listener.onResetConfirm(conn);
//                } else if (msg instanceof SccpConnDt1MessageImpl || msg instanceof SccpConnDt2MessageImpl
//                        || msg instanceof SccpConnAkMessageImpl || msg instanceof SccpConnAkMessageImpl) {
//                    conn.receiveMessage(msg);
//                } else if (msg instanceof SccpConnItMessageImpl) {
//                    conn.receiveMessage(msg);
//                } else if (msg instanceof SccpConnErrMessageImpl) {
//                    SccpConnErrMessageImpl err = (SccpConnErrMessageImpl) msg;
//                    if (!conn.isCouplingEnabled()) {
//
//                        if (err.getErrorCause().getValue() != null && err.getErrorCause().getValue() == SERVICE_CLASS_MISMATCH) {
//                            listener.onDisconnectIndication(conn, err.getErrorCause());
//                            conn.disconnect(new ReleaseCauseImpl(SCCP_FAILURE), new byte[] {});
//                        } else {
//                            listener.onDisconnectIndication(conn, err.getErrorCause());
//                            sccpStackImpl.removeConnection(ref);
//                        }
//
//                    } else {
//                        conn.receiveMessage(msg);
//                    }
//                }
//
//            } catch (Exception e) {
//                if (logger.isEnabledFor(Level.WARN)) {
//                    logger.warn(String.format(
//                            "Exception from the listener side when delivering SccpData to ssn=%d: Message=%s",
//                            msg.getOriginLocalSsn(), msg), e);
//                }
//            }
        } else {
            // sending message via connection to remote node, ssn is unknown
            // DPC present but its not local pointcode. This message should be Tx to MTP

            // Check if the DPC is not prohibited
            RemoteSignalingPointCode remoteSpc = this.sccpStackImpl.getSccpResource().getRemoteSpcByPC(dpc);
            if (remoteSpc == null) {
                if (logger.isEnabledFor(Level.WARN)) {
                    logger.warn(String.format(
                            "Received SccpMessage=%s for routing but no Remote Signaling Pointcode = %d resource defined ",
                            msg, dpc));
                }
                this.sendSccpErrorConn(msg, SCCP_FAILURE);
                return;
            }
            if (remoteSpc.isRemoteSpcProhibited()) {
                if (logger.isEnabledFor(Level.WARN)) {
                    logger.warn(String.format(
                            "Received SccpMessage=%s for routing but Remote Signaling Pointcode = %d is prohibited", msg, dpc));
                }
                this.sendSccpErrorConn(msg, ReleaseCauseValue.MTP_FAILURE);
                return;
            }

            Integer ssn = conn.getRemoteSsn();
            if (ssn == null || ssn > 0) {
                if (ssn != null && ssn > 1) {
                    // if a non-zero SSN is present but not the GT (case 2a) of 2.2.2),
                    // then the called party address provided shall
                    // contain this SSN and the routing indicator shall be set
                    // to "Route on SSN"; See 2.2.2.1 point 2 of ITU-T Q.714
                    // If routing based on SSN, check remote SSN is available
                    RemoteSubSystem remoteSsn = this.sccpStackImpl.getSccpResource().getRemoteSsn(dpc, ssn);
                    if (remoteSsn == null) {
                        if (logger.isEnabledFor(Level.WARN)) {
                            logger.warn(String.format(
                                    "Received SCCPMessage=%s for routing, but no Remote SubSystem = %d resource defined ", msg,
                                    ssn));
                        }
                        // Routing failed return error
                        this.sendSccpErrorConn(msg, SCCP_FAILURE);
                        return;
                    }

                    if (remoteSsn.isRemoteSsnProhibited()) {
                        if (logger.isEnabledFor(Level.WARN)) {
                            logger.warn(String.format(
                                    "Routing of Sccp Message=%s failed as Remote SubSystem = %d is prohibited ", msg, ssn));
                        }
                        this.sendSccpErrorConn(msg, ReleaseCauseValue.SUBSYSTEM_FAILURE);
                        return;
                    }
                }

                // send to MTP
                if (logger.isDebugEnabled()) {
                    logger.debug(String.format("Tx : SCCP Message=%s", msg.toString()));
                }
                this.sendMessageToMtpConn(msg);
            } else {

                logger.error(String.format("Received SCCPMessage=%s for routing, but neither SSN nor GT present", msg));
                this.sendSccpErrorConn(msg, SCCP_FAILURE);
            }
        }
    }

    private void deliverMessageToSccpUser(SccpListener listener, SccpMessage msg) {
        if (msg.getIsMtpOriginated() && msg instanceof SccpDataMessage) {
            listener.onMessage((SccpDataMessage) msg);
        } else {
            // we need to make asynch delivering for local user originated messages
            int seqControl = msg.getSls();
            SccpTransferDeliveryHandler hdl = new SccpTransferDeliveryHandler(msg, listener);
            seqControl = seqControl & this.sccpStackImpl.slsFilter;
            this.sccpStackImpl.msgDeliveryExecutors[this.sccpStackImpl.slsTable[seqControl]].execute(hdl);
        }
    }

    protected void sendMessageToMtp(SccpAddressedMessageImpl msg) throws Exception {

        msg.setOutgoingDpc(msg.getCalledPartyAddress().getSignalingPointCode());

        // if (msg.getSccpCreatesSls()) {
        // msg.setSls(this.sccpStackImpl.newSls());
        // }

        this.send(msg);
    }

    protected void sendMessageToMtpConn(SccpConnMessage message) throws Exception {
        if (message instanceof SccpConnCrMessageImpl) {
            throw new IllegalArgumentException();
        }
        SccpMessageImpl msg = (SccpMessageImpl)message;

        LocalReference sln = getSln(message);
        SccpConnectionImpl conn = sccpStackImpl.getConnection(sln);

        msg.setOutgoingDpc(conn.getRemoteDpc());

        // if (msg.getSccpCreatesSls()) {
        // msg.setSls(this.sccpStackImpl.newSls());
        // }

        ReleaseCauseValue er = this.sendConn(message);
        if (er != null) {
            this.sendSccpErrorConn(message, er);
        }
    }

    public void sendSccpError(SccpAddressedMessageImpl msg, ReturnCauseValue returnCauseInt, RefusalCauseValue refusalCauseInt) throws Exception {
        SccpMessage ans = null;
        if (!(msg instanceof SccpConnCrMessageImpl)) {
            // sending only if "ReturnMessageOnError" flag of the origin message
            if (!msg.getReturnMessageOnError())
                return;

            // in case we did not consume and this message has arrived from
            // other end.... we have to reply in some way Q.714 4.2 for now
            // SccpNoticeMessageImpl ans = null;
            // not sure if its proper
            ReturnCause returnCause = ((ParameterFactoryImpl) this.sccpProviderImpl.getParameterFactory())
                    .createReturnCause(returnCauseInt);
            if (msg instanceof SccpDataMessageImpl) {
                SccpDataMessageImpl msgData = (SccpDataMessageImpl) msg;
                ans = (SccpNoticeMessageImpl) messageFactory.createNoticeMessage(msg.getType(), returnCause,
                        msg.getCallingPartyAddress(), msg.getCalledPartyAddress(), msgData.getData(), msgData.getHopCounter(),
                        msgData.getImportance());
                //} else {
                // TODO: Implement return errors for connection-oriented messages
            }
        } else {
            SccpConnCrMessageImpl msgCr = (SccpConnCrMessageImpl)msg;
            SccpConnCrefMessageImpl answer = new SccpConnCrefMessageImpl(msgCr.getSls(), msgCr.getOriginLocalSsn());
            answer.setDestinationLocalReferenceNumber(msgCr.getSourceLocalReferenceNumber());
            if (msgCr.getCallingPartyAddress() != null) {
                answer.setCalledPartyAddress(msgCr.getCallingPartyAddress());
            }
            answer.setRefusalCause(new RefusalCauseImpl(refusalCauseInt));
            answer.setImportance(msgCr.getImportance());

            if (msg.getIncomingOpc() != -1) {
                answer.setOutgoingDpc(msgCr.getIncomingOpc());
            } else {
                // when both users are on the same stack
                answer.setOutgoingDpc(msgCr.getCalledPartyAddress().getSignalingPointCode());
            }
            ans = answer;
        }

        if (ans != null) {
            if (msg.getIsMtpOriginated()) {

                // send to MTP3
                if (logger.isDebugEnabled()) {
                    logger.debug(String.format("sendSccpError to a remote user: SCCP Message=%s", ans.toString()));
                }
                if (ans instanceof SccpAddressedMessageImpl) {
                    this.routeAddressed((SccpAddressedMessageImpl)ans);
                } else {
                    this.routeConn((SccpConnMessage)ans);
                }
            } else {

                // deliver locally
                if (logger.isDebugEnabled()) {
                    logger.debug(String.format("sendSccpError to a local user: SCCP Message=%s", ans.toString()));
                }
                SccpListener listener = this.sccpProviderImpl.getSccpListener(msg.getOriginLocalSsn());
                if (listener != null) {
                    if (!(msg instanceof SccpConnCrMessageImpl)) {
                        try {
                            listener.onNotice((SccpNoticeMessage)ans);
                        } catch (Exception e) {
                            if (logger.isEnabledFor(Level.WARN)) {
                                logger.warn(String.format(
                                        "Exception from the listener side when delivering SccpNotice to ssn=%d: Message=%s",
                                        msg.getOriginLocalSsn(), msg), e);
                            }
                        }
                    } else {
                        try {
                            SccpConnection conn = sccpStackImpl.getConnection(((SccpConnCrMessageImpl)msg).getSourceLocalReferenceNumber());
                            listener.onDisconnectIndication(conn, ((SccpConnCrefMessageImpl)ans).getRefusalCause(), new byte[]{});
                        } catch (Exception e) {
                            if (logger.isEnabledFor(Level.WARN)) {
                                logger.warn(String.format(
                                        "Exception from the listener side when delivering CREF message to ssn=%d: Message=%s",
                                        msg.getOriginLocalSsn(), msg), e);
                            }
                        }
                    }
                }
            }
        }
    }

    private void sendSccpErrorConn(SccpConnMessage msg, ReleaseCauseValue cause) throws Exception {
        if (msg instanceof SccpConnRlsdMessageImpl) {
            return;
        }
        LocalReference ref = (!msg.getIsIncoming()) ? getSln(msg) : getDln(msg);

        SccpConnectionImpl conn = sccpStackImpl.getConnection(ref);
        conn.disconnect(new ReleaseCauseImpl(cause), new byte[] {});
//        conn.setState(CLOSED);
    }

    private void processIncCR(SccpAddressedMessageImpl msg) throws Exception {
        SccpConnCrMessageImpl msgCr = (SccpConnCrMessageImpl)msg;
        SccpConnectionImpl conn = sccpStackImpl.newConnection(msgCr.getCalledPartyAddress().getSubsystemNumber(), msgCr.getProtocolClass());
        if (msgCr.getCallingPartyAddress() != null) {
            conn.remoteSsn = msgCr.getCallingPartyAddress().getSubsystemNumber();
        }
        conn.receiveMessage(msgCr);
        conn.getListener().onConnectIndication(conn, msgCr.getCalledPartyAddress(), msgCr.getCallingPartyAddress(),
                msgCr.getProtocolClass(), msgCr.getCredit(), msgCr.getUserData(), msgCr.getImportance());
    }

    private void processCoMessages(SccpConnMessage msg, SccpConnectionImpl conn, SccpListener listener) {
        try {
            if (msg instanceof SccpConnCcMessageImpl) {
                conn.receiveMessage(msg);
                if (listener != null) {
                    listener.onConnectConfirm(conn, ((SccpConnCcMessageImpl) msg).getUserData());
                }

            } else if (msg instanceof SccpConnRlsdMessageImpl) {
                if (!checkSourceLocalReferenceNumber(msg, conn)) {
                    conn.sendErr(new ErrorCauseImpl(LRN_MISMATCH_INCONSISTENT_SOURCE_LRN));
                    return;
                }
                SccpConnRlsdMessageImpl rlsd = (SccpConnRlsdMessageImpl)msg;

                if (!conn.isCouplingEnabled()) {

                    if (listener != null) {
                        listener.onDisconnectIndication(conn, rlsd.getReleaseCause(), rlsd.getUserData());
                    }
                    conn.receiveMessage(msg);

                    sccpStackImpl.removeConnection(conn.getLocalReference());
                } else {
                    conn.receiveMessage(msg);
                }

            } else if (msg instanceof SccpConnRlcMessageImpl) {
                if (!checkSourceLocalReferenceNumber(msg, conn)) {
                    logger.error(String
                            .format("Dropping message. Received SCCPMessage=%s but source LRN doesn't match remote LRN stored for connection",
                                    msg));
                    return;
                }

                if (!conn.isCouplingEnabled()) {
                    sccpStackImpl.removeConnection(conn.getLocalReference());

                    if (listener != null) {
                        listener.onDisconnectConfirm(conn);
                    }
                } else {
                    conn.receiveMessage(msg);
                }

            } else if (msg instanceof SccpConnCrefMessageImpl) {
                SccpConnCrefMessageImpl cref = (SccpConnCrefMessageImpl)msg;
                if (!conn.isCouplingEnabled()) {
                    sccpStackImpl.removeConnection(conn.getLocalReference());
                    listener.onDisconnectIndication(conn, cref.getRefusalCause(), cref.getUserData());
                } else {
                    conn.receiveMessage(msg);
                }

            } else if (msg instanceof SccpConnRsrMessageImpl) {
                if (!checkSourceLocalReferenceNumber(msg, conn)) {
                    conn.sendErr(new ErrorCauseImpl(LRN_MISMATCH_INCONSISTENT_SOURCE_LRN));
                    return;
                }
                if (!conn.isCouplingEnabled()) {
                    SccpConnRsrMessageImpl rsr = (SccpConnRsrMessageImpl) msg;
                    conn.receiveMessage(rsr);
                    listener.onResetIndication(conn, rsr.getResetCause());
                } else {
                    conn.receiveMessage(msg);
                }

            } else if (msg instanceof SccpConnRscMessageImpl) {
                if (!checkSourceLocalReferenceNumber(msg, conn)) {
                    conn.sendErr(new ErrorCauseImpl(LRN_MISMATCH_INCONSISTENT_SOURCE_LRN));
                    return;
                }
                if (!conn.isCouplingEnabled()) {
                    conn.receiveMessage(msg);
                    listener.onResetConfirm(conn);
                } else {
                    conn.receiveMessage(msg);
                }

            } else if (msg instanceof SccpConnDt1MessageImpl) {
                conn.receiveMessage(msg);

            } else if (msg instanceof SccpConnDt2MessageImpl) {
                conn.receiveMessage(msg);

            } else if (msg instanceof SccpConnAkMessageImpl) {
                conn.receiveMessage(msg);
            } else if (msg instanceof SccpConnItMessageImpl) {
                if (!checkSourceLocalReferenceNumber(msg, conn)) {
                    conn.sendErr(new ErrorCauseImpl(LRN_MISMATCH_INCONSISTENT_SOURCE_LRN));
                    return;
                }
                conn.receiveMessage(msg);
            } else if (msg instanceof SccpConnErrMessageImpl) {
                SccpConnErrMessageImpl err = (SccpConnErrMessageImpl) msg;
                if (!conn.isCouplingEnabled()) {

                    if (err.getErrorCause().getValue() != null && err.getErrorCause().getValue() == SERVICE_CLASS_MISMATCH) {
                        listener.onDisconnectIndication(conn, err.getErrorCause());
                        conn.disconnect(new ReleaseCauseImpl(SCCP_FAILURE), new byte[] {});
                    } else {
                        listener.onDisconnectIndication(conn, err.getErrorCause());
                        sccpStackImpl.removeConnection(conn.getLocalReference());
                    }

                } else {
                    conn.receiveMessage(msg);
                }
            }

        } catch (Exception e) {
            if (logger.isEnabledFor(Level.WARN)) {
                logger.warn(String.format(
                        "Exception from the listener side when delivering SccpData to ssn=%d: Message=%s",
                        msg.getOriginLocalSsn(), msg), e);
            }
        }
    }

    private boolean checkSourceLocalReferenceNumber(SccpConnMessage msg, SccpConnection conn) {
        return ((SccpConnReferencedMessageImpl)msg).getSourceLocalReferenceNumber().getValue() == conn.getRemoteReference().getValue();
    }

    private class SccpTransferDeliveryHandler implements Runnable {
        private SccpMessage msg;
        private SccpListener listener;

        public SccpTransferDeliveryHandler(SccpMessage msg, SccpListener listener) {
            this.msg = msg;
            this.listener = listener;
        }

        @Override
        public void run() {
            if (sccpStackImpl.isStarted()) {
                try {
                    if (msg instanceof SccpDataMessage) {
                        listener.onMessage((SccpDataMessage) msg);

                    } else if (msg instanceof SccpConnMessage) {

                        LocalReference dln = getDln((SccpConnMessage)msg);
                        SccpConnectionImpl dconn = sccpStackImpl.getConnection(dln);
                        if (dconn == null) {
                            logger.error(String
                                    .format("Dropping message. Received SCCPMessage=%s for routing but can't find connection by local reference %s in this message",
                                            msg, dln));
                            return;
                        }
                        int ssn = dconn.getLocalSsn();

                        // This message is for local routing
                        SccpListener connListener = sccpProviderImpl.getSccpListener(ssn);
                        if (connListener == null) {
                            if (logger.isEnabledFor(Level.WARN)) {
                                logger.warn(String.format(
                                        "Received SccpMessage=%s for routing but the SSN is not available for local routing", msg));
                            }
                            sendSccpErrorConn((SccpConnMessage)msg, ReleaseCauseValue.SUBSYSTEM_FAILURE);
                            return;
                        }
                        processCoMessages((SccpConnMessage)msg, dconn, connListener);
                    }


                } catch (Exception e) {
                    logger.error("Exception while delivering a system messages to the SCCP-user: " + e.getMessage(), e);
                }
            } else {
                logger.error(String.format("Received SccpDataMessage=%s but SccpStack is not started. Message will be dropped",
                        msg));
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy