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