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