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