]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/ntp/ntpq/ntpq-subs.c
MFC r338126: MFV r338092: ntp 4.2.8p12.
[FreeBSD/stable/10.git] / contrib / ntp / ntpq / ntpq-subs.c
1 /*
2  * ntpq-subs.c - subroutines which are called to perform ntpq commands.
3  */
4 #include <config.h>
5 #include <stdio.h>
6 #include <ctype.h>
7 #include <sys/types.h>
8 #include <sys/time.h>
9
10 #include "ntpq.h"
11 #include "ntpq-opts.h"
12
13 extern char     currenthost[];
14 extern int      currenthostisnum;
15 size_t          maxhostlen;
16
17 /*
18  * Declarations for command handlers in here
19  */
20 static  associd_t checkassocid  (u_int32);
21 static  struct varlist *findlistvar (struct varlist *, char *);
22 static  void    doaddvlist      (struct varlist *, const char *);
23 static  void    dormvlist       (struct varlist *, const char *);
24 static  void    doclearvlist    (struct varlist *);
25 static  void    makequerydata   (struct varlist *, size_t *, char *);
26 static  int     doquerylist     (struct varlist *, int, associd_t, int,
27                                  u_short *, size_t *, const char **);
28 static  void    doprintvlist    (struct varlist *, FILE *);
29 static  void    addvars         (struct parse *, FILE *);
30 static  void    rmvars          (struct parse *, FILE *);
31 static  void    clearvars       (struct parse *, FILE *);
32 static  void    showvars        (struct parse *, FILE *);
33 static  int     dolist          (struct varlist *, associd_t, int, int,
34                                  FILE *);
35 static  void    readlist        (struct parse *, FILE *);
36 static  void    writelist       (struct parse *, FILE *);
37 static  void    readvar         (struct parse *, FILE *);
38 static  void    writevar        (struct parse *, FILE *);
39 static  void    clocklist       (struct parse *, FILE *);
40 static  void    clockvar        (struct parse *, FILE *);
41 static  int     findassidrange  (u_int32, u_int32, int *, int *,
42                                  FILE *);
43 static  void    mreadlist       (struct parse *, FILE *);
44 static  void    mreadvar        (struct parse *, FILE *);
45 static  void    printassoc      (int, FILE *);
46 static  void    associations    (struct parse *, FILE *);
47 static  void    lassociations   (struct parse *, FILE *);
48 static  void    passociations   (struct parse *, FILE *);
49 static  void    lpassociations  (struct parse *, FILE *);
50
51 #ifdef  UNUSED
52 static  void    radiostatus (struct parse *, FILE *);
53 #endif  /* UNUSED */
54
55 static  void    authinfo        (struct parse *, FILE *);
56 static  void    pstats          (struct parse *, FILE *);
57 static  long    when            (l_fp *, l_fp *, l_fp *);
58 static  char *  prettyinterval  (char *, size_t, long);
59 static  int     doprintpeers    (struct varlist *, int, int, size_t, const char *, FILE *, int);
60 static  int     dogetpeers      (struct varlist *, associd_t, FILE *, int);
61 static  void    dopeers         (int, FILE *, int);
62 static  void    peers           (struct parse *, FILE *);
63 static  void    doapeers        (int, FILE *, int);
64 static  void    apeers          (struct parse *, FILE *);
65 static  void    lpeers          (struct parse *, FILE *);
66 static  void    doopeers        (int, FILE *, int);
67 static  void    opeers          (struct parse *, FILE *);
68 static  void    lopeers         (struct parse *, FILE *);
69 static  void    config          (struct parse *, FILE *);
70 static  void    saveconfig      (struct parse *, FILE *);
71 static  void    config_from_file(struct parse *, FILE *);
72 static  void    mrulist         (struct parse *, FILE *);
73 static  void    ifstats         (struct parse *, FILE *);
74 static  void    reslist         (struct parse *, FILE *);
75 static  void    sysstats        (struct parse *, FILE *);
76 static  void    sysinfo         (struct parse *, FILE *);
77 static  void    kerninfo        (struct parse *, FILE *);
78 static  void    monstats        (struct parse *, FILE *);
79 static  void    iostats         (struct parse *, FILE *);
80 static  void    timerstats      (struct parse *, FILE *);
81
82 /*
83  * Commands we understand.      Ntpdc imports this.
84  */
85 struct xcmd opcmds[] = {
86         { "saveconfig", saveconfig, { NTP_STR, NO, NO, NO },
87                 { "filename", "", "", ""}, 
88                 "save ntpd configuration to file, . for current config file"},
89         { "associations", associations, {  NO, NO, NO, NO },
90           { "", "", "", "" },
91           "print list of association ID's and statuses for the server's peers" },
92         { "passociations", passociations,   {  NO, NO, NO, NO },
93           { "", "", "", "" },
94           "print list of associations returned by last associations command" },
95         { "lassociations", lassociations,   {  NO, NO, NO, NO },
96           { "", "", "", "" },
97           "print list of associations including all client information" },
98         { "lpassociations", lpassociations, {  NO, NO, NO, NO },
99           { "", "", "", "" },
100           "print last obtained list of associations, including client information" },
101         { "addvars",    addvars,    { NTP_STR, NO, NO, NO },
102           { "name[=value][,...]", "", "", "" },
103           "add variables to the variable list or change their values" },
104         { "rmvars", rmvars,     { NTP_STR, NO, NO, NO },
105           { "name[,...]", "", "", "" },
106           "remove variables from the variable list" },
107         { "clearvars",  clearvars,  { NO, NO, NO, NO },
108           { "", "", "", "" },
109           "remove all variables from the variable list" },
110         { "showvars",   showvars,   { NO, NO, NO, NO },
111           { "", "", "", "" },
112           "print variables on the variable list" },
113         { "readlist",   readlist,   { OPT|NTP_UINT, NO, NO, NO },
114           { "assocID", "", "", "" },
115           "read the system or peer variables included in the variable list" },
116         { "rl",     readlist,   { OPT|NTP_UINT, NO, NO, NO },
117           { "assocID", "", "", "" },
118           "read the system or peer variables included in the variable list" },
119         { "writelist",  writelist,  { OPT|NTP_UINT, NO, NO, NO },
120           { "assocID", "", "", "" },
121           "write the system or peer variables included in the variable list" },
122         { "readvar", readvar,    { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
123           { "assocID", "varname1", "varname2", "varname3" },
124           "read system or peer variables" },
125         { "rv",      readvar,    { OPT|NTP_UINT, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, },
126           { "assocID", "varname1", "varname2", "varname3" },
127           "read system or peer variables" },
128         { "writevar",   writevar,   { NTP_UINT, NTP_STR, NO, NO },
129           { "assocID", "name=value,[...]", "", "" },
130           "write system or peer variables" },
131         { "mreadlist",  mreadlist,  { NTP_UINT, NTP_UINT, NO, NO },
132           { "assocIDlow", "assocIDhigh", "", "" },
133           "read the peer variables in the variable list for multiple peers" },
134         { "mrl",    mreadlist,  { NTP_UINT, NTP_UINT, NO, NO },
135           { "assocIDlow", "assocIDhigh", "", "" },
136           "read the peer variables in the variable list for multiple peers" },
137         { "mreadvar",   mreadvar,   { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
138           { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
139           "read peer variables from multiple peers" },
140         { "mrv",    mreadvar,   { NTP_UINT, NTP_UINT, OPT|NTP_STR, NO },
141           { "assocIDlow", "assocIDhigh", "name=value[,...]", "" },
142           "read peer variables from multiple peers" },
143         { "clocklist",  clocklist,  { OPT|NTP_UINT, NO, NO, NO },
144           { "assocID", "", "", "" },
145           "read the clock variables included in the variable list" },
146         { "cl",     clocklist,  { OPT|NTP_UINT, NO, NO, NO },
147           { "assocID", "", "", "" },
148           "read the clock variables included in the variable list" },
149         { "clockvar",   clockvar,   { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
150           { "assocID", "name=value[,...]", "", "" },
151           "read clock variables" },
152         { "cv",     clockvar,   { OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
153           { "assocID", "name=value[,...]", "", "" },
154           "read clock variables" },
155         { "pstats",    pstats,    { NTP_UINT, NO, NO, NO },
156           { "assocID", "", "", "" },
157           "show statistics for a peer" },
158         { "peers",  peers,      { OPT|IP_VERSION, NO, NO, NO },
159           { "-4|-6", "", "", "" },
160           "obtain and print a list of the server's peers [IP version]" },
161         { "apeers",  apeers,      { OPT|IP_VERSION, NO, NO, NO },
162           { "-4|-6", "", "", "" },
163           "obtain and print a list of the server's peers and their assocIDs [IP version]" },
164         { "lpeers", lpeers,     { OPT|IP_VERSION, NO, NO, NO },
165           { "-4|-6", "", "", "" },
166           "obtain and print a list of all peers and clients [IP version]" },
167         { "opeers", opeers,     { OPT|IP_VERSION, NO, NO, NO },
168           { "-4|-6", "", "", "" },
169           "print peer list the old way, with dstadr shown rather than refid [IP version]" },
170         { "lopeers", lopeers,   { OPT|IP_VERSION, NO, NO, NO },
171           { "-4|-6", "", "", "" },
172           "obtain and print a list of all peers and clients showing dstadr [IP version]" },
173         { ":config", config,   { NTP_STR, NO, NO, NO },
174           { "<configuration command line>", "", "", "" },
175           "send a remote configuration command to ntpd" },
176         { "config-from-file", config_from_file, { NTP_STR, NO, NO, NO },
177           { "<configuration filename>", "", "", "" },
178           "configure ntpd using the configuration filename" },
179         { "mrulist", mrulist, { OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
180           { "tag=value", "tag=value", "tag=value", "tag=value" },
181           "display the list of most recently seen source addresses, tags mincount=... resall=0x... resany=0x..." },
182         { "ifstats", ifstats, { NO, NO, NO, NO },
183           { "", "", "", "" },
184           "show statistics for each local address ntpd is using" },
185         { "reslist", reslist, { NO, NO, NO, NO },
186           { "", "", "", "" },
187           "show ntpd access control list" },
188         { "sysinfo", sysinfo, { NO, NO, NO, NO },
189           { "", "", "", "" },
190           "display system summary" },
191         { "kerninfo", kerninfo, { NO, NO, NO, NO },
192           { "", "", "", "" },
193           "display kernel loop and PPS statistics" },
194         { "sysstats", sysstats, { NO, NO, NO, NO },
195           { "", "", "", "" },
196           "display system uptime and packet counts" },
197         { "monstats", monstats, { NO, NO, NO, NO },
198           { "", "", "", "" },
199           "display monitor (mrulist) counters and limits" },
200         { "authinfo", authinfo, { NO, NO, NO, NO },
201           { "", "", "", "" },
202           "display symmetric authentication counters" },
203         { "iostats", iostats, { NO, NO, NO, NO },
204           { "", "", "", "" },
205           "display network input and output counters" },
206         { "timerstats", timerstats, { NO, NO, NO, NO },
207           { "", "", "", "" },
208           "display interval timer counters" },
209         { 0,            0,              { NO, NO, NO, NO },
210           { "-4|-6", "", "", "" }, "" }
211 };
212
213
214 /*
215  * Variable list data space
216  */
217 #define MAXLINE         512     /* maximum length of a line */
218 #define MAXLIST         128     /* maximum variables in list */
219 #define LENHOSTNAME     256     /* host name limit */
220
221 #define MRU_GOT_COUNT   0x1
222 #define MRU_GOT_LAST    0x2
223 #define MRU_GOT_FIRST   0x4
224 #define MRU_GOT_MV      0x8
225 #define MRU_GOT_RS      0x10
226 #define MRU_GOT_ADDR    0x20
227 #define MRU_GOT_ALL     (MRU_GOT_COUNT | MRU_GOT_LAST | MRU_GOT_FIRST \
228                          | MRU_GOT_MV | MRU_GOT_RS | MRU_GOT_ADDR)
229
230 /*
231  * mrulist() depends on MRUSORT_DEF and MRUSORT_RDEF being the first two
232  */
233 typedef enum mru_sort_order_tag {
234         MRUSORT_DEF = 0,        /* lstint ascending */
235         MRUSORT_R_DEF,          /* lstint descending */
236         MRUSORT_AVGINT,         /* avgint ascending */
237         MRUSORT_R_AVGINT,       /* avgint descending */
238         MRUSORT_ADDR,           /* IPv4 asc. then IPv6 asc. */
239         MRUSORT_R_ADDR,         /* IPv6 desc. then IPv4 desc. */
240         MRUSORT_COUNT,          /* hit count ascending */
241         MRUSORT_R_COUNT,        /* hit count descending */
242         MRUSORT_MAX,            /* special: count of this enum */
243 } mru_sort_order;
244
245 const char * const mru_sort_keywords[MRUSORT_MAX] = {
246         "lstint",               /* MRUSORT_DEF */
247         "-lstint",              /* MRUSORT_R_DEF */
248         "avgint",               /* MRUSORT_AVGINT */
249         "-avgint",              /* MRUSORT_R_AVGINT */
250         "addr",                 /* MRUSORT_ADDR */
251         "-addr",                /* MRUSORT_R_ADDR */
252         "count",                /* MRUSORT_COUNT */
253         "-count",               /* MRUSORT_R_COUNT */
254 };
255
256 typedef int (*qsort_cmp)(const void *, const void *);
257
258 /*
259  * Old CTL_PST defines for version 2.
260  */
261 #define OLD_CTL_PST_CONFIG              0x80
262 #define OLD_CTL_PST_AUTHENABLE          0x40
263 #define OLD_CTL_PST_AUTHENTIC           0x20
264 #define OLD_CTL_PST_REACH               0x10
265 #define OLD_CTL_PST_SANE                0x08
266 #define OLD_CTL_PST_DISP                0x04
267
268 #define OLD_CTL_PST_SEL_REJECT          0
269 #define OLD_CTL_PST_SEL_SELCAND         1
270 #define OLD_CTL_PST_SEL_SYNCCAND        2
271 #define OLD_CTL_PST_SEL_SYSPEER         3
272
273 char flash2[] = " .+*    "; /* flash decode for version 2 */
274 char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
275
276 struct varlist {
277         const char *name;
278         char *value;
279 } g_varlist[MAXLIST] = { { 0, 0 } };
280
281 /*
282  * Imported from ntpq.c
283  */
284 extern int showhostnames;
285 extern int wideremote;
286 extern int rawmode;
287 extern struct servent *server_entry;
288 extern struct association *assoc_cache;
289 extern u_char pktversion;
290
291 typedef struct mru_tag mru;
292 struct mru_tag {
293         mru *           hlink;  /* next in hash table bucket */
294         DECL_DLIST_LINK(mru, mlink);
295         int             count;
296         l_fp            last;
297         l_fp            first;
298         u_char          mode;
299         u_char          ver;
300         u_short         rs;
301         sockaddr_u      addr;
302 };
303
304 typedef struct ifstats_row_tag {
305         u_int           ifnum;
306         sockaddr_u      addr;
307         sockaddr_u      bcast;
308         int             enabled;
309         u_int           flags;
310         u_int           mcast_count;
311         char            name[32];
312         u_int           peer_count;
313         u_int           received;
314         u_int           sent;
315         u_int           send_errors;
316         u_int           ttl;
317         u_int           uptime;
318 } ifstats_row;
319
320 typedef struct reslist_row_tag {
321         u_int           idx;
322         sockaddr_u      addr;
323         sockaddr_u      mask;
324         u_long          hits;
325         char            flagstr[128];
326 } reslist_row;
327
328 typedef struct var_display_collection_tag {
329         const char * const tag;         /* system variable */
330         const char * const display;     /* descriptive text */
331         u_char type;                    /* NTP_STR, etc */
332         union {
333                 char *          str;
334                 sockaddr_u      sau;    /* NTP_ADD */
335                 l_fp            lfp;    /* NTP_LFP */
336         } v;                            /* retrieved value */
337 } vdc;
338 #if !defined(MISSING_C99_STRUCT_INIT)
339 # define VDC_INIT(a, b, c) { .tag = a, .display = b, .type = c }
340 #else
341 # define VDC_INIT(a, b, c) { a, b, c }
342 #endif
343 /*
344  * other local function prototypes
345  */
346 static int      mrulist_ctrl_c_hook(void);
347 static mru *    add_mru(mru *);
348 static int      collect_mru_list(const char *, l_fp *);
349 static int      fetch_nonce(char *, size_t);
350 static int      qcmp_mru_avgint(const void *, const void *);
351 static int      qcmp_mru_r_avgint(const void *, const void *);
352 static int      qcmp_mru_addr(const void *, const void *);
353 static int      qcmp_mru_r_addr(const void *, const void *);
354 static int      qcmp_mru_count(const void *, const void *);
355 static int      qcmp_mru_r_count(const void *, const void *);
356 static void     validate_ifnum(FILE *, u_int, int *, ifstats_row *);
357 static void     another_ifstats_field(int *, ifstats_row *, FILE *);
358 static void     collect_display_vdc(associd_t as, vdc *table,
359                                     int decodestatus, FILE *fp);
360
361 /*
362  * static globals
363  */
364 static u_int    mru_count;
365 static u_int    mru_dupes;
366 volatile int    mrulist_interrupted;
367 static mru      mru_list;               /* listhead */
368 static mru **   hash_table;
369
370 /*
371  * qsort comparison function table for mrulist().  The first two
372  * entries are NULL because they are handled without qsort().
373  */
374 static const qsort_cmp mru_qcmp_table[MRUSORT_MAX] = {
375         NULL,                   /* MRUSORT_DEF unused */
376         NULL,                   /* MRUSORT_R_DEF unused */
377         &qcmp_mru_avgint,       /* MRUSORT_AVGINT */
378         &qcmp_mru_r_avgint,     /* MRUSORT_R_AVGINT */
379         &qcmp_mru_addr,         /* MRUSORT_ADDR */
380         &qcmp_mru_r_addr,       /* MRUSORT_R_ADDR */
381         &qcmp_mru_count,        /* MRUSORT_COUNT */
382         &qcmp_mru_r_count,      /* MRUSORT_R_COUNT */
383 };
384
385 /*
386  * checkassocid - return the association ID, checking to see if it is valid
387  */
388 static associd_t
389 checkassocid(
390         u_int32 value
391         )
392 {
393         associd_t       associd;
394         u_long          ulvalue;
395
396         associd = (associd_t)value;
397         if (0 == associd || value != associd) {
398                 ulvalue = value;
399                 fprintf(stderr,
400                         "***Invalid association ID %lu specified\n",
401                         ulvalue);
402                 return 0;
403         }
404
405         return associd;
406 }
407
408
409 /*
410  * findlistvar - Look for the named variable in a varlist.  If found,
411  *               return a pointer to it.  Otherwise, if the list has
412  *               slots available, return the pointer to the first free
413  *               slot, or NULL if it's full.
414  */
415 static struct varlist *
416 findlistvar(
417         struct varlist *list,
418         char *name
419         )
420 {
421         struct varlist *vl;
422
423         for (vl = list; vl < list + MAXLIST && vl->name != NULL; vl++)
424                 if (!strcmp(name, vl->name))
425                         return vl;
426         if (vl < list + MAXLIST)
427                 return vl;
428
429         return NULL;
430 }
431
432
433 /*
434  * doaddvlist - add variable(s) to the variable list
435  */
436 static void
437 doaddvlist(
438         struct varlist *vlist,
439         const char *vars
440         )
441 {
442         struct varlist *vl;
443         size_t len;
444         char *name;
445         char *value;
446
447         len = strlen(vars);
448         while (nextvar(&len, &vars, &name, &value)) {
449                 INSIST(name && value);
450                 vl = findlistvar(vlist, name);
451                 if (NULL == vl) {
452                         fprintf(stderr, "Variable list full\n");
453                         return;
454                 }
455
456                 if (NULL == vl->name) {
457                         vl->name = estrdup(name);
458                 } else if (vl->value != NULL) {
459                         free(vl->value);
460                         vl->value = NULL;
461                 }
462
463                 if (value != NULL)
464                         vl->value = estrdup(value);
465         }
466 }
467
468
469 /*
470  * dormvlist - remove variable(s) from the variable list
471  */
472 static void
473 dormvlist(
474         struct varlist *vlist,
475         const char *vars
476         )
477 {
478         struct varlist *vl;
479         size_t len;
480         char *name;
481         char *value;
482
483         len = strlen(vars);
484         while (nextvar(&len, &vars, &name, &value)) {
485                 INSIST(name && value);
486                 vl = findlistvar(vlist, name);
487                 if (vl == 0 || vl->name == 0) {
488                         (void) fprintf(stderr, "Variable `%s' not found\n",
489                                        name);
490                 } else {
491                         free((void *)(intptr_t)vl->name);
492                         if (vl->value != 0)
493                             free(vl->value);
494                         for ( ; (vl+1) < (g_varlist + MAXLIST)
495                                       && (vl+1)->name != 0; vl++) {
496                                 vl->name = (vl+1)->name;
497                                 vl->value = (vl+1)->value;
498                         }
499                         vl->name = vl->value = 0;
500                 }
501         }
502 }
503
504
505 /*
506  * doclearvlist - clear a variable list
507  */
508 static void
509 doclearvlist(
510         struct varlist *vlist
511         )
512 {
513         register struct varlist *vl;
514
515         for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
516                 free((void *)(intptr_t)vl->name);
517                 vl->name = 0;
518                 if (vl->value != 0) {
519                         free(vl->value);
520                         vl->value = 0;
521                 }
522         }
523 }
524
525
526 /*
527  * makequerydata - form a data buffer to be included with a query
528  */
529 static void
530 makequerydata(
531         struct varlist *vlist,
532         size_t *datalen,
533         char *data
534         )
535 {
536         register struct varlist *vl;
537         register char *cp, *cpend;
538         register size_t namelen, valuelen;
539         register size_t totallen;
540
541         cp = data;
542         cpend = data + *datalen;
543
544         for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
545                 namelen = strlen(vl->name);
546                 if (vl->value == 0)
547                         valuelen = 0;
548                 else
549                         valuelen = strlen(vl->value);
550                 totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
551                 if (cp + totallen > cpend) {
552                     fprintf(stderr, 
553                             "***Ignoring variables starting with `%s'\n",
554                             vl->name);
555                     break;
556                 }
557
558                 if (cp != data)
559                         *cp++ = ',';
560                 memcpy(cp, vl->name, (size_t)namelen);
561                 cp += namelen;
562                 if (valuelen != 0) {
563                         *cp++ = '=';
564                         memcpy(cp, vl->value, (size_t)valuelen);
565                         cp += valuelen;
566                 }
567         }
568         *datalen = (size_t)(cp - data);
569 }
570
571
572 /*
573  * doquerylist - send a message including variables in a list
574  */
575 static int
576 doquerylist(
577         struct varlist *vlist,
578         int op,
579         associd_t associd,
580         int auth,
581         u_short *rstatus,
582         size_t *dsize,
583         const char **datap
584         )
585 {
586         char data[CTL_MAX_DATA_LEN];
587         size_t datalen;
588
589         datalen = sizeof(data);
590         makequerydata(vlist, &datalen, data);
591
592         return doquery(op, associd, auth, datalen, data, rstatus, dsize,
593                        datap);
594 }
595
596
597 /*
598  * doprintvlist - print the variables on a list
599  */
600 static void
601 doprintvlist(
602         struct varlist *vlist,
603         FILE *fp
604         )
605 {
606         size_t n;
607
608         if (NULL == vlist->name) {
609                 fprintf(fp, "No variables on list\n");
610                 return;
611         }
612         for (n = 0; n < MAXLIST && vlist[n].name != NULL; n++) {
613                 if (NULL == vlist[n].value)
614                         fprintf(fp, "%s\n", vlist[n].name);
615                 else
616                         fprintf(fp, "%s=%s\n", vlist[n].name,
617                                 vlist[n].value);
618         }
619 }
620
621 /*
622  * addvars - add variables to the variable list
623  */
624 /*ARGSUSED*/
625 static void
626 addvars(
627         struct parse *pcmd,
628         FILE *fp
629         )
630 {
631         doaddvlist(g_varlist, pcmd->argval[0].string);
632 }
633
634
635 /*
636  * rmvars - remove variables from the variable list
637  */
638 /*ARGSUSED*/
639 static void
640 rmvars(
641         struct parse *pcmd,
642         FILE *fp
643         )
644 {
645         dormvlist(g_varlist, pcmd->argval[0].string);
646 }
647
648
649 /*
650  * clearvars - clear the variable list
651  */
652 /*ARGSUSED*/
653 static void
654 clearvars(
655         struct parse *pcmd,
656         FILE *fp
657         )
658 {
659         doclearvlist(g_varlist);
660 }
661
662
663 /*
664  * showvars - show variables on the variable list
665  */
666 /*ARGSUSED*/
667 static void
668 showvars(
669         struct parse *pcmd,
670         FILE *fp
671         )
672 {
673         doprintvlist(g_varlist, fp);
674 }
675
676
677 /*
678  * dolist - send a request with the given list of variables
679  */
680 static int
681 dolist(
682         struct varlist *vlist,
683         associd_t associd,
684         int op,
685         int type,
686         FILE *fp
687         )
688 {
689         const char *datap;
690         int res;
691         size_t dsize;
692         u_short rstatus;
693         int quiet;
694
695         /*
696          * if we're asking for specific variables don't include the
697          * status header line in the output.
698          */
699         if (old_rv)
700                 quiet = 0;
701         else
702                 quiet = (vlist->name != NULL);
703
704         res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
705
706         if (res != 0)
707                 return 0;
708
709         if (numhosts > 1)
710                 fprintf(fp, "server=%s ", currenthost);
711         if (dsize == 0) {
712                 if (associd == 0)
713                         fprintf(fp, "No system%s variables returned\n",
714                                 (type == TYPE_CLOCK) ? " clock" : "");
715                 else
716                         fprintf(fp,
717                                 "No information returned for%s association %u\n",
718                                 (type == TYPE_CLOCK) ? " clock" : "",
719                                 associd);
720                 return 1;
721         }
722
723         if (!quiet)
724                 fprintf(fp, "associd=%u ", associd);
725         printvars(dsize, datap, (int)rstatus, type, quiet, fp);
726         return 1;
727 }
728
729
730 /*
731  * readlist - send a read variables request with the variables on the list
732  */
733 static void
734 readlist(
735         struct parse *pcmd,
736         FILE *fp
737         )
738 {
739         associd_t       associd;
740         int             type;
741
742         if (pcmd->nargs == 0) {
743                 associd = 0;
744         } else {
745           /* HMS: I think we want the u_int32 target here, not the u_long */
746                 if (pcmd->argval[0].uval == 0)
747                         associd = 0;
748                 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
749                         return;
750         }
751
752         type = (0 == associd)
753                    ? TYPE_SYS
754                    : TYPE_PEER;
755         dolist(g_varlist, associd, CTL_OP_READVAR, type, fp);
756 }
757
758
759 /*
760  * writelist - send a write variables request with the variables on the list
761  */
762 static void
763 writelist(
764         struct parse *pcmd,
765         FILE *fp
766         )
767 {
768         const char *datap;
769         int res;
770         associd_t associd;
771         size_t dsize;
772         u_short rstatus;
773
774         if (pcmd->nargs == 0) {
775                 associd = 0;
776         } else {
777                 /* HMS: Do we really want uval here? */
778                 if (pcmd->argval[0].uval == 0)
779                         associd = 0;
780                 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
781                         return;
782         }
783
784         res = doquerylist(g_varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
785                           &dsize, &datap);
786
787         if (res != 0)
788                 return;
789
790         if (numhosts > 1)
791                 (void) fprintf(fp, "server=%s ", currenthost);
792         if (dsize == 0)
793                 (void) fprintf(fp, "done! (no data returned)\n");
794         else {
795                 (void) fprintf(fp,"associd=%u ", associd);
796                 printvars(dsize, datap, (int)rstatus,
797                           (associd != 0) ? TYPE_PEER : TYPE_SYS, 0, fp);
798         }
799         return;
800 }
801
802
803 /*
804  * readvar - send a read variables request with the specified variables
805  */
806 static void
807 readvar(
808         struct parse *pcmd,
809         FILE *fp
810         )
811 {
812         associd_t       associd;
813         size_t          tmpcount;
814         size_t          u;
815         int             type;
816         struct varlist  tmplist[MAXLIST];
817
818
819         /* HMS: uval? */
820         if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
821                 associd = 0;
822         else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
823                 return;
824
825         ZERO(tmplist);
826         if (pcmd->nargs > 1) {
827                 tmpcount = pcmd->nargs - 1;
828                 for (u = 0; u < tmpcount; u++)
829                         doaddvlist(tmplist, pcmd->argval[1 + u].string);
830         }
831
832         type = (0 == associd)
833                    ? TYPE_SYS
834                    : TYPE_PEER;
835         dolist(tmplist, associd, CTL_OP_READVAR, type, fp);
836
837         doclearvlist(tmplist);
838 }
839
840
841 /*
842  * writevar - send a write variables request with the specified variables
843  */
844 static void
845 writevar(
846         struct parse *pcmd,
847         FILE *fp
848         )
849 {
850         const char *datap;
851         int res;
852         associd_t associd;
853         int type;
854         size_t dsize;
855         u_short rstatus;
856         struct varlist tmplist[MAXLIST];
857
858         /* HMS: uval? */
859         if (pcmd->argval[0].uval == 0)
860                 associd = 0;
861         else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
862                 return;
863
864         ZERO(tmplist);
865         doaddvlist(tmplist, pcmd->argval[1].string);
866
867         res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
868                           &dsize, &datap);
869
870         doclearvlist(tmplist);
871
872         if (res != 0)
873                 return;
874
875         if (numhosts > 1)
876                 fprintf(fp, "server=%s ", currenthost);
877         if (dsize == 0)
878                 fprintf(fp, "done! (no data returned)\n");
879         else {
880                 fprintf(fp,"associd=%u ", associd);
881                 type = (0 == associd)
882                            ? TYPE_SYS
883                            : TYPE_PEER;
884                 printvars(dsize, datap, (int)rstatus, type, 0, fp);
885         }
886         return;
887 }
888
889
890 /*
891  * clocklist - send a clock variables request with the variables on the list
892  */
893 static void
894 clocklist(
895         struct parse *pcmd,
896         FILE *fp
897         )
898 {
899         associd_t associd;
900
901         /* HMS: uval? */
902         if (pcmd->nargs == 0) {
903                 associd = 0;
904         } else {
905                 if (pcmd->argval[0].uval == 0)
906                         associd = 0;
907                 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
908                         return;
909         }
910
911         dolist(g_varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
912 }
913
914
915 /*
916  * clockvar - send a clock variables request with the specified variables
917  */
918 static void
919 clockvar(
920         struct parse *pcmd,
921         FILE *fp
922         )
923 {
924         associd_t associd;
925         struct varlist tmplist[MAXLIST];
926
927         /* HMS: uval? */
928         if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
929                 associd = 0;
930         else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
931                 return;
932
933         ZERO(tmplist);
934         if (pcmd->nargs >= 2)
935                 doaddvlist(tmplist, pcmd->argval[1].string);
936
937         dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
938
939         doclearvlist(tmplist);
940 }
941
942
943 /*
944  * findassidrange - verify a range of association ID's
945  */
946 static int
947 findassidrange(
948         u_int32 assid1,
949         u_int32 assid2,
950         int *   from,
951         int *   to,
952         FILE *  fp
953         )
954 {
955         associd_t       assids[2];
956         int             ind[COUNTOF(assids)];
957         u_int           i;
958         size_t          a;
959
960
961         if (0 == numassoc)
962                 dogetassoc(fp);
963
964         assids[0] = checkassocid(assid1);
965         if (0 == assids[0])
966                 return 0;
967         assids[1] = checkassocid(assid2);
968         if (0 == assids[1])
969                 return 0;
970
971         for (a = 0; a < COUNTOF(assids); a++) {
972                 ind[a] = -1;
973                 for (i = 0; i < numassoc; i++)
974                         if (assoc_cache[i].assid == assids[a])
975                                 ind[a] = i;
976         }
977         for (a = 0; a < COUNTOF(assids); a++)
978                 if (-1 == ind[a]) {
979                         fprintf(stderr,
980                                 "***Association ID %u not found in list\n",
981                                 assids[a]);
982                         return 0;
983                 }
984
985         if (ind[0] < ind[1]) {
986                 *from = ind[0];
987                 *to = ind[1];
988         } else {
989                 *to = ind[0];
990                 *from = ind[1];
991         }
992         return 1;
993 }
994
995
996
997 /*
998  * mreadlist - send a read variables request for multiple associations
999  */
1000 static void
1001 mreadlist(
1002         struct parse *pcmd,
1003         FILE *fp
1004         )
1005 {
1006         int i;
1007         int from;
1008         int to;
1009
1010         if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1011                             &from, &to, fp))
1012                 return;
1013
1014         for (i = from; i <= to; i++) {
1015                 if (i != from)
1016                         fprintf(fp, "\n");
1017                 if (!dolist(g_varlist, assoc_cache[i].assid,
1018                             CTL_OP_READVAR, TYPE_PEER, fp))
1019                         return;
1020         }
1021         return;
1022 }
1023
1024
1025 /*
1026  * mreadvar - send a read variables request for multiple associations
1027  */
1028 static void
1029 mreadvar(
1030         struct parse *pcmd,
1031         FILE *fp
1032         )
1033 {
1034         int i;
1035         int from;
1036         int to;
1037         struct varlist tmplist[MAXLIST];
1038         struct varlist *pvars;
1039
1040         if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
1041                                 &from, &to, fp))
1042                 return;
1043
1044         ZERO(tmplist);
1045         if (pcmd->nargs >= 3) {
1046                 doaddvlist(tmplist, pcmd->argval[2].string);
1047                 pvars = tmplist;
1048         } else {
1049                 pvars = g_varlist;
1050         }
1051
1052         for (i = from; i <= to; i++) {
1053                 if (!dolist(pvars, assoc_cache[i].assid, CTL_OP_READVAR,
1054                             TYPE_PEER, fp))
1055                         break;
1056         }
1057
1058         if (pvars == tmplist)
1059                 doclearvlist(tmplist);
1060
1061         return;
1062 }
1063
1064
1065 /*
1066  * dogetassoc - query the host for its list of associations
1067  */
1068 int
1069 dogetassoc(
1070         FILE *fp
1071         )
1072 {
1073         const char *datap;
1074         const u_short *pus;
1075         int res;
1076         size_t dsize;
1077         u_short rstatus;
1078
1079         res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
1080                           &dsize, &datap);
1081
1082         if (res != 0)
1083                 return 0;
1084
1085         if (dsize == 0) {
1086                 if (numhosts > 1)
1087                         fprintf(fp, "server=%s ", currenthost);
1088                 fprintf(fp, "No association ID's returned\n");
1089                 return 0;
1090         }
1091
1092         if (dsize & 0x3) {
1093                 if (numhosts > 1)
1094                         fprintf(stderr, "server=%s ", currenthost);
1095                 fprintf(stderr,
1096                         "***Server returned %zu octets, should be multiple of 4\n",
1097                         dsize);
1098                 return 0;
1099         }
1100
1101         numassoc = 0;
1102
1103         while (dsize > 0) {
1104                 if (numassoc >= assoc_cache_slots) {
1105                         grow_assoc_cache();
1106                 }
1107                 pus = (const void *)datap;
1108                 assoc_cache[numassoc].assid = ntohs(*pus);
1109                 datap += sizeof(*pus);
1110                 pus = (const void *)datap;
1111                 assoc_cache[numassoc].status = ntohs(*pus);
1112                 datap += sizeof(*pus);
1113                 dsize -= 2 * sizeof(*pus);
1114                 if (debug) {
1115                         fprintf(stderr, "[%u] ",
1116                                 assoc_cache[numassoc].assid);
1117                 }
1118                 numassoc++;
1119         }
1120         if (debug) {
1121                 fprintf(stderr, "\n%d associations total\n", numassoc);
1122         }
1123         sortassoc();
1124         return 1;
1125 }
1126
1127
1128 /*
1129  * printassoc - print the current list of associations
1130  */
1131 static void
1132 printassoc(
1133         int showall,
1134         FILE *fp
1135         )
1136 {
1137         register char *bp;
1138         u_int i;
1139         u_char statval;
1140         int event;
1141         u_long event_count;
1142         const char *conf;
1143         const char *reach;
1144         const char *auth;
1145         const char *condition = "";
1146         const char *last_event;
1147         char buf[128];
1148
1149         if (numassoc == 0) {
1150                 (void) fprintf(fp, "No association ID's in list\n");
1151                 return;
1152         }
1153
1154         /*
1155          * Output a header
1156          */
1157         (void) fprintf(fp,
1158                            "ind assid status  conf reach auth condition  last_event cnt\n");
1159         (void) fprintf(fp,
1160                            "===========================================================\n");
1161         for (i = 0; i < numassoc; i++) {
1162                 statval = (u_char) CTL_PEER_STATVAL(assoc_cache[i].status);
1163                 if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
1164                         continue;
1165                 event = CTL_PEER_EVENT(assoc_cache[i].status);
1166                 event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
1167                 if (statval & CTL_PST_CONFIG)
1168                         conf = "yes";
1169                 else
1170                         conf = "no";
1171                 if (statval & CTL_PST_BCAST) {
1172                         reach = "none";
1173                         if (statval & CTL_PST_AUTHENABLE)
1174                                 auth = "yes";
1175                         else
1176                                 auth = "none";
1177                 } else {
1178                         if (statval & CTL_PST_REACH)
1179                                 reach = "yes";
1180                         else
1181                                 reach = "no";
1182                         if (statval & CTL_PST_AUTHENABLE) {
1183                                 if (statval & CTL_PST_AUTHENTIC)
1184                                         auth = "ok ";
1185                                 else
1186                                         auth = "bad";
1187                         } else {
1188                                 auth = "none";
1189                         }
1190                 }
1191                 if (pktversion > NTP_OLDVERSION) {
1192                         switch (statval & 0x7) {
1193
1194                         case CTL_PST_SEL_REJECT:
1195                                 condition = "reject";
1196                                 break;
1197
1198                         case CTL_PST_SEL_SANE:
1199                                 condition = "falsetick";
1200                                 break;
1201
1202                         case CTL_PST_SEL_CORRECT:
1203                                 condition = "excess";
1204                                 break;
1205
1206                         case CTL_PST_SEL_SELCAND:
1207                                 condition = "outlier";
1208                                 break;
1209
1210                         case CTL_PST_SEL_SYNCCAND:
1211                                 condition = "candidate";
1212                                 break;
1213
1214                         case CTL_PST_SEL_EXCESS:
1215                                 condition = "backup";
1216                                 break;
1217
1218                         case CTL_PST_SEL_SYSPEER:
1219                                 condition = "sys.peer";
1220                                 break;
1221
1222                         case CTL_PST_SEL_PPS:
1223                                 condition = "pps.peer";
1224                                 break;
1225                         }
1226                 } else {
1227                         switch (statval & 0x3) {
1228
1229                         case OLD_CTL_PST_SEL_REJECT:
1230                                 if (!(statval & OLD_CTL_PST_SANE))
1231                                         condition = "insane";
1232                                 else if (!(statval & OLD_CTL_PST_DISP))
1233                                         condition = "hi_disp";
1234                                 else
1235                                         condition = "";
1236                                 break;
1237
1238                         case OLD_CTL_PST_SEL_SELCAND:
1239                                 condition = "sel_cand";
1240                                 break;
1241
1242                         case OLD_CTL_PST_SEL_SYNCCAND:
1243                                 condition = "sync_cand";
1244                                 break;
1245
1246                         case OLD_CTL_PST_SEL_SYSPEER:
1247                                 condition = "sys_peer";
1248                                 break;
1249                         }
1250                 }
1251                 switch (PEER_EVENT|event) {
1252
1253                 case PEVNT_MOBIL:
1254                         last_event = "mobilize";
1255                         break;
1256
1257                 case PEVNT_DEMOBIL:
1258                         last_event = "demobilize";
1259                         break;
1260
1261                 case PEVNT_REACH:
1262                         last_event = "reachable";
1263                         break;
1264
1265                 case PEVNT_UNREACH:
1266                         last_event = "unreachable";
1267                         break;
1268
1269                 case PEVNT_RESTART:
1270                         last_event = "restart";
1271                         break;
1272
1273                 case PEVNT_REPLY:
1274                         last_event = "no_reply";
1275                         break;
1276
1277                 case PEVNT_RATE:
1278                         last_event = "rate_exceeded";
1279                         break;
1280
1281                 case PEVNT_DENY:
1282                         last_event = "access_denied";
1283                         break;
1284
1285                 case PEVNT_ARMED:
1286                         last_event = "leap_armed";
1287                         break;
1288
1289                 case PEVNT_NEWPEER:
1290                         last_event = "sys_peer";
1291                         break;
1292
1293                 case PEVNT_CLOCK:
1294                         last_event = "clock_alarm";
1295                         break;
1296
1297                 default:
1298                         last_event = "";
1299                         break;
1300                 }
1301                 snprintf(buf, sizeof(buf),
1302                          "%3d %5u  %04x   %3.3s  %4s  %4.4s %9.9s %11s %2lu",
1303                          i + 1, assoc_cache[i].assid,
1304                          assoc_cache[i].status, conf, reach, auth,
1305                          condition, last_event, event_count);
1306                 bp = buf + strlen(buf);
1307                 while (bp > buf && ' ' == bp[-1])
1308                         --bp;
1309                 bp[0] = '\0';
1310                 fprintf(fp, "%s\n", buf);
1311         }
1312 }
1313
1314
1315 /*
1316  * associations - get, record and print a list of associations
1317  */
1318 /*ARGSUSED*/
1319 static void
1320 associations(
1321         struct parse *pcmd,
1322         FILE *fp
1323         )
1324 {
1325         if (dogetassoc(fp))
1326                 printassoc(0, fp);
1327 }
1328
1329
1330 /*
1331  * lassociations - get, record and print a long list of associations
1332  */
1333 /*ARGSUSED*/
1334 static void
1335 lassociations(
1336         struct parse *pcmd,
1337         FILE *fp
1338         )
1339 {
1340         if (dogetassoc(fp))
1341                 printassoc(1, fp);
1342 }
1343
1344
1345 /*
1346  * passociations - print the association list
1347  */
1348 /*ARGSUSED*/
1349 static void
1350 passociations(
1351         struct parse *pcmd,
1352         FILE *fp
1353         )
1354 {
1355         printassoc(0, fp);
1356 }
1357
1358
1359 /*
1360  * lpassociations - print the long association list
1361  */
1362 /*ARGSUSED*/
1363 static void
1364 lpassociations(
1365         struct parse *pcmd,
1366         FILE *fp
1367         )
1368 {
1369         printassoc(1, fp);
1370 }
1371
1372
1373 /*
1374  *  saveconfig - dump ntp server configuration to server file
1375  */
1376 static void
1377 saveconfig(
1378         struct parse *pcmd,
1379         FILE *fp
1380         )
1381 {
1382         const char *datap;
1383         int res;
1384         size_t dsize;
1385         u_short rstatus;
1386
1387         if (0 == pcmd->nargs)
1388                 return;
1389         
1390         res = doquery(CTL_OP_SAVECONFIG, 0, 1,
1391                       strlen(pcmd->argval[0].string),
1392                       pcmd->argval[0].string, &rstatus, &dsize,
1393                       &datap);
1394
1395         if (res != 0)
1396                 return;
1397
1398         if (0 == dsize)
1399                 fprintf(fp, "(no response message, curiously)");
1400         else
1401                 fprintf(fp, "%.*s", (int)dsize, datap); /* cast is wobbly */
1402 }
1403
1404
1405 #ifdef  UNUSED
1406 /*
1407  * radiostatus - print the radio status returned by the server
1408  */
1409 /*ARGSUSED*/
1410 static void
1411 radiostatus(
1412         struct parse *pcmd,
1413         FILE *fp
1414         )
1415 {
1416         char *datap;
1417         int res;
1418         int dsize;
1419         u_short rstatus;
1420
1421         res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
1422                           &dsize, &datap);
1423
1424         if (res != 0)
1425                 return;
1426
1427         if (numhosts > 1)
1428                 (void) fprintf(fp, "server=%s ", currenthost);
1429         if (dsize == 0) {
1430                 (void) fprintf(fp, "No radio status string returned\n");
1431                 return;
1432         }
1433
1434         asciize(dsize, datap, fp);
1435 }
1436 #endif  /* UNUSED */
1437
1438 /*
1439  * when - print how long its been since his last packet arrived
1440  */
1441 static long
1442 when(
1443         l_fp *ts,
1444         l_fp *rec,
1445         l_fp *reftime
1446         )
1447 {
1448         l_fp *lasttime;
1449
1450         if (rec->l_ui != 0)
1451                 lasttime = rec;
1452         else if (reftime->l_ui != 0)
1453                 lasttime = reftime;
1454         else
1455                 return 0;
1456
1457         if (ts->l_ui < lasttime->l_ui)
1458                 return -1;
1459         return (ts->l_ui - lasttime->l_ui);
1460 }
1461
1462
1463 /*
1464  * Pretty-print an interval into the given buffer, in a human-friendly format.
1465  */
1466 static char *
1467 prettyinterval(
1468         char *buf,
1469         size_t cb,
1470         long diff
1471         )
1472 {
1473         if (diff <= 0) {
1474                 buf[0] = '-';
1475                 buf[1] = 0;
1476                 return buf;
1477         }
1478
1479         if (diff <= 2048) {
1480                 snprintf(buf, cb, "%u", (unsigned int)diff);
1481                 return buf;
1482         }
1483
1484         diff = (diff + 29) / 60;
1485         if (diff <= 300) {
1486                 snprintf(buf, cb, "%um", (unsigned int)diff);
1487                 return buf;
1488         }
1489
1490         diff = (diff + 29) / 60;
1491         if (diff <= 96) {
1492                 snprintf(buf, cb, "%uh", (unsigned int)diff);
1493                 return buf;
1494         }
1495
1496         diff = (diff + 11) / 24;
1497         if (diff <= 999) {
1498                 snprintf(buf, cb, "%ud", (unsigned int)diff);
1499                 return buf;
1500         }
1501
1502         /* years are only approximated... */
1503         diff = (long)floor(diff / 365.25 + 0.5);
1504         if (diff <= 999) {
1505                 snprintf(buf, cb, "%uy", (unsigned int)diff);
1506                 return buf;
1507         }
1508         /* Ok, this amounts to infinity... */
1509         strlcpy(buf, "INF", cb);
1510         return buf;
1511 }
1512
1513 static char
1514 decodeaddrtype(
1515         sockaddr_u *sock
1516         )
1517 {
1518         char ch = '-';
1519         u_int32 dummy;
1520
1521         switch(AF(sock)) {
1522         case AF_INET:
1523                 dummy = SRCADR(sock);
1524                 ch = (char)(((dummy&0xf0000000)==0xe0000000) ? 'm' :
1525                         ((dummy&0x000000ff)==0x000000ff) ? 'b' :
1526                         ((dummy&0xffffffff)==0x7f000001) ? 'l' :
1527                         ((dummy&0xffffffe0)==0x00000000) ? '-' :
1528                         'u');
1529                 break;
1530         case AF_INET6:
1531                 if (IN6_IS_ADDR_MULTICAST(PSOCK_ADDR6(sock)))
1532                         ch = 'm';
1533                 else
1534                         ch = 'u';
1535                 break;
1536         default:
1537                 ch = '-';
1538                 break;
1539         }
1540         return ch;
1541 }
1542
1543 /*
1544  * A list of variables required by the peers command
1545  */
1546 struct varlist opeervarlist[] = {
1547         { "srcadr",     0 },    /* 0 */
1548         { "dstadr",     0 },    /* 1 */
1549         { "stratum",    0 },    /* 2 */
1550         { "hpoll",      0 },    /* 3 */
1551         { "ppoll",      0 },    /* 4 */
1552         { "reach",      0 },    /* 5 */
1553         { "delay",      0 },    /* 6 */
1554         { "offset",     0 },    /* 7 */
1555         { "jitter",     0 },    /* 8 */
1556         { "dispersion", 0 },    /* 9 */
1557         { "rec",        0 },    /* 10 */
1558         { "reftime",    0 },    /* 11 */
1559         { "srcport",    0 },    /* 12 */
1560         { "hmode",      0 },    /* 13 */
1561         { 0,            0 }
1562 };
1563
1564 struct varlist peervarlist[] = {
1565         { "srcadr",     0 },    /* 0 */
1566         { "refid",      0 },    /* 1 */
1567         { "stratum",    0 },    /* 2 */
1568         { "hpoll",      0 },    /* 3 */
1569         { "ppoll",      0 },    /* 4 */
1570         { "reach",      0 },    /* 5 */
1571         { "delay",      0 },    /* 6 */
1572         { "offset",     0 },    /* 7 */
1573         { "jitter",     0 },    /* 8 */
1574         { "dispersion", 0 },    /* 9 */
1575         { "rec",        0 },    /* 10 */
1576         { "reftime",    0 },    /* 11 */
1577         { "srcport",    0 },    /* 12 */
1578         { "hmode",      0 },    /* 13 */
1579         { "srchost",    0 },    /* 14 */
1580         { 0,            0 }
1581 };
1582
1583 struct varlist apeervarlist[] = {
1584         { "srcadr",     0 },    /* 0 */
1585         { "refid",      0 },    /* 1 */
1586         { "assid",      0 },    /* 2 */
1587         { "stratum",    0 },    /* 3 */
1588         { "hpoll",      0 },    /* 4 */
1589         { "ppoll",      0 },    /* 5 */
1590         { "reach",      0 },    /* 6 */
1591         { "delay",      0 },    /* 7 */
1592         { "offset",     0 },    /* 8 */
1593         { "jitter",     0 },    /* 9 */
1594         { "dispersion", 0 },    /* 10 */
1595         { "rec",        0 },    /* 11 */
1596         { "reftime",    0 },    /* 12 */
1597         { "srcport",    0 },    /* 13 */
1598         { "hmode",      0 },    /* 14 */
1599         { "srchost",    0 },    /* 15 */
1600         { 0,            0 }
1601 };
1602
1603
1604 /*
1605  * Decode an incoming data buffer and print a line in the peer list
1606  */
1607 static int
1608 doprintpeers(
1609         struct varlist *pvl,
1610         int associd,
1611         int rstatus,
1612         size_t datalen,
1613         const char *data,
1614         FILE *fp,
1615         int af
1616         )
1617 {
1618         char *name;
1619         char *value = NULL;
1620         int c;
1621         size_t len;
1622         int have_srchost;
1623         int have_dstadr;
1624         int have_da_rid;
1625         int have_jitter;
1626         sockaddr_u srcadr;
1627         sockaddr_u dstadr;
1628         sockaddr_u dum_store;
1629         sockaddr_u refidadr;
1630         long hmode = 0;
1631         u_long srcport = 0;
1632         u_int32 u32;
1633         const char *dstadr_refid = "0.0.0.0";
1634         const char *serverlocal;
1635         size_t drlen;
1636         u_long stratum = 0;
1637         long ppoll = 0;
1638         long hpoll = 0;
1639         u_long reach = 0;
1640         l_fp estoffset;
1641         l_fp estdelay;
1642         l_fp estjitter;
1643         l_fp estdisp;
1644         l_fp reftime;
1645         l_fp rec;
1646         l_fp ts;
1647         u_long poll_sec;
1648         u_long flash = 0;
1649         char type = '?';
1650         char clock_name[LENHOSTNAME];
1651         char whenbuf[12], pollbuf[12];
1652         /* [Bug 3482] formally whenbuf & pollbuf should be able to hold
1653          * a full signed int. Not that we would use that much string
1654          * data for it...
1655          */
1656         get_systime(&ts);
1657         
1658         have_srchost = FALSE;
1659         have_dstadr = FALSE;
1660         have_da_rid = FALSE;
1661         have_jitter = FALSE;
1662         ZERO_SOCK(&srcadr);
1663         ZERO_SOCK(&dstadr);
1664         clock_name[0] = '\0';
1665         ZERO(estoffset);
1666         ZERO(estdelay);
1667         ZERO(estjitter);
1668         ZERO(estdisp);
1669
1670         while (nextvar(&datalen, &data, &name, &value)) {
1671                 INSIST(name && value);
1672                 if (!strcmp("srcadr", name) ||
1673                     !strcmp("peeradr", name)) {
1674                         if (!decodenetnum(value, &srcadr))
1675                                 fprintf(stderr, "malformed %s=%s\n",
1676                                         name, value);
1677                 } else if (!strcmp("srchost", name)) {
1678                         if (pvl == peervarlist || pvl == apeervarlist) {
1679                                 len = strlen(value);
1680                                 if (2 < len &&
1681                                     (size_t)len < sizeof(clock_name)) {
1682                                         /* strip quotes */
1683                                         value++;
1684                                         len -= 2;
1685                                         memcpy(clock_name, value, len);
1686                                         clock_name[len] = '\0';
1687                                         have_srchost = TRUE;
1688                                 }
1689                         }
1690                 } else if (!strcmp("dstadr", name)) {
1691                         if (decodenetnum(value, &dum_store)) {
1692                                 type = decodeaddrtype(&dum_store);
1693                                 have_dstadr = TRUE;
1694                                 dstadr = dum_store;
1695                                 if (pvl == opeervarlist) {
1696                                         have_da_rid = TRUE;
1697                                         dstadr_refid = trunc_left(stoa(&dstadr), 15);
1698                                 }
1699                         }
1700                 } else if (!strcmp("hmode", name)) {
1701                         decodeint(value, &hmode);
1702                 } else if (!strcmp("refid", name)) {
1703                         if (   (pvl == peervarlist)
1704                             && (drefid == REFID_IPV4)) {
1705                                 have_da_rid = TRUE;
1706                                 drlen = strlen(value);
1707                                 if (0 == drlen) {
1708                                         dstadr_refid = "";
1709                                 } else if (drlen <= 4) {
1710                                         ZERO(u32);
1711                                         memcpy(&u32, value, drlen);
1712                                         dstadr_refid = refid_str(u32, 1);
1713                                 } else if (decodenetnum(value, &refidadr)) {
1714                                         if (SOCK_UNSPEC(&refidadr))
1715                                                 dstadr_refid = "0.0.0.0";
1716                                         else if (ISREFCLOCKADR(&refidadr))
1717                                                 dstadr_refid =
1718                                                     refnumtoa(&refidadr);
1719                                         else
1720                                                 dstadr_refid =
1721                                                     stoa(&refidadr);
1722                                 } else {
1723                                         have_da_rid = FALSE;
1724                                 }
1725                         } else if (   (pvl == apeervarlist)
1726                                    || (pvl == peervarlist)) {
1727                                 /* no need to check drefid == REFID_HASH */
1728                                 have_da_rid = TRUE;
1729                                 drlen = strlen(value);
1730                                 if (0 == drlen) {
1731                                         dstadr_refid = "";
1732                                 } else if (drlen <= 4) {
1733                                         ZERO(u32);
1734                                         memcpy(&u32, value, drlen);
1735                                         dstadr_refid = refid_str(u32, 1);
1736                                         //fprintf(stderr, "apeervarlist S1 refid: value=<%s>\n", value);
1737                                 } else if (decodenetnum(value, &refidadr)) {
1738                                         if (SOCK_UNSPEC(&refidadr))
1739                                                 dstadr_refid = "0.0.0.0";
1740                                         else if (ISREFCLOCKADR(&refidadr))
1741                                                 dstadr_refid =
1742                                                     refnumtoa(&refidadr);
1743                                         else {
1744                                                 char *buf = emalloc(10);
1745                                                 int i = ntohl(refidadr.sa4.sin_addr.s_addr);
1746
1747                                                 snprintf(buf, 10,
1748                                                         "%0x", i);
1749                                                 dstadr_refid = buf;
1750                                         //fprintf(stderr, "apeervarlist refid: value=<%x>\n", i);
1751                                         }
1752                                         //fprintf(stderr, "apeervarlist refid: value=<%s>\n", value);
1753                                 } else {
1754                                         have_da_rid = FALSE;
1755                                 }
1756                         }
1757                 } else if (!strcmp("stratum", name)) {
1758                         decodeuint(value, &stratum);
1759                 } else if (!strcmp("hpoll", name)) {
1760                         if (decodeint(value, &hpoll) && hpoll < 0)
1761                                 hpoll = NTP_MINPOLL;
1762                 } else if (!strcmp("ppoll", name)) {
1763                         if (decodeint(value, &ppoll) && ppoll < 0)
1764                                 ppoll = NTP_MINPOLL;
1765                 } else if (!strcmp("reach", name)) {
1766                         decodeuint(value, &reach);
1767                 } else if (!strcmp("delay", name)) {
1768                         decodetime(value, &estdelay);
1769                 } else if (!strcmp("offset", name)) {
1770                         decodetime(value, &estoffset);
1771                 } else if (!strcmp("jitter", name)) {
1772                         if ((pvl == peervarlist || pvl == apeervarlist)
1773                             && decodetime(value, &estjitter))
1774                                 have_jitter = 1;
1775                 } else if (!strcmp("rootdisp", name) ||
1776                            !strcmp("dispersion", name)) {
1777                         decodetime(value, &estdisp);
1778                 } else if (!strcmp("rec", name)) {
1779                         decodets(value, &rec);
1780                 } else if (!strcmp("srcport", name) ||
1781                            !strcmp("peerport", name)) {
1782                         decodeuint(value, &srcport);
1783                 } else if (!strcmp("reftime", name)) {
1784                         if (!decodets(value, &reftime))
1785                                 L_CLR(&reftime);
1786                 } else if (!strcmp("flash", name)) {
1787                     decodeuint(value, &flash);
1788                 } else {
1789                         // fprintf(stderr, "UNRECOGNIZED name=%s ", name);
1790                 }
1791         }
1792
1793         /*
1794          * hmode gives the best guidance for the t column.  If the response
1795          * did not include hmode we'll use the old decodeaddrtype() result.
1796          */
1797         switch (hmode) {
1798
1799         case MODE_BCLIENT:
1800                 /* broadcastclient or multicastclient */
1801                 type = 'b';
1802                 break;
1803
1804         case MODE_BROADCAST:
1805                 /* broadcast or multicast server */
1806                 if (IS_MCAST(&srcadr))
1807                         type = 'M';
1808                 else
1809                         type = 'B';
1810                 break;
1811
1812         case MODE_CLIENT:
1813                 if (ISREFCLOCKADR(&srcadr))
1814                         type = 'l';     /* local refclock*/
1815                 else if (SOCK_UNSPEC(&srcadr))
1816                         type = 'p';     /* pool */
1817                 else if (IS_MCAST(&srcadr))
1818                         type = 'a';     /* manycastclient */
1819                 else
1820                         type = 'u';     /* unicast */
1821                 break;
1822
1823         case MODE_ACTIVE:
1824                 type = 's';             /* symmetric active */
1825                 break;                  /* configured */
1826
1827         case MODE_PASSIVE:
1828                 type = 'S';             /* symmetric passive */
1829                 break;                  /* ephemeral */
1830         }
1831
1832         /*
1833          * Got everything, format the line
1834          */
1835         poll_sec = 1 << min(ppoll, hpoll);
1836         if (pktversion > NTP_OLDVERSION)
1837                 c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
1838         else
1839                 c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
1840         if (numhosts > 1) {
1841                 if ((pvl == peervarlist || pvl == apeervarlist)
1842                     && have_dstadr) {
1843                         serverlocal = nntohost_col(&dstadr,
1844                             (size_t)min(LIB_BUFLENGTH - 1, maxhostlen),
1845                             TRUE);
1846                 } else {
1847                         if (currenthostisnum)
1848                                 serverlocal = trunc_left(currenthost,
1849                                                          maxhostlen);
1850                         else
1851                                 serverlocal = currenthost;
1852                 }
1853                 fprintf(fp, "%-*s ", (int)maxhostlen, serverlocal);
1854         }
1855         if (AF_UNSPEC == af || AF(&srcadr) == af) {
1856                 if (!have_srchost)
1857                         strlcpy(clock_name, nntohost(&srcadr),
1858                                 sizeof(clock_name));
1859                 /* wide and long source - space over on next line */
1860                 /* allow for host + sp if > 1 and regular tally + source + sp */
1861                 if (wideremote && 15 < strlen(clock_name))
1862                         fprintf(fp, "%c%s\n%*s", c, clock_name,
1863                                 ((numhosts > 1) ? (int)maxhostlen + 1 : 0)
1864                                                         + 1 + 15 + 1, "");
1865                 else
1866                         fprintf(fp, "%c%-15.15s ", c, clock_name);
1867                 if ((flash & TEST12) && (pvl != opeervarlist)) {
1868                         drlen = fprintf(fp, "(loop)");
1869                 } else if (!have_da_rid) {
1870                         drlen = 0;
1871                 } else {
1872                         drlen = strlen(dstadr_refid);
1873                         makeascii(drlen, dstadr_refid, fp);
1874                 }
1875                 if (pvl == apeervarlist) {
1876                         while (drlen++ < 9)
1877                                 fputc(' ', fp);
1878                         fprintf(fp, "%-6d", associd);
1879                 } else {
1880                         while (drlen++ < 15)
1881                                 fputc(' ', fp);
1882                 }
1883                 fprintf(fp,
1884                         " %2ld %c %4.4s %4.4s  %3lo  %7.7s %8.7s %7.7s\n",
1885                         stratum, type,
1886                         prettyinterval(whenbuf, sizeof(whenbuf),
1887                                        when(&ts, &rec, &reftime)),
1888                         prettyinterval(pollbuf, sizeof(pollbuf), 
1889                                        (int)poll_sec),
1890                         reach, lfptoms(&estdelay, 3),
1891                         lfptoms(&estoffset, 3),
1892                         (have_jitter)
1893                             ? lfptoms(&estjitter, 3)
1894                             : lfptoms(&estdisp, 3));
1895                 return (1);
1896         }
1897         else
1898                 return(1);
1899 }
1900
1901
1902 /*
1903  * dogetpeers - given an association ID, read and print the spreadsheet
1904  *              peer variables.
1905  */
1906 static int
1907 dogetpeers(
1908         struct varlist *pvl,
1909         associd_t associd,
1910         FILE *fp,
1911         int af
1912         )
1913 {
1914         const char *datap;
1915         int res;
1916         size_t dsize;
1917         u_short rstatus;
1918
1919 #ifdef notdef
1920         res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
1921                           &dsize, &datap);
1922 #else
1923         /*
1924          * Damn fuzzballs
1925          */
1926         res = doquery(CTL_OP_READVAR, associd, 0, 0, NULL, &rstatus,
1927                           &dsize, &datap);
1928 #endif
1929
1930         if (res != 0)
1931                 return 0;
1932
1933         if (dsize == 0) {
1934                 if (numhosts > 1)
1935                         fprintf(stderr, "server=%s ", currenthost);
1936                 fprintf(stderr,
1937                         "***No information returned for association %u\n",
1938                         associd);
1939                 return 0;
1940         }
1941
1942         return doprintpeers(pvl, associd, (int)rstatus, dsize, datap,
1943                             fp, af);
1944 }
1945
1946
1947 /*
1948  * peers - print a peer spreadsheet
1949  */
1950 static void
1951 dopeers(
1952         int showall,
1953         FILE *fp,
1954         int af
1955         )
1956 {
1957         u_int           u;
1958         char            fullname[LENHOSTNAME];
1959         sockaddr_u      netnum;
1960         const char *    name_or_num;
1961         size_t          sl;
1962
1963         if (!dogetassoc(fp))
1964                 return;
1965
1966         for (u = 0; u < numhosts; u++) {
1967                 if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
1968                         name_or_num = nntohost(&netnum);
1969                         sl = strlen(name_or_num);
1970                         maxhostlen = max(maxhostlen, sl);
1971                 }
1972         }
1973         if (numhosts > 1)
1974                 fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
1975                         "server (local)");
1976         fprintf(fp,
1977                 "     remote           refid      st t when poll reach   delay   offset  jitter\n");
1978         if (numhosts > 1)
1979                 for (u = 0; u <= maxhostlen; u++)
1980                         fprintf(fp, "=");
1981         fprintf(fp,
1982                 "==============================================================================\n");
1983
1984         for (u = 0; u < numassoc; u++) {
1985                 if (!showall &&
1986                     !(CTL_PEER_STATVAL(assoc_cache[u].status)
1987                       & (CTL_PST_CONFIG|CTL_PST_REACH))) {
1988                         if (debug)
1989                                 fprintf(stderr, "eliding [%d]\n",
1990                                         (int)assoc_cache[u].assid);
1991                         continue;
1992                 }
1993                 if (!dogetpeers(peervarlist, (int)assoc_cache[u].assid,
1994                                 fp, af))
1995                         return;
1996         }
1997         return;
1998 }
1999
2000
2001 /*
2002  * doapeers - print a peer spreadsheet with assocIDs
2003  */
2004 static void
2005 doapeers(
2006         int showall,
2007         FILE *fp,
2008         int af
2009         )
2010 {
2011         u_int           u;
2012         char            fullname[LENHOSTNAME];
2013         sockaddr_u      netnum;
2014         const char *    name_or_num;
2015         size_t          sl;
2016
2017         if (!dogetassoc(fp))
2018                 return;
2019
2020         for (u = 0; u < numhosts; u++) {
2021                 if (getnetnum(chosts[u].name, &netnum, fullname, af)) {
2022                         name_or_num = nntohost(&netnum);
2023                         sl = strlen(name_or_num);
2024                         maxhostlen = max(maxhostlen, sl);
2025                 }
2026         }
2027         if (numhosts > 1)
2028                 fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2029                         "server (local)");
2030         fprintf(fp,
2031                 "     remote       refid   assid  st t when poll reach   delay   offset  jitter\n");
2032         if (numhosts > 1)
2033                 for (u = 0; u <= maxhostlen; u++)
2034                         fprintf(fp, "=");
2035         fprintf(fp,
2036                 "==============================================================================\n");
2037
2038         for (u = 0; u < numassoc; u++) {
2039                 if (!showall &&
2040                     !(CTL_PEER_STATVAL(assoc_cache[u].status)
2041                       & (CTL_PST_CONFIG|CTL_PST_REACH))) {
2042                         if (debug)
2043                                 fprintf(stderr, "eliding [%d]\n",
2044                                         (int)assoc_cache[u].assid);
2045                         continue;
2046                 }
2047                 if (!dogetpeers(apeervarlist, (int)assoc_cache[u].assid,
2048                                 fp, af))
2049                         return;
2050         }
2051         return;
2052 }
2053
2054
2055 /*
2056  * peers - print a peer spreadsheet
2057  */
2058 /*ARGSUSED*/
2059 static void
2060 peers(
2061         struct parse *pcmd,
2062         FILE *fp
2063         )
2064 {
2065         if (drefid == REFID_HASH) {
2066                 apeers(pcmd, fp);
2067         } else {
2068                 int af = 0;
2069
2070                 if (pcmd->nargs == 1) {
2071                         if (pcmd->argval->ival == 6)
2072                                 af = AF_INET6;
2073                         else
2074                                 af = AF_INET;
2075                 }
2076                 dopeers(0, fp, af);
2077         }
2078 }
2079
2080
2081 /*
2082  * apeers - print a peer spreadsheet, with assocIDs
2083  */
2084 /*ARGSUSED*/
2085 static void
2086 apeers(
2087         struct parse *pcmd,
2088         FILE *fp
2089         )
2090 {
2091         int af = 0;
2092
2093         if (pcmd->nargs == 1) {
2094                 if (pcmd->argval->ival == 6)
2095                         af = AF_INET6;
2096                 else
2097                         af = AF_INET;
2098         }
2099         doapeers(0, fp, af);
2100 }
2101
2102
2103 /*
2104  * lpeers - print a peer spreadsheet including all fuzzball peers
2105  */
2106 /*ARGSUSED*/
2107 static void
2108 lpeers(
2109         struct parse *pcmd,
2110         FILE *fp
2111         )
2112 {
2113         int af = 0;
2114
2115         if (pcmd->nargs == 1) {
2116                 if (pcmd->argval->ival == 6)
2117                         af = AF_INET6;
2118                 else
2119                         af = AF_INET;
2120         }
2121         dopeers(1, fp, af);
2122 }
2123
2124
2125 /*
2126  * opeers - print a peer spreadsheet
2127  */
2128 static void
2129 doopeers(
2130         int showall,
2131         FILE *fp,
2132         int af
2133         )
2134 {
2135         u_int i;
2136         char fullname[LENHOSTNAME];
2137         sockaddr_u netnum;
2138
2139         if (!dogetassoc(fp))
2140                 return;
2141
2142         for (i = 0; i < numhosts; ++i) {
2143                 if (getnetnum(chosts[i].name, &netnum, fullname, af))
2144                         if (strlen(fullname) > maxhostlen)
2145                                 maxhostlen = strlen(fullname);
2146         }
2147         if (numhosts > 1)
2148                 fprintf(fp, "%-*.*s ", (int)maxhostlen, (int)maxhostlen,
2149                         "server");
2150         fprintf(fp,
2151             "     remote           local      st t when poll reach   delay   offset    disp\n");
2152         if (numhosts > 1)
2153                 for (i = 0; i <= maxhostlen; ++i)
2154                         fprintf(fp, "=");
2155         fprintf(fp,
2156             "==============================================================================\n");
2157
2158         for (i = 0; i < numassoc; i++) {
2159                 if (!showall &&
2160                     !(CTL_PEER_STATVAL(assoc_cache[i].status) &
2161                       (CTL_PST_CONFIG | CTL_PST_REACH)))
2162                         continue;
2163                 if (!dogetpeers(opeervarlist, assoc_cache[i].assid, fp, af))
2164                         return;
2165         }
2166         return;
2167 }
2168
2169
2170 /*
2171  * opeers - print a peer spreadsheet the old way
2172  */
2173 /*ARGSUSED*/
2174 static void
2175 opeers(
2176         struct parse *pcmd,
2177         FILE *fp
2178         )
2179 {
2180         int af = 0;
2181
2182         if (pcmd->nargs == 1) {
2183                 if (pcmd->argval->ival == 6)
2184                         af = AF_INET6;
2185                 else
2186                         af = AF_INET;
2187         }
2188         doopeers(0, fp, af);
2189 }
2190
2191
2192 /*
2193  * lopeers - print a peer spreadsheet including all fuzzball peers
2194  */
2195 /*ARGSUSED*/
2196 static void
2197 lopeers(
2198         struct parse *pcmd,
2199         FILE *fp
2200         )
2201 {
2202         int af = 0;
2203
2204         if (pcmd->nargs == 1) {
2205                 if (pcmd->argval->ival == 6)
2206                         af = AF_INET6;
2207                 else
2208                         af = AF_INET;
2209         }
2210         doopeers(1, fp, af);
2211 }
2212
2213
2214 /* 
2215  * config - send a configuration command to a remote host
2216  */
2217 static void 
2218 config (
2219         struct parse *pcmd,
2220         FILE *fp
2221         )
2222 {
2223         const char *cfgcmd;
2224         u_short rstatus;
2225         size_t rsize;
2226         const char *rdata;
2227         char *resp;
2228         int res;
2229         int col;
2230         int i;
2231
2232         cfgcmd = pcmd->argval[0].string;
2233
2234         if (debug > 2)
2235                 fprintf(stderr, 
2236                         "In Config\n"
2237                         "Keyword = %s\n"
2238                         "Command = %s\n", pcmd->keyword, cfgcmd);
2239
2240         res = doquery(CTL_OP_CONFIGURE, 0, 1,
2241                       strlen(cfgcmd), cfgcmd,
2242                       &rstatus, &rsize, &rdata);
2243
2244         if (res != 0)
2245                 return;
2246
2247         if (rsize > 0 && '\n' == rdata[rsize - 1])
2248                 rsize--;
2249
2250         resp = emalloc(rsize + 1);
2251         memcpy(resp, rdata, rsize);
2252         resp[rsize] = '\0';
2253
2254         col = -1;
2255         if (1 == sscanf(resp, "column %d syntax error", &col)
2256             && col >= 0 && (size_t)col <= strlen(cfgcmd) + 1) {
2257                 if (interactive)
2258                         fputs("             *", stdout); /* "ntpq> :config " */
2259                 else
2260                         printf("%s\n", cfgcmd);
2261                 for (i = 0; i < col; i++)
2262                         fputc('_', stdout);
2263                 fputs("^\n", stdout);
2264         }
2265         printf("%s\n", resp);
2266         free(resp);
2267 }
2268
2269
2270 /* 
2271  * config_from_file - remotely configure an ntpd daemon using the
2272  * specified configuration file
2273  * SK: This function is a kludge at best and is full of bad design
2274  * bugs:
2275  * 1. ntpq uses UDP, which means that there is no guarantee of in-order,
2276  *    error-free delivery. 
2277  * 2. The maximum length of a packet is constrained, and as a result, the
2278  *    maximum length of a line in a configuration file is constrained. 
2279  *    Longer lines will lead to unpredictable results.
2280  * 3. Since this function is sending a line at a time, we can't update
2281  *    the control key through the configuration file (YUCK!!)
2282  *
2283  * Pearly: There are a few places where 'size_t' is cast to 'int' based
2284  * on the assumption that 'int' can hold the size of the involved
2285  * buffers without overflow.
2286  */
2287 static void 
2288 config_from_file (
2289         struct parse *pcmd,
2290         FILE *fp
2291         )
2292 {
2293         u_short rstatus;
2294         size_t rsize;
2295         const char *rdata;
2296         char * cp;
2297         int res;
2298         FILE *config_fd;
2299         char config_cmd[MAXLINE];
2300         size_t config_len;
2301         int i;
2302         int retry_limit;
2303
2304         if (debug > 2)
2305                 fprintf(stderr,
2306                         "In Config\n"
2307                         "Keyword = %s\n"
2308                         "Filename = %s\n", pcmd->keyword,
2309                         pcmd->argval[0].string);
2310
2311         config_fd = fopen(pcmd->argval[0].string, "r");
2312         if (NULL == config_fd) {
2313                 printf("ERROR!! Couldn't open file: %s\n",
2314                        pcmd->argval[0].string);
2315                 return;
2316         }
2317
2318         printf("Sending configuration file, one line at a time.\n");
2319         i = 0;
2320         while (fgets(config_cmd, MAXLINE, config_fd) != NULL) {
2321                 /* Eliminate comments first. */
2322                 cp = strchr(config_cmd, '#');
2323                 config_len = (NULL != cp)
2324                     ? (size_t)(cp - config_cmd)
2325                     : strlen(config_cmd);
2326                 
2327                 /* [Bug 3015] make sure there's no trailing whitespace;
2328                  * the fix for [Bug 2853] on the server side forbids
2329                  * those. And don't transmit empty lines, as this would
2330                  * just be waste.
2331                  */
2332                 while (config_len != 0 &&
2333                        (u_char)config_cmd[config_len-1] <= ' ')
2334                         --config_len;
2335                 config_cmd[config_len] = '\0';
2336
2337                 ++i;
2338                 if (0 == config_len)
2339                         continue;
2340
2341                 retry_limit = 2;
2342                 do 
2343                         res = doquery(CTL_OP_CONFIGURE, 0, 1,
2344                                       config_len, config_cmd,
2345                                       &rstatus, &rsize, &rdata);
2346                 while (res != 0 && retry_limit--);
2347                 if (res != 0) {
2348                         printf("Line No: %d query failed: %.*s\n"
2349                                "Subsequent lines not sent.\n",
2350                                i, (int)config_len, config_cmd);
2351                         fclose(config_fd);
2352                         return;
2353                 }
2354
2355                 /* Right-strip the result code string, then output the
2356                  * last line executed, with result code. */
2357                 while (rsize != 0 && (u_char)rdata[rsize - 1] <= ' ')
2358                         --rsize;
2359                 printf("Line No: %d %.*s: %.*s\n", i,
2360                        (int)rsize, rdata,
2361                        (int)config_len, config_cmd);
2362         }
2363         printf("Done sending file\n");
2364         fclose(config_fd);
2365 }
2366
2367
2368 static int
2369 fetch_nonce(
2370         char *  nonce,
2371         size_t  cb_nonce
2372         )
2373 {
2374         const char      nonce_eq[] = "nonce=";
2375         int             qres;
2376         u_short         rstatus;
2377         size_t          rsize;
2378         const char *    rdata;
2379         size_t          chars;
2380
2381         /*
2382          * Retrieve a nonce specific to this client to demonstrate to
2383          * ntpd that we're capable of receiving responses to our source
2384          * IP address, and thereby unlikely to be forging the source.
2385          */
2386         qres = doquery(CTL_OP_REQ_NONCE, 0, 0, 0, NULL, &rstatus,
2387                        &rsize, &rdata);
2388         if (qres) {
2389                 fprintf(stderr, "nonce request failed\n");
2390                 return FALSE;
2391         }
2392
2393         if ((size_t)rsize <= sizeof(nonce_eq) - 1 ||
2394             strncmp(rdata, nonce_eq, sizeof(nonce_eq) - 1)) {
2395                 fprintf(stderr, "unexpected nonce response format: %.*s\n",
2396                         (int)rsize, rdata); /* cast is wobbly */
2397                 return FALSE;
2398         }
2399         chars = rsize - (sizeof(nonce_eq) - 1);
2400         if (chars >= cb_nonce)
2401                 return FALSE;
2402         memcpy(nonce, rdata + sizeof(nonce_eq) - 1, chars);
2403         nonce[chars] = '\0';
2404         while (chars > 0 &&
2405                ('\r' == nonce[chars - 1] || '\n' == nonce[chars - 1])) {
2406                 chars--;
2407                 nonce[chars] = '\0';
2408         }
2409         
2410         return TRUE;
2411 }
2412
2413
2414 /*
2415  * add_mru      Add and entry to mru list, hash table, and allocate
2416  *              and return a replacement.
2417  *              This is a helper for collect_mru_list().
2418  */
2419 static mru *
2420 add_mru(
2421         mru *add
2422         )
2423 {
2424         u_short hash;
2425         mru *mon;
2426         mru *unlinked;
2427
2428
2429         hash = NTP_HASH_ADDR(&add->addr);
2430         /* see if we have it among previously received entries */
2431         for (mon = hash_table[hash]; mon != NULL; mon = mon->hlink)
2432                 if (SOCK_EQ(&mon->addr, &add->addr))
2433                         break;
2434         if (mon != NULL) {
2435                 if (!L_ISGEQ(&add->first, &mon->first)) {
2436                         fprintf(stderr,
2437                                 "add_mru duplicate %s new first ts %08x.%08x precedes prior %08x.%08x\n",
2438                                 sptoa(&add->addr), add->last.l_ui,
2439                                 add->last.l_uf, mon->last.l_ui,
2440                                 mon->last.l_uf);
2441                         exit(1);
2442                 }
2443                 UNLINK_DLIST(mon, mlink);
2444                 UNLINK_SLIST(unlinked, hash_table[hash], mon, hlink, mru);
2445                 INSIST(unlinked == mon);
2446                 mru_dupes++;
2447                 TRACE(2, ("(updated from %08x.%08x) ", mon->last.l_ui,
2448                       mon->last.l_uf));
2449         }
2450         LINK_DLIST(mru_list, add, mlink);
2451         LINK_SLIST(hash_table[hash], add, hlink);
2452         TRACE(2, ("add_mru %08x.%08x c %d m %d v %d rest %x first %08x.%08x %s\n",
2453               add->last.l_ui, add->last.l_uf, add->count,
2454               (int)add->mode, (int)add->ver, (u_int)add->rs,
2455               add->first.l_ui, add->first.l_uf, sptoa(&add->addr)));
2456         /* if we didn't update an existing entry, alloc replacement */
2457         if (NULL == mon) {
2458                 mon = emalloc(sizeof(*mon));
2459                 mru_count++;
2460         }
2461         ZERO(*mon);
2462
2463         return mon;
2464 }
2465
2466
2467 /* MGOT macro is specific to collect_mru_list() */
2468 #define MGOT(bit)                               \
2469         do {                                    \
2470                 got |= (bit);                   \
2471                 if (MRU_GOT_ALL == got) {       \
2472                         got = 0;                \
2473                         mon = add_mru(mon);     \
2474                         ci++;                   \
2475                 }                               \
2476         } while (0)
2477
2478
2479 int
2480 mrulist_ctrl_c_hook(void)
2481 {
2482         mrulist_interrupted = TRUE;
2483         return TRUE;
2484 }
2485
2486
2487 static int
2488 collect_mru_list(
2489         const char *    parms,
2490         l_fp *          pnow
2491         )
2492 {
2493         const u_int sleep_msecs = 5;
2494         static int ntpd_row_limit = MRU_ROW_LIMIT;
2495         int c_mru_l_rc;         /* this function's return code */
2496         u_char got;             /* MRU_GOT_* bits */
2497         time_t next_report;
2498         size_t cb;
2499         mru *mon;
2500         mru *head;
2501         mru *recent;
2502         int list_complete;
2503         char nonce[128];
2504         char buf[128];
2505         char req_buf[CTL_MAX_DATA_LEN];
2506         char *req;
2507         char *req_end;
2508         size_t chars;
2509         int qres;
2510         u_short rstatus;
2511         size_t rsize;
2512         const char *rdata;
2513         int limit;
2514         int frags;
2515         int cap_frags;
2516         char *tag;
2517         char *val;
2518         int si;         /* server index in response */
2519         int ci;         /* client (our) index for validation */
2520         int ri;         /* request index (.# suffix) */
2521         int mv;
2522         l_fp newest;
2523         l_fp last_older;
2524         sockaddr_u addr_older;
2525         int have_now;
2526         int have_addr_older; 
2527         int have_last_older;
2528         u_int restarted_count;
2529         u_int nonce_uses;
2530         u_short hash;
2531         mru *unlinked;
2532
2533         if (!fetch_nonce(nonce, sizeof(nonce)))
2534                 return FALSE;
2535
2536         nonce_uses = 0;
2537         restarted_count = 0;
2538         mru_count = 0;
2539         INIT_DLIST(mru_list, mlink);
2540         cb = NTP_HASH_SIZE * sizeof(*hash_table);
2541         INSIST(NULL == hash_table);
2542         hash_table = emalloc_zero(cb);
2543
2544         c_mru_l_rc = FALSE;
2545         list_complete = FALSE;
2546         have_now = FALSE;
2547         cap_frags = TRUE;
2548         got = 0;
2549         ri = 0;
2550         cb = sizeof(*mon);
2551         mon = emalloc_zero(cb);
2552         ZERO(*pnow);
2553         ZERO(last_older);
2554         next_report = time(NULL) + MRU_REPORT_SECS;
2555
2556         limit = min(3 * MAXFRAGS, ntpd_row_limit);
2557         frags = MAXFRAGS;
2558         snprintf(req_buf, sizeof(req_buf), "nonce=%s, frags=%d%s",
2559                  nonce, frags, parms);
2560         nonce_uses++;
2561
2562         while (TRUE) {
2563                 if (debug)
2564                         fprintf(stderr, "READ_MRU parms: %s\n", req_buf);
2565
2566                 qres = doqueryex(CTL_OP_READ_MRU, 0, 0,
2567                                  strlen(req_buf), req_buf,
2568                                  &rstatus, &rsize, &rdata, TRUE);
2569
2570                 if (CERR_UNKNOWNVAR == qres && ri > 0) {
2571                         /*
2572                          * None of the supplied prior entries match, so
2573                          * toss them from our list and try again.
2574                          */
2575                         if (debug)
2576                                 fprintf(stderr,
2577                                         "no overlap between %d prior entries and server MRU list\n",
2578                                         ri);
2579                         while (ri--) {
2580                                 recent = HEAD_DLIST(mru_list, mlink);
2581                                 INSIST(recent != NULL);
2582                                 if (debug)
2583                                         fprintf(stderr,
2584                                                 "tossing prior entry %s to resync\n",
2585                                                 sptoa(&recent->addr));
2586                                 UNLINK_DLIST(recent, mlink);
2587                                 hash = NTP_HASH_ADDR(&recent->addr);
2588                                 UNLINK_SLIST(unlinked, hash_table[hash],
2589                                              recent, hlink, mru);
2590                                 INSIST(unlinked == recent);
2591                                 free(recent);
2592                                 mru_count--;
2593                         }
2594                         if (NULL == HEAD_DLIST(mru_list, mlink)) {
2595                                 restarted_count++;
2596                                 if (restarted_count > 8) {
2597                                         fprintf(stderr,
2598                                                 "Giving up after 8 restarts from the beginning.\n"
2599                                                 "With high-traffic NTP servers, this can occur if the\n"
2600                                                 "MRU list is limited to less than about 16 seconds' of\n"
2601                                                 "entries.  See the 'mru' ntp.conf directive to adjust.\n");
2602                                         goto cleanup_return;
2603                                 }
2604                                 if (debug)
2605                                         fprintf(stderr,
2606                                                 "--->   Restarting from the beginning, retry #%u\n", 
2607                                                 restarted_count);
2608                         }
2609                 } else if (CERR_UNKNOWNVAR == qres) {
2610                         fprintf(stderr,
2611                                 "CERR_UNKNOWNVAR from ntpd but no priors given.\n");
2612                         goto cleanup_return;
2613                 } else if (CERR_BADVALUE == qres) {
2614                         if (cap_frags) {
2615                                 cap_frags = FALSE;
2616                                 if (debug)
2617                                         fprintf(stderr,
2618                                                 "Reverted to row limit from fragments limit.\n");
2619                         } else {
2620                                 /* ntpd has lower cap on row limit */
2621                                 ntpd_row_limit--;
2622                                 limit = min(limit, ntpd_row_limit);
2623                                 if (debug)
2624                                         fprintf(stderr,
2625                                                 "Row limit reduced to %d following CERR_BADVALUE.\n",
2626                                                 limit);
2627                         }
2628                 } else if (ERR_INCOMPLETE == qres ||
2629                            ERR_TIMEOUT == qres) {
2630                         /*
2631                          * Reduce the number of rows/frags requested by
2632                          * half to recover from lost response fragments.
2633                          */
2634                         if (cap_frags) {
2635                                 frags = max(2, frags / 2);
2636                                 if (debug)
2637                                         fprintf(stderr,
2638                                                 "Frag limit reduced to %d following incomplete response.\n",
2639                                                 frags);
2640                         } else {
2641                                 limit = max(2, limit / 2);
2642                                 if (debug)
2643                                         fprintf(stderr,
2644                                                 "Row limit reduced to %d following incomplete response.\n",
2645                                                 limit);
2646                         }
2647                 } else if (qres) {
2648                         show_error_msg(qres, 0);
2649                         goto cleanup_return;
2650                 }
2651                 /*
2652                  * This is a cheap cop-out implementation of rawmode
2653                  * output for mrulist.  A better approach would be to
2654                  * dump similar output after the list is collected by
2655                  * ntpq with a continuous sequence of indexes.  This
2656                  * cheap approach has indexes resetting to zero for
2657                  * each query/response, and duplicates are not 
2658                  * coalesced.
2659                  */
2660                 if (!qres && rawmode)
2661                         printvars(rsize, rdata, rstatus, TYPE_SYS, 1, stdout);
2662                 ci = 0;
2663                 have_addr_older = FALSE;
2664                 have_last_older = FALSE;
2665                 while (!qres && nextvar(&rsize, &rdata, &tag, &val)) {
2666                         INSIST(tag && val);
2667                         if (debug > 1)
2668                                 fprintf(stderr, "nextvar gave: %s = %s\n",
2669                                         tag, val);
2670                         switch(tag[0]) {
2671
2672                         case 'a':
2673                                 if (!strcmp(tag, "addr.older")) {
2674                                         if (!have_last_older) {
2675                                                 fprintf(stderr,
2676                                                         "addr.older %s before last.older\n",
2677                                                         val);
2678                                                 goto cleanup_return;
2679                                         }
2680                                         if (!decodenetnum(val, &addr_older)) {
2681                                                 fprintf(stderr,
2682                                                         "addr.older %s garbled\n",
2683                                                         val);
2684                                                 goto cleanup_return;
2685                                         }
2686                                         hash = NTP_HASH_ADDR(&addr_older);
2687                                         for (recent = hash_table[hash];
2688                                              recent != NULL;
2689                                              recent = recent->hlink)
2690                                                 if (ADDR_PORT_EQ(
2691                                                       &addr_older,
2692                                                       &recent->addr))
2693                                                         break;
2694                                         if (NULL == recent) {
2695                                                 fprintf(stderr,
2696                                                         "addr.older %s not in hash table\n",
2697                                                         val);
2698                                                 goto cleanup_return;
2699                                         }
2700                                         if (!L_ISEQU(&last_older,
2701                                                      &recent->last)) {
2702                                                 fprintf(stderr,
2703                                                         "last.older %08x.%08x mismatches %08x.%08x expected.\n",
2704                                                         last_older.l_ui,
2705                                                         last_older.l_uf,
2706                                                         recent->last.l_ui,
2707                                                         recent->last.l_uf);
2708                                                 goto cleanup_return;
2709                                         }
2710                                         have_addr_older = TRUE;
2711                                 } else if (1 != sscanf(tag, "addr.%d", &si)
2712                                            || si != ci)
2713                                         goto nomatch;
2714                                 else if (decodenetnum(val, &mon->addr))
2715                                         MGOT(MRU_GOT_ADDR);
2716                                 break;
2717
2718                         case 'l':
2719                                 if (!strcmp(tag, "last.older")) {
2720                                         if ('0' != val[0] ||
2721                                             'x' != val[1] ||
2722                                             !hextolfp(val + 2, &last_older)) {
2723                                                 fprintf(stderr,
2724                                                         "last.older %s garbled\n",
2725                                                         val);
2726                                                 goto cleanup_return;
2727                                         }
2728                                         have_last_older = TRUE;
2729                                 } else if (!strcmp(tag, "last.newest")) {
2730                                         if (0 != got) {
2731                                                 fprintf(stderr,
2732                                                         "last.newest %s before complete row, got = 0x%x\n",
2733                                                         val, (u_int)got);
2734                                                 goto cleanup_return;
2735                                         }
2736                                         if (!have_now) {
2737                                                 fprintf(stderr,
2738                                                         "last.newest %s before now=\n",
2739                                                         val);
2740                                                 goto cleanup_return;
2741                                         }
2742                                         head = HEAD_DLIST(mru_list, mlink);
2743                                         if (NULL != head) {
2744                                                 if ('0' != val[0] ||
2745                                                     'x' != val[1] ||
2746                                                     !hextolfp(val + 2, &newest) ||
2747                                                     !L_ISEQU(&newest,
2748                                                              &head->last)) {
2749                                                         fprintf(stderr,
2750                                                                 "last.newest %s mismatches %08x.%08x",
2751                                                                 val,
2752                                                                 head->last.l_ui,
2753                                                                 head->last.l_uf);
2754                                                         goto cleanup_return;
2755                                                 }
2756                                         }
2757                                         list_complete = TRUE;
2758                                 } else if (1 != sscanf(tag, "last.%d", &si) ||
2759                                            si != ci || '0' != val[0] ||
2760                                            'x' != val[1] ||
2761                                            !hextolfp(val + 2, &mon->last)) {
2762                                         goto nomatch;
2763                                 } else {
2764                                         MGOT(MRU_GOT_LAST);
2765                                         /*
2766                                          * allow interrupted retrieval,
2767                                          * using most recent retrieved
2768                                          * entry's last seen timestamp
2769                                          * as the end of operation.
2770                                          */
2771                                         *pnow = mon->last;
2772                                 }
2773                                 break;
2774
2775                         case 'f':
2776                                 if (1 != sscanf(tag, "first.%d", &si) ||
2777                                     si != ci || '0' != val[0] ||
2778                                     'x' != val[1] ||
2779                                     !hextolfp(val + 2, &mon->first))
2780                                         goto nomatch;
2781                                 MGOT(MRU_GOT_FIRST);
2782                                 break;
2783
2784                         case 'n':
2785                                 if (!strcmp(tag, "nonce")) {
2786                                         strlcpy(nonce, val, sizeof(nonce));
2787                                         nonce_uses = 0;
2788                                         break; /* case */
2789                                 } else if (strcmp(tag, "now") ||
2790                                            '0' != val[0] ||
2791                                            'x' != val[1] ||
2792                                             !hextolfp(val + 2, pnow))
2793                                         goto nomatch;
2794                                 have_now = TRUE;
2795                                 break;
2796
2797                         case 'c':
2798                                 if (1 != sscanf(tag, "ct.%d", &si) ||
2799                                     si != ci ||
2800                                     1 != sscanf(val, "%d", &mon->count)
2801                                     || mon->count < 1)
2802                                         goto nomatch;
2803                                 MGOT(MRU_GOT_COUNT);
2804                                 break;
2805
2806                         case 'm':
2807                                 if (1 != sscanf(tag, "mv.%d", &si) ||
2808                                     si != ci ||
2809                                     1 != sscanf(val, "%d", &mv))
2810                                         goto nomatch;
2811                                 mon->mode = PKT_MODE(mv);
2812                                 mon->ver = PKT_VERSION(mv);
2813                                 MGOT(MRU_GOT_MV);
2814                                 break;
2815
2816                         case 'r':
2817                                 if (1 != sscanf(tag, "rs.%d", &si) ||
2818                                     si != ci ||
2819                                     1 != sscanf(val, "0x%hx", &mon->rs))
2820                                         goto nomatch;
2821                                 MGOT(MRU_GOT_RS);
2822                                 break;
2823
2824                         default:
2825                         nomatch:        
2826                                 /* empty stmt */ ;
2827                                 /* ignore unknown tags */
2828                         }
2829                 }
2830                 if (have_now)
2831                         list_complete = TRUE;
2832                 if (list_complete) {
2833                         INSIST(0 == ri || have_addr_older);
2834                 }
2835                 if (mrulist_interrupted) {
2836                         printf("mrulist retrieval interrupted by operator.\n"
2837                                "Displaying partial client list.\n");
2838                         fflush(stdout);
2839                 }
2840                 if (list_complete || mrulist_interrupted) {
2841                         fprintf(stderr,
2842                                 "\rRetrieved %u unique MRU entries and %u updates.\n",
2843                                 mru_count, mru_dupes);
2844                         fflush(stderr);
2845                         break;
2846                 }
2847                 if (time(NULL) >= next_report) {
2848                         next_report += MRU_REPORT_SECS;
2849                         fprintf(stderr, "\r%u (%u updates) ", mru_count,
2850                                 mru_dupes);
2851                         fflush(stderr);
2852                 }
2853
2854                 /*
2855                  * Snooze for a bit between queries to let ntpd catch
2856                  * up with other duties.
2857                  */
2858 #ifdef SYS_WINNT
2859                 Sleep(sleep_msecs);
2860 #elif !defined(HAVE_NANOSLEEP)
2861                 sleep((sleep_msecs / 1000) + 1);
2862 #else
2863                 {
2864                         struct timespec interv = { 0,
2865                                                    1000 * sleep_msecs };
2866                         nanosleep(&interv, NULL);
2867                 }
2868 #endif
2869                 /*
2870                  * If there were no errors, increase the number of rows
2871                  * to a maximum of 3 * MAXFRAGS (the most packets ntpq
2872                  * can handle in one response), on the assumption that
2873                  * no less than 3 rows fit in each packet, capped at 
2874                  * our best guess at the server's row limit.
2875                  */
2876                 if (!qres) {
2877                         if (cap_frags) {
2878                                 frags = min(MAXFRAGS, frags + 1);
2879                         } else {
2880                                 limit = min3(3 * MAXFRAGS,
2881                                              ntpd_row_limit,
2882                                              max(limit + 1,
2883                                                  limit * 33 / 32));
2884                         }
2885                 }
2886                 /*
2887                  * prepare next query with as many address and last-seen
2888                  * timestamps as will fit in a single packet.
2889                  */
2890                 req = req_buf;
2891                 req_end = req_buf + sizeof(req_buf);
2892 #define REQ_ROOM        (req_end - req)
2893                 snprintf(req, REQ_ROOM, "nonce=%s, %s=%d%s", nonce,
2894                          (cap_frags)
2895                              ? "frags"
2896                              : "limit",
2897                          (cap_frags)
2898                              ? frags
2899                              : limit,
2900                          parms);
2901                 req += strlen(req);
2902                 nonce_uses++;
2903                 if (nonce_uses >= 4) {
2904                         if (!fetch_nonce(nonce, sizeof(nonce)))
2905                                 goto cleanup_return;
2906                         nonce_uses = 0;
2907                 }
2908
2909
2910                 for (ri = 0, recent = HEAD_DLIST(mru_list, mlink);
2911                      recent != NULL;
2912                      ri++, recent = NEXT_DLIST(mru_list, recent, mlink)) {
2913
2914                         snprintf(buf, sizeof(buf),
2915                                  ", addr.%d=%s, last.%d=0x%08x.%08x",
2916                                  ri, sptoa(&recent->addr), ri,
2917                                  recent->last.l_ui, recent->last.l_uf);
2918                         chars = strlen(buf);
2919                         if ((size_t)REQ_ROOM <= chars)
2920                                 break;
2921                         memcpy(req, buf, chars + 1);
2922                         req += chars;
2923                 }
2924         }
2925
2926         c_mru_l_rc = TRUE;
2927         goto retain_hash_table;
2928
2929 cleanup_return:
2930         free(hash_table);
2931         hash_table = NULL;
2932
2933 retain_hash_table:
2934         if (mon != NULL)
2935                 free(mon);
2936
2937         return c_mru_l_rc;
2938 }
2939
2940
2941 /*
2942  * qcmp_mru_addr - sort MRU entries by remote address.
2943  *
2944  * All IPv4 addresses sort before any IPv6, addresses are sorted by
2945  * value within address family.
2946  */
2947 static int
2948 qcmp_mru_addr(
2949         const void *v1,
2950         const void *v2
2951         )
2952 {
2953         const mru * const *     ppm1 = v1;
2954         const mru * const *     ppm2 = v2;
2955         const mru *             pm1;
2956         const mru *             pm2;
2957         u_short                 af1;
2958         u_short                 af2;
2959         size_t                  cmplen;
2960         size_t                  addr_off;
2961
2962         pm1 = *ppm1;
2963         pm2 = *ppm2;
2964
2965         af1 = AF(&pm1->addr);
2966         af2 = AF(&pm2->addr);
2967
2968         if (af1 != af2)
2969                 return (AF_INET == af1)
2970                            ? -1
2971                            : 1;
2972
2973         cmplen = SIZEOF_INADDR(af1);
2974         addr_off = (AF_INET == af1)
2975                       ? offsetof(struct sockaddr_in, sin_addr)
2976                       : offsetof(struct sockaddr_in6, sin6_addr);
2977
2978         return memcmp((const char *)&pm1->addr + addr_off,
2979                       (const char *)&pm2->addr + addr_off,
2980                       cmplen);
2981 }
2982
2983
2984 static int
2985 qcmp_mru_r_addr(
2986         const void *v1,
2987         const void *v2
2988         )
2989 {
2990         return -qcmp_mru_addr(v1, v2);
2991 }
2992
2993
2994 /*
2995  * qcmp_mru_count - sort MRU entries by times seen (hit count).
2996  */
2997 static int
2998 qcmp_mru_count(
2999         const void *v1,
3000         const void *v2
3001         )
3002 {
3003         const mru * const *     ppm1 = v1;
3004         const mru * const *     ppm2 = v2;
3005         const mru *             pm1;
3006         const mru *             pm2;
3007
3008         pm1 = *ppm1;
3009         pm2 = *ppm2;
3010         
3011         return (pm1->count < pm2->count)
3012                    ? -1
3013                    : ((pm1->count == pm2->count)
3014                           ? 0
3015                           : 1);
3016 }
3017
3018
3019 static int
3020 qcmp_mru_r_count(
3021         const void *v1,
3022         const void *v2
3023         )
3024 {
3025         return -qcmp_mru_count(v1, v2);
3026 }
3027
3028
3029 /*
3030  * qcmp_mru_avgint - sort MRU entries by average interval.
3031  */
3032 static int
3033 qcmp_mru_avgint(
3034         const void *v1,
3035         const void *v2
3036         )
3037 {
3038         const mru * const *     ppm1 = v1;
3039         const mru * const *     ppm2 = v2;
3040         const mru *             pm1;
3041         const mru *             pm2;
3042         l_fp                    interval;
3043         double                  avg1;
3044         double                  avg2;
3045
3046         pm1 = *ppm1;
3047         pm2 = *ppm2;
3048
3049         interval = pm1->last;
3050         L_SUB(&interval, &pm1->first);
3051         LFPTOD(&interval, avg1);
3052         avg1 /= pm1->count;
3053
3054         interval = pm2->last;
3055         L_SUB(&interval, &pm2->first);
3056         LFPTOD(&interval, avg2);
3057         avg2 /= pm2->count;
3058
3059         if (avg1 < avg2)
3060                 return -1;
3061         else if (avg1 > avg2)
3062                 return 1;
3063
3064         /* secondary sort on lstint - rarely tested */
3065         if (L_ISEQU(&pm1->last, &pm2->last))
3066                 return 0;
3067         else if (L_ISGEQ(&pm1->last, &pm2->last))
3068                 return -1;
3069         else
3070                 return 1;
3071 }
3072
3073
3074 static int
3075 qcmp_mru_r_avgint(
3076         const void *v1,
3077         const void *v2
3078         )
3079 {
3080         return -qcmp_mru_avgint(v1, v2);
3081 }
3082
3083
3084 /*
3085  * mrulist - ntpq's mrulist command to fetch an arbitrarily large Most
3086  *           Recently Used (seen) remote address list from ntpd.
3087  *
3088  * Similar to ntpdc's monlist command, but not limited to a single
3089  * request/response, and thereby not limited to a few hundred remote
3090  * addresses.
3091  *
3092  * See ntpd/ntp_control.c read_mru_list() for comments on the way
3093  * CTL_OP_READ_MRU is designed to be used.
3094  *
3095  * mrulist intentionally differs from monlist in the way the avgint
3096  * column is calculated.  monlist includes the time after the last
3097  * packet from the client until the monlist query time in the average,
3098  * while mrulist excludes it.  That is, monlist's average interval grows
3099  * over time for remote addresses not heard from in some time, while it
3100  * remains unchanged in mrulist.  This also affects the avgint value for
3101  * entries representing a single packet, with identical first and last
3102  * timestamps.  mrulist shows 0 avgint, monlist shows a value identical
3103  * to lstint.
3104  */
3105 static void 
3106 mrulist(
3107         struct parse *  pcmd,
3108         FILE *          fp
3109         )
3110 {
3111         const char mincount_eq[] =      "mincount=";
3112         const char resall_eq[] =        "resall=";
3113         const char resany_eq[] =        "resany=";
3114         const char maxlstint_eq[] =     "maxlstint=";
3115         const char laddr_eq[] =         "laddr=";
3116         const char sort_eq[] =          "sort=";
3117         mru_sort_order order;
3118         size_t n;
3119         char parms_buf[128];
3120         char buf[24];
3121         char *parms;
3122         const char *arg;
3123         size_t cb;
3124         mru **sorted;
3125         mru **ppentry;
3126         mru *recent;
3127         l_fp now;
3128         l_fp interval;
3129         double favgint;
3130         double flstint;
3131         int avgint;
3132         int lstint;
3133         size_t i;
3134
3135         mrulist_interrupted = FALSE;
3136         push_ctrl_c_handler(&mrulist_ctrl_c_hook);
3137         fprintf(stderr,
3138                 "Ctrl-C will stop MRU retrieval and display partial results.\n");
3139         fflush(stderr);
3140
3141         order = MRUSORT_DEF;
3142         parms_buf[0] = '\0';
3143         parms = parms_buf;
3144         for (i = 0; i < pcmd->nargs; i++) {
3145                 arg = pcmd->argval[i].string;
3146                 if (arg != NULL) {
3147                         cb = strlen(arg) + 1;
3148                         if ((!strncmp(resall_eq, arg, sizeof(resall_eq)
3149                             - 1) || !strncmp(resany_eq, arg,
3150                             sizeof(resany_eq) - 1) || !strncmp(
3151                             mincount_eq, arg, sizeof(mincount_eq) - 1) 
3152                             || !strncmp(laddr_eq, arg, sizeof(laddr_eq)
3153                             - 1) || !strncmp(maxlstint_eq, arg,
3154                             sizeof(laddr_eq) - 1)) && parms + cb + 2 <=
3155                             parms_buf + sizeof(parms_buf)) {
3156                                 /* these are passed intact to ntpd */
3157                                 memcpy(parms, ", ", 2);
3158                                 parms += 2;
3159                                 memcpy(parms, arg, cb);
3160                                 parms += cb - 1;
3161                         } else if (!strncmp(sort_eq, arg,
3162                                             sizeof(sort_eq) - 1)) {
3163                                 arg += sizeof(sort_eq) - 1;
3164                                 for (n = 0;
3165                                      n < COUNTOF(mru_sort_keywords);
3166                                      n++)
3167                                         if (!strcmp(mru_sort_keywords[n],
3168                                                     arg))
3169                                                 break;
3170                                 if (n < COUNTOF(mru_sort_keywords))
3171                                         order = n;
3172                         } else if (!strcmp("limited", arg) ||
3173                                    !strcmp("kod", arg)) {
3174                                 /* transform to resany=... */
3175                                 snprintf(buf, sizeof(buf),
3176                                          ", resany=0x%x",
3177                                          ('k' == arg[0])
3178                                              ? RES_KOD
3179                                              : RES_LIMITED);
3180                                 cb = 1 + strlen(buf);
3181                                 if (parms + cb <
3182                                         parms_buf + sizeof(parms_buf)) {
3183                                         memcpy(parms, buf, cb);
3184                                         parms += cb - 1;
3185                                 }
3186                         } else
3187                                 fprintf(stderr,
3188                                         "ignoring unrecognized mrulist parameter: %s\n",
3189                                         arg);
3190                 }
3191         }
3192         parms = parms_buf;
3193
3194         if (!collect_mru_list(parms, &now))
3195                 return;
3196
3197         /* display the results */
3198         if (rawmode)
3199                 goto cleanup_return;
3200
3201         /* construct an array of entry pointers in default order */
3202         sorted = eallocarray(mru_count, sizeof(*sorted));
3203         ppentry = sorted;
3204         if (MRUSORT_R_DEF != order) {
3205                 ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3206                         INSIST(ppentry < sorted + mru_count);
3207                         *ppentry = recent;
3208                         ppentry++;
3209                 ITER_DLIST_END()
3210         } else {
3211                 REV_ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3212                         INSIST(ppentry < sorted + mru_count);
3213                         *ppentry = recent;
3214                         ppentry++;
3215                 REV_ITER_DLIST_END()
3216         }
3217
3218         if (ppentry - sorted != (int)mru_count) {
3219                 fprintf(stderr,
3220                         "mru_count %u should match MRU list depth %ld.\n",
3221                         mru_count, (long)(ppentry - sorted));
3222                 free(sorted);
3223                 goto cleanup_return;
3224         }
3225
3226         /* re-sort sorted[] if not default or reverse default */
3227         if (MRUSORT_R_DEF < order)
3228                 qsort(sorted, mru_count, sizeof(sorted[0]),
3229                       mru_qcmp_table[order]);
3230
3231         mrulist_interrupted = FALSE;
3232         printf( "lstint avgint rstr r m v  count rport remote address\n"
3233                 "==============================================================================\n");
3234                 /* '=' x 78 */
3235         for (ppentry = sorted; ppentry < sorted + mru_count; ppentry++) {
3236                 recent = *ppentry;
3237                 interval = now;
3238                 L_SUB(&interval, &recent->last);
3239                 LFPTOD(&interval, flstint);
3240                 lstint = (int)(flstint + 0.5);
3241                 interval = recent->last;
3242                 L_SUB(&interval, &recent->first);
3243                 LFPTOD(&interval, favgint);
3244                 favgint /= recent->count;
3245                 avgint = (int)(favgint + 0.5);
3246                 fprintf(fp, "%6d %6d %4hx %c %d %d %6d %5u %s\n",
3247                         lstint, avgint, recent->rs,
3248                         (RES_KOD & recent->rs)
3249                             ? 'K'
3250                             : (RES_LIMITED & recent->rs)
3251                                   ? 'L'
3252                                   : '.',
3253                         (int)recent->mode, (int)recent->ver,
3254                         recent->count, SRCPORT(&recent->addr),
3255                         nntohost(&recent->addr));
3256                 if (showhostnames)
3257                         fflush(fp);
3258                 if (mrulist_interrupted) {
3259                         fputs("\n --interrupted--\n", fp);
3260                         fflush(fp);
3261                         break;
3262                 }
3263         }
3264         fflush(fp);
3265         if (debug) {
3266                 fprintf(stderr,
3267                         "--- completed, freeing sorted[] pointers\n");
3268                 fflush(stderr);
3269         }
3270         free(sorted);
3271
3272 cleanup_return:
3273         if (debug) {
3274                 fprintf(stderr, "... freeing MRU entries\n");
3275                 fflush(stderr);
3276         }
3277         ITER_DLIST_BEGIN(mru_list, recent, mlink, mru)
3278                 free(recent);
3279         ITER_DLIST_END()
3280         if (debug) {
3281                 fprintf(stderr, "... freeing hash_table[]\n");
3282                 fflush(stderr);
3283         }
3284         free(hash_table);
3285         hash_table = NULL;
3286         INIT_DLIST(mru_list, mlink);
3287
3288         pop_ctrl_c_handler(&mrulist_ctrl_c_hook);
3289 }
3290
3291
3292 /*
3293  * validate_ifnum - helper for ifstats()
3294  *
3295  * Ensures rows are received in order and complete.
3296  */
3297 static void
3298 validate_ifnum(
3299         FILE *          fp,
3300         u_int           ifnum,
3301         int *           pfields,
3302         ifstats_row *   prow
3303         )
3304 {
3305         if (prow->ifnum == ifnum)
3306                 return;
3307         if (prow->ifnum + 1 <= ifnum) {
3308                 if (*pfields < IFSTATS_FIELDS)
3309                         fprintf(fp, "Warning: incomplete row with %d (of %d) fields\n",
3310                                 *pfields, IFSTATS_FIELDS);
3311                 *pfields = 0;
3312                 prow->ifnum = ifnum;
3313                 return;
3314         }
3315         fprintf(stderr,
3316                 "received if index %u, have %d of %d fields for index %u, aborting.\n",
3317                 ifnum, *pfields, IFSTATS_FIELDS, prow->ifnum);
3318         exit(1);
3319 }
3320
3321
3322 /*
3323  * another_ifstats_field - helper for ifstats()
3324  *
3325  * If all fields for the row have been received, print it.
3326  */
3327 static void
3328 another_ifstats_field(
3329         int *           pfields,
3330         ifstats_row *   prow,
3331         FILE *          fp
3332         )
3333 {
3334         u_int ifnum;
3335
3336         (*pfields)++;
3337         /* we understand 12 tags */
3338         if (IFSTATS_FIELDS > *pfields)  
3339                 return;
3340         /*
3341         "    interface name                                        send\n"
3342         " #  address/broadcast     drop flag ttl mc received sent failed peers   uptime\n"
3343         "==============================================================================\n");
3344          */
3345         fprintf(fp,
3346                 "%3u %-24.24s %c %4x %3u %2u %6u %6u %6u %5u %8d\n"
3347                 "    %s\n",
3348                 prow->ifnum, prow->name,
3349                 (prow->enabled)
3350                     ? '.'
3351                     : 'D',
3352                 prow->flags, prow->ttl, prow->mcast_count,
3353                 prow->received, prow->sent, prow->send_errors,
3354                 prow->peer_count, prow->uptime, sptoa(&prow->addr));
3355         if (!SOCK_UNSPEC(&prow->bcast))
3356                 fprintf(fp, "    %s\n", sptoa(&prow->bcast));
3357         ifnum = prow->ifnum;
3358         ZERO(*prow);
3359         prow->ifnum = ifnum;
3360 }
3361
3362
3363 /*
3364  * ifstats - ntpq -c ifstats modeled on ntpdc -c ifstats.
3365  */
3366 static void 
3367 ifstats(
3368         struct parse *  pcmd,
3369         FILE *          fp
3370         )
3371 {
3372         const char      addr_fmt[] =    "addr.%u";
3373         const char      bcast_fmt[] =   "bcast.%u";
3374         const char      en_fmt[] =      "en.%u";        /* enabled */
3375         const char      flags_fmt[] =   "flags.%u";
3376         const char      mc_fmt[] =      "mc.%u";        /* mcast count */
3377         const char      name_fmt[] =    "name.%u";
3378         const char      pc_fmt[] =      "pc.%u";        /* peer count */
3379         const char      rx_fmt[] =      "rx.%u";
3380         const char      tl_fmt[] =      "tl.%u";        /* ttl */
3381         const char      tx_fmt[] =      "tx.%u";
3382         const char      txerr_fmt[] =   "txerr.%u";
3383         const char      up_fmt[] =      "up.%u";        /* uptime */
3384         const char *    datap;
3385         int             qres;
3386         size_t          dsize;
3387         u_short         rstatus;
3388         char *          tag;
3389         char *          val;
3390         int             fields;
3391         u_int           ui;
3392         ifstats_row     row;
3393         int             comprende;
3394         size_t          len;
3395
3396         qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, 0, NULL, &rstatus,
3397                        &dsize, &datap);
3398         if (qres)       /* message already displayed */
3399                 return;
3400
3401         fprintf(fp,
3402                 "    interface name                                        send\n"
3403                 " #  address/broadcast     drop flag ttl mc received sent failed peers   uptime\n"
3404                 "==============================================================================\n");
3405                 /* '=' x 78 */
3406
3407         ZERO(row);
3408         fields = 0;
3409         ui = 0;
3410         while (nextvar(&dsize, &datap, &tag, &val)) {
3411                 INSIST(tag && val);
3412                 if (debug > 1)
3413                     fprintf(stderr, "nextvar gave: %s = %s\n", tag, val);
3414                 comprende = FALSE;
3415                 switch(tag[0]) {
3416
3417                 case 'a':
3418                         if (1 == sscanf(tag, addr_fmt, &ui) &&
3419                             decodenetnum(val, &row.addr))
3420                                 comprende = TRUE;
3421                         break;
3422
3423                 case 'b':
3424                         if (1 == sscanf(tag, bcast_fmt, &ui) &&
3425                             ('\0' == *val ||
3426                              decodenetnum(val, &row.bcast)))
3427                                 comprende = TRUE;
3428                         break;
3429
3430                 case 'e':
3431                         if (1 == sscanf(tag, en_fmt, &ui) &&
3432                             1 == sscanf(val, "%d", &row.enabled))
3433                                 comprende = TRUE;
3434                         break;
3435
3436                 case 'f':
3437                         if (1 == sscanf(tag, flags_fmt, &ui) &&
3438                             1 == sscanf(val, "0x%x", &row.flags))
3439                                 comprende = TRUE;
3440                         break;
3441
3442                 case 'm':
3443                         if (1 == sscanf(tag, mc_fmt, &ui) &&
3444                             1 == sscanf(val, "%u", &row.mcast_count))
3445                                 comprende = TRUE;
3446                         break;
3447
3448                 case 'n':
3449                         if (1 == sscanf(tag, name_fmt, &ui)) {
3450                                 /* strip quotes */
3451                                 len = strlen(val);
3452                                 if (len >= 2 &&
3453                                     len - 2 < sizeof(row.name)) {
3454                                         len -= 2;
3455                                         memcpy(row.name, val + 1, len);
3456                                         row.name[len] = '\0';
3457                                         comprende = TRUE;
3458                                 }
3459                         }
3460                         break;
3461
3462                 case 'p':
3463                         if (1 == sscanf(tag, pc_fmt, &ui) &&
3464                             1 == sscanf(val, "%u", &row.peer_count))
3465                                 comprende = TRUE;
3466                         break;
3467
3468                 case 'r':
3469                         if (1 == sscanf(tag, rx_fmt, &ui) &&
3470                             1 == sscanf(val, "%u", &row.received))
3471                                 comprende = TRUE;
3472                         break;
3473
3474                 case 't':
3475                         if (1 == sscanf(tag, tl_fmt, &ui) &&
3476                             1 == sscanf(val, "%u", &row.ttl))
3477                                 comprende = TRUE;
3478                         else if (1 == sscanf(tag, tx_fmt, &ui) &&
3479                                  1 == sscanf(val, "%u", &row.sent))
3480                                 comprende = TRUE;
3481                         else if (1 == sscanf(tag, txerr_fmt, &ui) &&
3482                                  1 == sscanf(val, "%u", &row.send_errors))
3483                                 comprende = TRUE;
3484                         break;
3485
3486                 case 'u':
3487                         if (1 == sscanf(tag, up_fmt, &ui) &&
3488                             1 == sscanf(val, "%u", &row.uptime))
3489                                 comprende = TRUE;
3490                         break;
3491                 }
3492
3493                 if (comprende) {
3494                         /* error out if rows out of order */
3495                         validate_ifnum(fp, ui, &fields, &row);
3496                         /* if the row is complete, print it */
3497                         another_ifstats_field(&fields, &row, fp);
3498                 }
3499         }
3500         if (fields != IFSTATS_FIELDS)
3501                 fprintf(fp, "Warning: incomplete row with %d (of %d) fields\n",
3502                         fields, IFSTATS_FIELDS);
3503
3504         fflush(fp);
3505 }
3506
3507
3508 /*
3509  * validate_reslist_idx - helper for reslist()
3510  *
3511  * Ensures rows are received in order and complete.
3512  */
3513 static void
3514 validate_reslist_idx(
3515         FILE *          fp,
3516         u_int           idx,
3517         int *           pfields,
3518         reslist_row *   prow
3519         )
3520 {
3521         if (prow->idx == idx)
3522                 return;
3523         if (prow->idx + 1 == idx) {
3524                 if (*pfields < RESLIST_FIELDS)
3525                         fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3526                                 *pfields, RESLIST_FIELDS);
3527                 *pfields = 0;
3528                 prow->idx = idx;
3529                 return;
3530         }
3531         fprintf(stderr,
3532                 "received reslist index %u, have %d of %d fields for index %u, aborting.\n",
3533                 idx, *pfields, RESLIST_FIELDS, prow->idx);
3534         exit(1);
3535 }
3536
3537
3538 /*
3539  * another_reslist_field - helper for reslist()
3540  *
3541  * If all fields for the row have been received, print it.
3542  */
3543 static void
3544 another_reslist_field(
3545         int *           pfields,
3546         reslist_row *   prow,
3547         FILE *          fp
3548         )
3549 {
3550         char    addrmaskstr[128];
3551         int     prefix; /* subnet mask as prefix bits count */
3552         u_int   idx;
3553
3554         (*pfields)++;
3555         /* we understand 4 tags */
3556         if (RESLIST_FIELDS > *pfields)
3557                 return;
3558
3559         prefix = sockaddr_masktoprefixlen(&prow->mask);
3560         if (prefix >= 0)
3561                 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s/%d",
3562                          stoa(&prow->addr), prefix);
3563         else
3564                 snprintf(addrmaskstr, sizeof(addrmaskstr), "%s %s",
3565                          stoa(&prow->addr), stoa(&prow->mask));
3566
3567         /*
3568         "   hits    addr/prefix or addr mask\n"
3569         "           restrictions\n"
3570         "==============================================================================\n");
3571          */
3572         fprintf(fp,
3573                 "%10lu %s\n"
3574                 "           %s\n",
3575                 prow->hits, addrmaskstr, prow->flagstr);
3576         idx = prow->idx;
3577         ZERO(*prow);
3578         prow->idx = idx;
3579 }
3580
3581
3582 /*
3583  * reslist - ntpq -c reslist modeled on ntpdc -c reslist.
3584  */
3585 static void 
3586 reslist(
3587         struct parse *  pcmd,
3588         FILE *          fp
3589         )
3590 {
3591         const char addr_fmtu[] =        "addr.%u";
3592         const char mask_fmtu[] =        "mask.%u";
3593         const char hits_fmt[] =         "hits.%u";
3594         const char flags_fmt[] =        "flags.%u";
3595         const char qdata[] =            "addr_restrictions";
3596         const int qdata_chars =         COUNTOF(qdata) - 1;
3597         const char *    datap;
3598         int             qres;
3599         size_t          dsize;
3600         u_short         rstatus;
3601         char *          tag;
3602         char *          val;
3603         int             fields;
3604         u_int           ui;
3605         reslist_row     row;
3606         int             comprende;
3607         size_t          len;
3608
3609         qres = doquery(CTL_OP_READ_ORDLIST_A, 0, TRUE, qdata_chars,
3610                        qdata, &rstatus, &dsize, &datap);
3611         if (qres)       /* message already displayed */
3612                 return;
3613
3614         fprintf(fp,
3615                 "   hits    addr/prefix or addr mask\n"
3616                 "           restrictions\n"
3617                 "==============================================================================\n");
3618                 /* '=' x 78 */
3619
3620         ZERO(row);
3621         fields = 0;
3622         ui = 0;
3623         while (nextvar(&dsize, &datap, &tag, &val)) {
3624                 INSIST(tag && val);
3625                 if (debug > 1)
3626                         fprintf(stderr, "nextvar gave: %s = %s\n", tag, val);
3627                 comprende = FALSE;
3628                 switch(tag[0]) {
3629
3630                 case 'a':
3631                         if (1 == sscanf(tag, addr_fmtu, &ui) &&
3632                             decodenetnum(val, &row.addr))
3633                                 comprende = TRUE;
3634                         break;
3635
3636                 case 'f':
3637                         if (1 == sscanf(tag, flags_fmt, &ui)) {
3638                                 if (NULL == val) {
3639                                         row.flagstr[0] = '\0';
3640                                         comprende = TRUE;
3641                                 } else if ((len = strlen(val)) < sizeof(row.flagstr)) {
3642                                         memcpy(row.flagstr, val, len);
3643                                         row.flagstr[len] = '\0';
3644                                         comprende = TRUE;
3645                                 } else {
3646                                          /* no flags, and still !comprende */
3647                                         row.flagstr[0] = '\0';
3648                                 }
3649                         }
3650                         break;
3651
3652                 case 'h':
3653                         if (1 == sscanf(tag, hits_fmt, &ui) &&
3654                             1 == sscanf(val, "%lu", &row.hits))
3655                                 comprende = TRUE;
3656                         break;
3657
3658                 case 'm':
3659                         if (1 == sscanf(tag, mask_fmtu, &ui) &&
3660                             decodenetnum(val, &row.mask))
3661                                 comprende = TRUE;
3662                         break;
3663                 }
3664
3665                 if (comprende) {
3666                         /* error out if rows out of order */
3667                         validate_reslist_idx(fp, ui, &fields, &row);
3668                         /* if the row is complete, print it */
3669                         another_reslist_field(&fields, &row, fp);
3670                 }
3671         }
3672         if (fields != RESLIST_FIELDS)
3673                 fprintf(fp, "Warning: incomplete row with %d (of %d) fields",
3674                         fields, RESLIST_FIELDS);
3675
3676         fflush(fp);
3677 }
3678
3679
3680 /*
3681  * collect_display_vdc
3682  */
3683 static void 
3684 collect_display_vdc(
3685         associd_t       as,
3686         vdc *           table,
3687         int             decodestatus,
3688         FILE *          fp
3689         )
3690 {
3691         static const char * const suf[2] = { "adr", "port" };
3692         static const char * const leapbits[4] = { "00", "01",
3693                                                   "10", "11" };
3694         struct varlist vl[MAXLIST];
3695         char tagbuf[32];
3696         vdc *pvdc;
3697         u_short rstatus;
3698         size_t rsize;
3699         const char *rdata;
3700         int qres;
3701         char *tag;
3702         char *val;
3703         u_int n;
3704         size_t len;
3705         int match;
3706         u_long ul;
3707         int vtype;
3708
3709         ZERO(vl);
3710         for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3711                 ZERO(pvdc->v);
3712                 if (NTP_ADD != pvdc->type) {
3713                         doaddvlist(vl, pvdc->tag);
3714                 } else {
3715                         for (n = 0; n < COUNTOF(suf); n++) {
3716                                 snprintf(tagbuf, sizeof(tagbuf), "%s%s",
3717                                          pvdc->tag, suf[n]);
3718                                 doaddvlist(vl, tagbuf);
3719                         }
3720                 }
3721         }
3722         qres = doquerylist(vl, CTL_OP_READVAR, as, 0, &rstatus, &rsize,
3723                            &rdata);
3724         doclearvlist(vl);
3725         if (qres)
3726                 return;         /* error msg already displayed */
3727
3728         /*
3729          * iterate over the response variables filling vdc_table with
3730          * the retrieved values.
3731          */
3732         while (nextvar(&rsize, &rdata, &tag, &val)) {
3733                 INSIST(tag && val);
3734                 n = 0;
3735                 for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3736                         len = strlen(pvdc->tag);
3737                         if (strncmp(tag, pvdc->tag, len))
3738                                 continue;
3739                         if (NTP_ADD != pvdc->type) {
3740                                 if ('\0' != tag[len])
3741                                         continue;
3742                                 break;
3743                         }
3744                         match = FALSE;
3745                         for (n = 0; n < COUNTOF(suf); n++) {
3746                                 if (strcmp(tag + len, suf[n]))
3747                                         continue;
3748                                 match = TRUE;
3749                                 break;
3750                         }
3751                         if (match)
3752                                 break;
3753                 }
3754                 if (NULL == pvdc->tag)
3755                         continue;
3756                 switch (pvdc->type) {
3757
3758                 case NTP_STR:
3759                         /* strip surrounding double quotes */
3760                         if ('"' == val[0]) {
3761                                 len = strlen(val);
3762                                 if (len > 0 && '"' == val[len - 1]) {
3763                                         val[len - 1] = '\0';
3764                                         val++;
3765                                 }
3766                         }
3767                         /* fallthru */
3768                 case NTP_MODE:  /* fallthru */
3769                 case NTP_2BIT:
3770                         pvdc->v.str = estrdup(val);
3771                         break;
3772
3773                 case NTP_LFP:
3774                         decodets(val, &pvdc->v.lfp);
3775                         break;
3776
3777                 case NTP_ADP:
3778                         if (!decodenetnum(val, &pvdc->v.sau))
3779                                 fprintf(stderr, "malformed %s=%s\n",
3780                                         pvdc->tag, val);
3781                         break;
3782
3783                 case NTP_ADD:
3784                         if (0 == n) {   /* adr */
3785                                 if (!decodenetnum(val, &pvdc->v.sau))
3786                                         fprintf(stderr,
3787                                                 "malformed %s=%s\n",
3788                                                 pvdc->tag, val);
3789                         } else {        /* port */
3790                                 if (atouint(val, &ul))
3791                                         SET_PORT(&pvdc->v.sau, 
3792                                                  (u_short)ul);
3793                         }
3794                         break;
3795                 }
3796         }
3797
3798         /* and display */
3799         if (decodestatus) {
3800                 vtype = (0 == as)
3801                             ? TYPE_SYS
3802                             : TYPE_PEER;
3803                 fprintf(fp, "associd=%u status=%04x %s,\n", as, rstatus,
3804                         statustoa(vtype, rstatus));
3805         }
3806
3807         for (pvdc = table; pvdc->tag != NULL; pvdc++) {
3808                 switch (pvdc->type) {
3809
3810                 case NTP_STR:
3811                         if (pvdc->v.str != NULL) {
3812                                 fprintf(fp, "%s  %s\n", pvdc->display,
3813                                         pvdc->v.str);
3814                                 free(pvdc->v.str);
3815                                 pvdc->v.str = NULL;
3816                         }
3817                         break;
3818
3819                 case NTP_ADD:   /* fallthru */
3820                 case NTP_ADP:
3821                         fprintf(fp, "%s  %s\n", pvdc->display,
3822                                 nntohostp(&pvdc->v.sau));
3823                         break;
3824
3825                 case NTP_LFP:
3826                         fprintf(fp, "%s  %s\n", pvdc->display,
3827                                 prettydate(&pvdc->v.lfp));
3828                         break;
3829
3830                 case NTP_MODE:
3831                         atouint(pvdc->v.str, &ul);
3832                         fprintf(fp, "%s  %s\n", pvdc->display,
3833                                 modetoa((int)ul));
3834                         break;
3835
3836                 case NTP_2BIT:
3837                         atouint(pvdc->v.str, &ul);
3838                         fprintf(fp, "%s  %s\n", pvdc->display,
3839                                 leapbits[ul & 0x3]);
3840                         break;
3841
3842                 default:
3843                         fprintf(stderr, "unexpected vdc type %d for %s\n",
3844                                 pvdc->type, pvdc->tag);
3845                         break;
3846                 }
3847         }
3848 }
3849
3850
3851 /*
3852  * sysstats - implements ntpq -c sysstats modeled on ntpdc -c sysstats
3853  */
3854 static void
3855 sysstats(
3856         struct parse *pcmd,
3857         FILE *fp
3858         )
3859 {
3860     static vdc sysstats_vdc[] = {
3861         VDC_INIT("ss_uptime",           "uptime:               ", NTP_STR),
3862         VDC_INIT("ss_reset",            "sysstats reset:       ", NTP_STR),
3863         VDC_INIT("ss_received",         "packets received:     ", NTP_STR),
3864         VDC_INIT("ss_thisver",          "current version:      ", NTP_STR),
3865         VDC_INIT("ss_oldver",           "older version:        ", NTP_STR),
3866         VDC_INIT("ss_badformat",        "bad length or format: ", NTP_STR),
3867         VDC_INIT("ss_badauth",          "authentication failed:", NTP_STR),
3868         VDC_INIT("ss_declined",         "declined:             ", NTP_STR),
3869         VDC_INIT("ss_restricted",       "restricted:           ", NTP_STR),
3870         VDC_INIT("ss_limited",          "rate limited:         ", NTP_STR),
3871         VDC_INIT("ss_kodsent",          "KoD responses:        ", NTP_STR),
3872         VDC_INIT("ss_processed",        "processed for time:   ", NTP_STR),
3873 #if 0
3874         VDC_INIT("ss_lamport",          "Lamport violations:    ", NTP_STR),
3875         VDC_INIT("ss_tsrounding",       "bad timestamp rounding:", NTP_STR),
3876 #endif
3877         VDC_INIT(NULL,                  NULL,                     0)
3878     };
3879
3880         collect_display_vdc(0, sysstats_vdc, FALSE, fp);
3881 }
3882
3883
3884 /*
3885  * sysinfo - modeled on ntpdc's sysinfo
3886  */
3887 static void
3888 sysinfo(
3889         struct parse *pcmd,
3890         FILE *fp
3891         )
3892 {
3893     static vdc sysinfo_vdc[] = {
3894         VDC_INIT("peeradr",             "system peer:      ", NTP_ADP),
3895         VDC_INIT("peermode",            "system peer mode: ", NTP_MODE),
3896         VDC_INIT("leap",                "leap indicator:   ", NTP_2BIT),
3897         VDC_INIT("stratum",             "stratum:          ", NTP_STR),
3898         VDC_INIT("precision",           "log2 precision:   ", NTP_STR),
3899         VDC_INIT("rootdelay",           "root delay:       ", NTP_STR),
3900         VDC_INIT("rootdisp",            "root dispersion:  ", NTP_STR),
3901         VDC_INIT("refid",               "reference ID:     ", NTP_STR),
3902         VDC_INIT("reftime",             "reference time:   ", NTP_LFP),
3903         VDC_INIT("sys_jitter",          "system jitter:    ", NTP_STR),
3904         VDC_INIT("clk_jitter",          "clock jitter:     ", NTP_STR),
3905         VDC_INIT("clk_wander",          "clock wander:     ", NTP_STR),
3906         VDC_INIT("bcastdelay",          "broadcast delay:  ", NTP_STR),
3907         VDC_INIT("authdelay",           "symm. auth. delay:", NTP_STR),
3908         VDC_INIT(NULL,                  NULL,                 0)
3909     };
3910
3911         collect_display_vdc(0, sysinfo_vdc, TRUE, fp);
3912 }
3913
3914
3915 /*
3916  * kerninfo - modeled on ntpdc's kerninfo
3917  */
3918 static void
3919 kerninfo(
3920         struct parse *pcmd,
3921         FILE *fp
3922         )
3923 {
3924     static vdc kerninfo_vdc[] = {
3925         VDC_INIT("koffset",             "pll offset:          ", NTP_STR),
3926         VDC_INIT("kfreq",               "pll frequency:       ", NTP_STR),
3927         VDC_INIT("kmaxerr",             "maximum error:       ", NTP_STR),
3928         VDC_INIT("kesterr",             "estimated error:     ", NTP_STR),
3929         VDC_INIT("kstflags",            "kernel status:       ", NTP_STR),
3930         VDC_INIT("ktimeconst",          "pll time constant:   ", NTP_STR),
3931         VDC_INIT("kprecis",             "precision:           ", NTP_STR),
3932         VDC_INIT("kfreqtol",            "frequency tolerance: ", NTP_STR),
3933         VDC_INIT("kppsfreq",            "pps frequency:       ", NTP_STR),
3934         VDC_INIT("kppsstab",            "pps stability:       ", NTP_STR),
3935         VDC_INIT("kppsjitter",          "pps jitter:          ", NTP_STR),
3936         VDC_INIT("kppscalibdur",        "calibration interval ", NTP_STR),
3937         VDC_INIT("kppscalibs",          "calibration cycles:  ", NTP_STR),
3938         VDC_INIT("kppsjitexc",          "jitter exceeded:     ", NTP_STR),
3939         VDC_INIT("kppsstbexc",          "stability exceeded:  ", NTP_STR),
3940         VDC_INIT("kppscaliberrs",       "calibration errors:  ", NTP_STR),
3941         VDC_INIT(NULL,                  NULL,                    0)
3942     };
3943
3944         collect_display_vdc(0, kerninfo_vdc, TRUE, fp);
3945 }
3946
3947
3948 /*
3949  * monstats - implements ntpq -c monstats
3950  */
3951 static void
3952 monstats(
3953         struct parse *pcmd,
3954         FILE *fp
3955         )
3956 {
3957     static vdc monstats_vdc[] = {
3958         VDC_INIT("mru_enabled",         "enabled:            ", NTP_STR),
3959         VDC_INIT("mru_depth",           "addresses:          ", NTP_STR),
3960         VDC_INIT("mru_deepest",         "peak addresses:     ", NTP_STR),
3961         VDC_INIT("mru_maxdepth",        "maximum addresses:  ", NTP_STR),
3962         VDC_INIT("mru_mindepth",        "reclaim above count:", NTP_STR),
3963         VDC_INIT("mru_maxage",          "reclaim older than: ", NTP_STR),
3964         VDC_INIT("mru_mem",             "kilobytes:          ", NTP_STR),
3965         VDC_INIT("mru_maxmem",          "maximum kilobytes:  ", NTP_STR),
3966         VDC_INIT(NULL,                  NULL,                   0)
3967     };
3968
3969         collect_display_vdc(0, monstats_vdc, FALSE, fp);
3970 }
3971
3972
3973 /*
3974  * iostats - ntpq -c iostats - network input and output counters
3975  */
3976 static void
3977 iostats(
3978         struct parse *pcmd,
3979         FILE *fp
3980         )
3981 {
3982     static vdc iostats_vdc[] = {
3983         VDC_INIT("iostats_reset",       "time since reset:     ", NTP_STR),
3984         VDC_INIT("total_rbuf",          "receive buffers:      ", NTP_STR),
3985         VDC_INIT("free_rbuf",           "free receive buffers: ", NTP_STR),
3986         VDC_INIT("used_rbuf",           "used receive buffers: ", NTP_STR),
3987         VDC_INIT("rbuf_lowater",        "low water refills:    ", NTP_STR),
3988         VDC_INIT("io_dropped",          "dropped packets:      ", NTP_STR),
3989         VDC_INIT("io_ignored",          "ignored packets:      ", NTP_STR),
3990         VDC_INIT("io_received",         "received packets:     ", NTP_STR),
3991         VDC_INIT("io_sent",             "packets sent:         ", NTP_STR),
3992         VDC_INIT("io_sendfailed",       "packet send failures: ", NTP_STR),
3993         VDC_INIT("io_wakeups",          "input wakeups:        ", NTP_STR),
3994         VDC_INIT("io_goodwakeups",      "useful input wakeups: ", NTP_STR),
3995         VDC_INIT(NULL,                  NULL,                     0)
3996     };
3997
3998         collect_display_vdc(0, iostats_vdc, FALSE, fp);
3999 }
4000
4001
4002 /*
4003  * timerstats - ntpq -c timerstats - interval timer counters
4004  */
4005 static void
4006 timerstats(
4007         struct parse *pcmd,
4008         FILE *fp
4009         )
4010 {
4011     static vdc timerstats_vdc[] = {
4012         VDC_INIT("timerstats_reset",    "time since reset:  ", NTP_STR),
4013         VDC_INIT("timer_overruns",      "timer overruns:    ", NTP_STR),
4014         VDC_INIT("timer_xmts",          "calls to transmit: ", NTP_STR),
4015         VDC_INIT(NULL,                  NULL,                  0)
4016     };
4017
4018         collect_display_vdc(0, timerstats_vdc, FALSE, fp);
4019 }
4020
4021
4022 /*
4023  * authinfo - implements ntpq -c authinfo
4024  */
4025 static void
4026 authinfo(
4027         struct parse *pcmd,
4028         FILE *fp
4029         )
4030 {
4031     static vdc authinfo_vdc[] = {
4032         VDC_INIT("authreset",           "time since reset:", NTP_STR),
4033         VDC_INIT("authkeys",            "stored keys:     ", NTP_STR),
4034         VDC_INIT("authfreek",           "free keys:       ", NTP_STR),
4035         VDC_INIT("authklookups",        "key lookups:     ", NTP_STR),
4036         VDC_INIT("authknotfound",       "keys not found:  ", NTP_STR),
4037         VDC_INIT("authkuncached",       "uncached keys:   ", NTP_STR),
4038         VDC_INIT("authkexpired",        "expired keys:    ", NTP_STR),
4039         VDC_INIT("authencrypts",        "encryptions:     ", NTP_STR),
4040         VDC_INIT("authdecrypts",        "decryptions:     ", NTP_STR),
4041         VDC_INIT(NULL,                  NULL,                0)
4042     };
4043
4044         collect_display_vdc(0, authinfo_vdc, FALSE, fp);
4045 }
4046
4047
4048 /*
4049  * pstats - show statistics for a peer
4050  */
4051 static void
4052 pstats(
4053         struct parse *pcmd,
4054         FILE *fp
4055         )
4056 {
4057     static vdc pstats_vdc[] = {
4058         VDC_INIT("src",         "remote host:         ", NTP_ADD),
4059         VDC_INIT("dst",         "local address:       ", NTP_ADD),
4060         VDC_INIT("timerec",     "time last received:  ", NTP_STR),
4061         VDC_INIT("timer",       "time until next send:", NTP_STR),
4062         VDC_INIT("timereach",   "reachability change: ", NTP_STR),
4063         VDC_INIT("sent",        "packets sent:        ", NTP_STR),
4064         VDC_INIT("received",    "packets received:    ", NTP_STR),
4065         VDC_INIT("badauth",     "bad authentication:  ", NTP_STR),
4066         VDC_INIT("bogusorg",    "bogus origin:        ", NTP_STR),
4067         VDC_INIT("oldpkt",      "duplicate:           ", NTP_STR),
4068         VDC_INIT("seldisp",     "bad dispersion:      ", NTP_STR),
4069         VDC_INIT("selbroken",   "bad reference time:  ", NTP_STR),
4070         VDC_INIT("candidate",   "candidate order:     ", NTP_STR),
4071         VDC_INIT(NULL,          NULL,                    0)
4072     };
4073         associd_t associd;
4074
4075         associd = checkassocid(pcmd->argval[0].uval);
4076         if (0 == associd)
4077                 return;
4078
4079         collect_display_vdc(associd, pstats_vdc, TRUE, fp);
4080 }