├── .gitignore ├── src └── main │ ├── resources │ └── application.yml │ └── kotlin │ └── io │ └── sip3 │ └── ss7 │ └── commons │ ├── Routes.kt │ ├── util │ ├── AsUtil.kt │ ├── M3UAManagementUtil.kt │ ├── AspUtil.kt │ └── SccpAddressFactory.kt │ ├── domain │ ├── SccpMessage.kt │ └── TcapMessage.kt │ └── socket │ └── Socket.kt ├── README.md └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | *.iml 3 | *.ipr 4 | 5 | *.class 6 | target -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | #! Soсket 2 | socket: 3 | priAddr: 127.0.0.1 4 | secAddr: 127.0.0.1 5 | stp: 6 | - lPort: 2901 7 | rAddr: 127.0.0.1 8 | rPort: 2905 9 | rPC: 2308 10 | pcApp: 42 11 | gtVrt: "*" 12 | gtApp: 13 | - "19000000001" 14 | - "19000000002" 15 | ssnApp: 16 | - 146 -------------------------------------------------------------------------------- /src/main/kotlin/io/sip3/ss7/commons/Routes.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 SIP3.IO, Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.sip3.ss7.commons 18 | 19 | interface Routes : io.sip3.commons.Routes { 20 | 21 | companion object : Routes 22 | 23 | val send get() = "send" 24 | val handle get() = "handle" 25 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SIP3 SS7 Commons # 2 | 3 | **SIP3 SS7 Commons** is a set of common classes used in multiple SIP3 SS7 related projects. 4 | 5 | This repository is a Kotlin wrapper over [jSS7](https://github.com/RestComm/jss7) project from Telestax. 6 | 7 | ## 1. Prerequsites 8 | 9 | Before starting with `sip3-ss7-commons`, make sure you have installed: 10 | 11 | * [Maven](https://maven.apache.org/install.html) 12 | * [sip3-parent](https://github.com/sip3io/sip3-parent) 13 | * [sip3-commons](https://github.com/sip3io/sip3-commons) 14 | 15 | ## 2. Installation 16 | 17 | To install `sip3-ss7-commons` to local Maven repository run the following command: 18 | 19 | ``` 20 | mvn clean install 21 | ``` 22 | 23 | ## 3. Support 24 | 25 | If you have a question about SIP3, just leave us a message in our 26 | community [Slack](https://join.slack.com/t/sip3-community/shared_invite/enQtOTIyMjg3NDI0MjU3LWUwYzhlOTFhODYxMTEwNjllYjZjNzc1M2NmM2EyNDM0ZjJmNTVkOTg1MGQ3YmFmNWU5NjlhOGI3MWU1MzUwMjE) 27 | and [Telegram](https://t.me/sip3io), or send us an [email](mailto:support@sip3.io). We will be happy to help you. 28 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sip3/ss7/commons/util/AsUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 SIP3.IO, Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.sip3.ss7.commons.util 18 | 19 | import org.mobicents.protocols.ss7.m3ua.As 20 | import org.mobicents.protocols.ss7.m3ua.impl.AsImpl 21 | import org.mobicents.protocols.ss7.m3ua.impl.AsState 22 | import org.mobicents.protocols.ss7.m3ua.impl.TransitionState 23 | 24 | fun As.patchPeerFsm() { 25 | this as AsImpl 26 | 27 | peerFSM.createTransition(TransitionState.AS_STATE_CHANGE_ACTIVE, AsState.DOWN.toString(), AsState.ACTIVE.toString()) 28 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sip3/ss7/commons/util/M3UAManagementUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 SIP3.IO, Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.sip3.ss7.commons.util 18 | 19 | import org.mobicents.protocols.ss7.m3ua.M3UAManagement 20 | import org.mobicents.protocols.ss7.m3ua.impl.M3UAManagementImpl 21 | import org.mobicents.protocols.ss7.m3ua.impl.M3UARouteManagement 22 | 23 | fun M3UAManagement.patchLinksetSelection() { 24 | this as M3UAManagementImpl 25 | 26 | isUseLsbForLinksetSelection = true 27 | 28 | val routeManagement = M3UARouteManagement::class.java 29 | .getDeclaredConstructor(M3UAManagementImpl::class.java) 30 | .apply { 31 | isAccessible = true 32 | } 33 | .newInstance(this) 34 | 35 | M3UAManagementImpl::class.java 36 | .getDeclaredField("routeManagement") 37 | .apply { 38 | isAccessible = true 39 | } 40 | .set(this, routeManagement) 41 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sip3/ss7/commons/domain/SccpMessage.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 SIP3.IO, Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.sip3.ss7.commons.domain 18 | 19 | class SccpMessage { 20 | 21 | var sls = 0 22 | 23 | var dpc = 0 24 | var opc = 0 25 | 26 | lateinit var cdpa: Address 27 | lateinit var cgpa: Address 28 | 29 | lateinit var tcapPayload: ByteArray 30 | val tcapMessage: TcapMessage by lazy { 31 | TcapMessage(tcapPayload) 32 | } 33 | 34 | fun createEchoResponse(): SccpMessage { 35 | val m = SccpMessage() 36 | m.sls = sls 37 | m.dpc = opc 38 | m.opc = dpc 39 | m.cdpa = cgpa 40 | m.cgpa = cdpa 41 | m.tcapPayload = tcapPayload 42 | return m 43 | } 44 | 45 | override fun toString(): String { 46 | return "SccpMessage(sls=$sls, dpc=$dpc, opc=$opc, cdpa=$cdpa, cgpa=$cgpa, tcapMessage=$tcapMessage)" 47 | } 48 | 49 | class Address { 50 | 51 | lateinit var gt: String 52 | var ssn = 146 53 | 54 | override fun toString(): String { 55 | return "Address(gt='$gt', ssn=$ssn)" 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sip3/ss7/commons/util/AspUtil.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 SIP3.IO, Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.sip3.ss7.commons.util 18 | 19 | import javolution.util.FastMap 20 | import org.mobicents.protocols.ss7.m3ua.Asp 21 | import org.mobicents.protocols.ss7.m3ua.impl.AsImpl 22 | import org.mobicents.protocols.ss7.m3ua.impl.AspImpl 23 | import org.mobicents.protocols.ss7.m3ua.impl.AspState 24 | import org.mobicents.protocols.ss7.m3ua.impl.TransitionState 25 | import org.mobicents.protocols.ss7.m3ua.impl.fsm.FSMState 26 | 27 | fun Asp.patchLocalFsm() { 28 | this as AspImpl 29 | 30 | val states = localFSM.javaClass 31 | .getDeclaredField("states") 32 | .apply { 33 | isAccessible = true 34 | } 35 | .get(localFSM) as FastMap 36 | 37 | states.get(AspState.ACTIVE_SENT.toString())?.setOnTimeOut({ 38 | aspFactory.javaClass 39 | .getDeclaredMethod("sendAspActive", AsImpl::class.java) 40 | .apply { 41 | isAccessible = true 42 | invoke(aspFactory, `as`) 43 | } 44 | }, 2000) 45 | 46 | localFSM.createTransition(TransitionState.ASP_INACTIVE_ACK, AspState.ACTIVE.toString(), AspState.INACTIVE.toString()) 47 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sip3/ss7/commons/util/SccpAddressFactory.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 SIP3.IO, Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.sip3.ss7.commons.util 18 | 19 | import org.mobicents.protocols.ss7.indicator.NatureOfAddress 20 | import org.mobicents.protocols.ss7.indicator.NumberingPlan 21 | import org.mobicents.protocols.ss7.indicator.RoutingIndicator 22 | import org.mobicents.protocols.ss7.sccp.impl.parameter.BCDEvenEncodingScheme 23 | import org.mobicents.protocols.ss7.sccp.impl.parameter.BCDOddEncodingScheme 24 | import org.mobicents.protocols.ss7.sccp.impl.parameter.GlobalTitle0100Impl 25 | import org.mobicents.protocols.ss7.sccp.impl.parameter.SccpAddressImpl 26 | import org.mobicents.protocols.ss7.sccp.parameter.SccpAddress 27 | 28 | object SccpAddressFactory { 29 | 30 | fun create(number: String, pc: Int, ssn: Int = 146): SccpAddress { 31 | val encodingScheme = if (number.length % 2 == 0) BCDEvenEncodingScheme.INSTANCE else BCDOddEncodingScheme.INSTANCE 32 | 33 | val gt = GlobalTitle0100Impl( 34 | number, 35 | 0, 36 | encodingScheme, 37 | NumberingPlan.ISDN_TELEPHONY, 38 | NatureOfAddress.INTERNATIONAL 39 | ) 40 | 41 | return SccpAddressImpl( 42 | RoutingIndicator.ROUTING_BASED_ON_GLOBAL_TITLE, 43 | gt, 44 | pc, 45 | ssn 46 | ) 47 | } 48 | } -------------------------------------------------------------------------------- /src/main/kotlin/io/sip3/ss7/commons/domain/TcapMessage.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 SIP3.IO, Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.sip3.ss7.commons.domain 18 | 19 | import org.mobicents.protocols.asn.AsnInputStream 20 | import org.mobicents.protocols.asn.AsnOutputStream 21 | import org.mobicents.protocols.ss7.tcap.asn.DialogPortion 22 | import org.mobicents.protocols.ss7.tcap.asn.TcapFactory 23 | import org.mobicents.protocols.ss7.tcap.asn.Utils 24 | import org.mobicents.protocols.ss7.tcap.asn.comp.* 25 | 26 | class TcapMessage { 27 | 28 | companion object { 29 | 30 | const val UNKNOWN = "UNKNOWN" 31 | const val BEGIN = "BEGIN" 32 | const val CONTINUE = "CONTINUE" 33 | const val END = "END" 34 | const val ABORT = "ABORT" 35 | } 36 | 37 | var type = UNKNOWN 38 | 39 | var otid: Long? = null 40 | var dtid: Long? = null 41 | 42 | var dialogPortion: DialogPortion? = null 43 | var components: MutableList = mutableListOf() 44 | var abortCause: PAbortCauseType? = null 45 | 46 | constructor() 47 | 48 | constructor(payload: ByteArray) { 49 | AsnInputStream(payload).use { ais -> 50 | when (ais.readTag()) { 51 | TCBeginMessage._TAG -> { 52 | type = BEGIN 53 | val tbm = TcapFactory.createTCBeginMessage(ais) 54 | 55 | otid = Utils.decodeTransactionId(tbm.originatingTransactionId) 56 | tbm.dialogPortion?.let { dialogPortion = it } 57 | tbm.component?.let { components = it.toMutableList() } 58 | } 59 | TCContinueMessage._TAG -> { 60 | type = CONTINUE 61 | val tcm = TcapFactory.createTCContinueMessage(ais) 62 | 63 | otid = Utils.decodeTransactionId(tcm.originatingTransactionId) 64 | dtid = Utils.decodeTransactionId(tcm.destinationTransactionId) 65 | tcm.dialogPortion?.let { dialogPortion = it } 66 | tcm.component?.let { components = it.toMutableList() } 67 | } 68 | TCEndMessage._TAG -> { 69 | type = END 70 | val tem = TcapFactory.createTCEndMessage(ais) 71 | 72 | dtid = Utils.decodeTransactionId(tem.destinationTransactionId) 73 | tem.dialogPortion?.let { dialogPortion = it } 74 | tem.component?.let { components = it.toMutableList() } 75 | } 76 | TCAbortMessage._TAG -> { 77 | type = ABORT 78 | val tam = TcapFactory.createTCAbortMessage(ais) 79 | 80 | dtid = Utils.decodeTransactionId(tam.destinationTransactionId) 81 | tam.dialogPortion?.let { dialogPortion = it } 82 | tam.pAbortCause?.let { abortCause = it } 83 | } 84 | else -> { 85 | type = UNKNOWN 86 | } 87 | } 88 | } 89 | } 90 | 91 | fun encode(): ByteArray { 92 | AsnOutputStream().use { aos -> 93 | when (type) { 94 | BEGIN -> { 95 | val tbm = TcapFactory.createTCBeginMessage() 96 | 97 | tbm.originatingTransactionId = Utils.encodeTransactionId(otid!!) 98 | dialogPortion?.let { tbm.dialogPortion = it } 99 | components.let { 100 | if (components.isNotEmpty()) { 101 | tbm.component = it.toTypedArray() 102 | } 103 | } 104 | 105 | tbm.encode(aos) 106 | } 107 | CONTINUE -> { 108 | val tcm = TcapFactory.createTCContinueMessage() 109 | 110 | tcm.originatingTransactionId = Utils.encodeTransactionId(otid!!) 111 | tcm.destinationTransactionId = Utils.encodeTransactionId(dtid!!) 112 | dialogPortion?.let { tcm.dialogPortion = it } 113 | components.let { 114 | if (components.isNotEmpty()) { 115 | tcm.component = it.toTypedArray() 116 | } 117 | } 118 | 119 | tcm.encode(aos) 120 | } 121 | END -> { 122 | val tem = TcapFactory.createTCEndMessage() 123 | 124 | tem.destinationTransactionId = Utils.encodeTransactionId(dtid!!) 125 | dialogPortion?.let { tem.dialogPortion = it } 126 | components.let { 127 | if (components.isNotEmpty()) { 128 | tem.component = it.toTypedArray() 129 | } 130 | } 131 | 132 | tem.encode(aos) 133 | } 134 | ABORT -> { 135 | val tam = TcapFactory.createTCAbortMessage() 136 | 137 | tam.destinationTransactionId = Utils.encodeTransactionId(dtid!!) 138 | dialogPortion?.let { tam.dialogPortion = it } 139 | abortCause?.let { tam.pAbortCause = it } 140 | 141 | tam.encode(aos) 142 | } 143 | else -> { 144 | throw UnsupportedOperationException("Unsupported TCAP message type. Message: $this") 145 | } 146 | } 147 | 148 | return aos.toByteArray() 149 | } 150 | } 151 | 152 | override fun toString(): String { 153 | return "TcapMessage(type='$type', otid=$otid, dtid=$dtid, dialogPortion=$dialogPortion, components=$components, abortCause=$abortCause)" 154 | } 155 | } -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | io.sip3.ss7.commons 8 | sip3-ss7-commons 9 | 2022.1.1-SNAPSHOT 10 | 11 | 12 | io.sip3 13 | sip3-parent 14 | 2022.1.1-SNAPSHOT 15 | 16 | 17 | 18 | 2.0.10 19 | 8.0.50 20 | 21 | 22 | 23 | 24 | 25 | io.sip3.commons 26 | sip3-commons 27 | 2022.1.1-SNAPSHOT 28 | 29 | 30 | 31 | 32 | org.mobicents.protocols.sctp 33 | sctp-api 34 | ${sctp.version} 35 | 36 | 37 | log4j 38 | log4j 39 | 40 | 41 | io.netty 42 | netty-all 43 | 44 | 45 | 46 | 47 | org.mobicents.protocols.sctp 48 | sctp-impl 49 | ${sctp.version} 50 | 51 | 52 | log4j 53 | log4j 54 | 55 | 56 | io.netty 57 | netty-all 58 | 59 | 60 | 61 | 62 | org.mobicents.protocols.ss7.m3ua 63 | m3ua-api 64 | ${jss7.version} 65 | 66 | 67 | log4j 68 | log4j 69 | 70 | 71 | io.netty 72 | netty-all 73 | 74 | 75 | 76 | 77 | org.mobicents.protocols.ss7.m3ua 78 | m3ua-impl 79 | ${jss7.version} 80 | 81 | 82 | log4j 83 | log4j 84 | 85 | 86 | io.netty 87 | netty-all 88 | 89 | 90 | 91 | 92 | org.mobicents.protocols.ss7.sccp 93 | sccp-api 94 | ${jss7.version} 95 | 96 | 97 | log4j 98 | log4j 99 | 100 | 101 | io.netty 102 | netty-all 103 | 104 | 105 | 106 | 107 | org.mobicents.protocols.ss7.sccp 108 | sccp-impl 109 | ${jss7.version} 110 | 111 | 112 | log4j 113 | log4j 114 | 115 | 116 | io.netty 117 | netty-all 118 | 119 | 120 | 121 | 122 | org.mobicents.protocols.ss7.cap 123 | cap-api 124 | ${jss7.version} 125 | 126 | 127 | log4j 128 | log4j 129 | 130 | 131 | io.netty 132 | netty-all 133 | 134 | 135 | 136 | 137 | org.mobicents.protocols.ss7.cap 138 | cap-impl 139 | ${jss7.version} 140 | 141 | 142 | log4j 143 | log4j 144 | 145 | 146 | io.netty 147 | netty-all 148 | 149 | 150 | 151 | 152 | 153 | 154 | sip3-ss7-commons 155 | 156 | 157 | org.jetbrains.kotlin 158 | kotlin-maven-plugin 159 | ${kotlin.version} 160 | 161 | 162 | compile 163 | 164 | compile 165 | 166 | 167 | 168 | src/main/kotlin 169 | 170 | 171 | 172 | 173 | test-compile 174 | 175 | test-compile 176 | 177 | 178 | 179 | src/test/kotlin 180 | 181 | 182 | 183 | 184 | 185 | 186 | -Xinline-classes 187 | -Xopt-in=kotlin.RequiresOptIn 188 | 189 | 190 | 191 | 192 | org.apache.maven.plugins 193 | maven-surefire-plugin 194 | 2.22.2 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /src/main/kotlin/io/sip3/ss7/commons/socket/Socket.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2022 SIP3.IO, Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.sip3.ss7.commons.socket 18 | 19 | import io.sip3.commons.micrometer.Metrics 20 | import io.sip3.commons.vertx.annotations.Instance 21 | import io.sip3.commons.vertx.util.localSend 22 | import io.sip3.ss7.commons.Routes 23 | import io.sip3.ss7.commons.domain.SccpMessage 24 | import io.sip3.ss7.commons.util.SccpAddressFactory 25 | import io.sip3.ss7.commons.util.patchLinksetSelection 26 | import io.sip3.ss7.commons.util.patchLocalFsm 27 | import io.sip3.ss7.commons.util.patchPeerFsm 28 | import io.vertx.core.AbstractVerticle 29 | import io.vertx.core.json.JsonObject 30 | import mu.KotlinLogging 31 | import org.mobicents.protocols.api.IpChannelType 32 | import org.mobicents.protocols.api.Management 33 | import org.mobicents.protocols.sctp.ManagementImpl 34 | import org.mobicents.protocols.ss7.m3ua.ExchangeType 35 | import org.mobicents.protocols.ss7.m3ua.Functionality 36 | import org.mobicents.protocols.ss7.m3ua.M3UAManagement 37 | import org.mobicents.protocols.ss7.m3ua.impl.M3UAManagementImpl 38 | import org.mobicents.protocols.ss7.m3ua.impl.parameter.ParameterFactoryImpl 39 | import org.mobicents.protocols.ss7.mtp.Mtp3PausePrimitive 40 | import org.mobicents.protocols.ss7.mtp.Mtp3ResumePrimitive 41 | import org.mobicents.protocols.ss7.mtp.Mtp3UserPart 42 | import org.mobicents.protocols.ss7.sccp.* 43 | import org.mobicents.protocols.ss7.sccp.impl.SccpStackImpl 44 | import org.mobicents.protocols.ss7.sccp.impl.message.SccpDataMessageImpl 45 | import org.mobicents.protocols.ss7.sccp.impl.parameter.ProtocolClassImpl 46 | import org.mobicents.protocols.ss7.sccp.message.SccpDataMessage 47 | import org.mobicents.protocols.ss7.sccp.message.SccpNoticeMessage 48 | import kotlin.system.exitProcess 49 | 50 | @Instance(singleton = true) 51 | class Socket : AbstractVerticle(), SccpListener { 52 | 53 | private val logger = KotlinLogging.logger {} 54 | 55 | companion object { 56 | const val ASSOCIATION_NAME = "ASS" 57 | const val ASP_NAME = "ASP" 58 | const val AS_NAME = "AS" 59 | const val NETWORK_INDICATOR = 3 60 | } 61 | 62 | private lateinit var sctp: Management 63 | private lateinit var m3ua: M3UAManagement 64 | private lateinit var sccp: SccpStack 65 | 66 | private val sccpMessagesReceived = Metrics.counter("sccp_messages_received") 67 | private val sccpMessagesSent = Metrics.counter("sccp_messages_sent") 68 | 69 | override fun start() { 70 | try { 71 | open() 72 | } catch (e: Exception) { 73 | logger.error(e) { "Socket 'open()' failed." } 74 | exitProcess(-1) 75 | } 76 | 77 | vertx.eventBus().localConsumer(Routes.send) { event -> 78 | try { 79 | send(event.body()) 80 | } catch (e: Exception) { 81 | logger.error(e) { "Socket 'send()' failed." } 82 | } 83 | } 84 | } 85 | 86 | private fun open() { 87 | config().getJsonObject("socket").let { config -> 88 | val priAddr = config.getString("priAddr") 89 | val secAddr = config.getString("secAddr") 90 | 91 | // Configure SCTP layer 92 | sctp = object : ManagementImpl("SCTP") { 93 | override fun store() { 94 | // Do nothing... 95 | } 96 | }.apply { 97 | start() 98 | removeAllResourses() 99 | } 100 | config.getJsonArray("stp")?.forEach { stp -> 101 | stp as JsonObject 102 | val lPort = stp.getInteger("lPort") 103 | val rAddr = stp.getString("rAddr") 104 | val rPort = stp.getInteger("rPort") 105 | 106 | sctp.addAssociation(priAddr, lPort, rAddr, rPort, ASSOCIATION_NAME + "_$lPort", IpChannelType.SCTP, arrayOf(secAddr)) 107 | } 108 | 109 | val pcApp = config.getInteger("pcApp") 110 | 111 | // Configure M3UA layer 112 | m3ua = object : M3UAManagementImpl("M3UA", null) { 113 | override fun store() { 114 | // Do nothing... 115 | } 116 | }.apply { 117 | transportManagement = sctp 118 | start() 119 | patchLinksetSelection() 120 | removeAllResourses() 121 | } 122 | config.getJsonArray("stp")?.forEach { stp -> 123 | stp as JsonObject 124 | val lPort = stp.getInteger("lPort") 125 | val rPc = stp.getInteger("rPC") 126 | val rCtx = stp.getJsonArray("rCtx")?.map { it as Int }?.map { it.toLong() }?.toLongArray() 127 | 128 | m3ua.createAspFactory(ASP_NAME + "_$lPort", ASSOCIATION_NAME + "_$lPort", false) 129 | m3ua.createAs( 130 | AS_NAME + "_$lPort", 131 | Functionality.AS, 132 | ExchangeType.SE, 133 | null, 134 | rCtx?.let { ParameterFactoryImpl().createRoutingContext(it) }, 135 | null, 136 | 1, 137 | null 138 | ).apply { 139 | // This patch will help AS to recover in case of blocked M3UA links 140 | patchPeerFsm() 141 | } 142 | m3ua.assignAspToAs(AS_NAME + "_$lPort", ASP_NAME + "_$lPort").apply { 143 | // This patch will help ASP to recover in case of blocked M3UA links 144 | patchLocalFsm() 145 | } 146 | m3ua.addRoute(rPc, pcApp, -1, AS_NAME + "_$lPort") 147 | } 148 | 149 | // Configure SCCP layer 150 | sccp = object : SccpStackImpl("SCCP") { 151 | override fun onMtp3PauseMessage(msg: Mtp3PausePrimitive) { 152 | // Do nothing... 153 | } 154 | 155 | override fun onMtp3ResumeMessage(msg: Mtp3ResumePrimitive) { 156 | // Do nothing... 157 | } 158 | 159 | override fun store() { 160 | // Do nothing... 161 | } 162 | }.apply { 163 | setMtp3UserPart(1, m3ua as Mtp3UserPart) 164 | start() 165 | removeAllResourses() 166 | } 167 | config.getJsonArray("stp")?.forEach { stp -> 168 | stp as JsonObject 169 | val lPort = stp.getInteger("lPort") 170 | val rPc = stp.getInteger("rPC") 171 | 172 | sccp.router.apply { 173 | addMtp3ServiceAccessPoint(lPort, 1, pcApp, NETWORK_INDICATOR, 0) 174 | addMtp3Destination(lPort, rPc, rPc, rPc, 0, 255, 255) 175 | } 176 | sccp.sccpResource.addRemoteSpc(lPort, rPc, 0, 0) 177 | } 178 | sccp.router.apply { 179 | var i = 0 180 | config.getJsonArray("ssnApp") 181 | .map { it as Int } 182 | .forEach { ssnApp -> 183 | val sccpAddressVrt = config.getString("gtVrt")?.let { SccpAddressFactory.create(it, pcApp, ssnApp) } 184 | config.getJsonArray("gtApp") 185 | .map { it as String } 186 | .map { SccpAddressFactory.create(it, pcApp, ssnApp) } 187 | .forEach { sccpAddressApp -> 188 | addRoutingAddress(i, sccpAddressApp) 189 | addRule(i, RuleType.SOLITARY, LoadSharingAlgorithm.Undefined, OriginationType.ALL, sccpAddressVrt, "K", i, -1, null, 0) 190 | i++ 191 | } 192 | } 193 | } 194 | 195 | // Start ASP 196 | config.getJsonArray("stp")?.forEach { stp -> 197 | stp as JsonObject 198 | val lPort = stp.getInteger("lPort") 199 | 200 | m3ua.startAsp(ASP_NAME + "_$lPort") 201 | } 202 | 203 | config.getJsonArray("ssnApp") 204 | .map { it as Int } 205 | .forEach { ssnApp -> 206 | sccp.sccpProvider.registerSccpListener(ssnApp, this) 207 | } 208 | } 209 | 210 | logger.info { "Stack configuration checked" } 211 | } 212 | 213 | override fun onMessage(message: SccpDataMessage) { 214 | sccpMessagesReceived.increment() 215 | 216 | val m = SccpMessage().apply { 217 | sls = message.sls 218 | opc = message.incomingOpc 219 | dpc = message.incomingDpc 220 | message.calledPartyAddress.let { calledPartyAddress -> 221 | cdpa = SccpMessage.Address().apply { 222 | gt = calledPartyAddress.globalTitle.digits 223 | ssn = calledPartyAddress.subsystemNumber 224 | } 225 | } 226 | message.callingPartyAddress.let { callingPartyAddress -> 227 | cgpa = SccpMessage.Address().apply { 228 | gt = callingPartyAddress.globalTitle.digits 229 | ssn = callingPartyAddress.subsystemNumber 230 | } 231 | } 232 | tcapPayload = message.data 233 | } 234 | 235 | vertx.eventBus().localSend(Routes.handle, m) 236 | } 237 | 238 | override fun onNotice(message: SccpNoticeMessage) { 239 | // Do nothing... 240 | } 241 | 242 | override fun onCoordResponse(ssn: Int, multiplicityIndicator: Int) { 243 | // Do nothing... 244 | } 245 | 246 | override fun onState(dpc: Int, ssn: Int, inService: Boolean, multiplicityIndicator: Int) { 247 | // Do nothing... 248 | } 249 | 250 | override fun onPcState(dpc: Int, status: SignallingPointStatus, restrictedImportanceLevel: Int?, remoteSccpStatus: RemoteSccpStatus) { 251 | // Do nothing... 252 | } 253 | 254 | override fun onNetworkIdState(networkId: Int, networkIdState: NetworkIdState) { 255 | // Do nothing... 256 | } 257 | 258 | private fun send(message: SccpMessage) { 259 | sccpMessagesSent.increment() 260 | 261 | val sccpMessage = object : SccpDataMessageImpl( 262 | 2560, ProtocolClassImpl(1, false), 263 | message.sls, message.cgpa.ssn, 264 | SccpAddressFactory.create(message.cdpa.gt, message.dpc, message.cdpa.ssn), 265 | SccpAddressFactory.create(message.cgpa.gt, message.opc, message.cgpa.ssn), 266 | message.tcapPayload, 267 | null, null 268 | ) {} 269 | 270 | sccp.sccpProvider.send(sccpMessage) 271 | } 272 | 273 | override fun stop() { 274 | try { 275 | sccp.stop() 276 | m3ua.stop() 277 | sctp.stop() 278 | } catch (e: Exception) { 279 | logger.error(e) { "Socket 'stop()' failed." } 280 | } 281 | } 282 | } --------------------------------------------------------------------------------