|  | /* | 
|  | *  Copyright (c) 2021 The WebRTC project authors. All Rights Reserved. | 
|  | * | 
|  | *  Use of this source code is governed by a BSD-style license | 
|  | *  that can be found in the LICENSE file in the root of the source | 
|  | *  tree. An additional intellectual property rights grant can be found | 
|  | *  in the file PATENTS.  All contributing project authors may | 
|  | *  be found in the AUTHORS file in the root of the source tree. | 
|  | */ | 
|  | #include "net/dcsctp/packet/sctp_packet.h" | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "api/array_view.h" | 
|  | #include "net/dcsctp/common/internal_types.h" | 
|  | #include "net/dcsctp/packet/chunk/abort_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/cookie_ack_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/data_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/init_chunk.h" | 
|  | #include "net/dcsctp/packet/chunk/sack_chunk.h" | 
|  | #include "net/dcsctp/packet/error_cause/error_cause.h" | 
|  | #include "net/dcsctp/packet/error_cause/user_initiated_abort_cause.h" | 
|  | #include "net/dcsctp/packet/parameter/parameter.h" | 
|  | #include "net/dcsctp/packet/tlv_trait.h" | 
|  | #include "net/dcsctp/testing/testing_macros.h" | 
|  | #include "rtc_base/gunit.h" | 
|  | #include "test/gmock.h" | 
|  |  | 
|  | namespace dcsctp { | 
|  | namespace { | 
|  | using ::testing::SizeIs; | 
|  |  | 
|  | constexpr VerificationTag kVerificationTag = VerificationTag(0x12345678); | 
|  |  | 
|  | TEST(SctpPacketTest, DeserializeSimplePacketFromCapture) { | 
|  | /* | 
|  | Stream Control Transmission Protocol, Src Port: 5000 (5000), Dst Port: 5000 | 
|  | (5000) Source port: 5000 Destination port: 5000 Verification tag: 0x00000000 | 
|  | [Association index: 1] | 
|  | Checksum: 0xaa019d33 [unverified] | 
|  | [Checksum Status: Unverified] | 
|  | INIT chunk (Outbound streams: 1000, inbound streams: 1000) | 
|  | Chunk type: INIT (1) | 
|  | Chunk flags: 0x00 | 
|  | Chunk length: 90 | 
|  | Initiate tag: 0x0eddca08 | 
|  | Advertised receiver window credit (a_rwnd): 131072 | 
|  | Number of outbound streams: 1000 | 
|  | Number of inbound streams: 1000 | 
|  | Initial TSN: 1426601527 | 
|  | ECN parameter | 
|  | Parameter type: ECN (0x8000) | 
|  | Parameter length: 4 | 
|  | Forward TSN supported parameter | 
|  | Parameter type: Forward TSN supported (0xc000) | 
|  | Parameter length: 4 | 
|  | Supported Extensions parameter (Supported types: FORWARD_TSN, AUTH, | 
|  | ASCONF, ASCONF_ACK, RE_CONFIG) Parameter type: Supported Extensions | 
|  | (0x8008) Parameter length: 9 Supported chunk type: FORWARD_TSN (192) Supported | 
|  | chunk type: AUTH (15) Supported chunk type: ASCONF (193) Supported chunk type: | 
|  | ASCONF_ACK (128) Supported chunk type: RE_CONFIG (130) Parameter padding: | 
|  | 000000 Random parameter Parameter type: Random (0x8002) Parameter length: 36 | 
|  | Random number: c5a86155090e6f420050634cc8d6b908dfd53e17c99cb143… | 
|  | Requested HMAC Algorithm parameter (Supported HMACs: SHA-1) | 
|  | Parameter type: Requested HMAC Algorithm (0x8004) | 
|  | Parameter length: 6 | 
|  | HMAC identifier: SHA-1 (1) | 
|  | Parameter padding: 0000 | 
|  | Authenticated Chunk list parameter (Chunk types to be authenticated: | 
|  | ASCONF_ACK, ASCONF) Parameter type: Authenticated Chunk list | 
|  | (0x8003) Parameter length: 6 Chunk type: ASCONF_ACK (128) Chunk type: ASCONF | 
|  | (193) Chunk padding: 0000 | 
|  | */ | 
|  |  | 
|  | uint8_t data[] = { | 
|  | 0x13, 0x88, 0x13, 0x88, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x01, 0x9d, 0x33, | 
|  | 0x01, 0x00, 0x00, 0x5a, 0x0e, 0xdd, 0xca, 0x08, 0x00, 0x02, 0x00, 0x00, | 
|  | 0x03, 0xe8, 0x03, 0xe8, 0x55, 0x08, 0x36, 0x37, 0x80, 0x00, 0x00, 0x04, | 
|  | 0xc0, 0x00, 0x00, 0x04, 0x80, 0x08, 0x00, 0x09, 0xc0, 0x0f, 0xc1, 0x80, | 
|  | 0x82, 0x00, 0x00, 0x00, 0x80, 0x02, 0x00, 0x24, 0xc5, 0xa8, 0x61, 0x55, | 
|  | 0x09, 0x0e, 0x6f, 0x42, 0x00, 0x50, 0x63, 0x4c, 0xc8, 0xd6, 0xb9, 0x08, | 
|  | 0xdf, 0xd5, 0x3e, 0x17, 0xc9, 0x9c, 0xb1, 0x43, 0x28, 0x4e, 0xaf, 0x64, | 
|  | 0x68, 0x2a, 0xc2, 0x97, 0x80, 0x04, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, | 
|  | 0x80, 0x03, 0x00, 0x06, 0x80, 0xc1, 0x00, 0x00}; | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(data)); | 
|  | EXPECT_EQ(packet.common_header().source_port, 5000); | 
|  | EXPECT_EQ(packet.common_header().destination_port, 5000); | 
|  | EXPECT_EQ(packet.common_header().verification_tag, VerificationTag(0)); | 
|  | EXPECT_EQ(packet.common_header().checksum, 0xaa019d33); | 
|  |  | 
|  | EXPECT_THAT(packet.descriptors(), SizeIs(1)); | 
|  | EXPECT_EQ(packet.descriptors()[0].type, InitChunk::kType); | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(InitChunk init, | 
|  | InitChunk::Parse(packet.descriptors()[0].data)); | 
|  | EXPECT_EQ(init.initial_tsn(), TSN(1426601527)); | 
|  | } | 
|  |  | 
|  | TEST(SctpPacketTest, DeserializePacketWithTwoChunks) { | 
|  | /* | 
|  | Stream Control Transmission Protocol, Src Port: 1234 (1234), | 
|  | Dst Port: 4321 (4321) | 
|  | Source port: 1234 | 
|  | Destination port: 4321 | 
|  | Verification tag: 0x697e3a4e | 
|  | [Association index: 3] | 
|  | Checksum: 0xc06e8b36 [unverified] | 
|  | [Checksum Status: Unverified] | 
|  | COOKIE_ACK chunk | 
|  | Chunk type: COOKIE_ACK (11) | 
|  | Chunk flags: 0x00 | 
|  | Chunk length: 4 | 
|  | SACK chunk (Cumulative TSN: 2930332242, a_rwnd: 131072, | 
|  | gaps: 0, duplicate TSNs: 0) | 
|  | Chunk type: SACK (3) | 
|  | Chunk flags: 0x00 | 
|  | Chunk length: 16 | 
|  | Cumulative TSN ACK: 2930332242 | 
|  | Advertised receiver window credit (a_rwnd): 131072 | 
|  | Number of gap acknowledgement blocks: 0 | 
|  | Number of duplicated TSNs: 0 | 
|  | */ | 
|  |  | 
|  | uint8_t data[] = {0x04, 0xd2, 0x10, 0xe1, 0x69, 0x7e, 0x3a, 0x4e, | 
|  | 0xc0, 0x6e, 0x8b, 0x36, 0x0b, 0x00, 0x00, 0x04, | 
|  | 0x03, 0x00, 0x00, 0x10, 0xae, 0xa9, 0x52, 0x52, | 
|  | 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(data)); | 
|  | EXPECT_EQ(packet.common_header().source_port, 1234); | 
|  | EXPECT_EQ(packet.common_header().destination_port, 4321); | 
|  | EXPECT_EQ(packet.common_header().verification_tag, | 
|  | VerificationTag(0x697e3a4eu)); | 
|  | EXPECT_EQ(packet.common_header().checksum, 0xc06e8b36u); | 
|  |  | 
|  | EXPECT_THAT(packet.descriptors(), SizeIs(2)); | 
|  | EXPECT_EQ(packet.descriptors()[0].type, CookieAckChunk::kType); | 
|  | EXPECT_EQ(packet.descriptors()[1].type, SackChunk::kType); | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN( | 
|  | CookieAckChunk cookie_ack, | 
|  | CookieAckChunk::Parse(packet.descriptors()[0].data)); | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(SackChunk sack, | 
|  | SackChunk::Parse(packet.descriptors()[1].data)); | 
|  | } | 
|  |  | 
|  | TEST(SctpPacketTest, DeserializePacketWithWrongChecksum) { | 
|  | /* | 
|  | Stream Control Transmission Protocol, Src Port: 5000 (5000), | 
|  | Dst Port: 5000 (5000) | 
|  | Source port: 5000 | 
|  | Destination port: 5000 | 
|  | Verification tag: 0x0eddca08 | 
|  | [Association index: 1] | 
|  | Checksum: 0x2a81f531 [unverified] | 
|  | [Checksum Status: Unverified] | 
|  | SACK chunk (Cumulative TSN: 1426601536, a_rwnd: 131072, | 
|  | gaps: 0, duplicate TSNs: 0) | 
|  | Chunk type: SACK (3) | 
|  | Chunk flags: 0x00 | 
|  | Chunk length: 16 | 
|  | Cumulative TSN ACK: 1426601536 | 
|  | Advertised receiver window credit (a_rwnd): 131072 | 
|  | Number of gap acknowledgement blocks: 0 | 
|  | Number of duplicated TSNs: 0 | 
|  | */ | 
|  |  | 
|  | uint8_t data[] = {0x13, 0x88, 0x13, 0x88, 0x0e, 0xdd, 0xca, 0x08, 0x2a, 0x81, | 
|  | 0xf5, 0x31, 0x03, 0x00, 0x00, 0x10, 0x55, 0x08, 0x36, 0x40, | 
|  | 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | 
|  |  | 
|  | EXPECT_FALSE(SctpPacket::Parse(data).has_value()); | 
|  | } | 
|  |  | 
|  | TEST(SctpPacketTest, DeserializePacketDontValidateChecksum) { | 
|  | /* | 
|  | Stream Control Transmission Protocol, Src Port: 5000 (5000), | 
|  | Dst Port: 5000 (5000) | 
|  | Source port: 5000 | 
|  | Destination port: 5000 | 
|  | Verification tag: 0x0eddca08 | 
|  | [Association index: 1] | 
|  | Checksum: 0x2a81f531 [unverified] | 
|  | [Checksum Status: Unverified] | 
|  | SACK chunk (Cumulative TSN: 1426601536, a_rwnd: 131072, | 
|  | gaps: 0, duplicate TSNs: 0) | 
|  | Chunk type: SACK (3) | 
|  | Chunk flags: 0x00 | 
|  | Chunk length: 16 | 
|  | Cumulative TSN ACK: 1426601536 | 
|  | Advertised receiver window credit (a_rwnd): 131072 | 
|  | Number of gap acknowledgement blocks: 0 | 
|  | Number of duplicated TSNs: 0 | 
|  | */ | 
|  |  | 
|  | uint8_t data[] = {0x13, 0x88, 0x13, 0x88, 0x0e, 0xdd, 0xca, 0x08, 0x2a, 0x81, | 
|  | 0xf5, 0x31, 0x03, 0x00, 0x00, 0x10, 0x55, 0x08, 0x36, 0x40, | 
|  | 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN( | 
|  | SctpPacket packet, | 
|  | SctpPacket::Parse(data, /*disable_checksum_verification=*/true)); | 
|  | EXPECT_EQ(packet.common_header().source_port, 5000); | 
|  | EXPECT_EQ(packet.common_header().destination_port, 5000); | 
|  | EXPECT_EQ(packet.common_header().verification_tag, | 
|  | VerificationTag(0x0eddca08u)); | 
|  | EXPECT_EQ(packet.common_header().checksum, 0x2a81f531u); | 
|  | } | 
|  |  | 
|  | TEST(SctpPacketTest, SerializeAndDeserializeSingleChunk) { | 
|  | SctpPacket::Builder b(kVerificationTag, {}); | 
|  | InitChunk init(/*initiate_tag=*/VerificationTag(123), /*a_rwnd=*/456, | 
|  | /*nbr_outbound_streams=*/65535, | 
|  | /*nbr_inbound_streams=*/65534, /*initial_tsn=*/TSN(789), | 
|  | /*parameters=*/Parameters()); | 
|  |  | 
|  | b.Add(init); | 
|  | std::vector<uint8_t> serialized = b.Build(); | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(serialized)); | 
|  |  | 
|  | EXPECT_EQ(packet.common_header().verification_tag, kVerificationTag); | 
|  |  | 
|  | ASSERT_THAT(packet.descriptors(), SizeIs(1)); | 
|  | EXPECT_EQ(packet.descriptors()[0].type, InitChunk::kType); | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(InitChunk deserialized, | 
|  | InitChunk::Parse(packet.descriptors()[0].data)); | 
|  | EXPECT_EQ(deserialized.initiate_tag(), VerificationTag(123)); | 
|  | EXPECT_EQ(deserialized.a_rwnd(), 456u); | 
|  | EXPECT_EQ(deserialized.nbr_outbound_streams(), 65535u); | 
|  | EXPECT_EQ(deserialized.nbr_inbound_streams(), 65534u); | 
|  | EXPECT_EQ(deserialized.initial_tsn(), TSN(789)); | 
|  | } | 
|  |  | 
|  | TEST(SctpPacketTest, SerializeAndDeserializeThreeChunks) { | 
|  | SctpPacket::Builder b(kVerificationTag, {}); | 
|  | b.Add(SackChunk(/*cumulative_tsn_ack=*/TSN(999), /*a_rwnd=*/456, | 
|  | {SackChunk::GapAckBlock(2, 3)}, | 
|  | /*duplicate_tsns=*/{TSN(1), TSN(2), TSN(3)})); | 
|  | b.Add(DataChunk(TSN(123), StreamID(456), SSN(789), PPID(9090), | 
|  | /*payload=*/{1, 2, 3, 4, 5}, | 
|  | /*options=*/{})); | 
|  | b.Add(DataChunk(TSN(124), StreamID(654), SSN(987), PPID(909), | 
|  | /*payload=*/{5, 4, 3, 3, 1}, | 
|  | /*options=*/{})); | 
|  |  | 
|  | std::vector<uint8_t> serialized = b.Build(); | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(serialized)); | 
|  |  | 
|  | EXPECT_EQ(packet.common_header().verification_tag, kVerificationTag); | 
|  |  | 
|  | ASSERT_THAT(packet.descriptors(), SizeIs(3)); | 
|  | EXPECT_EQ(packet.descriptors()[0].type, SackChunk::kType); | 
|  | EXPECT_EQ(packet.descriptors()[1].type, DataChunk::kType); | 
|  | EXPECT_EQ(packet.descriptors()[2].type, DataChunk::kType); | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(SackChunk sack, | 
|  | SackChunk::Parse(packet.descriptors()[0].data)); | 
|  | EXPECT_EQ(sack.cumulative_tsn_ack(), TSN(999)); | 
|  | EXPECT_EQ(sack.a_rwnd(), 456u); | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(DataChunk data1, | 
|  | DataChunk::Parse(packet.descriptors()[1].data)); | 
|  | EXPECT_EQ(data1.tsn(), TSN(123)); | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(DataChunk data2, | 
|  | DataChunk::Parse(packet.descriptors()[2].data)); | 
|  | EXPECT_EQ(data2.tsn(), TSN(124)); | 
|  | } | 
|  |  | 
|  | TEST(SctpPacketTest, ParseAbortWithEmptyCause) { | 
|  | SctpPacket::Builder b(kVerificationTag, {}); | 
|  | b.Add(AbortChunk( | 
|  | /*filled_in_verification_tag=*/true, | 
|  | Parameters::Builder().Add(UserInitiatedAbortCause("")).Build())); | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(SctpPacket packet, SctpPacket::Parse(b.Build())); | 
|  |  | 
|  | EXPECT_EQ(packet.common_header().verification_tag, kVerificationTag); | 
|  |  | 
|  | ASSERT_THAT(packet.descriptors(), SizeIs(1)); | 
|  | EXPECT_EQ(packet.descriptors()[0].type, AbortChunk::kType); | 
|  |  | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN(AbortChunk abort, | 
|  | AbortChunk::Parse(packet.descriptors()[0].data)); | 
|  | ASSERT_HAS_VALUE_AND_ASSIGN( | 
|  | UserInitiatedAbortCause cause, | 
|  | abort.error_causes().get<UserInitiatedAbortCause>()); | 
|  | EXPECT_EQ(cause.upper_layer_abort_reason(), ""); | 
|  | } | 
|  |  | 
|  | TEST(SctpPacketTest, DetectPacketWithZeroSizeChunk) { | 
|  | uint8_t data[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0x0a, 0x0a, 0x0a, 0x5c, | 
|  | 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x00, 0x00, 0x00}; | 
|  |  | 
|  | EXPECT_FALSE(SctpPacket::Parse(data, true).has_value()); | 
|  | } | 
|  | }  // namespace | 
|  | }  // namespace dcsctp |