xref: /aosp_15_r20/external/webrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "modules/rtp_rtcp/source/rtcp_packet/sdes.h"
12 
13 #include <string.h>
14 
15 #include <utility>
16 
17 #include "absl/strings/string_view.h"
18 #include "modules/rtp_rtcp/source/byte_io.h"
19 #include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
20 #include "rtc_base/checks.h"
21 #include "rtc_base/logging.h"
22 
23 namespace webrtc {
24 namespace rtcp {
25 constexpr uint8_t Sdes::kPacketType;
26 constexpr size_t Sdes::kMaxNumberOfChunks;
27 // Source Description (SDES) (RFC 3550).
28 //
29 //         0                   1                   2                   3
30 //         0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
31 //        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
32 // header |V=2|P|    SC   |  PT=SDES=202  |             length            |
33 //        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
34 // chunk  |                          SSRC/CSRC_1                          |
35 //   1    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
36 //        |                           SDES items                          |
37 //        |                              ...                              |
38 //        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
39 // chunk  |                          SSRC/CSRC_2                          |
40 //   2    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
41 //        |                           SDES items                          |
42 //        |                              ...                              |
43 //        +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
44 //
45 // Canonical End-Point Identifier SDES Item (CNAME)
46 //
47 //    0                   1                   2                   3
48 //    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
49 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50 //   |    CNAME=1    |     length    | user and domain name        ...
51 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52 namespace {
53 const uint8_t kTerminatorTag = 0;
54 const uint8_t kCnameTag = 1;
55 
ChunkSize(const Sdes::Chunk & chunk)56 size_t ChunkSize(const Sdes::Chunk& chunk) {
57   // Chunk:
58   // SSRC/CSRC (4 bytes) | CNAME=1 (1 byte) | length (1 byte) | cname | padding.
59   size_t chunk_payload_size = 4 + 1 + 1 + chunk.cname.size();
60   size_t padding_size = 4 - (chunk_payload_size % 4);  // Minimum 1.
61   return chunk_payload_size + padding_size;
62 }
63 }  // namespace
64 
Sdes()65 Sdes::Sdes() : block_length_(RtcpPacket::kHeaderLength) {}
66 
~Sdes()67 Sdes::~Sdes() {}
68 
Parse(const CommonHeader & packet)69 bool Sdes::Parse(const CommonHeader& packet) {
70   RTC_DCHECK_EQ(packet.type(), kPacketType);
71 
72   uint8_t number_of_chunks = packet.count();
73   std::vector<Chunk> chunks;  // Read chunk into temporary array, so that in
74                               // case of an error original array would stay
75                               // unchanged.
76   size_t block_length = kHeaderLength;
77 
78   if (packet.payload_size_bytes() % 4 != 0) {
79     RTC_LOG(LS_WARNING) << "Invalid payload size "
80                         << packet.payload_size_bytes()
81                         << " bytes for a valid Sdes packet. Size should be"
82                            " multiple of 4 bytes";
83   }
84   const uint8_t* const payload_end =
85       packet.payload() + packet.payload_size_bytes();
86   const uint8_t* looking_at = packet.payload();
87   chunks.resize(number_of_chunks);
88   for (size_t i = 0; i < number_of_chunks;) {
89     // Each chunk consumes at least 8 bytes.
90     if (payload_end - looking_at < 8) {
91       RTC_LOG(LS_WARNING) << "Not enough space left for chunk #" << (i + 1);
92       return false;
93     }
94     chunks[i].ssrc = ByteReader<uint32_t>::ReadBigEndian(looking_at);
95     looking_at += sizeof(uint32_t);
96     bool cname_found = false;
97 
98     uint8_t item_type;
99     while ((item_type = *(looking_at++)) != kTerminatorTag) {
100       if (looking_at >= payload_end) {
101         RTC_LOG(LS_WARNING)
102             << "Unexpected end of packet while reading chunk #" << (i + 1)
103             << ". Expected to find size of the text.";
104         return false;
105       }
106       uint8_t item_length = *(looking_at++);
107       const size_t kTerminatorSize = 1;
108       if (looking_at + item_length + kTerminatorSize > payload_end) {
109         RTC_LOG(LS_WARNING)
110             << "Unexpected end of packet while reading chunk #" << (i + 1)
111             << ". Expected to find text of size " << item_length;
112         return false;
113       }
114       if (item_type == kCnameTag) {
115         if (cname_found) {
116           RTC_LOG(LS_WARNING)
117               << "Found extra CNAME for same ssrc in chunk #" << (i + 1);
118           return false;
119         }
120         cname_found = true;
121         chunks[i].cname.assign(reinterpret_cast<const char*>(looking_at),
122                                item_length);
123       }
124       looking_at += item_length;
125     }
126     if (cname_found) {
127       // block_length calculates length of the packet that would be generated by
128       // Build/Create functions. Adjust it same way WithCName function does.
129       block_length += ChunkSize(chunks[i]);
130       ++i;
131     } else {
132       // RFC states CNAME item is mandatory.
133       // But same time it allows chunk without items.
134       // So while parsing, ignore all chunks without cname,
135       // but do not fail the parse.
136       RTC_LOG(LS_WARNING) << "CNAME not found for ssrc " << chunks[i].ssrc;
137       --number_of_chunks;
138       chunks.resize(number_of_chunks);
139     }
140     // Adjust to 32bit boundary.
141     looking_at += (payload_end - looking_at) % 4;
142   }
143 
144   chunks_ = std::move(chunks);
145   block_length_ = block_length;
146   return true;
147 }
148 
AddCName(uint32_t ssrc,absl::string_view cname)149 bool Sdes::AddCName(uint32_t ssrc, absl::string_view cname) {
150   RTC_DCHECK_LE(cname.length(), 0xffu);
151   if (chunks_.size() >= kMaxNumberOfChunks) {
152     RTC_LOG(LS_WARNING) << "Max SDES chunks reached.";
153     return false;
154   }
155   Chunk chunk;
156   chunk.ssrc = ssrc;
157   chunk.cname = std::string(cname);
158   chunks_.push_back(chunk);
159   block_length_ += ChunkSize(chunk);
160   return true;
161 }
162 
BlockLength() const163 size_t Sdes::BlockLength() const {
164   return block_length_;
165 }
166 
Create(uint8_t * packet,size_t * index,size_t max_length,PacketReadyCallback callback) const167 bool Sdes::Create(uint8_t* packet,
168                   size_t* index,
169                   size_t max_length,
170                   PacketReadyCallback callback) const {
171   while (*index + BlockLength() > max_length) {
172     if (!OnBufferFull(packet, index, callback))
173       return false;
174   }
175   const size_t index_end = *index + BlockLength();
176   CreateHeader(chunks_.size(), kPacketType, HeaderLength(), packet, index);
177 
178   for (const Sdes::Chunk& chunk : chunks_) {
179     ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 0], chunk.ssrc);
180     ByteWriter<uint8_t>::WriteBigEndian(&packet[*index + 4], kCnameTag);
181     ByteWriter<uint8_t>::WriteBigEndian(
182         &packet[*index + 5], static_cast<uint8_t>(chunk.cname.size()));
183     memcpy(&packet[*index + 6], chunk.cname.data(), chunk.cname.size());
184     *index += (6 + chunk.cname.size());
185 
186     // In each chunk, the list of items must be terminated by one or more null
187     // octets. The next chunk must start on a 32-bit boundary.
188     // CNAME (1 byte) | length (1 byte) | name | padding.
189     size_t padding_size = 4 - ((6 + chunk.cname.size()) % 4);
190     const int kPadding = 0;
191     memset(packet + *index, kPadding, padding_size);
192     *index += padding_size;
193   }
194 
195   RTC_CHECK_EQ(*index, index_end);
196   return true;
197 }
198 }  // namespace rtcp
199 }  // namespace webrtc
200