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