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