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