1#include <l_stdlib.h>
2#include <ntp_fp.h>
3#include <ntp.h>
4#include <ntp_stdlib.h>
5#include <ntp_unixtime.h>
6#include <isc/result.h>
7#include <isc/net.h>
8#include <stdio.h>
9
10#include <sntp-opts.h>
11
12#include "crypto.h"
13#include "kod_management.h"
14#include "networking.h"
15#include "utilities.h"
16#include "log.h"
17
18char *progname = "sntp";	/* for msyslog */
19
20int ai_fam_pref = AF_UNSPEC;
21volatile int debug;
22
23struct key *keys = NULL;
24
25void set_li_vn_mode (struct pkt *spkt, char leap, char version, char mode);
26int sntp_main (int argc, char **argv);
27int on_wire (struct addrinfo *host);
28int set_time (double offset);
29
30
31int
32main (
33		int argc,
34		char **argv
35		)
36{
37	return sntp_main(argc, argv);
38}
39
40/*
41 * The actual main function.
42 */
43int
44sntp_main (
45		int argc,
46		char **argv
47		)
48{
49	register int c;
50	struct kod_entry *reason = NULL;
51	int optct;
52	int sync_data_suc = 0;
53	struct addrinfo **resh = NULL;
54	struct addrinfo *ai;
55	int resc;
56	int kodc;
57	int ow_ret = -1;
58	char *hostname;
59
60	optct = optionProcess(&sntpOptions, argc, argv);
61	argc -= optct;
62	argv += optct;
63
64	/* IPv6 available? */
65	if (isc_net_probeipv6() != ISC_R_SUCCESS) {
66		ai_fam_pref = AF_INET;
67#ifdef DEBUG
68		printf("No ipv6 support available, forcing ipv4\n");
69#endif
70	}
71	else {
72		/* Check for options -4 and -6 */
73		if (HAVE_OPT(IPV4))
74			ai_fam_pref = AF_INET;
75		else if (HAVE_OPT(IPV6))
76			ai_fam_pref = AF_INET6;
77	}
78
79	/* Parse config file if declared TODO */
80
81	/* Initialize logging system */
82	if (HAVE_OPT(FILELOG))
83		init_log(OPT_ARG(FILELOG));
84
85	/*
86	 * If there's a specified KOD file init KOD system.  If not use
87	 * default file.  For embedded systems with no writable
88	 * filesystem, -K /dev/null can be used to disable KoD storage.
89	 */
90	if (HAVE_OPT(KOD))
91		kod_init_kod_db(OPT_ARG(KOD));
92	else
93		kod_init_kod_db("/var/db/ntp-kod");
94
95	if (HAVE_OPT(KEYFILE))
96		auth_init(OPT_ARG(KEYFILE), &keys);
97
98#ifdef EXERCISE_KOD_DB
99	add_entry("192.168.169.170", "DENY");
100	add_entry("192.168.169.171", "DENY");
101	add_entry("192.168.169.172", "DENY");
102	add_entry("192.168.169.173", "DENY");
103	add_entry("192.168.169.174", "DENY");
104	delete_entry("192.168.169.174", "DENY");
105	delete_entry("192.168.169.172", "DENY");
106	delete_entry("192.168.169.170", "DENY");
107	if ((kodc = search_entry("192.168.169.173", &reason)) == 0)
108		printf("entry for 192.168.169.173 not found but should have been!\n");
109	else
110		free(reason);
111#endif
112
113	/* Considering employing a variable that prevents functions of doing anything until
114	 * everything is initialized properly
115	 */
116	resc = resolve_hosts(argv, argc, &resh, ai_fam_pref);
117
118	if (resc < 1) {
119		printf("Unable to resolve hostname(s)\n");
120		return -1;
121	}
122
123	/* Select a certain ntp server according to simple criteria? For now
124	 * let's just pay attention to previous KoDs.
125	 */
126	for (c = 0; c < resc && !sync_data_suc; c++) {
127		ai = resh[c];
128		do {
129			hostname = addrinfo_to_str(ai);
130
131			if ((kodc = search_entry(hostname, &reason)) == 0) {
132				if (is_reachable(ai)) {
133					ow_ret = on_wire(ai);
134					if (ow_ret < 0)
135						printf("on_wire failed for server %s!\n", hostname);
136					else
137						sync_data_suc = 1;
138				}
139			} else {
140				printf("%d prior KoD%s for %s, skipping.\n",
141					kodc, (kodc > 1) ? "s" : "", hostname);
142				free(reason);
143			}
144			free(hostname);
145			ai = ai->ai_next;
146		} while (NULL != ai && !sync_data_suc);
147		freeaddrinfo(resh[c]);
148	}
149	free(resh);
150
151	return ow_ret != 0;
152}
153
154/* The heart of (S)NTP, exchange NTP packets and compute values to correct the local clock */
155int
156on_wire (
157		struct addrinfo *host
158					)
159{
160	char logmsg[32 + INET6_ADDRSTRLEN];
161	char addr_buf[INET6_ADDRSTRLEN];
162	register int try;
163	SOCKET sock;
164	struct pkt x_pkt;
165	struct pkt r_pkt;
166	char *ref;
167
168	for(try=0; try<5; try++) {
169		struct timeval tv_xmt, tv_dst;
170		double t21, t34, delta, offset, precision, root_dispersion;
171		int digits, error, rpktl, sw_case;
172		char *hostname = NULL, *ts_str = NULL;
173		char *log_str;
174		u_fp p_rdly, p_rdsp;
175		l_fp p_rec, p_xmt, p_ref, p_org, xmt, tmp, dst;
176
177		memset(&r_pkt, 0, sizeof(r_pkt));
178		memset(&x_pkt, 0, sizeof(x_pkt));
179
180		error = GETTIMEOFDAY(&tv_xmt, (struct timezone *)NULL);
181
182		tv_xmt.tv_sec += JAN_1970;
183
184#ifdef DEBUG
185		printf("sntp on_wire: Current time sec: %i msec: %i\n", (unsigned int) tv_xmt.tv_sec,
186				(unsigned int) tv_xmt.tv_usec);
187#endif
188
189		TVTOTS(&tv_xmt, &xmt);
190		HTONL_FP(&xmt, &(x_pkt.xmt));
191
192		x_pkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
193		x_pkt.ppoll = 8;
194		/* FIXME! Modus broadcast + adr. check -> bdr. pkt */
195		set_li_vn_mode(&x_pkt, LEAP_NOTINSYNC, 4, 3);
196
197		create_socket(&sock, (sockaddr_u *)host->ai_addr);
198
199		if (0 == sendpkt(sock, (sockaddr_u *)host->ai_addr, &x_pkt, LEN_PKT_NOMAC)) {
200			rpktl = recvpkt(sock, &r_pkt, &x_pkt);
201		} else {
202			rpktl = SERVER_UNUSEABLE;
203		}
204
205
206		closesocket(sock);
207
208		if(rpktl > 0)
209			sw_case = 1;
210		else
211			sw_case = rpktl;
212
213		switch(sw_case) {
214			case SERVER_UNUSEABLE:
215				if (ENABLED_OPT(NORMALVERBOSE)) {
216					printf("Server unusable\n");
217				}
218				return -1;
219				break;
220
221			case PACKET_UNUSEABLE:
222				if (ENABLED_OPT(NORMALVERBOSE)) {
223					printf("Packet unusable\n");
224				}
225				break;
226
227			case SERVER_AUTH_FAIL:
228				if (ENABLED_OPT(NORMALVERBOSE)) {
229					printf("Server authorization failure\n");
230				}
231				break;
232
233			case KOD_DEMOBILIZE:
234				/* Received a DENY or RESTR KOD packet */
235				hostname = addrinfo_to_str(host);
236				ref = (char *)&r_pkt.refid;
237				add_entry(hostname, ref);
238
239				if (ENABLED_OPT(NORMALVERBOSE))
240					printf("sntp on_wire: Received KOD packet with code: %c%c%c%c from %s, demobilizing all connections\n",
241					       ref[0], ref[1], ref[2], ref[3],
242					       hostname);
243
244				log_str = emalloc(INET6_ADDRSTRLEN + 72);
245				snprintf(log_str, INET6_ADDRSTRLEN + 72,
246					 "Received a KOD packet with code %c%c%c%c from %s, demobilizing all connections",
247					 ref[0], ref[1], ref[2], ref[3],
248					 hostname);
249				log_msg(log_str, 2);
250				free(log_str);
251				break;
252
253			case KOD_RATE:
254				/* Hmm... probably we should sleep a bit here */
255				break;
256
257			case 1:
258
259			/* Convert timestamps from network to host byte order */
260			p_rdly = NTOHS_FP(r_pkt.rootdelay);
261			p_rdsp = NTOHS_FP(r_pkt.rootdisp);
262			NTOHL_FP(&r_pkt.reftime, &p_ref);
263			NTOHL_FP(&r_pkt.org, &p_org);
264			NTOHL_FP(&r_pkt.rec, &p_rec);
265			NTOHL_FP(&r_pkt.xmt, &p_xmt);
266
267			if (ENABLED_OPT(NORMALVERBOSE)) {
268				getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf,
269						sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
270
271				printf("sntp on_wire: Received %i bytes from %s\n", rpktl, addr_buf);
272			}
273
274			precision = LOGTOD(r_pkt.precision);
275#ifdef DEBUG
276			printf("sntp precision: %f\n", precision);
277#endif /* DEBUG */
278			for (digits = 0; (precision *= 10.) < 1.; ++digits)
279				/* empty */ ;
280			if (digits > 6)
281				digits = 6;
282
283			root_dispersion = FPTOD(p_rdsp);
284
285#ifdef DEBUG
286			printf("sntp rootdelay: %f\n", FPTOD(p_rdly));
287			printf("sntp rootdisp: %f\n", root_dispersion);
288
289			pkt_output(&r_pkt, rpktl, stdout);
290
291			printf("sntp on_wire: r_pkt.reftime:\n");
292			l_fp_output(&(r_pkt.reftime), stdout);
293			printf("sntp on_wire: r_pkt.org:\n");
294			l_fp_output(&(r_pkt.org), stdout);
295			printf("sntp on_wire: r_pkt.rec:\n");
296			l_fp_output(&(r_pkt.rec), stdout);
297			printf("sntp on_wire: r_pkt.rec:\n");
298			l_fp_output_bin(&(r_pkt.rec), stdout);
299			printf("sntp on_wire: r_pkt.rec:\n");
300			l_fp_output_dec(&(r_pkt.rec), stdout);
301			printf("sntp on_wire: r_pkt.xmt:\n");
302			l_fp_output(&(r_pkt.xmt), stdout);
303#endif
304
305			/* Compute offset etc. */
306			GETTIMEOFDAY(&tv_dst, (struct timezone *)NULL);
307
308			tv_dst.tv_sec += JAN_1970;
309
310			tmp = p_rec;
311			L_SUB(&tmp, &p_org);
312
313			LFPTOD(&tmp, t21);
314
315			TVTOTS(&tv_dst, &dst);
316
317			tmp = p_xmt;
318			L_SUB(&tmp, &dst);
319
320			LFPTOD(&tmp, t34);
321
322			offset = (t21 + t34) / 2.;
323			delta = t21 - t34;
324
325			if(ENABLED_OPT(NORMALVERBOSE))
326				printf("sntp on_wire:\tt21: %.6f\t\t t34: %.6f\n\t\tdelta: %.6f\t offset: %.6f\n",
327					t21, t34, delta, offset);
328
329			ts_str = tv_to_str(&tv_dst);
330
331			printf("%s ", ts_str);
332
333			if(offset > 0)
334				printf("+");
335
336			printf("%.*f", digits, offset);
337
338			if (root_dispersion > 0.)
339				printf(" +/- %f secs", root_dispersion);
340
341			printf("\n");
342
343			free(ts_str);
344
345			if(ENABLED_OPT(SETTOD) || ENABLED_OPT(ADJTIME))
346				return set_time(offset);
347
348			return 0;
349		}
350	}
351
352	getnameinfo(host->ai_addr, host->ai_addrlen, addr_buf, sizeof(addr_buf), NULL, 0, NI_NUMERICHOST);
353
354	snprintf(logmsg, sizeof(logmsg), "Received no useable packet from %s!", addr_buf);
355	log_msg(logmsg, 1);
356
357	if (ENABLED_OPT(NORMALVERBOSE))
358		printf("sntp on_wire: Received no useable packet from %s!\n", addr_buf);
359
360	return -1;
361}
362
363/* Compute the 8 bits for li_vn_mode */
364void
365set_li_vn_mode (
366		struct pkt *spkt,
367		char leap,
368		char version,
369		char mode
370	       )
371{
372
373	if(leap > 3) {
374		debug_msg("set_li_vn_mode: leap > 3 using max. 3");
375		leap = 3;
376	}
377
378	if(mode > 7) {
379		debug_msg("set_li_vn_mode: mode > 7, using client mode 3");
380		mode = 3;
381	}
382
383	spkt->li_vn_mode  = leap << 6;
384	spkt->li_vn_mode |= version << 3;
385	spkt->li_vn_mode |= mode;
386}
387
388/* set_time corrects the local clock by offset with either settimeofday() or by default
389 * with adjtime()/adjusttimeofday().
390 */
391int
392set_time (
393		double offset
394	 )
395{
396	const int USEC_PER_SEC = 1000000;
397	struct timeval tp;
398	double frac, whole;
399	frac = modf(offset, &whole);
400	if(ENABLED_OPT(SETTOD)) {
401		GETTIMEOFDAY(&tp, (struct timezone *)NULL);
402
403		tp.tv_sec += (int) offset;
404		tp.tv_usec += frac * USEC_PER_SEC;
405		if (tp.tv_usec < 0) {
406			tp.tv_usec += USEC_PER_SEC;
407			tp.tv_sec--;
408		} else if (tp.tv_usec > USEC_PER_SEC) {
409			tp.tv_usec -= USEC_PER_SEC;
410			tp.tv_sec++;
411		}
412
413		if(SETTIMEOFDAY(&tp, (struct timezone *)NULL) < 0) {
414			printf("set_time: settimeofday(): Time not set: %s\n",
415				strerror(errno));
416			return -1;
417		}
418		else {
419			return 0;
420		}
421	}
422	else {
423		tp.tv_sec = (int) offset;
424		tp.tv_usec = frac * USEC_PER_SEC;
425
426		if(ADJTIMEOFDAY(&tp, NULL) < 0) {
427			printf("set_time: adjtime(): Time not set: %s\n",
428				strerror(errno));
429			return -1;
430		}
431		else {
432			return 0;
433		}
434	}
435}
436