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