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