1 /*	$OpenBSD: cmds.c,v 1.19 2004/02/04 21:54:05 jmc 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 "timedc.h"
33 #include <sys/file.h>
34 
35 #include <netinet/in_systm.h>
36 #include <netinet/ip.h>
37 #include <netinet/ip_icmp.h>
38 
39 #include <poll.h>
40 #include <signal.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <unistd.h>
44 
45 __SCCSID("@(#)cmds.c	5.1 (Berkeley) 5/11/93");
46 __RCSID("$MirOS: src/usr.sbin/timed/timedc/cmds.c,v 1.4 2007/08/08 19:09:53 tg Exp $");
47 
48 #define TSPTYPES
49 #include <protocols/timed.h>
50 
51 #ifdef sgi
52 #include <bstring.h>
53 #include <sys/clock.h>
54 #else
55 #define	SECHR	(60*60)
56 #define	SECDAY	(24*SECHR)
57 #endif /* sgi */
58 
59 # define DATE_PROTO "udp"
60 # define DATE_PORT "time"
61 
62 
63 int sock;
64 int sock_raw;
65 char myname[MAXHOSTNAMELEN];
66 struct hostent *hp;
67 struct sockaddr_in server;
68 struct sockaddr_in dayaddr;
69 extern int measure_delta;
70 
71 void bytenetorder(struct tsp *);
72 void bytehostorder(struct tsp *);
73 
74 
75 #define BU ((unsigned long)2208988800U)	/* seconds before UNIX epoch */
76 
77 
78 /* compute the difference between our date and another machine
79  */
80 static int				/* difference in days from our time */
daydiff(char * hostname)81 daydiff(char *hostname)
82 {
83 	struct sockaddr_in from;
84 	struct timeval now;
85 	struct pollfd pfd;
86 	unsigned long sec;
87 	int i, trials;
88 	socklen_t fromlen;
89 
90 	for (trials = 0; trials < 10; trials++) {
91 		/* ask for the time */
92 		sec = 0;
93 
94 		siginterrupt(SIGINT, 1);
95 		if (sendto(sock, &sec, sizeof(sec), 0,
96 		    (struct sockaddr *)&dayaddr, sizeof(dayaddr)) < 0) {
97 			if (errno == EINTR && gotintr)
98 				goto bail;
99 			siginterrupt(SIGINT, 0);
100 			perror("sendto(sock)");
101 			goto bail;
102 		}
103 		siginterrupt(SIGINT, 0);
104 
105 		for (;;) {
106 			pfd.fd = sock;
107 			pfd.events = POLLIN;
108 			i = poll(&pfd, 1, 2 * 1000);
109 			if (i < 0) {
110 				if (errno == EINTR) {
111 					if (gotintr)
112 						goto bail;
113 					continue;
114 				}
115 				perror("poll(date read)");
116 				goto bail;
117 			}
118 			if (i == 0)
119 				break;
120 
121 			fromlen = sizeof(from);
122 			siginterrupt(SIGINT, 1);
123 			if (recvfrom(sock, &sec, sizeof(sec), 0,
124 			    (struct sockaddr *)&from, &fromlen) < 0) {
125 				if (errno == EINTR && gotintr)
126 					goto bail;
127 				siginterrupt(SIGINT, 0);
128 				perror("recvfrom(date read)");
129 				goto bail;
130 			}
131 			siginterrupt(SIGINT, 0);
132 
133 			sec = ntohl(sec);
134 			if (sec < BU) {
135 				fprintf(stderr, "%s says it is before 1970: %lu",
136 				    hostname, sec);
137 				goto bail;
138 			}
139 			sec -= BU;
140 
141 			(void)gettimeofday(&now, (struct timezone *)NULL);
142 			return (sec - now.tv_sec);
143 		}
144 	}
145 
146 	/* if we get here, we tried too many times */
147 	fprintf(stderr,"%s will not tell us the date\n", hostname);
148 
149 bail:
150 	siginterrupt(SIGINT, 0);
151 	return (0);
152 }
153 
154 
155 /*
156  * Clockdiff computes the difference between the time of the machine on
157  * which it is called and the time of the machines given as argument.
158  * The time differences measured by clockdiff are obtained using a sequence
159  * of ICMP TSTAMP messages which are returned to the sender by the IP module
160  * in the remote machine.
161  * In order to compare clocks of machines in different time zones, the time
162  * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
163  * If a hosts uses a different time format, it should set the high order
164  * bit of the 32-bit quantity it transmits.
165  * However, VMS apparently transmits the time in milliseconds since midnight
166  * local time (rather than GMT) without setting the high order bit.
167  * Furthermore, it does not understand daylight-saving time.  This makes
168  * clockdiff behaving inconsistently with hosts running VMS.
169  *
170  * In order to reduce the sensitivity to the variance of message transmission
171  * time, clockdiff sends a sequence of messages.  Yet, measures between
172  * two `distant' hosts can be affected by a small error. The error can,
173  * however, be reduced by increasing the number of messages sent in each
174  * measurement.
175  */
176 void
clockdiff(int argc,char * argv[])177 clockdiff(int argc, char *argv[])
178 {
179 	struct servent *sp;
180 	long avg;
181 	int avg_cnt;
182 	extern int measure(u_long, u_long, char *, struct sockaddr_in *, int);
183 	int measure_status;
184 
185 	if (argc < 2)  {
186 		printf("Usage: clockdiff host ... \n");
187 		return;
188 	}
189 
190 	(void)gethostname(myname,sizeof(myname));
191 
192 	/* get the address for the date ready */
193 	sp = getservbyname(DATE_PORT, DATE_PROTO);
194 	if (!sp) {
195 		(void)fprintf(stderr, "%s/%s is an unknown service\n",
196 		    DATE_PORT, DATE_PROTO);
197 		dayaddr.sin_port = 0;
198 	} else
199 		dayaddr.sin_port = sp->s_port;
200 
201 	measure_status = 0;
202 	while (argc > 1) {
203 		argc--;
204 		argv++;
205 
206 		siginterrupt(SIGINT, 1);
207 		hp = gethostbyname(*argv);
208 		if (hp == NULL) {
209 			if (errno == EINTR && gotintr) {
210 				siginterrupt(SIGINT, 0);
211 				return;
212 			}
213 			siginterrupt(SIGINT, 0);
214 			fprintf(stderr, "timedc: %s: ", *argv);
215 			herror(0);
216 			continue;
217 		}
218 		siginterrupt(SIGINT, 0);
219 
220 		server.sin_family = hp->h_addrtype;
221 		memmove(&server.sin_addr.s_addr, hp->h_addr, hp->h_length);
222 		for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) {
223 			measure_status = measure(10000, 100, *argv, &server, 1);
224 			if (measure_status != GOOD)
225 				break;
226 			avg += measure_delta;
227 		}
228 		if (measure_status == GOOD)
229 			measure_delta = avg/avg_cnt;
230 
231 		switch (measure_status) {
232 		case HOSTDOWN:
233 			printf("%s is down\n", hp->h_name);
234 			continue;
235 		case NONSTDTIME:
236 			printf("%s transmits a non-standard time format\n",
237 			    hp->h_name);
238 			continue;
239 		case UNREACHABLE:
240 			printf("%s is unreachable\n", hp->h_name);
241 			continue;
242 		}
243 
244 		/*
245 		 * Try to get the date only after using ICMP timestamps to
246 		 * get the time.  This is because the date protocol
247 		 * is optional.
248 		 */
249 		if (dayaddr.sin_port != 0) {
250 			dayaddr.sin_family = hp->h_addrtype;
251 			memmove(&dayaddr.sin_addr.s_addr, hp->h_addr,
252 			    hp->h_length);
253 			avg = daydiff(*argv);
254 			if (avg > SECDAY) {
255 				printf("time on %s is %ld days ahead %s\n",
256 				    hp->h_name, avg/SECDAY, myname);
257 				continue;
258 			} else if (avg < -SECDAY) {
259 				printf("time on %s is %ld days behind %s\n",
260 				    hp->h_name, -avg/SECDAY, myname);
261 				continue;
262 			}
263 		}
264 
265 		if (measure_delta > 0) {
266 			printf("time on %s is %d ms. ahead of time on %s\n",
267 			    hp->h_name, measure_delta, myname);
268 		} else if (measure_delta == 0) {
269 			printf("%s and %s have the same time\n",
270 			    hp->h_name, myname);
271 		} else {
272 			printf("time on %s is %d ms. behind time on %s\n",
273 			    hp->h_name, -measure_delta, myname);
274 		}
275 	}
276 }
277 
278 
279 /*
280  * finds location of master timedaemon
281  */
282 void
msite(int argc,char * argv[])283 msite(int argc, char *argv[])
284 {
285 	struct sockaddr_in dest, from;
286 	struct servent *srvp;
287 	int i, cc;
288 	socklen_t length;
289 	struct pollfd pfd;
290 	struct tsp msg;
291 	char *tgtname;
292 
293 	if (argc < 1) {
294 		printf("Usage: msite [hostname]\n");
295 		return;
296 	}
297 
298 	srvp = getservbyname("timed", "udp");
299 	if (srvp == 0) {
300 		fprintf(stderr, "udp/timed: unknown service\n");
301 		return;
302 	}
303 	memset(&dest, 0, sizeof dest);
304 	dest.sin_port = srvp->s_port;
305 	dest.sin_family = AF_INET;
306 
307 	(void)gethostname(myname, sizeof(myname));
308 	i = 1;
309 
310 	do {
311 		tgtname = (i >= argc) ? myname : argv[i];
312 		siginterrupt(SIGINT, 1);
313 		hp = gethostbyname(tgtname);
314 		if (hp == 0) {
315 			if (errno == EINTR && gotintr)
316 				goto bail;
317 			siginterrupt(SIGINT, 0);
318 			fprintf(stderr, "timedc: %s: ", tgtname);
319 			herror(0);
320 			continue;
321 		}
322 
323 		memmove(&dest.sin_addr.s_addr, hp->h_addr, hp->h_length);
324 		(void)strlcpy(msg.tsp_name, myname, sizeof msg.tsp_name);
325 		msg.tsp_type = TSP_MSITE;
326 		msg.tsp_vers = TSPVERSION;
327 		bytenetorder(&msg);
328 
329 		if (sendto(sock, &msg, sizeof(struct tsp), 0,
330 		    (struct sockaddr *)&dest, sizeof(dest)) < 0) {
331 			if (errno == EINTR && gotintr)
332 				goto bail;
333 			siginterrupt(SIGINT, 0);
334 			perror("sendto");
335 			continue;
336 		}
337 
338 		pfd.fd = sock;
339 		pfd.events = POLLIN;
340 		switch (poll(&pfd, 1, 15 * 1000)) {
341 		case -1:
342 			if (errno == EINTR && gotintr)
343 				goto bail;
344 			siginterrupt(SIGINT, 0);
345 			continue;
346 		case 1:
347 			length = sizeof(from);
348 			cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
349 			    (struct sockaddr *)&from, &length);
350 			if (cc < 0) {
351 				if (errno == EINTR && gotintr)
352 					goto bail;
353 				siginterrupt(SIGINT, 0);
354 				perror("recvfrom");
355 				continue;
356 			}
357 			siginterrupt(SIGINT, 0);
358 
359 			if (cc < sizeof(struct tsp)) {
360 				fprintf(stderr,
361 				    "short packet (%u/%zu bytes) from %s\n",
362 				    cc, sizeof(struct tsp),
363 				    inet_ntoa(from.sin_addr));
364 				continue;
365 			}
366 			bytehostorder(&msg);
367 			if (msg.tsp_type == TSP_ACK) {
368 				printf("master timedaemon at %s is %s\n",
369 				    tgtname, msg.tsp_name);
370 			} else {
371 				if (msg.tsp_type >= TSPTYPENUMBER)
372 					printf("received unknown ack: %u\n",
373 					    msg.tsp_type);
374 				else
375 					printf("received wrong ack: %s\n",
376 					    tsptype[msg.tsp_type]);
377 			}
378 			break;
379 		case 0:
380 			siginterrupt(SIGINT, 0);
381 			printf("communication error with %s\n", tgtname);
382 			break;
383 		}
384 	} while (++i < argc);
385 
386 bail:
387 	siginterrupt(SIGINT, 0);
388 }
389 
390 /*
391  * quits timedc
392  */
393 void
quit(int ignored,char * unused[])394 quit(int ignored, char *unused[])
395 {
396 	exit(0);
397 }
398 
399 
400 /*
401  * Causes the election timer to expire on the selected hosts
402  * It sends just one udp message per machine, relying on
403  * reliability of communication channel.
404  */
405 void
testing(int argc,char * argv[])406 testing(int argc, char *argv[])
407 {
408 	struct sockaddr_in sin;
409 	struct tsp msg;
410 	struct servent *srvp;
411 
412 	if (argc < 2)  {
413 		printf("Usage: election host1 [host2 ...]\n");
414 		return;
415 	}
416 
417 	srvp = getservbyname("timed", "udp");
418 	if (srvp == 0) {
419 		fprintf(stderr, "udp/timed: unknown service\n");
420 		return;
421 	}
422 
423 	while (argc > 1) {
424 		argc--;
425 		argv++;
426 
427 		siginterrupt(SIGINT, 1);
428 		hp = gethostbyname(*argv);
429 		if (hp == NULL) {
430 			if (errno == EINTR && gotintr)
431 				goto bail;
432 			siginterrupt(SIGINT, 0);
433 			fprintf(stderr, "timedc: %s: ", *argv);
434 			herror(0);
435 			argc--;
436 			argv++;
437 			continue;
438 		}
439 
440 		memset(&sin, 0, sizeof sin);
441 		sin.sin_port = srvp->s_port;
442 		sin.sin_family = hp->h_addrtype;
443 		memmove(&sin.sin_addr.s_addr, hp->h_addr, hp->h_length);
444 
445 		msg.tsp_type = TSP_TEST;
446 		msg.tsp_vers = TSPVERSION;
447 		(void)gethostname(myname, sizeof(myname));
448 		(void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
449 		bytenetorder(&msg);
450 
451 		if (sendto(sock, &msg, sizeof(struct tsp), 0,
452 		    (struct sockaddr *)&sin, sizeof(sin)) < 0) {
453 			if (errno == EINTR && gotintr)
454 				goto bail;
455 			siginterrupt(SIGINT, 0);
456 			perror("sendto");
457 		}
458 	}
459 bail:
460 	siginterrupt(SIGINT, 0);
461 }
462 
463 
464 /*
465  * Enables or disables tracing on local timedaemon
466  */
467 void
tracing(int argc,char * argv[])468 tracing(int argc, char *argv[])
469 {
470 	struct sockaddr_in dest;
471 	struct sockaddr_in from;
472 	struct tsp msg;
473 	struct servent *srvp;
474 	struct pollfd pfd;
475 	int cc, onflag;
476 	socklen_t length;
477 
478 	if (argc != 2) {
479 		printf("Usage: tracing { on | off }\n");
480 		return;
481 	}
482 
483 	srvp = getservbyname("timed", "udp");
484 	if (srvp == 0) {
485 		fprintf(stderr, "udp/timed: unknown service\n");
486 		return;
487 	}
488 
489 	memset(&dest, 0, sizeof dest);
490 	dest.sin_port = srvp->s_port;
491 	dest.sin_family = AF_INET;
492 
493 	(void)gethostname(myname,sizeof(myname));
494 	siginterrupt(SIGINT, 1);
495 	hp = gethostbyname(myname);
496 	if (hp == NULL && errno == EINTR && gotintr)
497 		goto bail;
498 
499 	memmove(&dest.sin_addr.s_addr, hp->h_addr, hp->h_length);
500 
501 	if (strcmp(argv[1], "on") == 0) {
502 		msg.tsp_type = TSP_TRACEON;
503 		onflag = ON;
504 	} else {
505 		msg.tsp_type = TSP_TRACEOFF;
506 		onflag = OFF;
507 	}
508 
509 	(void)strlcpy(msg.tsp_name, myname, sizeof msg.tsp_name);
510 	msg.tsp_vers = TSPVERSION;
511 	bytenetorder(&msg);
512 	if (sendto(sock, &msg, sizeof(struct tsp), 0,
513 	    (struct sockaddr *)&dest, sizeof(dest)) < 0) {
514 		if (errno == EINTR && gotintr)
515 			goto bail;
516 		siginterrupt(SIGINT, 0);
517 		perror("sendto");
518 		return;
519 	}
520 
521 	pfd.fd = sock;
522 	pfd.events = POLLIN;
523 	switch (poll(&pfd, 1, 5 * 1000)) {
524 	case 1:
525 		length = sizeof(from);
526 		cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
527 		    (struct sockaddr *)&from, &length);
528 		if (cc < 0) {
529 			if (errno == EINTR && gotintr)
530 				goto bail;
531 			siginterrupt(SIGINT, 0);
532 			perror("recvfrom");
533 			return;
534 		}
535 		siginterrupt(SIGINT, 0);
536 		if (cc < sizeof(struct tsp)) {
537 			fprintf(stderr, "short packet (%u/%zu bytes) from %s\n",
538 			    cc, sizeof(struct tsp), inet_ntoa(from.sin_addr));
539 			goto bail;
540 		}
541 		bytehostorder(&msg);
542 		if (msg.tsp_type == TSP_ACK) {
543 			if (onflag)
544 				printf("timed tracing enabled\n");
545 			else
546 				printf("timed tracing disabled\n");
547 		} else  {
548 			if (msg.tsp_type >= TSPTYPENUMBER)
549 				printf("unknown ack received: %u\n",
550 				    msg.tsp_type);
551 			else
552 				printf("wrong ack received: %s\n",
553 				    tsptype[msg.tsp_type]);
554 		}
555 		break;
556 	case 0:
557 		siginterrupt(SIGINT, 0);
558 		printf("communication error\n");
559 		break;
560 	}
561 bail:
562 	siginterrupt(SIGINT, 0);
563 }
564