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