1/*
2 * ntpq - query an NTP server using mode 6 commands
3 */
4
5#include <stdio.h>
6
7#include <ctype.h>
8#include <signal.h>
9#include <setjmp.h>
10#include <sys/types.h>
11#include <sys/time.h>
12
13#include "ntpq.h"
14#include "ntp_unixtime.h"
15#include "ntp_calendar.h"
16#include "ntp_io.h"
17#include "ntp_select.h"
18#include "ntp_stdlib.h"
19#include "ntp_assert.h"
20#include "ntp_lineedit.h"
21#include "ntp_debug.h"
22#include "isc/net.h"
23#include "isc/result.h"
24#include <ssl_applink.c>
25
26#include "ntpq-opts.h"
27
28#ifdef SYS_WINNT
29# include <Mswsock.h>
30# include <io.h>
31#endif /* SYS_WINNT */
32
33#ifdef SYS_VXWORKS
34				/* vxWorks needs mode flag -casey*/
35# define open(name, flags)   open(name, flags, 0777)
36# define SERVER_PORT_NUM     123
37#endif
38
39/* we use COMMAND as an autogen keyword */
40#ifdef COMMAND
41# undef COMMAND
42#endif
43
44/*
45 * Because we potentially understand a lot of commands we will run
46 * interactive if connected to a terminal.
47 */
48int interactive = 0;		/* set to 1 when we should prompt */
49const char *prompt = "ntpq> ";	/* prompt to ask him about */
50
51/*
52 * use old readvars behavior?  --old-rv processing in ntpq resets
53 * this value based on the presence or absence of --old-rv.  It is
54 * initialized to 1 here to maintain backward compatibility with
55 * libntpq clients such as ntpsnmpd, which are free to reset it as
56 * desired.
57 */
58int	old_rv = 1;
59
60
61/*
62 * for get_systime()
63 */
64s_char	sys_precision;		/* local clock precision (log2 s) */
65
66/*
67 * Keyid used for authenticated requests.  Obtained on the fly.
68 */
69u_long info_auth_keyid = 0;
70
71static	int	info_auth_keytype = NID_md5;	/* MD5 */
72static	size_t	info_auth_hashlen = 16;		/* MD5 */
73u_long	current_time;		/* needed by authkeys; not used */
74
75/*
76 * Flag which indicates we should always send authenticated requests
77 */
78int always_auth = 0;
79
80/*
81 * Flag which indicates raw mode output.
82 */
83int rawmode = 0;
84
85/*
86 * Packet version number we use
87 */
88u_char pktversion = NTP_OLDVERSION + 1;
89
90/*
91 * Don't jump if no set jmp.
92 */
93volatile int jump = 0;
94
95/*
96 * Format values
97 */
98#define	PADDING	0
99#define	TS	1	/* time stamp */
100#define	FL	2	/* l_fp type value */
101#define	FU	3	/* u_fp type value */
102#define	FS	4	/* s_fp type value */
103#define	UI	5	/* unsigned integer value */
104#define	SI	6	/* signed integer value */
105#define	HA	7	/* host address */
106#define	NA	8	/* network address */
107#define	ST	9	/* string value */
108#define	RF	10	/* refid (sometimes string, sometimes not) */
109#define	LP	11	/* leap (print in binary) */
110#define	OC	12	/* integer, print in octal */
111#define	MD	13	/* mode */
112#define	AR	14	/* array of times */
113#define FX	15	/* test flags */
114#define	EOV	255	/* end of table */
115
116
117/*
118 * System variable values.  The array can be indexed by
119 * the variable index to find the textual name.
120 */
121struct ctl_var sys_var[] = {
122	{ 0,		PADDING, "" },		/* 0 */
123	{ CS_LEAP,	LP,	"leap" },	/* 1 */
124	{ CS_STRATUM,	UI,	"stratum" },	/* 2 */
125	{ CS_PRECISION,	SI,	"precision" },	/* 3 */
126	{ CS_ROOTDELAY,	FS,	"rootdelay" },	/* 4 */
127	{ CS_ROOTDISPERSION, FU, "rootdispersion" }, /* 5 */
128	{ CS_REFID,	RF,	"refid" },	/* 6 */
129	{ CS_REFTIME,	TS,	"reftime" },	/* 7 */
130	{ CS_POLL,	UI,	"poll" },	/* 8 */
131	{ CS_PEERID,	UI,	"peer" },	/* 9 */
132	{ CS_OFFSET,	FL,	"offset" },	/* 10 */
133	{ CS_DRIFT,	FS,	"frequency" },	/* 11 */
134	{ CS_JITTER,	FU,	"jitter" },	/* 12 */
135	{ CS_CLOCK,	TS,	"clock" },	/* 13 */
136	{ CS_PROCESSOR,	ST,	"processor" },	/* 14 */
137	{ CS_SYSTEM,	ST,	"system" },	/* 15 */
138	{ CS_VERSION,	ST,	"version" },	/* 16 */
139	{ CS_STABIL,	FS,	"stability" },	/* 17 */
140	{ CS_VARLIST,	ST,	"sys_var_list" }, /* 18 */
141	{ 0,		EOV,	""	}
142};
143
144
145/*
146 * Peer variable list
147 */
148struct ctl_var peer_var[] = {
149	{ 0,		PADDING, "" },		/* 0 */
150	{ CP_CONFIG,	UI,	"config" },	/* 1 */
151	{ CP_AUTHENABLE, UI,	"authenable" },	/* 2 */
152	{ CP_AUTHENTIC,	UI,	"authentic" },	/* 3 */
153	{ CP_SRCADR,	HA,	"srcadr" },	/* 4 */
154	{ CP_SRCPORT,	UI,	"srcport" },	/* 5 */
155	{ CP_DSTADR,	NA,	"dstadr" },	/* 6 */
156	{ CP_DSTPORT,	UI,	"dstport" },	/* 7 */
157	{ CP_LEAP,	LP,	"leap" },	/* 8 */
158	{ CP_HMODE,	MD,	"hmode" },	/* 9 */
159	{ CP_STRATUM,	UI,	"stratum" },	/* 10 */
160	{ CP_PPOLL,	UI,	"ppoll" },	/* 11 */
161	{ CP_HPOLL,	UI,	"hpoll" },	/* 12 */
162	{ CP_PRECISION,	SI,	"precision" },	/* 13 */
163	{ CP_ROOTDELAY,	FS,	"rootdelay" },	/* 14 */
164	{ CP_ROOTDISPERSION, FU, "rootdisp" },	/* 15 */
165	{ CP_REFID,	RF,	"refid" },	/* 16 */
166	{ CP_REFTIME,	TS,	"reftime" },	/* 17 */
167	{ CP_ORG,	TS,	"org" },	/* 18 */
168	{ CP_REC,	TS,	"rec" },	/* 19 */
169	{ CP_XMT,	TS,	"xmt" },	/* 20 */
170	{ CP_REACH,	OC,	"reach" },	/* 21 */
171	{ CP_UNREACH,	UI,	"unreach" },	/* 22 */
172	{ CP_TIMER,	UI,	"timer" },	/* 23 */
173	{ CP_DELAY,	FS,	"delay" },	/* 24 */
174	{ CP_OFFSET,	FL,	"offset" },	/* 25 */
175	{ CP_JITTER,	FU,	"jitter" },	/* 26 */
176	{ CP_DISPERSION, FU,	"dispersion" },	/* 27 */
177	{ CP_KEYID,	UI,	"keyid" },	/* 28 */
178	{ CP_FILTDELAY,	AR,	"filtdelay" },	/* 29 */
179	{ CP_FILTOFFSET, AR,	"filtoffset" },	/* 30 */
180	{ CP_PMODE,	ST,	"pmode" },	/* 31 */
181	{ CP_RECEIVED,	UI,	"received" },	/* 32 */
182	{ CP_SENT,	UI,	"sent" },	/* 33 */
183	{ CP_FILTERROR,	AR,	"filtdisp" },	/* 34 */
184	{ CP_FLASH,     FX,	"flash" },	/* 35 */
185	{ CP_TTL,	UI,	"ttl" },	/* 36 */
186	/*
187	 * These are duplicate entries so that we can
188	 * process deviant version of the ntp protocol.
189	 */
190	{ CP_SRCADR,	HA,	"peeraddr" },	/* 4 */
191	{ CP_SRCPORT,	UI,	"peerport" },	/* 5 */
192	{ CP_PPOLL,	UI,	"peerpoll" },	/* 11 */
193	{ CP_HPOLL,	UI,	"hostpoll" },	/* 12 */
194	{ CP_FILTERROR,	AR,	"filterror" },	/* 34 */
195	{ 0,		EOV,	""	}
196};
197
198
199/*
200 * Clock variable list
201 */
202struct ctl_var clock_var[] = {
203	{ 0,		PADDING, "" },		/* 0 */
204	{ CC_TYPE,	UI,	"type" },	/* 1 */
205	{ CC_TIMECODE,	ST,	"timecode" },	/* 2 */
206	{ CC_POLL,	UI,	"poll" },	/* 3 */
207	{ CC_NOREPLY,	UI,	"noreply" },	/* 4 */
208	{ CC_BADFORMAT,	UI,	"badformat" },	/* 5 */
209	{ CC_BADDATA,	UI,	"baddata" },	/* 6 */
210	{ CC_FUDGETIME1, FL,	"fudgetime1" },	/* 7 */
211	{ CC_FUDGETIME2, FL,	"fudgetime2" },	/* 8 */
212	{ CC_FUDGEVAL1,	UI,	"stratum" },	/* 9 */
213	{ CC_FUDGEVAL2,	RF,	"refid" },	/* 10 */
214	{ CC_FLAGS,	UI,	"flags" },	/* 11 */
215	{ CC_DEVICE,	ST,	"device" },	/* 12 */
216	{ 0,		EOV,	""	}
217};
218
219
220/*
221 * flasher bits
222 */
223static const char *tstflagnames[] = {
224	"pkt_dup",		/* TEST1 */
225	"pkt_bogus",		/* TEST2 */
226	"pkt_unsync",		/* TEST3 */
227	"pkt_denied",		/* TEST4 */
228	"pkt_auth",		/* TEST5 */
229	"pkt_stratum",		/* TEST6 */
230	"pkt_header",		/* TEST7 */
231	"pkt_autokey",		/* TEST8 */
232	"pkt_crypto",		/* TEST9 */
233	"peer_stratum",		/* TEST10 */
234	"peer_dist",		/* TEST11 */
235	"peer_loop",		/* TEST12 */
236	"peer_unreach"		/* TEST13 */
237};
238
239
240/*
241 * Use getpassphrase() if configure.ac detected it, as Suns that
242 * have it truncate the password in getpass() to 8 characters.
243 */
244#ifdef HAVE_GETPASSPHRASE
245# define	getpass(str)	getpassphrase(str)
246#endif
247
248int		ntpqmain	(int,	char **);
249/*
250 * Built in command handler declarations
251 */
252static	int	openhost	(const char *);
253
254static	int	sendpkt		(void *, size_t);
255static	int	getresponse	(int, int, u_short *, int *, char **, int);
256static	int	sendrequest	(int, int, int, int, char *);
257static	char *	tstflags	(u_long);
258#ifndef BUILD_AS_LIB
259static	void	getcmds		(void);
260#ifndef SYS_WINNT
261static	RETSIGTYPE abortcmd	(int);
262#endif	/* SYS_WINNT */
263static	void	docmd		(const char *);
264static	void	tokenize	(const char *, char **, int *);
265static	int	getarg		(char *, int, arg_v *);
266#endif	/* BUILD_AS_LIB */
267static	int	findcmd		(char *, struct xcmd *, struct xcmd *, struct xcmd **);
268static	int	rtdatetolfp	(char *, l_fp *);
269static	int	decodearr	(char *, int *, l_fp *);
270static	void	help		(struct parse *, FILE *);
271#ifdef QSORT_USES_VOID_P
272static	int	helpsort	(const void *, const void *);
273#else
274static	int	helpsort	(char **, char **);
275#endif
276static	void	printusage	(struct xcmd *, FILE *);
277static	void	timeout		(struct parse *, FILE *);
278static	void	auth_delay	(struct parse *, FILE *);
279static	void	host		(struct parse *, FILE *);
280static	void	ntp_poll	(struct parse *, FILE *);
281static	void	keyid		(struct parse *, FILE *);
282static	void	keytype		(struct parse *, FILE *);
283static	void	passwd		(struct parse *, FILE *);
284static	void	hostnames	(struct parse *, FILE *);
285static	void	setdebug	(struct parse *, FILE *);
286static	void	quit		(struct parse *, FILE *);
287static	void	version		(struct parse *, FILE *);
288static	void	raw		(struct parse *, FILE *);
289static	void	cooked		(struct parse *, FILE *);
290static	void	authenticate	(struct parse *, FILE *);
291static	void	ntpversion	(struct parse *, FILE *);
292static	void	warning		(const char *, const char *, const char *);
293static	void	error		(const char *, const char *, const char *);
294static	u_long	getkeyid	(const char *);
295static	void	atoascii	(const char *, size_t, char *, size_t);
296static	void	makeascii	(int, char *, FILE *);
297static	void	cookedprint	(int, int, char *, int, int, FILE *);
298static	void	rawprint	(int, int, char *, int, int, FILE *);
299static	void	startoutput	(void);
300static	void	output		(FILE *, char *, char *);
301static	void	endoutput	(FILE *);
302static	void	outputarr	(FILE *, char *, int, l_fp *);
303#ifdef QSORT_USES_VOID_P
304static	int	assoccmp	(const void *, const void *);
305#else
306static	int	assoccmp	(struct association *, struct association *);
307#endif /* sgi || bsdi */
308void	ntpq_custom_opt_handler	(tOptions *, tOptDesc *);
309
310
311/*
312 * Built-in commands we understand
313 */
314struct xcmd builtins[] = {
315	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
316	  { "command", "", "", "" },
317	  "tell the use and syntax of commands" },
318	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
319	  { "command", "", "", "" },
320	  "tell the use and syntax of commands" },
321	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
322	  { "msec", "", "", "" },
323	  "set the primary receive time out" },
324	{ "delay",	auth_delay,	{ OPT|NTP_INT, NO, NO, NO },
325	  { "msec", "", "", "" },
326	  "set the delay added to encryption time stamps" },
327	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
328	  { "-4|-6", "hostname", "", "" },
329	  "specify the host whose NTP server we talk to" },
330	{ "poll",	ntp_poll,	{ OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
331	  { "n", "verbose", "", "" },
332	  "poll an NTP server in client mode `n' times" },
333	{ "passwd",	passwd,		{ NO, NO, NO, NO },
334	  { "", "", "", "" },
335	  "specify a password to use for authenticated requests"},
336	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
337	  { "yes|no", "", "", "" },
338	  "specify whether hostnames or net numbers are printed"},
339	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
340	  { "no|more|less", "", "", "" },
341	  "set/change debugging level" },
342	{ "quit",	quit,		{ NO, NO, NO, NO },
343	  { "", "", "", "" },
344	  "exit ntpq" },
345	{ "exit",	quit,		{ NO, NO, NO, NO },
346	  { "", "", "", "" },
347	  "exit ntpq" },
348	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
349	  { "key#", "", "", "" },
350	  "set keyid to use for authenticated requests" },
351	{ "version",	version,	{ NO, NO, NO, NO },
352	  { "", "", "", "" },
353	  "print version number" },
354	{ "raw",	raw,		{ NO, NO, NO, NO },
355	  { "", "", "", "" },
356	  "do raw mode variable output" },
357	{ "cooked",	cooked,		{ NO, NO, NO, NO },
358	  { "", "", "", "" },
359	  "do cooked mode variable output" },
360	{ "authenticate", authenticate,	{ OPT|NTP_STR, NO, NO, NO },
361	  { "yes|no", "", "", "" },
362	  "always authenticate requests to this server" },
363	{ "ntpversion",	ntpversion,	{ OPT|NTP_UINT, NO, NO, NO },
364	  { "version number", "", "", "" },
365	  "set the NTP version number to use for requests" },
366	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
367	  { "key type (md5|des)", "", "", "" },
368	  "set key type to use for authenticated requests (des|md5)" },
369	{ 0,		0,		{ NO, NO, NO, NO },
370	  { "", "", "", "" }, "" }
371};
372
373
374/*
375 * Default values we use.
376 */
377#define	DEFHOST		"localhost"	/* default host name */
378#define	DEFTIMEOUT	(5)		/* 5 second time out */
379#define	DEFSTIMEOUT	(2)		/* 2 second time out after first */
380#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
381#define	LENHOSTNAME	256		/* host name is 256 characters long */
382#define	MAXCMDS		100		/* maximum commands on cmd line */
383#define	MAXHOSTS	200		/* maximum hosts on cmd line */
384#define	MAXLINE		512		/* maximum line length */
385#define	MAXTOKENS	(1+MAXARGS+2)	/* maximum number of usable tokens */
386#define	MAXVARLEN	256		/* maximum length of a variable name */
387#define	MAXVALLEN	400		/* maximum length of a variable value */
388#define	MAXOUTLINE	72		/* maximum length of an output line */
389#define SCREENWIDTH	76		/* nominal screen width in columns */
390
391/*
392 * Some variables used and manipulated locally
393 */
394struct sock_timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
395struct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
396l_fp delay_time;				/* delay time */
397char currenthost[LENHOSTNAME];			/* current host name */
398struct sockaddr_in hostaddr = { 0 };		/* host address */
399int showhostnames = 1;				/* show host names by default */
400
401int ai_fam_templ;				/* address family */
402int ai_fam_default;				/* default address family */
403SOCKET sockfd;					/* fd socket is opened on */
404int havehost = 0;				/* set to 1 when host open */
405int s_port = 0;
406struct servent *server_entry = NULL;		/* server entry for ntp */
407
408
409/*
410 * Sequence number used for requests.  It is incremented before
411 * it is used.
412 */
413u_short sequence;
414
415/*
416 * Holds data returned from queries.  Declare buffer long to be sure of
417 * alignment.
418 */
419#define	MAXFRAGS	24		/* maximum number of fragments */
420#define	DATASIZE	(MAXFRAGS*480)	/* maximum amount of data */
421long pktdata[DATASIZE/sizeof(long)];
422
423/*
424 * Holds association data for use with the &n operator.
425 */
426struct association assoc_cache[MAXASSOC];
427int numassoc = 0;		/* number of cached associations */
428
429/*
430 * For commands typed on the command line (with the -c option)
431 */
432int numcmds = 0;
433const char *ccmds[MAXCMDS];
434#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
435
436/*
437 * When multiple hosts are specified.
438 */
439int numhosts = 0;
440const char *chosts[MAXHOSTS];
441#define	ADDHOST(cp)	if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
442
443/*
444 * Error codes for internal use
445 */
446#define	ERR_UNSPEC		256
447#define	ERR_INCOMPLETE	257
448#define	ERR_TIMEOUT		258
449#define	ERR_TOOMUCH		259
450
451/*
452 * Macro definitions we use
453 */
454#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
455#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
456#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
457
458/*
459 * Jump buffer for longjumping back to the command level
460 */
461jmp_buf interrupt_buf;
462
463/*
464 * Points at file being currently printed into
465 */
466FILE *current_output;
467
468/*
469 * Command table imported from ntpdc_ops.c
470 */
471extern struct xcmd opcmds[];
472
473char *progname;
474volatile int debug;
475
476#ifdef NO_MAIN_ALLOWED
477#ifndef BUILD_AS_LIB
478CALL(ntpq,"ntpq",ntpqmain);
479
480void clear_globals(void)
481{
482	extern int ntp_optind;
483	showhostnames = 0;	/* don'tshow host names by default */
484	ntp_optind = 0;
485	server_entry = NULL;	/* server entry for ntp */
486	havehost = 0;		/* set to 1 when host open */
487	numassoc = 0;		/* number of cached associations */
488	numcmds = 0;
489	numhosts = 0;
490}
491#endif /* !BUILD_AS_LIB */
492#endif /* NO_MAIN_ALLOWED */
493
494/*
495 * main - parse arguments and handle options
496 */
497#ifndef NO_MAIN_ALLOWED
498int
499main(
500	int argc,
501	char *argv[]
502	)
503{
504	return ntpqmain(argc, argv);
505}
506#endif
507
508#ifndef BUILD_AS_LIB
509int
510ntpqmain(
511	int argc,
512	char *argv[]
513	)
514{
515	extern int ntp_optind;
516
517#ifdef SYS_VXWORKS
518	clear_globals();
519	taskPrioritySet(taskIdSelf(), 100 );
520#endif
521
522	delay_time.l_ui = 0;
523	delay_time.l_uf = DEFDELAY;
524
525	init_lib();	/* sets up ipv4_works, ipv6_works */
526	ssl_applink();
527
528	/* Check to see if we have IPv6. Otherwise default to IPv4 */
529	if (!ipv6_works)
530		ai_fam_default = AF_INET;
531
532	progname = argv[0];
533
534	{
535		int optct = optionProcess(&ntpqOptions, argc, argv);
536		argc -= optct;
537		argv += optct;
538	}
539
540	/*
541	 * Process options other than -c and -p, which are specially
542	 * handled by ntpq_custom_opt_handler().
543	 */
544
545	debug = DESC(DEBUG_LEVEL).optOccCt;
546
547	if (HAVE_OPT(IPV4))
548		ai_fam_templ = AF_INET;
549	else if (HAVE_OPT(IPV6))
550		ai_fam_templ = AF_INET6;
551	else
552		ai_fam_templ = ai_fam_default;
553
554	if (HAVE_OPT(INTERACTIVE))
555		interactive = 1;
556
557	if (HAVE_OPT(NUMERIC))
558		showhostnames = 0;
559
560	old_rv = HAVE_OPT(OLD_RV);
561
562#if 0
563	while ((c = ntp_getopt(argc, argv, "46c:dinp")) != EOF)
564	    switch (c) {
565		case '4':
566		    ai_fam_templ = AF_INET;
567		    break;
568		case '6':
569		    ai_fam_templ = AF_INET6;
570		    break;
571		case 'c':
572		    ADDCMD(ntp_optarg);
573		    break;
574		case 'd':
575		    ++debug;
576		    break;
577		case 'i':
578		    interactive = 1;
579		    break;
580		case 'n':
581		    showhostnames = 0;
582		    break;
583		case 'p':
584		    ADDCMD("peers");
585		    break;
586		default:
587		    errflg++;
588		    break;
589	    }
590	if (errflg) {
591		(void) fprintf(stderr,
592			       "usage: %s [-46dinp] [-c cmd] host ...\n",
593			       progname);
594		exit(2);
595	}
596#endif
597	NTP_INSIST(ntp_optind <= argc);
598	if (ntp_optind == argc) {
599		ADDHOST(DEFHOST);
600	} else {
601		for (; ntp_optind < argc; ntp_optind++)
602			ADDHOST(argv[ntp_optind]);
603	}
604
605	if (numcmds == 0 && interactive == 0
606	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
607		interactive = 1;
608	}
609
610#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
611	if (interactive)
612	    (void) signal_no_reset(SIGINT, abortcmd);
613#endif /* SYS_WINNT */
614
615	if (numcmds == 0) {
616		(void) openhost(chosts[0]);
617		getcmds();
618	} else {
619		int ihost;
620		int icmd;
621
622		for (ihost = 0; ihost < numhosts; ihost++) {
623			if (openhost(chosts[ihost]))
624				for (icmd = 0; icmd < numcmds; icmd++)
625					docmd(ccmds[icmd]);
626		}
627	}
628#ifdef SYS_WINNT
629	WSACleanup();
630#endif /* SYS_WINNT */
631	return 0;
632}
633#endif /* !BUILD_AS_LIB */
634
635/*
636 * openhost - open a socket to a host
637 */
638static	int
639openhost(
640	const char *hname
641	)
642{
643	char temphost[LENHOSTNAME];
644	int a_info, i;
645	struct addrinfo hints, *ai = NULL;
646	register const char *cp;
647	char name[LENHOSTNAME];
648	char service[5];
649
650	/*
651	 * We need to get by the [] if they were entered
652	 */
653
654	cp = hname;
655
656	if (*cp == '[') {
657		cp++;
658		for (i = 0; *cp && *cp != ']'; cp++, i++)
659			name[i] = *cp;
660		if (*cp == ']') {
661			name[i] = '\0';
662			hname = name;
663		} else {
664			return 0;
665		}
666	}
667
668	/*
669	 * First try to resolve it as an ip address and if that fails,
670	 * do a fullblown (dns) lookup. That way we only use the dns
671	 * when it is needed and work around some implementations that
672	 * will return an "IPv4-mapped IPv6 address" address if you
673	 * give it an IPv4 address to lookup.
674	 */
675	strcpy(service, "ntp");
676	memset((char *)&hints, 0, sizeof(struct addrinfo));
677	hints.ai_family = ai_fam_templ;
678	hints.ai_protocol = IPPROTO_UDP;
679	hints.ai_socktype = SOCK_DGRAM;
680	hints.ai_flags = AI_NUMERICHOST;
681
682	a_info = getaddrinfo(hname, service, &hints, &ai);
683	if (a_info == EAI_NONAME
684#ifdef EAI_NODATA
685	    || a_info == EAI_NODATA
686#endif
687	   ) {
688		hints.ai_flags = AI_CANONNAME;
689#ifdef AI_ADDRCONFIG
690		hints.ai_flags |= AI_ADDRCONFIG;
691#endif
692		a_info = getaddrinfo(hname, service, &hints, &ai);
693	}
694#ifdef AI_ADDRCONFIG
695	/* Some older implementations don't like AI_ADDRCONFIG. */
696	if (a_info == EAI_BADFLAGS) {
697		hints.ai_flags = AI_CANONNAME;
698		a_info = getaddrinfo(hname, service, &hints, &ai);
699	}
700#endif
701	if (a_info != 0) {
702		(void) fprintf(stderr, "%s\n", gai_strerror(a_info));
703		return 0;
704	}
705
706	if (ai->ai_canonname == NULL) {
707		strncpy(temphost,
708			stoa((sockaddr_u *)ai->ai_addr),
709			LENHOSTNAME);
710
711	} else {
712		strncpy(temphost, ai->ai_canonname, LENHOSTNAME);
713	}
714	temphost[LENHOSTNAME-1] = '\0';
715
716	if (debug > 2)
717		printf("Opening host %s\n", temphost);
718
719	if (havehost == 1) {
720		if (debug > 2)
721			printf("Closing old host %s\n", currenthost);
722		(void) closesocket(sockfd);
723		havehost = 0;
724	}
725	(void) strcpy(currenthost, temphost);
726
727	/* port maps to the same location in both families */
728	s_port = ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port;
729#ifdef SYS_VXWORKS
730	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
731	if (ai->ai_family == AF_INET)
732		*(struct sockaddr_in *)&hostaddr=
733			*((struct sockaddr_in *)ai->ai_addr);
734	else
735		*(struct sockaddr_in6 *)&hostaddr=
736			*((struct sockaddr_in6 *)ai->ai_addr);
737#endif /* SYS_VXWORKS */
738
739#ifdef SYS_WINNT
740	{
741		int optionValue = SO_SYNCHRONOUS_NONALERT;
742		int err;
743
744		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
745				 (char *)&optionValue, sizeof(optionValue));
746		if (err) {
747			err = WSAGetLastError();
748			fprintf(stderr,
749				"setsockopt(SO_SYNCHRONOUS_NONALERT) "
750				"error: %s\n", strerror(err));
751			exit(1);
752		}
753	}
754#endif /* SYS_WINNT */
755
756	sockfd = socket(ai->ai_family, SOCK_DGRAM, 0);
757	if (sockfd == INVALID_SOCKET) {
758		error("socket", "", "");
759	}
760
761
762#ifdef NEED_RCVBUF_SLOP
763# ifdef SO_RCVBUF
764	{ int rbufsize = DATASIZE + 2048;	/* 2K for slop */
765	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
766		       &rbufsize, sizeof(int)) == -1)
767	    error("setsockopt", "", "");
768	}
769# endif
770#endif
771
772#ifdef SYS_VXWORKS
773	if (connect(sockfd, (struct sockaddr *)&hostaddr,
774		    sizeof(hostaddr)) == -1)
775#else
776	if (connect(sockfd, (struct sockaddr *)ai->ai_addr,
777		    ai->ai_addrlen) == -1)
778#endif /* SYS_VXWORKS */
779	    error("connect", "", "");
780	if (a_info == 0)
781		freeaddrinfo(ai);
782	havehost = 1;
783	return 1;
784}
785
786
787/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
788/*
789 * sendpkt - send a packet to the remote host
790 */
791static int
792sendpkt(
793	void *	xdata,
794	size_t	xdatalen
795	)
796{
797	if (debug >= 3)
798		printf("Sending %u octets\n", xdatalen);
799
800	if (send(sockfd, xdata, (size_t)xdatalen, 0) == -1) {
801		warning("write to %s failed", currenthost, "");
802		return -1;
803	}
804
805	if (debug >= 4) {
806		int first = 8;
807		char *cdata = xdata;
808
809		printf("Packet data:\n");
810		while (xdatalen-- > 0) {
811			if (first-- == 0) {
812				printf("\n");
813				first = 7;
814			}
815			printf(" %02x", *cdata++ & 0xff);
816		}
817		printf("\n");
818	}
819	return 0;
820}
821
822
823
824/*
825 * getresponse - get a (series of) response packet(s) and return the data
826 */
827static int
828getresponse(
829	int opcode,
830	int associd,
831	u_short *rstatus,
832	int *rsize,
833	char **rdata,
834	int timeo
835	)
836{
837	struct ntp_control rpkt;
838	struct sock_timeval tvo;
839	u_short offsets[MAXFRAGS+1];
840	u_short counts[MAXFRAGS+1];
841	u_short offset;
842	u_short count;
843	int numfrags;
844	int seenlastfrag;
845	int shouldbesize;
846	fd_set fds;
847	int n;
848
849	/*
850	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
851	 * back in response to the request.  We peel the data out of
852	 * each packet and collect it in one long block.  When the last
853	 * packet in the sequence is received we'll know how much data we
854	 * should have had.  Note we use one long time out, should reconsider.
855	 */
856	*rsize = 0;
857	if (rstatus)
858	    *rstatus = 0;
859	*rdata = (char *)pktdata;
860
861	numfrags = 0;
862	seenlastfrag = 0;
863
864	FD_ZERO(&fds);
865
866	/*
867	 * Loop until we have an error or a complete response.  Nearly all
868	 * aths to loop again use continue.
869	 */
870	for (;;) {
871
872		if (numfrags == 0)
873		    tvo = tvout;
874		else
875		    tvo = tvsout;
876
877		FD_SET(sockfd, &fds);
878		n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
879
880		if (n == -1) {
881			warning("select fails", "", "");
882			return -1;
883		}
884		if (n == 0) {
885			/*
886			 * Timed out.  Return what we have
887			 */
888			if (numfrags == 0) {
889				if (timeo)
890				    (void) fprintf(stderr,
891						   "%s: timed out, nothing received\n",
892						   currenthost);
893				return ERR_TIMEOUT;
894			} else {
895				if (timeo)
896				    (void) fprintf(stderr,
897						   "%s: timed out with incomplete data\n",
898						   currenthost);
899				if (debug) {
900					printf("Received fragments:\n");
901					for (n = 0; n < numfrags; n++)
902					    printf("%4d %d\n", offsets[n],
903						   counts[n]);
904					if (seenlastfrag)
905					    printf("last fragment received\n");
906					else
907					    printf("last fragment not received\n");
908				}
909				return ERR_INCOMPLETE;
910			}
911		}
912
913		n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
914		if (n == -1) {
915			warning("read", "", "");
916			return -1;
917		}
918
919		if (debug >= 4) {
920			int len = n, first = 8;
921			char *data = (char *)&rpkt;
922
923			printf("Packet data:\n");
924			while (len-- > 0) {
925				if (first-- == 0) {
926					printf("\n");
927					first = 7;
928				}
929				printf(" %02x", *data++ & 0xff);
930			}
931			printf("\n");
932		}
933
934		/*
935		 * Check for format errors.  Bug proofing.
936		 */
937		if (n < CTL_HEADER_LEN) {
938			if (debug)
939			    printf("Short (%d byte) packet received\n", n);
940			continue;
941		}
942		if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
943		    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
944			if (debug)
945			    printf("Packet received with version %d\n",
946				   PKT_VERSION(rpkt.li_vn_mode));
947			continue;
948		}
949		if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
950			if (debug)
951			    printf("Packet received with mode %d\n",
952				   PKT_MODE(rpkt.li_vn_mode));
953			continue;
954		}
955		if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
956			if (debug)
957			    printf("Received request packet, wanted response\n");
958			continue;
959		}
960
961		/*
962		 * Check opcode and sequence number for a match.
963		 * Could be old data getting to us.
964		 */
965		if (ntohs(rpkt.sequence) != sequence) {
966			if (debug)
967			    printf(
968				    "Received sequnce number %d, wanted %d\n",
969				    ntohs(rpkt.sequence), sequence);
970			continue;
971		}
972		if (CTL_OP(rpkt.r_m_e_op) != opcode) {
973			if (debug)
974			    printf(
975				    "Received opcode %d, wanted %d (sequence number okay)\n",
976				    CTL_OP(rpkt.r_m_e_op), opcode);
977			continue;
978		}
979
980		/*
981		 * Check the error code.  If non-zero, return it.
982		 */
983		if (CTL_ISERROR(rpkt.r_m_e_op)) {
984			int errcode;
985
986			errcode = (ntohs(rpkt.status) >> 8) & 0xff;
987			if (debug && CTL_ISMORE(rpkt.r_m_e_op)) {
988				printf("Error code %d received on not-final packet\n",
989				       errcode);
990			}
991			if (errcode == CERR_UNSPEC)
992			    return ERR_UNSPEC;
993			return errcode;
994		}
995
996		/*
997		 * Check the association ID to make sure it matches what
998		 * we sent.
999		 */
1000		if (ntohs(rpkt.associd) != associd) {
1001			if (debug)
1002			    printf("Association ID %d doesn't match expected %d\n",
1003				   ntohs(rpkt.associd), associd);
1004			/*
1005			 * Hack for silly fuzzballs which, at the time of writing,
1006			 * return an assID of sys.peer when queried for system variables.
1007			 */
1008#ifdef notdef
1009			continue;
1010#endif
1011		}
1012
1013		/*
1014		 * Collect offset and count.  Make sure they make sense.
1015		 */
1016		offset = ntohs(rpkt.offset);
1017		count = ntohs(rpkt.count);
1018
1019		/*
1020		 * validate received payload size is padded to next 32-bit
1021		 * boundary and no smaller than claimed by rpkt.count
1022		 */
1023		if (n & 0x3) {
1024			if (debug)
1025				printf("Response packet not padded, "
1026					"size = %d\n", n);
1027			continue;
1028		}
1029
1030		shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
1031
1032		if (n < shouldbesize) {
1033			printf("Response packet claims %u octets "
1034				"payload, above %d received\n",
1035				count,
1036				n - CTL_HEADER_LEN
1037				);
1038			return ERR_INCOMPLETE;
1039		}
1040
1041		if (debug >= 3 && shouldbesize > n) {
1042			u_int32 key;
1043			u_int32 *lpkt;
1044			int maclen;
1045
1046			/*
1047			 * Usually we ignore authentication, but for debugging purposes
1048			 * we watch it here.
1049			 */
1050			/* round to 8 octet boundary */
1051			shouldbesize = (shouldbesize + 7) & ~7;
1052
1053			maclen = n - shouldbesize;
1054			if (maclen >= MIN_MAC_LEN) {
1055				printf(
1056					"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1057					n, shouldbesize, maclen);
1058				lpkt = (u_int32 *)&rpkt;
1059				printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1060				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1061				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1062				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1063				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1064				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1065				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1066				key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1067				printf("Authenticated with keyid %lu\n", (u_long)key);
1068				if (key != 0 && key != info_auth_keyid) {
1069					printf("We don't know that key\n");
1070				} else {
1071					if (authdecrypt(key, (u_int32 *)&rpkt,
1072					    n - maclen, maclen)) {
1073						printf("Auth okay!\n");
1074					} else {
1075						printf("Auth failed!\n");
1076					}
1077				}
1078			}
1079		}
1080
1081		if (debug >= 2)
1082		    printf("Got packet, size = %d\n", n);
1083		if ((int)count > (n - CTL_HEADER_LEN)) {
1084			if (debug)
1085				printf("Received count of %d octets, "
1086					"data in packet is %d\n",
1087					count, n-CTL_HEADER_LEN);
1088			continue;
1089		}
1090		if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1091			if (debug)
1092			    printf("Received count of 0 in non-final fragment\n");
1093			continue;
1094		}
1095		if (offset + count > sizeof(pktdata)) {
1096			if (debug)
1097			    printf("Offset %d, count %d, too big for buffer\n",
1098				   offset, count);
1099			return ERR_TOOMUCH;
1100		}
1101		if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1102			if (debug)
1103			    printf("Received second last fragment packet\n");
1104			continue;
1105		}
1106
1107		/*
1108		 * So far, so good.  Record this fragment, making sure it doesn't
1109		 * overlap anything.
1110		 */
1111		if (debug >= 2)
1112		    printf("Packet okay\n");;
1113
1114		if (numfrags > (MAXFRAGS - 1)) {
1115			if (debug)
1116			    printf("Number of fragments exceeds maximum\n");
1117			return ERR_TOOMUCH;
1118		}
1119
1120		/*
1121		 * Find the position for the fragment relative to any
1122		 * previously received.
1123		 */
1124		for (n = 0;
1125		     n < numfrags && offsets[n] < offset;
1126		     n++) {
1127			/* empty body */ ;
1128		}
1129
1130		if (n < numfrags && offset == offsets[n]) {
1131			if (debug)
1132				printf("duplicate %u octets at %u "
1133					"ignored, prior %u at %u\n",
1134					count,
1135					offset,
1136					counts[n],
1137					offsets[n]
1138					);
1139			continue;
1140		}
1141
1142		if (n > 0 && (offsets[n-1] + counts[n-1]) > offset) {
1143			if (debug)
1144				printf("received frag at %u overlaps "
1145					"with %u octet frag at %u\n",
1146					offset,
1147					counts[n-1],
1148					offsets[n-1]
1149					);
1150			continue;
1151		}
1152
1153		if (n < numfrags && (offset + count) > offsets[n]) {
1154			if (debug)
1155				printf("received %u octet frag at %u "
1156					"overlaps with frag at %u\n",
1157					count,
1158					offset,
1159					offsets[n]
1160					);
1161			continue;
1162		}
1163
1164		{
1165			register int i;
1166
1167			for (i = numfrags; i > n; i--) {
1168				offsets[i] = offsets[i-1];
1169				counts[i] = counts[i-1];
1170			}
1171		}
1172		offsets[n] = offset;
1173		counts[n] = count;
1174		numfrags++;
1175
1176		/*
1177		 * Got that stuffed in right.  Figure out if this was the last.
1178		 * Record status info out of the last packet.
1179		 */
1180		if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1181			seenlastfrag = 1;
1182			if (rstatus != 0)
1183			    *rstatus = ntohs(rpkt.status);
1184		}
1185
1186		/*
1187		 * Copy the data into the data buffer.
1188		 */
1189		memmove((char *)pktdata + offset, (char *)rpkt.data, count);
1190
1191		/*
1192		 * If we've seen the last fragment, look for holes in the sequence.
1193		 * If there aren't any, we're done.
1194		 */
1195		if (seenlastfrag && offsets[0] == 0) {
1196			for (n = 1; n < numfrags; n++) {
1197				if (offsets[n-1] + counts[n-1] != offsets[n])
1198					break;
1199			}
1200			if (n == numfrags) {
1201				*rsize = offsets[numfrags-1] + counts[numfrags-1];
1202				return 0;
1203			}
1204		}
1205	}  /* giant for (;;) collecting response packets */
1206}  /* getresponse() */
1207
1208
1209/*
1210 * sendrequest - format and send a request packet
1211 */
1212static int
1213sendrequest(
1214	int opcode,
1215	int associd,
1216	int auth,
1217	int qsize,
1218	char *qdata
1219	)
1220{
1221	struct ntp_control qpkt;
1222	int	pktsize;
1223	u_long	key_id;
1224	char	pass_prompt[32];
1225	char *	pass;
1226	int	maclen;
1227
1228	/*
1229	 * Check to make sure the data will fit in one packet
1230	 */
1231	if (qsize > CTL_MAX_DATA_LEN) {
1232		fprintf(stderr,
1233			"***Internal error!  qsize (%d) too large\n",
1234			qsize);
1235		return 1;
1236	}
1237
1238	/*
1239	 * Fill in the packet
1240	 */
1241	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1242	qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
1243	qpkt.sequence = htons(sequence);
1244	qpkt.status = 0;
1245	qpkt.associd = htons((u_short)associd);
1246	qpkt.offset = 0;
1247	qpkt.count = htons((u_short)qsize);
1248
1249	pktsize = CTL_HEADER_LEN;
1250
1251	/*
1252	 * If we have data, copy and pad it out to a 32-bit boundary.
1253	 */
1254	if (qsize > 0) {
1255		memcpy(qpkt.data, qdata, (size_t)qsize);
1256		pktsize += qsize;
1257		while (pktsize & (sizeof(u_int32) - 1)) {
1258			qpkt.data[qsize++] = 0;
1259			pktsize++;
1260		}
1261	}
1262
1263	/*
1264	 * If it isn't authenticated we can just send it.  Otherwise
1265	 * we're going to have to think about it a little.
1266	 */
1267	if (!auth && !always_auth) {
1268		return sendpkt(&qpkt, pktsize);
1269	}
1270
1271	/*
1272	 * Pad out packet to a multiple of 8 octets to be sure
1273	 * receiver can handle it.
1274	 */
1275	while (pktsize & 7) {
1276		qpkt.data[qsize++] = 0;
1277		pktsize++;
1278	}
1279
1280	/*
1281	 * Get the keyid and the password if we don't have one.
1282	 */
1283	if (info_auth_keyid == 0) {
1284		key_id = getkeyid("Keyid: ");
1285		if (key_id == 0 || key_id > NTP_MAXKEY) {
1286			fprintf(stderr,
1287				"Invalid key identifier\n");
1288			return 1;
1289		}
1290		info_auth_keyid = key_id;
1291	}
1292	if (!authistrusted(info_auth_keyid)) {
1293		snprintf(pass_prompt, sizeof(pass_prompt),
1294			 "%s Password: ",
1295			 keytype_name(info_auth_keytype));
1296		pass = getpass(pass_prompt);
1297		if ('\0' == pass[0]) {
1298			fprintf(stderr, "Invalid password\n");
1299			return 1;
1300		}
1301		authusekey(info_auth_keyid, info_auth_keytype,
1302			   (u_char *)pass);
1303		authtrust(info_auth_keyid, 1);
1304	}
1305
1306	/*
1307	 * Do the encryption.
1308	 */
1309	maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1310	if (!maclen) {
1311		fprintf(stderr, "Key not found\n");
1312		return 1;
1313	} else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1314		fprintf(stderr,
1315			"%d octet MAC, %u expected with %u octet digest\n",
1316			maclen, (info_auth_hashlen + sizeof(keyid_t)),
1317			info_auth_hashlen);
1318		return 1;
1319	}
1320
1321	return sendpkt((char *)&qpkt, pktsize + maclen);
1322}
1323
1324
1325/*
1326 * doquery - send a request and process the response
1327 */
1328int
1329doquery(
1330	int opcode,
1331	int associd,
1332	int auth,
1333	int qsize,
1334	char *qdata,
1335	u_short *rstatus,
1336	int *rsize,
1337	char **rdata
1338	)
1339{
1340	int res;
1341	int done;
1342
1343	/*
1344	 * Check to make sure host is open
1345	 */
1346	if (!havehost) {
1347		(void) fprintf(stderr, "***No host open, use `host' command\n");
1348		return -1;
1349	}
1350
1351	done = 0;
1352	sequence++;
1353
1354    again:
1355	/*
1356	 * send a request
1357	 */
1358	res = sendrequest(opcode, associd, auth, qsize, qdata);
1359	if (res != 0)
1360	    return res;
1361
1362	/*
1363	 * Get the response.  If we got a standard error, print a message
1364	 */
1365	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
1366
1367	if (res > 0) {
1368		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
1369			if (res == ERR_INCOMPLETE) {
1370				/*
1371				 * better bump the sequence so we don't
1372				 * get confused about differing fragments.
1373				 */
1374				sequence++;
1375			}
1376			done = 1;
1377			goto again;
1378		}
1379		if (numhosts > 1)
1380			(void) fprintf(stderr, "server=%s ", currenthost);
1381		switch(res) {
1382		    case CERR_BADFMT:
1383			(void) fprintf(stderr,
1384			    "***Server reports a bad format request packet\n");
1385			break;
1386		    case CERR_PERMISSION:
1387			(void) fprintf(stderr,
1388			    "***Server disallowed request (authentication?)\n");
1389			break;
1390		    case CERR_BADOP:
1391			(void) fprintf(stderr,
1392			    "***Server reports a bad opcode in request\n");
1393			break;
1394		    case CERR_BADASSOC:
1395			(void) fprintf(stderr,
1396			    "***Association ID %d unknown to server\n",associd);
1397			break;
1398		    case CERR_UNKNOWNVAR:
1399			(void) fprintf(stderr,
1400			    "***A request variable unknown to the server\n");
1401			break;
1402		    case CERR_BADVALUE:
1403			(void) fprintf(stderr,
1404			    "***Server indicates a request variable was bad\n");
1405			break;
1406		    case ERR_UNSPEC:
1407			(void) fprintf(stderr,
1408			    "***Server returned an unspecified error\n");
1409			break;
1410		    case ERR_TIMEOUT:
1411			(void) fprintf(stderr, "***Request timed out\n");
1412			break;
1413		    case ERR_INCOMPLETE:
1414			(void) fprintf(stderr,
1415			    "***Response from server was incomplete\n");
1416			break;
1417		    case ERR_TOOMUCH:
1418			(void) fprintf(stderr,
1419			    "***Buffer size exceeded for returned data\n");
1420			break;
1421		    default:
1422			(void) fprintf(stderr,
1423			    "***Server returns unknown error code %d\n", res);
1424			break;
1425		}
1426	}
1427	return res;
1428}
1429
1430
1431#ifndef BUILD_AS_LIB
1432/*
1433 * getcmds - read commands from the standard input and execute them
1434 */
1435static void
1436getcmds(void)
1437{
1438	char *	line;
1439	int	count;
1440
1441	ntp_readline_init(interactive ? prompt : NULL);
1442
1443	for (;;) {
1444		line = ntp_readline(&count);
1445		if (NULL == line)
1446			break;
1447		docmd(line);
1448		free(line);
1449	}
1450
1451	ntp_readline_uninit();
1452}
1453#endif /* !BUILD_AS_LIB */
1454
1455
1456#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
1457/*
1458 * abortcmd - catch interrupts and abort the current command
1459 */
1460static RETSIGTYPE
1461abortcmd(
1462	int sig
1463	)
1464{
1465	if (current_output == stdout)
1466	    (void) fflush(stdout);
1467	putc('\n', stderr);
1468	(void) fflush(stderr);
1469	if (jump) longjmp(interrupt_buf, 1);
1470}
1471#endif	/* !SYS_WINNT && !BUILD_AS_LIB */
1472
1473
1474#ifndef	BUILD_AS_LIB
1475/*
1476 * docmd - decode the command line and execute a command
1477 */
1478static void
1479docmd(
1480	const char *cmdline
1481	)
1482{
1483	char *tokens[1+MAXARGS+2];
1484	struct parse pcmd;
1485	int ntok;
1486	static int i;
1487	struct xcmd *xcmd;
1488
1489	/*
1490	 * Tokenize the command line.  If nothing on it, return.
1491	 */
1492	tokenize(cmdline, tokens, &ntok);
1493	if (ntok == 0)
1494	    return;
1495
1496	/*
1497	 * Find the appropriate command description.
1498	 */
1499	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1500	if (i == 0) {
1501		(void) fprintf(stderr, "***Command `%s' unknown\n",
1502			       tokens[0]);
1503		return;
1504	} else if (i >= 2) {
1505		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
1506			       tokens[0]);
1507		return;
1508	}
1509
1510	/*
1511	 * Save the keyword, then walk through the arguments, interpreting
1512	 * as we go.
1513	 */
1514	pcmd.keyword = tokens[0];
1515	pcmd.nargs = 0;
1516	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
1517		if ((i+1) >= ntok) {
1518			if (!(xcmd->arg[i] & OPT)) {
1519				printusage(xcmd, stderr);
1520				return;
1521			}
1522			break;
1523		}
1524		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1525			break;
1526		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1527			return;
1528		pcmd.nargs++;
1529	}
1530
1531	i++;
1532	if (i < ntok && *tokens[i] == '>') {
1533		char *fname;
1534
1535		if (*(tokens[i]+1) != '\0')
1536			fname = tokens[i]+1;
1537		else if ((i+1) < ntok)
1538			fname = tokens[i+1];
1539		else {
1540			(void) fprintf(stderr, "***No file for redirect\n");
1541			return;
1542		}
1543
1544		current_output = fopen(fname, "w");
1545		if (current_output == NULL) {
1546			(void) fprintf(stderr, "***Error opening %s: ", fname);
1547			perror("");
1548			return;
1549		}
1550		i = 1;		/* flag we need a close */
1551	} else {
1552		current_output = stdout;
1553		i = 0;		/* flag no close */
1554	}
1555
1556	if (interactive && setjmp(interrupt_buf)) {
1557		jump = 0;
1558		return;
1559	} else {
1560		jump++;
1561		(xcmd->handler)(&pcmd, current_output);
1562		jump = 0;	/* HMS: 961106: was after fclose() */
1563		if (i) (void) fclose(current_output);
1564	}
1565}
1566
1567
1568/*
1569 * tokenize - turn a command line into tokens
1570 *
1571 * SK: Modified to allow a quoted string
1572 *
1573 * HMS: If the first character of the first token is a ':' then (after
1574 * eating inter-token whitespace) the 2nd token is the rest of the line.
1575 */
1576
1577static void
1578tokenize(
1579	const char *line,
1580	char **tokens,
1581	int *ntok
1582	)
1583{
1584	register const char *cp;
1585	register char *sp;
1586	static char tspace[MAXLINE];
1587
1588	sp = tspace;
1589	cp = line;
1590	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1591		tokens[*ntok] = sp;
1592
1593		/* Skip inter-token whitespace */
1594		while (ISSPACE(*cp))
1595		    cp++;
1596
1597		/* If we're at EOL we're done */
1598		if (ISEOL(*cp))
1599		    break;
1600
1601		/* If this is the 2nd token and the first token begins
1602		 * with a ':', then just grab to EOL.
1603		 */
1604
1605		if (*ntok == 1 && tokens[0][0] == ':') {
1606			do {
1607				*sp++ = *cp++;
1608			} while (!ISEOL(*cp));
1609		}
1610
1611		/* Check if this token begins with a double quote.
1612		 * If yes, continue reading till the next double quote
1613		 */
1614		else if (*cp == '\"') {
1615			++cp;
1616			do {
1617				*sp++ = *cp++;
1618			} while ((*cp != '\"') && !ISEOL(*cp));
1619			/* HMS: a missing closing " should be an error */
1620		}
1621		else {
1622			do {
1623				*sp++ = *cp++;
1624			} while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1625			/* HMS: Why check for a " in the previous line? */
1626		}
1627
1628		*sp++ = '\0';
1629	}
1630}
1631
1632
1633/*
1634 * getarg - interpret an argument token
1635 */
1636static int
1637getarg(
1638	char *str,
1639	int code,
1640	arg_v *argp
1641	)
1642{
1643	int isneg;
1644	char *cp, *np;
1645	static const char *digits = "0123456789";
1646
1647	switch (code & ~OPT) {
1648	    case NTP_STR:
1649		argp->string = str;
1650		break;
1651	    case NTP_ADD:
1652		if (!getnetnum(str, &(argp->netnum), (char *)0, 0)) {
1653			return 0;
1654		}
1655		break;
1656	    case NTP_INT:
1657	    case NTP_UINT:
1658		isneg = 0;
1659		np = str;
1660		if (*np == '&') {
1661			np++;
1662			isneg = atoi(np);
1663			if (isneg <= 0) {
1664				(void) fprintf(stderr,
1665					       "***Association value `%s' invalid/undecodable\n", str);
1666				return 0;
1667			}
1668			if (isneg > numassoc) {
1669				if (numassoc == 0) {
1670					(void) fprintf(stderr,
1671						       "***Association for `%s' unknown (max &%d)\n",
1672						       str, numassoc);
1673					return 0;
1674				} else {
1675					isneg = numassoc;
1676				}
1677			}
1678			argp->uval = assoc_cache[isneg-1].assid;
1679			break;
1680		}
1681
1682		if (*np == '-') {
1683			np++;
1684			isneg = 1;
1685		}
1686
1687		argp->uval = 0;
1688		do {
1689			cp = strchr(digits, *np);
1690			if (cp == NULL) {
1691				(void) fprintf(stderr,
1692					       "***Illegal integer value %s\n", str);
1693				return 0;
1694			}
1695			argp->uval *= 10;
1696			argp->uval += (cp - digits);
1697		} while (*(++np) != '\0');
1698
1699		if (isneg) {
1700			if ((code & ~OPT) == NTP_UINT) {
1701				(void) fprintf(stderr,
1702					       "***Value %s should be unsigned\n", str);
1703				return 0;
1704			}
1705			argp->ival = -argp->ival;
1706		}
1707		break;
1708	     case IP_VERSION:
1709		if (!strcmp("-6", str))
1710			argp->ival = 6 ;
1711		else if (!strcmp("-4", str))
1712			argp->ival = 4 ;
1713		else {
1714			(void) fprintf(stderr,
1715			    "***Version must be either 4 or 6\n");
1716			return 0;
1717		}
1718		break;
1719	}
1720
1721	return 1;
1722}
1723#endif	/* !BUILD_AS_LIB */
1724
1725
1726/*
1727 * findcmd - find a command in a command description table
1728 */
1729static int
1730findcmd(
1731	register char *str,
1732	struct xcmd *clist1,
1733	struct xcmd *clist2,
1734	struct xcmd **cmd
1735	)
1736{
1737	register struct xcmd *cl;
1738	register int clen;
1739	int nmatch;
1740	struct xcmd *nearmatch = NULL;
1741	struct xcmd *clist;
1742
1743	clen = strlen(str);
1744	nmatch = 0;
1745	if (clist1 != 0)
1746	    clist = clist1;
1747	else if (clist2 != 0)
1748	    clist = clist2;
1749	else
1750	    return 0;
1751
1752    again:
1753	for (cl = clist; cl->keyword != 0; cl++) {
1754		/* do a first character check, for efficiency */
1755		if (*str != *(cl->keyword))
1756		    continue;
1757		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1758			/*
1759			 * Could be extact match, could be approximate.
1760			 * Is exact if the length of the keyword is the
1761			 * same as the str.
1762			 */
1763			if (*((cl->keyword) + clen) == '\0') {
1764				*cmd = cl;
1765				return 1;
1766			}
1767			nmatch++;
1768			nearmatch = cl;
1769		}
1770	}
1771
1772	/*
1773	 * See if there is more to do.  If so, go again.  Sorry about the
1774	 * goto, too much looking at BSD sources...
1775	 */
1776	if (clist == clist1 && clist2 != 0) {
1777		clist = clist2;
1778		goto again;
1779	}
1780
1781	/*
1782	 * If we got extactly 1 near match, use it, else return number
1783	 * of matches.
1784	 */
1785	if (nmatch == 1) {
1786		*cmd = nearmatch;
1787		return 1;
1788	}
1789	return nmatch;
1790}
1791
1792
1793/*
1794 * getnetnum - given a host name, return its net number
1795 *	       and (optional) full name
1796 */
1797int
1798getnetnum(
1799	const char *hname,
1800	sockaddr_u *num,
1801	char *fullhost,
1802	int af
1803	)
1804{
1805	int sockaddr_len;
1806	struct addrinfo hints, *ai = NULL;
1807
1808	sockaddr_len = SIZEOF_SOCKADDR(af);
1809	memset(&hints, 0, sizeof(hints));
1810	hints.ai_flags = AI_CANONNAME;
1811#ifdef AI_ADDRCONFIG
1812	hints.ai_flags |= AI_ADDRCONFIG;
1813#endif
1814
1815	/* decodenetnum works with addresses only */
1816	if (decodenetnum(hname, num)) {
1817		if (fullhost != 0) {
1818			getnameinfo((struct sockaddr *)num, sockaddr_len,
1819					fullhost, sizeof(fullhost), NULL, 0,
1820					NI_NUMERICHOST);
1821		}
1822		return 1;
1823	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1824		memmove((char *)num, ai->ai_addr, ai->ai_addrlen);
1825		if (ai->ai_canonname != 0)
1826		    (void) strcpy(fullhost, ai->ai_canonname);
1827		return 1;
1828	} else {
1829		(void) fprintf(stderr, "***Can't find host %s\n", hname);
1830		return 0;
1831	}
1832	/*NOTREACHED*/
1833}
1834
1835/*
1836 * nntohost - convert network number to host name.  This routine enforces
1837 *	       the showhostnames setting.
1838 */
1839char *
1840nntohost(
1841	sockaddr_u *netnum
1842	)
1843{
1844	if (!showhostnames)
1845		return stoa(netnum);
1846	else if (ISREFCLOCKADR(netnum))
1847		return refnumtoa(netnum);
1848	else
1849		return socktohost(netnum);
1850}
1851
1852
1853/*
1854 * rtdatetolfp - decode an RT-11 date into an l_fp
1855 */
1856static int
1857rtdatetolfp(
1858	char *str,
1859	l_fp *lfp
1860	)
1861{
1862	register char *cp;
1863	register int i;
1864	struct calendar cal;
1865	char buf[4];
1866	static const char *months[12] = {
1867		"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1868		"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1869	};
1870
1871	cal.yearday = 0;
1872
1873	/*
1874	 * An RT-11 date looks like:
1875	 *
1876	 * d[d]-Mth-y[y] hh:mm:ss
1877	 *
1878	 * (No docs, but assume 4-digit years are also legal...)
1879	 *
1880	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
1881	 */
1882	cp = str;
1883	if (!isdigit((int)*cp)) {
1884		if (*cp == '-') {
1885			/*
1886			 * Catch special case
1887			 */
1888			L_CLR(lfp);
1889			return 1;
1890		}
1891		return 0;
1892	}
1893
1894	cal.monthday = (u_char) (*cp++ - '0');	/* ascii dependent */
1895	if (isdigit((int)*cp)) {
1896		cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
1897		cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
1898	}
1899
1900	if (*cp++ != '-')
1901	    return 0;
1902
1903	for (i = 0; i < 3; i++)
1904	    buf[i] = *cp++;
1905	buf[3] = '\0';
1906
1907	for (i = 0; i < 12; i++)
1908	    if (STREQ(buf, months[i]))
1909		break;
1910	if (i == 12)
1911	    return 0;
1912	cal.month = (u_char)(i + 1);
1913
1914	if (*cp++ != '-')
1915	    return 0;
1916
1917	if (!isdigit((int)*cp))
1918	    return 0;
1919	cal.year = (u_short)(*cp++ - '0');
1920	if (isdigit((int)*cp)) {
1921		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1922		cal.year = (u_short)(*cp++ - '0');
1923	}
1924	if (isdigit((int)*cp)) {
1925		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1926		cal.year = (u_short)(cal.year + *cp++ - '0');
1927	}
1928	if (isdigit((int)*cp)) {
1929		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
1930		cal.year = (u_short)(cal.year + *cp++ - '0');
1931	}
1932
1933	/*
1934	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
1935	 */
1936	if (cal.year == 0) {
1937		L_CLR(lfp);
1938		return 1;
1939	}
1940
1941	if (*cp++ != ' ' || !isdigit((int)*cp))
1942	    return 0;
1943	cal.hour = (u_char)(*cp++ - '0');
1944	if (isdigit((int)*cp)) {
1945		cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
1946		cal.hour = (u_char)(cal.hour + *cp++ - '0');
1947	}
1948
1949	if (*cp++ != ':' || !isdigit((int)*cp))
1950	    return 0;
1951	cal.minute = (u_char)(*cp++ - '0');
1952	if (isdigit((int)*cp)) {
1953		cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
1954		cal.minute = (u_char)(cal.minute + *cp++ - '0');
1955	}
1956
1957	if (*cp++ != ':' || !isdigit((int)*cp))
1958	    return 0;
1959	cal.second = (u_char)(*cp++ - '0');
1960	if (isdigit((int)*cp)) {
1961		cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
1962		cal.second = (u_char)(cal.second + *cp++ - '0');
1963	}
1964
1965	/*
1966	 * For RT-11, 1972 seems to be the pivot year
1967	 */
1968	if (cal.year < 72)
1969		cal.year += 2000;
1970	if (cal.year < 100)
1971		cal.year += 1900;
1972
1973	lfp->l_ui = caltontp(&cal);
1974	lfp->l_uf = 0;
1975	return 1;
1976}
1977
1978
1979/*
1980 * decodets - decode a timestamp into an l_fp format number, with
1981 *	      consideration of fuzzball formats.
1982 */
1983int
1984decodets(
1985	char *str,
1986	l_fp *lfp
1987	)
1988{
1989	/*
1990	 * If it starts with a 0x, decode as hex.
1991	 */
1992	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
1993		return hextolfp(str+2, lfp);
1994
1995	/*
1996	 * If it starts with a '"', try it as an RT-11 date.
1997	 */
1998	if (*str == '"') {
1999		register char *cp = str+1;
2000		register char *bp;
2001		char buf[30];
2002
2003		bp = buf;
2004		while (*cp != '"' && *cp != '\0' && bp < &buf[29])
2005			*bp++ = *cp++;
2006		*bp = '\0';
2007		return rtdatetolfp(buf, lfp);
2008	}
2009
2010	/*
2011	 * Might still be hex.  Check out the first character.  Talk
2012	 * about heuristics!
2013	 */
2014	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2015		return hextolfp(str, lfp);
2016
2017	/*
2018	 * Try it as a decimal.  If this fails, try as an unquoted
2019	 * RT-11 date.  This code should go away eventually.
2020	 */
2021	if (atolfp(str, lfp))
2022		return 1;
2023
2024	return rtdatetolfp(str, lfp);
2025}
2026
2027
2028/*
2029 * decodetime - decode a time value.  It should be in milliseconds
2030 */
2031int
2032decodetime(
2033	char *str,
2034	l_fp *lfp
2035	)
2036{
2037	return mstolfp(str, lfp);
2038}
2039
2040
2041/*
2042 * decodeint - decode an integer
2043 */
2044int
2045decodeint(
2046	char *str,
2047	long *val
2048	)
2049{
2050	if (*str == '0') {
2051		if (*(str+1) == 'x' || *(str+1) == 'X')
2052		    return hextoint(str+2, (u_long *)val);
2053		return octtoint(str, (u_long *)val);
2054	}
2055	return atoint(str, val);
2056}
2057
2058
2059/*
2060 * decodeuint - decode an unsigned integer
2061 */
2062int
2063decodeuint(
2064	char *str,
2065	u_long *val
2066	)
2067{
2068	if (*str == '0') {
2069		if (*(str + 1) == 'x' || *(str + 1) == 'X')
2070			return (hextoint(str + 2, val));
2071		return (octtoint(str, val));
2072	}
2073	return (atouint(str, val));
2074}
2075
2076
2077/*
2078 * decodearr - decode an array of time values
2079 */
2080static int
2081decodearr(
2082	char *str,
2083	int *narr,
2084	l_fp *lfparr
2085	)
2086{
2087	register char *cp, *bp;
2088	register l_fp *lfp;
2089	char buf[60];
2090
2091	lfp = lfparr;
2092	cp = str;
2093	*narr = 0;
2094
2095	while (*narr < 8) {
2096		while (isspace((int)*cp))
2097		    cp++;
2098		if (*cp == '\0')
2099		    break;
2100
2101		bp = buf;
2102		while (!isspace((int)*cp) && *cp != '\0')
2103		    *bp++ = *cp++;
2104		*bp++ = '\0';
2105
2106		if (!decodetime(buf, lfp))
2107		    return 0;
2108		(*narr)++;
2109		lfp++;
2110	}
2111	return 1;
2112}
2113
2114
2115/*
2116 * Finally, the built in command handlers
2117 */
2118
2119/*
2120 * help - tell about commands, or details of a particular command
2121 */
2122static void
2123help(
2124	struct parse *pcmd,
2125	FILE *fp
2126	)
2127{
2128	struct xcmd *xcp = NULL;	/* quiet warning */
2129	char *cmd;
2130	const char *list[100];
2131	int word, words;
2132	int row, rows;
2133	int col, cols;
2134
2135	if (pcmd->nargs == 0) {
2136		words = 0;
2137		for (xcp = builtins; xcp->keyword != 0; xcp++) {
2138			if (*(xcp->keyword) != '?')
2139				list[words++] = xcp->keyword;
2140		}
2141		for (xcp = opcmds; xcp->keyword != 0; xcp++)
2142			list[words++] = xcp->keyword;
2143
2144		qsort(
2145#ifdef QSORT_USES_VOID_P
2146		    (void *)
2147#else
2148		    (char *)
2149#endif
2150			(list), (size_t)(words), sizeof(char *), helpsort);
2151		col = 0;
2152		for (word = 0; word < words; word++) {
2153		 	int length = strlen(list[word]);
2154			if (col < length) {
2155				col = length;
2156			}
2157		}
2158
2159		cols = SCREENWIDTH / ++col;
2160		rows = (words + cols - 1) / cols;
2161
2162		(void) fprintf(fp, "ntpq commands:\n");
2163
2164		for (row = 0; row < rows; row++) {
2165			for (word = row; word < words; word += rows) {
2166				(void) fprintf(fp, "%-*.*s", col,
2167						   col-1, list[word]);
2168			}
2169			(void) fprintf(fp, "\n");
2170		}
2171	} else {
2172		cmd = pcmd->argval[0].string;
2173		words = findcmd(cmd, builtins, opcmds, &xcp);
2174		if (words == 0) {
2175			(void) fprintf(stderr,
2176				       "Command `%s' is unknown\n", cmd);
2177			return;
2178		} else if (words >= 2) {
2179			(void) fprintf(stderr,
2180				       "Command `%s' is ambiguous\n", cmd);
2181			return;
2182		}
2183		(void) fprintf(fp, "function: %s\n", xcp->comment);
2184		printusage(xcp, fp);
2185	}
2186}
2187
2188
2189/*
2190 * helpsort - do hostname qsort comparisons
2191 */
2192#ifdef QSORT_USES_VOID_P
2193static int
2194helpsort(
2195	const void *t1,
2196	const void *t2
2197	)
2198{
2199	char const * const * name1 = (char const * const *)t1;
2200	char const * const * name2 = (char const * const *)t2;
2201
2202	return strcmp(*name1, *name2);
2203}
2204
2205#else
2206static int
2207helpsort(
2208	char **name1,
2209	char **name2
2210	)
2211{
2212	return strcmp(*name1, *name2);
2213}
2214#endif
2215
2216/*
2217 * printusage - print usage information for a command
2218 */
2219static void
2220printusage(
2221	struct xcmd *xcp,
2222	FILE *fp
2223	)
2224{
2225	register int i;
2226
2227	(void) fprintf(fp, "usage: %s", xcp->keyword);
2228	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
2229		if (xcp->arg[i] & OPT)
2230		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
2231		else
2232		    (void) fprintf(fp, " %s", xcp->desc[i]);
2233	}
2234	(void) fprintf(fp, "\n");
2235}
2236
2237
2238/*
2239 * timeout - set time out time
2240 */
2241static void
2242timeout(
2243	struct parse *pcmd,
2244	FILE *fp
2245	)
2246{
2247	int val;
2248
2249	if (pcmd->nargs == 0) {
2250		val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
2251		(void) fprintf(fp, "primary timeout %d ms\n", val);
2252	} else {
2253		tvout.tv_sec = pcmd->argval[0].uval / 1000;
2254		tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
2255			* 1000;
2256	}
2257}
2258
2259
2260/*
2261 * auth_delay - set delay for auth requests
2262 */
2263static void
2264auth_delay(
2265	struct parse *pcmd,
2266	FILE *fp
2267	)
2268{
2269	int isneg;
2270	u_long val;
2271
2272	if (pcmd->nargs == 0) {
2273		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
2274		(void) fprintf(fp, "delay %lu ms\n", val);
2275	} else {
2276		if (pcmd->argval[0].ival < 0) {
2277			isneg = 1;
2278			val = (u_long)(-pcmd->argval[0].ival);
2279		} else {
2280			isneg = 0;
2281			val = (u_long)pcmd->argval[0].ival;
2282		}
2283
2284		delay_time.l_ui = val / 1000;
2285		val %= 1000;
2286		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
2287
2288		if (isneg)
2289		    L_NEG(&delay_time);
2290	}
2291}
2292
2293
2294/*
2295 * host - set the host we are dealing with.
2296 */
2297static void
2298host(
2299	struct parse *pcmd,
2300	FILE *fp
2301	)
2302{
2303	int i;
2304
2305	if (pcmd->nargs == 0) {
2306		if (havehost)
2307			(void) fprintf(fp, "current host is %s\n",
2308					   currenthost);
2309		else
2310			(void) fprintf(fp, "no current host\n");
2311		return;
2312	}
2313
2314	i = 0;
2315	ai_fam_templ = ai_fam_default;
2316	if (pcmd->nargs == 2) {
2317		if (!strcmp("-4", pcmd->argval[i].string))
2318			ai_fam_templ = AF_INET;
2319		else if (!strcmp("-6", pcmd->argval[i].string))
2320			ai_fam_templ = AF_INET6;
2321		else {
2322			if (havehost)
2323				(void) fprintf(fp,
2324					       "current host remains %s\n",
2325					       currenthost);
2326			else
2327				(void) fprintf(fp, "still no current host\n");
2328			return;
2329		}
2330		i = 1;
2331	}
2332	if (openhost(pcmd->argval[i].string)) {
2333		(void) fprintf(fp, "current host set to %s\n", currenthost);
2334		numassoc = 0;
2335	} else {
2336		if (havehost)
2337			(void) fprintf(fp,
2338				       "current host remains %s\n",
2339				       currenthost);
2340		else
2341			(void) fprintf(fp, "still no current host\n");
2342	}
2343}
2344
2345
2346/*
2347 * poll - do one (or more) polls of the host via NTP
2348 */
2349/*ARGSUSED*/
2350static void
2351ntp_poll(
2352	struct parse *pcmd,
2353	FILE *fp
2354	)
2355{
2356	(void) fprintf(fp, "poll not implemented yet\n");
2357}
2358
2359
2360/*
2361 * keyid - get a keyid to use for authenticating requests
2362 */
2363static void
2364keyid(
2365	struct parse *pcmd,
2366	FILE *fp
2367	)
2368{
2369	if (pcmd->nargs == 0) {
2370		if (info_auth_keyid == 0)
2371		    (void) fprintf(fp, "no keyid defined\n");
2372		else
2373		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
2374	} else {
2375		/* allow zero so that keyid can be cleared. */
2376		if(pcmd->argval[0].uval > NTP_MAXKEY)
2377		    (void) fprintf(fp, "Invalid key identifier\n");
2378		info_auth_keyid = pcmd->argval[0].uval;
2379	}
2380}
2381
2382/*
2383 * keytype - get type of key to use for authenticating requests
2384 */
2385static void
2386keytype(
2387	struct parse *pcmd,
2388	FILE *fp
2389	)
2390{
2391	const char *	digest_name;
2392	size_t		digest_len;
2393	int		key_type;
2394
2395	if (!pcmd->nargs) {
2396		fprintf(fp, "keytype is %s with %u octet digests\n",
2397			keytype_name(info_auth_keytype),
2398			info_auth_hashlen);
2399		return;
2400	}
2401
2402	digest_name = pcmd->argval[0].string;
2403	digest_len = 0;
2404	key_type = keytype_from_text(digest_name, &digest_len);
2405
2406	if (!key_type) {
2407		fprintf(fp, "keytype must be 'md5'%s\n",
2408#ifdef OPENSSL
2409			" or a digest type provided by OpenSSL");
2410#else
2411			"");
2412#endif
2413		return;
2414	}
2415
2416	info_auth_keytype = key_type;
2417	info_auth_hashlen = digest_len;
2418}
2419
2420
2421/*
2422 * passwd - get an authentication key
2423 */
2424/*ARGSUSED*/
2425static void
2426passwd(
2427	struct parse *pcmd,
2428	FILE *fp
2429	)
2430{
2431	char *pass;
2432
2433	if (info_auth_keyid == 0) {
2434		int u_keyid = getkeyid("Keyid: ");
2435		if (u_keyid == 0 || u_keyid > NTP_MAXKEY) {
2436			(void)fprintf(fp, "Invalid key identifier\n");
2437			return;
2438		}
2439		info_auth_keyid = u_keyid;
2440	}
2441	pass = getpass("MD5 Password: ");
2442	if (*pass == '\0')
2443		(void) fprintf(fp, "Password unchanged\n");
2444	else {
2445		authusekey(info_auth_keyid, info_auth_keytype, (u_char *)pass);
2446		authtrust(info_auth_keyid, 1);
2447	}
2448}
2449
2450
2451/*
2452 * hostnames - set the showhostnames flag
2453 */
2454static void
2455hostnames(
2456	struct parse *pcmd,
2457	FILE *fp
2458	)
2459{
2460	if (pcmd->nargs == 0) {
2461		if (showhostnames)
2462		    (void) fprintf(fp, "hostnames being shown\n");
2463		else
2464		    (void) fprintf(fp, "hostnames not being shown\n");
2465	} else {
2466		if (STREQ(pcmd->argval[0].string, "yes"))
2467		    showhostnames = 1;
2468		else if (STREQ(pcmd->argval[0].string, "no"))
2469		    showhostnames = 0;
2470		else
2471		    (void)fprintf(stderr, "What?\n");
2472	}
2473}
2474
2475
2476
2477/*
2478 * setdebug - set/change debugging level
2479 */
2480static void
2481setdebug(
2482	struct parse *pcmd,
2483	FILE *fp
2484	)
2485{
2486	if (pcmd->nargs == 0) {
2487		(void) fprintf(fp, "debug level is %d\n", debug);
2488		return;
2489	} else if (STREQ(pcmd->argval[0].string, "no")) {
2490		debug = 0;
2491	} else if (STREQ(pcmd->argval[0].string, "more")) {
2492		debug++;
2493	} else if (STREQ(pcmd->argval[0].string, "less")) {
2494		debug--;
2495	} else {
2496		(void) fprintf(fp, "What?\n");
2497		return;
2498	}
2499	(void) fprintf(fp, "debug level set to %d\n", debug);
2500}
2501
2502
2503/*
2504 * quit - stop this nonsense
2505 */
2506/*ARGSUSED*/
2507static void
2508quit(
2509	struct parse *pcmd,
2510	FILE *fp
2511	)
2512{
2513	if (havehost)
2514	    closesocket(sockfd);	/* cleanliness next to godliness */
2515	exit(0);
2516}
2517
2518
2519/*
2520 * version - print the current version number
2521 */
2522/*ARGSUSED*/
2523static void
2524version(
2525	struct parse *pcmd,
2526	FILE *fp
2527	)
2528{
2529
2530	(void) fprintf(fp, "%s\n", Version);
2531	return;
2532}
2533
2534
2535/*
2536 * raw - set raw mode output
2537 */
2538/*ARGSUSED*/
2539static void
2540raw(
2541	struct parse *pcmd,
2542	FILE *fp
2543	)
2544{
2545	rawmode = 1;
2546	(void) fprintf(fp, "Output set to raw\n");
2547}
2548
2549
2550/*
2551 * cooked - set cooked mode output
2552 */
2553/*ARGSUSED*/
2554static void
2555cooked(
2556	struct parse *pcmd,
2557	FILE *fp
2558	)
2559{
2560	rawmode = 0;
2561	(void) fprintf(fp, "Output set to cooked\n");
2562	return;
2563}
2564
2565
2566/*
2567 * authenticate - always authenticate requests to this host
2568 */
2569static void
2570authenticate(
2571	struct parse *pcmd,
2572	FILE *fp
2573	)
2574{
2575	if (pcmd->nargs == 0) {
2576		if (always_auth) {
2577			(void) fprintf(fp,
2578				       "authenticated requests being sent\n");
2579		} else
2580		    (void) fprintf(fp,
2581				   "unauthenticated requests being sent\n");
2582	} else {
2583		if (STREQ(pcmd->argval[0].string, "yes")) {
2584			always_auth = 1;
2585		} else if (STREQ(pcmd->argval[0].string, "no")) {
2586			always_auth = 0;
2587		} else
2588		    (void)fprintf(stderr, "What?\n");
2589	}
2590}
2591
2592
2593/*
2594 * ntpversion - choose the NTP version to use
2595 */
2596static void
2597ntpversion(
2598	struct parse *pcmd,
2599	FILE *fp
2600	)
2601{
2602	if (pcmd->nargs == 0) {
2603		(void) fprintf(fp,
2604			       "NTP version being claimed is %d\n", pktversion);
2605	} else {
2606		if (pcmd->argval[0].uval < NTP_OLDVERSION
2607		    || pcmd->argval[0].uval > NTP_VERSION) {
2608			(void) fprintf(stderr, "versions %d to %d, please\n",
2609				       NTP_OLDVERSION, NTP_VERSION);
2610		} else {
2611			pktversion = (u_char) pcmd->argval[0].uval;
2612		}
2613	}
2614}
2615
2616
2617/*
2618 * warning - print a warning message
2619 */
2620static void
2621warning(
2622	const char *fmt,
2623	const char *st1,
2624	const char *st2
2625	)
2626{
2627	(void) fprintf(stderr, "%s: ", progname);
2628	(void) fprintf(stderr, fmt, st1, st2);
2629	(void) fprintf(stderr, ": ");
2630	perror("");
2631}
2632
2633
2634/*
2635 * error - print a message and exit
2636 */
2637static void
2638error(
2639	const char *fmt,
2640	const char *st1,
2641	const char *st2
2642	)
2643{
2644	warning(fmt, st1, st2);
2645	exit(1);
2646}
2647
2648/*
2649 * getkeyid - prompt the user for a keyid to use
2650 */
2651static u_long
2652getkeyid(
2653	const char *keyprompt
2654	)
2655{
2656	register char *p;
2657	register int c;
2658	FILE *fi;
2659	char pbuf[20];
2660
2661#ifndef SYS_WINNT
2662	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
2663#else
2664	if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
2665#endif /* SYS_WINNT */
2666		fi = stdin;
2667	    else
2668		setbuf(fi, (char *)NULL);
2669	fprintf(stderr, "%s", keyprompt); fflush(stderr);
2670	for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
2671		if (p < &pbuf[18])
2672		    *p++ = (char)c;
2673	}
2674	*p = '\0';
2675	if (fi != stdin)
2676	    fclose(fi);
2677	if (strcmp(pbuf, "0") == 0)
2678	    return 0;
2679
2680	return (u_long) atoi(pbuf);
2681}
2682
2683
2684/*
2685 * atoascii - printable-ize possibly ascii data using the character
2686 *	      transformations cat -v uses.
2687 */
2688static void
2689atoascii(
2690	const char *in,
2691	size_t in_octets,
2692	char *out,
2693	size_t out_octets
2694	)
2695{
2696	register const u_char *	pchIn;
2697		 const u_char *	pchInLimit;
2698	register u_char *	pchOut;
2699	register u_char		c;
2700
2701	pchIn = (const u_char *)in;
2702	pchInLimit = pchIn + in_octets;
2703	pchOut = (u_char *)out;
2704
2705	if (NULL == pchIn) {
2706		if (0 < out_octets)
2707			*pchOut = '\0';
2708		return;
2709	}
2710
2711#define	ONEOUT(c)					\
2712do {							\
2713	if (0 == --out_octets) {			\
2714		*pchOut = '\0';				\
2715		return;					\
2716	}						\
2717	*pchOut++ = (c);				\
2718} while (0)
2719
2720	for (	; pchIn < pchInLimit; pchIn++) {
2721		c = *pchIn;
2722		if ('\0' == c)
2723			break;
2724		if (c & 0x80) {
2725			ONEOUT('M');
2726			ONEOUT('-');
2727			c &= 0x7f;
2728		}
2729		if (c < ' ') {
2730			ONEOUT('^');
2731			ONEOUT((u_char)(c + '@'));
2732		} else if (0x7f == c) {
2733			ONEOUT('^');
2734			ONEOUT('?');
2735		} else
2736			ONEOUT(c);
2737	}
2738	ONEOUT('\0');
2739
2740#undef ONEOUT
2741}
2742
2743
2744/*
2745 * makeascii - print possibly ascii data using the character
2746 *	       transformations that cat -v uses.
2747 */
2748static void
2749makeascii(
2750	int length,
2751	char *data,
2752	FILE *fp
2753	)
2754{
2755	register u_char *cp;
2756	register int c;
2757
2758	for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
2759		c = (int)*cp;
2760		if (c & 0x80) {
2761			putc('M', fp);
2762			putc('-', fp);
2763			c &= 0x7f;
2764		}
2765
2766		if (c < ' ') {
2767			putc('^', fp);
2768			putc(c + '@', fp);
2769		} else if (0x7f == c) {
2770			putc('^', fp);
2771			putc('?', fp);
2772		} else
2773			putc(c, fp);
2774	}
2775}
2776
2777
2778/*
2779 * asciize - same thing as makeascii except add a newline
2780 */
2781void
2782asciize(
2783	int length,
2784	char *data,
2785	FILE *fp
2786	)
2787{
2788	makeascii(length, data, fp);
2789	putc('\n', fp);
2790}
2791
2792
2793/*
2794 * Some circular buffer space
2795 */
2796#define	CBLEN	80
2797#define	NUMCB	6
2798
2799char circ_buf[NUMCB][CBLEN];
2800int nextcb = 0;
2801
2802/*
2803 * nextvar - find the next variable in the buffer
2804 */
2805int
2806nextvar(
2807	int *datalen,
2808	char **datap,
2809	char **vname,
2810	char **vvalue
2811	)
2812{
2813	register char *cp;
2814	register char *np;
2815	register char *cpend;
2816	register char *npend;	/* character after last */
2817	int quoted = 0;
2818	static char name[MAXVARLEN];
2819	static char value[MAXVALLEN];
2820
2821	cp = *datap;
2822	cpend = cp + *datalen;
2823
2824	/*
2825	 * Space past commas and white space
2826	 */
2827	while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
2828	    cp++;
2829	if (cp == cpend)
2830	    return 0;
2831
2832	/*
2833	 * Copy name until we hit a ',', an '=', a '\r' or a '\n'.  Backspace
2834	 * over any white space and terminate it.
2835	 */
2836	np = name;
2837	npend = &name[MAXVARLEN];
2838	while (cp < cpend && np < npend && *cp != ',' && *cp != '='
2839	       && *cp != '\r' && *cp != '\n')
2840	    *np++ = *cp++;
2841	/*
2842	 * Check if we ran out of name space, without reaching the end or a
2843	 * terminating character
2844	 */
2845	if (np == npend && !(cp == cpend || *cp == ',' || *cp == '=' ||
2846			     *cp == '\r' || *cp == '\n'))
2847	    return 0;
2848	while (isspace((int)(*(np-1))))
2849	    np--;
2850	*np = '\0';
2851	*vname = name;
2852
2853	/*
2854	 * Check if we hit the end of the buffer or a ','.  If so we are done.
2855	 */
2856	if (cp == cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
2857		if (cp != cpend)
2858		    cp++;
2859		*datap = cp;
2860		*datalen = cpend - cp;
2861		*vvalue = (char *)0;
2862		return 1;
2863	}
2864
2865	/*
2866	 * So far, so good.  Copy out the value
2867	 */
2868	cp++;	/* past '=' */
2869	while (cp < cpend && (isspace((int)*cp) && *cp != '\r' && *cp != '\n'))
2870	    cp++;
2871	np = value;
2872	npend = &value[MAXVALLEN];
2873	while (cp < cpend && np < npend && ((*cp != ',') || quoted))
2874	{
2875		quoted ^= ((*np++ = *cp++) == '"');
2876	}
2877
2878	/*
2879	 * Check if we overran the value buffer while still in a quoted string
2880	 * or without finding a comma
2881	 */
2882	if (np == npend && (quoted || *cp != ','))
2883	    return 0;
2884	/*
2885	 * Trim off any trailing whitespace
2886	 */
2887	while (np > value && isspace((int)(*(np-1))))
2888	    np--;
2889	*np = '\0';
2890
2891	/*
2892	 * Return this.  All done.
2893	 */
2894	if (cp != cpend)
2895	    cp++;
2896	*datap = cp;
2897	*datalen = cpend - cp;
2898	*vvalue = value;
2899	return 1;
2900}
2901
2902
2903/*
2904 * findvar - see if this variable is known to us.
2905 * If "code" is 1, return ctl_var->code.
2906 * Otherwise return the ordinal position of the found variable.
2907 */
2908int
2909findvar(
2910	char *varname,
2911	struct ctl_var *varlist,
2912	int code
2913	)
2914{
2915	register char *np;
2916	register struct ctl_var *vl;
2917
2918	vl = varlist;
2919	np = varname;
2920	while (vl->fmt != EOV) {
2921		if (vl->fmt != PADDING && STREQ(np, vl->text))
2922		    return (code)
2923				? vl->code
2924				: (vl - varlist)
2925			    ;
2926		vl++;
2927	}
2928	return 0;
2929}
2930
2931
2932
2933/*
2934 * printvars - print variables returned in response packet
2935 */
2936void
2937printvars(
2938	int length,
2939	char *data,
2940	int status,
2941	int sttype,
2942	int quiet,
2943	FILE *fp
2944	)
2945{
2946	if (rawmode)
2947	    rawprint(sttype, length, data, status, quiet, fp);
2948	else
2949	    cookedprint(sttype, length, data, status, quiet, fp);
2950}
2951
2952
2953/*
2954 * rawprint - do a printout of the data in raw mode
2955 */
2956static void
2957rawprint(
2958	int datatype,
2959	int length,
2960	char *data,
2961	int status,
2962	int quiet,
2963	FILE *fp
2964	)
2965{
2966	register char *cp;
2967	register char *cpend;
2968
2969	/*
2970	 * Essentially print the data as is.  We reformat unprintables, though.
2971	 */
2972	cp = data;
2973	cpend = data + length;
2974
2975	if (!quiet)
2976		(void) fprintf(fp, "status=0x%04x,\n", status);
2977
2978	while (cp < cpend) {
2979		if (*cp == '\r') {
2980			/*
2981			 * If this is a \r and the next character is a
2982			 * \n, supress this, else pretty print it.  Otherwise
2983			 * just output the character.
2984			 */
2985			if (cp == (cpend - 1) || *(cp + 1) != '\n')
2986			    makeascii(1, cp, fp);
2987		} else if (isspace(*cp) || isprint(*cp))
2988			putc(*cp, fp);
2989		else
2990			makeascii(1, cp, fp);
2991		cp++;
2992	}
2993}
2994
2995
2996/*
2997 * Global data used by the cooked output routines
2998 */
2999int out_chars;		/* number of characters output */
3000int out_linecount;	/* number of characters output on this line */
3001
3002
3003/*
3004 * startoutput - get ready to do cooked output
3005 */
3006static void
3007startoutput(void)
3008{
3009	out_chars = 0;
3010	out_linecount = 0;
3011}
3012
3013
3014/*
3015 * output - output a variable=value combination
3016 */
3017static void
3018output(
3019	FILE *fp,
3020	char *name,
3021	char *value
3022	)
3023{
3024	size_t len;
3025
3026	/* strlen of "name=value" */
3027	len = strlen(name) + 1 + strlen(value);
3028
3029	if (out_chars != 0) {
3030		out_chars += 2;
3031		if ((out_linecount + len + 2) > MAXOUTLINE) {
3032			fputs(",\n", fp);
3033			out_linecount = 0;
3034		} else {
3035			fputs(", ", fp);
3036			out_linecount += 2;
3037		}
3038	}
3039
3040	fputs(name, fp);
3041	putc('=', fp);
3042	fputs(value, fp);
3043	out_chars += len;
3044	out_linecount += len;
3045}
3046
3047
3048/*
3049 * endoutput - terminate a block of cooked output
3050 */
3051static void
3052endoutput(
3053	FILE *fp
3054	)
3055{
3056	if (out_chars != 0)
3057		putc('\n', fp);
3058}
3059
3060
3061/*
3062 * outputarr - output an array of values
3063 */
3064static void
3065outputarr(
3066	FILE *fp,
3067	char *name,
3068	int narr,
3069	l_fp *lfp
3070	)
3071{
3072	register char *bp;
3073	register char *cp;
3074	register int i;
3075	register int len;
3076	char buf[256];
3077
3078	bp = buf;
3079	/*
3080	 * Hack to align delay and offset values
3081	 */
3082	for (i = (int)strlen(name); i < 11; i++)
3083	    *bp++ = ' ';
3084
3085	for (i = narr; i > 0; i--) {
3086		if (i != narr)
3087		    *bp++ = ' ';
3088		cp = lfptoms(lfp, 2);
3089		len = strlen(cp);
3090		if (len > 7) {
3091			cp[7] = '\0';
3092			len = 7;
3093		}
3094		while (len < 7) {
3095			*bp++ = ' ';
3096			len++;
3097		}
3098		while (*cp != '\0')
3099		    *bp++ = *cp++;
3100		lfp++;
3101	}
3102	*bp = '\0';
3103	output(fp, name, buf);
3104}
3105
3106static char *
3107tstflags(
3108	u_long val
3109	)
3110{
3111	register char *cb, *s;
3112	register int i;
3113	register const char *sep;
3114
3115	sep = "";
3116	i = 0;
3117	s = cb = &circ_buf[nextcb][0];
3118	if (++nextcb >= NUMCB)
3119	    nextcb = 0;
3120
3121	sprintf(cb, "%02lx", val);
3122	cb += strlen(cb);
3123	if (!val) {
3124		strcat(cb, " ok");
3125		cb += strlen(cb);
3126	} else {
3127		*cb++ = ' ';
3128		for (i = 0; i < 13; i++) {
3129			if (val & 0x1) {
3130				sprintf(cb, "%s%s", sep, tstflagnames[i]);
3131				sep = ", ";
3132				cb += strlen(cb);
3133			}
3134			val >>= 1;
3135		}
3136	}
3137	*cb = '\0';
3138	return s;
3139}
3140
3141/*
3142 * cookedprint - output variables in cooked mode
3143 */
3144static void
3145cookedprint(
3146	int datatype,
3147	int length,
3148	char *data,
3149	int status,
3150	int quiet,
3151	FILE *fp
3152	)
3153{
3154	register int varid;
3155	char *name;
3156	char *value;
3157	char output_raw;
3158	int fmt;
3159	struct ctl_var *varlist;
3160	l_fp lfp;
3161	long ival;
3162	sockaddr_u hval;
3163	u_long uval;
3164	l_fp lfparr[8];
3165	int narr;
3166
3167	switch (datatype) {
3168	case TYPE_PEER:
3169		varlist = peer_var;
3170		break;
3171	case TYPE_SYS:
3172		varlist = sys_var;
3173		break;
3174	case TYPE_CLOCK:
3175		varlist = clock_var;
3176		break;
3177	default:
3178		fprintf(stderr, "Unknown datatype(0x%x) in cookedprint\n",
3179			datatype);
3180		return;
3181	}
3182
3183	if (!quiet)
3184		fprintf(fp, "status=%04x %s,\n", status,
3185			statustoa(datatype, status));
3186
3187	startoutput();
3188	while (nextvar(&length, &data, &name, &value)) {
3189		varid = findvar(name, varlist, 0);
3190		if (varid == 0) {
3191			output_raw = '*';
3192		} else {
3193			output_raw = 0;
3194			fmt = varlist[varid].fmt;
3195			switch(fmt) {
3196			    case TS:
3197				if (!decodets(value, &lfp))
3198				    output_raw = '?';
3199				else
3200				    output(fp, name, prettydate(&lfp));
3201				break;
3202			    case FL:
3203			    case FU:
3204			    case FS:
3205				if (!decodetime(value, &lfp))
3206				    output_raw = '?';
3207				else {
3208					switch (fmt) {
3209					    case FL:
3210						output(fp, name,
3211						       lfptoms(&lfp, 3));
3212						break;
3213					    case FU:
3214						output(fp, name,
3215						       ulfptoms(&lfp, 3));
3216						break;
3217					    case FS:
3218						output(fp, name,
3219						       lfptoms(&lfp, 3));
3220						break;
3221					}
3222				}
3223				break;
3224
3225			    case UI:
3226				if (!decodeuint(value, &uval))
3227				    output_raw = '?';
3228				else
3229				    output(fp, name, uinttoa(uval));
3230				break;
3231
3232			    case SI:
3233				if (!decodeint(value, &ival))
3234				    output_raw = '?';
3235				else
3236				    output(fp, name, inttoa(ival));
3237				break;
3238
3239			    case HA:
3240			    case NA:
3241				if (!decodenetnum(value, &hval))
3242				    output_raw = '?';
3243				else if (fmt == HA){
3244				    output(fp, name, nntohost(&hval));
3245				} else {
3246				    output(fp, name, stoa(&hval));
3247				}
3248				break;
3249
3250			    case ST:
3251				output_raw = '*';
3252				break;
3253
3254			    case RF:
3255				if (decodenetnum(value, &hval)) {
3256					if (ISREFCLOCKADR(&hval))
3257    						output(fp, name,
3258						    refnumtoa(&hval));
3259					else
3260				    		output(fp, name, stoa(&hval));
3261				} else if ((int)strlen(value) <= 4)
3262				    output(fp, name, value);
3263				else
3264				    output_raw = '?';
3265				break;
3266
3267			    case LP:
3268				if (!decodeuint(value, &uval) || uval > 3)
3269				    output_raw = '?';
3270				else {
3271					char b[3];
3272					b[0] = b[1] = '0';
3273					if (uval & 0x2)
3274					    b[0] = '1';
3275					if (uval & 0x1)
3276					    b[1] = '1';
3277					b[2] = '\0';
3278					output(fp, name, b);
3279				}
3280				break;
3281
3282			    case OC:
3283				if (!decodeuint(value, &uval))
3284				    output_raw = '?';
3285				else {
3286					char b[12];
3287
3288					(void) snprintf(b, sizeof b, "%03lo", uval);
3289					output(fp, name, b);
3290				}
3291				break;
3292
3293			    case MD:
3294				if (!decodeuint(value, &uval))
3295				    output_raw = '?';
3296				else
3297				    output(fp, name, uinttoa(uval));
3298				break;
3299
3300			    case AR:
3301				if (!decodearr(value, &narr, lfparr))
3302				    output_raw = '?';
3303				else
3304				    outputarr(fp, name, narr, lfparr);
3305				break;
3306
3307			    case FX:
3308				if (!decodeuint(value, &uval))
3309				    output_raw = '?';
3310				else
3311				    output(fp, name, tstflags(uval));
3312				break;
3313
3314			    default:
3315				(void) fprintf(stderr,
3316				    "Internal error in cookedprint, %s=%s, fmt %d\n",
3317				    name, value, fmt);
3318				break;
3319			}
3320
3321		}
3322		if (output_raw != 0) {
3323			char bn[401];
3324			char bv[401];
3325			int len;
3326
3327			atoascii(name, MAXVARLEN, bn, sizeof(bn));
3328			atoascii(value, MAXVARLEN, bv, sizeof(bv));
3329			if (output_raw != '*') {
3330				len = strlen(bv);
3331				bv[len] = output_raw;
3332				bv[len+1] = '\0';
3333			}
3334			output(fp, bn, bv);
3335		}
3336	}
3337	endoutput(fp);
3338}
3339
3340
3341/*
3342 * sortassoc - sort associations in the cache into ascending order
3343 */
3344void
3345sortassoc(void)
3346{
3347	if (numassoc > 1)
3348	    qsort(
3349#ifdef QSORT_USES_VOID_P
3350		    (void *)
3351#else
3352		    (char *)
3353#endif
3354		    assoc_cache, (size_t)numassoc,
3355		    sizeof(struct association), assoccmp);
3356}
3357
3358
3359/*
3360 * assoccmp - compare two associations
3361 */
3362#ifdef QSORT_USES_VOID_P
3363static int
3364assoccmp(
3365	const void *t1,
3366	const void *t2
3367	)
3368{
3369	const struct association *ass1 = (const struct association *)t1;
3370	const struct association *ass2 = (const struct association *)t2;
3371
3372	if (ass1->assid < ass2->assid)
3373		return -1;
3374	if (ass1->assid > ass2->assid)
3375		return 1;
3376	return 0;
3377}
3378#else
3379static int
3380assoccmp(
3381	struct association *ass1,
3382	struct association *ass2
3383	)
3384{
3385	if (ass1->assid < ass2->assid)
3386	    return -1;
3387	if (ass1->assid > ass2->assid)
3388	    return 1;
3389	return 0;
3390}
3391#endif /* not QSORT_USES_VOID_P */
3392
3393/*
3394 * ntpq_custom_opt_handler - autoopts handler for -c and -p
3395 *
3396 * By default, autoopts loses the relative order of -c and -p options
3397 * on the command line.  This routine replaces the default handler for
3398 * those routines and builds a list of commands to execute preserving
3399 * the order.
3400 */
3401void
3402ntpq_custom_opt_handler(
3403	tOptions *pOptions,
3404	tOptDesc *pOptDesc
3405	)
3406{
3407	switch (pOptDesc->optValue) {
3408
3409	default:
3410		fprintf(stderr,
3411			"ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3412			pOptDesc->optValue, pOptDesc->optValue);
3413		exit(-1);
3414
3415	case 'c':
3416		ADDCMD(pOptDesc->pzLastArg);
3417		break;
3418
3419	case 'p':
3420		ADDCMD("peers");
3421		break;
3422	}
3423}
3424