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