1 /*
2  * Copyright (C) 2017 XRADIO TECHNOLOGY CO., LTD. All rights reserved.
3  *
4  *  Redistribution and use in source and binary forms, with or without
5  *  modification, are permitted provided that the following conditions
6  *  are met:
7  *    1. Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *    2. Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the
12  *       distribution.
13  *    3. Neither the name of XRADIO TECHNOLOGY CO., LTD. nor the names of
14  *       its contributors may be used to endorse or promote products derived
15  *       from this software without specific prior written permission.
16  *
17  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  *  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  *  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  *  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  *  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  *  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  *  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <unistd.h>
31 
32 #include "sntp/sntp.h"
33 #include "sys/socket.h"
34 #include "netdb.h"
35 #include "sys/time.h"
36 #include "string.h"
37 #include "stdlib.h"
38 #include "errno.h"
39 
40 #include "ulog/ulog.h"
41 
42 #define TAG            "sntp"
43 
44 #define SNTP_DEBUG(format, ...)
45 #define SNTP_INFO(format, ...)     LOGI(TAG, format, ##__VA_ARGS__)
46 #define SNTP_ERROR(format, ...)    LOGE(TAG, format, ##__VA_ARGS__)
47 #define SNTP_WRANING(format, ...)  LOGW(TAG, format, ##__VA_ARGS__)
48 
49 /* SNTP protocol defines */
50 #define SNTP_MSG_LEN                48
51 #define SNTP_LI_NO_WARNING          0x00
52 #define SNTP_VERSION                3 /* NTP Version */
53 #define SNTP_MODE_MASK              0x07
54 #define SNTP_MODE_CLIENT            0x03
55 #define SNTP_MODE_SERVER            0x04
56 #define SNTP_MODE_BROADCAST         0x05
57 #define SNTP_STRATUM_KOD            0x00
58 /* number of seconds between 1900 and 1970 */
59 #define DIFF_SEC_1900_1970         (2208988800UL)
60 /* number of seconds between 1970 and Feb 7, 2036 (6:28:16 UTC) (MSB=0) */
61 #define DIFF_SEC_1970_2036         (2085978496UL)
62 
63 typedef struct
64 {
65   uint8_t li_vn_mode;	   // Eight bits. li, vn, and mode.
66 						   // li.	Two bits.	Leap indicator.
67 						   // vn.	Three bits. Version number of the protocol.
68 						   // mode. Three bits. Client will pick mode 3 for client.
69 
70   uint8_t stratum;		   // Eight bits. Stratum level of the local clock.
71   uint8_t poll; 		   // Eight bits. Maximum interval between successive messages.
72   uint8_t precision;	   // Eight bits. Precision of the local clock.
73 
74   uint32_t rootDelay;	   // 32 bits. Total round trip delay time.
75   uint32_t rootDispersion; // 32 bits. Max error aloud from primary clock source.
76   uint32_t refId;		   // 32 bits. Reference clock identifier.
77 
78   uint32_t refTm_s; 	   // 32 bits. Reference time-stamp seconds.
79   uint32_t refTm_f; 	   // 32 bits. Reference time-stamp fraction of a second.
80 
81   uint32_t origTm_s;	   // 32 bits. Originate time-stamp seconds.
82   uint32_t origTm_f;	   // 32 bits. Originate time-stamp fraction of a second.
83 
84   uint32_t rxTm_s;		   // 32 bits. Received time-stamp seconds.
85   uint32_t rxTm_f;		   // 32 bits. Received time-stamp fraction of a second.
86 
87   uint32_t txTm_s;		   // 32 bits and the most important field the client cares about. Transmit time-stamp seconds.
88   uint32_t txTm_f;		   // 32 bits. Transmit time-stamp fraction of a second.
89 } ntp_packet;			   // Total: 384 bits or 48 bytes.
90 
91 static sntp_time g_time;
92 
93 static struct timezone g_timezone = { 0, 0 };
94 
95 static int g_last_query_status = -1;
96 
97 // Sets time zone.
sntp_set_timezone(const struct timezone * tz)98 void sntp_set_timezone(const struct timezone *tz) {
99 	if (tz) {
100 		g_timezone = *tz;
101 	} else {
102 		g_timezone.tz_minuteswest = 0;
103 		g_timezone.tz_dsttime = 0;
104 	}
105 }
106 
107 #if SNTP_SUPPORT_MULTIPLE_SERVERS
108 typedef struct {
109   struct in_addr addr;
110 } sntp_server_info;
111 
112 static sntp_server_info sntp_servers[SNTP_MAX_SERVERS];
113 
sntp_get_server_addr(uint8_t idx)114 static const struct in_addr* sntp_get_server_addr(uint8_t idx)
115 {
116 	if (idx < SNTP_MAX_SERVERS) {
117 		return &sntp_servers[idx].addr;
118 	}
119 	return INADDR_ANY;
120 }
121 
sntp_set_server_addr(uint8_t idx,struct in_addr * server_addr)122 static int sntp_set_server_addr(uint8_t idx, struct in_addr *server_addr)
123 {
124 	if (idx >= SNTP_MAX_SERVERS)
125 		return -1;
126 	sntp_servers[idx].addr.s_addr = server_addr->s_addr;
127 	return 0;
128 }
129 
130 /**
131   * @brief Set the remote ntp server address by name.
132   * @note
133   * @param idx: numdns the index of the NTP server to set must be < SNTP_MAX_SERVERS.
134   *        arg: servers name that want to set. such as: "cn.ntp.org.cn" "118.24.4.66"
135   * @retval 0:success -1:fail
136   */
sntp_set_server(uint8_t idx,char * server)137 int sntp_set_server(uint8_t idx, char *server)
138 {
139 	struct hostent *host;
140 
141 	if (idx >= SNTP_MAX_SERVERS)
142 		return -1;
143 
144 	host = gethostbyname(server);
145 	if (!host) {
146 		SNTP_ERROR("invalid address parameter '%s'",server);
147 		return -1;
148 	}
149 
150 	sntp_set_server_addr(idx, (struct in_addr *)host->h_addr);
151 	return 0;
152 }
153 #endif
154 
sntp_init_request_packet(ntp_packet * packet)155 static void sntp_init_request_packet(ntp_packet *packet)
156 {
157 	struct timeval local_time;
158 
159 	memset(packet, 0, sizeof(ntp_packet));
160 	gettimeofday(&local_time, NULL);
161 	packet->li_vn_mode = SNTP_LI_NO_WARNING | SNTP_VERSION << 3 | SNTP_MODE_CLIENT;
162 	packet->txTm_s = htonl(local_time.tv_sec);
163 	packet->txTm_f = htonl(local_time.tv_usec);
164 }
165 
sntp_process_time(ntp_packet * packet,struct timeval * time)166 static int sntp_process_time(ntp_packet *packet, struct timeval *time)
167 {
168 	struct timeval local_time;
169 	uint32_t t0_s, t1_s, t2_s, t3_s, real_time_s;
170 	uint32_t t0_us, t1_us, t2_us, t3_us, real_time_us;
171 	int deta1, deta2, transmit_time;
172 
173 	if (((packet->li_vn_mode & SNTP_MODE_MASK) == SNTP_MODE_SERVER) ||
174 		((packet->li_vn_mode & SNTP_MODE_MASK) == SNTP_MODE_BROADCAST)) {
175 		gettimeofday(&local_time, NULL);
176 
177 		t0_s = ntohl(packet->origTm_s);
178 		t0_us = ntohl(packet->origTm_f);
179 
180 		t3_s = local_time.tv_sec;
181 		t3_us = local_time.tv_usec;
182 
183 		t1_s =  ntohl(packet->rxTm_s);
184 		t1_us = ntohl(packet->rxTm_f) / 4295;
185 
186 		t2_s =  ntohl(packet->txTm_s);
187 		t2_us = ntohl(packet->txTm_f) / 4295;
188 
189 		deta1 = (t3_s - t0_s) * 1000000 + (t3_us - t0_us); /* us */
190 		deta2 = (t2_s - t1_s) * 1000000 + (t2_us - t1_us); /* us */
191 
192 		transmit_time = (deta1 - deta2) / 2; /* us */
193 
194 		real_time_s = t2_s + (t2_us + transmit_time) / 1000000;
195 		real_time_us = (t2_us + transmit_time) % 1000000;
196 
197 		int is_1900_based = ((real_time_s & 0x80000000) != 0);
198 		real_time_s = is_1900_based ? (real_time_s - DIFF_SEC_1900_1970) : (real_time_s + DIFF_SEC_1970_2036);
199 		time->tv_sec = real_time_s + (g_timezone.tz_minuteswest + g_timezone.tz_dsttime * 60) * 60;
200 		time->tv_usec = real_time_us;
201 	} else {
202 		SNTP_ERROR("sntp_request: not response frame code");
203 		return -1;
204 	}
205 	return 0;
206 }
207 
sntp_query_server(sntp_arg * arg,struct timeval * ntp_time)208 static int sntp_query_server(sntp_arg *arg, struct timeval *ntp_time)
209 {
210 	ntp_packet packet;
211 	int sockfd = -1;
212 	struct sockaddr_in server_addr;
213 	struct hostent *host;
214 	char *server_name = NULL;
215 	int ret = 0;
216 	struct timeval timeout_val;
217 #if SNTP_SUPPORT_MULTIPLE_SERVERS
218 	uint8_t idx = 0;
219 #endif
220 
221 	memset(&server_addr, 0, sizeof(server_addr));
222 
223 	/* choose the right server */
224 	do {
225 		if (arg && arg->server_name) {
226 			server_name = arg->server_name;
227 			break;
228 		}
229 #if SNTP_SUPPORT_MULTIPLE_SERVERS
230 		while(idx < SNTP_MAX_SERVERS && sntp_get_server_addr(idx)->s_addr == INADDR_ANY)
231 			idx++;
232 		if (idx < SNTP_MAX_SERVERS) {
233 			server_name = NULL;
234 			server_addr.sin_addr.s_addr = sntp_get_server_addr(idx)->s_addr;
235 			break;
236 		}
237 #endif
238 		server_name = SNTP_SERVER_ADDRESS;
239 	} while(0);
240 
241 	if (server_name) {
242 		host = gethostbyname(server_name);
243 		if (!host) {
244 			SNTP_ERROR("invalid address parameter '%s'",server_name);
245 			ret = -1;
246 			goto exit;
247 		}
248 		server_addr.sin_addr.s_addr  = (*(struct in_addr *)host->h_addr).s_addr;
249 	}
250 
251 	server_addr.sin_family = AF_INET;
252 	server_addr.sin_port = htons(SNTP_PORT);
253 
254 	sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
255 	if (sockfd < 0) {
256 		SNTP_ERROR("socket create err!");
257 		ret = -1;
258 		goto exit;
259 	}
260 
261 	int val = 1;
262 	if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int)) != 0) {
263 		SNTP_ERROR("setsockopt(SO_REUSEADDR) failed");
264 		ret = -1;
265 		goto exit;
266 	}
267 
268 	int timeout_ms = arg && arg->recv_timeout > 0 ? arg->recv_timeout : SNTP_RECV_TIMEOUT;
269 	timeout_val.tv_sec = timeout_ms / 1000;
270 	timeout_val.tv_usec = timeout_ms % 1000 * 1000;
271 	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout_val, sizeof(struct timeval)) != 0) {
272 		SNTP_ERROR("setsockopt(SO_RCVTIMEO) failed %d %d", arg->recv_timeout, errno);
273 		ret = -1;
274 		goto exit;
275 	}
276 
277 #if SNTP_SUPPORT_MULTIPLE_SERVERS
278 send_request:
279 #endif
280 	SNTP_DEBUG("server name:%s", server_name ? server_name : "NULL");
281 	SNTP_DEBUG("server ip:%s", inet_ntoa(server_addr.sin_addr));
282 
283 	int retry_times = arg ? arg->retry_times : SNTP_RETRY_TIMES;
284 
285 	do {
286 		sntp_init_request_packet(&packet);
287 
288 		ret = sendto(sockfd, &packet, sizeof(packet), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
289 		if (ret <= 0) {
290 			SNTP_ERROR("send err,ret=%d,errno:%d", ret, errno);
291 			ret = -1;
292 			continue;
293 		}
294 
295 		ret = recvfrom(sockfd, &packet, sizeof(packet), 0, NULL, NULL);
296 		if (ret != SNTP_MSG_LEN) {
297 			if (errno != EAGAIN)
298 			    SNTP_ERROR("recv err,ret=%d,errno:%d", ret, errno);
299 			ret = -1;
300 			continue;
301 		} else {
302 			if ((ret = sntp_process_time(&packet, ntp_time)) != 0)
303 				ret = -1;
304 			else
305 				goto exit;
306 		}
307 	} while (--retry_times && retry_times > 0);
308 
309 #if SNTP_SUPPORT_MULTIPLE_SERVERS
310 	/* try and get the next ntp server */
311 	if (server_name == NULL) {
312 		for (idx++; idx < SNTP_MAX_SERVERS; idx++) {
313 			if (sntp_get_server_addr(idx)->s_addr != INADDR_ANY) {
314 				server_name = NULL;
315 				server_addr.sin_addr.s_addr = sntp_get_server_addr(idx)->s_addr;
316 				goto send_request;
317 			}
318 		}
319 	}
320 #endif
321 
322 exit:
323 	if (sockfd >= 0)
324 		close(sockfd);
325 	if (ret)
326 		return -1;
327 
328 	return 0;
329 }
330 
331 #define MIN_SNTP_SET_INTERVAL_MS 5000
332 static long long m_sntp_set_time = 0;
333 
334 /**
335   * @brief Get time from the remote server.
336   * @note This a blocking interface.
337   * @param ntp_time: Pointer to the struct timeval.
338   *        arg: The pointer of sntp module parameter
339   * @retval 0:success -1:fail
340   */
sntp_get_time(sntp_arg * arg,struct timeval * ntp_time)341 int sntp_get_time(sntp_arg *arg, struct timeval *ntp_time)
342 {
343     int ret = 0;
344 
345 	ret = sntp_query_server(arg, ntp_time);
346     if (ret == 0) {
347     	if (m_sntp_set_time == 0 ||
348     		aos_now_ms() - m_sntp_set_time > MIN_SNTP_SET_INTERVAL_MS) {
349     		aos_calendar_time_set(ntp_time->tv_sec * (uint64_t)1000 + (uint64_t)(ntp_time->tv_usec / 1000));
350     	    m_sntp_set_time = aos_now_ms();
351         }
352     }
353 
354     g_last_query_status = ret;
355 
356     return ret;
357 }
358 
359 /**
360  * Send an SNTP request via sockets. This interface has been deprecated.
361  * This is a very minimal implementation that does not fully conform
362  * to the SNTPv4 RFC, especially regarding server load and error procesing.
363  */
sntp_request(void * arg)364 int sntp_request(void *arg)
365 {
366 	struct timeval ntp_time;
367 	struct tm *gt;
368 	int ret;
369 
370 	ret = sntp_query_server(NULL, &ntp_time);
371 	if (ret != 0)
372 		return -1;
373 
374 	gt = gmtime(&ntp_time.tv_sec);
375 	if (gt==NULL) {
376 		return -1;
377 	}
378 
379 	g_time.year = gt->tm_year % 100;
380 	g_time.mon = gt->tm_mon + 1;
381 	g_time.day = gt->tm_mday;
382 	g_time.week = gt->tm_wday;
383 	g_time.hour = gt->tm_hour + 8;
384 	g_time.min = gt->tm_min;
385 	g_time.sec = gt->tm_sec;
386 
387 	return 0;
388 }
389 
390 /**
391  * obtain time
392  * This interface has been deprecated.
393  */
sntp_obtain_time(void)394 sntp_time *sntp_obtain_time(void)
395 {
396 	return &g_time;
397 }
398 
399 /*
400 * check if last sntp query is OK
401 * @param: None
402 * @retval: 0 success -1 fail
403 */
sntp_last_query_status(void)404 int sntp_last_query_status(void)
405 {
406 	return g_last_query_status;
407 }
408 
409 /*
410 * set system calender time directly
411 * @param: current time in millisecond
412 * @retval: 0 success -1 fail
413 */
sntp_set_time_direct(long long now_ms)414 int sntp_set_time_direct(long long now_ms)
415 {
416 	aos_calendar_time_set(now_ms);
417 
418 	return 0;
419 }
420 
421 
422