1 /* 2 * Copyright (C) 2019 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_message_builder.c" 39 40 #include "btstack_config.h" 41 42 #include <stdint.h> 43 #include <stdlib.h> 44 45 #include "btstack_util.h" 46 #include "btstack_debug.h" 47 #include "classic/obex.h" 48 #include "classic/obex_message_builder.h" 49 50 #if defined(ENABLE_LOG_DEBUG) || defined(ENABLE_LOG_ERROR) 51 #define LUT(which) [which] = #which 52 const char* lut_type[0xFF] = { 53 LUT(OBEX_HEADER_NAME), 54 LUT(OBEX_HEADER_DESCRIPTION), 55 LUT(OBEX_HEADER_IMG_HANDLE), 56 LUT(OBEX_HEADER_TYPE), 57 LUT(OBEX_HEADER_TIME_ISO_8601), 58 LUT(OBEX_HEADER_TARGET), 59 LUT(OBEX_HEADER_HTTP), 60 LUT(OBEX_HEADER_BODY), 61 LUT(OBEX_HEADER_END_OF_BODY), 62 LUT(OBEX_HEADER_WHO), 63 LUT(OBEX_HEADER_APPLICATION_PARAMETERS), 64 LUT(OBEX_HEADER_AUTHENTICATION_CHALLENGE), 65 LUT(OBEX_HEADER_AUTHENTICATION_RESPONSE), 66 LUT(OBEX_HEADER_OBJECT_CLASS), 67 LUT(OBEX_HEADER_IMG_DESCRIPTOR), 68 LUT(OBEX_HEADER_SINGLE_RESPONSE_MODE), 69 LUT(OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER), 70 LUT(OBEX_HEADER_COUNT), 71 LUT(OBEX_HEADER_LENGTH), 72 LUT(OBEX_HEADER_TIME_4_BYTE), 73 LUT(OBEX_HEADER_CONNECTION_ID), 74 }; 75 #endif 76 77 static uint8_t obex_message_builder_packet_init(uint8_t * buffer, uint16_t buffer_len, uint8_t opcode_or_response_code){ 78 if (buffer_len < 3) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 79 buffer[0] = opcode_or_response_code; 80 big_endian_store_16(buffer, 1, 3); 81 return ERROR_CODE_SUCCESS; 82 } 83 84 static uint8_t obex_message_builder_packet_append(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t len){ 85 uint16_t pos = big_endian_read_16(buffer, 1); 86 87 log_debug("add type:0x%02x(%s) buffer_len:%u pos+len:%u pos:%u len:%u", data[0], lut_type[data[0]], buffer_len, pos + len, pos, len); 88 89 if (buffer_len < pos + len) { 90 log_error("ERROR_CODE_MEMORY_CAPACITY_EXCEEDED type:0x%02x(%s) buffer_len:%u size:%u pos:%u len:%u", buffer[0], lut_type[buffer[0]], buffer_len, pos + len, pos, len); 91 return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 92 } 93 94 (void)memcpy(&buffer[pos], data, len); 95 pos += len; 96 big_endian_store_16(buffer, 1, pos); 97 return ERROR_CODE_SUCCESS; 98 } 99 100 uint16_t obex_message_builder_get_message_length(uint8_t * buffer){ 101 return big_endian_read_16(buffer, 1); 102 } 103 104 uint8_t obex_message_builder_header_add_byte(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, uint8_t value){ 105 uint8_t header[2]; 106 header[0] = header_type; 107 header[1] = value; 108 return obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header)); 109 } 110 111 uint8_t obex_message_builder_header_add_word(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, uint32_t value){ 112 uint8_t header[5]; 113 header[0] = header_type; 114 big_endian_store_32(header, 1, value); 115 return obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header)); 116 } 117 118 uint8_t obex_message_builder_header_add_variable(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, const uint8_t * header_data, uint16_t header_data_length){ 119 uint8_t header[3]; 120 header[0] = header_type; 121 big_endian_store_16(header, 1, sizeof(header) + header_data_length); 122 123 uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header)); 124 if (status != ERROR_CODE_SUCCESS) return status; 125 126 return obex_message_builder_packet_append(buffer, buffer_len, header_data, header_data_length); 127 } 128 129 uint8_t obex_message_builder_header_fillup_variable(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, const uint8_t * header_data, uint16_t header_data_length, uint32_t * ret_length){ 130 uint8_t header[3]; 131 header[0] = header_type; 132 uint16_t pos = big_endian_read_16(buffer, 1); 133 134 if (sizeof (header) + header_data_length + pos > buffer_len) 135 header_data_length = buffer_len - pos - sizeof (header); 136 137 big_endian_store_16(header, 1, sizeof(header) + header_data_length); 138 *ret_length = 0; 139 uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header)); 140 if (status != ERROR_CODE_SUCCESS) return status; 141 142 *ret_length = header_data_length; 143 return obex_message_builder_packet_append(buffer, buffer_len, header_data, header_data_length); 144 } 145 146 static uint8_t obex_message_builder_header_add_connection_id(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){ 147 // add connection_id header if set, must be first header if used 148 if (obex_connection_id == OBEX_CONNECTION_ID_INVALID) return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE; 149 return obex_message_builder_header_add_word(buffer, buffer_len, OBEX_HEADER_CONNECTION_ID, obex_connection_id); 150 } 151 152 static inline uint8_t obex_message_builder_create_connect(uint8_t * buffer, uint16_t buffer_len, uint8_t opcode, 153 uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){ 154 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, opcode); 155 if (status != ERROR_CODE_SUCCESS) return status; 156 157 uint8_t fields[4]; 158 fields[0] = obex_version_number; 159 fields[1] = flags; 160 big_endian_store_16(fields, 2, maximum_obex_packet_length); 161 return obex_message_builder_packet_append(buffer, buffer_len, &fields[0], sizeof(fields)); 162 } 163 164 uint8_t obex_message_builder_request_create_connect(uint8_t * buffer, uint16_t buffer_len, 165 uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){ 166 167 return obex_message_builder_create_connect(buffer, buffer_len, OBEX_OPCODE_CONNECT, obex_version_number, flags, maximum_obex_packet_length); 168 } 169 170 uint8_t obex_message_builder_response_create_connect(uint8_t * buffer, uint16_t buffer_len, uint8_t obex_version_number, 171 uint8_t flags, uint16_t maximum_obex_packet_length, uint32_t obex_connection_id){ 172 173 uint8_t status = obex_message_builder_create_connect(buffer, buffer_len, OBEX_RESP_SUCCESS, obex_version_number, flags, maximum_obex_packet_length); 174 if (status != ERROR_CODE_SUCCESS) return status; 175 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 176 } 177 178 uint8_t obex_message_builder_response_create_general(uint8_t * buffer, uint16_t buffer_len, uint8_t response_code){ 179 return obex_message_builder_packet_init(buffer, buffer_len, response_code); 180 } 181 182 uint8_t obex_message_builder_response_update_code(uint8_t * buffer, uint16_t buffer_len, uint8_t response_code){ 183 if (buffer_len < 3) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 184 buffer[0] = response_code; 185 return ERROR_CODE_SUCCESS; 186 } 187 188 uint8_t obex_message_builder_request_create_get(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){ 189 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_GET | OBEX_OPCODE_FINAL_BIT_MASK); 190 if (status != ERROR_CODE_SUCCESS) return status; 191 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 192 } 193 194 uint8_t obex_message_builder_request_create_put(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){ 195 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_PUT | OBEX_OPCODE_FINAL_BIT_MASK); 196 if (status != ERROR_CODE_SUCCESS) return status; 197 198 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 199 } 200 201 uint8_t obex_message_builder_request_create_set_path(uint8_t * buffer, uint16_t buffer_len, uint8_t flags, uint32_t obex_connection_id){ 202 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_SETPATH); 203 if (status != ERROR_CODE_SUCCESS) return status; 204 205 uint8_t fields[2]; 206 fields[0] = flags; 207 fields[1] = 0; // reserved 208 status = obex_message_builder_packet_append(buffer, buffer_len, &fields[0], sizeof(fields)); 209 if (status != ERROR_CODE_SUCCESS) return status; 210 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 211 } 212 213 uint8_t obex_message_builder_request_create_abort(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){ 214 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_ABORT); 215 if (status != ERROR_CODE_SUCCESS) return status; 216 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 217 } 218 219 uint8_t obex_message_builder_request_create_disconnect(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){ 220 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_DISCONNECT); 221 if (status != ERROR_CODE_SUCCESS) return status; 222 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 223 } 224 225 uint8_t obex_message_builder_set_final_bit (uint8_t * buffer, uint16_t buffer_len, bool final){ 226 if (buffer_len < 1) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 227 if (buffer[0] == OBEX_OPCODE_CONNECT || 228 buffer[0] == OBEX_OPCODE_DISCONNECT || 229 buffer[0] == OBEX_OPCODE_SETPATH || 230 buffer[0] == OBEX_OPCODE_SESSION || 231 buffer[0] == OBEX_OPCODE_ABORT){ 232 return ERROR_CODE_COMMAND_DISALLOWED; 233 } 234 buffer[0] &= ~OBEX_OPCODE_FINAL_BIT_MASK; 235 buffer[0] |= (final ? OBEX_OPCODE_FINAL_BIT_MASK : 0); 236 return ERROR_CODE_SUCCESS; 237 } 238 239 uint8_t obex_message_builder_header_add_srm_enable(uint8_t * buffer, uint16_t buffer_len){ 240 log_debug("SRM header enabled"); 241 return obex_message_builder_header_add_byte(buffer, buffer_len, OBEX_HEADER_SINGLE_RESPONSE_MODE, OBEX_SRM_ENABLE); 242 } 243 244 uint8_t obex_message_builder_header_add_srmp_wait(uint8_t* buffer, uint16_t buffer_len) { 245 return obex_message_builder_header_add_byte(buffer, buffer_len, OBEX_HEADER_SINGLE_RESPONSE_MODE_PARAMETER, OBEX_SRMP_WAIT); 246 } 247 248 uint8_t obex_message_builder_header_add_target(uint8_t * buffer, uint16_t buffer_len, const uint8_t * target, uint16_t length){ 249 return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_TARGET, target, length); 250 } 251 252 uint8_t obex_message_builder_header_add_application_parameters(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t length){ 253 return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_APPLICATION_PARAMETERS, data, length); 254 } 255 256 uint8_t obex_message_builder_header_add_challenge_response(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t length){ 257 return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_AUTHENTICATION_RESPONSE, data, length); 258 } 259 260 uint8_t obex_message_builder_header_add_who(uint8_t * buffer, uint16_t buffer_len, const uint8_t * who){ 261 return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_WHO, who, 16); 262 } 263 264 uint8_t obex_message_builder_body_add_static(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint32_t length){ 265 return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_END_OF_BODY, data, length); 266 } 267 268 uint8_t obex_message_builder_body_fillup_static(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint32_t length, uint32_t *ret_length){ 269 return obex_message_builder_header_fillup_variable(buffer, buffer_len, OBEX_HEADER_END_OF_BODY, data, length, ret_length); 270 } 271 272 uint8_t obex_message_builder_get_header_name_len_from_strlen(uint16_t name_len) { 273 274 // non-empty string have trailing \0 275 bool add_trailing_zero = name_len > 0; 276 return 1 + 2 + (name_len * 2) + (add_trailing_zero ? 2 : 0); 277 } 278 279 uint8_t obex_message_builder_header_add_unicode_prefix(uint8_t * buffer, uint16_t buffer_len, uint8_t header_id, const char * name, uint16_t name_len){ 280 // non-empty string have trailing \0 281 bool add_trailing_zero = name_len > 0; 282 283 uint16_t header_len = obex_message_builder_get_header_name_len_from_strlen(name_len); 284 if (buffer_len < header_len) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 285 286 uint16_t pos = big_endian_read_16(buffer, 1); 287 buffer[pos++] = header_id; 288 big_endian_store_16(buffer, pos, header_len); 289 pos += 2; 290 int i; 291 // @note name[len] == 0 292 for (i = 0 ; i < name_len ; i++){ 293 buffer[pos++] = 0; 294 buffer[pos++] = *name++; 295 } 296 if (add_trailing_zero){ 297 buffer[pos++] = 0; 298 buffer[pos++] = 0; 299 } 300 big_endian_store_16(buffer, 1, pos); 301 return ERROR_CODE_SUCCESS; 302 } 303 304 uint8_t obex_message_builder_header_add_name_prefix(uint8_t * buffer, uint16_t buffer_len, const char * name, uint16_t name_len){ 305 return obex_message_builder_header_add_unicode_prefix(buffer, buffer_len, OBEX_HEADER_NAME, name, name_len); 306 } 307 308 uint8_t obex_message_builder_header_add_name(uint8_t * buffer, uint16_t buffer_len, const char * name){ 309 uint16_t name_len = (uint16_t) strlen(name); 310 return obex_message_builder_header_add_unicode_prefix(buffer, buffer_len, OBEX_HEADER_NAME, name, name_len); 311 } 312 313 uint8_t obex_message_builder_get_header_type_len(char * type) { 314 if (type == NULL) 315 return 0; // type_header is ommited 316 317 return (uint8_t) ( 1 // Header Encoding + ID 318 + 2 // Length 319 + strlen(type) // length of string in bytes 320 + 1); // trailing \0 321 } 322 323 uint8_t obex_message_builder_header_add_type(uint8_t * buffer, uint16_t buffer_len, const char * type){ 324 uint8_t header[3]; 325 header[0] = OBEX_HEADER_TYPE; 326 int len_incl_zero = (uint16_t) strlen(type) + 1; 327 big_endian_store_16(header, 1, 1 + 2 + len_incl_zero); 328 329 uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header)); 330 if (status != ERROR_CODE_SUCCESS) return status; 331 return obex_message_builder_packet_append(buffer, buffer_len, (const uint8_t*)type, len_incl_zero); 332 } 333 334 uint8_t obex_message_builder_header_add_length(uint8_t * buffer, uint16_t buffer_len, uint32_t length){ 335 return obex_message_builder_header_add_word(buffer, buffer_len, OBEX_HEADER_LENGTH, length); 336 } 337