/src/libcoap/src/coap_block.c
Line | Count | Source |
1 | | /* coap_block.c -- block transfer |
2 | | * |
3 | | * Copyright (C) 2010--2012,2015-2026 Olaf Bergmann <bergmann@tzi.org> and others |
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_block.c |
13 | | * @brief CoAP Block handling |
14 | | */ |
15 | | |
16 | | #include "coap3/coap_libcoap_build.h" |
17 | | |
18 | | #include <stdio.h> |
19 | | |
20 | | #ifndef min |
21 | 0 | #define min(a,b) ((a) < (b) ? (a) : (b)) |
22 | | #endif |
23 | | |
24 | | /* Can be 1 - 8 bytes long */ |
25 | | #ifndef COAP_ETAG_MAX_BYTES |
26 | 0 | #define COAP_ETAG_MAX_BYTES 4 |
27 | | #endif |
28 | | #if COAP_ETAG_MAX_BYTES < 1 || COAP_ETAG_MAX_BYTES > 8 |
29 | | #error COAP_ETAG_MAX_BYTES byte size invalid |
30 | | #endif |
31 | | |
32 | 0 | #define COAP_LG_XMIT_TXT_SCALAR (8) |
33 | | |
34 | | #if COAP_Q_BLOCK_SUPPORT |
35 | | static int blocks_delete_entry(coap_rblock_t *rec_blocks, uint32_t block_num); |
36 | | static int blocks_add_entry(coap_rblock_t *rec_blocks, uint32_t block_num, uint32_t block_m); |
37 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
38 | | |
39 | | #if COAP_Q_BLOCK_SUPPORT |
40 | | int |
41 | 0 | coap_q_block_is_supported(void) { |
42 | 0 | return 1; |
43 | 0 | } |
44 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
45 | | int |
46 | | coap_q_block_is_supported(void) { |
47 | | return 0; |
48 | | } |
49 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
50 | | |
51 | | unsigned int |
52 | 0 | coap_opt_block_num(const coap_opt_t *block_opt) { |
53 | 0 | unsigned int num = 0; |
54 | 0 | uint16_t len; |
55 | |
|
56 | 0 | len = coap_opt_length(block_opt); |
57 | |
|
58 | 0 | if (len == 0) { |
59 | 0 | return 0; |
60 | 0 | } |
61 | | |
62 | 0 | if (len > 1) { |
63 | 0 | num = coap_decode_var_bytes(coap_opt_value(block_opt), |
64 | 0 | coap_opt_length(block_opt) - 1); |
65 | 0 | } |
66 | |
|
67 | 0 | return (num << 4) | ((COAP_OPT_BLOCK_END_BYTE(block_opt) & 0xF0) >> 4); |
68 | 0 | } |
69 | | |
70 | | int |
71 | | coap_get_block_b(const coap_session_t *session, const coap_pdu_t *pdu, |
72 | 0 | coap_option_num_t number, coap_block_b_t *block) { |
73 | 0 | coap_opt_iterator_t opt_iter; |
74 | 0 | coap_opt_t *option; |
75 | |
|
76 | 0 | assert(block); |
77 | 0 | memset(block, 0, sizeof(coap_block_b_t)); |
78 | |
|
79 | 0 | if (pdu && (option = coap_check_option(pdu, number, &opt_iter)) != NULL) { |
80 | 0 | uint32_t num; |
81 | |
|
82 | 0 | if (COAP_OPT_BLOCK_MORE(option)) |
83 | 0 | block->m = 1; |
84 | 0 | block->aszx = block->szx = COAP_OPT_BLOCK_SZX(option); |
85 | 0 | if (block->szx == 7) { |
86 | 0 | size_t length; |
87 | 0 | const uint8_t *data; |
88 | |
|
89 | 0 | if (session == NULL || COAP_PROTO_NOT_RELIABLE(session->proto) || |
90 | 0 | !(session->csm_bert_rem_support && session->csm_bert_loc_support)) |
91 | | /* No BERT support */ |
92 | 0 | return 0; |
93 | | |
94 | 0 | block->szx = 6; /* BERT is 1024 block chunks */ |
95 | 0 | block->bert = 1; |
96 | 0 | if (coap_get_data(pdu, &length, &data)) { |
97 | 0 | if (block->m && (length % 1024) != 0) { |
98 | 0 | coap_log_debug("block: Oversized packet - reduced to %" PRIuS " from %" PRIuS "\n", |
99 | 0 | length - (length % 1024), length); |
100 | 0 | length -= length % 1024; |
101 | 0 | } |
102 | 0 | block->chunk_size = (uint32_t)length; |
103 | 0 | } else |
104 | 0 | block->chunk_size = 0; |
105 | 0 | } else { |
106 | 0 | block->chunk_size = (size_t)1 << (block->szx + 4); |
107 | 0 | } |
108 | 0 | block->defined = 1; |
109 | | |
110 | | /* The block number is at most 20 bits, so values above 2^20 - 1 |
111 | | * are illegal. */ |
112 | 0 | num = coap_opt_block_num(option); |
113 | 0 | if (num > 0xFFFFF) { |
114 | 0 | return 0; |
115 | 0 | } |
116 | 0 | block->num = num; |
117 | 0 | return 1; |
118 | 0 | } |
119 | | |
120 | 0 | return 0; |
121 | 0 | } |
122 | | |
123 | | int |
124 | | coap_get_block(const coap_pdu_t *pdu, coap_option_num_t number, |
125 | 0 | coap_block_t *block) { |
126 | 0 | coap_block_b_t block_b; |
127 | |
|
128 | 0 | assert(block); |
129 | 0 | memset(block, 0, sizeof(coap_block_t)); |
130 | |
|
131 | 0 | if (coap_get_block_b(NULL, pdu, number, &block_b)) { |
132 | 0 | block->num = block_b.num; |
133 | 0 | block->m = block_b.m; |
134 | 0 | block->szx = block_b.szx; |
135 | 0 | return 1; |
136 | 0 | } |
137 | 0 | return 0; |
138 | 0 | } |
139 | | |
140 | | static int |
141 | | setup_block_b(coap_session_t *session, coap_pdu_t *pdu, coap_block_b_t *block, |
142 | | unsigned int num, |
143 | 0 | unsigned int blk_size, size_t total) { |
144 | 0 | size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; |
145 | 0 | size_t avail = pdu->max_size - token_options; |
146 | 0 | unsigned int start = num << (blk_size + 4); |
147 | 0 | unsigned int can_use_bert = block->defined == 0 || block->bert; |
148 | |
|
149 | 0 | assert(start <= total); |
150 | 0 | memset(block, 0, sizeof(*block)); |
151 | 0 | block->num = num; |
152 | 0 | block->szx = block->aszx = blk_size; |
153 | 0 | if (can_use_bert && blk_size == 6 && avail >= 1024 && session != NULL && |
154 | 0 | COAP_PROTO_RELIABLE(session->proto) && |
155 | 0 | session->csm_bert_rem_support && session->csm_bert_loc_support) { |
156 | 0 | block->bert = 1; |
157 | 0 | block->aszx = 7; |
158 | 0 | block->chunk_size = (uint32_t)((avail / 1024) * 1024); |
159 | 0 | } else { |
160 | 0 | block->chunk_size = (size_t)1 << (blk_size + 4); |
161 | 0 | if (avail < block->chunk_size && (total - start) >= avail) { |
162 | | /* Need to reduce block size */ |
163 | 0 | unsigned int szx; |
164 | 0 | int new_blk_size; |
165 | |
|
166 | 0 | if (avail < 16) { /* bad luck, this is the smallest block size */ |
167 | 0 | coap_log_debug("not enough space, even the smallest block does not fit (1)\n"); |
168 | 0 | return 0; |
169 | 0 | } |
170 | 0 | new_blk_size = coap_flsll((long long)avail) - 5; |
171 | 0 | coap_log_debug("decrease block size for %" PRIuS " to %d\n", avail, new_blk_size); |
172 | 0 | szx = block->szx; |
173 | 0 | block->szx = new_blk_size; |
174 | 0 | block->num <<= szx - block->szx; |
175 | 0 | block->chunk_size = (size_t)1 << (new_blk_size + 4); |
176 | 0 | } |
177 | 0 | } |
178 | 0 | block->m = block->chunk_size < total - start; |
179 | 0 | return 1; |
180 | 0 | } |
181 | | |
182 | | int |
183 | | coap_write_block_opt(coap_block_t *block, coap_option_num_t number, |
184 | 0 | coap_pdu_t *pdu, size_t data_length) { |
185 | 0 | size_t start; |
186 | 0 | unsigned char buf[4]; |
187 | 0 | coap_block_b_t block_b; |
188 | |
|
189 | 0 | assert(pdu); |
190 | | |
191 | 0 | start = block->num << (block->szx + 4); |
192 | 0 | if (block->num != 0 && data_length <= start) { |
193 | 0 | coap_log_debug("illegal block requested\n"); |
194 | 0 | return -2; |
195 | 0 | } |
196 | | |
197 | 0 | assert(pdu->max_size > 0); |
198 | | |
199 | 0 | block_b.defined = 1; |
200 | 0 | block_b.bert = 0; |
201 | 0 | if (!setup_block_b(NULL, pdu, &block_b, block->num, |
202 | 0 | block->szx, data_length)) |
203 | 0 | return -3; |
204 | | |
205 | | /* to re-encode the block option */ |
206 | 0 | coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf), |
207 | 0 | ((block_b.num << 4) | |
208 | 0 | (block_b.m << 3) | |
209 | 0 | block_b.szx)), |
210 | 0 | buf); |
211 | |
|
212 | 0 | return 1; |
213 | 0 | } |
214 | | |
215 | | int |
216 | | coap_write_block_b_opt(coap_session_t *session, coap_block_b_t *block, |
217 | | coap_option_num_t number, |
218 | 0 | coap_pdu_t *pdu, size_t data_length) { |
219 | 0 | size_t start; |
220 | 0 | unsigned char buf[4]; |
221 | |
|
222 | 0 | assert(pdu); |
223 | | |
224 | 0 | start = block->num << (block->szx + 4); |
225 | 0 | if (block->num != 0 && data_length <= start) { |
226 | 0 | coap_log_debug("illegal block requested\n"); |
227 | 0 | return -2; |
228 | 0 | } |
229 | | |
230 | 0 | assert(pdu->max_size > 0); |
231 | | |
232 | 0 | if (!setup_block_b(session, pdu, block, block->num, |
233 | 0 | block->szx, data_length)) |
234 | 0 | return -3; |
235 | | |
236 | | /* to re-encode the block option */ |
237 | 0 | coap_update_option(pdu, number, coap_encode_var_safe(buf, sizeof(buf), |
238 | 0 | ((block->num << 4) | |
239 | 0 | (block->m << 3) | |
240 | 0 | block->aszx)), |
241 | 0 | buf); |
242 | |
|
243 | 0 | return 1; |
244 | 0 | } |
245 | | |
246 | | int |
247 | | coap_add_block(coap_pdu_t *pdu, size_t len, const uint8_t *data, |
248 | 0 | unsigned int block_num, unsigned char block_szx) { |
249 | 0 | unsigned int start; |
250 | 0 | start = block_num << (block_szx + 4); |
251 | |
|
252 | 0 | if (len <= start) |
253 | 0 | return 0; |
254 | | |
255 | 0 | return coap_add_data(pdu, |
256 | 0 | min(len - start, ((size_t)1 << (block_szx + 4))), |
257 | 0 | data + start); |
258 | 0 | } |
259 | | |
260 | | int |
261 | | coap_add_block_b_data(coap_pdu_t *pdu, size_t len, const uint8_t *data, |
262 | 0 | coap_block_b_t *block) { |
263 | 0 | unsigned int start = block->num << (block->szx + 4); |
264 | 0 | size_t max_size; |
265 | |
|
266 | 0 | if (len <= start) |
267 | 0 | return 0; |
268 | | |
269 | 0 | if (block->bert) { |
270 | 0 | size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; |
271 | 0 | max_size = ((pdu->max_size - token_options) / 1024) * 1024; |
272 | 0 | } else { |
273 | 0 | max_size = (size_t)1 << (block->szx + 4); |
274 | 0 | } |
275 | 0 | block->chunk_size = (uint32_t)max_size; |
276 | |
|
277 | 0 | return coap_add_data(pdu, |
278 | 0 | min(len - start, max_size), |
279 | 0 | data + start); |
280 | 0 | } |
281 | | |
282 | | /* |
283 | | * Note that the COAP_OPTION_ have to be added in the correct order |
284 | | */ |
285 | | void |
286 | | coap_add_data_blocked_response(const coap_pdu_t *request, |
287 | | coap_pdu_t *response, |
288 | | uint16_t media_type, |
289 | | int maxage, |
290 | | size_t length, |
291 | | const uint8_t *data |
292 | 0 | ) { |
293 | 0 | unsigned char buf[4]; |
294 | 0 | coap_block_t block2; |
295 | 0 | int block2_requested = 0; |
296 | 0 | #if COAP_SERVER_SUPPORT |
297 | 0 | uint64_t etag = 0; |
298 | 0 | coap_digest_t digest; |
299 | 0 | coap_digest_ctx_t *dctx = NULL; |
300 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
301 | |
|
302 | 0 | memset(&block2, 0, sizeof(block2)); |
303 | | /* |
304 | | * Need to check that a valid block is getting asked for so that the |
305 | | * correct options are put into the PDU. |
306 | | */ |
307 | 0 | if (request) { |
308 | 0 | if (coap_get_block(request, COAP_OPTION_BLOCK2, &block2)) { |
309 | 0 | block2_requested = 1; |
310 | 0 | if (block2.num != 0 && length <= (block2.num << (block2.szx + 4))) { |
311 | 0 | coap_log_debug("Illegal block requested (%d > last = %" PRIuS ")\n", |
312 | 0 | block2.num, |
313 | 0 | length >> (block2.szx + 4)); |
314 | 0 | response->code = COAP_RESPONSE_CODE(400); |
315 | 0 | goto error; |
316 | 0 | } |
317 | 0 | } |
318 | 0 | } |
319 | 0 | response->code = COAP_RESPONSE_CODE(205); |
320 | |
|
321 | 0 | #if COAP_SERVER_SUPPORT |
322 | | /* add ETag for the resource data */ |
323 | 0 | if (length) { |
324 | 0 | dctx = coap_digest_setup(); |
325 | 0 | if (!dctx) |
326 | 0 | goto error; |
327 | 0 | if (request && request->session && |
328 | 0 | coap_is_mcast(&request->session->addr_info.local)) { |
329 | 0 | coap_digest_update(dctx, coap_unique_id, sizeof(coap_unique_id)); |
330 | 0 | } |
331 | 0 | if (!coap_digest_update(dctx, data, length)) |
332 | 0 | goto error; |
333 | 0 | if (!coap_digest_final(dctx, &digest)) |
334 | 0 | goto error; |
335 | 0 | dctx = NULL; |
336 | 0 | memcpy(&etag, digest.key, sizeof(etag)); |
337 | 0 | #if COAP_ETAG_MAX_BYTES != 8 |
338 | 0 | etag = etag >> 8*(8 - COAP_ETAG_MAX_BYTES); |
339 | 0 | #endif |
340 | 0 | if (!etag) |
341 | 0 | etag = 1; |
342 | 0 | coap_update_option(response, |
343 | 0 | COAP_OPTION_ETAG, |
344 | 0 | coap_encode_var_safe8(buf, sizeof(buf), etag), |
345 | 0 | buf); |
346 | 0 | } |
347 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
348 | | |
349 | 0 | coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT, |
350 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
351 | 0 | media_type), |
352 | 0 | buf); |
353 | |
|
354 | 0 | if (maxage >= 0) { |
355 | 0 | coap_insert_option(response, |
356 | 0 | COAP_OPTION_MAXAGE, |
357 | 0 | coap_encode_var_safe(buf, sizeof(buf), maxage), buf); |
358 | 0 | } |
359 | |
|
360 | 0 | if (block2_requested) { |
361 | 0 | int res; |
362 | |
|
363 | 0 | res = coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length); |
364 | |
|
365 | 0 | switch (res) { |
366 | 0 | case -2: /* illegal block (caught above) */ |
367 | 0 | response->code = COAP_RESPONSE_CODE(400); |
368 | 0 | goto error; |
369 | 0 | case -1: /* should really not happen */ |
370 | 0 | assert(0); |
371 | | /* fall through if assert is a no-op */ |
372 | 0 | case -3: /* cannot handle request */ |
373 | 0 | response->code = COAP_RESPONSE_CODE(500); |
374 | 0 | goto error; |
375 | 0 | default: /* everything is good */ |
376 | 0 | ; |
377 | 0 | } |
378 | | |
379 | 0 | coap_add_option_internal(response, |
380 | 0 | COAP_OPTION_SIZE2, |
381 | 0 | coap_encode_var_safe8(buf, sizeof(buf), length), |
382 | 0 | buf); |
383 | |
|
384 | 0 | coap_add_block(response, length, data, |
385 | 0 | block2.num, block2.szx); |
386 | 0 | return; |
387 | 0 | } |
388 | | |
389 | | /* |
390 | | * Block2 not requested |
391 | | */ |
392 | 0 | if (!coap_add_data(response, length, data)) { |
393 | | /* |
394 | | * Insufficient space to add in data - use block mode |
395 | | * set initial block size, will be lowered by |
396 | | * coap_write_block_opt() automatically |
397 | | */ |
398 | 0 | block2.num = 0; |
399 | 0 | block2.szx = 6; |
400 | 0 | coap_write_block_opt(&block2, COAP_OPTION_BLOCK2, response, length); |
401 | |
|
402 | 0 | coap_add_option_internal(response, |
403 | 0 | COAP_OPTION_SIZE2, |
404 | 0 | coap_encode_var_safe8(buf, sizeof(buf), length), |
405 | 0 | buf); |
406 | |
|
407 | 0 | coap_add_block(response, length, data, |
408 | 0 | block2.num, block2.szx); |
409 | 0 | } |
410 | 0 | return; |
411 | | |
412 | 0 | error: |
413 | 0 | #if COAP_SERVER_SUPPORT |
414 | 0 | coap_digest_free(dctx); |
415 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
416 | 0 | coap_add_data(response, |
417 | 0 | strlen(coap_response_phrase(response->code)), |
418 | 0 | (const unsigned char *)coap_response_phrase(response->code)); |
419 | 0 | } |
420 | | |
421 | | COAP_API void |
422 | | coap_context_set_block_mode(coap_context_t *context, |
423 | 0 | uint32_t block_mode) { |
424 | 0 | coap_lock_lock(return); |
425 | 0 | coap_context_set_block_mode_lkd(context, block_mode); |
426 | 0 | coap_lock_unlock(); |
427 | 0 | } |
428 | | |
429 | | void |
430 | | coap_context_set_block_mode_lkd(coap_context_t *context, |
431 | 0 | uint32_t block_mode) { |
432 | 0 | coap_lock_check_locked(); |
433 | 0 | if (!(block_mode & COAP_BLOCK_USE_LIBCOAP)) |
434 | 0 | block_mode = 0; |
435 | 0 | context->block_mode &= ~COAP_BLOCK_SET_MASK; |
436 | 0 | context->block_mode |= block_mode & COAP_BLOCK_SET_MASK; |
437 | | #if ! COAP_Q_BLOCK_SUPPORT |
438 | | if (block_mode & (COAP_BLOCK_TRY_Q_BLOCK|COAP_BLOCK_USE_M_Q_BLOCK|COAP_BLOCK_FORCE_Q_BLOCK)) |
439 | | coap_log_debug("Q-Block support not compiled in - ignored\n"); |
440 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
441 | 0 | } |
442 | | |
443 | | COAP_API int |
444 | | coap_context_set_max_block_size(coap_context_t *context, |
445 | 0 | size_t max_block_size) { |
446 | 0 | int ret; |
447 | |
|
448 | 0 | coap_lock_lock(return 0); |
449 | 0 | ret = coap_context_set_max_block_size_lkd(context, max_block_size); |
450 | 0 | coap_lock_unlock(); |
451 | 0 | return ret; |
452 | 0 | } |
453 | | |
454 | | int |
455 | 0 | coap_context_set_max_block_size_lkd(coap_context_t *context, size_t max_block_size) { |
456 | 0 | switch (max_block_size) { |
457 | 0 | case 0: |
458 | 0 | case 16: |
459 | 0 | case 32: |
460 | 0 | case 64: |
461 | 0 | case 128: |
462 | 0 | case 256: |
463 | 0 | case 512: |
464 | 0 | case 1024: |
465 | 0 | break; |
466 | 0 | default: |
467 | 0 | coap_log_info("coap_context_set_max_block_size: Invalid max block size (%" PRIuS ")\n", |
468 | 0 | max_block_size); |
469 | 0 | return 0; |
470 | 0 | } |
471 | 0 | coap_lock_check_locked(); |
472 | 0 | max_block_size = (coap_fls((uint32_t)max_block_size >> 4) - 1) & 0x07; |
473 | 0 | context->block_mode &= ~COAP_BLOCK_MAX_SIZE_MASK; |
474 | 0 | context->block_mode |= COAP_BLOCK_MAX_SIZE_SET((uint32_t)max_block_size); |
475 | 0 | return 1; |
476 | 0 | } |
477 | | |
478 | | COAP_STATIC_INLINE int |
479 | | full_match(const uint8_t *a, size_t alen, |
480 | 0 | const uint8_t *b, size_t blen) { |
481 | 0 | return alen == blen && (alen == 0 || memcmp(a, b, alen) == 0); |
482 | 0 | } |
483 | | |
484 | | coap_lg_xmit_t * |
485 | 0 | coap_find_lg_xmit(coap_session_t *session, coap_pdu_t *pdu) { |
486 | 0 | coap_lg_xmit_t *lg_xmit = NULL; |
487 | 0 | coap_lg_xmit_t *m_lg_xmit = NULL; |
488 | 0 | uint64_t token_match = |
489 | 0 | STATE_TOKEN_BASE(coap_decode_var_bytes8(pdu->actual_token.s, |
490 | 0 | pdu->actual_token.length)); |
491 | |
|
492 | 0 | LL_FOREACH(session->lg_xmit, lg_xmit) { |
493 | 0 | if (token_match != STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) && |
494 | 0 | !coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) { |
495 | | /* try out the next one */ |
496 | 0 | continue; |
497 | 0 | } |
498 | 0 | #if COAP_CLIENT_SUPPORT |
499 | 0 | if (COAP_PDU_IS_RESPONSE(pdu)) { |
500 | 0 | if (coap_is_mcast(&lg_xmit->b.b1.upstream)) { |
501 | 0 | m_lg_xmit = lg_xmit; |
502 | 0 | } |
503 | 0 | if (!coap_address_equals(&lg_xmit->b.b1.upstream, &session->addr_info.remote)) { |
504 | | /* try out the next one */ |
505 | 0 | continue; |
506 | 0 | } |
507 | 0 | } |
508 | | /* Have a match */ |
509 | 0 | return lg_xmit; |
510 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
511 | 0 | } |
512 | 0 | if (m_lg_xmit && (session->sock.flags & COAP_SOCKET_MULTICAST)) { |
513 | | /* Need to set up unicast version of mcast lg_xmit entry */ |
514 | 0 | lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t)); |
515 | 0 | if (!lg_xmit) |
516 | 0 | return NULL; |
517 | 0 | memcpy(lg_xmit, m_lg_xmit, sizeof(coap_lg_xmit_t)); |
518 | 0 | lg_xmit->next = NULL; |
519 | 0 | lg_xmit->b.b1.app_token = NULL; |
520 | 0 | lg_xmit->data_info->ref++; |
521 | 0 | lg_xmit->sent_pdu = coap_pdu_reference_lkd(m_lg_xmit->sent_pdu); |
522 | 0 | coap_address_copy(&lg_xmit->b.b1.upstream, &session->addr_info.remote); |
523 | 0 | lg_xmit->b.b1.app_token = coap_new_binary(m_lg_xmit->b.b1.app_token->length); |
524 | 0 | if (!lg_xmit->b.b1.app_token) |
525 | 0 | goto fail; |
526 | 0 | if (m_lg_xmit->b.b1.app_token->length) |
527 | 0 | memcpy(lg_xmit->b.b1.app_token->s, m_lg_xmit->b.b1.app_token->s, |
528 | 0 | m_lg_xmit->b.b1.app_token->length); |
529 | 0 | LL_PREPEND(session->lg_xmit, lg_xmit); |
530 | 0 | coap_log_debug("** %s: lg_xmit %p mcast slave initialized\n", |
531 | 0 | coap_session_str(session), (void *)lg_xmit); |
532 | | /* Allow the mcast lg_xmit to time out earlier */ |
533 | 0 | coap_ticks(&m_lg_xmit->last_all_sent); |
534 | 0 | #if COAP_CLIENT_SUPPORT |
535 | 0 | if (COAP_PDU_IS_RESPONSE(pdu)) { |
536 | 0 | coap_lg_crcv_t *lg_crcv; |
537 | |
|
538 | 0 | lg_crcv = coap_find_lg_crcv(session, pdu); |
539 | 0 | if (lg_crcv) { |
540 | 0 | lg_xmit->b.b1.state_token = lg_crcv->state_token; |
541 | 0 | } |
542 | 0 | } |
543 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
544 | 0 | } |
545 | 0 | return lg_xmit; |
546 | | |
547 | 0 | fail: |
548 | 0 | coap_block_delete_lg_xmit(session, lg_xmit); |
549 | 0 | return NULL; |
550 | 0 | } |
551 | | |
552 | | #if COAP_CLIENT_SUPPORT |
553 | | |
554 | | COAP_API int |
555 | | coap_cancel_observe(coap_session_t *session, coap_binary_t *token, |
556 | 0 | coap_pdu_type_t type) { |
557 | 0 | int ret; |
558 | |
|
559 | 0 | coap_lock_lock(return 0); |
560 | 0 | ret = coap_cancel_observe_lkd(session, token, type); |
561 | 0 | coap_lock_unlock(); |
562 | 0 | return ret; |
563 | 0 | } |
564 | | |
565 | | int |
566 | | coap_cancel_observe_lkd(coap_session_t *session, coap_binary_t *token, |
567 | 0 | coap_pdu_type_t type) { |
568 | 0 | coap_lg_crcv_t *lg_crcv, *q; |
569 | |
|
570 | 0 | assert(session); |
571 | 0 | if (!session) |
572 | 0 | return 0; |
573 | | |
574 | 0 | coap_lock_check_locked(); |
575 | 0 | if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) { |
576 | 0 | coap_log_debug("** %s: coap_cancel_observe: COAP_BLOCK_USE_LIBCOAP not enabled\n", |
577 | 0 | coap_session_str(session)); |
578 | 0 | return 0; |
579 | 0 | } |
580 | | |
581 | 0 | LL_FOREACH_SAFE(session->lg_crcv, lg_crcv, q) { |
582 | 0 | if (lg_crcv->observe_set) { |
583 | 0 | if ((!token && !lg_crcv->app_token->length) || (token && |
584 | 0 | coap_binary_equal(token, lg_crcv->app_token))) { |
585 | 0 | uint8_t buf[8]; |
586 | 0 | coap_mid_t mid; |
587 | 0 | size_t size; |
588 | 0 | const uint8_t *data; |
589 | 0 | #if COAP_Q_BLOCK_SUPPORT |
590 | 0 | coap_block_b_t block; |
591 | 0 | int using_q_block1 = coap_get_block_b(session, lg_crcv->sent_pdu, |
592 | 0 | COAP_OPTION_Q_BLOCK1, &block); |
593 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
594 | 0 | coap_bin_const_t *otoken = lg_crcv->obs_token ? |
595 | 0 | lg_crcv->obs_token[0] ? |
596 | 0 | lg_crcv->obs_token[0] : |
597 | 0 | (coap_bin_const_t *)lg_crcv->app_token : |
598 | 0 | (coap_bin_const_t *)lg_crcv->app_token; |
599 | 0 | coap_pdu_t *pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, |
600 | 0 | session, |
601 | 0 | otoken->length, |
602 | 0 | otoken->s, |
603 | 0 | NULL, |
604 | 0 | COAP_BOOL_FALSE); |
605 | |
|
606 | 0 | lg_crcv->observe_set = 0; |
607 | 0 | if (pdu == NULL) |
608 | 0 | return 0; |
609 | | /* Need to make sure that this is the correct requested type */ |
610 | 0 | pdu->type = type; |
611 | |
|
612 | 0 | coap_update_option(pdu, COAP_OPTION_OBSERVE, |
613 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
614 | 0 | COAP_OBSERVE_CANCEL), |
615 | 0 | buf); |
616 | 0 | if (lg_crcv->o_block_option) { |
617 | 0 | coap_update_option(pdu, lg_crcv->o_block_option, |
618 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
619 | 0 | lg_crcv->o_blk_size), |
620 | 0 | buf); |
621 | 0 | } |
622 | 0 | if (lg_crcv->obs_data) { |
623 | 0 | coap_add_data_large_request_lkd(session, pdu, |
624 | 0 | lg_crcv->obs_data->length, |
625 | 0 | lg_crcv->obs_data->data, NULL, NULL); |
626 | 0 | } else if (coap_get_data(lg_crcv->sent_pdu, &size, &data)) { |
627 | 0 | coap_add_data_large_request_lkd(session, pdu, size, data, NULL, NULL); |
628 | 0 | } |
629 | | |
630 | | /* |
631 | | * Need to fix lg_xmit stateless token as using tokens from |
632 | | * observe setup |
633 | | */ |
634 | 0 | if (pdu->lg_xmit) |
635 | 0 | pdu->lg_xmit->b.b1.state_token = lg_crcv->state_token; |
636 | |
|
637 | 0 | coap_address_copy(&session->addr_info.remote, &lg_crcv->upstream); |
638 | 0 | #if COAP_Q_BLOCK_SUPPORT |
639 | | /* See if large xmit using Q-Block1 (but not testing Q-Block1) */ |
640 | 0 | if (using_q_block1) { |
641 | 0 | mid = coap_send_q_block1(session, block, pdu, COAP_SEND_INC_PDU); |
642 | 0 | } else { |
643 | 0 | mid = coap_send_internal(session, pdu, NULL); |
644 | 0 | } |
645 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
646 | | mid = coap_send_internal(session, pdu, NULL); |
647 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
648 | 0 | if (mid == COAP_INVALID_MID) |
649 | 0 | break; |
650 | 0 | } |
651 | 0 | } |
652 | 0 | } |
653 | 0 | return 1; |
654 | 0 | } |
655 | | |
656 | | coap_lg_crcv_t * |
657 | 0 | coap_find_lg_crcv(coap_session_t *session, coap_pdu_t *pdu) { |
658 | 0 | coap_lg_crcv_t *lg_crcv; |
659 | 0 | coap_lg_crcv_t *m_lg_crcv = NULL; |
660 | 0 | uint64_t token_match = |
661 | 0 | STATE_TOKEN_BASE(coap_decode_var_bytes8(pdu->actual_token.s, |
662 | 0 | pdu->actual_token.length)); |
663 | |
|
664 | 0 | LL_FOREACH(session->lg_crcv, lg_crcv) { |
665 | 0 | if (token_match != STATE_TOKEN_BASE(lg_crcv->state_token) && |
666 | 0 | !coap_binary_equal(&pdu->actual_token, lg_crcv->app_token)) { |
667 | | /* try out the next one */ |
668 | 0 | continue; |
669 | 0 | } |
670 | 0 | if (coap_is_mcast(&lg_crcv->upstream)) { |
671 | 0 | m_lg_crcv = lg_crcv; |
672 | 0 | } |
673 | 0 | if (!coap_address_equals(&lg_crcv->upstream, &session->addr_info.remote)) { |
674 | | /* try out the next one */ |
675 | 0 | continue; |
676 | 0 | } |
677 | | /* Have a match */ |
678 | 0 | return lg_crcv; |
679 | 0 | } |
680 | 0 | if (m_lg_crcv && (session->sock.flags & COAP_SOCKET_MULTICAST)) { |
681 | | /* Need to set up unicast version of mcast lg_crcv entry */ |
682 | 0 | lg_crcv = coap_block_new_lg_crcv(session, m_lg_crcv->sent_pdu, NULL); |
683 | 0 | if (lg_crcv) { |
684 | 0 | if (m_lg_crcv->obs_data) { |
685 | 0 | m_lg_crcv->obs_data->ref++; |
686 | 0 | lg_crcv->obs_data = m_lg_crcv->obs_data; |
687 | 0 | } |
688 | 0 | LL_PREPEND(session->lg_crcv, lg_crcv); |
689 | 0 | } |
690 | 0 | } |
691 | 0 | return lg_crcv; |
692 | 0 | } |
693 | | |
694 | | #if COAP_OSCORE_SUPPORT |
695 | | coap_mid_t |
696 | | coap_retransmit_oscore_pdu(coap_session_t *session, |
697 | | coap_pdu_t *pdu, |
698 | 0 | coap_opt_t *echo) { |
699 | 0 | coap_lg_crcv_t *lg_crcv; |
700 | 0 | uint8_t ltoken[8]; |
701 | 0 | size_t ltoken_len; |
702 | 0 | uint64_t token; |
703 | 0 | const uint8_t *data; |
704 | 0 | size_t data_len; |
705 | 0 | coap_pdu_t *resend_pdu; |
706 | 0 | coap_block_b_t block; |
707 | |
|
708 | 0 | lg_crcv = coap_find_lg_crcv(session, pdu); |
709 | 0 | if (lg_crcv) { |
710 | 0 | coap_opt_iterator_t opt_iter; |
711 | 0 | coap_opt_t *opt = coap_check_option(pdu, COAP_OPTION_OBSERVE, |
712 | 0 | &opt_iter); |
713 | | /* Re-send request with new token */ |
714 | 0 | token = STATE_TOKEN_FULL(lg_crcv->state_token, |
715 | 0 | ++lg_crcv->retry_counter); |
716 | 0 | ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token); |
717 | | /* There could be a Block option in pdu */ |
718 | 0 | resend_pdu = coap_pdu_duplicate_lkd(pdu, session, ltoken_len, |
719 | 0 | ltoken, NULL, COAP_BOOL_FALSE); |
720 | 0 | if (!resend_pdu) |
721 | 0 | goto error; |
722 | 0 | if (echo) { |
723 | 0 | coap_insert_option(resend_pdu, COAP_OPTION_ECHO, coap_opt_length(echo), |
724 | 0 | coap_opt_value(echo)); |
725 | 0 | } |
726 | 0 | if (opt) { |
727 | 0 | if (!lg_crcv->obs_token) { |
728 | 0 | lg_crcv->obs_token = coap_malloc_type(COAP_STRING, sizeof(lg_crcv->obs_token[0])); |
729 | 0 | if (!lg_crcv->obs_token) { |
730 | 0 | return COAP_INVALID_MID; |
731 | 0 | } |
732 | 0 | lg_crcv->obs_token_cnt = 1; |
733 | 0 | } else { |
734 | 0 | coap_delete_bin_const(lg_crcv->obs_token[0]); |
735 | 0 | } |
736 | 0 | lg_crcv->obs_token[0] = coap_new_bin_const(ltoken, ltoken_len); |
737 | 0 | if (lg_crcv->obs_token[0] == NULL) |
738 | 0 | return COAP_INVALID_MID; |
739 | 0 | } |
740 | 0 | if (coap_get_data(lg_crcv->sent_pdu, &data_len, &data)) { |
741 | 0 | if (coap_get_block_b(session, resend_pdu, COAP_OPTION_BLOCK1, &block)) { |
742 | 0 | if (data_len > block.chunk_size && block.chunk_size != 0) { |
743 | 0 | data_len = block.chunk_size; |
744 | 0 | } |
745 | 0 | } |
746 | 0 | coap_add_data(resend_pdu, data_len, data); |
747 | 0 | } |
748 | |
|
749 | 0 | return coap_send_internal(session, resend_pdu, NULL); |
750 | 0 | } |
751 | 0 | error: |
752 | 0 | return COAP_INVALID_MID; |
753 | 0 | } |
754 | | #endif /* COAP_OSCORE_SUPPORT */ |
755 | | #endif /* COAP_CLIENT_SUPPORT */ |
756 | | |
757 | | #if COAP_SERVER_SUPPORT |
758 | | /* |
759 | | * Find the response lg_xmit |
760 | | */ |
761 | | coap_lg_xmit_t * |
762 | | coap_find_lg_xmit_response(const coap_session_t *session, |
763 | | const coap_pdu_t *request, |
764 | | const coap_resource_t *resource, |
765 | 0 | const coap_string_t *query) { |
766 | 0 | coap_lg_xmit_t *lg_xmit; |
767 | 0 | coap_opt_iterator_t opt_iter; |
768 | 0 | coap_opt_t *rtag_opt = coap_check_option(request, |
769 | 0 | COAP_OPTION_RTAG, |
770 | 0 | &opt_iter); |
771 | 0 | size_t rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0; |
772 | 0 | const uint8_t *rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL; |
773 | |
|
774 | 0 | LL_FOREACH(session->lg_xmit, lg_xmit) { |
775 | 0 | static coap_string_t empty = { 0, NULL}; |
776 | |
|
777 | 0 | if (COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu) || |
778 | 0 | resource != lg_xmit->b.b2.resource || |
779 | 0 | request->code != lg_xmit->b.b2.request_method || |
780 | 0 | !coap_string_equal(query ? query : &empty, |
781 | 0 | lg_xmit->b.b2.query ? |
782 | 0 | lg_xmit->b.b2.query : &empty)) { |
783 | | /* try out the next one */ |
784 | 0 | continue; |
785 | 0 | } |
786 | | /* lg_xmit is a response */ |
787 | 0 | if (rtag_opt || lg_xmit->b.b2.rtag_set == 1) { |
788 | 0 | if (!(rtag_opt && lg_xmit->b.b2.rtag_set == 1)) |
789 | 0 | continue; |
790 | 0 | if (lg_xmit->b.b2.rtag_length != rtag_length || |
791 | 0 | memcmp(lg_xmit->b.b2.rtag, rtag, rtag_length) != 0) |
792 | 0 | continue; |
793 | 0 | } |
794 | 0 | return lg_xmit; |
795 | 0 | } |
796 | 0 | return NULL; |
797 | 0 | } |
798 | | #endif /* COAP_SERVER_SUPPORT */ |
799 | | |
800 | | static int |
801 | | coap_add_data_large_internal(coap_session_t *session, |
802 | | const coap_pdu_t *request, |
803 | | coap_pdu_t *pdu, |
804 | | coap_resource_t *resource, |
805 | | const coap_string_t *query, |
806 | | int maxage, |
807 | | uint64_t etag, |
808 | | size_t length, |
809 | | const uint8_t *data, |
810 | | coap_release_large_data_t release_func, |
811 | | coap_get_large_data_t get_func, |
812 | | void *app_ptr, |
813 | 0 | int single_request, coap_pdu_code_t request_method) { |
814 | |
|
815 | 0 | ssize_t avail; |
816 | 0 | coap_block_b_t block; |
817 | 0 | #if COAP_Q_BLOCK_SUPPORT |
818 | 0 | coap_block_b_t alt_block; |
819 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
820 | 0 | size_t chunk; |
821 | 0 | coap_lg_xmit_t *lg_xmit = NULL; |
822 | 0 | uint8_t buf[8]; |
823 | 0 | int have_block_defined = 0; |
824 | 0 | uint8_t blk_size; |
825 | 0 | uint8_t max_blk_size; |
826 | 0 | uint16_t option; |
827 | 0 | size_t token_options; |
828 | 0 | coap_opt_t *opt; |
829 | 0 | coap_opt_iterator_t opt_iter; |
830 | 0 | #if COAP_Q_BLOCK_SUPPORT |
831 | 0 | uint16_t alt_option; |
832 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
833 | |
|
834 | | #if !COAP_SERVER_SUPPORT |
835 | | (void)etag; |
836 | | #endif /* COAP_SERVER_SUPPORT */ |
837 | |
|
838 | 0 | assert(pdu); |
839 | 0 | if (pdu->data) { |
840 | 0 | coap_log_warn("coap_add_data_large: PDU already contains data\n"); |
841 | 0 | if (release_func) { |
842 | 0 | coap_lock_callback(release_func(session, app_ptr)); |
843 | 0 | } |
844 | 0 | return 0; |
845 | 0 | } |
846 | | |
847 | 0 | if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) { |
848 | 0 | coap_log_debug("** %s: coap_add_data_large: COAP_BLOCK_USE_LIBCOAP not enabled\n", |
849 | 0 | coap_session_str(session)); |
850 | 0 | goto add_data; |
851 | 0 | } |
852 | | |
853 | | /* A lot of the reliable code assumes type is CON */ |
854 | 0 | if (COAP_PROTO_RELIABLE(session->proto) && pdu->type == COAP_MESSAGE_NON) |
855 | 0 | pdu->type = COAP_MESSAGE_CON; |
856 | | |
857 | | /* Block NUM max 20 bits (starting from 0) and block size is "2**(SZX + 4)" |
858 | | and using SZX max of 6 gives maximum size = 1,073,740,800 |
859 | | CSM Max-Message-Size theoretical maximum = 4,294,967,295 |
860 | | So, if using blocks, we are limited to 1,073,740,800. |
861 | | */ |
862 | 0 | #define MAX_BLK_LEN ((1UL << 20) * (1 << (6 + 4))) |
863 | | #if UINT_MAX < MAX_BLK_LEN |
864 | | #undef MAX_BLK_LEN |
865 | | #define MAX_BLK_LEN UINT_MAX |
866 | | #endif |
867 | |
|
868 | 0 | if (length > MAX_BLK_LEN) { |
869 | 0 | coap_log_warn("Size of large buffer restricted to 0x%lx bytes\n", MAX_BLK_LEN); |
870 | 0 | length = MAX_BLK_LEN; |
871 | 0 | } |
872 | |
|
873 | 0 | #if COAP_SERVER_SUPPORT |
874 | | /* Possible response code not yet set, so check if not request */ |
875 | 0 | if (!COAP_PDU_IS_REQUEST(pdu) && length) { |
876 | 0 | coap_opt_t *etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter); |
877 | |
|
878 | 0 | if (etag_opt) { |
879 | | /* Have to use ETag as supplied in the response PDU */ |
880 | 0 | etag = coap_decode_var_bytes8(coap_opt_value(etag_opt), |
881 | 0 | coap_opt_length(etag_opt)); |
882 | 0 | } else { |
883 | 0 | if (!etag) { |
884 | | /* calculate ETag for the response */ |
885 | 0 | coap_digest_t digest; |
886 | 0 | coap_digest_ctx_t *dctx = coap_digest_setup(); |
887 | |
|
888 | 0 | if (dctx) { |
889 | 0 | if (coap_is_mcast(&session->addr_info.local)) { |
890 | 0 | (void)coap_digest_update(dctx, coap_unique_id, sizeof(coap_unique_id)); |
891 | 0 | } |
892 | 0 | if (coap_digest_update(dctx, data, length)) { |
893 | 0 | if (coap_digest_final(dctx, &digest)) { |
894 | 0 | memcpy(&etag, digest.key, sizeof(etag)); |
895 | 0 | #if COAP_ETAG_MAX_BYTES != 8 |
896 | 0 | etag = etag >> 8*(8 - COAP_ETAG_MAX_BYTES); |
897 | 0 | #endif |
898 | 0 | dctx = NULL; |
899 | 0 | } |
900 | 0 | } |
901 | 0 | coap_digest_free(dctx); |
902 | 0 | } |
903 | 0 | if (!etag) |
904 | 0 | etag = 1; |
905 | 0 | } |
906 | 0 | coap_update_option(pdu, |
907 | 0 | COAP_OPTION_ETAG, |
908 | 0 | coap_encode_var_safe8(buf, sizeof(buf), etag), |
909 | 0 | buf); |
910 | 0 | } |
911 | 0 | if (request) { |
912 | 0 | etag_opt = coap_check_option(request, COAP_OPTION_ETAG, &opt_iter); |
913 | 0 | if (etag_opt) { |
914 | | /* There may be multiple ETag - need to check each one */ |
915 | 0 | coap_option_iterator_init(request, &opt_iter, COAP_OPT_ALL); |
916 | 0 | while ((etag_opt = coap_option_next(&opt_iter))) { |
917 | 0 | if (opt_iter.number == COAP_OPTION_ETAG) { |
918 | 0 | uint64_t etag_r = coap_decode_var_bytes8(coap_opt_value(etag_opt), |
919 | 0 | coap_opt_length(etag_opt)); |
920 | |
|
921 | 0 | if (etag == etag_r) { |
922 | 0 | pdu->code = COAP_RESPONSE_CODE(203); |
923 | 0 | return 1; |
924 | 0 | } |
925 | 0 | } |
926 | 0 | } |
927 | 0 | } |
928 | 0 | } |
929 | 0 | } |
930 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
931 | | |
932 | | /* Determine the block size to use, adding in sensible options if needed */ |
933 | 0 | if (COAP_PDU_IS_REQUEST(pdu)) { |
934 | 0 | coap_lg_xmit_t *q; |
935 | |
|
936 | 0 | #if COAP_Q_BLOCK_SUPPORT |
937 | 0 | if (session->block_mode & (COAP_BLOCK_HAS_Q_BLOCK|COAP_BLOCK_TRY_Q_BLOCK)) { |
938 | 0 | option = COAP_OPTION_Q_BLOCK1; |
939 | 0 | alt_option = COAP_OPTION_BLOCK1; |
940 | 0 | } else { |
941 | 0 | option = COAP_OPTION_BLOCK1; |
942 | 0 | alt_option = COAP_OPTION_Q_BLOCK1; |
943 | 0 | } |
944 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
945 | | if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) { |
946 | | coap_remove_option(pdu, COAP_OPTION_Q_BLOCK1); |
947 | | } |
948 | | option = COAP_OPTION_BLOCK1; |
949 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
950 | | |
951 | | /* See if this token is already in use for large bodies (unlikely) */ |
952 | 0 | LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) { |
953 | 0 | if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) { |
954 | | /* Unfortunately need to free this off as potential size change */ |
955 | 0 | int is_mcast = 0; |
956 | 0 | coap_tick_t last_all_sent = lg_xmit->last_all_sent; |
957 | 0 | #if COAP_CLIENT_SUPPORT |
958 | 0 | is_mcast = coap_is_mcast(&lg_xmit->b.b1.upstream); |
959 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
960 | 0 | LL_DELETE(session->lg_xmit, lg_xmit); |
961 | 0 | coap_block_delete_lg_xmit(session, lg_xmit); |
962 | 0 | lg_xmit = NULL; |
963 | 0 | if (!is_mcast && !last_all_sent) |
964 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session); |
965 | 0 | break; |
966 | 0 | } |
967 | 0 | } |
968 | 0 | } else { |
969 | | /* Have to assume that it is a response even if code is 0.00 */ |
970 | 0 | assert(resource); |
971 | 0 | #if COAP_Q_BLOCK_SUPPORT |
972 | 0 | if (session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) { |
973 | 0 | option = COAP_OPTION_Q_BLOCK2; |
974 | 0 | alt_option = COAP_OPTION_BLOCK2; |
975 | 0 | } else { |
976 | 0 | option = COAP_OPTION_BLOCK2; |
977 | 0 | alt_option = COAP_OPTION_Q_BLOCK2; |
978 | 0 | } |
979 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
980 | | if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) { |
981 | | coap_remove_option(pdu, COAP_OPTION_Q_BLOCK2); |
982 | | } |
983 | | option = COAP_OPTION_BLOCK2; |
984 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
985 | 0 | #if COAP_SERVER_SUPPORT |
986 | | /* |
987 | | * Check if resource+query+rtag is already in use for large bodies |
988 | | * (unlikely) |
989 | | */ |
990 | 0 | lg_xmit = coap_find_lg_xmit_response(session, request, resource, query); |
991 | 0 | if (lg_xmit) { |
992 | 0 | coap_tick_t last_all_sent = lg_xmit->last_all_sent; |
993 | | /* Unfortunately need to free this off as potential size change */ |
994 | 0 | LL_DELETE(session->lg_xmit, lg_xmit); |
995 | 0 | coap_block_delete_lg_xmit(session, lg_xmit); |
996 | 0 | lg_xmit = NULL; |
997 | 0 | if (!last_all_sent) |
998 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session); |
999 | 0 | } |
1000 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
1001 | 0 | } |
1002 | 0 | #if COAP_OSCORE_SUPPORT |
1003 | 0 | if (session->oscore_encryption) { |
1004 | | /* Need to convert Proxy-Uri to Proxy-Scheme option if needed */ |
1005 | 0 | if (COAP_PDU_IS_REQUEST(pdu) && !coap_rebuild_pdu_for_proxy(pdu)) |
1006 | 0 | goto fail; |
1007 | 0 | } |
1008 | 0 | #endif /* COAP_OSCORE_SUPPORT */ |
1009 | | |
1010 | 0 | token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; |
1011 | 0 | avail = pdu->max_size - token_options; |
1012 | | /* There may be a response with Echo option */ |
1013 | 0 | avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40); |
1014 | 0 | #if COAP_OSCORE_SUPPORT |
1015 | 0 | avail -= coap_oscore_overhead(session, pdu); |
1016 | 0 | #endif /* COAP_OSCORE_SUPPORT */ |
1017 | | /* May need token of length 8, so account for this */ |
1018 | 0 | avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0; |
1019 | |
|
1020 | 0 | if (avail < 16) { |
1021 | 0 | blk_size = 0; |
1022 | 0 | } else { |
1023 | 0 | blk_size = coap_flsll((long long)avail) - 4 - 1; |
1024 | 0 | } |
1025 | 0 | if (blk_size > 6) |
1026 | 0 | blk_size = 6; |
1027 | |
|
1028 | 0 | max_blk_size = COAP_BLOCK_MAX_SIZE_GET(session->block_mode); |
1029 | 0 | if (max_blk_size && blk_size > max_blk_size) |
1030 | 0 | blk_size = max_blk_size; |
1031 | | |
1032 | | /* see if BlockX defined - if so update blk_size as given by app */ |
1033 | 0 | if (coap_get_block_b(session, pdu, option, &block)) { |
1034 | 0 | if (block.szx < blk_size) |
1035 | 0 | blk_size = block.szx; |
1036 | 0 | have_block_defined = 1; |
1037 | 0 | } |
1038 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1039 | | /* see if alternate BlockX defined */ |
1040 | 0 | if (coap_get_block_b(session, pdu, alt_option, &alt_block)) { |
1041 | 0 | if (have_block_defined) { |
1042 | | /* Cannot have both options set */ |
1043 | 0 | coap_log_warn("Both BlockX and Q-BlockX cannot be set at the same time\n"); |
1044 | 0 | coap_remove_option(pdu, alt_option); |
1045 | 0 | } else { |
1046 | 0 | block = alt_block; |
1047 | 0 | if (block.szx < blk_size) |
1048 | 0 | blk_size = block.szx; |
1049 | 0 | have_block_defined = 1; |
1050 | 0 | option = alt_option; |
1051 | 0 | } |
1052 | 0 | } |
1053 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1054 | |
|
1055 | 0 | if (avail < 16 && ((ssize_t)length > avail || have_block_defined)) { |
1056 | | /* bad luck, this is the smallest block size */ |
1057 | 0 | coap_log_debug("not enough space, even the smallest block does not fit (2)\n"); |
1058 | 0 | goto fail; |
1059 | 0 | } |
1060 | | |
1061 | 0 | chunk = (size_t)1 << (blk_size + 4); |
1062 | 0 | if ((have_block_defined && block.num != 0) || single_request || |
1063 | 0 | ((session->block_mode & COAP_BLOCK_STLESS_BLOCK2) && session->type != COAP_SESSION_TYPE_CLIENT)) { |
1064 | | /* App is defining a single block to send or we are stateless */ |
1065 | 0 | size_t rem; |
1066 | |
|
1067 | 0 | if (length >= block.num * chunk) { |
1068 | 0 | #if COAP_SERVER_SUPPORT |
1069 | 0 | if (session->block_mode & COAP_BLOCK_STLESS_BLOCK2 && session->type != COAP_SESSION_TYPE_CLIENT) { |
1070 | | /* We are running server stateless */ |
1071 | 0 | coap_update_option(pdu, |
1072 | 0 | COAP_OPTION_SIZE2, |
1073 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1074 | 0 | (unsigned int)length), |
1075 | 0 | buf); |
1076 | 0 | if (request) { |
1077 | 0 | if (!coap_get_block_b(session, request, option, &block)) |
1078 | 0 | block.num = 0; |
1079 | 0 | } |
1080 | 0 | if (!setup_block_b(session, pdu, &block, block.num, |
1081 | 0 | blk_size, length)) |
1082 | 0 | goto fail; |
1083 | | |
1084 | | /* Add in with requested block num, more bit and block size */ |
1085 | 0 | coap_update_option(pdu, |
1086 | 0 | option, |
1087 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1088 | 0 | (block.num << 4) | (block.m << 3) | block.aszx), |
1089 | 0 | buf); |
1090 | 0 | } |
1091 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
1092 | 0 | rem = chunk; |
1093 | 0 | if (chunk > length - block.num * chunk) |
1094 | 0 | rem = length - block.num * chunk; |
1095 | 0 | if (!coap_add_data(pdu, rem, &data[block.num * chunk])) |
1096 | 0 | goto fail; |
1097 | 0 | } |
1098 | 0 | if (release_func) { |
1099 | 0 | coap_lock_callback(release_func(session, app_ptr)); |
1100 | 0 | } |
1101 | 0 | } else if ((have_block_defined && length > chunk) || (ssize_t)length > avail) { |
1102 | | /* Only add in lg_xmit if more than one block needs to be handled */ |
1103 | 0 | size_t rem; |
1104 | |
|
1105 | 0 | lg_xmit = coap_malloc_type(COAP_LG_XMIT, sizeof(coap_lg_xmit_t)); |
1106 | 0 | if (!lg_xmit) |
1107 | 0 | goto fail; |
1108 | | |
1109 | | /* Set up for displaying all the data in the pdu */ |
1110 | 0 | if (!get_func) { |
1111 | 0 | pdu->body_data = data; |
1112 | 0 | pdu->body_length = length; |
1113 | 0 | coap_log_debug("PDU presented by app.\n"); |
1114 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
1115 | 0 | pdu->body_data = NULL; |
1116 | 0 | pdu->body_length = 0; |
1117 | 0 | } else { |
1118 | 0 | coap_log_debug("PDU presented by app without data.\n"); |
1119 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
1120 | 0 | } |
1121 | |
|
1122 | 0 | coap_log_debug("** %s: lg_xmit %p initialized\n", |
1123 | 0 | coap_session_str(session), (void *)lg_xmit); |
1124 | | /* Update lg_xmit with large data information */ |
1125 | 0 | memset(lg_xmit, 0, sizeof(coap_lg_xmit_t)); |
1126 | 0 | lg_xmit->blk_size = blk_size; |
1127 | 0 | lg_xmit->option = option; |
1128 | 0 | lg_xmit->data_info = coap_malloc_type(COAP_STRING, sizeof(coap_lg_xmit_data_t)); |
1129 | 0 | if (!lg_xmit->data_info) |
1130 | 0 | goto fail; |
1131 | 0 | lg_xmit->data_info->ref = 0; |
1132 | 0 | lg_xmit->data_info->data = data; |
1133 | 0 | lg_xmit->data_info->length = length; |
1134 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1135 | 0 | lg_xmit->non_timeout_random_ticks = |
1136 | 0 | coap_get_non_timeout_random_ticks(session); |
1137 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1138 | 0 | lg_xmit->data_info->get_func = get_func; |
1139 | 0 | lg_xmit->data_info->release_func = release_func; |
1140 | 0 | lg_xmit->data_info->app_ptr = app_ptr; |
1141 | 0 | pdu->lg_xmit = lg_xmit; |
1142 | 0 | coap_ticks(&lg_xmit->last_obs); |
1143 | 0 | coap_ticks(&lg_xmit->last_sent); |
1144 | 0 | if (COAP_PDU_IS_REQUEST(pdu)) { |
1145 | | /* Need to keep original token for updating response PDUs */ |
1146 | 0 | lg_xmit->b.b1.app_token = coap_new_binary(pdu->actual_token.length); |
1147 | 0 | if (!lg_xmit->b.b1.app_token) |
1148 | 0 | goto fail; |
1149 | 0 | memcpy(lg_xmit->b.b1.app_token->s, pdu->actual_token.s, |
1150 | 0 | pdu->actual_token.length); |
1151 | | /* |
1152 | | * Need to set up new token for use during transmits |
1153 | | * RFC9177#section-5 |
1154 | | */ |
1155 | 0 | lg_xmit->b.b1.count = 1; |
1156 | 0 | lg_xmit->b.b1.state_token = STATE_TOKEN_FULL(++session->tx_token, |
1157 | 0 | lg_xmit->b.b1.count); |
1158 | 0 | coap_address_copy(&lg_xmit->b.b1.upstream, &session->addr_info.remote); |
1159 | | /* |
1160 | | * Token will be updated in pdu later as original pdu may be needed in |
1161 | | * coap_send() |
1162 | | */ |
1163 | 0 | coap_update_option(pdu, |
1164 | 0 | COAP_OPTION_SIZE1, |
1165 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1166 | 0 | (unsigned int)length), |
1167 | 0 | buf); |
1168 | 0 | if (!coap_check_option(pdu, COAP_OPTION_RTAG, &opt_iter)) |
1169 | 0 | coap_insert_option(pdu, |
1170 | 0 | COAP_OPTION_RTAG, |
1171 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1172 | 0 | ++session->tx_rtag), |
1173 | 0 | buf); |
1174 | 0 | } else { |
1175 | | /* |
1176 | | * resource+query+rtag match is used for Block2 large body transmissions |
1177 | | * token match is used for Block1 large body transmissions |
1178 | | */ |
1179 | 0 | lg_xmit->b.b2.resource = resource; |
1180 | 0 | if (query) { |
1181 | 0 | lg_xmit->b.b2.query = coap_new_string(query->length); |
1182 | 0 | if (lg_xmit->b.b2.query) { |
1183 | 0 | memcpy(lg_xmit->b.b2.query->s, query->s, query->length); |
1184 | 0 | } |
1185 | 0 | } else { |
1186 | 0 | lg_xmit->b.b2.query = NULL; |
1187 | 0 | } |
1188 | 0 | opt = coap_check_option(request, COAP_OPTION_RTAG, &opt_iter); |
1189 | 0 | if (opt) { |
1190 | 0 | lg_xmit->b.b2.rtag_length = (uint8_t)min(coap_opt_length(opt), |
1191 | 0 | sizeof(lg_xmit->b.b2.rtag)); |
1192 | 0 | memcpy(lg_xmit->b.b2.rtag, coap_opt_value(opt), lg_xmit->b.b2.rtag_length); |
1193 | 0 | lg_xmit->b.b2.rtag_set = 1; |
1194 | 0 | } else { |
1195 | 0 | lg_xmit->b.b2.rtag_set = 0; |
1196 | 0 | } |
1197 | 0 | lg_xmit->b.b2.etag = etag; |
1198 | 0 | lg_xmit->b.b2.request_method = request_method; |
1199 | 0 | if (maxage >= 0) { |
1200 | 0 | coap_tick_t now; |
1201 | |
|
1202 | 0 | coap_ticks(&now); |
1203 | 0 | lg_xmit->b.b2.maxage_expire = coap_ticks_to_rt(now) + maxage; |
1204 | 0 | } else { |
1205 | 0 | lg_xmit->b.b2.maxage_expire = 0; |
1206 | 0 | } |
1207 | 0 | coap_update_option(pdu, |
1208 | 0 | COAP_OPTION_SIZE2, |
1209 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1210 | 0 | (unsigned int)length), |
1211 | 0 | buf); |
1212 | 0 | } |
1213 | | |
1214 | 0 | if (!setup_block_b(session, pdu, &block, block.num, |
1215 | 0 | blk_size, lg_xmit->data_info->length)) |
1216 | 0 | goto fail; |
1217 | | |
1218 | | /* Add in with requested block num, more bit and block size */ |
1219 | 0 | coap_update_option(pdu, |
1220 | 0 | lg_xmit->option, |
1221 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1222 | 0 | (block.num << 4) | (block.m << 3) | block.aszx), |
1223 | 0 | buf); |
1224 | | |
1225 | | /* Reference PDU to use as a basis for all the subsequent blocks */ |
1226 | 0 | lg_xmit->sent_pdu = coap_pdu_reference_lkd(pdu); |
1227 | | |
1228 | | /* Check we still have space after adding in some options */ |
1229 | 0 | token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; |
1230 | 0 | avail = pdu->max_size - token_options; |
1231 | | /* There may be a response with Echo option */ |
1232 | 0 | avail -= coap_opt_encode_size(COAP_OPTION_ECHO, 40); |
1233 | | /* May need token of length 8, so account for this */ |
1234 | 0 | avail -= (pdu->actual_token.length < 8) ? 8 - pdu->actual_token.length : 0; |
1235 | 0 | #if COAP_OSCORE_SUPPORT |
1236 | 0 | avail -= coap_oscore_overhead(session, pdu); |
1237 | 0 | #endif /* COAP_OSCORE_SUPPORT */ |
1238 | 0 | if (avail < (ssize_t)chunk) { |
1239 | | /* chunk size change down */ |
1240 | 0 | if (avail < 16) { |
1241 | 0 | coap_log_warn("not enough space, even the smallest block does not fit (3)\n"); |
1242 | 0 | goto fail; |
1243 | 0 | } |
1244 | 0 | blk_size = coap_flsll((long long)avail) - 4 - 1; |
1245 | 0 | block.num = block.num << (lg_xmit->blk_size - blk_size); |
1246 | 0 | lg_xmit->blk_size = blk_size; |
1247 | 0 | chunk = (size_t)1 << (lg_xmit->blk_size + 4); |
1248 | 0 | block.chunk_size = (uint32_t)chunk; |
1249 | 0 | block.bert = 0; |
1250 | 0 | coap_update_option(pdu, |
1251 | 0 | lg_xmit->option, |
1252 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1253 | 0 | (block.num << 4) | (block.m << 3) | lg_xmit->blk_size), |
1254 | 0 | buf); |
1255 | 0 | } |
1256 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1257 | | /* Set up send_blocks */ |
1258 | 0 | lg_xmit->send_blocks.used = 1; |
1259 | 0 | lg_xmit->send_blocks.range[0].end = (uint32_t)((lg_xmit->data_info->length - 1) / chunk); |
1260 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1261 | |
|
1262 | 0 | rem = block.chunk_size; |
1263 | 0 | if (rem > lg_xmit->data_info->length - block.num * chunk) |
1264 | 0 | rem = lg_xmit->data_info->length - block.num * chunk; |
1265 | 0 | if (get_func) { |
1266 | | #if COAP_CONSTRAINED_STACK |
1267 | | /* Protected by global_lock if needed */ |
1268 | | static uint8_t l_data[1024]; |
1269 | | #else /* ! COAP_CONSTRAINED_STACK */ |
1270 | 0 | uint8_t l_data[1024]; |
1271 | 0 | #endif /* ! COAP_CONSTRAINED_STACK */ |
1272 | 0 | size_t l_length; |
1273 | |
|
1274 | 0 | assert(rem <= 1024); |
1275 | 0 | if (get_func(session, rem, block.num * chunk, l_data, &l_length, lg_xmit->data_info->app_ptr)) { |
1276 | 0 | if (!coap_add_data(pdu, l_length, l_data)) { |
1277 | 0 | goto fail; |
1278 | 0 | } |
1279 | 0 | } |
1280 | 0 | } else { |
1281 | 0 | if (!coap_add_data(pdu, rem, &data[block.num * chunk])) |
1282 | 0 | goto fail; |
1283 | 0 | } |
1284 | | |
1285 | 0 | if (COAP_PDU_IS_REQUEST(pdu)) |
1286 | 0 | lg_xmit->b.b1.bert_size = rem; |
1287 | |
|
1288 | 0 | lg_xmit->last_block = -1; |
1289 | | |
1290 | | /* Link the new lg_xmit in */ |
1291 | 0 | LL_PREPEND(session->lg_xmit,lg_xmit); |
1292 | 0 | } else { |
1293 | | /* No need to use blocks */ |
1294 | 0 | if (have_block_defined) { |
1295 | 0 | coap_update_option(pdu, |
1296 | 0 | option, |
1297 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1298 | 0 | (0 << 4) | (0 << 3) | blk_size), buf); |
1299 | 0 | if (COAP_PDU_IS_REQUEST(pdu)) { |
1300 | 0 | coap_update_option(pdu, |
1301 | 0 | COAP_OPTION_SIZE1, |
1302 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1303 | 0 | (unsigned int)length), |
1304 | 0 | buf); |
1305 | 0 | } else { |
1306 | 0 | coap_update_option(pdu, |
1307 | 0 | COAP_OPTION_SIZE2, |
1308 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1309 | 0 | (unsigned int)length), |
1310 | 0 | buf); |
1311 | 0 | } |
1312 | 0 | } |
1313 | 0 | add_data: |
1314 | 0 | if (get_func) { |
1315 | 0 | uint8_t *l_data = coap_malloc_type(COAP_STRING, length); |
1316 | 0 | size_t l_length; |
1317 | |
|
1318 | 0 | if (get_func(session, length, 0, l_data, &l_length, app_ptr)) { |
1319 | 0 | if (!coap_add_data(pdu, l_length, l_data)) { |
1320 | 0 | coap_free_type(COAP_STRING, l_data); |
1321 | 0 | goto fail; |
1322 | 0 | } |
1323 | 0 | coap_free_type(COAP_STRING, l_data); |
1324 | 0 | } |
1325 | 0 | } else { |
1326 | 0 | if (!coap_add_data(pdu, length, data)) |
1327 | 0 | goto fail; |
1328 | 0 | } |
1329 | | |
1330 | 0 | if (release_func) { |
1331 | 0 | coap_lock_callback(release_func(session, app_ptr)); |
1332 | 0 | } |
1333 | 0 | } |
1334 | 0 | return 1; |
1335 | | |
1336 | 0 | fail: |
1337 | 0 | if (lg_xmit) { |
1338 | 0 | coap_block_delete_lg_xmit(session, lg_xmit); |
1339 | 0 | } else if (release_func) { |
1340 | 0 | coap_lock_callback(release_func(session, app_ptr)); |
1341 | 0 | } |
1342 | 0 | return 0; |
1343 | 0 | } |
1344 | | |
1345 | | #if COAP_CLIENT_SUPPORT |
1346 | | COAP_API int |
1347 | | coap_add_data_large_request(coap_session_t *session, |
1348 | | coap_pdu_t *pdu, |
1349 | | size_t length, |
1350 | | const uint8_t *data, |
1351 | | coap_release_large_data_t release_func, |
1352 | | void *app_ptr |
1353 | 0 | ) { |
1354 | 0 | int ret; |
1355 | |
|
1356 | 0 | coap_lock_lock(return 0); |
1357 | 0 | ret = coap_add_data_large_request_lkd(session, pdu, length, data, |
1358 | 0 | release_func, app_ptr); |
1359 | 0 | coap_lock_unlock(); |
1360 | 0 | return ret; |
1361 | 0 | } |
1362 | | |
1363 | | int |
1364 | | coap_add_data_large_request_lkd(coap_session_t *session, |
1365 | | coap_pdu_t *pdu, |
1366 | | size_t length, |
1367 | | const uint8_t *data, |
1368 | | coap_release_large_data_t release_func, |
1369 | 0 | void *app_ptr) { |
1370 | | /* |
1371 | | * Delay if session->doing_first is set. |
1372 | | * E.g. Reliable and CSM not in yet for checking block support |
1373 | | */ |
1374 | 0 | if (coap_client_delay_first(session) == 0) { |
1375 | 0 | if (release_func) { |
1376 | 0 | coap_lock_callback(release_func(session, app_ptr)); |
1377 | 0 | } |
1378 | 0 | return 0; |
1379 | 0 | } |
1380 | 0 | return coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0, |
1381 | 0 | length, data, release_func, NULL, app_ptr, 0, 0); |
1382 | 0 | } |
1383 | | |
1384 | | COAP_API int |
1385 | | coap_add_data_large_request_app(coap_session_t *session, |
1386 | | coap_pdu_t *pdu, |
1387 | | size_t length, |
1388 | | coap_release_large_data_t release_func, |
1389 | | coap_get_large_data_t get_func, |
1390 | 0 | void *app_ptr) { |
1391 | 0 | int ret; |
1392 | |
|
1393 | 0 | coap_lock_lock(return 0); |
1394 | 0 | ret = coap_add_data_large_request_app_lkd(session, pdu, length, |
1395 | 0 | release_func, get_func, app_ptr); |
1396 | 0 | coap_lock_unlock(); |
1397 | 0 | return ret; |
1398 | 0 | } |
1399 | | |
1400 | | int |
1401 | | coap_add_data_large_request_app_lkd(coap_session_t *session, |
1402 | | coap_pdu_t *pdu, |
1403 | | size_t length, |
1404 | | coap_release_large_data_t release_func, |
1405 | | coap_get_large_data_t get_func, |
1406 | 0 | void *app_ptr) { |
1407 | | /* |
1408 | | * Delay if session->doing_first is set. |
1409 | | * E.g. Reliable and CSM not in yet for checking block support |
1410 | | */ |
1411 | 0 | if (coap_client_delay_first(session) == 0) { |
1412 | 0 | return 0; |
1413 | 0 | } |
1414 | 0 | return coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0, |
1415 | 0 | length, NULL, release_func, get_func, |
1416 | 0 | app_ptr, 0, 0); |
1417 | 0 | } |
1418 | | #endif /* ! COAP_CLIENT_SUPPORT */ |
1419 | | |
1420 | | #if COAP_SERVER_SUPPORT |
1421 | | COAP_API int |
1422 | | coap_add_data_large_response(coap_resource_t *resource, |
1423 | | coap_session_t *session, |
1424 | | const coap_pdu_t *request, |
1425 | | coap_pdu_t *response, |
1426 | | const coap_string_t *query, |
1427 | | uint16_t media_type, |
1428 | | int maxage, |
1429 | | uint64_t etag, |
1430 | | size_t length, |
1431 | | const uint8_t *data, |
1432 | | coap_release_large_data_t release_func, |
1433 | 0 | void *app_ptr) { |
1434 | 0 | int ret; |
1435 | |
|
1436 | 0 | coap_lock_lock(return 0); |
1437 | 0 | ret = coap_add_data_large_response_lkd(resource, session, request, |
1438 | 0 | response, query, media_type, maxage, etag, |
1439 | 0 | length, data, release_func, app_ptr); |
1440 | 0 | coap_lock_unlock(); |
1441 | 0 | return ret; |
1442 | 0 | } |
1443 | | |
1444 | | int |
1445 | | coap_add_data_large_response_lkd(coap_resource_t *resource, |
1446 | | coap_session_t *session, |
1447 | | const coap_pdu_t *request, |
1448 | | coap_pdu_t *response, |
1449 | | const coap_string_t *query, |
1450 | | uint16_t media_type, |
1451 | | int maxage, |
1452 | | uint64_t etag, |
1453 | | size_t length, |
1454 | | const uint8_t *data, |
1455 | | coap_release_large_data_t release_func, |
1456 | | void *app_ptr |
1457 | 0 | ) { |
1458 | 0 | unsigned char buf[4]; |
1459 | 0 | coap_block_b_t block; |
1460 | 0 | int block_requested = 0; |
1461 | 0 | int single_request = 0; |
1462 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1463 | 0 | uint32_t block_opt = (session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) ? |
1464 | 0 | COAP_OPTION_Q_BLOCK2 : COAP_OPTION_BLOCK2; |
1465 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
1466 | | uint16_t block_opt = COAP_OPTION_BLOCK2; |
1467 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
1468 | |
|
1469 | 0 | memset(&block, 0, sizeof(block)); |
1470 | | /* |
1471 | | * Need to check that a valid block is getting asked for so that the |
1472 | | * correct options are put into the PDU. |
1473 | | */ |
1474 | 0 | if (request) { |
1475 | 0 | if (coap_get_block_b(session, request, COAP_OPTION_BLOCK2, &block)) { |
1476 | 0 | block_requested = 1; |
1477 | 0 | if (block.num != 0 && length <= (block.num << (block.szx + 4))) { |
1478 | 0 | coap_log_debug("Illegal block requested (%d > last = %" PRIuS ")\n", |
1479 | 0 | block.num, |
1480 | 0 | length >> (block.szx + 4)); |
1481 | 0 | response->code = COAP_RESPONSE_CODE(400); |
1482 | 0 | goto error; |
1483 | 0 | } |
1484 | 0 | } |
1485 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1486 | 0 | else if (coap_get_block_b(session, request, COAP_OPTION_Q_BLOCK2, &block)) { |
1487 | 0 | block_requested = 1; |
1488 | 0 | if (block.num != 0 && length <= (block.num << (block.szx + 4))) { |
1489 | 0 | coap_log_debug("Illegal block requested (%d > last = %" PRIuS ")\n", |
1490 | 0 | block.num, |
1491 | 0 | length >> (block.szx + 4)); |
1492 | 0 | response->code = COAP_RESPONSE_CODE(400); |
1493 | 0 | goto error; |
1494 | 0 | } |
1495 | 0 | if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) { |
1496 | 0 | set_block_mode_has_q(session->block_mode); |
1497 | 0 | block_opt = COAP_OPTION_Q_BLOCK2; |
1498 | 0 | } |
1499 | 0 | if (block.m == 0) |
1500 | 0 | single_request = 1; |
1501 | 0 | } |
1502 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1503 | 0 | } |
1504 | | |
1505 | 0 | coap_update_option(response, COAP_OPTION_CONTENT_FORMAT, |
1506 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1507 | 0 | media_type), |
1508 | 0 | buf); |
1509 | |
|
1510 | 0 | if (maxage >= 0) { |
1511 | 0 | coap_update_option(response, |
1512 | 0 | COAP_OPTION_MAXAGE, |
1513 | 0 | coap_encode_var_safe(buf, sizeof(buf), maxage), buf); |
1514 | 0 | } |
1515 | |
|
1516 | 0 | if (block_requested) { |
1517 | 0 | int res; |
1518 | |
|
1519 | 0 | res = coap_write_block_b_opt(session, &block, block_opt, response, |
1520 | 0 | length); |
1521 | |
|
1522 | 0 | switch (res) { |
1523 | 0 | case -2: /* illegal block (caught above) */ |
1524 | 0 | response->code = COAP_RESPONSE_CODE(400); |
1525 | 0 | goto error; |
1526 | 0 | case -1: /* should really not happen */ |
1527 | 0 | assert(0); |
1528 | | /* fall through if assert is a no-op */ |
1529 | 0 | case -3: /* cannot handle request */ |
1530 | 0 | response->code = COAP_RESPONSE_CODE(500); |
1531 | 0 | goto error; |
1532 | 0 | default: /* everything is good */ |
1533 | 0 | ; |
1534 | 0 | } |
1535 | 0 | } |
1536 | | |
1537 | | /* add data body */ |
1538 | 0 | if (request && |
1539 | 0 | !coap_add_data_large_internal(session, request, response, resource, |
1540 | 0 | query, maxage, etag, length, data, |
1541 | 0 | release_func, NULL, app_ptr, single_request, |
1542 | 0 | request->code)) { |
1543 | 0 | response->code = COAP_RESPONSE_CODE(500); |
1544 | 0 | goto error_released; |
1545 | 0 | } |
1546 | | |
1547 | 0 | return 1; |
1548 | | |
1549 | 0 | error: |
1550 | 0 | if (release_func) { |
1551 | 0 | coap_lock_callback(release_func(session, app_ptr)); |
1552 | 0 | } |
1553 | 0 | error_released: |
1554 | 0 | #if COAP_ERROR_PHRASE_LENGTH > 0 |
1555 | 0 | coap_add_data(response, |
1556 | 0 | strlen(coap_response_phrase(response->code)), |
1557 | 0 | (const unsigned char *)coap_response_phrase(response->code)); |
1558 | 0 | #endif /* COAP_ERROR_PHRASE_LENGTH > 0 */ |
1559 | 0 | return 0; |
1560 | 0 | } |
1561 | | #endif /* ! COAP_SERVER_SUPPORT */ |
1562 | | |
1563 | | /* |
1564 | | * return 1 if there is a future expire time, else 0. |
1565 | | * update tim_rem with remaining value if return is 1. |
1566 | | */ |
1567 | | int |
1568 | | coap_block_check_lg_xmit_timeouts(coap_session_t *session, coap_tick_t now, |
1569 | 0 | coap_tick_t *tim_rem) { |
1570 | 0 | coap_lg_xmit_t *lg_xmit; |
1571 | 0 | coap_lg_xmit_t *q; |
1572 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1573 | 0 | coap_tick_t idle_timeout = COAP_LG_XMIT_TXT_SCALAR * COAP_NON_TIMEOUT_TICKS(session); |
1574 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
1575 | | coap_tick_t idle_timeout = 8 * COAP_TICKS_PER_SECOND; |
1576 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
1577 | 0 | coap_tick_t partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session); |
1578 | 0 | int ret = 0; |
1579 | |
|
1580 | 0 | *tim_rem = COAP_MAX_DELAY_TICKS; |
1581 | |
|
1582 | 0 | LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) { |
1583 | 0 | if (lg_xmit->last_all_sent) { |
1584 | 0 | if (lg_xmit->last_all_sent + idle_timeout <= now) { |
1585 | | /* Expire this entry */ |
1586 | 0 | LL_DELETE(session->lg_xmit, lg_xmit); |
1587 | 0 | coap_block_delete_lg_xmit(session, lg_xmit); |
1588 | 0 | } else { |
1589 | | /* Delay until the lg_xmit needs to expire */ |
1590 | 0 | if (*tim_rem > lg_xmit->last_all_sent + idle_timeout - now) { |
1591 | 0 | *tim_rem = lg_xmit->last_all_sent + idle_timeout - now; |
1592 | 0 | ret = 1; |
1593 | 0 | } |
1594 | 0 | } |
1595 | 0 | } else if (lg_xmit->last_sent) { |
1596 | 0 | if (lg_xmit->last_sent + partial_timeout <= now) { |
1597 | | /* Expire this entry */ |
1598 | 0 | coap_tick_t last_all_sent = lg_xmit->last_all_sent; |
1599 | 0 | int is_mcast = 0; |
1600 | 0 | #if COAP_CLIENT_SUPPORT |
1601 | 0 | is_mcast = COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu) && |
1602 | 0 | coap_is_mcast(&lg_xmit->b.b1.upstream); |
1603 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
1604 | 0 | LL_DELETE(session->lg_xmit, lg_xmit); |
1605 | |
|
1606 | 0 | coap_block_delete_lg_xmit(session, lg_xmit); |
1607 | 0 | if (!is_mcast && !last_all_sent) |
1608 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session); |
1609 | 0 | } else { |
1610 | | /* Delay until the lg_xmit needs to expire */ |
1611 | 0 | if (*tim_rem > lg_xmit->last_sent + partial_timeout - now) { |
1612 | 0 | *tim_rem = lg_xmit->last_sent + partial_timeout - now; |
1613 | 0 | ret = 1; |
1614 | 0 | } |
1615 | 0 | } |
1616 | 0 | } |
1617 | 0 | } |
1618 | 0 | return ret; |
1619 | 0 | } |
1620 | | |
1621 | | #if COAP_CLIENT_SUPPORT |
1622 | | #if COAP_Q_BLOCK_SUPPORT |
1623 | | static coap_pdu_t * |
1624 | 0 | coap_build_missing_pdu(coap_session_t *session, coap_lg_crcv_t *lg_crcv) { |
1625 | 0 | coap_pdu_t *pdu; |
1626 | 0 | coap_opt_filter_t drop_options; |
1627 | 0 | uint64_t token = STATE_TOKEN_FULL(lg_crcv->state_token, ++lg_crcv->retry_counter); |
1628 | 0 | uint8_t buf[8]; |
1629 | 0 | size_t len = coap_encode_var_safe8(buf, sizeof(token), token); |
1630 | |
|
1631 | 0 | memset(&drop_options, 0, sizeof(coap_opt_filter_t)); |
1632 | 0 | coap_option_filter_set(&drop_options, COAP_OPTION_Q_BLOCK1); |
1633 | 0 | coap_option_filter_set(&drop_options, COAP_OPTION_Q_BLOCK2); |
1634 | 0 | coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); |
1635 | 0 | pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf, |
1636 | 0 | &drop_options, COAP_BOOL_FALSE); |
1637 | 0 | if (!pdu) |
1638 | 0 | return NULL; |
1639 | 0 | pdu->type = lg_crcv->last_type; |
1640 | 0 | return pdu; |
1641 | 0 | } |
1642 | | |
1643 | | static void |
1644 | 0 | coap_request_missing_q_block2(coap_session_t *session, coap_lg_crcv_t *lg_crcv) { |
1645 | 0 | uint8_t buf[8]; |
1646 | 0 | uint32_t i; |
1647 | 0 | int block = -1; /* Last one seen */ |
1648 | 0 | size_t sofar; |
1649 | 0 | size_t block_size; |
1650 | 0 | coap_pdu_t *pdu = NULL; |
1651 | 0 | int block_payload_set = -1; |
1652 | |
|
1653 | 0 | if (session->block_mode & COAP_BLOCK_USE_M_Q_BLOCK) { |
1654 | | /* |
1655 | | * See if it is safe to use the single 'M' block variant of request |
1656 | | * |
1657 | | * If any blocks seen, then missing blocks are after range[0].end and |
1658 | | * terminate on the last block or before range[1].begin if set. |
1659 | | * If not defined or range[1].begin is in a different payload set then |
1660 | | * safe to use M bit. |
1661 | | */ |
1662 | 0 | if (lg_crcv->rec_blocks.used && |
1663 | 0 | (lg_crcv->rec_blocks.used < 2 || |
1664 | 0 | ((lg_crcv->rec_blocks.range[0].end + 1) / COAP_MAX_PAYLOADS(session) != |
1665 | 0 | (lg_crcv->rec_blocks.range[1].begin -1) / COAP_MAX_PAYLOADS(session)))) { |
1666 | 0 | block = lg_crcv->rec_blocks.range[0].end + 1; |
1667 | 0 | block_size = (size_t)1 << (lg_crcv->szx + 4); |
1668 | 0 | sofar = block * block_size; |
1669 | 0 | if (sofar < lg_crcv->total_len) { |
1670 | | /* Ask for missing blocks */ |
1671 | 0 | if (pdu == NULL) { |
1672 | 0 | pdu = coap_build_missing_pdu(session, lg_crcv); |
1673 | 0 | if (!pdu) |
1674 | 0 | return; |
1675 | 0 | } |
1676 | 0 | coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2, |
1677 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1678 | 0 | (block << 4) | (1 << 3) | lg_crcv->szx), |
1679 | 0 | buf); |
1680 | 0 | block_payload_set = block / COAP_MAX_PAYLOADS(session); |
1681 | 0 | goto send_it; |
1682 | 0 | } |
1683 | 0 | } |
1684 | 0 | } |
1685 | 0 | block = -1; |
1686 | 0 | for (i = 0; i < lg_crcv->rec_blocks.used; i++) { |
1687 | 0 | if (block < (int)lg_crcv->rec_blocks.range[i].begin && |
1688 | 0 | lg_crcv->rec_blocks.range[i].begin != 0) { |
1689 | | /* Ask for missing blocks */ |
1690 | 0 | if (pdu == NULL) { |
1691 | 0 | pdu = coap_build_missing_pdu(session, lg_crcv); |
1692 | 0 | if (!pdu) |
1693 | 0 | continue; |
1694 | 0 | } |
1695 | 0 | block++; |
1696 | 0 | if (block_payload_set == -1) |
1697 | 0 | block_payload_set = block / COAP_MAX_PAYLOADS(session); |
1698 | 0 | for (; block < (int)lg_crcv->rec_blocks.range[i].begin && |
1699 | 0 | block_payload_set == (block / COAP_MAX_PAYLOADS(session)); block++) { |
1700 | 0 | coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2, |
1701 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1702 | 0 | (block << 4) | (0 << 3) | lg_crcv->szx), |
1703 | 0 | buf); |
1704 | 0 | } |
1705 | 0 | } |
1706 | 0 | if (block < (int)lg_crcv->rec_blocks.range[i].end) { |
1707 | 0 | block = lg_crcv->rec_blocks.range[i].end; |
1708 | 0 | } |
1709 | 0 | } |
1710 | 0 | block_size = (size_t)1 << (lg_crcv->szx + 4); |
1711 | 0 | sofar = (block + 1) * block_size; |
1712 | 0 | if (sofar < lg_crcv->total_len) { |
1713 | | /* Ask for trailing missing blocks */ |
1714 | 0 | if (pdu == NULL) { |
1715 | 0 | pdu = coap_build_missing_pdu(session, lg_crcv); |
1716 | 0 | if (!pdu) |
1717 | 0 | return; |
1718 | 0 | } |
1719 | 0 | sofar = (lg_crcv->total_len + block_size - 1)/block_size; |
1720 | 0 | block++; |
1721 | 0 | if (block_payload_set == -1) |
1722 | 0 | block_payload_set = block / COAP_MAX_PAYLOADS(session); |
1723 | 0 | for (; block < (ssize_t)sofar && |
1724 | 0 | block_payload_set == (block / COAP_MAX_PAYLOADS(session)); block++) { |
1725 | 0 | coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2, |
1726 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1727 | 0 | (block << 4) | (0 << 3) | lg_crcv->szx), |
1728 | 0 | buf); |
1729 | 0 | } |
1730 | 0 | } |
1731 | 0 | send_it: |
1732 | 0 | if (pdu) |
1733 | 0 | coap_send_internal(session, pdu, NULL); |
1734 | 0 | lg_crcv->rec_blocks.retry++; |
1735 | 0 | if (block_payload_set != -1) |
1736 | 0 | lg_crcv->rec_blocks.processing_payload_set = block_payload_set; |
1737 | 0 | coap_ticks(&lg_crcv->rec_blocks.last_seen); |
1738 | 0 | } |
1739 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1740 | | |
1741 | | /* |
1742 | | * return 1 if there is a future expire time, else 0. |
1743 | | * update tim_rem with remaining value if return is 1. |
1744 | | */ |
1745 | | int |
1746 | | coap_block_check_lg_crcv_timeouts(coap_session_t *session, coap_tick_t now, |
1747 | 0 | coap_tick_t *tim_rem) { |
1748 | 0 | coap_lg_crcv_t *lg_crcv; |
1749 | 0 | coap_lg_crcv_t *q; |
1750 | 0 | coap_tick_t partial_timeout; |
1751 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1752 | 0 | coap_tick_t receive_timeout = COAP_NON_RECEIVE_TIMEOUT_TICKS(session); |
1753 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1754 | 0 | int ret = 0; |
1755 | |
|
1756 | 0 | *tim_rem = COAP_MAX_DELAY_TICKS; |
1757 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1758 | 0 | if (COAP_PROTO_NOT_RELIABLE(session->proto) && |
1759 | 0 | session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) |
1760 | 0 | partial_timeout = COAP_NON_PARTIAL_TIMEOUT_TICKS(session); |
1761 | 0 | else |
1762 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1763 | 0 | partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session); |
1764 | |
|
1765 | 0 | LL_FOREACH_SAFE(session->lg_crcv, lg_crcv, q) { |
1766 | 0 | if (COAP_PROTO_RELIABLE(session->proto) || lg_crcv->last_type != COAP_MESSAGE_NON) |
1767 | 0 | goto check_expire; |
1768 | | |
1769 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1770 | 0 | if (lg_crcv->block_option == COAP_OPTION_Q_BLOCK2 && lg_crcv->rec_blocks.used) { |
1771 | 0 | size_t scaled_timeout = receive_timeout * |
1772 | 0 | ((size_t)1 << lg_crcv->rec_blocks.retry); |
1773 | |
|
1774 | 0 | if (lg_crcv->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) { |
1775 | | /* Done NON_MAX_RETRANSMIT retries */ |
1776 | 0 | coap_handle_nack(session, lg_crcv->sent_pdu, |
1777 | 0 | COAP_NACK_TOO_MANY_RETRIES, lg_crcv->sent_pdu->mid); |
1778 | 0 | goto expire; |
1779 | 0 | } |
1780 | 0 | if (lg_crcv->rec_blocks.last_seen + scaled_timeout <= now) { |
1781 | 0 | coap_log_debug("** %s: lg_crcv %p timeout\n", |
1782 | 0 | coap_session_str(session), (void *)lg_crcv); |
1783 | 0 | coap_request_missing_q_block2(session, lg_crcv); |
1784 | 0 | } else { |
1785 | 0 | if (*tim_rem > lg_crcv->rec_blocks.last_seen + scaled_timeout - now) { |
1786 | 0 | *tim_rem = lg_crcv->rec_blocks.last_seen + scaled_timeout - now; |
1787 | 0 | ret = 1; |
1788 | 0 | } |
1789 | 0 | } |
1790 | 0 | } |
1791 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1792 | | /* Used for Block2 and Q-Block2 */ |
1793 | 0 | check_expire: |
1794 | 0 | if (!lg_crcv->observe_set && lg_crcv->last_used && |
1795 | 0 | lg_crcv->last_used + partial_timeout <= now) { |
1796 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1797 | 0 | expire: |
1798 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1799 | | /* Expire this entry */ |
1800 | 0 | LL_DELETE(session->lg_crcv, lg_crcv); |
1801 | 0 | coap_block_delete_lg_crcv(session, lg_crcv); |
1802 | 0 | } else if (!lg_crcv->observe_set && lg_crcv->last_used) { |
1803 | | /* Delay until the lg_crcv needs to expire */ |
1804 | 0 | if (*tim_rem > lg_crcv->last_used + partial_timeout - now) { |
1805 | 0 | *tim_rem = lg_crcv->last_used + partial_timeout - now; |
1806 | 0 | ret = 1; |
1807 | 0 | } |
1808 | 0 | } |
1809 | 0 | } |
1810 | 0 | return ret; |
1811 | 0 | } |
1812 | | #endif /* COAP_CLIENT_SUPPORT */ |
1813 | | |
1814 | | #if COAP_SERVER_SUPPORT |
1815 | | #if COAP_Q_BLOCK_SUPPORT |
1816 | | static coap_pdu_t * |
1817 | 0 | pdu_408_build(coap_session_t *session, coap_lg_srcv_t *lg_srcv) { |
1818 | 0 | coap_pdu_t *pdu; |
1819 | 0 | uint8_t buf[4]; |
1820 | |
|
1821 | 0 | pdu = coap_pdu_init(COAP_MESSAGE_NON, |
1822 | 0 | COAP_RESPONSE_CODE(408), |
1823 | 0 | coap_new_message_id_lkd(session), |
1824 | 0 | coap_session_max_pdu_size_lkd(session)); |
1825 | 0 | if (!pdu) |
1826 | 0 | return NULL; |
1827 | 0 | if (lg_srcv->last_token) |
1828 | 0 | coap_add_token(pdu, lg_srcv->last_token->length, lg_srcv->last_token->s); |
1829 | 0 | coap_add_option_internal(pdu, COAP_OPTION_CONTENT_TYPE, |
1830 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1831 | 0 | COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ), |
1832 | 0 | buf); |
1833 | 0 | pdu->token[pdu->used_size++] = COAP_PAYLOAD_START; |
1834 | 0 | pdu->data = pdu->token + pdu->used_size; |
1835 | 0 | return pdu; |
1836 | 0 | } |
1837 | | |
1838 | | static int |
1839 | 0 | add_408_block(coap_pdu_t *pdu, uint32_t block) { |
1840 | 0 | size_t len; |
1841 | 0 | uint8_t val[8]; |
1842 | |
|
1843 | 0 | assert(block < (1U << 20)); |
1844 | 0 | coap_log_debug("Q-Block1: Requesting missing block %" PRIu32 "\n", block); |
1845 | |
|
1846 | 0 | if (block >= (1U << 20)) { |
1847 | 0 | return 0; |
1848 | 0 | } else if (block < 24) { |
1849 | 0 | len = 1; |
1850 | 0 | val[0] = (uint8_t)block; |
1851 | 0 | } else if (block < 0x100) { |
1852 | 0 | len = 2; |
1853 | 0 | val[0] = 24; |
1854 | 0 | val[1] = (uint8_t)block; |
1855 | 0 | } else if (block < 0x10000) { |
1856 | 0 | len = 3; |
1857 | 0 | val[0] = 25; |
1858 | 0 | val[1] = (uint8_t)((block >> 8) & 0xff); |
1859 | 0 | val[2] = (uint8_t)(block & 0xff); |
1860 | 0 | } else { /* Largest block number is 2^^20 - 1 */ |
1861 | 0 | len = 5; |
1862 | 0 | val[0] = 26; |
1863 | 0 | val[1] = (uint8_t)((block >> 24) & 0xff); /* Will be 0 */ |
1864 | 0 | val[2] = (uint8_t)((block >> 16) & 0xff); |
1865 | 0 | val[3] = (uint8_t)((block >> 8) & 0xff); |
1866 | 0 | val[4] = (uint8_t)(block & 0xff); |
1867 | 0 | } |
1868 | 0 | if (coap_pdu_check_resize(pdu, pdu->used_size + len)) { |
1869 | 0 | memcpy(&pdu->token[pdu->used_size], val, len); |
1870 | 0 | pdu->used_size += len; |
1871 | 0 | return 1; |
1872 | 0 | } |
1873 | 0 | return 0; |
1874 | 0 | } |
1875 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1876 | | #endif /* COAP_SERVER_SUPPORT */ |
1877 | | |
1878 | | static int |
1879 | 0 | check_if_received_block(coap_rblock_t *rec_blocks, uint32_t block_num) { |
1880 | 0 | uint32_t i; |
1881 | |
|
1882 | 0 | for (i = 0; i < rec_blocks->used; i++) { |
1883 | 0 | if (block_num < rec_blocks->range[i].begin) |
1884 | 0 | return 0; |
1885 | 0 | if (block_num <= rec_blocks->range[i].end) |
1886 | 0 | return 1; |
1887 | 0 | } |
1888 | 0 | return 0; |
1889 | 0 | } |
1890 | | |
1891 | | #if COAP_SERVER_SUPPORT |
1892 | | static int |
1893 | 0 | check_if_next_block(coap_rblock_t *rec_blocks, uint32_t block_num) { |
1894 | 0 | if (rec_blocks->used == 0) { |
1895 | 0 | return block_num == 0 ? 1 : 0; |
1896 | 0 | } |
1897 | 0 | if (rec_blocks->range[rec_blocks->used-1].end + 1 == block_num) |
1898 | 0 | return 1; |
1899 | | |
1900 | 0 | return 0; |
1901 | 0 | } |
1902 | | #endif /* COAP_SERVER_SUPPORT */ |
1903 | | |
1904 | | static int |
1905 | 0 | check_all_blocks_in(coap_rblock_t *rec_blocks) { |
1906 | 0 | uint32_t i; |
1907 | 0 | uint32_t block = 0; |
1908 | |
|
1909 | 0 | if (rec_blocks->total_blocks == 0) { |
1910 | | /* Not seen block with More bit unset yet */ |
1911 | 0 | return 0; |
1912 | 0 | } |
1913 | | |
1914 | 0 | for (i = 0; i < rec_blocks->used; i++) { |
1915 | 0 | if (block < rec_blocks->range[i].begin) |
1916 | 0 | return 0; |
1917 | 0 | if (block < rec_blocks->range[i].end) |
1918 | 0 | block = rec_blocks->range[i].end; |
1919 | 0 | } |
1920 | 0 | return 1; |
1921 | 0 | } |
1922 | | |
1923 | | #if COAP_CLIENT_SUPPORT |
1924 | | #if COAP_Q_BLOCK_SUPPORT |
1925 | | static int |
1926 | | check_all_blocks_in_for_payload_set(coap_session_t *session, |
1927 | 0 | coap_rblock_t *rec_blocks) { |
1928 | 0 | if (rec_blocks->used && |
1929 | 0 | (rec_blocks->range[0].end + 1) / COAP_MAX_PAYLOADS(session) > |
1930 | 0 | rec_blocks->processing_payload_set) |
1931 | 0 | return 1; |
1932 | 0 | return 0; |
1933 | 0 | } |
1934 | | |
1935 | | static int |
1936 | | check_any_blocks_next_payload_set(coap_session_t *session, |
1937 | 0 | coap_rblock_t *rec_blocks) { |
1938 | 0 | if (rec_blocks->used > 1 && |
1939 | 0 | rec_blocks->range[1].begin / COAP_MAX_PAYLOADS(session) == |
1940 | 0 | rec_blocks->processing_payload_set) |
1941 | 0 | return 1; |
1942 | 0 | return 0; |
1943 | 0 | } |
1944 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1945 | | #endif /* COAP_CLIENT_SUPPORT */ |
1946 | | |
1947 | | #if COAP_SERVER_SUPPORT |
1948 | | #if COAP_Q_BLOCK_SUPPORT |
1949 | | /* |
1950 | | * To reduce the number of retransmitted missing requests, if the request comes |
1951 | | * from a inc_to_block in the same PAYLOAD_SET as the last time, the request is not |
1952 | | * retransmitted, unless this is from a timeout request when inc_to_block is the |
1953 | | * the last expected block based on SIZE1, not the last received block. |
1954 | | * |
1955 | | * If lg_srcv->rec_blocks.used == 0, then no blocks have been received (not expected). |
1956 | | * If lg_srcv->rec_blocks.used == 1 and lg_srcv->rec_blocks.range[0].begin == 0, |
1957 | | * then no blocks are missing, but the next block may be missing (picked up when |
1958 | | * there is a timeout request). |
1959 | | * If lg_srcv->rec_blocks.used > 1, then there are missing blocks. |
1960 | | * |
1961 | | * The final PAYLOAD_SET is a special case as it can have 1 to MAX_PAYLOADS -1 blocks. |
1962 | | * |
1963 | | * The first PAYLOAD_SET that has missing blocks is reported in the 4.08 code, and |
1964 | | * is triggered when inc_to_block is in the next PAYLOAD_SET (unless in final). |
1965 | | * |
1966 | | * It is not safe to report missing blocks in the same PAYLOAD_SET as inc_to_block as |
1967 | | * the individual blocks may arrive in a random order (generates a lot of duplicate |
1968 | | * blocks). |
1969 | | * |
1970 | | * However, if some missing blocks, but the last 2 blocks of a PAYLOAD_SET have been |
1971 | | * received, then immediately trigger the 4.08 missing blocks request (not suggested |
1972 | | * in RFC9177) as this considerably reduces the risk that the penultimate block is in |
1973 | | * flight, but received in the wrong order and hence requesting a duplicate |
1974 | | * re-transmission. |
1975 | | */ |
1976 | | static int |
1977 | | coap_request_missing_q_block1(coap_session_t *session, coap_lg_srcv_t *lg_srcv, |
1978 | 0 | uint32_t inc_to_block) { |
1979 | 0 | uint32_t i; |
1980 | 0 | int32_t block = -1; /* Last one seen */ |
1981 | 0 | uint32_t block_size = (uint32_t)1 << (lg_srcv->szx + 4); |
1982 | 0 | uint32_t first_missing; |
1983 | 0 | uint32_t missing_payload; |
1984 | 0 | uint32_t last_payload_block; |
1985 | 0 | coap_pdu_t *pdu = NULL; |
1986 | | /* Block number starts at 0 */ |
1987 | 0 | uint32_t end_block = ((uint32_t)lg_srcv->total_len + block_size - 1) / block_size - 1; |
1988 | 0 | int count = 0; |
1989 | |
|
1990 | 0 | if (COAP_MAX_PAYLOADS(session) == 0 || |
1991 | 0 | (int32_t)(inc_to_block / COAP_MAX_PAYLOADS(session)) == lg_srcv->r_m_payload_set) |
1992 | 0 | return 0; |
1993 | | |
1994 | 0 | if (inc_to_block != end_block) { |
1995 | 0 | lg_srcv->r_m_payload_set = inc_to_block / COAP_MAX_PAYLOADS(session); |
1996 | 0 | } |
1997 | |
|
1998 | 0 | if (lg_srcv->rec_blocks.range[0].begin != 0) { |
1999 | 0 | first_missing = 0; |
2000 | 0 | } else { |
2001 | 0 | first_missing = lg_srcv->rec_blocks.range[0].end + 1; |
2002 | 0 | } |
2003 | 0 | if (first_missing > end_block) |
2004 | 0 | return 0; |
2005 | 0 | missing_payload = first_missing / COAP_MAX_PAYLOADS(session); |
2006 | 0 | last_payload_block = (missing_payload + 1) * COAP_MAX_PAYLOADS(session) - 1; |
2007 | 0 | if (inc_to_block != end_block && inc_to_block > last_payload_block) { |
2008 | 0 | inc_to_block = last_payload_block; |
2009 | 0 | } |
2010 | | /* Ask for the missing blocks */ |
2011 | 0 | block = -1; |
2012 | 0 | for (i = 0; i < lg_srcv->rec_blocks.used; i++) { |
2013 | 0 | if (block < (int32_t)lg_srcv->rec_blocks.range[i].begin && |
2014 | 0 | lg_srcv->rec_blocks.range[i].begin != 0) { |
2015 | | /* Report on missing blocks */ |
2016 | 0 | if (pdu == NULL) { |
2017 | 0 | pdu = pdu_408_build(session, lg_srcv); |
2018 | 0 | if (!pdu) |
2019 | 0 | continue; |
2020 | 0 | } |
2021 | 0 | block++; |
2022 | 0 | for (; block < (int32_t)lg_srcv->rec_blocks.range[i].begin; block++) { |
2023 | 0 | if (!add_408_block(pdu, block)) { |
2024 | 0 | break; |
2025 | 0 | } |
2026 | 0 | count++; |
2027 | 0 | } |
2028 | 0 | } |
2029 | 0 | if (block < (int32_t)lg_srcv->rec_blocks.range[i].end) { |
2030 | 0 | block = lg_srcv->rec_blocks.range[i].end; |
2031 | 0 | } |
2032 | 0 | } |
2033 | 0 | if (inc_to_block == end_block && block < (int32_t)inc_to_block) { |
2034 | | /* Need to fill to the end */ |
2035 | 0 | if (pdu == NULL) { |
2036 | 0 | pdu = pdu_408_build(session, lg_srcv); |
2037 | 0 | if (!pdu) |
2038 | 0 | goto retry; |
2039 | 0 | } |
2040 | 0 | block++; |
2041 | 0 | for (; block <= (int32_t)inc_to_block && count < COAP_MAX_PAYLOADS(session); block++) { |
2042 | 0 | if (!add_408_block(pdu, block)) { |
2043 | 0 | break; |
2044 | 0 | } |
2045 | 0 | count++; |
2046 | 0 | } |
2047 | 0 | } |
2048 | 0 | if (pdu) |
2049 | 0 | coap_send_internal(session, pdu, NULL); |
2050 | 0 | retry: |
2051 | 0 | lg_srcv->rec_blocks.retry++; |
2052 | 0 | coap_ticks(&lg_srcv->rec_blocks.last_seen); |
2053 | 0 | return 1; |
2054 | 0 | } |
2055 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2056 | | |
2057 | | /* |
2058 | | * return 1 if there is a future expire time, else 0. |
2059 | | * update tim_rem with remaining value if return is 1. |
2060 | | */ |
2061 | | int |
2062 | | coap_block_check_lg_srcv_timeouts(coap_session_t *session, coap_tick_t now, |
2063 | 0 | coap_tick_t *tim_rem) { |
2064 | 0 | coap_lg_srcv_t *lg_srcv; |
2065 | 0 | coap_lg_srcv_t *q; |
2066 | 0 | coap_tick_t partial_timeout; |
2067 | 0 | #if COAP_Q_BLOCK_SUPPORT |
2068 | 0 | coap_tick_t receive_timeout = COAP_NON_RECEIVE_TIMEOUT_TICKS(session); |
2069 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2070 | 0 | int ret = 0; |
2071 | |
|
2072 | 0 | *tim_rem = COAP_MAX_DELAY_TICKS; |
2073 | 0 | #if COAP_Q_BLOCK_SUPPORT |
2074 | 0 | if (COAP_PROTO_NOT_RELIABLE(session->proto) && |
2075 | 0 | session->block_mode & COAP_BLOCK_HAS_Q_BLOCK) |
2076 | 0 | partial_timeout = COAP_NON_PARTIAL_TIMEOUT_TICKS(session); |
2077 | 0 | else |
2078 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2079 | 0 | partial_timeout = COAP_MAX_TRANSMIT_WAIT_TICKS(session); |
2080 | |
|
2081 | 0 | LL_FOREACH_SAFE(session->lg_srcv, lg_srcv, q) { |
2082 | 0 | if (lg_srcv->dont_timeout) { |
2083 | | /* Not safe to timeout at present */ |
2084 | 0 | continue; |
2085 | 0 | } |
2086 | 0 | if (COAP_PROTO_RELIABLE(session->proto) || lg_srcv->block_option != COAP_OPTION_Q_BLOCK1) |
2087 | 0 | goto check_expire; |
2088 | | |
2089 | 0 | #if COAP_Q_BLOCK_SUPPORT |
2090 | 0 | if (lg_srcv->block_option == COAP_OPTION_Q_BLOCK1 && lg_srcv->rec_blocks.used) { |
2091 | 0 | size_t scaled_timeout = receive_timeout * |
2092 | 0 | ((size_t)1 << lg_srcv->rec_blocks.retry); |
2093 | |
|
2094 | 0 | if (lg_srcv->rec_blocks.retry >= COAP_NON_MAX_RETRANSMIT(session)) { |
2095 | | /* Done NON_MAX_RETRANSMIT retries */ |
2096 | 0 | goto expire; |
2097 | 0 | } |
2098 | 0 | if (lg_srcv->rec_blocks.last_seen + scaled_timeout <= now) { |
2099 | 0 | uint32_t block_size = (size_t)1 << (lg_srcv->szx + 4); |
2100 | 0 | coap_log_debug("** %s: lg_srcv %p timeout\n", |
2101 | 0 | coap_session_str(session), (void *)lg_srcv); |
2102 | 0 | if (!coap_request_missing_q_block1(session, lg_srcv, |
2103 | 0 | ((uint32_t)lg_srcv->total_len + block_size - 1) / block_size - 1)) |
2104 | 0 | goto expire; |
2105 | 0 | } |
2106 | 0 | if (*tim_rem > lg_srcv->rec_blocks.last_seen + scaled_timeout - now) { |
2107 | 0 | *tim_rem = lg_srcv->rec_blocks.last_seen + scaled_timeout - now; |
2108 | 0 | ret = 1; |
2109 | 0 | } |
2110 | 0 | } |
2111 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2112 | | /* Used for Block1 and Q-Block1 */ |
2113 | 0 | check_expire: |
2114 | 0 | if (lg_srcv->no_more_seen) |
2115 | 0 | partial_timeout = 10 * COAP_TICKS_PER_SECOND; |
2116 | 0 | if (lg_srcv->last_used && lg_srcv->last_used + partial_timeout <= now) { |
2117 | 0 | #if COAP_Q_BLOCK_SUPPORT |
2118 | 0 | expire: |
2119 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2120 | | /* Expire this entry */ |
2121 | 0 | if (lg_srcv->no_more_seen && lg_srcv->block_option == COAP_OPTION_BLOCK1) { |
2122 | | /* |
2123 | | * Need to send a separate 4.08 to indicate missing blocks |
2124 | | * Using NON is permissible as per |
2125 | | * https://datatracker.ietf.org/doc/html/rfc7252#section-5.2.3 |
2126 | | */ |
2127 | 0 | coap_pdu_t *pdu; |
2128 | |
|
2129 | 0 | pdu = coap_pdu_init(COAP_MESSAGE_NON, |
2130 | 0 | COAP_RESPONSE_CODE(408), |
2131 | 0 | coap_new_message_id_lkd(session), |
2132 | 0 | coap_session_max_pdu_size_lkd(session)); |
2133 | 0 | if (pdu) { |
2134 | 0 | if (lg_srcv->last_token) |
2135 | 0 | coap_add_token(pdu, lg_srcv->last_token->length, lg_srcv->last_token->s); |
2136 | 0 | coap_add_data(pdu, sizeof("Missing interim block")-1, |
2137 | 0 | (const uint8_t *)"Missing interim block"); |
2138 | 0 | coap_send_internal(session, pdu, NULL); |
2139 | 0 | } |
2140 | 0 | } |
2141 | 0 | LL_DELETE(session->lg_srcv, lg_srcv); |
2142 | 0 | coap_block_delete_lg_srcv(session, lg_srcv); |
2143 | 0 | } else if (lg_srcv->last_used) { |
2144 | | /* Delay until the lg_srcv needs to expire */ |
2145 | 0 | if (*tim_rem > lg_srcv->last_used + partial_timeout - now) { |
2146 | 0 | *tim_rem = lg_srcv->last_used + partial_timeout - now; |
2147 | 0 | ret = 1; |
2148 | 0 | } |
2149 | 0 | } |
2150 | 0 | } |
2151 | 0 | return ret; |
2152 | 0 | } |
2153 | | #endif /* COAP_SERVER_SUPPORT */ |
2154 | | |
2155 | | #if COAP_Q_BLOCK_SUPPORT |
2156 | | static coap_pdu_t * |
2157 | | coap_next_block_to_send(coap_session_t *session, |
2158 | | coap_lg_xmit_t *lg_xmit, |
2159 | | coap_block_b_t *block, |
2160 | | coap_pdu_t *pdu, |
2161 | | uint32_t delayqueue_cnt, |
2162 | 0 | int is_first) { |
2163 | 0 | const uint8_t *ptoken; |
2164 | 0 | uint8_t ltoken[8]; |
2165 | 0 | size_t ltoken_length; |
2166 | 0 | coap_pdu_t *block_pdu = NULL; |
2167 | 0 | uint64_t token; |
2168 | 0 | coap_opt_filter_t drop_options; |
2169 | 0 | size_t chunk = ((size_t)1 << (lg_xmit->blk_size + 4)); |
2170 | 0 | size_t offset; |
2171 | 0 | uint8_t buf[8]; |
2172 | 0 | int need_block = 0; |
2173 | |
|
2174 | 0 | if (COAP_MAX_PAYLOADS(session) == 0) |
2175 | 0 | return NULL; |
2176 | | /* Get the next block to transmit (which could be a retry */ |
2177 | 0 | block->num = lg_xmit->send_blocks.range[0].begin; |
2178 | |
|
2179 | 0 | offset = block->num * chunk; |
2180 | 0 | block->m = offset + chunk < lg_xmit->data_info->length; |
2181 | |
|
2182 | 0 | if (!(block->m || lg_xmit->send_blocks.used) && COAP_MAX_PAYLOADS(session)) { |
2183 | 0 | return NULL; |
2184 | 0 | } |
2185 | 0 | if (is_first && |
2186 | 0 | ((pdu->type == COAP_MESSAGE_ACK && lg_xmit->option == COAP_OPTION_Q_BLOCK2) || |
2187 | 0 | (pdu->type == COAP_MESSAGE_CON && delayqueue_cnt < COAP_NSTART(session)))) { |
2188 | 0 | need_block = 1; |
2189 | 0 | } |
2190 | 0 | if (COAP_PROTO_RELIABLE(session->proto)) { |
2191 | 0 | need_block = 1; |
2192 | 0 | } |
2193 | |
|
2194 | 0 | if (need_block || |
2195 | 0 | (pdu->type == COAP_MESSAGE_NON && |
2196 | 0 | (lg_xmit->send_blocks.used > 1 || |
2197 | 0 | (block->num % COAP_MAX_PAYLOADS(session) != 0) || |
2198 | 0 | is_first))) { |
2199 | 0 | if (!is_first && block->num % COAP_MAX_PAYLOADS(session) == 0 && |
2200 | 0 | lg_xmit->send_blocks.used == 1 && !COAP_PROTO_RELIABLE(session->proto)) { |
2201 | 0 | coap_log_debug("Q-Block: Stopping at block %d\n", block->num); |
2202 | 0 | } else { |
2203 | | /* |
2204 | | * Allocate next block pdu if there is headroom and if one of |
2205 | | * NON and more in MAX_PAYLOADS |
2206 | | * CON and NSTART allows it (based on number in delayqueue) |
2207 | | * Reliable transport |
2208 | | */ |
2209 | 0 | coap_log_debug("Q-Block: %s block %d\n", is_first ? "First" : "Next", block->num); |
2210 | 0 | if (COAP_PDU_IS_RESPONSE(pdu)) { |
2211 | 0 | ptoken = pdu->actual_token.s; |
2212 | 0 | ltoken_length = pdu->actual_token.length; |
2213 | 0 | } else { |
2214 | 0 | token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token,++lg_xmit->b.b1.count); |
2215 | 0 | ltoken_length = coap_encode_var_safe8(ltoken, sizeof(token), token); |
2216 | 0 | ptoken = ltoken; |
2217 | 0 | } |
2218 | |
|
2219 | 0 | memset(&drop_options, 0, sizeof(coap_opt_filter_t)); |
2220 | 0 | coap_option_filter_set(&drop_options, lg_xmit->option); |
2221 | 0 | block_pdu = coap_pdu_duplicate_lkd(pdu, session, |
2222 | 0 | ltoken_length, |
2223 | 0 | ptoken, &drop_options, COAP_BOOL_FALSE); |
2224 | 0 | if (block_pdu->type == COAP_MESSAGE_ACK) |
2225 | 0 | block_pdu->type = COAP_MESSAGE_CON; |
2226 | |
|
2227 | 0 | if (!coap_update_option(block_pdu, lg_xmit->option, |
2228 | 0 | coap_encode_var_safe(buf, |
2229 | 0 | sizeof(buf), |
2230 | 0 | ((block->num) << 4) | |
2231 | 0 | (block->m << 3) | |
2232 | 0 | block->szx), |
2233 | 0 | buf)) { |
2234 | 0 | coap_log_warn("Internal update issue option\n"); |
2235 | 0 | coap_delete_pdu_lkd(block_pdu); |
2236 | 0 | return NULL; |
2237 | 0 | } |
2238 | 0 | } |
2239 | 0 | } |
2240 | 0 | return block_pdu; |
2241 | 0 | } |
2242 | | |
2243 | | /* |
2244 | | * pdu is always released before return IF COAP_SEND_INC_PDU |
2245 | | */ |
2246 | | coap_mid_t |
2247 | | coap_send_q_blocks(coap_session_t *session, |
2248 | | coap_lg_xmit_t *lg_xmit, |
2249 | | coap_block_b_t block, |
2250 | | coap_pdu_t *pdu, |
2251 | 0 | coap_send_pdu_t send_pdu) { |
2252 | 0 | coap_pdu_t *block_pdu = NULL; /* The next block to send after any initial PDU */ |
2253 | 0 | coap_mid_t mid = COAP_INVALID_MID; |
2254 | 0 | uint32_t delayqueue_cnt = 0; |
2255 | 0 | coap_block_b_t l_block; |
2256 | 0 | int is_non; |
2257 | |
|
2258 | 0 | if (!pdu) |
2259 | 0 | return COAP_INVALID_MID; |
2260 | | |
2261 | 0 | is_non = pdu->type == COAP_MESSAGE_NON ? 1 : 0; |
2262 | |
|
2263 | 0 | if (!lg_xmit || session->is_rate_limiting) { |
2264 | 0 | if (send_pdu == COAP_SEND_INC_PDU) { |
2265 | 0 | if (lg_xmit && |
2266 | 0 | (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &l_block) || |
2267 | 0 | coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &l_block))) { |
2268 | 0 | mid = coap_send_internal(session, pdu, NULL); |
2269 | 0 | if (mid != COAP_INVALID_MID) { |
2270 | 0 | blocks_delete_entry(&lg_xmit->send_blocks, l_block.num); |
2271 | 0 | } |
2272 | 0 | } else { |
2273 | 0 | return coap_send_internal(session, pdu, NULL); |
2274 | 0 | } |
2275 | 0 | } |
2276 | 0 | return COAP_INVALID_MID; |
2277 | 0 | } |
2278 | | |
2279 | 0 | if (pdu->type == COAP_MESSAGE_CON) { |
2280 | 0 | coap_queue_t *delayqueue; |
2281 | |
|
2282 | 0 | delayqueue_cnt = session->con_active + |
2283 | 0 | (send_pdu == COAP_SEND_INC_PDU ? 1 : 0); |
2284 | 0 | LL_FOREACH(session->delayqueue, delayqueue) { |
2285 | 0 | delayqueue_cnt++; |
2286 | 0 | } |
2287 | 0 | } |
2288 | 0 | pdu->lg_xmit = lg_xmit; |
2289 | | |
2290 | | /* Send initial pdu (which deletes 'pdu') */ |
2291 | 0 | if (send_pdu == COAP_SEND_INC_PDU) { |
2292 | 0 | if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &l_block) || |
2293 | 0 | coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &l_block)) { |
2294 | 0 | mid = coap_send_internal(session, pdu, NULL); |
2295 | 0 | if (mid != COAP_INVALID_MID) { |
2296 | 0 | blocks_delete_entry(&lg_xmit->send_blocks, l_block.num); |
2297 | 0 | } |
2298 | 0 | } else { |
2299 | 0 | mid = coap_send_internal(session, pdu, NULL); |
2300 | 0 | } |
2301 | 0 | if (mid == COAP_INVALID_MID) { |
2302 | | /* Not expected, underlying issue somewhere */ |
2303 | 0 | coap_delete_pdu_lkd(block_pdu); |
2304 | 0 | return COAP_INVALID_MID; |
2305 | 0 | } |
2306 | 0 | coap_ticks(&lg_xmit->last_sent); |
2307 | 0 | } |
2308 | | /* Unsafe to use pdu in later code */ |
2309 | | |
2310 | 0 | coap_lg_xmit_reference_lkd(lg_xmit); |
2311 | 0 | l_block = block; |
2312 | 0 | block_pdu = coap_next_block_to_send(session, lg_xmit, &l_block, pdu, delayqueue_cnt, 1); |
2313 | 0 | if (block_pdu) { |
2314 | 0 | block = l_block; |
2315 | 0 | } |
2316 | |
|
2317 | 0 | while (block_pdu && lg_xmit->send_blocks.used) { |
2318 | 0 | coap_pdu_t *t_pdu = NULL; |
2319 | |
|
2320 | 0 | if (lg_xmit->data_info->get_func) { |
2321 | | #if COAP_CONSTRAINED_STACK |
2322 | | /* Protected by global_lock if needed */ |
2323 | | static uint8_t l_data[1024]; |
2324 | | #else /* ! COAP_CONSTRAINED_STACK */ |
2325 | 0 | uint8_t l_data[1024]; |
2326 | 0 | #endif /* ! COAP_CONSTRAINED_STACK */ |
2327 | 0 | size_t l_length; |
2328 | 0 | size_t chunk = ((size_t)1 << (lg_xmit->blk_size + 4)); |
2329 | |
|
2330 | 0 | (void)chunk; |
2331 | 0 | assert(chunk <= 1024); |
2332 | 0 | if (lg_xmit->data_info->get_func(session, chunk, |
2333 | 0 | block.num * chunk, l_data, &l_length, |
2334 | 0 | lg_xmit->data_info->app_ptr)) { |
2335 | 0 | if (!coap_add_data(block_pdu, l_length, l_data)) { |
2336 | 0 | coap_log_warn("Internal update issue data (1)\n"); |
2337 | 0 | coap_delete_pdu_lkd(block_pdu); |
2338 | 0 | block_pdu = NULL; |
2339 | 0 | break; |
2340 | 0 | } |
2341 | 0 | } |
2342 | 0 | } else { |
2343 | 0 | if (!coap_add_block(block_pdu, |
2344 | 0 | lg_xmit->data_info->length, |
2345 | 0 | lg_xmit->data_info->data, |
2346 | 0 | block.num, |
2347 | 0 | block.szx)) { |
2348 | 0 | coap_log_warn("Internal update issue data (2)\n"); |
2349 | 0 | coap_delete_pdu_lkd(block_pdu); |
2350 | 0 | block_pdu = NULL; |
2351 | 0 | break; |
2352 | 0 | } |
2353 | 0 | } |
2354 | 0 | if (COAP_PDU_IS_RESPONSE(block_pdu)) { |
2355 | 0 | lg_xmit->last_block = block.num; |
2356 | 0 | } |
2357 | | /* This block will now be transmitted */ |
2358 | 0 | blocks_delete_entry(&lg_xmit->send_blocks, block.num); |
2359 | |
|
2360 | 0 | if (block_pdu->type == COAP_MESSAGE_NON || COAP_PROTO_RELIABLE(session->proto)) { |
2361 | | /* Get the next block to transmit (which could be a retry) */ |
2362 | 0 | l_block = block; |
2363 | 0 | t_pdu = coap_next_block_to_send(session, lg_xmit, &l_block, block_pdu, delayqueue_cnt, 0); |
2364 | 0 | if (t_pdu) { |
2365 | 0 | block = l_block; |
2366 | 0 | } |
2367 | 0 | } |
2368 | |
|
2369 | 0 | mid = coap_send_internal(session, block_pdu, NULL); |
2370 | 0 | if (mid == COAP_INVALID_MID) { |
2371 | | /* Need to resent this one ... */ |
2372 | 0 | blocks_add_entry(&lg_xmit->send_blocks, block.num, block.m); |
2373 | | /* Not expected, underlying issue somewhere */ |
2374 | 0 | coap_lg_xmit_release_lkd(session, lg_xmit); |
2375 | 0 | coap_delete_pdu_lkd(t_pdu); |
2376 | 0 | return COAP_INVALID_MID; |
2377 | 0 | } |
2378 | 0 | coap_ticks(&lg_xmit->last_sent); |
2379 | 0 | block_pdu = t_pdu; |
2380 | 0 | } |
2381 | 0 | if (is_non) { |
2382 | 0 | coap_log_debug("Q-Block: Current MAX_PAYLOAD sent\n"); |
2383 | 0 | } |
2384 | 0 | coap_delete_pdu_lkd(block_pdu); |
2385 | 0 | if (!block.m) { |
2386 | 0 | lg_xmit->last_payload = 0; |
2387 | 0 | coap_ticks(&lg_xmit->last_all_sent); |
2388 | 0 | } else |
2389 | 0 | coap_ticks(&lg_xmit->last_payload); |
2390 | 0 | coap_lg_xmit_release_lkd(session, lg_xmit); |
2391 | 0 | return mid; |
2392 | 0 | } |
2393 | | |
2394 | | #if COAP_CLIENT_SUPPORT |
2395 | | /* |
2396 | | * Return 1 if there is a future expire time, else 0. |
2397 | | * Update tim_rem with remaining value if return is 1. |
2398 | | */ |
2399 | | int |
2400 | 0 | coap_block_check_q_block1_xmit(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem) { |
2401 | 0 | coap_lg_xmit_t *lg_xmit; |
2402 | 0 | coap_lg_xmit_t *q; |
2403 | 0 | coap_tick_t timed_out; |
2404 | 0 | int ret = 0; |
2405 | |
|
2406 | 0 | *tim_rem = COAP_MAX_DELAY_TICKS; |
2407 | 0 | LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) { |
2408 | 0 | coap_tick_t non_timeout = lg_xmit->non_timeout_random_ticks; |
2409 | |
|
2410 | 0 | if (lg_xmit->sent_pdu->type != COAP_MESSAGE_NON) |
2411 | 0 | continue; |
2412 | 0 | if (now <= non_timeout) { |
2413 | | /* Too early in the startup cycle to have an accurate response */ |
2414 | 0 | *tim_rem = non_timeout - now; |
2415 | 0 | return 1; |
2416 | 0 | } |
2417 | 0 | timed_out = now - non_timeout; |
2418 | |
|
2419 | 0 | if (lg_xmit->last_payload && lg_xmit->send_blocks.used) { |
2420 | 0 | if (lg_xmit->last_payload <= timed_out) { |
2421 | | /* Send off the next MAX_PAYLOAD set */ |
2422 | 0 | coap_block_b_t block; |
2423 | 0 | size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4); |
2424 | 0 | size_t offset; |
2425 | |
|
2426 | 0 | coap_log_debug("Q-Block: Timeout - start next MAX_PAYLOADS_SET\n"); |
2427 | 0 | memset(&block, 0, sizeof(block)); |
2428 | 0 | block.num = lg_xmit->send_blocks.range[0].begin; |
2429 | 0 | offset = block.num * chunk; |
2430 | 0 | block.m = offset + chunk < lg_xmit->data_info->length; |
2431 | 0 | block.szx = lg_xmit->blk_size; |
2432 | 0 | coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU); |
2433 | 0 | if (*tim_rem > non_timeout) { |
2434 | 0 | *tim_rem = non_timeout; |
2435 | 0 | ret = 1; |
2436 | 0 | } |
2437 | 0 | } else { |
2438 | | /* Delay until the next MAX_PAYLOAD needs to be sent off */ |
2439 | 0 | if (*tim_rem > lg_xmit->last_payload - timed_out) { |
2440 | 0 | *tim_rem = lg_xmit->last_payload - timed_out; |
2441 | 0 | ret = 1; |
2442 | 0 | } |
2443 | 0 | } |
2444 | 0 | } else if (lg_xmit->last_all_sent) { |
2445 | 0 | non_timeout = COAP_NON_TIMEOUT_TICKS(session); |
2446 | 0 | if (lg_xmit->last_all_sent + COAP_LG_XMIT_TXT_SCALAR * non_timeout <= now) { |
2447 | | /* Expire this entry */ |
2448 | 0 | LL_DELETE(session->lg_xmit, lg_xmit); |
2449 | 0 | coap_block_delete_lg_xmit(session, lg_xmit); |
2450 | 0 | } else { |
2451 | | /* Delay until the lg_xmit needs to expire */ |
2452 | 0 | if (*tim_rem > lg_xmit->last_all_sent + COAP_LG_XMIT_TXT_SCALAR * non_timeout - now) { |
2453 | 0 | *tim_rem = lg_xmit->last_all_sent + COAP_LG_XMIT_TXT_SCALAR * non_timeout - now; |
2454 | 0 | ret = 1; |
2455 | 0 | } |
2456 | 0 | } |
2457 | 0 | } |
2458 | 0 | } |
2459 | 0 | return ret; |
2460 | 0 | } |
2461 | | #endif /* COAP_CLIENT_SUPPORT */ |
2462 | | |
2463 | | #if COAP_SERVER_SUPPORT |
2464 | | /* |
2465 | | * Return 1 if there is a future expire time, else 0. |
2466 | | * Update tim_rem with remaining value if return is 1. |
2467 | | */ |
2468 | | int |
2469 | 0 | coap_block_check_q_block2_xmit(coap_session_t *session, coap_tick_t now, coap_tick_t *tim_rem) { |
2470 | 0 | coap_lg_xmit_t *lg_xmit; |
2471 | 0 | coap_lg_xmit_t *q; |
2472 | 0 | coap_tick_t timed_out; |
2473 | 0 | int ret = 0; |
2474 | |
|
2475 | 0 | *tim_rem = (coap_tick_t)-1; |
2476 | 0 | LL_FOREACH_SAFE(session->lg_xmit, lg_xmit, q) { |
2477 | 0 | coap_tick_t non_timeout = lg_xmit->non_timeout_random_ticks; |
2478 | |
|
2479 | 0 | if (now <= non_timeout) { |
2480 | | /* Too early in the startup cycle to have an accurate response */ |
2481 | 0 | *tim_rem = non_timeout - now; |
2482 | 0 | return 1; |
2483 | 0 | } |
2484 | 0 | timed_out = now - non_timeout; |
2485 | |
|
2486 | 0 | if (lg_xmit->last_payload && lg_xmit->send_blocks.used) { |
2487 | 0 | if (lg_xmit->last_payload <= timed_out) { |
2488 | | /* Send off the next MAX_PAYLOAD set */ |
2489 | 0 | coap_block_b_t block; |
2490 | 0 | size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4); |
2491 | 0 | size_t offset; |
2492 | |
|
2493 | 0 | memset(&block, 0, sizeof(block)); |
2494 | 0 | block.num = lg_xmit->send_blocks.range[0].begin; |
2495 | 0 | offset = block.num * chunk; |
2496 | 0 | block.m = offset + chunk < lg_xmit->data_info->length; |
2497 | 0 | block.szx = lg_xmit->blk_size; |
2498 | 0 | if (block.num == (uint32_t)lg_xmit->last_block) |
2499 | 0 | coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU); |
2500 | 0 | if (*tim_rem > non_timeout) { |
2501 | 0 | *tim_rem = non_timeout; |
2502 | 0 | ret = 1; |
2503 | 0 | } |
2504 | 0 | } else { |
2505 | | /* Delay until the next MAX_PAYLOAD needs to be sent off */ |
2506 | 0 | if (*tim_rem > lg_xmit->last_payload - timed_out) { |
2507 | 0 | *tim_rem = lg_xmit->last_payload - timed_out; |
2508 | 0 | ret = 1; |
2509 | 0 | } |
2510 | 0 | } |
2511 | 0 | } else if (lg_xmit->last_all_sent) { |
2512 | 0 | non_timeout = COAP_NON_TIMEOUT_TICKS(session); |
2513 | 0 | if (lg_xmit->last_all_sent + 4 * non_timeout <= now) { |
2514 | | /* Expire this entry */ |
2515 | 0 | LL_DELETE(session->lg_xmit, lg_xmit); |
2516 | 0 | coap_block_delete_lg_xmit(session, lg_xmit); |
2517 | 0 | } else { |
2518 | | /* Delay until the lg_xmit needs to expire */ |
2519 | 0 | if (*tim_rem > lg_xmit->last_all_sent + 4 * non_timeout - now) { |
2520 | 0 | *tim_rem = lg_xmit->last_all_sent + 4 * non_timeout - now; |
2521 | 0 | ret = 1; |
2522 | 0 | } |
2523 | 0 | } |
2524 | 0 | } |
2525 | 0 | } |
2526 | 0 | return ret; |
2527 | 0 | } |
2528 | | #endif /* COAP_SERVER_SUPPORT */ |
2529 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2530 | | |
2531 | | #if COAP_CLIENT_SUPPORT |
2532 | | /* |
2533 | | * If Observe = 0, save the token away and return NULL |
2534 | | * Else If Observe = 1, return the saved token for this block |
2535 | | * Else, return NULL |
2536 | | */ |
2537 | | static coap_bin_const_t * |
2538 | | track_fetch_observe(coap_pdu_t *pdu, coap_lg_crcv_t *lg_crcv, |
2539 | 0 | uint32_t block_num, coap_bin_const_t *token) { |
2540 | | /* Need to handle Observe for large FETCH */ |
2541 | 0 | coap_opt_iterator_t opt_iter; |
2542 | 0 | coap_opt_t *opt = coap_check_option(pdu, COAP_OPTION_OBSERVE, |
2543 | 0 | &opt_iter); |
2544 | |
|
2545 | 0 | if (opt && lg_crcv) { |
2546 | 0 | int observe_action = -1; |
2547 | 0 | coap_bin_const_t **tmp; |
2548 | |
|
2549 | 0 | observe_action = coap_decode_var_bytes(coap_opt_value(opt), |
2550 | 0 | coap_opt_length(opt)); |
2551 | 0 | if (observe_action == COAP_OBSERVE_ESTABLISH) { |
2552 | | /* Save the token in lg_crcv */ |
2553 | 0 | if (lg_crcv->obs_token_cnt <= block_num) { |
2554 | 0 | size_t i; |
2555 | |
|
2556 | 0 | tmp = coap_realloc_type(COAP_STRING, lg_crcv->obs_token, |
2557 | 0 | (block_num + 1) * sizeof(lg_crcv->obs_token[0])); |
2558 | 0 | if (tmp == NULL) |
2559 | 0 | return NULL; |
2560 | 0 | lg_crcv->obs_token = tmp; |
2561 | 0 | for (i = lg_crcv->obs_token_cnt; i < block_num + 1; i++) { |
2562 | 0 | lg_crcv->obs_token[i] = NULL; |
2563 | 0 | } |
2564 | 0 | } |
2565 | 0 | coap_delete_bin_const(lg_crcv->obs_token[block_num]); |
2566 | |
|
2567 | 0 | if (lg_crcv->obs_token_cnt <= block_num) |
2568 | 0 | lg_crcv->obs_token_cnt = block_num + 1; |
2569 | 0 | lg_crcv->obs_token[block_num] = coap_new_bin_const(token->s, |
2570 | 0 | token->length); |
2571 | 0 | if (lg_crcv->obs_token[block_num] == NULL) |
2572 | 0 | return NULL; |
2573 | 0 | } else if (observe_action == COAP_OBSERVE_CANCEL) { |
2574 | | /* Use the token in lg_crcv */ |
2575 | 0 | if (block_num < lg_crcv->obs_token_cnt) { |
2576 | 0 | return lg_crcv->obs_token[block_num]; |
2577 | 0 | } |
2578 | 0 | } |
2579 | 0 | } |
2580 | 0 | return NULL; |
2581 | 0 | } |
2582 | | |
2583 | | #if COAP_Q_BLOCK_SUPPORT |
2584 | | coap_mid_t |
2585 | | coap_send_q_block1(coap_session_t *session, |
2586 | | coap_block_b_t block, |
2587 | | coap_pdu_t *request, |
2588 | 0 | coap_send_pdu_t send_request) { |
2589 | | /* Need to send up to MAX_PAYLOAD blocks if this is a Q_BLOCK1 */ |
2590 | 0 | coap_lg_xmit_t *lg_xmit; |
2591 | 0 | uint64_t token_match = |
2592 | 0 | STATE_TOKEN_BASE(coap_decode_var_bytes8(request->actual_token.s, |
2593 | 0 | request->actual_token.length)); |
2594 | |
|
2595 | 0 | LL_FOREACH(session->lg_xmit, lg_xmit) { |
2596 | 0 | if (lg_xmit->option == COAP_OPTION_Q_BLOCK1 && |
2597 | 0 | (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) || |
2598 | 0 | token_match == |
2599 | 0 | STATE_TOKEN_BASE(coap_decode_var_bytes8(lg_xmit->b.b1.app_token->s, |
2600 | 0 | lg_xmit->b.b1.app_token->length)))) |
2601 | 0 | break; |
2602 | | /* try out the next one */ |
2603 | 0 | } |
2604 | 0 | return coap_send_q_blocks(session, lg_xmit, block, request, send_request); |
2605 | 0 | } |
2606 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2607 | | #endif /* COAP_CLIENT_SUPPORT */ |
2608 | | |
2609 | | #if COAP_SERVER_SUPPORT |
2610 | | #if COAP_Q_BLOCK_SUPPORT |
2611 | | /* |
2612 | | * response is always released before return IF COAP_SEND_INC_PDU |
2613 | | */ |
2614 | | coap_mid_t |
2615 | | coap_send_q_block2(coap_session_t *session, |
2616 | | coap_resource_t *resource, |
2617 | | const coap_string_t *query, |
2618 | | coap_pdu_code_t request_method, |
2619 | | coap_block_b_t block, |
2620 | | coap_pdu_t *response, |
2621 | 0 | coap_send_pdu_t send_response) { |
2622 | | /* Need to send up to MAX_PAYLOAD blocks if this is a Q_BLOCK2 */ |
2623 | 0 | coap_lg_xmit_t *lg_xmit; |
2624 | 0 | coap_string_t empty = { 0, NULL}; |
2625 | |
|
2626 | 0 | LL_FOREACH(session->lg_xmit, lg_xmit) { |
2627 | 0 | if (lg_xmit->option == COAP_OPTION_Q_BLOCK2 && |
2628 | 0 | resource == lg_xmit->b.b2.resource && |
2629 | 0 | request_method == lg_xmit->b.b2.request_method && |
2630 | 0 | coap_string_equal(query ? query : &empty, |
2631 | 0 | lg_xmit->b.b2.query ? lg_xmit->b.b2.query : &empty)) |
2632 | 0 | break; |
2633 | 0 | } |
2634 | 0 | return coap_send_q_blocks(session, lg_xmit, block, response, send_response); |
2635 | 0 | } |
2636 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2637 | | #endif /* COAP_SERVER_SUPPORT */ |
2638 | | |
2639 | | static void |
2640 | | coap_block_release_lg_xmit_data(coap_session_t *session, |
2641 | 0 | coap_lg_xmit_data_t *data_info) { |
2642 | 0 | if (!data_info) |
2643 | 0 | return; |
2644 | 0 | if (data_info->ref > 0) { |
2645 | 0 | data_info->ref--; |
2646 | 0 | return; |
2647 | 0 | } |
2648 | 0 | if (data_info->release_func) { |
2649 | 0 | coap_lock_callback(data_info->release_func(session, |
2650 | 0 | data_info->app_ptr)); |
2651 | 0 | data_info->release_func = NULL; |
2652 | 0 | } |
2653 | 0 | coap_free_type(COAP_STRING, data_info); |
2654 | 0 | } |
2655 | | |
2656 | | #if COAP_CLIENT_SUPPORT |
2657 | | #if COAP_Q_BLOCK_SUPPORT |
2658 | | /* |
2659 | | * Send out a test PDU for Q-Block. |
2660 | | */ |
2661 | | coap_mid_t |
2662 | 0 | coap_block_test_q_block(coap_session_t *session, coap_pdu_t *actual) { |
2663 | 0 | coap_pdu_t *pdu; |
2664 | 0 | uint8_t token[8]; |
2665 | 0 | size_t token_len; |
2666 | 0 | uint8_t buf[4]; |
2667 | 0 | coap_mid_t mid; |
2668 | 0 | coap_bin_const_t *k_token; |
2669 | |
|
2670 | | #if NDEBUG |
2671 | | (void)actual; |
2672 | | #endif /* NDEBUG */ |
2673 | 0 | assert(session->block_mode & COAP_BLOCK_TRY_Q_BLOCK && |
2674 | 0 | session->type == COAP_SESSION_TYPE_CLIENT && |
2675 | 0 | COAP_PDU_IS_REQUEST(actual)); |
2676 | | |
2677 | 0 | coap_log_debug("Testing for Q-Block support\n"); |
2678 | | /* RFC9177 Section 4.1 when checking if available */ |
2679 | 0 | pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, |
2680 | 0 | coap_new_message_id_lkd(session), |
2681 | 0 | coap_session_max_pdu_size_lkd(session)); |
2682 | 0 | if (!pdu) { |
2683 | 0 | return COAP_INVALID_MID; |
2684 | 0 | } |
2685 | | |
2686 | 0 | coap_session_new_token(session, &token_len, token); |
2687 | 0 | coap_add_token(pdu, token_len, token); |
2688 | | /* Use a resource that the server MUST support (.well-known/core) */ |
2689 | 0 | coap_add_option(pdu, COAP_OPTION_URI_PATH, |
2690 | 0 | 11, (const uint8_t *)".well-known"); |
2691 | 0 | coap_add_option(pdu, COAP_OPTION_URI_PATH, |
2692 | 0 | 4, (const uint8_t *)"core"); |
2693 | | /* |
2694 | | * M needs to be unset as 'asking' for only the first block using |
2695 | | * Q-Block2 as a test for server support. |
2696 | | * See RFC9177 Section 4.4 Using the Q-Block2 Option. |
2697 | | * |
2698 | | * As the client is asking for 16 byte chunks, it is unlikely that |
2699 | | * the .well-known/core response will be 16 bytes or less, so |
2700 | | * if the server supports Q-Block, it will be forced to respond with |
2701 | | * a Q-Block2, so the client can detect the server Q-Block support. |
2702 | | */ |
2703 | 0 | coap_insert_option(pdu, COAP_OPTION_Q_BLOCK2, |
2704 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
2705 | 0 | (0 << 4) | (0 << 3) | 0), |
2706 | 0 | buf); |
2707 | 0 | k_token = coap_new_bin_const(pdu->actual_token.s, pdu->actual_token.length); |
2708 | 0 | set_block_mode_probe_q(session->block_mode); |
2709 | 0 | mid = coap_send_internal(session, pdu, NULL); |
2710 | 0 | if (mid == COAP_INVALID_MID) { |
2711 | 0 | coap_delete_bin_const(k_token); |
2712 | 0 | return COAP_INVALID_MID; |
2713 | 0 | } |
2714 | 0 | session->remote_test_mid = mid; |
2715 | 0 | coap_delete_bin_const(session->last_token); |
2716 | 0 | session->last_token = k_token; |
2717 | 0 | return mid; |
2718 | 0 | } |
2719 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2720 | | |
2721 | | coap_lg_crcv_t * |
2722 | | coap_block_new_lg_crcv(coap_session_t *session, coap_pdu_t *pdu, |
2723 | 0 | coap_lg_xmit_t *lg_xmit) { |
2724 | 0 | coap_block_b_t block; |
2725 | 0 | coap_lg_crcv_t *lg_crcv; |
2726 | 0 | uint64_t state_token = STATE_TOKEN_FULL(++session->tx_token, 1); |
2727 | |
|
2728 | 0 | lg_crcv = coap_malloc_type(COAP_LG_CRCV, sizeof(coap_lg_crcv_t)); |
2729 | |
|
2730 | 0 | if (lg_crcv == NULL) |
2731 | 0 | return NULL; |
2732 | | |
2733 | 0 | coap_log_debug("** %s: lg_crcv %p initialized - stateless token xxxxx%011llx\n", |
2734 | 0 | coap_session_str(session), (void *)lg_crcv, |
2735 | 0 | STATE_TOKEN_BASE(state_token)); |
2736 | 0 | memset(lg_crcv, 0, sizeof(coap_lg_crcv_t)); |
2737 | 0 | lg_crcv->initial = 1; |
2738 | 0 | coap_ticks(&lg_crcv->last_used); |
2739 | | /* Keep a copy of the sent pdu */ |
2740 | 0 | lg_crcv->sent_pdu = coap_pdu_reference_lkd(pdu); |
2741 | 0 | if (lg_xmit) { |
2742 | 0 | coap_opt_iterator_t opt_iter; |
2743 | 0 | coap_opt_t *opt; |
2744 | |
|
2745 | 0 | opt = coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter); |
2746 | |
|
2747 | 0 | if (opt) { |
2748 | 0 | int observe_action; |
2749 | |
|
2750 | 0 | observe_action = coap_decode_var_bytes(coap_opt_value(opt), |
2751 | 0 | coap_opt_length(opt)); |
2752 | 0 | if (observe_action == COAP_OBSERVE_ESTABLISH) { |
2753 | | /* Need to keep information for Observe Cancel */ |
2754 | 0 | size_t data_len; |
2755 | 0 | const uint8_t *data; |
2756 | |
|
2757 | 0 | if (coap_get_data(pdu, &data_len, &data)) { |
2758 | 0 | if (data_len < lg_xmit->data_info->length) { |
2759 | 0 | lg_xmit->data_info->ref++; |
2760 | 0 | lg_crcv->obs_data = lg_xmit->data_info; |
2761 | 0 | } |
2762 | 0 | } |
2763 | 0 | } |
2764 | 0 | } |
2765 | 0 | } |
2766 | | |
2767 | | /* Need to keep original token for updating response PDUs */ |
2768 | 0 | lg_crcv->app_token = coap_new_binary(pdu->actual_token.length); |
2769 | 0 | if (!lg_crcv->app_token) { |
2770 | 0 | coap_block_delete_lg_crcv(session, lg_crcv); |
2771 | 0 | return NULL; |
2772 | 0 | } |
2773 | 0 | memcpy(lg_crcv->app_token->s, pdu->actual_token.s, pdu->actual_token.length); |
2774 | | |
2775 | | /* Need to set up a base token for actual communications if retries needed */ |
2776 | 0 | lg_crcv->retry_counter = 1; |
2777 | 0 | lg_crcv->state_token = state_token; |
2778 | 0 | coap_address_copy(&lg_crcv->upstream, &session->addr_info.remote); |
2779 | |
|
2780 | 0 | if (pdu->code == COAP_REQUEST_CODE_FETCH) { |
2781 | 0 | coap_bin_const_t *new_token; |
2782 | | |
2783 | | /* Need to save/restore Observe Token for large FETCH */ |
2784 | 0 | new_token = track_fetch_observe(pdu, lg_crcv, 0, &pdu->actual_token); |
2785 | 0 | if (new_token) |
2786 | 0 | coap_update_token(pdu, new_token->length, new_token->s); |
2787 | 0 | } |
2788 | |
|
2789 | 0 | if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) { |
2790 | | /* In case it is there - must not be in continuing request PDUs */ |
2791 | 0 | lg_crcv->o_block_option = COAP_OPTION_BLOCK1; |
2792 | 0 | lg_crcv->o_blk_size = block.aszx; |
2793 | 0 | } |
2794 | |
|
2795 | 0 | return lg_crcv; |
2796 | 0 | } |
2797 | | |
2798 | | void |
2799 | | coap_block_delete_lg_crcv(coap_session_t *session, |
2800 | 0 | coap_lg_crcv_t *lg_crcv) { |
2801 | 0 | size_t i; |
2802 | |
|
2803 | | #if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG) |
2804 | | (void)session; |
2805 | | #endif |
2806 | 0 | if (lg_crcv == NULL) |
2807 | 0 | return; |
2808 | | |
2809 | 0 | if (lg_crcv->ref > 0) { |
2810 | 0 | lg_crcv->ref--; |
2811 | 0 | return; |
2812 | 0 | } |
2813 | | |
2814 | 0 | coap_free_type(COAP_STRING, lg_crcv->body_data); |
2815 | 0 | if (lg_crcv->obs_data) { |
2816 | 0 | coap_block_release_lg_xmit_data(session, lg_crcv->obs_data); |
2817 | 0 | lg_crcv->obs_data = NULL; |
2818 | 0 | } |
2819 | 0 | coap_address_copy(&session->addr_info.remote, &lg_crcv->upstream); |
2820 | 0 | coap_log_debug("** %s: lg_crcv %p released\n", |
2821 | 0 | coap_session_str(session), (void *)lg_crcv); |
2822 | 0 | coap_delete_binary(lg_crcv->app_token); |
2823 | 0 | for (i = 0; i < lg_crcv->obs_token_cnt; i++) { |
2824 | 0 | coap_delete_bin_const(lg_crcv->obs_token[i]); |
2825 | 0 | } |
2826 | 0 | coap_free_type(COAP_STRING, lg_crcv->obs_token); |
2827 | 0 | coap_delete_pdu_lkd(lg_crcv->sent_pdu); |
2828 | 0 | coap_free_type(COAP_LG_CRCV, lg_crcv); |
2829 | 0 | } |
2830 | | #endif /* COAP_CLIENT_SUPPORT */ |
2831 | | |
2832 | | #if COAP_SERVER_SUPPORT |
2833 | | void |
2834 | | coap_block_delete_lg_srcv(coap_session_t *session, |
2835 | 0 | coap_lg_srcv_t *lg_srcv) { |
2836 | | #if (COAP_MAX_LOGGING_LEVEL < _COAP_LOG_DEBUG) |
2837 | | (void)session; |
2838 | | #endif |
2839 | 0 | if (lg_srcv == NULL) |
2840 | 0 | return; |
2841 | | |
2842 | 0 | if (lg_srcv->ref > 0) { |
2843 | 0 | lg_srcv->ref--; |
2844 | 0 | return; |
2845 | 0 | } |
2846 | | |
2847 | 0 | coap_delete_str_const(lg_srcv->uri_path); |
2848 | 0 | coap_delete_bin_const(lg_srcv->last_token); |
2849 | 0 | coap_free_type(COAP_STRING, lg_srcv->body_data); |
2850 | 0 | coap_log_debug("** %s: lg_srcv %p released\n", |
2851 | 0 | coap_session_str(session), (void *)lg_srcv); |
2852 | 0 | coap_free_type(COAP_LG_SRCV, lg_srcv); |
2853 | 0 | } |
2854 | | #endif /* COAP_SERVER_SUPPORT */ |
2855 | | |
2856 | | void |
2857 | | coap_block_delete_lg_xmit(coap_session_t *session, |
2858 | 0 | coap_lg_xmit_t *lg_xmit) { |
2859 | 0 | if (lg_xmit == NULL) |
2860 | 0 | return; |
2861 | | |
2862 | 0 | if (lg_xmit->ref > 0) { |
2863 | 0 | lg_xmit->ref--; |
2864 | 0 | return; |
2865 | 0 | } |
2866 | | |
2867 | 0 | coap_block_release_lg_xmit_data(session, lg_xmit->data_info); |
2868 | 0 | if (COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu)) |
2869 | 0 | coap_delete_binary(lg_xmit->b.b1.app_token); |
2870 | 0 | else |
2871 | 0 | coap_delete_string(lg_xmit->b.b2.query); |
2872 | 0 | coap_delete_pdu_lkd(lg_xmit->sent_pdu); |
2873 | |
|
2874 | 0 | coap_log_debug("** %s: lg_xmit %p released\n", |
2875 | 0 | coap_session_str(session), (void *)lg_xmit); |
2876 | 0 | coap_free_type(COAP_LG_XMIT, lg_xmit); |
2877 | 0 | } |
2878 | | |
2879 | | #if COAP_SERVER_SUPPORT |
2880 | | typedef struct { |
2881 | | uint32_t num; |
2882 | | int is_continue; |
2883 | | } send_track; |
2884 | | |
2885 | | static int |
2886 | | add_block_send(uint32_t num, int is_continue, send_track *out_blocks, |
2887 | 0 | uint32_t *count, uint32_t max_count) { |
2888 | 0 | uint32_t i; |
2889 | |
|
2890 | 0 | for (i = 0; i < *count && *count < max_count; i++) { |
2891 | 0 | if (num == out_blocks[i].num) |
2892 | 0 | return 0; |
2893 | 0 | else if (num < out_blocks[i].num) { |
2894 | 0 | if (*count - i > 1) |
2895 | 0 | memmove(&out_blocks[i], &out_blocks[i+1], *count - i -1); |
2896 | 0 | out_blocks[i].num = num; |
2897 | 0 | out_blocks[i].is_continue = is_continue; |
2898 | 0 | (*count)++; |
2899 | 0 | return 1; |
2900 | 0 | } |
2901 | 0 | } |
2902 | 0 | if (*count < max_count) { |
2903 | 0 | out_blocks[i].num = num; |
2904 | 0 | out_blocks[i].is_continue = is_continue; |
2905 | 0 | (*count)++; |
2906 | 0 | return 1; |
2907 | 0 | } |
2908 | 0 | return 0; |
2909 | 0 | } |
2910 | | |
2911 | | /* |
2912 | | * Need to see if this is a request for the next block of a large body |
2913 | | * transfer. If so, need to initiate the response with the next blocks |
2914 | | * and not trouble the application. |
2915 | | * |
2916 | | * If additional responses needed, then these are explicitly sent out and |
2917 | | * 'response' is updated to be the last response to be sent. There can be |
2918 | | * multiple Q-Block2 in the request, as well as the 'Continue' Q-Block2 |
2919 | | * request. |
2920 | | * |
2921 | | * This is set up using coap_add_data_large_response_lkd() |
2922 | | * |
2923 | | * Server is sending a large data response to GET / observe (Block2) |
2924 | | * |
2925 | | * Return: 0 Call application handler |
2926 | | * 1 Do not call application handler - just send the built response |
2927 | | */ |
2928 | | int |
2929 | | coap_handle_request_send_block(coap_session_t *session, |
2930 | | coap_pdu_t *pdu, |
2931 | | coap_pdu_t *response, |
2932 | | coap_resource_t *resource, |
2933 | 0 | coap_string_t *query) { |
2934 | 0 | coap_lg_xmit_t *lg_xmit = NULL; |
2935 | 0 | coap_block_b_t block; |
2936 | 0 | coap_block_b_t alt_block; |
2937 | 0 | uint16_t block_opt = 0; |
2938 | 0 | send_track *out_blocks = NULL; |
2939 | 0 | const char *error_phrase; |
2940 | 0 | coap_opt_iterator_t opt_iter; |
2941 | 0 | size_t chunk; |
2942 | 0 | coap_opt_iterator_t opt_b_iter; |
2943 | 0 | coap_opt_t *option; |
2944 | 0 | uint32_t request_cnt, i; |
2945 | 0 | coap_opt_t *etag_opt = NULL; |
2946 | 0 | coap_pdu_t *out_pdu = response; |
2947 | 0 | #if COAP_Q_BLOCK_SUPPORT |
2948 | 0 | size_t max_block; |
2949 | | |
2950 | | /* Is client indicating that it supports Q_BLOCK2 ? */ |
2951 | 0 | if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) { |
2952 | 0 | if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) |
2953 | 0 | set_block_mode_has_q(session->block_mode); |
2954 | 0 | block_opt = COAP_OPTION_Q_BLOCK2; |
2955 | 0 | } |
2956 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2957 | 0 | if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK2, &alt_block)) { |
2958 | 0 | if (block_opt) { |
2959 | 0 | coap_log_warn("Block2 and Q-Block2 cannot be in the same request\n"); |
2960 | 0 | coap_add_data(response, sizeof("Both Block2 and Q-Block2 invalid")-1, |
2961 | 0 | (const uint8_t *)"Both Block2 and Q-Block2 invalid"); |
2962 | 0 | response->code = COAP_RESPONSE_CODE(400); |
2963 | 0 | goto skip_app_handler; |
2964 | 0 | } |
2965 | 0 | block = alt_block; |
2966 | 0 | block_opt = COAP_OPTION_BLOCK2; |
2967 | 0 | } |
2968 | 0 | if (block_opt == 0) |
2969 | 0 | return 0; |
2970 | 0 | if (block.num == 0) { |
2971 | 0 | #if COAP_Q_BLOCK_SUPPORT |
2972 | 0 | if (block_opt == COAP_OPTION_Q_BLOCK2) { |
2973 | 0 | if (block.m) { |
2974 | 0 | return 0; |
2975 | 0 | } |
2976 | 0 | } else |
2977 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
2978 | | /* Get a fresh copy of the data */ |
2979 | 0 | return 0; |
2980 | 0 | } |
2981 | 0 | lg_xmit = coap_find_lg_xmit_response(session, pdu, resource, query); |
2982 | 0 | if (lg_xmit == NULL) |
2983 | 0 | return 0; |
2984 | | |
2985 | 0 | #if COAP_Q_BLOCK_SUPPORT |
2986 | 0 | out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track) * COAP_MAX_PAYLOADS(session)); |
2987 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
2988 | | out_blocks = coap_malloc_type(COAP_STRING, sizeof(send_track)); |
2989 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
2990 | 0 | if (!out_blocks) { |
2991 | 0 | goto internal_issue; |
2992 | 0 | } |
2993 | | |
2994 | | /* lg_xmit (response) found */ |
2995 | | |
2996 | 0 | etag_opt = coap_check_option(pdu, COAP_OPTION_ETAG, &opt_iter); |
2997 | 0 | if (etag_opt) { |
2998 | | /* There may be multiple ETag - need to check each one */ |
2999 | 0 | coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); |
3000 | 0 | while ((etag_opt = coap_option_next(&opt_iter))) { |
3001 | 0 | if (opt_iter.number == COAP_OPTION_ETAG) { |
3002 | 0 | uint64_t etag = coap_decode_var_bytes8(coap_opt_value(etag_opt), |
3003 | 0 | coap_opt_length(etag_opt)); |
3004 | 0 | if (etag == lg_xmit->b.b2.etag) { |
3005 | 0 | break; |
3006 | 0 | } |
3007 | 0 | } |
3008 | 0 | } |
3009 | 0 | if (!etag_opt) { |
3010 | | /* Not a match - pass up to a higher level */ |
3011 | 0 | return 0; |
3012 | 0 | } |
3013 | 0 | } |
3014 | 0 | out_pdu->code = lg_xmit->sent_pdu->code; |
3015 | 0 | coap_ticks(&lg_xmit->last_obs); |
3016 | |
|
3017 | 0 | chunk = (size_t)1 << (lg_xmit->blk_size + 4); |
3018 | 0 | if (block_opt) { |
3019 | 0 | if (block.bert) { |
3020 | 0 | coap_log_debug("found Block option, block is BERT, block nr. %u, M %d\n", |
3021 | 0 | block.num, block.m); |
3022 | 0 | } else { |
3023 | 0 | coap_log_debug("found Block option, block size is %u, block nr. %u, M %d\n", |
3024 | 0 | 1 << (block.szx + 4), block.num, block.m); |
3025 | 0 | } |
3026 | 0 | if (block.bert == 0 && block.szx != lg_xmit->blk_size) { |
3027 | 0 | if (block.num == 0) { |
3028 | 0 | if (block.szx < lg_xmit->blk_size) { |
3029 | | /* |
3030 | | * Recompute the block number of the previous packet given |
3031 | | * the new block size |
3032 | | */ |
3033 | 0 | block.num = (uint32_t)((chunk >> (block.szx + 4)) - 1); |
3034 | 0 | chunk = (size_t)1 << (lg_xmit->blk_size + 4); |
3035 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3036 | 0 | lg_xmit->send_blocks.range[0].begin = block.num; |
3037 | 0 | lg_xmit->send_blocks.range[0].end = (uint32_t)((lg_xmit->data_info->length - 1) / chunk); |
3038 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3039 | 0 | lg_xmit->blk_size = block.szx; |
3040 | 0 | coap_log_debug("new Block size is %u, block number %u completed\n", |
3041 | 0 | (1 << (block.szx + 4)), block.num); |
3042 | 0 | } else { |
3043 | 0 | coap_log_debug("ignoring request to increase Block size from %u to %u\n", |
3044 | 0 | (1 << (lg_xmit->blk_size + 4)), (1 << (lg_xmit->blk_size + 4))); |
3045 | 0 | } |
3046 | 0 | } else { |
3047 | 0 | coap_log_debug("ignoring request to change Block size from %u to %u\n", |
3048 | 0 | (1 << (lg_xmit->blk_size + 4)), (1 << (block.szx + 4))); |
3049 | 0 | block.szx = block.aszx = lg_xmit->blk_size; |
3050 | 0 | } |
3051 | 0 | } |
3052 | 0 | } |
3053 | | |
3054 | | /* |
3055 | | * Need to check if there are multiple Q-Block2 requests. If so, they |
3056 | | * need to be sent out in order of requests with the final request being |
3057 | | * handled as per singular Block 2 request. |
3058 | | */ |
3059 | 0 | request_cnt = 0; |
3060 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3061 | 0 | max_block = (lg_xmit->data_info->length + chunk - 1)/chunk; |
3062 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3063 | 0 | coap_option_iterator_init(pdu, &opt_b_iter, COAP_OPT_ALL); |
3064 | 0 | while ((option = coap_option_next(&opt_b_iter))) { |
3065 | 0 | uint32_t num; |
3066 | 0 | if (opt_b_iter.number != lg_xmit->option) |
3067 | 0 | continue; |
3068 | 0 | num = coap_opt_block_num(option); |
3069 | 0 | if (num > 0xFFFFF) /* 20 bits max for num */ |
3070 | 0 | continue; |
3071 | 0 | if (block.aszx != COAP_OPT_BLOCK_SZX(option)) { |
3072 | 0 | coap_add_data(response, |
3073 | 0 | sizeof("Changing blocksize during request invalid")-1, |
3074 | 0 | (const uint8_t *)"Changing blocksize during request invalid"); |
3075 | 0 | response->code = COAP_RESPONSE_CODE(400); |
3076 | 0 | goto skip_app_handler; |
3077 | 0 | } |
3078 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3079 | 0 | if (COAP_OPT_BLOCK_MORE(option) && lg_xmit->option == COAP_OPTION_Q_BLOCK2) { |
3080 | 0 | if ((num % COAP_MAX_PAYLOADS(session)) == 0) { |
3081 | 0 | if (num == 0) { |
3082 | | /* This is a repeat request for everything - hmm */ |
3083 | 0 | goto call_app_handler; |
3084 | 0 | } |
3085 | | /* 'Continue' request */ |
3086 | 0 | for (i = 0; i < COAP_MAX_PAYLOADS(session) && |
3087 | 0 | num + i < max_block; i++) { |
3088 | 0 | add_block_send(num + i, 1, out_blocks, &request_cnt, |
3089 | 0 | COAP_MAX_PAYLOADS(session)); |
3090 | 0 | lg_xmit->last_block = num + i; |
3091 | 0 | } |
3092 | 0 | } else { |
3093 | | /* Requesting remaining payloads in this MAX_PAYLOADS */ |
3094 | 0 | for (i = 0; i < COAP_MAX_PAYLOADS(session) - |
3095 | 0 | num % COAP_MAX_PAYLOADS(session) && |
3096 | 0 | num + i < max_block; i++) { |
3097 | 0 | add_block_send(num + i, 0, out_blocks, &request_cnt, |
3098 | 0 | COAP_MAX_PAYLOADS(session)); |
3099 | 0 | } |
3100 | 0 | } |
3101 | 0 | } else |
3102 | 0 | add_block_send(num, 0, out_blocks, &request_cnt, |
3103 | 0 | COAP_MAX_PAYLOADS(session)); |
3104 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
3105 | | add_block_send(num, 0, out_blocks, &request_cnt, 1); |
3106 | | break; |
3107 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
3108 | 0 | } |
3109 | 0 | if (request_cnt == 0) { |
3110 | | /* Block2 or Q-Block2 not found - give them the first block */ |
3111 | 0 | block.szx = lg_xmit->blk_size; |
3112 | 0 | out_blocks[0].num = 0; |
3113 | 0 | out_blocks[0].is_continue = 0; |
3114 | 0 | request_cnt = 1; |
3115 | 0 | } |
3116 | |
|
3117 | 0 | for (i = 0; i < request_cnt; i++) { |
3118 | 0 | uint8_t buf[8]; |
3119 | 0 | size_t offset; |
3120 | |
|
3121 | 0 | block.num = out_blocks[i].num; |
3122 | |
|
3123 | 0 | if (i + 1 < request_cnt) { |
3124 | | /* Need to set up a copy of the pdu to send */ |
3125 | 0 | coap_opt_filter_t drop_options; |
3126 | |
|
3127 | 0 | memset(&drop_options, 0, sizeof(coap_opt_filter_t)); |
3128 | 0 | if (block.num != 0) |
3129 | 0 | coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); |
3130 | 0 | if (out_blocks[i].is_continue) { |
3131 | 0 | out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, |
3132 | 0 | lg_xmit->sent_pdu->actual_token.length, |
3133 | 0 | lg_xmit->sent_pdu->actual_token.s, |
3134 | 0 | &drop_options, COAP_BOOL_FALSE); |
3135 | 0 | } else { |
3136 | 0 | out_pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, |
3137 | 0 | pdu->actual_token.length, |
3138 | 0 | pdu->actual_token.s, |
3139 | 0 | &drop_options, COAP_BOOL_FALSE); |
3140 | 0 | } |
3141 | 0 | if (!out_pdu) { |
3142 | 0 | goto internal_issue; |
3143 | 0 | } |
3144 | 0 | } else { |
3145 | 0 | if (out_blocks[i].is_continue) |
3146 | 0 | coap_update_token(response, lg_xmit->sent_pdu->actual_token.length, |
3147 | 0 | lg_xmit->sent_pdu->actual_token.s); |
3148 | | /* |
3149 | | * Copy the options across and then fix the block option |
3150 | | * |
3151 | | * Need to drop Observe option if Block2 and block.num != 0 |
3152 | | */ |
3153 | 0 | coap_option_iterator_init(lg_xmit->sent_pdu, &opt_iter, COAP_OPT_ALL); |
3154 | 0 | while ((option = coap_option_next(&opt_iter))) { |
3155 | 0 | if (opt_iter.number == COAP_OPTION_OBSERVE && block.num != 0) |
3156 | 0 | continue; |
3157 | 0 | if (!coap_insert_option(response, opt_iter.number, |
3158 | 0 | coap_opt_length(option), |
3159 | 0 | coap_opt_value(option))) { |
3160 | 0 | goto internal_issue; |
3161 | 0 | } |
3162 | 0 | } |
3163 | 0 | out_pdu = response; |
3164 | 0 | } |
3165 | 0 | if (pdu->type == COAP_MESSAGE_NON) |
3166 | 0 | out_pdu->type = COAP_MESSAGE_NON; |
3167 | 0 | offset = block.num * chunk; |
3168 | 0 | if (block.bert) { |
3169 | 0 | size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : pdu->used_size; |
3170 | 0 | block.m = (lg_xmit->data_info->length - offset) > |
3171 | 0 | ((out_pdu->max_size - token_options) /1024) * 1024; |
3172 | 0 | } else { |
3173 | 0 | block.m = (offset + chunk) < lg_xmit->data_info->length; |
3174 | 0 | } |
3175 | 0 | if (!coap_update_option(out_pdu, lg_xmit->option, |
3176 | 0 | coap_encode_var_safe(buf, |
3177 | 0 | sizeof(buf), |
3178 | 0 | (block.num << 4) | |
3179 | 0 | (block.m << 3) | |
3180 | 0 | block.aszx), |
3181 | 0 | buf)) { |
3182 | 0 | goto internal_issue; |
3183 | 0 | } |
3184 | 0 | if (!(offset + chunk < lg_xmit->data_info->length)) { |
3185 | | /* Last block - keep in cache for 4 * ACK_TIMOUT */ |
3186 | 0 | coap_ticks(&lg_xmit->last_all_sent); |
3187 | 0 | } |
3188 | 0 | if (lg_xmit->b.b2.maxage_expire) { |
3189 | 0 | coap_tick_t now; |
3190 | 0 | coap_time_t rem; |
3191 | |
|
3192 | 0 | if (!(offset + chunk < lg_xmit->data_info->length)) { |
3193 | | /* Last block - keep in cache for 4 * ACK_TIMOUT */ |
3194 | 0 | coap_ticks(&lg_xmit->last_all_sent); |
3195 | 0 | } |
3196 | 0 | coap_ticks(&now); |
3197 | 0 | rem = coap_ticks_to_rt(now); |
3198 | 0 | if (lg_xmit->b.b2.maxage_expire > rem) { |
3199 | 0 | rem = lg_xmit->b.b2.maxage_expire - rem; |
3200 | 0 | } else { |
3201 | 0 | rem = 0; |
3202 | | /* Entry needs to be expired */ |
3203 | 0 | coap_ticks(&lg_xmit->last_all_sent); |
3204 | 0 | } |
3205 | 0 | if (!coap_update_option(out_pdu, COAP_OPTION_MAXAGE, |
3206 | 0 | coap_encode_var_safe8(buf, |
3207 | 0 | sizeof(buf), |
3208 | 0 | rem), |
3209 | 0 | buf)) { |
3210 | 0 | goto internal_issue; |
3211 | 0 | } |
3212 | 0 | } |
3213 | | |
3214 | 0 | if (!coap_add_block_b_data(out_pdu, |
3215 | 0 | lg_xmit->data_info->length, |
3216 | 0 | lg_xmit->data_info->data, |
3217 | 0 | &block)) { |
3218 | 0 | goto internal_issue; |
3219 | 0 | } |
3220 | 0 | if (i + 1 < request_cnt) { |
3221 | 0 | coap_ticks(&lg_xmit->last_sent); |
3222 | 0 | coap_send_internal(session, out_pdu, NULL); |
3223 | 0 | } |
3224 | 0 | } |
3225 | 0 | coap_ticks(&lg_xmit->last_payload); |
3226 | 0 | coap_ticks(&lg_xmit->last_sent); |
3227 | 0 | if (lg_xmit->last_all_sent) { |
3228 | 0 | coap_ticks(&lg_xmit->last_all_sent); |
3229 | 0 | } |
3230 | 0 | goto skip_app_handler; |
3231 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3232 | 0 | call_app_handler: |
3233 | 0 | coap_free_type(COAP_STRING, out_blocks); |
3234 | 0 | return 0; |
3235 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3236 | | |
3237 | 0 | internal_issue: |
3238 | 0 | response->code = COAP_RESPONSE_CODE(500); |
3239 | 0 | error_phrase = coap_response_phrase(response->code); |
3240 | 0 | coap_add_data(response, strlen(error_phrase), |
3241 | 0 | (const uint8_t *)error_phrase); |
3242 | | /* Keep in cache for 4 * ACK_TIMOUT in case of retry */ |
3243 | 0 | if (lg_xmit) |
3244 | 0 | coap_ticks(&lg_xmit->last_all_sent); |
3245 | |
|
3246 | 0 | skip_app_handler: |
3247 | 0 | coap_free_type(COAP_STRING, out_blocks); |
3248 | 0 | return 1; |
3249 | 0 | } |
3250 | | #endif /* COAP_SERVER_SUPPORT */ |
3251 | | |
3252 | | #if COAP_Q_BLOCK_SUPPORT |
3253 | | static int |
3254 | 0 | blocks_delete_entry(coap_rblock_t *rec_blocks, uint32_t block_num) { |
3255 | 0 | uint32_t i; |
3256 | |
|
3257 | 0 | if (rec_blocks->total_blocks && block_num + 1 > rec_blocks->total_blocks) { |
3258 | | /* received block number greater than Block No defined when More bit unset */ |
3259 | 0 | return 0; |
3260 | 0 | } |
3261 | | |
3262 | 0 | for (i = 0; i < rec_blocks->used; i++) { |
3263 | 0 | if (block_num >= rec_blocks->range[i].begin && |
3264 | 0 | block_num <= rec_blocks->range[i].end) { |
3265 | | /* In this block */ |
3266 | 0 | if (block_num == rec_blocks->range[i].begin) { |
3267 | 0 | if (block_num == rec_blocks->range[i].end) { |
3268 | | /* Need to delete this range */ |
3269 | 0 | if (i + 1 < rec_blocks->used) { |
3270 | 0 | memmove(&rec_blocks->range[i], &rec_blocks->range[i+1], |
3271 | 0 | (rec_blocks->used - i) * sizeof(rec_blocks->range[0])); |
3272 | 0 | } |
3273 | 0 | rec_blocks->used--; |
3274 | 0 | break; |
3275 | 0 | } |
3276 | 0 | rec_blocks->range[i].begin++; |
3277 | 0 | } else if (block_num == rec_blocks->range[i].end) { |
3278 | 0 | rec_blocks->range[i].end--; |
3279 | 0 | if (rec_blocks->range[i].begin == rec_blocks->range[i].end) { |
3280 | | /* Need to delete this range */ |
3281 | 0 | rec_blocks->used--; |
3282 | 0 | if (i == rec_blocks->used) |
3283 | 0 | break; |
3284 | 0 | memmove(&rec_blocks->range[i], &rec_blocks->range[i+1], |
3285 | 0 | sizeof(rec_blocks->range[i]) * (rec_blocks->used - i)); |
3286 | 0 | } |
3287 | 0 | } else { |
3288 | | /* Need to split the range */ |
3289 | 0 | if (rec_blocks->used == COAP_RBLOCK_CNT) |
3290 | | /* Too many losses */ |
3291 | 0 | return 0; |
3292 | 0 | memmove(&rec_blocks->range[i+1], &rec_blocks->range[i], |
3293 | 0 | (rec_blocks->used - i) * sizeof(rec_blocks->range[0])); |
3294 | 0 | rec_blocks->range[i].end = block_num - 1; |
3295 | 0 | rec_blocks->range[i+1].begin = block_num + 1; |
3296 | 0 | rec_blocks->used++; |
3297 | 0 | } |
3298 | 0 | break; |
3299 | 0 | } |
3300 | 0 | } |
3301 | 0 | coap_ticks(&rec_blocks->last_seen); |
3302 | 0 | return 1; |
3303 | 0 | } |
3304 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3305 | | |
3306 | | static int |
3307 | 0 | blocks_add_entry(coap_rblock_t *rec_blocks, uint32_t block_num, uint32_t block_m) { |
3308 | 0 | uint32_t i; |
3309 | |
|
3310 | 0 | if (rec_blocks->total_blocks && block_num + 1 > rec_blocks->total_blocks) { |
3311 | | /* received block number greater than Block No defined when More bit unset */ |
3312 | 0 | return 0; |
3313 | 0 | } |
3314 | | |
3315 | | /* Reset as there is activity */ |
3316 | 0 | rec_blocks->retry = 0; |
3317 | |
|
3318 | 0 | for (i = 0; i < rec_blocks->used; i++) { |
3319 | 0 | if (block_num >= rec_blocks->range[i].begin && |
3320 | 0 | block_num <= rec_blocks->range[i].end) |
3321 | 0 | break; |
3322 | | |
3323 | 0 | if (block_num < rec_blocks->range[i].begin) { |
3324 | 0 | if (block_num + 1 == rec_blocks->range[i].begin) { |
3325 | 0 | rec_blocks->range[i].begin = block_num; |
3326 | 0 | } else { |
3327 | | /* Need to insert a new range */ |
3328 | 0 | if (rec_blocks->used == COAP_RBLOCK_CNT) |
3329 | | /* Too many losses */ |
3330 | 0 | return 0; |
3331 | 0 | memmove(&rec_blocks->range[i+1], &rec_blocks->range[i], |
3332 | 0 | (rec_blocks->used - i) * sizeof(rec_blocks->range[0])); |
3333 | 0 | rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num; |
3334 | 0 | rec_blocks->used++; |
3335 | 0 | } |
3336 | 0 | break; |
3337 | 0 | } |
3338 | 0 | if (block_num == rec_blocks->range[i].end + 1) { |
3339 | 0 | rec_blocks->range[i].end = block_num; |
3340 | 0 | if (i + 1 < rec_blocks->used) { |
3341 | 0 | if (rec_blocks->range[i+1].begin == block_num + 1) { |
3342 | | /* Merge the 2 ranges */ |
3343 | 0 | rec_blocks->range[i].end = rec_blocks->range[i+1].end; |
3344 | 0 | if (i+2 < rec_blocks->used) { |
3345 | 0 | memmove(&rec_blocks->range[i+1], &rec_blocks->range[i+2], |
3346 | 0 | (rec_blocks->used - (i+2)) * sizeof(rec_blocks->range[0])); |
3347 | 0 | } |
3348 | 0 | rec_blocks->used--; |
3349 | 0 | } |
3350 | 0 | } |
3351 | 0 | break; |
3352 | 0 | } |
3353 | 0 | } |
3354 | 0 | if (i == rec_blocks->used) { |
3355 | 0 | if (rec_blocks->used == COAP_RBLOCK_CNT) { |
3356 | | /* Too many losses */ |
3357 | 0 | return 0; |
3358 | 0 | } |
3359 | 0 | rec_blocks->range[i].begin = rec_blocks->range[i].end = block_num; |
3360 | 0 | rec_blocks->used++; |
3361 | 0 | } |
3362 | 0 | if (!block_m) |
3363 | 0 | rec_blocks->total_blocks = block_num + 1; |
3364 | |
|
3365 | 0 | coap_ticks(&rec_blocks->last_seen); |
3366 | 0 | return 1; |
3367 | 0 | } |
3368 | | |
3369 | | #if COAP_SERVER_SUPPORT |
3370 | | /* |
3371 | | * Need to check if this is a large PUT / POST etc. using multiple blocks |
3372 | | * |
3373 | | * Server receiving PUT/POST etc. of a large amount of data (Block1) |
3374 | | * |
3375 | | * Return: 0 Call application handler |
3376 | | * 1 Do not call application handler - just send the built response |
3377 | | */ |
3378 | | int |
3379 | | coap_handle_request_put_block(coap_context_t *context, |
3380 | | coap_session_t *session, |
3381 | | coap_pdu_t *pdu, |
3382 | | coap_pdu_t *response, |
3383 | | coap_resource_t *resource, |
3384 | | coap_string_t *uri_path, |
3385 | | coap_opt_t *observe, |
3386 | | int *added_block, |
3387 | 0 | coap_lg_srcv_t **pfree_lg_srcv) { |
3388 | 0 | size_t length = 0; |
3389 | 0 | const uint8_t *data = NULL; |
3390 | 0 | size_t offset = 0; |
3391 | 0 | size_t total = 0; |
3392 | 0 | coap_block_b_t block; |
3393 | 0 | coap_opt_iterator_t opt_iter; |
3394 | 0 | uint16_t block_option = 0; |
3395 | 0 | coap_lg_srcv_t *lg_srcv; |
3396 | 0 | coap_opt_t *size_opt; |
3397 | 0 | coap_opt_t *fmt_opt; |
3398 | 0 | uint16_t fmt; |
3399 | 0 | coap_opt_t *rtag_opt; |
3400 | 0 | size_t rtag_length; |
3401 | 0 | const uint8_t *rtag; |
3402 | 0 | uint32_t max_block_szx; |
3403 | 0 | int update_data; |
3404 | 0 | unsigned int saved_num; |
3405 | 0 | size_t saved_offset; |
3406 | 0 | int lg_srcv_is_refed = 0; |
3407 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3408 | 0 | int request_missing = 0; |
3409 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3410 | |
|
3411 | 0 | *added_block = 0; |
3412 | 0 | *pfree_lg_srcv = NULL; |
3413 | 0 | coap_get_data_large(pdu, &length, &data, &offset, &total); |
3414 | 0 | pdu->body_offset = 0; |
3415 | 0 | pdu->body_total = length; |
3416 | |
|
3417 | 0 | if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block)) { |
3418 | 0 | block_option = COAP_OPTION_BLOCK1; |
3419 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3420 | 0 | if (coap_check_option(pdu, COAP_OPTION_Q_BLOCK1, &opt_iter)) { |
3421 | | /* Cannot handle Q-Block1 as well */ |
3422 | 0 | coap_add_data(response, sizeof("Block1 + Q-Block1 together")-1, |
3423 | 0 | (const uint8_t *)"Block1 + Q-Block1 together"); |
3424 | 0 | response->code = COAP_RESPONSE_CODE(402); |
3425 | 0 | goto skip_app_handler; |
3426 | 0 | } |
3427 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3428 | 0 | } |
3429 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3430 | 0 | else if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) { |
3431 | 0 | block_option = COAP_OPTION_Q_BLOCK1; |
3432 | 0 | set_block_mode_has_q(session->block_mode); |
3433 | 0 | } |
3434 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3435 | 0 | if (!block_option || |
3436 | 0 | (block_option == COAP_OPTION_BLOCK1 && block.num == 0 && block.m == 0)) { |
3437 | | /* Not blocked, or a single block */ |
3438 | 0 | if (context->max_body_size && total > context->max_body_size) { |
3439 | 0 | uint8_t buf[4]; |
3440 | |
|
3441 | 0 | coap_update_option(response, |
3442 | 0 | COAP_OPTION_SIZE1, |
3443 | 0 | coap_encode_var_safe((uint8_t *)buf, sizeof(buf), |
3444 | 0 | context->max_body_size), |
3445 | 0 | (uint8_t *)buf); |
3446 | 0 | response->code = COAP_RESPONSE_CODE(413); |
3447 | 0 | coap_log_warn("Unable to handle data size %" PRIuS " (max %" PRIu32 ")\n", total, |
3448 | 0 | context->max_body_size); |
3449 | 0 | goto skip_app_handler; |
3450 | 0 | } |
3451 | 0 | goto call_app_handler; |
3452 | 0 | } |
3453 | | |
3454 | 0 | size_opt = coap_check_option(pdu, |
3455 | 0 | COAP_OPTION_SIZE1, |
3456 | 0 | &opt_iter); |
3457 | 0 | fmt_opt = coap_check_option(pdu, |
3458 | 0 | COAP_OPTION_CONTENT_FORMAT, |
3459 | 0 | &opt_iter); |
3460 | 0 | fmt = fmt_opt ? coap_decode_var_bytes(coap_opt_value(fmt_opt), |
3461 | 0 | coap_opt_length(fmt_opt)) : |
3462 | 0 | COAP_MEDIATYPE_TEXT_PLAIN; |
3463 | 0 | rtag_opt = coap_check_option(pdu, |
3464 | 0 | COAP_OPTION_RTAG, |
3465 | 0 | &opt_iter); |
3466 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3467 | 0 | if (block_option == COAP_OPTION_Q_BLOCK1 && (!size_opt || !rtag_opt)) { |
3468 | | /* RFC9177 section-4.3 */ |
3469 | 0 | coap_log_info("Q-Block1: Size1 and RTag options required\n"); |
3470 | 0 | response->code = COAP_RESPONSE_CODE(400); |
3471 | 0 | goto skip_app_handler; |
3472 | 0 | } |
3473 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3474 | 0 | rtag_length = rtag_opt ? coap_opt_length(rtag_opt) : 0; |
3475 | 0 | rtag_length = min(rtag_length, 8); |
3476 | 0 | rtag = rtag_opt ? coap_opt_value(rtag_opt) : NULL; |
3477 | |
|
3478 | 0 | if (length > block.chunk_size) { |
3479 | 0 | coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %" PRIuS "\n", |
3480 | 0 | block.chunk_size, length); |
3481 | 0 | length = block.chunk_size; |
3482 | 0 | } else if (!block.bert && block.m && length != block.chunk_size) { |
3483 | 0 | coap_log_info("block: Undersized packet chunk %"PRIu32" got %" PRIuS "\n", |
3484 | 0 | block.chunk_size, length); |
3485 | 0 | response->code = COAP_RESPONSE_CODE(400); |
3486 | 0 | goto skip_app_handler; |
3487 | 0 | } |
3488 | 0 | total = size_opt ? coap_decode_var_bytes(coap_opt_value(size_opt), |
3489 | 0 | coap_opt_length(size_opt)) : 0; |
3490 | 0 | if (total) { |
3491 | 0 | uint32_t max_body; |
3492 | |
|
3493 | 0 | max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode); |
3494 | 0 | if (max_block_szx == 0 || max_block_szx > block.szx) { |
3495 | 0 | max_block_szx = block.szx; |
3496 | 0 | } |
3497 | 0 | max_body = ((1UL << 20) * (1 << (max_block_szx + 4))); |
3498 | 0 | if (max_body > MAX_BLK_LEN) |
3499 | 0 | max_body = MAX_BLK_LEN; |
3500 | 0 | if ((context->max_body_size && total > context->max_body_size) || |
3501 | 0 | (total > max_body)) { |
3502 | | /* Suggested body size larger than allowed */ |
3503 | 0 | char buf[32]; |
3504 | 0 | uint32_t max_body_size = context->max_body_size; |
3505 | |
|
3506 | 0 | if (max_body_size == 0 || max_body < max_body_size) { |
3507 | 0 | max_body_size = max_body; |
3508 | 0 | } |
3509 | 0 | coap_update_option(response, |
3510 | 0 | COAP_OPTION_SIZE1, |
3511 | 0 | coap_encode_var_safe((uint8_t *)buf, sizeof(buf), |
3512 | 0 | max_body_size), |
3513 | 0 | (uint8_t *)buf); |
3514 | 0 | snprintf(buf, sizeof(buf), "Max body size %" PRIu32, max_body_size); |
3515 | 0 | coap_add_data(response, strlen(buf), (uint8_t *)buf); |
3516 | 0 | response->code = COAP_RESPONSE_CODE(413); |
3517 | 0 | coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", total, max_body_size); |
3518 | 0 | goto skip_app_handler; |
3519 | 0 | } |
3520 | 0 | } |
3521 | 0 | offset = block.num << (block.szx + 4); |
3522 | |
|
3523 | 0 | if (!(session->block_mode & |
3524 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3525 | 0 | (COAP_BLOCK_SINGLE_BODY|COAP_BLOCK_HAS_Q_BLOCK|COAP_BLOCK_NOT_RANDOM_BLOCK1)) |
3526 | | #else /* COAP_Q_BLOCK_SUPPORT */ |
3527 | | (COAP_BLOCK_SINGLE_BODY|COAP_BLOCK_NOT_RANDOM_BLOCK1)) |
3528 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3529 | 0 | && !block.bert) { |
3530 | 0 | uint8_t buf[4]; |
3531 | | |
3532 | | /* Ask for the next block */ |
3533 | 0 | coap_insert_option(response, block_option, |
3534 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
3535 | 0 | (block.num << 4) | |
3536 | 0 | (block.m << 3) | |
3537 | 0 | block.aszx), |
3538 | 0 | buf); |
3539 | | /* Not re-assembling or checking for receipt order */ |
3540 | 0 | pdu->body_data = data; |
3541 | 0 | pdu->body_length = length; |
3542 | 0 | pdu->body_offset = offset; |
3543 | 0 | if (total < (length + offset + (block.m ? 1 : 0))) |
3544 | 0 | total = length + offset + (block.m ? 1 : 0); |
3545 | 0 | pdu->body_total = total; |
3546 | 0 | *added_block = block.m; |
3547 | | /* The application is responsible for returning the correct 2.01/2.04/2.31 etc. */ |
3548 | 0 | goto call_app_handler; |
3549 | 0 | } |
3550 | | |
3551 | | /* |
3552 | | * locate the lg_srcv |
3553 | | */ |
3554 | 0 | LL_FOREACH(session->lg_srcv, lg_srcv) { |
3555 | 0 | if (rtag_opt || lg_srcv->rtag_set == 1) { |
3556 | 0 | if (!(rtag_opt && lg_srcv->rtag_set == 1)) |
3557 | 0 | continue; |
3558 | 0 | if (lg_srcv->rtag_length != rtag_length || |
3559 | 0 | memcmp(lg_srcv->rtag, rtag, rtag_length) != 0) |
3560 | 0 | continue; |
3561 | 0 | } |
3562 | 0 | if (resource == lg_srcv->resource) { |
3563 | 0 | break; |
3564 | 0 | } |
3565 | 0 | if ((lg_srcv->resource == context->unknown_resource || |
3566 | 0 | resource == context->proxy_uri_resource) && |
3567 | 0 | coap_string_equal(uri_path, lg_srcv->uri_path)) |
3568 | 0 | break; |
3569 | 0 | } |
3570 | |
|
3571 | 0 | if (!lg_srcv && block.num != 0 && session->block_mode & COAP_BLOCK_NOT_RANDOM_BLOCK1) { |
3572 | 0 | coap_add_data(response, sizeof("Missing block 0")-1, |
3573 | 0 | (const uint8_t *)"Missing block 0"); |
3574 | 0 | response->code = COAP_RESPONSE_CODE(408); |
3575 | 0 | goto skip_app_handler; |
3576 | 0 | } |
3577 | | |
3578 | 0 | if (!lg_srcv) { |
3579 | | /* Allocate lg_srcv to use for tracking */ |
3580 | 0 | lg_srcv = coap_malloc_type(COAP_LG_SRCV, sizeof(coap_lg_srcv_t)); |
3581 | 0 | if (lg_srcv == NULL) { |
3582 | 0 | coap_add_data(response, sizeof("Memory issue")-1, |
3583 | 0 | (const uint8_t *)"Memory issue"); |
3584 | 0 | response->code = COAP_RESPONSE_CODE(500); |
3585 | 0 | goto skip_app_handler; |
3586 | 0 | } |
3587 | 0 | coap_log_debug("** %s: lg_srcv %p initialized\n", |
3588 | 0 | coap_session_str(session), (void *)lg_srcv); |
3589 | 0 | memset(lg_srcv, 0, sizeof(coap_lg_srcv_t)); |
3590 | 0 | lg_srcv->resource = resource; |
3591 | 0 | if (resource == context->unknown_resource || |
3592 | 0 | resource == context->proxy_uri_resource) |
3593 | 0 | lg_srcv->uri_path = coap_new_str_const(uri_path->s, uri_path->length); |
3594 | 0 | lg_srcv->content_format = fmt; |
3595 | 0 | lg_srcv->total_len = total; |
3596 | 0 | max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode); |
3597 | 0 | if (!block.bert && block.num == 0 && max_block_szx != 0 && |
3598 | 0 | max_block_szx < block.szx) { |
3599 | 0 | lg_srcv->szx = max_block_szx; |
3600 | 0 | } else { |
3601 | 0 | lg_srcv->szx = block.szx; |
3602 | 0 | } |
3603 | 0 | lg_srcv->block_option = block_option; |
3604 | 0 | if (observe) { |
3605 | 0 | lg_srcv->observe_length = min(coap_opt_length(observe), 3); |
3606 | 0 | memcpy(lg_srcv->observe, coap_opt_value(observe), lg_srcv->observe_length); |
3607 | 0 | lg_srcv->observe_set = 1; |
3608 | 0 | } |
3609 | 0 | if (rtag_opt) { |
3610 | 0 | lg_srcv->rtag_length = (uint8_t)rtag_length; |
3611 | 0 | memcpy(lg_srcv->rtag, coap_opt_value(rtag_opt), lg_srcv->rtag_length); |
3612 | 0 | lg_srcv->rtag_set = 1; |
3613 | 0 | } |
3614 | 0 | lg_srcv->body_data = NULL; |
3615 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3616 | 0 | lg_srcv->r_m_payload_set = -1; |
3617 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3618 | 0 | LL_PREPEND(session->lg_srcv, lg_srcv); |
3619 | 0 | } |
3620 | 0 | coap_ticks(&lg_srcv->last_used); |
3621 | 0 | coap_lg_srcv_reference_lkd(lg_srcv); |
3622 | 0 | lg_srcv_is_refed = 1; |
3623 | |
|
3624 | 0 | if (block_option == COAP_OPTION_BLOCK1 && |
3625 | 0 | session->block_mode & COAP_BLOCK_NOT_RANDOM_BLOCK1 && |
3626 | 0 | !check_if_next_block(&lg_srcv->rec_blocks, block.num)) { |
3627 | 0 | coap_add_data(response, sizeof("Missing interim block")-1, |
3628 | 0 | (const uint8_t *)"Missing interim block"); |
3629 | 0 | response->code = COAP_RESPONSE_CODE(408); |
3630 | 0 | goto skip_app_handler; |
3631 | 0 | } |
3632 | | |
3633 | 0 | if (fmt != lg_srcv->content_format) { |
3634 | 0 | coap_add_data(response, sizeof("Content-Format mismatch")-1, |
3635 | 0 | (const uint8_t *)"Content-Format mismatch"); |
3636 | 0 | response->code = COAP_RESPONSE_CODE(408); |
3637 | 0 | goto free_lg_srcv; |
3638 | 0 | } |
3639 | | |
3640 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3641 | 0 | if (block_option == COAP_OPTION_Q_BLOCK1) { |
3642 | 0 | if (total != lg_srcv->total_len) { |
3643 | 0 | coap_add_data(response, sizeof("Size1 mismatch")-1, |
3644 | 0 | (const uint8_t *)"Size1 mismatch"); |
3645 | 0 | response->code = COAP_RESPONSE_CODE(408); |
3646 | 0 | goto free_lg_srcv; |
3647 | 0 | } |
3648 | 0 | coap_delete_bin_const(lg_srcv->last_token); |
3649 | 0 | lg_srcv->last_token = coap_new_bin_const(pdu->actual_token.s, |
3650 | 0 | pdu->actual_token.length); |
3651 | 0 | } |
3652 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3653 | | |
3654 | 0 | lg_srcv->last_type = pdu->type; |
3655 | |
|
3656 | 0 | update_data = 0; |
3657 | 0 | saved_num = block.num; |
3658 | 0 | saved_offset = offset; |
3659 | |
|
3660 | 0 | while (offset < saved_offset + length) { |
3661 | 0 | uint32_t block_m = block.m; |
3662 | | |
3663 | | /* |
3664 | | * A BERT payload contains multiple logical 1024-byte blocks, but its |
3665 | | * Block1 M bit describes the complete BERT payload. Mark intermediate |
3666 | | * logical blocks as having more data so total_blocks is only set by the |
3667 | | * final logical block. |
3668 | | */ |
3669 | 0 | if (block.bert && offset + 1024 < saved_offset + length) |
3670 | 0 | block_m = 1; |
3671 | 0 | if (!check_if_received_block(&lg_srcv->rec_blocks, block.num)) { |
3672 | | /* Update list of blocks received */ |
3673 | 0 | if (blocks_add_entry(&lg_srcv->rec_blocks, block.num, block_m)) { |
3674 | 0 | update_data = 1; |
3675 | 0 | } else { |
3676 | 0 | coap_ticks(&lg_srcv->rec_blocks.last_seen); |
3677 | 0 | coap_log_debug("Block nr %u ignored (too many missing blocks)\n", block.num); |
3678 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3679 | 0 | if (block_option == COAP_OPTION_Q_BLOCK1) |
3680 | 0 | request_missing = 1; |
3681 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3682 | 0 | } |
3683 | 0 | } else { |
3684 | 0 | coap_ticks(&lg_srcv->rec_blocks.last_seen); |
3685 | 0 | coap_log_debug("Duplicate block nr %u\n", block.num); |
3686 | 0 | } |
3687 | 0 | block.num++; |
3688 | 0 | offset = block.num << (block.szx + 4); |
3689 | 0 | } |
3690 | 0 | if (length) |
3691 | 0 | block.num--; |
3692 | |
|
3693 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3694 | 0 | if (request_missing) { |
3695 | 0 | coap_request_missing_q_block1(session, lg_srcv, block.num); |
3696 | 0 | goto skip_app_handler; |
3697 | 0 | } |
3698 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3699 | | |
3700 | 0 | if (update_data) { |
3701 | | /* Update saved data */ |
3702 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3703 | 0 | lg_srcv->rec_blocks.processing_payload_set = |
3704 | 0 | block.num / COAP_MAX_PAYLOADS(session); |
3705 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3706 | 0 | if (lg_srcv->total_len < saved_offset + length) { |
3707 | 0 | lg_srcv->total_len = saved_offset + length; |
3708 | 0 | } |
3709 | |
|
3710 | 0 | #define USE_BLOCK_DATA_HANDLER (context && context->block_data_cb && \ |
3711 | 0 | !resource->is_proxy_uri && \ |
3712 | 0 | !resource->is_reverse_proxy && \ |
3713 | 0 | ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) && \ |
3714 | 0 | (resource->flags & COAP_RESOURCE_USE_BLOCK_DATA_HANDLER)) |
3715 | |
|
3716 | 0 | if (USE_BLOCK_DATA_HANDLER) { |
3717 | 0 | coap_response_t resp; |
3718 | |
|
3719 | 0 | coap_lock_callback_ret(resp, |
3720 | 0 | context->block_data_cb(session, pdu, resource, |
3721 | 0 | &lg_srcv->body_data, |
3722 | 0 | length, data, saved_offset, |
3723 | 0 | lg_srcv->total_len)); |
3724 | 0 | if (resp != COAP_RESPONSE_OK) { |
3725 | 0 | response->code = COAP_RESPONSE_CODE(500); |
3726 | 0 | goto skip_app_handler; |
3727 | 0 | } |
3728 | 0 | } else { |
3729 | 0 | lg_srcv->body_data = coap_block_build_body_lkd(lg_srcv->body_data, length, data, |
3730 | 0 | saved_offset, lg_srcv->total_len); |
3731 | 0 | if (!lg_srcv->body_data) { |
3732 | 0 | coap_add_data(response, sizeof("Memory issue")-1, |
3733 | 0 | (const uint8_t *)"Memory issue"); |
3734 | 0 | response->code = COAP_RESPONSE_CODE(500); |
3735 | 0 | goto skip_app_handler; |
3736 | 0 | } |
3737 | 0 | } |
3738 | 0 | } else { |
3739 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3740 | 0 | if (block_option == COAP_OPTION_Q_BLOCK1) { |
3741 | 0 | goto q_block_1_check; |
3742 | 0 | } |
3743 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3744 | | /* |
3745 | | * Every block carried by this request had already been received, so there |
3746 | | * is no new data and the application must not see the request twice |
3747 | | * (RFC7252 4.5). The request still has to be answered: falling straight |
3748 | | * through to skip_app_handler leaves response->code at 0, which goes out |
3749 | | * as an Empty ACK. |
3750 | | * Re-issue the 2.31 Continue this block was given the first time. |
3751 | | */ |
3752 | 0 | if (block.m) { |
3753 | 0 | uint8_t buf[4]; |
3754 | |
|
3755 | 0 | coap_insert_option(response, block_option, |
3756 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
3757 | 0 | (saved_num << 4) | |
3758 | 0 | (1 << 3) | |
3759 | 0 | lg_srcv->szx), |
3760 | 0 | buf); |
3761 | 0 | response->code = COAP_RESPONSE_CODE(231); |
3762 | 0 | } |
3763 | 0 | goto skip_app_handler; |
3764 | 0 | } |
3765 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3766 | 0 | q_block_1_check: |
3767 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3768 | |
|
3769 | 0 | if (block.m || |
3770 | 0 | !check_all_blocks_in(&lg_srcv->rec_blocks)) { |
3771 | | /* Not all the payloads of the body have arrived */ |
3772 | 0 | if (block.m) { |
3773 | 0 | uint8_t buf[4]; |
3774 | |
|
3775 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3776 | 0 | if (block_option == COAP_OPTION_Q_BLOCK1) { |
3777 | 0 | if (check_all_blocks_in(&lg_srcv->rec_blocks)) { |
3778 | 0 | goto give_app_data; |
3779 | 0 | } |
3780 | 0 | if (lg_srcv->rec_blocks.used == 1 && |
3781 | 0 | (lg_srcv->rec_blocks.range[0].end % COAP_MAX_PAYLOADS(session)) + 1 |
3782 | 0 | == COAP_MAX_PAYLOADS(session)) { |
3783 | | /* Seen all packets of this PAYLOAD_SET */ |
3784 | 0 | if (block.num != lg_srcv->rec_blocks.range[0].end) { |
3785 | | /* Blocks could arrive in wrong order but still need to send 2.31 */ |
3786 | 0 | saved_num = block.num = lg_srcv->rec_blocks.range[0].end; |
3787 | 0 | } |
3788 | 0 | } else if (lg_srcv->rec_blocks.used > 1 && |
3789 | 0 | (block.num % COAP_MAX_PAYLOADS(session)) + 1 == COAP_MAX_PAYLOADS(session) && |
3790 | 0 | lg_srcv->rec_blocks.range[lg_srcv->rec_blocks.used-1].begin < |
3791 | 0 | lg_srcv->rec_blocks.range[lg_srcv->rec_blocks.used-1].end && |
3792 | 0 | (lg_srcv->rec_blocks.range[lg_srcv->rec_blocks.used-1].end % COAP_MAX_PAYLOADS(session)) + 1 |
3793 | 0 | == COAP_MAX_PAYLOADS(session)) { |
3794 | | /* |
3795 | | * At least the last 2 of a payload set have been received, but some |
3796 | | * missing packets, but have seen the emd of a payload set. |
3797 | | */ |
3798 | 0 | coap_log_debug("Fast recovery for block %u\n", block.num); |
3799 | 0 | coap_request_missing_q_block1(session, lg_srcv, block.num); |
3800 | 0 | goto skip_app_handler; |
3801 | 0 | } else if (lg_srcv->rec_blocks.used > 1 && |
3802 | 0 | (block.num / COAP_MAX_PAYLOADS(session)) > |
3803 | 0 | (lg_srcv->rec_blocks.range[0].end / COAP_MAX_PAYLOADS(session))) { |
3804 | | /* |
3805 | | * Current MAX_PAYLOAD chunk is different to last MAX_PAYLOAD chunk |
3806 | | * with some missing packets in last MAX_PAYLOAD chunk. |
3807 | | * client has timed out and starting transmission of next PAYLOAD_SET. |
3808 | | */ |
3809 | 0 | coap_request_missing_q_block1(session, lg_srcv, block.num); |
3810 | 0 | goto skip_app_handler; |
3811 | 0 | } else { |
3812 | | /* The remote end will be sending the next one unless this |
3813 | | is a MAX_PAYLOADS and all previous have been received |
3814 | | as caught above */ |
3815 | 0 | goto skip_app_handler; |
3816 | 0 | } |
3817 | 0 | if (COAP_PROTO_RELIABLE(session->proto) || |
3818 | 0 | pdu->type != COAP_MESSAGE_NON) |
3819 | 0 | goto skip_app_handler; |
3820 | 0 | } |
3821 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3822 | | |
3823 | | /* Check to see if block size is getting forced down */ |
3824 | 0 | max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode); |
3825 | 0 | if (!block.bert && saved_num == 0 && max_block_szx != 0 && |
3826 | 0 | max_block_szx < block.aszx) { |
3827 | 0 | block.aszx = max_block_szx; |
3828 | 0 | } |
3829 | | |
3830 | | /* |
3831 | | * If the last block has been seen, packets are coming in in |
3832 | | * random order. If all blocks are now in, then need to send |
3833 | | * complete payload to application and acknowledge this current |
3834 | | * block. |
3835 | | */ |
3836 | 0 | if ((total == 0 && block.m) || !check_all_blocks_in(&lg_srcv->rec_blocks)) { |
3837 | | /* Ask for the next block */ |
3838 | 0 | coap_insert_option(response, block_option, |
3839 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
3840 | 0 | (saved_num << 4) | |
3841 | 0 | (block.m << 3) | |
3842 | 0 | block.aszx), |
3843 | 0 | buf); |
3844 | 0 | response->code = COAP_RESPONSE_CODE(231); |
3845 | 0 | } else { |
3846 | | /* Need to separately respond to this request */ |
3847 | 0 | coap_pdu_t *tmp_pdu = coap_pdu_duplicate_lkd(response, |
3848 | 0 | session, |
3849 | 0 | response->actual_token.length, |
3850 | 0 | response->actual_token.s, |
3851 | 0 | NULL, COAP_BOOL_FALSE); |
3852 | 0 | if (tmp_pdu) { |
3853 | 0 | tmp_pdu->code = COAP_RESPONSE_CODE(231); |
3854 | 0 | if (coap_send_internal(session, tmp_pdu, NULL) == COAP_INVALID_MID) { |
3855 | | /* lg_srcv may have got deleted */ |
3856 | 0 | coap_lg_srcv_t *sg; |
3857 | |
|
3858 | 0 | LL_FOREACH(session->lg_srcv, sg) { |
3859 | 0 | if (lg_srcv == sg) { |
3860 | | /* Still there */ |
3861 | 0 | break; |
3862 | 0 | } |
3863 | 0 | } |
3864 | 0 | if (!sg) |
3865 | 0 | goto skip_app_handler; |
3866 | 0 | } |
3867 | 0 | } |
3868 | 0 | if (lg_srcv->last_token) { |
3869 | 0 | coap_update_token(response, lg_srcv->last_token->length, lg_srcv->last_token->s); |
3870 | 0 | coap_update_token(pdu, lg_srcv->last_token->length, lg_srcv->last_token->s); |
3871 | 0 | } |
3872 | | /* Pass the assembled pdu and body to the application */ |
3873 | 0 | goto give_app_data; |
3874 | 0 | } |
3875 | 0 | } else { |
3876 | | /* block.m Block More option not set. Some outstanding blocks */ |
3877 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3878 | 0 | if (block_option != COAP_OPTION_Q_BLOCK1) { |
3879 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3880 | | /* Last chunk - but not all in */ |
3881 | 0 | coap_ticks(&lg_srcv->last_used); |
3882 | 0 | lg_srcv->no_more_seen = 1; |
3883 | 0 | coap_delete_bin_const(lg_srcv->last_token); |
3884 | 0 | lg_srcv->last_token = coap_new_bin_const(pdu->actual_token.s, |
3885 | 0 | pdu->actual_token.length); |
3886 | | |
3887 | | /* |
3888 | | * Need to just ACK (no response code) to handle client's NSTART. |
3889 | | * When final missing block comes in, we will pass all the data |
3890 | | * for processing so a 2.01, 2.04 etc. code can be generated |
3891 | | * and responded to as a separate response "RFC7252 5.2.2. Separate" |
3892 | | * If missing block(s) do not come in, then will generate a 4.08 |
3893 | | * when lg_srcv times out. |
3894 | | * Fall through to skip_app_handler. |
3895 | | */ |
3896 | 0 | #if COAP_Q_BLOCK_SUPPORT |
3897 | 0 | } else { |
3898 | 0 | coap_request_missing_q_block1(session, lg_srcv, block.num); |
3899 | 0 | } |
3900 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3901 | 0 | } |
3902 | 0 | goto skip_app_handler; |
3903 | 0 | } |
3904 | | |
3905 | | /* |
3906 | | * Entire payload received. |
3907 | | * Remove the Block1 option as passing all of the data to |
3908 | | * application layer. Add back in observe option if appropriate. |
3909 | | * Adjust all other information. |
3910 | | */ |
3911 | 0 | give_app_data: |
3912 | 0 | if (lg_srcv->observe_set) { |
3913 | 0 | coap_update_option(pdu, COAP_OPTION_OBSERVE, |
3914 | 0 | lg_srcv->observe_length, lg_srcv->observe); |
3915 | 0 | } |
3916 | 0 | coap_remove_option(pdu, block_option); |
3917 | 0 | if (lg_srcv->body_data) { |
3918 | 0 | pdu->body_data = lg_srcv->body_data->s; |
3919 | 0 | pdu->body_length = lg_srcv->total_len; |
3920 | 0 | } else { |
3921 | 0 | pdu->body_data = NULL; |
3922 | 0 | pdu->body_length = 0; |
3923 | 0 | } |
3924 | 0 | pdu->body_offset = 0; |
3925 | 0 | pdu->body_total = lg_srcv->total_len; |
3926 | 0 | if (USE_BLOCK_DATA_HANDLER) { |
3927 | | /* Data has already been provided - do not duplicate */ |
3928 | 0 | if (pdu->data) { |
3929 | 0 | pdu->used_size = pdu->data - pdu->token - 1; |
3930 | 0 | pdu->data = NULL; |
3931 | 0 | } |
3932 | 0 | } |
3933 | 0 | coap_log_debug("Server app version of updated PDU\n"); |
3934 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
3935 | 0 | lg_srcv->dont_timeout = 1; |
3936 | 0 | *pfree_lg_srcv = lg_srcv; |
3937 | |
|
3938 | 0 | call_app_handler: |
3939 | 0 | if (lg_srcv_is_refed) |
3940 | 0 | coap_lg_srcv_release_lkd(session, lg_srcv); |
3941 | 0 | return 0; |
3942 | | |
3943 | 0 | free_lg_srcv: |
3944 | 0 | LL_DELETE(session->lg_srcv, lg_srcv); |
3945 | 0 | coap_block_delete_lg_srcv(session, lg_srcv); |
3946 | |
|
3947 | 0 | skip_app_handler: |
3948 | 0 | if (lg_srcv_is_refed) |
3949 | 0 | coap_lg_srcv_release_lkd(session, lg_srcv); |
3950 | 0 | return 1; |
3951 | 0 | } |
3952 | | #endif /* COAP_SERVER_SUPPORT */ |
3953 | | |
3954 | | #if COAP_CLIENT_SUPPORT |
3955 | | #if COAP_Q_BLOCK_SUPPORT |
3956 | | static uint32_t |
3957 | 0 | derive_cbor_value(const uint8_t **bp, size_t rem_len) { |
3958 | 0 | uint32_t value = **bp & 0x1f; |
3959 | 0 | (*bp)++; |
3960 | 0 | if (value < 24) { |
3961 | 0 | return value; |
3962 | 0 | } else if (value == 24) { |
3963 | 0 | if (rem_len < 2) |
3964 | 0 | return (uint32_t)-1; |
3965 | 0 | value = **bp; |
3966 | 0 | (*bp)++; |
3967 | 0 | return value; |
3968 | 0 | } else if (value == 25) { |
3969 | 0 | if (rem_len < 3) |
3970 | 0 | return (uint32_t)-1; |
3971 | 0 | value = **bp << 8; |
3972 | 0 | (*bp)++; |
3973 | 0 | value |= **bp; |
3974 | 0 | (*bp)++; |
3975 | 0 | return value; |
3976 | 0 | } |
3977 | 0 | if (rem_len < 5) |
3978 | 0 | return (uint32_t)-1; |
3979 | 0 | value = (uint32_t)(**bp) << 24; |
3980 | 0 | (*bp)++; |
3981 | 0 | value |= **bp << 16; |
3982 | 0 | (*bp)++; |
3983 | 0 | value |= **bp << 8; |
3984 | 0 | (*bp)++; |
3985 | 0 | value |= **bp; |
3986 | 0 | (*bp)++; |
3987 | 0 | return value; |
3988 | 0 | } |
3989 | | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3990 | | |
3991 | | static int |
3992 | | check_freshness(coap_session_t *session, coap_pdu_t *rcvd, coap_pdu_t *sent, |
3993 | 0 | coap_lg_xmit_t *lg_xmit, coap_lg_crcv_t *lg_crcv) { |
3994 | | /* Check for Echo option for freshness */ |
3995 | 0 | coap_opt_iterator_t opt_iter; |
3996 | 0 | coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter); |
3997 | |
|
3998 | 0 | if (opt) { |
3999 | 0 | if (sent || lg_xmit || lg_crcv) { |
4000 | | /* Need to retransmit original request with Echo option added */ |
4001 | 0 | coap_pdu_t *echo_pdu; |
4002 | 0 | coap_mid_t mid; |
4003 | 0 | const uint8_t *data; |
4004 | 0 | size_t data_len; |
4005 | 0 | int have_data = 0; |
4006 | 0 | uint8_t ltoken[8]; |
4007 | 0 | size_t ltoken_len; |
4008 | 0 | uint64_t token; |
4009 | |
|
4010 | 0 | if (sent) { |
4011 | 0 | if (coap_get_data(sent, &data_len, &data)) |
4012 | 0 | have_data = 1; |
4013 | 0 | } else if (lg_xmit) { |
4014 | 0 | sent = lg_xmit->sent_pdu; |
4015 | 0 | if (lg_xmit->data_info->length) { |
4016 | 0 | size_t blk_size = (size_t)1 << (lg_xmit->blk_size + 4); |
4017 | 0 | size_t offset = (lg_xmit->last_block + 1) * blk_size; |
4018 | 0 | have_data = 1; |
4019 | 0 | data = &lg_xmit->data_info->data[offset]; |
4020 | 0 | data_len = (lg_xmit->data_info->length - offset) > blk_size ? blk_size : |
4021 | 0 | lg_xmit->data_info->length - offset; |
4022 | 0 | } |
4023 | 0 | } else { /* lg_crcv */ |
4024 | 0 | sent = lg_crcv->sent_pdu; |
4025 | 0 | if (coap_get_data(sent, &data_len, &data)) |
4026 | 0 | have_data = 1; |
4027 | 0 | } |
4028 | 0 | if (lg_xmit) { |
4029 | 0 | token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token, |
4030 | 0 | ++lg_xmit->b.b1.count); |
4031 | 0 | } else { |
4032 | 0 | token = STATE_TOKEN_FULL(lg_crcv->state_token, |
4033 | 0 | ++lg_crcv->retry_counter); |
4034 | 0 | } |
4035 | 0 | ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token); |
4036 | 0 | echo_pdu = coap_pdu_duplicate_lkd(sent, session, ltoken_len, ltoken, |
4037 | 0 | NULL, COAP_BOOL_FALSE); |
4038 | 0 | if (!echo_pdu) |
4039 | 0 | return 0; |
4040 | 0 | if (!coap_insert_option(echo_pdu, COAP_OPTION_ECHO, |
4041 | 0 | coap_opt_length(opt), coap_opt_value(opt))) |
4042 | 0 | goto not_sent; |
4043 | 0 | if (have_data) { |
4044 | 0 | coap_add_data(echo_pdu, data_len, data); |
4045 | 0 | } |
4046 | | /* Need to track Observe token change if Observe */ |
4047 | 0 | track_fetch_observe(echo_pdu, lg_crcv, 0, &echo_pdu->actual_token); |
4048 | 0 | #if COAP_OSCORE_SUPPORT |
4049 | 0 | if (session->oscore_encryption && |
4050 | 0 | (opt = coap_check_option(echo_pdu, COAP_OPTION_OBSERVE, &opt_iter)) && |
4051 | 0 | coap_decode_var_bytes(coap_opt_value(opt), coap_opt_length(opt) == 0)) { |
4052 | | /* Need to update the base PDU's Token for closing down Observe */ |
4053 | 0 | if (lg_xmit) { |
4054 | 0 | lg_xmit->b.b1.state_token = token; |
4055 | 0 | } else { |
4056 | 0 | lg_crcv->state_token = token; |
4057 | 0 | } |
4058 | 0 | } |
4059 | 0 | #endif /* COAP_OSCORE_SUPPORT */ |
4060 | 0 | mid = coap_send_internal(session, echo_pdu, NULL); |
4061 | 0 | if (mid == COAP_INVALID_MID) |
4062 | 0 | goto not_sent; |
4063 | 0 | return 1; |
4064 | 0 | } else { |
4065 | | /* Need to save Echo option value to add to next reansmission */ |
4066 | 0 | not_sent: |
4067 | 0 | coap_delete_bin_const(session->echo); |
4068 | 0 | session->echo = coap_new_bin_const(coap_opt_value(opt), |
4069 | 0 | coap_opt_length(opt)); |
4070 | 0 | } |
4071 | 0 | } |
4072 | 0 | return 0; |
4073 | 0 | } |
4074 | | |
4075 | | static void |
4076 | 0 | track_echo(coap_session_t *session, coap_pdu_t *rcvd) { |
4077 | 0 | coap_opt_iterator_t opt_iter; |
4078 | 0 | coap_opt_t *opt = coap_check_option(rcvd, COAP_OPTION_ECHO, &opt_iter); |
4079 | |
|
4080 | 0 | if (opt) { |
4081 | 0 | coap_delete_bin_const(session->echo); |
4082 | 0 | session->echo = coap_new_bin_const(coap_opt_value(opt), |
4083 | 0 | coap_opt_length(opt)); |
4084 | 0 | } |
4085 | 0 | } |
4086 | | |
4087 | | /* |
4088 | | * Need to see if this is a response to a large body request transfer. If so, |
4089 | | * need to initiate the request containing the next block and not trouble the |
4090 | | * application. Note that Token must unique per request/response. |
4091 | | * |
4092 | | * Client receives large data acknowledgement from server (Block1) |
4093 | | * |
4094 | | * This is set up using coap_add_data_large_request_lkd() |
4095 | | * |
4096 | | * Client is using GET etc. |
4097 | | * |
4098 | | * Return: 0 Call application handler |
4099 | | * 1 Do not call application handler - just send the built response |
4100 | | */ |
4101 | | int |
4102 | | coap_handle_response_send_block(coap_session_t *session, coap_pdu_t *sent, |
4103 | 0 | coap_pdu_t *rcvd) { |
4104 | 0 | coap_lg_xmit_t *lg_xmit; |
4105 | 0 | coap_lg_crcv_t *lg_crcv = NULL; |
4106 | 0 | int lg_crcv_is_refed = 0; |
4107 | |
|
4108 | 0 | lg_xmit = coap_find_lg_xmit(session, rcvd); |
4109 | 0 | if (lg_xmit) { |
4110 | | /* lg_xmit found */ |
4111 | 0 | size_t chunk = (size_t)1 << (lg_xmit->blk_size + 4); |
4112 | 0 | coap_block_b_t block; |
4113 | |
|
4114 | 0 | lg_crcv = coap_find_lg_crcv(session, rcvd); |
4115 | 0 | if (lg_crcv) { |
4116 | 0 | coap_ticks(&lg_crcv->last_used); |
4117 | 0 | coap_lg_crcv_reference_lkd(lg_crcv); |
4118 | 0 | lg_crcv_is_refed = 1; |
4119 | 0 | } |
4120 | |
|
4121 | 0 | if (COAP_RESPONSE_CLASS(rcvd->code) == 2 && |
4122 | 0 | coap_get_block_b(session, rcvd, lg_xmit->option, &block)) { |
4123 | |
|
4124 | 0 | if (block.bert) { |
4125 | 0 | coap_log_debug("found Block option, block is BERT, block nr. %u (%" PRIuS ")\n", |
4126 | 0 | block.num, lg_xmit->b.b1.bert_size); |
4127 | 0 | } else { |
4128 | 0 | coap_log_debug("found Block option, block size is %u, block nr. %u\n", |
4129 | 0 | 1 << (block.szx + 4), block.num); |
4130 | 0 | } |
4131 | 0 | if (block.szx != lg_xmit->blk_size) { |
4132 | 0 | if (block.szx > lg_xmit->blk_size) { |
4133 | 0 | coap_log_info("ignoring request to increase Block size, " |
4134 | 0 | "(%u > %u)\n", |
4135 | 0 | 1 << (block.szx + 4), 1 << (lg_xmit->blk_size + 4)); |
4136 | 0 | } else { |
4137 | | /* |
4138 | | * Recompute the block number of the previous packet given the |
4139 | | * new block size |
4140 | | */ |
4141 | 0 | block.num = (uint32_t)((chunk >> (block.szx + 4)) - 1); |
4142 | 0 | chunk = (size_t)1 << (lg_xmit->blk_size + 4); |
4143 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4144 | 0 | lg_xmit->send_blocks.range[0].begin = block.num; |
4145 | 0 | lg_xmit->send_blocks.range[0].end = (uint32_t)((lg_xmit->data_info->length -1) / chunk); |
4146 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4147 | 0 | lg_xmit->blk_size = block.szx; |
4148 | 0 | coap_log_debug("new Block size is %u, block number %u completed\n", |
4149 | 0 | 1 << (block.szx + 4), block.num); |
4150 | 0 | block.bert = 0; |
4151 | 0 | block.aszx = block.szx; |
4152 | 0 | } |
4153 | 0 | } |
4154 | 0 | track_echo(session, rcvd); |
4155 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4156 | 0 | if (rcvd->code == COAP_RESPONSE_CODE(231) && |
4157 | 0 | lg_xmit->option == COAP_OPTION_Q_BLOCK1) { |
4158 | 0 | coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU); |
4159 | 0 | goto skip_app_handler; |
4160 | 0 | } |
4161 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4162 | 0 | if (lg_xmit->last_block == (int)block.num && |
4163 | 0 | lg_xmit->option != COAP_OPTION_Q_BLOCK1) { |
4164 | | /* |
4165 | | * Duplicate Block1 ACK |
4166 | | * |
4167 | | * RFCs not clear here, but on a lossy connection, there could |
4168 | | * be multiple Block1 ACKs, causing the client to retransmit the |
4169 | | * same block multiple times, or the server retransmitting the |
4170 | | * same ACK. |
4171 | | * |
4172 | | * Once a block has been ACKd, there is no need to retransmit it. |
4173 | | */ |
4174 | 0 | goto skip_app_handler; |
4175 | 0 | } |
4176 | 0 | if (block.bert) |
4177 | 0 | block.num += (unsigned int)(lg_xmit->b.b1.bert_size / 1024 - 1); |
4178 | 0 | lg_xmit->last_block = block.num; |
4179 | 0 | if ((block.num + 1) * chunk < lg_xmit->data_info->length) { |
4180 | | /* Build the next PDU request based off the skeletal PDU */ |
4181 | 0 | uint8_t buf[8]; |
4182 | 0 | coap_pdu_t *pdu; |
4183 | 0 | uint64_t token = STATE_TOKEN_FULL(lg_xmit->b.b1.state_token, ++lg_xmit->b.b1.count); |
4184 | 0 | size_t len = coap_encode_var_safe8(buf, sizeof(token), token); |
4185 | 0 | size_t offset; |
4186 | |
|
4187 | 0 | if (lg_xmit->sent_pdu->code == COAP_REQUEST_CODE_FETCH) { |
4188 | | /* Need to handle Observe for large FETCH */ |
4189 | 0 | if (lg_crcv) { |
4190 | 0 | if (coap_binary_equal(lg_xmit->b.b1.app_token, lg_crcv->app_token)) { |
4191 | 0 | coap_bin_const_t *new_token; |
4192 | 0 | coap_bin_const_t ctoken = { len, buf }; |
4193 | | |
4194 | | /* Need to save/restore Observe Token for large FETCH */ |
4195 | 0 | new_token = track_fetch_observe(lg_xmit->sent_pdu, lg_crcv, block.num + 1, |
4196 | 0 | &ctoken); |
4197 | 0 | if (new_token) { |
4198 | 0 | assert(len <= sizeof(buf)); |
4199 | 0 | len = new_token->length; |
4200 | 0 | memcpy(buf, new_token->s, len); |
4201 | 0 | } |
4202 | 0 | } |
4203 | 0 | } |
4204 | 0 | } |
4205 | 0 | pdu = coap_pdu_duplicate_lkd(lg_xmit->sent_pdu, session, |
4206 | 0 | len, buf, NULL, COAP_BOOL_FALSE); |
4207 | 0 | if (!pdu) |
4208 | 0 | goto fail_body; |
4209 | | |
4210 | | /* |
4211 | | * If initial transmit was multicast, that would have been NON. |
4212 | | * Make subsequent traffic CON for reliability. |
4213 | | */ |
4214 | 0 | if (session->sock.flags & COAP_SOCKET_MULTICAST) { |
4215 | 0 | pdu->type = COAP_MESSAGE_CON; |
4216 | 0 | } |
4217 | |
|
4218 | 0 | block.num++; |
4219 | 0 | offset = block.num * chunk; |
4220 | 0 | if (block.bert) { |
4221 | 0 | size_t token_options = pdu->data ? (size_t)(pdu->data - pdu->token) : |
4222 | 0 | pdu->used_size; |
4223 | 0 | block.m = (lg_xmit->data_info->length - offset) > |
4224 | 0 | ((pdu->max_size - token_options) /1024) * 1024; |
4225 | 0 | } else { |
4226 | 0 | block.m = (offset + chunk) < lg_xmit->data_info->length; |
4227 | 0 | } |
4228 | 0 | coap_update_option(pdu, lg_xmit->option, |
4229 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
4230 | 0 | (block.num << 4) | |
4231 | 0 | (block.m << 3) | |
4232 | 0 | block.aszx), |
4233 | 0 | buf); |
4234 | |
|
4235 | 0 | if (lg_xmit->data_info->get_func) { |
4236 | | #if COAP_CONSTRAINED_STACK |
4237 | | /* Protected by global_lock if needed */ |
4238 | | static uint8_t l_data[1024]; |
4239 | | #else /* ! COAP_CONSTRAINED_STACK */ |
4240 | 0 | uint8_t l_data[1024]; |
4241 | 0 | #endif /* ! COAP_CONSTRAINED_STACK */ |
4242 | 0 | size_t l_length; |
4243 | |
|
4244 | 0 | assert(chunk <= 1024); |
4245 | 0 | if (lg_xmit->data_info->get_func(session, chunk, |
4246 | 0 | block.num * chunk, l_data, &l_length, |
4247 | 0 | lg_xmit->data_info->app_ptr)) { |
4248 | 0 | if (!coap_add_data(pdu, l_length, l_data)) { |
4249 | 0 | goto fail_body; |
4250 | 0 | } |
4251 | 0 | } |
4252 | 0 | } else { |
4253 | 0 | if (!coap_add_block_b_data(pdu, |
4254 | 0 | lg_xmit->data_info->length, |
4255 | 0 | lg_xmit->data_info->data, |
4256 | 0 | &block)) |
4257 | 0 | goto fail_body; |
4258 | 0 | } |
4259 | 0 | lg_xmit->b.b1.bert_size = block.chunk_size; |
4260 | 0 | coap_ticks(&lg_xmit->last_sent); |
4261 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4262 | 0 | if (lg_xmit->option == COAP_OPTION_Q_BLOCK1 && |
4263 | 0 | pdu->type == COAP_MESSAGE_NON) { |
4264 | 0 | if (coap_send_q_block1(session, block, pdu, |
4265 | 0 | COAP_SEND_INC_PDU) == COAP_INVALID_MID) |
4266 | 0 | goto fail_body; |
4267 | 0 | goto skip_app_handler; |
4268 | 0 | } else if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID) |
4269 | 0 | goto fail_body; |
4270 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
4271 | | if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID) |
4272 | | goto fail_body; |
4273 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
4274 | 0 | goto skip_app_handler; |
4275 | 0 | } |
4276 | 0 | } else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) { |
4277 | | /* |
4278 | | * Not a block response asking for the next block. |
4279 | | * Could be an Observe response overlapping with block FETCH doing |
4280 | | * Observe cancellation. |
4281 | | */ |
4282 | 0 | coap_opt_iterator_t opt_iter; |
4283 | 0 | coap_opt_t *obs_opt; |
4284 | 0 | int observe_action = -1; |
4285 | |
|
4286 | 0 | if (lg_xmit->sent_pdu->code != COAP_REQUEST_CODE_FETCH) { |
4287 | 0 | goto lg_xmit_finished; |
4288 | 0 | } |
4289 | 0 | obs_opt = coap_check_option(lg_xmit->sent_pdu, |
4290 | 0 | COAP_OPTION_OBSERVE, |
4291 | 0 | &opt_iter); |
4292 | 0 | if (obs_opt) { |
4293 | 0 | observe_action = coap_decode_var_bytes(coap_opt_value(obs_opt), |
4294 | 0 | coap_opt_length(obs_opt)); |
4295 | 0 | } |
4296 | 0 | if (observe_action != COAP_OBSERVE_CANCEL) { |
4297 | 0 | goto lg_xmit_finished; |
4298 | 0 | } |
4299 | 0 | obs_opt = coap_check_option(rcvd, |
4300 | 0 | COAP_OPTION_OBSERVE, |
4301 | 0 | &opt_iter); |
4302 | 0 | if (obs_opt) { |
4303 | 0 | goto call_app_handler; |
4304 | 0 | } |
4305 | 0 | goto lg_xmit_finished; |
4306 | 0 | } else if (rcvd->code == COAP_RESPONSE_CODE(401)) { |
4307 | 0 | if (check_freshness(session, rcvd, sent, lg_xmit, NULL)) |
4308 | 0 | goto skip_app_handler; |
4309 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4310 | 0 | } else if (rcvd->code == COAP_RESPONSE_CODE(402)) { |
4311 | | /* Q-Block1 or Q-Block2 not present in p - duplicate error ? */ |
4312 | 0 | if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block) || |
4313 | 0 | coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK1, &block)) |
4314 | 0 | goto skip_app_handler; |
4315 | 0 | } else if (rcvd->code == COAP_RESPONSE_CODE(408) && |
4316 | 0 | lg_xmit->option == COAP_OPTION_Q_BLOCK1) { |
4317 | 0 | size_t length; |
4318 | 0 | const uint8_t *data; |
4319 | 0 | coap_opt_iterator_t opt_iter; |
4320 | 0 | coap_opt_t *fmt_opt = coap_check_option(rcvd, |
4321 | 0 | COAP_OPTION_CONTENT_FORMAT, |
4322 | 0 | &opt_iter); |
4323 | 0 | uint16_t fmt = fmt_opt ? |
4324 | 0 | coap_decode_var_bytes(coap_opt_value(fmt_opt), |
4325 | 0 | coap_opt_length(fmt_opt)) : |
4326 | 0 | COAP_MEDIATYPE_TEXT_PLAIN; |
4327 | |
|
4328 | 0 | if (fmt != COAP_MEDIATYPE_APPLICATION_MB_CBOR_SEQ) |
4329 | 0 | goto fail_body; |
4330 | | |
4331 | 0 | if (COAP_PROTO_RELIABLE(session->proto) || |
4332 | 0 | rcvd->type != COAP_MESSAGE_NON) { |
4333 | 0 | coap_log_debug("Unexpected 4.08 - protocol violation - ignore\n"); |
4334 | 0 | goto skip_app_handler; |
4335 | 0 | } |
4336 | | |
4337 | 0 | if (coap_get_data(rcvd, &length, &data)) { |
4338 | | /* Need to decode CBOR to work out what blocks to re-send */ |
4339 | 0 | const uint8_t *bp = data; |
4340 | 0 | uint32_t i; |
4341 | |
|
4342 | 0 | for (i = 0; (bp < data + length) && |
4343 | 0 | i < COAP_MAX_PAYLOADS(session); i++) { |
4344 | 0 | if ((*bp & 0xc0) != 0x00) /* uint(value) */ |
4345 | 0 | goto fail_cbor; |
4346 | 0 | block.num = derive_cbor_value(&bp, data + length - bp); |
4347 | 0 | coap_log_debug("Q-Block1: Missing block %d\n", block.num); |
4348 | 0 | if (block.num > (1 << 20) -1) |
4349 | 0 | goto fail_cbor; |
4350 | 0 | block.m = (block.num + 1) * chunk < lg_xmit->data_info->length; |
4351 | 0 | block.szx = lg_xmit->blk_size; |
4352 | |
|
4353 | 0 | blocks_add_entry(&lg_xmit->send_blocks, block.num, block.m); |
4354 | 0 | } |
4355 | 0 | if (lg_xmit->send_blocks.used) { |
4356 | | /* Flush out the first one */ |
4357 | 0 | block.num = lg_xmit->send_blocks.range[0].begin; |
4358 | 0 | block.m = (block.num + 1) * chunk < lg_xmit->data_info->length; |
4359 | 0 | coap_send_q_blocks(session, lg_xmit, block, lg_xmit->sent_pdu, COAP_SEND_MISSING); |
4360 | 0 | } |
4361 | 0 | goto skip_app_handler; |
4362 | 0 | } |
4363 | 0 | fail_cbor: |
4364 | 0 | coap_log_info("Invalid application/missing-blocks+cbor-seq\n"); |
4365 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4366 | 0 | } |
4367 | 0 | goto lg_xmit_finished; |
4368 | 0 | } |
4369 | 0 | goto call_app_handler; |
4370 | | |
4371 | 0 | fail_body: |
4372 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_XMIT_BLOCK_FAIL, session); |
4373 | | /* There has been an internal error of some sort */ |
4374 | 0 | rcvd->code = COAP_RESPONSE_CODE(500); |
4375 | 0 | lg_xmit_finished: |
4376 | 0 | if (lg_crcv) { |
4377 | 0 | if (STATE_TOKEN_BASE(lg_xmit->b.b1.state_token) == |
4378 | 0 | STATE_TOKEN_BASE(lg_crcv->state_token)) { |
4379 | | /* In case of observe */ |
4380 | 0 | lg_crcv->state_token = lg_xmit->b.b1.state_token; |
4381 | 0 | lg_crcv->retry_counter = lg_xmit->b.b1.count; |
4382 | 0 | } |
4383 | 0 | } |
4384 | 0 | if (!lg_crcv) { |
4385 | | /* need to put back original token into rcvd */ |
4386 | 0 | if (lg_xmit->b.b1.app_token) |
4387 | 0 | coap_update_token(rcvd, lg_xmit->b.b1.app_token->length, |
4388 | 0 | lg_xmit->b.b1.app_token->s); |
4389 | 0 | coap_log_debug("Client app version of updated PDU (1)\n"); |
4390 | 0 | coap_show_pdu(COAP_LOG_DEBUG, rcvd); |
4391 | 0 | } else { |
4392 | 0 | lg_crcv->sent_pdu->lg_xmit = 0; |
4393 | 0 | } |
4394 | |
|
4395 | 0 | if (sent) { |
4396 | | /* need to put back original token into sent */ |
4397 | 0 | if (lg_xmit->b.b1.app_token) |
4398 | 0 | coap_update_token(sent, lg_xmit->b.b1.app_token->length, |
4399 | 0 | lg_xmit->b.b1.app_token->s); |
4400 | 0 | if (sent->lg_xmit) |
4401 | 0 | coap_remove_option(sent, sent->lg_xmit->option); |
4402 | 0 | sent->lg_xmit = NULL; |
4403 | 0 | } |
4404 | 0 | LL_DELETE(session->lg_xmit, lg_xmit); |
4405 | 0 | coap_block_delete_lg_xmit(session, lg_xmit); |
4406 | |
|
4407 | 0 | call_app_handler: |
4408 | 0 | if (lg_crcv_is_refed) |
4409 | 0 | coap_lg_crcv_release_lkd(session, lg_crcv); |
4410 | 0 | return 0; |
4411 | | |
4412 | 0 | skip_app_handler: |
4413 | 0 | if (lg_crcv_is_refed) |
4414 | 0 | coap_lg_crcv_release_lkd(session, lg_crcv); |
4415 | 0 | return 1; |
4416 | 0 | } |
4417 | | #endif /* COAP_CLIENT_SUPPORT */ |
4418 | | |
4419 | | void |
4420 | | coap_register_block_data_handler(coap_context_t *context, |
4421 | 0 | coap_block_data_handler_t block_data_handler) { |
4422 | 0 | context->block_data_cb = block_data_handler; |
4423 | 0 | } |
4424 | | |
4425 | | COAP_API coap_binary_t * |
4426 | | coap_block_build_body(coap_binary_t *body_data, size_t length, |
4427 | 0 | const uint8_t *data, size_t offset, size_t total) { |
4428 | 0 | coap_binary_t *ret; |
4429 | |
|
4430 | 0 | coap_lock_lock(return NULL); |
4431 | 0 | ret = coap_block_build_body_lkd(body_data, length, data, offset, total); |
4432 | 0 | coap_lock_unlock(); |
4433 | 0 | return ret; |
4434 | 0 | } |
4435 | | /* |
4436 | | * Re-assemble payloads into a body |
4437 | | */ |
4438 | | coap_binary_t * |
4439 | | coap_block_build_body_lkd(coap_binary_t *body_data, size_t length, |
4440 | 0 | const uint8_t *data, size_t offset, size_t total) { |
4441 | 0 | if (data == NULL) |
4442 | 0 | return NULL; |
4443 | 0 | if (body_data == NULL && total) { |
4444 | 0 | body_data = coap_new_binary(total); |
4445 | 0 | } |
4446 | 0 | if (body_data == NULL) |
4447 | 0 | return NULL; |
4448 | | |
4449 | | /* Check no overflow (including a 8 byte small headroom) */ |
4450 | 0 | if (SIZE_MAX - length < 8 || offset > SIZE_MAX - length - 8) { |
4451 | 0 | coap_delete_binary(body_data); |
4452 | 0 | return NULL; |
4453 | 0 | } |
4454 | | |
4455 | | /* Update saved data */ |
4456 | 0 | if (offset + length <= total && body_data->length >= total) { |
4457 | 0 | memcpy(&body_data->s[offset], data, length); |
4458 | 0 | } else { |
4459 | | /* |
4460 | | * total may be inaccurate as per |
4461 | | * https://rfc-editor.org/rfc/rfc7959#section-4 |
4462 | | * o In a request carrying a Block1 Option, to indicate the current |
4463 | | * estimate the client has of the total size of the resource |
4464 | | * representation, measured in bytes ("size indication"). |
4465 | | * o In a response carrying a Block2 Option, to indicate the current |
4466 | | * estimate the server has of the total size of the resource |
4467 | | * representation, measured in bytes ("size indication"). |
4468 | | */ |
4469 | 0 | coap_binary_t *new = coap_resize_binary(body_data, offset + length); |
4470 | |
|
4471 | 0 | if (new) { |
4472 | 0 | body_data = new; |
4473 | 0 | memcpy(&body_data->s[offset], data, length); |
4474 | 0 | } else { |
4475 | 0 | coap_delete_binary(body_data); |
4476 | 0 | return NULL; |
4477 | 0 | } |
4478 | 0 | } |
4479 | 0 | return body_data; |
4480 | 0 | } |
4481 | | |
4482 | | #if COAP_CLIENT_SUPPORT |
4483 | | /* |
4484 | | * Need to see if this is a large body response to a request. If so, |
4485 | | * need to initiate the request for the next block and not trouble the |
4486 | | * application. Note that Token must be unique per request/response. |
4487 | | * |
4488 | | * This is set up using coap_send() |
4489 | | * Client receives large data from server ((Q-)Block2) |
4490 | | * |
4491 | | * Return: 0 Call application handler |
4492 | | * 1 Do not call application handler - just sent the next request |
4493 | | */ |
4494 | | int |
4495 | | coap_handle_response_get_block(coap_context_t *context, |
4496 | | coap_session_t *session, |
4497 | | coap_pdu_t *sent, |
4498 | | coap_pdu_t *rcvd, |
4499 | 0 | coap_recurse_t recursive) { |
4500 | 0 | coap_lg_crcv_t *lg_crcv; |
4501 | 0 | coap_block_b_t block; |
4502 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4503 | 0 | coap_block_b_t qblock; |
4504 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4505 | 0 | int have_block = 0; |
4506 | 0 | uint16_t block_opt = 0; |
4507 | 0 | size_t offset; |
4508 | 0 | int ack_rst_sent = 0; |
4509 | 0 | coap_opt_iterator_t opt_iter; |
4510 | |
|
4511 | 0 | coap_lock_check_locked(); |
4512 | 0 | memset(&block, 0, sizeof(block)); |
4513 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4514 | 0 | memset(&qblock, 0, sizeof(qblock)); |
4515 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4516 | 0 | lg_crcv = coap_find_lg_crcv(session, rcvd); |
4517 | 0 | if (lg_crcv) { |
4518 | 0 | size_t chunk = 0; |
4519 | 0 | uint8_t buf[8]; |
4520 | 0 | coap_tick_t adjust; |
4521 | |
|
4522 | 0 | if (COAP_RESPONSE_CLASS(rcvd->code) == 2) { |
4523 | 0 | size_t length; |
4524 | 0 | const uint8_t *data; |
4525 | 0 | coap_opt_t *size_opt = coap_check_option(rcvd, COAP_OPTION_SIZE2, |
4526 | 0 | &opt_iter); |
4527 | 0 | size_t size2 = size_opt ? |
4528 | 0 | coap_decode_var_bytes(coap_opt_value(size_opt), |
4529 | 0 | coap_opt_length(size_opt)) : 0; |
4530 | | |
4531 | | /* length and data are cleared on error */ |
4532 | 0 | (void)coap_get_data(rcvd, &length, &data); |
4533 | 0 | rcvd->body_offset = 0; |
4534 | 0 | rcvd->body_total = length; |
4535 | 0 | if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) { |
4536 | 0 | have_block = 1; |
4537 | 0 | block_opt = COAP_OPTION_BLOCK2; |
4538 | 0 | } |
4539 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4540 | 0 | if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &qblock)) { |
4541 | 0 | if (have_block) { |
4542 | 0 | coap_log_warn("Both Block1 and Q-Block1 not supported in a response\n"); |
4543 | 0 | } |
4544 | 0 | have_block = 1; |
4545 | 0 | block_opt = COAP_OPTION_Q_BLOCK2; |
4546 | 0 | block = qblock; |
4547 | | /* server indicating that it supports Q_BLOCK */ |
4548 | 0 | if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) { |
4549 | 0 | set_block_mode_has_q(session->block_mode); |
4550 | 0 | } |
4551 | 0 | } |
4552 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4553 | 0 | track_echo(session, rcvd); |
4554 | 0 | if (have_block && (block.m || length)) { |
4555 | 0 | coap_opt_t *fmt_opt = coap_check_option(rcvd, |
4556 | 0 | COAP_OPTION_CONTENT_FORMAT, |
4557 | 0 | &opt_iter); |
4558 | 0 | uint16_t fmt = fmt_opt ? |
4559 | 0 | coap_decode_var_bytes(coap_opt_value(fmt_opt), |
4560 | 0 | coap_opt_length(fmt_opt)) : |
4561 | 0 | COAP_MEDIATYPE_TEXT_PLAIN; |
4562 | 0 | coap_opt_t *etag_opt = coap_check_option(rcvd, |
4563 | 0 | COAP_OPTION_ETAG, |
4564 | 0 | &opt_iter); |
4565 | 0 | size_t saved_offset; |
4566 | 0 | int updated_block; |
4567 | |
|
4568 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4569 | 0 | if (block_opt == COAP_OPTION_Q_BLOCK2 && (!size_opt || !etag_opt)) { |
4570 | | /* RFC9177 section-4.4 */ |
4571 | 0 | coap_log_info("Q-Block2: Size2 and ETag options required\n"); |
4572 | | /* Try to hint to the server there is an issue */ |
4573 | 0 | coap_send_rst_lkd(session, rcvd); |
4574 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_BLOCK_ISSUE, session); |
4575 | 0 | return 1; |
4576 | 0 | } |
4577 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4578 | 0 | if (length > block.chunk_size) { |
4579 | 0 | coap_log_debug("block: Oversized packet - reduced to %"PRIu32" from %" PRIuS "\n", |
4580 | 0 | block.chunk_size, length); |
4581 | 0 | length = block.chunk_size; |
4582 | 0 | } |
4583 | 0 | if (block.m && length != block.chunk_size) { |
4584 | 0 | coap_log_warn("block: Undersized packet - expected %"PRIu32", got %" PRIuS "\n", |
4585 | 0 | block.chunk_size, length); |
4586 | | /* Unclear how to properly handle this */ |
4587 | 0 | rcvd->code = COAP_RESPONSE_CODE(402); |
4588 | 0 | goto expire_lg_crcv; |
4589 | 0 | } |
4590 | | /* Possibility that Size2 not sent, or is too small */ |
4591 | 0 | chunk = (size_t)1 << (block.szx + 4); |
4592 | 0 | offset = block.num * chunk; |
4593 | 0 | if (size2 < (offset + length)) { |
4594 | 0 | if (block.m) |
4595 | 0 | size2 = offset + length + 1; |
4596 | 0 | else |
4597 | 0 | size2 = offset + length; |
4598 | 0 | } |
4599 | 0 | saved_offset = offset; |
4600 | |
|
4601 | 0 | if (lg_crcv->initial) { |
4602 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4603 | 0 | reinit: |
4604 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4605 | 0 | lg_crcv->initial = 0; |
4606 | 0 | if (lg_crcv->body_data) { |
4607 | 0 | coap_free_type(COAP_STRING, lg_crcv->body_data); |
4608 | 0 | lg_crcv->body_data = NULL; |
4609 | 0 | } |
4610 | 0 | if (etag_opt) { |
4611 | 0 | lg_crcv->etag_length = (uint8_t)min(coap_opt_length(etag_opt), sizeof(lg_crcv->etag)); |
4612 | 0 | memcpy(lg_crcv->etag, coap_opt_value(etag_opt), lg_crcv->etag_length); |
4613 | 0 | lg_crcv->etag_set = 1; |
4614 | 0 | } else { |
4615 | 0 | lg_crcv->etag_set = 0; |
4616 | 0 | } |
4617 | 0 | lg_crcv->total_len = size2; |
4618 | 0 | lg_crcv->content_format = fmt; |
4619 | 0 | lg_crcv->szx = block.szx; |
4620 | 0 | lg_crcv->block_option = block_opt; |
4621 | 0 | lg_crcv->last_type = rcvd->type; |
4622 | 0 | lg_crcv->rec_blocks.used = 0; |
4623 | 0 | lg_crcv->rec_blocks.total_blocks = 0; |
4624 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4625 | 0 | lg_crcv->rec_blocks.processing_payload_set = 0; |
4626 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4627 | 0 | } |
4628 | 0 | if (lg_crcv->total_len < size2) |
4629 | 0 | lg_crcv->total_len = size2; |
4630 | | |
4631 | | /* Check whether we can handle this size */ |
4632 | 0 | uint32_t max_body; |
4633 | 0 | uint8_t max_block_szx; |
4634 | |
|
4635 | 0 | max_block_szx = COAP_BLOCK_MAX_SIZE_GET(session->block_mode); |
4636 | 0 | if (max_block_szx == 0 || max_block_szx > block.szx) { |
4637 | 0 | max_block_szx = block.szx; |
4638 | 0 | } |
4639 | 0 | max_body = ((1UL << 20) * (1 << (max_block_szx + 4))); |
4640 | 0 | if (max_body > MAX_BLK_LEN) |
4641 | 0 | max_body = MAX_BLK_LEN; |
4642 | 0 | if ((context->max_body_size && size2 > context->max_body_size) || |
4643 | 0 | (size2 > max_body)) { |
4644 | 0 | uint32_t max_body_size = context->max_body_size; |
4645 | |
|
4646 | 0 | if (max_body_size == 0 || max_body < max_body_size) { |
4647 | 0 | max_body_size = max_body; |
4648 | 0 | } |
4649 | 0 | coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", size2, max_body_size); |
4650 | | /* Try to hint to the server there is an issue */ |
4651 | 0 | coap_send_rst_lkd(session, rcvd); |
4652 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_BLOCK_ISSUE, session); |
4653 | 0 | return 1; |
4654 | 0 | } |
4655 | | |
4656 | 0 | if (etag_opt) { |
4657 | 0 | if (!full_match(coap_opt_value(etag_opt), |
4658 | 0 | coap_opt_length(etag_opt), |
4659 | 0 | lg_crcv->etag, lg_crcv->etag_length)) { |
4660 | | /* body of data has changed - need to restart request */ |
4661 | 0 | coap_pdu_t *pdu; |
4662 | 0 | uint64_t token = STATE_TOKEN_FULL(lg_crcv->state_token, |
4663 | 0 | ++lg_crcv->retry_counter); |
4664 | 0 | size_t len = coap_encode_var_safe8(buf, sizeof(token), token); |
4665 | 0 | coap_opt_filter_t drop_options; |
4666 | |
|
4667 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4668 | 0 | if (block_opt == COAP_OPTION_Q_BLOCK2) |
4669 | 0 | goto reinit; |
4670 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4671 | | |
4672 | 0 | coap_log_warn("Data body updated during receipt - new request started\n"); |
4673 | 0 | if (!(session->block_mode & COAP_BLOCK_SINGLE_BODY)) |
4674 | 0 | coap_handle_event_lkd(context, COAP_EVENT_PARTIAL_BLOCK, session); |
4675 | |
|
4676 | 0 | lg_crcv->initial = 1; |
4677 | 0 | coap_free_type(COAP_STRING, lg_crcv->body_data); |
4678 | 0 | lg_crcv->body_data = NULL; |
4679 | |
|
4680 | 0 | coap_session_new_token(session, &len, buf); |
4681 | 0 | memset(&drop_options, 0, sizeof(coap_opt_filter_t)); |
4682 | 0 | coap_option_filter_set(&drop_options, COAP_OPTION_OBSERVE); |
4683 | 0 | coap_option_filter_set(&drop_options, COAP_OPTION_BLOCK1); |
4684 | 0 | pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf, |
4685 | 0 | &drop_options, COAP_BOOL_FALSE); |
4686 | 0 | if (!pdu) |
4687 | 0 | goto fail_resp; |
4688 | | |
4689 | 0 | coap_update_option(pdu, block_opt, |
4690 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
4691 | 0 | (0 << 4) | (0 << 3) | block.aszx), |
4692 | 0 | buf); |
4693 | |
|
4694 | 0 | if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID) |
4695 | 0 | goto fail_resp; |
4696 | | |
4697 | 0 | goto skip_app_handler; |
4698 | 0 | } |
4699 | 0 | } else if (lg_crcv->etag_set) { |
4700 | | /* Cannot handle this change in ETag to not being there */ |
4701 | 0 | coap_log_warn("Not all blocks have ETag option\n"); |
4702 | 0 | goto fail_resp; |
4703 | 0 | } |
4704 | | |
4705 | 0 | if (fmt != lg_crcv->content_format) { |
4706 | 0 | coap_log_warn("Content-Format option mismatch\n"); |
4707 | 0 | goto fail_resp; |
4708 | 0 | } |
4709 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4710 | 0 | if (block_opt == COAP_OPTION_Q_BLOCK2 && size2 != lg_crcv->total_len) { |
4711 | 0 | coap_log_warn("Size2 option mismatch (%"PRIuS " != %" PRIuS ")\n", |
4712 | 0 | size2, lg_crcv->total_len); |
4713 | 0 | goto fail_resp; |
4714 | 0 | } |
4715 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4716 | 0 | if (block.num == 0) { |
4717 | 0 | coap_opt_t *obs_opt = coap_check_option(rcvd, |
4718 | 0 | COAP_OPTION_OBSERVE, |
4719 | 0 | &opt_iter); |
4720 | 0 | if (obs_opt) { |
4721 | 0 | lg_crcv->observe_length = min(coap_opt_length(obs_opt), 3); |
4722 | 0 | memcpy(lg_crcv->observe, coap_opt_value(obs_opt), lg_crcv->observe_length); |
4723 | 0 | lg_crcv->observe_set = 1; |
4724 | 0 | } else { |
4725 | 0 | lg_crcv->observe_set = 0; |
4726 | 0 | } |
4727 | 0 | } |
4728 | 0 | updated_block = 0; |
4729 | 0 | while (offset < saved_offset + length) { |
4730 | 0 | if (!check_if_received_block(&lg_crcv->rec_blocks, block.num)) { |
4731 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4732 | 0 | uint32_t this_payload_set = block.num / COAP_MAX_PAYLOADS(session); |
4733 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4734 | |
|
4735 | 0 | coap_log_debug("found Block option, block size is %u, block nr. %u\n", |
4736 | 0 | 1 << (block.szx + 4), block.num); |
4737 | | /* Update list of blocks received */ |
4738 | 0 | if (blocks_add_entry(&lg_crcv->rec_blocks, block.num, block.m)) { |
4739 | 0 | updated_block = 1; |
4740 | 0 | } else { |
4741 | 0 | coap_log_debug("Block nr %u ignored (too many missing blocks)\n", block.num); |
4742 | 0 | } |
4743 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4744 | 0 | if (block_opt == COAP_OPTION_Q_BLOCK2) { |
4745 | 0 | if (lg_crcv->rec_blocks.used > 1 && |
4746 | 0 | (block.num == lg_crcv->rec_blocks.range[lg_crcv->rec_blocks.used-1].end || |
4747 | 0 | block.num == lg_crcv->rec_blocks.range[lg_crcv->rec_blocks.used-1].end - 1) && |
4748 | 0 | lg_crcv->rec_blocks.range[lg_crcv->rec_blocks.used-1].begin < |
4749 | 0 | lg_crcv->rec_blocks.range[lg_crcv->rec_blocks.used-1].end && |
4750 | 0 | (lg_crcv->rec_blocks.range[lg_crcv->rec_blocks.used-1].end % COAP_MAX_PAYLOADS(session)) + 1 |
4751 | 0 | == COAP_MAX_PAYLOADS(session)) { |
4752 | | /* |
4753 | | * At least the last 2 of a PAYLOAD_SET have been received, but some |
4754 | | * missing packets, but have seen the emd of a PAYLOAD_SET. |
4755 | | */ |
4756 | 0 | coap_log_debug("Fast recovery for block %u\n", block.num); |
4757 | 0 | coap_request_missing_q_block2(session, lg_crcv); |
4758 | 0 | } else if (lg_crcv->rec_blocks.used && |
4759 | 0 | this_payload_set > lg_crcv->rec_blocks.processing_payload_set && |
4760 | 0 | this_payload_set != lg_crcv->rec_blocks.latest_payload_set) { |
4761 | 0 | coap_request_missing_q_block2(session, lg_crcv); |
4762 | 0 | } |
4763 | 0 | } |
4764 | 0 | lg_crcv->rec_blocks.latest_payload_set = this_payload_set; |
4765 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4766 | 0 | } else { |
4767 | 0 | coap_log_debug("Duplicate block nr %u\n", block.num); |
4768 | 0 | } |
4769 | 0 | block.num++; |
4770 | 0 | offset = block.num << (block.szx + 4); |
4771 | 0 | if (!block.bert && block_opt != COAP_OPTION_Q_BLOCK2) |
4772 | 0 | break; |
4773 | 0 | } |
4774 | 0 | block.num--; |
4775 | | /* Only process if not duplicate block */ |
4776 | 0 | if (updated_block) { |
4777 | 0 | void *body_free; |
4778 | | |
4779 | | /* Update last_used to prevent premature timeout during long transfers */ |
4780 | 0 | coap_ticks(&lg_crcv->last_used); |
4781 | |
|
4782 | 0 | if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) { |
4783 | 0 | if (size2 < saved_offset + length) { |
4784 | 0 | size2 = saved_offset + length; |
4785 | 0 | } |
4786 | 0 | if (context && context->block_data_cb) { |
4787 | 0 | coap_response_t resp; |
4788 | |
|
4789 | 0 | coap_lock_callback_ret(resp, |
4790 | 0 | context->block_data_cb(session, rcvd, 0, |
4791 | 0 | &lg_crcv->body_data, |
4792 | 0 | length, data, |
4793 | 0 | saved_offset, size2)); |
4794 | 0 | if (resp != COAP_RESPONSE_OK) { |
4795 | 0 | goto fail_resp; |
4796 | 0 | } |
4797 | 0 | } else { |
4798 | 0 | lg_crcv->body_data = coap_block_build_body_lkd(lg_crcv->body_data, length, data, |
4799 | 0 | saved_offset, size2); |
4800 | 0 | if (lg_crcv->body_data == NULL) { |
4801 | 0 | goto fail_resp; |
4802 | 0 | } |
4803 | 0 | } |
4804 | 0 | } |
4805 | 0 | if (block.m || !check_all_blocks_in(&lg_crcv->rec_blocks)) { |
4806 | | /* Not all the payloads of the body have arrived */ |
4807 | 0 | size_t len; |
4808 | 0 | coap_pdu_t *pdu; |
4809 | 0 | uint64_t token; |
4810 | 0 | coap_opt_filter_t drop_options; |
4811 | |
|
4812 | 0 | if (block.m) { |
4813 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4814 | 0 | if (block_opt == COAP_OPTION_Q_BLOCK2) { |
4815 | | /* Blocks could arrive in wrong order */ |
4816 | 0 | if (check_all_blocks_in(&lg_crcv->rec_blocks)) { |
4817 | 0 | goto give_to_app; |
4818 | 0 | } |
4819 | 0 | if (check_all_blocks_in_for_payload_set(session, |
4820 | 0 | &lg_crcv->rec_blocks)) { |
4821 | 0 | block.num = lg_crcv->rec_blocks.range[0].end; |
4822 | | /* Now requesting next payload */ |
4823 | 0 | lg_crcv->rec_blocks.processing_payload_set = |
4824 | 0 | block.num / COAP_MAX_PAYLOADS(session) + 1; |
4825 | 0 | if (check_any_blocks_next_payload_set(session, |
4826 | 0 | &lg_crcv->rec_blocks)) { |
4827 | | /* Need to ask for them individually */ |
4828 | 0 | coap_request_missing_q_block2(session, lg_crcv); |
4829 | 0 | goto skip_app_handler; |
4830 | 0 | } |
4831 | 0 | } else { |
4832 | | /* The remote end will be sending the next one unless this |
4833 | | is a MAX_PAYLOADS and all previous have been received */ |
4834 | 0 | goto skip_app_handler; |
4835 | 0 | } |
4836 | 0 | if (COAP_PROTO_RELIABLE(session->proto) || |
4837 | 0 | rcvd->type != COAP_MESSAGE_NON) |
4838 | 0 | goto skip_app_handler; |
4839 | |
|
4840 | 0 | } else |
4841 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4842 | 0 | block.m = 0; |
4843 | | |
4844 | | /* Ask for the next block */ |
4845 | 0 | token = STATE_TOKEN_FULL(lg_crcv->state_token, ++lg_crcv->retry_counter); |
4846 | 0 | len = coap_encode_var_safe8(buf, sizeof(token), token); |
4847 | 0 | memset(&drop_options, 0, sizeof(coap_opt_filter_t)); |
4848 | 0 | coap_option_filter_set(&drop_options, COAP_OPTION_BLOCK1); |
4849 | 0 | pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, len, buf, |
4850 | 0 | &drop_options, COAP_BOOL_FALSE); |
4851 | 0 | if (!pdu) |
4852 | 0 | goto fail_resp; |
4853 | | |
4854 | 0 | if (rcvd->type == COAP_MESSAGE_NON) |
4855 | 0 | pdu->type = COAP_MESSAGE_NON; /* Server is using NON */ |
4856 | | |
4857 | | /* Only sent with the first block */ |
4858 | 0 | coap_remove_option(pdu, COAP_OPTION_OBSERVE); |
4859 | |
|
4860 | 0 | coap_update_option(pdu, block_opt, |
4861 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
4862 | 0 | ((block.num + 1) << 4) | |
4863 | 0 | (block.m << 3) | block.aszx), |
4864 | 0 | buf); |
4865 | |
|
4866 | 0 | if (session->block_mode & COAP_BLOCK_STLESS_FETCH && pdu->code == COAP_REQUEST_CODE_FETCH) { |
4867 | 0 | (void)coap_get_data(lg_crcv->sent_pdu, &length, &data); |
4868 | 0 | coap_add_data_large_internal(session, NULL, pdu, NULL, NULL, -1, 0, length, data, NULL, NULL, NULL, |
4869 | 0 | 0, 0); |
4870 | 0 | } |
4871 | 0 | if (coap_send_internal(session, pdu, NULL) == COAP_INVALID_MID) |
4872 | | /* Session could now be disconnected, so no lg_crcv */ |
4873 | 0 | goto skip_app_handler; |
4874 | 0 | } |
4875 | 0 | if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) |
4876 | 0 | goto skip_app_handler; |
4877 | | |
4878 | | /* need to put back original token into rcvd */ |
4879 | 0 | coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s); |
4880 | 0 | rcvd->body_offset = saved_offset; |
4881 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4882 | 0 | rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ? |
4883 | 0 | lg_crcv->total_len : size2; |
4884 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
4885 | | rcvd->body_total = size2; |
4886 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
4887 | 0 | coap_log_debug("Client app version of updated PDU (2)\n"); |
4888 | 0 | coap_show_pdu(COAP_LOG_DEBUG, rcvd); |
4889 | |
|
4890 | 0 | if (sent) { |
4891 | | /* need to put back original token into sent */ |
4892 | 0 | if (lg_crcv->app_token) |
4893 | 0 | coap_update_token(sent, lg_crcv->app_token->length, |
4894 | 0 | lg_crcv->app_token->s); |
4895 | 0 | coap_remove_option(sent, lg_crcv->block_option); |
4896 | 0 | } |
4897 | 0 | goto call_app_handler; |
4898 | 0 | } |
4899 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4900 | 0 | give_to_app: |
4901 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4902 | 0 | if ((session->block_mode & COAP_SINGLE_BLOCK_OR_Q) || block.bert) { |
4903 | | /* Pretend that there is no block */ |
4904 | 0 | coap_remove_option(rcvd, block_opt); |
4905 | 0 | if (lg_crcv->observe_set) { |
4906 | 0 | coap_update_option(rcvd, COAP_OPTION_OBSERVE, |
4907 | 0 | lg_crcv->observe_length, lg_crcv->observe); |
4908 | 0 | } |
4909 | 0 | rcvd->body_data = lg_crcv->body_data ? lg_crcv->body_data->s : NULL; |
4910 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4911 | 0 | if (context && context->block_data_cb) { |
4912 | | /* Data has already been provided - do not duplicate */ |
4913 | 0 | if (rcvd->data) { |
4914 | 0 | rcvd->used_size = rcvd->data - rcvd->token - 1; |
4915 | 0 | rcvd->data = NULL; |
4916 | 0 | } |
4917 | 0 | } |
4918 | 0 | rcvd->body_length = block_opt == COAP_OPTION_Q_BLOCK2 ? |
4919 | 0 | lg_crcv->total_len : saved_offset + length; |
4920 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
4921 | | rcvd->body_length = saved_offset + length; |
4922 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
4923 | 0 | rcvd->body_offset = 0; |
4924 | 0 | rcvd->body_total = rcvd->body_length; |
4925 | 0 | } else { |
4926 | 0 | rcvd->body_offset = saved_offset; |
4927 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4928 | 0 | rcvd->body_total = block_opt == COAP_OPTION_Q_BLOCK2 ? |
4929 | 0 | lg_crcv->total_len : size2; |
4930 | | #else /* ! COAP_Q_BLOCK_SUPPORT */ |
4931 | | rcvd->body_total = size2; |
4932 | | #endif /* ! COAP_Q_BLOCK_SUPPORT */ |
4933 | 0 | } |
4934 | | /* need to put back original token into rcvd */ |
4935 | 0 | if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) { |
4936 | 0 | coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s); |
4937 | 0 | coap_log_debug("Client app version of updated PDU (3)\n"); |
4938 | 0 | coap_show_pdu(COAP_LOG_DEBUG, rcvd); |
4939 | 0 | } |
4940 | 0 | if (sent) { |
4941 | | /* need to put back original token into sent */ |
4942 | 0 | if (lg_crcv->app_token) |
4943 | 0 | coap_update_token(sent, lg_crcv->app_token->length, |
4944 | 0 | lg_crcv->app_token->s); |
4945 | 0 | coap_remove_option(sent, lg_crcv->block_option); |
4946 | 0 | } |
4947 | 0 | body_free = lg_crcv->body_data; |
4948 | 0 | lg_crcv->body_data = NULL; |
4949 | 0 | coap_call_response_handler(session, sent, rcvd, body_free); |
4950 | |
|
4951 | 0 | ack_rst_sent = 1; |
4952 | 0 | if (lg_crcv->observe_set == 0) { |
4953 | | /* Expire this entry */ |
4954 | 0 | LL_DELETE(session->lg_crcv, lg_crcv); |
4955 | 0 | coap_block_delete_lg_crcv(session, lg_crcv); |
4956 | 0 | goto skip_app_handler; |
4957 | 0 | } |
4958 | | /* Set up for the next data body as observing */ |
4959 | 0 | lg_crcv->initial = 1; |
4960 | 0 | } |
4961 | 0 | coap_ticks(&lg_crcv->last_used); |
4962 | 0 | goto skip_app_handler; |
4963 | 0 | } else { |
4964 | 0 | coap_opt_t *obs_opt = coap_check_option(rcvd, |
4965 | 0 | COAP_OPTION_OBSERVE, |
4966 | 0 | &opt_iter); |
4967 | 0 | if (context->max_body_size && length > context->max_body_size) { |
4968 | 0 | coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", length, |
4969 | 0 | context->max_body_size); |
4970 | | /* Try to hint to the server there is an issue */ |
4971 | 0 | coap_send_rst_lkd(session, rcvd); |
4972 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_BLOCK_ISSUE, session); |
4973 | 0 | return 1; |
4974 | 0 | } |
4975 | 0 | if (obs_opt) { |
4976 | 0 | lg_crcv->observe_length = min(coap_opt_length(obs_opt), 3); |
4977 | 0 | memcpy(lg_crcv->observe, coap_opt_value(obs_opt), lg_crcv->observe_length); |
4978 | 0 | lg_crcv->observe_set = 1; |
4979 | 0 | } else { |
4980 | 0 | lg_crcv->observe_set = 0; |
4981 | 0 | if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) { |
4982 | | /* need to put back original token into rcvd */ |
4983 | 0 | coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s); |
4984 | 0 | coap_remove_option(rcvd, COAP_OPTION_BLOCK1); |
4985 | 0 | coap_log_debug("PDU presented to app.\n"); |
4986 | 0 | coap_show_pdu(COAP_LOG_DEBUG, rcvd); |
4987 | 0 | } |
4988 | | /* Expire this entry */ |
4989 | 0 | goto expire_lg_crcv; |
4990 | 0 | } |
4991 | 0 | } |
4992 | 0 | coap_ticks(&lg_crcv->last_used); |
4993 | 0 | } else if (rcvd->code == COAP_RESPONSE_CODE(401)) { |
4994 | 0 | #if COAP_OSCORE_SUPPORT |
4995 | 0 | if (check_freshness(session, rcvd, |
4996 | 0 | (session->oscore_encryption == 0) ? sent : NULL, |
4997 | 0 | NULL, lg_crcv)) |
4998 | | #else /* !COAP_OSCORE_SUPPORT */ |
4999 | | if (check_freshness(session, rcvd, sent, NULL, lg_crcv)) |
5000 | | #endif /* !COAP_OSCORE_SUPPORT */ |
5001 | 0 | goto skip_app_handler; |
5002 | 0 | goto expire_lg_crcv; |
5003 | 0 | } else if (rcvd->code == COAP_RESPONSE_CODE(402)) { |
5004 | 0 | coap_opt_t *abb_opt = sent ? coap_check_option(sent, |
5005 | 0 | COAP_OPTION_URI_PATH_ABB, |
5006 | 0 | &opt_iter) : NULL; |
5007 | 0 | if (abb_opt) { |
5008 | | /* Send the request again with the Uri-Path-Abbrev expanded out */ |
5009 | 0 | coap_pdu_t *pdu; |
5010 | 0 | size_t data_len; |
5011 | 0 | const uint8_t *data; |
5012 | 0 | uint64_t token; |
5013 | 0 | uint8_t ltoken[8]; |
5014 | 0 | size_t ltoken_len; |
5015 | |
|
5016 | 0 | session->no_path_abbrev = 1; |
5017 | 0 | token = STATE_TOKEN_FULL(lg_crcv->state_token, |
5018 | 0 | ++lg_crcv->retry_counter); |
5019 | 0 | ltoken_len = coap_encode_var_safe8(ltoken, sizeof(token), token); |
5020 | 0 | pdu = coap_pdu_duplicate_lkd(lg_crcv->sent_pdu, session, ltoken_len, |
5021 | 0 | ltoken, NULL, COAP_BOOL_TRUE); |
5022 | 0 | if (pdu) { |
5023 | 0 | if (coap_get_data(lg_crcv->sent_pdu, &data_len, &data)) { |
5024 | 0 | coap_add_data(pdu, data_len, data); |
5025 | 0 | } |
5026 | 0 | #if COAP_PROXY_SUPPORT |
5027 | 0 | coap_proxy_req_t *proxy_req = session->context->proxy_list_count ? |
5028 | 0 | coap_proxy_map_outgoing_request(session, rcvd, NULL) : NULL; |
5029 | |
|
5030 | 0 | if (proxy_req) { |
5031 | 0 | coap_bin_const_t *new = coap_new_bin_const(ltoken, ltoken_len); |
5032 | 0 | if (new) { |
5033 | 0 | coap_delete_bin_const(proxy_req->token_used); |
5034 | 0 | proxy_req->token_used = new; |
5035 | 0 | coap_proxy_log_entry(proxy_req->incoming, proxy_req->pdu, proxy_req->token_used, "upd"); |
5036 | 0 | } |
5037 | 0 | } |
5038 | 0 | #endif /* COAP_PROXY_SUPPORT */ |
5039 | 0 | coap_log_debug("* Retransmitting PDU with Uri-Path-Abbrev replaced (1)\n"); |
5040 | 0 | coap_delete_pdu_lkd(lg_crcv->sent_pdu); |
5041 | 0 | lg_crcv->sent_pdu = coap_pdu_reference_lkd(pdu); |
5042 | 0 | coap_send_internal(session, pdu, NULL); |
5043 | 0 | goto skip_app_handler; |
5044 | 0 | } |
5045 | 0 | } |
5046 | 0 | goto expire_lg_crcv; |
5047 | 0 | } else { |
5048 | | /* Not 2.xx, 4.01 or 4.02 - assume it is a failure of some sort */ |
5049 | 0 | goto expire_lg_crcv; |
5050 | 0 | } |
5051 | 0 | if (!block.m && !lg_crcv->observe_set) { |
5052 | 0 | fail_resp: |
5053 | | |
5054 | | /* lg_crcv no longer required - cache it for 1 sec */ |
5055 | 0 | if (COAP_MAX_TRANSMIT_WAIT_TICKS(session) >= COAP_TICKS_PER_SECOND) { |
5056 | 0 | adjust = COAP_MAX_TRANSMIT_WAIT_TICKS(session) - COAP_TICKS_PER_SECOND; |
5057 | 0 | } else { |
5058 | 0 | adjust = 0; |
5059 | 0 | } |
5060 | 0 | coap_ticks(&lg_crcv->last_used); |
5061 | 0 | lg_crcv->last_used -= adjust; |
5062 | 0 | } |
5063 | | /* need to put back original token into rcvd */ |
5064 | 0 | if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) { |
5065 | 0 | coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s); |
5066 | 0 | coap_log_debug("Client app version of updated PDU (4)\n"); |
5067 | 0 | coap_show_pdu(COAP_LOG_DEBUG, rcvd); |
5068 | 0 | } |
5069 | 0 | } |
5070 | | |
5071 | | /* Check if receiving a block response and if blocks can be set up */ |
5072 | 0 | if (recursive == COAP_RECURSE_OK && !lg_crcv) { |
5073 | 0 | if (!sent) { |
5074 | 0 | if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block) |
5075 | 0 | #if COAP_Q_BLOCK_SUPPORT |
5076 | 0 | || |
5077 | 0 | coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block) |
5078 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
5079 | 0 | ) { |
5080 | 0 | coap_log_debug("** %s: large body receive internal issue\n", |
5081 | 0 | coap_session_str(session)); |
5082 | 0 | goto skip_app_handler; |
5083 | 0 | } |
5084 | 0 | } else if (COAP_RESPONSE_CLASS(rcvd->code) == 2) { |
5085 | 0 | const uint8_t *data; |
5086 | 0 | size_t length; |
5087 | |
|
5088 | 0 | if (coap_get_block_b(session, rcvd, COAP_OPTION_BLOCK2, &block)) { |
5089 | 0 | #if COAP_Q_BLOCK_SUPPORT |
5090 | 0 | if (session->block_mode & COAP_BLOCK_PROBE_Q_BLOCK) { |
5091 | 0 | set_block_mode_drop_q(session->block_mode); |
5092 | 0 | coap_log_debug("Q-Block support disabled\n"); |
5093 | 0 | } |
5094 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
5095 | 0 | have_block = 1; |
5096 | 0 | if (block.num != 0) { |
5097 | | /* Assume random access and just give the single response to app */ |
5098 | 0 | size_t chunk = (size_t)1 << (block.szx + 4); |
5099 | |
|
5100 | 0 | coap_get_data(rcvd, &length, &data); |
5101 | 0 | rcvd->body_offset = block.num*chunk; |
5102 | 0 | rcvd->body_total = block.num*chunk + length + (block.m ? 1 : 0); |
5103 | 0 | goto call_app_handler; |
5104 | 0 | } |
5105 | 0 | } |
5106 | 0 | #if COAP_Q_BLOCK_SUPPORT |
5107 | 0 | else if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &block)) { |
5108 | 0 | have_block = 1; |
5109 | | /* server indicating that it supports Q_BLOCK2 */ |
5110 | 0 | if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) { |
5111 | 0 | set_block_mode_has_q(session->block_mode); |
5112 | 0 | } |
5113 | 0 | } |
5114 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
5115 | 0 | if (have_block) { |
5116 | 0 | lg_crcv = coap_block_new_lg_crcv(session, sent, NULL); |
5117 | |
|
5118 | 0 | if (lg_crcv) { |
5119 | 0 | LL_PREPEND(session->lg_crcv, lg_crcv); |
5120 | 0 | return coap_handle_response_get_block(context, session, sent, rcvd, |
5121 | 0 | COAP_RECURSE_NO); |
5122 | 0 | } |
5123 | 0 | } |
5124 | 0 | coap_get_data(rcvd, &length, &data); |
5125 | 0 | if (context->max_body_size && length > context->max_body_size) { |
5126 | 0 | coap_log_warn("Unable to handle body size %" PRIuS " (max %" PRIu32 ")\n", length, |
5127 | 0 | context->max_body_size); |
5128 | | /* Try to hint to the server there is an issue */ |
5129 | 0 | coap_send_rst_lkd(session, rcvd); |
5130 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_BLOCK_ISSUE, session); |
5131 | 0 | return 1; |
5132 | 0 | } |
5133 | 0 | track_echo(session, rcvd); |
5134 | 0 | } else if (rcvd->code == COAP_RESPONSE_CODE(401)) { |
5135 | 0 | lg_crcv = coap_block_new_lg_crcv(session, sent, NULL); |
5136 | |
|
5137 | 0 | if (lg_crcv) { |
5138 | 0 | LL_PREPEND(session->lg_crcv, lg_crcv); |
5139 | 0 | return coap_handle_response_get_block(context, session, sent, rcvd, |
5140 | 0 | COAP_RECURSE_NO); |
5141 | 0 | } |
5142 | 0 | } else if (rcvd->code == COAP_RESPONSE_CODE(402)) { |
5143 | 0 | coap_opt_t *abb_opt = coap_check_option(sent, |
5144 | 0 | COAP_OPTION_URI_PATH_ABB, |
5145 | 0 | &opt_iter); |
5146 | 0 | if (abb_opt) { |
5147 | | /* |
5148 | | * Send the request again with the Uri-Path-Abbrev expanded out, but need |
5149 | | lg_crcv in place to handle the token update. |
5150 | | */ |
5151 | 0 | lg_crcv = coap_block_new_lg_crcv(session, sent, NULL); |
5152 | |
|
5153 | 0 | if (lg_crcv) { |
5154 | 0 | LL_PREPEND(session->lg_crcv, lg_crcv); |
5155 | 0 | return coap_handle_response_get_block(context, session, sent, rcvd, |
5156 | 0 | COAP_RECURSE_NO); |
5157 | 0 | } |
5158 | 0 | } |
5159 | 0 | } |
5160 | 0 | } |
5161 | 0 | return 0; |
5162 | | |
5163 | 0 | expire_lg_crcv: |
5164 | | /* need to put back original token into rcvd */ |
5165 | 0 | if (!coap_binary_equal(&rcvd->actual_token, lg_crcv->app_token)) { |
5166 | 0 | coap_update_token(rcvd, lg_crcv->app_token->length, lg_crcv->app_token->s); |
5167 | 0 | coap_log_debug("Client app version of updated PDU (5)\n"); |
5168 | 0 | coap_show_pdu(COAP_LOG_DEBUG, rcvd); |
5169 | 0 | } |
5170 | |
|
5171 | 0 | if (sent) { |
5172 | | /* need to put back original token into sent */ |
5173 | 0 | if (lg_crcv->app_token) |
5174 | 0 | coap_update_token(sent, lg_crcv->app_token->length, |
5175 | 0 | lg_crcv->app_token->s); |
5176 | 0 | coap_remove_option(sent, lg_crcv->block_option); |
5177 | 0 | } |
5178 | | /* Expire this entry */ |
5179 | 0 | LL_DELETE(session->lg_crcv, lg_crcv); |
5180 | 0 | coap_block_delete_lg_crcv(session, lg_crcv); |
5181 | |
|
5182 | 0 | call_app_handler: |
5183 | 0 | return 0; |
5184 | | |
5185 | 0 | skip_app_handler: |
5186 | 0 | if (!ack_rst_sent) |
5187 | 0 | coap_send_ack_lkd(session, rcvd); |
5188 | 0 | return 1; |
5189 | 0 | } |
5190 | | #endif /* COAP_CLIENT_SUPPORT */ |
5191 | | |
5192 | | #if COAP_SERVER_SUPPORT |
5193 | | /* Check if lg_xmit generated and update PDU code if so */ |
5194 | | void |
5195 | | coap_check_code_lg_xmit(const coap_session_t *session, |
5196 | | const coap_pdu_t *request, |
5197 | | coap_pdu_t *response, const coap_resource_t *resource, |
5198 | 0 | const coap_string_t *query) { |
5199 | 0 | coap_lg_xmit_t *lg_xmit; |
5200 | |
|
5201 | 0 | if (response->code == 0) |
5202 | 0 | return; |
5203 | 0 | lg_xmit = coap_find_lg_xmit_response(session, request, resource, query); |
5204 | 0 | if (lg_xmit && lg_xmit->sent_pdu && lg_xmit->sent_pdu->code == 0) { |
5205 | 0 | lg_xmit->sent_pdu->code = response->code; |
5206 | 0 | return; |
5207 | 0 | } |
5208 | 0 | } |
5209 | | #endif /* COAP_SERVER_SUPPORT */ |
5210 | | |
5211 | | #if COAP_CLIENT_SUPPORT |
5212 | | void |
5213 | 0 | coap_check_update_token(coap_session_t *session, coap_pdu_t *pdu) { |
5214 | 0 | uint64_t token_match = |
5215 | 0 | STATE_TOKEN_BASE(coap_decode_var_bytes8(pdu->actual_token.s, |
5216 | 0 | pdu->actual_token.length)); |
5217 | 0 | coap_lg_xmit_t *lg_xmit; |
5218 | 0 | coap_lg_crcv_t *lg_crcv; |
5219 | |
|
5220 | 0 | if (session->lg_crcv) { |
5221 | 0 | LL_FOREACH(session->lg_crcv, lg_crcv) { |
5222 | 0 | if (coap_binary_equal(&pdu->actual_token, lg_crcv->app_token)) |
5223 | 0 | return; |
5224 | 0 | if (token_match == STATE_TOKEN_BASE(lg_crcv->state_token)) { |
5225 | 0 | coap_update_token(pdu, lg_crcv->app_token->length, |
5226 | 0 | lg_crcv->app_token->s); |
5227 | 0 | coap_log_debug("Client app version of updated PDU (6)\n"); |
5228 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
5229 | 0 | return; |
5230 | 0 | } |
5231 | 0 | } |
5232 | 0 | } |
5233 | 0 | if (COAP_PDU_IS_REQUEST(pdu) && session->lg_xmit) { |
5234 | 0 | LL_FOREACH(session->lg_xmit, lg_xmit) { |
5235 | 0 | if (coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) |
5236 | 0 | return; |
5237 | 0 | if (token_match == STATE_TOKEN_BASE(lg_xmit->b.b1.state_token)) { |
5238 | 0 | coap_update_token(pdu, lg_xmit->b.b1.app_token->length, |
5239 | 0 | lg_xmit->b.b1.app_token->s); |
5240 | 0 | coap_log_debug("Client app version of updated PDU (7)\n"); |
5241 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
5242 | 0 | return; |
5243 | 0 | } |
5244 | 0 | } |
5245 | 0 | } |
5246 | 0 | } |
5247 | | #endif /* ! COAP_CLIENT_SUPPORT */ |