xref: /NextBSD/usr.sbin/timed/timedc/cmds.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
1 /*-
2  * Copyright (c) 1985, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 4. Neither the name of the University nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #if 0
31 #ifndef lint
32 static char sccsid[] = "@(#)cmds.c	8.1 (Berkeley) 6/6/93";
33 #endif /* not lint */
34 #endif
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37 
38 #include "timedc.h"
39 #include <sys/file.h>
40 
41 #include <arpa/inet.h>
42 
43 #include <netinet/in_systm.h>
44 #include <netinet/ip.h>
45 #include <netinet/ip_icmp.h>
46 
47 #include <err.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 
52 #define TSPTYPES
53 #include <protocols/timed.h>
54 
55 #define	SECHR	(60*60)
56 #define	SECDAY	(24*SECHR)
57 
58 # define DATE_PROTO "udp"
59 # define DATE_PORT "time"
60 
61 
62 int sock;
63 int sock_raw;
64 char myname[MAXHOSTNAMELEN];
65 struct hostent *hp;
66 struct sockaddr_in server;
67 struct sockaddr_in dayaddr;
68 extern int measure_delta;
69 
70 void bytenetorder(struct tsp *);
71 void bytehostorder(struct tsp *);
72 
73 
74 #define BU (2208988800UL)	/* seconds before UNIX epoch */
75 
76 
77 /* compute the difference between our date and another machine
78  */
79 static int				/* difference in days from our time */
daydiff(char * hostname)80 daydiff(char *hostname)
81 {
82 	int i;
83 	int trials;
84 	struct timeval tout, now;
85 	fd_set ready;
86 	struct sockaddr from;
87 	int fromlen;
88 	unsigned long sec;
89 
90 
91 	/* wait 2 seconds between 10 tries */
92 	tout.tv_sec = 2;
93 	tout.tv_usec = 0;
94 	for (trials = 0; trials < 10; trials++) {
95 		/* ask for the time */
96 		sec = 0;
97 		if (sendto(sock, &sec, sizeof(sec), 0,
98 			   (struct sockaddr*)&dayaddr, sizeof(dayaddr)) < 0) {
99 			warn("sendto(sock)");
100 			return 0;
101 		}
102 
103 		for (;;) {
104 			FD_ZERO(&ready);
105 			FD_SET(sock, &ready);
106 			i = select(sock+1, &ready, (fd_set *)0,
107 				   (fd_set *)0, &tout);
108 			if (i < 0) {
109 				if (errno == EINTR)
110 					continue;
111 				warn("select(date read)");
112 				return 0;
113 			}
114 			if (0 == i)
115 				break;
116 
117 			fromlen = sizeof(from);
118 			if (recvfrom(sock,&sec,sizeof(sec),0,
119 				     &from,&fromlen) < 0) {
120 				warn("recvfrom(date read)");
121 				return 0;
122 			}
123 
124 			sec = ntohl(sec);
125 			if (sec < BU) {
126 				warnx("%s says it is before 1970: %lu",
127 					hostname, sec);
128 				return 0;
129 			}
130 			sec -= BU;
131 
132 			(void)gettimeofday(&now, NULL);
133 			return (sec - now.tv_sec);
134 		}
135 	}
136 
137 	/* if we get here, we tried too many times */
138 	warnx("%s will not tell us the date", hostname);
139 	return 0;
140 }
141 
142 
143 /*
144  * Clockdiff computes the difference between the time of the machine on
145  * which it is called and the time of the machines given as argument.
146  * The time differences measured by clockdiff are obtained using a sequence
147  * of ICMP TSTAMP messages which are returned to the sender by the IP module
148  * in the remote machine.
149  * In order to compare clocks of machines in different time zones, the time
150  * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
151  * If a hosts uses a different time format, it should set the high order
152  * bit of the 32-bit quantity it transmits.
153  * However, VMS apparently transmits the time in milliseconds since midnight
154  * local time (rather than GMT) without setting the high order bit.
155  * Furthermore, it does not understand daylight-saving time.  This makes
156  * clockdiff behaving inconsistently with hosts running VMS.
157  *
158  * In order to reduce the sensitivity to the variance of message transmission
159  * time, clockdiff sends a sequence of messages.  Yet, measures between
160  * two `distant' hosts can be affected by a small error. The error can,
161  * however, be reduced by increasing the number of messages sent in each
162  * measurement.
163  */
164 void
clockdiff(int argc,char * argv[])165 clockdiff(int argc, char *argv[])
166 {
167 	int measure_status;
168 	extern int measure(u_long, u_long, char *, struct sockaddr_in*, int);
169 	register int avg_cnt;
170 	register long avg;
171 	struct servent *sp;
172 
173 	if (argc < 2)  {
174 		printf("usage: timedc clockdiff host ...\n");
175 		return;
176 	}
177 
178 	if (gethostname(myname, sizeof(myname) - 1) < 0)
179 		err(1, "gethostname");
180 
181 	/* get the address for the date ready */
182 	sp = getservbyname(DATE_PORT, DATE_PROTO);
183 	if (!sp) {
184 		warnx("%s/%s: unknown service", DATE_PORT, DATE_PROTO);
185 		dayaddr.sin_port = 0;
186 	} else {
187 		dayaddr.sin_port = sp->s_port;
188 	}
189 
190 	while (argc > 1) {
191 		argc--; argv++;
192 		hp = gethostbyname(*argv);
193 		if (hp == NULL) {
194 			warnx("%s: %s", *argv, hstrerror(h_errno));
195 			continue;
196 		}
197 
198 		server.sin_family = hp->h_addrtype;
199 		bcopy(hp->h_addr, &server.sin_addr.s_addr, hp->h_length);
200 		for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) {
201 			measure_status = measure(10000,100, *argv, &server, 1);
202 			if (measure_status != GOOD)
203 				break;
204 			avg += measure_delta;
205 		}
206 		if (measure_status == GOOD)
207 			measure_delta = avg/avg_cnt;
208 
209 		switch (measure_status) {
210 		case HOSTDOWN:
211 			printf("%s is down\n", hp->h_name);
212 			continue;
213 		case NONSTDTIME:
214 			printf("%s transmits a non-standard time format\n",
215 			       hp->h_name);
216 			continue;
217 		case UNREACHABLE:
218 			printf("%s is unreachable\n", hp->h_name);
219 			continue;
220 		}
221 
222 		/*
223 		 * Try to get the date only after using ICMP timestamps to
224 		 * get the time.  This is because the date protocol
225 		 * is optional.
226 		 */
227 		if (dayaddr.sin_port != 0) {
228 			dayaddr.sin_family = hp->h_addrtype;
229 			bcopy(hp->h_addr, &dayaddr.sin_addr.s_addr,
230 			      hp->h_length);
231 			avg = daydiff(*argv);
232 			if (avg > SECDAY) {
233 				printf("time on %s is %ld days ahead %s\n",
234 				       hp->h_name, avg/SECDAY, myname);
235 				continue;
236 			} else if (avg < -SECDAY) {
237 				printf("time on %s is %ld days behind %s\n",
238 				       hp->h_name, -avg/SECDAY, myname);
239 				continue;
240 			}
241 		}
242 
243 		if (measure_delta > 0) {
244 			printf("time on %s is %d ms. ahead of time on %s\n",
245 			       hp->h_name, measure_delta, myname);
246 		} else if (measure_delta == 0) {
247 			printf("%s and %s have the same time\n",
248 			       hp->h_name, myname);
249 		} else {
250 			printf("time on %s is %d ms. behind time on %s\n",
251 			       hp->h_name, -measure_delta, myname);
252 		}
253 	}
254 	return;
255 }
256 
257 
258 /*
259  * finds location of master timedaemon
260  */
261 void
msite(int argc,char * argv[])262 msite(int argc, char *argv[])
263 {
264 	ssize_t cc;
265 	fd_set ready;
266 	struct sockaddr_in dest;
267 	int i, length;
268 	struct sockaddr_in from;
269 	struct timeval tout;
270 	struct tsp msg;
271 	struct servent *srvp;
272 	char *tgtname;
273 
274 	if (argc < 1) {
275 		printf("usage: timedc msite [host ...]\n");
276 		return;
277 	}
278 
279 	srvp = getservbyname("timed", "udp");
280 	if (srvp == 0) {
281 		warnx("timed/udp: unknown service");
282 		return;
283 	}
284 	dest.sin_port = srvp->s_port;
285 	dest.sin_family = AF_INET;
286 
287 	if (gethostname(myname, sizeof(myname) - 1) < 0)
288 		err(1, "gethostname");
289 	i = 1;
290 	do {
291 		tgtname = (i >= argc) ? myname : argv[i];
292 		hp = gethostbyname(tgtname);
293 		if (hp == 0) {
294 			warnx("%s: %s", tgtname, hstrerror(h_errno));
295 			continue;
296 		}
297 		bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
298 
299 		(void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
300 		msg.tsp_type = TSP_MSITE;
301 		msg.tsp_vers = TSPVERSION;
302 		bytenetorder(&msg);
303 		if (sendto(sock, &msg, sizeof(struct tsp), 0,
304 			   (struct sockaddr*)&dest,
305 			   sizeof(struct sockaddr)) < 0) {
306 			warn("sendto");
307 			continue;
308 		}
309 
310 		tout.tv_sec = 15;
311 		tout.tv_usec = 0;
312 		FD_ZERO(&ready);
313 		FD_SET(sock, &ready);
314 		if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0,
315 			   &tout)) {
316 			length = sizeof(from);
317 			cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
318 				      (struct sockaddr *)&from, &length);
319 			if (cc < 0) {
320 				warn("recvfrom");
321 				continue;
322 			}
323 			/*
324 			 * The 4.3BSD protocol spec had a 32-byte tsp_name field, and
325 			 * this is still OS-dependent.  Demand that the packet is at
326 			 * least long enough to hold a 4.3BSD packet.
327 			 */
328 			if (cc < (sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) {
329 				fprintf(stderr,
330 				   "short packet (%zd/%zu bytes) from %s\n",
331 				   cc, sizeof(struct tsp) - MAXHOSTNAMELEN + 32,
332 				   inet_ntoa(from.sin_addr));
333 				continue;
334 			}
335 			bytehostorder(&msg);
336 			if (msg.tsp_type == TSP_ACK) {
337 				printf("master timedaemon at %s is %s\n",
338 				       tgtname, msg.tsp_name);
339 			} else {
340 				if (msg.tsp_type >= TSPTYPENUMBER)
341 					printf("unknown ack received: %u\n",
342 						msg.tsp_type);
343 				else
344 					printf("wrong ack received: %s\n",
345 				       		tsptype[msg.tsp_type]);
346 			}
347 		} else {
348 			printf("communication error with %s\n", tgtname);
349 		}
350 	} while (++i < argc);
351 }
352 
353 /*
354  * quits timedc
355  */
356 void
quit(void)357 quit(void)
358 {
359 	exit(0);
360 }
361 
362 
363 /*
364  * Causes the election timer to expire on the selected hosts
365  * It sends just one udp message per machine, relying on
366  * reliability of communication channel.
367  */
368 void
testing(int argc,char * argv[])369 testing(int argc, char *argv[])
370 {
371 	struct servent *srvp;
372 	struct sockaddr_in sin;
373 	struct tsp msg;
374 
375 	if (argc < 2)  {
376 		printf("usage: timedc election host1 [host2 ...]\n");
377 		return;
378 	}
379 
380 	srvp = getservbyname("timed", "udp");
381 	if (srvp == 0) {
382 		warnx("timed/udp: unknown service");
383 		return;
384 	}
385 
386 	while (argc > 1) {
387 		argc--; argv++;
388 		hp = gethostbyname(*argv);
389 		if (hp == NULL) {
390 			warnx("%s: %s", *argv, hstrerror(h_errno));
391 			argc--; argv++;
392 			continue;
393 		}
394 		sin.sin_port = srvp->s_port;
395 		sin.sin_family = hp->h_addrtype;
396 		bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length);
397 
398 		msg.tsp_type = TSP_TEST;
399 		msg.tsp_vers = TSPVERSION;
400 		if (gethostname(myname, sizeof(myname) - 1) < 0)
401 			err(1, "gethostname");
402 		(void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
403 		bytenetorder(&msg);
404 		if (sendto(sock, &msg, sizeof(struct tsp), 0,
405 			   (struct sockaddr*)&sin,
406 			   sizeof(struct sockaddr)) < 0) {
407 			warn("sendto");
408 		}
409 	}
410 }
411 
412 
413 /*
414  * Enables or disables tracing on local timedaemon
415  */
416 void
tracing(int argc,char * argv[])417 tracing(int argc, char *argv[])
418 {
419 	int onflag;
420 	int length;
421 	ssize_t cc;
422 	fd_set ready;
423 	struct sockaddr_in dest;
424 	struct sockaddr_in from;
425 	struct timeval tout;
426 	struct tsp msg;
427 	struct servent *srvp;
428 
429 	if (argc != 2) {
430 		printf("usage: timedc trace { on | off }\n");
431 		return;
432 	}
433 
434 	srvp = getservbyname("timed", "udp");
435 	if (srvp == 0) {
436 		warnx("timed/udp: unknown service");
437 		return;
438 	}
439 	dest.sin_port = srvp->s_port;
440 	dest.sin_family = AF_INET;
441 
442 	if (gethostname(myname, sizeof(myname) - 1) < 0)
443 		err(1, "gethostname");
444 	hp = gethostbyname(myname);
445 	bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
446 
447 	if (strcmp(argv[1], "on") == 0) {
448 		msg.tsp_type = TSP_TRACEON;
449 		onflag = ON;
450 	} else {
451 		msg.tsp_type = TSP_TRACEOFF;
452 		onflag = OFF;
453 	}
454 
455 	(void)strcpy(msg.tsp_name, myname);
456 	msg.tsp_vers = TSPVERSION;
457 	bytenetorder(&msg);
458 	if (sendto(sock, &msg, sizeof(struct tsp), 0,
459 		   (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) {
460 		warn("sendto");
461 		return;
462 	}
463 
464 	tout.tv_sec = 5;
465 	tout.tv_usec = 0;
466 	FD_ZERO(&ready);
467 	FD_SET(sock, &ready);
468 	if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
469 		length = sizeof(from);
470 		cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
471 			      (struct sockaddr *)&from, &length);
472 		if (cc < 0) {
473 			warn("recvfrom");
474 			return;
475 		}
476 		/*
477 		 * The 4.3BSD protocol spec had a 32-byte tsp_name field, and
478 		 * this is still OS-dependent.  Demand that the packet is at
479 		 * least long enough to hold a 4.3BSD packet.
480 		 */
481 		if (cc < (sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) {
482 			fprintf(stderr, "short packet (%zd/%zu bytes) from %s\n",
483 			    cc, sizeof(struct tsp) - MAXHOSTNAMELEN + 32,
484 			    inet_ntoa(from.sin_addr));
485 			return;
486 		}
487 		bytehostorder(&msg);
488 		if (msg.tsp_type == TSP_ACK)
489 			if (onflag)
490 				printf("timed tracing enabled\n");
491 			else
492 				printf("timed tracing disabled\n");
493 		else {
494 			if (msg.tsp_type >= TSPTYPENUMBER)
495 				printf("unknown ack received: %u\n",
496 					msg.tsp_type);
497 			else
498 				printf("wrong ack received: %s\n",
499 						tsptype[msg.tsp_type]);
500 		}
501 	} else
502 		printf("communication error\n");
503 }
504 
505 int
priv_resources(void)506 priv_resources(void)
507 {
508 	int port;
509 	struct sockaddr_in sin;
510 
511 	sock = socket(AF_INET, SOCK_DGRAM, 0);
512 	if (sock < 0) {
513 		warn("opening socket");
514 		return(-1);
515 	}
516 
517 	sin.sin_family = AF_INET;
518 	sin.sin_addr.s_addr = 0;
519 	for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
520 		sin.sin_port = htons((u_short)port);
521 		if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0)
522 			break;
523 		if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
524 			warn("bind");
525 			(void) close(sock);
526 			return(-1);
527 		}
528 	}
529 	if (port == IPPORT_RESERVED / 2) {
530 		warnx("all reserved ports in use");
531 		(void) close(sock);
532 		return(-1);
533 	}
534 
535 	sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
536 	if (sock_raw < 0)  {
537 		warn("opening raw socket");
538 		(void) close(sock);
539 		return(-1);
540 	}
541 	return(1);
542 }
543