Home | History | Annotate | Line # | Download | only in timedc
      1 /*	$NetBSD: cmds.c,v 1.23 2007/02/04 21:17:01 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
     35 static char sccsid[] = "@(#)cmds.c	8.2 (Berkeley) 3/26/95";
     36 #else
     37 __RCSID("$NetBSD: cmds.c,v 1.23 2007/02/04 21:17:01 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 
     59 int sock_raw;	/* used by measure() */
     60 static int sock;
     61 extern int measure_delta;
     62 
     63 void bytenetorder(struct tsp *);
     64 void bytehostorder(struct tsp *);
     65 void set_tsp_name(struct tsp *, const char *);
     66 void get_tsp_name(const struct tsp *, char *, size_t);
     67 
     68 
     69 #define BU 2208988800UL	/* seconds before UNIX epoch */
     70 
     71 enum { CACHED, REFRESH };
     72 
     73 static const char *
     74 myname(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 
     87 static in_port_t
     88 udpservport(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 
    100 static const char *
    101 getaddr(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 
    119 static const char *
    120 tsp_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  */
    130 static int				/* difference in days from our time */
    131 daydiff(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  */
    224 void
    225 clockdiff(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  */
    310 void
    311 msite(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  */
    385 void
    386 quit(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  */
    399 void
    400 testing(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  */
    436 void
    437 tracing(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 
    501 int
    502 priv_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