diff --git a/osmo-st-network/.gitignore b/osmo-st-network/.gitignore
new file mode 100644
index 0000000..45d62d8
--- /dev/null
+++ b/osmo-st-network/.gitignore
@@ -0,0 +1 @@
+*.sw?
diff --git a/osmo-st-network/Makefile b/osmo-st-network/Makefile
new file mode 100644
index 0000000..986159d
--- /dev/null
+++ b/osmo-st-network/Makefile
@@ -0,0 +1,75 @@
+
+GST_PACKAGE = gst-package
+GST_CONVERT = gst-convert
+
+CONVERT_RULES = -r'Osmo.LogManager->LogManager' \
+ -r'Osmo.LogArea->LogArea' \
+ -r'Osmo.LogLevel->LogLevel' \
+ -r'Osmo.TimerScheduler->TimerScheduler' \
+ -r'Sockets.StreamSocket->SocketStream' \
+ -r'DateTime->DateAndTime' \
+ -r'(Duration milliseconds: ``@args1) -> (Duration milliSeconds: ``@args1)' \
+ -r'PP.PPCompositeParser->PPCompositeParser' \
+ -r'PP.PPCompositeParserTest->PPCompositeParserTest' \
+ -r'STInST.RBProgramNodeVisitor->RBProgramNodeVisitor' \
+ -r'STInST.RBBracketedMethodParser->RBParser' \
+ -r'Osmo.MessageBuffer->MessageBuffer' \
+ -r'SystemExceptions.NotFound->NotFound' \
+ -r'(``@object substrings: ``@args1)->(``@object subStrings: ``@args1)' \
+ -r'(Dictionary from: ``@args1)->(Dictionary newFrom: ``@args1)' \
+ -r'(``@object copyFrom: ``@args1)->(``@object copyFrom: ``@args1 to: ``@object size)' \
+ -r'(``@object nl)->(``@object cr; lf)' \
+ -r'(``@object methodSourceString)->(``@object sourceCode)' \
+ -C -IPAGSTTests
+
+# Can not be parsed right now..
+# -r'(``@object => ``@args1)->(``@object ==> ``@args1)'
+
+CORE = \
+ core/Extensions.st core/MessageStructure.st core/MessageBuffer.st \
+ core/LogAreas.st core/TLV.st core/TLVTests.st
+
+IPA = \
+ ipa/IPAConstants.st ipa/IPADispatcher.st ipa/IPAMuxer.st \
+ ipa/IPAProtoHandler.st ipa/IPAMsg.st \
+
+SCCP = \
+ sccp/SCCP.st sccp/SCCPAddress.st \
+ sccp/SCCPGlobalTitle.st sccp/SCCPGlobalTitleTranslation.st
+
+ISUP = \
+ isup/ISUP.st isup/isup_generated.st isup/ISUPExtensions.st \
+ isup/ISUPTests.st
+
+UA = \
+ ua/XUA.st
+
+M2UA = \
+ m2ua/M2UAConstants.st m2ua/M2UAMSG.st m2ua/M2UATag.st m2ua/M2UAMessages.st \
+ m2ua/M2UAStates.st m2ua/M2UAAspStateMachine.st \
+ m2ua/M2UAApplicationServerProcess.st m2ua/M2UALayerManagement.st \
+ m2ua/M2UAExamples.st m2ua/M2UATerminology.st m2ua/M2UATests.st
+
+
+OSMO = \
+ osmo/LogAreaOsmo.st \
+ osmo/OsmoUDPSocket.st osmo/OsmoCtrlLogging.st \
+ osmo/OsmoStreamSocketBase.st \
+ osmo/OsmoCtrlGrammar.st osmo/OsmoAppConnection.st \
+ osmo/OsmoCtrlConnection.st osmo/OsmoCtrlGrammarTest.st
+
+MTP3 = \
+ mtp3/MTP3Messages.st mtp3/MTP3MessagesTests.st
+
+
+
+all:
+ $(GST_PACKAGE) --test package.xml
+
+convert:
+ $(GST_CONVERT) $(CONVERT_RULES) -F squeak -f gst \
+ -o fileout.st pharo-porting/compat_for_pharo.st \
+ $(CORE) $(IPA) $(SCCP) $(ISUP) $(UA) $(OSMO) $(MTP3) $(M2UA) \
+ Tests.st pharo-porting/changes_for_pharo.st
+ sed -i s,"=>","==>",g fileout.st
+
diff --git a/osmo-st-network/README b/osmo-st-network/README
new file mode 100644
index 0000000..5fcf681
--- /dev/null
+++ b/osmo-st-network/README
@@ -0,0 +1 @@
+osmo-network a module for networking (SCCP, M3UA, IPA) protocol handling
diff --git a/osmo-st-network/Tests.st b/osmo-st-network/Tests.st
new file mode 100644
index 0000000..e395745
--- /dev/null
+++ b/osmo-st-network/Tests.st
@@ -0,0 +1,397 @@
+"
+ (C) 2010-2011 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+"Test Case for Osmo-Network"
+
+TestCase subclass: SCCPTests [
+
+
+ SCCPTests class >> packageNamesUnderTest [
+
+ ^ #('OsmoNetwork')
+ ]
+
+ testCreateForSSN [
+ | addr |
+ addr := SCCPAddress createForSSN: 'hlr'.
+ self assert: addr subSystemNumber = SCCPAddress ssnHLR.
+ self assert: addr routedOnSSN.
+ self assert: addr globalTitle isNil.
+ self assert: addr pointCode isNil.
+ ]
+
+ testPNCCreate [
+ | pnc |
+
+ pnc := SCCPPNC new.
+ pnc at: SCCPHelper pncData put: #(1 2 3) asByteArray.
+ self assert: pnc toMessage asByteArray = #(15 3 1 2 3 0) asByteArray.
+ ]
+
+ testReleasedFormat [
+ | rlsd msg |
+ rlsd := SCCPConnectionReleased initWithDst: 16r401 src: 16r1F0A01 cause: 16rFE.
+ msg := rlsd toMessage asByteArray.
+
+ self assert: msg = #(4 1 4 0 1 16r0A 16r1F 16rFE 1 0) asByteArray
+ ]
+
+ testDT1 [
+ | dt1 msg target |
+ target := #(6 1 4 0 0 1 4 49 50 51 52) asByteArray.
+ dt1 := SCCPConnectionData initWith: 16r401 data: '1234' asByteArray.
+ msg := dt1 toMessage asByteArray.
+
+ self assert: msg = target.
+
+ dt1 := SCCPMessage decode: target.
+ self assert: dt1 dst = 16r401.
+ self assert: dt1 data = '1234' asByteArray.
+ self assert: dt1 toMessage asByteArray = target.
+ ]
+
+ testCR [
+ | cr msg target |
+ target := #(1 191 0 3 2 2 4 2 66 254 15 4 49 50 51 52 0) asByteArray.
+
+ "encode it"
+ cr := SCCPConnectionRequest
+ initWith: 16r0300BF
+ dest: (SCCPAddress createWith: 254)
+ data: '1234' asByteArray.
+ msg := cr toMessage asByteArray.
+
+ self assert: msg = target.
+
+ "now decode it"
+ cr := SCCPMessage decode: target.
+ self assert: (cr isKindOf: SCCPConnectionRequest).
+ self assert: cr src = 16r0300BF.
+ self assert: cr dest asByteArray = (SCCPAddress createWith: 254) asByteArray.
+ self assert: cr data = '1234' asByteArray.
+
+ "now encode it again"
+ self assert: cr toMessage asByteArray = target.
+ ]
+
+ testCC [
+ | target cc |
+
+ target := #(2 191 0 3 1 3 176 2 1 0) asByteArray.
+
+ cc := SCCPConnectionConfirm
+ initWithSrc: 16rB00301 dst: 16r0300BF.
+ self assert: cc toMessage asByteArray = target.
+
+ cc := SCCPMessage decode: target.
+ self assert: (cc isKindOf: SCCPConnectionConfirm).
+ self assert: cc dst = 16r0300BF.
+ self assert: cc src = 16rB00301.
+
+ self assert: cc toMessage asByteArray = target.
+ ]
+
+ testRlsd [
+ | target rlsd |
+
+ target := #(4 154 2 0 66 5 5 0 1 0 ) asByteArray.
+ rlsd := SCCPConnectionReleased
+ initWithDst: 16r0029A src: 16r50542
+ cause: 0.
+ self assert: rlsd toMessage asByteArray = target.
+
+ rlsd := SCCPMessage decode: target.
+ self assert: rlsd dst = 16r0029A.
+ self assert: rlsd src = 16r50542.
+ self assert: rlsd cause = 0.
+ self assert: rlsd toMessage asByteArray = target.
+ ]
+
+ testCreateRLSD [
+ | target rlsd |
+ target := #(4 154 2 0 66 5 5 0 1 0 ) asByteArray.
+ rlsd := SCCPHelper createRLSD: 16r50542 dest: 16r0029A cause: 0.
+ self assert: rlsd asByteArray = target.
+ ]
+
+ testRlc [
+ | target rlc |
+
+ target := #(5 1 8 119 62 4 5 ) asByteArray.
+ rlc := SCCPConnectionReleaseComplete
+ initWithDst: 16r770801 src: 16r05043E.
+ self assert: rlc toMessage asByteArray = target.
+
+ rlc := SCCPMessage decode: target.
+ self assert: rlc dst = 16r770801.
+ self assert: rlc src = 16r05043E.
+ self assert: rlc toMessage asByteArray = target.
+ ]
+
+ testUdt [
+ | target udt called calling |
+ target := #(9 0 3 7 11 4 67 7 0 254 4 67 92 0 254 3 1 2 3) asByteArray.
+ called := SCCPAddress createWith: 254 pointCode: 7.
+ calling := SCCPAddress createWith: 254 pointCode: 92.
+ udt := SCCPUDT
+ initWith: called
+ calling: calling
+ data: #(1 2 3) asByteArray.
+ self assert: udt toMessage asByteArray = target.
+ udt := SCCPMessage decode: target.
+ self assert: (udt isKindOf: SCCPUDT).
+ self assert: udt calledAddr ssn = 254.
+ self assert: udt calledAddr pointCode = 7.
+ self assert: udt callingAddr ssn = 254.
+ self assert: udt callingAddr pointCode = 92.
+ self assert: udt toMessage asByteArray = target
+ ]
+
+ testUDTClass [
+ | target udt |
+ target := #(9 129 3 13 24 10 18 6 0 18 4 83 132 9 0 55 11 18 7 0
+ 18 4 54 25 8 0 4 49 70 100 68 73 4 81 1 3 78 107 42
+ 40 40 6 7 0 17 134 5 1 1 1 160 29 97 27 128 2 7 128
+ 161 9 6 7 4 0 0 1 0 27 3 162 3 2 1 0 163 5 161 3 2 1
+ 0 108 128 162 12 2 1 64 48 7 2 1 67 48 2 128 0 0 0) asByteArray.
+
+ udt := SCCPMessage decode: target.
+ self assert: udt udtClass = 1.
+ self assert: udt errorHandling = 8.
+ self assert: udt toMessage asByteArray = target.
+ ]
+
+ testIT [
+ | target it |
+ target := #(16 1 3 176 191 0 3 240 36 66 239) asByteArray.
+
+ it := SCCPMessage decode: target.
+ self assert: it src = 16r0300BF.
+ self assert: it dst = 16rB00301.
+ self assert: it credit = 16rEF.
+ self assert: it seq = #(16r24 16r42) asByteArray.
+ self assert: it protoClass = 16rF0.
+
+ self assert: it toMessage asByteArray = target.
+ ]
+
+ testAddrFromByteArray [
+ | byte |
+ byte := #(191 0 3) asByteArray.
+ self assert: (SCCPAddrReference fromByteArray: byte) = 16r0300BF
+ ]
+
+ testAddrGTIFromByteArrray [
+ | addr parsed gti |
+ addr := #(16r0A 16r12 16r06 16r0 16r12 16r04 16r53 16r84 16r09 16r00 16r37) asByteArray.
+ parsed := SCCPAddress parseFrom: addr.
+
+ self assert: parsed ssn = SCCPAddress ssnHLR.
+ self assert: parsed asByteArray = addr.
+
+ "Now test the GTI parsing"
+ gti := parsed gtiAsParsed.
+ self assert: gti translation = 0.
+ self assert: gti plan = SCCPGlobalTitle npISDN.
+ self assert: gti nature = SCCPGlobalTitle naiInternationalNumber.
+ self assert: gti addr = '3548900073'.
+ parsed gtiFromAddr: gti.
+ self assert: parsed asByteArray = addr.
+
+ ]
+
+ testAddrGTIOdd [
+ | addr parsed gti |
+ addr := #(16r0B 16r12 16r08 16r00 16r11 16r04 16r64 16r07 16r97 16r36 16r71 16r03) asByteArray.
+ parsed := SCCPAddress parseFrom: addr.
+
+ self assert: parsed ssn = SCCPAddress ssnMSC.
+ self assert: parsed asByteArray = addr.
+
+ "GTI encoding.."
+ gti := parsed gtiAsParsed.
+ self assert: gti translation = 0.
+ self assert: gti plan = SCCPGlobalTitle npISDN.
+ self assert: gti nature = SCCPGlobalTitle naiInternationalNumber.
+ self assert: gti addr = '46707963173'.
+ parsed gtiFromAddr: gti.
+ self assert: parsed asByteArray = addr.
+ ]
+]
+
+TestCase subclass: MessageBufferTest [
+
+
+ testAdd [
+ | msg1 msg2 msg3 msg_master |
+ msg1 := MessageBuffer new.
+ msg2 := MessageBuffer new.
+ msg3 := MessageBuffer new.
+
+ msg1 putByteArray: #(1 2 3) asByteArray.
+ msg2 putByteArray: #(4 5 6) asByteArray.
+ msg3 putByteArray: #(7 8 9) asByteArray.
+
+ msg_master := MessageBuffer new.
+ msg_master putByteArray: msg1.
+ msg_master putByteArray: msg2.
+ msg_master putByteArray: msg3.
+
+ self assert: msg_master size = 9.
+ self assert: msg_master toByteArray = #(1 2 3 4 5 6 7 8 9) asByteArray.
+ self assert: msg_master asByteArray = #(1 2 3 4 5 6 7 8 9) asByteArray.
+ ]
+
+ testEmptyByteArray [
+ | msg |
+ msg := MessageBuffer new.
+ msg putByteArray: ByteArray new.
+ self assert: msg size = 0.
+ self assert: msg toByteArray = #() asByteArray.
+ ]
+
+ testPrependByteArray [
+ | msg |
+ msg := MessageBuffer new.
+ msg putByteArray: #(3 4 5) asByteArray.
+ msg prependByteArray: #(1 2) asByteArray.
+ self assert: msg toByteArray = #(1 2 3 4 5) asByteArray.
+
+ msg := MessageBuffer new.
+ msg prependByteArray: #(1 2) asByteArray.
+ msg putByteArray: #(3 4 5) asByteArray.
+ self assert: msg toByteArray = #(1 2 3 4 5) asByteArray.
+
+ msg := MessageBuffer new.
+ msg prependByteArray: #(1 2) asByteArray.
+ self assert: msg toByteArray = #(1 2) asByteArray.
+ ]
+
+ testIdentity [
+ | msg |
+ msg := MessageBuffer new.
+ self assert: msg toMessage == msg.
+ ]
+]
+
+TestCase subclass: M2UAMSGTests [
+
+
+ testUnique [
+ "This should have some sanity checks on the enum"
+ ]
+
+ testParseTag [
+ | inp tag |
+ inp := #(16r00 16r11 16r00 16r08 16rAC 16r10 16r01 16r51) asByteArray.
+ tag := M2UATag fromStream: inp readStream.
+
+ self assert: tag nr = 16r11.
+ self assert: tag data = #(16rAC 16r10 16r01 16r51) asByteArray.
+ ]
+
+ testCreateTag [
+ | tag exp |
+ tag := M2UATag initWith: 16r11 data: (ByteArray new: 3 withAll: 6).
+ exp := #(16r00 16r11 16r00 16r07 16r06 16r06 16r06 16r00) asByteArray.
+ self assert: tag toMessage asByteArray = exp.
+ ]
+
+ testCreateMessage [
+ | msg data out res |
+ res := #(16r01 16r00 16r03 16r01 16r00 16r00 16r00 16r10
+ 16r00 16r11 16r00 16r08 16rAC 16r10 16r01 16r51) asByteArray.
+ data := #(16rAC 16r10 16r01 16r51) asByteArray.
+
+ msg := M2UAMSG fromClass: M2UAConstants clsASPSM type: M2UAConstants aspsmUp.
+ msg addTag: (M2UATag initWith: 16r11 data: data).
+ out := msg toMessage asByteArray.
+
+ self assert: out = res.
+ ]
+
+ testCreatePaddingMessage [
+ | msg data out res |
+ res := #(16r01 16r00 16r03 16r01 16r00 16r00 16r00 16r10
+ 16r00 16r11 16r00 16r07 16rAC 16r10 16r01 16r00) asByteArray.
+ data := #(16rAC 16r10 16r01) asByteArray.
+
+ msg := M2UAMSG fromClass: M2UAConstants clsASPSM type: M2UAConstants aspsmUp.
+ msg addTag: (M2UATag initWith: 16r11 data: data).
+ out := msg toMessage asByteArray.
+
+ self assert: out = res.
+ ]
+
+ testParseMessage [
+ | inp msg |
+
+ inp := #(16r01 16r00 16r03 16r01 16r00 16r00 16r00 16r10
+ 16r00 16r11 16r00 16r08 16rAC 16r10 16r01 16r51) asByteArray.
+ msg := M2UAMSG parseFrom: inp.
+ self assert: msg msgClass = UAConstants clsASPSM.
+ self assert: msg msgType = UAConstants aspsmUp.
+
+ inp := #(16r01 16r00 16r06 16r01 16r00 16r00 16r00 16r2C
+ 16r00 16r01 16r00 16r08 16r00 16r00 16r00 16r00
+ 16r03 16r00 16r00 16r1A 16r81 16r5C 16r00 16r07
+ 16r00 16r11 16rF0 16rAA 16rAA 16rAA 16rAA 16rAA
+ 16rAA 16rAA 16rAA 16rAA 16rAA 16rAA 16rAA 16rAA
+ 16rAA 16rAA 16r00 16r00) asByteArray.
+ msg := M2UAMSG parseFrom: inp.
+ self assert: msg msgClass = UAConstants clsMAUP.
+ self assert: msg msgType = UAConstants maupData.
+ ]
+
+ testFindTag [
+ | inp msg tag |
+ inp := #(16r01 16r00 16r03 16r01 16r00 16r00 16r00 16r10 16r00
+ 16r11 16r00 16r08 16rAC 16r10 16r01 16r51) asByteArray.
+ msg := M2UAMSG parseFrom: inp.
+ tag := msg findTag: M2UAConstants tagReserved ifAbsent: [ nil ].
+ self assert: tag isNil.
+
+ tag := msg findTag: M2UAConstants tagAspIdent ifAbsent: [self fail].
+ self deny: tag isNil.
+ ]
+]
+
+TestCase subclass: OsmoUDPSocketTest [
+
+
+ createSocket [
+ ^ Sockets.DatagramSocket new.
+ ]
+
+ testSocketCreation [
+ | socket rx tx |
+ socket := OsmoUDPSocket new
+ name: 'Test Socket';
+ start: self createSocket;
+ yourself.
+
+ "Verify that we are in processing"
+ rx := socket instVarNamed: #rx.
+ tx := socket instVarNamed: #tx.
+ self deny: rx isTerminated.
+ self deny: tx isTerminated.
+
+ socket stop.
+ self assert: rx isTerminated.
+ self assert: tx isTerminated.
+ ]
+]
diff --git a/osmo-st-network/contrib/Test.st b/osmo-st-network/contrib/Test.st
new file mode 100644
index 0000000..634ded3
--- /dev/null
+++ b/osmo-st-network/contrib/Test.st
@@ -0,0 +1,46 @@
+"
+ (C) 2010 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+"Test code"
+
+Eval [
+ | socket muxer demuxer data dispatcher ipa |
+ FileStream fileIn: 'Message.st'.
+ FileStream fileIn: 'IPAMuxer.st'.
+ FileStream fileIn: 'IPAConstants.st'.
+ FileStream fileIn: 'IPADispatcher.st'.
+ FileStream fileIn: 'IPAProtoHandler.st'.
+ FileStream fileIn: 'Extensions.st'.
+
+ socket := Sockets.Socket remote: '127.0.0.1' port: 5000.
+ demuxer := IPADemuxer initOn: socket.
+ muxer := IPAMuxer initOn: socket.
+
+ dispatcher := IPADispatcher new.
+
+ ipa := IPAProtoHandler new.
+ dispatcher addHandler: IPAConstants protocolIPA on: ipa with: #handleMsg:.
+ dispatcher addHandler: IPAConstants protocolMGCP on: ipa with: #handleNoop:.
+
+ [true] whileTrue: [
+ [
+ data := demuxer next.
+ data inspect.
+ dispatcher dispatch: data first with: data second.
+ ] on: SystemExceptions.EndOfStream do: [:e | ^ false ].
+ ]
+]
diff --git a/osmo-st-network/contrib/m2ua.st b/osmo-st-network/contrib/m2ua.st
new file mode 100644
index 0000000..2a70f32
--- /dev/null
+++ b/osmo-st-network/contrib/m2ua.st
@@ -0,0 +1,19 @@
+Eval [
+ | msg dt socket dgram |
+
+ PackageLoader fileInPackage: #Sockets.
+ PackageLoader fileInPackage: #OsmoNetwork.
+ msg := Osmo.M2UAMSG fromClass: Osmo.M2UAConstants clsMAUP type: Osmo.M2UAConstants maupData.
+ msg addTag: (Osmo.M2UATag initWith: Osmo.M2UAConstants tagIdentText data: 'm2ua' asByteArray).
+ msg addTag: (Osmo.M2UATag initWith: Osmo.M2UAConstants tagData data: #(0 0 0 0 0 0 0 0 0 0) asByteArray).
+ dt := msg toMessage asByteArray.
+
+ dt inspect.
+
+ socket := Sockets.DatagramSocket new.
+ dgram := Sockets.Datagram data: dt.
+ dgram port: 5001.
+ dgram address: Sockets.SocketAddress loopbackHost.
+
+ socket nextPut: dgram.
+]
diff --git a/osmo-st-network/core/Extensions.st b/osmo-st-network/core/Extensions.st
new file mode 100644
index 0000000..a8ecd52
--- /dev/null
+++ b/osmo-st-network/core/Extensions.st
@@ -0,0 +1,128 @@
+"
+ (C) 2010-2011 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+Integer extend [
+ swap16 [
+ | tmp |
+
+
+ tmp := self bitAnd: 16rFFFF.
+ ^ (tmp bitShift: -8) bitOr: ((tmp bitAnd: 16rFF) bitShift: 8)
+ ]
+
+ swap32 [
+ | tmp |
+ "Certainly not the most effective way"
+
+
+ tmp := 0.
+ tmp := tmp bitOr: ((self bitAnd: 16rFF000000) bitShift: -24).
+ tmp := tmp bitOr: ((self bitAnd: 16r00FF0000) bitShift: -8).
+ tmp := tmp bitOr: ((self bitAnd: 16r0000FF00) bitShift: 8).
+ tmp := tmp bitOr: ((self bitAnd: 16r000000FF) bitShift: 24).
+
+ ^ tmp
+ ]
+]
+
+Object extend [
+ toMessage [
+ | msg |
+
+ msg := Osmo.MessageBuffer new.
+ self writeOn: msg.
+ ^ msg
+ ]
+
+ toMessageOrByteArray [
+
+ ^ self toMessage
+ ]
+]
+
+ByteArray extend [
+ toMessageOrByteArray [
+
+ ^ self
+ ]
+]
+
+
+"Code from FileDescriptor, GST license"
+Sockets.Socket extend [
+ nextUshort [
+ "Return the next 2 bytes in the byte array, interpreted as a 16 bit unsigned int"
+
+ ^self nextBytes: 2 signed: false
+ ]
+
+ nextBytes: n signed: signed [
+ "Private - Get an integer out of the next anInteger bytes in the stream"
+
+
+ | int msb |
+ int := 0.
+ 0 to: n * 8 - 16
+ by: 8
+ do: [:i | int := int + (self nextByte bitShift: i)].
+ msb := self nextByte.
+ (signed and: [msb > 127]) ifTrue: [msb := msb - 256].
+ ^int + (msb bitShift: n * 8 - 8)
+ ]
+
+ nextByte [
+ "Return the next byte in the file, or nil at eof"
+
+
+ | a |
+ a := self next.
+ ^a isNil ifTrue: [a] ifFalse: [a asInteger]
+ ]
+]
+
+Sockets.StreamSocket extend [
+ nextUshort [
+ "Return the next 2 bytes in the byte array, interpreted as a 16 bit unsigned int"
+
+ ^self nextBytes: 2 signed: false
+ ]
+
+ nextBytes: n signed: signed [
+ "Private - Get an integer out of the next anInteger bytes in the stream"
+
+
+ | int msb |
+ int := 0.
+ 0 to: n * 8 - 16
+ by: 8
+ do: [:i | int := int + (self nextByte bitShift: i)].
+ msb := self nextByte.
+ (signed and: [msb > 127]) ifTrue: [msb := msb - 256].
+ ^int + (msb bitShift: n * 8 - 8)
+ ]
+
+ nextByte [
+ "Return the next byte in the file, or nil at eof"
+
+
+ | a |
+ a := self next.
+ ^a isNil ifTrue: [a] ifFalse: [a asInteger]
+ ]
+]
+
diff --git a/osmo-st-network/core/ExtensionsGST.st b/osmo-st-network/core/ExtensionsGST.st
new file mode 100644
index 0000000..efeec62
--- /dev/null
+++ b/osmo-st-network/core/ExtensionsGST.st
@@ -0,0 +1,28 @@
+"
+ (C) 2013 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+BlockClosure extend [
+ value: arg1 value: arg2 value: arg3 value: arg4 [
+
+ "Evaluate the receiver passing arg1, arg2, arg3 and arg4 as the parameters"
+
+
+
+ SystemExceptions.WrongArgumentCount signal
+ ]
+]
diff --git a/osmo-st-network/core/LogAreas.st b/osmo-st-network/core/LogAreas.st
new file mode 100644
index 0000000..b3ca5a6
--- /dev/null
+++ b/osmo-st-network/core/LogAreas.st
@@ -0,0 +1,62 @@
+"
+ (C) 2010-2012 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+Osmo.LogArea subclass: LogAreaSCCP [
+
+
+
+ LogAreaSCCP class >> areaName [ ^ #sccp ]
+ LogAreaSCCP class >> areaDescription [ ^ 'SCCP related' ]
+ LogAreaSCCP class >> default [
+
+ ^ self new
+ enabled: true;
+ minLevel: Osmo.LogLevel debug;
+ yourself
+ ]
+]
+
+Osmo.LogArea subclass: LogAreaIPA [
+
+
+
+ LogAreaIPA class >> areaName [ ^ #ipa ]
+ LogAreaIPA class >> areaDescription [ ^ 'IPA related' ]
+ LogAreaIPA class >> default [
+
+ ^ self new
+ enabled: true;
+ minLevel: Osmo.LogLevel debug;
+ yourself
+ ]
+]
+
+Osmo.LogArea subclass: LogAreaM2UA [
+
+
+
+ LogAreaM2UA class >> areaName [ ^ #m2ua ]
+ LogAreaM2UA class >> areaDescription [ ^ 'MTP2 User Adaption' ]
+ LogAreaM2UA class >> default [
+
+ ^ self new
+ enabled: true;
+ minLevel: Osmo.LogLevel debug;
+ yourself
+ ]
+]
diff --git a/osmo-st-network/core/MessageBuffer.st b/osmo-st-network/core/MessageBuffer.st
new file mode 100644
index 0000000..c42ffb0
--- /dev/null
+++ b/osmo-st-network/core/MessageBuffer.st
@@ -0,0 +1,103 @@
+"
+ (C) 2010-2012 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+Collection subclass: MessageBuffer [
+ | chunks |
+
+
+
+
+ MessageBuffer class >> new [
+
+ ^ (super new)
+ initialize;
+ yourself
+ ]
+
+ initialize [
+
+ chunks := OrderedCollection new.
+ ]
+
+ toMessage [
+
+ ^ self
+ ]
+
+ prependByteArray: aByteArray [
+
+ chunks addFirst: aByteArray.
+ ]
+
+ putByte: aByte [
+
+ chunks add: (ByteArray with: aByte)
+ ]
+
+ putByteArray: aByteArray [
+
+ chunks add: aByteArray.
+ ]
+
+ put16: aInt [
+ | data low high |
+
+ low := (aInt bitAnd: 16rFF).
+ high := (aInt bitShift: -8) bitAnd: 16rFF.
+ data := ByteArray with: low with: high.
+ chunks add: data.
+ ]
+
+ putLen16: aInt [
+ | data low high |
+
+ low := (aInt bitShift: -8) bitAnd: 16rFF.
+ high := aInt bitAnd: 16rFF.
+ data := ByteArray with: low with: high.
+ chunks add: data.
+ ]
+
+ putLen32: aInt [
+ | a b c d data |
+
+ a := (aInt bitShift: -24) bitAnd: 16rFF.
+ b := (aInt bitShift: -16) bitAnd: 16rFF.
+ c := (aInt bitShift: -8) bitAnd: 16rFF.
+ d := (aInt bitShift: 0) bitAnd: 16rFF.
+ data := ByteArray with: a with: b with: c with: d.
+ chunks add: data.
+ ]
+
+ toByteArray [
+
+ ^ self asByteArray.
+ ]
+
+ size [
+ "Count of how much data we have collected"
+
+ ^ chunks inject: 0 into: [:acc :each | acc + each size ]
+ ]
+
+ do: aBlock [
+
+ chunks do: [:chunk |
+ chunk do: aBlock.
+ ].
+ ]
+]
diff --git a/osmo-st-network/core/MessageStructure.st b/osmo-st-network/core/MessageStructure.st
new file mode 100644
index 0000000..e62a927
--- /dev/null
+++ b/osmo-st-network/core/MessageStructure.st
@@ -0,0 +1,451 @@
+"
+ (C) 2011-2012 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+"
+The next attempt to generalize the message pattern. We will just describe
+messages that have a type, mandatory and optional parameters. The parameters
+will be simple ids. There should be code to generate nice parsing routines
+"
+
+Object subclass: TLVDescriptionContainer [
+ | type fields |
+
+
+
+
+ TLVDescriptionContainer class >> initWith: aType [
+
+ ^ self new
+ instVarNamed: #type put: aType; yourself
+ ]
+
+ TLVDescriptionContainer class >> findTLVDescription: aType [
+
+ self allSubclassesDo: [:each | | struct |
+ struct := each tlvDescription.
+ struct type = aType ifTrue: [
+ ^ struct
+ ]
+ ].
+
+ ^ self error: ('Can not find TLV Description for type: <1p>' expandMacrosWith: aType).
+ ]
+
+ TLVDescriptionContainer class >> decodeByteStream: aStream type: aType [
+ | description |
+
+ "This is a generic decoding method that works by finding the
+ message structure and then following the structure and will
+ return an OrderedCollection with tuples."
+
+ description := self findTLVDescription: aType.
+ ^ description decodeByteStream: aStream.
+ ]
+
+ TLVDescriptionContainer class >> encodeCollection: aCollection type: aType [
+ | description |
+
+ "This is a generic encoding method that will put the collection
+ onto a MessageBuffer class."
+
+ description := self findTLVDescription: aType.
+ ^ description encodeCollection: aCollection.
+ ]
+
+ type: aType [
+
+ type := aType.
+ ]
+
+ type [
+
+ ^ type
+ ]
+
+ addFixed: aType [
+
+ self fields add: {#fixed. aType}
+ ]
+
+ addOptional: aType [
+
+ self fields add: {#optional. aType}
+ ]
+
+ addOptionals: aType [
+
+ "Optional Parameters that may appear more than once."
+ self fields add: {#optionals. aType}
+ ]
+
+ addVariable: aType [
+
+ self fields add: {#variable. aType}
+ ]
+
+ fields [
+
+ ^ fields ifNil: [fields := OrderedCollection new]
+ ]
+
+ fieldsDo: aBlock [
+
+ ^ self fields do: [:each | aBlock value: each first value: each second]
+ ]
+
+ filter: aFilter [
+ | lst |
+
+ lst := OrderedCollection new.
+ self fields inject: lst into: [:list :each |
+ each first = aFilter ifTrue: [
+ list add: each second.
+ ].
+ list].
+ ^ lst
+ ]
+
+ filterdDo: aBlock filter: aFilter [
+
+ ^ self fields do: [:each |
+ each first = aFilter ifTrue: [
+ aBlock value: each first value: each second]].
+ ]
+
+ fixed [
+
+ ^ self filter: #fixed
+ ]
+
+ fixedDo: aBlock [
+
+ ^ self filterdDo: aBlock filter: #fixed.
+ ]
+
+ variable [
+
+ ^ self filter: #variable
+ ]
+
+ variableDo: aBlock [
+
+ ^ self filterdDo: aBlock filter: #variable.
+ ]
+
+ optional [
+
+ ^ self filter: #optional
+ ]
+
+ optionals [
+
+ ^ self filter: #optionals
+ ]
+
+ parseFixed: aStream with: aClass into: decoded [
+
+ decoded add: (aClass readFixedFrom: aStream).
+ ^ true
+ ]
+
+ parseField: aStream with: aClass into: decoded [
+ | len |
+
+
+ "Is this an empty tag"
+ aClass lengthLength = 0 ifTrue: [
+ decoded add: (aClass readVariableFrom: aStream length: 0).
+ ^ true
+ ].
+
+ len := (aStream next: aClass lengthLength) byteAt: 1.
+ decoded add: (aClass readVariableFrom: aStream length: len).
+ ^ true
+ ]
+
+ parseVariable: aStream with: aClass into: decoded [
+
+
+ ^ self parseField: aStream with: aClass into: decoded.
+ ]
+
+ parseOptional: aStream with: aClass into: decoded [
+ | tag len |
+
+ tag := aStream peek.
+ tag = aClass parameterValue ifFalse: [^ false].
+
+ aStream skip: 1.
+ self parseField: aStream with: aClass into: decoded.
+ ^ true
+ ]
+
+ parseOptionals: aStream with: aClass into: decoded [
+
+
+ [
+ self parseOptional: aStream with: aClass into: decoded.
+ ] whileTrue: [].
+ ]
+
+ prepareOptional: aStream [
+
+ "Nothing to be done here. Subclasses can manipulate the stream"
+ ]
+
+ decodeByteStream: aStream [
+ | decoded first_optional |
+
+
+ decoded := OrderedCollection new.
+ first_optional := true.
+ self fieldsDo: [:type :clazz |
+ type = #fixed ifTrue: [
+ self parseFixed: aStream with: clazz into: decoded.
+ ].
+ type = #variable ifTrue: [
+ self parseVariable: aStream with: clazz into: decoded.
+ ].
+ type = #optional ifTrue: [
+ first_optional ifTrue: [first_optional := false. self prepareOptional: aStream].
+ self parseOptional: aStream with: clazz into: decoded.
+ ].
+ type = #optionals ifTrue: [
+ first_optional ifTrue: [first_optional := false. self prepareOptional: aStream].
+ self parseOptionals: aStream with: clazz into: decoded.
+ ].
+ ].
+
+ "TODO: complain about unfetched bytes?"
+ ^ decoded
+ ]
+
+ writeFixed: msg with: clazz from: field state: aState [
+
+
+ (clazz isCompatible: field) ifFalse: [
+ ^ self error:
+ ('Mandatory information must be <1p> but was <2p>.'
+ expandMacrosWith: clazz with: field).
+ ].
+
+ msg nextPutAll: field data.
+ ]
+
+ writeVariable: msg with: clazz from: field state: aState [
+
+
+ (clazz isCompatible: field) ifFalse: [
+ ^ self error:
+ ('Variable information must be <1p> but was <2p>.'
+ expandMacrosWith: clazz with: field).
+ ].
+
+ "TODO: Respect the lengthLength here"
+ field class lengthLength > 0 ifTrue: [
+ msg nextPut: field data size.
+ msg nextPutAll: field data.
+ ]
+ ]
+
+ writeOptional: msg with: clazz from: field state: aState [
+
+
+ (clazz isCompatible: field) ifFalse: [
+ ^ self error:
+ ('Optional information must be <1p> but was <2p>.'
+ expandMacrosWith: clazz with: field).
+ ].
+
+ "TODO: Respect the lengthLength here"
+ msg nextPut: field class parameterValue.
+ field class lengthLength > 0 ifTrue: [
+ msg nextPut: field data size.
+ msg nextPutAll: field data.
+ ]
+ ]
+
+ createState [
+
+ "Subclasses can create their own state to allow jumping in the
+ stream or leave markers"
+ ^ nil
+ ]
+
+ writeFixedEnd: aStream state: aState [
+
+ "Subclasses can use me to do something at the end of fixed messages."
+ ]
+
+ writeVariableEnd: aStream state: aState [
+
+ ]
+
+ encodeCollection: aCollection [
+ | stream msg aState |
+
+
+ msg := WriteStream on: (ByteArray new: 3).
+ stream := aCollection readStream.
+ aState := self createState.
+
+ "Try to match the fields of the TLV description with the fields of
+ the collection. We keep some local state to check if we are
+ passed the fixed and variable fields."
+
+ "Write the fixed portion"
+ self fixedDo: [:type :clazz |
+ self writeFixed: msg with: clazz from: stream next state: aState.
+ ].
+ self writeFixedEnd: msg state: aState.
+
+ "Write the variable portion"
+ self variableDo: [:type :clazz |
+ self writeVariable: msg with: clazz from: stream next state: aState.
+ ].
+ self writeVariableEnd: msg state: aState.
+
+ self fieldsDo: [:type :clazz |
+ "Check if we are compatible"
+ (clazz isCompatible: stream peek) ifTrue: [
+ type = #optional ifTrue: [
+ self writeOptional: msg with: clazz from: stream next state: aState.
+ ].
+ type = #optionals ifTrue: [
+ self notYetImplemented
+ ]
+ ].
+ ].
+
+ ^ msg contents
+ ]
+]
+
+Object subclass: MSGField [
+ | data |
+
+
+
+
+ MSGField class >> isCompatible: aField [
+
+ ^ aField isKindOf: self.
+ ]
+
+ MSGField class >> readVariableFrom: aStream length: aLength [
+
+ "I verify that I am allowed to read that much and then will read it"
+ aLength < self octalLength ifTrue: [
+ ^ self error:
+ ('The data is too short. <1p> < <2p>'
+ expandMacrosWith: aLength with: self octalLength).
+ ].
+ self maxLength ifNotNil: [
+ aLength > self maxLength ifTrue: [
+ ^ self error:
+ ('The data is too long <1p> > <2p>.'
+ expandMacrosWith: aLength with: self maxLength).
+ ]
+ ].
+
+ ^ self new
+ data: (aStream next: aLength);
+ yourself
+ ]
+
+ MSGField class >> parameterName [
+
+ ^ self subclassResponsibility
+ ]
+
+ MSGField class >> parameterValue [
+
+ ^ self subclassResponsibility
+ ]
+
+ MSGField class >> lengthLength [
+ "The length of the length field. The default is to assume a length of
+ one octet and in the units of octets"
+
+ ^ 1
+ ]
+
+ MSGField class >> octalLength [
+
+ ^ self subclassResponsibility
+ ]
+
+ MSGField class >> isVarible [
+
+ "If this field is variable in length"
+ ^ self subclassResponsibility
+ ]
+
+ MSGField class >> isFixed [
+
+ "If this field is of a fixed length"
+ ^ self subclassResponsibility
+ ]
+
+ MSGField class >> maxLength [
+
+ ^ nil
+ ]
+
+ data: aData [
+
+ data := aData.
+ ]
+
+ data [
+
+ ^ data
+ ]
+]
+
+MSGField subclass: MSGFixedField [
+
+
+
+ MSGFixedField class >> isVarible [ ^ false ]
+ MSGFixedField class >> isFixed [ ^ true ]
+
+ MSGFixedField class >> readFixedFrom: aStream [
+
+ ^ self new
+ data: (aStream next: self octalLength);
+ yourself
+ ]
+
+ MSGFixedField class >> readVariableFrom: aStream length: aLength [
+
+ aLength = self octalLength ifFalse: [
+ ^ self error: 'The size needs to be exact'.
+ ].
+
+ ^ super readVariableFrom: aStream length: aLength
+ ]
+]
+
+MSGField subclass: MSGVariableField [
+
+
+
+ MSGVariableField class >> isVarible [ ^ true ]
+ MSGVariableField class >> isFixed [ ^ false ]
+]
diff --git a/osmo-st-network/core/TLV.st b/osmo-st-network/core/TLV.st
new file mode 100644
index 0000000..f15b29f
--- /dev/null
+++ b/osmo-st-network/core/TLV.st
@@ -0,0 +1,304 @@
+"
+ (C) 2012-2014 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+Object subclass: TLVDescription [
+ | tag kind parse_class type inst_var min_size max_size len_size force_tag |
+
+
+
+ TLVDescription class [
+ optional [
+
+ ^ #optional
+ ]
+
+ mandatory [
+
+ ^ #mandatory
+ ]
+
+ conditional [
+
+ ^ #conditional
+ ]
+
+ tagLengthValue [
+
+ ^ #tlv
+ ]
+
+ tagValue [
+
+ ^ #tv
+ ]
+
+ valueOnly [
+
+ ^ #valueOnly
+ ]
+
+ tagOnly [
+
+ ^ #tagOnly
+ ]
+
+ lengthValue [
+
+ ^#lv
+ ]
+
+ new [
+
+ ^ super basicNew
+ initialize;
+ yourself
+ ]
+ ]
+
+ initialize [
+
+ kind := self class mandatory.
+ type := self class tagLengthValue.
+ len_size := 1.
+ force_tag := false.
+ ]
+
+ tag: aTag [
+
+ tag := aTag
+ ]
+
+ tag [
+
+ "The tag value for this tag inside the bytestream"
+ ^ tag
+ ]
+
+ minSize: aMin maxSize: aMax [
+
+ "This only makes sense for *LV elements"
+ min_size := aMin.
+ max_size := aMax.
+ ]
+
+ minSize: aMin [
+ min_size := aMin.
+ max_size := nil.
+ ]
+
+ valueSize: aSize [
+
+ ^ self minSize: aSize maxSize: aSize.
+ ]
+
+ valueSize [
+ ^ max_size
+ ]
+
+ isOptional [
+
+ ^ kind = self class optional
+ ]
+
+ isMandatory [
+
+ ^ kind = self class mandatory
+ ]
+
+ isConditional [
+
+ ^ kind = self class conditional
+ ]
+
+ isFixedSize [
+
+ ^ type = self class tagValue or: [type = self class valueOnly].
+ ]
+
+ hasLength [
+
+ ^ type = self class tagLengthValue or: [type = self class lengthValue]
+ ]
+
+ isLen16 [
+
+ ^ self hasLength and: [len_size = 2]
+ ]
+
+ isLen8 [
+
+ ^ self hasLength and: [len_size = 1]
+ ]
+
+ isForcedTag [
+
+ ^ force_tag
+ ]
+
+ hasTag [
+
+ ^type ~= self class lengthValue and: [type ~= self class valueOnly]
+ ]
+
+ needsTag [
+
+ ^force_tag or: [self hasTag and: [self isOptional or: [self isConditional]]].
+ ]
+
+ presenceKind: aKind [
+
+ "Is this required, optional, variable?"
+ kind := aKind
+ ]
+
+ beOptional [
+
+ self presenceKind: self class optional.
+ ]
+
+ beConditional [
+
+ self presenceKind: self class conditional.
+ ]
+
+ beForceTagged [
+
+ "Write a tag even if this element is mandatory"
+ force_tag := true.
+ ]
+
+ beTagOnly [
+
+ self typeKind: self class tagOnly.
+ ]
+
+ beTV [
+
+ self typeKind: self class tagValue
+ ]
+
+ beTLV [
+
+ self typeKind: self class tagLengthValue
+ ]
+
+ beLV [
+
+ self typeKind: self class lengthValue
+ ]
+
+ beLen16 [
+
+ len_size := 2.
+ ]
+
+ typeKind: aType [
+
+ type := aType
+ ]
+
+ typeKind [
+
+ ^ type
+ ]
+
+ parseClass: aClass [
+
+ "The class to be used to parse this"
+ parse_class := aClass
+ ]
+
+ parseClass [
+
+ ^ parse_class
+ ]
+
+ instVarName: aName [
+
+ inst_var := aName
+ ]
+
+ instVarName [
+
+ ^ inst_var
+ ]
+]
+
+Object subclass: TLVParserBase [
+
+
+
+ parseMandatory: attr tag: aTag stream: aStream [
+
+ aTag = attr tag
+ ifFalse: [^self error:
+ ('Mandatory <1p> element is missing'
+ expandMacrosWith: attr instVarName).].
+ aStream skip: 1.
+
+ self doParse: attr stream: aStream.
+ ]
+
+ parseConditional: attr tag: aTag stream: aStream [
+
+ ^ self parseOptional: attr tag: aTag stream: aStream
+ ]
+
+ parseOptional: attr tag: aTag stream: aStream [
+
+ aTag = attr tag
+ ifFalse: [^false].
+
+ aStream skip: 1.
+ self doParse: attr stream: aStream.
+ ]
+
+ doParse: attr stream: aStream [
+
+ attr parseClass isNil
+ ifTrue: [^self error: 'No parse class available'].
+
+ self instVarNamed: attr instVarName
+ put: (attr parseClass readFrom: aStream with: attr).
+ ^ true
+ ]
+
+ writeOn: aMsg [
+
+
+ "Write the header"
+ self writeHeaderOn: aMsg.
+
+ "Write each element"
+ self class tlvDescription do: [:attr |
+ | val |
+ val := self instVarNamed: attr instVarName.
+
+ "Check if it may be nil"
+ (val isNil and: [attr isMandatory])
+ ifTrue: [^self error: 'Mandatory parameter is nil.'].
+
+ "Now write it"
+ val isNil ifFalse: [
+ attr needsTag ifTrue: [aMsg putByte: attr tag].
+ val writeOn: aMsg with: attr.
+ ].
+ ]
+ ]
+]
diff --git a/osmo-st-network/core/TLVTests.st b/osmo-st-network/core/TLVTests.st
new file mode 100644
index 0000000..944b535
--- /dev/null
+++ b/osmo-st-network/core/TLVTests.st
@@ -0,0 +1,73 @@
+"
+ (C) 2012,2014 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+TestCase subclass: TLVDescriptionTest [
+
+
+
+ testTLVCreation [
+ | tlv |
+
+ "Test default"
+ tlv := TLVDescription new.
+ self
+ assert: tlv isMandatory;
+ deny: tlv isOptional.
+
+ "Test update"
+ tlv presenceKind: tlv class optional.
+ self
+ assert: tlv isOptional;
+ deny: tlv isMandatory.
+
+ tlv instVarName: #bla.
+ self assert: tlv instVarName = #bla.
+
+ tlv tag: 16r23.
+ self assert: tlv tag = 16r23.
+
+ tlv beLV.
+ self
+ assert: tlv typeKind equals: #lv;
+ assert: tlv hasLength;
+ deny: tlv hasTag.
+
+ tlv beTLV.
+ self
+ assert: tlv typeKind equals: #tlv;
+ assert: tlv hasLength;
+ assert: tlv hasTag.
+
+ tlv beTagOnly.
+ self
+ assert: tlv typeKind equals: #tagOnly;
+ assert: tlv hasTag;
+ deny: tlv hasLength.
+ ]
+
+ testNeedsTag [
+ | tlv |
+ tlv := TLVDescription new
+ tag: 16r23;
+ beTV;
+ beConditional;
+ yourself.
+
+ self assert: tlv needsTag.
+ ]
+]
diff --git a/osmo-st-network/ipa/IPAConstants.st b/osmo-st-network/ipa/IPAConstants.st
new file mode 100644
index 0000000..1e8ab40
--- /dev/null
+++ b/osmo-st-network/ipa/IPAConstants.st
@@ -0,0 +1,65 @@
+"
+ (C) 2010-2012 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+Object subclass: IPAConstants [
+
+
+
+ IPAConstants class >> protocolRSL [ ^ 16r00 ]
+ IPAConstants class >> protocolMGCP [ ^ 16rFC ]
+ IPAConstants class >> protocolSCCP [ ^ 16rFD ]
+ IPAConstants class >> protocolIPA [ ^ 16rFE ]
+ IPAConstants class >> protocolOML [ ^ 16rFF ]
+ IPAConstants class >> protocolOSMO [ ^ 16rEE ]
+
+ IPAConstants class >> msgPing [ ^ 16r00 ]
+ IPAConstants class >> msgPong [ ^ 16r01 ]
+ IPAConstants class >> msgIdGet [ ^ 16r04 ]
+ IPAConstants class >> msgIdResp [ ^ 16r05 ]
+ IPAConstants class >> msgIdAck [ ^ 16r06 ]
+ IPAConstants class >> msgSCCP [ ^ 16rFF ]
+
+ IPAConstants class >> idtagSernr [ ^ 16r00 ]
+ IPAConstants class >> idtagUnitName [ ^ 16r01 ]
+ IPAConstants class >> idtagLocation1 [ ^ 16r02 ]
+ IPAConstants class >> idtagLocation2 [ ^ 16r03 ]
+ IPAConstants class >> idtagEquipVer [ ^ 16r04 ]
+ IPAConstants class >> idtagSwVersion [ ^ 16r05 ]
+ IPAConstants class >> idtagIpaddr [ ^ 16r06 ]
+ IPAConstants class >> idtagMacaddr [ ^ 16r07 ]
+ IPAConstants class >> idtagUnit [ ^ 16r08 ]
+
+ IPAConstants class >> osmoCtrl [ ^ 16r00 ]
+ IPAConstants class >> osmoMgcp [ ^ 16r01 ]
+ IPAConstants class >> osmoLac [ ^ 16r02 ]
+
+ IPAConstants class >> protocolOsmoCTRL [
+
+ ^ {self protocolOSMO. self osmoCtrl}
+ ]
+
+ IPAConstants class >> protocolOsmoMGCP [
+
+ ^ {self protocolOSMO. self osmoMgcp}
+ ]
+
+ IPAConstants class >> protocolOsmoLAC [
+
+ ^ {self protocolOSMO. self osmoLac}
+ ]
+]
diff --git a/osmo-st-network/ipa/IPAConstantsGST.st b/osmo-st-network/ipa/IPAConstantsGST.st
new file mode 100644
index 0000000..b869005
--- /dev/null
+++ b/osmo-st-network/ipa/IPAConstantsGST.st
@@ -0,0 +1,77 @@
+"
+ (C) 2010-2013 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+CCompound subclass: CPackedStruct [
+
+
+
+
+ CPackedStruct class >> declaration: array [
+ "Compile methods that implement the declaration in array."
+
+
+ self
+ declaration: array
+ inject: self superclass sizeof
+ into: [:oldOffset :alignment | oldOffset]
+ ]
+
+ CPackedStruct class >> compileSize: size align: alignment [
+
+ ^ super compileSize: size align: 1.
+ ]
+]
+
+CPackedStruct subclass: IPASCCPState [
+
+
+
+
+
+ srcAddr [
+
+ ^ SCCPAddrReference fromCData: self src.
+ ]
+
+ dstAddr [
+
+ ^ SCCPAddrReference fromCData: self dst.
+ ]
+
+ imsiString [
+ "We will need to count how many chars of the array are used"
+
+
+ 0 to: 16 do: [:index | | c |
+ c := self imsi at: index.
+ c = (Character value: 0)
+ ifTrue: [
+ ^ String fromCData: self imsi size: index.
+ ].
+ ].
+
+ ^ String fromCData: self imsi.
+ ]
+]
diff --git a/osmo-st-network/ipa/IPADispatcher.st b/osmo-st-network/ipa/IPADispatcher.st
new file mode 100644
index 0000000..cfe918a
--- /dev/null
+++ b/osmo-st-network/ipa/IPADispatcher.st
@@ -0,0 +1,58 @@
+"
+ (C) 2010 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+Object subclass: IPADispatcher [
+ | handlers |
+
+
+
+
+ IPADispatcher class >> new [
+
+ ^ super new
+ initialize;
+ yourself
+ ]
+
+ initialize [
+
+ handlers := Dictionary new.
+ ]
+
+ addHandler: aStream on: anObject with: aSelector [
+
+ handlers at: aStream put: [:msg | anObject perform: aSelector with: msg].
+ ]
+
+ addHandler: aStream on: aBlock [
+
+ handlers at: aStream put: aBlock.
+ ]
+
+ dispatch: aStream with: aData [
+ | handler |
+
+ handler := handlers at: aStream ifAbsent: [
+ self logError: ('IPADispatcher has no registered handler for <1p>'
+ expandMacrosWith: aStream) area: #ipa.
+ ^ false
+ ].
+
+ handler value: aData.
+ ]
+]
diff --git a/osmo-st-network/ipa/IPAMsg.st b/osmo-st-network/ipa/IPAMsg.st
new file mode 100644
index 0000000..36833df
--- /dev/null
+++ b/osmo-st-network/ipa/IPAMsg.st
@@ -0,0 +1,193 @@
+"
+ (C) 2012 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+Object subclass: IPAMsgRequest [
+ | data type |
+
+
+
+
+ IPAMsgRequest class >> parse: aStream [
+ | type data |
+
+ "TLV parser for the IPAMessage"
+
+ type := aStream next.
+ data := self parseTLV: aStream.
+
+ ^ self new
+ type: type;
+ data: data;
+ yourself.
+ ]
+
+ IPAMsgRequest class >> parseTLV: aStream [
+ | data |
+
+ data := OrderedCollection new.
+
+ [aStream atEnd] whileFalse: [
+ | len tag msg |
+ len := aStream next.
+ tag := aStream next.
+
+ "On requests the length counts the tag"
+ msg := len > 1
+ ifTrue: [aStream next: len]
+ ifFalse: [nil].
+
+ data add: (Association key: tag value: msg)
+ ].
+
+ ^ data
+ ]
+
+ type: aType [
+
+ type := aType.
+ ]
+
+ data: aData [
+
+ data := aData.
+ ]
+
+ tags [
+
+ ^ data collect: [:each | each key].
+ ]
+
+ hasTag: aTag [
+
+ ^ self tags includes: aTag
+ ]
+
+ dataForTag: aTag [
+
+ data do: [:each | each key = aTag ifTrue: [^each value]].
+
+ ^ SystemExceptions.NotFound
+ signalOn: self what: 'Tag ', aTag asString, ' not found'.
+ ]
+
+ writeOn: aMsg [
+
+ aMsg putByte: type.
+
+ self writeTLV: aMsg.
+ ]
+
+ writeTLV: aMsg [
+
+
+ data do: [:each |
+ "Write the length and tag"
+ aMsg
+ putByte: 1 + each value basicSize;
+ putByte: each key.
+
+ "Write the optional value"
+ each value isNil ifFalse: [
+ aMsg putByteArray: each value.].
+ ].
+ ]
+]
+
+IPAMsgRequest subclass: IPAMsgResponse [
+
+
+
+
+ IPAMsgResponse class >> parse: aStream [
+ | type data |
+
+ "TLV parser for the IPAMessage"
+
+ type := aStream next.
+ aStream skip: 1.
+ data := self parseTLV: aStream.
+
+ ^ self new
+ type: type;
+ data: data;
+ yourself.
+ ]
+
+ IPAMsgResponse class >> readByteString: aStream length: aLen [
+ "Read a IPA string from a stream that might be up to len."
+ | str |
+ str := WriteStream on: (ByteArray new: aLen).
+
+ (1 to: aLen) do: [:each |
+ | chr |
+
+ chr := aStream next.
+ str nextPut: chr.
+
+ "deal with broken messages. so far only observed at the end
+ of a packet"
+ (aStream atEnd and: [chr = 16r0])
+ ifTrue: [^ str contents].
+ ].
+
+ ^ str contents.
+ ]
+
+ IPAMsgResponse class >> parseTLV: aStream [
+ | data |
+ "The messages generated by the ip.access nanoBTS do not follow
+ the TLV pattern properly. For responses the length does not include
+ the size of the tag and to make it worse sometimes the wrong size
+ is sent, e.g. there strings are null-terminated."
+
+ data := OrderedCollection new.
+
+ [aStream atEnd] whileFalse: [
+ | tag len string |
+ len := aStream next.
+ tag := aStream next.
+ string := self readByteString: aStream length: len.
+
+ data add: (Association key: tag value: string).
+ ].
+
+ ^ data
+ ]
+
+ writeOn: aMsg [
+
+
+ aMsg putByte: type.
+ aMsg putByte: 0.
+ self writeTLV: aMsg.
+ ]
+
+ writeTLV: aMsg [
+
+ "Request/Response appear to have different size constraints"
+
+ data do: [:each |
+ "Write the length and tag"
+ aMsg
+ putByte: each value basicSize;
+ putByte: each key;
+ putByteArray: each value.
+ ].
+ ]
+]
diff --git a/osmo-st-network/ipa/IPAMuxer.st b/osmo-st-network/ipa/IPAMuxer.st
new file mode 100644
index 0000000..c0f45ca
--- /dev/null
+++ b/osmo-st-network/ipa/IPAMuxer.st
@@ -0,0 +1,108 @@
+"
+ (C) 2010-2012 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+Object subclass: IPADemuxer [
+ | socket |
+
+
+
+
+ IPADemuxer class >> initOn: aSocket [
+
+ ^ (self new)
+ socket: aSocket;
+ yourself.
+ ]
+
+ next [
+ "Return a tuple of stream and bytearray"
+
+
+ | size stream data |
+
+ size := socket nextUshort swap16.
+ stream := socket nextByte.
+ data := socket next: size.
+
+ "I know about extensions. Check if this is..."
+ stream = IPAConstants protocolOSMO ifTrue: [
+ stream := Array with: stream with: data first asInteger.
+ data := data allButFirst.
+ ].
+
+ ^ Array with: stream with: data.
+ ]
+
+ socket: aSocket [
+
+ socket := aSocket.
+ ]
+]
+
+Object subclass: IPAMuxer [
+ | socket |
+
+
+
+
+ IPAMuxer class >> initOn: aSocket [
+
+ ^ (self new)
+ socket: aSocket;
+ yourself.
+ ]
+
+ prepareNext: aData with: aStream [
+ "Write the data onto the stream"
+ | msg |
+
+
+ aData size > 65535
+ ifTrue: [
+ self logError: 'Too much data' area: #ipa.
+ self error: 'Too much data'.
+ ].
+
+ msg := MessageBuffer new.
+
+ aStream isArray
+ ifTrue: [
+ msg putLen16: aData size + aStream size - 1.
+ msg putByteArray: aStream asByteArray]
+ ifFalse: [
+ msg putLen16: aData size.
+ msg putByte: aStream].
+
+ msg putByteArray: aData.
+
+ ^ msg asByteArray.
+ ]
+
+ nextPut: aData with: aStream [
+
+ socket nextPut: (self prepareNext: aData with: aStream).
+ ]
+
+ socket: aSocket [
+
+ socket := aSocket.
+ ]
+]
diff --git a/osmo-st-network/ipa/IPAProtoHandler.st b/osmo-st-network/ipa/IPAProtoHandler.st
new file mode 100644
index 0000000..a705d3b
--- /dev/null
+++ b/osmo-st-network/ipa/IPAProtoHandler.st
@@ -0,0 +1,108 @@
+"
+ (C) 2010-2012 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+Object subclass: IPAProtoHandler [
+ | token muxer |
+
+
+
+
+ IPAProtoHandler class [
+ | handlers |
+
+ initialize [
+
+ ^ self initializeHandlers
+ ]
+ ]
+
+ IPAProtoHandler class >> initializeHandlers [
+
+ (handlers := Dictionary new)
+ at: IPAConstants msgPing put: #handlePing:;
+ at: IPAConstants msgPong put: #handlePong:;
+ at: IPAConstants msgIdGet put: #handleIdGet:;
+ at: IPAConstants msgIdAck put: #handleIdAck:.
+ ]
+
+ IPAProtoHandler class >> handlers [
+ ^ handlers ifNil: [self initialize. handlers].
+ ]
+
+ registerOn: aDispatcher [
+
+ aDispatcher addHandler: IPAConstants protocolIPA on: self with: #handleMsg:.
+ ]
+
+ muxer: aMuxer [
+
+ muxer := aMuxer.
+ ]
+
+ token: aToken [
+
+ token := aToken.
+ ]
+
+ handleMsg: aMsg [
+ | selector |
+
+
+ selector := self class handlers at: (aMsg first asInteger) ifAbsent: [
+ self logError: 'IPA message not understood ', aMsg first asInteger asString
+ area: #ipa.
+ ^ false
+ ].
+
+ self perform: selector with: aMsg.
+ ]
+
+ handlePing: aMsg [
+
+ muxer nextPut: (ByteArray with: IPAConstants msgPong) with: IPAConstants protocolIPA.
+ ]
+
+ handlePong: aMsg [
+
+ self logDebug: 'PONG' area: #ipa.
+ ]
+
+ handleIdGet: aMsg [
+ | msg |
+
+
+ msg := MessageBuffer new.
+ msg putByte: IPAConstants msgIdResp.
+ msg putLen16: token size + 1.
+ msg putByte: IPAConstants idtagUnitName.
+ msg putByteArray: token asByteArray.
+
+ muxer nextPut: msg asByteArray with: IPAConstants protocolIPA.
+ ]
+
+ handleIdAck: aMsg [
+
+ self logDebug: 'ID ACK' area: #ipa.
+ ]
+]
+
+Eval [
+ IPAProtoHandler initialize.
+]
diff --git a/osmo-st-network/ipa/IPATests.st b/osmo-st-network/ipa/IPATests.st
new file mode 100644
index 0000000..97f86cc
--- /dev/null
+++ b/osmo-st-network/ipa/IPATests.st
@@ -0,0 +1,160 @@
+"
+ (C) 2010-2012 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+TestCase subclass: IPATests [
+ | called |
+
+
+
+ IPATests class >> packageNamesUnderTest [
+
+ ^ #('OsmoNetwork')
+ ]
+
+ testMux [
+ | data mux |
+ mux := IPAMuxer new.
+
+ data := {
+ {mux prepareNext: #(1 2 3) with: IPAConstants protocolOML.
+ #(0 3 255 1 2 3) asByteArray}.
+
+ {mux prepareNext: #(1 2 3) with: IPAConstants protocolOsmoMGCP.
+ #(0 4 238 1 1 2 3) asByteArray}.
+ }.
+
+ data do: [:each |
+ self assert: each first = each second.]
+ ]
+
+ testDispatch [
+ | dispatch |
+
+
+ called := false.
+ dispatch := IPADispatcher new
+ addHandler: 16r23 on: self with: #dispatchcallback:;
+ yourself.
+ dispatch dispatch: 16r23 with: 'data'.
+ self assert: called.
+
+ called := false.
+ dispatch
+ addHandler: 16r42 on: [:msg | called := msg = 'data' ];
+ dispatch: 16r42 with: 'data'.
+ self assert: called.
+ ]
+
+ dispatchcallback: aData [
+
+ called := aData = 'data'.
+ ]
+]
+
+TestCase subclass: IPAMsgTests [
+
+
+ IPAMsgTests class >> parseOnlyData [
+ ^ Array
+ with: IPAMsgResponse->#(16r05 16r00 16r0A 16r08 16r31 16r38 16r30 16r31
+ 16r2F 16r30 16r2F 16r30 16r00 16r00 16r13 16r07
+ 16r30 16r30 16r3A 16r30 16r32 16r3A 16r39 16r35
+ 16r3A 16r30 16r30 16r3A 16r34 16r30 16r3A 16r36
+ 16r34 16r00 16r00 16r02 16r02 16r00 16r00 16r0D
+ 16r03 16r42 16r54 16r53 16r5F 16r4E 16r42 16r54
+ 16r31 16r33 16r31 16r47 16r00 16r00 16r0C 16r04
+ 16r31 16r36 16r35 16r61 16r30 16r32 16r39 16r5F
+ 16r35 16r35 16r00 16r00 16r14 16r05 16r31 16r36
+ 16r38 16r64 16r34 16r37 16r32 16r5F 16r76 16r32
+ 16r30 16r30 16r62 16r31 16r34 16r33 16r64 16r30
+ 16r00 16r00 16r18 16r01 16r6E 16r62 16r74 16r73
+ 16r2D 16r30 16r30 16r2D 16r30 16r32 16r2D 16r39
+ 16r35 16r2D 16r30 16r30 16r2D 16r34 16r30 16r2D
+ 16r36 16r34 16r00 16r00 16r0A 16r00 16r30 16r30
+ 16r31 16r30 16r32 16r37 16r32 16r39 16r00).
+ ]
+
+ IPAMsgTests class >> data [
+
+ ^ Array
+ with: IPAMsgRequest->#(16r04 16r01 16r08 16r01 16r07 16r01 16r02 16r01
+ 16r03 16r01 16r04 16r01 16r05 16r01 16r01 16r01
+ 16r00)
+ with: IPAMsgResponse->#(16r05 16r00 16r04 16r01 16r31 16r38 16r30 16r31)
+ ]
+
+ testMsgDissect [
+
+ self class data do: [:test_data | | msg stream |
+ stream := test_data value readStream.
+ msg := test_data key parse: stream.
+
+ self
+ assert: stream atEnd;
+ assert: msg toMessage asByteArray = test_data value asByteArray;
+ should: [msg dataForTag: 9] raise: SystemExceptions.NotFound;
+ deny: (msg hasTag: 9).
+ ]
+ ]
+
+ testMsgInputStrict [
+ | test_data msg stream |
+ test_data := self class data first.
+ stream := test_data value readStream.
+ msg := test_data key parse: stream.
+
+ self
+ assert: stream atEnd;
+ assert: msg tags = #(8 7 2 3 4 5 1 0) asOrderedCollection;
+ assert: (msg hasTag: 8);
+ assert: (msg dataForTag: 8) = nil.
+ ]
+
+ testParseOnly [
+ "This tests that parsing a 'malformed' response will actually
+ work, generating the response will be different though."
+
+ self class parseOnlyData do: [:test_data | | msg stream |
+ stream := test_data value readStream.
+ msg := test_data key parse: stream.
+
+ self
+ assert: stream atEnd;
+ assert: (msg hasTag: 16r0);
+ assert: (msg hasTag: 16r1);
+ assert: (msg hasTag: 16r2);
+ assert: (msg hasTag: 16r3);
+ assert: (msg hasTag: 16r4);
+ assert: (msg hasTag: 16r5);
+ assert: (msg hasTag: 16r7);
+ assert: (msg hasTag: 16r8);
+ deny: (msg hasTag: 16rA);
+ assert: (msg dataForTag: 16r0) = #(16r30 16r30 16r31 16r30 16r32 16r37 16r32 16r39 16r0) asByteArray.
+ ]
+ ]
+]
+
+
+TestCase subclass: IPAGSTTests [
+
+
+ testSize [
+ self assert: IPASCCPState sizeof = 25.
+ ]
+]
+
diff --git a/osmo-st-network/isup/ISUP.st b/osmo-st-network/isup/ISUP.st
new file mode 100644
index 0000000..1808c9e
--- /dev/null
+++ b/osmo-st-network/isup/ISUP.st
@@ -0,0 +1,472 @@
+"
+ (C) 2011-2012 by Holger Hans Peter Freyther
+ All Rights Reserved
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+"
+
+Object subclass: ISUPConstants [
+
+
+
+ ISUPConstants class [
+ msgAPT [
+ "Application transport"
+
+ ^ 2r01000001
+ ]
+
+ msgACM [
+ "Address complete"
+ ^ 2r00000110
+ ]
+
+ msgAMN [
+ "Answer"
+