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