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