├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── extra └── crc32c │ └── crc32c.h ├── gtests ├── Association.cpp ├── CMakeFiles │ ├── CMakeDirectoryInformation.cmake │ ├── gtests.dir │ │ ├── CXX.includecache │ │ ├── DependInfo.cmake │ │ ├── build.make │ │ ├── cmake_clean.cmake │ │ ├── depend.internal │ │ ├── depend.make │ │ ├── flags.make │ │ ├── link.txt │ │ └── progress.make │ └── progress.marks ├── CMakeLists.txt ├── Chunks.cpp ├── FakeTimeService.h ├── Makefile ├── SequenceNumberWrapper.cpp └── cmake_install.cmake ├── include └── Datachannels.h ├── specs.md └── src ├── CMakeLists.txt ├── Datachannel.cpp ├── Datachannel.h ├── Datachannels.cpp ├── Endpoint.cpp ├── Endpoint.h ├── internal ├── Buffer.h ├── BufferReader.h └── BufferWritter.h └── sctp ├── Association.cpp ├── Association.h ├── CMakeLists.txt ├── Chunk.cpp ├── Chunk.h ├── ErrorCause.h ├── PacketHeader.cpp ├── PacketHeader.h ├── SequenceNumberWrapper.h ├── Stream.cpp ├── Stream.h └── chunks ├── AbortAssociationChunk.cpp ├── AbortAssociationChunk.h ├── CMakeLists.txt ├── CookieAckChunk.cpp ├── CookieAckChunk.h ├── CookieEchoChunk.cpp ├── CookieEchoChunk.h ├── ForwardCumulativeTSNChunk.cpp ├── ForwardCumulativeTSNChunk.h ├── HeartbeatAckChunk.cpp ├── HeartbeatAckChunk.h ├── HeartbeatRequestChunk.cpp ├── HeartbeatRequestChunk.h ├── InitiationAcknowledgementChunk.cpp ├── InitiationAcknowledgementChunk.h ├── InitiationChunk.cpp ├── InitiationChunk.h ├── OperationErrorChunk.cpp ├── OperationErrorChunk.h ├── PaddingChunk.cpp ├── PaddingChunk.h ├── PayloadDataChunk.cpp ├── PayloadDataChunk.h ├── ReConfigChunk.cpp ├── ReConfigChunk.h ├── SelectiveAcknowledgementChunk.cpp ├── SelectiveAcknowledgementChunk.h ├── ShutdownAcknowledgementChunk.cpp ├── ShutdownAcknowledgementChunk.h ├── ShutdownAssociationChunk.cpp ├── ShutdownAssociationChunk.h ├── ShutdownCompleteChunk.cpp ├── ShutdownCompleteChunk.h ├── UnknownChunk.cpp └── UnknownChunk.h /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject 2 | /build 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8.11) 2 | project (datachannels) 3 | 4 | set(CMAKE_CXX_STANDARD 17) 5 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 6 | set(CMAKE_CXX_EXTENSIONS OFF) 7 | 8 | 9 | find_package(Crc32c REQUIRED) 10 | 11 | INCLUDE(CheckCXXSourceCompiles) 12 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17") 13 | check_cxx_source_compiles(" 14 | #include 15 | int main() { int* p = static_cast(std::aligned_alloc(1024, 1024*sizeof *p)); std::free(p); return 0; } 16 | " 17 | DHAVE_STD_ALIGNED_ALLOC) 18 | if(DHAVE_STD_ALIGNED_ALLOC) 19 | add_compile_options("-DHAVE_STD_ALIGNED_ALLOC") 20 | endif(DHAVE_STD_ALIGNED_ALLOC) 21 | 22 | add_library(libdatachannels "") 23 | set_property(TARGET libdatachannels PROPERTY CXX_STANDARD 17) 24 | target_include_directories (libdatachannels PUBLIC ${PROJECT_SOURCE_DIR}/include) 25 | 26 | add_subdirectory (src) 27 | add_subdirectory (gtests) 28 | 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libdatachannels 2 | Lean and mean WebRTC datachanneles C++ library with ad-hoc SCTP stack 3 | -------------------------------------------------------------------------------- /extra/crc32c/crc32c.h: -------------------------------------------------------------------------------- 1 | #ifndef DATACHANNELS_EXTRA_CRC32C_H 2 | #define DATACHANNELS_EXTRA_CRC32C_H 3 | 4 | namespace crc32c { 5 | 6 | /*************************************************************/ 7 | /* Note Definition for Ross Williams table generator would */ 8 | /* be: TB_WIDTH=4, TB_POLLY=0x1EDC6F41, TB_REVER=TRUE */ 9 | /* For Mr. Williams direct calculation code use the settings */ 10 | /* cm_width=32, cm_poly=0x1EDC6F41, cm_init=0xFFFFFFFF, */ 11 | /* cm_refin=TRUE, cm_refot=TRUE, cm_xorort=0x00000000 */ 12 | /*************************************************************/ 13 | #define DC_CRC32C(c,d) (c=(c>>8)^dc_crc_c[(c^(d))&0xFF]) 14 | 15 | static uint32_t dc_crc_c[256] = 16 | { 17 | 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, 18 | 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, 19 | 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, 20 | 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, 21 | 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, 22 | 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, 23 | 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, 24 | 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, 25 | 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, 26 | 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, 27 | 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, 28 | 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, 29 | 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, 30 | 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, 31 | 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, 32 | 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, 33 | 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, 34 | 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, 35 | 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, 36 | 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, 37 | 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, 38 | 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, 39 | 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, 40 | 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, 41 | 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, 42 | 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, 43 | 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, 44 | 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, 45 | 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, 46 | 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, 47 | 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, 48 | 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, 49 | 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, 50 | 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, 51 | 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, 52 | 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, 53 | 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, 54 | 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, 55 | 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, 56 | 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, 57 | 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, 58 | 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, 59 | 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, 60 | 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, 61 | 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, 62 | 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, 63 | 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, 64 | 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, 65 | 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, 66 | 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, 67 | 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, 68 | 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, 69 | 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, 70 | 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, 71 | 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, 72 | 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, 73 | 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, 74 | 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, 75 | 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, 76 | 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, 77 | 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, 78 | 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, 79 | 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, 80 | 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L, 81 | }; 82 | 83 | uint32_t Crc32c(const uint8_t* data, size_t length) { 84 | { 85 | uint32_t crc32 = ~0L; 86 | 87 | for (size_t i = 0; i < length; i++) 88 | DC_CRC32C(crc32, buffer[i]); 89 | 90 | return ~crc32; 91 | } 92 | 93 | } 94 | 95 | 96 | #endif /* CRC32C_H */ 97 | 98 | -------------------------------------------------------------------------------- /gtests/Association.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: Association 3 | * Author: Sergio 4 | * 5 | * Created on 30-ene-2019, 17:47:50 6 | */ 7 | 8 | #include 9 | 10 | #include "Buffer.h" 11 | #include "FakeTimeService.h" 12 | #include "sctp/Association.h" 13 | 14 | class Association : public testing::Test 15 | { 16 | protected: 17 | }; 18 | 19 | 20 | TEST_F(Association, Init) 21 | { 22 | FakeTimeService timeService; 23 | sctp::Association association(timeService); 24 | 25 | association.SetLocalPort(1000); 26 | association.SetRemotePort(2000); 27 | 28 | ASSERT_EQ(association.GetState(),sctp::Association::Closed); 29 | ASSERT_EQ(association.HasPendingData(),false); 30 | } 31 | 32 | TEST_F(Association, EmptyRead) 33 | { 34 | Buffer buffer(1500); 35 | FakeTimeService timeService; 36 | sctp::Association association(timeService); 37 | ASSERT_EQ(association.HasPendingData(),false); 38 | ASSERT_FALSE(association.ReadPacket(buffer)); 39 | } 40 | 41 | TEST_F(Association, Associate) 42 | { 43 | FakeTimeService timeService; 44 | sctp::Association association(timeService); 45 | 46 | association.SetLocalPort(1000); 47 | association.SetRemotePort(2000); 48 | 49 | //Start association 50 | association.Associate(); 51 | 52 | //Init should be rtx three times 53 | size_t rtx = 0; 54 | //Ensure retransmissions 55 | while (rtx++<=sctp::Association::MaxInitRetransmits) 56 | { 57 | ASSERT_EQ(association.GetState(),sctp::Association::CookieWait); 58 | ASSERT_EQ(association.HasPendingData(),true); 59 | 60 | Buffer buffer(1500); 61 | //Read data 62 | size_t len = association.ReadPacket(buffer); 63 | //Ensure size 64 | ASSERT_TRUE(len); 65 | 66 | BufferReader reader(buffer); 67 | 68 | //Parse header 69 | auto header = sctp::PacketHeader::Parse(reader); 70 | 71 | //Ensure everything is correct 72 | ASSERT_EQ(header->sourcePortNumber , association.GetLocalPort()); 73 | ASSERT_EQ(header->destinationPortNumber , association.GetRemotePort()); 74 | ASSERT_EQ(header->verificationTag , 0); 75 | ASSERT_TRUE(header->checksum); 76 | 77 | //Read chunk 78 | auto chunk = sctp::Chunk::Parse(reader); 79 | ASSERT_TRUE(chunk); 80 | ASSERT_EQ(chunk->type,sctp::Chunk::INIT); 81 | 82 | //Ensure state 83 | ASSERT_EQ(association.GetState(),sctp::Association::CookieWait); 84 | ASSERT_EQ(association.HasPendingData(),false); 85 | 86 | //Launch next retransmission 87 | timeService.SetNow(timeService.GetNow() + sctp::Association::InitRetransmitTimeout); 88 | } 89 | //Ensure state 90 | ASSERT_EQ(association.GetState(),sctp::Association::Closed); 91 | ASSERT_EQ(association.HasPendingData(),false); 92 | } 93 | 94 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/CMakeDirectoryInformation.cmake: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "Unix Makefiles" Generator, CMake Version 3.9 3 | 4 | # Relative path conversion top directories. 5 | set(CMAKE_RELATIVE_PATH_TOP_SOURCE "/usr/local/src/libdatachannels") 6 | set(CMAKE_RELATIVE_PATH_TOP_BINARY "/usr/local/src/libdatachannels") 7 | 8 | # Force unix paths in dependencies. 9 | set(CMAKE_FORCE_UNIX_PATHS 1) 10 | 11 | 12 | # The C and CXX include file regular expressions for this directory. 13 | set(CMAKE_C_INCLUDE_REGEX_SCAN "^.*$") 14 | set(CMAKE_C_INCLUDE_REGEX_COMPLAIN "^$") 15 | set(CMAKE_CXX_INCLUDE_REGEX_SCAN ${CMAKE_C_INCLUDE_REGEX_SCAN}) 16 | set(CMAKE_CXX_INCLUDE_REGEX_COMPLAIN ${CMAKE_C_INCLUDE_REGEX_COMPLAIN}) 17 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/gtests.dir/CXX.includecache: -------------------------------------------------------------------------------- 1 | #IncludeRegexLine: ^[ ]*[#%][ ]*(include|import)[ ]*[<"]([^">]+)([">]) 2 | 3 | #IncludeRegexScan: ^.*$ 4 | 5 | #IncludeRegexComplain: ^$ 6 | 7 | #IncludeRegexTransform: 8 | 9 | /usr/local/src/libdatachannels/gtests/TestSuite.cpp 10 | gtest/gtest.h 11 | - 12 | 13 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/gtests.dir/DependInfo.cmake: -------------------------------------------------------------------------------- 1 | # The set of languages for which implicit dependencies are needed: 2 | set(CMAKE_DEPENDS_LANGUAGES 3 | "CXX" 4 | ) 5 | # The set of files for implicit dependencies of each language: 6 | set(CMAKE_DEPENDS_CHECK_CXX 7 | "/usr/local/src/libdatachannels/gtests/TestSuite.cpp" "/usr/local/src/libdatachannels/gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o" 8 | ) 9 | set(CMAKE_CXX_COMPILER_ID "GNU") 10 | 11 | # The include file search paths: 12 | set(CMAKE_CXX_TARGET_INCLUDE_PATH 13 | "src/../include" 14 | "src/internal" 15 | "src" 16 | ) 17 | 18 | # Targets to which this target links. 19 | set(CMAKE_TARGET_LINKED_INFO_FILES 20 | "/usr/local/src/libdatachannels/src/CMakeFiles/libdatachannels.dir/DependInfo.cmake" 21 | ) 22 | 23 | # Fortran module output directory. 24 | set(CMAKE_Fortran_TARGET_MODULE_DIR "") 25 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/gtests.dir/build.make: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "Unix Makefiles" Generator, CMake Version 3.9 3 | 4 | # Delete rule output on recipe failure. 5 | .DELETE_ON_ERROR: 6 | 7 | 8 | #============================================================================= 9 | # Special targets provided by cmake. 10 | 11 | # Disable implicit rules so canonical targets will work. 12 | .SUFFIXES: 13 | 14 | 15 | # Remove some rules from gmake that .SUFFIXES does not remove. 16 | SUFFIXES = 17 | 18 | .SUFFIXES: .hpux_make_needs_suffix_list 19 | 20 | 21 | # Suppress display of executed commands. 22 | $(VERBOSE).SILENT: 23 | 24 | 25 | # A target that is always out of date. 26 | cmake_force: 27 | 28 | .PHONY : cmake_force 29 | 30 | #============================================================================= 31 | # Set environment variables for the build. 32 | 33 | # The shell in which to execute make rules. 34 | SHELL = /bin/sh 35 | 36 | # The CMake executable. 37 | CMAKE_COMMAND = /usr/bin/cmake 38 | 39 | # The command to remove a file. 40 | RM = /usr/bin/cmake -E remove -f 41 | 42 | # Escaping for special characters. 43 | EQUALS = = 44 | 45 | # The top-level source directory on which CMake was run. 46 | CMAKE_SOURCE_DIR = /usr/local/src/libdatachannels 47 | 48 | # The top-level build directory on which CMake was run. 49 | CMAKE_BINARY_DIR = /usr/local/src/libdatachannels 50 | 51 | # Include any dependencies generated for this target. 52 | include gtests/CMakeFiles/gtests.dir/depend.make 53 | 54 | # Include the progress variables for this target. 55 | include gtests/CMakeFiles/gtests.dir/progress.make 56 | 57 | # Include the compile flags for this target's objects. 58 | include gtests/CMakeFiles/gtests.dir/flags.make 59 | 60 | gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o: gtests/CMakeFiles/gtests.dir/flags.make 61 | gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o: gtests/TestSuite.cpp 62 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --progress-dir=/usr/local/src/libdatachannels/CMakeFiles --progress-num=$(CMAKE_PROGRESS_1) "Building CXX object gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o" 63 | cd /usr/local/src/libdatachannels/gtests && /usr/bin/g++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -o CMakeFiles/gtests.dir/TestSuite.cpp.o -c /usr/local/src/libdatachannels/gtests/TestSuite.cpp 64 | 65 | gtests/CMakeFiles/gtests.dir/TestSuite.cpp.i: cmake_force 66 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Preprocessing CXX source to CMakeFiles/gtests.dir/TestSuite.cpp.i" 67 | cd /usr/local/src/libdatachannels/gtests && /usr/bin/g++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -E /usr/local/src/libdatachannels/gtests/TestSuite.cpp > CMakeFiles/gtests.dir/TestSuite.cpp.i 68 | 69 | gtests/CMakeFiles/gtests.dir/TestSuite.cpp.s: cmake_force 70 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green "Compiling CXX source to assembly CMakeFiles/gtests.dir/TestSuite.cpp.s" 71 | cd /usr/local/src/libdatachannels/gtests && /usr/bin/g++ $(CXX_DEFINES) $(CXX_INCLUDES) $(CXX_FLAGS) -S /usr/local/src/libdatachannels/gtests/TestSuite.cpp -o CMakeFiles/gtests.dir/TestSuite.cpp.s 72 | 73 | gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o.requires: 74 | 75 | .PHONY : gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o.requires 76 | 77 | gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o.provides: gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o.requires 78 | $(MAKE) -f gtests/CMakeFiles/gtests.dir/build.make gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o.provides.build 79 | .PHONY : gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o.provides 80 | 81 | gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o.provides.build: gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o 82 | 83 | 84 | # Object files for target gtests 85 | gtests_OBJECTS = \ 86 | "CMakeFiles/gtests.dir/TestSuite.cpp.o" 87 | 88 | # External object files for target gtests 89 | gtests_EXTERNAL_OBJECTS = 90 | 91 | gtests/gtests: gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o 92 | gtests/gtests: gtests/CMakeFiles/gtests.dir/build.make 93 | gtests/gtests: src/liblibdatachannels.a 94 | gtests/gtests: gtests/CMakeFiles/gtests.dir/link.txt 95 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/usr/local/src/libdatachannels/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Linking CXX executable gtests" 96 | cd /usr/local/src/libdatachannels/gtests && $(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/gtests.dir/link.txt --verbose=$(VERBOSE) 97 | 98 | # Rule to build all files generated by this target. 99 | gtests/CMakeFiles/gtests.dir/build: gtests/gtests 100 | 101 | .PHONY : gtests/CMakeFiles/gtests.dir/build 102 | 103 | gtests/CMakeFiles/gtests.dir/requires: gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o.requires 104 | 105 | .PHONY : gtests/CMakeFiles/gtests.dir/requires 106 | 107 | gtests/CMakeFiles/gtests.dir/clean: 108 | cd /usr/local/src/libdatachannels/gtests && $(CMAKE_COMMAND) -P CMakeFiles/gtests.dir/cmake_clean.cmake 109 | .PHONY : gtests/CMakeFiles/gtests.dir/clean 110 | 111 | gtests/CMakeFiles/gtests.dir/depend: 112 | cd /usr/local/src/libdatachannels && $(CMAKE_COMMAND) -E cmake_depends "Unix Makefiles" /usr/local/src/libdatachannels /usr/local/src/libdatachannels/gtests /usr/local/src/libdatachannels /usr/local/src/libdatachannels/gtests /usr/local/src/libdatachannels/gtests/CMakeFiles/gtests.dir/DependInfo.cmake --color=$(COLOR) 113 | .PHONY : gtests/CMakeFiles/gtests.dir/depend 114 | 115 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/gtests.dir/cmake_clean.cmake: -------------------------------------------------------------------------------- 1 | file(REMOVE_RECURSE 2 | "CMakeFiles/gtests.dir/TestSuite.cpp.o" 3 | "gtests.pdb" 4 | "gtests" 5 | ) 6 | 7 | # Per-language clean rules from dependency scanning. 8 | foreach(lang CXX) 9 | include(CMakeFiles/gtests.dir/cmake_clean_${lang}.cmake OPTIONAL) 10 | endforeach() 11 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/gtests.dir/depend.internal: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "Unix Makefiles" Generator, CMake Version 3.9 3 | 4 | gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o 5 | /usr/local/src/libdatachannels/gtests/TestSuite.cpp 6 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/gtests.dir/depend.make: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "Unix Makefiles" Generator, CMake Version 3.9 3 | 4 | gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o: gtests/TestSuite.cpp 5 | 6 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/gtests.dir/flags.make: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "Unix Makefiles" Generator, CMake Version 3.9 3 | 4 | # compile CXX with /usr/bin/g++ 5 | CXX_FLAGS = -g3 -gdwarf-2 6 | 7 | CXX_DEFINES = 8 | 9 | CXX_INCLUDES = -I/usr/local/src/libdatachannels/src/../include -I/usr/local/src/libdatachannels/src/internal -I/usr/local/src/libdatachannels/src 10 | 11 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/gtests.dir/link.txt: -------------------------------------------------------------------------------- 1 | /usr/bin/g++ -g3 -gdwarf-2 -rdynamic CMakeFiles/gtests.dir/TestSuite.cpp.o -o gtests ../src/liblibdatachannels.a 2 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/gtests.dir/progress.make: -------------------------------------------------------------------------------- 1 | CMAKE_PROGRESS_1 = 1 2 | CMAKE_PROGRESS_2 = 2 3 | 4 | -------------------------------------------------------------------------------- /gtests/CMakeFiles/progress.marks: -------------------------------------------------------------------------------- 1 | 5 2 | -------------------------------------------------------------------------------- /gtests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(GTest REQUIRED) 2 | find_package(Threads REQUIRED) 3 | find_library(Crc32c REQUIRED) 4 | add_executable (gtests Chunks.cpp Association.cpp SequenceNumberWrapper.cpp) 5 | target_link_libraries(gtests libdatachannels) 6 | target_link_libraries(gtests gtest gtest_main) 7 | target_link_libraries(gtests Threads::Threads) 8 | target_link_libraries(gtests Crc32c::crc32c) 9 | gtest_discover_tests(gtests) 10 | -------------------------------------------------------------------------------- /gtests/Chunks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: Chunks 3 | * Author: Sergio 4 | * 5 | * Created on 26-ene-2019, 22:25:25 6 | */ 7 | 8 | #include 9 | 10 | #include "BufferReader.h" 11 | #include "sctp/PacketHeader.h" 12 | #include "sctp/Chunk.h" 13 | 14 | class Chunks : public testing::Test 15 | { 16 | protected: 17 | }; 18 | 19 | TEST_F(Chunks, ParseInit) 20 | { 21 | uint8_t data[100] = { 22 | 0x13, 0x88, 0x13, 0x88, 23 | 0x00, 0x00, 0x00, 0x00, 24 | 0xfc, 0x36, 0x40, 0x87, 25 | 0x01, 0x00, 0x00, 0x56, 26 | 0xd2, 0xc0, 0x87, 0x0e, 27 | 0x00, 0x02, 0x00, 0x00, 28 | 0x04, 0x00, 0x08, 0x00, 29 | 0x18, 0xc1, 0x16, 0x32, 30 | 0xc0, 0x00, 0x00, 0x04, 31 | 0x80, 0x08, 0x00, 0x09, 32 | 0xc0, 0x0f, 0xc1, 0x80, 33 | 0x82, 0x00, 0x00, 0x00, 34 | 0x80, 0x02, 0x00, 0x24, 35 | 0x83, 0x52, 0x00, 0x00, 36 | 0xe3, 0x60, 0x00, 0x00, 37 | 0xf6, 0x1a, 0x00, 0x00, 38 | 0xd4, 0x07, 0x00, 0x00, 39 | 0xe4, 0x4f, 0x00, 0x00, 40 | 0xe6, 0x6b, 0x00, 0x00, 41 | 0xbd, 0x1a, 0x00, 0x00, 42 | 0x52, 0x27, 0x00, 0x00, 43 | 0x80, 0x04, 0x00, 0x06, 44 | 0x00, 0x01, 0x00, 0x00, 45 | 0x80, 0x03, 0x00, 0x06, 46 | 0x80, 0xc1, 0x00, 0x00 47 | }; 48 | 49 | BufferReader reader(data,sizeof(data)); 50 | 51 | auto header = sctp::PacketHeader::Parse(reader); 52 | ASSERT_TRUE(header); 53 | auto chunk = sctp::Chunk::Parse(reader); 54 | ASSERT_TRUE(chunk); 55 | ASSERT_EQ(chunk->type,sctp::Chunk::INIT); 56 | ASSERT_FALSE(reader.GetLeft()); 57 | 58 | } 59 | 60 | TEST_F(Chunks, SerializeInit) 61 | { 62 | Buffer buffer(1200); 63 | 64 | //Create chunk 65 | sctp::InitiationChunk init; 66 | init.advertisedReceiverWindowCredit = 6500; 67 | init.numberOfOutboundStreams = 1024; 68 | init.numberOfInboundStreams = 2048; 69 | init.initialTransmissionSequenceNumber = 128; 70 | init.suggestedCookieLifeSpanIncrement = 32000; 71 | init.supportedAddressTypes = {1,2}; 72 | init.supportedExtensions = {3,4,5,6,7}; 73 | init.forwardTSNSupported = true; 74 | 75 | //Serialize 76 | BufferWritter writter(buffer); 77 | size_t len = init.Serialize(writter); 78 | ASSERT_TRUE(len); 79 | buffer.SetSize(len); 80 | 81 | //Parse it again 82 | BufferReader reader(buffer); 83 | auto chunk = sctp::Chunk::Parse(reader); 84 | ASSERT_TRUE(chunk); 85 | ASSERT_EQ(chunk->type,sctp::Chunk::INIT); 86 | //Check chunk is equal to init chunk 87 | auto init2 = std::static_pointer_cast(chunk); 88 | ASSERT_EQ(init2->advertisedReceiverWindowCredit ,init.advertisedReceiverWindowCredit); 89 | ASSERT_EQ(init2->numberOfOutboundStreams ,init.numberOfOutboundStreams); 90 | ASSERT_EQ(init2->numberOfInboundStreams ,init.numberOfInboundStreams); 91 | ASSERT_EQ(init2->initialTransmissionSequenceNumber ,init.initialTransmissionSequenceNumber); 92 | ASSERT_EQ(init2->suggestedCookieLifeSpanIncrement ,init.suggestedCookieLifeSpanIncrement); 93 | ASSERT_EQ(init2->supportedAddressTypes.size() ,init.supportedAddressTypes.size()); 94 | ASSERT_EQ(init2->supportedExtensions.size() ,init.supportedExtensions.size()); 95 | ASSERT_EQ(init2->forwardTSNSupported ,init.forwardTSNSupported); 96 | 97 | } 98 | 99 | 100 | 101 | TEST_F(Chunks, ParseInitAck) 102 | { 103 | 104 | //INIT ACK 105 | uint8_t data[104] = { 106 | 0x13, 0x88, 0x13, 0x88, 107 | 0x29, 0x8f, 0x32, 0x21, 108 | 0xe9, 0x62, 0xaf, 0xbf, 109 | 0x02, 0x00, 0x00, 0x5c, 110 | 0xa1, 0x50, 0x72, 0xdd, 111 | 0xff, 0xff, 0xff, 0xff, 112 | 0xff, 0xff, 0xff, 0xff, 113 | 0x00, 0x00, 0x00, 0x00, 114 | 0x00, 0x07, 0x00, 0x08, 115 | 0x64, 0x74, 0x6c, 0x73, 116 | 0x00, 0x08, 0x00, 0x24, 117 | 0x12, 0x52, 0x00, 0x00, 118 | 0xc8, 0x6f, 0x00, 0x00, 119 | 0x16, 0x3f, 0x00, 0x00, 120 | 0x8a, 0x3c, 0x00, 0x00, 121 | 0x0b, 0x14, 0x00, 0x00, 122 | 0x21, 0x28, 0x00, 0x00, 123 | 0x82, 0x3d, 0x00, 0x00, 124 | 0x24, 0x64, 0x00, 0x00, 125 | 0x00, 0x08, 0x00, 0x06, 126 | 0x00, 0x01, 0x00, 0x00, 127 | 0x00, 0x08, 0x00, 0x06, 128 | 0x80, 0xc1, 0x00, 0x00, 129 | 0x80, 0x08, 0x00, 0x05, 130 | 0x82, 0x00, 0x00, 0x00, 131 | 0xc0, 0x00, 0x00, 0x04 132 | }; 133 | 134 | BufferReader reader(data,sizeof(data)); 135 | 136 | auto header = sctp::PacketHeader::Parse(reader); 137 | ASSERT_TRUE(header); 138 | auto chunk = sctp::Chunk::Parse(reader); 139 | ASSERT_TRUE(chunk); 140 | ASSERT_EQ(chunk->type,sctp::Chunk::INIT_ACK); 141 | ASSERT_FALSE(reader.GetLeft()); 142 | 143 | } 144 | 145 | TEST_F(Chunks, SerializeInitAck) 146 | { 147 | Buffer buffer(1200); 148 | uint8_t cookie[5] = {0,1,2,3,4}; 149 | Buffer unknown(4); 150 | 151 | //Create chunk 152 | sctp::InitiationAcknowledgementChunk ack; 153 | ack.advertisedReceiverWindowCredit = 6500; 154 | ack.numberOfOutboundStreams = 1024; 155 | ack.numberOfInboundStreams = 2048; 156 | ack.initialTransmissionSequenceNumber = 128; 157 | ack.supportedExtensions = {3,4,5,6,7}; 158 | ack.stateCookie.SetData(cookie,sizeof(cookie)); 159 | ack.forwardTSNSupported = true; 160 | ack.unrecognizedParameters.push_back(unknown.Clone()); 161 | //Serialize 162 | BufferWritter writter(buffer); 163 | size_t len = ack.Serialize(writter); 164 | ASSERT_TRUE(len); 165 | buffer.SetSize(len); 166 | 167 | //Parse it again 168 | BufferReader reader(buffer); 169 | auto chunk = sctp::Chunk::Parse(reader); 170 | ASSERT_TRUE(chunk); 171 | ASSERT_EQ(chunk->type,sctp::Chunk::INIT_ACK); 172 | //Check chunk is equal to init chunk 173 | auto ack2 = std::static_pointer_cast(chunk); 174 | ASSERT_EQ(ack2->advertisedReceiverWindowCredit ,ack.advertisedReceiverWindowCredit); 175 | ASSERT_EQ(ack2->numberOfOutboundStreams ,ack.numberOfOutboundStreams); 176 | ASSERT_EQ(ack2->numberOfInboundStreams ,ack.numberOfInboundStreams); 177 | ASSERT_EQ(ack2->initialTransmissionSequenceNumber ,ack.initialTransmissionSequenceNumber); 178 | ASSERT_EQ(ack2->stateCookie.GetSize() ,ack.stateCookie.GetSize()); 179 | ASSERT_EQ(ack2->supportedExtensions.size() ,ack.supportedExtensions.size()); 180 | ASSERT_EQ(ack2->forwardTSNSupported ,ack.forwardTSNSupported); 181 | 182 | } 183 | 184 | 185 | TEST_F(Chunks, ParseCookieEcho) 186 | { 187 | 188 | //COOKIE 189 | uint8_t data[20] = { 190 | 0x13, 0x88, 0x13, 0x88, 191 | 0xa1, 0x50, 0x72, 0xdd, 192 | 0xe0, 0x33, 0x22, 0xa8, 193 | 0x0a, 0x00, 0x00, 0x08, 194 | 0x64, 0x74, 0x6c, 0x73 195 | }; 196 | 197 | BufferReader reader(data,sizeof(data)); 198 | 199 | auto header = sctp::PacketHeader::Parse(reader); 200 | ASSERT_TRUE(header); 201 | auto chunk = sctp::Chunk::Parse(reader); 202 | ASSERT_TRUE(chunk); 203 | ASSERT_EQ(chunk->type,sctp::Chunk::COOKIE_ECHO); 204 | ASSERT_FALSE(reader.GetLeft()); 205 | 206 | } 207 | 208 | TEST_F(Chunks, SerializeCookieEcho) 209 | { 210 | Buffer buffer(1200); 211 | uint8_t cookie[5] = {0,1,2,3,4}; 212 | 213 | //Create chunk 214 | sctp::CookieEchoChunk echo; 215 | echo.cookie.SetData(cookie,sizeof(cookie)); 216 | 217 | //Serialize 218 | BufferWritter writter(buffer); 219 | size_t len = echo.Serialize(writter); 220 | ASSERT_TRUE(len); 221 | buffer.SetSize(len); 222 | 223 | //Parse it again 224 | BufferReader reader(buffer); 225 | auto chunk = sctp::Chunk::Parse(reader); 226 | ASSERT_TRUE(chunk); 227 | ASSERT_EQ(chunk->type,sctp::Chunk::COOKIE_ECHO); 228 | //Check chunk is equal to init chunk 229 | auto echo2 = std::static_pointer_cast(chunk); 230 | ASSERT_EQ(echo2->cookie.GetSize() ,echo.cookie.GetSize()); 231 | } 232 | 233 | 234 | 235 | TEST_F(Chunks, ParseCookieAck) 236 | { 237 | 238 | //COOKIE ECHO 239 | uint8_t data[16] = { 240 | 0x13, 0x88, 0x13, 0x88, 241 | 0x29, 0x8f, 0x32, 0x21, 242 | 0x4d, 0xdf, 0xef, 0x0b, 243 | 0x0b, 0x00, 0x00, 0x04 244 | }; 245 | 246 | BufferReader reader(data,sizeof(data)); 247 | 248 | auto header = sctp::PacketHeader::Parse(reader); 249 | ASSERT_TRUE(header); 250 | auto chunk = sctp::Chunk::Parse(reader); 251 | ASSERT_TRUE(chunk); 252 | ASSERT_EQ(chunk->type,sctp::Chunk::COOKIE_ACK); 253 | ASSERT_FALSE(reader.GetLeft()); 254 | 255 | } 256 | 257 | TEST_F(Chunks, SerializeCookieAck) 258 | { 259 | Buffer buffer(1200); 260 | //Create chunk 261 | sctp::CookieAckChunk ack; 262 | 263 | //Serialize 264 | BufferWritter writter(buffer); 265 | size_t len = ack.Serialize(writter); 266 | ASSERT_TRUE(len); 267 | buffer.SetSize(len); 268 | 269 | //Parse it again 270 | BufferReader reader(buffer); 271 | auto chunk = sctp::Chunk::Parse(reader); 272 | ASSERT_TRUE(chunk); 273 | ASSERT_EQ(chunk->type,sctp::Chunk::COOKIE_ACK); 274 | //Check chunk is equal to init chunk 275 | auto ack2 = std::static_pointer_cast(chunk); 276 | ASSERT_TRUE(ack2); 277 | 278 | } 279 | 280 | // 281 | //TEST_F(Chunks, SparseHeartbeatRequest) 282 | //{ 283 | // 284 | // BufferReader reader(data,sizeof(data)); 285 | // 286 | // auto header = sctp::PacketHeader::Parse(reader); 287 | // ASSERT_TRUE(header); 288 | // auto chunk = sctp::Chunk::Parse(reader); 289 | // ASSERT_TRUE(chunk); 290 | // ASSERT_EQ(chunk->type,sctp::Chunk::INIT); 291 | // ASSERT_FALSE(reader.GetLeft()); 292 | // 293 | //} 294 | 295 | TEST_F(Chunks, SerializeHeartbeatRequest) 296 | { 297 | Buffer buffer(1200); 298 | uint8_t info[5] = {0,1,2,3,4}; 299 | 300 | //Create chunk 301 | sctp::HeartbeatRequestChunk heartbeat; 302 | heartbeat.senderSpecificHearbeatInfo.SetData(info,sizeof(info)); 303 | 304 | //Serialize 305 | BufferWritter writter(buffer); 306 | size_t len = heartbeat.Serialize(writter); 307 | ASSERT_TRUE(len); 308 | buffer.SetSize(len); 309 | 310 | //Parse it again 311 | BufferReader reader(buffer); 312 | auto chunk = sctp::Chunk::Parse(reader); 313 | ASSERT_TRUE(chunk); 314 | ASSERT_EQ(chunk->type,sctp::Chunk::HEARTBEAT); 315 | //Check chunk is equal to init chunk 316 | auto heartbeat2 = std::static_pointer_cast(chunk); 317 | ASSERT_EQ(heartbeat2->senderSpecificHearbeatInfo.GetSize() ,heartbeat.senderSpecificHearbeatInfo.GetSize()); 318 | } 319 | // 320 | //TEST_F(Chunks, ParseHeartbeatAck) 321 | //{ 322 | // 323 | // BufferReader reader(data,sizeof(data)); 324 | // 325 | // auto header = sctp::PacketHeader::Parse(reader); 326 | // ASSERT_TRUE(header); 327 | // auto chunk = sctp::Chunk::Parse(reader); 328 | // ASSERT_TRUE(chunk); 329 | // ASSERT_EQ(chunk->type,sctp::Chunk::INIT); 330 | // ASSERT_FALSE(reader.GetLeft()); 331 | // 332 | //} 333 | 334 | TEST_F(Chunks, SerializeHeartbeatAck) 335 | { 336 | Buffer buffer(1200); 337 | uint8_t info[5] = {0,1,2,3,4}; 338 | 339 | //Create chunk 340 | sctp::HeartbeatAckChunk heartbeat; 341 | heartbeat.senderSpecificHearbeatInfo.SetData(info,sizeof(info)); 342 | 343 | //Serialize 344 | BufferWritter writter(buffer); 345 | size_t len = heartbeat.Serialize(writter); 346 | ASSERT_TRUE(len); 347 | buffer.SetSize(len); 348 | 349 | //Parse it again 350 | BufferReader reader(buffer); 351 | auto chunk = sctp::Chunk::Parse(reader); 352 | ASSERT_TRUE(chunk); 353 | ASSERT_EQ(chunk->type,sctp::Chunk::HEARTBEAT_ACK); 354 | //Check chunk is equal to init chunk 355 | auto heartbeat2 = std::static_pointer_cast(chunk); 356 | ASSERT_EQ(heartbeat2->senderSpecificHearbeatInfo.GetSize() ,heartbeat.senderSpecificHearbeatInfo.GetSize()); 357 | } 358 | // 359 | //TEST_F(Chunks, ParsePadding) 360 | //{ 361 | // 362 | // BufferReader reader(data,sizeof(data)); 363 | // 364 | // auto header = sctp::PacketHeader::Parse(reader); 365 | // ASSERT_TRUE(header); 366 | // auto chunk = sctp::Chunk::Parse(reader); 367 | // ASSERT_TRUE(chunk); 368 | // ASSERT_EQ(chunk->type,sctp::Chunk::INIT); 369 | // ASSERT_FALSE(reader.GetLeft()); 370 | //} 371 | 372 | TEST_F(Chunks, SerializePadding) 373 | { 374 | Buffer buffer(1200); 375 | uint8_t info[7] = {0,1,2,3,4,5,6}; 376 | 377 | //Create chunk 378 | sctp::PaddingChunk padding; 379 | padding.buffer.SetData(info,sizeof(info)); 380 | 381 | //Serialize 382 | BufferWritter writter(buffer); 383 | size_t len = padding.Serialize(writter); 384 | ASSERT_TRUE(len); 385 | buffer.SetSize(len); 386 | 387 | //Parse it again 388 | BufferReader reader(buffer); 389 | auto chunk = sctp::Chunk::Parse(reader); 390 | ASSERT_TRUE(chunk); 391 | ASSERT_EQ(chunk->type,sctp::Chunk::PAD); 392 | //Check chunk is equal to init chunk 393 | auto padding2 = std::static_pointer_cast(chunk); 394 | ASSERT_EQ(padding2->buffer.GetSize() ,padding.buffer.GetSize()); 395 | } 396 | 397 | TEST_F(Chunks, SerializeUnknown) 398 | { 399 | Buffer buffer(1200); 400 | uint8_t info[8] = {0,1,2,3,4,5,6,7}; 401 | 402 | //Create chunk 403 | sctp::UnknownChunk unknown(255); 404 | unknown.buffer.SetData(info,sizeof(info)); 405 | 406 | //Serialize 407 | BufferWritter writter(buffer); 408 | size_t len = unknown.Serialize(writter); 409 | ASSERT_TRUE(len); 410 | buffer.SetSize(len); 411 | 412 | //Parse it again 413 | BufferReader reader(buffer); 414 | auto chunk = sctp::Chunk::Parse(reader); 415 | ASSERT_TRUE(chunk); 416 | ASSERT_EQ(chunk->type,255); 417 | //Check chunk is equal to init chunk 418 | auto unknown2 = std::static_pointer_cast(chunk); 419 | ASSERT_EQ(unknown2->buffer.GetSize() ,unknown.buffer.GetSize()); 420 | } 421 | 422 | 423 | TEST_F(Chunks, ParsePayloadData) 424 | { 425 | 426 | //PDATA with datachannel open 427 | uint8_t data[56] = { 428 | 0x13, 0x88, 0x13, 0x88, 429 | 0xa1, 0x50, 0x72, 0xdd, 430 | 0x50, 0x4d, 0x63, 0x03, 431 | 0x00, 0x03, 0x00, 0x2c, 432 | 0x54, 0xb0, 0x12, 0xb4, 433 | 0x00, 0x00, 0x00, 0x00, 434 | 0x00, 0x00, 0x00, 0x32, 435 | 0x03, 0x00, 0x00, 0x00, 436 | 0x00, 0x00, 0x00, 0x00, 437 | 0x00, 0x10, 0x00, 0x00, 438 | 0x61, 0x61, 0x61, 0x61, 439 | 0x61, 0x61, 0x61, 0x61, 440 | 0x61, 0x61, 0x61, 0x61, 441 | 0x61, 0x61, 0x61, 0x61 442 | }; 443 | BufferReader reader(data,sizeof(data)); 444 | 445 | auto header = sctp::PacketHeader::Parse(reader); 446 | ASSERT_TRUE(header); 447 | auto chunk = sctp::Chunk::Parse(reader); 448 | ASSERT_TRUE(chunk); 449 | ASSERT_EQ(chunk->type,sctp::Chunk::PDATA); 450 | ASSERT_FALSE(reader.GetLeft()); 451 | 452 | } 453 | 454 | -------------------------------------------------------------------------------- /gtests/FakeTimeService.h: -------------------------------------------------------------------------------- 1 | #ifndef FAKETIMESERVICE_H 2 | #define FAKETIMESERVICE_H 3 | 4 | #include "Datachannels.h" 5 | 6 | using namespace std::chrono_literals; 7 | 8 | class FakeTimeService : public datachannels::TimeService 9 | { 10 | public: 11 | class TimerImpl : public datachannels::Timer, public std::enable_shared_from_this 12 | { 13 | public: 14 | using shared = std::shared_ptr; 15 | public: 16 | TimerImpl(FakeTimeService& timeService, const std::chrono::milliseconds& repeat, std::function callback) : 17 | timeService(timeService), 18 | next(0), 19 | repeat(repeat), 20 | callback(callback) 21 | { 22 | } 23 | 24 | virtual ~TimerImpl() 25 | { 26 | } 27 | 28 | TimerImpl(const TimerImpl&) = delete; 29 | 30 | virtual void Cancel() override 31 | { 32 | //We don't have to repeat this 33 | repeat = 0ms; 34 | 35 | //If not scheduled 36 | if (!next.count()) 37 | //Nothing 38 | return; 39 | 40 | //Get all timers at that tick 41 | auto result = timeService.timers.equal_range(next); 42 | 43 | //Reset next tick 44 | next = 0ms; 45 | 46 | // Iterate over the range 47 | for (auto it = result.first; it != result.second; ++it) 48 | { 49 | //If it is the same impl 50 | if (it->second.get()==this) 51 | { 52 | //Remove this one 53 | timeService.timers.erase(it); 54 | //Found 55 | break; 56 | } 57 | } 58 | } 59 | 60 | virtual void Again(const std::chrono::milliseconds& ms) override 61 | { 62 | //Get next run 63 | auto next = timeService.GetNow() + ms; 64 | 65 | //Remove us 66 | Cancel(); 67 | 68 | //Set next tick 69 | this->next = next; 70 | 71 | //Add to timer list 72 | timeService.timers.insert({next, this->shared_from_this()}); 73 | } 74 | 75 | virtual std::chrono::milliseconds GetRepeat() const override { return repeat; }; 76 | 77 | FakeTimeService& timeService; 78 | std::chrono::milliseconds next; 79 | std::chrono::milliseconds repeat; 80 | std::function callback; 81 | }; 82 | public: 83 | void SetNow(const std::chrono::milliseconds& ms) 84 | { 85 | //Ensure now is after now 86 | if (ms triggered; 91 | //Get all timers to process in this loop 92 | for (auto it = timers.begin(); it!=timers.end(); ) 93 | { 94 | //Check it we are still on the time 95 | if (it->first>now) 96 | //It is yet to come 97 | break; 98 | //Get timer 99 | triggered.push_back(it->second); 100 | //Remove from the list 101 | it = timers.erase(it); 102 | } 103 | 104 | //Now process all timers triggered 105 | for (auto& timer : triggered) 106 | { 107 | //UltraDebug("-EventLoop::Run() | timer triggered at ll%u\n",now.count()); 108 | //We are executing 109 | timer->next = 0ms; 110 | //Execute it 111 | timer->callback(now); 112 | //If we have to reschedule it again 113 | if (timer->repeat.count() && !timer->next.count()) 114 | { 115 | //Set next 116 | timer->next = now + timer->repeat; 117 | //Schedule 118 | timers.insert({timer->next, timer}); 119 | } 120 | } 121 | 122 | } 123 | 124 | virtual const std::chrono::milliseconds GetNow() const override 125 | { 126 | return now; 127 | }; 128 | 129 | virtual datachannels::Timer::shared CreateTimer(std::function callback) override 130 | { 131 | //Create timer without scheduling it 132 | auto timer = std::make_shared(*this,0ms,callback); 133 | //Done 134 | return std::static_pointer_cast(timer); 135 | }; 136 | 137 | virtual datachannels::Timer::shared CreateTimer(const std::chrono::milliseconds& ms, std::function timeout) override 138 | { 139 | //Timer without repeat 140 | return CreateTimer(ms,0ms,timeout); 141 | }; 142 | 143 | virtual datachannels::Timer::shared CreateTimer(const std::chrono::milliseconds& ms, const std::chrono::milliseconds& repeat, std::function timeout) override 144 | { 145 | //Create timer 146 | auto timer = std::make_shared(*this,repeat,timeout); 147 | 148 | //Set next tick 149 | timer->next = GetNow() + ms;; 150 | 151 | //Add to timer list 152 | timers.insert({timer->next, timer}); 153 | 154 | //Done 155 | return std::static_pointer_cast(timer); 156 | }; 157 | 158 | private: 159 | std::multimap timers; 160 | std::chrono::milliseconds now; 161 | }; 162 | 163 | #endif /* FAKETIMESERVICE_H */ 164 | 165 | -------------------------------------------------------------------------------- /gtests/Makefile: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "Unix Makefiles" Generator, CMake Version 3.9 3 | 4 | # Default target executed when no arguments are given to make. 5 | default_target: all 6 | 7 | .PHONY : default_target 8 | 9 | # Allow only one "make -f Makefile2" at a time, but pass parallelism. 10 | .NOTPARALLEL: 11 | 12 | 13 | #============================================================================= 14 | # Special targets provided by cmake. 15 | 16 | # Disable implicit rules so canonical targets will work. 17 | .SUFFIXES: 18 | 19 | 20 | # Remove some rules from gmake that .SUFFIXES does not remove. 21 | SUFFIXES = 22 | 23 | .SUFFIXES: .hpux_make_needs_suffix_list 24 | 25 | 26 | # Suppress display of executed commands. 27 | $(VERBOSE).SILENT: 28 | 29 | 30 | # A target that is always out of date. 31 | cmake_force: 32 | 33 | .PHONY : cmake_force 34 | 35 | #============================================================================= 36 | # Set environment variables for the build. 37 | 38 | # The shell in which to execute make rules. 39 | SHELL = /bin/sh 40 | 41 | # The CMake executable. 42 | CMAKE_COMMAND = /usr/bin/cmake 43 | 44 | # The command to remove a file. 45 | RM = /usr/bin/cmake -E remove -f 46 | 47 | # Escaping for special characters. 48 | EQUALS = = 49 | 50 | # The top-level source directory on which CMake was run. 51 | CMAKE_SOURCE_DIR = /usr/local/src/libdatachannels 52 | 53 | # The top-level build directory on which CMake was run. 54 | CMAKE_BINARY_DIR = /usr/local/src/libdatachannels 55 | 56 | #============================================================================= 57 | # Targets provided globally by CMake. 58 | 59 | # Special rule for the target rebuild_cache 60 | rebuild_cache: 61 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." 62 | /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) 63 | .PHONY : rebuild_cache 64 | 65 | # Special rule for the target rebuild_cache 66 | rebuild_cache/fast: rebuild_cache 67 | 68 | .PHONY : rebuild_cache/fast 69 | 70 | # Special rule for the target edit_cache 71 | edit_cache: 72 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." 73 | /usr/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. 74 | .PHONY : edit_cache 75 | 76 | # Special rule for the target edit_cache 77 | edit_cache/fast: edit_cache 78 | 79 | .PHONY : edit_cache/fast 80 | 81 | # The main all target 82 | all: cmake_check_build_system 83 | cd /usr/local/src/libdatachannels && $(CMAKE_COMMAND) -E cmake_progress_start /usr/local/src/libdatachannels/CMakeFiles /usr/local/src/libdatachannels/gtests/CMakeFiles/progress.marks 84 | cd /usr/local/src/libdatachannels && $(MAKE) -f CMakeFiles/Makefile2 gtests/all 85 | $(CMAKE_COMMAND) -E cmake_progress_start /usr/local/src/libdatachannels/CMakeFiles 0 86 | .PHONY : all 87 | 88 | # The main clean target 89 | clean: 90 | cd /usr/local/src/libdatachannels && $(MAKE) -f CMakeFiles/Makefile2 gtests/clean 91 | .PHONY : clean 92 | 93 | # The main clean target 94 | clean/fast: clean 95 | 96 | .PHONY : clean/fast 97 | 98 | # Prepare targets for installation. 99 | preinstall: all 100 | cd /usr/local/src/libdatachannels && $(MAKE) -f CMakeFiles/Makefile2 gtests/preinstall 101 | .PHONY : preinstall 102 | 103 | # Prepare targets for installation. 104 | preinstall/fast: 105 | cd /usr/local/src/libdatachannels && $(MAKE) -f CMakeFiles/Makefile2 gtests/preinstall 106 | .PHONY : preinstall/fast 107 | 108 | # clear depends 109 | depend: 110 | cd /usr/local/src/libdatachannels && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 111 | .PHONY : depend 112 | 113 | # Convenience name for target. 114 | gtests/CMakeFiles/gtests.dir/rule: 115 | cd /usr/local/src/libdatachannels && $(MAKE) -f CMakeFiles/Makefile2 gtests/CMakeFiles/gtests.dir/rule 116 | .PHONY : gtests/CMakeFiles/gtests.dir/rule 117 | 118 | # Convenience name for target. 119 | gtests: gtests/CMakeFiles/gtests.dir/rule 120 | 121 | .PHONY : gtests 122 | 123 | # fast build rule for target. 124 | gtests/fast: 125 | cd /usr/local/src/libdatachannels && $(MAKE) -f gtests/CMakeFiles/gtests.dir/build.make gtests/CMakeFiles/gtests.dir/build 126 | .PHONY : gtests/fast 127 | 128 | TestSuite.o: TestSuite.cpp.o 129 | 130 | .PHONY : TestSuite.o 131 | 132 | # target to build an object file 133 | TestSuite.cpp.o: 134 | cd /usr/local/src/libdatachannels && $(MAKE) -f gtests/CMakeFiles/gtests.dir/build.make gtests/CMakeFiles/gtests.dir/TestSuite.cpp.o 135 | .PHONY : TestSuite.cpp.o 136 | 137 | TestSuite.i: TestSuite.cpp.i 138 | 139 | .PHONY : TestSuite.i 140 | 141 | # target to preprocess a source file 142 | TestSuite.cpp.i: 143 | cd /usr/local/src/libdatachannels && $(MAKE) -f gtests/CMakeFiles/gtests.dir/build.make gtests/CMakeFiles/gtests.dir/TestSuite.cpp.i 144 | .PHONY : TestSuite.cpp.i 145 | 146 | TestSuite.s: TestSuite.cpp.s 147 | 148 | .PHONY : TestSuite.s 149 | 150 | # target to generate assembly for a file 151 | TestSuite.cpp.s: 152 | cd /usr/local/src/libdatachannels && $(MAKE) -f gtests/CMakeFiles/gtests.dir/build.make gtests/CMakeFiles/gtests.dir/TestSuite.cpp.s 153 | .PHONY : TestSuite.cpp.s 154 | 155 | # Help Target 156 | help: 157 | @echo "The following are some of the valid targets for this Makefile:" 158 | @echo "... all (the default if no target is provided)" 159 | @echo "... clean" 160 | @echo "... depend" 161 | @echo "... rebuild_cache" 162 | @echo "... gtests" 163 | @echo "... edit_cache" 164 | @echo "... TestSuite.o" 165 | @echo "... TestSuite.i" 166 | @echo "... TestSuite.s" 167 | .PHONY : help 168 | 169 | 170 | 171 | #============================================================================= 172 | # Special targets to cleanup operation of make. 173 | 174 | # Special rule to run CMake to check the build system integrity. 175 | # No rule that depends on this can have commands that come from listfiles 176 | # because they might be regenerated. 177 | cmake_check_build_system: 178 | cd /usr/local/src/libdatachannels && $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 179 | .PHONY : cmake_check_build_system 180 | 181 | -------------------------------------------------------------------------------- /gtests/SequenceNumberWrapper.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * File: Association 3 | * Author: Sergio 4 | * 5 | * Created on 30-ene-2019, 17:47:50 6 | */ 7 | 8 | #include 9 | 10 | #include "Buffer.h" 11 | #include "sctp/SequenceNumberWrapper.h" 12 | 13 | class SequenceNumberWrapper : public testing::Test 14 | { 15 | protected: 16 | }; 17 | 18 | 19 | TEST_F(SequenceNumberWrapper, Base) 20 | { 21 | 22 | sctp::SequenceNumberWrapper wrapper; 23 | 24 | for (uint32_t seq = 0; seq<~static_cast(0);seq+=0xFFF) 25 | { 26 | auto wrapped = wrapper.Wrap(seq); 27 | auto unwrapped = wrapper.Wrap(wrapped); 28 | ASSERT_EQ(wrapped,unwrapped); 29 | } 30 | } 31 | 32 | TEST_F(SequenceNumberWrapper, Wrap) 33 | { 34 | 35 | sctp::SequenceNumberWrapper wrapper; 36 | 37 | //Last seq number in wrap 38 | auto last = wrapper.Wrap(~0); 39 | auto next = wrapper.Wrap(0); 40 | //Check extended numbers are consecutive 41 | ASSERT_LT(last,next); 42 | ASSERT_EQ(next-last,1); 43 | ASSERT_EQ(~0,wrapper.UnWrap(last)); 44 | ASSERT_EQ(0 ,wrapper.UnWrap(next)); 45 | 46 | } 47 | 48 | TEST_F(SequenceNumberWrapper, OutOfOrder) 49 | { 50 | 51 | sctp::SequenceNumberWrapper wrapper; 52 | 53 | auto one = wrapper.Wrap(1); 54 | auto zero = wrapper.Wrap(0); 55 | //Check extended numbers are consecutive 56 | ASSERT_LT(zero,one); 57 | ASSERT_EQ(one-zero,1); 58 | ASSERT_EQ(0, wrapper.UnWrap(zero)); 59 | ASSERT_EQ(one, wrapper.UnWrap(one)); 60 | 61 | } 62 | 63 | TEST_F(SequenceNumberWrapper, PrevWrap) 64 | { 65 | 66 | sctp::SequenceNumberWrapper wrapper; 67 | 68 | //Last seq number in wrap 69 | auto last = wrapper.Wrap(~0); 70 | auto next = wrapper.Wrap(0); 71 | auto ooo = wrapper.Wrap(~0-1); 72 | //Check extended numbers are consecutive 73 | ASSERT_LT(last,next); 74 | ASSERT_LT(ooo,last); 75 | ASSERT_LT(ooo,next); 76 | ASSERT_EQ(next-last,1); 77 | ASSERT_EQ(next-ooo,2); 78 | ASSERT_EQ(last-ooo,1); 79 | ASSERT_EQ(~0,wrapper.UnWrap(last)); 80 | ASSERT_EQ(0 ,wrapper.UnWrap(next)); 81 | ASSERT_EQ(~0-1,wrapper.UnWrap(ooo)); 82 | 83 | } 84 | -------------------------------------------------------------------------------- /gtests/cmake_install.cmake: -------------------------------------------------------------------------------- 1 | # Install script for directory: /usr/local/src/libdatachannels/gtests 2 | 3 | # Set the install prefix 4 | if(NOT DEFINED CMAKE_INSTALL_PREFIX) 5 | set(CMAKE_INSTALL_PREFIX "/usr/local") 6 | endif() 7 | string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") 8 | 9 | # Set the install configuration name. 10 | if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) 11 | if(BUILD_TYPE) 12 | string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" 13 | CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") 14 | else() 15 | set(CMAKE_INSTALL_CONFIG_NAME "Debug") 16 | endif() 17 | message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") 18 | endif() 19 | 20 | # Set the component getting installed. 21 | if(NOT CMAKE_INSTALL_COMPONENT) 22 | if(COMPONENT) 23 | message(STATUS "Install component: \"${COMPONENT}\"") 24 | set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") 25 | else() 26 | set(CMAKE_INSTALL_COMPONENT) 27 | endif() 28 | endif() 29 | 30 | # Install shared libraries without execute permission? 31 | if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) 32 | set(CMAKE_INSTALL_SO_NO_EXE "1") 33 | endif() 34 | 35 | -------------------------------------------------------------------------------- /include/Datachannels.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDATACHANNELS_H_ 2 | #define LIBDATACHANNELS_H_ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace datachannels 10 | { 11 | 12 | class Timer 13 | { 14 | public: 15 | using shared = std::shared_ptr; 16 | public: 17 | virtual ~Timer() = default; 18 | virtual void Cancel() = 0; 19 | virtual void Again(const std::chrono::milliseconds& ms) = 0; 20 | virtual std::chrono::milliseconds GetRepeat() const = 0; 21 | }; 22 | 23 | class TimeService 24 | { 25 | public: 26 | virtual ~TimeService() = default; 27 | virtual const std::chrono::milliseconds GetNow() const = 0; 28 | virtual Timer::shared CreateTimer(std::function callback) = 0; 29 | virtual Timer::shared CreateTimer(const std::chrono::milliseconds& ms, std::function timeout) = 0; 30 | virtual Timer::shared CreateTimer(const std::chrono::milliseconds& ms, const std::chrono::milliseconds& repeat, std::function timeout) = 0; 31 | }; 32 | 33 | 34 | class Datachannel 35 | { 36 | public: 37 | enum MessageType 38 | { 39 | UTF8, 40 | Binary 41 | }; 42 | 43 | struct Options 44 | { 45 | 46 | }; 47 | 48 | using shared = std::shared_ptr; 49 | public: 50 | virtual ~Datachannel() = default; 51 | virtual bool Send(MessageType type, const uint8_t* data = nullptr, const uint64_t size = 0) = 0; 52 | virtual bool Close() = 0; 53 | 54 | // Event handlers 55 | virtual void OnMessage(const std::function& callback) = 0; 56 | 57 | }; 58 | 59 | enum Setup 60 | { 61 | Client, 62 | Server 63 | }; 64 | 65 | class Transport 66 | { 67 | public: 68 | virtual ~Transport() = default; 69 | virtual size_t ReadPacket(uint8_t *data, uint32_t size) = 0; 70 | virtual size_t WritePacket(uint8_t *data, uint32_t size) = 0; 71 | 72 | virtual void OnPendingData(std::function callback) = 0; 73 | }; 74 | 75 | class Endpoint 76 | { 77 | public: 78 | struct Options 79 | { 80 | uint16_t localPort = 5000; 81 | uint16_t remotePort = 5000; 82 | Setup setup = Server; 83 | }; 84 | 85 | using shared = std::shared_ptr; 86 | public: 87 | // Static methods 88 | 89 | // Create new datachannel endpoint 90 | // options.setup : Client/Server 91 | static Endpoint::shared Create(TimeService& timeService) ; 92 | 93 | public: 94 | virtual ~Endpoint() = default; 95 | virtual bool Init(const Options& options) = 0; 96 | virtual Datachannel::shared CreateDatachannel(const Datachannel::Options& options) = 0; 97 | virtual bool Close() = 0; 98 | 99 | // Getters 100 | virtual uint16_t GetLocalPort() const = 0; 101 | virtual uint16_t GetRemotePort() const = 0; 102 | virtual Transport& GetTransport() = 0; 103 | }; 104 | 105 | }; //namespace 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /specs.md: -------------------------------------------------------------------------------- 1 | Datagram Transport Layer Security (DTLS) Encapsulation of SCTP Packets 2 | https://tools.ietf.org/html/rfc8261 3 | 4 | WebRTC Data Channels 5 | https://tools.ietf.org/html/draft-ietf-rtcweb-data-channel-13 6 | 7 | 8 | SCTP as specified in [RFC4960] MUST be used in combination with the extension defined in [RFC3758] 9 | 10 | - The encapsulation of SCTP over DTLS defined in [RFC8261](https://tools.ietf.org/html/rfc8261) 11 | 12 | - No MTU discovery: The initial Path MTU at the IP layer SHOULD NOT exceed 1200 bytes. 13 | 14 | Each SCTP user message contains a Payload Protocol Identifier (PPID) 15 | that is passed to SCTP by its upper layer on the sending side and 16 | provided to its upper layer on the receiving side. The PPID can be 17 | used to multiplex/demultiplex multiple upper layers over a single 18 | SCTP association. In the WebRTC context, the PPID is used to 19 | distinguish between UTF-8 encoded user data, binary encoded userdata 20 | and the Data Channel Establishment Protocol defined in [I-D.ietf-rtcweb-data-protocol](https://tools.ietf.org/html/draft-ietf-rtcweb-data-protocol-09) 21 | 22 | 23 | The following SCTP protocol extensions are required: 24 | 25 | - The stream reconfiguration extension defined in [RFC6525] MUST be 26 | supported. It is used for closing channels. 27 | 28 | - The dynamic address reconfiguration extension defined in [RFC5061] 29 | MUST be used to signal the support of the stream reset extension 30 | defined in [RFC6525]. Other features of [RFC5061] are OPTIONAL. 31 | 32 | - The partial reliability extension defined in [RFC3758] MUST be 33 | supported. In addition to the timed reliability PR-SCTP policy 34 | defined in [RFC3758], the limited retransmission policy defined in 35 | [I-D.ietf-tsvwg-sctp-prpolicies] MUST be supported. Limiting the 36 | number of retransmissions to zero combined with unordered delivery 37 | provides a UDP-like service where each user message is sent 38 | exactly once and delivered in the order received. 39 | 40 | - The support for message interleaving as defined in [I-D.ietf-tsvwg-sctp-ndata] SHOULD be used. 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_include_directories (libdatachannels PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/internal) 2 | target_include_directories (libdatachannels PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 3 | 4 | target_sources(libdatachannels PUBLIC 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Endpoint.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/Datachannel.cpp 7 | ) 8 | 9 | add_subdirectory (sctp) 10 | -------------------------------------------------------------------------------- /src/Datachannel.cpp: -------------------------------------------------------------------------------- 1 | #include "Datachannel.h" 2 | 3 | namespace datachannels 4 | { 5 | namespace impl 6 | { 7 | 8 | Datachannel::Datachannel(const sctp::Stream::shared& stream) 9 | { 10 | this->stream = stream; 11 | this->stream->OnMessage([&](const uint8_t ppid, const uint8_t* buffer, const size_t size){ 12 | 13 | }); 14 | } 15 | 16 | bool Datachannel::Send(MessageType type, const uint8_t* data, const uint64_t size) 17 | { 18 | uint8_t empty = 0; 19 | 20 | if (!data || !size) 21 | // SCTP does not support the sending of empty user messages. Therefore, 22 | // if an empty message has to be sent, the appropriate PPID (WebRTC 23 | // String Empty or WebRTC Binary Empty) is used and the SCTP user 24 | // message of one zero byte is sent. When receiving an SCTP user 25 | // message with one of these PPIDs, the receiver MUST ignore the SCTP 26 | // user message and process it as an empty message. 27 | return stream->Send(type==UTF8 ? WebRTCStringEmpty : WebRTCBinaryEmpty, &empty, 1); 28 | else 29 | return stream->Send(type==UTF8 ? WebRTCString : WebRTCBinary, data, size); 30 | } 31 | 32 | bool Datachannel::Close() 33 | { 34 | // Closing of a data channel MUST be signaled by resetting the 35 | // corresponding outgoing streams [RFC6525]. This means that if one 36 | // side decides to close the data channel, it resets the corresponding 37 | // outgoing stream. When the peer sees that an incoming stream was 38 | // reset, it also resets its corresponding outgoing stream. Once this 39 | // is completed, the data channel is closed. Resetting a stream sets 40 | // the Stream Sequence Numbers (SSNs) of the stream back to 'zero' with 41 | // a corresponding notification to the application layer that the reset 42 | // has been performed. Streams are available for reuse after a reset 43 | // has been performed. 44 | // 45 | // [RFC6525] also guarantees that all the messages are delivered (or 46 | // abandoned) before the stream is reset. 47 | return true; 48 | } 49 | 50 | }; //namespace impl 51 | }; //namespace datachannel 52 | -------------------------------------------------------------------------------- /src/Datachannel.h: -------------------------------------------------------------------------------- 1 | #ifndef DATACHANNEL_IMPL_DATACHANNEL_H_ 2 | #define DATACHANNEL_IMPL_DATACHANNEL_H_ 3 | #include "Datachannels.h" 4 | 5 | #include "sctp/Stream.h" 6 | 7 | namespace datachannels 8 | { 9 | namespace impl 10 | { 11 | 12 | class Datachannel : public datachannels::Datachannel 13 | { 14 | public: 15 | enum Payload 16 | { 17 | WebRTCString = 51, 18 | WebRTCBinary = 53, 19 | WebRTCStringEmpty = 56, 20 | WebRTCBinaryEmpty = 57, 21 | 22 | }; 23 | 24 | public: 25 | Datachannel(const sctp::Stream::shared& stream); 26 | virtual ~Datachannel() = default; 27 | virtual bool Send(MessageType type, const uint8_t* data = nullptr, const uint64_t size = 0) override; 28 | virtual bool Close() override; 29 | 30 | // Event handlers 31 | virtual void OnMessage(const std::function& callback) override 32 | { 33 | //Store callback 34 | onMessage = callback; 35 | } 36 | private: 37 | sctp::Stream::shared stream; 38 | std::function onMessage; 39 | }; 40 | 41 | }; //namespace impl 42 | }; //namespace datachannel 43 | #endif 44 | -------------------------------------------------------------------------------- /src/Datachannels.cpp: -------------------------------------------------------------------------------- 1 | // Unity jumbo build file 2 | #include "Datachannel.cpp" 3 | #include "Endpoint.cpp" 4 | #include "sctp/Association.cpp" 5 | #include "sctp/PacketHeader.cpp" 6 | #include "sctp/Stream.cpp" 7 | #include "sctp/Chunk.cpp" 8 | #include "sctp/chunks/AbortAssociationChunk.cpp" 9 | #include "sctp/chunks/HeartbeatRequestChunk.cpp" 10 | #include "sctp/chunks/HeartbeatAckChunk.cpp" 11 | #include "sctp/chunks/OperationErrorChunk.cpp" 12 | #include "sctp/chunks/SelectiveAcknowledgementChunk.cpp" 13 | #include "sctp/chunks/ShutdownCompleteChunk.cpp" 14 | #include "sctp/chunks/CookieAckChunk.cpp" 15 | #include "sctp/chunks/InitiationAcknowledgementChunk.cpp" 16 | #include "sctp/chunks/PayloadDataChunk.cpp" 17 | #include "sctp/chunks/ShutdownAcknowledgementChunk.cpp" 18 | #include "sctp/chunks/CookieEchoChunk.cpp" 19 | #include "sctp/chunks/InitiationChunk.cpp" 20 | #include "sctp/chunks/ReConfigChunk.cpp" 21 | #include "sctp/chunks/ShutdownAssociationChunk.cpp" 22 | #include "sctp/chunks/ForwardCumulativeTSNChunk.cpp" 23 | #include "sctp/chunks/UnknownChunk.cpp" 24 | #include "sctp/chunks/PaddingChunk.cpp" 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/Endpoint.cpp: -------------------------------------------------------------------------------- 1 | #include "Endpoint.h" 2 | #include 3 | 4 | namespace datachannels 5 | { 6 | 7 | Endpoint::shared Endpoint::Create(TimeService& timeService) 8 | { 9 | //Create endpoint 10 | auto endpoint = std::make_shared(timeService); 11 | //Cast and return 12 | return std::static_pointer_cast(endpoint); 13 | } 14 | 15 | namespace impl 16 | { 17 | 18 | Endpoint::Endpoint(datachannels::TimeService& timeService) : 19 | association(sctp::Association::Create(timeService)) 20 | { 21 | 22 | } 23 | 24 | Endpoint::~Endpoint() 25 | { 26 | //Terminate association now! 27 | association->Abort(); 28 | } 29 | 30 | bool Endpoint::Init(const Options& options) 31 | { 32 | //Set ports on sctp 33 | association->SetLocalPort(options.localPort); 34 | association->SetRemotePort(options.remotePort); 35 | 36 | //If we are clients 37 | if (options.setup==Setup::Client) 38 | //Start association 39 | return association->Associate(); 40 | 41 | //OK, wait for client to associate 42 | return true; 43 | } 44 | 45 | Datachannel::shared Endpoint::CreateDatachannel(const Datachannel::Options& options) 46 | { 47 | return nullptr; 48 | } 49 | 50 | bool Endpoint::Close() 51 | { 52 | //Gracefuly stop association 53 | return association->Shutdown(); 54 | } 55 | 56 | // Getters 57 | uint16_t Endpoint::GetLocalPort() const 58 | { 59 | return association->GetLocalPort(); 60 | } 61 | 62 | uint16_t Endpoint::GetRemotePort() const 63 | { 64 | return association->GetRemotePort(); 65 | } 66 | 67 | datachannels::Transport& Endpoint::GetTransport() 68 | { 69 | return *association.get(); 70 | } 71 | 72 | }; // namespace impl 73 | }; // namespace datachannel 74 | -------------------------------------------------------------------------------- /src/Endpoint.h: -------------------------------------------------------------------------------- 1 | #ifndef DATACHANNEL_IMPL_ENDPOINT_H_ 2 | #define DATACHANNEL_IMPL_ENDPOINT_H_ 3 | #include "Datachannels.h" 4 | #include "sctp/Association.h" 5 | 6 | namespace datachannels 7 | { 8 | namespace impl 9 | { 10 | 11 | class Endpoint : public datachannels::Endpoint 12 | { 13 | public: 14 | Endpoint(TimeService& timeService); 15 | virtual ~Endpoint(); 16 | 17 | virtual bool Init(const Options& options) override; 18 | virtual Datachannel::shared CreateDatachannel(const Datachannel::Options& options) override; 19 | virtual bool Close() override; 20 | 21 | // Getters 22 | virtual uint16_t GetLocalPort() const override; 23 | virtual uint16_t GetRemotePort() const override; 24 | virtual datachannels::Transport& GetTransport() override; 25 | private: 26 | std::shared_ptr association; 27 | }; 28 | 29 | }; //namespace impl 30 | }; //namespace datachannels 31 | #endif 32 | -------------------------------------------------------------------------------- /src/internal/Buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDATACHANNELS_INTERNAL_BUFFER_H_ 2 | #define LIBDATACHANNELS_INTERNAL_BUFFER_H_ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | //Allingment must be a power of 2 11 | inline int RoundUp(size_t alignment, size_t size) 12 | { 13 | return size ? (size + alignment - 1) & -alignment : 0; 14 | } 15 | 16 | class Buffer 17 | { 18 | public: 19 | using shared = std::shared_ptr; 20 | using unique = std::unique_ptr; 21 | public: 22 | Buffer(const uint8_t* data, const size_t size) 23 | { 24 | //Set buffer size to a 25 | capacity = RoundUp(64,size); 26 | //Allocate memory 27 | #ifdef HAVE_STD_ALIGNED_ALLOC 28 | buffer = (uint8_t*) std::aligned_alloc(64, this->capacity); 29 | #else 30 | buffer = (uint8_t*) std::malloc(this->capacity); 31 | #endif 32 | //Copy 33 | memcpy(buffer,data,size); 34 | //Reset size 35 | this->size = size; 36 | } 37 | 38 | Buffer(size_t capacity = 0) 39 | { 40 | //Set buffer size 41 | this->capacity = RoundUp(64,capacity); 42 | #ifdef HAVE_STD_ALIGNED_ALLOC 43 | //Allocate memory 44 | buffer = capacity ? (uint8_t*) std::aligned_alloc(64, this->capacity) : nullptr; 45 | #else 46 | buffer = capacity ? (uint8_t*) std::malloc(this->capacity) : nullptr; 47 | #endif 48 | //NO size 49 | this->size = 0; 50 | } 51 | 52 | Buffer(Buffer &&other) noexcept 53 | { 54 | this->capacity = other.capacity; 55 | this->buffer = other.buffer; 56 | this->size = other.size; 57 | other.buffer = nullptr; 58 | other.capacity = 0; 59 | other.size = 0; 60 | } 61 | 62 | Buffer& operator=(Buffer&& other) noexcept 63 | { 64 | std::free(this->buffer); 65 | this->capacity = other.capacity; 66 | this->buffer = other.buffer; 67 | this->size = other.size; 68 | other.buffer = nullptr; 69 | other.capacity = 0; 70 | other.size = 0; 71 | return *this; 72 | } 73 | 74 | //Not copiable 75 | Buffer(const Buffer &) = delete; 76 | Buffer& operator=(const Buffer&) = delete; 77 | 78 | ~Buffer() 79 | { 80 | std::free(buffer); 81 | } 82 | 83 | uint8_t* GetData() const { return buffer; } 84 | size_t GetCapacity() const { return capacity; } 85 | size_t GetSize() const { return size; } 86 | 87 | bool IsEmpty() const { return !size; } 88 | 89 | void SetSize(size_t size) 90 | { 91 | //Check capacity 92 | if (size>capacity) 93 | //Allocate new size 94 | Alloc(size); 95 | //Set new size 96 | this->size = size; 97 | } 98 | 99 | void Alloc(size_t capacity) 100 | { 101 | //Calculate new size 102 | this->capacity = capacity; 103 | //Realloc 104 | buffer = ( uint8_t*) std::realloc(buffer,capacity); 105 | //Check new size 106 | if (size>capacity) 107 | //reduce size 108 | size = capacity; 109 | } 110 | 111 | void SetData(const uint8_t* data,const size_t size) 112 | { 113 | //Check size 114 | if (size>capacity) 115 | //Allocate new size 116 | Alloc(size); 117 | //Copy 118 | std::memcpy(buffer,data,size); 119 | //Reset size 120 | this->size = size; 121 | } 122 | 123 | void SetData(const Buffer& buffer) 124 | { 125 | SetData(buffer.GetData(),buffer.GetSize()); 126 | } 127 | 128 | void AppendData(const uint8_t* data,const size_t size) 129 | { 130 | //Check size 131 | if (this->size+size>capacity) 132 | //Allocate new size 133 | Alloc(this->size+size); 134 | //Copy 135 | std::memcpy(buffer+this->size,data,size); 136 | //Increase size 137 | this->size += size; 138 | } 139 | 140 | template 141 | void AppendData(Reader& reader, const size_t size) 142 | { 143 | AppendData(reader.GetData(size), size); 144 | } 145 | 146 | template 147 | void AppendData(Reader& reader) 148 | { 149 | AppendData(reader, reader.GetLeft()); 150 | } 151 | 152 | Buffer Clone() const 153 | { 154 | return Buffer(buffer,size); 155 | } 156 | 157 | static Buffer Wrap(uint8_t* data, size_t size) 158 | { 159 | Buffer buffer; 160 | 161 | buffer.buffer = data; 162 | buffer.capacity = size; 163 | buffer.size = size; 164 | 165 | return buffer; 166 | } 167 | 168 | void Reset() 169 | { 170 | size = 0; 171 | } 172 | 173 | protected: 174 | uint8_t* buffer = nullptr; 175 | size_t capacity = 0; 176 | size_t size = 0; 177 | }; 178 | 179 | #endif 180 | 181 | -------------------------------------------------------------------------------- /src/internal/BufferReader.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDATACHANNELS_INTERNAL_READER_H_ 2 | #define LIBDATACHANNELS_INTERNAL_READER_H_ 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "Buffer.h" 11 | 12 | class BufferReader 13 | { 14 | public: 15 | BufferReader() = default; 16 | BufferReader(const Buffer& buffer) : 17 | data(buffer.GetData()), 18 | size(buffer.GetSize()) 19 | { 20 | this->pos = 0; 21 | } 22 | 23 | BufferReader(const uint8_t* data, const size_t size) : 24 | data(data), 25 | size(size) 26 | { 27 | this->pos = 0; 28 | } 29 | 30 | template std::array Get(size_t i) const 31 | { 32 | std::array array; 33 | memcpy(array.data(),data+i,N); 34 | return array; 35 | } 36 | 37 | inline BufferReader GetReader(size_t i, size_t num) const 38 | { 39 | return BufferReader(data+i,num); 40 | } 41 | 42 | inline std::string GetString(size_t i, size_t num) const 43 | { 44 | return std::string((char*)data+i,num); 45 | } 46 | 47 | inline Buffer GetBuffer(size_t i, size_t num) const 48 | { 49 | return Buffer(data+i,num); 50 | } 51 | 52 | inline uint8_t Get1(size_t i) const 53 | { 54 | return data[i]; 55 | } 56 | 57 | inline uint16_t Get2(size_t i) const 58 | { 59 | return (uint16_t)(data[i+1]) | ((uint16_t)(data[i]))<<8; 60 | } 61 | 62 | inline uint32_t Get3(size_t i) const 63 | { 64 | return (uint32_t)(data[i+2]) | ((uint32_t)(data[i+1]))<<8 | ((uint32_t)(data[i]))<<16; 65 | } 66 | 67 | inline uint32_t Get4(size_t i) const 68 | { 69 | return (uint32_t)(data[i+3]) | ((uint32_t)(data[i+2]))<<8 | ((uint32_t)(data[i+1]))<<16 | ((uint32_t)(data[i]))<<24; 70 | } 71 | inline uint32_t Get4Reversed(size_t i) const 72 | { 73 | return (uint32_t)(data[i]) | ((uint32_t)(data[i+1]))<<8 | ((uint32_t)(data[i+2]))<<16 | ((uint32_t)(data[i+3]))<<24; 74 | } 75 | 76 | inline uint64_t Get8(size_t i) const 77 | { 78 | return ((uint64_t)Get4(i))<<32 | Get4(i+4); 79 | } 80 | 81 | template 82 | std::array Get() { pos+=N; return Get(pos-N); } 83 | inline BufferReader GetReader(size_t num) { pos+=num; return GetReader(pos-num, num); } 84 | inline std::string GetString(size_t num) { pos+=num; return GetString(pos-num, num); } 85 | inline Buffer GetBuffer(size_t num) { pos+=num; return GetBuffer(pos-num,num); } 86 | inline const uint8_t* GetData(size_t num) { const uint8_t* val = data+pos; pos+=num; return val; } 87 | inline uint8_t Get1() { auto val = Get1(pos); pos+=1; return val; } 88 | inline uint16_t Get2() { auto val = Get2(pos); pos+=2; return val; } 89 | inline uint32_t Get3() { auto val = Get3(pos); pos+=3; return val; } 90 | inline uint32_t Get4() { auto val = Get4(pos); pos+=4; return val; } 91 | inline uint32_t Get4Reversed() { auto val = Get4Reversed(pos); pos+=4; return val; } 92 | inline uint64_t Get8() { auto val = Get8(pos); pos+=8; return val; } 93 | inline uint8_t Peek1() { return Get1(pos); } 94 | inline uint16_t Peek2() { return Get2(pos); } 95 | inline uint32_t Peek3() { return Get3(pos); } 96 | inline uint32_t Peek4() { return Get4(pos); } 97 | inline uint64_t Peek8() { return Get8(pos); } 98 | inline const uint8_t* PeekData() { return data+pos; } 99 | inline const uint8_t* PeekData(size_t num) { return data+num; } 100 | size_t PadTo(size_t num) 101 | { 102 | size_t reminder = pos % num; 103 | size_t padding = reminder ? num - reminder : 0; 104 | if (!Assert(padding)) 105 | return 0; 106 | return Skip(padding); 107 | } 108 | 109 | bool Assert(size_t num) const { return pos+num<=size; } 110 | void GoTo(size_t mark) { pos = mark; } 111 | size_t Skip(size_t num) { size_t mark = pos; pos += num; return mark; } 112 | int64_t GetOffset(size_t mark) const { return pos-mark; } 113 | size_t Mark() const { return pos; } 114 | size_t GetLength() const { return pos; } 115 | size_t GetLeft() const { return size-pos; } 116 | size_t GetSize() const { return size; } 117 | 118 | uint64_t DecodeLev128() 119 | { 120 | uint64_t val = 0; 121 | uint32_t len = 0; 122 | 123 | 124 | //While we have data 125 | while (GetLeft()) 126 | { 127 | //Get curr value 128 | uint64_t cur = Get1(); 129 | 130 | //We only read the 7 least significant bits of each byte 131 | val |= (cur & 0x7f) << len; 132 | len += 7; 133 | 134 | // Most significant bit is 0, we're done 135 | if ((cur & 0x80) == 0) 136 | return val; 137 | } 138 | 139 | // If we got here, we read all bytes, but no one with 0 as MSB 140 | return std::numeric_limits::max(); 141 | } 142 | 143 | private: 144 | const uint8_t* data = nullptr; 145 | size_t size = 0; 146 | size_t pos = 0; 147 | }; 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /src/internal/BufferWritter.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDATACHANNELS_INTERNAL_WRITTER_H_ 2 | #define LIBDATACHANNELS_INTERNAL_WRITTER_H_ 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Buffer.h" 9 | 10 | class BufferWritter 11 | { 12 | public: 13 | BufferWritter(Buffer& buffer) 14 | { 15 | this->data = buffer.GetData(); 16 | this->size = buffer.GetCapacity(); 17 | this->pos = 0; 18 | } 19 | 20 | BufferWritter(uint8_t* data, size_t size) 21 | { 22 | this->data = data; 23 | this->size = size; 24 | this->pos = 0; 25 | } 26 | inline BufferWritter GetWritter(size_t num) { pos+=num; return GetWritter(pos-num, num); } 27 | inline BufferWritter GetWritter(size_t i,size_t num) { return BufferWritter(data+i, num); } 28 | 29 | template 30 | inline size_t Set(const std::array& array) { Set(pos,array); return pos+=N; } 31 | inline size_t Set(const Buffer& buffer) { Set(pos,buffer); return pos+=buffer.GetSize(); } 32 | inline size_t Set(const std::string& string) { Set(pos,string); return pos+=string.length(); } 33 | template 34 | inline size_t SetN(const std::array& array, size_t num) { auto n = std::min(num, N); SetN(pos, array, n); return pos += n; } 35 | inline size_t SetN(const Buffer& buffer, size_t num) { auto n = std::min(num, buffer.GetSize()); SetN(pos, buffer, n); return pos += n; } 36 | inline size_t SetN(const std::string& string, size_t num) { auto n = std::min(num, string.length()); SetN(pos, string, n); return pos += n; } 37 | inline size_t Set1(uint8_t val) { Set1(pos,val); return pos+=1; } 38 | inline size_t Set2(uint16_t val) { Set2(pos,val); return pos+=2; } 39 | inline size_t Set3(uint32_t val) { Set3(pos,val); return pos+=3; } 40 | inline size_t Set4(uint32_t val) { Set4(pos,val); return pos+=4; } 41 | inline size_t Set6(uint64_t val) { Set6(pos,val); return pos+=6; } 42 | inline size_t Set8(uint64_t val) { Set8(pos,val); return pos+=8; } 43 | inline size_t Set4Reversed(uint32_t val) { Set4Reversed(pos,val); return pos+=4; } 44 | 45 | 46 | template 47 | inline void Set(size_t i, const std::array& array) 48 | { 49 | memcpy(data+i,array.data(),N); 50 | } 51 | 52 | inline void Set(size_t i, const Buffer& buffer) 53 | { 54 | memcpy(data+i,buffer.GetData(),buffer.GetSize()); 55 | } 56 | 57 | inline void Set(size_t i, const std::string& string) 58 | { 59 | memcpy(data+i,string.data(),string.length()); 60 | } 61 | 62 | template 63 | inline void SetN(size_t i, const std::array& array, size_t num) 64 | { 65 | memcpy(data + i, array.data(), num); 66 | } 67 | 68 | inline void SetN(size_t i, const Buffer& buffer, size_t num) 69 | { 70 | memcpy(data + i, buffer.GetData(), num); 71 | } 72 | 73 | inline void SetN(size_t i, const std::string& string, size_t num) 74 | { 75 | memcpy(data + i, string.data(), num); 76 | } 77 | 78 | inline void Set1(size_t i, uint8_t val) 79 | { 80 | data[i] = val; 81 | } 82 | 83 | inline void Set2(size_t i, uint16_t val) 84 | { 85 | data[i+1] = (uint8_t)(val); 86 | data[i] = (uint8_t)(val>>8); 87 | } 88 | 89 | inline void Set3(size_t i, uint32_t val) 90 | { 91 | data[i+2] = (uint8_t)(val); 92 | data[i+1] = (uint8_t)(val>>8); 93 | data[i] = (uint8_t)(val>>16); 94 | } 95 | 96 | inline void Set4(size_t i, uint32_t val) 97 | { 98 | data[i+3] = (uint8_t)(val); 99 | data[i+2] = (uint8_t)(val>>8); 100 | data[i+1] = (uint8_t)(val>>16); 101 | data[i] = (uint8_t)(val>>24); 102 | } 103 | 104 | inline void Set4Reversed(size_t i, uint32_t val) 105 | { 106 | data[i] = (uint8_t)(val); 107 | data[i+1] = (uint8_t)(val>>8); 108 | data[i+2] = (uint8_t)(val>>16); 109 | data[i+3] = (uint8_t)(val>>24); 110 | } 111 | 112 | inline void Set6(size_t i, uint64_t val) 113 | { 114 | data[i+5] = (uint8_t)(val); 115 | data[i+4] = (uint8_t)(val>>8); 116 | data[i+3] = (uint8_t)(val>>16); 117 | data[i+2] = (uint8_t)(val>>24); 118 | data[i+1] = (uint8_t)(val>>32); 119 | data[i] = (uint8_t)(val>>40); 120 | } 121 | 122 | inline void Set8(size_t i, uint64_t val) 123 | { 124 | data[i+7] = (uint8_t)(val); 125 | data[i+6] = (uint8_t)(val>>8); 126 | data[i+5] = (uint8_t)(val>>16); 127 | data[i+4] = (uint8_t)(val>>24); 128 | data[i+3] = (uint8_t)(val>>32); 129 | data[i+2] = (uint8_t)(val>>40); 130 | data[i+1] = (uint8_t)(val>>48); 131 | data[i] = (uint8_t)(val>>56); 132 | } 133 | 134 | inline int EncodeLeb128(uint32_t value) 135 | { 136 | int size = 0; 137 | while (value >= 0x80) 138 | { 139 | Set1((value & 0x7F) | 0x80); 140 | value >>= 7; 141 | ++size; 142 | } 143 | Set1(value); 144 | ++size; 145 | return size; 146 | } 147 | 148 | size_t PadTo(size_t num, uint8_t val = 0) 149 | { 150 | while (pos % num && pos 5 | #include 6 | #include 7 | #include 8 | 9 | namespace sctp 10 | { 11 | 12 | //Random stuff 13 | std::random_device rd; 14 | std::mt19937 gen{rd()}; 15 | std::uniform_int_distribution dis{1, 4294967295}; 16 | 17 | Association::Association(datachannels::TimeService& timeService) : 18 | TimeServiceWrapper(timeService) 19 | { 20 | } 21 | 22 | Association::~Association() 23 | { 24 | //Reset timers 25 | ResetTimers(); 26 | } 27 | 28 | void Association::ResetTimers() 29 | { 30 | //Reset any pending timer 31 | if (sackTimer) 32 | { 33 | //Cancel it 34 | sackTimer->Cancel(); 35 | //Reset it 36 | sackTimer = nullptr; 37 | } 38 | if (cookieEchoTimer) 39 | { 40 | //Cancel it 41 | cookieEchoTimer->Cancel(); 42 | //Reset it 43 | cookieEchoTimer = nullptr; 44 | } 45 | if (initTimer) 46 | { 47 | //Cancel it 48 | initTimer->Cancel(); 49 | //Reset it 50 | initTimer = nullptr; 51 | } 52 | } 53 | 54 | void Association::SetState(State state) 55 | { 56 | this->state = state; 57 | } 58 | 59 | bool Association::Associate() 60 | { 61 | //Check state 62 | if (state!=State::Closed) 63 | //Error 64 | return false; 65 | 66 | // "A" first sends an INIT chunk to "Z". In the INIT, "A" must 67 | // provide its Verification Tag (Tag_A) in the Initiate Tag field. 68 | // Tag_A SHOULD be a random number in the range of 1 to 4294967295 69 | // (see Section 5.3.1 for Tag value selection). After sending the 70 | // INIT, "A" starts the T1-init timer and enters the COOKIE-WAIT 71 | // state. 72 | 73 | //Create new verification tag 74 | localVerificationTag = dis(gen); 75 | 76 | //Reset init retransmissions 77 | initRetransmissions = 0; 78 | 79 | //Enqueue new INIT chunk 80 | auto init = std::make_shared(); 81 | 82 | //Set params 83 | init->initiateTag = localVerificationTag; 84 | init->advertisedReceiverWindowCredit = 0; 85 | init->numberOfOutboundStreams = 0xFFFF; 86 | init->numberOfInboundStreams = 0xFFFF; 87 | init->initialTransmissionSequenceNumber = 0; 88 | 89 | // draft-ietf-rtcweb-data-channel-13 90 | // The INIT and INIT-ACK chunk MUST NOT contain any IPv4 Address or 91 | // IPv6 Address parameters. The INIT chunk MUST NOT contain the 92 | // Supported Address Types parameter. 93 | 94 | // draft-ietf-rtcweb-data-channel-13#page-7 95 | // The dynamic address reconfiguration extension defined in [RFC5061] 96 | // MUST be used to signal the support of the stream reset extension 97 | // defined in [RFC6525]. Other features of [RFC5061] are OPTIONAL. 98 | init->supportedExtensions.push_back(Chunk::Type::RE_CONFIG); 99 | 100 | //Set timer 101 | initTimer = CreateTimerSafe(InitRetransmitTimeout,[=](...){ 102 | //Retransmit init chunk 103 | if (initRetransmissions++(init)); 107 | //Retry again 108 | initTimer->Again(InitRetransmitTimeout); 109 | } else { 110 | //Close 111 | SetState(State::Closed); 112 | } 113 | }); 114 | 115 | //Change state 116 | SetState(State::CookieWait); 117 | 118 | //Enquee 119 | Enqueue(std::static_pointer_cast(init)); 120 | 121 | //Done 122 | return true; 123 | } 124 | 125 | bool Association::Shutdown() 126 | { 127 | //Reset timers 128 | ResetTimers(); 129 | //TODO: Shutdown 130 | return true; 131 | } 132 | 133 | bool Association::Abort() 134 | { 135 | //Reset timers 136 | ResetTimers(); 137 | //TODO: Abort 138 | return true; 139 | } 140 | 141 | size_t Association::WritePacket(uint8_t *data, uint32_t size) 142 | { 143 | //Create reader 144 | BufferReader reader(data,size); 145 | 146 | //TODO: Check crc 147 | 148 | //Parse packet header 149 | auto header = PacketHeader::Parse(reader); 150 | 151 | //Ensure it was correctly parsed 152 | if (!header) 153 | //Error 154 | return false; 155 | 156 | //Check correct local and remote port 157 | if (header->sourcePortNumber!=remotePort || header->destinationPortNumber!=localPort || header->verificationTag!=localVerificationTag) 158 | //Error 159 | return false; 160 | 161 | //Read chunks 162 | while (reader.GetLeft()>4) 163 | { 164 | //Parse chunk 165 | auto chunk = Chunk::Parse(reader); 166 | //Check 167 | if (!chunk) 168 | //Error 169 | return false; 170 | //Process it 171 | Process(chunk); 172 | } 173 | 174 | //If we need to acknowledge 175 | if (pendingAcknowledge) 176 | { 177 | //If we have to do it now 178 | if (pendingAcknowledgeTimeout == 0ms) 179 | //Acknoledge now 180 | Acknowledge(); 181 | //rfc4960#page-78 182 | // Specifically, an 183 | // acknowledgement SHOULD be generated for at least every second packet 184 | // (not every second DATA chunk) received, and SHOULD be generated 185 | // within 200 ms of the arrival of any unacknowledged DATA chunk. 186 | 187 | //If there was already a timeout 188 | else if (sackTimer.get()) 189 | //We should do sack now 190 | Acknowledge(); 191 | else 192 | //Schedule timer 193 | sackTimer = CreateTimerSafe(pendingAcknowledgeTimeout,[this](...){ 194 | //In the future 195 | Acknowledge(); 196 | }); 197 | } 198 | 199 | //Done 200 | return true; 201 | } 202 | 203 | size_t Association::ReadPacket(uint8_t *data, uint32_t size) 204 | { 205 | //Check there is pending data 206 | if (!pendingData) 207 | //Nothing to do 208 | return 0; 209 | 210 | //Create buffer writter 211 | BufferWritter writter(data,size); 212 | 213 | //Create new packet header 214 | PacketHeader header(localPort,remotePort,remoteVerificationTag); 215 | 216 | //Serialize it 217 | if (!header.Serialize(writter)) 218 | //Error 219 | return 0; 220 | 221 | size_t num = 0; 222 | 223 | //Fill chunks from control queue first 224 | for (auto it=queue.begin();it!=queue.end();) 225 | { 226 | //Get chunk 227 | auto chunk = *it; 228 | 229 | //Ensure we have enought space for chunk 230 | if (writter.GetLeft()GetSize()) 231 | //We cant send more on this packet 232 | break; 233 | 234 | //Check if it must be sent alone 235 | if (chunk->type==Chunk::Type::INIT || chunk->type==Chunk::Type::INIT_ACK || chunk->type==Chunk::Type::COOKIE_ECHO) 236 | { 237 | //IF it is not firest 238 | if (num) 239 | //Flush all before this 240 | break; 241 | } 242 | 243 | //Remove from queue and move to next chunk 244 | it = queue.erase(it); 245 | 246 | //Serialize chunk 247 | chunk->Serialize(writter); 248 | 249 | //Check if it must be sent alone 250 | if (chunk->type==Chunk::Type::INIT || chunk->type==Chunk::Type::INIT_ACK || chunk->type==Chunk::Type::COOKIE_ECHO) 251 | //Send alone 252 | break; 253 | } 254 | 255 | //TODO:Check in which stata data can be sent 256 | //TODO:Now fill data chunks from streams 257 | 258 | //Get length 259 | size_t length = writter.GetLength(); 260 | //Calculate crc 261 | header.checksum = crc32c::Crc32c(data,length); 262 | //Go to the begining 263 | writter.GoTo(0); 264 | 265 | //Serialize it now with checksum 266 | header.Serialize(writter); 267 | 268 | //Check if there is more data to send 269 | if (!queue.size()) 270 | //No 271 | pendingData = false; 272 | //Done 273 | return length; 274 | } 275 | 276 | void Association::Process(const Chunk::shared& chunk) 277 | { 278 | //Depending onthe state 279 | switch (state) 280 | { 281 | case State::Closed: 282 | { 283 | switch(chunk->type) 284 | { 285 | case Chunk::Type::INIT: 286 | { 287 | //Get chunk of correct type 288 | auto init = std::static_pointer_cast(chunk); 289 | 290 | //rfc4960#page-55 291 | // "Z" shall respond immediately with an INIT ACK chunk. The 292 | // destination IP address of the INIT ACK MUST be set to the source 293 | // IP address of the INIT to which this INIT ACK is responding. In 294 | // the response, besides filling in other parameters, "Z" must set 295 | // the Verification Tag field to Tag_A, and also provide its own 296 | // Verification Tag (Tag_Z) in the Initiate Tag field. 297 | // 298 | 299 | //Get remote verification tag 300 | remoteVerificationTag = init->initiateTag; 301 | 302 | //Create new verification tag 303 | localVerificationTag = dis(gen); 304 | 305 | //Reset init retransmissions 306 | initRetransmissions = 0; 307 | 308 | //Enqueue new INIT chunk 309 | auto initAck = std::make_shared(); 310 | 311 | //Set params 312 | initAck->initiateTag = localVerificationTag; 313 | initAck->advertisedReceiverWindowCredit = localAdvertisedReceiverWindowCredit; 314 | initAck->numberOfOutboundStreams = 0xFFFF; 315 | initAck->numberOfInboundStreams = 0xFFFF; 316 | initAck->initialTransmissionSequenceNumber = 0; 317 | 318 | // draft-ietf-rtcweb-data-channel-13 319 | // The INIT and INIT-ACK chunk MUST NOT contain any IPv4 Address or 320 | // IPv6 Address parameters. The INIT chunk MUST NOT contain the 321 | // Supported Address Types parameter. 322 | 323 | // draft-ietf-rtcweb-data-channel-13#page-7 324 | // The dynamic address reconfiguration extension defined in [RFC5061] 325 | // MUST be used to signal the support of the stream reset extension 326 | // defined in [RFC6525]. Other features of [RFC5061] are OPTIONAL. 327 | initAck->supportedExtensions.push_back(Chunk::Type::RE_CONFIG); 328 | 329 | //rfc4960#page-55 330 | // Moreover, "Z" MUST generate and send along with the INIT ACK a 331 | // State Cookie. See Section 5.1.3 for State Cookie generation. 332 | 333 | // AAA is ensured by the DTLS & ICE layer, so createing a complex cookie is unnecessary IMHO 334 | initAck->stateCookie.SetData((uint8_t*)"dtls",strlen("dtls")); 335 | 336 | //Send back unkown parameters 337 | for (const auto& unknown : init->unknownParameters) 338 | //Copy as unrecognized 339 | initAck->unrecognizedParameters.push_back(unknown.second.Clone()); 340 | 341 | ///Enquee 342 | Enqueue(std::static_pointer_cast(initAck)); 343 | 344 | break; 345 | } 346 | case Chunk::Type::COOKIE_ECHO: 347 | { 348 | //rfc4960#page-55 349 | // D) Upon reception of the COOKIE ECHO chunk, endpoint "Z" will reply 350 | // with a COOKIE ACK chunk after building a TCB and moving to the 351 | // ESTABLISHED state. A COOKIE ACK chunk may be bundled with any 352 | // pending DATA chunks (and/or SACK chunks), but the COOKIE ACK chunk 353 | // MUST be the first chunk in the packet. 354 | 355 | //Enqueue new INIT chunk 356 | auto cookieAck = std::make_shared(); 357 | 358 | //We don't check the cookie for seame reasons as we don't create one 359 | 360 | ///Enquee 361 | Enqueue(std::static_pointer_cast(cookieAck)); 362 | 363 | //Change state 364 | SetState(State::Established); 365 | } 366 | default: 367 | //Error 368 | break; 369 | } 370 | break; 371 | } 372 | case State::CookieWait: 373 | { 374 | switch(chunk->type) 375 | { 376 | case Chunk::Type::INIT_ACK: 377 | { 378 | //Get chunk of correct type 379 | auto initAck = std::static_pointer_cast(chunk); 380 | 381 | // C) Upon reception of the INIT ACK from "Z", "A" shall stop the T1- 382 | // init timer and leave the COOKIE-WAIT state. "A" shall then send 383 | // the State Cookie received in the INIT ACK chunk in a COOKIE ECHO 384 | // chunk, start the T1-cookie timer, and enter the COOKIE-ECHOED 385 | // state. 386 | // 387 | 388 | // Stop timer 389 | initTimer->Cancel(); 390 | 391 | //Enqueue new INIT chunk 392 | auto cookieEcho = std::make_shared(); 393 | 394 | //Copy cookie 395 | cookieEcho->cookie.SetData(initAck->stateCookie); 396 | 397 | //Reset cookie retransmissions 398 | initRetransmissions = 0; 399 | 400 | //Set timer 401 | cookieEchoTimer = CreateTimerSafe(100ms,[=](...){ 402 | //3) If the T1-cookie timer expires, the endpoint MUST retransmit 403 | // COOKIE ECHO and restart the T1-cookie timer without changing 404 | // state. This MUST be repeated up to 'Max.Init.Retransmits' times. 405 | // After that, the endpoint MUST abort the initialization process 406 | // and report the error to the SCTP user. 407 | if (initRetransmissions++(cookieEcho)); 411 | //Retry again 412 | initTimer->Again(100ms); 413 | } else { 414 | //Close 415 | SetState(State::Closed); 416 | } 417 | }); 418 | 419 | ///Enquee 420 | Enqueue(std::static_pointer_cast(cookieEcho)); 421 | 422 | //Set new state 423 | SetState(State::CookieEchoed); 424 | } 425 | default: 426 | //Error 427 | break; 428 | } 429 | break; 430 | } 431 | case State::CookieEchoed: 432 | { 433 | switch(chunk->type) 434 | { 435 | case Chunk::Type::COOKIE_ACK: 436 | { 437 | // E) Upon reception of the COOKIE ACK, endpoint "A" will move from the 438 | // COOKIE-ECHOED state to the ESTABLISHED state, stopping the T1- 439 | // cookie timer. 440 | 441 | // Stop timer 442 | cookieEchoTimer->Cancel(); 443 | 444 | //Change state 445 | SetState(State::Established); 446 | } 447 | default: 448 | //Error 449 | break; 450 | } 451 | break; 452 | } 453 | case State::Established: 454 | { 455 | switch(chunk->type) 456 | { 457 | case Chunk::Type::PDATA: 458 | { 459 | //Get chunk of correct type 460 | auto pdata = std::static_pointer_cast(chunk); 461 | 462 | // After the reception of the first DATA chunk in an association the 463 | // endpoint MUST immediately respond with a SACK to acknowledge the DATA 464 | // chunk. Subsequent acknowledgements should be done as described in 465 | bool first = lastReceivedTransmissionSequenceNumber == MaxTransmissionSequenceNumber; 466 | 467 | //Get tsn 468 | auto tsn = receivedTransmissionSequenceNumberWrapper.Wrap(pdata->transmissionSequenceNumber); 469 | 470 | //Storea tsn, if the container has elements with equivalent key, inserts at the upper bound of that range 471 | auto it = receivedTransmissionSequenceNumbers.insert(tsn); 472 | 473 | // When a packet arrives with duplicate DATA chunk(s) and with no new 474 | // DATA chunk(s), the endpoint MUST immediately send a SACK with no 475 | // delay. If a packet arrives with duplicate DATA chunk(s) bundled with 476 | // new DATA chunks, the endpoint MAY immediately send a SACK. 477 | bool duplicated = it != receivedTransmissionSequenceNumbers.begin() && *(--it)!=tsn; 478 | 479 | //rfc4960#page-89 480 | // Upon the reception of a new DATA chunk, an endpoint shall examine the 481 | // continuity of the TSNs received. If the endpoint detects a gap in 482 | // the received DATA chunk sequence, it SHOULD send a SACK with Gap Ack 483 | // Blocks immediately. The data receiver continues sending a SACK after 484 | // receipt of each SCTP packet that doesn't fill the gap. 485 | bool hasGaps = false; 486 | 487 | //Iterate the received transmission numbers 488 | uint64_t prev = MaxTransmissionSequenceNumber; 489 | for (auto curr : receivedTransmissionSequenceNumbers) 490 | { 491 | //Check if not first or if ther is a jump bigger than 1 seq num 492 | if (prev!=MaxTransmissionSequenceNumber && curr>prev+1) 493 | { 494 | //It has a gap at least 495 | hasGaps = true; 496 | break; 497 | } 498 | //Next 499 | prev = curr; 500 | } 501 | 502 | //rfc4960#page-78 503 | // When the receiver's advertised window is 0, the receiver MUST drop 504 | // any new incoming DATA chunk with a TSN larger than the largest TSN 505 | // received so far. If the new incoming DATA chunk holds a TSN value 506 | // less than the largest TSN received so far, then the receiver SHOULD 507 | // drop the largest TSN held for reordering and accept the new incoming 508 | // DATA chunk. In either case, if such a DATA chunk is dropped, the 509 | // receiver MUST immediately send back a SACK with the current receive 510 | // window showing only DATA chunks received and accepted so far. The 511 | // dropped DATA chunk(s) MUST NOT be included in the SACK, as they were 512 | // not accepted. 513 | 514 | //We need to acknoledfe 515 | pendingAcknowledge = true; 516 | 517 | //If we need to send it now 518 | if (first || hasGaps || duplicated || sackTimer) 519 | //Acknoledge now 520 | pendingAcknowledgeTimeout = 0ms; 521 | else 522 | //Create timer 523 | pendingAcknowledgeTimeout = SackTimeout; 524 | break; 525 | } 526 | case Chunk::Type::SACK: 527 | { 528 | break; 529 | } 530 | } 531 | break; 532 | } 533 | case State::ShutdownPending: 534 | { 535 | break; 536 | } 537 | case State::ShutDownSent: 538 | { 539 | break; 540 | } 541 | case State::ShutDownReceived: 542 | { 543 | break; 544 | } 545 | case State::ShutDown: 546 | { 547 | break; 548 | } 549 | case State::ShutDownAckSent: 550 | { 551 | break; 552 | } 553 | } 554 | } 555 | 556 | 557 | void Association::Acknowledge() 558 | { 559 | //New sack message 560 | auto sack = std::make_shared(); 561 | 562 | //rfc4960#page-34 563 | // By definition, the value of the Cumulative TSN Ack parameter is the 564 | // last TSN received before a break in the sequence of received TSNs 565 | // occurs; the next TSN value following this one has not yet been 566 | // received at the endpoint sending the SACK. This parameter therefore 567 | // acknowledges receipt of all TSNs less than or equal to its value. 568 | 569 | //rfc4960#page-34 570 | // The SACK also contains zero or more Gap Ack Blocks. Each Gap Ack 571 | // Block acknowledges a subsequence of TSNs received following a break 572 | // in the sequence of received TSNs. By definition, all TSNs 573 | // acknowledged by Gap Ack Blocks are greater than the value of the 574 | // Cumulative TSN Ack. 575 | 576 | //rfc4960#page-78 577 | // Any received DATA chunks with TSN 578 | // greater than the value in the Cumulative TSN Ack field are reported 579 | // in the Gap Ack Block fields. The SCTP endpoint MUST report as many 580 | // Gap Ack Blocks as can fit in a single SACK chunk limited by the 581 | // current path MTU. 582 | 583 | //Iterate the received transmission numbers 584 | uint64_t prev = MaxTransmissionSequenceNumber; 585 | uint64_t gap = MaxTransmissionSequenceNumber; 586 | for (auto it = receivedTransmissionSequenceNumbers.begin();it!=receivedTransmissionSequenceNumbers.end();) 587 | { 588 | //Get current 589 | uint64_t current = *it; 590 | //If we have a continous sequence number or it is the first one 591 | if (lastReceivedTransmissionSequenceNumber==MaxTransmissionSequenceNumber || current==lastReceivedTransmissionSequenceNumber+1) 592 | { 593 | //Update last received tsn 594 | lastReceivedTransmissionSequenceNumber = current; 595 | } 596 | //Check if is duplicated 597 | else if (prev==current) 598 | { 599 | //It is duplicated 600 | sack->duplicateTuplicateTrasnmissionSequenceNumbers.push_back(current); 601 | } 602 | //If it is a gap 603 | else if (prev!=MaxTransmissionSequenceNumber && current>prev+1) 604 | { 605 | //If we had a gap start 606 | if (gap) 607 | //Add block ending at previous one 608 | sack->gapAckBlocks.push_back({ 609 | static_cast(gap-lastReceivedTransmissionSequenceNumber), 610 | static_cast(prev-lastReceivedTransmissionSequenceNumber) 611 | }); 612 | //Start new gap 613 | gap = current; 614 | } 615 | 616 | //Move next 617 | prev = current; 618 | 619 | //Remove everything up to the cumulative sequence number 620 | if (lastReceivedTransmissionSequenceNumber==current) 621 | //Erase and move 622 | it = receivedTransmissionSequenceNumbers.erase(it); 623 | else 624 | //Next 625 | ++it; 626 | } 627 | 628 | //If we had a gap start 629 | if (gap!=MaxTransmissionSequenceNumber) 630 | //Add block ending at last one 631 | sack->gapAckBlocks.push_back({ 632 | static_cast(gap-lastReceivedTransmissionSequenceNumber), 633 | static_cast(prev-lastReceivedTransmissionSequenceNumber) 634 | }); 635 | 636 | //Set last consecutive recevied number 637 | sack->cumulativeTrasnmissionSequenceNumberAck = receivedTransmissionSequenceNumberWrapper.UnWrap(lastReceivedTransmissionSequenceNumber); 638 | 639 | //Set window 640 | sack->adveritsedReceiverWindowCredit = localAdvertisedReceiverWindowCredit; 641 | 642 | //Send it 643 | Enqueue(sack); 644 | 645 | //No need to acknoledge 646 | pendingAcknowledge = false; 647 | 648 | //Reset any pending sack timer 649 | if (sackTimer) 650 | { 651 | //Cancel it 652 | sackTimer->Cancel(); 653 | //Reset it 654 | sackTimer = nullptr; 655 | } 656 | } 657 | 658 | void Association::Enqueue(const Chunk::shared& chunk) 659 | { 660 | bool wasPending = pendingData; 661 | //Push back 662 | queue.push_back(chunk); 663 | //Reset flag 664 | pendingData = true; 665 | //If it is first 666 | if (!wasPending && onPendingData) 667 | //Call callback 668 | onPendingData(); 669 | } 670 | 671 | }; //namespace sctp 672 | -------------------------------------------------------------------------------- /src/sctp/Association.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_ASSOCIATION_H_ 2 | #define SCTP_ASSOCIATION_H_ 3 | #include 4 | #include 5 | #include 6 | 7 | #include "Datachannels.h" 8 | #include "sctp/SequenceNumberWrapper.h" 9 | #include "sctp/PacketHeader.h" 10 | #include "sctp/Stream.h" 11 | #include "BufferWritter.h" 12 | #include "BufferReader.h" 13 | 14 | using namespace std::chrono_literals; 15 | 16 | namespace sctp 17 | { 18 | 19 | class Association : public datachannels::Transport, public TimeServiceWrapper 20 | { 21 | private: 22 | using TransmissionSequenceNumberWrapper = SequenceNumberWrapper; 23 | static constexpr const uint64_t MaxTransmissionSequenceNumber = TransmissionSequenceNumberWrapper::MaxSequenceNumber; 24 | public: 25 | enum State 26 | { 27 | Closed, 28 | CookieWait, 29 | CookieEchoed, 30 | Established, 31 | ShutdownPending, 32 | ShutDownSent, 33 | ShutDownReceived, 34 | ShutDown, 35 | ShutDownAckSent 36 | }; 37 | private: 38 | // Private constructor to prevent creating without TimeServiceWrapper::Create() factory 39 | friend class TimeServiceWrapper; 40 | Association(datachannels::TimeService& timeService); 41 | 42 | public: 43 | virtual ~Association(); 44 | 45 | bool Associate(); 46 | bool Shutdown(); 47 | bool Abort(); 48 | 49 | void SetLocalPort(uint16_t port) { localPort = port; } 50 | void SetRemotePort(uint16_t port) { remotePort = port; } 51 | uint16_t GetLocalPort() const { return localPort; } 52 | uint16_t GetRemotePort() const { return remotePort; } 53 | State GetState() const { return state; } 54 | bool HasPendingData() const { return pendingData; } 55 | 56 | 57 | virtual size_t ReadPacket(uint8_t *data, uint32_t size) override; 58 | virtual size_t WritePacket(uint8_t *data, uint32_t size) override; 59 | 60 | inline size_t ReadPacket(Buffer& buffer) 61 | { 62 | size_t len = ReadPacket(buffer.GetData(),buffer.GetCapacity()); 63 | buffer.SetSize(len); 64 | return len; 65 | } 66 | inline size_t WritePacket(Buffer& buffer) 67 | { 68 | return WritePacket(buffer.GetData(),buffer.GetSize()); 69 | } 70 | 71 | virtual void OnPendingData(std::function callback) override 72 | { 73 | onPendingData = callback; 74 | } 75 | 76 | static constexpr const size_t MaxInitRetransmits = 10; 77 | static constexpr const std::chrono::milliseconds InitRetransmitTimeout = 100ms; 78 | static constexpr const std::chrono::milliseconds SackTimeout = 100ms; 79 | private: 80 | void Process(const Chunk::shared& chunk); 81 | void SetState(State state); 82 | void Enqueue(const Chunk::shared& chunk); 83 | void Acknowledge(); 84 | void ResetTimers(); 85 | private: 86 | State state = State::Closed; 87 | std::list queue; 88 | 89 | uint16_t localPort = 0; 90 | uint16_t remotePort = 0; 91 | uint32_t localAdvertisedReceiverWindowCredit = 0xFFFFFFFF; 92 | uint32_t remoteAdvertisedReceiverWindowCredit = 0; 93 | uint32_t localVerificationTag = 0; 94 | uint32_t remoteVerificationTag = 0; 95 | uint32_t initRetransmissions = 0; 96 | 97 | bool pendingAcknowledge = false; 98 | std::chrono::milliseconds pendingAcknowledgeTimeout = 0ms; 99 | 100 | datachannels::Timer::shared initTimer; 101 | datachannels::Timer::shared cookieEchoTimer; 102 | datachannels::Timer::shared sackTimer; 103 | 104 | size_t numberOfPacketsWithoutAcknowledge = 0; 105 | TransmissionSequenceNumberWrapper receivedTransmissionSequenceNumberWrapper; 106 | uint64_t lastReceivedTransmissionSequenceNumber = MaxTransmissionSequenceNumber; 107 | 108 | bool pendingData = false; 109 | std::function onPendingData; 110 | std::multiset receivedTransmissionSequenceNumbers; 111 | std::map streams; 112 | }; 113 | 114 | } 115 | #endif 116 | -------------------------------------------------------------------------------- /src/sctp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(libdatachannels PUBLIC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/Association.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/PacketHeader.cpp 4 | ${CMAKE_CURRENT_SOURCE_DIR}/Stream.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/Chunk.cpp 6 | ) 7 | 8 | add_subdirectory (chunks) 9 | -------------------------------------------------------------------------------- /src/sctp/Chunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/Chunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | Chunk::shared Chunk::Parse(BufferReader& reader) 7 | { 8 | //Ensure we have at laast the header 9 | if (!reader.Assert(4)) 10 | //Error 11 | return nullptr; 12 | 13 | // Peek type 14 | switch((Type)reader.Peek1()) 15 | { 16 | case Type::PDATA: 17 | return PayloadDataChunk::Parse(reader); 18 | case Type::INIT: 19 | return InitiationChunk::Parse(reader); 20 | case Type::INIT_ACK: 21 | return InitiationAcknowledgementChunk::Parse(reader); 22 | case Type::SACK: 23 | return SelectiveAcknowledgementChunk::Parse(reader); 24 | case Type::HEARTBEAT: 25 | return HeartbeatRequestChunk::Parse(reader); 26 | case Type::HEARTBEAT_ACK: 27 | return HeartbeatAckChunk::Parse(reader); 28 | case Type::ABORT: 29 | return AbortAssociationChunk::Parse(reader); 30 | case Type::SHUTDOWN: 31 | return ShutdownAssociationChunk::Parse(reader); 32 | case Type::SHUTDOWN_ACK: 33 | return ShutdownAcknowledgementChunk::Parse(reader); 34 | case Type::ERROR: 35 | return OperationErrorChunk::Parse(reader); 36 | case Type::COOKIE_ECHO: 37 | return CookieEchoChunk::Parse(reader); 38 | case Type::COOKIE_ACK: 39 | return CookieAckChunk::Parse(reader); 40 | case Type::ECNE: 41 | return UnknownChunk::Parse(reader); 42 | case Type::CWR: 43 | return UnknownChunk::Parse(reader); 44 | case Type::SHUTDOWN_COMPLETE: 45 | return ShutdownCompleteChunk::Parse(reader); 46 | case Type::RE_CONFIG: 47 | return ReConfigChunk::Parse(reader); 48 | case Type::FORWARD_CUMULATIVE_TSN: 49 | return ForwardCumulativeTSNChunk::Parse(reader); 50 | } 51 | 52 | return UnknownChunk::Parse(reader); 53 | } 54 | 55 | }; 56 | -------------------------------------------------------------------------------- /src/sctp/Chunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_CHUNK_H_ 2 | #define SCTP_CHUNK_H_ 3 | #include 4 | #include 5 | 6 | #include "Buffer.h" 7 | #include "BufferReader.h" 8 | #include "BufferWritter.h" 9 | 10 | namespace sctp 11 | { 12 | 13 | inline size_t SizePad(size_t size, size_t num) 14 | { 15 | return ((size + num -1) / num ) * num; 16 | } 17 | 18 | class Chunk 19 | { 20 | public: 21 | using shared = std::shared_ptr; 22 | 23 | enum Type 24 | { 25 | PDATA = 0, // Payload Data (DATA) 26 | INIT = 1, // Initiation (INIT) 27 | INIT_ACK = 2, // Initiation Acknowledgement (INIT ACK) 28 | SACK = 3, // Selective Acknowledgement (SACK) 29 | HEARTBEAT = 4, // Heartbeat Request () 30 | HEARTBEAT_ACK = 5, // Heartbeat Acknowledgement (HEARTBEAT ACK) 31 | ABORT = 6, // Abort (ABORT) 32 | SHUTDOWN = 7, // Shutdown (SHUTDOWN) 33 | SHUTDOWN_ACK = 8, // Shutdown Acknowledgement (SHUTDOWN ACK) 34 | ERROR = 9, // Operation Error (ERROR) 35 | COOKIE_ECHO = 10, // State Cookie (COOKIE ECHO) 36 | COOKIE_ACK = 11, // Cookie Acknowledgement (COOKIE ACK), 37 | ECNE = 12, // Reserved for Explicit Congestion Notification Echo 38 | CWR = 13, // Reserved for Congestion Window Reduced (CWR) 39 | SHUTDOWN_COMPLETE = 14, // Shutdown Complete 40 | // 15 to 62 - available 41 | // 63 - reserved for IETF-defined Chunk Extensions 42 | // 64 to 126 - available 43 | PAD = 84, 44 | // 127 - reserved for IETF-defined Chunk Extensions 45 | // 128 to 190 - available 46 | RE_CONFIG = 130, // Re-configuration Chunk (RE-CONFIG) rfc6525 47 | // 191 - reserved for IETF-defined Chunk Extensions 48 | // 192 to 254 - available 49 | FORWARD_CUMULATIVE_TSN = 192, 50 | // 255 - reserved for IETF-defined Chunk Extensions 51 | }; 52 | 53 | enum Parameter 54 | { 55 | 56 | HeartbeatInfo = 1, 57 | IPv4Address = 5, 58 | IPv6Address = 6, 59 | StateCookie = 7, 60 | UnrecognizedParameter = 8, 61 | CookiePreservative = 9, 62 | ReservedforECNCapable = 32768, 63 | HostNameAddress = 11, 64 | SupportedAddressTypes = 12, 65 | OutgoingSSNResetRequestParameter = 13, 66 | IncomingSSNResetRequestParameter = 14, 67 | SSNTSNResetRequestParameter = 15, 68 | ReCconfigurationResponseParameter = 16, 69 | AddOutgoingStreamsRequestParameter = 17, 70 | AddIncomingStreamsRequestParameter = 18, 71 | SupportedExtensions = 0x8008, //rfc5061 72 | ForwardTSNSupported = 49152, //rfc3758 (0xC000) 73 | Padding = 0x8005, //rfc480 74 | }; 75 | 76 | Chunk(uint8_t type) 77 | { 78 | this->type = type; 79 | } 80 | virtual ~Chunk() = default; 81 | 82 | static Chunk::shared Parse(BufferReader& buffer); 83 | virtual size_t GetSize() const = 0; 84 | virtual size_t Serialize(BufferWritter& buffer) const = 0; 85 | 86 | public: 87 | // Chunk Types are encoded such that the highest-order 2 bits specify 88 | // the action that must be taken if the processing endpoint does not 89 | // recognize the Chunk Type. 90 | // 91 | // 00 - Stop processing this SCTP packet and discard it, do not 92 | // process any further chunks within it. 93 | // 94 | // 01 - Stop processing this SCTP packet and discard it, do not 95 | // process any further chunks within it, and report the 96 | // unrecognized chunk in an 'Unrecognized Chunk Type'. 97 | // 98 | // 10 - Skip this chunk and continue processing. 99 | // 100 | // 11 - Skip this chunk and continue processing, but report in an 101 | // ERROR chunk using the 'Unrecognized Chunk Type' cause of 102 | static bool SkipOnUnknown(uint8_t type) { return type & 0x80; } 103 | static bool ReportOnUnknown(uint8_t type) { return type & 0x40; } 104 | 105 | public: 106 | uint8_t type; 107 | uint8_t flag = 0; 108 | }; 109 | 110 | }; // namespace sctp 111 | 112 | #include "sctp/chunks/AbortAssociationChunk.h" 113 | #include "sctp/chunks/HeartbeatRequestChunk.h" 114 | #include "sctp/chunks/HeartbeatAckChunk.h" 115 | #include "sctp/chunks/OperationErrorChunk.h" 116 | #include "sctp/chunks/SelectiveAcknowledgementChunk.h" 117 | #include "sctp/chunks/ShutdownCompleteChunk.h" 118 | #include "sctp/chunks/CookieAckChunk.h" 119 | #include "sctp/chunks/InitiationAcknowledgementChunk.h" 120 | #include "sctp/chunks/PayloadDataChunk.h" 121 | #include "sctp/chunks/ShutdownAcknowledgementChunk.h" 122 | #include "sctp/chunks/CookieEchoChunk.h" 123 | #include "sctp/chunks/InitiationChunk.h" 124 | #include "sctp/chunks/ReConfigChunk.h" 125 | #include "sctp/chunks/ShutdownAssociationChunk.h" 126 | #include "sctp/chunks/ForwardCumulativeTSNChunk.h" 127 | #include "sctp/chunks/UnknownChunk.h" 128 | #include "sctp/chunks/PaddingChunk.h" 129 | 130 | 131 | 132 | #endif 133 | -------------------------------------------------------------------------------- /src/sctp/ErrorCause.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_ERROR_CAUSE_H_ 2 | #define SCTP_ERROR_CAUSE_H_ 3 | #include 4 | 5 | #include "Buffer.h" 6 | 7 | namespace sctp 8 | { 9 | 10 | class ErrorCause 11 | { 12 | public: 13 | enum Cause 14 | { 15 | InvalidStreamIdentifier = 1, 16 | MissingMandatoryParameter = 2, 17 | StaleCookieError = 3, 18 | OutofResource = 4, 19 | UnresolvableAddress = 5, 20 | UnrecognizedChunkType = 6, 21 | InvalidMandatoryParameter = 7, 22 | UnrecognizedParameters = 8, 23 | NoUserData = 9, 24 | CookieReceivedWhileShuttingDown = 10, 25 | RestartofanAssociationwithNewAddresses = 11, 26 | UserInitiatedAbort = 12, 27 | ProtocolViolation = 13, 28 | }; 29 | public: 30 | uint16_t code; 31 | Buffer info; 32 | }; 33 | 34 | }; // namespace sctp 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /src/sctp/PacketHeader.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/PacketHeader.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | PacketHeader::PacketHeader(uint16_t sourcePortNumber,uint16_t destinationPortNumber,uint32_t verificationTag, uint32_t checksum) 7 | { 8 | this->sourcePortNumber = sourcePortNumber; 9 | this->destinationPortNumber = destinationPortNumber; 10 | this->verificationTag = verificationTag; 11 | this->checksum = checksum; 12 | } 13 | 14 | PacketHeader::shared PacketHeader::Parse(BufferReader& reader) 15 | { 16 | //Check size 17 | if (!reader.Assert(8)) return nullptr; 18 | 19 | //Get header 20 | uint16_t sourcePortNumber = reader.Get2(); 21 | uint16_t destinationPortNumber = reader.Get2(); 22 | uint32_t verificationTag = reader.Get4(); 23 | uint32_t checksum = reader.Get4Reversed(); 24 | 25 | //Create PacketHeader 26 | auto header = std::make_shared(sourcePortNumber,destinationPortNumber,verificationTag,checksum); 27 | 28 | //Done 29 | return header; 30 | } 31 | 32 | size_t PacketHeader::GetSize() const 33 | { 34 | //ports + tag + checksum 35 | return 12; 36 | } 37 | 38 | size_t PacketHeader::Serialize(BufferWritter& writter) const 39 | { 40 | //Check size 41 | if (!writter.Assert(12)) return 0; 42 | 43 | //Set header 44 | writter.Set2(sourcePortNumber); 45 | writter.Set2(destinationPortNumber); 46 | writter.Set4(verificationTag); 47 | writter.Set4Reversed(checksum); 48 | 49 | //Done 50 | return writter.GetLength(); 51 | } 52 | 53 | }; //namespace sctp 54 | -------------------------------------------------------------------------------- /src/sctp/PacketHeader.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_PACKET_HEADER_H_ 2 | #define SCTP_PACKET_HEADER_H_ 3 | #include 4 | #include 5 | #include 6 | #include "BufferWritter.h" 7 | #include "BufferReader.h" 8 | #include "sctp/Chunk.h" 9 | 10 | namespace sctp 11 | { 12 | 13 | class PacketHeader 14 | { 15 | public: 16 | using shared = std::shared_ptr; 17 | public: 18 | PacketHeader(uint16_t sourcePortNumber,uint16_t destinationPortNumber,uint32_t verificationTag, uint32_t checksum = 0); 19 | ~PacketHeader() = default; 20 | 21 | static PacketHeader::shared Parse(BufferReader& buffer) ; 22 | size_t Serialize(BufferWritter& buffer) const; 23 | size_t GetSize() const; 24 | public: 25 | uint16_t sourcePortNumber = 0; 26 | uint16_t destinationPortNumber = 0; 27 | uint32_t verificationTag = 0; 28 | uint32_t checksum = 0; 29 | }; 30 | 31 | }; // namespace sctp 32 | 33 | #endif 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/sctp/SequenceNumberWrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef SEQUENCENUMBERWRAPPER_H 2 | #define SEQUENCENUMBERWRAPPER_H 3 | 4 | #include 5 | 6 | namespace sctp 7 | { 8 | 9 | template 10 | class SequenceNumberWrapper 11 | { 12 | public: 13 | static constexpr const E MaxSequenceNumber = ~static_cast(0); 14 | static constexpr const E Mask = ~static_cast(0) >> static_cast(sizeof(E)*8-N); 15 | static constexpr const T OutOfOrderWindow = ~static_cast(0) >> (N/2); 16 | 17 | E Wrap(T seqNum) 18 | { 19 | //Input should be withing given range 20 | assert((seqNum & Mask) == seqNum); 21 | 22 | //Current war cycle 23 | uint64_t seqCycles = cycles; 24 | 25 | //If not the first 26 | if (maxExtSeqNum!=MaxSequenceNumber) 27 | { 28 | //Check if we have a sequence wrap 29 | if (seqNumOutOfOrderWindow) 30 | //Increase warp cycles 31 | seqCycles = ++cycles; 32 | //Check if we have a packet from previous cycle 33 | else if (seqNum>maxSeqNum && seqNum-maxSeqNum>OutOfOrderWindow) 34 | //It is from the previous one 35 | --seqCycles; 36 | } 37 | 38 | //Generate extended sequence number 39 | E extSeqNum = (seqCycles << N) | seqNum; 40 | 41 | //Update maximum seen value 42 | if (extSeqNum>maxSeqNum || maxExtSeqNum==MaxSequenceNumber) 43 | { 44 | //Update max 45 | maxSeqNum = seqNum; 46 | maxExtSeqNum = extSeqNum; 47 | } 48 | 49 | //Done 50 | return extSeqNum; 51 | } 52 | 53 | T UnWrap(E extSeqNum) 54 | { 55 | return static_cast(extSeqNum & Mask); 56 | } 57 | private: 58 | 59 | E cycles = 0; 60 | T maxSeqNum = 0; 61 | E maxExtSeqNum = MaxSequenceNumber; 62 | 63 | }; 64 | 65 | } // namespace sctp 66 | 67 | #endif /* SEQUENCENUMBERWRAPPER_H */ 68 | 69 | -------------------------------------------------------------------------------- /src/sctp/Stream.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/Stream.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | Stream::Stream(Association &association, uint16_t id) : 7 | association(association) 8 | { 9 | this->id = id; 10 | } 11 | 12 | Stream::~Stream() 13 | { 14 | } 15 | 16 | bool Stream::Recv(const uint8_t ppid, const uint8_t* buffer, const size_t size) //first,last? 17 | { 18 | //onMessage(ppid,buff) 19 | return true; 20 | } 21 | 22 | bool Stream::Send(const uint8_t ppid, const uint8_t* buffer, const size_t size) 23 | { 24 | //TODO: check max queue size? 25 | 26 | //Add new message to ougogin queue 27 | outgoingMessages.push_back(std::make_pair<>(ppid,Buffer(buffer,size))); 28 | 29 | //TODO: signal pending data 30 | 31 | //done 32 | return true; 33 | } 34 | 35 | }; // namespace sctp 36 | -------------------------------------------------------------------------------- /src/sctp/Stream.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_STREAM_H 2 | #define SCTP_STREAM_H 3 | 4 | #include "Datachannels.h" 5 | 6 | #include 7 | #include 8 | 9 | #include "Buffer.h" 10 | 11 | 12 | namespace sctp 13 | { 14 | 15 | class Association; 16 | 17 | class Stream 18 | { 19 | public: 20 | using shared = std::shared_ptr; 21 | public: 22 | Stream(Association &association, uint16_t id); 23 | virtual ~Stream(); 24 | 25 | bool Recv(const uint8_t ppid, const uint8_t* buffer, const size_t size); 26 | bool Send(const uint8_t ppid, const uint8_t* buffer, const size_t size); 27 | 28 | uint16_t GetId() const { return id; } 29 | 30 | // Event handlers 31 | void OnMessage(std::function callback) 32 | { 33 | //Store callback 34 | onMessage = callback; 35 | } 36 | private: 37 | uint16_t id; 38 | Association &association; 39 | std::list> outgoingMessages; 40 | Buffer incomingMessage; 41 | 42 | std::function onMessage; 43 | }; 44 | 45 | }; // namespace 46 | #endif /* SCTP_STREAM_H */ 47 | 48 | -------------------------------------------------------------------------------- /src/sctp/chunks/AbortAssociationChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/AbortAssociationChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t AbortAssociationChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t AbortAssociationChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Get init pos 18 | size_t ini = writter.Mark(); 19 | 20 | //Write header 21 | writter.Set1(type); 22 | writter.Set1(0); 23 | //Skip length position 24 | size_t mark = writter.Skip(2); 25 | 26 | //Get length 27 | size_t length = writter.GetOffset(ini); 28 | //Set it 29 | writter.Set2(mark,length); 30 | 31 | //Done 32 | return length; 33 | } 34 | 35 | Chunk::shared AbortAssociationChunk::Parse(BufferReader& reader) 36 | { 37 | //Check size 38 | if (!reader.Assert(20)) 39 | //Error 40 | return nullptr; 41 | 42 | //Get header 43 | size_t mark = reader.Mark(); 44 | uint8_t type = reader.Get1(); 45 | uint8_t flag = reader.Get1(); //Ignored, should be 0 46 | uint16_t length = reader.Get2(); 47 | 48 | //Check type 49 | if (type!=Type::ABORT) 50 | //Error 51 | return nullptr; 52 | 53 | //Create chunk 54 | auto abort = std::make_shared(); 55 | 56 | //Done 57 | return std::static_pointer_cast(abort); 58 | } 59 | 60 | }; 61 | -------------------------------------------------------------------------------- /src/sctp/chunks/AbortAssociationChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_ABORTASSOCIATIONCHUNK_H_ 2 | #define SCTP_ABORTASSOCIATIONCHUNK_H_ 3 | 4 | #include 5 | #include 6 | #include "Buffer.h" 7 | #include "sctp/Chunk.h" 8 | #include "sctp/ErrorCause.h" 9 | 10 | namespace sctp 11 | { 12 | 13 | class AbortAssociationChunk : public Chunk 14 | { 15 | public: 16 | AbortAssociationChunk () : Chunk(Chunk::ABORT ) {} 17 | virtual ~AbortAssociationChunk() = default; 18 | 19 | virtual size_t Serialize(BufferWritter& buffer) const override; 20 | virtual size_t GetSize() const override; 21 | 22 | static Chunk::shared Parse(BufferReader& reader); 23 | public: 24 | // 0 1 2 3 25 | // 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 26 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 27 | // | Type = 6 |Reserved |T| Length | 28 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 29 | // \ \ 30 | // / zero or more Error Causes / 31 | // \ \ 32 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 33 | // 34 | bool verificationTag; 35 | std::vector errorCauses; 36 | }; 37 | 38 | }; // namespace sctp 39 | 40 | #endif 41 | 42 | -------------------------------------------------------------------------------- /src/sctp/chunks/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | target_sources(libdatachannels PUBLIC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/AbortAssociationChunk.cpp 3 | ${CMAKE_CURRENT_SOURCE_DIR}/CookieAckChunk.cpp 4 | ${CMAKE_CURRENT_SOURCE_DIR}/CookieEchoChunk.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/ForwardCumulativeTSNChunk.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/HeartbeatAckChunk.cpp 7 | ${CMAKE_CURRENT_SOURCE_DIR}/HeartbeatRequestChunk.cpp 8 | ${CMAKE_CURRENT_SOURCE_DIR}/InitiationAcknowledgementChunk.cpp 9 | ${CMAKE_CURRENT_SOURCE_DIR}/InitiationChunk.cpp 10 | ${CMAKE_CURRENT_SOURCE_DIR}/OperationErrorChunk.cpp 11 | ${CMAKE_CURRENT_SOURCE_DIR}/PayloadDataChunk.cpp 12 | ${CMAKE_CURRENT_SOURCE_DIR}/ReConfigChunk.cpp 13 | ${CMAKE_CURRENT_SOURCE_DIR}/SelectiveAcknowledgementChunk.cpp 14 | ${CMAKE_CURRENT_SOURCE_DIR}/ShutdownAcknowledgementChunk.cpp 15 | ${CMAKE_CURRENT_SOURCE_DIR}/ShutdownAssociationChunk.cpp 16 | ${CMAKE_CURRENT_SOURCE_DIR}/ShutdownCompleteChunk.cpp 17 | ${CMAKE_CURRENT_SOURCE_DIR}/UnknownChunk.cpp 18 | ${CMAKE_CURRENT_SOURCE_DIR}/PaddingChunk.cpp 19 | ) 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/sctp/chunks/CookieAckChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/CookieAckChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t CookieAckChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t CookieAckChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Get init pos 18 | size_t ini = writter.Mark(); 19 | 20 | //Write header 21 | writter.Set1(type); 22 | writter.Set1(flag); 23 | //Skip length position 24 | size_t mark = writter.Skip(2); 25 | 26 | //Get length 27 | size_t length = writter.GetOffset(ini); 28 | //Set it 29 | writter.Set2(mark,length); 30 | 31 | //Done 32 | return length; 33 | } 34 | 35 | Chunk::shared CookieAckChunk::Parse(BufferReader& reader) 36 | { 37 | //Check size 38 | if (!reader.Assert(4)) 39 | //Error 40 | return nullptr; 41 | 42 | //Get header 43 | size_t mark = reader.Mark(); 44 | uint8_t type = reader.Get1(); 45 | uint8_t flag = reader.Get1(); //Ignored, should be 0 46 | uint16_t length = reader.Get2(); 47 | 48 | //Check type 49 | if (type!=Type::COOKIE_ACK) 50 | //Error 51 | return nullptr; 52 | 53 | //Create chunk 54 | auto ack = std::make_shared(); 55 | 56 | //Done 57 | return std::static_pointer_cast(ack); 58 | } 59 | 60 | }; 61 | -------------------------------------------------------------------------------- /src/sctp/chunks/CookieAckChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_COOKIEACKCHUNK_H_ 2 | #define SCTP_COOKIEACKCHUNK_H_ 3 | 4 | #include "Buffer.h" 5 | #include "sctp/Chunk.h" 6 | 7 | namespace sctp 8 | { 9 | 10 | class CookieAckChunk : public Chunk 11 | { 12 | public: 13 | CookieAckChunk () : Chunk(Chunk::COOKIE_ACK) {} 14 | virtual ~CookieAckChunk() = default; 15 | 16 | virtual size_t Serialize(BufferWritter& buffer) const override; 17 | virtual size_t GetSize() const override; 18 | 19 | static Chunk::shared Parse(BufferReader& reader); 20 | public: 21 | // 0 1 2 3 22 | // 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 23 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 24 | // | Type = 11 |Chunk Flags | Length = 4 | 25 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 | // 27 | }; 28 | 29 | }; // namespace sctp 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /src/sctp/chunks/CookieEchoChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/CookieEchoChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t CookieEchoChunk::GetSize() const 7 | { 8 | //Header + cookie 9 | return SizePad(4,4+cookie.GetSize()); 10 | } 11 | 12 | size_t CookieEchoChunk::Serialize(BufferWritter& writter) const 13 | { 14 | //Check header length 15 | if (!writter.Assert(4)) 16 | return 0; 17 | 18 | //Get init pos 19 | size_t ini = writter.Mark(); 20 | 21 | //Write header 22 | writter.Set1(type); 23 | writter.Set1(0); // Always 0 24 | 25 | //Skip length position 26 | size_t mark = writter.Skip(2); 27 | 28 | //Check cookie size 29 | if (!writter.Assert(cookie.GetSize())) 30 | return 0; 31 | 32 | //Write cooke 33 | writter.Set(cookie); 34 | 35 | //Get length 36 | size_t length = writter.GetOffset(ini); 37 | //Set it 38 | writter.Set2(mark,length); 39 | 40 | //Pad 41 | return writter.PadTo(4); 42 | } 43 | 44 | Chunk::shared CookieEchoChunk::Parse(BufferReader& reader) 45 | { 46 | //Check size 47 | if (!reader.Assert(4)) 48 | //Error 49 | return nullptr; 50 | 51 | //Get header 52 | size_t mark = reader.Mark(); 53 | uint8_t type = reader.Get1(); 54 | uint8_t flag = reader.Get1(); //Ignored, should be 0 55 | uint16_t length = reader.Get2(); 56 | 57 | //Check type 58 | if (type!=Type::COOKIE_ECHO) 59 | //Error 60 | return nullptr; 61 | 62 | //Create chunk 63 | auto cookieEcho = std::make_shared(); 64 | 65 | //Check size 66 | if (!reader.Assert(length-4)) 67 | //Error 68 | return nullptr; 69 | 70 | //Get cookie 71 | cookieEcho->cookie = reader.GetBuffer(length-4); 72 | 73 | //Pad input 74 | if (!reader.PadTo(4)) 75 | return nullptr; 76 | 77 | //Done 78 | return std::static_pointer_cast(cookieEcho); 79 | } 80 | 81 | }; 82 | -------------------------------------------------------------------------------- /src/sctp/chunks/CookieEchoChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_COOKIEECHOCHUNK_H_ 2 | #define SCTP_COOKIEECHOCHUNK_H_ 3 | 4 | #include "Buffer.h" 5 | #include "sctp/Chunk.h" 6 | 7 | namespace sctp 8 | { 9 | 10 | class CookieEchoChunk : public Chunk 11 | { 12 | public: 13 | CookieEchoChunk () : Chunk(Chunk::COOKIE_ECHO) {} 14 | virtual ~CookieEchoChunk() = default; 15 | 16 | virtual size_t Serialize(BufferWritter& buffer) const override; 17 | virtual size_t GetSize() const override; 18 | 19 | static Chunk::shared Parse(BufferReader& reader); 20 | public: 21 | // 0 1 2 3 22 | // 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 23 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 24 | // | Type = 10 |Chunk Flags | Length | 25 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 | // / Cookie / 27 | // \ \ 28 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 29 | 30 | Buffer cookie; 31 | }; 32 | 33 | }; // namespace sctp 34 | 35 | #endif 36 | 37 | -------------------------------------------------------------------------------- /src/sctp/chunks/ForwardCumulativeTSNChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/ForwardCumulativeTSNChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t ForwardCumulativeTSNChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t ForwardCumulativeTSNChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Get init pos 18 | size_t ini = writter.Mark(); 19 | 20 | //Write header 21 | writter.Set1(type); 22 | writter.Set1(0); 23 | //Skip length position 24 | size_t mark = writter.Skip(2); 25 | 26 | 27 | 28 | //Get length 29 | size_t length = writter.GetOffset(ini); 30 | //Set it 31 | writter.Set2(mark,length); 32 | 33 | //Done 34 | return length; 35 | } 36 | 37 | Chunk::shared ForwardCumulativeTSNChunk::Parse(BufferReader& reader) 38 | { 39 | //Check size 40 | if (!reader.Assert(20)) 41 | //Error 42 | return nullptr; 43 | 44 | //Get header 45 | size_t mark = reader.Mark(); 46 | uint8_t type = reader.Get1(); 47 | uint8_t flag = reader.Get1(); //Ignored, should be 0 48 | uint16_t length = reader.Get2(); 49 | 50 | //Check type 51 | if (type!=Type::FORWARD_CUMULATIVE_TSN) 52 | //Error 53 | return nullptr; 54 | 55 | //Create chunk 56 | auto reconfig = std::make_shared(); 57 | 58 | //Done 59 | return std::static_pointer_cast(reconfig); 60 | } 61 | 62 | }; 63 | 64 | -------------------------------------------------------------------------------- /src/sctp/chunks/ForwardCumulativeTSNChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_FORWARDCUMULATIVETSNCHUNK_H 2 | #define SCTP_FORWARDCUMULATIVETSNCHUNK_H 3 | 4 | #include 5 | 6 | #include "sctp/Chunk.h" 7 | 8 | namespace sctp 9 | { 10 | 11 | 12 | class ForwardCumulativeTSNChunk : public Chunk 13 | { 14 | public: 15 | ForwardCumulativeTSNChunk () : Chunk(Chunk::FORWARD_CUMULATIVE_TSN) {} 16 | virtual ~ForwardCumulativeTSNChunk() = default; 17 | 18 | virtual size_t Serialize(BufferWritter& buffer) const override; 19 | virtual size_t GetSize() const override; 20 | 21 | static Chunk::shared Parse(BufferReader& reader); 22 | public: 23 | // 0 1 2 3 24 | // 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 25 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 | // | Type = 192 | Flags = 0x00 | Length = Variable | 27 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 28 | // | New Cumulative TSN | 29 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 30 | // | Stream-1 | Stream Sequence-1 | 31 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | // \ / 33 | // / \ 34 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 35 | // | Stream-N | Stream Sequence-N | 36 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 37 | uint32_t newCumulativeTSN; 38 | std::map streamsSequence; 39 | 40 | }; 41 | 42 | }; // namespace 43 | 44 | #endif /* SCTP_FORWARDCUMULATIVETSNCHUNK_H */ 45 | 46 | -------------------------------------------------------------------------------- /src/sctp/chunks/HeartbeatAckChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/HeartbeatAckChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t HeartbeatAckChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t HeartbeatAckChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Check header length 18 | if (!writter.Assert(4)) 19 | return 0; 20 | 21 | //Get init pos 22 | size_t ini = writter.Mark(); 23 | 24 | //Write header 25 | writter.Set1(type); 26 | writter.Set1(flag); 27 | 28 | //Skip length position 29 | size_t mark = writter.Skip(2); 30 | 31 | //Heartbeat param 32 | { 33 | //Check parameter length 34 | size_t len = 4+senderSpecificHearbeatInfo.GetSize(); 35 | if (!writter.Assert(len)) 36 | return 0; 37 | //Write it 38 | writter.Set2(Parameter::HeartbeatInfo); 39 | writter.Set2(len); 40 | writter.Set(senderSpecificHearbeatInfo); 41 | writter.PadTo(4); 42 | } 43 | 44 | //Get length 45 | size_t length = writter.GetOffset(ini); 46 | //Set it 47 | writter.Set2(mark,length); 48 | 49 | //Done 50 | return length; 51 | } 52 | 53 | Chunk::shared HeartbeatAckChunk::Parse(BufferReader& reader) 54 | { 55 | //Check size 56 | if (!reader.Assert(4)) 57 | //Error 58 | return nullptr; 59 | 60 | //Get header 61 | size_t mark = reader.Mark(); 62 | uint8_t type = reader.Get1(); 63 | uint8_t flag = reader.Get1(); //Ignored, should be 0 64 | uint16_t length = reader.Get2(); 65 | 66 | //Check type 67 | if (type!=Type::HEARTBEAT_ACK) 68 | //Error 69 | return nullptr; 70 | 71 | //Create chunk 72 | auto heartbeatRequest = std::make_shared(); 73 | 74 | //Read parameters 75 | while (reader.GetLeft()>=4) 76 | { 77 | //Get parameter type 78 | uint16_t paramType = reader.Get2(); 79 | uint16_t paramLength = reader.Get2(); 80 | //Ensure lenghth is correct as it has to contain the type and length itself 81 | if (paramLength<4) 82 | return nullptr; 83 | //Remove header 84 | paramLength-=4; 85 | //Ensure we have enought length 86 | if (!reader.Assert(paramLength)) return nullptr; 87 | //Get reader for the param length 88 | BufferReader paramReader = reader.GetReader(paramLength); 89 | //Depending on the parameter type 90 | switch(paramType) 91 | { 92 | case Parameter::HeartbeatInfo: 93 | heartbeatRequest->senderSpecificHearbeatInfo = paramReader.GetBuffer(paramReader.GetLeft()); 94 | break; 95 | default: 96 | //Unkonwn 97 | return nullptr; 98 | } 99 | //Ensure all input has been consumed 100 | if (paramReader.GetLeft()) 101 | return nullptr; 102 | //Do padding 103 | reader.PadTo(4); 104 | } 105 | 106 | //Done 107 | return std::static_pointer_cast(heartbeatRequest); 108 | } 109 | 110 | }; 111 | -------------------------------------------------------------------------------- /src/sctp/chunks/HeartbeatAckChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_HEARTBEATACKCHUNK_H_ 2 | #define SCTP_HEARTBEATACKCHUNK_H_ 3 | 4 | 5 | #include "Buffer.h" 6 | #include "sctp/Chunk.h" 7 | 8 | namespace sctp 9 | { 10 | 11 | class HeartbeatAckChunk : public Chunk 12 | { 13 | public: 14 | HeartbeatAckChunk () : Chunk(Chunk::HEARTBEAT_ACK) {} 15 | virtual ~HeartbeatAckChunk() = default; 16 | 17 | virtual size_t Serialize(BufferWritter& buffer) const override; 18 | virtual size_t GetSize() const override; 19 | 20 | static Chunk::shared Parse(BufferReader& reader); 21 | public: 22 | // 0 1 2 3 23 | // 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 24 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 25 | // | Type = 5 | Chunk Flags | Heartbeat Length | 26 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 27 | // | Heartbeat Info Type=1 | HB Info Length | 28 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 29 | // / Sender-Specific Heartbeat Info / 30 | // \ \ 31 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | 33 | Buffer senderSpecificHearbeatInfo; 34 | }; 35 | 36 | }; // namespace sctp 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /src/sctp/chunks/HeartbeatRequestChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/HeartbeatRequestChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t HeartbeatRequestChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t HeartbeatRequestChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Check header length 18 | if (!writter.Assert(4)) 19 | return 0; 20 | 21 | //Get init pos 22 | size_t ini = writter.Mark(); 23 | 24 | //Write header 25 | writter.Set1(type); 26 | writter.Set1(flag); 27 | 28 | //Skip length position 29 | size_t mark = writter.Skip(2); 30 | 31 | //Heartbeat param 32 | { 33 | //Check parameter length 34 | size_t len = 4+senderSpecificHearbeatInfo.GetSize(); 35 | if (!writter.Assert(len)) 36 | return 0; 37 | //Write it 38 | writter.Set2(Parameter::HeartbeatInfo); 39 | writter.Set2(len); 40 | writter.Set(senderSpecificHearbeatInfo); 41 | writter.PadTo(4); 42 | } 43 | 44 | //Get length 45 | size_t length = writter.GetOffset(ini); 46 | //Set it 47 | writter.Set2(mark,length); 48 | 49 | //Done 50 | return length; 51 | } 52 | 53 | Chunk::shared HeartbeatRequestChunk::Parse(BufferReader& reader) 54 | { 55 | //Check size 56 | if (!reader.Assert(4)) 57 | //Error 58 | return nullptr; 59 | 60 | //Get header 61 | size_t mark = reader.Mark(); 62 | uint8_t type = reader.Get1(); 63 | uint8_t flag = reader.Get1(); //Ignored, should be 0 64 | uint16_t length = reader.Get2(); 65 | 66 | //Check type 67 | if (type!=Type::HEARTBEAT) 68 | //Error 69 | return nullptr; 70 | 71 | //Create chunk 72 | auto heartbeatRequest = std::make_shared(); 73 | 74 | //Read parameters 75 | while (reader.GetLeft()>=4) 76 | { 77 | //Get parameter type 78 | uint16_t paramType = reader.Get2(); 79 | uint16_t paramLength = reader.Get2(); 80 | //Ensure lenghth is correct as it has to contain the type and length itself 81 | if (paramLength<4) 82 | return nullptr; 83 | //Remove header 84 | paramLength-=4; 85 | //Ensure we have enought length 86 | if (!reader.Assert(paramLength)) return nullptr; 87 | //Get reader for the param length 88 | BufferReader paramReader = reader.GetReader(paramLength); 89 | //Depending on the parameter type 90 | switch(paramType) 91 | { 92 | case Parameter::HeartbeatInfo: 93 | heartbeatRequest->senderSpecificHearbeatInfo = paramReader.GetBuffer(paramReader.GetLeft()); 94 | break; 95 | default: 96 | //Unkonwn 97 | return nullptr; 98 | } 99 | //Ensure all input has been consumed 100 | if (paramReader.GetLeft()) 101 | return nullptr; 102 | //Do padding 103 | reader.PadTo(4); 104 | } 105 | 106 | //Done 107 | return std::static_pointer_cast(heartbeatRequest); 108 | } 109 | 110 | }; 111 | 112 | -------------------------------------------------------------------------------- /src/sctp/chunks/HeartbeatRequestChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_HEARTBEATREQUESTCHUNK_H_ 2 | #define SCTP_HEARTBEATREQUESTCHUNK_H_ 3 | 4 | #include "Buffer.h" 5 | #include "sctp/Chunk.h" 6 | 7 | namespace sctp 8 | { 9 | 10 | class HeartbeatRequestChunk : public Chunk 11 | { 12 | public: 13 | HeartbeatRequestChunk () : Chunk(Chunk::HEARTBEAT) {} 14 | virtual ~HeartbeatRequestChunk() = default; 15 | 16 | virtual size_t Serialize(BufferWritter& buffer) const override; 17 | virtual size_t GetSize() const override; 18 | 19 | static Chunk::shared Parse(BufferReader& reader); 20 | public: 21 | // 0 1 2 3 22 | // 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 23 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 24 | // | Type = 4 | Chunk Flags | Heartbeat Length | 25 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 | // | Heartbeat Info Type=1 | HB Info Length | 27 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 28 | // / Sender-Specific Heartbeat Info / 29 | // \ \ 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 | // | Heartbeat Info Type=1 | HB Info Length | 36 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 37 | // / Sender-Specific Heartbeat Info / 38 | // \ \ 39 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 40 | 41 | Buffer senderSpecificHearbeatInfo; 42 | }; 43 | 44 | }; // namespace sctp 45 | 46 | #endif 47 | 48 | -------------------------------------------------------------------------------- /src/sctp/chunks/InitiationAcknowledgementChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/InitiationAcknowledgementChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t InitiationAcknowledgementChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t InitiationAcknowledgementChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Check header length 18 | if (!writter.Assert(20)) 19 | return 0; 20 | 21 | //Get init pos 22 | size_t ini = writter.Mark(); 23 | 24 | //Write header 25 | writter.Set1(type); 26 | writter.Set1(flag); 27 | //Skip length position 28 | size_t mark = writter.Skip(2); 29 | 30 | //Set attributes 31 | writter.Set4(initiateTag); 32 | writter.Set4(advertisedReceiverWindowCredit); 33 | writter.Set2(numberOfOutboundStreams); 34 | writter.Set2(numberOfInboundStreams); 35 | writter.Set4(initialTransmissionSequenceNumber); 36 | 37 | //Cookie param 38 | { 39 | //Check parameter length 40 | size_t len = 4+stateCookie.GetSize(); 41 | if (!writter.Assert(len)) 42 | return 0; 43 | //Write it 44 | writter.Set2(Parameter::StateCookie); 45 | writter.Set2(len); 46 | writter.Set(stateCookie); 47 | //Pad input 48 | if (!writter.PadTo(4)) 49 | return 0; 50 | } 51 | 52 | //IPV4 addresses 53 | for (const auto& ipV4Address : ipV4Addresses) 54 | { 55 | //Check parameter length 56 | size_t len = 12; 57 | if (!writter.Assert(len)) 58 | return 0; 59 | //Write it 60 | writter.Set2(Parameter::IPv4Address); 61 | writter.Set2(len); 62 | writter.Set<8>(ipV4Address); 63 | //Pad input 64 | if (!writter.PadTo(4)) 65 | return 0; 66 | } 67 | 68 | //IPV6 addresses 69 | for (const auto& ipV6Address : ipV6Addresses) 70 | { 71 | //Check parameter length 72 | size_t len = 24; 73 | if (!writter.Assert(len)) 74 | return 0; 75 | //Write it 76 | writter.Set2(Parameter::IPv6Address); 77 | writter.Set2(len); 78 | writter.Set<20>(ipV6Address); 79 | //Pad input 80 | if (!writter.PadTo(4)) 81 | return 0; 82 | } 83 | 84 | //Optional Host name 85 | if (hostName) 86 | { 87 | //Check parameter length 88 | size_t len = 4+hostName->length(); 89 | if (!writter.Assert(len)) 90 | return 0; 91 | //Write it 92 | writter.Set2(Parameter::HostNameAddress); 93 | writter.Set2(len); 94 | writter.Set(*hostName); 95 | //Pad input 96 | if (!writter.PadTo(4)) 97 | return 0; 98 | } 99 | 100 | //Unrecognized parameters from INIT 101 | for (const auto& unrecognizedParameter : unrecognizedParameters) 102 | { 103 | //Check parameter length 104 | size_t len = 4+unrecognizedParameter.GetSize(); 105 | if (!writter.Assert(len)) 106 | return 0; 107 | //Write it 108 | writter.Set2(Parameter::UnrecognizedParameter); 109 | writter.Set2(len); 110 | writter.Set(unrecognizedParameter); 111 | //Pad input 112 | if (!writter.PadTo(4)) 113 | return 0; 114 | } 115 | 116 | //Supported extensions parameter 117 | if (supportedExtensions.size()) 118 | { 119 | //Check parameter length 120 | size_t len = 4+supportedExtensions.size(); 121 | if (!writter.Assert(len)) 122 | return 0; 123 | //Write it 124 | writter.Set2(Parameter::SupportedExtensions); 125 | writter.Set2(len); 126 | for (const auto& supportedExtension : supportedExtensions) 127 | writter.Set1(supportedExtension); 128 | //Pad input 129 | if (!writter.PadTo(4)) 130 | return 0; 131 | } 132 | 133 | //Unknown parameters 134 | for (const auto& unknownParameter : unknownParameters) 135 | { 136 | //Check parameter length 137 | size_t len = 4+unknownParameter.second.GetSize(); 138 | if (!writter.Assert(len)) 139 | return 0; 140 | //Write it 141 | writter.Set2(unknownParameter.first); 142 | writter.Set2(len); 143 | writter.Set(unknownParameter.second); 144 | //Pad input 145 | if (!writter.PadTo(4)) 146 | return 0; 147 | } 148 | 149 | //Support for ForwardTSN 150 | if (forwardTSNSupported) 151 | { 152 | //Check parameter length 153 | size_t len = 4;; 154 | if (!writter.Assert(len)) 155 | return 0; 156 | //Write it 157 | writter.Set2(Parameter::ForwardTSNSupported); 158 | writter.Set2(len); 159 | //Pad input 160 | if (!writter.PadTo(4)) 161 | return 0; 162 | } 163 | 164 | //Get length 165 | size_t length = writter.GetOffset(ini); 166 | //Set it 167 | writter.Set2(mark,length); 168 | 169 | //Done 170 | return length; 171 | } 172 | 173 | Chunk::shared InitiationAcknowledgementChunk::Parse(BufferReader& reader) 174 | { 175 | //Check size 176 | if (!reader.Assert(20)) 177 | //Error 178 | return nullptr; 179 | 180 | //Get header 181 | size_t mark = reader.Mark(); 182 | uint8_t type = reader.Get1(); 183 | uint8_t flag = reader.Get1(); //Ignored, should be 0 184 | uint16_t length = reader.Get2(); 185 | 186 | //Check type 187 | if (type!=Type::INIT_ACK) 188 | //Error 189 | return nullptr; 190 | 191 | //Create chunk 192 | auto ack = std::make_shared(); 193 | 194 | //Set attributes 195 | ack->initiateTag = reader.Get4(); 196 | ack->advertisedReceiverWindowCredit = reader.Get4(); 197 | ack->numberOfOutboundStreams = reader.Get2(); 198 | ack->numberOfInboundStreams = reader.Get2(); 199 | ack->initialTransmissionSequenceNumber = reader.Get4(); 200 | ack->forwardTSNSupported = false; 201 | 202 | //Read parameters 203 | while (reader.GetLeft()>=4) 204 | { 205 | //Get parameter type 206 | uint16_t paramType = reader.Get2(); 207 | uint16_t paramLength = reader.Get2(); 208 | //Ensure lenghth is correct as it has to contain the type and length itself 209 | if (paramLength<4) 210 | return nullptr; 211 | //Remove header 212 | paramLength-=4; 213 | //Ensure we have enought length 214 | if (!reader.Assert(paramLength)) return nullptr; 215 | //Get reader for the param length 216 | BufferReader paramReader = reader.GetReader(paramLength); 217 | //Depending on the parameter type 218 | switch(paramType) 219 | { 220 | case Parameter::IPv4Address: 221 | if (!paramReader.Assert(8)) return nullptr; 222 | ack->ipV4Addresses.push_back(paramReader.Get<8>()); 223 | break; 224 | case Parameter::IPv6Address: 225 | if (!paramReader.Assert(20)) return nullptr; 226 | ack->ipV6Addresses.push_back(paramReader.Get<20>()); 227 | break; 228 | case Parameter::HostNameAddress: 229 | ack->hostName = paramReader.GetString(paramReader.GetLeft()); 230 | break; 231 | case Parameter::StateCookie: 232 | ack->stateCookie = paramReader.GetBuffer(paramReader.GetLeft()); 233 | break; 234 | case Parameter::SupportedExtensions: 235 | while (paramReader.GetLeft()) 236 | ack->supportedExtensions.push_back(paramReader.Get1()); 237 | break; 238 | case Parameter::ForwardTSNSupported: 239 | ack->forwardTSNSupported = true; 240 | break; 241 | case Parameter::UnrecognizedParameter: 242 | ack->unrecognizedParameters.push_back(paramReader.GetBuffer(paramReader.GetLeft())); 243 | break; 244 | default: 245 | //Unkonwn 246 | ack->unknownParameters.push_back(std::make_pair(paramType,paramReader.GetBuffer(paramReader.GetLeft()))); 247 | } 248 | //Ensure all input has been consumed 249 | if (paramReader.GetLeft()) 250 | return nullptr; 251 | //Do padding 252 | reader.PadTo(4); 253 | } 254 | 255 | //Done 256 | return std::static_pointer_cast(ack); 257 | } 258 | 259 | }; 260 | -------------------------------------------------------------------------------- /src/sctp/chunks/InitiationAcknowledgementChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_INITIATIONACKNOWLEDGEMENTCHUNK_H_ 2 | #define SCTP_INITIATIONACKNOWLEDGEMENTCHUNK_H_ 3 | 4 | #include "Buffer.h" 5 | #include "sctp/Chunk.h" 6 | 7 | #include 8 | 9 | namespace sctp 10 | { 11 | 12 | class InitiationAcknowledgementChunk : public Chunk 13 | { 14 | public: 15 | InitiationAcknowledgementChunk () : Chunk(Chunk::INIT_ACK) {} 16 | virtual ~InitiationAcknowledgementChunk() = default; 17 | 18 | virtual size_t Serialize(BufferWritter& buffer) const override; 19 | virtual size_t GetSize() const override; 20 | 21 | static Chunk::shared Parse(BufferReader& reader); 22 | public: 23 | // 0 1 2 3 24 | // 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 25 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 | // | Type = 1 | Chunk Flags | Chunk Length | 27 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 28 | // | Initiate Tag | 29 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 30 | // | Advertised Receiver Window Credit (a_rwnd) | 31 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | // | Number of Outbound Streams | Number of Inbound Streams | 33 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 34 | // | Initial TSN | 35 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 36 | // \ \ 37 | // / Optional/Variable-Length Parameters / 38 | // \ \ 39 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 40 | uint32_t initiateTag = 0; 41 | uint32_t advertisedReceiverWindowCredit = 0; 42 | uint16_t numberOfOutboundStreams = 0; 43 | uint16_t numberOfInboundStreams = 0; 44 | uint32_t initialTransmissionSequenceNumber = 0; 45 | Buffer stateCookie; 46 | 47 | std::vector> ipV4Addresses; // IPv4 Address Parameter (5) 48 | std::vector> ipV6Addresses; // IPv6 Address Parameter (6) 49 | std::optional hostName; // Host Name Address (11) 50 | std::vector unrecognizedParameters; // Unrecognized Parameter (8) 51 | std::vector> unknownParameters; 52 | std::vector supportedExtensions; // Supported Extensions (0x8008) rfc5061#page-13 53 | 54 | bool forwardTSNSupported = true; // Forward-TSN-Supported 49152 (0xC000) rfc3758 55 | }; 56 | 57 | }; // namespace sctp 58 | 59 | #endif 60 | 61 | -------------------------------------------------------------------------------- /src/sctp/chunks/InitiationChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/InitiationChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t InitiationChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Set parameters 12 | size += ipV4Addresses.size() * 8; 13 | size += ipV6Addresses.size() * 20; 14 | if (suggestedCookieLifeSpanIncrement) 15 | size += 8; 16 | if (hostName) 17 | size += SizePad(hostName->length(), 4) ; 18 | size += SizePad(supportedAddressTypes.size() * 2, 4); 19 | size += SizePad(supportedExtensions.size(), 4); 20 | for (const auto& unknownParameter : unknownParameters) 21 | size += SizePad(unknownParameter.second.GetSize(), 4); 22 | 23 | //Done 24 | return size; 25 | } 26 | 27 | size_t InitiationChunk::Serialize(BufferWritter& writter) const 28 | { 29 | //Check header length 30 | if (!writter.Assert(20)) 31 | return 0; 32 | 33 | //Get init pos 34 | size_t ini = writter.Mark(); 35 | 36 | //Write header 37 | writter.Set1(type); 38 | writter.Set1(flag); 39 | //Skip length position 40 | size_t mark = writter.Skip(2); 41 | 42 | //Set attributes 43 | writter.Set4(initiateTag); 44 | writter.Set4(advertisedReceiverWindowCredit); 45 | writter.Set2(numberOfOutboundStreams); 46 | writter.Set2(numberOfInboundStreams); 47 | writter.Set4(initialTransmissionSequenceNumber); 48 | 49 | //IPV4 addresses 50 | for (const auto& ipV4Address : ipV4Addresses) 51 | { 52 | //Check parameter length 53 | size_t len = 12; 54 | if (!writter.Assert(len)) 55 | return 0; 56 | //Write it 57 | writter.Set2(Parameter::IPv4Address); 58 | writter.Set2(len); 59 | writter.Set<8>(ipV4Address); 60 | //Pad input 61 | if (!writter.PadTo(4)) 62 | return 0; 63 | } 64 | 65 | //IPV6 addresses 66 | for (const auto& ipV6Address : ipV6Addresses) 67 | { 68 | //Check parameter length 69 | size_t len = 24; 70 | if (!writter.Assert(len)) 71 | return 0; 72 | //Write it 73 | writter.Set2(Parameter::IPv6Address); 74 | writter.Set2(len); 75 | writter.Set<20>(ipV6Address); 76 | //Pad input 77 | if (!writter.PadTo(4)) 78 | return 0; 79 | } 80 | 81 | //Cookie life span 82 | if (suggestedCookieLifeSpanIncrement) 83 | { 84 | //Check parameter length 85 | size_t len = 12; 86 | if (!writter.Assert(len)) 87 | return 0; 88 | //Write it 89 | writter.Set2(Parameter::CookiePreservative); 90 | writter.Set2(len); 91 | writter.Set8(*suggestedCookieLifeSpanIncrement); 92 | //Pad input 93 | if (!writter.PadTo(4)) 94 | return 0; 95 | } 96 | 97 | //Optional Host name 98 | if (hostName) 99 | { 100 | //Check parameter length 101 | size_t len = 4+hostName->length(); 102 | if (!writter.Assert(len)) 103 | return 0; 104 | //Write it 105 | writter.Set2(Parameter::HostNameAddress); 106 | writter.Set2(len); 107 | writter.Set(*hostName); 108 | //Pad input 109 | if (!writter.PadTo(4)) 110 | return 0; 111 | } 112 | 113 | //Supported extensions parameter 114 | if (supportedAddressTypes.size()) 115 | { 116 | //Check parameter length 117 | size_t len = 4+supportedAddressTypes.size()*2; 118 | if (!writter.Assert(len)) 119 | return 0; 120 | //Write it 121 | writter.Set2(Parameter::SupportedAddressTypes); 122 | writter.Set2(len); 123 | for (const auto& supportedAddressType : supportedAddressTypes) 124 | writter.Set2(supportedAddressType); 125 | //Pad input 126 | if (!writter.PadTo(4)) 127 | return 0; 128 | } 129 | 130 | //Supported extensions parameter 131 | if (supportedExtensions.size()) 132 | { 133 | //Check parameter length 134 | size_t len = 4+supportedExtensions.size(); 135 | if (!writter.Assert(len)) 136 | return 0; 137 | //Write it 138 | writter.Set2(Parameter::SupportedExtensions); 139 | writter.Set2(len); 140 | for (const auto& supportedExtension : supportedExtensions) 141 | writter.Set1(supportedExtension); 142 | //Pad input 143 | if (!writter.PadTo(4)) 144 | return 0; 145 | } 146 | 147 | //Unknown parameters 148 | for (const auto& unknownParameter : unknownParameters) 149 | { 150 | //Check parameter length 151 | size_t len = 4+unknownParameter.second.GetSize(); 152 | if (!writter.Assert(len)) 153 | return 0; 154 | //Write it 155 | writter.Set2(unknownParameter.first); 156 | writter.Set2(len); 157 | writter.Set(unknownParameter.second); 158 | //Pad input 159 | if (!writter.PadTo(4)) 160 | return 0; 161 | } 162 | 163 | //Support for ForwardTSN 164 | if (forwardTSNSupported) 165 | { 166 | //Check parameter length 167 | size_t len = 4;; 168 | if (!writter.Assert(len)) 169 | return 0; 170 | //Write it 171 | writter.Set2(Parameter::ForwardTSNSupported); 172 | writter.Set2(len); 173 | //Pad input 174 | if (!writter.PadTo(4)) 175 | return 0; 176 | } 177 | 178 | //Get length 179 | size_t length = writter.GetOffset(ini); 180 | //Set it 181 | writter.Set2(mark,length); 182 | 183 | //Done 184 | return length; 185 | } 186 | 187 | Chunk::shared InitiationChunk::Parse(BufferReader& reader) 188 | { 189 | //Check size 190 | if (!reader.Assert(20)) 191 | //Error 192 | return nullptr; 193 | 194 | //Get header 195 | size_t mark = reader.Mark(); 196 | uint8_t type = reader.Get1(); 197 | uint8_t flag = reader.Get1(); //Ignored, should be 0 198 | uint16_t length = reader.Get2(); 199 | 200 | //Check type 201 | if (type!=Type::INIT) 202 | //Error 203 | return nullptr; 204 | 205 | //Create chunk 206 | auto init = std::make_shared(); 207 | 208 | //Set attributes 209 | init->initiateTag = reader.Get4(); 210 | init->advertisedReceiverWindowCredit = reader.Get4(); 211 | init->numberOfOutboundStreams = reader.Get2(); 212 | init->numberOfInboundStreams = reader.Get2(); 213 | init->initialTransmissionSequenceNumber = reader.Get4(); 214 | init->forwardTSNSupported = false; 215 | 216 | //Read parameters 217 | while (reader.GetLeft()>=4) 218 | { 219 | //Get parameter type 220 | uint16_t paramType = reader.Get2(); 221 | uint16_t paramLength = reader.Get2(); 222 | //Ensure lenghth is correct as it has to contain the type and length itself 223 | if (paramLength<4) 224 | return nullptr; 225 | //Remove header 226 | paramLength-=4; 227 | //Ensure we have enought length 228 | if (!reader.Assert(paramLength)) return nullptr; 229 | //Get reader for the param length 230 | BufferReader paramReader = reader.GetReader(paramLength); 231 | //Depending on the parameter type 232 | switch(paramType) 233 | { 234 | case Parameter::IPv4Address: 235 | if (!paramReader.Assert(8)) return nullptr; 236 | init->ipV4Addresses.push_back(paramReader.Get<8>()); 237 | break; 238 | case Parameter::IPv6Address: 239 | if (!paramReader.Assert(20)) return nullptr; 240 | init->ipV6Addresses.push_back(paramReader.Get<20>()); 241 | break; 242 | case Parameter::HostNameAddress: 243 | init->hostName = paramReader.GetString(paramReader.GetLeft()); 244 | break; 245 | case Parameter::SupportedAddressTypes: 246 | while (paramReader.GetLeft()) 247 | init->supportedAddressTypes.push_back(paramReader.Get2()); 248 | break; 249 | case Parameter::SupportedExtensions: 250 | while (paramReader.GetLeft()) 251 | init->supportedExtensions.push_back(paramReader.Get1()); 252 | break; 253 | case Parameter::CookiePreservative: 254 | if (!paramReader.Assert(8)) return nullptr; 255 | init->suggestedCookieLifeSpanIncrement = paramReader.Get8(); 256 | break; 257 | case Parameter::ForwardTSNSupported: 258 | init->forwardTSNSupported = true; 259 | break; 260 | case Parameter::Padding: 261 | //The PAD parameter MAY be included only in the INIT chunk. It MUST 262 | //NOT be included in any other chunk. The receiver of the PAD 263 | //parameter MUST silently discard this parameter and continue 264 | //processing the rest of the INIT chunk. 265 | reader.Skip(reader.GetLeft()); 266 | default: 267 | //Unkonwn 268 | init->unknownParameters.push_back(std::make_pair(paramType,paramReader.GetBuffer(paramReader.GetLeft()))); 269 | } 270 | //Ensure all input has been consumed 271 | if (paramReader.GetLeft()) 272 | //Error 273 | return nullptr; 274 | //Do padding 275 | reader.PadTo(4); 276 | } 277 | 278 | //Done 279 | return std::static_pointer_cast(init); 280 | } 281 | 282 | }; 283 | -------------------------------------------------------------------------------- /src/sctp/chunks/InitiationChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_INITIATIONCHUNK_H_ 2 | #define SCTP_INITIATIONCHUNK_H_ 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include "Buffer.h" 9 | #include "sctp/Chunk.h" 10 | 11 | namespace sctp 12 | { 13 | 14 | class InitiationChunk : public Chunk 15 | { 16 | public: 17 | InitiationChunk () : Chunk(Chunk::INIT) {} 18 | virtual ~InitiationChunk() = default; 19 | 20 | virtual size_t Serialize(BufferWritter& buffer) const override; 21 | virtual size_t GetSize() const override; 22 | 23 | static Chunk::shared Parse(BufferReader& reader); 24 | public: 25 | // 0 1 2 3 26 | // 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 27 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 28 | // | Type = 1 | Chunk Flags | Chunk Length | 29 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 30 | // | Initiate Tag | 31 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | // | Advertised Receiver Window Credit (a_rwnd) | 33 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 34 | // | Number of Outbound Streams | Number of Inbound Streams | 35 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 36 | // | Initial TSN | 37 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 | // \ \ 39 | // / Optional/Variable-Length Parameters / 40 | // \ \ 41 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 42 | uint32_t initiateTag = 0; 43 | uint32_t advertisedReceiverWindowCredit = 0; 44 | uint16_t numberOfOutboundStreams = 0; 45 | uint16_t numberOfInboundStreams = 0; 46 | uint32_t initialTransmissionSequenceNumber = 0; 47 | //Optional parameters 48 | std::vector> ipV4Addresses; // IPv4 Address Parameter (5) 49 | std::vector> ipV6Addresses; // IPv6 Address Parameter (6) 50 | std::optional suggestedCookieLifeSpanIncrement; // Cookie Preservative (9) 51 | std::optional hostName; // Host Name Address (11) 52 | std::vector supportedAddressTypes; // Supported Address Types (12) 53 | std::vector supportedExtensions; // Supported Extensions (0x8008) rfc5061#page-13 54 | std::vector> unknownParameters; 55 | bool forwardTSNSupported = true; // Forward-TSN-Supported 49152 (0xC000) rfc3758 56 | }; 57 | 58 | }; // namespace sctp 59 | 60 | #endif 61 | 62 | -------------------------------------------------------------------------------- /src/sctp/chunks/OperationErrorChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/OperationErrorChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t OperationErrorChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t OperationErrorChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Get init pos 18 | size_t ini = writter.Mark(); 19 | 20 | //Write header 21 | writter.Set1(type); 22 | writter.Set1(0); 23 | //Skip length position 24 | size_t mark = writter.Skip(2); 25 | 26 | 27 | 28 | //Get length 29 | size_t length = writter.GetOffset(ini); 30 | //Set it 31 | writter.Set2(mark,length); 32 | 33 | //Done 34 | return length; 35 | } 36 | 37 | Chunk::shared OperationErrorChunk::Parse(BufferReader& reader) 38 | { 39 | //Check size 40 | if (!reader.Assert(20)) 41 | //Error 42 | return nullptr; 43 | 44 | //Get header 45 | size_t mark = reader.Mark(); 46 | uint8_t type = reader.Get1(); 47 | uint8_t flag = reader.Get1(); //Ignored, should be 0 48 | uint16_t length = reader.Get2(); 49 | 50 | //Check type 51 | if (type!=Type::ERROR) 52 | //Error 53 | return nullptr; 54 | 55 | //Create chunk 56 | auto oe = std::make_shared(); 57 | 58 | 59 | //Done 60 | return std::static_pointer_cast(oe); 61 | } 62 | 63 | }; 64 | -------------------------------------------------------------------------------- /src/sctp/chunks/OperationErrorChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_OPERATIONERRORCHUNK_H_ 2 | #define SCTP_OPERATIONERRORCHUNK_H_ 3 | 4 | 5 | #include 6 | #include "Buffer.h" 7 | #include "sctp/Chunk.h" 8 | #include "sctp/ErrorCause.h" 9 | 10 | namespace sctp 11 | { 12 | 13 | class OperationErrorChunk : public Chunk 14 | { 15 | public: 16 | OperationErrorChunk () : Chunk(Chunk::ERROR ) {} 17 | virtual ~OperationErrorChunk() = default; 18 | 19 | virtual size_t Serialize(BufferWritter& buffer) const override; 20 | virtual size_t GetSize() const override; 21 | 22 | static Chunk::shared Parse(BufferReader& reader); 23 | public: 24 | // 0 1 2 3 25 | // 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 26 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 27 | // | Type = 9 | Chunk Flags | Length | 28 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 29 | // \ \ 30 | // / one or more Error Causes / 31 | // \ \ 32 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 33 | std::vector errorCauses; 34 | }; 35 | 36 | }; // namespace sctp 37 | 38 | #endif 39 | 40 | -------------------------------------------------------------------------------- /src/sctp/chunks/PaddingChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/PaddingChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t PaddingChunk::GetSize() const 7 | { 8 | //Header + buffer 9 | return SizePad(4, 4+buffer.GetSize()); 10 | } 11 | 12 | size_t PaddingChunk::Serialize(BufferWritter& writter) const 13 | { 14 | //Check header length 15 | if (!writter.Assert(4)) 16 | return 0; 17 | 18 | //Get init pos 19 | size_t ini = writter.Mark(); 20 | 21 | //Write header 22 | writter.Set1(type); 23 | writter.Set1(flag); 24 | 25 | //Skip length position 26 | size_t mark = writter.Skip(2); 27 | 28 | //Check buffer size 29 | if (!writter.Assert(buffer.GetSize())) 30 | return 0; 31 | 32 | //Write padding 33 | writter.Set(buffer); 34 | 35 | //Get length 36 | size_t length = writter.GetOffset(ini); 37 | //Set it 38 | writter.Set2(mark,length); 39 | 40 | //Pad 41 | return writter.PadTo(4); 42 | } 43 | 44 | Chunk::shared PaddingChunk::Parse(BufferReader& reader) 45 | { 46 | //Check size 47 | if (!reader.Assert(4)) 48 | //Error 49 | return nullptr; 50 | 51 | //Get header 52 | size_t mark = reader.Mark(); 53 | uint8_t type = reader.Get1(); 54 | uint8_t flag = reader.Get1(); //Ignored, should be 0 55 | uint16_t length = reader.Get2(); 56 | 57 | //Check type 58 | if (type!=Type::PAD) 59 | //Error 60 | return nullptr; 61 | 62 | //Create chunk 63 | auto padding = std::make_shared(); 64 | 65 | //Check size 66 | if (length<4 || !reader.Assert(length-4)) 67 | //Error 68 | return nullptr; 69 | 70 | //Get cookie 71 | padding->buffer = reader.GetBuffer(length-4); 72 | 73 | //Pad input 74 | if (!reader.PadTo(4)) 75 | return nullptr; 76 | 77 | //Done 78 | return std::static_pointer_cast(padding); 79 | } 80 | 81 | }; 82 | -------------------------------------------------------------------------------- /src/sctp/chunks/PaddingChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_PADDINGCHUNK_H_ 2 | #define SCTP_PADDINGCHUNK_H_ 3 | 4 | #include "Buffer.h" 5 | #include "sctp/Chunk.h" 6 | 7 | namespace sctp 8 | { 9 | 10 | class PaddingChunk : public Chunk 11 | { 12 | public: 13 | PaddingChunk() : Chunk(PAD) {} 14 | virtual ~PaddingChunk() = default; 15 | 16 | virtual size_t Serialize(BufferWritter& buffer) const override; 17 | virtual size_t GetSize() const override; 18 | 19 | static Chunk::shared Parse(BufferReader& reader); 20 | public: 21 | // 0 1 2 3 22 | // 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 23 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 24 | // | Type = 0x84 | Flags=0 | Length | 25 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 | // | | 27 | // \ Padding Data / 28 | // / \ 29 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 30 | Buffer buffer; 31 | }; 32 | 33 | }; // namespace sctp 34 | 35 | #endif 36 | 37 | -------------------------------------------------------------------------------- /src/sctp/chunks/PayloadDataChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/PayloadDataChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t PayloadDataChunk::GetSize() const 7 | { 8 | //Header + attributes + user data 9 | return SizePad(4,16+userData.GetSize()); 10 | } 11 | 12 | size_t PayloadDataChunk::Serialize(BufferWritter& writter) const 13 | { 14 | //Check header length 15 | if (!writter.Assert(16)) 16 | return 0; 17 | 18 | //Get init pos 19 | size_t ini = writter.Mark(); 20 | 21 | //Creage flag 22 | uint8_t flag = (unordered & 0x04) | (beginingFragment & 0x02) | (endingFragment & 0x01); 23 | 24 | //Write header 25 | writter.Set1(type); 26 | writter.Set1(flag); 27 | //Skip length position 28 | size_t mark = writter.Skip(2); 29 | 30 | //Set attributes 31 | writter.Set4(transmissionSequenceNumber); 32 | writter.Set2(streamIdentifier); 33 | writter.Set2(streamSequenceNumber); 34 | writter.Set4(payloadProtocolIdentifier); 35 | 36 | //Check user data size 37 | if (!writter.Assert(userData.GetSize())) 38 | return 0; 39 | 40 | //Write cooke 41 | writter.Set(userData); 42 | 43 | ///Get length 44 | size_t length = writter.GetOffset(ini); 45 | //Set it 46 | writter.Set2(mark,length); 47 | 48 | //Pad 49 | return writter.PadTo(4); 50 | } 51 | 52 | Chunk::shared PayloadDataChunk::Parse(BufferReader& reader) 53 | { 54 | //Check size 55 | if (!reader.Assert(16)) 56 | //Error 57 | return nullptr; 58 | 59 | //Get header 60 | size_t mark = reader.Mark(); 61 | uint8_t type = reader.Get1(); 62 | uint8_t flag = reader.Get1(); 63 | uint16_t length = reader.Get2(); 64 | 65 | //Check type 66 | if (type!=Type::PDATA) 67 | //Error 68 | return nullptr; 69 | 70 | //Create chunk 71 | auto data = std::make_shared(); 72 | 73 | //Set flag bits 74 | data->unordered = flag & 0x04; 75 | data->beginingFragment = flag & 0x02; 76 | data->beginingFragment = flag & 0x01; 77 | 78 | //Read params 79 | data->transmissionSequenceNumber = reader.Get4(); 80 | data->streamIdentifier = reader.Get2(); 81 | data->streamSequenceNumber = reader.Get2(); 82 | data->payloadProtocolIdentifier = reader.Get4(); 83 | 84 | //Check size 85 | if (!reader.Assert(length-16)) 86 | //Error 87 | return nullptr; 88 | 89 | //Get user data 90 | data->userData = reader.GetBuffer(length-16); 91 | 92 | //Pad input 93 | if (!reader.PadTo(4)) 94 | return nullptr; 95 | 96 | //Done 97 | return std::static_pointer_cast(data); 98 | } 99 | 100 | }; 101 | -------------------------------------------------------------------------------- /src/sctp/chunks/PayloadDataChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_PAYLOADDATACHUNK_H_ 2 | #define SCTP_PAYLOADDATACHUNK_H_ 3 | 4 | 5 | #include "Buffer.h" 6 | #include "sctp/Chunk.h" 7 | 8 | namespace sctp 9 | { 10 | 11 | 12 | class PayloadDataChunk : public Chunk 13 | { 14 | public: 15 | PayloadDataChunk () : Chunk(Chunk::PDATA) {} 16 | virtual ~PayloadDataChunk() = default; 17 | 18 | virtual size_t Serialize(BufferWritter& buffer) const override; 19 | virtual size_t GetSize() const override; 20 | 21 | static Chunk::shared Parse(BufferReader& reader); 22 | public: 23 | // 0 1 2 3 24 | // 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 25 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 | // | Type = 0 | Reserved|U|B|E| Length | 27 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 28 | // | TSN | 29 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 30 | // | Stream Identifier S | Stream Sequence Number n | 31 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 32 | // | Payload Protocol Identifier | 33 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 34 | // \ \ 35 | // / User Data (seq n of Stream S) / 36 | // \ \ 37 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 38 | bool unordered = false; 39 | bool beginingFragment = false; 40 | bool endingFragment = false; 41 | uint32_t transmissionSequenceNumber = 0; 42 | uint16_t streamIdentifier = 0; 43 | uint16_t streamSequenceNumber = 0; 44 | uint32_t payloadProtocolIdentifier = 0; 45 | Buffer userData = 0; 46 | }; 47 | 48 | 49 | }; // namespace sctp 50 | 51 | #endif 52 | 53 | -------------------------------------------------------------------------------- /src/sctp/chunks/ReConfigChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/ReConfigChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t ReConfigChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t ReConfigChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Get init pos 18 | size_t ini = writter.Mark(); 19 | 20 | //Write header 21 | writter.Set1(type); 22 | writter.Set1(0); 23 | //Skip length position 24 | size_t mark = writter.Skip(2); 25 | 26 | 27 | 28 | //Get length 29 | size_t length = writter.GetOffset(ini); 30 | //Set it 31 | writter.Set2(mark,length); 32 | 33 | //Done 34 | return length; 35 | } 36 | 37 | Chunk::shared ReConfigChunk::Parse(BufferReader& reader) 38 | { 39 | //Check size 40 | if (!reader.Assert(20)) 41 | //Error 42 | return nullptr; 43 | 44 | //Get header 45 | size_t mark = reader.Mark(); 46 | uint8_t type = reader.Get1(); 47 | uint8_t flag = reader.Get1(); //Ignored, should be 0 48 | uint16_t length = reader.Get2(); 49 | 50 | //Check type 51 | if (type!=Type::RE_CONFIG) 52 | //Error 53 | return nullptr; 54 | 55 | //Create chunk 56 | auto reconfig = std::make_shared(); 57 | 58 | //Done 59 | return std::static_pointer_cast(reconfig); 60 | } 61 | 62 | }; 63 | -------------------------------------------------------------------------------- /src/sctp/chunks/ReConfigChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_RECONFIGCHUNK_H_ 2 | #define SCTP_RECONFIGCHUNK_H_ 3 | 4 | #include "Buffer.h" 5 | #include "sctp/Chunk.h" 6 | 7 | namespace sctp 8 | { 9 | 10 | class ReConfigChunk : public Chunk 11 | { 12 | public: 13 | ReConfigChunk () : Chunk(Chunk::RE_CONFIG) {} 14 | virtual ~ReConfigChunk() = default; 15 | 16 | virtual size_t Serialize(BufferWritter& buffer) const override; 17 | virtual size_t GetSize() const override; 18 | 19 | static Chunk::shared Parse(BufferReader& reader); 20 | public: 21 | 22 | // 0 1 2 3 23 | // 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 24 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 25 | // | Type = 130 | Chunk Flags | Chunk Length | 26 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 27 | // \ \ 28 | // / Re-configuration Parameter / 29 | // \ \ 30 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 31 | // \ \ 32 | // / Re-configuration Parameter (optional) / 33 | // \ \ 34 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 35 | 36 | Buffer cookie; 37 | }; 38 | 39 | }; // namespace sctp 40 | 41 | #endif 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/sctp/chunks/SelectiveAcknowledgementChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/SelectiveAcknowledgementChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t SelectiveAcknowledgementChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 16 + gapAckBlocks.size()*4 + duplicateTuplicateTrasnmissionSequenceNumbers.size()*4; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t SelectiveAcknowledgementChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Check header length 18 | if (!writter.Assert(16)) 19 | return 0; 20 | 21 | //Get init pos 22 | size_t ini = writter.Mark(); 23 | 24 | //Write header 25 | writter.Set1(type); 26 | writter.Set1(0); 27 | //Skip length position 28 | size_t mark = writter.Skip(2); 29 | 30 | //Set attributes 31 | writter.Set4(cumulativeTrasnmissionSequenceNumberAck); 32 | writter.Set4(adveritsedReceiverWindowCredit); 33 | writter.Set2(gapAckBlocks.size()); 34 | writter.Set2(duplicateTuplicateTrasnmissionSequenceNumbers.size()); 35 | 36 | //For each gap 37 | for (const auto& gap : gapAckBlocks) 38 | { 39 | //Check header length 40 | if (!writter.Assert(4)) 41 | return 0; 42 | ///Write gap 43 | writter.Set2(gap.first); 44 | writter.Set2(gap.second); 45 | } 46 | 47 | //For each duplicated tsn 48 | for (const auto& duplicated : duplicateTuplicateTrasnmissionSequenceNumbers) 49 | { 50 | //Check header length 51 | if (!writter.Assert(4)) 52 | return 0; 53 | ///Write gap 54 | writter.Set4(duplicated); 55 | } 56 | 57 | //Get length 58 | size_t length = writter.GetOffset(ini); 59 | //Set it 60 | writter.Set2(mark,length); 61 | 62 | //Done 63 | return length; 64 | } 65 | 66 | Chunk::shared SelectiveAcknowledgementChunk::Parse(BufferReader& reader) 67 | { 68 | //Check size 69 | if (!reader.Assert(16)) 70 | //Error 71 | return nullptr; 72 | 73 | //Get header 74 | size_t mark = reader.Mark(); 75 | uint8_t type = reader.Get1(); 76 | uint8_t flag = reader.Get1(); //Ignored, should be 0 77 | uint16_t length = reader.Get2(); 78 | 79 | //Check type 80 | if (type!=Type::SACK) 81 | //Error 82 | return nullptr; 83 | 84 | //Create chunk 85 | auto ack = std::make_shared(); 86 | 87 | //Read params 88 | ack->cumulativeTrasnmissionSequenceNumberAck = reader.Get4(); 89 | ack->adveritsedReceiverWindowCredit = reader.Get4(); 90 | const auto numGapAckBlocks = reader.Get2(); 91 | const auto numDuplicatedTSNs = reader.Get2(); 92 | 93 | //For each gap 94 | for (size_t i=0;igapAckBlocks.push_back({ 102 | reader.Get2(), 103 | reader.Get2() 104 | }); 105 | } 106 | 107 | //For each duplicated tsn 108 | for (size_t i=0;iduplicateTuplicateTrasnmissionSequenceNumbers.push_back(reader.Get4()); 116 | } 117 | 118 | //Check size 119 | if (!reader.Assert(length-16)) 120 | //Error 121 | return nullptr; 122 | 123 | //Done 124 | return std::static_pointer_cast(ack); 125 | } 126 | 127 | }; 128 | -------------------------------------------------------------------------------- /src/sctp/chunks/SelectiveAcknowledgementChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_SELECTIVEACKNOWLEDGEMENTCHUNK_H_ 2 | #define SCTP_SELECTIVEACKNOWLEDGEMENTCHUNK_H_ 3 | 4 | 5 | #include 6 | #include 7 | #include "Buffer.h" 8 | #include "sctp/Chunk.h" 9 | 10 | namespace sctp 11 | { 12 | 13 | class SelectiveAcknowledgementChunk : public Chunk 14 | { 15 | public: 16 | SelectiveAcknowledgementChunk () : Chunk(Chunk::SACK) {} 17 | virtual ~SelectiveAcknowledgementChunk() = default; 18 | 19 | virtual size_t Serialize(BufferWritter& buffer) const override; 20 | virtual size_t GetSize() const override; 21 | 22 | static Chunk::shared Parse(BufferReader& reader); 23 | public: 24 | // 0 1 2 3 25 | // 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 26 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 27 | // | Type = 3 |Chunk Flags | Chunk Length | 28 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 29 | // | Cumulative TSN Ack | 30 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 31 | // | Advertised Receiver Window Credit (a_rwnd) | 32 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 33 | // | Number of Gap Ack Blocks = N | Number of Duplicate TSNs = X | 34 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 35 | // | Gap Ack Block #1 Start | Gap Ack Block #1 End | 36 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 37 | // / / 38 | // \ ... \ 39 | // / / 40 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 41 | // | Gap Ack Block #N Start | Gap Ack Block #N End | 42 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 43 | // | Duplicate TSN 1 | 44 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 45 | // / / 46 | // \ ... \ 47 | // / / 48 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 49 | // | Duplicate TSN X | 50 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 51 | 52 | uint32_t cumulativeTrasnmissionSequenceNumberAck = 0; 53 | uint32_t adveritsedReceiverWindowCredit = 0; 54 | std::vector> gapAckBlocks; 55 | std::vector duplicateTuplicateTrasnmissionSequenceNumbers; 56 | }; 57 | 58 | }; // namespace sctp 59 | 60 | #endif 61 | 62 | -------------------------------------------------------------------------------- /src/sctp/chunks/ShutdownAcknowledgementChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/ShutdownAcknowledgementChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t ShutdownAcknowledgementChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t ShutdownAcknowledgementChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Get init pos 18 | size_t ini = writter.Mark(); 19 | 20 | //Write header 21 | writter.Set1(type); 22 | writter.Set1(0); 23 | //Skip length position 24 | size_t mark = writter.Skip(2); 25 | 26 | 27 | 28 | //Get length 29 | size_t length = writter.GetOffset(ini); 30 | //Set it 31 | writter.Set2(mark,length); 32 | 33 | //Done 34 | return length; 35 | } 36 | 37 | Chunk::shared ShutdownAcknowledgementChunk::Parse(BufferReader& reader) 38 | { 39 | //Check size 40 | if (!reader.Assert(20)) 41 | //Error 42 | return nullptr; 43 | 44 | //Get header 45 | size_t mark = reader.Mark(); 46 | uint8_t type = reader.Get1(); 47 | uint8_t flag = reader.Get1(); //Ignored, should be 0 48 | uint16_t length = reader.Get2(); 49 | 50 | //Check type 51 | if (type!=SHUTDOWN_ACK) 52 | //Error 53 | return nullptr; 54 | 55 | //Create chunk 56 | auto ack = std::make_shared(); 57 | 58 | //Done 59 | return std::static_pointer_cast(ack); 60 | } 61 | 62 | }; 63 | -------------------------------------------------------------------------------- /src/sctp/chunks/ShutdownAcknowledgementChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_SHUTDOWNACKNOWLEDGEMENTCHUNK_H_ 2 | #define SCTP_SHUTDOWNACKNOWLEDGEMENTCHUNK_H_ 3 | 4 | 5 | #include "Buffer.h" 6 | #include "sctp/Chunk.h" 7 | 8 | namespace sctp 9 | { 10 | 11 | class ShutdownAcknowledgementChunk : public Chunk 12 | { 13 | public: 14 | ShutdownAcknowledgementChunk () : Chunk(Chunk::SHUTDOWN_ACK) {} 15 | virtual ~ShutdownAcknowledgementChunk() = default; 16 | 17 | virtual size_t Serialize(BufferWritter& buffer) const override; 18 | virtual size_t GetSize() const override; 19 | 20 | static Chunk::shared Parse(BufferReader& reader); 21 | public: 22 | // 0 1 2 3 23 | // 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 24 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 25 | // | Type = 8 | Chunk Flags | Length = 8 | 26 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 27 | }; 28 | 29 | }; // namespace sctp 30 | 31 | #endif 32 | 33 | -------------------------------------------------------------------------------- /src/sctp/chunks/ShutdownAssociationChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/ShutdownAssociationChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t ShutdownAssociationChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t ShutdownAssociationChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Get init pos 18 | size_t ini = writter.Mark(); 19 | 20 | //Write header 21 | writter.Set1(type); 22 | writter.Set1(0); 23 | //Skip length position 24 | size_t mark = writter.Skip(2); 25 | 26 | 27 | 28 | //Get length 29 | size_t length = writter.GetOffset(ini); 30 | //Set it 31 | writter.Set2(mark,length); 32 | 33 | //Done 34 | return length; 35 | } 36 | 37 | Chunk::shared ShutdownAssociationChunk::Parse(BufferReader& reader) 38 | { 39 | //Check size 40 | if (!reader.Assert(20)) 41 | //Error 42 | return nullptr; 43 | 44 | //Get header 45 | size_t mark = reader.Mark(); 46 | uint8_t type = reader.Get1(); 47 | uint8_t flag = reader.Get1(); //Ignored, should be 0 48 | uint16_t length = reader.Get2(); 49 | 50 | //Check type 51 | if (type!=Type::SHUTDOWN) 52 | //Error 53 | return nullptr; 54 | 55 | //Create chunk 56 | auto shutdown = std::make_shared(); 57 | 58 | //Done 59 | return std::static_pointer_cast(shutdown); 60 | } 61 | 62 | }; 63 | -------------------------------------------------------------------------------- /src/sctp/chunks/ShutdownAssociationChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_SHUTDOWNASSOCIATIONCHUNK_H_ 2 | #define SCTP_SHUTDOWNASSOCIATIONCHUNK_H_ 3 | 4 | 5 | #include "Buffer.h" 6 | #include "sctp/Chunk.h" 7 | 8 | namespace sctp 9 | { 10 | 11 | class ShutdownAssociationChunk : public Chunk 12 | { 13 | public: 14 | ShutdownAssociationChunk () : Chunk(Chunk::SHUTDOWN) {} 15 | virtual ~ShutdownAssociationChunk() = default; 16 | 17 | virtual size_t Serialize(BufferWritter& buffer) const override; 18 | virtual size_t GetSize() const override; 19 | 20 | static Chunk::shared Parse(BufferReader& reader); 21 | public: 22 | // 0 1 2 3 23 | // 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 24 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 25 | // | Type = 7 | Chunk Flags | Length = 8 | 26 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 27 | // | Cumulative TSN Ack | 28 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 29 | uint32_t cumulativeTrasnmissionSequenceNumberAck; 30 | }; 31 | 32 | }; // namespace sctp 33 | 34 | #endif 35 | 36 | -------------------------------------------------------------------------------- /src/sctp/chunks/ShutdownCompleteChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/ShutdownCompleteChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t ShutdownCompleteChunk::GetSize() const 7 | { 8 | //Header + attributes 9 | size_t size = 20; 10 | 11 | //Done 12 | return size; 13 | } 14 | 15 | size_t ShutdownCompleteChunk::Serialize(BufferWritter& writter) const 16 | { 17 | //Get init pos 18 | size_t ini = writter.Mark(); 19 | 20 | //Write header 21 | writter.Set1(type); 22 | writter.Set1(0); 23 | //Skip length position 24 | size_t mark = writter.Skip(2); 25 | 26 | 27 | 28 | //Get length 29 | size_t length = writter.GetOffset(ini); 30 | //Set it 31 | writter.Set2(mark,length); 32 | 33 | //Done 34 | return length; 35 | } 36 | 37 | Chunk::shared ShutdownCompleteChunk::Parse(BufferReader& reader) 38 | { 39 | //Check size 40 | if (!reader.Assert(20)) 41 | //Error 42 | return nullptr; 43 | 44 | //Get header 45 | size_t mark = reader.Mark(); 46 | uint8_t type = reader.Get1(); 47 | uint8_t flag = reader.Get1(); //Ignored, should be 0 48 | uint16_t length = reader.Get2(); 49 | 50 | //Check type 51 | if (type!=Type::SHUTDOWN_COMPLETE) 52 | //Error 53 | return nullptr; 54 | 55 | //Create chunk 56 | auto complete = std::make_shared(); 57 | 58 | //Done 59 | return std::static_pointer_cast(complete); 60 | } 61 | 62 | }; 63 | -------------------------------------------------------------------------------- /src/sctp/chunks/ShutdownCompleteChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_SHUTDOWNCOMPLETECHUNK_H_ 2 | #define SCTP_SHUTDOWNCOMPLETECHUNK_H_ 3 | 4 | 5 | #include "Buffer.h" 6 | #include "sctp/Chunk.h" 7 | 8 | namespace sctp 9 | { 10 | 11 | class ShutdownCompleteChunk : public Chunk 12 | { 13 | public: 14 | ShutdownCompleteChunk () : Chunk(Chunk::SHUTDOWN_COMPLETE) {} 15 | virtual ~ShutdownCompleteChunk() = default; 16 | 17 | virtual size_t Serialize(BufferWritter& buffer) const override; 18 | virtual size_t GetSize() const override; 19 | 20 | static Chunk::shared Parse(BufferReader& reader); 21 | public: 22 | // 0 1 2 3 23 | // 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 24 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 25 | // | Type = 14 |Reserved |T| Length = 4 | 26 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 27 | bool verificationTag; 28 | }; 29 | 30 | }; // namespace sctp 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /src/sctp/chunks/UnknownChunk.cpp: -------------------------------------------------------------------------------- 1 | #include "sctp/chunks/UnknownChunk.h" 2 | 3 | namespace sctp 4 | { 5 | 6 | size_t UnknownChunk::GetSize() const 7 | { 8 | //Header + buffer 9 | return SizePad(4, 4+buffer.GetSize()); 10 | } 11 | 12 | size_t UnknownChunk::Serialize(BufferWritter& writter) const 13 | { 14 | //Get init pos 15 | size_t ini = writter.Mark(); 16 | 17 | //Check header length 18 | if (!writter.Assert(4)) 19 | return 0; 20 | 21 | //Write header 22 | writter.Set1(type); 23 | writter.Set1(flag); 24 | 25 | //Skip length position 26 | size_t mark = writter.Skip(2); 27 | 28 | //Check buffer size 29 | if (!writter.Assert(buffer.GetSize())) 30 | return 0; 31 | 32 | //Write buffer 33 | writter.Set(buffer); 34 | 35 | //Get length 36 | size_t length = writter.GetOffset(ini); 37 | //Set it 38 | writter.Set2(mark,length); 39 | 40 | //Pad 41 | return writter.PadTo(4); 42 | } 43 | 44 | Chunk::shared UnknownChunk::Parse(BufferReader& reader) 45 | { 46 | //Check size 47 | if (!reader.Assert(4)) 48 | //Error 49 | return nullptr; 50 | 51 | //Get header 52 | uint8_t type = reader.Get1(); 53 | uint8_t flag = reader.Get1(); //Ignored, should be 0 54 | uint16_t length = reader.Get2(); 55 | 56 | //Check size 57 | if (length<4 || !reader.Assert(length-4)) 58 | //Error 59 | return nullptr; 60 | 61 | //Create chunk 62 | auto unknown = std::make_shared(type); 63 | 64 | //Set attributes 65 | unknown->flag = flag; 66 | unknown->buffer = reader.GetBuffer(length-4); 67 | 68 | //Done 69 | return std::static_pointer_cast(unknown); 70 | } 71 | 72 | }; 73 | -------------------------------------------------------------------------------- /src/sctp/chunks/UnknownChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef SCTP_UNKNOWNCHUNK_H_ 2 | #define SCTP_UNKNOWNCHUNK_H_ 3 | 4 | #include "Buffer.h" 5 | #include "sctp/Chunk.h" 6 | 7 | namespace sctp 8 | { 9 | 10 | class UnknownChunk : public Chunk 11 | { 12 | public: 13 | UnknownChunk(uint8_t type) : Chunk(type) {} 14 | virtual ~UnknownChunk() = default; 15 | 16 | virtual size_t Serialize(BufferWritter& buffer) const override; 17 | virtual size_t GetSize() const override; 18 | 19 | static Chunk::shared Parse(BufferReader& reader); 20 | public: 21 | // 0 1 2 3 22 | // 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 23 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 24 | // | Type = ?? |Chunk Flags | Length | 25 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 26 | // / Buffer / 27 | // \ \ 28 | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 29 | Buffer buffer; 30 | }; 31 | 32 | }; // namespace sctp 33 | 34 | #endif 35 | 36 | --------------------------------------------------------------------------------