1 /* $NetBSD: cmds.c,v 1.13 2003/05/17 20:55:45 itojun 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.13 2003/05/17 20:55:45 itojun 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 memset(msg.tsp_name, 0, sizeof(msg.tsp_name)); 309 (void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name)); 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 if (poll(set, 1, tout)) { 321 length = sizeof(struct sockaddr); 322 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0, 323 &from, &length); 324 if (cc < 0) { 325 perror("recvfrom"); 326 continue; 327 } 328 bytehostorder(&msg); 329 if (msg.tsp_type == TSP_ACK) { 330 printf("master timedaemon at %s is %s\n", 331 tgtname, msg.tsp_name); 332 } else { 333 printf("received wrong ack: %s\n", 334 tsptype[msg.tsp_type]); 335 } 336 } else { 337 printf("communication error with %s\n", tgtname); 338 } 339 } while (++i < argc); 340 } 341 342 /* 343 * quits timedc 344 */ 345 void 346 quit(int argc, char *argv[]) 347 { 348 exit(0); 349 } 350 351 352 /* 353 * Causes the election timer to expire on the selected hosts 354 * It sends just one udp message per machine, relying on 355 * reliability of communication channel. 356 */ 357 void 358 testing(int argc, char *argv[]) 359 { 360 struct servent *srvp; 361 struct sockaddr_in sin; 362 struct tsp msg; 363 364 if (argc < 2) { 365 printf("Usage: election host1 [host2 ...]\n"); 366 return; 367 } 368 369 srvp = getservbyname("timed", "udp"); 370 if (srvp == 0) { 371 fprintf(stderr, "udp/timed: unknown service\n"); 372 return; 373 } 374 375 while (argc > 1) { 376 argc--; argv++; 377 hp = gethostbyname(*argv); 378 if (hp == NULL) { 379 fprintf(stderr, "timedc: %s: ", *argv); 380 herror(0); 381 argc--; argv++; 382 continue; 383 } 384 sin.sin_port = srvp->s_port; 385 sin.sin_family = hp->h_addrtype; 386 bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length); 387 388 msg.tsp_type = TSP_TEST; 389 msg.tsp_vers = TSPVERSION; 390 (void)gethostname(myname, sizeof(myname)); 391 memset(msg.tsp_name, 0, sizeof(msg.tsp_name)); 392 (void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name)); 393 bytenetorder(&msg); 394 if (sendto(sock, &msg, sizeof(struct tsp), 0, 395 (struct sockaddr*)&sin, 396 sizeof(struct sockaddr)) < 0) { 397 perror("sendto"); 398 } 399 } 400 } 401 402 403 /* 404 * Enables or disables tracing on local timedaemon 405 */ 406 void 407 tracing(int argc, char *argv[]) 408 { 409 int onflag; 410 int length; 411 int cc; 412 struct pollfd set[1]; 413 struct sockaddr_in dest; 414 struct sockaddr from; 415 int tout; 416 struct tsp msg; 417 struct servent *srvp; 418 419 if (argc != 2) { 420 printf("Usage: tracing { on | off }\n"); 421 return; 422 } 423 424 srvp = getservbyname("timed", "udp"); 425 if (srvp == 0) { 426 fprintf(stderr, "udp/timed: unknown service\n"); 427 return; 428 } 429 dest.sin_port = srvp->s_port; 430 dest.sin_family = AF_INET; 431 432 (void)gethostname(myname,sizeof(myname)); 433 hp = gethostbyname(myname); 434 bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length); 435 436 if (strcmp(argv[1], "on") == 0) { 437 msg.tsp_type = TSP_TRACEON; 438 onflag = ON; 439 } else { 440 msg.tsp_type = TSP_TRACEOFF; 441 onflag = OFF; 442 } 443 444 memset(msg.tsp_name, 0, sizeof(msg.tsp_name)); 445 (void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name)); 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 = 5000; 455 set[0].fd = sock; 456 set[0].events = POLLIN; 457 if (poll(set, 1, tout)) { 458 length = sizeof(struct sockaddr); 459 cc = recvfrom(sock, &msg, sizeof(struct tsp), 0, 460 &from, &length); 461 if (cc < 0) { 462 perror("recvfrom"); 463 return; 464 } 465 bytehostorder(&msg); 466 if (msg.tsp_type == TSP_ACK) 467 if (onflag) 468 printf("timed tracing enabled\n"); 469 else 470 printf("timed tracing disabled\n"); 471 else 472 printf("wrong ack received: %s\n", 473 tsptype[msg.tsp_type]); 474 } else 475 printf("communication error\n"); 476 } 477 478 int 479 priv_resources(void) 480 { 481 int port; 482 struct sockaddr_in sin; 483 484 sock = socket(AF_INET, SOCK_DGRAM, 0); 485 if (sock < 0) { 486 perror("opening socket"); 487 return(-1); 488 } 489 490 sin.sin_family = AF_INET; 491 sin.sin_addr.s_addr = 0; 492 for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) { 493 sin.sin_port = htons((u_short)port); 494 if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0) 495 break; 496 if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) { 497 perror("bind"); 498 (void) close(sock); 499 return(-1); 500 } 501 } 502 if (port == IPPORT_RESERVED / 2) { 503 fprintf(stderr, "all reserved ports in use\n"); 504 (void) close(sock); 505 return(-1); 506 } 507 508 sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 509 if (sock_raw < 0) { 510 perror("opening raw socket"); 511 (void) close(sock); 512 return(-1); 513 } 514 return(1); 515 } 516