1 /*
2 * Copyright (c) 2023 Basalte bv
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <string.h>
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(net_coap, CONFIG_COAP_LOG_LEVEL);
10
11 #include <zephyr/net/socket.h>
12
13 #include <zephyr/init.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/net/coap.h>
16 #include <zephyr/net/coap_link_format.h>
17 #include <zephyr/net/coap_mgmt.h>
18 #include <zephyr/net/coap_service.h>
19 #include <zephyr/sys/fdtable.h>
20 #include <zephyr/zvfs/eventfd.h>
21
22 #if defined(CONFIG_NET_TC_THREAD_COOPERATIVE)
23 /* Lowest priority cooperative thread */
24 #define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1)
25 #else
26 #define THREAD_PRIORITY K_PRIO_PREEMPT(CONFIG_NUM_PREEMPT_PRIORITIES - 1)
27 #endif
28
29 #define ADDRLEN(sock) \
30 (((struct sockaddr *)sock)->sa_family == AF_INET ? \
31 sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6))
32
33 /* Shortened defines */
34 #define MAX_OPTIONS CONFIG_COAP_SERVER_MESSAGE_OPTIONS
35 #define MAX_PENDINGS CONFIG_COAP_SERVICE_PENDING_MESSAGES
36 #define MAX_OBSERVERS CONFIG_COAP_SERVICE_OBSERVERS
37 #define MAX_POLL_FD CONFIG_ZVFS_POLL_MAX
38
39 BUILD_ASSERT(CONFIG_ZVFS_POLL_MAX > 0, "CONFIG_ZVFS_POLL_MAX can't be 0");
40
41 static K_MUTEX_DEFINE(lock);
42 static int control_sock;
43
44 #if defined(CONFIG_COAP_SERVER_PENDING_ALLOCATOR_STATIC)
45 K_MEM_SLAB_DEFINE_STATIC(pending_data, CONFIG_COAP_SERVER_MESSAGE_SIZE,
46 CONFIG_COAP_SERVER_PENDING_ALLOCATOR_STATIC_BLOCKS, 4);
47 #endif
48
coap_server_alloc(size_t len)49 static inline void *coap_server_alloc(size_t len)
50 {
51 #if defined(CONFIG_COAP_SERVER_PENDING_ALLOCATOR_STATIC)
52 void *ptr;
53 int ret;
54
55 if (len > CONFIG_COAP_SERVER_MESSAGE_SIZE) {
56 return NULL;
57 }
58
59 ret = k_mem_slab_alloc(&pending_data, &ptr, K_NO_WAIT);
60 if (ret < 0) {
61 return NULL;
62 }
63
64 return ptr;
65 #elif defined(CONFIG_COAP_SERVER_PENDING_ALLOCATOR_SYSTEM_HEAP)
66 return k_malloc(len);
67 #else
68 ARG_UNUSED(len);
69
70 return NULL;
71 #endif
72 }
73
coap_server_free(void * ptr)74 static inline void coap_server_free(void *ptr)
75 {
76 #if defined(CONFIG_COAP_SERVER_PENDING_ALLOCATOR_STATIC)
77 k_mem_slab_free(&pending_data, ptr);
78 #elif defined(CONFIG_COAP_SERVER_PENDING_ALLOCATOR_SYSTEM_HEAP)
79 k_free(ptr);
80 #else
81 ARG_UNUSED(ptr);
82 #endif
83 }
84
coap_service_remove_observer(const struct coap_service * service,struct coap_resource * resource,const struct sockaddr * addr,const uint8_t * token,uint8_t tkl)85 static int coap_service_remove_observer(const struct coap_service *service,
86 struct coap_resource *resource,
87 const struct sockaddr *addr,
88 const uint8_t *token, uint8_t tkl)
89 {
90 struct coap_observer *obs;
91
92 if (tkl > 0 && addr != NULL) {
93 /* Prefer addr+token to find the observer */
94 obs = coap_find_observer(service->data->observers, MAX_OBSERVERS, addr, token, tkl);
95 } else if (tkl > 0) {
96 /* Then try to find the observer by token */
97 obs = coap_find_observer_by_token(service->data->observers, MAX_OBSERVERS, token,
98 tkl);
99 } else if (addr != NULL) {
100 obs = coap_find_observer_by_addr(service->data->observers, MAX_OBSERVERS, addr);
101 } else {
102 /* Either a token or an address is required */
103 return -EINVAL;
104 }
105
106 if (obs == NULL) {
107 return 0;
108 }
109
110 if (resource == NULL) {
111 COAP_SERVICE_FOREACH_RESOURCE(service, it) {
112 if (coap_remove_observer(it, obs)) {
113 memset(obs, 0, sizeof(*obs));
114 return 1;
115 }
116 }
117 } else if (coap_remove_observer(resource, obs)) {
118 memset(obs, 0, sizeof(*obs));
119 return 1;
120 }
121
122 return 0;
123 }
124
coap_server_process(int sock_fd)125 static int coap_server_process(int sock_fd)
126 {
127 static uint8_t buf[CONFIG_COAP_SERVER_MESSAGE_SIZE];
128
129 struct sockaddr client_addr;
130 socklen_t client_addr_len = sizeof(client_addr);
131 struct coap_service *service = NULL;
132 struct coap_packet request;
133 struct coap_pending *pending;
134 struct coap_option options[MAX_OPTIONS] = { 0 };
135 uint8_t opt_num = MAX_OPTIONS;
136 uint8_t type;
137 ssize_t received;
138 int ret;
139 int flags = ZSOCK_MSG_DONTWAIT;
140
141 if (IS_ENABLED(CONFIG_COAP_SERVER_TRUNCATE_MSGS)) {
142 flags |= ZSOCK_MSG_TRUNC;
143 }
144
145 received = zsock_recvfrom(sock_fd, buf, sizeof(buf), flags, &client_addr, &client_addr_len);
146
147 if (received < 0) {
148 if (errno == EWOULDBLOCK) {
149 return 0;
150 }
151
152 LOG_ERR("Failed to process client request (%d)", -errno);
153 return -errno;
154 }
155
156 ret = coap_packet_parse(&request, buf, MIN(received, sizeof(buf)), options, opt_num);
157 if (ret < 0) {
158 LOG_ERR("Failed To parse coap message (%d)", ret);
159 return ret;
160 }
161
162 (void)k_mutex_lock(&lock, K_FOREVER);
163 /* Find the active service */
164 COAP_SERVICE_FOREACH(svc) {
165 if (svc->data->sock_fd == sock_fd) {
166 service = svc;
167 break;
168 }
169 }
170 if (service == NULL) {
171 ret = -ENOENT;
172 goto unlock;
173 }
174
175 type = coap_header_get_type(&request);
176
177 if (received > sizeof(buf)) {
178 /* The message was truncated and can't be processed further */
179 struct coap_packet response;
180 uint8_t token[COAP_TOKEN_MAX_LEN];
181 uint8_t tkl = coap_header_get_token(&request, token);
182 uint16_t id = coap_header_get_id(&request);
183
184 if (type == COAP_TYPE_CON) {
185 type = COAP_TYPE_ACK;
186 } else {
187 type = COAP_TYPE_NON_CON;
188 }
189
190 ret = coap_packet_init(&response, buf, sizeof(buf), COAP_VERSION_1, type, tkl,
191 token, COAP_RESPONSE_CODE_REQUEST_TOO_LARGE, id);
192 if (ret < 0) {
193 LOG_ERR("Failed to init response (%d)", ret);
194 goto unlock;
195 }
196
197 ret = coap_append_option_int(&response, COAP_OPTION_SIZE1,
198 CONFIG_COAP_SERVER_MESSAGE_SIZE);
199 if (ret < 0) {
200 LOG_ERR("Failed to add SIZE1 option (%d)", ret);
201 goto unlock;
202 }
203
204 ret = coap_service_send(service, &response, &client_addr, client_addr_len, NULL);
205 if (ret < 0) {
206 LOG_ERR("Failed to reply \"Request Entity Too Large\" (%d)", ret);
207 goto unlock;
208 }
209
210 goto unlock;
211 }
212
213 pending = coap_pending_received(&request, service->data->pending, MAX_PENDINGS);
214 if (pending) {
215 uint8_t token[COAP_TOKEN_MAX_LEN];
216 uint8_t tkl;
217
218 switch (type) {
219 case COAP_TYPE_RESET:
220 tkl = coap_header_get_token(&request, token);
221 coap_service_remove_observer(service, NULL, &client_addr, token, tkl);
222 __fallthrough;
223 case COAP_TYPE_ACK:
224 coap_server_free(pending->data);
225 coap_pending_clear(pending);
226 break;
227 default:
228 LOG_WRN("Unexpected pending type %d", type);
229 ret = -EINVAL;
230 goto unlock;
231 }
232
233 goto unlock;
234 } else if (type == COAP_TYPE_ACK || type == COAP_TYPE_RESET) {
235 LOG_WRN("Unexpected type %d without pending packet", type);
236 ret = -EINVAL;
237 goto unlock;
238 }
239
240 if (IS_ENABLED(CONFIG_COAP_SERVER_WELL_KNOWN_CORE) &&
241 coap_header_get_code(&request) == COAP_METHOD_GET &&
242 coap_uri_path_match(COAP_WELL_KNOWN_CORE_PATH, options, opt_num)) {
243 uint8_t well_known_buf[CONFIG_COAP_SERVER_MESSAGE_SIZE];
244 struct coap_packet response;
245
246 ret = coap_well_known_core_get_len(service->res_begin,
247 COAP_SERVICE_RESOURCE_COUNT(service),
248 &request, &response,
249 well_known_buf, sizeof(well_known_buf));
250 if (ret < 0) {
251 LOG_ERR("Failed to build well known core for %s (%d)", service->name, ret);
252 goto unlock;
253 }
254
255 ret = coap_service_send(service, &response, &client_addr, client_addr_len, NULL);
256 } else {
257 ret = coap_handle_request_len(&request, service->res_begin,
258 COAP_SERVICE_RESOURCE_COUNT(service),
259 options, opt_num, &client_addr, client_addr_len);
260
261 /* Translate errors to response codes */
262 switch (ret) {
263 case -ENOENT:
264 ret = COAP_RESPONSE_CODE_NOT_FOUND;
265 break;
266 case -ENOTSUP:
267 ret = COAP_RESPONSE_CODE_BAD_REQUEST;
268 break;
269 case -EPERM:
270 ret = COAP_RESPONSE_CODE_NOT_ALLOWED;
271 break;
272 }
273
274 /* Shortcut for replying a code without a body */
275 if (ret > 0 && type == COAP_TYPE_CON) {
276 /* Minimal sized ack buffer */
277 uint8_t ack_buf[COAP_TOKEN_MAX_LEN + 4U];
278 struct coap_packet ack;
279
280 ret = coap_ack_init(&ack, &request, ack_buf, sizeof(ack_buf), (uint8_t)ret);
281 if (ret < 0) {
282 LOG_ERR("Failed to init ACK (%d)", ret);
283 goto unlock;
284 }
285
286 ret = coap_service_send(service, &ack, &client_addr, client_addr_len, NULL);
287 }
288 }
289
290 unlock:
291 (void)k_mutex_unlock(&lock);
292
293 return ret;
294 }
295
coap_server_retransmit(void)296 static void coap_server_retransmit(void)
297 {
298 struct coap_pending *pending;
299 int64_t remaining;
300 int64_t now = k_uptime_get();
301 int ret;
302
303 (void)k_mutex_lock(&lock, K_FOREVER);
304
305 COAP_SERVICE_FOREACH(service) {
306 if (service->data->sock_fd < 0) {
307 continue;
308 }
309
310 pending = coap_pending_next_to_expire(service->data->pending, MAX_PENDINGS);
311 if (pending == NULL) {
312 /* No work to be done */
313 continue;
314 }
315
316 /* Check if the pending request has expired */
317 remaining = pending->t0 + pending->timeout - now;
318 if (remaining > 0) {
319 continue;
320 }
321
322 if (coap_pending_cycle(pending)) {
323 ret = zsock_sendto(service->data->sock_fd, pending->data, pending->len, 0,
324 &pending->addr, ADDRLEN(&pending->addr));
325 if (ret < 0) {
326 LOG_ERR("Failed to send pending retransmission for %s (%d)",
327 service->name, ret);
328 }
329 __ASSERT_NO_MSG(ret == pending->len);
330 } else {
331 LOG_WRN("Packet retransmission failed for %s", service->name);
332
333 coap_service_remove_observer(service, NULL, &pending->addr, NULL, 0U);
334 coap_server_free(pending->data);
335 coap_pending_clear(pending);
336 }
337 }
338
339 (void)k_mutex_unlock(&lock);
340 }
341
coap_server_poll_timeout(void)342 static int coap_server_poll_timeout(void)
343 {
344 struct coap_pending *pending;
345 int64_t result = INT64_MAX;
346 int64_t remaining;
347 int64_t now = k_uptime_get();
348
349 COAP_SERVICE_FOREACH(svc) {
350 if (svc->data->sock_fd < -1) {
351 continue;
352 }
353
354 pending = coap_pending_next_to_expire(svc->data->pending, MAX_PENDINGS);
355 if (pending == NULL) {
356 continue;
357 }
358
359 remaining = pending->t0 + pending->timeout - now;
360 if (result > remaining) {
361 result = remaining;
362 }
363 }
364
365 if (result == INT64_MAX) {
366 return -1;
367 }
368
369 return MAX(result, 0);
370 }
371
coap_server_update_services(void)372 static void coap_server_update_services(void)
373 {
374 if (zvfs_eventfd_write(control_sock, 1)) {
375 LOG_ERR("Failed to notify server thread (%d)", errno);
376 }
377 }
378
coap_service_in_section(const struct coap_service * service)379 static inline bool coap_service_in_section(const struct coap_service *service)
380 {
381 STRUCT_SECTION_START_EXTERN(coap_service);
382 STRUCT_SECTION_END_EXTERN(coap_service);
383
384 return STRUCT_SECTION_START(coap_service) <= service &&
385 STRUCT_SECTION_END(coap_service) > service;
386 }
387
coap_service_raise_event(const struct coap_service * service,uint64_t mgmt_event)388 static inline void coap_service_raise_event(const struct coap_service *service, uint64_t mgmt_event)
389 {
390 #if defined(CONFIG_NET_MGMT_EVENT_INFO)
391 const struct net_event_coap_service net_event = {
392 .service = service,
393 };
394
395 net_mgmt_event_notify_with_info(mgmt_event, NULL, (void *)&net_event, sizeof(net_event));
396 #else
397 ARG_UNUSED(service);
398
399 net_mgmt_event_notify(mgmt_event, NULL);
400 #endif
401 }
402
coap_service_start(const struct coap_service * service)403 int coap_service_start(const struct coap_service *service)
404 {
405 int ret;
406
407 uint8_t af;
408 socklen_t len;
409 struct sockaddr_storage addr_storage;
410 union {
411 struct sockaddr *addr;
412 struct sockaddr_in *addr4;
413 struct sockaddr_in6 *addr6;
414 } addr_ptrs = {
415 .addr = (struct sockaddr *)&addr_storage,
416 };
417 int proto = IPPROTO_UDP;
418
419 if (!coap_service_in_section(service)) {
420 __ASSERT_NO_MSG(false);
421 return -EINVAL;
422 }
423
424 k_mutex_lock(&lock, K_FOREVER);
425
426 if (service->data->sock_fd >= 0) {
427 ret = -EALREADY;
428 goto end;
429 }
430
431 /* set the default address (in6addr_any / INADDR_ANY are all 0) */
432 addr_storage = (struct sockaddr_storage){0};
433 if (IS_ENABLED(CONFIG_NET_IPV6) && service->host != NULL &&
434 zsock_inet_pton(AF_INET6, service->host, &addr_ptrs.addr6->sin6_addr) == 1) {
435 /* if a literal IPv6 address is provided as the host, use IPv6 */
436 af = AF_INET6;
437 len = sizeof(struct sockaddr_in6);
438
439 addr_ptrs.addr6->sin6_family = AF_INET6;
440 addr_ptrs.addr6->sin6_port = htons(*service->port);
441 } else if (IS_ENABLED(CONFIG_NET_IPV4) && service->host != NULL &&
442 zsock_inet_pton(AF_INET, service->host, &addr_ptrs.addr4->sin_addr) == 1) {
443 /* if a literal IPv4 address is provided as the host, use IPv4 */
444 af = AF_INET;
445 len = sizeof(struct sockaddr_in);
446
447 addr_ptrs.addr4->sin_family = AF_INET;
448 addr_ptrs.addr4->sin_port = htons(*service->port);
449 } else if (IS_ENABLED(CONFIG_NET_IPV6)) {
450 /* prefer IPv6 if both IPv6 and IPv4 are supported */
451 af = AF_INET6;
452 len = sizeof(struct sockaddr_in6);
453
454 addr_ptrs.addr6->sin6_family = AF_INET6;
455 addr_ptrs.addr6->sin6_port = htons(*service->port);
456 } else if (IS_ENABLED(CONFIG_NET_IPV4)) {
457 af = AF_INET;
458 len = sizeof(struct sockaddr_in);
459
460 addr_ptrs.addr4->sin_family = AF_INET;
461 addr_ptrs.addr4->sin_port = htons(*service->port);
462 } else {
463 ret = -ENOTSUP;
464 goto end;
465 }
466
467 #if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
468 if (service->sec_tag_list != NULL) {
469 proto = IPPROTO_DTLS_1_2;
470 }
471 #endif
472
473 service->data->sock_fd = zsock_socket(af, SOCK_DGRAM, proto);
474 if (service->data->sock_fd < 0) {
475 ret = -errno;
476 goto end;
477 }
478
479 #if defined(CONFIG_NET_SOCKETS_ENABLE_DTLS)
480 if (service->sec_tag_list != NULL) {
481 int role = TLS_DTLS_ROLE_SERVER;
482
483 ret = zsock_setsockopt(service->data->sock_fd, SOL_TLS, TLS_SEC_TAG_LIST,
484 service->sec_tag_list, service->sec_tag_list_size);
485 if (ret < 0) {
486 ret = -errno;
487 goto close;
488 }
489
490 ret = zsock_setsockopt(service->data->sock_fd, SOL_TLS, TLS_DTLS_ROLE,
491 &role, sizeof(role));
492 if (ret < 0) {
493 ret = -errno;
494 goto close;
495 }
496 }
497 #endif
498
499 ret = zsock_fcntl(service->data->sock_fd, ZVFS_F_SETFL, ZVFS_O_NONBLOCK);
500 if (ret < 0) {
501 ret = -errno;
502 goto close;
503 }
504
505 ret = zsock_bind(service->data->sock_fd, addr_ptrs.addr, len);
506 if (ret < 0) {
507 ret = -errno;
508 goto close;
509 }
510
511 if (*service->port == 0) {
512 /* ephemeral port - read back the port number */
513 len = sizeof(addr_storage);
514 ret = zsock_getsockname(service->data->sock_fd, addr_ptrs.addr, &len);
515 if (ret < 0) {
516 goto close;
517 }
518
519 if (af == AF_INET6) {
520 *service->port = addr_ptrs.addr6->sin6_port;
521 } else {
522 *service->port = addr_ptrs.addr4->sin_port;
523 }
524 }
525
526 end:
527 k_mutex_unlock(&lock);
528
529 coap_server_update_services();
530
531 coap_service_raise_event(service, NET_EVENT_COAP_SERVICE_STARTED);
532
533 return ret;
534
535 close:
536 (void)zsock_close(service->data->sock_fd);
537 service->data->sock_fd = -1;
538
539 k_mutex_unlock(&lock);
540
541 return ret;
542 }
543
coap_service_stop(const struct coap_service * service)544 int coap_service_stop(const struct coap_service *service)
545 {
546 int ret;
547
548 if (!coap_service_in_section(service)) {
549 __ASSERT_NO_MSG(false);
550 return -EINVAL;
551 }
552
553 k_mutex_lock(&lock, K_FOREVER);
554
555 if (service->data->sock_fd < 0) {
556 k_mutex_unlock(&lock);
557 return -EALREADY;
558 }
559
560 /* Closing a socket will trigger a poll event */
561 ret = zsock_close(service->data->sock_fd);
562 service->data->sock_fd = -1;
563
564 k_mutex_unlock(&lock);
565
566 coap_service_raise_event(service, NET_EVENT_COAP_SERVICE_STOPPED);
567
568 return ret;
569 }
570
coap_service_is_running(const struct coap_service * service)571 int coap_service_is_running(const struct coap_service *service)
572 {
573 int ret;
574
575 if (!coap_service_in_section(service)) {
576 __ASSERT_NO_MSG(false);
577 return -EINVAL;
578 }
579
580 k_mutex_lock(&lock, K_FOREVER);
581
582 ret = (service->data->sock_fd < 0) ? 0 : 1;
583
584 k_mutex_unlock(&lock);
585
586 return ret;
587 }
588
coap_service_send(const struct coap_service * service,const struct coap_packet * cpkt,const struct sockaddr * addr,socklen_t addr_len,const struct coap_transmission_parameters * params)589 int coap_service_send(const struct coap_service *service, const struct coap_packet *cpkt,
590 const struct sockaddr *addr, socklen_t addr_len,
591 const struct coap_transmission_parameters *params)
592 {
593 int ret;
594
595 if (!coap_service_in_section(service)) {
596 __ASSERT_NO_MSG(false);
597 return -EINVAL;
598 }
599
600 (void)k_mutex_lock(&lock, K_FOREVER);
601
602 if (service->data->sock_fd < 0) {
603 (void)k_mutex_unlock(&lock);
604 return -EBADF;
605 }
606
607 /*
608 * Check if we should start with retransmits, if creating a pending message fails we still
609 * try to send.
610 */
611 if (coap_header_get_type(cpkt) == COAP_TYPE_CON) {
612 struct coap_pending *pending = coap_pending_next_unused(service->data->pending,
613 MAX_PENDINGS);
614
615 if (pending == NULL) {
616 LOG_WRN("No pending message available for %s", service->name);
617 goto send;
618 }
619
620 ret = coap_pending_init(pending, cpkt, addr, params);
621 if (ret < 0) {
622 LOG_WRN("Failed to init pending message for %s (%d)", service->name, ret);
623 goto send;
624 }
625
626 /* Replace tracked data with our allocated copy */
627 pending->data = coap_server_alloc(pending->len);
628 if (pending->data == NULL) {
629 LOG_WRN("Failed to allocate pending message data for %s", service->name);
630 coap_pending_clear(pending);
631 goto send;
632 }
633 memcpy(pending->data, cpkt->data, pending->len);
634
635 coap_pending_cycle(pending);
636
637 /* Trigger event in receive loop to schedule retransmit */
638 coap_server_update_services();
639 }
640
641 send:
642 (void)k_mutex_unlock(&lock);
643
644 ret = zsock_sendto(service->data->sock_fd, cpkt->data, cpkt->offset, 0, addr, addr_len);
645 if (ret < 0) {
646 LOG_ERR("Failed to send CoAP message (%d)", ret);
647 return ret;
648 }
649 __ASSERT_NO_MSG(ret == cpkt->offset);
650
651 return 0;
652 }
653
coap_resource_send(const struct coap_resource * resource,const struct coap_packet * cpkt,const struct sockaddr * addr,socklen_t addr_len,const struct coap_transmission_parameters * params)654 int coap_resource_send(const struct coap_resource *resource, const struct coap_packet *cpkt,
655 const struct sockaddr *addr, socklen_t addr_len,
656 const struct coap_transmission_parameters *params)
657 {
658 /* Find owning service */
659 COAP_SERVICE_FOREACH(svc) {
660 if (COAP_SERVICE_HAS_RESOURCE(svc, resource)) {
661 return coap_service_send(svc, cpkt, addr, addr_len, params);
662 }
663 }
664
665 return -ENOENT;
666 }
667
coap_resource_parse_observe(struct coap_resource * resource,const struct coap_packet * request,const struct sockaddr * addr)668 int coap_resource_parse_observe(struct coap_resource *resource, const struct coap_packet *request,
669 const struct sockaddr *addr)
670 {
671 const struct coap_service *service = NULL;
672 int ret;
673 uint8_t token[COAP_TOKEN_MAX_LEN];
674 uint8_t tkl;
675
676 if (!coap_packet_is_request(request)) {
677 return -EINVAL;
678 }
679
680 ret = coap_get_option_int(request, COAP_OPTION_OBSERVE);
681 if (ret < 0) {
682 return ret;
683 }
684
685 /* Find owning service */
686 COAP_SERVICE_FOREACH(svc) {
687 if (COAP_SERVICE_HAS_RESOURCE(svc, resource)) {
688 service = svc;
689 break;
690 }
691 }
692
693 if (service == NULL) {
694 return -ENOENT;
695 }
696
697 tkl = coap_header_get_token(request, token);
698 if (tkl == 0) {
699 return -EINVAL;
700 }
701
702 (void)k_mutex_lock(&lock, K_FOREVER);
703
704 if (ret == 0) {
705 struct coap_observer *observer;
706
707 /* RFC7641 section 4.1 - Check if the current observer already exists */
708 observer = coap_find_observer(service->data->observers, MAX_OBSERVERS, addr, token,
709 tkl);
710 if (observer != NULL) {
711 /* Client refresh */
712 goto unlock;
713 }
714
715 /* New client */
716 observer = coap_observer_next_unused(service->data->observers, MAX_OBSERVERS);
717 if (observer == NULL) {
718 ret = -ENOMEM;
719 goto unlock;
720 }
721
722 coap_observer_init(observer, request, addr);
723 coap_register_observer(resource, observer);
724 } else if (ret == 1) {
725 ret = coap_service_remove_observer(service, resource, addr, token, tkl);
726 if (ret < 0) {
727 LOG_WRN("Failed to remove observer (%d)", ret);
728 goto unlock;
729 }
730
731 if (ret == 0) {
732 /* Observer not found */
733 ret = -ENOENT;
734 }
735 }
736
737 unlock:
738 (void)k_mutex_unlock(&lock);
739
740 return ret;
741 }
742
coap_resource_remove_observer(struct coap_resource * resource,const struct sockaddr * addr,const uint8_t * token,uint8_t token_len)743 static int coap_resource_remove_observer(struct coap_resource *resource,
744 const struct sockaddr *addr,
745 const uint8_t *token, uint8_t token_len)
746 {
747 const struct coap_service *service = NULL;
748 int ret;
749
750 /* Find owning service */
751 COAP_SERVICE_FOREACH(svc) {
752 if (COAP_SERVICE_HAS_RESOURCE(svc, resource)) {
753 service = svc;
754 break;
755 }
756 }
757
758 if (service == NULL) {
759 return -ENOENT;
760 }
761
762 (void)k_mutex_lock(&lock, K_FOREVER);
763 ret = coap_service_remove_observer(service, resource, addr, token, token_len);
764 (void)k_mutex_unlock(&lock);
765
766 if (ret == 1) {
767 /* An observer was found and removed */
768 return 0;
769 } else if (ret == 0) {
770 /* No matching observer found */
771 return -ENOENT;
772 }
773
774 /* An error occurred */
775 return ret;
776 }
777
coap_resource_remove_observer_by_addr(struct coap_resource * resource,const struct sockaddr * addr)778 int coap_resource_remove_observer_by_addr(struct coap_resource *resource,
779 const struct sockaddr *addr)
780 {
781 return coap_resource_remove_observer(resource, addr, NULL, 0);
782 }
783
coap_resource_remove_observer_by_token(struct coap_resource * resource,const uint8_t * token,uint8_t token_len)784 int coap_resource_remove_observer_by_token(struct coap_resource *resource,
785 const uint8_t *token, uint8_t token_len)
786 {
787 return coap_resource_remove_observer(resource, NULL, token, token_len);
788 }
789
coap_server_thread(void * p1,void * p2,void * p3)790 static void coap_server_thread(void *p1, void *p2, void *p3)
791 {
792 struct zsock_pollfd sock_fds[MAX_POLL_FD];
793 int sock_nfds;
794 int ret;
795
796 ARG_UNUSED(p1);
797 ARG_UNUSED(p2);
798 ARG_UNUSED(p3);
799
800 control_sock = zvfs_eventfd(0, ZVFS_EFD_NONBLOCK);
801 if (control_sock < 0) {
802 LOG_ERR("Failed to create event fd (%d)", -errno);
803 return;
804 }
805
806 COAP_SERVICE_FOREACH(svc) {
807 if (svc->flags & COAP_SERVICE_AUTOSTART) {
808 ret = coap_service_start(svc);
809 if (ret < 0) {
810 LOG_ERR("Failed to autostart service %s (%d)", svc->name, ret);
811 }
812 }
813 }
814
815 while (true) {
816 sock_nfds = 0;
817 COAP_SERVICE_FOREACH(svc) {
818 if (svc->data->sock_fd < 0) {
819 continue;
820 }
821 if (sock_nfds >= MAX_POLL_FD) {
822 LOG_ERR("Maximum active CoAP services reached (%d), "
823 "increase CONFIG_ZVFS_POLL_MAX to support more.",
824 MAX_POLL_FD);
825 break;
826 }
827
828 sock_fds[sock_nfds].fd = svc->data->sock_fd;
829 sock_fds[sock_nfds].events = ZSOCK_POLLIN;
830 sock_fds[sock_nfds].revents = 0;
831 sock_nfds++;
832 }
833
834 /* Add event FD to allow wake up */
835 if (sock_nfds < MAX_POLL_FD) {
836 sock_fds[sock_nfds].fd = control_sock;
837 sock_fds[sock_nfds].events = ZSOCK_POLLIN;
838 sock_fds[sock_nfds].revents = 0;
839 sock_nfds++;
840 }
841
842 __ASSERT_NO_MSG(sock_nfds > 0);
843
844 ret = zsock_poll(sock_fds, sock_nfds, coap_server_poll_timeout());
845 if (ret < 0) {
846 LOG_ERR("Poll error (%d)", -errno);
847 k_msleep(10);
848 }
849
850 for (int i = 0; i < sock_nfds; ++i) {
851 /* Check the wake up event */
852 if (sock_fds[i].fd == control_sock &&
853 sock_fds[i].revents & ZSOCK_POLLIN) {
854 zvfs_eventfd_t tmp;
855
856 zvfs_eventfd_read(sock_fds[i].fd, &tmp);
857 continue;
858 }
859
860 /* Check if socket can receive/was closed first */
861 if (sock_fds[i].revents & ZSOCK_POLLIN) {
862 coap_server_process(sock_fds[i].fd);
863 continue;
864 }
865
866 if (sock_fds[i].revents & ZSOCK_POLLERR) {
867 LOG_ERR("Poll error on %d", sock_fds[i].fd);
868 }
869 if (sock_fds[i].revents & ZSOCK_POLLHUP) {
870 LOG_DBG("Poll hup on %d", sock_fds[i].fd);
871 }
872 if (sock_fds[i].revents & ZSOCK_POLLNVAL) {
873 LOG_ERR("Poll invalid on %d", sock_fds[i].fd);
874 }
875 }
876
877 /* Process retransmits */
878 coap_server_retransmit();
879 }
880 }
881
882 K_THREAD_DEFINE(coap_server_id, CONFIG_COAP_SERVER_STACK_SIZE,
883 coap_server_thread, NULL, NULL, NULL,
884 THREAD_PRIORITY, 0, 0);
885