154359Sroberto/*
254359Sroberto * ntpq - query an NTP server using mode 6 commands
354359Sroberto */
4290001Sglebius#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>
11290001Sglebius#ifdef HAVE_UNISTD_H
12290001Sglebius# include <unistd.h>
13290001Sglebius#endif
14290001Sglebius#ifdef HAVE_FCNTL_H
15290001Sglebius# include <fcntl.h>
16290001Sglebius#endif
17290001Sglebius#ifdef SYS_WINNT
18290001Sglebius# include <mswsock.h>
19290001Sglebius#endif
20290001Sglebius#include <isc/net.h>
21290001Sglebius#include <isc/result.h>
22182007Sroberto
2382498Sroberto#include "ntpq.h"
24290001Sglebius#include "ntp_assert.h"
25290001Sglebius#include "ntp_stdlib.h"
2682498Sroberto#include "ntp_unixtime.h"
2782498Sroberto#include "ntp_calendar.h"
2882498Sroberto#include "ntp_select.h"
29290001Sglebius#include "ntp_assert.h"
30290001Sglebius#include "lib_strbuf.h"
31290001Sglebius#include "ntp_lineedit.h"
32290001Sglebius#include "ntp_debug.h"
33290001Sglebius#ifdef OPENSSL
34290001Sglebius#include "openssl/evp.h"
35290001Sglebius#include "openssl/objects.h"
36290001Sglebius#include "openssl/err.h"
37310419Sdelphij#include "libssl_compat.h"
38290001Sglebius#endif
39290001Sglebius#include <ssl_applink.c>
4082498Sroberto
41290001Sglebius#include "ntp_libopts.h"
42293896Sglebius#include "safecast.h"
43182007Sroberto
44290001Sglebius#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
61290001Sglebius/*
62290001Sglebius * use old readvars behavior?  --old-rv processing in ntpq resets
63290001Sglebius * this value based on the presence or absence of --old-rv.  It is
64290001Sglebius * initialized to 1 here to maintain backward compatibility with
65290001Sglebius * libntpq clients such as ntpsnmpd, which are free to reset it as
66290001Sglebius * desired.
67290001Sglebius */
68290001Sglebiusint	old_rv = 1;
6954359Sroberto
70298770Sdelphij/*
71298770Sdelphij * How should we display the refid?
72298770Sdelphij * REFID_HASH, REFID_IPV4
73298770Sdelphij */
74298770Sdelphijte_Refid drefid = -1;
75290001Sglebius
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
86290001Sglebiusstatic	int	info_auth_keytype = NID_md5;	/* MD5 */
87290001Sglebiusstatic	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
114290001Sglebius#define	HA	1	/* host address */
115290001Sglebius#define	NA	2	/* network address */
116290001Sglebius#define	LP	3	/* leap (print in binary) */
117290001Sglebius#define	RF	4	/* refid (sometimes string, sometimes not) */
118290001Sglebius#define	AR	5	/* array of times */
119290001Sglebius#define FX	6	/* test flags */
120290001Sglebius#define TS	7	/* l_fp timestamp in hex */
121290001Sglebius#define	OC	8	/* integer, print in octal */
12254359Sroberto#define	EOV	255	/* end of table */
12354359Sroberto
12454359Sroberto/*
125290001Sglebius * For the most part ntpq simply displays what ntpd provides in the
126290001Sglebius * mostly plain-text mode 6 responses.  A few variable names are by
127290001Sglebius * default "cooked" to provide more human-friendly output.
12854359Sroberto */
129290001Sglebiusconst var_format cookedvars[] = {
130290001Sglebius	{ "leap",		LP },
131290001Sglebius	{ "reach",		OC },
132290001Sglebius	{ "refid",		RF },
133290001Sglebius	{ "reftime",		TS },
134290001Sglebius	{ "clock",		TS },
135290001Sglebius	{ "org",		TS },
136290001Sglebius	{ "rec",		TS },
137290001Sglebius	{ "xmt",		TS },
138290001Sglebius	{ "flash",		FX },
139290001Sglebius	{ "srcadr",		HA },
140290001Sglebius	{ "peeradr",		HA },	/* compat with others */
141290001Sglebius	{ "dstadr",		NA },
142290001Sglebius	{ "filtdelay",		AR },
143290001Sglebius	{ "filtoffset",		AR },
144290001Sglebius	{ "filtdisp",		AR },
145290001Sglebius	{ "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 */
156290001Sglebius	"pkt_unsync",		/* TEST3 */
157182007Sroberto	"pkt_denied",		/* TEST4 */
158182007Sroberto	"pkt_auth",		/* TEST5 */
159290001Sglebius	"pkt_stratum",		/* TEST6 */
160290001Sglebius	"pkt_header",		/* TEST7 */
161182007Sroberto	"pkt_autokey",		/* TEST8 */
162182007Sroberto	"pkt_crypto",		/* TEST9 */
163182007Sroberto	"peer_stratum",		/* TEST10 */
164182007Sroberto	"peer_dist",		/* TEST11 */
165182007Sroberto	"peer_loop",		/* TEST12 */
166290001Sglebius	"peer_unreach"		/* TEST13 */
16754359Sroberto};
16854359Sroberto
16954359Sroberto
170290001Sglebiusint		ntpqmain	(int,	char **);
17154359Sroberto/*
17254359Sroberto * Built in command handler declarations
17354359Sroberto */
174290001Sglebiusstatic	int	openhost	(const char *, int);
175290001Sglebiusstatic	void	dump_hex_printable(const void *, size_t);
176290001Sglebiusstatic	int	sendpkt		(void *, size_t);
177293896Sglebiusstatic	int	getresponse	(int, int, u_short *, size_t *, const char **, int);
178293896Sglebiusstatic	int	sendrequest	(int, associd_t, int, size_t, const char *);
179290001Sglebiusstatic	char *	tstflags	(u_long);
180290001Sglebius#ifndef BUILD_AS_LIB
181290001Sglebiusstatic	void	getcmds		(void);
182290001Sglebius#ifndef SYS_WINNT
183293896Sglebiusstatic	int	abortcmd	(void);
184290001Sglebius#endif	/* SYS_WINNT */
185290001Sglebiusstatic	void	docmd		(const char *);
186290001Sglebiusstatic	void	tokenize	(const char *, char **, int *);
187290001Sglebiusstatic	int	getarg		(const char *, int, arg_v *);
188290001Sglebius#endif	/* BUILD_AS_LIB */
189290001Sglebiusstatic	int	findcmd		(const char *, struct xcmd *,
190290001Sglebius				 struct xcmd *, struct xcmd **);
191290001Sglebiusstatic	int	rtdatetolfp	(char *, l_fp *);
192290001Sglebiusstatic	int	decodearr	(char *, int *, l_fp *);
193290001Sglebiusstatic	void	help		(struct parse *, FILE *);
194290001Sglebiusstatic	int	helpsort	(const void *, const void *);
195290001Sglebiusstatic	void	printusage	(struct xcmd *, FILE *);
196290001Sglebiusstatic	void	timeout		(struct parse *, FILE *);
197290001Sglebiusstatic	void	auth_delay	(struct parse *, FILE *);
198290001Sglebiusstatic	void	host		(struct parse *, FILE *);
199290001Sglebiusstatic	void	ntp_poll	(struct parse *, FILE *);
200290001Sglebiusstatic	void	keyid		(struct parse *, FILE *);
201290001Sglebiusstatic	void	keytype		(struct parse *, FILE *);
202290001Sglebiusstatic	void	passwd		(struct parse *, FILE *);
203290001Sglebiusstatic	void	hostnames	(struct parse *, FILE *);
204290001Sglebiusstatic	void	setdebug	(struct parse *, FILE *);
205290001Sglebiusstatic	void	quit		(struct parse *, FILE *);
206298770Sdelphijstatic	void	showdrefid	(struct parse *, FILE *);
207290001Sglebiusstatic	void	version		(struct parse *, FILE *);
208290001Sglebiusstatic	void	raw		(struct parse *, FILE *);
209290001Sglebiusstatic	void	cooked		(struct parse *, FILE *);
210290001Sglebiusstatic	void	authenticate	(struct parse *, FILE *);
211290001Sglebiusstatic	void	ntpversion	(struct parse *, FILE *);
212290001Sglebiusstatic	void	warning		(const char *, ...)
213290001Sglebius    __attribute__((__format__(__printf__, 1, 2)));
214290001Sglebiusstatic	void	error		(const char *, ...)
215290001Sglebius    __attribute__((__format__(__printf__, 1, 2)));
216290001Sglebiusstatic	u_long	getkeyid	(const char *);
217290001Sglebiusstatic	void	atoascii	(const char *, size_t, char *, size_t);
218293896Sglebiusstatic	void	cookedprint	(int, size_t, const char *, int, int, FILE *);
219293896Sglebiusstatic	void	rawprint	(int, size_t, const char *, int, int, FILE *);
220290001Sglebiusstatic	void	startoutput	(void);
221290001Sglebiusstatic	void	output		(FILE *, const char *, const char *);
222290001Sglebiusstatic	void	endoutput	(FILE *);
223290001Sglebiusstatic	void	outputarr	(FILE *, char *, int, l_fp *);
224290001Sglebiusstatic	int	assoccmp	(const void *, const void *);
225293896Sglebiusstatic	void	on_ctrlc	(void);
226290001Sglebius	u_short	varfmt		(const char *);
227294905Sdelphijstatic	int	my_easprintf	(char**, const char *, ...) NTP_PRINTF(2, 3);
228290001Sglebiusvoid	ntpq_custom_opt_handler	(tOptions *, tOptDesc *);
229290001Sglebius
230290001Sglebius#ifdef OPENSSL
231290001Sglebius# ifdef HAVE_EVP_MD_DO_ALL_SORTED
232290001Sglebiusstatic void list_md_fn(const EVP_MD *m, const char *from,
233290001Sglebius		       const char *to, void *arg );
234290001Sglebius# endif
23554359Sroberto#endif
236290001Sglebiusstatic char *list_digest_names(void);
23754359Sroberto
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" },
260290001Sglebius	{ "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" },
278298770Sdelphij	{ "drefid",	showdrefid,	{ OPT|NTP_STR, NO, NO, NO },
279298770Sdelphij	  { "hash|ipv4", "", "", "" },
280298770Sdelphij	  "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 },
297290001Sglebius	  { "key type %s", "", "", "" },
298290001Sglebius	  NULL },
29954359Sroberto	{ 0,		0,		{ NO, NO, NO, NO },
30054359Sroberto	  { "", "", "", "" }, "" }
30154359Sroberto};
30254359Sroberto
30354359Sroberto
30454359Sroberto/*
30554359Sroberto * Default values we use.
30654359Sroberto */
307290001Sglebius#define	DEFHOST		"localhost"	/* default host name */
308290001Sglebius#define	DEFTIMEOUT	5		/* wait 5 seconds for 1st pkt */
309290001Sglebius#define	DEFSTIMEOUT	3		/* and 3 more for each additional */
310290001Sglebius/*
311290001Sglebius * Requests are automatically retried once, so total timeout with no
312290001Sglebius * response is a bit over 2 * DEFTIMEOUT, or 10 seconds.  At the other
313290001Sglebius * extreme, a request eliciting 32 packets of responses each for some
314290001Sglebius * reason nearly DEFSTIMEOUT seconds after the prior in that series,
315290001Sglebius * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
316290001Sglebius * 93 seconds to fail each of two times, or 186 seconds.
317290001Sglebius * Some commands involve a series of requests, such as "peers" and
318290001Sglebius * "mrulist", so the cumulative timeouts are even longer for those.
319290001Sglebius */
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 */
327290001Sglebius#define	MAXVALLEN	2048		/* maximum length of a variable value */
32854359Sroberto#define	MAXOUTLINE	72		/* maximum length of an output line */
329290001Sglebius#define SCREENWIDTH	76		/* nominal screen width in columns */
33054359Sroberto
33154359Sroberto/*
33254359Sroberto * Some variables used and manipulated locally
33354359Sroberto */
334290001Sglebiusstruct sock_timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
335290001Sglebiusstruct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
33654359Srobertol_fp delay_time;				/* delay time */
33754359Srobertochar currenthost[LENHOSTNAME];			/* current host name */
338290001Sglebiusint currenthostisnum;				/* is prior text from IP? */
339290001Sglebiusstruct sockaddr_in hostaddr;			/* host address */
34054359Srobertoint showhostnames = 1;				/* show host names by default */
341290001Sglebiusint 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/*
365290001Sglebius * assoc_cache[] is a dynamic array which allows references to
366290001Sglebius * associations using &1 ... &N for n associations, avoiding manual
367290001Sglebius * lookup of the current association IDs for a given ntpd.  It also
368290001Sglebius * caches the status word for each association, retrieved incidentally.
36954359Sroberto */
370290001Sglebiusstruct association *	assoc_cache;
371290001Sglebiusu_int assoc_cache_slots;/* count of allocated array entries */
372290001Sglebiusu_int numassoc;		/* number of cached associations */
37354359Sroberto
37454359Sroberto/*
37554359Sroberto * For commands typed on the command line (with the -c option)
37654359Sroberto */
37754359Srobertoint 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
385290001Sglebiusu_int numhosts;
38654359Sroberto
387290001Sglebiuschost chosts[MAXHOSTS];
388290001Sglebius#define	ADDHOST(cp)						\
389290001Sglebius	do {							\
390290001Sglebius		if (numhosts < MAXHOSTS) {			\
391290001Sglebius			chosts[numhosts].name = (cp);		\
392290001Sglebius			chosts[numhosts].fam = ai_fam_templ;	\
393290001Sglebius			numhosts++;				\
394290001Sglebius		}						\
395290001Sglebius	} while (0)
396290001Sglebius
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
419290001Sglebiuschar const *progname;
42054359Sroberto
42154359Sroberto#ifdef NO_MAIN_ALLOWED
422290001Sglebius#ifndef BUILD_AS_LIB
42354359SrobertoCALL(ntpq,"ntpq",ntpqmain);
42454359Sroberto
42554359Srobertovoid clear_globals(void)
42654359Sroberto{
427290001Sglebius	extern int ntp_optind;
428290001Sglebius	showhostnames = 0;	/* don'tshow host names by default */
429290001Sglebius	ntp_optind = 0;
430290001Sglebius	server_entry = NULL;	/* server entry for ntp */
431290001Sglebius	havehost = 0;		/* set to 1 when host open */
432290001Sglebius	numassoc = 0;		/* number of cached associations */
433290001Sglebius	numcmds = 0;
434290001Sglebius	numhosts = 0;
43554359Sroberto}
436290001Sglebius#endif /* !BUILD_AS_LIB */
437290001Sglebius#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
453290001Sglebius#ifndef BUILD_AS_LIB
45454359Srobertoint
45554359Srobertontpqmain(
45654359Sroberto	int argc,
45754359Sroberto	char *argv[]
45854359Sroberto	)
45954359Sroberto{
460290001Sglebius	u_int ihost;
461290001Sglebius	int icmd;
46254359Sroberto
463290001Sglebius
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
472290001Sglebius	init_lib();	/* sets up ipv4_works, ipv6_works */
473290001Sglebius	ssl_applink();
474290001Sglebius	init_auth();
475290001Sglebius
476290001Sglebius	/* Check to see if we have IPv6. Otherwise default to IPv4 */
477290001Sglebius	if (!ipv6_works)
478290001Sglebius		ai_fam_default = AF_INET;
479290001Sglebius
480290001Sglebius	/* Fixup keytype's help based on available digest names */
481290001Sglebius
482132451Sroberto	{
483290001Sglebius	    char *list;
484294905Sdelphij	    char *msg;
485132451Sroberto
486290001Sglebius	    list = list_digest_names();
487290001Sglebius	    for (icmd = 0; icmd < sizeof(builtins)/sizeof(builtins[0]); icmd++) {
488290001Sglebius		if (strcmp("keytype", builtins[icmd].keyword) == 0)
489290001Sglebius		    break;
490290001Sglebius	    }
491290001Sglebius
492290001Sglebius	    /* CID: 1295478 */
493290001Sglebius	    /* This should only "trip" if "keytype" is removed from builtins */
494290001Sglebius	    INSIST(icmd < sizeof(builtins)/sizeof(builtins[0]));
495290001Sglebius
496290001Sglebius#ifdef OPENSSL
497290001Sglebius	    builtins[icmd].desc[0] = "digest-name";
498294905Sdelphij	    my_easprintf(&msg,
499294905Sdelphij			 "set key type to use for authenticated requests, one of:%s",
500294905Sdelphij			 list);
501290001Sglebius#else
502290001Sglebius	    builtins[icmd].desc[0] = "md5";
503294905Sdelphij	    my_easprintf(&msg,
504294905Sdelphij			 "set key type to use for authenticated requests (%s)",
505294905Sdelphij			 list);
506290001Sglebius#endif
507290001Sglebius	    builtins[icmd].comment = msg;
508290001Sglebius	    free(list);
509132451Sroberto	}
510132451Sroberto
51154359Sroberto	progname = argv[0];
512182007Sroberto
513182007Sroberto	{
514290001Sglebius		int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
515182007Sroberto		argc -= optct;
516182007Sroberto		argv += optct;
517182007Sroberto	}
518182007Sroberto
519290001Sglebius	/*
520290001Sglebius	 * Process options other than -c and -p, which are specially
521290001Sglebius	 * handled by ntpq_custom_opt_handler().
522290001Sglebius	 */
523290001Sglebius
524290001Sglebius	debug = OPT_VALUE_SET_DEBUG_LEVEL;
525290001Sglebius
526290001Sglebius	if (HAVE_OPT(IPV4))
527182007Sroberto		ai_fam_templ = AF_INET;
528290001Sglebius	else if (HAVE_OPT(IPV6))
529182007Sroberto		ai_fam_templ = AF_INET6;
530290001Sglebius	else
531182007Sroberto		ai_fam_templ = ai_fam_default;
532182007Sroberto
533290001Sglebius	if (HAVE_OPT(INTERACTIVE))
534182007Sroberto		interactive = 1;
535182007Sroberto
536290001Sglebius	if (HAVE_OPT(NUMERIC))
537182007Sroberto		showhostnames = 0;
538182007Sroberto
539290001Sglebius	if (HAVE_OPT(WIDE))
540290001Sglebius		wideremote = 1;
541182007Sroberto
542290001Sglebius	old_rv = HAVE_OPT(OLD_RV);
543290001Sglebius
544298770Sdelphij	drefid = OPT_VALUE_REFID;
545298770Sdelphij
546290001Sglebius	if (0 == argc) {
54754359Sroberto		ADDHOST(DEFHOST);
54854359Sroberto	} else {
549290001Sglebius		for (ihost = 0; ihost < (u_int)argc; ihost++) {
550290001Sglebius			if ('-' == *argv[ihost]) {
551290001Sglebius				//
552290001Sglebius				// If I really cared I'd also check:
553290001Sglebius				// 0 == argv[ihost][2]
554290001Sglebius				//
555290001Sglebius				// and there are other cases as well...
556290001Sglebius				//
557290001Sglebius				if ('4' == argv[ihost][1]) {
558290001Sglebius					ai_fam_templ = AF_INET;
559290001Sglebius					continue;
560290001Sglebius				} else if ('6' == argv[ihost][1]) {
561290001Sglebius					ai_fam_templ = AF_INET6;
562290001Sglebius					continue;
563290001Sglebius				} else {
564290001Sglebius					// XXX Throw a usage error
565290001Sglebius				}
566290001Sglebius			}
567290001Sglebius			ADDHOST(argv[ihost]);
568290001Sglebius		}
56954359Sroberto	}
57054359Sroberto
57154359Sroberto	if (numcmds == 0 && interactive == 0
57254359Sroberto	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
57354359Sroberto		interactive = 1;
57454359Sroberto	}
57554359Sroberto
576293896Sglebius	set_ctrl_c_hook(on_ctrlc);
57754359Sroberto#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
57854359Sroberto	if (interactive)
579293896Sglebius		push_ctrl_c_handler(abortcmd);
58054359Sroberto#endif /* SYS_WINNT */
58154359Sroberto
58254359Sroberto	if (numcmds == 0) {
583290001Sglebius		(void) openhost(chosts[0].name, chosts[0].fam);
58454359Sroberto		getcmds();
58554359Sroberto	} else {
58654359Sroberto		for (ihost = 0; ihost < numhosts; ihost++) {
587290001Sglebius			if (openhost(chosts[ihost].name, chosts[ihost].fam))
588290001Sglebius				for (icmd = 0; icmd < numcmds; icmd++)
589290001Sglebius					docmd(ccmds[icmd]);
59054359Sroberto		}
59154359Sroberto	}
59254359Sroberto#ifdef SYS_WINNT
59354359Sroberto	WSACleanup();
59454359Sroberto#endif /* SYS_WINNT */
59554359Sroberto	return 0;
59654359Sroberto}
597290001Sglebius#endif /* !BUILD_AS_LIB */
59854359Sroberto
59954359Sroberto/*
60054359Sroberto * openhost - open a socket to a host
60154359Sroberto */
602290001Sglebiusstatic	int
60354359Srobertoopenhost(
604290001Sglebius	const char *hname,
605290001Sglebius	int	    fam
60654359Sroberto	)
60754359Sroberto{
608290001Sglebius	const char svc[] = "ntp";
60954359Sroberto	char temphost[LENHOSTNAME];
610132451Sroberto	int a_info, i;
611290001Sglebius	struct addrinfo hints, *ai;
612290001Sglebius	sockaddr_u addr;
613290001Sglebius	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	 */
620290001Sglebius
621132451Sroberto	cp = hname;
622290001Sglebius
623290001Sglebius	if (*cp == '[') {
624132451Sroberto		cp++;
625290001Sglebius		for (i = 0; *cp && *cp != ']'; cp++, i++)
626132451Sroberto			name[i] = *cp;
627290001Sglebius		if (*cp == ']') {
628290001Sglebius			name[i] = '\0';
629290001Sglebius			hname = name;
630290001Sglebius		} else {
631290001Sglebius			return 0;
632290001Sglebius		}
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	 */
642290001Sglebius	ZERO(hints);
643290001Sglebius	hints.ai_family = fam;
644132451Sroberto	hints.ai_protocol = IPPROTO_UDP;
645132451Sroberto	hints.ai_socktype = SOCK_DGRAM;
646290001Sglebius	hints.ai_flags = Z_AI_NUMERICHOST;
647290001Sglebius	ai = NULL;
648132451Sroberto
649290001Sglebius	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
659290001Sglebius		a_info = getaddrinfo(hname, svc, &hints, &ai);
660132451Sroberto	}
661290001Sglebius#ifdef AI_ADDRCONFIG
662132451Sroberto	/* Some older implementations don't like AI_ADDRCONFIG. */
663132451Sroberto	if (a_info == EAI_BADFLAGS) {
664290001Sglebius		hints.ai_flags &= ~AI_ADDRCONFIG;
665290001Sglebius		a_info = getaddrinfo(hname, svc, &hints, &ai);
666132451Sroberto	}
667290001Sglebius#endif
668132451Sroberto	if (a_info != 0) {
669290001Sglebius		fprintf(stderr, "%s\n", gai_strerror(a_info));
670132451Sroberto		return 0;
671132451Sroberto	}
672132451Sroberto
673290001Sglebius	INSIST(ai != NULL);
674290001Sglebius	ZERO(addr);
675290001Sglebius	octets = min(sizeof(addr), ai->ai_addrlen);
676290001Sglebius	memcpy(&addr, ai->ai_addr, octets);
677290001Sglebius
678132451Sroberto	if (ai->ai_canonname == NULL) {
679290001Sglebius		strlcpy(temphost, stoa(&addr), sizeof(temphost));
680290001Sglebius		currenthostisnum = TRUE;
681132451Sroberto	} else {
682290001Sglebius		strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
683290001Sglebius		currenthostisnum = FALSE;
684132451Sroberto	}
685132451Sroberto
68654359Sroberto	if (debug > 2)
687290001Sglebius		printf("Opening host %s (%s)\n",
688290001Sglebius			temphost,
689290001Sglebius			(ai->ai_family == AF_INET)
690290001Sglebius			? "AF_INET"
691290001Sglebius			: (ai->ai_family == AF_INET6)
692290001Sglebius			  ? "AF_INET6"
693290001Sglebius			  : "AF-???"
694290001Sglebius			);
69554359Sroberto
69654359Sroberto	if (havehost == 1) {
69754359Sroberto		if (debug > 2)
698290001Sglebius			printf("Closing old host %s\n", currenthost);
699290001Sglebius		closesocket(sockfd);
70054359Sroberto		havehost = 0;
70154359Sroberto	}
702290001Sglebius	strlcpy(currenthost, temphost, sizeof(currenthost));
70354359Sroberto
704132451Sroberto	/* port maps to the same location in both families */
705290001Sglebius	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
721290001Sglebius		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
722290001Sglebius				 (char *)&optionValue, sizeof(optionValue));
723290001Sglebius		if (err) {
724290001Sglebius			mfprintf(stderr,
725290001Sglebius				 "setsockopt(SO_SYNCHRONOUS_NONALERT)"
726290001Sglebius				 " error: %m\n");
727290001Sglebius			freeaddrinfo(ai);
72854359Sroberto			exit(1);
72954359Sroberto		}
73054359Sroberto	}
731132451Sroberto#endif /* SYS_WINNT */
73254359Sroberto
733290001Sglebius	sockfd = socket(ai->ai_family, ai->ai_socktype,
734290001Sglebius			ai->ai_protocol);
73554359Sroberto	if (sockfd == INVALID_SOCKET) {
736290001Sglebius		error("socket");
737290001Sglebius		freeaddrinfo(ai);
738290001Sglebius		return 0;
73954359Sroberto	}
74054359Sroberto
741290001Sglebius
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)
747290001Sglebius		error("setsockopt");
74854359Sroberto	}
74954359Sroberto# endif
75054359Sroberto#endif
75154359Sroberto
752290001Sglebius	if
753132451Sroberto#ifdef SYS_VXWORKS
754290001Sglebius	   (connect(sockfd, (struct sockaddr *)&hostaddr,
75554359Sroberto		    sizeof(hostaddr)) == -1)
756132451Sroberto#else
757290001Sglebius	   (connect(sockfd, (struct sockaddr *)ai->ai_addr,
758293896Sglebius		ai->ai_addrlen) == -1)
759132451Sroberto#endif /* SYS_VXWORKS */
760293896Sglebius	{
761290001Sglebius		error("connect");
762132451Sroberto		freeaddrinfo(ai);
763290001Sglebius		return 0;
764290001Sglebius	}
765290001Sglebius	freeaddrinfo(ai);
76654359Sroberto	havehost = 1;
767290001Sglebius	numassoc = 0;
768290001Sglebius
76954359Sroberto	return 1;
77054359Sroberto}
77154359Sroberto
77254359Sroberto
773290001Sglebiusstatic void
774290001Sglebiusdump_hex_printable(
775290001Sglebius	const void *	data,
776290001Sglebius	size_t		len
777290001Sglebius	)
778290001Sglebius{
779290001Sglebius	const char *	cdata;
780290001Sglebius	const char *	rowstart;
781290001Sglebius	size_t		idx;
782290001Sglebius	size_t		rowlen;
783290001Sglebius	u_char		uch;
784290001Sglebius
785290001Sglebius	cdata = data;
786290001Sglebius	while (len > 0) {
787290001Sglebius		rowstart = cdata;
788290001Sglebius		rowlen = min(16, len);
789290001Sglebius		for (idx = 0; idx < rowlen; idx++) {
790290001Sglebius			uch = *(cdata++);
791290001Sglebius			printf("%02x ", uch);
792290001Sglebius		}
793290001Sglebius		for ( ; idx < 16 ; idx++)
794290001Sglebius			printf("   ");
795290001Sglebius		cdata = rowstart;
796290001Sglebius		for (idx = 0; idx < rowlen; idx++) {
797290001Sglebius			uch = *(cdata++);
798290001Sglebius			printf("%c", (isprint(uch))
799290001Sglebius					 ? uch
800290001Sglebius					 : '.');
801290001Sglebius		}
802290001Sglebius		printf("\n");
803290001Sglebius		len -= rowlen;
804290001Sglebius	}
805290001Sglebius}
806290001Sglebius
807290001Sglebius
80854359Sroberto/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
80954359Sroberto/*
81054359Sroberto * sendpkt - send a packet to the remote host
81154359Sroberto */
81254359Srobertostatic int
81354359Srobertosendpkt(
814290001Sglebius	void *	xdata,
815290001Sglebius	size_t	xdatalen
81654359Sroberto	)
81754359Sroberto{
81854359Sroberto	if (debug >= 3)
819290001Sglebius		printf("Sending %zu octets\n", xdatalen);
82054359Sroberto
821293896Sglebius	if (send(sockfd, xdata, xdatalen, 0) == -1) {
822290001Sglebius		warning("write to %s failed", currenthost);
82354359Sroberto		return -1;
82454359Sroberto	}
82554359Sroberto
82654359Sroberto	if (debug >= 4) {
827290001Sglebius		printf("Request packet:\n");
828290001Sglebius		dump_hex_printable(xdata, xdatalen);
82954359Sroberto	}
83054359Sroberto	return 0;
83154359Sroberto}
83254359Sroberto
83354359Sroberto/*
83454359Sroberto * getresponse - get a (series of) response packet(s) and return the data
83554359Sroberto */
83654359Srobertostatic int
83754359Srobertogetresponse(
83854359Sroberto	int opcode,
83954359Sroberto	int associd,
84054359Sroberto	u_short *rstatus,
841293896Sglebius	size_t *rsize,
842290001Sglebius	const char **rdata,
84354359Sroberto	int timeo
84454359Sroberto	)
84554359Sroberto{
84654359Sroberto	struct ntp_control rpkt;
847290001Sglebius	struct sock_timeval tvo;
84854359Sroberto	u_short offsets[MAXFRAGS+1];
84954359Sroberto	u_short counts[MAXFRAGS+1];
85054359Sroberto	u_short offset;
85154359Sroberto	u_short count;
852290001Sglebius	size_t numfrags;
853290001Sglebius	size_t f;
854290001Sglebius	size_t ff;
85554359Sroberto	int seenlastfrag;
856290001Sglebius	int shouldbesize;
85754359Sroberto	fd_set fds;
85854359Sroberto	int n;
859290001Sglebius	int errcode;
860294905Sdelphij	/* absolute timeout checks. Not 'time_t' by intention! */
861294905Sdelphij	uint32_t tobase;	/* base value for timeout */
862294905Sdelphij	uint32_t tospan;	/* timeout span (max delay) */
863294905Sdelphij	uint32_t todiff;	/* current delay */
86454359Sroberto
86554359Sroberto	/*
86654359Sroberto	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
86754359Sroberto	 * back in response to the request.  We peel the data out of
86854359Sroberto	 * each packet and collect it in one long block.  When the last
86954359Sroberto	 * packet in the sequence is received we'll know how much data we
87054359Sroberto	 * should have had.  Note we use one long time out, should reconsider.
87154359Sroberto	 */
87254359Sroberto	*rsize = 0;
87354359Sroberto	if (rstatus)
874290001Sglebius		*rstatus = 0;
87554359Sroberto	*rdata = (char *)pktdata;
87654359Sroberto
87754359Sroberto	numfrags = 0;
87854359Sroberto	seenlastfrag = 0;
87954359Sroberto
880294905Sdelphij	tobase = (uint32_t)time(NULL);
881294905Sdelphij
88254359Sroberto	FD_ZERO(&fds);
88354359Sroberto
884290001Sglebius	/*
885290001Sglebius	 * Loop until we have an error or a complete response.  Nearly all
886290001Sglebius	 * code paths to loop again use continue.
887290001Sglebius	 */
888290001Sglebius	for (;;) {
88954359Sroberto
890290001Sglebius		if (numfrags == 0)
891290001Sglebius			tvo = tvout;
892290001Sglebius		else
893290001Sglebius			tvo = tvsout;
894294905Sdelphij		tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
89554359Sroberto
896290001Sglebius		FD_SET(sockfd, &fds);
897293896Sglebius		n = select(sockfd+1, &fds, NULL, NULL, &tvo);
898290001Sglebius		if (n == -1) {
899294905Sdelphij#if !defined(SYS_WINNT) && defined(EINTR)
900294905Sdelphij			/* Windows does not know about EINTR (until very
901294905Sdelphij			 * recently) and the handling of console events
902294905Sdelphij			 * is *very* different from POSIX/UNIX signal
903294905Sdelphij			 * handling anyway.
904294905Sdelphij			 *
905294905Sdelphij			 * Under non-windows targets we map EINTR as
906294905Sdelphij			 * 'last packet was received' and try to exit
907294905Sdelphij			 * the receive sequence.
908294905Sdelphij			 */
909294905Sdelphij			if (errno == EINTR) {
910294905Sdelphij				seenlastfrag = 1;
911294905Sdelphij				goto maybe_final;
912294905Sdelphij			}
913294905Sdelphij#endif
914290001Sglebius			warning("select fails");
915290001Sglebius			return -1;
916290001Sglebius		}
917294905Sdelphij
918294905Sdelphij		/*
919294905Sdelphij		 * Check if this is already too late. Trash the data and
920294905Sdelphij		 * fake a timeout if this is so.
921294905Sdelphij		 */
922294905Sdelphij		todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
923294905Sdelphij		if ((n > 0) && (todiff > tospan)) {
924294905Sdelphij			n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
925294905Sdelphij			n = 0; /* faked timeout return from 'select()'*/
926294905Sdelphij		}
927294905Sdelphij
928290001Sglebius		if (n == 0) {
929290001Sglebius			/*
930290001Sglebius			 * Timed out.  Return what we have
931290001Sglebius			 */
932290001Sglebius			if (numfrags == 0) {
933290001Sglebius				if (timeo)
934290001Sglebius					fprintf(stderr,
935290001Sglebius						"%s: timed out, nothing received\n",
936290001Sglebius						currenthost);
937290001Sglebius				return ERR_TIMEOUT;
938290001Sglebius			}
93954359Sroberto			if (timeo)
940290001Sglebius				fprintf(stderr,
941290001Sglebius					"%s: timed out with incomplete data\n",
942290001Sglebius					currenthost);
94354359Sroberto			if (debug) {
944290001Sglebius				fprintf(stderr,
945290001Sglebius					"ERR_INCOMPLETE: Received fragments:\n");
946290001Sglebius				for (f = 0; f < numfrags; f++)
947290001Sglebius					fprintf(stderr,
948290001Sglebius						"%2u: %5d %5d\t%3d octets\n",
949290001Sglebius						(u_int)f, offsets[f],
950290001Sglebius						offsets[f] +
951290001Sglebius						counts[f],
952290001Sglebius						counts[f]);
953290001Sglebius				fprintf(stderr,
954290001Sglebius					"last fragment %sreceived\n",
955290001Sglebius					(seenlastfrag)
956290001Sglebius					    ? ""
957290001Sglebius					    : "not ");
95854359Sroberto			}
95954359Sroberto			return ERR_INCOMPLETE;
96054359Sroberto		}
96154359Sroberto
962290001Sglebius		n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
963290001Sglebius		if (n == -1) {
964290001Sglebius			warning("read");
965290001Sglebius			return -1;
966290001Sglebius		}
96754359Sroberto
968290001Sglebius		if (debug >= 4) {
969290001Sglebius			printf("Response packet:\n");
970290001Sglebius			dump_hex_printable(&rpkt, n);
971290001Sglebius		}
97254359Sroberto
973290001Sglebius		/*
974290001Sglebius		 * Check for format errors.  Bug proofing.
975290001Sglebius		 */
976290001Sglebius		if (n < (int)CTL_HEADER_LEN) {
977290001Sglebius			if (debug)
978290001Sglebius				printf("Short (%d byte) packet received\n", n);
979290001Sglebius			continue;
98054359Sroberto		}
981290001Sglebius		if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
982290001Sglebius		    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
983290001Sglebius			if (debug)
984290001Sglebius				printf("Packet received with version %d\n",
985290001Sglebius				       PKT_VERSION(rpkt.li_vn_mode));
986290001Sglebius			continue;
987290001Sglebius		}
988290001Sglebius		if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
989290001Sglebius			if (debug)
990290001Sglebius				printf("Packet received with mode %d\n",
991290001Sglebius				       PKT_MODE(rpkt.li_vn_mode));
992290001Sglebius			continue;
993290001Sglebius		}
994290001Sglebius		if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
995290001Sglebius			if (debug)
996290001Sglebius				printf("Received request packet, wanted response\n");
997290001Sglebius			continue;
998290001Sglebius		}
99954359Sroberto
1000290001Sglebius		/*
1001290001Sglebius		 * Check opcode and sequence number for a match.
1002290001Sglebius		 * Could be old data getting to us.
1003290001Sglebius		 */
1004290001Sglebius		if (ntohs(rpkt.sequence) != sequence) {
1005290001Sglebius			if (debug)
1006290001Sglebius				printf("Received sequnce number %d, wanted %d\n",
1007290001Sglebius				       ntohs(rpkt.sequence), sequence);
1008290001Sglebius			continue;
1009290001Sglebius		}
1010290001Sglebius		if (CTL_OP(rpkt.r_m_e_op) != opcode) {
1011290001Sglebius			if (debug)
1012290001Sglebius			    printf(
1013290001Sglebius				    "Received opcode %d, wanted %d (sequence number okay)\n",
1014290001Sglebius				    CTL_OP(rpkt.r_m_e_op), opcode);
1015290001Sglebius			continue;
1016290001Sglebius		}
101754359Sroberto
1018290001Sglebius		/*
1019290001Sglebius		 * Check the error code.  If non-zero, return it.
1020290001Sglebius		 */
1021290001Sglebius		if (CTL_ISERROR(rpkt.r_m_e_op)) {
1022290001Sglebius			errcode = (ntohs(rpkt.status) >> 8) & 0xff;
1023290001Sglebius			if (CTL_ISMORE(rpkt.r_m_e_op))
1024290001Sglebius				TRACE(1, ("Error code %d received on not-final packet\n",
1025290001Sglebius					  errcode));
1026290001Sglebius			if (errcode == CERR_UNSPEC)
1027290001Sglebius				return ERR_UNSPEC;
1028290001Sglebius			return errcode;
102954359Sroberto		}
103054359Sroberto
103154359Sroberto		/*
1032290001Sglebius		 * Check the association ID to make sure it matches what
1033290001Sglebius		 * we sent.
103454359Sroberto		 */
1035290001Sglebius		if (ntohs(rpkt.associd) != associd) {
1036290001Sglebius			TRACE(1, ("Association ID %d doesn't match expected %d\n",
1037290001Sglebius				  ntohs(rpkt.associd), associd));
1038290001Sglebius			/*
1039290001Sglebius			 * Hack for silly fuzzballs which, at the time of writing,
1040290001Sglebius			 * return an assID of sys.peer when queried for system variables.
1041290001Sglebius			 */
104254359Sroberto#ifdef notdef
1043290001Sglebius			continue;
104454359Sroberto#endif
1045290001Sglebius		}
104654359Sroberto
1047290001Sglebius		/*
1048290001Sglebius		 * Collect offset and count.  Make sure they make sense.
1049290001Sglebius		 */
1050290001Sglebius		offset = ntohs(rpkt.offset);
1051290001Sglebius		count = ntohs(rpkt.count);
105254359Sroberto
105354359Sroberto		/*
1054290001Sglebius		 * validate received payload size is padded to next 32-bit
1055290001Sglebius		 * boundary and no smaller than claimed by rpkt.count
105654359Sroberto		 */
1057290001Sglebius		if (n & 0x3) {
1058290001Sglebius			TRACE(1, ("Response packet not padded, size = %d\n",
1059290001Sglebius				  n));
1060290001Sglebius			continue;
1061290001Sglebius		}
106254359Sroberto
1063290001Sglebius		shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
106454359Sroberto
1065290001Sglebius		if (n < shouldbesize) {
1066290001Sglebius			printf("Response packet claims %u octets payload, above %ld received\n",
1067301301Sdelphij			       count, (long)(n - CTL_HEADER_LEN));
1068290001Sglebius			return ERR_INCOMPLETE;
1069290001Sglebius		}
1070290001Sglebius
1071290001Sglebius		if (debug >= 3 && shouldbesize > n) {
1072290001Sglebius			u_int32 key;
1073290001Sglebius			u_int32 *lpkt;
1074290001Sglebius			int maclen;
1075290001Sglebius
1076290001Sglebius			/*
1077290001Sglebius			 * Usually we ignore authentication, but for debugging purposes
1078290001Sglebius			 * we watch it here.
1079290001Sglebius			 */
1080290001Sglebius			/* round to 8 octet boundary */
1081290001Sglebius			shouldbesize = (shouldbesize + 7) & ~7;
1082290001Sglebius
1083290001Sglebius			maclen = n - shouldbesize;
1084290001Sglebius			if (maclen >= (int)MIN_MAC_LEN) {
1085290001Sglebius				printf(
1086290001Sglebius					"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1087290001Sglebius					n, shouldbesize, maclen);
1088290001Sglebius				lpkt = (u_int32 *)&rpkt;
1089290001Sglebius				printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1090290001Sglebius				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1091290001Sglebius				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1092290001Sglebius				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1093290001Sglebius				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1094290001Sglebius				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1095290001Sglebius				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1096290001Sglebius				key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1097290001Sglebius				printf("Authenticated with keyid %lu\n", (u_long)key);
1098290001Sglebius				if (key != 0 && key != info_auth_keyid) {
1099290001Sglebius					printf("We don't know that key\n");
110054359Sroberto				} else {
1101290001Sglebius					if (authdecrypt(key, (u_int32 *)&rpkt,
1102290001Sglebius					    n - maclen, maclen)) {
1103290001Sglebius						printf("Auth okay!\n");
1104290001Sglebius					} else {
1105290001Sglebius						printf("Auth failed!\n");
1106290001Sglebius					}
110754359Sroberto				}
110854359Sroberto			}
110954359Sroberto		}
111054359Sroberto
1111290001Sglebius		TRACE(2, ("Got packet, size = %d\n", n));
1112290001Sglebius		if (count > (n - CTL_HEADER_LEN)) {
1113290001Sglebius			TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
1114290001Sglebius				  count, (long)n - CTL_HEADER_LEN));
1115290001Sglebius			continue;
1116290001Sglebius		}
1117290001Sglebius		if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1118290001Sglebius			TRACE(1, ("Received count of 0 in non-final fragment\n"));
1119290001Sglebius			continue;
1120290001Sglebius		}
1121290001Sglebius		if (offset + count > sizeof(pktdata)) {
1122290001Sglebius			TRACE(1, ("Offset %u, count %u, too big for buffer\n",
1123290001Sglebius				  offset, count));
1124290001Sglebius			return ERR_TOOMUCH;
1125290001Sglebius		}
1126290001Sglebius		if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1127290001Sglebius			TRACE(1, ("Received second last fragment packet\n"));
1128290001Sglebius			continue;
1129290001Sglebius		}
113054359Sroberto
1131290001Sglebius		/*
1132290001Sglebius		 * So far, so good.  Record this fragment, making sure it doesn't
1133290001Sglebius		 * overlap anything.
1134290001Sglebius		 */
1135290001Sglebius		TRACE(2, ("Packet okay\n"));
113654359Sroberto
1137290001Sglebius		if (numfrags > (MAXFRAGS - 1)) {
1138290001Sglebius			TRACE(2, ("Number of fragments exceeds maximum %d\n",
1139290001Sglebius				  MAXFRAGS - 1));
1140290001Sglebius			return ERR_TOOMUCH;
114154359Sroberto		}
114254359Sroberto
1143290001Sglebius		/*
1144290001Sglebius		 * Find the position for the fragment relative to any
1145290001Sglebius		 * previously received.
1146290001Sglebius		 */
1147290001Sglebius		for (f = 0;
1148290001Sglebius		     f < numfrags && offsets[f] < offset;
1149290001Sglebius		     f++) {
1150290001Sglebius			/* empty body */ ;
1151290001Sglebius		}
115254359Sroberto
1153290001Sglebius		if (f < numfrags && offset == offsets[f]) {
1154290001Sglebius			TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
1155290001Sglebius				  count, offset, counts[f], offsets[f]));
1156290001Sglebius			continue;
1157290001Sglebius		}
115854359Sroberto
1159290001Sglebius		if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
1160290001Sglebius			TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
1161290001Sglebius				  offset, counts[f-1], offsets[f-1]));
1162290001Sglebius			continue;
116354359Sroberto		}
1164290001Sglebius
1165290001Sglebius		if (f < numfrags && (offset + count) > offsets[f]) {
1166290001Sglebius			TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
1167290001Sglebius				  count, offset, offsets[f]));
1168290001Sglebius			continue;
116954359Sroberto		}
117054359Sroberto
1171290001Sglebius		for (ff = numfrags; ff > f; ff--) {
1172290001Sglebius			offsets[ff] = offsets[ff-1];
1173290001Sglebius			counts[ff] = counts[ff-1];
1174290001Sglebius		}
1175290001Sglebius		offsets[f] = offset;
1176290001Sglebius		counts[f] = count;
1177290001Sglebius		numfrags++;
117854359Sroberto
1179290001Sglebius		/*
1180290001Sglebius		 * Got that stuffed in right.  Figure out if this was the last.
1181290001Sglebius		 * Record status info out of the last packet.
1182290001Sglebius		 */
1183290001Sglebius		if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1184290001Sglebius			seenlastfrag = 1;
1185290001Sglebius			if (rstatus != 0)
1186290001Sglebius				*rstatus = ntohs(rpkt.status);
1187290001Sglebius		}
118854359Sroberto
1189290001Sglebius		/*
1190294905Sdelphij		 * Copy the data into the data buffer, and bump the
1191294905Sdelphij		 * timout base in case we need more.
1192290001Sglebius		 */
1193290001Sglebius		memcpy((char *)pktdata + offset, &rpkt.u, count);
1194294905Sdelphij		tobase = (uint32_t)time(NULL);
1195294905Sdelphij
1196290001Sglebius		/*
1197290001Sglebius		 * If we've seen the last fragment, look for holes in the sequence.
1198290001Sglebius		 * If there aren't any, we're done.
1199290001Sglebius		 */
1200301301Sdelphij#if !defined(SYS_WINNT) && defined(EINTR)
1201301301Sdelphij		maybe_final:
1202301301Sdelphij#endif
1203301301Sdelphij
1204290001Sglebius		if (seenlastfrag && offsets[0] == 0) {
1205290001Sglebius			for (f = 1; f < numfrags; f++)
1206290001Sglebius				if (offsets[f-1] + counts[f-1] !=
1207290001Sglebius				    offsets[f])
1208290001Sglebius					break;
1209290001Sglebius			if (f == numfrags) {
1210290001Sglebius				*rsize = offsets[f-1] + counts[f-1];
1211290001Sglebius				TRACE(1, ("%lu packets reassembled into response\n",
1212290001Sglebius					  (u_long)numfrags));
1213290001Sglebius				return 0;
1214290001Sglebius			}
1215290001Sglebius		}
1216290001Sglebius	}  /* giant for (;;) collecting response packets */
1217290001Sglebius}  /* getresponse() */
1218290001Sglebius
1219290001Sglebius
122054359Sroberto/*
122154359Sroberto * sendrequest - format and send a request packet
122254359Sroberto */
122354359Srobertostatic int
122454359Srobertosendrequest(
122554359Sroberto	int opcode,
1226290001Sglebius	associd_t associd,
122754359Sroberto	int auth,
1228293896Sglebius	size_t qsize,
1229290001Sglebius	const char *qdata
123054359Sroberto	)
123154359Sroberto{
123254359Sroberto	struct ntp_control qpkt;
1233293896Sglebius	size_t	pktsize;
1234290001Sglebius	u_long	key_id;
1235290001Sglebius	char *	pass;
1236293896Sglebius	size_t	maclen;
123754359Sroberto
123854359Sroberto	/*
123954359Sroberto	 * Check to make sure the data will fit in one packet
124054359Sroberto	 */
124154359Sroberto	if (qsize > CTL_MAX_DATA_LEN) {
1242290001Sglebius		fprintf(stderr,
1243293896Sglebius			"***Internal error!  qsize (%zu) too large\n",
1244290001Sglebius			qsize);
124554359Sroberto		return 1;
124654359Sroberto	}
124754359Sroberto
124854359Sroberto	/*
124954359Sroberto	 * Fill in the packet
125054359Sroberto	 */
125154359Sroberto	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1252132451Sroberto	qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
125354359Sroberto	qpkt.sequence = htons(sequence);
125454359Sroberto	qpkt.status = 0;
125554359Sroberto	qpkt.associd = htons((u_short)associd);
125654359Sroberto	qpkt.offset = 0;
125754359Sroberto	qpkt.count = htons((u_short)qsize);
125854359Sroberto
1259290001Sglebius	pktsize = CTL_HEADER_LEN;
1260290001Sglebius
126154359Sroberto	/*
1262290001Sglebius	 * If we have data, copy and pad it out to a 32-bit boundary.
126354359Sroberto	 */
126454359Sroberto	if (qsize > 0) {
1265290001Sglebius		memcpy(&qpkt.u, qdata, (size_t)qsize);
1266290001Sglebius		pktsize += qsize;
1267290001Sglebius		while (pktsize & (sizeof(u_int32) - 1)) {
1268290001Sglebius			qpkt.u.data[qsize++] = 0;
126954359Sroberto			pktsize++;
127054359Sroberto		}
127154359Sroberto	}
127254359Sroberto
127354359Sroberto	/*
127454359Sroberto	 * If it isn't authenticated we can just send it.  Otherwise
127554359Sroberto	 * we're going to have to think about it a little.
127654359Sroberto	 */
127754359Sroberto	if (!auth && !always_auth) {
1278290001Sglebius		return sendpkt(&qpkt, pktsize);
1279290001Sglebius	}
128054359Sroberto
1281290001Sglebius	/*
1282290001Sglebius	 * Pad out packet to a multiple of 8 octets to be sure
1283290001Sglebius	 * receiver can handle it.
1284290001Sglebius	 */
1285290001Sglebius	while (pktsize & 7) {
1286290001Sglebius		qpkt.u.data[qsize++] = 0;
1287290001Sglebius		pktsize++;
1288290001Sglebius	}
128954359Sroberto
1290290001Sglebius	/*
1291290001Sglebius	 * Get the keyid and the password if we don't have one.
1292290001Sglebius	 */
1293290001Sglebius	if (info_auth_keyid == 0) {
1294290001Sglebius		key_id = getkeyid("Keyid: ");
1295290001Sglebius		if (key_id == 0 || key_id > NTP_MAXKEY) {
1296290001Sglebius			fprintf(stderr,
1297290001Sglebius				"Invalid key identifier\n");
1298290001Sglebius			return 1;
129954359Sroberto		}
1300290001Sglebius		info_auth_keyid = key_id;
1301290001Sglebius	}
1302290001Sglebius	if (!authistrusted(info_auth_keyid)) {
1303290001Sglebius		pass = getpass_keytype(info_auth_keytype);
1304290001Sglebius		if ('\0' == pass[0]) {
1305290001Sglebius			fprintf(stderr, "Invalid password\n");
1306290001Sglebius			return 1;
130754359Sroberto		}
1308290001Sglebius		authusekey(info_auth_keyid, info_auth_keytype,
1309290001Sglebius			   (u_char *)pass);
131054359Sroberto		authtrust(info_auth_keyid, 1);
1311290001Sglebius	}
131254359Sroberto
1313290001Sglebius	/*
1314290001Sglebius	 * Do the encryption.
1315290001Sglebius	 */
1316290001Sglebius	maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1317290001Sglebius	if (!maclen) {
1318290001Sglebius		fprintf(stderr, "Key not found\n");
1319290001Sglebius		return 1;
1320290001Sglebius	} else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1321290001Sglebius		fprintf(stderr,
1322293896Sglebius			"%zu octet MAC, %zu expected with %zu octet digest\n",
1323290001Sglebius			maclen, (info_auth_hashlen + sizeof(keyid_t)),
1324290001Sglebius			info_auth_hashlen);
1325290001Sglebius		return 1;
132654359Sroberto	}
1327290001Sglebius
1328290001Sglebius	return sendpkt((char *)&qpkt, pktsize + maclen);
132954359Sroberto}
133054359Sroberto
133154359Sroberto
133254359Sroberto/*
1333290001Sglebius * show_error_msg - display the error text for a mode 6 error response.
133454359Sroberto */
1335290001Sglebiusvoid
1336290001Sglebiusshow_error_msg(
1337290001Sglebius	int		m6resp,
1338290001Sglebius	associd_t	associd
1339290001Sglebius	)
1340290001Sglebius{
1341290001Sglebius	if (numhosts > 1)
1342290001Sglebius		fprintf(stderr, "server=%s ", currenthost);
1343290001Sglebius
1344298770Sdelphij	switch (m6resp) {
1345290001Sglebius
1346290001Sglebius	case CERR_BADFMT:
1347290001Sglebius		fprintf(stderr,
1348290001Sglebius		    "***Server reports a bad format request packet\n");
1349290001Sglebius		break;
1350290001Sglebius
1351290001Sglebius	case CERR_PERMISSION:
1352290001Sglebius		fprintf(stderr,
1353290001Sglebius		    "***Server disallowed request (authentication?)\n");
1354290001Sglebius		break;
1355290001Sglebius
1356290001Sglebius	case CERR_BADOP:
1357290001Sglebius		fprintf(stderr,
1358290001Sglebius		    "***Server reports a bad opcode in request\n");
1359290001Sglebius		break;
1360290001Sglebius
1361290001Sglebius	case CERR_BADASSOC:
1362290001Sglebius		fprintf(stderr,
1363290001Sglebius		    "***Association ID %d unknown to server\n",
1364290001Sglebius		    associd);
1365290001Sglebius		break;
1366290001Sglebius
1367290001Sglebius	case CERR_UNKNOWNVAR:
1368290001Sglebius		fprintf(stderr,
1369290001Sglebius		    "***A request variable unknown to the server\n");
1370290001Sglebius		break;
1371290001Sglebius
1372290001Sglebius	case CERR_BADVALUE:
1373290001Sglebius		fprintf(stderr,
1374290001Sglebius		    "***Server indicates a request variable was bad\n");
1375290001Sglebius		break;
1376290001Sglebius
1377290001Sglebius	case ERR_UNSPEC:
1378290001Sglebius		fprintf(stderr,
1379290001Sglebius		    "***Server returned an unspecified error\n");
1380290001Sglebius		break;
1381290001Sglebius
1382290001Sglebius	case ERR_TIMEOUT:
1383290001Sglebius		fprintf(stderr, "***Request timed out\n");
1384290001Sglebius		break;
1385290001Sglebius
1386290001Sglebius	case ERR_INCOMPLETE:
1387290001Sglebius		fprintf(stderr,
1388290001Sglebius		    "***Response from server was incomplete\n");
1389290001Sglebius		break;
1390290001Sglebius
1391290001Sglebius	case ERR_TOOMUCH:
1392290001Sglebius		fprintf(stderr,
1393290001Sglebius		    "***Buffer size exceeded for returned data\n");
1394290001Sglebius		break;
1395290001Sglebius
1396290001Sglebius	default:
1397290001Sglebius		fprintf(stderr,
1398290001Sglebius		    "***Server returns unknown error code %d\n",
1399290001Sglebius		    m6resp);
1400290001Sglebius	}
1401290001Sglebius}
1402290001Sglebius
1403290001Sglebius/*
1404290001Sglebius * doquery - send a request and process the response, displaying
1405290001Sglebius *	     error messages for any error responses.
1406290001Sglebius */
140754359Srobertoint
140854359Srobertodoquery(
140954359Sroberto	int opcode,
1410290001Sglebius	associd_t associd,
141154359Sroberto	int auth,
1412293896Sglebius	size_t qsize,
1413290001Sglebius	const char *qdata,
141454359Sroberto	u_short *rstatus,
1415293896Sglebius	size_t *rsize,
1416290001Sglebius	const char **rdata
141754359Sroberto	)
141854359Sroberto{
1419290001Sglebius	return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
1420290001Sglebius			 rsize, rdata, FALSE);
1421290001Sglebius}
1422290001Sglebius
1423290001Sglebius
1424290001Sglebius/*
1425290001Sglebius * doqueryex - send a request and process the response, optionally
1426290001Sglebius *	       displaying error messages for any error responses.
1427290001Sglebius */
1428290001Sglebiusint
1429290001Sglebiusdoqueryex(
1430290001Sglebius	int opcode,
1431290001Sglebius	associd_t associd,
1432290001Sglebius	int auth,
1433293896Sglebius	size_t qsize,
1434290001Sglebius	const char *qdata,
1435290001Sglebius	u_short *rstatus,
1436293896Sglebius	size_t *rsize,
1437290001Sglebius	const char **rdata,
1438290001Sglebius	int quiet
1439290001Sglebius	)
1440290001Sglebius{
144154359Sroberto	int res;
144254359Sroberto	int done;
144354359Sroberto
144454359Sroberto	/*
144554359Sroberto	 * Check to make sure host is open
144654359Sroberto	 */
144754359Sroberto	if (!havehost) {
1448290001Sglebius		fprintf(stderr, "***No host open, use `host' command\n");
144954359Sroberto		return -1;
145054359Sroberto	}
145154359Sroberto
145254359Sroberto	done = 0;
145354359Sroberto	sequence++;
145454359Sroberto
145554359Sroberto    again:
145654359Sroberto	/*
145754359Sroberto	 * send a request
145854359Sroberto	 */
145954359Sroberto	res = sendrequest(opcode, associd, auth, qsize, qdata);
146054359Sroberto	if (res != 0)
1461290001Sglebius		return res;
1462290001Sglebius
146354359Sroberto	/*
146454359Sroberto	 * Get the response.  If we got a standard error, print a message
146554359Sroberto	 */
146654359Sroberto	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
146754359Sroberto
146854359Sroberto	if (res > 0) {
146954359Sroberto		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
147054359Sroberto			if (res == ERR_INCOMPLETE) {
147154359Sroberto				/*
147254359Sroberto				 * better bump the sequence so we don't
147354359Sroberto				 * get confused about differing fragments.
147454359Sroberto				 */
147554359Sroberto				sequence++;
147654359Sroberto			}
147754359Sroberto			done = 1;
147854359Sroberto			goto again;
147954359Sroberto		}
1480290001Sglebius		if (!quiet)
1481290001Sglebius			show_error_msg(res, associd);
1482290001Sglebius
148354359Sroberto	}
148454359Sroberto	return res;
148554359Sroberto}
148654359Sroberto
148754359Sroberto
1488290001Sglebius#ifndef BUILD_AS_LIB
148954359Sroberto/*
149054359Sroberto * getcmds - read commands from the standard input and execute them
149154359Sroberto */
149254359Srobertostatic void
149354359Srobertogetcmds(void)
149454359Sroberto{
1495290001Sglebius	char *	line;
1496290001Sglebius	int	count;
149754359Sroberto
1498290001Sglebius	ntp_readline_init(interactive ? prompt : NULL);
1499106163Sroberto
1500290001Sglebius	for (;;) {
1501290001Sglebius		line = ntp_readline(&count);
1502290001Sglebius		if (NULL == line)
1503290001Sglebius			break;
1504290001Sglebius		docmd(line);
1505290001Sglebius		free(line);
1506290001Sglebius	}
150754359Sroberto
1508290001Sglebius	ntp_readline_uninit();
150954359Sroberto}
1510290001Sglebius#endif /* !BUILD_AS_LIB */
151154359Sroberto
1512290001Sglebius
1513290001Sglebius#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
151454359Sroberto/*
151554359Sroberto * abortcmd - catch interrupts and abort the current command
151654359Sroberto */
1517293896Sglebiusstatic int
1518293896Sglebiusabortcmd(void)
151954359Sroberto{
152054359Sroberto	if (current_output == stdout)
1521293896Sglebius		(void) fflush(stdout);
152254359Sroberto	putc('\n', stderr);
152354359Sroberto	(void) fflush(stderr);
1524293896Sglebius	if (jump) {
1525293896Sglebius		jump = 0;
1526293896Sglebius		longjmp(interrupt_buf, 1);
1527293896Sglebius	}
1528293896Sglebius	return TRUE;
152954359Sroberto}
1530290001Sglebius#endif	/* !SYS_WINNT && !BUILD_AS_LIB */
153154359Sroberto
1532290001Sglebius
1533290001Sglebius#ifndef	BUILD_AS_LIB
153454359Sroberto/*
153554359Sroberto * docmd - decode the command line and execute a command
153654359Sroberto */
153754359Srobertostatic void
153854359Srobertodocmd(
153954359Sroberto	const char *cmdline
154054359Sroberto	)
154154359Sroberto{
154254359Sroberto	char *tokens[1+MAXARGS+2];
154354359Sroberto	struct parse pcmd;
154454359Sroberto	int ntok;
154554359Sroberto	static int i;
154654359Sroberto	struct xcmd *xcmd;
154754359Sroberto
154854359Sroberto	/*
154954359Sroberto	 * Tokenize the command line.  If nothing on it, return.
155054359Sroberto	 */
155154359Sroberto	tokenize(cmdline, tokens, &ntok);
155254359Sroberto	if (ntok == 0)
155354359Sroberto	    return;
1554290001Sglebius
155554359Sroberto	/*
155654359Sroberto	 * Find the appropriate command description.
155754359Sroberto	 */
155854359Sroberto	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
155954359Sroberto	if (i == 0) {
156054359Sroberto		(void) fprintf(stderr, "***Command `%s' unknown\n",
156154359Sroberto			       tokens[0]);
156254359Sroberto		return;
156354359Sroberto	} else if (i >= 2) {
156454359Sroberto		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
156554359Sroberto			       tokens[0]);
156654359Sroberto		return;
156754359Sroberto	}
1568290001Sglebius
1569290001Sglebius	/* Warn about ignored extra args */
1570290001Sglebius	for (i = MAXARGS + 1; i < ntok ; ++i) {
1571290001Sglebius		fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
1572290001Sglebius	}
1573290001Sglebius
157454359Sroberto	/*
157554359Sroberto	 * Save the keyword, then walk through the arguments, interpreting
157654359Sroberto	 * as we go.
157754359Sroberto	 */
157854359Sroberto	pcmd.keyword = tokens[0];
157954359Sroberto	pcmd.nargs = 0;
158054359Sroberto	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
158154359Sroberto		if ((i+1) >= ntok) {
158254359Sroberto			if (!(xcmd->arg[i] & OPT)) {
158354359Sroberto				printusage(xcmd, stderr);
158454359Sroberto				return;
158554359Sroberto			}
158654359Sroberto			break;
158754359Sroberto		}
158854359Sroberto		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1589290001Sglebius			break;
159054359Sroberto		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1591290001Sglebius			return;
159254359Sroberto		pcmd.nargs++;
159354359Sroberto	}
159454359Sroberto
159554359Sroberto	i++;
159654359Sroberto	if (i < ntok && *tokens[i] == '>') {
159754359Sroberto		char *fname;
159854359Sroberto
159954359Sroberto		if (*(tokens[i]+1) != '\0')
1600290001Sglebius			fname = tokens[i]+1;
160154359Sroberto		else if ((i+1) < ntok)
1602290001Sglebius			fname = tokens[i+1];
160354359Sroberto		else {
160454359Sroberto			(void) fprintf(stderr, "***No file for redirect\n");
160554359Sroberto			return;
160654359Sroberto		}
160754359Sroberto
160854359Sroberto		current_output = fopen(fname, "w");
160954359Sroberto		if (current_output == NULL) {
161054359Sroberto			(void) fprintf(stderr, "***Error opening %s: ", fname);
161154359Sroberto			perror("");
161254359Sroberto			return;
161354359Sroberto		}
161454359Sroberto		i = 1;		/* flag we need a close */
161554359Sroberto	} else {
161654359Sroberto		current_output = stdout;
161754359Sroberto		i = 0;		/* flag no close */
161854359Sroberto	}
161954359Sroberto
162054359Sroberto	if (interactive && setjmp(interrupt_buf)) {
162154359Sroberto		jump = 0;
162254359Sroberto		return;
162354359Sroberto	} else {
162454359Sroberto		jump++;
162554359Sroberto		(xcmd->handler)(&pcmd, current_output);
162654359Sroberto		jump = 0;	/* HMS: 961106: was after fclose() */
162754359Sroberto		if (i) (void) fclose(current_output);
162854359Sroberto	}
1629290001Sglebius
1630290001Sglebius	return;
163154359Sroberto}
163254359Sroberto
163354359Sroberto
163454359Sroberto/*
163554359Sroberto * tokenize - turn a command line into tokens
1636290001Sglebius *
1637290001Sglebius * SK: Modified to allow a quoted string
1638290001Sglebius *
1639290001Sglebius * HMS: If the first character of the first token is a ':' then (after
1640290001Sglebius * eating inter-token whitespace) the 2nd token is the rest of the line.
164154359Sroberto */
1642290001Sglebius
164354359Srobertostatic void
164454359Srobertotokenize(
164554359Sroberto	const char *line,
164654359Sroberto	char **tokens,
164754359Sroberto	int *ntok
164854359Sroberto	)
164954359Sroberto{
165054359Sroberto	register const char *cp;
165154359Sroberto	register char *sp;
165254359Sroberto	static char tspace[MAXLINE];
165354359Sroberto
165454359Sroberto	sp = tspace;
165554359Sroberto	cp = line;
165654359Sroberto	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
165754359Sroberto		tokens[*ntok] = sp;
1658290001Sglebius
1659290001Sglebius		/* Skip inter-token whitespace */
166054359Sroberto		while (ISSPACE(*cp))
166154359Sroberto		    cp++;
1662290001Sglebius
1663290001Sglebius		/* If we're at EOL we're done */
166454359Sroberto		if (ISEOL(*cp))
166554359Sroberto		    break;
166654359Sroberto
1667290001Sglebius		/* If this is the 2nd token and the first token begins
1668290001Sglebius		 * with a ':', then just grab to EOL.
1669290001Sglebius		 */
1670290001Sglebius
1671290001Sglebius		if (*ntok == 1 && tokens[0][0] == ':') {
1672290001Sglebius			do {
1673290001Sglebius				if (sp - tspace >= MAXLINE)
1674290001Sglebius					goto toobig;
1675290001Sglebius				*sp++ = *cp++;
1676290001Sglebius			} while (!ISEOL(*cp));
1677290001Sglebius		}
1678290001Sglebius
1679290001Sglebius		/* Check if this token begins with a double quote.
1680290001Sglebius		 * If yes, continue reading till the next double quote
1681290001Sglebius		 */
1682290001Sglebius		else if (*cp == '\"') {
1683290001Sglebius			++cp;
1684290001Sglebius			do {
1685290001Sglebius				if (sp - tspace >= MAXLINE)
1686290001Sglebius					goto toobig;
1687290001Sglebius				*sp++ = *cp++;
1688290001Sglebius			} while ((*cp != '\"') && !ISEOL(*cp));
1689290001Sglebius			/* HMS: a missing closing " should be an error */
1690290001Sglebius		}
1691290001Sglebius		else {
1692290001Sglebius			do {
1693290001Sglebius				if (sp - tspace >= MAXLINE)
1694290001Sglebius					goto toobig;
1695290001Sglebius				*sp++ = *cp++;
1696290001Sglebius			} while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1697290001Sglebius			/* HMS: Why check for a " in the previous line? */
1698290001Sglebius		}
1699290001Sglebius
1700290001Sglebius		if (sp - tspace >= MAXLINE)
1701290001Sglebius			goto toobig;
170254359Sroberto		*sp++ = '\0';
170354359Sroberto	}
1704290001Sglebius	return;
1705290001Sglebius
1706290001Sglebius  toobig:
1707290001Sglebius	*ntok = 0;
1708290001Sglebius	fprintf(stderr,
1709290001Sglebius		"***Line `%s' is too big\n",
1710290001Sglebius		line);
1711290001Sglebius	return;
171254359Sroberto}
171354359Sroberto
171454359Sroberto
1715290001Sglebius/*
1716290001Sglebius * getarg - interpret an argument token
1717290001Sglebius */
1718290001Sglebiusstatic int
1719290001Sglebiusgetarg(
1720290001Sglebius	const char *str,
1721290001Sglebius	int code,
1722290001Sglebius	arg_v *argp
1723290001Sglebius	)
1724290001Sglebius{
1725290001Sglebius	u_long ul;
172654359Sroberto
1727290001Sglebius	switch (code & ~OPT) {
1728290001Sglebius	case NTP_STR:
1729290001Sglebius		argp->string = str;
1730290001Sglebius		break;
1731290001Sglebius
1732290001Sglebius	case NTP_ADD:
1733290001Sglebius		if (!getnetnum(str, &argp->netnum, NULL, 0))
1734290001Sglebius			return 0;
1735290001Sglebius		break;
1736290001Sglebius
1737290001Sglebius	case NTP_UINT:
1738290001Sglebius		if ('&' == str[0]) {
1739290001Sglebius			if (!atouint(&str[1], &ul)) {
1740290001Sglebius				fprintf(stderr,
1741290001Sglebius					"***Association index `%s' invalid/undecodable\n",
1742290001Sglebius					str);
1743290001Sglebius				return 0;
1744290001Sglebius			}
1745290001Sglebius			if (0 == numassoc) {
1746290001Sglebius				dogetassoc(stdout);
1747290001Sglebius				if (0 == numassoc) {
1748290001Sglebius					fprintf(stderr,
1749290001Sglebius						"***No associations found, `%s' unknown\n",
1750290001Sglebius						str);
1751290001Sglebius					return 0;
1752290001Sglebius				}
1753290001Sglebius			}
1754290001Sglebius			ul = min(ul, numassoc);
1755290001Sglebius			argp->uval = assoc_cache[ul - 1].assid;
1756290001Sglebius			break;
1757290001Sglebius		}
1758290001Sglebius		if (!atouint(str, &argp->uval)) {
1759290001Sglebius			fprintf(stderr, "***Illegal unsigned value %s\n",
1760290001Sglebius				str);
1761290001Sglebius			return 0;
1762290001Sglebius		}
1763290001Sglebius		break;
1764290001Sglebius
1765290001Sglebius	case NTP_INT:
1766290001Sglebius		if (!atoint(str, &argp->ival)) {
1767290001Sglebius			fprintf(stderr, "***Illegal integer value %s\n",
1768290001Sglebius				str);
1769290001Sglebius			return 0;
1770290001Sglebius		}
1771290001Sglebius		break;
1772290001Sglebius
1773290001Sglebius	case IP_VERSION:
1774290001Sglebius		if (!strcmp("-6", str)) {
1775290001Sglebius			argp->ival = 6;
1776290001Sglebius		} else if (!strcmp("-4", str)) {
1777290001Sglebius			argp->ival = 4;
1778290001Sglebius		} else {
1779290001Sglebius			fprintf(stderr, "***Version must be either 4 or 6\n");
1780290001Sglebius			return 0;
1781290001Sglebius		}
1782290001Sglebius		break;
1783290001Sglebius	}
1784290001Sglebius
1785290001Sglebius	return 1;
1786290001Sglebius}
1787290001Sglebius#endif	/* !BUILD_AS_LIB */
1788290001Sglebius
1789290001Sglebius
179054359Sroberto/*
179154359Sroberto * findcmd - find a command in a command description table
179254359Sroberto */
179354359Srobertostatic int
179454359Srobertofindcmd(
1795290001Sglebius	const char *	str,
1796290001Sglebius	struct xcmd *	clist1,
1797290001Sglebius	struct xcmd *	clist2,
1798290001Sglebius	struct xcmd **	cmd
179954359Sroberto	)
180054359Sroberto{
1801290001Sglebius	struct xcmd *cl;
1802293896Sglebius	size_t clen;
180354359Sroberto	int nmatch;
180454359Sroberto	struct xcmd *nearmatch = NULL;
180554359Sroberto	struct xcmd *clist;
180654359Sroberto
180754359Sroberto	clen = strlen(str);
180854359Sroberto	nmatch = 0;
180954359Sroberto	if (clist1 != 0)
181054359Sroberto	    clist = clist1;
181154359Sroberto	else if (clist2 != 0)
181254359Sroberto	    clist = clist2;
181354359Sroberto	else
181454359Sroberto	    return 0;
181554359Sroberto
181654359Sroberto    again:
181754359Sroberto	for (cl = clist; cl->keyword != 0; cl++) {
181854359Sroberto		/* do a first character check, for efficiency */
181954359Sroberto		if (*str != *(cl->keyword))
182054359Sroberto		    continue;
182154359Sroberto		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
182254359Sroberto			/*
182354359Sroberto			 * Could be extact match, could be approximate.
182454359Sroberto			 * Is exact if the length of the keyword is the
182554359Sroberto			 * same as the str.
182654359Sroberto			 */
182754359Sroberto			if (*((cl->keyword) + clen) == '\0') {
182854359Sroberto				*cmd = cl;
182954359Sroberto				return 1;
183054359Sroberto			}
183154359Sroberto			nmatch++;
183254359Sroberto			nearmatch = cl;
183354359Sroberto		}
183454359Sroberto	}
183554359Sroberto
183654359Sroberto	/*
183754359Sroberto	 * See if there is more to do.  If so, go again.  Sorry about the
183854359Sroberto	 * goto, too much looking at BSD sources...
183954359Sroberto	 */
184054359Sroberto	if (clist == clist1 && clist2 != 0) {
184154359Sroberto		clist = clist2;
184254359Sroberto		goto again;
184354359Sroberto	}
184454359Sroberto
184554359Sroberto	/*
184654359Sroberto	 * If we got extactly 1 near match, use it, else return number
184754359Sroberto	 * of matches.
184854359Sroberto	 */
184954359Sroberto	if (nmatch == 1) {
185054359Sroberto		*cmd = nearmatch;
185154359Sroberto		return 1;
185254359Sroberto	}
185354359Sroberto	return nmatch;
185454359Sroberto}
185554359Sroberto
185654359Sroberto
185754359Sroberto/*
185854359Sroberto * getnetnum - given a host name, return its net number
185954359Sroberto *	       and (optional) full name
186054359Sroberto */
186154359Srobertoint
186254359Srobertogetnetnum(
186354359Sroberto	const char *hname,
1864290001Sglebius	sockaddr_u *num,
1865132451Sroberto	char *fullhost,
1866132451Sroberto	int af
186754359Sroberto	)
186854359Sroberto{
1869132451Sroberto	struct addrinfo hints, *ai = NULL;
187054359Sroberto
1871290001Sglebius	ZERO(hints);
1872132451Sroberto	hints.ai_flags = AI_CANONNAME;
1873132451Sroberto#ifdef AI_ADDRCONFIG
1874132451Sroberto	hints.ai_flags |= AI_ADDRCONFIG;
1875132451Sroberto#endif
1876290001Sglebius
1877290001Sglebius	/*
1878290001Sglebius	 * decodenetnum only works with addresses, but handles syntax
1879290001Sglebius	 * that getaddrinfo doesn't:  [2001::1]:1234
1880290001Sglebius	 */
188154359Sroberto	if (decodenetnum(hname, num)) {
1882290001Sglebius		if (fullhost != NULL)
1883290001Sglebius			getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1884290001Sglebius				    LENHOSTNAME, NULL, 0, 0);
188554359Sroberto		return 1;
1886182007Sroberto	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1887290001Sglebius		INSIST(sizeof(*num) >= ai->ai_addrlen);
1888290001Sglebius		memcpy(num, ai->ai_addr, ai->ai_addrlen);
1889290001Sglebius		if (fullhost != NULL) {
1890290001Sglebius			if (ai->ai_canonname != NULL)
1891290001Sglebius				strlcpy(fullhost, ai->ai_canonname,
1892290001Sglebius					LENHOSTNAME);
1893290001Sglebius			else
1894290001Sglebius				getnameinfo(&num->sa, SOCKLEN(num),
1895290001Sglebius					    fullhost, LENHOSTNAME, NULL,
1896290001Sglebius					    0, 0);
1897290001Sglebius		}
1898290001Sglebius		freeaddrinfo(ai);
189954359Sroberto		return 1;
190054359Sroberto	}
1901290001Sglebius	fprintf(stderr, "***Can't find host %s\n", hname);
1902290001Sglebius
1903290001Sglebius	return 0;
190454359Sroberto}
190554359Sroberto
1906290001Sglebius
190754359Sroberto/*
190854359Sroberto * nntohost - convert network number to host name.  This routine enforces
190954359Sroberto *	       the showhostnames setting.
191054359Sroberto */
1911290001Sglebiusconst char *
191254359Srobertonntohost(
1913290001Sglebius	sockaddr_u *netnum
191454359Sroberto	)
191554359Sroberto{
1916290001Sglebius	return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
191754359Sroberto}
191854359Sroberto
191954359Sroberto
192054359Sroberto/*
1921290001Sglebius * nntohost_col - convert network number to host name in fixed width.
1922290001Sglebius *		  This routine enforces the showhostnames setting.
1923290001Sglebius *		  When displaying hostnames longer than the width,
1924290001Sglebius *		  the first part of the hostname is displayed.  When
1925290001Sglebius *		  displaying numeric addresses longer than the width,
1926290001Sglebius *		  Such as IPv6 addresses, the caller decides whether
1927290001Sglebius *		  the first or last of the numeric address is used.
1928290001Sglebius */
1929290001Sglebiusconst char *
1930290001Sglebiusnntohost_col(
1931290001Sglebius	sockaddr_u *	addr,
1932290001Sglebius	size_t		width,
1933290001Sglebius	int		preserve_lowaddrbits
1934290001Sglebius	)
1935290001Sglebius{
1936290001Sglebius	const char *	out;
1937290001Sglebius
1938290001Sglebius	if (!showhostnames || SOCK_UNSPEC(addr)) {
1939290001Sglebius		if (preserve_lowaddrbits)
1940290001Sglebius			out = trunc_left(stoa(addr), width);
1941290001Sglebius		else
1942290001Sglebius			out = trunc_right(stoa(addr), width);
1943290001Sglebius	} else if (ISREFCLOCKADR(addr)) {
1944290001Sglebius		out = refnumtoa(addr);
1945290001Sglebius	} else {
1946290001Sglebius		out = trunc_right(socktohost(addr), width);
1947290001Sglebius	}
1948290001Sglebius	return out;
1949290001Sglebius}
1950290001Sglebius
1951290001Sglebius
1952290001Sglebius/*
1953290001Sglebius * nntohostp() is the same as nntohost() plus a :port suffix
1954290001Sglebius */
1955290001Sglebiusconst char *
1956290001Sglebiusnntohostp(
1957290001Sglebius	sockaddr_u *netnum
1958290001Sglebius	)
1959290001Sglebius{
1960290001Sglebius	const char *	hostn;
1961290001Sglebius	char *		buf;
1962290001Sglebius
1963290001Sglebius	if (!showhostnames || SOCK_UNSPEC(netnum))
1964290001Sglebius		return sptoa(netnum);
1965290001Sglebius	else if (ISREFCLOCKADR(netnum))
1966290001Sglebius		return refnumtoa(netnum);
1967290001Sglebius
1968290001Sglebius	hostn = socktohost(netnum);
1969290001Sglebius	LIB_GETBUF(buf);
1970290001Sglebius	snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
1971290001Sglebius
1972290001Sglebius	return buf;
1973290001Sglebius}
1974290001Sglebius
1975290001Sglebius/*
197654359Sroberto * rtdatetolfp - decode an RT-11 date into an l_fp
197754359Sroberto */
197854359Srobertostatic int
197954359Srobertortdatetolfp(
198054359Sroberto	char *str,
198154359Sroberto	l_fp *lfp
198254359Sroberto	)
198354359Sroberto{
198454359Sroberto	register char *cp;
198554359Sroberto	register int i;
198654359Sroberto	struct calendar cal;
198754359Sroberto	char buf[4];
198854359Sroberto
198954359Sroberto	cal.yearday = 0;
199054359Sroberto
199154359Sroberto	/*
199254359Sroberto	 * An RT-11 date looks like:
199354359Sroberto	 *
199454359Sroberto	 * d[d]-Mth-y[y] hh:mm:ss
199554359Sroberto	 *
199654359Sroberto	 * (No docs, but assume 4-digit years are also legal...)
199754359Sroberto	 *
199854359Sroberto	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
199954359Sroberto	 */
200054359Sroberto	cp = str;
200154359Sroberto	if (!isdigit((int)*cp)) {
200254359Sroberto		if (*cp == '-') {
200354359Sroberto			/*
200454359Sroberto			 * Catch special case
200554359Sroberto			 */
200654359Sroberto			L_CLR(lfp);
200754359Sroberto			return 1;
200854359Sroberto		}
200954359Sroberto		return 0;
201054359Sroberto	}
201154359Sroberto
2012132451Sroberto	cal.monthday = (u_char) (*cp++ - '0');	/* ascii dependent */
201354359Sroberto	if (isdigit((int)*cp)) {
2014132451Sroberto		cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
2015132451Sroberto		cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
201654359Sroberto	}
201754359Sroberto
201854359Sroberto	if (*cp++ != '-')
201954359Sroberto	    return 0;
2020290001Sglebius
202154359Sroberto	for (i = 0; i < 3; i++)
202254359Sroberto	    buf[i] = *cp++;
202354359Sroberto	buf[3] = '\0';
202454359Sroberto
202554359Sroberto	for (i = 0; i < 12; i++)
202654359Sroberto	    if (STREQ(buf, months[i]))
202754359Sroberto		break;
202854359Sroberto	if (i == 12)
202954359Sroberto	    return 0;
2030132451Sroberto	cal.month = (u_char)(i + 1);
203154359Sroberto
203254359Sroberto	if (*cp++ != '-')
203354359Sroberto	    return 0;
2034290001Sglebius
203554359Sroberto	if (!isdigit((int)*cp))
203654359Sroberto	    return 0;
2037132451Sroberto	cal.year = (u_short)(*cp++ - '0');
203854359Sroberto	if (isdigit((int)*cp)) {
2039132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2040132451Sroberto		cal.year = (u_short)(*cp++ - '0');
204154359Sroberto	}
204254359Sroberto	if (isdigit((int)*cp)) {
2043132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2044132451Sroberto		cal.year = (u_short)(cal.year + *cp++ - '0');
204554359Sroberto	}
204654359Sroberto	if (isdigit((int)*cp)) {
2047132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2048132451Sroberto		cal.year = (u_short)(cal.year + *cp++ - '0');
204954359Sroberto	}
205054359Sroberto
205154359Sroberto	/*
205254359Sroberto	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
205354359Sroberto	 */
205454359Sroberto	if (cal.year == 0) {
205554359Sroberto		L_CLR(lfp);
205654359Sroberto		return 1;
205754359Sroberto	}
205854359Sroberto
205954359Sroberto	if (*cp++ != ' ' || !isdigit((int)*cp))
206054359Sroberto	    return 0;
2061132451Sroberto	cal.hour = (u_char)(*cp++ - '0');
206254359Sroberto	if (isdigit((int)*cp)) {
2063132451Sroberto		cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
2064132451Sroberto		cal.hour = (u_char)(cal.hour + *cp++ - '0');
206554359Sroberto	}
206654359Sroberto
206754359Sroberto	if (*cp++ != ':' || !isdigit((int)*cp))
206854359Sroberto	    return 0;
2069132451Sroberto	cal.minute = (u_char)(*cp++ - '0');
207054359Sroberto	if (isdigit((int)*cp)) {
2071132451Sroberto		cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
2072132451Sroberto		cal.minute = (u_char)(cal.minute + *cp++ - '0');
207354359Sroberto	}
207454359Sroberto
207554359Sroberto	if (*cp++ != ':' || !isdigit((int)*cp))
207654359Sroberto	    return 0;
2077132451Sroberto	cal.second = (u_char)(*cp++ - '0');
207854359Sroberto	if (isdigit((int)*cp)) {
2079132451Sroberto		cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
2080132451Sroberto		cal.second = (u_char)(cal.second + *cp++ - '0');
208154359Sroberto	}
208254359Sroberto
208354359Sroberto	/*
208454359Sroberto	 * For RT-11, 1972 seems to be the pivot year
208554359Sroberto	 */
208654359Sroberto	if (cal.year < 72)
208754359Sroberto		cal.year += 2000;
208854359Sroberto	if (cal.year < 100)
208954359Sroberto		cal.year += 1900;
209054359Sroberto
209154359Sroberto	lfp->l_ui = caltontp(&cal);
209254359Sroberto	lfp->l_uf = 0;
209354359Sroberto	return 1;
209454359Sroberto}
209554359Sroberto
209654359Sroberto
209754359Sroberto/*
209854359Sroberto * decodets - decode a timestamp into an l_fp format number, with
209954359Sroberto *	      consideration of fuzzball formats.
210054359Sroberto */
210154359Srobertoint
210254359Srobertodecodets(
210354359Sroberto	char *str,
210454359Sroberto	l_fp *lfp
210554359Sroberto	)
210654359Sroberto{
2107290001Sglebius	char *cp;
2108290001Sglebius	char buf[30];
2109290001Sglebius	size_t b;
2110290001Sglebius
211154359Sroberto	/*
211254359Sroberto	 * If it starts with a 0x, decode as hex.
211354359Sroberto	 */
211454359Sroberto	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
2115290001Sglebius		return hextolfp(str+2, lfp);
211654359Sroberto
211754359Sroberto	/*
211854359Sroberto	 * If it starts with a '"', try it as an RT-11 date.
211954359Sroberto	 */
212054359Sroberto	if (*str == '"') {
2121290001Sglebius		cp = str + 1;
2122290001Sglebius		b = 0;
2123290001Sglebius		while ('"' != *cp && '\0' != *cp &&
2124290001Sglebius		       b < COUNTOF(buf) - 1)
2125290001Sglebius			buf[b++] = *cp++;
2126290001Sglebius		buf[b] = '\0';
212754359Sroberto		return rtdatetolfp(buf, lfp);
212854359Sroberto	}
212954359Sroberto
213054359Sroberto	/*
213154359Sroberto	 * Might still be hex.  Check out the first character.  Talk
213254359Sroberto	 * about heuristics!
213354359Sroberto	 */
213454359Sroberto	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2135290001Sglebius		return hextolfp(str, lfp);
213654359Sroberto
213754359Sroberto	/*
213854359Sroberto	 * Try it as a decimal.  If this fails, try as an unquoted
213954359Sroberto	 * RT-11 date.  This code should go away eventually.
214054359Sroberto	 */
214154359Sroberto	if (atolfp(str, lfp))
2142290001Sglebius		return 1;
2143290001Sglebius
214454359Sroberto	return rtdatetolfp(str, lfp);
214554359Sroberto}
214654359Sroberto
214754359Sroberto
214854359Sroberto/*
214954359Sroberto * decodetime - decode a time value.  It should be in milliseconds
215054359Sroberto */
215154359Srobertoint
215254359Srobertodecodetime(
215354359Sroberto	char *str,
215454359Sroberto	l_fp *lfp
215554359Sroberto	)
215654359Sroberto{
215754359Sroberto	return mstolfp(str, lfp);
215854359Sroberto}
215954359Sroberto
216054359Sroberto
216154359Sroberto/*
216254359Sroberto * decodeint - decode an integer
216354359Sroberto */
216454359Srobertoint
216554359Srobertodecodeint(
216654359Sroberto	char *str,
216754359Sroberto	long *val
216854359Sroberto	)
216954359Sroberto{
217054359Sroberto	if (*str == '0') {
217154359Sroberto		if (*(str+1) == 'x' || *(str+1) == 'X')
2172290001Sglebius		    return hextoint(str+2, (u_long *)val);
2173290001Sglebius		return octtoint(str, (u_long *)val);
217454359Sroberto	}
217554359Sroberto	return atoint(str, val);
217654359Sroberto}
217754359Sroberto
217854359Sroberto
217954359Sroberto/*
218054359Sroberto * decodeuint - decode an unsigned integer
218154359Sroberto */
218254359Srobertoint
218354359Srobertodecodeuint(
218454359Sroberto	char *str,
218554359Sroberto	u_long *val
218654359Sroberto	)
218754359Sroberto{
218854359Sroberto	if (*str == '0') {
218954359Sroberto		if (*(str + 1) == 'x' || *(str + 1) == 'X')
219054359Sroberto			return (hextoint(str + 2, val));
219154359Sroberto		return (octtoint(str, val));
219254359Sroberto	}
219354359Sroberto	return (atouint(str, val));
219454359Sroberto}
219554359Sroberto
219654359Sroberto
219754359Sroberto/*
219854359Sroberto * decodearr - decode an array of time values
219954359Sroberto */
220054359Srobertostatic int
220154359Srobertodecodearr(
220254359Sroberto	char *str,
220354359Sroberto	int *narr,
220454359Sroberto	l_fp *lfparr
220554359Sroberto	)
220654359Sroberto{
220754359Sroberto	register char *cp, *bp;
220854359Sroberto	register l_fp *lfp;
220954359Sroberto	char buf[60];
221054359Sroberto
221154359Sroberto	lfp = lfparr;
221254359Sroberto	cp = str;
221354359Sroberto	*narr = 0;
221454359Sroberto
221554359Sroberto	while (*narr < 8) {
221654359Sroberto		while (isspace((int)*cp))
221754359Sroberto		    cp++;
221854359Sroberto		if (*cp == '\0')
221954359Sroberto		    break;
222054359Sroberto
222154359Sroberto		bp = buf;
222254359Sroberto		while (!isspace((int)*cp) && *cp != '\0')
222354359Sroberto		    *bp++ = *cp++;
222454359Sroberto		*bp++ = '\0';
222554359Sroberto
222654359Sroberto		if (!decodetime(buf, lfp))
222754359Sroberto		    return 0;
222854359Sroberto		(*narr)++;
222954359Sroberto		lfp++;
223054359Sroberto	}
223154359Sroberto	return 1;
223254359Sroberto}
223354359Sroberto
223454359Sroberto
223554359Sroberto/*
223654359Sroberto * Finally, the built in command handlers
223754359Sroberto */
223854359Sroberto
223954359Sroberto/*
224054359Sroberto * help - tell about commands, or details of a particular command
224154359Sroberto */
224254359Srobertostatic void
224354359Srobertohelp(
224454359Sroberto	struct parse *pcmd,
224554359Sroberto	FILE *fp
224654359Sroberto	)
224754359Sroberto{
2248290001Sglebius	struct xcmd *xcp = NULL;	/* quiet warning */
2249290001Sglebius	const char *cmd;
2250182007Sroberto	const char *list[100];
2251290001Sglebius	size_t word, words;
2252290001Sglebius	size_t row, rows;
2253290001Sglebius	size_t col, cols;
2254290001Sglebius	size_t length;
225554359Sroberto
225654359Sroberto	if (pcmd->nargs == 0) {
2257182007Sroberto		words = 0;
2258290001Sglebius		for (xcp = builtins; xcp->keyword != NULL; xcp++) {
2259290001Sglebius			if (*(xcp->keyword) != '?' &&
2260290001Sglebius			    words < COUNTOF(list))
2261290001Sglebius				list[words++] = xcp->keyword;
226254359Sroberto		}
2263290001Sglebius		for (xcp = opcmds; xcp->keyword != NULL; xcp++)
2264290001Sglebius			if (words < COUNTOF(list))
2265290001Sglebius				list[words++] = xcp->keyword;
226654359Sroberto
2267290001Sglebius		qsort((void *)list, words, sizeof(list[0]), helpsort);
2268182007Sroberto		col = 0;
2269182007Sroberto		for (word = 0; word < words; word++) {
2270290001Sglebius			length = strlen(list[word]);
2271290001Sglebius			col = max(col, length);
227254359Sroberto		}
227354359Sroberto
2274182007Sroberto		cols = SCREENWIDTH / ++col;
2275290001Sglebius		rows = (words + cols - 1) / cols;
2276182007Sroberto
2277290001Sglebius		fprintf(fp, "ntpq commands:\n");
2278182007Sroberto
2279182007Sroberto		for (row = 0; row < rows; row++) {
2280290001Sglebius			for (word = row; word < words; word += rows)
2281290001Sglebius				fprintf(fp, "%-*.*s", (int)col,
2282290001Sglebius					(int)col - 1, list[word]);
2283290001Sglebius			fprintf(fp, "\n");
2284290001Sglebius		}
228554359Sroberto	} else {
228654359Sroberto		cmd = pcmd->argval[0].string;
2287182007Sroberto		words = findcmd(cmd, builtins, opcmds, &xcp);
2288182007Sroberto		if (words == 0) {
2289290001Sglebius			fprintf(stderr,
2290290001Sglebius				"Command `%s' is unknown\n", cmd);
229154359Sroberto			return;
2292182007Sroberto		} else if (words >= 2) {
2293290001Sglebius			fprintf(stderr,
2294290001Sglebius				"Command `%s' is ambiguous\n", cmd);
229554359Sroberto			return;
229654359Sroberto		}
2297290001Sglebius		fprintf(fp, "function: %s\n", xcp->comment);
229854359Sroberto		printusage(xcp, fp);
229954359Sroberto	}
230054359Sroberto}
230154359Sroberto
230254359Sroberto
230354359Sroberto/*
230454359Sroberto * helpsort - do hostname qsort comparisons
230554359Sroberto */
230654359Srobertostatic int
230754359Srobertohelpsort(
230854359Sroberto	const void *t1,
230954359Sroberto	const void *t2
231054359Sroberto	)
231154359Sroberto{
2312290001Sglebius	const char * const *	name1 = t1;
2313290001Sglebius	const char * const *	name2 = t2;
231454359Sroberto
231554359Sroberto	return strcmp(*name1, *name2);
231654359Sroberto}
231754359Sroberto
231854359Sroberto
231954359Sroberto/*
232054359Sroberto * printusage - print usage information for a command
232154359Sroberto */
232254359Srobertostatic void
232354359Srobertoprintusage(
232454359Sroberto	struct xcmd *xcp,
232554359Sroberto	FILE *fp
232654359Sroberto	)
232754359Sroberto{
232854359Sroberto	register int i;
232954359Sroberto
2330290001Sglebius	/* XXX: Do we need to warn about extra args here too? */
2331290001Sglebius
233254359Sroberto	(void) fprintf(fp, "usage: %s", xcp->keyword);
233354359Sroberto	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
233454359Sroberto		if (xcp->arg[i] & OPT)
233554359Sroberto		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
233654359Sroberto		else
233754359Sroberto		    (void) fprintf(fp, " %s", xcp->desc[i]);
233854359Sroberto	}
233954359Sroberto	(void) fprintf(fp, "\n");
234054359Sroberto}
234154359Sroberto
234254359Sroberto
234354359Sroberto/*
234454359Sroberto * timeout - set time out time
234554359Sroberto */
234654359Srobertostatic void
234754359Srobertotimeout(
234854359Sroberto	struct parse *pcmd,
234954359Sroberto	FILE *fp
235054359Sroberto	)
235154359Sroberto{
235254359Sroberto	int val;
235354359Sroberto
235454359Sroberto	if (pcmd->nargs == 0) {
2355290001Sglebius		val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
235654359Sroberto		(void) fprintf(fp, "primary timeout %d ms\n", val);
235754359Sroberto	} else {
235854359Sroberto		tvout.tv_sec = pcmd->argval[0].uval / 1000;
2359290001Sglebius		tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
236054359Sroberto			* 1000;
236154359Sroberto	}
236254359Sroberto}
236354359Sroberto
236454359Sroberto
236554359Sroberto/*
236654359Sroberto * auth_delay - set delay for auth requests
236754359Sroberto */
236854359Srobertostatic void
236954359Srobertoauth_delay(
237054359Sroberto	struct parse *pcmd,
237154359Sroberto	FILE *fp
237254359Sroberto	)
237354359Sroberto{
237454359Sroberto	int isneg;
237554359Sroberto	u_long val;
237654359Sroberto
237754359Sroberto	if (pcmd->nargs == 0) {
237854359Sroberto		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
237954359Sroberto		(void) fprintf(fp, "delay %lu ms\n", val);
238054359Sroberto	} else {
238154359Sroberto		if (pcmd->argval[0].ival < 0) {
238254359Sroberto			isneg = 1;
238354359Sroberto			val = (u_long)(-pcmd->argval[0].ival);
238454359Sroberto		} else {
238554359Sroberto			isneg = 0;
238654359Sroberto			val = (u_long)pcmd->argval[0].ival;
238754359Sroberto		}
238854359Sroberto
238954359Sroberto		delay_time.l_ui = val / 1000;
239054359Sroberto		val %= 1000;
239154359Sroberto		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
239254359Sroberto
239354359Sroberto		if (isneg)
239454359Sroberto		    L_NEG(&delay_time);
239554359Sroberto	}
239654359Sroberto}
239754359Sroberto
239854359Sroberto
239954359Sroberto/*
240054359Sroberto * host - set the host we are dealing with.
240154359Sroberto */
240254359Srobertostatic void
240354359Srobertohost(
240454359Sroberto	struct parse *pcmd,
240554359Sroberto	FILE *fp
240654359Sroberto	)
240754359Sroberto{
2408132451Sroberto	int i;
2409132451Sroberto
241054359Sroberto	if (pcmd->nargs == 0) {
241154359Sroberto		if (havehost)
2412290001Sglebius			(void) fprintf(fp, "current host is %s\n",
2413290001Sglebius					   currenthost);
241454359Sroberto		else
2415290001Sglebius			(void) fprintf(fp, "no current host\n");
2416132451Sroberto		return;
2417132451Sroberto	}
2418132451Sroberto
2419132451Sroberto	i = 0;
2420132451Sroberto	ai_fam_templ = ai_fam_default;
2421132451Sroberto	if (pcmd->nargs == 2) {
2422132451Sroberto		if (!strcmp("-4", pcmd->argval[i].string))
2423132451Sroberto			ai_fam_templ = AF_INET;
2424132451Sroberto		else if (!strcmp("-6", pcmd->argval[i].string))
2425132451Sroberto			ai_fam_templ = AF_INET6;
2426290001Sglebius		else
2427290001Sglebius			goto no_change;
2428132451Sroberto		i = 1;
2429132451Sroberto	}
2430290001Sglebius	if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
2431290001Sglebius		fprintf(fp, "current host set to %s\n", currenthost);
243254359Sroberto	} else {
2433290001Sglebius    no_change:
243454359Sroberto		if (havehost)
2435290001Sglebius			fprintf(fp, "current host remains %s\n",
2436290001Sglebius				currenthost);
243754359Sroberto		else
2438290001Sglebius			fprintf(fp, "still no current host\n");
243954359Sroberto	}
244054359Sroberto}
244154359Sroberto
244254359Sroberto
244354359Sroberto/*
244454359Sroberto * poll - do one (or more) polls of the host via NTP
244554359Sroberto */
244654359Sroberto/*ARGSUSED*/
244754359Srobertostatic void
244854359Srobertontp_poll(
244954359Sroberto	struct parse *pcmd,
245054359Sroberto	FILE *fp
245154359Sroberto	)
245254359Sroberto{
245354359Sroberto	(void) fprintf(fp, "poll not implemented yet\n");
245454359Sroberto}
245554359Sroberto
245654359Sroberto
245754359Sroberto/*
2458298770Sdelphij * showdrefid2str - return a string explanation of the value of drefid
2459298770Sdelphij */
2460298770Sdelphijstatic char *
2461298770Sdelphijshowdrefid2str(void)
2462298770Sdelphij{
2463298770Sdelphij	switch (drefid) {
2464298770Sdelphij	    case REFID_HASH:
2465298770Sdelphij	    	return "hash";
2466298770Sdelphij	    case REFID_IPV4:
2467298770Sdelphij	    	return "ipv4";
2468298770Sdelphij	    default:
2469298770Sdelphij	    	return "Unknown";
2470298770Sdelphij	}
2471298770Sdelphij}
2472298770Sdelphij
2473298770Sdelphij
2474298770Sdelphij/*
2475298770Sdelphij * drefid - display/change "display hash"
2476298770Sdelphij */
2477298770Sdelphijstatic void
2478298770Sdelphijshowdrefid(
2479298770Sdelphij	struct parse *pcmd,
2480298770Sdelphij	FILE *fp
2481298770Sdelphij	)
2482298770Sdelphij{
2483298770Sdelphij	if (pcmd->nargs == 0) {
2484298770Sdelphij		(void) fprintf(fp, "drefid value is %s\n", showdrefid2str());
2485298770Sdelphij		return;
2486298770Sdelphij	} else if (STREQ(pcmd->argval[0].string, "hash")) {
2487298770Sdelphij		drefid = REFID_HASH;
2488298770Sdelphij	} else if (STREQ(pcmd->argval[0].string, "ipv4")) {
2489298770Sdelphij		drefid = REFID_IPV4;
2490298770Sdelphij	} else {
2491298770Sdelphij		(void) fprintf(fp, "What?\n");
2492298770Sdelphij		return;
2493298770Sdelphij	}
2494298770Sdelphij	(void) fprintf(fp, "drefid value set to %s\n", showdrefid2str());
2495298770Sdelphij}
2496298770Sdelphij
2497298770Sdelphij
2498298770Sdelphij/*
249954359Sroberto * keyid - get a keyid to use for authenticating requests
250054359Sroberto */
250154359Srobertostatic void
250254359Srobertokeyid(
250354359Sroberto	struct parse *pcmd,
250454359Sroberto	FILE *fp
250554359Sroberto	)
250654359Sroberto{
250754359Sroberto	if (pcmd->nargs == 0) {
2508132451Sroberto		if (info_auth_keyid == 0)
250954359Sroberto		    (void) fprintf(fp, "no keyid defined\n");
251054359Sroberto		else
251154359Sroberto		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
251254359Sroberto	} else {
2513132451Sroberto		/* allow zero so that keyid can be cleared. */
2514132451Sroberto		if(pcmd->argval[0].uval > NTP_MAXKEY)
2515132451Sroberto		    (void) fprintf(fp, "Invalid key identifier\n");
251654359Sroberto		info_auth_keyid = pcmd->argval[0].uval;
251754359Sroberto	}
251854359Sroberto}
251954359Sroberto
252054359Sroberto/*
252154359Sroberto * keytype - get type of key to use for authenticating requests
252254359Sroberto */
252354359Srobertostatic void
252454359Srobertokeytype(
252554359Sroberto	struct parse *pcmd,
252654359Sroberto	FILE *fp
252754359Sroberto	)
252854359Sroberto{
2529290001Sglebius	const char *	digest_name;
2530290001Sglebius	size_t		digest_len;
2531290001Sglebius	int		key_type;
253254359Sroberto
2533290001Sglebius	if (!pcmd->nargs) {
2534290001Sglebius		fprintf(fp, "keytype is %s with %lu octet digests\n",
2535290001Sglebius			keytype_name(info_auth_keytype),
2536290001Sglebius			(u_long)info_auth_hashlen);
2537290001Sglebius		return;
2538290001Sglebius	}
2539290001Sglebius
2540290001Sglebius	digest_name = pcmd->argval[0].string;
2541290001Sglebius	digest_len = 0;
2542290001Sglebius	key_type = keytype_from_text(digest_name, &digest_len);
2543290001Sglebius
2544290001Sglebius	if (!key_type) {
2545290001Sglebius		fprintf(fp, "keytype is not valid. "
2546290001Sglebius#ifdef OPENSSL
2547290001Sglebius			"Type \"help keytype\" for the available digest types.\n");
2548290001Sglebius#else
2549290001Sglebius			"Only \"md5\" is available.\n");
2550290001Sglebius#endif
2551290001Sglebius		return;
2552290001Sglebius	}
2553290001Sglebius
2554290001Sglebius	info_auth_keytype = key_type;
2555290001Sglebius	info_auth_hashlen = digest_len;
255654359Sroberto}
255754359Sroberto
255854359Sroberto
255954359Sroberto/*
256054359Sroberto * passwd - get an authentication key
256154359Sroberto */
256254359Sroberto/*ARGSUSED*/
256354359Srobertostatic void
256454359Srobertopasswd(
256554359Sroberto	struct parse *pcmd,
256654359Sroberto	FILE *fp
256754359Sroberto	)
256854359Sroberto{
2569290001Sglebius	const char *pass;
257054359Sroberto
2571132451Sroberto	if (info_auth_keyid == 0) {
2572290001Sglebius		info_auth_keyid = getkeyid("Keyid: ");
2573290001Sglebius		if (info_auth_keyid == 0) {
2574290001Sglebius			(void)fprintf(fp, "Keyid must be defined\n");
257554359Sroberto			return;
257654359Sroberto		}
257754359Sroberto	}
2578290001Sglebius	if (pcmd->nargs >= 1)
2579290001Sglebius		pass = pcmd->argval[0].string;
2580132451Sroberto	else {
2581290001Sglebius		pass = getpass_keytype(info_auth_keytype);
2582290001Sglebius		if ('\0' == pass[0]) {
2583290001Sglebius			fprintf(fp, "Password unchanged\n");
2584290001Sglebius			return;
2585290001Sglebius		}
2586132451Sroberto	}
2587290001Sglebius	authusekey(info_auth_keyid, info_auth_keytype,
2588290001Sglebius		   (const u_char *)pass);
2589290001Sglebius	authtrust(info_auth_keyid, 1);
259054359Sroberto}
259154359Sroberto
259254359Sroberto
259354359Sroberto/*
259454359Sroberto * hostnames - set the showhostnames flag
259554359Sroberto */
259654359Srobertostatic void
259754359Srobertohostnames(
259854359Sroberto	struct parse *pcmd,
259954359Sroberto	FILE *fp
260054359Sroberto	)
260154359Sroberto{
260254359Sroberto	if (pcmd->nargs == 0) {
260354359Sroberto		if (showhostnames)
260454359Sroberto		    (void) fprintf(fp, "hostnames being shown\n");
260554359Sroberto		else
260654359Sroberto		    (void) fprintf(fp, "hostnames not being shown\n");
260754359Sroberto	} else {
260854359Sroberto		if (STREQ(pcmd->argval[0].string, "yes"))
260954359Sroberto		    showhostnames = 1;
261054359Sroberto		else if (STREQ(pcmd->argval[0].string, "no"))
261154359Sroberto		    showhostnames = 0;
261254359Sroberto		else
261354359Sroberto		    (void)fprintf(stderr, "What?\n");
261454359Sroberto	}
261554359Sroberto}
261654359Sroberto
261754359Sroberto
261854359Sroberto
261954359Sroberto/*
262054359Sroberto * setdebug - set/change debugging level
262154359Sroberto */
262254359Srobertostatic void
262354359Srobertosetdebug(
262454359Sroberto	struct parse *pcmd,
262554359Sroberto	FILE *fp
262654359Sroberto	)
262754359Sroberto{
262854359Sroberto	if (pcmd->nargs == 0) {
262954359Sroberto		(void) fprintf(fp, "debug level is %d\n", debug);
263054359Sroberto		return;
263154359Sroberto	} else if (STREQ(pcmd->argval[0].string, "no")) {
263254359Sroberto		debug = 0;
263354359Sroberto	} else if (STREQ(pcmd->argval[0].string, "more")) {
263454359Sroberto		debug++;
263554359Sroberto	} else if (STREQ(pcmd->argval[0].string, "less")) {
263654359Sroberto		debug--;
263754359Sroberto	} else {
263854359Sroberto		(void) fprintf(fp, "What?\n");
263954359Sroberto		return;
264054359Sroberto	}
264154359Sroberto	(void) fprintf(fp, "debug level set to %d\n", debug);
264254359Sroberto}
264354359Sroberto
264454359Sroberto
264554359Sroberto/*
264654359Sroberto * quit - stop this nonsense
264754359Sroberto */
264854359Sroberto/*ARGSUSED*/
264954359Srobertostatic void
265054359Srobertoquit(
265154359Sroberto	struct parse *pcmd,
265254359Sroberto	FILE *fp
265354359Sroberto	)
265454359Sroberto{
265554359Sroberto	if (havehost)
265654359Sroberto	    closesocket(sockfd);	/* cleanliness next to godliness */
265754359Sroberto	exit(0);
265854359Sroberto}
265954359Sroberto
266054359Sroberto
266154359Sroberto/*
266254359Sroberto * version - print the current version number
266354359Sroberto */
266454359Sroberto/*ARGSUSED*/
266554359Srobertostatic void
266654359Srobertoversion(
266754359Sroberto	struct parse *pcmd,
266854359Sroberto	FILE *fp
266954359Sroberto	)
267054359Sroberto{
267154359Sroberto
267254359Sroberto	(void) fprintf(fp, "%s\n", Version);
267354359Sroberto	return;
267454359Sroberto}
267554359Sroberto
267654359Sroberto
267754359Sroberto/*
267854359Sroberto * raw - set raw mode output
267954359Sroberto */
268054359Sroberto/*ARGSUSED*/
268154359Srobertostatic void
268254359Srobertoraw(
268354359Sroberto	struct parse *pcmd,
268454359Sroberto	FILE *fp
268554359Sroberto	)
268654359Sroberto{
268754359Sroberto	rawmode = 1;
268854359Sroberto	(void) fprintf(fp, "Output set to raw\n");
268954359Sroberto}
269054359Sroberto
269154359Sroberto
269254359Sroberto/*
269354359Sroberto * cooked - set cooked mode output
269454359Sroberto */
269554359Sroberto/*ARGSUSED*/
269654359Srobertostatic void
269754359Srobertocooked(
269854359Sroberto	struct parse *pcmd,
269954359Sroberto	FILE *fp
270054359Sroberto	)
270154359Sroberto{
270254359Sroberto	rawmode = 0;
270354359Sroberto	(void) fprintf(fp, "Output set to cooked\n");
270454359Sroberto	return;
270554359Sroberto}
270654359Sroberto
270754359Sroberto
270854359Sroberto/*
270954359Sroberto * authenticate - always authenticate requests to this host
271054359Sroberto */
271154359Srobertostatic void
271254359Srobertoauthenticate(
271354359Sroberto	struct parse *pcmd,
271454359Sroberto	FILE *fp
271554359Sroberto	)
271654359Sroberto{
271754359Sroberto	if (pcmd->nargs == 0) {
271854359Sroberto		if (always_auth) {
271954359Sroberto			(void) fprintf(fp,
272054359Sroberto				       "authenticated requests being sent\n");
272154359Sroberto		} else
272254359Sroberto		    (void) fprintf(fp,
272354359Sroberto				   "unauthenticated requests being sent\n");
272454359Sroberto	} else {
272554359Sroberto		if (STREQ(pcmd->argval[0].string, "yes")) {
272654359Sroberto			always_auth = 1;
272754359Sroberto		} else if (STREQ(pcmd->argval[0].string, "no")) {
272854359Sroberto			always_auth = 0;
272954359Sroberto		} else
273054359Sroberto		    (void)fprintf(stderr, "What?\n");
273154359Sroberto	}
273254359Sroberto}
273354359Sroberto
273454359Sroberto
273554359Sroberto/*
273654359Sroberto * ntpversion - choose the NTP version to use
273754359Sroberto */
273854359Srobertostatic void
273954359Srobertontpversion(
274054359Sroberto	struct parse *pcmd,
274154359Sroberto	FILE *fp
274254359Sroberto	)
274354359Sroberto{
274454359Sroberto	if (pcmd->nargs == 0) {
274554359Sroberto		(void) fprintf(fp,
274654359Sroberto			       "NTP version being claimed is %d\n", pktversion);
274754359Sroberto	} else {
274854359Sroberto		if (pcmd->argval[0].uval < NTP_OLDVERSION
274954359Sroberto		    || pcmd->argval[0].uval > NTP_VERSION) {
275054359Sroberto			(void) fprintf(stderr, "versions %d to %d, please\n",
275154359Sroberto				       NTP_OLDVERSION, NTP_VERSION);
275254359Sroberto		} else {
275354359Sroberto			pktversion = (u_char) pcmd->argval[0].uval;
275454359Sroberto		}
275554359Sroberto	}
275654359Sroberto}
275754359Sroberto
275854359Sroberto
2759290001Sglebiusstatic void __attribute__((__format__(__printf__, 1, 0)))
2760290001Sglebiusvwarning(const char *fmt, va_list ap)
2761290001Sglebius{
2762290001Sglebius	int serrno = errno;
2763290001Sglebius	(void) fprintf(stderr, "%s: ", progname);
2764290001Sglebius	vfprintf(stderr, fmt, ap);
2765293896Sglebius	(void) fprintf(stderr, ": %s\n", strerror(serrno));
2766290001Sglebius}
2767290001Sglebius
276854359Sroberto/*
276954359Sroberto * warning - print a warning message
277054359Sroberto */
2771290001Sglebiusstatic void __attribute__((__format__(__printf__, 1, 2)))
277254359Srobertowarning(
277354359Sroberto	const char *fmt,
2774290001Sglebius	...
277554359Sroberto	)
277654359Sroberto{
2777290001Sglebius	va_list ap;
2778290001Sglebius	va_start(ap, fmt);
2779290001Sglebius	vwarning(fmt, ap);
2780290001Sglebius	va_end(ap);
278154359Sroberto}
278254359Sroberto
278354359Sroberto
278454359Sroberto/*
278554359Sroberto * error - print a message and exit
278654359Sroberto */
2787290001Sglebiusstatic void __attribute__((__format__(__printf__, 1, 2)))
278854359Srobertoerror(
278954359Sroberto	const char *fmt,
2790290001Sglebius	...
279154359Sroberto	)
279254359Sroberto{
2793290001Sglebius	va_list ap;
2794290001Sglebius	va_start(ap, fmt);
2795290001Sglebius	vwarning(fmt, ap);
2796290001Sglebius	va_end(ap);
279754359Sroberto	exit(1);
279854359Sroberto}
279954359Sroberto/*
280054359Sroberto * getkeyid - prompt the user for a keyid to use
280154359Sroberto */
280254359Srobertostatic u_long
280354359Srobertogetkeyid(
280454359Sroberto	const char *keyprompt
280554359Sroberto	)
280654359Sroberto{
2807290001Sglebius	int c;
280854359Sroberto	FILE *fi;
280954359Sroberto	char pbuf[20];
2810290001Sglebius	size_t i;
2811290001Sglebius	size_t ilim;
281254359Sroberto
281354359Sroberto#ifndef SYS_WINNT
281454359Sroberto	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
281554359Sroberto#else
2816290001Sglebius	if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
281754359Sroberto#endif /* SYS_WINNT */
281854359Sroberto		fi = stdin;
2819290001Sglebius	else
282054359Sroberto		setbuf(fi, (char *)NULL);
282154359Sroberto	fprintf(stderr, "%s", keyprompt); fflush(stderr);
2822290001Sglebius	for (i = 0, ilim = COUNTOF(pbuf) - 1;
2823290001Sglebius	     i < ilim && (c = getc(fi)) != '\n' && c != EOF;
2824290001Sglebius	     )
2825290001Sglebius		pbuf[i++] = (char)c;
2826290001Sglebius	pbuf[i] = '\0';
282754359Sroberto	if (fi != stdin)
2828290001Sglebius		fclose(fi);
282954359Sroberto
283054359Sroberto	return (u_long) atoi(pbuf);
283154359Sroberto}
283254359Sroberto
283354359Sroberto
283454359Sroberto/*
283554359Sroberto * atoascii - printable-ize possibly ascii data using the character
283654359Sroberto *	      transformations cat -v uses.
283754359Sroberto */
283854359Srobertostatic void
283954359Srobertoatoascii(
2840290001Sglebius	const char *in,
2841290001Sglebius	size_t in_octets,
2842290001Sglebius	char *out,
2843290001Sglebius	size_t out_octets
284454359Sroberto	)
284554359Sroberto{
2846290001Sglebius	const u_char *	pchIn;
2847290001Sglebius	const u_char *	pchInLimit;
2848290001Sglebius	u_char *	pchOut;
2849290001Sglebius	u_char		c;
285054359Sroberto
2851290001Sglebius	pchIn = (const u_char *)in;
2852290001Sglebius	pchInLimit = pchIn + in_octets;
2853290001Sglebius	pchOut = (u_char *)out;
2854290001Sglebius
2855290001Sglebius	if (NULL == pchIn) {
2856290001Sglebius		if (0 < out_octets)
2857290001Sglebius			*pchOut = '\0';
285854359Sroberto		return;
285954359Sroberto	}
286054359Sroberto
2861290001Sglebius#define	ONEOUT(c)					\
2862290001Sglebiusdo {							\
2863290001Sglebius	if (0 == --out_octets) {			\
2864290001Sglebius		*pchOut = '\0';				\
2865290001Sglebius		return;					\
2866290001Sglebius	}						\
2867290001Sglebius	*pchOut++ = (c);				\
2868290001Sglebius} while (0)
2869290001Sglebius
2870290001Sglebius	for (	; pchIn < pchInLimit; pchIn++) {
2871290001Sglebius		c = *pchIn;
2872290001Sglebius		if ('\0' == c)
2873290001Sglebius			break;
2874290001Sglebius		if (c & 0x80) {
2875290001Sglebius			ONEOUT('M');
2876290001Sglebius			ONEOUT('-');
2877290001Sglebius			c &= 0x7f;
287854359Sroberto		}
287954359Sroberto		if (c < ' ') {
2880290001Sglebius			ONEOUT('^');
2881290001Sglebius			ONEOUT((u_char)(c + '@'));
2882290001Sglebius		} else if (0x7f == c) {
2883290001Sglebius			ONEOUT('^');
2884290001Sglebius			ONEOUT('?');
2885290001Sglebius		} else
2886290001Sglebius			ONEOUT(c);
288754359Sroberto	}
2888290001Sglebius	ONEOUT('\0');
2889290001Sglebius
2890290001Sglebius#undef ONEOUT
289154359Sroberto}
289254359Sroberto
289354359Sroberto
289454359Sroberto/*
289554359Sroberto * makeascii - print possibly ascii data using the character
289654359Sroberto *	       transformations that cat -v uses.
289754359Sroberto */
2898290001Sglebiusvoid
289954359Srobertomakeascii(
2900293896Sglebius	size_t length,
2901290001Sglebius	const char *data,
290254359Sroberto	FILE *fp
290354359Sroberto	)
290454359Sroberto{
2905290001Sglebius	const u_char *data_u_char;
2906290001Sglebius	const u_char *cp;
2907290001Sglebius	int c;
290854359Sroberto
2909290001Sglebius	data_u_char = (const u_char *)data;
2910290001Sglebius
2911290001Sglebius	for (cp = data_u_char; cp < data_u_char + length; cp++) {
291254359Sroberto		c = (int)*cp;
2913290001Sglebius		if (c & 0x80) {
291454359Sroberto			putc('M', fp);
291554359Sroberto			putc('-', fp);
2916290001Sglebius			c &= 0x7f;
291754359Sroberto		}
291854359Sroberto
291954359Sroberto		if (c < ' ') {
292054359Sroberto			putc('^', fp);
2921290001Sglebius			putc(c + '@', fp);
2922290001Sglebius		} else if (0x7f == c) {
292354359Sroberto			putc('^', fp);
292454359Sroberto			putc('?', fp);
2925290001Sglebius		} else
292654359Sroberto			putc(c, fp);
292754359Sroberto	}
292854359Sroberto}
292954359Sroberto
293054359Sroberto
293154359Sroberto/*
293254359Sroberto * asciize - same thing as makeascii except add a newline
293354359Sroberto */
293454359Srobertovoid
293554359Srobertoasciize(
293654359Sroberto	int length,
293754359Sroberto	char *data,
293854359Sroberto	FILE *fp
293954359Sroberto	)
294054359Sroberto{
294154359Sroberto	makeascii(length, data, fp);
294254359Sroberto	putc('\n', fp);
294354359Sroberto}
294454359Sroberto
294554359Sroberto
294654359Sroberto/*
2947290001Sglebius * truncate string to fit clipping excess at end.
2948290001Sglebius *	"too long"	->	"too l"
2949290001Sglebius * Used for hostnames.
2950290001Sglebius */
2951290001Sglebiusconst char *
2952290001Sglebiustrunc_right(
2953290001Sglebius	const char *	src,
2954290001Sglebius	size_t		width
2955290001Sglebius	)
2956290001Sglebius{
2957290001Sglebius	size_t	sl;
2958290001Sglebius	char *	out;
2959290001Sglebius
2960290001Sglebius
2961290001Sglebius	sl = strlen(src);
2962290001Sglebius	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
2963290001Sglebius		LIB_GETBUF(out);
2964290001Sglebius		memcpy(out, src, width);
2965290001Sglebius		out[width] = '\0';
2966290001Sglebius
2967290001Sglebius		return out;
2968290001Sglebius	}
2969290001Sglebius
2970290001Sglebius	return src;
2971290001Sglebius}
2972290001Sglebius
2973290001Sglebius
2974290001Sglebius/*
2975290001Sglebius * truncate string to fit by preserving right side and using '_' to hint
2976290001Sglebius *	"too long"	->	"_long"
2977290001Sglebius * Used for local IPv6 addresses, where low bits differentiate.
2978290001Sglebius */
2979290001Sglebiusconst char *
2980290001Sglebiustrunc_left(
2981290001Sglebius	const char *	src,
2982290001Sglebius	size_t		width
2983290001Sglebius	)
2984290001Sglebius{
2985290001Sglebius	size_t	sl;
2986290001Sglebius	char *	out;
2987290001Sglebius
2988290001Sglebius
2989290001Sglebius	sl = strlen(src);
2990290001Sglebius	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
2991290001Sglebius		LIB_GETBUF(out);
2992290001Sglebius		out[0] = '_';
2993290001Sglebius		memcpy(&out[1], &src[sl + 1 - width], width);
2994290001Sglebius
2995290001Sglebius		return out;
2996290001Sglebius	}
2997290001Sglebius
2998290001Sglebius	return src;
2999290001Sglebius}
3000290001Sglebius
3001290001Sglebius
3002290001Sglebius/*
300354359Sroberto * Some circular buffer space
300454359Sroberto */
300554359Sroberto#define	CBLEN	80
300654359Sroberto#define	NUMCB	6
300754359Sroberto
300854359Srobertochar circ_buf[NUMCB][CBLEN];
300954359Srobertoint nextcb = 0;
301054359Sroberto
301154359Sroberto/*
301254359Sroberto * nextvar - find the next variable in the buffer
301354359Sroberto */
301454359Srobertoint
301554359Srobertonextvar(
3016293896Sglebius	size_t *datalen,
3017290001Sglebius	const char **datap,
301854359Sroberto	char **vname,
301954359Sroberto	char **vvalue
302054359Sroberto	)
302154359Sroberto{
3022290001Sglebius	const char *cp;
3023290001Sglebius	const char *np;
3024290001Sglebius	const char *cpend;
3025290001Sglebius	size_t srclen;
3026290001Sglebius	size_t len;
302754359Sroberto	static char name[MAXVARLEN];
302854359Sroberto	static char value[MAXVALLEN];
302954359Sroberto
303054359Sroberto	cp = *datap;
303154359Sroberto	cpend = cp + *datalen;
303254359Sroberto
303354359Sroberto	/*
303454359Sroberto	 * Space past commas and white space
303554359Sroberto	 */
303654359Sroberto	while (cp < cpend && (*cp == ',' || isspace((int)*cp)))
3037290001Sglebius		cp++;
3038290001Sglebius	if (cp >= cpend)
3039290001Sglebius		return 0;
3040290001Sglebius
304154359Sroberto	/*
304254359Sroberto	 * Copy name until we hit a ',', an '=', a '\r' or a '\n'.  Backspace
304354359Sroberto	 * over any white space and terminate it.
304454359Sroberto	 */
3045290001Sglebius	srclen = strcspn(cp, ",=\r\n");
3046290001Sglebius	srclen = min(srclen, (size_t)(cpend - cp));
3047290001Sglebius	len = srclen;
3048290001Sglebius	while (len > 0 && isspace((unsigned char)cp[len - 1]))
3049290001Sglebius		len--;
3050294905Sdelphij	if (len >= sizeof(name))
3051294905Sdelphij	    return 0;
3052290001Sglebius	if (len > 0)
3053290001Sglebius		memcpy(name, cp, len);
3054290001Sglebius	name[len] = '\0';
305554359Sroberto	*vname = name;
3056290001Sglebius	cp += srclen;
305754359Sroberto
305854359Sroberto	/*
305954359Sroberto	 * Check if we hit the end of the buffer or a ','.  If so we are done.
306054359Sroberto	 */
3061290001Sglebius	if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
3062290001Sglebius		if (cp < cpend)
3063290001Sglebius			cp++;
306454359Sroberto		*datap = cp;
3065293896Sglebius		*datalen = size2int_sat(cpend - cp);
3066290001Sglebius		*vvalue = NULL;
306754359Sroberto		return 1;
306854359Sroberto	}
306954359Sroberto
307054359Sroberto	/*
307154359Sroberto	 * So far, so good.  Copy out the value
307254359Sroberto	 */
307354359Sroberto	cp++;	/* past '=' */
3074290001Sglebius	while (cp < cpend && (isspace((unsigned char)*cp) && *cp != '\r' && *cp != '\n'))
3075290001Sglebius		cp++;
3076290001Sglebius	np = cp;
3077290001Sglebius	if ('"' == *np) {
3078290001Sglebius		do {
3079290001Sglebius			np++;
3080290001Sglebius		} while (np < cpend && '"' != *np);
3081290001Sglebius		if (np < cpend && '"' == *np)
3082290001Sglebius			np++;
3083290001Sglebius	} else {
3084290001Sglebius		while (np < cpend && ',' != *np && '\r' != *np)
3085290001Sglebius			np++;
308654359Sroberto	}
3087290001Sglebius	len = np - cp;
3088290001Sglebius	if (np > cpend || len >= sizeof(value) ||
3089290001Sglebius	    (np < cpend && ',' != *np && '\r' != *np))
3090290001Sglebius		return 0;
3091290001Sglebius	memcpy(value, cp, len);
309254359Sroberto	/*
309354359Sroberto	 * Trim off any trailing whitespace
309454359Sroberto	 */
3095290001Sglebius	while (len > 0 && isspace((unsigned char)value[len - 1]))
3096290001Sglebius		len--;
3097290001Sglebius	value[len] = '\0';
309854359Sroberto
309954359Sroberto	/*
310054359Sroberto	 * Return this.  All done.
310154359Sroberto	 */
3102290001Sglebius	if (np < cpend && ',' == *np)
3103290001Sglebius		np++;
3104290001Sglebius	*datap = np;
3105293896Sglebius	*datalen = size2int_sat(cpend - np);
310654359Sroberto	*vvalue = value;
310754359Sroberto	return 1;
310854359Sroberto}
310954359Sroberto
311054359Sroberto
3111290001Sglebiusu_short
3112290001Sglebiusvarfmt(const char * varname)
311354359Sroberto{
3114290001Sglebius	u_int n;
311554359Sroberto
3116290001Sglebius	for (n = 0; n < COUNTOF(cookedvars); n++)
3117290001Sglebius		if (!strcmp(varname, cookedvars[n].varname))
3118290001Sglebius			return cookedvars[n].fmt;
3119290001Sglebius
3120290001Sglebius	return PADDING;
312154359Sroberto}
312254359Sroberto
312354359Sroberto
312454359Sroberto/*
312554359Sroberto * printvars - print variables returned in response packet
312654359Sroberto */
312754359Srobertovoid
312854359Srobertoprintvars(
3129293896Sglebius	size_t length,
3130290001Sglebius	const char *data,
313154359Sroberto	int status,
313254359Sroberto	int sttype,
3133290001Sglebius	int quiet,
313454359Sroberto	FILE *fp
313554359Sroberto	)
313654359Sroberto{
313754359Sroberto	if (rawmode)
3138290001Sglebius	    rawprint(sttype, length, data, status, quiet, fp);
313954359Sroberto	else
3140290001Sglebius	    cookedprint(sttype, length, data, status, quiet, fp);
314154359Sroberto}
314254359Sroberto
314354359Sroberto
314454359Sroberto/*
314554359Sroberto * rawprint - do a printout of the data in raw mode
314654359Sroberto */
314754359Srobertostatic void
314854359Srobertorawprint(
314954359Sroberto	int datatype,
3150293896Sglebius	size_t length,
3151290001Sglebius	const char *data,
315254359Sroberto	int status,
3153290001Sglebius	int quiet,
315454359Sroberto	FILE *fp
315554359Sroberto	)
315654359Sroberto{
3157290001Sglebius	const char *cp;
3158290001Sglebius	const char *cpend;
315954359Sroberto
316054359Sroberto	/*
316154359Sroberto	 * Essentially print the data as is.  We reformat unprintables, though.
316254359Sroberto	 */
316354359Sroberto	cp = data;
316454359Sroberto	cpend = data + length;
316554359Sroberto
3166290001Sglebius	if (!quiet)
3167290001Sglebius		(void) fprintf(fp, "status=0x%04x,\n", status);
316854359Sroberto
316954359Sroberto	while (cp < cpend) {
317054359Sroberto		if (*cp == '\r') {
317154359Sroberto			/*
317254359Sroberto			 * If this is a \r and the next character is a
317354359Sroberto			 * \n, supress this, else pretty print it.  Otherwise
317454359Sroberto			 * just output the character.
317554359Sroberto			 */
3176290001Sglebius			if (cp == (cpend - 1) || *(cp + 1) != '\n')
317754359Sroberto			    makeascii(1, cp, fp);
3178290001Sglebius		} else if (isspace((unsigned char)*cp) || isprint((unsigned char)*cp))
317954359Sroberto			putc(*cp, fp);
3180290001Sglebius		else
318154359Sroberto			makeascii(1, cp, fp);
318254359Sroberto		cp++;
318354359Sroberto	}
318454359Sroberto}
318554359Sroberto
318654359Sroberto
318754359Sroberto/*
318854359Sroberto * Global data used by the cooked output routines
318954359Sroberto */
319054359Srobertoint out_chars;		/* number of characters output */
319154359Srobertoint out_linecount;	/* number of characters output on this line */
319254359Sroberto
319354359Sroberto
319454359Sroberto/*
319554359Sroberto * startoutput - get ready to do cooked output
319654359Sroberto */
319754359Srobertostatic void
319854359Srobertostartoutput(void)
319954359Sroberto{
320054359Sroberto	out_chars = 0;
320154359Sroberto	out_linecount = 0;
320254359Sroberto}
320354359Sroberto
320454359Sroberto
320554359Sroberto/*
320654359Sroberto * output - output a variable=value combination
320754359Sroberto */
320854359Srobertostatic void
320954359Srobertooutput(
321054359Sroberto	FILE *fp,
3211290001Sglebius	const char *name,
3212290001Sglebius	const char *value
321354359Sroberto	)
321454359Sroberto{
3215293896Sglebius	int len;
321654359Sroberto
3217290001Sglebius	/* strlen of "name=value" */
3218293896Sglebius	len = size2int_sat(strlen(name) + 1 + strlen(value));
321954359Sroberto
322054359Sroberto	if (out_chars != 0) {
3221290001Sglebius		out_chars += 2;
3222290001Sglebius		if ((out_linecount + len + 2) > MAXOUTLINE) {
3223290001Sglebius			fputs(",\n", fp);
322454359Sroberto			out_linecount = 0;
322554359Sroberto		} else {
3226290001Sglebius			fputs(", ", fp);
3227290001Sglebius			out_linecount += 2;
322854359Sroberto		}
322954359Sroberto	}
323054359Sroberto
323154359Sroberto	fputs(name, fp);
323254359Sroberto	putc('=', fp);
323354359Sroberto	fputs(value, fp);
3234290001Sglebius	out_chars += len;
3235290001Sglebius	out_linecount += len;
323654359Sroberto}
323754359Sroberto
323854359Sroberto
323954359Sroberto/*
324054359Sroberto * endoutput - terminate a block of cooked output
324154359Sroberto */
324254359Srobertostatic void
324354359Srobertoendoutput(
324454359Sroberto	FILE *fp
324554359Sroberto	)
324654359Sroberto{
324754359Sroberto	if (out_chars != 0)
3248290001Sglebius		putc('\n', fp);
324954359Sroberto}
325054359Sroberto
325154359Sroberto
325254359Sroberto/*
325354359Sroberto * outputarr - output an array of values
325454359Sroberto */
325554359Srobertostatic void
325654359Srobertooutputarr(
325754359Sroberto	FILE *fp,
325854359Sroberto	char *name,
325954359Sroberto	int narr,
326054359Sroberto	l_fp *lfp
326154359Sroberto	)
326254359Sroberto{
3263293896Sglebius	char *bp;
3264293896Sglebius	char *cp;
3265293896Sglebius	size_t i;
3266293896Sglebius	size_t len;
326754359Sroberto	char buf[256];
326854359Sroberto
326954359Sroberto	bp = buf;
327054359Sroberto	/*
327154359Sroberto	 * Hack to align delay and offset values
327254359Sroberto	 */
327354359Sroberto	for (i = (int)strlen(name); i < 11; i++)
327454359Sroberto	    *bp++ = ' ';
3275290001Sglebius
327654359Sroberto	for (i = narr; i > 0; i--) {
327754359Sroberto		if (i != narr)
327854359Sroberto		    *bp++ = ' ';
327954359Sroberto		cp = lfptoms(lfp, 2);
328054359Sroberto		len = strlen(cp);
328154359Sroberto		if (len > 7) {
328254359Sroberto			cp[7] = '\0';
328354359Sroberto			len = 7;
328454359Sroberto		}
328554359Sroberto		while (len < 7) {
328654359Sroberto			*bp++ = ' ';
328754359Sroberto			len++;
328854359Sroberto		}
328954359Sroberto		while (*cp != '\0')
329054359Sroberto		    *bp++ = *cp++;
329154359Sroberto		lfp++;
329254359Sroberto	}
329354359Sroberto	*bp = '\0';
329454359Sroberto	output(fp, name, buf);
329554359Sroberto}
329654359Sroberto
329754359Srobertostatic char *
329854359Srobertotstflags(
329954359Sroberto	u_long val
330054359Sroberto	)
330154359Sroberto{
3302290001Sglebius	register char *cp, *s;
3303290001Sglebius	size_t cb;
330454359Sroberto	register int i;
330554359Sroberto	register const char *sep;
330654359Sroberto
330754359Sroberto	sep = "";
3308290001Sglebius	s = cp = circ_buf[nextcb];
330954359Sroberto	if (++nextcb >= NUMCB)
3310290001Sglebius		nextcb = 0;
3311290001Sglebius	cb = sizeof(circ_buf[0]);
331254359Sroberto
3313290001Sglebius	snprintf(cp, cb, "%02lx", val);
3314290001Sglebius	cp += strlen(cp);
3315290001Sglebius	cb -= strlen(cp);
331654359Sroberto	if (!val) {
3317290001Sglebius		strlcat(cp, " ok", cb);
3318290001Sglebius		cp += strlen(cp);
3319290001Sglebius		cb -= strlen(cp);
332054359Sroberto	} else {
3321290001Sglebius		if (cb) {
3322290001Sglebius			*cp++ = ' ';
3323290001Sglebius			cb--;
3324290001Sglebius		}
3325290001Sglebius		for (i = 0; i < (int)COUNTOF(tstflagnames); i++) {
332654359Sroberto			if (val & 0x1) {
3327290001Sglebius				snprintf(cp, cb, "%s%s", sep,
3328290001Sglebius					 tstflagnames[i]);
332954359Sroberto				sep = ", ";
3330290001Sglebius				cp += strlen(cp);
3331290001Sglebius				cb -= strlen(cp);
333254359Sroberto			}
333354359Sroberto			val >>= 1;
333454359Sroberto		}
333554359Sroberto	}
3336290001Sglebius	if (cb)
3337290001Sglebius		*cp = '\0';
3338290001Sglebius
333954359Sroberto	return s;
334054359Sroberto}
334154359Sroberto
334254359Sroberto/*
334354359Sroberto * cookedprint - output variables in cooked mode
334454359Sroberto */
334554359Srobertostatic void
334654359Srobertocookedprint(
334754359Sroberto	int datatype,
3348293896Sglebius	size_t length,
3349290001Sglebius	const char *data,
335054359Sroberto	int status,
3351290001Sglebius	int quiet,
335254359Sroberto	FILE *fp
335354359Sroberto	)
335454359Sroberto{
335554359Sroberto	char *name;
335654359Sroberto	char *value;
3357132451Sroberto	char output_raw;
335854359Sroberto	int fmt;
335954359Sroberto	l_fp lfp;
3360290001Sglebius	sockaddr_u hval;
336154359Sroberto	u_long uval;
3362290001Sglebius	int narr;
3363290001Sglebius	size_t len;
336454359Sroberto	l_fp lfparr[8];
3365290001Sglebius	char b[12];
3366290001Sglebius	char bn[2 * MAXVARLEN];
3367290001Sglebius	char bv[2 * MAXVALLEN];
336854359Sroberto
3369290001Sglebius	UNUSED_ARG(datatype);
337054359Sroberto
3371290001Sglebius	if (!quiet)
3372290001Sglebius		fprintf(fp, "status=%04x %s,\n", status,
3373290001Sglebius			statustoa(datatype, status));
337454359Sroberto
337554359Sroberto	startoutput();
337654359Sroberto	while (nextvar(&length, &data, &name, &value)) {
3377290001Sglebius		fmt = varfmt(name);
3378290001Sglebius		output_raw = 0;
3379290001Sglebius		switch (fmt) {
3380290001Sglebius
3381290001Sglebius		case PADDING:
338254359Sroberto			output_raw = '*';
3383290001Sglebius			break;
338454359Sroberto
3385290001Sglebius		case TS:
3386290001Sglebius			if (!decodets(value, &lfp))
3387290001Sglebius				output_raw = '?';
3388290001Sglebius			else
3389290001Sglebius				output(fp, name, prettydate(&lfp));
3390290001Sglebius			break;
339154359Sroberto
3392290001Sglebius		case HA:	/* fallthru */
3393290001Sglebius		case NA:
3394290001Sglebius			if (!decodenetnum(value, &hval)) {
3395290001Sglebius				output_raw = '?';
3396290001Sglebius			} else if (fmt == HA){
3397290001Sglebius				output(fp, name, nntohost(&hval));
3398290001Sglebius			} else {
3399290001Sglebius				output(fp, name, stoa(&hval));
3400290001Sglebius			}
3401290001Sglebius			break;
340254359Sroberto
3403290001Sglebius		case RF:
3404290001Sglebius			if (decodenetnum(value, &hval)) {
3405290001Sglebius				if (ISREFCLOCKADR(&hval))
3406290001Sglebius					output(fp, name,
3407290001Sglebius					       refnumtoa(&hval));
340854359Sroberto				else
3409290001Sglebius					output(fp, name, stoa(&hval));
3410290001Sglebius			} else if (strlen(value) <= 4) {
3411290001Sglebius				output(fp, name, value);
3412290001Sglebius			} else {
3413290001Sglebius				output_raw = '?';
3414290001Sglebius			}
3415290001Sglebius			break;
341654359Sroberto
3417290001Sglebius		case LP:
3418290001Sglebius			if (!decodeuint(value, &uval) || uval > 3) {
3419290001Sglebius				output_raw = '?';
3420290001Sglebius			} else {
3421290001Sglebius				b[0] = (0x2 & uval)
3422290001Sglebius					   ? '1'
3423290001Sglebius					   : '0';
3424290001Sglebius				b[1] = (0x1 & uval)
3425290001Sglebius					   ? '1'
3426290001Sglebius					   : '0';
3427290001Sglebius				b[2] = '\0';
3428290001Sglebius				output(fp, name, b);
342954359Sroberto			}
3430290001Sglebius			break;
343154359Sroberto
3432290001Sglebius		case OC:
3433290001Sglebius			if (!decodeuint(value, &uval)) {
3434290001Sglebius				output_raw = '?';
3435290001Sglebius			} else {
3436290001Sglebius				snprintf(b, sizeof(b), "%03lo", uval);
3437290001Sglebius				output(fp, name, b);
3438290001Sglebius			}
3439290001Sglebius			break;
3440290001Sglebius
3441290001Sglebius		case AR:
3442290001Sglebius			if (!decodearr(value, &narr, lfparr))
3443290001Sglebius				output_raw = '?';
3444290001Sglebius			else
3445290001Sglebius				outputarr(fp, name, narr, lfparr);
3446290001Sglebius			break;
3447290001Sglebius
3448290001Sglebius		case FX:
3449290001Sglebius			if (!decodeuint(value, &uval))
3450290001Sglebius				output_raw = '?';
3451290001Sglebius			else
3452290001Sglebius				output(fp, name, tstflags(uval));
3453290001Sglebius			break;
3454290001Sglebius
3455290001Sglebius		default:
3456290001Sglebius			fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
3457290001Sglebius				name, value, fmt);
3458290001Sglebius			output_raw = '?';
3459290001Sglebius			break;
346054359Sroberto		}
3461290001Sglebius
346254359Sroberto		if (output_raw != 0) {
3463290001Sglebius			/* TALOS-CAN-0063: avoid buffer overrun */
3464290001Sglebius			atoascii(name, MAXVARLEN, bn, sizeof(bn));
346554359Sroberto			if (output_raw != '*') {
3466290001Sglebius				atoascii(value, MAXVALLEN,
3467290001Sglebius					 bv, sizeof(bv) - 1);
346854359Sroberto				len = strlen(bv);
346954359Sroberto				bv[len] = output_raw;
347054359Sroberto				bv[len+1] = '\0';
3471290001Sglebius			} else {
3472290001Sglebius				atoascii(value, MAXVALLEN,
3473290001Sglebius					 bv, sizeof(bv));
347454359Sroberto			}
347554359Sroberto			output(fp, bn, bv);
347654359Sroberto		}
347754359Sroberto	}
347854359Sroberto	endoutput(fp);
347954359Sroberto}
348054359Sroberto
348154359Sroberto
348254359Sroberto/*
348354359Sroberto * sortassoc - sort associations in the cache into ascending order
348454359Sroberto */
348554359Srobertovoid
348654359Srobertosortassoc(void)
348754359Sroberto{
348854359Sroberto	if (numassoc > 1)
3489290001Sglebius		qsort(assoc_cache, (size_t)numassoc,
3490290001Sglebius		      sizeof(assoc_cache[0]), &assoccmp);
349154359Sroberto}
349254359Sroberto
349354359Sroberto
349454359Sroberto/*
349554359Sroberto * assoccmp - compare two associations
349654359Sroberto */
349754359Srobertostatic int
349854359Srobertoassoccmp(
349954359Sroberto	const void *t1,
350054359Sroberto	const void *t2
350154359Sroberto	)
350254359Sroberto{
3503290001Sglebius	const struct association *ass1 = t1;
3504290001Sglebius	const struct association *ass2 = t2;
350554359Sroberto
350654359Sroberto	if (ass1->assid < ass2->assid)
3507290001Sglebius		return -1;
350854359Sroberto	if (ass1->assid > ass2->assid)
3509290001Sglebius		return 1;
351054359Sroberto	return 0;
351154359Sroberto}
3512290001Sglebius
3513290001Sglebius
3514290001Sglebius/*
3515290001Sglebius * grow_assoc_cache() - enlarge dynamic assoc_cache array
3516290001Sglebius *
3517290001Sglebius * The strategy is to add an assumed 4k page size at a time, leaving
3518290001Sglebius * room for malloc() bookkeeping overhead equivalent to 4 pointers.
3519290001Sglebius */
3520290001Sglebiusvoid
3521290001Sglebiusgrow_assoc_cache(void)
3522290001Sglebius{
3523290001Sglebius	static size_t	prior_sz;
3524290001Sglebius	size_t		new_sz;
3525290001Sglebius
3526290001Sglebius	new_sz = prior_sz + 4 * 1024;
3527290001Sglebius	if (0 == prior_sz) {
3528290001Sglebius		new_sz -= 4 * sizeof(void *);
3529290001Sglebius	}
3530290001Sglebius	assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz);
3531290001Sglebius	prior_sz = new_sz;
3532293896Sglebius	assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0]));
3533290001Sglebius}
3534290001Sglebius
3535290001Sglebius
3536290001Sglebius/*
3537290001Sglebius * ntpq_custom_opt_handler - autoopts handler for -c and -p
3538290001Sglebius *
3539290001Sglebius * By default, autoopts loses the relative order of -c and -p options
3540290001Sglebius * on the command line.  This routine replaces the default handler for
3541290001Sglebius * those routines and builds a list of commands to execute preserving
3542290001Sglebius * the order.
3543290001Sglebius */
3544290001Sglebiusvoid
3545290001Sglebiusntpq_custom_opt_handler(
3546290001Sglebius	tOptions *pOptions,
3547290001Sglebius	tOptDesc *pOptDesc
354854359Sroberto	)
354954359Sroberto{
3550290001Sglebius	switch (pOptDesc->optValue) {
3551290001Sglebius
3552290001Sglebius	default:
3553290001Sglebius		fprintf(stderr,
3554290001Sglebius			"ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3555290001Sglebius			pOptDesc->optValue, pOptDesc->optValue);
3556290001Sglebius		exit(1);
3557290001Sglebius
3558290001Sglebius	case 'c':
3559290001Sglebius		ADDCMD(pOptDesc->pzLastArg);
3560290001Sglebius		break;
3561290001Sglebius
3562290001Sglebius	case 'p':
3563290001Sglebius		ADDCMD("peers");
3564290001Sglebius		break;
3565290001Sglebius	}
356654359Sroberto}
3567290001Sglebius/*
3568290001Sglebius * Obtain list of digest names
3569290001Sglebius */
3570290001Sglebius
3571290001Sglebius#ifdef OPENSSL
3572290001Sglebius# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3573290001Sglebiusstruct hstate {
3574290001Sglebius   char *list;
3575290001Sglebius   const char **seen;
3576290001Sglebius   int idx;
3577290001Sglebius};
3578290001Sglebius#define K_PER_LINE 8
3579290001Sglebius#define K_NL_PFX_STR "\n    "
3580290001Sglebius#define K_DELIM_STR ", "
3581290001Sglebiusstatic void list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg )
3582290001Sglebius{
3583290001Sglebius    size_t len, n;
3584290001Sglebius    const char *name, *cp, **seen;
3585290001Sglebius    struct hstate *hstate = arg;
3586310419Sdelphij    EVP_MD_CTX *ctx;
3587290001Sglebius    u_int digest_len;
3588290001Sglebius    u_char digest[EVP_MAX_MD_SIZE];
3589290001Sglebius
3590290001Sglebius    if (!m)
3591290001Sglebius        return; /* Ignore aliases */
3592290001Sglebius
3593290001Sglebius    name = EVP_MD_name(m);
3594290001Sglebius
3595290001Sglebius    /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */
3596290001Sglebius
3597290001Sglebius    for( cp = name; *cp; cp++ ) {
3598290001Sglebius	if( islower(*cp) )
3599290001Sglebius	    return;
3600290001Sglebius    }
3601290001Sglebius    len = (cp - name) + 1;
3602290001Sglebius
3603290001Sglebius    /* There are duplicates.  Discard if name has been seen. */
3604290001Sglebius
3605290001Sglebius    for (seen = hstate->seen; *seen; seen++)
3606290001Sglebius        if (!strcmp(*seen, name))
3607290001Sglebius	    return;
3608290001Sglebius    n = (seen - hstate->seen) + 2;
3609290001Sglebius    hstate->seen = erealloc(hstate->seen, n * sizeof(*seen));
3610290001Sglebius    hstate->seen[n-2] = name;
3611290001Sglebius    hstate->seen[n-1] = NULL;
3612290001Sglebius
3613290001Sglebius    /* Discard MACs that NTP won't accept.
3614290001Sglebius     * Keep this consistent with keytype_from_text() in ssl_init.c.
3615290001Sglebius     */
3616290001Sglebius
3617310419Sdelphij    ctx = EVP_MD_CTX_new();
3618310419Sdelphij    EVP_DigestInit(ctx, EVP_get_digestbyname(name));
3619310419Sdelphij    EVP_DigestFinal(ctx, digest, &digest_len);
3620310419Sdelphij    EVP_MD_CTX_free(ctx);
3621290001Sglebius    if (digest_len > (MAX_MAC_LEN - sizeof(keyid_t)))
3622290001Sglebius        return;
3623290001Sglebius
3624290001Sglebius    if (hstate->list != NULL)
3625290001Sglebius	len += strlen(hstate->list);
3626290001Sglebius    len += (hstate->idx >= K_PER_LINE)? strlen(K_NL_PFX_STR): strlen(K_DELIM_STR);
3627290001Sglebius
3628290001Sglebius    if (hstate->list == NULL) {
3629290001Sglebius	hstate->list = (char *)emalloc(len);
3630290001Sglebius	hstate->list[0] = '\0';
3631290001Sglebius    } else
3632290001Sglebius	hstate->list = (char *)erealloc(hstate->list, len);
3633290001Sglebius
3634290001Sglebius    sprintf(hstate->list + strlen(hstate->list), "%s%s",
3635290001Sglebius	    ((hstate->idx >= K_PER_LINE)? K_NL_PFX_STR : K_DELIM_STR),
3636290001Sglebius	    name);
3637290001Sglebius    if (hstate->idx >= K_PER_LINE)
3638290001Sglebius	hstate->idx = 1;
3639290001Sglebius    else
3640290001Sglebius	hstate->idx++;
3641290001Sglebius}
3642290001Sglebius# endif
3643290001Sglebius#endif
3644290001Sglebius
3645290001Sglebiusstatic char *list_digest_names(void)
3646290001Sglebius{
3647290001Sglebius    char *list = NULL;
3648290001Sglebius
3649290001Sglebius#ifdef OPENSSL
3650290001Sglebius# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3651290001Sglebius    struct hstate hstate = { NULL, NULL, K_PER_LINE+1 };
3652290001Sglebius
3653290001Sglebius    hstate.seen = (const char **) emalloc_zero(1*sizeof( const char * )); // replaces -> calloc(1, sizeof( const char * ));
3654290001Sglebius
3655290001Sglebius    INIT_SSL();
3656290001Sglebius    EVP_MD_do_all_sorted(list_md_fn, &hstate);
3657290001Sglebius    list = hstate.list;
3658290001Sglebius    free(hstate.seen);
3659290001Sglebius# else
3660290001Sglebius    list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)"));
3661290001Sglebius    strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)");
3662290001Sglebius# endif
3663290001Sglebius#else
3664290001Sglebius    list = (char *)emalloc(sizeof("md5"));
3665290001Sglebius    strcpy(list, "md5");
3666290001Sglebius#endif
3667290001Sglebius
3668290001Sglebius    return list;
3669290001Sglebius}
3670293896Sglebius
3671293896Sglebius#define CTRLC_STACK_MAX 4
3672293896Sglebiusstatic volatile size_t		ctrlc_stack_len = 0;
3673293896Sglebiusstatic volatile Ctrl_C_Handler	ctrlc_stack[CTRLC_STACK_MAX];
3674293896Sglebius
3675293896Sglebius
3676293896Sglebius
3677293896Sglebiusint/*BOOL*/
3678293896Sglebiuspush_ctrl_c_handler(
3679293896Sglebius	Ctrl_C_Handler func
3680293896Sglebius	)
3681293896Sglebius{
3682293896Sglebius	size_t size = ctrlc_stack_len;
3683293896Sglebius	if (func && (size < CTRLC_STACK_MAX)) {
3684293896Sglebius		ctrlc_stack[size] = func;
3685293896Sglebius		ctrlc_stack_len = size + 1;
3686293896Sglebius		return TRUE;
3687293896Sglebius	}
3688293896Sglebius	return FALSE;
3689293896Sglebius}
3690293896Sglebius
3691293896Sglebiusint/*BOOL*/
3692293896Sglebiuspop_ctrl_c_handler(
3693293896Sglebius	Ctrl_C_Handler func
3694293896Sglebius	)
3695293896Sglebius{
3696293896Sglebius	size_t size = ctrlc_stack_len;
3697293896Sglebius	if (size) {
3698293896Sglebius		--size;
3699293896Sglebius		if (func == NULL || func == ctrlc_stack[size]) {
3700293896Sglebius			ctrlc_stack_len = size;
3701293896Sglebius			return TRUE;
3702293896Sglebius		}
3703293896Sglebius	}
3704293896Sglebius	return FALSE;
3705293896Sglebius}
3706293896Sglebius
3707293896Sglebiusstatic void
3708293896Sglebiuson_ctrlc(void)
3709293896Sglebius{
3710293896Sglebius	size_t size = ctrlc_stack_len;
3711293896Sglebius	while (size)
3712293896Sglebius		if ((*ctrlc_stack[--size])())
3713293896Sglebius			break;
3714293896Sglebius}
3715294905Sdelphij
3716294905Sdelphijstatic int
3717294905Sdelphijmy_easprintf(
3718294905Sdelphij	char ** 	ppinto,
3719294905Sdelphij	const char *	fmt   ,
3720294905Sdelphij	...
3721294905Sdelphij	)
3722294905Sdelphij{
3723294905Sdelphij	va_list	va;
3724294905Sdelphij	int	prc;
3725294905Sdelphij	size_t	len = 128;
3726294905Sdelphij	char *	buf = emalloc(len);
3727294905Sdelphij
3728294905Sdelphij  again:
3729294905Sdelphij	/* Note: we expect the memory allocation to fail long before the
3730294905Sdelphij	 * increment in buffer size actually overflows.
3731294905Sdelphij	 */
3732294905Sdelphij	buf = (buf) ? erealloc(buf, len) : emalloc(len);
3733294905Sdelphij
3734294905Sdelphij	va_start(va, fmt);
3735294905Sdelphij	prc = vsnprintf(buf, len, fmt, va);
3736294905Sdelphij	va_end(va);
3737294905Sdelphij
3738294905Sdelphij	if (prc < 0) {
3739294905Sdelphij		/* might be very old vsnprintf. Or actually MSVC... */
3740294905Sdelphij		len += len >> 1;
3741294905Sdelphij		goto again;
3742294905Sdelphij	}
3743294905Sdelphij	if ((size_t)prc >= len) {
3744294905Sdelphij		/* at least we have the proper size now... */
3745294905Sdelphij		len = (size_t)prc + 1;
3746294905Sdelphij		goto again;
3747294905Sdelphij	}
3748294905Sdelphij	if ((size_t)prc < (len - 32))
3749294905Sdelphij		buf = erealloc(buf, (size_t)prc + 1);
3750294905Sdelphij	*ppinto = buf;
3751294905Sdelphij	return prc;
3752294905Sdelphij}
3753