xref: /netbsd-src/usr.sbin/timed/timedc/cmds.c (revision 08c81a9c2dc8c7300e893321eb65c0925d60871c)
1 /*	$NetBSD: cmds.c,v 1.11 2002/07/06 22:01:40 wiz 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.11 2002/07/06 22:01:40 wiz 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(int argc, char *argv[])
171 {
172 	int measure_status;
173 	extern int measure(u_long, u_long, char *, struct sockaddr_in*, int);
174 	register int avg_cnt;
175 	register long avg;
176 	struct servent *sp;
177 
178 	if (argc < 2)  {
179 		printf("Usage: clockdiff host ... \n");
180 		return;
181 	}
182 
183 	(void)gethostname(myname,sizeof(myname));
184 	myname[sizeof(myname) - 1] = '\0';
185 
186 	/* get the address for the date ready */
187 	sp = getservbyname(DATE_PORT, DATE_PROTO);
188 	if (!sp) {
189 		(void)fprintf(stderr, "%s/%s is an unknown service\n",
190 			      DATE_PORT, DATE_PROTO);
191 		dayaddr.sin_port = 0;
192 	} else {
193 		dayaddr.sin_port = sp->s_port;
194 	}
195 
196 	while (argc > 1) {
197 		argc--; argv++;
198 		hp = gethostbyname(*argv);
199 		if (hp == NULL) {
200 			fprintf(stderr, "timedc: %s: ", *argv);
201 			herror(0);
202 			continue;
203 		}
204 
205 		server.sin_family = hp->h_addrtype;
206 		bcopy(hp->h_addr, &server.sin_addr.s_addr, hp->h_length);
207 		for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) {
208 			measure_status = measure(10000,100, *argv, &server, 1);
209 			if (measure_status != GOOD)
210 				break;
211 			avg += measure_delta;
212 		}
213 		if (measure_status == GOOD)
214 			measure_delta = avg/avg_cnt;
215 
216 		switch (measure_status) {
217 		case HOSTDOWN:
218 			printf("%s is down\n", hp->h_name);
219 			continue;
220 		case NONSTDTIME:
221 			printf("%s transmitts a non-standard time format\n",
222 			       hp->h_name);
223 			continue;
224 		case UNREACHABLE:
225 			printf("%s is unreachable\n", hp->h_name);
226 			continue;
227 		}
228 
229 		/*
230 		 * Try to get the date only after using ICMP timestamps to
231 		 * get the time.  This is because the date protocol
232 		 * is optional.
233 		 */
234 		if (dayaddr.sin_port != 0) {
235 			dayaddr.sin_family = hp->h_addrtype;
236 			bcopy(hp->h_addr, &dayaddr.sin_addr.s_addr,
237 			      hp->h_length);
238 			avg = daydiff(*argv);
239 			if (avg > SECDAY) {
240 				printf("time on %s is %ld days ahead %s\n",
241 				       hp->h_name, avg/SECDAY, myname);
242 				continue;
243 			} else if (avg < -SECDAY) {
244 				printf("time on %s is %ld days behind %s\n",
245 				       hp->h_name, -avg/SECDAY, myname);
246 				continue;
247 			}
248 		}
249 
250 		if (measure_delta > 0) {
251 			printf("time on %s is %d ms. ahead of time on %s\n",
252 			       hp->h_name, measure_delta, myname);
253 		} else if (measure_delta == 0) {
254 			printf("%s and %s have the same time\n",
255 			       hp->h_name, myname);
256 		} else {
257 			printf("time on %s is %d ms. behind time on %s\n",
258 			       hp->h_name, -measure_delta, myname);
259 		}
260 	}
261 	return;
262 }
263 
264 
265 /*
266  * finds location of master timedaemon
267  */
268 void
269 msite(int argc, char *argv[])
270 {
271 	int cc;
272 	fd_set ready;
273 	struct sockaddr_in dest;
274 	int i, length;
275 	struct sockaddr from;
276 	struct timeval tout;
277 	struct tsp msg;
278 	struct servent *srvp;
279 	char *tgtname;
280 
281 	if (argc < 1) {
282 		printf("Usage: msite [hostname]\n");
283 		return;
284 	}
285 
286 	srvp = getservbyname("timed", "udp");
287 	if (srvp == 0) {
288 		fprintf(stderr, "udp/timed: unknown service\n");
289 		return;
290 	}
291 	dest.sin_port = srvp->s_port;
292 	dest.sin_family = AF_INET;
293 
294 	(void)gethostname(myname, sizeof(myname));
295 	i = 1;
296 	do {
297 		tgtname = (i >= argc) ? myname : argv[i];
298 		hp = gethostbyname(tgtname);
299 		if (hp == 0) {
300 			fprintf(stderr, "timedc: %s: ", tgtname);
301 			herror(0);
302 			continue;
303 		}
304 		bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
305 
306 		(void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name) - 1);
307 		msg.tsp_type = TSP_MSITE;
308 		msg.tsp_vers = TSPVERSION;
309 		bytenetorder(&msg);
310 		if (sendto(sock, &msg, sizeof(struct tsp), 0,
311 			   (struct sockaddr*)&dest,
312 			   sizeof(struct sockaddr)) < 0) {
313 			perror("sendto");
314 			continue;
315 		}
316 
317 		tout.tv_sec = 15;
318 		tout.tv_usec = 0;
319 		FD_ZERO(&ready);
320 		FD_SET(sock, &ready);
321 		if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0,
322 			   &tout)) {
323 			length = sizeof(struct sockaddr);
324 			cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
325 				      &from, &length);
326 			if (cc < 0) {
327 				perror("recvfrom");
328 				continue;
329 			}
330 			bytehostorder(&msg);
331 			if (msg.tsp_type == TSP_ACK) {
332 				printf("master timedaemon at %s is %s\n",
333 				       tgtname, msg.tsp_name);
334 			} else {
335 				printf("received wrong ack: %s\n",
336 				       tsptype[msg.tsp_type]);
337 			}
338 		} else {
339 			printf("communication error with %s\n", tgtname);
340 		}
341 	} while (++i < argc);
342 }
343 
344 /*
345  * quits timedc
346  */
347 void
348 quit(int argc, char *argv[])
349 {
350 	exit(0);
351 }
352 
353 
354 /*
355  * Causes the election timer to expire on the selected hosts
356  * It sends just one udp message per machine, relying on
357  * reliability of communication channel.
358  */
359 void
360 testing(int argc, char *argv[])
361 {
362 	struct servent *srvp;
363 	struct sockaddr_in sin;
364 	struct tsp msg;
365 
366 	if (argc < 2)  {
367 		printf("Usage: election host1 [host2 ...]\n");
368 		return;
369 	}
370 
371 	srvp = getservbyname("timed", "udp");
372 	if (srvp == 0) {
373 		fprintf(stderr, "udp/timed: unknown service\n");
374 		return;
375 	}
376 
377 	while (argc > 1) {
378 		argc--; argv++;
379 		hp = gethostbyname(*argv);
380 		if (hp == NULL) {
381 			fprintf(stderr, "timedc: %s: ", *argv);
382 			herror(0);
383 			argc--; argv++;
384 			continue;
385 		}
386 		sin.sin_port = srvp->s_port;
387 		sin.sin_family = hp->h_addrtype;
388 		bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length);
389 
390 		msg.tsp_type = TSP_TEST;
391 		msg.tsp_vers = TSPVERSION;
392 		(void)gethostname(myname, sizeof(myname));
393 		(void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
394 		bytenetorder(&msg);
395 		if (sendto(sock, &msg, sizeof(struct tsp), 0,
396 			   (struct sockaddr*)&sin,
397 			   sizeof(struct sockaddr)) < 0) {
398 			perror("sendto");
399 		}
400 	}
401 }
402 
403 
404 /*
405  * Enables or disables tracing on local timedaemon
406  */
407 void
408 tracing(int argc, char *argv[])
409 {
410 	int onflag;
411 	int length;
412 	int cc;
413 	fd_set ready;
414 	struct sockaddr_in dest;
415 	struct sockaddr from;
416 	struct timeval tout;
417 	struct tsp msg;
418 	struct servent *srvp;
419 
420 	if (argc != 2) {
421 		printf("Usage: tracing { on | off }\n");
422 		return;
423 	}
424 
425 	srvp = getservbyname("timed", "udp");
426 	if (srvp == 0) {
427 		fprintf(stderr, "udp/timed: unknown service\n");
428 		return;
429 	}
430 	dest.sin_port = srvp->s_port;
431 	dest.sin_family = AF_INET;
432 
433 	(void)gethostname(myname,sizeof(myname));
434 	hp = gethostbyname(myname);
435 	bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
436 
437 	if (strcmp(argv[1], "on") == 0) {
438 		msg.tsp_type = TSP_TRACEON;
439 		onflag = ON;
440 	} else {
441 		msg.tsp_type = TSP_TRACEOFF;
442 		onflag = OFF;
443 	}
444 
445 	(void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name) - 1);
446 	msg.tsp_vers = TSPVERSION;
447 	bytenetorder(&msg);
448 	if (sendto(sock, &msg, sizeof(struct tsp), 0,
449 		   (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) {
450 		perror("sendto");
451 		return;
452 	}
453 
454 	tout.tv_sec = 5;
455 	tout.tv_usec = 0;
456 	FD_ZERO(&ready);
457 	FD_SET(sock, &ready);
458 	if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
459 		length = sizeof(struct sockaddr);
460 		cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
461 			      &from, &length);
462 		if (cc < 0) {
463 			perror("recvfrom");
464 			return;
465 		}
466 		bytehostorder(&msg);
467 		if (msg.tsp_type == TSP_ACK)
468 			if (onflag)
469 				printf("timed tracing enabled\n");
470 			else
471 				printf("timed tracing disabled\n");
472 		else
473 			printf("wrong ack received: %s\n",
474 						tsptype[msg.tsp_type]);
475 	} else
476 		printf("communication error\n");
477 }
478 
479 int
480 priv_resources(void)
481 {
482 	int port;
483 	struct sockaddr_in sin;
484 
485 	sock = socket(AF_INET, SOCK_DGRAM, 0);
486 	if (sock < 0) {
487 		perror("opening socket");
488 		return(-1);
489 	}
490 
491 	sin.sin_family = AF_INET;
492 	sin.sin_addr.s_addr = 0;
493 	for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
494 		sin.sin_port = htons((u_short)port);
495 		if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0)
496 			break;
497 		if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
498 			perror("bind");
499 			(void) close(sock);
500 			return(-1);
501 		}
502 	}
503 	if (port == IPPORT_RESERVED / 2) {
504 		fprintf(stderr, "all reserved ports in use\n");
505 		(void) close(sock);
506 		return(-1);
507 	}
508 
509 	sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
510 	if (sock_raw < 0)  {
511 		perror("opening raw socket");
512 		(void) close(sock);
513 		return(-1);
514 	}
515 	return(1);
516 }
517