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