1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <logging/log.h>
8 LOG_MODULE_REGISTER(net_coap_client_sample, LOG_LEVEL_DBG);
9 
10 #include <errno.h>
11 #include <sys/printk.h>
12 #include <sys/byteorder.h>
13 #include <zephyr.h>
14 
15 #include <net/socket.h>
16 #include <net/net_mgmt.h>
17 #include <net/net_ip.h>
18 #include <net/udp.h>
19 #include <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 0;
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,uint8_t * token,uint8_t tkl)391 static int send_obs_reply_ack(uint16_t id, uint8_t *token, uint8_t tkl)
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 -ENOMEM;
400 	}
401 
402 	r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
403 			     COAP_VERSION_1, COAP_TYPE_ACK, tkl, token, 0, id);
404 	if (r < 0) {
405 		LOG_ERR("Failed to init CoAP message");
406 		goto end;
407 	}
408 
409 	net_hexdump("Request", request.data, request.offset);
410 
411 	r = send(sock, request.data, request.offset, 0);
412 end:
413 	k_free(data);
414 
415 	return r;
416 }
417 
process_obs_coap_reply(void)418 static int process_obs_coap_reply(void)
419 {
420 	struct coap_packet reply;
421 	uint16_t id;
422 	uint8_t token[COAP_TOKEN_MAX_LEN];
423 	uint8_t *data;
424 	uint8_t type;
425 	uint8_t tkl;
426 	int rcvd;
427 	int ret;
428 
429 	wait();
430 
431 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
432 	if (!data) {
433 		return -ENOMEM;
434 	}
435 
436 	rcvd = recv(sock, data, MAX_COAP_MSG_LEN, MSG_DONTWAIT);
437 	if (rcvd == 0) {
438 		ret = -EIO;
439 		goto end;
440 	}
441 
442 	if (rcvd < 0) {
443 		if (errno == EAGAIN || errno == EWOULDBLOCK) {
444 			ret = 0;
445 		} else {
446 			ret = -errno;
447 		}
448 
449 		goto end;
450 	}
451 
452 	net_hexdump("Response", data, rcvd);
453 
454 	ret = coap_packet_parse(&reply, data, rcvd, NULL, 0);
455 	if (ret < 0) {
456 		LOG_ERR("Invalid data received");
457 		goto end;
458 	}
459 
460 	tkl = coap_header_get_token(&reply, token);
461 	id = coap_header_get_id(&reply);
462 
463 	type = coap_header_get_type(&reply);
464 	if (type == COAP_TYPE_ACK) {
465 		ret = 0;
466 	} else if (type == COAP_TYPE_CON) {
467 		ret = send_obs_reply_ack(id, token, tkl);
468 	}
469 end:
470 	k_free(data);
471 
472 	return ret;
473 }
474 
send_obs_coap_request(void)475 static int send_obs_coap_request(void)
476 {
477 	struct coap_packet request;
478 	const char * const *p;
479 	uint8_t *data;
480 	int r;
481 
482 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
483 	if (!data) {
484 		return -ENOMEM;
485 	}
486 
487 	r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
488 			     COAP_VERSION_1, COAP_TYPE_CON,
489 			     COAP_TOKEN_MAX_LEN, coap_next_token(),
490 			     COAP_METHOD_GET, coap_next_id());
491 	if (r < 0) {
492 		LOG_ERR("Failed to init CoAP message");
493 		goto end;
494 	}
495 
496 	r = coap_append_option_int(&request, COAP_OPTION_OBSERVE, 0);
497 	if (r < 0) {
498 		LOG_ERR("Failed to append Observe option");
499 		goto end;
500 	}
501 
502 	for (p = obs_path; p && *p; p++) {
503 		r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
504 					      *p, strlen(*p));
505 		if (r < 0) {
506 			LOG_ERR("Unable add option to request");
507 			goto end;
508 		}
509 	}
510 
511 	net_hexdump("Request", request.data, request.offset);
512 
513 	r = send(sock, request.data, request.offset, 0);
514 
515 end:
516 	k_free(data);
517 
518 	return r;
519 }
520 
send_obs_reset_coap_request(void)521 static int send_obs_reset_coap_request(void)
522 {
523 	struct coap_packet request;
524 	const char * const *p;
525 	uint8_t *data;
526 	int r;
527 
528 	data = (uint8_t *)k_malloc(MAX_COAP_MSG_LEN);
529 	if (!data) {
530 		return -ENOMEM;
531 	}
532 
533 	r = coap_packet_init(&request, data, MAX_COAP_MSG_LEN,
534 			     COAP_VERSION_1, COAP_TYPE_RESET,
535 			     COAP_TOKEN_MAX_LEN, coap_next_token(),
536 			     0, coap_next_id());
537 	if (r < 0) {
538 		LOG_ERR("Failed to init CoAP message");
539 		goto end;
540 	}
541 
542 	r = coap_append_option_int(&request, COAP_OPTION_OBSERVE, 0);
543 	if (r < 0) {
544 		LOG_ERR("Failed to append Observe option");
545 		goto end;
546 	}
547 
548 	for (p = obs_path; p && *p; p++) {
549 		r = coap_packet_append_option(&request, COAP_OPTION_URI_PATH,
550 					      *p, strlen(*p));
551 		if (r < 0) {
552 			LOG_ERR("Unable add option to request");
553 			goto end;
554 		}
555 	}
556 
557 	net_hexdump("Request", request.data, request.offset);
558 
559 	r = send(sock, request.data, request.offset, 0);
560 
561 end:
562 	k_free(data);
563 
564 	return r;
565 }
566 
register_observer(void)567 static int register_observer(void)
568 {
569 	uint8_t counter = 0U;
570 	int r;
571 
572 	while (1) {
573 		/* Test CoAP OBS GET method */
574 		if (!counter) {
575 			printk("\nCoAP client OBS GET\n");
576 			r = send_obs_coap_request();
577 			if (r < 0) {
578 				return r;
579 			}
580 		} else {
581 			printk("\nCoAP OBS Notification\n");
582 		}
583 
584 		r = process_obs_coap_reply();
585 		if (r < 0) {
586 			return r;
587 		}
588 
589 		counter++;
590 
591 		/* Unregister */
592 		if (counter == 5U) {
593 			/* TODO: Functionality can be verified byt waiting for
594 			 * some time and make sure client shouldn't receive
595 			 * any notifications. If client still receives
596 			 * notifications means, Observer is not removed.
597 			 */
598 			return send_obs_reset_coap_request();
599 		}
600 	}
601 
602 	return 0;
603 }
604 
main(void)605 void main(void)
606 {
607 	int r;
608 
609 	LOG_DBG("Start CoAP-client sample");
610 	r = start_coap_client();
611 	if (r < 0) {
612 		goto quit;
613 	}
614 
615 	/* GET, PUT, POST, DELETE */
616 	r = send_simple_coap_msgs_and_wait_for_reply();
617 	if (r < 0) {
618 		goto quit;
619 	}
620 
621 	/* Block-wise transfer */
622 	r = get_large_coap_msgs();
623 	if (r < 0) {
624 		goto quit;
625 	}
626 
627 	/* Register observer, get notifications and unregister */
628 	r = register_observer();
629 	if (r < 0) {
630 		goto quit;
631 	}
632 
633 	/* Close the socket */
634 	(void)close(sock);
635 
636 	LOG_DBG("Done");
637 
638 	return;
639 
640 quit:
641 	(void)close(sock);
642 
643 	LOG_ERR("quit");
644 }
645