ntpq.c revision 301256
1219820Sjeff/*
2219820Sjeff * ntpq - query an NTP server using mode 6 commands
3219820Sjeff */
4219820Sjeff#include <config.h>
5219820Sjeff#include <stdio.h>
6219820Sjeff#include <ctype.h>
7219820Sjeff#include <signal.h>
8309378Sjhb#include <setjmp.h>
9219820Sjeff#include <sys/types.h>
10219820Sjeff#include <sys/time.h>
11219820Sjeff#ifdef HAVE_UNISTD_H
12219820Sjeff# include <unistd.h>
13219820Sjeff#endif
14219820Sjeff#ifdef HAVE_FCNTL_H
15219820Sjeff# include <fcntl.h>
16219820Sjeff#endif
17219820Sjeff#ifdef SYS_WINNT
18219820Sjeff# include <mswsock.h>
19219820Sjeff#endif
20219820Sjeff#include <isc/net.h>
21219820Sjeff#include <isc/result.h>
22219820Sjeff
23219820Sjeff#include "ntpq.h"
24219820Sjeff#include "ntp_assert.h"
25219820Sjeff#include "ntp_stdlib.h"
26219820Sjeff#include "ntp_unixtime.h"
27219820Sjeff#include "ntp_calendar.h"
28219820Sjeff#include "ntp_select.h"
29219820Sjeff#include "ntp_assert.h"
30219820Sjeff#include "lib_strbuf.h"
31219820Sjeff#include "ntp_lineedit.h"
32219820Sjeff#include "ntp_debug.h"
33219820Sjeff#ifdef OPENSSL
34219820Sjeff#include "openssl/evp.h"
35219820Sjeff#include "openssl/objects.h"
36219820Sjeff#include "openssl/err.h"
37219820Sjeff#endif
38219820Sjeff#include <ssl_applink.c>
39309378Sjhb
40309378Sjhb#include "ntp_libopts.h"
41219820Sjeff#include "safecast.h"
42219820Sjeff
43219820Sjeff#ifdef SYS_VXWORKS		/* vxWorks needs mode flag -casey*/
44219820Sjeff# define open(name, flags)   open(name, flags, 0777)
45219820Sjeff# define SERVER_PORT_NUM     123
46219820Sjeff#endif
47219820Sjeff
48219820Sjeff/* we use COMMAND as an autogen keyword */
49271127Shselasky#ifdef COMMAND
50309378Sjhb# undef COMMAND
51309378Sjhb#endif
52219820Sjeff
53309378Sjhb/*
54219820Sjeff * Because we potentially understand a lot of commands we will run
55219820Sjeff * interactive if connected to a terminal.
56219820Sjeff */
57219820Sjeffint interactive = 0;		/* set to 1 when we should prompt */
58219820Sjeffconst char *prompt = "ntpq> ";	/* prompt to ask him about */
59219820Sjeff
60219820Sjeff/*
61219820Sjeff * use old readvars behavior?  --old-rv processing in ntpq resets
62219820Sjeff * this value based on the presence or absence of --old-rv.  It is
63219820Sjeff * initialized to 1 here to maintain backward compatibility with
64219820Sjeff * libntpq clients such as ntpsnmpd, which are free to reset it as
65219820Sjeff * desired.
66219820Sjeff */
67219820Sjeffint	old_rv = 1;
68219820Sjeff
69219820Sjeff/*
70219820Sjeff * How should we display the refid?
71309378Sjhb * REFID_HASH, REFID_IPV4
72309378Sjhb */
73309378Sjhbte_Refid drefid = -1;
74309378Sjhb
75219820Sjeff/*
76309378Sjhb * for get_systime()
77309378Sjhb */
78309378Sjhbs_char	sys_precision;		/* local clock precision (log2 s) */
79309378Sjhb
80309378Sjhb/*
81309378Sjhb * Keyid used for authenticated requests.  Obtained on the fly.
82309378Sjhb */
83309378Sjhbu_long info_auth_keyid = 0;
84309378Sjhb
85309378Sjhbstatic	int	info_auth_keytype = NID_md5;	/* MD5 */
86309378Sjhbstatic	size_t	info_auth_hashlen = 16;		/* MD5 */
87309378Sjhbu_long	current_time;		/* needed by authkeys; not used */
88309378Sjhb
89309378Sjhb/*
90309378Sjhb * Flag which indicates we should always send authenticated requests
91309378Sjhb */
92309378Sjhbint always_auth = 0;
93309378Sjhb
94309378Sjhb/*
95309378Sjhb * Flag which indicates raw mode output.
96309378Sjhb */
97309378Sjhbint rawmode = 0;
98309378Sjhb
99309378Sjhb/*
100309378Sjhb * Packet version number we use
101309378Sjhb */
102309378Sjhbu_char pktversion = NTP_OLDVERSION + 1;
103309378Sjhb
104309378Sjhb/*
105309378Sjhb * Don't jump if no set jmp.
106309378Sjhb */
107309378Sjhbvolatile int jump = 0;
108309378Sjhb
109309378Sjhb/*
110309378Sjhb * Format values
111309378Sjhb */
112309378Sjhb#define	PADDING	0
113309378Sjhb#define	HA	1	/* host address */
114309378Sjhb#define	NA	2	/* network address */
115309378Sjhb#define	LP	3	/* leap (print in binary) */
116309378Sjhb#define	RF	4	/* refid (sometimes string, sometimes not) */
117309378Sjhb#define	AR	5	/* array of times */
118309378Sjhb#define FX	6	/* test flags */
119309378Sjhb#define TS	7	/* l_fp timestamp in hex */
120309378Sjhb#define	OC	8	/* integer, print in octal */
121309378Sjhb#define	EOV	255	/* end of table */
122309378Sjhb
123309378Sjhb/*
124309378Sjhb * For the most part ntpq simply displays what ntpd provides in the
125309378Sjhb * mostly plain-text mode 6 responses.  A few variable names are by
126309378Sjhb * default "cooked" to provide more human-friendly output.
127309378Sjhb */
128309378Sjhbconst var_format cookedvars[] = {
129309378Sjhb	{ "leap",		LP },
130309378Sjhb	{ "reach",		OC },
131309378Sjhb	{ "refid",		RF },
132309378Sjhb	{ "reftime",		TS },
133309378Sjhb	{ "clock",		TS },
134309378Sjhb	{ "org",		TS },
135309378Sjhb	{ "rec",		TS },
136309378Sjhb	{ "xmt",		TS },
137309378Sjhb	{ "flash",		FX },
138309378Sjhb	{ "srcadr",		HA },
139309378Sjhb	{ "peeradr",		HA },	/* compat with others */
140309378Sjhb	{ "dstadr",		NA },
141309378Sjhb	{ "filtdelay",		AR },
142309378Sjhb	{ "filtoffset",		AR },
143309378Sjhb	{ "filtdisp",		AR },
144309378Sjhb	{ "filterror",		AR },	/* compat with others */
145309378Sjhb};
146309378Sjhb
147309378Sjhb
148309378Sjhb
149309378Sjhb/*
150219820Sjeff * flasher bits
151219820Sjeff */
152219820Sjeffstatic const char *tstflagnames[] = {
153219820Sjeff	"pkt_dup",		/* TEST1 */
154219820Sjeff	"pkt_bogus",		/* TEST2 */
155219820Sjeff	"pkt_unsync",		/* TEST3 */
156219820Sjeff	"pkt_denied",		/* TEST4 */
157219820Sjeff	"pkt_auth",		/* TEST5 */
158219820Sjeff	"pkt_stratum",		/* TEST6 */
159219820Sjeff	"pkt_header",		/* TEST7 */
160219820Sjeff	"pkt_autokey",		/* TEST8 */
161219820Sjeff	"pkt_crypto",		/* TEST9 */
162219820Sjeff	"peer_stratum",		/* TEST10 */
163219820Sjeff	"peer_dist",		/* TEST11 */
164219820Sjeff	"peer_loop",		/* TEST12 */
165219820Sjeff	"peer_unreach"		/* TEST13 */
166219820Sjeff};
167219820Sjeff
168219820Sjeff
169219820Sjeffint		ntpqmain	(int,	char **);
170219820Sjeff/*
171219820Sjeff * Built in command handler declarations
172219820Sjeff */
173219820Sjeffstatic	int	openhost	(const char *, int);
174219820Sjeffstatic	void	dump_hex_printable(const void *, size_t);
175219820Sjeffstatic	int	sendpkt		(void *, size_t);
176219820Sjeffstatic	int	getresponse	(int, int, u_short *, size_t *, const char **, int);
177219820Sjeffstatic	int	sendrequest	(int, associd_t, int, size_t, const char *);
178219820Sjeffstatic	char *	tstflags	(u_long);
179219820Sjeff#ifndef BUILD_AS_LIB
180219820Sjeffstatic	void	getcmds		(void);
181219820Sjeff#ifndef SYS_WINNT
182219820Sjeffstatic	int	abortcmd	(void);
183219820Sjeff#endif	/* SYS_WINNT */
184219820Sjeffstatic	void	docmd		(const char *);
185219820Sjeffstatic	void	tokenize	(const char *, char **, int *);
186219820Sjeffstatic	int	getarg		(const char *, int, arg_v *);
187219820Sjeff#endif	/* BUILD_AS_LIB */
188219820Sjeffstatic	int	findcmd		(const char *, struct xcmd *,
189219820Sjeff				 struct xcmd *, struct xcmd **);
190219820Sjeffstatic	int	rtdatetolfp	(char *, l_fp *);
191219820Sjeffstatic	int	decodearr	(char *, int *, l_fp *);
192219820Sjeffstatic	void	help		(struct parse *, FILE *);
193219820Sjeffstatic	int	helpsort	(const void *, const void *);
194219820Sjeffstatic	void	printusage	(struct xcmd *, FILE *);
195219820Sjeffstatic	void	timeout		(struct parse *, FILE *);
196219820Sjeffstatic	void	auth_delay	(struct parse *, FILE *);
197219820Sjeffstatic	void	host		(struct parse *, FILE *);
198219820Sjeffstatic	void	ntp_poll	(struct parse *, FILE *);
199219820Sjeffstatic	void	keyid		(struct parse *, FILE *);
200219820Sjeffstatic	void	keytype		(struct parse *, FILE *);
201219820Sjeffstatic	void	passwd		(struct parse *, FILE *);
202219820Sjeffstatic	void	hostnames	(struct parse *, FILE *);
203219820Sjeffstatic	void	setdebug	(struct parse *, FILE *);
204219820Sjeffstatic	void	quit		(struct parse *, FILE *);
205219820Sjeffstatic	void	showdrefid	(struct parse *, FILE *);
206219820Sjeffstatic	void	version		(struct parse *, FILE *);
207219820Sjeffstatic	void	raw		(struct parse *, FILE *);
208219820Sjeffstatic	void	cooked		(struct parse *, FILE *);
209219820Sjeffstatic	void	authenticate	(struct parse *, FILE *);
210219820Sjeffstatic	void	ntpversion	(struct parse *, FILE *);
211219820Sjeffstatic	void	warning		(const char *, ...)
212219820Sjeff    __attribute__((__format__(__printf__, 1, 2)));
213219820Sjeffstatic	void	error		(const char *, ...)
214219820Sjeff    __attribute__((__format__(__printf__, 1, 2)));
215219820Sjeffstatic	u_long	getkeyid	(const char *);
216219820Sjeffstatic	void	atoascii	(const char *, size_t, char *, size_t);
217219820Sjeffstatic	void	cookedprint	(int, size_t, const char *, int, int, FILE *);
218219820Sjeffstatic	void	rawprint	(int, size_t, const char *, int, int, FILE *);
219219820Sjeffstatic	void	startoutput	(void);
220219820Sjeffstatic	void	output		(FILE *, const char *, const char *);
221219820Sjeffstatic	void	endoutput	(FILE *);
222219820Sjeffstatic	void	outputarr	(FILE *, char *, int, l_fp *);
223219820Sjeffstatic	int	assoccmp	(const void *, const void *);
224219820Sjeffstatic	void	on_ctrlc	(void);
225219820Sjeff	u_short	varfmt		(const char *);
226219820Sjeffstatic	int	my_easprintf	(char**, const char *, ...) NTP_PRINTF(2, 3);
227219820Sjeffvoid	ntpq_custom_opt_handler	(tOptions *, tOptDesc *);
228219820Sjeff
229219820Sjeff#ifdef OPENSSL
230219820Sjeff# ifdef HAVE_EVP_MD_DO_ALL_SORTED
231219820Sjeffstatic void list_md_fn(const EVP_MD *m, const char *from,
232219820Sjeff		       const char *to, void *arg );
233219820Sjeff# endif
234219820Sjeff#endif
235219820Sjeffstatic char *list_digest_names(void);
236219820Sjeff
237219820Sjeff/*
238219820Sjeff * Built-in commands we understand
239219820Sjeff */
240219820Sjeffstruct xcmd builtins[] = {
241219820Sjeff	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
242219820Sjeff	  { "command", "", "", "" },
243219820Sjeff	  "tell the use and syntax of commands" },
244219820Sjeff	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
245219820Sjeff	  { "command", "", "", "" },
246219820Sjeff	  "tell the use and syntax of commands" },
247219820Sjeff	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
248219820Sjeff	  { "msec", "", "", "" },
249219820Sjeff	  "set the primary receive time out" },
250219820Sjeff	{ "delay",	auth_delay,	{ OPT|NTP_INT, NO, NO, NO },
251219820Sjeff	  { "msec", "", "", "" },
252219820Sjeff	  "set the delay added to encryption time stamps" },
253219820Sjeff	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
254219820Sjeff	  { "-4|-6", "hostname", "", "" },
255219820Sjeff	  "specify the host whose NTP server we talk to" },
256219820Sjeff	{ "poll",	ntp_poll,	{ OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
257219820Sjeff	  { "n", "verbose", "", "" },
258219820Sjeff	  "poll an NTP server in client mode `n' times" },
259219820Sjeff	{ "passwd",	passwd,		{ OPT|NTP_STR, NO, NO, NO },
260219820Sjeff	  { "", "", "", "" },
261219820Sjeff	  "specify a password to use for authenticated requests"},
262219820Sjeff	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
263219820Sjeff	  { "yes|no", "", "", "" },
264219820Sjeff	  "specify whether hostnames or net numbers are printed"},
265219820Sjeff	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
266309378Sjhb	  { "no|more|less", "", "", "" },
267309378Sjhb	  "set/change debugging level" },
268219820Sjeff	{ "quit",	quit,		{ NO, NO, NO, NO },
269309378Sjhb	  { "", "", "", "" },
270309378Sjhb	  "exit ntpq" },
271309378Sjhb	{ "exit",	quit,		{ NO, NO, NO, NO },
272309378Sjhb	  { "", "", "", "" },
273309378Sjhb	  "exit ntpq" },
274309378Sjhb	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
275309378Sjhb	  { "key#", "", "", "" },
276219820Sjeff	  "set keyid to use for authenticated requests" },
277219820Sjeff	{ "drefid",	showdrefid,	{ OPT|NTP_STR, NO, NO, NO },
278219820Sjeff	  { "hash|ipv4", "", "", "" },
279219820Sjeff	  "display refid's as IPv4 or hash" },
280219820Sjeff	{ "version",	version,	{ NO, NO, NO, NO },
281219820Sjeff	  { "", "", "", "" },
282219820Sjeff	  "print version number" },
283219820Sjeff	{ "raw",	raw,		{ NO, NO, NO, NO },
284237263Snp	  { "", "", "", "" },
285219820Sjeff	  "do raw mode variable output" },
286219820Sjeff	{ "cooked",	cooked,		{ NO, NO, NO, NO },
287219820Sjeff	  { "", "", "", "" },
288219820Sjeff	  "do cooked mode variable output" },
289219820Sjeff	{ "authenticate", authenticate,	{ OPT|NTP_STR, NO, NO, NO },
290219820Sjeff	  { "yes|no", "", "", "" },
291219820Sjeff	  "always authenticate requests to this server" },
292219820Sjeff	{ "ntpversion",	ntpversion,	{ OPT|NTP_UINT, NO, NO, NO },
293219820Sjeff	  { "version number", "", "", "" },
294219820Sjeff	  "set the NTP version number to use for requests" },
295219820Sjeff	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
296219820Sjeff	  { "key type %s", "", "", "" },
297219820Sjeff	  NULL },
298219820Sjeff	{ 0,		0,		{ NO, NO, NO, NO },
299219820Sjeff	  { "", "", "", "" }, "" }
300219820Sjeff};
301237263Snp
302219820Sjeff
303219820Sjeff/*
304219820Sjeff * Default values we use.
305219820Sjeff */
306219820Sjeff#define	DEFHOST		"localhost"	/* default host name */
307219820Sjeff#define	DEFTIMEOUT	5		/* wait 5 seconds for 1st pkt */
308219820Sjeff#define	DEFSTIMEOUT	3		/* and 3 more for each additional */
309219820Sjeff/*
310219820Sjeff * Requests are automatically retried once, so total timeout with no
311219820Sjeff * response is a bit over 2 * DEFTIMEOUT, or 10 seconds.  At the other
312219820Sjeff * extreme, a request eliciting 32 packets of responses each for some
313219820Sjeff * reason nearly DEFSTIMEOUT seconds after the prior in that series,
314219820Sjeff * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
315219820Sjeff * 93 seconds to fail each of two times, or 186 seconds.
316219820Sjeff * Some commands involve a series of requests, such as "peers" and
317219820Sjeff * "mrulist", so the cumulative timeouts are even longer for those.
318219820Sjeff */
319219820Sjeff#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
320219820Sjeff#define	LENHOSTNAME	256		/* host name is 256 characters long */
321219820Sjeff#define	MAXCMDS		100		/* maximum commands on cmd line */
322219820Sjeff#define	MAXHOSTS	200		/* maximum hosts on cmd line */
323219820Sjeff#define	MAXLINE		512		/* maximum line length */
324219820Sjeff#define	MAXTOKENS	(1+MAXARGS+2)	/* maximum number of usable tokens */
325219820Sjeff#define	MAXVARLEN	256		/* maximum length of a variable name */
326219820Sjeff#define	MAXVALLEN	2048		/* maximum length of a variable value */
327219820Sjeff#define	MAXOUTLINE	72		/* maximum length of an output line */
328219820Sjeff#define SCREENWIDTH	76		/* nominal screen width in columns */
329219820Sjeff
330219820Sjeff/*
331219820Sjeff * Some variables used and manipulated locally
332219820Sjeff */
333219820Sjeffstruct sock_timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
334219820Sjeffstruct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
335219820Sjeffl_fp delay_time;				/* delay time */
336219820Sjeffchar currenthost[LENHOSTNAME];			/* current host name */
337219820Sjeffint currenthostisnum;				/* is prior text from IP? */
338219820Sjeffstruct sockaddr_in hostaddr;			/* host address */
339219820Sjeffint showhostnames = 1;				/* show host names by default */
340219820Sjeffint wideremote = 0;				/* show wide remote names? */
341219820Sjeff
342219820Sjeffint ai_fam_templ;				/* address family */
343219820Sjeffint ai_fam_default;				/* default address family */
344219820SjeffSOCKET sockfd;					/* fd socket is opened on */
345219820Sjeffint havehost = 0;				/* set to 1 when host open */
346219820Sjeffint s_port = 0;
347219820Sjeffstruct servent *server_entry = NULL;		/* server entry for ntp */
348219820Sjeff
349219820Sjeff
350219820Sjeff/*
351219820Sjeff * Sequence number used for requests.  It is incremented before
352219820Sjeff * it is used.
353219820Sjeff */
354219820Sjeffu_short sequence;
355219820Sjeff
356219820Sjeff/*
357219820Sjeff * Holds data returned from queries.  Declare buffer long to be sure of
358219820Sjeff * alignment.
359219820Sjeff */
360219820Sjeff#define	DATASIZE	(MAXFRAGS*480)	/* maximum amount of data */
361219820Sjefflong pktdata[DATASIZE/sizeof(long)];
362219820Sjeff
363219820Sjeff/*
364219820Sjeff * assoc_cache[] is a dynamic array which allows references to
365219820Sjeff * associations using &1 ... &N for n associations, avoiding manual
366219820Sjeff * lookup of the current association IDs for a given ntpd.  It also
367219820Sjeff * caches the status word for each association, retrieved incidentally.
368219820Sjeff */
369219820Sjeffstruct association *	assoc_cache;
370219820Sjeffu_int assoc_cache_slots;/* count of allocated array entries */
371219820Sjeffu_int numassoc;		/* number of cached associations */
372219820Sjeff
373219820Sjeff/*
374219820Sjeff * For commands typed on the command line (with the -c option)
375219820Sjeff */
376219820Sjeffint numcmds = 0;
377219820Sjeffconst char *ccmds[MAXCMDS];
378219820Sjeff#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
379219820Sjeff
380219820Sjeff/*
381219820Sjeff * When multiple hosts are specified.
382219820Sjeff */
383219820Sjeff
384219820Sjeffu_int numhosts;
385219820Sjeff
386219820Sjeffchost chosts[MAXHOSTS];
387219820Sjeff#define	ADDHOST(cp)						\
388219820Sjeff	do {							\
389219820Sjeff		if (numhosts < MAXHOSTS) {			\
390219820Sjeff			chosts[numhosts].name = (cp);		\
391219820Sjeff			chosts[numhosts].fam = ai_fam_templ;	\
392219820Sjeff			numhosts++;				\
393219820Sjeff		}						\
394219820Sjeff	} while (0)
395219820Sjeff
396219820Sjeff/*
397219820Sjeff * Macro definitions we use
398219820Sjeff */
399219820Sjeff#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
400219820Sjeff#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
401219820Sjeff#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
402219820Sjeff
403219820Sjeff/*
404219820Sjeff * Jump buffer for longjumping back to the command level
405219820Sjeff */
406219820Sjeffjmp_buf interrupt_buf;
407219820Sjeff
408219820Sjeff/*
409219820Sjeff * Points at file being currently printed into
410219820Sjeff */
411309378SjhbFILE *current_output;
412309378Sjhb
413309378Sjhb/*
414309378Sjhb * Command table imported from ntpdc_ops.c
415309378Sjhb */
416309378Sjhbextern struct xcmd opcmds[];
417309378Sjhb
418309378Sjhbchar const *progname;
419309378Sjhb
420309378Sjhb#ifdef NO_MAIN_ALLOWED
421309378Sjhb#ifndef BUILD_AS_LIB
422309378SjhbCALL(ntpq,"ntpq",ntpqmain);
423309378Sjhb
424309378Sjhbvoid clear_globals(void)
425309378Sjhb{
426309378Sjhb	extern int ntp_optind;
427309378Sjhb	showhostnames = 0;	/* don'tshow host names by default */
428309378Sjhb	ntp_optind = 0;
429309378Sjhb	server_entry = NULL;	/* server entry for ntp */
430309378Sjhb	havehost = 0;		/* set to 1 when host open */
431309378Sjhb	numassoc = 0;		/* number of cached associations */
432309378Sjhb	numcmds = 0;
433309378Sjhb	numhosts = 0;
434309378Sjhb}
435309378Sjhb#endif /* !BUILD_AS_LIB */
436309378Sjhb#endif /* NO_MAIN_ALLOWED */
437309378Sjhb
438309378Sjhb/*
439309378Sjhb * main - parse arguments and handle options
440309378Sjhb */
441309378Sjhb#ifndef NO_MAIN_ALLOWED
442309378Sjhbint
443309378Sjhbmain(
444309378Sjhb	int argc,
445309378Sjhb	char *argv[]
446309378Sjhb	)
447309378Sjhb{
448309378Sjhb	return ntpqmain(argc, argv);
449309378Sjhb}
450309378Sjhb#endif
451309378Sjhb
452309378Sjhb#ifndef BUILD_AS_LIB
453309378Sjhbint
454309378Sjhbntpqmain(
455309378Sjhb	int argc,
456309378Sjhb	char *argv[]
457309378Sjhb	)
458309378Sjhb{
459309378Sjhb	u_int ihost;
460309378Sjhb	int icmd;
461309378Sjhb
462309378Sjhb
463309378Sjhb#ifdef SYS_VXWORKS
464309378Sjhb	clear_globals();
465309378Sjhb	taskPrioritySet(taskIdSelf(), 100 );
466309378Sjhb#endif
467309378Sjhb
468309378Sjhb	delay_time.l_ui = 0;
469309378Sjhb	delay_time.l_uf = DEFDELAY;
470309378Sjhb
471309378Sjhb	init_lib();	/* sets up ipv4_works, ipv6_works */
472309378Sjhb	ssl_applink();
473309378Sjhb	init_auth();
474309378Sjhb
475309378Sjhb	/* Check to see if we have IPv6. Otherwise default to IPv4 */
476309378Sjhb	if (!ipv6_works)
477309378Sjhb		ai_fam_default = AF_INET;
478309378Sjhb
479309378Sjhb	/* Fixup keytype's help based on available digest names */
480309378Sjhb
481309378Sjhb	{
482309378Sjhb	    char *list;
483309378Sjhb	    char *msg;
484309378Sjhb
485309378Sjhb	    list = list_digest_names();
486309378Sjhb	    for (icmd = 0; icmd < sizeof(builtins)/sizeof(builtins[0]); icmd++) {
487309378Sjhb		if (strcmp("keytype", builtins[icmd].keyword) == 0)
488309378Sjhb		    break;
489309378Sjhb	    }
490309378Sjhb
491309378Sjhb	    /* CID: 1295478 */
492309378Sjhb	    /* This should only "trip" if "keytype" is removed from builtins */
493309378Sjhb	    INSIST(icmd < sizeof(builtins)/sizeof(builtins[0]));
494309378Sjhb
495309378Sjhb#ifdef OPENSSL
496309378Sjhb	    builtins[icmd].desc[0] = "digest-name";
497309378Sjhb	    my_easprintf(&msg,
498309378Sjhb			 "set key type to use for authenticated requests, one of:%s",
499309378Sjhb			 list);
500309378Sjhb#else
501309378Sjhb	    builtins[icmd].desc[0] = "md5";
502309378Sjhb	    my_easprintf(&msg,
503309378Sjhb			 "set key type to use for authenticated requests (%s)",
504309378Sjhb			 list);
505309378Sjhb#endif
506309378Sjhb	    builtins[icmd].comment = msg;
507309378Sjhb	    free(list);
508309378Sjhb	}
509309378Sjhb
510309378Sjhb	progname = argv[0];
511309378Sjhb
512309378Sjhb	{
513309378Sjhb		int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
514309378Sjhb		argc -= optct;
515309378Sjhb		argv += optct;
516309378Sjhb	}
517309378Sjhb
518309378Sjhb	/*
519309378Sjhb	 * Process options other than -c and -p, which are specially
520309378Sjhb	 * handled by ntpq_custom_opt_handler().
521309378Sjhb	 */
522309378Sjhb
523309378Sjhb	debug = OPT_VALUE_SET_DEBUG_LEVEL;
524309378Sjhb
525309378Sjhb	if (HAVE_OPT(IPV4))
526309378Sjhb		ai_fam_templ = AF_INET;
527309378Sjhb	else if (HAVE_OPT(IPV6))
528314606Snp		ai_fam_templ = AF_INET6;
529309378Sjhb	else
530309378Sjhb		ai_fam_templ = ai_fam_default;
531309378Sjhb
532309378Sjhb	if (HAVE_OPT(INTERACTIVE))
533309378Sjhb		interactive = 1;
534309378Sjhb
535309378Sjhb	if (HAVE_OPT(NUMERIC))
536314606Snp		showhostnames = 0;
537309378Sjhb
538309378Sjhb	if (HAVE_OPT(WIDE))
539309378Sjhb		wideremote = 1;
540309378Sjhb
541309378Sjhb	old_rv = HAVE_OPT(OLD_RV);
542309378Sjhb
543309378Sjhb	drefid = OPT_VALUE_REFID;
544309378Sjhb
545314606Snp	if (0 == argc) {
546309378Sjhb		ADDHOST(DEFHOST);
547314606Snp	} else {
548314606Snp		for (ihost = 0; ihost < (u_int)argc; ihost++) {
549309378Sjhb			if ('-' == *argv[ihost]) {
550309378Sjhb				//
551309378Sjhb				// If I really cared I'd also check:
552309378Sjhb				// 0 == argv[ihost][2]
553309378Sjhb				//
554314606Snp				// and there are other cases as well...
555314606Snp				//
556309378Sjhb				if ('4' == argv[ihost][1]) {
557309378Sjhb					ai_fam_templ = AF_INET;
558309378Sjhb					continue;
559219820Sjeff				} else if ('6' == argv[ihost][1]) {
560219820Sjeff					ai_fam_templ = AF_INET6;
561219820Sjeff					continue;
562219820Sjeff				} else {
563219820Sjeff					// XXX Throw a usage error
564219820Sjeff				}
565219820Sjeff			}
566219820Sjeff			ADDHOST(argv[ihost]);
567219820Sjeff		}
568219820Sjeff	}
569309378Sjhb
570219820Sjeff	if (numcmds == 0 && interactive == 0
571219820Sjeff	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
572219820Sjeff		interactive = 1;
573219820Sjeff	}
574219820Sjeff
575219820Sjeff	set_ctrl_c_hook(on_ctrlc);
576219820Sjeff#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
577219820Sjeff	if (interactive)
578219820Sjeff		push_ctrl_c_handler(abortcmd);
579219820Sjeff#endif /* SYS_WINNT */
580219820Sjeff
581219820Sjeff	if (numcmds == 0) {
582219820Sjeff		(void) openhost(chosts[0].name, chosts[0].fam);
583219820Sjeff		getcmds();
584309378Sjhb	} else {
585309378Sjhb		for (ihost = 0; ihost < numhosts; ihost++) {
586309378Sjhb			if (openhost(chosts[ihost].name, chosts[ihost].fam))
587309378Sjhb				for (icmd = 0; icmd < numcmds; icmd++)
588309378Sjhb					docmd(ccmds[icmd]);
589309378Sjhb		}
590309378Sjhb	}
591309378Sjhb#ifdef SYS_WINNT
592309378Sjhb	WSACleanup();
593309378Sjhb#endif /* SYS_WINNT */
594309378Sjhb	return 0;
595309378Sjhb}
596219820Sjeff#endif /* !BUILD_AS_LIB */
597219820Sjeff
598219820Sjeff/*
599219820Sjeff * openhost - open a socket to a host
600219820Sjeff */
601219820Sjeffstatic	int
602219820Sjeffopenhost(
603219820Sjeff	const char *hname,
604219820Sjeff	int	    fam
605219820Sjeff	)
606219820Sjeff{
607219820Sjeff	const char svc[] = "ntp";
608219820Sjeff	char temphost[LENHOSTNAME];
609219820Sjeff	int a_info, i;
610219820Sjeff	struct addrinfo hints, *ai;
611219820Sjeff	sockaddr_u addr;
612219820Sjeff	size_t octets;
613219820Sjeff	register const char *cp;
614219820Sjeff	char name[LENHOSTNAME];
615219820Sjeff
616219820Sjeff	/*
617219820Sjeff	 * We need to get by the [] if they were entered
618219820Sjeff	 */
619219820Sjeff
620219820Sjeff	cp = hname;
621219820Sjeff
622219820Sjeff	if (*cp == '[') {
623219820Sjeff		cp++;
624219820Sjeff		for (i = 0; *cp && *cp != ']'; cp++, i++)
625219820Sjeff			name[i] = *cp;
626219820Sjeff		if (*cp == ']') {
627219820Sjeff			name[i] = '\0';
628219820Sjeff			hname = name;
629219820Sjeff		} else {
630219820Sjeff			return 0;
631219820Sjeff		}
632219820Sjeff	}
633219820Sjeff
634219820Sjeff	/*
635219820Sjeff	 * First try to resolve it as an ip address and if that fails,
636219820Sjeff	 * do a fullblown (dns) lookup. That way we only use the dns
637219820Sjeff	 * when it is needed and work around some implementations that
638219820Sjeff	 * will return an "IPv4-mapped IPv6 address" address if you
639219820Sjeff	 * give it an IPv4 address to lookup.
640219820Sjeff	 */
641219820Sjeff	ZERO(hints);
642219820Sjeff	hints.ai_family = fam;
643219820Sjeff	hints.ai_protocol = IPPROTO_UDP;
644219820Sjeff	hints.ai_socktype = SOCK_DGRAM;
645219820Sjeff	hints.ai_flags = Z_AI_NUMERICHOST;
646219820Sjeff	ai = NULL;
647219820Sjeff
648219820Sjeff	a_info = getaddrinfo(hname, svc, &hints, &ai);
649219820Sjeff	if (a_info == EAI_NONAME
650219820Sjeff#ifdef EAI_NODATA
651219820Sjeff	    || a_info == EAI_NODATA
652219820Sjeff#endif
653314606Snp	   ) {
654314606Snp		hints.ai_flags = AI_CANONNAME;
655314606Snp#ifdef AI_ADDRCONFIG
656219820Sjeff		hints.ai_flags |= AI_ADDRCONFIG;
657219820Sjeff#endif
658219820Sjeff		a_info = getaddrinfo(hname, svc, &hints, &ai);
659219820Sjeff	}
660219820Sjeff#ifdef AI_ADDRCONFIG
661219820Sjeff	/* Some older implementations don't like AI_ADDRCONFIG. */
662219820Sjeff	if (a_info == EAI_BADFLAGS) {
663219820Sjeff		hints.ai_flags &= ~AI_ADDRCONFIG;
664219820Sjeff		a_info = getaddrinfo(hname, svc, &hints, &ai);
665219820Sjeff	}
666219820Sjeff#endif
667219820Sjeff	if (a_info != 0) {
668219820Sjeff		fprintf(stderr, "%s\n", gai_strerror(a_info));
669219820Sjeff		return 0;
670309378Sjhb	}
671219820Sjeff
672219820Sjeff	INSIST(ai != NULL);
673219820Sjeff	ZERO(addr);
674219820Sjeff	octets = min(sizeof(addr), ai->ai_addrlen);
675219820Sjeff	memcpy(&addr, ai->ai_addr, octets);
676219820Sjeff
677219820Sjeff	if (ai->ai_canonname == NULL) {
678219820Sjeff		strlcpy(temphost, stoa(&addr), sizeof(temphost));
679219820Sjeff		currenthostisnum = TRUE;
680219820Sjeff	} else {
681219820Sjeff		strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
682219820Sjeff		currenthostisnum = FALSE;
683309378Sjhb	}
684309378Sjhb
685309378Sjhb	if (debug > 2)
686309378Sjhb		printf("Opening host %s (%s)\n",
687309378Sjhb			temphost,
688309378Sjhb			(ai->ai_family == AF_INET)
689309378Sjhb			? "AF_INET"
690309378Sjhb			: (ai->ai_family == AF_INET6)
691309378Sjhb			  ? "AF_INET6"
692309378Sjhb			  : "AF-???"
693309378Sjhb			);
694309378Sjhb
695309378Sjhb	if (havehost == 1) {
696309378Sjhb		if (debug > 2)
697309378Sjhb			printf("Closing old host %s\n", currenthost);
698309378Sjhb		closesocket(sockfd);
699309378Sjhb		havehost = 0;
700309378Sjhb	}
701309378Sjhb	strlcpy(currenthost, temphost, sizeof(currenthost));
702309378Sjhb
703309378Sjhb	/* port maps to the same location in both families */
704309378Sjhb	s_port = NSRCPORT(&addr);
705309378Sjhb#ifdef SYS_VXWORKS
706309378Sjhb	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
707309378Sjhb	if (ai->ai_family == AF_INET)
708219820Sjeff		*(struct sockaddr_in *)&hostaddr=
709309378Sjhb			*((struct sockaddr_in *)ai->ai_addr);
710219820Sjeff	else
711219820Sjeff		*(struct sockaddr_in6 *)&hostaddr=
712219820Sjeff			*((struct sockaddr_in6 *)ai->ai_addr);
713219820Sjeff#endif /* SYS_VXWORKS */
714219820Sjeff
715219820Sjeff#ifdef SYS_WINNT
716219820Sjeff	{
717219820Sjeff		int optionValue = SO_SYNCHRONOUS_NONALERT;
718219820Sjeff		int err;
719219820Sjeff
720219820Sjeff		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
721219820Sjeff				 (char *)&optionValue, sizeof(optionValue));
722219820Sjeff		if (err) {
723219820Sjeff			mfprintf(stderr,
724219820Sjeff				 "setsockopt(SO_SYNCHRONOUS_NONALERT)"
725219820Sjeff				 " error: %m\n");
726219820Sjeff			freeaddrinfo(ai);
727219820Sjeff			exit(1);
728219820Sjeff		}
729219820Sjeff	}
730219820Sjeff#endif /* SYS_WINNT */
731219820Sjeff
732219820Sjeff	sockfd = socket(ai->ai_family, ai->ai_socktype,
733219820Sjeff			ai->ai_protocol);
734219820Sjeff	if (sockfd == INVALID_SOCKET) {
735219820Sjeff		error("socket");
736219820Sjeff		freeaddrinfo(ai);
737219820Sjeff		return 0;
738219820Sjeff	}
739219820Sjeff
740219820Sjeff
741219820Sjeff#ifdef NEED_RCVBUF_SLOP
742219820Sjeff# ifdef SO_RCVBUF
743219820Sjeff	{ int rbufsize = DATASIZE + 2048;	/* 2K for slop */
744219820Sjeff	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
745219820Sjeff		       &rbufsize, sizeof(int)) == -1)
746219820Sjeff		error("setsockopt");
747219820Sjeff	}
748219820Sjeff# endif
749219820Sjeff#endif
750219820Sjeff
751219820Sjeff	if
752219820Sjeff#ifdef SYS_VXWORKS
753219820Sjeff	   (connect(sockfd, (struct sockaddr *)&hostaddr,
754219820Sjeff		    sizeof(hostaddr)) == -1)
755219820Sjeff#else
756219820Sjeff	   (connect(sockfd, (struct sockaddr *)ai->ai_addr,
757219820Sjeff		ai->ai_addrlen) == -1)
758219820Sjeff#endif /* SYS_VXWORKS */
759219820Sjeff	{
760219820Sjeff		error("connect");
761219820Sjeff		freeaddrinfo(ai);
762219820Sjeff		return 0;
763219820Sjeff	}
764219820Sjeff	freeaddrinfo(ai);
765219820Sjeff	havehost = 1;
766219820Sjeff	numassoc = 0;
767219820Sjeff
768219820Sjeff	return 1;
769219820Sjeff}
770219820Sjeff
771219820Sjeff
772219820Sjeffstatic void
773219820Sjeffdump_hex_printable(
774219820Sjeff	const void *	data,
775219820Sjeff	size_t		len
776219820Sjeff	)
777219820Sjeff{
778219820Sjeff	const char *	cdata;
779219820Sjeff	const char *	rowstart;
780219820Sjeff	size_t		idx;
781219820Sjeff	size_t		rowlen;
782219820Sjeff	u_char		uch;
783219820Sjeff
784219820Sjeff	cdata = data;
785219820Sjeff	while (len > 0) {
786219820Sjeff		rowstart = cdata;
787219820Sjeff		rowlen = min(16, len);
788219820Sjeff		for (idx = 0; idx < rowlen; idx++) {
789219820Sjeff			uch = *(cdata++);
790219820Sjeff			printf("%02x ", uch);
791219820Sjeff		}
792219820Sjeff		for ( ; idx < 16 ; idx++)
793219820Sjeff			printf("   ");
794219820Sjeff		cdata = rowstart;
795219820Sjeff		for (idx = 0; idx < rowlen; idx++) {
796219820Sjeff			uch = *(cdata++);
797219820Sjeff			printf("%c", (isprint(uch))
798219820Sjeff					 ? uch
799219820Sjeff					 : '.');
800219820Sjeff		}
801219820Sjeff		printf("\n");
802219820Sjeff		len -= rowlen;
803219820Sjeff	}
804219820Sjeff}
805219820Sjeff
806219820Sjeff
807219820Sjeff/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
808219820Sjeff/*
809219820Sjeff * sendpkt - send a packet to the remote host
810219820Sjeff */
811219820Sjeffstatic int
812219820Sjeffsendpkt(
813219820Sjeff	void *	xdata,
814219820Sjeff	size_t	xdatalen
815219820Sjeff	)
816219820Sjeff{
817219820Sjeff	if (debug >= 3)
818219820Sjeff		printf("Sending %zu octets\n", xdatalen);
819219820Sjeff
820219820Sjeff	if (send(sockfd, xdata, xdatalen, 0) == -1) {
821219820Sjeff		warning("write to %s failed", currenthost);
822219820Sjeff		return -1;
823219820Sjeff	}
824219820Sjeff
825219820Sjeff	if (debug >= 4) {
826219820Sjeff		printf("Request packet:\n");
827219820Sjeff		dump_hex_printable(xdata, xdatalen);
828219820Sjeff	}
829219820Sjeff	return 0;
830219820Sjeff}
831219820Sjeff
832219820Sjeff/*
833219820Sjeff * getresponse - get a (series of) response packet(s) and return the data
834219820Sjeff */
835219820Sjeffstatic int
836219820Sjeffgetresponse(
837219820Sjeff	int opcode,
838219820Sjeff	int associd,
839219820Sjeff	u_short *rstatus,
840219820Sjeff	size_t *rsize,
841219820Sjeff	const char **rdata,
842219820Sjeff	int timeo
843219820Sjeff	)
844219820Sjeff{
845219820Sjeff	struct ntp_control rpkt;
846219820Sjeff	struct sock_timeval tvo;
847219820Sjeff	u_short offsets[MAXFRAGS+1];
848219820Sjeff	u_short counts[MAXFRAGS+1];
849219820Sjeff	u_short offset;
850219820Sjeff	u_short count;
851219820Sjeff	size_t numfrags;
852219820Sjeff	size_t f;
853219820Sjeff	size_t ff;
854219820Sjeff	int seenlastfrag;
855219820Sjeff	int shouldbesize;
856219820Sjeff	fd_set fds;
857219820Sjeff	int n;
858219820Sjeff	int errcode;
859219820Sjeff	/* absolute timeout checks. Not 'time_t' by intention! */
860219820Sjeff	uint32_t tobase;	/* base value for timeout */
861219820Sjeff	uint32_t tospan;	/* timeout span (max delay) */
862219820Sjeff	uint32_t todiff;	/* current delay */
863219820Sjeff
864219820Sjeff	/*
865219820Sjeff	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
866219820Sjeff	 * back in response to the request.  We peel the data out of
867219820Sjeff	 * each packet and collect it in one long block.  When the last
868219820Sjeff	 * packet in the sequence is received we'll know how much data we
869219820Sjeff	 * should have had.  Note we use one long time out, should reconsider.
870219820Sjeff	 */
871219820Sjeff	*rsize = 0;
872219820Sjeff	if (rstatus)
873219820Sjeff		*rstatus = 0;
874219820Sjeff	*rdata = (char *)pktdata;
875219820Sjeff
876219820Sjeff	numfrags = 0;
877219820Sjeff	seenlastfrag = 0;
878219820Sjeff
879219820Sjeff	tobase = (uint32_t)time(NULL);
880219820Sjeff
881219820Sjeff	FD_ZERO(&fds);
882219820Sjeff
883219820Sjeff	/*
884219820Sjeff	 * Loop until we have an error or a complete response.  Nearly all
885219820Sjeff	 * code paths to loop again use continue.
886219820Sjeff	 */
887219820Sjeff	for (;;) {
888219820Sjeff
889219820Sjeff		if (numfrags == 0)
890219820Sjeff			tvo = tvout;
891219820Sjeff		else
892219820Sjeff			tvo = tvsout;
893219820Sjeff		tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
894219820Sjeff
895219820Sjeff		FD_SET(sockfd, &fds);
896219820Sjeff		n = select(sockfd+1, &fds, NULL, NULL, &tvo);
897219820Sjeff		if (n == -1) {
898219820Sjeff#if !defined(SYS_WINNT) && defined(EINTR)
899219820Sjeff			/* Windows does not know about EINTR (until very
900219820Sjeff			 * recently) and the handling of console events
901219820Sjeff			 * is *very* different from POSIX/UNIX signal
902219820Sjeff			 * handling anyway.
903219820Sjeff			 *
904219820Sjeff			 * Under non-windows targets we map EINTR as
905219820Sjeff			 * 'last packet was received' and try to exit
906219820Sjeff			 * the receive sequence.
907219820Sjeff			 */
908219820Sjeff			if (errno == EINTR) {
909219820Sjeff				seenlastfrag = 1;
910219820Sjeff				goto maybe_final;
911237263Snp			}
912219820Sjeff#endif
913219820Sjeff			warning("select fails");
914219820Sjeff			return -1;
915219820Sjeff		}
916219820Sjeff
917219820Sjeff		/*
918219820Sjeff		 * Check if this is already too late. Trash the data and
919219820Sjeff		 * fake a timeout if this is so.
920219820Sjeff		 */
921219820Sjeff		todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
922219820Sjeff		if ((n > 0) && (todiff > tospan)) {
923219820Sjeff			n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
924219820Sjeff			n = 0; /* faked timeout return from 'select()'*/
925219820Sjeff		}
926219820Sjeff
927219820Sjeff		if (n == 0) {
928219820Sjeff			/*
929219820Sjeff			 * Timed out.  Return what we have
930219820Sjeff			 */
931219820Sjeff			if (numfrags == 0) {
932219820Sjeff				if (timeo)
933219820Sjeff					fprintf(stderr,
934219820Sjeff						"%s: timed out, nothing received\n",
935219820Sjeff						currenthost);
936219820Sjeff				return ERR_TIMEOUT;
937219820Sjeff			}
938219820Sjeff			if (timeo)
939219820Sjeff				fprintf(stderr,
940219820Sjeff					"%s: timed out with incomplete data\n",
941219820Sjeff					currenthost);
942219820Sjeff			if (debug) {
943219820Sjeff				fprintf(stderr,
944219820Sjeff					"ERR_INCOMPLETE: Received fragments:\n");
945219820Sjeff				for (f = 0; f < numfrags; f++)
946219820Sjeff					fprintf(stderr,
947219820Sjeff						"%2u: %5d %5d\t%3d octets\n",
948219820Sjeff						(u_int)f, offsets[f],
949219820Sjeff						offsets[f] +
950219820Sjeff						counts[f],
951219820Sjeff						counts[f]);
952219820Sjeff				fprintf(stderr,
953219820Sjeff					"last fragment %sreceived\n",
954219820Sjeff					(seenlastfrag)
955219820Sjeff					    ? ""
956219820Sjeff					    : "not ");
957219820Sjeff			}
958219820Sjeff			return ERR_INCOMPLETE;
959219820Sjeff		}
960219820Sjeff
961219820Sjeff		n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
962219820Sjeff		if (n == -1) {
963219820Sjeff			warning("read");
964219820Sjeff			return -1;
965219820Sjeff		}
966219820Sjeff
967219820Sjeff		if (debug >= 4) {
968219820Sjeff			printf("Response packet:\n");
969219820Sjeff			dump_hex_printable(&rpkt, n);
970219820Sjeff		}
971219820Sjeff
972219820Sjeff		/*
973219820Sjeff		 * Check for format errors.  Bug proofing.
974219820Sjeff		 */
975219820Sjeff		if (n < (int)CTL_HEADER_LEN) {
976219820Sjeff			if (debug)
977219820Sjeff				printf("Short (%d byte) packet received\n", n);
978219820Sjeff			continue;
979219820Sjeff		}
980219820Sjeff		if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
981219820Sjeff		    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
982219820Sjeff			if (debug)
983219820Sjeff				printf("Packet received with version %d\n",
984219820Sjeff				       PKT_VERSION(rpkt.li_vn_mode));
985219820Sjeff			continue;
986219820Sjeff		}
987219820Sjeff		if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
988219820Sjeff			if (debug)
989219820Sjeff				printf("Packet received with mode %d\n",
990219820Sjeff				       PKT_MODE(rpkt.li_vn_mode));
991219820Sjeff			continue;
992219820Sjeff		}
993219820Sjeff		if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
994219820Sjeff			if (debug)
995219820Sjeff				printf("Received request packet, wanted response\n");
996219820Sjeff			continue;
997219820Sjeff		}
998219820Sjeff
999219820Sjeff		/*
1000219820Sjeff		 * Check opcode and sequence number for a match.
1001219820Sjeff		 * Could be old data getting to us.
1002219820Sjeff		 */
1003219820Sjeff		if (ntohs(rpkt.sequence) != sequence) {
1004219820Sjeff			if (debug)
1005219820Sjeff				printf("Received sequnce number %d, wanted %d\n",
1006219820Sjeff				       ntohs(rpkt.sequence), sequence);
1007219820Sjeff			continue;
1008219820Sjeff		}
1009219820Sjeff		if (CTL_OP(rpkt.r_m_e_op) != opcode) {
1010219820Sjeff			if (debug)
1011219820Sjeff			    printf(
1012219820Sjeff				    "Received opcode %d, wanted %d (sequence number okay)\n",
1013219820Sjeff				    CTL_OP(rpkt.r_m_e_op), opcode);
1014219820Sjeff			continue;
1015219820Sjeff		}
1016219820Sjeff
1017219820Sjeff		/*
1018219820Sjeff		 * Check the error code.  If non-zero, return it.
1019219820Sjeff		 */
1020219820Sjeff		if (CTL_ISERROR(rpkt.r_m_e_op)) {
1021219820Sjeff			errcode = (ntohs(rpkt.status) >> 8) & 0xff;
1022219820Sjeff			if (CTL_ISMORE(rpkt.r_m_e_op))
1023219820Sjeff				TRACE(1, ("Error code %d received on not-final packet\n",
1024219820Sjeff					  errcode));
1025219820Sjeff			if (errcode == CERR_UNSPEC)
1026219820Sjeff				return ERR_UNSPEC;
1027219820Sjeff			return errcode;
1028219820Sjeff		}
1029219820Sjeff
1030219820Sjeff		/*
1031219820Sjeff		 * Check the association ID to make sure it matches what
1032219820Sjeff		 * we sent.
1033219820Sjeff		 */
1034219820Sjeff		if (ntohs(rpkt.associd) != associd) {
1035219820Sjeff			TRACE(1, ("Association ID %d doesn't match expected %d\n",
1036219820Sjeff				  ntohs(rpkt.associd), associd));
1037219820Sjeff			/*
1038219820Sjeff			 * Hack for silly fuzzballs which, at the time of writing,
1039219820Sjeff			 * return an assID of sys.peer when queried for system variables.
1040219820Sjeff			 */
1041219820Sjeff#ifdef notdef
1042219820Sjeff			continue;
1043219820Sjeff#endif
1044219820Sjeff		}
1045219820Sjeff
1046219820Sjeff		/*
1047219820Sjeff		 * Collect offset and count.  Make sure they make sense.
1048219820Sjeff		 */
1049219820Sjeff		offset = ntohs(rpkt.offset);
1050219820Sjeff		count = ntohs(rpkt.count);
1051219820Sjeff
1052219820Sjeff		/*
1053219820Sjeff		 * validate received payload size is padded to next 32-bit
1054219820Sjeff		 * boundary and no smaller than claimed by rpkt.count
1055219820Sjeff		 */
1056219820Sjeff		if (n & 0x3) {
1057219820Sjeff			TRACE(1, ("Response packet not padded, size = %d\n",
1058219820Sjeff				  n));
1059219820Sjeff			continue;
1060219820Sjeff		}
1061219820Sjeff
1062219820Sjeff		shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
1063219820Sjeff
1064219820Sjeff		if (n < shouldbesize) {
1065219820Sjeff			printf("Response packet claims %u octets payload, above %ld received\n",
1066219820Sjeff			       count, (long)(n - CTL_HEADER_LEN));
1067219820Sjeff			return ERR_INCOMPLETE;
1068219820Sjeff		}
1069219820Sjeff
1070219820Sjeff		if (debug >= 3 && shouldbesize > n) {
1071219820Sjeff			u_int32 key;
1072219820Sjeff			u_int32 *lpkt;
1073219820Sjeff			int maclen;
1074219820Sjeff
1075219820Sjeff			/*
1076219820Sjeff			 * Usually we ignore authentication, but for debugging purposes
1077219820Sjeff			 * we watch it here.
1078219820Sjeff			 */
1079219820Sjeff			/* round to 8 octet boundary */
1080219820Sjeff			shouldbesize = (shouldbesize + 7) & ~7;
1081219820Sjeff
1082219820Sjeff			maclen = n - shouldbesize;
1083219820Sjeff			if (maclen >= (int)MIN_MAC_LEN) {
1084219820Sjeff				printf(
1085219820Sjeff					"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1086219820Sjeff					n, shouldbesize, maclen);
1087219820Sjeff				lpkt = (u_int32 *)&rpkt;
1088219820Sjeff				printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1089219820Sjeff				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1090219820Sjeff				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1091219820Sjeff				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1092219820Sjeff				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1093219820Sjeff				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1094219820Sjeff				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1095219820Sjeff				key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1096219820Sjeff				printf("Authenticated with keyid %lu\n", (u_long)key);
1097219820Sjeff				if (key != 0 && key != info_auth_keyid) {
1098219820Sjeff					printf("We don't know that key\n");
1099219820Sjeff				} else {
1100219820Sjeff					if (authdecrypt(key, (u_int32 *)&rpkt,
1101219820Sjeff					    n - maclen, maclen)) {
1102219820Sjeff						printf("Auth okay!\n");
1103219820Sjeff					} else {
1104219820Sjeff						printf("Auth failed!\n");
1105219820Sjeff					}
1106219820Sjeff				}
1107219820Sjeff			}
1108219820Sjeff		}
1109219820Sjeff
1110219820Sjeff		TRACE(2, ("Got packet, size = %d\n", n));
1111219820Sjeff		if (count > (n - CTL_HEADER_LEN)) {
1112219820Sjeff			TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
1113219820Sjeff				  count, (long)n - CTL_HEADER_LEN));
1114219820Sjeff			continue;
1115219820Sjeff		}
1116219820Sjeff		if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1117219820Sjeff			TRACE(1, ("Received count of 0 in non-final fragment\n"));
1118219820Sjeff			continue;
1119219820Sjeff		}
1120219820Sjeff		if (offset + count > sizeof(pktdata)) {
1121219820Sjeff			TRACE(1, ("Offset %u, count %u, too big for buffer\n",
1122219820Sjeff				  offset, count));
1123219820Sjeff			return ERR_TOOMUCH;
1124219820Sjeff		}
1125219820Sjeff		if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1126219820Sjeff			TRACE(1, ("Received second last fragment packet\n"));
1127219820Sjeff			continue;
1128219820Sjeff		}
1129219820Sjeff
1130219820Sjeff		/*
1131219820Sjeff		 * So far, so good.  Record this fragment, making sure it doesn't
1132219820Sjeff		 * overlap anything.
1133219820Sjeff		 */
1134219820Sjeff		TRACE(2, ("Packet okay\n"));
1135219820Sjeff
1136219820Sjeff		if (numfrags > (MAXFRAGS - 1)) {
1137219820Sjeff			TRACE(2, ("Number of fragments exceeds maximum %d\n",
1138219820Sjeff				  MAXFRAGS - 1));
1139219820Sjeff			return ERR_TOOMUCH;
1140219820Sjeff		}
1141219820Sjeff
1142219820Sjeff		/*
1143219820Sjeff		 * Find the position for the fragment relative to any
1144219820Sjeff		 * previously received.
1145219820Sjeff		 */
1146219820Sjeff		for (f = 0;
1147219820Sjeff		     f < numfrags && offsets[f] < offset;
1148219820Sjeff		     f++) {
1149219820Sjeff			/* empty body */ ;
1150219820Sjeff		}
1151219820Sjeff
1152309378Sjhb		if (f < numfrags && offset == offsets[f]) {
1153309378Sjhb			TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
1154219820Sjeff				  count, offset, counts[f], offsets[f]));
1155219820Sjeff			continue;
1156219820Sjeff		}
1157219820Sjeff
1158219820Sjeff		if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
1159219820Sjeff			TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
1160219820Sjeff				  offset, counts[f-1], offsets[f-1]));
1161219820Sjeff			continue;
1162219820Sjeff		}
1163219820Sjeff
1164219820Sjeff		if (f < numfrags && (offset + count) > offsets[f]) {
1165219820Sjeff			TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
1166219820Sjeff				  count, offset, offsets[f]));
1167219820Sjeff			continue;
1168219820Sjeff		}
1169219820Sjeff
1170219820Sjeff		for (ff = numfrags; ff > f; ff--) {
1171219820Sjeff			offsets[ff] = offsets[ff-1];
1172219820Sjeff			counts[ff] = counts[ff-1];
1173219820Sjeff		}
1174219820Sjeff		offsets[f] = offset;
1175219820Sjeff		counts[f] = count;
1176219820Sjeff		numfrags++;
1177219820Sjeff
1178219820Sjeff		/*
1179219820Sjeff		 * Got that stuffed in right.  Figure out if this was the last.
1180219820Sjeff		 * Record status info out of the last packet.
1181219820Sjeff		 */
1182219820Sjeff		if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1183219820Sjeff			seenlastfrag = 1;
1184219820Sjeff			if (rstatus != 0)
1185219820Sjeff				*rstatus = ntohs(rpkt.status);
1186219820Sjeff		}
1187219820Sjeff
1188219820Sjeff		/*
1189219820Sjeff		 * Copy the data into the data buffer, and bump the
1190219820Sjeff		 * timout base in case we need more.
1191219820Sjeff		 */
1192219820Sjeff		memcpy((char *)pktdata + offset, &rpkt.u, count);
1193219820Sjeff		tobase = (uint32_t)time(NULL);
1194219820Sjeff
1195219820Sjeff		/*
1196219820Sjeff		 * If we've seen the last fragment, look for holes in the sequence.
1197219820Sjeff		 * If there aren't any, we're done.
1198219820Sjeff		 */
1199219820Sjeff#if !defined(SYS_WINNT) && defined(EINTR)
1200219820Sjeff		maybe_final:
1201219820Sjeff#endif
1202219820Sjeff
1203219820Sjeff		if (seenlastfrag && offsets[0] == 0) {
1204219820Sjeff			for (f = 1; f < numfrags; f++)
1205219820Sjeff				if (offsets[f-1] + counts[f-1] !=
1206219820Sjeff				    offsets[f])
1207219820Sjeff					break;
1208219820Sjeff			if (f == numfrags) {
1209219820Sjeff				*rsize = offsets[f-1] + counts[f-1];
1210219820Sjeff				TRACE(1, ("%lu packets reassembled into response\n",
1211219820Sjeff					  (u_long)numfrags));
1212219820Sjeff				return 0;
1213219820Sjeff			}
1214219820Sjeff		}
1215219820Sjeff	}  /* giant for (;;) collecting response packets */
1216219820Sjeff}  /* getresponse() */
1217219820Sjeff
1218219820Sjeff
1219219820Sjeff/*
1220219820Sjeff * sendrequest - format and send a request packet
1221219820Sjeff */
1222219820Sjeffstatic int
1223219820Sjeffsendrequest(
1224219820Sjeff	int opcode,
1225219820Sjeff	associd_t associd,
1226219820Sjeff	int auth,
1227219820Sjeff	size_t qsize,
1228219820Sjeff	const char *qdata
1229219820Sjeff	)
1230219820Sjeff{
1231219820Sjeff	struct ntp_control qpkt;
1232219820Sjeff	size_t	pktsize;
1233219820Sjeff	u_long	key_id;
1234219820Sjeff	char *	pass;
1235219820Sjeff	size_t	maclen;
1236219820Sjeff
1237219820Sjeff	/*
1238219820Sjeff	 * Check to make sure the data will fit in one packet
1239219820Sjeff	 */
1240219820Sjeff	if (qsize > CTL_MAX_DATA_LEN) {
1241219820Sjeff		fprintf(stderr,
1242219820Sjeff			"***Internal error!  qsize (%zu) too large\n",
1243219820Sjeff			qsize);
1244219820Sjeff		return 1;
1245219820Sjeff	}
1246219820Sjeff
1247219820Sjeff	/*
1248219820Sjeff	 * Fill in the packet
1249219820Sjeff	 */
1250219820Sjeff	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1251219820Sjeff	qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
1252219820Sjeff	qpkt.sequence = htons(sequence);
1253219820Sjeff	qpkt.status = 0;
1254219820Sjeff	qpkt.associd = htons((u_short)associd);
1255219820Sjeff	qpkt.offset = 0;
1256219820Sjeff	qpkt.count = htons((u_short)qsize);
1257219820Sjeff
1258219820Sjeff	pktsize = CTL_HEADER_LEN;
1259219820Sjeff
1260219820Sjeff	/*
1261219820Sjeff	 * If we have data, copy and pad it out to a 32-bit boundary.
1262219820Sjeff	 */
1263219820Sjeff	if (qsize > 0) {
1264219820Sjeff		memcpy(&qpkt.u, qdata, (size_t)qsize);
1265219820Sjeff		pktsize += qsize;
1266219820Sjeff		while (pktsize & (sizeof(u_int32) - 1)) {
1267219820Sjeff			qpkt.u.data[qsize++] = 0;
1268219820Sjeff			pktsize++;
1269219820Sjeff		}
1270219820Sjeff	}
1271219820Sjeff
1272219820Sjeff	/*
1273219820Sjeff	 * If it isn't authenticated we can just send it.  Otherwise
1274219820Sjeff	 * we're going to have to think about it a little.
1275219820Sjeff	 */
1276219820Sjeff	if (!auth && !always_auth) {
1277219820Sjeff		return sendpkt(&qpkt, pktsize);
1278219820Sjeff	}
1279219820Sjeff
1280219820Sjeff	/*
1281219820Sjeff	 * Pad out packet to a multiple of 8 octets to be sure
1282219820Sjeff	 * receiver can handle it.
1283219820Sjeff	 */
1284219820Sjeff	while (pktsize & 7) {
1285219820Sjeff		qpkt.u.data[qsize++] = 0;
1286219820Sjeff		pktsize++;
1287219820Sjeff	}
1288219820Sjeff
1289219820Sjeff	/*
1290219820Sjeff	 * Get the keyid and the password if we don't have one.
1291219820Sjeff	 */
1292219820Sjeff	if (info_auth_keyid == 0) {
1293219820Sjeff		key_id = getkeyid("Keyid: ");
1294219820Sjeff		if (key_id == 0 || key_id > NTP_MAXKEY) {
1295219820Sjeff			fprintf(stderr,
1296219820Sjeff				"Invalid key identifier\n");
1297219820Sjeff			return 1;
1298219820Sjeff		}
1299219820Sjeff		info_auth_keyid = key_id;
1300219820Sjeff	}
1301219820Sjeff	if (!authistrusted(info_auth_keyid)) {
1302219820Sjeff		pass = getpass_keytype(info_auth_keytype);
1303219820Sjeff		if ('\0' == pass[0]) {
1304219820Sjeff			fprintf(stderr, "Invalid password\n");
1305219820Sjeff			return 1;
1306219820Sjeff		}
1307219820Sjeff		authusekey(info_auth_keyid, info_auth_keytype,
1308			   (u_char *)pass);
1309		authtrust(info_auth_keyid, 1);
1310	}
1311
1312	/*
1313	 * Do the encryption.
1314	 */
1315	maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1316	if (!maclen) {
1317		fprintf(stderr, "Key not found\n");
1318		return 1;
1319	} else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1320		fprintf(stderr,
1321			"%zu octet MAC, %zu expected with %zu octet digest\n",
1322			maclen, (info_auth_hashlen + sizeof(keyid_t)),
1323			info_auth_hashlen);
1324		return 1;
1325	}
1326
1327	return sendpkt((char *)&qpkt, pktsize + maclen);
1328}
1329
1330
1331/*
1332 * show_error_msg - display the error text for a mode 6 error response.
1333 */
1334void
1335show_error_msg(
1336	int		m6resp,
1337	associd_t	associd
1338	)
1339{
1340	if (numhosts > 1)
1341		fprintf(stderr, "server=%s ", currenthost);
1342
1343	switch (m6resp) {
1344
1345	case CERR_BADFMT:
1346		fprintf(stderr,
1347		    "***Server reports a bad format request packet\n");
1348		break;
1349
1350	case CERR_PERMISSION:
1351		fprintf(stderr,
1352		    "***Server disallowed request (authentication?)\n");
1353		break;
1354
1355	case CERR_BADOP:
1356		fprintf(stderr,
1357		    "***Server reports a bad opcode in request\n");
1358		break;
1359
1360	case CERR_BADASSOC:
1361		fprintf(stderr,
1362		    "***Association ID %d unknown to server\n",
1363		    associd);
1364		break;
1365
1366	case CERR_UNKNOWNVAR:
1367		fprintf(stderr,
1368		    "***A request variable unknown to the server\n");
1369		break;
1370
1371	case CERR_BADVALUE:
1372		fprintf(stderr,
1373		    "***Server indicates a request variable was bad\n");
1374		break;
1375
1376	case ERR_UNSPEC:
1377		fprintf(stderr,
1378		    "***Server returned an unspecified error\n");
1379		break;
1380
1381	case ERR_TIMEOUT:
1382		fprintf(stderr, "***Request timed out\n");
1383		break;
1384
1385	case ERR_INCOMPLETE:
1386		fprintf(stderr,
1387		    "***Response from server was incomplete\n");
1388		break;
1389
1390	case ERR_TOOMUCH:
1391		fprintf(stderr,
1392		    "***Buffer size exceeded for returned data\n");
1393		break;
1394
1395	default:
1396		fprintf(stderr,
1397		    "***Server returns unknown error code %d\n",
1398		    m6resp);
1399	}
1400}
1401
1402/*
1403 * doquery - send a request and process the response, displaying
1404 *	     error messages for any error responses.
1405 */
1406int
1407doquery(
1408	int opcode,
1409	associd_t associd,
1410	int auth,
1411	size_t qsize,
1412	const char *qdata,
1413	u_short *rstatus,
1414	size_t *rsize,
1415	const char **rdata
1416	)
1417{
1418	return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
1419			 rsize, rdata, FALSE);
1420}
1421
1422
1423/*
1424 * doqueryex - send a request and process the response, optionally
1425 *	       displaying error messages for any error responses.
1426 */
1427int
1428doqueryex(
1429	int opcode,
1430	associd_t associd,
1431	int auth,
1432	size_t qsize,
1433	const char *qdata,
1434	u_short *rstatus,
1435	size_t *rsize,
1436	const char **rdata,
1437	int quiet
1438	)
1439{
1440	int res;
1441	int done;
1442
1443	/*
1444	 * Check to make sure host is open
1445	 */
1446	if (!havehost) {
1447		fprintf(stderr, "***No host open, use `host' command\n");
1448		return -1;
1449	}
1450
1451	done = 0;
1452	sequence++;
1453
1454    again:
1455	/*
1456	 * send a request
1457	 */
1458	res = sendrequest(opcode, associd, auth, qsize, qdata);
1459	if (res != 0)
1460		return res;
1461
1462	/*
1463	 * Get the response.  If we got a standard error, print a message
1464	 */
1465	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
1466
1467	if (res > 0) {
1468		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
1469			if (res == ERR_INCOMPLETE) {
1470				/*
1471				 * better bump the sequence so we don't
1472				 * get confused about differing fragments.
1473				 */
1474				sequence++;
1475			}
1476			done = 1;
1477			goto again;
1478		}
1479		if (!quiet)
1480			show_error_msg(res, associd);
1481
1482	}
1483	return res;
1484}
1485
1486
1487#ifndef BUILD_AS_LIB
1488/*
1489 * getcmds - read commands from the standard input and execute them
1490 */
1491static void
1492getcmds(void)
1493{
1494	char *	line;
1495	int	count;
1496
1497	ntp_readline_init(interactive ? prompt : NULL);
1498
1499	for (;;) {
1500		line = ntp_readline(&count);
1501		if (NULL == line)
1502			break;
1503		docmd(line);
1504		free(line);
1505	}
1506
1507	ntp_readline_uninit();
1508}
1509#endif /* !BUILD_AS_LIB */
1510
1511
1512#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
1513/*
1514 * abortcmd - catch interrupts and abort the current command
1515 */
1516static int
1517abortcmd(void)
1518{
1519	if (current_output == stdout)
1520		(void) fflush(stdout);
1521	putc('\n', stderr);
1522	(void) fflush(stderr);
1523	if (jump) {
1524		jump = 0;
1525		longjmp(interrupt_buf, 1);
1526	}
1527	return TRUE;
1528}
1529#endif	/* !SYS_WINNT && !BUILD_AS_LIB */
1530
1531
1532#ifndef	BUILD_AS_LIB
1533/*
1534 * docmd - decode the command line and execute a command
1535 */
1536static void
1537docmd(
1538	const char *cmdline
1539	)
1540{
1541	char *tokens[1+MAXARGS+2];
1542	struct parse pcmd;
1543	int ntok;
1544	static int i;
1545	struct xcmd *xcmd;
1546
1547	/*
1548	 * Tokenize the command line.  If nothing on it, return.
1549	 */
1550	tokenize(cmdline, tokens, &ntok);
1551	if (ntok == 0)
1552	    return;
1553
1554	/*
1555	 * Find the appropriate command description.
1556	 */
1557	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1558	if (i == 0) {
1559		(void) fprintf(stderr, "***Command `%s' unknown\n",
1560			       tokens[0]);
1561		return;
1562	} else if (i >= 2) {
1563		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
1564			       tokens[0]);
1565		return;
1566	}
1567
1568	/* Warn about ignored extra args */
1569	for (i = MAXARGS + 1; i < ntok ; ++i) {
1570		fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
1571	}
1572
1573	/*
1574	 * Save the keyword, then walk through the arguments, interpreting
1575	 * as we go.
1576	 */
1577	pcmd.keyword = tokens[0];
1578	pcmd.nargs = 0;
1579	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
1580		if ((i+1) >= ntok) {
1581			if (!(xcmd->arg[i] & OPT)) {
1582				printusage(xcmd, stderr);
1583				return;
1584			}
1585			break;
1586		}
1587		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1588			break;
1589		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1590			return;
1591		pcmd.nargs++;
1592	}
1593
1594	i++;
1595	if (i < ntok && *tokens[i] == '>') {
1596		char *fname;
1597
1598		if (*(tokens[i]+1) != '\0')
1599			fname = tokens[i]+1;
1600		else if ((i+1) < ntok)
1601			fname = tokens[i+1];
1602		else {
1603			(void) fprintf(stderr, "***No file for redirect\n");
1604			return;
1605		}
1606
1607		current_output = fopen(fname, "w");
1608		if (current_output == NULL) {
1609			(void) fprintf(stderr, "***Error opening %s: ", fname);
1610			perror("");
1611			return;
1612		}
1613		i = 1;		/* flag we need a close */
1614	} else {
1615		current_output = stdout;
1616		i = 0;		/* flag no close */
1617	}
1618
1619	if (interactive && setjmp(interrupt_buf)) {
1620		jump = 0;
1621		return;
1622	} else {
1623		jump++;
1624		(xcmd->handler)(&pcmd, current_output);
1625		jump = 0;	/* HMS: 961106: was after fclose() */
1626		if (i) (void) fclose(current_output);
1627	}
1628
1629	return;
1630}
1631
1632
1633/*
1634 * tokenize - turn a command line into tokens
1635 *
1636 * SK: Modified to allow a quoted string
1637 *
1638 * HMS: If the first character of the first token is a ':' then (after
1639 * eating inter-token whitespace) the 2nd token is the rest of the line.
1640 */
1641
1642static void
1643tokenize(
1644	const char *line,
1645	char **tokens,
1646	int *ntok
1647	)
1648{
1649	register const char *cp;
1650	register char *sp;
1651	static char tspace[MAXLINE];
1652
1653	sp = tspace;
1654	cp = line;
1655	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1656		tokens[*ntok] = sp;
1657
1658		/* Skip inter-token whitespace */
1659		while (ISSPACE(*cp))
1660		    cp++;
1661
1662		/* If we're at EOL we're done */
1663		if (ISEOL(*cp))
1664		    break;
1665
1666		/* If this is the 2nd token and the first token begins
1667		 * with a ':', then just grab to EOL.
1668		 */
1669
1670		if (*ntok == 1 && tokens[0][0] == ':') {
1671			do {
1672				if (sp - tspace >= MAXLINE)
1673					goto toobig;
1674				*sp++ = *cp++;
1675			} while (!ISEOL(*cp));
1676		}
1677
1678		/* Check if this token begins with a double quote.
1679		 * If yes, continue reading till the next double quote
1680		 */
1681		else if (*cp == '\"') {
1682			++cp;
1683			do {
1684				if (sp - tspace >= MAXLINE)
1685					goto toobig;
1686				*sp++ = *cp++;
1687			} while ((*cp != '\"') && !ISEOL(*cp));
1688			/* HMS: a missing closing " should be an error */
1689		}
1690		else {
1691			do {
1692				if (sp - tspace >= MAXLINE)
1693					goto toobig;
1694				*sp++ = *cp++;
1695			} while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1696			/* HMS: Why check for a " in the previous line? */
1697		}
1698
1699		if (sp - tspace >= MAXLINE)
1700			goto toobig;
1701		*sp++ = '\0';
1702	}
1703	return;
1704
1705  toobig:
1706	*ntok = 0;
1707	fprintf(stderr,
1708		"***Line `%s' is too big\n",
1709		line);
1710	return;
1711}
1712
1713
1714/*
1715 * getarg - interpret an argument token
1716 */
1717static int
1718getarg(
1719	const char *str,
1720	int code,
1721	arg_v *argp
1722	)
1723{
1724	u_long ul;
1725
1726	switch (code & ~OPT) {
1727	case NTP_STR:
1728		argp->string = str;
1729		break;
1730
1731	case NTP_ADD:
1732		if (!getnetnum(str, &argp->netnum, NULL, 0))
1733			return 0;
1734		break;
1735
1736	case NTP_UINT:
1737		if ('&' == str[0]) {
1738			if (!atouint(&str[1], &ul)) {
1739				fprintf(stderr,
1740					"***Association index `%s' invalid/undecodable\n",
1741					str);
1742				return 0;
1743			}
1744			if (0 == numassoc) {
1745				dogetassoc(stdout);
1746				if (0 == numassoc) {
1747					fprintf(stderr,
1748						"***No associations found, `%s' unknown\n",
1749						str);
1750					return 0;
1751				}
1752			}
1753			ul = min(ul, numassoc);
1754			argp->uval = assoc_cache[ul - 1].assid;
1755			break;
1756		}
1757		if (!atouint(str, &argp->uval)) {
1758			fprintf(stderr, "***Illegal unsigned value %s\n",
1759				str);
1760			return 0;
1761		}
1762		break;
1763
1764	case NTP_INT:
1765		if (!atoint(str, &argp->ival)) {
1766			fprintf(stderr, "***Illegal integer value %s\n",
1767				str);
1768			return 0;
1769		}
1770		break;
1771
1772	case IP_VERSION:
1773		if (!strcmp("-6", str)) {
1774			argp->ival = 6;
1775		} else if (!strcmp("-4", str)) {
1776			argp->ival = 4;
1777		} else {
1778			fprintf(stderr, "***Version must be either 4 or 6\n");
1779			return 0;
1780		}
1781		break;
1782	}
1783
1784	return 1;
1785}
1786#endif	/* !BUILD_AS_LIB */
1787
1788
1789/*
1790 * findcmd - find a command in a command description table
1791 */
1792static int
1793findcmd(
1794	const char *	str,
1795	struct xcmd *	clist1,
1796	struct xcmd *	clist2,
1797	struct xcmd **	cmd
1798	)
1799{
1800	struct xcmd *cl;
1801	size_t clen;
1802	int nmatch;
1803	struct xcmd *nearmatch = NULL;
1804	struct xcmd *clist;
1805
1806	clen = strlen(str);
1807	nmatch = 0;
1808	if (clist1 != 0)
1809	    clist = clist1;
1810	else if (clist2 != 0)
1811	    clist = clist2;
1812	else
1813	    return 0;
1814
1815    again:
1816	for (cl = clist; cl->keyword != 0; cl++) {
1817		/* do a first character check, for efficiency */
1818		if (*str != *(cl->keyword))
1819		    continue;
1820		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1821			/*
1822			 * Could be extact match, could be approximate.
1823			 * Is exact if the length of the keyword is the
1824			 * same as the str.
1825			 */
1826			if (*((cl->keyword) + clen) == '\0') {
1827				*cmd = cl;
1828				return 1;
1829			}
1830			nmatch++;
1831			nearmatch = cl;
1832		}
1833	}
1834
1835	/*
1836	 * See if there is more to do.  If so, go again.  Sorry about the
1837	 * goto, too much looking at BSD sources...
1838	 */
1839	if (clist == clist1 && clist2 != 0) {
1840		clist = clist2;
1841		goto again;
1842	}
1843
1844	/*
1845	 * If we got extactly 1 near match, use it, else return number
1846	 * of matches.
1847	 */
1848	if (nmatch == 1) {
1849		*cmd = nearmatch;
1850		return 1;
1851	}
1852	return nmatch;
1853}
1854
1855
1856/*
1857 * getnetnum - given a host name, return its net number
1858 *	       and (optional) full name
1859 */
1860int
1861getnetnum(
1862	const char *hname,
1863	sockaddr_u *num,
1864	char *fullhost,
1865	int af
1866	)
1867{
1868	struct addrinfo hints, *ai = NULL;
1869
1870	ZERO(hints);
1871	hints.ai_flags = AI_CANONNAME;
1872#ifdef AI_ADDRCONFIG
1873	hints.ai_flags |= AI_ADDRCONFIG;
1874#endif
1875
1876	/*
1877	 * decodenetnum only works with addresses, but handles syntax
1878	 * that getaddrinfo doesn't:  [2001::1]:1234
1879	 */
1880	if (decodenetnum(hname, num)) {
1881		if (fullhost != NULL)
1882			getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1883				    LENHOSTNAME, NULL, 0, 0);
1884		return 1;
1885	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1886		INSIST(sizeof(*num) >= ai->ai_addrlen);
1887		memcpy(num, ai->ai_addr, ai->ai_addrlen);
1888		if (fullhost != NULL) {
1889			if (ai->ai_canonname != NULL)
1890				strlcpy(fullhost, ai->ai_canonname,
1891					LENHOSTNAME);
1892			else
1893				getnameinfo(&num->sa, SOCKLEN(num),
1894					    fullhost, LENHOSTNAME, NULL,
1895					    0, 0);
1896		}
1897		freeaddrinfo(ai);
1898		return 1;
1899	}
1900	fprintf(stderr, "***Can't find host %s\n", hname);
1901
1902	return 0;
1903}
1904
1905
1906/*
1907 * nntohost - convert network number to host name.  This routine enforces
1908 *	       the showhostnames setting.
1909 */
1910const char *
1911nntohost(
1912	sockaddr_u *netnum
1913	)
1914{
1915	return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
1916}
1917
1918
1919/*
1920 * nntohost_col - convert network number to host name in fixed width.
1921 *		  This routine enforces the showhostnames setting.
1922 *		  When displaying hostnames longer than the width,
1923 *		  the first part of the hostname is displayed.  When
1924 *		  displaying numeric addresses longer than the width,
1925 *		  Such as IPv6 addresses, the caller decides whether
1926 *		  the first or last of the numeric address is used.
1927 */
1928const char *
1929nntohost_col(
1930	sockaddr_u *	addr,
1931	size_t		width,
1932	int		preserve_lowaddrbits
1933	)
1934{
1935	const char *	out;
1936
1937	if (!showhostnames || SOCK_UNSPEC(addr)) {
1938		if (preserve_lowaddrbits)
1939			out = trunc_left(stoa(addr), width);
1940		else
1941			out = trunc_right(stoa(addr), width);
1942	} else if (ISREFCLOCKADR(addr)) {
1943		out = refnumtoa(addr);
1944	} else {
1945		out = trunc_right(socktohost(addr), width);
1946	}
1947	return out;
1948}
1949
1950
1951/*
1952 * nntohostp() is the same as nntohost() plus a :port suffix
1953 */
1954const char *
1955nntohostp(
1956	sockaddr_u *netnum
1957	)
1958{
1959	const char *	hostn;
1960	char *		buf;
1961
1962	if (!showhostnames || SOCK_UNSPEC(netnum))
1963		return sptoa(netnum);
1964	else if (ISREFCLOCKADR(netnum))
1965		return refnumtoa(netnum);
1966
1967	hostn = socktohost(netnum);
1968	LIB_GETBUF(buf);
1969	snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
1970
1971	return buf;
1972}
1973
1974/*
1975 * rtdatetolfp - decode an RT-11 date into an l_fp
1976 */
1977static int
1978rtdatetolfp(
1979	char *str,
1980	l_fp *lfp
1981	)
1982{
1983	register char *cp;
1984	register int i;
1985	struct calendar cal;
1986	char buf[4];
1987
1988	cal.yearday = 0;
1989
1990	/*
1991	 * An RT-11 date looks like:
1992	 *
1993	 * d[d]-Mth-y[y] hh:mm:ss
1994	 *
1995	 * (No docs, but assume 4-digit years are also legal...)
1996	 *
1997	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
1998	 */
1999	cp = str;
2000	if (!isdigit((int)*cp)) {
2001		if (*cp == '-') {
2002			/*
2003			 * Catch special case
2004			 */
2005			L_CLR(lfp);
2006			return 1;
2007		}
2008		return 0;
2009	}
2010
2011	cal.monthday = (u_char) (*cp++ - '0');	/* ascii dependent */
2012	if (isdigit((int)*cp)) {
2013		cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
2014		cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
2015	}
2016
2017	if (*cp++ != '-')
2018	    return 0;
2019
2020	for (i = 0; i < 3; i++)
2021	    buf[i] = *cp++;
2022	buf[3] = '\0';
2023
2024	for (i = 0; i < 12; i++)
2025	    if (STREQ(buf, months[i]))
2026		break;
2027	if (i == 12)
2028	    return 0;
2029	cal.month = (u_char)(i + 1);
2030
2031	if (*cp++ != '-')
2032	    return 0;
2033
2034	if (!isdigit((int)*cp))
2035	    return 0;
2036	cal.year = (u_short)(*cp++ - '0');
2037	if (isdigit((int)*cp)) {
2038		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2039		cal.year = (u_short)(*cp++ - '0');
2040	}
2041	if (isdigit((int)*cp)) {
2042		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2043		cal.year = (u_short)(cal.year + *cp++ - '0');
2044	}
2045	if (isdigit((int)*cp)) {
2046		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2047		cal.year = (u_short)(cal.year + *cp++ - '0');
2048	}
2049
2050	/*
2051	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
2052	 */
2053	if (cal.year == 0) {
2054		L_CLR(lfp);
2055		return 1;
2056	}
2057
2058	if (*cp++ != ' ' || !isdigit((int)*cp))
2059	    return 0;
2060	cal.hour = (u_char)(*cp++ - '0');
2061	if (isdigit((int)*cp)) {
2062		cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
2063		cal.hour = (u_char)(cal.hour + *cp++ - '0');
2064	}
2065
2066	if (*cp++ != ':' || !isdigit((int)*cp))
2067	    return 0;
2068	cal.minute = (u_char)(*cp++ - '0');
2069	if (isdigit((int)*cp)) {
2070		cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
2071		cal.minute = (u_char)(cal.minute + *cp++ - '0');
2072	}
2073
2074	if (*cp++ != ':' || !isdigit((int)*cp))
2075	    return 0;
2076	cal.second = (u_char)(*cp++ - '0');
2077	if (isdigit((int)*cp)) {
2078		cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
2079		cal.second = (u_char)(cal.second + *cp++ - '0');
2080	}
2081
2082	/*
2083	 * For RT-11, 1972 seems to be the pivot year
2084	 */
2085	if (cal.year < 72)
2086		cal.year += 2000;
2087	if (cal.year < 100)
2088		cal.year += 1900;
2089
2090	lfp->l_ui = caltontp(&cal);
2091	lfp->l_uf = 0;
2092	return 1;
2093}
2094
2095
2096/*
2097 * decodets - decode a timestamp into an l_fp format number, with
2098 *	      consideration of fuzzball formats.
2099 */
2100int
2101decodets(
2102	char *str,
2103	l_fp *lfp
2104	)
2105{
2106	char *cp;
2107	char buf[30];
2108	size_t b;
2109
2110	/*
2111	 * If it starts with a 0x, decode as hex.
2112	 */
2113	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
2114		return hextolfp(str+2, lfp);
2115
2116	/*
2117	 * If it starts with a '"', try it as an RT-11 date.
2118	 */
2119	if (*str == '"') {
2120		cp = str + 1;
2121		b = 0;
2122		while ('"' != *cp && '\0' != *cp &&
2123		       b < COUNTOF(buf) - 1)
2124			buf[b++] = *cp++;
2125		buf[b] = '\0';
2126		return rtdatetolfp(buf, lfp);
2127	}
2128
2129	/*
2130	 * Might still be hex.  Check out the first character.  Talk
2131	 * about heuristics!
2132	 */
2133	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2134		return hextolfp(str, lfp);
2135
2136	/*
2137	 * Try it as a decimal.  If this fails, try as an unquoted
2138	 * RT-11 date.  This code should go away eventually.
2139	 */
2140	if (atolfp(str, lfp))
2141		return 1;
2142
2143	return rtdatetolfp(str, lfp);
2144}
2145
2146
2147/*
2148 * decodetime - decode a time value.  It should be in milliseconds
2149 */
2150int
2151decodetime(
2152	char *str,
2153	l_fp *lfp
2154	)
2155{
2156	return mstolfp(str, lfp);
2157}
2158
2159
2160/*
2161 * decodeint - decode an integer
2162 */
2163int
2164decodeint(
2165	char *str,
2166	long *val
2167	)
2168{
2169	if (*str == '0') {
2170		if (*(str+1) == 'x' || *(str+1) == 'X')
2171		    return hextoint(str+2, (u_long *)val);
2172		return octtoint(str, (u_long *)val);
2173	}
2174	return atoint(str, val);
2175}
2176
2177
2178/*
2179 * decodeuint - decode an unsigned integer
2180 */
2181int
2182decodeuint(
2183	char *str,
2184	u_long *val
2185	)
2186{
2187	if (*str == '0') {
2188		if (*(str + 1) == 'x' || *(str + 1) == 'X')
2189			return (hextoint(str + 2, val));
2190		return (octtoint(str, val));
2191	}
2192	return (atouint(str, val));
2193}
2194
2195
2196/*
2197 * decodearr - decode an array of time values
2198 */
2199static int
2200decodearr(
2201	char *str,
2202	int *narr,
2203	l_fp *lfparr
2204	)
2205{
2206	register char *cp, *bp;
2207	register l_fp *lfp;
2208	char buf[60];
2209
2210	lfp = lfparr;
2211	cp = str;
2212	*narr = 0;
2213
2214	while (*narr < 8) {
2215		while (isspace((int)*cp))
2216		    cp++;
2217		if (*cp == '\0')
2218		    break;
2219
2220		bp = buf;
2221		while (!isspace((int)*cp) && *cp != '\0')
2222		    *bp++ = *cp++;
2223		*bp++ = '\0';
2224
2225		if (!decodetime(buf, lfp))
2226		    return 0;
2227		(*narr)++;
2228		lfp++;
2229	}
2230	return 1;
2231}
2232
2233
2234/*
2235 * Finally, the built in command handlers
2236 */
2237
2238/*
2239 * help - tell about commands, or details of a particular command
2240 */
2241static void
2242help(
2243	struct parse *pcmd,
2244	FILE *fp
2245	)
2246{
2247	struct xcmd *xcp = NULL;	/* quiet warning */
2248	const char *cmd;
2249	const char *list[100];
2250	size_t word, words;
2251	size_t row, rows;
2252	size_t col, cols;
2253	size_t length;
2254
2255	if (pcmd->nargs == 0) {
2256		words = 0;
2257		for (xcp = builtins; xcp->keyword != NULL; xcp++) {
2258			if (*(xcp->keyword) != '?' &&
2259			    words < COUNTOF(list))
2260				list[words++] = xcp->keyword;
2261		}
2262		for (xcp = opcmds; xcp->keyword != NULL; xcp++)
2263			if (words < COUNTOF(list))
2264				list[words++] = xcp->keyword;
2265
2266		qsort((void *)list, words, sizeof(list[0]), helpsort);
2267		col = 0;
2268		for (word = 0; word < words; word++) {
2269			length = strlen(list[word]);
2270			col = max(col, length);
2271		}
2272
2273		cols = SCREENWIDTH / ++col;
2274		rows = (words + cols - 1) / cols;
2275
2276		fprintf(fp, "ntpq commands:\n");
2277
2278		for (row = 0; row < rows; row++) {
2279			for (word = row; word < words; word += rows)
2280				fprintf(fp, "%-*.*s", (int)col,
2281					(int)col - 1, list[word]);
2282			fprintf(fp, "\n");
2283		}
2284	} else {
2285		cmd = pcmd->argval[0].string;
2286		words = findcmd(cmd, builtins, opcmds, &xcp);
2287		if (words == 0) {
2288			fprintf(stderr,
2289				"Command `%s' is unknown\n", cmd);
2290			return;
2291		} else if (words >= 2) {
2292			fprintf(stderr,
2293				"Command `%s' is ambiguous\n", cmd);
2294			return;
2295		}
2296		fprintf(fp, "function: %s\n", xcp->comment);
2297		printusage(xcp, fp);
2298	}
2299}
2300
2301
2302/*
2303 * helpsort - do hostname qsort comparisons
2304 */
2305static int
2306helpsort(
2307	const void *t1,
2308	const void *t2
2309	)
2310{
2311	const char * const *	name1 = t1;
2312	const char * const *	name2 = t2;
2313
2314	return strcmp(*name1, *name2);
2315}
2316
2317
2318/*
2319 * printusage - print usage information for a command
2320 */
2321static void
2322printusage(
2323	struct xcmd *xcp,
2324	FILE *fp
2325	)
2326{
2327	register int i;
2328
2329	/* XXX: Do we need to warn about extra args here too? */
2330
2331	(void) fprintf(fp, "usage: %s", xcp->keyword);
2332	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
2333		if (xcp->arg[i] & OPT)
2334		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
2335		else
2336		    (void) fprintf(fp, " %s", xcp->desc[i]);
2337	}
2338	(void) fprintf(fp, "\n");
2339}
2340
2341
2342/*
2343 * timeout - set time out time
2344 */
2345static void
2346timeout(
2347	struct parse *pcmd,
2348	FILE *fp
2349	)
2350{
2351	int val;
2352
2353	if (pcmd->nargs == 0) {
2354		val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
2355		(void) fprintf(fp, "primary timeout %d ms\n", val);
2356	} else {
2357		tvout.tv_sec = pcmd->argval[0].uval / 1000;
2358		tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
2359			* 1000;
2360	}
2361}
2362
2363
2364/*
2365 * auth_delay - set delay for auth requests
2366 */
2367static void
2368auth_delay(
2369	struct parse *pcmd,
2370	FILE *fp
2371	)
2372{
2373	int isneg;
2374	u_long val;
2375
2376	if (pcmd->nargs == 0) {
2377		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
2378		(void) fprintf(fp, "delay %lu ms\n", val);
2379	} else {
2380		if (pcmd->argval[0].ival < 0) {
2381			isneg = 1;
2382			val = (u_long)(-pcmd->argval[0].ival);
2383		} else {
2384			isneg = 0;
2385			val = (u_long)pcmd->argval[0].ival;
2386		}
2387
2388		delay_time.l_ui = val / 1000;
2389		val %= 1000;
2390		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
2391
2392		if (isneg)
2393		    L_NEG(&delay_time);
2394	}
2395}
2396
2397
2398/*
2399 * host - set the host we are dealing with.
2400 */
2401static void
2402host(
2403	struct parse *pcmd,
2404	FILE *fp
2405	)
2406{
2407	int i;
2408
2409	if (pcmd->nargs == 0) {
2410		if (havehost)
2411			(void) fprintf(fp, "current host is %s\n",
2412					   currenthost);
2413		else
2414			(void) fprintf(fp, "no current host\n");
2415		return;
2416	}
2417
2418	i = 0;
2419	ai_fam_templ = ai_fam_default;
2420	if (pcmd->nargs == 2) {
2421		if (!strcmp("-4", pcmd->argval[i].string))
2422			ai_fam_templ = AF_INET;
2423		else if (!strcmp("-6", pcmd->argval[i].string))
2424			ai_fam_templ = AF_INET6;
2425		else
2426			goto no_change;
2427		i = 1;
2428	}
2429	if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
2430		fprintf(fp, "current host set to %s\n", currenthost);
2431	} else {
2432    no_change:
2433		if (havehost)
2434			fprintf(fp, "current host remains %s\n",
2435				currenthost);
2436		else
2437			fprintf(fp, "still no current host\n");
2438	}
2439}
2440
2441
2442/*
2443 * poll - do one (or more) polls of the host via NTP
2444 */
2445/*ARGSUSED*/
2446static void
2447ntp_poll(
2448	struct parse *pcmd,
2449	FILE *fp
2450	)
2451{
2452	(void) fprintf(fp, "poll not implemented yet\n");
2453}
2454
2455
2456/*
2457 * showdrefid2str - return a string explanation of the value of drefid
2458 */
2459static char *
2460showdrefid2str(void)
2461{
2462	switch (drefid) {
2463	    case REFID_HASH:
2464	    	return "hash";
2465	    case REFID_IPV4:
2466	    	return "ipv4";
2467	    default:
2468	    	return "Unknown";
2469	}
2470}
2471
2472
2473/*
2474 * drefid - display/change "display hash"
2475 */
2476static void
2477showdrefid(
2478	struct parse *pcmd,
2479	FILE *fp
2480	)
2481{
2482	if (pcmd->nargs == 0) {
2483		(void) fprintf(fp, "drefid value is %s\n", showdrefid2str());
2484		return;
2485	} else if (STREQ(pcmd->argval[0].string, "hash")) {
2486		drefid = REFID_HASH;
2487	} else if (STREQ(pcmd->argval[0].string, "ipv4")) {
2488		drefid = REFID_IPV4;
2489	} else {
2490		(void) fprintf(fp, "What?\n");
2491		return;
2492	}
2493	(void) fprintf(fp, "drefid value set to %s\n", showdrefid2str());
2494}
2495
2496
2497/*
2498 * keyid - get a keyid to use for authenticating requests
2499 */
2500static void
2501keyid(
2502	struct parse *pcmd,
2503	FILE *fp
2504	)
2505{
2506	if (pcmd->nargs == 0) {
2507		if (info_auth_keyid == 0)
2508		    (void) fprintf(fp, "no keyid defined\n");
2509		else
2510		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
2511	} else {
2512		/* allow zero so that keyid can be cleared. */
2513		if(pcmd->argval[0].uval > NTP_MAXKEY)
2514		    (void) fprintf(fp, "Invalid key identifier\n");
2515		info_auth_keyid = pcmd->argval[0].uval;
2516	}
2517}
2518
2519/*
2520 * keytype - get type of key to use for authenticating requests
2521 */
2522static void
2523keytype(
2524	struct parse *pcmd,
2525	FILE *fp
2526	)
2527{
2528	const char *	digest_name;
2529	size_t		digest_len;
2530	int		key_type;
2531
2532	if (!pcmd->nargs) {
2533		fprintf(fp, "keytype is %s with %lu octet digests\n",
2534			keytype_name(info_auth_keytype),
2535			(u_long)info_auth_hashlen);
2536		return;
2537	}
2538
2539	digest_name = pcmd->argval[0].string;
2540	digest_len = 0;
2541	key_type = keytype_from_text(digest_name, &digest_len);
2542
2543	if (!key_type) {
2544		fprintf(fp, "keytype is not valid. "
2545#ifdef OPENSSL
2546			"Type \"help keytype\" for the available digest types.\n");
2547#else
2548			"Only \"md5\" is available.\n");
2549#endif
2550		return;
2551	}
2552
2553	info_auth_keytype = key_type;
2554	info_auth_hashlen = digest_len;
2555}
2556
2557
2558/*
2559 * passwd - get an authentication key
2560 */
2561/*ARGSUSED*/
2562static void
2563passwd(
2564	struct parse *pcmd,
2565	FILE *fp
2566	)
2567{
2568	const char *pass;
2569
2570	if (info_auth_keyid == 0) {
2571		info_auth_keyid = getkeyid("Keyid: ");
2572		if (info_auth_keyid == 0) {
2573			(void)fprintf(fp, "Keyid must be defined\n");
2574			return;
2575		}
2576	}
2577	if (pcmd->nargs >= 1)
2578		pass = pcmd->argval[0].string;
2579	else {
2580		pass = getpass_keytype(info_auth_keytype);
2581		if ('\0' == pass[0]) {
2582			fprintf(fp, "Password unchanged\n");
2583			return;
2584		}
2585	}
2586	authusekey(info_auth_keyid, info_auth_keytype,
2587		   (const u_char *)pass);
2588	authtrust(info_auth_keyid, 1);
2589}
2590
2591
2592/*
2593 * hostnames - set the showhostnames flag
2594 */
2595static void
2596hostnames(
2597	struct parse *pcmd,
2598	FILE *fp
2599	)
2600{
2601	if (pcmd->nargs == 0) {
2602		if (showhostnames)
2603		    (void) fprintf(fp, "hostnames being shown\n");
2604		else
2605		    (void) fprintf(fp, "hostnames not being shown\n");
2606	} else {
2607		if (STREQ(pcmd->argval[0].string, "yes"))
2608		    showhostnames = 1;
2609		else if (STREQ(pcmd->argval[0].string, "no"))
2610		    showhostnames = 0;
2611		else
2612		    (void)fprintf(stderr, "What?\n");
2613	}
2614}
2615
2616
2617
2618/*
2619 * setdebug - set/change debugging level
2620 */
2621static void
2622setdebug(
2623	struct parse *pcmd,
2624	FILE *fp
2625	)
2626{
2627	if (pcmd->nargs == 0) {
2628		(void) fprintf(fp, "debug level is %d\n", debug);
2629		return;
2630	} else if (STREQ(pcmd->argval[0].string, "no")) {
2631		debug = 0;
2632	} else if (STREQ(pcmd->argval[0].string, "more")) {
2633		debug++;
2634	} else if (STREQ(pcmd->argval[0].string, "less")) {
2635		debug--;
2636	} else {
2637		(void) fprintf(fp, "What?\n");
2638		return;
2639	}
2640	(void) fprintf(fp, "debug level set to %d\n", debug);
2641}
2642
2643
2644/*
2645 * quit - stop this nonsense
2646 */
2647/*ARGSUSED*/
2648static void
2649quit(
2650	struct parse *pcmd,
2651	FILE *fp
2652	)
2653{
2654	if (havehost)
2655	    closesocket(sockfd);	/* cleanliness next to godliness */
2656	exit(0);
2657}
2658
2659
2660/*
2661 * version - print the current version number
2662 */
2663/*ARGSUSED*/
2664static void
2665version(
2666	struct parse *pcmd,
2667	FILE *fp
2668	)
2669{
2670
2671	(void) fprintf(fp, "%s\n", Version);
2672	return;
2673}
2674
2675
2676/*
2677 * raw - set raw mode output
2678 */
2679/*ARGSUSED*/
2680static void
2681raw(
2682	struct parse *pcmd,
2683	FILE *fp
2684	)
2685{
2686	rawmode = 1;
2687	(void) fprintf(fp, "Output set to raw\n");
2688}
2689
2690
2691/*
2692 * cooked - set cooked mode output
2693 */
2694/*ARGSUSED*/
2695static void
2696cooked(
2697	struct parse *pcmd,
2698	FILE *fp
2699	)
2700{
2701	rawmode = 0;
2702	(void) fprintf(fp, "Output set to cooked\n");
2703	return;
2704}
2705
2706
2707/*
2708 * authenticate - always authenticate requests to this host
2709 */
2710static void
2711authenticate(
2712	struct parse *pcmd,
2713	FILE *fp
2714	)
2715{
2716	if (pcmd->nargs == 0) {
2717		if (always_auth) {
2718			(void) fprintf(fp,
2719				       "authenticated requests being sent\n");
2720		} else
2721		    (void) fprintf(fp,
2722				   "unauthenticated requests being sent\n");
2723	} else {
2724		if (STREQ(pcmd->argval[0].string, "yes")) {
2725			always_auth = 1;
2726		} else if (STREQ(pcmd->argval[0].string, "no")) {
2727			always_auth = 0;
2728		} else
2729		    (void)fprintf(stderr, "What?\n");
2730	}
2731}
2732
2733
2734/*
2735 * ntpversion - choose the NTP version to use
2736 */
2737static void
2738ntpversion(
2739	struct parse *pcmd,
2740	FILE *fp
2741	)
2742{
2743	if (pcmd->nargs == 0) {
2744		(void) fprintf(fp,
2745			       "NTP version being claimed is %d\n", pktversion);
2746	} else {
2747		if (pcmd->argval[0].uval < NTP_OLDVERSION
2748		    || pcmd->argval[0].uval > NTP_VERSION) {
2749			(void) fprintf(stderr, "versions %d to %d, please\n",
2750				       NTP_OLDVERSION, NTP_VERSION);
2751		} else {
2752			pktversion = (u_char) pcmd->argval[0].uval;
2753		}
2754	}
2755}
2756
2757
2758static void __attribute__((__format__(__printf__, 1, 0)))
2759vwarning(const char *fmt, va_list ap)
2760{
2761	int serrno = errno;
2762	(void) fprintf(stderr, "%s: ", progname);
2763	vfprintf(stderr, fmt, ap);
2764	(void) fprintf(stderr, ": %s\n", strerror(serrno));
2765}
2766
2767/*
2768 * warning - print a warning message
2769 */
2770static void __attribute__((__format__(__printf__, 1, 2)))
2771warning(
2772	const char *fmt,
2773	...
2774	)
2775{
2776	va_list ap;
2777	va_start(ap, fmt);
2778	vwarning(fmt, ap);
2779	va_end(ap);
2780}
2781
2782
2783/*
2784 * error - print a message and exit
2785 */
2786static void __attribute__((__format__(__printf__, 1, 2)))
2787error(
2788	const char *fmt,
2789	...
2790	)
2791{
2792	va_list ap;
2793	va_start(ap, fmt);
2794	vwarning(fmt, ap);
2795	va_end(ap);
2796	exit(1);
2797}
2798/*
2799 * getkeyid - prompt the user for a keyid to use
2800 */
2801static u_long
2802getkeyid(
2803	const char *keyprompt
2804	)
2805{
2806	int c;
2807	FILE *fi;
2808	char pbuf[20];
2809	size_t i;
2810	size_t ilim;
2811
2812#ifndef SYS_WINNT
2813	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
2814#else
2815	if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
2816#endif /* SYS_WINNT */
2817		fi = stdin;
2818	else
2819		setbuf(fi, (char *)NULL);
2820	fprintf(stderr, "%s", keyprompt); fflush(stderr);
2821	for (i = 0, ilim = COUNTOF(pbuf) - 1;
2822	     i < ilim && (c = getc(fi)) != '\n' && c != EOF;
2823	     )
2824		pbuf[i++] = (char)c;
2825	pbuf[i] = '\0';
2826	if (fi != stdin)
2827		fclose(fi);
2828
2829	return (u_long) atoi(pbuf);
2830}
2831
2832
2833/*
2834 * atoascii - printable-ize possibly ascii data using the character
2835 *	      transformations cat -v uses.
2836 */
2837static void
2838atoascii(
2839	const char *in,
2840	size_t in_octets,
2841	char *out,
2842	size_t out_octets
2843	)
2844{
2845	const u_char *	pchIn;
2846	const u_char *	pchInLimit;
2847	u_char *	pchOut;
2848	u_char		c;
2849
2850	pchIn = (const u_char *)in;
2851	pchInLimit = pchIn + in_octets;
2852	pchOut = (u_char *)out;
2853
2854	if (NULL == pchIn) {
2855		if (0 < out_octets)
2856			*pchOut = '\0';
2857		return;
2858	}
2859
2860#define	ONEOUT(c)					\
2861do {							\
2862	if (0 == --out_octets) {			\
2863		*pchOut = '\0';				\
2864		return;					\
2865	}						\
2866	*pchOut++ = (c);				\
2867} while (0)
2868
2869	for (	; pchIn < pchInLimit; pchIn++) {
2870		c = *pchIn;
2871		if ('\0' == c)
2872			break;
2873		if (c & 0x80) {
2874			ONEOUT('M');
2875			ONEOUT('-');
2876			c &= 0x7f;
2877		}
2878		if (c < ' ') {
2879			ONEOUT('^');
2880			ONEOUT((u_char)(c + '@'));
2881		} else if (0x7f == c) {
2882			ONEOUT('^');
2883			ONEOUT('?');
2884		} else
2885			ONEOUT(c);
2886	}
2887	ONEOUT('\0');
2888
2889#undef ONEOUT
2890}
2891
2892
2893/*
2894 * makeascii - print possibly ascii data using the character
2895 *	       transformations that cat -v uses.
2896 */
2897void
2898makeascii(
2899	size_t length,
2900	const char *data,
2901	FILE *fp
2902	)
2903{
2904	const u_char *data_u_char;
2905	const u_char *cp;
2906	int c;
2907
2908	data_u_char = (const u_char *)data;
2909
2910	for (cp = data_u_char; cp < data_u_char + length; cp++) {
2911		c = (int)*cp;
2912		if (c & 0x80) {
2913			putc('M', fp);
2914			putc('-', fp);
2915			c &= 0x7f;
2916		}
2917
2918		if (c < ' ') {
2919			putc('^', fp);
2920			putc(c + '@', fp);
2921		} else if (0x7f == c) {
2922			putc('^', fp);
2923			putc('?', fp);
2924		} else
2925			putc(c, fp);
2926	}
2927}
2928
2929
2930/*
2931 * asciize - same thing as makeascii except add a newline
2932 */
2933void
2934asciize(
2935	int length,
2936	char *data,
2937	FILE *fp
2938	)
2939{
2940	makeascii(length, data, fp);
2941	putc('\n', fp);
2942}
2943
2944
2945/*
2946 * truncate string to fit clipping excess at end.
2947 *	"too long"	->	"too l"
2948 * Used for hostnames.
2949 */
2950const char *
2951trunc_right(
2952	const char *	src,
2953	size_t		width
2954	)
2955{
2956	size_t	sl;
2957	char *	out;
2958
2959
2960	sl = strlen(src);
2961	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
2962		LIB_GETBUF(out);
2963		memcpy(out, src, width);
2964		out[width] = '\0';
2965
2966		return out;
2967	}
2968
2969	return src;
2970}
2971
2972
2973/*
2974 * truncate string to fit by preserving right side and using '_' to hint
2975 *	"too long"	->	"_long"
2976 * Used for local IPv6 addresses, where low bits differentiate.
2977 */
2978const char *
2979trunc_left(
2980	const char *	src,
2981	size_t		width
2982	)
2983{
2984	size_t	sl;
2985	char *	out;
2986
2987
2988	sl = strlen(src);
2989	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
2990		LIB_GETBUF(out);
2991		out[0] = '_';
2992		memcpy(&out[1], &src[sl + 1 - width], width);
2993
2994		return out;
2995	}
2996
2997	return src;
2998}
2999
3000
3001/*
3002 * Some circular buffer space
3003 */
3004#define	CBLEN	80
3005#define	NUMCB	6
3006
3007char circ_buf[NUMCB][CBLEN];
3008int nextcb = 0;
3009
3010/*
3011 * nextvar - find the next variable in the buffer
3012 */
3013int
3014nextvar(
3015	size_t *datalen,
3016	const char **datap,
3017	char **vname,
3018	char **vvalue
3019	)
3020{
3021	const char *cp;
3022	const char *np;
3023	const char *cpend;
3024	size_t srclen;
3025	size_t len;
3026	static char name[MAXVARLEN];
3027	static char value[MAXVALLEN];
3028
3029	cp = *datap;
3030	cpend = cp + *datalen;
3031
3032	/*
3033	 * Space past commas and white space
3034	 */
3035	while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
3036		cp++;
3037	if (cp >= cpend)
3038		return 0;
3039
3040	/*
3041	 * Copy name until we hit a ',', an '=', a '\r' or a '\n'.  Backspace
3042	 * over any white space and terminate it.
3043	 */
3044	srclen = strcspn(cp, ",=\r\n");
3045	srclen = min(srclen, (size_t)(cpend - cp));
3046	len = srclen;
3047	while (len > 0 && isspace((unsigned char)cp[len - 1]))
3048		len--;
3049	if (len >= sizeof(name))
3050	    return 0;
3051	if (len > 0)
3052		memcpy(name, cp, len);
3053	name[len] = '\0';
3054	*vname = name;
3055	cp += srclen;
3056
3057	/*
3058	 * Check if we hit the end of the buffer or a ','.  If so we are done.
3059	 */
3060	if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
3061		if (cp < cpend)
3062			cp++;
3063		*datap = cp;
3064		*datalen = size2int_sat(cpend - cp);
3065		*vvalue = NULL;
3066		return 1;
3067	}
3068
3069	/*
3070	 * So far, so good.  Copy out the value
3071	 */
3072	cp++;	/* past '=' */
3073	while (cp < cpend && (isspace((unsigned char)*cp) && *cp != '\r' && *cp != '\n'))
3074		cp++;
3075	np = cp;
3076	if ('"' == *np) {
3077		do {
3078			np++;
3079		} while (np < cpend && '"' != *np);
3080		if (np < cpend && '"' == *np)
3081			np++;
3082	} else {
3083		while (np < cpend && ',' != *np && '\r' != *np)
3084			np++;
3085	}
3086	len = np - cp;
3087	if (np > cpend || len >= sizeof(value) ||
3088	    (np < cpend && ',' != *np && '\r' != *np))
3089		return 0;
3090	memcpy(value, cp, len);
3091	/*
3092	 * Trim off any trailing whitespace
3093	 */
3094	while (len > 0 && isspace((unsigned char)value[len - 1]))
3095		len--;
3096	value[len] = '\0';
3097
3098	/*
3099	 * Return this.  All done.
3100	 */
3101	if (np < cpend && ',' == *np)
3102		np++;
3103	*datap = np;
3104	*datalen = size2int_sat(cpend - np);
3105	*vvalue = value;
3106	return 1;
3107}
3108
3109
3110u_short
3111varfmt(const char * varname)
3112{
3113	u_int n;
3114
3115	for (n = 0; n < COUNTOF(cookedvars); n++)
3116		if (!strcmp(varname, cookedvars[n].varname))
3117			return cookedvars[n].fmt;
3118
3119	return PADDING;
3120}
3121
3122
3123/*
3124 * printvars - print variables returned in response packet
3125 */
3126void
3127printvars(
3128	size_t length,
3129	const char *data,
3130	int status,
3131	int sttype,
3132	int quiet,
3133	FILE *fp
3134	)
3135{
3136	if (rawmode)
3137	    rawprint(sttype, length, data, status, quiet, fp);
3138	else
3139	    cookedprint(sttype, length, data, status, quiet, fp);
3140}
3141
3142
3143/*
3144 * rawprint - do a printout of the data in raw mode
3145 */
3146static void
3147rawprint(
3148	int datatype,
3149	size_t length,
3150	const char *data,
3151	int status,
3152	int quiet,
3153	FILE *fp
3154	)
3155{
3156	const char *cp;
3157	const char *cpend;
3158
3159	/*
3160	 * Essentially print the data as is.  We reformat unprintables, though.
3161	 */
3162	cp = data;
3163	cpend = data + length;
3164
3165	if (!quiet)
3166		(void) fprintf(fp, "status=0x%04x,\n", status);
3167
3168	while (cp < cpend) {
3169		if (*cp == '\r') {
3170			/*
3171			 * If this is a \r and the next character is a
3172			 * \n, supress this, else pretty print it.  Otherwise
3173			 * just output the character.
3174			 */
3175			if (cp == (cpend - 1) || *(cp + 1) != '\n')
3176			    makeascii(1, cp, fp);
3177		} else if (isspace((unsigned char)*cp) || isprint((unsigned char)*cp))
3178			putc(*cp, fp);
3179		else
3180			makeascii(1, cp, fp);
3181		cp++;
3182	}
3183}
3184
3185
3186/*
3187 * Global data used by the cooked output routines
3188 */
3189int out_chars;		/* number of characters output */
3190int out_linecount;	/* number of characters output on this line */
3191
3192
3193/*
3194 * startoutput - get ready to do cooked output
3195 */
3196static void
3197startoutput(void)
3198{
3199	out_chars = 0;
3200	out_linecount = 0;
3201}
3202
3203
3204/*
3205 * output - output a variable=value combination
3206 */
3207static void
3208output(
3209	FILE *fp,
3210	const char *name,
3211	const char *value
3212	)
3213{
3214	int len;
3215
3216	/* strlen of "name=value" */
3217	len = size2int_sat(strlen(name) + 1 + strlen(value));
3218
3219	if (out_chars != 0) {
3220		out_chars += 2;
3221		if ((out_linecount + len + 2) > MAXOUTLINE) {
3222			fputs(",\n", fp);
3223			out_linecount = 0;
3224		} else {
3225			fputs(", ", fp);
3226			out_linecount += 2;
3227		}
3228	}
3229
3230	fputs(name, fp);
3231	putc('=', fp);
3232	fputs(value, fp);
3233	out_chars += len;
3234	out_linecount += len;
3235}
3236
3237
3238/*
3239 * endoutput - terminate a block of cooked output
3240 */
3241static void
3242endoutput(
3243	FILE *fp
3244	)
3245{
3246	if (out_chars != 0)
3247		putc('\n', fp);
3248}
3249
3250
3251/*
3252 * outputarr - output an array of values
3253 */
3254static void
3255outputarr(
3256	FILE *fp,
3257	char *name,
3258	int narr,
3259	l_fp *lfp
3260	)
3261{
3262	char *bp;
3263	char *cp;
3264	size_t i;
3265	size_t len;
3266	char buf[256];
3267
3268	bp = buf;
3269	/*
3270	 * Hack to align delay and offset values
3271	 */
3272	for (i = (int)strlen(name); i < 11; i++)
3273	    *bp++ = ' ';
3274
3275	for (i = narr; i > 0; i--) {
3276		if (i != narr)
3277		    *bp++ = ' ';
3278		cp = lfptoms(lfp, 2);
3279		len = strlen(cp);
3280		if (len > 7) {
3281			cp[7] = '\0';
3282			len = 7;
3283		}
3284		while (len < 7) {
3285			*bp++ = ' ';
3286			len++;
3287		}
3288		while (*cp != '\0')
3289		    *bp++ = *cp++;
3290		lfp++;
3291	}
3292	*bp = '\0';
3293	output(fp, name, buf);
3294}
3295
3296static char *
3297tstflags(
3298	u_long val
3299	)
3300{
3301	register char *cp, *s;
3302	size_t cb;
3303	register int i;
3304	register const char *sep;
3305
3306	sep = "";
3307	s = cp = circ_buf[nextcb];
3308	if (++nextcb >= NUMCB)
3309		nextcb = 0;
3310	cb = sizeof(circ_buf[0]);
3311
3312	snprintf(cp, cb, "%02lx", val);
3313	cp += strlen(cp);
3314	cb -= strlen(cp);
3315	if (!val) {
3316		strlcat(cp, " ok", cb);
3317		cp += strlen(cp);
3318		cb -= strlen(cp);
3319	} else {
3320		if (cb) {
3321			*cp++ = ' ';
3322			cb--;
3323		}
3324		for (i = 0; i < (int)COUNTOF(tstflagnames); i++) {
3325			if (val & 0x1) {
3326				snprintf(cp, cb, "%s%s", sep,
3327					 tstflagnames[i]);
3328				sep = ", ";
3329				cp += strlen(cp);
3330				cb -= strlen(cp);
3331			}
3332			val >>= 1;
3333		}
3334	}
3335	if (cb)
3336		*cp = '\0';
3337
3338	return s;
3339}
3340
3341/*
3342 * cookedprint - output variables in cooked mode
3343 */
3344static void
3345cookedprint(
3346	int datatype,
3347	size_t length,
3348	const char *data,
3349	int status,
3350	int quiet,
3351	FILE *fp
3352	)
3353{
3354	char *name;
3355	char *value;
3356	char output_raw;
3357	int fmt;
3358	l_fp lfp;
3359	sockaddr_u hval;
3360	u_long uval;
3361	int narr;
3362	size_t len;
3363	l_fp lfparr[8];
3364	char b[12];
3365	char bn[2 * MAXVARLEN];
3366	char bv[2 * MAXVALLEN];
3367
3368	UNUSED_ARG(datatype);
3369
3370	if (!quiet)
3371		fprintf(fp, "status=%04x %s,\n", status,
3372			statustoa(datatype, status));
3373
3374	startoutput();
3375	while (nextvar(&length, &data, &name, &value)) {
3376		fmt = varfmt(name);
3377		output_raw = 0;
3378		switch (fmt) {
3379
3380		case PADDING:
3381			output_raw = '*';
3382			break;
3383
3384		case TS:
3385			if (!decodets(value, &lfp))
3386				output_raw = '?';
3387			else
3388				output(fp, name, prettydate(&lfp));
3389			break;
3390
3391		case HA:	/* fallthru */
3392		case NA:
3393			if (!decodenetnum(value, &hval)) {
3394				output_raw = '?';
3395			} else if (fmt == HA){
3396				output(fp, name, nntohost(&hval));
3397			} else {
3398				output(fp, name, stoa(&hval));
3399			}
3400			break;
3401
3402		case RF:
3403			if (decodenetnum(value, &hval)) {
3404				if (ISREFCLOCKADR(&hval))
3405					output(fp, name,
3406					       refnumtoa(&hval));
3407				else
3408					output(fp, name, stoa(&hval));
3409			} else if (strlen(value) <= 4) {
3410				output(fp, name, value);
3411			} else {
3412				output_raw = '?';
3413			}
3414			break;
3415
3416		case LP:
3417			if (!decodeuint(value, &uval) || uval > 3) {
3418				output_raw = '?';
3419			} else {
3420				b[0] = (0x2 & uval)
3421					   ? '1'
3422					   : '0';
3423				b[1] = (0x1 & uval)
3424					   ? '1'
3425					   : '0';
3426				b[2] = '\0';
3427				output(fp, name, b);
3428			}
3429			break;
3430
3431		case OC:
3432			if (!decodeuint(value, &uval)) {
3433				output_raw = '?';
3434			} else {
3435				snprintf(b, sizeof(b), "%03lo", uval);
3436				output(fp, name, b);
3437			}
3438			break;
3439
3440		case AR:
3441			if (!decodearr(value, &narr, lfparr))
3442				output_raw = '?';
3443			else
3444				outputarr(fp, name, narr, lfparr);
3445			break;
3446
3447		case FX:
3448			if (!decodeuint(value, &uval))
3449				output_raw = '?';
3450			else
3451				output(fp, name, tstflags(uval));
3452			break;
3453
3454		default:
3455			fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
3456				name, value, fmt);
3457			output_raw = '?';
3458			break;
3459		}
3460
3461		if (output_raw != 0) {
3462			/* TALOS-CAN-0063: avoid buffer overrun */
3463			atoascii(name, MAXVARLEN, bn, sizeof(bn));
3464			if (output_raw != '*') {
3465				atoascii(value, MAXVALLEN,
3466					 bv, sizeof(bv) - 1);
3467				len = strlen(bv);
3468				bv[len] = output_raw;
3469				bv[len+1] = '\0';
3470			} else {
3471				atoascii(value, MAXVALLEN,
3472					 bv, sizeof(bv));
3473			}
3474			output(fp, bn, bv);
3475		}
3476	}
3477	endoutput(fp);
3478}
3479
3480
3481/*
3482 * sortassoc - sort associations in the cache into ascending order
3483 */
3484void
3485sortassoc(void)
3486{
3487	if (numassoc > 1)
3488		qsort(assoc_cache, (size_t)numassoc,
3489		      sizeof(assoc_cache[0]), &assoccmp);
3490}
3491
3492
3493/*
3494 * assoccmp - compare two associations
3495 */
3496static int
3497assoccmp(
3498	const void *t1,
3499	const void *t2
3500	)
3501{
3502	const struct association *ass1 = t1;
3503	const struct association *ass2 = t2;
3504
3505	if (ass1->assid < ass2->assid)
3506		return -1;
3507	if (ass1->assid > ass2->assid)
3508		return 1;
3509	return 0;
3510}
3511
3512
3513/*
3514 * grow_assoc_cache() - enlarge dynamic assoc_cache array
3515 *
3516 * The strategy is to add an assumed 4k page size at a time, leaving
3517 * room for malloc() bookkeeping overhead equivalent to 4 pointers.
3518 */
3519void
3520grow_assoc_cache(void)
3521{
3522	static size_t	prior_sz;
3523	size_t		new_sz;
3524
3525	new_sz = prior_sz + 4 * 1024;
3526	if (0 == prior_sz) {
3527		new_sz -= 4 * sizeof(void *);
3528	}
3529	assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz);
3530	prior_sz = new_sz;
3531	assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0]));
3532}
3533
3534
3535/*
3536 * ntpq_custom_opt_handler - autoopts handler for -c and -p
3537 *
3538 * By default, autoopts loses the relative order of -c and -p options
3539 * on the command line.  This routine replaces the default handler for
3540 * those routines and builds a list of commands to execute preserving
3541 * the order.
3542 */
3543void
3544ntpq_custom_opt_handler(
3545	tOptions *pOptions,
3546	tOptDesc *pOptDesc
3547	)
3548{
3549	switch (pOptDesc->optValue) {
3550
3551	default:
3552		fprintf(stderr,
3553			"ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3554			pOptDesc->optValue, pOptDesc->optValue);
3555		exit(1);
3556
3557	case 'c':
3558		ADDCMD(pOptDesc->pzLastArg);
3559		break;
3560
3561	case 'p':
3562		ADDCMD("peers");
3563		break;
3564	}
3565}
3566/*
3567 * Obtain list of digest names
3568 */
3569
3570#ifdef OPENSSL
3571# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3572struct hstate {
3573   char *list;
3574   const char **seen;
3575   int idx;
3576};
3577#define K_PER_LINE 8
3578#define K_NL_PFX_STR "\n    "
3579#define K_DELIM_STR ", "
3580static void list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg )
3581{
3582    size_t len, n;
3583    const char *name, *cp, **seen;
3584    struct hstate *hstate = arg;
3585    EVP_MD_CTX ctx;
3586    u_int digest_len;
3587    u_char digest[EVP_MAX_MD_SIZE];
3588
3589    if (!m)
3590        return; /* Ignore aliases */
3591
3592    name = EVP_MD_name(m);
3593
3594    /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */
3595
3596    for( cp = name; *cp; cp++ ) {
3597	if( islower(*cp) )
3598	    return;
3599    }
3600    len = (cp - name) + 1;
3601
3602    /* There are duplicates.  Discard if name has been seen. */
3603
3604    for (seen = hstate->seen; *seen; seen++)
3605        if (!strcmp(*seen, name))
3606	    return;
3607    n = (seen - hstate->seen) + 2;
3608    hstate->seen = erealloc(hstate->seen, n * sizeof(*seen));
3609    hstate->seen[n-2] = name;
3610    hstate->seen[n-1] = NULL;
3611
3612    /* Discard MACs that NTP won't accept.
3613     * Keep this consistent with keytype_from_text() in ssl_init.c.
3614     */
3615
3616    EVP_DigestInit(&ctx, EVP_get_digestbyname(name));
3617    EVP_DigestFinal(&ctx, digest, &digest_len);
3618    if (digest_len > (MAX_MAC_LEN - sizeof(keyid_t)))
3619        return;
3620
3621    if (hstate->list != NULL)
3622	len += strlen(hstate->list);
3623    len += (hstate->idx >= K_PER_LINE)? strlen(K_NL_PFX_STR): strlen(K_DELIM_STR);
3624
3625    if (hstate->list == NULL) {
3626	hstate->list = (char *)emalloc(len);
3627	hstate->list[0] = '\0';
3628    } else
3629	hstate->list = (char *)erealloc(hstate->list, len);
3630
3631    sprintf(hstate->list + strlen(hstate->list), "%s%s",
3632	    ((hstate->idx >= K_PER_LINE)? K_NL_PFX_STR : K_DELIM_STR),
3633	    name);
3634    if (hstate->idx >= K_PER_LINE)
3635	hstate->idx = 1;
3636    else
3637	hstate->idx++;
3638}
3639# endif
3640#endif
3641
3642static char *list_digest_names(void)
3643{
3644    char *list = NULL;
3645
3646#ifdef OPENSSL
3647# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3648    struct hstate hstate = { NULL, NULL, K_PER_LINE+1 };
3649
3650    hstate.seen = (const char **) emalloc_zero(1*sizeof( const char * )); // replaces -> calloc(1, sizeof( const char * ));
3651
3652    INIT_SSL();
3653    EVP_MD_do_all_sorted(list_md_fn, &hstate);
3654    list = hstate.list;
3655    free(hstate.seen);
3656# else
3657    list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)"));
3658    strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)");
3659# endif
3660#else
3661    list = (char *)emalloc(sizeof("md5"));
3662    strcpy(list, "md5");
3663#endif
3664
3665    return list;
3666}
3667
3668#define CTRLC_STACK_MAX 4
3669static volatile size_t		ctrlc_stack_len = 0;
3670static volatile Ctrl_C_Handler	ctrlc_stack[CTRLC_STACK_MAX];
3671
3672
3673
3674int/*BOOL*/
3675push_ctrl_c_handler(
3676	Ctrl_C_Handler func
3677	)
3678{
3679	size_t size = ctrlc_stack_len;
3680	if (func && (size < CTRLC_STACK_MAX)) {
3681		ctrlc_stack[size] = func;
3682		ctrlc_stack_len = size + 1;
3683		return TRUE;
3684	}
3685	return FALSE;
3686}
3687
3688int/*BOOL*/
3689pop_ctrl_c_handler(
3690	Ctrl_C_Handler func
3691	)
3692{
3693	size_t size = ctrlc_stack_len;
3694	if (size) {
3695		--size;
3696		if (func == NULL || func == ctrlc_stack[size]) {
3697			ctrlc_stack_len = size;
3698			return TRUE;
3699		}
3700	}
3701	return FALSE;
3702}
3703
3704static void
3705on_ctrlc(void)
3706{
3707	size_t size = ctrlc_stack_len;
3708	while (size)
3709		if ((*ctrlc_stack[--size])())
3710			break;
3711}
3712
3713static int
3714my_easprintf(
3715	char ** 	ppinto,
3716	const char *	fmt   ,
3717	...
3718	)
3719{
3720	va_list	va;
3721	int	prc;
3722	size_t	len = 128;
3723	char *	buf = emalloc(len);
3724
3725  again:
3726	/* Note: we expect the memory allocation to fail long before the
3727	 * increment in buffer size actually overflows.
3728	 */
3729	buf = (buf) ? erealloc(buf, len) : emalloc(len);
3730
3731	va_start(va, fmt);
3732	prc = vsnprintf(buf, len, fmt, va);
3733	va_end(va);
3734
3735	if (prc < 0) {
3736		/* might be very old vsnprintf. Or actually MSVC... */
3737		len += len >> 1;
3738		goto again;
3739	}
3740	if ((size_t)prc >= len) {
3741		/* at least we have the proper size now... */
3742		len = (size_t)prc + 1;
3743		goto again;
3744	}
3745	if ((size_t)prc < (len - 32))
3746		buf = erealloc(buf, (size_t)prc + 1);
3747	*ppinto = buf;
3748	return prc;
3749}
3750