1 /*
2 * Copyright (C) 2021 BlueKitchen GmbH
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the copyright holders nor the names of
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 * 4. Any redistribution, use, or modification is done solely for
17 * personal benefit and not for any commercial purpose or for
18 * monetary gain.
19 *
20 * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24 * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 *
33 * Please inquire about commercial licensing options at
34 * [email protected]
35 *
36 */
37
38 #define BTSTACK_FILE__ "obex_parser.c"
39
40 #include "btstack_config.h"
41
42 #include "classic/obex.h"
43 #include "classic/obex_parser.h"
44 #include <string.h>
45 #include "btstack_debug.h"
46 #include "btstack_util.h"
47
48 #ifdef ENABLE_LOG_OBEX
49 #define RETURN_OBEX_STATE(state) { log_debug("RETURN_OBEX_STATE <%s>(%d)", #state, state); return state; }
50 #else
51 #define log_app_messaging(...)
52 #define RETURN_OBEX_STATE(state) return state
53 #endif
54
obex_parser_param_size_for_request(uint8_t opcode)55 static uint16_t obex_parser_param_size_for_request(uint8_t opcode){
56 switch (opcode) {
57 case OBEX_OPCODE_SETPATH:
58 return 4;
59 case OBEX_OPCODE_CONNECT:
60 return 6;
61 default:
62 return 2;
63 }
64 }
65
obex_parser_param_size_for_response(uint8_t opcode)66 static uint16_t obex_parser_param_size_for_response(uint8_t opcode){
67 switch (opcode) {
68 case OBEX_OPCODE_CONNECT:
69 return 6;
70 default:
71 return 2;
72 }
73 }
74
obex_parser_init(obex_parser_t * obex_parser,obex_parser_callback_t obex_parser_callback,void * user_data)75 static void obex_parser_init(obex_parser_t * obex_parser, obex_parser_callback_t obex_parser_callback, void * user_data){
76 memset(obex_parser, 0, sizeof(obex_parser_t));
77 obex_parser->packet_size = 3;
78 obex_parser->user_data = user_data;
79 obex_parser->callback = obex_parser_callback;
80 }
81
obex_parser_init_for_request(obex_parser_t * obex_parser,obex_parser_callback_t obex_parser_callback,void * user_data)82 void obex_parser_init_for_request(obex_parser_t * obex_parser, obex_parser_callback_t obex_parser_callback, void * user_data){
83 obex_parser_init(obex_parser, obex_parser_callback, user_data);
84 obex_parser->state = OBEX_PARSER_STATE_W4_OPCODE;
85 }
86
obex_parser_init_for_response(obex_parser_t * obex_parser,uint8_t opcode,obex_parser_callback_t obex_parser_callback,void * user_data)87 void obex_parser_init_for_response(obex_parser_t * obex_parser, uint8_t opcode, obex_parser_callback_t obex_parser_callback, void * user_data){
88 obex_parser_init(obex_parser, obex_parser_callback, user_data);
89 obex_parser->state = OBEX_PARSER_STATE_W4_RESPONSE_CODE;
90 obex_parser->opcode = opcode;
91 }
92
obex_parser_process_data(obex_parser_t * obex_parser,const uint8_t * data_buffer,uint16_t data_len)93 obex_parser_object_state_t obex_parser_process_data(obex_parser_t *obex_parser, const uint8_t *data_buffer, uint16_t data_len) {
94 while (data_len){
95 uint16_t bytes_to_consume = 1;
96 uint8_t header_type;
97 switch (obex_parser->state){
98 case OBEX_PARSER_STATE_W4_OPCODE:
99 obex_parser->opcode = *data_buffer;
100 obex_parser->state = OBEX_PARSER_STATE_W4_PACKET_LEN;
101 obex_parser->item_len = obex_parser_param_size_for_request(obex_parser->opcode);
102 obex_parser->packet_size = 1 + obex_parser->item_len;
103 break;
104 case OBEX_PARSER_STATE_W4_RESPONSE_CODE:
105 obex_parser->response_code = *data_buffer;
106 obex_parser->state = OBEX_PARSER_STATE_W4_PACKET_LEN;
107 obex_parser->item_len = obex_parser_param_size_for_response(obex_parser->opcode);
108 obex_parser->packet_size = 1 + obex_parser->item_len;
109 break;
110 case OBEX_PARSER_STATE_W4_PACKET_LEN:
111 bytes_to_consume = btstack_min(2 - obex_parser->item_pos, data_len);
112 memcpy(&obex_parser->params[obex_parser->item_pos], data_buffer, bytes_to_consume);
113 obex_parser->item_pos += bytes_to_consume;
114 if (obex_parser->item_pos == 2){
115 // validate packet large enough for header
116 obex_parser->packet_size = big_endian_read_16(obex_parser->params, 0);
117 if (obex_parser->packet_size < (obex_parser->item_len + 1)){
118 // packet size smaller than opcode + params
119 obex_parser->state = OBEX_PARSER_STATE_INVALID;
120 break;
121 }
122 // params already complete?
123 if (obex_parser->item_len == 2){
124 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
125 } else {
126 obex_parser->state = OBEX_PARSER_STATE_W4_PARAMS;
127 }
128 }
129 break;
130 case OBEX_PARSER_STATE_W4_PARAMS:
131 bytes_to_consume = btstack_min(obex_parser->item_len - obex_parser->item_pos, data_len);
132 memcpy(&obex_parser->params[obex_parser->item_pos], data_buffer, bytes_to_consume);
133 obex_parser->item_pos += bytes_to_consume;
134 if (obex_parser->item_pos == obex_parser->item_len){
135 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
136 }
137 break;
138 case OBEX_PARSER_STATE_W4_HEADER_ID:
139 obex_parser->header_id = *data_buffer; // constants in obex.h encode type as well, so just use these as well
140 header_type = *data_buffer >> 6;
141 obex_parser->item_pos = 0;
142 switch (header_type){
143 case 0:
144 case 1:
145 // 16-bit length info prefixed
146 obex_parser->item_len = 2;
147 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_LEN_FIRST;
148 break;
149 case 2:
150 // 8-bit value
151 obex_parser->item_len = 1;
152 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_VALUE;
153 break;
154 case 3:
155 // 32-bit value
156 obex_parser->item_len = 4;
157 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_VALUE;
158 break;
159 default:
160 // avoid compiler warning about unused cases (encoding in [0..3])
161 break;
162 }
163 break;
164 case OBEX_PARSER_STATE_W4_HEADER_LEN_FIRST:
165 obex_parser->item_len = *data_buffer << 8;
166 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_LEN_SECOND;
167 break;
168 case OBEX_PARSER_STATE_W4_HEADER_LEN_SECOND:
169 obex_parser->item_len = obex_parser->item_len + *data_buffer;
170 if (obex_parser->item_len < 3){
171 // len to small to even cover header
172 obex_parser->state = OBEX_PARSER_STATE_INVALID;
173 break;
174 };
175 if (obex_parser->item_len == 3){
176 // borderline: empty value
177 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
178 break;
179 }
180 obex_parser->item_len -= 3;
181 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_VALUE;
182 break;
183 case OBEX_PARSER_STATE_W4_HEADER_VALUE:
184 bytes_to_consume = btstack_min(obex_parser->item_len - obex_parser->item_pos, data_len);
185 if (*obex_parser->callback != NULL){
186 (*obex_parser->callback)(obex_parser->user_data, obex_parser->header_id, obex_parser->item_len, obex_parser->item_pos, data_buffer, bytes_to_consume);
187 }
188 obex_parser->item_pos += bytes_to_consume;
189 if (obex_parser->item_pos == obex_parser->item_len){
190 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
191 }
192 break;
193 case OBEX_PARSER_STATE_COMPLETE:
194 obex_parser->state = OBEX_PARSER_STATE_OVERRUN;
195 break;
196 case OBEX_PARSER_STATE_INVALID:
197 break;
198 default:
199 btstack_unreachable();
200 break;
201 }
202
203 data_buffer += bytes_to_consume;
204 data_len -= bytes_to_consume;
205 obex_parser->packet_pos += bytes_to_consume;
206
207 // all bytes read? then check state
208 if (obex_parser->packet_pos == obex_parser->packet_size){
209 if (obex_parser->state == OBEX_PARSER_STATE_W4_HEADER_ID){
210 obex_parser->state = OBEX_PARSER_STATE_COMPLETE;
211 } else {
212 obex_parser->state = OBEX_PARSER_STATE_INVALID;
213 }
214 }
215 }
216
217 switch (obex_parser->state){
218 case OBEX_PARSER_STATE_COMPLETE:
219 RETURN_OBEX_STATE(OBEX_PARSER_OBJECT_STATE_COMPLETE);
220 case OBEX_PARSER_STATE_OVERRUN:
221 RETURN_OBEX_STATE(OBEX_PARSER_OBJECT_STATE_OVERRUN);
222 case OBEX_PARSER_STATE_INVALID:
223 RETURN_OBEX_STATE(OBEX_PARSER_OBJECT_STATE_INVALID);
224 default:
225 RETURN_OBEX_STATE(OBEX_PARSER_OBJECT_STATE_INCOMPLETE);
226 }
227 }
228
obex_parser_get_operation_info(obex_parser_t * obex_parser,obex_parser_operation_info_t * obex_operation_info)229 void obex_parser_get_operation_info(obex_parser_t * obex_parser, obex_parser_operation_info_t * obex_operation_info){
230 memset(obex_operation_info, 0, sizeof(obex_parser_operation_info_t));
231 obex_operation_info->opcode = obex_parser->opcode;
232 obex_operation_info->response_code = obex_parser->response_code;
233 switch (obex_parser->opcode){
234 case OBEX_OPCODE_CONNECT:
235 obex_operation_info->obex_version_number = obex_parser->params[2];
236 obex_operation_info->flags = obex_parser->params[3];
237 obex_operation_info->max_packet_length = big_endian_read_16(obex_parser->params, 4);
238 break;
239 case OBEX_OPCODE_SETPATH:
240 obex_operation_info->flags = obex_parser->params[2];
241 break;
242 default:
243 break;
244 }
245 }
obex_parser_header_store(uint8_t * header_buffer,uint16_t buffer_size,uint16_t total_len,uint16_t data_offset,const uint8_t * data_buffer,uint16_t data_len)246 obex_parser_header_state_t obex_parser_header_store(uint8_t * header_buffer, uint16_t buffer_size, uint16_t total_len,
247 uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){
248 uint16_t bytes_to_store = btstack_min(buffer_size - data_offset, data_len);
249 memcpy(&header_buffer[data_offset], data_buffer, bytes_to_store);
250 uint16_t new_offset = data_offset + bytes_to_store;
251 if (new_offset > buffer_size){
252 RETURN_OBEX_STATE(OBEX_PARSER_HEADER_OVERRUN);
253 } else if (new_offset == total_len) {
254 RETURN_OBEX_STATE(OBEX_PARSER_HEADER_COMPLETE);
255 } else {
256 RETURN_OBEX_STATE(OBEX_PARSER_HEADER_INCOMPLETE);
257 }
258 }
259
260
261 /* OBEX App Param Parser */
262
obex_app_param_parser_init(obex_app_param_parser_t * parser,obex_app_param_parser_callback_t callback,uint16_t param_size,void * user_data)263 void obex_app_param_parser_init(obex_app_param_parser_t * parser, obex_app_param_parser_callback_t callback, uint16_t param_size, void * user_data){
264 parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_TYPE;
265 parser->callback = callback;
266 parser->user_data = user_data;
267 parser->param_size = param_size;
268 parser->param_pos = 0;
269 }
270
obex_app_param_parser_process_data(obex_app_param_parser_t * parser,const uint8_t * data_buffer,uint16_t data_len)271 obex_app_param_parser_params_state_t obex_app_param_parser_process_data(obex_app_param_parser_t *parser, const uint8_t *data_buffer, uint16_t data_len){
272 while ((data_len > 0) && (parser->param_pos < parser->param_size)){
273 uint16_t bytes_to_consume = 1;
274 switch(parser->state){
275 case OBEX_APP_PARAM_PARSER_STATE_INVALID:
276 RETURN_OBEX_STATE(OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID);
277 case OBEX_APP_PARAM_PARSER_STATE_W4_TYPE:
278 parser->tag_id = *data_buffer;
279 parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_LEN;
280 break;
281 case OBEX_APP_PARAM_PARSER_STATE_W4_LEN:
282 parser->tag_len = *data_buffer;
283 if ((parser->param_pos + parser->tag_len) > parser->param_size){
284 parser->state = OBEX_APP_PARAM_PARSER_STATE_INVALID;
285 RETURN_OBEX_STATE(OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID);
286 }
287 parser->tag_pos = 0;
288 parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_VALUE;
289 break;
290 case OBEX_APP_PARAM_PARSER_STATE_W4_VALUE:
291 bytes_to_consume = btstack_min(parser->tag_len - parser->tag_pos, data_len);
292 // param_size is uint8_t
293 (*parser->callback)(parser->user_data, parser->tag_id, (uint8_t) parser->tag_len, (uint8_t) parser->tag_pos, data_buffer, (uint8_t) bytes_to_consume);
294 parser->tag_pos += bytes_to_consume;
295 if (parser->tag_pos == parser->tag_len){
296 parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_TYPE;
297 }
298 break;
299 default:
300 btstack_unreachable();
301 break;
302 }
303
304 data_buffer += bytes_to_consume;
305 data_len -= bytes_to_consume;
306 parser->param_pos += bytes_to_consume;
307
308 // all bytes read? then check state
309 if (parser->param_pos == parser->param_size){
310 if (parser->state == OBEX_APP_PARAM_PARSER_STATE_W4_TYPE){
311 parser->state = OBEX_APP_PARAM_PARSER_STATE_COMPLETE;
312 } else {
313 parser->state = OBEX_APP_PARAM_PARSER_STATE_INVALID;
314 }
315 }
316 }
317
318 if (data_len > 0){
319 RETURN_OBEX_STATE(OBEX_APP_PARAM_PARSER_PARAMS_STATE_OVERRUN);
320 }
321
322 switch (parser->state){
323 case OBEX_APP_PARAM_PARSER_STATE_COMPLETE:
324 RETURN_OBEX_STATE(OBEX_APP_PARAM_PARSER_PARAMS_STATE_COMPLETE);
325 case OBEX_APP_PARAM_PARSER_STATE_INVALID:
326 RETURN_OBEX_STATE(OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID);
327 default:
328 RETURN_OBEX_STATE(OBEX_APP_PARAM_PARSER_PARAMS_STATE_INCOMPLETE);
329 }
330 }
331
332
obex_app_param_parser_tag_store(uint8_t * header_buffer,uint8_t buffer_size,uint8_t total_len,uint8_t data_offset,const uint8_t * data_buffer,uint8_t data_len)333 obex_app_param_parser_tag_state_t obex_app_param_parser_tag_store(uint8_t * header_buffer, uint8_t buffer_size, uint8_t total_len,
334 uint8_t data_offset, const uint8_t * data_buffer, uint8_t data_len){
335 uint16_t bytes_to_store = btstack_min(buffer_size - data_offset, data_len);
336 memcpy(&header_buffer[data_offset], data_buffer, bytes_to_store);
337 uint16_t new_offset = data_offset + bytes_to_store;
338 if (new_offset > buffer_size){
339 RETURN_OBEX_STATE(OBEX_APP_PARAM_PARSER_TAG_OVERRUN);
340 } else if (new_offset == total_len) {
341 RETURN_OBEX_STATE(OBEX_APP_PARAM_PARSER_TAG_COMPLETE);
342 } else {
343 RETURN_OBEX_STATE(OBEX_APP_PARAM_PARSER_TAG_INCOMPLETE);
344 }
345 }
346