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