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