• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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