xref: /btstack/src/classic/obex_parser.c (revision ac9a0d84d5f7a715f496fcd8d0dd3b859de2f81a)
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 static uint16_t obex_parser_param_size_for_request(uint8_t opcode){
49     switch (opcode) {
50         case OBEX_OPCODE_SETPATH:
51             return 4;
52         case OBEX_OPCODE_CONNECT:
53             return 6;
54         default:
55             return 2;
56     }
57 }
58 
59 static uint16_t obex_parser_param_size_for_response(uint8_t opcode){
60     switch (opcode) {
61         case OBEX_OPCODE_CONNECT:
62             return 6;
63         default:
64             return 2;
65     }
66 }
67 
68 static void obex_parser_init(obex_parser_t * obex_parser, obex_parser_callback_t obex_parser_callback, void * user_data){
69     memset(obex_parser, 0, sizeof(obex_parser_t));
70     obex_parser->packet_size = 3;
71     obex_parser->user_data = user_data;
72     obex_parser->callback = obex_parser_callback;
73 }
74 
75 void obex_parser_init_for_request(obex_parser_t * obex_parser, obex_parser_callback_t obex_parser_callback, void * user_data){
76     obex_parser_init(obex_parser, obex_parser_callback, user_data);
77     obex_parser->state = OBEX_PARSER_STATE_W4_OPCODE;
78 }
79 
80 void obex_parser_init_for_response(obex_parser_t * obex_parser, uint8_t opcode, obex_parser_callback_t obex_parser_callback, void * user_data){
81     obex_parser_init(obex_parser, obex_parser_callback, user_data);
82     obex_parser->state = OBEX_PARSER_STATE_W4_RESPONSE_CODE;
83     obex_parser->opcode = opcode;
84 }
85 
86 obex_parser_object_state_t obex_parser_process_data(obex_parser_t *obex_parser, const uint8_t *data_buffer, uint16_t data_len) {
87     while (data_len){
88         uint16_t bytes_to_consume = 1;
89         uint8_t  header_type;
90         switch (obex_parser->state){
91             case OBEX_PARSER_STATE_W4_OPCODE:
92                 obex_parser->opcode = *data_buffer;
93                 obex_parser->state = OBEX_PARSER_STATE_W4_PACKET_LEN;
94                 obex_parser->item_len = obex_parser_param_size_for_request(obex_parser->opcode);
95                 obex_parser->packet_size = 1 + obex_parser->item_len;
96                 break;
97             case OBEX_PARSER_STATE_W4_RESPONSE_CODE:
98                 obex_parser->response_code = *data_buffer;
99                 obex_parser->state = OBEX_PARSER_STATE_W4_PACKET_LEN;
100                 obex_parser->item_len = obex_parser_param_size_for_response(obex_parser->opcode);
101                 obex_parser->packet_size = 1 + obex_parser->item_len;
102                 break;
103             case OBEX_PARSER_STATE_W4_PACKET_LEN:
104                 bytes_to_consume = btstack_min(2 - obex_parser->item_pos, data_len);
105                 memcpy(&obex_parser->params[obex_parser->item_pos], data_buffer, bytes_to_consume);
106                 obex_parser->item_pos += bytes_to_consume;
107                 if (obex_parser->item_pos == 2){
108                     // validate packet large enough for header
109                     obex_parser->packet_size = big_endian_read_16(obex_parser->params, 0);
110                     if (obex_parser->packet_size < (obex_parser->item_len + 1)){
111                         // packet size smaller than opcode + params
112                         obex_parser->state = OBEX_PARSER_STATE_INVALID;
113                         break;
114                     }
115                     // params already complete?
116                     if (obex_parser->item_len == 2){
117                         obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
118                     } else {
119                         obex_parser->state = OBEX_PARSER_STATE_W4_PARAMS;
120                     }
121                 }
122                 break;
123             case OBEX_PARSER_STATE_W4_PARAMS:
124                 bytes_to_consume = btstack_min(obex_parser->item_len - obex_parser->item_pos, data_len);
125                 memcpy(&obex_parser->params[obex_parser->item_pos], data_buffer, bytes_to_consume);
126                 obex_parser->item_pos += bytes_to_consume;
127                 if (obex_parser->item_pos == obex_parser->item_len){
128                     obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
129                 }
130                 break;
131             case OBEX_PARSER_STATE_W4_HEADER_ID:
132                 obex_parser->header_id = *data_buffer; // constants in obex.h encode type as well, so just use these as well
133                 header_type = *data_buffer >> 6;
134                 obex_parser->item_pos = 0;
135                 switch (header_type){
136                     case 0:
137                     case 1:
138                         // 16-bit length info prefixed
139                         obex_parser->item_len = 2;
140                         obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_LEN_FIRST;
141                         break;
142                     case 2:
143                         // 8-bit value
144                         obex_parser->item_len = 1;
145                         obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_VALUE;
146                         break;
147                     case 3:
148                         // 32-bit value
149                         obex_parser->item_len = 4;
150                         obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_VALUE;
151                         break;
152                     default:
153                         // avoid compiler warning about unused cases (encoding in [0..3])
154                         break;
155                 }
156                 break;
157             case OBEX_PARSER_STATE_W4_HEADER_LEN_FIRST:
158                 obex_parser->item_len = *data_buffer << 8;
159                 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_LEN_SECOND;
160                 break;
161             case OBEX_PARSER_STATE_W4_HEADER_LEN_SECOND:
162                 obex_parser->item_len = obex_parser->item_len + *data_buffer - 3;
163                 if ( obex_parser->item_len > 0){
164                     obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_VALUE;
165                 } else {
166                     obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
167                 }
168                 break;
169             case OBEX_PARSER_STATE_W4_HEADER_VALUE:
170                 bytes_to_consume = btstack_min(obex_parser->item_len - obex_parser->item_pos, data_len);
171                 if (*obex_parser->callback != NULL){
172                     (*obex_parser->callback)(obex_parser->user_data, obex_parser->header_id, obex_parser->item_len, obex_parser->item_pos, data_buffer, bytes_to_consume);
173                 }
174                 obex_parser->item_pos += bytes_to_consume;
175                 if (obex_parser->item_pos == obex_parser->item_len){
176                     obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID;
177                 }
178                 break;
179             case OBEX_PARSER_STATE_COMPLETE:
180                 obex_parser->state = OBEX_PARSER_STATE_OVERRUN;
181                 break;
182             case OBEX_PARSER_STATE_INVALID:
183                 break;
184             default:
185                 btstack_unreachable();
186                 break;
187         }
188 
189         data_buffer += bytes_to_consume;
190         data_len    -= bytes_to_consume;
191         obex_parser->packet_pos += bytes_to_consume;
192 
193         // all bytes read? then check state
194         if (obex_parser->packet_pos == obex_parser->packet_size){
195             if (obex_parser->state == OBEX_PARSER_STATE_W4_HEADER_ID){
196                 obex_parser->state = OBEX_PARSER_STATE_COMPLETE;
197             } else {
198                 obex_parser->state = OBEX_PARSER_STATE_INVALID;
199             }
200         }
201     }
202 
203     switch (obex_parser->state){
204         case OBEX_PARSER_STATE_COMPLETE:
205             return OBEX_PARSER_OBJECT_STATE_COMPLETE;
206         case OBEX_PARSER_STATE_OVERRUN:
207             return OBEX_PARSER_OBJECT_STATE_OVERRUN;
208         case OBEX_PARSER_STATE_INVALID:
209             return OBEX_PARSER_OBJECT_STATE_INVALID;
210         default:
211             return OBEX_PARSER_OBJECT_STATE_INCOMPLETE;
212     }
213 }
214 
215 void obex_parser_get_operation_info(obex_parser_t * obex_parser, obex_parser_operation_info_t * obex_operation_info){
216     memset(obex_operation_info, 0, sizeof(obex_parser_operation_info_t));
217     obex_operation_info->opcode = obex_parser->opcode;
218     obex_operation_info->response_code = obex_parser->response_code;
219     switch (obex_parser->opcode){
220         case OBEX_OPCODE_CONNECT:
221             obex_operation_info->obex_version_number = obex_parser->params[2];
222             obex_operation_info->flags = obex_parser->params[3];
223             obex_operation_info->max_packet_length = big_endian_read_16(obex_parser->params, 4);
224             break;
225         case OBEX_OPCODE_SETPATH:
226             obex_operation_info->flags = obex_parser->params[2];
227             break;
228         default:
229             break;
230     }
231 }
232 obex_parser_header_state_t obex_parser_header_store(uint8_t * header_buffer, uint16_t buffer_size, uint16_t total_len,
233                                                     uint16_t data_offset,  const uint8_t * data_buffer, uint16_t data_len){
234     uint16_t bytes_to_store = btstack_min(buffer_size - data_offset, data_len);
235     memcpy(&header_buffer[data_offset], data_buffer, bytes_to_store);
236     uint16_t new_offset = data_offset + bytes_to_store;
237     if (new_offset > buffer_size){
238         return OBEX_PARSER_HEADER_OVERRUN;
239     } else if (new_offset == total_len) {
240         return OBEX_PARSER_HEADER_COMPLETE;
241     } else {
242         return OBEX_PARSER_HEADER_INCOMPLETE;
243     }
244 }
245