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