1 /* 2 * ntpq - query an NTP server using mode 6 commands 3 */ 4 5 #include <stdio.h> 6 7 #include "ntpq.h" 8 #include "ntp_unixtime.h" 9 #include "ntp_calendar.h" 10 #include "ntp_io.h" 11 #include "ntp_select.h" 12 #include "ntp_stdlib.h" 13 /* Don't include ISC's version of IPv6 variables and structures */ 14 #define ISC_IPV6_H 1 15 #include "isc/net.h" 16 #include "isc/result.h" 17 18 #include <ctype.h> 19 #include <signal.h> 20 #include <setjmp.h> 21 #include <sys/types.h> 22 #include <sys/time.h> 23 #include <netdb.h> 24 #ifdef SYS_WINNT 25 # include <io.h> 26 #else 27 #define closesocket close 28 #endif /* SYS_WINNT */ 29 30 #if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT) 31 # include <readline/readline.h> 32 # include <readline/history.h> 33 #endif /* HAVE_LIBREADLINE || HAVE_LIBEDIT */ 34 35 #ifdef SYS_VXWORKS 36 /* vxWorks needs mode flag -casey*/ 37 #define open(name, flags) open(name, flags, 0777) 38 #define SERVER_PORT_NUM 123 39 #endif 40 41 /* 42 * Because we potentially understand a lot of commands we will run 43 * interactive if connected to a terminal. 44 */ 45 int interactive = 0; /* set to 1 when we should prompt */ 46 const char *prompt = "ntpq> "; /* prompt to ask him about */ 47 48 49 /* 50 * Keyid used for authenticated requests. Obtained on the fly. 51 */ 52 u_long info_auth_keyid = 0; 53 54 /* 55 * Type of key md5 56 */ 57 #define KEY_TYPE_MD5 4 58 59 static int info_auth_keytype = KEY_TYPE_MD5; /* MD5 */ 60 u_long current_time; /* needed by authkeys; not used */ 61 62 /* 63 * Flag which indicates we should always send authenticated requests 64 */ 65 int always_auth = 0; 66 67 /* 68 * Flag which indicates raw mode output. 69 */ 70 int rawmode = 0; 71 72 /* 73 * Packet version number we use 74 */ 75 u_char pktversion = NTP_OLDVERSION + 1; 76 77 /* 78 * Don't jump if no set jmp. 79 */ 80 volatile int jump = 0; 81 82 /* 83 * Format values 84 */ 85 #define PADDING 0 86 #define TS 1 /* time stamp */ 87 #define FL 2 /* l_fp type value */ 88 #define FU 3 /* u_fp type value */ 89 #define FS 4 /* s_fp type value */ 90 #define UI 5 /* unsigned integer value */ 91 #define SI 6 /* signed integer value */ 92 #define HA 7 /* host address */ 93 #define NA 8 /* network address */ 94 #define ST 9 /* string value */ 95 #define RF 10 /* refid (sometimes string, sometimes not) */ 96 #define LP 11 /* leap (print in binary) */ 97 #define OC 12 /* integer, print in octal */ 98 #define MD 13 /* mode */ 99 #define AR 14 /* array of times */ 100 #define FX 15 /* test flags */ 101 #define EOV 255 /* end of table */ 102 103 104 /* 105 * System variable values. The array can be indexed by 106 * the variable index to find the textual name. 107 */ 108 struct ctl_var sys_var[] = { 109 { 0, PADDING, "" }, /* 0 */ 110 { CS_LEAP, LP, "leap" }, /* 1 */ 111 { CS_STRATUM, UI, "stratum" }, /* 2 */ 112 { CS_PRECISION, SI, "precision" }, /* 3 */ 113 { CS_ROOTDELAY, FS, "rootdelay" }, /* 4 */ 114 { CS_ROOTDISPERSION, FU, "rootdispersion" }, /* 5 */ 115 { CS_REFID, RF, "refid" }, /* 6 */ 116 { CS_REFTIME, TS, "reftime" }, /* 7 */ 117 { CS_POLL, UI, "poll" }, /* 8 */ 118 { CS_PEERID, UI, "peer" }, /* 9 */ 119 { CS_STATE, UI, "state" }, /* 10 */ 120 { CS_OFFSET, FL, "offset" }, /* 11 */ 121 { CS_DRIFT, FS, "frequency" }, /* 12 */ 122 { CS_JITTER, FU, "jitter" }, /* 13 */ 123 { CS_CLOCK, TS, "clock" }, /* 14 */ 124 { CS_PROCESSOR, ST, "processor" }, /* 15 */ 125 { CS_SYSTEM, ST, "system" }, /* 16 */ 126 { CS_VERSION, ST, "version" }, /* 17 */ 127 { CS_STABIL, FS, "stability" }, /* 18 */ 128 { CS_VARLIST, ST, "sys_var_list" }, /* 19 */ 129 { 0, EOV, "" } 130 }; 131 132 133 /* 134 * Peer variable list 135 */ 136 struct ctl_var peer_var[] = { 137 { 0, PADDING, "" }, /* 0 */ 138 { CP_CONFIG, UI, "config" }, /* 1 */ 139 { CP_AUTHENABLE, UI, "authenable" }, /* 2 */ 140 { CP_AUTHENTIC, UI, "authentic" }, /* 3 */ 141 { CP_SRCADR, HA, "srcadr" }, /* 4 */ 142 { CP_SRCPORT, UI, "srcport" }, /* 5 */ 143 { CP_DSTADR, NA, "dstadr" }, /* 6 */ 144 { CP_DSTPORT, UI, "dstport" }, /* 7 */ 145 { CP_LEAP, LP, "leap" }, /* 8 */ 146 { CP_HMODE, MD, "hmode" }, /* 9 */ 147 { CP_STRATUM, UI, "stratum" }, /* 10 */ 148 { CP_PPOLL, UI, "ppoll" }, /* 11 */ 149 { CP_HPOLL, UI, "hpoll" }, /* 12 */ 150 { CP_PRECISION, SI, "precision" }, /* 13 */ 151 { CP_ROOTDELAY, FS, "rootdelay" }, /* 14 */ 152 { CP_ROOTDISPERSION, FU, "rootdispersion" }, /* 15 */ 153 { CP_REFID, RF, "refid" }, /* 16 */ 154 { CP_REFTIME, TS, "reftime" }, /* 17 */ 155 { CP_ORG, TS, "org" }, /* 18 */ 156 { CP_REC, TS, "rec" }, /* 19 */ 157 { CP_XMT, TS, "xmt" }, /* 20 */ 158 { CP_REACH, OC, "reach" }, /* 21 */ 159 { CP_VALID, UI, "valid" }, /* 22 */ 160 { CP_TIMER, UI, "timer" }, /* 23 */ 161 { CP_DELAY, FS, "delay" }, /* 24 */ 162 { CP_OFFSET, FL, "offset" }, /* 25 */ 163 { CP_JITTER, FU, "jitter" }, /* 26 */ 164 { CP_DISPERSION, FU, "dispersion" }, /* 27 */ 165 { CP_KEYID, UI, "keyid" }, /* 28 */ 166 { CP_FILTDELAY, AR, "filtdelay" }, /* 29 */ 167 { CP_FILTOFFSET, AR, "filtoffset" }, /* 30 */ 168 { CP_PMODE, ST, "pmode" }, /* 31 */ 169 { CP_RECEIVED, UI, "received" }, /* 32 */ 170 { CP_SENT, UI, "sent" }, /* 33 */ 171 { CP_FILTERROR, AR, "filtdisp" }, /* 34 */ 172 { CP_FLASH, FX, "flash" }, /* 35 */ 173 { CP_TTL, UI, "ttl" }, /* 36 */ 174 /* 175 * These are duplicate entries so that we can 176 * process deviant version of the ntp protocol. 177 */ 178 { CP_SRCADR, HA, "peeraddr" }, /* 4 */ 179 { CP_SRCPORT, UI, "peerport" }, /* 5 */ 180 { CP_PPOLL, UI, "peerpoll" }, /* 11 */ 181 { CP_HPOLL, UI, "hostpoll" }, /* 12 */ 182 { CP_FILTERROR, AR, "filterror" }, /* 34 */ 183 { 0, EOV, "" } 184 }; 185 186 187 /* 188 * Clock variable list 189 */ 190 struct ctl_var clock_var[] = { 191 { 0, PADDING, "" }, /* 0 */ 192 { CC_TYPE, UI, "type" }, /* 1 */ 193 { CC_TIMECODE, ST, "timecode" }, /* 2 */ 194 { CC_POLL, UI, "poll" }, /* 3 */ 195 { CC_NOREPLY, UI, "noreply" }, /* 4 */ 196 { CC_BADFORMAT, UI, "badformat" }, /* 5 */ 197 { CC_BADDATA, UI, "baddata" }, /* 6 */ 198 { CC_FUDGETIME1, FL, "fudgetime1" }, /* 7 */ 199 { CC_FUDGETIME2, FL, "fudgetime2" }, /* 8 */ 200 { CC_FUDGEVAL1, UI, "stratum" }, /* 9 */ 201 { CC_FUDGEVAL2, RF, "refid" }, /* 10 */ 202 { CC_FLAGS, UI, "flags" }, /* 11 */ 203 { CC_DEVICE, ST, "device" }, /* 12 */ 204 { 0, EOV, "" } 205 }; 206 207 208 /* 209 * flasher bits 210 */ 211 static const char *tstflagnames[] = { 212 "dup_pkt", /* TEST1 */ 213 "bogus_pkt", /* TEST2 */ 214 "proto_unsync", /* TEST3 */ 215 "no_access", /* TEST4 */ 216 "bad_auth", /* TEST5 */ 217 "peer_unsync", /* TEST6 */ 218 "peer_stratum", /* TEST7 */ 219 "root_bounds", /* TEST8 */ 220 "peer_bounds", /* TEST9 */ 221 "bad_autokey", /* TEST10 */ 222 "not_proventic" /* TEST11*/ 223 }; 224 225 226 int ntpqmain P((int, char **)); 227 /* 228 * Built in command handler declarations 229 */ 230 static int openhost P((const char *)); 231 static int sendpkt P((char *, int)); 232 static int getresponse P((int, int, u_short *, int *, char **, int)); 233 static int sendrequest P((int, int, int, int, char *)); 234 static char * tstflags P((u_long)); 235 static void getcmds P((void)); 236 static RETSIGTYPE abortcmd P((int)); 237 static void docmd P((const char *)); 238 static void tokenize P((const char *, char **, int *)); 239 static int findcmd P((char *, struct xcmd *, struct xcmd *, struct xcmd **)); 240 static int getarg P((char *, int, arg_v *)); 241 static int rtdatetolfp P((char *, l_fp *)); 242 static int decodearr P((char *, int *, l_fp *)); 243 static void help P((struct parse *, FILE *)); 244 #ifdef QSORT_USES_VOID_P 245 static int helpsort P((const void *, const void *)); 246 #else 247 static int helpsort P((char **, char **)); 248 #endif 249 static void printusage P((struct xcmd *, FILE *)); 250 static void timeout P((struct parse *, FILE *)); 251 static void auth_delay P((struct parse *, FILE *)); 252 static void host P((struct parse *, FILE *)); 253 static void ntp_poll P((struct parse *, FILE *)); 254 static void keyid P((struct parse *, FILE *)); 255 static void keytype P((struct parse *, FILE *)); 256 static void passwd P((struct parse *, FILE *)); 257 static void hostnames P((struct parse *, FILE *)); 258 static void setdebug P((struct parse *, FILE *)); 259 static void quit P((struct parse *, FILE *)); 260 static void version P((struct parse *, FILE *)); 261 static void raw P((struct parse *, FILE *)); 262 static void cooked P((struct parse *, FILE *)); 263 static void authenticate P((struct parse *, FILE *)); 264 static void ntpversion P((struct parse *, FILE *)); 265 static void warning P((const char *, const char *, const char *)); 266 static void error P((const char *, const char *, const char *)); 267 static u_long getkeyid P((const char *)); 268 static void atoascii P((int, char *, char *)); 269 static void makeascii P((int, char *, FILE *)); 270 static void rawprint P((int, int, char *, int, FILE *)); 271 static void startoutput P((void)); 272 static void output P((FILE *, char *, char *)); 273 static void endoutput P((FILE *)); 274 static void outputarr P((FILE *, char *, int, l_fp *)); 275 static void cookedprint P((int, int, char *, int, FILE *)); 276 #ifdef QSORT_USES_VOID_P 277 static int assoccmp P((const void *, const void *)); 278 #else 279 static int assoccmp P((struct association *, struct association *)); 280 #endif /* sgi || bsdi */ 281 282 283 /* 284 * Built-in commands we understand 285 */ 286 struct xcmd builtins[] = { 287 { "?", help, { OPT|STR, NO, NO, NO }, 288 { "command", "", "", "" }, 289 "tell the use and syntax of commands" }, 290 { "help", help, { OPT|STR, NO, NO, NO }, 291 { "command", "", "", "" }, 292 "tell the use and syntax of commands" }, 293 { "timeout", timeout, { OPT|UINT, NO, NO, NO }, 294 { "msec", "", "", "" }, 295 "set the primary receive time out" }, 296 { "delay", auth_delay, { OPT|INT, NO, NO, NO }, 297 { "msec", "", "", "" }, 298 "set the delay added to encryption time stamps" }, 299 { "host", host, { OPT|STR, OPT|STR, NO, NO }, 300 { "-4|-6", "hostname", "", "" }, 301 "specify the host whose NTP server we talk to" }, 302 { "poll", ntp_poll, { OPT|UINT, OPT|STR, NO, NO }, 303 { "n", "verbose", "", "" }, 304 "poll an NTP server in client mode `n' times" }, 305 { "passwd", passwd, { NO, NO, NO, NO }, 306 { "", "", "", "" }, 307 "specify a password to use for authenticated requests"}, 308 { "hostnames", hostnames, { OPT|STR, NO, NO, NO }, 309 { "yes|no", "", "", "" }, 310 "specify whether hostnames or net numbers are printed"}, 311 { "debug", setdebug, { OPT|STR, NO, NO, NO }, 312 { "no|more|less", "", "", "" }, 313 "set/change debugging level" }, 314 { "quit", quit, { NO, NO, NO, NO }, 315 { "", "", "", "" }, 316 "exit ntpq" }, 317 { "exit", quit, { NO, NO, NO, NO }, 318 { "", "", "", "" }, 319 "exit ntpq" }, 320 { "keyid", keyid, { OPT|UINT, NO, NO, NO }, 321 { "key#", "", "", "" }, 322 "set keyid to use for authenticated requests" }, 323 { "version", version, { NO, NO, NO, NO }, 324 { "", "", "", "" }, 325 "print version number" }, 326 { "raw", raw, { NO, NO, NO, NO }, 327 { "", "", "", "" }, 328 "do raw mode variable output" }, 329 { "cooked", cooked, { NO, NO, NO, NO }, 330 { "", "", "", "" }, 331 "do cooked mode variable output" }, 332 { "authenticate", authenticate, { OPT|STR, NO, NO, NO }, 333 { "yes|no", "", "", "" }, 334 "always authenticate requests to this server" }, 335 { "ntpversion", ntpversion, { OPT|UINT, NO, NO, NO }, 336 { "version number", "", "", "" }, 337 "set the NTP version number to use for requests" }, 338 { "keytype", keytype, { OPT|STR, NO, NO, NO }, 339 { "key type (md5|des)", "", "", "" }, 340 "set key type to use for authenticated requests (des|md5)" }, 341 { 0, 0, { NO, NO, NO, NO }, 342 { "", "", "", "" }, "" } 343 }; 344 345 346 /* 347 * Default values we use. 348 */ 349 #define DEFTIMEOUT (5) /* 5 second time out */ 350 #define DEFSTIMEOUT (2) /* 2 second time out after first */ 351 #define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */ 352 #define DEFHOST "localhost" /* default host name */ 353 #define LENHOSTNAME 256 /* host name is 256 characters long */ 354 #define MAXCMDS 100 /* maximum commands on cmd line */ 355 #define MAXHOSTS 200 /* maximum hosts on cmd line */ 356 #define MAXLINE 512 /* maximum line length */ 357 #define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */ 358 #define MAXVARLEN 256 /* maximum length of a variable name */ 359 #define MAXVALLEN 400 /* maximum length of a variable value */ 360 #define MAXOUTLINE 72 /* maximum length of an output line */ 361 362 /* 363 * Some variables used and manipulated locally 364 */ 365 struct timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */ 366 struct timeval tvsout = { DEFSTIMEOUT, 0 }; /* secondary time out */ 367 l_fp delay_time; /* delay time */ 368 char currenthost[LENHOSTNAME]; /* current host name */ 369 struct sockaddr_in hostaddr = { 0 }; /* host address */ 370 int showhostnames = 1; /* show host names by default */ 371 372 int ai_fam_templ; /* address family */ 373 int ai_fam_default; /* default address family */ 374 SOCKET sockfd; /* fd socket is opened on */ 375 int havehost = 0; /* set to 1 when host open */ 376 int s_port = 0; 377 struct servent *server_entry = NULL; /* server entry for ntp */ 378 379 #ifdef SYS_WINNT 380 DWORD NumberOfBytesWritten; 381 382 HANDLE TimerThreadHandle = NULL; /* 1998/06/03 - Used in ntplib/machines.c */ 383 void timer(void) { ; }; /* 1998/06/03 - Used in ntplib/machines.c */ 384 385 #endif /* SYS_WINNT */ 386 387 /* 388 * Sequence number used for requests. It is incremented before 389 * it is used. 390 */ 391 u_short sequence; 392 393 /* 394 * Holds data returned from queries. Declare buffer long to be sure of 395 * alignment. 396 */ 397 #define MAXFRAGS 24 /* maximum number of fragments */ 398 #define DATASIZE (MAXFRAGS*480) /* maximum amount of data */ 399 long pktdata[DATASIZE/sizeof(long)]; 400 401 /* 402 * Holds association data for use with the &n operator. 403 */ 404 struct association assoc_cache[MAXASSOC]; 405 int numassoc = 0; /* number of cached associations */ 406 407 /* 408 * For commands typed on the command line (with the -c option) 409 */ 410 int numcmds = 0; 411 const char *ccmds[MAXCMDS]; 412 #define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp) 413 414 /* 415 * When multiple hosts are specified. 416 */ 417 int numhosts = 0; 418 const char *chosts[MAXHOSTS]; 419 #define ADDHOST(cp) if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp) 420 421 /* 422 * Error codes for internal use 423 */ 424 #define ERR_UNSPEC 256 425 #define ERR_INCOMPLETE 257 426 #define ERR_TIMEOUT 258 427 #define ERR_TOOMUCH 259 428 429 /* 430 * Macro definitions we use 431 */ 432 #define ISSPACE(c) ((c) == ' ' || (c) == '\t') 433 #define ISEOL(c) ((c) == '\n' || (c) == '\r' || (c) == '\0') 434 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0) 435 436 /* 437 * Jump buffer for longjumping back to the command level 438 */ 439 jmp_buf interrupt_buf; 440 441 /* 442 * Points at file being currently printed into 443 */ 444 FILE *current_output; 445 446 /* 447 * Command table imported from ntpdc_ops.c 448 */ 449 extern struct xcmd opcmds[]; 450 451 char *progname; 452 volatile int debug; 453 454 #ifdef NO_MAIN_ALLOWED 455 CALL(ntpq,"ntpq",ntpqmain); 456 457 void clear_globals(void) 458 { 459 extern int ntp_optind; 460 extern char *ntp_optarg; 461 showhostnames = 0; /* don'tshow host names by default */ 462 ntp_optind = 0; 463 ntp_optarg = 0; 464 server_entry = NULL; /* server entry for ntp */ 465 havehost = 0; /* set to 1 when host open */ 466 numassoc = 0; /* number of cached associations */ 467 numcmds = 0; 468 numhosts = 0; 469 } 470 #endif 471 472 /* 473 * main - parse arguments and handle options 474 */ 475 #ifndef NO_MAIN_ALLOWED 476 int 477 main( 478 int argc, 479 char *argv[] 480 ) 481 { 482 return ntpqmain(argc, argv); 483 } 484 #endif 485 486 int 487 ntpqmain( 488 int argc, 489 char *argv[] 490 ) 491 { 492 int c; 493 int errflg = 0; 494 extern int ntp_optind; 495 extern char *ntp_optarg; 496 497 #ifdef NO_MAIN_ALLOWED 498 clear_globals(); 499 taskPrioritySet(taskIdSelf(), 100 ); 500 #endif 501 delay_time.l_ui = 0; 502 delay_time.l_uf = DEFDELAY; 503 504 #ifdef SYS_WINNT 505 if (!Win32InitSockets()) 506 { 507 fprintf(stderr, "No useable winsock.dll:"); 508 exit(1); 509 } 510 #endif /* SYS_WINNT */ 511 512 /* Check to see if we have IPv6. Otherwise force the -4 flag */ 513 if (isc_net_probeipv6() != ISC_R_SUCCESS) { 514 ai_fam_default = AF_INET; 515 } 516 517 progname = argv[0]; 518 ai_fam_templ = ai_fam_default; 519 while ((c = ntp_getopt(argc, argv, "46c:dinp")) != EOF) 520 switch (c) { 521 case '4': 522 ai_fam_templ = AF_INET; 523 break; 524 case '6': 525 ai_fam_templ = AF_INET6; 526 break; 527 case 'c': 528 ADDCMD(ntp_optarg); 529 break; 530 case 'd': 531 ++debug; 532 break; 533 case 'i': 534 interactive = 1; 535 break; 536 case 'n': 537 showhostnames = 0; 538 break; 539 case 'p': 540 ADDCMD("peers"); 541 break; 542 default: 543 errflg++; 544 break; 545 } 546 if (errflg) { 547 (void) fprintf(stderr, 548 "usage: %s [-46dinp] [-c cmd] host ...\n", 549 progname); 550 exit(2); 551 } 552 if (ntp_optind == argc) { 553 ADDHOST(DEFHOST); 554 } else { 555 for (; ntp_optind < argc; ntp_optind++) 556 ADDHOST(argv[ntp_optind]); 557 } 558 559 if (numcmds == 0 && interactive == 0 560 && isatty(fileno(stdin)) && isatty(fileno(stderr))) { 561 interactive = 1; 562 } 563 564 #ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */ 565 if (interactive) 566 (void) signal_no_reset(SIGINT, abortcmd); 567 #endif /* SYS_WINNT */ 568 569 if (numcmds == 0) { 570 (void) openhost(chosts[0]); 571 getcmds(); 572 } else { 573 int ihost; 574 int icmd; 575 576 for (ihost = 0; ihost < numhosts; ihost++) { 577 if (openhost(chosts[ihost])) 578 for (icmd = 0; icmd < numcmds; icmd++) 579 docmd(ccmds[icmd]); 580 } 581 } 582 #ifdef SYS_WINNT 583 WSACleanup(); 584 #endif /* SYS_WINNT */ 585 return 0; 586 } 587 588 589 /* 590 * openhost - open a socket to a host 591 */ 592 static int 593 openhost( 594 const char *hname 595 ) 596 { 597 char temphost[LENHOSTNAME]; 598 int a_info, i; 599 struct addrinfo hints, *ai = NULL; 600 register const char *cp; 601 char name[LENHOSTNAME]; 602 char service[5]; 603 604 /* 605 * We need to get by the [] if they were entered 606 */ 607 608 cp = hname; 609 610 if(*cp == '[') { 611 cp++; 612 for(i = 0; *cp != ']'; cp++, i++) 613 name[i] = *cp; 614 name[i] = '\0'; 615 hname = name; 616 } 617 618 /* 619 * First try to resolve it as an ip address and if that fails, 620 * do a fullblown (dns) lookup. That way we only use the dns 621 * when it is needed and work around some implementations that 622 * will return an "IPv4-mapped IPv6 address" address if you 623 * give it an IPv4 address to lookup. 624 */ 625 strcpy(service, "ntp"); 626 memset((char *)&hints, 0, sizeof(struct addrinfo)); 627 hints.ai_family = ai_fam_templ; 628 hints.ai_protocol = IPPROTO_UDP; 629 hints.ai_socktype = SOCK_DGRAM; 630 hints.ai_flags = AI_NUMERICHOST; 631 632 a_info = getaddrinfo(hname, service, &hints, &ai); 633 if (a_info == EAI_NONAME || a_info == EAI_NODATA) { 634 hints.ai_flags = AI_CANONNAME; 635 #ifdef AI_ADDRCONFIG 636 hints.ai_flags |= AI_ADDRCONFIG; 637 #endif 638 a_info = getaddrinfo(hname, service, &hints, &ai); 639 } 640 /* Some older implementations don't like AI_ADDRCONFIG. */ 641 if (a_info == EAI_BADFLAGS) { 642 hints.ai_flags = AI_CANONNAME; 643 a_info = getaddrinfo(hname, service, &hints, &ai); 644 } 645 if (a_info != 0) { 646 (void) fprintf(stderr, "%s\n", gai_strerror(a_info)); 647 return 0; 648 } 649 650 if (ai->ai_canonname == NULL) { 651 strncpy(temphost, stoa((struct sockaddr_storage *)ai->ai_addr), 652 LENHOSTNAME); 653 temphost[LENHOSTNAME-1] = '\0'; 654 655 } else { 656 strncpy(temphost, ai->ai_canonname, LENHOSTNAME); 657 temphost[LENHOSTNAME-1] = '\0'; 658 } 659 660 if (debug > 2) 661 printf("Opening host %s\n", temphost); 662 663 if (havehost == 1) { 664 if (debug > 2) 665 printf("Closing old host %s\n", currenthost); 666 (void) closesocket(sockfd); 667 havehost = 0; 668 } 669 (void) strcpy(currenthost, temphost); 670 671 /* port maps to the same location in both families */ 672 s_port = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port; 673 #ifdef SYS_VXWORKS 674 ((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM); 675 if (ai->ai_family == AF_INET) 676 *(struct sockaddr_in *)&hostaddr= 677 *((struct sockaddr_in *)ai->ai_addr); 678 else 679 *(struct sockaddr_in6 *)&hostaddr= 680 *((struct sockaddr_in6 *)ai->ai_addr); 681 #endif /* SYS_VXWORKS */ 682 683 #ifdef SYS_WINNT 684 { 685 int optionValue = SO_SYNCHRONOUS_NONALERT; 686 int err; 687 err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&optionValue, sizeof(optionValue)); 688 if (err != NO_ERROR) { 689 (void) fprintf(stderr, "cannot open nonoverlapped sockets\n"); 690 exit(1); 691 } 692 } 693 #endif /* SYS_WINNT */ 694 695 sockfd = socket(ai->ai_family, SOCK_DGRAM, 0); 696 if (sockfd == INVALID_SOCKET) { 697 error("socket", "", ""); 698 } 699 700 701 #ifdef NEED_RCVBUF_SLOP 702 # ifdef SO_RCVBUF 703 { int rbufsize = DATASIZE + 2048; /* 2K for slop */ 704 if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, 705 &rbufsize, sizeof(int)) == -1) 706 error("setsockopt", "", ""); 707 } 708 # endif 709 #endif 710 711 #ifdef SYS_VXWORKS 712 if (connect(sockfd, (struct sockaddr *)&hostaddr, 713 sizeof(hostaddr)) == -1) 714 #else 715 if (connect(sockfd, (struct sockaddr *)ai->ai_addr, 716 ai->ai_addrlen) == -1) 717 #endif /* SYS_VXWORKS */ 718 error("connect", "", ""); 719 if (a_info == 0) 720 freeaddrinfo(ai); 721 havehost = 1; 722 return 1; 723 } 724 725 726 /* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */ 727 /* 728 * sendpkt - send a packet to the remote host 729 */ 730 static int 731 sendpkt( 732 char *xdata, 733 int xdatalen 734 ) 735 { 736 if (debug >= 3) 737 printf("Sending %d octets\n", xdatalen); 738 739 740 if (send(sockfd, xdata, (size_t)xdatalen, 0) == -1) { 741 warning("write to %s failed", currenthost, ""); 742 return -1; 743 } 744 745 if (debug >= 4) { 746 int first = 8; 747 printf("Packet data:\n"); 748 while (xdatalen-- > 0) { 749 if (first-- == 0) { 750 printf("\n"); 751 first = 7; 752 } 753 printf(" %02x", *xdata++ & 0xff); 754 } 755 printf("\n"); 756 } 757 return 0; 758 } 759 760 761 762 /* 763 * getresponse - get a (series of) response packet(s) and return the data 764 */ 765 static int 766 getresponse( 767 int opcode, 768 int associd, 769 u_short *rstatus, 770 int *rsize, 771 char **rdata, 772 int timeo 773 ) 774 { 775 struct ntp_control rpkt; 776 struct timeval tvo; 777 u_short offsets[MAXFRAGS+1]; 778 u_short counts[MAXFRAGS+1]; 779 u_short offset; 780 u_short count; 781 int numfrags; 782 int seenlastfrag; 783 fd_set fds; 784 int n; 785 786 /* 787 * This is pretty tricky. We may get between 1 and MAXFRAG packets 788 * back in response to the request. We peel the data out of 789 * each packet and collect it in one long block. When the last 790 * packet in the sequence is received we'll know how much data we 791 * should have had. Note we use one long time out, should reconsider. 792 */ 793 *rsize = 0; 794 if (rstatus) 795 *rstatus = 0; 796 *rdata = (char *)pktdata; 797 798 numfrags = 0; 799 seenlastfrag = 0; 800 801 FD_ZERO(&fds); 802 803 again: 804 if (numfrags == 0) 805 tvo = tvout; 806 else 807 tvo = tvsout; 808 809 FD_SET(sockfd, &fds); 810 n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo); 811 812 #if 0 813 if (debug >= 1) 814 printf("select() returns %d\n", n); 815 #endif 816 817 if (n == -1) { 818 warning("select fails", "", ""); 819 return -1; 820 } 821 if (n == 0) { 822 /* 823 * Timed out. Return what we have 824 */ 825 if (numfrags == 0) { 826 if (timeo) 827 (void) fprintf(stderr, 828 "%s: timed out, nothing received\n", 829 currenthost); 830 return ERR_TIMEOUT; 831 } else { 832 if (timeo) 833 (void) fprintf(stderr, 834 "%s: timed out with incomplete data\n", 835 currenthost); 836 if (debug) { 837 printf("Received fragments:\n"); 838 for (n = 0; n < numfrags; n++) 839 printf("%4d %d\n", offsets[n], 840 counts[n]); 841 if (seenlastfrag) 842 printf("last fragment received\n"); 843 else 844 printf("last fragment not received\n"); 845 } 846 return ERR_INCOMPLETE; 847 } 848 } 849 850 n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0); 851 if (n == -1) { 852 warning("read", "", ""); 853 return -1; 854 } 855 856 if (debug >= 4) { 857 int len = n, first = 8; 858 char *data = (char *)&rpkt; 859 860 printf("Packet data:\n"); 861 while (len-- > 0) { 862 if (first-- == 0) { 863 printf("\n"); 864 first = 7; 865 } 866 printf(" %02x", *data++ & 0xff); 867 } 868 printf("\n"); 869 } 870 871 /* 872 * Check for format errors. Bug proofing. 873 */ 874 if (n < CTL_HEADER_LEN) { 875 if (debug) 876 printf("Short (%d byte) packet received\n", n); 877 goto again; 878 } 879 if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION 880 || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) { 881 if (debug) 882 printf("Packet received with version %d\n", 883 PKT_VERSION(rpkt.li_vn_mode)); 884 goto again; 885 } 886 if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) { 887 if (debug) 888 printf("Packet received with mode %d\n", 889 PKT_MODE(rpkt.li_vn_mode)); 890 goto again; 891 } 892 if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) { 893 if (debug) 894 printf("Received request packet, wanted response\n"); 895 goto again; 896 } 897 898 /* 899 * Check opcode and sequence number for a match. 900 * Could be old data getting to us. 901 */ 902 if (ntohs(rpkt.sequence) != sequence) { 903 if (debug) 904 printf( 905 "Received sequnce number %d, wanted %d\n", 906 ntohs(rpkt.sequence), sequence); 907 goto again; 908 } 909 if (CTL_OP(rpkt.r_m_e_op) != opcode) { 910 if (debug) 911 printf( 912 "Received opcode %d, wanted %d (sequence number okay)\n", 913 CTL_OP(rpkt.r_m_e_op), opcode); 914 goto again; 915 } 916 917 /* 918 * Check the error code. If non-zero, return it. 919 */ 920 if (CTL_ISERROR(rpkt.r_m_e_op)) { 921 int errcode; 922 923 errcode = (ntohs(rpkt.status) >> 8) & 0xff; 924 if (debug && CTL_ISMORE(rpkt.r_m_e_op)) { 925 printf("Error code %d received on not-final packet\n", 926 errcode); 927 } 928 if (errcode == CERR_UNSPEC) 929 return ERR_UNSPEC; 930 return errcode; 931 } 932 933 /* 934 * Check the association ID to make sure it matches what 935 * we sent. 936 */ 937 if (ntohs(rpkt.associd) != associd) { 938 if (debug) 939 printf("Association ID %d doesn't match expected %d\n", 940 ntohs(rpkt.associd), associd); 941 /* 942 * Hack for silly fuzzballs which, at the time of writing, 943 * return an assID of sys.peer when queried for system variables. 944 */ 945 #ifdef notdef 946 goto again; 947 #endif 948 } 949 950 /* 951 * Collect offset and count. Make sure they make sense. 952 */ 953 offset = ntohs(rpkt.offset); 954 count = ntohs(rpkt.count); 955 956 if (debug >= 3) { 957 int shouldbesize; 958 u_long key; 959 u_long *lpkt; 960 int maclen; 961 962 /* 963 * Usually we ignore authentication, but for debugging purposes 964 * we watch it here. 965 */ 966 shouldbesize = CTL_HEADER_LEN + count; 967 968 /* round to 8 octet boundary */ 969 shouldbesize = (shouldbesize + 7) & ~7; 970 971 if (n & 0x3) { 972 printf("Packet not padded, size = %d\n", n); 973 } if ((maclen = n - shouldbesize) >= MIN_MAC_LEN) { 974 printf( 975 "Packet shows signs of authentication (total %d, data %d, mac %d)\n", 976 n, shouldbesize, maclen); 977 lpkt = (u_long *)&rpkt; 978 printf("%08lx %08lx %08lx %08lx %08lx %08lx\n", 979 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 3]), 980 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 2]), 981 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 1]), 982 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long)]), 983 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 1]), 984 (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 2])); 985 key = ntohl(lpkt[(n - maclen) / sizeof(u_long)]); 986 printf("Authenticated with keyid %lu\n", (u_long)key); 987 if (key != 0 && key != info_auth_keyid) { 988 printf("We don't know that key\n"); 989 } else { 990 if (authdecrypt(key, (u_int32 *)&rpkt, 991 n - maclen, maclen)) { 992 printf("Auth okay!\n"); 993 } else { 994 printf("Auth failed!\n"); 995 } 996 } 997 } 998 } 999 1000 if (debug >= 2) 1001 printf("Got packet, size = %d\n", n); 1002 if (count > (u_short)(n-CTL_HEADER_LEN)) { 1003 if (debug) 1004 printf( 1005 "Received count of %d octets, data in packet is %d\n", 1006 count, n-CTL_HEADER_LEN); 1007 goto again; 1008 } 1009 if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) { 1010 if (debug) 1011 printf("Received count of 0 in non-final fragment\n"); 1012 goto again; 1013 } 1014 if (offset + count > sizeof(pktdata)) { 1015 if (debug) 1016 printf("Offset %d, count %d, too big for buffer\n", 1017 offset, count); 1018 return ERR_TOOMUCH; 1019 } 1020 if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) { 1021 if (debug) 1022 printf("Received second last fragment packet\n"); 1023 goto again; 1024 } 1025 1026 /* 1027 * So far, so good. Record this fragment, making sure it doesn't 1028 * overlap anything. 1029 */ 1030 if (debug >= 2) 1031 printf("Packet okay\n");; 1032 1033 if (numfrags == MAXFRAGS) { 1034 if (debug) 1035 printf("Number of fragments exceeds maximum\n"); 1036 return ERR_TOOMUCH; 1037 } 1038 1039 for (n = 0; n < numfrags; n++) { 1040 if (offset == offsets[n]) 1041 goto again; /* duplicate */ 1042 if (offset < offsets[n]) 1043 break; 1044 } 1045 1046 if ((u_short)(n > 0 && offsets[n-1] + counts[n-1]) > offset) 1047 goto overlap; 1048 if (n < numfrags && (u_short)(offset + count) > offsets[n]) 1049 goto overlap; 1050 1051 { 1052 register int i; 1053 1054 for (i = numfrags; i > n; i--) { 1055 offsets[i] = offsets[i-1]; 1056 counts[i] = counts[i-1]; 1057 } 1058 } 1059 offsets[n] = offset; 1060 counts[n] = count; 1061 numfrags++; 1062 1063 /* 1064 * Got that stuffed in right. Figure out if this was the last. 1065 * Record status info out of the last packet. 1066 */ 1067 if (!CTL_ISMORE(rpkt.r_m_e_op)) { 1068 seenlastfrag = 1; 1069 if (rstatus != 0) 1070 *rstatus = ntohs(rpkt.status); 1071 } 1072 1073 /* 1074 * Copy the data into the data buffer. 1075 */ 1076 memmove((char *)pktdata + offset, (char *)rpkt.data, count); 1077 1078 /* 1079 * If we've seen the last fragment, look for holes in the sequence. 1080 * If there aren't any, we're done. 1081 */ 1082 if (seenlastfrag && offsets[0] == 0) { 1083 for (n = 1; n < numfrags; n++) { 1084 if (offsets[n-1] + counts[n-1] != offsets[n]) 1085 break; 1086 } 1087 if (n == numfrags) { 1088 *rsize = offsets[numfrags-1] + counts[numfrags-1]; 1089 return 0; 1090 } 1091 } 1092 goto again; 1093 1094 overlap: 1095 /* 1096 * Print debugging message about overlapping fragments 1097 */ 1098 if (debug) 1099 printf("Overlapping fragments returned in response\n"); 1100 goto again; 1101 } 1102 1103 1104 /* 1105 * sendrequest - format and send a request packet 1106 */ 1107 static int 1108 sendrequest( 1109 int opcode, 1110 int associd, 1111 int auth, 1112 int qsize, 1113 char *qdata 1114 ) 1115 { 1116 struct ntp_control qpkt; 1117 int pktsize; 1118 1119 /* 1120 * Check to make sure the data will fit in one packet 1121 */ 1122 if (qsize > CTL_MAX_DATA_LEN) { 1123 (void) fprintf(stderr, 1124 "***Internal error! qsize (%d) too large\n", 1125 qsize); 1126 return 1; 1127 } 1128 1129 /* 1130 * Fill in the packet 1131 */ 1132 qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL); 1133 qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK); 1134 qpkt.sequence = htons(sequence); 1135 qpkt.status = 0; 1136 qpkt.associd = htons((u_short)associd); 1137 qpkt.offset = 0; 1138 qpkt.count = htons((u_short)qsize); 1139 1140 /* 1141 * If we have data, copy it in and pad it out to a 64 1142 * bit boundary. 1143 */ 1144 if (qsize > 0) { 1145 memmove((char *)qpkt.data, qdata, (unsigned)qsize); 1146 pktsize = qsize + CTL_HEADER_LEN; 1147 while (pktsize & (sizeof(u_long) - 1)) { 1148 qpkt.data[qsize++] = 0; 1149 pktsize++; 1150 } 1151 } else { 1152 pktsize = CTL_HEADER_LEN; 1153 } 1154 1155 /* 1156 * If it isn't authenticated we can just send it. Otherwise 1157 * we're going to have to think about it a little. 1158 */ 1159 if (!auth && !always_auth) { 1160 return sendpkt((char *)&qpkt, pktsize); 1161 } else { 1162 const char *pass = "\0"; 1163 int maclen = 0; 1164 u_long my_keyid; 1165 1166 /* 1167 * Pad out packet to a multiple of 8 octets to be sure 1168 * receiver can handle it. 1169 */ 1170 while (pktsize & 7) { 1171 qpkt.data[qsize++] = 0; 1172 pktsize++; 1173 } 1174 1175 /* 1176 * Get the keyid and the password if we don't have one. 1177 */ 1178 if (info_auth_keyid == 0) { 1179 int u_keyid = getkeyid("Keyid: "); 1180 if (u_keyid == 0 || u_keyid > NTP_MAXKEY) { 1181 (void) fprintf(stderr, 1182 "Invalid key identifier\n"); 1183 return 1; 1184 } 1185 info_auth_keyid = u_keyid; 1186 } 1187 if (!authistrusted(info_auth_keyid)) { 1188 pass = getpass("MD5 Password: "); 1189 if (*pass == '\0') { 1190 (void) fprintf(stderr, 1191 "Invalid password\n"); 1192 return (1); 1193 } 1194 } 1195 authusekey(info_auth_keyid, info_auth_keytype, (const u_char *)pass); 1196 authtrust(info_auth_keyid, 1); 1197 1198 /* 1199 * Stick the keyid in the packet where 1200 * cp currently points. Cp should be aligned 1201 * properly. Then do the encryptions. 1202 */ 1203 my_keyid = htonl(info_auth_keyid); 1204 memcpy(&qpkt.data[qsize], &my_keyid, sizeof my_keyid); 1205 maclen = authencrypt(info_auth_keyid, (u_int32 *)&qpkt, 1206 pktsize); 1207 if (maclen == 0) { 1208 (void) fprintf(stderr, "Key not found\n"); 1209 return (1); 1210 } 1211 return sendpkt((char *)&qpkt, pktsize + maclen); 1212 } 1213 /*NOTREACHED*/ 1214 } 1215 1216 1217 /* 1218 * doquery - send a request and process the response 1219 */ 1220 int 1221 doquery( 1222 int opcode, 1223 int associd, 1224 int auth, 1225 int qsize, 1226 char *qdata, 1227 u_short *rstatus, 1228 int *rsize, 1229 char **rdata 1230 ) 1231 { 1232 int res; 1233 int done; 1234 1235 /* 1236 * Check to make sure host is open 1237 */ 1238 if (!havehost) { 1239 (void) fprintf(stderr, "***No host open, use `host' command\n"); 1240 return -1; 1241 } 1242 1243 done = 0; 1244 sequence++; 1245 1246 again: 1247 /* 1248 * send a request 1249 */ 1250 res = sendrequest(opcode, associd, auth, qsize, qdata); 1251 if (res != 0) 1252 return res; 1253 1254 /* 1255 * Get the response. If we got a standard error, print a message 1256 */ 1257 res = getresponse(opcode, associd, rstatus, rsize, rdata, done); 1258 1259 if (res > 0) { 1260 if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) { 1261 if (res == ERR_INCOMPLETE) { 1262 /* 1263 * better bump the sequence so we don't 1264 * get confused about differing fragments. 1265 */ 1266 sequence++; 1267 } 1268 done = 1; 1269 goto again; 1270 } 1271 switch(res) { 1272 case CERR_BADFMT: 1273 (void) fprintf(stderr, 1274 "***Server reports a bad format request packet\n"); 1275 break; 1276 case CERR_PERMISSION: 1277 (void) fprintf(stderr, 1278 "***Server disallowed request (authentication?)\n"); 1279 break; 1280 case CERR_BADOP: 1281 (void) fprintf(stderr, 1282 "***Server reports a bad opcode in request\n"); 1283 break; 1284 case CERR_BADASSOC: 1285 (void) fprintf(stderr, 1286 "***Association ID %d unknown to server\n",associd); 1287 break; 1288 case CERR_UNKNOWNVAR: 1289 (void) fprintf(stderr, 1290 "***A request variable unknown to the server\n"); 1291 break; 1292 case CERR_BADVALUE: 1293 (void) fprintf(stderr, 1294 "***Server indicates a request variable was bad\n"); 1295 break; 1296 case ERR_UNSPEC: 1297 (void) fprintf(stderr, 1298 "***Server returned an unspecified error\n"); 1299 break; 1300 case ERR_TIMEOUT: 1301 (void) fprintf(stderr, "***Request timed out\n"); 1302 break; 1303 case ERR_INCOMPLETE: 1304 (void) fprintf(stderr, 1305 "***Response from server was incomplete\n"); 1306 break; 1307 case ERR_TOOMUCH: 1308 (void) fprintf(stderr, 1309 "***Buffer size exceeded for returned data\n"); 1310 break; 1311 default: 1312 (void) fprintf(stderr, 1313 "***Server returns unknown error code %d\n", res); 1314 break; 1315 } 1316 } 1317 return res; 1318 } 1319 1320 1321 /* 1322 * getcmds - read commands from the standard input and execute them 1323 */ 1324 static void 1325 getcmds(void) 1326 { 1327 #if defined(HAVE_LIBREADLINE) || defined(HAVE_LIBEDIT) 1328 char *line; 1329 1330 for (;;) { 1331 if ((line = readline(interactive?prompt:"")) == NULL) return; 1332 if (*line) add_history(line); 1333 docmd(line); 1334 free(line); 1335 } 1336 #else /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */ 1337 char line[MAXLINE]; 1338 1339 for (;;) { 1340 if (interactive) { 1341 #ifdef VMS /* work around a problem with mixing stdout & stderr */ 1342 fputs("",stdout); 1343 #endif 1344 (void) fputs(prompt, stderr); 1345 (void) fflush(stderr); 1346 } 1347 1348 if (fgets(line, sizeof line, stdin) == NULL) 1349 return; 1350 1351 docmd(line); 1352 } 1353 #endif /* not (HAVE_LIBREADLINE || HAVE_LIBEDIT) */ 1354 } 1355 1356 #ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */ 1357 /* 1358 * abortcmd - catch interrupts and abort the current command 1359 */ 1360 static RETSIGTYPE 1361 abortcmd( 1362 int sig 1363 ) 1364 { 1365 if (current_output == stdout) 1366 (void) fflush(stdout); 1367 putc('\n', stderr); 1368 (void) fflush(stderr); 1369 if (jump) longjmp(interrupt_buf, 1); 1370 } 1371 #endif /* SYS_WINNT */ 1372 1373 /* 1374 * docmd - decode the command line and execute a command 1375 */ 1376 static void 1377 docmd( 1378 const char *cmdline 1379 ) 1380 { 1381 char *tokens[1+MAXARGS+2]; 1382 struct parse pcmd; 1383 int ntok; 1384 static int i; 1385 struct xcmd *xcmd; 1386 1387 /* 1388 * Tokenize the command line. If nothing on it, return. 1389 */ 1390 tokenize(cmdline, tokens, &ntok); 1391 if (ntok == 0) 1392 return; 1393 1394 /* 1395 * Find the appropriate command description. 1396 */ 1397 i = findcmd(tokens[0], builtins, opcmds, &xcmd); 1398 if (i == 0) { 1399 (void) fprintf(stderr, "***Command `%s' unknown\n", 1400 tokens[0]); 1401 return; 1402 } else if (i >= 2) { 1403 (void) fprintf(stderr, "***Command `%s' ambiguous\n", 1404 tokens[0]); 1405 return; 1406 } 1407 1408 /* 1409 * Save the keyword, then walk through the arguments, interpreting 1410 * as we go. 1411 */ 1412 pcmd.keyword = tokens[0]; 1413 pcmd.nargs = 0; 1414 for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) { 1415 if ((i+1) >= ntok) { 1416 if (!(xcmd->arg[i] & OPT)) { 1417 printusage(xcmd, stderr); 1418 return; 1419 } 1420 break; 1421 } 1422 if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>')) 1423 break; 1424 if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i])) 1425 return; 1426 pcmd.nargs++; 1427 } 1428 1429 i++; 1430 if (i < ntok && *tokens[i] == '>') { 1431 char *fname; 1432 1433 if (*(tokens[i]+1) != '\0') 1434 fname = tokens[i]+1; 1435 else if ((i+1) < ntok) 1436 fname = tokens[i+1]; 1437 else { 1438 (void) fprintf(stderr, "***No file for redirect\n"); 1439 return; 1440 } 1441 1442 current_output = fopen(fname, "w"); 1443 if (current_output == NULL) { 1444 (void) fprintf(stderr, "***Error opening %s: ", fname); 1445 perror(""); 1446 return; 1447 } 1448 i = 1; /* flag we need a close */ 1449 } else { 1450 current_output = stdout; 1451 i = 0; /* flag no close */ 1452 } 1453 1454 if (interactive && setjmp(interrupt_buf)) { 1455 jump = 0; 1456 return; 1457 } else { 1458 jump++; 1459 (xcmd->handler)(&pcmd, current_output); 1460 jump = 0; /* HMS: 961106: was after fclose() */ 1461 if (i) (void) fclose(current_output); 1462 } 1463 } 1464 1465 1466 /* 1467 * tokenize - turn a command line into tokens 1468 */ 1469 static void 1470 tokenize( 1471 const char *line, 1472 char **tokens, 1473 int *ntok 1474 ) 1475 { 1476 register const char *cp; 1477 register char *sp; 1478 static char tspace[MAXLINE]; 1479 1480 sp = tspace; 1481 cp = line; 1482 for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) { 1483 tokens[*ntok] = sp; 1484 while (ISSPACE(*cp)) 1485 cp++; 1486 if (ISEOL(*cp)) 1487 break; 1488 do { 1489 *sp++ = *cp++; 1490 } while (!ISSPACE(*cp) && !ISEOL(*cp)); 1491 1492 *sp++ = '\0'; 1493 } 1494 } 1495 1496 1497 1498 /* 1499 * findcmd - find a command in a command description table 1500 */ 1501 static int 1502 findcmd( 1503 register char *str, 1504 struct xcmd *clist1, 1505 struct xcmd *clist2, 1506 struct xcmd **cmd 1507 ) 1508 { 1509 register struct xcmd *cl; 1510 register int clen; 1511 int nmatch; 1512 struct xcmd *nearmatch = NULL; 1513 struct xcmd *clist; 1514 1515 clen = strlen(str); 1516 nmatch = 0; 1517 if (clist1 != 0) 1518 clist = clist1; 1519 else if (clist2 != 0) 1520 clist = clist2; 1521 else 1522 return 0; 1523 1524 again: 1525 for (cl = clist; cl->keyword != 0; cl++) { 1526 /* do a first character check, for efficiency */ 1527 if (*str != *(cl->keyword)) 1528 continue; 1529 if (strncmp(str, cl->keyword, (unsigned)clen) == 0) { 1530 /* 1531 * Could be extact match, could be approximate. 1532 * Is exact if the length of the keyword is the 1533 * same as the str. 1534 */ 1535 if (*((cl->keyword) + clen) == '\0') { 1536 *cmd = cl; 1537 return 1; 1538 } 1539 nmatch++; 1540 nearmatch = cl; 1541 } 1542 } 1543 1544 /* 1545 * See if there is more to do. If so, go again. Sorry about the 1546 * goto, too much looking at BSD sources... 1547 */ 1548 if (clist == clist1 && clist2 != 0) { 1549 clist = clist2; 1550 goto again; 1551 } 1552 1553 /* 1554 * If we got extactly 1 near match, use it, else return number 1555 * of matches. 1556 */ 1557 if (nmatch == 1) { 1558 *cmd = nearmatch; 1559 return 1; 1560 } 1561 return nmatch; 1562 } 1563 1564 1565 /* 1566 * getarg - interpret an argument token 1567 */ 1568 static int 1569 getarg( 1570 char *str, 1571 int code, 1572 arg_v *argp 1573 ) 1574 { 1575 int isneg; 1576 char *cp, *np; 1577 static const char *digits = "0123456789"; 1578 1579 switch (code & ~OPT) { 1580 case STR: 1581 argp->string = str; 1582 break; 1583 case ADD: 1584 if (!getnetnum(str, &(argp->netnum), (char *)0, 0)) { 1585 return 0; 1586 } 1587 break; 1588 case INT: 1589 case UINT: 1590 isneg = 0; 1591 np = str; 1592 if (*np == '&') { 1593 np++; 1594 isneg = atoi(np); 1595 if (isneg <= 0) { 1596 (void) fprintf(stderr, 1597 "***Association value `%s' invalid/undecodable\n", str); 1598 return 0; 1599 } 1600 if (isneg > numassoc) { 1601 if (numassoc == 0) { 1602 (void) fprintf(stderr, 1603 "***Association for `%s' unknown (max &%d)\n", 1604 str, numassoc); 1605 return 0; 1606 } else { 1607 isneg = numassoc; 1608 } 1609 } 1610 argp->uval = assoc_cache[isneg-1].assid; 1611 break; 1612 } 1613 1614 if (*np == '-') { 1615 np++; 1616 isneg = 1; 1617 } 1618 1619 argp->uval = 0; 1620 do { 1621 cp = strchr(digits, *np); 1622 if (cp == NULL) { 1623 (void) fprintf(stderr, 1624 "***Illegal integer value %s\n", str); 1625 return 0; 1626 } 1627 argp->uval *= 10; 1628 argp->uval += (cp - digits); 1629 } while (*(++np) != '\0'); 1630 1631 if (isneg) { 1632 if ((code & ~OPT) == UINT) { 1633 (void) fprintf(stderr, 1634 "***Value %s should be unsigned\n", str); 1635 return 0; 1636 } 1637 argp->ival = -argp->ival; 1638 } 1639 break; 1640 case IP_VERSION: 1641 if (!strcmp("-6", str)) 1642 argp->ival = 6 ; 1643 else if (!strcmp("-4", str)) 1644 argp->ival = 4 ; 1645 else { 1646 (void) fprintf(stderr, 1647 "***Version must be either 4 or 6\n"); 1648 return 0; 1649 } 1650 break; 1651 } 1652 1653 return 1; 1654 } 1655 1656 1657 /* 1658 * getnetnum - given a host name, return its net number 1659 * and (optional) full name 1660 */ 1661 int 1662 getnetnum( 1663 const char *hname, 1664 struct sockaddr_storage *num, 1665 char *fullhost, 1666 int af 1667 ) 1668 { 1669 int err; 1670 int sockaddr_len; 1671 struct addrinfo hints, *ai = NULL; 1672 1673 sockaddr_len = (af == AF_INET) 1674 ? sizeof(struct sockaddr_in) 1675 : sizeof(struct sockaddr_in6); 1676 memset((char *)&hints, 0, sizeof(struct addrinfo)); 1677 hints.ai_flags = AI_CANONNAME; 1678 #ifdef AI_ADDRCONFIG 1679 hints.ai_flags |= AI_ADDRCONFIG; 1680 #endif 1681 1682 /* decodenetnum works with addresses only */ 1683 if (decodenetnum(hname, num)) { 1684 if (fullhost != 0) { 1685 getnameinfo((struct sockaddr *)num, sockaddr_len, 1686 fullhost, sizeof(fullhost), NULL, 0, 1687 NI_NUMERICHOST); 1688 1689 } 1690 return 1; 1691 } else if ((err = getaddrinfo(hname, "ntp", &hints, &ai)) == 0) { 1692 memmove((char *)num, ai->ai_addr, ai->ai_addrlen); 1693 if (ai->ai_canonname != 0) 1694 (void) strcpy(fullhost, ai->ai_canonname); 1695 return 1; 1696 } else { 1697 (void) fprintf(stderr, "***Can't find host %s\n", hname); 1698 return 0; 1699 } 1700 /*NOTREACHED*/ 1701 } 1702 1703 /* 1704 * nntohost - convert network number to host name. This routine enforces 1705 * the showhostnames setting. 1706 */ 1707 char * 1708 nntohost( 1709 struct sockaddr_storage *netnum 1710 ) 1711 { 1712 if (!showhostnames) 1713 return stoa(netnum); 1714 if ((netnum->ss_family == AF_INET) && ISREFCLOCKADR(netnum)) 1715 return refnumtoa(netnum); 1716 return socktohost(netnum); 1717 } 1718 1719 1720 /* 1721 * rtdatetolfp - decode an RT-11 date into an l_fp 1722 */ 1723 static int 1724 rtdatetolfp( 1725 char *str, 1726 l_fp *lfp 1727 ) 1728 { 1729 register char *cp; 1730 register int i; 1731 struct calendar cal; 1732 char buf[4]; 1733 static const char *months[12] = { 1734 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 1735 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 1736 }; 1737 1738 cal.yearday = 0; 1739 1740 /* 1741 * An RT-11 date looks like: 1742 * 1743 * d[d]-Mth-y[y] hh:mm:ss 1744 * 1745 * (No docs, but assume 4-digit years are also legal...) 1746 * 1747 * d[d]-Mth-y[y[y[y]]] hh:mm:ss 1748 */ 1749 cp = str; 1750 if (!isdigit((int)*cp)) { 1751 if (*cp == '-') { 1752 /* 1753 * Catch special case 1754 */ 1755 L_CLR(lfp); 1756 return 1; 1757 } 1758 return 0; 1759 } 1760 1761 cal.monthday = (u_char) (*cp++ - '0'); /* ascii dependent */ 1762 if (isdigit((int)*cp)) { 1763 cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1)); 1764 cal.monthday = (u_char)(cal.monthday + *cp++ - '0'); 1765 } 1766 1767 if (*cp++ != '-') 1768 return 0; 1769 1770 for (i = 0; i < 3; i++) 1771 buf[i] = *cp++; 1772 buf[3] = '\0'; 1773 1774 for (i = 0; i < 12; i++) 1775 if (STREQ(buf, months[i])) 1776 break; 1777 if (i == 12) 1778 return 0; 1779 cal.month = (u_char)(i + 1); 1780 1781 if (*cp++ != '-') 1782 return 0; 1783 1784 if (!isdigit((int)*cp)) 1785 return 0; 1786 cal.year = (u_short)(*cp++ - '0'); 1787 if (isdigit((int)*cp)) { 1788 cal.year = (u_short)((cal.year << 3) + (cal.year << 1)); 1789 cal.year = (u_short)(*cp++ - '0'); 1790 } 1791 if (isdigit((int)*cp)) { 1792 cal.year = (u_short)((cal.year << 3) + (cal.year << 1)); 1793 cal.year = (u_short)(cal.year + *cp++ - '0'); 1794 } 1795 if (isdigit((int)*cp)) { 1796 cal.year = (u_short)((cal.year << 3) + (cal.year << 1)); 1797 cal.year = (u_short)(cal.year + *cp++ - '0'); 1798 } 1799 1800 /* 1801 * Catch special case. If cal.year == 0 this is a zero timestamp. 1802 */ 1803 if (cal.year == 0) { 1804 L_CLR(lfp); 1805 return 1; 1806 } 1807 1808 if (*cp++ != ' ' || !isdigit((int)*cp)) 1809 return 0; 1810 cal.hour = (u_char)(*cp++ - '0'); 1811 if (isdigit((int)*cp)) { 1812 cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1)); 1813 cal.hour = (u_char)(cal.hour + *cp++ - '0'); 1814 } 1815 1816 if (*cp++ != ':' || !isdigit((int)*cp)) 1817 return 0; 1818 cal.minute = (u_char)(*cp++ - '0'); 1819 if (isdigit((int)*cp)) { 1820 cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1)); 1821 cal.minute = (u_char)(cal.minute + *cp++ - '0'); 1822 } 1823 1824 if (*cp++ != ':' || !isdigit((int)*cp)) 1825 return 0; 1826 cal.second = (u_char)(*cp++ - '0'); 1827 if (isdigit((int)*cp)) { 1828 cal.second = (u_char)((cal.second << 3) + (cal.second << 1)); 1829 cal.second = (u_char)(cal.second + *cp++ - '0'); 1830 } 1831 1832 /* 1833 * For RT-11, 1972 seems to be the pivot year 1834 */ 1835 if (cal.year < 72) 1836 cal.year += 2000; 1837 if (cal.year < 100) 1838 cal.year += 1900; 1839 1840 lfp->l_ui = caltontp(&cal); 1841 lfp->l_uf = 0; 1842 return 1; 1843 } 1844 1845 1846 /* 1847 * decodets - decode a timestamp into an l_fp format number, with 1848 * consideration of fuzzball formats. 1849 */ 1850 int 1851 decodets( 1852 char *str, 1853 l_fp *lfp 1854 ) 1855 { 1856 /* 1857 * If it starts with a 0x, decode as hex. 1858 */ 1859 if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X')) 1860 return hextolfp(str+2, lfp); 1861 1862 /* 1863 * If it starts with a '"', try it as an RT-11 date. 1864 */ 1865 if (*str == '"') { 1866 register char *cp = str+1; 1867 register char *bp; 1868 char buf[30]; 1869 1870 bp = buf; 1871 while (*cp != '"' && *cp != '\0' && bp < &buf[29]) 1872 *bp++ = *cp++; 1873 *bp = '\0'; 1874 return rtdatetolfp(buf, lfp); 1875 } 1876 1877 /* 1878 * Might still be hex. Check out the first character. Talk 1879 * about heuristics! 1880 */ 1881 if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f')) 1882 return hextolfp(str, lfp); 1883 1884 /* 1885 * Try it as a decimal. If this fails, try as an unquoted 1886 * RT-11 date. This code should go away eventually. 1887 */ 1888 if (atolfp(str, lfp)) 1889 return 1; 1890 return rtdatetolfp(str, lfp); 1891 } 1892 1893 1894 /* 1895 * decodetime - decode a time value. It should be in milliseconds 1896 */ 1897 int 1898 decodetime( 1899 char *str, 1900 l_fp *lfp 1901 ) 1902 { 1903 return mstolfp(str, lfp); 1904 } 1905 1906 1907 /* 1908 * decodeint - decode an integer 1909 */ 1910 int 1911 decodeint( 1912 char *str, 1913 long *val 1914 ) 1915 { 1916 if (*str == '0') { 1917 if (*(str+1) == 'x' || *(str+1) == 'X') 1918 return hextoint(str+2, (void *)&val); 1919 return octtoint(str, (void *)&val); 1920 } 1921 return atoint(str, val); 1922 } 1923 1924 1925 /* 1926 * decodeuint - decode an unsigned integer 1927 */ 1928 int 1929 decodeuint( 1930 char *str, 1931 u_long *val 1932 ) 1933 { 1934 if (*str == '0') { 1935 if (*(str + 1) == 'x' || *(str + 1) == 'X') 1936 return (hextoint(str + 2, val)); 1937 return (octtoint(str, val)); 1938 } 1939 return (atouint(str, val)); 1940 } 1941 1942 1943 /* 1944 * decodearr - decode an array of time values 1945 */ 1946 static int 1947 decodearr( 1948 char *str, 1949 int *narr, 1950 l_fp *lfparr 1951 ) 1952 { 1953 register char *cp, *bp; 1954 register l_fp *lfp; 1955 char buf[60]; 1956 1957 lfp = lfparr; 1958 cp = str; 1959 *narr = 0; 1960 1961 while (*narr < 8) { 1962 while (isspace((int)*cp)) 1963 cp++; 1964 if (*cp == '\0') 1965 break; 1966 1967 bp = buf; 1968 while (!isspace((int)*cp) && *cp != '\0') 1969 *bp++ = *cp++; 1970 *bp++ = '\0'; 1971 1972 if (!decodetime(buf, lfp)) 1973 return 0; 1974 (*narr)++; 1975 lfp++; 1976 } 1977 return 1; 1978 } 1979 1980 1981 /* 1982 * Finally, the built in command handlers 1983 */ 1984 1985 /* 1986 * help - tell about commands, or details of a particular command 1987 */ 1988 static void 1989 help( 1990 struct parse *pcmd, 1991 FILE *fp 1992 ) 1993 { 1994 int i; 1995 int n; 1996 struct xcmd *xcp; 1997 char *cmd; 1998 const char *cmdsort[100]; 1999 int length[100]; 2000 int maxlength; 2001 int numperline; 2002 static const char *spaces = " "; /* 20 spaces */ 2003 2004 if (pcmd->nargs == 0) { 2005 n = 0; 2006 for (xcp = builtins; xcp->keyword != 0; xcp++) { 2007 if (*(xcp->keyword) != '?') 2008 cmdsort[n++] = xcp->keyword; 2009 } 2010 for (xcp = opcmds; xcp->keyword != 0; xcp++) 2011 cmdsort[n++] = xcp->keyword; 2012 2013 #ifdef QSORT_USES_VOID_P 2014 qsort(cmdsort, (size_t)n, sizeof(char *), helpsort); 2015 #else 2016 qsort((char *)cmdsort, (size_t)n, sizeof(char *), helpsort); 2017 #endif 2018 2019 maxlength = 0; 2020 for (i = 0; i < n; i++) { 2021 length[i] = strlen(cmdsort[i]); 2022 if (length[i] > maxlength) 2023 maxlength = length[i]; 2024 } 2025 maxlength++; 2026 numperline = 76 / maxlength; 2027 2028 (void) fprintf(fp, "Commands available:\n"); 2029 for (i = 0; i < n; i++) { 2030 if ((i % numperline) == (numperline-1) 2031 || i == (n-1)) 2032 (void) fprintf(fp, "%s\n", cmdsort[i]); 2033 else 2034 (void) fprintf(fp, "%s%s", cmdsort[i], 2035 spaces+20-maxlength+length[i]); 2036 } 2037 } else { 2038 cmd = pcmd->argval[0].string; 2039 n = findcmd(cmd, builtins, opcmds, &xcp); 2040 if (n == 0) { 2041 (void) fprintf(stderr, 2042 "Command `%s' is unknown\n", cmd); 2043 return; 2044 } else if (n >= 2) { 2045 (void) fprintf(stderr, 2046 "Command `%s' is ambiguous\n", cmd); 2047 return; 2048 } 2049 (void) fprintf(fp, "function: %s\n", xcp->comment); 2050 printusage(xcp, fp); 2051 } 2052 } 2053 2054 2055 /* 2056 * helpsort - do hostname qsort comparisons 2057 */ 2058 #ifdef QSORT_USES_VOID_P 2059 static int 2060 helpsort( 2061 const void *t1, 2062 const void *t2 2063 ) 2064 { 2065 char const * const * name1 = (char const * const *)t1; 2066 char const * const * name2 = (char const * const *)t2; 2067 2068 return strcmp(*name1, *name2); 2069 } 2070 2071 #else 2072 static int 2073 helpsort( 2074 char **name1, 2075 char **name2 2076 ) 2077 { 2078 return strcmp(*name1, *name2); 2079 } 2080 #endif 2081 2082 /* 2083 * printusage - print usage information for a command 2084 */ 2085 static void 2086 printusage( 2087 struct xcmd *xcp, 2088 FILE *fp 2089 ) 2090 { 2091 register int i; 2092 2093 (void) fprintf(fp, "usage: %s", xcp->keyword); 2094 for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) { 2095 if (xcp->arg[i] & OPT) 2096 (void) fprintf(fp, " [ %s ]", xcp->desc[i]); 2097 else 2098 (void) fprintf(fp, " %s", xcp->desc[i]); 2099 } 2100 (void) fprintf(fp, "\n"); 2101 } 2102 2103 2104 /* 2105 * timeout - set time out time 2106 */ 2107 static void 2108 timeout( 2109 struct parse *pcmd, 2110 FILE *fp 2111 ) 2112 { 2113 int val; 2114 2115 if (pcmd->nargs == 0) { 2116 val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000; 2117 (void) fprintf(fp, "primary timeout %d ms\n", val); 2118 } else { 2119 tvout.tv_sec = pcmd->argval[0].uval / 1000; 2120 tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000)) 2121 * 1000; 2122 } 2123 } 2124 2125 2126 /* 2127 * auth_delay - set delay for auth requests 2128 */ 2129 static void 2130 auth_delay( 2131 struct parse *pcmd, 2132 FILE *fp 2133 ) 2134 { 2135 int isneg; 2136 u_long val; 2137 2138 if (pcmd->nargs == 0) { 2139 val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967; 2140 (void) fprintf(fp, "delay %lu ms\n", val); 2141 } else { 2142 if (pcmd->argval[0].ival < 0) { 2143 isneg = 1; 2144 val = (u_long)(-pcmd->argval[0].ival); 2145 } else { 2146 isneg = 0; 2147 val = (u_long)pcmd->argval[0].ival; 2148 } 2149 2150 delay_time.l_ui = val / 1000; 2151 val %= 1000; 2152 delay_time.l_uf = val * 4294967; /* 2**32/1000 */ 2153 2154 if (isneg) 2155 L_NEG(&delay_time); 2156 } 2157 } 2158 2159 2160 /* 2161 * host - set the host we are dealing with. 2162 */ 2163 static void 2164 host( 2165 struct parse *pcmd, 2166 FILE *fp 2167 ) 2168 { 2169 int i; 2170 2171 if (pcmd->nargs == 0) { 2172 if (havehost) 2173 (void) fprintf(fp, "current host is %s\n", currenthost); 2174 else 2175 (void) fprintf(fp, "no current host\n"); 2176 return; 2177 } 2178 2179 i = 0; 2180 ai_fam_templ = ai_fam_default; 2181 if (pcmd->nargs == 2) { 2182 if (!strcmp("-4", pcmd->argval[i].string)) 2183 ai_fam_templ = AF_INET; 2184 else if (!strcmp("-6", pcmd->argval[i].string)) 2185 ai_fam_templ = AF_INET6; 2186 else { 2187 if (havehost) 2188 (void) fprintf(fp, 2189 "current host remains %s\n", currenthost); 2190 else 2191 (void) fprintf(fp, "still no current host\n"); 2192 return; 2193 } 2194 i = 1; 2195 } 2196 if (openhost(pcmd->argval[i].string)) { 2197 (void) fprintf(fp, "current host set to %s\n", currenthost); 2198 numassoc = 0; 2199 } else { 2200 if (havehost) 2201 (void) fprintf(fp, 2202 "current host remains %s\n", currenthost); 2203 else 2204 (void) fprintf(fp, "still no current host\n"); 2205 } 2206 } 2207 2208 2209 /* 2210 * poll - do one (or more) polls of the host via NTP 2211 */ 2212 /*ARGSUSED*/ 2213 static void 2214 ntp_poll( 2215 struct parse *pcmd, 2216 FILE *fp 2217 ) 2218 { 2219 (void) fprintf(fp, "poll not implemented yet\n"); 2220 } 2221 2222 2223 /* 2224 * keyid - get a keyid to use for authenticating requests 2225 */ 2226 static void 2227 keyid( 2228 struct parse *pcmd, 2229 FILE *fp 2230 ) 2231 { 2232 if (pcmd->nargs == 0) { 2233 if (info_auth_keyid == 0) 2234 (void) fprintf(fp, "no keyid defined\n"); 2235 else 2236 (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid); 2237 } else { 2238 /* allow zero so that keyid can be cleared. */ 2239 if(pcmd->argval[0].uval > NTP_MAXKEY) 2240 (void) fprintf(fp, "Invalid key identifier\n"); 2241 info_auth_keyid = pcmd->argval[0].uval; 2242 } 2243 } 2244 2245 /* 2246 * keytype - get type of key to use for authenticating requests 2247 */ 2248 static void 2249 keytype( 2250 struct parse *pcmd, 2251 FILE *fp 2252 ) 2253 { 2254 if (pcmd->nargs == 0) 2255 fprintf(fp, "keytype is %s\n", 2256 (info_auth_keytype == KEY_TYPE_MD5) ? "MD5" : "???"); 2257 else 2258 switch (*(pcmd->argval[0].string)) { 2259 case 'm': 2260 case 'M': 2261 info_auth_keytype = KEY_TYPE_MD5; 2262 break; 2263 2264 default: 2265 fprintf(fp, "keytype must be 'md5'\n"); 2266 } 2267 } 2268 2269 2270 2271 /* 2272 * passwd - get an authentication key 2273 */ 2274 /*ARGSUSED*/ 2275 static void 2276 passwd( 2277 struct parse *pcmd, 2278 FILE *fp 2279 ) 2280 { 2281 char *pass; 2282 2283 if (info_auth_keyid == 0) { 2284 int u_keyid = getkeyid("Keyid: "); 2285 if (u_keyid == 0 || u_keyid > NTP_MAXKEY) { 2286 (void)fprintf(fp, "Invalid key identifier\n"); 2287 return; 2288 } 2289 info_auth_keyid = u_keyid; 2290 } 2291 pass = getpass("MD5 Password: "); 2292 if (*pass == '\0') 2293 (void) fprintf(fp, "Password unchanged\n"); 2294 else { 2295 authusekey(info_auth_keyid, info_auth_keytype, (u_char *)pass); 2296 authtrust(info_auth_keyid, 1); 2297 } 2298 } 2299 2300 2301 /* 2302 * hostnames - set the showhostnames flag 2303 */ 2304 static void 2305 hostnames( 2306 struct parse *pcmd, 2307 FILE *fp 2308 ) 2309 { 2310 if (pcmd->nargs == 0) { 2311 if (showhostnames) 2312 (void) fprintf(fp, "hostnames being shown\n"); 2313 else 2314 (void) fprintf(fp, "hostnames not being shown\n"); 2315 } else { 2316 if (STREQ(pcmd->argval[0].string, "yes")) 2317 showhostnames = 1; 2318 else if (STREQ(pcmd->argval[0].string, "no")) 2319 showhostnames = 0; 2320 else 2321 (void)fprintf(stderr, "What?\n"); 2322 } 2323 } 2324 2325 2326 2327 /* 2328 * setdebug - set/change debugging level 2329 */ 2330 static void 2331 setdebug( 2332 struct parse *pcmd, 2333 FILE *fp 2334 ) 2335 { 2336 if (pcmd->nargs == 0) { 2337 (void) fprintf(fp, "debug level is %d\n", debug); 2338 return; 2339 } else if (STREQ(pcmd->argval[0].string, "no")) { 2340 debug = 0; 2341 } else if (STREQ(pcmd->argval[0].string, "more")) { 2342 debug++; 2343 } else if (STREQ(pcmd->argval[0].string, "less")) { 2344 debug--; 2345 } else { 2346 (void) fprintf(fp, "What?\n"); 2347 return; 2348 } 2349 (void) fprintf(fp, "debug level set to %d\n", debug); 2350 } 2351 2352 2353 /* 2354 * quit - stop this nonsense 2355 */ 2356 /*ARGSUSED*/ 2357 static void 2358 quit( 2359 struct parse *pcmd, 2360 FILE *fp 2361 ) 2362 { 2363 if (havehost) 2364 closesocket(sockfd); /* cleanliness next to godliness */ 2365 exit(0); 2366 } 2367 2368 2369 /* 2370 * version - print the current version number 2371 */ 2372 /*ARGSUSED*/ 2373 static void 2374 version( 2375 struct parse *pcmd, 2376 FILE *fp 2377 ) 2378 { 2379 2380 (void) fprintf(fp, "%s\n", Version); 2381 return; 2382 } 2383 2384 2385 /* 2386 * raw - set raw mode output 2387 */ 2388 /*ARGSUSED*/ 2389 static void 2390 raw( 2391 struct parse *pcmd, 2392 FILE *fp 2393 ) 2394 { 2395 rawmode = 1; 2396 (void) fprintf(fp, "Output set to raw\n"); 2397 } 2398 2399 2400 /* 2401 * cooked - set cooked mode output 2402 */ 2403 /*ARGSUSED*/ 2404 static void 2405 cooked( 2406 struct parse *pcmd, 2407 FILE *fp 2408 ) 2409 { 2410 rawmode = 0; 2411 (void) fprintf(fp, "Output set to cooked\n"); 2412 return; 2413 } 2414 2415 2416 /* 2417 * authenticate - always authenticate requests to this host 2418 */ 2419 static void 2420 authenticate( 2421 struct parse *pcmd, 2422 FILE *fp 2423 ) 2424 { 2425 if (pcmd->nargs == 0) { 2426 if (always_auth) { 2427 (void) fprintf(fp, 2428 "authenticated requests being sent\n"); 2429 } else 2430 (void) fprintf(fp, 2431 "unauthenticated requests being sent\n"); 2432 } else { 2433 if (STREQ(pcmd->argval[0].string, "yes")) { 2434 always_auth = 1; 2435 } else if (STREQ(pcmd->argval[0].string, "no")) { 2436 always_auth = 0; 2437 } else 2438 (void)fprintf(stderr, "What?\n"); 2439 } 2440 } 2441 2442 2443 /* 2444 * ntpversion - choose the NTP version to use 2445 */ 2446 static void 2447 ntpversion( 2448 struct parse *pcmd, 2449 FILE *fp 2450 ) 2451 { 2452 if (pcmd->nargs == 0) { 2453 (void) fprintf(fp, 2454 "NTP version being claimed is %d\n", pktversion); 2455 } else { 2456 if (pcmd->argval[0].uval < NTP_OLDVERSION 2457 || pcmd->argval[0].uval > NTP_VERSION) { 2458 (void) fprintf(stderr, "versions %d to %d, please\n", 2459 NTP_OLDVERSION, NTP_VERSION); 2460 } else { 2461 pktversion = (u_char) pcmd->argval[0].uval; 2462 } 2463 } 2464 } 2465 2466 2467 /* 2468 * warning - print a warning message 2469 */ 2470 static void 2471 warning( 2472 const char *fmt, 2473 const char *st1, 2474 const char *st2 2475 ) 2476 { 2477 (void) fprintf(stderr, "%s: ", progname); 2478 (void) fprintf(stderr, fmt, st1, st2); 2479 (void) fprintf(stderr, ": "); 2480 perror(""); 2481 } 2482 2483 2484 /* 2485 * error - print a message and exit 2486 */ 2487 static void 2488 error( 2489 const char *fmt, 2490 const char *st1, 2491 const char *st2 2492 ) 2493 { 2494 warning(fmt, st1, st2); 2495 exit(1); 2496 } 2497 2498 /* 2499 * getkeyid - prompt the user for a keyid to use 2500 */ 2501 static u_long 2502 getkeyid( 2503 const char *keyprompt 2504 ) 2505 { 2506 register char *p; 2507 register int c; 2508 FILE *fi; 2509 char pbuf[20]; 2510 2511 #ifndef SYS_WINNT 2512 if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL) 2513 #else 2514 if ((fi = _fdopen((int)GetStdHandle(STD_INPUT_HANDLE), "r")) == NULL) 2515 #endif /* SYS_WINNT */ 2516 fi = stdin; 2517 else 2518 setbuf(fi, (char *)NULL); 2519 fprintf(stderr, "%s", keyprompt); fflush(stderr); 2520 for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) { 2521 if (p < &pbuf[18]) 2522 *p++ = (char)c; 2523 } 2524 *p = '\0'; 2525 if (fi != stdin) 2526 fclose(fi); 2527 if (strcmp(pbuf, "0") == 0) 2528 return 0; 2529 2530 return (u_long) atoi(pbuf); 2531 } 2532 2533 2534 /* 2535 * atoascii - printable-ize possibly ascii data using the character 2536 * transformations cat -v uses. 2537 */ 2538 static void 2539 atoascii( 2540 int length, 2541 char *data, 2542 char *outdata 2543 ) 2544 { 2545 register u_char *cp; 2546 register u_char *ocp; 2547 register u_char c; 2548 2549 if (!data) 2550 { 2551 *outdata = '\0'; 2552 return; 2553 } 2554 2555 ocp = (u_char *)outdata; 2556 for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) { 2557 c = *cp; 2558 if (c == '\0') 2559 break; 2560 if (c == '\0') 2561 break; 2562 if (c > 0177) { 2563 *ocp++ = 'M'; 2564 *ocp++ = '-'; 2565 c &= 0177; 2566 } 2567 2568 if (c < ' ') { 2569 *ocp++ = '^'; 2570 *ocp++ = (u_char)(c + '@'); 2571 } else if (c == 0177) { 2572 *ocp++ = '^'; 2573 *ocp++ = '?'; 2574 } else { 2575 *ocp++ = c; 2576 } 2577 if (ocp >= ((u_char *)outdata + length - 4)) 2578 break; 2579 } 2580 *ocp++ = '\0'; 2581 } 2582 2583 2584 2585 /* 2586 * makeascii - print possibly ascii data using the character 2587 * transformations that cat -v uses. 2588 */ 2589 static void 2590 makeascii( 2591 int length, 2592 char *data, 2593 FILE *fp 2594 ) 2595 { 2596 register u_char *cp; 2597 register int c; 2598 2599 for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) { 2600 c = (int)*cp; 2601 if (c > 0177) { 2602 putc('M', fp); 2603 putc('-', fp); 2604 c &= 0177; 2605 } 2606 2607 if (c < ' ') { 2608 putc('^', fp); 2609 putc(c+'@', fp); 2610 } else if (c == 0177) { 2611 putc('^', fp); 2612 putc('?', fp); 2613 } else { 2614 putc(c, fp); 2615 } 2616 } 2617 } 2618 2619 2620 /* 2621 * asciize - same thing as makeascii except add a newline 2622 */ 2623 void 2624 asciize( 2625 int length, 2626 char *data, 2627 FILE *fp 2628 ) 2629 { 2630 makeascii(length, data, fp); 2631 putc('\n', fp); 2632 } 2633 2634 2635 /* 2636 * Some circular buffer space 2637 */ 2638 #define CBLEN 80 2639 #define NUMCB 6 2640 2641 char circ_buf[NUMCB][CBLEN]; 2642 int nextcb = 0; 2643 2644 /* 2645 * nextvar - find the next variable in the buffer 2646 */ 2647 int 2648 nextvar( 2649 int *datalen, 2650 char **datap, 2651 char **vname, 2652 char **vvalue 2653 ) 2654 { 2655 register char *cp; 2656 register char *np; 2657 register char *cpend; 2658 register char *npend; /* character after last */ 2659 int quoted = 0; 2660 static char name[MAXVARLEN]; 2661 static char value[MAXVALLEN]; 2662 2663 cp = *datap; 2664 cpend = cp + *datalen; 2665 2666 /* 2667 * Space past commas and white space 2668 */ 2669 while (cp < cpend && (*cp == ',' || isspace((int)*cp))) 2670 cp++; 2671 if (cp == cpend) 2672 return 0; 2673 2674 /* 2675 * Copy name until we hit a ',', an '=', a '\r' or a '\n'. Backspace 2676 * over any white space and terminate it. 2677 */ 2678 np = name; 2679 npend = &name[MAXVARLEN]; 2680 while (cp < cpend && np < npend && *cp != ',' && *cp != '=' 2681 && *cp != '\r' && *cp != '\n') 2682 *np++ = *cp++; 2683 /* 2684 * Check if we ran out of name space, without reaching the end or a 2685 * terminating character 2686 */ 2687 if (np == npend && !(cp == cpend || *cp == ',' || *cp == '=' || 2688 *cp == '\r' || *cp == '\n')) 2689 return 0; 2690 while (isspace((int)(*(np-1)))) 2691 np--; 2692 *np = '\0'; 2693 *vname = name; 2694 2695 /* 2696 * Check if we hit the end of the buffer or a ','. If so we are done. 2697 */ 2698 if (cp == cpend || *cp == ',' || *cp == '\r' || *cp == '\n') { 2699 if (cp != cpend) 2700 cp++; 2701 *datap = cp; 2702 *datalen = cpend - cp; 2703 *vvalue = (char *)0; 2704 return 1; 2705 } 2706 2707 /* 2708 * So far, so good. Copy out the value 2709 */ 2710 cp++; /* past '=' */ 2711 while (cp < cpend && (isspace((int)*cp) && *cp != '\r' && *cp != '\n')) 2712 cp++; 2713 np = value; 2714 npend = &value[MAXVALLEN]; 2715 while (cp < cpend && np < npend && ((*cp != ',') || quoted)) 2716 { 2717 quoted ^= ((*np++ = *cp++) == '"'); 2718 } 2719 2720 /* 2721 * Check if we overran the value buffer while still in a quoted string 2722 * or without finding a comma 2723 */ 2724 if (np == npend && (quoted || *cp != ',')) 2725 return 0; 2726 /* 2727 * Trim off any trailing whitespace 2728 */ 2729 while (np > value && isspace((int)(*(np-1)))) 2730 np--; 2731 *np = '\0'; 2732 2733 /* 2734 * Return this. All done. 2735 */ 2736 if (cp != cpend) 2737 cp++; 2738 *datap = cp; 2739 *datalen = cpend - cp; 2740 *vvalue = value; 2741 return 1; 2742 } 2743 2744 2745 /* 2746 * findvar - see if this variable is known to us 2747 */ 2748 int 2749 findvar( 2750 char *varname, 2751 struct ctl_var *varlist 2752 ) 2753 { 2754 register char *np; 2755 register struct ctl_var *vl; 2756 2757 vl = varlist; 2758 np = varname; 2759 while (vl->fmt != EOV) { 2760 if (vl->fmt != PADDING && STREQ(np, vl->text)) 2761 return vl->code; 2762 vl++; 2763 } 2764 return 0; 2765 } 2766 2767 2768 2769 /* 2770 * printvars - print variables returned in response packet 2771 */ 2772 void 2773 printvars( 2774 int length, 2775 char *data, 2776 int status, 2777 int sttype, 2778 FILE *fp 2779 ) 2780 { 2781 if (rawmode) 2782 rawprint(sttype, length, data, status, fp); 2783 else 2784 cookedprint(sttype, length, data, status, fp); 2785 } 2786 2787 2788 /* 2789 * rawprint - do a printout of the data in raw mode 2790 */ 2791 static void 2792 rawprint( 2793 int datatype, 2794 int length, 2795 char *data, 2796 int status, 2797 FILE *fp 2798 ) 2799 { 2800 register char *cp; 2801 register char *cpend; 2802 2803 /* 2804 * Essentially print the data as is. We reformat unprintables, though. 2805 */ 2806 cp = data; 2807 cpend = data + length; 2808 2809 (void) fprintf(fp, "status=0x%04x,\n", status); 2810 2811 while (cp < cpend) { 2812 if (*cp == '\r') { 2813 /* 2814 * If this is a \r and the next character is a 2815 * \n, supress this, else pretty print it. Otherwise 2816 * just output the character. 2817 */ 2818 if (cp == (cpend-1) || *(cp+1) != '\n') 2819 makeascii(1, cp, fp); 2820 } else if (isspace((int)*cp) || isprint((int)*cp)) { 2821 putc(*cp, fp); 2822 } else { 2823 makeascii(1, cp, fp); 2824 } 2825 cp++; 2826 } 2827 } 2828 2829 2830 /* 2831 * Global data used by the cooked output routines 2832 */ 2833 int out_chars; /* number of characters output */ 2834 int out_linecount; /* number of characters output on this line */ 2835 2836 2837 /* 2838 * startoutput - get ready to do cooked output 2839 */ 2840 static void 2841 startoutput(void) 2842 { 2843 out_chars = 0; 2844 out_linecount = 0; 2845 } 2846 2847 2848 /* 2849 * output - output a variable=value combination 2850 */ 2851 static void 2852 output( 2853 FILE *fp, 2854 char *name, 2855 char *value 2856 ) 2857 { 2858 int lenname; 2859 int lenvalue; 2860 2861 lenname = strlen(name); 2862 lenvalue = strlen(value); 2863 2864 if (out_chars != 0) { 2865 putc(',', fp); 2866 out_chars++; 2867 out_linecount++; 2868 if ((out_linecount + lenname + lenvalue + 3) > MAXOUTLINE) { 2869 putc('\n', fp); 2870 out_chars++; 2871 out_linecount = 0; 2872 } else { 2873 putc(' ', fp); 2874 out_chars++; 2875 out_linecount++; 2876 } 2877 } 2878 2879 fputs(name, fp); 2880 putc('=', fp); 2881 fputs(value, fp); 2882 out_chars += lenname + 1 + lenvalue; 2883 out_linecount += lenname + 1 + lenvalue; 2884 } 2885 2886 2887 /* 2888 * endoutput - terminate a block of cooked output 2889 */ 2890 static void 2891 endoutput( 2892 FILE *fp 2893 ) 2894 { 2895 if (out_chars != 0) 2896 putc('\n', fp); 2897 } 2898 2899 2900 /* 2901 * outputarr - output an array of values 2902 */ 2903 static void 2904 outputarr( 2905 FILE *fp, 2906 char *name, 2907 int narr, 2908 l_fp *lfp 2909 ) 2910 { 2911 register char *bp; 2912 register char *cp; 2913 register int i; 2914 register int len; 2915 char buf[256]; 2916 2917 bp = buf; 2918 /* 2919 * Hack to align delay and offset values 2920 */ 2921 for (i = (int)strlen(name); i < 11; i++) 2922 *bp++ = ' '; 2923 2924 for (i = narr; i > 0; i--) { 2925 if (i != narr) 2926 *bp++ = ' '; 2927 cp = lfptoms(lfp, 2); 2928 len = strlen(cp); 2929 if (len > 7) { 2930 cp[7] = '\0'; 2931 len = 7; 2932 } 2933 while (len < 7) { 2934 *bp++ = ' '; 2935 len++; 2936 } 2937 while (*cp != '\0') 2938 *bp++ = *cp++; 2939 lfp++; 2940 } 2941 *bp = '\0'; 2942 output(fp, name, buf); 2943 } 2944 2945 static char * 2946 tstflags( 2947 u_long val 2948 ) 2949 { 2950 register char *cb, *s; 2951 register int i; 2952 register const char *sep; 2953 2954 sep = ""; 2955 i = 0; 2956 s = cb = &circ_buf[nextcb][0]; 2957 if (++nextcb >= NUMCB) 2958 nextcb = 0; 2959 2960 sprintf(cb, "%02lx", val); 2961 cb += strlen(cb); 2962 if (!val) { 2963 strcat(cb, " ok"); 2964 cb += strlen(cb); 2965 } else { 2966 *cb++ = ' '; 2967 for (i = 0; i < 11; i++) { 2968 if (val & 0x1) { 2969 sprintf(cb, "%s%s", sep, tstflagnames[i]); 2970 sep = ", "; 2971 cb += strlen(cb); 2972 } 2973 val >>= 1; 2974 } 2975 } 2976 *cb = '\0'; 2977 return s; 2978 } 2979 2980 /* 2981 * cookedprint - output variables in cooked mode 2982 */ 2983 static void 2984 cookedprint( 2985 int datatype, 2986 int length, 2987 char *data, 2988 int status, 2989 FILE *fp 2990 ) 2991 { 2992 register int varid; 2993 char *name; 2994 char *value; 2995 char output_raw; 2996 int fmt; 2997 struct ctl_var *varlist; 2998 l_fp lfp; 2999 long ival; 3000 struct sockaddr_storage hval; 3001 u_long uval; 3002 l_fp lfparr[8]; 3003 int narr; 3004 3005 switch (datatype) { 3006 case TYPE_PEER: 3007 varlist = peer_var; 3008 break; 3009 case TYPE_SYS: 3010 varlist = sys_var; 3011 break; 3012 case TYPE_CLOCK: 3013 varlist = clock_var; 3014 break; 3015 default: 3016 (void) fprintf(stderr, "Unknown datatype(0x%x) in cookedprint\n", datatype); 3017 return; 3018 } 3019 3020 (void) fprintf(fp, "status=%04x %s,\n", status, 3021 statustoa(datatype, status)); 3022 3023 startoutput(); 3024 while (nextvar(&length, &data, &name, &value)) { 3025 varid = findvar(name, varlist); 3026 if (varid == 0) { 3027 output_raw = '*'; 3028 } else { 3029 output_raw = 0; 3030 fmt = varlist[varid].fmt; 3031 switch(fmt) { 3032 case TS: 3033 if (!decodets(value, &lfp)) 3034 output_raw = '?'; 3035 else 3036 output(fp, name, prettydate(&lfp)); 3037 break; 3038 case FL: 3039 case FU: 3040 case FS: 3041 if (!decodetime(value, &lfp)) 3042 output_raw = '?'; 3043 else { 3044 switch (fmt) { 3045 case FL: 3046 output(fp, name, 3047 lfptoms(&lfp, 3)); 3048 break; 3049 case FU: 3050 output(fp, name, 3051 ulfptoms(&lfp, 3)); 3052 break; 3053 case FS: 3054 output(fp, name, 3055 lfptoms(&lfp, 3)); 3056 break; 3057 } 3058 } 3059 break; 3060 3061 case UI: 3062 if (!decodeuint(value, &uval)) 3063 output_raw = '?'; 3064 else 3065 output(fp, name, uinttoa(uval)); 3066 break; 3067 3068 case SI: 3069 if (!decodeint(value, &ival)) 3070 output_raw = '?'; 3071 else 3072 output(fp, name, inttoa(ival)); 3073 break; 3074 3075 case HA: 3076 case NA: 3077 if (!decodenetnum(value, &hval)) 3078 output_raw = '?'; 3079 else if (fmt == HA){ 3080 output(fp, name, nntohost(&hval)); 3081 } else { 3082 output(fp, name, stoa(&hval)); 3083 } 3084 break; 3085 3086 case ST: 3087 output_raw = '*'; 3088 break; 3089 3090 case RF: 3091 if (decodenetnum(value, &hval)) { 3092 if ((hval.ss_family == AF_INET) && 3093 ISREFCLOCKADR(&hval)) 3094 output(fp, name, 3095 refnumtoa(&hval)); 3096 else 3097 output(fp, name, stoa(&hval)); 3098 } else if ((int)strlen(value) <= 4) 3099 output(fp, name, value); 3100 else 3101 output_raw = '?'; 3102 break; 3103 3104 case LP: 3105 if (!decodeuint(value, &uval) || uval > 3) 3106 output_raw = '?'; 3107 else { 3108 char b[3]; 3109 b[0] = b[1] = '0'; 3110 if (uval & 0x2) 3111 b[0] = '1'; 3112 if (uval & 0x1) 3113 b[1] = '1'; 3114 b[2] = '\0'; 3115 output(fp, name, b); 3116 } 3117 break; 3118 3119 case OC: 3120 if (!decodeuint(value, &uval)) 3121 output_raw = '?'; 3122 else { 3123 char b[10]; 3124 3125 (void) sprintf(b, "%03lo", uval); 3126 output(fp, name, b); 3127 } 3128 break; 3129 3130 case MD: 3131 if (!decodeuint(value, &uval)) 3132 output_raw = '?'; 3133 else 3134 output(fp, name, uinttoa(uval)); 3135 break; 3136 3137 case AR: 3138 if (!decodearr(value, &narr, lfparr)) 3139 output_raw = '?'; 3140 else 3141 outputarr(fp, name, narr, lfparr); 3142 break; 3143 3144 case FX: 3145 if (!decodeuint(value, &uval)) 3146 output_raw = '?'; 3147 else 3148 output(fp, name, tstflags(uval)); 3149 break; 3150 3151 default: 3152 (void) fprintf(stderr, 3153 "Internal error in cookedprint, %s=%s, fmt %d\n", 3154 name, value, fmt); 3155 break; 3156 } 3157 3158 } 3159 if (output_raw != 0) { 3160 char bn[401]; 3161 char bv[401]; 3162 int len; 3163 3164 atoascii(400, name, bn); 3165 atoascii(400, value, bv); 3166 if (output_raw != '*') { 3167 len = strlen(bv); 3168 bv[len] = output_raw; 3169 bv[len+1] = '\0'; 3170 } 3171 output(fp, bn, bv); 3172 } 3173 } 3174 endoutput(fp); 3175 } 3176 3177 3178 /* 3179 * sortassoc - sort associations in the cache into ascending order 3180 */ 3181 void 3182 sortassoc(void) 3183 { 3184 if (numassoc > 1) 3185 qsort( 3186 #ifdef QSORT_USES_VOID_P 3187 (void *) 3188 #else 3189 (char *) 3190 #endif 3191 assoc_cache, (size_t)numassoc, 3192 sizeof(struct association), assoccmp); 3193 } 3194 3195 3196 /* 3197 * assoccmp - compare two associations 3198 */ 3199 #ifdef QSORT_USES_VOID_P 3200 static int 3201 assoccmp( 3202 const void *t1, 3203 const void *t2 3204 ) 3205 { 3206 const struct association *ass1 = (const struct association *)t1; 3207 const struct association *ass2 = (const struct association *)t2; 3208 3209 if (ass1->assid < ass2->assid) 3210 return -1; 3211 if (ass1->assid > ass2->assid) 3212 return 1; 3213 return 0; 3214 } 3215 #else 3216 static int 3217 assoccmp( 3218 struct association *ass1, 3219 struct association *ass2 3220 ) 3221 { 3222 if (ass1->assid < ass2->assid) 3223 return -1; 3224 if (ass1->assid > ass2->assid) 3225 return 1; 3226 return 0; 3227 } 3228 #endif /* not QSORT_USES_VOID_P */ 3229