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