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  * Copyright (c) 2015, Yanzi Networks AB.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. Neither the name of the copyright holder nor the names of its
21  *    contributors may be used to endorse or promote products derived
22  *    from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35  * OF THE POSSIBILITY OF SUCH DAMAGE.
36  */
37 
38 /*
39  * Original Authors:
40  *         Joakim Eriksson <joakime@sics.se>
41  *         Niclas Finne <nfi@sics.se>
42  */
43 
44 /*
45  * Zephyr Contribution by Michael Scott <michael.scott@linaro.org>
46  * - Zephyr code style changes / code cleanup
47  * - Move to Zephyr APIs where possible
48  * - Convert to Zephyr int/uint types
49  * - Remove engine dependency (replace with writer context)
50  * - Add write int64 function
51  */
52 
53 /*
54  * TODO:
55  * - Lots of byte-order API clean up
56  * - Var / parameter type cleanup
57  * - Replace magic #'s with defines
58  */
59 
60 #define LOG_MODULE_NAME net_lwm2m_oma_tlv
61 #define LOG_LEVEL CONFIG_LWM2M_LOG_LEVEL
62 
63 #include <zephyr/logging/log.h>
64 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
65 
66 #include <string.h>
67 #include <stdint.h>
68 #include <zephyr/sys/byteorder.h>
69 
70 #include "lwm2m_rw_oma_tlv.h"
71 #include "lwm2m_engine.h"
72 #include "lwm2m_rd_client.h"
73 #include "lwm2m_util.h"
74 
75 enum {
76 	OMA_TLV_TYPE_OBJECT_INSTANCE   = 0,
77 	OMA_TLV_TYPE_RESOURCE_INSTANCE = 1,
78 	OMA_TLV_TYPE_MULTI_RESOURCE    = 2,
79 	OMA_TLV_TYPE_RESOURCE          = 3
80 };
81 
82 struct oma_tlv {
83 	uint8_t  type;
84 	uint16_t id; /* can be 8-bit or 16-bit when serialized */
85 	uint32_t length;
86 };
87 
get_len_type(const struct oma_tlv * tlv)88 static uint8_t get_len_type(const struct oma_tlv *tlv)
89 {
90 	if (tlv->length < 8) {
91 		return 0;
92 	} else if (tlv->length < 0x100) {
93 		return 1;
94 	} else if (tlv->length < 0x10000) {
95 		return 2;
96 	}
97 
98 	return 3;
99 }
100 
tlv_calc_type(uint8_t flags)101 static uint8_t tlv_calc_type(uint8_t flags)
102 {
103 	return flags & WRITER_RESOURCE_INSTANCE ?
104 			OMA_TLV_TYPE_RESOURCE_INSTANCE : OMA_TLV_TYPE_RESOURCE;
105 }
106 
tlv_calc_id(uint8_t flags,struct lwm2m_obj_path * path)107 static uint16_t tlv_calc_id(uint8_t flags, struct lwm2m_obj_path *path)
108 {
109 	return flags & WRITER_RESOURCE_INSTANCE ?
110 			path->res_inst_id : path->res_id;
111 }
112 
tlv_setup(struct oma_tlv * tlv,uint8_t type,uint16_t id,uint32_t buflen)113 static void tlv_setup(struct oma_tlv *tlv, uint8_t type, uint16_t id,
114 		      uint32_t buflen)
115 {
116 	if (tlv) {
117 		tlv->type = type;
118 		tlv->id = id;
119 		tlv->length = buflen;
120 	}
121 }
122 
oma_tlv_put_u8(struct lwm2m_output_context * out,uint8_t value,bool insert)123 static int oma_tlv_put_u8(struct lwm2m_output_context *out,
124 			  uint8_t value, bool insert)
125 {
126 	struct tlv_out_formatter_data *fd;
127 	int ret;
128 
129 	if (insert) {
130 		fd = engine_get_out_user_data(out);
131 		if (!fd) {
132 			return -EINVAL;
133 		}
134 
135 		ret = buf_insert(CPKT_BUF_WRITE(out->out_cpkt),
136 				 fd->mark_pos, &value, 1);
137 		if (ret < 0) {
138 			return ret;
139 		}
140 
141 		fd->mark_pos++;
142 	} else {
143 		ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt), &value, 1);
144 		if (ret < 0) {
145 			return ret;
146 		}
147 	}
148 
149 	return 0;
150 }
151 
oma_tlv_put(const struct oma_tlv * tlv,struct lwm2m_output_context * out,uint8_t * value,bool insert)152 static int oma_tlv_put(const struct oma_tlv *tlv,
153 		       struct lwm2m_output_context *out, uint8_t *value,
154 		       bool insert)
155 {
156 	size_t pos;
157 	int ret, i;
158 	uint8_t len_type, tmp;
159 
160 	/* len_type is the same as number of bytes required for length */
161 	len_type = get_len_type(tlv);
162 
163 	/* first type byte in TLV header */
164 	tmp = (tlv->type << 6) |
165 	      (tlv->id > 255 ? (1 << 5) : 0) |
166 	      (len_type << 3) |
167 	      (len_type == 0U ? tlv->length : 0);
168 
169 	ret = oma_tlv_put_u8(out, tmp, insert);
170 	if (ret < 0) {
171 		return ret;
172 	}
173 
174 	pos = 1;
175 
176 	/* The ID */
177 	if (tlv->id > 255) {
178 		ret = oma_tlv_put_u8(out, (tlv->id >> 8) & 0xff, insert);
179 		if (ret < 0) {
180 			return ret;
181 		}
182 
183 		pos++;
184 	}
185 
186 	ret = oma_tlv_put_u8(out, tlv->id & 0xff, insert);
187 	if (ret < 0) {
188 		return ret;
189 	}
190 
191 	pos++;
192 
193 	for (i = 2; i >= 0; i--) {
194 		if (len_type > i) {
195 			ret = oma_tlv_put_u8(out,
196 					     (tlv->length >> (i * 8)) & 0xff,
197 					     insert);
198 			if (ret < 0) {
199 				return ret;
200 			}
201 
202 			pos++;
203 		}
204 	}
205 
206 	/* finally add the value */
207 	if (value != NULL && tlv->length > 0 && !insert) {
208 		ret = buf_append(CPKT_BUF_WRITE(out->out_cpkt),
209 				 value, tlv->length);
210 		if (ret < 0) {
211 			return ret;
212 		}
213 	}
214 
215 	return pos + tlv->length;
216 }
217 
oma_tlv_get(struct oma_tlv * tlv,struct lwm2m_input_context * in,bool dont_advance)218 static int oma_tlv_get(struct oma_tlv *tlv, struct lwm2m_input_context *in,
219 		       bool dont_advance)
220 {
221 	uint8_t len_type;
222 	uint8_t len_pos = 1U;
223 	size_t tlv_len;
224 	uint16_t tmp_offset;
225 	uint8_t buf[2];
226 	int ret;
227 
228 	tmp_offset = in->offset;
229 	ret = buf_read_u8(&buf[0], CPKT_BUF_READ(in->in_cpkt), &tmp_offset);
230 	if (ret < 0) {
231 		goto error;
232 	}
233 
234 	tlv->type = (buf[0] >> 6) & 3;
235 	len_type = (buf[0] >> 3) & 3;
236 	len_pos = 1 + (((buf[0] & (1 << 5)) != 0U) ? 2 : 1);
237 
238 	ret = buf_read_u8(&buf[1], CPKT_BUF_READ(in->in_cpkt), &tmp_offset);
239 	if (ret < 0) {
240 		goto error;
241 	}
242 
243 	tlv->id = buf[1];
244 
245 	/* if len_pos > 2 it means that there are more ID to read */
246 	if (len_pos > 2) {
247 		ret = buf_read_u8(&buf[1], CPKT_BUF_READ(in->in_cpkt),
248 				  &tmp_offset);
249 		if (ret < 0) {
250 			goto error;
251 		}
252 
253 		tlv->id = (tlv->id << 8) + buf[1];
254 	}
255 
256 	if (len_type == 0U) {
257 		tlv_len = buf[0] & 7;
258 	} else {
259 		/* read the length */
260 		tlv_len = 0;
261 		while (len_type > 0) {
262 			ret = buf_read_u8(&buf[1], CPKT_BUF_READ(in->in_cpkt),
263 					  &tmp_offset);
264 			if (ret < 0) {
265 				goto error;
266 			}
267 
268 			len_pos++;
269 			tlv_len = tlv_len << 8 | buf[1];
270 			len_type--;
271 		}
272 	}
273 
274 	/* and read out the data??? */
275 	tlv->length = tlv_len;
276 
277 	if (!dont_advance) {
278 		in->offset = tmp_offset;
279 	}
280 
281 	return len_pos + tlv_len;
282 
283 error:
284 	if (!dont_advance) {
285 		in->offset = tmp_offset;
286 	}
287 
288 	return ret;
289 }
290 
put_begin_tlv(struct lwm2m_output_context * out,uint16_t * mark_pos,uint8_t * writer_flags,int writer_flag)291 static int put_begin_tlv(struct lwm2m_output_context *out, uint16_t *mark_pos,
292 			 uint8_t *writer_flags, int writer_flag)
293 {
294 	/* set flags */
295 	*writer_flags |= writer_flag;
296 
297 	/*
298 	 * store position for inserting TLV when we know the length
299 	 */
300 	*mark_pos = out->out_cpkt->offset;
301 
302 	return 0;
303 }
304 
put_end_tlv(struct lwm2m_output_context * out,uint16_t mark_pos,uint8_t * writer_flags,uint8_t writer_flag,int tlv_type,int tlv_id)305 static int put_end_tlv(struct lwm2m_output_context *out, uint16_t mark_pos,
306 		       uint8_t *writer_flags, uint8_t writer_flag, int tlv_type,
307 		       int tlv_id)
308 {
309 	struct tlv_out_formatter_data *fd;
310 	struct oma_tlv tlv;
311 	int len;
312 
313 	fd = engine_get_out_user_data(out);
314 	if (!fd) {
315 		return -EINVAL;
316 	}
317 
318 	*writer_flags &= ~writer_flag;
319 
320 	len = out->out_cpkt->offset - mark_pos;
321 
322 	/* use stored location */
323 	fd->mark_pos = mark_pos;
324 
325 	/* set instance length */
326 	tlv_setup(&tlv, tlv_type, tlv_id, len);
327 	len = oma_tlv_put(&tlv, out, NULL, true) - tlv.length;
328 
329 	return len;
330 }
331 
put_begin_oi(struct lwm2m_output_context * out,struct lwm2m_obj_path * path)332 static int put_begin_oi(struct lwm2m_output_context *out,
333 			struct lwm2m_obj_path *path)
334 {
335 	struct tlv_out_formatter_data *fd;
336 
337 	/* No need for oi level TLV constructs */
338 	if (path->level > LWM2M_PATH_LEVEL_OBJECT) {
339 		return 0;
340 	}
341 
342 	fd = engine_get_out_user_data(out);
343 	if (!fd) {
344 		return -EINVAL;
345 	}
346 
347 	return put_begin_tlv(out, &fd->mark_pos_oi, &fd->writer_flags, 0);
348 }
349 
put_end_oi(struct lwm2m_output_context * out,struct lwm2m_obj_path * path)350 static int put_end_oi(struct lwm2m_output_context *out,
351 		      struct lwm2m_obj_path *path)
352 {
353 	struct tlv_out_formatter_data *fd;
354 
355 	if (path->level > LWM2M_PATH_LEVEL_OBJECT) {
356 		return 0;
357 	}
358 
359 	fd = engine_get_out_user_data(out);
360 	if (!fd) {
361 		return -EINVAL;
362 	}
363 
364 	return put_end_tlv(out, fd->mark_pos_oi, &fd->writer_flags, 0,
365 			   OMA_TLV_TYPE_OBJECT_INSTANCE, path->obj_inst_id);
366 }
367 
put_begin_ri(struct lwm2m_output_context * out,struct lwm2m_obj_path * path)368 static int put_begin_ri(struct lwm2m_output_context *out,
369 			struct lwm2m_obj_path *path)
370 {
371 	struct tlv_out_formatter_data *fd;
372 
373 	fd = engine_get_out_user_data(out);
374 	if (!fd) {
375 		return -EINVAL;
376 	}
377 
378 	return put_begin_tlv(out, &fd->mark_pos_ri, &fd->writer_flags,
379 			     WRITER_RESOURCE_INSTANCE);
380 }
381 
put_end_ri(struct lwm2m_output_context * out,struct lwm2m_obj_path * path)382 static int put_end_ri(struct lwm2m_output_context *out,
383 		      struct lwm2m_obj_path *path)
384 {
385 	struct tlv_out_formatter_data *fd;
386 
387 	fd = engine_get_out_user_data(out);
388 	if (!fd) {
389 		return -EINVAL;
390 	}
391 
392 	/* Skip writing Multiple Resource TLV if path level is 4 */
393 	if (IS_ENABLED(CONFIG_LWM2M_VERSION_1_1) &&
394 		path->level == LWM2M_PATH_LEVEL_RESOURCE_INST) {
395 		return 0;
396 	}
397 
398 	return put_end_tlv(out, fd->mark_pos_ri, &fd->writer_flags,
399 			   WRITER_RESOURCE_INSTANCE,
400 			   OMA_TLV_TYPE_MULTI_RESOURCE, path->res_id);
401 }
402 
put_s8(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,int8_t value)403 static int put_s8(struct lwm2m_output_context *out, struct lwm2m_obj_path *path,
404 		  int8_t value)
405 {
406 	struct tlv_out_formatter_data *fd;
407 	int len;
408 	struct oma_tlv tlv;
409 
410 	fd = engine_get_out_user_data(out);
411 	if (!fd) {
412 		return -EINVAL;
413 	}
414 
415 	tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
416 		  tlv_calc_id(fd->writer_flags, path), sizeof(value));
417 
418 	len = oma_tlv_put(&tlv, out, (uint8_t *)&value, false);
419 	return len;
420 }
421 
put_s16(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,int16_t value)422 static int put_s16(struct lwm2m_output_context *out,
423 		   struct lwm2m_obj_path *path, int16_t value)
424 {
425 	struct tlv_out_formatter_data *fd;
426 	int len;
427 	struct oma_tlv tlv;
428 	int16_t net_value;
429 
430 	if (INT8_MIN <= value && value <= INT8_MAX) {
431 		return put_s8(out, path, (int8_t)value);
432 	}
433 
434 	fd = engine_get_out_user_data(out);
435 	if (!fd) {
436 		return -EINVAL;
437 	}
438 
439 	net_value = sys_cpu_to_be16(value);
440 	tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
441 		  tlv_calc_id(fd->writer_flags, path), sizeof(net_value));
442 
443 	len = oma_tlv_put(&tlv, out, (uint8_t *)&net_value, false);
444 	return len;
445 }
446 
put_s32(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,int32_t value)447 static int put_s32(struct lwm2m_output_context *out,
448 		   struct lwm2m_obj_path *path, int32_t value)
449 {
450 	struct tlv_out_formatter_data *fd;
451 	int len;
452 	struct oma_tlv tlv;
453 	int32_t net_value;
454 
455 	if (INT16_MIN <= value && value <= INT16_MAX) {
456 		return put_s16(out, path, (int16_t)value);
457 	}
458 
459 	fd = engine_get_out_user_data(out);
460 	if (!fd) {
461 		return -EINVAL;
462 	}
463 
464 	net_value = sys_cpu_to_be32(value);
465 	tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
466 		  tlv_calc_id(fd->writer_flags, path), sizeof(net_value));
467 
468 	len = oma_tlv_put(&tlv, out, (uint8_t *)&net_value, false);
469 
470 	return len;
471 }
472 
put_s64(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,int64_t value)473 static int put_s64(struct lwm2m_output_context *out,
474 		   struct lwm2m_obj_path *path, int64_t value)
475 {
476 	struct tlv_out_formatter_data *fd;
477 	int len;
478 	struct oma_tlv tlv;
479 	int64_t net_value;
480 
481 	if (INT32_MIN <= value && value <= INT32_MAX) {
482 		return put_s32(out, path, (int32_t)value);
483 	}
484 
485 	fd = engine_get_out_user_data(out);
486 	if (!fd) {
487 		return -EINVAL;
488 	}
489 
490 	net_value = sys_cpu_to_be64(value);
491 	tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
492 		  tlv_calc_id(fd->writer_flags, path), sizeof(net_value));
493 
494 	len = oma_tlv_put(&tlv, out, (uint8_t *)&net_value, false);
495 	return len;
496 }
497 
498 
put_time(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,time_t value)499 static int put_time(struct lwm2m_output_context *out, struct lwm2m_obj_path *path, time_t value)
500 {
501 	return put_s64(out, path, (int64_t)value);
502 }
503 
put_string(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,char * buf,size_t buflen)504 static int put_string(struct lwm2m_output_context *out,
505 		      struct lwm2m_obj_path *path, char *buf, size_t buflen)
506 {
507 	struct tlv_out_formatter_data *fd;
508 	int len;
509 	struct oma_tlv tlv;
510 
511 	fd = engine_get_out_user_data(out);
512 	if (!fd) {
513 		return -EINVAL;
514 	}
515 
516 	tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
517 		  tlv_calc_id(fd->writer_flags, path), (uint32_t)buflen);
518 	len = oma_tlv_put(&tlv, out, (uint8_t *)buf, false);
519 	return len;
520 }
521 
put_float(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,double * value)522 static int put_float(struct lwm2m_output_context *out,
523 		     struct lwm2m_obj_path *path, double *value)
524 {
525 	struct tlv_out_formatter_data *fd;
526 	int len;
527 	struct oma_tlv tlv;
528 	int ret;
529 	uint8_t b64[8];
530 
531 	fd = engine_get_out_user_data(out);
532 	if (!fd) {
533 		return -EINVAL;
534 	}
535 
536 	ret = lwm2m_float_to_b64(value, b64, sizeof(b64));
537 	if (ret < 0) {
538 		LOG_ERR("float32 conversion error: %d", ret);
539 		return ret;
540 	}
541 
542 	tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
543 		  tlv_calc_id(fd->writer_flags, path), sizeof(b64));
544 	len = oma_tlv_put(&tlv, out, b64, false);
545 	return len;
546 }
547 
put_bool(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,bool value)548 static int put_bool(struct lwm2m_output_context *out,
549 		    struct lwm2m_obj_path *path, bool value)
550 {
551 	int8_t value_s8 = (value != 0 ? 1 : 0);
552 
553 	return put_s8(out, path, value_s8);
554 }
555 
put_opaque(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,char * buf,size_t buflen)556 static int put_opaque(struct lwm2m_output_context *out,
557 		      struct lwm2m_obj_path *path, char *buf, size_t buflen)
558 {
559 	return put_string(out, path, buf, buflen);
560 }
561 
put_objlnk(struct lwm2m_output_context * out,struct lwm2m_obj_path * path,struct lwm2m_objlnk * value)562 static int put_objlnk(struct lwm2m_output_context *out,
563 		      struct lwm2m_obj_path *path, struct lwm2m_objlnk *value)
564 {
565 	struct tlv_out_formatter_data *fd;
566 	struct oma_tlv tlv;
567 	int32_t net_value = sys_cpu_to_be32(
568 				((value->obj_id) << 16) | value->obj_inst);
569 
570 	fd = engine_get_out_user_data(out);
571 	if (!fd) {
572 		return -EINVAL;
573 	}
574 
575 	tlv_setup(&tlv, tlv_calc_type(fd->writer_flags),
576 		  tlv_calc_id(fd->writer_flags, path), sizeof(net_value));
577 
578 	return oma_tlv_put(&tlv, out, (uint8_t *)&net_value, false);
579 }
580 
get_number(struct lwm2m_input_context * in,int64_t * value,uint8_t max_len)581 static int get_number(struct lwm2m_input_context *in, int64_t *value,
582 		      uint8_t max_len)
583 {
584 	struct oma_tlv tlv;
585 	int size;
586 	int64_t temp;
587 	int ret;
588 
589 	size = oma_tlv_get(&tlv, in, false);
590 	if (size < 0) {
591 		return size;
592 	}
593 
594 	if (tlv.length > max_len) {
595 		LOG_ERR("invalid length: %u", tlv.length);
596 		return -ENOMEM;
597 	}
598 
599 	ret = buf_read((uint8_t *)&temp, tlv.length,
600 		       CPKT_BUF_READ(in->in_cpkt), &in->offset);
601 	if (ret < 0) {
602 		return ret;
603 	}
604 
605 	switch (tlv.length) {
606 	case 1:
607 		*value = (int8_t)temp;
608 		break;
609 	case 2:
610 		*value = sys_cpu_to_be16((int16_t)temp);
611 		break;
612 	case 4:
613 		*value = sys_cpu_to_be32((int32_t)temp);
614 		break;
615 	case 8:
616 		*value = sys_cpu_to_be64(temp);
617 		break;
618 	default:
619 		LOG_ERR("invalid length: %u", tlv.length);
620 		return -EBADMSG;
621 	}
622 
623 	return size;
624 }
625 
get_s64(struct lwm2m_input_context * in,int64_t * value)626 static int get_s64(struct lwm2m_input_context *in, int64_t *value)
627 {
628 	return get_number(in, value, 8);
629 }
630 
get_time(struct lwm2m_input_context * in,time_t * value)631 static int get_time(struct lwm2m_input_context *in, time_t *value)
632 {
633 	int64_t temp64;
634 	int ret;
635 
636 	ret = get_number(in, &temp64, 8);
637 	*value = (time_t)temp64;
638 
639 	return ret;
640 }
641 
get_s32(struct lwm2m_input_context * in,int32_t * value)642 static int get_s32(struct lwm2m_input_context *in, int32_t *value)
643 {
644 	int64_t temp;
645 	int size;
646 
647 	size = get_number(in, &temp, 4);
648 	if (size < 0) {
649 		return size;
650 	}
651 
652 	*value = (int32_t)temp;
653 
654 	return size;
655 }
656 
get_string(struct lwm2m_input_context * in,uint8_t * buf,size_t buflen)657 static int get_string(struct lwm2m_input_context *in, uint8_t *buf,
658 		      size_t buflen)
659 {
660 	struct oma_tlv tlv;
661 	int size;
662 	int ret;
663 
664 	size = oma_tlv_get(&tlv, in, false);
665 	if (size < 0) {
666 		return size;
667 	}
668 
669 	if (buflen <= tlv.length) {
670 		return -ENOMEM;
671 	}
672 
673 	ret = buf_read(buf, tlv.length, CPKT_BUF_READ(in->in_cpkt),
674 		       &in->offset);
675 	if (ret < 0) {
676 		return ret;
677 	}
678 
679 	buf[tlv.length] = '\0';
680 
681 	return size;
682 }
683 
684 /* convert float to fixpoint */
get_float(struct lwm2m_input_context * in,double * value)685 static int get_float(struct lwm2m_input_context *in, double *value)
686 {
687 	struct oma_tlv tlv;
688 	int size;
689 	uint8_t buf[8];
690 	int ret;
691 
692 	size = oma_tlv_get(&tlv, in, false);
693 	if (size < 0) {
694 		return size;
695 	}
696 
697 	if (tlv.length != 4U && tlv.length != 8U) {
698 		LOG_ERR("Invalid float length: %d", tlv.length);
699 		return -EBADMSG;
700 	}
701 
702 	/* read float in network byte order */
703 	ret = buf_read(buf, tlv.length, CPKT_BUF_READ(in->in_cpkt),
704 		       &in->offset);
705 	if (ret < 0) {
706 		return ret;
707 	}
708 
709 	if (tlv.length == 4U) {
710 		ret = lwm2m_b32_to_float(buf, 4, value);
711 	} else {
712 		ret = lwm2m_b64_to_float(buf, 8, value);
713 	}
714 
715 	if (ret < 0) {
716 		LOG_ERR("binary%s conversion error: %d",
717 			tlv.length == 4U ? "32" : "64", ret);
718 		return ret;
719 	}
720 
721 	return size;
722 }
723 
get_bool(struct lwm2m_input_context * in,bool * value)724 static int get_bool(struct lwm2m_input_context *in, bool *value)
725 {
726 	int64_t temp;
727 	int size;
728 
729 	size = get_number(in, &temp, 2);
730 	if (size < 0) {
731 		return size;
732 
733 	}
734 
735 	*value = (temp != 0);
736 
737 	return size;
738 }
739 
get_opaque(struct lwm2m_input_context * in,uint8_t * value,size_t buflen,struct lwm2m_opaque_context * opaque,bool * last_block)740 static int get_opaque(struct lwm2m_input_context *in, uint8_t *value,
741 		      size_t buflen, struct lwm2m_opaque_context *opaque,
742 		      bool *last_block)
743 {
744 	struct oma_tlv tlv;
745 	int size;
746 
747 	/* Get the TLV header only on first read. */
748 	if (opaque->remaining == 0) {
749 		size = oma_tlv_get(&tlv, in, false);
750 		if (size < 0) {
751 			return size;
752 		}
753 
754 		opaque->len = tlv.length;
755 		opaque->remaining = tlv.length;
756 	}
757 
758 	return lwm2m_engine_get_opaque_more(in, value, buflen,
759 					    opaque, last_block);
760 }
761 
get_objlnk(struct lwm2m_input_context * in,struct lwm2m_objlnk * value)762 static int get_objlnk(struct lwm2m_input_context *in,
763 		      struct lwm2m_objlnk *value)
764 {
765 	int32_t value_s32;
766 	int size;
767 
768 	size = get_s32(in, &value_s32);
769 	if (size < 0) {
770 		return size;
771 	}
772 
773 	value->obj_id = (value_s32 >> 16) & 0xFFFF;
774 	value->obj_inst = value_s32 & 0xFFFF;
775 
776 	return size;
777 }
778 
779 const struct lwm2m_writer oma_tlv_writer = {
780 	.put_begin_oi = put_begin_oi,
781 	.put_end_oi = put_end_oi,
782 	.put_begin_ri = put_begin_ri,
783 	.put_end_ri = put_end_ri,
784 	.put_s8 = put_s8,
785 	.put_s16 = put_s16,
786 	.put_s32 = put_s32,
787 	.put_s64 = put_s64,
788 	.put_string = put_string,
789 	.put_float = put_float,
790 	.put_time = put_time,
791 	.put_bool = put_bool,
792 	.put_opaque = put_opaque,
793 	.put_objlnk = put_objlnk,
794 };
795 
796 const struct lwm2m_reader oma_tlv_reader = {
797 	.get_s32 = get_s32,
798 	.get_s64 = get_s64,
799 	.get_string = get_string,
800 	.get_time = get_time,
801 	.get_float = get_float,
802 	.get_bool = get_bool,
803 	.get_opaque = get_opaque,
804 	.get_objlnk = get_objlnk,
805 };
806 
do_read_op_tlv(struct lwm2m_message * msg,int content_format)807 int do_read_op_tlv(struct lwm2m_message *msg, int content_format)
808 {
809 	struct tlv_out_formatter_data fd;
810 	int ret;
811 
812 	(void)memset(&fd, 0, sizeof(fd));
813 	engine_set_out_user_data(&msg->out, &fd);
814 	ret = lwm2m_perform_read_op(msg, content_format);
815 	engine_clear_out_user_data(&msg->out);
816 	return ret;
817 }
818 
do_write_op_tlv_dummy_read(struct lwm2m_message * msg)819 static int do_write_op_tlv_dummy_read(struct lwm2m_message *msg)
820 {
821 	struct oma_tlv tlv;
822 	uint8_t read_char;
823 
824 	oma_tlv_get(&tlv, &msg->in, false);
825 	while (tlv.length--) {
826 		if (buf_read_u8(&read_char, CPKT_BUF_READ(msg->in.in_cpkt),
827 				&msg->in.offset) < 0) {
828 			break;
829 		}
830 	}
831 
832 	return 0;
833 }
834 
do_write_op_tlv_item(struct lwm2m_message * msg)835 static int do_write_op_tlv_item(struct lwm2m_message *msg)
836 {
837 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
838 	struct lwm2m_engine_res *res = NULL;
839 	struct lwm2m_engine_res_inst *res_inst = NULL;
840 	struct lwm2m_engine_obj_field *obj_field = NULL;
841 	uint8_t created = 0U;
842 	int ret;
843 
844 	ret = lwm2m_get_or_create_engine_obj(msg, &obj_inst, &created);
845 	if (ret < 0) {
846 		goto error;
847 	}
848 
849 	ret = lwm2m_engine_validate_write_access(msg, obj_inst, &obj_field);
850 	if (ret < 0) {
851 		goto error;
852 	}
853 
854 	ret = lwm2m_engine_get_create_res_inst(&msg->path, &res, &res_inst);
855 
856 	if (ret < 0) {
857 		/* if OPTIONAL and BOOTSTRAP-WRITE or CREATE use ENOTSUP */
858 		if ((msg->ctx->bootstrap_mode ||
859 		     msg->operation == LWM2M_OP_CREATE) &&
860 		    LWM2M_HAS_PERM(obj_field, BIT(LWM2M_FLAG_OPTIONAL))) {
861 			ret = -ENOTSUP;
862 			goto error;
863 		}
864 
865 		ret = -ENOENT;
866 		goto error;
867 	}
868 
869 	ret = lwm2m_write_handler(obj_inst, res, res_inst, obj_field, msg);
870 	if (ret == -EACCES || ret == -ENOENT) {
871 		/* if read-only or non-existent data buffer move on */
872 		do_write_op_tlv_dummy_read(msg);
873 		ret = 0;
874 	}
875 
876 	return ret;
877 
878 error:
879 	do_write_op_tlv_dummy_read(msg);
880 	return ret;
881 }
882 
write_tlv_resource(struct lwm2m_message * msg,struct oma_tlv * tlv)883 static int write_tlv_resource(struct lwm2m_message *msg, struct oma_tlv *tlv)
884 {
885 	int ret;
886 
887 	if (msg->in.block_ctx) {
888 		msg->in.block_ctx->path.res_id = tlv->id;
889 	}
890 
891 	msg->path.res_id = tlv->id;
892 	msg->path.level = 3U;
893 	ret = do_write_op_tlv_item(msg);
894 
895 	/*
896 	 * ignore errors for CREATE op
897 	 * for OP_CREATE and BOOTSTRAP WRITE: errors on
898 	 * optional resources are ignored (ENOTSUP)
899 	 */
900 	if (ret < 0 &&
901 	    !((ret == -ENOTSUP) &&
902 	      (msg->ctx->bootstrap_mode ||
903 	       msg->operation == LWM2M_OP_CREATE))) {
904 		return ret;
905 	}
906 
907 	return 0;
908 }
909 
lwm2m_multi_resource_tlv_parse(struct lwm2m_message * msg,struct oma_tlv * multi_resource_tlv)910 static int lwm2m_multi_resource_tlv_parse(struct lwm2m_message *msg,
911 					  struct oma_tlv *multi_resource_tlv)
912 {
913 	struct oma_tlv tlv_resource_instance;
914 	int len2;
915 	int pos = 0;
916 	int ret;
917 
918 	if (msg->in.block_ctx) {
919 		msg->in.block_ctx->path.res_id = multi_resource_tlv->id;
920 	}
921 
922 	if (multi_resource_tlv->length == 0U) {
923 		/* No data for resource instances, so create only a resource */
924 		return write_tlv_resource(msg, multi_resource_tlv);
925 	}
926 
927 	while (pos < multi_resource_tlv->length &&
928 	       (len2 = oma_tlv_get(&tlv_resource_instance, &msg->in, true))) {
929 		if (tlv_resource_instance.type != OMA_TLV_TYPE_RESOURCE_INSTANCE) {
930 			LOG_ERR("Multi resource id not supported %u %d", tlv_resource_instance.id,
931 				tlv_resource_instance.length);
932 			return -ENOTSUP;
933 		}
934 
935 		msg->path.res_id = multi_resource_tlv->id;
936 		msg->path.res_inst_id = tlv_resource_instance.id;
937 		msg->path.level = LWM2M_PATH_LEVEL_RESOURCE_INST;
938 		ret = do_write_op_tlv_item(msg);
939 
940 		/*
941 		 * Ignore errors on optional resources when doing
942 		 * BOOTSTRAP WRITE or CREATE operation.
943 		 */
944 		if (ret < 0 && !((ret == -ENOTSUP) &&
945 				 (msg->ctx->bootstrap_mode || msg->operation == LWM2M_OP_CREATE))) {
946 			return ret;
947 		}
948 
949 		pos += len2;
950 	}
951 
952 	return 0;
953 }
954 
do_write_op_tlv(struct lwm2m_message * msg)955 int do_write_op_tlv(struct lwm2m_message *msg)
956 {
957 	struct lwm2m_engine_obj_inst *obj_inst = NULL;
958 	int len;
959 	struct oma_tlv tlv;
960 	int ret;
961 
962 	/* In case of block transfer go directly to the
963 	 * message processing - consecutive blocks will not carry the TLV
964 	 * header.
965 	 */
966 	if (msg->in.block_ctx != NULL && msg->in.block_ctx->ctx.current > 0) {
967 		msg->path.res_id = msg->in.block_ctx->path.res_id;
968 		msg->path.level = 3U;
969 		ret = do_write_op_tlv_item(msg);
970 		if (ret < 0) {
971 			return ret;
972 		}
973 
974 		return 0;
975 	}
976 
977 	while (true) {
978 		/*
979 		 * This initial read of TLV data won't advance frag/offset.
980 		 * We need tlv.type to determine how to proceed.
981 		 */
982 		len = oma_tlv_get(&tlv, &msg->in, true);
983 		if (len < 0) {
984 			break;
985 		}
986 
987 		if (tlv.type == OMA_TLV_TYPE_OBJECT_INSTANCE) {
988 			struct oma_tlv tlv2;
989 			int len2;
990 			int pos = 0;
991 
992 			oma_tlv_get(&tlv, &msg->in, false);
993 			msg->path.obj_inst_id = tlv.id;
994 			if (tlv.length == 0U) {
995 				/* Create only - no data */
996 				ret = lwm2m_create_obj_inst(
997 						msg->path.obj_id,
998 						msg->path.obj_inst_id,
999 						&obj_inst);
1000 				if (ret < 0) {
1001 					return ret;
1002 				}
1003 
1004 				if (!msg->ctx->bootstrap_mode) {
1005 					engine_trigger_update(true);
1006 				}
1007 			}
1008 
1009 			while (pos < tlv.length &&
1010 			       (len2 = oma_tlv_get(&tlv2, &msg->in, true))) {
1011 				if (tlv2.type == OMA_TLV_TYPE_RESOURCE) {
1012 					ret = write_tlv_resource(msg, &tlv2);
1013 					if (ret) {
1014 						return ret;
1015 					}
1016 				} else if (tlv2.type == OMA_TLV_TYPE_MULTI_RESOURCE) {
1017 					oma_tlv_get(&tlv2, &msg->in, false);
1018 					ret = lwm2m_multi_resource_tlv_parse(msg, &tlv2);
1019 					if (ret) {
1020 						return ret;
1021 					}
1022 				} else {
1023 					/* Skip Unsupported TLV type */
1024 					return -ENOTSUP;
1025 				}
1026 
1027 				pos += len2;
1028 			}
1029 		} else if (tlv.type == OMA_TLV_TYPE_RESOURCE) {
1030 			if (msg->path.level < LWM2M_PATH_LEVEL_OBJECT_INST) {
1031 				return -ENOTSUP;
1032 			}
1033 			ret = write_tlv_resource(msg, &tlv);
1034 			if (ret) {
1035 				return ret;
1036 			}
1037 		} else if (tlv.type == OMA_TLV_TYPE_MULTI_RESOURCE) {
1038 			if (msg->path.level < LWM2M_PATH_LEVEL_OBJECT_INST) {
1039 				return -ENOTSUP;
1040 			}
1041 			oma_tlv_get(&tlv, &msg->in, false);
1042 			ret = lwm2m_multi_resource_tlv_parse(msg, &tlv);
1043 			if (ret) {
1044 				return ret;
1045 			}
1046 		} else {
1047 			return -ENOTSUP;
1048 		}
1049 	}
1050 
1051 	return 0;
1052 }
1053