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