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