1 /*
2  * Copyright (c) 2017 Linaro Limited
3  * Copyright (c) 2018-2019 Foundries.io
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 /*
9  * Uses some original concepts by:
10  *         Joakim Eriksson <joakime@sics.se>
11  *         Niclas Finne <nfi@sics.se>
12  *         Joel Hoglund <joel@sics.se>
13  */
14 
15 #define LOG_MODULE_NAME net_lwm2m_engine
16 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
17 
18 #include <logging/log.h>
19 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
20 
21 #include <fcntl.h>
22 #include <zephyr/types.h>
23 #include <stddef.h>
24 #include <stdlib.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <errno.h>
29 #include <init.h>
30 #include <sys/printk.h>
31 #include <net/net_ip.h>
32 #include <net/http_parser_url.h>
33 #include <net/socket.h>
34 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
35 #include <net/tls_credentials.h>
36 #endif
37 #if defined(CONFIG_DNS_RESOLVER)
38 #include <net/dns_resolve.h>
39 #endif
40 
41 #include "lwm2m_object.h"
42 #include "lwm2m_engine.h"
43 #include "lwm2m_rw_link_format.h"
44 #include "lwm2m_rw_plain_text.h"
45 #include "lwm2m_rw_oma_tlv.h"
46 #include "lwm2m_util.h"
47 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
48 #include "lwm2m_rw_json.h"
49 #endif
50 #ifdef CONFIG_LWM2M_RD_CLIENT_SUPPORT
51 #include "lwm2m_rd_client.h"
52 #endif
53 
54 #if IS_ENABLED(CONFIG_NET_TC_THREAD_COOPERATIVE)
55 /* Lowest priority cooperative thread */
56 #define THREAD_PRIORITY K_PRIO_COOP(CONFIG_NUM_COOP_PRIORITIES - 1)
57 #else
58 #define THREAD_PRIORITY K_PRIO_PREEMPT(CONFIG_NUM_PREEMPT_PRIORITIES - 1)
59 #endif
60 
61 #define ENGINE_UPDATE_INTERVAL_MS 500
62 #define OBSERVE_COUNTER_START 0U
63 
64 #if defined(CONFIG_COAP_EXTENDED_OPTIONS_LEN)
65 #define	COAP_OPTION_BUF_LEN	(CONFIG_COAP_EXTENDED_OPTIONS_LEN_VALUE + 1)
66 #else
67 #define	COAP_OPTION_BUF_LEN	13
68 #endif
69 
70 #define MAX_TOKEN_LEN		8
71 
72 #define LWM2M_MAX_PATH_STR_LEN sizeof("65535/65535/65535/65535")
73 
74 struct observe_node {
75 	sys_snode_t node;
76 	struct lwm2m_obj_path path;
77 	uint8_t  token[MAX_TOKEN_LEN];
78 	int64_t event_timestamp;
79 	int64_t last_timestamp;
80 	uint32_t min_period_sec;
81 	uint32_t max_period_sec;
82 	uint32_t counter;
83 	uint16_t format;
84 	uint8_t  tkl;
85 };
86 
87 struct notification_attrs {
88 	/* use to determine which value is set */
89 	float32_value_t gt;
90 	float32_value_t lt;
91 	float32_value_t st;
92 	int32_t pmin;
93 	int32_t pmax;
94 	uint8_t flags;
95 };
96 
97 static struct observe_node observe_node_data[CONFIG_LWM2M_ENGINE_MAX_OBSERVER];
98 
99 #define MAX_PERIODIC_SERVICE	10
100 
101 struct service_node {
102 	sys_snode_t node;
103 	k_work_handler_t service_work;
104 	uint32_t min_call_period; /* ms */
105 	uint64_t last_timestamp; /* ms */
106 };
107 
108 static struct service_node service_node_data[MAX_PERIODIC_SERVICE];
109 
110 static sys_slist_t engine_obj_list;
111 static sys_slist_t engine_obj_inst_list;
112 static sys_slist_t engine_service_list;
113 
114 static K_KERNEL_STACK_DEFINE(engine_thread_stack,
115 			      CONFIG_LWM2M_ENGINE_STACK_SIZE);
116 static struct k_thread engine_thread_data;
117 
118 #define MAX_POLL_FD		CONFIG_NET_SOCKETS_POLL_MAX
119 
120 static struct lwm2m_ctx *sock_ctx[MAX_POLL_FD];
121 static struct pollfd sock_fds[MAX_POLL_FD];
122 static int sock_nfds;
123 
124 #define NUM_BLOCK1_CONTEXT	CONFIG_LWM2M_NUM_BLOCK1_CONTEXT
125 
126 /* TODO: figure out what's correct value */
127 #define TIMEOUT_BLOCKWISE_TRANSFER_MS (MSEC_PER_SEC * 30)
128 
129 static struct lwm2m_block_context block1_contexts[NUM_BLOCK1_CONTEXT];
130 
131 /* write-attribute related definitons */
132 static const char * const LWM2M_ATTR_STR[] = { "pmin", "pmax",
133 					       "gt", "lt", "st" };
134 static const uint8_t LWM2M_ATTR_LEN[] = { 4, 4, 2, 2, 2 };
135 
136 static struct lwm2m_attr write_attr_pool[CONFIG_LWM2M_NUM_ATTR];
137 
138 static struct lwm2m_engine_obj *get_engine_obj(int obj_id);
139 static struct lwm2m_engine_obj_inst *get_engine_obj_inst(int obj_id,
140 							 int obj_inst_id);
141 
142 /* Shared set of in-flight LwM2M messages */
143 static struct lwm2m_message messages[CONFIG_LWM2M_ENGINE_MAX_MESSAGES];
144 
145 /* for debugging: to print IP addresses */
lwm2m_sprint_ip_addr(const struct sockaddr * addr)146 char *lwm2m_sprint_ip_addr(const struct sockaddr *addr)
147 {
148 	static char buf[NET_IPV6_ADDR_LEN];
149 
150 	if (addr->sa_family == AF_INET6) {
151 		return net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr,
152 				     buf, sizeof(buf));
153 	}
154 
155 	if (addr->sa_family == AF_INET) {
156 		return net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr,
157 				     buf, sizeof(buf));
158 	}
159 
160 	LOG_ERR("Unknown IP address family:%d", addr->sa_family);
161 	strcpy(buf, "unk");
162 	return buf;
163 }
164 
to_hex_digit(uint8_t digit)165 static uint8_t to_hex_digit(uint8_t digit)
166 {
167 	if (digit >= 10U) {
168 		return digit - 10U + 'a';
169 	}
170 
171 	return digit + '0';
172 }
173 
sprint_token(const uint8_t * token,uint8_t tkl)174 static char *sprint_token(const uint8_t *token, uint8_t tkl)
175 {
176 	static char buf[32];
177 	char *ptr = buf;
178 
179 	if (token && tkl != 0) {
180 		int i;
181 
182 		tkl = MIN(tkl, sizeof(buf) / 2 - 1);
183 
184 		for (i = 0; i < tkl; i++) {
185 			*ptr++ = to_hex_digit(token[i] >> 4);
186 			*ptr++ = to_hex_digit(token[i] & 0x0F);
187 		}
188 
189 		*ptr = '\0';
190 	} else {
191 		strcpy(buf, "[no-token]");
192 	}
193 
194 	return buf;
195 }
196 
197 /* block-wise transfer functions */
198 
lwm2m_default_block_size(void)199 enum coap_block_size lwm2m_default_block_size(void)
200 {
201 	switch (CONFIG_LWM2M_COAP_BLOCK_SIZE) {
202 	case 16:
203 		return COAP_BLOCK_16;
204 	case 32:
205 		return COAP_BLOCK_32;
206 	case 64:
207 		return COAP_BLOCK_64;
208 	case 128:
209 		return COAP_BLOCK_128;
210 	case 256:
211 		return COAP_BLOCK_256;
212 	case 512:
213 		return COAP_BLOCK_512;
214 	case 1024:
215 		return COAP_BLOCK_1024;
216 	}
217 
218 	return COAP_BLOCK_256;
219 }
220 
init_block_ctx(const uint8_t * token,uint8_t tkl,struct lwm2m_block_context ** ctx)221 static int init_block_ctx(const uint8_t *token, uint8_t tkl,
222 			  struct lwm2m_block_context **ctx)
223 {
224 	int i;
225 	int64_t timestamp;
226 
227 	*ctx = NULL;
228 	timestamp = k_uptime_get();
229 	for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
230 		if (block1_contexts[i].tkl == 0U) {
231 			*ctx = &block1_contexts[i];
232 			break;
233 		}
234 
235 		if (timestamp - block1_contexts[i].timestamp >
236 		    TIMEOUT_BLOCKWISE_TRANSFER_MS) {
237 			*ctx = &block1_contexts[i];
238 			/* TODO: notify application for block
239 			 * transfer timeout
240 			 */
241 			break;
242 		}
243 	}
244 
245 	if (*ctx == NULL) {
246 		LOG_ERR("Cannot find free block context");
247 		return -ENOMEM;
248 	}
249 
250 	(*ctx)->tkl = tkl;
251 	memcpy((*ctx)->token, token, tkl);
252 	coap_block_transfer_init(&(*ctx)->ctx, lwm2m_default_block_size(), 0);
253 	(*ctx)->timestamp = timestamp;
254 	(*ctx)->expected = 0;
255 	(*ctx)->last_block = false;
256 	memset(&(*ctx)->opaque, 0, sizeof((*ctx)->opaque));
257 
258 	return 0;
259 }
260 
get_block_ctx(const uint8_t * token,uint8_t tkl,struct lwm2m_block_context ** ctx)261 static int get_block_ctx(const uint8_t *token, uint8_t tkl,
262 			 struct lwm2m_block_context **ctx)
263 {
264 	int i;
265 
266 	*ctx = NULL;
267 
268 	for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
269 		if (block1_contexts[i].tkl == tkl &&
270 		    memcmp(token, block1_contexts[i].token, tkl) == 0) {
271 			*ctx = &block1_contexts[i];
272 			/* refresh timestamp */
273 			(*ctx)->timestamp = k_uptime_get();
274 			break;
275 		}
276 	}
277 
278 	if (*ctx == NULL) {
279 		return -ENOENT;
280 	}
281 
282 	return 0;
283 }
284 
free_block_ctx(struct lwm2m_block_context * ctx)285 static void free_block_ctx(struct lwm2m_block_context *ctx)
286 {
287 	if (ctx == NULL) {
288 		return;
289 	}
290 
291 	ctx->tkl = 0U;
292 }
293 
294 /* observer functions */
295 
update_attrs(void * ref,struct notification_attrs * out)296 static int update_attrs(void *ref, struct notification_attrs *out)
297 {
298 	int i;
299 
300 	for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
301 		if (ref != write_attr_pool[i].ref) {
302 			continue;
303 		}
304 
305 		switch (write_attr_pool[i].type) {
306 		case LWM2M_ATTR_PMIN:
307 			out->pmin = write_attr_pool[i].int_val;
308 			break;
309 		case LWM2M_ATTR_PMAX:
310 			out->pmax = write_attr_pool[i].int_val;
311 			break;
312 		case LWM2M_ATTR_LT:
313 			out->lt = write_attr_pool[i].float_val;
314 			break;
315 		case LWM2M_ATTR_GT:
316 			out->gt = write_attr_pool[i].float_val;
317 			break;
318 		case LWM2M_ATTR_STEP:
319 			out->st = write_attr_pool[i].float_val;
320 			break;
321 		default:
322 			LOG_ERR("Unrecognize attr: %d",
323 				write_attr_pool[i].type);
324 			return -EINVAL;
325 		}
326 
327 		/* mark as set */
328 		out->flags |= BIT(write_attr_pool[i].type);
329 	}
330 
331 	return 0;
332 }
333 
clear_attrs(void * ref)334 static void clear_attrs(void *ref)
335 {
336 	int i;
337 
338 	for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
339 		if (ref == write_attr_pool[i].ref) {
340 			(void)memset(&write_attr_pool[i], 0,
341 				     sizeof(write_attr_pool[i]));
342 		}
343 	}
344 }
345 
lwm2m_notify_observer(uint16_t obj_id,uint16_t obj_inst_id,uint16_t res_id)346 int lwm2m_notify_observer(uint16_t obj_id, uint16_t obj_inst_id, uint16_t res_id)
347 {
348 	struct observe_node *obs;
349 	int ret = 0;
350 	int i;
351 
352 	/* look for observers which match our resource */
353 	for (i = 0; i < sock_nfds; ++i) {
354 		SYS_SLIST_FOR_EACH_CONTAINER(&sock_ctx[i]->observer, obs, node) {
355 			if (obs->path.obj_id == obj_id &&
356 			    obs->path.obj_inst_id == obj_inst_id &&
357 			    (obs->path.level < 3 ||
358 			     obs->path.res_id == res_id)) {
359 				/* update the event time for this observer */
360 				obs->event_timestamp = k_uptime_get();
361 
362 				LOG_DBG("NOTIFY EVENT %u/%u/%u",
363 					obj_id, obj_inst_id, res_id);
364 
365 				ret++;
366 			}
367 		}
368 	}
369 
370 	return ret;
371 }
372 
lwm2m_notify_observer_path(struct lwm2m_obj_path * path)373 int lwm2m_notify_observer_path(struct lwm2m_obj_path *path)
374 {
375 	return lwm2m_notify_observer(path->obj_id, path->obj_inst_id,
376 				     path->res_id);
377 }
378 
engine_add_observer(struct lwm2m_message * msg,const uint8_t * token,uint8_t tkl,uint16_t format)379 static int engine_add_observer(struct lwm2m_message *msg,
380 			       const uint8_t *token, uint8_t tkl,
381 			       uint16_t format)
382 {
383 	struct lwm2m_engine_obj *obj = NULL;
384 	struct lwm2m_engine_obj_field *obj_field = NULL;
385 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
386 	struct observe_node *obs;
387 	struct notification_attrs attrs = {
388 		.flags = BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX),
389 	};
390 	int i, ret;
391 
392 	if (!msg || !msg->ctx) {
393 		LOG_ERR("valid lwm2m message is required");
394 		return -EINVAL;
395 	}
396 
397 	if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
398 		LOG_ERR("token(%p) and token length(%u) must be valid.",
399 			token, tkl);
400 		return -EINVAL;
401 	}
402 
403 	/* defaults from server object */
404 	attrs.pmin = lwm2m_server_get_pmin(msg->ctx->srv_obj_inst);
405 	attrs.pmax = lwm2m_server_get_pmax(msg->ctx->srv_obj_inst);
406 
407 	/* TODO: observe dup checking */
408 
409 	/* make sure this observer doesn't exist already */
410 	SYS_SLIST_FOR_EACH_CONTAINER(&msg->ctx->observer, obs, node) {
411 		/* TODO: distinguish server object */
412 		if (memcmp(&obs->path, &msg->path, sizeof(msg->path)) == 0) {
413 			/* quietly update the token information */
414 			memcpy(obs->token, token, tkl);
415 			obs->tkl = tkl;
416 
417 			LOG_DBG("OBSERVER DUPLICATE %u/%u/%u(%u) [%s]",
418 				msg->path.obj_id, msg->path.obj_inst_id,
419 				msg->path.res_id, msg->path.level,
420 				log_strdup(
421 				lwm2m_sprint_ip_addr(&msg->ctx->remote_addr)));
422 
423 			return 0;
424 		}
425 	}
426 
427 	/* check if object exists */
428 	obj = get_engine_obj(msg->path.obj_id);
429 	if (!obj) {
430 		LOG_ERR("unable to find obj: %u", msg->path.obj_id);
431 		return -ENOENT;
432 	}
433 
434 	ret = update_attrs(obj, &attrs);
435 	if (ret < 0) {
436 		return ret;
437 	}
438 
439 	/* check if object instance exists */
440 	if (msg->path.level >= 2U) {
441 		obj_inst = get_engine_obj_inst(msg->path.obj_id,
442 					       msg->path.obj_inst_id);
443 		if (!obj_inst) {
444 			LOG_ERR("unable to find obj_inst: %u/%u",
445 				msg->path.obj_id, msg->path.obj_inst_id);
446 			return -ENOENT;
447 		}
448 
449 		ret = update_attrs(obj_inst, &attrs);
450 		if (ret < 0) {
451 			return ret;
452 		}
453 	}
454 
455 	/* check if resource exists */
456 	if (msg->path.level >= 3U) {
457 		for (i = 0; i < obj_inst->resource_count; i++) {
458 			if (obj_inst->resources[i].res_id == msg->path.res_id) {
459 				break;
460 			}
461 		}
462 
463 		if (i == obj_inst->resource_count) {
464 			LOG_ERR("unable to find res_id: %u/%u/%u",
465 				msg->path.obj_id, msg->path.obj_inst_id,
466 				msg->path.res_id);
467 			return -ENOENT;
468 		}
469 
470 		/* load object field data */
471 		obj_field = lwm2m_get_engine_obj_field(obj,
472 				obj_inst->resources[i].res_id);
473 		if (!obj_field) {
474 			LOG_ERR("unable to find obj_field: %u/%u/%u",
475 				msg->path.obj_id, msg->path.obj_inst_id,
476 				msg->path.res_id);
477 			return -ENOENT;
478 		}
479 
480 		/* check for READ permission on matching resource */
481 		if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
482 			return -EPERM;
483 		}
484 
485 		ret = update_attrs(&obj_inst->resources[i], &attrs);
486 		if (ret < 0) {
487 			return ret;
488 		}
489 	}
490 
491 	/* find an unused observer index node */
492 	for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) {
493 		if (!observe_node_data[i].tkl) {
494 			break;
495 		}
496 	}
497 
498 	/* couldn't find an index */
499 	if (i == CONFIG_LWM2M_ENGINE_MAX_OBSERVER) {
500 		return -ENOMEM;
501 	}
502 
503 	/* copy the values and add it to the list */
504 	memcpy(&observe_node_data[i].path, &msg->path, sizeof(msg->path));
505 	memcpy(observe_node_data[i].token, token, tkl);
506 	observe_node_data[i].tkl = tkl;
507 	observe_node_data[i].last_timestamp = k_uptime_get();
508 	observe_node_data[i].event_timestamp =
509 			observe_node_data[i].last_timestamp;
510 	observe_node_data[i].min_period_sec = attrs.pmin;
511 	observe_node_data[i].max_period_sec = (attrs.pmax > 0) ? MAX(attrs.pmax, attrs.pmin)
512 							       : attrs.pmax;
513 	observe_node_data[i].format = format;
514 	observe_node_data[i].counter = OBSERVE_COUNTER_START;
515 	sys_slist_append(&msg->ctx->observer,
516 			 &observe_node_data[i].node);
517 
518 	LOG_DBG("OBSERVER ADDED %u/%u/%u(%u) token:'%s' addr:%s",
519 		msg->path.obj_id, msg->path.obj_inst_id,
520 		msg->path.res_id, msg->path.level,
521 		log_strdup(sprint_token(token, tkl)),
522 		log_strdup(lwm2m_sprint_ip_addr(&msg->ctx->remote_addr)));
523 
524 	return 0;
525 }
526 
engine_remove_observer(struct lwm2m_ctx * ctx,const uint8_t * token,uint8_t tkl)527 static int engine_remove_observer(struct lwm2m_ctx *ctx, const uint8_t *token, uint8_t tkl)
528 {
529 	struct observe_node *obs, *found_obj = NULL;
530 	sys_snode_t *prev_node = NULL;
531 
532 	if (!token || (tkl == 0U || tkl > MAX_TOKEN_LEN)) {
533 		LOG_ERR("token(%p) and token length(%u) must be valid.",
534 			token, tkl);
535 		return -EINVAL;
536 	}
537 
538 	/* find the node index */
539 	SYS_SLIST_FOR_EACH_CONTAINER(&ctx->observer, obs, node) {
540 		if (memcmp(obs->token, token, tkl) == 0) {
541 			found_obj = obs;
542 			break;
543 		}
544 
545 		prev_node = &obs->node;
546 	}
547 
548 	if (!found_obj) {
549 		return -ENOENT;
550 	}
551 
552 	sys_slist_remove(&ctx->observer, prev_node, &found_obj->node);
553 	(void)memset(found_obj, 0, sizeof(*found_obj));
554 
555 	LOG_DBG("observer '%s' removed", log_strdup(sprint_token(token, tkl)));
556 
557 	return 0;
558 }
559 
560 #if defined(CONFIG_LOG)
lwm2m_path_log_strdup(char * buf,struct lwm2m_obj_path * path)561 char *lwm2m_path_log_strdup(char *buf, struct lwm2m_obj_path *path)
562 {
563 	size_t cur = sprintf(buf, "%u", path->obj_id);
564 
565 	if (path->level > 1) {
566 		cur += sprintf(buf + cur, "/%u", path->obj_inst_id);
567 	}
568 	if (path->level > 2) {
569 		cur += sprintf(buf + cur, "/%u", path->res_id);
570 	}
571 	if (path->level > 3) {
572 		cur += sprintf(buf + cur, "/%u", path->res_inst_id);
573 	}
574 
575 	return log_strdup(buf);
576 }
577 #endif /* CONFIG_LOG */
578 
579 #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH)
engine_remove_observer_by_path(struct lwm2m_ctx * ctx,struct lwm2m_obj_path * path)580 static int engine_remove_observer_by_path(struct lwm2m_ctx *ctx,
581 					  struct lwm2m_obj_path *path)
582 {
583 	char buf[LWM2M_MAX_PATH_STR_LEN];
584 	struct observe_node *obs, *found_obj = NULL;
585 	sys_snode_t *prev_node = NULL;
586 
587 	/* find the node index */
588 	SYS_SLIST_FOR_EACH_CONTAINER(&ctx->observer, obs, node) {
589 		if (memcmp(path, &obs->path, sizeof(*path)) == 0) {
590 			found_obj = obs;
591 			break;
592 		}
593 
594 		prev_node = &obs->node;
595 	}
596 
597 	if (!found_obj) {
598 		return -ENOENT;
599 	}
600 
601 	LOG_INF("Removing observer for path %s",
602 		lwm2m_path_log_strdup(buf, path));
603 	sys_slist_remove(&ctx->observer, prev_node, &found_obj->node);
604 	(void)memset(found_obj, 0, sizeof(*found_obj));
605 
606 	return 0;
607 }
608 #endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */
609 
engine_remove_observer_by_id(uint16_t obj_id,int32_t obj_inst_id)610 static void engine_remove_observer_by_id(uint16_t obj_id, int32_t obj_inst_id)
611 {
612 	struct observe_node *obs, *tmp;
613 	sys_snode_t *prev_node = NULL;
614 	int i;
615 
616 	/* remove observer instances accordingly */
617 	for (i = 0; i < sock_nfds; ++i) {
618 		SYS_SLIST_FOR_EACH_CONTAINER_SAFE(
619 			&sock_ctx[i]->observer, obs, tmp, node) {
620 			if (!(obj_id == obs->path.obj_id &&
621 			      obj_inst_id == obs->path.obj_inst_id)) {
622 				prev_node = &obs->node;
623 				continue;
624 			}
625 
626 			sys_slist_remove(&sock_ctx[i]->observer, prev_node, &obs->node);
627 			(void)memset(obs, 0, sizeof(*obs));
628 		}
629 	}
630 }
631 
632 /* engine object */
633 
lwm2m_register_obj(struct lwm2m_engine_obj * obj)634 void lwm2m_register_obj(struct lwm2m_engine_obj *obj)
635 {
636 	sys_slist_append(&engine_obj_list, &obj->node);
637 }
638 
lwm2m_unregister_obj(struct lwm2m_engine_obj * obj)639 void lwm2m_unregister_obj(struct lwm2m_engine_obj *obj)
640 {
641 	engine_remove_observer_by_id(obj->obj_id, -1);
642 	sys_slist_find_and_remove(&engine_obj_list, &obj->node);
643 }
644 
get_engine_obj(int obj_id)645 static struct lwm2m_engine_obj *get_engine_obj(int obj_id)
646 {
647 	struct lwm2m_engine_obj *obj;
648 
649 	SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
650 		if (obj->obj_id == obj_id) {
651 			return obj;
652 		}
653 	}
654 
655 	return NULL;
656 }
657 
658 struct lwm2m_engine_obj_field *
lwm2m_get_engine_obj_field(struct lwm2m_engine_obj * obj,int res_id)659 lwm2m_get_engine_obj_field(struct lwm2m_engine_obj *obj, int res_id)
660 {
661 	int i;
662 
663 	if (obj && obj->fields && obj->field_count > 0) {
664 		for (i = 0; i < obj->field_count; i++) {
665 			if (obj->fields[i].res_id == res_id) {
666 				return &obj->fields[i];
667 			}
668 		}
669 	}
670 
671 	return NULL;
672 }
673 
674 /* engine object instance */
675 
engine_register_obj_inst(struct lwm2m_engine_obj_inst * obj_inst)676 static void engine_register_obj_inst(struct lwm2m_engine_obj_inst *obj_inst)
677 {
678 	sys_slist_append(&engine_obj_inst_list, &obj_inst->node);
679 }
680 
engine_unregister_obj_inst(struct lwm2m_engine_obj_inst * obj_inst)681 static void engine_unregister_obj_inst(struct lwm2m_engine_obj_inst *obj_inst)
682 {
683 	engine_remove_observer_by_id(
684 			obj_inst->obj->obj_id, obj_inst->obj_inst_id);
685 	sys_slist_find_and_remove(&engine_obj_inst_list, &obj_inst->node);
686 }
687 
get_engine_obj_inst(int obj_id,int obj_inst_id)688 static struct lwm2m_engine_obj_inst *get_engine_obj_inst(int obj_id,
689 							 int obj_inst_id)
690 {
691 	struct lwm2m_engine_obj_inst *obj_inst;
692 
693 	SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, obj_inst,
694 				     node) {
695 		if (obj_inst->obj->obj_id == obj_id &&
696 		    obj_inst->obj_inst_id == obj_inst_id) {
697 			return obj_inst;
698 		}
699 	}
700 
701 	return NULL;
702 }
703 
704 static struct lwm2m_engine_obj_inst *
next_engine_obj_inst(int obj_id,int obj_inst_id)705 next_engine_obj_inst(int obj_id, int obj_inst_id)
706 {
707 	struct lwm2m_engine_obj_inst *obj_inst, *next = NULL;
708 
709 	SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list, obj_inst,
710 				     node) {
711 		if (obj_inst->obj->obj_id == obj_id &&
712 		    obj_inst->obj_inst_id > obj_inst_id &&
713 		    (!next || next->obj_inst_id > obj_inst->obj_inst_id)) {
714 			next = obj_inst;
715 		}
716 	}
717 
718 	return next;
719 }
720 
lwm2m_create_obj_inst(uint16_t obj_id,uint16_t obj_inst_id,struct lwm2m_engine_obj_inst ** obj_inst)721 int lwm2m_create_obj_inst(uint16_t obj_id, uint16_t obj_inst_id,
722 			  struct lwm2m_engine_obj_inst **obj_inst)
723 {
724 	struct lwm2m_engine_obj *obj;
725 	int ret;
726 
727 	*obj_inst = NULL;
728 	obj = get_engine_obj(obj_id);
729 	if (!obj) {
730 		LOG_ERR("unable to find obj: %u", obj_id);
731 		return -ENOENT;
732 	}
733 
734 	if (!obj->create_cb) {
735 		LOG_ERR("obj %u has no create_cb", obj_id);
736 		return -EINVAL;
737 	}
738 
739 	if (obj->instance_count + 1 > obj->max_instance_count) {
740 		LOG_ERR("no more instances available for obj %u", obj_id);
741 		return -ENOMEM;
742 	}
743 
744 	*obj_inst = obj->create_cb(obj_inst_id);
745 	if (!*obj_inst) {
746 		LOG_ERR("unable to create obj %u instance %u",
747 			obj_id, obj_inst_id);
748 		/*
749 		 * Already checked for instance count total.
750 		 * This can only be an error if the object instance exists.
751 		 */
752 		return -EEXIST;
753 	}
754 
755 	obj->instance_count++;
756 	(*obj_inst)->obj = obj;
757 	(*obj_inst)->obj_inst_id = obj_inst_id;
758 	engine_register_obj_inst(*obj_inst);
759 
760 	if (obj->user_create_cb) {
761 		ret = obj->user_create_cb(obj_inst_id);
762 		if (ret < 0) {
763 			LOG_ERR("Error in user obj create %u/%u: %d",
764 				obj_id, obj_inst_id, ret);
765 			lwm2m_delete_obj_inst(obj_id, obj_inst_id);
766 			return ret;
767 		}
768 	}
769 
770 	return 0;
771 }
772 
lwm2m_delete_obj_inst(uint16_t obj_id,uint16_t obj_inst_id)773 int lwm2m_delete_obj_inst(uint16_t obj_id, uint16_t obj_inst_id)
774 {
775 	int i, ret = 0;
776 	struct lwm2m_engine_obj *obj;
777 	struct lwm2m_engine_obj_inst *obj_inst;
778 
779 	obj = get_engine_obj(obj_id);
780 	if (!obj) {
781 		return -ENOENT;
782 	}
783 
784 	obj_inst = get_engine_obj_inst(obj_id, obj_inst_id);
785 	if (!obj_inst) {
786 		return -ENOENT;
787 	}
788 
789 	if (obj->user_delete_cb) {
790 		ret = obj->user_delete_cb(obj_inst_id);
791 		if (ret < 0) {
792 			LOG_ERR("Error in user obj delete %u/%u: %d",
793 				obj_id, obj_inst_id, ret);
794 			/* don't return error */
795 		}
796 	}
797 
798 	engine_unregister_obj_inst(obj_inst);
799 	obj->instance_count--;
800 
801 	if (obj->delete_cb) {
802 		ret = obj->delete_cb(obj_inst_id);
803 	}
804 
805 	/* reset obj_inst and res_inst data structure */
806 	for (i = 0; i < obj_inst->resource_count; i++) {
807 		clear_attrs(&obj_inst->resources[i]);
808 		(void)memset(obj_inst->resources + i, 0,
809 			     sizeof(struct lwm2m_engine_res));
810 	}
811 
812 	clear_attrs(obj_inst);
813 	(void)memset(obj_inst, 0, sizeof(struct lwm2m_engine_obj_inst));
814 	return ret;
815 }
816 
817 /* utility functions */
818 
atou16(uint8_t * buf,uint16_t buflen,uint16_t * len)819 static uint16_t atou16(uint8_t *buf, uint16_t buflen, uint16_t *len)
820 {
821 	uint16_t val = 0U;
822 	uint16_t pos = 0U;
823 
824 	/* we should get a value first - consume all numbers */
825 	while (pos < buflen && isdigit(buf[pos])) {
826 		val = val * 10U + (buf[pos] - '0');
827 		pos++;
828 	}
829 
830 	*len = pos;
831 	return val;
832 }
833 
coap_options_to_path(struct coap_option * opt,int options_count,struct lwm2m_obj_path * path)834 static int coap_options_to_path(struct coap_option *opt, int options_count,
835 				struct lwm2m_obj_path *path)
836 {
837 	uint16_t len, *id[4] = { &path->obj_id, &path->obj_inst_id,
838 			      &path->res_id, &path->res_inst_id };
839 
840 	path->level = options_count;
841 
842 	for (int i = 0; i < options_count; i++) {
843 		*id[i] = atou16(opt[i].value, opt[i].len, &len);
844 		if (len == 0U || opt[i].len != len) {
845 			path->level = i;
846 			break;
847 		}
848 	}
849 
850 	return options_count == path->level ? 0 : -EINVAL;
851 }
852 
find_msg(struct coap_pending * pending,struct coap_reply * reply)853 static struct lwm2m_message *find_msg(struct coap_pending *pending,
854 				      struct coap_reply *reply)
855 {
856 	size_t i;
857 
858 	if (!pending && !reply) {
859 		return NULL;
860 	}
861 
862 	for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
863 		if (pending != NULL && messages[i].ctx &&
864 		    messages[i].pending == pending) {
865 			return &messages[i];
866 		}
867 
868 		if (reply != NULL && messages[i].ctx &&
869 		    messages[i].reply == reply) {
870 			return &messages[i];
871 		}
872 	}
873 
874 	return NULL;
875 }
876 
lwm2m_get_message(struct lwm2m_ctx * client_ctx)877 struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx)
878 {
879 	size_t i;
880 
881 	for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
882 		if (!messages[i].ctx) {
883 			messages[i].ctx = client_ctx;
884 			return &messages[i];
885 		}
886 	}
887 
888 	return NULL;
889 }
890 
lwm2m_reset_message(struct lwm2m_message * msg,bool release)891 void lwm2m_reset_message(struct lwm2m_message *msg, bool release)
892 {
893 	if (!msg) {
894 		return;
895 	}
896 
897 	if (msg->pending) {
898 		coap_pending_clear(msg->pending);
899 	}
900 
901 	if (msg->reply) {
902 		/* make sure we want to clear the reply */
903 		coap_reply_clear(msg->reply);
904 	}
905 
906 	if (release) {
907 		(void)memset(msg, 0, sizeof(*msg));
908 	} else {
909 		msg->message_timeout_cb = NULL;
910 		(void)memset(&msg->cpkt, 0, sizeof(msg->cpkt));
911 	}
912 }
913 
lwm2m_init_message(struct lwm2m_message * msg)914 int lwm2m_init_message(struct lwm2m_message *msg)
915 {
916 	uint8_t tokenlen = 0U;
917 	uint8_t *token = NULL;
918 	int r = 0;
919 
920 	if (!msg || !msg->ctx) {
921 		LOG_ERR("LwM2M message is invalid.");
922 		return -EINVAL;
923 	}
924 
925 	if (msg->tkl == LWM2M_MSG_TOKEN_GENERATE_NEW) {
926 		tokenlen = 8U;
927 		token = coap_next_token();
928 	} else if (msg->token && msg->tkl != 0) {
929 		tokenlen = msg->tkl;
930 		token = msg->token;
931 	}
932 
933 	r = coap_packet_init(&msg->cpkt, msg->msg_data, sizeof(msg->msg_data),
934 			     COAP_VERSION_1, msg->type, tokenlen, token,
935 			     msg->code, msg->mid);
936 	if (r < 0) {
937 		LOG_ERR("coap packet init error (err:%d)", r);
938 		goto cleanup;
939 	}
940 
941 	/* only TYPE_CON messages need pending tracking / reply handling */
942 	if (msg->type != COAP_TYPE_CON) {
943 		return 0;
944 	}
945 
946 	msg->pending = coap_pending_next_unused(
947 				msg->ctx->pendings,
948 				CONFIG_LWM2M_ENGINE_MAX_PENDING);
949 	if (!msg->pending) {
950 		LOG_ERR("Unable to find a free pending to track "
951 			"retransmissions.");
952 		r = -ENOMEM;
953 		goto cleanup;
954 	}
955 
956 	r = coap_pending_init(msg->pending, &msg->cpkt, &msg->ctx->remote_addr,
957 			      COAP_DEFAULT_MAX_RETRANSMIT);
958 	if (r < 0) {
959 		LOG_ERR("Unable to initialize a pending "
960 			"retransmission (err:%d).", r);
961 		goto cleanup;
962 	}
963 
964 	if (msg->reply_cb) {
965 		msg->reply = coap_reply_next_unused(
966 				msg->ctx->replies,
967 				CONFIG_LWM2M_ENGINE_MAX_REPLIES);
968 		if (!msg->reply) {
969 			LOG_ERR("No resources for waiting for replies.");
970 			r = -ENOMEM;
971 			goto cleanup;
972 		}
973 
974 		coap_reply_clear(msg->reply);
975 		coap_reply_init(msg->reply, &msg->cpkt);
976 		msg->reply->reply = msg->reply_cb;
977 	}
978 
979 	return 0;
980 
981 cleanup:
982 	lwm2m_reset_message(msg, true);
983 
984 	return r;
985 }
986 
lwm2m_send_message_async(struct lwm2m_message * msg)987 int lwm2m_send_message_async(struct lwm2m_message *msg)
988 {
989 	sys_slist_append(&msg->ctx->pending_sends, &msg->node);
990 	return 0;
991 }
992 
lwm2m_send_message(struct lwm2m_message * msg)993 static int lwm2m_send_message(struct lwm2m_message *msg)
994 {
995 	int rc;
996 
997 	if (!msg || !msg->ctx) {
998 		LOG_ERR("LwM2M message is invalid.");
999 		return -EINVAL;
1000 	}
1001 
1002 	if (msg->type == COAP_TYPE_CON) {
1003 		coap_pending_cycle(msg->pending);
1004 	}
1005 
1006 	rc = send(msg->ctx->sock_fd, msg->cpkt.data, msg->cpkt.offset, 0);
1007 
1008 	if (rc < 0) {
1009 		LOG_ERR("Failed to send packet, err %d", errno);
1010 		if (msg->type != COAP_TYPE_CON) {
1011 			lwm2m_reset_message(msg, true);
1012 		}
1013 
1014 		return -errno;
1015 	}
1016 
1017 	if (msg->type != COAP_TYPE_CON) {
1018 		lwm2m_reset_message(msg, true);
1019 	}
1020 
1021 	if (IS_ENABLED(CONFIG_LWM2M_RD_CLIENT_SUPPORT) &&
1022 	    IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
1023 		engine_update_tx_time();
1024 	}
1025 
1026 	return 0;
1027 }
1028 
lwm2m_send_empty_ack(struct lwm2m_ctx * client_ctx,uint16_t mid)1029 int lwm2m_send_empty_ack(struct lwm2m_ctx *client_ctx, uint16_t mid)
1030 {
1031 	struct lwm2m_message *msg;
1032 	int ret;
1033 
1034 	msg = lwm2m_get_message(client_ctx);
1035 	if (!msg) {
1036 		LOG_ERR("Unable to get a lwm2m message!");
1037 		return -ENOMEM;
1038 	}
1039 
1040 	msg->type = COAP_TYPE_ACK;
1041 	msg->code = COAP_CODE_EMPTY;
1042 	msg->mid = mid;
1043 
1044 	ret = lwm2m_init_message(msg);
1045 	if (ret) {
1046 		goto cleanup;
1047 	}
1048 
1049 	lwm2m_send_message_async(msg);
1050 
1051 	return 0;
1052 
1053 cleanup:
1054 	lwm2m_reset_message(msg, true);
1055 	return ret;
1056 }
1057 
lwm2m_acknowledge(struct lwm2m_ctx * client_ctx)1058 void lwm2m_acknowledge(struct lwm2m_ctx *client_ctx)
1059 {
1060 	struct lwm2m_message *request;
1061 
1062 	if (client_ctx == NULL || client_ctx->processed_req == NULL) {
1063 		return;
1064 	}
1065 
1066 	request = (struct lwm2m_message *)client_ctx->processed_req;
1067 
1068 	if (request->acknowledged) {
1069 		return;
1070 	}
1071 
1072 	if (lwm2m_send_empty_ack(client_ctx, request->mid) < 0) {
1073 		return;
1074 	}
1075 
1076 	request->acknowledged = true;
1077 }
1078 
lwm2m_register_payload_handler(struct lwm2m_message * msg)1079 int lwm2m_register_payload_handler(struct lwm2m_message *msg)
1080 {
1081 	struct lwm2m_engine_obj *obj;
1082 	struct lwm2m_engine_obj_inst *obj_inst;
1083 	int ret;
1084 
1085 	engine_put_begin(&msg->out, NULL);
1086 
1087 	SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
1088 		/* Security obj MUST NOT be part of registration message */
1089 		if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
1090 			continue;
1091 		}
1092 
1093 		/* Only report <OBJ_ID> when no instance available or it's
1094 		 * needed to report object version.
1095 		 */
1096 		if (obj->instance_count == 0U ||
1097 		    lwm2m_engine_shall_report_obj_version(obj)) {
1098 			struct lwm2m_obj_path path = {
1099 				.obj_id = obj->obj_id,
1100 				.level = LWM2M_PATH_LEVEL_OBJECT,
1101 			};
1102 
1103 			ret = engine_put_corelink(&msg->out, &path);
1104 			if (ret < 0) {
1105 				return ret;
1106 			}
1107 
1108 			if (obj->instance_count == 0U) {
1109 				continue;
1110 			}
1111 		}
1112 
1113 		SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list,
1114 					     obj_inst, node) {
1115 			if (obj_inst->obj->obj_id == obj->obj_id) {
1116 				struct lwm2m_obj_path path = {
1117 					.obj_id = obj_inst->obj->obj_id,
1118 					.obj_inst_id = obj_inst->obj_inst_id,
1119 					.level = LWM2M_PATH_LEVEL_OBJECT_INST,
1120 				};
1121 
1122 				ret = engine_put_corelink(&msg->out, &path);
1123 				if (ret < 0) {
1124 					return ret;
1125 				}
1126 			}
1127 		}
1128 	}
1129 
1130 	return 0;
1131 }
1132 
1133 /* input / output selection */
1134 
select_writer(struct lwm2m_output_context * out,uint16_t accept)1135 static int select_writer(struct lwm2m_output_context *out, uint16_t accept)
1136 {
1137 	switch (accept) {
1138 
1139 	case LWM2M_FORMAT_APP_LINK_FORMAT:
1140 		out->writer = &link_format_writer;
1141 		break;
1142 
1143 	case LWM2M_FORMAT_PLAIN_TEXT:
1144 	case LWM2M_FORMAT_OMA_PLAIN_TEXT:
1145 		out->writer = &plain_text_writer;
1146 		break;
1147 
1148 	case LWM2M_FORMAT_OMA_TLV:
1149 	case LWM2M_FORMAT_OMA_OLD_TLV:
1150 		out->writer = &oma_tlv_writer;
1151 		break;
1152 
1153 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
1154 	case LWM2M_FORMAT_OMA_JSON:
1155 	case LWM2M_FORMAT_OMA_OLD_JSON:
1156 		out->writer = &json_writer;
1157 		break;
1158 #endif
1159 
1160 	default:
1161 		LOG_WRN("Unknown content type %u", accept);
1162 		return -ENOMSG;
1163 
1164 	}
1165 
1166 	return 0;
1167 }
1168 
select_reader(struct lwm2m_input_context * in,uint16_t format)1169 static int select_reader(struct lwm2m_input_context *in, uint16_t format)
1170 {
1171 	switch (format) {
1172 
1173 	case LWM2M_FORMAT_APP_OCTET_STREAM:
1174 	case LWM2M_FORMAT_PLAIN_TEXT:
1175 	case LWM2M_FORMAT_OMA_PLAIN_TEXT:
1176 		in->reader = &plain_text_reader;
1177 		break;
1178 
1179 	case LWM2M_FORMAT_OMA_TLV:
1180 	case LWM2M_FORMAT_OMA_OLD_TLV:
1181 		in->reader = &oma_tlv_reader;
1182 		break;
1183 
1184 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
1185 	case LWM2M_FORMAT_OMA_JSON:
1186 	case LWM2M_FORMAT_OMA_OLD_JSON:
1187 		in->reader = &json_reader;
1188 		break;
1189 #endif
1190 
1191 	default:
1192 		LOG_WRN("Unknown content type %u", format);
1193 		return -ENOMSG;
1194 	}
1195 
1196 	return 0;
1197 }
1198 
1199 /* user data setter functions */
1200 
string_to_path(char * pathstr,struct lwm2m_obj_path * path,char delim)1201 static int string_to_path(char *pathstr, struct lwm2m_obj_path *path,
1202 			  char delim)
1203 {
1204 	uint16_t value, len;
1205 	int i, tokstart = -1, toklen;
1206 	int end_index = strlen(pathstr) - 1;
1207 
1208 	(void)memset(path, 0, sizeof(*path));
1209 	for (i = 0; i <= end_index; i++) {
1210 		/* search for first numeric */
1211 		if (tokstart == -1) {
1212 			if (!isdigit((unsigned char)pathstr[i])) {
1213 				continue;
1214 			}
1215 
1216 			tokstart = i;
1217 		}
1218 
1219 		/* find delimiter char or end of string */
1220 		if (pathstr[i] == delim || i == end_index) {
1221 			toklen = i - tokstart + 1;
1222 
1223 			/* don't process delimiter char */
1224 			if (pathstr[i] == delim) {
1225 				toklen--;
1226 			}
1227 
1228 			if (toklen <= 0) {
1229 				continue;
1230 			}
1231 
1232 			value = atou16(&pathstr[tokstart], toklen, &len);
1233 			switch (path->level) {
1234 
1235 			case 0:
1236 				path->obj_id = value;
1237 				break;
1238 
1239 			case 1:
1240 				path->obj_inst_id = value;
1241 				break;
1242 
1243 			case 2:
1244 				path->res_id = value;
1245 				break;
1246 
1247 			case 3:
1248 				path->res_inst_id = value;
1249 				break;
1250 
1251 			default:
1252 				LOG_ERR("invalid level (%d)", path->level);
1253 				return -EINVAL;
1254 
1255 			}
1256 
1257 			/* increase the path level for each token found */
1258 			path->level++;
1259 			tokstart = -1;
1260 		}
1261 	}
1262 
1263 	return 0;
1264 }
1265 
path_to_objs(const struct lwm2m_obj_path * path,struct lwm2m_engine_obj_inst ** obj_inst,struct lwm2m_engine_obj_field ** obj_field,struct lwm2m_engine_res ** res,struct lwm2m_engine_res_inst ** res_inst)1266 static int path_to_objs(const struct lwm2m_obj_path *path,
1267 			struct lwm2m_engine_obj_inst **obj_inst,
1268 			struct lwm2m_engine_obj_field **obj_field,
1269 			struct lwm2m_engine_res **res,
1270 			struct lwm2m_engine_res_inst **res_inst)
1271 {
1272 	struct lwm2m_engine_obj_inst *oi;
1273 	struct lwm2m_engine_obj_field *of;
1274 	struct lwm2m_engine_res *r = NULL;
1275 	struct lwm2m_engine_res_inst *ri = NULL;
1276 	int i;
1277 
1278 	if (!path) {
1279 		return -EINVAL;
1280 	}
1281 
1282 	oi = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
1283 	if (!oi) {
1284 		LOG_ERR("obj instance %d/%d not found",
1285 			path->obj_id, path->obj_inst_id);
1286 		return -ENOENT;
1287 	}
1288 
1289 	if (!oi->resources || oi->resource_count == 0U) {
1290 		LOG_ERR("obj instance has no resources");
1291 		return -EINVAL;
1292 	}
1293 
1294 	of = lwm2m_get_engine_obj_field(oi->obj, path->res_id);
1295 	if (!of) {
1296 		LOG_ERR("obj field %d not found", path->res_id);
1297 		return -ENOENT;
1298 	}
1299 
1300 	for (i = 0; i < oi->resource_count; i++) {
1301 		if (oi->resources[i].res_id == path->res_id) {
1302 			r = &oi->resources[i];
1303 			break;
1304 		}
1305 	}
1306 
1307 	if (!r) {
1308 		LOG_ERR("resource %d not found", path->res_id);
1309 		return -ENOENT;
1310 	}
1311 
1312 	for (i = 0; i < r->res_inst_count; i++) {
1313 		if (r->res_instances[i].res_inst_id == path->res_inst_id) {
1314 			ri = &r->res_instances[i];
1315 			break;
1316 		}
1317 	}
1318 
1319 	/* specifically don't complain about missing resource instance */
1320 
1321 	if (obj_inst) {
1322 		*obj_inst = oi;
1323 	}
1324 
1325 	if (obj_field) {
1326 		*obj_field = of;
1327 	}
1328 
1329 	if (res) {
1330 		*res = r;
1331 	}
1332 
1333 	if (ri && res_inst) {
1334 		*res_inst = ri;
1335 	}
1336 
1337 	return 0;
1338 }
1339 
lwm2m_engine_get_next_attr(const void * ref,struct lwm2m_attr * prev)1340 struct lwm2m_attr *lwm2m_engine_get_next_attr(const void *ref,
1341 					      struct lwm2m_attr *prev)
1342 {
1343 	struct lwm2m_attr *iter = (prev == NULL) ? write_attr_pool : prev + 1;
1344 	struct lwm2m_attr *result = NULL;
1345 
1346 	if (!PART_OF_ARRAY(write_attr_pool, iter)) {
1347 		return NULL;
1348 	}
1349 
1350 	while (iter < &write_attr_pool[ARRAY_SIZE(write_attr_pool)]) {
1351 		if (ref == iter->ref) {
1352 			result = iter;
1353 			break;
1354 		}
1355 
1356 		++iter;
1357 	}
1358 
1359 	return result;
1360 }
1361 
lwm2m_engine_get_attr_name(const struct lwm2m_attr * attr)1362 const char *lwm2m_engine_get_attr_name(const struct lwm2m_attr *attr)
1363 {
1364 	if (attr->type >= NR_LWM2M_ATTR) {
1365 		return NULL;
1366 	}
1367 
1368 	return LWM2M_ATTR_STR[attr->type];
1369 }
1370 
lwm2m_engine_create_obj_inst(char * pathstr)1371 int lwm2m_engine_create_obj_inst(char *pathstr)
1372 {
1373 	struct lwm2m_obj_path path;
1374 	struct lwm2m_engine_obj_inst *obj_inst;
1375 	int ret = 0;
1376 
1377 	LOG_DBG("path:%s", log_strdup(pathstr));
1378 
1379 	/* translate path -> path_obj */
1380 	ret = string_to_path(pathstr, &path, '/');
1381 	if (ret < 0) {
1382 		return ret;
1383 	}
1384 
1385 	if (path.level != 2U) {
1386 		LOG_ERR("path must have 2 parts");
1387 		return -EINVAL;
1388 	}
1389 
1390 	ret = lwm2m_create_obj_inst(path.obj_id, path.obj_inst_id, &obj_inst);
1391 	if (ret < 0) {
1392 		return ret;
1393 	}
1394 
1395 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
1396 	engine_trigger_update(true);
1397 #endif
1398 
1399 	return 0;
1400 }
1401 
lwm2m_engine_delete_obj_inst(char * pathstr)1402 int lwm2m_engine_delete_obj_inst(char *pathstr)
1403 {
1404 	struct lwm2m_obj_path path;
1405 	int ret = 0;
1406 
1407 	LOG_DBG("path: %s", log_strdup(pathstr));
1408 
1409 	/* translate path -> path_obj */
1410 	ret = string_to_path(pathstr, &path, '/');
1411 	if (ret < 0) {
1412 		return ret;
1413 	}
1414 
1415 	if (path.level != 2U) {
1416 		LOG_ERR("path must have 2 parts");
1417 		return -EINVAL;
1418 	}
1419 
1420 	ret = lwm2m_delete_obj_inst(path.obj_id, path.obj_inst_id);
1421 	if (ret < 0) {
1422 		return ret;
1423 	}
1424 
1425 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
1426 	engine_trigger_update(true);
1427 #endif
1428 
1429 	return 0;
1430 }
1431 
1432 
lwm2m_engine_set_res_data(char * pathstr,void * data_ptr,uint16_t data_len,uint8_t data_flags)1433 int lwm2m_engine_set_res_data(char *pathstr, void *data_ptr, uint16_t data_len,
1434 			      uint8_t data_flags)
1435 {
1436 	struct lwm2m_obj_path path;
1437 	struct lwm2m_engine_res_inst *res_inst = NULL;
1438 	int ret = 0;
1439 
1440 	/* translate path -> path_obj */
1441 	ret = string_to_path(pathstr, &path, '/');
1442 	if (ret < 0) {
1443 		return ret;
1444 	}
1445 
1446 	if (path.level < 3) {
1447 		LOG_ERR("path must have at least 3 parts");
1448 		return -EINVAL;
1449 	}
1450 
1451 	/* look up resource obj */
1452 	ret = path_to_objs(&path, NULL, NULL, NULL, &res_inst);
1453 	if (ret < 0) {
1454 		return ret;
1455 	}
1456 
1457 	if (!res_inst) {
1458 		LOG_ERR("res instance %d not found", path.res_inst_id);
1459 		return -ENOENT;
1460 	}
1461 
1462 	/* assign data elements */
1463 	res_inst->data_ptr = data_ptr;
1464 	res_inst->data_len = data_len;
1465 	res_inst->max_data_len = data_len;
1466 	res_inst->data_flags = data_flags;
1467 
1468 	return ret;
1469 }
1470 
lwm2m_engine_set(char * pathstr,void * value,uint16_t len)1471 static int lwm2m_engine_set(char *pathstr, void *value, uint16_t len)
1472 {
1473 	struct lwm2m_obj_path path;
1474 	struct lwm2m_engine_obj_inst *obj_inst;
1475 	struct lwm2m_engine_obj_field *obj_field;
1476 	struct lwm2m_engine_res *res = NULL;
1477 	struct lwm2m_engine_res_inst *res_inst = NULL;
1478 	void *data_ptr = NULL;
1479 	size_t max_data_len = 0;
1480 	int ret = 0;
1481 	bool changed = false;
1482 
1483 	LOG_DBG("path:%s, value:%p, len:%d", log_strdup(pathstr), value, len);
1484 
1485 	/* translate path -> path_obj */
1486 	ret = string_to_path(pathstr, &path, '/');
1487 	if (ret < 0) {
1488 		return ret;
1489 	}
1490 
1491 	if (path.level < 3) {
1492 		LOG_ERR("path must have at least 3 parts");
1493 		return -EINVAL;
1494 	}
1495 
1496 	/* look up resource obj */
1497 	ret = path_to_objs(&path, &obj_inst, &obj_field, &res, &res_inst);
1498 	if (ret < 0) {
1499 		return ret;
1500 	}
1501 
1502 	if (!res_inst) {
1503 		LOG_ERR("res instance %d not found", path.res_inst_id);
1504 		return -ENOENT;
1505 	}
1506 
1507 	if (LWM2M_HAS_RES_FLAG(res_inst, LWM2M_RES_DATA_FLAG_RO)) {
1508 		LOG_ERR("res instance data pointer is read-only "
1509 			"[%u/%u/%u/%u:%u]", path.obj_id, path.obj_inst_id,
1510 			path.res_id, path.res_inst_id, path.level);
1511 		return -EACCES;
1512 	}
1513 
1514 	/* setup initial data elements */
1515 	data_ptr = res_inst->data_ptr;
1516 	max_data_len = res_inst->max_data_len;
1517 
1518 	/* allow user to override data elements via callback */
1519 	if (res->pre_write_cb) {
1520 		data_ptr = res->pre_write_cb(obj_inst->obj_inst_id,
1521 					     res->res_id, res_inst->res_inst_id,
1522 					     &max_data_len);
1523 	}
1524 
1525 	if (!data_ptr) {
1526 		LOG_ERR("res instance data pointer is NULL [%u/%u/%u/%u:%u]",
1527 			path.obj_id, path.obj_inst_id, path.res_id,
1528 			path.res_inst_id, path.level);
1529 		return -EINVAL;
1530 	}
1531 
1532 	/* check length (note: we add 1 to string length for NULL pad) */
1533 	if (len > max_data_len -
1534 		(obj_field->data_type == LWM2M_RES_TYPE_STRING ? 1 : 0)) {
1535 		LOG_ERR("length %u is too long for res instance %d data",
1536 			len, path.res_id);
1537 		return -ENOMEM;
1538 	}
1539 
1540 	if (memcmp(data_ptr, value, len) !=  0) {
1541 		changed = true;
1542 	}
1543 
1544 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
1545 	if (res->validate_cb) {
1546 		ret = res->validate_cb(obj_inst->obj_inst_id, res->res_id,
1547 				       res_inst->res_inst_id, value,
1548 				       len, false, 0);
1549 		if (ret < 0) {
1550 			return -EINVAL;
1551 		}
1552 	}
1553 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
1554 
1555 	switch (obj_field->data_type) {
1556 
1557 	case LWM2M_RES_TYPE_OPAQUE:
1558 		memcpy((uint8_t *)data_ptr, value, len);
1559 		break;
1560 
1561 	case LWM2M_RES_TYPE_STRING:
1562 		memcpy((uint8_t *)data_ptr, value, len);
1563 		((uint8_t *)data_ptr)[len] = '\0';
1564 		break;
1565 
1566 	case LWM2M_RES_TYPE_U32:
1567 	case LWM2M_RES_TYPE_TIME:
1568 		*((uint32_t *)data_ptr) = *(uint32_t *)value;
1569 		break;
1570 
1571 	case LWM2M_RES_TYPE_U16:
1572 		*((uint16_t *)data_ptr) = *(uint16_t *)value;
1573 		break;
1574 
1575 	case LWM2M_RES_TYPE_U8:
1576 		*((uint8_t *)data_ptr) = *(uint8_t *)value;
1577 		break;
1578 
1579 	case LWM2M_RES_TYPE_S64:
1580 		*((int64_t *)data_ptr) = *(int64_t *)value;
1581 		break;
1582 
1583 	case LWM2M_RES_TYPE_S32:
1584 		*((int32_t *)data_ptr) = *(int32_t *)value;
1585 		break;
1586 
1587 	case LWM2M_RES_TYPE_S16:
1588 		*((int16_t *)data_ptr) = *(int16_t *)value;
1589 		break;
1590 
1591 	case LWM2M_RES_TYPE_S8:
1592 		*((int8_t *)data_ptr) = *(int8_t *)value;
1593 		break;
1594 
1595 	case LWM2M_RES_TYPE_BOOL:
1596 		*((bool *)data_ptr) = *(bool *)value;
1597 		break;
1598 
1599 	case LWM2M_RES_TYPE_FLOAT:
1600 		((float32_value_t *)data_ptr)->val1 =
1601 				((float32_value_t *)value)->val1;
1602 		((float32_value_t *)data_ptr)->val2 =
1603 				((float32_value_t *)value)->val2;
1604 		break;
1605 
1606 	case LWM2M_RES_TYPE_OBJLNK:
1607 		*((struct lwm2m_objlnk *)data_ptr) =
1608 				*(struct lwm2m_objlnk *)value;
1609 		break;
1610 
1611 	default:
1612 		LOG_ERR("unknown obj data_type %d", obj_field->data_type);
1613 		return -EINVAL;
1614 
1615 	}
1616 
1617 	res_inst->data_len = len;
1618 
1619 	if (res->post_write_cb) {
1620 		ret = res->post_write_cb(obj_inst->obj_inst_id,
1621 					 res->res_id, res_inst->res_inst_id,
1622 					 data_ptr, len, false, 0);
1623 	}
1624 
1625 	if (changed && LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
1626 		NOTIFY_OBSERVER_PATH(&path);
1627 	}
1628 
1629 	return ret;
1630 }
1631 
lwm2m_engine_set_opaque(char * pathstr,char * data_ptr,uint16_t data_len)1632 int lwm2m_engine_set_opaque(char *pathstr, char *data_ptr, uint16_t data_len)
1633 {
1634 	return lwm2m_engine_set(pathstr, data_ptr, data_len);
1635 }
1636 
lwm2m_engine_set_string(char * pathstr,char * data_ptr)1637 int lwm2m_engine_set_string(char *pathstr, char *data_ptr)
1638 {
1639 	return lwm2m_engine_set(pathstr, data_ptr, strlen(data_ptr));
1640 }
1641 
lwm2m_engine_set_u8(char * pathstr,uint8_t value)1642 int lwm2m_engine_set_u8(char *pathstr, uint8_t value)
1643 {
1644 	return lwm2m_engine_set(pathstr, &value, 1);
1645 }
1646 
lwm2m_engine_set_u16(char * pathstr,uint16_t value)1647 int lwm2m_engine_set_u16(char *pathstr, uint16_t value)
1648 {
1649 	return lwm2m_engine_set(pathstr, &value, 2);
1650 }
1651 
lwm2m_engine_set_u32(char * pathstr,uint32_t value)1652 int lwm2m_engine_set_u32(char *pathstr, uint32_t value)
1653 {
1654 	return lwm2m_engine_set(pathstr, &value, 4);
1655 }
1656 
lwm2m_engine_set_u64(char * pathstr,uint64_t value)1657 int lwm2m_engine_set_u64(char *pathstr, uint64_t value)
1658 {
1659 	return lwm2m_engine_set(pathstr, &value, 8);
1660 }
1661 
lwm2m_engine_set_s8(char * pathstr,int8_t value)1662 int lwm2m_engine_set_s8(char *pathstr, int8_t value)
1663 {
1664 	return lwm2m_engine_set(pathstr, &value, 1);
1665 }
1666 
lwm2m_engine_set_s16(char * pathstr,int16_t value)1667 int lwm2m_engine_set_s16(char *pathstr, int16_t value)
1668 {
1669 	return lwm2m_engine_set(pathstr, &value, 2);
1670 }
1671 
lwm2m_engine_set_s32(char * pathstr,int32_t value)1672 int lwm2m_engine_set_s32(char *pathstr, int32_t value)
1673 {
1674 	return lwm2m_engine_set(pathstr, &value, 4);
1675 }
1676 
lwm2m_engine_set_s64(char * pathstr,int64_t value)1677 int lwm2m_engine_set_s64(char *pathstr, int64_t value)
1678 {
1679 	return lwm2m_engine_set(pathstr, &value, 8);
1680 }
1681 
lwm2m_engine_set_bool(char * pathstr,bool value)1682 int lwm2m_engine_set_bool(char *pathstr, bool value)
1683 {
1684 	uint8_t temp = (value != 0 ? 1 : 0);
1685 
1686 	return lwm2m_engine_set(pathstr, &temp, 1);
1687 }
1688 
lwm2m_engine_set_float32(char * pathstr,float32_value_t * value)1689 int lwm2m_engine_set_float32(char *pathstr, float32_value_t *value)
1690 {
1691 	return lwm2m_engine_set(pathstr, value, sizeof(float32_value_t));
1692 }
1693 
lwm2m_engine_set_objlnk(char * pathstr,struct lwm2m_objlnk * value)1694 int lwm2m_engine_set_objlnk(char *pathstr, struct lwm2m_objlnk *value)
1695 {
1696 	return lwm2m_engine_set(pathstr, value, sizeof(struct lwm2m_objlnk));
1697 }
1698 
1699 /* user data getter functions */
1700 
lwm2m_engine_get_res_data(char * pathstr,void ** data_ptr,uint16_t * data_len,uint8_t * data_flags)1701 int lwm2m_engine_get_res_data(char *pathstr, void **data_ptr, uint16_t *data_len,
1702 			      uint8_t *data_flags)
1703 {
1704 	struct lwm2m_obj_path path;
1705 	struct lwm2m_engine_res_inst *res_inst = NULL;
1706 	int ret = 0;
1707 
1708 	/* translate path -> path_obj */
1709 	ret = string_to_path(pathstr, &path, '/');
1710 	if (ret < 0) {
1711 		return ret;
1712 	}
1713 
1714 	if (path.level < 3) {
1715 		LOG_ERR("path must have at least 3 parts");
1716 		return -EINVAL;
1717 	}
1718 
1719 	/* look up resource obj */
1720 	ret = path_to_objs(&path, NULL, NULL, NULL, &res_inst);
1721 	if (ret < 0) {
1722 		return ret;
1723 	}
1724 
1725 	if (!res_inst) {
1726 		LOG_ERR("res instance %d not found", path.res_inst_id);
1727 		return -ENOENT;
1728 	}
1729 
1730 	*data_ptr = res_inst->data_ptr;
1731 	*data_len = res_inst->data_len;
1732 	*data_flags = res_inst->data_flags;
1733 
1734 	return 0;
1735 }
1736 
lwm2m_engine_get(char * pathstr,void * buf,uint16_t buflen)1737 static int lwm2m_engine_get(char *pathstr, void *buf, uint16_t buflen)
1738 {
1739 	int ret = 0;
1740 	struct lwm2m_obj_path path;
1741 	struct lwm2m_engine_obj_inst *obj_inst;
1742 	struct lwm2m_engine_obj_field *obj_field;
1743 	struct lwm2m_engine_res *res = NULL;
1744 	struct lwm2m_engine_res_inst *res_inst = NULL;
1745 	void *data_ptr = NULL;
1746 	size_t data_len = 0;
1747 
1748 	LOG_DBG("path:%s, buf:%p, buflen:%d", log_strdup(pathstr), buf, buflen);
1749 
1750 	/* translate path -> path_obj */
1751 	ret = string_to_path(pathstr, &path, '/');
1752 	if (ret < 0) {
1753 		return ret;
1754 	}
1755 
1756 	if (path.level < 3) {
1757 		LOG_ERR("path must have at least 3 parts");
1758 		return -EINVAL;
1759 	}
1760 
1761 	/* look up resource obj */
1762 	ret = path_to_objs(&path, &obj_inst, &obj_field, &res, &res_inst);
1763 	if (ret < 0) {
1764 		return ret;
1765 	}
1766 
1767 	if (!res_inst) {
1768 		LOG_ERR("res instance %d not found", path.res_inst_id);
1769 		return -ENOENT;
1770 	}
1771 
1772 	/* setup initial data elements */
1773 	data_ptr = res_inst->data_ptr;
1774 	data_len = res_inst->data_len;
1775 
1776 	/* allow user to override data elements via callback */
1777 	if (res->read_cb) {
1778 		data_ptr = res->read_cb(obj_inst->obj_inst_id,
1779 					res->res_id, res_inst->res_inst_id,
1780 					&data_len);
1781 	}
1782 
1783 	/* TODO: handle data_len > buflen case */
1784 
1785 	if (data_ptr && data_len > 0) {
1786 		switch (obj_field->data_type) {
1787 
1788 		case LWM2M_RES_TYPE_OPAQUE:
1789 			if (data_len > buflen) {
1790 				return -ENOMEM;
1791 			}
1792 
1793 			memcpy(buf, data_ptr, data_len);
1794 			break;
1795 
1796 		case LWM2M_RES_TYPE_STRING:
1797 			strncpy((uint8_t *)buf, (uint8_t *)data_ptr, buflen);
1798 			break;
1799 
1800 		case LWM2M_RES_TYPE_U32:
1801 		case LWM2M_RES_TYPE_TIME:
1802 			*(uint32_t *)buf = *(uint32_t *)data_ptr;
1803 			break;
1804 
1805 		case LWM2M_RES_TYPE_U16:
1806 			*(uint16_t *)buf = *(uint16_t *)data_ptr;
1807 			break;
1808 
1809 		case LWM2M_RES_TYPE_U8:
1810 			*(uint8_t *)buf = *(uint8_t *)data_ptr;
1811 			break;
1812 
1813 		case LWM2M_RES_TYPE_S64:
1814 			*(int64_t *)buf = *(int64_t *)data_ptr;
1815 			break;
1816 
1817 		case LWM2M_RES_TYPE_S32:
1818 			*(int32_t *)buf = *(int32_t *)data_ptr;
1819 			break;
1820 
1821 		case LWM2M_RES_TYPE_S16:
1822 			*(int16_t *)buf = *(int16_t *)data_ptr;
1823 			break;
1824 
1825 		case LWM2M_RES_TYPE_S8:
1826 			*(int8_t *)buf = *(int8_t *)data_ptr;
1827 			break;
1828 
1829 		case LWM2M_RES_TYPE_BOOL:
1830 			*(bool *)buf = *(bool *)data_ptr;
1831 			break;
1832 
1833 		case LWM2M_RES_TYPE_FLOAT:
1834 			((float32_value_t *)buf)->val1 =
1835 				((float32_value_t *)data_ptr)->val1;
1836 			((float32_value_t *)buf)->val2 =
1837 				((float32_value_t *)data_ptr)->val2;
1838 			break;
1839 
1840 		case LWM2M_RES_TYPE_OBJLNK:
1841 			*(struct lwm2m_objlnk *)buf =
1842 				*(struct lwm2m_objlnk *)data_ptr;
1843 			break;
1844 
1845 		default:
1846 			LOG_ERR("unknown obj data_type %d",
1847 				obj_field->data_type);
1848 			return -EINVAL;
1849 
1850 		}
1851 	}
1852 
1853 	return 0;
1854 }
1855 
lwm2m_engine_get_opaque(char * pathstr,void * buf,uint16_t buflen)1856 int lwm2m_engine_get_opaque(char *pathstr, void *buf, uint16_t buflen)
1857 {
1858 	return lwm2m_engine_get(pathstr, buf, buflen);
1859 }
1860 
lwm2m_engine_get_string(char * pathstr,void * buf,uint16_t buflen)1861 int lwm2m_engine_get_string(char *pathstr, void *buf, uint16_t buflen)
1862 {
1863 	return lwm2m_engine_get(pathstr, buf, buflen);
1864 }
1865 
lwm2m_engine_get_u8(char * pathstr,uint8_t * value)1866 int lwm2m_engine_get_u8(char *pathstr, uint8_t *value)
1867 {
1868 	return lwm2m_engine_get(pathstr, value, 1);
1869 }
1870 
lwm2m_engine_get_u16(char * pathstr,uint16_t * value)1871 int lwm2m_engine_get_u16(char *pathstr, uint16_t *value)
1872 {
1873 	return lwm2m_engine_get(pathstr, value, 2);
1874 }
1875 
lwm2m_engine_get_u32(char * pathstr,uint32_t * value)1876 int lwm2m_engine_get_u32(char *pathstr, uint32_t *value)
1877 {
1878 	return lwm2m_engine_get(pathstr, value, 4);
1879 }
1880 
lwm2m_engine_get_u64(char * pathstr,uint64_t * value)1881 int lwm2m_engine_get_u64(char *pathstr, uint64_t *value)
1882 {
1883 	return lwm2m_engine_get(pathstr, value, 8);
1884 }
1885 
lwm2m_engine_get_s8(char * pathstr,int8_t * value)1886 int lwm2m_engine_get_s8(char *pathstr, int8_t *value)
1887 {
1888 	return lwm2m_engine_get(pathstr, value, 1);
1889 }
1890 
lwm2m_engine_get_s16(char * pathstr,int16_t * value)1891 int lwm2m_engine_get_s16(char *pathstr, int16_t *value)
1892 {
1893 	return lwm2m_engine_get(pathstr, value, 2);
1894 }
1895 
lwm2m_engine_get_s32(char * pathstr,int32_t * value)1896 int lwm2m_engine_get_s32(char *pathstr, int32_t *value)
1897 {
1898 	return lwm2m_engine_get(pathstr, value, 4);
1899 }
1900 
lwm2m_engine_get_s64(char * pathstr,int64_t * value)1901 int lwm2m_engine_get_s64(char *pathstr, int64_t *value)
1902 {
1903 	return lwm2m_engine_get(pathstr, value, 8);
1904 }
1905 
lwm2m_engine_get_bool(char * pathstr,bool * value)1906 int lwm2m_engine_get_bool(char *pathstr, bool *value)
1907 {
1908 	int ret = 0;
1909 	int8_t temp = 0;
1910 
1911 	ret = lwm2m_engine_get_s8(pathstr, &temp);
1912 	if (!ret) {
1913 		*value = temp != 0;
1914 	}
1915 
1916 	return ret;
1917 }
1918 
lwm2m_engine_get_float32(char * pathstr,float32_value_t * buf)1919 int lwm2m_engine_get_float32(char *pathstr, float32_value_t *buf)
1920 {
1921 	return lwm2m_engine_get(pathstr, buf, sizeof(float32_value_t));
1922 }
1923 
lwm2m_engine_get_objlnk(char * pathstr,struct lwm2m_objlnk * buf)1924 int lwm2m_engine_get_objlnk(char *pathstr, struct lwm2m_objlnk *buf)
1925 {
1926 	return lwm2m_engine_get(pathstr, buf, sizeof(struct lwm2m_objlnk));
1927 }
1928 
lwm2m_engine_get_resource(char * pathstr,struct lwm2m_engine_res ** res)1929 int lwm2m_engine_get_resource(char *pathstr, struct lwm2m_engine_res **res)
1930 {
1931 	int ret;
1932 	struct lwm2m_obj_path path;
1933 
1934 	ret = string_to_path(pathstr, &path, '/');
1935 	if (ret < 0) {
1936 		return ret;
1937 	}
1938 
1939 	if (path.level < 3) {
1940 		LOG_ERR("path must have 3 parts");
1941 		return -EINVAL;
1942 	}
1943 
1944 	return path_to_objs(&path, NULL, NULL, res, NULL);
1945 }
1946 
lwm2m_engine_update_observer_min_period(char * pathstr,uint32_t period_s)1947 int lwm2m_engine_update_observer_min_period(char *pathstr, uint32_t period_s)
1948 {
1949 	int i, ret;
1950 	struct lwm2m_obj_path path;
1951 
1952 	ret = string_to_path(pathstr, &path, '/');
1953 	if (ret < 0) {
1954 		return ret;
1955 	}
1956 
1957 	for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) {
1958 		if (observe_node_data[i].path.level == path.level &&
1959 		    observe_node_data[i].path.obj_id == path.obj_id &&
1960 		    (path.level >= 2 ?
1961 		     observe_node_data[i].path.obj_inst_id == path.obj_inst_id : true) &&
1962 		    (path.level >= 3 ?
1963 		     observe_node_data[i].path.res_id == path.res_id : true)) {
1964 
1965 			observe_node_data[i].min_period_sec = period_s;
1966 			return 0;
1967 		}
1968 	}
1969 
1970 	return -ENOENT;
1971 }
1972 
lwm2m_engine_update_observer_max_period(char * pathstr,uint32_t period_s)1973 int lwm2m_engine_update_observer_max_period(char *pathstr, uint32_t period_s)
1974 {
1975 	int i, ret;
1976 	struct lwm2m_obj_path path;
1977 
1978 	ret = string_to_path(pathstr, &path, '/');
1979 	if (ret < 0) {
1980 		return ret;
1981 	}
1982 
1983 	for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_OBSERVER; i++) {
1984 		if (observe_node_data[i].path.level == path.level &&
1985 		    observe_node_data[i].path.obj_id == path.obj_id &&
1986 		    (path.level >= 2 ?
1987 		     observe_node_data[i].path.obj_inst_id == path.obj_inst_id : true) &&
1988 		    (path.level >= 3 ?
1989 		     observe_node_data[i].path.res_id == path.res_id : true)) {
1990 
1991 			observe_node_data[i].max_period_sec = period_s;
1992 			return 0;
1993 		}
1994 	}
1995 
1996 	return -ENOENT;
1997 }
1998 
lwm2m_engine_get_binding(char * binding)1999 void lwm2m_engine_get_binding(char *binding)
2000 {
2001 	if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
2002 		strcpy(binding, "UQ");
2003 	} else {
2004 		/* Defaults to UDP. */
2005 		strcpy(binding, "U");
2006 	}
2007 }
2008 
lwm2m_engine_create_res_inst(char * pathstr)2009 int lwm2m_engine_create_res_inst(char *pathstr)
2010 {
2011 	int ret, i;
2012 	struct lwm2m_engine_res *res = NULL;
2013 	struct lwm2m_engine_res_inst *res_inst = NULL;
2014 	struct lwm2m_obj_path path;
2015 
2016 	ret = string_to_path(pathstr, &path, '/');
2017 	if (ret < 0) {
2018 		return ret;
2019 	}
2020 
2021 	if (path.level < 4) {
2022 		LOG_ERR("path must have 4 parts");
2023 		return -EINVAL;
2024 	}
2025 
2026 	ret = path_to_objs(&path, NULL, NULL, &res, &res_inst);
2027 	if (ret < 0) {
2028 		return ret;
2029 	}
2030 
2031 	if (!res) {
2032 		LOG_ERR("resource %u not found", path.res_id);
2033 		return -ENOENT;
2034 	}
2035 
2036 	if (res_inst && res_inst->res_inst_id != RES_INSTANCE_NOT_CREATED) {
2037 		LOG_ERR("res instance %u already exists", path.res_inst_id);
2038 		return -EINVAL;
2039 	}
2040 
2041 	if (!res->res_instances || res->res_inst_count == 0) {
2042 		LOG_ERR("no available res instances");
2043 		return -ENOMEM;
2044 	}
2045 
2046 	for (i = 0; i < res->res_inst_count; i++) {
2047 		if (res->res_instances[i].res_inst_id ==
2048 		    RES_INSTANCE_NOT_CREATED) {
2049 			break;
2050 		}
2051 	}
2052 
2053 	if (i >= res->res_inst_count) {
2054 		LOG_ERR("no available res instances");
2055 		return -ENOMEM;
2056 	}
2057 
2058 	res->res_instances[i].res_inst_id = path.res_inst_id;
2059 	return 0;
2060 }
2061 
lwm2m_engine_delete_res_inst(char * pathstr)2062 int lwm2m_engine_delete_res_inst(char *pathstr)
2063 {
2064 	int ret;
2065 	struct lwm2m_engine_res_inst *res_inst = NULL;
2066 	struct lwm2m_obj_path path;
2067 
2068 	ret = string_to_path(pathstr, &path, '/');
2069 	if (ret < 0) {
2070 		return ret;
2071 	}
2072 
2073 	if (path.level < 4) {
2074 		LOG_ERR("path must have 4 parts");
2075 		return -EINVAL;
2076 	}
2077 
2078 	ret = path_to_objs(&path, NULL, NULL, NULL, &res_inst);
2079 	if (ret < 0) {
2080 		return ret;
2081 	}
2082 
2083 	if (!res_inst) {
2084 		LOG_ERR("res instance %u not found", path.res_inst_id);
2085 		return -ENOENT;
2086 	}
2087 
2088 	res_inst->data_ptr = NULL;
2089 	res_inst->max_data_len = 0U;
2090 	res_inst->data_len = 0U;
2091 	res_inst->res_inst_id = RES_INSTANCE_NOT_CREATED;
2092 
2093 	return 0;
2094 }
2095 
lwm2m_engine_register_read_callback(char * pathstr,lwm2m_engine_get_data_cb_t cb)2096 int lwm2m_engine_register_read_callback(char *pathstr,
2097 					lwm2m_engine_get_data_cb_t cb)
2098 {
2099 	int ret;
2100 	struct lwm2m_engine_res *res = NULL;
2101 
2102 	ret = lwm2m_engine_get_resource(pathstr, &res);
2103 	if (ret < 0) {
2104 		return ret;
2105 	}
2106 
2107 	res->read_cb = cb;
2108 	return 0;
2109 }
2110 
lwm2m_engine_register_pre_write_callback(char * pathstr,lwm2m_engine_get_data_cb_t cb)2111 int lwm2m_engine_register_pre_write_callback(char *pathstr,
2112 					     lwm2m_engine_get_data_cb_t cb)
2113 {
2114 	int ret;
2115 	struct lwm2m_engine_res *res = NULL;
2116 
2117 	ret = lwm2m_engine_get_resource(pathstr, &res);
2118 	if (ret < 0) {
2119 		return ret;
2120 	}
2121 
2122 	res->pre_write_cb = cb;
2123 	return 0;
2124 }
2125 
lwm2m_engine_register_validate_callback(char * pathstr,lwm2m_engine_set_data_cb_t cb)2126 int lwm2m_engine_register_validate_callback(char *pathstr,
2127 					    lwm2m_engine_set_data_cb_t cb)
2128 {
2129 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2130 	int ret;
2131 	struct lwm2m_engine_res *res = NULL;
2132 
2133 	ret = lwm2m_engine_get_resource(pathstr, &res);
2134 	if (ret < 0) {
2135 		return ret;
2136 	}
2137 
2138 	res->validate_cb = cb;
2139 	return 0;
2140 #else
2141 	ARG_UNUSED(pathstr);
2142 	ARG_UNUSED(cb);
2143 
2144 	LOG_ERR("Validation disabled. Set "
2145 		"CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 to "
2146 		"enable validation support.");
2147 	return -ENOTSUP;
2148 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
2149 }
2150 
lwm2m_engine_register_post_write_callback(char * pathstr,lwm2m_engine_set_data_cb_t cb)2151 int lwm2m_engine_register_post_write_callback(char *pathstr,
2152 					 lwm2m_engine_set_data_cb_t cb)
2153 {
2154 	int ret;
2155 	struct lwm2m_engine_res *res = NULL;
2156 
2157 	ret = lwm2m_engine_get_resource(pathstr, &res);
2158 	if (ret < 0) {
2159 		return ret;
2160 	}
2161 
2162 	res->post_write_cb = cb;
2163 	return 0;
2164 }
2165 
lwm2m_engine_register_exec_callback(char * pathstr,lwm2m_engine_execute_cb_t cb)2166 int lwm2m_engine_register_exec_callback(char *pathstr,
2167 					lwm2m_engine_execute_cb_t cb)
2168 {
2169 	int ret;
2170 	struct lwm2m_engine_res *res = NULL;
2171 
2172 	ret = lwm2m_engine_get_resource(pathstr, &res);
2173 	if (ret < 0) {
2174 		return ret;
2175 	}
2176 
2177 	res->execute_cb = cb;
2178 	return 0;
2179 }
2180 
lwm2m_engine_register_create_callback(uint16_t obj_id,lwm2m_engine_user_cb_t cb)2181 int lwm2m_engine_register_create_callback(uint16_t obj_id,
2182 					  lwm2m_engine_user_cb_t cb)
2183 {
2184 	struct lwm2m_engine_obj *obj = NULL;
2185 
2186 	obj = get_engine_obj(obj_id);
2187 	if (!obj) {
2188 		LOG_ERR("unable to find obj: %u", obj_id);
2189 		return -ENOENT;
2190 	}
2191 
2192 	obj->user_create_cb = cb;
2193 	return 0;
2194 }
2195 
lwm2m_engine_register_delete_callback(uint16_t obj_id,lwm2m_engine_user_cb_t cb)2196 int lwm2m_engine_register_delete_callback(uint16_t obj_id,
2197 					  lwm2m_engine_user_cb_t cb)
2198 {
2199 	struct lwm2m_engine_obj *obj = NULL;
2200 
2201 	obj = get_engine_obj(obj_id);
2202 	if (!obj) {
2203 		LOG_ERR("unable to find obj: %u", obj_id);
2204 		return -ENOENT;
2205 	}
2206 
2207 	obj->user_delete_cb = cb;
2208 	return 0;
2209 }
2210 
2211 /* generic data handlers */
2212 
lwm2m_read_handler(struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res,struct lwm2m_engine_obj_field * obj_field,struct lwm2m_message * msg)2213 static int lwm2m_read_handler(struct lwm2m_engine_obj_inst *obj_inst,
2214 			      struct lwm2m_engine_res *res,
2215 			      struct lwm2m_engine_obj_field *obj_field,
2216 			      struct lwm2m_message *msg)
2217 {
2218 	int i, loop_max = 1, found_values = 0;
2219 	uint16_t res_inst_id_tmp = 0U;
2220 	void *data_ptr = NULL;
2221 	size_t data_len = 0;
2222 
2223 	if (!obj_inst || !res || !obj_field || !msg) {
2224 		return -EINVAL;
2225 	}
2226 
2227 	loop_max = res->res_inst_count;
2228 	if (res->multi_res_inst) {
2229 		/* search for valid resource instances */
2230 		for (i = 0; i < loop_max; i++) {
2231 			if (res->res_instances[i].res_inst_id !=
2232 			    RES_INSTANCE_NOT_CREATED) {
2233 				found_values = 1;
2234 				break;
2235 			}
2236 		}
2237 
2238 		if (!found_values) {
2239 			return -ENOENT;
2240 		}
2241 
2242 		engine_put_begin_ri(&msg->out, &msg->path);
2243 		res_inst_id_tmp = msg->path.res_inst_id;
2244 	}
2245 
2246 	for (i = 0; i < loop_max; i++) {
2247 		if (res->res_instances[i].res_inst_id ==
2248 		    RES_INSTANCE_NOT_CREATED) {
2249 			continue;
2250 		}
2251 
2252 		if (res->res_inst_count > 1) {
2253 			msg->path.res_inst_id =
2254 				res->res_instances[i].res_inst_id;
2255 		}
2256 
2257 		/* setup initial data elements */
2258 		data_ptr = res->res_instances[i].data_ptr;
2259 		data_len = res->res_instances[i].data_len;
2260 
2261 		/* allow user to override data elements via callback */
2262 		if (res->read_cb) {
2263 			data_ptr = res->read_cb(obj_inst->obj_inst_id,
2264 					res->res_id,
2265 					res->res_instances[i].res_inst_id,
2266 					&data_len);
2267 		}
2268 
2269 		if (!data_ptr || data_len == 0) {
2270 			return -ENOENT;
2271 		}
2272 
2273 		switch (obj_field->data_type) {
2274 
2275 		case LWM2M_RES_TYPE_OPAQUE:
2276 			engine_put_opaque(&msg->out, &msg->path,
2277 					  (uint8_t *)data_ptr,
2278 					  data_len);
2279 			break;
2280 
2281 		case LWM2M_RES_TYPE_STRING:
2282 			engine_put_string(&msg->out, &msg->path,
2283 					  (uint8_t *)data_ptr,
2284 					  strlen((uint8_t *)data_ptr));
2285 			break;
2286 
2287 		case LWM2M_RES_TYPE_U32:
2288 		case LWM2M_RES_TYPE_TIME:
2289 			engine_put_s64(&msg->out, &msg->path,
2290 				       (int64_t)*(uint32_t *)data_ptr);
2291 			break;
2292 
2293 		case LWM2M_RES_TYPE_U16:
2294 			engine_put_s32(&msg->out, &msg->path,
2295 				       (int32_t)*(uint16_t *)data_ptr);
2296 			break;
2297 
2298 		case LWM2M_RES_TYPE_U8:
2299 			engine_put_s16(&msg->out, &msg->path,
2300 				       (int16_t)*(uint8_t *)data_ptr);
2301 			break;
2302 
2303 		case LWM2M_RES_TYPE_S64:
2304 			engine_put_s64(&msg->out, &msg->path,
2305 				       *(int64_t *)data_ptr);
2306 			break;
2307 
2308 		case LWM2M_RES_TYPE_S32:
2309 			engine_put_s32(&msg->out, &msg->path,
2310 				       *(int32_t *)data_ptr);
2311 			break;
2312 
2313 		case LWM2M_RES_TYPE_S16:
2314 			engine_put_s16(&msg->out, &msg->path,
2315 				       *(int16_t *)data_ptr);
2316 			break;
2317 
2318 		case LWM2M_RES_TYPE_S8:
2319 			engine_put_s8(&msg->out, &msg->path,
2320 				      *(int8_t *)data_ptr);
2321 			break;
2322 
2323 		case LWM2M_RES_TYPE_BOOL:
2324 			engine_put_bool(&msg->out, &msg->path,
2325 					*(bool *)data_ptr);
2326 			break;
2327 
2328 		case LWM2M_RES_TYPE_FLOAT:
2329 			engine_put_float32fix(&msg->out, &msg->path,
2330 				(float32_value_t *)data_ptr);
2331 			break;
2332 
2333 		case LWM2M_RES_TYPE_OBJLNK:
2334 			engine_put_objlnk(&msg->out, &msg->path,
2335 					  (struct lwm2m_objlnk *)data_ptr);
2336 			break;
2337 
2338 		default:
2339 			LOG_ERR("unknown obj data_type %d",
2340 				obj_field->data_type);
2341 			return -EINVAL;
2342 
2343 		}
2344 	}
2345 
2346 	if (res->multi_res_inst) {
2347 		engine_put_end_ri(&msg->out, &msg->path);
2348 		msg->path.res_inst_id = res_inst_id_tmp;
2349 	}
2350 
2351 	return 0;
2352 }
2353 
lwm2m_engine_get_opaque_more(struct lwm2m_input_context * in,uint8_t * buf,size_t buflen,struct lwm2m_opaque_context * opaque,bool * last_block)2354 size_t lwm2m_engine_get_opaque_more(struct lwm2m_input_context *in,
2355 				    uint8_t *buf, size_t buflen,
2356 				    struct lwm2m_opaque_context *opaque,
2357 				    bool *last_block)
2358 {
2359 	uint32_t in_len = opaque->remaining;
2360 	uint16_t remaining = in->in_cpkt->max_len - in->offset;
2361 
2362 	if (in_len > buflen) {
2363 		in_len = buflen;
2364 	}
2365 
2366 	if (in_len > remaining) {
2367 		in_len = remaining;
2368 	}
2369 
2370 	opaque->remaining -= in_len;
2371 	remaining -= in_len;
2372 	if (opaque->remaining == 0U || remaining == 0) {
2373 		*last_block = true;
2374 	}
2375 
2376 	if (buf_read(buf, in_len, CPKT_BUF_READ(in->in_cpkt),
2377 		     &in->offset) < 0) {
2378 		*last_block = true;
2379 		return 0;
2380 	}
2381 
2382 	return (size_t)in_len;
2383 }
2384 
lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res,struct lwm2m_engine_res_inst * res_inst,struct lwm2m_message * msg,void * data_ptr,size_t data_len)2385 static int lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst *obj_inst,
2386 				      struct lwm2m_engine_res *res,
2387 				      struct lwm2m_engine_res_inst *res_inst,
2388 				      struct lwm2m_message *msg,
2389 				      void *data_ptr, size_t data_len)
2390 {
2391 	size_t len = 1;
2392 	bool last_pkt_block = false;
2393 	int ret = 0;
2394 	bool last_block = true;
2395 	struct lwm2m_opaque_context opaque_ctx = { 0 };
2396 	void *write_buf;
2397 	size_t write_buf_len;
2398 
2399 	if (msg->in.block_ctx != NULL) {
2400 		last_block = msg->in.block_ctx->last_block;
2401 
2402 		/* Restore the opaque context from the block context, if used.
2403 		*/
2404 		opaque_ctx = msg->in.block_ctx->opaque;
2405 	}
2406 
2407 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2408 	/* In case validation callback is present, write data to the temporary
2409 	 * buffer first, for validation. Otherwise, write to the data buffer
2410 	 * directly.
2411 	 */
2412 	if (res->validate_cb) {
2413 		write_buf = msg->ctx->validate_buf;
2414 		write_buf_len = sizeof(msg->ctx->validate_buf);
2415 	} else
2416 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
2417 	{
2418 		write_buf = data_ptr;
2419 		write_buf_len = data_len;
2420 	}
2421 
2422 	while (!last_pkt_block && len > 0) {
2423 		len = engine_get_opaque(&msg->in, write_buf,
2424 					MIN(data_len, write_buf_len),
2425 					&opaque_ctx, &last_pkt_block);
2426 		if (len == 0) {
2427 			/* ignore empty content and continue */
2428 			return 0;
2429 		}
2430 
2431 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2432 		if (res->validate_cb) {
2433 			ret = res->validate_cb(
2434 				obj_inst->obj_inst_id, res->res_id,
2435 				res_inst->res_inst_id, write_buf, len,
2436 				last_pkt_block && last_block, opaque_ctx.len);
2437 			if (ret < 0) {
2438 				/* -EEXIST will generate Bad Request LWM2M response. */
2439 				return -EEXIST;
2440 			}
2441 
2442 			memcpy(data_ptr, write_buf, len);
2443 		}
2444 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
2445 
2446 		if (res->post_write_cb) {
2447 			ret = res->post_write_cb(
2448 				obj_inst->obj_inst_id, res->res_id,
2449 				res_inst->res_inst_id, data_ptr, len,
2450 				last_pkt_block && last_block, opaque_ctx.len);
2451 			if (ret < 0) {
2452 				return ret;
2453 			}
2454 		}
2455 	}
2456 
2457 	if (msg->in.block_ctx != NULL) {
2458 		msg->in.block_ctx->opaque = opaque_ctx;
2459 	}
2460 
2461 	return opaque_ctx.len;
2462 }
2463 
2464 /* This function is exposed for the content format writers */
lwm2m_write_handler(struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res,struct lwm2m_engine_res_inst * res_inst,struct lwm2m_engine_obj_field * obj_field,struct lwm2m_message * msg)2465 int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst,
2466 			struct lwm2m_engine_res *res,
2467 			struct lwm2m_engine_res_inst *res_inst,
2468 			struct lwm2m_engine_obj_field *obj_field,
2469 			struct lwm2m_message *msg)
2470 {
2471 	void *data_ptr = NULL;
2472 	size_t data_len = 0;
2473 	size_t len = 0;
2474 	size_t total_size = 0;
2475 	int64_t temp64 = 0;
2476 	int32_t temp32 = 0;
2477 	int ret = 0;
2478 	bool last_block = true;
2479 	void *write_buf;
2480 	size_t write_buf_len;
2481 
2482 	if (!obj_inst || !res || !res_inst || !obj_field || !msg) {
2483 		return -EINVAL;
2484 	}
2485 
2486 	if (LWM2M_HAS_RES_FLAG(res_inst, LWM2M_RES_DATA_FLAG_RO)) {
2487 		return -EACCES;
2488 	}
2489 
2490 	/* setup initial data elements */
2491 	data_ptr = res_inst->data_ptr;
2492 	data_len = res_inst->max_data_len;
2493 
2494 	/* allow user to override data elements via callback */
2495 	if (res->pre_write_cb) {
2496 		data_ptr = res->pre_write_cb(obj_inst->obj_inst_id,
2497 					     res->res_id, res_inst->res_inst_id,
2498 					     &data_len);
2499 	}
2500 
2501 	if (res->post_write_cb
2502 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2503 	    || res->validate_cb
2504 #endif
2505 	) {
2506 		if (msg->in.block_ctx != NULL) {
2507 			/* Get block_ctx for total_size (might be zero) */
2508 			total_size = msg->in.block_ctx->ctx.total_size;
2509 			LOG_DBG("BLOCK1: total:%zu current:%zu"
2510 				" last:%u",
2511 				msg->in.block_ctx->ctx.total_size,
2512 				msg->in.block_ctx->ctx.current,
2513 				msg->in.block_ctx->last_block);
2514 		}
2515 	}
2516 
2517 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2518 	/* In case validation callback is present, write data to the temporary
2519 	 * buffer first, for validation. Otherwise, write to the data buffer
2520 	 * directly.
2521 	 */
2522 	if (res->validate_cb) {
2523 		write_buf = msg->ctx->validate_buf;
2524 		write_buf_len = sizeof(msg->ctx->validate_buf);
2525 	} else
2526 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
2527 	{
2528 		write_buf = data_ptr;
2529 		write_buf_len = data_len;
2530 	}
2531 
2532 	if (data_ptr && data_len > 0) {
2533 		switch (obj_field->data_type) {
2534 
2535 		case LWM2M_RES_TYPE_OPAQUE:
2536 			ret = lwm2m_write_handler_opaque(obj_inst, res,
2537 							 res_inst, msg,
2538 							 data_ptr, data_len);
2539 			if (ret < 0) {
2540 				return ret;
2541 			}
2542 
2543 			len = ret;
2544 			break;
2545 
2546 		case LWM2M_RES_TYPE_STRING:
2547 			engine_get_string(&msg->in, write_buf, write_buf_len);
2548 			len = strlen((char *)write_buf);
2549 			break;
2550 
2551 		case LWM2M_RES_TYPE_U32:
2552 		case LWM2M_RES_TYPE_TIME:
2553 			engine_get_s64(&msg->in, &temp64);
2554 			*(uint32_t *)write_buf = temp64;
2555 			len = 4;
2556 			break;
2557 
2558 		case LWM2M_RES_TYPE_U16:
2559 			engine_get_s32(&msg->in, &temp32);
2560 			*(uint16_t *)write_buf = temp32;
2561 			len = 2;
2562 			break;
2563 
2564 		case LWM2M_RES_TYPE_U8:
2565 			engine_get_s32(&msg->in, &temp32);
2566 			*(uint8_t *)write_buf = temp32;
2567 			len = 1;
2568 			break;
2569 
2570 		case LWM2M_RES_TYPE_S64:
2571 			engine_get_s64(&msg->in, (int64_t *)write_buf);
2572 			len = 8;
2573 			break;
2574 
2575 		case LWM2M_RES_TYPE_S32:
2576 			engine_get_s32(&msg->in, (int32_t *)write_buf);
2577 			len = 4;
2578 			break;
2579 
2580 		case LWM2M_RES_TYPE_S16:
2581 			engine_get_s32(&msg->in, &temp32);
2582 			*(int16_t *)write_buf = temp32;
2583 			len = 2;
2584 			break;
2585 
2586 		case LWM2M_RES_TYPE_S8:
2587 			engine_get_s32(&msg->in, &temp32);
2588 			*(int8_t *)write_buf = temp32;
2589 			len = 1;
2590 			break;
2591 
2592 		case LWM2M_RES_TYPE_BOOL:
2593 			engine_get_bool(&msg->in, (bool *)write_buf);
2594 			len = 1;
2595 			break;
2596 
2597 		case LWM2M_RES_TYPE_FLOAT:
2598 			engine_get_float32fix(&msg->in,
2599 					      (float32_value_t *)write_buf);
2600 			len = sizeof(float32_value_t);
2601 			break;
2602 
2603 		case LWM2M_RES_TYPE_OBJLNK:
2604 			engine_get_objlnk(&msg->in,
2605 					  (struct lwm2m_objlnk *)write_buf);
2606 			len = sizeof(struct lwm2m_objlnk);
2607 			break;
2608 
2609 		default:
2610 			LOG_ERR("unknown obj data_type %d",
2611 				obj_field->data_type);
2612 			return -EINVAL;
2613 
2614 		}
2615 	} else {
2616 		return -ENOENT;
2617 	}
2618 
2619 	if (obj_field->data_type != LWM2M_RES_TYPE_OPAQUE) {
2620 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
2621 		if (res->validate_cb) {
2622 			ret = res->validate_cb(
2623 				obj_inst->obj_inst_id, res->res_id,
2624 				res_inst->res_inst_id, write_buf, len,
2625 				last_block, total_size);
2626 			if (ret < 0) {
2627 				/* -EEXIST will generate Bad Request LWM2M response. */
2628 				return -EEXIST;
2629 			}
2630 
2631 			if (len > data_len) {
2632 				LOG_ERR("Received data won't fit into provided "
2633 					"bufffer");
2634 				return -ENOMEM;
2635 			}
2636 
2637 			if (obj_field->data_type == LWM2M_RES_TYPE_STRING) {
2638 				strncpy(data_ptr, write_buf, data_len);
2639 			} else {
2640 				memcpy(data_ptr, write_buf, len);
2641 			}
2642 		}
2643 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
2644 
2645 		if (res->post_write_cb) {
2646 			ret = res->post_write_cb(
2647 				obj_inst->obj_inst_id, res->res_id,
2648 				res_inst->res_inst_id, data_ptr, len,
2649 				last_block, total_size);
2650 		}
2651 	}
2652 
2653 	res_inst->data_len = len;
2654 
2655 	if (LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
2656 		NOTIFY_OBSERVER_PATH(&msg->path);
2657 	}
2658 
2659 	return ret;
2660 }
2661 
lwm2m_write_attr_handler(struct lwm2m_engine_obj * obj,struct lwm2m_message * msg)2662 static int lwm2m_write_attr_handler(struct lwm2m_engine_obj *obj,
2663 				    struct lwm2m_message *msg)
2664 {
2665 	bool update_observe_node = false;
2666 	char opt_buf[COAP_OPTION_BUF_LEN];
2667 	int nr_opt, i, ret = 0;
2668 	struct coap_option options[NR_LWM2M_ATTR];
2669 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
2670 	struct lwm2m_engine_res *res = NULL;
2671 	struct lwm2m_attr *attr;
2672 	struct notification_attrs nattrs = { 0 };
2673 	struct observe_node *obs;
2674 	uint8_t type = 0U;
2675 	void *nattr_ptrs[NR_LWM2M_ATTR] = {
2676 		&nattrs.pmin, &nattrs.pmax, &nattrs.gt, &nattrs.lt, &nattrs.st
2677 	};
2678 	void *ref;
2679 
2680 	if (!obj || !msg) {
2681 		return -EINVAL;
2682 	}
2683 
2684 	/* do not expose security obj */
2685 	if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
2686 		return -ENOENT;
2687 	}
2688 
2689 	nr_opt = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_QUERY,
2690 				   options, NR_LWM2M_ATTR);
2691 	if (nr_opt <= 0) {
2692 		LOG_ERR("No attribute found!");
2693 		/* translate as bad request */
2694 		return -EEXIST;
2695 	}
2696 
2697 	/* get lwm2m_attr slist */
2698 	if (msg->path.level == 3U) {
2699 		ret = path_to_objs(&msg->path, NULL, NULL, &res, NULL);
2700 		if (ret < 0) {
2701 			return ret;
2702 		}
2703 
2704 		ref = res;
2705 	} else if (msg->path.level == 1U) {
2706 		ref = obj;
2707 	} else if (msg->path.level == 2U) {
2708 		obj_inst = get_engine_obj_inst(msg->path.obj_id,
2709 					       msg->path.obj_inst_id);
2710 		if (!obj_inst) {
2711 			return -ENOENT;
2712 		}
2713 
2714 		ref = obj_inst;
2715 	} else {
2716 		/* bad request */
2717 		return -EEXIST;
2718 	}
2719 
2720 	/* retrieve existing attributes */
2721 	ret = update_attrs(ref, &nattrs);
2722 	if (ret < 0) {
2723 		return ret;
2724 	}
2725 
2726 	/* loop through options to parse attribute */
2727 	for (i = 0; i < nr_opt; i++) {
2728 		int limit = MIN(options[i].len, 5), plen = 0, vlen;
2729 		float32_value_t val = { 0 };
2730 		type = 0U;
2731 
2732 		/* search for '=' */
2733 		while (plen < limit && options[i].value[plen] != '=') {
2734 			plen += 1;
2735 		}
2736 
2737 		/* either length = 2(gt/lt/st) or = 4(pmin/pmax) */
2738 		if (plen != 2 && plen != 4) {
2739 			continue;
2740 		}
2741 
2742 		/* matching attribute name */
2743 		for (type = 0U; type < NR_LWM2M_ATTR; type++) {
2744 			if (LWM2M_ATTR_LEN[type] == plen &&
2745 			    !memcmp(options[i].value, LWM2M_ATTR_STR[type],
2746 				    LWM2M_ATTR_LEN[type])) {
2747 				break;
2748 			}
2749 		}
2750 
2751 		/* unrecognized attribute */
2752 		if (type == NR_LWM2M_ATTR) {
2753 			continue;
2754 		}
2755 
2756 		/* unset attribute when no value's given */
2757 		if (options[i].len == plen) {
2758 			nattrs.flags &= ~BIT(type);
2759 
2760 			(void)memset(nattr_ptrs[type], 0,
2761 				     type <= LWM2M_ATTR_PMAX ? sizeof(int32_t) :
2762 				     sizeof(float32_value_t));
2763 			continue;
2764 		}
2765 
2766 		/* gt/lt/st cannot be assigned to obj/obj_inst unless unset */
2767 		if (plen == 2 && msg->path.level <= 2U) {
2768 			return -EEXIST;
2769 		}
2770 
2771 		vlen = options[i].len - plen - 1;
2772 		memcpy(opt_buf, options[i].value + plen + 1, vlen);
2773 		opt_buf[vlen] = '\0';
2774 
2775 		/* convert value to integer or float */
2776 		if (plen == 4) {
2777 			char *end;
2778 			long int v;
2779 
2780 			/* pmin/pmax: integer (sec 5.1.2)
2781 			 * however, negative is non-sense
2782 			 */
2783 			errno = 0;
2784 			v = strtol(opt_buf, &end, 10);
2785 			if (errno || *end || v < 0) {
2786 				ret = -EINVAL;
2787 			}
2788 
2789 			val.val1 = v;
2790 		} else {
2791 			/* gt/lt/st: type float */
2792 			ret = lwm2m_atof32(opt_buf, &val);
2793 		}
2794 
2795 		if (ret < 0) {
2796 			LOG_ERR("invalid attr[%s] value",
2797 				log_strdup(LWM2M_ATTR_STR[type]));
2798 			/* bad request */
2799 			return -EEXIST;
2800 		}
2801 
2802 		if (type <= LWM2M_ATTR_PMAX) {
2803 			*(int32_t *)nattr_ptrs[type] = val.val1;
2804 		} else {
2805 			memcpy(nattr_ptrs[type], &val, sizeof(float32_value_t));
2806 		}
2807 
2808 		nattrs.flags |= BIT(type);
2809 	}
2810 
2811 	if ((nattrs.flags & (BIT(LWM2M_ATTR_PMIN) | BIT(LWM2M_ATTR_PMAX))) &&
2812 	    nattrs.pmin > nattrs.pmax) {
2813 		LOG_DBG("pmin (%d) > pmax (%d)", nattrs.pmin, nattrs.pmax);
2814 		return -EEXIST;
2815 	}
2816 
2817 	if (nattrs.flags & (BIT(LWM2M_ATTR_LT) | BIT(LWM2M_ATTR_GT))) {
2818 		if (!((nattrs.lt.val1 < nattrs.gt.val1) ||
2819 		      (nattrs.lt.val2 < nattrs.gt.val2))) {
2820 			LOG_DBG("lt > gt");
2821 			return -EEXIST;
2822 		}
2823 
2824 		if (nattrs.flags & BIT(LWM2M_ATTR_STEP)) {
2825 			int32_t st1 = nattrs.st.val1 * 2 +
2826 				    nattrs.st.val2 * 2 / 1000000;
2827 			int32_t st2 = nattrs.st.val2 * 2 % 1000000;
2828 			if (!(((nattrs.lt.val1 + st1) < nattrs.gt.val1) ||
2829 			      ((nattrs.lt.val2 + st2) < nattrs.gt.val2))) {
2830 				LOG_DBG("lt + 2*st > gt");
2831 				return -EEXIST;
2832 			}
2833 		}
2834 	}
2835 
2836 	/* find matching attributes */
2837 	for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
2838 		if (ref != write_attr_pool[i].ref) {
2839 			continue;
2840 		}
2841 
2842 		attr = write_attr_pool + i;
2843 		type = attr->type;
2844 
2845 		if (!(BIT(type) & nattrs.flags)) {
2846 			LOG_DBG("Unset attr %s",
2847 				log_strdup(LWM2M_ATTR_STR[type]));
2848 			(void)memset(attr, 0, sizeof(*attr));
2849 
2850 			if (type <= LWM2M_ATTR_PMAX) {
2851 				update_observe_node = true;
2852 			}
2853 
2854 			continue;
2855 		}
2856 
2857 		nattrs.flags &= ~BIT(type);
2858 
2859 		if (type <= LWM2M_ATTR_PMAX) {
2860 			if (attr->int_val == *(int32_t *)nattr_ptrs[type]) {
2861 				continue;
2862 			}
2863 
2864 			attr->int_val = *(int32_t *)nattr_ptrs[type];
2865 			update_observe_node = true;
2866 		} else {
2867 			if (!memcmp(&attr->float_val, nattr_ptrs[type],
2868 				    sizeof(float32_value_t))) {
2869 				continue;
2870 			}
2871 
2872 			memcpy(&attr->float_val, nattr_ptrs[type],
2873 			       sizeof(float32_value_t));
2874 		}
2875 
2876 		LOG_DBG("Update %s to %d.%06d",
2877 			log_strdup(LWM2M_ATTR_STR[type]),
2878 			attr->float_val.val1, attr->float_val.val2);
2879 	}
2880 
2881 	/* add attribute to obj/obj_inst/res */
2882 	for (type = 0U; nattrs.flags && type < NR_LWM2M_ATTR; type++) {
2883 		if (!(BIT(type) & nattrs.flags)) {
2884 			continue;
2885 		}
2886 
2887 		/* grab an entry for newly added attribute */
2888 		for (i = 0; i < CONFIG_LWM2M_NUM_ATTR; i++) {
2889 			if (!write_attr_pool[i].ref) {
2890 				break;
2891 			}
2892 		}
2893 
2894 		if (i == CONFIG_LWM2M_NUM_ATTR) {
2895 			return -ENOMEM;
2896 		}
2897 
2898 		attr = write_attr_pool + i;
2899 		attr->type = type;
2900 		attr->ref = ref;
2901 
2902 		if (type <= LWM2M_ATTR_PMAX) {
2903 			attr->int_val = *(int32_t *)nattr_ptrs[type];
2904 			update_observe_node = true;
2905 		} else {
2906 			memcpy(&attr->float_val, nattr_ptrs[type],
2907 			       sizeof(float32_value_t));
2908 		}
2909 
2910 		nattrs.flags &= ~BIT(type);
2911 		LOG_DBG("Add %s to %d.%06d", log_strdup(LWM2M_ATTR_STR[type]),
2912 			attr->float_val.val1, attr->float_val.val2);
2913 	}
2914 
2915 	/* check only pmin/pmax */
2916 	if (!update_observe_node) {
2917 		return 0;
2918 	}
2919 
2920 	/* update observe_node accordingly */
2921 	SYS_SLIST_FOR_EACH_CONTAINER(&msg->ctx->observer, obs, node) {
2922 		/* updated path is deeper than obs node, skip */
2923 		if (msg->path.level > obs->path.level) {
2924 			continue;
2925 		}
2926 
2927 		/* check obj id matched or not */
2928 		if (msg->path.obj_id != obs->path.obj_id) {
2929 			continue;
2930 		}
2931 
2932 		/* defaults from server object */
2933 		nattrs.pmin = lwm2m_server_get_pmin(msg->ctx->srv_obj_inst);
2934 		nattrs.pmax = lwm2m_server_get_pmax(msg->ctx->srv_obj_inst);
2935 
2936 		ret = update_attrs(obj, &nattrs);
2937 		if (ret < 0) {
2938 			return ret;
2939 		}
2940 
2941 		if (obs->path.level > 1) {
2942 			if (msg->path.level > 1 &&
2943 			    msg->path.obj_inst_id != obs->path.obj_inst_id) {
2944 				continue;
2945 			}
2946 
2947 			/* get obj_inst */
2948 			if (!obj_inst || obj_inst->obj_inst_id !=
2949 					 obs->path.obj_inst_id) {
2950 				obj_inst = get_engine_obj_inst(
2951 						obs->path.obj_id,
2952 						obs->path.obj_inst_id);
2953 				if (!obj_inst) {
2954 					return -ENOENT;
2955 				}
2956 			}
2957 
2958 			ret = update_attrs(obj_inst, &nattrs);
2959 			if (ret < 0) {
2960 				return ret;
2961 			}
2962 		}
2963 
2964 		if (obs->path.level > 2) {
2965 			if (msg->path.level > 2 &&
2966 			    msg->path.res_id != obs->path.res_id) {
2967 				continue;
2968 			}
2969 
2970 			if (!res || res->res_id != obs->path.res_id) {
2971 				ret = path_to_objs(&obs->path, NULL, NULL,
2972 						   &res, NULL);
2973 				if (ret < 0) {
2974 					return ret;
2975 				}
2976 			}
2977 
2978 			ret = update_attrs(res, &nattrs);
2979 			if (ret < 0) {
2980 				return ret;
2981 			}
2982 		}
2983 
2984 		LOG_DBG("%d/%d/%d(%d) updated from %d/%d to %u/%u",
2985 			obs->path.obj_id, obs->path.obj_inst_id,
2986 			obs->path.res_id, obs->path.level,
2987 			obs->min_period_sec, obs->max_period_sec,
2988 			nattrs.pmin, MAX(nattrs.pmin, nattrs.pmax));
2989 		obs->min_period_sec = (uint32_t)nattrs.pmin;
2990 		obs->max_period_sec = (uint32_t)MAX(nattrs.pmin, nattrs.pmax);
2991 		(void)memset(&nattrs, 0, sizeof(nattrs));
2992 	}
2993 
2994 	return 0;
2995 }
2996 
lwm2m_exec_handler(struct lwm2m_message * msg)2997 static int lwm2m_exec_handler(struct lwm2m_message *msg)
2998 {
2999 	struct lwm2m_engine_obj_inst *obj_inst;
3000 	struct lwm2m_engine_res *res = NULL;
3001 	int ret;
3002 	uint8_t *args;
3003 	uint16_t args_len;
3004 
3005 	if (!msg) {
3006 		return -EINVAL;
3007 	}
3008 
3009 	ret = path_to_objs(&msg->path, &obj_inst, NULL, &res, NULL);
3010 	if (ret < 0) {
3011 		return ret;
3012 	}
3013 
3014 	args = (uint8_t *)coap_packet_get_payload(msg->in.in_cpkt, &args_len);
3015 
3016 	if (res->execute_cb) {
3017 		return res->execute_cb(obj_inst->obj_inst_id, args, args_len);
3018 	}
3019 
3020 	/* TODO: something else to handle for execute? */
3021 	return -ENOENT;
3022 }
3023 
lwm2m_delete_handler(struct lwm2m_message * msg)3024 static int lwm2m_delete_handler(struct lwm2m_message *msg)
3025 {
3026 	int ret;
3027 
3028 	if (!msg) {
3029 		return -EINVAL;
3030 	}
3031 
3032 	/* Device management interface is not allowed to delete Security and
3033 	 * Device objects instances.
3034 	 */
3035 	if (msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID ||
3036 	    msg->path.obj_id == LWM2M_OBJECT_DEVICE_ID) {
3037 		return -EPERM;
3038 	}
3039 
3040 	ret = lwm2m_delete_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
3041 	if (ret < 0) {
3042 		return ret;
3043 	}
3044 
3045 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
3046 	if (!msg->ctx->bootstrap_mode) {
3047 		engine_trigger_update(true);
3048 	}
3049 #endif
3050 
3051 	return 0;
3052 }
3053 
do_read_op(struct lwm2m_message * msg,uint16_t content_format)3054 static int do_read_op(struct lwm2m_message *msg, uint16_t content_format)
3055 {
3056 	switch (content_format) {
3057 
3058 	case LWM2M_FORMAT_APP_OCTET_STREAM:
3059 	case LWM2M_FORMAT_PLAIN_TEXT:
3060 	case LWM2M_FORMAT_OMA_PLAIN_TEXT:
3061 		return do_read_op_plain_text(msg, content_format);
3062 
3063 	case LWM2M_FORMAT_OMA_TLV:
3064 	case LWM2M_FORMAT_OMA_OLD_TLV:
3065 		return do_read_op_tlv(msg, content_format);
3066 
3067 #if defined(CONFIG_LWM2M_RW_JSON_SUPPORT)
3068 	case LWM2M_FORMAT_OMA_JSON:
3069 	case LWM2M_FORMAT_OMA_OLD_JSON:
3070 		return do_read_op_json(msg, content_format);
3071 #endif
3072 
3073 	default:
3074 		LOG_ERR("Unsupported content-format: %u", content_format);
3075 		return -ENOMSG;
3076 
3077 	}
3078 }
3079 
lwm2m_perform_read_op(struct lwm2m_message * msg,uint16_t content_format)3080 int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
3081 {
3082 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
3083 	struct lwm2m_engine_res *res = NULL;
3084 	struct lwm2m_engine_obj_field *obj_field;
3085 	struct lwm2m_obj_path temp_path;
3086 	int ret = 0, index;
3087 	uint8_t num_read = 0U;
3088 
3089 	if (msg->path.level >= 2U) {
3090 		obj_inst = get_engine_obj_inst(msg->path.obj_id,
3091 					       msg->path.obj_inst_id);
3092 	} else if (msg->path.level == 1U) {
3093 		/* find first obj_inst with path's obj_id */
3094 		obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
3095 	}
3096 
3097 	if (!obj_inst) {
3098 		return -ENOENT;
3099 	}
3100 
3101 	/* set output content-format */
3102 	ret = coap_append_option_int(msg->out.out_cpkt,
3103 				     COAP_OPTION_CONTENT_FORMAT,
3104 				     content_format);
3105 	if (ret < 0) {
3106 		LOG_ERR("Error setting response content-format: %d", ret);
3107 		return ret;
3108 	}
3109 
3110 	ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
3111 	if (ret < 0) {
3112 		LOG_ERR("Error appending payload marker: %d", ret);
3113 		return ret;
3114 	}
3115 
3116 	/* store original path values so we can change them during processing */
3117 	memcpy(&temp_path, &msg->path, sizeof(temp_path));
3118 	engine_put_begin(&msg->out, &msg->path);
3119 
3120 	while (obj_inst) {
3121 		if (!obj_inst->resources || obj_inst->resource_count == 0U) {
3122 			goto move_forward;
3123 		}
3124 
3125 		/* update the obj_inst_id as we move through the instances */
3126 		msg->path.obj_inst_id = obj_inst->obj_inst_id;
3127 
3128 		if (msg->path.level <= 1U) {
3129 			/* start instance formatting */
3130 			engine_put_begin_oi(&msg->out, &msg->path);
3131 		}
3132 
3133 		for (index = 0; index < obj_inst->resource_count; index++) {
3134 			if (msg->path.level > 2 &&
3135 			    msg->path.res_id !=
3136 					obj_inst->resources[index].res_id) {
3137 				continue;
3138 			}
3139 
3140 			res = &obj_inst->resources[index];
3141 
3142 			/*
3143 			 * On an entire object instance, we need to set path's
3144 			 * res_id for lwm2m_read_handler to read this specific
3145 			 * resource.
3146 			 */
3147 			msg->path.res_id = res->res_id;
3148 			obj_field = lwm2m_get_engine_obj_field(obj_inst->obj,
3149 							       res->res_id);
3150 			if (!obj_field) {
3151 				ret = -ENOENT;
3152 			} else if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
3153 				ret = -EPERM;
3154 			} else {
3155 				/* start resource formatting */
3156 				engine_put_begin_r(&msg->out, &msg->path);
3157 
3158 				/* perform read operation on this resource */
3159 				ret = lwm2m_read_handler(obj_inst, res,
3160 							 obj_field, msg);
3161 				if (ret < 0) {
3162 					/* ignore errors unless single read */
3163 					if (msg->path.level > 2 &&
3164 					    !LWM2M_HAS_PERM(obj_field,
3165 						BIT(LWM2M_FLAG_OPTIONAL))) {
3166 						LOG_ERR("READ OP: %d", ret);
3167 					}
3168 				} else {
3169 					num_read += 1U;
3170 				}
3171 
3172 				/* end resource formatting */
3173 				engine_put_end_r(&msg->out, &msg->path);
3174 			}
3175 
3176 			/* on single read break if errors */
3177 			if (ret < 0 && msg->path.level > 2) {
3178 				break;
3179 			}
3180 
3181 			/* when reading multiple resources ignore return code */
3182 			ret = 0;
3183 		}
3184 
3185 move_forward:
3186 		if (msg->path.level <= 1U) {
3187 			/* end instance formatting */
3188 			engine_put_end_oi(&msg->out, &msg->path);
3189 		}
3190 
3191 		if (msg->path.level <= 1U) {
3192 			/* advance to the next object instance */
3193 			obj_inst = next_engine_obj_inst(msg->path.obj_id,
3194 							obj_inst->obj_inst_id);
3195 		} else {
3196 			obj_inst = NULL;
3197 		}
3198 	}
3199 
3200 	engine_put_end(&msg->out, &msg->path);
3201 
3202 	/* restore original path values */
3203 	memcpy(&msg->path, &temp_path, sizeof(temp_path));
3204 
3205 	/* did not read anything even if we should have - on single item */
3206 	if (ret == 0 && num_read == 0U && msg->path.level == 3U) {
3207 		return -ENOENT;
3208 	}
3209 
3210 	return ret;
3211 }
3212 
lwm2m_discover_handler(struct lwm2m_message * msg,bool is_bootstrap)3213 int lwm2m_discover_handler(struct lwm2m_message *msg, bool is_bootstrap)
3214 {
3215 	struct lwm2m_engine_obj *obj;
3216 	struct lwm2m_engine_obj_inst *obj_inst;
3217 	int ret;
3218 	bool reported = false;
3219 
3220 	/* Object ID is required in Device Management Discovery (5.4.2). */
3221 	if (!is_bootstrap &&
3222 	    (msg->path.level == LWM2M_PATH_LEVEL_NONE ||
3223 	     msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID)) {
3224 		return -EPERM;
3225 	}
3226 
3227 	/* Bootstrap discovery allows to specify at most Object ID. */
3228 	if (is_bootstrap && msg->path.level > LWM2M_PATH_LEVEL_OBJECT) {
3229 		return -EPERM;
3230 	}
3231 
3232 	/* set output content-format */
3233 	ret = coap_append_option_int(msg->out.out_cpkt,
3234 				     COAP_OPTION_CONTENT_FORMAT,
3235 				     LWM2M_FORMAT_APP_LINK_FORMAT);
3236 	if (ret < 0) {
3237 		LOG_ERR("Error setting response content-format: %d", ret);
3238 		return ret;
3239 	}
3240 
3241 	ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
3242 	if (ret < 0) {
3243 		return ret;
3244 	}
3245 
3246 	/*
3247 	 * Add required prefix for bootstrap discovery (5.2.7.3).
3248 	 * For device management discovery, `engine_put_begin()` adds nothing.
3249 	 */
3250 	engine_put_begin(&msg->out, &msg->path);
3251 
3252 	SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_list, obj, node) {
3253 		/* Skip unrelated objects */
3254 		if (msg->path.level > 0 && msg->path.obj_id != obj->obj_id) {
3255 			continue;
3256 		}
3257 
3258 		/* For bootstrap discover, only report object ID when no
3259 		 * instance is available or it's needed to report object
3260 		 * version.
3261 		 * For device management discovery, only report object ID with
3262 		 * attributes if object ID (alone) was provided.
3263 		 */
3264 		if ((is_bootstrap && (obj->instance_count == 0U ||
3265 				      lwm2m_engine_shall_report_obj_version(obj))) ||
3266 		    (!is_bootstrap && msg->path.level == LWM2M_PATH_LEVEL_OBJECT)) {
3267 			struct lwm2m_obj_path path = {
3268 				.obj_id = obj->obj_id,
3269 				.level = LWM2M_PATH_LEVEL_OBJECT,
3270 			};
3271 
3272 			ret = engine_put_corelink(&msg->out, &path);
3273 			if (ret < 0) {
3274 				return ret;
3275 			}
3276 
3277 			reported = true;
3278 
3279 			if (obj->instance_count == 0U) {
3280 				continue;
3281 			}
3282 		}
3283 
3284 		SYS_SLIST_FOR_EACH_CONTAINER(&engine_obj_inst_list,
3285 					     obj_inst, node) {
3286 			if (obj_inst->obj->obj_id != obj->obj_id) {
3287 				continue;
3288 			}
3289 
3290 			/* Skip unrelated object instance. */
3291 			if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT &&
3292 			    msg->path.obj_inst_id != obj_inst->obj_inst_id) {
3293 				continue;
3294 			}
3295 
3296 			/* Report object instances only if no Resource ID is
3297 			 * provided.
3298 			 */
3299 			if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT_INST) {
3300 				struct lwm2m_obj_path path = {
3301 					.obj_id = obj_inst->obj->obj_id,
3302 					.obj_inst_id = obj_inst->obj_inst_id,
3303 					.level = LWM2M_PATH_LEVEL_OBJECT_INST,
3304 				};
3305 
3306 				ret = engine_put_corelink(&msg->out, &path);
3307 				if (ret < 0) {
3308 					return ret;
3309 				}
3310 
3311 				reported = true;
3312 			}
3313 
3314 			/* Do not report resources in bootstrap discovery. */
3315 			if (is_bootstrap) {
3316 				continue;
3317 			}
3318 
3319 			for (int i = 0; i < obj_inst->resource_count; i++) {
3320 				/* Skip unrelated resources. */
3321 				if (msg->path.level == LWM2M_PATH_LEVEL_RESOURCE &&
3322 				    msg->path.res_id != obj_inst->resources[i].res_id) {
3323 					continue;
3324 				}
3325 
3326 				struct lwm2m_obj_path path = {
3327 					.obj_id = obj_inst->obj->obj_id,
3328 					.obj_inst_id = obj_inst->obj_inst_id,
3329 					.res_id = obj_inst->resources[i].res_id,
3330 					.level = LWM2M_PATH_LEVEL_RESOURCE,
3331 				};
3332 
3333 				ret = engine_put_corelink(&msg->out, &path);
3334 				if (ret < 0) {
3335 					return ret;
3336 				}
3337 
3338 				reported = true;
3339 			}
3340 		}
3341 	}
3342 
3343 	return reported ? 0 : -ENOENT;
3344 }
3345 
do_discover_op(struct lwm2m_message * msg,uint16_t content_format)3346 static int do_discover_op(struct lwm2m_message *msg, uint16_t content_format)
3347 {
3348 	switch (content_format) {
3349 	case LWM2M_FORMAT_APP_LINK_FORMAT:
3350 		return do_discover_op_link_format(
3351 				msg, msg->ctx->bootstrap_mode);
3352 
3353 	default:
3354 		LOG_ERR("Unsupported format: %u", content_format);
3355 		return -ENOMSG;
3356 	}
3357 }
3358 
lwm2m_get_or_create_engine_obj(struct lwm2m_message * msg,struct lwm2m_engine_obj_inst ** obj_inst,uint8_t * created)3359 int lwm2m_get_or_create_engine_obj(struct lwm2m_message *msg,
3360 				   struct lwm2m_engine_obj_inst **obj_inst,
3361 				   uint8_t *created)
3362 {
3363 	int ret = 0;
3364 
3365 	if (created) {
3366 		*created = 0U;
3367 	}
3368 
3369 	*obj_inst = get_engine_obj_inst(msg->path.obj_id,
3370 					msg->path.obj_inst_id);
3371 	if (!*obj_inst) {
3372 		ret = lwm2m_create_obj_inst(msg->path.obj_id,
3373 					    msg->path.obj_inst_id,
3374 					    obj_inst);
3375 		if (ret < 0) {
3376 			return ret;
3377 		}
3378 
3379 		/* set created flag to one */
3380 		if (created) {
3381 			*created = 1U;
3382 		}
3383 
3384 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT)
3385 		if (!msg->ctx->bootstrap_mode) {
3386 			engine_trigger_update(true);
3387 		}
3388 #endif
3389 	}
3390 
3391 	return ret;
3392 }
3393 
lwm2m_engine_get_obj(const struct lwm2m_obj_path * path)3394 struct lwm2m_engine_obj *lwm2m_engine_get_obj(
3395 					const struct lwm2m_obj_path *path)
3396 {
3397 	if (path->level < LWM2M_PATH_LEVEL_OBJECT) {
3398 		return NULL;
3399 	}
3400 
3401 	return get_engine_obj(path->obj_id);
3402 }
3403 
lwm2m_engine_get_obj_inst(const struct lwm2m_obj_path * path)3404 struct lwm2m_engine_obj_inst *lwm2m_engine_get_obj_inst(
3405 					const struct lwm2m_obj_path *path)
3406 {
3407 	if (path->level < LWM2M_PATH_LEVEL_OBJECT_INST) {
3408 		return NULL;
3409 	}
3410 
3411 	return get_engine_obj_inst(path->obj_id, path->obj_inst_id);
3412 }
3413 
lwm2m_engine_get_res(const struct lwm2m_obj_path * path)3414 struct lwm2m_engine_res *lwm2m_engine_get_res(
3415 					const struct lwm2m_obj_path *path)
3416 {
3417 	struct lwm2m_engine_res *res = NULL;
3418 	int ret;
3419 
3420 	if (path->level < LWM2M_PATH_LEVEL_RESOURCE) {
3421 		return NULL;
3422 	}
3423 
3424 	ret = path_to_objs(path, NULL, NULL, &res, NULL);
3425 	if (ret < 0) {
3426 		return NULL;
3427 	}
3428 
3429 	return res;
3430 }
3431 
lwm2m_engine_shall_report_obj_version(const struct lwm2m_engine_obj * obj)3432 bool lwm2m_engine_shall_report_obj_version(const struct lwm2m_engine_obj *obj)
3433 {
3434 	if (obj->is_core) {
3435 		return obj->version_major != LWM2M_PROTOCOL_VERSION_MAJOR ||
3436 		       obj->version_minor != LWM2M_PROTOCOL_VERSION_MINOR;
3437 	}
3438 
3439 	return obj->version_major != 1 || obj->version_minor != 0;
3440 }
3441 
do_write_op(struct lwm2m_message * msg,uint16_t format)3442 static int do_write_op(struct lwm2m_message *msg,
3443 		       uint16_t format)
3444 {
3445 	switch (format) {
3446 
3447 	case LWM2M_FORMAT_APP_OCTET_STREAM:
3448 	case LWM2M_FORMAT_PLAIN_TEXT:
3449 	case LWM2M_FORMAT_OMA_PLAIN_TEXT:
3450 		return do_write_op_plain_text(msg);
3451 
3452 	case LWM2M_FORMAT_OMA_TLV:
3453 	case LWM2M_FORMAT_OMA_OLD_TLV:
3454 		return do_write_op_tlv(msg);
3455 
3456 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
3457 	case LWM2M_FORMAT_OMA_JSON:
3458 	case LWM2M_FORMAT_OMA_OLD_JSON:
3459 		return do_write_op_json(msg);
3460 #endif
3461 
3462 	default:
3463 		LOG_ERR("Unsupported format: %u", format);
3464 		return -ENOMSG;
3465 
3466 	}
3467 }
3468 
3469 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
bootstrap_delete_allowed(int obj_id,int obj_inst_id)3470 static bool bootstrap_delete_allowed(int obj_id, int obj_inst_id)
3471 {
3472 	char pathstr[MAX_RESOURCE_LEN];
3473 	bool bootstrap_server;
3474 	int ret;
3475 
3476 	if (obj_id == LWM2M_OBJECT_SECURITY_ID) {
3477 		snprintk(pathstr, sizeof(pathstr), "%d/%d/1",
3478 			 LWM2M_OBJECT_SECURITY_ID, obj_inst_id);
3479 		ret = lwm2m_engine_get_bool(pathstr, &bootstrap_server);
3480 		if (ret < 0) {
3481 			return false;
3482 		}
3483 
3484 		if (bootstrap_server) {
3485 			return false;
3486 		}
3487 	}
3488 
3489 	if (obj_id == LWM2M_OBJECT_DEVICE_ID) {
3490 		return false;
3491 	}
3492 
3493 	return true;
3494 }
3495 
3496 
bootstrap_delete(struct lwm2m_message * msg)3497 static int bootstrap_delete(struct lwm2m_message *msg)
3498 {
3499 	struct lwm2m_engine_obj_inst *obj_inst, *tmp;
3500 	int ret = 0;
3501 
3502 	if (msg->path.level > 2) {
3503 		return -EPERM;
3504 	}
3505 
3506 	if (msg->path.level == 2) {
3507 		if (!bootstrap_delete_allowed(msg->path.obj_id,
3508 					      msg->path.obj_inst_id)) {
3509 			return -EPERM;
3510 		}
3511 
3512 		return lwm2m_delete_obj_inst(msg->path.obj_id,
3513 					     msg->path.obj_inst_id);
3514 	}
3515 
3516 	/* DELETE all instances of a specific object or all object instances if
3517 	 * not specified, excluding the following exceptions (according to the
3518 	 * LwM2M specification v1.0.2, ch 5.2.7.5):
3519 	 * - LwM2M Bootstrap-Server Account (Bootstrap Security object, ID 0)
3520 	 * - Device object (ID 3)
3521 	 */
3522 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&engine_obj_inst_list,
3523 					  obj_inst, tmp, node) {
3524 		if (msg->path.level == 1 &&
3525 		    obj_inst->obj->obj_id != msg->path.obj_id) {
3526 			continue;
3527 		}
3528 
3529 		if (!bootstrap_delete_allowed(obj_inst->obj->obj_id,
3530 					      obj_inst->obj_inst_id)) {
3531 			continue;
3532 		}
3533 
3534 		ret = lwm2m_delete_obj_inst(obj_inst->obj->obj_id,
3535 					    obj_inst->obj_inst_id);
3536 		if (ret < 0) {
3537 			return ret;
3538 		}
3539 	}
3540 
3541 	return ret;
3542 }
3543 #endif
3544 
handle_request(struct coap_packet * request,struct lwm2m_message * msg)3545 static int handle_request(struct coap_packet *request,
3546 			  struct lwm2m_message *msg)
3547 {
3548 	int r;
3549 	uint8_t code;
3550 	struct coap_option options[4];
3551 	struct lwm2m_engine_obj *obj = NULL;
3552 	uint8_t token[8];
3553 	uint8_t tkl = 0U;
3554 	uint16_t format = LWM2M_FORMAT_NONE, accept;
3555 	int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */
3556 	int block_opt, block_num;
3557 	struct lwm2m_block_context *block_ctx = NULL;
3558 	enum coap_block_size block_size;
3559 	uint16_t payload_len = 0U;
3560 	bool last_block = false;
3561 	bool ignore = false;
3562 	const uint8_t *payload_start;
3563 
3564 	/* set CoAP request / message */
3565 	msg->in.in_cpkt = request;
3566 	msg->out.out_cpkt = &msg->cpkt;
3567 
3568 	/* set default reader/writer */
3569 	msg->in.reader = &plain_text_reader;
3570 	msg->out.writer = &plain_text_writer;
3571 
3572 	code = coap_header_get_code(msg->in.in_cpkt);
3573 
3574 	/* setup response token */
3575 	tkl = coap_header_get_token(msg->in.in_cpkt, token);
3576 	if (tkl) {
3577 		msg->tkl = tkl;
3578 		msg->token = token;
3579 	}
3580 
3581 	/* parse the URL path into components */
3582 	r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_PATH, options,
3583 			      ARRAY_SIZE(options));
3584 	if (r < 0) {
3585 		goto error;
3586 	}
3587 
3588 	/* Treat empty URI path option as is there were no option - this will be
3589 	 * represented as a level "zero" in the path structure.
3590 	 */
3591 	if (r == 1 && options[0].len == 0) {
3592 		r = 0;
3593 	}
3594 
3595 	if (r == 0) {
3596 		/* No URI path or empty URI path option - allowed only during
3597 		 * bootstrap.
3598 		 */
3599 		switch (code & COAP_REQUEST_MASK) {
3600 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
3601 		case COAP_METHOD_DELETE:
3602 		case COAP_METHOD_GET:
3603 			if (msg->ctx->bootstrap_mode) {
3604 				break;
3605 			}
3606 
3607 			r = -EPERM;
3608 			goto error;
3609 #endif
3610 		default:
3611 			r = -EPERM;
3612 			goto error;
3613 		}
3614 	}
3615 
3616 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
3617 	/* check for bootstrap-finish */
3618 	if ((code & COAP_REQUEST_MASK) == COAP_METHOD_POST && r == 1 &&
3619 	    strncmp(options[0].value, "bs", options[0].len) == 0) {
3620 		engine_bootstrap_finish();
3621 
3622 		msg->code = COAP_RESPONSE_CODE_CHANGED;
3623 
3624 		r = lwm2m_init_message(msg);
3625 		if (r < 0) {
3626 			goto error;
3627 		}
3628 
3629 		return 0;
3630 	} else
3631 #endif
3632 	{
3633 		r = coap_options_to_path(options, r, &msg->path);
3634 		if (r < 0) {
3635 			r = -ENOENT;
3636 			goto error;
3637 		}
3638 	}
3639 
3640 	/* read Content Format / setup in.reader */
3641 	r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_CONTENT_FORMAT,
3642 			      options, 1);
3643 	if (r > 0) {
3644 		format = coap_option_value_to_int(&options[0]);
3645 		r = select_reader(&msg->in, format);
3646 		if (r < 0) {
3647 			goto error;
3648 		}
3649 	}
3650 
3651 	/* read Accept / setup out.writer */
3652 	r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_ACCEPT, options, 1);
3653 	if (r > 0) {
3654 		accept = coap_option_value_to_int(&options[0]);
3655 	} else {
3656 		LOG_DBG("No accept option given. Assume OMA TLV.");
3657 		accept = LWM2M_FORMAT_OMA_TLV;
3658 	}
3659 
3660 	r = select_writer(&msg->out, accept);
3661 	if (r < 0) {
3662 		goto error;
3663 	}
3664 
3665 	if (!(msg->ctx->bootstrap_mode && msg->path.level == 0)) {
3666 		/* find registered obj */
3667 		obj = get_engine_obj(msg->path.obj_id);
3668 		if (!obj) {
3669 			/* No matching object found - ignore request */
3670 			r = -ENOENT;
3671 			goto error;
3672 		}
3673 	}
3674 
3675 	/* set the operation */
3676 	switch (code & COAP_REQUEST_MASK) {
3677 
3678 	case COAP_METHOD_GET:
3679 		/*
3680 		 * LwM2M V1_0_1-20170704-A, table 25,
3681 		 * Discover: CoAP GET + accept=LWM2M_FORMAT_APP_LINK_FORMAT
3682 		 */
3683 		if (accept == LWM2M_FORMAT_APP_LINK_FORMAT) {
3684 			msg->operation = LWM2M_OP_DISCOVER;
3685 			accept = LWM2M_FORMAT_APP_LINK_FORMAT;
3686 		} else {
3687 			msg->operation = LWM2M_OP_READ;
3688 		}
3689 
3690 		/* check for observe */
3691 		observe = coap_get_option_int(msg->in.in_cpkt,
3692 					      COAP_OPTION_OBSERVE);
3693 		msg->code = COAP_RESPONSE_CODE_CONTENT;
3694 		break;
3695 
3696 	case COAP_METHOD_POST:
3697 		if (msg->path.level == 1U) {
3698 			/* create an object instance */
3699 			msg->operation = LWM2M_OP_CREATE;
3700 			msg->code = COAP_RESPONSE_CODE_CREATED;
3701 		} else if (msg->path.level == 2U) {
3702 			/* write values to an object instance */
3703 			msg->operation = LWM2M_OP_WRITE;
3704 			msg->code = COAP_RESPONSE_CODE_CHANGED;
3705 		} else {
3706 			msg->operation = LWM2M_OP_EXECUTE;
3707 			msg->code = COAP_RESPONSE_CODE_CHANGED;
3708 		}
3709 
3710 		break;
3711 
3712 	case COAP_METHOD_PUT:
3713 		/* write attributes if content-format is absent */
3714 		if (format == LWM2M_FORMAT_NONE) {
3715 			msg->operation = LWM2M_OP_WRITE_ATTR;
3716 		} else {
3717 			msg->operation = LWM2M_OP_WRITE;
3718 		}
3719 
3720 		msg->code = COAP_RESPONSE_CODE_CHANGED;
3721 		break;
3722 
3723 	case COAP_METHOD_DELETE:
3724 		msg->operation = LWM2M_OP_DELETE;
3725 		msg->code = COAP_RESPONSE_CODE_DELETED;
3726 		break;
3727 
3728 	default:
3729 		break;
3730 	}
3731 
3732 	/* setup incoming data */
3733 	payload_start = coap_packet_get_payload(msg->in.in_cpkt, &payload_len);
3734 	if (payload_len > 0) {
3735 		msg->in.offset = payload_start - msg->in.in_cpkt->data;
3736 	} else {
3737 		msg->in.offset = msg->in.in_cpkt->offset;
3738 	}
3739 
3740 	/* Check for block transfer */
3741 
3742 	block_opt = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1);
3743 	if (block_opt > 0) {
3744 		last_block = !GET_MORE(block_opt);
3745 
3746 		/* RFC7252: 4.6. Message Size */
3747 		block_size = GET_BLOCK_SIZE(block_opt);
3748 		if (!last_block &&
3749 		    coap_block_size_to_bytes(block_size) > payload_len) {
3750 			LOG_DBG("Trailing payload is discarded!");
3751 			r = -EFBIG;
3752 			goto error;
3753 		}
3754 
3755 		block_num = GET_BLOCK_NUM(block_opt);
3756 
3757 		/* Try to retrieve existing block context. If one not exists,
3758 		 * and we've received first block, allocate new context.
3759 		 */
3760 		r = get_block_ctx(token, tkl, &block_ctx);
3761 		if (r < 0 && block_num == 0) {
3762 			r = init_block_ctx(token, tkl, &block_ctx);
3763 		}
3764 
3765 		if (r < 0) {
3766 			LOG_ERR("Cannot find block context");
3767 			goto error;
3768 		}
3769 
3770 		msg->in.block_ctx = block_ctx;
3771 
3772 		if (block_num < block_ctx->expected) {
3773 			LOG_WRN("Block already handled %d, expected %d",
3774 				block_num, block_ctx->expected);
3775 			ignore = true;
3776 		} else if (block_num > block_ctx->expected) {
3777 			LOG_WRN("Block out of order %d, expected %d",
3778 				block_num, block_ctx->expected);
3779 			r = -EFAULT;
3780 			goto error;
3781 		} else {
3782 			r = coap_update_from_block(msg->in.in_cpkt, &block_ctx->ctx);
3783 			if (r < 0) {
3784 				LOG_ERR("Error from block update: %d", r);
3785 				goto error;
3786 			}
3787 
3788 			block_ctx->last_block = last_block;
3789 
3790 			/* Initial block sent by the server might be larger than
3791 			 * our block size therefore it is needed to take this
3792 			 * into account when calculating next expected block
3793 			 * number.
3794 			 */
3795 			block_ctx->expected += GET_BLOCK_SIZE(block_opt) -
3796 					       block_ctx->ctx.block_size + 1;
3797 		}
3798 
3799 		/* Handle blockwise 1 (Part 1): Set response code */
3800 		if (!last_block) {
3801 			msg->code = COAP_RESPONSE_CODE_CONTINUE;
3802 		}
3803 	}
3804 
3805 	/* render CoAP packet header */
3806 	r = lwm2m_init_message(msg);
3807 	if (r < 0) {
3808 		goto error;
3809 	}
3810 
3811 	if (!ignore) {
3812 
3813 		switch (msg->operation) {
3814 
3815 		case LWM2M_OP_READ:
3816 			if (observe == 0) {
3817 				/* add new observer */
3818 				if (msg->token) {
3819 					r = coap_append_option_int(
3820 						msg->out.out_cpkt,
3821 						COAP_OPTION_OBSERVE,
3822 						OBSERVE_COUNTER_START);
3823 					if (r < 0) {
3824 						LOG_ERR("OBSERVE option error: %d", r);
3825 						goto error;
3826 					}
3827 
3828 					r = engine_add_observer(msg, token, tkl,
3829 								accept);
3830 					if (r < 0) {
3831 						LOG_ERR("add OBSERVE error: %d", r);
3832 						goto error;
3833 					}
3834 				} else {
3835 					LOG_ERR("OBSERVE request missing token");
3836 					r = -EINVAL;
3837 					goto error;
3838 				}
3839 			} else if (observe == 1) {
3840 				/* remove observer */
3841 				r = engine_remove_observer(msg->ctx, token, tkl);
3842 				if (r < 0) {
3843 #if defined(CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH)
3844 					r = engine_remove_observer_by_path(msg->ctx,
3845 									   &msg->path);
3846 					if (r < 0)
3847 #endif /* CONFIG_LWM2M_CANCEL_OBSERVE_BY_PATH */
3848 					{
3849 						LOG_ERR("remove observe error: %d", r);
3850 					}
3851 				}
3852 			}
3853 
3854 			r = do_read_op(msg, accept);
3855 			break;
3856 
3857 		case LWM2M_OP_DISCOVER:
3858 			r = do_discover_op(msg, accept);
3859 			break;
3860 
3861 		case LWM2M_OP_WRITE:
3862 		case LWM2M_OP_CREATE:
3863 			r = do_write_op(msg, format);
3864 			break;
3865 
3866 		case LWM2M_OP_WRITE_ATTR:
3867 			r = lwm2m_write_attr_handler(obj, msg);
3868 			break;
3869 
3870 		case LWM2M_OP_EXECUTE:
3871 			r = lwm2m_exec_handler(msg);
3872 			break;
3873 
3874 		case LWM2M_OP_DELETE:
3875 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
3876 			if (msg->ctx->bootstrap_mode) {
3877 				r = bootstrap_delete(msg);
3878 				break;
3879 			}
3880 #endif
3881 			r = lwm2m_delete_handler(msg);
3882 			break;
3883 
3884 		default:
3885 			LOG_ERR("Unknown operation: %u", msg->operation);
3886 			r = -EINVAL;
3887 		}
3888 
3889 		if (r < 0) {
3890 			goto error;
3891 		}
3892 	}
3893 
3894 	/* Handle blockwise 1 (Part 2): Append BLOCK1 option / free context */
3895 	if (block_ctx) {
3896 		if (!last_block) {
3897 			/* More to come, ack with correspond block # */
3898 			r = coap_append_block1_option(msg->out.out_cpkt,
3899 						      &block_ctx->ctx);
3900 			if (r < 0) {
3901 				/* report as internal server error */
3902 				LOG_ERR("Fail adding block1 option: %d", r);
3903 				r = -EINVAL;
3904 				goto error;
3905 			}
3906 		} else {
3907 			/* Free context when finished */
3908 			free_block_ctx(block_ctx);
3909 		}
3910 	}
3911 
3912 	return 0;
3913 
3914 error:
3915 	lwm2m_reset_message(msg, false);
3916 	if (r == -ENOENT) {
3917 		msg->code = COAP_RESPONSE_CODE_NOT_FOUND;
3918 	} else if (r == -EPERM) {
3919 		msg->code = COAP_RESPONSE_CODE_NOT_ALLOWED;
3920 	} else if (r == -EEXIST) {
3921 		msg->code = COAP_RESPONSE_CODE_BAD_REQUEST;
3922 	} else if (r == -EFAULT) {
3923 		msg->code = COAP_RESPONSE_CODE_INCOMPLETE;
3924 	} else if (r == -EFBIG) {
3925 		msg->code = COAP_RESPONSE_CODE_REQUEST_TOO_LARGE;
3926 	} else if (r == -ENOTSUP) {
3927 		msg->code = COAP_RESPONSE_CODE_NOT_IMPLEMENTED;
3928 	} else if (r == -ENOMSG) {
3929 		msg->code = COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT;
3930 	} else {
3931 		/* Failed to handle the request */
3932 		msg->code = COAP_RESPONSE_CODE_INTERNAL_ERROR;
3933 	}
3934 
3935 	r = lwm2m_init_message(msg);
3936 	if (r < 0) {
3937 		LOG_ERR("Error recreating message: %d", r);
3938 	}
3939 
3940 	/* Free block context when error happened */
3941 	free_block_ctx(block_ctx);
3942 
3943 	return 0;
3944 }
3945 
lwm2m_response_promote_to_con(struct lwm2m_message * msg)3946 static int lwm2m_response_promote_to_con(struct lwm2m_message *msg)
3947 {
3948 	int ret;
3949 
3950 	msg->type = COAP_TYPE_CON;
3951 	msg->mid = coap_next_id();
3952 
3953 	/* Since the response CoAP packet is already generated at this point,
3954 	 * tweak the specific fields manually:
3955 	 * - CoAP message type (byte 0, bits 2 and 3)
3956 	 * - CoAP message id (bytes 2 and 3)
3957 	 */
3958 	msg->cpkt.data[0] &= ~(0x3 << 4);
3959 	msg->cpkt.data[0] |= (msg->type & 0x3) << 4;
3960 	msg->cpkt.data[2] = msg->mid >> 8;
3961 	msg->cpkt.data[3] = (uint8_t) msg->mid;
3962 
3963 	/* Add the packet to the pending list. */
3964 	msg->pending = coap_pending_next_unused(
3965 				msg->ctx->pendings,
3966 				CONFIG_LWM2M_ENGINE_MAX_PENDING);
3967 	if (!msg->pending) {
3968 		LOG_ERR("Unable to find a free pending to track "
3969 			"retransmissions.");
3970 		return -ENOMEM;
3971 	}
3972 
3973 	ret = coap_pending_init(msg->pending, &msg->cpkt,
3974 				&msg->ctx->remote_addr,
3975 				COAP_DEFAULT_MAX_RETRANSMIT);
3976 	if (ret < 0) {
3977 		LOG_ERR("Unable to initialize a pending "
3978 			"retransmission (err:%d).", ret);
3979 	}
3980 
3981 	return ret;
3982 }
3983 
lwm2m_udp_receive(struct lwm2m_ctx * client_ctx,uint8_t * buf,uint16_t buf_len,struct sockaddr * from_addr,udp_request_handler_cb_t udp_request_handler)3984 static void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx,
3985 			      uint8_t *buf, uint16_t buf_len,
3986 			      struct sockaddr *from_addr,
3987 			      udp_request_handler_cb_t udp_request_handler)
3988 {
3989 	struct lwm2m_message *msg = NULL;
3990 	struct coap_pending *pending;
3991 	struct coap_reply *reply;
3992 	struct coap_packet response;
3993 	int r;
3994 	uint8_t token[8];
3995 	uint8_t tkl;
3996 
3997 	r = coap_packet_parse(&response, buf, buf_len, NULL, 0);
3998 	if (r < 0) {
3999 		LOG_ERR("Invalid data received (err:%d)", r);
4000 		return;
4001 	}
4002 
4003 	tkl = coap_header_get_token(&response, token);
4004 	pending = coap_pending_received(&response, client_ctx->pendings,
4005 					CONFIG_LWM2M_ENGINE_MAX_PENDING);
4006 	if (pending && coap_header_get_type(&response) == COAP_TYPE_ACK) {
4007 		msg = find_msg(pending, NULL);
4008 		if (msg == NULL) {
4009 			LOG_DBG("Orphaned pending %p.", pending);
4010 			return;
4011 		}
4012 
4013 		msg->acknowledged = true;
4014 
4015 		if (msg->reply == NULL) {
4016 			/* No response expected, release the message. */
4017 			lwm2m_reset_message(msg, true);
4018 			return;
4019 		}
4020 
4021 		/* If the original message was a request and an empty
4022 		 * ACK was received, expect separate response later.
4023 		 */
4024 		if ((msg->code >= COAP_METHOD_GET) &&
4025 			(msg->code <= COAP_METHOD_DELETE) &&
4026 			(coap_header_get_code(&response) == COAP_CODE_EMPTY)) {
4027 			LOG_DBG("Empty ACK, expect separate response.");
4028 			return;
4029 		}
4030 	}
4031 
4032 	LOG_DBG("checking for reply from [%s]",
4033 		log_strdup(lwm2m_sprint_ip_addr(from_addr)));
4034 	reply = coap_response_received(&response, from_addr,
4035 				       client_ctx->replies,
4036 				       CONFIG_LWM2M_ENGINE_MAX_REPLIES);
4037 	if (reply) {
4038 		msg = find_msg(NULL, reply);
4039 
4040 		if (coap_header_get_type(&response) == COAP_TYPE_CON) {
4041 			r = lwm2m_send_empty_ack(client_ctx,
4042 						 coap_header_get_id(&response));
4043 			if (r < 0) {
4044 				LOG_ERR("Error transmitting ACK");
4045 			}
4046 		}
4047 
4048 		/* skip release if reply->user_data has error condition */
4049 		if (reply && reply->user_data != COAP_REPLY_STATUS_NONE) {
4050 			/* reset reply->user_data for next time */
4051 			reply->user_data = (void *)COAP_REPLY_STATUS_NONE;
4052 			LOG_DBG("reply %p NOT removed", reply);
4053 			return;
4054 		}
4055 
4056 		/* free up msg resources */
4057 		if (msg) {
4058 			lwm2m_reset_message(msg, true);
4059 		}
4060 
4061 		LOG_DBG("reply %p handled and removed", reply);
4062 		return;
4063 	}
4064 
4065 	/*
4066 	 * If no normal response handler is found, then this is
4067 	 * a new request coming from the server.  Let's look
4068 	 * at registered objects to find a handler.
4069 	 */
4070 	if (udp_request_handler &&
4071 	    coap_header_get_type(&response) == COAP_TYPE_CON) {
4072 		msg = lwm2m_get_message(client_ctx);
4073 		if (!msg) {
4074 			LOG_ERR("Unable to get a lwm2m message!");
4075 			return;
4076 		}
4077 
4078 		/* Create a response message if we reach this point */
4079 		msg->type = COAP_TYPE_ACK;
4080 		msg->code = coap_header_get_code(&response);
4081 		msg->mid = coap_header_get_id(&response);
4082 		/* skip token generation by default */
4083 		msg->tkl = 0;
4084 
4085 		client_ctx->processed_req = msg;
4086 
4087 		/* process the response to this request */
4088 		r = udp_request_handler(&response, msg);
4089 		if (r < 0) {
4090 			return;
4091 		}
4092 
4093 		if (msg->acknowledged) {
4094 			r = lwm2m_response_promote_to_con(msg);
4095 			if (r < 0) {
4096 				LOG_ERR("Failed to promote reponse to CON: %d",
4097 					r);
4098 				lwm2m_reset_message(msg, true);
4099 				return;
4100 			}
4101 		}
4102 
4103 		client_ctx->processed_req = NULL;
4104 		lwm2m_send_message_async(msg);
4105 	} else {
4106 		LOG_DBG("No handler for response");
4107 	}
4108 }
4109 
4110 /* returns ms until the next retransmission is due, or INT32_MAX
4111  * if no retransmissions are necessary
4112  */
retransmit_request(struct lwm2m_ctx * client_ctx,const uint32_t timestamp)4113 static int32_t retransmit_request(struct lwm2m_ctx *client_ctx,
4114 				  const uint32_t timestamp)
4115 {
4116 	struct lwm2m_message *msg;
4117 	struct coap_pending *p;
4118 	int32_t remaining, next_retransmission = INT32_MAX;
4119 	int i;
4120 
4121 	for (i = 0, p = client_ctx->pendings;
4122 	     i < CONFIG_LWM2M_ENGINE_MAX_PENDING; i++, p++) {
4123 		if (!p->timeout) {
4124 			continue;
4125 		}
4126 
4127 		remaining = p->t0 + p->timeout - timestamp;
4128 		if (remaining < 0) {
4129 			msg = find_msg(p, NULL);
4130 			if (!msg) {
4131 				LOG_ERR("pending has no valid LwM2M message!");
4132 				coap_pending_clear(p);
4133 				continue;
4134 			}
4135 			if (!p->retries) {
4136 				/* pending request has expired */
4137 				if (msg->message_timeout_cb) {
4138 					msg->message_timeout_cb(msg);
4139 				}
4140 				lwm2m_reset_message(msg, true);
4141 				continue;
4142 			}
4143 			if (msg->acknowledged) {
4144 				/* No need to retransmit, just keep the timer running to
4145 				 * timeout in case no response arrives.
4146 				 */
4147 				coap_pending_cycle(p);
4148 				continue;
4149 			}
4150 
4151 			lwm2m_send_message_async(msg);
4152 			break;
4153 		}
4154 		if (remaining < next_retransmission) {
4155 			next_retransmission = remaining;
4156 		}
4157 	}
4158 
4159 	return next_retransmission;
4160 }
4161 
notify_message_timeout_cb(struct lwm2m_message * msg)4162 static void notify_message_timeout_cb(struct lwm2m_message *msg)
4163 {
4164 	if (msg->ctx != NULL) {
4165 		struct lwm2m_ctx *client_ctx = msg->ctx;
4166 
4167 		if (client_ctx->notify_timeout_cb != NULL) {
4168 			client_ctx->notify_timeout_cb();
4169 		}
4170 	}
4171 
4172 	LOG_ERR("Notify Message Timed Out : %p", msg);
4173 }
4174 
notify_message_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)4175 static int notify_message_reply_cb(const struct coap_packet *response,
4176 				   struct coap_reply *reply,
4177 				   const struct sockaddr *from)
4178 {
4179 	int ret = 0;
4180 	uint8_t type, code;
4181 	struct lwm2m_message *msg;
4182 
4183 	type = coap_header_get_type(response);
4184 	code = coap_header_get_code(response);
4185 
4186 	LOG_DBG("NOTIFY ACK type:%u code:%d.%d reply_token:'%s'",
4187 		type,
4188 		COAP_RESPONSE_CODE_CLASS(code),
4189 		COAP_RESPONSE_CODE_DETAIL(code),
4190 		log_strdup(sprint_token(reply->token, reply->tkl)));
4191 
4192 	/* remove observer on COAP_TYPE_RESET */
4193 	if (type == COAP_TYPE_RESET) {
4194 		if (reply->tkl > 0) {
4195 			msg = find_msg(NULL, reply);
4196 			ret = engine_remove_observer(msg->ctx, reply->token, reply->tkl);
4197 			if (ret) {
4198 				LOG_ERR("remove observe error: %d", ret);
4199 			}
4200 		} else {
4201 			LOG_ERR("notify reply missing token -- ignored.");
4202 		}
4203 	}
4204 
4205 	return 0;
4206 }
4207 
generate_notify_message(struct lwm2m_ctx * ctx,struct observe_node * obs,bool manual_trigger)4208 static int generate_notify_message(struct lwm2m_ctx *ctx,
4209 				   struct observe_node *obs,
4210 				   bool manual_trigger)
4211 {
4212 	struct lwm2m_message *msg;
4213 	struct lwm2m_engine_obj_inst *obj_inst;
4214 	int ret = 0;
4215 
4216 	msg = lwm2m_get_message(ctx);
4217 	if (!msg) {
4218 		LOG_ERR("Unable to get a lwm2m message!");
4219 		return -ENOMEM;
4220 	}
4221 
4222 	/* copy path */
4223 	memcpy(&msg->path, &obs->path, sizeof(struct lwm2m_obj_path));
4224 	msg->operation = LWM2M_OP_READ;
4225 
4226 	LOG_DBG("[%s] NOTIFY MSG START: %u/%u/%u(%u) token:'%s' [%s] %lld",
4227 		manual_trigger ? "MANUAL" : "AUTO",
4228 		obs->path.obj_id,
4229 		obs->path.obj_inst_id,
4230 		obs->path.res_id,
4231 		obs->path.level,
4232 		log_strdup(sprint_token(obs->token, obs->tkl)),
4233 		log_strdup(lwm2m_sprint_ip_addr(&ctx->remote_addr)),
4234 		k_uptime_get());
4235 
4236 	obj_inst = get_engine_obj_inst(obs->path.obj_id,
4237 				       obs->path.obj_inst_id);
4238 	if (!obj_inst) {
4239 		LOG_ERR("unable to get engine obj for %u/%u",
4240 			obs->path.obj_id,
4241 			obs->path.obj_inst_id);
4242 		ret = -EINVAL;
4243 		goto cleanup;
4244 	}
4245 
4246 	msg->type = COAP_TYPE_CON;
4247 	msg->code = COAP_RESPONSE_CODE_CONTENT;
4248 	msg->mid = coap_next_id();
4249 	msg->token = obs->token;
4250 	msg->tkl = obs->tkl;
4251 	msg->reply_cb = notify_message_reply_cb;
4252 	msg->message_timeout_cb = notify_message_timeout_cb;
4253 	msg->out.out_cpkt = &msg->cpkt;
4254 
4255 	ret = lwm2m_init_message(msg);
4256 	if (ret < 0) {
4257 		LOG_ERR("Unable to init lwm2m message! (err: %d)", ret);
4258 		goto cleanup;
4259 	}
4260 
4261 	/* each notification should increment the obs counter */
4262 	obs->counter++;
4263 	ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_OBSERVE,
4264 				     obs->counter);
4265 	if (ret < 0) {
4266 		LOG_ERR("OBSERVE option error: %d", ret);
4267 		goto cleanup;
4268 	}
4269 
4270 	/* set the output writer */
4271 	select_writer(&msg->out, obs->format);
4272 
4273 	ret = do_read_op(msg, obs->format);
4274 	if (ret < 0) {
4275 		LOG_ERR("error in multi-format read (err:%d)", ret);
4276 		goto cleanup;
4277 	}
4278 
4279 	lwm2m_send_message_async(msg);
4280 
4281 	LOG_DBG("NOTIFY MSG: SENT");
4282 	return 0;
4283 
4284 cleanup:
4285 	lwm2m_reset_message(msg, true);
4286 	return ret;
4287 }
4288 
engine_next_service_timeout_ms(uint32_t max_timeout,const int64_t timestamp)4289 static int32_t engine_next_service_timeout_ms(uint32_t max_timeout,
4290 					      const int64_t timestamp)
4291 {
4292 	struct service_node *srv;
4293 	uint64_t time_left_ms;
4294 	uint32_t timeout = max_timeout;
4295 
4296 	SYS_SLIST_FOR_EACH_CONTAINER(&engine_service_list, srv, node) {
4297 		time_left_ms = srv->last_timestamp + srv->min_call_period;
4298 
4299 		/* service is due */
4300 		if (time_left_ms < timestamp) {
4301 			return 0;
4302 		}
4303 
4304 		/* service timeout is less than the current timeout */
4305 		time_left_ms -= timestamp;
4306 		if (time_left_ms < timeout) {
4307 			timeout = time_left_ms;
4308 		}
4309 	}
4310 
4311 	return timeout;
4312 }
4313 
lwm2m_engine_add_service(k_work_handler_t service,uint32_t period_ms)4314 int lwm2m_engine_add_service(k_work_handler_t service, uint32_t period_ms)
4315 {
4316 	int i;
4317 
4318 	/* find an unused service index node */
4319 	for (i = 0; i < MAX_PERIODIC_SERVICE; i++) {
4320 		if (!service_node_data[i].service_work) {
4321 			break;
4322 		}
4323 	}
4324 
4325 	if (i == MAX_PERIODIC_SERVICE) {
4326 		return -ENOMEM;
4327 	}
4328 
4329 	service_node_data[i].service_work = service;
4330 	service_node_data[i].min_call_period = period_ms;
4331 	service_node_data[i].last_timestamp = 0U;
4332 
4333 	sys_slist_append(&engine_service_list,
4334 			 &service_node_data[i].node);
4335 
4336 	return 0;
4337 }
4338 
lwm2m_engine_update_service_period(k_work_handler_t service,uint32_t period_ms)4339 int lwm2m_engine_update_service_period(k_work_handler_t service, uint32_t period_ms)
4340 {
4341 	int i = 0;
4342 
4343 	for (i = 0; i < MAX_PERIODIC_SERVICE; i++) {
4344 		if (service_node_data[i].service_work == service) {
4345 			service_node_data[i].min_call_period = period_ms;
4346 			return 0;
4347 		}
4348 	}
4349 
4350 	return -ENOENT;
4351 }
4352 
lwm2m_engine_service(const int64_t timestamp)4353 static int32_t lwm2m_engine_service(const int64_t timestamp)
4354 {
4355 	struct service_node *srv;
4356 	int64_t service_due_timestamp;
4357 
4358 	SYS_SLIST_FOR_EACH_CONTAINER(&engine_service_list, srv, node) {
4359 		service_due_timestamp = srv->last_timestamp +
4360 					srv->min_call_period;
4361 		/* service is due */
4362 		if (timestamp >= service_due_timestamp) {
4363 			srv->last_timestamp = k_uptime_get();
4364 			srv->service_work(NULL);
4365 		}
4366 	}
4367 
4368 	/* calculate how long to sleep till the next service */
4369 	return engine_next_service_timeout_ms(ENGINE_UPDATE_INTERVAL_MS,
4370 					      timestamp);
4371 }
4372 
lwm2m_engine_context_close(struct lwm2m_ctx * client_ctx)4373 int lwm2m_engine_context_close(struct lwm2m_ctx *client_ctx)
4374 {
4375 	int sock_fd = client_ctx->sock_fd;
4376 	struct lwm2m_message *msg;
4377 	sys_snode_t *obs_node;
4378 	struct observe_node *obs;
4379 	size_t i;
4380 
4381 	/* Remove observes for this context */
4382 	while (!sys_slist_is_empty(&client_ctx->observer)) {
4383 		obs_node = sys_slist_get_not_empty(&client_ctx->observer);
4384 		obs = SYS_SLIST_CONTAINER(obs_node, obs, node);
4385 		(void)memset(obs, 0, sizeof(*obs));
4386 	}
4387 
4388 	for (i = 0, msg = messages; i < ARRAY_SIZE(messages); i++, msg++) {
4389 		if (msg->ctx == client_ctx) {
4390 			lwm2m_reset_message(msg, true);
4391 		}
4392 	}
4393 
4394 	coap_pendings_clear(client_ctx->pendings,
4395 			    CONFIG_LWM2M_ENGINE_MAX_PENDING);
4396 	coap_replies_clear(client_ctx->replies,
4397 			   CONFIG_LWM2M_ENGINE_MAX_REPLIES);
4398 
4399 	lwm2m_socket_del(client_ctx);
4400 	client_ctx->sock_fd = -1;
4401 	if (sock_fd >= 0) {
4402 		return close(sock_fd);
4403 	} else {
4404 		return 0;
4405 	}
4406 }
4407 
lwm2m_engine_context_init(struct lwm2m_ctx * client_ctx)4408 void lwm2m_engine_context_init(struct lwm2m_ctx *client_ctx)
4409 {
4410 	sys_slist_init(&client_ctx->pending_sends);
4411 	sys_slist_init(&client_ctx->observer);
4412 }
4413 
4414 /* LwM2M Socket Integration */
4415 
lwm2m_socket_add(struct lwm2m_ctx * ctx)4416 int lwm2m_socket_add(struct lwm2m_ctx *ctx)
4417 {
4418 	if (sock_nfds >= MAX_POLL_FD) {
4419 		return -ENOMEM;
4420 	}
4421 
4422 	sock_ctx[sock_nfds] = ctx;
4423 	sock_fds[sock_nfds].fd = ctx->sock_fd;
4424 	sock_fds[sock_nfds].events = POLLIN;
4425 	sock_nfds++;
4426 
4427 	return 0;
4428 }
4429 
lwm2m_socket_del(struct lwm2m_ctx * ctx)4430 void lwm2m_socket_del(struct lwm2m_ctx *ctx)
4431 {
4432 	for (int i = 0; i < sock_nfds; i++) {
4433 		if (sock_ctx[i] != ctx) {
4434 			continue;
4435 		}
4436 
4437 		sock_nfds--;
4438 
4439 		/* If not last, overwrite the entry with the last one. */
4440 		if (i < sock_nfds) {
4441 			sock_ctx[i] = sock_ctx[sock_nfds];
4442 			sock_fds[i].fd = sock_fds[sock_nfds].fd;
4443 			sock_fds[i].events = sock_fds[sock_nfds].events;
4444 		}
4445 
4446 		/* Remove the last entry. */
4447 		sock_ctx[sock_nfds] = NULL;
4448 		sock_fds[sock_nfds].fd = -1;
4449 		break;
4450 	}
4451 }
4452 
manual_notify_is_due(const struct observe_node * obs,const int64_t timestamp)4453 static bool manual_notify_is_due(const struct observe_node *obs,
4454 				 const int64_t timestamp)
4455 {
4456 	const bool has_min_period = obs->min_period_sec != 0;
4457 
4458 	return obs->event_timestamp > obs->last_timestamp &&
4459 		(!has_min_period || timestamp > obs->last_timestamp +
4460 		 MSEC_PER_SEC * obs->min_period_sec);
4461 }
4462 
automatic_notify_is_due(const struct observe_node * obs,const int64_t timestamp)4463 static bool automatic_notify_is_due(const struct observe_node *obs,
4464 				    const int64_t timestamp)
4465 {
4466 	const bool has_max_period = obs->max_period_sec != 0;
4467 
4468 	return has_max_period && (timestamp > obs->last_timestamp +
4469 				  MSEC_PER_SEC * obs->max_period_sec);
4470 }
4471 
check_notifications(struct lwm2m_ctx * ctx,const int64_t timestamp)4472 static void check_notifications(struct lwm2m_ctx *ctx,
4473 				const int64_t timestamp)
4474 {
4475 	struct observe_node *obs;
4476 	int rc;
4477 	bool manual_notify, automatic_notify;
4478 
4479 	SYS_SLIST_FOR_EACH_CONTAINER(&ctx->observer, obs, node) {
4480 		manual_notify = manual_notify_is_due(obs, timestamp);
4481 		automatic_notify = automatic_notify_is_due(obs, timestamp);
4482 		if (!manual_notify && !automatic_notify) {
4483 			continue;
4484 		}
4485 		rc = generate_notify_message(ctx, obs, manual_notify);
4486 		if (rc == -ENOMEM) {
4487 			/* no memory/messages available, retry later */
4488 			return;
4489 		}
4490 		obs->last_timestamp = timestamp;
4491 		if (!rc) {
4492 			/* create at most one notification */
4493 			return;
4494 		}
4495 	}
4496 }
4497 
socket_recv_message(struct lwm2m_ctx * client_ctx)4498 static int socket_recv_message(struct lwm2m_ctx *client_ctx)
4499 {
4500 	static uint8_t in_buf[NET_IPV6_MTU];
4501 	socklen_t from_addr_len;
4502 	ssize_t len;
4503 	static struct sockaddr from_addr;
4504 
4505 	from_addr_len = sizeof(from_addr);
4506 	len = recvfrom(client_ctx->sock_fd, in_buf, sizeof(in_buf) - 1,
4507 		       0, &from_addr, &from_addr_len);
4508 
4509 	if (len < 0) {
4510 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
4511 			return -errno;
4512 		}
4513 
4514 		LOG_ERR("Error reading response: %d", errno);
4515 		if (client_ctx->fault_cb != NULL) {
4516 			client_ctx->fault_cb(errno);
4517 		}
4518 		return -errno;
4519 	}
4520 
4521 	if (len == 0) {
4522 		LOG_ERR("Zero length recv");
4523 		return 0;
4524 	}
4525 
4526 	in_buf[len] = 0U;
4527 	lwm2m_udp_receive(client_ctx, in_buf, len, &from_addr, handle_request);
4528 
4529 	return 0;
4530 }
4531 
socket_send_message(struct lwm2m_ctx * client_ctx)4532 static int socket_send_message(struct lwm2m_ctx *client_ctx)
4533 {
4534 	sys_snode_t *msg_node = sys_slist_get(&client_ctx->pending_sends);
4535 	struct lwm2m_message *msg;
4536 
4537 	if (!msg_node) {
4538 		return 0;
4539 	}
4540 	msg = SYS_SLIST_CONTAINER(msg_node, msg, node);
4541 	return lwm2m_send_message(msg);
4542 }
4543 
socket_reset_pollfd_events(void)4544 static void socket_reset_pollfd_events(void)
4545 {
4546 	for (int i = 0; i < sock_nfds; ++i) {
4547 		sock_fds[i].events = POLLIN
4548 			| (sys_slist_is_empty(&sock_ctx[i]->pending_sends) ? 0 : POLLOUT);
4549 		sock_fds[i].revents = 0;
4550 	}
4551 }
4552 
4553 /* LwM2M main work loop */
socket_loop(void)4554 static void socket_loop(void)
4555 {
4556 	int i, rc;
4557 	int64_t timestamp;
4558 	int32_t timeout, next_retransmit;
4559 
4560 	while (1) {
4561 		timestamp = k_uptime_get();
4562 		timeout = lwm2m_engine_service(timestamp);
4563 
4564 		/* wait for sockets */
4565 		if (sock_nfds < 1) {
4566 			k_msleep(timeout);
4567 			continue;
4568 		}
4569 
4570 		for (i = 0; i < sock_nfds; ++i) {
4571 			if (sys_slist_is_empty(&sock_ctx[i]->pending_sends)) {
4572 				next_retransmit = retransmit_request(sock_ctx[i], timestamp);
4573 				if (next_retransmit < timeout) {
4574 					timeout = next_retransmit;
4575 				}
4576 			}
4577 			if (sys_slist_is_empty(&sock_ctx[i]->pending_sends)) {
4578 				check_notifications(sock_ctx[i], timestamp);
4579 			}
4580 		}
4581 
4582 		socket_reset_pollfd_events();
4583 
4584 		/*
4585 		 * FIXME: Currently we timeout and restart poll in case fds
4586 		 *        were modified.
4587 		 */
4588 		rc = poll(sock_fds, sock_nfds, timeout);
4589 		if (rc < 0) {
4590 			LOG_ERR("Error in poll:%d", errno);
4591 			errno = 0;
4592 			k_msleep(ENGINE_UPDATE_INTERVAL_MS);
4593 			continue;
4594 		}
4595 
4596 		for (i = 0; i < sock_nfds; i++) {
4597 			if ((sock_fds[i].revents & POLLERR) ||
4598 			    (sock_fds[i].revents & POLLNVAL) ||
4599 			    (sock_fds[i].revents & POLLHUP)) {
4600 				LOG_ERR("Poll reported a socket error, %02x.",
4601 					sock_fds[i].revents);
4602 				if (sock_ctx[i] != NULL &&
4603 				    sock_ctx[i]->fault_cb != NULL) {
4604 					sock_ctx[i]->fault_cb(EIO);
4605 				}
4606 				continue;
4607 			}
4608 
4609 			if (sock_fds[i].revents & POLLIN) {
4610 				while (sock_ctx[i]) {
4611 					rc = socket_recv_message(sock_ctx[i]);
4612 					if (rc) {
4613 						break;
4614 					}
4615 				}
4616 			}
4617 
4618 			if (sock_fds[i].revents & POLLOUT) {
4619 				socket_send_message(sock_ctx[i]);
4620 			}
4621 		}
4622 	}
4623 }
4624 
4625 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
load_tls_credential(struct lwm2m_ctx * client_ctx,uint16_t res_id,enum tls_credential_type type)4626 static int load_tls_credential(struct lwm2m_ctx *client_ctx, uint16_t res_id,
4627 			       enum tls_credential_type type)
4628 {
4629 	int ret = 0;
4630 	void *cred = NULL;
4631 	uint16_t cred_len;
4632 	uint8_t cred_flags;
4633 	char pathstr[MAX_RESOURCE_LEN];
4634 
4635 	/* ignore error value */
4636 	tls_credential_delete(client_ctx->tls_tag, type);
4637 
4638 	snprintk(pathstr, sizeof(pathstr), "0/%d/%u", client_ctx->sec_obj_inst,
4639 		 res_id);
4640 
4641 	ret = lwm2m_engine_get_res_data(pathstr, &cred, &cred_len, &cred_flags);
4642 	if (ret < 0) {
4643 		LOG_ERR("Unable to get resource data for '%s'",
4644 			log_strdup(pathstr));
4645 		return ret;
4646 	}
4647 
4648 	ret = tls_credential_add(client_ctx->tls_tag, type, cred, cred_len);
4649 	if (ret < 0) {
4650 		LOG_ERR("Error setting cred tag %d type %d: Error %d",
4651 			client_ctx->tls_tag, type, ret);
4652 	}
4653 
4654 	return ret;
4655 }
4656 #endif /* CONFIG_LWM2M_DTLS_SUPPORT */
4657 
lwm2m_socket_start(struct lwm2m_ctx * client_ctx)4658 int lwm2m_socket_start(struct lwm2m_ctx *client_ctx)
4659 {
4660 	int flags;
4661 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
4662 	int ret;
4663 
4664 	if (client_ctx->load_credentials) {
4665 		ret = client_ctx->load_credentials(client_ctx);
4666 		if (ret < 0) {
4667 			return ret;
4668 		}
4669 	} else {
4670 		ret = load_tls_credential(client_ctx, 3, TLS_CREDENTIAL_PSK_ID);
4671 		if (ret < 0) {
4672 			return ret;
4673 		}
4674 
4675 		ret = load_tls_credential(client_ctx, 5, TLS_CREDENTIAL_PSK);
4676 		if (ret < 0) {
4677 			return ret;
4678 		}
4679 	}
4680 
4681 	if (client_ctx->use_dtls) {
4682 		client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family,
4683 					     SOCK_DGRAM, IPPROTO_DTLS_1_2);
4684 	} else
4685 #endif /* CONFIG_LWM2M_DTLS_SUPPORT */
4686 	{
4687 		client_ctx->sock_fd = socket(client_ctx->remote_addr.sa_family,
4688 					     SOCK_DGRAM, IPPROTO_UDP);
4689 	}
4690 
4691 	if (client_ctx->sock_fd < 0) {
4692 		LOG_ERR("Failed to create socket: %d", errno);
4693 		return -errno;
4694 	}
4695 
4696 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
4697 	if (client_ctx->use_dtls) {
4698 		sec_tag_t tls_tag_list[] = {
4699 			client_ctx->tls_tag,
4700 		};
4701 
4702 		ret = setsockopt(client_ctx->sock_fd, SOL_TLS, TLS_SEC_TAG_LIST,
4703 				 tls_tag_list, sizeof(tls_tag_list));
4704 		if (ret < 0) {
4705 			LOG_ERR("Failed to set TLS_SEC_TAG_LIST option: %d",
4706 				errno);
4707 			lwm2m_engine_context_close(client_ctx);
4708 			return -errno;
4709 		}
4710 	}
4711 #endif /* CONFIG_LWM2M_DTLS_SUPPORT */
4712 
4713 	if (connect(client_ctx->sock_fd, &client_ctx->remote_addr,
4714 		    NET_SOCKADDR_MAX_SIZE) < 0) {
4715 		LOG_ERR("Cannot connect UDP (-%d)", errno);
4716 		lwm2m_engine_context_close(client_ctx);
4717 		return -errno;
4718 	}
4719 
4720 	flags = fcntl(client_ctx->sock_fd, F_GETFL, 0);
4721 	if (flags == -1) {
4722 		return -errno;
4723 	}
4724 	fcntl(client_ctx->sock_fd, F_SETFL, flags | O_NONBLOCK);
4725 
4726 	return lwm2m_socket_add(client_ctx);
4727 }
4728 
lwm2m_parse_peerinfo(char * url,struct sockaddr * addr,bool * use_dtls)4729 int lwm2m_parse_peerinfo(char *url, struct sockaddr *addr, bool *use_dtls)
4730 {
4731 	struct http_parser_url parser;
4732 #if defined(CONFIG_LWM2M_DNS_SUPPORT)
4733 	struct addrinfo *res, hints = { 0 };
4734 #endif
4735 	int ret;
4736 	uint16_t off, len;
4737 	uint8_t tmp;
4738 
4739 	LOG_DBG("Parse url: %s", log_strdup(url));
4740 
4741 	http_parser_url_init(&parser);
4742 	ret = http_parser_parse_url(url, strlen(url), 0, &parser);
4743 	if (ret < 0) {
4744 		LOG_ERR("Invalid url: %s", log_strdup(url));
4745 		return -ENOTSUP;
4746 	}
4747 
4748 	off = parser.field_data[UF_SCHEMA].off;
4749 	len = parser.field_data[UF_SCHEMA].len;
4750 
4751 	/* check for supported protocol */
4752 	if (strncmp(url + off, "coaps", len) != 0) {
4753 		return -EPROTONOSUPPORT;
4754 	}
4755 
4756 	/* check for DTLS requirement */
4757 	*use_dtls = false;
4758 	if (len == 5U && strncmp(url + off, "coaps", len) == 0) {
4759 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
4760 		*use_dtls = true;
4761 #else
4762 		return -EPROTONOSUPPORT;
4763 #endif /* CONFIG_LWM2M_DTLS_SUPPORT */
4764 	}
4765 
4766 	if (!(parser.field_set & (1 << UF_PORT))) {
4767 		/* Set to default port of CoAP */
4768 		parser.port = CONFIG_LWM2M_PEER_PORT;
4769 	}
4770 
4771 	off = parser.field_data[UF_HOST].off;
4772 	len = parser.field_data[UF_HOST].len;
4773 
4774 	/* truncate host portion */
4775 	tmp = url[off + len];
4776 	url[off + len] = '\0';
4777 
4778 	/* initialize addr */
4779 	(void)memset(addr, 0, sizeof(*addr));
4780 
4781 	/* try and set IP address directly */
4782 	addr->sa_family = AF_INET6;
4783 	ret = net_addr_pton(AF_INET6, url + off,
4784 			    &((struct sockaddr_in6 *)addr)->sin6_addr);
4785 	/* Try to parse again using AF_INET */
4786 	if (ret < 0) {
4787 		addr->sa_family = AF_INET;
4788 		ret = net_addr_pton(AF_INET, url + off,
4789 				    &((struct sockaddr_in *)addr)->sin_addr);
4790 	}
4791 
4792 	if (ret < 0) {
4793 #if defined(CONFIG_LWM2M_DNS_SUPPORT)
4794 #if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4)
4795 		hints.ai_family = AF_UNSPEC;
4796 #elif defined(CONFIG_NET_IPV6)
4797 		hints.ai_family = AF_INET6;
4798 #elif defined(CONFIG_NET_IPV4)
4799 		hints.ai_family = AF_INET;
4800 #else
4801 		hints.ai_family = AF_UNSPEC;
4802 #endif /* defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4) */
4803 		hints.ai_socktype = SOCK_DGRAM;
4804 		ret = getaddrinfo(url + off, NULL, &hints, &res);
4805 		if (ret != 0) {
4806 			LOG_ERR("Unable to resolve address");
4807 			/* DNS error codes don't align with normal errors */
4808 			ret = -ENOENT;
4809 			goto cleanup;
4810 		}
4811 
4812 		memcpy(addr, res->ai_addr, sizeof(*addr));
4813 		addr->sa_family = res->ai_family;
4814 		freeaddrinfo(res);
4815 #else
4816 		goto cleanup;
4817 #endif /* CONFIG_LWM2M_DNS_SUPPORT */
4818 	}
4819 
4820 	/* set port */
4821 	if (addr->sa_family == AF_INET6) {
4822 		net_sin6(addr)->sin6_port = htons(parser.port);
4823 	} else if (addr->sa_family == AF_INET) {
4824 		net_sin(addr)->sin_port = htons(parser.port);
4825 	} else {
4826 		ret = -EPROTONOSUPPORT;
4827 	}
4828 
4829 cleanup:
4830 	/* restore host separator */
4831 	url[off + len] = tmp;
4832 	return ret;
4833 }
4834 
lwm2m_engine_start(struct lwm2m_ctx * client_ctx)4835 int lwm2m_engine_start(struct lwm2m_ctx *client_ctx)
4836 {
4837 	char pathstr[MAX_RESOURCE_LEN];
4838 	char *url;
4839 	uint16_t url_len;
4840 	uint8_t url_data_flags;
4841 	int ret = 0U;
4842 
4843 	/* get the server URL */
4844 	snprintk(pathstr, sizeof(pathstr), "0/%d/0", client_ctx->sec_obj_inst);
4845 	ret = lwm2m_engine_get_res_data(pathstr, (void **)&url, &url_len,
4846 					&url_data_flags);
4847 	if (ret < 0) {
4848 		return ret;
4849 	}
4850 
4851 	url[url_len] = '\0';
4852 	ret = lwm2m_parse_peerinfo(url, &client_ctx->remote_addr,
4853 				   &client_ctx->use_dtls);
4854 	if (ret < 0) {
4855 		return ret;
4856 	}
4857 
4858 	lwm2m_engine_context_init(client_ctx);
4859 	return lwm2m_socket_start(client_ctx);
4860 }
4861 
lwm2m_engine_init(const struct device * dev)4862 static int lwm2m_engine_init(const struct device *dev)
4863 {
4864 	(void)memset(block1_contexts, 0, sizeof(block1_contexts));
4865 
4866 	/* start sock receive thread */
4867 	k_thread_create(&engine_thread_data, &engine_thread_stack[0],
4868 			K_KERNEL_STACK_SIZEOF(engine_thread_stack),
4869 			(k_thread_entry_t)socket_loop, NULL, NULL, NULL,
4870 			THREAD_PRIORITY, 0, K_NO_WAIT);
4871 	k_thread_name_set(&engine_thread_data, "lwm2m-sock-recv");
4872 	LOG_DBG("LWM2M engine socket receive thread started");
4873 
4874 	return 0;
4875 }
4876 
4877 SYS_INIT(lwm2m_engine_init, APPLICATION, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
4878