• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::gatt::server::att_database::{AttAttribute, AttDatabase};
2 use crate::packets::att::{self, AttErrorCode};
3 use pdl_runtime::EncodeError;
4 
5 use super::helpers::att_range_filter::filter_to_range;
6 use super::helpers::payload_accumulator::PayloadAccumulator;
7 
handle_find_information_request<T: AttDatabase>( request: att::AttFindInformationRequest, mtu: usize, db: &T, ) -> Result<att::Att, EncodeError>8 pub fn handle_find_information_request<T: AttDatabase>(
9     request: att::AttFindInformationRequest,
10     mtu: usize,
11     db: &T,
12 ) -> Result<att::Att, EncodeError> {
13     let Some(attrs) = filter_to_range(
14         request.starting_handle.clone().into(),
15         request.ending_handle.into(),
16         db.list_attributes().into_iter(),
17     ) else {
18         return att::AttErrorResponse {
19             opcode_in_error: att::AttOpcode::FindInformationRequest,
20             handle_in_error: request.starting_handle.clone(),
21             error_code: AttErrorCode::InvalidHandle,
22         }
23         .try_into();
24     };
25 
26     if let Some(resp) = handle_find_information_request_short(attrs.clone(), mtu) {
27         resp.try_into()
28     } else if let Some(resp) = handle_find_information_request_long(attrs, mtu) {
29         resp.try_into()
30     } else {
31         att::AttErrorResponse {
32             opcode_in_error: att::AttOpcode::FindInformationRequest,
33             handle_in_error: request.starting_handle,
34             error_code: AttErrorCode::AttributeNotFound,
35         }
36         .try_into()
37     }
38 }
39 
40 /// Returns a builder IF we can return at least one attribute, otherwise returns
41 /// None
handle_find_information_request_short( attributes: impl Iterator<Item = AttAttribute>, mtu: usize, ) -> Option<att::AttFindInformationShortResponse>42 fn handle_find_information_request_short(
43     attributes: impl Iterator<Item = AttAttribute>,
44     mtu: usize,
45 ) -> Option<att::AttFindInformationShortResponse> {
46     // Core Spec 5.3 Vol 3F 3.4.3.2 gives the ATT_MTU - 2 limit
47     let mut out = PayloadAccumulator::new(mtu - 2);
48     for AttAttribute { handle, type_: uuid, .. } in attributes {
49         if let Ok(uuid) = uuid.try_into() {
50             if out.push(att::AttFindInformationResponseShortEntry { handle: handle.into(), uuid }) {
51                 // If we successfully pushed a 16-bit UUID, continue. In all other cases, we
52                 // should break.
53                 continue;
54             }
55         }
56         break;
57     }
58 
59     if out.is_empty() {
60         None
61     } else {
62         Some(att::AttFindInformationShortResponse { data: out.into_vec() })
63     }
64 }
65 
handle_find_information_request_long( attributes: impl Iterator<Item = AttAttribute>, mtu: usize, ) -> Option<att::AttFindInformationLongResponse>66 fn handle_find_information_request_long(
67     attributes: impl Iterator<Item = AttAttribute>,
68     mtu: usize,
69 ) -> Option<att::AttFindInformationLongResponse> {
70     // Core Spec 5.3 Vol 3F 3.4.3.2 gives the ATT_MTU - 2 limit
71     let mut out = PayloadAccumulator::new(mtu - 2);
72 
73     for AttAttribute { handle, type_: uuid, .. } in attributes {
74         if !out.push(att::AttFindInformationResponseLongEntry {
75             handle: handle.into(),
76             uuid: uuid.into(),
77         }) {
78             break;
79         }
80     }
81 
82     if out.is_empty() {
83         None
84     } else {
85         Some(att::AttFindInformationLongResponse { data: out.into_vec() })
86     }
87 }
88 
89 #[cfg(test)]
90 mod test {
91     use crate::core::uuid::Uuid;
92     use crate::gatt::server::gatt_database::AttPermissions;
93     use crate::gatt::server::test::test_att_db::TestAttDatabase;
94     use crate::gatt::server::AttHandle;
95     use crate::packets::att;
96 
97     use super::*;
98 
99     #[test]
test_long_uuids()100     fn test_long_uuids() {
101         // arrange
102         let db = TestAttDatabase::new(vec![
103             (
104                 AttAttribute {
105                     handle: AttHandle(3),
106                     type_: Uuid::new(0x01020304),
107                     permissions: AttPermissions::READABLE,
108                 },
109                 vec![4, 5],
110             ),
111             (
112                 AttAttribute {
113                     handle: AttHandle(4),
114                     type_: Uuid::new(0x01020305),
115                     permissions: AttPermissions::READABLE,
116                 },
117                 vec![4, 5],
118             ),
119             (
120                 AttAttribute {
121                     handle: AttHandle(5),
122                     type_: Uuid::new(0x01020306),
123                     permissions: AttPermissions::READABLE,
124                 },
125                 vec![4, 5],
126             ),
127         ]);
128 
129         // act
130         let att_view = att::AttFindInformationRequest {
131             starting_handle: AttHandle(3).into(),
132             ending_handle: AttHandle(4).into(),
133         };
134         let response = handle_find_information_request(att_view, 128, &db);
135 
136         // assert
137         assert_eq!(
138             response,
139             att::AttFindInformationLongResponse {
140                 data: vec![
141                     att::AttFindInformationResponseLongEntry {
142                         handle: AttHandle(3).into(),
143                         uuid: Uuid::new(0x01020304).into(),
144                     },
145                     att::AttFindInformationResponseLongEntry {
146                         handle: AttHandle(4).into(),
147                         uuid: Uuid::new(0x01020305).into(),
148                     }
149                 ]
150             }
151             .try_into()
152         );
153     }
154 
155     #[test]
test_short_uuids()156     fn test_short_uuids() {
157         // arrange
158         let db = TestAttDatabase::new(vec![
159             (
160                 AttAttribute {
161                     handle: AttHandle(3),
162                     type_: Uuid::new(0x0102),
163                     permissions: AttPermissions::READABLE,
164                 },
165                 vec![4, 5],
166             ),
167             (
168                 AttAttribute {
169                     handle: AttHandle(4),
170                     type_: Uuid::new(0x0103),
171                     permissions: AttPermissions::READABLE,
172                 },
173                 vec![4, 5],
174             ),
175             (
176                 AttAttribute {
177                     handle: AttHandle(5),
178                     type_: Uuid::new(0x01020306),
179                     permissions: AttPermissions::READABLE,
180                 },
181                 vec![4, 5],
182             ),
183         ]);
184 
185         // act
186         let att_view = att::AttFindInformationRequest {
187             starting_handle: AttHandle(3).into(),
188             ending_handle: AttHandle(5).into(),
189         };
190         let response = handle_find_information_request(att_view, 128, &db);
191 
192         // assert
193         assert_eq!(
194             response,
195             att::AttFindInformationShortResponse {
196                 data: vec![
197                     att::AttFindInformationResponseShortEntry {
198                         handle: AttHandle(3).into(),
199                         uuid: Uuid::new(0x0102).try_into().unwrap(),
200                     },
201                     att::AttFindInformationResponseShortEntry {
202                         handle: AttHandle(4).into(),
203                         uuid: Uuid::new(0x0103).try_into().unwrap(),
204                     }
205                 ]
206             }
207             .try_into()
208         );
209     }
210 
211     #[test]
test_handle_validation()212     fn test_handle_validation() {
213         // arrange: empty db
214         let db = TestAttDatabase::new(vec![]);
215 
216         // act: use an invalid handle range
217         let att_view = att::AttFindInformationRequest {
218             starting_handle: AttHandle(3).into(),
219             ending_handle: AttHandle(2).into(),
220         };
221         let response = handle_find_information_request(att_view, 128, &db);
222 
223         // assert: got INVALID_HANDLE
224         assert_eq!(
225             response,
226             att::AttErrorResponse {
227                 opcode_in_error: att::AttOpcode::FindInformationRequest,
228                 handle_in_error: AttHandle(3).into(),
229                 error_code: AttErrorCode::InvalidHandle,
230             }
231             .try_into()
232         );
233     }
234 
235     #[test]
test_limit_total_size()236     fn test_limit_total_size() {
237         // arrange
238         let db = TestAttDatabase::new(vec![
239             (
240                 AttAttribute {
241                     handle: AttHandle(3),
242                     type_: Uuid::new(0x0102),
243                     permissions: AttPermissions::READABLE,
244                 },
245                 vec![4, 5],
246             ),
247             (
248                 AttAttribute {
249                     handle: AttHandle(4),
250                     type_: Uuid::new(0x0103),
251                     permissions: AttPermissions::READABLE,
252                 },
253                 vec![4, 5],
254             ),
255         ]);
256 
257         // act: use MTU = 6, so only one entry can fit
258         let att_view = att::AttFindInformationRequest {
259             starting_handle: AttHandle(3).into(),
260             ending_handle: AttHandle(5).into(),
261         };
262         let response = handle_find_information_request(att_view, 6, &db);
263 
264         // assert: only one entry (not two) provided
265         assert_eq!(
266             response,
267             att::AttFindInformationShortResponse {
268                 data: vec![att::AttFindInformationResponseShortEntry {
269                     handle: AttHandle(3).into(),
270                     uuid: Uuid::new(0x0102).try_into().unwrap(),
271                 },]
272             }
273             .try_into()
274         );
275     }
276 
277     #[test]
test_empty_output()278     fn test_empty_output() {
279         // arrange
280         let db = TestAttDatabase::new(vec![(
281             AttAttribute {
282                 handle: AttHandle(3),
283                 type_: Uuid::new(0x0102),
284                 permissions: AttPermissions::READABLE,
285             },
286             vec![4, 5],
287         )]);
288 
289         // act: use a range that matches no attributes
290         let att_view = att::AttFindInformationRequest {
291             starting_handle: AttHandle(4).into(),
292             ending_handle: AttHandle(5).into(),
293         };
294         let response = handle_find_information_request(att_view, 6, &db);
295 
296         // assert: got ATTRIBUTE_NOT_FOUND
297         assert_eq!(
298             response,
299             att::AttErrorResponse {
300                 opcode_in_error: att::AttOpcode::FindInformationRequest,
301                 handle_in_error: AttHandle(4).into(),
302                 error_code: AttErrorCode::AttributeNotFound,
303             }
304             .try_into()
305         );
306     }
307 }
308