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