|  | /* | 
|  | *  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 <stddef.h> | 
|  |  | 
|  | #include <cstdint> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "absl/memory/memory.h" | 
|  | #include "absl/types/optional.h" | 
|  | #include "api/array_view.h" | 
|  | #include "net/dcsctp/common/math.h" | 
|  | #include "net/dcsctp/packet/bounded_byte_reader.h" | 
|  | #include "net/dcsctp/packet/bounded_byte_writer.h" | 
|  | #include "net/dcsctp/packet/chunk/chunk.h" | 
|  | #include "net/dcsctp/packet/crc32c.h" | 
|  | #include "net/dcsctp/public/dcsctp_options.h" | 
|  | #include "rtc_base/logging.h" | 
|  | #include "rtc_base/strings/string_format.h" | 
|  |  | 
|  | namespace dcsctp { | 
|  | namespace { | 
|  | constexpr size_t kMaxUdpPacketSize = 65535; | 
|  | constexpr size_t kChunkTlvHeaderSize = 4; | 
|  | constexpr size_t kExpectedDescriptorCount = 4; | 
|  | }  // namespace | 
|  |  | 
|  | /* | 
|  | 0                   1                   2                   3 | 
|  | 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 | 
|  | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 
|  | |     Source Port Number        |     Destination Port Number   | | 
|  | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 
|  | |                      Verification Tag                         | | 
|  | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 
|  | |                           Checksum                            | | 
|  | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 
|  | */ | 
|  |  | 
|  | SctpPacket::Builder::Builder(VerificationTag verification_tag, | 
|  | const DcSctpOptions& options) | 
|  | : verification_tag_(verification_tag), | 
|  | source_port_(options.local_port), | 
|  | dest_port_(options.remote_port), | 
|  | max_packet_size_(RoundDownTo4(options.mtu)) {} | 
|  |  | 
|  | SctpPacket::Builder& SctpPacket::Builder::Add(const Chunk& chunk) { | 
|  | if (out_.empty()) { | 
|  | out_.reserve(max_packet_size_); | 
|  | out_.resize(SctpPacket::kHeaderSize); | 
|  | BoundedByteWriter<kHeaderSize> buffer(out_); | 
|  | buffer.Store16<0>(source_port_); | 
|  | buffer.Store16<2>(dest_port_); | 
|  | buffer.Store32<4>(*verification_tag_); | 
|  | // Checksum is at offset 8 - written when calling Build(), if configured. | 
|  | } | 
|  | RTC_DCHECK(IsDivisibleBy4(out_.size())); | 
|  |  | 
|  | chunk.SerializeTo(out_); | 
|  | if (out_.size() % 4 != 0) { | 
|  | out_.resize(RoundUpTo4(out_.size())); | 
|  | } | 
|  |  | 
|  | RTC_DCHECK(out_.size() <= max_packet_size_) | 
|  | << "Exceeded max size, data=" << out_.size() | 
|  | << ", max_size=" << max_packet_size_; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | size_t SctpPacket::Builder::bytes_remaining() const { | 
|  | if (out_.empty()) { | 
|  | // The packet header (CommonHeader) hasn't been written yet: | 
|  | return max_packet_size_ - kHeaderSize; | 
|  | } else if (out_.size() > max_packet_size_) { | 
|  | RTC_DCHECK_NOTREACHED() << "Exceeded max size, data=" << out_.size() | 
|  | << ", max_size=" << max_packet_size_; | 
|  | return 0; | 
|  | } | 
|  | return max_packet_size_ - out_.size(); | 
|  | } | 
|  |  | 
|  | std::vector<uint8_t> SctpPacket::Builder::Build(bool write_checksum) { | 
|  | std::vector<uint8_t> out; | 
|  | out_.swap(out); | 
|  |  | 
|  | if (!out.empty() && write_checksum) { | 
|  | uint32_t crc = GenerateCrc32C(out); | 
|  | BoundedByteWriter<kHeaderSize>(out).Store32<8>(crc); | 
|  | } | 
|  |  | 
|  | RTC_DCHECK(out.size() <= max_packet_size_) | 
|  | << "Exceeded max size, data=" << out.size() | 
|  | << ", max_size=" << max_packet_size_; | 
|  |  | 
|  | return out; | 
|  | } | 
|  |  | 
|  | absl::optional<SctpPacket> SctpPacket::Parse(rtc::ArrayView<const uint8_t> data, | 
|  | const DcSctpOptions& options) { | 
|  | if (data.size() < kHeaderSize + kChunkTlvHeaderSize || | 
|  | data.size() > kMaxUdpPacketSize) { | 
|  | RTC_DLOG(LS_WARNING) << "Invalid packet size"; | 
|  | return absl::nullopt; | 
|  | } | 
|  |  | 
|  | BoundedByteReader<kHeaderSize> reader(data); | 
|  |  | 
|  | CommonHeader common_header; | 
|  | common_header.source_port = reader.Load16<0>(); | 
|  | common_header.destination_port = reader.Load16<2>(); | 
|  | common_header.verification_tag = VerificationTag(reader.Load32<4>()); | 
|  | common_header.checksum = reader.Load32<8>(); | 
|  |  | 
|  | // Create a copy of the packet, which will be held by this object. | 
|  | std::vector<uint8_t> data_copy = | 
|  | std::vector<uint8_t>(data.begin(), data.end()); | 
|  |  | 
|  | if (options.disable_checksum_verification || | 
|  | (options.enable_zero_checksum && common_header.checksum == 0u)) { | 
|  | // https://www.ietf.org/archive/id/draft-tuexen-tsvwg-sctp-zero-checksum-01.html#section-4.3: | 
|  | // If an end point has sent the Zero Checksum Acceptable Chunk Parameter in | 
|  | // an INIT or INIT ACK chunk, it MUST accept SCTP packets using an incorrect | 
|  | // checksum value of zero in addition to SCTP packets containing the correct | 
|  | // CRC32c checksum value for this association. | 
|  | } else { | 
|  | // Verify the checksum. The checksum field must be zero when that's done. | 
|  | BoundedByteWriter<kHeaderSize>(data_copy).Store32<8>(0); | 
|  | uint32_t calculated_checksum = GenerateCrc32C(data_copy); | 
|  | if (calculated_checksum != common_header.checksum) { | 
|  | RTC_DLOG(LS_WARNING) << rtc::StringFormat( | 
|  | "Invalid packet checksum, packet_checksum=0x%08x, " | 
|  | "calculated_checksum=0x%08x", | 
|  | common_header.checksum, calculated_checksum); | 
|  | return absl::nullopt; | 
|  | } | 
|  | // Restore the checksum in the header. | 
|  | BoundedByteWriter<kHeaderSize>(data_copy).Store32<8>( | 
|  | common_header.checksum); | 
|  | } | 
|  |  | 
|  | // Validate and parse the chunk headers in the message. | 
|  | /* | 
|  | 0                   1                   2                   3 | 
|  | 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 | 
|  | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 
|  | |   Chunk Type  | Chunk  Flags  |        Chunk Length           | | 
|  | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | 
|  | */ | 
|  |  | 
|  | std::vector<ChunkDescriptor> descriptors; | 
|  | descriptors.reserve(kExpectedDescriptorCount); | 
|  | rtc::ArrayView<const uint8_t> descriptor_data = | 
|  | rtc::ArrayView<const uint8_t>(data_copy).subview(kHeaderSize); | 
|  | while (!descriptor_data.empty()) { | 
|  | if (descriptor_data.size() < kChunkTlvHeaderSize) { | 
|  | RTC_DLOG(LS_WARNING) << "Too small chunk"; | 
|  | return absl::nullopt; | 
|  | } | 
|  | BoundedByteReader<kChunkTlvHeaderSize> chunk_header(descriptor_data); | 
|  | uint8_t type = chunk_header.Load8<0>(); | 
|  | uint8_t flags = chunk_header.Load8<1>(); | 
|  | uint16_t length = chunk_header.Load16<2>(); | 
|  | uint16_t padded_length = RoundUpTo4(length); | 
|  | if (padded_length > descriptor_data.size()) { | 
|  | RTC_DLOG(LS_WARNING) << "Too large chunk. length=" << length | 
|  | << ", remaining=" << descriptor_data.size(); | 
|  | return absl::nullopt; | 
|  | } else if (padded_length < kChunkTlvHeaderSize) { | 
|  | RTC_DLOG(LS_WARNING) << "Too small chunk. length=" << length; | 
|  | return absl::nullopt; | 
|  | } | 
|  | descriptors.emplace_back(type, flags, | 
|  | descriptor_data.subview(0, padded_length)); | 
|  | descriptor_data = descriptor_data.subview(padded_length); | 
|  | } | 
|  |  | 
|  | // Note that iterators (and pointer) are guaranteed to be stable when moving a | 
|  | // std::vector, and `descriptors` have pointers to within `data_copy`. | 
|  | return SctpPacket(common_header, std::move(data_copy), | 
|  | std::move(descriptors)); | 
|  | } | 
|  | }  // namespace dcsctp |