1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #include "pw_bluetooth_sapphire/internal/host/sdp/pdu.h"
16
17 #include <endian.h>
18
19 #include <memory>
20
21 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
22 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
23 #include "pw_bluetooth_sapphire/internal/host/common/packet_view.h"
24 #include "pw_bluetooth_sapphire/internal/host/common/slab_allocator.h"
25
26 namespace bt::sdp {
27
28 namespace {
29
30 // Min size is Sequence uint8 (2 bytes) + uint16_t (3 bytes)
31 // See description of AttributeIDList in ServiceAttribute transaction
32 // Spec v5.0, Vol 3, Part B, Sec 4.6.1
33 constexpr size_t kMinAttributeIDListBytes = 5;
34
35 // The maximum amount of services allowed in a service search.
36 // Spec v5.0, Vol 3, Part B, Sec 4.5.1
37 constexpr size_t kMaxServiceSearchSize = 12;
38
39 // The maximum amount of Attribute list data we will store when parsing a
40 // response to ServiceAttribute or ServiceSearchAttribute responses. 640kb ought
41 // to be enough for anybody.
42 constexpr size_t kMaxSupportedAttributeListBytes = 655360;
43
44 // Validates continuation state in |buf|, which should be the configuration
45 // state bytes of a PDU.
46 // Returns true if the continuation state is valid here, false otherwise.
47 // Sets |out| to point to it if present and valid.
ValidContinuationState(const ByteBuffer & buf,BufferView * out)48 bool ValidContinuationState(const ByteBuffer& buf, BufferView* out) {
49 BT_DEBUG_ASSERT(out);
50 if (buf.size() == 0) {
51 return false;
52 }
53 uint8_t len = buf[0];
54 if (len == 0) {
55 *out = BufferView();
56 return true;
57 }
58 if (len >= kMaxContStateLength || len > (buf.size() - 1)) {
59 return false;
60 }
61 *out = buf.view(1, len);
62 return true;
63 }
64
NewSdpBuffer(size_t buffer_size)65 MutableByteBufferPtr NewSdpBuffer(size_t buffer_size) {
66 // TODO(fxbug.dev/42083692): Remove unique_ptr->DynamicByteBuffer double
67 // indirection once sufficient progress has been made on the attached bug
68 // (specifically re:l2cap::Channel::Send).
69 return std::make_unique<DynamicByteBuffer>(buffer_size);
70 }
71
BuildNewPdu(OpCode pdu_id,TransactionId tid,uint16_t param_length)72 MutableByteBufferPtr BuildNewPdu(OpCode pdu_id,
73 TransactionId tid,
74 uint16_t param_length) {
75 MutableByteBufferPtr ptr = NewSdpBuffer(sizeof(Header) + param_length);
76 MutablePacketView<Header> packet(ptr.get(), param_length);
77 packet.mutable_header()->pdu_id = pdu_id;
78 packet.mutable_header()->tid = htobe16(tid);
79 packet.mutable_header()->param_length = htobe16(param_length);
80 return ptr;
81 }
82
83 // Parses an Attribute ID List sequence where every element is either:
84 // - 16-bit unsigned integer representing a specific Attribute ID
85 // - 32-bit unsigned integer which the high order 16-bits represent a
86 // beginning attribute ID and the low order 16-bits represent a
87 // ending attribute ID of a range.
88 // Returns the number of bytes taken by the list, or zero if an error
89 // occurred (wrong order, wrong format).
ReadAttributeIDList(const ByteBuffer & buf,std::list<AttributeRange> * attribute_ranges)90 size_t ReadAttributeIDList(const ByteBuffer& buf,
91 std::list<AttributeRange>* attribute_ranges) {
92 DataElement attribute_list_elem;
93 size_t elem_size = DataElement::Read(&attribute_list_elem, buf);
94 if ((elem_size == 0) ||
95 (attribute_list_elem.type() != DataElement::Type::kSequence)) {
96 bt_log(TRACE, "sdp", "failed to parse attribute ranges, or not a sequence");
97 attribute_ranges->clear();
98 return 0;
99 }
100 uint16_t last_attr = 0x0000;
101 const DataElement* it = attribute_list_elem.At(0);
102 for (size_t i = 0; it != nullptr; it = attribute_list_elem.At(++i)) {
103 if (it->type() != DataElement::Type::kUnsignedInt) {
104 bt_log(TRACE, "sdp", "attribute range sequence invalid element type");
105 attribute_ranges->clear();
106 return 0;
107 }
108 if (it->size() == DataElement::Size::kTwoBytes) {
109 uint16_t single_attr_id = *(it->Get<uint16_t>());
110 if (single_attr_id < last_attr) {
111 attribute_ranges->clear();
112 return 0;
113 }
114 attribute_ranges->emplace_back(single_attr_id, single_attr_id);
115 last_attr = single_attr_id;
116 } else if (it->size() == DataElement::Size::kFourBytes) {
117 uint32_t attr_range = *(it->Get<uint32_t>());
118 uint16_t start_id = attr_range >> 16;
119 uint16_t end_id = attr_range & 0xFFFF;
120 if ((start_id < last_attr) || (end_id < start_id)) {
121 attribute_ranges->clear();
122 return 0;
123 }
124 attribute_ranges->emplace_back(start_id, end_id);
125 last_attr = end_id;
126 } else {
127 attribute_ranges->clear();
128 return 0;
129 }
130 }
131 return elem_size;
132 }
133
AddToAttributeRanges(std::list<AttributeRange> * ranges,AttributeId start,AttributeId end)134 void AddToAttributeRanges(std::list<AttributeRange>* ranges,
135 AttributeId start,
136 AttributeId end) {
137 auto it = ranges->begin();
138 // Put the range in the list (possibly overlapping other ranges), with the
139 // start in order.
140 for (; it != ranges->end(); ++it) {
141 if (start < it->start) {
142 // This is where it should go.
143 ranges->emplace(it, start, end);
144 }
145 }
146 if (it == ranges->end()) {
147 // It must be on the end.
148 ranges->emplace_back(start, end);
149 }
150 // Merge any overlapping or adjacent ranges with no gaps.
151 for (it = ranges->begin(); it != ranges->end();) {
152 auto next = it;
153 next++;
154 if (next == ranges->end()) {
155 return;
156 }
157 if (it->end >= (next->start - 1)) {
158 next->start = it->start;
159 if (next->end < it->end) {
160 next->end = it->end;
161 }
162 it = ranges->erase(it);
163 } else {
164 ++it;
165 }
166 }
167 }
168
169 } // namespace
170
Request()171 Request::Request() { cont_state_.Fill(0); }
172
SetContinuationState(const ByteBuffer & buf)173 void Request::SetContinuationState(const ByteBuffer& buf) {
174 BT_DEBUG_ASSERT(buf.size() < kMaxContStateLength);
175 cont_state_[0] = static_cast<uint8_t>(buf.size());
176 if (cont_state_[0] == 0) {
177 return;
178 }
179 auto v = cont_state_.mutable_view(sizeof(uint8_t));
180 buf.Copy(&v);
181 }
182
ParseContinuationState(const ByteBuffer & buf)183 bool Request::ParseContinuationState(const ByteBuffer& buf) {
184 BufferView view;
185 if (!ValidContinuationState(buf, &view)) {
186 return false;
187 }
188 SetContinuationState(view);
189 return true;
190 }
191
WriteContinuationState(MutableByteBuffer * buf) const192 size_t Request::WriteContinuationState(MutableByteBuffer* buf) const {
193 BT_DEBUG_ASSERT(buf->size() > cont_info_size());
194 size_t written_size = sizeof(uint8_t) + cont_info_size();
195 buf->Write(cont_state_.view(0, written_size));
196 return written_size;
197 }
198
Parse(const ByteBuffer & buf)199 fit::result<Error<>> ErrorResponse::Parse(const ByteBuffer& buf) {
200 if (complete()) {
201 return ToResult(HostError::kNotReady);
202 }
203 if (buf.size() != sizeof(ErrorCode)) {
204 return ToResult(HostError::kPacketMalformed);
205 }
206 error_code_ = ErrorCode(be16toh(buf.To<uint16_t>()));
207 return fit::ok();
208 }
209
GetPDU(uint16_t,TransactionId tid,uint16_t,const ByteBuffer &) const210 MutableByteBufferPtr ErrorResponse::GetPDU(uint16_t,
211 TransactionId tid,
212 uint16_t,
213 const ByteBuffer&) const {
214 if (!complete()) {
215 return nullptr;
216 }
217 auto ptr = BuildNewPdu(kErrorResponse, tid, sizeof(ErrorCode));
218 size_t written = sizeof(Header);
219
220 ptr->WriteObj(htobe16(static_cast<uint16_t>(error_code_.value())), written);
221
222 return ptr;
223 }
224
ServiceSearchRequest()225 ServiceSearchRequest::ServiceSearchRequest()
226 : Request(), max_service_record_count_(0xFFFF) {}
227
ServiceSearchRequest(const ByteBuffer & params)228 ServiceSearchRequest::ServiceSearchRequest(const ByteBuffer& params)
229 : ServiceSearchRequest() {
230 DataElement search_pattern;
231 size_t read_size = DataElement::Read(&search_pattern, params);
232 if ((read_size == 0) ||
233 (search_pattern.type() != DataElement::Type::kSequence)) {
234 bt_log(TRACE, "sdp", "Failed to read search pattern");
235 return;
236 }
237 size_t min_size = read_size + sizeof(uint16_t) + sizeof(uint8_t);
238 if (params.size() < min_size) {
239 bt_log(
240 TRACE, "sdp", "Params too small: %zu < %zu", params.size(), min_size);
241 return;
242 }
243 const DataElement* it;
244 size_t count;
245 for (count = 0, it = search_pattern.At(count); it != nullptr;
246 it = search_pattern.At(++count)) {
247 if ((count >= kMaxServiceSearchSize) ||
248 (it->type() != DataElement::Type::kUuid)) {
249 bt_log(TRACE, "sdp", "Search pattern invalid: wrong type or too many");
250 service_search_pattern_.clear();
251 return;
252 }
253 service_search_pattern_.emplace(*(it->Get<UUID>()));
254 }
255 if (count == 0) {
256 bt_log(TRACE, "sdp", "Search pattern invalid: no records");
257 return;
258 }
259 max_service_record_count_ = be16toh(params.view(read_size).To<uint16_t>());
260 // Max returned count must be 0x0001-0xFFFF (Spec Vol 3, Part B, 4.5.1)
261 if (max_service_record_count_ == 0) {
262 bt_log(TRACE, "sdp", "Search invalid: max record count must be > 0");
263 return;
264 }
265 read_size += sizeof(uint16_t);
266 if (!ParseContinuationState(params.view(read_size))) {
267 service_search_pattern_.clear();
268 return;
269 }
270 BT_DEBUG_ASSERT(valid());
271 }
272
valid() const273 bool ServiceSearchRequest::valid() const {
274 return max_service_record_count_ > 0 && service_search_pattern_.size() > 0 &&
275 service_search_pattern_.size() <= kMaxServiceSearchSize;
276 }
277
GetPDU(TransactionId tid) const278 ByteBufferPtr ServiceSearchRequest::GetPDU(TransactionId tid) const {
279 if (!valid()) {
280 return nullptr;
281 }
282
283 // MaximumServiceRecordCount + continuation state count + continuation state
284 // size
285 uint16_t size = sizeof(uint16_t) + sizeof(uint8_t) + cont_info_size();
286
287 std::vector<DataElement> pattern(service_search_pattern_.size());
288 size_t i = 0;
289 for (auto& it : service_search_pattern_) {
290 pattern.at(i).Set(it);
291 i++;
292 }
293 DataElement search_pattern(std::move(pattern));
294
295 // The maximum number of service UUIDs in the service is 12 (checked by
296 // valid() above), so |size| shouldn't be anywhere near the max PDU size.
297 size += search_pattern.WriteSize();
298
299 auto buf = BuildNewPdu(kServiceSearchRequest, tid, size);
300 size_t written = sizeof(Header);
301
302 // Write ServiceSearchPattern
303 auto write_view = buf->mutable_view(written);
304 written += search_pattern.Write(&write_view);
305 // Write MaxServiceRecordCount
306 buf->WriteObj(htobe16(max_service_record_count_), written);
307 written += sizeof(uint16_t);
308 // Write Continuation State
309 write_view = buf->mutable_view(written);
310 written += WriteContinuationState(&write_view);
311
312 BT_DEBUG_ASSERT(written == sizeof(Header) + size);
313 return buf;
314 }
315
ServiceSearchResponse()316 ServiceSearchResponse::ServiceSearchResponse()
317 : total_service_record_count_(0) {}
318
complete() const319 bool ServiceSearchResponse::complete() const {
320 return total_service_record_count_ == service_record_handle_list_.size();
321 }
322
ContinuationState() const323 const BufferView ServiceSearchResponse::ContinuationState() const {
324 if (!continuation_state_) {
325 return BufferView();
326 }
327 return continuation_state_->view();
328 }
329
Parse(const ByteBuffer & buf)330 fit::result<Error<>> ServiceSearchResponse::Parse(const ByteBuffer& buf) {
331 if (complete() && total_service_record_count_ != 0) {
332 // This response was previously complete and non-empty.
333 bt_log(TRACE, "sdp", "Can't parse into a complete response");
334 return ToResult(HostError::kNotReady);
335 }
336 if (buf.size() < (2 * sizeof(uint16_t))) {
337 bt_log(TRACE, "sdp", "Packet too small to parse");
338 return ToResult(HostError::kPacketMalformed);
339 }
340
341 uint16_t total_service_record_count = be16toh(buf.To<uint16_t>());
342 size_t read_size = sizeof(uint16_t);
343 if (total_service_record_count_ != 0 &&
344 total_service_record_count_ != total_service_record_count) {
345 bt_log(TRACE, "sdp", "Continuing packet has different record count");
346 return ToResult(HostError::kPacketMalformed);
347 }
348 total_service_record_count_ = total_service_record_count;
349
350 uint16_t record_count = be16toh(buf.view(read_size).To<uint16_t>());
351 read_size += sizeof(uint16_t);
352 size_t expected_record_bytes = sizeof(ServiceHandle) * record_count;
353 if (buf.size() < (read_size + expected_record_bytes)) {
354 bt_log(TRACE,
355 "sdp",
356 "Packet too small for %d records: %zu",
357 record_count,
358 buf.size());
359 return ToResult(HostError::kPacketMalformed);
360 }
361 BufferView cont_state_view;
362 if (!ValidContinuationState(buf.view(read_size + expected_record_bytes),
363 &cont_state_view)) {
364 bt_log(TRACE, "sdp", "Failed to find continuation state");
365 return ToResult(HostError::kPacketMalformed);
366 }
367 size_t expected_size = read_size + expected_record_bytes +
368 cont_state_view.size() + sizeof(uint8_t);
369 if (expected_size != buf.size()) {
370 bt_log(TRACE,
371 "sdp",
372 "Packet should be %zu not %zu",
373 expected_size,
374 buf.size());
375 return ToResult(HostError::kPacketMalformed);
376 }
377
378 for (uint16_t i = 0; i < record_count; i++) {
379 auto view = buf.view(read_size + i * sizeof(ServiceHandle));
380 service_record_handle_list_.emplace_back(be32toh(view.To<uint32_t>()));
381 }
382 if (cont_state_view.size() == 0) {
383 continuation_state_ = nullptr;
384 } else {
385 continuation_state_ = NewBuffer(cont_state_view.size());
386 continuation_state_->Write(cont_state_view);
387 return ToResult(HostError::kInProgress);
388 }
389 return fit::ok();
390 }
391
392 // Continuation state: Index of the start record for the continued response.
GetPDU(uint16_t req_max,TransactionId tid,uint16_t max_size,const ByteBuffer & cont_state) const393 MutableByteBufferPtr ServiceSearchResponse::GetPDU(
394 uint16_t req_max,
395 TransactionId tid,
396 uint16_t max_size,
397 const ByteBuffer& cont_state) const {
398 if (!complete()) {
399 return nullptr;
400 }
401 uint16_t start_idx = 0;
402 if (cont_state.size() == sizeof(uint16_t)) {
403 start_idx = be16toh(cont_state.To<uint16_t>());
404 } else if (cont_state.size() != 0) {
405 // We don't generate continuation state of any other length.
406 return nullptr;
407 }
408
409 uint16_t response_record_count = total_service_record_count_;
410 if (req_max < response_record_count) {
411 bt_log(TRACE,
412 "sdp",
413 "Limit ServiceSearchResponse to %d/%d records",
414 req_max,
415 response_record_count);
416 response_record_count = req_max;
417 }
418
419 if (cont_state.size() > 0 && response_record_count <= start_idx) {
420 // Invalid continuation state, out of range.
421 return nullptr;
422 }
423
424 uint16_t current_record_count = response_record_count - start_idx;
425
426 // Minimum size is zero records with no continuation state.
427 // Header + TotalServiceRecordCount(uint16_t) +
428 // CurrentServiceRecordCount(uint16_t) + ContinuationState count (uint8_t)
429 constexpr uint16_t min_size =
430 sizeof(Header) + (2 * sizeof(uint16_t)) + sizeof(uint8_t);
431
432 if (max_size < min_size) {
433 // Can't generate a PDU, it's too small to hold even no records.
434 return nullptr;
435 }
436
437 // The most records we can send in a packet of max_size (including a
438 // continuation and Header)
439 const uint16_t max_records = (max_size - min_size) / sizeof(ServiceHandle);
440
441 uint8_t info_length = 0;
442 if (max_records < current_record_count) {
443 bt_log(TRACE,
444 "sdp",
445 "Max Size limits to %hu/%d records",
446 max_records,
447 current_record_count);
448 current_record_count = max_records;
449 info_length = sizeof(uint16_t);
450 }
451
452 // Note: we exclude Header from size here
453 // TotalServiceRecordCount(uint16_t) + CurrentServiceRecordCount(uint16_t) +
454 // ServiceRecordHandleList (CurrentServiceRecordCount * ServiceHandle) +
455 // ContinuationState count (uint8_t)
456 const uint16_t size = (2 * sizeof(uint16_t)) +
457 (current_record_count * sizeof(ServiceHandle)) +
458 sizeof(uint8_t) + info_length;
459
460 auto buf = BuildNewPdu(kServiceSearchResponse, tid, size);
461 if (!buf) {
462 return buf;
463 }
464 BT_ASSERT(buf->size() <= max_size);
465
466 size_t written = sizeof(Header);
467 buf->WriteObj(htobe16(response_record_count), written);
468 written += sizeof(uint16_t);
469 buf->WriteObj(htobe16(current_record_count), written);
470 written += sizeof(uint16_t);
471
472 for (size_t i = 0; i < current_record_count; i++) {
473 buf->WriteObj(htobe32(service_record_handle_list_.at(start_idx + i)),
474 written);
475 written += sizeof(ServiceHandle);
476 }
477
478 // Continuation state
479 buf->WriteObj(info_length, written);
480 written += sizeof(uint8_t);
481 if (info_length > 0) {
482 start_idx += current_record_count;
483 buf->WriteObj(htobe16(start_idx), written);
484 written += sizeof(uint16_t);
485 }
486 BT_DEBUG_ASSERT(written == sizeof(Header) + size);
487 return buf;
488 }
489
ServiceAttributeRequest()490 ServiceAttributeRequest::ServiceAttributeRequest()
491 : service_record_handle_(0), max_attribute_byte_count_(0xFFFF) {}
492
ServiceAttributeRequest(const ByteBuffer & params)493 ServiceAttributeRequest::ServiceAttributeRequest(const ByteBuffer& params) {
494 if (params.size() < sizeof(uint32_t) + sizeof(uint16_t)) {
495 bt_log(TRACE, "sdp", "packet too small for ServiceAttributeRequest");
496 max_attribute_byte_count_ = 0;
497 return;
498 }
499
500 service_record_handle_ = be32toh(params.To<uint32_t>());
501 size_t read_size = sizeof(uint32_t);
502 max_attribute_byte_count_ = be16toh(params.view(read_size).To<uint16_t>());
503 if (max_attribute_byte_count_ < kMinMaximumAttributeByteCount) {
504 bt_log(TRACE,
505 "sdp",
506 "max attribute byte count too small (%hu < %zu)",
507 max_attribute_byte_count_,
508 kMinMaximumAttributeByteCount);
509 return;
510 }
511 read_size += sizeof(uint16_t);
512
513 size_t elem_size =
514 ReadAttributeIDList(params.view(read_size), &attribute_ranges_);
515 if (attribute_ranges_.size() == 0) {
516 max_attribute_byte_count_ = 0;
517 return;
518 }
519 read_size += elem_size;
520
521 if (!ParseContinuationState(params.view(read_size))) {
522 attribute_ranges_.clear();
523 return;
524 }
525 BT_DEBUG_ASSERT(valid());
526 }
527
valid() const528 bool ServiceAttributeRequest::valid() const {
529 return (max_attribute_byte_count_ >= kMinMaximumAttributeByteCount) &&
530 (attribute_ranges_.size() > 0) &&
531 (attribute_ranges_.size() <= kMaxAttributeRangesInRequest);
532 }
533
GetPDU(TransactionId tid) const534 ByteBufferPtr ServiceAttributeRequest::GetPDU(TransactionId tid) const {
535 if (!valid()) {
536 return nullptr;
537 }
538
539 size_t size = sizeof(ServiceHandle) + sizeof(uint16_t) + sizeof(uint8_t) +
540 cont_info_size();
541
542 std::vector<DataElement> attribute_list(attribute_ranges_.size());
543 size_t idx = 0;
544 for (const auto& it : attribute_ranges_) {
545 if (it.start == it.end) {
546 attribute_list.at(idx).Set<uint16_t>(it.start);
547 } else {
548 uint32_t attr_range = (static_cast<uint32_t>(it.start) << 16);
549 attr_range |= it.end;
550 attribute_list.at(idx).Set<uint32_t>(attr_range);
551 }
552 idx++;
553 }
554
555 DataElement attribute_list_elem(std::move(attribute_list));
556 size += attribute_list_elem.WriteSize();
557
558 // valid() ensures that |size| can't overflow due to too many attribute
559 // ranges.
560 BT_DEBUG_ASSERT(size <= std::numeric_limits<uint16_t>::max());
561 auto buf =
562 BuildNewPdu(kServiceAttributeRequest, tid, static_cast<uint16_t>(size));
563
564 size_t written = sizeof(Header);
565
566 buf->WriteObj(htobe32(service_record_handle_), written);
567 written += sizeof(uint32_t);
568
569 buf->WriteObj(htobe16(max_attribute_byte_count_), written);
570 written += sizeof(uint16_t);
571
572 auto mut_view = buf->mutable_view(written);
573 written += attribute_list_elem.Write(&mut_view);
574
575 mut_view = buf->mutable_view(written);
576 written += WriteContinuationState(&mut_view);
577 BT_DEBUG_ASSERT(written == sizeof(Header) + size);
578 return buf;
579 }
580
AddAttribute(AttributeId id)581 void ServiceAttributeRequest::AddAttribute(AttributeId id) {
582 AddToAttributeRanges(&attribute_ranges_, id, id);
583 }
584
AddAttributeRange(AttributeId start,AttributeId end)585 void ServiceAttributeRequest::AddAttributeRange(AttributeId start,
586 AttributeId end) {
587 AddToAttributeRanges(&attribute_ranges_, start, end);
588 }
589
ServiceAttributeResponse()590 ServiceAttributeResponse::ServiceAttributeResponse() {}
591
ContinuationState() const592 const BufferView ServiceAttributeResponse::ContinuationState() const {
593 if (!continuation_state_) {
594 return BufferView();
595 }
596 return continuation_state_->view();
597 }
598
complete() const599 bool ServiceAttributeResponse::complete() const { return !continuation_state_; }
600
Parse(const ByteBuffer & buf)601 fit::result<Error<>> ServiceAttributeResponse::Parse(const ByteBuffer& buf) {
602 if (complete() && attributes_.size() != 0) {
603 // This response was previously complete and non-empty
604 bt_log(TRACE, "sdp", "Can't parse into a complete response");
605 // partial_response_ is already empty
606 return ToResult(HostError::kNotReady);
607 }
608
609 if (buf.size() < sizeof(uint16_t)) {
610 bt_log(TRACE, "sdp", "Packet too small to parse");
611 return ToResult(HostError::kPacketMalformed);
612 }
613
614 uint32_t attribute_list_byte_count = be16toh(buf.To<uint16_t>());
615 size_t read_size = sizeof(uint16_t);
616 if (buf.size() < read_size + attribute_list_byte_count + sizeof(uint8_t)) {
617 bt_log(TRACE, "sdp", "Not enough bytes in rest of packet");
618 return ToResult(HostError::kPacketMalformed);
619 }
620 // Check to see if there's continuation.
621 BufferView cont_state_view;
622 if (!ValidContinuationState(buf.view(read_size + attribute_list_byte_count),
623 &cont_state_view)) {
624 bt_log(TRACE, "sdp", "Continuation state is not valid");
625 return ToResult(HostError::kPacketMalformed);
626 }
627
628 if (cont_state_view.size() == 0) {
629 continuation_state_ = nullptr;
630 } else {
631 continuation_state_ = NewBuffer(cont_state_view.size());
632 continuation_state_->Write(cont_state_view);
633 }
634
635 size_t expected_size = read_size + attribute_list_byte_count +
636 cont_state_view.size() + sizeof(uint8_t);
637 if (buf.size() != expected_size) {
638 bt_log(TRACE,
639 "sdp",
640 "Packet should be %zu not %zu",
641 expected_size,
642 buf.size());
643 return ToResult(HostError::kPacketMalformed);
644 }
645
646 auto attribute_list_bytes = buf.view(read_size, attribute_list_byte_count);
647 if (partial_response_ || ContinuationState().size()) {
648 // Append to the incomplete buffer.
649 size_t new_partial_size = attribute_list_byte_count;
650 if (partial_response_) {
651 new_partial_size += partial_response_->size();
652 }
653 // We currently don't support more than approx 10 packets of the max size.
654 if (new_partial_size > kMaxSupportedAttributeListBytes) {
655 bt_log(INFO,
656 "sdp",
657 "ServiceAttributeResponse exceeds supported size (%zu), dropping",
658 new_partial_size);
659 partial_response_ = nullptr;
660 return ToResult(HostError::kNotSupported);
661 }
662
663 auto new_partial = NewBuffer(new_partial_size);
664 if (partial_response_) {
665 new_partial->Write(partial_response_->view());
666 new_partial->Write(attribute_list_bytes, partial_response_->size());
667 } else {
668 new_partial->Write(attribute_list_bytes);
669 }
670 partial_response_ = std::move(new_partial);
671 if (continuation_state_) {
672 // This is incomplete, we can't parse it yet.
673 bt_log(TRACE, "sdp", "Continuation state, returning in progress");
674 return ToResult(HostError::kInProgress);
675 }
676 attribute_list_bytes = partial_response_->view();
677 }
678
679 DataElement attribute_list;
680 size_t elem_size = DataElement::Read(&attribute_list, attribute_list_bytes);
681 if ((elem_size == 0) ||
682 (attribute_list.type() != DataElement::Type::kSequence)) {
683 bt_log(
684 TRACE, "sdp", "Couldn't parse attribute list or it wasn't a sequence");
685 return ToResult(HostError::kPacketMalformed);
686 }
687
688 // Data Element sequence containing alternating attribute id and attribute
689 // value pairs. Only the requested attributes that are present are included.
690 // They are sorted in ascending attribute ID order.
691 AttributeId last_id = 0;
692 size_t idx = 0;
693 for (auto* it = attribute_list.At(0); it != nullptr;
694 it = attribute_list.At(idx)) {
695 auto* val = attribute_list.At(idx + 1);
696 std::optional<AttributeId> id = it->Get<uint16_t>();
697 if (!id || (val == nullptr)) {
698 attributes_.clear();
699 return ToResult(HostError::kPacketMalformed);
700 }
701 if (*id < last_id) {
702 attributes_.clear();
703 return ToResult(HostError::kPacketMalformed);
704 }
705 attributes_.emplace(*id, val->Clone());
706 last_id = *id;
707 idx += 2;
708 }
709 return fit::ok();
710 }
711
712 // Continuation state: index of # of bytes into the attribute list element
GetPDU(uint16_t req_max,TransactionId tid,uint16_t max_size,const ByteBuffer & cont_state) const713 MutableByteBufferPtr ServiceAttributeResponse::GetPDU(
714 uint16_t req_max,
715 TransactionId tid,
716 uint16_t max_size,
717 const ByteBuffer& cont_state) const {
718 if (!complete()) {
719 return nullptr;
720 }
721 // If there's continuation state, it's the # of bytes previously written
722 // of the attribute list.
723 uint32_t bytes_skipped = 0;
724 if (cont_state.size() == sizeof(uint32_t)) {
725 bytes_skipped = be32toh(cont_state.To<uint32_t>());
726 } else if (cont_state.size() != 0) {
727 // We don't generate continuation states of any other length.
728 return nullptr;
729 }
730
731 // Returned in pairs of (attribute id, attribute value)
732 std::vector<DataElement> list;
733 list.reserve(2 * attributes_.size());
734 for (const auto& it : attributes_) {
735 list.emplace_back(static_cast<uint16_t>(it.first));
736 list.emplace_back(it.second.Clone());
737 }
738 DataElement list_elem(std::move(list));
739
740 size_t write_size = list_elem.WriteSize();
741
742 if (bytes_skipped > write_size) {
743 bt_log(TRACE,
744 "sdp",
745 "continuation out of range: %d > %zu",
746 bytes_skipped,
747 write_size);
748 return nullptr;
749 }
750
751 // Minimum size is header, byte_count, 2 attribute bytes, and a zero length
752 // continuation state
753 constexpr uint16_t min_size =
754 sizeof(Header) + sizeof(uint16_t) + 2 + sizeof(uint8_t);
755
756 if (min_size > max_size) {
757 // Can't make a PDU because we don't have enough space.
758 return nullptr;
759 }
760
761 uint8_t info_length = 0;
762 size_t attribute_list_byte_count = write_size - bytes_skipped;
763
764 // Two attribute bytes counted in the min_size are excluded
765 const uint16_t max_attribute_byte_count = max_size - min_size + 2;
766 if (attribute_list_byte_count > max_attribute_byte_count) {
767 info_length = sizeof(uint32_t);
768 bt_log(TRACE,
769 "sdp",
770 "Max size limits attribute size to %hu of %zu",
771 max_attribute_byte_count - info_length,
772 attribute_list_byte_count);
773 attribute_list_byte_count = max_attribute_byte_count - info_length;
774 }
775
776 if (attribute_list_byte_count > req_max) {
777 bt_log(TRACE,
778 "sdp",
779 "Requested size limits attribute size to %d of %zu",
780 req_max,
781 attribute_list_byte_count);
782 attribute_list_byte_count = req_max;
783 info_length = sizeof(uint32_t);
784 }
785
786 // Casting to uint16_t is safe as attribute_list_byte_count was limited to
787 // max_attribute_byte_count above.
788 uint16_t size =
789 static_cast<uint16_t>(sizeof(uint16_t) + attribute_list_byte_count +
790 sizeof(uint8_t) + info_length);
791 auto buf = BuildNewPdu(kServiceAttributeResponse, tid, size);
792
793 size_t written = sizeof(Header);
794
795 buf->WriteObj(htobe16(attribute_list_byte_count), written);
796 written += sizeof(uint16_t);
797
798 auto attribute_list_bytes = NewBuffer(write_size);
799 list_elem.Write(attribute_list_bytes.get());
800 buf->Write(
801 attribute_list_bytes->view(bytes_skipped, attribute_list_byte_count),
802 written);
803 written += attribute_list_byte_count;
804
805 // Continuation state
806 buf->WriteObj(info_length, written);
807 written += sizeof(uint8_t);
808 if (info_length > 0) {
809 bytes_skipped += attribute_list_byte_count;
810 buf->WriteObj(htobe32(bytes_skipped), written);
811 written += sizeof(uint32_t);
812 }
813 BT_DEBUG_ASSERT(written == sizeof(Header) + size);
814 return buf;
815 }
816
ServiceSearchAttributeRequest()817 ServiceSearchAttributeRequest::ServiceSearchAttributeRequest()
818 : Request(), max_attribute_byte_count_(0xFFFF) {}
819
ServiceSearchAttributeRequest(const ByteBuffer & params)820 ServiceSearchAttributeRequest::ServiceSearchAttributeRequest(
821 const ByteBuffer& params) {
822 DataElement search_pattern;
823 size_t read_size = DataElement::Read(&search_pattern, params);
824 if ((read_size == 0) ||
825 (search_pattern.type() != DataElement::Type::kSequence)) {
826 bt_log(TRACE, "sdp", "failed to read search pattern");
827 max_attribute_byte_count_ = 0;
828 return;
829 }
830 // Minimum size is ServiceSearchPattern (varies, above) +
831 // MaximumAttributeByteCount + AttributeIDList + Cont State (uint8)
832 if (params.size() < read_size + sizeof(max_attribute_byte_count_) +
833 kMinAttributeIDListBytes + sizeof(uint8_t)) {
834 bt_log(TRACE, "sdp", "packet too small for ServiceSearchAttributeRequest");
835 max_attribute_byte_count_ = 0;
836 return;
837 }
838
839 const DataElement* it;
840 size_t count;
841 for (count = 0, it = search_pattern.At(count); it != nullptr;
842 it = search_pattern.At(++count)) {
843 if ((count >= kMaxServiceSearchSize) ||
844 (it->type() != DataElement::Type::kUuid)) {
845 bt_log(TRACE, "sdp", "search pattern is invalid");
846 service_search_pattern_.clear();
847 return;
848 }
849 service_search_pattern_.emplace(*(it->Get<UUID>()));
850 }
851 if (count == 0) {
852 bt_log(TRACE, "sdp", "no elements in search pattern");
853 max_attribute_byte_count_ = 0;
854 return;
855 }
856
857 max_attribute_byte_count_ = be16toh(params.view(read_size).To<uint16_t>());
858 if (max_attribute_byte_count_ < kMinMaximumAttributeByteCount) {
859 bt_log(TRACE,
860 "sdp",
861 "max attribute byte count to small (%d)",
862 max_attribute_byte_count_);
863 max_attribute_byte_count_ = 0;
864 return;
865 }
866 read_size += sizeof(uint16_t);
867
868 size_t elem_size =
869 ReadAttributeIDList(params.view(read_size), &attribute_ranges_);
870 if (attribute_ranges_.size() == 0) {
871 max_attribute_byte_count_ = 0;
872 return;
873 }
874 read_size += elem_size;
875
876 if (!ParseContinuationState(params.view(read_size))) {
877 attribute_ranges_.clear();
878 return;
879 }
880
881 bt_log(TRACE,
882 "sdp",
883 "parsed: %zu search uuids, %hu max bytes, %zu attribute ranges",
884 service_search_pattern_.size(),
885 max_attribute_byte_count_,
886 attribute_ranges_.size());
887
888 BT_DEBUG_ASSERT(valid());
889 }
890
valid() const891 bool ServiceSearchAttributeRequest::valid() const {
892 return (max_attribute_byte_count_ > kMinMaximumAttributeByteCount) &&
893 (service_search_pattern_.size() > 0) &&
894 (service_search_pattern_.size() <= kMaxServiceSearchSize) &&
895 (attribute_ranges_.size() > 0) &&
896 (attribute_ranges_.size() <= kMaxAttributeRangesInRequest);
897 }
898
GetPDU(TransactionId tid) const899 ByteBufferPtr ServiceSearchAttributeRequest::GetPDU(TransactionId tid) const {
900 if (!valid()) {
901 return nullptr;
902 }
903
904 // Size of fixed length components: MaxAttributesByteCount, continuation info
905 uint16_t size = sizeof(max_attribute_byte_count_) + cont_info_size() + 1;
906
907 std::vector<DataElement> attribute_list(attribute_ranges_.size());
908 size_t idx = 0;
909 for (const auto& it : attribute_ranges_) {
910 if (it.start == it.end) {
911 attribute_list.at(idx).Set<uint16_t>(it.start);
912 } else {
913 uint32_t attr_range = (static_cast<uint32_t>(it.start) << 16);
914 attr_range |= it.end;
915 attribute_list.at(idx).Set<uint32_t>(attr_range);
916 }
917 idx++;
918 }
919
920 DataElement attribute_list_elem(std::move(attribute_list));
921 // The valid() check above prevents the attribute list from being long enough
922 // to overflow |size|.
923 size += attribute_list_elem.WriteSize();
924
925 std::vector<DataElement> pattern(service_search_pattern_.size());
926 size_t i = 0;
927 for (const auto& it : service_search_pattern_) {
928 pattern.at(i).Set<UUID>(it);
929 i++;
930 }
931 DataElement search_pattern(std::move(pattern));
932 size += search_pattern.WriteSize();
933
934 auto buf = BuildNewPdu(kServiceSearchAttributeRequest, tid, size);
935
936 size_t written = sizeof(Header);
937
938 auto mut_view = buf->mutable_view(written);
939 written += search_pattern.Write(&mut_view);
940
941 buf->WriteObj(htobe16(max_attribute_byte_count_), written);
942 written += sizeof(uint16_t);
943
944 mut_view = buf->mutable_view(written);
945 written += attribute_list_elem.Write(&mut_view);
946
947 mut_view = buf->mutable_view(written);
948 written += WriteContinuationState(&mut_view);
949 BT_DEBUG_ASSERT(written == sizeof(Header) + size);
950 return buf;
951 }
952
AddAttribute(AttributeId id)953 void ServiceSearchAttributeRequest::AddAttribute(AttributeId id) {
954 AddToAttributeRanges(&attribute_ranges_, id, id);
955 }
956
AddAttributeRange(AttributeId start,AttributeId end)957 void ServiceSearchAttributeRequest::AddAttributeRange(AttributeId start,
958 AttributeId end) {
959 AddToAttributeRanges(&attribute_ranges_, start, end);
960 }
961
ServiceSearchAttributeResponse()962 ServiceSearchAttributeResponse::ServiceSearchAttributeResponse() {}
963
ContinuationState() const964 const BufferView ServiceSearchAttributeResponse::ContinuationState() const {
965 if (!continuation_state_) {
966 return BufferView();
967 }
968 return continuation_state_->view();
969 }
970
complete() const971 bool ServiceSearchAttributeResponse::complete() const {
972 return !continuation_state_;
973 }
974
Parse(const ByteBuffer & buf)975 fit::result<Error<>> ServiceSearchAttributeResponse::Parse(
976 const ByteBuffer& buf) {
977 if (complete() && attribute_lists_.size() != 0) {
978 // This response was previously complete and non-empty
979 bt_log(TRACE, "sdp", "can't parse into a complete response");
980 BT_DEBUG_ASSERT(!partial_response_);
981 return ToResult(HostError::kNotReady);
982 }
983
984 // Minimum size is an AttributeListsByteCount, an empty AttributeLists
985 // (two bytes) and an empty continuation state (1 byte) of AttributeLists
986 if (buf.size() < sizeof(uint16_t) + 3) {
987 bt_log(TRACE, "sdp", "packet too small to parse");
988 return ToResult(HostError::kPacketMalformed);
989 }
990
991 uint16_t attribute_lists_byte_count = be16toh(buf.To<uint16_t>());
992 size_t read_size = sizeof(uint16_t);
993 if (buf.view(read_size).size() <
994 attribute_lists_byte_count + sizeof(uint8_t)) {
995 bt_log(TRACE, "sdp", "not enough bytes in rest of packet as indicated");
996 return ToResult(HostError::kPacketMalformed);
997 }
998 // Check to see if there's continuation.
999 BufferView cont_state_view;
1000 if (!ValidContinuationState(buf.view(read_size + attribute_lists_byte_count),
1001 &cont_state_view)) {
1002 bt_log(TRACE, "sdp", "continuation state is not valid");
1003 return ToResult(HostError::kPacketMalformed);
1004 }
1005
1006 if (cont_state_view.size() == 0) {
1007 continuation_state_ = nullptr;
1008 } else {
1009 continuation_state_ = NewBuffer(cont_state_view.size());
1010 continuation_state_->Write(cont_state_view);
1011 }
1012
1013 auto attribute_lists_bytes = buf.view(read_size, attribute_lists_byte_count);
1014 if (partial_response_ || ContinuationState().size()) {
1015 // Append to the incomplete buffer.
1016 size_t new_partial_size = attribute_lists_byte_count;
1017 if (partial_response_) {
1018 new_partial_size += partial_response_->size();
1019 }
1020 // We currently don't support more than approx 10 packets of the max size.
1021 if (new_partial_size > kMaxSupportedAttributeListBytes) {
1022 bt_log(INFO,
1023 "sdp",
1024 "ServiceSearchAttributeResponse exceeds supported size, dropping");
1025 partial_response_ = nullptr;
1026 return ToResult(HostError::kNotSupported);
1027 }
1028
1029 auto new_partial = NewBuffer(new_partial_size);
1030 if (partial_response_) {
1031 new_partial->Write(partial_response_->view());
1032 new_partial->Write(attribute_lists_bytes, partial_response_->size());
1033 } else {
1034 new_partial->Write(attribute_lists_bytes);
1035 }
1036 partial_response_ = std::move(new_partial);
1037 if (continuation_state_) {
1038 // This is incomplete, we can't parse it yet.
1039 bt_log(TRACE, "sdp", "continuation state found, returning in progress");
1040 return ToResult(HostError::kInProgress);
1041 }
1042 attribute_lists_bytes = partial_response_->view();
1043 }
1044
1045 DataElement attribute_lists;
1046 size_t elem_size = DataElement::Read(&attribute_lists, attribute_lists_bytes);
1047 if ((elem_size == 0) ||
1048 (attribute_lists.type() != DataElement::Type::kSequence)) {
1049 bt_log(TRACE, "sdp", "couldn't parse attribute lists or wasn't a sequence");
1050 return ToResult(HostError::kPacketMalformed);
1051 }
1052 bt_log(TRACE,
1053 "sdp",
1054 "parsed AttributeLists: %s",
1055 attribute_lists.ToString().c_str());
1056
1057 // Data Element sequence containing alternating attribute id and attribute
1058 // value pairs. Only the requested attributes that are present are included.
1059 // They are sorted in ascending attribute ID order.
1060 size_t list_idx = 0;
1061 for (auto* src_list_it = attribute_lists.At(0); src_list_it != nullptr;
1062 src_list_it = attribute_lists.At(++list_idx)) {
1063 if ((src_list_it->type() != DataElement::Type::kSequence)) {
1064 bt_log(TRACE, "sdp", "list %zu wasn't a sequence", list_idx);
1065 return ToResult(HostError::kPacketMalformed);
1066 }
1067 auto [dest_list_it, _] = attribute_lists_.emplace(
1068 list_idx, std::map<AttributeId, DataElement>());
1069 AttributeId last_id = 0;
1070 size_t idx = 0;
1071 for (const DataElement* it = src_list_it->At(0); it != nullptr;
1072 it = src_list_it->At(idx)) {
1073 const DataElement* val = src_list_it->At(idx + 1);
1074 std::optional<AttributeId> id = it->Get<uint16_t>();
1075 if (!id || (val == nullptr)) {
1076 attribute_lists_.clear();
1077 bt_log(TRACE, "sdp", "attribute isn't a number or value doesn't exist");
1078 return ToResult(HostError::kPacketMalformed);
1079 }
1080 bt_log(TRACE,
1081 "sdp",
1082 "adding %zu:%s = %s",
1083 list_idx,
1084 bt_str(*it),
1085 bt_str(*val));
1086 if (*id < last_id) {
1087 bt_log(INFO,
1088 "sdp",
1089 "attribute ids are in wrong order, ignoring for compat");
1090 }
1091 auto [_, inserted] = (*dest_list_it).second.emplace(*id, val->Clone());
1092 if (!inserted) {
1093 attribute_lists_.clear();
1094 bt_log(WARN, "sdp", "attribute was duplicated in attribute response");
1095 return ToResult(HostError::kPacketMalformed);
1096 }
1097 last_id = *id;
1098 idx += 2;
1099 }
1100 }
1101 partial_response_ = nullptr;
1102 return fit::ok();
1103 }
1104
SetAttribute(uint32_t idx,AttributeId id,DataElement value)1105 void ServiceSearchAttributeResponse::SetAttribute(uint32_t idx,
1106 AttributeId id,
1107 DataElement value) {
1108 if (attribute_lists_.find(idx) == attribute_lists_.end()) {
1109 attribute_lists_.emplace(idx, std::map<AttributeId, DataElement>());
1110 }
1111 attribute_lists_[idx].emplace(id, std::move(value));
1112 }
1113
1114 // Continuation state: index of # of bytes into the attribute list element
GetPDU(uint16_t req_max,TransactionId tid,uint16_t max_size,const ByteBuffer & cont_state) const1115 MutableByteBufferPtr ServiceSearchAttributeResponse::GetPDU(
1116 uint16_t req_max,
1117 TransactionId tid,
1118 uint16_t max_size,
1119 const ByteBuffer& cont_state) const {
1120 if (!complete()) {
1121 return nullptr;
1122 }
1123 // If there's continuation state, it's the # of bytes previously written
1124 // of the attribute list.
1125 uint32_t bytes_skipped = 0;
1126 if (cont_state.size() == sizeof(uint32_t)) {
1127 bytes_skipped = be32toh(cont_state.To<uint32_t>());
1128 } else if (cont_state.size() != 0) {
1129 // We don't generate continuation states of any other length.
1130 return nullptr;
1131 }
1132
1133 std::vector<DataElement> lists;
1134 lists.reserve(attribute_lists_.size());
1135 for (const auto& it : attribute_lists_) {
1136 // Returned in pairs of (attribute id, attribute value)
1137 std::vector<DataElement> list;
1138 list.reserve(2 * it.second.size());
1139 for (const auto& elem_it : it.second) {
1140 list.emplace_back(static_cast<uint16_t>(elem_it.first));
1141 list.emplace_back(elem_it.second.Clone());
1142 }
1143
1144 lists.emplace_back(std::move(list));
1145 }
1146
1147 DataElement list_elem(std::move(lists));
1148
1149 size_t write_size = list_elem.WriteSize();
1150
1151 if (bytes_skipped > write_size) {
1152 bt_log(TRACE,
1153 "sdp",
1154 "continuation out of range: %d > %zu",
1155 bytes_skipped,
1156 write_size);
1157 return nullptr;
1158 }
1159
1160 // Minimum size is header, byte_count, 2 attribute bytes, and a zero length
1161 // continuation state
1162 constexpr uint16_t min_size =
1163 sizeof(Header) + sizeof(uint16_t) + 2 + sizeof(uint8_t);
1164
1165 if (min_size > max_size) {
1166 // Can't make a PDU because we don't have enough space.
1167 return nullptr;
1168 }
1169
1170 uint8_t info_length = 0;
1171 size_t attribute_lists_byte_count = write_size - bytes_skipped;
1172
1173 // Two attribute bytes counted in the min_size excluded
1174 uint16_t max_attribute_byte_count = max_size - min_size + 2;
1175 if (attribute_lists_byte_count > max_attribute_byte_count) {
1176 info_length = sizeof(uint32_t);
1177 bt_log(TRACE,
1178 "sdp",
1179 "Max size limits attribute size to %hu of %zu",
1180 max_attribute_byte_count - info_length,
1181 attribute_lists_byte_count);
1182 attribute_lists_byte_count = max_attribute_byte_count - info_length;
1183 }
1184
1185 if (attribute_lists_byte_count > req_max) {
1186 bt_log(TRACE,
1187 "sdp",
1188 "Requested size limits attribute size to %d of %zu",
1189 req_max,
1190 attribute_lists_byte_count);
1191 attribute_lists_byte_count = req_max;
1192 info_length = sizeof(uint32_t);
1193 }
1194
1195 // Safe to case to uint16_t as attribute_lists_byte_count was limited to
1196 // max_attribute_byte_count above.
1197 uint16_t size =
1198 static_cast<uint16_t>(sizeof(uint16_t) + attribute_lists_byte_count +
1199 sizeof(uint8_t) + info_length);
1200 auto buf = BuildNewPdu(kServiceSearchAttributeResponse, tid, size);
1201
1202 size_t written = sizeof(Header);
1203
1204 buf->WriteObj(htobe16(attribute_lists_byte_count), written);
1205 written += sizeof(uint16_t);
1206
1207 auto attribute_list_bytes = NewBuffer(write_size);
1208 list_elem.Write(attribute_list_bytes.get());
1209 buf->Write(
1210 attribute_list_bytes->view(bytes_skipped, attribute_lists_byte_count),
1211 written);
1212 written += attribute_lists_byte_count;
1213
1214 // Continuation state
1215 buf->WriteObj(info_length, written);
1216 written += sizeof(uint8_t);
1217 if (info_length > 0) {
1218 // Safe to cast because the value is constrained by the number of local
1219 // attributes, which will be much less than UINT32_MAX. bytes_skipped (an
1220 // untrusted value) is checked for out-of-range above.
1221 BT_DEBUG_ASSERT(bytes_skipped + attribute_lists_byte_count <
1222 std::numeric_limits<uint32_t>::max());
1223 bytes_skipped =
1224 static_cast<uint32_t>(bytes_skipped + attribute_lists_byte_count);
1225 buf->WriteObj(htobe32(bytes_skipped), written);
1226 written += sizeof(uint32_t);
1227 }
1228 BT_DEBUG_ASSERT(written == sizeof(Header) + size);
1229 return buf;
1230 }
1231
1232 } // namespace bt::sdp
1233