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