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