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