1/*
2 *  libsntp_query.c
3 *  ntp
4 *
5 *  Created by Morgan Grainger on 2/23/11.
6 *  Copyright 2011-2014 Apple Inc. All rights reserved.
7 *
8 */
9
10#include <sys/time.h>
11#include <sys/socket.h>
12#include <dispatch/dispatch.h>
13#include <os/trace.h>
14#include <mach/mach_time.h>
15
16#include "networking.h"
17
18#include "libsntp.h"
19
20#define NTP_SERVICE_PORT 123
21#define GETTIMEOFDAY_FAILED -100
22
23volatile int debug;
24char *progname = "libsntp";	/* for msyslog */
25
26/* Forward declarations */
27sntp_query_result_t on_wire (struct addrinfo *host, bool use_service_port, int numTries, struct timeval timeout, /* out */ uint64_t *out_mach_time, /* out */ struct timeval *out_time, /* out */ double *out_delay, /* out */ double *out_dispersion, int *retry_attempts);
28void set_li_vn_mode (struct pkt *spkt, char leap, char version, char mode);
29void adjust_tv_by_offset(struct timeval *tv, double offset);
30
31void
32sntp_query(char *host, bool use_service_port, sntp_query_result_handler_t result_handler)
33{
34	struct timeval attempt_timeout = {15, 0};
35	return sntp_query_extended(host, "123", use_service_port, 5, attempt_timeout, ^(sntp_query_result_t query_result, uint64_t mach_timestamp, struct timeval time, double delay, double dispersion, bool more_servers, const char *ip_addr, const char *port, int retry_attempts){
36		return result_handler(query_result, time, delay, dispersion, more_servers);
37	});
38}
39
40void
41sntp_query_extended(const char *host, const char *servname, bool use_service_port, int attempts, struct timeval attempt_timeout, sntp_query_extended_result_handler_t result_handler)
42{
43	__block char *our_host = strdup(host);
44	__block char *our_servname = strdup(servname?:"123");
45
46	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
47		char adr_buf[INET6_ADDRSTRLEN];
48		char serv_buf[NI_MAXSERV];
49		struct addrinfo **resolved_hosts = NULL;
50
51		uint64_t mach_timestamp = 0;
52		struct timeval time_estimate;
53		double delay, dispersion;
54
55		if (resolve_hosts(&our_host, 1, our_servname, &resolved_hosts, AF_INET) != 1) {
56#ifdef DEBUG
57			fprintf(stderr, "Unable to resolve hostname %s\n", our_host);
58#endif
59			result_handler(SNTP_RESULT_FAILURE_DNS, mach_timestamp, time_estimate, delay, dispersion, false, NULL, NULL, 0);
60		} else {
61			struct addrinfo *ai = resolved_hosts[0];
62
63			do {
64				int retry_attempts;
65				sntp_query_result_t this_result = on_wire(ai, use_service_port, attempts, attempt_timeout, &mach_timestamp, &time_estimate, &delay, &dispersion, &retry_attempts);
66
67				adr_buf[0] = serv_buf[0] = 0;
68				int name_error = getnameinfo(ai->ai_addr, ai->ai_addrlen, adr_buf, sizeof(adr_buf), serv_buf, sizeof(serv_buf), NI_NUMERICHOST|NI_NUMERICSERV);
69				if (name_error) {
70					fprintf(stderr, "libsntp: getnameinfo: %d: %s", name_error, gai_strerror(name_error));
71					adr_buf[0] = serv_buf[0] = 0;
72				}
73
74				if (this_result != SNTP_RESULT_SUCCESS) {
75#ifdef DEBUG
76					fprintf(stderr, "on_wire failed for server %s:%s!\n", adr_buf, serv_buf);
77#endif
78				}
79
80				ai = ai->ai_next;
81				if (!result_handler(this_result, mach_timestamp, time_estimate, delay, dispersion, (ai != NULL), adr_buf, serv_buf, retry_attempts)) {
82					break;
83				}
84			} while (ai != NULL);
85
86			freeaddrinfo(resolved_hosts[0]);
87			free(resolved_hosts);
88		}
89
90		free(our_host);
91		free(our_servname);
92	});
93}
94
95/* The heart of (S)NTP, exchange NTP packets and compute values to correct the local clock */
96sntp_query_result_t
97on_wire (
98		 struct addrinfo *host,
99		 bool use_service_port,
100		 const int numTries,
101		 struct timeval timeout,
102		 /* out */ uint64_t *out_mach_time,
103		 /* out */ struct timeval *out_time,
104		 /* out */ double *out_delay,
105		 /* out */ double *out_dispersion,
106		 /* out */ int *retry_attempts
107		 )
108{
109	char addr_buf[INET6_ADDRSTRLEN];
110	SOCKET sock = -1;
111	struct pkt x_pkt;
112	struct pkt r_pkt;
113	sntp_query_result_t result = SNTP_RESULT_FAILURE_INTERNAL;
114
115
116	for(*retry_attempts=0; *retry_attempts<numTries; ++*retry_attempts) {
117		struct timeval tv_xmt, tv_dst;
118		double t21, t34, delta, offset, precision, root_dispersion;
119		int digits, rpktl, sw_case;
120		u_fp p_rdly, p_rdsp;
121		l_fp p_rec, p_xmt, p_ref, p_org, xmt, tmp, dst;
122
123		memset(&r_pkt, 0, sizeof(r_pkt));
124		memset(&x_pkt, 0, sizeof(x_pkt));
125
126		create_socket(&sock, (sockaddr_u *)host->ai_addr);
127		if (sock < 0) {
128			os_trace("socket failed errno:%d", errno);
129			continue;
130		}
131		if (use_service_port) {
132			struct sockaddr_in send_addr;
133			int reuse = 1;
134			bzero(&send_addr, sizeof(send_addr));
135
136			setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));
137
138			send_addr.sin_family = host->ai_addr->sa_family;
139			send_addr.sin_addr.s_addr = INADDR_ANY;
140			send_addr.sin_port = htons(NTP_SERVICE_PORT);
141			if (0 != bind(sock, (struct sockaddr *)&send_addr, sizeof(send_addr))) {
142				os_trace("bind failed errno:%d", errno);
143				result = SNTP_RESULT_FAILURE_CANNOT_BIND_SOCKET;
144				closesocket(sock);
145				return result;
146			}
147		}
148
149		if (GETTIMEOFDAY(&tv_xmt, (struct timezone *)NULL)) {
150			fprintf(stderr, "libsntp: gettimeofday failed: %d: %s\n", errno, strerror(errno));
151			rpktl = GETTIMEOFDAY_FAILED;
152		} else {
153			tv_xmt.tv_sec += JAN_1970;
154
155#ifdef DEBUG
156			printf("sntp on_wire: Current time sec: %i msec: %i\n", (unsigned int) tv_xmt.tv_sec,
157				   (unsigned int) tv_xmt.tv_usec);
158#endif
159
160			TVTOTS(&tv_xmt, &xmt);
161			HTONL_FP(&xmt, &(x_pkt.xmt));
162
163			x_pkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
164			x_pkt.ppoll = 8;
165			/* FIXME! Modus broadcast + adr. check -> bdr. pkt */
166			set_li_vn_mode(&x_pkt, LEAP_NOTINSYNC, 4, 3);
167
168			if (0 == sendpkt(sock, (sockaddr_u *)host->ai_addr, &x_pkt, LEN_PKT_NOMAC)) {
169				rpktl = recvpkt(sock, timeout, &r_pkt, &x_pkt);
170			} else {
171				rpktl = SERVER_UNUSEABLE;
172				fprintf(stderr, "libsntp: sendpkt failed\n");
173			}
174		}
175
176		*out_mach_time = mach_absolute_time();
177		if (GETTIMEOFDAY(&tv_dst, (struct timezone *)NULL)) {
178			fprintf(stderr, "libsntp: gettimeofday failed: %d: %s\n", errno, strerror(errno));
179			if (rpktl > 0) {
180				rpktl = GETTIMEOFDAY_FAILED;
181			}
182		}
183
184
185		closesocket(sock);
186
187		if(rpktl > 0)
188			sw_case = 1;
189		else
190			sw_case = rpktl;
191
192		switch(sw_case) {
193			case SERVER_UNUSEABLE:
194				result = SNTP_RESULT_FAILURE_SERVER_UNUSABLE;
195				break;
196
197			case PACKET_UNUSEABLE:
198				result = SNTP_RESULT_FAILURE_PACKET_UNUSABLE;
199				break;
200
201			case SERVER_AUTH_FAIL:
202				result = SNTP_RESULT_FAILURE_AUTHORIZATION;
203				break;
204
205			case KOD_DEMOBILIZE:
206				result = SNTP_RESULT_FAILURE_SERVER_KISSOFDEATH;
207				break;
208
209			case KOD_RATE:
210				result = SNTP_RESULT_FAILURE_SERVER_RATE_LIMIT;
211				break;
212
213			case GETTIMEOFDAY_FAILED:
214				// this really shouldn't happen, no need for it's own code
215				result = SNTP_RESULT_FAILURE_INTERNAL;
216				break;
217
218			case 1:
219
220				/* Convert timestamps from network to host byte order */
221				p_rdly = NTOHS_FP(r_pkt.rootdelay);
222				p_rdsp = NTOHS_FP(r_pkt.rootdisp);
223				NTOHL_FP(&r_pkt.reftime, &p_ref);
224				NTOHL_FP(&r_pkt.org, &p_org);
225				NTOHL_FP(&r_pkt.rec, &p_rec);
226				NTOHL_FP(&r_pkt.xmt, &p_xmt);
227
228				if (ENABLED_OPT(NORMALVERBOSE)) {
229					getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf,
230								sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
231
232					printf("sntp on_wire: Received %i bytes from %s\n", rpktl, addr_buf);
233				}
234
235				precision = LOGTOD(r_pkt.precision);
236#ifdef DEBUG
237				fprintf(stderr, "sntp precision: %f\n", precision);
238#endif /* DEBUG */
239				for (digits = 0; (precision *= 10.) < 1.; ++digits)
240				/* empty */ ;
241				if (digits > 6)
242					digits = 6;
243
244				root_dispersion = FPTOD(p_rdsp);
245
246#ifdef DEBUG
247				fprintf(stderr, "sntp rootdelay: %f\n", FPTOD(p_rdly));
248				fprintf(stderr, "sntp rootdisp: %f\n", root_dispersion);
249
250				pkt_output(&r_pkt, rpktl, stdout);
251
252				fprintf(stderr, "sntp on_wire: r_pkt.reftime:\n");
253				l_fp_output(&(r_pkt.reftime), stdout);
254				fprintf(stderr, "sntp on_wire: r_pkt.org:\n");
255				l_fp_output(&(r_pkt.org), stdout);
256				fprintf(stderr, "sntp on_wire: r_pkt.rec:\n");
257				l_fp_output(&(r_pkt.rec), stdout);
258				fprintf(stderr, "sntp on_wire: r_pkt.rec:\n");
259				l_fp_output_bin(&(r_pkt.rec), stdout);
260				fprintf(stderr, "sntp on_wire: r_pkt.rec:\n");
261				l_fp_output_dec(&(r_pkt.rec), stdout);
262				fprintf(stderr, "sntp on_wire: r_pkt.xmt:\n");
263				l_fp_output(&(r_pkt.xmt), stdout);
264#endif
265
266				/* Compute offset etc. */
267				tv_dst.tv_sec += JAN_1970;
268
269				tmp = p_rec;
270				L_SUB(&tmp, &p_org);
271
272				LFPTOD(&tmp, t21);
273
274				TVTOTS(&tv_dst, &dst);
275
276				tmp = p_xmt;
277				L_SUB(&tmp, &dst);
278
279				LFPTOD(&tmp, t34);
280
281				offset = (t21 + t34) / 2.;
282				delta = t21 - t34;
283
284				*out_time = tv_dst;
285				out_time->tv_sec -= JAN_1970;
286
287				adjust_tv_by_offset(out_time, offset);
288				*out_delay = delta;
289				*out_dispersion = (root_dispersion > 0 ? root_dispersion : -1);
290
291				return SNTP_RESULT_SUCCESS;
292		}
293	}
294
295#if DEBUG
296	getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
297	fprintf(stderr,  "Received no useable packet from %s!", addr_buf);
298#endif
299
300	return result;
301}
302
303void
304adjust_tv_by_offset(struct timeval *tv, double offset)
305{
306	double frac, whole;
307	frac = modf(offset, &whole);
308
309	tv->tv_sec += (int) offset;
310	tv->tv_usec += frac * USEC_PER_SEC;
311	if (tv->tv_usec < 0) {
312		tv->tv_usec += USEC_PER_SEC;
313		tv->tv_sec--;
314	} else if (tv->tv_usec > USEC_PER_SEC) {
315		tv->tv_usec -= USEC_PER_SEC;
316		tv->tv_sec++;
317	}
318}
319
320/* Compute the 8 bits for li_vn_mode */
321void
322set_li_vn_mode (
323				struct pkt *spkt,
324				char leap,
325				char version,
326				char mode
327				)
328{
329
330	if(leap > 3) {
331		debug_msg("set_li_vn_mode: leap > 3 using max. 3");
332		leap = 3;
333	}
334
335	if(mode > 7) {
336		debug_msg("set_li_vn_mode: mode > 7, using client mode 3");
337		mode = 3;
338	}
339
340	spkt->li_vn_mode  = leap << 6;
341	spkt->li_vn_mode |= version << 3;
342	spkt->li_vn_mode |= mode;
343}
344