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