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