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