/src/libcoap/src/coap_net.c
Line | Count | Source |
1 | | /* coap_net.c -- CoAP context interface |
2 | | * |
3 | | * Copyright (C) 2010--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_net.c |
13 | | * @brief CoAP context functions |
14 | | */ |
15 | | |
16 | | #include "coap3/coap_libcoap_build.h" |
17 | | #include "coap3/coap_mutex_internal.h" |
18 | | |
19 | | #include <ctype.h> |
20 | | #include <stdio.h> |
21 | | #ifdef HAVE_LIMITS_H |
22 | | #include <limits.h> |
23 | | #endif |
24 | | |
25 | | #ifndef __ZEPHYR__ |
26 | | #ifdef HAVE_UNISTD_H |
27 | | #include <unistd.h> |
28 | | #else |
29 | | #ifdef HAVE_SYS_UNISTD_H |
30 | | #include <sys/unistd.h> |
31 | | #endif |
32 | | #endif |
33 | | #ifdef HAVE_SYS_TYPES_H |
34 | | #include <sys/types.h> |
35 | | #endif |
36 | | #ifdef HAVE_SYS_SOCKET_H |
37 | | #include <sys/socket.h> |
38 | | #endif |
39 | | #ifdef HAVE_SYS_IOCTL_H |
40 | | #include <sys/ioctl.h> |
41 | | #endif |
42 | | #ifdef HAVE_NETINET_IN_H |
43 | | #include <netinet/in.h> |
44 | | #endif |
45 | | #ifdef HAVE_ARPA_INET_H |
46 | | #include <arpa/inet.h> |
47 | | #endif |
48 | | #ifdef HAVE_NET_IF_H |
49 | | #include <net/if.h> |
50 | | #endif |
51 | | #ifdef COAP_EPOLL_SUPPORT |
52 | | #include <sys/epoll.h> |
53 | | #include <sys/timerfd.h> |
54 | | #endif /* COAP_EPOLL_SUPPORT */ |
55 | | #ifdef HAVE_WS2TCPIP_H |
56 | | #include <ws2tcpip.h> |
57 | | #endif |
58 | | |
59 | | #ifdef HAVE_NETDB_H |
60 | | #include <netdb.h> |
61 | | #endif |
62 | | #endif /* !__ZEPHYR__ */ |
63 | | |
64 | | #ifdef WITH_LWIP |
65 | | #include <lwip/pbuf.h> |
66 | | #include <lwip/udp.h> |
67 | | #include <lwip/timeouts.h> |
68 | | #include <lwip/tcpip.h> |
69 | | #endif |
70 | | |
71 | | #ifndef INET6_ADDRSTRLEN |
72 | | #define INET6_ADDRSTRLEN 40 |
73 | | #endif |
74 | | |
75 | | #ifndef min |
76 | 0 | #define min(a,b) ((a) < (b) ? (a) : (b)) |
77 | | #endif |
78 | | |
79 | | /** |
80 | | * The number of bits for the fractional part of ACK_TIMEOUT and |
81 | | * ACK_RANDOM_FACTOR. Must be less or equal 8. |
82 | | */ |
83 | | #define FRAC_BITS 6 |
84 | | |
85 | | /** |
86 | | * The maximum number of bits for fixed point integers that are used |
87 | | * for retransmission time calculation. Currently this must be @c 8. |
88 | | */ |
89 | | #define MAX_BITS 8 |
90 | | |
91 | | #if FRAC_BITS > 8 |
92 | | #error FRAC_BITS must be less or equal 8 |
93 | | #endif |
94 | | |
95 | | /** creates a Qx.frac from fval in coap_fixed_point_t */ |
96 | | #define Q(frac,fval) ((uint16_t)(((1 << (frac)) * fval.integer_part) + \ |
97 | | ((1 << (frac)) * fval.fractional_part + 500)/1000)) |
98 | | |
99 | | /** creates a Qx.FRAC_BITS from session's 'ack_random_factor' */ |
100 | | #define ACK_RANDOM_FACTOR \ |
101 | | Q(FRAC_BITS, session->ack_random_factor) |
102 | | |
103 | | /** creates a Qx.FRAC_BITS from session's 'ack_timeout' */ |
104 | | #define ACK_TIMEOUT Q(FRAC_BITS, session->ack_timeout) |
105 | | |
106 | | static int send_recv_terminate = 0; |
107 | | |
108 | | COAP_STATIC_INLINE coap_queue_t * |
109 | 0 | coap_malloc_node(void) { |
110 | 0 | return (coap_queue_t *)coap_malloc_type(COAP_NODE, sizeof(coap_queue_t)); |
111 | 0 | } |
112 | | |
113 | | COAP_STATIC_INLINE void |
114 | 0 | coap_free_node(coap_queue_t *node) { |
115 | 0 | coap_free_type(COAP_NODE, node); |
116 | 0 | } |
117 | | |
118 | | unsigned int |
119 | 0 | coap_adjust_basetime(coap_context_t *ctx, coap_tick_t now) { |
120 | 0 | unsigned int result = 0; |
121 | 0 | coap_tick_diff_t delta = (coap_tick_diff_t)now - (coap_tick_diff_t)ctx->sendqueue_basetime; |
122 | |
|
123 | 0 | if (ctx->sendqueue) { |
124 | | /* delta < 0 means that the new time stamp is before the old. */ |
125 | 0 | if (delta <= 0) { |
126 | 0 | ctx->sendqueue->t = (coap_tick_diff_t)ctx->sendqueue->t - delta; |
127 | 0 | } else { |
128 | | /* This case is more complex: The time must be advanced forward, |
129 | | * thus possibly leading to timed out elements at the queue's |
130 | | * start. For every element that has timed out, its relative |
131 | | * time is set to zero and the result counter is increased. */ |
132 | |
|
133 | 0 | coap_queue_t *q = ctx->sendqueue; |
134 | 0 | coap_tick_t t = 0; |
135 | 0 | while (q && (t + q->t < (coap_tick_t)delta)) { |
136 | 0 | t += q->t; |
137 | 0 | q->t = 0; |
138 | 0 | result++; |
139 | 0 | q = q->next; |
140 | 0 | } |
141 | | |
142 | | /* finally adjust the first element that has not expired */ |
143 | 0 | if (q) { |
144 | 0 | q->t = (coap_tick_t)delta - t; |
145 | 0 | } |
146 | 0 | } |
147 | 0 | } |
148 | | |
149 | | /* adjust basetime */ |
150 | 0 | ctx->sendqueue_basetime = (coap_tick_diff_t)ctx->sendqueue_basetime + delta; |
151 | |
|
152 | 0 | return result; |
153 | 0 | } |
154 | | |
155 | | int |
156 | 0 | coap_insert_node(coap_queue_t **queue, coap_queue_t *node) { |
157 | 0 | coap_queue_t *p, *q; |
158 | 0 | if (!queue || !node) |
159 | 0 | return 0; |
160 | | |
161 | | /* set queue head if empty */ |
162 | 0 | if (!*queue) { |
163 | 0 | *queue = node; |
164 | 0 | return 1; |
165 | 0 | } |
166 | | |
167 | | /* replace queue head if PDU's time is less than head's time */ |
168 | 0 | q = *queue; |
169 | 0 | if (node->t < q->t) { |
170 | 0 | node->next = q; |
171 | 0 | *queue = node; |
172 | 0 | q->t -= node->t; /* make q->t relative to node->t */ |
173 | 0 | return 1; |
174 | 0 | } |
175 | | |
176 | | /* search for right place to insert */ |
177 | 0 | do { |
178 | 0 | node->t -= q->t; /* make node-> relative to q->t */ |
179 | 0 | p = q; |
180 | 0 | q = q->next; |
181 | 0 | } while (q && q->t <= node->t); |
182 | | |
183 | | /* insert new item */ |
184 | 0 | if (q) { |
185 | 0 | q->t -= node->t; /* make q->t relative to node->t */ |
186 | 0 | } |
187 | 0 | node->next = q; |
188 | 0 | p->next = node; |
189 | 0 | return 1; |
190 | 0 | } |
191 | | |
192 | | COAP_API int |
193 | 0 | coap_delete_node(coap_queue_t *node) { |
194 | 0 | int ret; |
195 | |
|
196 | 0 | if (!node) |
197 | 0 | return 0; |
198 | | |
199 | 0 | coap_lock_lock(return 0); |
200 | 0 | ret = coap_delete_node_lkd(node); |
201 | 0 | coap_lock_unlock(); |
202 | 0 | return ret; |
203 | 0 | } |
204 | | |
205 | | int |
206 | 100 | coap_delete_node_lkd(coap_queue_t *node) { |
207 | 100 | if (!node) |
208 | 100 | return 0; |
209 | | |
210 | 0 | coap_delete_pdu_lkd(node->pdu); |
211 | 0 | if (node->session) { |
212 | | /* |
213 | | * Need to remove out of context->sendqueue as added in by coap_wait_ack() |
214 | | */ |
215 | 0 | if (node->session->context->sendqueue) { |
216 | 0 | LL_DELETE(node->session->context->sendqueue, node); |
217 | 0 | } |
218 | 0 | coap_session_release_lkd(node->session); |
219 | 0 | } |
220 | 0 | coap_free_node(node); |
221 | |
|
222 | 0 | return 1; |
223 | 100 | } |
224 | | |
225 | | void |
226 | 32 | coap_delete_all(coap_queue_t *queue) { |
227 | 32 | if (!queue) |
228 | 32 | return; |
229 | | |
230 | 0 | coap_delete_all(queue->next); |
231 | 0 | coap_delete_node_lkd(queue); |
232 | 0 | } |
233 | | |
234 | | coap_queue_t * |
235 | 0 | coap_new_node(void) { |
236 | 0 | coap_queue_t *node; |
237 | 0 | node = coap_malloc_node(); |
238 | |
|
239 | 0 | if (!node) { |
240 | 0 | coap_log_warn("coap_new_node: malloc failed\n"); |
241 | 0 | return NULL; |
242 | 0 | } |
243 | | |
244 | 0 | memset(node, 0, sizeof(*node)); |
245 | 0 | return node; |
246 | 0 | } |
247 | | |
248 | | coap_queue_t * |
249 | 0 | coap_peek_next(coap_context_t *context) { |
250 | 0 | if (!context || !context->sendqueue) |
251 | 0 | return NULL; |
252 | | |
253 | 0 | return context->sendqueue; |
254 | 0 | } |
255 | | |
256 | | coap_queue_t * |
257 | 0 | coap_pop_next(coap_context_t *context) { |
258 | 0 | coap_queue_t *next; |
259 | |
|
260 | 0 | if (!context || !context->sendqueue) |
261 | 0 | return NULL; |
262 | | |
263 | 0 | next = context->sendqueue; |
264 | 0 | context->sendqueue = context->sendqueue->next; |
265 | 0 | if (context->sendqueue) { |
266 | 0 | context->sendqueue->t += next->t; |
267 | 0 | } |
268 | 0 | next->next = NULL; |
269 | 0 | return next; |
270 | 0 | } |
271 | | |
272 | | #if COAP_CLIENT_SUPPORT |
273 | | const coap_bin_const_t * |
274 | 0 | coap_get_session_client_psk_key(const coap_session_t *session) { |
275 | |
|
276 | 0 | if (session->psk_key) { |
277 | 0 | return session->psk_key; |
278 | 0 | } |
279 | 0 | if (session->cpsk_setup_data.psk_info.key.length) |
280 | 0 | return &session->cpsk_setup_data.psk_info.key; |
281 | | |
282 | | /* Not defined in coap_new_client_session_psk2() */ |
283 | 0 | return NULL; |
284 | 0 | } |
285 | | |
286 | | const coap_bin_const_t * |
287 | 0 | coap_get_session_client_psk_identity(const coap_session_t *session) { |
288 | |
|
289 | 0 | if (session->psk_identity) { |
290 | 0 | return session->psk_identity; |
291 | 0 | } |
292 | 0 | if (session->cpsk_setup_data.psk_info.identity.length) |
293 | 0 | return &session->cpsk_setup_data.psk_info.identity; |
294 | | |
295 | | /* Not defined in coap_new_client_session_psk2() */ |
296 | 0 | return NULL; |
297 | 0 | } |
298 | | #endif /* COAP_CLIENT_SUPPORT */ |
299 | | |
300 | | #if COAP_SERVER_SUPPORT |
301 | | const coap_bin_const_t * |
302 | 0 | coap_get_session_server_psk_key(const coap_session_t *session) { |
303 | |
|
304 | 0 | if (session->psk_key) |
305 | 0 | return session->psk_key; |
306 | | |
307 | 0 | if (session->context->spsk_setup_data.psk_info.key.length) |
308 | 0 | return &session->context->spsk_setup_data.psk_info.key; |
309 | | |
310 | | /* Not defined in coap_context_set_psk2() */ |
311 | 0 | return NULL; |
312 | 0 | } |
313 | | |
314 | | const coap_bin_const_t * |
315 | 0 | coap_get_session_server_psk_hint(const coap_session_t *session) { |
316 | |
|
317 | 0 | if (session->psk_hint) |
318 | 0 | return session->psk_hint; |
319 | | |
320 | 0 | if (session->context->spsk_setup_data.psk_info.hint.length) |
321 | 0 | return &session->context->spsk_setup_data.psk_info.hint; |
322 | | |
323 | | /* Not defined in coap_context_set_psk2() */ |
324 | 0 | return NULL; |
325 | 0 | } |
326 | | |
327 | | COAP_API int |
328 | | coap_context_set_psk(coap_context_t *ctx, |
329 | | const char *hint, |
330 | | const uint8_t *key, |
331 | 0 | size_t key_len) { |
332 | 0 | int ret; |
333 | |
|
334 | 0 | coap_lock_lock(return 0); |
335 | 0 | ret = coap_context_set_psk_lkd(ctx, hint, key, key_len); |
336 | 0 | coap_lock_unlock(); |
337 | 0 | return ret; |
338 | 0 | } |
339 | | |
340 | | int |
341 | | coap_context_set_psk_lkd(coap_context_t *ctx, |
342 | | const char *hint, |
343 | | const uint8_t *key, |
344 | 0 | size_t key_len) { |
345 | 0 | coap_dtls_spsk_t setup_data; |
346 | |
|
347 | 0 | coap_lock_check_locked(); |
348 | 0 | memset(&setup_data, 0, sizeof(setup_data)); |
349 | 0 | if (hint) { |
350 | 0 | setup_data.psk_info.hint.s = (const uint8_t *)hint; |
351 | 0 | setup_data.psk_info.hint.length = strlen(hint); |
352 | 0 | } |
353 | |
|
354 | 0 | if (key && key_len > 0) { |
355 | 0 | setup_data.psk_info.key.s = key; |
356 | 0 | setup_data.psk_info.key.length = key_len; |
357 | 0 | } |
358 | |
|
359 | 0 | return coap_context_set_psk2_lkd(ctx, &setup_data); |
360 | 0 | } |
361 | | |
362 | | COAP_API int |
363 | 0 | coap_context_set_psk2(coap_context_t *ctx, coap_dtls_spsk_t *setup_data) { |
364 | 0 | int ret; |
365 | |
|
366 | 0 | coap_lock_lock(return 0); |
367 | 0 | ret = coap_context_set_psk2_lkd(ctx, setup_data); |
368 | 0 | coap_lock_unlock(); |
369 | 0 | return ret; |
370 | 0 | } |
371 | | |
372 | | int |
373 | 0 | coap_context_set_psk2_lkd(coap_context_t *ctx, coap_dtls_spsk_t *setup_data) { |
374 | 0 | if (!setup_data) |
375 | 0 | return 0; |
376 | | |
377 | 0 | coap_lock_check_locked(); |
378 | 0 | ctx->spsk_setup_data = *setup_data; |
379 | |
|
380 | 0 | if (coap_dtls_is_supported() || coap_tls_is_supported()) { |
381 | 0 | return coap_dtls_context_set_spsk(ctx, setup_data); |
382 | 0 | } |
383 | 0 | return 0; |
384 | 0 | } |
385 | | |
386 | | COAP_API int |
387 | | coap_context_set_pki(coap_context_t *ctx, |
388 | 0 | const coap_dtls_pki_t *setup_data) { |
389 | 0 | int ret; |
390 | |
|
391 | 0 | coap_lock_lock(return 0); |
392 | 0 | ret = coap_context_set_pki_lkd(ctx, setup_data); |
393 | 0 | coap_lock_unlock(); |
394 | 0 | return ret; |
395 | 0 | } |
396 | | |
397 | | int |
398 | | coap_context_set_pki_lkd(coap_context_t *ctx, |
399 | 0 | const coap_dtls_pki_t *setup_data) { |
400 | 0 | coap_lock_check_locked(); |
401 | 0 | if (!setup_data) |
402 | 0 | return 0; |
403 | 0 | if (setup_data->version != COAP_DTLS_PKI_SETUP_VERSION) { |
404 | 0 | coap_log_err("coap_context_set_pki: Wrong version of setup_data\n"); |
405 | 0 | return 0; |
406 | 0 | } |
407 | 0 | if (coap_dtls_is_supported() || coap_tls_is_supported()) { |
408 | 0 | return coap_dtls_context_set_pki(ctx, setup_data, COAP_DTLS_ROLE_SERVER); |
409 | 0 | } |
410 | 0 | return 0; |
411 | 0 | } |
412 | | #endif /* ! COAP_SERVER_SUPPORT */ |
413 | | |
414 | | COAP_API int |
415 | | coap_context_set_pki_root_cas(coap_context_t *ctx, |
416 | | const char *ca_file, |
417 | 0 | const char *ca_dir) { |
418 | 0 | int ret; |
419 | |
|
420 | 0 | coap_lock_lock(return 0); |
421 | 0 | ret = coap_context_set_pki_root_cas_lkd(ctx, ca_file, ca_dir); |
422 | 0 | coap_lock_unlock(); |
423 | 0 | return ret; |
424 | 0 | } |
425 | | |
426 | | int |
427 | | coap_context_set_pki_root_cas_lkd(coap_context_t *ctx, |
428 | | const char *ca_file, |
429 | 0 | const char *ca_dir) { |
430 | 0 | if (coap_dtls_is_supported() || coap_tls_is_supported()) { |
431 | 0 | return coap_dtls_context_set_pki_root_cas(ctx, ca_file, ca_dir); |
432 | 0 | } |
433 | 0 | return 0; |
434 | 0 | } |
435 | | |
436 | | COAP_API int |
437 | 0 | coap_context_load_pki_trust_store(coap_context_t *ctx) { |
438 | 0 | int ret; |
439 | |
|
440 | 0 | coap_lock_lock(return 0); |
441 | 0 | ret = coap_context_load_pki_trust_store_lkd(ctx); |
442 | 0 | coap_lock_unlock(); |
443 | 0 | return ret; |
444 | 0 | } |
445 | | |
446 | | int |
447 | 0 | coap_context_load_pki_trust_store_lkd(coap_context_t *ctx) { |
448 | 0 | if (coap_dtls_is_supported() || coap_tls_is_supported()) { |
449 | 0 | return coap_dtls_context_load_pki_trust_store(ctx); |
450 | 0 | } |
451 | 0 | return 0; |
452 | 0 | } |
453 | | |
454 | | |
455 | | void |
456 | 0 | coap_context_set_keepalive(coap_context_t *context, unsigned int seconds) { |
457 | 0 | context->ping_timeout = seconds; |
458 | 0 | } |
459 | | |
460 | | int |
461 | 0 | coap_context_set_cid_tuple_change(coap_context_t *context, uint8_t every) { |
462 | 0 | #if COAP_CLIENT_SUPPORT |
463 | 0 | return coap_dtls_set_cid_tuple_change(context, every); |
464 | | #else /* ! COAP_CLIENT_SUPPORT */ |
465 | | (void)context; |
466 | | (void)every; |
467 | | return 0; |
468 | | #endif /* ! COAP_CLIENT_SUPPORT */ |
469 | 0 | } |
470 | | |
471 | | void |
472 | | coap_context_rate_limit_ppm(coap_context_t *context, |
473 | 0 | uint64_t rate_limit_ppm) { |
474 | 0 | if (rate_limit_ppm) { |
475 | 0 | context->rl_ticks_per_packet = (60ULL * COAP_TICKS_PER_SECOND) / rate_limit_ppm; |
476 | 0 | } else { |
477 | 0 | context->rl_ticks_per_packet = 0; |
478 | 0 | } |
479 | 0 | } |
480 | | |
481 | | void |
482 | | coap_context_set_max_body_size(coap_context_t *context, |
483 | 0 | size_t max_body_size) { |
484 | 0 | assert(max_body_size == 0 || max_body_size > 1024); |
485 | 0 | if (max_body_size == 0 || max_body_size > 1024) { |
486 | 0 | context->max_body_size = max_body_size; |
487 | 0 | } |
488 | 0 | } |
489 | | |
490 | | void |
491 | | coap_context_set_max_bodies_ram(coap_context_t *context, |
492 | 0 | size_t max_bodies_ram) { |
493 | 0 | #if COAP_SERVER_SUPPORT |
494 | 0 | assert(max_bodies_ram == 0 || max_bodies_ram > 1024); |
495 | 0 | if (max_bodies_ram == 0 || max_bodies_ram > 1024) { |
496 | 0 | context->max_bodies_ram = max_bodies_ram; |
497 | 0 | } |
498 | | /* Cannot let a single session keep failing if it is larger then max_bodies_ram */ |
499 | 0 | if (context->max_body_size == 0) |
500 | 0 | context->max_body_size = max_bodies_ram; |
501 | | #else |
502 | | (void)context; |
503 | | (void)max_bodies_ram; |
504 | | #endif /* COAP_SERVER_SUPPORT */ |
505 | 0 | } |
506 | | |
507 | | void |
508 | | coap_context_set_max_token_size(coap_context_t *context, |
509 | 0 | size_t max_token_size) { |
510 | 0 | assert(max_token_size >= COAP_TOKEN_DEFAULT_MAX && |
511 | 0 | max_token_size <= COAP_TOKEN_EXT_MAX); |
512 | 0 | if (max_token_size >= COAP_TOKEN_DEFAULT_MAX && |
513 | 0 | max_token_size <= COAP_TOKEN_EXT_MAX) { |
514 | 0 | context->max_token_size = (uint32_t)max_token_size; |
515 | 0 | } |
516 | 0 | } |
517 | | |
518 | | void |
519 | | coap_context_set_max_idle_sessions(coap_context_t *context, |
520 | 0 | unsigned int max_idle_sessions) { |
521 | 0 | context->max_idle_sessions = max_idle_sessions; |
522 | 0 | } |
523 | | |
524 | | unsigned int |
525 | 0 | coap_context_get_max_idle_sessions(const coap_context_t *context) { |
526 | 0 | return context->max_idle_sessions; |
527 | 0 | } |
528 | | |
529 | | void |
530 | | coap_context_set_max_handshake_sessions(coap_context_t *context, |
531 | 0 | unsigned int max_handshake_sessions) { |
532 | 0 | context->max_handshake_sessions = max_handshake_sessions; |
533 | 0 | } |
534 | | |
535 | | unsigned int |
536 | 0 | coap_context_get_max_handshake_sessions(const coap_context_t *context) { |
537 | 0 | return context->max_handshake_sessions; |
538 | 0 | } |
539 | | |
540 | | static unsigned int s_csm_timeout = 30; |
541 | | |
542 | | void |
543 | | coap_context_set_csm_timeout(coap_context_t *context, |
544 | 0 | unsigned int csm_timeout) { |
545 | 0 | s_csm_timeout = csm_timeout; |
546 | 0 | coap_context_set_csm_timeout_ms(context, csm_timeout * 1000); |
547 | 0 | } |
548 | | |
549 | | unsigned int |
550 | 0 | coap_context_get_csm_timeout(const coap_context_t *context) { |
551 | 0 | (void)context; |
552 | 0 | return s_csm_timeout; |
553 | 0 | } |
554 | | |
555 | | void |
556 | | coap_context_set_csm_timeout_ms(coap_context_t *context, |
557 | 0 | unsigned int csm_timeout_ms) { |
558 | 0 | if (csm_timeout_ms < 10) |
559 | 0 | csm_timeout_ms = 10; |
560 | 0 | if (csm_timeout_ms > 10000) |
561 | 0 | csm_timeout_ms = 10000; |
562 | 0 | context->csm_timeout_ms = csm_timeout_ms; |
563 | 0 | } |
564 | | |
565 | | unsigned int |
566 | 0 | coap_context_get_csm_timeout_ms(const coap_context_t *context) { |
567 | 0 | return context->csm_timeout_ms; |
568 | 0 | } |
569 | | |
570 | | void |
571 | | coap_context_set_csm_max_message_size(coap_context_t *context, |
572 | 0 | uint32_t csm_max_message_size) { |
573 | 0 | assert(csm_max_message_size >= 64); |
574 | 0 | if (csm_max_message_size > COAP_DEFAULT_MAX_PDU_RX_SIZE) { |
575 | 0 | csm_max_message_size = COAP_DEFAULT_MAX_PDU_RX_SIZE; |
576 | 0 | coap_log_debug("Restricting CSM Max-Message-Size size to %" PRIu32 "\n", |
577 | 0 | csm_max_message_size); |
578 | 0 | } |
579 | |
|
580 | 0 | context->csm_max_message_size = csm_max_message_size; |
581 | 0 | } |
582 | | |
583 | | uint32_t |
584 | 0 | coap_context_get_csm_max_message_size(const coap_context_t *context) { |
585 | 0 | return context->csm_max_message_size; |
586 | 0 | } |
587 | | |
588 | | void |
589 | | coap_context_set_session_timeout(coap_context_t *context, |
590 | 0 | unsigned int session_timeout) { |
591 | 0 | context->session_timeout = session_timeout; |
592 | 0 | } |
593 | | |
594 | | void |
595 | | coap_context_set_session_reconnect_time(coap_context_t *context, |
596 | 0 | unsigned int reconnect_time) { |
597 | 0 | coap_context_set_session_reconnect_time2(context, reconnect_time, 0); |
598 | 0 | } |
599 | | |
600 | | void |
601 | | coap_context_set_session_reconnect_time2(coap_context_t *context, |
602 | | unsigned int reconnect_time, |
603 | 0 | uint8_t retry_count) { |
604 | 0 | #if COAP_CLIENT_SUPPORT |
605 | 0 | context->reconnect_time = reconnect_time; |
606 | 0 | context->retry_count = retry_count; |
607 | | #else /* ! COAP_CLIENT_SUPPORT */ |
608 | | (void)context; |
609 | | (void)reconnect_time; |
610 | | (void)retry_count; |
611 | | #endif /* ! COAP_CLIENT_SUPPORT */ |
612 | 0 | } |
613 | | |
614 | | unsigned int |
615 | 0 | coap_context_get_session_timeout(const coap_context_t *context) { |
616 | 0 | return context->session_timeout; |
617 | 0 | } |
618 | | |
619 | | void |
620 | 0 | coap_context_set_shutdown_no_observe(coap_context_t *context) { |
621 | 0 | #if COAP_SERVER_SUPPORT |
622 | 0 | context->shutdown_no_send_observe = 1; |
623 | | #else /* ! COAP_SERVER_SUPPORT */ |
624 | | (void)context; |
625 | | #endif /* ! COAP_SERVER_SUPPORT */ |
626 | 0 | } |
627 | | |
628 | | int |
629 | 0 | coap_context_get_coap_fd(const coap_context_t *context) { |
630 | 0 | #if COAP_EPOLL_SUPPORT |
631 | 0 | return context->epfd; |
632 | | #else /* ! COAP_EPOLL_SUPPORT */ |
633 | | (void)context; |
634 | | return -1; |
635 | | #endif /* ! COAP_EPOLL_SUPPORT */ |
636 | 0 | } |
637 | | |
638 | | int |
639 | 0 | coap_epoll_is_supported(void) { |
640 | 0 | #if COAP_EPOLL_SUPPORT |
641 | 0 | return 1; |
642 | | #else /* ! COAP_EPOLL_SUPPORT */ |
643 | | return 0; |
644 | | #endif /* ! COAP_EPOLL_SUPPORT */ |
645 | 0 | } |
646 | | |
647 | | int |
648 | 0 | coap_threadsafe_is_supported(void) { |
649 | | #if COAP_THREAD_SAFE |
650 | | return 1; |
651 | | #else /* ! COAP_THREAD_SAFE */ |
652 | 0 | return 0; |
653 | 0 | #endif /* ! COAP_THREAD_SAFE */ |
654 | 0 | } |
655 | | |
656 | | int |
657 | 0 | coap_ipv4_is_supported(void) { |
658 | 0 | #if COAP_IPV4_SUPPORT |
659 | 0 | return 1; |
660 | | #else /* ! COAP_IPV4_SUPPORT */ |
661 | | return 0; |
662 | | #endif /* ! COAP_IPV4_SUPPORT */ |
663 | 0 | } |
664 | | |
665 | | int |
666 | 0 | coap_ipv6_is_supported(void) { |
667 | 0 | #if COAP_IPV6_SUPPORT |
668 | 0 | return 1; |
669 | | #else /* ! COAP_IPV6_SUPPORT */ |
670 | | return 0; |
671 | | #endif /* ! COAP_IPV6_SUPPORT */ |
672 | 0 | } |
673 | | |
674 | | int |
675 | 0 | coap_client_is_supported(void) { |
676 | 0 | #if COAP_CLIENT_SUPPORT |
677 | 0 | return 1; |
678 | | #else /* ! COAP_CLIENT_SUPPORT */ |
679 | | return 0; |
680 | | #endif /* ! COAP_CLIENT_SUPPORT */ |
681 | 0 | } |
682 | | |
683 | | int |
684 | 0 | coap_server_is_supported(void) { |
685 | 0 | #if COAP_SERVER_SUPPORT |
686 | 0 | return 1; |
687 | | #else /* ! COAP_SERVER_SUPPORT */ |
688 | | return 0; |
689 | | #endif /* ! COAP_SERVER_SUPPORT */ |
690 | 0 | } |
691 | | |
692 | | int |
693 | 0 | coap_af_unix_is_supported(void) { |
694 | 0 | #if COAP_AF_UNIX_SUPPORT |
695 | 0 | return 1; |
696 | | #else /* ! COAP_AF_UNIX_SUPPORT */ |
697 | | return 0; |
698 | | #endif /* ! COAP_AF_UNIX_SUPPORT */ |
699 | 0 | } |
700 | | |
701 | | COAP_API void |
702 | 0 | coap_context_set_app_data(coap_context_t *context, void *app_data) { |
703 | 0 | assert(context); |
704 | 0 | coap_lock_lock(return); |
705 | 0 | coap_context_set_app_data2_lkd(context, app_data, NULL); |
706 | 0 | coap_lock_unlock(); |
707 | 0 | } |
708 | | |
709 | | void * |
710 | 0 | coap_context_get_app_data(const coap_context_t *context) { |
711 | 0 | assert(context); |
712 | 0 | return context->app_data; |
713 | 0 | } |
714 | | |
715 | | COAP_API void * |
716 | | coap_context_set_app_data2(coap_context_t *context, void *app_data, |
717 | 0 | coap_app_data_free_callback_t callback) { |
718 | 0 | void *old_data; |
719 | |
|
720 | 0 | coap_lock_lock(return NULL); |
721 | 0 | old_data = coap_context_set_app_data2_lkd(context, app_data, callback); |
722 | 0 | coap_lock_unlock(); |
723 | 0 | return old_data; |
724 | 0 | } |
725 | | |
726 | | void * |
727 | | coap_context_set_app_data2_lkd(coap_context_t *context, void *app_data, |
728 | 0 | coap_app_data_free_callback_t callback) { |
729 | 0 | void *old_data = context->app_data; |
730 | |
|
731 | 0 | context->app_data = app_data; |
732 | 0 | context->app_cb = app_data ? callback : NULL; |
733 | 0 | return old_data; |
734 | 0 | } |
735 | | |
736 | | coap_context_t * |
737 | 32 | coap_new_context(const coap_address_t *listen_addr) { |
738 | 32 | coap_context_t *c; |
739 | | |
740 | | #if ! COAP_SERVER_SUPPORT |
741 | | (void)listen_addr; |
742 | | #endif /* COAP_SERVER_SUPPORT */ |
743 | | |
744 | 32 | if (!coap_started) { |
745 | 0 | coap_startup(); |
746 | 0 | coap_log_warn("coap_startup() should be called before any other " |
747 | 0 | "coap_*() functions are called\n"); |
748 | 0 | } |
749 | | |
750 | 32 | c = coap_malloc_type(COAP_CONTEXT, sizeof(coap_context_t)); |
751 | 32 | if (!c) { |
752 | 0 | coap_log_emerg("coap_init: malloc: failed\n"); |
753 | 0 | return NULL; |
754 | 0 | } |
755 | 32 | memset(c, 0, sizeof(coap_context_t)); |
756 | | |
757 | 32 | coap_lock_lock(coap_free_type(COAP_CONTEXT, c); return NULL); |
758 | 32 | #ifdef COAP_EPOLL_SUPPORT |
759 | 32 | c->epfd = epoll_create1(0); |
760 | 32 | if (c->epfd == -1) { |
761 | 0 | coap_log_err("coap_new_context: Unable to epoll_create: %s (%d)\n", |
762 | 0 | coap_socket_strerror(), |
763 | 0 | errno); |
764 | 0 | goto onerror; |
765 | 0 | } |
766 | 32 | if (c->epfd != -1) { |
767 | 32 | c->eptimerfd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK); |
768 | 32 | if (c->eptimerfd == -1) { |
769 | 0 | coap_log_err("coap_new_context: Unable to timerfd_create: %s (%d)\n", |
770 | 0 | coap_socket_strerror(), |
771 | 0 | errno); |
772 | 0 | goto onerror; |
773 | 32 | } else { |
774 | 32 | int ret; |
775 | 32 | struct epoll_event event; |
776 | | |
777 | | /* Needed if running 32bit as ptr is only 32bit */ |
778 | 32 | memset(&event, 0, sizeof(event)); |
779 | 32 | event.events = EPOLLIN; |
780 | | /* We special case this event by setting to NULL */ |
781 | 32 | event.data.ptr = NULL; |
782 | | |
783 | 32 | ret = epoll_ctl(c->epfd, EPOLL_CTL_ADD, c->eptimerfd, &event); |
784 | 32 | if (ret == -1) { |
785 | 0 | coap_log_err("%s: epoll_ctl ADD failed: %s (%d)\n", |
786 | 0 | "coap_new_context", |
787 | 0 | coap_socket_strerror(), errno); |
788 | 0 | goto onerror; |
789 | 0 | } |
790 | 32 | } |
791 | 32 | } |
792 | 32 | #endif /* COAP_EPOLL_SUPPORT */ |
793 | | |
794 | 32 | if (coap_dtls_is_supported() || coap_tls_is_supported()) { |
795 | 32 | c->dtls_context = coap_dtls_new_context(c); |
796 | 32 | if (!c->dtls_context) { |
797 | 0 | coap_log_emerg("coap_init: no DTLS context available\n"); |
798 | 0 | goto onerror; |
799 | 0 | } |
800 | 32 | } |
801 | | |
802 | | /* set default CSM values */ |
803 | 32 | c->csm_timeout_ms = 1000; |
804 | 32 | c->csm_max_message_size = COAP_DEFAULT_MAX_PDU_RX_SIZE; |
805 | | |
806 | 32 | #if COAP_SERVER_SUPPORT |
807 | 32 | if (listen_addr) { |
808 | 0 | coap_endpoint_t *endpoint = coap_new_endpoint_lkd(c, listen_addr, COAP_PROTO_UDP); |
809 | 0 | if (endpoint == NULL) { |
810 | 0 | goto onerror; |
811 | 0 | } |
812 | 0 | } |
813 | 32 | #endif /* COAP_SERVER_SUPPORT */ |
814 | | |
815 | 32 | c->max_token_size = COAP_TOKEN_DEFAULT_MAX; /* RFC8974 */ |
816 | | |
817 | | #if defined(WITH_LWIP) |
818 | | #if NO_SYS == 0 |
819 | | if (sys_sem_new(&c->coap_io_timeout_sem, 0) != ERR_OK) |
820 | | coap_log_warn("coap_new_context: Failed to set up semaphore\n"); |
821 | | #endif /* NO_SYS == 0 */ |
822 | | #endif /* ! WITH_LWIP */ |
823 | 32 | coap_lock_unlock(); |
824 | 32 | return c; |
825 | | |
826 | 0 | onerror: |
827 | 0 | coap_lock_unlock(); |
828 | 0 | coap_free_type(COAP_CONTEXT, c); |
829 | 0 | return NULL; |
830 | 32 | } |
831 | | |
832 | | COAP_API void |
833 | 0 | coap_set_app_data(coap_context_t *context, void *app_data) { |
834 | 0 | assert(context); |
835 | 0 | coap_lock_lock(return); |
836 | 0 | coap_context_set_app_data2_lkd(context, app_data, NULL); |
837 | 0 | coap_lock_unlock(); |
838 | 0 | } |
839 | | |
840 | | void * |
841 | 0 | coap_get_app_data(const coap_context_t *ctx) { |
842 | 0 | assert(ctx); |
843 | 0 | return ctx->app_data; |
844 | 0 | } |
845 | | |
846 | | COAP_API void |
847 | 32 | coap_free_context(coap_context_t *context) { |
848 | 32 | if (!context) |
849 | 0 | return; |
850 | 32 | coap_lock_lock(return); |
851 | 32 | coap_free_context_lkd(context); |
852 | 32 | coap_lock_unlock(); |
853 | 32 | } |
854 | | |
855 | | void |
856 | 32 | coap_free_context_lkd(coap_context_t *context) { |
857 | 32 | if (!context) |
858 | 0 | return; |
859 | | |
860 | 32 | coap_lock_check_locked(); |
861 | 32 | #if COAP_SERVER_SUPPORT |
862 | | /* Removing a resource may cause a NON unsolicited observe to be sent */ |
863 | 32 | context->context_going_away = 1; |
864 | 32 | if (context->shutdown_no_send_observe) |
865 | 0 | context->observe_no_clear = 1; |
866 | 32 | coap_delete_all_resources(context); |
867 | 32 | #endif /* COAP_SERVER_SUPPORT */ |
868 | 32 | #if COAP_CLIENT_SUPPORT |
869 | | /* Stop any attempts at reconnection */ |
870 | 32 | context->reconnect_time = 0; |
871 | 32 | #endif /* COAP_CLIENT_SUPPORT */ |
872 | | |
873 | 32 | coap_delete_all(context->sendqueue); |
874 | 32 | context->sendqueue = NULL; |
875 | | |
876 | | #ifdef WITH_LWIP |
877 | | if (context->timer_configured) { |
878 | | LOCK_TCPIP_CORE(); |
879 | | sys_untimeout(coap_io_process_timeout, (void *)context); |
880 | | UNLOCK_TCPIP_CORE(); |
881 | | context->timer_configured = 0; |
882 | | } |
883 | | #endif /* WITH_LWIP */ |
884 | | |
885 | 32 | #if COAP_ASYNC_SUPPORT |
886 | 32 | coap_delete_all_async(context); |
887 | 32 | #endif /* COAP_ASYNC_SUPPORT */ |
888 | | |
889 | 32 | #if COAP_SERVER_SUPPORT |
890 | 32 | coap_cache_entry_t *cp, *ctmp; |
891 | 32 | coap_endpoint_t *ep, *tmp; |
892 | | |
893 | 32 | HASH_ITER(hh, context->cache, cp, ctmp) { |
894 | 0 | coap_delete_cache_entry(context, cp); |
895 | 0 | } |
896 | 32 | if (context->cache_ignore_count) { |
897 | 0 | coap_free_type(COAP_STRING, context->cache_ignore_options); |
898 | 0 | } |
899 | | |
900 | 32 | LL_FOREACH_SAFE(context->endpoint, ep, tmp) { |
901 | 0 | coap_free_endpoint_lkd(ep); |
902 | 0 | } |
903 | 32 | #endif /* COAP_SERVER_SUPPORT */ |
904 | | |
905 | 32 | #if COAP_CLIENT_SUPPORT |
906 | 32 | coap_session_t *sp, *rtmp; |
907 | | |
908 | 32 | SESSIONS_ITER_SAFE(context->sessions, sp, rtmp) { |
909 | 32 | coap_session_release_lkd(sp); |
910 | 32 | } |
911 | 32 | #endif /* COAP_CLIENT_SUPPORT */ |
912 | | |
913 | 32 | #if COAP_OSCORE_SUPPORT |
914 | 32 | coap_delete_all_oscore(context); |
915 | 32 | #endif /* COAP_OSCORE_SUPPORT */ |
916 | | |
917 | 32 | if (context->dtls_context) |
918 | 32 | coap_dtls_free_context(context->dtls_context); |
919 | 32 | #ifdef COAP_EPOLL_SUPPORT |
920 | 32 | if (context->eptimerfd != -1) { |
921 | 32 | int ret; |
922 | 32 | struct epoll_event event; |
923 | | |
924 | | /* Kernels prior to 2.6.9 expect non NULL event parameter */ |
925 | 32 | ret = epoll_ctl(context->epfd, EPOLL_CTL_DEL, context->eptimerfd, &event); |
926 | 32 | if (ret == -1) { |
927 | 0 | coap_log_err("%s: epoll_ctl DEL failed: %s (%d)\n", |
928 | 0 | "coap_free_context", |
929 | 0 | coap_socket_strerror(), errno); |
930 | 0 | } |
931 | 32 | close(context->eptimerfd); |
932 | 32 | context->eptimerfd = -1; |
933 | 32 | } |
934 | 32 | if (context->epfd != -1) { |
935 | 32 | close(context->epfd); |
936 | 32 | context->epfd = -1; |
937 | 32 | } |
938 | 32 | #endif /* COAP_EPOLL_SUPPORT */ |
939 | 32 | #if COAP_SERVER_SUPPORT |
940 | 32 | #if COAP_WITH_OBSERVE_PERSIST |
941 | 32 | coap_persist_cleanup(context); |
942 | 32 | #endif /* COAP_WITH_OBSERVE_PERSIST */ |
943 | 32 | #endif /* COAP_SERVER_SUPPORT */ |
944 | 32 | #if COAP_PROXY_SUPPORT |
945 | 32 | coap_proxy_cleanup(context); |
946 | 32 | #endif /* COAP_PROXY_SUPPORT */ |
947 | | |
948 | 32 | if (context->app_cb) { |
949 | 0 | coap_lock_callback(context->app_cb(context->app_data)); |
950 | 0 | } |
951 | | #if defined(WITH_LWIP) |
952 | | #if NO_SYS == 0 |
953 | | sys_sem_free(&context->coap_io_timeout_sem); |
954 | | #endif /* NO_SYS == 0 */ |
955 | | #endif /* ! WITH_LWIP */ |
956 | | #if COAP_THREAD_SAFE && !WITH_LWIP |
957 | | coap_io_process_remove_threads_lkd(context); |
958 | | #endif /* COAP_THREAD_SAFE && !WITH_LWIP */ |
959 | 32 | coap_free_type(COAP_CONTEXT, context); |
960 | 32 | coap_dump_memory_type_counts(COAP_LOG_DEBUG); |
961 | 32 | } |
962 | | |
963 | | static coap_crit_type_t |
964 | 191 | coap_is_session_proxy(coap_session_t *session, coap_pdu_t *pdu) { |
965 | 191 | #if COAP_SERVER_SUPPORT |
966 | 191 | coap_opt_iterator_t t_iter; |
967 | 191 | coap_opt_t *proxy_uri = NULL; |
968 | 191 | coap_opt_t *proxy_scheme = NULL; |
969 | | |
970 | 191 | if (session->proxy_session) { |
971 | 0 | return COAP_CRIT_PROXY; |
972 | 191 | } else if (COAP_PDU_IS_REQUEST(pdu) && session->context->unknown_resource && |
973 | 0 | session->context->unknown_resource->is_reverse_proxy) { |
974 | 0 | return COAP_CRIT_PROXY; |
975 | 191 | } else if (COAP_PDU_IS_REQUEST(pdu) && session->context->proxy_uri_resource && |
976 | 0 | ((proxy_uri = coap_check_option(pdu, COAP_OPTION_PROXY_URI, &t_iter)) || |
977 | 0 | (proxy_scheme = coap_check_option(pdu, COAP_OPTION_PROXY_SCHEME, &t_iter)))) { |
978 | 0 | if (proxy_uri || proxy_scheme) { |
979 | 0 | coap_uri_t uri; |
980 | | |
981 | | /* Duplicates some of the code in handle_request() */ |
982 | 0 | if (proxy_uri) { |
983 | 0 | if (coap_split_proxy_uri(coap_opt_value(proxy_uri), |
984 | 0 | coap_opt_length(proxy_uri), &uri) < 0) { |
985 | 0 | return COAP_CRIT_PROXY; |
986 | 0 | } |
987 | 0 | } else { |
988 | 0 | coap_opt_t *opt; |
989 | 0 | coap_resource_t *resource; |
990 | |
|
991 | 0 | memset(&uri, 0, sizeof(uri)); |
992 | 0 | opt = coap_check_option(pdu, COAP_OPTION_URI_HOST, &t_iter); |
993 | 0 | if (opt) { |
994 | 0 | uri.host.length = coap_opt_length(opt); |
995 | 0 | uri.host.s = coap_opt_value(opt); |
996 | 0 | } else { |
997 | 0 | uri.host.length = 0; |
998 | 0 | } |
999 | | /* See if we are the endpoint */ |
1000 | 0 | resource = session->context->proxy_uri_resource; |
1001 | 0 | if (uri.host.length && resource->proxy_name_count && |
1002 | 0 | resource->proxy_name_list) { |
1003 | 0 | size_t i; |
1004 | |
|
1005 | 0 | if (resource->proxy_name_count == 1 && |
1006 | 0 | resource->proxy_name_list[0]->length == 0) { |
1007 | | /* If proxy_name_list[0] is zero length, then this is the endpoint */ |
1008 | 0 | i = 0; |
1009 | 0 | } else { |
1010 | 0 | for (i = 0; i < resource->proxy_name_count; i++) { |
1011 | 0 | if (coap_string_equal(&uri.host, resource->proxy_name_list[i])) { |
1012 | 0 | break; |
1013 | 0 | } |
1014 | 0 | } |
1015 | 0 | } |
1016 | 0 | if (i != resource->proxy_name_count) { |
1017 | 0 | return COAP_CRIT_NOT_PROXY; |
1018 | 0 | } |
1019 | 0 | } |
1020 | 0 | } |
1021 | 0 | return COAP_CRIT_PROXY; |
1022 | 0 | } |
1023 | 0 | } |
1024 | 191 | return COAP_CRIT_NOT_PROXY; |
1025 | | #else /* ! COAP_SERVER_SUPPORT */ |
1026 | | #endif /* ! COAP_SERVER_SUPPORT */ |
1027 | 0 | (void)session; |
1028 | 0 | (void)pdu; |
1029 | 0 | return COAP_CRIT_NOT_PROXY; |
1030 | 191 | } |
1031 | | |
1032 | | int |
1033 | | coap_option_check_critical(coap_session_t *session, |
1034 | | coap_pdu_t *pdu, |
1035 | | coap_opt_filter_t *unknown, |
1036 | 198 | coap_crit_type_t is_proxy) { |
1037 | 198 | coap_context_t *ctx = session->context; |
1038 | 198 | coap_opt_iterator_t opt_iter; |
1039 | 198 | int ok = 1; |
1040 | 198 | coap_option_num_t last_number = -1; |
1041 | | |
1042 | 198 | coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); |
1043 | | |
1044 | 579 | while (coap_option_next(&opt_iter)) { |
1045 | | /* Check for explicitly reserved option RFC 5272 12.2 Table 7 */ |
1046 | | /* Need to check reserved options */ |
1047 | 381 | switch (opt_iter.number) { |
1048 | 4 | case 0: |
1049 | 4 | case 128: |
1050 | 4 | case 132: |
1051 | 4 | case 136: |
1052 | 4 | case 140: |
1053 | 4 | if (coap_option_filter_get(&ctx->known_options, opt_iter.number) <= 0) { |
1054 | 4 | coap_log_debug("Unknown reserved option %d\n", opt_iter.number); |
1055 | 4 | ok = 0; |
1056 | | |
1057 | | /* When opt_iter.number cannot be set in unknown, all of the appropriate |
1058 | | * slots have been used up and no more options can be tracked. |
1059 | | * Safe to break out of this loop as ok is already set. */ |
1060 | 4 | if (coap_option_filter_set(unknown, opt_iter.number) == 0) { |
1061 | 0 | goto overflow; |
1062 | 0 | } |
1063 | 4 | } |
1064 | 4 | break; |
1065 | 377 | default: |
1066 | 377 | break; |
1067 | 381 | } |
1068 | 381 | if (opt_iter.number & 0x01) { |
1069 | | /* first check the known built-in critical options */ |
1070 | 237 | switch (opt_iter.number) { |
1071 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1072 | 3 | case COAP_OPTION_Q_BLOCK1: |
1073 | 3 | case COAP_OPTION_Q_BLOCK2: |
1074 | 3 | if (!(ctx->block_mode & COAP_BLOCK_TRY_Q_BLOCK)) { |
1075 | 3 | coap_log_debug("Critical option '%s' (%d) disabled - not supported\n", |
1076 | 3 | coap_option_string(pdu->code, opt_iter.number), opt_iter.number); |
1077 | 3 | ok = 0; |
1078 | | /* When opt_iter.number cannot be set in unknown, all of the appropriate |
1079 | | * slots have been used up and no more options can be tracked. |
1080 | | * Safe to break out of this loop as ok is already set. */ |
1081 | 3 | if (coap_option_filter_set(unknown, opt_iter.number) == 0) { |
1082 | 0 | goto overflow; |
1083 | 0 | } |
1084 | 3 | } |
1085 | 3 | break; |
1086 | 3 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1087 | 3 | case COAP_OPTION_IF_MATCH: |
1088 | 6 | case COAP_OPTION_URI_HOST: |
1089 | 6 | case COAP_OPTION_IF_NONE_MATCH: |
1090 | 7 | case COAP_OPTION_URI_PORT: |
1091 | 225 | case COAP_OPTION_URI_PATH: |
1092 | 226 | case COAP_OPTION_URI_PATH_ABB: |
1093 | 230 | case COAP_OPTION_URI_QUERY: |
1094 | 232 | case COAP_OPTION_ACCEPT: |
1095 | 234 | case COAP_OPTION_BLOCK2: |
1096 | 234 | case COAP_OPTION_BLOCK1: |
1097 | 234 | case COAP_OPTION_PROXY_URI: |
1098 | 234 | case COAP_OPTION_PROXY_SCHEME: |
1099 | 234 | break; |
1100 | 0 | case COAP_OPTION_OSCORE: |
1101 | | /* Valid critical if doing OSCORE */ |
1102 | 0 | #if COAP_OSCORE_SUPPORT |
1103 | | /* Generally configured or has coap oscore enabled helper function */ |
1104 | 0 | if (ctx->p_osc_ctx || ctx->oscore_find_cb) |
1105 | 0 | break; |
1106 | 0 | #endif /* COAP_OSCORE_SUPPORT */ |
1107 | | /* Fall Through */ |
1108 | 0 | default: |
1109 | 0 | if (coap_option_filter_get(&ctx->known_options, opt_iter.number) <= 0) { |
1110 | 0 | #if COAP_SERVER_SUPPORT |
1111 | 0 | if ((opt_iter.number & 0x02) == 0) { |
1112 | | /* Safe to forward critical? - check if proxy pdu */ |
1113 | 0 | if (is_proxy == COAP_CRIT_UNKNOWN) { |
1114 | 0 | is_proxy = coap_is_session_proxy(session, pdu); |
1115 | 0 | } |
1116 | 0 | if (is_proxy == COAP_CRIT_PROXY) { |
1117 | 0 | pdu->crit_opt = 1; |
1118 | 0 | break; |
1119 | 0 | } |
1120 | 0 | } |
1121 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
1122 | 0 | coap_log_debug("Critical option %u dropped\n", opt_iter.number); |
1123 | 0 | ok = 0; |
1124 | | |
1125 | | /* When opt_iter.number cannot be set in unknown, all of the appropriate |
1126 | | * slots have been used up and no more options can be tracked. |
1127 | | * Safe to break out of this loop as ok is already set. */ |
1128 | 0 | if (coap_option_filter_set(unknown, opt_iter.number) == 0) { |
1129 | 0 | goto overflow; |
1130 | 0 | } |
1131 | 0 | } |
1132 | 237 | } |
1133 | 237 | } |
1134 | 381 | if (opt_iter.number & 0x02) { |
1135 | | /* Check for safe to forward for a proxy */ |
1136 | 366 | if (is_proxy == COAP_CRIT_UNKNOWN) { |
1137 | 191 | is_proxy = coap_is_session_proxy(session, pdu); |
1138 | 191 | } |
1139 | 366 | if (is_proxy == COAP_CRIT_PROXY) { |
1140 | 0 | switch (opt_iter.number) { |
1141 | 0 | case COAP_OPTION_URI_HOST: |
1142 | 0 | case COAP_OPTION_OBSERVE: |
1143 | 0 | case COAP_OPTION_URI_PORT: |
1144 | 0 | case COAP_OPTION_URI_PATH: |
1145 | 0 | case COAP_OPTION_MAXAGE: |
1146 | 0 | case COAP_OPTION_URI_QUERY: |
1147 | 0 | case COAP_OPTION_Q_BLOCK1: |
1148 | 0 | case COAP_OPTION_BLOCK2: |
1149 | 0 | case COAP_OPTION_BLOCK1: |
1150 | 0 | case COAP_OPTION_Q_BLOCK2: |
1151 | 0 | case COAP_OPTION_PROXY_URI: |
1152 | 0 | case COAP_OPTION_PROXY_SCHEME: |
1153 | 0 | break; |
1154 | 0 | default: |
1155 | 0 | coap_log_debug("Not Safe option %u cannot be forwarded - dropped\n", |
1156 | 0 | opt_iter.number); |
1157 | 0 | ok = 0; |
1158 | | |
1159 | | /* When opt_iter.number cannot be set in unknown, all of the appropriate |
1160 | | * slots have been used up and no more options can be tracked. |
1161 | | * Safe to break out of this loop as ok is already set. */ |
1162 | 0 | if (coap_option_filter_set(unknown, opt_iter.number) == 0) { |
1163 | 0 | goto overflow; |
1164 | 0 | } |
1165 | 0 | } |
1166 | 0 | } |
1167 | 366 | } |
1168 | 381 | if (last_number == opt_iter.number) { |
1169 | | /* Check for duplicated option RFC 5272 5.4.5 */ |
1170 | 33 | if (!coap_option_check_repeatable(pdu, opt_iter.number)) { |
1171 | 2 | if (coap_option_filter_get(&ctx->known_options, opt_iter.number) <= 0) { |
1172 | 2 | ok = 0; |
1173 | 2 | if (coap_option_filter_set(unknown, opt_iter.number) == 0) { |
1174 | 0 | goto overflow; |
1175 | 0 | } |
1176 | 2 | } |
1177 | 2 | } |
1178 | 348 | } else if (opt_iter.number == COAP_OPTION_BLOCK2 && |
1179 | 2 | COAP_PDU_IS_REQUEST(pdu)) { |
1180 | | /* Check the M Bit is not set on a GET request RFC 7959 2.2 */ |
1181 | 2 | coap_block_b_t block; |
1182 | | |
1183 | 2 | if (coap_get_block_b(session, pdu, opt_iter.number, &block)) { |
1184 | 2 | if (block.m) { |
1185 | 0 | size_t used_size = pdu->used_size; |
1186 | 0 | unsigned char buf[4]; |
1187 | |
|
1188 | 0 | coap_log_debug("Option Block2 has invalid set M bit - cleared\n"); |
1189 | 0 | block.m = 0; |
1190 | 0 | coap_update_option(pdu, opt_iter.number, |
1191 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1192 | 0 | ((block.num << 4) | |
1193 | 0 | (block.m << 3) | |
1194 | 0 | block.aszx)), |
1195 | 0 | buf); |
1196 | 0 | if (used_size != pdu->used_size) { |
1197 | | /* Unfortunately need to restart the scan */ |
1198 | 0 | coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); |
1199 | 0 | last_number = -1; |
1200 | 0 | continue; |
1201 | 0 | } |
1202 | 0 | } |
1203 | 2 | } |
1204 | 2 | } |
1205 | 381 | last_number = opt_iter.number; |
1206 | 381 | } |
1207 | 198 | overflow: |
1208 | 198 | return ok; |
1209 | 198 | } |
1210 | | |
1211 | | COAP_API coap_mid_t |
1212 | 0 | coap_send_rst(coap_session_t *session, const coap_pdu_t *request) { |
1213 | 0 | coap_mid_t mid; |
1214 | |
|
1215 | 0 | coap_lock_lock(return COAP_INVALID_MID); |
1216 | 0 | mid = coap_send_rst_lkd(session, request); |
1217 | 0 | coap_lock_unlock(); |
1218 | 0 | return mid; |
1219 | 0 | } |
1220 | | |
1221 | | coap_mid_t |
1222 | 16 | coap_send_rst_lkd(coap_session_t *session, const coap_pdu_t *request) { |
1223 | 16 | if (request->type == COAP_MESSAGE_CON || request->type == COAP_MESSAGE_NON) |
1224 | 13 | return coap_send_message_type_lkd(session, request, COAP_MESSAGE_RST); |
1225 | 3 | return COAP_INVALID_MID; |
1226 | 16 | } |
1227 | | |
1228 | | COAP_API coap_mid_t |
1229 | 0 | coap_send_ack(coap_session_t *session, const coap_pdu_t *request) { |
1230 | 0 | coap_mid_t mid; |
1231 | |
|
1232 | 0 | coap_lock_lock(return COAP_INVALID_MID); |
1233 | 0 | mid = coap_send_ack_lkd(session, request); |
1234 | 0 | coap_lock_unlock(); |
1235 | 0 | return mid; |
1236 | 0 | } |
1237 | | |
1238 | | coap_mid_t |
1239 | 0 | coap_send_ack_lkd(coap_session_t *session, const coap_pdu_t *request) { |
1240 | 0 | coap_pdu_t *response; |
1241 | 0 | coap_mid_t result = COAP_INVALID_MID; |
1242 | |
|
1243 | 0 | coap_lock_check_locked(); |
1244 | 0 | if (request && request->type == COAP_MESSAGE_CON && |
1245 | 0 | COAP_PROTO_NOT_RELIABLE(session->proto)) { |
1246 | 0 | response = coap_pdu_init(COAP_MESSAGE_ACK, 0, request->mid, 0); |
1247 | 0 | if (response) |
1248 | 0 | result = coap_send_internal(session, response, NULL); |
1249 | 0 | } |
1250 | 0 | return result; |
1251 | 0 | } |
1252 | | |
1253 | | ssize_t |
1254 | 218 | coap_session_send_pdu(coap_session_t *session, coap_pdu_t *pdu) { |
1255 | 218 | ssize_t bytes_written = -1; |
1256 | 218 | assert(pdu->hdr_size > 0); |
1257 | | |
1258 | | /* Caller handles partial writes */ |
1259 | 218 | bytes_written = session->sock.lfunc[COAP_LAYER_SESSION].l_write(session, |
1260 | 218 | pdu->token - pdu->hdr_size, |
1261 | 218 | pdu->used_size + pdu->hdr_size); |
1262 | 218 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
1263 | 218 | return bytes_written; |
1264 | 218 | } |
1265 | | |
1266 | | static ssize_t |
1267 | 218 | coap_send_pdu(coap_session_t *session, coap_pdu_t *pdu, coap_queue_t *node) { |
1268 | 218 | ssize_t bytes_written; |
1269 | | |
1270 | 218 | if (session->state == COAP_SESSION_STATE_NONE) { |
1271 | | #if ! COAP_CLIENT_SUPPORT |
1272 | | return -1; |
1273 | | #else /* COAP_CLIENT_SUPPORT */ |
1274 | 0 | if (session->type != COAP_SESSION_TYPE_CLIENT) |
1275 | 0 | return -1; |
1276 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
1277 | 0 | } |
1278 | | |
1279 | 218 | if (pdu->type == COAP_MESSAGE_CON && |
1280 | 0 | (session->sock.flags & COAP_SOCKET_NOT_EMPTY) && |
1281 | 0 | coap_is_mcast(&session->addr_info.remote)) { |
1282 | | /* Violates RFC72522 8.1 */ |
1283 | 0 | coap_log_err("Multicast requests cannot be Confirmable (RFC7252 8.1)\n"); |
1284 | 0 | return -1; |
1285 | 0 | } |
1286 | | |
1287 | 218 | if (session->state != COAP_SESSION_STATE_ESTABLISHED || |
1288 | 218 | (pdu->type == COAP_MESSAGE_CON && |
1289 | 0 | session->con_active >= COAP_NSTART(session))) { |
1290 | 0 | return coap_session_delay_pdu(session, pdu, node); |
1291 | 0 | } |
1292 | | |
1293 | 218 | if ((session->sock.flags & COAP_SOCKET_NOT_EMPTY) && |
1294 | 57 | (session->sock.flags & COAP_SOCKET_WANT_WRITE)) |
1295 | 0 | return coap_session_delay_pdu(session, pdu, node); |
1296 | | |
1297 | 218 | bytes_written = coap_session_send_pdu(session, pdu); |
1298 | 218 | if (bytes_written >= 0 && pdu->type == COAP_MESSAGE_CON && |
1299 | 0 | COAP_PROTO_NOT_RELIABLE(session->proto)) |
1300 | 0 | session->con_active++; |
1301 | | |
1302 | 218 | return bytes_written; |
1303 | 218 | } |
1304 | | |
1305 | | COAP_API coap_mid_t |
1306 | | coap_send_error(coap_session_t *session, |
1307 | | const coap_pdu_t *request, |
1308 | | coap_pdu_code_t code, |
1309 | 0 | coap_opt_filter_t *opts) { |
1310 | 0 | coap_mid_t mid; |
1311 | |
|
1312 | 0 | coap_lock_lock(return COAP_INVALID_MID); |
1313 | 0 | mid = coap_send_error_lkd(session, request, code, opts); |
1314 | 0 | coap_lock_unlock(); |
1315 | 0 | return mid; |
1316 | 0 | } |
1317 | | |
1318 | | coap_mid_t |
1319 | | coap_send_error_lkd(coap_session_t *session, |
1320 | | const coap_pdu_t *request, |
1321 | | coap_pdu_code_t code, |
1322 | 0 | coap_opt_filter_t *opts) { |
1323 | 0 | coap_pdu_t *response; |
1324 | 0 | coap_mid_t result = COAP_INVALID_MID; |
1325 | |
|
1326 | 0 | assert(request); |
1327 | 0 | assert(session); |
1328 | | |
1329 | 0 | response = coap_new_error_response(request, code, opts); |
1330 | 0 | if (response) |
1331 | 0 | result = coap_send_internal(session, response, NULL); |
1332 | |
|
1333 | 0 | return result; |
1334 | 0 | } |
1335 | | |
1336 | | COAP_API coap_mid_t |
1337 | | coap_send_message_type(coap_session_t *session, const coap_pdu_t *request, |
1338 | 0 | coap_pdu_type_t type) { |
1339 | 0 | coap_mid_t mid; |
1340 | |
|
1341 | 0 | coap_lock_lock(return COAP_INVALID_MID); |
1342 | 0 | mid = coap_send_message_type_lkd(session, request, type); |
1343 | 0 | coap_lock_unlock(); |
1344 | 0 | return mid; |
1345 | 0 | } |
1346 | | |
1347 | | coap_mid_t |
1348 | | coap_send_message_type_lkd(coap_session_t *session, const coap_pdu_t *request, |
1349 | 14 | coap_pdu_type_t type) { |
1350 | 14 | coap_pdu_t *response; |
1351 | 14 | coap_mid_t result = COAP_INVALID_MID; |
1352 | | |
1353 | 14 | coap_lock_check_locked(); |
1354 | 14 | if (request && COAP_PROTO_NOT_RELIABLE(session->proto) && |
1355 | 14 | !(type == COAP_MESSAGE_RST && coap_is_mcast(&session->addr_info.local))) { |
1356 | 14 | response = coap_pdu_init(type, 0, request->mid, 0); |
1357 | 14 | if (response) |
1358 | 14 | result = coap_send_internal(session, response, NULL); |
1359 | 14 | } |
1360 | 14 | return result; |
1361 | 14 | } |
1362 | | |
1363 | | /** |
1364 | | * Calculates the initial timeout based on the session CoAP transmission |
1365 | | * parameters 'ack_timeout', 'ack_random_factor', and COAP_TICKS_PER_SECOND. |
1366 | | * The calculation requires 'ack_timeout' and 'ack_random_factor' to be in |
1367 | | * Qx.FRAC_BITS fixed point notation, whereas the passed parameter @p r |
1368 | | * is interpreted as the fractional part of a Q0.MAX_BITS random value. |
1369 | | * |
1370 | | * @param session session timeout is associated with |
1371 | | * @param r random value as fractional part of a Q0.MAX_BITS fixed point |
1372 | | * value |
1373 | | * @return COAP_TICKS_PER_SECOND * 'ack_timeout' * |
1374 | | * (1 + ('ack_random_factor' - 1) * r) |
1375 | | */ |
1376 | | unsigned int |
1377 | 0 | coap_calc_timeout(coap_session_t *session, unsigned char r) { |
1378 | 0 | unsigned int result; |
1379 | | |
1380 | | /* The integer 1.0 as a Qx.FRAC_BITS */ |
1381 | 0 | #define FP1 Q(FRAC_BITS, ((coap_fixed_point_t){1,0})) |
1382 | | |
1383 | | /* rounds val up and right shifts by frac positions */ |
1384 | 0 | #define SHR_FP(val,frac) (((val) + (1 << ((frac) - 1))) >> (frac)) |
1385 | | |
1386 | | /* Inner term: multiply ACK_RANDOM_FACTOR by Q0.MAX_BITS[r] and |
1387 | | * make the result a rounded Qx.FRAC_BITS */ |
1388 | 0 | result = SHR_FP((ACK_RANDOM_FACTOR - FP1) * r, MAX_BITS); |
1389 | | |
1390 | | /* Add 1 to the inner term and multiply with ACK_TIMEOUT, then |
1391 | | * make the result a rounded Qx.FRAC_BITS */ |
1392 | 0 | result = SHR_FP(((result + FP1) * ACK_TIMEOUT), FRAC_BITS); |
1393 | | |
1394 | | /* Multiply with COAP_TICKS_PER_SECOND to yield system ticks |
1395 | | * (yields a Qx.FRAC_BITS) and shift to get an integer */ |
1396 | 0 | return SHR_FP((COAP_TICKS_PER_SECOND * result), FRAC_BITS); |
1397 | |
|
1398 | 0 | #undef FP1 |
1399 | 0 | #undef SHR_FP |
1400 | 0 | } |
1401 | | |
1402 | | coap_mid_t |
1403 | | coap_wait_ack(coap_context_t *context, coap_session_t *session, |
1404 | 0 | coap_queue_t *node) { |
1405 | 0 | coap_tick_t now; |
1406 | |
|
1407 | 0 | node->session = coap_session_reference_lkd(session); |
1408 | | |
1409 | | /* Set timer for pdu retransmission. If this is the first element in |
1410 | | * the retransmission queue, the base time is set to the current |
1411 | | * time and the retransmission time is node->timeout. If there is |
1412 | | * already an entry in the sendqueue, we must check if this node is |
1413 | | * to be retransmitted earlier. Therefore, node->timeout is first |
1414 | | * normalized to the base time and then inserted into the queue with |
1415 | | * an adjusted relative time. |
1416 | | */ |
1417 | 0 | coap_ticks(&now); |
1418 | 0 | if (context->sendqueue == NULL) { |
1419 | 0 | node->t = node->timeout << node->retransmit_cnt; |
1420 | 0 | context->sendqueue_basetime = now; |
1421 | 0 | } else { |
1422 | | /* make node->t relative to context->sendqueue_basetime */ |
1423 | 0 | node->t = (now - context->sendqueue_basetime) + |
1424 | 0 | (node->timeout << node->retransmit_cnt); |
1425 | 0 | } |
1426 | 0 | coap_address_copy(&node->remote, &session->addr_info.remote); |
1427 | |
|
1428 | 0 | coap_insert_node(&context->sendqueue, node); |
1429 | |
|
1430 | 0 | coap_log_debug("** %s: mid=0x%04x: added to retransmit queue (%ums)\n", |
1431 | 0 | coap_session_str(node->session), node->id, |
1432 | 0 | (unsigned)((node->timeout << node->retransmit_cnt) * 1000 / |
1433 | 0 | COAP_TICKS_PER_SECOND)); |
1434 | |
|
1435 | 0 | coap_update_io_timer(context, node->t); |
1436 | |
|
1437 | 0 | return node->id; |
1438 | 0 | } |
1439 | | |
1440 | | #if COAP_CLIENT_SUPPORT |
1441 | | /* |
1442 | | * Sent out a test PDU for Extended Token |
1443 | | */ |
1444 | | static coap_mid_t |
1445 | 0 | coap_send_test_extended_token(coap_session_t *session) { |
1446 | 0 | coap_pdu_t *pdu; |
1447 | 0 | coap_mid_t mid = COAP_INVALID_MID; |
1448 | 0 | size_t i; |
1449 | 0 | coap_binary_t *token; |
1450 | 0 | coap_lg_crcv_t *lg_crcv; |
1451 | |
|
1452 | 0 | coap_log_debug("Testing for Extended Token support\n"); |
1453 | | /* https://rfc-editor.org/rfc/rfc8974#section-2.2.2 */ |
1454 | 0 | pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, |
1455 | 0 | coap_new_message_id_lkd(session), |
1456 | 0 | coap_session_max_pdu_size_lkd(session)); |
1457 | 0 | if (!pdu) |
1458 | 0 | return COAP_INVALID_MID; |
1459 | | |
1460 | 0 | token = coap_new_binary(session->max_token_size); |
1461 | 0 | if (token == NULL) { |
1462 | 0 | coap_delete_pdu_lkd(pdu); |
1463 | 0 | return COAP_INVALID_MID; |
1464 | 0 | } |
1465 | 0 | for (i = 0; i < session->max_token_size; i++) { |
1466 | 0 | token->s[i] = (uint8_t)(i + 1); |
1467 | 0 | } |
1468 | 0 | coap_add_token(pdu, session->max_token_size, token->s); |
1469 | 0 | coap_delete_binary(token); |
1470 | |
|
1471 | 0 | coap_delete_bin_const(session->last_token); |
1472 | 0 | session->last_token = coap_new_bin_const(pdu->actual_token.s, |
1473 | 0 | pdu->actual_token.length); |
1474 | |
|
1475 | 0 | coap_insert_option(pdu, COAP_OPTION_IF_NONE_MATCH, 0, NULL); |
1476 | |
|
1477 | 0 | session->max_token_checked = COAP_EXT_T_CHECKING; /* Checking out this one */ |
1478 | | |
1479 | | /* Need to track in case OSCORE / Echo etc. comes back after non-piggy-backed ACK */ |
1480 | 0 | lg_crcv = coap_block_new_lg_crcv(session, pdu, NULL); |
1481 | 0 | if (lg_crcv) { |
1482 | 0 | LL_PREPEND(session->lg_crcv, lg_crcv); |
1483 | 0 | } |
1484 | 0 | mid = coap_send_internal(session, pdu, NULL); |
1485 | 0 | if (mid == COAP_INVALID_MID) |
1486 | 0 | return COAP_INVALID_MID; |
1487 | 0 | session->remote_test_mid = mid; |
1488 | 0 | return mid; |
1489 | 0 | } |
1490 | | #endif /* COAP_CLIENT_SUPPORT */ |
1491 | | |
1492 | | /* |
1493 | | * Return: 0 Something failed |
1494 | | * 1 Success |
1495 | | */ |
1496 | | int |
1497 | 0 | coap_client_delay_first(coap_session_t *session) { |
1498 | 0 | #if COAP_CLIENT_SUPPORT |
1499 | 0 | if (session->type == COAP_SESSION_TYPE_CLIENT && session->doing_first) { |
1500 | 0 | int timeout_ms = 5000; |
1501 | 0 | coap_session_state_t current_state = session->state; |
1502 | |
|
1503 | 0 | if (session->delay_recursive) { |
1504 | 0 | return 0; |
1505 | 0 | } else { |
1506 | 0 | session->delay_recursive = 1; |
1507 | 0 | } |
1508 | | /* |
1509 | | * Need to wait for first request to get out and response back before |
1510 | | * continuing.. Response handler has to clear doing_first if not an error. |
1511 | | */ |
1512 | 0 | coap_session_reference_lkd(session); |
1513 | 0 | while (session->doing_first != 0) { |
1514 | 0 | int result = coap_io_process_lkd(session->context, 1000); |
1515 | |
|
1516 | 0 | if (result < 0) { |
1517 | 0 | coap_reset_doing_first(session); |
1518 | 0 | session->delay_recursive = 0; |
1519 | 0 | coap_session_release_lkd(session); |
1520 | 0 | return 0; |
1521 | 0 | } |
1522 | | |
1523 | | /* coap_io_process_lkd() may have updated session state */ |
1524 | 0 | if (session->state == COAP_SESSION_STATE_CSM && |
1525 | 0 | current_state != COAP_SESSION_STATE_CSM) { |
1526 | | /* Update timeout and restart the clock for CSM timeout */ |
1527 | 0 | current_state = COAP_SESSION_STATE_CSM; |
1528 | 0 | timeout_ms = session->context->csm_timeout_ms; |
1529 | 0 | result = 0; |
1530 | 0 | } |
1531 | |
|
1532 | 0 | if (result < timeout_ms) { |
1533 | 0 | timeout_ms -= result; |
1534 | 0 | } else { |
1535 | 0 | if (session->doing_first == 1) { |
1536 | | /* Timeout failure of some sort with first request */ |
1537 | 0 | if (session->state == COAP_SESSION_STATE_CSM) { |
1538 | 0 | coap_log_debug("** %s: timeout waiting for CSM response\n", |
1539 | 0 | coap_session_str(session)); |
1540 | 0 | session->csm_not_seen = 1; |
1541 | 0 | } else { |
1542 | 0 | coap_log_debug("** %s: timeout waiting for first response\n", |
1543 | 0 | coap_session_str(session)); |
1544 | 0 | } |
1545 | 0 | coap_reset_doing_first(session); |
1546 | 0 | coap_session_connected(session); |
1547 | 0 | } |
1548 | 0 | } |
1549 | 0 | } |
1550 | 0 | session->delay_recursive = 0; |
1551 | 0 | coap_session_release_lkd(session); |
1552 | 0 | } |
1553 | | #else /* ! COAP_CLIENT_SUPPORT */ |
1554 | | (void)session; |
1555 | | #endif /* ! COAP_CLIENT_SUPPORT */ |
1556 | 0 | return 1; |
1557 | 0 | } |
1558 | | |
1559 | | /* |
1560 | | * return 0 Invalid |
1561 | | * 1 Valid |
1562 | | */ |
1563 | | int |
1564 | 277 | coap_check_code_class(coap_session_t *session, coap_pdu_t *pdu) { |
1565 | | |
1566 | | /* Check validity of sending code */ |
1567 | 277 | switch (COAP_RESPONSE_CLASS(pdu->code)) { |
1568 | 98 | case 0: /* Empty or request */ |
1569 | 276 | case 2: /* Success */ |
1570 | 276 | case 3: /* Reserved for future use */ |
1571 | 277 | case 4: /* Client error */ |
1572 | 277 | case 5: /* Server error */ |
1573 | 277 | break; |
1574 | 0 | case 7: /* Reliable signalling */ |
1575 | 0 | if (COAP_PROTO_RELIABLE(session->proto)) |
1576 | 0 | break; |
1577 | | /* Not valid if UDP */ |
1578 | | /* Fall through */ |
1579 | 0 | case 1: /* Invalid */ |
1580 | 0 | case 6: /* Invalid */ |
1581 | 0 | default: |
1582 | 0 | return 0; |
1583 | 277 | } |
1584 | 277 | return 1; |
1585 | 277 | } |
1586 | | |
1587 | | #if COAP_CLIENT_SUPPORT |
1588 | | /* |
1589 | | * If type is CON and protocol is not reliable, there is no need to set up |
1590 | | * lg_crcv if it can be built up based on sent PDU if there is a |
1591 | | * (Q-)Block2 in the response. However, still need it for Observe, Oscore and |
1592 | | * (Q-)Block1. |
1593 | | */ |
1594 | | static int |
1595 | 0 | coap_check_send_need_lg_crcv(coap_session_t *session, coap_pdu_t *pdu) { |
1596 | 0 | coap_opt_iterator_t opt_iter; |
1597 | |
|
1598 | 0 | if (!COAP_PDU_IS_REQUEST(pdu)) |
1599 | 0 | return 0; |
1600 | | |
1601 | 0 | if ( |
1602 | 0 | #if COAP_OSCORE_SUPPORT |
1603 | 0 | session->oscore_encryption || |
1604 | 0 | #endif /* COAP_OSCORE_SUPPORT */ |
1605 | 0 | pdu->type == COAP_MESSAGE_NON || |
1606 | 0 | COAP_PROTO_RELIABLE(session->proto) || |
1607 | 0 | coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter) || |
1608 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1609 | 0 | coap_check_option(pdu, COAP_OPTION_Q_BLOCK1, &opt_iter) || |
1610 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1611 | 0 | coap_check_option(pdu, COAP_OPTION_BLOCK1, &opt_iter)) { |
1612 | 0 | return 1; |
1613 | 0 | } |
1614 | 0 | return 0; |
1615 | 0 | } |
1616 | | #endif /* COAP_CLIENT_SUPPORT */ |
1617 | | |
1618 | | COAP_API coap_mid_t |
1619 | 0 | coap_send(coap_session_t *session, coap_pdu_t *pdu) { |
1620 | 0 | coap_mid_t mid; |
1621 | |
|
1622 | 0 | coap_lock_lock(return COAP_INVALID_MID); |
1623 | 0 | mid = coap_send_lkd(session, pdu); |
1624 | 0 | coap_lock_unlock(); |
1625 | 0 | return mid; |
1626 | 0 | } |
1627 | | |
1628 | | coap_mid_t |
1629 | 0 | coap_send_lkd(coap_session_t *session, coap_pdu_t *pdu) { |
1630 | 0 | coap_mid_t mid = COAP_INVALID_MID; |
1631 | 0 | #if COAP_CLIENT_SUPPORT |
1632 | 0 | coap_lg_crcv_t *lg_crcv = NULL; |
1633 | 0 | coap_opt_iterator_t opt_iter; |
1634 | 0 | coap_block_b_t block; |
1635 | 0 | int observe_action = -1; |
1636 | 0 | int have_block1 = 0; |
1637 | 0 | coap_opt_t *opt; |
1638 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
1639 | |
|
1640 | 0 | assert(pdu); |
1641 | | |
1642 | 0 | coap_lock_check_locked(); |
1643 | | |
1644 | | /* Check validity of sending code */ |
1645 | 0 | if (!coap_check_code_class(session, pdu)) { |
1646 | 0 | coap_log_err("coap_send: Invalid PDU code (%d.%02d)\n", |
1647 | 0 | COAP_RESPONSE_CLASS(pdu->code), |
1648 | 0 | pdu->code & 0x1f); |
1649 | 0 | goto error; |
1650 | 0 | } |
1651 | 0 | pdu->session = session; |
1652 | 0 | #if COAP_CLIENT_SUPPORT |
1653 | 0 | if (session->type == COAP_SESSION_TYPE_CLIENT && |
1654 | 0 | !coap_netif_available(session) && !session->session_failed) { |
1655 | 0 | coap_log_debug("coap_send: Socket closed\n"); |
1656 | 0 | goto error; |
1657 | 0 | } |
1658 | | |
1659 | 0 | if (session->doing_first) { |
1660 | 0 | LL_APPEND(session->doing_first_pdu, pdu); |
1661 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
1662 | 0 | coap_log_debug("** %s: mid=0x%04x: queued\n", |
1663 | 0 | coap_session_str(session), pdu->mid); |
1664 | 0 | return pdu->mid; |
1665 | 0 | } |
1666 | | |
1667 | | /* Indicate support for Extended Tokens if appropriate */ |
1668 | 0 | if (session->max_token_checked == COAP_EXT_T_NOT_CHECKED && |
1669 | 0 | session->max_token_size > COAP_TOKEN_DEFAULT_MAX && |
1670 | 0 | session->type == COAP_SESSION_TYPE_CLIENT && |
1671 | 0 | COAP_PDU_IS_REQUEST(pdu)) { |
1672 | 0 | if (COAP_PROTO_NOT_RELIABLE(session->proto)) { |
1673 | | /* |
1674 | | * When the pass / fail response for Extended Token is received, this PDU |
1675 | | * will get transmitted. |
1676 | | */ |
1677 | 0 | if (coap_send_test_extended_token(session) == COAP_INVALID_MID) { |
1678 | 0 | goto error; |
1679 | 0 | } |
1680 | 0 | } |
1681 | | /* |
1682 | | * For reliable protocols, this will get cleared after CSM exchanged |
1683 | | * in coap_session_connected() where Token size support is indicated in the CSM. |
1684 | | */ |
1685 | 0 | session->doing_first = 1; |
1686 | 0 | coap_ticks(&session->doing_first_timeout); |
1687 | 0 | LL_PREPEND(session->doing_first_pdu, pdu); |
1688 | 0 | if (session->proto != COAP_PROTO_UDP) { |
1689 | | /* In case the next handshake / CSM is already in */ |
1690 | 0 | coap_io_process_lkd(session->context, COAP_IO_NO_WAIT); |
1691 | 0 | } |
1692 | | /* |
1693 | | * Once Extended Token support size is determined, coap_send_lkd(session, pdu) |
1694 | | * will get called again. |
1695 | | */ |
1696 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
1697 | 0 | coap_log_debug("** %s: mid=0x%04x: queued\n", |
1698 | 0 | coap_session_str(session), pdu->mid); |
1699 | 0 | return pdu->mid; |
1700 | 0 | } |
1701 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1702 | | /* Indicate support for Q-Block if appropriate */ |
1703 | 0 | if (session->block_mode & COAP_BLOCK_TRY_Q_BLOCK && |
1704 | 0 | session->type == COAP_SESSION_TYPE_CLIENT && |
1705 | 0 | COAP_PDU_IS_REQUEST(pdu)) { |
1706 | 0 | if (coap_block_test_q_block(session, pdu) == COAP_INVALID_MID) { |
1707 | 0 | goto error; |
1708 | 0 | } |
1709 | 0 | session->doing_first = 1; |
1710 | 0 | coap_ticks(&session->doing_first_timeout); |
1711 | 0 | LL_PREPEND(session->doing_first_pdu, pdu); |
1712 | 0 | if (session->proto != COAP_PROTO_UDP) { |
1713 | | /* In case the next handshake / CSM is already in */ |
1714 | 0 | coap_io_process_lkd(session->context, COAP_IO_NO_WAIT); |
1715 | 0 | } |
1716 | | /* |
1717 | | * Once Extended Token support size is determined, coap_send_lkd(session, pdu) |
1718 | | * will get called again. |
1719 | | */ |
1720 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
1721 | 0 | coap_log_debug("** %s: mid=0x%04x: queued\n", |
1722 | 0 | coap_session_str(session), pdu->mid); |
1723 | 0 | return pdu->mid; |
1724 | 0 | } |
1725 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1726 | | |
1727 | | /* |
1728 | | * Check validity of token length |
1729 | | */ |
1730 | 0 | if (COAP_PDU_IS_REQUEST(pdu) && |
1731 | 0 | pdu->actual_token.length > session->max_token_size) { |
1732 | 0 | coap_log_warn("coap_send: PDU dropped as token too long (%" PRIuS " > %" PRIu32 ")\n", |
1733 | 0 | pdu->actual_token.length, session->max_token_size); |
1734 | 0 | goto error; |
1735 | 0 | } |
1736 | | |
1737 | | /* A lot of the reliable code assumes type is CON */ |
1738 | 0 | if (COAP_PROTO_RELIABLE(session->proto) && pdu->type != COAP_MESSAGE_CON) |
1739 | 0 | pdu->type = COAP_MESSAGE_CON; |
1740 | |
|
1741 | 0 | #if COAP_OSCORE_SUPPORT |
1742 | 0 | if (session->oscore_encryption) { |
1743 | 0 | if (session->recipient_ctx->initial_state == 1 && |
1744 | 0 | !session->recipient_ctx->silent_server) { |
1745 | | /* |
1746 | | * Not sure if remote supports OSCORE, or is going to send us a |
1747 | | * "4.01 + ECHO" etc. so need to hold off future coap_send()s until all |
1748 | | * is OK. Continue sending current pdu to test things. |
1749 | | */ |
1750 | 0 | session->doing_first = 1; |
1751 | 0 | } |
1752 | | /* Need to convert Proxy-Uri to Proxy-Scheme option if needed */ |
1753 | 0 | if (COAP_PDU_IS_REQUEST(pdu) && !coap_rebuild_pdu_for_proxy(pdu)) { |
1754 | 0 | goto error; |
1755 | 0 | } |
1756 | 0 | } |
1757 | 0 | #endif /* COAP_OSCORE_SUPPORT */ |
1758 | | |
1759 | 0 | if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) { |
1760 | 0 | return coap_send_internal(session, pdu, NULL); |
1761 | 0 | } |
1762 | | |
1763 | 0 | if (session->no_path_abbrev) { |
1764 | 0 | opt = coap_check_option(pdu, COAP_OPTION_URI_PATH_ABB, &opt_iter); |
1765 | 0 | if (opt) { |
1766 | | /* Server cannot handle Uri-Path-Abbrev */ |
1767 | 0 | coap_pdu_t *new; |
1768 | 0 | size_t data_len; |
1769 | 0 | const uint8_t *data; |
1770 | |
|
1771 | 0 | new = coap_pdu_duplicate_lkd(pdu, session, pdu->actual_token.length, |
1772 | 0 | pdu->actual_token.s, NULL, COAP_BOOL_TRUE); |
1773 | 0 | if (new) { |
1774 | 0 | if (coap_get_data(pdu, &data_len, &data)) { |
1775 | 0 | coap_add_data(pdu, data_len, data); |
1776 | 0 | } |
1777 | 0 | coap_log_debug("* Retransmitting PDU with Uri-Path-Abbrev replaced (3)\n"); |
1778 | 0 | coap_delete_pdu_lkd(pdu); |
1779 | 0 | pdu = new; |
1780 | 0 | } |
1781 | 0 | } |
1782 | 0 | } |
1783 | |
|
1784 | 0 | if (COAP_PDU_IS_REQUEST(pdu)) { |
1785 | 0 | uint8_t buf[4]; |
1786 | |
|
1787 | 0 | opt = coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter); |
1788 | |
|
1789 | 0 | if (opt) { |
1790 | 0 | observe_action = coap_decode_var_bytes(coap_opt_value(opt), |
1791 | 0 | coap_opt_length(opt)); |
1792 | 0 | } |
1793 | |
|
1794 | 0 | if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK1, &block) && |
1795 | 0 | (block.m == 1 || block.bert == 1)) { |
1796 | 0 | have_block1 = 1; |
1797 | 0 | } |
1798 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1799 | 0 | if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block) && |
1800 | 0 | (block.m == 1 || block.bert == 1)) { |
1801 | 0 | if (have_block1) { |
1802 | 0 | coap_log_warn("Block1 and Q-Block1 cannot be in the same request\n"); |
1803 | 0 | coap_remove_option(pdu, COAP_OPTION_BLOCK1); |
1804 | 0 | } |
1805 | 0 | have_block1 = 1; |
1806 | 0 | } |
1807 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1808 | 0 | if (observe_action != COAP_OBSERVE_CANCEL) { |
1809 | | /* Warn about reuse of tokens */ |
1810 | 0 | if (session->last_token && |
1811 | 0 | coap_binary_equal(&pdu->actual_token, session->last_token)) { |
1812 | 0 | if (coap_get_log_level() >= COAP_LOG_DEBUG) { |
1813 | 0 | char scratch[24]; |
1814 | 0 | size_t size; |
1815 | 0 | size_t i; |
1816 | |
|
1817 | 0 | scratch[0] = '\000'; |
1818 | 0 | for (i = 0; i < pdu->actual_token.length; i++) { |
1819 | 0 | size = strlen(scratch); |
1820 | 0 | snprintf(&scratch[size], sizeof(scratch)-size, |
1821 | 0 | "%02x", pdu->actual_token.s[i]); |
1822 | 0 | } |
1823 | 0 | coap_log_debug("Token {%s} reused - see https://rfc-editor.org/rfc/rfc9175.html#section-4.2\n", |
1824 | 0 | scratch); |
1825 | 0 | } |
1826 | 0 | } |
1827 | 0 | coap_delete_bin_const(session->last_token); |
1828 | 0 | session->last_token = coap_new_bin_const(pdu->actual_token.s, |
1829 | 0 | pdu->actual_token.length); |
1830 | 0 | } else { |
1831 | | /* observe_action == COAP_OBSERVE_CANCEL */ |
1832 | 0 | coap_binary_t tmp; |
1833 | 0 | int ret; |
1834 | |
|
1835 | 0 | coap_log_debug("coap_send: Using coap_cancel_observe() to do OBSERVE cancellation\n"); |
1836 | | /* Unfortunately need to change the ptr type to be r/w */ |
1837 | 0 | memcpy(&tmp.s, &pdu->actual_token.s, sizeof(tmp.s)); |
1838 | 0 | tmp.length = pdu->actual_token.length; |
1839 | 0 | ret = coap_cancel_observe_lkd(session, &tmp, pdu->type); |
1840 | 0 | if (ret == 1) { |
1841 | | /* Observe Cancel successfully sent */ |
1842 | 0 | coap_delete_pdu_lkd(pdu); |
1843 | 0 | return ret; |
1844 | 0 | } |
1845 | | /* Some mismatch somewhere - continue to send original packet */ |
1846 | 0 | } |
1847 | 0 | if (!coap_check_option(pdu, COAP_OPTION_RTAG, &opt_iter) && |
1848 | 0 | (session->block_mode & COAP_BLOCK_NO_PREEMPTIVE_RTAG) == 0 && |
1849 | 0 | pdu->code != COAP_REQUEST_CODE_DELETE) |
1850 | 0 | coap_insert_option(pdu, |
1851 | 0 | COAP_OPTION_RTAG, |
1852 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1853 | 0 | ++session->tx_rtag), |
1854 | 0 | buf); |
1855 | 0 | } else { |
1856 | 0 | memset(&block, 0, sizeof(block)); |
1857 | 0 | } |
1858 | | |
1859 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1860 | 0 | if (!(session->block_mode & COAP_BLOCK_HAS_Q_BLOCK)) |
1861 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1862 | 0 | { |
1863 | | /* Need to check if we need to reset Q-Block to Block */ |
1864 | 0 | uint8_t buf[4]; |
1865 | |
|
1866 | 0 | if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) { |
1867 | 0 | coap_remove_option(pdu, COAP_OPTION_Q_BLOCK2); |
1868 | 0 | coap_insert_option(pdu, COAP_OPTION_BLOCK2, |
1869 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1870 | 0 | (block.num << 4) | (0 << 3) | block.szx), |
1871 | 0 | buf); |
1872 | 0 | coap_log_debug("Replaced option Q-Block2 with Block2\n"); |
1873 | | /* Need to update associated lg_xmit */ |
1874 | 0 | coap_lg_xmit_t *lg_xmit; |
1875 | |
|
1876 | 0 | LL_FOREACH(session->lg_xmit, lg_xmit) { |
1877 | 0 | if (COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu) && |
1878 | 0 | lg_xmit->b.b1.app_token && |
1879 | 0 | coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) { |
1880 | | /* Update the skeletal PDU with the block1 option */ |
1881 | 0 | coap_remove_option(lg_xmit->sent_pdu, COAP_OPTION_Q_BLOCK2); |
1882 | 0 | coap_update_option(lg_xmit->sent_pdu, COAP_OPTION_BLOCK2, |
1883 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1884 | 0 | (block.num << 4) | (0 << 3) | block.szx), |
1885 | 0 | buf); |
1886 | 0 | break; |
1887 | 0 | } |
1888 | 0 | } |
1889 | 0 | } |
1890 | 0 | if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) { |
1891 | 0 | coap_remove_option(pdu, COAP_OPTION_Q_BLOCK1); |
1892 | 0 | coap_insert_option(pdu, COAP_OPTION_BLOCK1, |
1893 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1894 | 0 | (block.num << 4) | (block.m << 3) | block.szx), |
1895 | 0 | buf); |
1896 | 0 | coap_log_debug("Replaced option Q-Block1 with Block1\n"); |
1897 | | /* Need to update associated lg_xmit */ |
1898 | 0 | coap_lg_xmit_t *lg_xmit; |
1899 | |
|
1900 | 0 | LL_FOREACH(session->lg_xmit, lg_xmit) { |
1901 | 0 | if (COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu) && |
1902 | 0 | lg_xmit->b.b1.app_token && |
1903 | 0 | coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) { |
1904 | | /* Update the skeletal PDU with the block1 option */ |
1905 | 0 | coap_remove_option(lg_xmit->sent_pdu, COAP_OPTION_Q_BLOCK1); |
1906 | 0 | coap_update_option(lg_xmit->sent_pdu, COAP_OPTION_BLOCK1, |
1907 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1908 | 0 | (block.num << 4) | |
1909 | 0 | (block.m << 3) | |
1910 | 0 | block.szx), |
1911 | 0 | buf); |
1912 | | /* Update as this is a Request */ |
1913 | 0 | lg_xmit->option = COAP_OPTION_BLOCK1; |
1914 | 0 | break; |
1915 | 0 | } |
1916 | 0 | } |
1917 | 0 | } |
1918 | 0 | } |
1919 | |
|
1920 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1921 | 0 | if (COAP_PDU_IS_REQUEST(pdu) && |
1922 | 0 | coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, &block)) { |
1923 | 0 | if (block.num == 0 && block.m == 0) { |
1924 | 0 | uint8_t buf[4]; |
1925 | | |
1926 | | /* M needs to be set as asking for all the blocks */ |
1927 | 0 | coap_update_option(pdu, COAP_OPTION_Q_BLOCK2, |
1928 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
1929 | 0 | (0 << 4) | (1 << 3) | block.szx), |
1930 | 0 | buf); |
1931 | 0 | } |
1932 | 0 | } |
1933 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1934 | | |
1935 | | /* |
1936 | | * If type is CON and protocol is not reliable, there is no need to set up |
1937 | | * lg_crcv here as it can be built up based on sent PDU if there is a |
1938 | | * (Q-)Block2 in the response. However, still need it for Observe, Oscore and |
1939 | | * (Q-)Block1. |
1940 | | */ |
1941 | 0 | if (coap_check_send_need_lg_crcv(session, pdu)) { |
1942 | 0 | coap_lg_xmit_t *lg_xmit = NULL; |
1943 | |
|
1944 | 0 | if (!session->lg_xmit && have_block1) { |
1945 | 0 | coap_log_debug("PDU presented by app\n"); |
1946 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
1947 | 0 | } |
1948 | | /* See if this token is already in use for large body responses */ |
1949 | 0 | LL_FOREACH(session->lg_crcv, lg_crcv) { |
1950 | 0 | if (coap_binary_equal(&pdu->actual_token, lg_crcv->app_token)) { |
1951 | | /* Need to terminate and clean up previous response setup */ |
1952 | 0 | LL_DELETE(session->lg_crcv, lg_crcv); |
1953 | 0 | coap_block_delete_lg_crcv(session, lg_crcv); |
1954 | 0 | break; |
1955 | 0 | } |
1956 | 0 | } |
1957 | |
|
1958 | 0 | if (have_block1 && session->lg_xmit) { |
1959 | 0 | LL_FOREACH(session->lg_xmit, lg_xmit) { |
1960 | 0 | if (COAP_PDU_IS_REQUEST(lg_xmit->sent_pdu) && |
1961 | 0 | lg_xmit->b.b1.app_token && |
1962 | 0 | coap_binary_equal(&pdu->actual_token, lg_xmit->b.b1.app_token)) { |
1963 | 0 | break; |
1964 | 0 | } |
1965 | 0 | } |
1966 | 0 | } |
1967 | 0 | lg_crcv = coap_block_new_lg_crcv(session, pdu, lg_xmit); |
1968 | 0 | if (lg_crcv == NULL) { |
1969 | 0 | goto error; |
1970 | 0 | } |
1971 | 0 | if (lg_xmit) { |
1972 | | /* Need to update the token as set up in the session->lg_xmit */ |
1973 | 0 | lg_xmit->b.b1.state_token = lg_crcv->state_token; |
1974 | 0 | } |
1975 | 0 | } |
1976 | 0 | if (session->sock.flags & COAP_SOCKET_MULTICAST) |
1977 | 0 | coap_address_copy(&session->addr_info.remote, &session->sock.mcast_addr); |
1978 | |
|
1979 | 0 | #if COAP_Q_BLOCK_SUPPORT |
1980 | | /* See if large xmit using Q-Block1 (but not testing Q-Block1) */ |
1981 | 0 | if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) { |
1982 | 0 | mid = coap_send_q_block1(session, block, pdu, COAP_SEND_INC_PDU); |
1983 | 0 | } else |
1984 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
1985 | 0 | mid = coap_send_internal(session, pdu, NULL); |
1986 | | #else /* !COAP_CLIENT_SUPPORT */ |
1987 | | mid = coap_send_internal(session, pdu, NULL); |
1988 | | #endif /* !COAP_CLIENT_SUPPORT */ |
1989 | 0 | #if COAP_CLIENT_SUPPORT |
1990 | 0 | if (lg_crcv) { |
1991 | 0 | if (mid != COAP_INVALID_MID) { |
1992 | 0 | LL_PREPEND(session->lg_crcv, lg_crcv); |
1993 | 0 | } else { |
1994 | 0 | coap_block_delete_lg_crcv(session, lg_crcv); |
1995 | 0 | } |
1996 | 0 | } |
1997 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
1998 | 0 | return mid; |
1999 | | |
2000 | 0 | error: |
2001 | 0 | coap_delete_pdu_lkd(pdu); |
2002 | 0 | return COAP_INVALID_MID; |
2003 | 0 | } |
2004 | | |
2005 | | static int |
2006 | 0 | prepend_508_ip(coap_session_t *session, coap_pdu_t *pdu) { |
2007 | 0 | char addr_str[INET6_ADDRSTRLEN + 8 + 1]; |
2008 | 0 | coap_opt_t *opt; |
2009 | 0 | coap_opt_iterator_t opt_iter; |
2010 | 0 | size_t hop_limit; |
2011 | |
|
2012 | 0 | addr_str[sizeof(addr_str)-1] = '\000'; |
2013 | 0 | if (coap_print_addr(&session->addr_info.local, (uint8_t *)addr_str, |
2014 | 0 | sizeof(addr_str) - 1)) { |
2015 | 0 | char *cp; |
2016 | 0 | size_t len; |
2017 | |
|
2018 | 0 | if (addr_str[0] == '[') { |
2019 | 0 | cp = strchr(addr_str, ']'); |
2020 | 0 | if (cp) |
2021 | 0 | *cp = '\000'; |
2022 | 0 | if (memcmp(&addr_str[1], "::ffff:", 7) == 0) { |
2023 | | /* IPv4 embedded into IPv6 */ |
2024 | 0 | cp = &addr_str[8]; |
2025 | 0 | } else { |
2026 | 0 | cp = &addr_str[1]; |
2027 | 0 | } |
2028 | 0 | } else { |
2029 | 0 | cp = strchr(addr_str, ':'); |
2030 | 0 | if (cp) |
2031 | 0 | *cp = '\000'; |
2032 | 0 | cp = addr_str; |
2033 | 0 | } |
2034 | 0 | len = strlen(cp); |
2035 | | |
2036 | | /* See if Hop Limit option is being used in return path */ |
2037 | 0 | opt = coap_check_option(pdu, COAP_OPTION_HOP_LIMIT, &opt_iter); |
2038 | 0 | if (opt) { |
2039 | 0 | uint8_t buf[4]; |
2040 | |
|
2041 | 0 | hop_limit = |
2042 | 0 | coap_decode_var_bytes(coap_opt_value(opt), coap_opt_length(opt)); |
2043 | 0 | if (hop_limit == 1) { |
2044 | 0 | coap_log_warn("Proxy loop detected '%s'\n", |
2045 | 0 | (char *)pdu->data); |
2046 | 0 | coap_delete_pdu_lkd(pdu); |
2047 | 0 | return (coap_mid_t)COAP_DROPPED_RESPONSE; |
2048 | 0 | } else if (hop_limit < 1 || hop_limit > 255) { |
2049 | | /* Something is bad - need to drop this pdu (TODO or delete option) */ |
2050 | 0 | coap_log_warn("Proxy return has bad hop limit count '%" PRIuS "'\n", |
2051 | 0 | hop_limit); |
2052 | 0 | coap_delete_pdu_lkd(pdu); |
2053 | 0 | return 0; |
2054 | 0 | } |
2055 | 0 | hop_limit--; |
2056 | 0 | coap_update_option(pdu, COAP_OPTION_HOP_LIMIT, |
2057 | 0 | coap_encode_var_safe8(buf, sizeof(buf), hop_limit), |
2058 | 0 | buf); |
2059 | 0 | } |
2060 | | |
2061 | | /* Need to check that we are not seeing this proxy in the return loop */ |
2062 | 0 | if (pdu->data && opt == NULL) { |
2063 | 0 | char *a_match; |
2064 | 0 | size_t data_len; |
2065 | |
|
2066 | 0 | if (pdu->used_size + 1 > pdu->max_size) { |
2067 | | /* No space */ |
2068 | 0 | coap_delete_pdu_lkd(pdu); |
2069 | 0 | return 0; |
2070 | 0 | } |
2071 | 0 | if (!coap_pdu_resize(pdu, pdu->used_size + 1)) { |
2072 | | /* Internal error */ |
2073 | 0 | coap_delete_pdu_lkd(pdu); |
2074 | 0 | return 0; |
2075 | 0 | } |
2076 | 0 | data_len = pdu->used_size - (pdu->data - pdu->token); |
2077 | 0 | pdu->data[data_len] = '\000'; |
2078 | 0 | a_match = strstr((char *)pdu->data, cp); |
2079 | 0 | if (a_match && (a_match == (char *)pdu->data || a_match[-1] == ' ') && |
2080 | 0 | ((size_t)(a_match - (char *)pdu->data + len) == data_len || |
2081 | 0 | a_match[len] == ' ')) { |
2082 | 0 | coap_log_warn("Proxy loop detected '%s'\n", |
2083 | 0 | (char *)pdu->data); |
2084 | 0 | coap_delete_pdu_lkd(pdu); |
2085 | 0 | return 0; |
2086 | 0 | } |
2087 | 0 | } |
2088 | 0 | if (pdu->used_size + len + 1 <= pdu->max_size) { |
2089 | 0 | size_t old_size = pdu->used_size; |
2090 | 0 | if (coap_pdu_resize(pdu, pdu->used_size + len + 1)) { |
2091 | 0 | if (pdu->data == NULL) { |
2092 | | /* |
2093 | | * Set Hop Limit to max for return path. If this libcoap is in |
2094 | | * a proxy loop path, it will always decrement hop limit in code |
2095 | | * above and hence timeout / drop the response as appropriate |
2096 | | */ |
2097 | 0 | hop_limit = 255; |
2098 | 0 | coap_insert_option(pdu, COAP_OPTION_HOP_LIMIT, 1, |
2099 | 0 | (uint8_t *)&hop_limit); |
2100 | 0 | coap_add_data(pdu, len, (uint8_t *)cp); |
2101 | 0 | } else { |
2102 | | /* prepend with space separator, leaving hop limit "as is" */ |
2103 | 0 | memmove(pdu->data + len + 1, pdu->data, |
2104 | 0 | old_size - (pdu->data - pdu->token)); |
2105 | 0 | memcpy(pdu->data, cp, len); |
2106 | 0 | pdu->data[len] = ' '; |
2107 | 0 | pdu->used_size += len + 1; |
2108 | 0 | } |
2109 | 0 | } |
2110 | 0 | } |
2111 | 0 | } |
2112 | 0 | return 1; |
2113 | 0 | } |
2114 | | |
2115 | | coap_mid_t |
2116 | 218 | coap_send_internal(coap_session_t *session, coap_pdu_t *pdu, coap_pdu_t *request_pdu) { |
2117 | 218 | uint8_t r; |
2118 | 218 | ssize_t bytes_written; |
2119 | | |
2120 | | #if ! COAP_SERVER_SUPPORT |
2121 | | (void)request_pdu; |
2122 | | #endif /* COAP_SERVER_SUPPORT */ |
2123 | 218 | pdu->session = session; |
2124 | 218 | #if COAP_CLIENT_SUPPORT |
2125 | 218 | if (session->session_failed) { |
2126 | 0 | coap_session_reconnect(session); |
2127 | 0 | if (session->session_failed) |
2128 | 0 | goto error; |
2129 | 0 | } |
2130 | 218 | #endif /* COAP_CLIENT_SUPPORT */ |
2131 | 218 | if (pdu->type == COAP_MESSAGE_NON && session->rl_ticks_per_packet) { |
2132 | 0 | coap_tick_t now; |
2133 | |
|
2134 | 0 | if (!session->is_rate_limiting) { |
2135 | 0 | coap_ticks(&now); |
2136 | 0 | #if (COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_DEBUG) |
2137 | 0 | if (now - session->last_tx < session->rl_ticks_per_packet) { |
2138 | 0 | uint32_t rem = (uint32_t)(session->rl_ticks_per_packet - |
2139 | 0 | (now - session->last_tx)) * 1000 / COAP_TICKS_PER_SECOND; |
2140 | 0 | coap_log_debug("** %s: mid 0x%04x: delaying transmission (%" PRIu32 ".%03" PRIu32 "s)\n", |
2141 | 0 | coap_session_str(session), pdu->mid, rem / 1000, rem %1000); |
2142 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
2143 | 0 | } |
2144 | 0 | #endif /* COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_DEBUG */ |
2145 | 0 | while (1) { |
2146 | 0 | uint32_t timeout_ms; |
2147 | |
|
2148 | 0 | if (send_recv_terminate) { |
2149 | 0 | goto error; |
2150 | 0 | } |
2151 | | |
2152 | 0 | if (now - session->last_tx >= session->rl_ticks_per_packet) { |
2153 | 0 | break; |
2154 | 0 | } |
2155 | 0 | timeout_ms = (uint32_t)(((session->rl_ticks_per_packet - (now - session->last_tx)) * |
2156 | 0 | 1000) / COAP_TICKS_PER_SECOND); |
2157 | |
|
2158 | 0 | if (timeout_ms == 0) { |
2159 | 0 | timeout_ms = COAP_IO_NO_WAIT; |
2160 | 0 | } |
2161 | |
|
2162 | 0 | session->is_rate_limiting = 1; |
2163 | 0 | coap_io_process_lkd(session->context, timeout_ms); |
2164 | 0 | session->is_rate_limiting = 0; |
2165 | 0 | coap_ticks(&now); |
2166 | 0 | } |
2167 | 0 | coap_log_debug("** %s: mid 0x%04x: now transmitting\n", |
2168 | 0 | coap_session_str(session), pdu->mid); |
2169 | 0 | session->last_tx = now; |
2170 | 0 | } |
2171 | 0 | } |
2172 | 218 | #if COAP_PROXY_SUPPORT |
2173 | 218 | if (session->server_list) { |
2174 | | /* Local session wanting to use proxy logic */ |
2175 | 0 | return coap_proxy_local_write(session, pdu); |
2176 | 0 | } |
2177 | 218 | #endif /* COAP_PROXY_SUPPORT */ |
2178 | 218 | if (pdu->code == COAP_RESPONSE_CODE(508)) { |
2179 | | /* |
2180 | | * Need to prepend our IP identifier to the data as per |
2181 | | * https://rfc-editor.org/rfc/rfc8768.html#section-4 |
2182 | | */ |
2183 | 0 | if (!prepend_508_ip(session, pdu)) { |
2184 | 0 | return (coap_mid_t)COAP_DROPPED_RESPONSE; |
2185 | 0 | } |
2186 | 0 | } |
2187 | | |
2188 | 218 | if (session->echo) { |
2189 | 0 | if (!coap_insert_option(pdu, COAP_OPTION_ECHO, session->echo->length, |
2190 | 0 | session->echo->s)) |
2191 | 0 | goto error; |
2192 | 0 | coap_delete_bin_const(session->echo); |
2193 | 0 | session->echo = NULL; |
2194 | 0 | } |
2195 | 218 | #if COAP_OSCORE_SUPPORT |
2196 | 218 | if (session->oscore_encryption) { |
2197 | | /* Need to convert Proxy-Uri to Proxy-Scheme option if needed */ |
2198 | 0 | if (COAP_PDU_IS_REQUEST(pdu) && !coap_rebuild_pdu_for_proxy(pdu)) |
2199 | 0 | goto error; |
2200 | 0 | } |
2201 | 218 | #endif /* COAP_OSCORE_SUPPORT */ |
2202 | | |
2203 | 218 | if (!coap_pdu_encode_header(pdu, session->proto)) { |
2204 | 0 | goto error; |
2205 | 0 | } |
2206 | | |
2207 | 218 | #if !COAP_DISABLE_TCP |
2208 | 218 | if (COAP_PROTO_RELIABLE(session->proto) && |
2209 | 0 | session->state == COAP_SESSION_STATE_ESTABLISHED) { |
2210 | 0 | coap_opt_iterator_t opt_iter; |
2211 | |
|
2212 | 0 | if (!session->csm_block_supported) { |
2213 | | /* |
2214 | | * Need to check that this instance is not sending any block options as |
2215 | | * the remote end via CSM has not informed us that there is support |
2216 | | * https://rfc-editor.org/rfc/rfc8323#section-5.3.2 |
2217 | | * This includes potential BERT blocks. |
2218 | | */ |
2219 | 0 | if (coap_check_option(pdu, COAP_OPTION_BLOCK1, &opt_iter) != NULL) { |
2220 | 0 | coap_log_debug("Remote end did not indicate CSM support for Block1 enabled\n"); |
2221 | 0 | } |
2222 | 0 | if (coap_check_option(pdu, COAP_OPTION_BLOCK2, &opt_iter) != NULL) { |
2223 | 0 | coap_log_debug("Remote end did not indicate CSM support for Block2 enabled\n"); |
2224 | 0 | } |
2225 | 0 | } else if (!session->csm_bert_rem_support) { |
2226 | 0 | coap_opt_t *opt; |
2227 | |
|
2228 | 0 | opt = coap_check_option(pdu, COAP_OPTION_BLOCK1, &opt_iter); |
2229 | 0 | if (opt && COAP_OPT_BLOCK_SZX(opt) == 7) { |
2230 | 0 | coap_log_debug("Remote end did not indicate CSM support for BERT Block1\n"); |
2231 | 0 | } |
2232 | 0 | opt = coap_check_option(pdu, COAP_OPTION_BLOCK2, &opt_iter); |
2233 | 0 | if (opt && COAP_OPT_BLOCK_SZX(opt) == 7) { |
2234 | 0 | coap_log_debug("Remote end did not indicate CSM support for BERT Block2\n"); |
2235 | 0 | } |
2236 | 0 | } |
2237 | 0 | } |
2238 | 218 | #endif /* !COAP_DISABLE_TCP */ |
2239 | | |
2240 | 218 | #if COAP_OSCORE_SUPPORT |
2241 | 218 | if (session->oscore_encryption && |
2242 | 0 | pdu->type != COAP_MESSAGE_RST && |
2243 | 0 | !(pdu->type == COAP_MESSAGE_ACK && pdu->code == COAP_EMPTY_CODE) && |
2244 | 0 | !(COAP_PROTO_RELIABLE(session->proto) && pdu->code == COAP_SIGNALING_CODE_PONG)) { |
2245 | | /* Refactor PDU as appropriate RFC8613 */ |
2246 | 0 | coap_pdu_t *osc_pdu = coap_oscore_new_pdu_encrypted_lkd(session, pdu, NULL, 0); |
2247 | |
|
2248 | 0 | if (osc_pdu == NULL) { |
2249 | 0 | coap_log_warn("OSCORE: PDU could not be encrypted\n"); |
2250 | 0 | if (coap_get_log_level() < COAP_LOG_DEBUG) |
2251 | 0 | coap_show_pdu(COAP_LOG_WARN, pdu); |
2252 | 0 | goto error; |
2253 | 0 | } |
2254 | 0 | bytes_written = coap_send_pdu(session, osc_pdu, NULL); |
2255 | 0 | coap_delete_pdu_lkd(pdu); |
2256 | 0 | pdu = osc_pdu; |
2257 | 0 | } else |
2258 | 218 | #endif /* COAP_OSCORE_SUPPORT */ |
2259 | 218 | bytes_written = coap_send_pdu(session, pdu, NULL); |
2260 | | |
2261 | 218 | #if COAP_SERVER_SUPPORT |
2262 | 218 | if (session->last_resp_pdu != pdu && |
2263 | 218 | request_pdu && COAP_PROTO_NOT_RELIABLE(session->proto) && |
2264 | 96 | COAP_PDU_IS_REQUEST(request_pdu) && |
2265 | 96 | COAP_PDU_IS_RESPONSE(pdu) && pdu->type == COAP_MESSAGE_ACK) { |
2266 | 78 | coap_delete_pdu_lkd(session->last_resp_pdu); |
2267 | 78 | session->last_resp_pdu = pdu; |
2268 | 78 | coap_pdu_reference_lkd(session->last_resp_pdu); |
2269 | 78 | } |
2270 | 218 | #endif /* COAP_SERVER_SUPPORT */ |
2271 | | |
2272 | 218 | if (bytes_written == COAP_PDU_DELAYED) { |
2273 | | /* do not free pdu as it is stored with session for later use */ |
2274 | 0 | return pdu->mid; |
2275 | 0 | } |
2276 | 218 | if (bytes_written < 0) { |
2277 | 106 | if (pdu->code != 0) |
2278 | 103 | coap_session_disconnected_lkd(session, COAP_NACK_NOT_DELIVERABLE); |
2279 | 106 | goto error; |
2280 | 106 | } |
2281 | | |
2282 | 112 | #if !COAP_DISABLE_TCP |
2283 | 112 | if (COAP_PROTO_RELIABLE(session->proto) && |
2284 | 0 | (size_t)bytes_written < pdu->used_size + pdu->hdr_size) { |
2285 | 0 | if (coap_session_delay_pdu(session, pdu, NULL) == COAP_PDU_DELAYED) { |
2286 | 0 | session->partial_write = (size_t)bytes_written; |
2287 | | /* do not free pdu as it is stored with session for later use */ |
2288 | 0 | return pdu->mid; |
2289 | 0 | } else { |
2290 | 0 | goto error; |
2291 | 0 | } |
2292 | 0 | } |
2293 | 112 | #endif /* !COAP_DISABLE_TCP */ |
2294 | | |
2295 | 112 | if (pdu->type != COAP_MESSAGE_CON |
2296 | 112 | || COAP_PROTO_RELIABLE(session->proto)) { |
2297 | 112 | coap_mid_t id = pdu->mid; |
2298 | 112 | coap_delete_pdu_lkd(pdu); |
2299 | 112 | return id; |
2300 | 112 | } |
2301 | | |
2302 | 0 | coap_queue_t *node = coap_new_node(); |
2303 | 0 | if (!node) { |
2304 | 0 | coap_log_debug("coap_wait_ack: insufficient memory\n"); |
2305 | 0 | goto error; |
2306 | 0 | } |
2307 | | |
2308 | 0 | node->id = pdu->mid; |
2309 | 0 | node->pdu = pdu; |
2310 | 0 | coap_prng_lkd(&r, sizeof(r)); |
2311 | | /* add timeout in range [ACK_TIMEOUT...ACK_TIMEOUT * ACK_RANDOM_FACTOR] */ |
2312 | 0 | node->timeout = coap_calc_timeout(session, r); |
2313 | 0 | return coap_wait_ack(session->context, session, node); |
2314 | 106 | error: |
2315 | 106 | coap_delete_pdu_lkd(pdu); |
2316 | 106 | return COAP_INVALID_MID; |
2317 | 0 | } |
2318 | | |
2319 | | void |
2320 | 0 | coap_send_recv_terminate(void) { |
2321 | 0 | send_recv_terminate = 1; |
2322 | 0 | } |
2323 | | |
2324 | | COAP_API int |
2325 | | coap_send_recv(coap_session_t *session, coap_pdu_t *request_pdu, |
2326 | 0 | coap_pdu_t **response_pdu, uint32_t timeout_ms) { |
2327 | 0 | int ret; |
2328 | |
|
2329 | 0 | coap_lock_lock(return 0); |
2330 | 0 | ret = coap_send_recv_lkd(session, request_pdu, response_pdu, timeout_ms); |
2331 | 0 | coap_lock_unlock(); |
2332 | 0 | return ret; |
2333 | 0 | } |
2334 | | |
2335 | | /* |
2336 | | * Return 0 or +ve Time in function in ms after successful transfer |
2337 | | * -1 Invalid timeout parameter |
2338 | | * -2 Failed to transmit PDU |
2339 | | * -3 Nack or Event handler invoked, cancelling request |
2340 | | * -4 coap_io_process returned error (fail to re-lock or select()) |
2341 | | * -5 Response not received in the given time |
2342 | | * -6 Terminated by user |
2343 | | * -7 Client mode code not enabled |
2344 | | */ |
2345 | | int |
2346 | | coap_send_recv_lkd(coap_session_t *session, coap_pdu_t *request_pdu, |
2347 | 0 | coap_pdu_t **response_pdu, uint32_t timeout_ms) { |
2348 | 0 | #if COAP_CLIENT_SUPPORT |
2349 | 0 | coap_mid_t mid = COAP_INVALID_MID; |
2350 | 0 | uint32_t rem_timeout = timeout_ms; |
2351 | 0 | uint32_t block_mode = session->block_mode; |
2352 | 0 | int ret = 0; |
2353 | 0 | coap_tick_t now; |
2354 | 0 | coap_tick_t start; |
2355 | 0 | coap_tick_t ticks_so_far; |
2356 | 0 | uint32_t time_so_far_ms; |
2357 | |
|
2358 | 0 | coap_ticks(&start); |
2359 | 0 | assert(request_pdu); |
2360 | | |
2361 | 0 | coap_lock_check_locked(); |
2362 | |
|
2363 | 0 | session->resp_pdu = NULL; |
2364 | 0 | session->req_token = coap_new_bin_const(request_pdu->actual_token.s, |
2365 | 0 | request_pdu->actual_token.length); |
2366 | |
|
2367 | 0 | if (timeout_ms == COAP_IO_NO_WAIT || timeout_ms == COAP_IO_WAIT) { |
2368 | 0 | ret = -1; |
2369 | 0 | goto fail; |
2370 | 0 | } |
2371 | 0 | if (session->state == COAP_SESSION_STATE_NONE) { |
2372 | 0 | ret = -3; |
2373 | 0 | goto fail; |
2374 | 0 | } |
2375 | | |
2376 | 0 | session->block_mode |= COAP_BLOCK_SINGLE_BODY; |
2377 | 0 | if (coap_is_mcast(&session->addr_info.remote)) |
2378 | 0 | block_mode = session->block_mode; |
2379 | |
|
2380 | 0 | session->doing_send_recv = 1; |
2381 | | /* So the user needs to delete the PDU */ |
2382 | 0 | coap_pdu_reference_lkd(request_pdu); |
2383 | 0 | mid = coap_send_lkd(session, request_pdu); |
2384 | 0 | if (mid == COAP_INVALID_MID) { |
2385 | 0 | if (!session->doing_send_recv) |
2386 | 0 | ret = -3; |
2387 | 0 | else |
2388 | 0 | ret = -2; |
2389 | 0 | goto fail; |
2390 | 0 | } |
2391 | | |
2392 | | /* Wait for the response to come in */ |
2393 | 0 | while (rem_timeout > 0 && session->doing_send_recv && !session->resp_pdu) { |
2394 | 0 | if (send_recv_terminate) { |
2395 | 0 | ret = -6; |
2396 | 0 | goto fail; |
2397 | 0 | } |
2398 | 0 | ret = coap_io_process_lkd(session->context, rem_timeout); |
2399 | 0 | if (ret < 0) { |
2400 | 0 | ret = -4; |
2401 | 0 | goto fail; |
2402 | 0 | } |
2403 | | /* timeout_ms is for timeout between specific request and response */ |
2404 | 0 | coap_ticks(&now); |
2405 | 0 | ticks_so_far = now - session->last_rx_tx; |
2406 | 0 | time_so_far_ms = (uint32_t)((ticks_so_far * 1000) / COAP_TICKS_PER_SECOND); |
2407 | 0 | if (time_so_far_ms >= timeout_ms) { |
2408 | 0 | rem_timeout = 0; |
2409 | 0 | } else { |
2410 | 0 | rem_timeout = timeout_ms - time_so_far_ms; |
2411 | 0 | } |
2412 | 0 | if (session->state != COAP_SESSION_STATE_ESTABLISHED) { |
2413 | | /* To pick up on (D)TLS setup issues */ |
2414 | 0 | coap_ticks(&now); |
2415 | 0 | ticks_so_far = now - start; |
2416 | 0 | time_so_far_ms = (uint32_t)((ticks_so_far * 1000) / COAP_TICKS_PER_SECOND); |
2417 | 0 | if (time_so_far_ms >= timeout_ms) { |
2418 | 0 | rem_timeout = 0; |
2419 | 0 | } else { |
2420 | 0 | rem_timeout = timeout_ms - time_so_far_ms; |
2421 | 0 | } |
2422 | 0 | } |
2423 | 0 | } |
2424 | | |
2425 | 0 | if (rem_timeout) { |
2426 | 0 | coap_ticks(&now); |
2427 | 0 | ticks_so_far = now - start; |
2428 | 0 | time_so_far_ms = (uint32_t)((ticks_so_far * 1000) / COAP_TICKS_PER_SECOND); |
2429 | 0 | ret = time_so_far_ms; |
2430 | | /* Give PDU to user who will be calling coap_delete_pdu() */ |
2431 | 0 | *response_pdu = session->resp_pdu; |
2432 | 0 | session->resp_pdu = NULL; |
2433 | 0 | if (*response_pdu == NULL) { |
2434 | 0 | ret = -3; |
2435 | 0 | } |
2436 | 0 | } else { |
2437 | | /* If there is a resp_pdu, it will get cleared below */ |
2438 | 0 | ret = -5; |
2439 | 0 | } |
2440 | |
|
2441 | 0 | fail: |
2442 | 0 | session->block_mode = block_mode; |
2443 | 0 | session->doing_send_recv = 0; |
2444 | | /* delete referenced copy */ |
2445 | 0 | coap_delete_pdu_lkd(session->resp_pdu); |
2446 | 0 | session->resp_pdu = NULL; |
2447 | 0 | coap_delete_bin_const(session->req_token); |
2448 | 0 | session->req_token = NULL; |
2449 | 0 | return ret; |
2450 | |
|
2451 | | #else /* !COAP_CLIENT_SUPPORT */ |
2452 | | |
2453 | | (void)session; |
2454 | | (void)timeout_ms; |
2455 | | (void)request_pdu; |
2456 | | coap_log_warn("coap_send_recv: Client mode not supported\n"); |
2457 | | *response_pdu = NULL; |
2458 | | return -7; |
2459 | | |
2460 | | #endif /* ! COAP_CLIENT_SUPPORT */ |
2461 | 0 | } |
2462 | | |
2463 | | coap_mid_t |
2464 | 0 | coap_retransmit(coap_context_t *context, coap_queue_t *node) { |
2465 | 0 | if (!context || !node || !node->session) |
2466 | 0 | return COAP_INVALID_MID; |
2467 | | |
2468 | 0 | #if COAP_CLIENT_SUPPORT |
2469 | 0 | if (node->session->session_failed) { |
2470 | | /* Force failure */ |
2471 | 0 | node->retransmit_cnt = (unsigned char)node->session->max_retransmit; |
2472 | 0 | } |
2473 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
2474 | | |
2475 | | /* re-initialize timeout when maximum number of retransmissions are not reached yet */ |
2476 | 0 | if (node->retransmit_cnt < node->session->max_retransmit) { |
2477 | 0 | ssize_t bytes_written; |
2478 | 0 | coap_tick_t now; |
2479 | 0 | coap_tick_t next_delay; |
2480 | 0 | coap_address_t remote; |
2481 | |
|
2482 | 0 | node->retransmit_cnt++; |
2483 | 0 | coap_handle_event_lkd(context, COAP_EVENT_MSG_RETRANSMITTED, node->session); |
2484 | |
|
2485 | 0 | next_delay = (coap_tick_t)node->timeout << node->retransmit_cnt; |
2486 | 0 | if (context->ping_timeout && |
2487 | 0 | context->ping_timeout * COAP_TICKS_PER_SECOND < next_delay) { |
2488 | 0 | uint8_t byte; |
2489 | |
|
2490 | 0 | coap_prng_lkd(&byte, sizeof(byte)); |
2491 | | /* Don't exceed the ping timeout value */ |
2492 | 0 | next_delay = context->ping_timeout * COAP_TICKS_PER_SECOND - 255 + byte; |
2493 | 0 | } |
2494 | |
|
2495 | 0 | coap_ticks(&now); |
2496 | 0 | if (context->sendqueue == NULL) { |
2497 | 0 | node->t = next_delay; |
2498 | 0 | context->sendqueue_basetime = now; |
2499 | 0 | } else { |
2500 | | /* make node->t relative to context->sendqueue_basetime */ |
2501 | 0 | node->t = (now - context->sendqueue_basetime) + next_delay; |
2502 | 0 | } |
2503 | 0 | coap_insert_node(&context->sendqueue, node); |
2504 | 0 | coap_address_copy(&remote, &node->session->addr_info.remote); |
2505 | 0 | coap_address_copy(&node->session->addr_info.remote, &node->remote); |
2506 | |
|
2507 | 0 | if (node->is_mcast) { |
2508 | 0 | coap_log_debug("** %s: mid=0x%04x: mcast delayed transmission\n", |
2509 | 0 | coap_session_str(node->session), node->id); |
2510 | 0 | } else { |
2511 | 0 | coap_log_debug("** %s: mid=0x%04x: retransmission #%d (next %ums)\n", |
2512 | 0 | coap_session_str(node->session), node->id, |
2513 | 0 | node->retransmit_cnt, |
2514 | 0 | (unsigned)(next_delay * 1000 / COAP_TICKS_PER_SECOND)); |
2515 | 0 | } |
2516 | |
|
2517 | 0 | if (node->session->con_active) |
2518 | 0 | node->session->con_active--; |
2519 | 0 | bytes_written = coap_send_pdu(node->session, node->pdu, node); |
2520 | |
|
2521 | 0 | if (bytes_written == COAP_PDU_DELAYED) { |
2522 | | /* PDU was not retransmitted immediately because a new handshake is |
2523 | | in progress. node was moved to the send queue of the session. */ |
2524 | 0 | return node->id; |
2525 | 0 | } |
2526 | | |
2527 | 0 | coap_address_copy(&node->session->addr_info.remote, &remote); |
2528 | 0 | if (node->is_mcast) { |
2529 | 0 | coap_session_connected(node->session); |
2530 | 0 | coap_delete_node_lkd(node); |
2531 | 0 | return COAP_INVALID_MID; |
2532 | 0 | } |
2533 | | |
2534 | 0 | if (bytes_written < 0) |
2535 | 0 | return (int)bytes_written; |
2536 | | |
2537 | 0 | return node->id; |
2538 | 0 | } |
2539 | | |
2540 | 0 | #if COAP_CLIENT_SUPPORT |
2541 | 0 | if (node->session->session_failed) { |
2542 | 0 | coap_log_info("** %s: mid=0x%04x: deleted due to reconnection issue\n", |
2543 | 0 | coap_session_str(node->session), node->id); |
2544 | 0 | } else { |
2545 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
2546 | | /* no more retransmissions, remove node from system */ |
2547 | 0 | coap_log_warn("** %s: mid=0x%04x: give up after %d attempts\n", |
2548 | 0 | coap_session_str(node->session), node->id, node->retransmit_cnt); |
2549 | 0 | #if COAP_CLIENT_SUPPORT |
2550 | 0 | } |
2551 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
2552 | |
|
2553 | 0 | #if COAP_SERVER_SUPPORT |
2554 | | /* Check if subscriptions exist that should be canceled after |
2555 | | COAP_OBS_MAX_FAIL */ |
2556 | 0 | if (COAP_RESPONSE_CLASS(node->pdu->code) >= 2 && |
2557 | 0 | (node->session->ref_subscriptions || node->session->ref_proxy_subs)) { |
2558 | 0 | if (context->ping_timeout) { |
2559 | 0 | coap_session_server_keepalive_failed(node->session); |
2560 | 0 | coap_delete_node_lkd(node); |
2561 | 0 | return COAP_INVALID_MID; |
2562 | 0 | } else { |
2563 | 0 | if (node->session->ref_subscriptions) |
2564 | 0 | coap_handle_failed_notify(context, node->session, &node->pdu->actual_token); |
2565 | 0 | #if COAP_PROXY_SUPPORT |
2566 | | /* Need to check is there is a proxy subscription active and delete it */ |
2567 | 0 | if (node->session->ref_proxy_subs) |
2568 | 0 | coap_delete_proxy_subscriber(node->session, &node->pdu->actual_token, |
2569 | 0 | 0, COAP_PROXY_SUBS_TOKEN); |
2570 | 0 | #endif /* COAP_PROXY_SUPPORT */ |
2571 | 0 | } |
2572 | 0 | } |
2573 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
2574 | 0 | if (node->session->con_active) { |
2575 | 0 | node->session->con_active--; |
2576 | 0 | if (node->session->state == COAP_SESSION_STATE_ESTABLISHED) { |
2577 | | /* |
2578 | | * As there may be another CON in a different queue entry on the same |
2579 | | * session that needs to be immediately released, |
2580 | | * coap_session_connected() is called. |
2581 | | * However, there is the possibility coap_wait_ack() may be called for |
2582 | | * this node (queue) and re-added to context->sendqueue. |
2583 | | * coap_delete_node_lkd(node) called shortly will handle this and |
2584 | | * remove it. |
2585 | | */ |
2586 | 0 | coap_session_connected(node->session); |
2587 | 0 | } |
2588 | 0 | } |
2589 | |
|
2590 | 0 | if (node->pdu->type == COAP_MESSAGE_CON) { |
2591 | 0 | coap_handle_nack(node->session, node->pdu, COAP_NACK_TOO_MANY_RETRIES, node->id); |
2592 | 0 | } |
2593 | 0 | #if COAP_CLIENT_SUPPORT |
2594 | 0 | node->session->doing_send_recv = 0; |
2595 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
2596 | | /* And finally delete the node */ |
2597 | 0 | coap_delete_node_lkd(node); |
2598 | 0 | return COAP_INVALID_MID; |
2599 | 0 | } |
2600 | | |
2601 | | static int |
2602 | 0 | coap_handle_dgram_for_proto(coap_context_t *ctx, coap_session_t *session, coap_packet_t *packet) { |
2603 | 0 | uint8_t *data; |
2604 | 0 | size_t data_len; |
2605 | 0 | int result = -1; |
2606 | |
|
2607 | 0 | coap_packet_get_memmapped(packet, &data, &data_len); |
2608 | 0 | if (session->proto == COAP_PROTO_DTLS) { |
2609 | 0 | #if COAP_SERVER_SUPPORT |
2610 | 0 | if (session->type == COAP_SESSION_TYPE_HELLO) |
2611 | 0 | result = coap_dtls_hello(session, data, data_len); |
2612 | 0 | else |
2613 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
2614 | 0 | if (session->tls) |
2615 | 0 | result = coap_dtls_receive(session, data, data_len); |
2616 | 0 | } else if (session->proto == COAP_PROTO_UDP) { |
2617 | 0 | result = coap_handle_dgram(ctx, session, data, data_len); |
2618 | 0 | } |
2619 | 0 | return result; |
2620 | 0 | } |
2621 | | |
2622 | | #if COAP_CLIENT_SUPPORT |
2623 | | void |
2624 | 0 | coap_connect_session(coap_session_t *session, coap_tick_t now) { |
2625 | | #if COAP_DISABLE_TCP |
2626 | | (void)now; |
2627 | | |
2628 | | session->sock.flags &= ~(COAP_SOCKET_WANT_CONNECT | COAP_SOCKET_CAN_CONNECT); |
2629 | | #else /* !COAP_DISABLE_TCP */ |
2630 | 0 | if (coap_netif_strm_connect2(session)) { |
2631 | 0 | session->last_rx_tx = now; |
2632 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_TCP_CONNECTED, session); |
2633 | 0 | session->sock.lfunc[COAP_LAYER_SESSION].l_establish(session); |
2634 | 0 | } else { |
2635 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_TCP_FAILED, session); |
2636 | 0 | coap_session_disconnected_lkd(session, COAP_NACK_NOT_DELIVERABLE); |
2637 | 0 | } |
2638 | 0 | #endif /* !COAP_DISABLE_TCP */ |
2639 | 0 | } |
2640 | | #endif /* COAP_CLIENT_SUPPORT */ |
2641 | | |
2642 | | static void |
2643 | 0 | coap_write_session(coap_context_t *ctx, coap_session_t *session, coap_tick_t now) { |
2644 | 0 | coap_queue_t *q; |
2645 | |
|
2646 | 0 | (void)ctx; |
2647 | 0 | assert(session->sock.flags & COAP_SOCKET_CONNECTED); |
2648 | | |
2649 | 0 | while ((q = coap_remove_first_from_delayq(session)) != NULL) { |
2650 | 0 | ssize_t bytes_written; |
2651 | |
|
2652 | 0 | coap_address_copy(&session->addr_info.remote, &q->remote); |
2653 | 0 | coap_log_debug("** %s: mid=0x%04x: transmitted after delay (1)\n", |
2654 | 0 | coap_session_str(session), (int)q->id); |
2655 | 0 | assert(session->partial_write < q->pdu->used_size + q->pdu->hdr_size); |
2656 | 0 | bytes_written = session->sock.lfunc[COAP_LAYER_SESSION].l_write(session, |
2657 | 0 | q->pdu->token - q->pdu->hdr_size + session->partial_write, |
2658 | 0 | q->pdu->used_size + q->pdu->hdr_size - session->partial_write); |
2659 | 0 | if (bytes_written > 0) |
2660 | 0 | session->last_rx_tx = now; |
2661 | 0 | if (bytes_written <= 0 || |
2662 | 0 | (size_t)bytes_written < q->pdu->used_size + q->pdu->hdr_size - session->partial_write) { |
2663 | 0 | if (bytes_written > 0) |
2664 | 0 | session->partial_write += (size_t)bytes_written; |
2665 | 0 | coap_add_to_head_delayq(session, q); |
2666 | 0 | break; |
2667 | 0 | } |
2668 | 0 | session->partial_write = 0; |
2669 | 0 | coap_delete_node_lkd(q); |
2670 | 0 | } |
2671 | 0 | } |
2672 | | |
2673 | | void |
2674 | 0 | coap_read_session(coap_context_t *ctx, coap_session_t *session, coap_tick_t now) { |
2675 | | #if COAP_CONSTRAINED_STACK |
2676 | | /* payload and packet can be protected by global_lock if needed */ |
2677 | | static unsigned char payload[COAP_RXBUFFER_SIZE]; |
2678 | | static coap_packet_t s_packet; |
2679 | | #else /* ! COAP_CONSTRAINED_STACK */ |
2680 | 0 | unsigned char payload[COAP_RXBUFFER_SIZE]; |
2681 | 0 | coap_packet_t s_packet; |
2682 | 0 | #endif /* ! COAP_CONSTRAINED_STACK */ |
2683 | 0 | coap_packet_t *packet = &s_packet; |
2684 | |
|
2685 | 0 | assert(session->sock.flags & (COAP_SOCKET_CONNECTED | COAP_SOCKET_MULTICAST)); |
2686 | | |
2687 | 0 | packet->length = sizeof(payload); |
2688 | 0 | packet->payload = payload; |
2689 | |
|
2690 | 0 | if (COAP_PROTO_NOT_RELIABLE(session->proto)) { |
2691 | 0 | ssize_t bytes_read; |
2692 | 0 | coap_address_t remote; |
2693 | |
|
2694 | 0 | coap_address_copy(&remote, &session->addr_info.remote); |
2695 | 0 | memcpy(&packet->addr_info, &session->addr_info, sizeof(packet->addr_info)); |
2696 | 0 | bytes_read = coap_netif_dgrm_read(session, packet); |
2697 | |
|
2698 | 0 | if (bytes_read < 0) { |
2699 | 0 | if (bytes_read == -2) { |
2700 | 0 | coap_address_copy(&session->addr_info.remote, &remote); |
2701 | | /* Reset the session back to startup defaults */ |
2702 | 0 | coap_session_disconnected_lkd(session, COAP_NACK_ICMP_ISSUE); |
2703 | 0 | } |
2704 | 0 | } else if (bytes_read > 0) { |
2705 | 0 | session->last_rx_tx = now; |
2706 | 0 | #if COAP_CLIENT_SUPPORT |
2707 | 0 | if (session->session_failed) { |
2708 | 0 | session->session_failed = 0; |
2709 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_RECONNECT_SUCCESS, session); |
2710 | 0 | } |
2711 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
2712 | | /* coap_netif_dgrm_read() updates session->addr_info from packet->addr_info */ |
2713 | 0 | coap_handle_dgram_for_proto(ctx, session, packet); |
2714 | 0 | } else { |
2715 | 0 | coap_address_copy(&session->addr_info.remote, &remote); |
2716 | 0 | } |
2717 | 0 | #if !COAP_DISABLE_TCP |
2718 | 0 | } else if (session->proto == COAP_PROTO_WS || |
2719 | 0 | session->proto == COAP_PROTO_WSS) { |
2720 | 0 | ssize_t bytes_read = 0; |
2721 | | |
2722 | | /* WebSocket layer passes us the whole packet */ |
2723 | 0 | bytes_read = session->sock.lfunc[COAP_LAYER_SESSION].l_read(session, |
2724 | 0 | packet->payload, |
2725 | 0 | packet->length); |
2726 | 0 | if (bytes_read < 0) { |
2727 | 0 | coap_session_disconnected_lkd(session, COAP_NACK_NOT_DELIVERABLE); |
2728 | 0 | } else if (bytes_read > 2) { |
2729 | 0 | coap_pdu_t *pdu; |
2730 | |
|
2731 | 0 | session->last_rx_tx = now; |
2732 | | /* Need max space in case PDU is updated with updated token etc. */ |
2733 | 0 | pdu = coap_pdu_init(0, 0, 0, coap_session_max_pdu_rcv_size(session)); |
2734 | 0 | if (!pdu) { |
2735 | 0 | return; |
2736 | 0 | } |
2737 | | |
2738 | 0 | if (!coap_pdu_parse(session->proto, packet->payload, bytes_read, pdu)) { |
2739 | 0 | coap_handle_event_lkd(session->context, COAP_EVENT_BAD_PACKET, session); |
2740 | 0 | coap_log_warn("discard malformed PDU\n"); |
2741 | 0 | coap_delete_pdu_lkd(pdu); |
2742 | 0 | return; |
2743 | 0 | } |
2744 | | |
2745 | 0 | coap_dispatch(ctx, session, pdu); |
2746 | 0 | coap_delete_pdu_lkd(pdu); |
2747 | 0 | return; |
2748 | 0 | } |
2749 | 0 | } else { |
2750 | 0 | ssize_t bytes_read = 0; |
2751 | 0 | const uint8_t *p; |
2752 | 0 | int retry; |
2753 | |
|
2754 | 0 | do { |
2755 | 0 | bytes_read = session->sock.lfunc[COAP_LAYER_SESSION].l_read(session, |
2756 | 0 | packet->payload, |
2757 | 0 | packet->length); |
2758 | 0 | if (bytes_read > 0) { |
2759 | 0 | session->last_rx_tx = now; |
2760 | 0 | } |
2761 | 0 | p = packet->payload; |
2762 | 0 | retry = bytes_read == (ssize_t)packet->length; |
2763 | 0 | while (bytes_read > 0) { |
2764 | 0 | if (session->partial_pdu) { |
2765 | 0 | size_t len = session->partial_pdu->used_size |
2766 | 0 | + session->partial_pdu->hdr_size |
2767 | 0 | - session->partial_read; |
2768 | 0 | size_t n = min(len, (size_t)bytes_read); |
2769 | 0 | memcpy(session->partial_pdu->token - session->partial_pdu->hdr_size |
2770 | 0 | + session->partial_read, p, n); |
2771 | 0 | p += n; |
2772 | 0 | bytes_read -= n; |
2773 | 0 | if (n == len) { |
2774 | 0 | coap_opt_filter_t error_opts; |
2775 | 0 | coap_pdu_t *pdu = session->partial_pdu; |
2776 | |
|
2777 | 0 | session->partial_pdu = NULL; |
2778 | 0 | session->partial_read = 0; |
2779 | |
|
2780 | 0 | coap_option_filter_clear(&error_opts); |
2781 | 0 | if (coap_pdu_parse_header(pdu, session->proto) |
2782 | 0 | && coap_pdu_parse_opt(pdu, &error_opts)) { |
2783 | 0 | coap_dispatch(ctx, session, pdu); |
2784 | 0 | } else if (error_opts.mask) { |
2785 | 0 | coap_pdu_t *response = |
2786 | 0 | coap_new_error_response(pdu, |
2787 | 0 | COAP_RESPONSE_CODE(402), &error_opts); |
2788 | 0 | if (!response) { |
2789 | 0 | coap_log_warn("coap_read_session: cannot create error response\n"); |
2790 | 0 | } else { |
2791 | 0 | if (coap_send_internal(session, response, NULL) == COAP_INVALID_MID) |
2792 | 0 | coap_log_warn("coap_read_session: error sending response\n"); |
2793 | 0 | } |
2794 | 0 | } |
2795 | 0 | coap_delete_pdu_lkd(pdu); |
2796 | 0 | } else { |
2797 | 0 | session->partial_read += n; |
2798 | 0 | } |
2799 | 0 | } else if (session->partial_read > 0) { |
2800 | 0 | size_t hdr_size = coap_pdu_parse_header_size(session->proto, |
2801 | 0 | session->read_header); |
2802 | 0 | size_t tkl = session->read_header[0] & 0x0f; |
2803 | 0 | size_t tok_ext_bytes = tkl == COAP_TOKEN_EXT_1B_TKL ? 1 : |
2804 | 0 | tkl == COAP_TOKEN_EXT_2B_TKL ? 2 : 0; |
2805 | 0 | size_t len = hdr_size + tok_ext_bytes - session->partial_read; |
2806 | 0 | size_t n = min(len, (size_t)bytes_read); |
2807 | 0 | memcpy(session->read_header + session->partial_read, p, n); |
2808 | 0 | p += n; |
2809 | 0 | bytes_read -= n; |
2810 | 0 | if (n == len) { |
2811 | | /* Header now all in */ |
2812 | 0 | size_t size = coap_pdu_parse_size(session->proto, session->read_header, |
2813 | 0 | hdr_size + tok_ext_bytes); |
2814 | 0 | if (size > COAP_DEFAULT_MAX_PDU_RX_SIZE) { |
2815 | 0 | coap_log_warn("** %s: incoming PDU length too large (%" PRIuS " > %lu)\n", |
2816 | 0 | coap_session_str(session), |
2817 | 0 | size, COAP_DEFAULT_MAX_PDU_RX_SIZE); |
2818 | 0 | bytes_read = -1; |
2819 | 0 | break; |
2820 | 0 | } |
2821 | | /* Need max space in case PDU is updated with updated token etc. */ |
2822 | 0 | session->partial_pdu = coap_pdu_init(0, 0, 0, |
2823 | 0 | coap_session_max_pdu_rcv_size(session)); |
2824 | 0 | if (session->partial_pdu == NULL) { |
2825 | 0 | bytes_read = -1; |
2826 | 0 | break; |
2827 | 0 | } |
2828 | 0 | if (session->partial_pdu->alloc_size < size && !coap_pdu_resize(session->partial_pdu, size)) { |
2829 | 0 | bytes_read = -1; |
2830 | 0 | break; |
2831 | 0 | } |
2832 | 0 | session->partial_pdu->hdr_size = (uint8_t)hdr_size; |
2833 | 0 | session->partial_pdu->used_size = size; |
2834 | 0 | memcpy(session->partial_pdu->token - hdr_size, session->read_header, hdr_size + tok_ext_bytes); |
2835 | 0 | session->partial_read = hdr_size + tok_ext_bytes; |
2836 | 0 | if (size == 0) { |
2837 | 0 | coap_pdu_t *pdu = session->partial_pdu; |
2838 | |
|
2839 | 0 | session->partial_pdu = NULL; |
2840 | 0 | session->partial_read = 0; |
2841 | 0 | if (coap_pdu_parse_header(pdu, session->proto)) { |
2842 | 0 | coap_dispatch(ctx, session, pdu); |
2843 | 0 | } |
2844 | 0 | coap_delete_pdu_lkd(pdu); |
2845 | 0 | } |
2846 | 0 | } else { |
2847 | | /* More of the header to go */ |
2848 | 0 | session->partial_read += n; |
2849 | 0 | } |
2850 | 0 | } else { |
2851 | | /* Get in first byte of the header */ |
2852 | 0 | session->read_header[0] = *p++; |
2853 | 0 | bytes_read -= 1; |
2854 | 0 | if (!coap_pdu_parse_header_size(session->proto, |
2855 | 0 | session->read_header)) { |
2856 | 0 | bytes_read = -1; |
2857 | 0 | break; |
2858 | 0 | } |
2859 | 0 | session->partial_read = 1; |
2860 | 0 | } |
2861 | 0 | } |
2862 | 0 | } while (bytes_read == 0 && retry); |
2863 | 0 | if (bytes_read < 0) |
2864 | 0 | coap_session_disconnected_lkd(session, COAP_NACK_NOT_DELIVERABLE); |
2865 | 0 | #endif /* !COAP_DISABLE_TCP */ |
2866 | 0 | } |
2867 | 0 | } |
2868 | | |
2869 | | #if COAP_SERVER_SUPPORT |
2870 | | static int |
2871 | 0 | coap_read_endpoint(coap_context_t *ctx, coap_endpoint_t *endpoint, coap_tick_t now) { |
2872 | 0 | ssize_t bytes_read = -1; |
2873 | 0 | int result = -1; /* the value to be returned */ |
2874 | | #if COAP_CONSTRAINED_STACK |
2875 | | /* payload and e_packet can be protected by global_lock if needed */ |
2876 | | static unsigned char payload[COAP_RXBUFFER_SIZE]; |
2877 | | static coap_packet_t e_packet; |
2878 | | #else /* ! COAP_CONSTRAINED_STACK */ |
2879 | 0 | unsigned char payload[COAP_RXBUFFER_SIZE]; |
2880 | 0 | coap_packet_t e_packet; |
2881 | 0 | #endif /* ! COAP_CONSTRAINED_STACK */ |
2882 | 0 | coap_packet_t *packet = &e_packet; |
2883 | |
|
2884 | 0 | assert(COAP_PROTO_NOT_RELIABLE(endpoint->proto)); |
2885 | 0 | assert(endpoint->sock.flags & COAP_SOCKET_BOUND); |
2886 | | |
2887 | | /* Need to do this as there may be holes in addr_info */ |
2888 | 0 | memset(&packet->addr_info, 0, sizeof(packet->addr_info)); |
2889 | 0 | packet->length = sizeof(payload); |
2890 | 0 | packet->payload = payload; |
2891 | 0 | coap_address_init(&packet->addr_info.remote); |
2892 | 0 | coap_address_copy(&packet->addr_info.local, &endpoint->bind_addr); |
2893 | |
|
2894 | 0 | bytes_read = coap_netif_dgrm_read_ep(endpoint, packet); |
2895 | 0 | if (bytes_read < 0) { |
2896 | 0 | if (errno != EAGAIN) { |
2897 | 0 | coap_log_warn("* %s: read failed\n", coap_endpoint_str(endpoint)); |
2898 | 0 | } |
2899 | 0 | } else if (bytes_read > 0) { |
2900 | 0 | coap_session_t *session = coap_endpoint_get_session(endpoint, packet, now); |
2901 | 0 | if (session) { |
2902 | 0 | coap_session_reference_lkd(session); |
2903 | 0 | coap_log_debug("* %s: netif: recv %4" PRIdS " bytes\n", |
2904 | 0 | coap_session_str(session), bytes_read); |
2905 | 0 | result = coap_handle_dgram_for_proto(ctx, session, packet); |
2906 | 0 | if (endpoint->proto == COAP_PROTO_DTLS && session->type == COAP_SESSION_TYPE_HELLO && result == 1) |
2907 | 0 | coap_session_new_dtls_session(session, now); |
2908 | 0 | coap_session_release_lkd(session); |
2909 | 0 | } |
2910 | 0 | } |
2911 | 0 | return result; |
2912 | 0 | } |
2913 | | |
2914 | | static int |
2915 | 0 | coap_write_endpoint(coap_context_t *ctx, coap_endpoint_t *endpoint, coap_tick_t now) { |
2916 | 0 | (void)ctx; |
2917 | 0 | (void)endpoint; |
2918 | 0 | (void)now; |
2919 | 0 | return 0; |
2920 | 0 | } |
2921 | | |
2922 | | #if !COAP_DISABLE_TCP |
2923 | | static int |
2924 | | coap_accept_endpoint(coap_context_t *ctx, coap_endpoint_t *endpoint, |
2925 | 0 | coap_tick_t now, void *extra) { |
2926 | 0 | coap_session_t *session = coap_new_server_session(ctx, endpoint, extra); |
2927 | 0 | if (session) |
2928 | 0 | session->last_rx_tx = now; |
2929 | 0 | return session != NULL; |
2930 | 0 | } |
2931 | | #endif /* !COAP_DISABLE_TCP */ |
2932 | | #endif /* COAP_SERVER_SUPPORT */ |
2933 | | |
2934 | | COAP_API void |
2935 | 0 | coap_io_do_io(coap_context_t *ctx, coap_tick_t now) { |
2936 | 0 | coap_lock_lock(return); |
2937 | 0 | coap_io_do_io_lkd(ctx, now); |
2938 | 0 | coap_lock_unlock(); |
2939 | 0 | } |
2940 | | |
2941 | | void |
2942 | 0 | coap_io_do_io_lkd(coap_context_t *ctx, coap_tick_t now) { |
2943 | 0 | #ifdef COAP_EPOLL_SUPPORT |
2944 | 0 | (void)ctx; |
2945 | 0 | (void)now; |
2946 | 0 | coap_log_emerg("coap_io_do_io() requires libcoap not compiled for using epoll\n"); |
2947 | | #else /* ! COAP_EPOLL_SUPPORT */ |
2948 | | coap_session_t *s, *rtmp; |
2949 | | |
2950 | | coap_lock_check_locked(); |
2951 | | #if COAP_SERVER_SUPPORT |
2952 | | coap_endpoint_t *ep, *tmp; |
2953 | | LL_FOREACH_SAFE(ctx->endpoint, ep, tmp) { |
2954 | | if ((ep->sock.flags & COAP_SOCKET_CAN_READ) != 0) |
2955 | | coap_read_endpoint(ctx, ep, now); |
2956 | | if ((ep->sock.flags & COAP_SOCKET_CAN_WRITE) != 0) |
2957 | | coap_write_endpoint(ctx, ep, now); |
2958 | | #if !COAP_DISABLE_TCP |
2959 | | if ((ep->sock.flags & COAP_SOCKET_CAN_ACCEPT) != 0) |
2960 | | coap_accept_endpoint(ctx, ep, now, NULL); |
2961 | | #endif /* !COAP_DISABLE_TCP */ |
2962 | | SESSIONS_ITER_SAFE(ep->sessions, s, rtmp) { |
2963 | | /* Make sure the session object is not deleted in one of the callbacks */ |
2964 | | coap_session_reference_lkd(s); |
2965 | | #if COAP_CLIENT_SUPPORT |
2966 | | if (s->client_initiated && (s->sock.flags & COAP_SOCKET_CAN_CONNECT) != 0) { |
2967 | | coap_connect_session(s, now); |
2968 | | } |
2969 | | #endif /* COAP_CLIENT_SUPPORT */ |
2970 | | if ((s->sock.flags & COAP_SOCKET_CAN_READ) != 0) { |
2971 | | coap_read_session(ctx, s, now); |
2972 | | } |
2973 | | if ((s->sock.flags & COAP_SOCKET_CAN_WRITE) != 0) { |
2974 | | coap_write_session(ctx, s, now); |
2975 | | } |
2976 | | coap_session_release_lkd(s); |
2977 | | } |
2978 | | } |
2979 | | #endif /* COAP_SERVER_SUPPORT */ |
2980 | | |
2981 | | #if COAP_CLIENT_SUPPORT |
2982 | | SESSIONS_ITER_SAFE(ctx->sessions, s, rtmp) { |
2983 | | /* Make sure the session object is not deleted in one of the callbacks */ |
2984 | | coap_session_reference_lkd(s); |
2985 | | if ((s->sock.flags & COAP_SOCKET_CAN_CONNECT) != 0) { |
2986 | | coap_connect_session(s, now); |
2987 | | } |
2988 | | if ((s->sock.flags & COAP_SOCKET_CAN_READ) != 0 && s->ref > 1) { |
2989 | | coap_read_session(ctx, s, now); |
2990 | | } |
2991 | | if ((s->sock.flags & COAP_SOCKET_CAN_WRITE) != 0 && s->ref > 1) { |
2992 | | coap_write_session(ctx, s, now); |
2993 | | } |
2994 | | coap_session_release_lkd(s); |
2995 | | } |
2996 | | #endif /* COAP_CLIENT_SUPPORT */ |
2997 | | #endif /* ! COAP_EPOLL_SUPPORT */ |
2998 | 0 | } |
2999 | | |
3000 | | COAP_API void |
3001 | 0 | coap_io_do_epoll(coap_context_t *ctx, struct epoll_event *events, size_t nevents) { |
3002 | 0 | coap_lock_lock(return); |
3003 | 0 | coap_io_do_epoll_lkd(ctx, events, nevents); |
3004 | 0 | coap_lock_unlock(); |
3005 | 0 | } |
3006 | | |
3007 | | /* |
3008 | | * While this code in part replicates coap_io_do_io_lkd(), doing the functions |
3009 | | * directly saves having to iterate through the endpoints / sessions. |
3010 | | */ |
3011 | | void |
3012 | 0 | coap_io_do_epoll_lkd(coap_context_t *ctx, struct epoll_event *events, size_t nevents) { |
3013 | | #ifndef COAP_EPOLL_SUPPORT |
3014 | | (void)ctx; |
3015 | | (void)events; |
3016 | | (void)nevents; |
3017 | | coap_log_emerg("coap_io_do_epoll() requires libcoap compiled for using epoll\n"); |
3018 | | #else /* COAP_EPOLL_SUPPORT */ |
3019 | 0 | coap_tick_t now; |
3020 | 0 | size_t j; |
3021 | |
|
3022 | 0 | coap_lock_check_locked(); |
3023 | 0 | coap_ticks(&now); |
3024 | 0 | for (j = 0; j < nevents; j++) { |
3025 | 0 | coap_socket_t *sock = (coap_socket_t *)events[j].data.ptr; |
3026 | | |
3027 | | /* Ignore 'timer trigger' ptr which is NULL */ |
3028 | 0 | if (sock) { |
3029 | 0 | #if COAP_SERVER_SUPPORT |
3030 | 0 | if (sock->endpoint) { |
3031 | 0 | coap_endpoint_t *endpoint = sock->endpoint; |
3032 | 0 | if ((sock->flags & COAP_SOCKET_WANT_READ) && |
3033 | 0 | (events[j].events & (EPOLLIN|EPOLLERR|EPOLLHUP|EPOLLRDHUP))) { |
3034 | 0 | sock->flags |= COAP_SOCKET_CAN_READ; |
3035 | 0 | coap_read_endpoint(endpoint->context, endpoint, now); |
3036 | 0 | } |
3037 | |
|
3038 | 0 | if ((sock->flags & COAP_SOCKET_WANT_WRITE) && |
3039 | 0 | (events[j].events & (EPOLLOUT|EPOLLERR|EPOLLHUP|EPOLLRDHUP))) { |
3040 | | /* |
3041 | | * Need to update this to EPOLLIN as EPOLLOUT will normally always |
3042 | | * be true causing epoll_wait to return early |
3043 | | */ |
3044 | 0 | coap_epoll_ctl_mod(sock, EPOLLIN, __func__); |
3045 | 0 | sock->flags |= COAP_SOCKET_CAN_WRITE; |
3046 | 0 | coap_write_endpoint(endpoint->context, endpoint, now); |
3047 | 0 | } |
3048 | |
|
3049 | 0 | #if !COAP_DISABLE_TCP |
3050 | 0 | if ((sock->flags & COAP_SOCKET_WANT_ACCEPT) && |
3051 | 0 | (events[j].events & EPOLLIN)) { |
3052 | 0 | sock->flags |= COAP_SOCKET_CAN_ACCEPT; |
3053 | 0 | coap_accept_endpoint(endpoint->context, endpoint, now, NULL); |
3054 | 0 | } |
3055 | 0 | #endif /* !COAP_DISABLE_TCP */ |
3056 | |
|
3057 | 0 | } else |
3058 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
3059 | 0 | if (sock->session) { |
3060 | 0 | coap_session_t *session = sock->session; |
3061 | | |
3062 | | /* Make sure the session object is not deleted |
3063 | | in one of the callbacks */ |
3064 | 0 | coap_session_reference_lkd(session); |
3065 | 0 | #if COAP_CLIENT_SUPPORT |
3066 | 0 | if ((sock->flags & COAP_SOCKET_WANT_CONNECT) && |
3067 | 0 | (events[j].events & (EPOLLOUT|EPOLLERR|EPOLLHUP|EPOLLRDHUP))) { |
3068 | 0 | sock->flags |= COAP_SOCKET_CAN_CONNECT; |
3069 | 0 | coap_connect_session(session, now); |
3070 | 0 | if (coap_netif_available(session) && |
3071 | 0 | !(sock->flags & COAP_SOCKET_WANT_WRITE)) { |
3072 | 0 | coap_epoll_ctl_mod(sock, EPOLLIN, __func__); |
3073 | 0 | } |
3074 | 0 | } |
3075 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
3076 | |
|
3077 | 0 | if ((sock->flags & COAP_SOCKET_WANT_READ) && |
3078 | 0 | (events[j].events & (EPOLLIN|EPOLLERR|EPOLLHUP|EPOLLRDHUP))) { |
3079 | 0 | sock->flags |= COAP_SOCKET_CAN_READ; |
3080 | 0 | coap_read_session(session->context, session, now); |
3081 | 0 | } |
3082 | |
|
3083 | 0 | if ((sock->flags & COAP_SOCKET_WANT_WRITE) && |
3084 | 0 | (events[j].events & (EPOLLOUT|EPOLLERR|EPOLLHUP|EPOLLRDHUP))) { |
3085 | | /* |
3086 | | * Need to update this to EPOLLIN as EPOLLOUT will normally always |
3087 | | * be true causing epoll_wait to return early |
3088 | | */ |
3089 | 0 | coap_epoll_ctl_mod(sock, EPOLLIN, __func__); |
3090 | 0 | sock->flags |= COAP_SOCKET_CAN_WRITE; |
3091 | 0 | coap_write_session(session->context, session, now); |
3092 | 0 | } |
3093 | | /* Now dereference session so it can go away if needed */ |
3094 | 0 | coap_session_release_lkd(session); |
3095 | 0 | } |
3096 | 0 | } else if (ctx->eptimerfd != -1) { |
3097 | | /* |
3098 | | * 'timer trigger' must have fired. eptimerfd needs to be read to clear |
3099 | | * it so that it does not set EPOLLIN in the next epoll_wait(). |
3100 | | */ |
3101 | 0 | uint64_t count; |
3102 | | |
3103 | | /* Check the result from read() to suppress the warning on |
3104 | | * systems that declare read() with warn_unused_result. */ |
3105 | 0 | if (read(ctx->eptimerfd, &count, sizeof(count)) == -1) { |
3106 | 0 | /* do nothing */; |
3107 | 0 | } |
3108 | 0 | } |
3109 | 0 | } |
3110 | | /* And update eptimerfd as to when to next trigger */ |
3111 | 0 | coap_ticks(&now); |
3112 | 0 | coap_io_prepare_epoll_lkd(ctx, now); |
3113 | 0 | #endif /* COAP_EPOLL_SUPPORT */ |
3114 | 0 | } |
3115 | | |
3116 | | int |
3117 | | coap_handle_dgram(coap_context_t *ctx, coap_session_t *session, |
3118 | 28 | uint8_t *msg, size_t msg_len) { |
3119 | | |
3120 | 28 | coap_pdu_t *pdu = NULL; |
3121 | 28 | coap_opt_filter_t error_opts; |
3122 | | |
3123 | 28 | assert(COAP_PROTO_NOT_RELIABLE(session->proto)); |
3124 | 28 | if (msg_len < 4) { |
3125 | | /* Minimum size of CoAP header - ignore runt */ |
3126 | 0 | return -1; |
3127 | 0 | } |
3128 | 28 | if ((msg[0] >> 6) != COAP_DEFAULT_VERSION) { |
3129 | | /* |
3130 | | * As per https://datatracker.ietf.org/doc/html/rfc7252#section-3, |
3131 | | * this MUST be silently ignored. |
3132 | | */ |
3133 | 5 | coap_log_debug("coap_handle_dgram: UDP version not supported\n"); |
3134 | 5 | return -1; |
3135 | 5 | } |
3136 | | |
3137 | | /* Need max space in case PDU is updated with updated token etc. */ |
3138 | 23 | pdu = coap_pdu_init(0, 0, 0, coap_session_max_pdu_rcv_size(session)); |
3139 | 23 | if (!pdu) |
3140 | 0 | goto error; |
3141 | | |
3142 | 23 | coap_option_filter_clear(&error_opts); |
3143 | 23 | if (!coap_pdu_parse2(session->proto, msg, msg_len, pdu, &error_opts)) { |
3144 | 15 | coap_handle_event_lkd(session->context, COAP_EVENT_BAD_PACKET, session); |
3145 | 15 | coap_log_warn("discard malformed PDU\n"); |
3146 | 15 | if (error_opts.mask && COAP_PDU_IS_REQUEST(pdu)) { |
3147 | 1 | coap_pdu_t *response = |
3148 | 1 | coap_new_error_response(pdu, |
3149 | 1 | COAP_RESPONSE_CODE(402), &error_opts); |
3150 | 1 | if (!response) { |
3151 | 0 | coap_log_warn("coap_handle_dgram: cannot create error response\n"); |
3152 | 1 | } else { |
3153 | 1 | if (coap_send_internal(session, response, NULL) == COAP_INVALID_MID) |
3154 | 1 | coap_log_warn("coap_handle_dgram: error sending response\n"); |
3155 | 1 | } |
3156 | 1 | coap_delete_pdu_lkd(pdu); |
3157 | 1 | return -1; |
3158 | 14 | } else { |
3159 | 14 | goto error; |
3160 | 14 | } |
3161 | 15 | } |
3162 | | |
3163 | 8 | if (coap_debug_recv_packet()) { |
3164 | 8 | coap_dispatch(ctx, session, pdu); |
3165 | 8 | } else { |
3166 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
3167 | 0 | } |
3168 | 8 | coap_delete_pdu_lkd(pdu); |
3169 | 8 | return 0; |
3170 | | |
3171 | 14 | error: |
3172 | | /* |
3173 | | * https://rfc-editor.org/rfc/rfc7252#section-4.2 MUST send RST |
3174 | | * https://rfc-editor.org/rfc/rfc7252#section-4.3 MAY send RST |
3175 | | */ |
3176 | 14 | coap_send_rst_lkd(session, pdu); |
3177 | 14 | coap_delete_pdu_lkd(pdu); |
3178 | 14 | return -1; |
3179 | 23 | } |
3180 | | |
3181 | | coap_queue_t * |
3182 | 0 | coap_remove_mid_from_delayq(coap_session_t *session, coap_mid_t mid) { |
3183 | 0 | coap_queue_t *p = NULL; |
3184 | 0 | coap_queue_t *q; |
3185 | |
|
3186 | 0 | LL_FOREACH(session->delayqueue, q) { |
3187 | 0 | if (q->id == mid) { |
3188 | 0 | if (p) { |
3189 | 0 | p->next = q->next; |
3190 | 0 | } else { |
3191 | 0 | session->delayqueue = q->next; |
3192 | 0 | } |
3193 | 0 | if (session->delayqueue_tail == q) |
3194 | 0 | session->delayqueue_tail = p; |
3195 | 0 | q->next = NULL; |
3196 | 0 | return q; |
3197 | 0 | } |
3198 | 0 | p = q; |
3199 | 0 | } |
3200 | 0 | return NULL; |
3201 | 0 | } |
3202 | | |
3203 | | coap_queue_t * |
3204 | 238 | coap_remove_first_from_delayq(coap_session_t *session) { |
3205 | 238 | coap_queue_t *q = session->delayqueue; |
3206 | | |
3207 | 238 | if (q) { |
3208 | 0 | session->delayqueue = q->next; |
3209 | 0 | if (session->delayqueue == NULL) |
3210 | 0 | session->delayqueue_tail = NULL; |
3211 | 0 | q->next = NULL; |
3212 | 0 | } |
3213 | 238 | return q; |
3214 | 238 | } |
3215 | | |
3216 | | void |
3217 | 0 | coap_add_to_tail_delayq(coap_session_t *session, coap_queue_t *node) { |
3218 | 0 | node->next = NULL; |
3219 | 0 | if (session->delayqueue_tail) { |
3220 | 0 | session->delayqueue_tail->next = node; |
3221 | 0 | } else { |
3222 | 0 | session->delayqueue = node; |
3223 | 0 | } |
3224 | 0 | session->delayqueue_tail = node; |
3225 | 0 | } |
3226 | | |
3227 | | void |
3228 | 0 | coap_add_to_head_delayq(coap_session_t *session, coap_queue_t *node) { |
3229 | 0 | node->next = session->delayqueue; |
3230 | 0 | session->delayqueue = node; |
3231 | 0 | if (node->next == NULL) |
3232 | 0 | session->delayqueue_tail = node; |
3233 | 0 | } |
3234 | | |
3235 | | int |
3236 | | coap_remove_from_queue(coap_queue_t **queue, coap_session_t *session, coap_mid_t mid, |
3237 | 1 | coap_bin_const_t *token, coap_queue_t **node) { |
3238 | 1 | coap_queue_t *p, *q; |
3239 | | |
3240 | 1 | if (!queue || !*queue) { |
3241 | 1 | *node = NULL; |
3242 | 1 | return 0; |
3243 | 1 | } |
3244 | | |
3245 | | /* replace queue head if PDU's time is less than head's time */ |
3246 | | |
3247 | 0 | if (session == (*queue)->session && mid == (*queue)->id && |
3248 | 0 | (!token || coap_binary_equal(token, &(*queue)->pdu->actual_token))) { /* found message id */ |
3249 | 0 | *node = *queue; |
3250 | 0 | *queue = (*queue)->next; |
3251 | 0 | if (*queue) { /* adjust relative time of new queue head */ |
3252 | 0 | (*queue)->t += (*node)->t; |
3253 | 0 | } |
3254 | 0 | (*node)->next = NULL; |
3255 | 0 | coap_log_debug("** %s: mid=0x%04x: removed (1)\n", |
3256 | 0 | coap_session_str(session), mid); |
3257 | 0 | return 1; |
3258 | 0 | } |
3259 | | |
3260 | | /* search message id in queue to remove (only first occurrence will be removed) */ |
3261 | 0 | q = *queue; |
3262 | 0 | do { |
3263 | 0 | p = q; |
3264 | 0 | q = q->next; |
3265 | 0 | } while (q && (session != q->session || mid != q->id || |
3266 | 0 | (token && ! coap_binary_equal(token, &q->pdu->actual_token)))); |
3267 | |
|
3268 | 0 | if (q) { /* found message id */ |
3269 | 0 | p->next = q->next; |
3270 | 0 | if (p->next) { /* must update relative time of p->next */ |
3271 | 0 | p->next->t += q->t; |
3272 | 0 | } |
3273 | 0 | q->next = NULL; |
3274 | 0 | *node = q; |
3275 | 0 | coap_log_debug("** %s: mid=0x%04x: removed (2)\n", |
3276 | 0 | coap_session_str(session), mid); |
3277 | 0 | return 1; |
3278 | 0 | } |
3279 | | |
3280 | 0 | *node = NULL; |
3281 | 0 | return 0; |
3282 | |
|
3283 | 0 | } |
3284 | | |
3285 | | static int |
3286 | | coap_remove_from_queue_token(coap_queue_t **queue, coap_session_t *session, |
3287 | 78 | coap_bin_const_t *token, coap_queue_t **node) { |
3288 | 78 | coap_queue_t *p, *q; |
3289 | | |
3290 | 78 | if (!queue || !*queue) |
3291 | 78 | return 0; |
3292 | | |
3293 | | /* replace queue head if PDU's time is less than head's time */ |
3294 | | |
3295 | 0 | if (session == (*queue)->session && |
3296 | 0 | (!token || coap_binary_equal(&(*queue)->pdu->actual_token, token))) { /* found token */ |
3297 | 0 | *node = *queue; |
3298 | 0 | *queue = (*queue)->next; |
3299 | 0 | if (*queue) { /* adjust relative time of new queue head */ |
3300 | 0 | (*queue)->t += (*node)->t; |
3301 | 0 | } |
3302 | 0 | (*node)->next = NULL; |
3303 | 0 | coap_log_debug("** %s: mid=0x%04x: removed (7)\n", |
3304 | 0 | coap_session_str(session), (*node)->id); |
3305 | 0 | if ((*node)->pdu->type == COAP_MESSAGE_CON && session->con_active) { |
3306 | 0 | session->con_active--; |
3307 | 0 | if (session->state == COAP_SESSION_STATE_ESTABLISHED) |
3308 | | /* Flush out any entries on session->delayqueue */ |
3309 | 0 | coap_session_connected(session); |
3310 | 0 | } |
3311 | 0 | return 1; |
3312 | 0 | } |
3313 | | |
3314 | | /* search token in queue to remove (only first occurrence will be removed) */ |
3315 | 0 | q = *queue; |
3316 | 0 | do { |
3317 | 0 | p = q; |
3318 | 0 | q = q->next; |
3319 | 0 | } while (q && (session != q->session || |
3320 | 0 | !(!token || coap_binary_equal(&q->pdu->actual_token, token)))); |
3321 | |
|
3322 | 0 | if (q) { /* found token */ |
3323 | 0 | p->next = q->next; |
3324 | 0 | if (p->next) { /* must update relative time of p->next */ |
3325 | 0 | p->next->t += q->t; |
3326 | 0 | } |
3327 | 0 | q->next = NULL; |
3328 | 0 | *node = q; |
3329 | 0 | coap_log_debug("** %s: mid=0x%04x: removed (8)\n", |
3330 | 0 | coap_session_str(session), (*node)->id); |
3331 | 0 | if (q->pdu->type == COAP_MESSAGE_CON && session->con_active) { |
3332 | 0 | session->con_active--; |
3333 | 0 | if (session->state == COAP_SESSION_STATE_ESTABLISHED) |
3334 | | /* Flush out any entries on session->delayqueue */ |
3335 | 0 | coap_session_connected(session); |
3336 | 0 | } |
3337 | 0 | return 1; |
3338 | 0 | } |
3339 | | |
3340 | 0 | return 0; |
3341 | |
|
3342 | 0 | } |
3343 | | |
3344 | | void |
3345 | | coap_cancel_session_messages(coap_context_t *context, coap_session_t *session, |
3346 | 103 | coap_nack_reason_t reason) { |
3347 | 103 | coap_queue_t *p, *q; |
3348 | | |
3349 | 103 | while (context->sendqueue && context->sendqueue->session == session) { |
3350 | 0 | q = context->sendqueue; |
3351 | 0 | context->sendqueue = q->next; |
3352 | 0 | coap_log_debug("** %s: mid=0x%04x: removed (3)\n", |
3353 | 0 | coap_session_str(session), q->id); |
3354 | 0 | if (q->pdu->type == COAP_MESSAGE_CON) { |
3355 | 0 | coap_handle_nack(session, q->pdu, reason, q->id); |
3356 | 0 | } |
3357 | 0 | coap_delete_node_lkd(q); |
3358 | 0 | } |
3359 | | |
3360 | 103 | if (!context->sendqueue) |
3361 | 103 | return; |
3362 | | |
3363 | 0 | p = context->sendqueue; |
3364 | 0 | q = p->next; |
3365 | |
|
3366 | 0 | while (q) { |
3367 | 0 | if (q->session == session) { |
3368 | 0 | p->next = q->next; |
3369 | 0 | coap_log_debug("** %s: mid=0x%04x: removed (4)\n", |
3370 | 0 | coap_session_str(session), q->id); |
3371 | 0 | if (q->pdu->type == COAP_MESSAGE_CON) { |
3372 | 0 | coap_handle_nack(session, q->pdu, reason, q->id); |
3373 | 0 | } |
3374 | 0 | coap_delete_node_lkd(q); |
3375 | 0 | q = p->next; |
3376 | 0 | } else { |
3377 | 0 | p = q; |
3378 | 0 | q = q->next; |
3379 | 0 | } |
3380 | 0 | } |
3381 | 0 | } |
3382 | | |
3383 | | void |
3384 | | coap_cancel_all_messages(coap_context_t *context, coap_session_t *session, |
3385 | 0 | coap_bin_const_t *token) { |
3386 | | /* cancel all messages in sendqueue that belong to session |
3387 | | * and use the specified token */ |
3388 | 0 | coap_queue_t **p, *q; |
3389 | |
|
3390 | 0 | if (!context->sendqueue) |
3391 | 0 | return; |
3392 | | |
3393 | 0 | p = &context->sendqueue; |
3394 | 0 | q = *p; |
3395 | |
|
3396 | 0 | while (q) { |
3397 | 0 | if (q->session == session && |
3398 | 0 | (!token || coap_binary_equal(&q->pdu->actual_token, token))) { |
3399 | 0 | *p = q->next; |
3400 | 0 | coap_log_debug("** %s: mid=0x%04x: removed (6)\n", |
3401 | 0 | coap_session_str(session), q->id); |
3402 | 0 | if (q->pdu->type == COAP_MESSAGE_CON && session->con_active) { |
3403 | 0 | session->con_active--; |
3404 | 0 | if (session->state == COAP_SESSION_STATE_ESTABLISHED) |
3405 | | /* Flush out any entries on session->delayqueue */ |
3406 | 0 | coap_session_connected(session); |
3407 | 0 | } |
3408 | 0 | coap_delete_node_lkd(q); |
3409 | 0 | } else { |
3410 | 0 | p = &(q->next); |
3411 | 0 | } |
3412 | 0 | q = *p; |
3413 | 0 | } |
3414 | 0 | } |
3415 | | |
3416 | | coap_pdu_t * |
3417 | | coap_new_error_response(const coap_pdu_t *request, coap_pdu_code_t code, |
3418 | 27 | coap_opt_filter_t *opts) { |
3419 | 27 | coap_opt_iterator_t opt_iter; |
3420 | 27 | coap_pdu_t *response; |
3421 | 27 | unsigned char type; |
3422 | | |
3423 | 27 | #if COAP_ERROR_PHRASE_LENGTH > 0 |
3424 | 27 | const char *phrase; |
3425 | 27 | if (code != COAP_RESPONSE_CODE(508)) { |
3426 | 27 | phrase = coap_response_phrase(code); |
3427 | 27 | } else { |
3428 | 0 | phrase = NULL; |
3429 | 0 | } |
3430 | 27 | #endif |
3431 | | |
3432 | 27 | assert(request); |
3433 | | |
3434 | 27 | if (request->type != COAP_MESSAGE_NON && request->type != COAP_MESSAGE_CON) |
3435 | 0 | return NULL; |
3436 | | |
3437 | | /* cannot send ACK if original request was not confirmable */ |
3438 | 27 | type = request->type == COAP_MESSAGE_CON ? |
3439 | 19 | COAP_MESSAGE_ACK : COAP_MESSAGE_NON; |
3440 | | |
3441 | | /* Now create the response and fill with options and payload data. */ |
3442 | 27 | response = coap_pdu_init(type, code, request->mid, |
3443 | 27 | request->session ? |
3444 | 26 | coap_session_max_pdu_size_lkd(request->session) : 512); |
3445 | 27 | if (response) { |
3446 | | /* copy token */ |
3447 | 27 | if (request->actual_token.length && |
3448 | 20 | !coap_add_token(response, request->actual_token.length, |
3449 | 20 | request->actual_token.s)) { |
3450 | 0 | coap_log_debug("cannot add token to error response\n"); |
3451 | 0 | coap_delete_pdu_lkd(response); |
3452 | 0 | return NULL; |
3453 | 0 | } |
3454 | 27 | if (response->code == COAP_RESPONSE_CODE(402)) { |
3455 | 2 | char buf[128]; |
3456 | 2 | int first = 1; |
3457 | 2 | int i; |
3458 | 2 | size_t len; |
3459 | | |
3460 | 2 | #if COAP_ERROR_PHRASE_LENGTH > 0 |
3461 | 2 | snprintf(buf, sizeof(buf), "%s", phrase ? phrase : ""); |
3462 | | #else |
3463 | | buf[0] = '\000'; |
3464 | | #endif |
3465 | | /* copy all reported options into diagnostic message */ |
3466 | 14 | for (i = COAP_OPT_FILTER_SHORT - 1; i >= 0; i--) { |
3467 | 12 | if (opts->mask & (1 << (COAP_OPT_FILTER_LONG + i))) { |
3468 | 3 | len = strlen(buf); |
3469 | 3 | snprintf(&buf[len], sizeof(buf) - len, "%s%d", first ? " " : ",", |
3470 | 3 | opts->short_opts[i]); |
3471 | 3 | first = 0; |
3472 | 3 | } |
3473 | 12 | } |
3474 | 6 | for (i = COAP_OPT_FILTER_LONG - 1; i >= 0; i--) { |
3475 | 4 | if (opts->mask & (1 << i)) { |
3476 | 0 | len = strlen(buf); |
3477 | 0 | snprintf(&buf[len], sizeof(buf) - len, "%s%d", first ? " " : ",", |
3478 | 0 | opts->long_opts[i]); |
3479 | 0 | first = 0; |
3480 | 0 | } |
3481 | 4 | } |
3482 | 2 | coap_add_data(response, (size_t)strlen(buf), (const uint8_t *)buf); |
3483 | 25 | } else if (opts && opts->mask) { |
3484 | 0 | coap_opt_t *option; |
3485 | | |
3486 | | /* copy all options */ |
3487 | 0 | coap_option_iterator_init(request, &opt_iter, opts); |
3488 | 0 | while ((option = coap_option_next(&opt_iter))) { |
3489 | 0 | coap_add_option_internal(response, opt_iter.number, |
3490 | 0 | coap_opt_length(option), |
3491 | 0 | coap_opt_value(option)); |
3492 | 0 | } |
3493 | 0 | #if COAP_ERROR_PHRASE_LENGTH > 0 |
3494 | 0 | if (phrase) |
3495 | 0 | coap_add_data(response, (size_t)strlen(phrase), (const uint8_t *)phrase); |
3496 | 25 | } else { |
3497 | | /* note that diagnostic messages do not need a Content-Format option. */ |
3498 | 25 | if (phrase) |
3499 | 25 | coap_add_data(response, (size_t)strlen(phrase), (const uint8_t *)phrase); |
3500 | 25 | #endif |
3501 | 25 | } |
3502 | 27 | } |
3503 | | |
3504 | 27 | return response; |
3505 | 27 | } |
3506 | | |
3507 | | #if COAP_SERVER_SUPPORT |
3508 | | #define SZX_TO_BYTES(SZX) ((size_t)(1 << ((SZX) + 4))) |
3509 | | |
3510 | | static void |
3511 | 0 | free_wellknown_response(coap_session_t *session COAP_UNUSED, void *app_ptr) { |
3512 | 0 | coap_delete_string(app_ptr); |
3513 | 0 | } |
3514 | | |
3515 | | /* |
3516 | | * Caution: As this handler is in libcoap space, it is called with |
3517 | | * context locked. |
3518 | | */ |
3519 | | static void |
3520 | | hnd_get_wellknown_lkd(coap_resource_t *resource, |
3521 | | coap_session_t *session, |
3522 | | const coap_pdu_t *request, |
3523 | | const coap_string_t *query, |
3524 | 0 | coap_pdu_t *response) { |
3525 | 0 | size_t len = 0; |
3526 | 0 | coap_string_t *data_string = NULL; |
3527 | 0 | coap_print_status_t result = 0; |
3528 | 0 | size_t wkc_len = 0; |
3529 | 0 | uint8_t buf[4]; |
3530 | | |
3531 | | /* |
3532 | | * Quick hack to determine the size of the resource descriptions for |
3533 | | * .well-known/core. |
3534 | | */ |
3535 | 0 | result = coap_print_wellknown_lkd(session->context, buf, &wkc_len, UINT_MAX, query); |
3536 | 0 | if (result & COAP_PRINT_STATUS_ERROR) { |
3537 | 0 | coap_log_warn("cannot determine length of /.well-known/core\n"); |
3538 | 0 | goto error; |
3539 | 0 | } |
3540 | | |
3541 | 0 | if (wkc_len > 0) { |
3542 | 0 | data_string = coap_new_string(wkc_len); |
3543 | 0 | if (!data_string) |
3544 | 0 | goto error; |
3545 | | |
3546 | 0 | len = wkc_len; |
3547 | 0 | result = coap_print_wellknown_lkd(session->context, data_string->s, &len, 0, query); |
3548 | 0 | if ((result & COAP_PRINT_STATUS_ERROR) != 0) { |
3549 | 0 | coap_log_debug("coap_print_wellknown failed\n"); |
3550 | 0 | goto error; |
3551 | 0 | } |
3552 | 0 | assert(len <= (size_t)wkc_len); |
3553 | 0 | data_string->length = len; |
3554 | |
|
3555 | 0 | if (!(session->block_mode & COAP_BLOCK_USE_LIBCOAP)) { |
3556 | 0 | if (!coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT, |
3557 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
3558 | 0 | COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), buf)) { |
3559 | 0 | goto error; |
3560 | 0 | } |
3561 | 0 | if (response->used_size + len + 1 > response->max_size) { |
3562 | | /* |
3563 | | * Data does not fit into a packet and no libcoap block support |
3564 | | * +1 for end of options marker |
3565 | | */ |
3566 | 0 | coap_log_debug(".well-known/core: truncating data length to %" PRIuS " from %" PRIuS "\n", |
3567 | 0 | len, response->max_size - response->used_size - 1); |
3568 | 0 | len = response->max_size - response->used_size - 1; |
3569 | 0 | } |
3570 | 0 | if (!coap_add_data(response, len, data_string->s)) { |
3571 | 0 | goto error; |
3572 | 0 | } |
3573 | 0 | free_wellknown_response(session, data_string); |
3574 | 0 | } else if (!coap_add_data_large_response_lkd(resource, session, request, |
3575 | 0 | response, query, |
3576 | 0 | COAP_MEDIATYPE_APPLICATION_LINK_FORMAT, |
3577 | 0 | -1, 0, data_string->length, |
3578 | 0 | data_string->s, |
3579 | 0 | free_wellknown_response, |
3580 | 0 | data_string)) { |
3581 | 0 | goto error_released; |
3582 | 0 | } |
3583 | 0 | } else { |
3584 | 0 | if (!coap_insert_option(response, COAP_OPTION_CONTENT_FORMAT, |
3585 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
3586 | 0 | COAP_MEDIATYPE_APPLICATION_LINK_FORMAT), buf)) { |
3587 | 0 | goto error; |
3588 | 0 | } |
3589 | 0 | } |
3590 | 0 | response->code = COAP_RESPONSE_CODE(205); |
3591 | 0 | return; |
3592 | | |
3593 | 0 | error: |
3594 | 0 | free_wellknown_response(session, data_string); |
3595 | 0 | error_released: |
3596 | 0 | if (response->code == 0) { |
3597 | | /* set error code 5.03 and remove all options and data from response */ |
3598 | 0 | response->code = COAP_RESPONSE_CODE(503); |
3599 | 0 | response->used_size = response->e_token_length; |
3600 | 0 | response->data = NULL; |
3601 | 0 | } |
3602 | 0 | } |
3603 | | #endif /* COAP_SERVER_SUPPORT */ |
3604 | | |
3605 | | /** |
3606 | | * This function cancels outstanding messages for the session and |
3607 | | * token specified in @p sent. Any observation relationship for |
3608 | | * sent->session and the token are removed. Calling this function is |
3609 | | * required when receiving an RST message (usually in response to a |
3610 | | * notification) or a GET request with the Observe option set to 1. |
3611 | | * |
3612 | | * This function returns @c 0 when the token is unknown with this |
3613 | | * peer, or a value greater than zero otherwise. |
3614 | | */ |
3615 | | static int |
3616 | 0 | coap_cancel(coap_context_t *context, const coap_queue_t *sent) { |
3617 | 0 | int num_cancelled = 0; /* the number of observers cancelled */ |
3618 | |
|
3619 | | #ifndef COAP_SERVER_SUPPORT |
3620 | | (void)sent; |
3621 | | #endif /* ! COAP_SERVER_SUPPORT */ |
3622 | 0 | (void)context; |
3623 | |
|
3624 | 0 | #if COAP_SERVER_SUPPORT |
3625 | | /* remove observer for this resource, if any |
3626 | | * Use token from sent and try to find a matching resource. Uh! |
3627 | | */ |
3628 | 0 | RESOURCES_ITER(context->resources, r) { |
3629 | 0 | coap_cancel_all_messages(context, sent->session, &sent->pdu->actual_token); |
3630 | 0 | num_cancelled += coap_delete_observer(r, sent->session, &sent->pdu->actual_token); |
3631 | 0 | } |
3632 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
3633 | |
|
3634 | 0 | return num_cancelled; |
3635 | 0 | } |
3636 | | |
3637 | | #if COAP_SERVER_SUPPORT |
3638 | | /** |
3639 | | * Internal flags to control the treatment of responses (specifically |
3640 | | * in presence of the No-Response option). |
3641 | | */ |
3642 | | enum respond_t { RESPONSE_DEFAULT, RESPONSE_DROP, RESPONSE_SEND }; |
3643 | | |
3644 | | /* |
3645 | | * Checks for No-Response option in given @p request and |
3646 | | * returns @c RESPONSE_DROP if @p response should be suppressed |
3647 | | * according to RFC 7967. |
3648 | | * |
3649 | | * If the response is a confirmable piggybacked response and RESPONSE_DROP, |
3650 | | * change it to an empty ACK and @c RESPONSE_SEND so the client does not keep |
3651 | | * on retrying. |
3652 | | * |
3653 | | * Checks if the response code is 0.00 and if the response is confirmable, |
3654 | | * non-confirmable, or the session is reliable, @c RESPONSE_DROP is also |
3655 | | * returned. An Empty confirmable message is a ping, not a response. |
3656 | | * |
3657 | | * Multicast response checking is also carried out. |
3658 | | * |
3659 | | * NOTE: It is the responsibility of the application to determine whether |
3660 | | * a delayed separate response should be sent as the original requesting packet |
3661 | | * containing the No-Response option has long since gone. |
3662 | | * |
3663 | | * The value of the No-Response option is encoded as |
3664 | | * follows: |
3665 | | * |
3666 | | * @verbatim |
3667 | | * +-------+-----------------------+-----------------------------------+ |
3668 | | * | Value | Binary Representation | Description | |
3669 | | * +-------+-----------------------+-----------------------------------+ |
3670 | | * | 0 | <empty> | Interested in all responses. | |
3671 | | * +-------+-----------------------+-----------------------------------+ |
3672 | | * | 2 | 00000010 | Not interested in 2.xx responses. | |
3673 | | * +-------+-----------------------+-----------------------------------+ |
3674 | | * | 8 | 00001000 | Not interested in 4.xx responses. | |
3675 | | * +-------+-----------------------+-----------------------------------+ |
3676 | | * | 16 | 00010000 | Not interested in 5.xx responses. | |
3677 | | * +-------+-----------------------+-----------------------------------+ |
3678 | | * @endverbatim |
3679 | | * |
3680 | | * @param request The CoAP request to check for the No-Response option. |
3681 | | * This parameter must not be NULL. |
3682 | | * @param response The response that is potentially suppressed. |
3683 | | * This parameter must not be NULL. |
3684 | | * @param session The session this request/response are associated with. |
3685 | | * This parameter must not be NULL. |
3686 | | * @return RESPONSE_DEFAULT when no special treatment is requested, |
3687 | | * RESPONSE_DROP when the response must be discarded, or |
3688 | | * RESPONSE_SEND when the response must be sent. |
3689 | | */ |
3690 | | static enum respond_t |
3691 | | no_response(coap_pdu_t *request, coap_pdu_t *response, |
3692 | 96 | coap_session_t *session, coap_resource_t *resource) { |
3693 | 96 | coap_opt_t *nores; |
3694 | 96 | coap_opt_iterator_t opt_iter; |
3695 | 96 | unsigned int val = 0; |
3696 | | |
3697 | 96 | assert(request); |
3698 | 96 | assert(response); |
3699 | | |
3700 | 96 | if (COAP_RESPONSE_CLASS(response->code) > 0) { |
3701 | 96 | nores = coap_check_option(request, COAP_OPTION_NORESPONSE, &opt_iter); |
3702 | | |
3703 | 96 | if (nores) { |
3704 | 2 | val = coap_decode_var_bytes(coap_opt_value(nores), coap_opt_length(nores)); |
3705 | | |
3706 | | /* The response should be dropped when the bit corresponding to |
3707 | | * the response class is set (cf. table in function |
3708 | | * documentation). When a No-Response option is present and the |
3709 | | * bit is not set, the sender explicitly indicates interest in |
3710 | | * this response. */ |
3711 | 2 | if (((1 << (COAP_RESPONSE_CLASS(response->code) - 1)) & val) > 0) { |
3712 | | /* Should be dropping the response */ |
3713 | 0 | if (response->type == COAP_MESSAGE_ACK && |
3714 | 0 | COAP_PROTO_NOT_RELIABLE(session->proto)) { |
3715 | | /* Still need to ACK the request */ |
3716 | 0 | response->code = 0; |
3717 | | /* Remove token/data from piggybacked acknowledgment PDU */ |
3718 | 0 | response->actual_token.length = 0; |
3719 | 0 | response->e_token_length = 0; |
3720 | 0 | response->used_size = 0; |
3721 | 0 | response->data = NULL; |
3722 | 0 | return RESPONSE_SEND; |
3723 | 0 | } else { |
3724 | 0 | return RESPONSE_DROP; |
3725 | 0 | } |
3726 | 2 | } else { |
3727 | | /* True for mcast as well RFC7967 2.1 */ |
3728 | 2 | return RESPONSE_SEND; |
3729 | 2 | } |
3730 | 94 | } else if (resource && session->context->mcast_per_resource && |
3731 | 0 | coap_is_mcast(&session->addr_info.local)) { |
3732 | | /* Handle any mcast suppression specifics if no NoResponse option */ |
3733 | 0 | if ((resource->flags & |
3734 | 0 | COAP_RESOURCE_FLAGS_LIB_ENA_MCAST_SUPPRESS_2_XX) && |
3735 | 0 | COAP_RESPONSE_CLASS(response->code) == 2) { |
3736 | 0 | return RESPONSE_DROP; |
3737 | 0 | } else if ((resource->flags & |
3738 | 0 | COAP_RESOURCE_FLAGS_LIB_ENA_MCAST_SUPPRESS_2_05) && |
3739 | 0 | response->code == COAP_RESPONSE_CODE(205)) { |
3740 | 0 | if (response->data == NULL) |
3741 | 0 | return RESPONSE_DROP; |
3742 | 0 | } else if ((resource->flags & |
3743 | 0 | COAP_RESOURCE_FLAGS_LIB_DIS_MCAST_SUPPRESS_4_XX) == 0 && |
3744 | 0 | COAP_RESPONSE_CLASS(response->code) == 4) { |
3745 | 0 | return RESPONSE_DROP; |
3746 | 0 | } else if ((resource->flags & |
3747 | 0 | COAP_RESOURCE_FLAGS_LIB_DIS_MCAST_SUPPRESS_5_XX) == 0 && |
3748 | 0 | COAP_RESPONSE_CLASS(response->code) == 5) { |
3749 | 0 | return RESPONSE_DROP; |
3750 | 0 | } |
3751 | 0 | } |
3752 | 96 | } else if (COAP_PDU_IS_EMPTY(response) && |
3753 | 0 | (response->type == COAP_MESSAGE_NON || |
3754 | 0 | response->type == COAP_MESSAGE_CON || |
3755 | 0 | COAP_PROTO_RELIABLE(session->proto))) { |
3756 | | /* Response is 0.00, and this is reliable, non-confirmable, or a separate |
3757 | | * (confirmable) response. An Empty CON is a ping (RFC 7252 4.2), not a |
3758 | | * response, and the PDU still has the request's token attached, which |
3759 | | * RFC 7252 4.1 does not allow. A CON would then get retransmitted up to |
3760 | | * MAX_RETRANSMIT times. */ |
3761 | 0 | return RESPONSE_DROP; |
3762 | 0 | } |
3763 | | |
3764 | | /* |
3765 | | * Do not send error responses for requests that were received via |
3766 | | * IP multicast. RFC7252 8.1 |
3767 | | */ |
3768 | | |
3769 | 94 | if (coap_is_mcast(&session->addr_info.local)) { |
3770 | 0 | if (request->type == COAP_MESSAGE_NON && |
3771 | 0 | response->type == COAP_MESSAGE_RST) |
3772 | 0 | return RESPONSE_DROP; |
3773 | | |
3774 | 0 | if ((!resource || session->context->mcast_per_resource == 0) && |
3775 | 0 | COAP_RESPONSE_CLASS(response->code) > 2) |
3776 | 0 | return RESPONSE_DROP; |
3777 | 0 | } |
3778 | | |
3779 | | /* Default behavior applies when we are not dealing with a response |
3780 | | * (class == 0) or the request did not contain a No-Response option. |
3781 | | */ |
3782 | 94 | return RESPONSE_DEFAULT; |
3783 | 94 | } |
3784 | | |
3785 | | static coap_str_const_t coap_default_uri_wellknown = { |
3786 | | sizeof(COAP_DEFAULT_URI_WELLKNOWN)-1, |
3787 | | (const uint8_t *)COAP_DEFAULT_URI_WELLKNOWN |
3788 | | }; |
3789 | | |
3790 | | /* Initialized in coap_startup() */ |
3791 | | static coap_resource_t resource_uri_wellknown; |
3792 | | |
3793 | | static void |
3794 | | handle_request(coap_context_t *context, coap_session_t *session, coap_pdu_t *pdu, |
3795 | 96 | coap_pdu_t *orig_pdu) { |
3796 | 96 | coap_method_handler_t h = NULL; |
3797 | 96 | coap_pdu_t *response = NULL; |
3798 | 96 | coap_opt_filter_t opt_filter; |
3799 | 96 | coap_resource_t *resource = NULL; |
3800 | | /* The respond field indicates whether a response must be treated |
3801 | | * specially due to a No-Response option that declares disinterest |
3802 | | * or interest in a specific response class. DEFAULT indicates that |
3803 | | * No-Response has not been specified. */ |
3804 | 96 | enum respond_t respond = RESPONSE_DEFAULT; |
3805 | 96 | coap_opt_iterator_t opt_iter; |
3806 | 96 | coap_opt_t *opt; |
3807 | 96 | int is_proxy_uri = 0; |
3808 | 96 | int is_proxy_scheme = 0; |
3809 | 96 | int skip_hop_limit_check = 0; |
3810 | 96 | int resp = 0; |
3811 | 96 | coap_string_t *query = NULL; |
3812 | 96 | coap_opt_t *observe = NULL; |
3813 | 96 | coap_string_t *uri_path = NULL; |
3814 | 96 | int observe_action = COAP_OBSERVE_CANCEL; |
3815 | 96 | coap_block_b_t block; |
3816 | 96 | int added_block = 0; |
3817 | 96 | coap_lg_srcv_t *free_lg_srcv = NULL; |
3818 | 96 | #if COAP_Q_BLOCK_SUPPORT |
3819 | 96 | int lg_xmit_ctrl = 0; |
3820 | 96 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
3821 | 96 | #if COAP_ASYNC_SUPPORT |
3822 | 96 | coap_async_t *async; |
3823 | 96 | #endif /* COAP_ASYNC_SUPPORT */ |
3824 | | |
3825 | 96 | #if COAP_ASYNC_SUPPORT |
3826 | 96 | async = coap_find_async_lkd(session, pdu->actual_token); |
3827 | 96 | if (async) { |
3828 | 0 | coap_tick_t now; |
3829 | |
|
3830 | 0 | coap_ticks(&now); |
3831 | 0 | if (async->delay == 0 || async->delay > now) { |
3832 | | /* re-transmit missing ACK (only if CON) */ |
3833 | 0 | coap_log_info("Retransmit async response\n"); |
3834 | 0 | coap_send_ack_lkd(session, pdu); |
3835 | | /* and do not pass on to the upper layers */ |
3836 | 0 | return; |
3837 | 0 | } |
3838 | 0 | } |
3839 | 96 | #endif /* COAP_ASYNC_SUPPORT */ |
3840 | | |
3841 | 96 | coap_option_filter_clear(&opt_filter); |
3842 | 96 | if (!(context->unknown_resource && context->unknown_resource->is_reverse_proxy)) { |
3843 | 96 | opt = coap_check_option(pdu, COAP_OPTION_PROXY_SCHEME, &opt_iter); |
3844 | 96 | if (opt) { |
3845 | 0 | opt = coap_check_option(pdu, COAP_OPTION_URI_HOST, &opt_iter); |
3846 | 0 | if (!opt) { |
3847 | 0 | coap_log_debug("Proxy-Scheme requires Uri-Host\n"); |
3848 | 0 | resp = 402; |
3849 | 0 | goto fail_response; |
3850 | 0 | } |
3851 | 0 | is_proxy_scheme = 1; |
3852 | 0 | } |
3853 | | |
3854 | 96 | opt = coap_check_option(pdu, COAP_OPTION_PROXY_URI, &opt_iter); |
3855 | 96 | if (opt) |
3856 | 0 | is_proxy_uri = 1; |
3857 | 96 | } |
3858 | | |
3859 | 96 | if (is_proxy_scheme || is_proxy_uri) { |
3860 | 0 | coap_uri_t uri; |
3861 | |
|
3862 | 0 | if (!context->proxy_uri_resource) { |
3863 | | /* Need to return a 5.05 RFC7252 Section 5.7.2 */ |
3864 | 0 | coap_log_debug("Proxy-%s support not configured\n", |
3865 | 0 | is_proxy_scheme ? "Scheme" : "Uri"); |
3866 | 0 | resp = 505; |
3867 | 0 | goto fail_response; |
3868 | 0 | } |
3869 | 0 | if (((size_t)pdu->code - 1 < |
3870 | 0 | (sizeof(resource->handler) / sizeof(resource->handler[0]))) && |
3871 | 0 | !(context->proxy_uri_resource->handler[pdu->code - 1])) { |
3872 | | /* Need to return a 5.05 RFC7252 Section 5.7.2 */ |
3873 | 0 | coap_log_debug("Proxy-%s code %d.%02d handler not supported\n", |
3874 | 0 | is_proxy_scheme ? "Scheme" : "Uri", |
3875 | 0 | pdu->code/100, pdu->code%100); |
3876 | 0 | resp = 505; |
3877 | 0 | goto fail_response; |
3878 | 0 | } |
3879 | | |
3880 | | /* Need to check if authority is the proxy endpoint RFC7252 Section 5.7.2 */ |
3881 | 0 | if (is_proxy_uri) { |
3882 | 0 | if (coap_split_proxy_uri(coap_opt_value(opt), |
3883 | 0 | coap_opt_length(opt), &uri) < 0) { |
3884 | | /* Need to return a 5.05 RFC7252 Section 5.7.2 */ |
3885 | 0 | coap_log_debug("Proxy-URI not decodable\n"); |
3886 | 0 | resp = 505; |
3887 | 0 | goto fail_response; |
3888 | 0 | } |
3889 | 0 | } else { |
3890 | 0 | memset(&uri, 0, sizeof(uri)); |
3891 | 0 | opt = coap_check_option(pdu, COAP_OPTION_URI_HOST, &opt_iter); |
3892 | 0 | if (opt) { |
3893 | 0 | uri.host.length = coap_opt_length(opt); |
3894 | 0 | uri.host.s = coap_opt_value(opt); |
3895 | 0 | } else |
3896 | 0 | uri.host.length = 0; |
3897 | 0 | } |
3898 | | |
3899 | 0 | resource = context->proxy_uri_resource; |
3900 | 0 | if (uri.host.length && resource->proxy_name_count && |
3901 | 0 | resource->proxy_name_list) { |
3902 | 0 | size_t i; |
3903 | |
|
3904 | 0 | if (resource->proxy_name_count == 1 && |
3905 | 0 | resource->proxy_name_list[0]->length == 0) { |
3906 | | /* If proxy_name_list[0] is zero length, then this is the endpoint */ |
3907 | 0 | i = 0; |
3908 | 0 | } else { |
3909 | 0 | for (i = 0; i < resource->proxy_name_count; i++) { |
3910 | 0 | if (coap_string_equal(&uri.host, resource->proxy_name_list[i])) { |
3911 | 0 | break; |
3912 | 0 | } |
3913 | 0 | } |
3914 | 0 | } |
3915 | 0 | if (i != resource->proxy_name_count) { |
3916 | | /* This server is hosting the proxy connection endpoint */ |
3917 | 0 | if (pdu->crit_opt) { |
3918 | | /* Cannot handle critical option */ |
3919 | 0 | pdu->crit_opt = 0; |
3920 | 0 | resp = 402; |
3921 | 0 | resource = NULL; |
3922 | 0 | goto fail_response; |
3923 | 0 | } |
3924 | 0 | is_proxy_uri = 0; |
3925 | 0 | is_proxy_scheme = 0; |
3926 | 0 | skip_hop_limit_check = 1; |
3927 | 0 | } |
3928 | 0 | } |
3929 | 0 | resource = NULL; |
3930 | 0 | } |
3931 | 96 | assert(resource == NULL); |
3932 | | |
3933 | 96 | if (!skip_hop_limit_check) { |
3934 | 96 | opt = coap_check_option(pdu, COAP_OPTION_HOP_LIMIT, &opt_iter); |
3935 | 96 | if (opt) { |
3936 | 0 | size_t hop_limit; |
3937 | 0 | uint8_t buf[4]; |
3938 | |
|
3939 | 0 | hop_limit = |
3940 | 0 | coap_decode_var_bytes(coap_opt_value(opt), coap_opt_length(opt)); |
3941 | 0 | if (hop_limit == 1) { |
3942 | | /* coap_send_internal() will fill in the IP address for us */ |
3943 | 0 | resp = 508; |
3944 | 0 | goto fail_response; |
3945 | 0 | } else if (hop_limit < 1 || hop_limit > 255) { |
3946 | | /* Need to return a 4.00 RFC8768 Section 3 */ |
3947 | 0 | coap_log_info("Invalid Hop Limit\n"); |
3948 | 0 | resp = 400; |
3949 | 0 | goto fail_response; |
3950 | 0 | } |
3951 | 0 | hop_limit--; |
3952 | 0 | coap_update_option(pdu, COAP_OPTION_HOP_LIMIT, |
3953 | 0 | coap_encode_var_safe8(buf, sizeof(buf), hop_limit), |
3954 | 0 | buf); |
3955 | 0 | } |
3956 | 96 | } |
3957 | | |
3958 | 96 | uri_path = coap_get_uri_path(pdu); |
3959 | 96 | if (!uri_path) { |
3960 | 0 | resp = 402; |
3961 | 0 | goto fail_response; |
3962 | 0 | } |
3963 | | |
3964 | 96 | if (!is_proxy_uri && !is_proxy_scheme) { |
3965 | | /* try to find the resource from the request URI */ |
3966 | 96 | coap_str_const_t uri_path_c = { uri_path->length, uri_path->s }; |
3967 | 96 | resource = coap_get_resource_from_uri_path_lkd(context, &uri_path_c); |
3968 | 96 | } |
3969 | | |
3970 | 96 | if ((resource == NULL) || (resource->is_unknown == 1) || |
3971 | 92 | (resource->is_proxy_uri == 1)) { |
3972 | | /* The resource was not found or there is an unexpected match against the |
3973 | | * resource defined for handling unknown or proxy URIs. |
3974 | | */ |
3975 | 4 | if (resource != NULL) |
3976 | | /* Close down unexpected match */ |
3977 | 0 | resource = NULL; |
3978 | | /* |
3979 | | * Check if the request URI happens to be the well-known URI, or if the |
3980 | | * unknown resource handler is defined, a PUT or optionally other methods, |
3981 | | * if configured, for the unknown handler. |
3982 | | * |
3983 | | * if a PROXY URI/Scheme request and proxy URI handler defined, call the |
3984 | | * proxy URI handler. |
3985 | | * |
3986 | | * else if unknown URI handler defined and COAP_RESOURCE_HANDLE_WELLKNOWN_CORE |
3987 | | * set, call the unknown URI handler with any unknown URI (including |
3988 | | * .well-known/core) if the appropriate method is defined. |
3989 | | * |
3990 | | * else if well-known URI generate a default response. |
3991 | | * |
3992 | | * else if unknown URI handler defined, call the unknown |
3993 | | * URI handler (to allow for potential generation of resource |
3994 | | * [RFC7272 5.8.3]) if the appropriate method is defined. |
3995 | | * |
3996 | | * else if DELETE return 2.02 (RFC7252: 5.8.4. DELETE). |
3997 | | * |
3998 | | * else return 4.04. |
3999 | | */ |
4000 | | |
4001 | 4 | if (is_proxy_uri || is_proxy_scheme) { |
4002 | 0 | resource = context->proxy_uri_resource; |
4003 | 4 | } else if (context->unknown_resource != NULL && |
4004 | 0 | context->unknown_resource->flags & COAP_RESOURCE_HANDLE_WELLKNOWN_CORE && |
4005 | 0 | ((size_t)pdu->code - 1 < |
4006 | 0 | (sizeof(resource->handler) / sizeof(coap_method_handler_t))) && |
4007 | 0 | (context->unknown_resource->handler[pdu->code - 1])) { |
4008 | 0 | resource = context->unknown_resource; |
4009 | 4 | } else if (coap_string_equal(uri_path, &coap_default_uri_wellknown)) { |
4010 | | /* request for .well-known/core */ |
4011 | 0 | resource = &resource_uri_wellknown; |
4012 | 4 | } else if ((context->unknown_resource != NULL) && |
4013 | 0 | ((size_t)pdu->code - 1 < |
4014 | 0 | (sizeof(resource->handler) / sizeof(coap_method_handler_t))) && |
4015 | 0 | (context->unknown_resource->handler[pdu->code - 1])) { |
4016 | | /* |
4017 | | * The unknown_resource can be used to handle undefined resources |
4018 | | * for a PUT request and can support any other registered handler |
4019 | | * defined for it |
4020 | | * Example set up code:- |
4021 | | * r = coap_resource_unknown_init(hnd_put_unknown); |
4022 | | * coap_register_request_handler(r, COAP_REQUEST_POST, |
4023 | | * hnd_post_unknown); |
4024 | | * coap_register_request_handler(r, COAP_REQUEST_GET, |
4025 | | * hnd_get_unknown); |
4026 | | * coap_register_request_handler(r, COAP_REQUEST_DELETE, |
4027 | | * hnd_delete_unknown); |
4028 | | * coap_add_resource(ctx, r); |
4029 | | * |
4030 | | * Note: It is not possible to observe the unknown_resource, a separate |
4031 | | * resource must be created (by PUT or POST) which has a GET |
4032 | | * handler to be observed |
4033 | | */ |
4034 | 0 | resource = context->unknown_resource; |
4035 | 4 | } else if (pdu->code == COAP_REQUEST_CODE_DELETE) { |
4036 | | /* |
4037 | | * Request for DELETE on non-existent resource (RFC7252: 5.8.4. DELETE) |
4038 | | */ |
4039 | 0 | coap_log_debug("request for unknown resource '%*.*s'," |
4040 | 0 | " return 2.02\n", |
4041 | 0 | (int)uri_path->length, |
4042 | 0 | (int)uri_path->length, |
4043 | 0 | uri_path->s); |
4044 | 0 | resp = 202; |
4045 | 0 | goto fail_response; |
4046 | 4 | } else if (context->dyn_create_handler != NULL) { |
4047 | 0 | resource = coap_add_dynamic_resource(session, pdu); |
4048 | 0 | if (!resource) { |
4049 | 0 | resp = 406; |
4050 | 0 | goto fail_response; |
4051 | 0 | } |
4052 | 4 | } else { /* request for any another resource, return 4.04 */ |
4053 | | |
4054 | 4 | coap_log_debug("request for unknown resource '%*.*s', return 4.04\n", |
4055 | 4 | (int)uri_path->length, (int)uri_path->length, uri_path->s); |
4056 | 4 | resp = 404; |
4057 | 4 | goto fail_response; |
4058 | 4 | } |
4059 | | |
4060 | 4 | } |
4061 | | |
4062 | 92 | coap_resource_reference_lkd(resource); |
4063 | | |
4064 | 92 | #if COAP_OSCORE_SUPPORT |
4065 | 92 | if ((resource->flags & COAP_RESOURCE_FLAGS_OSCORE_ONLY) && !session->oscore_encryption) { |
4066 | 0 | coap_log_debug("request for OSCORE only resource '%*.*s', return 4.04\n", |
4067 | 0 | (int)uri_path->length, (int)uri_path->length, uri_path->s); |
4068 | 0 | resp = 401; |
4069 | 0 | goto fail_response; |
4070 | 0 | } |
4071 | 92 | #endif /* COAP_OSCORE_SUPPORT */ |
4072 | 92 | if (resource->is_unknown == 0 && resource->is_proxy_uri == 0) { |
4073 | | /* Check for existing resource and If-Non-Match */ |
4074 | 92 | opt = coap_check_option(pdu, COAP_OPTION_IF_NONE_MATCH, &opt_iter); |
4075 | 92 | if (opt) { |
4076 | 0 | resp = 412; |
4077 | 0 | goto fail_response; |
4078 | 0 | } |
4079 | 92 | } |
4080 | | |
4081 | | /* the resource was found, check if there is a registered handler */ |
4082 | 92 | if ((size_t)pdu->code - 1 < |
4083 | 92 | sizeof(resource->handler) / sizeof(coap_method_handler_t)) |
4084 | 92 | h = resource->handler[pdu->code - 1]; |
4085 | | |
4086 | 92 | if (h == NULL) { |
4087 | 21 | resp = 405; |
4088 | 21 | goto fail_response; |
4089 | 21 | } |
4090 | 71 | if (pdu->code == COAP_REQUEST_CODE_FETCH) { |
4091 | 0 | if (coap_check_option(pdu, COAP_OPTION_OSCORE, &opt_iter) == NULL) { |
4092 | 0 | opt = coap_check_option(pdu, COAP_OPTION_CONTENT_FORMAT, &opt_iter); |
4093 | 0 | if (opt == NULL) { |
4094 | | /* RFC 8132 2.3.1 */ |
4095 | 0 | resp = 415; |
4096 | 0 | goto fail_response; |
4097 | 0 | } |
4098 | 0 | } |
4099 | 0 | } |
4100 | 71 | if (context->mcast_per_resource && |
4101 | 0 | (resource->flags & COAP_RESOURCE_FLAGS_HAS_MCAST_SUPPORT) == 0 && |
4102 | 0 | coap_is_mcast(&session->addr_info.local)) { |
4103 | 0 | resp = 405; |
4104 | 0 | goto fail_response; |
4105 | 0 | } |
4106 | | |
4107 | 71 | if (pdu->type == COAP_MESSAGE_CON) { |
4108 | 70 | response = coap_pdu_init(COAP_MESSAGE_ACK, 0, pdu->mid, |
4109 | 70 | coap_session_max_pdu_size_lkd(session)); |
4110 | 70 | } else { |
4111 | 1 | response = coap_pdu_init(COAP_MESSAGE_NON, 0, coap_new_message_id_lkd(session), |
4112 | 1 | coap_session_max_pdu_size_lkd(session)); |
4113 | 1 | } |
4114 | 71 | if (!response) { |
4115 | 0 | coap_log_err("could not create response PDU\n"); |
4116 | 0 | resp = 500; |
4117 | 0 | goto fail_response; |
4118 | 0 | } |
4119 | 71 | response->session = session; |
4120 | 71 | #if COAP_ASYNC_SUPPORT |
4121 | | /* If handling a separate response, need CON, not ACK response */ |
4122 | 71 | if (async && pdu->type == COAP_MESSAGE_CON) |
4123 | 0 | response->type = COAP_MESSAGE_CON; |
4124 | 71 | #endif /* COAP_ASYNC_SUPPORT */ |
4125 | | /* A lot of the reliable code assumes type is CON */ |
4126 | 71 | if (COAP_PROTO_RELIABLE(session->proto) && response->type != COAP_MESSAGE_CON) |
4127 | 0 | response->type = COAP_MESSAGE_CON; |
4128 | | |
4129 | 71 | if (!coap_add_token(response, pdu->actual_token.length, |
4130 | 71 | pdu->actual_token.s)) { |
4131 | 0 | resp = 500; |
4132 | 0 | goto fail_response; |
4133 | 0 | } |
4134 | | |
4135 | | /* |
4136 | | * RFC7959 2.2: the SZX value 7 "is reserved, i.e., MUST NOT be sent and |
4137 | | * MUST lead to a 4.00 Bad Request response code upon reception in a |
4138 | | * request". SZX 7 is only meaningful as the BERT escape (RFC8323 6), |
4139 | | * which needs a reliable transport with BERT negotiated in both CSMs. |
4140 | | * Anywhere else it must be rejected here: coap_get_block_b() reports a |
4141 | | * reserved SZX as "no Block option present", which is indistinguishable |
4142 | | * further down from a request that never carried one. |
4143 | | */ |
4144 | 71 | if (COAP_PROTO_NOT_RELIABLE(session->proto) || |
4145 | 71 | !(session->csm_bert_rem_support && session->csm_bert_loc_support)) { |
4146 | 71 | static const coap_option_num_t block_nums[] = { |
4147 | 71 | COAP_OPTION_BLOCK1, COAP_OPTION_BLOCK2, COAP_OPTION_Q_BLOCK1, COAP_OPTION_Q_BLOCK2 |
4148 | 71 | }; |
4149 | 71 | size_t bn; |
4150 | | |
4151 | 355 | for (bn = 0; bn < sizeof(block_nums)/sizeof(block_nums[0]); bn++) { |
4152 | 284 | coap_opt_t *block_opt = coap_check_option(pdu, block_nums[bn], &opt_iter); |
4153 | | |
4154 | 284 | if (block_opt && COAP_OPT_BLOCK_SZX(block_opt) == 7) { |
4155 | 0 | coap_log_debug("request: reserved Block SZX 7 (RFC7959 2.2)\n"); |
4156 | 0 | resp = 400; |
4157 | 0 | goto fail_response; |
4158 | 0 | } |
4159 | 284 | } |
4160 | 71 | } |
4161 | | |
4162 | 71 | query = coap_get_query(pdu); |
4163 | | |
4164 | | /* check for Observe option RFC7641 and RFC8132 */ |
4165 | 71 | if (resource->observable && |
4166 | 71 | (pdu->code == COAP_REQUEST_CODE_GET || |
4167 | 71 | pdu->code == COAP_REQUEST_CODE_FETCH)) { |
4168 | 71 | observe = coap_check_option(pdu, COAP_OPTION_OBSERVE, &opt_iter); |
4169 | 71 | } |
4170 | | |
4171 | | /* |
4172 | | * See if blocks need to be aggregated or next requests sent off |
4173 | | * before invoking application request handler |
4174 | | */ |
4175 | 71 | if (session->block_mode & COAP_BLOCK_USE_LIBCOAP) { |
4176 | 0 | uint32_t block_mode = session->block_mode; |
4177 | |
|
4178 | 0 | if (observe || |
4179 | 0 | resource->flags & COAP_RESOURCE_FLAGS_FORCE_SINGLE_BODY) |
4180 | 0 | session->block_mode |= COAP_BLOCK_SINGLE_BODY; |
4181 | 0 | if (coap_handle_request_put_block(context, session, pdu, response, |
4182 | 0 | resource, observe, |
4183 | 0 | &added_block, &free_lg_srcv)) { |
4184 | 0 | session->block_mode = block_mode; |
4185 | 0 | goto skip_handler; |
4186 | 0 | } |
4187 | 0 | session->block_mode = block_mode; |
4188 | |
|
4189 | 0 | if (coap_handle_request_send_block(session, pdu, response, resource, |
4190 | 0 | query)) { |
4191 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4192 | 0 | lg_xmit_ctrl = 1; |
4193 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4194 | 0 | goto skip_handler; |
4195 | 0 | } |
4196 | 0 | } |
4197 | | |
4198 | 71 | if (observe) { |
4199 | 64 | observe_action = |
4200 | 64 | coap_decode_var_bytes(coap_opt_value(observe), |
4201 | 64 | coap_opt_length(observe)); |
4202 | | |
4203 | 64 | if (observe_action == COAP_OBSERVE_ESTABLISH) { |
4204 | 32 | coap_subscription_t *subscription; |
4205 | | |
4206 | 32 | if (coap_get_block_b(session, pdu, COAP_OPTION_BLOCK2, &block)) { |
4207 | 0 | if (block.num != 0) { |
4208 | 0 | response->code = COAP_RESPONSE_CODE(400); |
4209 | 0 | goto skip_handler; |
4210 | 0 | } |
4211 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4212 | 32 | } else if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK2, |
4213 | 32 | &block)) { |
4214 | 0 | if (block.num != 0) { |
4215 | 0 | response->code = COAP_RESPONSE_CODE(400); |
4216 | 0 | goto skip_handler; |
4217 | 0 | } |
4218 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4219 | 0 | } |
4220 | 32 | subscription = coap_add_observer(resource, session, &pdu->actual_token, |
4221 | 32 | pdu); |
4222 | 32 | if (subscription) { |
4223 | 32 | uint8_t buf[4]; |
4224 | | |
4225 | 32 | coap_touch_observer(context, session, &pdu->actual_token); |
4226 | 32 | coap_insert_option(response, COAP_OPTION_OBSERVE, |
4227 | 32 | coap_encode_var_safe(buf, sizeof(buf), |
4228 | 32 | resource->observe), |
4229 | 32 | buf); |
4230 | 32 | } |
4231 | 32 | } else if (observe_action == COAP_OBSERVE_CANCEL) { |
4232 | 32 | coap_delete_observer_request(resource, session, &pdu->actual_token, pdu, free_lg_srcv != NULL); |
4233 | 32 | } else { |
4234 | 0 | coap_log_info("observe: unexpected action %d\n", observe_action); |
4235 | 0 | } |
4236 | 64 | } |
4237 | | |
4238 | 71 | #if COAP_WITH_OBSERVE_PERSIST |
4239 | | /* If we are maintaining Observe persist */ |
4240 | 71 | if (resource == context->unknown_resource) { |
4241 | 0 | context->unknown_pdu = pdu; |
4242 | 0 | context->unknown_session = session; |
4243 | 0 | } else |
4244 | 71 | context->unknown_pdu = NULL; |
4245 | 71 | #endif /* COAP_WITH_OBSERVE_PERSIST */ |
4246 | | |
4247 | | /* |
4248 | | * Call the request handler with everything set up |
4249 | | */ |
4250 | 71 | if (resource == &resource_uri_wellknown) { |
4251 | | /* Leave context locked */ |
4252 | 0 | coap_log_debug("call handler for pseudo resource '%*.*s' (3)\n", |
4253 | 0 | (int)resource->uri_path->length, (int)resource->uri_path->length, |
4254 | 0 | resource->uri_path->s); |
4255 | 0 | h(resource, session, pdu, query, response); |
4256 | 0 | if (COAP_RESPONSE_CLASS(response->code) == 2 && response->data == NULL && |
4257 | 0 | coap_is_mcast(&session->addr_info.local)) { |
4258 | 0 | goto drop_it_debug; |
4259 | 0 | } |
4260 | 71 | } else { |
4261 | 71 | coap_log_debug("call custom handler for resource '%*.*s' (3)\n", |
4262 | 71 | (int)resource->uri_path->length, (int)resource->uri_path->length, |
4263 | 71 | resource->uri_path->s); |
4264 | 71 | if (resource->flags & COAP_RESOURCE_SAFE_REQUEST_HANDLER) { |
4265 | 0 | coap_lock_callback_release(h(resource, session, pdu, query, response), |
4266 | | /* context is being freed off */ |
4267 | 0 | goto finish); |
4268 | 71 | } else { |
4269 | 71 | coap_lock_specific_callback_release(&resource->lock, |
4270 | 71 | h(resource, session, pdu, query, response), |
4271 | | /* context is being freed off */ |
4272 | 71 | goto finish); |
4273 | 71 | } |
4274 | 71 | } |
4275 | | |
4276 | | /* Check validity of response code */ |
4277 | 71 | if (!coap_check_code_class(session, response)) { |
4278 | 0 | coap_log_warn("handle_request: Invalid PDU response code (%d.%02d)\n", |
4279 | 0 | COAP_RESPONSE_CLASS(response->code), |
4280 | 0 | response->code & 0x1f); |
4281 | 0 | goto drop_it_no_debug; |
4282 | 0 | } |
4283 | | |
4284 | | /* Check correct content type returned by application */ |
4285 | 71 | if (response->code != 0 && (opt = coap_check_option(pdu, COAP_OPTION_ACCEPT, &opt_iter)) && |
4286 | 0 | !(COAP_RESPONSE_CLASS(response->code) == 4 || COAP_RESPONSE_CLASS(response->code) == 5)) { |
4287 | 0 | coap_opt_t *ropt = coap_check_option(response, COAP_OPTION_CONTENT_FORMAT, &opt_iter); |
4288 | |
|
4289 | 0 | if (!ropt) { |
4290 | 0 | coap_add_option_internal(response, COAP_OPTION_CONTENT_FORMAT, |
4291 | 0 | coap_opt_length(opt), coap_opt_value(opt)); |
4292 | 0 | } else if (coap_opt_length(opt) != coap_opt_length(ropt) || |
4293 | 0 | memcmp(coap_opt_value(opt), coap_opt_value(ropt), coap_opt_length(opt)) != 0) { |
4294 | 0 | coap_show_pdu(COAP_LOG_DEBUG, response); |
4295 | 0 | coap_log_debug("handle_request: response: Invalid Content-Format\n"); |
4296 | | /* Need to convert response to 4.06 as incorrect content type */ |
4297 | 0 | response->code = COAP_RESPONSE_CODE(406); |
4298 | 0 | response->used_size = response->e_token_length; |
4299 | 0 | response->data = NULL; |
4300 | 0 | response->max_opt = 0; |
4301 | 0 | coap_add_option_internal(response, COAP_OPTION_ACCEPT, |
4302 | 0 | coap_opt_length(opt), |
4303 | 0 | coap_opt_value(opt)); |
4304 | 0 | coap_add_data(response, sizeof("Not Acceptable")-1, (const uint8_t *)"Not Acceptable"); |
4305 | 0 | } |
4306 | 0 | } |
4307 | | |
4308 | | /* Check if lg_xmit generated and update PDU code if so */ |
4309 | 71 | coap_check_code_lg_xmit(session, pdu, response, resource, query); |
4310 | | |
4311 | 71 | if (free_lg_srcv) { |
4312 | | /* Check to see if the server is doing a 4.01 + Echo response */ |
4313 | 0 | if (response->code == COAP_RESPONSE_CODE(401) && |
4314 | 0 | coap_check_option(response, COAP_OPTION_ECHO, &opt_iter)) { |
4315 | | /* Need to keep lg_srcv around for client's response */ |
4316 | 0 | } else { |
4317 | 0 | coap_lg_srcv_t *lg_srcv; |
4318 | | /* |
4319 | | * Need to check free_lg_srcv still exists in case of error or timing window |
4320 | | */ |
4321 | 0 | LL_FOREACH(session->lg_srcv, lg_srcv) { |
4322 | 0 | if (lg_srcv == free_lg_srcv) { |
4323 | 0 | #if COAP_Q_BLOCK_SUPPORT |
4324 | 0 | if (lg_srcv->block_option == COAP_OPTION_Q_BLOCK1) { |
4325 | 0 | coap_tick_t adjust; |
4326 | | |
4327 | | /* cache the lg_srcv for 1 second */ |
4328 | 0 | if (COAP_NON_RECEIVE_TIMEOUT_TICKS(session) >= COAP_TICKS_PER_SECOND) { |
4329 | 0 | adjust = COAP_NON_RECEIVE_TIMEOUT_TICKS(session) - COAP_TICKS_PER_SECOND; |
4330 | 0 | } else { |
4331 | 0 | adjust = 0; |
4332 | 0 | } |
4333 | 0 | coap_ticks(&free_lg_srcv->rec_blocks.last_seen); |
4334 | 0 | if (free_lg_srcv->rec_blocks.last_seen > adjust) { |
4335 | 0 | free_lg_srcv->rec_blocks.last_seen -= adjust; |
4336 | 0 | } |
4337 | 0 | free_lg_srcv->dont_timeout = 0; |
4338 | 0 | break; |
4339 | 0 | } |
4340 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4341 | 0 | LL_DELETE(session->lg_srcv, free_lg_srcv); |
4342 | 0 | coap_block_delete_lg_srcv(session, free_lg_srcv); |
4343 | 0 | break; |
4344 | 0 | } |
4345 | 0 | } |
4346 | 0 | } |
4347 | 0 | } |
4348 | 71 | if (added_block && COAP_RESPONSE_CLASS(response->code) == 2) { |
4349 | | /* Just in case, as there are more to go */ |
4350 | 0 | response->code = COAP_RESPONSE_CODE(231); |
4351 | 0 | } |
4352 | | |
4353 | 96 | skip_handler: |
4354 | 96 | respond = no_response(pdu, response, session, resource); |
4355 | 96 | if (respond != RESPONSE_DROP) { |
4356 | 96 | #if (COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_DEBUG) |
4357 | 96 | coap_mid_t mid = pdu->mid; |
4358 | 96 | #endif |
4359 | 96 | if (COAP_RESPONSE_CLASS(response->code) != 2) { |
4360 | 25 | if (observe) { |
4361 | 0 | coap_remove_option(response, COAP_OPTION_OBSERVE); |
4362 | 0 | } |
4363 | 25 | } |
4364 | 96 | if (COAP_RESPONSE_CLASS(response->code) > 2) { |
4365 | 25 | if (observe) |
4366 | 0 | coap_delete_observer(resource, session, &pdu->actual_token); |
4367 | 25 | if (response->code != COAP_RESPONSE_CODE(413)) |
4368 | 25 | coap_remove_option(response, COAP_OPTION_BLOCK1); |
4369 | 25 | } |
4370 | | |
4371 | | /* If original request contained a token, and the registered |
4372 | | * application handler made no changes to the response, then |
4373 | | * this is an empty ACK with a token, which is a malformed |
4374 | | * PDU */ |
4375 | 96 | if ((response->type == COAP_MESSAGE_ACK) |
4376 | 78 | && (response->code == 0)) { |
4377 | | /* Remove token from otherwise-empty acknowledgment PDU */ |
4378 | 0 | response->actual_token.length = 0; |
4379 | 0 | response->e_token_length = 0; |
4380 | 0 | response->used_size = 0; |
4381 | 0 | response->data = NULL; |
4382 | 0 | } |
4383 | | |
4384 | 96 | if (!coap_is_mcast(&session->addr_info.local) || |
4385 | 0 | (context->mcast_per_resource && |
4386 | 0 | resource && |
4387 | 96 | (resource->flags & COAP_RESOURCE_FLAGS_LIB_DIS_MCAST_DELAYS))) { |
4388 | | /* No delays to response */ |
4389 | 96 | #if COAP_Q_BLOCK_SUPPORT |
4390 | 96 | if (session->block_mode & COAP_BLOCK_USE_LIBCOAP && |
4391 | 0 | !lg_xmit_ctrl && COAP_RESPONSE_CLASS(response->code) == 2 && |
4392 | 0 | coap_get_block_b(session, response, COAP_OPTION_Q_BLOCK2, &block) && |
4393 | 0 | block.m) { |
4394 | 0 | if (coap_send_q_block2(session, resource, query, pdu->code, block, |
4395 | 0 | response, |
4396 | 0 | COAP_SEND_INC_PDU) == COAP_INVALID_MID) |
4397 | 0 | coap_log_debug("cannot send response for mid=0x%x\n", mid); |
4398 | 0 | response = NULL; |
4399 | 0 | goto finish; |
4400 | 0 | } |
4401 | 96 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4402 | 96 | if (coap_send_internal(session, response, orig_pdu ? orig_pdu : pdu) == COAP_INVALID_MID) { |
4403 | 48 | coap_log_debug("cannot send response for mid=0x%04x\n", mid); |
4404 | 48 | goto finish; |
4405 | 48 | } |
4406 | 96 | } else { |
4407 | | /* Need to delay mcast response */ |
4408 | 0 | coap_queue_t *node = coap_new_node(); |
4409 | 0 | uint8_t r; |
4410 | 0 | coap_tick_t delay; |
4411 | |
|
4412 | 0 | if (!node) { |
4413 | 0 | coap_log_debug("mcast delay: insufficient memory\n"); |
4414 | 0 | goto drop_it_no_debug; |
4415 | 0 | } |
4416 | 0 | if (!coap_pdu_encode_header(response, session->proto)) { |
4417 | 0 | coap_delete_node_lkd(node); |
4418 | 0 | goto drop_it_no_debug; |
4419 | 0 | } |
4420 | | |
4421 | 0 | node->id = response->mid; |
4422 | 0 | node->pdu = response; |
4423 | 0 | node->is_mcast = 1; |
4424 | 0 | coap_prng_lkd(&r, sizeof(r)); |
4425 | 0 | delay = (COAP_DEFAULT_LEISURE_TICKS(session) * r) / 256; |
4426 | 0 | coap_log_debug(" %s: mid=0x%04x: mcast response delayed for %u.%03u secs\n", |
4427 | 0 | coap_session_str(session), |
4428 | 0 | response->mid, |
4429 | 0 | (unsigned int)(delay / COAP_TICKS_PER_SECOND), |
4430 | 0 | (unsigned int)((delay % COAP_TICKS_PER_SECOND) * |
4431 | 0 | 1000 / COAP_TICKS_PER_SECOND)); |
4432 | 0 | node->timeout = (unsigned int)delay; |
4433 | | /* Use this to delay transmission */ |
4434 | 0 | coap_wait_ack(session->context, session, node); |
4435 | 0 | } |
4436 | 96 | } else if (COAP_PDU_IS_EMPTY(response) && |
4437 | 0 | (response->type == COAP_MESSAGE_NON || |
4438 | 0 | response->type == COAP_MESSAGE_CON || |
4439 | 0 | COAP_PROTO_RELIABLE(session->proto))) { |
4440 | 0 | coap_delete_pdu_lkd(response); |
4441 | 0 | } else { |
4442 | 0 | drop_it_debug: |
4443 | 0 | coap_log_debug(" %s: mid=0x%04x: response dropped\n", |
4444 | 0 | coap_session_str(session), |
4445 | 0 | response->mid); |
4446 | 0 | coap_show_pdu(COAP_LOG_DEBUG, response); |
4447 | 0 | drop_it_no_debug: |
4448 | 0 | coap_delete_pdu_lkd(response); |
4449 | 0 | } |
4450 | 48 | #if COAP_Q_BLOCK_SUPPORT |
4451 | 48 | if (coap_get_block_b(session, pdu, COAP_OPTION_Q_BLOCK1, &block)) { |
4452 | 0 | if (COAP_PROTO_RELIABLE(session->proto)) { |
4453 | 0 | if (block.m) { |
4454 | | /* All of the sequence not in yet */ |
4455 | 0 | goto finish; |
4456 | 0 | } |
4457 | 0 | } else if (pdu->type == COAP_MESSAGE_NON) { |
4458 | | /* More to go and not at a payload break */ |
4459 | 0 | if (block.m && ((block.num + 1) % COAP_MAX_PAYLOADS(session))) { |
4460 | 0 | goto finish; |
4461 | 0 | } |
4462 | 0 | } |
4463 | 0 | } |
4464 | 48 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4465 | | |
4466 | 96 | finish: |
4467 | 96 | if (query) |
4468 | 2 | coap_delete_string(query); |
4469 | 96 | if (resource) |
4470 | 92 | coap_resource_release_lkd(resource); |
4471 | 96 | coap_delete_string(uri_path); |
4472 | 96 | return; |
4473 | | |
4474 | 25 | fail_response: |
4475 | 25 | coap_delete_pdu_lkd(response); |
4476 | 25 | response = |
4477 | 25 | coap_new_error_response(pdu, COAP_RESPONSE_CODE(resp), |
4478 | 25 | &opt_filter); |
4479 | 25 | if (response) |
4480 | 25 | goto skip_handler; |
4481 | 0 | if (resource) |
4482 | 0 | coap_resource_release_lkd(resource); |
4483 | 0 | coap_delete_string(uri_path); |
4484 | 0 | } |
4485 | | #endif /* COAP_SERVER_SUPPORT */ |
4486 | | |
4487 | | #if COAP_CLIENT_SUPPORT |
4488 | | /* Call application-specific response handler when available. */ |
4489 | | void |
4490 | | coap_call_response_handler(coap_session_t *session, |
4491 | | coap_pdu_t *sent, coap_pdu_t *rcvd, |
4492 | 1 | void *body_data) { |
4493 | 1 | coap_context_t *context = session->context; |
4494 | 1 | coap_response_t ret; |
4495 | | |
4496 | 1 | #if COAP_PROXY_SUPPORT |
4497 | 1 | if (context->proxy_response_cb) { |
4498 | 0 | coap_proxy_entry_t *proxy_entry; |
4499 | 0 | coap_proxy_req_t *proxy_req = coap_proxy_map_outgoing_request(session, |
4500 | 0 | rcvd, |
4501 | 0 | &proxy_entry); |
4502 | |
|
4503 | 0 | if (proxy_req && proxy_req->incoming && !proxy_req->incoming->server_list) { |
4504 | 0 | coap_proxy_process_incoming(session, rcvd, body_data, proxy_req, |
4505 | 0 | proxy_entry); |
4506 | 0 | return; |
4507 | 0 | } |
4508 | 0 | } |
4509 | 1 | #endif /* COAP_PROXY_SUPPORT */ |
4510 | 1 | if (session->doing_send_recv && session->req_token && |
4511 | 0 | coap_binary_equal(session->req_token, &rcvd->actual_token)) { |
4512 | | /* processing coap_send_recv() call */ |
4513 | 0 | session->resp_pdu = rcvd; |
4514 | 0 | coap_pdu_reference_lkd(rcvd); |
4515 | | /* Will get freed off when PDU is freed off */ |
4516 | 0 | rcvd->data_free = body_data; |
4517 | 0 | coap_send_ack_lkd(session, rcvd); |
4518 | 0 | session->last_con_handler_res = COAP_RESPONSE_OK; |
4519 | 0 | return; |
4520 | 1 | } else if (context->response_cb) { |
4521 | 0 | coap_lock_callback_ret_release(ret, |
4522 | 0 | context->response_cb(session, |
4523 | 0 | sent, |
4524 | 0 | rcvd, |
4525 | 0 | rcvd->mid), |
4526 | | /* context is being freed off */ |
4527 | 0 | return); |
4528 | 1 | } else { |
4529 | | /* |
4530 | | * RFC7252 4.2 |
4531 | | * (b) reject the message if the recipient lacks context to process the |
4532 | | * message properly |
4533 | | */ |
4534 | 1 | ret = COAP_RESPONSE_FAIL; |
4535 | 1 | } |
4536 | 1 | if (ret == COAP_RESPONSE_FAIL) { |
4537 | 1 | coap_send_rst_lkd(session, rcvd); |
4538 | 1 | session->last_con_handler_res = COAP_RESPONSE_FAIL; |
4539 | 1 | } else { |
4540 | 0 | coap_send_ack_lkd(session, rcvd); |
4541 | 0 | session->last_con_handler_res = COAP_RESPONSE_OK; |
4542 | 0 | } |
4543 | 1 | coap_free_type(COAP_STRING, body_data); |
4544 | 1 | } |
4545 | | |
4546 | | static void |
4547 | | handle_response(coap_context_t *context, coap_session_t *session, |
4548 | 1 | coap_pdu_t *sent, coap_pdu_t *rcvd) { |
4549 | | |
4550 | | /* Set in case there is a later call to coap_update_token() */ |
4551 | 1 | rcvd->session = session; |
4552 | | |
4553 | | /* Check for message duplication */ |
4554 | 1 | if (COAP_PROTO_NOT_RELIABLE(session->proto)) { |
4555 | 1 | if (rcvd->type == COAP_MESSAGE_CON) { |
4556 | 0 | if (rcvd->mid == session->last_resp_mid) { |
4557 | | /* Duplicate response: send ACK/RST, but don't process */ |
4558 | 0 | if (session->last_con_handler_res == COAP_RESPONSE_OK) |
4559 | 0 | coap_send_ack_lkd(session, rcvd); |
4560 | 0 | else |
4561 | 0 | coap_send_rst_lkd(session, rcvd); |
4562 | 0 | return; |
4563 | 0 | } |
4564 | 1 | } else if (rcvd->type == COAP_MESSAGE_ACK) { |
4565 | 1 | if (rcvd->mid == session->last_resp_mid) { |
4566 | | /* Duplicate response */ |
4567 | 0 | return; |
4568 | 0 | } |
4569 | 1 | } |
4570 | 1 | session->last_resp_mid = rcvd->mid; |
4571 | 1 | } |
4572 | | /* Check to see if checking out extended token support */ |
4573 | 1 | if (session->max_token_checked == COAP_EXT_T_CHECKING && |
4574 | 0 | session->last_token) { |
4575 | 0 | coap_lg_crcv_t *lg_crcv; |
4576 | |
|
4577 | 0 | if (!coap_binary_equal(session->last_token, &rcvd->actual_token) || |
4578 | 0 | rcvd->actual_token.length != session->max_token_size || |
4579 | 0 | rcvd->code == COAP_RESPONSE_CODE(400) || |
4580 | 0 | rcvd->code == COAP_RESPONSE_CODE(503)) { |
4581 | 0 | coap_log_debug("Extended Token requested size support not available\n"); |
4582 | 0 | session->max_token_size = COAP_TOKEN_DEFAULT_MAX; |
4583 | 0 | } else { |
4584 | 0 | coap_log_debug("Extended Token support available\n"); |
4585 | 0 | } |
4586 | 0 | session->max_token_checked = COAP_EXT_T_CHECKED; |
4587 | | /* Need to remove lg_crcv set up for this test */ |
4588 | 0 | lg_crcv = coap_find_lg_crcv(session, rcvd); |
4589 | 0 | if (lg_crcv) { |
4590 | 0 | LL_DELETE(session->lg_crcv, lg_crcv); |
4591 | 0 | coap_block_delete_lg_crcv(session, lg_crcv); |
4592 | 0 | } |
4593 | 0 | coap_send_ack_lkd(session, rcvd); |
4594 | 0 | coap_reset_doing_first(session); |
4595 | 0 | return; |
4596 | 0 | } |
4597 | 1 | #if COAP_Q_BLOCK_SUPPORT |
4598 | | /* Check to see if checking out Q-Block support */ |
4599 | 1 | if (session->block_mode & COAP_BLOCK_PROBE_Q_BLOCK) { |
4600 | 0 | if (rcvd->code == COAP_RESPONSE_CODE(402)) { |
4601 | 0 | coap_log_debug("Q-Block support not available\n"); |
4602 | 0 | set_block_mode_drop_q(session->block_mode); |
4603 | 0 | } else { |
4604 | 0 | coap_block_b_t qblock; |
4605 | |
|
4606 | 0 | if (coap_get_block_b(session, rcvd, COAP_OPTION_Q_BLOCK2, &qblock)) { |
4607 | 0 | coap_log_debug("Q-Block support available\n"); |
4608 | 0 | set_block_mode_has_q(session->block_mode); |
4609 | 0 | if (session->state == COAP_SESSION_STATE_ESTABLISHED) |
4610 | | /* Flush out any entries on session->delayqueue */ |
4611 | 0 | coap_session_connected(session); |
4612 | 0 | } else { |
4613 | 0 | coap_log_debug("Q-Block support not available\n"); |
4614 | 0 | set_block_mode_drop_q(session->block_mode); |
4615 | 0 | } |
4616 | 0 | } |
4617 | 0 | coap_send_ack_lkd(session, rcvd); |
4618 | 0 | coap_reset_doing_first(session); |
4619 | 0 | return; |
4620 | 0 | } |
4621 | 1 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
4622 | | |
4623 | | /* See if the server is struggling */ |
4624 | 1 | if (rcvd->code == COAP_RESPONSE_CODE(503) || rcvd->code == COAP_RESPONSE_CODE(429)) { |
4625 | 0 | if (!sent && session->lg_crcv) |
4626 | 0 | sent = session->lg_crcv->sent_pdu; |
4627 | |
|
4628 | 0 | if (sent) { |
4629 | | /* sent is not active, so safe to reuse */ |
4630 | 0 | uint8_t buf[4]; |
4631 | | /* Need to resend request in max_age seconds */ |
4632 | 0 | size_t max_age = 60; |
4633 | 0 | coap_opt_iterator_t opt_iter; |
4634 | 0 | coap_opt_t *option = coap_check_option(rcvd, COAP_OPTION_MAXAGE, &opt_iter); |
4635 | 0 | coap_queue_t *node = coap_new_node(); |
4636 | 0 | if (option) |
4637 | 0 | max_age = coap_decode_var_bytes(coap_opt_value(option), |
4638 | 0 | coap_opt_length(option)); |
4639 | |
|
4640 | 0 | if (!node) { |
4641 | 0 | coap_log_debug("retransmit delay: insufficient memory\n"); |
4642 | 0 | return; |
4643 | 0 | } |
4644 | | |
4645 | 0 | coap_pdu_reference_lkd(sent); |
4646 | 0 | sent->mid = coap_new_message_id_lkd(session); |
4647 | 0 | coap_update_option(sent, |
4648 | 0 | COAP_OPTION_RTAG, |
4649 | 0 | coap_encode_var_safe(buf, sizeof(buf), |
4650 | 0 | ++session->tx_rtag), |
4651 | 0 | buf); |
4652 | 0 | coap_pdu_encode_header(sent, session->proto); |
4653 | 0 | node->id = sent->mid; |
4654 | 0 | node->pdu = sent; |
4655 | 0 | coap_log_debug(" %s: mid=0x%04x: re-request delayed for %u secs\n", |
4656 | 0 | coap_session_str(session), |
4657 | 0 | sent->mid, |
4658 | 0 | (unsigned int)max_age); |
4659 | 0 | node->timeout = (unsigned int)max_age * COAP_TICKS_PER_SECOND; |
4660 | | /* Use this to delay transmission */ |
4661 | 0 | coap_wait_ack(session->context, session, node); |
4662 | 0 | } |
4663 | 0 | return; |
4664 | 0 | } |
4665 | | |
4666 | 1 | if (session->block_mode & COAP_BLOCK_USE_LIBCOAP) { |
4667 | | /* See if need to send next block to server */ |
4668 | 0 | if (coap_handle_response_send_block(session, sent, rcvd)) { |
4669 | | /* Next block transmitted, no need to inform app */ |
4670 | 0 | coap_send_ack_lkd(session, rcvd); |
4671 | 0 | return; |
4672 | 0 | } |
4673 | | |
4674 | | /* Need to see if needing to request next block */ |
4675 | 0 | if (coap_handle_response_get_block(context, session, sent, rcvd, |
4676 | 0 | COAP_RECURSE_OK)) { |
4677 | | /* Next block transmitted, ack sent no need to inform app */ |
4678 | 0 | return; |
4679 | 0 | } |
4680 | 0 | } |
4681 | 1 | coap_reset_doing_first(session); |
4682 | | |
4683 | | /* Call application-specific response handler when available. */ |
4684 | 1 | coap_call_response_handler(session, sent, rcvd, NULL); |
4685 | 1 | } |
4686 | | #endif /* COAP_CLIENT_SUPPORT */ |
4687 | | |
4688 | | #if !COAP_DISABLE_TCP |
4689 | | static void |
4690 | | handle_signaling(coap_context_t *context, coap_session_t *session, |
4691 | 0 | coap_pdu_t *pdu) { |
4692 | 0 | coap_opt_iterator_t opt_iter; |
4693 | 0 | coap_opt_t *option; |
4694 | 0 | int set_mtu = 0; |
4695 | |
|
4696 | 0 | coap_option_iterator_init(pdu, &opt_iter, COAP_OPT_ALL); |
4697 | |
|
4698 | 0 | if (pdu->code == COAP_SIGNALING_CODE_CSM) { |
4699 | 0 | if (session->csm_not_seen) { |
4700 | 0 | coap_tick_t now; |
4701 | |
|
4702 | 0 | coap_ticks(&now); |
4703 | | /* CSM timeout before CSM seen */ |
4704 | 0 | coap_log_warn("***%s: CSM received after CSM timeout\n", |
4705 | 0 | coap_session_str(session)); |
4706 | 0 | coap_log_warn("***%s: Increase timeout in coap_context_set_csm_timeout_ms() to > %d\n", |
4707 | 0 | coap_session_str(session), |
4708 | 0 | (int)(((now - session->csm_tx) * 1000) / COAP_TICKS_PER_SECOND)); |
4709 | 0 | } |
4710 | 0 | if (session->max_token_checked == COAP_EXT_T_NOT_CHECKED) { |
4711 | 0 | session->max_token_size = COAP_TOKEN_DEFAULT_MAX; |
4712 | 0 | } |
4713 | 0 | while ((option = coap_option_next(&opt_iter))) { |
4714 | 0 | unsigned max_recv; |
4715 | |
|
4716 | 0 | switch ((coap_sig_csm_opt_t)opt_iter.number) { |
4717 | 0 | case COAP_SIG_OPT_MAX_MESSAGE_SIZE: |
4718 | 0 | max_recv = coap_decode_var_bytes(coap_opt_value(option), coap_opt_length(option)); |
4719 | 0 | if (max_recv > COAP_DEFAULT_MAX_PDU_RX_SIZE) { |
4720 | 0 | max_recv = COAP_DEFAULT_MAX_PDU_RX_SIZE; |
4721 | 0 | coap_log_debug("* %s: Restricting CSM Max-Message-Size size to %u\n", |
4722 | 0 | coap_session_str(session), max_recv); |
4723 | 0 | } |
4724 | 0 | coap_session_set_mtu(session, max_recv); |
4725 | 0 | set_mtu = 1; |
4726 | 0 | break; |
4727 | 0 | case COAP_SIG_OPT_BLOCK_WISE_TRANSFER: |
4728 | 0 | session->csm_block_supported = 1; |
4729 | 0 | break; |
4730 | 0 | case COAP_SIG_OPT_EXTENDED_TOKEN_LENGTH: |
4731 | 0 | session->max_token_size = |
4732 | 0 | coap_decode_var_bytes(coap_opt_value(option), |
4733 | 0 | coap_opt_length(option)); |
4734 | 0 | if (session->max_token_size < COAP_TOKEN_DEFAULT_MAX) |
4735 | 0 | session->max_token_size = COAP_TOKEN_DEFAULT_MAX; |
4736 | 0 | else if (session->max_token_size > COAP_TOKEN_EXT_MAX) |
4737 | 0 | session->max_token_size = COAP_TOKEN_EXT_MAX; |
4738 | 0 | session->max_token_checked = COAP_EXT_T_CHECKED; |
4739 | 0 | break; |
4740 | 0 | default: |
4741 | 0 | break; |
4742 | 0 | } |
4743 | 0 | } |
4744 | 0 | if (set_mtu) { |
4745 | 0 | if (session->mtu > COAP_BERT_BASE && session->csm_block_supported) |
4746 | 0 | session->csm_bert_rem_support = 1; |
4747 | 0 | else |
4748 | 0 | session->csm_bert_rem_support = 0; |
4749 | 0 | } |
4750 | 0 | if (session->state == COAP_SESSION_STATE_CSM) |
4751 | 0 | coap_session_connected(session); |
4752 | 0 | } else if (pdu->code == COAP_SIGNALING_CODE_PING) { |
4753 | 0 | coap_pdu_t *pong = coap_pdu_init(COAP_MESSAGE_CON, COAP_SIGNALING_CODE_PONG, 0, 1); |
4754 | 0 | if (context->ping_cb) { |
4755 | 0 | coap_lock_callback(context->ping_cb(session, pdu, pdu->mid)); |
4756 | 0 | } |
4757 | 0 | if (pong) { |
4758 | 0 | coap_add_option_internal(pong, (coap_option_num_t)COAP_SIG_OPT_CUSTODY, |
4759 | 0 | 0, NULL); |
4760 | 0 | coap_send_internal(session, pong, NULL); |
4761 | 0 | } |
4762 | 0 | } else if (pdu->code == COAP_SIGNALING_CODE_PONG) { |
4763 | 0 | session->last_pong = session->last_rx_tx; |
4764 | 0 | session->ping_failed = 0; |
4765 | 0 | if (context->pong_cb) { |
4766 | 0 | coap_lock_callback(context->pong_cb(session, pdu, pdu->mid)); |
4767 | 0 | } |
4768 | 0 | } else if (pdu->code == COAP_SIGNALING_CODE_RELEASE |
4769 | 0 | || pdu->code == COAP_SIGNALING_CODE_ABORT) { |
4770 | 0 | coap_session_disconnected_lkd(session, COAP_NACK_RST); |
4771 | 0 | } else { |
4772 | 0 | coap_log_debug("PDU signaling code %u.%02u unknown\n", (pdu->code >> 5) & 0x7, pdu->code & 0x1f); |
4773 | 0 | } |
4774 | 0 | } |
4775 | | #endif /* !COAP_DISABLE_TCP */ |
4776 | | |
4777 | | static int |
4778 | 99 | check_token_size(coap_session_t *session, const coap_pdu_t *pdu, int is_local_mcast) { |
4779 | 99 | if (COAP_PDU_IS_REQUEST(pdu) && |
4780 | 97 | pdu->actual_token.length > |
4781 | 97 | (session->type == COAP_SESSION_TYPE_CLIENT ? |
4782 | 97 | session->max_token_size : session->context->max_token_size)) { |
4783 | | /* https://rfc-editor.org/rfc/rfc8974#section-2.2.2 */ |
4784 | 0 | if (is_local_mcast) |
4785 | 0 | return 0; |
4786 | 0 | if (session->max_token_size > COAP_TOKEN_DEFAULT_MAX) { |
4787 | 0 | coap_opt_filter_t opt_filter; |
4788 | 0 | coap_pdu_t *response; |
4789 | |
|
4790 | 0 | memset(&opt_filter, 0, sizeof(coap_opt_filter_t)); |
4791 | 0 | response = coap_new_error_response(pdu, COAP_RESPONSE_CODE(400), |
4792 | 0 | &opt_filter); |
4793 | 0 | if (!response) { |
4794 | 0 | coap_log_warn("coap_dispatch: cannot create error response\n"); |
4795 | 0 | } else { |
4796 | | /* |
4797 | | * Note - have to leave in oversize token as per |
4798 | | * https://rfc-editor.org/rfc/rfc7252#section-5.3.1 |
4799 | | */ |
4800 | 0 | if (coap_send_internal(session, response, NULL) == COAP_INVALID_MID) |
4801 | 0 | coap_log_warn("coap_dispatch: error sending response\n"); |
4802 | 0 | } |
4803 | 0 | } else { |
4804 | | /* Indicate no extended token support */ |
4805 | 0 | coap_send_rst_lkd(session, pdu); |
4806 | 0 | } |
4807 | 0 | return 0; |
4808 | 0 | } |
4809 | 99 | return 1; |
4810 | 99 | } |
4811 | | |
4812 | | void |
4813 | | coap_dispatch(coap_context_t *context, coap_session_t *session, |
4814 | 100 | coap_pdu_t *pdu) { |
4815 | 100 | coap_queue_t *sent = NULL; |
4816 | 100 | coap_pdu_t *response; |
4817 | 100 | coap_pdu_t *orig_pdu = NULL; |
4818 | 100 | coap_opt_filter_t opt_filter; |
4819 | 100 | int is_ping_rst; |
4820 | 100 | int packet_is_bad = 0; |
4821 | 100 | #if COAP_OSCORE_SUPPORT |
4822 | 100 | coap_opt_iterator_t opt_iter; |
4823 | 100 | coap_pdu_t *dec_pdu = NULL; |
4824 | 100 | #endif /* COAP_OSCORE_SUPPORT */ |
4825 | 100 | int is_ext_token_rst = 0; |
4826 | 100 | int oscore_invalid = 0; |
4827 | 100 | int is_local_mcast = 0; |
4828 | | |
4829 | 100 | coap_pdu_reference_lkd(pdu); |
4830 | 100 | pdu->session = session; |
4831 | 100 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
4832 | | |
4833 | 100 | if (COAP_PDU_IS_REQUEST(pdu) && coap_is_mcast(&session->addr_info.local)) { |
4834 | | /* Need to be careful with responses to multicast requests */ |
4835 | 0 | is_local_mcast = 1; |
4836 | 0 | if (COAP_PROTO_RELIABLE(session->proto) || pdu->type != COAP_MESSAGE_NON) { |
4837 | 0 | coap_log_info("Invalid multicast packet received RFC7252 8.1\n"); |
4838 | 0 | return; |
4839 | 0 | } |
4840 | 0 | } |
4841 | | |
4842 | | /* Check validity of received code */ |
4843 | 100 | if (!coap_check_code_class(session, pdu)) { |
4844 | 0 | coap_log_info("coap_dispatch: Received invalid PDU code (%d.%02d)\n", |
4845 | 0 | COAP_RESPONSE_CLASS(pdu->code), |
4846 | 0 | pdu->code & 0x1f); |
4847 | 0 | packet_is_bad = 1; |
4848 | 0 | if (pdu->type == COAP_MESSAGE_CON) { |
4849 | 0 | coap_send_message_type_lkd(session, pdu, COAP_MESSAGE_RST); |
4850 | 0 | } |
4851 | | /* find message id in sendqueue to stop retransmission (code is not 0.00) */ |
4852 | 0 | coap_remove_from_queue(&context->sendqueue, session, pdu->mid, &pdu->actual_token, &sent); |
4853 | 0 | goto cleanup; |
4854 | 0 | } |
4855 | | |
4856 | 100 | coap_option_filter_clear(&opt_filter); |
4857 | | |
4858 | 100 | #if COAP_SERVER_SUPPORT |
4859 | | /* See if this a repeat request */ |
4860 | 100 | if (COAP_PDU_IS_REQUEST(pdu) && session->last_resp_pdu && |
4861 | 65 | pdu->mid == session->last_resp_pdu->mid) { |
4862 | 0 | #if COAP_OSCORE_SUPPORT |
4863 | 0 | uint8_t oscore_encryption = session->oscore_encryption; |
4864 | |
|
4865 | 0 | session->oscore_encryption = 0; |
4866 | 0 | #endif /* COAP_OSCORE_SUPPORT */ |
4867 | | /* Account for coap_send_internal() doing a coap_delete_pdu() and |
4868 | | last_resp_pdu must not be removed */ |
4869 | 0 | coap_pdu_reference_lkd(session->last_resp_pdu); |
4870 | 0 | coap_log_debug("Retransmit response to duplicate request\n"); |
4871 | 0 | if (coap_send_internal(session, session->last_resp_pdu, NULL) != COAP_INVALID_MID) { |
4872 | 0 | #if COAP_OSCORE_SUPPORT |
4873 | 0 | session->oscore_encryption = oscore_encryption; |
4874 | 0 | #endif /* COAP_OSCORE_SUPPORT */ |
4875 | 0 | goto finish; |
4876 | 0 | } |
4877 | 0 | #if COAP_OSCORE_SUPPORT |
4878 | 0 | session->oscore_encryption = oscore_encryption; |
4879 | 0 | #endif /* COAP_OSCORE_SUPPORT */ |
4880 | 0 | } |
4881 | 100 | #endif /* COAP_SERVER_SUPPORT */ |
4882 | 100 | if (pdu->type == COAP_MESSAGE_NON || pdu->type == COAP_MESSAGE_CON) { |
4883 | 99 | if (!check_token_size(session, pdu, is_local_mcast)) { |
4884 | 0 | goto cleanup; |
4885 | 0 | } |
4886 | 99 | } |
4887 | 100 | #if COAP_OSCORE_SUPPORT |
4888 | 100 | if (!COAP_PDU_IS_SIGNALING(pdu) && |
4889 | 100 | coap_option_check_critical(session, pdu, &opt_filter, COAP_CRIT_UNKNOWN) == 0) { |
4890 | 2 | if (!is_local_mcast && (pdu->type == COAP_MESSAGE_CON || pdu->type == COAP_MESSAGE_NON)) { |
4891 | 2 | if (COAP_PDU_IS_REQUEST(pdu)) { |
4892 | 1 | response = |
4893 | 1 | coap_new_error_response(pdu, COAP_RESPONSE_CODE(402), &opt_filter); |
4894 | | |
4895 | 1 | if (!response) { |
4896 | 0 | coap_log_warn("coap_dispatch: cannot create error response\n"); |
4897 | 1 | } else { |
4898 | 1 | if (coap_send_internal(session, response, NULL) == COAP_INVALID_MID) |
4899 | 1 | coap_log_warn("coap_dispatch: error sending response\n"); |
4900 | 1 | } |
4901 | 1 | } else { |
4902 | 1 | coap_send_rst_lkd(session, pdu); |
4903 | 1 | } |
4904 | 2 | } |
4905 | 2 | goto cleanup; |
4906 | 2 | } |
4907 | | |
4908 | 98 | if (coap_check_option(pdu, COAP_OPTION_OSCORE, &opt_iter) != NULL) { |
4909 | 0 | int decrypt = 1; |
4910 | 0 | #if COAP_SERVER_SUPPORT |
4911 | 0 | coap_opt_t *opt; |
4912 | 0 | coap_resource_t *resource; |
4913 | 0 | coap_uri_t uri; |
4914 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
4915 | |
|
4916 | 0 | if (COAP_PDU_IS_RESPONSE(pdu) && !session->oscore_encryption) |
4917 | 0 | decrypt = 0; |
4918 | |
|
4919 | 0 | #if COAP_SERVER_SUPPORT |
4920 | 0 | if (decrypt && COAP_PDU_IS_REQUEST(pdu) && |
4921 | 0 | coap_check_option(pdu, COAP_OPTION_PROXY_SCHEME, &opt_iter) != NULL && |
4922 | 0 | (opt = coap_check_option(pdu, COAP_OPTION_URI_HOST, &opt_iter)) |
4923 | 0 | != NULL) { |
4924 | | /* Need to check whether this is a direct or proxy session */ |
4925 | 0 | memset(&uri, 0, sizeof(uri)); |
4926 | 0 | uri.host.length = coap_opt_length(opt); |
4927 | 0 | uri.host.s = coap_opt_value(opt); |
4928 | 0 | resource = context->proxy_uri_resource; |
4929 | 0 | if (uri.host.length && resource && resource->proxy_name_count && |
4930 | 0 | resource->proxy_name_list) { |
4931 | 0 | size_t i; |
4932 | 0 | for (i = 0; i < resource->proxy_name_count; i++) { |
4933 | 0 | if (coap_string_equal(&uri.host, resource->proxy_name_list[i])) { |
4934 | 0 | break; |
4935 | 0 | } |
4936 | 0 | } |
4937 | 0 | if (i == resource->proxy_name_count) { |
4938 | | /* This server is not hosting the proxy connection endpoint */ |
4939 | 0 | decrypt = 0; |
4940 | 0 | } |
4941 | 0 | } |
4942 | 0 | } |
4943 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
4944 | 0 | if (decrypt) { |
4945 | | /* find message id in sendqueue to stop retransmission and get sent (not empty packet) */ |
4946 | 0 | coap_remove_from_queue(&context->sendqueue, session, pdu->mid, &pdu->actual_token, &sent); |
4947 | | /* Bump ref so pdu is not freed of, and keep a pointer to it */ |
4948 | 0 | orig_pdu = pdu; |
4949 | 0 | coap_pdu_reference_lkd(orig_pdu); |
4950 | 0 | if ((dec_pdu = coap_oscore_decrypt_pdu(session, pdu)) == NULL) { |
4951 | 0 | if (session->recipient_ctx == NULL || |
4952 | 0 | (session->recipient_ctx->initial_state == 0 && |
4953 | 0 | session->b_2_step == COAP_OSCORE_B_2_NONE)) { |
4954 | 0 | coap_log_warn("OSCORE: PDU could not be decrypted\n"); |
4955 | 0 | } |
4956 | 0 | coap_delete_node_lkd(sent); |
4957 | 0 | coap_delete_pdu_lkd(orig_pdu); |
4958 | 0 | goto finish; |
4959 | 0 | } else { |
4960 | 0 | session->oscore_encryption = 1; |
4961 | 0 | coap_pdu_reference_lkd(dec_pdu); |
4962 | 0 | coap_pdu_release_lkd(pdu); |
4963 | 0 | pdu = dec_pdu; |
4964 | 0 | } |
4965 | 0 | coap_log_debug("Decrypted PDU\n"); |
4966 | 0 | coap_show_pdu(COAP_LOG_DEBUG, pdu); |
4967 | 0 | } |
4968 | 98 | } else if (COAP_PDU_IS_RESPONSE(pdu) && |
4969 | 1 | session->oscore_encryption && |
4970 | 0 | pdu->type != COAP_MESSAGE_RST) { |
4971 | 0 | if (COAP_RESPONSE_CLASS(pdu->code) == 2) { |
4972 | | /* Violates RFC 8613 2 */ |
4973 | 0 | coap_log_err("received an invalid response to the OSCORE request\n"); |
4974 | 0 | oscore_invalid = 1; |
4975 | 0 | } |
4976 | 0 | } |
4977 | 98 | #endif /* COAP_OSCORE_SUPPORT */ |
4978 | | |
4979 | 98 | switch (pdu->type) { |
4980 | 1 | case COAP_MESSAGE_ACK: |
4981 | 1 | if (NULL == sent) { |
4982 | | /* find message id in sendqueue to stop retransmission (no token if empty) */ |
4983 | 1 | coap_remove_from_queue(&context->sendqueue, session, pdu->mid, |
4984 | 1 | pdu->code == 0 ? NULL : &pdu->actual_token, &sent); |
4985 | 1 | } |
4986 | | |
4987 | 1 | if (sent && session->con_active) { |
4988 | 0 | session->con_active--; |
4989 | 0 | if (session->state == COAP_SESSION_STATE_ESTABLISHED) |
4990 | | /* Flush out any entries on session->delayqueue */ |
4991 | 0 | coap_session_connected(session); |
4992 | 0 | } |
4993 | 1 | if (oscore_invalid || |
4994 | 1 | coap_option_check_critical(session, pdu, &opt_filter, COAP_CRIT_UNKNOWN) == 0) { |
4995 | 0 | packet_is_bad = 1; |
4996 | 0 | goto cleanup; |
4997 | 0 | } |
4998 | | |
4999 | 1 | #if COAP_SERVER_SUPPORT |
5000 | | /* if sent code was >= 64 the message might have been a |
5001 | | * notification. Then, we must flag the observer to be alive |
5002 | | * by setting obs->fail_cnt = 0. */ |
5003 | 1 | if (sent && COAP_RESPONSE_CLASS(sent->pdu->code) == 2) { |
5004 | 0 | coap_touch_observer(context, sent->session, &sent->pdu->actual_token); |
5005 | 0 | } |
5006 | 1 | #endif /* COAP_SERVER_SUPPORT */ |
5007 | | |
5008 | 1 | #if COAP_Q_BLOCK_SUPPORT |
5009 | 1 | if (session->lg_xmit && sent && sent->pdu && sent->pdu->type == COAP_MESSAGE_CON && |
5010 | 0 | !(session->block_mode & COAP_BLOCK_PROBE_Q_BLOCK)) { |
5011 | 0 | int doing_q_block = 0; |
5012 | 0 | coap_lg_xmit_t *lg_xmit = NULL; |
5013 | |
|
5014 | 0 | LL_FOREACH(session->lg_xmit, lg_xmit) { |
5015 | 0 | if ((lg_xmit->option == COAP_OPTION_Q_BLOCK1 || lg_xmit->option == COAP_OPTION_Q_BLOCK2) && |
5016 | 0 | lg_xmit->last_all_sent == 0 && lg_xmit->sent_pdu->type != COAP_MESSAGE_NON) { |
5017 | 0 | doing_q_block = 1; |
5018 | 0 | break; |
5019 | 0 | } |
5020 | 0 | } |
5021 | 0 | if (doing_q_block && lg_xmit) { |
5022 | 0 | coap_block_b_t block; |
5023 | |
|
5024 | 0 | memset(&block, 0, sizeof(block)); |
5025 | 0 | if (lg_xmit->option == COAP_OPTION_Q_BLOCK1) { |
5026 | 0 | block.num = lg_xmit->last_block + lg_xmit->b.b1.count; |
5027 | 0 | } else { |
5028 | 0 | block.num = lg_xmit->last_block; |
5029 | 0 | } |
5030 | 0 | block.m = 1; |
5031 | 0 | block.szx = block.aszx = lg_xmit->blk_size; |
5032 | 0 | block.defined = 1; |
5033 | 0 | block.bert = 0; |
5034 | 0 | block.chunk_size = 1024; |
5035 | |
|
5036 | 0 | coap_send_q_blocks(session, lg_xmit, block, |
5037 | 0 | lg_xmit->sent_pdu, COAP_SEND_SKIP_PDU); |
5038 | 0 | } |
5039 | 0 | } |
5040 | 1 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
5041 | 1 | if (pdu->code == 0) { |
5042 | 0 | #if COAP_CLIENT_SUPPORT |
5043 | | /* |
5044 | | * In coap_send(), lg_crcv was not set up if type is CON and protocol is not |
5045 | | * reliable to save overhead as this can be set up on detection of a (Q)-Block2 |
5046 | | * response if the response was piggy-backed. Here, a separate response |
5047 | | * detected and so the lg_crcv needs to be set up before the sent PDU |
5048 | | * information is lost. |
5049 | | * |
5050 | | * lg_crcv was not set up if not a CoAP request. |
5051 | | * |
5052 | | * lg_crcv was always set up in coap_send() if Observe, Oscore and (Q)-Block1 |
5053 | | * options. |
5054 | | */ |
5055 | 0 | if (sent && |
5056 | 0 | !coap_check_send_need_lg_crcv(session, sent->pdu) && |
5057 | 0 | COAP_PDU_IS_REQUEST(sent->pdu)) { |
5058 | | /* |
5059 | | * lg_crcv was not set up in coap_send(). It could have been set up |
5060 | | * the first separate response. |
5061 | | * See if there already is a lg_crcv set up. |
5062 | | */ |
5063 | 0 | coap_lg_crcv_t *lg_crcv; |
5064 | 0 | uint64_t token_match = |
5065 | 0 | STATE_TOKEN_BASE(coap_decode_var_bytes8(sent->pdu->actual_token.s, |
5066 | 0 | sent->pdu->actual_token.length)); |
5067 | |
|
5068 | 0 | LL_FOREACH(session->lg_crcv, lg_crcv) { |
5069 | 0 | if (token_match == STATE_TOKEN_BASE(lg_crcv->state_token) || |
5070 | 0 | coap_binary_equal(&sent->pdu->actual_token, lg_crcv->app_token)) { |
5071 | 0 | break; |
5072 | 0 | } |
5073 | 0 | } |
5074 | 0 | if (!lg_crcv) { |
5075 | | /* |
5076 | | * Need to set up a lg_crcv as it was not set up in coap_send() |
5077 | | * to save time, but server has not sent back a piggy-back response. |
5078 | | */ |
5079 | 0 | lg_crcv = coap_block_new_lg_crcv(session, sent->pdu, NULL); |
5080 | 0 | if (lg_crcv) { |
5081 | 0 | LL_PREPEND(session->lg_crcv, lg_crcv); |
5082 | 0 | } |
5083 | 0 | } |
5084 | 0 | } |
5085 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
5086 | | /* an empty ACK needs no further handling */ |
5087 | 0 | goto cleanup; |
5088 | 1 | } else if (COAP_PDU_IS_REQUEST(pdu)) { |
5089 | | /* This is not legitimate - Request using ACK - ignore */ |
5090 | 0 | coap_log_debug("dropped ACK with request code (%d.%02d)\n", |
5091 | 0 | COAP_RESPONSE_CLASS(pdu->code), |
5092 | 0 | pdu->code & 0x1f); |
5093 | 0 | packet_is_bad = 1; |
5094 | 0 | goto cleanup; |
5095 | 0 | } |
5096 | | |
5097 | 1 | break; |
5098 | | |
5099 | 1 | case COAP_MESSAGE_RST: |
5100 | | /* We have sent something the receiver disliked, so we remove |
5101 | | * not only the message id but also the subscriptions we might |
5102 | | * have. */ |
5103 | 0 | is_ping_rst = 0; |
5104 | 0 | if (pdu->mid == session->last_ping_mid && |
5105 | 0 | session->last_ping > 0) |
5106 | 0 | is_ping_rst = 1; |
5107 | |
|
5108 | 0 | #if COAP_CLIENT_SUPPORT |
5109 | 0 | #if COAP_Q_BLOCK_SUPPORT |
5110 | | /* Check to see if checking out Q-Block support */ |
5111 | 0 | if (session->block_mode & COAP_BLOCK_PROBE_Q_BLOCK && |
5112 | 0 | session->remote_test_mid == pdu->mid) { |
5113 | 0 | coap_log_debug("Q-Block support not available\n"); |
5114 | 0 | set_block_mode_drop_q(session->block_mode); |
5115 | 0 | coap_reset_doing_first(session); |
5116 | 0 | } |
5117 | 0 | #endif /* COAP_Q_BLOCK_SUPPORT */ |
5118 | | |
5119 | | /* Check to see if checking out extended token support */ |
5120 | 0 | if (session->max_token_checked == COAP_EXT_T_CHECKING && |
5121 | 0 | session->remote_test_mid == pdu->mid) { |
5122 | 0 | coap_log_debug("Extended Token support not available\n"); |
5123 | 0 | session->max_token_size = COAP_TOKEN_DEFAULT_MAX; |
5124 | 0 | session->max_token_checked = COAP_EXT_T_CHECKED; |
5125 | 0 | coap_reset_doing_first(session); |
5126 | 0 | is_ext_token_rst = 1; |
5127 | 0 | } |
5128 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
5129 | |
|
5130 | 0 | if (!is_ping_rst && !is_ext_token_rst) |
5131 | 0 | coap_log_alert("got RST for mid=0x%04x\n", pdu->mid); |
5132 | |
|
5133 | 0 | if (session->con_active) { |
5134 | 0 | session->con_active--; |
5135 | 0 | if (session->state == COAP_SESSION_STATE_ESTABLISHED) |
5136 | | /* Flush out any entries on session->delayqueue */ |
5137 | 0 | coap_session_connected(session); |
5138 | 0 | } |
5139 | | |
5140 | | /* find message id in sendqueue to stop retransmission (no token as RST) */ |
5141 | 0 | coap_remove_from_queue(&context->sendqueue, session, pdu->mid, NULL, &sent); |
5142 | |
|
5143 | 0 | if (sent) { |
5144 | 0 | if (!is_ping_rst) |
5145 | 0 | coap_cancel(context, sent); |
5146 | |
|
5147 | 0 | if (!is_ping_rst && !is_ext_token_rst) { |
5148 | 0 | if (sent->pdu->type==COAP_MESSAGE_CON) { |
5149 | 0 | coap_handle_nack(sent->session, sent->pdu, COAP_NACK_RST, sent->id); |
5150 | 0 | } |
5151 | 0 | } else if (is_ping_rst) { |
5152 | 0 | if (context->pong_cb) { |
5153 | 0 | coap_lock_callback(context->pong_cb(session, pdu, pdu->mid)); |
5154 | 0 | } |
5155 | 0 | session->last_pong = session->last_rx_tx; |
5156 | 0 | session->ping_failed = 0; |
5157 | 0 | session->last_ping_mid = COAP_INVALID_MID; |
5158 | 0 | } |
5159 | 0 | } else { |
5160 | 0 | #if COAP_SERVER_SUPPORT |
5161 | | /* Need to check is there is a subscription active and delete it */ |
5162 | 0 | RESOURCES_ITER(context->resources, r) { |
5163 | 0 | coap_subscription_t *obs, *tmp; |
5164 | 0 | LL_FOREACH_SAFE(r->subscribers, obs, tmp) { |
5165 | 0 | if (obs->pdu->mid == pdu->mid && obs->session == session) { |
5166 | | /* Need to do this now as session may get de-referenced */ |
5167 | 0 | coap_session_reference_lkd(session); |
5168 | 0 | coap_delete_observer(r, session, &obs->pdu->actual_token); |
5169 | 0 | coap_handle_nack(session, NULL, COAP_NACK_RST, pdu->mid); |
5170 | 0 | coap_session_release_lkd(session); |
5171 | 0 | goto cleanup; |
5172 | 0 | } |
5173 | 0 | } |
5174 | 0 | } |
5175 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
5176 | 0 | coap_handle_nack(session, NULL, COAP_NACK_RST, pdu->mid); |
5177 | 0 | } |
5178 | 0 | #if COAP_PROXY_SUPPORT |
5179 | 0 | if (!is_ping_rst) { |
5180 | | /* Need to check is there is a proxy subscription active and delete it */ |
5181 | 0 | coap_delete_proxy_subscriber(session, NULL, pdu->mid, COAP_PROXY_SUBS_MID); |
5182 | 0 | } |
5183 | 0 | #endif /* COAP_PROXY_SUPPORT */ |
5184 | 0 | goto cleanup; |
5185 | | |
5186 | 19 | case COAP_MESSAGE_NON: |
5187 | | /* check for oscore issue or unknown critical options */ |
5188 | 19 | if (oscore_invalid || |
5189 | 19 | coap_option_check_critical(session, pdu, &opt_filter, COAP_CRIT_UNKNOWN) == 0) { |
5190 | 0 | packet_is_bad = 1; |
5191 | 0 | if (COAP_PDU_IS_REQUEST(pdu)) { |
5192 | 0 | response = |
5193 | 0 | coap_new_error_response(pdu, COAP_RESPONSE_CODE(402), &opt_filter); |
5194 | |
|
5195 | 0 | if (!response) { |
5196 | 0 | coap_log_warn("coap_dispatch: cannot create error response\n"); |
5197 | 0 | } else { |
5198 | 0 | if (coap_send_internal(session, response, NULL) == COAP_INVALID_MID) |
5199 | 0 | coap_log_warn("coap_dispatch: error sending response\n"); |
5200 | 0 | } |
5201 | 0 | } else { |
5202 | 0 | coap_send_rst_lkd(session, pdu); |
5203 | 0 | } |
5204 | 0 | goto cleanup; |
5205 | 0 | } |
5206 | 19 | break; |
5207 | | |
5208 | 78 | case COAP_MESSAGE_CON: |
5209 | | /* In a lossy context, the ACK of a separate response may have |
5210 | | * been lost, so we need to stop retransmitting requests with the |
5211 | | * same token. Matching on token potentially containing ext length bytes. |
5212 | | */ |
5213 | | /* find message token in sendqueue to stop retransmission */ |
5214 | 78 | if (pdu->code != 0) |
5215 | 78 | coap_remove_from_queue_token(&context->sendqueue, session, &pdu->actual_token, &sent); |
5216 | | |
5217 | | /* check for oscore issue or unknown critical options in non-signaling messages */ |
5218 | 78 | if (oscore_invalid || |
5219 | 78 | (!COAP_PDU_IS_SIGNALING(pdu) && |
5220 | 78 | coap_option_check_critical(session, pdu, &opt_filter, COAP_CRIT_UNKNOWN) == 0)) { |
5221 | 0 | packet_is_bad = 1; |
5222 | 0 | if (COAP_PDU_IS_REQUEST(pdu)) { |
5223 | 0 | response = |
5224 | 0 | coap_new_error_response(pdu, COAP_RESPONSE_CODE(402), &opt_filter); |
5225 | |
|
5226 | 0 | if (!response) { |
5227 | 0 | coap_log_warn("coap_dispatch: cannot create error response\n"); |
5228 | 0 | } else { |
5229 | 0 | if (coap_send_internal(session, response, NULL) == COAP_INVALID_MID) |
5230 | 0 | coap_log_warn("coap_dispatch: error sending response\n"); |
5231 | 0 | } |
5232 | 0 | } else { |
5233 | 0 | coap_send_rst_lkd(session, pdu); |
5234 | 0 | } |
5235 | 0 | goto cleanup; |
5236 | 0 | } |
5237 | 78 | break; |
5238 | 78 | default: |
5239 | 0 | break; |
5240 | 98 | } |
5241 | | |
5242 | | /* Pass message to upper layer if a specific handler was |
5243 | | * registered for a request that should be handled locally. */ |
5244 | 98 | #if !COAP_DISABLE_TCP |
5245 | 98 | if (COAP_PDU_IS_SIGNALING(pdu)) |
5246 | 0 | handle_signaling(context, session, pdu); |
5247 | 98 | else |
5248 | 98 | #endif /* !COAP_DISABLE_TCP */ |
5249 | 98 | #if COAP_SERVER_SUPPORT |
5250 | 98 | if (COAP_PDU_IS_REQUEST(pdu)) |
5251 | 96 | handle_request(context, session, pdu, orig_pdu); |
5252 | 2 | else |
5253 | 2 | #endif /* COAP_SERVER_SUPPORT */ |
5254 | 2 | #if COAP_CLIENT_SUPPORT |
5255 | 2 | if (COAP_PDU_IS_RESPONSE(pdu)) |
5256 | 1 | handle_response(context, session, sent ? sent->pdu : NULL, pdu); |
5257 | 1 | else |
5258 | 1 | #endif /* COAP_CLIENT_SUPPORT */ |
5259 | 1 | { |
5260 | 1 | if (COAP_PDU_IS_EMPTY(pdu)) { |
5261 | 1 | if (context->ping_cb) { |
5262 | 0 | coap_lock_callback(context->ping_cb(session, pdu, pdu->mid)); |
5263 | 0 | } |
5264 | 1 | } else { |
5265 | 0 | packet_is_bad = 1; |
5266 | 0 | } |
5267 | 1 | coap_log_debug("dropped message with invalid code (%d.%02d)\n", |
5268 | 1 | COAP_RESPONSE_CLASS(pdu->code), |
5269 | 1 | pdu->code & 0x1f); |
5270 | | |
5271 | 1 | if (!coap_is_mcast(&session->addr_info.local)) { |
5272 | 1 | if (COAP_PDU_IS_EMPTY(pdu)) { |
5273 | 1 | if (COAP_PROTO_NOT_RELIABLE(session->proto)) { |
5274 | 1 | coap_tick_t now; |
5275 | 1 | coap_ticks(&now); |
5276 | 1 | if (session->last_tx_rst + COAP_TICKS_PER_SECOND/4 < now) { |
5277 | 1 | coap_send_message_type_lkd(session, pdu, COAP_MESSAGE_RST); |
5278 | 1 | session->last_tx_rst = now; |
5279 | 1 | } |
5280 | 1 | } |
5281 | 1 | } else { |
5282 | 0 | if (pdu->type == COAP_MESSAGE_CON) |
5283 | 0 | coap_send_message_type_lkd(session, pdu, COAP_MESSAGE_RST); |
5284 | 0 | } |
5285 | 1 | } |
5286 | 1 | } |
5287 | | |
5288 | 100 | cleanup: |
5289 | 100 | if (packet_is_bad) { |
5290 | 0 | if (sent) { |
5291 | 0 | coap_handle_nack(session, sent->pdu, COAP_NACK_BAD_RESPONSE, sent->id); |
5292 | 0 | } else { |
5293 | 0 | coap_handle_event_lkd(context, COAP_EVENT_BAD_PACKET, session); |
5294 | 0 | } |
5295 | 0 | } |
5296 | 100 | coap_delete_pdu_lkd(orig_pdu); |
5297 | 100 | coap_delete_node_lkd(sent); |
5298 | 100 | #if COAP_OSCORE_SUPPORT |
5299 | 100 | coap_delete_pdu_lkd(dec_pdu); |
5300 | 100 | #endif /* COAP_OSCORE_SUPPORT */ |
5301 | | |
5302 | 100 | #if COAP_SERVER_SUPPORT || COAP_OSCORE_SUPPORT |
5303 | 100 | finish: |
5304 | 100 | #endif /* COAP_SERVER_SUPPORT || COAP_OSCORE_SUPPORT */ |
5305 | 100 | coap_pdu_release_lkd(pdu); |
5306 | 100 | } |
5307 | | |
5308 | | #if COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_DEBUG |
5309 | | static const char * |
5310 | 0 | coap_event_name(coap_event_t event) { |
5311 | 0 | switch (event) { |
5312 | 0 | case COAP_EVENT_DTLS_CLOSED: |
5313 | 0 | return "COAP_EVENT_DTLS_CLOSED"; |
5314 | 0 | case COAP_EVENT_DTLS_CONNECTED: |
5315 | 0 | return "COAP_EVENT_DTLS_CONNECTED"; |
5316 | 0 | case COAP_EVENT_DTLS_RENEGOTIATE: |
5317 | 0 | return "COAP_EVENT_DTLS_RENEGOTIATE"; |
5318 | 0 | case COAP_EVENT_DTLS_ERROR: |
5319 | 0 | return "COAP_EVENT_DTLS_ERROR"; |
5320 | 0 | case COAP_EVENT_TCP_CONNECTED: |
5321 | 0 | return "COAP_EVENT_TCP_CONNECTED"; |
5322 | 0 | case COAP_EVENT_TCP_CLOSED: |
5323 | 0 | return "COAP_EVENT_TCP_CLOSED"; |
5324 | 0 | case COAP_EVENT_TCP_FAILED: |
5325 | 0 | return "COAP_EVENT_TCP_FAILED"; |
5326 | 0 | case COAP_EVENT_SESSION_CONNECTED: |
5327 | 0 | return "COAP_EVENT_SESSION_CONNECTED"; |
5328 | 0 | case COAP_EVENT_SESSION_CLOSED: |
5329 | 0 | return "COAP_EVENT_SESSION_CLOSED"; |
5330 | 0 | case COAP_EVENT_SESSION_FAILED: |
5331 | 0 | return "COAP_EVENT_SESSION_FAILED"; |
5332 | 0 | case COAP_EVENT_PARTIAL_BLOCK: |
5333 | 0 | return "COAP_EVENT_PARTIAL_BLOCK"; |
5334 | 0 | case COAP_EVENT_XMIT_BLOCK_FAIL: |
5335 | 0 | return "COAP_EVENT_XMIT_BLOCK_FAIL"; |
5336 | 0 | case COAP_EVENT_BLOCK_ISSUE: |
5337 | 0 | return "COAP_EVENT_BLOCK_ISSUE"; |
5338 | 0 | case COAP_EVENT_SERVER_SESSION_NEW: |
5339 | 0 | return "COAP_EVENT_SERVER_SESSION_NEW"; |
5340 | 0 | case COAP_EVENT_SERVER_SESSION_DEL: |
5341 | 0 | return "COAP_EVENT_SERVER_SESSION_DEL"; |
5342 | 0 | case COAP_EVENT_SERVER_SESSION_CONNECTED: |
5343 | 0 | return "COAP_EVENT_SERVER_SESSION_CONNECTED"; |
5344 | 0 | case COAP_EVENT_BAD_PACKET: |
5345 | 0 | return "COAP_EVENT_BAD_PACKET"; |
5346 | 0 | case COAP_EVENT_MSG_RETRANSMITTED: |
5347 | 0 | return "COAP_EVENT_MSG_RETRANSMITTED"; |
5348 | 0 | case COAP_EVENT_FIRST_PDU_FAIL: |
5349 | 0 | return "COAP_EVENT_FIRST_PDU_FAIL"; |
5350 | 0 | case COAP_EVENT_OSCORE_DECRYPTION_FAILURE: |
5351 | 0 | return "COAP_EVENT_OSCORE_DECRYPTION_FAILURE"; |
5352 | 0 | case COAP_EVENT_OSCORE_NOT_ENABLED: |
5353 | 0 | return "COAP_EVENT_OSCORE_NOT_ENABLED"; |
5354 | 0 | case COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD: |
5355 | 0 | return "COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD"; |
5356 | 0 | case COAP_EVENT_OSCORE_NO_SECURITY: |
5357 | 0 | return "COAP_EVENT_OSCORE_NO_SECURITY"; |
5358 | 0 | case COAP_EVENT_OSCORE_INTERNAL_ERROR: |
5359 | 0 | return "COAP_EVENT_OSCORE_INTERNAL_ERROR"; |
5360 | 0 | case COAP_EVENT_OSCORE_DECODE_ERROR: |
5361 | 0 | return "COAP_EVENT_OSCORE_DECODE_ERROR"; |
5362 | 0 | case COAP_EVENT_WS_PACKET_SIZE: |
5363 | 0 | return "COAP_EVENT_WS_PACKET_SIZE"; |
5364 | 0 | case COAP_EVENT_WS_CONNECTED: |
5365 | 0 | return "COAP_EVENT_WS_CONNECTED"; |
5366 | 0 | case COAP_EVENT_WS_CLOSED: |
5367 | 0 | return "COAP_EVENT_WS_CLOSED"; |
5368 | 0 | case COAP_EVENT_KEEPALIVE_FAILURE: |
5369 | 0 | return "COAP_EVENT_KEEPALIVE_FAILURE"; |
5370 | 0 | case COAP_EVENT_RECONNECT_FAILED: |
5371 | 0 | return "COAP_EVENT_RECONNECT_FAILED"; |
5372 | 0 | case COAP_EVENT_RECONNECT_SUCCESS: |
5373 | 0 | return "COAP_EVENT_RECONNECT_SUCCESS"; |
5374 | 0 | case COAP_EVENT_RECONNECT_NO_MORE: |
5375 | 0 | return "COAP_EVENT_RECONNECT_NO_MORE"; |
5376 | 0 | case COAP_EVENT_RECONNECT_STARTED: |
5377 | 0 | return "COAP_EVENT_RECONNECT_STARTED"; |
5378 | 0 | default: |
5379 | 0 | return "???"; |
5380 | 0 | } |
5381 | 0 | } |
5382 | | #endif /* COAP_MAX_LOGGING_LEVEL >= _COAP_LOG_DEBUG */ |
5383 | | |
5384 | | COAP_API int |
5385 | | coap_handle_event(coap_context_t *context, coap_event_t event, |
5386 | 0 | coap_session_t *session) { |
5387 | 0 | int ret; |
5388 | |
|
5389 | 0 | coap_lock_lock(return 0); |
5390 | 0 | ret = coap_handle_event_lkd(context, event, session); |
5391 | 0 | coap_lock_unlock(); |
5392 | 0 | return ret; |
5393 | 0 | } |
5394 | | |
5395 | | int |
5396 | | coap_handle_event_lkd(coap_context_t *context, coap_event_t event, |
5397 | 15 | coap_session_t *session) { |
5398 | 15 | int ret = 0; |
5399 | | |
5400 | 15 | coap_log_debug("***EVENT: %s\n", coap_event_name(event)); |
5401 | | |
5402 | 15 | #if COAP_PROXY_SUPPORT |
5403 | 15 | if (event == COAP_EVENT_SERVER_SESSION_DEL) |
5404 | 0 | coap_proxy_remove_association(session, 0); |
5405 | 15 | #endif /* COAP_PROXY_SUPPORT */ |
5406 | | |
5407 | 15 | if (context->event_cb) { |
5408 | 15 | coap_lock_callback_ret(ret, context->event_cb(session, event)); |
5409 | 15 | #if COAP_CLIENT_SUPPORT |
5410 | 15 | switch (event) { |
5411 | 0 | case COAP_EVENT_DTLS_CLOSED: |
5412 | 0 | case COAP_EVENT_TCP_CLOSED: |
5413 | 0 | case COAP_EVENT_SESSION_CLOSED: |
5414 | 0 | case COAP_EVENT_OSCORE_DECRYPTION_FAILURE: |
5415 | 0 | case COAP_EVENT_OSCORE_NOT_ENABLED: |
5416 | 0 | case COAP_EVENT_OSCORE_NO_PROTECTED_PAYLOAD: |
5417 | 0 | case COAP_EVENT_OSCORE_NO_SECURITY: |
5418 | 0 | case COAP_EVENT_OSCORE_INTERNAL_ERROR: |
5419 | 0 | case COAP_EVENT_OSCORE_DECODE_ERROR: |
5420 | 0 | case COAP_EVENT_WS_PACKET_SIZE: |
5421 | 0 | case COAP_EVENT_WS_CLOSED: |
5422 | 15 | case COAP_EVENT_BAD_PACKET: |
5423 | 15 | case COAP_EVENT_FIRST_PDU_FAIL: |
5424 | 15 | case COAP_EVENT_RECONNECT_NO_MORE: |
5425 | | /* Those that are deemed fatal to end sending a request */ |
5426 | 15 | session->doing_send_recv = 0; |
5427 | 15 | break; |
5428 | 0 | case COAP_EVENT_DTLS_CONNECTED: |
5429 | | /* Session will now be available as well - for call-home */ |
5430 | 0 | if (session->type == COAP_SESSION_TYPE_SERVER && session->proto == COAP_PROTO_DTLS) { |
5431 | 0 | coap_handle_event_lkd(context, COAP_EVENT_SERVER_SESSION_CONNECTED, |
5432 | 0 | session); |
5433 | 0 | } |
5434 | 0 | break; |
5435 | 0 | case COAP_EVENT_DTLS_RENEGOTIATE: |
5436 | 0 | case COAP_EVENT_DTLS_ERROR: |
5437 | 0 | case COAP_EVENT_TCP_CONNECTED: |
5438 | 0 | case COAP_EVENT_TCP_FAILED: |
5439 | 0 | case COAP_EVENT_RECONNECT_FAILED: |
5440 | 0 | break; |
5441 | 0 | case COAP_EVENT_SESSION_CONNECTED: |
5442 | | /* Session will now be available as well - for call-home if not (D)TLS */ |
5443 | 0 | if (session->type == COAP_SESSION_TYPE_SERVER && |
5444 | 0 | (session->proto == COAP_PROTO_TCP || session->proto == COAP_PROTO_TLS)) { |
5445 | 0 | coap_handle_event_lkd(context, COAP_EVENT_SERVER_SESSION_CONNECTED, |
5446 | 0 | session); |
5447 | 0 | } |
5448 | 0 | break; |
5449 | 0 | case COAP_EVENT_SESSION_FAILED: |
5450 | 0 | case COAP_EVENT_PARTIAL_BLOCK: |
5451 | 0 | case COAP_EVENT_XMIT_BLOCK_FAIL: |
5452 | 0 | case COAP_EVENT_BLOCK_ISSUE: |
5453 | 0 | break; |
5454 | 0 | case COAP_EVENT_SERVER_SESSION_NEW: |
5455 | | /* Session will now be available as well - for call-home if not (D)TLS */ |
5456 | 0 | if (session->proto == COAP_PROTO_UDP) { |
5457 | 0 | coap_handle_event_lkd(context, COAP_EVENT_SERVER_SESSION_CONNECTED, |
5458 | 0 | session); |
5459 | 0 | } |
5460 | 0 | break; |
5461 | 0 | case COAP_EVENT_SERVER_SESSION_DEL: |
5462 | 0 | case COAP_EVENT_SERVER_SESSION_CONNECTED: |
5463 | 0 | case COAP_EVENT_MSG_RETRANSMITTED: |
5464 | 0 | case COAP_EVENT_WS_CONNECTED: |
5465 | 0 | case COAP_EVENT_KEEPALIVE_FAILURE: |
5466 | 0 | case COAP_EVENT_RECONNECT_SUCCESS: |
5467 | 0 | case COAP_EVENT_RECONNECT_STARTED: |
5468 | 0 | default: |
5469 | 0 | break; |
5470 | 15 | } |
5471 | 15 | #endif /* COAP_CLIENT_SUPPORT */ |
5472 | 15 | } |
5473 | 15 | return ret; |
5474 | 15 | } |
5475 | | |
5476 | | COAP_API int |
5477 | 0 | coap_can_exit(coap_context_t *context) { |
5478 | 0 | int ret; |
5479 | |
|
5480 | 0 | coap_lock_lock(return 0); |
5481 | 0 | ret = coap_can_exit_lkd(context); |
5482 | 0 | coap_lock_unlock(); |
5483 | 0 | return ret; |
5484 | 0 | } |
5485 | | |
5486 | | int |
5487 | 0 | coap_can_exit_lkd(coap_context_t *context) { |
5488 | 0 | coap_session_t *s, *rtmp; |
5489 | 0 | if (!context) |
5490 | 0 | return 1; |
5491 | 0 | coap_lock_check_locked(); |
5492 | 0 | if (context->sendqueue) |
5493 | 0 | return 0; |
5494 | 0 | #if COAP_SERVER_SUPPORT |
5495 | 0 | coap_endpoint_t *ep; |
5496 | |
|
5497 | 0 | LL_FOREACH(context->endpoint, ep) { |
5498 | 0 | SESSIONS_ITER(ep->sessions, s, rtmp) { |
5499 | 0 | if (s->delayqueue) |
5500 | 0 | return 0; |
5501 | 0 | if (s->lg_xmit) |
5502 | 0 | return 0; |
5503 | 0 | } |
5504 | 0 | } |
5505 | 0 | #endif /* COAP_SERVER_SUPPORT */ |
5506 | 0 | #if COAP_CLIENT_SUPPORT |
5507 | 0 | SESSIONS_ITER(context->sessions, s, rtmp) { |
5508 | 0 | if (s->delayqueue) |
5509 | 0 | return 0; |
5510 | 0 | if (s->lg_xmit) |
5511 | 0 | return 0; |
5512 | 0 | } |
5513 | 0 | #endif /* COAP_CLIENT_SUPPORT */ |
5514 | 0 | return 1; |
5515 | 0 | } |
5516 | | #if COAP_SERVER_SUPPORT |
5517 | | #if COAP_ASYNC_SUPPORT |
5518 | | /* |
5519 | | * Return 1 if there is a future expire time, else 0. |
5520 | | * Update tim_rem with remaining value if return is 1. |
5521 | | */ |
5522 | | int |
5523 | 0 | coap_check_async(coap_context_t *context, coap_tick_t now, coap_tick_t *tim_rem) { |
5524 | 0 | coap_tick_t next_due = COAP_MAX_DELAY_TICKS; |
5525 | 0 | coap_async_t *async, *tmp; |
5526 | 0 | int ret = 0; |
5527 | |
|
5528 | 0 | if (context->async_state_traversing) |
5529 | 0 | return 0; |
5530 | 0 | context->async_state_traversing = 1; |
5531 | 0 | LL_FOREACH_SAFE(context->async_state, async, tmp) { |
5532 | 0 | if (async->delay != 0 && !async->session->is_rate_limiting) { |
5533 | 0 | if (async->delay <= now) { |
5534 | | /* Restore the local address the request was received on */ |
5535 | 0 | coap_address_copy(&async->session->addr_info.local, &async->local_if); |
5536 | | /* Send off the request to the application */ |
5537 | 0 | coap_log_debug("Async PDU presented to app.\n"); |
5538 | 0 | coap_show_pdu(COAP_LOG_DEBUG, async->pdu); |
5539 | 0 | handle_request(context, async->session, async->pdu, NULL); |
5540 | | |
5541 | | /* Remove this async entry as it has now fired */ |
5542 | 0 | coap_free_async_lkd(async->session, async); |
5543 | 0 | } else { |
5544 | 0 | next_due = async->delay - now; |
5545 | 0 | ret = 1; |
5546 | 0 | } |
5547 | 0 | } |
5548 | 0 | } |
5549 | 0 | if (tim_rem) |
5550 | 0 | *tim_rem = next_due; |
5551 | 0 | context->async_state_traversing = 0; |
5552 | 0 | return ret; |
5553 | 0 | } |
5554 | | #endif /* COAP_ASYNC_SUPPORT */ |
5555 | | #endif /* COAP_SERVER_SUPPORT */ |
5556 | | |
5557 | | int coap_started = 0; |
5558 | | uint8_t coap_unique_id[8] = { 0 }; |
5559 | | |
5560 | | #if COAP_THREAD_SAFE |
5561 | | /* |
5562 | | * Global lock for multi-thread support |
5563 | | */ |
5564 | | coap_lock_t global_lock; |
5565 | | /* |
5566 | | * low level protection mutex |
5567 | | */ |
5568 | | coap_mutex_t m_show_pdu; |
5569 | | coap_mutex_t m_log_impl; |
5570 | | coap_mutex_t m_io_threads; |
5571 | | #endif /* COAP_THREAD_SAFE */ |
5572 | | |
5573 | | void |
5574 | 32 | coap_startup(void) { |
5575 | 32 | coap_tick_t now; |
5576 | 32 | #ifndef WITH_CONTIKI |
5577 | 32 | uint64_t us; |
5578 | 32 | #endif /* !WITH_CONTIKI */ |
5579 | | |
5580 | 32 | if (coap_started) |
5581 | 0 | return; |
5582 | 32 | coap_started = 1; |
5583 | | |
5584 | | #if COAP_THREAD_SAFE |
5585 | | coap_lock_init(&global_lock); |
5586 | | coap_mutex_init(&m_show_pdu); |
5587 | | coap_mutex_init(&m_log_impl); |
5588 | | coap_mutex_init(&m_io_threads); |
5589 | | #endif /* COAP_THREAD_SAFE */ |
5590 | | |
5591 | | #if defined(HAVE_WINSOCK2_H) |
5592 | | WORD wVersionRequested = MAKEWORD(2, 2); |
5593 | | WSADATA wsaData; |
5594 | | WSAStartup(wVersionRequested, &wsaData); |
5595 | | #endif |
5596 | 32 | coap_clock_init(); |
5597 | 32 | coap_ticks(&now); |
5598 | 32 | #ifndef WITH_CONTIKI |
5599 | 32 | us = coap_ticks_to_rt_us(now); |
5600 | | /* Be accurate to the nearest (approx) us */ |
5601 | 32 | coap_prng_init_lkd((unsigned int)us); |
5602 | | #else /* WITH_CONTIKI */ |
5603 | | coap_start_io_process(); |
5604 | | #endif /* WITH_CONTIKI */ |
5605 | 32 | coap_memory_init(); |
5606 | 32 | coap_dtls_startup(); |
5607 | | #ifdef WITH_LWIP |
5608 | | coap_io_lwip_init(); |
5609 | | #endif /* WITH_LWIP */ |
5610 | 32 | #if COAP_SERVER_SUPPORT |
5611 | 32 | static coap_str_const_t well_known = { sizeof(".well-known/core")-1, |
5612 | 32 | (const uint8_t *)".well-known/core" |
5613 | 32 | }; |
5614 | 32 | memset(&resource_uri_wellknown, 0, sizeof(resource_uri_wellknown)); |
5615 | 32 | resource_uri_wellknown.ref = 1; |
5616 | 32 | resource_uri_wellknown.handler[COAP_REQUEST_GET-1] = hnd_get_wellknown_lkd; |
5617 | 32 | resource_uri_wellknown.flags = COAP_RESOURCE_FLAGS_HAS_MCAST_SUPPORT; |
5618 | 32 | resource_uri_wellknown.uri_path = &well_known; |
5619 | 32 | #endif /* COAP_SERVER_SUPPORT */ |
5620 | 32 | send_recv_terminate = 0; |
5621 | 32 | coap_prng_lkd(&coap_unique_id, sizeof(coap_unique_id)); |
5622 | 32 | } |
5623 | | |
5624 | | void |
5625 | 32 | coap_cleanup(void) { |
5626 | 32 | if (!coap_started) |
5627 | 0 | return; |
5628 | 32 | coap_started = 0; |
5629 | | #if defined(HAVE_WINSOCK2_H) |
5630 | | WSACleanup(); |
5631 | | #elif defined(WITH_CONTIKI) |
5632 | | coap_stop_io_process(); |
5633 | | #endif |
5634 | | #ifdef WITH_LWIP |
5635 | | coap_io_lwip_cleanup(); |
5636 | | #endif /* WITH_LWIP */ |
5637 | 32 | coap_dtls_shutdown(); |
5638 | | |
5639 | 32 | coap_delete_upa_chain(coap_upa_client_fallback_chain); |
5640 | 32 | coap_upa_client_fallback_chain = NULL; |
5641 | 32 | coap_delete_upa_chain(coap_upa_server_mapping_chain); |
5642 | 32 | coap_upa_server_mapping_chain = NULL; |
5643 | | #if COAP_THREAD_SAFE |
5644 | | coap_mutex_destroy(&m_show_pdu); |
5645 | | coap_mutex_destroy(&m_log_impl); |
5646 | | coap_mutex_destroy(&m_io_threads); |
5647 | | #endif /* COAP_THREAD_SAFE */ |
5648 | | |
5649 | 32 | coap_debug_reset(); |
5650 | 32 | } |
5651 | | |
5652 | | void |
5653 | | coap_register_response_handler(coap_context_t *context, |
5654 | 0 | coap_response_handler_t handler) { |
5655 | 0 | #if COAP_CLIENT_SUPPORT |
5656 | 0 | context->response_cb = handler; |
5657 | | #else /* ! COAP_CLIENT_SUPPORT */ |
5658 | | (void)context; |
5659 | | (void)handler; |
5660 | | #endif /* ! COAP_CLIENT_SUPPORT */ |
5661 | 0 | } |
5662 | | |
5663 | | void |
5664 | | coap_register_proxy_response_handler(coap_context_t *context, |
5665 | 0 | coap_proxy_response_handler_t handler) { |
5666 | 0 | #if COAP_PROXY_SUPPORT |
5667 | 0 | context->proxy_response_cb = handler; |
5668 | | #else /* ! COAP_PROXY_SUPPORT */ |
5669 | | (void)context; |
5670 | | (void)handler; |
5671 | | #endif /* ! COAP_PROXY_SUPPORT */ |
5672 | 0 | } |
5673 | | |
5674 | | void |
5675 | | coap_register_nack_handler(coap_context_t *context, |
5676 | 0 | coap_nack_handler_t handler) { |
5677 | 0 | context->nack_cb = handler; |
5678 | 0 | } |
5679 | | |
5680 | | void |
5681 | | coap_register_ping_handler(coap_context_t *context, |
5682 | 0 | coap_ping_handler_t handler) { |
5683 | 0 | context->ping_cb = handler; |
5684 | 0 | } |
5685 | | |
5686 | | void |
5687 | | coap_register_pong_handler(coap_context_t *context, |
5688 | 0 | coap_pong_handler_t handler) { |
5689 | 0 | context->pong_cb = handler; |
5690 | 0 | } |
5691 | | |
5692 | | void |
5693 | | coap_register_dynamic_resource_handler(coap_context_t *context, |
5694 | | coap_resource_dynamic_create_t dyn_create_handler, |
5695 | 0 | uint32_t dynamic_max) { |
5696 | 0 | context->dyn_create_handler = dyn_create_handler; |
5697 | 0 | context->dynamic_max = dynamic_max; |
5698 | 0 | return; |
5699 | 0 | } |
5700 | | |
5701 | | COAP_API void |
5702 | 0 | coap_register_option(coap_context_t *ctx, coap_option_num_t type) { |
5703 | 0 | coap_lock_lock(return); |
5704 | 0 | coap_register_option_lkd(ctx, type); |
5705 | 0 | coap_lock_unlock(); |
5706 | 0 | } |
5707 | | |
5708 | | void |
5709 | 0 | coap_register_option_lkd(coap_context_t *ctx, coap_option_num_t type) { |
5710 | 0 | coap_option_filter_set(&ctx->known_options, type); |
5711 | 0 | } |
5712 | | |
5713 | | #if ! defined WITH_CONTIKI && ! defined WITH_LWIP && ! defined RIOT_VERSION && !defined(__ZEPHYR__) |
5714 | | #if COAP_SERVER_SUPPORT |
5715 | | COAP_API int |
5716 | | coap_join_mcast_group_intf(coap_context_t *ctx, const char *group_name, |
5717 | 0 | const char *ifname) { |
5718 | 0 | int ret; |
5719 | |
|
5720 | 0 | coap_lock_lock(return -1); |
5721 | 0 | ret = coap_join_mcast_group_intf_lkd(ctx, NULL, group_name, ifname); |
5722 | 0 | coap_lock_unlock(); |
5723 | 0 | return ret; |
5724 | 0 | } |
5725 | | |
5726 | | int |
5727 | | coap_join_mcast_group_intf_lkd(coap_context_t *ctx, |
5728 | | coap_endpoint_t *single_endpoint, |
5729 | | const char *group_name, |
5730 | 0 | const char *ifname) { |
5731 | 0 | #if COAP_IPV4_SUPPORT |
5732 | 0 | struct ip_mreq mreq4; |
5733 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
5734 | 0 | #if COAP_IPV6_SUPPORT |
5735 | 0 | struct ipv6_mreq mreq6; |
5736 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
5737 | 0 | struct addrinfo *resmulti = NULL, hints, *ainfo; |
5738 | 0 | int result = -1; |
5739 | 0 | coap_endpoint_t *endpoint; |
5740 | | #if !defined(ESPIDF_VERSION) && COAP_IPV6_SUPPORT && !defined(HAVE_IF_NAMETOINDEX) && !defined(__QNXNTO__) |
5741 | | coap_endpoint_t *lookup_endpoint; |
5742 | | #endif /* !ESPIDF_VERSION && COAP_IPV6_SUPPORT && !HAVE_IF_NAMETOINDEX && !__QNXNTO__ */ |
5743 | 0 | int mgroup_setup = 0; |
5744 | |
|
5745 | 0 | if (single_endpoint) { |
5746 | 0 | if (single_endpoint->proto != COAP_PROTO_UDP) |
5747 | 0 | return -1; |
5748 | | #if !defined(ESPIDF_VERSION) && COAP_IPV6_SUPPORT && !defined(HAVE_IF_NAMETOINDEX) && !defined(__QNXNTO__) |
5749 | | lookup_endpoint = single_endpoint; |
5750 | | #endif /* !ESPIDF_VERSION && COAP_IPV6_SUPPORT && !HAVE_IF_NAMETOINDEX && !__QNXNTO__ */ |
5751 | 0 | } else { |
5752 | | /* Need to have at least one endpoint! */ |
5753 | 0 | assert(ctx->endpoint); |
5754 | 0 | if (!ctx->endpoint) |
5755 | 0 | return -1; |
5756 | | #if !defined(ESPIDF_VERSION) && COAP_IPV6_SUPPORT && !defined(HAVE_IF_NAMETOINDEX) && !defined(__QNXNTO__) |
5757 | | lookup_endpoint = ctx->endpoint; |
5758 | | #endif /* !ESPIDF_VERSION && COAP_IPV6_SUPPORT && !HAVE_IF_NAMETOINDEX && !__QNXNTO__ */ |
5759 | 0 | } |
5760 | | |
5761 | | /* Default is let the kernel choose */ |
5762 | 0 | #if COAP_IPV6_SUPPORT |
5763 | 0 | mreq6.ipv6mr_interface = 0; |
5764 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
5765 | 0 | #if COAP_IPV4_SUPPORT |
5766 | 0 | mreq4.imr_interface.s_addr = INADDR_ANY; |
5767 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
5768 | |
|
5769 | 0 | memset(&hints, 0, sizeof(hints)); |
5770 | 0 | hints.ai_socktype = SOCK_DGRAM; |
5771 | | |
5772 | | /* resolve the multicast group address */ |
5773 | 0 | result = getaddrinfo(group_name, NULL, &hints, &resmulti); |
5774 | |
|
5775 | 0 | if (result != 0) { |
5776 | 0 | coap_log_err("coap_join_mcast_group_intf: %s: " |
5777 | 0 | "Cannot resolve multicast address: %s\n", |
5778 | 0 | group_name, gai_strerror(result)); |
5779 | 0 | goto finish; |
5780 | 0 | } |
5781 | | |
5782 | | /* Need to do a windows equivalent at some point */ |
5783 | 0 | #ifndef _WIN32 |
5784 | 0 | if (ifname) { |
5785 | | /* interface specified - check if we have correct IPv4/IPv6 information */ |
5786 | 0 | int done_ip4 = 0; |
5787 | 0 | int done_ip6 = 0; |
5788 | | #if defined(ESPIDF_VERSION) |
5789 | | struct netif *netif; |
5790 | | #else /* !ESPIDF_VERSION */ |
5791 | 0 | #if COAP_IPV4_SUPPORT |
5792 | 0 | int ip4fd; |
5793 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
5794 | 0 | struct ifreq ifr; |
5795 | 0 | #endif /* !ESPIDF_VERSION */ |
5796 | | |
5797 | | /* See which mcast address family types are being asked for */ |
5798 | 0 | for (ainfo = resmulti; ainfo != NULL && !(done_ip4 == 1 && done_ip6 == 1); |
5799 | 0 | ainfo = ainfo->ai_next) { |
5800 | 0 | switch (ainfo->ai_family) { |
5801 | 0 | #if COAP_IPV6_SUPPORT |
5802 | 0 | case AF_INET6: |
5803 | 0 | if (done_ip6) |
5804 | 0 | break; |
5805 | 0 | done_ip6 = 1; |
5806 | | #if defined(ESPIDF_VERSION) |
5807 | | netif = netif_find(ifname); |
5808 | | if (netif) |
5809 | | mreq6.ipv6mr_interface = netif_get_index(netif); |
5810 | | else |
5811 | | coap_log_err("coap_join_mcast_group_intf: %s: " |
5812 | | "Cannot get IPv4 address: %s\n", |
5813 | | ifname, coap_socket_strerror()); |
5814 | | #else /* !ESPIDF_VERSION */ |
5815 | 0 | memset(&ifr, 0, sizeof(ifr)); |
5816 | 0 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); |
5817 | 0 | ifr.ifr_name[IFNAMSIZ - 1] = '\000'; |
5818 | |
|
5819 | 0 | #ifdef HAVE_IF_NAMETOINDEX |
5820 | 0 | mreq6.ipv6mr_interface = if_nametoindex(ifr.ifr_name); |
5821 | 0 | if (mreq6.ipv6mr_interface == 0) { |
5822 | 0 | coap_log_warn("coap_join_mcast_group_intf: " |
5823 | 0 | "cannot get interface index for '%s'\n", |
5824 | 0 | ifname); |
5825 | 0 | } |
5826 | | #elif defined(__QNXNTO__) |
5827 | | #else /* !HAVE_IF_NAMETOINDEX */ |
5828 | | result = ioctl(lookup_endpoint->sock.fd, SIOCGIFINDEX, &ifr); |
5829 | | if (result != 0) { |
5830 | | coap_log_warn("coap_join_mcast_group_intf: " |
5831 | | "cannot get interface index for '%s': %s\n", |
5832 | | ifname, coap_socket_strerror()); |
5833 | | } else { |
5834 | | /* Capture the IPv6 if_index for later */ |
5835 | | mreq6.ipv6mr_interface = ifr.ifr_ifindex; |
5836 | | } |
5837 | | #endif /* !HAVE_IF_NAMETOINDEX */ |
5838 | 0 | #endif /* !ESPIDF_VERSION */ |
5839 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
5840 | 0 | break; |
5841 | 0 | #if COAP_IPV4_SUPPORT |
5842 | 0 | case AF_INET: |
5843 | 0 | if (done_ip4) |
5844 | 0 | break; |
5845 | 0 | done_ip4 = 1; |
5846 | | #if defined(ESPIDF_VERSION) |
5847 | | netif = netif_find(ifname); |
5848 | | if (netif) |
5849 | | mreq4.imr_interface.s_addr = netif_ip4_addr(netif)->addr; |
5850 | | else |
5851 | | coap_log_err("coap_join_mcast_group_intf: %s: " |
5852 | | "Cannot get IPv4 address: %s\n", |
5853 | | ifname, coap_socket_strerror()); |
5854 | | #else /* !ESPIDF_VERSION */ |
5855 | | /* |
5856 | | * Need an AF_INET socket to do this unfortunately to stop |
5857 | | * "Invalid argument" error if AF_INET6 socket is used for SIOCGIFADDR |
5858 | | */ |
5859 | 0 | ip4fd = socket(AF_INET, SOCK_DGRAM, 0); |
5860 | 0 | if (ip4fd == -1) { |
5861 | 0 | coap_log_err("coap_join_mcast_group_intf: %s: socket: %s\n", |
5862 | 0 | ifname, coap_socket_strerror()); |
5863 | 0 | continue; |
5864 | 0 | } |
5865 | 0 | memset(&ifr, 0, sizeof(ifr)); |
5866 | 0 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); |
5867 | 0 | ifr.ifr_name[IFNAMSIZ - 1] = '\000'; |
5868 | 0 | result = ioctl(ip4fd, SIOCGIFADDR, &ifr); |
5869 | 0 | if (result != 0) { |
5870 | 0 | coap_log_err("coap_join_mcast_group_intf: %s: " |
5871 | 0 | "Cannot get IPv4 address: %s\n", |
5872 | 0 | ifname, coap_socket_strerror()); |
5873 | 0 | } else { |
5874 | | /* Capture the IPv4 address for later */ |
5875 | 0 | mreq4.imr_interface = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr; |
5876 | 0 | } |
5877 | 0 | close(ip4fd); |
5878 | 0 | #endif /* !ESPIDF_VERSION */ |
5879 | 0 | break; |
5880 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
5881 | 0 | default: |
5882 | 0 | break; |
5883 | 0 | } |
5884 | 0 | } |
5885 | 0 | } |
5886 | | #else /* _WIN32 */ |
5887 | | /* |
5888 | | * On Windows this function ignores the ifname variable so we unset this |
5889 | | * variable on this platform in any case in order to enable the interface |
5890 | | * selection from the bind address below. |
5891 | | */ |
5892 | | ifname = 0; |
5893 | | #endif /* _WIN32 */ |
5894 | | |
5895 | | /* Add in mcast address(es) to appropriate interface */ |
5896 | 0 | for (ainfo = resmulti; ainfo != NULL; ainfo = ainfo->ai_next) { |
5897 | 0 | for (endpoint = single_endpoint ? single_endpoint : ctx->endpoint; |
5898 | 0 | endpoint != NULL; |
5899 | 0 | endpoint = single_endpoint ? NULL : endpoint->next) { |
5900 | | /* Only UDP currently supported */ |
5901 | 0 | if (endpoint->proto == COAP_PROTO_UDP) { |
5902 | 0 | coap_address_t gaddr; |
5903 | |
|
5904 | 0 | coap_address_init(&gaddr); |
5905 | 0 | #if COAP_IPV6_SUPPORT |
5906 | 0 | if (ainfo->ai_family == AF_INET6) { |
5907 | 0 | if (!ifname) { |
5908 | 0 | if (endpoint->bind_addr.addr.sa.sa_family == AF_INET6) { |
5909 | | /* |
5910 | | * Do it on the ifindex that the server is listening on |
5911 | | * (sin6_scope_id could still be 0) |
5912 | | */ |
5913 | 0 | mreq6.ipv6mr_interface = |
5914 | 0 | endpoint->bind_addr.addr.sin6.sin6_scope_id; |
5915 | 0 | } else { |
5916 | 0 | mreq6.ipv6mr_interface = 0; |
5917 | 0 | } |
5918 | 0 | } |
5919 | 0 | gaddr.addr.sin6.sin6_family = AF_INET6; |
5920 | 0 | gaddr.addr.sin6.sin6_port = endpoint->bind_addr.addr.sin6.sin6_port; |
5921 | 0 | gaddr.addr.sin6.sin6_addr = mreq6.ipv6mr_multiaddr = |
5922 | 0 | ((struct sockaddr_in6 *)ainfo->ai_addr)->sin6_addr; |
5923 | 0 | result = setsockopt(endpoint->sock.fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, |
5924 | 0 | (char *)&mreq6, sizeof(mreq6)); |
5925 | 0 | } |
5926 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
5927 | 0 | #if COAP_IPV4_SUPPORT && COAP_IPV6_SUPPORT |
5928 | 0 | else |
5929 | 0 | #endif /* COAP_IPV4_SUPPORT && COAP_IPV6_SUPPORT */ |
5930 | 0 | #if COAP_IPV4_SUPPORT |
5931 | 0 | if (ainfo->ai_family == AF_INET) { |
5932 | 0 | if (!ifname) { |
5933 | 0 | if (endpoint->bind_addr.addr.sa.sa_family == AF_INET) { |
5934 | | /* |
5935 | | * Do it on the interface that the server is listening on |
5936 | | * (sin_addr could still be INADDR_ANY) |
5937 | | */ |
5938 | 0 | mreq4.imr_interface = endpoint->bind_addr.addr.sin.sin_addr; |
5939 | 0 | } else { |
5940 | 0 | mreq4.imr_interface.s_addr = INADDR_ANY; |
5941 | 0 | } |
5942 | 0 | } |
5943 | 0 | gaddr.addr.sin.sin_family = AF_INET; |
5944 | 0 | gaddr.addr.sin.sin_port = endpoint->bind_addr.addr.sin.sin_port; |
5945 | 0 | gaddr.addr.sin.sin_addr.s_addr = mreq4.imr_multiaddr.s_addr = |
5946 | 0 | ((struct sockaddr_in *)ainfo->ai_addr)->sin_addr.s_addr; |
5947 | 0 | result = setsockopt(endpoint->sock.fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, |
5948 | 0 | (char *)&mreq4, sizeof(mreq4)); |
5949 | 0 | } |
5950 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
5951 | 0 | else { |
5952 | 0 | continue; |
5953 | 0 | } |
5954 | | |
5955 | 0 | if (result == COAP_SOCKET_ERROR) { |
5956 | 0 | coap_log_err("coap_join_mcast_group_intf: %s: setsockopt: %s\n", |
5957 | 0 | group_name, coap_socket_strerror()); |
5958 | 0 | } else { |
5959 | 0 | char addr_str[INET6_ADDRSTRLEN + 8 + 1]; |
5960 | |
|
5961 | 0 | addr_str[sizeof(addr_str)-1] = '\000'; |
5962 | 0 | if (coap_print_addr(&gaddr, (uint8_t *)addr_str, |
5963 | 0 | sizeof(addr_str) - 1)) { |
5964 | 0 | if (ifname) |
5965 | 0 | coap_log_debug("added mcast group %s i/f %s\n", addr_str, |
5966 | 0 | ifname); |
5967 | 0 | else |
5968 | 0 | coap_log_debug("added mcast group %s\n", addr_str); |
5969 | 0 | } |
5970 | 0 | mgroup_setup = 1; |
5971 | 0 | } |
5972 | 0 | } |
5973 | 0 | } |
5974 | 0 | } |
5975 | 0 | if (!mgroup_setup) { |
5976 | 0 | result = -1; |
5977 | 0 | } |
5978 | |
|
5979 | 0 | finish: |
5980 | 0 | freeaddrinfo(resmulti); |
5981 | |
|
5982 | 0 | return result; |
5983 | 0 | } |
5984 | | |
5985 | | COAP_API int |
5986 | | coap_endpoint_join_mcast_group_intf(coap_endpoint_t *endpoint, |
5987 | | const char *group_name, |
5988 | 0 | const char *ifname) { |
5989 | 0 | int ret; |
5990 | |
|
5991 | 0 | if (!endpoint || !endpoint->context) |
5992 | 0 | return -1; |
5993 | | |
5994 | 0 | coap_lock_lock(return -1); |
5995 | 0 | ret = coap_join_mcast_group_intf_lkd(endpoint->context, endpoint, group_name, ifname); |
5996 | 0 | coap_lock_unlock(); |
5997 | 0 | return ret; |
5998 | 0 | } |
5999 | | |
6000 | | void |
6001 | 0 | coap_mcast_per_resource(coap_context_t *context) { |
6002 | 0 | context->mcast_per_resource = 1; |
6003 | 0 | } |
6004 | | |
6005 | | #endif /* ! COAP_SERVER_SUPPORT */ |
6006 | | |
6007 | | #if COAP_CLIENT_SUPPORT |
6008 | | int |
6009 | 0 | coap_mcast_set_hops(coap_session_t *session, size_t hops) { |
6010 | 0 | if (session && coap_is_mcast(&session->addr_info.remote)) { |
6011 | 0 | switch (session->addr_info.remote.addr.sa.sa_family) { |
6012 | 0 | #if COAP_IPV4_SUPPORT |
6013 | 0 | case AF_INET: |
6014 | 0 | if (setsockopt(session->sock.fd, IPPROTO_IP, IP_MULTICAST_TTL, |
6015 | 0 | (const char *)&hops, sizeof(hops)) < 0) { |
6016 | 0 | coap_log_info("coap_mcast_set_hops: %" PRIuS ": setsockopt: %s\n", |
6017 | 0 | hops, coap_socket_strerror()); |
6018 | 0 | return 0; |
6019 | 0 | } |
6020 | 0 | return 1; |
6021 | 0 | #endif /* COAP_IPV4_SUPPORT */ |
6022 | 0 | #if COAP_IPV6_SUPPORT |
6023 | 0 | case AF_INET6: |
6024 | 0 | if (setsockopt(session->sock.fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, |
6025 | 0 | (const char *)&hops, sizeof(hops)) < 0) { |
6026 | 0 | coap_log_info("coap_mcast_set_hops: %" PRIuS ": setsockopt: %s\n", |
6027 | 0 | hops, coap_socket_strerror()); |
6028 | 0 | return 0; |
6029 | 0 | } |
6030 | 0 | return 1; |
6031 | 0 | #endif /* COAP_IPV6_SUPPORT */ |
6032 | 0 | default: |
6033 | 0 | break; |
6034 | 0 | } |
6035 | 0 | } |
6036 | 0 | return 0; |
6037 | 0 | } |
6038 | | #endif /* COAP_CLIENT_SUPPORT */ |
6039 | | |
6040 | | #else /* defined WITH_CONTIKI || defined WITH_LWIP || defined RIOT_VERSION || defined(__ZEPHYR__) */ |
6041 | | COAP_API int |
6042 | | coap_join_mcast_group_intf(coap_context_t *ctx COAP_UNUSED, |
6043 | | const char *group_name COAP_UNUSED, |
6044 | | const char *ifname COAP_UNUSED) { |
6045 | | return -1; |
6046 | | } |
6047 | | |
6048 | | COAP_API int |
6049 | | coap_endpoint_join_mcast_group_intf(coap_endpoint_t *endpoint COAP_UNUSED, |
6050 | | const char *group_name COAP_UNUSED, |
6051 | | const char *ifname COAP_UNUSED) { |
6052 | | return -1; |
6053 | | } |
6054 | | |
6055 | | int |
6056 | | coap_mcast_set_hops(coap_session_t *session COAP_UNUSED, |
6057 | | size_t hops COAP_UNUSED) { |
6058 | | return 0; |
6059 | | } |
6060 | | |
6061 | | void |
6062 | | coap_mcast_per_resource(coap_context_t *context COAP_UNUSED) { |
6063 | | } |
6064 | | #endif /* defined WITH_CONTIKI || defined WITH_LWIP || defined RIOT_VERSION || defined(__ZEPHYR__) */ |