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