1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 LOG_MODULE_REGISTER(net_coap_client_sample, LOG_LEVEL_DBG);
9 
10 #include <errno.h>
11 #include <zephyr/sys/printk.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/kernel.h>
14 
15 #include <zephyr/net/socket.h>
16 #include <zephyr/net/net_mgmt.h>
17 #include <zephyr/net/net_ip.h>
18 #include <zephyr/net/udp.h>
19 #include <zephyr/net/coap.h>
20 
21 #include "net_private.h"
22 
23 #define PEER_PORT 5683
24 #define MAX_COAP_MSG_LEN 256
25 
26 /* CoAP socket fd */
27 static int sock;
28 
29 struct pollfd fds[1];
30 static int nfds;
31 
32 /* CoAP Options */
33 static const char * const test_path[] = { "test", NULL };
34 
35 static const char * const large_path[] = { "large", NULL };
36 
37 static const char * const obs_path[] = { "obs", NULL };
38 
39 #define BLOCK_WISE_TRANSFER_SIZE_GET 2048
40 
41 static struct coap_block_context blk_ctx;
42 
wait(void)43 static void wait(void)
44 {
45 	if (poll(fds, nfds, -1) < 0) {
46 		LOG_ERR("Error in poll:%d", errno);
47 	}
48 }
49 
prepare_fds(void)50 static void prepare_fds(void)
51 {
52 	fds[nfds].fd = sock;
53 	fds[nfds].events = POLLIN;
54 	nfds++;
55 }
56 
start_coap_client(void)57 static int start_coap_client(void)
58 {
59 	int ret = 0;
60 	struct sockaddr_in6 addr6;
61 
62 	addr6.sin6_family = AF_INET6;
63 	addr6.sin6_port = htons(PEER_PORT);
64 	addr6.sin6_scope_id = 0U;
65 
66 	inet_pton(AF_INET6, CONFIG_NET_CONFIG_PEER_IPV6_ADDR,
67 		  &addr6.sin6_addr);
68 
69 	sock = socket(addr6.sin6_family, SOCK_DGRAM, IPPROTO_UDP);
70 	if (sock < 0) {
71 		LOG_ERR("Failed to create UDP socket %d", errno);
72 		return -errno;
73 	}
74 
75 	ret = connect(sock, (struct sockaddr *)&addr6, sizeof(addr6));
76 	if (ret < 0) {
77 		LOG_ERR("Cannot connect to UDP remote : %d", errno);
78 		return -errno;
79 	}
80 
81 	prepare_fds();
82 
83 	return 0;
84 }
85 
process_simple_coap_reply(void)86 static int process_simple_coap_reply(void)
87 {
88 	struct coap_packet reply;
89 	uint8_t *data;
90 	int rcvd;
91 	int ret;
92 
93 	wait();
94 
95 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
96 	if (!data) {
97 		return -ENOMEM;
98 	}
99 
100 	rcvd = recv(sock, data, MAX_COAP_MSG_LEN, MSG_DONTWAIT);
101 	if (rcvd == 0) {
102 		ret = -EIO;
103 		goto end;
104 	}
105 
106 	if (rcvd < 0) {
107 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
108 			ret = 0;
109 		} else {
110 			ret = -errno;
111 		}
112 
113 		goto end;
114 	}
115 
116 	net_hexdump("Response", data, rcvd);
117 
118 	ret = coap_packet_parse(&reply, data, rcvd, NULL, 0);
119 	if (ret < 0) {
120 		LOG_ERR("Invalid data received");
121 	}
122 
123 end:
124 	k_free(data);
125 
126 	return ret;
127 }
128 
send_simple_coap_request(uint8_t method)129 static int send_simple_coap_request(uint8_t method)
130 {
131 	uint8_t payload[] = "payload";
132 	struct coap_packet request;
133 	const char * const *p;
134 	uint8_t *data;
135 	int r;
136 
137 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
138 	if (!data) {
139 		return -ENOMEM;
140 	}
141 
142 	r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
143 			     COAP_VERSION_1, COAP_TYPE_CON,
144 			     COAP_TOKEN_MAX_LEN, coap_next_token(),
145 			     method, coap_next_id());
146 	if (r < 0) {
147 		LOG_ERR("Failed to init CoAP message");
148 		goto end;
149 	}
150 
151 	for (p = test_path; p && *p; p++) {
152 		r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
153 					      *p, strlen(*p));
154 		if (r < 0) {
155 			LOG_ERR("Unable add option to request");
156 			goto end;
157 		}
158 	}
159 
160 	switch (method) {
161 	case COAP_METHOD_GET:
162 	case COAP_METHOD_DELETE:
163 		break;
164 
165 	case COAP_METHOD_PUT:
166 	case COAP_METHOD_POST:
167 		r = coap_packet_append_payload_marker(&request);
168 		if (r < 0) {
169 			LOG_ERR("Unable to append payload marker");
170 			goto end;
171 		}
172 
173 		r = coap_packet_append_payload(&request, (uint8_t *)payload,
174 					       sizeof(payload) - 1);
175 		if (r < 0) {
176 			LOG_ERR("Not able to append payload");
177 			goto end;
178 		}
179 
180 		break;
181 	default:
182 		r = -EINVAL;
183 		goto end;
184 	}
185 
186 	net_hexdump("Request", request.data, request.offset);
187 
188 	r = send(sock, request.data, request.offset, 0);
189 
190 end:
191 	k_free(data);
192 
193 	return r;
194 }
195 
send_simple_coap_msgs_and_wait_for_reply(void)196 static int send_simple_coap_msgs_and_wait_for_reply(void)
197 {
198 	uint8_t test_type = 0U;
199 	int r;
200 
201 	while (1) {
202 		switch (test_type) {
203 		case 0:
204 			/* Test CoAP GET method */
205 			printk("\nCoAP client GET\n");
206 			r = send_simple_coap_request(COAP_METHOD_GET);
207 			if (r < 0) {
208 				return r;
209 			}
210 
211 			break;
212 		case 1:
213 			/* Test CoAP PUT method */
214 			printk("\nCoAP client PUT\n");
215 			r = send_simple_coap_request(COAP_METHOD_PUT);
216 			if (r < 0) {
217 				return r;
218 			}
219 
220 			break;
221 		case 2:
222 			/* Test CoAP POST method*/
223 			printk("\nCoAP client POST\n");
224 			r = send_simple_coap_request(COAP_METHOD_POST);
225 			if (r < 0) {
226 				return r;
227 			}
228 
229 			break;
230 		case 3:
231 			/* Test CoAP DELETE method*/
232 			printk("\nCoAP client DELETE\n");
233 			r = send_simple_coap_request(COAP_METHOD_DELETE);
234 			if (r < 0) {
235 				return r;
236 			}
237 
238 			break;
239 		default:
240 			return 0;
241 		}
242 
243 		r = process_simple_coap_reply();
244 		if (r < 0) {
245 			return r;
246 		}
247 
248 		test_type++;
249 	}
250 
251 	return 0;
252 }
253 
process_large_coap_reply(void)254 static int process_large_coap_reply(void)
255 {
256 	struct coap_packet reply;
257 	uint8_t *data;
258 	bool last_block;
259 	int rcvd;
260 	int ret;
261 
262 	wait();
263 
264 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
265 	if (!data) {
266 		return -ENOMEM;
267 	}
268 
269 	rcvd = recv(sock, data, MAX_COAP_MSG_LEN, MSG_DONTWAIT);
270 	if (rcvd == 0) {
271 		ret = -EIO;
272 		goto end;
273 	}
274 
275 	if (rcvd < 0) {
276 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
277 			ret = 0;
278 		} else {
279 			ret = -errno;
280 		}
281 
282 		goto end;
283 	}
284 
285 	net_hexdump("Response", data, rcvd);
286 
287 	ret = coap_packet_parse(&reply, data, rcvd, NULL, 0);
288 	if (ret < 0) {
289 		LOG_ERR("Invalid data received");
290 		goto end;
291 	}
292 
293 	ret = coap_update_from_block(&reply, &blk_ctx);
294 	if (ret < 0) {
295 		goto end;
296 	}
297 
298 	last_block = coap_next_block(&reply, &blk_ctx);
299 	if (!last_block) {
300 		ret = 1;
301 		goto end;
302 	}
303 
304 	ret = 0;
305 
306 end:
307 	k_free(data);
308 
309 	return ret;
310 }
311 
send_large_coap_request(void)312 static int send_large_coap_request(void)
313 {
314 	struct coap_packet request;
315 	const char * const *p;
316 	uint8_t *data;
317 	int r;
318 
319 	if (blk_ctx.total_size == 0) {
320 		coap_block_transfer_init(&blk_ctx, COAP_BLOCK_64,
321 					 BLOCK_WISE_TRANSFER_SIZE_GET);
322 	}
323 
324 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
325 	if (!data) {
326 		return -ENOMEM;
327 	}
328 
329 	r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
330 			     COAP_VERSION_1, COAP_TYPE_CON,
331 			     COAP_TOKEN_MAX_LEN, coap_next_token(),
332 			     COAP_METHOD_GET, coap_next_id());
333 	if (r < 0) {
334 		LOG_ERR("Failed to init CoAP message");
335 		goto end;
336 	}
337 
338 	for (p = large_path; p && *p; p++) {
339 		r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
340 					      *p, strlen(*p));
341 		if (r < 0) {
342 			LOG_ERR("Unable add option to request");
343 			goto end;
344 		}
345 	}
346 
347 	r = coap_append_block2_option(&request, &blk_ctx);
348 	if (r < 0) {
349 		LOG_ERR("Unable to add block2 option.");
350 		goto end;
351 	}
352 
353 	net_hexdump("Request", request.data, request.offset);
354 
355 	r = send(sock, request.data, request.offset, 0);
356 
357 end:
358 	k_free(data);
359 
360 	return r;
361 }
362 
get_large_coap_msgs(void)363 static int get_large_coap_msgs(void)
364 {
365 	int r;
366 
367 	while (1) {
368 		/* Test CoAP Large GET method */
369 		printk("\nCoAP client Large GET (block %zd)\n",
370 		       blk_ctx.current / 64 /*COAP_BLOCK_64*/);
371 		r = send_large_coap_request();
372 		if (r < 0) {
373 			return r;
374 		}
375 
376 		r = process_large_coap_reply();
377 		if (r < 0) {
378 			return r;
379 		}
380 
381 		/* Received last block */
382 		if (r == 1) {
383 			memset(&blk_ctx, 0, sizeof(blk_ctx));
384 			return 0;
385 		}
386 	}
387 
388 	return 0;
389 }
390 
send_obs_reply_ack(uint16_t id)391 static void send_obs_reply_ack(uint16_t id)
392 {
393 	struct coap_packet request;
394 	uint8_t *data;
395 	int r;
396 
397 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
398 	if (!data) {
399 		return;
400 	}
401 
402 	r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
403 			     COAP_VERSION_1, COAP_TYPE_ACK, 0, NULL, 0, id);
404 	if (r < 0) {
405 		LOG_ERR("Failed to init CoAP message");
406 		goto end;
407 	}
408 
409 	net_hexdump("ACK", request.data, request.offset);
410 
411 	r = send(sock, request.data, request.offset, 0);
412 	if (r < 0) {
413 		LOG_ERR("Failed to send CoAP ACK");
414 	}
415 end:
416 	k_free(data);
417 }
418 
419 
obs_notification_cb(const struct coap_packet * response,struct coap_reply * reply,const struct sockaddr * from)420 static int obs_notification_cb(const struct coap_packet *response,
421 			       struct coap_reply *reply,
422 			       const struct sockaddr *from)
423 {
424 	uint16_t id = coap_header_get_id(response);
425 	uint8_t type = coap_header_get_type(response);
426 	uint8_t *counter = (uint8_t *)reply->user_data;
427 
428 	ARG_UNUSED(from);
429 
430 	printk("\nCoAP OBS Notification\n");
431 
432 	(*counter)++;
433 
434 	if (type == COAP_TYPE_CON) {
435 		send_obs_reply_ack(id);
436 	}
437 
438 	return 0;
439 }
440 
process_obs_coap_reply(struct coap_reply * reply)441 static int process_obs_coap_reply(struct coap_reply *reply)
442 {
443 	struct coap_packet reply_msg;
444 	uint8_t *data;
445 	int rcvd;
446 	int ret;
447 
448 	wait();
449 
450 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
451 	if (!data) {
452 		return -ENOMEM;
453 	}
454 
455 	rcvd = recv(sock, data, MAX_COAP_MSG_LEN, MSG_DONTWAIT);
456 	if (rcvd == 0) {
457 		ret = -EIO;
458 		goto end;
459 	}
460 
461 	if (rcvd < 0) {
462 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
463 			ret = 0;
464 		} else {
465 			ret = -errno;
466 		}
467 
468 		goto end;
469 	}
470 
471 	net_hexdump("Response", data, rcvd);
472 
473 	ret = coap_packet_parse(&reply_msg, data, rcvd, NULL, 0);
474 	if (ret < 0) {
475 		LOG_ERR("Invalid data received");
476 		goto end;
477 	}
478 
479 	if (coap_response_received(&reply_msg, NULL, reply, 1) == NULL) {
480 		printk("\nOther response received\n");
481 	}
482 
483 end:
484 	k_free(data);
485 
486 	return ret;
487 }
488 
send_obs_coap_request(struct coap_reply * reply,void * user_data)489 static int send_obs_coap_request(struct coap_reply *reply, void *user_data)
490 {
491 	struct coap_packet request;
492 	const char * const *p;
493 	uint8_t *data;
494 	int r;
495 
496 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
497 	if (!data) {
498 		return -ENOMEM;
499 	}
500 
501 	r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
502 			     COAP_VERSION_1, COAP_TYPE_CON,
503 			     COAP_TOKEN_MAX_LEN, coap_next_token(),
504 			     COAP_METHOD_GET, coap_next_id());
505 	if (r < 0) {
506 		LOG_ERR("Failed to init CoAP message");
507 		goto end;
508 	}
509 
510 	r = coap_append_option_int(&request, COAP_OPTION_OBSERVE, 0);
511 	if (r < 0) {
512 		LOG_ERR("Failed to append Observe option");
513 		goto end;
514 	}
515 
516 	for (p = obs_path; p && *p; p++) {
517 		r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
518 					      *p, strlen(*p));
519 		if (r < 0) {
520 			LOG_ERR("Unable add option to request");
521 			goto end;
522 		}
523 	}
524 
525 	net_hexdump("Request", request.data, request.offset);
526 
527 	coap_reply_init(reply, &request);
528 	reply->reply = obs_notification_cb;
529 	reply->user_data = user_data;
530 
531 	r = send(sock, request.data, request.offset, 0);
532 
533 end:
534 	k_free(data);
535 
536 	return r;
537 }
538 
send_obs_reset_coap_request(struct coap_reply * reply)539 static int send_obs_reset_coap_request(struct coap_reply *reply)
540 {
541 	struct coap_packet request;
542 	const char * const *p;
543 	uint8_t *data;
544 	int r;
545 
546 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
547 	if (!data) {
548 		return -ENOMEM;
549 	}
550 
551 	r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
552 			     COAP_VERSION_1, COAP_TYPE_CON,
553 			     reply->tkl, reply->token,
554 			     COAP_METHOD_GET, coap_next_id());
555 	if (r < 0) {
556 		LOG_ERR("Failed to init CoAP message");
557 		goto end;
558 	}
559 
560 	r = coap_append_option_int(&request, COAP_OPTION_OBSERVE, 1);
561 	if (r < 0) {
562 		LOG_ERR("Failed to append Observe option");
563 		goto end;
564 	}
565 
566 	for (p = obs_path; p && *p; p++) {
567 		r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
568 					      *p, strlen(*p));
569 		if (r < 0) {
570 			LOG_ERR("Unable add option to request");
571 			goto end;
572 		}
573 	}
574 
575 	net_hexdump("Request", request.data, request.offset);
576 
577 	r = send(sock, request.data, request.offset, 0);
578 
579 end:
580 	k_free(data);
581 
582 	return r;
583 }
584 
register_observer(void)585 static int register_observer(void)
586 {
587 	struct coap_reply reply;
588 	uint8_t counter = 0;
589 	int r;
590 
591 	printk("\nCoAP client OBS GET\n");
592 	r = send_obs_coap_request(&reply, &counter);
593 	if (r < 0) {
594 		return r;
595 	}
596 
597 	while (1) {
598 		r = process_obs_coap_reply(&reply);
599 		if (r < 0) {
600 			return r;
601 		}
602 
603 		if (counter >= 5) {
604 			/* TODO: Functionality can be verified byt waiting for
605 			 * some time and make sure client shouldn't receive
606 			 * any notifications. If client still receives
607 			 * notifications means, Observer is not removed.
608 			 */
609 			r = send_obs_reset_coap_request(&reply);
610 			if (r < 0) {
611 				return r;
612 			}
613 
614 			/* Wait for the final ACK */
615 			r = process_obs_coap_reply(&reply);
616 			if (r < 0) {
617 				return r;
618 			}
619 
620 			break;
621 		}
622 	}
623 
624 	return 0;
625 }
626 
main(void)627 int main(void)
628 {
629 	int r;
630 
631 	LOG_DBG("Start CoAP-client sample");
632 	r = start_coap_client();
633 	if (r < 0) {
634 		goto quit;
635 	}
636 
637 	/* GET, PUT, POST, DELETE */
638 	r = send_simple_coap_msgs_and_wait_for_reply();
639 	if (r < 0) {
640 		goto quit;
641 	}
642 
643 	/* Block-wise transfer */
644 	r = get_large_coap_msgs();
645 	if (r < 0) {
646 		goto quit;
647 	}
648 
649 	/* Register observer, get notifications and unregister */
650 	r = register_observer();
651 	if (r < 0) {
652 		goto quit;
653 	}
654 
655 	/* Close the socket */
656 	(void)close(sock);
657 
658 	LOG_DBG("Done");
659 
660 	return 0;
661 
662 quit:
663 	(void)close(sock);
664 
665 	LOG_ERR("quit");
666 	return 0;
667 }
668