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