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