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