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