154359Sroberto/*
254359Sroberto * ntpq - query an NTP server using mode 6 commands
354359Sroberto */
4280849Scy#include <config.h>
554359Sroberto#include <stdio.h>
6182007Sroberto#include <ctype.h>
7182007Sroberto#include <signal.h>
8182007Sroberto#include <setjmp.h>
9182007Sroberto#include <sys/types.h>
10182007Sroberto#include <sys/time.h>
11280849Scy#ifdef HAVE_UNISTD_H
12280849Scy# include <unistd.h>
13280849Scy#endif
14280849Scy#ifdef HAVE_FCNTL_H
15280849Scy# include <fcntl.h>
16280849Scy#endif
17280849Scy#ifdef SYS_WINNT
18280849Scy# include <mswsock.h>
19280849Scy#endif
20280849Scy#include <isc/net.h>
21280849Scy#include <isc/result.h>
22182007Sroberto
2382498Sroberto#include "ntpq.h"
24285169Scy#include "ntp_assert.h"
25280849Scy#include "ntp_stdlib.h"
2682498Sroberto#include "ntp_unixtime.h"
2782498Sroberto#include "ntp_calendar.h"
2882498Sroberto#include "ntp_select.h"
29280849Scy#include "ntp_assert.h"
30280849Scy#include "lib_strbuf.h"
31280849Scy#include "ntp_lineedit.h"
32280849Scy#include "ntp_debug.h"
33280849Scy#ifdef OPENSSL
34280849Scy#include "openssl/evp.h"
35280849Scy#include "openssl/objects.h"
36285169Scy#include "openssl/err.h"
37310419Sdelphij#include "libssl_compat.h"
38280849Scy#endif
39280849Scy#include <ssl_applink.c>
4082498Sroberto
41280849Scy#include "ntp_libopts.h"
42293423Sdelphij#include "safecast.h"
43182007Sroberto
44280849Scy#ifdef SYS_VXWORKS		/* vxWorks needs mode flag -casey*/
45182007Sroberto# define open(name, flags)   open(name, flags, 0777)
46182007Sroberto# define SERVER_PORT_NUM     123
4754359Sroberto#endif
4854359Sroberto
49182007Sroberto/* we use COMMAND as an autogen keyword */
50182007Sroberto#ifdef COMMAND
51182007Sroberto# undef COMMAND
52182007Sroberto#endif
53182007Sroberto
5454359Sroberto/*
5554359Sroberto * Because we potentially understand a lot of commands we will run
5654359Sroberto * interactive if connected to a terminal.
5754359Sroberto */
5854359Srobertoint interactive = 0;		/* set to 1 when we should prompt */
5954359Srobertoconst char *prompt = "ntpq> ";	/* prompt to ask him about */
6054359Sroberto
61280849Scy/*
62280849Scy * use old readvars behavior?  --old-rv processing in ntpq resets
63280849Scy * this value based on the presence or absence of --old-rv.  It is
64280849Scy * initialized to 1 here to maintain backward compatibility with
65280849Scy * libntpq clients such as ntpsnmpd, which are free to reset it as
66280849Scy * desired.
67280849Scy */
68280849Scyint	old_rv = 1;
6954359Sroberto
70298695Sdelphij/*
71298695Sdelphij * How should we display the refid?
72298695Sdelphij * REFID_HASH, REFID_IPV4
73298695Sdelphij */
74298695Sdelphijte_Refid drefid = -1;
75280849Scy
7654359Sroberto/*
77182007Sroberto * for get_systime()
78182007Sroberto */
79182007Srobertos_char	sys_precision;		/* local clock precision (log2 s) */
80182007Sroberto
81182007Sroberto/*
8254359Sroberto * Keyid used for authenticated requests.  Obtained on the fly.
8354359Sroberto */
84132451Srobertou_long info_auth_keyid = 0;
8554359Sroberto
86280849Scystatic	int	info_auth_keytype = NID_md5;	/* MD5 */
87280849Scystatic	size_t	info_auth_hashlen = 16;		/* MD5 */
8854359Srobertou_long	current_time;		/* needed by authkeys; not used */
8954359Sroberto
9054359Sroberto/*
9154359Sroberto * Flag which indicates we should always send authenticated requests
9254359Sroberto */
9354359Srobertoint always_auth = 0;
9454359Sroberto
9554359Sroberto/*
9654359Sroberto * Flag which indicates raw mode output.
9754359Sroberto */
9854359Srobertoint rawmode = 0;
9954359Sroberto
10054359Sroberto/*
10154359Sroberto * Packet version number we use
10254359Sroberto */
10354359Srobertou_char pktversion = NTP_OLDVERSION + 1;
10454359Sroberto
10554359Sroberto/*
10654359Sroberto * Don't jump if no set jmp.
10754359Sroberto */
10854359Srobertovolatile int jump = 0;
10954359Sroberto
11054359Sroberto/*
11154359Sroberto * Format values
11254359Sroberto */
11354359Sroberto#define	PADDING	0
114280849Scy#define	HA	1	/* host address */
115280849Scy#define	NA	2	/* network address */
116280849Scy#define	LP	3	/* leap (print in binary) */
117280849Scy#define	RF	4	/* refid (sometimes string, sometimes not) */
118280849Scy#define	AR	5	/* array of times */
119280849Scy#define FX	6	/* test flags */
120280849Scy#define TS	7	/* l_fp timestamp in hex */
121280849Scy#define	OC	8	/* integer, print in octal */
12254359Sroberto#define	EOV	255	/* end of table */
12354359Sroberto
12454359Sroberto/*
125280849Scy * For the most part ntpq simply displays what ntpd provides in the
126280849Scy * mostly plain-text mode 6 responses.  A few variable names are by
127280849Scy * default "cooked" to provide more human-friendly output.
12854359Sroberto */
129280849Scyconst var_format cookedvars[] = {
130280849Scy	{ "leap",		LP },
131280849Scy	{ "reach",		OC },
132280849Scy	{ "refid",		RF },
133280849Scy	{ "reftime",		TS },
134280849Scy	{ "clock",		TS },
135280849Scy	{ "org",		TS },
136280849Scy	{ "rec",		TS },
137280849Scy	{ "xmt",		TS },
138280849Scy	{ "flash",		FX },
139280849Scy	{ "srcadr",		HA },
140280849Scy	{ "peeradr",		HA },	/* compat with others */
141280849Scy	{ "dstadr",		NA },
142280849Scy	{ "filtdelay",		AR },
143280849Scy	{ "filtoffset",		AR },
144280849Scy	{ "filtdisp",		AR },
145280849Scy	{ "filterror",		AR },	/* compat with others */
14654359Sroberto};
14754359Sroberto
14854359Sroberto
14954359Sroberto
15054359Sroberto/*
15154359Sroberto * flasher bits
15254359Sroberto */
15354359Srobertostatic const char *tstflagnames[] = {
154182007Sroberto	"pkt_dup",		/* TEST1 */
155182007Sroberto	"pkt_bogus",		/* TEST2 */
156280849Scy	"pkt_unsync",		/* TEST3 */
157182007Sroberto	"pkt_denied",		/* TEST4 */
158182007Sroberto	"pkt_auth",		/* TEST5 */
159280849Scy	"pkt_stratum",		/* TEST6 */
160280849Scy	"pkt_header",		/* TEST7 */
161182007Sroberto	"pkt_autokey",		/* TEST8 */
162182007Sroberto	"pkt_crypto",		/* TEST9 */
163182007Sroberto	"peer_stratum",		/* TEST10 */
164182007Sroberto	"peer_dist",		/* TEST11 */
165182007Sroberto	"peer_loop",		/* TEST12 */
166280849Scy	"peer_unreach"		/* TEST13 */
16754359Sroberto};
16854359Sroberto
16954359Sroberto
170280849Scyint		ntpqmain	(int,	char **);
17154359Sroberto/*
17254359Sroberto * Built in command handler declarations
17354359Sroberto */
174280849Scystatic	int	openhost	(const char *, int);
175280849Scystatic	void	dump_hex_printable(const void *, size_t);
176280849Scystatic	int	sendpkt		(void *, size_t);
177293423Sdelphijstatic	int	getresponse	(int, int, u_short *, size_t *, const char **, int);
178293423Sdelphijstatic	int	sendrequest	(int, associd_t, int, size_t, const char *);
179280849Scystatic	char *	tstflags	(u_long);
180280849Scy#ifndef BUILD_AS_LIB
181280849Scystatic	void	getcmds		(void);
182280849Scy#ifndef SYS_WINNT
183293423Sdelphijstatic	int	abortcmd	(void);
184280849Scy#endif	/* SYS_WINNT */
185280849Scystatic	void	docmd		(const char *);
186280849Scystatic	void	tokenize	(const char *, char **, int *);
187280849Scystatic	int	getarg		(const char *, int, arg_v *);
188280849Scy#endif	/* BUILD_AS_LIB */
189280849Scystatic	int	findcmd		(const char *, struct xcmd *,
190280849Scy				 struct xcmd *, struct xcmd **);
191280849Scystatic	int	rtdatetolfp	(char *, l_fp *);
192280849Scystatic	int	decodearr	(char *, int *, l_fp *);
193280849Scystatic	void	help		(struct parse *, FILE *);
194280849Scystatic	int	helpsort	(const void *, const void *);
195280849Scystatic	void	printusage	(struct xcmd *, FILE *);
196280849Scystatic	void	timeout		(struct parse *, FILE *);
197280849Scystatic	void	auth_delay	(struct parse *, FILE *);
198280849Scystatic	void	host		(struct parse *, FILE *);
199280849Scystatic	void	ntp_poll	(struct parse *, FILE *);
200280849Scystatic	void	keyid		(struct parse *, FILE *);
201280849Scystatic	void	keytype		(struct parse *, FILE *);
202280849Scystatic	void	passwd		(struct parse *, FILE *);
203280849Scystatic	void	hostnames	(struct parse *, FILE *);
204280849Scystatic	void	setdebug	(struct parse *, FILE *);
205280849Scystatic	void	quit		(struct parse *, FILE *);
206298695Sdelphijstatic	void	showdrefid	(struct parse *, FILE *);
207280849Scystatic	void	version		(struct parse *, FILE *);
208280849Scystatic	void	raw		(struct parse *, FILE *);
209280849Scystatic	void	cooked		(struct parse *, FILE *);
210280849Scystatic	void	authenticate	(struct parse *, FILE *);
211280849Scystatic	void	ntpversion	(struct parse *, FILE *);
212280849Scystatic	void	warning		(const char *, ...)
213280849Scy    __attribute__((__format__(__printf__, 1, 2)));
214280849Scystatic	void	error		(const char *, ...)
215280849Scy    __attribute__((__format__(__printf__, 1, 2)));
216280849Scystatic	u_long	getkeyid	(const char *);
217280849Scystatic	void	atoascii	(const char *, size_t, char *, size_t);
218293423Sdelphijstatic	void	cookedprint	(int, size_t, const char *, int, int, FILE *);
219293423Sdelphijstatic	void	rawprint	(int, size_t, const char *, int, int, FILE *);
220280849Scystatic	void	startoutput	(void);
221280849Scystatic	void	output		(FILE *, const char *, const char *);
222280849Scystatic	void	endoutput	(FILE *);
223280849Scystatic	void	outputarr	(FILE *, char *, int, l_fp *);
224280849Scystatic	int	assoccmp	(const void *, const void *);
225293423Sdelphijstatic	void	on_ctrlc	(void);
226280849Scy	u_short	varfmt		(const char *);
227294554Sdelphijstatic	int	my_easprintf	(char**, const char *, ...) NTP_PRINTF(2, 3);
228280849Scyvoid	ntpq_custom_opt_handler	(tOptions *, tOptDesc *);
22954359Sroberto
230285169Scy#ifdef OPENSSL
231285169Scy# ifdef HAVE_EVP_MD_DO_ALL_SORTED
232285169Scystatic void list_md_fn(const EVP_MD *m, const char *from,
233285169Scy		       const char *to, void *arg );
234285169Scy# endif
235285169Scy#endif
236285169Scystatic char *list_digest_names(void);
237280849Scy
23854359Sroberto/*
23954359Sroberto * Built-in commands we understand
24054359Sroberto */
24154359Srobertostruct xcmd builtins[] = {
242182007Sroberto	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
24354359Sroberto	  { "command", "", "", "" },
24454359Sroberto	  "tell the use and syntax of commands" },
245182007Sroberto	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
24654359Sroberto	  { "command", "", "", "" },
24754359Sroberto	  "tell the use and syntax of commands" },
248182007Sroberto	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
24954359Sroberto	  { "msec", "", "", "" },
25054359Sroberto	  "set the primary receive time out" },
251182007Sroberto	{ "delay",	auth_delay,	{ OPT|NTP_INT, NO, NO, NO },
25254359Sroberto	  { "msec", "", "", "" },
25354359Sroberto	  "set the delay added to encryption time stamps" },
254182007Sroberto	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
255132451Sroberto	  { "-4|-6", "hostname", "", "" },
25654359Sroberto	  "specify the host whose NTP server we talk to" },
257182007Sroberto	{ "poll",	ntp_poll,	{ OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
25854359Sroberto	  { "n", "verbose", "", "" },
25954359Sroberto	  "poll an NTP server in client mode `n' times" },
260280849Scy	{ "passwd",	passwd,		{ OPT|NTP_STR, NO, NO, NO },
26154359Sroberto	  { "", "", "", "" },
26254359Sroberto	  "specify a password to use for authenticated requests"},
263182007Sroberto	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
26454359Sroberto	  { "yes|no", "", "", "" },
26554359Sroberto	  "specify whether hostnames or net numbers are printed"},
266182007Sroberto	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
26754359Sroberto	  { "no|more|less", "", "", "" },
26854359Sroberto	  "set/change debugging level" },
26954359Sroberto	{ "quit",	quit,		{ NO, NO, NO, NO },
27054359Sroberto	  { "", "", "", "" },
27154359Sroberto	  "exit ntpq" },
27254359Sroberto	{ "exit",	quit,		{ NO, NO, NO, NO },
27354359Sroberto	  { "", "", "", "" },
27454359Sroberto	  "exit ntpq" },
275182007Sroberto	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
27654359Sroberto	  { "key#", "", "", "" },
27754359Sroberto	  "set keyid to use for authenticated requests" },
278298695Sdelphij	{ "drefid",	showdrefid,	{ OPT|NTP_STR, NO, NO, NO },
279298695Sdelphij	  { "hash|ipv4", "", "", "" },
280298695Sdelphij	  "display refid's as IPv4 or hash" },
28154359Sroberto	{ "version",	version,	{ NO, NO, NO, NO },
28254359Sroberto	  { "", "", "", "" },
28354359Sroberto	  "print version number" },
28454359Sroberto	{ "raw",	raw,		{ NO, NO, NO, NO },
28554359Sroberto	  { "", "", "", "" },
28654359Sroberto	  "do raw mode variable output" },
28754359Sroberto	{ "cooked",	cooked,		{ NO, NO, NO, NO },
28854359Sroberto	  { "", "", "", "" },
28954359Sroberto	  "do cooked mode variable output" },
290182007Sroberto	{ "authenticate", authenticate,	{ OPT|NTP_STR, NO, NO, NO },
29154359Sroberto	  { "yes|no", "", "", "" },
29254359Sroberto	  "always authenticate requests to this server" },
293182007Sroberto	{ "ntpversion",	ntpversion,	{ OPT|NTP_UINT, NO, NO, NO },
29454359Sroberto	  { "version number", "", "", "" },
29554359Sroberto	  "set the NTP version number to use for requests" },
296182007Sroberto	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
297285169Scy	  { "key type %s", "", "", "" },
298285169Scy	  NULL },
29954359Sroberto	{ 0,		0,		{ NO, NO, NO, NO },
30054359Sroberto	  { "", "", "", "" }, "" }
30154359Sroberto};
30254359Sroberto
30354359Sroberto
30454359Sroberto/*
30554359Sroberto * Default values we use.
30654359Sroberto */
307280849Scy#define	DEFHOST		"localhost"	/* default host name */
308280849Scy#define	DEFTIMEOUT	5		/* wait 5 seconds for 1st pkt */
309280849Scy#define	DEFSTIMEOUT	3		/* and 3 more for each additional */
310280849Scy/*
311280849Scy * Requests are automatically retried once, so total timeout with no
312280849Scy * response is a bit over 2 * DEFTIMEOUT, or 10 seconds.  At the other
313280849Scy * extreme, a request eliciting 32 packets of responses each for some
314280849Scy * reason nearly DEFSTIMEOUT seconds after the prior in that series,
315280849Scy * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
316280849Scy * 93 seconds to fail each of two times, or 186 seconds.
317280849Scy * Some commands involve a series of requests, such as "peers" and
318280849Scy * "mrulist", so the cumulative timeouts are even longer for those.
319280849Scy */
32054359Sroberto#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
32154359Sroberto#define	LENHOSTNAME	256		/* host name is 256 characters long */
32254359Sroberto#define	MAXCMDS		100		/* maximum commands on cmd line */
32354359Sroberto#define	MAXHOSTS	200		/* maximum hosts on cmd line */
32454359Sroberto#define	MAXLINE		512		/* maximum line length */
32554359Sroberto#define	MAXTOKENS	(1+MAXARGS+2)	/* maximum number of usable tokens */
32654359Sroberto#define	MAXVARLEN	256		/* maximum length of a variable name */
327280849Scy#define	MAXVALLEN	2048		/* maximum length of a variable value */
32854359Sroberto#define	MAXOUTLINE	72		/* maximum length of an output line */
329280849Scy#define SCREENWIDTH	76		/* nominal screen width in columns */
33054359Sroberto
33154359Sroberto/*
33254359Sroberto * Some variables used and manipulated locally
33354359Sroberto */
334280849Scystruct sock_timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
335280849Scystruct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
33654359Srobertol_fp delay_time;				/* delay time */
33754359Srobertochar currenthost[LENHOSTNAME];			/* current host name */
338280849Scyint currenthostisnum;				/* is prior text from IP? */
339280849Scystruct sockaddr_in hostaddr;			/* host address */
34054359Srobertoint showhostnames = 1;				/* show host names by default */
341280849Scyint wideremote = 0;				/* show wide remote names? */
34254359Sroberto
343132451Srobertoint ai_fam_templ;				/* address family */
344132451Srobertoint ai_fam_default;				/* default address family */
345132451SrobertoSOCKET sockfd;					/* fd socket is opened on */
34654359Srobertoint havehost = 0;				/* set to 1 when host open */
347132451Srobertoint s_port = 0;
34854359Srobertostruct servent *server_entry = NULL;		/* server entry for ntp */
34954359Sroberto
35054359Sroberto
35154359Sroberto/*
35254359Sroberto * Sequence number used for requests.  It is incremented before
35354359Sroberto * it is used.
35454359Sroberto */
35554359Srobertou_short sequence;
35654359Sroberto
35754359Sroberto/*
35854359Sroberto * Holds data returned from queries.  Declare buffer long to be sure of
35954359Sroberto * alignment.
36054359Sroberto */
36154359Sroberto#define	DATASIZE	(MAXFRAGS*480)	/* maximum amount of data */
36254359Srobertolong pktdata[DATASIZE/sizeof(long)];
36354359Sroberto
36454359Sroberto/*
365280849Scy * assoc_cache[] is a dynamic array which allows references to
366280849Scy * associations using &1 ... &N for n associations, avoiding manual
367280849Scy * lookup of the current association IDs for a given ntpd.  It also
368280849Scy * caches the status word for each association, retrieved incidentally.
36954359Sroberto */
370280849Scystruct association *	assoc_cache;
371280849Scyu_int assoc_cache_slots;/* count of allocated array entries */
372280849Scyu_int numassoc;		/* number of cached associations */
37354359Sroberto
37454359Sroberto/*
37554359Sroberto * For commands typed on the command line (with the -c option)
37654359Sroberto */
377316722Sdelphijsize_t numcmds = 0;
37854359Srobertoconst char *ccmds[MAXCMDS];
37954359Sroberto#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
38054359Sroberto
38154359Sroberto/*
38254359Sroberto * When multiple hosts are specified.
38354359Sroberto */
38454359Sroberto
385280849Scyu_int numhosts;
38654359Sroberto
387280849Scychost chosts[MAXHOSTS];
388280849Scy#define	ADDHOST(cp)						\
389280849Scy	do {							\
390280849Scy		if (numhosts < MAXHOSTS) {			\
391280849Scy			chosts[numhosts].name = (cp);		\
392280849Scy			chosts[numhosts].fam = ai_fam_templ;	\
393280849Scy			numhosts++;				\
394280849Scy		}						\
395280849Scy	} while (0)
396280849Scy
39754359Sroberto/*
39854359Sroberto * Macro definitions we use
39954359Sroberto */
40054359Sroberto#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
40154359Sroberto#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
40254359Sroberto#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
40354359Sroberto
40454359Sroberto/*
40554359Sroberto * Jump buffer for longjumping back to the command level
40654359Sroberto */
40754359Srobertojmp_buf interrupt_buf;
40854359Sroberto
40954359Sroberto/*
41054359Sroberto * Points at file being currently printed into
41154359Sroberto */
41254359SrobertoFILE *current_output;
41354359Sroberto
41454359Sroberto/*
41554359Sroberto * Command table imported from ntpdc_ops.c
41654359Sroberto */
41754359Srobertoextern struct xcmd opcmds[];
41854359Sroberto
419289764Sglebiuschar const *progname;
42054359Sroberto
42154359Sroberto#ifdef NO_MAIN_ALLOWED
422280849Scy#ifndef BUILD_AS_LIB
42354359SrobertoCALL(ntpq,"ntpq",ntpqmain);
42454359Sroberto
42554359Srobertovoid clear_globals(void)
42654359Sroberto{
427280849Scy	extern int ntp_optind;
428280849Scy	showhostnames = 0;	/* don'tshow host names by default */
429280849Scy	ntp_optind = 0;
430280849Scy	server_entry = NULL;	/* server entry for ntp */
431280849Scy	havehost = 0;		/* set to 1 when host open */
432280849Scy	numassoc = 0;		/* number of cached associations */
433280849Scy	numcmds = 0;
434280849Scy	numhosts = 0;
43554359Sroberto}
436280849Scy#endif /* !BUILD_AS_LIB */
437280849Scy#endif /* NO_MAIN_ALLOWED */
43854359Sroberto
43954359Sroberto/*
44054359Sroberto * main - parse arguments and handle options
44154359Sroberto */
44254359Sroberto#ifndef NO_MAIN_ALLOWED
44354359Srobertoint
44454359Srobertomain(
44554359Sroberto	int argc,
44654359Sroberto	char *argv[]
44754359Sroberto	)
44854359Sroberto{
44954359Sroberto	return ntpqmain(argc, argv);
45054359Sroberto}
45154359Sroberto#endif
45254359Sroberto
453280849Scy#ifndef BUILD_AS_LIB
45454359Srobertoint
45554359Srobertontpqmain(
45654359Sroberto	int argc,
45754359Sroberto	char *argv[]
45854359Sroberto	)
45954359Sroberto{
460280849Scy	u_int ihost;
461316722Sdelphij	size_t icmd;
46254359Sroberto
463280849Scy
464182007Sroberto#ifdef SYS_VXWORKS
465182007Sroberto	clear_globals();
466182007Sroberto	taskPrioritySet(taskIdSelf(), 100 );
46754359Sroberto#endif
468182007Sroberto
46954359Sroberto	delay_time.l_ui = 0;
47054359Sroberto	delay_time.l_uf = DEFDELAY;
47154359Sroberto
472280849Scy	init_lib();	/* sets up ipv4_works, ipv6_works */
473280849Scy	ssl_applink();
474280849Scy	init_auth();
475132451Sroberto
476280849Scy	/* Check to see if we have IPv6. Otherwise default to IPv4 */
477280849Scy	if (!ipv6_works)
478132451Sroberto		ai_fam_default = AF_INET;
479132451Sroberto
480285169Scy	/* Fixup keytype's help based on available digest names */
481285169Scy
482285169Scy	{
483285169Scy	    char *list;
484294554Sdelphij	    char *msg;
485285169Scy
486285169Scy	    list = list_digest_names();
487285169Scy	    for (icmd = 0; icmd < sizeof(builtins)/sizeof(builtins[0]); icmd++) {
488285169Scy		if (strcmp("keytype", builtins[icmd].keyword) == 0)
489285169Scy		    break;
490285169Scy	    }
491285169Scy
492285169Scy	    /* CID: 1295478 */
493285169Scy	    /* This should only "trip" if "keytype" is removed from builtins */
494285169Scy	    INSIST(icmd < sizeof(builtins)/sizeof(builtins[0]));
495285169Scy
496285169Scy#ifdef OPENSSL
497285169Scy	    builtins[icmd].desc[0] = "digest-name";
498294554Sdelphij	    my_easprintf(&msg,
499294554Sdelphij			 "set key type to use for authenticated requests, one of:%s",
500294554Sdelphij			 list);
501285169Scy#else
502285169Scy	    builtins[icmd].desc[0] = "md5";
503294554Sdelphij	    my_easprintf(&msg,
504294554Sdelphij			 "set key type to use for authenticated requests (%s)",
505294554Sdelphij			 list);
506285169Scy#endif
507285169Scy	    builtins[icmd].comment = msg;
508285169Scy	    free(list);
509285169Scy	}
510285169Scy
51154359Sroberto	progname = argv[0];
512182007Sroberto
513182007Sroberto	{
514280849Scy		int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
515182007Sroberto		argc -= optct;
516182007Sroberto		argv += optct;
517182007Sroberto	}
518182007Sroberto
519280849Scy	/*
520280849Scy	 * Process options other than -c and -p, which are specially
521280849Scy	 * handled by ntpq_custom_opt_handler().
522280849Scy	 */
523280849Scy
524280849Scy	debug = OPT_VALUE_SET_DEBUG_LEVEL;
525280849Scy
526280849Scy	if (HAVE_OPT(IPV4))
527182007Sroberto		ai_fam_templ = AF_INET;
528280849Scy	else if (HAVE_OPT(IPV6))
529182007Sroberto		ai_fam_templ = AF_INET6;
530280849Scy	else
531182007Sroberto		ai_fam_templ = ai_fam_default;
532182007Sroberto
533280849Scy	if (HAVE_OPT(INTERACTIVE))
534182007Sroberto		interactive = 1;
535182007Sroberto
536280849Scy	if (HAVE_OPT(NUMERIC))
537182007Sroberto		showhostnames = 0;
538182007Sroberto
539280849Scy	if (HAVE_OPT(WIDE))
540280849Scy		wideremote = 1;
541182007Sroberto
542280849Scy	old_rv = HAVE_OPT(OLD_RV);
543280849Scy
544298695Sdelphij	drefid = OPT_VALUE_REFID;
545298695Sdelphij
546280849Scy	if (0 == argc) {
54754359Sroberto		ADDHOST(DEFHOST);
54854359Sroberto	} else {
549280849Scy		for (ihost = 0; ihost < (u_int)argc; ihost++) {
550280849Scy			if ('-' == *argv[ihost]) {
551280849Scy				//
552280849Scy				// If I really cared I'd also check:
553280849Scy				// 0 == argv[ihost][2]
554280849Scy				//
555280849Scy				// and there are other cases as well...
556280849Scy				//
557280849Scy				if ('4' == argv[ihost][1]) {
558280849Scy					ai_fam_templ = AF_INET;
559280849Scy					continue;
560280849Scy				} else if ('6' == argv[ihost][1]) {
561280849Scy					ai_fam_templ = AF_INET6;
562280849Scy					continue;
563280849Scy				} else {
564280849Scy					// XXX Throw a usage error
565280849Scy				}
566280849Scy			}
567280849Scy			ADDHOST(argv[ihost]);
568280849Scy		}
56954359Sroberto	}
57054359Sroberto
57154359Sroberto	if (numcmds == 0 && interactive == 0
57254359Sroberto	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
57354359Sroberto		interactive = 1;
57454359Sroberto	}
57554359Sroberto
576293423Sdelphij	set_ctrl_c_hook(on_ctrlc);
57754359Sroberto#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
57854359Sroberto	if (interactive)
579293423Sdelphij		push_ctrl_c_handler(abortcmd);
58054359Sroberto#endif /* SYS_WINNT */
58154359Sroberto
58254359Sroberto	if (numcmds == 0) {
583280849Scy		(void) openhost(chosts[0].name, chosts[0].fam);
58454359Sroberto		getcmds();
58554359Sroberto	} else {
58654359Sroberto		for (ihost = 0; ihost < numhosts; ihost++) {
587280849Scy			if (openhost(chosts[ihost].name, chosts[ihost].fam))
588280849Scy				for (icmd = 0; icmd < numcmds; icmd++)
589280849Scy					docmd(ccmds[icmd]);
59054359Sroberto		}
59154359Sroberto	}
59254359Sroberto#ifdef SYS_WINNT
59354359Sroberto	WSACleanup();
59454359Sroberto#endif /* SYS_WINNT */
59554359Sroberto	return 0;
59654359Sroberto}
597280849Scy#endif /* !BUILD_AS_LIB */
59854359Sroberto
59954359Sroberto/*
60054359Sroberto * openhost - open a socket to a host
60154359Sroberto */
602280849Scystatic	int
60354359Srobertoopenhost(
604280849Scy	const char *hname,
605280849Scy	int	    fam
60654359Sroberto	)
60754359Sroberto{
608280849Scy	const char svc[] = "ntp";
60954359Sroberto	char temphost[LENHOSTNAME];
610132451Sroberto	int a_info, i;
611280849Scy	struct addrinfo hints, *ai;
612280849Scy	sockaddr_u addr;
613280849Scy	size_t octets;
614132451Sroberto	register const char *cp;
615132451Sroberto	char name[LENHOSTNAME];
61654359Sroberto
617132451Sroberto	/*
618132451Sroberto	 * We need to get by the [] if they were entered
619132451Sroberto	 */
620280849Scy
621132451Sroberto	cp = hname;
622280849Scy
623280849Scy	if (*cp == '[') {
624132451Sroberto		cp++;
625280849Scy		for (i = 0; *cp && *cp != ']'; cp++, i++)
626132451Sroberto			name[i] = *cp;
627280849Scy		if (*cp == ']') {
628280849Scy			name[i] = '\0';
629280849Scy			hname = name;
630280849Scy		} else {
631280849Scy			return 0;
632280849Scy		}
63354359Sroberto	}
63454359Sroberto
635132451Sroberto	/*
636132451Sroberto	 * First try to resolve it as an ip address and if that fails,
637132451Sroberto	 * do a fullblown (dns) lookup. That way we only use the dns
638132451Sroberto	 * when it is needed and work around some implementations that
639132451Sroberto	 * will return an "IPv4-mapped IPv6 address" address if you
640132451Sroberto	 * give it an IPv4 address to lookup.
641132451Sroberto	 */
642280849Scy	ZERO(hints);
643280849Scy	hints.ai_family = fam;
644132451Sroberto	hints.ai_protocol = IPPROTO_UDP;
645132451Sroberto	hints.ai_socktype = SOCK_DGRAM;
646280849Scy	hints.ai_flags = Z_AI_NUMERICHOST;
647280849Scy	ai = NULL;
648132451Sroberto
649280849Scy	a_info = getaddrinfo(hname, svc, &hints, &ai);
650182007Sroberto	if (a_info == EAI_NONAME
651182007Sroberto#ifdef EAI_NODATA
652182007Sroberto	    || a_info == EAI_NODATA
653182007Sroberto#endif
654182007Sroberto	   ) {
655132451Sroberto		hints.ai_flags = AI_CANONNAME;
656132451Sroberto#ifdef AI_ADDRCONFIG
657132451Sroberto		hints.ai_flags |= AI_ADDRCONFIG;
658132451Sroberto#endif
659280849Scy		a_info = getaddrinfo(hname, svc, &hints, &ai);
660132451Sroberto	}
661280849Scy#ifdef AI_ADDRCONFIG
662132451Sroberto	/* Some older implementations don't like AI_ADDRCONFIG. */
663132451Sroberto	if (a_info == EAI_BADFLAGS) {
664280849Scy		hints.ai_flags &= ~AI_ADDRCONFIG;
665280849Scy		a_info = getaddrinfo(hname, svc, &hints, &ai);
666132451Sroberto	}
667280849Scy#endif
668132451Sroberto	if (a_info != 0) {
669280849Scy		fprintf(stderr, "%s\n", gai_strerror(a_info));
670132451Sroberto		return 0;
671132451Sroberto	}
672132451Sroberto
673280849Scy	INSIST(ai != NULL);
674280849Scy	ZERO(addr);
675280849Scy	octets = min(sizeof(addr), ai->ai_addrlen);
676280849Scy	memcpy(&addr, ai->ai_addr, octets);
677280849Scy
678132451Sroberto	if (ai->ai_canonname == NULL) {
679280849Scy		strlcpy(temphost, stoa(&addr), sizeof(temphost));
680280849Scy		currenthostisnum = TRUE;
681132451Sroberto	} else {
682280849Scy		strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
683280849Scy		currenthostisnum = FALSE;
684132451Sroberto	}
685132451Sroberto
68654359Sroberto	if (debug > 2)
687280849Scy		printf("Opening host %s (%s)\n",
688280849Scy			temphost,
689280849Scy			(ai->ai_family == AF_INET)
690280849Scy			? "AF_INET"
691280849Scy			: (ai->ai_family == AF_INET6)
692280849Scy			  ? "AF_INET6"
693280849Scy			  : "AF-???"
694280849Scy			);
69554359Sroberto
69654359Sroberto	if (havehost == 1) {
69754359Sroberto		if (debug > 2)
698280849Scy			printf("Closing old host %s\n", currenthost);
699280849Scy		closesocket(sockfd);
70054359Sroberto		havehost = 0;
70154359Sroberto	}
702280849Scy	strlcpy(currenthost, temphost, sizeof(currenthost));
70354359Sroberto
704132451Sroberto	/* port maps to the same location in both families */
705280849Scy	s_port = NSRCPORT(&addr);
706132451Sroberto#ifdef SYS_VXWORKS
707132451Sroberto	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
708132451Sroberto	if (ai->ai_family == AF_INET)
709132451Sroberto		*(struct sockaddr_in *)&hostaddr=
710132451Sroberto			*((struct sockaddr_in *)ai->ai_addr);
711132451Sroberto	else
712132451Sroberto		*(struct sockaddr_in6 *)&hostaddr=
713132451Sroberto			*((struct sockaddr_in6 *)ai->ai_addr);
714132451Sroberto#endif /* SYS_VXWORKS */
71554359Sroberto
71654359Sroberto#ifdef SYS_WINNT
71754359Sroberto	{
71854359Sroberto		int optionValue = SO_SYNCHRONOUS_NONALERT;
71954359Sroberto		int err;
720182007Sroberto
721280849Scy		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
722280849Scy				 (char *)&optionValue, sizeof(optionValue));
723280849Scy		if (err) {
724280849Scy			mfprintf(stderr,
725280849Scy				 "setsockopt(SO_SYNCHRONOUS_NONALERT)"
726280849Scy				 " error: %m\n");
727280849Scy			freeaddrinfo(ai);
72854359Sroberto			exit(1);
72954359Sroberto		}
73054359Sroberto	}
731132451Sroberto#endif /* SYS_WINNT */
73254359Sroberto
733280849Scy	sockfd = socket(ai->ai_family, ai->ai_socktype,
734280849Scy			ai->ai_protocol);
73554359Sroberto	if (sockfd == INVALID_SOCKET) {
736280849Scy		error("socket");
737280849Scy		freeaddrinfo(ai);
738280849Scy		return 0;
73954359Sroberto	}
74054359Sroberto
741280849Scy
74254359Sroberto#ifdef NEED_RCVBUF_SLOP
74354359Sroberto# ifdef SO_RCVBUF
74454359Sroberto	{ int rbufsize = DATASIZE + 2048;	/* 2K for slop */
74554359Sroberto	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
74654359Sroberto		       &rbufsize, sizeof(int)) == -1)
747280849Scy		error("setsockopt");
74854359Sroberto	}
74954359Sroberto# endif
75054359Sroberto#endif
75154359Sroberto
752280849Scy	if
753132451Sroberto#ifdef SYS_VXWORKS
754280849Scy	   (connect(sockfd, (struct sockaddr *)&hostaddr,
75554359Sroberto		    sizeof(hostaddr)) == -1)
756132451Sroberto#else
757280849Scy	   (connect(sockfd, (struct sockaddr *)ai->ai_addr,
758293423Sdelphij		ai->ai_addrlen) == -1)
759132451Sroberto#endif /* SYS_VXWORKS */
760293423Sdelphij	{
761280849Scy		error("connect");
762132451Sroberto		freeaddrinfo(ai);
763280849Scy		return 0;
764280849Scy	}
765280849Scy	freeaddrinfo(ai);
76654359Sroberto	havehost = 1;
767280849Scy	numassoc = 0;
768280849Scy
76954359Sroberto	return 1;
77054359Sroberto}
77154359Sroberto
77254359Sroberto
773280849Scystatic void
774280849Scydump_hex_printable(
775280849Scy	const void *	data,
776280849Scy	size_t		len
777280849Scy	)
778280849Scy{
779316722Sdelphij	/* every line shows at most 16 bytes, so we need a buffer of
780316722Sdelphij	 *   4 * 16 (2 xdigits, 1 char, one sep for xdigits)
781316722Sdelphij	 * + 2 * 1  (block separators)
782316722Sdelphij	 * + <LF> + <NUL>
783316722Sdelphij	 *---------------
784316722Sdelphij	 *  68 bytes
785316722Sdelphij	 */
786316722Sdelphij	static const char s_xdig[16] = "0123456789ABCDEF";
787280849Scy
788316722Sdelphij	char lbuf[68];
789316722Sdelphij	int  ch, rowlen;
790316722Sdelphij	const u_char * cdata = data;
791316722Sdelphij	char *xptr, *pptr;
792316722Sdelphij
793316722Sdelphij	while (len) {
794316722Sdelphij		memset(lbuf, ' ', sizeof(lbuf));
795316722Sdelphij		xptr = lbuf;
796316722Sdelphij		pptr = lbuf + 3*16 + 2;
797316722Sdelphij
798316722Sdelphij		rowlen = (len > 16) ? 16 : (int)len;
799280849Scy		len -= rowlen;
800316722Sdelphij
801316722Sdelphij		do {
802316722Sdelphij			ch = *cdata++;
803316722Sdelphij
804316722Sdelphij			*xptr++ = s_xdig[ch >> 4  ];
805316722Sdelphij			*xptr++ = s_xdig[ch & 0x0F];
806316722Sdelphij			if (++xptr == lbuf + 3*8)
807316722Sdelphij				++xptr;
808316722Sdelphij
809316722Sdelphij			*pptr++ = isprint(ch) ? (char)ch : '.';
810316722Sdelphij		} while (--rowlen);
811316722Sdelphij
812316722Sdelphij		*pptr++ = '\n';
813316722Sdelphij		*pptr   = '\0';
814316722Sdelphij		fputs(lbuf, stdout);
815280849Scy	}
816280849Scy}
817280849Scy
818280849Scy
81954359Sroberto/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
82054359Sroberto/*
82154359Sroberto * sendpkt - send a packet to the remote host
82254359Sroberto */
82354359Srobertostatic int
82454359Srobertosendpkt(
825280849Scy	void *	xdata,
826280849Scy	size_t	xdatalen
82754359Sroberto	)
82854359Sroberto{
82954359Sroberto	if (debug >= 3)
830280849Scy		printf("Sending %zu octets\n", xdatalen);
83154359Sroberto
832293423Sdelphij	if (send(sockfd, xdata, xdatalen, 0) == -1) {
833280849Scy		warning("write to %s failed", currenthost);
83454359Sroberto		return -1;
83554359Sroberto	}
83654359Sroberto
83754359Sroberto	if (debug >= 4) {
838280849Scy		printf("Request packet:\n");
839280849Scy		dump_hex_printable(xdata, xdatalen);
84054359Sroberto	}
84154359Sroberto	return 0;
84254359Sroberto}
84354359Sroberto
84454359Sroberto/*
84554359Sroberto * getresponse - get a (series of) response packet(s) and return the data
84654359Sroberto */
84754359Srobertostatic int
84854359Srobertogetresponse(
84954359Sroberto	int opcode,
85054359Sroberto	int associd,
85154359Sroberto	u_short *rstatus,
852293423Sdelphij	size_t *rsize,
853280849Scy	const char **rdata,
85454359Sroberto	int timeo
85554359Sroberto	)
85654359Sroberto{
85754359Sroberto	struct ntp_control rpkt;
858280849Scy	struct sock_timeval tvo;
85954359Sroberto	u_short offsets[MAXFRAGS+1];
86054359Sroberto	u_short counts[MAXFRAGS+1];
86154359Sroberto	u_short offset;
86254359Sroberto	u_short count;
863280849Scy	size_t numfrags;
864280849Scy	size_t f;
865280849Scy	size_t ff;
86654359Sroberto	int seenlastfrag;
867280849Scy	int shouldbesize;
86854359Sroberto	fd_set fds;
86954359Sroberto	int n;
870280849Scy	int errcode;
871294554Sdelphij	/* absolute timeout checks. Not 'time_t' by intention! */
872294554Sdelphij	uint32_t tobase;	/* base value for timeout */
873294554Sdelphij	uint32_t tospan;	/* timeout span (max delay) */
874294554Sdelphij	uint32_t todiff;	/* current delay */
87554359Sroberto
876316722Sdelphij	memset(offsets, 0, sizeof(offsets));
877316722Sdelphij	memset(counts , 0, sizeof(counts ));
878316722Sdelphij
87954359Sroberto	/*
88054359Sroberto	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
88154359Sroberto	 * back in response to the request.  We peel the data out of
88254359Sroberto	 * each packet and collect it in one long block.  When the last
88354359Sroberto	 * packet in the sequence is received we'll know how much data we
88454359Sroberto	 * should have had.  Note we use one long time out, should reconsider.
88554359Sroberto	 */
88654359Sroberto	*rsize = 0;
88754359Sroberto	if (rstatus)
888280849Scy		*rstatus = 0;
88954359Sroberto	*rdata = (char *)pktdata;
89054359Sroberto
89154359Sroberto	numfrags = 0;
89254359Sroberto	seenlastfrag = 0;
89354359Sroberto
894294554Sdelphij	tobase = (uint32_t)time(NULL);
895294554Sdelphij
89654359Sroberto	FD_ZERO(&fds);
89754359Sroberto
898280849Scy	/*
899280849Scy	 * Loop until we have an error or a complete response.  Nearly all
900280849Scy	 * code paths to loop again use continue.
901280849Scy	 */
902280849Scy	for (;;) {
90354359Sroberto
904280849Scy		if (numfrags == 0)
905280849Scy			tvo = tvout;
906280849Scy		else
907280849Scy			tvo = tvsout;
908294554Sdelphij		tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
90954359Sroberto
910280849Scy		FD_SET(sockfd, &fds);
911293423Sdelphij		n = select(sockfd+1, &fds, NULL, NULL, &tvo);
912280849Scy		if (n == -1) {
913294554Sdelphij#if !defined(SYS_WINNT) && defined(EINTR)
914294554Sdelphij			/* Windows does not know about EINTR (until very
915294554Sdelphij			 * recently) and the handling of console events
916294554Sdelphij			 * is *very* different from POSIX/UNIX signal
917294554Sdelphij			 * handling anyway.
918294554Sdelphij			 *
919294554Sdelphij			 * Under non-windows targets we map EINTR as
920294554Sdelphij			 * 'last packet was received' and try to exit
921294554Sdelphij			 * the receive sequence.
922294554Sdelphij			 */
923294554Sdelphij			if (errno == EINTR) {
924294554Sdelphij				seenlastfrag = 1;
925294554Sdelphij				goto maybe_final;
926294554Sdelphij			}
927294554Sdelphij#endif
928280849Scy			warning("select fails");
929280849Scy			return -1;
930280849Scy		}
931294554Sdelphij
932294554Sdelphij		/*
933294554Sdelphij		 * Check if this is already too late. Trash the data and
934294554Sdelphij		 * fake a timeout if this is so.
935294554Sdelphij		 */
936294554Sdelphij		todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
937294554Sdelphij		if ((n > 0) && (todiff > tospan)) {
938294554Sdelphij			n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
939316722Sdelphij			n -= n; /* faked timeout return from 'select()',
940316722Sdelphij				 * execute RMW cycle on 'n'
941316722Sdelphij				 */
942294554Sdelphij		}
943294554Sdelphij
944316722Sdelphij		if (n <= 0) {
945280849Scy			/*
946280849Scy			 * Timed out.  Return what we have
947280849Scy			 */
948280849Scy			if (numfrags == 0) {
949280849Scy				if (timeo)
950280849Scy					fprintf(stderr,
951280849Scy						"%s: timed out, nothing received\n",
952280849Scy						currenthost);
953280849Scy				return ERR_TIMEOUT;
954280849Scy			}
95554359Sroberto			if (timeo)
956280849Scy				fprintf(stderr,
957280849Scy					"%s: timed out with incomplete data\n",
958280849Scy					currenthost);
95954359Sroberto			if (debug) {
960280849Scy				fprintf(stderr,
961280849Scy					"ERR_INCOMPLETE: Received fragments:\n");
962280849Scy				for (f = 0; f < numfrags; f++)
963280849Scy					fprintf(stderr,
964280849Scy						"%2u: %5d %5d\t%3d octets\n",
965280849Scy						(u_int)f, offsets[f],
966280849Scy						offsets[f] +
967280849Scy						counts[f],
968280849Scy						counts[f]);
969280849Scy				fprintf(stderr,
970280849Scy					"last fragment %sreceived\n",
971280849Scy					(seenlastfrag)
972280849Scy					    ? ""
973280849Scy					    : "not ");
97454359Sroberto			}
97554359Sroberto			return ERR_INCOMPLETE;
97654359Sroberto		}
97754359Sroberto
978280849Scy		n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
979316722Sdelphij		if (n < 0) {
980280849Scy			warning("read");
981280849Scy			return -1;
982280849Scy		}
98354359Sroberto
984280849Scy		if (debug >= 4) {
985280849Scy			printf("Response packet:\n");
986280849Scy			dump_hex_printable(&rpkt, n);
987280849Scy		}
98854359Sroberto
989280849Scy		/*
990280849Scy		 * Check for format errors.  Bug proofing.
991280849Scy		 */
992280849Scy		if (n < (int)CTL_HEADER_LEN) {
993280849Scy			if (debug)
994280849Scy				printf("Short (%d byte) packet received\n", n);
995280849Scy			continue;
99654359Sroberto		}
997280849Scy		if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
998280849Scy		    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
999280849Scy			if (debug)
1000280849Scy				printf("Packet received with version %d\n",
1001280849Scy				       PKT_VERSION(rpkt.li_vn_mode));
1002280849Scy			continue;
1003280849Scy		}
1004280849Scy		if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
1005280849Scy			if (debug)
1006280849Scy				printf("Packet received with mode %d\n",
1007280849Scy				       PKT_MODE(rpkt.li_vn_mode));
1008280849Scy			continue;
1009280849Scy		}
1010280849Scy		if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
1011280849Scy			if (debug)
1012280849Scy				printf("Received request packet, wanted response\n");
1013280849Scy			continue;
1014280849Scy		}
101554359Sroberto
1016280849Scy		/*
1017280849Scy		 * Check opcode and sequence number for a match.
1018280849Scy		 * Could be old data getting to us.
1019280849Scy		 */
1020280849Scy		if (ntohs(rpkt.sequence) != sequence) {
1021280849Scy			if (debug)
1022280849Scy				printf("Received sequnce number %d, wanted %d\n",
1023280849Scy				       ntohs(rpkt.sequence), sequence);
1024280849Scy			continue;
1025280849Scy		}
1026280849Scy		if (CTL_OP(rpkt.r_m_e_op) != opcode) {
1027280849Scy			if (debug)
1028280849Scy			    printf(
1029280849Scy				    "Received opcode %d, wanted %d (sequence number okay)\n",
1030280849Scy				    CTL_OP(rpkt.r_m_e_op), opcode);
1031280849Scy			continue;
1032280849Scy		}
103354359Sroberto
1034280849Scy		/*
1035280849Scy		 * Check the error code.  If non-zero, return it.
1036280849Scy		 */
1037280849Scy		if (CTL_ISERROR(rpkt.r_m_e_op)) {
1038280849Scy			errcode = (ntohs(rpkt.status) >> 8) & 0xff;
1039280849Scy			if (CTL_ISMORE(rpkt.r_m_e_op))
1040280849Scy				TRACE(1, ("Error code %d received on not-final packet\n",
1041280849Scy					  errcode));
1042280849Scy			if (errcode == CERR_UNSPEC)
1043280849Scy				return ERR_UNSPEC;
1044280849Scy			return errcode;
104554359Sroberto		}
104654359Sroberto
104754359Sroberto		/*
1048280849Scy		 * Check the association ID to make sure it matches what
1049280849Scy		 * we sent.
105054359Sroberto		 */
1051280849Scy		if (ntohs(rpkt.associd) != associd) {
1052280849Scy			TRACE(1, ("Association ID %d doesn't match expected %d\n",
1053280849Scy				  ntohs(rpkt.associd), associd));
1054280849Scy			/*
1055280849Scy			 * Hack for silly fuzzballs which, at the time of writing,
1056280849Scy			 * return an assID of sys.peer when queried for system variables.
1057280849Scy			 */
105854359Sroberto#ifdef notdef
1059280849Scy			continue;
106054359Sroberto#endif
1061280849Scy		}
106254359Sroberto
1063280849Scy		/*
1064280849Scy		 * Collect offset and count.  Make sure they make sense.
1065280849Scy		 */
1066280849Scy		offset = ntohs(rpkt.offset);
1067280849Scy		count = ntohs(rpkt.count);
106854359Sroberto
106954359Sroberto		/*
1070280849Scy		 * validate received payload size is padded to next 32-bit
1071280849Scy		 * boundary and no smaller than claimed by rpkt.count
107254359Sroberto		 */
1073280849Scy		if (n & 0x3) {
1074280849Scy			TRACE(1, ("Response packet not padded, size = %d\n",
1075280849Scy				  n));
1076280849Scy			continue;
1077280849Scy		}
107854359Sroberto
1079280849Scy		shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
108054359Sroberto
1081280849Scy		if (n < shouldbesize) {
1082280849Scy			printf("Response packet claims %u octets payload, above %ld received\n",
1083301247Sdelphij			       count, (long)(n - CTL_HEADER_LEN));
1084280849Scy			return ERR_INCOMPLETE;
1085280849Scy		}
1086280849Scy
1087280849Scy		if (debug >= 3 && shouldbesize > n) {
1088280849Scy			u_int32 key;
1089280849Scy			u_int32 *lpkt;
1090280849Scy			int maclen;
1091280849Scy
1092280849Scy			/*
1093280849Scy			 * Usually we ignore authentication, but for debugging purposes
1094280849Scy			 * we watch it here.
1095280849Scy			 */
1096280849Scy			/* round to 8 octet boundary */
1097280849Scy			shouldbesize = (shouldbesize + 7) & ~7;
1098280849Scy
1099280849Scy			maclen = n - shouldbesize;
1100280849Scy			if (maclen >= (int)MIN_MAC_LEN) {
1101280849Scy				printf(
1102280849Scy					"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1103280849Scy					n, shouldbesize, maclen);
1104280849Scy				lpkt = (u_int32 *)&rpkt;
1105280849Scy				printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1106280849Scy				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1107280849Scy				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1108280849Scy				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1109280849Scy				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1110280849Scy				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1111280849Scy				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1112280849Scy				key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1113280849Scy				printf("Authenticated with keyid %lu\n", (u_long)key);
1114280849Scy				if (key != 0 && key != info_auth_keyid) {
1115280849Scy					printf("We don't know that key\n");
111654359Sroberto				} else {
1117280849Scy					if (authdecrypt(key, (u_int32 *)&rpkt,
1118280849Scy					    n - maclen, maclen)) {
1119280849Scy						printf("Auth okay!\n");
1120280849Scy					} else {
1121280849Scy						printf("Auth failed!\n");
1122280849Scy					}
112354359Sroberto				}
112454359Sroberto			}
112554359Sroberto		}
112654359Sroberto
1127280849Scy		TRACE(2, ("Got packet, size = %d\n", n));
1128280849Scy		if (count > (n - CTL_HEADER_LEN)) {
1129280849Scy			TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
1130280849Scy				  count, (long)n - CTL_HEADER_LEN));
1131280849Scy			continue;
1132280849Scy		}
1133280849Scy		if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1134280849Scy			TRACE(1, ("Received count of 0 in non-final fragment\n"));
1135280849Scy			continue;
1136280849Scy		}
1137280849Scy		if (offset + count > sizeof(pktdata)) {
1138280849Scy			TRACE(1, ("Offset %u, count %u, too big for buffer\n",
1139280849Scy				  offset, count));
1140280849Scy			return ERR_TOOMUCH;
1141280849Scy		}
1142280849Scy		if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1143280849Scy			TRACE(1, ("Received second last fragment packet\n"));
1144280849Scy			continue;
1145280849Scy		}
114654359Sroberto
1147280849Scy		/*
1148280849Scy		 * So far, so good.  Record this fragment, making sure it doesn't
1149280849Scy		 * overlap anything.
1150280849Scy		 */
1151280849Scy		TRACE(2, ("Packet okay\n"));
115254359Sroberto
1153280849Scy		if (numfrags > (MAXFRAGS - 1)) {
1154280849Scy			TRACE(2, ("Number of fragments exceeds maximum %d\n",
1155280849Scy				  MAXFRAGS - 1));
1156280849Scy			return ERR_TOOMUCH;
115754359Sroberto		}
115854359Sroberto
1159280849Scy		/*
1160280849Scy		 * Find the position for the fragment relative to any
1161280849Scy		 * previously received.
1162280849Scy		 */
1163280849Scy		for (f = 0;
1164280849Scy		     f < numfrags && offsets[f] < offset;
1165280849Scy		     f++) {
1166280849Scy			/* empty body */ ;
1167280849Scy		}
116854359Sroberto
1169280849Scy		if (f < numfrags && offset == offsets[f]) {
1170280849Scy			TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
1171280849Scy				  count, offset, counts[f], offsets[f]));
1172280849Scy			continue;
1173280849Scy		}
117454359Sroberto
1175280849Scy		if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
1176280849Scy			TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
1177280849Scy				  offset, counts[f-1], offsets[f-1]));
1178280849Scy			continue;
117954359Sroberto		}
1180280849Scy
1181280849Scy		if (f < numfrags && (offset + count) > offsets[f]) {
1182280849Scy			TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
1183280849Scy				  count, offset, offsets[f]));
1184280849Scy			continue;
118554359Sroberto		}
118654359Sroberto
1187280849Scy		for (ff = numfrags; ff > f; ff--) {
1188280849Scy			offsets[ff] = offsets[ff-1];
1189280849Scy			counts[ff] = counts[ff-1];
1190280849Scy		}
1191280849Scy		offsets[f] = offset;
1192280849Scy		counts[f] = count;
1193280849Scy		numfrags++;
119454359Sroberto
1195280849Scy		/*
1196280849Scy		 * Got that stuffed in right.  Figure out if this was the last.
1197280849Scy		 * Record status info out of the last packet.
1198280849Scy		 */
1199280849Scy		if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1200280849Scy			seenlastfrag = 1;
1201280849Scy			if (rstatus != 0)
1202280849Scy				*rstatus = ntohs(rpkt.status);
1203280849Scy		}
120454359Sroberto
1205280849Scy		/*
1206294554Sdelphij		 * Copy the data into the data buffer, and bump the
1207294554Sdelphij		 * timout base in case we need more.
1208280849Scy		 */
1209280849Scy		memcpy((char *)pktdata + offset, &rpkt.u, count);
1210294554Sdelphij		tobase = (uint32_t)time(NULL);
1211294554Sdelphij
1212280849Scy		/*
1213280849Scy		 * If we've seen the last fragment, look for holes in the sequence.
1214280849Scy		 * If there aren't any, we're done.
1215280849Scy		 */
1216301247Sdelphij#if !defined(SYS_WINNT) && defined(EINTR)
1217301247Sdelphij		maybe_final:
1218301247Sdelphij#endif
1219301247Sdelphij
1220280849Scy		if (seenlastfrag && offsets[0] == 0) {
1221280849Scy			for (f = 1; f < numfrags; f++)
1222280849Scy				if (offsets[f-1] + counts[f-1] !=
1223280849Scy				    offsets[f])
1224280849Scy					break;
1225280849Scy			if (f == numfrags) {
1226280849Scy				*rsize = offsets[f-1] + counts[f-1];
1227280849Scy				TRACE(1, ("%lu packets reassembled into response\n",
1228280849Scy					  (u_long)numfrags));
1229280849Scy				return 0;
1230280849Scy			}
1231280849Scy		}
1232280849Scy	}  /* giant for (;;) collecting response packets */
1233280849Scy}  /* getresponse() */
1234280849Scy
1235280849Scy
123654359Sroberto/*
123754359Sroberto * sendrequest - format and send a request packet
123854359Sroberto */
123954359Srobertostatic int
124054359Srobertosendrequest(
124154359Sroberto	int opcode,
1242280849Scy	associd_t associd,
124354359Sroberto	int auth,
1244293423Sdelphij	size_t qsize,
1245280849Scy	const char *qdata
124654359Sroberto	)
124754359Sroberto{
124854359Sroberto	struct ntp_control qpkt;
1249293423Sdelphij	size_t	pktsize;
1250280849Scy	u_long	key_id;
1251280849Scy	char *	pass;
1252293423Sdelphij	size_t	maclen;
125354359Sroberto
125454359Sroberto	/*
125554359Sroberto	 * Check to make sure the data will fit in one packet
125654359Sroberto	 */
125754359Sroberto	if (qsize > CTL_MAX_DATA_LEN) {
1258280849Scy		fprintf(stderr,
1259293423Sdelphij			"***Internal error!  qsize (%zu) too large\n",
1260280849Scy			qsize);
126154359Sroberto		return 1;
126254359Sroberto	}
126354359Sroberto
126454359Sroberto	/*
126554359Sroberto	 * Fill in the packet
126654359Sroberto	 */
126754359Sroberto	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1268132451Sroberto	qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
126954359Sroberto	qpkt.sequence = htons(sequence);
127054359Sroberto	qpkt.status = 0;
127154359Sroberto	qpkt.associd = htons((u_short)associd);
127254359Sroberto	qpkt.offset = 0;
127354359Sroberto	qpkt.count = htons((u_short)qsize);
127454359Sroberto
1275280849Scy	pktsize = CTL_HEADER_LEN;
1276280849Scy
127754359Sroberto	/*
1278280849Scy	 * If we have data, copy and pad it out to a 32-bit boundary.
127954359Sroberto	 */
128054359Sroberto	if (qsize > 0) {
1281280849Scy		memcpy(&qpkt.u, qdata, (size_t)qsize);
1282280849Scy		pktsize += qsize;
1283280849Scy		while (pktsize & (sizeof(u_int32) - 1)) {
1284280849Scy			qpkt.u.data[qsize++] = 0;
128554359Sroberto			pktsize++;
128654359Sroberto		}
128754359Sroberto	}
128854359Sroberto
128954359Sroberto	/*
129054359Sroberto	 * If it isn't authenticated we can just send it.  Otherwise
129154359Sroberto	 * we're going to have to think about it a little.
129254359Sroberto	 */
129354359Sroberto	if (!auth && !always_auth) {
1294280849Scy		return sendpkt(&qpkt, pktsize);
1295280849Scy	}
129654359Sroberto
1297280849Scy	/*
1298280849Scy	 * Pad out packet to a multiple of 8 octets to be sure
1299280849Scy	 * receiver can handle it.
1300280849Scy	 */
1301280849Scy	while (pktsize & 7) {
1302280849Scy		qpkt.u.data[qsize++] = 0;
1303280849Scy		pktsize++;
1304280849Scy	}
130554359Sroberto
1306280849Scy	/*
1307280849Scy	 * Get the keyid and the password if we don't have one.
1308280849Scy	 */
1309280849Scy	if (info_auth_keyid == 0) {
1310280849Scy		key_id = getkeyid("Keyid: ");
1311280849Scy		if (key_id == 0 || key_id > NTP_MAXKEY) {
1312280849Scy			fprintf(stderr,
1313280849Scy				"Invalid key identifier\n");
1314280849Scy			return 1;
131554359Sroberto		}
1316280849Scy		info_auth_keyid = key_id;
1317280849Scy	}
1318280849Scy	if (!authistrusted(info_auth_keyid)) {
1319280849Scy		pass = getpass_keytype(info_auth_keytype);
1320280849Scy		if ('\0' == pass[0]) {
1321280849Scy			fprintf(stderr, "Invalid password\n");
1322280849Scy			return 1;
132354359Sroberto		}
1324280849Scy		authusekey(info_auth_keyid, info_auth_keytype,
1325280849Scy			   (u_char *)pass);
132654359Sroberto		authtrust(info_auth_keyid, 1);
1327280849Scy	}
132854359Sroberto
1329280849Scy	/*
1330280849Scy	 * Do the encryption.
1331280849Scy	 */
1332280849Scy	maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1333280849Scy	if (!maclen) {
1334280849Scy		fprintf(stderr, "Key not found\n");
1335280849Scy		return 1;
1336280849Scy	} else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1337280849Scy		fprintf(stderr,
1338293423Sdelphij			"%zu octet MAC, %zu expected with %zu octet digest\n",
1339280849Scy			maclen, (info_auth_hashlen + sizeof(keyid_t)),
1340280849Scy			info_auth_hashlen);
1341280849Scy		return 1;
134254359Sroberto	}
1343280849Scy
1344280849Scy	return sendpkt((char *)&qpkt, pktsize + maclen);
134554359Sroberto}
134654359Sroberto
134754359Sroberto
134854359Sroberto/*
1349280849Scy * show_error_msg - display the error text for a mode 6 error response.
135054359Sroberto */
1351280849Scyvoid
1352280849Scyshow_error_msg(
1353280849Scy	int		m6resp,
1354280849Scy	associd_t	associd
1355280849Scy	)
1356280849Scy{
1357280849Scy	if (numhosts > 1)
1358280849Scy		fprintf(stderr, "server=%s ", currenthost);
1359280849Scy
1360298695Sdelphij	switch (m6resp) {
1361280849Scy
1362280849Scy	case CERR_BADFMT:
1363280849Scy		fprintf(stderr,
1364280849Scy		    "***Server reports a bad format request packet\n");
1365280849Scy		break;
1366280849Scy
1367280849Scy	case CERR_PERMISSION:
1368280849Scy		fprintf(stderr,
1369280849Scy		    "***Server disallowed request (authentication?)\n");
1370280849Scy		break;
1371280849Scy
1372280849Scy	case CERR_BADOP:
1373280849Scy		fprintf(stderr,
1374280849Scy		    "***Server reports a bad opcode in request\n");
1375280849Scy		break;
1376280849Scy
1377280849Scy	case CERR_BADASSOC:
1378280849Scy		fprintf(stderr,
1379280849Scy		    "***Association ID %d unknown to server\n",
1380280849Scy		    associd);
1381280849Scy		break;
1382280849Scy
1383280849Scy	case CERR_UNKNOWNVAR:
1384280849Scy		fprintf(stderr,
1385280849Scy		    "***A request variable unknown to the server\n");
1386280849Scy		break;
1387280849Scy
1388280849Scy	case CERR_BADVALUE:
1389280849Scy		fprintf(stderr,
1390280849Scy		    "***Server indicates a request variable was bad\n");
1391280849Scy		break;
1392280849Scy
1393280849Scy	case ERR_UNSPEC:
1394280849Scy		fprintf(stderr,
1395280849Scy		    "***Server returned an unspecified error\n");
1396280849Scy		break;
1397280849Scy
1398280849Scy	case ERR_TIMEOUT:
1399280849Scy		fprintf(stderr, "***Request timed out\n");
1400280849Scy		break;
1401280849Scy
1402280849Scy	case ERR_INCOMPLETE:
1403280849Scy		fprintf(stderr,
1404280849Scy		    "***Response from server was incomplete\n");
1405280849Scy		break;
1406280849Scy
1407280849Scy	case ERR_TOOMUCH:
1408280849Scy		fprintf(stderr,
1409280849Scy		    "***Buffer size exceeded for returned data\n");
1410280849Scy		break;
1411280849Scy
1412280849Scy	default:
1413280849Scy		fprintf(stderr,
1414280849Scy		    "***Server returns unknown error code %d\n",
1415280849Scy		    m6resp);
1416280849Scy	}
1417280849Scy}
1418280849Scy
1419280849Scy/*
1420280849Scy * doquery - send a request and process the response, displaying
1421280849Scy *	     error messages for any error responses.
1422280849Scy */
142354359Srobertoint
142454359Srobertodoquery(
142554359Sroberto	int opcode,
1426280849Scy	associd_t associd,
142754359Sroberto	int auth,
1428293423Sdelphij	size_t qsize,
1429280849Scy	const char *qdata,
143054359Sroberto	u_short *rstatus,
1431293423Sdelphij	size_t *rsize,
1432280849Scy	const char **rdata
143354359Sroberto	)
143454359Sroberto{
1435280849Scy	return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
1436280849Scy			 rsize, rdata, FALSE);
1437280849Scy}
1438280849Scy
1439280849Scy
1440280849Scy/*
1441280849Scy * doqueryex - send a request and process the response, optionally
1442280849Scy *	       displaying error messages for any error responses.
1443280849Scy */
1444280849Scyint
1445280849Scydoqueryex(
1446280849Scy	int opcode,
1447280849Scy	associd_t associd,
1448280849Scy	int auth,
1449293423Sdelphij	size_t qsize,
1450280849Scy	const char *qdata,
1451280849Scy	u_short *rstatus,
1452293423Sdelphij	size_t *rsize,
1453280849Scy	const char **rdata,
1454280849Scy	int quiet
1455280849Scy	)
1456280849Scy{
145754359Sroberto	int res;
145854359Sroberto	int done;
145954359Sroberto
146054359Sroberto	/*
146154359Sroberto	 * Check to make sure host is open
146254359Sroberto	 */
146354359Sroberto	if (!havehost) {
1464280849Scy		fprintf(stderr, "***No host open, use `host' command\n");
146554359Sroberto		return -1;
146654359Sroberto	}
146754359Sroberto
146854359Sroberto	done = 0;
146954359Sroberto	sequence++;
147054359Sroberto
147154359Sroberto    again:
147254359Sroberto	/*
147354359Sroberto	 * send a request
147454359Sroberto	 */
147554359Sroberto	res = sendrequest(opcode, associd, auth, qsize, qdata);
147654359Sroberto	if (res != 0)
1477280849Scy		return res;
1478280849Scy
147954359Sroberto	/*
148054359Sroberto	 * Get the response.  If we got a standard error, print a message
148154359Sroberto	 */
148254359Sroberto	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
148354359Sroberto
148454359Sroberto	if (res > 0) {
148554359Sroberto		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
148654359Sroberto			if (res == ERR_INCOMPLETE) {
148754359Sroberto				/*
148854359Sroberto				 * better bump the sequence so we don't
148954359Sroberto				 * get confused about differing fragments.
149054359Sroberto				 */
149154359Sroberto				sequence++;
149254359Sroberto			}
149354359Sroberto			done = 1;
149454359Sroberto			goto again;
149554359Sroberto		}
1496280849Scy		if (!quiet)
1497280849Scy			show_error_msg(res, associd);
1498280849Scy
149954359Sroberto	}
150054359Sroberto	return res;
150154359Sroberto}
150254359Sroberto
150354359Sroberto
1504280849Scy#ifndef BUILD_AS_LIB
150554359Sroberto/*
150654359Sroberto * getcmds - read commands from the standard input and execute them
150754359Sroberto */
150854359Srobertostatic void
150954359Srobertogetcmds(void)
151054359Sroberto{
1511280849Scy	char *	line;
1512280849Scy	int	count;
151354359Sroberto
1514280849Scy	ntp_readline_init(interactive ? prompt : NULL);
1515106163Sroberto
1516280849Scy	for (;;) {
1517280849Scy		line = ntp_readline(&count);
1518280849Scy		if (NULL == line)
1519280849Scy			break;
1520280849Scy		docmd(line);
1521280849Scy		free(line);
1522280849Scy	}
152354359Sroberto
1524280849Scy	ntp_readline_uninit();
152554359Sroberto}
1526280849Scy#endif /* !BUILD_AS_LIB */
152754359Sroberto
1528280849Scy
1529280849Scy#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
153054359Sroberto/*
153154359Sroberto * abortcmd - catch interrupts and abort the current command
153254359Sroberto */
1533293423Sdelphijstatic int
1534293423Sdelphijabortcmd(void)
153554359Sroberto{
153654359Sroberto	if (current_output == stdout)
1537293423Sdelphij		(void) fflush(stdout);
153854359Sroberto	putc('\n', stderr);
153954359Sroberto	(void) fflush(stderr);
1540293423Sdelphij	if (jump) {
1541293423Sdelphij		jump = 0;
1542293423Sdelphij		longjmp(interrupt_buf, 1);
1543293423Sdelphij	}
1544293423Sdelphij	return TRUE;
154554359Sroberto}
1546280849Scy#endif	/* !SYS_WINNT && !BUILD_AS_LIB */
154754359Sroberto
1548280849Scy
1549280849Scy#ifndef	BUILD_AS_LIB
155054359Sroberto/*
155154359Sroberto * docmd - decode the command line and execute a command
155254359Sroberto */
155354359Srobertostatic void
155454359Srobertodocmd(
155554359Sroberto	const char *cmdline
155654359Sroberto	)
155754359Sroberto{
155854359Sroberto	char *tokens[1+MAXARGS+2];
155954359Sroberto	struct parse pcmd;
156054359Sroberto	int ntok;
156154359Sroberto	static int i;
156254359Sroberto	struct xcmd *xcmd;
156354359Sroberto
156454359Sroberto	/*
156554359Sroberto	 * Tokenize the command line.  If nothing on it, return.
156654359Sroberto	 */
156754359Sroberto	tokenize(cmdline, tokens, &ntok);
156854359Sroberto	if (ntok == 0)
156954359Sroberto	    return;
1570280849Scy
157154359Sroberto	/*
157254359Sroberto	 * Find the appropriate command description.
157354359Sroberto	 */
157454359Sroberto	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
157554359Sroberto	if (i == 0) {
157654359Sroberto		(void) fprintf(stderr, "***Command `%s' unknown\n",
157754359Sroberto			       tokens[0]);
157854359Sroberto		return;
157954359Sroberto	} else if (i >= 2) {
158054359Sroberto		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
158154359Sroberto			       tokens[0]);
158254359Sroberto		return;
158354359Sroberto	}
1584280849Scy
1585280849Scy	/* Warn about ignored extra args */
1586280849Scy	for (i = MAXARGS + 1; i < ntok ; ++i) {
1587280849Scy		fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
1588280849Scy	}
1589280849Scy
159054359Sroberto	/*
159154359Sroberto	 * Save the keyword, then walk through the arguments, interpreting
159254359Sroberto	 * as we go.
159354359Sroberto	 */
159454359Sroberto	pcmd.keyword = tokens[0];
159554359Sroberto	pcmd.nargs = 0;
159654359Sroberto	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
159754359Sroberto		if ((i+1) >= ntok) {
159854359Sroberto			if (!(xcmd->arg[i] & OPT)) {
159954359Sroberto				printusage(xcmd, stderr);
160054359Sroberto				return;
160154359Sroberto			}
160254359Sroberto			break;
160354359Sroberto		}
160454359Sroberto		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1605280849Scy			break;
160654359Sroberto		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1607280849Scy			return;
160854359Sroberto		pcmd.nargs++;
160954359Sroberto	}
161054359Sroberto
161154359Sroberto	i++;
161254359Sroberto	if (i < ntok && *tokens[i] == '>') {
161354359Sroberto		char *fname;
161454359Sroberto
161554359Sroberto		if (*(tokens[i]+1) != '\0')
1616280849Scy			fname = tokens[i]+1;
161754359Sroberto		else if ((i+1) < ntok)
1618280849Scy			fname = tokens[i+1];
161954359Sroberto		else {
162054359Sroberto			(void) fprintf(stderr, "***No file for redirect\n");
162154359Sroberto			return;
162254359Sroberto		}
162354359Sroberto
162454359Sroberto		current_output = fopen(fname, "w");
162554359Sroberto		if (current_output == NULL) {
162654359Sroberto			(void) fprintf(stderr, "***Error opening %s: ", fname);
162754359Sroberto			perror("");
162854359Sroberto			return;
162954359Sroberto		}
163054359Sroberto		i = 1;		/* flag we need a close */
163154359Sroberto	} else {
163254359Sroberto		current_output = stdout;
163354359Sroberto		i = 0;		/* flag no close */
163454359Sroberto	}
163554359Sroberto
163654359Sroberto	if (interactive && setjmp(interrupt_buf)) {
163754359Sroberto		jump = 0;
163854359Sroberto		return;
163954359Sroberto	} else {
164054359Sroberto		jump++;
164154359Sroberto		(xcmd->handler)(&pcmd, current_output);
164254359Sroberto		jump = 0;	/* HMS: 961106: was after fclose() */
164354359Sroberto		if (i) (void) fclose(current_output);
164454359Sroberto	}
1645280849Scy
1646280849Scy	return;
164754359Sroberto}
164854359Sroberto
164954359Sroberto
165054359Sroberto/*
165154359Sroberto * tokenize - turn a command line into tokens
1652280849Scy *
1653280849Scy * SK: Modified to allow a quoted string
1654280849Scy *
1655280849Scy * HMS: If the first character of the first token is a ':' then (after
1656280849Scy * eating inter-token whitespace) the 2nd token is the rest of the line.
165754359Sroberto */
1658280849Scy
165954359Srobertostatic void
166054359Srobertotokenize(
166154359Sroberto	const char *line,
166254359Sroberto	char **tokens,
166354359Sroberto	int *ntok
166454359Sroberto	)
166554359Sroberto{
166654359Sroberto	register const char *cp;
166754359Sroberto	register char *sp;
166854359Sroberto	static char tspace[MAXLINE];
166954359Sroberto
167054359Sroberto	sp = tspace;
167154359Sroberto	cp = line;
167254359Sroberto	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
167354359Sroberto		tokens[*ntok] = sp;
1674280849Scy
1675280849Scy		/* Skip inter-token whitespace */
167654359Sroberto		while (ISSPACE(*cp))
167754359Sroberto		    cp++;
1678280849Scy
1679280849Scy		/* If we're at EOL we're done */
168054359Sroberto		if (ISEOL(*cp))
168154359Sroberto		    break;
168254359Sroberto
1683280849Scy		/* If this is the 2nd token and the first token begins
1684280849Scy		 * with a ':', then just grab to EOL.
1685280849Scy		 */
1686280849Scy
1687280849Scy		if (*ntok == 1 && tokens[0][0] == ':') {
1688280849Scy			do {
1689280849Scy				if (sp - tspace >= MAXLINE)
1690280849Scy					goto toobig;
1691280849Scy				*sp++ = *cp++;
1692280849Scy			} while (!ISEOL(*cp));
1693280849Scy		}
1694280849Scy
1695280849Scy		/* Check if this token begins with a double quote.
1696280849Scy		 * If yes, continue reading till the next double quote
1697280849Scy		 */
1698280849Scy		else if (*cp == '\"') {
1699280849Scy			++cp;
1700280849Scy			do {
1701280849Scy				if (sp - tspace >= MAXLINE)
1702280849Scy					goto toobig;
1703280849Scy				*sp++ = *cp++;
1704280849Scy			} while ((*cp != '\"') && !ISEOL(*cp));
1705280849Scy			/* HMS: a missing closing " should be an error */
1706280849Scy		}
1707280849Scy		else {
1708280849Scy			do {
1709280849Scy				if (sp - tspace >= MAXLINE)
1710280849Scy					goto toobig;
1711280849Scy				*sp++ = *cp++;
1712280849Scy			} while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1713280849Scy			/* HMS: Why check for a " in the previous line? */
1714280849Scy		}
1715280849Scy
1716280849Scy		if (sp - tspace >= MAXLINE)
1717280849Scy			goto toobig;
171854359Sroberto		*sp++ = '\0';
171954359Sroberto	}
1720280849Scy	return;
1721280849Scy
1722280849Scy  toobig:
1723280849Scy	*ntok = 0;
1724280849Scy	fprintf(stderr,
1725280849Scy		"***Line `%s' is too big\n",
1726280849Scy		line);
1727280849Scy	return;
172854359Sroberto}
172954359Sroberto
173054359Sroberto
1731280849Scy/*
1732280849Scy * getarg - interpret an argument token
1733280849Scy */
1734280849Scystatic int
1735280849Scygetarg(
1736280849Scy	const char *str,
1737280849Scy	int code,
1738280849Scy	arg_v *argp
1739280849Scy	)
1740280849Scy{
1741280849Scy	u_long ul;
174254359Sroberto
1743280849Scy	switch (code & ~OPT) {
1744280849Scy	case NTP_STR:
1745280849Scy		argp->string = str;
1746280849Scy		break;
1747280849Scy
1748280849Scy	case NTP_ADD:
1749280849Scy		if (!getnetnum(str, &argp->netnum, NULL, 0))
1750280849Scy			return 0;
1751280849Scy		break;
1752280849Scy
1753280849Scy	case NTP_UINT:
1754280849Scy		if ('&' == str[0]) {
1755280849Scy			if (!atouint(&str[1], &ul)) {
1756280849Scy				fprintf(stderr,
1757280849Scy					"***Association index `%s' invalid/undecodable\n",
1758280849Scy					str);
1759280849Scy				return 0;
1760280849Scy			}
1761280849Scy			if (0 == numassoc) {
1762280849Scy				dogetassoc(stdout);
1763280849Scy				if (0 == numassoc) {
1764280849Scy					fprintf(stderr,
1765280849Scy						"***No associations found, `%s' unknown\n",
1766280849Scy						str);
1767280849Scy					return 0;
1768280849Scy				}
1769280849Scy			}
1770280849Scy			ul = min(ul, numassoc);
1771280849Scy			argp->uval = assoc_cache[ul - 1].assid;
1772280849Scy			break;
1773280849Scy		}
1774280849Scy		if (!atouint(str, &argp->uval)) {
1775280849Scy			fprintf(stderr, "***Illegal unsigned value %s\n",
1776280849Scy				str);
1777280849Scy			return 0;
1778280849Scy		}
1779280849Scy		break;
1780280849Scy
1781280849Scy	case NTP_INT:
1782280849Scy		if (!atoint(str, &argp->ival)) {
1783280849Scy			fprintf(stderr, "***Illegal integer value %s\n",
1784280849Scy				str);
1785280849Scy			return 0;
1786280849Scy		}
1787280849Scy		break;
1788280849Scy
1789280849Scy	case IP_VERSION:
1790280849Scy		if (!strcmp("-6", str)) {
1791280849Scy			argp->ival = 6;
1792280849Scy		} else if (!strcmp("-4", str)) {
1793280849Scy			argp->ival = 4;
1794280849Scy		} else {
1795280849Scy			fprintf(stderr, "***Version must be either 4 or 6\n");
1796280849Scy			return 0;
1797280849Scy		}
1798280849Scy		break;
1799280849Scy	}
1800280849Scy
1801280849Scy	return 1;
1802280849Scy}
1803280849Scy#endif	/* !BUILD_AS_LIB */
1804280849Scy
1805280849Scy
180654359Sroberto/*
180754359Sroberto * findcmd - find a command in a command description table
180854359Sroberto */
180954359Srobertostatic int
181054359Srobertofindcmd(
1811280849Scy	const char *	str,
1812280849Scy	struct xcmd *	clist1,
1813280849Scy	struct xcmd *	clist2,
1814280849Scy	struct xcmd **	cmd
181554359Sroberto	)
181654359Sroberto{
1817280849Scy	struct xcmd *cl;
1818293423Sdelphij	size_t clen;
181954359Sroberto	int nmatch;
182054359Sroberto	struct xcmd *nearmatch = NULL;
182154359Sroberto	struct xcmd *clist;
182254359Sroberto
182354359Sroberto	clen = strlen(str);
182454359Sroberto	nmatch = 0;
182554359Sroberto	if (clist1 != 0)
182654359Sroberto	    clist = clist1;
182754359Sroberto	else if (clist2 != 0)
182854359Sroberto	    clist = clist2;
182954359Sroberto	else
183054359Sroberto	    return 0;
183154359Sroberto
183254359Sroberto    again:
183354359Sroberto	for (cl = clist; cl->keyword != 0; cl++) {
183454359Sroberto		/* do a first character check, for efficiency */
183554359Sroberto		if (*str != *(cl->keyword))
183654359Sroberto		    continue;
183754359Sroberto		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
183854359Sroberto			/*
183954359Sroberto			 * Could be extact match, could be approximate.
184054359Sroberto			 * Is exact if the length of the keyword is the
184154359Sroberto			 * same as the str.
184254359Sroberto			 */
184354359Sroberto			if (*((cl->keyword) + clen) == '\0') {
184454359Sroberto				*cmd = cl;
184554359Sroberto				return 1;
184654359Sroberto			}
184754359Sroberto			nmatch++;
184854359Sroberto			nearmatch = cl;
184954359Sroberto		}
185054359Sroberto	}
185154359Sroberto
185254359Sroberto	/*
185354359Sroberto	 * See if there is more to do.  If so, go again.  Sorry about the
185454359Sroberto	 * goto, too much looking at BSD sources...
185554359Sroberto	 */
185654359Sroberto	if (clist == clist1 && clist2 != 0) {
185754359Sroberto		clist = clist2;
185854359Sroberto		goto again;
185954359Sroberto	}
186054359Sroberto
186154359Sroberto	/*
186254359Sroberto	 * If we got extactly 1 near match, use it, else return number
186354359Sroberto	 * of matches.
186454359Sroberto	 */
186554359Sroberto	if (nmatch == 1) {
186654359Sroberto		*cmd = nearmatch;
186754359Sroberto		return 1;
186854359Sroberto	}
186954359Sroberto	return nmatch;
187054359Sroberto}
187154359Sroberto
187254359Sroberto
187354359Sroberto/*
187454359Sroberto * getnetnum - given a host name, return its net number
187554359Sroberto *	       and (optional) full name
187654359Sroberto */
187754359Srobertoint
187854359Srobertogetnetnum(
187954359Sroberto	const char *hname,
1880280849Scy	sockaddr_u *num,
1881132451Sroberto	char *fullhost,
1882132451Sroberto	int af
188354359Sroberto	)
188454359Sroberto{
1885132451Sroberto	struct addrinfo hints, *ai = NULL;
188654359Sroberto
1887280849Scy	ZERO(hints);
1888132451Sroberto	hints.ai_flags = AI_CANONNAME;
1889132451Sroberto#ifdef AI_ADDRCONFIG
1890132451Sroberto	hints.ai_flags |= AI_ADDRCONFIG;
1891132451Sroberto#endif
1892280849Scy
1893280849Scy	/*
1894280849Scy	 * decodenetnum only works with addresses, but handles syntax
1895280849Scy	 * that getaddrinfo doesn't:  [2001::1]:1234
1896280849Scy	 */
189754359Sroberto	if (decodenetnum(hname, num)) {
1898280849Scy		if (fullhost != NULL)
1899280849Scy			getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1900280849Scy				    LENHOSTNAME, NULL, 0, 0);
190154359Sroberto		return 1;
1902182007Sroberto	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1903280849Scy		INSIST(sizeof(*num) >= ai->ai_addrlen);
1904280849Scy		memcpy(num, ai->ai_addr, ai->ai_addrlen);
1905280849Scy		if (fullhost != NULL) {
1906280849Scy			if (ai->ai_canonname != NULL)
1907280849Scy				strlcpy(fullhost, ai->ai_canonname,
1908280849Scy					LENHOSTNAME);
1909280849Scy			else
1910280849Scy				getnameinfo(&num->sa, SOCKLEN(num),
1911280849Scy					    fullhost, LENHOSTNAME, NULL,
1912280849Scy					    0, 0);
1913280849Scy		}
1914280849Scy		freeaddrinfo(ai);
191554359Sroberto		return 1;
191654359Sroberto	}
1917280849Scy	fprintf(stderr, "***Can't find host %s\n", hname);
1918280849Scy
1919280849Scy	return 0;
192054359Sroberto}
192154359Sroberto
1922280849Scy
192354359Sroberto/*
192454359Sroberto * nntohost - convert network number to host name.  This routine enforces
192554359Sroberto *	       the showhostnames setting.
192654359Sroberto */
1927280849Scyconst char *
192854359Srobertonntohost(
1929280849Scy	sockaddr_u *netnum
193054359Sroberto	)
193154359Sroberto{
1932280849Scy	return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
193354359Sroberto}
193454359Sroberto
193554359Sroberto
193654359Sroberto/*
1937280849Scy * nntohost_col - convert network number to host name in fixed width.
1938280849Scy *		  This routine enforces the showhostnames setting.
1939280849Scy *		  When displaying hostnames longer than the width,
1940280849Scy *		  the first part of the hostname is displayed.  When
1941280849Scy *		  displaying numeric addresses longer than the width,
1942280849Scy *		  Such as IPv6 addresses, the caller decides whether
1943280849Scy *		  the first or last of the numeric address is used.
1944280849Scy */
1945280849Scyconst char *
1946280849Scynntohost_col(
1947280849Scy	sockaddr_u *	addr,
1948280849Scy	size_t		width,
1949280849Scy	int		preserve_lowaddrbits
1950280849Scy	)
1951280849Scy{
1952280849Scy	const char *	out;
1953280849Scy
1954280849Scy	if (!showhostnames || SOCK_UNSPEC(addr)) {
1955280849Scy		if (preserve_lowaddrbits)
1956280849Scy			out = trunc_left(stoa(addr), width);
1957280849Scy		else
1958280849Scy			out = trunc_right(stoa(addr), width);
1959280849Scy	} else if (ISREFCLOCKADR(addr)) {
1960280849Scy		out = refnumtoa(addr);
1961280849Scy	} else {
1962280849Scy		out = trunc_right(socktohost(addr), width);
1963280849Scy	}
1964280849Scy	return out;
1965280849Scy}
1966280849Scy
1967280849Scy
1968280849Scy/*
1969280849Scy * nntohostp() is the same as nntohost() plus a :port suffix
1970280849Scy */
1971280849Scyconst char *
1972280849Scynntohostp(
1973280849Scy	sockaddr_u *netnum
1974280849Scy	)
1975280849Scy{
1976280849Scy	const char *	hostn;
1977280849Scy	char *		buf;
1978280849Scy
1979280849Scy	if (!showhostnames || SOCK_UNSPEC(netnum))
1980280849Scy		return sptoa(netnum);
1981280849Scy	else if (ISREFCLOCKADR(netnum))
1982280849Scy		return refnumtoa(netnum);
1983280849Scy
1984280849Scy	hostn = socktohost(netnum);
1985280849Scy	LIB_GETBUF(buf);
1986280849Scy	snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
1987280849Scy
1988280849Scy	return buf;
1989280849Scy}
1990280849Scy
1991280849Scy/*
199254359Sroberto * rtdatetolfp - decode an RT-11 date into an l_fp
199354359Sroberto */
199454359Srobertostatic int
199554359Srobertortdatetolfp(
199654359Sroberto	char *str,
199754359Sroberto	l_fp *lfp
199854359Sroberto	)
199954359Sroberto{
200054359Sroberto	register char *cp;
200154359Sroberto	register int i;
200254359Sroberto	struct calendar cal;
200354359Sroberto	char buf[4];
200454359Sroberto
200554359Sroberto	cal.yearday = 0;
200654359Sroberto
200754359Sroberto	/*
200854359Sroberto	 * An RT-11 date looks like:
200954359Sroberto	 *
201054359Sroberto	 * d[d]-Mth-y[y] hh:mm:ss
201154359Sroberto	 *
201254359Sroberto	 * (No docs, but assume 4-digit years are also legal...)
201354359Sroberto	 *
201454359Sroberto	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
201554359Sroberto	 */
201654359Sroberto	cp = str;
201754359Sroberto	if (!isdigit((int)*cp)) {
201854359Sroberto		if (*cp == '-') {
201954359Sroberto			/*
202054359Sroberto			 * Catch special case
202154359Sroberto			 */
202254359Sroberto			L_CLR(lfp);
202354359Sroberto			return 1;
202454359Sroberto		}
202554359Sroberto		return 0;
202654359Sroberto	}
202754359Sroberto
2028132451Sroberto	cal.monthday = (u_char) (*cp++ - '0');	/* ascii dependent */
202954359Sroberto	if (isdigit((int)*cp)) {
2030132451Sroberto		cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
2031132451Sroberto		cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
203254359Sroberto	}
203354359Sroberto
203454359Sroberto	if (*cp++ != '-')
203554359Sroberto	    return 0;
2036280849Scy
203754359Sroberto	for (i = 0; i < 3; i++)
203854359Sroberto	    buf[i] = *cp++;
203954359Sroberto	buf[3] = '\0';
204054359Sroberto
204154359Sroberto	for (i = 0; i < 12; i++)
204254359Sroberto	    if (STREQ(buf, months[i]))
204354359Sroberto		break;
204454359Sroberto	if (i == 12)
204554359Sroberto	    return 0;
2046132451Sroberto	cal.month = (u_char)(i + 1);
204754359Sroberto
204854359Sroberto	if (*cp++ != '-')
204954359Sroberto	    return 0;
2050280849Scy
205154359Sroberto	if (!isdigit((int)*cp))
205254359Sroberto	    return 0;
2053132451Sroberto	cal.year = (u_short)(*cp++ - '0');
205454359Sroberto	if (isdigit((int)*cp)) {
2055132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2056132451Sroberto		cal.year = (u_short)(*cp++ - '0');
205754359Sroberto	}
205854359Sroberto	if (isdigit((int)*cp)) {
2059132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2060132451Sroberto		cal.year = (u_short)(cal.year + *cp++ - '0');
206154359Sroberto	}
206254359Sroberto	if (isdigit((int)*cp)) {
2063132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2064132451Sroberto		cal.year = (u_short)(cal.year + *cp++ - '0');
206554359Sroberto	}
206654359Sroberto
206754359Sroberto	/*
206854359Sroberto	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
206954359Sroberto	 */
207054359Sroberto	if (cal.year == 0) {
207154359Sroberto		L_CLR(lfp);
207254359Sroberto		return 1;
207354359Sroberto	}
207454359Sroberto
207554359Sroberto	if (*cp++ != ' ' || !isdigit((int)*cp))
207654359Sroberto	    return 0;
2077132451Sroberto	cal.hour = (u_char)(*cp++ - '0');
207854359Sroberto	if (isdigit((int)*cp)) {
2079132451Sroberto		cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
2080132451Sroberto		cal.hour = (u_char)(cal.hour + *cp++ - '0');
208154359Sroberto	}
208254359Sroberto
208354359Sroberto	if (*cp++ != ':' || !isdigit((int)*cp))
208454359Sroberto	    return 0;
2085132451Sroberto	cal.minute = (u_char)(*cp++ - '0');
208654359Sroberto	if (isdigit((int)*cp)) {
2087132451Sroberto		cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
2088132451Sroberto		cal.minute = (u_char)(cal.minute + *cp++ - '0');
208954359Sroberto	}
209054359Sroberto
209154359Sroberto	if (*cp++ != ':' || !isdigit((int)*cp))
209254359Sroberto	    return 0;
2093132451Sroberto	cal.second = (u_char)(*cp++ - '0');
209454359Sroberto	if (isdigit((int)*cp)) {
2095132451Sroberto		cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
2096132451Sroberto		cal.second = (u_char)(cal.second + *cp++ - '0');
209754359Sroberto	}
209854359Sroberto
209954359Sroberto	/*
210054359Sroberto	 * For RT-11, 1972 seems to be the pivot year
210154359Sroberto	 */
210254359Sroberto	if (cal.year < 72)
210354359Sroberto		cal.year += 2000;
210454359Sroberto	if (cal.year < 100)
210554359Sroberto		cal.year += 1900;
210654359Sroberto
210754359Sroberto	lfp->l_ui = caltontp(&cal);
210854359Sroberto	lfp->l_uf = 0;
210954359Sroberto	return 1;
211054359Sroberto}
211154359Sroberto
211254359Sroberto
211354359Sroberto/*
211454359Sroberto * decodets - decode a timestamp into an l_fp format number, with
211554359Sroberto *	      consideration of fuzzball formats.
211654359Sroberto */
211754359Srobertoint
211854359Srobertodecodets(
211954359Sroberto	char *str,
212054359Sroberto	l_fp *lfp
212154359Sroberto	)
212254359Sroberto{
2123280849Scy	char *cp;
2124280849Scy	char buf[30];
2125280849Scy	size_t b;
2126280849Scy
212754359Sroberto	/*
212854359Sroberto	 * If it starts with a 0x, decode as hex.
212954359Sroberto	 */
213054359Sroberto	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
2131280849Scy		return hextolfp(str+2, lfp);
213254359Sroberto
213354359Sroberto	/*
213454359Sroberto	 * If it starts with a '"', try it as an RT-11 date.
213554359Sroberto	 */
213654359Sroberto	if (*str == '"') {
2137280849Scy		cp = str + 1;
2138280849Scy		b = 0;
2139280849Scy		while ('"' != *cp && '\0' != *cp &&
2140280849Scy		       b < COUNTOF(buf) - 1)
2141280849Scy			buf[b++] = *cp++;
2142280849Scy		buf[b] = '\0';
214354359Sroberto		return rtdatetolfp(buf, lfp);
214454359Sroberto	}
214554359Sroberto
214654359Sroberto	/*
214754359Sroberto	 * Might still be hex.  Check out the first character.  Talk
214854359Sroberto	 * about heuristics!
214954359Sroberto	 */
215054359Sroberto	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2151280849Scy		return hextolfp(str, lfp);
215254359Sroberto
215354359Sroberto	/*
215454359Sroberto	 * Try it as a decimal.  If this fails, try as an unquoted
215554359Sroberto	 * RT-11 date.  This code should go away eventually.
215654359Sroberto	 */
215754359Sroberto	if (atolfp(str, lfp))
2158280849Scy		return 1;
2159280849Scy
216054359Sroberto	return rtdatetolfp(str, lfp);
216154359Sroberto}
216254359Sroberto
216354359Sroberto
216454359Sroberto/*
216554359Sroberto * decodetime - decode a time value.  It should be in milliseconds
216654359Sroberto */
216754359Srobertoint
216854359Srobertodecodetime(
216954359Sroberto	char *str,
217054359Sroberto	l_fp *lfp
217154359Sroberto	)
217254359Sroberto{
217354359Sroberto	return mstolfp(str, lfp);
217454359Sroberto}
217554359Sroberto
217654359Sroberto
217754359Sroberto/*
217854359Sroberto * decodeint - decode an integer
217954359Sroberto */
218054359Srobertoint
218154359Srobertodecodeint(
218254359Sroberto	char *str,
218354359Sroberto	long *val
218454359Sroberto	)
218554359Sroberto{
218654359Sroberto	if (*str == '0') {
218754359Sroberto		if (*(str+1) == 'x' || *(str+1) == 'X')
2188280849Scy		    return hextoint(str+2, (u_long *)val);
2189280849Scy		return octtoint(str, (u_long *)val);
219054359Sroberto	}
219154359Sroberto	return atoint(str, val);
219254359Sroberto}
219354359Sroberto
219454359Sroberto
219554359Sroberto/*
219654359Sroberto * decodeuint - decode an unsigned integer
219754359Sroberto */
219854359Srobertoint
219954359Srobertodecodeuint(
220054359Sroberto	char *str,
220154359Sroberto	u_long *val
220254359Sroberto	)
220354359Sroberto{
220454359Sroberto	if (*str == '0') {
220554359Sroberto		if (*(str + 1) == 'x' || *(str + 1) == 'X')
220654359Sroberto			return (hextoint(str + 2, val));
220754359Sroberto		return (octtoint(str, val));
220854359Sroberto	}
220954359Sroberto	return (atouint(str, val));
221054359Sroberto}
221154359Sroberto
221254359Sroberto
221354359Sroberto/*
221454359Sroberto * decodearr - decode an array of time values
221554359Sroberto */
221654359Srobertostatic int
221754359Srobertodecodearr(
221854359Sroberto	char *str,
221954359Sroberto	int *narr,
222054359Sroberto	l_fp *lfparr
222154359Sroberto	)
222254359Sroberto{
222354359Sroberto	register char *cp, *bp;
222454359Sroberto	register l_fp *lfp;
222554359Sroberto	char buf[60];
222654359Sroberto
222754359Sroberto	lfp = lfparr;
222854359Sroberto	cp = str;
222954359Sroberto	*narr = 0;
223054359Sroberto
223154359Sroberto	while (*narr < 8) {
223254359Sroberto		while (isspace((int)*cp))
223354359Sroberto		    cp++;
223454359Sroberto		if (*cp == '\0')
223554359Sroberto		    break;
223654359Sroberto
223754359Sroberto		bp = buf;
223854359Sroberto		while (!isspace((int)*cp) && *cp != '\0')
223954359Sroberto		    *bp++ = *cp++;
224054359Sroberto		*bp++ = '\0';
224154359Sroberto
224254359Sroberto		if (!decodetime(buf, lfp))
224354359Sroberto		    return 0;
224454359Sroberto		(*narr)++;
224554359Sroberto		lfp++;
224654359Sroberto	}
224754359Sroberto	return 1;
224854359Sroberto}
224954359Sroberto
225054359Sroberto
225154359Sroberto/*
225254359Sroberto * Finally, the built in command handlers
225354359Sroberto */
225454359Sroberto
225554359Sroberto/*
225654359Sroberto * help - tell about commands, or details of a particular command
225754359Sroberto */
225854359Srobertostatic void
225954359Srobertohelp(
226054359Sroberto	struct parse *pcmd,
226154359Sroberto	FILE *fp
226254359Sroberto	)
226354359Sroberto{
2264280849Scy	struct xcmd *xcp = NULL;	/* quiet warning */
2265280849Scy	const char *cmd;
2266182007Sroberto	const char *list[100];
2267280849Scy	size_t word, words;
2268280849Scy	size_t row, rows;
2269280849Scy	size_t col, cols;
2270280849Scy	size_t length;
227154359Sroberto
227254359Sroberto	if (pcmd->nargs == 0) {
2273182007Sroberto		words = 0;
2274280849Scy		for (xcp = builtins; xcp->keyword != NULL; xcp++) {
2275280849Scy			if (*(xcp->keyword) != '?' &&
2276280849Scy			    words < COUNTOF(list))
2277280849Scy				list[words++] = xcp->keyword;
227854359Sroberto		}
2279280849Scy		for (xcp = opcmds; xcp->keyword != NULL; xcp++)
2280280849Scy			if (words < COUNTOF(list))
2281280849Scy				list[words++] = xcp->keyword;
228254359Sroberto
2283280849Scy		qsort((void *)list, words, sizeof(list[0]), helpsort);
2284182007Sroberto		col = 0;
2285182007Sroberto		for (word = 0; word < words; word++) {
2286280849Scy			length = strlen(list[word]);
2287280849Scy			col = max(col, length);
228854359Sroberto		}
228954359Sroberto
2290182007Sroberto		cols = SCREENWIDTH / ++col;
2291280849Scy		rows = (words + cols - 1) / cols;
2292182007Sroberto
2293280849Scy		fprintf(fp, "ntpq commands:\n");
2294182007Sroberto
2295182007Sroberto		for (row = 0; row < rows; row++) {
2296280849Scy			for (word = row; word < words; word += rows)
2297280849Scy				fprintf(fp, "%-*.*s", (int)col,
2298280849Scy					(int)col - 1, list[word]);
2299280849Scy			fprintf(fp, "\n");
2300280849Scy		}
230154359Sroberto	} else {
230254359Sroberto		cmd = pcmd->argval[0].string;
2303182007Sroberto		words = findcmd(cmd, builtins, opcmds, &xcp);
2304182007Sroberto		if (words == 0) {
2305280849Scy			fprintf(stderr,
2306280849Scy				"Command `%s' is unknown\n", cmd);
230754359Sroberto			return;
2308182007Sroberto		} else if (words >= 2) {
2309280849Scy			fprintf(stderr,
2310280849Scy				"Command `%s' is ambiguous\n", cmd);
231154359Sroberto			return;
231254359Sroberto		}
2313280849Scy		fprintf(fp, "function: %s\n", xcp->comment);
231454359Sroberto		printusage(xcp, fp);
231554359Sroberto	}
231654359Sroberto}
231754359Sroberto
231854359Sroberto
231954359Sroberto/*
232054359Sroberto * helpsort - do hostname qsort comparisons
232154359Sroberto */
232254359Srobertostatic int
232354359Srobertohelpsort(
232454359Sroberto	const void *t1,
232554359Sroberto	const void *t2
232654359Sroberto	)
232754359Sroberto{
2328280849Scy	const char * const *	name1 = t1;
2329280849Scy	const char * const *	name2 = t2;
233054359Sroberto
233154359Sroberto	return strcmp(*name1, *name2);
233254359Sroberto}
233354359Sroberto
233454359Sroberto
233554359Sroberto/*
233654359Sroberto * printusage - print usage information for a command
233754359Sroberto */
233854359Srobertostatic void
233954359Srobertoprintusage(
234054359Sroberto	struct xcmd *xcp,
234154359Sroberto	FILE *fp
234254359Sroberto	)
234354359Sroberto{
234454359Sroberto	register int i;
234554359Sroberto
2346280849Scy	/* XXX: Do we need to warn about extra args here too? */
2347280849Scy
234854359Sroberto	(void) fprintf(fp, "usage: %s", xcp->keyword);
234954359Sroberto	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
235054359Sroberto		if (xcp->arg[i] & OPT)
235154359Sroberto		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
235254359Sroberto		else
235354359Sroberto		    (void) fprintf(fp, " %s", xcp->desc[i]);
235454359Sroberto	}
235554359Sroberto	(void) fprintf(fp, "\n");
235654359Sroberto}
235754359Sroberto
235854359Sroberto
235954359Sroberto/*
236054359Sroberto * timeout - set time out time
236154359Sroberto */
236254359Srobertostatic void
236354359Srobertotimeout(
236454359Sroberto	struct parse *pcmd,
236554359Sroberto	FILE *fp
236654359Sroberto	)
236754359Sroberto{
236854359Sroberto	int val;
236954359Sroberto
237054359Sroberto	if (pcmd->nargs == 0) {
2371280849Scy		val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
237254359Sroberto		(void) fprintf(fp, "primary timeout %d ms\n", val);
237354359Sroberto	} else {
237454359Sroberto		tvout.tv_sec = pcmd->argval[0].uval / 1000;
2375280849Scy		tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
237654359Sroberto			* 1000;
237754359Sroberto	}
237854359Sroberto}
237954359Sroberto
238054359Sroberto
238154359Sroberto/*
238254359Sroberto * auth_delay - set delay for auth requests
238354359Sroberto */
238454359Srobertostatic void
238554359Srobertoauth_delay(
238654359Sroberto	struct parse *pcmd,
238754359Sroberto	FILE *fp
238854359Sroberto	)
238954359Sroberto{
239054359Sroberto	int isneg;
239154359Sroberto	u_long val;
239254359Sroberto
239354359Sroberto	if (pcmd->nargs == 0) {
239454359Sroberto		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
239554359Sroberto		(void) fprintf(fp, "delay %lu ms\n", val);
239654359Sroberto	} else {
239754359Sroberto		if (pcmd->argval[0].ival < 0) {
239854359Sroberto			isneg = 1;
239954359Sroberto			val = (u_long)(-pcmd->argval[0].ival);
240054359Sroberto		} else {
240154359Sroberto			isneg = 0;
240254359Sroberto			val = (u_long)pcmd->argval[0].ival;
240354359Sroberto		}
240454359Sroberto
240554359Sroberto		delay_time.l_ui = val / 1000;
240654359Sroberto		val %= 1000;
240754359Sroberto		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
240854359Sroberto
240954359Sroberto		if (isneg)
241054359Sroberto		    L_NEG(&delay_time);
241154359Sroberto	}
241254359Sroberto}
241354359Sroberto
241454359Sroberto
241554359Sroberto/*
241654359Sroberto * host - set the host we are dealing with.
241754359Sroberto */
241854359Srobertostatic void
241954359Srobertohost(
242054359Sroberto	struct parse *pcmd,
242154359Sroberto	FILE *fp
242254359Sroberto	)
242354359Sroberto{
2424132451Sroberto	int i;
2425132451Sroberto
242654359Sroberto	if (pcmd->nargs == 0) {
242754359Sroberto		if (havehost)
2428280849Scy			(void) fprintf(fp, "current host is %s\n",
2429280849Scy					   currenthost);
243054359Sroberto		else
2431280849Scy			(void) fprintf(fp, "no current host\n");
2432132451Sroberto		return;
2433132451Sroberto	}
2434132451Sroberto
2435132451Sroberto	i = 0;
2436132451Sroberto	ai_fam_templ = ai_fam_default;
2437132451Sroberto	if (pcmd->nargs == 2) {
2438132451Sroberto		if (!strcmp("-4", pcmd->argval[i].string))
2439132451Sroberto			ai_fam_templ = AF_INET;
2440132451Sroberto		else if (!strcmp("-6", pcmd->argval[i].string))
2441132451Sroberto			ai_fam_templ = AF_INET6;
2442280849Scy		else
2443280849Scy			goto no_change;
2444132451Sroberto		i = 1;
2445132451Sroberto	}
2446280849Scy	if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
2447280849Scy		fprintf(fp, "current host set to %s\n", currenthost);
244854359Sroberto	} else {
2449280849Scy    no_change:
245054359Sroberto		if (havehost)
2451280849Scy			fprintf(fp, "current host remains %s\n",
2452280849Scy				currenthost);
245354359Sroberto		else
2454280849Scy			fprintf(fp, "still no current host\n");
245554359Sroberto	}
245654359Sroberto}
245754359Sroberto
245854359Sroberto
245954359Sroberto/*
246054359Sroberto * poll - do one (or more) polls of the host via NTP
246154359Sroberto */
246254359Sroberto/*ARGSUSED*/
246354359Srobertostatic void
246454359Srobertontp_poll(
246554359Sroberto	struct parse *pcmd,
246654359Sroberto	FILE *fp
246754359Sroberto	)
246854359Sroberto{
246954359Sroberto	(void) fprintf(fp, "poll not implemented yet\n");
247054359Sroberto}
247154359Sroberto
247254359Sroberto
247354359Sroberto/*
2474298695Sdelphij * showdrefid2str - return a string explanation of the value of drefid
2475298695Sdelphij */
2476298695Sdelphijstatic char *
2477298695Sdelphijshowdrefid2str(void)
2478298695Sdelphij{
2479298695Sdelphij	switch (drefid) {
2480298695Sdelphij	    case REFID_HASH:
2481298695Sdelphij	    	return "hash";
2482298695Sdelphij	    case REFID_IPV4:
2483298695Sdelphij	    	return "ipv4";
2484298695Sdelphij	    default:
2485298695Sdelphij	    	return "Unknown";
2486298695Sdelphij	}
2487298695Sdelphij}
2488298695Sdelphij
2489298695Sdelphij
2490298695Sdelphij/*
2491298695Sdelphij * drefid - display/change "display hash"
2492298695Sdelphij */
2493298695Sdelphijstatic void
2494298695Sdelphijshowdrefid(
2495298695Sdelphij	struct parse *pcmd,
2496298695Sdelphij	FILE *fp
2497298695Sdelphij	)
2498298695Sdelphij{
2499298695Sdelphij	if (pcmd->nargs == 0) {
2500298695Sdelphij		(void) fprintf(fp, "drefid value is %s\n", showdrefid2str());
2501298695Sdelphij		return;
2502298695Sdelphij	} else if (STREQ(pcmd->argval[0].string, "hash")) {
2503298695Sdelphij		drefid = REFID_HASH;
2504298695Sdelphij	} else if (STREQ(pcmd->argval[0].string, "ipv4")) {
2505298695Sdelphij		drefid = REFID_IPV4;
2506298695Sdelphij	} else {
2507298695Sdelphij		(void) fprintf(fp, "What?\n");
2508298695Sdelphij		return;
2509298695Sdelphij	}
2510298695Sdelphij	(void) fprintf(fp, "drefid value set to %s\n", showdrefid2str());
2511298695Sdelphij}
2512298695Sdelphij
2513298695Sdelphij
2514298695Sdelphij/*
251554359Sroberto * keyid - get a keyid to use for authenticating requests
251654359Sroberto */
251754359Srobertostatic void
251854359Srobertokeyid(
251954359Sroberto	struct parse *pcmd,
252054359Sroberto	FILE *fp
252154359Sroberto	)
252254359Sroberto{
252354359Sroberto	if (pcmd->nargs == 0) {
2524132451Sroberto		if (info_auth_keyid == 0)
252554359Sroberto		    (void) fprintf(fp, "no keyid defined\n");
252654359Sroberto		else
252754359Sroberto		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
252854359Sroberto	} else {
2529132451Sroberto		/* allow zero so that keyid can be cleared. */
2530132451Sroberto		if(pcmd->argval[0].uval > NTP_MAXKEY)
2531132451Sroberto		    (void) fprintf(fp, "Invalid key identifier\n");
253254359Sroberto		info_auth_keyid = pcmd->argval[0].uval;
253354359Sroberto	}
253454359Sroberto}
253554359Sroberto
253654359Sroberto/*
253754359Sroberto * keytype - get type of key to use for authenticating requests
253854359Sroberto */
253954359Srobertostatic void
254054359Srobertokeytype(
254154359Sroberto	struct parse *pcmd,
254254359Sroberto	FILE *fp
254354359Sroberto	)
254454359Sroberto{
2545280849Scy	const char *	digest_name;
2546280849Scy	size_t		digest_len;
2547280849Scy	int		key_type;
254854359Sroberto
2549280849Scy	if (!pcmd->nargs) {
2550280849Scy		fprintf(fp, "keytype is %s with %lu octet digests\n",
2551280849Scy			keytype_name(info_auth_keytype),
2552280849Scy			(u_long)info_auth_hashlen);
2553280849Scy		return;
2554280849Scy	}
2555280849Scy
2556280849Scy	digest_name = pcmd->argval[0].string;
2557280849Scy	digest_len = 0;
2558280849Scy	key_type = keytype_from_text(digest_name, &digest_len);
2559280849Scy
2560280849Scy	if (!key_type) {
2561285169Scy		fprintf(fp, "keytype is not valid. "
2562280849Scy#ifdef OPENSSL
2563285169Scy			"Type \"help keytype\" for the available digest types.\n");
2564280849Scy#else
2565285169Scy			"Only \"md5\" is available.\n");
2566280849Scy#endif
2567280849Scy		return;
2568280849Scy	}
2569280849Scy
2570280849Scy	info_auth_keytype = key_type;
2571280849Scy	info_auth_hashlen = digest_len;
257254359Sroberto}
257354359Sroberto
257454359Sroberto
257554359Sroberto/*
257654359Sroberto * passwd - get an authentication key
257754359Sroberto */
257854359Sroberto/*ARGSUSED*/
257954359Srobertostatic void
258054359Srobertopasswd(
258154359Sroberto	struct parse *pcmd,
258254359Sroberto	FILE *fp
258354359Sroberto	)
258454359Sroberto{
2585280849Scy	const char *pass;
258654359Sroberto
2587132451Sroberto	if (info_auth_keyid == 0) {
2588280849Scy		info_auth_keyid = getkeyid("Keyid: ");
2589280849Scy		if (info_auth_keyid == 0) {
2590280849Scy			(void)fprintf(fp, "Keyid must be defined\n");
259154359Sroberto			return;
259254359Sroberto		}
259354359Sroberto	}
2594280849Scy	if (pcmd->nargs >= 1)
2595280849Scy		pass = pcmd->argval[0].string;
2596132451Sroberto	else {
2597280849Scy		pass = getpass_keytype(info_auth_keytype);
2598280849Scy		if ('\0' == pass[0]) {
2599280849Scy			fprintf(fp, "Password unchanged\n");
2600280849Scy			return;
2601280849Scy		}
2602132451Sroberto	}
2603280849Scy	authusekey(info_auth_keyid, info_auth_keytype,
2604280849Scy		   (const u_char *)pass);
2605280849Scy	authtrust(info_auth_keyid, 1);
260654359Sroberto}
260754359Sroberto
260854359Sroberto
260954359Sroberto/*
261054359Sroberto * hostnames - set the showhostnames flag
261154359Sroberto */
261254359Srobertostatic void
261354359Srobertohostnames(
261454359Sroberto	struct parse *pcmd,
261554359Sroberto	FILE *fp
261654359Sroberto	)
261754359Sroberto{
261854359Sroberto	if (pcmd->nargs == 0) {
261954359Sroberto		if (showhostnames)
262054359Sroberto		    (void) fprintf(fp, "hostnames being shown\n");
262154359Sroberto		else
262254359Sroberto		    (void) fprintf(fp, "hostnames not being shown\n");
262354359Sroberto	} else {
262454359Sroberto		if (STREQ(pcmd->argval[0].string, "yes"))
262554359Sroberto		    showhostnames = 1;
262654359Sroberto		else if (STREQ(pcmd->argval[0].string, "no"))
262754359Sroberto		    showhostnames = 0;
262854359Sroberto		else
262954359Sroberto		    (void)fprintf(stderr, "What?\n");
263054359Sroberto	}
263154359Sroberto}
263254359Sroberto
263354359Sroberto
263454359Sroberto
263554359Sroberto/*
263654359Sroberto * setdebug - set/change debugging level
263754359Sroberto */
263854359Srobertostatic void
263954359Srobertosetdebug(
264054359Sroberto	struct parse *pcmd,
264154359Sroberto	FILE *fp
264254359Sroberto	)
264354359Sroberto{
264454359Sroberto	if (pcmd->nargs == 0) {
264554359Sroberto		(void) fprintf(fp, "debug level is %d\n", debug);
264654359Sroberto		return;
264754359Sroberto	} else if (STREQ(pcmd->argval[0].string, "no")) {
264854359Sroberto		debug = 0;
264954359Sroberto	} else if (STREQ(pcmd->argval[0].string, "more")) {
265054359Sroberto		debug++;
265154359Sroberto	} else if (STREQ(pcmd->argval[0].string, "less")) {
265254359Sroberto		debug--;
265354359Sroberto	} else {
265454359Sroberto		(void) fprintf(fp, "What?\n");
265554359Sroberto		return;
265654359Sroberto	}
265754359Sroberto	(void) fprintf(fp, "debug level set to %d\n", debug);
265854359Sroberto}
265954359Sroberto
266054359Sroberto
266154359Sroberto/*
266254359Sroberto * quit - stop this nonsense
266354359Sroberto */
266454359Sroberto/*ARGSUSED*/
266554359Srobertostatic void
266654359Srobertoquit(
266754359Sroberto	struct parse *pcmd,
266854359Sroberto	FILE *fp
266954359Sroberto	)
267054359Sroberto{
267154359Sroberto	if (havehost)
267254359Sroberto	    closesocket(sockfd);	/* cleanliness next to godliness */
267354359Sroberto	exit(0);
267454359Sroberto}
267554359Sroberto
267654359Sroberto
267754359Sroberto/*
267854359Sroberto * version - print the current version number
267954359Sroberto */
268054359Sroberto/*ARGSUSED*/
268154359Srobertostatic void
268254359Srobertoversion(
268354359Sroberto	struct parse *pcmd,
268454359Sroberto	FILE *fp
268554359Sroberto	)
268654359Sroberto{
268754359Sroberto
268854359Sroberto	(void) fprintf(fp, "%s\n", Version);
268954359Sroberto	return;
269054359Sroberto}
269154359Sroberto
269254359Sroberto
269354359Sroberto/*
269454359Sroberto * raw - set raw mode output
269554359Sroberto */
269654359Sroberto/*ARGSUSED*/
269754359Srobertostatic void
269854359Srobertoraw(
269954359Sroberto	struct parse *pcmd,
270054359Sroberto	FILE *fp
270154359Sroberto	)
270254359Sroberto{
270354359Sroberto	rawmode = 1;
270454359Sroberto	(void) fprintf(fp, "Output set to raw\n");
270554359Sroberto}
270654359Sroberto
270754359Sroberto
270854359Sroberto/*
270954359Sroberto * cooked - set cooked mode output
271054359Sroberto */
271154359Sroberto/*ARGSUSED*/
271254359Srobertostatic void
271354359Srobertocooked(
271454359Sroberto	struct parse *pcmd,
271554359Sroberto	FILE *fp
271654359Sroberto	)
271754359Sroberto{
271854359Sroberto	rawmode = 0;
271954359Sroberto	(void) fprintf(fp, "Output set to cooked\n");
272054359Sroberto	return;
272154359Sroberto}
272254359Sroberto
272354359Sroberto
272454359Sroberto/*
272554359Sroberto * authenticate - always authenticate requests to this host
272654359Sroberto */
272754359Srobertostatic void
272854359Srobertoauthenticate(
272954359Sroberto	struct parse *pcmd,
273054359Sroberto	FILE *fp
273154359Sroberto	)
273254359Sroberto{
273354359Sroberto	if (pcmd->nargs == 0) {
273454359Sroberto		if (always_auth) {
273554359Sroberto			(void) fprintf(fp,
273654359Sroberto				       "authenticated requests being sent\n");
273754359Sroberto		} else
273854359Sroberto		    (void) fprintf(fp,
273954359Sroberto				   "unauthenticated requests being sent\n");
274054359Sroberto	} else {
274154359Sroberto		if (STREQ(pcmd->argval[0].string, "yes")) {
274254359Sroberto			always_auth = 1;
274354359Sroberto		} else if (STREQ(pcmd->argval[0].string, "no")) {
274454359Sroberto			always_auth = 0;
274554359Sroberto		} else
274654359Sroberto		    (void)fprintf(stderr, "What?\n");
274754359Sroberto	}
274854359Sroberto}
274954359Sroberto
275054359Sroberto
275154359Sroberto/*
275254359Sroberto * ntpversion - choose the NTP version to use
275354359Sroberto */
275454359Srobertostatic void
275554359Srobertontpversion(
275654359Sroberto	struct parse *pcmd,
275754359Sroberto	FILE *fp
275854359Sroberto	)
275954359Sroberto{
276054359Sroberto	if (pcmd->nargs == 0) {
276154359Sroberto		(void) fprintf(fp,
276254359Sroberto			       "NTP version being claimed is %d\n", pktversion);
276354359Sroberto	} else {
276454359Sroberto		if (pcmd->argval[0].uval < NTP_OLDVERSION
276554359Sroberto		    || pcmd->argval[0].uval > NTP_VERSION) {
276654359Sroberto			(void) fprintf(stderr, "versions %d to %d, please\n",
276754359Sroberto				       NTP_OLDVERSION, NTP_VERSION);
276854359Sroberto		} else {
276954359Sroberto			pktversion = (u_char) pcmd->argval[0].uval;
277054359Sroberto		}
277154359Sroberto	}
277254359Sroberto}
277354359Sroberto
277454359Sroberto
2775280849Scystatic void __attribute__((__format__(__printf__, 1, 0)))
2776280849Scyvwarning(const char *fmt, va_list ap)
2777280849Scy{
2778280849Scy	int serrno = errno;
2779280849Scy	(void) fprintf(stderr, "%s: ", progname);
2780280849Scy	vfprintf(stderr, fmt, ap);
2781293423Sdelphij	(void) fprintf(stderr, ": %s\n", strerror(serrno));
2782280849Scy}
2783280849Scy
278454359Sroberto/*
278554359Sroberto * warning - print a warning message
278654359Sroberto */
2787280849Scystatic void __attribute__((__format__(__printf__, 1, 2)))
278854359Srobertowarning(
278954359Sroberto	const char *fmt,
2790280849Scy	...
279154359Sroberto	)
279254359Sroberto{
2793280849Scy	va_list ap;
2794280849Scy	va_start(ap, fmt);
2795280849Scy	vwarning(fmt, ap);
2796280849Scy	va_end(ap);
279754359Sroberto}
279854359Sroberto
279954359Sroberto
280054359Sroberto/*
280154359Sroberto * error - print a message and exit
280254359Sroberto */
2803280849Scystatic void __attribute__((__format__(__printf__, 1, 2)))
280454359Srobertoerror(
280554359Sroberto	const char *fmt,
2806280849Scy	...
280754359Sroberto	)
280854359Sroberto{
2809280849Scy	va_list ap;
2810280849Scy	va_start(ap, fmt);
2811280849Scy	vwarning(fmt, ap);
2812280849Scy	va_end(ap);
281354359Sroberto	exit(1);
281454359Sroberto}
281554359Sroberto/*
281654359Sroberto * getkeyid - prompt the user for a keyid to use
281754359Sroberto */
281854359Srobertostatic u_long
281954359Srobertogetkeyid(
282054359Sroberto	const char *keyprompt
282154359Sroberto	)
282254359Sroberto{
2823280849Scy	int c;
282454359Sroberto	FILE *fi;
282554359Sroberto	char pbuf[20];
2826280849Scy	size_t i;
2827280849Scy	size_t ilim;
282854359Sroberto
282954359Sroberto#ifndef SYS_WINNT
283054359Sroberto	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
283154359Sroberto#else
2832280849Scy	if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
283354359Sroberto#endif /* SYS_WINNT */
283454359Sroberto		fi = stdin;
2835280849Scy	else
283654359Sroberto		setbuf(fi, (char *)NULL);
283754359Sroberto	fprintf(stderr, "%s", keyprompt); fflush(stderr);
2838280849Scy	for (i = 0, ilim = COUNTOF(pbuf) - 1;
2839280849Scy	     i < ilim && (c = getc(fi)) != '\n' && c != EOF;
2840280849Scy	     )
2841280849Scy		pbuf[i++] = (char)c;
2842280849Scy	pbuf[i] = '\0';
284354359Sroberto	if (fi != stdin)
2844280849Scy		fclose(fi);
284554359Sroberto
284654359Sroberto	return (u_long) atoi(pbuf);
284754359Sroberto}
284854359Sroberto
284954359Sroberto
285054359Sroberto/*
285154359Sroberto * atoascii - printable-ize possibly ascii data using the character
285254359Sroberto *	      transformations cat -v uses.
285354359Sroberto */
285454359Srobertostatic void
285554359Srobertoatoascii(
2856280849Scy	const char *in,
2857280849Scy	size_t in_octets,
2858280849Scy	char *out,
2859280849Scy	size_t out_octets
286054359Sroberto	)
286154359Sroberto{
2862280849Scy	const u_char *	pchIn;
2863280849Scy	const u_char *	pchInLimit;
2864280849Scy	u_char *	pchOut;
2865280849Scy	u_char		c;
286654359Sroberto
2867280849Scy	pchIn = (const u_char *)in;
2868280849Scy	pchInLimit = pchIn + in_octets;
2869280849Scy	pchOut = (u_char *)out;
2870280849Scy
2871280849Scy	if (NULL == pchIn) {
2872280849Scy		if (0 < out_octets)
2873280849Scy			*pchOut = '\0';
287454359Sroberto		return;
287554359Sroberto	}
287654359Sroberto
2877280849Scy#define	ONEOUT(c)					\
2878280849Scydo {							\
2879280849Scy	if (0 == --out_octets) {			\
2880280849Scy		*pchOut = '\0';				\
2881280849Scy		return;					\
2882280849Scy	}						\
2883280849Scy	*pchOut++ = (c);				\
2884280849Scy} while (0)
2885280849Scy
2886280849Scy	for (	; pchIn < pchInLimit; pchIn++) {
2887280849Scy		c = *pchIn;
2888280849Scy		if ('\0' == c)
2889280849Scy			break;
2890280849Scy		if (c & 0x80) {
2891280849Scy			ONEOUT('M');
2892280849Scy			ONEOUT('-');
2893280849Scy			c &= 0x7f;
289454359Sroberto		}
289554359Sroberto		if (c < ' ') {
2896280849Scy			ONEOUT('^');
2897280849Scy			ONEOUT((u_char)(c + '@'));
2898280849Scy		} else if (0x7f == c) {
2899280849Scy			ONEOUT('^');
2900280849Scy			ONEOUT('?');
2901280849Scy		} else
2902280849Scy			ONEOUT(c);
290354359Sroberto	}
2904280849Scy	ONEOUT('\0');
2905280849Scy
2906280849Scy#undef ONEOUT
290754359Sroberto}
290854359Sroberto
290954359Sroberto
291054359Sroberto/*
291154359Sroberto * makeascii - print possibly ascii data using the character
291254359Sroberto *	       transformations that cat -v uses.
291354359Sroberto */
2914280849Scyvoid
291554359Srobertomakeascii(
2916293423Sdelphij	size_t length,
2917280849Scy	const char *data,
291854359Sroberto	FILE *fp
291954359Sroberto	)
292054359Sroberto{
2921280849Scy	const u_char *data_u_char;
2922280849Scy	const u_char *cp;
2923280849Scy	int c;
292454359Sroberto
2925280849Scy	data_u_char = (const u_char *)data;
2926280849Scy
2927280849Scy	for (cp = data_u_char; cp < data_u_char + length; cp++) {
292854359Sroberto		c = (int)*cp;
2929280849Scy		if (c & 0x80) {
293054359Sroberto			putc('M', fp);
293154359Sroberto			putc('-', fp);
2932280849Scy			c &= 0x7f;
293354359Sroberto		}
293454359Sroberto
293554359Sroberto		if (c < ' ') {
293654359Sroberto			putc('^', fp);
2937280849Scy			putc(c + '@', fp);
2938280849Scy		} else if (0x7f == c) {
293954359Sroberto			putc('^', fp);
294054359Sroberto			putc('?', fp);
2941280849Scy		} else
294254359Sroberto			putc(c, fp);
294354359Sroberto	}
294454359Sroberto}
294554359Sroberto
294654359Sroberto
294754359Sroberto/*
294854359Sroberto * asciize - same thing as makeascii except add a newline
294954359Sroberto */
295054359Srobertovoid
295154359Srobertoasciize(
295254359Sroberto	int length,
295354359Sroberto	char *data,
295454359Sroberto	FILE *fp
295554359Sroberto	)
295654359Sroberto{
295754359Sroberto	makeascii(length, data, fp);
295854359Sroberto	putc('\n', fp);
295954359Sroberto}
296054359Sroberto
296154359Sroberto
296254359Sroberto/*
2963280849Scy * truncate string to fit clipping excess at end.
2964280849Scy *	"too long"	->	"too l"
2965280849Scy * Used for hostnames.
2966280849Scy */
2967280849Scyconst char *
2968280849Scytrunc_right(
2969280849Scy	const char *	src,
2970280849Scy	size_t		width
2971280849Scy	)
2972280849Scy{
2973280849Scy	size_t	sl;
2974280849Scy	char *	out;
2975280849Scy
2976280849Scy
2977280849Scy	sl = strlen(src);
2978280849Scy	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
2979280849Scy		LIB_GETBUF(out);
2980280849Scy		memcpy(out, src, width);
2981280849Scy		out[width] = '\0';
2982280849Scy
2983280849Scy		return out;
2984280849Scy	}
2985280849Scy
2986280849Scy	return src;
2987280849Scy}
2988280849Scy
2989280849Scy
2990280849Scy/*
2991280849Scy * truncate string to fit by preserving right side and using '_' to hint
2992280849Scy *	"too long"	->	"_long"
2993280849Scy * Used for local IPv6 addresses, where low bits differentiate.
2994280849Scy */
2995280849Scyconst char *
2996280849Scytrunc_left(
2997280849Scy	const char *	src,
2998280849Scy	size_t		width
2999280849Scy	)
3000280849Scy{
3001280849Scy	size_t	sl;
3002280849Scy	char *	out;
3003280849Scy
3004280849Scy
3005280849Scy	sl = strlen(src);
3006280849Scy	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
3007280849Scy		LIB_GETBUF(out);
3008280849Scy		out[0] = '_';
3009280849Scy		memcpy(&out[1], &src[sl + 1 - width], width);
3010280849Scy
3011280849Scy		return out;
3012280849Scy	}
3013280849Scy
3014280849Scy	return src;
3015280849Scy}
3016280849Scy
3017280849Scy
3018280849Scy/*
301954359Sroberto * Some circular buffer space
302054359Sroberto */
302154359Sroberto#define	CBLEN	80
302254359Sroberto#define	NUMCB	6
302354359Sroberto
302454359Srobertochar circ_buf[NUMCB][CBLEN];
302554359Srobertoint nextcb = 0;
302654359Sroberto
302754359Sroberto/*
302854359Sroberto * nextvar - find the next variable in the buffer
302954359Sroberto */
303054359Srobertoint
303154359Srobertonextvar(
3032293423Sdelphij	size_t *datalen,
3033280849Scy	const char **datap,
303454359Sroberto	char **vname,
303554359Sroberto	char **vvalue
303654359Sroberto	)
303754359Sroberto{
3038280849Scy	const char *cp;
3039280849Scy	const char *np;
3040280849Scy	const char *cpend;
3041280849Scy	size_t srclen;
3042280849Scy	size_t len;
304354359Sroberto	static char name[MAXVARLEN];
304454359Sroberto	static char value[MAXVALLEN];
304554359Sroberto
304654359Sroberto	cp = *datap;
304754359Sroberto	cpend = cp + *datalen;
304854359Sroberto
304954359Sroberto	/*
305054359Sroberto	 * Space past commas and white space
305154359Sroberto	 */
305254359Sroberto	while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
3053280849Scy		cp++;
3054280849Scy	if (cp >= cpend)
3055280849Scy		return 0;
3056280849Scy
305754359Sroberto	/*
305854359Sroberto	 * Copy name until we hit a ',', an '=', a '\r' or a '\n'.  Backspace
305954359Sroberto	 * over any white space and terminate it.
306054359Sroberto	 */
3061280849Scy	srclen = strcspn(cp, ",=\r\n");
3062280849Scy	srclen = min(srclen, (size_t)(cpend - cp));
3063280849Scy	len = srclen;
3064280849Scy	while (len > 0 && isspace((unsigned char)cp[len - 1]))
3065280849Scy		len--;
3066294554Sdelphij	if (len >= sizeof(name))
3067294554Sdelphij	    return 0;
3068280849Scy	if (len > 0)
3069280849Scy		memcpy(name, cp, len);
3070280849Scy	name[len] = '\0';
307154359Sroberto	*vname = name;
3072280849Scy	cp += srclen;
307354359Sroberto
307454359Sroberto	/*
307554359Sroberto	 * Check if we hit the end of the buffer or a ','.  If so we are done.
307654359Sroberto	 */
3077280849Scy	if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
3078280849Scy		if (cp < cpend)
3079280849Scy			cp++;
308054359Sroberto		*datap = cp;
3081293423Sdelphij		*datalen = size2int_sat(cpend - cp);
3082280849Scy		*vvalue = NULL;
308354359Sroberto		return 1;
308454359Sroberto	}
308554359Sroberto
308654359Sroberto	/*
308754359Sroberto	 * So far, so good.  Copy out the value
308854359Sroberto	 */
308954359Sroberto	cp++;	/* past '=' */
3090280849Scy	while (cp < cpend && (isspace((unsigned char)*cp) && *cp != '\r' && *cp != '\n'))
3091280849Scy		cp++;
3092280849Scy	np = cp;
3093280849Scy	if ('"' == *np) {
3094280849Scy		do {
3095280849Scy			np++;
3096280849Scy		} while (np < cpend && '"' != *np);
3097280849Scy		if (np < cpend && '"' == *np)
3098280849Scy			np++;
3099280849Scy	} else {
3100280849Scy		while (np < cpend && ',' != *np && '\r' != *np)
3101280849Scy			np++;
310254359Sroberto	}
3103280849Scy	len = np - cp;
3104280849Scy	if (np > cpend || len >= sizeof(value) ||
3105280849Scy	    (np < cpend && ',' != *np && '\r' != *np))
3106280849Scy		return 0;
3107280849Scy	memcpy(value, cp, len);
310854359Sroberto	/*
310954359Sroberto	 * Trim off any trailing whitespace
311054359Sroberto	 */
3111280849Scy	while (len > 0 && isspace((unsigned char)value[len - 1]))
3112280849Scy		len--;
3113280849Scy	value[len] = '\0';
311454359Sroberto
311554359Sroberto	/*
311654359Sroberto	 * Return this.  All done.
311754359Sroberto	 */
3118280849Scy	if (np < cpend && ',' == *np)
3119280849Scy		np++;
3120280849Scy	*datap = np;
3121293423Sdelphij	*datalen = size2int_sat(cpend - np);
312254359Sroberto	*vvalue = value;
312354359Sroberto	return 1;
312454359Sroberto}
312554359Sroberto
312654359Sroberto
3127280849Scyu_short
3128280849Scyvarfmt(const char * varname)
312954359Sroberto{
3130280849Scy	u_int n;
313154359Sroberto
3132280849Scy	for (n = 0; n < COUNTOF(cookedvars); n++)
3133280849Scy		if (!strcmp(varname, cookedvars[n].varname))
3134280849Scy			return cookedvars[n].fmt;
3135280849Scy
3136280849Scy	return PADDING;
313754359Sroberto}
313854359Sroberto
313954359Sroberto
314054359Sroberto/*
314154359Sroberto * printvars - print variables returned in response packet
314254359Sroberto */
314354359Srobertovoid
314454359Srobertoprintvars(
3145293423Sdelphij	size_t length,
3146280849Scy	const char *data,
314754359Sroberto	int status,
314854359Sroberto	int sttype,
3149280849Scy	int quiet,
315054359Sroberto	FILE *fp
315154359Sroberto	)
315254359Sroberto{
315354359Sroberto	if (rawmode)
3154280849Scy	    rawprint(sttype, length, data, status, quiet, fp);
315554359Sroberto	else
3156280849Scy	    cookedprint(sttype, length, data, status, quiet, fp);
315754359Sroberto}
315854359Sroberto
315954359Sroberto
316054359Sroberto/*
316154359Sroberto * rawprint - do a printout of the data in raw mode
316254359Sroberto */
316354359Srobertostatic void
316454359Srobertorawprint(
316554359Sroberto	int datatype,
3166293423Sdelphij	size_t length,
3167280849Scy	const char *data,
316854359Sroberto	int status,
3169280849Scy	int quiet,
317054359Sroberto	FILE *fp
317154359Sroberto	)
317254359Sroberto{
3173280849Scy	const char *cp;
3174280849Scy	const char *cpend;
317554359Sroberto
317654359Sroberto	/*
317754359Sroberto	 * Essentially print the data as is.  We reformat unprintables, though.
317854359Sroberto	 */
317954359Sroberto	cp = data;
318054359Sroberto	cpend = data + length;
318154359Sroberto
3182280849Scy	if (!quiet)
3183280849Scy		(void) fprintf(fp, "status=0x%04x,\n", status);
318454359Sroberto
318554359Sroberto	while (cp < cpend) {
318654359Sroberto		if (*cp == '\r') {
318754359Sroberto			/*
318854359Sroberto			 * If this is a \r and the next character is a
318954359Sroberto			 * \n, supress this, else pretty print it.  Otherwise
319054359Sroberto			 * just output the character.
319154359Sroberto			 */
3192280849Scy			if (cp == (cpend - 1) || *(cp + 1) != '\n')
319354359Sroberto			    makeascii(1, cp, fp);
3194280849Scy		} else if (isspace((unsigned char)*cp) || isprint((unsigned char)*cp))
319554359Sroberto			putc(*cp, fp);
3196280849Scy		else
319754359Sroberto			makeascii(1, cp, fp);
319854359Sroberto		cp++;
319954359Sroberto	}
320054359Sroberto}
320154359Sroberto
320254359Sroberto
320354359Sroberto/*
320454359Sroberto * Global data used by the cooked output routines
320554359Sroberto */
320654359Srobertoint out_chars;		/* number of characters output */
320754359Srobertoint out_linecount;	/* number of characters output on this line */
320854359Sroberto
320954359Sroberto
321054359Sroberto/*
321154359Sroberto * startoutput - get ready to do cooked output
321254359Sroberto */
321354359Srobertostatic void
321454359Srobertostartoutput(void)
321554359Sroberto{
321654359Sroberto	out_chars = 0;
321754359Sroberto	out_linecount = 0;
321854359Sroberto}
321954359Sroberto
322054359Sroberto
322154359Sroberto/*
322254359Sroberto * output - output a variable=value combination
322354359Sroberto */
322454359Srobertostatic void
322554359Srobertooutput(
322654359Sroberto	FILE *fp,
3227280849Scy	const char *name,
3228280849Scy	const char *value
322954359Sroberto	)
323054359Sroberto{
3231293423Sdelphij	int len;
323254359Sroberto
3233280849Scy	/* strlen of "name=value" */
3234293423Sdelphij	len = size2int_sat(strlen(name) + 1 + strlen(value));
323554359Sroberto
323654359Sroberto	if (out_chars != 0) {
3237280849Scy		out_chars += 2;
3238280849Scy		if ((out_linecount + len + 2) > MAXOUTLINE) {
3239280849Scy			fputs(",\n", fp);
324054359Sroberto			out_linecount = 0;
324154359Sroberto		} else {
3242280849Scy			fputs(", ", fp);
3243280849Scy			out_linecount += 2;
324454359Sroberto		}
324554359Sroberto	}
324654359Sroberto
324754359Sroberto	fputs(name, fp);
324854359Sroberto	putc('=', fp);
324954359Sroberto	fputs(value, fp);
3250280849Scy	out_chars += len;
3251280849Scy	out_linecount += len;
325254359Sroberto}
325354359Sroberto
325454359Sroberto
325554359Sroberto/*
325654359Sroberto * endoutput - terminate a block of cooked output
325754359Sroberto */
325854359Srobertostatic void
325954359Srobertoendoutput(
326054359Sroberto	FILE *fp
326154359Sroberto	)
326254359Sroberto{
326354359Sroberto	if (out_chars != 0)
3264280849Scy		putc('\n', fp);
326554359Sroberto}
326654359Sroberto
326754359Sroberto
326854359Sroberto/*
326954359Sroberto * outputarr - output an array of values
327054359Sroberto */
327154359Srobertostatic void
327254359Srobertooutputarr(
327354359Sroberto	FILE *fp,
327454359Sroberto	char *name,
327554359Sroberto	int narr,
327654359Sroberto	l_fp *lfp
327754359Sroberto	)
327854359Sroberto{
3279293423Sdelphij	char *bp;
3280293423Sdelphij	char *cp;
3281293423Sdelphij	size_t i;
3282293423Sdelphij	size_t len;
328354359Sroberto	char buf[256];
328454359Sroberto
328554359Sroberto	bp = buf;
328654359Sroberto	/*
328754359Sroberto	 * Hack to align delay and offset values
328854359Sroberto	 */
328954359Sroberto	for (i = (int)strlen(name); i < 11; i++)
329054359Sroberto	    *bp++ = ' ';
3291280849Scy
329254359Sroberto	for (i = narr; i > 0; i--) {
329354359Sroberto		if (i != narr)
329454359Sroberto		    *bp++ = ' ';
329554359Sroberto		cp = lfptoms(lfp, 2);
329654359Sroberto		len = strlen(cp);
329754359Sroberto		if (len > 7) {
329854359Sroberto			cp[7] = '\0';
329954359Sroberto			len = 7;
330054359Sroberto		}
330154359Sroberto		while (len < 7) {
330254359Sroberto			*bp++ = ' ';
330354359Sroberto			len++;
330454359Sroberto		}
330554359Sroberto		while (*cp != '\0')
330654359Sroberto		    *bp++ = *cp++;
330754359Sroberto		lfp++;
330854359Sroberto	}
330954359Sroberto	*bp = '\0';
331054359Sroberto	output(fp, name, buf);
331154359Sroberto}
331254359Sroberto
331354359Srobertostatic char *
331454359Srobertotstflags(
331554359Sroberto	u_long val
331654359Sroberto	)
331754359Sroberto{
3318280849Scy	register char *cp, *s;
3319280849Scy	size_t cb;
332054359Sroberto	register int i;
332154359Sroberto	register const char *sep;
332254359Sroberto
332354359Sroberto	sep = "";
3324280849Scy	s = cp = circ_buf[nextcb];
332554359Sroberto	if (++nextcb >= NUMCB)
3326280849Scy		nextcb = 0;
3327280849Scy	cb = sizeof(circ_buf[0]);
332854359Sroberto
3329280849Scy	snprintf(cp, cb, "%02lx", val);
3330280849Scy	cp += strlen(cp);
3331280849Scy	cb -= strlen(cp);
333254359Sroberto	if (!val) {
3333280849Scy		strlcat(cp, " ok", cb);
3334280849Scy		cp += strlen(cp);
3335280849Scy		cb -= strlen(cp);
333654359Sroberto	} else {
3337280849Scy		if (cb) {
3338280849Scy			*cp++ = ' ';
3339280849Scy			cb--;
3340280849Scy		}
3341280849Scy		for (i = 0; i < (int)COUNTOF(tstflagnames); i++) {
334254359Sroberto			if (val & 0x1) {
3343280849Scy				snprintf(cp, cb, "%s%s", sep,
3344280849Scy					 tstflagnames[i]);
334554359Sroberto				sep = ", ";
3346280849Scy				cp += strlen(cp);
3347280849Scy				cb -= strlen(cp);
334854359Sroberto			}
334954359Sroberto			val >>= 1;
335054359Sroberto		}
335154359Sroberto	}
3352280849Scy	if (cb)
3353280849Scy		*cp = '\0';
3354280849Scy
335554359Sroberto	return s;
335654359Sroberto}
335754359Sroberto
335854359Sroberto/*
335954359Sroberto * cookedprint - output variables in cooked mode
336054359Sroberto */
336154359Srobertostatic void
336254359Srobertocookedprint(
336354359Sroberto	int datatype,
3364293423Sdelphij	size_t length,
3365280849Scy	const char *data,
336654359Sroberto	int status,
3367280849Scy	int quiet,
336854359Sroberto	FILE *fp
336954359Sroberto	)
337054359Sroberto{
337154359Sroberto	char *name;
337254359Sroberto	char *value;
3373132451Sroberto	char output_raw;
337454359Sroberto	int fmt;
337554359Sroberto	l_fp lfp;
3376280849Scy	sockaddr_u hval;
337754359Sroberto	u_long uval;
3378280849Scy	int narr;
3379280849Scy	size_t len;
338054359Sroberto	l_fp lfparr[8];
3381280849Scy	char b[12];
3382280849Scy	char bn[2 * MAXVARLEN];
3383280849Scy	char bv[2 * MAXVALLEN];
338454359Sroberto
3385280849Scy	UNUSED_ARG(datatype);
338654359Sroberto
3387280849Scy	if (!quiet)
3388280849Scy		fprintf(fp, "status=%04x %s,\n", status,
3389280849Scy			statustoa(datatype, status));
339054359Sroberto
339154359Sroberto	startoutput();
339254359Sroberto	while (nextvar(&length, &data, &name, &value)) {
3393280849Scy		fmt = varfmt(name);
3394280849Scy		output_raw = 0;
3395280849Scy		switch (fmt) {
3396280849Scy
3397280849Scy		case PADDING:
339854359Sroberto			output_raw = '*';
3399280849Scy			break;
340054359Sroberto
3401280849Scy		case TS:
3402280849Scy			if (!decodets(value, &lfp))
3403280849Scy				output_raw = '?';
3404280849Scy			else
3405280849Scy				output(fp, name, prettydate(&lfp));
3406280849Scy			break;
340754359Sroberto
3408280849Scy		case HA:	/* fallthru */
3409280849Scy		case NA:
3410280849Scy			if (!decodenetnum(value, &hval)) {
3411280849Scy				output_raw = '?';
3412280849Scy			} else if (fmt == HA){
3413280849Scy				output(fp, name, nntohost(&hval));
3414280849Scy			} else {
3415280849Scy				output(fp, name, stoa(&hval));
3416280849Scy			}
3417280849Scy			break;
341854359Sroberto
3419280849Scy		case RF:
3420280849Scy			if (decodenetnum(value, &hval)) {
3421280849Scy				if (ISREFCLOCKADR(&hval))
3422280849Scy					output(fp, name,
3423280849Scy					       refnumtoa(&hval));
342454359Sroberto				else
3425280849Scy					output(fp, name, stoa(&hval));
3426280849Scy			} else if (strlen(value) <= 4) {
3427280849Scy				output(fp, name, value);
3428280849Scy			} else {
3429280849Scy				output_raw = '?';
3430280849Scy			}
3431280849Scy			break;
343254359Sroberto
3433280849Scy		case LP:
3434280849Scy			if (!decodeuint(value, &uval) || uval > 3) {
3435280849Scy				output_raw = '?';
3436280849Scy			} else {
3437280849Scy				b[0] = (0x2 & uval)
3438280849Scy					   ? '1'
3439280849Scy					   : '0';
3440280849Scy				b[1] = (0x1 & uval)
3441280849Scy					   ? '1'
3442280849Scy					   : '0';
3443280849Scy				b[2] = '\0';
3444280849Scy				output(fp, name, b);
344554359Sroberto			}
3446280849Scy			break;
344754359Sroberto
3448280849Scy		case OC:
3449280849Scy			if (!decodeuint(value, &uval)) {
3450280849Scy				output_raw = '?';
3451280849Scy			} else {
3452280849Scy				snprintf(b, sizeof(b), "%03lo", uval);
3453280849Scy				output(fp, name, b);
3454280849Scy			}
3455280849Scy			break;
3456280849Scy
3457280849Scy		case AR:
3458280849Scy			if (!decodearr(value, &narr, lfparr))
3459280849Scy				output_raw = '?';
3460280849Scy			else
3461280849Scy				outputarr(fp, name, narr, lfparr);
3462280849Scy			break;
3463280849Scy
3464280849Scy		case FX:
3465280849Scy			if (!decodeuint(value, &uval))
3466280849Scy				output_raw = '?';
3467280849Scy			else
3468280849Scy				output(fp, name, tstflags(uval));
3469280849Scy			break;
3470280849Scy
3471280849Scy		default:
3472280849Scy			fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
3473280849Scy				name, value, fmt);
3474280849Scy			output_raw = '?';
3475280849Scy			break;
347654359Sroberto		}
3477280849Scy
347854359Sroberto		if (output_raw != 0) {
3479289764Sglebius			/* TALOS-CAN-0063: avoid buffer overrun */
3480280849Scy			atoascii(name, MAXVARLEN, bn, sizeof(bn));
348154359Sroberto			if (output_raw != '*') {
3482289764Sglebius				atoascii(value, MAXVALLEN,
3483289764Sglebius					 bv, sizeof(bv) - 1);
348454359Sroberto				len = strlen(bv);
348554359Sroberto				bv[len] = output_raw;
348654359Sroberto				bv[len+1] = '\0';
3487289764Sglebius			} else {
3488289764Sglebius				atoascii(value, MAXVALLEN,
3489289764Sglebius					 bv, sizeof(bv));
349054359Sroberto			}
349154359Sroberto			output(fp, bn, bv);
349254359Sroberto		}
349354359Sroberto	}
349454359Sroberto	endoutput(fp);
349554359Sroberto}
349654359Sroberto
349754359Sroberto
349854359Sroberto/*
349954359Sroberto * sortassoc - sort associations in the cache into ascending order
350054359Sroberto */
350154359Srobertovoid
350254359Srobertosortassoc(void)
350354359Sroberto{
350454359Sroberto	if (numassoc > 1)
3505280849Scy		qsort(assoc_cache, (size_t)numassoc,
3506280849Scy		      sizeof(assoc_cache[0]), &assoccmp);
350754359Sroberto}
350854359Sroberto
350954359Sroberto
351054359Sroberto/*
351154359Sroberto * assoccmp - compare two associations
351254359Sroberto */
351354359Srobertostatic int
351454359Srobertoassoccmp(
351554359Sroberto	const void *t1,
351654359Sroberto	const void *t2
351754359Sroberto	)
351854359Sroberto{
3519280849Scy	const struct association *ass1 = t1;
3520280849Scy	const struct association *ass2 = t2;
352154359Sroberto
352254359Sroberto	if (ass1->assid < ass2->assid)
3523280849Scy		return -1;
352454359Sroberto	if (ass1->assid > ass2->assid)
3525280849Scy		return 1;
352654359Sroberto	return 0;
352754359Sroberto}
3528280849Scy
3529280849Scy
3530280849Scy/*
3531280849Scy * grow_assoc_cache() - enlarge dynamic assoc_cache array
3532280849Scy *
3533280849Scy * The strategy is to add an assumed 4k page size at a time, leaving
3534280849Scy * room for malloc() bookkeeping overhead equivalent to 4 pointers.
3535280849Scy */
3536280849Scyvoid
3537280849Scygrow_assoc_cache(void)
3538280849Scy{
3539280849Scy	static size_t	prior_sz;
3540280849Scy	size_t		new_sz;
3541280849Scy
3542280849Scy	new_sz = prior_sz + 4 * 1024;
3543280849Scy	if (0 == prior_sz) {
3544280849Scy		new_sz -= 4 * sizeof(void *);
3545280849Scy	}
3546280849Scy	assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz);
3547280849Scy	prior_sz = new_sz;
3548293423Sdelphij	assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0]));
3549280849Scy}
3550280849Scy
3551280849Scy
3552280849Scy/*
3553280849Scy * ntpq_custom_opt_handler - autoopts handler for -c and -p
3554280849Scy *
3555280849Scy * By default, autoopts loses the relative order of -c and -p options
3556280849Scy * on the command line.  This routine replaces the default handler for
3557280849Scy * those routines and builds a list of commands to execute preserving
3558280849Scy * the order.
3559280849Scy */
3560280849Scyvoid
3561280849Scyntpq_custom_opt_handler(
3562280849Scy	tOptions *pOptions,
3563280849Scy	tOptDesc *pOptDesc
356454359Sroberto	)
356554359Sroberto{
3566280849Scy	switch (pOptDesc->optValue) {
3567280849Scy
3568280849Scy	default:
3569280849Scy		fprintf(stderr,
3570280849Scy			"ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3571280849Scy			pOptDesc->optValue, pOptDesc->optValue);
3572280849Scy		exit(1);
3573280849Scy
3574280849Scy	case 'c':
3575280849Scy		ADDCMD(pOptDesc->pzLastArg);
3576280849Scy		break;
3577280849Scy
3578280849Scy	case 'p':
3579280849Scy		ADDCMD("peers");
3580280849Scy		break;
3581280849Scy	}
358254359Sroberto}
3583285169Scy/*
3584285169Scy * Obtain list of digest names
3585285169Scy */
3586285169Scy
3587285169Scy#ifdef OPENSSL
3588285169Scy# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3589285169Scystruct hstate {
3590285169Scy   char *list;
3591285169Scy   const char **seen;
3592285169Scy   int idx;
3593285169Scy};
3594285169Scy#define K_PER_LINE 8
3595285169Scy#define K_NL_PFX_STR "\n    "
3596285169Scy#define K_DELIM_STR ", "
3597285169Scystatic void list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg )
3598285169Scy{
3599285169Scy    size_t len, n;
3600285169Scy    const char *name, *cp, **seen;
3601285169Scy    struct hstate *hstate = arg;
3602310419Sdelphij    EVP_MD_CTX *ctx;
3603285169Scy    u_int digest_len;
3604285169Scy    u_char digest[EVP_MAX_MD_SIZE];
3605285169Scy
3606285169Scy    if (!m)
3607285169Scy        return; /* Ignore aliases */
3608285169Scy
3609285169Scy    name = EVP_MD_name(m);
3610285169Scy
3611285169Scy    /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */
3612285169Scy
3613285169Scy    for( cp = name; *cp; cp++ ) {
3614316722Sdelphij	if( islower((unsigned char)*cp) )
3615285169Scy	    return;
3616285169Scy    }
3617285169Scy    len = (cp - name) + 1;
3618285169Scy
3619285169Scy    /* There are duplicates.  Discard if name has been seen. */
3620285169Scy
3621285169Scy    for (seen = hstate->seen; *seen; seen++)
3622285169Scy        if (!strcmp(*seen, name))
3623285169Scy	    return;
3624285169Scy    n = (seen - hstate->seen) + 2;
3625289764Sglebius    hstate->seen = erealloc(hstate->seen, n * sizeof(*seen));
3626285169Scy    hstate->seen[n-2] = name;
3627285169Scy    hstate->seen[n-1] = NULL;
3628285169Scy
3629285169Scy    /* Discard MACs that NTP won't accept.
3630285169Scy     * Keep this consistent with keytype_from_text() in ssl_init.c.
3631285169Scy     */
3632285169Scy
3633310419Sdelphij    ctx = EVP_MD_CTX_new();
3634310419Sdelphij    EVP_DigestInit(ctx, EVP_get_digestbyname(name));
3635310419Sdelphij    EVP_DigestFinal(ctx, digest, &digest_len);
3636310419Sdelphij    EVP_MD_CTX_free(ctx);
3637285169Scy    if (digest_len > (MAX_MAC_LEN - sizeof(keyid_t)))
3638285169Scy        return;
3639285169Scy
3640285169Scy    if (hstate->list != NULL)
3641285169Scy	len += strlen(hstate->list);
3642285169Scy    len += (hstate->idx >= K_PER_LINE)? strlen(K_NL_PFX_STR): strlen(K_DELIM_STR);
3643285169Scy
3644285169Scy    if (hstate->list == NULL) {
3645289764Sglebius	hstate->list = (char *)emalloc(len);
3646285169Scy	hstate->list[0] = '\0';
3647285169Scy    } else
3648289764Sglebius	hstate->list = (char *)erealloc(hstate->list, len);
3649285169Scy
3650285169Scy    sprintf(hstate->list + strlen(hstate->list), "%s%s",
3651285169Scy	    ((hstate->idx >= K_PER_LINE)? K_NL_PFX_STR : K_DELIM_STR),
3652285169Scy	    name);
3653285169Scy    if (hstate->idx >= K_PER_LINE)
3654285169Scy	hstate->idx = 1;
3655285169Scy    else
3656285169Scy	hstate->idx++;
3657285169Scy}
3658285169Scy# endif
3659285169Scy#endif
3660285169Scy
3661285169Scystatic char *list_digest_names(void)
3662285169Scy{
3663285169Scy    char *list = NULL;
3664285169Scy
3665285169Scy#ifdef OPENSSL
3666285169Scy# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3667285169Scy    struct hstate hstate = { NULL, NULL, K_PER_LINE+1 };
3668285169Scy
3669289764Sglebius    hstate.seen = (const char **) emalloc_zero(1*sizeof( const char * )); // replaces -> calloc(1, sizeof( const char * ));
3670285169Scy
3671285169Scy    INIT_SSL();
3672285169Scy    EVP_MD_do_all_sorted(list_md_fn, &hstate);
3673285169Scy    list = hstate.list;
3674285169Scy    free(hstate.seen);
3675285169Scy# else
3676289764Sglebius    list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)"));
3677285169Scy    strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)");
3678285169Scy# endif
3679285169Scy#else
3680289764Sglebius    list = (char *)emalloc(sizeof("md5"));
3681285169Scy    strcpy(list, "md5");
3682285169Scy#endif
3683285169Scy
3684285169Scy    return list;
3685285169Scy}
3686293423Sdelphij
3687293423Sdelphij#define CTRLC_STACK_MAX 4
3688293423Sdelphijstatic volatile size_t		ctrlc_stack_len = 0;
3689293423Sdelphijstatic volatile Ctrl_C_Handler	ctrlc_stack[CTRLC_STACK_MAX];
3690293423Sdelphij
3691293423Sdelphij
3692293423Sdelphij
3693293423Sdelphijint/*BOOL*/
3694293423Sdelphijpush_ctrl_c_handler(
3695293423Sdelphij	Ctrl_C_Handler func
3696293423Sdelphij	)
3697293423Sdelphij{
3698293423Sdelphij	size_t size = ctrlc_stack_len;
3699293423Sdelphij	if (func && (size < CTRLC_STACK_MAX)) {
3700293423Sdelphij		ctrlc_stack[size] = func;
3701293423Sdelphij		ctrlc_stack_len = size + 1;
3702293423Sdelphij		return TRUE;
3703293423Sdelphij	}
3704293423Sdelphij	return FALSE;
3705293423Sdelphij}
3706293423Sdelphij
3707293423Sdelphijint/*BOOL*/
3708293423Sdelphijpop_ctrl_c_handler(
3709293423Sdelphij	Ctrl_C_Handler func
3710293423Sdelphij	)
3711293423Sdelphij{
3712293423Sdelphij	size_t size = ctrlc_stack_len;
3713293423Sdelphij	if (size) {
3714293423Sdelphij		--size;
3715293423Sdelphij		if (func == NULL || func == ctrlc_stack[size]) {
3716293423Sdelphij			ctrlc_stack_len = size;
3717293423Sdelphij			return TRUE;
3718293423Sdelphij		}
3719293423Sdelphij	}
3720293423Sdelphij	return FALSE;
3721293423Sdelphij}
3722293423Sdelphij
3723293423Sdelphijstatic void
3724293423Sdelphijon_ctrlc(void)
3725293423Sdelphij{
3726293423Sdelphij	size_t size = ctrlc_stack_len;
3727293423Sdelphij	while (size)
3728293423Sdelphij		if ((*ctrlc_stack[--size])())
3729293423Sdelphij			break;
3730293423Sdelphij}
3731294554Sdelphij
3732294554Sdelphijstatic int
3733294554Sdelphijmy_easprintf(
3734294554Sdelphij	char ** 	ppinto,
3735294554Sdelphij	const char *	fmt   ,
3736294554Sdelphij	...
3737294554Sdelphij	)
3738294554Sdelphij{
3739294554Sdelphij	va_list	va;
3740294554Sdelphij	int	prc;
3741294554Sdelphij	size_t	len = 128;
3742294554Sdelphij	char *	buf = emalloc(len);
3743294554Sdelphij
3744294554Sdelphij  again:
3745294554Sdelphij	/* Note: we expect the memory allocation to fail long before the
3746294554Sdelphij	 * increment in buffer size actually overflows.
3747294554Sdelphij	 */
3748294554Sdelphij	buf = (buf) ? erealloc(buf, len) : emalloc(len);
3749294554Sdelphij
3750294554Sdelphij	va_start(va, fmt);
3751294554Sdelphij	prc = vsnprintf(buf, len, fmt, va);
3752294554Sdelphij	va_end(va);
3753294554Sdelphij
3754294554Sdelphij	if (prc < 0) {
3755294554Sdelphij		/* might be very old vsnprintf. Or actually MSVC... */
3756294554Sdelphij		len += len >> 1;
3757294554Sdelphij		goto again;
3758294554Sdelphij	}
3759294554Sdelphij	if ((size_t)prc >= len) {
3760294554Sdelphij		/* at least we have the proper size now... */
3761294554Sdelphij		len = (size_t)prc + 1;
3762294554Sdelphij		goto again;
3763294554Sdelphij	}
3764294554Sdelphij	if ((size_t)prc < (len - 32))
3765294554Sdelphij		buf = erealloc(buf, (size_t)prc + 1);
3766294554Sdelphij	*ppinto = buf;
3767294554Sdelphij	return prc;
3768294554Sdelphij}
3769