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