1 /* $NetBSD: cmds.c,v 1.11 2002/07/06 22:01:40 wiz 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.11 2002/07/06 22:01:40 wiz 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(int argc, char *argv[]) 171 { 172 int measure_status; 173 extern int measure(u_long, u_long, char *, struct sockaddr_in*, int); 174 register int avg_cnt; 175 register long avg; 176 struct servent *sp; 177 178 if (argc < 2) { 179 printf("Usage: clockdiff host ... \n"); 180 return; 181 } 182 183 (void)gethostname(myname,sizeof(myname)); 184 myname[sizeof(myname) - 1] = '\0'; 185 186 /* get the address for the date ready */ 187 sp = getservbyname(DATE_PORT, DATE_PROTO); 188 if (!sp) { 189 (void)fprintf(stderr, "%s/%s is an unknown service\n", 190 DATE_PORT, DATE_PROTO); 191 dayaddr.sin_port = 0; 192 } else { 193 dayaddr.sin_port = sp->s_port; 194 } 195 196 while (argc > 1) { 197 argc--; argv++; 198 hp = gethostbyname(*argv); 199 if (hp == NULL) { 200 fprintf(stderr, "timedc: %s: ", *argv); 201 herror(0); 202 continue; 203 } 204 205 server.sin_family = hp->h_addrtype; 206 bcopy(hp->h_addr, &server.sin_addr.s_addr, hp->h_length); 207 for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) { 208 measure_status = measure(10000,100, *argv, &server, 1); 209 if (measure_status != GOOD) 210 break; 211 avg += measure_delta; 212 } 213 if (measure_status == GOOD) 214 measure_delta = avg/avg_cnt; 215 216 switch (measure_status) { 217 case HOSTDOWN: 218 printf("%s is down\n", hp->h_name); 219 continue; 220 case NONSTDTIME: 221 printf("%s transmitts a non-standard time format\n", 222 hp->h_name); 223 continue; 224 case UNREACHABLE: 225 printf("%s is unreachable\n", hp->h_name); 226 continue; 227 } 228 229 /* 230 * Try to get the date only after using ICMP timestamps to 231 * get the time. This is because the date protocol 232 * is optional. 233 */ 234 if (dayaddr.sin_port != 0) { 235 dayaddr.sin_family = hp->h_addrtype; 236 bcopy(hp->h_addr, &dayaddr.sin_addr.s_addr, 237 hp->h_length); 238 avg = daydiff(*argv); 239 if (avg > SECDAY) { 240 printf("time on %s is %ld days ahead %s\n", 241 hp->h_name, avg/SECDAY, myname); 242 continue; 243 } else if (avg < -SECDAY) { 244 printf("time on %s is %ld days behind %s\n", 245 hp->h_name, -avg/SECDAY, myname); 246 continue; 247 } 248 } 249 250 if (measure_delta > 0) { 251 printf("time on %s is %d ms. ahead of time on %s\n", 252 hp->h_name, measure_delta, myname); 253 } else if (measure_delta == 0) { 254 printf("%s and %s have the same time\n", 255 hp->h_name, myname); 256 } else { 257 printf("time on %s is %d ms. behind time on %s\n", 258 hp->h_name, -measure_delta, myname); 259 } 260 } 261 return; 262 } 263 264 265 /* 266 * finds location of master timedaemon 267 */ 268 void 269 msite(int argc, char *argv[]) 270 { 271 int cc; 272 fd_set ready; 273 struct sockaddr_in dest; 274 int i, length; 275 struct sockaddr from; 276 struct timeval tout; 277 struct tsp msg; 278 struct servent *srvp; 279 char *tgtname; 280 281 if (argc < 1) { 282 printf("Usage: msite [hostname]\n"); 283 return; 284 } 285 286 srvp = getservbyname("timed", "udp"); 287 if (srvp == 0) { 288 fprintf(stderr, "udp/timed: unknown service\n"); 289 return; 290 } 291 dest.sin_port = srvp->s_port; 292 dest.sin_family = AF_INET; 293 294 (void)gethostname(myname, sizeof(myname)); 295 i = 1; 296 do { 297 tgtname = (i >= argc) ? myname : argv[i]; 298 hp = gethostbyname(tgtname); 299 if (hp == 0) { 300 fprintf(stderr, "timedc: %s: ", tgtname); 301 herror(0); 302 continue; 303 } 304 bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length); 305 306 (void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name) - 1); 307 msg.tsp_type = TSP_MSITE; 308 msg.tsp_vers = TSPVERSION; 309 bytenetorder(&msg); 310 if (sendto(sock, &msg, sizeof(struct tsp), 0, 311 (struct sockaddr*)&dest, 312 sizeof(struct sockaddr)) < 0) { 313 perror("sendto"); 314 continue; 315 } 316 317 tout.tv_sec = 15; 318 tout.tv_usec = 0; 319 FD_ZERO(&ready); 320 FD_SET(sock, &ready); 321 if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, 322 &tout)) { 323 length = sizeof(struct sockaddr); 324 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0, 325 &from, &length); 326 if (cc < 0) { 327 perror("recvfrom"); 328 continue; 329 } 330 bytehostorder(&msg); 331 if (msg.tsp_type == TSP_ACK) { 332 printf("master timedaemon at %s is %s\n", 333 tgtname, msg.tsp_name); 334 } else { 335 printf("received wrong ack: %s\n", 336 tsptype[msg.tsp_type]); 337 } 338 } else { 339 printf("communication error with %s\n", tgtname); 340 } 341 } while (++i < argc); 342 } 343 344 /* 345 * quits timedc 346 */ 347 void 348 quit(int argc, char *argv[]) 349 { 350 exit(0); 351 } 352 353 354 /* 355 * Causes the election timer to expire on the selected hosts 356 * It sends just one udp message per machine, relying on 357 * reliability of communication channel. 358 */ 359 void 360 testing(int argc, char *argv[]) 361 { 362 struct servent *srvp; 363 struct sockaddr_in sin; 364 struct tsp msg; 365 366 if (argc < 2) { 367 printf("Usage: election host1 [host2 ...]\n"); 368 return; 369 } 370 371 srvp = getservbyname("timed", "udp"); 372 if (srvp == 0) { 373 fprintf(stderr, "udp/timed: unknown service\n"); 374 return; 375 } 376 377 while (argc > 1) { 378 argc--; argv++; 379 hp = gethostbyname(*argv); 380 if (hp == NULL) { 381 fprintf(stderr, "timedc: %s: ", *argv); 382 herror(0); 383 argc--; argv++; 384 continue; 385 } 386 sin.sin_port = srvp->s_port; 387 sin.sin_family = hp->h_addrtype; 388 bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length); 389 390 msg.tsp_type = TSP_TEST; 391 msg.tsp_vers = TSPVERSION; 392 (void)gethostname(myname, sizeof(myname)); 393 (void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name)); 394 bytenetorder(&msg); 395 if (sendto(sock, &msg, sizeof(struct tsp), 0, 396 (struct sockaddr*)&sin, 397 sizeof(struct sockaddr)) < 0) { 398 perror("sendto"); 399 } 400 } 401 } 402 403 404 /* 405 * Enables or disables tracing on local timedaemon 406 */ 407 void 408 tracing(int argc, char *argv[]) 409 { 410 int onflag; 411 int length; 412 int cc; 413 fd_set ready; 414 struct sockaddr_in dest; 415 struct sockaddr from; 416 struct timeval tout; 417 struct tsp msg; 418 struct servent *srvp; 419 420 if (argc != 2) { 421 printf("Usage: tracing { on | off }\n"); 422 return; 423 } 424 425 srvp = getservbyname("timed", "udp"); 426 if (srvp == 0) { 427 fprintf(stderr, "udp/timed: unknown service\n"); 428 return; 429 } 430 dest.sin_port = srvp->s_port; 431 dest.sin_family = AF_INET; 432 433 (void)gethostname(myname,sizeof(myname)); 434 hp = gethostbyname(myname); 435 bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length); 436 437 if (strcmp(argv[1], "on") == 0) { 438 msg.tsp_type = TSP_TRACEON; 439 onflag = ON; 440 } else { 441 msg.tsp_type = TSP_TRACEOFF; 442 onflag = OFF; 443 } 444 445 (void)strncpy(msg.tsp_name, myname, sizeof(msg.tsp_name) - 1); 446 msg.tsp_vers = TSPVERSION; 447 bytenetorder(&msg); 448 if (sendto(sock, &msg, sizeof(struct tsp), 0, 449 (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) { 450 perror("sendto"); 451 return; 452 } 453 454 tout.tv_sec = 5; 455 tout.tv_usec = 0; 456 FD_ZERO(&ready); 457 FD_SET(sock, &ready); 458 if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) { 459 length = sizeof(struct sockaddr); 460 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0, 461 &from, &length); 462 if (cc < 0) { 463 perror("recvfrom"); 464 return; 465 } 466 bytehostorder(&msg); 467 if (msg.tsp_type == TSP_ACK) 468 if (onflag) 469 printf("timed tracing enabled\n"); 470 else 471 printf("timed tracing disabled\n"); 472 else 473 printf("wrong ack received: %s\n", 474 tsptype[msg.tsp_type]); 475 } else 476 printf("communication error\n"); 477 } 478 479 int 480 priv_resources(void) 481 { 482 int port; 483 struct sockaddr_in sin; 484 485 sock = socket(AF_INET, SOCK_DGRAM, 0); 486 if (sock < 0) { 487 perror("opening socket"); 488 return(-1); 489 } 490 491 sin.sin_family = AF_INET; 492 sin.sin_addr.s_addr = 0; 493 for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) { 494 sin.sin_port = htons((u_short)port); 495 if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0) 496 break; 497 if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) { 498 perror("bind"); 499 (void) close(sock); 500 return(-1); 501 } 502 } 503 if (port == IPPORT_RESERVED / 2) { 504 fprintf(stderr, "all reserved ports in use\n"); 505 (void) close(sock); 506 return(-1); 507 } 508 509 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 510 if (sock_raw < 0) { 511 perror("opening raw socket"); 512 (void) close(sock); 513 return(-1); 514 } 515 return(1); 516 } 517