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