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 static uint8_t obex_message_builder_packet_init(uint8_t * buffer, uint16_t buffer_len, uint8_t opcode_or_response_code){ 51 if (buffer_len < 3) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 52 buffer[0] = opcode_or_response_code; 53 big_endian_store_16(buffer, 1, 3); 54 return ERROR_CODE_SUCCESS; 55 } 56 57 static uint8_t obex_message_builder_packet_append(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t len){ 58 uint16_t pos = big_endian_read_16(buffer, 1); 59 if (buffer_len < pos + len) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 60 (void)memcpy(&buffer[pos], data, len); 61 pos += len; 62 big_endian_store_16(buffer, 1, pos); 63 return ERROR_CODE_SUCCESS; 64 } 65 66 uint16_t obex_message_builder_get_message_length(uint8_t * buffer){ 67 return big_endian_read_16(buffer, 1); 68 } 69 70 uint8_t obex_message_builder_header_add_byte(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, uint8_t value){ 71 uint8_t header[2]; 72 header[0] = header_type; 73 header[1] = value; 74 return obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header)); 75 } 76 77 uint8_t obex_message_builder_header_add_word(uint8_t * buffer, uint16_t buffer_len, uint8_t header_type, uint32_t value){ 78 uint8_t header[5]; 79 header[0] = header_type; 80 big_endian_store_32(header, 1, value); 81 return obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header)); 82 } 83 84 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){ 85 uint8_t header[3]; 86 header[0] = header_type; 87 big_endian_store_16(header, 1, sizeof(header) + header_data_length); 88 89 uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header)); 90 if (status != ERROR_CODE_SUCCESS) return status; 91 92 return obex_message_builder_packet_append(buffer, buffer_len, header_data, header_data_length); 93 } 94 95 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){ 96 uint8_t header[3]; 97 header[0] = header_type; 98 uint16_t pos = big_endian_read_16(buffer, 1); 99 100 if (sizeof (header) + header_data_length + pos > buffer_len) 101 header_data_length = buffer_len - pos - sizeof (header); 102 103 big_endian_store_16(header, 1, sizeof(header) + header_data_length); 104 *ret_length = 0; 105 uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header)); 106 if (status != ERROR_CODE_SUCCESS) return status; 107 108 *ret_length = header_data_length; 109 return obex_message_builder_packet_append(buffer, buffer_len, header_data, header_data_length); 110 } 111 112 static uint8_t obex_message_builder_header_add_connection_id(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){ 113 // add connection_id header if set, must be first header if used 114 if (obex_connection_id == OBEX_CONNECTION_ID_INVALID) return ERROR_CODE_PARAMETER_OUT_OF_MANDATORY_RANGE; 115 return obex_message_builder_header_add_word(buffer, buffer_len, OBEX_HEADER_CONNECTION_ID, obex_connection_id); 116 } 117 118 static inline uint8_t obex_message_builder_create_connect(uint8_t * buffer, uint16_t buffer_len, uint8_t opcode, 119 uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){ 120 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, opcode); 121 if (status != ERROR_CODE_SUCCESS) return status; 122 123 uint8_t fields[4]; 124 fields[0] = obex_version_number; 125 fields[1] = flags; 126 big_endian_store_16(fields, 2, maximum_obex_packet_length); 127 return obex_message_builder_packet_append(buffer, buffer_len, &fields[0], sizeof(fields)); 128 } 129 130 uint8_t obex_message_builder_request_create_connect(uint8_t * buffer, uint16_t buffer_len, 131 uint8_t obex_version_number, uint8_t flags, uint16_t maximum_obex_packet_length){ 132 133 return obex_message_builder_create_connect(buffer, buffer_len, OBEX_OPCODE_CONNECT, obex_version_number, flags, maximum_obex_packet_length); 134 } 135 136 uint8_t obex_message_builder_response_create_connect(uint8_t * buffer, uint16_t buffer_len, uint8_t obex_version_number, 137 uint8_t flags, uint16_t maximum_obex_packet_length, uint32_t obex_connection_id){ 138 139 uint8_t status = obex_message_builder_create_connect(buffer, buffer_len, OBEX_RESP_SUCCESS, obex_version_number, flags, maximum_obex_packet_length); 140 if (status != ERROR_CODE_SUCCESS) return status; 141 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 142 } 143 144 uint8_t obex_message_builder_response_create_general(uint8_t * buffer, uint16_t buffer_len, uint8_t response_code){ 145 return obex_message_builder_packet_init(buffer, buffer_len, response_code); 146 } 147 148 uint8_t obex_message_builder_response_update_code(uint8_t * buffer, uint16_t buffer_len, uint8_t response_code){ 149 if (buffer_len < 3) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 150 buffer[0] = response_code; 151 return ERROR_CODE_SUCCESS; 152 } 153 154 uint8_t obex_message_builder_request_create_get(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){ 155 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_GET | OBEX_OPCODE_FINAL_BIT_MASK); 156 if (status != ERROR_CODE_SUCCESS) return status; 157 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 158 } 159 160 uint8_t obex_message_builder_request_create_put(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){ 161 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_PUT | OBEX_OPCODE_FINAL_BIT_MASK); 162 if (status != ERROR_CODE_SUCCESS) return status; 163 164 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 165 } 166 167 uint8_t obex_message_builder_request_create_set_path(uint8_t * buffer, uint16_t buffer_len, uint8_t flags, uint32_t obex_connection_id){ 168 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_SETPATH); 169 if (status != ERROR_CODE_SUCCESS) return status; 170 171 uint8_t fields[2]; 172 fields[0] = flags; 173 fields[1] = 0; // reserved 174 status = obex_message_builder_packet_append(buffer, buffer_len, &fields[0], sizeof(fields)); 175 if (status != ERROR_CODE_SUCCESS) return status; 176 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 177 } 178 179 uint8_t obex_message_builder_request_create_abort(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){ 180 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_ABORT); 181 if (status != ERROR_CODE_SUCCESS) return status; 182 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 183 } 184 185 uint8_t obex_message_builder_request_create_disconnect(uint8_t * buffer, uint16_t buffer_len, uint32_t obex_connection_id){ 186 uint8_t status = obex_message_builder_packet_init(buffer, buffer_len, OBEX_OPCODE_DISCONNECT); 187 if (status != ERROR_CODE_SUCCESS) return status; 188 return obex_message_builder_header_add_connection_id(buffer, buffer_len, obex_connection_id); 189 } 190 191 uint8_t obex_message_builder_set_final_bit (uint8_t * buffer, uint16_t buffer_len, bool final){ 192 if (buffer_len < 1) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 193 if (buffer[0] == OBEX_OPCODE_CONNECT || 194 buffer[0] == OBEX_OPCODE_DISCONNECT || 195 buffer[0] == OBEX_OPCODE_SETPATH || 196 buffer[0] == OBEX_OPCODE_SESSION || 197 buffer[0] == OBEX_OPCODE_ABORT){ 198 return ERROR_CODE_COMMAND_DISALLOWED; 199 } 200 buffer[0] &= ~OBEX_OPCODE_FINAL_BIT_MASK; 201 buffer[0] |= (final ? OBEX_OPCODE_FINAL_BIT_MASK : 0); 202 return ERROR_CODE_SUCCESS; 203 } 204 205 uint8_t obex_message_builder_header_add_srm_enable(uint8_t * buffer, uint16_t buffer_len){ 206 return obex_message_builder_header_add_byte(buffer, buffer_len, OBEX_HEADER_SINGLE_RESPONSE_MODE, OBEX_SRM_ENABLE); 207 } 208 209 uint8_t obex_message_builder_header_add_target(uint8_t * buffer, uint16_t buffer_len, const uint8_t * target, uint16_t length){ 210 return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_TARGET, target, length); 211 } 212 213 uint8_t obex_message_builder_header_add_application_parameters(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t length){ 214 return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_APPLICATION_PARAMETERS, data, length); 215 } 216 217 uint8_t obex_message_builder_header_add_challenge_response(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint16_t length){ 218 return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_AUTHENTICATION_RESPONSE, data, length); 219 } 220 221 uint8_t obex_message_builder_header_add_who(uint8_t * buffer, uint16_t buffer_len, const uint8_t * who){ 222 return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_WHO, who, 16); 223 } 224 225 uint8_t obex_message_builder_body_add_static(uint8_t * buffer, uint16_t buffer_len, const uint8_t * data, uint32_t length){ 226 return obex_message_builder_header_add_variable(buffer, buffer_len, OBEX_HEADER_END_OF_BODY, data, length); 227 } 228 229 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){ 230 return obex_message_builder_header_fillup_variable(buffer, buffer_len, OBEX_HEADER_END_OF_BODY, data, length, ret_length); 231 } 232 233 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){ 234 // non-empty string have trailing \0 235 bool add_trailing_zero = name_len > 0; 236 237 uint16_t header_len = 1 + 2 + (name_len * 2) + (add_trailing_zero ? 2 : 0); 238 if (buffer_len < header_len) return ERROR_CODE_MEMORY_CAPACITY_EXCEEDED; 239 240 uint16_t pos = big_endian_read_16(buffer, 1); 241 buffer[pos++] = header_id; 242 big_endian_store_16(buffer, pos, header_len); 243 pos += 2; 244 int i; 245 // @note name[len] == 0 246 for (i = 0 ; i < name_len ; i++){ 247 buffer[pos++] = 0; 248 buffer[pos++] = *name++; 249 } 250 if (add_trailing_zero){ 251 buffer[pos++] = 0; 252 buffer[pos++] = 0; 253 } 254 big_endian_store_16(buffer, 1, pos); 255 return ERROR_CODE_SUCCESS; 256 } 257 258 uint8_t obex_message_builder_header_add_name_prefix(uint8_t * buffer, uint16_t buffer_len, const char * name, uint16_t name_len){ 259 return obex_message_builder_header_add_unicode_prefix(buffer, buffer_len, OBEX_HEADER_NAME, name, name_len); 260 } 261 262 uint8_t obex_message_builder_header_add_name(uint8_t * buffer, uint16_t buffer_len, const char * name){ 263 uint16_t name_len = (uint16_t) strlen(name); 264 return obex_message_builder_header_add_unicode_prefix(buffer, buffer_len, OBEX_HEADER_NAME, name, name_len); 265 } 266 267 uint8_t obex_message_builder_header_add_type(uint8_t * buffer, uint16_t buffer_len, const char * type){ 268 uint8_t header[3]; 269 header[0] = OBEX_HEADER_TYPE; 270 int len_incl_zero = (uint16_t) strlen(type) + 1; 271 big_endian_store_16(header, 1, 1 + 2 + len_incl_zero); 272 273 uint8_t status = obex_message_builder_packet_append(buffer, buffer_len, &header[0], sizeof(header)); 274 if (status != ERROR_CODE_SUCCESS) return status; 275 return obex_message_builder_packet_append(buffer, buffer_len, (const uint8_t*)type, len_incl_zero); 276 } 277 278 uint8_t obex_message_builder_header_add_length(uint8_t * buffer, uint16_t buffer_len, uint32_t length){ 279 return obex_message_builder_header_add_word(buffer, buffer_len, OBEX_HEADER_LENGTH, length); 280 } 281