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
obex_message_builder_packet_init(uint8_t * buffer,uint16_t buffer_len,uint8_t opcode_or_response_code)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
obex_message_builder_packet_append(uint8_t * buffer,uint16_t buffer_len,const uint8_t * data,uint16_t len)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
obex_message_builder_get_message_length(uint8_t * buffer)100 uint16_t obex_message_builder_get_message_length(uint8_t * buffer){
101 return big_endian_read_16(buffer, 1);
102 }
103
obex_message_builder_header_add_byte(uint8_t * buffer,uint16_t buffer_len,uint8_t header_type,uint8_t value)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
obex_message_builder_header_add_word(uint8_t * buffer,uint16_t buffer_len,uint8_t header_type,uint32_t value)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
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)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
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)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
obex_message_builder_header_add_connection_id(uint8_t * buffer,uint16_t buffer_len,uint32_t obex_connection_id)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
obex_message_builder_create_connect(uint8_t * buffer,uint16_t buffer_len,uint8_t opcode,uint8_t obex_version_number,uint8_t flags,uint16_t maximum_obex_packet_length)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
obex_message_builder_request_create_connect(uint8_t * buffer,uint16_t buffer_len,uint8_t obex_version_number,uint8_t flags,uint16_t maximum_obex_packet_length)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
obex_message_builder_response_create_connect(uint8_t * buffer,uint16_t buffer_len,uint8_t obex_version_number,uint8_t flags,uint16_t maximum_obex_packet_length,uint32_t obex_connection_id)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
obex_message_builder_response_create_general(uint8_t * buffer,uint16_t buffer_len,uint8_t response_code)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
obex_message_builder_response_update_code(uint8_t * buffer,uint16_t buffer_len,uint8_t response_code)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
obex_message_builder_request_create_get(uint8_t * buffer,uint16_t buffer_len,uint32_t obex_connection_id)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
obex_message_builder_request_create_put(uint8_t * buffer,uint16_t buffer_len,uint32_t obex_connection_id)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
obex_message_builder_request_create_set_path(uint8_t * buffer,uint16_t buffer_len,uint8_t flags,uint32_t obex_connection_id)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
obex_message_builder_request_create_abort(uint8_t * buffer,uint16_t buffer_len,uint32_t obex_connection_id)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
obex_message_builder_request_create_disconnect(uint8_t * buffer,uint16_t buffer_len,uint32_t obex_connection_id)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
obex_message_builder_set_final_bit(uint8_t * buffer,uint16_t buffer_len,bool final)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
obex_message_builder_header_add_srm_enable(uint8_t * buffer,uint16_t buffer_len)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
obex_message_builder_header_add_srmp_wait(uint8_t * buffer,uint16_t buffer_len)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
obex_message_builder_header_add_target(uint8_t * buffer,uint16_t buffer_len,const uint8_t * target,uint16_t length)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
obex_message_builder_header_add_application_parameters(uint8_t * buffer,uint16_t buffer_len,const uint8_t * data,uint16_t length)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
obex_message_builder_header_add_challenge_response(uint8_t * buffer,uint16_t buffer_len,const uint8_t * data,uint16_t length)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
obex_message_builder_header_add_who(uint8_t * buffer,uint16_t buffer_len,const uint8_t * who)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
obex_message_builder_body_add_static(uint8_t * buffer,uint16_t buffer_len,const uint8_t * data,uint32_t length)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
obex_message_builder_body_fillup_static(uint8_t * buffer,uint16_t buffer_len,const uint8_t * data,uint32_t length,uint32_t * ret_length)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
obex_message_builder_get_header_name_len_from_strlen(uint16_t name_len)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
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)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
obex_message_builder_header_add_name_prefix(uint8_t * buffer,uint16_t buffer_len,const char * name,uint16_t name_len)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
obex_message_builder_header_add_name(uint8_t * buffer,uint16_t buffer_len,const char * name)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
obex_message_builder_get_header_type_len(char * type)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
obex_message_builder_header_add_type(uint8_t * buffer,uint16_t buffer_len,const char * type)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
obex_message_builder_header_add_length(uint8_t * buffer,uint16_t buffer_len,uint32_t length)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