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