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; 163 if (obex_parser->item_len < 3){ 164 // len to small to even cover header 165 obex_parser->state = OBEX_PARSER_STATE_INVALID; 166 break; 167 }; 168 if (obex_parser->item_len == 3){ 169 // borderline: empty value 170 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID; 171 break; 172 } 173 obex_parser->item_len -= 3; 174 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_VALUE; 175 break; 176 case OBEX_PARSER_STATE_W4_HEADER_VALUE: 177 bytes_to_consume = btstack_min(obex_parser->item_len - obex_parser->item_pos, data_len); 178 if (*obex_parser->callback != NULL){ 179 (*obex_parser->callback)(obex_parser->user_data, obex_parser->header_id, obex_parser->item_len, obex_parser->item_pos, data_buffer, bytes_to_consume); 180 } 181 obex_parser->item_pos += bytes_to_consume; 182 if (obex_parser->item_pos == obex_parser->item_len){ 183 obex_parser->state = OBEX_PARSER_STATE_W4_HEADER_ID; 184 } 185 break; 186 case OBEX_PARSER_STATE_COMPLETE: 187 obex_parser->state = OBEX_PARSER_STATE_OVERRUN; 188 break; 189 case OBEX_PARSER_STATE_INVALID: 190 break; 191 default: 192 btstack_unreachable(); 193 break; 194 } 195 196 data_buffer += bytes_to_consume; 197 data_len -= bytes_to_consume; 198 obex_parser->packet_pos += bytes_to_consume; 199 200 // all bytes read? then check state 201 if (obex_parser->packet_pos == obex_parser->packet_size){ 202 if (obex_parser->state == OBEX_PARSER_STATE_W4_HEADER_ID){ 203 obex_parser->state = OBEX_PARSER_STATE_COMPLETE; 204 } else { 205 obex_parser->state = OBEX_PARSER_STATE_INVALID; 206 } 207 } 208 } 209 210 switch (obex_parser->state){ 211 case OBEX_PARSER_STATE_COMPLETE: 212 return OBEX_PARSER_OBJECT_STATE_COMPLETE; 213 case OBEX_PARSER_STATE_OVERRUN: 214 return OBEX_PARSER_OBJECT_STATE_OVERRUN; 215 case OBEX_PARSER_STATE_INVALID: 216 return OBEX_PARSER_OBJECT_STATE_INVALID; 217 default: 218 return OBEX_PARSER_OBJECT_STATE_INCOMPLETE; 219 } 220 } 221 222 void obex_parser_get_operation_info(obex_parser_t * obex_parser, obex_parser_operation_info_t * obex_operation_info){ 223 memset(obex_operation_info, 0, sizeof(obex_parser_operation_info_t)); 224 obex_operation_info->opcode = obex_parser->opcode; 225 obex_operation_info->response_code = obex_parser->response_code; 226 switch (obex_parser->opcode){ 227 case OBEX_OPCODE_CONNECT: 228 obex_operation_info->obex_version_number = obex_parser->params[2]; 229 obex_operation_info->flags = obex_parser->params[3]; 230 obex_operation_info->max_packet_length = big_endian_read_16(obex_parser->params, 4); 231 break; 232 case OBEX_OPCODE_SETPATH: 233 obex_operation_info->flags = obex_parser->params[2]; 234 break; 235 default: 236 break; 237 } 238 } 239 obex_parser_header_state_t obex_parser_header_store(uint8_t * header_buffer, uint16_t buffer_size, uint16_t total_len, 240 uint16_t data_offset, const uint8_t * data_buffer, uint16_t data_len){ 241 uint16_t bytes_to_store = btstack_min(buffer_size - data_offset, data_len); 242 memcpy(&header_buffer[data_offset], data_buffer, bytes_to_store); 243 uint16_t new_offset = data_offset + bytes_to_store; 244 if (new_offset > buffer_size){ 245 return OBEX_PARSER_HEADER_OVERRUN; 246 } else if (new_offset == total_len) { 247 return OBEX_PARSER_HEADER_COMPLETE; 248 } else { 249 return OBEX_PARSER_HEADER_INCOMPLETE; 250 } 251 } 252 253 254 /* OBEX App Param Parser */ 255 256 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){ 257 parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_TYPE; 258 parser->callback = callback; 259 parser->user_data = user_data; 260 parser->param_size = param_size; 261 parser->param_pos = 0; 262 } 263 264 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){ 265 while ((data_len > 0) && (parser->param_pos < parser->param_size)){ 266 uint16_t bytes_to_consume = 1; 267 switch(parser->state){ 268 case OBEX_APP_PARAM_PARSER_STATE_INVALID: 269 return OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID; 270 case OBEX_APP_PARAM_PARSER_STATE_W4_TYPE: 271 parser->tag_id = *data_buffer; 272 parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_LEN; 273 break; 274 case OBEX_APP_PARAM_PARSER_STATE_W4_LEN: 275 parser->tag_len = *data_buffer; 276 if ((parser->param_pos + parser->tag_len) > parser->param_size){ 277 parser->state = OBEX_APP_PARAM_PARSER_STATE_INVALID; 278 return OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID; 279 } 280 parser->tag_pos = 0; 281 parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_VALUE; 282 break; 283 case OBEX_APP_PARAM_PARSER_STATE_W4_VALUE: 284 bytes_to_consume = btstack_min(parser->tag_len - parser->tag_pos, data_len); 285 // param_size is uint8_t 286 (*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); 287 parser->tag_pos += bytes_to_consume; 288 if (parser->tag_pos == parser->tag_len){ 289 parser->state = OBEX_APP_PARAM_PARSER_STATE_W4_TYPE; 290 } 291 break; 292 default: 293 btstack_unreachable(); 294 break; 295 } 296 297 data_buffer += bytes_to_consume; 298 data_len -= bytes_to_consume; 299 parser->param_pos += bytes_to_consume; 300 301 // all bytes read? then check state 302 if (parser->param_pos == parser->param_size){ 303 if (parser->state == OBEX_APP_PARAM_PARSER_STATE_W4_TYPE){ 304 parser->state = OBEX_APP_PARAM_PARSER_STATE_COMPLETE; 305 } else { 306 parser->state = OBEX_APP_PARAM_PARSER_STATE_INVALID; 307 } 308 } 309 } 310 311 if (data_len > 0){ 312 return OBEX_APP_PARAM_PARSER_PARAMS_STATE_OVERRUN; 313 } 314 315 switch (parser->state){ 316 case OBEX_APP_PARAM_PARSER_STATE_COMPLETE: 317 return OBEX_APP_PARAM_PARSER_PARAMS_STATE_COMPLETE; 318 case OBEX_APP_PARAM_PARSER_STATE_INVALID: 319 return OBEX_APP_PARAM_PARSER_PARAMS_STATE_INVALID; 320 default: 321 return OBEX_APP_PARAM_PARSER_PARAMS_STATE_INCOMPLETE; 322 } 323 } 324 325 326 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, 327 uint8_t data_offset, const uint8_t * data_buffer, uint8_t data_len){ 328 uint16_t bytes_to_store = btstack_min(buffer_size - data_offset, data_len); 329 memcpy(&header_buffer[data_offset], data_buffer, bytes_to_store); 330 uint16_t new_offset = data_offset + bytes_to_store; 331 if (new_offset > buffer_size){ 332 return OBEX_APP_PARAM_PARSER_TAG_OVERRUN; 333 } else if (new_offset == total_len) { 334 return OBEX_APP_PARAM_PARSER_TAG_COMPLETE; 335 } else { 336 return OBEX_APP_PARAM_PARSER_TAG_INCOMPLETE; 337 } 338 } 339