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