1 /*
2 * Copyright (c) 2018 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_DECLARE(net_coap, CONFIG_COAP_LOG_LEVEL);
9
10 #include <stdlib.h>
11 #include <stddef.h>
12 #include <zephyr/types.h>
13 #include <string.h>
14 #include <stdbool.h>
15 #include <errno.h>
16
17 #include <zephyr/sys/byteorder.h>
18
19 #include <zephyr/sys/printk.h>
20
21 #include <zephyr/net/coap.h>
22 #include <zephyr/net/coap_link_format.h>
23
append_u8(struct coap_packet * cpkt,uint8_t data)24 static inline bool append_u8(struct coap_packet *cpkt, uint8_t data)
25 {
26 if (!cpkt) {
27 return false;
28 }
29
30 if (cpkt->max_len - cpkt->offset < 1) {
31 return false;
32 }
33
34 cpkt->data[cpkt->offset++] = data;
35
36 return true;
37 }
38
append_be16(struct coap_packet * cpkt,uint16_t data)39 static inline bool append_be16(struct coap_packet *cpkt, uint16_t data)
40 {
41 if (!cpkt) {
42 return false;
43 }
44
45 if (cpkt->max_len - cpkt->offset < 2) {
46 return false;
47 }
48
49 cpkt->data[cpkt->offset++] = data >> 8;
50 cpkt->data[cpkt->offset++] = (uint8_t) data;
51
52 return true;
53 }
54
append(struct coap_packet * cpkt,const uint8_t * data,uint16_t len)55 static inline bool append(struct coap_packet *cpkt, const uint8_t *data, uint16_t len)
56 {
57 if (!cpkt || !data) {
58 return false;
59 }
60
61 if (cpkt->max_len - cpkt->offset < len) {
62 return false;
63 }
64
65 memcpy(cpkt->data + cpkt->offset, data, len);
66 cpkt->offset += len;
67
68 return true;
69 }
70
match_path_uri(const char * const * path,const char * uri,uint16_t len)71 static bool match_path_uri(const char * const *path,
72 const char *uri, uint16_t len)
73 {
74 const char * const *p = NULL;
75 int i, j, k, plen;
76
77 if (!path) {
78 return false;
79 }
80
81 if (len <= 1U || uri[0] != '/') {
82 return false;
83 }
84
85 p = path;
86 plen = *p ? strlen(*p) : 0;
87
88 if (plen == 0) {
89 return false;
90 }
91
92 /* Go through uri and try to find a matching path */
93 for (i = 1; i < len; i++) {
94 while (*p) {
95 plen = strlen(*p);
96
97 k = i;
98
99 for (j = 0; j < plen; j++) {
100 if (uri[k] == '*') {
101 if ((k + 1) == len) {
102 return true;
103 }
104 }
105
106 if (uri[k] != (*p)[j]) {
107 goto next;
108 }
109
110 k++;
111 }
112
113 if (i == (k - 1) && j == plen) {
114 return true;
115 }
116
117 if (k == len && j == plen) {
118 return true;
119 }
120
121 next:
122 p++;
123 }
124 }
125
126 /* Did we find the resource or not */
127 if (i == len && !*p) {
128 return false;
129 }
130
131 return true;
132 }
133
match_attributes(const char * const * attributes,const struct coap_option * query)134 static bool match_attributes(const char * const *attributes,
135 const struct coap_option *query)
136 {
137 const char * const *attr;
138
139 /* FIXME: deal with the case when there are multiple options in a
140 * query, for example: 'rt=lux temperature', if I want to list
141 * resources with resource type lux or temperature.
142 */
143 for (attr = attributes; attr && *attr; attr++) {
144 uint16_t attr_len = strlen(*attr);
145
146 if (query->len != attr_len) {
147 continue;
148 }
149
150 if (!strncmp((char *) query->value, *attr, attr_len)) {
151 return true;
152 }
153 }
154
155 return false;
156 }
157
match_queries_resource(const struct coap_resource * resource,const struct coap_option * query,int num_queries)158 static bool match_queries_resource(const struct coap_resource *resource,
159 const struct coap_option *query,
160 int num_queries)
161 {
162 struct coap_core_metadata *meta = resource->user_data;
163 const char * const *attributes = NULL;
164 const int href_len = strlen("href");
165
166 if (num_queries == 0) {
167 return true;
168 }
169
170 if (meta && meta->attributes) {
171 attributes = meta->attributes;
172 }
173
174 if (!attributes) {
175 return false;
176 }
177
178 if (query->len > href_len + 1 &&
179 !strncmp((char *) query->value, "href", href_len)) {
180 /* The stuff after 'href=' */
181 const char *uri = (char *) query->value + href_len + 1;
182 uint16_t uri_len = query->len - (href_len + 1);
183
184 return match_path_uri(resource->path, uri, uri_len);
185 }
186
187 return match_attributes(attributes, query);
188 }
189
190 #if defined(CONFIG_COAP_WELL_KNOWN_BLOCK_WISE)
191
192 #define MAX_BLOCK_WISE_TRANSFER_SIZE 2048
193
default_block_size(void)194 enum coap_block_size default_block_size(void)
195 {
196 switch (CONFIG_COAP_WELL_KNOWN_BLOCK_WISE_SIZE) {
197 case 16:
198 return COAP_BLOCK_16;
199 case 32:
200 return COAP_BLOCK_32;
201 case 64:
202 return COAP_BLOCK_64;
203 case 128:
204 return COAP_BLOCK_128;
205 case 256:
206 return COAP_BLOCK_256;
207 case 512:
208 return COAP_BLOCK_512;
209 case 1024:
210 return COAP_BLOCK_1024;
211 }
212
213 return COAP_BLOCK_64;
214 }
215
append_to_coap_pkt(struct coap_packet * response,const char * str,uint16_t len,uint16_t * remaining,size_t * offset,size_t current)216 static bool append_to_coap_pkt(struct coap_packet *response,
217 const char *str, uint16_t len,
218 uint16_t *remaining, size_t *offset,
219 size_t current)
220 {
221 uint16_t pos = 0U;
222 bool res;
223
224 if (!*remaining) {
225 return true;
226 }
227
228 if (*offset < current) {
229 pos = current - *offset;
230
231 if (len >= pos) {
232 len -= pos;
233 *offset += pos;
234 } else {
235 *offset += len;
236 return true;
237 }
238 }
239
240 if (len > *remaining) {
241 len = *remaining;
242 }
243
244 res = append(response, str + pos, len);
245
246 *remaining -= len;
247 *offset += len;
248
249 return res;
250 }
251
format_uri(const char * const * path,struct coap_packet * response,uint16_t * remaining,size_t * offset,size_t current,bool * more)252 static int format_uri(const char * const *path,
253 struct coap_packet *response,
254 uint16_t *remaining, size_t *offset,
255 size_t current, bool *more)
256 {
257 static const char prefix[] = "</";
258 const char * const *p;
259 bool res;
260
261 if (!path) {
262 return -EINVAL;
263 }
264
265 res = append_to_coap_pkt(response, &prefix[0], sizeof(prefix) - 1,
266 remaining, offset, current);
267 if (!res) {
268 return -ENOMEM;
269 }
270
271 if (!*remaining) {
272 *more = true;
273 return 0;
274 }
275
276 for (p = path; *p; ) {
277 uint16_t path_len = strlen(*p);
278
279 res = append_to_coap_pkt(response, *p, path_len, remaining,
280 offset, current);
281 if (!res) {
282 return -ENOMEM;
283 }
284
285 if (!*remaining) {
286 *more = true;
287 return 0;
288 }
289
290 p++;
291 if (!*p) {
292 continue;
293 }
294
295 res = append_to_coap_pkt(response, "/", 1, remaining, offset,
296 current);
297 if (!res) {
298 return -ENOMEM;
299 }
300
301 if (!*remaining) {
302 *more = true;
303 return 0;
304 }
305
306 }
307
308 res = append_to_coap_pkt(response, ">", 1, remaining, offset, current);
309 if (!res) {
310 return -ENOMEM;
311 }
312
313 if (!*remaining) {
314 *more = true;
315 return 0;
316 }
317
318 *more = false;
319
320 return 0;
321 }
322
format_attributes(const char * const * attributes,struct coap_packet * response,uint16_t * remaining,size_t * offset,size_t current,bool * more)323 static int format_attributes(const char * const *attributes,
324 struct coap_packet *response,
325 uint16_t *remaining, size_t *offset,
326 size_t current, bool *more)
327 {
328 const char * const *attr;
329 bool res;
330
331 if (!attributes) {
332 *more = false;
333 return 0;
334 }
335
336 for (attr = attributes; *attr; attr++) {
337 int attr_len;
338
339 res = append_to_coap_pkt(response, ";", 1,
340 remaining, offset, current);
341 if (!res) {
342 return -ENOMEM;
343 }
344
345 if (!*remaining) {
346 *more = true;
347 return 0;
348 }
349
350 attr_len = strlen(*attr);
351
352 res = append_to_coap_pkt(response, *attr, attr_len,
353 remaining, offset, current);
354 if (!res) {
355 return -ENOMEM;
356 }
357
358 if (*(attr + 1) && !*remaining) {
359 *more = true;
360 return 0;
361 }
362 }
363
364 *more = false;
365 return 0;
366 }
367
format_resource(const struct coap_resource * resource,struct coap_packet * response,uint16_t * remaining,size_t * offset,size_t current,bool * more)368 static int format_resource(const struct coap_resource *resource,
369 struct coap_packet *response,
370 uint16_t *remaining, size_t *offset,
371 size_t current, bool *more)
372 {
373 struct coap_core_metadata *meta = resource->user_data;
374 const char * const *attributes = NULL;
375 int r;
376
377 r = format_uri(resource->path, response, remaining,
378 offset, current, more);
379 if (r < 0) {
380 return r;
381 }
382
383 if (!*remaining) {
384 *more = true;
385 return 0;
386 }
387
388 if (meta && meta->attributes) {
389 attributes = meta->attributes;
390 }
391
392 return format_attributes(attributes, response, remaining, offset,
393 current, more);
394 }
395
396 /* coap_well_known_core_get() added Option (delta and len) with
397 * out any extended options so this function will not consider Extended
398 * options at the moment.
399 */
clear_more_flag(struct coap_packet * cpkt)400 int clear_more_flag(struct coap_packet *cpkt)
401 {
402 uint16_t offset;
403 uint8_t opt;
404 uint8_t delta;
405 uint8_t len;
406
407 offset = cpkt->hdr_len;
408 delta = 0U;
409
410 while (1) {
411 opt = cpkt->data[offset++];
412
413 delta += ((opt & 0xF0) >> 4);
414 len = (opt & 0xF);
415
416 if (delta == COAP_OPTION_BLOCK2) {
417 break;
418 }
419
420 offset += len;
421 }
422
423 /* As per RFC 7959 Sec 2.2 : NUM filed can be on 0-3 bytes.
424 * Skip NUM field to update M bit.
425 */
426 if (len > 1) {
427 offset = offset + len - 1;
428 }
429
430 cpkt->data[offset] = cpkt->data[offset] & 0xF7;
431
432 return 0;
433 }
434
coap_well_known_core_get_len(struct coap_resource * resources,size_t resources_len,struct coap_packet * request,struct coap_packet * response,uint8_t * data,uint16_t len)435 int coap_well_known_core_get_len(struct coap_resource *resources,
436 size_t resources_len,
437 struct coap_packet *request,
438 struct coap_packet *response,
439 uint8_t *data, uint16_t len)
440 {
441 static struct coap_block_context ctx;
442 struct coap_option query;
443 unsigned int num_queries;
444 size_t offset;
445 uint8_t token[COAP_TOKEN_MAX_LEN];
446 uint16_t remaining;
447 uint16_t id;
448 uint8_t tkl;
449 int r;
450 bool more = false, first = true;
451
452 if (!resources || !request || !response || !data || !len) {
453 return -EINVAL;
454 }
455
456 if (ctx.total_size == 0) {
457 /* We have to iterate through resources and it's attributes,
458 * total size is unknown, so initialize it to
459 * MAX_BLOCK_WISE_TRANSFER_SIZE and update it according to
460 * offset.
461 */
462 coap_block_transfer_init(&ctx, default_block_size(),
463 MAX_BLOCK_WISE_TRANSFER_SIZE);
464 }
465
466 r = coap_update_from_block(request, &ctx);
467 if (r < 0) {
468 goto end;
469 }
470
471 id = coap_header_get_id(request);
472 tkl = coap_header_get_token(request, token);
473
474 /* Per RFC 6690, Section 4.1, only one (or none) query parameter may be
475 * provided, use the first if multiple.
476 */
477 r = coap_find_options(request, COAP_OPTION_URI_QUERY, &query, 1);
478 if (r < 0) {
479 goto end;
480 }
481
482 num_queries = r;
483
484 r = coap_packet_init(response, data, len, COAP_VERSION_1, COAP_TYPE_ACK,
485 tkl, token, COAP_RESPONSE_CODE_CONTENT, id);
486 if (r < 0) {
487 goto end;
488 }
489
490 r = coap_append_option_int(response, COAP_OPTION_CONTENT_FORMAT,
491 COAP_CONTENT_FORMAT_APP_LINK_FORMAT);
492 if (r < 0) {
493 goto end;
494 }
495
496 r = coap_append_block2_option(response, &ctx);
497 if (r < 0) {
498 goto end;
499 }
500
501 r = coap_packet_append_payload_marker(response);
502 if (r < 0) {
503 goto end;
504 }
505
506 offset = 0;
507 remaining = coap_block_size_to_bytes(ctx.block_size);
508
509 for (size_t i = 0; i < resources_len; ++i) {
510 if (!remaining) {
511 more = true;
512 break;
513 }
514
515 if (!match_queries_resource(&resources[i], &query, num_queries)) {
516 continue;
517 }
518
519 if (first) {
520 first = false;
521 } else {
522 r = append_to_coap_pkt(response, ",", 1, &remaining,
523 &offset, ctx.current);
524 if (!r) {
525 goto end;
526 }
527 }
528
529 r = format_resource(&resources[i], response, &remaining, &offset,
530 ctx.current, &more);
531 if (r < 0) {
532 goto end;
533 }
534 }
535
536 /* Offset is the total size now, but block2 option is already
537 * appended. So update only 'more' flag.
538 */
539 if (!more) {
540 ctx.total_size = offset;
541 r = clear_more_flag(response);
542 }
543
544 end:
545 /* So it's a last block, reset context */
546 if (!more) {
547 (void)memset(&ctx, 0, sizeof(ctx));
548 }
549
550 return r;
551 }
552
553 #else
554
format_uri(const char * const * path,struct coap_packet * response)555 static int format_uri(const char * const *path, struct coap_packet *response)
556 {
557 const char * const *p;
558 char *prefix = "</";
559 bool res;
560
561 if (!path) {
562 return -EINVAL;
563 }
564
565 res = append(response, (uint8_t *) prefix, strlen(prefix));
566 if (!res) {
567 return -ENOMEM;
568 }
569
570 for (p = path; *p; ) {
571 res = append(response, (uint8_t *) *p, strlen(*p));
572 if (!res) {
573 return -ENOMEM;
574 }
575
576 p++;
577 if (*p) {
578 res = append_u8(response, (uint8_t) '/');
579 if (!res) {
580 return -ENOMEM;
581 }
582 }
583 }
584
585 res = append_u8(response, (uint8_t) '>');
586 if (!res) {
587 return -ENOMEM;
588 }
589
590 return 0;
591 }
592
format_attributes(const char * const * attributes,struct coap_packet * response)593 static int format_attributes(const char * const *attributes,
594 struct coap_packet *response)
595 {
596 const char * const *attr;
597 bool res;
598
599 if (!attributes) {
600 return 0;
601 }
602
603 for (attr = attributes; *attr; attr++) {
604 res = append_u8(response, (uint8_t) ';');
605 if (!res) {
606 return -ENOMEM;
607 }
608
609 res = append(response, (uint8_t *) *attr, strlen(*attr));
610 if (!res) {
611 return -ENOMEM;
612 }
613 }
614
615 return 0;
616 }
617
format_resource(const struct coap_resource * resource,struct coap_packet * response)618 static int format_resource(const struct coap_resource *resource,
619 struct coap_packet *response)
620 {
621 struct coap_core_metadata *meta = resource->user_data;
622 const char * const *attributes = NULL;
623 int r;
624
625 r = format_uri(resource->path, response);
626 if (r < 0) {
627 return r;
628 }
629
630 if (meta && meta->attributes) {
631 attributes = meta->attributes;
632 }
633
634 return format_attributes(attributes, response);
635 }
636
coap_well_known_core_get_len(struct coap_resource * resources,size_t resources_len,const struct coap_packet * request,struct coap_packet * response,uint8_t * data,uint16_t data_len)637 int coap_well_known_core_get_len(struct coap_resource *resources,
638 size_t resources_len,
639 const struct coap_packet *request,
640 struct coap_packet *response,
641 uint8_t *data, uint16_t data_len)
642 {
643 struct coap_option query;
644 uint8_t token[COAP_TOKEN_MAX_LEN];
645 uint16_t id;
646 uint8_t tkl;
647 uint8_t num_queries;
648 int r;
649 bool first = true;
650
651 if (!resources || !request || !response || !data || !data_len) {
652 return -EINVAL;
653 }
654
655 id = coap_header_get_id(request);
656 tkl = coap_header_get_token(request, token);
657
658 /* Per RFC 6690, Section 4.1, only one (or none) query parameter may be
659 * provided, use the first if multiple.
660 */
661 r = coap_find_options(request, COAP_OPTION_URI_QUERY, &query, 1);
662 if (r < 0) {
663 return r;
664 }
665
666 num_queries = r;
667
668 r = coap_packet_init(response, data, data_len, COAP_VERSION_1, COAP_TYPE_ACK,
669 tkl, token, COAP_RESPONSE_CODE_CONTENT, id);
670 if (r < 0) {
671 return r;
672 }
673
674 r = coap_append_option_int(response, COAP_OPTION_CONTENT_FORMAT,
675 COAP_CONTENT_FORMAT_APP_LINK_FORMAT);
676 if (r < 0) {
677 return -EINVAL;
678 }
679
680 r = coap_packet_append_payload_marker(response);
681 if (r < 0) {
682 return -EINVAL;
683 }
684
685 for (size_t i = 0; i < resources_len; ++i) {
686 if (!match_queries_resource(&resources[i], &query, num_queries)) {
687 continue;
688 }
689
690 if (first) {
691 first = false;
692 } else {
693 r = append_u8(response, (uint8_t) ',');
694 if (!r) {
695 return -ENOMEM;
696 }
697 }
698
699 r = format_resource(&resources[i], response);
700 if (r < 0) {
701 return r;
702 }
703 }
704
705 return 0;
706 }
707 #endif
708
coap_well_known_core_get(struct coap_resource * resource,const struct coap_packet * request,struct coap_packet * response,uint8_t * data,uint16_t data_len)709 int coap_well_known_core_get(struct coap_resource *resource,
710 const struct coap_packet *request,
711 struct coap_packet *response,
712 uint8_t *data, uint16_t data_len)
713 {
714 struct coap_resource *resources = resource + 1;
715 size_t resources_len = 0;
716
717 if (resource == NULL) {
718 return -EINVAL;
719 }
720
721 while (resources[resources_len].path) {
722 resources_len++;
723 }
724
725 return coap_well_known_core_get_len(resources, resources_len, request, response, data,
726 data_len);
727 }
728
729 /* Exposing some of the APIs to CoAP unit tests in tests/net/lib/coap */
730 #if defined(CONFIG_COAP_TEST_API_ENABLE)
_coap_match_path_uri(const char * const * path,const char * uri,uint16_t len)731 bool _coap_match_path_uri(const char * const *path,
732 const char *uri, uint16_t len)
733 {
734 return match_path_uri(path, uri, len);
735 }
736 #endif
737