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