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