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 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 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 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 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 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 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 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 } 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 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 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 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