├── .github └── workflows │ ├── maven-publish.yml │ └── maven.yml ├── README ├── pom.xml ├── src ├── main │ └── java │ │ ├── io │ │ └── netty │ │ │ └── handler │ │ │ └── codec │ │ │ └── compression │ │ │ └── Crc32c.java │ │ └── pe │ │ └── pi │ │ └── sctp4j │ │ └── sctp │ │ ├── Association.java │ │ ├── AssociationListener.java │ │ ├── MessageCompleteHandler.java │ │ ├── ReconfigState.java │ │ ├── SCTPByteStreamListener.java │ │ ├── SCTPMessage.java │ │ ├── SCTPOutboundStreamOpenedListener.java │ │ ├── SCTPStream.java │ │ ├── SCTPStreamListener.java │ │ ├── SCTPTimer.java │ │ ├── StreamNumberInUseException.java │ │ ├── behave │ │ ├── OldDCEPStreamBehaviour.java │ │ ├── OrderedStreamBehaviour.java │ │ ├── SCTPStreamBehaviour.java │ │ └── UnorderedStreamBehaviour.java │ │ ├── dataChannel │ │ └── DECP │ │ │ └── DCOpen.java │ │ ├── dummy │ │ └── DummyStream.java │ │ ├── leakey │ │ ├── LeakyAssociation.java │ │ └── LeakyTransport.java │ │ ├── messages │ │ ├── AbortChunk.java │ │ ├── Chunk.java │ │ ├── CookieAckChunk.java │ │ ├── CookieEchoChunk.java │ │ ├── DataChunk.java │ │ ├── ErrorChunk.java │ │ ├── HeartBeatAckChunk.java │ │ ├── HeartBeatChunk.java │ │ ├── InitAckChunk.java │ │ ├── InitChunk.java │ │ ├── Packet.java │ │ ├── ReConfigChunk.java │ │ ├── SackChunk.java │ │ ├── exceptions │ │ │ ├── ChecksumException.java │ │ │ ├── InvalidDataChunkException.java │ │ │ ├── InvalidSCTPPacketException.java │ │ │ ├── MessageException.java │ │ │ ├── SctpPacketFormatException.java │ │ │ ├── StaleCookieException.java │ │ │ └── UnreadyAssociationException.java │ │ └── params │ │ │ ├── AddIncomingStreamsRequestParameter.java │ │ │ ├── AddOutgoingStreamsRequestParameter.java │ │ │ ├── AddStreamsRequestParameter.java │ │ │ ├── CookiePreservative.java │ │ │ ├── HostNameAddress.java │ │ │ ├── IPv4Address.java │ │ │ ├── IPv6Address.java │ │ │ ├── IncomingSSNResetRequestParameter.java │ │ │ ├── KnownError.java │ │ │ ├── KnownParam.java │ │ │ ├── OutgoingSSNResetRequestParameter.java │ │ │ ├── ProtocolViolationError.java │ │ │ ├── ReconfigurationResponseParameter.java │ │ │ ├── RequestedHMACAlgorithmParameter.java │ │ │ ├── SSNTSNResetRequestParameter.java │ │ │ ├── StaleCookieError.java │ │ │ ├── StateCookie.java │ │ │ ├── SupportedAddressTypes.java │ │ │ ├── Unknown.java │ │ │ ├── UnrecognizedParameters.java │ │ │ └── VariableParam.java │ │ └── small │ │ ├── BlockingSCTPStream.java │ │ ├── MessageSizeExceededException.java │ │ ├── SimpleSCTPTimer.java │ │ ├── ThreadedAssociation.java │ │ └── UDPForwardingStream.java └── test │ └── java │ └── pe │ └── pi │ └── sctp4j │ └── sctp │ ├── SCTPMessageTest.java │ ├── behave │ ├── OrderedStreamBehaviourTest.java │ └── UnorderedStreamBehaviourTest.java │ └── small │ ├── ThreadedAssociationTest.java │ └── ThreadedAssociationTestEarlies.java └── test ├── com └── ipseorama │ └── sctp │ └── messages │ └── TestPacket.groovy └── main ├── groovy └── com │ └── ipseorama │ └── sctp │ ├── TestAssociation.groovy │ └── TestMessage.groovy └── java └── com └── ipseorama └── sctp └── MockAssociation.java /.github/workflows/maven-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a package using Maven and then publish it to GitHub packages when a release is created 2 | # For more information see: https://github.com/actions/setup-java#apache-maven-with-a-settings-path 3 | 4 | name: Maven Package 5 | 6 | on: 7 | release: 8 | types: [created] 9 | 10 | jobs: 11 | build: 12 | 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Set up JDK 11 18 | uses: actions/setup-java@v1 19 | with: 20 | java-version: 11 21 | server-id: github # Value of the distributionManagement/repository/id field of the pom.xml 22 | settings-path: ${{ github.workspace }} # location for the settings.xml file 23 | 24 | - name: Build with Maven 25 | run: mvn -B package --file pom.xml -s $GITHUB_WORKSPACE/settings.xml 26 | env: 27 | GITHUB_TOKEN: ${{ github.token }} 28 | 29 | - name: Publish to GitHub Packages Apache Maven 30 | run: mvn deploy -s $GITHUB_WORKSPACE/settings.xml 31 | env: 32 | GITHUB_TOKEN: ${{ github.token }} 33 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Java CI with Maven 5 | 6 | on: 7 | push: 8 | branches: [ main ] 9 | pull_request: 10 | branches: [ main ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 11 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 11 23 | - name: Build with Maven 24 | run: mvn -B package --file pom.xml 25 | env: 26 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | SCTP4J - by |pipe| 2 | 3 | This is a pure java implementation of the SCTP protocol. 4 | The target usecase is small devices that want to run the webRTC datachannel. 5 | 6 | This implementation does not include all the necessary parts for a full 7 | webRTC stack. You'll need DTLS (we use Bouncy Castle) and ICE/STUN/TURN (you can use Jitsi's ice4j or our slice ). 8 | 9 | This implementation assumes that datagrams will arrive from an DTLS/ICE stack 10 | which implements a Datagram Transport. It also assumes a consumer of open SCTP Streams (or datachannels) - it is pure middleware. 11 | 12 | Brief note on how to drive it: 13 | 14 | Once you have a BouncyCastle DTLS transport open to a WebRTC agent you can: 15 | 16 | public Association makeAssociation(DTLSTransport trans, AssociationListener li) { 17 | a = new ThreadedAssociation(trans, li); 18 | } 19 | The associationListener will fire events when an association is made and when inbound dataChannels are created. 20 | SCTPStreamListener sl; 21 | // create a streamListener here 22 | SCTPStream echo = a.mkStream("echo", sl); 23 | Will create an outbound stream labeled 'echo'; 24 | 25 | Background: 26 | 27 | The stack tries to keep the details of concurrency in a single package, so that the current pure thread model could be replaced with Akka actors or NIO-like async mechanisms. 28 | 29 | This implementation works well on small devices (eg Raspberry Pi, C.H.I.P etc) 30 | 31 | Thanks to Michael Tüxen (@tuexen) for help and advice 32 | Thanks to Emil Ivov (@Emcho) for Ice4J and encouragement. 33 | 34 | Some context on the long silence, we hurried a release out as opensource at the request of some friends, 35 | in the end they didn't adopt the release, so it got substantially sidelined whilst I worked on the rest of the |pipe| stack. 36 | I've now circled back and am attempting to make this a viable public project. Sorry for the delay. 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | pe.pi 6 | sctp4j 7 | 1.0.7 8 | jar 9 | 10 | sctp4j 11 | 12 | 13 | ${project.build.directory}/endorsed 14 | UTF-8 15 | 16 | 17 | 18 | 19 | github 20 | Srtplight 21 | https://maven.pkg.github.com/steely-glint/srtplight 22 | 23 | true 24 | 25 | 26 | true 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.bouncycastle 34 | bcutil-jdk18on 35 | 1.78.1 36 | 37 | 38 | org.glassfish 39 | javax.json 40 | 1.0.4 41 | 42 | 43 | org.bouncycastle 44 | bctls-jdk18on 45 | 1.78.1 46 | 47 | 48 | junit 49 | junit 50 | 4.13.1 51 | test 52 | 53 | 54 | org.hamcrest 55 | hamcrest-core 56 | 1.3 57 | test 58 | 59 | 60 | javax 61 | javaee-web-api 62 | 7.0 63 | provided 64 | 65 | 66 | com.phono 67 | srtplight 68 | 1.1.12 69 | 70 | 71 | 72 | 73 | github 74 | GitHub Packages 75 | https://maven.pkg.github.com/pipe/sctp4j 76 | 77 | 78 | 79 | 80 | 81 | org.apache.maven.plugins 82 | maven-compiler-plugin 83 | 3.1 84 | 85 | 11 86 | 11 87 | 88 | ${endorsed.dir} 89 | 90 | 91 | 92 | 93 | org.apache.maven.plugins 94 | maven-war-plugin 95 | 2.3 96 | 97 | false 98 | 99 | 100 | 101 | org.apache.maven.plugins 102 | maven-dependency-plugin 103 | 2.6 104 | 105 | 106 | validate 107 | 108 | copy 109 | 110 | 111 | ${endorsed.dir} 112 | true 113 | 114 | 115 | javax 116 | javaee-endorsed-api 117 | 7.0 118 | jar 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/main/java/io/netty/handler/codec/compression/Crc32c.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 The Netty Project 3 | * 4 | * The Netty Project licenses this file to you under the Apache License, 5 | * version 2.0 (the "License"); you may not use this file except in compliance 6 | * with the License. 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, WITHOUT 12 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | * License for the specific language governing permissions and limitations 14 | * under the License. 15 | */ 16 | package io.netty.handler.codec.compression; 17 | 18 | import java.util.zip.Checksum; 19 | 20 | /** 21 | * Implements CRC32-C as defined in: 22 | * "Optimization of Cyclic Redundancy-CHeck Codes with 24 and 32 Parity Bits", 23 | * IEEE Transactions on Communications 41(6): 883-892 (1993). 24 | * 25 | * The implementation of this class has been sourced from the Appendix of RFC 3309, 26 | * but with masking due to Java not being able to support unsigned types. 27 | */ 28 | public class Crc32c implements Checksum { 29 | private static final int[] CRC_TABLE = { 30 | 0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4, 31 | 0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB, 32 | 0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B, 33 | 0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24, 34 | 0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B, 35 | 0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384, 36 | 0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54, 37 | 0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B, 38 | 0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A, 39 | 0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35, 40 | 0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5, 41 | 0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA, 42 | 0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45, 43 | 0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A, 44 | 0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A, 45 | 0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595, 46 | 0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48, 47 | 0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957, 48 | 0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687, 49 | 0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198, 50 | 0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927, 51 | 0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38, 52 | 0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8, 53 | 0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7, 54 | 0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096, 55 | 0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789, 56 | 0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859, 57 | 0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46, 58 | 0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9, 59 | 0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6, 60 | 0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36, 61 | 0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829, 62 | 0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C, 63 | 0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93, 64 | 0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043, 65 | 0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C, 66 | 0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3, 67 | 0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC, 68 | 0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C, 69 | 0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033, 70 | 0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652, 71 | 0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D, 72 | 0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D, 73 | 0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982, 74 | 0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D, 75 | 0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622, 76 | 0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2, 77 | 0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED, 78 | 0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530, 79 | 0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F, 80 | 0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF, 81 | 0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0, 82 | 0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F, 83 | 0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540, 84 | 0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90, 85 | 0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F, 86 | 0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE, 87 | 0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1, 88 | 0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321, 89 | 0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E, 90 | 0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81, 91 | 0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E, 92 | 0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E, 93 | 0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351, 94 | }; 95 | 96 | private static final long LONG_MASK = 0xFFFFFFFFL; 97 | private static final int BYTE_MASK = 0xFF; 98 | 99 | private int crc = ~0; 100 | 101 | @Override 102 | public void update(int b) { 103 | crc = crc32c(crc, b); 104 | } 105 | 106 | @Override 107 | public void update(byte[] buffer, int offset, int length) { 108 | for (int i = offset; i < offset + length; i++) { 109 | crc = crc32c(crc, buffer[i]); 110 | } 111 | } 112 | 113 | @Override 114 | public long getValue() { 115 | return (crc ^ LONG_MASK) & LONG_MASK; 116 | } 117 | 118 | @Override 119 | public void reset() { 120 | crc = ~0; 121 | } 122 | 123 | private static int crc32c(int crc, int b) { 124 | return crc >>> 8 ^ CRC_TABLE[(crc ^ b & BYTE_MASK) & BYTE_MASK]; 125 | } 126 | } 127 | 128 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/AssociationListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp; 18 | 19 | /** 20 | * 21 | * @author Westhawk Ltd 22 | */ 23 | public interface AssociationListener { 24 | 25 | public void onAssociated(Association a); 26 | 27 | public void onDisAssociated(Association a); 28 | 29 | public void onDCEPStream(SCTPStream s, String label, int type) throws Exception ; 30 | 31 | public void onRawStream(SCTPStream s); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/MessageCompleteHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp; 19 | 20 | import pe.pi.sctp4j.sctp.messages.exceptions.MessageException; 21 | 22 | /** 23 | * 24 | * @author Westhawk Ltd 25 | */ 26 | public interface MessageCompleteHandler { 27 | /** 28 | * 29 | * @param m message which has completed 30 | * @param me Null if message was delivered - or indicates failure mode 31 | */ 32 | public void messageComplete(SCTPMessage m, MessageException me); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/ReconfigState.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp; 18 | 19 | import pe.pi.sctp4j.sctp.messages.Chunk; 20 | import pe.pi.sctp4j.sctp.messages.ReConfigChunk; 21 | import pe.pi.sctp4j.sctp.messages.params.IncomingSSNResetRequestParameter; 22 | import pe.pi.sctp4j.sctp.messages.params.OutgoingSSNResetRequestParameter; 23 | import pe.pi.sctp4j.sctp.messages.params.ReconfigurationResponseParameter; 24 | import com.phono.srtplight.Log; 25 | import java.util.concurrent.ConcurrentLinkedQueue; 26 | 27 | /** 28 | * 29 | * @author thp 30 | */ 31 | class ReconfigState { 32 | 33 | ReConfigChunk recentInbound = null; 34 | ReConfigChunk recentOutboundRequest = null; 35 | ReConfigChunk sentReply = null; 36 | boolean timerRunning = false; 37 | long nearSeqno = 0; 38 | long farSeqno = 0; 39 | Association assoc; 40 | ConcurrentLinkedQueue listOfStreamsToReset; 41 | 42 | ReconfigState(Association a, long farTSN) { 43 | nearSeqno = a.getNearTSN(); 44 | farSeqno = farTSN; 45 | assoc = a; 46 | listOfStreamsToReset = new ConcurrentLinkedQueue(); 47 | } 48 | 49 | private boolean haveSeen(ReConfigChunk rconf) { 50 | return rconf.sameAs(recentInbound); 51 | } 52 | 53 | private ReConfigChunk getPrevious(ReConfigChunk rconf) { 54 | return rconf.sameAs(recentInbound) ? sentReply : null; 55 | } 56 | 57 | private boolean timerIsRunning() { 58 | return timerRunning; 59 | } 60 | 61 | private void markAsAcked(ReConfigChunk rconf) { 62 | // ooh, what does this button do ??? To Do 63 | } 64 | 65 | private long nextNearNo() { 66 | return nearSeqno++; 67 | } 68 | 69 | private long nextFarNo() { 70 | return farSeqno++; 71 | } 72 | 73 | public long nextDue() { 74 | return 1000L; 75 | } 76 | 77 | /* 78 | * https://tools.ietf.org/html/rfc6525 79 | */ 80 | Chunk[] deal(ReConfigChunk rconf) { 81 | Chunk[] ret = new Chunk[1]; 82 | ReConfigChunk reply = null; 83 | Log.debug("Got a reconfig message to deal with"); 84 | if (haveSeen(rconf)) { 85 | // if not - is this a repeat 86 | reply = getPrevious(rconf); // then send the same reply 87 | } 88 | if (reply == null) { 89 | // not a repeat then 90 | reply = new ReConfigChunk(); // create a new thing 91 | if (rconf.hasOutgoingReset()) { 92 | OutgoingSSNResetRequestParameter oreset = rconf.getOutgoingReset(); 93 | int[] streams = oreset.getStreams(); 94 | if (streams.length == 0) { 95 | streams = assoc.allStreams(); 96 | } 97 | if (timerIsRunning()) { 98 | markAsAcked(rconf); 99 | } 100 | // if we are behind, we are supposed to wait untill we catch up. 101 | if (oreset.getLastAssignedTSN() > assoc.getCumAckPt()) { 102 | Log.debug("Last assigned > farTSN " + oreset.getLastAssignedTSN() + " v " + assoc.getCumAckPt()); 103 | for (int s : streams) { 104 | SCTPStream defstr = assoc.getStream(s); 105 | defstr.setDeferred(true); 106 | } 107 | ReconfigurationResponseParameter rep = new ReconfigurationResponseParameter(); 108 | rep.setSeq(oreset.getReqSeqNo()); 109 | rep.setResult(rep.IN_PROGRESS); 110 | reply.addParam(rep); 111 | } else { 112 | // somehow invoke this when TSN catches up ?!?! ToDo 113 | Log.debug("we are up-to-date "); 114 | ReconfigurationResponseParameter rep = new ReconfigurationResponseParameter(); 115 | rep.setSeq(oreset.getReqSeqNo()); 116 | int result = streams.length > 0 ? rep.SUCCESS_PERFORMED : rep.SUCCESS_NOTHING_TO_DO; 117 | rep.setResult(result); // assume all good 118 | for (int s : streams) { 119 | SCTPStream cstrm = assoc.delStream(s); 120 | if (cstrm == null) { 121 | Log.error("(re)Close a non existant stream ="+s); 122 | //rep.setResult(rep.ERROR_WRONG_SSN); 123 | //break; 124 | // bidriectional might be a problem here... 125 | } else { 126 | cstrm.reset(); 127 | } 128 | } 129 | reply.addParam(rep); 130 | } 131 | } 132 | // ponder putting this in a second chunk ? 133 | if (rconf.hasIncomingReset()) { 134 | IncomingSSNResetRequestParameter ireset = rconf.getIncomingReset(); 135 | /*The Re-configuration 136 | Response Sequence Number of the Outgoing SSN Reset Request 137 | Parameter MUST be the Re-configuration Request Sequence Number 138 | of the Incoming SSN Reset Request Parameter. */ 139 | OutgoingSSNResetRequestParameter rep = new OutgoingSSNResetRequestParameter(nextNearNo(), ireset.getReqNo(), assoc.getNearTSN()); 140 | int[] streams = ireset.getStreams(); 141 | rep.setStreams(streams); 142 | if (streams.length == 0) { 143 | streams = assoc.allStreams(); 144 | } 145 | for (int s : streams) { 146 | SCTPStream st = assoc.getStream(s); 147 | if (st != null) { 148 | st.setClosing(true); 149 | } 150 | } 151 | reply.addParam(rep); 152 | // set outbound timer running here ??? 153 | Log.debug("Ireset " + ireset); 154 | } 155 | } 156 | if (reply.hasParam()) { 157 | ret[0] = reply; 158 | // todo should add sack here 159 | Log.debug("about to reply with " + reply.toString()); 160 | } else { 161 | ret = null; 162 | } 163 | return ret; 164 | } 165 | 166 | /* we can only demand they close their outbound streams */ 167 | /* we can request they start to close inbound (ie ask us to shut our outbound */ 168 | /* DCEP treats streams as bi-directional - so this is somewhat of an inpedance mis-match */ 169 | /* resulting in a temporary 'half closed' state */ 170 | /* mull this over.... */ 171 | ReConfigChunk makeClose(SCTPStream st) throws Exception { 172 | ReConfigChunk ret = null; 173 | Log.debug("building reconfig so close stream " + st); 174 | st.setClosing(true); 175 | listOfStreamsToReset.add(st); 176 | if (!timerIsRunning()) { 177 | ret = makeSSNResets(); 178 | } 179 | return ret; 180 | } 181 | 182 | private ReConfigChunk makeSSNResets() throws Exception { 183 | 184 | ReConfigChunk reply = new ReConfigChunk(); // create a new thing 185 | Log.debug("closing streams n=" + listOfStreamsToReset.size()); 186 | int[] streams = listOfStreamsToReset.stream().filter((SCTPStream s) -> { 187 | return s.InboundIsOpen(); 188 | }).mapToInt((SCTPStream s) -> { 189 | return s.getNum(); 190 | }).toArray(); 191 | if (streams.length > 0) { 192 | OutgoingSSNResetRequestParameter rep = new OutgoingSSNResetRequestParameter(nextNearNo(), farSeqno - 1, assoc.getNearTSN()); 193 | rep.setStreams(streams); 194 | reply.addParam(rep); 195 | } 196 | streams = listOfStreamsToReset.stream().filter((SCTPStream s) -> { 197 | return s.OutboundIsOpen(); 198 | }).mapToInt((SCTPStream s) -> { 199 | return s.getNum(); 200 | }).toArray(); 201 | if (streams.length > 0) { 202 | IncomingSSNResetRequestParameter rep = new IncomingSSNResetRequestParameter(nextNearNo()); 203 | rep.setStreams(streams); 204 | reply.addParam(rep); 205 | } 206 | Log.debug("reconfig chunk is " + reply.toString()); 207 | return reply; 208 | } 209 | 210 | 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/SCTPByteStreamListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | public interface SCTPByteStreamListener extends SCTPStreamListener{ 25 | public void onMessage(SCTPStream s, byte[] message); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/SCTPMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp; 18 | 19 | import pe.pi.sctp4j.sctp.messages.DataChunk; 20 | import com.phono.srtplight.Log; 21 | import java.nio.ByteBuffer; 22 | import java.util.SortedSet; 23 | import pe.pi.sctp4j.sctp.behave.SCTPStreamBehaviour; 24 | import pe.pi.sctp4j.sctp.dataChannel.DECP.DCOpen; 25 | 26 | /** 27 | * 28 | * @author Westhawk Ltd 29 | */ 30 | public class SCTPMessage implements Runnable { 31 | 32 | private final SCTPStream _stream; 33 | private final byte[] _data; 34 | private int _offset = 0; 35 | private int _pPid = 0; 36 | private int _mseq; // note do we need these ? 37 | private SCTPStreamListener _li; 38 | private boolean _delivered; 39 | private Runnable onAcked; 40 | 41 | /** 42 | * Outbound message - note that we assume no one will mess with data between 43 | * calls to fill() 44 | * 45 | * @param data 46 | * @param s 47 | */ 48 | public SCTPMessage(byte[] data, SCTPStream s) { 49 | _data = (data.length > 0) ? data : new byte[1]; 50 | _stream = s; 51 | _pPid = (data.length > 0) ? DataChunk.WEBRTCBINARY : DataChunk.WEBRTCBINARYEMPTY; 52 | } 53 | 54 | public SCTPMessage(String data, SCTPStream s) { 55 | _data = (data.length() > 0) ? data.getBytes() : new byte[1]; 56 | _stream = s; 57 | _pPid = (data.length() > 0) ? DataChunk.WEBRTCSTRING : DataChunk.WEBRTCSTRINGEMPTY; 58 | } 59 | 60 | public SCTPMessage(DCOpen dcep, SCTPStream s) { 61 | byte[] data = dcep.getBytes(); 62 | _data = (data.length > 0) ? data : new byte[1]; 63 | _stream = s; 64 | _pPid = (data.length > 0) ? DataChunk.WEBRTCCONTROL : DataChunk.WEBRTCBINARYEMPTY; 65 | } 66 | 67 | public SCTPMessage(SCTPStream s, SortedSet chunks) { 68 | _stream = s; 69 | int tot = 0; 70 | if ((chunks.first().getFlags() & DataChunk.BEGINFLAG) == 0) { 71 | throw new IllegalArgumentException("must start with 'start' chunk"); 72 | } 73 | if ((chunks.last().getFlags() & DataChunk.ENDFLAG) == 0) { 74 | throw new IllegalArgumentException("must end with 'end' chunk"); 75 | } 76 | _pPid = chunks.first().getPpid(); 77 | for (DataChunk dc : chunks) { 78 | tot += dc.getDataSize(); 79 | if (_pPid != dc.getPpid()) { 80 | // aaagh 81 | throw new IllegalArgumentException("chunk has wrong ppid" + dc.getPpid() + " vs " + _pPid); 82 | } 83 | } 84 | _data = new byte[tot]; 85 | int offs = 0; 86 | for (DataChunk dc : chunks) { 87 | System.arraycopy(dc.getData(), 0, _data, offs, dc.getDataSize()); 88 | offs += dc.getDataSize(); 89 | } 90 | } 91 | 92 | public SCTPMessage(SCTPStream s, DataChunk singleChunk) { 93 | _stream = s; 94 | int flags = singleChunk.getFlags(); 95 | if ((flags & singleChunk.SINGLEFLAG) > 0) { 96 | _data = singleChunk.getData(); 97 | _pPid = singleChunk.getPpid(); 98 | } else { 99 | throw new IllegalArgumentException("must use a 'single' chunk"); 100 | } 101 | } 102 | 103 | public void setCompleteHandler(MessageCompleteHandler mch) { 104 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 105 | } 106 | 107 | public boolean hasMoreData() { 108 | return (_offset < _data.length); 109 | } 110 | 111 | /** 112 | * available datachunks are put here to be filled with data from this 113 | * outbound message 114 | * 115 | * @param dc 116 | */ 117 | public void fill(DataChunk dc) { 118 | int dsz = dc.getCapacity(); 119 | int remain = _data.length - _offset; 120 | if (_offset == 0) { 121 | if (remain <= dsz) { 122 | // only one chunk 123 | dc.setFlags(dc.SINGLEFLAG); 124 | dc.setData(_data); 125 | _offset = _data.length; 126 | } else { 127 | // first chunk of many 128 | dc.setFlags(dc.BEGINFLAG); 129 | dc.setData(_data, _offset, dsz); 130 | _offset += dsz; 131 | } 132 | } else {// not first 133 | if (remain <= dsz) { 134 | // last chunk, this will all fit. 135 | dc.setFlags(dc.ENDFLAG); 136 | dc.setData(_data, _offset, remain); 137 | _offset += remain; // should be _data_length now 138 | } else { 139 | // middle chunk. 140 | dc.setFlags(0); 141 | dc.setData(_data, _offset, dsz); 142 | _offset += dsz; 143 | } 144 | } 145 | dc.setPpid(_pPid); 146 | dc.setsSeqNo(_mseq); 147 | _stream.outbound(dc); 148 | } 149 | 150 | public boolean deliver(SCTPStreamListener li) { 151 | _li = li; 152 | _delivered = false; 153 | Log.debug("delegating message delivery to stream of type " + _stream.getClass().getSimpleName()); 154 | _stream.deliverMessage(this); 155 | return true; 156 | } 157 | 158 | public byte[] getData() { 159 | return _data; 160 | } 161 | 162 | public void setSeq(int mseq) { 163 | _mseq = mseq; 164 | } 165 | 166 | public int getSeq() { 167 | return _mseq; 168 | } 169 | 170 | private void dcepMessageDeal(byte[] data) { 171 | ByteBuffer bb = ByteBuffer.wrap(data); 172 | try { 173 | DCOpen dcep = new DCOpen(bb); 174 | SCTPStreamBehaviour behave = dcep.mkStreamBehaviour(); 175 | _stream.setBehave(behave); 176 | if (!dcep.isAck()) { 177 | Log.debug("decp open " + dcep.toString()); 178 | _stream.setLabel(dcep.getLabel()); 179 | try { 180 | _stream.openAck(dcep); 181 | _stream.alOnDCEPStream(_stream, _stream.getLabel(), _pPid); 182 | // really you would rather have these two the other way around. Not acking 183 | // unless the creation fully works, But this is a 0-rtt protocol - so 184 | // so you have to ack before the stream can send anything. 185 | } catch (Exception x) { 186 | Log.error("Dcep ack failed to send"); 187 | if (Log.getLevel() >= Log.DEBUG) {x.printStackTrace();} 188 | try { 189 | _stream.close(); 190 | } catch (Exception sx) { 191 | Log.error("Can't close " + _stream.toString() + " because " + x.getMessage()); 192 | } 193 | } 194 | } else { 195 | Log.debug("got a dcep ack for " + _stream.getLabel()); 196 | if ((_li != null) && (_li instanceof SCTPOutboundStreamOpenedListener)) { 197 | ((SCTPOutboundStreamOpenedListener) _li).opened(_stream); 198 | } 199 | } 200 | } catch (Exception x) { 201 | Log.error("Problem with DCOpen " + x.getMessage()); 202 | if (Log.getLevel() >= Log.DEBUG) {x.printStackTrace();} 203 | 204 | } 205 | } 206 | 207 | @Override 208 | public void run() { 209 | Log.debug("delegated message delivery from stream of type " + _stream.getClass().getSimpleName()); 210 | if (_li != null){ 211 | Log.debug("delegated message delivery to listener of type " + _li.getClass().getSimpleName()); 212 | } 213 | byte data[] = _data; 214 | switch (_pPid) { 215 | case DataChunk.WEBRTCBINARYEMPTY: 216 | data = new byte[0]; 217 | case DataChunk.WEBRTCBINARY: 218 | if ((_li != null) && (_li instanceof SCTPByteStreamListener)) { 219 | ((SCTPByteStreamListener) _li).onMessage(_stream, data); 220 | _delivered = true; 221 | } else { 222 | _stream.earlyMessageEnqueue(this); 223 | } 224 | break; 225 | case DataChunk.WEBRTCSTRINGEMPTY: 226 | data = new byte[0]; 227 | case DataChunk.WEBRTCSTRING: 228 | if (_li != null) { 229 | _li.onMessage(_stream, new String(data)); 230 | _delivered = true; 231 | } else { 232 | _stream.earlyMessageEnqueue(this); 233 | } 234 | break; 235 | case DataChunk.WEBRTCCONTROL: 236 | dcepMessageDeal(data); 237 | _delivered = true; 238 | break; 239 | } 240 | 241 | if (!_delivered) { 242 | Log.debug("Undelivered message to " + (_stream == null ? "null stream" : _stream.getLabel()) + " via " + (_li == null ? "null listener" : _li.getClass().getSimpleName()) + " ppid is " + _pPid); 243 | } 244 | } 245 | 246 | public void setAckCallBack(Runnable r) { 247 | onAcked = r; 248 | } 249 | 250 | public void acked() { 251 | if (onAcked != null) { 252 | onAcked.run(); 253 | } 254 | } 255 | 256 | } 257 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/SCTPOutboundStreamOpenedListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package pe.pi.sctp4j.sctp; 7 | 8 | /** 9 | * 10 | * @author thp 11 | */ 12 | public interface SCTPOutboundStreamOpenedListener { 13 | public void opened(SCTPStream s); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/SCTPStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp; 18 | 19 | import pe.pi.sctp4j.sctp.behave.OrderedStreamBehaviour; 20 | import pe.pi.sctp4j.sctp.behave.SCTPStreamBehaviour; 21 | import pe.pi.sctp4j.sctp.messages.Chunk; 22 | import pe.pi.sctp4j.sctp.messages.DataChunk; 23 | import com.phono.srtplight.Log; 24 | import java.util.concurrent.ConcurrentSkipListSet; 25 | import java.util.concurrent.LinkedBlockingQueue; 26 | import pe.pi.sctp4j.sctp.dataChannel.DECP.DCOpen; 27 | 28 | /** 29 | * 30 | * @author Westhawk Ltd 31 | */ 32 | public abstract class SCTPStream { 33 | 34 | /* unfortunately a webRTC SCTP stream can change it's reliability rules etc post creation 35 | so we can't encapsulate the streams into multiple implementations of the same interface/abstract 36 | So what we do is put the bulk of the stream code here, then delegate the variant rules off to the 37 | behave class - which has to be stateless since it can be swapped out - it is ugly 38 | - and I wonder if closures would do it better. 39 | */ 40 | private SCTPStreamBehaviour _behave; 41 | Association _ass; 42 | private Integer _sno; 43 | private String _label; 44 | private ConcurrentSkipListSet _stash; 45 | private SCTPStreamListener _sl; 46 | private int _nextMessageSeqIn; 47 | private int _nextMessageSeqOut; 48 | protected LinkedBlockingQueue _earlyQueue; 49 | private boolean closing; 50 | private State state = State.OPEN; 51 | 52 | public boolean InboundIsOpen() { 53 | return ((state == State.OPEN) || (state == State.INBOUNDONLY)); 54 | } 55 | 56 | public boolean OutboundIsOpen() { 57 | return ((state == State.OPEN) || (state == State.OUTBOUNDONLY)); 58 | } 59 | 60 | public Chunk immediateClose() { 61 | Chunk ret = null; 62 | try { 63 | ret = _ass.addToCloseList(this); 64 | } catch (Exception ex) { 65 | Log.error("Can't make immediate close for " + this._sno + " because " + ex.getMessage()); 66 | } 67 | return ret; 68 | } 69 | 70 | abstract public void delivered(DataChunk d); 71 | 72 | public SCTPStreamListener getSCTPStreamListener() { 73 | return _sl; 74 | } 75 | 76 | @Override 77 | public String toString() { 78 | return this.getClass().getSimpleName() 79 | + "[" + this._sno + "]" 80 | + "=" + this._label 81 | + "|" + _behave.getClass().getSimpleName() + "|" 82 | + "->" 83 | + ((_sl != null) ? _sl.getClass().getSimpleName() : "null"); 84 | } 85 | 86 | public String getBehave(){ 87 | return (_behave==null)?"unknown":_behave.getClass().getSimpleName(); 88 | } 89 | /* 90 | void send(SCTPMessage mess) { 91 | try { 92 | _ass.sendAndBlock(mess); 93 | } catch (Exception ex) { 94 | Log.warn("Can't send SCTPmessage because "+ex.getMessage()); 95 | } 96 | } 97 | */ 98 | synchronized void setAsNextMessage(SCTPMessage m) { 99 | int mseq = getNextMessageSeqOut(); 100 | setNextMessageSeqOut(mseq + 1); 101 | m.setSeq(mseq); 102 | } 103 | 104 | public void openAck(DCOpen dcep) throws Exception { 105 | DCOpen ack = DCOpen.mkAck(); 106 | Log.debug("made a dcep ack for "+_label); 107 | send(ack); 108 | } 109 | 110 | protected void alOnDCEPStream(SCTPStream _stream, String label, int _pPid) throws Exception { 111 | _ass.alOnDCEPStream(_stream, label, _pPid); 112 | } 113 | 114 | void earlyMessageEnqueue(SCTPMessage early) { 115 | Log.debug("enqueue an early message seq "+early.getSeq()+" on "+this.toString()); 116 | _earlyQueue.add(early); 117 | } 118 | 119 | enum State { 120 | CLOSED, INBOUNDONLY, OUTBOUNDONLY, OPEN 121 | } 122 | 123 | public SCTPStream(Association a, Integer id) { 124 | _ass = a; 125 | _sno = id; 126 | _stash = new ConcurrentSkipListSet(); // sort bt tsn 127 | _behave = new OrderedStreamBehaviour(); // default 'till we know different 128 | _earlyQueue = new LinkedBlockingQueue(100); 129 | } 130 | 131 | public void setLabel(String l) { 132 | _label = l; 133 | } 134 | 135 | public Integer getNum() { 136 | return _sno; 137 | } 138 | 139 | public Chunk[] append(DataChunk dc) { 140 | Log.debug("adding data to stash on stream " + ((_label == null) ? "*unnamed*" : _label) + "(" + dc + ")"); 141 | _stash.add(dc); 142 | return _behave.respond(this); 143 | } 144 | 145 | /** 146 | * note that behaviours must be stateless - since they can be swapped out 147 | * when we finally get the 'open' 148 | * 149 | * @param behave 150 | */ 151 | public void setBehave(SCTPStreamBehaviour behave) { 152 | _behave = behave; 153 | } 154 | 155 | // seqno management. 156 | /** 157 | * annotate the outgoing chunk with stuff this stream knows. 158 | * 159 | * @param chunk 160 | */ 161 | public void outbound(DataChunk chunk) { 162 | chunk.setStreamId(_sno); 163 | // roll seqno here.... hopefully.... 164 | } 165 | 166 | void inbound(DataChunk dc) { 167 | if (_behave != null) { 168 | _behave.deliver(this, _stash, _sl); 169 | } else { 170 | Log.warn("No behaviour set"); 171 | } 172 | } 173 | 174 | public String getLabel() { 175 | return _label; 176 | } 177 | 178 | int stashCap() { 179 | int ret = 0; 180 | for (DataChunk d : _stash) { 181 | ret += d.getData().length; 182 | } 183 | return ret; 184 | } 185 | 186 | public void setSCTPStreamListener(SCTPStreamListener sl) { 187 | _sl = sl; 188 | Log.debug("adding listener for "+this._label+" of "+sl.getClass().getName()); 189 | if (_earlyQueue != null) { 190 | Log.debug("delivering early " + _earlyQueue.size() + " messages to "+sl.getClass().getName()); 191 | SCTPMessage e = null; 192 | while (null != (e = _earlyQueue.poll())) { 193 | e.deliver(_sl); 194 | } 195 | } else { 196 | Log.debug("no early queue for "+_label); 197 | } 198 | } 199 | 200 | abstract public void send(String message) throws Exception; 201 | 202 | abstract public void send(byte[] message) throws Exception; 203 | 204 | abstract public void send(DCOpen message) throws Exception; 205 | 206 | public Association getAssociation() { 207 | return _ass; 208 | } 209 | 210 | public void close() throws Exception { 211 | Log.debug("closing stream " + this._label + " " + this._sno); 212 | _ass.closeStream(this); 213 | } 214 | 215 | public void setNextMessageSeqIn(int expectedSeq) { 216 | _nextMessageSeqIn = (expectedSeq == 1 + Character.MAX_VALUE) ? 0 : expectedSeq; 217 | } 218 | 219 | public int getNextMessageSeqIn() { 220 | return _nextMessageSeqIn; 221 | } 222 | 223 | public void setNextMessageSeqOut(int expectedSeq) { 224 | _nextMessageSeqOut = (expectedSeq == 1 + Character.MAX_VALUE) ? 0 : expectedSeq; 225 | } 226 | 227 | public int getNextMessageSeqOut() { 228 | return _nextMessageSeqOut; 229 | } 230 | 231 | abstract public void deliverMessage(SCTPMessage message); 232 | 233 | void setDeferred(boolean b) { 234 | boolean deferred = true; 235 | } 236 | 237 | void reset() { 238 | Log.debug("Resetting stream " + this._sno); 239 | if (this._sl != null) { 240 | _sl.close(this); 241 | } 242 | } 243 | 244 | void setClosing(boolean b) { 245 | closing = b; 246 | } 247 | 248 | boolean isClosing() { 249 | return closing; 250 | } 251 | 252 | void setOutboundClosed() { 253 | switch (state) { 254 | case OPEN: 255 | state = State.INBOUNDONLY; 256 | break; 257 | case OUTBOUNDONLY: 258 | state = State.CLOSED; 259 | break; 260 | case CLOSED: 261 | case INBOUNDONLY: 262 | break; 263 | } 264 | Log.debug("Stream State for " + _sno + " is now " + state); 265 | } 266 | 267 | void setInboundClosed() { 268 | switch (state) { 269 | case OPEN: 270 | state = State.OUTBOUNDONLY; 271 | break; 272 | case INBOUNDONLY: 273 | state = State.CLOSED; 274 | break; 275 | case CLOSED: 276 | case OUTBOUNDONLY: 277 | break; 278 | } 279 | Log.debug("Stream State for " + _sno + " is now " + state); 280 | } 281 | 282 | State getState() { 283 | Log.debug("Stream State for " + _sno + " is currently " + state); 284 | return state; 285 | } 286 | 287 | public boolean idle() { 288 | return true; 289 | } 290 | } 291 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/SCTPStreamListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | public interface SCTPStreamListener { 25 | public void onMessage(SCTPStream s, String message); 26 | 27 | public void close(SCTPStream aThis); 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/SCTPTimer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp; 19 | 20 | /** 21 | * interface that hides the threading implementation. 22 | * @author Westhawk Ltd 23 | */ 24 | public interface SCTPTimer { 25 | public void setRunnable(Runnable r,long at); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/StreamNumberInUseException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | public class StreamNumberInUseException extends Exception { 25 | 26 | public StreamNumberInUseException() { 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/behave/OldDCEPStreamBehaviour.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.behave; 18 | 19 | import pe.pi.sctp4j.sctp.SCTPStream; 20 | import pe.pi.sctp4j.sctp.SCTPStreamListener; 21 | import pe.pi.sctp4j.sctp.messages.Chunk; 22 | import pe.pi.sctp4j.sctp.messages.DataChunk; 23 | import com.phono.srtplight.Log; 24 | import java.util.SortedSet; 25 | import pe.pi.sctp4j.sctp.AssociationListener; 26 | import pe.pi.sctp4j.sctp.SCTPOutboundStreamOpenedListener; 27 | import pe.pi.sctp4j.sctp.dataChannel.DECP.DCOpen; 28 | 29 | /** 30 | * 31 | * @author tim what DCEPS do 32 | */ 33 | public class OldDCEPStreamBehaviour implements 34 | SCTPStreamBehaviour { 35 | 36 | private final AssociationListener al; 37 | 38 | public OldDCEPStreamBehaviour(AssociationListener associationListener) { 39 | al = associationListener; 40 | Log.debug("DCEPStreamBehaviour"); 41 | } 42 | 43 | @Override 44 | public Chunk[] respond(SCTPStream a) { 45 | return null; 46 | } 47 | 48 | @Override 49 | public void deliver(SCTPStream s, SortedSet a, SCTPStreamListener l) { 50 | DataChunk dc = a.first(); 51 | int flags = dc.getFlags() & DataChunk.SINGLEFLAG; // mask to the bits we want 52 | long tsn = dc.getTsn(); 53 | int messageNo = s.getNextMessageSeqIn(); 54 | 55 | 56 | 57 | // only interested in the first chunk which should be an ack or an open. 58 | DCOpen dcep = dc.getDCEP(); 59 | if (dcep != null) { 60 | Log.debug("DCEPStreamBehaviour has a dcep first."); 61 | if(flags != DataChunk.SINGLEFLAG){ 62 | Log.error("Dcep isn't a single !!?!"); 63 | } 64 | messageNo++; 65 | s.setNextMessageSeqIn(messageNo); 66 | a.remove(dc); 67 | SCTPStreamBehaviour behave = dcep.mkStreamBehaviour(); 68 | s.setBehave(behave); 69 | if (!dcep.isAck()) { 70 | Log.debug("decp open " + dcep.toString()); 71 | s.setLabel(dcep.getLabel()); 72 | try { 73 | s.openAck(dcep); 74 | al.onDCEPStream(s, s.getLabel(), dc.getPpid()); 75 | // really you would rather have these two the other way around. Not acking 76 | // unless the creation fully works, But this is a 0-rtt protocol - so 77 | // it is best to ack asap. 78 | } catch (Exception x) { 79 | try { 80 | s.close(); 81 | } catch (Exception sx) { 82 | Log.error("Can't close " + s.toString() + " because " + x.getMessage()); 83 | } 84 | } 85 | } else { 86 | Log.debug("got a dcep ack for " + s.getLabel()); 87 | if ((l != null) && (l instanceof SCTPOutboundStreamOpenedListener)) { 88 | ((SCTPOutboundStreamOpenedListener) l).opened(s); 89 | } 90 | } 91 | 92 | // and consume the rest using the new behave. 93 | if (behave != null) { 94 | behave.deliver(s, a, l); 95 | } 96 | } else { 97 | Log.debug("Cant deliver chunks before DCEP"); 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/behave/OrderedStreamBehaviour.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.behave; 18 | 19 | import pe.pi.sctp4j.sctp.SCTPMessage; 20 | import pe.pi.sctp4j.sctp.SCTPStream; 21 | import pe.pi.sctp4j.sctp.SCTPStreamListener; 22 | import pe.pi.sctp4j.sctp.messages.Chunk; 23 | import pe.pi.sctp4j.sctp.messages.DataChunk; 24 | import com.phono.srtplight.Log; 25 | import java.util.ArrayList; 26 | import java.util.SortedSet; 27 | import java.util.TreeSet; 28 | 29 | /** 30 | * 31 | * @author tim 32 | */ 33 | public class OrderedStreamBehaviour implements SCTPStreamBehaviour { 34 | 35 | protected boolean _ordered = true; 36 | 37 | @Override 38 | public void deliver(SCTPStream s, SortedSet stash, SCTPStreamListener l) { 39 | //stash is the list of all DataChunks that have not yet been turned into whole messages 40 | //we assume that it is sorted by stream sequence number. 41 | ArrayList delivered = new ArrayList(); 42 | SortedSet message = null; 43 | if (stash.isEmpty()){ 44 | return; // I'm not fond of these early returns 45 | } 46 | long expectedTsn = stash.first().getTsn(); // This ignores gaps - but _hopefully_ messageNo will catch any 47 | // gaps we care about - ie gaps in the sequence for _this_ stream 48 | // we can deliver ordered messages on this stream even if earlier messages from other streams are missing 49 | // - this does assume that the tsn's of a message are contiguous -which is odd. 50 | 51 | 52 | for (DataChunk dc : stash) { 53 | int messageNo = s.getNextMessageSeqIn(); 54 | 55 | int flags = dc.getFlags() & DataChunk.SINGLEFLAG; // mask to the bits we want 56 | long tsn = dc.getTsn(); 57 | boolean lookingForOrderedMessages = _ordered || (message != null); 58 | // which is to say for unordered messages we can tolerate gaps _between_ messages 59 | // but not within them 60 | if (lookingForOrderedMessages && (tsn != expectedTsn)) { 61 | Log.debug("Hole in chunk sequence " + tsn + " expected " + expectedTsn); 62 | break; 63 | } 64 | switch (flags) { 65 | case DataChunk.SINGLEFLAG: 66 | // singles are easy - just dispatch. 67 | if (_ordered && (messageNo != dc.getSSeqNo())) { 68 | Log.debug("Hole (single) in message sequence " + dc.getSSeqNo() + " expected " + messageNo); 69 | break; // not the message we are looking for... 70 | } 71 | SCTPMessage single = new SCTPMessage(s, dc); 72 | if (single.deliver(l)) { 73 | delivered.add(dc); 74 | messageNo++; 75 | s.setNextMessageSeqIn(messageNo); 76 | } 77 | break; 78 | case DataChunk.BEGINFLAG: 79 | if (_ordered && (messageNo != dc.getSSeqNo())) { 80 | Log.debug("Hole (begin) in message sequence " + dc.getSSeqNo() + " expected " + messageNo); 81 | break; // not the message we are looking for... 82 | } 83 | message = new TreeSet(); 84 | message.add(dc); 85 | Log.verb("new message no" + dc.getSSeqNo() + " starts with " + dc.getTsn()); 86 | break; 87 | case 0: // middle 88 | if (message != null) { 89 | message.add(dc); 90 | Log.verb("continued message no" + dc.getSSeqNo() + " with " + dc.getTsn()); 91 | } else { 92 | // perhaps check sno ? 93 | Log.debug("Middle with no start" + dc.getSSeqNo() + " tsn " + dc.getTsn()); 94 | } 95 | break; 96 | case DataChunk.ENDFLAG: 97 | if (message != null) { 98 | message.add(dc); 99 | Log.verb("finished message no" + dc.getSSeqNo() + " with " + dc.getTsn()); 100 | SCTPMessage deliverable = new SCTPMessage(s, message); 101 | if (deliverable.deliver(l)) { 102 | delivered.addAll(message); 103 | messageNo++; 104 | s.setNextMessageSeqIn(messageNo); 105 | } 106 | message = null; 107 | } else { 108 | Log.debug("End with no start" + dc.getSSeqNo() + " tsn " + dc.getTsn()); 109 | message = null; 110 | } 111 | break; 112 | default: 113 | throw new IllegalStateException("Impossible value in stream logic"); 114 | } 115 | expectedTsn = tsn + 1; 116 | } 117 | stash.removeAll(delivered); 118 | } 119 | 120 | @Override 121 | public Chunk[] respond(SCTPStream a) { 122 | return null; 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/behave/SCTPStreamBehaviour.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.behave; 18 | 19 | import pe.pi.sctp4j.sctp.SCTPStream; 20 | import pe.pi.sctp4j.sctp.SCTPStreamListener; 21 | import pe.pi.sctp4j.sctp.messages.Chunk; 22 | import pe.pi.sctp4j.sctp.messages.DataChunk; 23 | import java.util.SortedSet; 24 | 25 | /** 26 | * 27 | * @author tim 28 | */ 29 | public interface SCTPStreamBehaviour { 30 | 31 | // Something has happend to the stream, this is our chance to respond. 32 | // typically this means sending nothing 33 | public Chunk[] respond(SCTPStream a); 34 | 35 | // we have a sorted queue of datachunks for this stream to deliver 36 | // according to the appropriate behaviour. 37 | public void deliver(SCTPStream s, SortedSet a, SCTPStreamListener l) ; 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/behave/UnorderedStreamBehaviour.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.behave; 18 | 19 | /** 20 | * 21 | * @author tim 22 | */ 23 | public class UnorderedStreamBehaviour extends OrderedStreamBehaviour { 24 | 25 | public UnorderedStreamBehaviour() { 26 | _ordered = false; 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/dataChannel/DECP/DCOpen.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.dataChannel.DECP; 18 | 19 | import pe.pi.sctp4j.sctp.behave.SCTPStreamBehaviour; 20 | import pe.pi.sctp4j.sctp.behave.OrderedStreamBehaviour; 21 | import pe.pi.sctp4j.sctp.behave.UnorderedStreamBehaviour; 22 | import pe.pi.sctp4j.sctp.messages.Packet; 23 | import pe.pi.sctp4j.sctp.messages.exceptions.InvalidDataChunkException; 24 | import com.phono.srtplight.Log; 25 | import java.nio.Buffer; 26 | import java.nio.ByteBuffer; 27 | 28 | /** 29 | * 30 | * @author Westhawk Ltd 31 | */ 32 | public class DCOpen { 33 | 34 | /* 35 | +------------------------------------------------+------+-----------+ 36 | | Name | Type | Reference | 37 | +------------------------------------------------+------+-----------+ 38 | | DATA_CHANNEL_RELIABLE | 0x00 | [RFCXXXX] | 39 | | DATA_CHANNEL_RELIABLE_UNORDERED | 0x80 | [RFCXXXX] | 40 | | DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT | 0x01 | [RFCXXXX] | 41 | | DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED | 0x81 | [RFCXXXX] | 42 | | DATA_CHANNEL_PARTIAL_RELIABLE_TIMED | 0x02 | [RFCXXXX] | 43 | | DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED | 0x82 | [RFCXXXX] | 44 | | Reserved | 0x7f | [RFCXXXX] | 45 | | Reserved | 0xff | [RFCXXXX] | 46 | | Unassigned | rest | | 47 | +------------------------------------------------+------+-----------+ 48 | */ 49 | public final static byte RELIABLE = 0x0; 50 | public final static byte PARTIAL_RELIABLE_REXMIT = 0x01; 51 | public final static byte PARTIAL_RELIABLE_REXMIT_UNORDERED = (byte) 0x81; 52 | public final static byte PARTIAL_RELIABLE_TIMED = 0x02; 53 | public final static byte PARTIAL_RELIABLE_TIMED_UNORDERED = (byte) 0x82; 54 | public final static byte RELIABLE_UNORDERED = (byte) 0x80; 55 | 56 | /* 57 | 5.1. DATA_CHANNEL_OPEN Message 58 | 59 | This message is sent initially on the stream used for user messages 60 | using the channel. 61 | 62 | 0 1 2 3 63 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 64 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 65 | | Message Type | Channel Type | Priority | 66 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 67 | | Reliability Parameter | 68 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 69 | | Label Length | Protocol Length | 70 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 71 | \ / 72 | | Label | 73 | / \ 74 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 75 | \ / 76 | | Protocol | 77 | / \ 78 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 79 | */ 80 | private byte _messType; 81 | private byte _chanType; 82 | private int _priority; 83 | private long _reliablity; 84 | int _labLen; 85 | int _protLen; 86 | private byte[] _label; 87 | private byte[] _protocol; 88 | final static int OPEN = 0x03; 89 | final static int ACK = 0x02; 90 | boolean _isAck = false; 91 | 92 | public DCOpen(String label) { 93 | this((byte) RELIABLE, 0, 0, label, ""); 94 | } 95 | 96 | // used to make Acks only 97 | private DCOpen() { 98 | } 99 | 100 | public DCOpen( 101 | byte chanType, 102 | int priority, 103 | long reliablity, 104 | String label, 105 | String protocol) { 106 | _messType = (byte) OPEN; 107 | _chanType = chanType; 108 | _priority = priority; 109 | _reliablity = reliablity; 110 | _label = label.getBytes(); 111 | _protocol = protocol.getBytes(); 112 | _labLen = _label.length; 113 | _protLen = _protocol.length; 114 | } 115 | 116 | public byte[] getBytes() { 117 | int sz = 12 + _labLen + pad(_labLen) + _protLen + pad(_protLen); 118 | Log.verb("dcopen needs " + sz + " bytes "); 119 | 120 | byte[] ret = new byte[sz]; 121 | ByteBuffer buff = ByteBuffer.wrap(ret); 122 | buff.put((byte) _messType); 123 | buff.put((byte) _chanType); 124 | buff.putChar((char) _priority); 125 | buff.putInt((int) _reliablity); 126 | buff.putChar((char) _labLen); 127 | buff.putChar((char) _protLen); 128 | buff.put(_label); 129 | Buffer bu = (Buffer)buff; // work around for needless incompatibility between JDK 11 and 8 130 | bu.position(bu.position() + pad(_labLen)); 131 | buff.put(_protocol); 132 | bu.position(bu.position() + pad(_protLen)); 133 | 134 | return ret; 135 | } 136 | 137 | final static public int pad(int len) { 138 | int mod = len % 4; 139 | int res = 0; 140 | Log.verb("field of " + len + " mod 4 is " + mod); 141 | 142 | if (mod > 0) { 143 | res = (4 - mod); 144 | } 145 | Log.verb("padded by " + res); 146 | return res; 147 | } 148 | 149 | public DCOpen(ByteBuffer bb) throws InvalidDataChunkException { 150 | _messType = bb.get(); 151 | switch (_messType) { 152 | case OPEN: 153 | _chanType = bb.get(); 154 | _priority = bb.getChar(); 155 | _reliablity = bb.getInt(); 156 | _labLen = bb.getChar(); 157 | _protLen = bb.getChar(); 158 | _label = new byte[_labLen]; 159 | bb.get(_label); 160 | _protocol = new byte[_protLen]; 161 | bb.get(_protocol); 162 | break; 163 | case ACK: 164 | _isAck = true; 165 | break; 166 | default: 167 | throw new InvalidDataChunkException("Unexpected DCEP message type " + _messType); 168 | //break; 169 | 170 | } 171 | } 172 | 173 | public String toString() { 174 | return _isAck ? "Ack " : "Open " 175 | + " _chanType =" + (int) _chanType 176 | + " _priority = " + _priority 177 | + " _reliablity = " + _reliablity 178 | + " _label = " + new String(_label) 179 | + " _protocol = " + Packet.getHex(_protocol); 180 | } 181 | 182 | public boolean isAck() { 183 | return _isAck; 184 | } 185 | 186 | public SCTPStreamBehaviour mkStreamBehaviour() { 187 | String loglab = _label == null?"_null_":new String(_label); 188 | Log.debug("Making a behaviour for dcep stream " + loglab); 189 | SCTPStreamBehaviour behave = null; 190 | switch (_chanType) { 191 | case RELIABLE: 192 | behave = new OrderedStreamBehaviour(); 193 | break; 194 | case RELIABLE_UNORDERED: 195 | behave = new UnorderedStreamBehaviour(); 196 | break; 197 | // todo these next 4 are wrong... the odering is atleast correct 198 | // even if the retry is wrong. 199 | case PARTIAL_RELIABLE_REXMIT: 200 | case PARTIAL_RELIABLE_TIMED: 201 | behave = new OrderedStreamBehaviour(); 202 | break; 203 | case PARTIAL_RELIABLE_REXMIT_UNORDERED: 204 | case PARTIAL_RELIABLE_TIMED_UNORDERED: 205 | behave = new UnorderedStreamBehaviour(); 206 | break; 207 | } 208 | if (behave != null) { 209 | Log.debug(loglab + " behaviour is " + behave.getClass().getSimpleName()); 210 | } 211 | 212 | return behave; 213 | } 214 | 215 | public String getLabel() { 216 | return new String(_label); 217 | } 218 | 219 | public static DCOpen mkAck() { 220 | DCOpen ack = new DCOpen() { 221 | @Override 222 | public byte[] getBytes() { 223 | byte[] a = new byte[1]; 224 | a[0] = ACK; 225 | return a; 226 | } 227 | }; 228 | ack._isAck = true; 229 | return ack; 230 | } 231 | 232 | } 233 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/dummy/DummyStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package pe.pi.sctp4j.sctp.dummy; 7 | 8 | import pe.pi.sctp4j.sctp.Association; 9 | import pe.pi.sctp4j.sctp.SCTPMessage; 10 | import pe.pi.sctp4j.sctp.SCTPStream; 11 | import pe.pi.sctp4j.sctp.dataChannel.DECP.DCOpen; 12 | import pe.pi.sctp4j.sctp.messages.DataChunk; 13 | 14 | /** 15 | * Concrete stream which mocks the abstract methods - used as a place holder in tests etc 16 | * @author tim 17 | */ 18 | public class DummyStream extends SCTPStream{ 19 | 20 | public DummyStream(Association a, Integer id) { 21 | super(a, id); 22 | } 23 | 24 | @Override 25 | public void delivered(DataChunk d) { 26 | } 27 | 28 | @Override 29 | public void send(String message) throws Exception { 30 | } 31 | 32 | @Override 33 | public void send(byte[] message) throws Exception { 34 | } 35 | 36 | @Override 37 | public void send(DCOpen message) throws Exception { 38 | } 39 | 40 | @Override 41 | public void deliverMessage(SCTPMessage message) { 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/leakey/LeakyAssociation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.leakey; 18 | 19 | import pe.pi.sctp4j.sctp.AssociationListener; 20 | import pe.pi.sctp4j.sctp.small.ThreadedAssociation; 21 | import org.bouncycastle.tls.DatagramTransport; 22 | 23 | /** 24 | * 25 | * @author Westhawk Ltd 26 | */ 27 | public class LeakyAssociation extends ThreadedAssociation { 28 | 29 | public LeakyAssociation(DatagramTransport transport, AssociationListener al) { 30 | super(new LeakyTransport(transport), al); 31 | } 32 | 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/leakey/LeakyTransport.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.leakey; 18 | 19 | import com.phono.srtplight.Log; 20 | import java.io.IOException; 21 | import java.net.DatagramPacket; 22 | import java.net.DatagramSocket; 23 | import java.net.InetAddress; 24 | import java.net.InetSocketAddress; 25 | import org.bouncycastle.tls.DatagramTransport; 26 | 27 | /** 28 | * 29 | * @author Westhawk Ltd 30 | * 31 | * class that taps off the DTLS decoded SCTP traffic and echoes it to a pair of 32 | * UDP ports so they can be pcaped by wireshark Do not use this class in 33 | * production code. test/debug only! 34 | * 35 | */ 36 | class LeakyTransport implements DatagramTransport { 37 | 38 | DatagramTransport _dtls; 39 | DatagramSocket _logrec; 40 | DatagramSocket _logsend; 41 | static short SCTP = 9800; 42 | 43 | public LeakyTransport(DatagramTransport transport) { 44 | InetAddress me = java.net.Inet4Address.getLoopbackAddress(); 45 | try { 46 | _dtls = transport; 47 | _logrec = new DatagramSocket(SCTP, me); 48 | _logsend = new DatagramSocket(SCTP + 1, me); 49 | 50 | InetSocketAddress s = (InetSocketAddress) _logsend.getLocalSocketAddress(); 51 | Log.warn("Leaking to send address " + s.getHostString() + ":" + s.getPort()); 52 | InetSocketAddress r = (InetSocketAddress) _logrec.getLocalSocketAddress(); 53 | Log.warn("Leaking to recv address " + r.getHostString() + ":" + r.getPort()); 54 | } catch (Exception ex) { 55 | Log.error("exception in making Leaky socket "+SCTP+" loopback "+me); 56 | } 57 | SCTP +=10; 58 | } 59 | 60 | @Override 61 | public int getReceiveLimit() throws IOException { 62 | return _dtls.getReceiveLimit(); 63 | } 64 | 65 | @Override 66 | public int getSendLimit() throws IOException { 67 | return _dtls.getSendLimit(); 68 | } 69 | 70 | @Override 71 | public int receive(byte[] bytes, int offs, int len, int sleep) throws IOException { 72 | int sz = _dtls.receive(bytes, offs, len, sleep); 73 | if (sz > 0) { 74 | DatagramPacket p = new DatagramPacket(bytes, offs, sz, _logsend.getLocalSocketAddress()); 75 | _logrec.send(p); 76 | } 77 | return sz; 78 | } 79 | 80 | @Override 81 | public void send(byte[] bytes, int offs, int len) throws IOException { 82 | if ((bytes == null) || (bytes.length < offs + len) || (bytes.length <1)){ 83 | Log.error("Implausible packet for encryption "); 84 | if (bytes == null) { 85 | Log.error("null buffer"); 86 | }else { 87 | Log.error("Length ="+bytes.length+" len ="+len+" offs="+offs); 88 | } 89 | return; 90 | } 91 | try { 92 | DatagramPacket p = new DatagramPacket(bytes, offs, len, _logrec.getLocalSocketAddress()); 93 | _logsend.send(p); 94 | } catch (Exception x) { 95 | Log.error("can't leak to " + _logrec.getLocalSocketAddress()); 96 | x.printStackTrace(); 97 | } 98 | _dtls.send(bytes, offs, len); 99 | } 100 | 101 | @Override 102 | public void close() throws IOException { 103 | _dtls.close(); 104 | _logrec.close(); 105 | _logsend.close(); 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/AbortChunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages; 18 | 19 | import pe.pi.sctp4j.sctp.messages.params.VariableParam; 20 | import com.phono.srtplight.Log; 21 | import java.nio.ByteBuffer; 22 | import java.util.ArrayList; 23 | 24 | /** 25 | * 26 | * @author Westhawk Ltd 27 | */ 28 | 29 | /* 30 | 0 1 2 3 31 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 32 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 33 | | Type = 6 |Reserved |T| Length | 34 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 35 | \ \ 36 | / zero or more Error Causes / 37 | \ \ 38 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 39 | 40 | 41 | */ 42 | public class AbortChunk extends Chunk { 43 | 44 | 45 | 46 | public AbortChunk() { 47 | super((byte) Chunk.ABORT); 48 | } 49 | 50 | 51 | public AbortChunk(byte type, byte flags, int length, ByteBuffer pkt) { 52 | super(type, flags, length, pkt); 53 | if (_body.remaining() >= 4) { 54 | Log.verb("Abort" + this.toString()); 55 | while (_body.hasRemaining()) { 56 | VariableParam v = readErrorParam(); 57 | _varList.add(v); 58 | } 59 | } 60 | } 61 | 62 | 63 | 64 | @Override 65 | void putFixedParams(ByteBuffer ret) { 66 | 67 | } 68 | 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/CookieAckChunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | /** 22 | * 23 | * @author Westhawk Ltd 24 | */ 25 | public class CookieAckChunk extends Chunk { 26 | 27 | public CookieAckChunk(byte type, byte flags, int length, ByteBuffer pkt) { 28 | super(type, flags, length, pkt); 29 | } 30 | 31 | public CookieAckChunk() { 32 | super((byte) Chunk.COOKIE_ACK); 33 | } 34 | 35 | @Override 36 | void putFixedParams(ByteBuffer ret) { 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/CookieEchoChunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages; 18 | 19 | import pe.pi.sctp4j.sctp.Association; 20 | import pe.pi.sctp4j.sctp.messages.exceptions.SctpPacketFormatException; 21 | import com.phono.srtplight.Log; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * 26 | * @author Westhawk Ltd 27 | * 28 | * 29 | * 3.3.11. Cookie Echo (COOKIE ECHO) (10) 30 | 31 | This chunk is used only during the initialization of an association. 32 | It is sent by the initiator of an association to its peer to complete 33 | the initialization process. This chunk MUST precede any DATA chunk 34 | sent within the association, but MAY be bundled with one or more DATA 35 | chunks in the same packet. 36 | 37 | 0 1 2 3 38 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 39 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 40 | | Type = 10 |Chunk Flags | Length | 41 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 42 | / Cookie / 43 | \ \ 44 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 45 | 46 | Chunk Flags: 8 bit 47 | 48 | Set to 0 on transmit and ignored on receipt. 49 | 50 | Length: 16 bits (unsigned integer) 51 | 52 | Set to the size of the chunk in bytes, including the 4 bytes of 53 | the chunk header and the size of the cookie. 54 | 55 | 56 | 57 | 58 | 59 | Stewart Standards Track [Page 50] 60 | 61 | RFC 4960 Stream Control Transmission Protocol September 2007 62 | 63 | 64 | Cookie: variable size 65 | 66 | This field must contain the exact cookie received in the State 67 | Cookie parameter from the previous INIT ACK. 68 | 69 | An implementation SHOULD make the cookie as small as possible to 70 | ensure interoperability. 71 | 72 | Note: A Cookie Echo does NOT contain a State Cookie parameter; 73 | instead, the data within the State Cookie's Parameter Value 74 | becomes the data within the Cookie Echo's Chunk Value. This 75 | allows an implementation to change only the first 2 bytes of the 76 | State Cookie parameter to become a COOKIE ECHO chunk. 77 | * 78 | */ 79 | public class CookieEchoChunk extends Chunk { 80 | private byte[] _cookieData; 81 | 82 | public CookieEchoChunk(byte type, byte flags, int length, ByteBuffer pkt) { 83 | super(type, flags, length, pkt); 84 | _cookieData = new byte[_body.remaining()]; 85 | _body.get(_cookieData); 86 | } 87 | 88 | public CookieEchoChunk() { 89 | super((byte)COOKIE_ECHO); 90 | } 91 | @Override 92 | public void validate() throws SctpPacketFormatException{ 93 | if (_cookieData.length != Association.COOKIESIZE){ 94 | throw new SctpPacketFormatException("cookie Echo wrong length for our association "+ _cookieData.length +" != "+ Association.COOKIESIZE); 95 | } 96 | } 97 | 98 | public void setCookieData(byte[] cd){ 99 | _cookieData = cd; 100 | } 101 | 102 | public byte [] getCookieData(){ 103 | return _cookieData; 104 | } 105 | @Override 106 | void putFixedParams(ByteBuffer ret) { 107 | Log.debug("cookie is "+_cookieData +"and buffer is "+ret); 108 | ret.put(_cookieData); 109 | } 110 | 111 | 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/ErrorChunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages; 18 | 19 | import pe.pi.sctp4j.sctp.messages.params.KnownError; 20 | import pe.pi.sctp4j.sctp.messages.params.VariableParam; 21 | import com.phono.srtplight.Log; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * 26 | * @author Westhawk Ltd 27 | */ 28 | 29 | /* 30 | 3.3.10. Operation Error (ERROR) (9) 31 | 32 | An endpoint sends this chunk to its peer endpoint to notify it of 33 | certain error conditions. It contains one or more error causes. An 34 | Operation Error is not considered fatal in and of itself, but may be 35 | used with an ABORT chunk to report a fatal condition. It has the 36 | following parameters: 37 | 38 | 0 1 2 3 39 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 40 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 41 | | Type = 9 | Chunk Flags | Length | 42 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 43 | \ \ 44 | / one or more Error Causes / 45 | \ \ 46 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 47 | 48 | Chunk Flags: 8 bits 49 | 50 | Set to 0 on transmit and ignored on receipt. 51 | 52 | Length: 16 bits (unsigned integer) 53 | 54 | Set to the size of the chunk in bytes, including the chunk header 55 | and all the Error Cause fields present. 56 | */ 57 | public class ErrorChunk extends Chunk { 58 | 59 | public ErrorChunk() { 60 | super((byte) Chunk.ERROR); 61 | } 62 | 63 | public ErrorChunk(KnownError e) { 64 | this(); 65 | _varList.add(e); 66 | } 67 | 68 | public ErrorChunk(KnownError[] el) { 69 | this(); 70 | for (KnownError e : el) { 71 | _varList.add(e); 72 | } 73 | } 74 | 75 | public ErrorChunk(byte type, byte flags, int length, ByteBuffer pkt) { 76 | super(type, flags, length, pkt); 77 | if (_body.remaining() >= 4) { 78 | Log.verb("Error" + this.toString()); 79 | while (_body.hasRemaining()) { 80 | VariableParam v = readErrorParam(); 81 | _varList.add(v); 82 | } 83 | } 84 | } 85 | 86 | @Override 87 | void putFixedParams(ByteBuffer ret) { 88 | 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/HeartBeatAckChunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | /** 22 | * 23 | * @author Westhawk Ltd 24 | */ 25 | public class HeartBeatAckChunk extends Chunk { 26 | 27 | public HeartBeatAckChunk() { 28 | super((byte) Chunk.HEARTBEAT_ACK); 29 | } 30 | 31 | 32 | @Override 33 | void putFixedParams(ByteBuffer ret) { 34 | // none. 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/HeartBeatChunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages; 18 | 19 | import com.phono.srtplight.Log; 20 | import pe.pi.sctp4j.sctp.messages.exceptions.SctpPacketFormatException; 21 | import pe.pi.sctp4j.sctp.messages.params.VariableParam; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * 26 | * @author Westhawk Ltd 27 | */ 28 | public class HeartBeatChunk extends Chunk { 29 | 30 | public HeartBeatChunk() throws SctpPacketFormatException { 31 | super((byte) HEARTBEAT); 32 | HeartbeatInfo req = new HeartbeatInfo(1, "HeartbeatInfo"); 33 | String t = ""+ System.currentTimeMillis(); 34 | req.setData(t.getBytes()); 35 | Log.debug("adding "+req+" to "+this); 36 | _varList.add(req); 37 | validate(); 38 | } 39 | 40 | public HeartBeatChunk(byte type, byte flags, int length, ByteBuffer pkt) { 41 | super(type, flags, length, pkt); 42 | if (_body.remaining() >= 4) { 43 | while (_body.hasRemaining()) { 44 | VariableParam v = readVariable(); 45 | _varList.add(v); 46 | } 47 | } 48 | } 49 | 50 | @Override 51 | public void validate() throws SctpPacketFormatException { 52 | VariableParam hbd; 53 | if ((_varList == null) || (_varList.size() != 1)) { 54 | throw new SctpPacketFormatException("No (or too much content in this heartbeat packet"); 55 | } 56 | hbd = _varList.get(0); 57 | if (!(hbd instanceof HeartbeatInfo)) { 58 | throw new SctpPacketFormatException("Expected a heartbeatinfo in this packet"); 59 | } 60 | } 61 | 62 | public Chunk[] mkReply() { 63 | Chunk[] rep = new Chunk[1]; 64 | HeartBeatAckChunk dub = new HeartBeatAckChunk(); 65 | dub._varList = this._varList; 66 | rep[0] = dub; 67 | return rep; 68 | } 69 | 70 | @Override 71 | void putFixedParams(ByteBuffer ret) { 72 | // none 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/InitAckChunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages; 18 | 19 | import pe.pi.sctp4j.sctp.messages.params.StateCookie; 20 | import pe.pi.sctp4j.sctp.messages.params.VariableParam; 21 | import com.phono.srtplight.Log; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * 26 | * @author Westhawk Ltd 27 | */ 28 | 29 | /* 30 | 31 | The format of the INIT ACK chunk is shown below: 32 | 33 | 0 1 2 3 34 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 35 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 36 | | Type = 2 | Chunk Flags | Chunk Length | 37 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 | | Initiate Tag | 39 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 40 | | Advertised Receiver Window Credit | 41 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 42 | | Number of Outbound Streams | Number of Inbound Streams | 43 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 44 | | Initial TSN | 45 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 46 | \ \ 47 | / Optional/Variable-Length Parameters / 48 | \ \ 49 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 50 | */ 51 | public class InitAckChunk extends Chunk { 52 | 53 | int _initiateTag; 54 | long _adRecWinCredit; 55 | int _numOutStreams; 56 | int _numInStreams; 57 | long _initialTSN; 58 | private byte[] _cookie; 59 | private byte[] _supportedExtensions; 60 | 61 | public InitAckChunk() { 62 | super((byte) INITACK); 63 | } 64 | 65 | public int getInitiateTag() { 66 | return _initiateTag; 67 | } 68 | 69 | public void setInitiateTag(int v) { 70 | _initiateTag = v; 71 | } 72 | 73 | public long getAdRecWinCredit() { 74 | return _adRecWinCredit; 75 | } 76 | 77 | public void setAdRecWinCredit(int v) { 78 | _adRecWinCredit = v; 79 | } 80 | 81 | public int getNumOutStreams() { 82 | return _numOutStreams; 83 | } 84 | 85 | public void setNumOutStreams(int v) { 86 | _numOutStreams = v; 87 | } 88 | 89 | public int getNumInStreams() { 90 | return _numInStreams; 91 | } 92 | 93 | public void setNumInStreams(int v) { 94 | _numInStreams = v; 95 | } 96 | 97 | public long getInitialTSN() { 98 | return _initialTSN; 99 | } 100 | 101 | public void setInitialTSN(long v) { 102 | _initialTSN = v; 103 | } 104 | 105 | public byte[] getCookie() { 106 | return _cookie; 107 | } 108 | 109 | public void setCookie(byte[] v) { 110 | _cookie = v; 111 | } 112 | 113 | public InitAckChunk(byte type, byte flags, int length, ByteBuffer pkt) { 114 | super(type, flags, length, pkt); 115 | if (_body.remaining() >= 16) { 116 | _initiateTag = _body.getInt(); 117 | _adRecWinCredit = getUnsignedInt(_body); 118 | _numOutStreams = _body.getChar(); 119 | _numInStreams = _body.getChar(); 120 | _initialTSN = getUnsignedInt(_body); 121 | Log.verb("Init Ack" + this.toString()); 122 | while (_body.hasRemaining()) { 123 | VariableParam v = readVariable(); 124 | _varList.add(v); 125 | } 126 | 127 | for (VariableParam v : _varList) { 128 | // now look for variables we are expecting... 129 | Log.verb("variable of type: " + v.getName() + " " + v.toString()); 130 | if (v instanceof StateCookie) { 131 | _cookie = ((StateCookie) v).getData(); 132 | } else { 133 | Log.verb("ignored variable of type: " + v.getName()); 134 | } 135 | } 136 | 137 | } 138 | } 139 | 140 | public String toString() { 141 | String ret = super.toString(); 142 | ret += " initiateTag : " + _initiateTag 143 | + " adRecWinCredit : " + _adRecWinCredit 144 | + " numOutStreams : " + _numOutStreams 145 | + " numInStreams : " + _numInStreams 146 | + " initialTSN : " + _initialTSN 147 | + ((_supportedExtensions == null) ? " no supported extensions" : " supported extensions are: " + chunksToNames(_supportedExtensions)); 148 | ; 149 | return ret; 150 | } 151 | 152 | @Override 153 | void putFixedParams(ByteBuffer ret) { 154 | ret.putInt(_initiateTag); 155 | putUnsignedInt(ret, _adRecWinCredit); 156 | ret.putChar((char) _numOutStreams); 157 | ret.putChar((char) _numInStreams); 158 | putUnsignedInt(ret, _initialTSN); 159 | if (_cookie != null) { 160 | StateCookie sc = new StateCookie(); 161 | sc.setData(_cookie); 162 | _varList.add(sc); 163 | } 164 | if (_supportedExtensions != null) { 165 | SupportedExtensions se = new SupportedExtensions(); 166 | se.setData(_supportedExtensions); 167 | _varList.add(se); 168 | } 169 | } 170 | 171 | public byte[] getSupportedExtensions(byte[] v) { 172 | return _supportedExtensions; 173 | } 174 | 175 | public void setSupportedExtensions(byte[] v) { 176 | _supportedExtensions = v; 177 | } 178 | 179 | } 180 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/InitChunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages; 18 | 19 | import pe.pi.sctp4j.sctp.messages.params.RequestedHMACAlgorithmParameter; 20 | import pe.pi.sctp4j.sctp.messages.params.VariableParam; 21 | import com.phono.srtplight.Log; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * 26 | * @author tim 27 | */ 28 | public class InitChunk extends Chunk { 29 | /* 30 | 0 1 2 3 31 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 32 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 33 | | Type = 1 | Chunk Flags | Chunk Length | 34 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 35 | | Initiate Tag | 36 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 37 | | Advertised Receiver Window Credit (a_rwnd) | 38 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 39 | | Number of Outbound Streams | Number of Inbound Streams | 40 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 41 | | Initial TSN | 42 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 43 | \ \ 44 | / Optional/Variable-Length Parameters / 45 | \ \ 46 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 47 | */ 48 | 49 | long _initiateTag; 50 | long _adRecWinCredit; 51 | int _numOutStreams; 52 | int _numInStreams; 53 | long _initialTSN; 54 | byte [] _farSupportedExtensions; 55 | byte [] _farRandom; 56 | boolean _farForwardTSNsupported; 57 | byte[] _farHmacs; 58 | byte[] _farChunks; 59 | public int _outStreams; 60 | 61 | public InitChunk(){ 62 | super((byte)INIT); 63 | } 64 | 65 | public InitChunk(byte type, byte flags, int length, ByteBuffer pkt) { 66 | super(type, flags, length, pkt); 67 | if (_body.remaining() >= 16) { 68 | _initiateTag = _body.getInt(); 69 | _adRecWinCredit = getUnsignedInt(_body); 70 | _numOutStreams = _body.getChar(); 71 | _numInStreams = _body.getChar(); 72 | _initialTSN = getUnsignedInt(_body); 73 | Log.verb("Init " + this.toString()); 74 | while (_body.hasRemaining()) { 75 | VariableParam v = readVariable(); 76 | _varList.add(v); 77 | } 78 | for (VariableParam v : _varList){ 79 | // now look for variables we are expecting... 80 | Log.verb("variable of type: "+v.getName()+" "+ v.toString()); 81 | if (v instanceof SupportedExtensions){ 82 | _farSupportedExtensions = ((SupportedExtensions)v).getData(); 83 | } else if (v instanceof RandomParam){ 84 | _farRandom = ((RandomParam)v).getData(); 85 | } else if (v instanceof ForwardTSNsupported){ 86 | _farForwardTSNsupported = true; 87 | } else if (v instanceof RequestedHMACAlgorithmParameter){ 88 | _farHmacs = ((RequestedHMACAlgorithmParameter)v).getData(); 89 | } else if (v instanceof ChunkListParam){ 90 | _farChunks = ((ChunkListParam)v).getData(); 91 | } else { 92 | Log.debug("unexpected variable of type: "+v.getName()); 93 | } 94 | } 95 | } 96 | } 97 | public String toString() { 98 | String ret = super.toString(); 99 | ret += " initiateTag : " + _initiateTag 100 | + " adRecWinCredit : " + _adRecWinCredit 101 | + " numOutStreams : " + _numOutStreams 102 | + " numInStreams : " + _numInStreams 103 | + " initialTSN : " + _initialTSN 104 | + " farForwardTSNsupported : "+_farForwardTSNsupported 105 | + ((_farSupportedExtensions == null) ?" no supported extensions": " supported extensions are: "+chunksToNames(_farSupportedExtensions)); 106 | return ret; 107 | } 108 | 109 | @Override 110 | void putFixedParams(ByteBuffer ret) { 111 | ret.putInt((int)_initiateTag); 112 | putUnsignedInt(ret,_adRecWinCredit); 113 | ret.putChar((char) _numOutStreams); 114 | ret.putChar((char) _numInStreams); 115 | Chunk.putUnsignedInt(ret,_initialTSN); 116 | } 117 | 118 | public int getInitiateTag() { 119 | return (int)_initiateTag; 120 | } 121 | 122 | public long getAdRecWinCredit(){ 123 | return _adRecWinCredit; 124 | } 125 | public int getNumOutStreams(){ 126 | return _numOutStreams; 127 | } 128 | public int getNumInStreams(){ 129 | return _numInStreams; 130 | } 131 | public long getInitialTSN(){ 132 | return _initialTSN; 133 | } 134 | public void setInitialTSN(long tsn){ 135 | _initialTSN = tsn; 136 | } 137 | public void setAdRecWinCredit(long credit){ 138 | _adRecWinCredit = credit; 139 | } 140 | public void setNumOutStreams(int outn){ 141 | _numOutStreams = outn; 142 | } 143 | public void setNumInStreams(int inn){ 144 | _numInStreams = inn; 145 | } 146 | public byte [] getFarSupportedExtensions(){ 147 | return _farSupportedExtensions; 148 | } 149 | 150 | public void setInitiate(long tag) { 151 | this._initiateTag = tag; 152 | } 153 | 154 | 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/ReConfigChunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages; 18 | 19 | import pe.pi.sctp4j.sctp.messages.params.AddIncomingStreamsRequestParameter; 20 | import pe.pi.sctp4j.sctp.messages.params.AddOutgoingStreamsRequestParameter; 21 | import pe.pi.sctp4j.sctp.messages.params.IncomingSSNResetRequestParameter; 22 | import pe.pi.sctp4j.sctp.messages.params.OutgoingSSNResetRequestParameter; 23 | import pe.pi.sctp4j.sctp.messages.params.ReconfigurationResponseParameter; 24 | import pe.pi.sctp4j.sctp.messages.params.VariableParam; 25 | import com.phono.srtplight.Log; 26 | import java.nio.ByteBuffer; 27 | 28 | /** 29 | * 30 | * @author thp 31 | */ 32 | public class ReConfigChunk extends Chunk { 33 | 34 | private long sentAt; 35 | private int retries; 36 | 37 | public ReConfigChunk(byte type, byte flags, int length, ByteBuffer pkt) { 38 | super(type, flags, length, pkt); 39 | Log.debug("ReConfig chunk" + this.toString()); 40 | if (_body.remaining() >= 4) { 41 | while (_body.hasRemaining()) { 42 | VariableParam v = this.readVariable(); 43 | _varList.add(v); 44 | Log.debug("\tParam :" + v.toString()); 45 | } 46 | } 47 | } 48 | 49 | public ReConfigChunk() { 50 | super((byte) Chunk.RE_CONFIG); 51 | } 52 | 53 | @Override 54 | void putFixedParams(ByteBuffer ret) { 55 | //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 56 | } 57 | 58 | public boolean hasIncomingReset() { 59 | return _varList.stream().anyMatch((VariableParam v) -> { 60 | return v instanceof IncomingSSNResetRequestParameter; 61 | }); 62 | } 63 | 64 | public IncomingSSNResetRequestParameter getIncomingReset() { 65 | return _varList.stream().filter((VariableParam v) -> { 66 | return v instanceof IncomingSSNResetRequestParameter; 67 | }) 68 | .map((VariableParam v) -> { 69 | return (IncomingSSNResetRequestParameter) v; 70 | }) 71 | .findFirst().orElse(null); 72 | } 73 | 74 | public boolean hasOutgoingReset() { 75 | return _varList.stream().anyMatch((VariableParam v) -> { 76 | return v instanceof OutgoingSSNResetRequestParameter; 77 | }); 78 | } 79 | 80 | private boolean hasOutgoingAdd() { 81 | return _varList.stream().anyMatch((VariableParam v) -> { 82 | return v instanceof AddOutgoingStreamsRequestParameter; 83 | }); 84 | } 85 | 86 | private boolean hasResponse() { 87 | return _varList.stream().anyMatch((VariableParam v) -> { 88 | return v instanceof ReconfigurationResponseParameter; 89 | }); 90 | } 91 | 92 | public OutgoingSSNResetRequestParameter getOutgoingReset() { 93 | return _varList.stream().filter((VariableParam v) -> { 94 | return v instanceof OutgoingSSNResetRequestParameter; 95 | }) 96 | .map((VariableParam v) -> { 97 | return (OutgoingSSNResetRequestParameter) v; 98 | }) 99 | .findFirst().orElse(null); 100 | } 101 | 102 | public boolean hasParam() { 103 | return !_varList.isEmpty(); 104 | } 105 | 106 | /* 107 | 1. Outgoing SSN Reset Request Parameter. 108 | 109 | 2. Incoming SSN Reset Request Parameter. 110 | 111 | 3. Outgoing SSN Reset Request Parameter, Incoming SSN Reset Request 112 | Parameter. 113 | 114 | 4. SSN/TSN Reset Request Parameter. 115 | 116 | 5. Add Outgoing Streams Request Parameter. 117 | 118 | 6. Add Incoming Streams Request Parameter. 119 | 120 | 7. Add Outgoing Streams Request Parameter, Add Incoming Streams 121 | Request Parameter. 122 | 123 | 8. Re-configuration Response Parameter. 124 | 125 | 9. Re-configuration Response Parameter, Outgoing SSN Reset Request 126 | Parameter. 127 | 128 | 10. Re-configuration Response Parameter, Re-configuration Response 129 | Parameter. 130 | */ 131 | @Override 132 | public void validate() { 133 | if (_varList.size() < 1) { 134 | throw new IllegalArgumentException("Too few params " + _varList.size()); 135 | } 136 | if (_varList.size() > 2) { 137 | throw new IllegalArgumentException("Too many params " + _varList.size()); 138 | } 139 | // now check for invalid combos 140 | if ((_varList.size() == 2)) { 141 | if (this.hasOutgoingReset()) { 142 | VariableParam remain = _varList.stream().filter((VariableParam v) -> { 143 | return !(v instanceof OutgoingSSNResetRequestParameter); 144 | }).findFirst().orElse(null); 145 | if (remain == null) { 146 | throw new IllegalArgumentException("2 OutgoingSSNResetRequestParameter in one Chunk not allowed "); 147 | } 148 | if (!((remain instanceof IncomingSSNResetRequestParameter) //3 149 | || (remain instanceof ReconfigurationResponseParameter))) //9 150 | { 151 | throw new IllegalArgumentException("OutgoingSSNResetRequestParameter and " + remain.getClass().getSimpleName() + " in same Chunk not allowed "); 152 | } 153 | } else if (this.hasOutgoingAdd()) { 154 | VariableParam remain = _varList.stream().filter((VariableParam v) -> { 155 | return !(v instanceof AddOutgoingStreamsRequestParameter); 156 | }).findFirst().orElse(null); 157 | if (remain == null) { 158 | throw new IllegalArgumentException("2 AddOutgoingStreamsRequestParameter in one Chunk not allowed "); 159 | } 160 | if (!(remain instanceof AddIncomingStreamsRequestParameter)) //7 161 | { 162 | throw new IllegalArgumentException("OutgoingSSNResetRequestParameter and " + remain.getClass().getSimpleName() + " in same Chunk not allowed "); 163 | } 164 | } else if (this.hasResponse()) { 165 | VariableParam remain = _varList.stream().filter((VariableParam v) -> { 166 | return !(v instanceof ReconfigurationResponseParameter); 167 | }).findFirst().orElse(null); 168 | if (remain != null) { // 10 169 | throw new IllegalArgumentException("ReconfigurationResponseParameter and " + remain.getClass().getSimpleName() + " in same Chunk not allowed "); 170 | } 171 | } 172 | } // implicitly just one - which is ok 1,2,4,5,6,8 173 | } 174 | 175 | public void addParam(VariableParam rep) { 176 | Log.debug("adding "+rep+" to "+this); 177 | _varList.add(rep); 178 | validate(); 179 | } 180 | 181 | public boolean sameAs(ReConfigChunk other) { 182 | // we ignore other var types for now.... 183 | boolean ret = false; // assume the negative. 184 | if (other != null) { 185 | // if there are 2 params and both match 186 | if ((this.hasIncomingReset() && other.hasIncomingReset()) 187 | && (this.hasOutgoingReset() && other.hasOutgoingReset())) { 188 | ret = this.getIncomingReset().sameAs(other.getIncomingReset()) 189 | && this.getOutgoingReset().sameAs(other.getOutgoingReset()); 190 | } else { 191 | // there is only one (of these) params 192 | // that has to match too 193 | if (this.hasIncomingReset() && other.hasIncomingReset()) { 194 | ret = this.getIncomingReset().sameAs(other.getIncomingReset()); 195 | } 196 | if (this.hasOutgoingReset() && other.hasOutgoingReset()) { 197 | ret = this.getOutgoingReset().sameAs(other.getOutgoingReset()); 198 | } 199 | } 200 | } 201 | return ret; 202 | } 203 | // stuff to manage outbound retries 204 | public long getSentTime(){ 205 | return sentAt; 206 | } 207 | public void setSentTime(long now){ 208 | sentAt = now; 209 | } 210 | public int getAndIncrementRetryCount(){ 211 | return retries++; 212 | } 213 | 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/SackChunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages; 18 | 19 | import java.nio.ByteBuffer; 20 | import java.util.ArrayList; 21 | 22 | /** 23 | * 24 | * @author Westhawk Ltd 25 | */ 26 | public class SackChunk extends Chunk { 27 | 28 | /** 29 | * @return the cumuTSNAck 30 | */ 31 | public long getCumuTSNAck() { 32 | return _cumuTSNAck; 33 | } 34 | 35 | /** 36 | * @param cumuTSNAck the _cumuTSNAck to set 37 | */ 38 | public void setCumuTSNAck(long cumuTSNAck) { 39 | _cumuTSNAck = cumuTSNAck; 40 | } 41 | 42 | /** 43 | * @return the _arWin 44 | */ 45 | public long getArWin() { 46 | return _arWin; 47 | } 48 | 49 | /** 50 | * @param _arWin the _arWin to set 51 | */ 52 | public void setArWin(long arWin) { 53 | _arWin = Math.max(arWin,0); 54 | } 55 | /* 56 | 57 | 0 1 2 3 58 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 59 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 60 | | Type = 3 |Chunk Flags | Chunk Length | 61 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 62 | | Cumulative TSN Ack | 63 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 64 | | Advertised Receiver Window Credit (a_rwnd) | 65 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 66 | | Number of Gap Ack Blocks = N | Number of Duplicate TSNs = X | 67 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 68 | | Gap Ack Block #1 Start | Gap Ack Block #1 End | 69 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 70 | / / 71 | \ ... \ 72 | / / 73 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 74 | | Gap Ack Block #N Start | Gap Ack Block #N End | 75 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 76 | | Duplicate TSN 1 | 77 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 78 | / / 79 | \ ... \ 80 | / / 81 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 82 | | Duplicate TSN X | 83 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 84 | */ 85 | 86 | 87 | 88 | public class GapBlock { 89 | char _start; 90 | char _end; 91 | GapBlock(ByteBuffer b){ 92 | _start = b.getChar(); 93 | _end = b.getChar(); 94 | } 95 | GapBlock(char start){ 96 | _start = start; 97 | } 98 | void setEnd(char end){ 99 | _end = end; 100 | } 101 | 102 | private void put(ByteBuffer b) { 103 | b.putChar(_start); 104 | b.putChar(_end); 105 | } 106 | public char getStart(){ 107 | return _start; 108 | } 109 | public char getEnd(){ 110 | return _end; 111 | } 112 | } 113 | 114 | GapBlock[] _gaps; 115 | long[] _duplicateTSNs; 116 | private long _cumuTSNAck; 117 | private long _arWin; 118 | 119 | public SackChunk(byte type, byte flags, int length, ByteBuffer pkt) { 120 | super(type, flags, length, pkt); 121 | _cumuTSNAck = Chunk.getUnsignedInt(_body); 122 | _arWin = Chunk.getUnsignedInt(_body); 123 | int ngaps = _body.getChar(); 124 | int nDTSNs = _body.getChar(); 125 | _gaps = new GapBlock[ngaps]; 126 | _duplicateTSNs = new long[nDTSNs]; 127 | for (int i=0;i dups) { 148 | _duplicateTSNs = new long[dups.size()]; 149 | int i=0; 150 | for (Long du:dups){ 151 | _duplicateTSNs[i++]= du.longValue(); 152 | } 153 | } 154 | 155 | public void setGaps(ArrayList seenTsns){ 156 | long cuTsn = _cumuTSNAck; 157 | ArrayList gaplist = new ArrayList(); 158 | GapBlock currentGap = null; 159 | char prevoff = 0; 160 | for(Long t:seenTsns){ 161 | char offs = (char) (t.longValue() - cuTsn); 162 | if (currentGap == null){ 163 | currentGap = new GapBlock(offs); 164 | currentGap.setEnd(offs); 165 | gaplist.add(currentGap); 166 | } else { 167 | if (offs == prevoff +1){ 168 | currentGap.setEnd(offs); 169 | } else { 170 | currentGap = new GapBlock(offs); 171 | currentGap.setEnd(offs); 172 | gaplist.add(currentGap); 173 | } 174 | } 175 | prevoff = offs; 176 | } 177 | _gaps = new GapBlock[gaplist.size()]; 178 | int i=0; 179 | for (GapBlock g:gaplist){ 180 | _gaps[i++] = g; 181 | } 182 | } 183 | 184 | @Override 185 | void putFixedParams(ByteBuffer ret) { 186 | Chunk.putUnsignedInt(ret, _cumuTSNAck); 187 | Chunk.putUnsignedInt(ret,_arWin); 188 | ret.putChar((char)_gaps.length); 189 | ret.putChar((char)_duplicateTSNs.length); 190 | for (int i=0;i<_gaps.length;i++){ 191 | _gaps[i].put(ret); 192 | } 193 | for (int i=0;i<_duplicateTSNs.length;i++){ 194 | Chunk.putUnsignedInt(ret,_duplicateTSNs[i]); 195 | } 196 | } 197 | public String toString(){ 198 | StringBuffer ret= new StringBuffer("SACK cumuTSNAck="+_cumuTSNAck) 199 | .append(" _arWin="+_arWin) 200 | .append(" _gaps="+_gaps.length+" ["); 201 | for (GapBlock g:_gaps){ 202 | ret.append("\n\t{"+(int)g._start+","+(int)g._end+"}"); 203 | } 204 | ret.append("]\n _duplicateTSNs="+_duplicateTSNs.length); 205 | for( long t:_duplicateTSNs){ 206 | ret.append("\n\t"+t); 207 | } 208 | return ret.toString(); 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/exceptions/ChecksumException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.exceptions; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | public class ChecksumException extends Exception { 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/exceptions/InvalidDataChunkException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.exceptions; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | public class InvalidDataChunkException extends Exception { 25 | 26 | public InvalidDataChunkException(String message) { 27 | super(message); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/exceptions/InvalidSCTPPacketException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.exceptions; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | public class InvalidSCTPPacketException extends Exception { 25 | 26 | public InvalidSCTPPacketException(String reason) { 27 | super(reason); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/exceptions/MessageException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.exceptions; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | public class MessageException extends Exception{ 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/exceptions/SctpPacketFormatException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.exceptions; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | public class SctpPacketFormatException extends Exception { 25 | 26 | public SctpPacketFormatException(String message) { 27 | super(message); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/exceptions/StaleCookieException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.exceptions; 18 | 19 | /** 20 | * 21 | * @author Westhawk Ltd 22 | */ 23 | public class StaleCookieException extends Exception { 24 | 25 | long _stale; 26 | 27 | public StaleCookieException(long howstale) { 28 | _stale = howstale; 29 | } 30 | 31 | public long getStale() { 32 | return _stale; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/exceptions/UnreadyAssociationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.exceptions; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | public class UnreadyAssociationException extends Exception { 25 | 26 | public UnreadyAssociationException() { 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/AddIncomingStreamsRequestParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.params; 19 | 20 | /** 21 | * 22 | * @author tim 23 | */ 24 | public class AddIncomingStreamsRequestParameter extends AddStreamsRequestParameter { 25 | public AddIncomingStreamsRequestParameter(int t, String n) { 26 | super(t, n); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/AddOutgoingStreamsRequestParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.params; 19 | 20 | /** 21 | * 22 | * @author tim 23 | */ 24 | public class AddOutgoingStreamsRequestParameter extends AddStreamsRequestParameter { 25 | 26 | public AddOutgoingStreamsRequestParameter(int t, String n) { 27 | super(t, n); 28 | } 29 | 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/AddStreamsRequestParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import pe.pi.sctp4j.sctp.messages.Chunk; 20 | import java.nio.ByteBuffer; 21 | 22 | /** 23 | * 24 | * @author tim 25 | */ 26 | public class AddStreamsRequestParameter extends Unknown { 27 | 28 | public AddStreamsRequestParameter(int t, String n) { 29 | super(t, n); 30 | } 31 | /* 32 | 0 1 2 3 33 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 34 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 35 | | Parameter Type = 17 | Parameter Length = 12 | 36 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 37 | | Re-configuration Request Sequence Number | 38 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 39 | | Number of new streams | Reserved | 40 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 41 | */ 42 | 43 | long reconfReqSeqNo; 44 | int numNewStreams; 45 | int reserved; 46 | 47 | @Override 48 | public void readBody(ByteBuffer body, int blen) { 49 | reconfReqSeqNo = Chunk.getUnsignedInt(body); 50 | numNewStreams = body.getChar(); 51 | reserved = body.getChar(); 52 | } 53 | 54 | @Override 55 | public void writeBody(ByteBuffer body) { 56 | Chunk.putUnsignedInt(body,reconfReqSeqNo); 57 | body.putChar((char) numNewStreams); 58 | body.putChar((char) reserved); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/CookiePreservative.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.params; 19 | 20 | import pe.pi.sctp4j.sctp.messages.exceptions.SctpPacketFormatException; 21 | import java.nio.ByteBuffer; 22 | 23 | /** 24 | * 25 | * @author tim 26 | */ 27 | public class CookiePreservative extends KnownParam { 28 | Long time ; 29 | public CookiePreservative(int t, String n) { 30 | super(t, n); 31 | } 32 | @Override 33 | public void readBody(ByteBuffer body, int blen) throws SctpPacketFormatException { 34 | int ctime = body.getInt(); 35 | time = new Long(ctime); 36 | } 37 | 38 | @Override 39 | public void writeBody(ByteBuffer body) throws SctpPacketFormatException { 40 | body.putInt(time.intValue()); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/HostNameAddress.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | /** 22 | * 23 | * @author tim 24 | */ 25 | public class HostNameAddress extends KnownParam { 26 | 27 | String hostname; 28 | 29 | public HostNameAddress(int t, String n) { 30 | super(t, n); 31 | } 32 | 33 | public void readBody(ByteBuffer body, int blen) { 34 | byte data[] = new byte[blen]; 35 | body.get(data); 36 | int off = blen - 1; 37 | // trim any 0 bytes off the end. 38 | while ((off > 0) && (data[off--] == 0)) { 39 | blen--; 40 | } 41 | hostname = new String(data, 0, blen); 42 | } 43 | public void writeBody(ByteBuffer body) { 44 | // gonz up a C style string. 45 | byte [] b = hostname.getBytes(); 46 | _data = new byte[b.length+1]; 47 | System.arraycopy(b, 0, _data, 0, b.length); 48 | body.put(_data); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/IPv4Address.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import pe.pi.sctp4j.sctp.messages.exceptions.SctpPacketFormatException; 20 | import java.net.Inet4Address; 21 | import java.net.UnknownHostException; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * 26 | * @author tim 27 | */ 28 | public class IPv4Address extends KnownParam { 29 | 30 | Inet4Address addr; 31 | 32 | public IPv4Address(int t, String n) { 33 | super(t, n); 34 | } 35 | 36 | @Override 37 | public void readBody(ByteBuffer body, int blen) throws SctpPacketFormatException { 38 | byte[] data = new byte[blen]; 39 | body.get(data); 40 | try { 41 | addr = (Inet4Address) Inet4Address.getByAddress(data); 42 | } catch (UnknownHostException ex) { 43 | throw new SctpPacketFormatException(ex.getMessage()); 44 | } 45 | } 46 | 47 | @Override 48 | public void writeBody(ByteBuffer body) throws SctpPacketFormatException { 49 | body.put(addr.getAddress()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/IPv6Address.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import pe.pi.sctp4j.sctp.messages.exceptions.SctpPacketFormatException; 20 | import java.net.Inet6Address; 21 | import java.net.UnknownHostException; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * 26 | * @author tim 27 | */ 28 | public class IPv6Address extends KnownParam { 29 | 30 | Inet6Address addr; 31 | 32 | public IPv6Address(int t, String n) { 33 | super(t, n); 34 | } 35 | 36 | @Override 37 | public void readBody(ByteBuffer body, int blen) throws SctpPacketFormatException { 38 | byte[] data = new byte[blen]; 39 | body.get(data); 40 | try { 41 | addr = (Inet6Address) Inet6Address.getByAddress(data); 42 | } catch (UnknownHostException ex) { 43 | throw new SctpPacketFormatException(ex.getMessage()); 44 | } 45 | } 46 | 47 | @Override 48 | public void writeBody(ByteBuffer body) throws SctpPacketFormatException { 49 | body.put(addr.getAddress()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/IncomingSSNResetRequestParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import pe.pi.sctp4j.sctp.messages.Chunk; 20 | import com.phono.srtplight.Log; 21 | import java.nio.ByteBuffer; 22 | 23 | /** 24 | * 25 | * @author tim 26 | */ 27 | public class IncomingSSNResetRequestParameter extends KnownParam { 28 | 29 | /* 30 | 0 1 2 3 31 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 32 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 33 | | Parameter Type = 14 | Parameter Length = 8 + 2 * N | 34 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 35 | | Re-configuration Request Sequence Number | 36 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 37 | | Stream Number 1 (optional) | Stream Number 2 (optional) | 38 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 39 | / ...... / 40 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 41 | | Stream Number N-1 (optional) | Stream Number N (optional) | 42 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 43 | */ 44 | long reqSeqNo; 45 | int streams[]; 46 | 47 | public IncomingSSNResetRequestParameter(int t, String n) { 48 | super(t, n); 49 | } 50 | 51 | public IncomingSSNResetRequestParameter() { 52 | super(14, "IncomingSSNResetRequestParameter"); 53 | } 54 | 55 | public IncomingSSNResetRequestParameter(long reqNo) { 56 | this(); 57 | this.reqSeqNo = reqNo; 58 | } 59 | 60 | @Override 61 | public void readBody(ByteBuffer body, int blen) { 62 | if (blen < 4) { 63 | Log.error("Huh ? No body to this " + this.getName()); 64 | return; 65 | } 66 | reqSeqNo = Chunk.getUnsignedInt(body); 67 | if (blen > 4) { 68 | this.streams = new int[(blen - 4) / 2]; 69 | for (int i = 0; i < streams.length; i++) { 70 | streams[i] = body.getChar(); 71 | } 72 | } else { 73 | this.streams = new int[0]; 74 | Log.warn("No inbound stream mentioned"); 75 | } 76 | } 77 | 78 | @Override 79 | public void writeBody(ByteBuffer body) { 80 | Chunk.putUnsignedInt(body, reqSeqNo); 81 | if (streams != null) { 82 | for (int i = 0; i < streams.length; i++) { 83 | body.putChar((char) streams[i]); 84 | } 85 | } 86 | } 87 | 88 | @Override 89 | public String toString() { 90 | StringBuffer ret = new StringBuffer(); 91 | ret.append(this.getClass().getSimpleName()).append(" "); 92 | ret.append("seq:" + this.reqSeqNo); 93 | if (streams != null) { 94 | ret.append("streams {"); 95 | for (int s : streams) { 96 | ret.append("" + s); 97 | } 98 | ret.append("}"); 99 | } else { 100 | ret.append("no streams"); 101 | } 102 | return ret.toString(); 103 | } 104 | 105 | public boolean sameAs(IncomingSSNResetRequestParameter other) { 106 | return this.reqSeqNo == other.reqSeqNo; 107 | } 108 | 109 | public int[] getStreams() { 110 | return streams; 111 | } 112 | 113 | public long getReqNo() { 114 | return this.reqSeqNo; 115 | } 116 | public void setStreams(int[] ss) { 117 | this.streams =ss; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/KnownError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.params; 19 | 20 | import pe.pi.sctp4j.sctp.messages.Packet; 21 | 22 | /** 23 | * 24 | * @author tim 25 | */ 26 | public class KnownError extends Unknown { 27 | 28 | public KnownError(int t, String n) { 29 | super(t, n); 30 | } 31 | 32 | public byte[] getData(){ 33 | return _data; 34 | } 35 | public void setData(byte [] data){ 36 | _data = data; 37 | } 38 | @Override 39 | public String toString(){ 40 | return "Variable error "+this.getName() + " data "+new String(_data) + " "+ Packet.getHex(_data); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/KnownParam.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.params; 19 | 20 | /** 21 | * 22 | * @author tim 23 | */ 24 | public class KnownParam extends Unknown { 25 | 26 | public KnownParam(int t, String n) { 27 | super(t, n); 28 | } 29 | 30 | public byte[] getData(){ 31 | return _data; 32 | } 33 | public void setData(byte [] data){ 34 | _data = data; 35 | } 36 | @Override 37 | public String toString(){ 38 | return "Variable param "+this.getName(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/OutgoingSSNResetRequestParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import pe.pi.sctp4j.sctp.messages.Chunk; 20 | import java.nio.ByteBuffer; 21 | 22 | /** 23 | * 24 | * @author tim 25 | */ 26 | public class OutgoingSSNResetRequestParameter extends KnownParam { 27 | 28 | 29 | 30 | /* 31 | 0 1 2 3 32 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 33 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 34 | | Parameter Type = 13 | Parameter Length = 16 + 2 * N | 35 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 36 | | Re-configuration Request Sequence Number | 37 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 | | Re-configuration Response Sequence Number | 39 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 40 | | Sender's Last Assigned TSN | 41 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 42 | | Stream Number 1 (optional) | Stream Number 2 (optional) | 43 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 44 | / ...... / 45 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 46 | | Stream Number N-1 (optional) | Stream Number N (optional) | 47 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 48 | */ 49 | long reqSeqNo; 50 | long respSeqNo; 51 | long lastTsn; 52 | int streams[]; 53 | 54 | public OutgoingSSNResetRequestParameter(int t, String n) { 55 | super(t, n); 56 | } 57 | public OutgoingSSNResetRequestParameter() { 58 | super(13, "OutgoingSSNResetRequestParameter"); 59 | } 60 | public OutgoingSSNResetRequestParameter(long reqNo,long respNo, long lastTsn) { 61 | this(); 62 | this.respSeqNo = respNo; 63 | this.lastTsn = lastTsn; 64 | this.reqSeqNo = reqNo; 65 | } 66 | 67 | public long getRespSeqNo() { 68 | return respSeqNo; 69 | } 70 | 71 | public long getReqSeqNo() { 72 | return reqSeqNo; 73 | } 74 | 75 | @Override 76 | public void readBody(ByteBuffer body, int blen) { 77 | reqSeqNo = Chunk.getUnsignedInt(body); 78 | respSeqNo = Chunk.getUnsignedInt(body); 79 | lastTsn = Chunk.getUnsignedInt(body); 80 | streams = new int[(blen - 12) / 2]; 81 | for (int i = 0; i < streams.length; i++) { 82 | streams[i] = body.getChar(); 83 | } 84 | } 85 | 86 | @Override 87 | public void writeBody(ByteBuffer body) { 88 | Chunk.putUnsignedInt(body, reqSeqNo); 89 | Chunk.putUnsignedInt(body, respSeqNo); 90 | Chunk.putUnsignedInt(body, lastTsn); 91 | if (streams != null) { 92 | for (int i = 0; i < streams.length; i++) { 93 | body.putChar((char) streams[i]); 94 | } 95 | } 96 | } 97 | 98 | @Override 99 | public String toString() { 100 | StringBuffer ret = new StringBuffer(); 101 | ret.append(this.getClass().getSimpleName()).append(" "); 102 | ret.append("reqseq:").append(this.reqSeqNo).append(" "); 103 | ret.append("respseq:").append(this.respSeqNo).append(" "); 104 | ret.append("latsTSN:").append(this.lastTsn).append(" "); 105 | 106 | if (streams != null) { 107 | ret.append("streams {"); 108 | for (int s : streams) { 109 | ret.append("" + s); 110 | } 111 | ret.append("}"); 112 | } else { 113 | ret.append("no streams"); 114 | } 115 | return ret.toString(); 116 | } 117 | 118 | public long getLastAssignedTSN() { 119 | return lastTsn; 120 | } 121 | 122 | public int[] getStreams() { 123 | return streams; 124 | } 125 | public void setStreams(int[] ss) { 126 | this.streams =ss; 127 | } 128 | 129 | public boolean sameAs(OutgoingSSNResetRequestParameter other) { 130 | return this.reqSeqNo == other.reqSeqNo; 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/ProtocolViolationError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.params; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | public class ProtocolViolationError extends KnownError { 25 | 26 | public ProtocolViolationError(int t, String n) { 27 | super(t, n); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/ReconfigurationResponseParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import pe.pi.sctp4j.sctp.messages.Chunk; 20 | import java.nio.ByteBuffer; 21 | 22 | /** 23 | * 24 | * @author tim 25 | */ 26 | public class ReconfigurationResponseParameter extends KnownParam { 27 | 28 | /* 29 | 0 1 2 3 30 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 31 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | | Parameter Type = 16 | Parameter Length | 33 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 34 | | Re-configuration Response Sequence Number | 35 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 36 | | Result | 37 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 | | Sender's Next TSN (optional) | 39 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 40 | | Receiver's Next TSN (optional) | 41 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 42 | */ 43 | long seqNo; 44 | long result; 45 | long senderNextTSN; 46 | long receiverNextTSN; 47 | boolean hasTSNs; 48 | public final static int SUCCESS_NOTHING_TO_DO = 0; 49 | public final static int SUCCESS_PERFORMED = 1; 50 | public final static int DENIED = 2; 51 | public final static int ERROR_WRONG_SSN = 3; 52 | public final static int ERROR_REQUEST_ALREADY_IN_PROGESS = 4; 53 | public final static int ERROR_BAD_SEQUENCE_NUMBER = 5; 54 | public final static int IN_PROGRESS = 6; 55 | final static String valuenames[] = { 56 | "Success - Nothing to do", 57 | "Success - Performed", 58 | "Denied", 59 | "Error - Wrong SSN", 60 | "Error - Request already in progress", 61 | "Error - Bad Sequence Number", 62 | "In progress" 63 | }; 64 | 65 | /* 66 | +--------+-------------------------------------+ 67 | | Result | Description | 68 | +--------+-------------------------------------+ 69 | | 0 | Success - Nothing to do | 70 | | 1 | Success - Performed | 71 | | 2 | Denied | 72 | | 3 | Error - Wrong SSN | 73 | | 4 | Error - Request already in progress | 74 | | 5 | Error - Bad Sequence Number | 75 | | 6 | In progress | 76 | +--------+-------------------------------------+ 77 | */ 78 | public ReconfigurationResponseParameter(int t, String n) { 79 | super(t, n); 80 | } 81 | 82 | public ReconfigurationResponseParameter() { 83 | this(16, "ReconfigurationResponseParameter"); 84 | } 85 | 86 | @Override 87 | public void readBody(ByteBuffer body, int blen) { 88 | this.seqNo = Chunk.getUnsignedInt(body); 89 | this.result = Chunk.getUnsignedInt(body); 90 | if (blen == 16) { 91 | this.senderNextTSN = Chunk.getUnsignedInt(body); 92 | this.receiverNextTSN = Chunk.getUnsignedInt(body); 93 | hasTSNs = true; 94 | } 95 | } 96 | 97 | @Override 98 | public void writeBody(ByteBuffer body) { 99 | Chunk.putUnsignedInt(body, seqNo); 100 | Chunk.putUnsignedInt(body, result); 101 | if (hasTSNs) { 102 | Chunk.putUnsignedInt(body, senderNextTSN); 103 | Chunk.putUnsignedInt(body, receiverNextTSN); 104 | } 105 | } 106 | 107 | private String resultToName() { 108 | return ((result >= 0) && (result < valuenames.length)) 109 | ? valuenames[(int) result] : "invalid value"; 110 | } 111 | 112 | public String toString() { 113 | StringBuffer ret = new StringBuffer(); 114 | ret.append(this.getClass().getSimpleName()).append(" "); 115 | ret.append("seqNo:").append(this.seqNo).append(" "); 116 | ret.append("result:").append(resultToName()).append(" "); 117 | if (hasTSNs) { 118 | ret.append("senderNextTSN:").append(this.senderNextTSN).append(" "); 119 | ret.append("receiverNextTSN:").append(this.receiverNextTSN).append(" "); 120 | } 121 | return ret.toString(); 122 | } 123 | 124 | public void setResult(int res) { 125 | result = res; 126 | } 127 | 128 | public void setSeq(long reqSeqNo) { 129 | seqNo = reqSeqNo; 130 | } 131 | 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/RequestedHMACAlgorithmParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | /** 22 | * 23 | * @author tim 24 | */ 25 | public class RequestedHMACAlgorithmParameter extends KnownParam { 26 | /* 27 | 28 | 0 1 2 3 29 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 30 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 31 | | Parameter Type = 0x8004 | Parameter Length | 32 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 33 | | HMAC Identifier 1 | HMAC Identifier 2 | 34 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 35 | / / 36 | \ ... \ 37 | / / 38 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 39 | | HMAC Identifier n | Padding | 40 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 41 | */ 42 | 43 | int hmacs[]; 44 | /* 45 | +-----------------+--------------------------+ 46 | | HMAC Identifier | Message Digest Algorithm | 47 | +-----------------+--------------------------+ 48 | | 0 | Reserved | 49 | | 1 | SHA-1 defined in [8] | 50 | | 2 | Reserved | 51 | | 3 | SHA-256 defined in [8] | 52 | +-----------------+--------------------------+ 53 | */ 54 | 55 | public RequestedHMACAlgorithmParameter(int t, String n) { 56 | super(t, n); 57 | } 58 | 59 | public void readBody(ByteBuffer body, int blen) { 60 | hmacs = new int[blen / 2]; 61 | for (int i = 0; i < hmacs.length; i++) { 62 | hmacs[i] = body.getChar(); 63 | } 64 | } 65 | 66 | public void writeBody(ByteBuffer body) { 67 | if (hmacs != null) { 68 | for (int i = 0; i < hmacs.length; i++) { 69 | body.putChar((char) hmacs[i]); 70 | } 71 | } 72 | } 73 | 74 | public String toString() { 75 | String ret = " Hmacs are "; 76 | for (int i = 0; i < hmacs.length; i++) { 77 | switch (hmacs[i]) { 78 | case 0: 79 | case 2: 80 | ret += " Reserved "; 81 | break; 82 | case 1: 83 | ret += " SHA-1 "; 84 | break; 85 | case 3: 86 | ret += " SHA-256 "; 87 | break; 88 | } 89 | } 90 | return super.toString() + ret; 91 | } 92 | 93 | public boolean doesSha256() { 94 | boolean ret = false; 95 | for (int i = 0; i < hmacs.length; i++) { 96 | if (3 == hmacs[i]) { 97 | ret = true; 98 | } 99 | } 100 | return ret; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/SSNTSNResetRequestParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.params; 19 | 20 | import pe.pi.sctp4j.sctp.messages.Chunk; 21 | import pe.pi.sctp4j.sctp.messages.exceptions.SctpPacketFormatException; 22 | import java.nio.ByteBuffer; 23 | 24 | /** 25 | * 26 | * @author tim 27 | */ 28 | public class SSNTSNResetRequestParameter extends Unknown { 29 | 30 | long _seqno; 31 | public SSNTSNResetRequestParameter(int t, String n) { 32 | super(t, n); 33 | } 34 | /* 35 | 0 1 2 3 36 | 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 37 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 | | Parameter Type = 15 | Parameter Length = 8 | 39 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 40 | | Re-configuration Request Sequence Number | 41 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 42 | */ 43 | @Override 44 | public void readBody(ByteBuffer body, int blen) throws SctpPacketFormatException { 45 | _seqno = Chunk.getUnsignedInt(body); 46 | } 47 | @Override 48 | public void writeBody(ByteBuffer body) throws SctpPacketFormatException { 49 | Chunk.putUnsignedInt(body, _seqno); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/StaleCookieError.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import pe.pi.sctp4j.sctp.messages.Chunk; 20 | import java.nio.ByteBuffer; 21 | 22 | /** 23 | * 24 | * @author Westhawk Ltd 25 | */ 26 | public class StaleCookieError extends KnownError { 27 | private long _measure; 28 | /* 29 | 30 | Stale Cookie Error (3) 31 | 32 | Cause of error 33 | -------------- 34 | 35 | Stale Cookie Error: Indicates the receipt of a valid State Cookie 36 | that has expired. 37 | 38 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 39 | | Cause Code=3 | Cause Length=8 | 40 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 41 | | Measure of Staleness (usec.) | 42 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 43 | 44 | Measure of Staleness: 32 bits (unsigned integer) 45 | 46 | This field contains the difference, in microseconds, between the 47 | current time and the time the State Cookie expired. 48 | 49 | The sender of this error cause MAY choose to report how long past 50 | expiration the State Cookie is by including a non-zero value in 51 | the Measure of Staleness field. If the sender does not wish to 52 | provide this information, it should set the Measure of Staleness 53 | field to the value of zero. 54 | 55 | */ 56 | 57 | public StaleCookieError() { 58 | super(3, "StaleCookieError"); 59 | } 60 | 61 | @Override 62 | public void readBody(ByteBuffer body, int blen) { 63 | _measure = Chunk.getUnsignedInt(body); 64 | } 65 | 66 | 67 | @Override 68 | public void writeBody(ByteBuffer body) { 69 | Chunk.putUnsignedInt(body,_measure); 70 | } 71 | 72 | public long getMeasure(){ 73 | return _measure; 74 | } 75 | 76 | public void setMeasure(long mes){ 77 | _measure = mes; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/StateCookie.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | /** 20 | * 21 | * @author tim 22 | */ 23 | public class StateCookie extends KnownParam { 24 | 25 | public StateCookie() { 26 | this(7, "StateCookie"); 27 | } 28 | 29 | public StateCookie(int t, String n) { 30 | super(t, n); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/SupportedAddressTypes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import java.nio.ByteBuffer; 20 | 21 | /** 22 | * 23 | * @author tim 24 | */ 25 | public class SupportedAddressTypes extends KnownParam { 26 | 27 | int supported[]; 28 | 29 | public SupportedAddressTypes(int t, String n) { 30 | super(t, n); 31 | } 32 | 33 | public void readBody(ByteBuffer body, int blen) { 34 | supported = new int[blen / 2]; 35 | for (int i = 0; i < supported.length; i++) { 36 | supported[i] = body.getChar(); 37 | } 38 | } 39 | 40 | public void writeBody(ByteBuffer body) { 41 | if (supported != null) { 42 | for (int i = 0; i < supported.length; i++) { 43 | body.putChar((char) supported[i]); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/Unknown.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.messages.params; 18 | 19 | import pe.pi.sctp4j.sctp.messages.exceptions.SctpPacketFormatException; 20 | import java.nio.ByteBuffer; 21 | 22 | /** 23 | * 24 | * @author tim 25 | */ 26 | public class Unknown implements VariableParam { 27 | 28 | byte _data[]; 29 | final int _type; 30 | final String _name; 31 | 32 | public Unknown(int t,String n){ 33 | _type = t; 34 | _name = n; 35 | } 36 | 37 | @Override 38 | public void readBody(ByteBuffer b, int len) throws SctpPacketFormatException { 39 | _data = new byte[len]; 40 | b.get(_data); 41 | } 42 | 43 | @Override 44 | public void writeBody(ByteBuffer b) throws SctpPacketFormatException { 45 | b.put(this._data); 46 | } 47 | 48 | @Override 49 | public int getType() { 50 | return _type; 51 | } 52 | 53 | @Override 54 | public String getName() { 55 | return _name; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/UnrecognizedParameters.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.params; 19 | 20 | import pe.pi.sctp4j.sctp.messages.params.Unknown; 21 | 22 | /** 23 | * 24 | * @author tim 25 | */ 26 | public class UnrecognizedParameters extends KnownParam { 27 | 28 | public UnrecognizedParameters(int t, String n) { 29 | super(t, n); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/messages/params/VariableParam.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.messages.params; 19 | 20 | import pe.pi.sctp4j.sctp.messages.exceptions.SctpPacketFormatException; 21 | import java.nio.ByteBuffer; 22 | 23 | /** 24 | * 25 | * @author tim 26 | */ 27 | public interface VariableParam { 28 | public void readBody(ByteBuffer b, int len) throws SctpPacketFormatException; 29 | public void writeBody(ByteBuffer b) throws SctpPacketFormatException; 30 | 31 | public int getType(); 32 | 33 | public String getName(); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/small/BlockingSCTPStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.small; 18 | 19 | import pe.pi.sctp4j.sctp.SCTPMessage; 20 | import pe.pi.sctp4j.sctp.SCTPStream; 21 | import pe.pi.sctp4j.sctp.messages.DataChunk; 22 | import java.util.HashMap; 23 | import java.util.concurrent.Executors; 24 | import pe.pi.sctp4j.sctp.dataChannel.DECP.DCOpen; 25 | import com.phono.srtplight.Log; 26 | import java.util.concurrent.ExecutorService; 27 | 28 | /** 29 | * 30 | * @author Westhawk Ltd 31 | */ 32 | public class BlockingSCTPStream extends SCTPStream { 33 | 34 | private HashMap undeliveredOutboundMessages = new HashMap(); 35 | private final ThreadedAssociation _ta; 36 | private final ExecutorService _ex; 37 | 38 | BlockingSCTPStream(ThreadedAssociation a, Integer id) { 39 | super(a, id); 40 | _ex = Executors.newSingleThreadExecutor((Runnable r) -> new Thread(r, "Stream-" + id + "-Exec")); 41 | _ta = a; 42 | } 43 | 44 | @Override 45 | synchronized public void send(String message) throws Exception { 46 | SCTPMessage m = _ta.makeMessage(message, this); 47 | undeliveredOutboundMessages.put(m.getSeq(), m); 48 | _ta.sendAndBlock(m); 49 | } 50 | 51 | @Override 52 | synchronized public void send(byte[] message) throws Exception { 53 | SCTPMessage m = _ta.makeMessage(message, this); 54 | undeliveredOutboundMessages.put(m.getSeq(), m); 55 | _ta.sendAndBlock(m); 56 | } 57 | 58 | @Override 59 | public void send(DCOpen message) throws Exception { 60 | SCTPMessage m = _ta.makeMessage(message, this); 61 | undeliveredOutboundMessages.put(m.getSeq(), m); 62 | Log.debug("About to send message for dcep size is " + m.getData().length); 63 | _ta.sendAndBlock(m); 64 | } 65 | 66 | @Override 67 | public void deliverMessage(SCTPMessage message) { 68 | _ex.execute(message); 69 | } 70 | 71 | @Override 72 | protected void alOnDCEPStream(SCTPStream _stream, String label, int _pPid) throws Exception { 73 | _ex.execute(() -> { 74 | try { 75 | super.alOnDCEPStream(_stream, label, _pPid); 76 | } catch (Exception ex) { 77 | Log.error("can't notify DCEPStream " + ex.getMessage()); 78 | } 79 | }); 80 | } 81 | 82 | @Override 83 | public void delivered(DataChunk d) { 84 | int f = d.getFlags(); 85 | if ((f & DataChunk.ENDFLAG) > 0) { 86 | int ssn = d.getSSeqNo(); 87 | SCTPMessage st = undeliveredOutboundMessages.remove(ssn); 88 | if (st != null) { 89 | st.acked(); 90 | } 91 | } 92 | } 93 | 94 | @Override 95 | public boolean idle() { 96 | return undeliveredOutboundMessages.isEmpty(); 97 | } 98 | 99 | @Override 100 | public void close() throws Exception { 101 | super.close(); 102 | if ((_ex != null) && (!_ex.isShutdown())) { 103 | _ex.shutdownNow(); 104 | Log.debug("shutdown of Stream-" + this.getNum() + "-Exec"); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/small/MessageSizeExceededException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 | 18 | package pe.pi.sctp4j.sctp.small; 19 | 20 | /** 21 | * 22 | * @author Westhawk Ltd 23 | */ 24 | class MessageSizeExceededException extends Exception { 25 | 26 | public MessageSizeExceededException() { 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/small/SimpleSCTPTimer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.small; 18 | 19 | import com.phono.srtplight.Log; 20 | import java.util.Timer; 21 | import java.util.TimerTask; 22 | 23 | /** 24 | * 25 | * @author tim Only one queued invocation. New requests replace queued ones if 26 | * they are earlier. 27 | */ 28 | abstract class SimpleSCTPTimer { 29 | 30 | protected static Timer _timer = new Timer("SCTPTimer", true); 31 | static int tno = 1; 32 | long scheduledAt = Long.MAX_VALUE; 33 | TimerTask task = null; 34 | 35 | public abstract void tick(); 36 | 37 | public void setNextRun(long by) { 38 | long now = System.currentTimeMillis(); 39 | long when = now + by; 40 | if (when < scheduledAt) { 41 | if (task != null) { 42 | task.cancel(); 43 | Log.verb("cancelled future task scheduled for " + scheduledAt + " because new task at " + when); 44 | } 45 | task = new TimerTask() { 46 | @Override 47 | public void run() { 48 | try { 49 | Log.verb("running task"); 50 | tick(); 51 | scheduledAt = Long.MAX_VALUE; 52 | } catch (Throwable t) { 53 | Log.error("SCTPTimerTask threw Exception " + t); 54 | if (Log.getLevel() >= Log.DEBUG) { 55 | t.printStackTrace(); 56 | } 57 | } 58 | } 59 | }; 60 | _timer.schedule(task, by); 61 | Log.verb("SCTPTimer task now scheduled at " + when); 62 | scheduledAt = when; 63 | } else { 64 | Log.verb ("already have a task scheduled for "+scheduledAt+" which is earlier than "+when); 65 | } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/pe/pi/sctp4j/sctp/small/UDPForwardingStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp.small; 18 | 19 | import pe.pi.sctp4j.sctp.Association; 20 | import pe.pi.sctp4j.sctp.behave.SCTPStreamBehaviour; 21 | import pe.pi.sctp4j.sctp.behave.UnorderedStreamBehaviour; 22 | import com.phono.srtplight.Log; 23 | import java.net.DatagramPacket; 24 | import java.net.DatagramSocket; 25 | import java.net.InetSocketAddress; 26 | import java.net.SocketAddress; 27 | import java.net.SocketException; 28 | import java.net.SocketTimeoutException; 29 | 30 | /** 31 | * 32 | * @author tim 33 | */ 34 | public class UDPForwardingStream extends BlockingSCTPStream implements Runnable { 35 | 36 | DatagramSocket _udpSock; 37 | private final Thread _rcv; 38 | 39 | public UDPForwardingStream(Association a, Integer id, Integer toPort) throws SocketException { 40 | super((ThreadedAssociation)a, id); 41 | _udpSock = new DatagramSocket(); 42 | SocketAddress addr = new InetSocketAddress("localhost", toPort.shortValue()); 43 | _udpSock.bind(addr); 44 | _rcv = new Thread(this, "UDPForwarding_rcv"); 45 | _rcv.start(); 46 | SCTPStreamBehaviour behave = mkBehave(); 47 | super.setBehave(behave); 48 | } 49 | 50 | @Override 51 | public void run() { 52 | try { 53 | _udpSock.setSoTimeout(1000); 54 | byte buff[] = new byte[4096]; 55 | DatagramPacket dgp = new DatagramPacket(buff, buff.length); 56 | while (_rcv != null) { 57 | try { 58 | _udpSock.receive(dgp); 59 | int l = dgp.getLength(); 60 | if (l > buff.length) { 61 | Log.warn("truncated packet from " + _udpSock.getRemoteSocketAddress().toString()); 62 | l = buff.length; 63 | } 64 | byte pkt[] = new byte[l]; 65 | System.arraycopy(buff, 0, pkt, 0, l); 66 | send(pkt); 67 | } catch (SocketTimeoutException stx) { 68 | ; // ignore - lets us check for close.... 69 | } 70 | } 71 | } catch (Exception x) { 72 | 73 | } 74 | // clean up here..... 75 | } 76 | 77 | 78 | private SCTPStreamBehaviour mkBehave() { 79 | return new UnorderedStreamBehaviour(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/pe/pi/sctp4j/sctp/SCTPMessageTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 pi.pe gmbh . 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 pe.pi.sctp4j.sctp; 18 | 19 | import pe.pi.sctp4j.sctp.messages.*; 20 | import pe.pi.sctp4j.sctp.messages.exceptions.SctpPacketFormatException; 21 | import pe.pi.sctp4j.sctp.small.BlockingSCTPStream; 22 | import java.io.IOException; 23 | import java.util.Random; 24 | import java.util.TreeSet; 25 | import org.bouncycastle.tls.DatagramTransport; 26 | import org.junit.After; 27 | import org.junit.AfterClass; 28 | import org.junit.Before; 29 | import org.junit.BeforeClass; 30 | import org.junit.Test; 31 | import static org.junit.Assert.*; 32 | import pe.pi.sctp4j.sctp.dataChannel.DECP.DCOpen; 33 | import pe.pi.sctp4j.sctp.dummy.DummyStream; 34 | 35 | /** 36 | * 37 | * @author tim 38 | */ 39 | public class SCTPMessageTest { 40 | 41 | private Random _rand = new Random(1); // deterministic non crypto quality random for repeatable tests 42 | private SCTPStream _fakeStream; 43 | 44 | public SCTPMessageTest() { 45 | } 46 | 47 | @BeforeClass 48 | public static void setUpClass() { 49 | } 50 | 51 | @AfterClass 52 | public static void tearDownClass() { 53 | } 54 | Association _fakeAssociation; 55 | 56 | @Before 57 | public void setUp() { 58 | DatagramTransport _fakedt = new DatagramTransport() { 59 | 60 | @Override 61 | public int getReceiveLimit() throws IOException { 62 | return 1200; 63 | } 64 | 65 | @Override 66 | public int getSendLimit() throws IOException { 67 | return 1200; 68 | } 69 | 70 | @Override 71 | public int receive(byte[] bytes, int i, int i1, int waitMs) throws IOException { 72 | try { 73 | Thread.sleep(waitMs); 74 | } catch (Exception x) { 75 | } 76 | throw new java.io.InterruptedIOException("empty"); 77 | } 78 | 79 | @Override 80 | public void send(byte[] bytes, int i, int i1) throws IOException { 81 | } 82 | 83 | @Override 84 | public void close() throws IOException { 85 | } 86 | 87 | }; 88 | _fakeAssociation = new Association(_fakedt, null) { 89 | @Override 90 | public void associate() throws SctpPacketFormatException, IOException { 91 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 92 | } 93 | 94 | @Override 95 | public void enqueue(DataChunk d) { 96 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 97 | } 98 | 99 | @Override 100 | public SCTPStream mkStream(int id) { 101 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 102 | } 103 | 104 | @Override 105 | protected Chunk[] sackDeal(SackChunk sackChunk) { 106 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 107 | } 108 | }; 109 | _fakeStream = new DummyStream(_fakeAssociation, new Integer(22)) { 110 | @Override 111 | public void deliverMessage(SCTPMessage message) { 112 | message.run(); 113 | } 114 | }; 115 | } 116 | 117 | @After 118 | public void tearDown() { 119 | } 120 | 121 | /** 122 | * Test of setCompleteHandler method, of class SCTPMessage. 123 | */ 124 | /* 125 | @Test 126 | public void testSetCompleteHandler() { 127 | System.out.println("setCompleteHandler"); 128 | MessageCompleteHandler mch = null; 129 | SCTPMessage instance = null; 130 | instance.setCompleteHandler(mch); 131 | // TODO review the generated test code and remove the default call to fail. 132 | fail("The test case is a prototype."); 133 | } 134 | */ 135 | /** 136 | * Test of hasMoreData method, of class SCTPMessage. 137 | */ 138 | /* 139 | @Test 140 | public void testHasMoreData() { 141 | 142 | } 143 | */ 144 | /** 145 | * Test of fill method, of class SCTPMessage. 146 | */ 147 | @Test 148 | public void testFillShortString() { 149 | System.out.println("--> fill short string "); 150 | String testString = "This is a short test"; 151 | SCTPMessage instance = new SCTPMessage(testString, _fakeStream); 152 | TreeSet chunks = new TreeSet(); 153 | while (instance.hasMoreData()) { 154 | DataChunk dc = new DataChunk(); 155 | instance.fill(dc); 156 | chunks.add(dc); 157 | } 158 | assertEquals(chunks.size(), 1); 159 | int ppid = ((DataChunk)chunks.first()).getPpid(); 160 | assertEquals(ppid,DataChunk.WEBRTCSTRING); 161 | } 162 | @Test 163 | public void testFillShortBlob() { 164 | System.out.println("--> fill short blob "); 165 | byte [] testBlob = new byte[21]; 166 | _rand.nextBytes(testBlob); 167 | SCTPMessage instance = new SCTPMessage(testBlob, _fakeStream); 168 | TreeSet chunks = new TreeSet(); 169 | while (instance.hasMoreData()) { 170 | DataChunk dc = new DataChunk(); 171 | instance.fill(dc); 172 | chunks.add(dc); 173 | } 174 | assertEquals(chunks.size(), 1); 175 | int ppid = ((DataChunk)chunks.first()).getPpid(); 176 | assertEquals(ppid,DataChunk.WEBRTCBINARY); 177 | } 178 | @Test 179 | public void testFillLongString() { 180 | System.out.println("--> fill long"); 181 | StringBuffer sb = new StringBuffer("This is a"); 182 | for (int i = 0; i < 1030; i++) { 183 | sb.append(" long"); 184 | } 185 | sb.append(" test."); 186 | String testString = sb.toString(); 187 | SCTPMessage instance = new SCTPMessage(testString, _fakeStream); 188 | TreeSet chunks = new TreeSet(); 189 | long tsn = 111; 190 | 191 | while (instance.hasMoreData()) { 192 | DataChunk dc = new DataChunk(); 193 | dc.setTsn(tsn++); 194 | instance.fill(dc); 195 | chunks.add(dc); 196 | } 197 | double pktsz = chunks.first().getDataSize(); 198 | int estimate = (int) Math.ceil(testString.length() / pktsz); 199 | assertEquals(chunks.size(), estimate); 200 | } 201 | 202 | @Test 203 | public void testEmptyString() { 204 | System.out.println("--> fill empty string"); 205 | StringBuffer sb = new StringBuffer(""); 206 | String testString = sb.toString(); 207 | SCTPMessage instance = new SCTPMessage(testString, _fakeStream); 208 | TreeSet chunks = new TreeSet(); 209 | long tsn = 111; 210 | 211 | while (instance.hasMoreData()) { 212 | DataChunk dc = new DataChunk(); 213 | dc.setTsn(tsn++); 214 | instance.fill(dc); 215 | chunks.add(dc); 216 | } 217 | int pktsz = chunks.first().getDataSize(); 218 | assertEquals(chunks.size(), 1); 219 | assertEquals(pktsz,1); 220 | int ppid = ((DataChunk)chunks.first()).getPpid(); 221 | assertEquals(ppid,DataChunk.WEBRTCSTRINGEMPTY); 222 | } 223 | 224 | @Test 225 | public void testEmptyBlob() { 226 | System.out.println("--> fill empty blob"); 227 | byte [] testBlob = new byte[0]; 228 | SCTPMessage instance = new SCTPMessage(testBlob, _fakeStream); 229 | TreeSet chunks = new TreeSet(); 230 | long tsn = 111; 231 | 232 | while (instance.hasMoreData()) { 233 | DataChunk dc = new DataChunk(); 234 | dc.setTsn(tsn++); 235 | instance.fill(dc); 236 | chunks.add(dc); 237 | } 238 | assertEquals(chunks.size(), 1); 239 | int pktsz = chunks.first().getDataSize(); 240 | assertEquals(pktsz,1); 241 | int ppid = ((DataChunk)chunks.first()).getPpid(); 242 | assertEquals(ppid,DataChunk.WEBRTCBINARYEMPTY); 243 | } 244 | 245 | /** 246 | * Test of getData method, of class SCTPMessage. 247 | */ 248 | /* 249 | @Test 250 | public void testGetData() { 251 | System.out.println("getData"); 252 | SCTPMessage instance = null; 253 | byte[] expResult = null; 254 | byte[] result = instance.getData(); 255 | assertArrayEquals(expResult, result); 256 | // TODO review the generated test code and remove the default call to fail. 257 | fail("The test case is a prototype."); 258 | }*/ 259 | } 260 | -------------------------------------------------------------------------------- /test/com/ipseorama/sctp/messages/TestPacket.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Westhawk Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package com.ipseorama.sctp.messages 20 | 21 | import com.ipseorama.sctp.Association 22 | import com.ipseorama.sctp.MockAssociation 23 | import com.ipseorama.sctp.messages.exceptions.SctpPacketFormatException; 24 | import java.nio.Buffer 25 | import java.nio.ByteBuffer; 26 | import org.bouncycastle.crypto.tls.DatagramTransport 27 | 28 | import com.phono.srtplight.Log; 29 | /** 30 | * 31 | * @author Westhawk Ltd 32 | */ 33 | class TestPacket extends GroovyTestCase { 34 | byte [] sampleInit = [ 35 | (byte)0x13, (byte)0x88, (byte)0x13, (byte)0x88, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x68, 36 | (byte)0x1c, (byte)0xb9, (byte)0xa0, (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x56, (byte)0x95, 37 | (byte)0xaa, (byte)0x39, (byte)0xc0, (byte)0x00, (byte)0x02, (byte)0x00, (byte)0x00, (byte)0x03, 38 | (byte)0xff, (byte)0x08, (byte)0x00, (byte)0x83, (byte)0x14, (byte)0xf5, (byte)0xb3, (byte)0xc0, 39 | (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x80, (byte)0x08, (byte)0x00, (byte)0x0a, (byte)0xc1, 40 | (byte)0x80, (byte)0xc0, (byte)0x81, (byte)0x82, (byte)0x0f, (byte)0x00, (byte)0x00, (byte)0x80, 41 | (byte)0x02, (byte)0x00, (byte)0x24, (byte)0x8e, (byte)0xb2, (byte)0x16, (byte)0x46, (byte)0x28, 42 | (byte)0xe1, (byte)0xaf, (byte)0x0d, (byte)0xf7, (byte)0x19, (byte)0xef, (byte)0x53, (byte)0xa7, 43 | (byte)0xa7, (byte)0x7c, (byte)0x6c, (byte)0x0e, (byte)0x93, (byte)0x73, (byte)0x60, (byte)0x54, 44 | (byte)0x73, (byte)0xee, (byte)0x2c, (byte)0x6f, (byte)0x8c, (byte)0x23, (byte)0x6c, (byte)0x51, 45 | (byte)0xe1, (byte)0xbe, (byte)0x5f, (byte)0x80, (byte)0x04, (byte)0x00, (byte)0x06, (byte)0x00, 46 | (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x80, (byte)0x03, (byte)0x00, (byte)0x06, (byte)0x80, 47 | (byte)0xc1, (byte)0x00, (byte)0x00 ]; 48 | 49 | byte [] sampleAbort = [(byte)0x13, (byte)0x88, (byte)0x13, (byte)0x88, (byte)0x27, (byte)0x44, (byte)0xfa, (byte)0x52, (byte)0xb0, (byte)0x85 50 | , (byte)0xdd, (byte)0x7b, (byte)0x06, (byte)0x01, (byte)0x00, (byte)0x0c, (byte)0x00, (byte)0x0d, (byte)0x00, (byte)0x08, (byte)0x40, (byte)0x00, (byte)0x00 51 | , (byte)0x01]; 52 | byte [] sampleAbort2 = [(byte)0x13,(byte)0x88,(byte)0x13,(byte)0x88,(byte)0x53,(byte)0x05,(byte)0x6d,(byte)0xb5,(byte)0x97,(byte)0xe0,(byte)0xd0 53 | ,(byte)0x20,(byte)0x06,(byte)0x00,(byte)0x00,(byte)0x14,(byte)0x00,(byte)0x0d,(byte)0x00,(byte)0x10,(byte)0x30,(byte)0x00,(byte)0x00,(byte)0x02 54 | ,(byte)0x44,(byte)0x15,(byte)0xd2,(byte)0x71,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00]; 55 | 56 | byte [] sampleData = [(byte)0x13, (byte)0x88, (byte)0x13, (byte)0x88, (byte)0x13, (byte)0xfc, (byte)0x3a, (byte)0x88, (byte)0x87, (byte)0xa9 57 | , (byte)0x5b, (byte)0xfc, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x20, (byte)0xf8, (byte)0x13, (byte)0x18, (byte)0x5b, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x03, (byte)0x00 58 | , (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x63, (byte)0x68, (byte)0x61, (byte)0x74]; 59 | 60 | byte [] sampleHeartBeat = [(byte)0x13, (byte)0x88, (byte)0x13, (byte)0x88, (byte)0x00, (byte)0x6f, (byte)0x0e, (byte)0x57, (byte)0x9a, (byte)0x69, (byte)0x21, (byte)0x26 61 | , (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x2c, (byte)0x00, (byte)0x01, (byte)0x00, (byte)0x28, (byte)0xd7, (byte)0x47, (byte)0x62, (byte)0x53, (byte)0x88, (byte)0xc4, (byte)0x0d, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x7b, (byte)0x08, (byte)0x00, (byte)0x00, (byte)0xa0, (byte)0x82 62 | , (byte)0x07, (byte)0x7c, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00]; 63 | 64 | byte [] dcepack = [(byte)0x13,(byte)0x88,(byte)0x13,(byte)0x88,(byte)0x96,(byte)0x83,(byte)0x0e,(byte)0xe2,(byte)0xa4,(byte)0xed,(byte)0x62,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x11,(byte)0x57,(byte)0xd3,(byte)0x59,(byte)0x0a,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x32,(byte)0x02]; 65 | byte [] cookieEcho = [(byte)0x13,(byte)0x88,(byte)0x13,(byte)0x88,(byte)0xf5,(byte)0x36,(byte)0xd8,(byte)0x68,(byte)0x23,(byte)0xf0,(byte)0x16,(byte)0x11,(byte)0x0a,(byte)0x00 66 | ,(byte)0x00,(byte)0x24,(byte)0x59,(byte)0xfe,(byte)0xc8,(byte)0x5b,(byte)0xf0,(byte)0x2c,(byte)0x25,(byte)0xe6,(byte)0x97,(byte)0x23,(byte)0x33,(byte)0xa7,(byte)0x71,(byte)0x5e,(byte)0xb0,(byte)0x42,(byte)0x16,(byte)0x5f 67 | ,(byte)0xdd,(byte)0xa9,(byte)0x0a,(byte)0x5a,(byte)0xfa,(byte)0xa1,(byte)0x90,(byte)0xfe,(byte)0x0f,(byte)0x2b,(byte)0xd0,(byte)0x08,(byte)0x56,(byte)0xd4,(byte)0x00,(byte)0x03,(byte)0x00,(byte)0x20,(byte)0xad 68 | ,(byte)0xac,(byte)0x5b,(byte)0xe2,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x32,(byte)0x03,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x00 69 | ,(byte)0x00,(byte)0x04,(byte)0x00,(byte)0x00,(byte)0x63,(byte)0x68,(byte)0x61,(byte)0x74]; 70 | void setUp(){ 71 | Log.setLevel(Log.ALL); 72 | } 73 | 74 | class NonvalidatingPacket extends Packet{ 75 | NonvalidatingPacket(ByteBuffer pkt){ 76 | super(pkt); 77 | } 78 | void setChecksum(ByteBuffer pkt){} 79 | void checkChecksum(ByteBuffer pkt){ 80 | // some of the tests would produce invalid checksums - we don't care - for the moment. 81 | } 82 | } 83 | void testValidHeader(){ 84 | ByteBuffer b = ByteBuffer.wrap(sampleInit,0,12); 85 | Packet p = new NonvalidatingPacket(b); 86 | assertEquals("DestPort should be 5000", 5000, p.getDestPort()); 87 | assertEquals("SrcPort should be 5000", 5000, p.getSrcPort()); 88 | assertEquals("verTag should be 0", 0, p.getVerTag()); 89 | } 90 | void testShortHeader(){ 91 | ByteBuffer b = ByteBuffer.allocate(1); 92 | Exception x = null; 93 | try { 94 | Packet p = new NonvalidatingPacket(b); 95 | } catch (Exception e){ 96 | x = e; 97 | } 98 | assertEquals("Exception should be thrown ",false,(x==null)); 99 | assertEquals("Expecting exception SctpPacketException ",x.getClass(),SctpPacketFormatException.class); 100 | } 101 | 102 | void testRecycleHeader(){ 103 | ByteBuffer b = ByteBuffer.wrap(sampleInit,0,12); 104 | Packet p = new NonvalidatingPacket(b); 105 | Buffer bb = p.getByteBuffer(); 106 | assertEquals("Expecting same content in packets ",p.getHex(b),p.getHex(bb)); 107 | } 108 | 109 | void testChunks(){ 110 | ByteBuffer b = ByteBuffer.wrap(sampleInit); 111 | Packet p = new Packet(b); 112 | assertEquals("Expecting 1 chunk ",1,p.getChunkList().size()) 113 | } 114 | 115 | void testInitWrite(){ 116 | ByteBuffer b = ByteBuffer.wrap(sampleInit); 117 | Packet p = new Packet(b); 118 | List chunks = p.getChunkList(); 119 | assertEquals("Expecting 1 chunk ",1,chunks.size()); 120 | Chunk init = chunks.get(0); 121 | assertEquals("Expecting an init chunk",init.getType(),Chunk.INIT); 122 | ByteBuffer bout = ByteBuffer.allocate(sampleInit.length-12); 123 | init.write(bout); 124 | byte[]sin = new byte[sampleInit.length-12]; 125 | System.arraycopy(sampleInit,12,sin,0, sin.length); 126 | assertEquals("Expected to re-make same packet",Packet.getHex(sin),Packet.getHex(bout.array())); 127 | } 128 | 129 | void testAbort(){ 130 | ByteBuffer b = ByteBuffer.wrap(sampleAbort); 131 | Log.debug("Sample Abort is " + sampleAbort.length); 132 | 133 | Packet p = new Packet(b); 134 | List chunks = p.getChunkList(); 135 | assertEquals("Expecting 1 chunk ",1,chunks.size()); 136 | Chunk abort = chunks.get(0); 137 | assertEquals("Expecting an abort chunk",abort.getType(),Chunk.ABORT); 138 | } 139 | 140 | void testAbort2(){ 141 | ByteBuffer b = ByteBuffer.wrap(sampleAbort2); 142 | Log.debug("Sample Abort is " + sampleAbort2.length); 143 | 144 | Packet p = new Packet(b); 145 | List chunks = p.getChunkList(); 146 | assertEquals("Expecting 1 chunk ",1,chunks.size()); 147 | Chunk abort = chunks.get(0); 148 | assertEquals("Expecting an abort chunk",abort.getType(),Chunk.ABORT); 149 | } 150 | 151 | void testCookieEcho(){ 152 | ByteBuffer b = ByteBuffer.wrap(cookieEcho); 153 | Packet p = new Packet(b); 154 | List chunks = p.getChunkList(); 155 | assertEquals("Expecting 2 chunks ",2,chunks.size()); 156 | Chunk ic = chunks.get(0); 157 | assertEquals("Expecting a cookie echo chunk",ic.getType(),Chunk.COOKIE_ECHO); 158 | Chunk dopen = chunks.get(1); 159 | assertEquals("Expecting a data chunk",dopen.getType(),Chunk.DATA); 160 | 161 | } 162 | void testInitAck(){ 163 | ByteBuffer b = ByteBuffer.wrap(sampleInit); 164 | Packet p = new Packet(b); 165 | List chunks = p.getChunkList(); 166 | assertEquals("Expecting 1 chunk ",1,chunks.size()); 167 | Chunk ic = chunks.get(0); 168 | assertEquals("Expecting an init chunk",ic.getType(),Chunk.INIT); 169 | 170 | Association ass = new MockAssociation(null,null); 171 | Chunk [] ca = ass.inboundInit(ic); 172 | assertEquals("Expecting a single reply chunk",1,ca.length); 173 | ByteBuffer iacbb = ass.mkPkt(ca); 174 | Packet iac = new Packet(iacbb); 175 | chunks = iac.getChunkList(); 176 | assertEquals("Expecting 1 chunk ",1,chunks.size()); 177 | ic = chunks.get(0); 178 | assertEquals("Expecting an InitAck chunk",ic.getType(),Chunk.INITACK); 179 | 180 | } 181 | 182 | 183 | void testOpenDataChunk(){ 184 | ByteBuffer b = ByteBuffer.wrap(sampleData); 185 | Packet p = new Packet(b); 186 | List chunks = p.getChunkList(); 187 | assertEquals("Expecting 1 chunk ",1,chunks.size()); 188 | DataChunk dat = chunks.get(0); 189 | assertEquals("Expecting a Data chunk",dat.getType(),Chunk.DATA); 190 | Log.debug("got "+dat.getClass().getName()+ " chunk" + dat.toString()); 191 | assertEquals("Expecting seqno of zero",dat.getSSeqNo(),0); 192 | assertEquals("Expecting stream of zero",dat.getStreamId(),0); 193 | assertEquals("Expecting an DCEP",dat.getPpid(),50); 194 | assertEquals("Data should be zero",dat.getData(),null); 195 | assertEquals("Expected to parse a DCEP packet",dat.getDCEP()!=null,true); 196 | assertEquals("Expected an open DCEP packet ",dat.getDCEP().isAck(),false); 197 | 198 | 199 | } 200 | 201 | void testHeartBeatChunk(){ 202 | ByteBuffer b = ByteBuffer.wrap(sampleHeartBeat); 203 | Packet p = new Packet(b); 204 | List chunks = p.getChunkList(); 205 | assertEquals("Expecting 1 chunk ",1,chunks.size()); 206 | HeartBeatChunk dat = chunks.get(0); 207 | assertEquals("Expecting a HeartBeatChunk",dat.getType(),Chunk.HEARTBEAT); 208 | Chunk[] r = dat.mkReply(); 209 | assertEquals("Expecting 1 chunk ",1,r.length); 210 | assertEquals("Expecting a HeartBeatAckChunk",r[0].getType(),Chunk.HEARTBEAT_ACK); 211 | } 212 | 213 | void testDCEPAckChunk(){ 214 | ByteBuffer b = ByteBuffer.wrap(dcepack); 215 | Packet p = new Packet(b); 216 | List chunks = p.getChunkList(); 217 | assertEquals("Expecting 1 chunk ",1,chunks.size()); 218 | DataChunk dat = chunks.get(0); 219 | assertEquals("Expecting a DataChunk",dat.getType(),Chunk.DATA); 220 | } 221 | 222 | } 223 | 224 | -------------------------------------------------------------------------------- /test/main/groovy/com/ipseorama/sctp/TestAssociation.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Westhawk Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package com.ipseorama.sctp 20 | 21 | import com.ipseorama.sctp.Association 22 | import com.ipseorama.sctp.AssociationListener 23 | import com.ipseorama.sctp.messages.DataChunk 24 | import com.ipseorama.sctp.messages.Packet 25 | import com.ipseorama.sctp.messages.SackChunk 26 | import com.ipseorama.sctp.messages.exceptions.SctpPacketFormatException; 27 | import com.ipseorama.base.dataChannel.DECP.DCOpen; 28 | import java.nio.Buffer 29 | import java.nio.ByteBuffer; 30 | import java.security.SecureRandom 31 | import org.bouncycastle.crypto.tls.DTLSTransport 32 | import org.bouncycastle.crypto.tls.DatagramTransport 33 | 34 | import com.phono.srtplight.Log; 35 | /** 36 | * 37 | * @author Westhawk Ltd 38 | */ 39 | class TestAssociation extends GroovyTestCase { 40 | 41 | byte [] sampleDataOpen = [(byte)0x13, (byte)0x88, (byte)0x13, (byte)0x88, (byte)0x13, (byte)0xfc, (byte)0x3a, (byte)0x88, (byte)0x8d, (byte)0xa9 42 | , (byte)0xa7, (byte)0x20, (byte)0x00, (byte)0x03, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x03, (byte)0x00 43 | , (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x04, (byte)0x00, (byte)0x00, (byte)0x63, (byte)0x68, (byte)0x61, (byte)0x74]; 44 | 45 | void setUp(){ 46 | Log.setLevel(Log.ALL); 47 | } 48 | 49 | 50 | void testAss(){ 51 | ByteBuffer sent; 52 | DatagramTransport mock = new DatagramTransport(){ 53 | 54 | public int getReceiveLimit() throws IOException{ 55 | return 1200; 56 | } 57 | 58 | public int getSendLimit() throws IOException{ 59 | return 1200; 60 | } 61 | 62 | public int receive(byte[] bytes, int i, int i1, int i2) throws IOException{ 63 | Thread.sleep(i2); 64 | return 0; 65 | } 66 | 67 | public void send(byte[] ou, int off, int pos) throws IOException{ 68 | byte [] bsent = new byte[pos-off]; 69 | java.lang.System.arraycopy(ou,off,bsent,(int)0,bsent.length); 70 | sent = ByteBuffer.wrap(bsent) 71 | } 72 | 73 | public void close() throws IOException{ 74 | 75 | } 76 | }; 77 | SCTPStream stream ; 78 | AssociationListener al = new AssociationListener(){ 79 | 80 | public void onAssociated(Association a){ 81 | Log.debug("Associated"); 82 | 83 | } 84 | public void onDisAssociated(Association a){ 85 | Log.debug("Disssociated"); 86 | 87 | } 88 | public void onStream(SCTPStream s){ 89 | Log.debug("Association opened stream "); 90 | stream = s; 91 | } 92 | }; 93 | MockAssociation ass = new MockAssociation(mock,al); 94 | ass.setMyVerTag(335297160); 95 | ByteBuffer b = ByteBuffer.wrap(sampleDataOpen); 96 | Packet p = new Packet(b); 97 | ass.deal(p); 98 | 99 | Packet ack = new Packet(sent); 100 | List chunks = ack.getChunkList(); 101 | assertEquals("Expecting 1 chunk ",1,chunks.size()); 102 | DataChunk dat = chunks.get(0); 103 | assertEquals("Expecting a Data chunk",dat.getType(),DataChunk.DATA); 104 | Log.debug("got "+dat.getClass().getName()+ " chunk" + dat.toString()); 105 | assertEquals("Expecting seqno of zero",dat.getSSeqNo(),0); 106 | assertEquals("Expecting stream of zero",dat.getStreamId(),0); 107 | assertEquals("Expecting an DCEP",dat.getPpid(),50); 108 | assertEquals("Data should be zero",dat.getData(),null); 109 | assertEquals("Expected to parse a DCEP packet",dat.getDCEP()!=null,true); 110 | assertEquals("Expected an ack DCEP packet ",dat.getDCEP().isAck(),true); 111 | 112 | assertEquals("expecting a stream",(stream == null), false); 113 | stream.send("hello"); 114 | // ugh - uses a side effect on the sent buffer, which we capture. 115 | Packet pack = new Packet(sent); 116 | chunks = pack.getChunkList(); 117 | assertEquals("Expecting 1 chunk ",1,chunks.size()); 118 | dat = chunks.get(0); 119 | assertEquals("Expecting a Data chunk",dat.getType(),DataChunk.DATA); 120 | Log.debug("got "+dat.getClass().getName()+ " chunk" + dat.toString()); 121 | assertEquals("Expecting seqno of one",dat.getSSeqNo(),1); // we've done a DCEP ack by now. 122 | assertEquals("Expecting stream of zero",dat.getStreamId(),0); 123 | assertEquals("Expecting hello in the data",dat.getDataAsString(),"hello"); 124 | 125 | } 126 | } 127 | 128 | -------------------------------------------------------------------------------- /test/main/groovy/com/ipseorama/sctp/TestMessage.groovy: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Westhawk Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package com.ipseorama.sctp 20 | 21 | import com.ipseorama.sctp.Association 22 | import com.ipseorama.sctp.AssociationListener 23 | import com.ipseorama.sctp.messages.DataChunk 24 | import com.ipseorama.sctp.messages.Packet 25 | import com.ipseorama.sctp.messages.SackChunk 26 | import com.ipseorama.sctp.messages.exceptions.SctpPacketFormatException; 27 | import com.ipseorama.base.dataChannel.DECP.DCOpen; 28 | import java.nio.Buffer 29 | import java.nio.ByteBuffer; 30 | import java.security.SecureRandom 31 | import org.bouncycastle.crypto.tls.DTLSTransport 32 | import org.bouncycastle.crypto.tls.DatagramTransport 33 | 34 | import com.phono.srtplight.Log; 35 | /** 36 | * 37 | * @author Westhawk Ltd 38 | */ 39 | class TestMessage extends GroovyTestCase { 40 | def data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; 41 | Association a; 42 | SCTPStream stream; 43 | def id; 44 | 45 | void setUp(){ 46 | Log.setLevel(Log.ALL); 47 | a = null; 48 | id = 1000; 49 | stream = new SCTPStream(a, id){ 50 | public void send(String message){ 51 | } 52 | }; 53 | } 54 | 55 | void testMessFillSingle(){ 56 | // assume capacity > 52 57 | SCTPMessage m = new SCTPMessage(data,stream); 58 | def chunks = []; 59 | while (m.hasMoreData()) { 60 | DataChunk dc = new DataChunk() 61 | m.fill(dc); 62 | chunks += dc; 63 | } 64 | Log.debug("chunks "+chunks.size); 65 | assertEquals ("Wrong number of chunks",chunks.size,1); 66 | 67 | assertEquals ("First (and only) chunk should have single flag set",chunks[0].getFlags(),DataChunk.SINGLEFLAG); 68 | 69 | } 70 | 71 | void testMessFill1(){ 72 | 73 | SCTPMessage m = new SCTPMessage(data,stream); 74 | def chunks = []; 75 | while (m.hasMoreData()) { 76 | DataChunk dc = new DataChunk(){ 77 | public int getCapacity() { 78 | return 1; // shrug 79 | } 80 | }; 81 | m.fill(dc); 82 | chunks += dc; 83 | } 84 | Log.debug("chunks "+chunks.size); 85 | assertEquals ("Wrong number of chunks",chunks.size,data.length()); 86 | 87 | assertEquals ("Start chunk should have start flag set",chunks[0].getFlags(),DataChunk.BEGINFLAG); 88 | assertEquals ("End chunk should have end flag set",chunks[data.length()-1].getFlags(),DataChunk.ENDFLAG); 89 | for (int i = 1; i< data.length()-1;i++){ 90 | assertEquals ("middle chunk should have no flag set",chunks[i].getFlags(),0); 91 | assertEquals ("middle data should match input",chunks[i].getDataAsString(),data[i]); 92 | } 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /test/main/java/com/ipseorama/sctp/MockAssociation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2014 Westhawk Ltd 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | package com.ipseorama.sctp; 19 | 20 | import com.ipseorama.sctp.messages.Chunk; 21 | import com.ipseorama.sctp.messages.DataChunk; 22 | import com.ipseorama.sctp.messages.InitChunk; 23 | import com.ipseorama.sctp.messages.SackChunk; 24 | import com.ipseorama.sctp.small.BlockingSCTPStream; 25 | import org.bouncycastle.crypto.tls.DatagramTransport; 26 | 27 | /** 28 | * 29 | * @author Westhawk Ltd 30 | */ 31 | public class MockAssociation extends Association { 32 | 33 | public MockAssociation(DatagramTransport transport, AssociationListener al) { 34 | super(transport, al); 35 | } 36 | 37 | 38 | 39 | @Override 40 | public void enqueue(DataChunk d) { 41 | throw new UnsupportedOperationException("Not supported yet. (enqueue)"); //To change body of generated methods, choose Tools | Templates. 42 | } 43 | 44 | @Override 45 | public SCTPStream mkStream(int id) { 46 | return new SCTPStream(this, id) { 47 | 48 | @Override 49 | public void send(String message) throws Exception { 50 | SCTPMessage m = new SCTPMessage(message, this); 51 | _ass.sendAndBlock(m); 52 | } 53 | 54 | }; 55 | } 56 | 57 | @Override 58 | public void sendAndBlock(SCTPMessage m) throws Exception { 59 | Chunk[] dar = new Chunk[1]; 60 | 61 | DataChunk dc = new DataChunk(); 62 | m.fill(dc); 63 | dc.setTsn(_nearTSN++); 64 | // check rollover - will break at maxint. 65 | dar[0] = dc; 66 | send(dar); 67 | 68 | } 69 | 70 | @Override 71 | public SCTPMessage makeMessage(byte[] bytes, BlockingSCTPStream aThis) { 72 | throw new UnsupportedOperationException("Not supported yet. (makeMessage)"); //To change body of generated methods, choose Tools | Templates. 73 | } 74 | 75 | @Override 76 | public Chunk[] inboundInit(InitChunk i) { 77 | return super.inboundInit(i); 78 | } 79 | 80 | public void setMyVerTag(int v) { 81 | super._myVerTag = v; 82 | } 83 | 84 | @Override 85 | public SCTPMessage makeMessage(String s, BlockingSCTPStream aThis) { 86 | throw new UnsupportedOperationException("Not supported yet.(Make Message - string"); //To change body of generated methods, choose Tools | Templates. 87 | } 88 | 89 | @Override 90 | protected Chunk[] sackDeal(SackChunk sackChunk) { 91 | throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. 92 | } 93 | 94 | 95 | } 96 | --------------------------------------------------------------------------------