/src/libcoap/src/coap_pdu.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* coap_pdu.c -- CoAP PDU handling |
2 | | * |
3 | | * Copyright (C) 2010--2024 Olaf Bergmann <bergmann@tzi.org> |
4 | | * |
5 | | * SPDX-License-Identifier: BSD-2-Clause |
6 | | * |
7 | | * This file is part of the CoAP library libcoap. Please see |
8 | | * README for terms of use. |
9 | | */ |
10 | | |
11 | | /** |
12 | | * @file coap_pdu.c |
13 | | * @brief CoAP PDU handling |
14 | | */ |
15 | | |
16 | | #include "coap3/coap_libcoap_build.h" |
17 | | |
18 | | #if defined(HAVE_LIMITS_H) |
19 | | #include <limits.h> |
20 | | #endif |
21 | | |
22 | | #include <stdlib.h> |
23 | | #include <stdio.h> |
24 | | #include <string.h> |
25 | | #ifdef HAVE_ARPA_INET_H |
26 | | #include <arpa/inet.h> |
27 | | #endif |
28 | | #ifdef HAVE_WINSOCK2_H |
29 | | #include <winsock2.h> |
30 | | #endif |
31 | | #include <ctype.h> |
32 | | |
33 | | #ifndef min |
34 | 10.9k | #define min(a,b) ((a) < (b) ? (a) : (b)) |
35 | | #endif |
36 | | |
37 | | #ifndef max |
38 | 0 | #define max(a,b) ((a) > (b) ? (a) : (b)) |
39 | | #endif |
40 | | |
41 | | void |
42 | 10.9k | coap_pdu_clear(coap_pdu_t *pdu, size_t size) { |
43 | 10.9k | assert(pdu); |
44 | 10.9k | assert(pdu->token); |
45 | 10.9k | assert(pdu->max_hdr_size >= COAP_PDU_MAX_UDP_HEADER_SIZE); |
46 | 10.9k | if (pdu->alloc_size > size) |
47 | 0 | pdu->alloc_size = size; |
48 | 10.9k | pdu->type = 0; |
49 | 10.9k | pdu->code = 0; |
50 | 10.9k | pdu->ref = 0; |
51 | 10.9k | pdu->hdr_size = 0; |
52 | 10.9k | pdu->actual_token.length = 0; |
53 | 10.9k | pdu->e_token_length = 0; |
54 | 10.9k | pdu->crit_opt = 0; |
55 | 10.9k | pdu->mid = 0; |
56 | 10.9k | pdu->max_opt = 0; |
57 | 10.9k | pdu->max_size = size; |
58 | 10.9k | pdu->used_size = 0; |
59 | 10.9k | pdu->data = NULL; |
60 | 10.9k | pdu->body_data = NULL; |
61 | 10.9k | pdu->body_length = 0; |
62 | 10.9k | pdu->body_offset = 0; |
63 | 10.9k | pdu->body_total = 0; |
64 | 10.9k | pdu->lg_xmit = NULL; |
65 | 10.9k | pdu->session = NULL; |
66 | 10.9k | pdu->data_free = NULL; |
67 | 10.9k | } |
68 | | |
69 | | #ifdef WITH_LWIP |
70 | | coap_pdu_t * |
71 | | coap_pdu_from_pbuf(struct pbuf *pbuf) { |
72 | | coap_pdu_t *pdu; |
73 | | |
74 | | if (pbuf == NULL) |
75 | | return NULL; |
76 | | |
77 | | LWIP_ASSERT("Can only deal with contiguous PBUFs (increase PBUF_POOL_BUFSIZE)", |
78 | | pbuf->tot_len == pbuf->len); |
79 | | LWIP_ASSERT("coap_io_do_io needs to receive an exclusive copy of the incoming pbuf", |
80 | | pbuf->ref == 1); |
81 | | |
82 | | pdu = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t)); |
83 | | if (!pdu) { |
84 | | pbuf_free(pbuf); |
85 | | return NULL; |
86 | | } |
87 | | |
88 | | pdu->max_hdr_size = COAP_PDU_MAX_UDP_HEADER_SIZE; |
89 | | pdu->pbuf = pbuf; |
90 | | pdu->token = (uint8_t *)pbuf->payload + pdu->max_hdr_size; |
91 | | pdu->alloc_size = pbuf->tot_len - pdu->max_hdr_size; |
92 | | coap_pdu_clear(pdu, pdu->alloc_size); |
93 | | |
94 | | return pdu; |
95 | | } |
96 | | #endif /* LWIP */ |
97 | | |
98 | | coap_pdu_t * |
99 | | coap_pdu_init(coap_pdu_type_t type, coap_pdu_code_t code, coap_mid_t mid, |
100 | 10.9k | size_t size) { |
101 | 10.9k | coap_pdu_t *pdu; |
102 | | |
103 | 10.9k | #ifndef RIOT_VERSION |
104 | 10.9k | assert(type <= 0x3); |
105 | 10.9k | assert(code <= 0xff); |
106 | 10.9k | assert(mid >= 0 && mid <= 0xffff); |
107 | 10.9k | #endif /* RIOT_VERSION */ |
108 | | |
109 | | #ifdef WITH_LWIP |
110 | | #if MEMP_STATS |
111 | | /* Reserve 1 PDU for a response packet */ |
112 | | if (memp_pools[MEMP_COAP_PDU]->stats->used + 1 >= |
113 | | memp_pools[MEMP_COAP_PDU]->stats->avail) { |
114 | | memp_pools[MEMP_COAP_PDU]->stats->err++; |
115 | | return NULL; |
116 | | } |
117 | | #endif /* MEMP_STATS */ |
118 | | #endif /* LWIP */ |
119 | 10.9k | pdu = coap_malloc_type(COAP_PDU, sizeof(coap_pdu_t)); |
120 | 10.9k | if (!pdu) |
121 | 0 | return NULL; |
122 | | |
123 | | #if COAP_DEFAULT_MAX_PDU_RX_SIZE <= COAP_MAX_MESSAGE_SIZE_TCP16 |
124 | | /* on TCP, the CoAP header will also have a maximum length of 4 bytes */ |
125 | | pdu->max_hdr_size = COAP_PDU_MAX_UDP_HEADER_SIZE; |
126 | | #else |
127 | 10.9k | pdu->max_hdr_size = COAP_PDU_MAX_TCP_HEADER_SIZE; |
128 | 10.9k | #endif |
129 | 10.9k | if (size > ((size_t)COAP_DEFAULT_MAX_PDU_RX_SIZE - pdu->max_hdr_size)) { |
130 | 0 | coap_free_type(COAP_PDU, pdu); |
131 | 0 | return NULL; |
132 | 0 | } |
133 | | |
134 | | #ifdef WITH_LWIP |
135 | | pdu->pbuf = pbuf_alloc(PBUF_TRANSPORT, size + pdu->max_hdr_size, PBUF_RAM); |
136 | | if (pdu->pbuf == NULL) { |
137 | | coap_free_type(COAP_PDU, pdu); |
138 | | return NULL; |
139 | | } |
140 | | pdu->token = (uint8_t *)pdu->pbuf->payload + pdu->max_hdr_size; |
141 | | #else /* WITH_LWIP */ |
142 | 10.9k | uint8_t *buf; |
143 | 10.9k | pdu->alloc_size = min(size, 256); |
144 | 10.9k | buf = coap_malloc_type(COAP_PDU_BUF, pdu->alloc_size + pdu->max_hdr_size); |
145 | 10.9k | if (buf == NULL) { |
146 | 0 | coap_free_type(COAP_PDU, pdu); |
147 | 0 | return NULL; |
148 | 0 | } |
149 | 10.9k | pdu->token = buf + pdu->max_hdr_size; |
150 | 10.9k | #endif /* WITH_LWIP */ |
151 | 10.9k | coap_pdu_clear(pdu, size); |
152 | 10.9k | pdu->mid = mid; |
153 | 10.9k | pdu->type = type; |
154 | 10.9k | pdu->code = code; |
155 | 10.9k | return pdu; |
156 | 10.9k | } |
157 | | |
158 | | COAP_API coap_pdu_t * |
159 | | coap_new_pdu(coap_pdu_type_t type, coap_pdu_code_t code, |
160 | 0 | coap_session_t *session) { |
161 | 0 | coap_pdu_t *pdu; |
162 | |
|
163 | 0 | coap_lock_lock(session->context, return NULL); |
164 | 0 | pdu = coap_new_pdu_lkd(type, code, session); |
165 | 0 | coap_lock_unlock(session->context); |
166 | 0 | return pdu; |
167 | 0 | } |
168 | | |
169 | | coap_pdu_t * |
170 | | coap_new_pdu_lkd(coap_pdu_type_t type, coap_pdu_code_t code, |
171 | 0 | coap_session_t *session) { |
172 | 0 | coap_pdu_t *pdu; |
173 | |
|
174 | 0 | coap_lock_check_locked(session->context); |
175 | 0 | pdu = coap_pdu_init(type, code, coap_new_message_id_lkd(session), |
176 | 0 | coap_session_max_pdu_size_lkd(session)); |
177 | 0 | if (!pdu) |
178 | 0 | coap_log_crit("coap_new_pdu: cannot allocate memory for new PDU\n"); |
179 | 0 | return pdu; |
180 | 0 | } |
181 | | |
182 | | COAP_API void |
183 | 10.9k | coap_delete_pdu(coap_pdu_t *pdu) { |
184 | 10.9k | coap_lock_lock(NULL, return); |
185 | 10.9k | coap_delete_pdu_lkd(pdu); |
186 | 10.9k | coap_lock_unlock(NULL); |
187 | 10.9k | } |
188 | | |
189 | | void |
190 | 10.9k | coap_delete_pdu_lkd(coap_pdu_t *pdu) { |
191 | 10.9k | if (pdu != NULL) { |
192 | 10.9k | if (pdu->ref) { |
193 | 0 | pdu->ref--; |
194 | 0 | return; |
195 | 0 | } |
196 | | #ifdef WITH_LWIP |
197 | | pbuf_free(pdu->pbuf); |
198 | | #else |
199 | 10.9k | if (pdu->token != NULL) |
200 | 10.9k | coap_free_type(COAP_PDU_BUF, pdu->token - pdu->max_hdr_size); |
201 | 10.9k | #endif |
202 | 10.9k | coap_delete_binary(pdu->data_free); |
203 | 10.9k | coap_free_type(COAP_PDU, pdu); |
204 | 10.9k | } |
205 | 10.9k | } |
206 | | |
207 | | COAP_API coap_pdu_t * |
208 | | coap_pdu_duplicate(const coap_pdu_t *old_pdu, |
209 | | coap_session_t *session, |
210 | | size_t token_length, |
211 | | const uint8_t *token, |
212 | 0 | coap_opt_filter_t *drop_options) { |
213 | 0 | coap_pdu_t *new_pdu; |
214 | |
|
215 | 0 | coap_lock_lock(session->context, return NULL); |
216 | 0 | new_pdu = coap_pdu_duplicate_lkd(old_pdu, |
217 | 0 | session, |
218 | 0 | token_length, |
219 | 0 | token, |
220 | 0 | drop_options); |
221 | 0 | coap_lock_unlock(session->context); |
222 | 0 | return new_pdu; |
223 | 0 | } |
224 | | |
225 | | |
226 | | /* |
227 | | * Note: This does not include any data, just the token and options |
228 | | */ |
229 | | coap_pdu_t * |
230 | | coap_pdu_duplicate_lkd(const coap_pdu_t *old_pdu, |
231 | | coap_session_t *session, |
232 | | size_t token_length, |
233 | | const uint8_t *token, |
234 | 0 | coap_opt_filter_t *drop_options) { |
235 | 0 | uint8_t doing_first = session->doing_first; |
236 | 0 | coap_pdu_t *pdu; |
237 | |
|
238 | 0 | coap_lock_check_locked(session->context); |
239 | | /* |
240 | | * Need to make sure that coap_session_max_pdu_size_lkd() immediately |
241 | | * returns, rather than wait for the first CSM response from remote |
242 | | * that indicates BERT size (TCP/TLS only) as this may be called early |
243 | | * the OSCORE logic. |
244 | | */ |
245 | 0 | session->doing_first = 0; |
246 | 0 | pdu = coap_pdu_init(old_pdu->type, old_pdu->code, |
247 | 0 | coap_new_message_id_lkd(session), |
248 | 0 | max(old_pdu->max_size, |
249 | 0 | coap_session_max_pdu_size_lkd(session))); |
250 | | /* Restore any pending waits */ |
251 | 0 | session->doing_first = doing_first; |
252 | 0 | if (pdu == NULL) |
253 | 0 | return NULL; |
254 | | |
255 | 0 | coap_add_token(pdu, token_length, token); |
256 | 0 | pdu->lg_xmit = old_pdu->lg_xmit; |
257 | |
|
258 | 0 | if (drop_options == NULL) { |
259 | | /* Drop COAP_PAYLOAD_START as well if data */ |
260 | 0 | size_t length = old_pdu->used_size - old_pdu->e_token_length - |
261 | 0 | (old_pdu->data ? |
262 | 0 | old_pdu->used_size - (old_pdu->data - old_pdu->token) +1 : 0); |
263 | 0 | if (!coap_pdu_resize(pdu, length + pdu->e_token_length)) |
264 | 0 | goto fail; |
265 | | /* Copy the options but not any data across */ |
266 | 0 | memcpy(pdu->token + pdu->e_token_length, |
267 | 0 | old_pdu->token + old_pdu->e_token_length, length); |
268 | 0 | pdu->used_size += length; |
269 | 0 | pdu->max_opt = old_pdu->max_opt; |
270 | 0 | } else { |
271 | | /* Copy across all the options the slow way */ |
272 | 0 | coap_opt_iterator_t opt_iter; |
273 | 0 | coap_opt_t *option; |
274 | |
|
275 | 0 | coap_option_iterator_init(old_pdu, &opt_iter, COAP_OPT_ALL); |
276 | 0 | while ((option = coap_option_next(&opt_iter))) { |
277 | 0 | if (drop_options && coap_option_filter_get(drop_options, opt_iter.number)) |
278 | 0 | continue; |
279 | 0 | if (!coap_add_option_internal(pdu, opt_iter.number, |
280 | 0 | coap_opt_length(option), |
281 | 0 | coap_opt_value(option))) |
282 | 0 | goto fail; |
283 | 0 | } |
284 | 0 | } |
285 | 0 | return pdu; |
286 | | |
287 | 0 | fail: |
288 | 0 | coap_delete_pdu_lkd(pdu); |
289 | 0 | return NULL; |
290 | 0 | } |
291 | | |
292 | | |
293 | | /* |
294 | | * The new size does not include the coap header (max_hdr_size) |
295 | | */ |
296 | | int |
297 | 10.9k | coap_pdu_resize(coap_pdu_t *pdu, size_t new_size) { |
298 | 10.9k | if (new_size > pdu->alloc_size) { |
299 | 2.91k | #if !defined(WITH_LWIP) |
300 | 2.91k | uint8_t *new_hdr; |
301 | 2.91k | size_t offset; |
302 | 2.91k | #endif |
303 | 2.91k | if (pdu->max_size && new_size > pdu->max_size) { |
304 | 0 | coap_log_warn("coap_pdu_resize: pdu too big\n"); |
305 | 0 | return 0; |
306 | 0 | } |
307 | 2.91k | #if !defined(WITH_LWIP) |
308 | 2.91k | if (pdu->data != NULL) { |
309 | 0 | assert(pdu->data > pdu->token); |
310 | 0 | offset = pdu->data - pdu->token; |
311 | 2.91k | } else { |
312 | 2.91k | offset = 0; |
313 | 2.91k | } |
314 | 2.91k | new_hdr = (uint8_t *)coap_realloc_type(COAP_PDU_BUF, |
315 | 2.91k | pdu->token - pdu->max_hdr_size, |
316 | 2.91k | new_size + pdu->max_hdr_size); |
317 | 2.91k | if (new_hdr == NULL) { |
318 | 0 | coap_log_warn("coap_pdu_resize: realloc failed\n"); |
319 | 0 | return 0; |
320 | 0 | } |
321 | 2.91k | pdu->token = new_hdr + pdu->max_hdr_size; |
322 | 2.91k | if (offset > 0) |
323 | 0 | pdu->data = pdu->token + offset; |
324 | 2.91k | else |
325 | 2.91k | pdu->data = NULL; |
326 | 2.91k | if (pdu->actual_token.length < COAP_TOKEN_EXT_1B_BIAS) |
327 | 2.91k | pdu->actual_token.s = &pdu->token[0]; |
328 | 0 | else if (pdu->actual_token.length < COAP_TOKEN_EXT_2B_BIAS) |
329 | 0 | pdu->actual_token.s = &pdu->token[1]; |
330 | 0 | else |
331 | 0 | pdu->actual_token.s = &pdu->token[2]; |
332 | 2.91k | #endif |
333 | 2.91k | } |
334 | 10.9k | pdu->alloc_size = new_size; |
335 | 10.9k | return 1; |
336 | 10.9k | } |
337 | | |
338 | | int |
339 | 0 | coap_pdu_check_resize(coap_pdu_t *pdu, size_t size) { |
340 | 0 | if (size > pdu->alloc_size) { |
341 | 0 | size_t new_size = max(256, pdu->alloc_size * 2); |
342 | 0 | while (size > new_size) |
343 | 0 | new_size *= 2; |
344 | 0 | if (pdu->max_size && new_size > pdu->max_size) { |
345 | 0 | new_size = pdu->max_size; |
346 | 0 | if (new_size < size) |
347 | 0 | return 0; |
348 | 0 | } |
349 | 0 | if (!coap_pdu_resize(pdu, new_size)) |
350 | 0 | return 0; |
351 | 0 | } |
352 | 0 | return 1; |
353 | 0 | } |
354 | | |
355 | | int |
356 | 0 | coap_add_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) { |
357 | 0 | size_t bias = 0; |
358 | | |
359 | | /* must allow for pdu == NULL as callers may rely on this */ |
360 | 0 | if (!pdu) |
361 | 0 | return 0; |
362 | | |
363 | 0 | if (pdu->used_size) { |
364 | 0 | coap_log_warn("coap_add_token: The token must defined first. Token ignored\n"); |
365 | 0 | return 0; |
366 | 0 | } |
367 | 0 | pdu->actual_token.length = len; |
368 | 0 | if (len < COAP_TOKEN_EXT_1B_BIAS) { |
369 | 0 | bias = 0; |
370 | 0 | } else if (len < COAP_TOKEN_EXT_2B_BIAS) { |
371 | 0 | bias = 1; |
372 | 0 | } else if (len <= COAP_TOKEN_EXT_MAX) { |
373 | 0 | bias = 2; |
374 | 0 | } else { |
375 | 0 | coap_log_warn("coap_add_token: Token size too large. Token ignored\n"); |
376 | 0 | return 0; |
377 | 0 | } |
378 | 0 | if (!coap_pdu_check_resize(pdu, len + bias)) { |
379 | 0 | coap_log_warn("coap_add_token: Insufficient space for token. Token ignored\n"); |
380 | 0 | return 0; |
381 | 0 | } |
382 | | |
383 | 0 | pdu->actual_token.length = len; |
384 | 0 | pdu->actual_token.s = &pdu->token[bias]; |
385 | 0 | pdu->e_token_length = (uint32_t)(len + bias); |
386 | 0 | if (len) { |
387 | 0 | switch (bias) { |
388 | 0 | case 0: |
389 | 0 | memcpy(pdu->token, data, len); |
390 | 0 | break; |
391 | 0 | case 1: |
392 | 0 | pdu->token[0] = (uint8_t)(len - COAP_TOKEN_EXT_1B_BIAS); |
393 | 0 | memcpy(&pdu->token[1], data, len); |
394 | 0 | break; |
395 | 0 | case 2: |
396 | 0 | pdu->token[0] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) >> 8); |
397 | 0 | pdu->token[1] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) & 0xff); |
398 | 0 | memcpy(&pdu->token[2], data, len); |
399 | 0 | break; |
400 | 0 | default: |
401 | 0 | break; |
402 | 0 | } |
403 | 0 | } |
404 | 0 | pdu->max_opt = 0; |
405 | 0 | pdu->used_size = len + bias; |
406 | 0 | pdu->data = NULL; |
407 | |
|
408 | 0 | return 1; |
409 | 0 | } |
410 | | |
411 | | /* It is assumed that coap_encode_var_safe8() has been called to reduce data */ |
412 | | int |
413 | 0 | coap_update_token(coap_pdu_t *pdu, size_t len, const uint8_t *data) { |
414 | 0 | size_t bias = 0; |
415 | 0 | size_t old_len; |
416 | | |
417 | | /* must allow for pdu == NULL as callers may rely on this */ |
418 | 0 | if (!pdu) |
419 | 0 | return 0; |
420 | | |
421 | 0 | if (pdu->used_size == 0) { |
422 | 0 | return coap_add_token(pdu, len, data); |
423 | 0 | } |
424 | | |
425 | 0 | old_len = pdu->e_token_length; |
426 | |
|
427 | 0 | if (len < COAP_TOKEN_EXT_1B_BIAS) { |
428 | 0 | bias = 0; |
429 | 0 | } else if (len < COAP_TOKEN_EXT_2B_BIAS) { |
430 | 0 | bias = 1; |
431 | 0 | } else if (len <= COAP_TOKEN_EXT_MAX) { |
432 | 0 | bias = 2; |
433 | 0 | } else { |
434 | 0 | coap_log_warn("coap_add_token: Token size too large. Token ignored\n"); |
435 | 0 | return 0; |
436 | 0 | } |
437 | 0 | if ((len + bias) == pdu->e_token_length) { |
438 | | /* Easy case - just data has changed */ |
439 | 0 | } else if ((len + bias) > pdu->e_token_length) { |
440 | 0 | if (!coap_pdu_check_resize(pdu, |
441 | 0 | pdu->used_size + (len + bias) - pdu->e_token_length)) { |
442 | 0 | coap_log_warn("Failed to update token\n"); |
443 | 0 | return 0; |
444 | 0 | } |
445 | 0 | memmove(&pdu->token[(len + bias) - pdu->e_token_length], |
446 | 0 | pdu->token, pdu->used_size); |
447 | 0 | pdu->used_size += len + bias - pdu->e_token_length; |
448 | 0 | if (pdu->data) { |
449 | 0 | pdu->data += (len + bias) - pdu->e_token_length; |
450 | 0 | } |
451 | 0 | } else { |
452 | 0 | pdu->used_size -= pdu->e_token_length - (len + bias); |
453 | 0 | memmove(pdu->token, &pdu->token[pdu->e_token_length - (len + bias)], pdu->used_size); |
454 | 0 | if (pdu->data) { |
455 | 0 | pdu->data -= pdu->e_token_length - (len + bias); |
456 | 0 | } |
457 | 0 | } |
458 | | |
459 | 0 | pdu->actual_token.length = len; |
460 | 0 | pdu->actual_token.s = &pdu->token[bias]; |
461 | 0 | pdu->e_token_length = (uint8_t)(len + bias); |
462 | 0 | if (len) { |
463 | 0 | switch (bias) { |
464 | 0 | case 0: |
465 | 0 | if (memcmp(pdu->token, data, len) != 0) |
466 | 0 | memcpy(pdu->token, data, len); |
467 | 0 | break; |
468 | 0 | case 1: |
469 | 0 | pdu->token[0] = (uint8_t)(len - COAP_TOKEN_EXT_1B_BIAS); |
470 | 0 | memcpy(&pdu->token[1], data, len); |
471 | 0 | break; |
472 | 0 | case 2: |
473 | 0 | pdu->token[0] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) >> 8); |
474 | 0 | pdu->token[1] = (uint8_t)((len - COAP_TOKEN_EXT_2B_BIAS) & 0xff); |
475 | 0 | memcpy(&pdu->token[2], data, len); |
476 | 0 | break; |
477 | 0 | default: |
478 | 0 | break; |
479 | 0 | } |
480 | 0 | } |
481 | 0 | if (old_len != pdu->e_token_length && pdu->hdr_size && pdu->session) |
482 | | /* Need to fix up the header */ |
483 | 0 | if (!coap_pdu_encode_header(pdu, pdu->session->proto)) |
484 | 0 | return 0; |
485 | 0 | return 1; |
486 | 0 | } |
487 | | |
488 | | int |
489 | 0 | coap_remove_option(coap_pdu_t *pdu, coap_option_num_t number) { |
490 | 0 | coap_opt_iterator_t opt_iter; |
491 | 0 | coap_opt_t *option; |
492 | 0 | coap_opt_t *next_option = NULL; |
493 | 0 | size_t opt_delta; |
494 | 0 | coap_option_t decode_this; |
495 | 0 | coap_option_t decode_next; |
496 | | |
497 | | /* Need to locate where in current options to remove this one */ |
498 | 0 | coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); |
499 | 0 | while ((option = coap_option_next(&opt_iter))) { |
500 | 0 | if (opt_iter.number == number) { |
501 | | /* Found option to delete */ |
502 | 0 | break; |
503 | 0 | } |
504 | 0 | } |
505 | 0 | if (!option) |
506 | 0 | return 0; |
507 | | |
508 | 0 | if (!coap_opt_parse(option, pdu->used_size - (option - pdu->token), |
509 | 0 | &decode_this)) |
510 | 0 | return 0; |
511 | | |
512 | 0 | next_option = coap_option_next(&opt_iter); |
513 | 0 | if (next_option) { |
514 | 0 | if (!coap_opt_parse(next_option, |
515 | 0 | pdu->used_size - (next_option - pdu->token), |
516 | 0 | &decode_next)) |
517 | 0 | return 0; |
518 | 0 | opt_delta = decode_this.delta + decode_next.delta; |
519 | 0 | if (opt_delta < 13) { |
520 | | /* can simply update the delta of next option */ |
521 | 0 | next_option[0] = (next_option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4); |
522 | 0 | } else if (opt_delta < 269 && decode_next.delta < 13) { |
523 | | /* next option delta size increase */ |
524 | 0 | next_option -= 1; |
525 | 0 | next_option[0] = (next_option[1] & 0x0f) + (13 << 4); |
526 | 0 | next_option[1] = (coap_opt_t)(opt_delta - 13); |
527 | 0 | } else if (opt_delta < 269) { |
528 | | /* can simply update the delta of next option */ |
529 | 0 | next_option[1] = (coap_opt_t)(opt_delta - 13); |
530 | 0 | } else if (decode_next.delta < 13) { /* opt_delta >= 269 */ |
531 | | /* next option delta size increase */ |
532 | 0 | if (next_option - option < 2) { |
533 | | /* Need to shuffle everything up by 1 before decrement */ |
534 | 0 | if (!coap_pdu_check_resize(pdu, pdu->used_size + 1)) |
535 | 0 | return 0; |
536 | | /* Possible a re-size took place with a realloc() */ |
537 | | /* Need to rediscover this and next options */ |
538 | 0 | coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); |
539 | 0 | while ((option = coap_option_next(&opt_iter))) { |
540 | 0 | if (opt_iter.number == number) { |
541 | | /* Found option to delete */ |
542 | 0 | break; |
543 | 0 | } |
544 | 0 | } |
545 | 0 | next_option = coap_option_next(&opt_iter); |
546 | 0 | assert(option != NULL); |
547 | 0 | assert(next_option != NULL); |
548 | 0 | memmove(&next_option[1], next_option, |
549 | 0 | pdu->used_size - (next_option - pdu->token)); |
550 | 0 | pdu->used_size++; |
551 | 0 | if (pdu->data) |
552 | 0 | pdu->data++; |
553 | 0 | next_option++; |
554 | 0 | } |
555 | 0 | next_option -= 2; |
556 | 0 | next_option[0] = (next_option[2] & 0x0f) + (14 << 4); |
557 | 0 | next_option[1] = (coap_opt_t)((opt_delta - 269) >> 8); |
558 | 0 | next_option[2] = (opt_delta - 269) & 0xff; |
559 | 0 | } else if (decode_next.delta < 269) { /* opt_delta >= 269 */ |
560 | | /* next option delta size increase */ |
561 | 0 | next_option -= 1; |
562 | 0 | next_option[0] = (next_option[1] & 0x0f) + (14 << 4); |
563 | 0 | next_option[1] = (coap_opt_t)((opt_delta - 269) >> 8); |
564 | 0 | next_option[2] = (opt_delta - 269) & 0xff; |
565 | 0 | } else { /* decode_next.delta >= 269 && opt_delta >= 269 */ |
566 | 0 | next_option[1] = (coap_opt_t)((opt_delta - 269) >> 8); |
567 | 0 | next_option[2] = (opt_delta - 269) & 0xff; |
568 | 0 | } |
569 | 0 | } else { |
570 | 0 | next_option = option + coap_opt_encode_size(decode_this.delta, |
571 | 0 | coap_opt_length(option)); |
572 | 0 | pdu->max_opt -= decode_this.delta; |
573 | 0 | } |
574 | 0 | if (pdu->used_size - (next_option - pdu->token)) |
575 | 0 | memmove(option, next_option, pdu->used_size - (next_option - pdu->token)); |
576 | 0 | pdu->used_size -= next_option - option; |
577 | 0 | if (pdu->data) |
578 | 0 | pdu->data -= next_option - option; |
579 | 0 | return 1; |
580 | 0 | } |
581 | | |
582 | | int |
583 | 0 | coap_option_check_repeatable(coap_option_num_t number) { |
584 | | /* Validate that the option is repeatable */ |
585 | 0 | switch (number) { |
586 | | /* Ignore list of genuine repeatable */ |
587 | 0 | case COAP_OPTION_IF_MATCH: |
588 | 0 | case COAP_OPTION_ETAG: |
589 | 0 | case COAP_OPTION_LOCATION_PATH: |
590 | 0 | case COAP_OPTION_URI_PATH: |
591 | 0 | case COAP_OPTION_URI_QUERY: |
592 | 0 | case COAP_OPTION_LOCATION_QUERY: |
593 | 0 | case COAP_OPTION_RTAG: |
594 | 0 | break; |
595 | | /* Protest at the known non-repeatable options and ignore them */ |
596 | 0 | case COAP_OPTION_URI_HOST: |
597 | 0 | case COAP_OPTION_IF_NONE_MATCH: |
598 | 0 | case COAP_OPTION_OBSERVE: |
599 | 0 | case COAP_OPTION_URI_PORT: |
600 | 0 | case COAP_OPTION_OSCORE: |
601 | 0 | case COAP_OPTION_CONTENT_FORMAT: |
602 | 0 | case COAP_OPTION_MAXAGE: |
603 | 0 | case COAP_OPTION_HOP_LIMIT: |
604 | 0 | case COAP_OPTION_ACCEPT: |
605 | 0 | case COAP_OPTION_BLOCK2: |
606 | 0 | case COAP_OPTION_BLOCK1: |
607 | 0 | case COAP_OPTION_SIZE2: |
608 | 0 | case COAP_OPTION_PROXY_URI: |
609 | 0 | case COAP_OPTION_PROXY_SCHEME: |
610 | 0 | case COAP_OPTION_SIZE1: |
611 | 0 | case COAP_OPTION_ECHO: |
612 | 0 | case COAP_OPTION_NORESPONSE: |
613 | 0 | coap_log_info("Option number %d is not defined as repeatable - dropped\n", |
614 | 0 | number); |
615 | 0 | return 0; |
616 | 0 | default: |
617 | 0 | coap_log_info("Option number %d is not defined as repeatable\n", |
618 | 0 | number); |
619 | | /* Accepting it after warning as there may be user defineable options */ |
620 | 0 | break; |
621 | 0 | } |
622 | 0 | return 1; |
623 | 0 | } |
624 | | |
625 | | size_t |
626 | | coap_insert_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len, |
627 | 0 | const uint8_t *data) { |
628 | 0 | coap_opt_iterator_t opt_iter; |
629 | 0 | coap_opt_t *option; |
630 | 0 | uint16_t prev_number = 0; |
631 | 0 | size_t shift; |
632 | 0 | size_t opt_delta; |
633 | 0 | coap_option_t decode; |
634 | 0 | size_t shrink = 0; |
635 | |
|
636 | 0 | if (number >= pdu->max_opt) |
637 | 0 | return coap_add_option_internal(pdu, number, len, data); |
638 | | |
639 | | /* Need to locate where in current options to insert this one */ |
640 | 0 | coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); |
641 | 0 | while ((option = coap_option_next(&opt_iter))) { |
642 | 0 | if (opt_iter.number > number) { |
643 | | /* Found where to insert */ |
644 | 0 | break; |
645 | 0 | } |
646 | 0 | prev_number = opt_iter.number; |
647 | 0 | } |
648 | 0 | assert(option != NULL); |
649 | | /* size of option inc header to insert */ |
650 | 0 | shift = coap_opt_encode_size(number - prev_number, len); |
651 | | |
652 | | /* size of next option (header may shrink in size as delta changes */ |
653 | 0 | if (!coap_opt_parse(option, pdu->used_size - (option - pdu->token), &decode)) |
654 | 0 | return 0; |
655 | 0 | opt_delta = opt_iter.number - number; |
656 | 0 | if (opt_delta == 0) { |
657 | 0 | if (!coap_option_check_repeatable(number)) |
658 | 0 | return 0; |
659 | 0 | } |
660 | | |
661 | 0 | if (!coap_pdu_check_resize(pdu, |
662 | 0 | pdu->used_size + shift - shrink)) |
663 | 0 | return 0; |
664 | | |
665 | | /* Possible a re-size took place with a realloc() */ |
666 | | /* Need to locate where in current options to insert this one */ |
667 | 0 | coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); |
668 | 0 | while ((option = coap_option_next(&opt_iter))) { |
669 | 0 | if (opt_iter.number > number) { |
670 | | /* Found where to insert */ |
671 | 0 | break; |
672 | 0 | } |
673 | 0 | } |
674 | 0 | assert(option != NULL); |
675 | | |
676 | 0 | if (decode.delta < 13) { |
677 | | /* can simply patch in the new delta of next option */ |
678 | 0 | option[0] = (option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4); |
679 | 0 | } else if (decode.delta < 269 && opt_delta < 13) { |
680 | | /* option header is going to shrink by one byte */ |
681 | 0 | option[1] = (option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4); |
682 | 0 | shrink = 1; |
683 | 0 | } else if (decode.delta < 269 && opt_delta < 269) { |
684 | | /* can simply patch in the new delta of next option */ |
685 | 0 | option[1] = (coap_opt_t)(opt_delta - 13); |
686 | 0 | } else if (opt_delta < 13) { |
687 | | /* option header is going to shrink by two bytes */ |
688 | 0 | option[2] = (option[0] & 0x0f) + (coap_opt_t)(opt_delta << 4); |
689 | 0 | shrink = 2; |
690 | 0 | } else if (opt_delta < 269) { |
691 | | /* option header is going to shrink by one bytes */ |
692 | 0 | option[1] = (option[0] & 0x0f) + 0xd0; |
693 | 0 | option[2] = (coap_opt_t)(opt_delta - 13); |
694 | 0 | shrink = 1; |
695 | 0 | } else { |
696 | | /* can simply patch in the new delta of next option */ |
697 | 0 | option[1] = (coap_opt_t)((opt_delta - 269) >> 8); |
698 | 0 | option[2] = (opt_delta - 269) & 0xff; |
699 | 0 | } |
700 | |
|
701 | 0 | memmove(&option[shift], &option[shrink], |
702 | 0 | pdu->used_size - (option - pdu->token) - shrink); |
703 | 0 | if (!coap_opt_encode(option, pdu->alloc_size - pdu->used_size, |
704 | 0 | number - prev_number, data, len)) |
705 | 0 | return 0; |
706 | | |
707 | 0 | if (shift >= shrink) { |
708 | 0 | pdu->used_size += shift - shrink; |
709 | 0 | if (pdu->data) |
710 | 0 | pdu->data += shift - shrink; |
711 | 0 | } else { |
712 | 0 | pdu->used_size -= shrink - shift; |
713 | 0 | if (pdu->data) |
714 | 0 | pdu->data -= shrink - shift; |
715 | 0 | } |
716 | 0 | return shift; |
717 | 0 | } |
718 | | |
719 | | size_t |
720 | | coap_update_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len, |
721 | 0 | const uint8_t *data) { |
722 | 0 | coap_opt_iterator_t opt_iter; |
723 | 0 | coap_opt_t *option; |
724 | 0 | coap_option_t decode; |
725 | 0 | size_t new_length = 0; |
726 | 0 | size_t old_length = 0; |
727 | |
|
728 | 0 | option = coap_check_option(pdu, number, &opt_iter); |
729 | 0 | if (!option) |
730 | 0 | return coap_insert_option(pdu, number, len, data); |
731 | | |
732 | 0 | old_length = coap_opt_parse(option, (size_t)-1, &decode); |
733 | 0 | if (old_length == 0) |
734 | 0 | return 0; |
735 | 0 | new_length = coap_opt_encode_size(decode.delta, len); |
736 | |
|
737 | 0 | if (new_length > old_length) { |
738 | 0 | if (!coap_pdu_check_resize(pdu, |
739 | 0 | pdu->used_size + new_length - old_length)) |
740 | 0 | return 0; |
741 | | /* Possible a re-size took place with a realloc() */ |
742 | 0 | option = coap_check_option(pdu, number, &opt_iter); |
743 | 0 | } |
744 | | |
745 | 0 | if (new_length != old_length) |
746 | 0 | memmove(&option[new_length], &option[old_length], |
747 | 0 | pdu->used_size - (option - pdu->token) - old_length); |
748 | |
|
749 | 0 | if (!coap_opt_encode(option, new_length, |
750 | 0 | decode.delta, data, len)) |
751 | 0 | return 0; |
752 | | |
753 | 0 | if (new_length >= old_length) { |
754 | 0 | pdu->used_size += new_length - old_length; |
755 | 0 | if (pdu->data) |
756 | 0 | pdu->data += new_length - old_length; |
757 | 0 | } else { |
758 | 0 | pdu->used_size -= old_length - new_length; |
759 | 0 | if (pdu->data) |
760 | 0 | pdu->data -= old_length - new_length; |
761 | 0 | } |
762 | 0 | return 1; |
763 | 0 | } |
764 | | |
765 | | size_t |
766 | | coap_add_option(coap_pdu_t *pdu, coap_option_num_t number, size_t len, |
767 | 0 | const uint8_t *data) { |
768 | 0 | if (pdu->data) { |
769 | 0 | coap_log_warn("coap_add_optlist_pdu: PDU already contains data\n"); |
770 | 0 | return 0; |
771 | 0 | } |
772 | 0 | return coap_add_option_internal(pdu, number, len, data); |
773 | 0 | } |
774 | | |
775 | | size_t |
776 | | coap_add_option_internal(coap_pdu_t *pdu, coap_option_num_t number, size_t len, |
777 | 0 | const uint8_t *data) { |
778 | 0 | size_t optsize; |
779 | 0 | coap_opt_t *opt; |
780 | |
|
781 | 0 | assert(pdu); |
782 | | |
783 | 0 | if (number == pdu->max_opt) { |
784 | 0 | if (!coap_option_check_repeatable(number)) |
785 | 0 | return 0; |
786 | 0 | } |
787 | | |
788 | 0 | if (COAP_PDU_IS_REQUEST(pdu) && |
789 | 0 | (number == COAP_OPTION_PROXY_URI || |
790 | 0 | number == COAP_OPTION_PROXY_SCHEME)) { |
791 | | /* |
792 | | * Need to check whether there is a hop-limit option. If not, it needs |
793 | | * to be inserted by default (RFC 8768). |
794 | | */ |
795 | 0 | coap_opt_iterator_t opt_iter; |
796 | |
|
797 | 0 | if (coap_check_option(pdu, COAP_OPTION_HOP_LIMIT, &opt_iter) == NULL) { |
798 | 0 | size_t hop_limit = COAP_OPTION_HOP_LIMIT; |
799 | |
|
800 | 0 | coap_insert_option(pdu, COAP_OPTION_HOP_LIMIT, 1, (uint8_t *)&hop_limit); |
801 | 0 | } |
802 | 0 | } |
803 | |
|
804 | 0 | if (number < pdu->max_opt) { |
805 | 0 | coap_log_debug("coap_add_option: options are not in correct order\n"); |
806 | 0 | return coap_insert_option(pdu, number, len, data); |
807 | 0 | } |
808 | | |
809 | 0 | optsize = coap_opt_encode_size(number - pdu->max_opt, len); |
810 | 0 | if (!coap_pdu_check_resize(pdu, |
811 | 0 | pdu->used_size + optsize)) |
812 | 0 | return 0; |
813 | | |
814 | 0 | if (pdu->data) { |
815 | | /* include option delimiter */ |
816 | 0 | memmove(&pdu->data[optsize-1], &pdu->data[-1], |
817 | 0 | pdu->used_size - (pdu->data - pdu->token) + 1); |
818 | 0 | opt = pdu->data -1; |
819 | 0 | pdu->data += optsize; |
820 | 0 | } else { |
821 | 0 | opt = pdu->token + pdu->used_size; |
822 | 0 | } |
823 | | |
824 | | /* encode option and check length */ |
825 | 0 | optsize = coap_opt_encode(opt, pdu->alloc_size - pdu->used_size, |
826 | 0 | number - pdu->max_opt, data, len); |
827 | |
|
828 | 0 | if (!optsize) { |
829 | 0 | coap_log_warn("coap_add_option: cannot add option\n"); |
830 | | /* error */ |
831 | 0 | return 0; |
832 | 0 | } else { |
833 | 0 | pdu->max_opt = number; |
834 | 0 | pdu->used_size += optsize; |
835 | 0 | } |
836 | | |
837 | 0 | return optsize; |
838 | 0 | } |
839 | | |
840 | | int |
841 | 0 | coap_add_data(coap_pdu_t *pdu, size_t len, const uint8_t *data) { |
842 | 0 | if (len == 0) { |
843 | 0 | return 1; |
844 | 0 | } else { |
845 | 0 | uint8_t *payload = coap_add_data_after(pdu, len); |
846 | 0 | if (payload != NULL) |
847 | 0 | memcpy(payload, data, len); |
848 | 0 | return payload != NULL; |
849 | 0 | } |
850 | 0 | } |
851 | | |
852 | | uint8_t * |
853 | 0 | coap_add_data_after(coap_pdu_t *pdu, size_t len) { |
854 | 0 | assert(pdu); |
855 | 0 | if (pdu->data) { |
856 | 0 | coap_log_warn("coap_add_data: PDU already contains data\n"); |
857 | 0 | return 0; |
858 | 0 | } |
859 | | |
860 | 0 | if (len == 0) |
861 | 0 | return NULL; |
862 | | |
863 | 0 | if (!coap_pdu_resize(pdu, pdu->used_size + len + 1)) |
864 | 0 | return 0; |
865 | 0 | pdu->token[pdu->used_size++] = COAP_PAYLOAD_START; |
866 | 0 | pdu->data = pdu->token + pdu->used_size; |
867 | 0 | pdu->used_size += len; |
868 | 0 | return pdu->data; |
869 | 0 | } |
870 | | |
871 | | int |
872 | 10.7k | coap_get_data(const coap_pdu_t *pdu, size_t *len, const uint8_t **data) { |
873 | 10.7k | size_t offset; |
874 | 10.7k | size_t total; |
875 | | |
876 | 10.7k | return coap_get_data_large(pdu, len, data, &offset, &total); |
877 | 10.7k | } |
878 | | |
879 | | int |
880 | | coap_get_data_large(const coap_pdu_t *pdu, size_t *len, const uint8_t **data, |
881 | 10.7k | size_t *offset, size_t *total) { |
882 | 10.7k | assert(pdu); |
883 | 10.7k | assert(len); |
884 | 10.7k | assert(data); |
885 | | |
886 | 10.7k | *offset = pdu->body_offset; |
887 | 10.7k | *total = pdu->body_total; |
888 | 10.7k | if (pdu->body_data) { |
889 | 0 | *data = pdu->body_data; |
890 | 0 | *len = pdu->body_length; |
891 | 0 | return 1; |
892 | 0 | } |
893 | 10.7k | *data = pdu->data; |
894 | 10.7k | if (pdu->data == NULL) { |
895 | 7.53k | *len = 0; |
896 | 7.53k | *total = 0; |
897 | 7.53k | return 0; |
898 | 7.53k | } |
899 | | |
900 | 3.19k | *len = pdu->used_size - (pdu->data - pdu->token); |
901 | 3.19k | if (*total == 0) |
902 | 3.19k | *total = *len; |
903 | | |
904 | 3.19k | return 1; |
905 | 10.7k | } |
906 | | |
907 | | #ifndef SHORT_ERROR_RESPONSE |
908 | | typedef struct { |
909 | | unsigned char code; |
910 | | const char *phrase; |
911 | | } error_desc_t; |
912 | | |
913 | | /* if you change anything here, make sure, that the longest string does not |
914 | | * exceed COAP_ERROR_PHRASE_LENGTH. */ |
915 | | error_desc_t coap_error[] = { |
916 | | { COAP_RESPONSE_CODE(201), "Created" }, |
917 | | { COAP_RESPONSE_CODE(202), "Deleted" }, |
918 | | { COAP_RESPONSE_CODE(203), "Valid" }, |
919 | | { COAP_RESPONSE_CODE(204), "Changed" }, |
920 | | { COAP_RESPONSE_CODE(205), "Content" }, |
921 | | { COAP_RESPONSE_CODE(231), "Continue" }, |
922 | | { COAP_RESPONSE_CODE(400), "Bad Request" }, |
923 | | { COAP_RESPONSE_CODE(401), "Unauthorized" }, |
924 | | { COAP_RESPONSE_CODE(402), "Bad Option" }, |
925 | | { COAP_RESPONSE_CODE(403), "Forbidden" }, |
926 | | { COAP_RESPONSE_CODE(404), "Not Found" }, |
927 | | { COAP_RESPONSE_CODE(405), "Method Not Allowed" }, |
928 | | { COAP_RESPONSE_CODE(406), "Not Acceptable" }, |
929 | | { COAP_RESPONSE_CODE(408), "Request Entity Incomplete" }, |
930 | | { COAP_RESPONSE_CODE(409), "Conflict" }, |
931 | | { COAP_RESPONSE_CODE(412), "Precondition Failed" }, |
932 | | { COAP_RESPONSE_CODE(413), "Request Entity Too Large" }, |
933 | | { COAP_RESPONSE_CODE(415), "Unsupported Content-Format" }, |
934 | | { COAP_RESPONSE_CODE(422), "Unprocessable" }, |
935 | | { COAP_RESPONSE_CODE(429), "Too Many Requests" }, |
936 | | { COAP_RESPONSE_CODE(500), "Internal Server Error" }, |
937 | | { COAP_RESPONSE_CODE(501), "Not Implemented" }, |
938 | | { COAP_RESPONSE_CODE(502), "Bad Gateway" }, |
939 | | { COAP_RESPONSE_CODE(503), "Service Unavailable" }, |
940 | | { COAP_RESPONSE_CODE(504), "Gateway Timeout" }, |
941 | | { COAP_RESPONSE_CODE(505), "Proxying Not Supported" }, |
942 | | { COAP_RESPONSE_CODE(508), "Hop Limit Reached" }, |
943 | | { 0, NULL } /* end marker */ |
944 | | }; |
945 | | |
946 | | const char * |
947 | 0 | coap_response_phrase(unsigned char code) { |
948 | 0 | int i; |
949 | 0 | for (i = 0; coap_error[i].code; ++i) { |
950 | 0 | if (coap_error[i].code == code) |
951 | 0 | return coap_error[i].phrase; |
952 | 0 | } |
953 | 0 | return NULL; |
954 | 0 | } |
955 | | #endif |
956 | | |
957 | | /** |
958 | | * Advances *optp to next option if still in PDU. This function |
959 | | * returns the number of bytes opt has been advanced or @c 0 |
960 | | * on error. |
961 | | */ |
962 | | static size_t |
963 | 35.3M | next_option_safe(coap_opt_t **optp, size_t *length, uint16_t *max_opt) { |
964 | 35.3M | coap_option_t option; |
965 | 35.3M | size_t optsize; |
966 | | |
967 | 35.3M | assert(optp); |
968 | 35.3M | assert(*optp); |
969 | 35.3M | assert(length); |
970 | | |
971 | 35.3M | optsize = coap_opt_parse(*optp, *length, &option); |
972 | 35.3M | if (optsize) { |
973 | 35.3M | assert(optsize <= *length); |
974 | | |
975 | | /* signal an error if this option would exceed the |
976 | | * allowed number space */ |
977 | 35.3M | if (*max_opt + option.delta > COAP_MAX_OPT) { |
978 | 42 | return 0; |
979 | 42 | } |
980 | 35.3M | *max_opt += option.delta; |
981 | 35.3M | *optp += optsize; |
982 | 35.3M | *length -= optsize; |
983 | 35.3M | } |
984 | | |
985 | 35.3M | return optsize; |
986 | 35.3M | } |
987 | | |
988 | | size_t |
989 | | coap_pdu_parse_header_size(coap_proto_t proto, |
990 | 10.9k | const uint8_t *data) { |
991 | 10.9k | assert(data); |
992 | 10.9k | size_t header_size = 0; |
993 | | |
994 | 10.9k | if (proto == COAP_PROTO_TCP || proto==COAP_PROTO_TLS) { |
995 | 3.68k | uint8_t len = *data >> 4; |
996 | 3.68k | if (len < 13) |
997 | 3.59k | header_size = 2; |
998 | 93 | else if (len==13) |
999 | 41 | header_size = 3; |
1000 | 52 | else if (len==14) |
1001 | 29 | header_size = 4; |
1002 | 23 | else |
1003 | 23 | header_size = 6; |
1004 | 7.25k | } else if (proto == COAP_PROTO_WS || proto==COAP_PROTO_WSS) { |
1005 | 3.57k | header_size = 2; |
1006 | 3.68k | } else if (proto == COAP_PROTO_UDP || proto==COAP_PROTO_DTLS) { |
1007 | 3.68k | header_size = 4; |
1008 | 3.68k | } |
1009 | | |
1010 | 10.9k | return header_size; |
1011 | 10.9k | } |
1012 | | |
1013 | | #if !COAP_DISABLE_TCP |
1014 | | /* |
1015 | | * strm |
1016 | | * return +ve PDU size including token |
1017 | | * 0 PDU does not parse |
1018 | | */ |
1019 | | size_t |
1020 | | coap_pdu_parse_size(coap_proto_t proto, |
1021 | | const uint8_t *data, |
1022 | 0 | size_t length) { |
1023 | 0 | assert(data); |
1024 | 0 | assert(proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS || |
1025 | 0 | proto == COAP_PROTO_WS || proto == COAP_PROTO_WSS); |
1026 | 0 | assert(coap_pdu_parse_header_size(proto, data) <= length); |
1027 | | |
1028 | 0 | size_t size = 0; |
1029 | 0 | const uint8_t *token_start = NULL; |
1030 | |
|
1031 | 0 | if ((proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS) && length >= 1) { |
1032 | 0 | uint8_t len = *data >> 4; |
1033 | 0 | uint8_t tkl = *data & 0x0f; |
1034 | |
|
1035 | 0 | if (len < 13) { |
1036 | 0 | size = len; |
1037 | 0 | token_start = &data[2]; |
1038 | 0 | } else if (length >= 2) { |
1039 | 0 | if (len==13) { |
1040 | 0 | size = (size_t)data[1] + COAP_MESSAGE_SIZE_OFFSET_TCP8; |
1041 | 0 | token_start = &data[3]; |
1042 | 0 | } else if (length >= 3) { |
1043 | 0 | if (len==14) { |
1044 | 0 | size = ((size_t)data[1] << 8) + data[2] + COAP_MESSAGE_SIZE_OFFSET_TCP16; |
1045 | 0 | token_start = &data[4]; |
1046 | 0 | } else if (length >= 5) { |
1047 | 0 | size = ((size_t)data[1] << 24) + ((size_t)data[2] << 16) |
1048 | 0 | + ((size_t)data[3] << 8) + data[4] + COAP_MESSAGE_SIZE_OFFSET_TCP32; |
1049 | 0 | token_start = &data[6]; |
1050 | 0 | } |
1051 | 0 | } |
1052 | 0 | } |
1053 | 0 | if (token_start) { |
1054 | | /* account for the token length */ |
1055 | 0 | if (tkl < COAP_TOKEN_EXT_1B_TKL) { |
1056 | 0 | size += tkl; |
1057 | 0 | } else if (tkl == COAP_TOKEN_EXT_1B_TKL) { |
1058 | 0 | size += token_start[0] + COAP_TOKEN_EXT_1B_BIAS + 1; |
1059 | 0 | } else if (tkl == COAP_TOKEN_EXT_2B_TKL) { |
1060 | 0 | size += ((uint16_t)token_start[0] << 8) + token_start[1] + |
1061 | 0 | COAP_TOKEN_EXT_2B_BIAS + 2; |
1062 | 0 | } else { |
1063 | | /* Invalid at this point - caught later as undersized */ |
1064 | 0 | } |
1065 | 0 | } |
1066 | 0 | } |
1067 | |
|
1068 | 0 | return size; |
1069 | 0 | } |
1070 | | #endif /* ! COAP_DISABLE_TCP */ |
1071 | | |
1072 | | int |
1073 | 10.9k | coap_pdu_parse_header(coap_pdu_t *pdu, coap_proto_t proto) { |
1074 | 10.9k | uint8_t *hdr = pdu->token - pdu->hdr_size; |
1075 | 10.9k | uint8_t e_token_length; |
1076 | | |
1077 | 10.9k | if (proto == COAP_PROTO_UDP || proto == COAP_PROTO_DTLS) { |
1078 | 3.67k | assert(pdu->hdr_size == 4); |
1079 | 3.67k | if ((hdr[0] >> 6) != COAP_DEFAULT_VERSION) { |
1080 | 6 | coap_log_debug("coap_pdu_parse: UDP version not supported\n"); |
1081 | 6 | return 0; |
1082 | 6 | } |
1083 | 3.67k | pdu->type = (hdr[0] >> 4) & 0x03; |
1084 | 3.67k | pdu->code = hdr[1]; |
1085 | 3.67k | pdu->mid = (uint16_t)hdr[2] << 8 | hdr[3]; |
1086 | 7.25k | } else if (proto == COAP_PROTO_TCP || proto == COAP_PROTO_TLS) { |
1087 | 3.67k | assert(pdu->hdr_size >= 2 && pdu->hdr_size <= 6); |
1088 | 3.67k | pdu->type = COAP_MESSAGE_CON; |
1089 | 3.67k | pdu->code = hdr[pdu->hdr_size-1]; |
1090 | 3.67k | pdu->mid = 0; |
1091 | 3.67k | } else if (proto == COAP_PROTO_WS || proto == COAP_PROTO_WSS) { |
1092 | 3.57k | assert(pdu->hdr_size == 2); |
1093 | 3.57k | pdu->type = COAP_MESSAGE_CON; |
1094 | 3.57k | pdu->code = hdr[pdu->hdr_size-1]; |
1095 | 3.57k | pdu->mid = 0; |
1096 | 3.57k | } else { |
1097 | 0 | coap_log_debug("coap_pdu_parse: unsupported protocol\n"); |
1098 | 0 | return 0; |
1099 | 0 | } |
1100 | | |
1101 | 10.9k | e_token_length = hdr[0] & 0x0f; |
1102 | 10.9k | if (e_token_length < COAP_TOKEN_EXT_1B_TKL) { |
1103 | 10.6k | pdu->e_token_length = e_token_length; |
1104 | 10.6k | pdu->actual_token.length = pdu->e_token_length; |
1105 | 10.6k | pdu->actual_token.s = &pdu->token[0]; |
1106 | 10.6k | } else if (e_token_length == COAP_TOKEN_EXT_1B_TKL) { |
1107 | 97 | pdu->e_token_length = pdu->token[0] + COAP_TOKEN_EXT_1B_BIAS + 1; |
1108 | 97 | pdu->actual_token.length = pdu->e_token_length - 1; |
1109 | 97 | pdu->actual_token.s = &pdu->token[1]; |
1110 | 195 | } else if (e_token_length == COAP_TOKEN_EXT_2B_TKL) { |
1111 | 188 | pdu->e_token_length = ((uint16_t)pdu->token[0] << 8) + pdu->token[1] + |
1112 | 188 | COAP_TOKEN_EXT_2B_BIAS + 2; |
1113 | 188 | pdu->actual_token.length = pdu->e_token_length - 2; |
1114 | 188 | pdu->actual_token.s = &pdu->token[2]; |
1115 | 188 | } |
1116 | 10.9k | if (pdu->e_token_length > pdu->alloc_size || e_token_length == 15) { |
1117 | | /* Invalid PDU provided - not wise to assert here though */ |
1118 | 67 | coap_log_debug("coap_pdu_parse: PDU header token size broken\n"); |
1119 | 67 | pdu->e_token_length = 0; |
1120 | 67 | pdu->actual_token.length = 0; |
1121 | 67 | return 0; |
1122 | 67 | } |
1123 | 10.8k | return 1; |
1124 | 10.9k | } |
1125 | | |
1126 | | static int |
1127 | 14.7M | coap_pdu_parse_opt_csm(coap_pdu_t *pdu, uint16_t len) { |
1128 | 14.7M | switch ((coap_pdu_signaling_proto_t)pdu->code) { |
1129 | 29.5k | case COAP_SIGNALING_CSM: |
1130 | 29.5k | switch (pdu->max_opt) { |
1131 | 5.24k | case COAP_SIGNALING_OPTION_MAX_MESSAGE_SIZE: |
1132 | 5.24k | if (len > 4) |
1133 | 3.14k | goto bad; |
1134 | 2.09k | break; |
1135 | 4.53k | case COAP_SIGNALING_OPTION_BLOCK_WISE_TRANSFER: |
1136 | 4.53k | if (len > 0) |
1137 | 3.18k | goto bad; |
1138 | 1.34k | break; |
1139 | 5.03k | case COAP_SIGNALING_OPTION_EXTENDED_TOKEN_LENGTH: |
1140 | 5.03k | if (len > 3) |
1141 | 2.95k | goto bad; |
1142 | 2.07k | break; |
1143 | 14.7k | default: |
1144 | 14.7k | if (pdu->max_opt & 0x01) |
1145 | 7.31k | goto bad; /* Critical */ |
1146 | 29.5k | } |
1147 | 12.9k | break; |
1148 | 20.7k | case COAP_SIGNALING_PING: |
1149 | 35.9k | case COAP_SIGNALING_PONG: |
1150 | 35.9k | switch (pdu->max_opt) { |
1151 | 5.50k | case COAP_SIGNALING_OPTION_CUSTODY: |
1152 | 5.50k | if (len > 0) |
1153 | 3.36k | goto bad; |
1154 | 2.14k | break; |
1155 | 30.4k | default: |
1156 | 30.4k | if (pdu->max_opt & 0x01) |
1157 | 21.0k | goto bad; /* Critical */ |
1158 | 35.9k | } |
1159 | 11.5k | break; |
1160 | 73.6k | case COAP_SIGNALING_RELEASE: |
1161 | 73.6k | switch (pdu->max_opt) { |
1162 | 35.0k | case COAP_SIGNALING_OPTION_ALTERNATIVE_ADDRESS: |
1163 | 35.0k | if (len < 1 || len > 255) |
1164 | 32.2k | goto bad; |
1165 | 2.77k | break; |
1166 | 7.25k | case COAP_SIGNALING_OPTION_HOLD_OFF: |
1167 | 7.25k | if (len > 3) |
1168 | 3.13k | goto bad; |
1169 | 4.12k | break; |
1170 | 31.3k | default: |
1171 | 31.3k | if (pdu->max_opt & 0x01) |
1172 | 18.4k | goto bad; /* Critical */ |
1173 | 73.6k | } |
1174 | 19.8k | break; |
1175 | 19.8k | case COAP_SIGNALING_ABORT: |
1176 | 10.3k | switch (pdu->max_opt) { |
1177 | 4.74k | case COAP_SIGNALING_OPTION_BAD_CSM_OPTION: |
1178 | 4.74k | if (len > 2) |
1179 | 3.17k | goto bad; |
1180 | 1.56k | break; |
1181 | 5.64k | default: |
1182 | 5.64k | if (pdu->max_opt & 0x01) |
1183 | 3.90k | goto bad; /* Critical */ |
1184 | 10.3k | } |
1185 | 3.30k | break; |
1186 | 14.6M | default: |
1187 | 14.6M | ; |
1188 | 14.7M | } |
1189 | 14.6M | return 1; |
1190 | 101k | bad: |
1191 | 101k | return 0; |
1192 | 14.7M | } |
1193 | | |
1194 | | static int |
1195 | 20.5M | coap_pdu_parse_opt_base(coap_pdu_t *pdu, uint16_t len) { |
1196 | 20.5M | int res = 1; |
1197 | | |
1198 | 20.5M | switch (pdu->max_opt) { |
1199 | 5.99k | case COAP_OPTION_IF_MATCH: |
1200 | 5.99k | if (len > 8) |
1201 | 3.02k | res = 0; |
1202 | 5.99k | break; |
1203 | 13.5k | case COAP_OPTION_URI_HOST: |
1204 | 13.5k | if (len < 1 || len > 255) |
1205 | 11.7k | res = 0; |
1206 | 13.5k | break; |
1207 | 26.8k | case COAP_OPTION_ETAG: |
1208 | 26.8k | if (len < 1 || len > 8) |
1209 | 25.3k | res = 0; |
1210 | 26.8k | break; |
1211 | 10.9k | case COAP_OPTION_IF_NONE_MATCH: |
1212 | 10.9k | if (len != 0) |
1213 | 3.34k | res = 0; |
1214 | 10.9k | break; |
1215 | 14.3k | case COAP_OPTION_OBSERVE: |
1216 | 14.3k | if (len > 3) |
1217 | 3.24k | res = 0; |
1218 | 14.3k | break; |
1219 | 6.11k | case COAP_OPTION_URI_PORT: |
1220 | 6.11k | if (len > 2) |
1221 | 3.33k | res = 0; |
1222 | 6.11k | break; |
1223 | 64.0k | case COAP_OPTION_LOCATION_PATH: |
1224 | 64.0k | if (len > 255) |
1225 | 2.96k | res = 0; |
1226 | 64.0k | break; |
1227 | 466k | case COAP_OPTION_OSCORE: |
1228 | 466k | if (len > 255) |
1229 | 3.38k | res = 0; |
1230 | 466k | break; |
1231 | 3.62M | case COAP_OPTION_URI_PATH: |
1232 | 3.62M | if (len > 255) |
1233 | 3.20k | res = 0; |
1234 | 3.62M | break; |
1235 | 9.26k | case COAP_OPTION_CONTENT_FORMAT: |
1236 | 9.26k | if (len > 2) |
1237 | 3.37k | res = 0; |
1238 | 9.26k | break; |
1239 | 120k | case COAP_OPTION_MAXAGE: |
1240 | 120k | if (len > 4) |
1241 | 3.34k | res = 0; |
1242 | 120k | break; |
1243 | 61.9k | case COAP_OPTION_URI_QUERY: |
1244 | 61.9k | if (len < 1 || len > 255) |
1245 | 59.9k | res = 0; |
1246 | 61.9k | break; |
1247 | 45.0k | case COAP_OPTION_HOP_LIMIT: |
1248 | 45.0k | if (len != 1) |
1249 | 44.4k | res = 0; |
1250 | 45.0k | break; |
1251 | 7.32k | case COAP_OPTION_ACCEPT: |
1252 | 7.32k | if (len > 2) |
1253 | 3.15k | res = 0; |
1254 | 7.32k | break; |
1255 | 13.2k | case COAP_OPTION_LOCATION_QUERY: |
1256 | 13.2k | if (len > 255) |
1257 | 3.05k | res = 0; |
1258 | 13.2k | break; |
1259 | 8.01k | case COAP_OPTION_BLOCK2: |
1260 | 8.01k | if (len > 3) |
1261 | 3.24k | res = 0; |
1262 | 8.01k | break; |
1263 | 5.59k | case COAP_OPTION_BLOCK1: |
1264 | 5.59k | if (len > 3) |
1265 | 3.20k | res = 0; |
1266 | 5.59k | break; |
1267 | 8.69k | case COAP_OPTION_SIZE2: |
1268 | 8.69k | if (len > 4) |
1269 | 3.27k | res = 0; |
1270 | 8.69k | break; |
1271 | 35.6k | case COAP_OPTION_PROXY_URI: |
1272 | 35.6k | if (len < 1 || len > 1034) |
1273 | 27.4k | res = 0; |
1274 | 35.6k | break; |
1275 | 14.2k | case COAP_OPTION_PROXY_SCHEME: |
1276 | 14.2k | if (len < 1 || len > 255) |
1277 | 13.0k | res = 0; |
1278 | 14.2k | break; |
1279 | 7.10k | case COAP_OPTION_SIZE1: |
1280 | 7.10k | if (len > 4) |
1281 | 3.09k | res = 0; |
1282 | 7.10k | break; |
1283 | 5.22k | case COAP_OPTION_ECHO: |
1284 | 5.22k | if (len > 40) |
1285 | 2.92k | res = 0; |
1286 | 5.22k | break; |
1287 | 4.49k | case COAP_OPTION_NORESPONSE: |
1288 | 4.49k | if (len > 1) |
1289 | 3.04k | res = 0; |
1290 | 4.49k | break; |
1291 | 4.70k | case COAP_OPTION_RTAG: |
1292 | 4.70k | if (len > 8) |
1293 | 3.05k | res = 0; |
1294 | 4.70k | break; |
1295 | 15.9M | default: |
1296 | 15.9M | ; |
1297 | 20.5M | } |
1298 | 20.5M | return res; |
1299 | 20.5M | } |
1300 | | |
1301 | | static int |
1302 | 261k | write_prefix(char **obp, size_t *len, const char *prf, size_t prflen) { |
1303 | | /* Make sure space for null terminating byte */ |
1304 | 261k | if (*len < prflen +1) { |
1305 | 104 | return 0; |
1306 | 104 | } |
1307 | | |
1308 | 261k | memcpy(*obp, prf, prflen); |
1309 | 261k | *obp += prflen; |
1310 | 261k | *len -= prflen; |
1311 | 261k | return 1; |
1312 | 261k | } |
1313 | | |
1314 | | static int |
1315 | 2.46M | write_char(char **obp, size_t *len, int c, int printable) { |
1316 | | /* Make sure space for null terminating byte */ |
1317 | 2.46M | if (*len < 2 +1) { |
1318 | 2.01k | return 0; |
1319 | 2.01k | } |
1320 | | |
1321 | 2.45M | if (!printable) { |
1322 | 1.22M | const uint8_t hex[] = "0123456789abcdef"; |
1323 | 1.22M | (*obp)[0] = hex[(c & 0xf0) >> 4]; |
1324 | 1.22M | (*obp)[1] = hex[c & 0x0f]; |
1325 | 1.22M | } else { |
1326 | 1.22M | (*obp)[0] = isprint(c) ? c : '.'; |
1327 | 1.22M | (*obp)[1] = ' '; |
1328 | 1.22M | } |
1329 | 2.45M | *obp += 2; |
1330 | 2.45M | *len -= 2; |
1331 | 2.45M | return 1; |
1332 | 2.46M | } |
1333 | | |
1334 | | int |
1335 | 10.8k | coap_pdu_parse_opt(coap_pdu_t *pdu) { |
1336 | 10.8k | int good = 1; |
1337 | | |
1338 | | /* sanity checks */ |
1339 | 10.8k | if (pdu->code == 0) { |
1340 | 49 | if (pdu->used_size != 0 || pdu->e_token_length) { |
1341 | 46 | coap_log_debug("coap_pdu_parse: empty message is not empty\n"); |
1342 | 46 | return 0; |
1343 | 46 | } |
1344 | 49 | } |
1345 | | |
1346 | 10.8k | if (pdu->e_token_length > pdu->used_size) { |
1347 | 0 | coap_log_debug("coap_pdu_parse: invalid Token\n"); |
1348 | 0 | return 0; |
1349 | 0 | } |
1350 | | |
1351 | 10.8k | pdu->max_opt = 0; |
1352 | 10.8k | if (pdu->code == 0) { |
1353 | | /* empty packet */ |
1354 | 3 | pdu->used_size = 0; |
1355 | 3 | pdu->data = NULL; |
1356 | 10.8k | } else { |
1357 | | /* skip header + token */ |
1358 | 10.8k | coap_opt_t *opt = pdu->token + pdu->e_token_length; |
1359 | 10.8k | size_t length = pdu->used_size - pdu->e_token_length; |
1360 | | |
1361 | 23.9M | while (length > 0 && *opt != COAP_PAYLOAD_START) { |
1362 | 23.9M | #if (COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_WARN) |
1363 | 23.9M | coap_opt_t *opt_last = opt; |
1364 | 23.9M | #endif |
1365 | 23.9M | size_t optsize = next_option_safe(&opt, &length, &pdu->max_opt); |
1366 | 23.9M | const uint32_t len = |
1367 | 23.9M | optsize ? coap_opt_length((const uint8_t *)opt - optsize) : 0; |
1368 | 23.9M | if (optsize == 0) { |
1369 | 1.00k | coap_log_debug("coap_pdu_parse: %d.%02d: offset %u malformed option\n", |
1370 | 1.00k | pdu->code >> 5, pdu->code & 0x1F, |
1371 | 1.00k | (int)(opt_last - pdu->token - pdu->e_token_length)); |
1372 | 1.00k | good = 0; |
1373 | 1.00k | break; |
1374 | 1.00k | } |
1375 | 23.9M | if (COAP_PDU_IS_SIGNALING(pdu) ? |
1376 | 14.0M | !coap_pdu_parse_opt_csm(pdu, len) : |
1377 | 23.9M | !coap_pdu_parse_opt_base(pdu, len)) { |
1378 | 113k | coap_log_warn("coap_pdu_parse: %d.%02d: offset %u option %u has bad length %" PRIu32 "\n", |
1379 | 113k | pdu->code >> 5, pdu->code & 0x1F, |
1380 | 113k | (int)(opt_last - pdu->token - pdu->e_token_length), pdu->max_opt, |
1381 | 113k | len); |
1382 | 113k | good = 0; |
1383 | 113k | } |
1384 | 23.9M | } |
1385 | | |
1386 | 10.8k | if (!good) { |
1387 | | /* |
1388 | | * Dump the options in the PDU for analysis, space separated except |
1389 | | * error options which are prefixed by * |
1390 | | * Two rows - hex and ascii (if printable) |
1391 | | */ |
1392 | 3.37k | static char outbuf[COAP_DEBUG_BUF_SIZE]; |
1393 | 3.37k | char *obp; |
1394 | 3.37k | size_t tlen; |
1395 | 3.37k | size_t outbuflen; |
1396 | 3.37k | int i; |
1397 | 3.37k | int ok; |
1398 | | |
1399 | 10.1k | for (i = 0; i < 2; i++) { |
1400 | 6.74k | opt = pdu->token + pdu->e_token_length; |
1401 | 6.74k | length = pdu->used_size - pdu->e_token_length; |
1402 | 6.74k | pdu->max_opt = 0; |
1403 | | |
1404 | 6.74k | outbuflen = sizeof(outbuf); |
1405 | 6.74k | obp = outbuf; |
1406 | 6.74k | ok = write_prefix(&obp, &outbuflen, "O: ", 3); |
1407 | | /* |
1408 | | * Not safe to check for 'ok' here as a lot of variables may get |
1409 | | * partially changed due to lack of outbuflen */ |
1410 | 11.3M | while (length > 0 && *opt != COAP_PAYLOAD_START) { |
1411 | 11.3M | coap_opt_t *opt_last = opt; |
1412 | 11.3M | size_t optsize = next_option_safe(&opt, &length, &pdu->max_opt); |
1413 | 11.3M | const uint32_t len = |
1414 | 11.3M | optsize ? coap_opt_length((const uint8_t *)opt - optsize) : 0; |
1415 | 11.3M | if (!optsize || (COAP_PDU_IS_SIGNALING(pdu) ? |
1416 | 753k | !coap_pdu_parse_opt_csm(pdu, len) : |
1417 | 11.3M | !coap_pdu_parse_opt_base(pdu, len))) { |
1418 | 229k | ok = ok && write_prefix(&obp, &outbuflen, "*", 1); |
1419 | 229k | if (!optsize) { |
1420 | | /* Skip to end of options to output all data */ |
1421 | 2.01k | opt = pdu->token + pdu->used_size; |
1422 | 2.01k | length = 0; |
1423 | 2.01k | } |
1424 | 11.1M | } else { |
1425 | 11.1M | ok = ok && write_prefix(&obp, &outbuflen, " ", 1); |
1426 | 11.1M | } |
1427 | 11.3M | tlen = opt - opt_last; |
1428 | 161M | while (tlen--) { |
1429 | 150M | ok = ok && write_char(&obp, &outbuflen, *opt_last, i); |
1430 | 150M | opt_last++; |
1431 | 150M | } |
1432 | 11.3M | } |
1433 | 6.74k | if (length && *opt == COAP_PAYLOAD_START) { |
1434 | 276 | write_char(&obp, &outbuflen, *opt, i); |
1435 | 276 | } |
1436 | | /* write_*() always leaves a spare byte to null terminate */ |
1437 | 6.74k | *obp = '\000'; |
1438 | 6.74k | coap_log_debug("%s\n", outbuf); |
1439 | 6.74k | } |
1440 | 3.37k | } |
1441 | | |
1442 | 10.8k | if (length > 0) { |
1443 | 1.31k | assert(*opt == COAP_PAYLOAD_START); |
1444 | 1.31k | opt++; |
1445 | 1.31k | length--; |
1446 | | |
1447 | 1.31k | if (length == 0) { |
1448 | 76 | coap_log_debug("coap_pdu_parse: message ending in payload start marker\n"); |
1449 | 76 | return 0; |
1450 | 76 | } |
1451 | 1.31k | } |
1452 | 10.7k | if (length > 0) |
1453 | 1.23k | pdu->data = (uint8_t *)opt; |
1454 | 9.49k | else |
1455 | 9.49k | pdu->data = NULL; |
1456 | 10.7k | } |
1457 | | |
1458 | 10.7k | return good; |
1459 | 10.8k | } |
1460 | | |
1461 | | int |
1462 | | coap_pdu_parse(coap_proto_t proto, |
1463 | | const uint8_t *data, |
1464 | | size_t length, |
1465 | 10.9k | coap_pdu_t *pdu) { |
1466 | 10.9k | size_t hdr_size; |
1467 | | |
1468 | 10.9k | if (length == 0) |
1469 | 0 | return 0; |
1470 | 10.9k | hdr_size = coap_pdu_parse_header_size(proto, data); |
1471 | 10.9k | if (!hdr_size || hdr_size > length) |
1472 | 18 | return 0; |
1473 | 10.9k | if (hdr_size > pdu->max_hdr_size) |
1474 | 0 | return 0; |
1475 | 10.9k | if (!coap_pdu_resize(pdu, length - hdr_size)) |
1476 | 0 | return 0; |
1477 | 10.9k | if (pdu->token - hdr_size != data) |
1478 | 10.9k | memcpy(pdu->token - hdr_size, data, length); |
1479 | 10.9k | pdu->hdr_size = (uint8_t)hdr_size; |
1480 | 10.9k | pdu->used_size = length - hdr_size; |
1481 | 10.9k | return coap_pdu_parse_header(pdu, proto) && coap_pdu_parse_opt(pdu); |
1482 | 10.9k | } |
1483 | | |
1484 | | size_t |
1485 | 7.40k | coap_pdu_encode_header(coap_pdu_t *pdu, coap_proto_t proto) { |
1486 | 7.40k | uint8_t e_token_length; |
1487 | | |
1488 | 7.40k | if (pdu->actual_token.length < COAP_TOKEN_EXT_1B_BIAS) { |
1489 | 7.18k | e_token_length = (uint8_t)pdu->actual_token.length; |
1490 | 7.18k | } else if (pdu->actual_token.length < COAP_TOKEN_EXT_2B_BIAS) { |
1491 | 74 | e_token_length = COAP_TOKEN_EXT_1B_TKL; |
1492 | 149 | } else if (pdu->actual_token.length <= COAP_TOKEN_EXT_MAX) { |
1493 | 112 | e_token_length = COAP_TOKEN_EXT_2B_TKL; |
1494 | 112 | } else { |
1495 | 37 | coap_log_warn("coap_add_token: Token size too large. PDU ignored\n"); |
1496 | 37 | return 0; |
1497 | 37 | } |
1498 | 7.36k | if (COAP_PROTO_NOT_RELIABLE(proto)) { |
1499 | 2.47k | assert(pdu->max_hdr_size >= 4); |
1500 | 2.47k | if (pdu->max_hdr_size < 4) { |
1501 | 0 | coap_log_warn("coap_pdu_encode_header: not enough space for UDP-style header\n"); |
1502 | 0 | return 0; |
1503 | 0 | } |
1504 | 2.47k | pdu->token[-4] = COAP_DEFAULT_VERSION << 6 |
1505 | 2.47k | | pdu->type << 4 |
1506 | 2.47k | | e_token_length; |
1507 | 2.47k | pdu->token[-3] = pdu->code; |
1508 | 2.47k | pdu->token[-2] = (uint8_t)(pdu->mid >> 8); |
1509 | 2.47k | pdu->token[-1] = (uint8_t)(pdu->mid); |
1510 | 2.47k | pdu->hdr_size = 4; |
1511 | 2.47k | #if !COAP_DISABLE_TCP |
1512 | 4.89k | } else if (COAP_PROTO_RELIABLE(proto)) { |
1513 | 4.89k | size_t len; |
1514 | 4.89k | assert(pdu->used_size >= pdu->e_token_length); |
1515 | 4.89k | if (pdu->used_size < pdu->e_token_length) { |
1516 | 0 | coap_log_warn("coap_pdu_encode_header: corrupted PDU\n"); |
1517 | 0 | return 0; |
1518 | 0 | } |
1519 | | |
1520 | | /* A lot of the reliable code assumes type is CON */ |
1521 | 4.89k | if (pdu->type != COAP_MESSAGE_CON) |
1522 | 0 | pdu->type = COAP_MESSAGE_CON; |
1523 | | |
1524 | 4.89k | if (proto == COAP_PROTO_WS || proto == COAP_PROTO_WSS) |
1525 | 2.40k | len = 0; |
1526 | 2.48k | else |
1527 | 2.48k | len = pdu->used_size - pdu->e_token_length; |
1528 | 4.89k | if (len <= COAP_MAX_MESSAGE_SIZE_TCP0) { |
1529 | 3.67k | assert(pdu->max_hdr_size >= 2); |
1530 | 3.67k | if (pdu->max_hdr_size < 2) { |
1531 | 0 | coap_log_warn("coap_pdu_encode_header: not enough space for TCP0 header\n"); |
1532 | 0 | return 0; |
1533 | 0 | } |
1534 | 3.67k | pdu->token[-2] = (uint8_t)len << 4 |
1535 | 3.67k | | e_token_length; |
1536 | 3.67k | pdu->token[-1] = pdu->code; |
1537 | 3.67k | pdu->hdr_size = 2; |
1538 | 3.67k | } else if (len <= COAP_MAX_MESSAGE_SIZE_TCP8) { |
1539 | 786 | assert(pdu->max_hdr_size >= 3); |
1540 | 786 | if (pdu->max_hdr_size < 3) { |
1541 | 0 | coap_log_warn("coap_pdu_encode_header: not enough space for TCP8 header\n"); |
1542 | 0 | return 0; |
1543 | 0 | } |
1544 | 786 | pdu->token[-3] = 13 << 4 | e_token_length; |
1545 | 786 | pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP8); |
1546 | 786 | pdu->token[-1] = pdu->code; |
1547 | 786 | pdu->hdr_size = 3; |
1548 | 786 | } else if (len <= COAP_MAX_MESSAGE_SIZE_TCP16) { |
1549 | 365 | assert(pdu->max_hdr_size >= 4); |
1550 | 365 | if (pdu->max_hdr_size < 4) { |
1551 | 0 | coap_log_warn("coap_pdu_encode_header: not enough space for TCP16 header\n"); |
1552 | 0 | return 0; |
1553 | 0 | } |
1554 | 365 | pdu->token[-4] = 14 << 4 | e_token_length; |
1555 | 365 | pdu->token[-3] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP16) >> 8); |
1556 | 365 | pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP16); |
1557 | 365 | pdu->token[-1] = pdu->code; |
1558 | 365 | pdu->hdr_size = 4; |
1559 | 365 | } else { |
1560 | 69 | assert(pdu->max_hdr_size >= 6); |
1561 | 69 | if (pdu->max_hdr_size < 6) { |
1562 | 0 | coap_log_warn("coap_pdu_encode_header: not enough space for TCP32 header\n"); |
1563 | 0 | return 0; |
1564 | 0 | } |
1565 | 69 | pdu->token[-6] = 15 << 4 | e_token_length; |
1566 | 69 | pdu->token[-5] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 24); |
1567 | 69 | pdu->token[-4] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 16); |
1568 | 69 | pdu->token[-3] = (uint8_t)((len - COAP_MESSAGE_SIZE_OFFSET_TCP32) >> 8); |
1569 | 69 | pdu->token[-2] = (uint8_t)(len - COAP_MESSAGE_SIZE_OFFSET_TCP32); |
1570 | 69 | pdu->token[-1] = pdu->code; |
1571 | 69 | pdu->hdr_size = 6; |
1572 | 69 | } |
1573 | 4.89k | #endif /* ! COAP_DISABLE_TCP */ |
1574 | 4.89k | } else { |
1575 | 0 | coap_log_warn("coap_pdu_encode_header: unsupported protocol\n"); |
1576 | 0 | } |
1577 | 7.36k | return pdu->hdr_size; |
1578 | 7.36k | } |
1579 | | |
1580 | | coap_pdu_code_t |
1581 | 0 | coap_pdu_get_code(const coap_pdu_t *pdu) { |
1582 | 0 | return pdu->code; |
1583 | 0 | } |
1584 | | |
1585 | | void |
1586 | 0 | coap_pdu_set_code(coap_pdu_t *pdu, coap_pdu_code_t code) { |
1587 | 0 | #ifndef RIOT_VERSION |
1588 | 0 | assert(code <= 0xff); |
1589 | 0 | #endif /* RIOT_VERSION */ |
1590 | 0 | pdu->code = code; |
1591 | 0 | } |
1592 | | |
1593 | | coap_pdu_type_t |
1594 | 0 | coap_pdu_get_type(const coap_pdu_t *pdu) { |
1595 | 0 | return pdu->type; |
1596 | 0 | } |
1597 | | |
1598 | | void |
1599 | 0 | coap_pdu_set_type(coap_pdu_t *pdu, coap_pdu_type_t type) { |
1600 | 0 | assert(type <= 0x3); |
1601 | 0 | pdu->type = type; |
1602 | 0 | } |
1603 | | |
1604 | | coap_bin_const_t |
1605 | 0 | coap_pdu_get_token(const coap_pdu_t *pdu) { |
1606 | 0 | return pdu->actual_token; |
1607 | 0 | } |
1608 | | |
1609 | | coap_mid_t |
1610 | 0 | coap_pdu_get_mid(const coap_pdu_t *pdu) { |
1611 | 0 | return pdu->mid; |
1612 | 0 | } |
1613 | | |
1614 | | void |
1615 | 0 | coap_pdu_set_mid(coap_pdu_t *pdu, coap_mid_t mid) { |
1616 | 0 | #if (UINT_MAX > 65535) |
1617 | 0 | assert(mid >= 0 && mid <= 0xffff); |
1618 | 0 | #endif /* UINT_MAX > 65535 */ |
1619 | 0 | pdu->mid = mid; |
1620 | 0 | } |