xref: /netbsd/usr.sbin/timed/timedc/cmds.c (revision c4a72b64)
1 /*	$NetBSD: cmds.c,v 1.12 2002/09/19 00:01:33 mycroft 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.12 2002/09/19 00:01:33 mycroft 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 		(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 		if (poll(set, 1, tout)) {
320 			length = sizeof(struct sockaddr);
321 			cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
322 				      &from, &length);
323 			if (cc < 0) {
324 				perror("recvfrom");
325 				continue;
326 			}
327 			bytehostorder(&msg);
328 			if (msg.tsp_type == TSP_ACK) {
329 				printf("master timedaemon at %s is %s\n",
330 				       tgtname, msg.tsp_name);
331 			} else {
332 				printf("received wrong ack: %s\n",
333 				       tsptype[msg.tsp_type]);
334 			}
335 		} else {
336 			printf("communication error with %s\n", tgtname);
337 		}
338 	} while (++i < argc);
339 }
340 
341 /*
342  * quits timedc
343  */
344 void
345 quit(int argc, char *argv[])
346 {
347 	exit(0);
348 }
349 
350 
351 /*
352  * Causes the election timer to expire on the selected hosts
353  * It sends just one udp message per machine, relying on
354  * reliability of communication channel.
355  */
356 void
357 testing(int argc, char *argv[])
358 {
359 	struct servent *srvp;
360 	struct sockaddr_in sin;
361 	struct tsp msg;
362 
363 	if (argc < 2)  {
364 		printf("Usage: election host1 [host2 ...]\n");
365 		return;
366 	}
367 
368 	srvp = getservbyname("timed", "udp");
369 	if (srvp == 0) {
370 		fprintf(stderr, "udp/timed: unknown service\n");
371 		return;
372 	}
373 
374 	while (argc > 1) {
375 		argc--; argv++;
376 		hp = gethostbyname(*argv);
377 		if (hp == NULL) {
378 			fprintf(stderr, "timedc: %s: ", *argv);
379 			herror(0);
380 			argc--; argv++;
381 			continue;
382 		}
383 		sin.sin_port = srvp->s_port;
384 		sin.sin_family = hp->h_addrtype;
385 		bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length);
386 
387 		msg.tsp_type = TSP_TEST;
388 		msg.tsp_vers = TSPVERSION;
389 		(void)gethostname(myname, sizeof(myname));
390 		(void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
391 		bytenetorder(&msg);
392 		if (sendto(sock, &msg, sizeof(struct tsp), 0,
393 			   (struct sockaddr*)&sin,
394 			   sizeof(struct sockaddr)) < 0) {
395 			perror("sendto");
396 		}
397 	}
398 }
399 
400 
401 /*
402  * Enables or disables tracing on local timedaemon
403  */
404 void
405 tracing(int argc, char *argv[])
406 {
407 	int onflag;
408 	int length;
409 	int cc;
410 	struct pollfd set[1];
411 	struct sockaddr_in dest;
412 	struct sockaddr from;
413 	int tout;
414 	struct tsp msg;
415 	struct servent *srvp;
416 
417 	if (argc != 2) {
418 		printf("Usage: tracing { on | off }\n");
419 		return;
420 	}
421 
422 	srvp = getservbyname("timed", "udp");
423 	if (srvp == 0) {
424 		fprintf(stderr, "udp/timed: unknown service\n");
425 		return;
426 	}
427 	dest.sin_port = srvp->s_port;
428 	dest.sin_family = AF_INET;
429 
430 	(void)gethostname(myname,sizeof(myname));
431 	hp = gethostbyname(myname);
432 	bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
433 
434 	if (strcmp(argv[1], "on") == 0) {
435 		msg.tsp_type = TSP_TRACEON;
436 		onflag = ON;
437 	} else {
438 		msg.tsp_type = TSP_TRACEOFF;
439 		onflag = OFF;
440 	}
441 
442 	(void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name) - 1);
443 	msg.tsp_vers = TSPVERSION;
444 	bytenetorder(&msg);
445 	if (sendto(sock, &msg, sizeof(struct tsp), 0,
446 		   (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) {
447 		perror("sendto");
448 		return;
449 	}
450 
451 	tout = 5000;
452 	set[0].fd = sock;
453 	set[0].events = POLLIN;
454 	if (poll(set, 1, tout)) {
455 		length = sizeof(struct sockaddr);
456 		cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
457 			      &from, &length);
458 		if (cc < 0) {
459 			perror("recvfrom");
460 			return;
461 		}
462 		bytehostorder(&msg);
463 		if (msg.tsp_type == TSP_ACK)
464 			if (onflag)
465 				printf("timed tracing enabled\n");
466 			else
467 				printf("timed tracing disabled\n");
468 		else
469 			printf("wrong ack received: %s\n",
470 						tsptype[msg.tsp_type]);
471 	} else
472 		printf("communication error\n");
473 }
474 
475 int
476 priv_resources(void)
477 {
478 	int port;
479 	struct sockaddr_in sin;
480 
481 	sock = socket(AF_INET, SOCK_DGRAM, 0);
482 	if (sock < 0) {
483 		perror("opening socket");
484 		return(-1);
485 	}
486 
487 	sin.sin_family = AF_INET;
488 	sin.sin_addr.s_addr = 0;
489 	for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
490 		sin.sin_port = htons((u_short)port);
491 		if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0)
492 			break;
493 		if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
494 			perror("bind");
495 			(void) close(sock);
496 			return(-1);
497 		}
498 	}
499 	if (port == IPPORT_RESERVED / 2) {
500 		fprintf(stderr, "all reserved ports in use\n");
501 		(void) close(sock);
502 		return(-1);
503 	}
504 
505 	sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
506 	if (sock_raw < 0)  {
507 		perror("opening raw socket");
508 		(void) close(sock);
509 		return(-1);
510 	}
511 	return(1);
512 }
513