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