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