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