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