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