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