ntpq.c revision 294904
1178354Ssam/*
2178354Ssam * ntpq - query an NTP server using mode 6 commands
3178354Ssam */
4178354Ssam#include <config.h>
5178354Ssam#include <stdio.h>
6178354Ssam#include <ctype.h>
7178354Ssam#include <signal.h>
8178354Ssam#include <setjmp.h>
9178354Ssam#include <sys/types.h>
10178354Ssam#include <sys/time.h>
11178354Ssam#ifdef HAVE_UNISTD_H
12178354Ssam# include <unistd.h>
13178354Ssam#endif
14178354Ssam#ifdef HAVE_FCNTL_H
15178354Ssam# include <fcntl.h>
16178354Ssam#endif
17178354Ssam#ifdef SYS_WINNT
18178354Ssam# include <mswsock.h>
19178354Ssam#endif
20178354Ssam#include <isc/net.h>
21178354Ssam#include <isc/result.h>
22178354Ssam
23178354Ssam#include "ntpq.h"
24178354Ssam#include "ntp_assert.h"
25178354Ssam#include "ntp_stdlib.h"
26178354Ssam#include "ntp_unixtime.h"
27178354Ssam#include "ntp_calendar.h"
28178354Ssam#include "ntp_select.h"
29178354Ssam#include "ntp_assert.h"
30178354Ssam#include "lib_strbuf.h"
31178354Ssam#include "ntp_lineedit.h"
32178354Ssam#include "ntp_debug.h"
33178354Ssam#ifdef OPENSSL
34178354Ssam#include "openssl/evp.h"
35178354Ssam#include "openssl/objects.h"
36178354Ssam#include "openssl/err.h"
37178354Ssam#endif
38178354Ssam#include <ssl_applink.c>
39178354Ssam
40178354Ssam#include "ntp_libopts.h"
41178354Ssam#include "ntpq-opts.h"
42178354Ssam#include "safecast.h"
43178354Ssam
44178354Ssam#ifdef SYS_VXWORKS		/* vxWorks needs mode flag -casey*/
45178354Ssam# define open(name, flags)   open(name, flags, 0777)
46178354Ssam# define SERVER_PORT_NUM     123
47178354Ssam#endif
48178354Ssam
49178354Ssam/* we use COMMAND as an autogen keyword */
50178354Ssam#ifdef COMMAND
51178354Ssam# undef COMMAND
52178354Ssam#endif
53178354Ssam
54178354Ssam/*
55178354Ssam * Because we potentially understand a lot of commands we will run
56178354Ssam * interactive if connected to a terminal.
57178354Ssam */
58178354Ssamint interactive = 0;		/* set to 1 when we should prompt */
59178354Ssamconst char *prompt = "ntpq> ";	/* prompt to ask him about */
60190391Ssam
61190391Ssam/*
62190391Ssam * use old readvars behavior?  --old-rv processing in ntpq resets
63178354Ssam * this value based on the presence or absence of --old-rv.  It is
64178354Ssam * initialized to 1 here to maintain backward compatibility with
65178354Ssam * libntpq clients such as ntpsnmpd, which are free to reset it as
66178354Ssam * desired.
67178354Ssam */
68178354Ssamint	old_rv = 1;
69178354Ssam
70192468Ssam
71178354Ssam/*
72178354Ssam * for get_systime()
73178354Ssam */
74192468Ssams_char	sys_precision;		/* local clock precision (log2 s) */
75191546Ssam
76178354Ssam/*
77178354Ssam * Keyid used for authenticated requests.  Obtained on the fly.
78178354Ssam */
79178354Ssamu_long info_auth_keyid = 0;
80178354Ssam
81178354Ssamstatic	int	info_auth_keytype = NID_md5;	/* MD5 */
82178354Ssamstatic	size_t	info_auth_hashlen = 16;		/* MD5 */
83178354Ssamu_long	current_time;		/* needed by authkeys; not used */
84178354Ssam
85178354Ssam/*
86178354Ssam * Flag which indicates we should always send authenticated requests
87178354Ssam */
88178354Ssamint always_auth = 0;
89178354Ssam
90178354Ssam/*
91178354Ssam * Flag which indicates raw mode output.
92178354Ssam */
93178354Ssamint rawmode = 0;
94178354Ssam
95178354Ssam/*
96178354Ssam * Packet version number we use
97178354Ssam */
98178354Ssamu_char pktversion = NTP_OLDVERSION + 1;
99191546Ssam
100178354Ssam/*
101178354Ssam * Don't jump if no set jmp.
102241138Sadrian */
103178354Ssamvolatile int jump = 0;
104178354Ssam
105178354Ssam/*
106178354Ssam * Format values
107178354Ssam */
108178354Ssam#define	PADDING	0
109178354Ssam#define	HA	1	/* host address */
110178354Ssam#define	NA	2	/* network address */
111178354Ssam#define	LP	3	/* leap (print in binary) */
112178354Ssam#define	RF	4	/* refid (sometimes string, sometimes not) */
113178354Ssam#define	AR	5	/* array of times */
114178354Ssam#define FX	6	/* test flags */
115178354Ssam#define TS	7	/* l_fp timestamp in hex */
116178354Ssam#define	OC	8	/* integer, print in octal */
117193413Ssam#define	EOV	255	/* end of table */
118193413Ssam
119193413Ssam/*
120193413Ssam * For the most part ntpq simply displays what ntpd provides in the
121193413Ssam * mostly plain-text mode 6 responses.  A few variable names are by
122193413Ssam * default "cooked" to provide more human-friendly output.
123193413Ssam */
124193413Ssamconst var_format cookedvars[] = {
125193413Ssam	{ "leap",		LP },
126193413Ssam	{ "reach",		OC },
127193413Ssam	{ "refid",		RF },
128193413Ssam	{ "reftime",		TS },
129193413Ssam	{ "clock",		TS },
130193414Ssam	{ "org",		TS },
131193414Ssam	{ "rec",		TS },
132193414Ssam	{ "xmt",		TS },
133193414Ssam	{ "flash",		FX },
134193414Ssam	{ "srcadr",		HA },
135193414Ssam	{ "peeradr",		HA },	/* compat with others */
136193414Ssam	{ "dstadr",		NA },
137193414Ssam	{ "filtdelay",		AR },
138193414Ssam	{ "filtoffset",		AR },
139178354Ssam	{ "filtdisp",		AR },
140193414Ssam	{ "filterror",		AR },	/* compat with others */
141193414Ssam};
142193414Ssam
143193414Ssam
144193414Ssam
145193414Ssam/*
146193414Ssam * flasher bits
147193414Ssam */
148193414Ssamstatic const char *tstflagnames[] = {
149193414Ssam	"pkt_dup",		/* TEST1 */
150193414Ssam	"pkt_bogus",		/* TEST2 */
151193414Ssam	"pkt_unsync",		/* TEST3 */
152193414Ssam	"pkt_denied",		/* TEST4 */
153178354Ssam	"pkt_auth",		/* TEST5 */
154178354Ssam	"pkt_stratum",		/* TEST6 */
155178354Ssam	"pkt_header",		/* TEST7 */
156178354Ssam	"pkt_autokey",		/* TEST8 */
157178354Ssam	"pkt_crypto",		/* TEST9 */
158178354Ssam	"peer_stratum",		/* TEST10 */
159178354Ssam	"peer_dist",		/* TEST11 */
160178354Ssam	"peer_loop",		/* TEST12 */
161178354Ssam	"peer_unreach"		/* TEST13 */
162178354Ssam};
163178354Ssam
164178354Ssam
165178354Ssamint		ntpqmain	(int,	char **);
166178354Ssam/*
167178354Ssam * Built in command handler declarations
168178354Ssam */
169178354Ssamstatic	int	openhost	(const char *, int);
170178354Ssamstatic	void	dump_hex_printable(const void *, size_t);
171178354Ssamstatic	int	sendpkt		(void *, size_t);
172178354Ssamstatic	int	getresponse	(int, int, u_short *, size_t *, const char **, int);
173178354Ssamstatic	int	sendrequest	(int, associd_t, int, size_t, const char *);
174178354Ssamstatic	char *	tstflags	(u_long);
175178354Ssam#ifndef BUILD_AS_LIB
176178354Ssamstatic	void	getcmds		(void);
177178354Ssam#ifndef SYS_WINNT
178178354Ssamstatic	int	abortcmd	(void);
179178354Ssam#endif	/* SYS_WINNT */
180178354Ssamstatic	void	docmd		(const char *);
181178354Ssamstatic	void	tokenize	(const char *, char **, int *);
182178354Ssamstatic	int	getarg		(const char *, int, arg_v *);
183178354Ssam#endif	/* BUILD_AS_LIB */
184178354Ssamstatic	int	findcmd		(const char *, struct xcmd *,
185178354Ssam				 struct xcmd *, struct xcmd **);
186178354Ssamstatic	int	rtdatetolfp	(char *, l_fp *);
187178354Ssamstatic	int	decodearr	(char *, int *, l_fp *);
188178354Ssamstatic	void	help		(struct parse *, FILE *);
189178354Ssamstatic	int	helpsort	(const void *, const void *);
190178354Ssamstatic	void	printusage	(struct xcmd *, FILE *);
191178354Ssamstatic	void	timeout		(struct parse *, FILE *);
192178354Ssamstatic	void	auth_delay	(struct parse *, FILE *);
193178354Ssamstatic	void	host		(struct parse *, FILE *);
194178354Ssamstatic	void	ntp_poll	(struct parse *, FILE *);
195178354Ssamstatic	void	keyid		(struct parse *, FILE *);
196178354Ssamstatic	void	keytype		(struct parse *, FILE *);
197178354Ssamstatic	void	passwd		(struct parse *, FILE *);
198178354Ssamstatic	void	hostnames	(struct parse *, FILE *);
199178354Ssamstatic	void	setdebug	(struct parse *, FILE *);
200178354Ssamstatic	void	quit		(struct parse *, FILE *);
201178354Ssamstatic	void	version		(struct parse *, FILE *);
202178354Ssamstatic	void	raw		(struct parse *, FILE *);
203178354Ssamstatic	void	cooked		(struct parse *, FILE *);
204193655Ssamstatic	void	authenticate	(struct parse *, FILE *);
205178354Ssamstatic	void	ntpversion	(struct parse *, FILE *);
206178354Ssamstatic	void	warning		(const char *, ...)
207178354Ssam    __attribute__((__format__(__printf__, 1, 2)));
208178354Ssamstatic	void	error		(const char *, ...)
209178354Ssam    __attribute__((__format__(__printf__, 1, 2)));
210178354Ssamstatic	u_long	getkeyid	(const char *);
211178354Ssamstatic	void	atoascii	(const char *, size_t, char *, size_t);
212178354Ssamstatic	void	cookedprint	(int, size_t, const char *, int, int, FILE *);
213178354Ssamstatic	void	rawprint	(int, size_t, const char *, int, int, FILE *);
214178354Ssamstatic	void	startoutput	(void);
215178354Ssamstatic	void	output		(FILE *, const char *, const char *);
216178354Ssamstatic	void	endoutput	(FILE *);
217178354Ssamstatic	void	outputarr	(FILE *, char *, int, l_fp *);
218178354Ssamstatic	int	assoccmp	(const void *, const void *);
219178354Ssamstatic	void	on_ctrlc	(void);
220178354Ssam	u_short	varfmt		(const char *);
221178354Ssamstatic	int	my_easprintf	(char**, const char *, ...) NTP_PRINTF(2, 3);
222178354Ssamvoid	ntpq_custom_opt_handler	(tOptions *, tOptDesc *);
223178354Ssam
224178354Ssam#ifdef OPENSSL
225178354Ssam# ifdef HAVE_EVP_MD_DO_ALL_SORTED
226178354Ssamstatic void list_md_fn(const EVP_MD *m, const char *from,
227178354Ssam		       const char *to, void *arg );
228178354Ssam# endif
229178354Ssam#endif
230178354Ssamstatic char *list_digest_names(void);
231178354Ssam
232178354Ssam/*
233178354Ssam * Built-in commands we understand
234178354Ssam */
235178354Ssamstruct xcmd builtins[] = {
236178354Ssam	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
237178354Ssam	  { "command", "", "", "" },
238178354Ssam	  "tell the use and syntax of commands" },
239178354Ssam	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
240178354Ssam	  { "command", "", "", "" },
241178354Ssam	  "tell the use and syntax of commands" },
242178354Ssam	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
243178354Ssam	  { "msec", "", "", "" },
244178354Ssam	  "set the primary receive time out" },
245178354Ssam	{ "delay",	auth_delay,	{ OPT|NTP_INT, NO, NO, NO },
246178354Ssam	  { "msec", "", "", "" },
247178354Ssam	  "set the delay added to encryption time stamps" },
248178354Ssam	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
249178354Ssam	  { "-4|-6", "hostname", "", "" },
250178354Ssam	  "specify the host whose NTP server we talk to" },
251178354Ssam	{ "poll",	ntp_poll,	{ OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
252178354Ssam	  { "n", "verbose", "", "" },
253178354Ssam	  "poll an NTP server in client mode `n' times" },
254178354Ssam	{ "passwd",	passwd,		{ OPT|NTP_STR, NO, NO, NO },
255178354Ssam	  { "", "", "", "" },
256178354Ssam	  "specify a password to use for authenticated requests"},
257178354Ssam	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
258178354Ssam	  { "yes|no", "", "", "" },
259178354Ssam	  "specify whether hostnames or net numbers are printed"},
260178354Ssam	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
261178354Ssam	  { "no|more|less", "", "", "" },
262178354Ssam	  "set/change debugging level" },
263178354Ssam	{ "quit",	quit,		{ NO, NO, NO, NO },
264178354Ssam	  { "", "", "", "" },
265178354Ssam	  "exit ntpq" },
266178354Ssam	{ "exit",	quit,		{ NO, NO, NO, NO },
267178354Ssam	  { "", "", "", "" },
268178354Ssam	  "exit ntpq" },
269178354Ssam	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
270178354Ssam	  { "key#", "", "", "" },
271178354Ssam	  "set keyid to use for authenticated requests" },
272178354Ssam	{ "version",	version,	{ NO, NO, NO, NO },
273178354Ssam	  { "", "", "", "" },
274178354Ssam	  "print version number" },
275193655Ssam	{ "raw",	raw,		{ NO, NO, NO, NO },
276178354Ssam	  { "", "", "", "" },
277178354Ssam	  "do raw mode variable output" },
278178354Ssam	{ "cooked",	cooked,		{ NO, NO, NO, NO },
279178354Ssam	  { "", "", "", "" },
280178354Ssam	  "do cooked mode variable output" },
281178354Ssam	{ "authenticate", authenticate,	{ OPT|NTP_STR, NO, NO, NO },
282178354Ssam	  { "yes|no", "", "", "" },
283178354Ssam	  "always authenticate requests to this server" },
284178354Ssam	{ "ntpversion",	ntpversion,	{ OPT|NTP_UINT, NO, NO, NO },
285178354Ssam	  { "version number", "", "", "" },
286178354Ssam	  "set the NTP version number to use for requests" },
287193413Ssam	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
288193413Ssam	  { "key type %s", "", "", "" },
289193413Ssam	  NULL },
290193413Ssam	{ 0,		0,		{ NO, NO, NO, NO },
291193413Ssam	  { "", "", "", "" }, "" }
292178354Ssam};
293178354Ssam
294178354Ssam
295178354Ssam/*
296178354Ssam * Default values we use.
297178354Ssam */
298178354Ssam#define	DEFHOST		"localhost"	/* default host name */
299178354Ssam#define	DEFTIMEOUT	5		/* wait 5 seconds for 1st pkt */
300178354Ssam#define	DEFSTIMEOUT	3		/* and 3 more for each additional */
301178354Ssam/*
302178354Ssam * Requests are automatically retried once, so total timeout with no
303178354Ssam * response is a bit over 2 * DEFTIMEOUT, or 10 seconds.  At the other
304178354Ssam * extreme, a request eliciting 32 packets of responses each for some
305178354Ssam * reason nearly DEFSTIMEOUT seconds after the prior in that series,
306178354Ssam * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
307178354Ssam * 93 seconds to fail each of two times, or 186 seconds.
308178354Ssam * Some commands involve a series of requests, such as "peers" and
309178354Ssam * "mrulist", so the cumulative timeouts are even longer for those.
310178354Ssam */
311178354Ssam#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
312178354Ssam#define	LENHOSTNAME	256		/* host name is 256 characters long */
313178354Ssam#define	MAXCMDS		100		/* maximum commands on cmd line */
314178354Ssam#define	MAXHOSTS	200		/* maximum hosts on cmd line */
315178354Ssam#define	MAXLINE		512		/* maximum line length */
316178354Ssam#define	MAXTOKENS	(1+MAXARGS+2)	/* maximum number of usable tokens */
317178354Ssam#define	MAXVARLEN	256		/* maximum length of a variable name */
318178354Ssam#define	MAXVALLEN	2048		/* maximum length of a variable value */
319178354Ssam#define	MAXOUTLINE	72		/* maximum length of an output line */
320178354Ssam#define SCREENWIDTH	76		/* nominal screen width in columns */
321178354Ssam
322178354Ssam/*
323178354Ssam * Some variables used and manipulated locally
324178354Ssam */
325178354Ssamstruct sock_timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
326178354Ssamstruct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
327178354Ssaml_fp delay_time;				/* delay time */
328178354Ssamchar currenthost[LENHOSTNAME];			/* current host name */
329178354Ssamint currenthostisnum;				/* is prior text from IP? */
330178354Ssamstruct sockaddr_in hostaddr;			/* host address */
331178354Ssamint showhostnames = 1;				/* show host names by default */
332193414Ssamint wideremote = 0;				/* show wide remote names? */
333193414Ssam
334193414Ssamint ai_fam_templ;				/* address family */
335193414Ssamint ai_fam_default;				/* default address family */
336193414SsamSOCKET sockfd;					/* fd socket is opened on */
337193414Ssamint havehost = 0;				/* set to 1 when host open */
338193414Ssamint s_port = 0;
339193414Ssamstruct servent *server_entry = NULL;		/* server entry for ntp */
340193414Ssam
341193414Ssam
342193414Ssam/*
343193414Ssam * Sequence number used for requests.  It is incremented before
344178354Ssam * it is used.
345178354Ssam */
346178354Ssamu_short sequence;
347178354Ssam
348178354Ssam/*
349178354Ssam * Holds data returned from queries.  Declare buffer long to be sure of
350178354Ssam * alignment.
351178354Ssam */
352178354Ssam#define	DATASIZE	(MAXFRAGS*480)	/* maximum amount of data */
353178354Ssamlong pktdata[DATASIZE/sizeof(long)];
354178354Ssam
355178354Ssam/*
356178354Ssam * assoc_cache[] is a dynamic array which allows references to
357193292Ssam * associations using &1 ... &N for n associations, avoiding manual
358254640Sadrian * lookup of the current association IDs for a given ntpd.  It also
359254523Sandre * caches the status word for each association, retrieved incidentally.
360254523Sandre */
361254640Sadrianstruct association *	assoc_cache;
362254640Sadrianu_int assoc_cache_slots;/* count of allocated array entries */
363254640Sadrianu_int numassoc;		/* number of cached associations */
364193292Ssam
365178354Ssam/*
366178354Ssam * For commands typed on the command line (with the -c option)
367178354Ssam */
368178354Ssamint numcmds = 0;
369178354Ssamconst char *ccmds[MAXCMDS];
370178354Ssam#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
371178354Ssam
372178354Ssam/*
373178354Ssam * When multiple hosts are specified.
374178354Ssam */
375178354Ssam
376178354Ssamu_int numhosts;
377178354Ssam
378178354Ssamchost chosts[MAXHOSTS];
379178354Ssam#define	ADDHOST(cp)						\
380178354Ssam	do {							\
381178354Ssam		if (numhosts < MAXHOSTS) {			\
382178354Ssam			chosts[numhosts].name = (cp);		\
383178354Ssam			chosts[numhosts].fam = ai_fam_templ;	\
384243882Sglebius			numhosts++;				\
385178354Ssam		}						\
386178354Ssam	} while (0)
387178354Ssam
388178354Ssam/*
389178354Ssam * Macro definitions we use
390178354Ssam */
391178354Ssam#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
392178354Ssam#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
393178354Ssam#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
394178354Ssam
395178354Ssam/*
396178354Ssam * Jump buffer for longjumping back to the command level
397178354Ssam */
398178354Ssamjmp_buf interrupt_buf;
399178354Ssam
400178354Ssam/*
401178354Ssam * Points at file being currently printed into
402178354Ssam */
403178354SsamFILE *current_output;
404178354Ssam
405178354Ssam/*
406178354Ssam * Command table imported from ntpdc_ops.c
407178354Ssam */
408178354Ssamextern struct xcmd opcmds[];
409178354Ssam
410178354Ssamchar const *progname;
411178354Ssam
412178354Ssam#ifdef NO_MAIN_ALLOWED
413178354Ssam#ifndef BUILD_AS_LIB
414178354SsamCALL(ntpq,"ntpq",ntpqmain);
415178354Ssam
416178354Ssamvoid clear_globals(void)
417178354Ssam{
418178354Ssam	extern int ntp_optind;
419178354Ssam	showhostnames = 0;	/* don'tshow host names by default */
420254082Sadrian	ntp_optind = 0;
421178354Ssam	server_entry = NULL;	/* server entry for ntp */
422178354Ssam	havehost = 0;		/* set to 1 when host open */
423178354Ssam	numassoc = 0;		/* number of cached associations */
424178354Ssam	numcmds = 0;
425178354Ssam	numhosts = 0;
426178354Ssam}
427178354Ssam#endif /* !BUILD_AS_LIB */
428178354Ssam#endif /* NO_MAIN_ALLOWED */
429178354Ssam
430178354Ssam/*
431178354Ssam * main - parse arguments and handle options
432178354Ssam */
433178354Ssam#ifndef NO_MAIN_ALLOWED
434178354Ssamint
435178354Ssammain(
436178354Ssam	int argc,
437178354Ssam	char *argv[]
438178354Ssam	)
439178354Ssam{
440178354Ssam	return ntpqmain(argc, argv);
441178354Ssam}
442178354Ssam#endif
443178354Ssam
444178354Ssam#ifndef BUILD_AS_LIB
445178354Ssamint
446178354Ssamntpqmain(
447178354Ssam	int argc,
448178354Ssam	char *argv[]
449178354Ssam	)
450178354Ssam{
451178354Ssam	u_int ihost;
452178354Ssam	int icmd;
453178354Ssam
454178354Ssam
455178354Ssam#ifdef SYS_VXWORKS
456178354Ssam	clear_globals();
457178354Ssam	taskPrioritySet(taskIdSelf(), 100 );
458178354Ssam#endif
459178354Ssam
460178354Ssam	delay_time.l_ui = 0;
461178354Ssam	delay_time.l_uf = DEFDELAY;
462178354Ssam
463178354Ssam	init_lib();	/* sets up ipv4_works, ipv6_works */
464178354Ssam	ssl_applink();
465178354Ssam	init_auth();
466178354Ssam
467178354Ssam	/* Check to see if we have IPv6. Otherwise default to IPv4 */
468178354Ssam	if (!ipv6_works)
469178354Ssam		ai_fam_default = AF_INET;
470178354Ssam
471178354Ssam	/* Fixup keytype's help based on available digest names */
472178354Ssam
473178354Ssam	{
474178354Ssam	    char *list;
475178354Ssam	    char *msg;
476178354Ssam
477178354Ssam	    list = list_digest_names();
478192468Ssam	    for (icmd = 0; icmd < sizeof(builtins)/sizeof(builtins[0]); icmd++) {
479178354Ssam		if (strcmp("keytype", builtins[icmd].keyword) == 0)
480178354Ssam		    break;
481178354Ssam	    }
482178354Ssam
483178354Ssam	    /* CID: 1295478 */
484178354Ssam	    /* This should only "trip" if "keytype" is removed from builtins */
485178354Ssam	    INSIST(icmd < sizeof(builtins)/sizeof(builtins[0]));
486178354Ssam
487203422Srpaulo#ifdef OPENSSL
488178354Ssam	    builtins[icmd].desc[0] = "digest-name";
489178354Ssam	    my_easprintf(&msg,
490178354Ssam			 "set key type to use for authenticated requests, one of:%s",
491178354Ssam			 list);
492183247Ssam#else
493178354Ssam	    builtins[icmd].desc[0] = "md5";
494178354Ssam	    my_easprintf(&msg,
495183247Ssam			 "set key type to use for authenticated requests (%s)",
496183247Ssam			 list);
497183247Ssam#endif
498183247Ssam	    builtins[icmd].comment = msg;
499183247Ssam	    free(list);
500178354Ssam	}
501178354Ssam
502178354Ssam	progname = argv[0];
503178354Ssam
504178354Ssam	{
505178354Ssam		int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
506178354Ssam		argc -= optct;
507178354Ssam		argv += optct;
508178354Ssam	}
509178354Ssam
510178354Ssam	/*
511178354Ssam	 * Process options other than -c and -p, which are specially
512178354Ssam	 * handled by ntpq_custom_opt_handler().
513178354Ssam	 */
514178354Ssam
515178354Ssam	debug = OPT_VALUE_SET_DEBUG_LEVEL;
516178354Ssam
517178354Ssam	if (HAVE_OPT(IPV4))
518178354Ssam		ai_fam_templ = AF_INET;
519178354Ssam	else if (HAVE_OPT(IPV6))
520178354Ssam		ai_fam_templ = AF_INET6;
521178354Ssam	else
522178354Ssam		ai_fam_templ = ai_fam_default;
523178354Ssam
524178354Ssam	if (HAVE_OPT(INTERACTIVE))
525178354Ssam		interactive = 1;
526178354Ssam
527178354Ssam	if (HAVE_OPT(NUMERIC))
528178354Ssam		showhostnames = 0;
529178354Ssam
530178354Ssam	if (HAVE_OPT(WIDE))
531178354Ssam		wideremote = 1;
532191547Ssam
533191547Ssam	old_rv = HAVE_OPT(OLD_RV);
534178354Ssam
535178354Ssam	if (0 == argc) {
536178354Ssam		ADDHOST(DEFHOST);
537178354Ssam	} else {
538178354Ssam		for (ihost = 0; ihost < (u_int)argc; ihost++) {
539178354Ssam			if ('-' == *argv[ihost]) {
540178354Ssam				//
541178354Ssam				// If I really cared I'd also check:
542178354Ssam				// 0 == argv[ihost][2]
543178354Ssam				//
544178354Ssam				// and there are other cases as well...
545178354Ssam				//
546178354Ssam				if ('4' == argv[ihost][1]) {
547178354Ssam					ai_fam_templ = AF_INET;
548178354Ssam					continue;
549178354Ssam				} else if ('6' == argv[ihost][1]) {
550178354Ssam					ai_fam_templ = AF_INET6;
551178354Ssam					continue;
552178354Ssam				} else {
553178354Ssam					// XXX Throw a usage error
554178354Ssam				}
555178354Ssam			}
556178354Ssam			ADDHOST(argv[ihost]);
557178354Ssam		}
558178354Ssam	}
559178354Ssam
560178354Ssam	if (numcmds == 0 && interactive == 0
561178354Ssam	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
562178354Ssam		interactive = 1;
563178354Ssam	}
564178354Ssam
565178354Ssam	set_ctrl_c_hook(on_ctrlc);
566178354Ssam#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
567178354Ssam	if (interactive)
568178354Ssam		push_ctrl_c_handler(abortcmd);
569178354Ssam#endif /* SYS_WINNT */
570178354Ssam
571178354Ssam	if (numcmds == 0) {
572192468Ssam		(void) openhost(chosts[0].name, chosts[0].fam);
573178354Ssam		getcmds();
574178354Ssam	} else {
575178354Ssam		for (ihost = 0; ihost < numhosts; ihost++) {
576178354Ssam			if (openhost(chosts[ihost].name, chosts[ihost].fam))
577178354Ssam				for (icmd = 0; icmd < numcmds; icmd++)
578178354Ssam					docmd(ccmds[icmd]);
579221418Sadrian		}
580178354Ssam	}
581178354Ssam#ifdef SYS_WINNT
582178354Ssam	WSACleanup();
583178354Ssam#endif /* SYS_WINNT */
584178354Ssam	return 0;
585178354Ssam}
586178354Ssam#endif /* !BUILD_AS_LIB */
587178354Ssam
588178354Ssam/*
589178354Ssam * openhost - open a socket to a host
590178354Ssam */
591178354Ssamstatic	int
592178354Ssamopenhost(
593178354Ssam	const char *hname,
594178354Ssam	int	    fam
595178354Ssam	)
596178354Ssam{
597178354Ssam	const char svc[] = "ntp";
598178354Ssam	char temphost[LENHOSTNAME];
599178354Ssam	int a_info, i;
600178354Ssam	struct addrinfo hints, *ai;
601178354Ssam	sockaddr_u addr;
602178354Ssam	size_t octets;
603178354Ssam	register const char *cp;
604178354Ssam	char name[LENHOSTNAME];
605178354Ssam
606178354Ssam	/*
607178354Ssam	 * We need to get by the [] if they were entered
608178354Ssam	 */
609178354Ssam
610178354Ssam	cp = hname;
611178354Ssam
612178354Ssam	if (*cp == '[') {
613178354Ssam		cp++;
614178354Ssam		for (i = 0; *cp && *cp != ']'; cp++, i++)
615178354Ssam			name[i] = *cp;
616178354Ssam		if (*cp == ']') {
617178354Ssam			name[i] = '\0';
618178354Ssam			hname = name;
619178354Ssam		} else {
620178354Ssam			return 0;
621178354Ssam		}
622178354Ssam	}
623178354Ssam
624178354Ssam	/*
625178354Ssam	 * First try to resolve it as an ip address and if that fails,
626178354Ssam	 * do a fullblown (dns) lookup. That way we only use the dns
627178354Ssam	 * when it is needed and work around some implementations that
628178354Ssam	 * will return an "IPv4-mapped IPv6 address" address if you
629178354Ssam	 * give it an IPv4 address to lookup.
630178354Ssam	 */
631178354Ssam	ZERO(hints);
632178354Ssam	hints.ai_family = fam;
633178354Ssam	hints.ai_protocol = IPPROTO_UDP;
634178354Ssam	hints.ai_socktype = SOCK_DGRAM;
635178354Ssam	hints.ai_flags = Z_AI_NUMERICHOST;
636178354Ssam	ai = NULL;
637178354Ssam
638178354Ssam	a_info = getaddrinfo(hname, svc, &hints, &ai);
639178354Ssam	if (a_info == EAI_NONAME
640178354Ssam#ifdef EAI_NODATA
641178354Ssam	    || a_info == EAI_NODATA
642178354Ssam#endif
643178354Ssam	   ) {
644178354Ssam		hints.ai_flags = AI_CANONNAME;
645178354Ssam#ifdef AI_ADDRCONFIG
646178354Ssam		hints.ai_flags |= AI_ADDRCONFIG;
647178354Ssam#endif
648178354Ssam		a_info = getaddrinfo(hname, svc, &hints, &ai);
649178354Ssam	}
650178354Ssam#ifdef AI_ADDRCONFIG
651178354Ssam	/* Some older implementations don't like AI_ADDRCONFIG. */
652178354Ssam	if (a_info == EAI_BADFLAGS) {
653241138Sadrian		hints.ai_flags &= ~AI_ADDRCONFIG;
654178354Ssam		a_info = getaddrinfo(hname, svc, &hints, &ai);
655178354Ssam	}
656178354Ssam#endif
657178354Ssam	if (a_info != 0) {
658178354Ssam		fprintf(stderr, "%s\n", gai_strerror(a_info));
659178354Ssam		return 0;
660178354Ssam	}
661178354Ssam
662178354Ssam	INSIST(ai != NULL);
663178354Ssam	ZERO(addr);
664178354Ssam	octets = min(sizeof(addr), ai->ai_addrlen);
665178354Ssam	memcpy(&addr, ai->ai_addr, octets);
666178354Ssam
667178354Ssam	if (ai->ai_canonname == NULL) {
668178354Ssam		strlcpy(temphost, stoa(&addr), sizeof(temphost));
669178354Ssam		currenthostisnum = TRUE;
670178354Ssam	} else {
671178354Ssam		strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
672178354Ssam		currenthostisnum = FALSE;
673178354Ssam	}
674178354Ssam
675178354Ssam	if (debug > 2)
676183247Ssam		printf("Opening host %s (%s)\n",
677183247Ssam			temphost,
678178354Ssam			(ai->ai_family == AF_INET)
679178354Ssam			? "AF_INET"
680178354Ssam			: (ai->ai_family == AF_INET6)
681183247Ssam			  ? "AF_INET6"
682178354Ssam			  : "AF-???"
683178354Ssam			);
684178354Ssam
685178354Ssam	if (havehost == 1) {
686178354Ssam		if (debug > 2)
687178354Ssam			printf("Closing old host %s\n", currenthost);
688178354Ssam		closesocket(sockfd);
689178354Ssam		havehost = 0;
690178354Ssam	}
691178354Ssam	strlcpy(currenthost, temphost, sizeof(currenthost));
692178354Ssam
693178354Ssam	/* port maps to the same location in both families */
694178354Ssam	s_port = NSRCPORT(&addr);
695178354Ssam#ifdef SYS_VXWORKS
696178354Ssam	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
697178354Ssam	if (ai->ai_family == AF_INET)
698178354Ssam		*(struct sockaddr_in *)&hostaddr=
699178354Ssam			*((struct sockaddr_in *)ai->ai_addr);
700178354Ssam	else
701178354Ssam		*(struct sockaddr_in6 *)&hostaddr=
702178354Ssam			*((struct sockaddr_in6 *)ai->ai_addr);
703178354Ssam#endif /* SYS_VXWORKS */
704178354Ssam
705178354Ssam#ifdef SYS_WINNT
706178354Ssam	{
707178354Ssam		int optionValue = SO_SYNCHRONOUS_NONALERT;
708178354Ssam		int err;
709178354Ssam
710178354Ssam		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
711178354Ssam				 (char *)&optionValue, sizeof(optionValue));
712178354Ssam		if (err) {
713178354Ssam			mfprintf(stderr,
714178354Ssam				 "setsockopt(SO_SYNCHRONOUS_NONALERT)"
715178354Ssam				 " error: %m\n");
716178354Ssam			freeaddrinfo(ai);
717178354Ssam			exit(1);
718178354Ssam		}
719178354Ssam	}
720178354Ssam#endif /* SYS_WINNT */
721178354Ssam
722178354Ssam	sockfd = socket(ai->ai_family, ai->ai_socktype,
723178354Ssam			ai->ai_protocol);
724178354Ssam	if (sockfd == INVALID_SOCKET) {
725178354Ssam		error("socket");
726178354Ssam		freeaddrinfo(ai);
727178354Ssam		return 0;
728178354Ssam	}
729178354Ssam
730178354Ssam
731178354Ssam#ifdef NEED_RCVBUF_SLOP
732178354Ssam# ifdef SO_RCVBUF
733178354Ssam	{ int rbufsize = DATASIZE + 2048;	/* 2K for slop */
734178354Ssam	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
735178354Ssam		       &rbufsize, sizeof(int)) == -1)
736178354Ssam		error("setsockopt");
737178354Ssam	}
738178354Ssam# endif
739178354Ssam#endif
740178354Ssam
741178354Ssam	if
742178354Ssam#ifdef SYS_VXWORKS
743178354Ssam	   (connect(sockfd, (struct sockaddr *)&hostaddr,
744178354Ssam		    sizeof(hostaddr)) == -1)
745178354Ssam#else
746178354Ssam	   (connect(sockfd, (struct sockaddr *)ai->ai_addr,
747178354Ssam		ai->ai_addrlen) == -1)
748178354Ssam#endif /* SYS_VXWORKS */
749178354Ssam	{
750178354Ssam		error("connect");
751178354Ssam		freeaddrinfo(ai);
752178354Ssam		return 0;
753192468Ssam	}
754192468Ssam	freeaddrinfo(ai);
755178354Ssam	havehost = 1;
756178354Ssam	numassoc = 0;
757178354Ssam
758178354Ssam	return 1;
759178354Ssam}
760178354Ssam
761178354Ssam
762178354Ssamstatic void
763178354Ssamdump_hex_printable(
764178354Ssam	const void *	data,
765178354Ssam	size_t		len
766178354Ssam	)
767178354Ssam{
768178354Ssam	const char *	cdata;
769178354Ssam	const char *	rowstart;
770178354Ssam	size_t		idx;
771178354Ssam	size_t		rowlen;
772178354Ssam	u_char		uch;
773178354Ssam
774178354Ssam	cdata = data;
775178354Ssam	while (len > 0) {
776178354Ssam		rowstart = cdata;
777178354Ssam		rowlen = min(16, len);
778178354Ssam		for (idx = 0; idx < rowlen; idx++) {
779178354Ssam			uch = *(cdata++);
780178354Ssam			printf("%02x ", uch);
781178354Ssam		}
782178354Ssam		for ( ; idx < 16 ; idx++)
783178354Ssam			printf("   ");
784178354Ssam		cdata = rowstart;
785178354Ssam		for (idx = 0; idx < rowlen; idx++) {
786178354Ssam			uch = *(cdata++);
787178354Ssam			printf("%c", (isprint(uch))
788178354Ssam					 ? uch
789178354Ssam					 : '.');
790178354Ssam		}
791178354Ssam		printf("\n");
792178354Ssam		len -= rowlen;
793178354Ssam	}
794178354Ssam}
795178354Ssam
796178354Ssam
797178354Ssam/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
798178354Ssam/*
799178354Ssam * sendpkt - send a packet to the remote host
800178354Ssam */
801178354Ssamstatic int
802178354Ssamsendpkt(
803178354Ssam	void *	xdata,
804178354Ssam	size_t	xdatalen
805178354Ssam	)
806178354Ssam{
807178354Ssam	if (debug >= 3)
808178354Ssam		printf("Sending %zu octets\n", xdatalen);
809178354Ssam
810178354Ssam	if (send(sockfd, xdata, xdatalen, 0) == -1) {
811178354Ssam		warning("write to %s failed", currenthost);
812190391Ssam		return -1;
813190391Ssam	}
814190391Ssam
815190391Ssam	if (debug >= 4) {
816178354Ssam		printf("Request packet:\n");
817190391Ssam		dump_hex_printable(xdata, xdatalen);
818178354Ssam	}
819178354Ssam	return 0;
820178354Ssam}
821178354Ssam
822178354Ssam/*
823178354Ssam * getresponse - get a (series of) response packet(s) and return the data
824178354Ssam */
825178354Ssamstatic int
826178354Ssamgetresponse(
827178354Ssam	int opcode,
828178354Ssam	int associd,
829178354Ssam	u_short *rstatus,
830178354Ssam	size_t *rsize,
831178354Ssam	const char **rdata,
832178354Ssam	int timeo
833178354Ssam	)
834178354Ssam{
835178354Ssam	struct ntp_control rpkt;
836178354Ssam	struct sock_timeval tvo;
837178354Ssam	u_short offsets[MAXFRAGS+1];
838178354Ssam	u_short counts[MAXFRAGS+1];
839178354Ssam	u_short offset;
840178354Ssam	u_short count;
841178354Ssam	size_t numfrags;
842178354Ssam	size_t f;
843178354Ssam	size_t ff;
844178354Ssam	int seenlastfrag;
845178354Ssam	int shouldbesize;
846178354Ssam	fd_set fds;
847178354Ssam	int n;
848178354Ssam	int errcode;
849178354Ssam	/* absolute timeout checks. Not 'time_t' by intention! */
850178354Ssam	uint32_t tobase;	/* base value for timeout */
851178354Ssam	uint32_t tospan;	/* timeout span (max delay) */
852178354Ssam	uint32_t todiff;	/* current delay */
853178354Ssam
854178354Ssam	/*
855178354Ssam	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
856178354Ssam	 * back in response to the request.  We peel the data out of
857178354Ssam	 * each packet and collect it in one long block.  When the last
858178354Ssam	 * packet in the sequence is received we'll know how much data we
859178354Ssam	 * should have had.  Note we use one long time out, should reconsider.
860178354Ssam	 */
861178354Ssam	*rsize = 0;
862178354Ssam	if (rstatus)
863178354Ssam		*rstatus = 0;
864178354Ssam	*rdata = (char *)pktdata;
865178354Ssam
866178354Ssam	numfrags = 0;
867178354Ssam	seenlastfrag = 0;
868178354Ssam
869178354Ssam	tobase = (uint32_t)time(NULL);
870178354Ssam
871178354Ssam	FD_ZERO(&fds);
872178354Ssam
873178354Ssam	/*
874178354Ssam	 * Loop until we have an error or a complete response.  Nearly all
875178354Ssam	 * code paths to loop again use continue.
876178354Ssam	 */
877178354Ssam	for (;;) {
878178354Ssam
879178354Ssam		if (numfrags == 0)
880178354Ssam			tvo = tvout;
881178354Ssam		else
882178354Ssam			tvo = tvsout;
883178354Ssam		tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
884178354Ssam
885178354Ssam		FD_SET(sockfd, &fds);
886178354Ssam		n = select(sockfd+1, &fds, NULL, NULL, &tvo);
887178354Ssam		if (n == -1) {
888205791Srpaulo#if !defined(SYS_WINNT) && defined(EINTR)
889205791Srpaulo			/* Windows does not know about EINTR (until very
890205791Srpaulo			 * recently) and the handling of console events
891205791Srpaulo			 * is *very* different from POSIX/UNIX signal
892205791Srpaulo			 * handling anyway.
893205516Srpaulo			 *
894205516Srpaulo			 * Under non-windows targets we map EINTR as
895205516Srpaulo			 * 'last packet was received' and try to exit
896192468Ssam			 * the receive sequence.
897191534Ssam			 */
898178354Ssam			if (errno == EINTR) {
899178354Ssam				seenlastfrag = 1;
900178354Ssam				goto maybe_final;
901178354Ssam			}
902191546Ssam#endif
903178354Ssam			warning("select fails");
904178354Ssam			return -1;
905178354Ssam		}
906178354Ssam
907178354Ssam		/*
908178354Ssam		 * Check if this is already too late. Trash the data and
909178354Ssam		 * fake a timeout if this is so.
910178354Ssam		 */
911178354Ssam		todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
912178354Ssam		if ((n > 0) && (todiff > tospan)) {
913178354Ssam			n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
914192765Ssam			n = 0; /* faked timeout return from 'select()'*/
915192468Ssam		}
916178354Ssam
917178354Ssam		if (n == 0) {
918178354Ssam			/*
919178354Ssam			 * Timed out.  Return what we have
920178354Ssam			 */
921178354Ssam			if (numfrags == 0) {
922178354Ssam				if (timeo)
923192468Ssam					fprintf(stderr,
924178354Ssam						"%s: timed out, nothing received\n",
925178354Ssam						currenthost);
926178354Ssam				return ERR_TIMEOUT;
927178354Ssam			}
928178354Ssam			if (timeo)
929178354Ssam				fprintf(stderr,
930178354Ssam					"%s: timed out with incomplete data\n",
931178354Ssam					currenthost);
932178354Ssam			if (debug) {
933178354Ssam				fprintf(stderr,
934178354Ssam					"ERR_INCOMPLETE: Received fragments:\n");
935178354Ssam				for (f = 0; f < numfrags; f++)
936178354Ssam					fprintf(stderr,
937178354Ssam						"%2u: %5d %5d\t%3d octets\n",
938178354Ssam						(u_int)f, offsets[f],
939178354Ssam						offsets[f] +
940186302Ssam						counts[f],
941178354Ssam						counts[f]);
942178354Ssam				fprintf(stderr,
943178354Ssam					"last fragment %sreceived\n",
944178354Ssam					(seenlastfrag)
945178354Ssam					    ? ""
946178354Ssam					    : "not ");
947178354Ssam			}
948178354Ssam			return ERR_INCOMPLETE;
949178354Ssam		}
950178354Ssam
951178354Ssam		n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
952178354Ssam		if (n == -1) {
953178354Ssam			warning("read");
954178354Ssam			return -1;
955178354Ssam		}
956178354Ssam
957178354Ssam		if (debug >= 4) {
958178354Ssam			printf("Response packet:\n");
959178354Ssam			dump_hex_printable(&rpkt, n);
960178354Ssam		}
961178354Ssam
962178354Ssam		/*
963178354Ssam		 * Check for format errors.  Bug proofing.
964178354Ssam		 */
965178354Ssam		if (n < (int)CTL_HEADER_LEN) {
966186099Ssam			if (debug)
967186151Ssam				printf("Short (%d byte) packet received\n", n);
968186099Ssam			continue;
969186099Ssam		}
970186099Ssam		if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
971178354Ssam		    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
972178354Ssam			if (debug)
973178354Ssam				printf("Packet received with version %d\n",
974178354Ssam				       PKT_VERSION(rpkt.li_vn_mode));
975178354Ssam			continue;
976178354Ssam		}
977178354Ssam		if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
978178354Ssam			if (debug)
979178354Ssam				printf("Packet received with mode %d\n",
980178354Ssam				       PKT_MODE(rpkt.li_vn_mode));
981178354Ssam			continue;
982178354Ssam		}
983178354Ssam		if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
984178354Ssam			if (debug)
985178354Ssam				printf("Received request packet, wanted response\n");
986178354Ssam			continue;
987178354Ssam		}
988178354Ssam
989178354Ssam		/*
990178354Ssam		 * Check opcode and sequence number for a match.
991178354Ssam		 * Could be old data getting to us.
992178354Ssam		 */
993178354Ssam		if (ntohs(rpkt.sequence) != sequence) {
994178354Ssam			if (debug)
995178354Ssam				printf("Received sequnce number %d, wanted %d\n",
996178354Ssam				       ntohs(rpkt.sequence), sequence);
997178354Ssam			continue;
998178354Ssam		}
999178354Ssam		if (CTL_OP(rpkt.r_m_e_op) != opcode) {
1000178354Ssam			if (debug)
1001192468Ssam			    printf(
1002178354Ssam				    "Received opcode %d, wanted %d (sequence number okay)\n",
1003178354Ssam				    CTL_OP(rpkt.r_m_e_op), opcode);
1004178354Ssam			continue;
1005178354Ssam		}
1006178354Ssam
1007178354Ssam		/*
1008178354Ssam		 * Check the error code.  If non-zero, return it.
1009178354Ssam		 */
1010178354Ssam		if (CTL_ISERROR(rpkt.r_m_e_op)) {
1011178354Ssam			errcode = (ntohs(rpkt.status) >> 8) & 0xff;
1012178354Ssam			if (CTL_ISMORE(rpkt.r_m_e_op))
1013178354Ssam				TRACE(1, ("Error code %d received on not-final packet\n",
1014178354Ssam					  errcode));
1015178354Ssam			if (errcode == CERR_UNSPEC)
1016178354Ssam				return ERR_UNSPEC;
1017178354Ssam			return errcode;
1018178354Ssam		}
1019178354Ssam
1020178354Ssam		/*
1021178354Ssam		 * Check the association ID to make sure it matches what
1022178354Ssam		 * we sent.
1023178354Ssam		 */
1024178354Ssam		if (ntohs(rpkt.associd) != associd) {
1025178354Ssam			TRACE(1, ("Association ID %d doesn't match expected %d\n",
1026178354Ssam				  ntohs(rpkt.associd), associd));
1027178354Ssam			/*
1028178354Ssam			 * Hack for silly fuzzballs which, at the time of writing,
1029178354Ssam			 * return an assID of sys.peer when queried for system variables.
1030178354Ssam			 */
1031178354Ssam#ifdef notdef
1032178354Ssam			continue;
1033178354Ssam#endif
1034178354Ssam		}
1035178354Ssam
1036178354Ssam		/*
1037178354Ssam		 * Collect offset and count.  Make sure they make sense.
1038178354Ssam		 */
1039178354Ssam		offset = ntohs(rpkt.offset);
1040178354Ssam		count = ntohs(rpkt.count);
1041178354Ssam
1042178354Ssam		/*
1043178354Ssam		 * validate received payload size is padded to next 32-bit
1044178354Ssam		 * boundary and no smaller than claimed by rpkt.count
1045178354Ssam		 */
1046178354Ssam		if (n & 0x3) {
1047178354Ssam			TRACE(1, ("Response packet not padded, size = %d\n",
1048178354Ssam				  n));
1049178354Ssam			continue;
1050178354Ssam		}
1051178354Ssam
1052178354Ssam		shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
1053178354Ssam
1054178354Ssam		if (n < shouldbesize) {
1055178354Ssam			printf("Response packet claims %u octets payload, above %ld received\n",
1056178354Ssam			       count, (long)n - CTL_HEADER_LEN);
1057178354Ssam			return ERR_INCOMPLETE;
1058178354Ssam		}
1059178354Ssam
1060178354Ssam		if (debug >= 3 && shouldbesize > n) {
1061178354Ssam			u_int32 key;
1062178354Ssam			u_int32 *lpkt;
1063178354Ssam			int maclen;
1064178354Ssam
1065178354Ssam			/*
1066178354Ssam			 * Usually we ignore authentication, but for debugging purposes
1067178354Ssam			 * we watch it here.
1068178354Ssam			 */
1069178354Ssam			/* round to 8 octet boundary */
1070178354Ssam			shouldbesize = (shouldbesize + 7) & ~7;
1071178354Ssam
1072178354Ssam			maclen = n - shouldbesize;
1073178354Ssam			if (maclen >= (int)MIN_MAC_LEN) {
1074178354Ssam				printf(
1075178354Ssam					"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1076178354Ssam					n, shouldbesize, maclen);
1077178354Ssam				lpkt = (u_int32 *)&rpkt;
1078178354Ssam				printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1079178354Ssam				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1080178354Ssam				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1081178354Ssam				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1082178354Ssam				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1083178354Ssam				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1084178354Ssam				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1085178354Ssam				key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1086178354Ssam				printf("Authenticated with keyid %lu\n", (u_long)key);
1087178354Ssam				if (key != 0 && key != info_auth_keyid) {
1088178354Ssam					printf("We don't know that key\n");
1089178354Ssam				} else {
1090178354Ssam					if (authdecrypt(key, (u_int32 *)&rpkt,
1091178354Ssam					    n - maclen, maclen)) {
1092178354Ssam						printf("Auth okay!\n");
1093178354Ssam					} else {
1094178354Ssam						printf("Auth failed!\n");
1095178354Ssam					}
1096178354Ssam				}
1097186099Ssam			}
1098186099Ssam		}
1099186099Ssam
1100186099Ssam		TRACE(2, ("Got packet, size = %d\n", n));
1101186099Ssam		if (count > (n - CTL_HEADER_LEN)) {
1102178354Ssam			TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
1103192468Ssam				  count, (long)n - CTL_HEADER_LEN));
1104178354Ssam			continue;
1105178354Ssam		}
1106178354Ssam		if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1107178354Ssam			TRACE(1, ("Received count of 0 in non-final fragment\n"));
1108178354Ssam			continue;
1109178354Ssam		}
1110178354Ssam		if (offset + count > sizeof(pktdata)) {
1111178354Ssam			TRACE(1, ("Offset %u, count %u, too big for buffer\n",
1112178354Ssam				  offset, count));
1113178354Ssam			return ERR_TOOMUCH;
1114178354Ssam		}
1115178354Ssam		if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1116178354Ssam			TRACE(1, ("Received second last fragment packet\n"));
1117178354Ssam			continue;
1118178354Ssam		}
1119178354Ssam
1120178354Ssam		/*
1121178354Ssam		 * So far, so good.  Record this fragment, making sure it doesn't
1122178354Ssam		 * overlap anything.
1123178354Ssam		 */
1124178354Ssam		TRACE(2, ("Packet okay\n"));
1125178354Ssam
1126178354Ssam		if (numfrags > (MAXFRAGS - 1)) {
1127178354Ssam			TRACE(2, ("Number of fragments exceeds maximum %d\n",
1128178354Ssam				  MAXFRAGS - 1));
1129178354Ssam			return ERR_TOOMUCH;
1130178354Ssam		}
1131178354Ssam
1132178354Ssam		/*
1133178354Ssam		 * Find the position for the fragment relative to any
1134178354Ssam		 * previously received.
1135178354Ssam		 */
1136178354Ssam		for (f = 0;
1137178354Ssam		     f < numfrags && offsets[f] < offset;
1138178354Ssam		     f++) {
1139178354Ssam			/* empty body */ ;
1140178354Ssam		}
1141178354Ssam
1142178354Ssam		if (f < numfrags && offset == offsets[f]) {
1143178354Ssam			TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
1144178354Ssam				  count, offset, counts[f], offsets[f]));
1145178354Ssam			continue;
1146178354Ssam		}
1147178354Ssam
1148178354Ssam		if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
1149178354Ssam			TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
1150178354Ssam				  offset, counts[f-1], offsets[f-1]));
1151178354Ssam			continue;
1152178354Ssam		}
1153178354Ssam
1154178354Ssam		if (f < numfrags && (offset + count) > offsets[f]) {
1155178354Ssam			TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
1156178354Ssam				  count, offset, offsets[f]));
1157178354Ssam			continue;
1158178354Ssam		}
1159178354Ssam
1160178354Ssam		for (ff = numfrags; ff > f; ff--) {
1161178354Ssam			offsets[ff] = offsets[ff-1];
1162178354Ssam			counts[ff] = counts[ff-1];
1163178354Ssam		}
1164178354Ssam		offsets[f] = offset;
1165178354Ssam		counts[f] = count;
1166178354Ssam		numfrags++;
1167178354Ssam
1168178354Ssam		/*
1169178354Ssam		 * Got that stuffed in right.  Figure out if this was the last.
1170178354Ssam		 * Record status info out of the last packet.
1171178354Ssam		 */
1172178354Ssam		if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1173178354Ssam			seenlastfrag = 1;
1174178354Ssam			if (rstatus != 0)
1175178354Ssam				*rstatus = ntohs(rpkt.status);
1176178354Ssam		}
1177178354Ssam
1178178354Ssam		/*
1179178354Ssam		 * Copy the data into the data buffer, and bump the
1180178354Ssam		 * timout base in case we need more.
1181178354Ssam		 */
1182178354Ssam		memcpy((char *)pktdata + offset, &rpkt.u, count);
1183178354Ssam		tobase = (uint32_t)time(NULL);
1184178354Ssam
1185178354Ssam		/*
1186178354Ssam		 * If we've seen the last fragment, look for holes in the sequence.
1187178354Ssam		 * If there aren't any, we're done.
1188178354Ssam		 */
1189178354Ssam	  maybe_final:
1190178354Ssam		if (seenlastfrag && offsets[0] == 0) {
1191178354Ssam			for (f = 1; f < numfrags; f++)
1192178354Ssam				if (offsets[f-1] + counts[f-1] !=
1193178354Ssam				    offsets[f])
1194178354Ssam					break;
1195178354Ssam			if (f == numfrags) {
1196178354Ssam				*rsize = offsets[f-1] + counts[f-1];
1197178354Ssam				TRACE(1, ("%lu packets reassembled into response\n",
1198178354Ssam					  (u_long)numfrags));
1199178354Ssam				return 0;
1200178354Ssam			}
1201178354Ssam		}
1202178354Ssam	}  /* giant for (;;) collecting response packets */
1203178354Ssam}  /* getresponse() */
1204178354Ssam
1205178354Ssam
1206178354Ssam/*
1207178354Ssam * sendrequest - format and send a request packet
1208178354Ssam */
1209178354Ssamstatic int
1210178354Ssamsendrequest(
1211178354Ssam	int opcode,
1212178354Ssam	associd_t associd,
1213178354Ssam	int auth,
1214178354Ssam	size_t qsize,
1215178354Ssam	const char *qdata
1216178354Ssam	)
1217178354Ssam{
1218178354Ssam	struct ntp_control qpkt;
1219178354Ssam	size_t	pktsize;
1220178354Ssam	u_long	key_id;
1221178354Ssam	char *	pass;
1222178354Ssam	size_t	maclen;
1223178354Ssam
1224178354Ssam	/*
1225178354Ssam	 * Check to make sure the data will fit in one packet
1226178354Ssam	 */
1227178354Ssam	if (qsize > CTL_MAX_DATA_LEN) {
1228178354Ssam		fprintf(stderr,
1229178354Ssam			"***Internal error!  qsize (%zu) too large\n",
1230178354Ssam			qsize);
1231178354Ssam		return 1;
1232178354Ssam	}
1233178354Ssam
1234178354Ssam	/*
1235178354Ssam	 * Fill in the packet
1236178354Ssam	 */
1237178354Ssam	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1238178354Ssam	qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
1239178354Ssam	qpkt.sequence = htons(sequence);
1240178354Ssam	qpkt.status = 0;
1241178354Ssam	qpkt.associd = htons((u_short)associd);
1242178354Ssam	qpkt.offset = 0;
1243178354Ssam	qpkt.count = htons((u_short)qsize);
1244178354Ssam
1245178354Ssam	pktsize = CTL_HEADER_LEN;
1246178354Ssam
1247178354Ssam	/*
1248178354Ssam	 * If we have data, copy and pad it out to a 32-bit boundary.
1249178354Ssam	 */
1250178354Ssam	if (qsize > 0) {
1251178354Ssam		memcpy(&qpkt.u, qdata, (size_t)qsize);
1252178354Ssam		pktsize += qsize;
1253178354Ssam		while (pktsize & (sizeof(u_int32) - 1)) {
1254178354Ssam			qpkt.u.data[qsize++] = 0;
1255178354Ssam			pktsize++;
1256178354Ssam		}
1257178354Ssam	}
1258178354Ssam
1259178354Ssam	/*
1260178354Ssam	 * If it isn't authenticated we can just send it.  Otherwise
1261178354Ssam	 * we're going to have to think about it a little.
1262178354Ssam	 */
1263200242Srpaulo	if (!auth && !always_auth) {
1264178354Ssam		return sendpkt(&qpkt, pktsize);
1265178354Ssam	}
1266178354Ssam
1267178354Ssam	/*
1268178354Ssam	 * Pad out packet to a multiple of 8 octets to be sure
1269178354Ssam	 * receiver can handle it.
1270178354Ssam	 */
1271178354Ssam	while (pktsize & 7) {
1272178354Ssam		qpkt.u.data[qsize++] = 0;
1273178354Ssam		pktsize++;
1274178354Ssam	}
1275178354Ssam
1276178354Ssam	/*
1277178354Ssam	 * Get the keyid and the password if we don't have one.
1278178354Ssam	 */
1279178354Ssam	if (info_auth_keyid == 0) {
1280178354Ssam		key_id = getkeyid("Keyid: ");
1281178354Ssam		if (key_id == 0 || key_id > NTP_MAXKEY) {
1282178354Ssam			fprintf(stderr,
1283178354Ssam				"Invalid key identifier\n");
1284178354Ssam			return 1;
1285178354Ssam		}
1286178354Ssam		info_auth_keyid = key_id;
1287178354Ssam	}
1288178354Ssam	if (!authistrusted(info_auth_keyid)) {
1289178354Ssam		pass = getpass_keytype(info_auth_keytype);
1290178354Ssam		if ('\0' == pass[0]) {
1291178354Ssam			fprintf(stderr, "Invalid password\n");
1292178354Ssam			return 1;
1293178354Ssam		}
1294178354Ssam		authusekey(info_auth_keyid, info_auth_keytype,
1295178354Ssam			   (u_char *)pass);
1296178354Ssam		authtrust(info_auth_keyid, 1);
1297178354Ssam	}
1298178354Ssam
1299178354Ssam	/*
1300178354Ssam	 * Do the encryption.
1301178354Ssam	 */
1302178354Ssam	maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1303178354Ssam	if (!maclen) {
1304178354Ssam		fprintf(stderr, "Key not found\n");
1305178354Ssam		return 1;
1306178354Ssam	} else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1307178354Ssam		fprintf(stderr,
1308178354Ssam			"%zu octet MAC, %zu expected with %zu octet digest\n",
1309178354Ssam			maclen, (info_auth_hashlen + sizeof(keyid_t)),
1310178354Ssam			info_auth_hashlen);
1311178354Ssam		return 1;
1312178354Ssam	}
1313178354Ssam
1314178354Ssam	return sendpkt((char *)&qpkt, pktsize + maclen);
1315178354Ssam}
1316178354Ssam
1317178354Ssam
1318178354Ssam/*
1319178354Ssam * show_error_msg - display the error text for a mode 6 error response.
1320178354Ssam */
1321178354Ssamvoid
1322178354Ssamshow_error_msg(
1323178354Ssam	int		m6resp,
1324178354Ssam	associd_t	associd
1325178354Ssam	)
1326178354Ssam{
1327178354Ssam	if (numhosts > 1)
1328178354Ssam		fprintf(stderr, "server=%s ", currenthost);
1329178354Ssam
1330178354Ssam	switch(m6resp) {
1331178354Ssam
1332178354Ssam	case CERR_BADFMT:
1333178354Ssam		fprintf(stderr,
1334178354Ssam		    "***Server reports a bad format request packet\n");
1335178354Ssam		break;
1336178354Ssam
1337178354Ssam	case CERR_PERMISSION:
1338178354Ssam		fprintf(stderr,
1339178354Ssam		    "***Server disallowed request (authentication?)\n");
1340178354Ssam		break;
1341178354Ssam
1342178354Ssam	case CERR_BADOP:
1343178354Ssam		fprintf(stderr,
1344178354Ssam		    "***Server reports a bad opcode in request\n");
1345178354Ssam		break;
1346178354Ssam
1347178354Ssam	case CERR_BADASSOC:
1348178354Ssam		fprintf(stderr,
1349178354Ssam		    "***Association ID %d unknown to server\n",
1350178354Ssam		    associd);
1351178354Ssam		break;
1352178354Ssam
1353178354Ssam	case CERR_UNKNOWNVAR:
1354178354Ssam		fprintf(stderr,
1355178354Ssam		    "***A request variable unknown to the server\n");
1356178354Ssam		break;
1357178354Ssam
1358178354Ssam	case CERR_BADVALUE:
1359178354Ssam		fprintf(stderr,
1360178354Ssam		    "***Server indicates a request variable was bad\n");
1361178354Ssam		break;
1362178354Ssam
1363178354Ssam	case ERR_UNSPEC:
1364178354Ssam		fprintf(stderr,
1365178354Ssam		    "***Server returned an unspecified error\n");
1366178354Ssam		break;
1367178354Ssam
1368178354Ssam	case ERR_TIMEOUT:
1369178354Ssam		fprintf(stderr, "***Request timed out\n");
1370178354Ssam		break;
1371178354Ssam
1372178354Ssam	case ERR_INCOMPLETE:
1373178354Ssam		fprintf(stderr,
1374178354Ssam		    "***Response from server was incomplete\n");
1375178354Ssam		break;
1376178354Ssam
1377178354Ssam	case ERR_TOOMUCH:
1378178354Ssam		fprintf(stderr,
1379178354Ssam		    "***Buffer size exceeded for returned data\n");
1380178354Ssam		break;
1381178354Ssam
1382178354Ssam	default:
1383178354Ssam		fprintf(stderr,
1384178354Ssam		    "***Server returns unknown error code %d\n",
1385178354Ssam		    m6resp);
1386178354Ssam	}
1387178354Ssam}
1388178354Ssam
1389178354Ssam/*
1390178354Ssam * doquery - send a request and process the response, displaying
1391178354Ssam *	     error messages for any error responses.
1392178354Ssam */
1393178354Ssamint
1394178354Ssamdoquery(
1395178354Ssam	int opcode,
1396178354Ssam	associd_t associd,
1397178354Ssam	int auth,
1398178354Ssam	size_t qsize,
1399178354Ssam	const char *qdata,
1400178354Ssam	u_short *rstatus,
1401178354Ssam	size_t *rsize,
1402178354Ssam	const char **rdata
1403178354Ssam	)
1404178354Ssam{
1405178354Ssam	return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
1406178354Ssam			 rsize, rdata, FALSE);
1407178354Ssam}
1408178354Ssam
1409178354Ssam
1410178354Ssam/*
1411178354Ssam * doqueryex - send a request and process the response, optionally
1412178354Ssam *	       displaying error messages for any error responses.
1413178354Ssam */
1414178354Ssamint
1415178354Ssamdoqueryex(
1416178354Ssam	int opcode,
1417178354Ssam	associd_t associd,
1418178354Ssam	int auth,
1419178354Ssam	size_t qsize,
1420178354Ssam	const char *qdata,
1421178354Ssam	u_short *rstatus,
1422178354Ssam	size_t *rsize,
1423178354Ssam	const char **rdata,
1424178354Ssam	int quiet
1425178354Ssam	)
1426178354Ssam{
1427178354Ssam	int res;
1428178354Ssam	int done;
1429178354Ssam
1430178354Ssam	/*
1431178354Ssam	 * Check to make sure host is open
1432178354Ssam	 */
1433178354Ssam	if (!havehost) {
1434178354Ssam		fprintf(stderr, "***No host open, use `host' command\n");
1435178354Ssam		return -1;
1436178354Ssam	}
1437178354Ssam
1438178354Ssam	done = 0;
1439178354Ssam	sequence++;
1440178354Ssam
1441178354Ssam    again:
1442178354Ssam	/*
1443178354Ssam	 * send a request
1444178354Ssam	 */
1445178354Ssam	res = sendrequest(opcode, associd, auth, qsize, qdata);
1446178354Ssam	if (res != 0)
1447178354Ssam		return res;
1448178354Ssam
1449178354Ssam	/*
1450178354Ssam	 * Get the response.  If we got a standard error, print a message
1451178354Ssam	 */
1452178354Ssam	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
1453178354Ssam
1454178354Ssam	if (res > 0) {
1455178354Ssam		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
1456178354Ssam			if (res == ERR_INCOMPLETE) {
1457178354Ssam				/*
1458178354Ssam				 * better bump the sequence so we don't
1459178354Ssam				 * get confused about differing fragments.
1460178354Ssam				 */
1461178354Ssam				sequence++;
1462178354Ssam			}
1463178354Ssam			done = 1;
1464178354Ssam			goto again;
1465178354Ssam		}
1466178354Ssam		if (!quiet)
1467178354Ssam			show_error_msg(res, associd);
1468178354Ssam
1469178354Ssam	}
1470178354Ssam	return res;
1471178354Ssam}
1472178354Ssam
1473178354Ssam
1474178354Ssam#ifndef BUILD_AS_LIB
1475178354Ssam/*
1476178354Ssam * getcmds - read commands from the standard input and execute them
1477178354Ssam */
1478178354Ssamstatic void
1479178354Ssamgetcmds(void)
1480178354Ssam{
1481178354Ssam	char *	line;
1482178354Ssam	int	count;
1483178354Ssam
1484178354Ssam	ntp_readline_init(interactive ? prompt : NULL);
1485178354Ssam
1486178354Ssam	for (;;) {
1487178354Ssam		line = ntp_readline(&count);
1488178354Ssam		if (NULL == line)
1489178354Ssam			break;
1490178354Ssam		docmd(line);
1491178354Ssam		free(line);
1492178354Ssam	}
1493178354Ssam
1494178354Ssam	ntp_readline_uninit();
1495178354Ssam}
1496178354Ssam#endif /* !BUILD_AS_LIB */
1497178354Ssam
1498178354Ssam
1499178354Ssam#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
1500178354Ssam/*
1501178354Ssam * abortcmd - catch interrupts and abort the current command
1502178354Ssam */
1503178354Ssamstatic int
1504178354Ssamabortcmd(void)
1505178354Ssam{
1506178354Ssam	if (current_output == stdout)
1507178354Ssam		(void) fflush(stdout);
1508178354Ssam	putc('\n', stderr);
1509178354Ssam	(void) fflush(stderr);
1510178354Ssam	if (jump) {
1511178354Ssam		jump = 0;
1512178354Ssam		longjmp(interrupt_buf, 1);
1513178354Ssam	}
1514178354Ssam	return TRUE;
1515178354Ssam}
1516178354Ssam#endif	/* !SYS_WINNT && !BUILD_AS_LIB */
1517178354Ssam
1518178354Ssam
1519178354Ssam#ifndef	BUILD_AS_LIB
1520178354Ssam/*
1521178354Ssam * docmd - decode the command line and execute a command
1522178354Ssam */
1523178354Ssamstatic void
1524178354Ssamdocmd(
1525178354Ssam	const char *cmdline
1526178354Ssam	)
1527178354Ssam{
1528178354Ssam	char *tokens[1+MAXARGS+2];
1529178354Ssam	struct parse pcmd;
1530178354Ssam	int ntok;
1531178354Ssam	static int i;
1532178354Ssam	struct xcmd *xcmd;
1533178354Ssam
1534178354Ssam	/*
1535178354Ssam	 * Tokenize the command line.  If nothing on it, return.
1536178354Ssam	 */
1537178354Ssam	tokenize(cmdline, tokens, &ntok);
1538178354Ssam	if (ntok == 0)
1539178354Ssam	    return;
1540178354Ssam
1541178354Ssam	/*
1542178354Ssam	 * Find the appropriate command description.
1543178354Ssam	 */
1544178354Ssam	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
1545178354Ssam	if (i == 0) {
1546178354Ssam		(void) fprintf(stderr, "***Command `%s' unknown\n",
1547178354Ssam			       tokens[0]);
1548178354Ssam		return;
1549178354Ssam	} else if (i >= 2) {
1550178354Ssam		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
1551178354Ssam			       tokens[0]);
1552178354Ssam		return;
1553178354Ssam	}
1554178354Ssam
1555178354Ssam	/* Warn about ignored extra args */
1556178354Ssam	for (i = MAXARGS + 1; i < ntok ; ++i) {
1557178354Ssam		fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
1558178354Ssam	}
1559178354Ssam
1560178354Ssam	/*
1561178354Ssam	 * Save the keyword, then walk through the arguments, interpreting
1562178354Ssam	 * as we go.
1563178354Ssam	 */
1564178354Ssam	pcmd.keyword = tokens[0];
1565178354Ssam	pcmd.nargs = 0;
1566178354Ssam	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
1567178354Ssam		if ((i+1) >= ntok) {
1568178354Ssam			if (!(xcmd->arg[i] & OPT)) {
1569178354Ssam				printusage(xcmd, stderr);
1570178354Ssam				return;
1571178354Ssam			}
1572178354Ssam			break;
1573178354Ssam		}
1574178354Ssam		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1575178354Ssam			break;
1576178354Ssam		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1577178354Ssam			return;
1578178354Ssam		pcmd.nargs++;
1579178354Ssam	}
1580178354Ssam
1581178354Ssam	i++;
1582178354Ssam	if (i < ntok && *tokens[i] == '>') {
1583178354Ssam		char *fname;
1584178354Ssam
1585178354Ssam		if (*(tokens[i]+1) != '\0')
1586178354Ssam			fname = tokens[i]+1;
1587178354Ssam		else if ((i+1) < ntok)
1588178354Ssam			fname = tokens[i+1];
1589178354Ssam		else {
1590178354Ssam			(void) fprintf(stderr, "***No file for redirect\n");
1591178354Ssam			return;
1592178354Ssam		}
1593178354Ssam
1594178354Ssam		current_output = fopen(fname, "w");
1595178354Ssam		if (current_output == NULL) {
1596178354Ssam			(void) fprintf(stderr, "***Error opening %s: ", fname);
1597178354Ssam			perror("");
1598178354Ssam			return;
1599178354Ssam		}
1600178354Ssam		i = 1;		/* flag we need a close */
1601178354Ssam	} else {
1602178354Ssam		current_output = stdout;
1603178354Ssam		i = 0;		/* flag no close */
1604178354Ssam	}
1605178354Ssam
1606178354Ssam	if (interactive && setjmp(interrupt_buf)) {
1607178354Ssam		jump = 0;
1608178354Ssam		return;
1609178354Ssam	} else {
1610178354Ssam		jump++;
1611178354Ssam		(xcmd->handler)(&pcmd, current_output);
1612178354Ssam		jump = 0;	/* HMS: 961106: was after fclose() */
1613178354Ssam		if (i) (void) fclose(current_output);
1614178354Ssam	}
1615178354Ssam
1616178354Ssam	return;
1617178354Ssam}
1618178354Ssam
1619178354Ssam
1620178354Ssam/*
1621178354Ssam * tokenize - turn a command line into tokens
1622178354Ssam *
1623178354Ssam * SK: Modified to allow a quoted string
1624178354Ssam *
1625178354Ssam * HMS: If the first character of the first token is a ':' then (after
1626178354Ssam * eating inter-token whitespace) the 2nd token is the rest of the line.
1627178354Ssam */
1628178354Ssam
1629178354Ssamstatic void
1630178354Ssamtokenize(
1631178354Ssam	const char *line,
1632178354Ssam	char **tokens,
1633178354Ssam	int *ntok
1634178354Ssam	)
1635178354Ssam{
1636178354Ssam	register const char *cp;
1637178354Ssam	register char *sp;
1638178354Ssam	static char tspace[MAXLINE];
1639193543Ssam
1640178354Ssam	sp = tspace;
1641178354Ssam	cp = line;
1642178354Ssam	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
1643178354Ssam		tokens[*ntok] = sp;
1644178354Ssam
1645178354Ssam		/* Skip inter-token whitespace */
1646178354Ssam		while (ISSPACE(*cp))
1647178354Ssam		    cp++;
1648178354Ssam
1649178354Ssam		/* If we're at EOL we're done */
1650178354Ssam		if (ISEOL(*cp))
1651178354Ssam		    break;
1652178354Ssam
1653178354Ssam		/* If this is the 2nd token and the first token begins
1654178354Ssam		 * with a ':', then just grab to EOL.
1655178354Ssam		 */
1656178354Ssam
1657178354Ssam		if (*ntok == 1 && tokens[0][0] == ':') {
1658178354Ssam			do {
1659178354Ssam				if (sp - tspace >= MAXLINE)
1660178354Ssam					goto toobig;
1661178354Ssam				*sp++ = *cp++;
1662178354Ssam			} while (!ISEOL(*cp));
1663178354Ssam		}
1664178354Ssam
1665178354Ssam		/* Check if this token begins with a double quote.
1666178354Ssam		 * If yes, continue reading till the next double quote
1667178354Ssam		 */
1668178354Ssam		else if (*cp == '\"') {
1669178354Ssam			++cp;
1670178354Ssam			do {
1671178354Ssam				if (sp - tspace >= MAXLINE)
1672178354Ssam					goto toobig;
1673178354Ssam				*sp++ = *cp++;
1674178354Ssam			} while ((*cp != '\"') && !ISEOL(*cp));
1675178354Ssam			/* HMS: a missing closing " should be an error */
1676178354Ssam		}
1677178354Ssam		else {
1678178354Ssam			do {
1679178354Ssam				if (sp - tspace >= MAXLINE)
1680178354Ssam					goto toobig;
1681178354Ssam				*sp++ = *cp++;
1682178354Ssam			} while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1683178354Ssam			/* HMS: Why check for a " in the previous line? */
1684192468Ssam		}
1685178354Ssam
1686178354Ssam		if (sp - tspace >= MAXLINE)
1687178354Ssam			goto toobig;
1688178354Ssam		*sp++ = '\0';
1689178354Ssam	}
1690178354Ssam	return;
1691178354Ssam
1692178354Ssam  toobig:
1693178354Ssam	*ntok = 0;
1694178354Ssam	fprintf(stderr,
1695178354Ssam		"***Line `%s' is too big\n",
1696178354Ssam		line);
1697178354Ssam	return;
1698178354Ssam}
1699178354Ssam
1700178354Ssam
1701178354Ssam/*
1702178354Ssam * getarg - interpret an argument token
1703178354Ssam */
1704178354Ssamstatic int
1705178354Ssamgetarg(
1706178354Ssam	const char *str,
1707178354Ssam	int code,
1708178354Ssam	arg_v *argp
1709178354Ssam	)
1710178354Ssam{
1711178354Ssam	u_long ul;
1712178354Ssam
1713178354Ssam	switch (code & ~OPT) {
1714178354Ssam	case NTP_STR:
1715178354Ssam		argp->string = str;
1716178354Ssam		break;
1717178354Ssam
1718178354Ssam	case NTP_ADD:
1719178354Ssam		if (!getnetnum(str, &argp->netnum, NULL, 0))
1720178354Ssam			return 0;
1721178354Ssam		break;
1722178354Ssam
1723178354Ssam	case NTP_UINT:
1724178354Ssam		if ('&' == str[0]) {
1725178354Ssam			if (!atouint(&str[1], &ul)) {
1726178354Ssam				fprintf(stderr,
1727178354Ssam					"***Association index `%s' invalid/undecodable\n",
1728178354Ssam					str);
1729178354Ssam				return 0;
1730178354Ssam			}
1731178354Ssam			if (0 == numassoc) {
1732178354Ssam				dogetassoc(stdout);
1733178354Ssam				if (0 == numassoc) {
1734178354Ssam					fprintf(stderr,
1735178354Ssam						"***No associations found, `%s' unknown\n",
1736178354Ssam						str);
1737178354Ssam					return 0;
1738178354Ssam				}
1739192468Ssam			}
1740178354Ssam			ul = min(ul, numassoc);
1741178354Ssam			argp->uval = assoc_cache[ul - 1].assid;
1742178354Ssam			break;
1743178354Ssam		}
1744178354Ssam		if (!atouint(str, &argp->uval)) {
1745178354Ssam			fprintf(stderr, "***Illegal unsigned value %s\n",
1746178354Ssam				str);
1747178354Ssam			return 0;
1748178354Ssam		}
1749178354Ssam		break;
1750178354Ssam
1751178354Ssam	case NTP_INT:
1752178354Ssam		if (!atoint(str, &argp->ival)) {
1753178354Ssam			fprintf(stderr, "***Illegal integer value %s\n",
1754178354Ssam				str);
1755178354Ssam			return 0;
1756178354Ssam		}
1757178354Ssam		break;
1758178354Ssam
1759178354Ssam	case IP_VERSION:
1760178354Ssam		if (!strcmp("-6", str)) {
1761178354Ssam			argp->ival = 6;
1762178354Ssam		} else if (!strcmp("-4", str)) {
1763178354Ssam			argp->ival = 4;
1764178354Ssam		} else {
1765178354Ssam			fprintf(stderr, "***Version must be either 4 or 6\n");
1766178354Ssam			return 0;
1767178354Ssam		}
1768178354Ssam		break;
1769178354Ssam	}
1770178354Ssam
1771178354Ssam	return 1;
1772178354Ssam}
1773178354Ssam#endif	/* !BUILD_AS_LIB */
1774178354Ssam
1775178354Ssam
1776178354Ssam/*
1777178354Ssam * findcmd - find a command in a command description table
1778178354Ssam */
1779178354Ssamstatic int
1780178354Ssamfindcmd(
1781178354Ssam	const char *	str,
1782178354Ssam	struct xcmd *	clist1,
1783178354Ssam	struct xcmd *	clist2,
1784178354Ssam	struct xcmd **	cmd
1785178354Ssam	)
1786178354Ssam{
1787178354Ssam	struct xcmd *cl;
1788178354Ssam	size_t clen;
1789178354Ssam	int nmatch;
1790178354Ssam	struct xcmd *nearmatch = NULL;
1791178354Ssam	struct xcmd *clist;
1792178354Ssam
1793178354Ssam	clen = strlen(str);
1794178354Ssam	nmatch = 0;
1795178354Ssam	if (clist1 != 0)
1796178354Ssam	    clist = clist1;
1797178354Ssam	else if (clist2 != 0)
1798178354Ssam	    clist = clist2;
1799178354Ssam	else
1800178354Ssam	    return 0;
1801178354Ssam
1802178354Ssam    again:
1803228514Sadrian	for (cl = clist; cl->keyword != 0; cl++) {
1804228514Sadrian		/* do a first character check, for efficiency */
1805228622Sbschmidt		if (*str != *(cl->keyword))
1806228514Sadrian		    continue;
1807228514Sadrian		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
1808228514Sadrian			/*
1809228514Sadrian			 * Could be extact match, could be approximate.
1810228514Sadrian			 * Is exact if the length of the keyword is the
1811228514Sadrian			 * same as the str.
1812178354Ssam			 */
1813178354Ssam			if (*((cl->keyword) + clen) == '\0') {
1814178354Ssam				*cmd = cl;
1815178354Ssam				return 1;
1816178354Ssam			}
1817178354Ssam			nmatch++;
1818178354Ssam			nearmatch = cl;
1819178354Ssam		}
1820178354Ssam	}
1821178354Ssam
1822178354Ssam	/*
1823178354Ssam	 * See if there is more to do.  If so, go again.  Sorry about the
1824178354Ssam	 * goto, too much looking at BSD sources...
1825178354Ssam	 */
1826178354Ssam	if (clist == clist1 && clist2 != 0) {
1827178354Ssam		clist = clist2;
1828178354Ssam		goto again;
1829178354Ssam	}
1830178354Ssam
1831178354Ssam	/*
1832178354Ssam	 * If we got extactly 1 near match, use it, else return number
1833178354Ssam	 * of matches.
1834178354Ssam	 */
1835178354Ssam	if (nmatch == 1) {
1836178354Ssam		*cmd = nearmatch;
1837178354Ssam		return 1;
1838178354Ssam	}
1839178354Ssam	return nmatch;
1840178354Ssam}
1841178354Ssam
1842178354Ssam
1843178354Ssam/*
1844178354Ssam * getnetnum - given a host name, return its net number
1845178354Ssam *	       and (optional) full name
1846178354Ssam */
1847178354Ssamint
1848178354Ssamgetnetnum(
1849178354Ssam	const char *hname,
1850178354Ssam	sockaddr_u *num,
1851178354Ssam	char *fullhost,
1852178354Ssam	int af
1853178354Ssam	)
1854178354Ssam{
1855178354Ssam	struct addrinfo hints, *ai = NULL;
1856178354Ssam
1857178354Ssam	ZERO(hints);
1858178354Ssam	hints.ai_flags = AI_CANONNAME;
1859178354Ssam#ifdef AI_ADDRCONFIG
1860178354Ssam	hints.ai_flags |= AI_ADDRCONFIG;
1861178354Ssam#endif
1862178354Ssam
1863178354Ssam	/*
1864178354Ssam	 * decodenetnum only works with addresses, but handles syntax
1865178354Ssam	 * that getaddrinfo doesn't:  [2001::1]:1234
1866178354Ssam	 */
1867178354Ssam	if (decodenetnum(hname, num)) {
1868178354Ssam		if (fullhost != NULL)
1869178354Ssam			getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1870178354Ssam				    LENHOSTNAME, NULL, 0, 0);
1871178354Ssam		return 1;
1872178354Ssam	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1873178354Ssam		INSIST(sizeof(*num) >= ai->ai_addrlen);
1874178354Ssam		memcpy(num, ai->ai_addr, ai->ai_addrlen);
1875178354Ssam		if (fullhost != NULL) {
1876178354Ssam			if (ai->ai_canonname != NULL)
1877178354Ssam				strlcpy(fullhost, ai->ai_canonname,
1878178354Ssam					LENHOSTNAME);
1879178354Ssam			else
1880178354Ssam				getnameinfo(&num->sa, SOCKLEN(num),
1881178354Ssam					    fullhost, LENHOSTNAME, NULL,
1882178354Ssam					    0, 0);
1883178354Ssam		}
1884178354Ssam		freeaddrinfo(ai);
1885178354Ssam		return 1;
1886178354Ssam	}
1887178354Ssam	fprintf(stderr, "***Can't find host %s\n", hname);
1888178354Ssam
1889178354Ssam	return 0;
1890228622Sbschmidt}
1891178354Ssam
1892178354Ssam
1893178354Ssam/*
1894178354Ssam * nntohost - convert network number to host name.  This routine enforces
1895178354Ssam *	       the showhostnames setting.
1896178354Ssam */
1897178354Ssamconst char *
1898178354Ssamnntohost(
1899178354Ssam	sockaddr_u *netnum
1900178354Ssam	)
1901178354Ssam{
1902178354Ssam	return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
1903178354Ssam}
1904178354Ssam
1905178354Ssam
1906178354Ssam/*
1907178354Ssam * nntohost_col - convert network number to host name in fixed width.
1908178354Ssam *		  This routine enforces the showhostnames setting.
1909178354Ssam *		  When displaying hostnames longer than the width,
1910192468Ssam *		  the first part of the hostname is displayed.  When
1911192468Ssam *		  displaying numeric addresses longer than the width,
1912178354Ssam *		  Such as IPv6 addresses, the caller decides whether
1913192468Ssam *		  the first or last of the numeric address is used.
1914178354Ssam */
1915178354Ssamconst char *
1916178354Ssamnntohost_col(
1917178354Ssam	sockaddr_u *	addr,
1918178354Ssam	size_t		width,
1919178354Ssam	int		preserve_lowaddrbits
1920178354Ssam	)
1921178354Ssam{
1922178354Ssam	const char *	out;
1923178354Ssam
1924178354Ssam	if (!showhostnames || SOCK_UNSPEC(addr)) {
1925178354Ssam		if (preserve_lowaddrbits)
1926178354Ssam			out = trunc_left(stoa(addr), width);
1927178354Ssam		else
1928178354Ssam			out = trunc_right(stoa(addr), width);
1929178354Ssam	} else if (ISREFCLOCKADR(addr)) {
1930178354Ssam		out = refnumtoa(addr);
1931178354Ssam	} else {
1932178354Ssam		out = trunc_right(socktohost(addr), width);
1933178354Ssam	}
1934178354Ssam	return out;
1935178354Ssam}
1936178354Ssam
1937178354Ssam
1938178354Ssam/*
1939178354Ssam * nntohostp() is the same as nntohost() plus a :port suffix
1940178354Ssam */
1941178354Ssamconst char *
1942178354Ssamnntohostp(
1943178354Ssam	sockaddr_u *netnum
1944178354Ssam	)
1945178354Ssam{
1946178354Ssam	const char *	hostn;
1947178354Ssam	char *		buf;
1948178354Ssam
1949178354Ssam	if (!showhostnames || SOCK_UNSPEC(netnum))
1950178354Ssam		return sptoa(netnum);
1951178354Ssam	else if (ISREFCLOCKADR(netnum))
1952178354Ssam		return refnumtoa(netnum);
1953178354Ssam
1954178354Ssam	hostn = socktohost(netnum);
1955178354Ssam	LIB_GETBUF(buf);
1956178354Ssam	snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
1957178354Ssam
1958178354Ssam	return buf;
1959178354Ssam}
1960178354Ssam
1961178354Ssam/*
1962178354Ssam * rtdatetolfp - decode an RT-11 date into an l_fp
1963178354Ssam */
1964178354Ssamstatic int
1965178354Ssamrtdatetolfp(
1966178354Ssam	char *str,
1967178354Ssam	l_fp *lfp
1968178354Ssam	)
1969178354Ssam{
1970178354Ssam	register char *cp;
1971178354Ssam	register int i;
1972178354Ssam	struct calendar cal;
1973178354Ssam	char buf[4];
1974178354Ssam
1975178354Ssam	cal.yearday = 0;
1976178354Ssam
1977178354Ssam	/*
1978178354Ssam	 * An RT-11 date looks like:
1979178354Ssam	 *
1980178354Ssam	 * d[d]-Mth-y[y] hh:mm:ss
1981178354Ssam	 *
1982178354Ssam	 * (No docs, but assume 4-digit years are also legal...)
1983178354Ssam	 *
1984178354Ssam	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
1985178354Ssam	 */
1986178354Ssam	cp = str;
1987178354Ssam	if (!isdigit((int)*cp)) {
1988178354Ssam		if (*cp == '-') {
1989178354Ssam			/*
1990178354Ssam			 * Catch special case
1991178354Ssam			 */
1992178354Ssam			L_CLR(lfp);
1993178354Ssam			return 1;
1994178354Ssam		}
1995178354Ssam		return 0;
1996178354Ssam	}
1997178354Ssam
1998178354Ssam	cal.monthday = (u_char) (*cp++ - '0');	/* ascii dependent */
1999178354Ssam	if (isdigit((int)*cp)) {
2000178354Ssam		cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
2001178354Ssam		cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
2002178354Ssam	}
2003178354Ssam
2004178354Ssam	if (*cp++ != '-')
2005178354Ssam	    return 0;
2006178354Ssam
2007178354Ssam	for (i = 0; i < 3; i++)
2008178354Ssam	    buf[i] = *cp++;
2009178354Ssam	buf[3] = '\0';
2010190391Ssam
2011178354Ssam	for (i = 0; i < 12; i++)
2012178354Ssam	    if (STREQ(buf, months[i]))
2013190391Ssam		break;
2014193655Ssam	if (i == 12)
2015178354Ssam	    return 0;
2016178354Ssam	cal.month = (u_char)(i + 1);
2017178354Ssam
2018178354Ssam	if (*cp++ != '-')
2019178354Ssam	    return 0;
2020178354Ssam
2021178354Ssam	if (!isdigit((int)*cp))
2022178354Ssam	    return 0;
2023178354Ssam	cal.year = (u_short)(*cp++ - '0');
2024178354Ssam	if (isdigit((int)*cp)) {
2025178354Ssam		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2026178354Ssam		cal.year = (u_short)(*cp++ - '0');
2027178354Ssam	}
2028178354Ssam	if (isdigit((int)*cp)) {
2029178354Ssam		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2030178354Ssam		cal.year = (u_short)(cal.year + *cp++ - '0');
2031178354Ssam	}
2032178354Ssam	if (isdigit((int)*cp)) {
2033178354Ssam		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2034178354Ssam		cal.year = (u_short)(cal.year + *cp++ - '0');
2035178354Ssam	}
2036178354Ssam
2037178354Ssam	/*
2038178354Ssam	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
2039178354Ssam	 */
2040178354Ssam	if (cal.year == 0) {
2041186302Ssam		L_CLR(lfp);
2042178354Ssam		return 1;
2043178354Ssam	}
2044178354Ssam
2045178354Ssam	if (*cp++ != ' ' || !isdigit((int)*cp))
2046178354Ssam	    return 0;
2047178354Ssam	cal.hour = (u_char)(*cp++ - '0');
2048178354Ssam	if (isdigit((int)*cp)) {
2049178354Ssam		cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
2050178354Ssam		cal.hour = (u_char)(cal.hour + *cp++ - '0');
2051178354Ssam	}
2052178354Ssam
2053178354Ssam	if (*cp++ != ':' || !isdigit((int)*cp))
2054178354Ssam	    return 0;
2055178354Ssam	cal.minute = (u_char)(*cp++ - '0');
2056178354Ssam	if (isdigit((int)*cp)) {
2057178354Ssam		cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
2058178354Ssam		cal.minute = (u_char)(cal.minute + *cp++ - '0');
2059178354Ssam	}
2060178354Ssam
2061178354Ssam	if (*cp++ != ':' || !isdigit((int)*cp))
2062178354Ssam	    return 0;
2063178354Ssam	cal.second = (u_char)(*cp++ - '0');
2064178354Ssam	if (isdigit((int)*cp)) {
2065178354Ssam		cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
2066178354Ssam		cal.second = (u_char)(cal.second + *cp++ - '0');
2067178354Ssam	}
2068178354Ssam
2069178354Ssam	/*
2070178354Ssam	 * For RT-11, 1972 seems to be the pivot year
2071178354Ssam	 */
2072178354Ssam	if (cal.year < 72)
2073178354Ssam		cal.year += 2000;
2074178354Ssam	if (cal.year < 100)
2075178354Ssam		cal.year += 1900;
2076178354Ssam
2077178354Ssam	lfp->l_ui = caltontp(&cal);
2078178354Ssam	lfp->l_uf = 0;
2079178354Ssam	return 1;
2080178354Ssam}
2081178354Ssam
2082178354Ssam
2083178354Ssam/*
2084178354Ssam * decodets - decode a timestamp into an l_fp format number, with
2085178354Ssam *	      consideration of fuzzball formats.
2086178354Ssam */
2087178354Ssamint
2088178354Ssamdecodets(
2089178354Ssam	char *str,
2090178354Ssam	l_fp *lfp
2091178354Ssam	)
2092178354Ssam{
2093183254Ssam	char *cp;
2094183254Ssam	char buf[30];
2095178354Ssam	size_t b;
2096178354Ssam
2097190579Ssam	/*
2098190579Ssam	 * If it starts with a 0x, decode as hex.
2099190579Ssam	 */
2100190579Ssam	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
2101178354Ssam		return hextolfp(str+2, lfp);
2102178354Ssam
2103178354Ssam	/*
2104178354Ssam	 * If it starts with a '"', try it as an RT-11 date.
2105178354Ssam	 */
2106178354Ssam	if (*str == '"') {
2107178354Ssam		cp = str + 1;
2108178354Ssam		b = 0;
2109178354Ssam		while ('"' != *cp && '\0' != *cp &&
2110178354Ssam		       b < COUNTOF(buf) - 1)
2111178354Ssam			buf[b++] = *cp++;
2112178354Ssam		buf[b] = '\0';
2113178354Ssam		return rtdatetolfp(buf, lfp);
2114178354Ssam	}
2115178354Ssam
2116178354Ssam	/*
2117178354Ssam	 * Might still be hex.  Check out the first character.  Talk
2118178354Ssam	 * about heuristics!
2119178354Ssam	 */
2120178354Ssam	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2121178354Ssam		return hextolfp(str, lfp);
2122193655Ssam
2123178354Ssam	/*
2124178354Ssam	 * Try it as a decimal.  If this fails, try as an unquoted
2125178354Ssam	 * RT-11 date.  This code should go away eventually.
2126178354Ssam	 */
2127178354Ssam	if (atolfp(str, lfp))
2128178354Ssam		return 1;
2129192468Ssam
2130178354Ssam	return rtdatetolfp(str, lfp);
2131178354Ssam}
2132178354Ssam
2133178354Ssam
2134178354Ssam/*
2135178354Ssam * decodetime - decode a time value.  It should be in milliseconds
2136178354Ssam */
2137178354Ssamint
2138178354Ssamdecodetime(
2139178354Ssam	char *str,
2140178354Ssam	l_fp *lfp
2141178354Ssam	)
2142178354Ssam{
2143178354Ssam	return mstolfp(str, lfp);
2144178354Ssam}
2145178354Ssam
2146178354Ssam
2147178354Ssam/*
2148178354Ssam * decodeint - decode an integer
2149178354Ssam */
2150178354Ssamint
2151178354Ssamdecodeint(
2152178354Ssam	char *str,
2153178354Ssam	long *val
2154190391Ssam	)
2155178354Ssam{
2156178354Ssam	if (*str == '0') {
2157178354Ssam		if (*(str+1) == 'x' || *(str+1) == 'X')
2158178354Ssam		    return hextoint(str+2, (u_long *)val);
2159178354Ssam		return octtoint(str, (u_long *)val);
2160178354Ssam	}
2161178354Ssam	return atoint(str, val);
2162190391Ssam}
2163178354Ssam
2164178354Ssam
2165178354Ssam/*
2166178354Ssam * decodeuint - decode an unsigned integer
2167178354Ssam */
2168178354Ssamint
2169178354Ssamdecodeuint(
2170178354Ssam	char *str,
2171178354Ssam	u_long *val
2172178354Ssam	)
2173178354Ssam{
2174178354Ssam	if (*str == '0') {
2175178354Ssam		if (*(str + 1) == 'x' || *(str + 1) == 'X')
2176178354Ssam			return (hextoint(str + 2, val));
2177178354Ssam		return (octtoint(str, val));
2178178354Ssam	}
2179178354Ssam	return (atouint(str, val));
2180178354Ssam}
2181178354Ssam
2182178354Ssam
2183178354Ssam/*
2184178354Ssam * decodearr - decode an array of time values
2185178354Ssam */
2186178354Ssamstatic int
2187178354Ssamdecodearr(
2188178354Ssam	char *str,
2189178354Ssam	int *narr,
2190178354Ssam	l_fp *lfparr
2191178354Ssam	)
2192178354Ssam{
2193178354Ssam	register char *cp, *bp;
2194178354Ssam	register l_fp *lfp;
2195178354Ssam	char buf[60];
2196178354Ssam
2197178354Ssam	lfp = lfparr;
2198178354Ssam	cp = str;
2199178354Ssam	*narr = 0;
2200178354Ssam
2201178354Ssam	while (*narr < 8) {
2202178354Ssam		while (isspace((int)*cp))
2203178354Ssam		    cp++;
2204178354Ssam		if (*cp == '\0')
2205178354Ssam		    break;
2206218927Sbschmidt
2207218958Sbschmidt		bp = buf;
2208218927Sbschmidt		while (!isspace((int)*cp) && *cp != '\0')
2209218958Sbschmidt		    *bp++ = *cp++;
2210218958Sbschmidt		*bp++ = '\0';
2211218958Sbschmidt
2212218958Sbschmidt		if (!decodetime(buf, lfp))
2213218958Sbschmidt		    return 0;
2214218958Sbschmidt		(*narr)++;
2215218958Sbschmidt		lfp++;
2216218958Sbschmidt	}
2217218958Sbschmidt	return 1;
2218218927Sbschmidt}
2219218927Sbschmidt
2220178354Ssam
2221218958Sbschmidt/*
2222218958Sbschmidt * Finally, the built in command handlers
2223218958Sbschmidt */
2224218927Sbschmidt
2225178354Ssam/*
2226178354Ssam * help - tell about commands, or details of a particular command
2227178354Ssam */
2228178354Ssamstatic void
2229218927Sbschmidthelp(
2230218927Sbschmidt	struct parse *pcmd,
2231218927Sbschmidt	FILE *fp
2232218927Sbschmidt	)
2233218927Sbschmidt{
2234218927Sbschmidt	struct xcmd *xcp = NULL;	/* quiet warning */
2235178354Ssam	const char *cmd;
2236178354Ssam	const char *list[100];
2237218927Sbschmidt	size_t word, words;
2238178354Ssam	size_t row, rows;
2239178354Ssam	size_t col, cols;
2240178354Ssam	size_t length;
2241178354Ssam
2242178354Ssam	if (pcmd->nargs == 0) {
2243191546Ssam		words = 0;
2244191546Ssam		for (xcp = builtins; xcp->keyword != NULL; xcp++) {
2245191546Ssam			if (*(xcp->keyword) != '?' &&
2246191546Ssam			    words < COUNTOF(list))
2247191546Ssam				list[words++] = xcp->keyword;
2248241138Sadrian		}
2249191546Ssam		for (xcp = opcmds; xcp->keyword != NULL; xcp++)
2250191546Ssam			if (words < COUNTOF(list))
2251191546Ssam				list[words++] = xcp->keyword;
2252191546Ssam
2253191546Ssam		qsort((void *)list, words, sizeof(list[0]), helpsort);
2254191546Ssam		col = 0;
2255191546Ssam		for (word = 0; word < words; word++) {
2256178354Ssam			length = strlen(list[word]);
2257178354Ssam			col = max(col, length);
2258178354Ssam		}
2259241138Sadrian
2260241138Sadrian		cols = SCREENWIDTH / ++col;
2261178354Ssam		rows = (words + cols - 1) / cols;
2262178354Ssam
2263248069Sadrian		fprintf(fp, "ntpq commands:\n");
2264178354Ssam
2265178354Ssam		for (row = 0; row < rows; row++) {
2266178354Ssam			for (word = row; word < words; word += rows)
2267178354Ssam				fprintf(fp, "%-*.*s", (int)col,
2268178354Ssam					(int)col - 1, list[word]);
2269178354Ssam			fprintf(fp, "\n");
2270178354Ssam		}
2271178354Ssam	} else {
2272178354Ssam		cmd = pcmd->argval[0].string;
2273178354Ssam		words = findcmd(cmd, builtins, opcmds, &xcp);
2274178354Ssam		if (words == 0) {
2275178354Ssam			fprintf(stderr,
2276178354Ssam				"Command `%s' is unknown\n", cmd);
2277178354Ssam			return;
2278178354Ssam		} else if (words >= 2) {
2279178354Ssam			fprintf(stderr,
2280178354Ssam				"Command `%s' is ambiguous\n", cmd);
2281178354Ssam			return;
2282178354Ssam		}
2283178354Ssam		fprintf(fp, "function: %s\n", xcp->comment);
2284178354Ssam		printusage(xcp, fp);
2285178354Ssam	}
2286178354Ssam}
2287178354Ssam
2288178354Ssam
2289180837Ssam/*
2290180837Ssam * helpsort - do hostname qsort comparisons
2291180837Ssam */
2292180837Ssamstatic int
2293180837Ssamhelpsort(
2294180837Ssam	const void *t1,
2295180837Ssam	const void *t2
2296180837Ssam	)
2297178354Ssam{
2298178354Ssam	const char * const *	name1 = t1;
2299178354Ssam	const char * const *	name2 = t2;
2300178354Ssam
2301184288Ssam	return strcmp(*name1, *name2);
2302178354Ssam}
2303178354Ssam
2304178354Ssam
2305178354Ssam/*
2306178354Ssam * printusage - print usage information for a command
2307178354Ssam */
2308178354Ssamstatic void
2309178354Ssamprintusage(
2310178354Ssam	struct xcmd *xcp,
2311178354Ssam	FILE *fp
2312178354Ssam	)
2313178354Ssam{
2314178354Ssam	register int i;
2315178354Ssam
2316178354Ssam	/* XXX: Do we need to warn about extra args here too? */
2317178354Ssam
2318178354Ssam	(void) fprintf(fp, "usage: %s", xcp->keyword);
2319178354Ssam	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
2320178354Ssam		if (xcp->arg[i] & OPT)
2321178354Ssam		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
2322178354Ssam		else
2323178354Ssam		    (void) fprintf(fp, " %s", xcp->desc[i]);
2324178354Ssam	}
2325178354Ssam	(void) fprintf(fp, "\n");
2326178354Ssam}
2327184288Ssam
2328245098Sadrian
2329248069Sadrian/*
2330254082Sadrian * timeout - set time out time
2331254082Sadrian */
2332245098Sadrianstatic void
2333248069Sadriantimeout(
2334254082Sadrian	struct parse *pcmd,
2335245098Sadrian	FILE *fp
2336248069Sadrian	)
2337254082Sadrian{
2338245098Sadrian	int val;
2339178354Ssam
2340	if (pcmd->nargs == 0) {
2341		val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
2342		(void) fprintf(fp, "primary timeout %d ms\n", val);
2343	} else {
2344		tvout.tv_sec = pcmd->argval[0].uval / 1000;
2345		tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
2346			* 1000;
2347	}
2348}
2349
2350
2351/*
2352 * auth_delay - set delay for auth requests
2353 */
2354static void
2355auth_delay(
2356	struct parse *pcmd,
2357	FILE *fp
2358	)
2359{
2360	int isneg;
2361	u_long val;
2362
2363	if (pcmd->nargs == 0) {
2364		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
2365		(void) fprintf(fp, "delay %lu ms\n", val);
2366	} else {
2367		if (pcmd->argval[0].ival < 0) {
2368			isneg = 1;
2369			val = (u_long)(-pcmd->argval[0].ival);
2370		} else {
2371			isneg = 0;
2372			val = (u_long)pcmd->argval[0].ival;
2373		}
2374
2375		delay_time.l_ui = val / 1000;
2376		val %= 1000;
2377		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
2378
2379		if (isneg)
2380		    L_NEG(&delay_time);
2381	}
2382}
2383
2384
2385/*
2386 * host - set the host we are dealing with.
2387 */
2388static void
2389host(
2390	struct parse *pcmd,
2391	FILE *fp
2392	)
2393{
2394	int i;
2395
2396	if (pcmd->nargs == 0) {
2397		if (havehost)
2398			(void) fprintf(fp, "current host is %s\n",
2399					   currenthost);
2400		else
2401			(void) fprintf(fp, "no current host\n");
2402		return;
2403	}
2404
2405	i = 0;
2406	ai_fam_templ = ai_fam_default;
2407	if (pcmd->nargs == 2) {
2408		if (!strcmp("-4", pcmd->argval[i].string))
2409			ai_fam_templ = AF_INET;
2410		else if (!strcmp("-6", pcmd->argval[i].string))
2411			ai_fam_templ = AF_INET6;
2412		else
2413			goto no_change;
2414		i = 1;
2415	}
2416	if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
2417		fprintf(fp, "current host set to %s\n", currenthost);
2418	} else {
2419    no_change:
2420		if (havehost)
2421			fprintf(fp, "current host remains %s\n",
2422				currenthost);
2423		else
2424			fprintf(fp, "still no current host\n");
2425	}
2426}
2427
2428
2429/*
2430 * poll - do one (or more) polls of the host via NTP
2431 */
2432/*ARGSUSED*/
2433static void
2434ntp_poll(
2435	struct parse *pcmd,
2436	FILE *fp
2437	)
2438{
2439	(void) fprintf(fp, "poll not implemented yet\n");
2440}
2441
2442
2443/*
2444 * keyid - get a keyid to use for authenticating requests
2445 */
2446static void
2447keyid(
2448	struct parse *pcmd,
2449	FILE *fp
2450	)
2451{
2452	if (pcmd->nargs == 0) {
2453		if (info_auth_keyid == 0)
2454		    (void) fprintf(fp, "no keyid defined\n");
2455		else
2456		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
2457	} else {
2458		/* allow zero so that keyid can be cleared. */
2459		if(pcmd->argval[0].uval > NTP_MAXKEY)
2460		    (void) fprintf(fp, "Invalid key identifier\n");
2461		info_auth_keyid = pcmd->argval[0].uval;
2462	}
2463}
2464
2465/*
2466 * keytype - get type of key to use for authenticating requests
2467 */
2468static void
2469keytype(
2470	struct parse *pcmd,
2471	FILE *fp
2472	)
2473{
2474	const char *	digest_name;
2475	size_t		digest_len;
2476	int		key_type;
2477
2478	if (!pcmd->nargs) {
2479		fprintf(fp, "keytype is %s with %lu octet digests\n",
2480			keytype_name(info_auth_keytype),
2481			(u_long)info_auth_hashlen);
2482		return;
2483	}
2484
2485	digest_name = pcmd->argval[0].string;
2486	digest_len = 0;
2487	key_type = keytype_from_text(digest_name, &digest_len);
2488
2489	if (!key_type) {
2490		fprintf(fp, "keytype is not valid. "
2491#ifdef OPENSSL
2492			"Type \"help keytype\" for the available digest types.\n");
2493#else
2494			"Only \"md5\" is available.\n");
2495#endif
2496		return;
2497	}
2498
2499	info_auth_keytype = key_type;
2500	info_auth_hashlen = digest_len;
2501}
2502
2503
2504/*
2505 * passwd - get an authentication key
2506 */
2507/*ARGSUSED*/
2508static void
2509passwd(
2510	struct parse *pcmd,
2511	FILE *fp
2512	)
2513{
2514	const char *pass;
2515
2516	if (info_auth_keyid == 0) {
2517		info_auth_keyid = getkeyid("Keyid: ");
2518		if (info_auth_keyid == 0) {
2519			(void)fprintf(fp, "Keyid must be defined\n");
2520			return;
2521		}
2522	}
2523	if (pcmd->nargs >= 1)
2524		pass = pcmd->argval[0].string;
2525	else {
2526		pass = getpass_keytype(info_auth_keytype);
2527		if ('\0' == pass[0]) {
2528			fprintf(fp, "Password unchanged\n");
2529			return;
2530		}
2531	}
2532	authusekey(info_auth_keyid, info_auth_keytype,
2533		   (const u_char *)pass);
2534	authtrust(info_auth_keyid, 1);
2535}
2536
2537
2538/*
2539 * hostnames - set the showhostnames flag
2540 */
2541static void
2542hostnames(
2543	struct parse *pcmd,
2544	FILE *fp
2545	)
2546{
2547	if (pcmd->nargs == 0) {
2548		if (showhostnames)
2549		    (void) fprintf(fp, "hostnames being shown\n");
2550		else
2551		    (void) fprintf(fp, "hostnames not being shown\n");
2552	} else {
2553		if (STREQ(pcmd->argval[0].string, "yes"))
2554		    showhostnames = 1;
2555		else if (STREQ(pcmd->argval[0].string, "no"))
2556		    showhostnames = 0;
2557		else
2558		    (void)fprintf(stderr, "What?\n");
2559	}
2560}
2561
2562
2563
2564/*
2565 * setdebug - set/change debugging level
2566 */
2567static void
2568setdebug(
2569	struct parse *pcmd,
2570	FILE *fp
2571	)
2572{
2573	if (pcmd->nargs == 0) {
2574		(void) fprintf(fp, "debug level is %d\n", debug);
2575		return;
2576	} else if (STREQ(pcmd->argval[0].string, "no")) {
2577		debug = 0;
2578	} else if (STREQ(pcmd->argval[0].string, "more")) {
2579		debug++;
2580	} else if (STREQ(pcmd->argval[0].string, "less")) {
2581		debug--;
2582	} else {
2583		(void) fprintf(fp, "What?\n");
2584		return;
2585	}
2586	(void) fprintf(fp, "debug level set to %d\n", debug);
2587}
2588
2589
2590/*
2591 * quit - stop this nonsense
2592 */
2593/*ARGSUSED*/
2594static void
2595quit(
2596	struct parse *pcmd,
2597	FILE *fp
2598	)
2599{
2600	if (havehost)
2601	    closesocket(sockfd);	/* cleanliness next to godliness */
2602	exit(0);
2603}
2604
2605
2606/*
2607 * version - print the current version number
2608 */
2609/*ARGSUSED*/
2610static void
2611version(
2612	struct parse *pcmd,
2613	FILE *fp
2614	)
2615{
2616
2617	(void) fprintf(fp, "%s\n", Version);
2618	return;
2619}
2620
2621
2622/*
2623 * raw - set raw mode output
2624 */
2625/*ARGSUSED*/
2626static void
2627raw(
2628	struct parse *pcmd,
2629	FILE *fp
2630	)
2631{
2632	rawmode = 1;
2633	(void) fprintf(fp, "Output set to raw\n");
2634}
2635
2636
2637/*
2638 * cooked - set cooked mode output
2639 */
2640/*ARGSUSED*/
2641static void
2642cooked(
2643	struct parse *pcmd,
2644	FILE *fp
2645	)
2646{
2647	rawmode = 0;
2648	(void) fprintf(fp, "Output set to cooked\n");
2649	return;
2650}
2651
2652
2653/*
2654 * authenticate - always authenticate requests to this host
2655 */
2656static void
2657authenticate(
2658	struct parse *pcmd,
2659	FILE *fp
2660	)
2661{
2662	if (pcmd->nargs == 0) {
2663		if (always_auth) {
2664			(void) fprintf(fp,
2665				       "authenticated requests being sent\n");
2666		} else
2667		    (void) fprintf(fp,
2668				   "unauthenticated requests being sent\n");
2669	} else {
2670		if (STREQ(pcmd->argval[0].string, "yes")) {
2671			always_auth = 1;
2672		} else if (STREQ(pcmd->argval[0].string, "no")) {
2673			always_auth = 0;
2674		} else
2675		    (void)fprintf(stderr, "What?\n");
2676	}
2677}
2678
2679
2680/*
2681 * ntpversion - choose the NTP version to use
2682 */
2683static void
2684ntpversion(
2685	struct parse *pcmd,
2686	FILE *fp
2687	)
2688{
2689	if (pcmd->nargs == 0) {
2690		(void) fprintf(fp,
2691			       "NTP version being claimed is %d\n", pktversion);
2692	} else {
2693		if (pcmd->argval[0].uval < NTP_OLDVERSION
2694		    || pcmd->argval[0].uval > NTP_VERSION) {
2695			(void) fprintf(stderr, "versions %d to %d, please\n",
2696				       NTP_OLDVERSION, NTP_VERSION);
2697		} else {
2698			pktversion = (u_char) pcmd->argval[0].uval;
2699		}
2700	}
2701}
2702
2703
2704static void __attribute__((__format__(__printf__, 1, 0)))
2705vwarning(const char *fmt, va_list ap)
2706{
2707	int serrno = errno;
2708	(void) fprintf(stderr, "%s: ", progname);
2709	vfprintf(stderr, fmt, ap);
2710	(void) fprintf(stderr, ": %s\n", strerror(serrno));
2711}
2712
2713/*
2714 * warning - print a warning message
2715 */
2716static void __attribute__((__format__(__printf__, 1, 2)))
2717warning(
2718	const char *fmt,
2719	...
2720	)
2721{
2722	va_list ap;
2723	va_start(ap, fmt);
2724	vwarning(fmt, ap);
2725	va_end(ap);
2726}
2727
2728
2729/*
2730 * error - print a message and exit
2731 */
2732static void __attribute__((__format__(__printf__, 1, 2)))
2733error(
2734	const char *fmt,
2735	...
2736	)
2737{
2738	va_list ap;
2739	va_start(ap, fmt);
2740	vwarning(fmt, ap);
2741	va_end(ap);
2742	exit(1);
2743}
2744/*
2745 * getkeyid - prompt the user for a keyid to use
2746 */
2747static u_long
2748getkeyid(
2749	const char *keyprompt
2750	)
2751{
2752	int c;
2753	FILE *fi;
2754	char pbuf[20];
2755	size_t i;
2756	size_t ilim;
2757
2758#ifndef SYS_WINNT
2759	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
2760#else
2761	if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
2762#endif /* SYS_WINNT */
2763		fi = stdin;
2764	else
2765		setbuf(fi, (char *)NULL);
2766	fprintf(stderr, "%s", keyprompt); fflush(stderr);
2767	for (i = 0, ilim = COUNTOF(pbuf) - 1;
2768	     i < ilim && (c = getc(fi)) != '\n' && c != EOF;
2769	     )
2770		pbuf[i++] = (char)c;
2771	pbuf[i] = '\0';
2772	if (fi != stdin)
2773		fclose(fi);
2774
2775	return (u_long) atoi(pbuf);
2776}
2777
2778
2779/*
2780 * atoascii - printable-ize possibly ascii data using the character
2781 *	      transformations cat -v uses.
2782 */
2783static void
2784atoascii(
2785	const char *in,
2786	size_t in_octets,
2787	char *out,
2788	size_t out_octets
2789	)
2790{
2791	const u_char *	pchIn;
2792	const u_char *	pchInLimit;
2793	u_char *	pchOut;
2794	u_char		c;
2795
2796	pchIn = (const u_char *)in;
2797	pchInLimit = pchIn + in_octets;
2798	pchOut = (u_char *)out;
2799
2800	if (NULL == pchIn) {
2801		if (0 < out_octets)
2802			*pchOut = '\0';
2803		return;
2804	}
2805
2806#define	ONEOUT(c)					\
2807do {							\
2808	if (0 == --out_octets) {			\
2809		*pchOut = '\0';				\
2810		return;					\
2811	}						\
2812	*pchOut++ = (c);				\
2813} while (0)
2814
2815	for (	; pchIn < pchInLimit; pchIn++) {
2816		c = *pchIn;
2817		if ('\0' == c)
2818			break;
2819		if (c & 0x80) {
2820			ONEOUT('M');
2821			ONEOUT('-');
2822			c &= 0x7f;
2823		}
2824		if (c < ' ') {
2825			ONEOUT('^');
2826			ONEOUT((u_char)(c + '@'));
2827		} else if (0x7f == c) {
2828			ONEOUT('^');
2829			ONEOUT('?');
2830		} else
2831			ONEOUT(c);
2832	}
2833	ONEOUT('\0');
2834
2835#undef ONEOUT
2836}
2837
2838
2839/*
2840 * makeascii - print possibly ascii data using the character
2841 *	       transformations that cat -v uses.
2842 */
2843void
2844makeascii(
2845	size_t length,
2846	const char *data,
2847	FILE *fp
2848	)
2849{
2850	const u_char *data_u_char;
2851	const u_char *cp;
2852	int c;
2853
2854	data_u_char = (const u_char *)data;
2855
2856	for (cp = data_u_char; cp < data_u_char + length; cp++) {
2857		c = (int)*cp;
2858		if (c & 0x80) {
2859			putc('M', fp);
2860			putc('-', fp);
2861			c &= 0x7f;
2862		}
2863
2864		if (c < ' ') {
2865			putc('^', fp);
2866			putc(c + '@', fp);
2867		} else if (0x7f == c) {
2868			putc('^', fp);
2869			putc('?', fp);
2870		} else
2871			putc(c, fp);
2872	}
2873}
2874
2875
2876/*
2877 * asciize - same thing as makeascii except add a newline
2878 */
2879void
2880asciize(
2881	int length,
2882	char *data,
2883	FILE *fp
2884	)
2885{
2886	makeascii(length, data, fp);
2887	putc('\n', fp);
2888}
2889
2890
2891/*
2892 * truncate string to fit clipping excess at end.
2893 *	"too long"	->	"too l"
2894 * Used for hostnames.
2895 */
2896const char *
2897trunc_right(
2898	const char *	src,
2899	size_t		width
2900	)
2901{
2902	size_t	sl;
2903	char *	out;
2904
2905
2906	sl = strlen(src);
2907	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
2908		LIB_GETBUF(out);
2909		memcpy(out, src, width);
2910		out[width] = '\0';
2911
2912		return out;
2913	}
2914
2915	return src;
2916}
2917
2918
2919/*
2920 * truncate string to fit by preserving right side and using '_' to hint
2921 *	"too long"	->	"_long"
2922 * Used for local IPv6 addresses, where low bits differentiate.
2923 */
2924const char *
2925trunc_left(
2926	const char *	src,
2927	size_t		width
2928	)
2929{
2930	size_t	sl;
2931	char *	out;
2932
2933
2934	sl = strlen(src);
2935	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
2936		LIB_GETBUF(out);
2937		out[0] = '_';
2938		memcpy(&out[1], &src[sl + 1 - width], width);
2939
2940		return out;
2941	}
2942
2943	return src;
2944}
2945
2946
2947/*
2948 * Some circular buffer space
2949 */
2950#define	CBLEN	80
2951#define	NUMCB	6
2952
2953char circ_buf[NUMCB][CBLEN];
2954int nextcb = 0;
2955
2956/*
2957 * nextvar - find the next variable in the buffer
2958 */
2959int
2960nextvar(
2961	size_t *datalen,
2962	const char **datap,
2963	char **vname,
2964	char **vvalue
2965	)
2966{
2967	const char *cp;
2968	const char *np;
2969	const char *cpend;
2970	size_t srclen;
2971	size_t len;
2972	static char name[MAXVARLEN];
2973	static char value[MAXVALLEN];
2974
2975	cp = *datap;
2976	cpend = cp + *datalen;
2977
2978	/*
2979	 * Space past commas and white space
2980	 */
2981	while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
2982		cp++;
2983	if (cp >= cpend)
2984		return 0;
2985
2986	/*
2987	 * Copy name until we hit a ',', an '=', a '\r' or a '\n'.  Backspace
2988	 * over any white space and terminate it.
2989	 */
2990	srclen = strcspn(cp, ",=\r\n");
2991	srclen = min(srclen, (size_t)(cpend - cp));
2992	len = srclen;
2993	while (len > 0 && isspace((unsigned char)cp[len - 1]))
2994		len--;
2995	if (len >= sizeof(name))
2996	    return 0;
2997	if (len > 0)
2998		memcpy(name, cp, len);
2999	name[len] = '\0';
3000	*vname = name;
3001	cp += srclen;
3002
3003	/*
3004	 * Check if we hit the end of the buffer or a ','.  If so we are done.
3005	 */
3006	if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
3007		if (cp < cpend)
3008			cp++;
3009		*datap = cp;
3010		*datalen = size2int_sat(cpend - cp);
3011		*vvalue = NULL;
3012		return 1;
3013	}
3014
3015	/*
3016	 * So far, so good.  Copy out the value
3017	 */
3018	cp++;	/* past '=' */
3019	while (cp < cpend && (isspace((unsigned char)*cp) && *cp != '\r' && *cp != '\n'))
3020		cp++;
3021	np = cp;
3022	if ('"' == *np) {
3023		do {
3024			np++;
3025		} while (np < cpend && '"' != *np);
3026		if (np < cpend && '"' == *np)
3027			np++;
3028	} else {
3029		while (np < cpend && ',' != *np && '\r' != *np)
3030			np++;
3031	}
3032	len = np - cp;
3033	if (np > cpend || len >= sizeof(value) ||
3034	    (np < cpend && ',' != *np && '\r' != *np))
3035		return 0;
3036	memcpy(value, cp, len);
3037	/*
3038	 * Trim off any trailing whitespace
3039	 */
3040	while (len > 0 && isspace((unsigned char)value[len - 1]))
3041		len--;
3042	value[len] = '\0';
3043
3044	/*
3045	 * Return this.  All done.
3046	 */
3047	if (np < cpend && ',' == *np)
3048		np++;
3049	*datap = np;
3050	*datalen = size2int_sat(cpend - np);
3051	*vvalue = value;
3052	return 1;
3053}
3054
3055
3056u_short
3057varfmt(const char * varname)
3058{
3059	u_int n;
3060
3061	for (n = 0; n < COUNTOF(cookedvars); n++)
3062		if (!strcmp(varname, cookedvars[n].varname))
3063			return cookedvars[n].fmt;
3064
3065	return PADDING;
3066}
3067
3068
3069/*
3070 * printvars - print variables returned in response packet
3071 */
3072void
3073printvars(
3074	size_t length,
3075	const char *data,
3076	int status,
3077	int sttype,
3078	int quiet,
3079	FILE *fp
3080	)
3081{
3082	if (rawmode)
3083	    rawprint(sttype, length, data, status, quiet, fp);
3084	else
3085	    cookedprint(sttype, length, data, status, quiet, fp);
3086}
3087
3088
3089/*
3090 * rawprint - do a printout of the data in raw mode
3091 */
3092static void
3093rawprint(
3094	int datatype,
3095	size_t length,
3096	const char *data,
3097	int status,
3098	int quiet,
3099	FILE *fp
3100	)
3101{
3102	const char *cp;
3103	const char *cpend;
3104
3105	/*
3106	 * Essentially print the data as is.  We reformat unprintables, though.
3107	 */
3108	cp = data;
3109	cpend = data + length;
3110
3111	if (!quiet)
3112		(void) fprintf(fp, "status=0x%04x,\n", status);
3113
3114	while (cp < cpend) {
3115		if (*cp == '\r') {
3116			/*
3117			 * If this is a \r and the next character is a
3118			 * \n, supress this, else pretty print it.  Otherwise
3119			 * just output the character.
3120			 */
3121			if (cp == (cpend - 1) || *(cp + 1) != '\n')
3122			    makeascii(1, cp, fp);
3123		} else if (isspace((unsigned char)*cp) || isprint((unsigned char)*cp))
3124			putc(*cp, fp);
3125		else
3126			makeascii(1, cp, fp);
3127		cp++;
3128	}
3129}
3130
3131
3132/*
3133 * Global data used by the cooked output routines
3134 */
3135int out_chars;		/* number of characters output */
3136int out_linecount;	/* number of characters output on this line */
3137
3138
3139/*
3140 * startoutput - get ready to do cooked output
3141 */
3142static void
3143startoutput(void)
3144{
3145	out_chars = 0;
3146	out_linecount = 0;
3147}
3148
3149
3150/*
3151 * output - output a variable=value combination
3152 */
3153static void
3154output(
3155	FILE *fp,
3156	const char *name,
3157	const char *value
3158	)
3159{
3160	int len;
3161
3162	/* strlen of "name=value" */
3163	len = size2int_sat(strlen(name) + 1 + strlen(value));
3164
3165	if (out_chars != 0) {
3166		out_chars += 2;
3167		if ((out_linecount + len + 2) > MAXOUTLINE) {
3168			fputs(",\n", fp);
3169			out_linecount = 0;
3170		} else {
3171			fputs(", ", fp);
3172			out_linecount += 2;
3173		}
3174	}
3175
3176	fputs(name, fp);
3177	putc('=', fp);
3178	fputs(value, fp);
3179	out_chars += len;
3180	out_linecount += len;
3181}
3182
3183
3184/*
3185 * endoutput - terminate a block of cooked output
3186 */
3187static void
3188endoutput(
3189	FILE *fp
3190	)
3191{
3192	if (out_chars != 0)
3193		putc('\n', fp);
3194}
3195
3196
3197/*
3198 * outputarr - output an array of values
3199 */
3200static void
3201outputarr(
3202	FILE *fp,
3203	char *name,
3204	int narr,
3205	l_fp *lfp
3206	)
3207{
3208	char *bp;
3209	char *cp;
3210	size_t i;
3211	size_t len;
3212	char buf[256];
3213
3214	bp = buf;
3215	/*
3216	 * Hack to align delay and offset values
3217	 */
3218	for (i = (int)strlen(name); i < 11; i++)
3219	    *bp++ = ' ';
3220
3221	for (i = narr; i > 0; i--) {
3222		if (i != narr)
3223		    *bp++ = ' ';
3224		cp = lfptoms(lfp, 2);
3225		len = strlen(cp);
3226		if (len > 7) {
3227			cp[7] = '\0';
3228			len = 7;
3229		}
3230		while (len < 7) {
3231			*bp++ = ' ';
3232			len++;
3233		}
3234		while (*cp != '\0')
3235		    *bp++ = *cp++;
3236		lfp++;
3237	}
3238	*bp = '\0';
3239	output(fp, name, buf);
3240}
3241
3242static char *
3243tstflags(
3244	u_long val
3245	)
3246{
3247	register char *cp, *s;
3248	size_t cb;
3249	register int i;
3250	register const char *sep;
3251
3252	sep = "";
3253	s = cp = circ_buf[nextcb];
3254	if (++nextcb >= NUMCB)
3255		nextcb = 0;
3256	cb = sizeof(circ_buf[0]);
3257
3258	snprintf(cp, cb, "%02lx", val);
3259	cp += strlen(cp);
3260	cb -= strlen(cp);
3261	if (!val) {
3262		strlcat(cp, " ok", cb);
3263		cp += strlen(cp);
3264		cb -= strlen(cp);
3265	} else {
3266		if (cb) {
3267			*cp++ = ' ';
3268			cb--;
3269		}
3270		for (i = 0; i < (int)COUNTOF(tstflagnames); i++) {
3271			if (val & 0x1) {
3272				snprintf(cp, cb, "%s%s", sep,
3273					 tstflagnames[i]);
3274				sep = ", ";
3275				cp += strlen(cp);
3276				cb -= strlen(cp);
3277			}
3278			val >>= 1;
3279		}
3280	}
3281	if (cb)
3282		*cp = '\0';
3283
3284	return s;
3285}
3286
3287/*
3288 * cookedprint - output variables in cooked mode
3289 */
3290static void
3291cookedprint(
3292	int datatype,
3293	size_t length,
3294	const char *data,
3295	int status,
3296	int quiet,
3297	FILE *fp
3298	)
3299{
3300	char *name;
3301	char *value;
3302	char output_raw;
3303	int fmt;
3304	l_fp lfp;
3305	sockaddr_u hval;
3306	u_long uval;
3307	int narr;
3308	size_t len;
3309	l_fp lfparr[8];
3310	char b[12];
3311	char bn[2 * MAXVARLEN];
3312	char bv[2 * MAXVALLEN];
3313
3314	UNUSED_ARG(datatype);
3315
3316	if (!quiet)
3317		fprintf(fp, "status=%04x %s,\n", status,
3318			statustoa(datatype, status));
3319
3320	startoutput();
3321	while (nextvar(&length, &data, &name, &value)) {
3322		fmt = varfmt(name);
3323		output_raw = 0;
3324		switch (fmt) {
3325
3326		case PADDING:
3327			output_raw = '*';
3328			break;
3329
3330		case TS:
3331			if (!decodets(value, &lfp))
3332				output_raw = '?';
3333			else
3334				output(fp, name, prettydate(&lfp));
3335			break;
3336
3337		case HA:	/* fallthru */
3338		case NA:
3339			if (!decodenetnum(value, &hval)) {
3340				output_raw = '?';
3341			} else if (fmt == HA){
3342				output(fp, name, nntohost(&hval));
3343			} else {
3344				output(fp, name, stoa(&hval));
3345			}
3346			break;
3347
3348		case RF:
3349			if (decodenetnum(value, &hval)) {
3350				if (ISREFCLOCKADR(&hval))
3351					output(fp, name,
3352					       refnumtoa(&hval));
3353				else
3354					output(fp, name, stoa(&hval));
3355			} else if (strlen(value) <= 4) {
3356				output(fp, name, value);
3357			} else {
3358				output_raw = '?';
3359			}
3360			break;
3361
3362		case LP:
3363			if (!decodeuint(value, &uval) || uval > 3) {
3364				output_raw = '?';
3365			} else {
3366				b[0] = (0x2 & uval)
3367					   ? '1'
3368					   : '0';
3369				b[1] = (0x1 & uval)
3370					   ? '1'
3371					   : '0';
3372				b[2] = '\0';
3373				output(fp, name, b);
3374			}
3375			break;
3376
3377		case OC:
3378			if (!decodeuint(value, &uval)) {
3379				output_raw = '?';
3380			} else {
3381				snprintf(b, sizeof(b), "%03lo", uval);
3382				output(fp, name, b);
3383			}
3384			break;
3385
3386		case AR:
3387			if (!decodearr(value, &narr, lfparr))
3388				output_raw = '?';
3389			else
3390				outputarr(fp, name, narr, lfparr);
3391			break;
3392
3393		case FX:
3394			if (!decodeuint(value, &uval))
3395				output_raw = '?';
3396			else
3397				output(fp, name, tstflags(uval));
3398			break;
3399
3400		default:
3401			fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
3402				name, value, fmt);
3403			output_raw = '?';
3404			break;
3405		}
3406
3407		if (output_raw != 0) {
3408			/* TALOS-CAN-0063: avoid buffer overrun */
3409			atoascii(name, MAXVARLEN, bn, sizeof(bn));
3410			if (output_raw != '*') {
3411				atoascii(value, MAXVALLEN,
3412					 bv, sizeof(bv) - 1);
3413				len = strlen(bv);
3414				bv[len] = output_raw;
3415				bv[len+1] = '\0';
3416			} else {
3417				atoascii(value, MAXVALLEN,
3418					 bv, sizeof(bv));
3419			}
3420			output(fp, bn, bv);
3421		}
3422	}
3423	endoutput(fp);
3424}
3425
3426
3427/*
3428 * sortassoc - sort associations in the cache into ascending order
3429 */
3430void
3431sortassoc(void)
3432{
3433	if (numassoc > 1)
3434		qsort(assoc_cache, (size_t)numassoc,
3435		      sizeof(assoc_cache[0]), &assoccmp);
3436}
3437
3438
3439/*
3440 * assoccmp - compare two associations
3441 */
3442static int
3443assoccmp(
3444	const void *t1,
3445	const void *t2
3446	)
3447{
3448	const struct association *ass1 = t1;
3449	const struct association *ass2 = t2;
3450
3451	if (ass1->assid < ass2->assid)
3452		return -1;
3453	if (ass1->assid > ass2->assid)
3454		return 1;
3455	return 0;
3456}
3457
3458
3459/*
3460 * grow_assoc_cache() - enlarge dynamic assoc_cache array
3461 *
3462 * The strategy is to add an assumed 4k page size at a time, leaving
3463 * room for malloc() bookkeeping overhead equivalent to 4 pointers.
3464 */
3465void
3466grow_assoc_cache(void)
3467{
3468	static size_t	prior_sz;
3469	size_t		new_sz;
3470
3471	new_sz = prior_sz + 4 * 1024;
3472	if (0 == prior_sz) {
3473		new_sz -= 4 * sizeof(void *);
3474	}
3475	assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz);
3476	prior_sz = new_sz;
3477	assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0]));
3478}
3479
3480
3481/*
3482 * ntpq_custom_opt_handler - autoopts handler for -c and -p
3483 *
3484 * By default, autoopts loses the relative order of -c and -p options
3485 * on the command line.  This routine replaces the default handler for
3486 * those routines and builds a list of commands to execute preserving
3487 * the order.
3488 */
3489void
3490ntpq_custom_opt_handler(
3491	tOptions *pOptions,
3492	tOptDesc *pOptDesc
3493	)
3494{
3495	switch (pOptDesc->optValue) {
3496
3497	default:
3498		fprintf(stderr,
3499			"ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3500			pOptDesc->optValue, pOptDesc->optValue);
3501		exit(1);
3502
3503	case 'c':
3504		ADDCMD(pOptDesc->pzLastArg);
3505		break;
3506
3507	case 'p':
3508		ADDCMD("peers");
3509		break;
3510	}
3511}
3512/*
3513 * Obtain list of digest names
3514 */
3515
3516#ifdef OPENSSL
3517# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3518struct hstate {
3519   char *list;
3520   const char **seen;
3521   int idx;
3522};
3523#define K_PER_LINE 8
3524#define K_NL_PFX_STR "\n    "
3525#define K_DELIM_STR ", "
3526static void list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg )
3527{
3528    size_t len, n;
3529    const char *name, *cp, **seen;
3530    struct hstate *hstate = arg;
3531    EVP_MD_CTX ctx;
3532    u_int digest_len;
3533    u_char digest[EVP_MAX_MD_SIZE];
3534
3535    if (!m)
3536        return; /* Ignore aliases */
3537
3538    name = EVP_MD_name(m);
3539
3540    /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */
3541
3542    for( cp = name; *cp; cp++ ) {
3543	if( islower(*cp) )
3544	    return;
3545    }
3546    len = (cp - name) + 1;
3547
3548    /* There are duplicates.  Discard if name has been seen. */
3549
3550    for (seen = hstate->seen; *seen; seen++)
3551        if (!strcmp(*seen, name))
3552	    return;
3553    n = (seen - hstate->seen) + 2;
3554    hstate->seen = erealloc(hstate->seen, n * sizeof(*seen));
3555    hstate->seen[n-2] = name;
3556    hstate->seen[n-1] = NULL;
3557
3558    /* Discard MACs that NTP won't accept.
3559     * Keep this consistent with keytype_from_text() in ssl_init.c.
3560     */
3561
3562    EVP_DigestInit(&ctx, EVP_get_digestbyname(name));
3563    EVP_DigestFinal(&ctx, digest, &digest_len);
3564    if (digest_len > (MAX_MAC_LEN - sizeof(keyid_t)))
3565        return;
3566
3567    if (hstate->list != NULL)
3568	len += strlen(hstate->list);
3569    len += (hstate->idx >= K_PER_LINE)? strlen(K_NL_PFX_STR): strlen(K_DELIM_STR);
3570
3571    if (hstate->list == NULL) {
3572	hstate->list = (char *)emalloc(len);
3573	hstate->list[0] = '\0';
3574    } else
3575	hstate->list = (char *)erealloc(hstate->list, len);
3576
3577    sprintf(hstate->list + strlen(hstate->list), "%s%s",
3578	    ((hstate->idx >= K_PER_LINE)? K_NL_PFX_STR : K_DELIM_STR),
3579	    name);
3580    if (hstate->idx >= K_PER_LINE)
3581	hstate->idx = 1;
3582    else
3583	hstate->idx++;
3584}
3585# endif
3586#endif
3587
3588static char *list_digest_names(void)
3589{
3590    char *list = NULL;
3591
3592#ifdef OPENSSL
3593# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3594    struct hstate hstate = { NULL, NULL, K_PER_LINE+1 };
3595
3596    hstate.seen = (const char **) emalloc_zero(1*sizeof( const char * )); // replaces -> calloc(1, sizeof( const char * ));
3597
3598    INIT_SSL();
3599    EVP_MD_do_all_sorted(list_md_fn, &hstate);
3600    list = hstate.list;
3601    free(hstate.seen);
3602# else
3603    list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)"));
3604    strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)");
3605# endif
3606#else
3607    list = (char *)emalloc(sizeof("md5"));
3608    strcpy(list, "md5");
3609#endif
3610
3611    return list;
3612}
3613
3614#define CTRLC_STACK_MAX 4
3615static volatile size_t		ctrlc_stack_len = 0;
3616static volatile Ctrl_C_Handler	ctrlc_stack[CTRLC_STACK_MAX];
3617
3618
3619
3620int/*BOOL*/
3621push_ctrl_c_handler(
3622	Ctrl_C_Handler func
3623	)
3624{
3625	size_t size = ctrlc_stack_len;
3626	if (func && (size < CTRLC_STACK_MAX)) {
3627		ctrlc_stack[size] = func;
3628		ctrlc_stack_len = size + 1;
3629		return TRUE;
3630	}
3631	return FALSE;
3632}
3633
3634int/*BOOL*/
3635pop_ctrl_c_handler(
3636	Ctrl_C_Handler func
3637	)
3638{
3639	size_t size = ctrlc_stack_len;
3640	if (size) {
3641		--size;
3642		if (func == NULL || func == ctrlc_stack[size]) {
3643			ctrlc_stack_len = size;
3644			return TRUE;
3645		}
3646	}
3647	return FALSE;
3648}
3649
3650static void
3651on_ctrlc(void)
3652{
3653	size_t size = ctrlc_stack_len;
3654	while (size)
3655		if ((*ctrlc_stack[--size])())
3656			break;
3657}
3658
3659static int
3660my_easprintf(
3661	char ** 	ppinto,
3662	const char *	fmt   ,
3663	...
3664	)
3665{
3666	va_list	va;
3667	int	prc;
3668	size_t	len = 128;
3669	char *	buf = emalloc(len);
3670
3671  again:
3672	/* Note: we expect the memory allocation to fail long before the
3673	 * increment in buffer size actually overflows.
3674	 */
3675	buf = (buf) ? erealloc(buf, len) : emalloc(len);
3676
3677	va_start(va, fmt);
3678	prc = vsnprintf(buf, len, fmt, va);
3679	va_end(va);
3680
3681	if (prc < 0) {
3682		/* might be very old vsnprintf. Or actually MSVC... */
3683		len += len >> 1;
3684		goto again;
3685	}
3686	if ((size_t)prc >= len) {
3687		/* at least we have the proper size now... */
3688		len = (size_t)prc + 1;
3689		goto again;
3690	}
3691	if ((size_t)prc < (len - 32))
3692		buf = erealloc(buf, (size_t)prc + 1);
3693	*ppinto = buf;
3694	return prc;
3695}
3696