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_message_handling
16 #define LOG_LEVEL	CONFIG_LWM2M_LOG_LEVEL
17 
18 #include <zephyr/logging/log.h>
19 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
20 
21 #include <ctype.h>
22 #include <errno.h>
23 #include <stddef.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include <zephyr/init.h>
29 #include <zephyr/net/http/parser_url.h>
30 #include <zephyr/net/lwm2m.h>
31 #include <zephyr/net/lwm2m_path.h>
32 #include <zephyr/net/net_ip.h>
33 #include <zephyr/net/socket.h>
34 #include <zephyr/sys/printk.h>
35 #include <zephyr/types.h>
36 #include <zephyr/sys/hash_function.h>
37 
38 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
39 #include <zephyr/net/tls_credentials.h>
40 #endif
41 #if defined(CONFIG_DNS_RESOLVER)
42 #include <zephyr/net/dns_resolve.h>
43 #endif
44 
45 #include "lwm2m_engine.h"
46 #include "lwm2m_object.h"
47 #include "lwm2m_obj_access_control.h"
48 #include "lwm2m_obj_server.h"
49 #include "lwm2m_obj_gateway.h"
50 #include "lwm2m_rw_link_format.h"
51 #include "lwm2m_rw_oma_tlv.h"
52 #include "lwm2m_rw_plain_text.h"
53 #include "lwm2m_rw_opaque.h"
54 #include "lwm2m_util.h"
55 #include "lwm2m_rd_client.h"
56 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
57 #include "lwm2m_rw_senml_json.h"
58 #endif
59 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
60 #include "lwm2m_rw_json.h"
61 #endif
62 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT
63 #include "lwm2m_rw_cbor.h"
64 #endif
65 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT
66 #include "lwm2m_rw_senml_cbor.h"
67 #endif
68 
69 /* TODO: figure out what's correct value */
70 #define TIMEOUT_BLOCKWISE_TRANSFER_MS (MSEC_PER_SEC * 30)
71 
72 #define LWM2M_DP_CLIENT_URI "dp"
73 
74 #define OUTPUT_CONTEXT_IN_USE_MARK (enum coap_block_size)(-1)
75 
76 #ifdef CONFIG_ZTEST
77 #define STATIC
78 #else
79 #define STATIC static
80 #endif
81 
82 /* Resources */
83 
84 /* Shared set of in-flight LwM2M messages */
85 static struct lwm2m_message messages[CONFIG_LWM2M_ENGINE_MAX_MESSAGES];
86 static struct lwm2m_block_context block1_contexts[NUM_BLOCK1_CONTEXT];
87 static struct lwm2m_message *ongoing_block2_tx;
88 
89 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
90 /* we need 1 more buffer as the payload is encoded in that buffer first even if
91  * block transfer is not required for the message.
92  */
93 #define ENCODE_BUFFER_POOL_SIZE (CONFIG_LWM2M_NUM_OUTPUT_BLOCK_CONTEXT + 1)
94 /* buffers for serializing big message bodies */
95 K_MEM_SLAB_DEFINE_STATIC(body_encode_buffer_slab, CONFIG_LWM2M_COAP_ENCODE_BUFFER_SIZE,
96 			 ENCODE_BUFFER_POOL_SIZE, 4);
97 #endif
98 
99 /* External resources */
100 sys_slist_t *lwm2m_engine_obj_list(void);
101 
102 sys_slist_t *lwm2m_engine_obj_inst_list(void);
103 
104 static int handle_request(struct coap_packet *request, struct lwm2m_message *msg);
105 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
106 STATIC int build_msg_block_for_send(struct lwm2m_message *msg, uint16_t block_num);
107 struct coap_block_context *lwm2m_output_block_context(void);
108 #endif
109 
110 /* block-wise transfer functions */
111 
lwm2m_default_block_size(void)112 enum coap_block_size lwm2m_default_block_size(void)
113 {
114 	switch (CONFIG_LWM2M_COAP_BLOCK_SIZE) {
115 	case 16:
116 		return COAP_BLOCK_16;
117 	case 32:
118 		return COAP_BLOCK_32;
119 	case 64:
120 		return COAP_BLOCK_64;
121 	case 128:
122 		return COAP_BLOCK_128;
123 	case 256:
124 		return COAP_BLOCK_256;
125 	case 512:
126 		return COAP_BLOCK_512;
127 	case 1024:
128 		return COAP_BLOCK_1024;
129 	}
130 
131 	return COAP_BLOCK_256;
132 }
133 
lwm2m_clear_block_contexts(void)134 void lwm2m_clear_block_contexts(void)
135 {
136 	(void)memset(block1_contexts, 0, sizeof(block1_contexts));
137 }
138 
init_block_ctx(const struct lwm2m_obj_path * path,struct lwm2m_block_context ** ctx)139 static int init_block_ctx(const struct lwm2m_obj_path *path, struct lwm2m_block_context **ctx)
140 {
141 	int i;
142 	int64_t timestamp;
143 
144 	if (!path) {
145 		LOG_ERR("Null block ctx path");
146 		return -EFAULT;
147 	}
148 
149 	*ctx = NULL;
150 	timestamp = k_uptime_get();
151 	for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
152 		if (block1_contexts[i].path.level == 0U) {
153 			*ctx = &block1_contexts[i];
154 			break;
155 		}
156 
157 		if (timestamp - block1_contexts[i].timestamp >
158 		    TIMEOUT_BLOCKWISE_TRANSFER_MS) {
159 			*ctx = &block1_contexts[i];
160 			/* TODO: notify application for block
161 			 * transfer timeout
162 			 */
163 			break;
164 		}
165 	}
166 
167 	if (*ctx == NULL) {
168 		LOG_ERR("Cannot find free block context");
169 		return -ENOMEM;
170 	}
171 
172 	memcpy(&(*ctx)->path, path, sizeof(struct lwm2m_obj_path));
173 	coap_block_transfer_init(&(*ctx)->ctx, lwm2m_default_block_size(), 0);
174 	(*ctx)->timestamp = timestamp;
175 	(*ctx)->expected = 0;
176 	(*ctx)->last_block = false;
177 	memset(&(*ctx)->opaque, 0, sizeof((*ctx)->opaque));
178 
179 	return 0;
180 }
181 
get_block_ctx(const struct lwm2m_obj_path * path,struct lwm2m_block_context ** ctx)182 static int get_block_ctx(const struct lwm2m_obj_path *path, struct lwm2m_block_context **ctx)
183 {
184 	int i;
185 
186 	if (!path) {
187 		LOG_ERR("Null block ctx path");
188 		return -EFAULT;
189 	}
190 
191 	*ctx = NULL;
192 
193 	for (i = 0; i < NUM_BLOCK1_CONTEXT; i++) {
194 		if (memcmp(path, &block1_contexts[i].path,
195 			  sizeof(struct lwm2m_obj_path)) == 0) {
196 			*ctx = &block1_contexts[i];
197 			/* refresh timestamp */
198 			(*ctx)->timestamp = k_uptime_get();
199 			break;
200 		}
201 	}
202 
203 	if (*ctx == NULL) {
204 		return -ENOENT;
205 	}
206 
207 	return 0;
208 }
209 
free_block_ctx(struct lwm2m_block_context * ctx)210 static void free_block_ctx(struct lwm2m_block_context *ctx)
211 {
212 	if (ctx == NULL) {
213 		return;
214 	}
215 
216 	memset(&ctx->path, 0, sizeof(struct lwm2m_obj_path));
217 }
218 
219 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
request_output_block_ctx(struct coap_block_context ** ctx)220 STATIC int request_output_block_ctx(struct coap_block_context **ctx)
221 {
222 	*ctx = NULL;
223 	int i;
224 
225 	for (i = 0; i < NUM_OUTPUT_BLOCK_CONTEXT; i++) {
226 		if (lwm2m_output_block_context()[i].block_size == 0) {
227 			*ctx = &lwm2m_output_block_context()[i];
228 			(*ctx)->block_size = OUTPUT_CONTEXT_IN_USE_MARK;
229 			return 0;
230 		}
231 	}
232 
233 	return -ENOMEM;
234 }
235 
release_output_block_ctx(struct coap_block_context ** ctx)236 STATIC void release_output_block_ctx(struct coap_block_context **ctx)
237 {
238 	int i;
239 
240 	if (ctx == NULL) {
241 		return;
242 	}
243 
244 	for (i = 0; i < NUM_OUTPUT_BLOCK_CONTEXT; i++) {
245 		if (&lwm2m_output_block_context()[i] == *ctx) {
246 			lwm2m_output_block_context()[i].block_size = 0;
247 			*ctx = NULL;
248 		}
249 	}
250 }
251 
252 
log_buffer_usage(void)253 static inline void log_buffer_usage(void)
254 {
255 #if defined(CONFIG_LWM2M_LOG_ENCODE_BUFFER_ALLOCATIONS)
256 	LOG_PRINTK("body_encode_buffer_slab: free: %u, allocated: %u, max. allocated: %u\n",
257 		 k_mem_slab_num_free_get(&body_encode_buffer_slab),
258 		 k_mem_slab_num_used_get(&body_encode_buffer_slab),
259 		 k_mem_slab_max_used_get(&body_encode_buffer_slab));
260 #endif
261 }
262 
request_body_encode_buffer(uint8_t ** buffer)263 static inline int request_body_encode_buffer(uint8_t **buffer)
264 {
265 	int r;
266 
267 	r = k_mem_slab_alloc(&body_encode_buffer_slab, (void **)buffer, K_NO_WAIT);
268 	log_buffer_usage();
269 	return r;
270 }
271 
release_body_encode_buffer(uint8_t ** buffer)272 static inline void release_body_encode_buffer(uint8_t **buffer)
273 {
274 	if (buffer && *buffer) {
275 		k_mem_slab_free(&body_encode_buffer_slab, (void *)*buffer);
276 		log_buffer_usage();
277 	}
278 }
279 
build_msg_block_for_send(struct lwm2m_message * msg,uint16_t block_num)280 STATIC int build_msg_block_for_send(struct lwm2m_message *msg, uint16_t block_num)
281 {
282 	int ret;
283 	uint16_t payload_size;
284 	const uint16_t block_size_bytes = coap_block_size_to_bytes(lwm2m_default_block_size());
285 	uint16_t complete_payload_len;
286 	const uint8_t *complete_payload =
287 		coap_packet_get_payload(&msg->body_encode_buffer, &complete_payload_len);
288 	uint8_t token[COAP_TOKEN_MAX_LEN];
289 	uint8_t tkl;
290 
291 	NET_ASSERT(msg->msg_data == msg->cpkt.data,
292 		   "big data buffer should not be in use for writing message");
293 
294 	if (block_num * block_size_bytes >= complete_payload_len) {
295 		return -EINVAL;
296 	}
297 
298 	if (block_num == 0) {
299 		/* Copy the header only for first block.
300 		 * For following blocks a new one is generated.
301 		 */
302 		ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), msg->body_encode_buffer.data,
303 				 msg->body_encode_buffer.hdr_len);
304 		if (ret < 0) {
305 			return ret;
306 		}
307 		msg->cpkt.hdr_len = msg->body_encode_buffer.hdr_len;
308 	} else {
309 		/* Keep user data between blocks */
310 		void *user_data = msg->reply ? msg->reply->user_data : NULL;
311 
312 		/* reuse message for next block. Copy token from the new query to allow
313 		 * CoAP clients to use new token for every query of ongoing transaction
314 		 */
315 		lwm2m_reset_message(msg, false);
316 		if (msg->type == COAP_TYPE_ACK) {
317 			msg->mid = coap_header_get_id(msg->in.in_cpkt);
318 			tkl = coap_header_get_token(msg->in.in_cpkt, token);
319 		} else {
320 			msg->mid = coap_next_id();
321 			tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
322 		}
323 		msg->token = token;
324 		msg->tkl = tkl;
325 		ret = lwm2m_init_message(msg);
326 		if (ret < 0) {
327 			lwm2m_reset_message(msg, true);
328 			LOG_ERR("Unable to init lwm2m message for next block!");
329 			return ret;
330 		}
331 		if (msg->reply) {
332 			msg->reply->user_data = user_data;
333 		}
334 	}
335 
336 	/* copy the options */
337 	ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt),
338 			 msg->body_encode_buffer.data + msg->body_encode_buffer.hdr_len,
339 			 msg->body_encode_buffer.opt_len);
340 	if (ret < 0) {
341 		return ret;
342 	}
343 	msg->cpkt.opt_len = msg->body_encode_buffer.opt_len;
344 
345 	msg->cpkt.delta = msg->body_encode_buffer.delta;
346 
347 	if (block_num == 0) {
348 		ret = request_output_block_ctx(&msg->out.block_ctx);
349 		if (ret < 0) {
350 			LOG_ERR("coap packet init error: no output block context available");
351 			return ret;
352 		}
353 		ret = coap_block_transfer_init(msg->out.block_ctx, lwm2m_default_block_size(),
354 					       complete_payload_len);
355 		if (ret < 0) {
356 			return ret;
357 		}
358 		if (msg->type == COAP_TYPE_ACK) {
359 			ongoing_block2_tx = msg;
360 		}
361 		msg->block_send = true;
362 	} else {
363 		/*  update block context */
364 		msg->out.block_ctx->current = block_num * block_size_bytes;
365 	}
366 
367 	ret = coap_append_descriptive_block_option(&msg->cpkt, msg->out.block_ctx);
368 	if (ret < 0) {
369 		return ret;
370 	}
371 
372 	ret = coap_packet_append_payload_marker(&msg->cpkt);
373 	if (ret < 0) {
374 		return ret;
375 	}
376 
377 	payload_size = MIN(complete_payload_len - block_num * block_size_bytes, block_size_bytes);
378 	ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt),
379 			 complete_payload + (block_num * block_size_bytes), payload_size);
380 	if (ret < 0) {
381 		return ret;
382 	}
383 
384 	return 0;
385 }
386 
prepare_msg_for_send(struct lwm2m_message * msg)387 STATIC int prepare_msg_for_send(struct lwm2m_message *msg)
388 {
389 	int ret;
390 	uint16_t len;
391 	const uint8_t *payload;
392 
393 	/* save the big buffer for later use (splitting blocks) */
394 	msg->body_encode_buffer = msg->cpkt;
395 
396 	/* set the default (small) buffer for sending blocks */
397 	msg->cpkt.data = msg->msg_data;
398 	msg->cpkt.offset = 0;
399 	msg->cpkt.max_len = MAX_PACKET_SIZE;
400 
401 	payload = coap_packet_get_payload(&msg->body_encode_buffer, &len);
402 	if (len <= CONFIG_LWM2M_COAP_MAX_MSG_SIZE) {
403 
404 		/* copy the packet */
405 		ret = buf_append(CPKT_BUF_WRITE(&msg->cpkt), msg->body_encode_buffer.data,
406 				 msg->body_encode_buffer.offset);
407 		if (ret != 0) {
408 			return ret;
409 		}
410 
411 		msg->cpkt.hdr_len = msg->body_encode_buffer.hdr_len;
412 		msg->cpkt.opt_len = msg->body_encode_buffer.opt_len;
413 
414 		/* clear big buffer */
415 		release_body_encode_buffer(&msg->body_encode_buffer.data);
416 		msg->body_encode_buffer.data = NULL;
417 
418 		NET_ASSERT(msg->out.block_ctx == NULL, "Expecting to have no context to release");
419 	} else {
420 		/* Before splitting the content, append Etag option to protect the integrity of
421 		 * the payload.
422 		 */
423 		if (IS_ENABLED(CONFIG_SYS_HASH_FUNC32)) {
424 			uint32_t hash = sys_hash32(payload, len);
425 
426 			coap_packet_append_option(&msg->body_encode_buffer, COAP_OPTION_ETAG,
427 						  (const uint8_t *)&hash, sizeof(hash));
428 		}
429 
430 		ret = build_msg_block_for_send(msg, 0);
431 		if (ret != 0) {
432 			return ret;
433 		}
434 	}
435 
436 	return 0;
437 }
438 
439 #endif
440 
lwm2m_engine_context_close(struct lwm2m_ctx * client_ctx)441 void lwm2m_engine_context_close(struct lwm2m_ctx *client_ctx)
442 {
443 	struct lwm2m_message *msg;
444 	sys_snode_t *obs_node;
445 	struct observe_node *obs;
446 	size_t i;
447 
448 	/* Remove observes for this context */
449 	while (!sys_slist_is_empty(&client_ctx->observer)) {
450 		obs_node = sys_slist_get_not_empty(&client_ctx->observer);
451 		obs = SYS_SLIST_CONTAINER(obs_node, obs, node);
452 		remove_observer_from_list(client_ctx, NULL, obs);
453 	}
454 
455 	for (i = 0, msg = messages; i < ARRAY_SIZE(messages); i++, msg++) {
456 		if (msg->ctx == client_ctx) {
457 			lwm2m_reset_message(msg, true);
458 		}
459 	}
460 
461 	coap_pendings_clear(client_ctx->pendings, ARRAY_SIZE(client_ctx->pendings));
462 	coap_replies_clear(client_ctx->replies, ARRAY_SIZE(client_ctx->replies));
463 
464 
465 	client_ctx->connection_suspended = false;
466 #if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED)
467 	client_ctx->buffer_client_messages = true;
468 #endif
469 }
470 
lwm2m_engine_context_init(struct lwm2m_ctx * client_ctx)471 void lwm2m_engine_context_init(struct lwm2m_ctx *client_ctx)
472 {
473 	sys_slist_init(&client_ctx->pending_sends);
474 	sys_slist_init(&client_ctx->observer);
475 	client_ctx->connection_suspended = false;
476 #if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED)
477 	client_ctx->buffer_client_messages = true;
478 	sys_slist_init(&client_ctx->queued_messages);
479 #endif
480 }
481 /* utility functions */
482 
coap_options_to_path(struct coap_option * opt,int options_count,struct lwm2m_obj_path * path)483 int coap_options_to_path(struct coap_option *opt, int options_count,
484 				struct lwm2m_obj_path *path)
485 {
486 	uint16_t len,
487 		*id[4] = {&path->obj_id, &path->obj_inst_id, &path->res_id, &path->res_inst_id};
488 
489 	path->level = options_count;
490 
491 	for (int i = 0; i < options_count; i++) {
492 		*id[i] = lwm2m_atou16(opt[i].value, opt[i].len, &len);
493 		if (len == 0U || opt[i].len != len) {
494 			path->level = i;
495 			break;
496 		}
497 	}
498 
499 	return options_count == path->level ? 0 : -EINVAL;
500 }
501 
find_msg(struct coap_pending * pending,struct coap_reply * reply)502 struct lwm2m_message *find_msg(struct coap_pending *pending, struct coap_reply *reply)
503 {
504 	size_t i;
505 	struct lwm2m_message *msg;
506 
507 	if (!pending && !reply) {
508 		return NULL;
509 	}
510 
511 	msg = lwm2m_get_ongoing_rd_msg();
512 	if (msg) {
513 		if (pending != NULL && msg->pending == pending) {
514 			return msg;
515 		}
516 
517 		if (reply != NULL && msg->reply == reply) {
518 			return msg;
519 		}
520 	}
521 
522 	for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
523 		if (pending != NULL && messages[i].ctx && messages[i].pending == pending) {
524 			return &messages[i];
525 		}
526 
527 		if (reply != NULL && messages[i].ctx && messages[i].reply == reply) {
528 			return &messages[i];
529 		}
530 	}
531 
532 	return NULL;
533 }
534 
lwm2m_get_message(struct lwm2m_ctx * client_ctx)535 struct lwm2m_message *lwm2m_get_message(struct lwm2m_ctx *client_ctx)
536 {
537 	size_t i;
538 
539 	for (i = 0; i < CONFIG_LWM2M_ENGINE_MAX_MESSAGES; i++) {
540 		if (!messages[i].ctx) {
541 			messages[i].ctx = client_ctx;
542 			return &messages[i];
543 		}
544 	}
545 
546 	return NULL;
547 }
548 
lm2m_message_clear_allocations(struct lwm2m_message * msg)549 void lm2m_message_clear_allocations(struct lwm2m_message *msg)
550 {
551 	if (msg->pending) {
552 		coap_pending_clear(msg->pending);
553 		msg->pending = NULL;
554 	}
555 
556 	if (msg->reply) {
557 		/* make sure we want to clear the reply */
558 		coap_reply_clear(msg->reply);
559 		msg->reply = NULL;
560 	}
561 }
562 
lwm2m_reset_message(struct lwm2m_message * msg,bool release)563 void lwm2m_reset_message(struct lwm2m_message *msg, bool release)
564 {
565 	if (!msg) {
566 		return;
567 	}
568 
569 	lm2m_message_clear_allocations(msg);
570 
571 	if (msg->ctx) {
572 		sys_slist_find_and_remove(&msg->ctx->pending_sends, &msg->node);
573 #if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED)
574 		sys_slist_find_and_remove(&msg->ctx->queued_messages, &msg->node);
575 #endif
576 	}
577 
578 	if (release) {
579 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
580 		release_output_block_ctx(&msg->out.block_ctx);
581 		release_body_encode_buffer(&msg->body_encode_buffer.data);
582 #endif
583 		(void)memset(msg, 0, sizeof(*msg));
584 	} else {
585 		msg->message_timeout_cb = NULL;
586 		(void)memset(&msg->cpkt, 0, sizeof(msg->cpkt));
587 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
588 		msg->cache_info = NULL;
589 #endif
590 	}
591 }
592 
lwm2m_init_message(struct lwm2m_message * msg)593 int lwm2m_init_message(struct lwm2m_message *msg)
594 {
595 	uint8_t tokenlen = 0U;
596 	uint8_t *token = NULL;
597 	uint8_t *body_data;
598 	uint16_t body_data_max_len;
599 	int r = 0;
600 
601 	if (!msg || !msg->ctx) {
602 		LOG_ERR("LwM2M message is invalid.");
603 		return -EINVAL;
604 	}
605 
606 	if (msg->tkl == LWM2M_MSG_TOKEN_GENERATE_NEW) {
607 		tokenlen = 8U;
608 		token = coap_next_token();
609 	} else if (msg->token && msg->tkl != 0) {
610 		tokenlen = msg->tkl;
611 		token = msg->token;
612 	}
613 
614 	lm2m_message_clear_allocations(msg);
615 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
616 	msg->cache_info = NULL;
617 #endif
618 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
619 	if (msg->body_encode_buffer.data == NULL) {
620 		/* Get new big buffer for serializing the message */
621 		r = request_body_encode_buffer(&body_data);
622 		if (r < 0) {
623 			LOG_ERR("coap packet init error: no msg buffer available");
624 			goto cleanup;
625 		}
626 		/*  in case of failure the buffer is released with this pointer */
627 		msg->body_encode_buffer.data = body_data;
628 		body_data_max_len = CONFIG_LWM2M_COAP_ENCODE_BUFFER_SIZE;
629 	} else {
630 		/* We have already a big buffer. The message is reused for each block. */
631 		body_data = msg->msg_data;
632 		body_data_max_len = sizeof(msg->msg_data);
633 	}
634 #else
635 	body_data = msg->msg_data;
636 	body_data_max_len = sizeof(msg->msg_data);
637 #endif
638 	r = coap_packet_init(&msg->cpkt, body_data, body_data_max_len, COAP_VERSION_1, msg->type,
639 			     tokenlen, token, msg->code, msg->mid);
640 	if (r < 0) {
641 		LOG_ERR("coap packet init error (err:%d)", r);
642 		goto cleanup;
643 	}
644 
645 	/* only TYPE_CON messages need pending tracking / reply handling */
646 	if (msg->type != COAP_TYPE_CON) {
647 		return 0;
648 	}
649 
650 	msg->pending = coap_pending_next_unused(msg->ctx->pendings, ARRAY_SIZE(msg->ctx->pendings));
651 	if (!msg->pending) {
652 		LOG_ERR("Unable to find a free pending to track "
653 			"retransmissions.");
654 		r = -ENOMEM;
655 		goto cleanup;
656 	}
657 
658 	r = coap_pending_init(msg->pending, &msg->cpkt, &msg->ctx->remote_addr, NULL);
659 	if (r < 0) {
660 		LOG_ERR("Unable to initialize a pending "
661 			"retransmission (err:%d).",
662 			r);
663 		goto cleanup;
664 	}
665 
666 	if (msg->reply_cb) {
667 		msg->reply =
668 			coap_reply_next_unused(msg->ctx->replies, ARRAY_SIZE(msg->ctx->replies));
669 		if (!msg->reply) {
670 			LOG_ERR("No resources for waiting for replies.");
671 			r = -ENOMEM;
672 			goto cleanup;
673 		}
674 
675 		coap_reply_clear(msg->reply);
676 		coap_reply_init(msg->reply, &msg->cpkt);
677 		msg->reply->reply = msg->reply_cb;
678 	}
679 
680 	return 0;
681 
682 cleanup:
683 	lwm2m_reset_message(msg, true);
684 
685 	return r;
686 }
687 
lwm2m_send_message_async(struct lwm2m_message * msg)688 int lwm2m_send_message_async(struct lwm2m_message *msg)
689 {
690 	int ret = 0;
691 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
692 
693 	/* check if body encode buffer is in use => packet is not yet prepared for send */
694 	if (msg->body_encode_buffer.data == msg->cpkt.data) {
695 		ret = prepare_msg_for_send(msg);
696 		if (ret) {
697 			lwm2m_reset_message(msg, true);
698 			return ret;
699 		}
700 	}
701 #endif
702 #if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED)
703 	ret = lwm2m_rd_client_connection_resume(msg->ctx);
704 	if (ret) {
705 		lwm2m_reset_message(msg, true);
706 		return ret;
707 	}
708 #endif
709 	sys_slist_append(&msg->ctx->pending_sends, &msg->node);
710 
711 	if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_ENABLED)) {
712 		engine_update_tx_time();
713 	}
714 	lwm2m_engine_wake_up();
715 	return ret;
716 }
717 
lwm2m_information_interface_send(struct lwm2m_message * msg)718 int lwm2m_information_interface_send(struct lwm2m_message *msg)
719 {
720 #if defined(CONFIG_LWM2M_QUEUE_MODE_ENABLED)
721 	int ret;
722 
723 	ret = lwm2m_rd_client_connection_resume(msg->ctx);
724 	if (ret) {
725 		lwm2m_reset_message(msg, true);
726 		return ret;
727 	}
728 
729 	if (IS_ENABLED(CONFIG_LWM2M_QUEUE_MODE_NO_MSG_BUFFERING)) {
730 		sys_slist_append(&msg->ctx->pending_sends, &msg->node);
731 		lwm2m_engine_wake_up();
732 		lwm2m_engine_connection_resume(msg->ctx);
733 		return 0;
734 	}
735 
736 	if (msg->ctx->buffer_client_messages) {
737 		sys_slist_append(&msg->ctx->queued_messages, &msg->node);
738 		lwm2m_engine_wake_up();
739 		return 0;
740 	}
741 #endif
742 
743 	return lwm2m_send_message_async(msg);
744 }
745 
lwm2m_send_empty_ack(struct lwm2m_ctx * client_ctx,uint16_t mid)746 int lwm2m_send_empty_ack(struct lwm2m_ctx *client_ctx, uint16_t mid)
747 {
748 	struct lwm2m_message *msg;
749 	int ret;
750 
751 	msg = lwm2m_get_message(client_ctx);
752 	if (!msg) {
753 		LOG_ERR("Unable to get a lwm2m message!");
754 		return -ENOMEM;
755 	}
756 
757 	msg->type = COAP_TYPE_ACK;
758 	msg->code = COAP_CODE_EMPTY;
759 	msg->mid = mid;
760 
761 	ret = lwm2m_init_message(msg);
762 	if (ret) {
763 		goto cleanup;
764 	}
765 
766 	lwm2m_send_message_async(msg);
767 
768 	return 0;
769 
770 cleanup:
771 	lwm2m_reset_message(msg, true);
772 	return ret;
773 }
774 
lwm2m_acknowledge(struct lwm2m_ctx * client_ctx)775 void lwm2m_acknowledge(struct lwm2m_ctx *client_ctx)
776 {
777 	struct lwm2m_message *request;
778 
779 	if (client_ctx == NULL || client_ctx->processed_req == NULL) {
780 		return;
781 	}
782 
783 	request = (struct lwm2m_message *)client_ctx->processed_req;
784 
785 	if (request->acknowledged) {
786 		return;
787 	}
788 
789 	if (lwm2m_send_empty_ack(client_ctx, request->mid) < 0) {
790 		return;
791 	}
792 
793 	request->acknowledged = true;
794 }
795 
lwm2m_register_payload_handler(struct lwm2m_message * msg)796 int lwm2m_register_payload_handler(struct lwm2m_message *msg)
797 {
798 	struct lwm2m_engine_obj *obj;
799 	struct lwm2m_engine_obj_inst *obj_inst;
800 	int ret;
801 	sys_slist_t *engine_obj_list = lwm2m_engine_obj_list();
802 	sys_slist_t *engine_obj_inst_list = lwm2m_engine_obj_inst_list();
803 
804 	ret = engine_put_begin(&msg->out, NULL);
805 	if (ret < 0) {
806 		return ret;
807 	}
808 
809 	SYS_SLIST_FOR_EACH_CONTAINER(engine_obj_list, obj, node) {
810 		/* Security obj MUST NOT be part of registration message */
811 		if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
812 			continue;
813 		}
814 
815 		/* Only report <OBJ_ID> when no instance available or it's
816 		 * needed to report object version.
817 		 */
818 		if (obj->instance_count == 0U || lwm2m_engine_shall_report_obj_version(obj)) {
819 			ret = engine_put_corelink(&msg->out, &LWM2M_OBJ(obj->obj_id));
820 			if (ret < 0) {
821 				return ret;
822 			}
823 
824 			if (obj->instance_count == 0U) {
825 				continue;
826 			}
827 		}
828 
829 		SYS_SLIST_FOR_EACH_CONTAINER(engine_obj_inst_list, obj_inst, node) {
830 			if (obj_inst->obj->obj_id == obj->obj_id) {
831 				ret = engine_put_corelink(
832 					&msg->out,
833 					&LWM2M_OBJ(obj_inst->obj->obj_id, obj_inst->obj_inst_id));
834 				if (ret < 0) {
835 					return ret;
836 				}
837 			}
838 		}
839 	}
840 
841 	return 0;
842 }
843 
select_writer(struct lwm2m_output_context * out,uint16_t accept)844 static int select_writer(struct lwm2m_output_context *out, uint16_t accept)
845 {
846 	switch (accept) {
847 
848 	case LWM2M_FORMAT_APP_LINK_FORMAT:
849 		out->writer = &link_format_writer;
850 		break;
851 
852 	case LWM2M_FORMAT_APP_OCTET_STREAM:
853 		out->writer = &opaque_writer;
854 		break;
855 
856 	case LWM2M_FORMAT_PLAIN_TEXT:
857 	case LWM2M_FORMAT_OMA_PLAIN_TEXT:
858 		out->writer = &plain_text_writer;
859 		break;
860 
861 #ifdef CONFIG_LWM2M_RW_OMA_TLV_SUPPORT
862 	case LWM2M_FORMAT_OMA_TLV:
863 	case LWM2M_FORMAT_OMA_OLD_TLV:
864 		out->writer = &oma_tlv_writer;
865 		break;
866 #endif
867 
868 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
869 	case LWM2M_FORMAT_OMA_JSON:
870 	case LWM2M_FORMAT_OMA_OLD_JSON:
871 		out->writer = &json_writer;
872 		break;
873 #endif
874 
875 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
876 	case LWM2M_FORMAT_APP_SEML_JSON:
877 		out->writer = &senml_json_writer;
878 		break;
879 #endif
880 
881 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT
882 	case LWM2M_FORMAT_APP_CBOR:
883 		out->writer = &cbor_writer;
884 		break;
885 #endif
886 
887 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT
888 	case LWM2M_FORMAT_APP_SENML_CBOR:
889 		out->writer = &senml_cbor_writer;
890 		break;
891 #endif
892 
893 	default:
894 		LOG_WRN("Unknown content type %u", accept);
895 		return -ECANCELED;
896 	}
897 
898 	return 0;
899 }
900 
select_reader(struct lwm2m_input_context * in,uint16_t format)901 static int select_reader(struct lwm2m_input_context *in, uint16_t format)
902 {
903 	switch (format) {
904 
905 	case LWM2M_FORMAT_APP_OCTET_STREAM:
906 		in->reader = &opaque_reader;
907 		break;
908 
909 	case LWM2M_FORMAT_PLAIN_TEXT:
910 	case LWM2M_FORMAT_OMA_PLAIN_TEXT:
911 		in->reader = &plain_text_reader;
912 		break;
913 
914 #ifdef CONFIG_LWM2M_RW_OMA_TLV_SUPPORT
915 	case LWM2M_FORMAT_OMA_TLV:
916 	case LWM2M_FORMAT_OMA_OLD_TLV:
917 		in->reader = &oma_tlv_reader;
918 		break;
919 #endif
920 
921 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
922 	case LWM2M_FORMAT_OMA_JSON:
923 	case LWM2M_FORMAT_OMA_OLD_JSON:
924 		in->reader = &json_reader;
925 		break;
926 #endif
927 
928 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
929 	case LWM2M_FORMAT_APP_SEML_JSON:
930 		in->reader = &senml_json_reader;
931 		break;
932 #endif
933 
934 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT
935 	case LWM2M_FORMAT_APP_CBOR:
936 		in->reader = &cbor_reader;
937 		break;
938 #endif
939 
940 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT
941 	case LWM2M_FORMAT_APP_SENML_CBOR:
942 		in->reader = &senml_cbor_reader;
943 		break;
944 #endif
945 	default:
946 		LOG_WRN("Unknown content type %u", format);
947 		return -ENOMSG;
948 	}
949 
950 	return 0;
951 }
952 
953 /* generic data handlers */
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)954 static int lwm2m_write_handler_opaque(struct lwm2m_engine_obj_inst *obj_inst,
955 				      struct lwm2m_engine_res *res,
956 				      struct lwm2m_engine_res_inst *res_inst,
957 				      struct lwm2m_message *msg, void *data_ptr, size_t data_len)
958 {
959 	int len = 1;
960 	bool last_pkt_block = false;
961 	int ret = 0;
962 	bool last_block = true;
963 	struct lwm2m_opaque_context opaque_ctx = {0};
964 	void *write_buf;
965 	size_t write_buf_len;
966 
967 	if (msg->in.block_ctx != NULL) {
968 		last_block = msg->in.block_ctx->last_block;
969 
970 		/* Restore the opaque context from the block context, if used. */
971 		opaque_ctx = msg->in.block_ctx->opaque;
972 	}
973 
974 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
975 	/* In case validation callback is present, write data to the temporary
976 	 * buffer first, for validation. Otherwise, write to the data buffer
977 	 * directly.
978 	 */
979 	if (res->validate_cb) {
980 		write_buf = msg->ctx->validate_buf;
981 		write_buf_len = sizeof(msg->ctx->validate_buf);
982 	} else
983 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
984 	{
985 		write_buf = data_ptr;
986 		write_buf_len = data_len;
987 	}
988 
989 	while (!last_pkt_block && len > 0) {
990 		len = engine_get_opaque(&msg->in, write_buf, MIN(data_len, write_buf_len),
991 					&opaque_ctx, &last_pkt_block);
992 		if (len <= 0) {
993 			return len;
994 		}
995 
996 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
997 		if (res->validate_cb) {
998 			ret = res->validate_cb(obj_inst->obj_inst_id, res->res_id,
999 					       res_inst->res_inst_id, write_buf, len,
1000 					       last_pkt_block && last_block, opaque_ctx.len);
1001 			if (ret < 0) {
1002 				/* -EEXIST will generate Bad Request LWM2M response. */
1003 				return -EEXIST;
1004 			}
1005 
1006 			memcpy(data_ptr, write_buf, len);
1007 		}
1008 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
1009 
1010 		if (res->post_write_cb) {
1011 			ret = res->post_write_cb(obj_inst->obj_inst_id, res->res_id,
1012 						 res_inst->res_inst_id, data_ptr, len,
1013 						 last_pkt_block && last_block, opaque_ctx.len);
1014 			if (ret < 0) {
1015 				return ret;
1016 			}
1017 		}
1018 	}
1019 
1020 	if (msg->in.block_ctx != NULL) {
1021 		msg->in.block_ctx->opaque = opaque_ctx;
1022 	}
1023 
1024 	return opaque_ctx.len;
1025 }
1026 /* 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)1027 int lwm2m_write_handler(struct lwm2m_engine_obj_inst *obj_inst, struct lwm2m_engine_res *res,
1028 			struct lwm2m_engine_res_inst *res_inst,
1029 			struct lwm2m_engine_obj_field *obj_field, struct lwm2m_message *msg)
1030 {
1031 	void *data_ptr = NULL;
1032 	size_t data_len = 0;
1033 	size_t len = 0;
1034 	size_t total_size = 0;
1035 	int64_t temp64 = 0;
1036 	int32_t temp32 = 0;
1037 	time_t temp_time = 0;
1038 	int ret = 0;
1039 	bool last_block = true;
1040 	void *write_buf;
1041 	size_t write_buf_len;
1042 
1043 	if (!obj_inst || !res || !res_inst || !obj_field || !msg) {
1044 		return -EINVAL;
1045 	}
1046 
1047 	if (LWM2M_HAS_RES_FLAG(res_inst, LWM2M_RES_DATA_FLAG_RO)) {
1048 		return -EACCES;
1049 	}
1050 
1051 	/* setup initial data elements */
1052 	data_ptr = res_inst->data_ptr;
1053 	data_len = res_inst->max_data_len;
1054 
1055 	/* allow user to override data elements via callback */
1056 	if (res->pre_write_cb) {
1057 		data_ptr = res->pre_write_cb(obj_inst->obj_inst_id, res->res_id,
1058 					     res_inst->res_inst_id, &data_len);
1059 	}
1060 
1061 	if (res->post_write_cb
1062 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
1063 	    || res->validate_cb
1064 #endif
1065 	) {
1066 		if (msg->in.block_ctx != NULL) {
1067 			/* Get block_ctx for total_size (might be zero) */
1068 			total_size = msg->in.block_ctx->ctx.total_size;
1069 			LOG_DBG("BLOCK1: total:%zu current:%zu"
1070 				" last:%u",
1071 				msg->in.block_ctx->ctx.total_size, msg->in.block_ctx->ctx.current,
1072 				msg->in.block_ctx->last_block);
1073 		}
1074 	}
1075 
1076 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
1077 	/* In case validation callback is present, write data to the temporary
1078 	 * buffer first, for validation. Otherwise, write to the data buffer
1079 	 * directly.
1080 	 */
1081 	if (res->validate_cb) {
1082 		write_buf = msg->ctx->validate_buf;
1083 		write_buf_len = sizeof(msg->ctx->validate_buf);
1084 	} else
1085 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
1086 	{
1087 		write_buf = data_ptr;
1088 		write_buf_len = data_len;
1089 	}
1090 
1091 	if (data_ptr && data_len > 0) {
1092 		switch (obj_field->data_type) {
1093 
1094 		case LWM2M_RES_TYPE_OPAQUE:
1095 			ret = lwm2m_write_handler_opaque(obj_inst, res, res_inst, msg, data_ptr,
1096 							 data_len);
1097 			len = ret;
1098 			break;
1099 
1100 		case LWM2M_RES_TYPE_STRING:
1101 			ret = engine_get_string(&msg->in, write_buf, write_buf_len);
1102 			if (ret < 0) {
1103 				break;
1104 			}
1105 
1106 			len = strlen((char *)write_buf) + 1;
1107 			break;
1108 
1109 		case LWM2M_RES_TYPE_TIME:
1110 			ret = engine_get_time(&msg->in, &temp_time);
1111 			if (ret < 0) {
1112 				break;
1113 			}
1114 
1115 			if (write_buf_len == sizeof(time_t)) {
1116 				*(time_t *)write_buf = temp_time;
1117 				len = sizeof(time_t);
1118 			} else if (write_buf_len == sizeof(uint32_t)) {
1119 				*(uint32_t *)write_buf = (uint32_t)temp_time;
1120 				len = sizeof(uint32_t);
1121 			} else {
1122 				LOG_ERR("Time resource buf len not supported %zu", write_buf_len);
1123 				ret = -EINVAL;
1124 			}
1125 
1126 			break;
1127 
1128 		case LWM2M_RES_TYPE_U32:
1129 			ret = engine_get_s64(&msg->in, &temp64);
1130 			if (ret < 0) {
1131 				break;
1132 			}
1133 
1134 			*(uint32_t *)write_buf = temp64;
1135 			len = 4;
1136 			break;
1137 
1138 		case LWM2M_RES_TYPE_U16:
1139 			ret = engine_get_s32(&msg->in, &temp32);
1140 			if (ret < 0) {
1141 				break;
1142 			}
1143 
1144 			*(uint16_t *)write_buf = temp32;
1145 			len = 2;
1146 			break;
1147 
1148 		case LWM2M_RES_TYPE_U8:
1149 			ret = engine_get_s32(&msg->in, &temp32);
1150 			if (ret < 0) {
1151 				break;
1152 			}
1153 
1154 			*(uint8_t *)write_buf = temp32;
1155 			len = 1;
1156 			break;
1157 
1158 		case LWM2M_RES_TYPE_S64:
1159 			ret = engine_get_s64(&msg->in, (int64_t *)write_buf);
1160 			len = 8;
1161 			break;
1162 
1163 		case LWM2M_RES_TYPE_S32:
1164 			ret = engine_get_s32(&msg->in, (int32_t *)write_buf);
1165 			len = 4;
1166 			break;
1167 
1168 		case LWM2M_RES_TYPE_S16:
1169 			ret = engine_get_s32(&msg->in, &temp32);
1170 			if (ret < 0) {
1171 				break;
1172 			}
1173 
1174 			*(int16_t *)write_buf = temp32;
1175 			len = 2;
1176 			break;
1177 
1178 		case LWM2M_RES_TYPE_S8:
1179 			ret = engine_get_s32(&msg->in, &temp32);
1180 			if (ret < 0) {
1181 				break;
1182 			}
1183 
1184 			*(int8_t *)write_buf = temp32;
1185 			len = 1;
1186 			break;
1187 
1188 		case LWM2M_RES_TYPE_BOOL:
1189 			ret = engine_get_bool(&msg->in, (bool *)write_buf);
1190 			len = 1;
1191 			break;
1192 
1193 		case LWM2M_RES_TYPE_FLOAT:
1194 			ret = engine_get_float(&msg->in, (double *)write_buf);
1195 			len = sizeof(double);
1196 			break;
1197 
1198 		case LWM2M_RES_TYPE_OBJLNK:
1199 			ret = engine_get_objlnk(&msg->in, (struct lwm2m_objlnk *)write_buf);
1200 			len = sizeof(struct lwm2m_objlnk);
1201 			break;
1202 
1203 		default:
1204 			LOG_ERR("unknown obj data_type %d", obj_field->data_type);
1205 			return -EINVAL;
1206 		}
1207 
1208 		if (ret < 0) {
1209 			return ret;
1210 		}
1211 	} else {
1212 		return -ENOENT;
1213 	}
1214 
1215 	if (obj_field->data_type != LWM2M_RES_TYPE_OPAQUE) {
1216 #if CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0
1217 		if (res->validate_cb) {
1218 			ret = res->validate_cb(obj_inst->obj_inst_id, res->res_id,
1219 					       res_inst->res_inst_id, write_buf, len, last_block,
1220 					       total_size);
1221 			if (ret < 0) {
1222 				/* -EEXIST will generate Bad Request LWM2M response. */
1223 				return -EEXIST;
1224 			}
1225 
1226 			if (len > data_len) {
1227 				LOG_ERR("Received data won't fit into provided "
1228 					"bufffer");
1229 				return -ENOMEM;
1230 			}
1231 
1232 			if (obj_field->data_type == LWM2M_RES_TYPE_STRING) {
1233 				strncpy(data_ptr, write_buf, data_len);
1234 			} else {
1235 				memcpy(data_ptr, write_buf, len);
1236 			}
1237 		}
1238 #endif /* CONFIG_LWM2M_ENGINE_VALIDATION_BUFFER_SIZE > 0 */
1239 
1240 		if (res->post_write_cb) {
1241 			ret = res->post_write_cb(obj_inst->obj_inst_id, res->res_id,
1242 						 res_inst->res_inst_id, data_ptr, len, last_block,
1243 						 total_size);
1244 		}
1245 	}
1246 
1247 	res_inst->data_len = len;
1248 
1249 	if (LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
1250 		lwm2m_notify_observer_path(&msg->path);
1251 	}
1252 
1253 	return ret;
1254 }
1255 
lwm2m_read_resource_data(struct lwm2m_message * msg,void * data_ptr,size_t data_len,uint8_t data_type)1256 static int lwm2m_read_resource_data(struct lwm2m_message *msg, void *data_ptr, size_t data_len,
1257 			       uint8_t data_type)
1258 {
1259 	int ret;
1260 
1261 	switch (data_type) {
1262 
1263 	case LWM2M_RES_TYPE_OPAQUE:
1264 		ret = engine_put_opaque(&msg->out, &msg->path, (uint8_t *)data_ptr, data_len);
1265 		break;
1266 
1267 	case LWM2M_RES_TYPE_STRING:
1268 		if (data_len) {
1269 			data_len -= 1; /* Remove the '\0' */
1270 		}
1271 		ret = engine_put_string(&msg->out, &msg->path, (uint8_t *)data_ptr, data_len);
1272 		break;
1273 
1274 	case LWM2M_RES_TYPE_U32:
1275 		ret = engine_put_s64(&msg->out, &msg->path, (int64_t) *(uint32_t *)data_ptr);
1276 		break;
1277 
1278 	case LWM2M_RES_TYPE_U16:
1279 		ret = engine_put_s32(&msg->out, &msg->path, (int32_t) *(uint16_t *)data_ptr);
1280 		break;
1281 
1282 	case LWM2M_RES_TYPE_U8:
1283 		ret = engine_put_s16(&msg->out, &msg->path, (int16_t) *(uint8_t *)data_ptr);
1284 		break;
1285 
1286 	case LWM2M_RES_TYPE_S64:
1287 		ret = engine_put_s64(&msg->out, &msg->path, *(int64_t *)data_ptr);
1288 		break;
1289 
1290 	case LWM2M_RES_TYPE_S32:
1291 		ret = engine_put_s32(&msg->out, &msg->path, *(int32_t *)data_ptr);
1292 		break;
1293 
1294 	case LWM2M_RES_TYPE_S16:
1295 		ret = engine_put_s16(&msg->out, &msg->path, *(int16_t *)data_ptr);
1296 		break;
1297 
1298 	case LWM2M_RES_TYPE_S8:
1299 		ret = engine_put_s8(&msg->out, &msg->path, *(int8_t *)data_ptr);
1300 		break;
1301 
1302 	case LWM2M_RES_TYPE_TIME:
1303 		if (data_len == sizeof(time_t)) {
1304 			ret = engine_put_time(&msg->out, &msg->path, *(time_t *)data_ptr);
1305 		} else if (data_len == sizeof(uint32_t)) {
1306 			ret = engine_put_time(&msg->out, &msg->path,
1307 					      (time_t) *((uint32_t *)data_ptr));
1308 		} else {
1309 			LOG_ERR("Resource time length not supported %zu", data_len);
1310 			ret = -EINVAL;
1311 		}
1312 
1313 		break;
1314 
1315 	case LWM2M_RES_TYPE_BOOL:
1316 		ret = engine_put_bool(&msg->out, &msg->path, *(bool *)data_ptr);
1317 		break;
1318 
1319 	case LWM2M_RES_TYPE_FLOAT:
1320 		ret = engine_put_float(&msg->out, &msg->path, (double *)data_ptr);
1321 		break;
1322 
1323 	case LWM2M_RES_TYPE_OBJLNK:
1324 		ret = engine_put_objlnk(&msg->out, &msg->path, (struct lwm2m_objlnk *)data_ptr);
1325 		break;
1326 
1327 	default:
1328 		LOG_ERR("unknown obj data_type %d", data_type);
1329 		ret = -EINVAL;
1330 	}
1331 
1332 	return ret;
1333 }
1334 
lwm2m_read_cached_data(struct lwm2m_message * msg,struct lwm2m_time_series_resource * cached_data,uint8_t data_type)1335 static int lwm2m_read_cached_data(struct lwm2m_message *msg,
1336 				  struct lwm2m_time_series_resource *cached_data, uint8_t data_type)
1337 {
1338 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1339 	int ret;
1340 	struct lwm2m_time_series_elem buf;
1341 	struct lwm2m_cache_read_entry *read_info;
1342 	size_t  length = lwm2m_cache_size(cached_data);
1343 
1344 	LOG_DBG("Read cached data size %u", length);
1345 
1346 	if (msg->cache_info) {
1347 		read_info = &msg->cache_info->read_info[msg->cache_info->entry_size];
1348 		/* Store original timeseries ring buffer get states for failure handling */
1349 		read_info->cache_data = cached_data;
1350 		read_info->original_get_base = cached_data->rb.get_base;
1351 		read_info->original_get_head = cached_data->rb.get_head;
1352 		read_info->original_get_tail = cached_data->rb.get_tail;
1353 		msg->cache_info->entry_size++;
1354 		if (msg->cache_info->entry_limit) {
1355 			length = MIN(length, msg->cache_info->entry_limit);
1356 			LOG_DBG("Limited number of read %d", length);
1357 		}
1358 	}
1359 
1360 	for (size_t i = 0; i < length; i++) {
1361 
1362 		if (!lwm2m_cache_read(cached_data, &buf)) {
1363 			LOG_ERR("Read operation fail");
1364 			return -ENOMEM;
1365 		}
1366 
1367 		ret = engine_put_timestamp(&msg->out, buf.t);
1368 		if (ret) {
1369 			return ret;
1370 		}
1371 
1372 		switch (data_type) {
1373 
1374 		case LWM2M_RES_TYPE_U32:
1375 			ret = engine_put_s64(&msg->out, &msg->path, (int64_t)buf.u32);
1376 			break;
1377 
1378 		case LWM2M_RES_TYPE_U16:
1379 			ret = engine_put_s32(&msg->out, &msg->path, (int32_t)buf.u16);
1380 			break;
1381 
1382 		case LWM2M_RES_TYPE_U8:
1383 			ret = engine_put_s16(&msg->out, &msg->path, (int16_t)buf.u8);
1384 			break;
1385 
1386 		case LWM2M_RES_TYPE_S64:
1387 			ret = engine_put_s64(&msg->out, &msg->path, buf.i64);
1388 			break;
1389 
1390 		case LWM2M_RES_TYPE_S32:
1391 			ret = engine_put_s32(&msg->out, &msg->path, buf.i32);
1392 			break;
1393 
1394 		case LWM2M_RES_TYPE_S16:
1395 			ret = engine_put_s16(&msg->out, &msg->path, buf.i16);
1396 			break;
1397 
1398 		case LWM2M_RES_TYPE_S8:
1399 			ret = engine_put_s8(&msg->out, &msg->path, buf.i8);
1400 			break;
1401 
1402 		case LWM2M_RES_TYPE_BOOL:
1403 			ret = engine_put_bool(&msg->out, &msg->path, buf.b);
1404 			break;
1405 
1406 		case LWM2M_RES_TYPE_TIME:
1407 			ret = engine_put_time(&msg->out, &msg->path, buf.time);
1408 			break;
1409 
1410 		default:
1411 			ret = engine_put_float(&msg->out, &msg->path, &buf.f);
1412 			break;
1413 
1414 		}
1415 
1416 		/* Validate that we really read some data */
1417 		if (ret < 0) {
1418 			LOG_ERR("Read operation fail");
1419 			return -ENOMEM;
1420 		}
1421 	}
1422 
1423 	return 0;
1424 #else
1425 	return -ENOTSUP;
1426 #endif
1427 }
1428 
lwm2m_accept_timeseries_read(struct lwm2m_message * msg,struct lwm2m_time_series_resource * cached_data)1429 static bool lwm2m_accept_timeseries_read(struct lwm2m_message *msg,
1430 					 struct lwm2m_time_series_resource *cached_data)
1431 {
1432 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
1433 	if (cached_data && msg->cache_info && lwm2m_cache_size(cached_data) &&
1434 	    msg->out.writer->put_data_timestamp) {
1435 		return true;
1436 	}
1437 #endif
1438 	return false;
1439 }
1440 
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)1441 static int lwm2m_read_handler(struct lwm2m_engine_obj_inst *obj_inst, struct lwm2m_engine_res *res,
1442 			      struct lwm2m_engine_obj_field *obj_field, struct lwm2m_message *msg)
1443 {
1444 	int i, loop_max = 1, found_values = 0;
1445 	uint16_t res_inst_id_tmp = 0U;
1446 	void *data_ptr = NULL;
1447 	struct lwm2m_time_series_resource *cached_data = NULL;
1448 	size_t data_len = 0;
1449 	struct lwm2m_obj_path temp_path;
1450 	int ret = 0;
1451 
1452 	if (!obj_inst || !res || !obj_field || !msg) {
1453 		return -EINVAL;
1454 	}
1455 	temp_path.obj_id = obj_inst->obj->obj_id;
1456 
1457 	temp_path.obj_inst_id = obj_inst->obj_inst_id;
1458 	temp_path.res_id = obj_field->res_id;
1459 	temp_path.level = LWM2M_PATH_LEVEL_RESOURCE;
1460 
1461 	loop_max = res->res_inst_count;
1462 	if (res->multi_res_inst) {
1463 		/* search for valid resource instances */
1464 		for (i = 0; i < loop_max; i++) {
1465 			if (res->res_instances[i].res_inst_id != RES_INSTANCE_NOT_CREATED) {
1466 				found_values = 1;
1467 				break;
1468 			}
1469 		}
1470 
1471 		if (!found_values) {
1472 			return -ENOENT;
1473 		}
1474 
1475 		ret = engine_put_begin_ri(&msg->out, &msg->path);
1476 		if (ret < 0) {
1477 			return ret;
1478 		}
1479 
1480 		res_inst_id_tmp = msg->path.res_inst_id;
1481 	}
1482 
1483 	for (i = 0; i < loop_max; i++) {
1484 		if (res->res_instances[i].res_inst_id == RES_INSTANCE_NOT_CREATED) {
1485 			continue;
1486 		}
1487 
1488 		if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
1489 		    msg->path.level == LWM2M_PATH_LEVEL_RESOURCE_INST &&
1490 		    msg->path.res_inst_id != res->res_instances[i].res_inst_id) {
1491 			continue;
1492 		}
1493 
1494 		if (res->res_inst_count > 1) {
1495 			msg->path.res_inst_id = res->res_instances[i].res_inst_id;
1496 		}
1497 		if (res->multi_res_inst) {
1498 			temp_path.res_inst_id = res->res_instances[i].res_inst_id;
1499 			temp_path.level = LWM2M_PATH_LEVEL_RESOURCE_INST;
1500 		}
1501 
1502 		cached_data = lwm2m_cache_entry_get_by_object(&temp_path);
1503 
1504 		if (lwm2m_accept_timeseries_read(msg, cached_data)) {
1505 			/* Content Format Writer have to support timestamp write */
1506 			ret = lwm2m_read_cached_data(msg, cached_data, obj_field->data_type);
1507 		} else {
1508 			/* setup initial data elements */
1509 			data_ptr = res->res_instances[i].data_ptr;
1510 			data_len = res->res_instances[i].data_len;
1511 
1512 			/* allow user to override data elements via callback */
1513 			if (res->read_cb) {
1514 				data_ptr =
1515 					res->read_cb(obj_inst->obj_inst_id, res->res_id,
1516 						     res->res_instances[i].res_inst_id, &data_len);
1517 			}
1518 
1519 			if (!data_ptr && data_len) {
1520 				return -ENOENT;
1521 			}
1522 
1523 			if (!data_len) {
1524 				if (obj_field->data_type != LWM2M_RES_TYPE_OPAQUE &&
1525 				    obj_field->data_type != LWM2M_RES_TYPE_STRING) {
1526 					return -ENOENT;
1527 				}
1528 				/* Only opaque and string types can be empty, and when
1529 				 * empty, we should not give pointer to potentially uninitialized
1530 				 * data to a content formatter. Give pointer to empty string
1531 				 * instead.
1532 				 */
1533 				data_ptr = "";
1534 			}
1535 			ret = lwm2m_read_resource_data(msg, data_ptr, data_len,
1536 						       obj_field->data_type);
1537 		}
1538 
1539 		/* Validate that we really read some data */
1540 		if (ret < 0) {
1541 			LOG_ERR("Read operation fail");
1542 			return -ENOMEM;
1543 		}
1544 	}
1545 
1546 	if (res->multi_res_inst) {
1547 		ret = engine_put_end_ri(&msg->out, &msg->path);
1548 		if (ret < 0) {
1549 			return ret;
1550 		}
1551 
1552 		msg->path.res_inst_id = res_inst_id_tmp;
1553 	}
1554 
1555 	return 0;
1556 }
1557 
lwm2m_delete_handler(struct lwm2m_message * msg)1558 static int lwm2m_delete_handler(struct lwm2m_message *msg)
1559 {
1560 	int ret;
1561 
1562 	if (!msg) {
1563 		return -EINVAL;
1564 	}
1565 
1566 	/* Device management interface is not allowed to delete Security and
1567 	 * Device objects instances.
1568 	 */
1569 	if (msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID ||
1570 	    msg->path.obj_id == LWM2M_OBJECT_DEVICE_ID) {
1571 		return -EPERM;
1572 	}
1573 
1574 	ret = lwm2m_delete_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
1575 	if (ret < 0) {
1576 		return ret;
1577 	}
1578 
1579 	if (!msg->ctx->bootstrap_mode) {
1580 		engine_trigger_update(true);
1581 	}
1582 
1583 	return 0;
1584 }
1585 
do_read_op(struct lwm2m_message * msg,uint16_t content_format)1586 static int do_read_op(struct lwm2m_message *msg, uint16_t content_format)
1587 {
1588 	switch (content_format) {
1589 
1590 	case LWM2M_FORMAT_APP_OCTET_STREAM:
1591 		return do_read_op_opaque(msg, content_format);
1592 
1593 	case LWM2M_FORMAT_PLAIN_TEXT:
1594 	case LWM2M_FORMAT_OMA_PLAIN_TEXT:
1595 		return do_read_op_plain_text(msg, content_format);
1596 
1597 #if defined(CONFIG_LWM2M_RW_OMA_TLV_SUPPORT)
1598 	case LWM2M_FORMAT_OMA_TLV:
1599 	case LWM2M_FORMAT_OMA_OLD_TLV:
1600 		return do_read_op_tlv(msg, content_format);
1601 #endif
1602 
1603 #if defined(CONFIG_LWM2M_RW_JSON_SUPPORT)
1604 	case LWM2M_FORMAT_OMA_JSON:
1605 	case LWM2M_FORMAT_OMA_OLD_JSON:
1606 		return do_read_op_json(msg, content_format);
1607 #endif
1608 
1609 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
1610 	case LWM2M_FORMAT_APP_SEML_JSON:
1611 		return do_read_op_senml_json(msg);
1612 #endif
1613 
1614 #if defined(CONFIG_LWM2M_RW_CBOR_SUPPORT)
1615 	case LWM2M_FORMAT_APP_CBOR:
1616 		return do_read_op_cbor(msg);
1617 #endif
1618 
1619 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
1620 	case LWM2M_FORMAT_APP_SENML_CBOR:
1621 		return do_read_op_senml_cbor(msg);
1622 #endif
1623 
1624 	default:
1625 		LOG_ERR("Unsupported content-format: %u", content_format);
1626 		return -ENOMSG;
1627 	}
1628 }
1629 
do_composite_read_op(struct lwm2m_message * msg,uint16_t content_format)1630 static int do_composite_read_op(struct lwm2m_message *msg, uint16_t content_format)
1631 {
1632 	switch (content_format) {
1633 
1634 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
1635 	case LWM2M_FORMAT_APP_SEML_JSON:
1636 		return do_composite_read_op_senml_json(msg);
1637 #endif
1638 
1639 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
1640 	case LWM2M_FORMAT_APP_SENML_CBOR:
1641 		return do_composite_read_op_senml_cbor(msg);
1642 #endif
1643 
1644 	default:
1645 		LOG_ERR("Unsupported content-format: %u", content_format);
1646 		return -ENOMSG;
1647 	}
1648 }
1649 
lwm2m_perform_read_object_instance(struct lwm2m_message * msg,struct lwm2m_engine_obj_inst * obj_inst,uint8_t * num_read)1650 static int lwm2m_perform_read_object_instance(struct lwm2m_message *msg,
1651 					      struct lwm2m_engine_obj_inst *obj_inst,
1652 					      uint8_t *num_read)
1653 {
1654 	struct lwm2m_engine_res *res = NULL;
1655 	struct lwm2m_engine_obj_field *obj_field;
1656 	int ret = 0;
1657 
1658 	while (obj_inst) {
1659 		if (!obj_inst->resources || obj_inst->resource_count == 0U) {
1660 			goto move_forward;
1661 		}
1662 
1663 		/* update the obj_inst_id as we move through the instances */
1664 		msg->path.obj_inst_id = obj_inst->obj_inst_id;
1665 
1666 		ret = engine_put_begin_oi(&msg->out, &msg->path);
1667 		if (ret < 0) {
1668 			return ret;
1669 		}
1670 
1671 		for (int index = 0; index < obj_inst->resource_count; index++) {
1672 			if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST &&
1673 			    msg->path.res_id != obj_inst->resources[index].res_id) {
1674 				continue;
1675 			}
1676 
1677 			res = &obj_inst->resources[index];
1678 			msg->path.res_id = res->res_id;
1679 			obj_field = lwm2m_get_engine_obj_field(obj_inst->obj, res->res_id);
1680 			if (!obj_field) {
1681 				ret = -ENOENT;
1682 			} else if (!LWM2M_HAS_PERM(obj_field, LWM2M_PERM_R)) {
1683 				ret = -EPERM;
1684 			} else {
1685 				/* start resource formatting */
1686 				ret = engine_put_begin_r(&msg->out, &msg->path);
1687 				if (ret < 0) {
1688 					return ret;
1689 				}
1690 
1691 				/* perform read operation on this resource */
1692 				ret = lwm2m_read_handler(obj_inst, res, obj_field, msg);
1693 				if (ret == -ENOMEM) {
1694 					/* No point continuing if there's no
1695 					 * memory left in a message.
1696 					 */
1697 					return ret;
1698 				} else if (ret < 0) {
1699 					/* ignore errors unless single read */
1700 					if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST &&
1701 					    !LWM2M_HAS_PERM(obj_field, BIT(LWM2M_FLAG_OPTIONAL))) {
1702 						LOG_ERR("READ OP: %d", ret);
1703 					}
1704 				} else {
1705 					*num_read += 1U;
1706 				}
1707 
1708 				/* end resource formatting */
1709 				ret = engine_put_end_r(&msg->out, &msg->path);
1710 				if (ret < 0) {
1711 					return ret;
1712 				}
1713 			}
1714 
1715 			/* on single read break if errors */
1716 			if (ret < 0 && msg->path.level > LWM2M_PATH_LEVEL_OBJECT_INST) {
1717 				break;
1718 			}
1719 		}
1720 
1721 move_forward:
1722 		ret = engine_put_end_oi(&msg->out, &msg->path);
1723 		if (ret < 0) {
1724 			return ret;
1725 		}
1726 
1727 		if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT) {
1728 			/* advance to the next object instance */
1729 			obj_inst = next_engine_obj_inst(msg->path.obj_id, obj_inst->obj_inst_id);
1730 		} else {
1731 			obj_inst = NULL;
1732 		}
1733 	}
1734 
1735 	return ret;
1736 }
1737 
lwm2m_perform_read_op(struct lwm2m_message * msg,uint16_t content_format)1738 int lwm2m_perform_read_op(struct lwm2m_message *msg, uint16_t content_format)
1739 {
1740 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
1741 	struct lwm2m_obj_path temp_path;
1742 	int ret = 0;
1743 	uint8_t num_read = 0U;
1744 
1745 	if (msg->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
1746 		obj_inst = get_engine_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
1747 		if (!obj_inst) {
1748 			/* When Object instace is indicated error have to be reported */
1749 			return -ENOENT;
1750 		}
1751 	} else if (msg->path.level == LWM2M_PATH_LEVEL_OBJECT) {
1752 		/* find first obj_inst with path's obj_id.
1753 		 * Path level 1 can accept NULL. It define empty payload to response.
1754 		 */
1755 		obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
1756 	}
1757 
1758 	/* set output content-format */
1759 	ret = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_CONTENT_FORMAT, content_format);
1760 	if (ret < 0) {
1761 		LOG_ERR("Error setting response content-format: %d", ret);
1762 		return ret;
1763 	}
1764 
1765 	ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
1766 	if (ret < 0) {
1767 		LOG_ERR("Error appending payload marker: %d", ret);
1768 		return ret;
1769 	}
1770 
1771 	/* store original path values so we can change them during processing */
1772 	memcpy(&temp_path, &msg->path, sizeof(temp_path));
1773 
1774 	if (engine_put_begin(&msg->out, &msg->path) < 0) {
1775 		return -ENOMEM;
1776 	}
1777 
1778 	ret = lwm2m_perform_read_object_instance(msg, obj_inst, &num_read);
1779 	if (ret < 0) {
1780 		return ret;
1781 	}
1782 
1783 	if (engine_put_end(&msg->out, &msg->path) < 0) {
1784 		return -ENOMEM;
1785 	}
1786 
1787 	/* restore original path values */
1788 	memcpy(&msg->path, &temp_path, sizeof(temp_path));
1789 
1790 	/* did not read anything even if we should have - on single item */
1791 	if (ret == 0 && num_read == 0U) {
1792 		if (msg->path.level == LWM2M_PATH_LEVEL_RESOURCE) {
1793 			return -ENOENT;
1794 		}
1795 
1796 		if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
1797 		    msg->path.level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
1798 			return -ENOENT;
1799 		}
1800 	}
1801 
1802 	return ret;
1803 }
1804 
lwm2m_discover_add_res(struct lwm2m_message * msg,struct lwm2m_engine_obj_inst * obj_inst,struct lwm2m_engine_res * res)1805 static int lwm2m_discover_add_res(struct lwm2m_message *msg, struct lwm2m_engine_obj_inst *obj_inst,
1806 				  struct lwm2m_engine_res *res)
1807 {
1808 	int ret;
1809 
1810 	ret = engine_put_corelink(
1811 		&msg->out, &LWM2M_OBJ(obj_inst->obj->obj_id, obj_inst->obj_inst_id, res->res_id));
1812 	if (ret < 0) {
1813 		return ret;
1814 	}
1815 
1816 	/* Report resource instances, if applicable. */
1817 	if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) && msg->path.level == LWM2M_PATH_LEVEL_RESOURCE &&
1818 	    res->multi_res_inst) {
1819 		for (int i = 0; i < res->res_inst_count; i++) {
1820 			struct lwm2m_engine_res_inst *res_inst = &res->res_instances[i];
1821 
1822 			if (res_inst->res_inst_id == RES_INSTANCE_NOT_CREATED) {
1823 				continue;
1824 			}
1825 
1826 			ret = engine_put_corelink(
1827 				&msg->out, &LWM2M_OBJ(obj_inst->obj->obj_id, obj_inst->obj_inst_id,
1828 						      res->res_id, res_inst->res_inst_id));
1829 			if (ret < 0) {
1830 				return ret;
1831 			}
1832 		}
1833 	}
1834 
1835 	return 0;
1836 }
1837 
lwm2m_discover_handler(struct lwm2m_message * msg,bool is_bootstrap)1838 int lwm2m_discover_handler(struct lwm2m_message *msg, bool is_bootstrap)
1839 {
1840 	struct lwm2m_engine_obj *obj;
1841 	struct lwm2m_engine_obj_inst *obj_inst;
1842 	int ret;
1843 	bool reported = false;
1844 	sys_slist_t *engine_obj_list = lwm2m_engine_obj_list();
1845 	sys_slist_t *engine_obj_inst_list = lwm2m_engine_obj_inst_list();
1846 
1847 	/* Object ID is required in Device Management Discovery (5.4.2). */
1848 	if (!is_bootstrap && (msg->path.level == LWM2M_PATH_LEVEL_NONE ||
1849 			      msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID)) {
1850 		return -EPERM;
1851 	}
1852 
1853 	/* Bootstrap discovery allows to specify at most Object ID. */
1854 	if (is_bootstrap && msg->path.level > LWM2M_PATH_LEVEL_OBJECT) {
1855 		return -EPERM;
1856 	}
1857 
1858 	/* set output content-format */
1859 	ret = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_CONTENT_FORMAT,
1860 				     LWM2M_FORMAT_APP_LINK_FORMAT);
1861 	if (ret < 0) {
1862 		LOG_ERR("Error setting response content-format: %d", ret);
1863 		return ret;
1864 	}
1865 
1866 	ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
1867 	if (ret < 0) {
1868 		return ret;
1869 	}
1870 
1871 	/*
1872 	 * Add required prefix for bootstrap discovery (5.2.7.3).
1873 	 * For device management discovery, `engine_put_begin()` adds nothing.
1874 	 */
1875 	ret = engine_put_begin(&msg->out, &msg->path);
1876 	if (ret < 0) {
1877 		return ret;
1878 	}
1879 
1880 	SYS_SLIST_FOR_EACH_CONTAINER(engine_obj_list, obj, node) {
1881 		/* Skip unrelated objects */
1882 		if (msg->path.level > 0 && msg->path.obj_id != obj->obj_id) {
1883 			continue;
1884 		}
1885 
1886 		/* For bootstrap discover, only report object ID when no
1887 		 * instance is available or it's needed to report object
1888 		 * version.
1889 		 * For device management discovery, only report object ID with
1890 		 * attributes if object ID (alone) was provided.
1891 		 */
1892 		if ((is_bootstrap &&
1893 		     (obj->instance_count == 0U || lwm2m_engine_shall_report_obj_version(obj))) ||
1894 		    (!is_bootstrap && msg->path.level == LWM2M_PATH_LEVEL_OBJECT)) {
1895 			ret = engine_put_corelink(&msg->out, &LWM2M_OBJ(obj->obj_id));
1896 			if (ret < 0) {
1897 				return ret;
1898 			}
1899 
1900 			reported = true;
1901 
1902 			if (obj->instance_count == 0U) {
1903 				continue;
1904 			}
1905 		}
1906 
1907 		SYS_SLIST_FOR_EACH_CONTAINER(engine_obj_inst_list, obj_inst, node) {
1908 			if (obj_inst->obj->obj_id != obj->obj_id) {
1909 				continue;
1910 			}
1911 
1912 			/* Skip unrelated object instance. */
1913 			if (msg->path.level > LWM2M_PATH_LEVEL_OBJECT &&
1914 			    msg->path.obj_inst_id != obj_inst->obj_inst_id) {
1915 				continue;
1916 			}
1917 
1918 			/* Report object instances only if no Resource ID is
1919 			 * provided.
1920 			 */
1921 			if (msg->path.level <= LWM2M_PATH_LEVEL_OBJECT_INST) {
1922 				ret = engine_put_corelink(
1923 					&msg->out,
1924 					&LWM2M_OBJ(obj_inst->obj->obj_id, obj_inst->obj_inst_id));
1925 				if (ret < 0) {
1926 					return ret;
1927 				}
1928 
1929 				reported = true;
1930 			}
1931 
1932 			/* Do not report resources in bootstrap discovery. */
1933 			if (is_bootstrap) {
1934 				continue;
1935 			}
1936 
1937 			for (int i = 0; i < obj_inst->resource_count; i++) {
1938 				/* Skip unrelated resources. */
1939 				if (msg->path.level == LWM2M_PATH_LEVEL_RESOURCE &&
1940 				    msg->path.res_id != obj_inst->resources[i].res_id) {
1941 					continue;
1942 				}
1943 
1944 				ret = lwm2m_discover_add_res(msg, obj_inst,
1945 							     &obj_inst->resources[i]);
1946 				if (ret < 0) {
1947 					return ret;
1948 				}
1949 
1950 				reported = true;
1951 			}
1952 		}
1953 	}
1954 
1955 	return reported ? 0 : -ENOENT;
1956 }
1957 
do_discover_op(struct lwm2m_message * msg,uint16_t content_format)1958 static int do_discover_op(struct lwm2m_message *msg, uint16_t content_format)
1959 {
1960 	switch (content_format) {
1961 	case LWM2M_FORMAT_APP_LINK_FORMAT:
1962 		return do_discover_op_link_format(msg, msg->ctx->bootstrap_mode);
1963 
1964 	default:
1965 		LOG_ERR("Unsupported format: %u", content_format);
1966 		return -ENOMSG;
1967 	}
1968 }
1969 
do_write_op(struct lwm2m_message * msg,uint16_t format)1970 static int do_write_op(struct lwm2m_message *msg, uint16_t format)
1971 {
1972 	int r;
1973 
1974 	switch (format) {
1975 
1976 	case LWM2M_FORMAT_APP_OCTET_STREAM:
1977 		r = do_write_op_opaque(msg);
1978 		break;
1979 
1980 	case LWM2M_FORMAT_PLAIN_TEXT:
1981 	case LWM2M_FORMAT_OMA_PLAIN_TEXT:
1982 		r = do_write_op_plain_text(msg);
1983 		break;
1984 
1985 #ifdef CONFIG_LWM2M_RW_OMA_TLV_SUPPORT
1986 	case LWM2M_FORMAT_OMA_TLV:
1987 	case LWM2M_FORMAT_OMA_OLD_TLV:
1988 		r = do_write_op_tlv(msg);
1989 		break;
1990 #endif
1991 
1992 #ifdef CONFIG_LWM2M_RW_JSON_SUPPORT
1993 	case LWM2M_FORMAT_OMA_JSON:
1994 	case LWM2M_FORMAT_OMA_OLD_JSON:
1995 		r = do_write_op_json(msg);
1996 		break;
1997 #endif
1998 
1999 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
2000 	case LWM2M_FORMAT_APP_SEML_JSON:
2001 		r = do_write_op_senml_json(msg);
2002 		break;
2003 #endif
2004 
2005 #ifdef CONFIG_LWM2M_RW_CBOR_SUPPORT
2006 	case LWM2M_FORMAT_APP_CBOR:
2007 		r = do_write_op_cbor(msg);
2008 		break;
2009 #endif
2010 
2011 #ifdef CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT
2012 	case LWM2M_FORMAT_APP_SENML_CBOR:
2013 		r = do_write_op_senml_cbor(msg);
2014 		break;
2015 #endif
2016 
2017 	default:
2018 		LOG_ERR("Unsupported format: %u", format);
2019 		r = -ENOMSG;
2020 		break;
2021 	}
2022 
2023 	return r;
2024 }
2025 
parse_write_op(struct lwm2m_message * msg,uint16_t format)2026 static int parse_write_op(struct lwm2m_message *msg, uint16_t format)
2027 {
2028 	int block_opt, block_num;
2029 	struct lwm2m_block_context *block_ctx = NULL;
2030 	enum coap_block_size block_size;
2031 	bool last_block = false;
2032 	int r;
2033 	uint16_t payload_len = 0U;
2034 	const uint8_t *payload_start;
2035 
2036 	/* setup incoming data */
2037 	payload_start = coap_packet_get_payload(msg->in.in_cpkt, &payload_len);
2038 	if (payload_len > 0) {
2039 		msg->in.offset = payload_start - msg->in.in_cpkt->data;
2040 	} else {
2041 		msg->in.offset = msg->in.in_cpkt->offset;
2042 	}
2043 
2044 	/* Check for block transfer */
2045 	block_opt = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1);
2046 	if (block_opt > 0) {
2047 		last_block = !GET_MORE(block_opt);
2048 
2049 		/* RFC7252: 4.6. Message Size */
2050 		block_size = GET_BLOCK_SIZE(block_opt);
2051 		if (!last_block && coap_block_size_to_bytes(block_size) > payload_len) {
2052 			LOG_DBG("Trailing payload is discarded!");
2053 			return -EFBIG;
2054 		}
2055 
2056 		block_num = GET_BLOCK_NUM(block_opt);
2057 
2058 		/* Try to retrieve existing block context. If one not exists,
2059 		 * and we've received first block, allocate new context.
2060 		 */
2061 		r = get_block_ctx(&msg->path, &block_ctx);
2062 		if (r < 0 && block_num == 0) {
2063 			r = init_block_ctx(&msg->path, &block_ctx);
2064 		}
2065 
2066 		if (r < 0) {
2067 			LOG_ERR("Cannot find block context");
2068 			return r;
2069 		}
2070 
2071 		msg->in.block_ctx = block_ctx;
2072 
2073 		if (block_num < block_ctx->expected) {
2074 			LOG_WRN("Block already handled %d, expected %d", block_num,
2075 				block_ctx->expected);
2076 			(void)coap_header_set_code(msg->out.out_cpkt, COAP_RESPONSE_CODE_CONTINUE);
2077 			/* Respond with the original Block1 header, original Ack migh have been
2078 			 * lost, and this is a retry. We don't know the original response, but
2079 			 * since it is handled, just assume we can continue.
2080 			 */
2081 			(void)coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_BLOCK1,
2082 						     block_opt);
2083 			return 0;
2084 		}
2085 		if (block_num > block_ctx->expected) {
2086 			LOG_WRN("Block out of order %d, expected %d", block_num,
2087 				block_ctx->expected);
2088 			r = -EFAULT;
2089 			return r;
2090 		}
2091 		r = coap_update_from_block(msg->in.in_cpkt, &block_ctx->ctx);
2092 		if (r < 0) {
2093 			LOG_ERR("Error from block update: %d", r);
2094 			return r;
2095 		}
2096 
2097 		block_ctx->last_block = last_block;
2098 
2099 		/* Initial block sent by the server might be larger than
2100 		 * our block size therefore it is needed to take this
2101 		 * into account when calculating next expected block
2102 		 * number.
2103 		 */
2104 		block_ctx->expected += GET_BLOCK_SIZE(block_opt) - block_ctx->ctx.block_size + 1;
2105 	}
2106 
2107 	r = do_write_op(msg, format);
2108 
2109 	/* Handle blockwise 1 (Part 2): Append BLOCK1 option / free context */
2110 	if (block_ctx) {
2111 		if (r >= 0) {
2112 			/* Add block1 option to response.
2113 			 * As RFC7959 Section-2.3, More flag is off, because we have already
2114 			 * written the data.
2115 			 */
2116 			r = coap_append_block1_option(msg->out.out_cpkt, &block_ctx->ctx);
2117 			if (r < 0) {
2118 				/* report as internal server error */
2119 				LOG_DBG("Fail adding block1 option: %d", r);
2120 				r = -EINVAL;
2121 			}
2122 			if (!last_block) {
2123 				r = coap_header_set_code(msg->out.out_cpkt,
2124 							 COAP_RESPONSE_CODE_CONTINUE);
2125 				if (r < 0) {
2126 					LOG_DBG("Failed to modify response code");
2127 					r = -EINVAL;
2128 				}
2129 			}
2130 		}
2131 		if (r < 0 || last_block) {
2132 			/* Free context when finished or when there is error */
2133 			free_block_ctx(block_ctx);
2134 		}
2135 	}
2136 
2137 	return r;
2138 }
2139 
do_composite_write_op(struct lwm2m_message * msg,uint16_t format)2140 static int do_composite_write_op(struct lwm2m_message *msg, uint16_t format)
2141 {
2142 	uint16_t payload_len = 0U;
2143 	const uint8_t *payload_start;
2144 
2145 	/* setup incoming data */
2146 	payload_start = coap_packet_get_payload(msg->in.in_cpkt, &payload_len);
2147 	if (payload_len > 0) {
2148 		msg->in.offset = payload_start - msg->in.in_cpkt->data;
2149 	} else {
2150 		msg->in.offset = msg->in.in_cpkt->offset;
2151 	}
2152 
2153 	if (coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_BLOCK1) >= 0) {
2154 		return -ENOTSUP;
2155 	}
2156 
2157 	switch (format) {
2158 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
2159 	case LWM2M_FORMAT_APP_SEML_JSON:
2160 		return do_write_op_senml_json(msg);
2161 #endif
2162 
2163 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
2164 	case LWM2M_FORMAT_APP_SENML_CBOR:
2165 		return do_write_op_senml_cbor(msg);
2166 #endif
2167 
2168 	default:
2169 		LOG_ERR("Unsupported format: %u", format);
2170 		return -ENOMSG;
2171 	}
2172 }
2173 
lwm2m_engine_path_included(uint8_t code,bool bootstrap_mode)2174 static bool lwm2m_engine_path_included(uint8_t code, bool bootstrap_mode)
2175 {
2176 	switch (code & COAP_REQUEST_MASK) {
2177 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
2178 	case COAP_METHOD_DELETE:
2179 	case COAP_METHOD_GET:
2180 		if (bootstrap_mode) {
2181 			return false;
2182 		}
2183 		break;
2184 #endif
2185 	case COAP_METHOD_FETCH:
2186 	/* Composite Read operation */
2187 	case COAP_METHOD_IPATCH:
2188 		/* Composite write operation */
2189 		return false;
2190 	default:
2191 		break;
2192 	}
2193 	return true;
2194 }
2195 
lwm2m_engine_default_content_format(uint16_t * accept_format)2196 static int lwm2m_engine_default_content_format(uint16_t *accept_format)
2197 {
2198 	if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1)) {
2199 		/* Select content format use SenML CBOR when it possible */
2200 		if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)) {
2201 			LOG_DBG("No accept option given. Assume SenML CBOR.");
2202 			*accept_format = LWM2M_FORMAT_APP_SENML_CBOR;
2203 		} else if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)) {
2204 			LOG_DBG("No accept option given. Assume SenML Json.");
2205 			*accept_format = LWM2M_FORMAT_APP_SEML_JSON;
2206 		} else if (IS_ENABLED(CONFIG_LWM2M_RW_CBOR_SUPPORT)) {
2207 			LOG_DBG("No accept option given. Assume CBOR.");
2208 			*accept_format = LWM2M_FORMAT_APP_CBOR;
2209 		} else {
2210 			LOG_ERR("CBOR, SenML CBOR or SenML JSON is not supported");
2211 			return -ENOTSUP;
2212 		}
2213 	} else if (IS_ENABLED(CONFIG_LWM2M_RW_OMA_TLV_SUPPORT)) {
2214 		LOG_DBG("No accept option given. Assume OMA TLV.");
2215 		*accept_format = LWM2M_FORMAT_OMA_TLV;
2216 	} else {
2217 		LOG_ERR("No default content format is set");
2218 		return -ENOTSUP;
2219 	}
2220 
2221 	return 0;
2222 }
2223 
lwm2m_exec_handler(struct lwm2m_message * msg)2224 static int lwm2m_exec_handler(struct lwm2m_message *msg)
2225 {
2226 	struct lwm2m_engine_obj_inst *obj_inst;
2227 	struct lwm2m_engine_res *res = NULL;
2228 	int ret;
2229 	uint8_t *args;
2230 	uint16_t args_len;
2231 
2232 	if (!msg) {
2233 		return -EINVAL;
2234 	}
2235 
2236 	ret = path_to_objs(&msg->path, &obj_inst, NULL, &res, NULL);
2237 	if (ret < 0) {
2238 		return ret;
2239 	}
2240 
2241 	args = (uint8_t *)coap_packet_get_payload(msg->in.in_cpkt, &args_len);
2242 
2243 	if (res->execute_cb) {
2244 		return res->execute_cb(obj_inst->obj_inst_id, args, args_len);
2245 	}
2246 
2247 	/* TODO: something else to handle for execute? */
2248 	return -ENOENT;
2249 }
2250 
handle_request(struct coap_packet * request,struct lwm2m_message * msg)2251 static int handle_request(struct coap_packet *request, struct lwm2m_message *msg)
2252 {
2253 	int r;
2254 	uint8_t code;
2255 	struct coap_option options[4];
2256 	struct lwm2m_engine_obj *obj = NULL;
2257 	uint8_t token[8];
2258 	uint8_t tkl = 0U;
2259 	uint16_t format = LWM2M_FORMAT_NONE, accept;
2260 	int observe = -1; /* default to -1, 0 = ENABLE, 1 = DISABLE */
2261 
2262 	/* set CoAP request / message */
2263 	msg->in.in_cpkt = request;
2264 	msg->out.out_cpkt = &msg->cpkt;
2265 
2266 	/* set default reader/writer */
2267 	msg->in.reader = &plain_text_reader;
2268 	msg->out.writer = &plain_text_writer;
2269 
2270 	code = coap_header_get_code(msg->in.in_cpkt);
2271 
2272 	/* setup response token */
2273 	tkl = coap_header_get_token(msg->in.in_cpkt, token);
2274 	if (tkl) {
2275 		msg->tkl = tkl;
2276 		msg->token = token;
2277 	}
2278 
2279 	if (IS_ENABLED(CONFIG_LWM2M_GATEWAY_OBJ_SUPPORT)) {
2280 		r = lwm2m_gw_handle_req(msg);
2281 		if (r == 0) {
2282 			return 0;
2283 		} else if (r != -ENOENT) {
2284 			goto error;
2285 		}
2286 	}
2287 
2288 	/* parse the URL path into components */
2289 	r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_URI_PATH, options, ARRAY_SIZE(options));
2290 	if (r < 0) {
2291 		goto error;
2292 	}
2293 
2294 	/* Treat empty URI path option as is there were no option - this will be
2295 	 * represented as a level "zero" in the path structure.
2296 	 */
2297 	if (r == 1 && options[0].len == 0) {
2298 		r = 0;
2299 	}
2300 
2301 	if (r == 0 && lwm2m_engine_path_included(code, msg->ctx->bootstrap_mode)) {
2302 		/* No URI path or empty URI path option - allowed only during
2303 		 * bootstrap or CoAP Fetch or iPATCH.
2304 		 */
2305 
2306 		r = -EPERM;
2307 		goto error;
2308 	}
2309 
2310 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
2311 	/* check for bootstrap-finish */
2312 	if ((code & COAP_REQUEST_MASK) == COAP_METHOD_POST && r == 1 &&
2313 	    strncmp(options[0].value, "bs", options[0].len) == 0) {
2314 		engine_bootstrap_finish();
2315 
2316 		msg->code = COAP_RESPONSE_CODE_CHANGED;
2317 
2318 		r = lwm2m_init_message(msg);
2319 		if (r < 0) {
2320 			goto error;
2321 		}
2322 
2323 		return 0;
2324 	}
2325 #endif
2326 
2327 	r = coap_options_to_path(options, r, &msg->path);
2328 	if (r < 0) {
2329 		r = -ENOENT;
2330 		goto error;
2331 	}
2332 
2333 	/* read Content Format / setup in.reader */
2334 	r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_CONTENT_FORMAT, options, 1);
2335 	if (r > 0) {
2336 		format = coap_option_value_to_int(&options[0]);
2337 		r = select_reader(&msg->in, format);
2338 		if (r < 0) {
2339 			goto error;
2340 		}
2341 	}
2342 
2343 	/* read Accept / setup out.writer */
2344 	r = coap_find_options(msg->in.in_cpkt, COAP_OPTION_ACCEPT, options, 1);
2345 	if (r > 0) {
2346 		accept = coap_option_value_to_int(&options[0]);
2347 	} else {
2348 		/* Select Default based LWM2M_VERSION */
2349 		r = lwm2m_engine_default_content_format(&accept);
2350 		if (r) {
2351 			goto error;
2352 		}
2353 	}
2354 
2355 	r = select_writer(&msg->out, accept);
2356 	if (r < 0) {
2357 		goto error;
2358 	}
2359 
2360 	/* Do Only Object find if path have been parsed */
2361 	if (lwm2m_engine_path_included(code, msg->ctx->bootstrap_mode)) {
2362 		if (!(msg->ctx->bootstrap_mode && msg->path.level == LWM2M_PATH_LEVEL_NONE)) {
2363 			/* find registered obj */
2364 			obj = get_engine_obj(msg->path.obj_id);
2365 			if (!obj) {
2366 				/* No matching object found - ignore request */
2367 				r = -ENOENT;
2368 				goto error;
2369 			}
2370 		}
2371 	}
2372 
2373 	/* set the operation */
2374 	switch (code & COAP_REQUEST_MASK) {
2375 
2376 	case COAP_METHOD_GET:
2377 		/*
2378 		 * LwM2M V1_0_1-20170704-A, table 25,
2379 		 * Discover: CoAP GET + accept=LWM2M_FORMAT_APP_LINK_FORMAT
2380 		 */
2381 		if (accept == LWM2M_FORMAT_APP_LINK_FORMAT) {
2382 			msg->operation = LWM2M_OP_DISCOVER;
2383 			accept = LWM2M_FORMAT_APP_LINK_FORMAT;
2384 		} else {
2385 			msg->operation = LWM2M_OP_READ;
2386 		}
2387 
2388 		/* check for observe */
2389 		observe = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_OBSERVE);
2390 		msg->code = COAP_RESPONSE_CODE_CONTENT;
2391 		break;
2392 
2393 	case COAP_METHOD_FETCH:
2394 		msg->operation = LWM2M_OP_READ;
2395 		/* check for observe */
2396 		observe = coap_get_option_int(msg->in.in_cpkt, COAP_OPTION_OBSERVE);
2397 		msg->code = COAP_RESPONSE_CODE_CONTENT;
2398 		break;
2399 
2400 	case COAP_METHOD_IPATCH:
2401 		msg->operation = LWM2M_OP_WRITE;
2402 		msg->code = COAP_RESPONSE_CODE_CHANGED;
2403 		break;
2404 
2405 	case COAP_METHOD_POST:
2406 		if (msg->path.level == 1U) {
2407 			/* create an object instance */
2408 			msg->operation = LWM2M_OP_CREATE;
2409 			msg->code = COAP_RESPONSE_CODE_CREATED;
2410 		} else if (msg->path.level == 2U) {
2411 			/* write values to an object instance */
2412 			msg->operation = LWM2M_OP_WRITE;
2413 			msg->code = COAP_RESPONSE_CODE_CHANGED;
2414 		} else {
2415 			msg->operation = LWM2M_OP_EXECUTE;
2416 			msg->code = COAP_RESPONSE_CODE_CHANGED;
2417 		}
2418 
2419 		break;
2420 
2421 	case COAP_METHOD_PUT:
2422 		/* write attributes if content-format is absent */
2423 		if (format == LWM2M_FORMAT_NONE) {
2424 			msg->operation = LWM2M_OP_WRITE_ATTR;
2425 		} else {
2426 			msg->operation = LWM2M_OP_WRITE;
2427 		}
2428 
2429 		msg->code = COAP_RESPONSE_CODE_CHANGED;
2430 		break;
2431 
2432 	case COAP_METHOD_DELETE:
2433 		msg->operation = LWM2M_OP_DELETE;
2434 		msg->code = COAP_RESPONSE_CODE_DELETED;
2435 		break;
2436 
2437 	default:
2438 		break;
2439 	}
2440 
2441 	/* render CoAP packet header */
2442 	r = lwm2m_init_message(msg);
2443 	if (r < 0) {
2444 		goto error;
2445 	}
2446 
2447 #if defined(CONFIG_LWM2M_ACCESS_CONTROL_ENABLE)
2448 	r = access_control_check_access(msg->path.obj_id, msg->path.obj_inst_id,
2449 					msg->ctx->srv_obj_inst, msg->operation,
2450 					msg->ctx->bootstrap_mode);
2451 	if (r < 0) {
2452 		LOG_ERR("Access denied - Server obj %u does not have proper access to "
2453 			"resource",
2454 			msg->ctx->srv_obj_inst);
2455 		goto error;
2456 	}
2457 #endif
2458 	if (msg->path.level > LWM2M_PATH_LEVEL_NONE &&
2459 	    msg->path.obj_id == LWM2M_OBJECT_SECURITY_ID && !msg->ctx->bootstrap_mode) {
2460 		r = -EACCES;
2461 		goto error;
2462 	}
2463 
2464 	switch (msg->operation) {
2465 
2466 	case LWM2M_OP_READ:
2467 		if (observe >= 0) {
2468 			/* Validate That Token is valid for Observation */
2469 			if (!msg->token) {
2470 				LOG_ERR("OBSERVE request missing token");
2471 				r = -EINVAL;
2472 				goto error;
2473 			}
2474 
2475 			if ((code & COAP_REQUEST_MASK) == COAP_METHOD_GET) {
2476 				/* Normal Observation Request or Cancel */
2477 				r = lwm2m_engine_observation_handler(msg, observe, accept,
2478 									false);
2479 				if (r < 0) {
2480 					goto error;
2481 				}
2482 
2483 				r = do_read_op(msg, accept);
2484 			} else {
2485 				/* Composite Observation request & cancel handler */
2486 				r = lwm2m_engine_observation_handler(msg, observe, accept,
2487 									true);
2488 				if (r < 0) {
2489 					goto error;
2490 				}
2491 			}
2492 		} else {
2493 			if ((code & COAP_REQUEST_MASK) == COAP_METHOD_GET) {
2494 				r = do_read_op(msg, accept);
2495 			} else {
2496 				r = do_composite_read_op(msg, accept);
2497 			}
2498 		}
2499 		break;
2500 
2501 	case LWM2M_OP_DISCOVER:
2502 		r = do_discover_op(msg, accept);
2503 		break;
2504 
2505 	case LWM2M_OP_WRITE:
2506 	case LWM2M_OP_CREATE:
2507 		if ((code & COAP_REQUEST_MASK) == COAP_METHOD_IPATCH) {
2508 			/* iPATCH is for Composite purpose */
2509 			r = do_composite_write_op(msg, format);
2510 		} else {
2511 			/* Single resource write Operation */
2512 			r = parse_write_op(msg, format);
2513 		}
2514 #if defined(CONFIG_LWM2M_ACCESS_CONTROL_ENABLE)
2515 		if (msg->operation == LWM2M_OP_CREATE && r >= 0) {
2516 			access_control_add(msg->path.obj_id, msg->path.obj_inst_id,
2517 						msg->ctx->srv_obj_inst);
2518 		}
2519 #endif
2520 		break;
2521 
2522 	case LWM2M_OP_WRITE_ATTR:
2523 		r = lwm2m_write_attr_handler(obj, msg);
2524 		break;
2525 
2526 	case LWM2M_OP_EXECUTE:
2527 		r = lwm2m_exec_handler(msg);
2528 		break;
2529 
2530 	case LWM2M_OP_DELETE:
2531 #if defined(CONFIG_LWM2M_RD_CLIENT_SUPPORT_BOOTSTRAP)
2532 		if (msg->ctx->bootstrap_mode) {
2533 			r = bootstrap_delete(msg);
2534 			break;
2535 		}
2536 #endif
2537 		r = lwm2m_delete_handler(msg);
2538 		break;
2539 
2540 	default:
2541 		LOG_ERR("Unknown operation: %u", msg->operation);
2542 		r = -EINVAL;
2543 	}
2544 
2545 	if (r < 0) {
2546 		goto error;
2547 	}
2548 
2549 	return 0;
2550 
2551 error:
2552 	lwm2m_reset_message(msg, false);
2553 	if (r == -ENOENT) {
2554 		msg->code = COAP_RESPONSE_CODE_NOT_FOUND;
2555 	} else if (r == -EPERM) {
2556 		msg->code = COAP_RESPONSE_CODE_NOT_ALLOWED;
2557 	} else if (r == -EEXIST) {
2558 		msg->code = COAP_RESPONSE_CODE_BAD_REQUEST;
2559 	} else if (r == -EFAULT) {
2560 		msg->code = COAP_RESPONSE_CODE_INCOMPLETE;
2561 	} else if (r == -EFBIG) {
2562 		msg->code = COAP_RESPONSE_CODE_REQUEST_TOO_LARGE;
2563 	} else if (r == -ENOTSUP) {
2564 		msg->code = COAP_RESPONSE_CODE_NOT_IMPLEMENTED;
2565 	} else if (r == -ENOMSG) {
2566 		msg->code = COAP_RESPONSE_CODE_UNSUPPORTED_CONTENT_FORMAT;
2567 	} else if (r == -EACCES) {
2568 		msg->code = COAP_RESPONSE_CODE_UNAUTHORIZED;
2569 	} else if (r == -ECANCELED) {
2570 		msg->code = COAP_RESPONSE_CODE_NOT_ACCEPTABLE;
2571 	} else {
2572 		/* Failed to handle the request */
2573 		msg->code = COAP_RESPONSE_CODE_INTERNAL_ERROR;
2574 	}
2575 
2576 	r = lwm2m_init_message(msg);
2577 	if (r < 0) {
2578 		LOG_ERR("Error recreating message: %d", r);
2579 	}
2580 
2581 	return 0;
2582 }
2583 
lwm2m_response_promote_to_con(struct lwm2m_message * msg)2584 static int lwm2m_response_promote_to_con(struct lwm2m_message *msg)
2585 {
2586 	int ret;
2587 
2588 	msg->type = COAP_TYPE_CON;
2589 	msg->mid = coap_next_id();
2590 
2591 	/* Since the response CoAP packet is already generated at this point,
2592 	 * tweak the specific fields manually:
2593 	 * - CoAP message type (byte 0, bits 2 and 3)
2594 	 * - CoAP message id (bytes 2 and 3)
2595 	 */
2596 	msg->cpkt.data[0] &= ~(0x3 << 4);
2597 	msg->cpkt.data[0] |= (msg->type & 0x3) << 4;
2598 	msg->cpkt.data[2] = msg->mid >> 8;
2599 	msg->cpkt.data[3] = (uint8_t)msg->mid;
2600 
2601 	if (msg->pending) {
2602 		coap_pending_clear(msg->pending);
2603 	}
2604 
2605 	/* Add the packet to the pending list. */
2606 	msg->pending = coap_pending_next_unused(msg->ctx->pendings, ARRAY_SIZE(msg->ctx->pendings));
2607 	if (!msg->pending) {
2608 		LOG_ERR("Unable to find a free pending to track "
2609 			"retransmissions.");
2610 		return -ENOMEM;
2611 	}
2612 
2613 	ret = coap_pending_init(msg->pending, &msg->cpkt, &msg->ctx->remote_addr, NULL);
2614 	if (ret < 0) {
2615 		LOG_ERR("Unable to initialize a pending "
2616 			"retransmission (err:%d).",
2617 			ret);
2618 	}
2619 
2620 	return ret;
2621 }
2622 
find_ongoing_block2_tx(void)2623 static struct lwm2m_message *find_ongoing_block2_tx(void)
2624 {
2625 	/* TODO: I could try to check if there is Request-Tags attached, and then match queries
2626 	 * for those, but currently popular LwM2M servers don't attach those tags, so in reality
2627 	 * I have no way of properly matching query with BLOCK2 option to a previous query.
2628 	 * Therefore we can only support one ongoing BLOCK2 transfer and assume all BLOCK2 requests
2629 	 * are part of currently ongoing one.
2630 	 */
2631 	return ongoing_block2_tx;
2632 }
2633 
clear_ongoing_block2_tx(void)2634 static void clear_ongoing_block2_tx(void)
2635 {
2636 	if (ongoing_block2_tx) {
2637 		LOG_DBG("clear");
2638 		lwm2m_reset_message(ongoing_block2_tx, true);
2639 		ongoing_block2_tx = NULL;
2640 	}
2641 }
2642 
handle_ongoing_block2_tx(struct lwm2m_message * msg,struct coap_packet * cpkt)2643 static void handle_ongoing_block2_tx(struct lwm2m_message *msg, struct coap_packet *cpkt)
2644 {
2645 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
2646 	int r;
2647 	uint8_t block;
2648 
2649 	r = coap_get_block2_option(cpkt, &block);
2650 	if (r < 0) {
2651 		LOG_ERR("Failed to parse BLOCK2");
2652 		return;
2653 	}
2654 
2655 	msg->in.in_cpkt = cpkt;
2656 
2657 	r = build_msg_block_for_send(msg, block);
2658 	if (r < 0) {
2659 		clear_ongoing_block2_tx();
2660 		LOG_ERR("Unable to build next block of lwm2m message! r=%d", r);
2661 		return;
2662 	}
2663 
2664 	r = lwm2m_send_message_async(msg);
2665 	if (r < 0) {
2666 		clear_ongoing_block2_tx();
2667 		LOG_ERR("Unable to send next block of lwm2m message!");
2668 		return;
2669 	}
2670 #endif
2671 }
2672 
lwm2m_udp_receive(struct lwm2m_ctx * client_ctx,uint8_t * buf,uint16_t buf_len,struct sockaddr * from_addr)2673 void lwm2m_udp_receive(struct lwm2m_ctx *client_ctx, uint8_t *buf, uint16_t buf_len,
2674 		       struct sockaddr *from_addr)
2675 {
2676 	struct lwm2m_message *msg = NULL;
2677 	struct coap_pending *pending;
2678 	struct coap_reply *reply;
2679 	struct coap_packet response;
2680 	int r;
2681 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
2682 	bool more_blocks = false;
2683 	uint8_t block_num;
2684 	uint8_t last_block_num;
2685 #endif
2686 	bool has_block2;
2687 
2688 	r = coap_packet_parse(&response, buf, buf_len, NULL, 0);
2689 	if (r < 0) {
2690 		LOG_ERR("Invalid data received (err:%d)", r);
2691 		return;
2692 	}
2693 
2694 	has_block2 = coap_get_option_int(&response, COAP_OPTION_BLOCK2) > 0 ? true : false;
2695 	pending = coap_pending_received(&response, client_ctx->pendings,
2696 					ARRAY_SIZE(client_ctx->pendings));
2697 	if (pending && coap_header_get_type(&response) == COAP_TYPE_ACK) {
2698 		msg = find_msg(pending, NULL);
2699 		if (msg == NULL) {
2700 			LOG_DBG("Orphaned pending %p.", pending);
2701 			coap_pending_clear(pending);
2702 			return;
2703 		}
2704 
2705 		msg->acknowledged = true;
2706 
2707 		if (msg->reply == NULL) {
2708 			/* No response expected, release the message. */
2709 			lwm2m_reset_message(msg, true);
2710 			return;
2711 		}
2712 
2713 		/* If the original message was a request and an empty
2714 		 * ACK was received, expect separate response later.
2715 		 */
2716 		if ((msg->code >= COAP_METHOD_GET) && (msg->code <= COAP_METHOD_DELETE) &&
2717 		    (coap_header_get_code(&response) == COAP_CODE_EMPTY)) {
2718 			LOG_DBG("Empty ACK, expect separate response.");
2719 			return;
2720 		}
2721 	}
2722 
2723 	reply = coap_response_received(&response, from_addr, client_ctx->replies,
2724 				       ARRAY_SIZE(client_ctx->replies));
2725 	if (reply) {
2726 		msg = find_msg(NULL, reply);
2727 
2728 		if (coap_header_get_type(&response) == COAP_TYPE_CON) {
2729 			r = lwm2m_send_empty_ack(client_ctx, coap_header_get_id(&response));
2730 			if (r < 0) {
2731 				LOG_ERR("Error transmitting ACK");
2732 			}
2733 		}
2734 
2735 #if defined(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)
2736 		if (coap_header_get_code(&response) == COAP_RESPONSE_CODE_CONTINUE) {
2737 
2738 			r = coap_get_block1_option(&response, &more_blocks, &block_num);
2739 			if (r < 0) {
2740 				LOG_ERR("Missing block1 option in response with continue");
2741 				return;
2742 			}
2743 
2744 			if (r != CONFIG_LWM2M_COAP_BLOCK_SIZE) {
2745 				LOG_WRN("Server requests different block size: ignore");
2746 			}
2747 
2748 			if (!more_blocks) {
2749 				lwm2m_reset_message(msg, true);
2750 				LOG_ERR("Missing more flag in response with continue");
2751 				return;
2752 			}
2753 
2754 			last_block_num = msg->out.block_ctx->current /
2755 					 coap_block_size_to_bytes(msg->out.block_ctx->block_size);
2756 			if (last_block_num > block_num) {
2757 				LOG_INF("Block already sent: ignore");
2758 				return;
2759 			} else if (last_block_num < block_num) {
2760 				LOG_WRN("Requested block out of order");
2761 				return;
2762 			}
2763 
2764 			r = build_msg_block_for_send(msg, block_num + 1);
2765 			if (r < 0) {
2766 				lwm2m_reset_message(msg, true);
2767 				LOG_ERR("Unable to build next block of lwm2m message!");
2768 				return;
2769 			}
2770 
2771 			r = lwm2m_send_message_async(msg);
2772 			if (r < 0) {
2773 				lwm2m_reset_message(msg, true);
2774 				LOG_ERR("Unable to send next block of lwm2m message!");
2775 				return;
2776 			}
2777 
2778 			/* skip release as message was reused for new block */
2779 			LOG_DBG("Block # %d sent", block_num + 1);
2780 			return;
2781 		}
2782 #endif
2783 
2784 		/* skip release if reply->user_data has error condition */
2785 		if (reply && reply->user_data == (void *)COAP_REPLY_STATUS_ERROR) {
2786 			/* reset reply->user_data for next time */
2787 			reply->user_data = (void *)COAP_REPLY_STATUS_NONE;
2788 			LOG_DBG("reply %p NOT removed", reply);
2789 			return;
2790 		}
2791 
2792 		/* free up msg resources */
2793 		if (msg) {
2794 			lwm2m_reset_message(msg, true);
2795 		}
2796 
2797 		LOG_DBG("reply %p handled and removed", reply);
2798 		return;
2799 	}
2800 
2801 	if (coap_header_get_type(&response) == COAP_TYPE_CON) {
2802 		if (has_block2 && IS_ENABLED(CONFIG_LWM2M_COAP_BLOCK_TRANSFER)) {
2803 			msg = find_ongoing_block2_tx();
2804 			if (msg) {
2805 				return handle_ongoing_block2_tx(msg, &response);
2806 			}
2807 			return;
2808 		}
2809 
2810 		/* Clear out existing Block2 transfers when new requests come */
2811 		clear_ongoing_block2_tx();
2812 
2813 		msg = lwm2m_get_message(client_ctx);
2814 		if (!msg) {
2815 			LOG_ERR("Unable to get a lwm2m message!");
2816 			return;
2817 		}
2818 
2819 		/* Create a response message if we reach this point */
2820 		msg->type = COAP_TYPE_ACK;
2821 		msg->code = coap_header_get_code(&response);
2822 		msg->mid = coap_header_get_id(&response);
2823 		/* skip token generation by default */
2824 		msg->tkl = 0;
2825 
2826 		client_ctx->processed_req = msg;
2827 
2828 		lwm2m_registry_lock();
2829 		/* process the response to this request */
2830 		r = handle_request(&response, msg);
2831 		lwm2m_registry_unlock();
2832 		if (r < 0) {
2833 			return;
2834 		}
2835 
2836 		if (msg->acknowledged) {
2837 			r = lwm2m_response_promote_to_con(msg);
2838 			if (r < 0) {
2839 				LOG_ERR("Failed to promote response to CON: %d", r);
2840 				lwm2m_reset_message(msg, true);
2841 				return;
2842 			}
2843 		}
2844 
2845 		client_ctx->processed_req = NULL;
2846 		lwm2m_send_message_async(msg);
2847 	} else {
2848 		LOG_DBG("No handler for response");
2849 	}
2850 }
2851 
notify_message_timeout_cb(struct lwm2m_message * msg)2852 static void notify_message_timeout_cb(struct lwm2m_message *msg)
2853 {
2854 	if (msg->ctx != NULL) {
2855 		struct observe_node *obs;
2856 		struct lwm2m_ctx *client_ctx = msg->ctx;
2857 		sys_snode_t *prev_node = NULL;
2858 
2859 		obs = engine_observe_node_discover(&client_ctx->observer, &prev_node, NULL,
2860 						   msg->token, msg->tkl);
2861 
2862 		if (obs) {
2863 			obs->active_notify = NULL;
2864 			if (client_ctx->observe_cb) {
2865 				client_ctx->observe_cb(LWM2M_OBSERVE_EVENT_NOTIFY_TIMEOUT,
2866 						       &msg->path, msg->reply->user_data);
2867 			}
2868 
2869 			lwm2m_rd_client_timeout(client_ctx);
2870 		}
2871 	}
2872 
2873 	LOG_ERR("Notify Message Timed Out : %p", msg);
2874 }
2875 
lwm2m_read_first_path_ptr(sys_slist_t * lwm2m_path_list)2876 static struct lwm2m_obj_path *lwm2m_read_first_path_ptr(sys_slist_t *lwm2m_path_list)
2877 {
2878 	struct lwm2m_obj_path_list *entry;
2879 
2880 	entry = (struct lwm2m_obj_path_list *)sys_slist_peek_head(lwm2m_path_list);
2881 	return &entry->path;
2882 }
2883 
notify_cached_pending_data_trig(struct observe_node * obs)2884 static void notify_cached_pending_data_trig(struct observe_node *obs)
2885 {
2886 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
2887 	struct lwm2m_time_series_resource *cached_data;
2888 	struct lwm2m_obj_path_list *entry;
2889 
2890 	SYS_SLIST_FOR_EACH_CONTAINER(&obs->path_list, entry, node) {
2891 		cached_data = lwm2m_cache_entry_get_by_object(&entry->path);
2892 		if (!cached_data || lwm2m_cache_size(cached_data) == 0) {
2893 			continue;
2894 		}
2895 
2896 		/* Trig next send by iMin */
2897 		lwm2m_notify_observer_path(&entry->path);
2898 	}
2899 #endif
2900 }
2901 
notify_message_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)2902 static int notify_message_reply_cb(const struct coap_packet *response, struct coap_reply *reply,
2903 				   const struct sockaddr *from)
2904 {
2905 	int ret = 0;
2906 	uint8_t type, code;
2907 	struct lwm2m_message *msg;
2908 	struct observe_node *obs;
2909 	sys_snode_t *prev_node = NULL;
2910 
2911 	type = coap_header_get_type(response);
2912 	code = coap_header_get_code(response);
2913 
2914 	LOG_DBG("NOTIFY ACK type:%u code:%d.%d reply_token:'%s'", type,
2915 		COAP_RESPONSE_CODE_CLASS(code), COAP_RESPONSE_CODE_DETAIL(code),
2916 		sprint_token(reply->token, reply->tkl));
2917 
2918 	msg = find_msg(NULL, reply);
2919 
2920 	/* remove observer on COAP_TYPE_RESET */
2921 	if (type == COAP_TYPE_RESET) {
2922 		if (reply->tkl > 0) {
2923 			ret = engine_remove_observer_by_token(msg->ctx, reply->token, reply->tkl);
2924 			if (ret) {
2925 				LOG_ERR("remove observe error: %d", ret);
2926 			}
2927 		} else {
2928 			LOG_ERR("notify reply missing token -- ignored.");
2929 		}
2930 	} else {
2931 		obs = engine_observe_node_discover(&msg->ctx->observer, &prev_node, NULL,
2932 						   reply->token, reply->tkl);
2933 
2934 		if (obs) {
2935 			obs->active_notify = NULL;
2936 			if (msg->ctx->observe_cb) {
2937 				msg->ctx->observe_cb(LWM2M_OBSERVE_EVENT_NOTIFY_ACK,
2938 						     lwm2m_read_first_path_ptr(&obs->path_list),
2939 						     reply->user_data);
2940 			}
2941 			notify_cached_pending_data_trig(obs);
2942 		}
2943 	}
2944 
2945 	return 0;
2946 }
2947 
do_send_op(struct lwm2m_message * msg,uint16_t content_format,sys_slist_t * lwm2m_path_list)2948 static int do_send_op(struct lwm2m_message *msg, uint16_t content_format,
2949 		      sys_slist_t *lwm2m_path_list)
2950 {
2951 	switch (content_format) {
2952 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
2953 	case LWM2M_FORMAT_APP_SEML_JSON:
2954 		return do_send_op_senml_json(msg, lwm2m_path_list);
2955 #endif
2956 
2957 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
2958 	case LWM2M_FORMAT_APP_SENML_CBOR:
2959 		return do_send_op_senml_cbor(msg, lwm2m_path_list);
2960 #endif
2961 
2962 	default:
2963 		LOG_ERR("Unsupported content-format for /dp: %u", content_format);
2964 		return -ENOMSG;
2965 	}
2966 }
2967 
lwm2m_timeseries_data_rebuild(struct lwm2m_message * msg,int error_code)2968 static bool lwm2m_timeseries_data_rebuild(struct lwm2m_message *msg, int error_code)
2969 {
2970 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
2971 	struct lwm2m_cache_read_info *cache_temp;
2972 
2973 	if (error_code != -ENOMEM) {
2974 		return false;
2975 	}
2976 
2977 	cache_temp = msg->cache_info;
2978 
2979 	if (!cache_temp || !cache_temp->entry_size) {
2980 		return false;
2981 	}
2982 
2983 	/* Put Ring buffer back to original */
2984 	for (int i = 0; i < cache_temp->entry_size; i++) {
2985 		cache_temp->read_info[i].cache_data->rb.get_head =
2986 			cache_temp->read_info[i].original_get_head;
2987 		cache_temp->read_info[i].cache_data->rb.get_tail =
2988 			cache_temp->read_info[i].original_get_tail;
2989 		cache_temp->read_info[i].cache_data->rb.get_base =
2990 			cache_temp->read_info[i].original_get_base;
2991 	}
2992 
2993 	if (cache_temp->entry_limit) {
2994 		/* Limited number of message build fail also */
2995 		return false;
2996 	}
2997 
2998 	/* Limit re-build entry count */
2999 	cache_temp->entry_limit = LWM2M_LIMITED_TIMESERIES_RESOURCE_COUNT / cache_temp->entry_size;
3000 	cache_temp->entry_size = 0;
3001 
3002 	lwm2m_reset_message(msg, false);
3003 	LOG_INF("Try re-buildbuild again with limited cache size %d", cache_temp->entry_limit);
3004 	return true;
3005 #else
3006 	return false;
3007 #endif
3008 }
3009 
generate_notify_message(struct lwm2m_ctx * ctx,struct observe_node * obs,void * user_data)3010 int generate_notify_message(struct lwm2m_ctx *ctx, struct observe_node *obs, void *user_data)
3011 {
3012 	struct lwm2m_message *msg;
3013 	struct lwm2m_engine_obj_inst *obj_inst;
3014 	struct lwm2m_obj_path *path;
3015 	int ret = 0;
3016 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3017 	struct lwm2m_cache_read_info cache_temp_info;
3018 
3019 	cache_temp_info.entry_size = 0;
3020 	cache_temp_info.entry_limit = 0;
3021 #endif
3022 
3023 	msg = lwm2m_get_message(ctx);
3024 	if (!msg) {
3025 		LOG_ERR("Unable to get a lwm2m message!");
3026 		return -ENOMEM;
3027 	}
3028 msg_init:
3029 
3030 	if (!obs->composite) {
3031 		path = lwm2m_read_first_path_ptr(&obs->path_list);
3032 		if (!path) {
3033 			LOG_ERR("Observation node not include path");
3034 			ret = -EINVAL;
3035 			goto cleanup;
3036 		}
3037 		/* copy path */
3038 		memcpy(&msg->path, path, sizeof(struct lwm2m_obj_path));
3039 		LOG_DBG("[%s] NOTIFY MSG START: %u/%u/%u(%u) token:'%s' [%s] %lld",
3040 			obs->resource_update ? "MANUAL" : "AUTO", path->obj_id, path->obj_inst_id,
3041 			path->res_id, path->level, sprint_token(obs->token, obs->tkl),
3042 			lwm2m_sprint_ip_addr(&ctx->remote_addr), (long long)k_uptime_get());
3043 
3044 		obj_inst = get_engine_obj_inst(path->obj_id, path->obj_inst_id);
3045 		if (!obj_inst) {
3046 			LOG_ERR("unable to get engine obj for %u/%u", path->obj_id,
3047 				path->obj_inst_id);
3048 			ret = -EINVAL;
3049 			goto cleanup;
3050 		}
3051 	} else {
3052 		LOG_DBG("[%s] NOTIFY MSG START: (Composite)) token:'%s' [%s] %lld",
3053 			obs->resource_update ? "MANUAL" : "AUTO",
3054 			sprint_token(obs->token, obs->tkl), lwm2m_sprint_ip_addr(&ctx->remote_addr),
3055 			(long long)k_uptime_get());
3056 	}
3057 
3058 	msg->operation = LWM2M_OP_READ;
3059 	msg->type = COAP_TYPE_CON;
3060 	msg->code = COAP_RESPONSE_CODE_CONTENT;
3061 	msg->mid = coap_next_id();
3062 	msg->token = obs->token;
3063 	msg->tkl = obs->tkl;
3064 	msg->reply_cb = notify_message_reply_cb;
3065 	msg->message_timeout_cb = notify_message_timeout_cb;
3066 	msg->out.out_cpkt = &msg->cpkt;
3067 
3068 	ret = lwm2m_init_message(msg);
3069 	if (ret < 0) {
3070 		LOG_ERR("Unable to init lwm2m message! (err: %d)", ret);
3071 		goto cleanup;
3072 	}
3073 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3074 	msg->cache_info = &cache_temp_info;
3075 #endif
3076 
3077 	/* lwm2m_init_message() cleans the coap reply fields, so we assign our data here */
3078 	msg->reply->user_data = user_data;
3079 
3080 	/* each notification should increment the obs counter */
3081 	obs->counter++;
3082 	ret = coap_append_option_int(&msg->cpkt, COAP_OPTION_OBSERVE, obs->counter);
3083 	if (ret < 0) {
3084 		LOG_ERR("OBSERVE option error: %d", ret);
3085 		goto cleanup;
3086 	}
3087 
3088 	/* set the output writer */
3089 	select_writer(&msg->out, obs->format);
3090 	if (obs->composite) {
3091 		/* Use do send which actually do Composite read operation */
3092 		ret = do_send_op(msg, obs->format, &obs->path_list);
3093 	} else {
3094 		ret = do_read_op(msg, obs->format);
3095 	}
3096 
3097 	if (ret < 0) {
3098 		if (lwm2m_timeseries_data_rebuild(msg, ret)) {
3099 			/* Message Build fail by ENOMEM and data include timeseries data.
3100 			 * Try rebuild message again by limiting timeseries data entry lenghts.
3101 			 */
3102 			goto msg_init;
3103 		}
3104 		LOG_ERR("error in multi-format read (err:%d)", ret);
3105 		goto cleanup;
3106 	}
3107 
3108 	obs->active_notify = msg;
3109 	obs->resource_update = false;
3110 	lwm2m_information_interface_send(msg);
3111 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3112 	msg->cache_info = NULL;
3113 #endif
3114 
3115 	LOG_DBG("NOTIFY MSG: SENT");
3116 	return 0;
3117 
3118 cleanup:
3119 	lwm2m_reset_message(msg, true);
3120 	return ret;
3121 }
3122 
lwm2m_perform_composite_read_root(struct lwm2m_message * msg,uint8_t * num_read)3123 static int lwm2m_perform_composite_read_root(struct lwm2m_message *msg, uint8_t *num_read)
3124 {
3125 	int ret;
3126 	struct lwm2m_engine_obj *obj;
3127 	struct lwm2m_engine_obj_inst *obj_inst;
3128 	sys_slist_t *engine_obj_list = lwm2m_engine_obj_list();
3129 
3130 	SYS_SLIST_FOR_EACH_CONTAINER(engine_obj_list, obj, node) {
3131 		/* Security obj MUST NOT be part of registration message */
3132 		if (obj->obj_id == LWM2M_OBJECT_SECURITY_ID) {
3133 			continue;
3134 		}
3135 
3136 		msg->path.level = 1;
3137 		msg->path.obj_id = obj->obj_id;
3138 
3139 		obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
3140 
3141 		if (!obj_inst) {
3142 			continue;
3143 		}
3144 
3145 		ret = lwm2m_perform_read_object_instance(msg, obj_inst, num_read);
3146 		if (ret == -ENOMEM) {
3147 			return ret;
3148 		}
3149 	}
3150 	return 0;
3151 }
3152 
lwm2m_perform_composite_read_op(struct lwm2m_message * msg,uint16_t content_format,sys_slist_t * lwm2m_path_list)3153 int lwm2m_perform_composite_read_op(struct lwm2m_message *msg, uint16_t content_format,
3154 				    sys_slist_t *lwm2m_path_list)
3155 {
3156 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
3157 	struct lwm2m_obj_path_list *entry;
3158 	int ret = 0;
3159 	uint8_t num_read = 0U;
3160 
3161 	/* set output content-format */
3162 	ret = coap_append_option_int(msg->out.out_cpkt, COAP_OPTION_CONTENT_FORMAT, content_format);
3163 	if (ret < 0) {
3164 		LOG_ERR("Error setting response content-format: %d", ret);
3165 		return ret;
3166 	}
3167 
3168 	ret = coap_packet_append_payload_marker(msg->out.out_cpkt);
3169 	if (ret < 0) {
3170 		LOG_ERR("Error appending payload marker: %d", ret);
3171 		return ret;
3172 	}
3173 
3174 	/* Add object start mark */
3175 	engine_put_begin(&msg->out, &msg->path);
3176 
3177 	/* Read resource from path */
3178 	SYS_SLIST_FOR_EACH_CONTAINER(lwm2m_path_list, entry, node) {
3179 		/* Copy path to message path */
3180 		memcpy(&msg->path, &entry->path, sizeof(struct lwm2m_obj_path));
3181 
3182 		if (msg->path.level >= LWM2M_PATH_LEVEL_OBJECT_INST) {
3183 			obj_inst = get_engine_obj_inst(msg->path.obj_id, msg->path.obj_inst_id);
3184 		} else if (msg->path.level == LWM2M_PATH_LEVEL_OBJECT) {
3185 			/* find first obj_inst with path's obj_id */
3186 			obj_inst = next_engine_obj_inst(msg->path.obj_id, -1);
3187 		} else {
3188 			/* Read root Path */
3189 			ret = lwm2m_perform_composite_read_root(msg, &num_read);
3190 			if (ret == -ENOMEM) {
3191 				LOG_ERR("Supported message size is too small for read root");
3192 				return ret;
3193 			}
3194 			break;
3195 		}
3196 
3197 		if (!obj_inst) {
3198 			continue;
3199 		}
3200 
3201 		ret = lwm2m_perform_read_object_instance(msg, obj_inst, &num_read);
3202 		if (ret == -ENOMEM) {
3203 			return ret;
3204 		}
3205 	}
3206 	/* did not read anything even if we should have - on single item */
3207 	if (num_read == 0U) {
3208 		return -ENOENT;
3209 	}
3210 
3211 	/* Add object end mark */
3212 	if (engine_put_end(&msg->out, &msg->path) < 0) {
3213 		return -ENOMEM;
3214 	}
3215 
3216 	return 0;
3217 }
3218 
lwm2m_parse_peerinfo(char * url,struct lwm2m_ctx * client_ctx,bool is_firmware_uri)3219 int lwm2m_parse_peerinfo(char *url, struct lwm2m_ctx *client_ctx, bool is_firmware_uri)
3220 {
3221 	struct http_parser_url parser;
3222 #if defined(CONFIG_LWM2M_DNS_SUPPORT)
3223 	struct zsock_addrinfo *res, hints = {0};
3224 #endif
3225 	int ret;
3226 	uint16_t off, len;
3227 	uint8_t tmp;
3228 
3229 	LOG_DBG("Parse url: %s", url);
3230 
3231 	http_parser_url_init(&parser);
3232 	ret = http_parser_parse_url(url, strlen(url), 0, &parser);
3233 	if (ret < 0) {
3234 		LOG_ERR("Invalid url: %s", url);
3235 		return -ENOTSUP;
3236 	}
3237 
3238 	off = parser.field_data[UF_SCHEMA].off;
3239 	len = parser.field_data[UF_SCHEMA].len;
3240 
3241 	/* check for supported protocol */
3242 	if (strncmp(url + off, "coaps", len) != 0) {
3243 		return -EPROTONOSUPPORT;
3244 	}
3245 
3246 	/* check for DTLS requirement */
3247 	client_ctx->use_dtls = false;
3248 	if (len == 5U && strncmp(url + off, "coaps", len) == 0) {
3249 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
3250 		client_ctx->use_dtls = true;
3251 #else
3252 		return -EPROTONOSUPPORT;
3253 #endif /* CONFIG_LWM2M_DTLS_SUPPORT */
3254 	}
3255 
3256 	if (!(parser.field_set & (1 << UF_PORT))) {
3257 		if (is_firmware_uri && client_ctx->use_dtls) {
3258 			/* Set to default coaps firmware update port */
3259 			parser.port = CONFIG_LWM2M_FIRMWARE_PORT_SECURE;
3260 		} else if (is_firmware_uri) {
3261 			/* Set to default coap firmware update port */
3262 			parser.port = CONFIG_LWM2M_FIRMWARE_PORT_NONSECURE;
3263 		} else {
3264 			/* Set to default LwM2M server port */
3265 			parser.port = CONFIG_LWM2M_PEER_PORT;
3266 		}
3267 	}
3268 
3269 	off = parser.field_data[UF_HOST].off;
3270 	len = parser.field_data[UF_HOST].len;
3271 
3272 #if defined(CONFIG_LWM2M_DTLS_SUPPORT)
3273 	/** copy url pointer to be used in socket */
3274 	client_ctx->desthostname = url + off;
3275 	client_ctx->desthostnamelen = len;
3276 #endif
3277 
3278 	/* truncate host portion */
3279 	tmp = url[off + len];
3280 	url[off + len] = '\0';
3281 
3282 	/* initialize remote_addr */
3283 	(void)memset(&client_ctx->remote_addr, 0, sizeof(client_ctx->remote_addr));
3284 
3285 	/* try and set IP address directly */
3286 	client_ctx->remote_addr.sa_family = AF_INET6;
3287 	ret = net_addr_pton(AF_INET6, url + off,
3288 			    &((struct sockaddr_in6 *)&client_ctx->remote_addr)->sin6_addr);
3289 	/* Try to parse again using AF_INET */
3290 	if (ret < 0) {
3291 		client_ctx->remote_addr.sa_family = AF_INET;
3292 		ret = net_addr_pton(AF_INET, url + off,
3293 				    &((struct sockaddr_in *)&client_ctx->remote_addr)->sin_addr);
3294 	}
3295 
3296 	if (ret < 0) {
3297 #if defined(CONFIG_LWM2M_DNS_SUPPORT)
3298 #if defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4)
3299 		hints.ai_family = AF_UNSPEC;
3300 #elif defined(CONFIG_NET_IPV6)
3301 		hints.ai_family = AF_INET6;
3302 #elif defined(CONFIG_NET_IPV4)
3303 		hints.ai_family = AF_INET;
3304 #else
3305 		hints.ai_family = AF_UNSPEC;
3306 #endif /* defined(CONFIG_NET_IPV6) && defined(CONFIG_NET_IPV4) */
3307 		hints.ai_socktype = SOCK_DGRAM;
3308 		ret = zsock_getaddrinfo(url + off, NULL, &hints, &res);
3309 		if (ret != 0) {
3310 			LOG_ERR("Unable to resolve address");
3311 			/* DNS error codes don't align with normal errors */
3312 			ret = -ENOENT;
3313 			goto cleanup;
3314 		}
3315 
3316 		memcpy(&client_ctx->remote_addr, res->ai_addr, sizeof(client_ctx->remote_addr));
3317 		client_ctx->remote_addr.sa_family = res->ai_family;
3318 		zsock_freeaddrinfo(res);
3319 #else
3320 		goto cleanup;
3321 #endif /* CONFIG_LWM2M_DNS_SUPPORT */
3322 	}
3323 
3324 	/* set port */
3325 	if (client_ctx->remote_addr.sa_family == AF_INET6) {
3326 		net_sin6(&client_ctx->remote_addr)->sin6_port = htons(parser.port);
3327 	} else if (client_ctx->remote_addr.sa_family == AF_INET) {
3328 		net_sin(&client_ctx->remote_addr)->sin_port = htons(parser.port);
3329 	} else {
3330 		ret = -EPROTONOSUPPORT;
3331 	}
3332 
3333 cleanup:
3334 	/* restore host separator */
3335 	url[off + len] = tmp;
3336 	return ret;
3337 }
3338 
do_composite_read_op_for_parsed_list(struct lwm2m_message * msg,uint16_t content_format,sys_slist_t * path_list)3339 int do_composite_read_op_for_parsed_list(struct lwm2m_message *msg, uint16_t content_format,
3340 					 sys_slist_t *path_list)
3341 {
3342 	struct lwm2m_obj_path_list *entry;
3343 
3344 	/* Check access rights */
3345 	SYS_SLIST_FOR_EACH_CONTAINER(path_list, entry, node) {
3346 		if (entry->path.level > LWM2M_PATH_LEVEL_NONE &&
3347 		    entry->path.obj_id == LWM2M_OBJECT_SECURITY_ID && !msg->ctx->bootstrap_mode) {
3348 			return -EACCES;
3349 		}
3350 	}
3351 
3352 	switch (content_format) {
3353 
3354 #if defined(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)
3355 	case LWM2M_FORMAT_APP_SEML_JSON:
3356 		return do_composite_read_op_for_parsed_list_senml_json(msg, path_list);
3357 #endif
3358 
3359 #if defined(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)
3360 	case LWM2M_FORMAT_APP_SENML_CBOR:
3361 		return do_composite_read_op_for_parsed_path_senml_cbor(msg, path_list);
3362 #endif
3363 
3364 	default:
3365 		LOG_ERR("Unsupported content-format: %u", content_format);
3366 		return -ENOMSG;
3367 	}
3368 }
3369 
3370 #if defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)
do_send_reply_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)3371 static int do_send_reply_cb(const struct coap_packet *response, struct coap_reply *reply,
3372 			    const struct sockaddr *from)
3373 {
3374 	uint8_t code;
3375 	struct lwm2m_message *msg = (struct lwm2m_message *)reply->user_data;
3376 
3377 	code = coap_header_get_code(response);
3378 	LOG_DBG("Send callback (code:%u.%u)", COAP_RESPONSE_CODE_CLASS(code),
3379 		COAP_RESPONSE_CODE_DETAIL(code));
3380 
3381 	if (code == COAP_RESPONSE_CODE_CHANGED) {
3382 		LOG_INF("Send done!");
3383 		if (msg && msg->send_status_cb) {
3384 			msg->send_status_cb(LWM2M_SEND_STATUS_SUCCESS);
3385 		}
3386 		return 0;
3387 	}
3388 
3389 	LOG_ERR("Failed with code %u.%u. Not Retrying.", COAP_RESPONSE_CODE_CLASS(code),
3390 		COAP_RESPONSE_CODE_DETAIL(code));
3391 
3392 	if (msg && msg->send_status_cb) {
3393 		msg->send_status_cb(LWM2M_SEND_STATUS_FAILURE);
3394 	}
3395 
3396 	return 0;
3397 }
3398 
do_send_timeout_cb(struct lwm2m_message * msg)3399 static void do_send_timeout_cb(struct lwm2m_message *msg)
3400 {
3401 	if (msg->send_status_cb) {
3402 		msg->send_status_cb(LWM2M_SEND_STATUS_TIMEOUT);
3403 	}
3404 	LOG_WRN("Send Timeout");
3405 	lwm2m_rd_client_timeout(msg->ctx);
3406 }
3407 #endif
3408 
3409 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
init_next_pending_timeseries_data(struct lwm2m_cache_read_info * cache_temp,sys_slist_t * lwm2m_path_list,sys_slist_t * lwm2m_path_free_list)3410 static bool init_next_pending_timeseries_data(struct lwm2m_cache_read_info *cache_temp,
3411 					  sys_slist_t *lwm2m_path_list,
3412 					  sys_slist_t *lwm2m_path_free_list)
3413 {
3414 	uint32_t bytes_available = 0;
3415 
3416 	/* Check do we have still pending data to send */
3417 	for (int i = 0; i < cache_temp->entry_size; i++) {
3418 		if (ring_buf_is_empty(&cache_temp->read_info[i].cache_data->rb)) {
3419 			/* Skip Emtpy cached buffers */
3420 			continue;
3421 		}
3422 
3423 		/* Add to linked list */
3424 		if (lwm2m_engine_add_path_to_list(lwm2m_path_list, lwm2m_path_free_list,
3425 						  &cache_temp->read_info[i].cache_data->path)) {
3426 			return false;
3427 		}
3428 
3429 		bytes_available += ring_buf_size_get(&cache_temp->read_info[i].cache_data->rb);
3430 	}
3431 
3432 	if (bytes_available == 0) {
3433 		return false;
3434 	}
3435 
3436 	LOG_INF("Allocate a new message for pending data %u", bytes_available);
3437 	cache_temp->entry_size = 0;
3438 	cache_temp->entry_limit = 0;
3439 	return true;
3440 }
3441 #endif
3442 
lwm2m_send_cb(struct lwm2m_ctx * ctx,const struct lwm2m_obj_path path_list[],uint8_t path_list_size,lwm2m_send_cb_t reply_cb)3443 int lwm2m_send_cb(struct lwm2m_ctx *ctx, const struct lwm2m_obj_path path_list[],
3444 			 uint8_t path_list_size, lwm2m_send_cb_t reply_cb)
3445 {
3446 #if defined(CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1)
3447 	struct lwm2m_message *msg;
3448 	int ret;
3449 	uint16_t content_format;
3450 
3451 	/* Path list buffer */
3452 	struct lwm2m_obj_path_list lwm2m_path_list_buf[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
3453 	sys_slist_t lwm2m_path_list;
3454 	sys_slist_t lwm2m_path_free_list;
3455 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3456 	struct lwm2m_cache_read_info cache_temp_info;
3457 
3458 	cache_temp_info.entry_size = 0;
3459 	cache_temp_info.entry_limit = 0;
3460 #endif
3461 
3462 	/* Validate Connection */
3463 	if (!lwm2m_rd_client_is_registred(ctx)) {
3464 		return -EPERM;
3465 	}
3466 
3467 	if (lwm2m_server_get_mute_send(ctx->srv_obj_inst)) {
3468 		LOG_WRN("Send operation is muted by server");
3469 		return -EPERM;
3470 	}
3471 
3472 	/* Init list */
3473 	lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list, lwm2m_path_list_buf,
3474 				    CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
3475 
3476 	if (path_list_size > CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE) {
3477 		return -E2BIG;
3478 	}
3479 
3480 	if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_CBOR_SUPPORT)) {
3481 		content_format = LWM2M_FORMAT_APP_SENML_CBOR;
3482 	} else if (IS_ENABLED(CONFIG_LWM2M_RW_SENML_JSON_SUPPORT)) {
3483 		content_format = LWM2M_FORMAT_APP_SEML_JSON;
3484 	} else {
3485 		LOG_WRN("SenML CBOR or JSON is not supported");
3486 		return -ENOTSUP;
3487 	}
3488 
3489 	/* Parse Path to internal used object path format */
3490 	for (int i = 0; i < path_list_size; i++) {
3491 		/* Add to linked list */
3492 		if (lwm2m_engine_add_path_to_list(&lwm2m_path_list, &lwm2m_path_free_list,
3493 						  &path_list[i])) {
3494 			return -1;
3495 		}
3496 	}
3497 	/* Clear path which are part are part of recursive path /1 will include /1/0/1 */
3498 	lwm2m_engine_clear_duplicate_path(&lwm2m_path_list, &lwm2m_path_free_list);
3499 	lwm2m_registry_lock();
3500 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3501 msg_alloc:
3502 #endif
3503 	/* Allocate Message buffer */
3504 	msg = lwm2m_get_message(ctx);
3505 	if (!msg) {
3506 		lwm2m_registry_unlock();
3507 		LOG_ERR("Unable to get a lwm2m message!");
3508 		return -ENOMEM;
3509 	}
3510 
3511 msg_init:
3512 
3513 	msg->type = COAP_TYPE_CON;
3514 	msg->reply_cb = do_send_reply_cb;
3515 	msg->message_timeout_cb = do_send_timeout_cb;
3516 	msg->code = COAP_METHOD_POST;
3517 	msg->mid = coap_next_id();
3518 	msg->tkl = LWM2M_MSG_TOKEN_GENERATE_NEW;
3519 	msg->out.out_cpkt = &msg->cpkt;
3520 
3521 	ret = lwm2m_init_message(msg);
3522 	if (ret) {
3523 		goto cleanup;
3524 	}
3525 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3526 	msg->cache_info = &cache_temp_info;
3527 #endif
3528 
3529 	/* Register user callback if defined for confirmation */
3530 	if (reply_cb) {
3531 		msg->reply->user_data = msg;
3532 		msg->send_status_cb = reply_cb;
3533 	}
3534 
3535 	ret = select_writer(&msg->out, content_format);
3536 	if (ret) {
3537 		goto cleanup;
3538 	}
3539 
3540 	ret = coap_packet_append_option(&msg->cpkt, COAP_OPTION_URI_PATH, LWM2M_DP_CLIENT_URI,
3541 					strlen(LWM2M_DP_CLIENT_URI));
3542 	if (ret < 0) {
3543 		goto cleanup;
3544 	}
3545 
3546 	/* Write requested path data */
3547 	ret = do_send_op(msg, content_format, &lwm2m_path_list);
3548 	if (ret < 0) {
3549 		if (lwm2m_timeseries_data_rebuild(msg, ret)) {
3550 			/* Message Build fail by ENOMEM and data include timeseries data.
3551 			 * Try rebuild message again by limiting timeseries data entry lenghts.
3552 			 */
3553 			goto msg_init;
3554 		}
3555 
3556 		LOG_ERR("Send (err:%d)", ret);
3557 		goto cleanup;
3558 	}
3559 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3560 	msg->cache_info = NULL;
3561 #endif
3562 	LOG_INF("Send op to server (/dp)");
3563 	lwm2m_information_interface_send(msg);
3564 
3565 #if defined(CONFIG_LWM2M_RESOURCE_DATA_CACHE_SUPPORT)
3566 	if (cache_temp_info.entry_size) {
3567 		/* Init Path list for continuous message allocation */
3568 		lwm2m_engine_path_list_init(&lwm2m_path_list, &lwm2m_path_free_list,
3569 					    lwm2m_path_list_buf,
3570 					    CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE);
3571 
3572 		if (init_next_pending_timeseries_data(&cache_temp_info, &lwm2m_path_list,
3573 						     &lwm2m_path_free_list)) {
3574 			goto msg_alloc;
3575 		}
3576 	}
3577 #endif
3578 	lwm2m_registry_unlock();
3579 	return 0;
3580 cleanup:
3581 	lwm2m_registry_unlock();
3582 	lwm2m_reset_message(msg, true);
3583 	return ret;
3584 #else
3585 	LOG_WRN("LwM2M send is only supported for CONFIG_LWM2M_SERVER_OBJECT_VERSION_1_1");
3586 	return -ENOTSUP;
3587 #endif
3588 }
3589 
lwm2m_send(struct lwm2m_ctx * ctx,const struct lwm2m_obj_path path_list[],uint8_t path_list_size,bool confirmation_request)3590 int lwm2m_send(struct lwm2m_ctx *ctx, const struct lwm2m_obj_path path_list[],
3591 			 uint8_t path_list_size, bool confirmation_request)
3592 {
3593 	if (!confirmation_request) {
3594 		return -EINVAL;
3595 	}
3596 
3597 	return lwm2m_send_cb(ctx, path_list, path_list_size, NULL);
3598 }
3599 
lwm2m_engine_send(struct lwm2m_ctx * ctx,char const * path_list[],uint8_t path_list_size,bool confirmation_request)3600 int lwm2m_engine_send(struct lwm2m_ctx *ctx, char const *path_list[], uint8_t path_list_size,
3601 		      bool confirmation_request)
3602 {
3603 	int ret;
3604 	struct lwm2m_obj_path lwm2m_path_list[CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE];
3605 
3606 	if (path_list_size > CONFIG_LWM2M_COMPOSITE_PATH_LIST_SIZE) {
3607 		return -E2BIG;
3608 	}
3609 
3610 	for (int i = 0; i < path_list_size; i++) {
3611 		/* translate path -> path_obj */
3612 		ret = lwm2m_string_to_path(path_list[i], &lwm2m_path_list[i], '/');
3613 		if (ret < 0) {
3614 			return ret;
3615 		}
3616 	}
3617 
3618 	return lwm2m_send_cb(ctx, lwm2m_path_list, path_list_size, NULL);
3619 }
3620