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