xref: /netbsd-src/usr.sbin/timed/timedc/cmds.c (revision d710132b4b8ce7f7cccaaf660cb16aa16b4077a0)
1 /*	$NetBSD: cmds.c,v 1.13 2003/05/17 20:55:45 itojun 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.13 2003/05/17 20:55:45 itojun 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 	int tout;
89 	struct timeval now;
90 	struct pollfd set[1];
91 	struct sockaddr from;
92 	int fromlen;
93 	unsigned long sec;
94 
95 
96 	/* wait 2 seconds between 10 tries */
97 	tout = 2000;
98 	set[0].fd = sock;
99 	set[0].events = POLLIN;
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 			i = poll(set, 1, tout);
111 			if (i < 0) {
112 				if (errno == EINTR)
113 					continue;
114 				perror("poll(date read)");
115 				return 0;
116 			}
117 			if (0 == i)
118 				break;
119 
120 			fromlen = sizeof(from);
121 			if (recvfrom(sock,&sec,sizeof(sec),0,
122 				     &from,&fromlen) < 0) {
123 				perror("recvfrom(date read)");
124 				return 0;
125 			}
126 
127 			sec = ntohl(sec);
128 			if (sec < BU) {
129 				fprintf(stderr,
130 					"%s says it is before 1970: %lu",
131 					hostname, sec);
132 				return 0;
133 			}
134 			sec -= BU;
135 
136 			(void)gettimeofday(&now, (struct timezone*)0);
137 			return (sec - now.tv_sec);
138 		}
139 	}
140 
141 	/* if we get here, we tried too many times */
142 	fprintf(stderr,"%s will not tell us the date\n", hostname);
143 	return 0;
144 }
145 
146 
147 /*
148  * Clockdiff computes the difference between the time of the machine on
149  * which it is called and the time of the machines given as argument.
150  * The time differences measured by clockdiff are obtained using a sequence
151  * of ICMP TSTAMP messages which are returned to the sender by the IP module
152  * in the remote machine.
153  * In order to compare clocks of machines in different time zones, the time
154  * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
155  * If a hosts uses a different time format, it should set the high order
156  * bit of the 32-bit quantity it transmits.
157  * However, VMS apparently transmits the time in milliseconds since midnight
158  * local time (rather than GMT) without setting the high order bit.
159  * Furthermore, it does not understand daylight-saving time.  This makes
160  * clockdiff behaving inconsistently with hosts running VMS.
161  *
162  * In order to reduce the sensitivity to the variance of message transmission
163  * time, clockdiff sends a sequence of messages.  Yet, measures between
164  * two `distant' hosts can be affected by a small error. The error can,
165  * however, be reduced by increasing the number of messages sent in each
166  * measurement.
167  */
168 void
169 clockdiff(int argc, char *argv[])
170 {
171 	int measure_status;
172 	extern int measure(u_long, u_long, char *, struct sockaddr_in*, int);
173 	register int avg_cnt;
174 	register long avg;
175 	struct servent *sp;
176 
177 	if (argc < 2)  {
178 		printf("Usage: clockdiff host ... \n");
179 		return;
180 	}
181 
182 	(void)gethostname(myname,sizeof(myname));
183 	myname[sizeof(myname) - 1] = '\0';
184 
185 	/* get the address for the date ready */
186 	sp = getservbyname(DATE_PORT, DATE_PROTO);
187 	if (!sp) {
188 		(void)fprintf(stderr, "%s/%s is an unknown service\n",
189 			      DATE_PORT, DATE_PROTO);
190 		dayaddr.sin_port = 0;
191 	} else {
192 		dayaddr.sin_port = sp->s_port;
193 	}
194 
195 	while (argc > 1) {
196 		argc--; argv++;
197 		hp = gethostbyname(*argv);
198 		if (hp == NULL) {
199 			fprintf(stderr, "timedc: %s: ", *argv);
200 			herror(0);
201 			continue;
202 		}
203 
204 		server.sin_family = hp->h_addrtype;
205 		bcopy(hp->h_addr, &server.sin_addr.s_addr, hp->h_length);
206 		for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) {
207 			measure_status = measure(10000,100, *argv, &server, 1);
208 			if (measure_status != GOOD)
209 				break;
210 			avg += measure_delta;
211 		}
212 		if (measure_status == GOOD)
213 			measure_delta = avg/avg_cnt;
214 
215 		switch (measure_status) {
216 		case HOSTDOWN:
217 			printf("%s is down\n", hp->h_name);
218 			continue;
219 		case NONSTDTIME:
220 			printf("%s transmitts a non-standard time format\n",
221 			       hp->h_name);
222 			continue;
223 		case UNREACHABLE:
224 			printf("%s is unreachable\n", hp->h_name);
225 			continue;
226 		}
227 
228 		/*
229 		 * Try to get the date only after using ICMP timestamps to
230 		 * get the time.  This is because the date protocol
231 		 * is optional.
232 		 */
233 		if (dayaddr.sin_port != 0) {
234 			dayaddr.sin_family = hp->h_addrtype;
235 			bcopy(hp->h_addr, &dayaddr.sin_addr.s_addr,
236 			      hp->h_length);
237 			avg = daydiff(*argv);
238 			if (avg > SECDAY) {
239 				printf("time on %s is %ld days ahead %s\n",
240 				       hp->h_name, avg/SECDAY, myname);
241 				continue;
242 			} else if (avg < -SECDAY) {
243 				printf("time on %s is %ld days behind %s\n",
244 				       hp->h_name, -avg/SECDAY, myname);
245 				continue;
246 			}
247 		}
248 
249 		if (measure_delta > 0) {
250 			printf("time on %s is %d ms. ahead of time on %s\n",
251 			       hp->h_name, measure_delta, myname);
252 		} else if (measure_delta == 0) {
253 			printf("%s and %s have the same time\n",
254 			       hp->h_name, myname);
255 		} else {
256 			printf("time on %s is %d ms. behind time on %s\n",
257 			       hp->h_name, -measure_delta, myname);
258 		}
259 	}
260 	return;
261 }
262 
263 
264 /*
265  * finds location of master timedaemon
266  */
267 void
268 msite(int argc, char *argv[])
269 {
270 	int cc;
271 	struct pollfd set[1];
272 	struct sockaddr_in dest;
273 	int i, length;
274 	struct sockaddr from;
275 	int tout;
276 	struct tsp msg;
277 	struct servent *srvp;
278 	char *tgtname;
279 
280 	if (argc < 1) {
281 		printf("Usage: msite [hostname]\n");
282 		return;
283 	}
284 
285 	srvp = getservbyname("timed", "udp");
286 	if (srvp == 0) {
287 		fprintf(stderr, "udp/timed: unknown service\n");
288 		return;
289 	}
290 	dest.sin_port = srvp->s_port;
291 	dest.sin_family = AF_INET;
292 
293 	(void)gethostname(myname, sizeof(myname));
294 	i = 1;
295 	tout = 15000;
296 	set[0].fd = sock;
297 	set[0].events = POLLIN;
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 		memset(msg.tsp_name, 0, sizeof(msg.tsp_name));
309 		(void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
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 		if (poll(set, 1, tout)) {
321 			length = sizeof(struct sockaddr);
322 			cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
323 				      &from, &length);
324 			if (cc < 0) {
325 				perror("recvfrom");
326 				continue;
327 			}
328 			bytehostorder(&msg);
329 			if (msg.tsp_type == TSP_ACK) {
330 				printf("master timedaemon at %s is %s\n",
331 				       tgtname, msg.tsp_name);
332 			} else {
333 				printf("received wrong ack: %s\n",
334 				       tsptype[msg.tsp_type]);
335 			}
336 		} else {
337 			printf("communication error with %s\n", tgtname);
338 		}
339 	} while (++i < argc);
340 }
341 
342 /*
343  * quits timedc
344  */
345 void
346 quit(int argc, char *argv[])
347 {
348 	exit(0);
349 }
350 
351 
352 /*
353  * Causes the election timer to expire on the selected hosts
354  * It sends just one udp message per machine, relying on
355  * reliability of communication channel.
356  */
357 void
358 testing(int argc, char *argv[])
359 {
360 	struct servent *srvp;
361 	struct sockaddr_in sin;
362 	struct tsp msg;
363 
364 	if (argc < 2)  {
365 		printf("Usage: election host1 [host2 ...]\n");
366 		return;
367 	}
368 
369 	srvp = getservbyname("timed", "udp");
370 	if (srvp == 0) {
371 		fprintf(stderr, "udp/timed: unknown service\n");
372 		return;
373 	}
374 
375 	while (argc > 1) {
376 		argc--; argv++;
377 		hp = gethostbyname(*argv);
378 		if (hp == NULL) {
379 			fprintf(stderr, "timedc: %s: ", *argv);
380 			herror(0);
381 			argc--; argv++;
382 			continue;
383 		}
384 		sin.sin_port = srvp->s_port;
385 		sin.sin_family = hp->h_addrtype;
386 		bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length);
387 
388 		msg.tsp_type = TSP_TEST;
389 		msg.tsp_vers = TSPVERSION;
390 		(void)gethostname(myname, sizeof(myname));
391 		memset(msg.tsp_name, 0, sizeof(msg.tsp_name));
392 		(void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
393 		bytenetorder(&msg);
394 		if (sendto(sock, &msg, sizeof(struct tsp), 0,
395 			   (struct sockaddr*)&sin,
396 			   sizeof(struct sockaddr)) < 0) {
397 			perror("sendto");
398 		}
399 	}
400 }
401 
402 
403 /*
404  * Enables or disables tracing on local timedaemon
405  */
406 void
407 tracing(int argc, char *argv[])
408 {
409 	int onflag;
410 	int length;
411 	int cc;
412 	struct pollfd set[1];
413 	struct sockaddr_in dest;
414 	struct sockaddr from;
415 	int tout;
416 	struct tsp msg;
417 	struct servent *srvp;
418 
419 	if (argc != 2) {
420 		printf("Usage: tracing { on | off }\n");
421 		return;
422 	}
423 
424 	srvp = getservbyname("timed", "udp");
425 	if (srvp == 0) {
426 		fprintf(stderr, "udp/timed: unknown service\n");
427 		return;
428 	}
429 	dest.sin_port = srvp->s_port;
430 	dest.sin_family = AF_INET;
431 
432 	(void)gethostname(myname,sizeof(myname));
433 	hp = gethostbyname(myname);
434 	bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
435 
436 	if (strcmp(argv[1], "on") == 0) {
437 		msg.tsp_type = TSP_TRACEON;
438 		onflag = ON;
439 	} else {
440 		msg.tsp_type = TSP_TRACEOFF;
441 		onflag = OFF;
442 	}
443 
444 	memset(msg.tsp_name, 0, sizeof(msg.tsp_name));
445 	(void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
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 = 5000;
455 	set[0].fd = sock;
456 	set[0].events = POLLIN;
457 	if (poll(set, 1, tout)) {
458 		length = sizeof(struct sockaddr);
459 		cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
460 			      &from, &length);
461 		if (cc < 0) {
462 			perror("recvfrom");
463 			return;
464 		}
465 		bytehostorder(&msg);
466 		if (msg.tsp_type == TSP_ACK)
467 			if (onflag)
468 				printf("timed tracing enabled\n");
469 			else
470 				printf("timed tracing disabled\n");
471 		else
472 			printf("wrong ack received: %s\n",
473 						tsptype[msg.tsp_type]);
474 	} else
475 		printf("communication error\n");
476 }
477 
478 int
479 priv_resources(void)
480 {
481 	int port;
482 	struct sockaddr_in sin;
483 
484 	sock = socket(AF_INET, SOCK_DGRAM, 0);
485 	if (sock < 0) {
486 		perror("opening socket");
487 		return(-1);
488 	}
489 
490 	sin.sin_family = AF_INET;
491 	sin.sin_addr.s_addr = 0;
492 	for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
493 		sin.sin_port = htons((u_short)port);
494 		if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0)
495 			break;
496 		if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
497 			perror("bind");
498 			(void) close(sock);
499 			return(-1);
500 		}
501 	}
502 	if (port == IPPORT_RESERVED / 2) {
503 		fprintf(stderr, "all reserved ports in use\n");
504 		(void) close(sock);
505 		return(-1);
506 	}
507 
508 	sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
509 	if (sock_raw < 0)  {
510 		perror("opening raw socket");
511 		(void) close(sock);
512 		return(-1);
513 	}
514 	return(1);
515 }
516