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