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