1 /*
2  * Copyright (c) 2023 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/fff.h>
8 #include <zephyr/logging/log.h>
9 #include <zephyr/ztest.h>
10 #include <zephyr/net/socket.h>
11 
12 #include "lwm2m_engine.h"
13 #include "lwm2m_rd_client.h"
14 
15 #include "stubs.h"
16 #if defined(CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME)
17 #include "timer_model.h"
18 #endif
19 
20 #define LOG_LEVEL	LOG_LEVEL_DBG
21 LOG_MODULE_REGISTER(lwm2m_engine_test);
22 
23 DEFINE_FFF_GLOBALS;
24 #define FFF_FAKES_LIST(FAKE)
25 
26 static uint8_t my_buf[256];
27 static uint16_t my_data_len = 1;
28 static struct lwm2m_message my_msg;
29 static struct lwm2m_engine_obj_field my_obj_field;
30 
lwm2m_get_res_buf_custom_fake(const struct lwm2m_obj_path * path,void ** buffer_ptr,uint16_t * buffer_len,uint16_t * data_len,uint8_t * data_flags)31 static int lwm2m_get_res_buf_custom_fake(const struct lwm2m_obj_path *path, void **buffer_ptr,
32 					 uint16_t *buffer_len, uint16_t *data_len,
33 					 uint8_t *data_flags)
34 {
35 	if (buffer_ptr)
36 		*buffer_ptr = my_buf;
37 	if (buffer_len)
38 		*buffer_len = sizeof(my_buf);
39 	if (data_len)
40 		*data_len = my_data_len;
41 
42 	return 0;
43 }
44 
find_msg_custom_fake(struct coap_pending * pending,struct coap_reply * reply)45 static struct lwm2m_message *find_msg_custom_fake(struct coap_pending *pending,
46 						  struct coap_reply *reply)
47 {
48 	return &my_msg;
49 }
50 
51 static struct lwm2m_engine_obj_field *
lwm2m_get_engine_obj_field_custom_fake(struct lwm2m_engine_obj * obj,int res_id)52 lwm2m_get_engine_obj_field_custom_fake(struct lwm2m_engine_obj *obj, int res_id)
53 {
54 	return &my_obj_field;
55 }
56 
lwm2m_get_bool_custom_fake(const struct lwm2m_obj_path * path,bool * value)57 static int lwm2m_get_bool_custom_fake(const struct lwm2m_obj_path *path, bool *value)
58 {
59 	*value = false;
60 
61 	return 0;
62 }
63 
test_service(struct k_work * work)64 static void test_service(struct k_work *work)
65 {
66 	k_sleep(K_MSEC(10));
67 }
68 
setup(void * data)69 static void setup(void *data)
70 {
71 #if defined(CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME)
72 	/* It is enough that some slow-down is happening on sleeps, it does not have to be
73 	 * real time
74 	 */
75 	hwtimer_set_rt_ratio(100.0);
76 #endif
77 	/* Register resets */
78 	DO_FOREACH_FAKE(RESET_FAKE);
79 
80 	/* reset common FFF internal structures */
81 	FFF_RESET_HISTORY();
82 
83 	clear_socket_events();
84 	lwm2m_get_res_buf_fake.custom_fake = lwm2m_get_res_buf_custom_fake;
85 	find_msg_fake.custom_fake = find_msg_custom_fake;
86 	lwm2m_get_engine_obj_field_fake.custom_fake = lwm2m_get_engine_obj_field_custom_fake;
87 	lwm2m_get_bool_fake.custom_fake = lwm2m_get_bool_custom_fake;
88 }
89 
90 ZTEST_SUITE(lwm2m_engine, NULL, NULL, setup, NULL, NULL);
91 
ZTEST(lwm2m_engine,test_start_stop)92 ZTEST(lwm2m_engine, test_start_stop)
93 {
94 	int ret;
95 	struct lwm2m_ctx ctx;
96 	char host_name[10] = "my_host";
97 
98 	(void)memset(&ctx, 0x0, sizeof(ctx));
99 
100 	ctx.remote_addr.sa_family = AF_INET;
101 	ctx.sock_fd = -1;
102 	ctx.load_credentials = NULL;
103 	ctx.desthostname = host_name;
104 	ctx.desthostnamelen = strlen(host_name);
105 	ctx.hostname_verify = true;
106 	ctx.use_dtls = true;
107 
108 	ret = lwm2m_engine_start(&ctx);
109 	zassert_equal(ret, 0);
110 
111 	struct lwm2m_ctx **eng_ctx = lwm2m_sock_ctx();
112 	int nfds = lwm2m_sock_nfds();
113 
114 	zassert_not_null(eng_ctx);
115 	zassert_true(nfds > 0);
116 	zassert_equal(eng_ctx[0], &ctx);
117 
118 	/* wait for socket receive thread */
119 	k_sleep(K_MSEC(1000));
120 	ret = lwm2m_engine_stop(&ctx);
121 	zassert_equal(ret, 0);
122 }
123 
ZTEST(lwm2m_engine,test_pause_resume)124 ZTEST(lwm2m_engine, test_pause_resume)
125 {
126 	int ret;
127 	struct lwm2m_ctx ctx;
128 
129 	(void)memset(&ctx, 0x0, sizeof(ctx));
130 
131 	ctx.remote_addr.sa_family = AF_INET;
132 	ctx.sock_fd = -1;
133 	ctx.load_credentials = NULL;
134 
135 	ret = lwm2m_engine_start(&ctx);
136 	zassert_equal(ret, 0);
137 	ret = lwm2m_engine_resume();
138 	zassert_equal(ret, -EPERM);
139 	ret = lwm2m_engine_pause();
140 	zassert_equal(ret, 0);
141 	ret = lwm2m_engine_pause();
142 	zassert_equal(ret, 0);
143 	ret = lwm2m_engine_resume();
144 	zassert_equal(ret, 0);
145 	ret = lwm2m_engine_stop(&ctx);
146 	zassert_equal(ret, 0);
147 }
148 
ZTEST(lwm2m_engine,test_engine_add_service)149 ZTEST(lwm2m_engine, test_engine_add_service)
150 {
151 	int ret;
152 	struct lwm2m_ctx ctx;
153 
154 	(void)memset(&ctx, 0x0, sizeof(ctx));
155 
156 	ctx.remote_addr.sa_family = AF_INET;
157 	ctx.load_credentials = NULL;
158 
159 	ret = lwm2m_engine_start(&ctx);
160 	zassert_equal(ret, 0);
161 	ret = lwm2m_engine_add_service(test_service, 1000);
162 	zassert_equal(ret, 0);
163 	/* wait for socket receive thread */
164 	k_sleep(K_MSEC(1500));
165 	ret = lwm2m_engine_update_service_period(test_service, 500);
166 	zassert_equal(ret, 0);
167 	ret = lwm2m_engine_stop(&ctx);
168 	zassert_equal(ret, 0);
169 }
170 
ZTEST(lwm2m_engine,test_no_sa_family)171 ZTEST(lwm2m_engine, test_no_sa_family)
172 {
173 	int ret;
174 	struct lwm2m_ctx ctx;
175 
176 	(void)memset(&ctx, 0x0, sizeof(ctx));
177 
178 	ctx.sock_fd = -1;
179 	ctx.load_credentials = NULL;
180 
181 	ret = lwm2m_engine_start(&ctx);
182 	zassert_equal(ret, -EPROTONOSUPPORT);
183 	lwm2m_engine_stop(&ctx);
184 }
185 
ZTEST(lwm2m_engine,test_connect_fail)186 ZTEST(lwm2m_engine, test_connect_fail)
187 {
188 	int ret;
189 	struct lwm2m_ctx ctx;
190 
191 	(void)memset(&ctx, 0x0, sizeof(ctx));
192 
193 	ctx.sock_fd = -1;
194 	ctx.load_credentials = NULL;
195 	ctx.remote_addr.sa_family = AF_INET;
196 
197 	errno = ENETDOWN;
198 	z_impl_zsock_connect_fake.return_val = -1;
199 	ret = lwm2m_engine_start(&ctx);
200 	zassert_equal(ret, -ENETDOWN);
201 	lwm2m_engine_stop(&ctx);
202 }
203 
ZTEST(lwm2m_engine,test_socket_suspend_resume_connection)204 ZTEST(lwm2m_engine, test_socket_suspend_resume_connection)
205 {
206 	int ret;
207 	struct lwm2m_ctx ctx;
208 
209 	(void)memset(&ctx, 0x0, sizeof(ctx));
210 
211 	ctx.sock_fd = -1;
212 	ctx.load_credentials = NULL;
213 	ctx.remote_addr.sa_family = AF_INET;
214 
215 	ret = lwm2m_engine_start(&ctx);
216 	zassert_equal(ret, 0);
217 	ret = lwm2m_socket_suspend(&ctx);
218 	zassert_equal(ret, 0);
219 	zassert_equal(ctx.connection_suspended, true);
220 	ret = lwm2m_engine_connection_resume(&ctx);
221 	zassert_equal(ret, 0);
222 	zassert_equal(ctx.connection_suspended, false);
223 	lwm2m_engine_stop(&ctx);
224 }
225 
ZTEST(lwm2m_engine,test_check_notifications)226 ZTEST(lwm2m_engine, test_check_notifications)
227 {
228 	int ret;
229 	struct lwm2m_ctx ctx;
230 	struct observe_node obs;
231 
232 	(void)memset(&ctx, 0x0, sizeof(ctx));
233 
234 	ctx.sock_fd = -1;
235 	ctx.load_credentials = NULL;
236 	ctx.remote_addr.sa_family = AF_INET;
237 	sys_slist_init(&ctx.observer);
238 
239 	obs.last_timestamp = k_uptime_get();
240 	obs.event_timestamp = k_uptime_get() + 1000U;
241 	obs.resource_update = false;
242 	obs.active_notify = NULL;
243 
244 	sys_slist_append(&ctx.observer, &obs.node);
245 
246 	lwm2m_rd_client_is_registred_fake.return_val = true;
247 	ret = lwm2m_engine_start(&ctx);
248 	zassert_equal(ret, 0);
249 	/* wait for socket receive thread */
250 	k_sleep(K_MSEC(2000));
251 	ret = lwm2m_engine_stop(&ctx);
252 	zassert_equal(ret, 0);
253 	zassert_equal(generate_notify_message_fake.call_count, 1, "Notify message not generated");
254 	zassert_equal(engine_observe_shedule_next_event_fake.call_count, 1,
255 		      "Next observe event not scheduled");
256 }
257 
ZTEST(lwm2m_engine,test_push_queued_buffers)258 ZTEST(lwm2m_engine, test_push_queued_buffers)
259 {
260 	int ret;
261 	struct lwm2m_ctx ctx;
262 	struct lwm2m_message msg;
263 	struct coap_pending pending;
264 
265 	(void)memset(&ctx, 0x0, sizeof(ctx));
266 
267 	sys_slist_init(&ctx.queued_messages);
268 	msg.ctx = &ctx;
269 	msg.pending = &pending;
270 	sys_slist_append(&ctx.queued_messages, &msg.node);
271 	ret = lwm2m_push_queued_buffers(&ctx);
272 	zassert_equal(ret, 0);
273 }
274 
ZTEST(lwm2m_engine,test_validate_write_access)275 ZTEST(lwm2m_engine, test_validate_write_access)
276 {
277 	int ret;
278 	struct lwm2m_ctx ctx;
279 	struct lwm2m_message msg;
280 	struct lwm2m_engine_res resources;
281 	struct lwm2m_engine_obj_inst obj_inst;
282 	struct lwm2m_engine_obj_field *obj_field = NULL;
283 
284 	(void)memset(&ctx, 0x0, sizeof(ctx));
285 
286 	ctx.bootstrap_mode = true;
287 	msg.ctx = &ctx;
288 	msg.path = LWM2M_OBJ(LWM2M_OBJECT_SECURITY_ID, 0);
289 	obj_inst.resources = &resources;
290 	obj_inst.resource_count = 1U;
291 	ret = lwm2m_engine_validate_write_access(&msg, &obj_inst, &obj_field);
292 	zassert_equal(ret, 0);
293 
294 	obj_inst.resource_count = 0U;
295 	ret = lwm2m_engine_validate_write_access(&msg, &obj_inst, &obj_field);
296 	zassert_equal(ret, -EINVAL);
297 
298 	msg.path = LWM2M_OBJ(LWM2M_OBJECT_DEVICE_ID, 0);
299 	ret = lwm2m_engine_validate_write_access(&msg, &obj_inst, &obj_field);
300 	zassert_equal(ret, -EPERM);
301 }
302 
ZTEST(lwm2m_engine,test_bootstrap_delete)303 ZTEST(lwm2m_engine, test_bootstrap_delete)
304 {
305 	int ret;
306 	struct lwm2m_message msg;
307 
308 	msg.path = LWM2M_OBJ(LWM2M_OBJECT_SECURITY_ID, 0, 0);
309 	ret = bootstrap_delete(&msg);
310 	zassert_equal(ret, -EPERM);
311 
312 	msg.path = LWM2M_OBJ(LWM2M_OBJECT_SECURITY_ID, 1);
313 	ret = bootstrap_delete(&msg);
314 	zassert_equal(ret, 0);
315 	zassert_equal(0, lwm2m_delete_obj_inst_fake.arg0_history[0]);
316 	zassert_equal(1, lwm2m_delete_obj_inst_fake.arg1_history[0]);
317 
318 
319 	struct lwm2m_engine_obj sec_obj = {.obj_id = 0};
320 	struct lwm2m_engine_obj_inst sec_inst = {
321 		.obj_inst_id = 2,
322 		.obj = &sec_obj
323 	};
324 	sys_slist_append(lwm2m_engine_obj_inst_list(), &sec_inst.node);
325 
326 	msg.path = LWM2M_OBJ(LWM2M_OBJECT_SECURITY_ID);
327 	ret = bootstrap_delete(&msg);
328 	zassert_equal(ret, 0);
329 	zassert_equal(0, lwm2m_delete_obj_inst_fake.arg0_history[1]);
330 	zassert_equal(2, lwm2m_delete_obj_inst_fake.arg1_history[1]);
331 
332 	msg.path = LWM2M_OBJ(LWM2M_OBJECT_DEVICE_ID, 0);
333 	ret = bootstrap_delete(&msg);
334 	zassert_equal(ret, -EPERM);
335 }
336 
ZTEST(lwm2m_engine,test_retransmit_request)337 ZTEST(lwm2m_engine, test_retransmit_request)
338 {
339 	int ret;
340 	struct lwm2m_ctx ctx;
341 	struct coap_pending pending_1;
342 	struct coap_pending pending_2;
343 
344 	(void)memset(&ctx, 0x0, sizeof(ctx));
345 
346 	ctx.sock_fd = -1;
347 	ctx.load_credentials = NULL;
348 	ctx.remote_addr.sa_family = AF_INET;
349 
350 	pending_1.t0 = k_uptime_get();
351 	pending_1.timeout = 200U;
352 	pending_1.retries = 0;
353 	ctx.pendings[0] = pending_1;
354 
355 	pending_2.t0 = k_uptime_get();
356 	pending_2.timeout = 200U;
357 	pending_2.retries = 1;
358 	ctx.pendings[1] = pending_2;
359 
360 	ret = lwm2m_engine_start(&ctx);
361 	zassert_equal(ret, 0);
362 	/* wait for socket receive thread */
363 	k_sleep(K_MSEC(500));
364 	ret = lwm2m_engine_stop(&ctx);
365 	zassert_equal(ret, 0);
366 	zassert_not_equal(lwm2m_reset_message_fake.call_count, 0, "Message was not reseted");
367 	zassert_not_equal(lwm2m_send_message_async_fake.call_count, 0, "Message was not sent");
368 }
369 
ZTEST(lwm2m_engine,test_socket_recv)370 ZTEST(lwm2m_engine, test_socket_recv)
371 {
372 	int ret;
373 	struct lwm2m_ctx ctx;
374 
375 	(void)memset(&ctx, 0x0, sizeof(ctx));
376 
377 	ctx.remote_addr.sa_family = AF_INET;
378 	ctx.sock_fd = -1;
379 
380 	set_socket_events(ZSOCK_POLLIN);
381 
382 	ret = lwm2m_engine_start(&ctx);
383 	zassert_equal(ret, 0);
384 	/* wait for socket receive thread */
385 	k_sleep(K_MSEC(1000));
386 	ret = lwm2m_engine_stop(&ctx);
387 	zassert_equal(ret, 0);
388 	zassert_true(lwm2m_udp_receive_fake.call_count > 0);
389 }
390 
ZTEST(lwm2m_engine,test_socket_send)391 ZTEST(lwm2m_engine, test_socket_send)
392 {
393 	int ret;
394 	struct lwm2m_ctx ctx;
395 	struct lwm2m_message msg;
396 	struct coap_pending pending;
397 
398 	(void)memset(&ctx, 0x0, sizeof(ctx));
399 
400 	ctx.remote_addr.sa_family = AF_INET;
401 	ctx.sock_fd = -1;
402 	sys_slist_init(&ctx.queued_messages);
403 	msg.ctx = &ctx;
404 	msg.pending = &pending;
405 	msg.type = COAP_TYPE_CON;
406 	sys_slist_append(&ctx.queued_messages, &msg.node);
407 
408 	ret = lwm2m_push_queued_buffers(&ctx);
409 	zassert_equal(ret, 0);
410 
411 	set_socket_events(ZSOCK_POLLOUT);
412 
413 	ret = lwm2m_engine_start(&ctx);
414 	zassert_equal(ret, 0);
415 	/* wait for socket receive thread */
416 	k_sleep(K_MSEC(2000));
417 	ret = lwm2m_engine_stop(&ctx);
418 	zassert_equal(ret, 0);
419 	LOG_INF("Count %d", coap_pending_cycle_fake.call_count);
420 	zassert_equal(coap_pending_cycle_fake.call_count, 1, "coap_pending_cycle not called");
421 }
422 
ZTEST(lwm2m_engine,test_security)423 ZTEST(lwm2m_engine, test_security)
424 {
425 	struct lwm2m_ctx ctx;
426 	char host_name[10] = "my_host";
427 
428 	(void)memset(&ctx, 0x0, sizeof(ctx));
429 	my_data_len = snprintk(my_buf, sizeof(my_buf), "-----BEGIN SOMETHING");
430 
431 	ctx.remote_addr.sa_family = AF_INET;
432 	ctx.sock_fd = -1;
433 	ctx.load_credentials = NULL;
434 	ctx.desthostname = host_name;
435 	ctx.desthostnamelen = strlen(host_name);
436 	ctx.hostname_verify = true;
437 	ctx.use_dtls = false;
438 
439 	lwm2m_security_mode_fake.return_val = LWM2M_SECURITY_NOSEC;
440 
441 	zassert_equal(lwm2m_engine_start(&ctx), 0);
442 	zassert_equal(lwm2m_engine_stop(&ctx), 0);
443 
444 	ctx.use_dtls = true;
445 	zassert_equal(lwm2m_engine_start(&ctx), -EINVAL);
446 	zassert_equal(lwm2m_engine_stop(&ctx), 0);
447 
448 	RESET_FAKE(z_impl_zsock_setsockopt);
449 	lwm2m_security_mode_fake.return_val = LWM2M_SECURITY_PSK;
450 	zassert_equal(lwm2m_engine_start(&ctx), 0);
451 	zassert_equal(z_impl_zsock_setsockopt_fake.arg2_history[0], TLS_SEC_TAG_LIST);
452 	zassert_equal(z_impl_zsock_setsockopt_fake.arg2_history[1], TLS_HOSTNAME);
453 	zassert_equal(z_impl_zsock_setsockopt_fake.arg2_history[2], TLS_PEER_VERIFY);
454 	zassert_equal(z_impl_zsock_setsockopt_fake.arg2_history[3], TLS_CIPHERSUITE_LIST);
455 	zassert_true(tls_credential_delete_fake.call_count > 3);
456 	zassert_true(tls_credential_add_fake.call_count == 2);
457 	zassert_equal(tls_credential_add_fake.arg1_history[0], TLS_CREDENTIAL_PSK_ID);
458 	zassert_equal(tls_credential_add_fake.arg1_history[1], TLS_CREDENTIAL_PSK);
459 	zassert_equal(lwm2m_engine_stop(&ctx), 0);
460 
461 	RESET_FAKE(z_impl_zsock_setsockopt);
462 	RESET_FAKE(tls_credential_add);
463 	lwm2m_security_mode_fake.return_val = LWM2M_SECURITY_CERT;
464 	ctx.hostname_verify = false;
465 	zassert_equal(lwm2m_engine_start(&ctx), 0);
466 	zassert_equal(z_impl_zsock_setsockopt_fake.arg2_history[0], TLS_SEC_TAG_LIST);
467 	zassert_equal(z_impl_zsock_setsockopt_fake.arg2_history[1], TLS_PEER_VERIFY);
468 	zassert_equal(z_impl_zsock_setsockopt_fake.arg2_history[2], TLS_CIPHERSUITE_LIST);
469 	zassert_true(tls_credential_add_fake.call_count == 3);
470 	zassert_equal(tls_credential_add_fake.arg1_history[0], TLS_CREDENTIAL_SERVER_CERTIFICATE);
471 	zassert_equal(tls_credential_add_fake.arg1_history[1], TLS_CREDENTIAL_PRIVATE_KEY);
472 	zassert_equal(tls_credential_add_fake.arg1_history[2], TLS_CREDENTIAL_CA_CERTIFICATE);
473 	zassert_equal(lwm2m_engine_stop(&ctx), 0);
474 }
475 
476 static enum lwm2m_socket_states last_state;
477 
socket_state(int fd,enum lwm2m_socket_states state)478 static void socket_state(int fd, enum lwm2m_socket_states state)
479 {
480 	(void) fd;
481 	last_state = state;
482 }
483 
ZTEST(lwm2m_engine,test_socket_state)484 ZTEST(lwm2m_engine, test_socket_state)
485 {
486 	int ret;
487 	struct lwm2m_ctx ctx = {
488 		.remote_addr.sa_family = AF_INET,
489 		.sock_fd = -1,
490 		.set_socket_state = socket_state,
491 	};
492 	struct lwm2m_message msg1 = {
493 		.ctx = &ctx,
494 		.type = COAP_TYPE_CON,
495 	};
496 	struct lwm2m_message msg2 = msg1;
497 	struct lwm2m_message ack = {
498 		.ctx = &ctx,
499 		.type = COAP_TYPE_ACK,
500 	};
501 
502 	sys_slist_init(&ctx.pending_sends);
503 	ret = lwm2m_engine_start(&ctx);
504 	zassert_equal(ret, 0);
505 
506 	/* One confimable in queue, should cause ONE_RESPONSE status */
507 	coap_pendings_count_fake.return_val = 1;
508 	sys_slist_append(&ctx.pending_sends, &msg1.node);
509 	set_socket_events(ZSOCK_POLLOUT);
510 	k_sleep(K_MSEC(100));
511 	zassert_equal(last_state, LWM2M_SOCKET_STATE_ONE_RESPONSE);
512 
513 	/* More than one messages in queue, not empty, should cause ONGOING */
514 	coap_pendings_count_fake.return_val = 2;
515 	sys_slist_append(&ctx.pending_sends, &msg1.node);
516 	sys_slist_append(&ctx.pending_sends, &msg2.node);
517 	set_socket_events(ZSOCK_POLLOUT);
518 	k_sleep(K_MSEC(100));
519 	zassert_equal(last_state, LWM2M_SOCKET_STATE_ONGOING);
520 
521 	/* Last out, while waiting for ACK to both, should still cause ONGOING */
522 	coap_pendings_count_fake.return_val = 2;
523 	set_socket_events(ZSOCK_POLLOUT);
524 	k_sleep(K_MSEC(100));
525 	zassert_equal(last_state, LWM2M_SOCKET_STATE_ONGOING);
526 
527 	/* Only one Ack transmiting, nothing expected back -> LAST */
528 	coap_pendings_count_fake.return_val = 0;
529 	sys_slist_append(&ctx.pending_sends, &ack.node);
530 	set_socket_events(ZSOCK_POLLOUT);
531 	k_sleep(K_MSEC(100));
532 	zassert_equal(last_state, LWM2M_SOCKET_STATE_LAST);
533 
534 	/* Socket suspended (as in QUEUE_RX_OFF), should cause NO_DATA */
535 	ret = lwm2m_socket_suspend(&ctx);
536 	zassert_equal(ret, 0);
537 	zassert_equal(last_state, LWM2M_SOCKET_STATE_NO_DATA);
538 
539 	ret = lwm2m_engine_stop(&ctx);
540 	zassert_equal(ret, 0);
541 }
542