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