/*
* 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.mobicents.protocols.ss7.sccp.impl;
import javolution.util.FastMap;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.mobicents.protocols.ss7.indicator.RoutingIndicator;
import org.mobicents.protocols.ss7.mtp.Mtp3;
import org.mobicents.protocols.ss7.mtp.Mtp3TransferPrimitive;
import org.mobicents.protocols.ss7.mtp.Mtp3TransferPrimitiveFactory;
import org.mobicents.protocols.ss7.mtp.Mtp3UserPart;
import org.mobicents.protocols.ss7.sccp.LoadSharingAlgorithm;
import org.mobicents.protocols.ss7.sccp.LongMessageRule;
import org.mobicents.protocols.ss7.sccp.LongMessageRuleType;
import org.mobicents.protocols.ss7.sccp.Mtp3ServiceAccessPoint;
import org.mobicents.protocols.ss7.sccp.RemoteSignalingPointCode;
import org.mobicents.protocols.ss7.sccp.RemoteSubSystem;
import org.mobicents.protocols.ss7.sccp.Rule;
import org.mobicents.protocols.ss7.sccp.RuleType;
import org.mobicents.protocols.ss7.sccp.SccpListener;
import org.mobicents.protocols.ss7.sccp.impl.message.EncodingResultData;
import org.mobicents.protocols.ss7.sccp.impl.message.MessageFactoryImpl;
import org.mobicents.protocols.ss7.sccp.impl.message.SccpAddressedMessageImpl;
import org.mobicents.protocols.ss7.sccp.impl.message.SccpDataMessageImpl;
import org.mobicents.protocols.ss7.sccp.impl.message.SccpMessageImpl;
import org.mobicents.protocols.ss7.sccp.impl.message.SccpNoticeMessageImpl;
import org.mobicents.protocols.ss7.sccp.impl.parameter.ParameterFactoryImpl;
import org.mobicents.protocols.ss7.sccp.message.SccpDataMessage;
import org.mobicents.protocols.ss7.sccp.message.SccpNoticeMessage;
import org.mobicents.protocols.ss7.sccp.parameter.GlobalTitle;
import org.mobicents.protocols.ss7.sccp.parameter.ReturnCause;
import org.mobicents.protocols.ss7.sccp.parameter.ReturnCauseValue;
import org.mobicents.protocols.ss7.sccp.parameter.SccpAddress;
import java.io.IOException;
import java.util.Map;
/**
*
* @author amit bhayani
* @author sergey vetyutnev
*
*/
public class SccpRoutingControl {
private static final Logger logger = Logger.getLogger(SccpRoutingControl.class);
private SccpStackImpl sccpStackImpl = null;
private SccpProviderImpl sccpProviderImpl = null;
private SccpManagement sccpManagement = null;
private MessageFactoryImpl messageFactory;
public SccpRoutingControl(SccpProviderImpl sccpProviderImpl, SccpStackImpl sccpStackImpl) {
this.messageFactory = sccpStackImpl.messageFactory;
this.sccpProviderImpl = sccpProviderImpl;
this.sccpStackImpl = sccpStackImpl;
}
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<Integer, SccpListener> lstListn = this.sccpProviderImpl.getAllSccpListeners();
for (Map.Entry<Integer, SccpListener> val : lstListn.entrySet()) {
SccpListener listener = val.getValue();
if (msg instanceof SccpDataMessage) {
// SccpDataMessage dataMsg = (SccpDataMessage) msg;
// listener.onMessage(dataMsg);
deliverMessageToSccpUser(listener, (SccpDataMessage) msg);
}
}
return;
}
// TODO if the local SCCP or node is in an overload condition, SCRC
// shall inform SCMG
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);
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 {
// TODO: process connection-oriented messages
}
} 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:
this.translationFunction(msg);
break;
default:
// This can never happen
logger.error(String.format("Invalid Routing Indictaor received for message=%s from MTP3", msg));
break;
}
}
protected void routeMssgFromSccpUser(SccpAddressedMessageImpl msg) throws Exception {
if (this.sccpStackImpl.isPreviewMode()) {
// we drop off local originated message in pereviewMode
return;
}
this.route(msg);
}
private long lastCongAnnounseTime;
protected ReturnCauseValue send(SccpMessageImpl 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.sccpManagement.getSccpCongestionControl().isCongControl_blockingOutgoungScpMessages()
&& 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 ReturnCauseValue.NETWORK_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 ReturnCauseValue.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 ReturnCauseValue.SCCP_FAILURE;
}
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 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);
}
}
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);
}
}
private enum TranslationAddressCheckingResult {
destinationAvailable, destinationUnavailable_SubsystemFailure, destinationUnavailable_MtpFailure, destinationUnavailable_Congestion, translationFailure;
}
private TranslationAddressCheckingResult checkTranslationAddress(SccpAddressedMessageImpl msg, Rule rule,
SccpAddress translationAddress, String destName) {
if (translationAddress == null) {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn(String.format(
"Received SccpMessage=%s for Translation but no matching %s Address defined for Rule=%s for routing",
msg, destName, rule));
}
return TranslationAddressCheckingResult.translationFailure;
}
if (!translationAddress.getAddressIndicator().isPCPresent()) {
// destination PC is absent - bad rule
if (logger.isEnabledFor(Level.WARN)) {
logger.warn(String.format("Received SccpMessage=%s for Translation but no PC is present for %s Address ", msg,
destName));
}
return TranslationAddressCheckingResult.translationFailure;
}
int targetSsn = translationAddress.getSubsystemNumber();
if (targetSsn == 0)
targetSsn = msg.getCalledPartyAddress().getSubsystemNumber();
if (this.sccpStackImpl.router.spcIsLocal(translationAddress.getSignalingPointCode())) {
// destination PC is local
if (targetSsn == 1 || this.sccpProviderImpl.getSccpListener(targetSsn) != null) {
return TranslationAddressCheckingResult.destinationAvailable;
} else {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn(String.format(
"Received SccpMessage=%s for Translation but no local SSN is present for %s Address ", msg,
destName));
}
return TranslationAddressCheckingResult.destinationUnavailable_SubsystemFailure;
}
}
// Check if the DPC is prohibited
RemoteSignalingPointCode remoteSpc = this.sccpStackImpl.getSccpResource().getRemoteSpcByPC(
translationAddress.getSignalingPointCode());
if (remoteSpc == null) {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn(String.format(
"Received SccpMessage=%s for Translation but no %s Remote Signaling Pointcode = %d resource defined ",
msg, destName, translationAddress.getSignalingPointCode()));
}
return TranslationAddressCheckingResult.translationFailure;
}
if (remoteSpc.isRemoteSpcProhibited()) {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn(String.format(
"Received SccpMessage=%s for Translation but %s Remote Signaling Pointcode = %d is prohibited ", msg,
destName, translationAddress.getSignalingPointCode()));
}
return TranslationAddressCheckingResult.destinationUnavailable_MtpFailure;
}
// Check if the DPC is congested
if (remoteSpc.getCurrentRestrictionLevel() > 1) {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn(String
.format("Received SccpMessage=%s for Translation but %s Remote Signaling Pointcode = %d is congested with level %d ",
msg, destName, translationAddress.getSignalingPointCode(),
remoteSpc.getCurrentRestrictionLevel()));
}
return TranslationAddressCheckingResult.destinationUnavailable_Congestion;
}
if (translationAddress.getAddressIndicator().getRoutingIndicator() == RoutingIndicator.ROUTING_BASED_ON_DPC_AND_SSN) {
if (targetSsn != 1) {
RemoteSubSystem remoteSubSystem = this.sccpStackImpl.getSccpResource().getRemoteSsn(
translationAddress.getSignalingPointCode(), targetSsn);
if (remoteSubSystem == null) {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn(String.format("Received SccpMessage=%s for Translation but no %s Remote SubSystem = %d (dpc=%d) resource defined ", msg,
destName, targetSsn, translationAddress.getSignalingPointCode()));
}
return TranslationAddressCheckingResult.translationFailure;
}
if (remoteSubSystem.isRemoteSsnProhibited()) {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn(String.format("Received SccpMessage=%s for Translation but %s Remote SubSystem = %d (dpc=%d) is prohibited ", msg,
destName, targetSsn, translationAddress.getSignalingPointCode()));
}
return TranslationAddressCheckingResult.destinationUnavailable_SubsystemFailure;
}
}
}
return TranslationAddressCheckingResult.destinationAvailable;
}
private void translationFunction(SccpAddressedMessageImpl msg) throws Exception {
// checking for hop counter
if (!msg.reduceHopCounter()) {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn(String.format(
"Received SccpMessage for Translation but hop counter violation detected\nSccpMessage=%s", msg));
}
this.sendSccpError(msg, ReturnCauseValue.HOP_COUNTER_VIOLATION);
return;
}
SccpAddress calledPartyAddress = msg.getCalledPartyAddress();
SccpAddress callingPartyAddress = msg.getCallingPartyAddress();
Rule rule = this.sccpStackImpl.router.findRule(calledPartyAddress, callingPartyAddress, msg.getIsMtpOriginated(), msg.getNetworkId());
if (rule == null) {
if (logger.isEnabledFor(Level.WARN)) {
logger.warn(String.format(
"Received SccpMessage for Translation but no matching Rule found for local routing\nSccpMessage=%s",
msg));
}
// Translation failed return error
this.sendSccpError(msg, ReturnCauseValue.NO_TRANSLATION_FOR_ADDRESS);
return;
}
// Check whether to use primary or backup address
SccpAddress translationAddressPri = this.sccpStackImpl.router.getRoutingAddress(rule.getPrimaryAddressId());
TranslationAddressCheckingResult resPri = this.checkTranslationAddress(msg, rule, translationAddressPri, "primary");
if (resPri == TranslationAddressCheckingResult.translationFailure) {
this.sendSccpError(msg, ReturnCauseValue.NO_TRANSLATION_FOR_ADDRESS);
return;
}
SccpAddress translationAddressSec = null;
TranslationAddressCheckingResult resSec = TranslationAddressCheckingResult.destinationUnavailable_SubsystemFailure;
if (rule.getRuleType() != RuleType.SOLITARY) {
translationAddressSec = this.sccpStackImpl.router.getRoutingAddress(rule.getSecondaryAddressId());
resSec = this.checkTranslationAddress(msg, rule, translationAddressSec, "secondary");
if (resSec == TranslationAddressCheckingResult.translationFailure) {
this.sendSccpError(msg, ReturnCauseValue.NO_TRANSLATION_FOR_ADDRESS);
return;
}
}
if (resPri != TranslationAddressCheckingResult.destinationAvailable
&& resPri != TranslationAddressCheckingResult.destinationUnavailable_Congestion
&& resSec != TranslationAddressCheckingResult.destinationAvailable
&& resSec != TranslationAddressCheckingResult.destinationUnavailable_Congestion) {
switch (resPri) {
case destinationUnavailable_SubsystemFailure:
this.sendSccpError(msg, ReturnCauseValue.SUBSYSTEM_FAILURE);
return;
case destinationUnavailable_MtpFailure:
this.sendSccpError(msg, ReturnCauseValue.MTP_FAILURE);
return;
case destinationUnavailable_Congestion:
this.sendSccpError(msg, ReturnCauseValue.NETWORK_CONGESTION);
return;
default:
this.sendSccpError(msg, ReturnCauseValue.SCCP_FAILURE);
return;
}
}
SccpAddress translationAddress = null;
SccpAddress translationAddress2 = null;
if (resPri == TranslationAddressCheckingResult.destinationAvailable
&& resSec != TranslationAddressCheckingResult.destinationAvailable) {
translationAddress = translationAddressPri;
} else if (resPri != TranslationAddressCheckingResult.destinationAvailable
&& resSec == TranslationAddressCheckingResult.destinationAvailable) {
translationAddress = translationAddressSec;
} else if (resPri == TranslationAddressCheckingResult.destinationUnavailable_Congestion
&& resSec != TranslationAddressCheckingResult.destinationAvailable) {
translationAddress = translationAddressPri;
} else if (resPri != TranslationAddressCheckingResult.destinationAvailable
&& resSec == TranslationAddressCheckingResult.destinationUnavailable_Congestion) {
translationAddress = translationAddressSec;
} else {
switch (rule.getRuleType()) {
case SOLITARY:
case DOMINANT:
translationAddress = translationAddressPri;
break;
case LOADSHARED:
// loadsharing case and both destinations are available
if (msg.getSccpCreatesSls()) {
if (this.sccpStackImpl.newSelector())
translationAddress = translationAddressPri;
else
translationAddress = translationAddressSec;
} else {
if (this.selectLoadSharingRoute(rule.getLoadSharingAlgorithm(), msg))
translationAddress = translationAddressPri;
else
translationAddress = translationAddressSec;
}
break;
case BROADCAST:
// Broadcast case and both destinations are available
translationAddress = translationAddressPri;
translationAddress2 = translationAddressSec;
break;
}
}
// changing calling party address if a rule has NewCallingPartyAddress
if (rule.getNewCallingPartyAddressId() != null) {
SccpAddress newCallingPartyAddress = this.sccpStackImpl.router
.getRoutingAddress(rule.getNewCallingPartyAddressId());
if (newCallingPartyAddress != null) {
msg.setCallingPartyAddress(newCallingPartyAddress);
if (logger.isDebugEnabled()) {
logger.debug(String.format("New CallingPartyAddress assigned after translation = %s",
newCallingPartyAddress));
}
}
}
// translate address
SccpAddress address = rule.translate(calledPartyAddress, translationAddress);
msg.setCalledPartyAddress(address);
if (logger.isDebugEnabled()) {
logger.debug(String.format("Matching rule found: [%s] CalledPartyAddress after translation = %s", rule, address));
}
// routing procedures then continue's
this.route(msg);
if (translationAddress2 != null) {
// for broadcast mode - route to a secondary destination if it is available
address = rule.translate(calledPartyAddress, translationAddress2);
msg.setCalledPartyAddress(address);
msg.clearReturnMessageOnError();
if (logger.isDebugEnabled()) {
logger.debug(String.format("CalledPartyAddress after translation - a second broadcast address = %s", address));
}
// routing procedures then continue's
this.route(msg);
}
}
private boolean selectLoadSharingRoute(LoadSharingAlgorithm loadSharingAlgo, SccpAddressedMessageImpl msg) {
if (loadSharingAlgo == LoadSharingAlgorithm.Bit4) {
if ((msg.getSls() & 0x10) == 0)
return true;
else
return false;
} else if (loadSharingAlgo == LoadSharingAlgorithm.Bit3) {
if ((msg.getSls() & 0x08) == 0)
return true;
else
return false;
} else if (loadSharingAlgo == LoadSharingAlgorithm.Bit2) {
if ((msg.getSls() & 0x04) == 0)
return true;
else
return false;
} else if (loadSharingAlgo == LoadSharingAlgorithm.Bit1) {
if ((msg.getSls() & 0x02) == 0)
return true;
else
return false;
} else if (loadSharingAlgo == LoadSharingAlgorithm.Bit0) {
if ((msg.getSls() & 0x01) == 0)
return true;
else
return false;
} else {
// TODO: implement complicated algorithms for selecting a destination
// (CallingPartyAddress & SLS depended)
// Look at Q.815 8.1.3 - active loadsharing
return true;
}
}
private void route(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);
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 {
// TODO: process connection-oriented messages
}
} 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);
return;
}
this.translationFunction(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);
}
} 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);
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);
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);
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);
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);
}
}
} 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);
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);
return;
}
this.translationFunction(msg);
}
}
private void deliverMessageToSccpUser(SccpListener listener, SccpDataMessage msg) {
if (msg.getIsMtpOriginated()) {
listener.onMessage(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());
// }
ReturnCauseValue er = this.send(msg);
if (er != null) {
this.sendSccpError(msg, er);
}
}
protected void sendSccpError(SccpAddressedMessageImpl msg, ReturnCauseValue returnCauseInt) throws Exception {
// 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
}
if (ans != null) {
if (msg.getIsMtpOriginated()) {
// send to MTP3
if (logger.isDebugEnabled()) {
logger.debug(String.format("sendSccpError to a remote user: SCCP Message=%s", msg.toString()));
}
this.route(ans);
} else {
// deliver locally
if (logger.isDebugEnabled()) {
logger.debug(String.format("sendSccpError to a local user: SCCP Message=%s", msg.toString()));
}
SccpListener listener = this.sccpProviderImpl.getSccpListener(msg.getOriginLocalSsn());
if (listener != null) {
try {
listener.onNotice(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);
}
}
}
}
}
}
private class SccpTransferDeliveryHandler implements Runnable {
private SccpDataMessage msg;
private SccpListener listener;
public SccpTransferDeliveryHandler(SccpDataMessage msg, SccpListener listener) {
this.msg = msg;
this.listener = listener;
}
@Override
public void run() {
if (sccpStackImpl.isStarted()) {
try {
listener.onMessage(msg);
} 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));
}
}
}
}