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