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