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