1 /*
2  * Copyright (c) 2019 Linaro Limited
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <errno.h>
7 #include <stdbool.h>
8 
9 #include <zephyr/net/sntp.h>
10 #include <zephyr/net/socketutils.h>
11 
sntp_simple_helper(struct sockaddr * addr,socklen_t addr_len,uint32_t timeout,struct sntp_time * ts)12 static int sntp_simple_helper(struct sockaddr *addr, socklen_t addr_len, uint32_t timeout,
13 			      struct sntp_time *ts)
14 {
15 	int res;
16 	struct sntp_ctx sntp_ctx;
17 	uint64_t deadline;
18 	uint32_t iter_timeout;
19 	bool first_iter;
20 
21 	res = sntp_init(&sntp_ctx, addr, addr_len);
22 	if (res < 0) {
23 		return res;
24 	}
25 
26 	if (timeout == SYS_FOREVER_MS) {
27 		deadline = (uint64_t)timeout;
28 	} else {
29 		deadline = k_uptime_get() + (uint64_t)timeout;
30 	}
31 
32 	/* Timeout for current iteration */
33 	iter_timeout = 100;
34 	first_iter = true;
35 
36 	while (k_uptime_get() < deadline) {
37 		res = sntp_query(&sntp_ctx, iter_timeout, ts);
38 
39 		if (res != -ETIMEDOUT) {
40 			if (false == first_iter && -ERANGE == res) {
41 				while (-ERANGE == res) {
42 					/* Possible out of order packet received.
43 					 * Retry recv with current iteration timeout
44 					 * until an error or timeout (flushing the socket
45 					 * of old iteration responses until we timeout or
46 					 * receive our iteration's response)
47 					 */
48 					res = sntp_recv_response(&sntp_ctx, iter_timeout, ts);
49 				}
50 
51 				if (res != ETIMEDOUT) {
52 					break;
53 				}
54 			} else {
55 				break;
56 			}
57 		}
58 
59 		/* Exponential backoff with limit */
60 		if (iter_timeout < 1000) {
61 			iter_timeout *= 2;
62 		}
63 
64 		if (first_iter) {
65 			first_iter = false;
66 		}
67 	}
68 
69 	sntp_close(&sntp_ctx);
70 
71 	return res;
72 }
73 
sntp_simple_addr(struct sockaddr * addr,socklen_t addr_len,uint32_t timeout,struct sntp_time * ts)74 int sntp_simple_addr(struct sockaddr *addr, socklen_t addr_len, uint32_t timeout,
75 		     struct sntp_time *ts)
76 {
77 	/* 123 is the standard SNTP port per RFC4330 */
78 	int res = net_port_set_default(addr, 123);
79 
80 	if (res < 0) {
81 		return res;
82 	}
83 
84 	return sntp_simple_helper(addr, addr_len, timeout, ts);
85 }
86 
sntp_simple(const char * server,uint32_t timeout,struct sntp_time * ts)87 int sntp_simple(const char *server, uint32_t timeout, struct sntp_time *ts)
88 {
89 	int res;
90 	static struct zsock_addrinfo hints;
91 	struct zsock_addrinfo *addr;
92 
93 	hints.ai_family = AF_UNSPEC;
94 	hints.ai_socktype = SOCK_DGRAM;
95 	hints.ai_protocol = 0;
96 	/* 123 is the standard SNTP port per RFC4330 */
97 	res = net_getaddrinfo_addr_str(server, "123", &hints, &addr);
98 	if (res < 0) {
99 		/* Just in case, as namespace for getaddrinfo errors is
100 		 * different from errno errors.
101 		 */
102 		errno = EDOM;
103 		return res;
104 	}
105 	res = sntp_simple_helper(addr->ai_addr, addr->ai_addrlen, timeout, ts);
106 
107 	zsock_freeaddrinfo(addr);
108 
109 	return res;
110 }
111