• 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 #pragma once
16 #include <lib/fit/result.h>
17 
18 #include "pw_bluetooth_sapphire/internal/host/common/error.h"
19 #include "pw_bluetooth_sapphire/internal/host/sdp/sdp.h"
20 
21 namespace bt::sdp {
22 
23 constexpr uint64_t kInvalidContState = 0xFFFFFFFF;
24 
25 // Maximum length of continuation information is 16 bytes, and the InfoLength
26 // is one byte. See v5.0, Vol 3, Part B, Sec 4.3
27 constexpr size_t kMaxContStateLength = 17;
28 
29 // Minimum length allowed by the Maximum Attribute Byte Count in
30 // ServiceAttribute and ServiceSearchAttribute requests
31 constexpr size_t kMinMaximumAttributeByteCount = 0x0007;
32 
33 // Selected to be larger than FIDL limit of 512. Prevent poor performance in
34 // worst case scenarios. Clients should use larger ranges if they need anywhere
35 // near this number of attributes.
36 constexpr size_t kMaxAttributeRangesInRequest = 520;
37 
38 class Request {
39  public:
40   Request();
41   virtual ~Request() = default;
42 
43   // Returns true if the request is valid.
44   virtual bool valid() const = 0;
45 
46   // Gets a buffer containing the PDU representation of this request.
47   // Returns nullptr if the request is not valid.
48   virtual ByteBufferPtr GetPDU(TransactionId tid) const = 0;
49 
50   // Returns a view with the current continuation state.
51   // In a response packet with more than one packet, this contains the most
52   // recent continuation state (so it can be read to request a continuation).
ContinuationState()53   const BufferView ContinuationState() const {
54     return cont_state_.view(1, cont_info_size());
55   }
56 
57   // Sets the continuation state for this request.
58   void SetContinuationState(const ByteBuffer& buf);
59 
60  protected:
61   // Parses the continuation state portion of a packet, which is in |buf|.
62   // Returns true if the parsing succeeded.
63   bool ParseContinuationState(const ByteBuffer& buf);
64 
65   // Writes the continuation state to |buf|, which must have at least
66   // cont_info_size() + 1 bytes available.
67   size_t WriteContinuationState(MutableByteBuffer* buf) const;
68 
cont_info_size()69   uint8_t cont_info_size() const { return cont_state_.data()[0]; }
70 
71  private:
72   // Continuation information, including the length.
73   StaticByteBuffer<kMaxContStateLength> cont_state_;
74 };
75 
76 // SDP Response objects are used in two places:
77 //  - to construct a response for returning from a request on the server
78 //  - to receive responses from a server as a client, possibly building from
79 //    multiple response PDUs
80 class Response {
81  public:
82   virtual ~Response() = default;
83 
84   // Returns true if these parameters represent a complete response.
85   virtual bool complete() const = 0;
86 
87   // Returns the continuation state from a partial response, used to
88   // make an additional request.  Returns an empty view if this packet
89   // is complete.
90   virtual const BufferView ContinuationState() const = 0;
91 
92   // Parses parameters from a PDU response, storing a partial result if
93   // necessary.
94   // Returns a success status if the parameters could ba parsed, or a status
95   // containing:
96   //  - kNotReady if this response is already complete.
97   //  - kPacketMalformed: if the parameters couldn't be parsed.
98   //  - kOutOfMemory: if memory isn't available to store a partial response.
99   virtual fit::result<Error<>> Parse(const ByteBuffer& buf) = 0;
100 
101   // Returns a buffer containing the PDU representation of this response,
102   // including the header, which will have the transaction id |tid|.
103   // |req_max| will control the maximum size of the parameters based on the
104   // transaction type:
105   //  - for ServiceSearchResponse, this should be the maximum records requested
106   //    to be included from the ServiceSearchRequest
107   //  - for ServiceAttributeResponse or ServiceSearchAttributeResponse, this
108   //    is the MaximumAttributeByteCount from the request
109   // |max_size| is the maximum size of a PDU generated by this method.
110   // The buffer parameters will contain continuation state if it does not
111   // contain the end of the response. If that continuation state is passed to
112   // this function with the same |req_max| argument it will produce the next
113   // section of response.
114   virtual MutableByteBufferPtr GetPDU(uint16_t req_max,
115                                       TransactionId tid,
116                                       uint16_t max_size,
117                                       const ByteBuffer& cont_state) const = 0;
118 };
119 
120 // Error Response PDU, generated when the SDP server can't respond to a PDU
121 // because it is malformed or for another reason.
122 // See v5.0, Vol 3, Part B, 4.4.1
123 class ErrorResponse : public Response {
124  public:
125   ErrorResponse(std::optional<ErrorCode> code = std::nullopt)
error_code_(code)126       : error_code_(code) {}
127   // Response overrides.
complete()128   bool complete() const override { return error_code_.has_value(); }
129 
ContinuationState()130   const BufferView ContinuationState() const override {
131     // ErrorResponses never have continuation state.
132     return BufferView();
133   }
134 
135   fit::result<Error<>> Parse(const ByteBuffer& buf) override;
136 
137   // Note: |max_size| and |cont_state| are ignored.
138   // Error Responses do not have a valid continuation.
139   MutableByteBufferPtr GetPDU(uint16_t req_max,
140                               TransactionId tid,
141                               uint16_t max_size,
142                               const ByteBuffer& cont_state) const override;
143 
error_code()144   const std::optional<ErrorCode>& error_code() const { return error_code_; }
set_error_code(ErrorCode code)145   void set_error_code(ErrorCode code) { error_code_ = code; }
146 
147  private:
148   std::optional<ErrorCode> error_code_;
149 };
150 
151 // Used to locate service records that match a pattern.
152 // Note: there is no mechanism to retrieve all service records.
153 // See v5.0, Vol 3, Part B, 4.5.1
154 class ServiceSearchRequest : public Request {
155  public:
156   // Create an empty search request.
157   ServiceSearchRequest();
158   // Parse the parameters given in |params| to initialize this request.
159   explicit ServiceSearchRequest(const ByteBuffer& params);
160 
161   // Request overrides
162   bool valid() const override;
163   ByteBufferPtr GetPDU(TransactionId tid) const override;
164 
165   // A service search pattern matches if every UUID in the pattern is contained
166   // within one of the services' attribute values.  They don't need to be in any
167   // specific attribute or in any particular order, and extraneous UUIDs are
168   // allowed to exist in the attribute value.
169   // See v5.0, Volume 3, Part B, Sec 2.5.2
set_search_pattern(std::unordered_set<UUID> pattern)170   void set_search_pattern(std::unordered_set<UUID> pattern) {
171     service_search_pattern_ = pattern;
172   }
service_search_pattern()173   const std::unordered_set<UUID>& service_search_pattern() const {
174     return service_search_pattern_;
175   }
176 
177   // The maximum count of records that should be included in any
178   // response.
set_max_service_record_count(uint16_t count)179   void set_max_service_record_count(uint16_t count) {
180     max_service_record_count_ = count;
181   }
max_service_record_count()182   uint16_t max_service_record_count() const {
183     return max_service_record_count_;
184   }
185 
186  private:
187   std::unordered_set<UUID> service_search_pattern_;
188   uint16_t max_service_record_count_;
189 };
190 
191 // Generated by the SDP server in response to a ServiceSearchRequest.
192 // See v5.0, Volume 3, Part B, Sec 4.5.2
193 class ServiceSearchResponse : public Response {
194  public:
195   ServiceSearchResponse();
196 
197   // Response overrides
198   bool complete() const override;
199   const BufferView ContinuationState() const override;
200   fit::result<Error<>> Parse(const ByteBuffer& buf) override;
201   MutableByteBufferPtr GetPDU(uint16_t req_max,
202                               TransactionId tid,
203                               uint16_t max_size,
204                               const ByteBuffer& cont_state) const override;
205 
206   // The ServiceRecordHandleList contains as list of service record handles.
207   // This should be set to the list of handles that match the request.
208   // Limiting the response to the maximum requested is handled by
209   // GetPDU();
set_service_record_handle_list(std::vector<ServiceHandle> handles)210   void set_service_record_handle_list(std::vector<ServiceHandle> handles) {
211     service_record_handle_list_ = handles;
212     total_service_record_count_ = static_cast<uint16_t>(handles.size());
213   }
service_record_handle_list()214   std::vector<ServiceHandle> service_record_handle_list() const {
215     return service_record_handle_list_;
216   }
217 
218  private:
219   // The list of service record handles.
220   std::vector<ServiceHandle> service_record_handle_list_;
221   // The total number of service records in the full response.
222   uint16_t total_service_record_count_;
223 
224   MutableByteBufferPtr continuation_state_;
225 };
226 
227 // Represents a range of attributes, inclusive of |start| and |end|.
228 struct AttributeRange {
AttributeRangeAttributeRange229   AttributeRange(AttributeId start, AttributeId end) : start(start), end(end) {
230     BT_DEBUG_ASSERT(start <= end);
231   }
232 
233   AttributeId start;
234   AttributeId end;
235 };
236 
237 // Used to retrieve a set of attributes from a specific service record.
238 // See v5.0, Volume 3, Part B, Sec 4.6.1
239 class ServiceAttributeRequest : public Request {
240  public:
241   // Create an empty search request.
242   ServiceAttributeRequest();
243   // Parse the parameters in |params| to initialize this request.
244   // valid() will be false if |params| don't represent valid a valid request.
245   explicit ServiceAttributeRequest(const ByteBuffer& params);
246 
247   // Request overrides
248   bool valid() const override;
249   ByteBufferPtr GetPDU(TransactionId tid) const override;
250 
set_service_record_handle(ServiceHandle handle)251   void set_service_record_handle(ServiceHandle handle) {
252     service_record_handle_ = handle;
253   }
service_record_handle()254   ServiceHandle service_record_handle() const { return service_record_handle_; }
255 
256   // Set the maximum size allowed in the response in the Attribute list
257   // Not allowed to be lower than kMinMaximumAttributeByteCount (7)
set_max_attribute_byte_count(uint16_t count)258   void set_max_attribute_byte_count(uint16_t count) {
259     BT_DEBUG_ASSERT(count >= kMinMaximumAttributeByteCount);
260     max_attribute_byte_count_ = count;
261   }
max_attribute_byte_count()262   uint16_t max_attribute_byte_count() const {
263     return max_attribute_byte_count_;
264   }
265 
266   // Adds a single attribute to the requested IDs. Used to ensure a specific
267   // attribute is requested.
268   // Automatically merges attribute ranges that are contiguous to save bytes in
269   // the request.
270   void AddAttribute(AttributeId id);
271 
272   // Adds a range of attributes to the requested IDs.
273   // Like AddAttribute(), attribute ranges that are contiguous are merged to
274   // save bytes in the resulting request.
275   void AddAttributeRange(AttributeId start, AttributeId end);
276 
attribute_ranges()277   const std::list<AttributeRange>& attribute_ranges() {
278     return attribute_ranges_;
279   }
280 
281  private:
282   // The service record handle for which attributes should be retrieved.
283   // Should be obtained by using a ServiceSearch transaction.
284   ServiceHandle service_record_handle_;
285 
286   // Maximum number of bytes of attribute data to be returned in the response.
287   // If the attributes don't fit, the server decides how to segment them.
288   // Clients should use continuation state to request more data.
289   uint16_t max_attribute_byte_count_;
290 
291   // The attribute(s) to retrieve.
292   // This is a list of ranges, inclusive of the ends.
293   // They are non-overlapping and sorted by the start id of each range.
294   std::list<AttributeRange> attribute_ranges_;
295 };
296 
297 // Generated upon receiving a ServiceAttributeRequest.
298 // See v5.0, Volume 3, Part B, Sec 4.6.2
299 class ServiceAttributeResponse : public Response {
300  public:
301   ServiceAttributeResponse();
302 
303   // Response overrides
304   const BufferView ContinuationState() const override;
305   bool complete() const override;
306   fit::result<Error<>> Parse(const ByteBuffer& buf) override;
307   MutableByteBufferPtr GetPDU(uint16_t req_max,
308                               TransactionId tid,
309                               uint16_t max_size,
310                               const ByteBuffer& cont_state) const override;
311 
set_attribute(AttributeId id,DataElement value)312   void set_attribute(AttributeId id, DataElement value) {
313     attributes_.emplace(id, std::move(value));
314   }
attributes()315   const std::map<AttributeId, DataElement>& attributes() const {
316     return attributes_;
317   }
318 
319  private:
320   // The list of attributes that matched the search and their values.
321   // This is sorted (it is in ascending order in the response).
322   std::map<AttributeId, DataElement> attributes_;
323 
324   // Attribute List(s) can be truncated due to:
325   //  - Response too long for MTU
326   //  - MaxAttributeListByteCount is set too low
327   //  - Because the server wants to
328   //
329   // This contains the partial attribute list response if there is continuation
330   // state.
331   MutableByteBufferPtr partial_response_;
332 
333   MutableByteBufferPtr continuation_state_;
334 };
335 
336 // Combines the capabilities of ServiceSearchRequest and ServiceAttributeRequest
337 // Note that the record handle is not included in the response by default, and
338 // myst be requested if needed.
339 // See v5.0, Volume 3, Part B,Sec 4.7.1
340 class ServiceSearchAttributeRequest : public Request {
341  public:
342   // Create an empty service search attribute request.
343   ServiceSearchAttributeRequest();
344   // Parse the parameters in |params| to initialize this request.
345   explicit ServiceSearchAttributeRequest(const ByteBuffer& params);
346 
347   // Request overrides
348   bool valid() const override;
349   ByteBufferPtr GetPDU(TransactionId tid) const override;
350 
351   // A service search pattern matches if every UUID in the pattern is contained
352   // within one of the services' attribute values.  They don't need to be in any
353   // specific attribute or in any particular order, and extraneous UUIDs are
354   // allowed to exist in the attribute value.
355   // See v5.0, Volume 3, Part B, Sec 2.5.2.
set_search_pattern(std::unordered_set<UUID> pattern)356   void set_search_pattern(std::unordered_set<UUID> pattern) {
357     service_search_pattern_ = pattern;
358   }
service_search_pattern()359   const std::unordered_set<UUID>& service_search_pattern() const {
360     return service_search_pattern_;
361   }
362 
363   // Set the maximum size allowed in the response in the Attribute list
364   // Not allowed to be lower than kMinMaximumAttributeByteCount (7)
set_max_attribute_byte_count(uint16_t count)365   void set_max_attribute_byte_count(uint16_t count) {
366     BT_DEBUG_ASSERT(count >= kMinMaximumAttributeByteCount);
367     max_attribute_byte_count_ = count;
368   }
max_attribute_byte_count()369   uint16_t max_attribute_byte_count() const {
370     return max_attribute_byte_count_;
371   }
372 
373   // Adds a single attribute to the requested IDs
374   void AddAttribute(AttributeId id);
375 
376   // Adds a range of attributes to the requested IDs.
377   void AddAttributeRange(AttributeId start, AttributeId end);
378 
attribute_ranges()379   const std::list<AttributeRange>& attribute_ranges() {
380     return attribute_ranges_;
381   }
382 
383  private:
384   // The service search pattern to match services.
385   std::unordered_set<UUID> service_search_pattern_;
386 
387   // Maximum number of bytes of attribute data to be returned in the response.
388   // If the attributes don't fit, the server decides how to segment them.
389   // Clients should use continuation state to request more data.
390   uint16_t max_attribute_byte_count_;
391 
392   // The attribute(s) to retrieve.
393   // This is a list of ranges, inclusive of the ends.
394   // They are non-overlapping and sorted by the first attribute id.
395   std::list<AttributeRange> attribute_ranges_;
396 };
397 
398 // Generated in response to a ServiceSearchAttributeRequest
399 // See v5.0, Volume 3, Part B,Sec 4.7.2
400 class ServiceSearchAttributeResponse : public Response {
401  public:
402   ServiceSearchAttributeResponse();
403 
404   // Response overrides
405   const BufferView ContinuationState() const override;
406   bool complete() const override;
407   fit::result<Error<>> Parse(const ByteBuffer& buf) override;
408   MutableByteBufferPtr GetPDU(uint16_t req_max,
409                               TransactionId tid,
410                               uint16_t max_size,
411                               const ByteBuffer& cont_state) const override;
412 
413   // Set an attribute to be included in the response.
414   // |idx| is used to group attributes and does not need to be contiguous for
415   // convenience (i.e. a service's handle), although parsed responses will
416   // be numbered starting from 0.
417   void SetAttribute(uint32_t idx, AttributeId id, DataElement value);
418 
419   // The number of attribute lists in this response.
num_attribute_lists()420   size_t num_attribute_lists() const { return attribute_lists_.size(); }
421 
422   // Retrieve attributes in response from a specific index.
423   // Attribute lists are numbered starting from 0 when parsed.
attributes(uint32_t idx)424   const std::map<AttributeId, DataElement>& attributes(uint32_t idx) const {
425     return attribute_lists_.at(idx);
426   }
427 
428  private:
429   // The list of lists that is to be returned / was returned in the response.
430   // They are in ascending order of index, which has no relation to the
431   // service IDs (they may not be included).
432   std::map<uint32_t, std::map<AttributeId, DataElement>> attribute_lists_;
433 
434   // The Attribute Lists can be truncated due to:
435   //  - Response too long for MTU
436   //  - MaxAttributeListByteCount is set too low
437   //  - Because the server wants to
438   //
439   // This contains the partial attribute list response if there is continuation
440   // state.
441   MutableByteBufferPtr partial_response_;
442 
443   MutableByteBufferPtr continuation_state_;
444 };
445 
446 }  // namespace bt::sdp
447