154359Sroberto/*
254359Sroberto * ntpq - query an NTP server using mode 6 commands
354359Sroberto */
4285612Sdelphij#include <config.h>
5182007Sroberto#include <ctype.h>
6182007Sroberto#include <signal.h>
7182007Sroberto#include <setjmp.h>
8330567Sgordon#include <stddef.h>
9330567Sgordon#include <stdio.h>
10182007Sroberto#include <sys/types.h>
11182007Sroberto#include <sys/time.h>
12285612Sdelphij#ifdef HAVE_UNISTD_H
13285612Sdelphij# include <unistd.h>
14285612Sdelphij#endif
15285612Sdelphij#ifdef HAVE_FCNTL_H
16285612Sdelphij# include <fcntl.h>
17285612Sdelphij#endif
18285612Sdelphij#ifdef SYS_WINNT
19285612Sdelphij# include <mswsock.h>
20285612Sdelphij#endif
21285612Sdelphij#include <isc/net.h>
22285612Sdelphij#include <isc/result.h>
23182007Sroberto
2482498Sroberto#include "ntpq.h"
25285612Sdelphij#include "ntp_assert.h"
26285612Sdelphij#include "ntp_stdlib.h"
2782498Sroberto#include "ntp_unixtime.h"
2882498Sroberto#include "ntp_calendar.h"
2982498Sroberto#include "ntp_select.h"
30285612Sdelphij#include "ntp_assert.h"
31285612Sdelphij#include "lib_strbuf.h"
32285612Sdelphij#include "ntp_lineedit.h"
33285612Sdelphij#include "ntp_debug.h"
34285612Sdelphij#ifdef OPENSSL
35285612Sdelphij#include "openssl/evp.h"
36285612Sdelphij#include "openssl/objects.h"
37285612Sdelphij#include "openssl/err.h"
38330567Sgordon#ifdef SYS_WINNT
39330567Sgordon# include "openssl/opensslv.h"
40330567Sgordon# if !defined(HAVE_EVP_MD_DO_ALL_SORTED) && OPENSSL_VERSION_NUMBER > 0x10000000L
41330567Sgordon#    define HAVE_EVP_MD_DO_ALL_SORTED	1
42330567Sgordon# endif
43330567Sgordon#endif
44310419Sdelphij#include "libssl_compat.h"
45330567Sgordon
46330567Sgordon#define CMAC "AES128CMAC"
47285612Sdelphij#endif
48285612Sdelphij#include <ssl_applink.c>
4982498Sroberto
50285612Sdelphij#include "ntp_libopts.h"
51293650Sglebius#include "safecast.h"
52182007Sroberto
53285612Sdelphij#ifdef SYS_VXWORKS		/* vxWorks needs mode flag -casey*/
54182007Sroberto# define open(name, flags)   open(name, flags, 0777)
55182007Sroberto# define SERVER_PORT_NUM     123
5654359Sroberto#endif
5754359Sroberto
58182007Sroberto/* we use COMMAND as an autogen keyword */
59182007Sroberto#ifdef COMMAND
60182007Sroberto# undef COMMAND
61182007Sroberto#endif
62182007Sroberto
6354359Sroberto/*
6454359Sroberto * Because we potentially understand a lot of commands we will run
6554359Sroberto * interactive if connected to a terminal.
6654359Sroberto */
6754359Srobertoint interactive = 0;		/* set to 1 when we should prompt */
6854359Srobertoconst char *prompt = "ntpq> ";	/* prompt to ask him about */
6954359Sroberto
70285612Sdelphij/*
71285612Sdelphij * use old readvars behavior?  --old-rv processing in ntpq resets
72285612Sdelphij * this value based on the presence or absence of --old-rv.  It is
73285612Sdelphij * initialized to 1 here to maintain backward compatibility with
74285612Sdelphij * libntpq clients such as ntpsnmpd, which are free to reset it as
75285612Sdelphij * desired.
76285612Sdelphij */
77285612Sdelphijint	old_rv = 1;
7854359Sroberto
79298770Sdelphij/*
80298770Sdelphij * How should we display the refid?
81298770Sdelphij * REFID_HASH, REFID_IPV4
82298770Sdelphij */
83298770Sdelphijte_Refid drefid = -1;
84285612Sdelphij
8554359Sroberto/*
86182007Sroberto * for get_systime()
87182007Sroberto */
88182007Srobertos_char	sys_precision;		/* local clock precision (log2 s) */
89182007Sroberto
90182007Sroberto/*
9154359Sroberto * Keyid used for authenticated requests.  Obtained on the fly.
9254359Sroberto */
93132451Srobertou_long info_auth_keyid = 0;
9454359Sroberto
95285612Sdelphijstatic	int	info_auth_keytype = NID_md5;	/* MD5 */
96285612Sdelphijstatic	size_t	info_auth_hashlen = 16;		/* MD5 */
9754359Srobertou_long	current_time;		/* needed by authkeys; not used */
9854359Sroberto
9954359Sroberto/*
10054359Sroberto * Flag which indicates we should always send authenticated requests
10154359Sroberto */
10254359Srobertoint always_auth = 0;
10354359Sroberto
10454359Sroberto/*
10554359Sroberto * Flag which indicates raw mode output.
10654359Sroberto */
10754359Srobertoint rawmode = 0;
10854359Sroberto
10954359Sroberto/*
11054359Sroberto * Packet version number we use
11154359Sroberto */
11254359Srobertou_char pktversion = NTP_OLDVERSION + 1;
11354359Sroberto
11454359Sroberto/*
11554359Sroberto * Don't jump if no set jmp.
11654359Sroberto */
11754359Srobertovolatile int jump = 0;
11854359Sroberto
11954359Sroberto/*
12054359Sroberto * Format values
12154359Sroberto */
12254359Sroberto#define	PADDING	0
123285612Sdelphij#define	HA	1	/* host address */
124285612Sdelphij#define	NA	2	/* network address */
125285612Sdelphij#define	LP	3	/* leap (print in binary) */
126285612Sdelphij#define	RF	4	/* refid (sometimes string, sometimes not) */
127285612Sdelphij#define	AR	5	/* array of times */
128285612Sdelphij#define FX	6	/* test flags */
129285612Sdelphij#define TS	7	/* l_fp timestamp in hex */
130285612Sdelphij#define	OC	8	/* integer, print in octal */
13154359Sroberto#define	EOV	255	/* end of table */
13254359Sroberto
13354359Sroberto/*
134285612Sdelphij * For the most part ntpq simply displays what ntpd provides in the
135285612Sdelphij * mostly plain-text mode 6 responses.  A few variable names are by
136285612Sdelphij * default "cooked" to provide more human-friendly output.
13754359Sroberto */
138285612Sdelphijconst var_format cookedvars[] = {
139285612Sdelphij	{ "leap",		LP },
140285612Sdelphij	{ "reach",		OC },
141285612Sdelphij	{ "refid",		RF },
142285612Sdelphij	{ "reftime",		TS },
143285612Sdelphij	{ "clock",		TS },
144285612Sdelphij	{ "org",		TS },
145285612Sdelphij	{ "rec",		TS },
146285612Sdelphij	{ "xmt",		TS },
147285612Sdelphij	{ "flash",		FX },
148285612Sdelphij	{ "srcadr",		HA },
149285612Sdelphij	{ "peeradr",		HA },	/* compat with others */
150285612Sdelphij	{ "dstadr",		NA },
151285612Sdelphij	{ "filtdelay",		AR },
152285612Sdelphij	{ "filtoffset",		AR },
153285612Sdelphij	{ "filtdisp",		AR },
154285612Sdelphij	{ "filterror",		AR },	/* compat with others */
15554359Sroberto};
15654359Sroberto
15754359Sroberto
15854359Sroberto
15954359Sroberto/*
16054359Sroberto * flasher bits
16154359Sroberto */
16254359Srobertostatic const char *tstflagnames[] = {
163182007Sroberto	"pkt_dup",		/* TEST1 */
164182007Sroberto	"pkt_bogus",		/* TEST2 */
165285612Sdelphij	"pkt_unsync",		/* TEST3 */
166182007Sroberto	"pkt_denied",		/* TEST4 */
167182007Sroberto	"pkt_auth",		/* TEST5 */
168285612Sdelphij	"pkt_stratum",		/* TEST6 */
169285612Sdelphij	"pkt_header",		/* TEST7 */
170182007Sroberto	"pkt_autokey",		/* TEST8 */
171182007Sroberto	"pkt_crypto",		/* TEST9 */
172182007Sroberto	"peer_stratum",		/* TEST10 */
173182007Sroberto	"peer_dist",		/* TEST11 */
174182007Sroberto	"peer_loop",		/* TEST12 */
175285612Sdelphij	"peer_unreach"		/* TEST13 */
17654359Sroberto};
17754359Sroberto
17854359Sroberto
179285612Sdelphijint		ntpqmain	(int,	char **);
18054359Sroberto/*
18154359Sroberto * Built in command handler declarations
18254359Sroberto */
183285612Sdelphijstatic	int	openhost	(const char *, int);
184285612Sdelphijstatic	void	dump_hex_printable(const void *, size_t);
185285612Sdelphijstatic	int	sendpkt		(void *, size_t);
186293650Sglebiusstatic	int	getresponse	(int, int, u_short *, size_t *, const char **, int);
187293650Sglebiusstatic	int	sendrequest	(int, associd_t, int, size_t, const char *);
188285612Sdelphijstatic	char *	tstflags	(u_long);
189285612Sdelphij#ifndef BUILD_AS_LIB
190285612Sdelphijstatic	void	getcmds		(void);
191285612Sdelphij#ifndef SYS_WINNT
192293650Sglebiusstatic	int	abortcmd	(void);
193285612Sdelphij#endif	/* SYS_WINNT */
194285612Sdelphijstatic	void	docmd		(const char *);
195285612Sdelphijstatic	void	tokenize	(const char *, char **, int *);
196285612Sdelphijstatic	int	getarg		(const char *, int, arg_v *);
197285612Sdelphij#endif	/* BUILD_AS_LIB */
198285612Sdelphijstatic	int	findcmd		(const char *, struct xcmd *,
199285612Sdelphij				 struct xcmd *, struct xcmd **);
200285612Sdelphijstatic	int	rtdatetolfp	(char *, l_fp *);
201330567Sgordonstatic	int	decodearr	(char *, int *, l_fp *, int);
202285612Sdelphijstatic	void	help		(struct parse *, FILE *);
203285612Sdelphijstatic	int	helpsort	(const void *, const void *);
204285612Sdelphijstatic	void	printusage	(struct xcmd *, FILE *);
205285612Sdelphijstatic	void	timeout		(struct parse *, FILE *);
206285612Sdelphijstatic	void	auth_delay	(struct parse *, FILE *);
207285612Sdelphijstatic	void	host		(struct parse *, FILE *);
208285612Sdelphijstatic	void	ntp_poll	(struct parse *, FILE *);
209285612Sdelphijstatic	void	keyid		(struct parse *, FILE *);
210285612Sdelphijstatic	void	keytype		(struct parse *, FILE *);
211285612Sdelphijstatic	void	passwd		(struct parse *, FILE *);
212285612Sdelphijstatic	void	hostnames	(struct parse *, FILE *);
213285612Sdelphijstatic	void	setdebug	(struct parse *, FILE *);
214285612Sdelphijstatic	void	quit		(struct parse *, FILE *);
215298770Sdelphijstatic	void	showdrefid	(struct parse *, FILE *);
216285612Sdelphijstatic	void	version		(struct parse *, FILE *);
217285612Sdelphijstatic	void	raw		(struct parse *, FILE *);
218285612Sdelphijstatic	void	cooked		(struct parse *, FILE *);
219285612Sdelphijstatic	void	authenticate	(struct parse *, FILE *);
220285612Sdelphijstatic	void	ntpversion	(struct parse *, FILE *);
221285612Sdelphijstatic	void	warning		(const char *, ...)
222285612Sdelphij    __attribute__((__format__(__printf__, 1, 2)));
223285612Sdelphijstatic	void	error		(const char *, ...)
224285612Sdelphij    __attribute__((__format__(__printf__, 1, 2)));
225285612Sdelphijstatic	u_long	getkeyid	(const char *);
226285612Sdelphijstatic	void	atoascii	(const char *, size_t, char *, size_t);
227293650Sglebiusstatic	void	cookedprint	(int, size_t, const char *, int, int, FILE *);
228293650Sglebiusstatic	void	rawprint	(int, size_t, const char *, int, int, FILE *);
229285612Sdelphijstatic	void	startoutput	(void);
230285612Sdelphijstatic	void	output		(FILE *, const char *, const char *);
231285612Sdelphijstatic	void	endoutput	(FILE *);
232285612Sdelphijstatic	void	outputarr	(FILE *, char *, int, l_fp *);
233285612Sdelphijstatic	int	assoccmp	(const void *, const void *);
234293650Sglebiusstatic	void	on_ctrlc	(void);
235285612Sdelphij	u_short	varfmt		(const char *);
236294569Sdelphijstatic	int	my_easprintf	(char**, const char *, ...) NTP_PRINTF(2, 3);
237285612Sdelphijvoid	ntpq_custom_opt_handler	(tOptions *, tOptDesc *);
238285612Sdelphij
239330567Sgordon/* read a character from memory and expand to integer */
240330567Sgordonstatic inline int
241330567Sgordonpgetc(
242330567Sgordon	const char *cp
243330567Sgordon	)
244330567Sgordon{
245330567Sgordon	return (int)*(const unsigned char*)cp;
246330567Sgordon}
247330567Sgordon
248330567Sgordon
249285612Sdelphij#ifdef OPENSSL
250285612Sdelphij# ifdef HAVE_EVP_MD_DO_ALL_SORTED
251285612Sdelphijstatic void list_md_fn(const EVP_MD *m, const char *from,
252285612Sdelphij		       const char *to, void *arg );
253285612Sdelphij# endif
25454359Sroberto#endif
255330567Sgordonstatic char *insert_cmac(char *list);
256285612Sdelphijstatic char *list_digest_names(void);
25754359Sroberto
25854359Sroberto/*
25954359Sroberto * Built-in commands we understand
26054359Sroberto */
26154359Srobertostruct xcmd builtins[] = {
262182007Sroberto	{ "?",		help,		{  OPT|NTP_STR, NO, NO, NO },
26354359Sroberto	  { "command", "", "", "" },
26454359Sroberto	  "tell the use and syntax of commands" },
265182007Sroberto	{ "help",	help,		{  OPT|NTP_STR, NO, NO, NO },
26654359Sroberto	  { "command", "", "", "" },
26754359Sroberto	  "tell the use and syntax of commands" },
268182007Sroberto	{ "timeout",	timeout,	{ OPT|NTP_UINT, NO, NO, NO },
26954359Sroberto	  { "msec", "", "", "" },
27054359Sroberto	  "set the primary receive time out" },
271182007Sroberto	{ "delay",	auth_delay,	{ OPT|NTP_INT, NO, NO, NO },
27254359Sroberto	  { "msec", "", "", "" },
27354359Sroberto	  "set the delay added to encryption time stamps" },
274182007Sroberto	{ "host",	host,		{ OPT|NTP_STR, OPT|NTP_STR, NO, NO },
275132451Sroberto	  { "-4|-6", "hostname", "", "" },
27654359Sroberto	  "specify the host whose NTP server we talk to" },
277182007Sroberto	{ "poll",	ntp_poll,	{ OPT|NTP_UINT, OPT|NTP_STR, NO, NO },
27854359Sroberto	  { "n", "verbose", "", "" },
27954359Sroberto	  "poll an NTP server in client mode `n' times" },
280285612Sdelphij	{ "passwd",	passwd,		{ OPT|NTP_STR, NO, NO, NO },
28154359Sroberto	  { "", "", "", "" },
28254359Sroberto	  "specify a password to use for authenticated requests"},
283182007Sroberto	{ "hostnames",	hostnames,	{ OPT|NTP_STR, NO, NO, NO },
28454359Sroberto	  { "yes|no", "", "", "" },
28554359Sroberto	  "specify whether hostnames or net numbers are printed"},
286182007Sroberto	{ "debug",	setdebug,	{ OPT|NTP_STR, NO, NO, NO },
28754359Sroberto	  { "no|more|less", "", "", "" },
28854359Sroberto	  "set/change debugging level" },
28954359Sroberto	{ "quit",	quit,		{ NO, NO, NO, NO },
29054359Sroberto	  { "", "", "", "" },
29154359Sroberto	  "exit ntpq" },
29254359Sroberto	{ "exit",	quit,		{ NO, NO, NO, NO },
29354359Sroberto	  { "", "", "", "" },
29454359Sroberto	  "exit ntpq" },
295182007Sroberto	{ "keyid",	keyid,		{ OPT|NTP_UINT, NO, NO, NO },
29654359Sroberto	  { "key#", "", "", "" },
29754359Sroberto	  "set keyid to use for authenticated requests" },
298298770Sdelphij	{ "drefid",	showdrefid,	{ OPT|NTP_STR, NO, NO, NO },
299298770Sdelphij	  { "hash|ipv4", "", "", "" },
300298770Sdelphij	  "display refid's as IPv4 or hash" },
30154359Sroberto	{ "version",	version,	{ NO, NO, NO, NO },
30254359Sroberto	  { "", "", "", "" },
30354359Sroberto	  "print version number" },
30454359Sroberto	{ "raw",	raw,		{ NO, NO, NO, NO },
30554359Sroberto	  { "", "", "", "" },
30654359Sroberto	  "do raw mode variable output" },
30754359Sroberto	{ "cooked",	cooked,		{ NO, NO, NO, NO },
30854359Sroberto	  { "", "", "", "" },
30954359Sroberto	  "do cooked mode variable output" },
310182007Sroberto	{ "authenticate", authenticate,	{ OPT|NTP_STR, NO, NO, NO },
31154359Sroberto	  { "yes|no", "", "", "" },
31254359Sroberto	  "always authenticate requests to this server" },
313182007Sroberto	{ "ntpversion",	ntpversion,	{ OPT|NTP_UINT, NO, NO, NO },
31454359Sroberto	  { "version number", "", "", "" },
31554359Sroberto	  "set the NTP version number to use for requests" },
316182007Sroberto	{ "keytype",	keytype,	{ OPT|NTP_STR, NO, NO, NO },
317285612Sdelphij	  { "key type %s", "", "", "" },
318285612Sdelphij	  NULL },
31954359Sroberto	{ 0,		0,		{ NO, NO, NO, NO },
32054359Sroberto	  { "", "", "", "" }, "" }
32154359Sroberto};
32254359Sroberto
32354359Sroberto
32454359Sroberto/*
32554359Sroberto * Default values we use.
32654359Sroberto */
327285612Sdelphij#define	DEFHOST		"localhost"	/* default host name */
328285612Sdelphij#define	DEFTIMEOUT	5		/* wait 5 seconds for 1st pkt */
329285612Sdelphij#define	DEFSTIMEOUT	3		/* and 3 more for each additional */
330285612Sdelphij/*
331285612Sdelphij * Requests are automatically retried once, so total timeout with no
332285612Sdelphij * response is a bit over 2 * DEFTIMEOUT, or 10 seconds.  At the other
333285612Sdelphij * extreme, a request eliciting 32 packets of responses each for some
334285612Sdelphij * reason nearly DEFSTIMEOUT seconds after the prior in that series,
335285612Sdelphij * with a single packet dropped, would take around 32 * DEFSTIMEOUT, or
336285612Sdelphij * 93 seconds to fail each of two times, or 186 seconds.
337285612Sdelphij * Some commands involve a series of requests, such as "peers" and
338285612Sdelphij * "mrulist", so the cumulative timeouts are even longer for those.
339285612Sdelphij */
34054359Sroberto#define	DEFDELAY	0x51EB852	/* 20 milliseconds, l_fp fraction */
34154359Sroberto#define	LENHOSTNAME	256		/* host name is 256 characters long */
34254359Sroberto#define	MAXCMDS		100		/* maximum commands on cmd line */
34354359Sroberto#define	MAXHOSTS	200		/* maximum hosts on cmd line */
34454359Sroberto#define	MAXLINE		512		/* maximum line length */
34554359Sroberto#define	MAXTOKENS	(1+MAXARGS+2)	/* maximum number of usable tokens */
34654359Sroberto#define	MAXVARLEN	256		/* maximum length of a variable name */
347285612Sdelphij#define	MAXVALLEN	2048		/* maximum length of a variable value */
34854359Sroberto#define	MAXOUTLINE	72		/* maximum length of an output line */
349285612Sdelphij#define SCREENWIDTH	76		/* nominal screen width in columns */
35054359Sroberto
35154359Sroberto/*
35254359Sroberto * Some variables used and manipulated locally
35354359Sroberto */
354285612Sdelphijstruct sock_timeval tvout = { DEFTIMEOUT, 0 };	/* time out for reads */
355285612Sdelphijstruct sock_timeval tvsout = { DEFSTIMEOUT, 0 };/* secondary time out */
35654359Srobertol_fp delay_time;				/* delay time */
35754359Srobertochar currenthost[LENHOSTNAME];			/* current host name */
358285612Sdelphijint currenthostisnum;				/* is prior text from IP? */
359285612Sdelphijstruct sockaddr_in hostaddr;			/* host address */
36054359Srobertoint showhostnames = 1;				/* show host names by default */
361285612Sdelphijint wideremote = 0;				/* show wide remote names? */
36254359Sroberto
363132451Srobertoint ai_fam_templ;				/* address family */
364132451Srobertoint ai_fam_default;				/* default address family */
365132451SrobertoSOCKET sockfd;					/* fd socket is opened on */
36654359Srobertoint havehost = 0;				/* set to 1 when host open */
367132451Srobertoint s_port = 0;
36854359Srobertostruct servent *server_entry = NULL;		/* server entry for ntp */
36954359Sroberto
37054359Sroberto
37154359Sroberto/*
37254359Sroberto * Sequence number used for requests.  It is incremented before
37354359Sroberto * it is used.
37454359Sroberto */
37554359Srobertou_short sequence;
37654359Sroberto
37754359Sroberto/*
37854359Sroberto * Holds data returned from queries.  Declare buffer long to be sure of
37954359Sroberto * alignment.
38054359Sroberto */
38154359Sroberto#define	DATASIZE	(MAXFRAGS*480)	/* maximum amount of data */
38254359Srobertolong pktdata[DATASIZE/sizeof(long)];
38354359Sroberto
38454359Sroberto/*
385285612Sdelphij * assoc_cache[] is a dynamic array which allows references to
386285612Sdelphij * associations using &1 ... &N for n associations, avoiding manual
387285612Sdelphij * lookup of the current association IDs for a given ntpd.  It also
388285612Sdelphij * caches the status word for each association, retrieved incidentally.
38954359Sroberto */
390285612Sdelphijstruct association *	assoc_cache;
391285612Sdelphiju_int assoc_cache_slots;/* count of allocated array entries */
392285612Sdelphiju_int numassoc;		/* number of cached associations */
39354359Sroberto
39454359Sroberto/*
39554359Sroberto * For commands typed on the command line (with the -c option)
39654359Sroberto */
397316722Sdelphijsize_t numcmds = 0;
39854359Srobertoconst char *ccmds[MAXCMDS];
39954359Sroberto#define	ADDCMD(cp)	if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
40054359Sroberto
40154359Sroberto/*
40254359Sroberto * When multiple hosts are specified.
40354359Sroberto */
40454359Sroberto
405285612Sdelphiju_int numhosts;
40654359Sroberto
407285612Sdelphijchost chosts[MAXHOSTS];
408285612Sdelphij#define	ADDHOST(cp)						\
409285612Sdelphij	do {							\
410285612Sdelphij		if (numhosts < MAXHOSTS) {			\
411285612Sdelphij			chosts[numhosts].name = (cp);		\
412285612Sdelphij			chosts[numhosts].fam = ai_fam_templ;	\
413285612Sdelphij			numhosts++;				\
414285612Sdelphij		}						\
415285612Sdelphij	} while (0)
416285612Sdelphij
41754359Sroberto/*
41854359Sroberto * Macro definitions we use
41954359Sroberto */
42054359Sroberto#define	ISSPACE(c)	((c) == ' ' || (c) == '\t')
42154359Sroberto#define	ISEOL(c)	((c) == '\n' || (c) == '\r' || (c) == '\0')
42254359Sroberto#define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
42354359Sroberto
42454359Sroberto/*
42554359Sroberto * Jump buffer for longjumping back to the command level
42654359Sroberto */
42754359Srobertojmp_buf interrupt_buf;
42854359Sroberto
42954359Sroberto/*
43054359Sroberto * Points at file being currently printed into
43154359Sroberto */
43254359SrobertoFILE *current_output;
43354359Sroberto
43454359Sroberto/*
43554359Sroberto * Command table imported from ntpdc_ops.c
43654359Sroberto */
43754359Srobertoextern struct xcmd opcmds[];
43854359Sroberto
439289997Sglebiuschar const *progname;
44054359Sroberto
44154359Sroberto#ifdef NO_MAIN_ALLOWED
442285612Sdelphij#ifndef BUILD_AS_LIB
44354359SrobertoCALL(ntpq,"ntpq",ntpqmain);
44454359Sroberto
44554359Srobertovoid clear_globals(void)
44654359Sroberto{
447285612Sdelphij	extern int ntp_optind;
448285612Sdelphij	showhostnames = 0;	/* don'tshow host names by default */
449285612Sdelphij	ntp_optind = 0;
450285612Sdelphij	server_entry = NULL;	/* server entry for ntp */
451285612Sdelphij	havehost = 0;		/* set to 1 when host open */
452285612Sdelphij	numassoc = 0;		/* number of cached associations */
453285612Sdelphij	numcmds = 0;
454285612Sdelphij	numhosts = 0;
45554359Sroberto}
456285612Sdelphij#endif /* !BUILD_AS_LIB */
457285612Sdelphij#endif /* NO_MAIN_ALLOWED */
45854359Sroberto
45954359Sroberto/*
46054359Sroberto * main - parse arguments and handle options
46154359Sroberto */
46254359Sroberto#ifndef NO_MAIN_ALLOWED
46354359Srobertoint
46454359Srobertomain(
46554359Sroberto	int argc,
46654359Sroberto	char *argv[]
46754359Sroberto	)
46854359Sroberto{
46954359Sroberto	return ntpqmain(argc, argv);
47054359Sroberto}
47154359Sroberto#endif
47254359Sroberto
473330567Sgordon
474285612Sdelphij#ifndef BUILD_AS_LIB
47554359Srobertoint
47654359Srobertontpqmain(
47754359Sroberto	int argc,
47854359Sroberto	char *argv[]
47954359Sroberto	)
48054359Sroberto{
481285612Sdelphij	u_int ihost;
482316722Sdelphij	size_t icmd;
48354359Sroberto
484285612Sdelphij
485182007Sroberto#ifdef SYS_VXWORKS
486182007Sroberto	clear_globals();
487182007Sroberto	taskPrioritySet(taskIdSelf(), 100 );
48854359Sroberto#endif
489182007Sroberto
49054359Sroberto	delay_time.l_ui = 0;
49154359Sroberto	delay_time.l_uf = DEFDELAY;
49254359Sroberto
493285612Sdelphij	init_lib();	/* sets up ipv4_works, ipv6_works */
494285612Sdelphij	ssl_applink();
495285612Sdelphij	init_auth();
496285612Sdelphij
497285612Sdelphij	/* Check to see if we have IPv6. Otherwise default to IPv4 */
498285612Sdelphij	if (!ipv6_works)
499285612Sdelphij		ai_fam_default = AF_INET;
500285612Sdelphij
501285612Sdelphij	/* Fixup keytype's help based on available digest names */
502285612Sdelphij
503132451Sroberto	{
504285612Sdelphij	    char *list;
505294569Sdelphij	    char *msg;
506132451Sroberto
507285612Sdelphij	    list = list_digest_names();
508330567Sgordon
509330567Sgordon	    for (icmd = 0; icmd < sizeof(builtins)/sizeof(*builtins); icmd++) {
510330567Sgordon		if (strcmp("keytype", builtins[icmd].keyword) == 0) {
511285612Sdelphij		    break;
512330567Sgordon		}
513285612Sdelphij	    }
514285612Sdelphij
515285612Sdelphij	    /* CID: 1295478 */
516285612Sdelphij	    /* This should only "trip" if "keytype" is removed from builtins */
517330567Sgordon	    INSIST(icmd < sizeof(builtins)/sizeof(*builtins));
518285612Sdelphij
519285612Sdelphij#ifdef OPENSSL
520285612Sdelphij	    builtins[icmd].desc[0] = "digest-name";
521294569Sdelphij	    my_easprintf(&msg,
522294569Sdelphij			 "set key type to use for authenticated requests, one of:%s",
523294569Sdelphij			 list);
524285612Sdelphij#else
525285612Sdelphij	    builtins[icmd].desc[0] = "md5";
526294569Sdelphij	    my_easprintf(&msg,
527294569Sdelphij			 "set key type to use for authenticated requests (%s)",
528294569Sdelphij			 list);
529285612Sdelphij#endif
530285612Sdelphij	    builtins[icmd].comment = msg;
531285612Sdelphij	    free(list);
532132451Sroberto	}
533132451Sroberto
53454359Sroberto	progname = argv[0];
535182007Sroberto
536182007Sroberto	{
537285612Sdelphij		int optct = ntpOptionProcess(&ntpqOptions, argc, argv);
538182007Sroberto		argc -= optct;
539182007Sroberto		argv += optct;
540182007Sroberto	}
541182007Sroberto
542285612Sdelphij	/*
543285612Sdelphij	 * Process options other than -c and -p, which are specially
544285612Sdelphij	 * handled by ntpq_custom_opt_handler().
545285612Sdelphij	 */
546285612Sdelphij
547285612Sdelphij	debug = OPT_VALUE_SET_DEBUG_LEVEL;
548285612Sdelphij
549285612Sdelphij	if (HAVE_OPT(IPV4))
550182007Sroberto		ai_fam_templ = AF_INET;
551285612Sdelphij	else if (HAVE_OPT(IPV6))
552182007Sroberto		ai_fam_templ = AF_INET6;
553285612Sdelphij	else
554182007Sroberto		ai_fam_templ = ai_fam_default;
555182007Sroberto
556285612Sdelphij	if (HAVE_OPT(INTERACTIVE))
557182007Sroberto		interactive = 1;
558182007Sroberto
559285612Sdelphij	if (HAVE_OPT(NUMERIC))
560182007Sroberto		showhostnames = 0;
561182007Sroberto
562285612Sdelphij	if (HAVE_OPT(WIDE))
563285612Sdelphij		wideremote = 1;
564182007Sroberto
565285612Sdelphij	old_rv = HAVE_OPT(OLD_RV);
566285612Sdelphij
567298770Sdelphij	drefid = OPT_VALUE_REFID;
568298770Sdelphij
569285612Sdelphij	if (0 == argc) {
57054359Sroberto		ADDHOST(DEFHOST);
57154359Sroberto	} else {
572285612Sdelphij		for (ihost = 0; ihost < (u_int)argc; ihost++) {
573285612Sdelphij			if ('-' == *argv[ihost]) {
574285612Sdelphij				//
575285612Sdelphij				// If I really cared I'd also check:
576285612Sdelphij				// 0 == argv[ihost][2]
577285612Sdelphij				//
578285612Sdelphij				// and there are other cases as well...
579285612Sdelphij				//
580285612Sdelphij				if ('4' == argv[ihost][1]) {
581285612Sdelphij					ai_fam_templ = AF_INET;
582285612Sdelphij					continue;
583285612Sdelphij				} else if ('6' == argv[ihost][1]) {
584285612Sdelphij					ai_fam_templ = AF_INET6;
585285612Sdelphij					continue;
586285612Sdelphij				} else {
587285612Sdelphij					// XXX Throw a usage error
588285612Sdelphij				}
589285612Sdelphij			}
590285612Sdelphij			ADDHOST(argv[ihost]);
591285612Sdelphij		}
59254359Sroberto	}
59354359Sroberto
59454359Sroberto	if (numcmds == 0 && interactive == 0
59554359Sroberto	    && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
59654359Sroberto		interactive = 1;
59754359Sroberto	}
59854359Sroberto
599293650Sglebius	set_ctrl_c_hook(on_ctrlc);
60054359Sroberto#ifndef SYS_WINNT /* Under NT cannot handle SIGINT, WIN32 spawns a handler */
60154359Sroberto	if (interactive)
602293650Sglebius		push_ctrl_c_handler(abortcmd);
60354359Sroberto#endif /* SYS_WINNT */
60454359Sroberto
60554359Sroberto	if (numcmds == 0) {
606285612Sdelphij		(void) openhost(chosts[0].name, chosts[0].fam);
60754359Sroberto		getcmds();
60854359Sroberto	} else {
60954359Sroberto		for (ihost = 0; ihost < numhosts; ihost++) {
610330567Sgordon			if (openhost(chosts[ihost].name, chosts[ihost].fam)) {
611330567Sgordon				if (ihost)
612330567Sgordon					fputc('\n', current_output);
613330567Sgordon				for (icmd = 0; icmd < numcmds; icmd++) {
614330567Sgordon					if (icmd)
615330567Sgordon						fputc('\n', current_output);
616285612Sdelphij					docmd(ccmds[icmd]);
617330567Sgordon				}
618330567Sgordon			}
61954359Sroberto		}
62054359Sroberto	}
62154359Sroberto#ifdef SYS_WINNT
62254359Sroberto	WSACleanup();
62354359Sroberto#endif /* SYS_WINNT */
62454359Sroberto	return 0;
62554359Sroberto}
626285612Sdelphij#endif /* !BUILD_AS_LIB */
62754359Sroberto
62854359Sroberto/*
62954359Sroberto * openhost - open a socket to a host
63054359Sroberto */
631285612Sdelphijstatic	int
63254359Srobertoopenhost(
633285612Sdelphij	const char *hname,
634285612Sdelphij	int	    fam
63554359Sroberto	)
63654359Sroberto{
637285612Sdelphij	const char svc[] = "ntp";
63854359Sroberto	char temphost[LENHOSTNAME];
639132451Sroberto	int a_info, i;
640285612Sdelphij	struct addrinfo hints, *ai;
641285612Sdelphij	sockaddr_u addr;
642285612Sdelphij	size_t octets;
643132451Sroberto	register const char *cp;
644132451Sroberto	char name[LENHOSTNAME];
64554359Sroberto
646132451Sroberto	/*
647132451Sroberto	 * We need to get by the [] if they were entered
648132451Sroberto	 */
649285612Sdelphij
650132451Sroberto	cp = hname;
651285612Sdelphij
652285612Sdelphij	if (*cp == '[') {
653132451Sroberto		cp++;
654285612Sdelphij		for (i = 0; *cp && *cp != ']'; cp++, i++)
655132451Sroberto			name[i] = *cp;
656285612Sdelphij		if (*cp == ']') {
657285612Sdelphij			name[i] = '\0';
658285612Sdelphij			hname = name;
659285612Sdelphij		} else {
660285612Sdelphij			return 0;
661285612Sdelphij		}
66254359Sroberto	}
66354359Sroberto
664132451Sroberto	/*
665132451Sroberto	 * First try to resolve it as an ip address and if that fails,
666132451Sroberto	 * do a fullblown (dns) lookup. That way we only use the dns
667132451Sroberto	 * when it is needed and work around some implementations that
668132451Sroberto	 * will return an "IPv4-mapped IPv6 address" address if you
669132451Sroberto	 * give it an IPv4 address to lookup.
670132451Sroberto	 */
671285612Sdelphij	ZERO(hints);
672285612Sdelphij	hints.ai_family = fam;
673132451Sroberto	hints.ai_protocol = IPPROTO_UDP;
674132451Sroberto	hints.ai_socktype = SOCK_DGRAM;
675285612Sdelphij	hints.ai_flags = Z_AI_NUMERICHOST;
676285612Sdelphij	ai = NULL;
677132451Sroberto
678285612Sdelphij	a_info = getaddrinfo(hname, svc, &hints, &ai);
679182007Sroberto	if (a_info == EAI_NONAME
680182007Sroberto#ifdef EAI_NODATA
681182007Sroberto	    || a_info == EAI_NODATA
682182007Sroberto#endif
683182007Sroberto	   ) {
684132451Sroberto		hints.ai_flags = AI_CANONNAME;
685132451Sroberto#ifdef AI_ADDRCONFIG
686132451Sroberto		hints.ai_flags |= AI_ADDRCONFIG;
687132451Sroberto#endif
688285612Sdelphij		a_info = getaddrinfo(hname, svc, &hints, &ai);
689132451Sroberto	}
690285612Sdelphij#ifdef AI_ADDRCONFIG
691132451Sroberto	/* Some older implementations don't like AI_ADDRCONFIG. */
692132451Sroberto	if (a_info == EAI_BADFLAGS) {
693285612Sdelphij		hints.ai_flags &= ~AI_ADDRCONFIG;
694285612Sdelphij		a_info = getaddrinfo(hname, svc, &hints, &ai);
695132451Sroberto	}
696285612Sdelphij#endif
697132451Sroberto	if (a_info != 0) {
698285612Sdelphij		fprintf(stderr, "%s\n", gai_strerror(a_info));
699132451Sroberto		return 0;
700132451Sroberto	}
701132451Sroberto
702285612Sdelphij	INSIST(ai != NULL);
703285612Sdelphij	ZERO(addr);
704285612Sdelphij	octets = min(sizeof(addr), ai->ai_addrlen);
705285612Sdelphij	memcpy(&addr, ai->ai_addr, octets);
706285612Sdelphij
707132451Sroberto	if (ai->ai_canonname == NULL) {
708285612Sdelphij		strlcpy(temphost, stoa(&addr), sizeof(temphost));
709285612Sdelphij		currenthostisnum = TRUE;
710132451Sroberto	} else {
711285612Sdelphij		strlcpy(temphost, ai->ai_canonname, sizeof(temphost));
712285612Sdelphij		currenthostisnum = FALSE;
713132451Sroberto	}
714132451Sroberto
71554359Sroberto	if (debug > 2)
716285612Sdelphij		printf("Opening host %s (%s)\n",
717285612Sdelphij			temphost,
718285612Sdelphij			(ai->ai_family == AF_INET)
719285612Sdelphij			? "AF_INET"
720285612Sdelphij			: (ai->ai_family == AF_INET6)
721285612Sdelphij			  ? "AF_INET6"
722285612Sdelphij			  : "AF-???"
723285612Sdelphij			);
72454359Sroberto
72554359Sroberto	if (havehost == 1) {
72654359Sroberto		if (debug > 2)
727285612Sdelphij			printf("Closing old host %s\n", currenthost);
728285612Sdelphij		closesocket(sockfd);
72954359Sroberto		havehost = 0;
73054359Sroberto	}
731285612Sdelphij	strlcpy(currenthost, temphost, sizeof(currenthost));
73254359Sroberto
733132451Sroberto	/* port maps to the same location in both families */
734285612Sdelphij	s_port = NSRCPORT(&addr);
735132451Sroberto#ifdef SYS_VXWORKS
736132451Sroberto	((struct sockaddr_in6 *)&hostaddr)->sin6_port = htons(SERVER_PORT_NUM);
737132451Sroberto	if (ai->ai_family == AF_INET)
738132451Sroberto		*(struct sockaddr_in *)&hostaddr=
739132451Sroberto			*((struct sockaddr_in *)ai->ai_addr);
740132451Sroberto	else
741132451Sroberto		*(struct sockaddr_in6 *)&hostaddr=
742132451Sroberto			*((struct sockaddr_in6 *)ai->ai_addr);
743132451Sroberto#endif /* SYS_VXWORKS */
74454359Sroberto
74554359Sroberto#ifdef SYS_WINNT
74654359Sroberto	{
74754359Sroberto		int optionValue = SO_SYNCHRONOUS_NONALERT;
74854359Sroberto		int err;
749182007Sroberto
750285612Sdelphij		err = setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,
751330567Sgordon				 (void *)&optionValue, sizeof(optionValue));
752285612Sdelphij		if (err) {
753285612Sdelphij			mfprintf(stderr,
754285612Sdelphij				 "setsockopt(SO_SYNCHRONOUS_NONALERT)"
755285612Sdelphij				 " error: %m\n");
756285612Sdelphij			freeaddrinfo(ai);
75754359Sroberto			exit(1);
75854359Sroberto		}
75954359Sroberto	}
760132451Sroberto#endif /* SYS_WINNT */
76154359Sroberto
762285612Sdelphij	sockfd = socket(ai->ai_family, ai->ai_socktype,
763285612Sdelphij			ai->ai_protocol);
76454359Sroberto	if (sockfd == INVALID_SOCKET) {
765285612Sdelphij		error("socket");
766285612Sdelphij		freeaddrinfo(ai);
767285612Sdelphij		return 0;
76854359Sroberto	}
76954359Sroberto
770285612Sdelphij
77154359Sroberto#ifdef NEED_RCVBUF_SLOP
77254359Sroberto# ifdef SO_RCVBUF
77354359Sroberto	{ int rbufsize = DATASIZE + 2048;	/* 2K for slop */
77454359Sroberto	if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
775330567Sgordon		       (void *)&rbufsize, sizeof(int)) == -1)
776285612Sdelphij		error("setsockopt");
77754359Sroberto	}
77854359Sroberto# endif
77954359Sroberto#endif
78054359Sroberto
781285612Sdelphij	if
782132451Sroberto#ifdef SYS_VXWORKS
783285612Sdelphij	   (connect(sockfd, (struct sockaddr *)&hostaddr,
78454359Sroberto		    sizeof(hostaddr)) == -1)
785132451Sroberto#else
786285612Sdelphij	   (connect(sockfd, (struct sockaddr *)ai->ai_addr,
787293650Sglebius		ai->ai_addrlen) == -1)
788132451Sroberto#endif /* SYS_VXWORKS */
789293650Sglebius	{
790285612Sdelphij		error("connect");
791132451Sroberto		freeaddrinfo(ai);
792285612Sdelphij		return 0;
793285612Sdelphij	}
794285612Sdelphij	freeaddrinfo(ai);
79554359Sroberto	havehost = 1;
796285612Sdelphij	numassoc = 0;
797285612Sdelphij
79854359Sroberto	return 1;
79954359Sroberto}
80054359Sroberto
80154359Sroberto
802285612Sdelphijstatic void
803285612Sdelphijdump_hex_printable(
804285612Sdelphij	const void *	data,
805285612Sdelphij	size_t		len
806285612Sdelphij	)
807285612Sdelphij{
808316722Sdelphij	/* every line shows at most 16 bytes, so we need a buffer of
809316722Sdelphij	 *   4 * 16 (2 xdigits, 1 char, one sep for xdigits)
810316722Sdelphij	 * + 2 * 1  (block separators)
811316722Sdelphij	 * + <LF> + <NUL>
812316722Sdelphij	 *---------------
813316722Sdelphij	 *  68 bytes
814316722Sdelphij	 */
815316722Sdelphij	static const char s_xdig[16] = "0123456789ABCDEF";
816285612Sdelphij
817316722Sdelphij	char lbuf[68];
818316722Sdelphij	int  ch, rowlen;
819316722Sdelphij	const u_char * cdata = data;
820316722Sdelphij	char *xptr, *pptr;
821316722Sdelphij
822316722Sdelphij	while (len) {
823316722Sdelphij		memset(lbuf, ' ', sizeof(lbuf));
824316722Sdelphij		xptr = lbuf;
825316722Sdelphij		pptr = lbuf + 3*16 + 2;
826316722Sdelphij
827316722Sdelphij		rowlen = (len > 16) ? 16 : (int)len;
828285612Sdelphij		len -= rowlen;
829316722Sdelphij
830316722Sdelphij		do {
831316722Sdelphij			ch = *cdata++;
832316722Sdelphij
833316722Sdelphij			*xptr++ = s_xdig[ch >> 4  ];
834316722Sdelphij			*xptr++ = s_xdig[ch & 0x0F];
835316722Sdelphij			if (++xptr == lbuf + 3*8)
836316722Sdelphij				++xptr;
837316722Sdelphij
838316722Sdelphij			*pptr++ = isprint(ch) ? (char)ch : '.';
839316722Sdelphij		} while (--rowlen);
840316722Sdelphij
841316722Sdelphij		*pptr++ = '\n';
842316722Sdelphij		*pptr   = '\0';
843316722Sdelphij		fputs(lbuf, stdout);
844285612Sdelphij	}
845285612Sdelphij}
846285612Sdelphij
847285612Sdelphij
84854359Sroberto/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
84954359Sroberto/*
85054359Sroberto * sendpkt - send a packet to the remote host
85154359Sroberto */
85254359Srobertostatic int
85354359Srobertosendpkt(
854285612Sdelphij	void *	xdata,
855285612Sdelphij	size_t	xdatalen
85654359Sroberto	)
85754359Sroberto{
85854359Sroberto	if (debug >= 3)
859285612Sdelphij		printf("Sending %zu octets\n", xdatalen);
86054359Sroberto
861293650Sglebius	if (send(sockfd, xdata, xdatalen, 0) == -1) {
862285612Sdelphij		warning("write to %s failed", currenthost);
86354359Sroberto		return -1;
86454359Sroberto	}
86554359Sroberto
86654359Sroberto	if (debug >= 4) {
867285612Sdelphij		printf("Request packet:\n");
868285612Sdelphij		dump_hex_printable(xdata, xdatalen);
86954359Sroberto	}
87054359Sroberto	return 0;
87154359Sroberto}
87254359Sroberto
87354359Sroberto/*
87454359Sroberto * getresponse - get a (series of) response packet(s) and return the data
87554359Sroberto */
87654359Srobertostatic int
87754359Srobertogetresponse(
87854359Sroberto	int opcode,
87954359Sroberto	int associd,
88054359Sroberto	u_short *rstatus,
881293650Sglebius	size_t *rsize,
882285612Sdelphij	const char **rdata,
88354359Sroberto	int timeo
88454359Sroberto	)
88554359Sroberto{
88654359Sroberto	struct ntp_control rpkt;
887285612Sdelphij	struct sock_timeval tvo;
88854359Sroberto	u_short offsets[MAXFRAGS+1];
88954359Sroberto	u_short counts[MAXFRAGS+1];
89054359Sroberto	u_short offset;
89154359Sroberto	u_short count;
892285612Sdelphij	size_t numfrags;
893285612Sdelphij	size_t f;
894285612Sdelphij	size_t ff;
89554359Sroberto	int seenlastfrag;
896285612Sdelphij	int shouldbesize;
89754359Sroberto	fd_set fds;
89854359Sroberto	int n;
899285612Sdelphij	int errcode;
900294569Sdelphij	/* absolute timeout checks. Not 'time_t' by intention! */
901294569Sdelphij	uint32_t tobase;	/* base value for timeout */
902294569Sdelphij	uint32_t tospan;	/* timeout span (max delay) */
903294569Sdelphij	uint32_t todiff;	/* current delay */
90454359Sroberto
905316722Sdelphij	memset(offsets, 0, sizeof(offsets));
906316722Sdelphij	memset(counts , 0, sizeof(counts ));
907316722Sdelphij
90854359Sroberto	/*
90954359Sroberto	 * This is pretty tricky.  We may get between 1 and MAXFRAG packets
91054359Sroberto	 * back in response to the request.  We peel the data out of
91154359Sroberto	 * each packet and collect it in one long block.  When the last
91254359Sroberto	 * packet in the sequence is received we'll know how much data we
91354359Sroberto	 * should have had.  Note we use one long time out, should reconsider.
91454359Sroberto	 */
91554359Sroberto	*rsize = 0;
91654359Sroberto	if (rstatus)
917285612Sdelphij		*rstatus = 0;
91854359Sroberto	*rdata = (char *)pktdata;
91954359Sroberto
92054359Sroberto	numfrags = 0;
92154359Sroberto	seenlastfrag = 0;
92254359Sroberto
923294569Sdelphij	tobase = (uint32_t)time(NULL);
924294569Sdelphij
92554359Sroberto	FD_ZERO(&fds);
92654359Sroberto
927285612Sdelphij	/*
928285612Sdelphij	 * Loop until we have an error or a complete response.  Nearly all
929285612Sdelphij	 * code paths to loop again use continue.
930285612Sdelphij	 */
931285612Sdelphij	for (;;) {
93254359Sroberto
933285612Sdelphij		if (numfrags == 0)
934285612Sdelphij			tvo = tvout;
935285612Sdelphij		else
936285612Sdelphij			tvo = tvsout;
937294569Sdelphij		tospan = (uint32_t)tvo.tv_sec + (tvo.tv_usec != 0);
93854359Sroberto
939285612Sdelphij		FD_SET(sockfd, &fds);
940293650Sglebius		n = select(sockfd+1, &fds, NULL, NULL, &tvo);
941285612Sdelphij		if (n == -1) {
942294569Sdelphij#if !defined(SYS_WINNT) && defined(EINTR)
943294569Sdelphij			/* Windows does not know about EINTR (until very
944294569Sdelphij			 * recently) and the handling of console events
945294569Sdelphij			 * is *very* different from POSIX/UNIX signal
946294569Sdelphij			 * handling anyway.
947294569Sdelphij			 *
948294569Sdelphij			 * Under non-windows targets we map EINTR as
949294569Sdelphij			 * 'last packet was received' and try to exit
950294569Sdelphij			 * the receive sequence.
951294569Sdelphij			 */
952294569Sdelphij			if (errno == EINTR) {
953294569Sdelphij				seenlastfrag = 1;
954294569Sdelphij				goto maybe_final;
955294569Sdelphij			}
956294569Sdelphij#endif
957285612Sdelphij			warning("select fails");
958285612Sdelphij			return -1;
959285612Sdelphij		}
960294569Sdelphij
961294569Sdelphij		/*
962294569Sdelphij		 * Check if this is already too late. Trash the data and
963294569Sdelphij		 * fake a timeout if this is so.
964294569Sdelphij		 */
965294569Sdelphij		todiff = (((uint32_t)time(NULL)) - tobase) & 0x7FFFFFFFu;
966294569Sdelphij		if ((n > 0) && (todiff > tospan)) {
967294569Sdelphij			n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
968316722Sdelphij			n -= n; /* faked timeout return from 'select()',
969316722Sdelphij				 * execute RMW cycle on 'n'
970316722Sdelphij				 */
971294569Sdelphij		}
972294569Sdelphij
973316722Sdelphij		if (n <= 0) {
974285612Sdelphij			/*
975285612Sdelphij			 * Timed out.  Return what we have
976285612Sdelphij			 */
977285612Sdelphij			if (numfrags == 0) {
978285612Sdelphij				if (timeo)
979285612Sdelphij					fprintf(stderr,
980285612Sdelphij						"%s: timed out, nothing received\n",
981285612Sdelphij						currenthost);
982285612Sdelphij				return ERR_TIMEOUT;
983285612Sdelphij			}
98454359Sroberto			if (timeo)
985285612Sdelphij				fprintf(stderr,
986285612Sdelphij					"%s: timed out with incomplete data\n",
987285612Sdelphij					currenthost);
98854359Sroberto			if (debug) {
989285612Sdelphij				fprintf(stderr,
990285612Sdelphij					"ERR_INCOMPLETE: Received fragments:\n");
991285612Sdelphij				for (f = 0; f < numfrags; f++)
992285612Sdelphij					fprintf(stderr,
993285612Sdelphij						"%2u: %5d %5d\t%3d octets\n",
994285612Sdelphij						(u_int)f, offsets[f],
995285612Sdelphij						offsets[f] +
996285612Sdelphij						counts[f],
997285612Sdelphij						counts[f]);
998285612Sdelphij				fprintf(stderr,
999285612Sdelphij					"last fragment %sreceived\n",
1000285612Sdelphij					(seenlastfrag)
1001285612Sdelphij					    ? ""
1002285612Sdelphij					    : "not ");
100354359Sroberto			}
100454359Sroberto			return ERR_INCOMPLETE;
100554359Sroberto		}
100654359Sroberto
1007285612Sdelphij		n = recv(sockfd, (char *)&rpkt, sizeof(rpkt), 0);
1008316722Sdelphij		if (n < 0) {
1009285612Sdelphij			warning("read");
1010285612Sdelphij			return -1;
1011285612Sdelphij		}
101254359Sroberto
1013285612Sdelphij		if (debug >= 4) {
1014285612Sdelphij			printf("Response packet:\n");
1015285612Sdelphij			dump_hex_printable(&rpkt, n);
1016285612Sdelphij		}
101754359Sroberto
1018285612Sdelphij		/*
1019285612Sdelphij		 * Check for format errors.  Bug proofing.
1020285612Sdelphij		 */
1021285612Sdelphij		if (n < (int)CTL_HEADER_LEN) {
1022285612Sdelphij			if (debug)
1023285612Sdelphij				printf("Short (%d byte) packet received\n", n);
1024285612Sdelphij			continue;
102554359Sroberto		}
1026285612Sdelphij		if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
1027285612Sdelphij		    || PKT_VERSION(rpkt.li_vn_mode) < NTP_OLDVERSION) {
1028285612Sdelphij			if (debug)
1029285612Sdelphij				printf("Packet received with version %d\n",
1030285612Sdelphij				       PKT_VERSION(rpkt.li_vn_mode));
1031285612Sdelphij			continue;
1032285612Sdelphij		}
1033285612Sdelphij		if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
1034285612Sdelphij			if (debug)
1035285612Sdelphij				printf("Packet received with mode %d\n",
1036285612Sdelphij				       PKT_MODE(rpkt.li_vn_mode));
1037285612Sdelphij			continue;
1038285612Sdelphij		}
1039285612Sdelphij		if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
1040285612Sdelphij			if (debug)
1041285612Sdelphij				printf("Received request packet, wanted response\n");
1042285612Sdelphij			continue;
1043285612Sdelphij		}
104454359Sroberto
1045285612Sdelphij		/*
1046285612Sdelphij		 * Check opcode and sequence number for a match.
1047285612Sdelphij		 * Could be old data getting to us.
1048285612Sdelphij		 */
1049285612Sdelphij		if (ntohs(rpkt.sequence) != sequence) {
1050285612Sdelphij			if (debug)
1051285612Sdelphij				printf("Received sequnce number %d, wanted %d\n",
1052285612Sdelphij				       ntohs(rpkt.sequence), sequence);
1053285612Sdelphij			continue;
1054285612Sdelphij		}
1055285612Sdelphij		if (CTL_OP(rpkt.r_m_e_op) != opcode) {
1056285612Sdelphij			if (debug)
1057285612Sdelphij			    printf(
1058285612Sdelphij				    "Received opcode %d, wanted %d (sequence number okay)\n",
1059285612Sdelphij				    CTL_OP(rpkt.r_m_e_op), opcode);
1060285612Sdelphij			continue;
1061285612Sdelphij		}
106254359Sroberto
1063285612Sdelphij		/*
1064285612Sdelphij		 * Check the error code.  If non-zero, return it.
1065285612Sdelphij		 */
1066285612Sdelphij		if (CTL_ISERROR(rpkt.r_m_e_op)) {
1067285612Sdelphij			errcode = (ntohs(rpkt.status) >> 8) & 0xff;
1068285612Sdelphij			if (CTL_ISMORE(rpkt.r_m_e_op))
1069285612Sdelphij				TRACE(1, ("Error code %d received on not-final packet\n",
1070285612Sdelphij					  errcode));
1071285612Sdelphij			if (errcode == CERR_UNSPEC)
1072285612Sdelphij				return ERR_UNSPEC;
1073285612Sdelphij			return errcode;
107454359Sroberto		}
107554359Sroberto
107654359Sroberto		/*
1077285612Sdelphij		 * Check the association ID to make sure it matches what
1078285612Sdelphij		 * we sent.
107954359Sroberto		 */
1080285612Sdelphij		if (ntohs(rpkt.associd) != associd) {
1081285612Sdelphij			TRACE(1, ("Association ID %d doesn't match expected %d\n",
1082285612Sdelphij				  ntohs(rpkt.associd), associd));
1083285612Sdelphij			/*
1084285612Sdelphij			 * Hack for silly fuzzballs which, at the time of writing,
1085285612Sdelphij			 * return an assID of sys.peer when queried for system variables.
1086285612Sdelphij			 */
108754359Sroberto#ifdef notdef
1088285612Sdelphij			continue;
108954359Sroberto#endif
1090285612Sdelphij		}
109154359Sroberto
1092285612Sdelphij		/*
1093285612Sdelphij		 * Collect offset and count.  Make sure they make sense.
1094285612Sdelphij		 */
1095285612Sdelphij		offset = ntohs(rpkt.offset);
1096285612Sdelphij		count = ntohs(rpkt.count);
109754359Sroberto
109854359Sroberto		/*
1099285612Sdelphij		 * validate received payload size is padded to next 32-bit
1100285612Sdelphij		 * boundary and no smaller than claimed by rpkt.count
110154359Sroberto		 */
1102285612Sdelphij		if (n & 0x3) {
1103285612Sdelphij			TRACE(1, ("Response packet not padded, size = %d\n",
1104285612Sdelphij				  n));
1105285612Sdelphij			continue;
1106285612Sdelphij		}
110754359Sroberto
1108285612Sdelphij		shouldbesize = (CTL_HEADER_LEN + count + 3) & ~3;
110954359Sroberto
1110285612Sdelphij		if (n < shouldbesize) {
1111285612Sdelphij			printf("Response packet claims %u octets payload, above %ld received\n",
1112301301Sdelphij			       count, (long)(n - CTL_HEADER_LEN));
1113285612Sdelphij			return ERR_INCOMPLETE;
1114285612Sdelphij		}
1115285612Sdelphij
1116285612Sdelphij		if (debug >= 3 && shouldbesize > n) {
1117285612Sdelphij			u_int32 key;
1118285612Sdelphij			u_int32 *lpkt;
1119285612Sdelphij			int maclen;
1120285612Sdelphij
1121285612Sdelphij			/*
1122285612Sdelphij			 * Usually we ignore authentication, but for debugging purposes
1123285612Sdelphij			 * we watch it here.
1124285612Sdelphij			 */
1125285612Sdelphij			/* round to 8 octet boundary */
1126285612Sdelphij			shouldbesize = (shouldbesize + 7) & ~7;
1127285612Sdelphij
1128285612Sdelphij			maclen = n - shouldbesize;
1129285612Sdelphij			if (maclen >= (int)MIN_MAC_LEN) {
1130285612Sdelphij				printf(
1131285612Sdelphij					"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
1132285612Sdelphij					n, shouldbesize, maclen);
1133285612Sdelphij				lpkt = (u_int32 *)&rpkt;
1134285612Sdelphij				printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
1135285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 3]),
1136285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 2]),
1137285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) - 1]),
1138285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32)]),
1139285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 1]),
1140285612Sdelphij				       (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_int32) + 2]));
1141285612Sdelphij				key = ntohl(lpkt[(n - maclen) / sizeof(u_int32)]);
1142285612Sdelphij				printf("Authenticated with keyid %lu\n", (u_long)key);
1143285612Sdelphij				if (key != 0 && key != info_auth_keyid) {
1144285612Sdelphij					printf("We don't know that key\n");
114554359Sroberto				} else {
1146285612Sdelphij					if (authdecrypt(key, (u_int32 *)&rpkt,
1147285612Sdelphij					    n - maclen, maclen)) {
1148285612Sdelphij						printf("Auth okay!\n");
1149285612Sdelphij					} else {
1150285612Sdelphij						printf("Auth failed!\n");
1151285612Sdelphij					}
115254359Sroberto				}
115354359Sroberto			}
115454359Sroberto		}
115554359Sroberto
1156285612Sdelphij		TRACE(2, ("Got packet, size = %d\n", n));
1157285612Sdelphij		if (count > (n - CTL_HEADER_LEN)) {
1158285612Sdelphij			TRACE(1, ("Received count of %u octets, data in packet is %ld\n",
1159285612Sdelphij				  count, (long)n - CTL_HEADER_LEN));
1160285612Sdelphij			continue;
1161285612Sdelphij		}
1162285612Sdelphij		if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
1163285612Sdelphij			TRACE(1, ("Received count of 0 in non-final fragment\n"));
1164285612Sdelphij			continue;
1165285612Sdelphij		}
1166285612Sdelphij		if (offset + count > sizeof(pktdata)) {
1167285612Sdelphij			TRACE(1, ("Offset %u, count %u, too big for buffer\n",
1168285612Sdelphij				  offset, count));
1169285612Sdelphij			return ERR_TOOMUCH;
1170285612Sdelphij		}
1171285612Sdelphij		if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
1172285612Sdelphij			TRACE(1, ("Received second last fragment packet\n"));
1173285612Sdelphij			continue;
1174285612Sdelphij		}
117554359Sroberto
1176285612Sdelphij		/*
1177285612Sdelphij		 * So far, so good.  Record this fragment, making sure it doesn't
1178285612Sdelphij		 * overlap anything.
1179285612Sdelphij		 */
1180285612Sdelphij		TRACE(2, ("Packet okay\n"));
118154359Sroberto
1182285612Sdelphij		if (numfrags > (MAXFRAGS - 1)) {
1183285612Sdelphij			TRACE(2, ("Number of fragments exceeds maximum %d\n",
1184285612Sdelphij				  MAXFRAGS - 1));
1185285612Sdelphij			return ERR_TOOMUCH;
118654359Sroberto		}
118754359Sroberto
1188285612Sdelphij		/*
1189285612Sdelphij		 * Find the position for the fragment relative to any
1190285612Sdelphij		 * previously received.
1191285612Sdelphij		 */
1192285612Sdelphij		for (f = 0;
1193285612Sdelphij		     f < numfrags && offsets[f] < offset;
1194285612Sdelphij		     f++) {
1195285612Sdelphij			/* empty body */ ;
1196285612Sdelphij		}
119754359Sroberto
1198285612Sdelphij		if (f < numfrags && offset == offsets[f]) {
1199285612Sdelphij			TRACE(1, ("duplicate %u octets at %u ignored, prior %u at %u\n",
1200285612Sdelphij				  count, offset, counts[f], offsets[f]));
1201285612Sdelphij			continue;
1202285612Sdelphij		}
120354359Sroberto
1204285612Sdelphij		if (f > 0 && (offsets[f-1] + counts[f-1]) > offset) {
1205285612Sdelphij			TRACE(1, ("received frag at %u overlaps with %u octet frag at %u\n",
1206285612Sdelphij				  offset, counts[f-1], offsets[f-1]));
1207285612Sdelphij			continue;
120854359Sroberto		}
1209285612Sdelphij
1210285612Sdelphij		if (f < numfrags && (offset + count) > offsets[f]) {
1211285612Sdelphij			TRACE(1, ("received %u octet frag at %u overlaps with frag at %u\n",
1212285612Sdelphij				  count, offset, offsets[f]));
1213285612Sdelphij			continue;
121454359Sroberto		}
121554359Sroberto
1216285612Sdelphij		for (ff = numfrags; ff > f; ff--) {
1217285612Sdelphij			offsets[ff] = offsets[ff-1];
1218285612Sdelphij			counts[ff] = counts[ff-1];
1219285612Sdelphij		}
1220285612Sdelphij		offsets[f] = offset;
1221285612Sdelphij		counts[f] = count;
1222285612Sdelphij		numfrags++;
122354359Sroberto
1224285612Sdelphij		/*
1225285612Sdelphij		 * Got that stuffed in right.  Figure out if this was the last.
1226285612Sdelphij		 * Record status info out of the last packet.
1227285612Sdelphij		 */
1228285612Sdelphij		if (!CTL_ISMORE(rpkt.r_m_e_op)) {
1229285612Sdelphij			seenlastfrag = 1;
1230285612Sdelphij			if (rstatus != 0)
1231285612Sdelphij				*rstatus = ntohs(rpkt.status);
1232285612Sdelphij		}
123354359Sroberto
1234285612Sdelphij		/*
1235294569Sdelphij		 * Copy the data into the data buffer, and bump the
1236294569Sdelphij		 * timout base in case we need more.
1237285612Sdelphij		 */
1238285612Sdelphij		memcpy((char *)pktdata + offset, &rpkt.u, count);
1239294569Sdelphij		tobase = (uint32_t)time(NULL);
1240294569Sdelphij
1241285612Sdelphij		/*
1242285612Sdelphij		 * If we've seen the last fragment, look for holes in the sequence.
1243285612Sdelphij		 * If there aren't any, we're done.
1244285612Sdelphij		 */
1245301301Sdelphij#if !defined(SYS_WINNT) && defined(EINTR)
1246301301Sdelphij		maybe_final:
1247301301Sdelphij#endif
1248301301Sdelphij
1249285612Sdelphij		if (seenlastfrag && offsets[0] == 0) {
1250285612Sdelphij			for (f = 1; f < numfrags; f++)
1251285612Sdelphij				if (offsets[f-1] + counts[f-1] !=
1252285612Sdelphij				    offsets[f])
1253285612Sdelphij					break;
1254285612Sdelphij			if (f == numfrags) {
1255285612Sdelphij				*rsize = offsets[f-1] + counts[f-1];
1256285612Sdelphij				TRACE(1, ("%lu packets reassembled into response\n",
1257285612Sdelphij					  (u_long)numfrags));
1258285612Sdelphij				return 0;
1259285612Sdelphij			}
1260285612Sdelphij		}
1261285612Sdelphij	}  /* giant for (;;) collecting response packets */
1262285612Sdelphij}  /* getresponse() */
1263285612Sdelphij
1264285612Sdelphij
126554359Sroberto/*
126654359Sroberto * sendrequest - format and send a request packet
126754359Sroberto */
126854359Srobertostatic int
126954359Srobertosendrequest(
127054359Sroberto	int opcode,
1271285612Sdelphij	associd_t associd,
127254359Sroberto	int auth,
1273293650Sglebius	size_t qsize,
1274285612Sdelphij	const char *qdata
127554359Sroberto	)
127654359Sroberto{
127754359Sroberto	struct ntp_control qpkt;
1278293650Sglebius	size_t	pktsize;
1279285612Sdelphij	u_long	key_id;
1280285612Sdelphij	char *	pass;
1281293650Sglebius	size_t	maclen;
128254359Sroberto
128354359Sroberto	/*
128454359Sroberto	 * Check to make sure the data will fit in one packet
128554359Sroberto	 */
128654359Sroberto	if (qsize > CTL_MAX_DATA_LEN) {
1287285612Sdelphij		fprintf(stderr,
1288293650Sglebius			"***Internal error!  qsize (%zu) too large\n",
1289285612Sdelphij			qsize);
129054359Sroberto		return 1;
129154359Sroberto	}
129254359Sroberto
129354359Sroberto	/*
129454359Sroberto	 * Fill in the packet
129554359Sroberto	 */
129654359Sroberto	qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
1297132451Sroberto	qpkt.r_m_e_op = (u_char)(opcode & CTL_OP_MASK);
129854359Sroberto	qpkt.sequence = htons(sequence);
129954359Sroberto	qpkt.status = 0;
130054359Sroberto	qpkt.associd = htons((u_short)associd);
130154359Sroberto	qpkt.offset = 0;
130254359Sroberto	qpkt.count = htons((u_short)qsize);
130354359Sroberto
1304285612Sdelphij	pktsize = CTL_HEADER_LEN;
1305285612Sdelphij
130654359Sroberto	/*
1307285612Sdelphij	 * If we have data, copy and pad it out to a 32-bit boundary.
130854359Sroberto	 */
130954359Sroberto	if (qsize > 0) {
1310285612Sdelphij		memcpy(&qpkt.u, qdata, (size_t)qsize);
1311285612Sdelphij		pktsize += qsize;
1312285612Sdelphij		while (pktsize & (sizeof(u_int32) - 1)) {
1313285612Sdelphij			qpkt.u.data[qsize++] = 0;
131454359Sroberto			pktsize++;
131554359Sroberto		}
131654359Sroberto	}
131754359Sroberto
131854359Sroberto	/*
131954359Sroberto	 * If it isn't authenticated we can just send it.  Otherwise
132054359Sroberto	 * we're going to have to think about it a little.
132154359Sroberto	 */
132254359Sroberto	if (!auth && !always_auth) {
1323285612Sdelphij		return sendpkt(&qpkt, pktsize);
1324285612Sdelphij	}
132554359Sroberto
1326285612Sdelphij	/*
1327285612Sdelphij	 * Pad out packet to a multiple of 8 octets to be sure
1328285612Sdelphij	 * receiver can handle it.
1329285612Sdelphij	 */
1330285612Sdelphij	while (pktsize & 7) {
1331285612Sdelphij		qpkt.u.data[qsize++] = 0;
1332285612Sdelphij		pktsize++;
1333285612Sdelphij	}
133454359Sroberto
1335285612Sdelphij	/*
1336285612Sdelphij	 * Get the keyid and the password if we don't have one.
1337285612Sdelphij	 */
1338285612Sdelphij	if (info_auth_keyid == 0) {
1339285612Sdelphij		key_id = getkeyid("Keyid: ");
1340285612Sdelphij		if (key_id == 0 || key_id > NTP_MAXKEY) {
1341285612Sdelphij			fprintf(stderr,
1342285612Sdelphij				"Invalid key identifier\n");
1343285612Sdelphij			return 1;
134454359Sroberto		}
1345285612Sdelphij		info_auth_keyid = key_id;
1346285612Sdelphij	}
1347285612Sdelphij	if (!authistrusted(info_auth_keyid)) {
1348285612Sdelphij		pass = getpass_keytype(info_auth_keytype);
1349285612Sdelphij		if ('\0' == pass[0]) {
1350285612Sdelphij			fprintf(stderr, "Invalid password\n");
1351285612Sdelphij			return 1;
135254359Sroberto		}
1353285612Sdelphij		authusekey(info_auth_keyid, info_auth_keytype,
1354285612Sdelphij			   (u_char *)pass);
135554359Sroberto		authtrust(info_auth_keyid, 1);
1356285612Sdelphij	}
135754359Sroberto
1358285612Sdelphij	/*
1359285612Sdelphij	 * Do the encryption.
1360285612Sdelphij	 */
1361285612Sdelphij	maclen = authencrypt(info_auth_keyid, (void *)&qpkt, pktsize);
1362285612Sdelphij	if (!maclen) {
1363285612Sdelphij		fprintf(stderr, "Key not found\n");
1364285612Sdelphij		return 1;
1365285612Sdelphij	} else if ((size_t)maclen != (info_auth_hashlen + sizeof(keyid_t))) {
1366285612Sdelphij		fprintf(stderr,
1367293650Sglebius			"%zu octet MAC, %zu expected with %zu octet digest\n",
1368285612Sdelphij			maclen, (info_auth_hashlen + sizeof(keyid_t)),
1369285612Sdelphij			info_auth_hashlen);
1370285612Sdelphij		return 1;
137154359Sroberto	}
1372285612Sdelphij
1373285612Sdelphij	return sendpkt((char *)&qpkt, pktsize + maclen);
137454359Sroberto}
137554359Sroberto
137654359Sroberto
137754359Sroberto/*
1378285612Sdelphij * show_error_msg - display the error text for a mode 6 error response.
137954359Sroberto */
1380285612Sdelphijvoid
1381285612Sdelphijshow_error_msg(
1382285612Sdelphij	int		m6resp,
1383285612Sdelphij	associd_t	associd
1384285612Sdelphij	)
1385285612Sdelphij{
1386285612Sdelphij	if (numhosts > 1)
1387285612Sdelphij		fprintf(stderr, "server=%s ", currenthost);
1388285612Sdelphij
1389298770Sdelphij	switch (m6resp) {
1390285612Sdelphij
1391285612Sdelphij	case CERR_BADFMT:
1392285612Sdelphij		fprintf(stderr,
1393285612Sdelphij		    "***Server reports a bad format request packet\n");
1394285612Sdelphij		break;
1395285612Sdelphij
1396285612Sdelphij	case CERR_PERMISSION:
1397285612Sdelphij		fprintf(stderr,
1398285612Sdelphij		    "***Server disallowed request (authentication?)\n");
1399285612Sdelphij		break;
1400285612Sdelphij
1401285612Sdelphij	case CERR_BADOP:
1402285612Sdelphij		fprintf(stderr,
1403285612Sdelphij		    "***Server reports a bad opcode in request\n");
1404285612Sdelphij		break;
1405285612Sdelphij
1406285612Sdelphij	case CERR_BADASSOC:
1407285612Sdelphij		fprintf(stderr,
1408285612Sdelphij		    "***Association ID %d unknown to server\n",
1409285612Sdelphij		    associd);
1410285612Sdelphij		break;
1411285612Sdelphij
1412285612Sdelphij	case CERR_UNKNOWNVAR:
1413285612Sdelphij		fprintf(stderr,
1414285612Sdelphij		    "***A request variable unknown to the server\n");
1415285612Sdelphij		break;
1416285612Sdelphij
1417285612Sdelphij	case CERR_BADVALUE:
1418285612Sdelphij		fprintf(stderr,
1419285612Sdelphij		    "***Server indicates a request variable was bad\n");
1420285612Sdelphij		break;
1421285612Sdelphij
1422285612Sdelphij	case ERR_UNSPEC:
1423285612Sdelphij		fprintf(stderr,
1424285612Sdelphij		    "***Server returned an unspecified error\n");
1425285612Sdelphij		break;
1426285612Sdelphij
1427285612Sdelphij	case ERR_TIMEOUT:
1428285612Sdelphij		fprintf(stderr, "***Request timed out\n");
1429285612Sdelphij		break;
1430285612Sdelphij
1431285612Sdelphij	case ERR_INCOMPLETE:
1432285612Sdelphij		fprintf(stderr,
1433285612Sdelphij		    "***Response from server was incomplete\n");
1434285612Sdelphij		break;
1435285612Sdelphij
1436285612Sdelphij	case ERR_TOOMUCH:
1437285612Sdelphij		fprintf(stderr,
1438285612Sdelphij		    "***Buffer size exceeded for returned data\n");
1439285612Sdelphij		break;
1440285612Sdelphij
1441285612Sdelphij	default:
1442285612Sdelphij		fprintf(stderr,
1443285612Sdelphij		    "***Server returns unknown error code %d\n",
1444285612Sdelphij		    m6resp);
1445285612Sdelphij	}
1446285612Sdelphij}
1447285612Sdelphij
1448285612Sdelphij/*
1449285612Sdelphij * doquery - send a request and process the response, displaying
1450285612Sdelphij *	     error messages for any error responses.
1451285612Sdelphij */
145254359Srobertoint
145354359Srobertodoquery(
145454359Sroberto	int opcode,
1455285612Sdelphij	associd_t associd,
145654359Sroberto	int auth,
1457293650Sglebius	size_t qsize,
1458285612Sdelphij	const char *qdata,
145954359Sroberto	u_short *rstatus,
1460293650Sglebius	size_t *rsize,
1461285612Sdelphij	const char **rdata
146254359Sroberto	)
146354359Sroberto{
1464285612Sdelphij	return doqueryex(opcode, associd, auth, qsize, qdata, rstatus,
1465285612Sdelphij			 rsize, rdata, FALSE);
1466285612Sdelphij}
1467285612Sdelphij
1468285612Sdelphij
1469285612Sdelphij/*
1470285612Sdelphij * doqueryex - send a request and process the response, optionally
1471285612Sdelphij *	       displaying error messages for any error responses.
1472285612Sdelphij */
1473285612Sdelphijint
1474285612Sdelphijdoqueryex(
1475285612Sdelphij	int opcode,
1476285612Sdelphij	associd_t associd,
1477285612Sdelphij	int auth,
1478293650Sglebius	size_t qsize,
1479285612Sdelphij	const char *qdata,
1480285612Sdelphij	u_short *rstatus,
1481293650Sglebius	size_t *rsize,
1482285612Sdelphij	const char **rdata,
1483285612Sdelphij	int quiet
1484285612Sdelphij	)
1485285612Sdelphij{
148654359Sroberto	int res;
148754359Sroberto	int done;
148854359Sroberto
148954359Sroberto	/*
149054359Sroberto	 * Check to make sure host is open
149154359Sroberto	 */
149254359Sroberto	if (!havehost) {
1493285612Sdelphij		fprintf(stderr, "***No host open, use `host' command\n");
149454359Sroberto		return -1;
149554359Sroberto	}
149654359Sroberto
149754359Sroberto	done = 0;
149854359Sroberto	sequence++;
149954359Sroberto
150054359Sroberto    again:
150154359Sroberto	/*
150254359Sroberto	 * send a request
150354359Sroberto	 */
150454359Sroberto	res = sendrequest(opcode, associd, auth, qsize, qdata);
150554359Sroberto	if (res != 0)
1506285612Sdelphij		return res;
1507285612Sdelphij
150854359Sroberto	/*
150954359Sroberto	 * Get the response.  If we got a standard error, print a message
151054359Sroberto	 */
151154359Sroberto	res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
151254359Sroberto
151354359Sroberto	if (res > 0) {
151454359Sroberto		if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
151554359Sroberto			if (res == ERR_INCOMPLETE) {
151654359Sroberto				/*
151754359Sroberto				 * better bump the sequence so we don't
151854359Sroberto				 * get confused about differing fragments.
151954359Sroberto				 */
152054359Sroberto				sequence++;
152154359Sroberto			}
152254359Sroberto			done = 1;
152354359Sroberto			goto again;
152454359Sroberto		}
1525285612Sdelphij		if (!quiet)
1526285612Sdelphij			show_error_msg(res, associd);
1527285612Sdelphij
152854359Sroberto	}
152954359Sroberto	return res;
153054359Sroberto}
153154359Sroberto
153254359Sroberto
1533285612Sdelphij#ifndef BUILD_AS_LIB
153454359Sroberto/*
153554359Sroberto * getcmds - read commands from the standard input and execute them
153654359Sroberto */
153754359Srobertostatic void
153854359Srobertogetcmds(void)
153954359Sroberto{
1540285612Sdelphij	char *	line;
1541285612Sdelphij	int	count;
154254359Sroberto
1543285612Sdelphij	ntp_readline_init(interactive ? prompt : NULL);
1544106163Sroberto
1545285612Sdelphij	for (;;) {
1546285612Sdelphij		line = ntp_readline(&count);
1547285612Sdelphij		if (NULL == line)
1548285612Sdelphij			break;
1549285612Sdelphij		docmd(line);
1550285612Sdelphij		free(line);
1551285612Sdelphij	}
155254359Sroberto
1553285612Sdelphij	ntp_readline_uninit();
155454359Sroberto}
1555285612Sdelphij#endif /* !BUILD_AS_LIB */
155654359Sroberto
1557285612Sdelphij
1558285612Sdelphij#if !defined(SYS_WINNT) && !defined(BUILD_AS_LIB)
155954359Sroberto/*
156054359Sroberto * abortcmd - catch interrupts and abort the current command
156154359Sroberto */
1562293650Sglebiusstatic int
1563293650Sglebiusabortcmd(void)
156454359Sroberto{
156554359Sroberto	if (current_output == stdout)
1566293650Sglebius		(void) fflush(stdout);
156754359Sroberto	putc('\n', stderr);
156854359Sroberto	(void) fflush(stderr);
1569293650Sglebius	if (jump) {
1570293650Sglebius		jump = 0;
1571293650Sglebius		longjmp(interrupt_buf, 1);
1572293650Sglebius	}
1573293650Sglebius	return TRUE;
157454359Sroberto}
1575285612Sdelphij#endif	/* !SYS_WINNT && !BUILD_AS_LIB */
157654359Sroberto
1577285612Sdelphij
1578285612Sdelphij#ifndef	BUILD_AS_LIB
157954359Sroberto/*
158054359Sroberto * docmd - decode the command line and execute a command
158154359Sroberto */
158254359Srobertostatic void
158354359Srobertodocmd(
158454359Sroberto	const char *cmdline
158554359Sroberto	)
158654359Sroberto{
158754359Sroberto	char *tokens[1+MAXARGS+2];
158854359Sroberto	struct parse pcmd;
158954359Sroberto	int ntok;
159054359Sroberto	static int i;
159154359Sroberto	struct xcmd *xcmd;
159254359Sroberto
159354359Sroberto	/*
159454359Sroberto	 * Tokenize the command line.  If nothing on it, return.
159554359Sroberto	 */
159654359Sroberto	tokenize(cmdline, tokens, &ntok);
159754359Sroberto	if (ntok == 0)
159854359Sroberto	    return;
1599285612Sdelphij
160054359Sroberto	/*
160154359Sroberto	 * Find the appropriate command description.
160254359Sroberto	 */
160354359Sroberto	i = findcmd(tokens[0], builtins, opcmds, &xcmd);
160454359Sroberto	if (i == 0) {
160554359Sroberto		(void) fprintf(stderr, "***Command `%s' unknown\n",
160654359Sroberto			       tokens[0]);
160754359Sroberto		return;
160854359Sroberto	} else if (i >= 2) {
160954359Sroberto		(void) fprintf(stderr, "***Command `%s' ambiguous\n",
161054359Sroberto			       tokens[0]);
161154359Sroberto		return;
161254359Sroberto	}
1613285612Sdelphij
1614285612Sdelphij	/* Warn about ignored extra args */
1615285612Sdelphij	for (i = MAXARGS + 1; i < ntok ; ++i) {
1616285612Sdelphij		fprintf(stderr, "***Extra arg `%s' ignored\n", tokens[i]);
1617285612Sdelphij	}
1618285612Sdelphij
161954359Sroberto	/*
162054359Sroberto	 * Save the keyword, then walk through the arguments, interpreting
162154359Sroberto	 * as we go.
162254359Sroberto	 */
162354359Sroberto	pcmd.keyword = tokens[0];
162454359Sroberto	pcmd.nargs = 0;
162554359Sroberto	for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
162654359Sroberto		if ((i+1) >= ntok) {
162754359Sroberto			if (!(xcmd->arg[i] & OPT)) {
162854359Sroberto				printusage(xcmd, stderr);
162954359Sroberto				return;
163054359Sroberto			}
163154359Sroberto			break;
163254359Sroberto		}
163354359Sroberto		if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
1634285612Sdelphij			break;
163554359Sroberto		if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
1636285612Sdelphij			return;
163754359Sroberto		pcmd.nargs++;
163854359Sroberto	}
163954359Sroberto
164054359Sroberto	i++;
164154359Sroberto	if (i < ntok && *tokens[i] == '>') {
164254359Sroberto		char *fname;
164354359Sroberto
164454359Sroberto		if (*(tokens[i]+1) != '\0')
1645285612Sdelphij			fname = tokens[i]+1;
164654359Sroberto		else if ((i+1) < ntok)
1647285612Sdelphij			fname = tokens[i+1];
164854359Sroberto		else {
164954359Sroberto			(void) fprintf(stderr, "***No file for redirect\n");
165054359Sroberto			return;
165154359Sroberto		}
165254359Sroberto
165354359Sroberto		current_output = fopen(fname, "w");
165454359Sroberto		if (current_output == NULL) {
165554359Sroberto			(void) fprintf(stderr, "***Error opening %s: ", fname);
165654359Sroberto			perror("");
165754359Sroberto			return;
165854359Sroberto		}
165954359Sroberto		i = 1;		/* flag we need a close */
166054359Sroberto	} else {
166154359Sroberto		current_output = stdout;
166254359Sroberto		i = 0;		/* flag no close */
166354359Sroberto	}
166454359Sroberto
166554359Sroberto	if (interactive && setjmp(interrupt_buf)) {
166654359Sroberto		jump = 0;
166754359Sroberto		return;
166854359Sroberto	} else {
166954359Sroberto		jump++;
167054359Sroberto		(xcmd->handler)(&pcmd, current_output);
167154359Sroberto		jump = 0;	/* HMS: 961106: was after fclose() */
167254359Sroberto		if (i) (void) fclose(current_output);
167354359Sroberto	}
1674285612Sdelphij
1675285612Sdelphij	return;
167654359Sroberto}
167754359Sroberto
167854359Sroberto
167954359Sroberto/*
168054359Sroberto * tokenize - turn a command line into tokens
1681285612Sdelphij *
1682285612Sdelphij * SK: Modified to allow a quoted string
1683285612Sdelphij *
1684285612Sdelphij * HMS: If the first character of the first token is a ':' then (after
1685285612Sdelphij * eating inter-token whitespace) the 2nd token is the rest of the line.
168654359Sroberto */
1687285612Sdelphij
168854359Srobertostatic void
168954359Srobertotokenize(
169054359Sroberto	const char *line,
169154359Sroberto	char **tokens,
169254359Sroberto	int *ntok
169354359Sroberto	)
169454359Sroberto{
169554359Sroberto	register const char *cp;
169654359Sroberto	register char *sp;
169754359Sroberto	static char tspace[MAXLINE];
169854359Sroberto
169954359Sroberto	sp = tspace;
170054359Sroberto	cp = line;
170154359Sroberto	for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
170254359Sroberto		tokens[*ntok] = sp;
1703285612Sdelphij
1704285612Sdelphij		/* Skip inter-token whitespace */
170554359Sroberto		while (ISSPACE(*cp))
170654359Sroberto		    cp++;
1707285612Sdelphij
1708285612Sdelphij		/* If we're at EOL we're done */
170954359Sroberto		if (ISEOL(*cp))
171054359Sroberto		    break;
171154359Sroberto
1712285612Sdelphij		/* If this is the 2nd token and the first token begins
1713285612Sdelphij		 * with a ':', then just grab to EOL.
1714285612Sdelphij		 */
1715285612Sdelphij
1716285612Sdelphij		if (*ntok == 1 && tokens[0][0] == ':') {
1717285612Sdelphij			do {
1718285612Sdelphij				if (sp - tspace >= MAXLINE)
1719285612Sdelphij					goto toobig;
1720285612Sdelphij				*sp++ = *cp++;
1721285612Sdelphij			} while (!ISEOL(*cp));
1722285612Sdelphij		}
1723285612Sdelphij
1724285612Sdelphij		/* Check if this token begins with a double quote.
1725285612Sdelphij		 * If yes, continue reading till the next double quote
1726285612Sdelphij		 */
1727285612Sdelphij		else if (*cp == '\"') {
1728285612Sdelphij			++cp;
1729285612Sdelphij			do {
1730285612Sdelphij				if (sp - tspace >= MAXLINE)
1731285612Sdelphij					goto toobig;
1732285612Sdelphij				*sp++ = *cp++;
1733285612Sdelphij			} while ((*cp != '\"') && !ISEOL(*cp));
1734285612Sdelphij			/* HMS: a missing closing " should be an error */
1735285612Sdelphij		}
1736285612Sdelphij		else {
1737285612Sdelphij			do {
1738285612Sdelphij				if (sp - tspace >= MAXLINE)
1739285612Sdelphij					goto toobig;
1740285612Sdelphij				*sp++ = *cp++;
1741285612Sdelphij			} while ((*cp != '\"') && !ISSPACE(*cp) && !ISEOL(*cp));
1742285612Sdelphij			/* HMS: Why check for a " in the previous line? */
1743285612Sdelphij		}
1744285612Sdelphij
1745285612Sdelphij		if (sp - tspace >= MAXLINE)
1746285612Sdelphij			goto toobig;
174754359Sroberto		*sp++ = '\0';
174854359Sroberto	}
1749285612Sdelphij	return;
1750285612Sdelphij
1751285612Sdelphij  toobig:
1752285612Sdelphij	*ntok = 0;
1753285612Sdelphij	fprintf(stderr,
1754285612Sdelphij		"***Line `%s' is too big\n",
1755285612Sdelphij		line);
1756285612Sdelphij	return;
175754359Sroberto}
175854359Sroberto
175954359Sroberto
1760285612Sdelphij/*
1761285612Sdelphij * getarg - interpret an argument token
1762285612Sdelphij */
1763285612Sdelphijstatic int
1764285612Sdelphijgetarg(
1765285612Sdelphij	const char *str,
1766285612Sdelphij	int code,
1767285612Sdelphij	arg_v *argp
1768285612Sdelphij	)
1769285612Sdelphij{
1770285612Sdelphij	u_long ul;
177154359Sroberto
1772285612Sdelphij	switch (code & ~OPT) {
1773285612Sdelphij	case NTP_STR:
1774285612Sdelphij		argp->string = str;
1775285612Sdelphij		break;
1776285612Sdelphij
1777285612Sdelphij	case NTP_ADD:
1778285612Sdelphij		if (!getnetnum(str, &argp->netnum, NULL, 0))
1779285612Sdelphij			return 0;
1780285612Sdelphij		break;
1781285612Sdelphij
1782285612Sdelphij	case NTP_UINT:
1783285612Sdelphij		if ('&' == str[0]) {
1784285612Sdelphij			if (!atouint(&str[1], &ul)) {
1785285612Sdelphij				fprintf(stderr,
1786285612Sdelphij					"***Association index `%s' invalid/undecodable\n",
1787285612Sdelphij					str);
1788285612Sdelphij				return 0;
1789285612Sdelphij			}
1790285612Sdelphij			if (0 == numassoc) {
1791285612Sdelphij				dogetassoc(stdout);
1792285612Sdelphij				if (0 == numassoc) {
1793285612Sdelphij					fprintf(stderr,
1794285612Sdelphij						"***No associations found, `%s' unknown\n",
1795285612Sdelphij						str);
1796285612Sdelphij					return 0;
1797285612Sdelphij				}
1798285612Sdelphij			}
1799285612Sdelphij			ul = min(ul, numassoc);
1800285612Sdelphij			argp->uval = assoc_cache[ul - 1].assid;
1801285612Sdelphij			break;
1802285612Sdelphij		}
1803285612Sdelphij		if (!atouint(str, &argp->uval)) {
1804285612Sdelphij			fprintf(stderr, "***Illegal unsigned value %s\n",
1805285612Sdelphij				str);
1806285612Sdelphij			return 0;
1807285612Sdelphij		}
1808285612Sdelphij		break;
1809285612Sdelphij
1810285612Sdelphij	case NTP_INT:
1811285612Sdelphij		if (!atoint(str, &argp->ival)) {
1812285612Sdelphij			fprintf(stderr, "***Illegal integer value %s\n",
1813285612Sdelphij				str);
1814285612Sdelphij			return 0;
1815285612Sdelphij		}
1816285612Sdelphij		break;
1817285612Sdelphij
1818285612Sdelphij	case IP_VERSION:
1819285612Sdelphij		if (!strcmp("-6", str)) {
1820285612Sdelphij			argp->ival = 6;
1821285612Sdelphij		} else if (!strcmp("-4", str)) {
1822285612Sdelphij			argp->ival = 4;
1823285612Sdelphij		} else {
1824285612Sdelphij			fprintf(stderr, "***Version must be either 4 or 6\n");
1825285612Sdelphij			return 0;
1826285612Sdelphij		}
1827285612Sdelphij		break;
1828285612Sdelphij	}
1829285612Sdelphij
1830285612Sdelphij	return 1;
1831285612Sdelphij}
1832285612Sdelphij#endif	/* !BUILD_AS_LIB */
1833285612Sdelphij
1834285612Sdelphij
183554359Sroberto/*
183654359Sroberto * findcmd - find a command in a command description table
183754359Sroberto */
183854359Srobertostatic int
183954359Srobertofindcmd(
1840285612Sdelphij	const char *	str,
1841285612Sdelphij	struct xcmd *	clist1,
1842285612Sdelphij	struct xcmd *	clist2,
1843285612Sdelphij	struct xcmd **	cmd
184454359Sroberto	)
184554359Sroberto{
1846285612Sdelphij	struct xcmd *cl;
1847293650Sglebius	size_t clen;
184854359Sroberto	int nmatch;
184954359Sroberto	struct xcmd *nearmatch = NULL;
185054359Sroberto	struct xcmd *clist;
185154359Sroberto
185254359Sroberto	clen = strlen(str);
185354359Sroberto	nmatch = 0;
185454359Sroberto	if (clist1 != 0)
185554359Sroberto	    clist = clist1;
185654359Sroberto	else if (clist2 != 0)
185754359Sroberto	    clist = clist2;
185854359Sroberto	else
185954359Sroberto	    return 0;
186054359Sroberto
186154359Sroberto    again:
186254359Sroberto	for (cl = clist; cl->keyword != 0; cl++) {
186354359Sroberto		/* do a first character check, for efficiency */
186454359Sroberto		if (*str != *(cl->keyword))
186554359Sroberto		    continue;
186654359Sroberto		if (strncmp(str, cl->keyword, (unsigned)clen) == 0) {
186754359Sroberto			/*
186854359Sroberto			 * Could be extact match, could be approximate.
186954359Sroberto			 * Is exact if the length of the keyword is the
187054359Sroberto			 * same as the str.
187154359Sroberto			 */
187254359Sroberto			if (*((cl->keyword) + clen) == '\0') {
187354359Sroberto				*cmd = cl;
187454359Sroberto				return 1;
187554359Sroberto			}
187654359Sroberto			nmatch++;
187754359Sroberto			nearmatch = cl;
187854359Sroberto		}
187954359Sroberto	}
188054359Sroberto
188154359Sroberto	/*
188254359Sroberto	 * See if there is more to do.  If so, go again.  Sorry about the
188354359Sroberto	 * goto, too much looking at BSD sources...
188454359Sroberto	 */
188554359Sroberto	if (clist == clist1 && clist2 != 0) {
188654359Sroberto		clist = clist2;
188754359Sroberto		goto again;
188854359Sroberto	}
188954359Sroberto
189054359Sroberto	/*
189154359Sroberto	 * If we got extactly 1 near match, use it, else return number
189254359Sroberto	 * of matches.
189354359Sroberto	 */
189454359Sroberto	if (nmatch == 1) {
189554359Sroberto		*cmd = nearmatch;
189654359Sroberto		return 1;
189754359Sroberto	}
189854359Sroberto	return nmatch;
189954359Sroberto}
190054359Sroberto
190154359Sroberto
190254359Sroberto/*
190354359Sroberto * getnetnum - given a host name, return its net number
190454359Sroberto *	       and (optional) full name
190554359Sroberto */
190654359Srobertoint
190754359Srobertogetnetnum(
190854359Sroberto	const char *hname,
1909285612Sdelphij	sockaddr_u *num,
1910132451Sroberto	char *fullhost,
1911132451Sroberto	int af
191254359Sroberto	)
191354359Sroberto{
1914132451Sroberto	struct addrinfo hints, *ai = NULL;
191554359Sroberto
1916285612Sdelphij	ZERO(hints);
1917132451Sroberto	hints.ai_flags = AI_CANONNAME;
1918132451Sroberto#ifdef AI_ADDRCONFIG
1919132451Sroberto	hints.ai_flags |= AI_ADDRCONFIG;
1920132451Sroberto#endif
1921285612Sdelphij
1922285612Sdelphij	/*
1923285612Sdelphij	 * decodenetnum only works with addresses, but handles syntax
1924285612Sdelphij	 * that getaddrinfo doesn't:  [2001::1]:1234
1925285612Sdelphij	 */
192654359Sroberto	if (decodenetnum(hname, num)) {
1927285612Sdelphij		if (fullhost != NULL)
1928285612Sdelphij			getnameinfo(&num->sa, SOCKLEN(num), fullhost,
1929285612Sdelphij				    LENHOSTNAME, NULL, 0, 0);
193054359Sroberto		return 1;
1931182007Sroberto	} else if (getaddrinfo(hname, "ntp", &hints, &ai) == 0) {
1932285612Sdelphij		INSIST(sizeof(*num) >= ai->ai_addrlen);
1933285612Sdelphij		memcpy(num, ai->ai_addr, ai->ai_addrlen);
1934285612Sdelphij		if (fullhost != NULL) {
1935285612Sdelphij			if (ai->ai_canonname != NULL)
1936285612Sdelphij				strlcpy(fullhost, ai->ai_canonname,
1937285612Sdelphij					LENHOSTNAME);
1938285612Sdelphij			else
1939285612Sdelphij				getnameinfo(&num->sa, SOCKLEN(num),
1940285612Sdelphij					    fullhost, LENHOSTNAME, NULL,
1941285612Sdelphij					    0, 0);
1942285612Sdelphij		}
1943285612Sdelphij		freeaddrinfo(ai);
194454359Sroberto		return 1;
194554359Sroberto	}
1946285612Sdelphij	fprintf(stderr, "***Can't find host %s\n", hname);
1947285612Sdelphij
1948285612Sdelphij	return 0;
194954359Sroberto}
195054359Sroberto
1951285612Sdelphij
195254359Sroberto/*
195354359Sroberto * nntohost - convert network number to host name.  This routine enforces
195454359Sroberto *	       the showhostnames setting.
195554359Sroberto */
1956285612Sdelphijconst char *
195754359Srobertonntohost(
1958285612Sdelphij	sockaddr_u *netnum
195954359Sroberto	)
196054359Sroberto{
1961285612Sdelphij	return nntohost_col(netnum, LIB_BUFLENGTH - 1, FALSE);
196254359Sroberto}
196354359Sroberto
196454359Sroberto
196554359Sroberto/*
1966285612Sdelphij * nntohost_col - convert network number to host name in fixed width.
1967285612Sdelphij *		  This routine enforces the showhostnames setting.
1968285612Sdelphij *		  When displaying hostnames longer than the width,
1969285612Sdelphij *		  the first part of the hostname is displayed.  When
1970285612Sdelphij *		  displaying numeric addresses longer than the width,
1971285612Sdelphij *		  Such as IPv6 addresses, the caller decides whether
1972285612Sdelphij *		  the first or last of the numeric address is used.
1973285612Sdelphij */
1974285612Sdelphijconst char *
1975285612Sdelphijnntohost_col(
1976285612Sdelphij	sockaddr_u *	addr,
1977285612Sdelphij	size_t		width,
1978285612Sdelphij	int		preserve_lowaddrbits
1979285612Sdelphij	)
1980285612Sdelphij{
1981285612Sdelphij	const char *	out;
1982285612Sdelphij
1983285612Sdelphij	if (!showhostnames || SOCK_UNSPEC(addr)) {
1984285612Sdelphij		if (preserve_lowaddrbits)
1985285612Sdelphij			out = trunc_left(stoa(addr), width);
1986285612Sdelphij		else
1987285612Sdelphij			out = trunc_right(stoa(addr), width);
1988285612Sdelphij	} else if (ISREFCLOCKADR(addr)) {
1989285612Sdelphij		out = refnumtoa(addr);
1990285612Sdelphij	} else {
1991285612Sdelphij		out = trunc_right(socktohost(addr), width);
1992285612Sdelphij	}
1993285612Sdelphij	return out;
1994285612Sdelphij}
1995285612Sdelphij
1996285612Sdelphij
1997285612Sdelphij/*
1998285612Sdelphij * nntohostp() is the same as nntohost() plus a :port suffix
1999285612Sdelphij */
2000285612Sdelphijconst char *
2001285612Sdelphijnntohostp(
2002285612Sdelphij	sockaddr_u *netnum
2003285612Sdelphij	)
2004285612Sdelphij{
2005285612Sdelphij	const char *	hostn;
2006285612Sdelphij	char *		buf;
2007285612Sdelphij
2008285612Sdelphij	if (!showhostnames || SOCK_UNSPEC(netnum))
2009285612Sdelphij		return sptoa(netnum);
2010285612Sdelphij	else if (ISREFCLOCKADR(netnum))
2011285612Sdelphij		return refnumtoa(netnum);
2012285612Sdelphij
2013285612Sdelphij	hostn = socktohost(netnum);
2014285612Sdelphij	LIB_GETBUF(buf);
2015285612Sdelphij	snprintf(buf, LIB_BUFLENGTH, "%s:%u", hostn, SRCPORT(netnum));
2016285612Sdelphij
2017285612Sdelphij	return buf;
2018285612Sdelphij}
2019285612Sdelphij
2020285612Sdelphij/*
202154359Sroberto * rtdatetolfp - decode an RT-11 date into an l_fp
202254359Sroberto */
202354359Srobertostatic int
202454359Srobertortdatetolfp(
202554359Sroberto	char *str,
202654359Sroberto	l_fp *lfp
202754359Sroberto	)
202854359Sroberto{
202954359Sroberto	register char *cp;
203054359Sroberto	register int i;
203154359Sroberto	struct calendar cal;
203254359Sroberto	char buf[4];
203354359Sroberto
203454359Sroberto	cal.yearday = 0;
203554359Sroberto
203654359Sroberto	/*
203754359Sroberto	 * An RT-11 date looks like:
203854359Sroberto	 *
203954359Sroberto	 * d[d]-Mth-y[y] hh:mm:ss
204054359Sroberto	 *
204154359Sroberto	 * (No docs, but assume 4-digit years are also legal...)
204254359Sroberto	 *
204354359Sroberto	 * d[d]-Mth-y[y[y[y]]] hh:mm:ss
204454359Sroberto	 */
204554359Sroberto	cp = str;
2046330567Sgordon	if (!isdigit(pgetc(cp))) {
204754359Sroberto		if (*cp == '-') {
204854359Sroberto			/*
204954359Sroberto			 * Catch special case
205054359Sroberto			 */
205154359Sroberto			L_CLR(lfp);
205254359Sroberto			return 1;
205354359Sroberto		}
205454359Sroberto		return 0;
205554359Sroberto	}
205654359Sroberto
2057132451Sroberto	cal.monthday = (u_char) (*cp++ - '0');	/* ascii dependent */
2058330567Sgordon	if (isdigit(pgetc(cp))) {
2059132451Sroberto		cal.monthday = (u_char)((cal.monthday << 3) + (cal.monthday << 1));
2060132451Sroberto		cal.monthday = (u_char)(cal.monthday + *cp++ - '0');
206154359Sroberto	}
206254359Sroberto
206354359Sroberto	if (*cp++ != '-')
206454359Sroberto	    return 0;
2065285612Sdelphij
206654359Sroberto	for (i = 0; i < 3; i++)
206754359Sroberto	    buf[i] = *cp++;
206854359Sroberto	buf[3] = '\0';
206954359Sroberto
207054359Sroberto	for (i = 0; i < 12; i++)
207154359Sroberto	    if (STREQ(buf, months[i]))
207254359Sroberto		break;
207354359Sroberto	if (i == 12)
207454359Sroberto	    return 0;
2075132451Sroberto	cal.month = (u_char)(i + 1);
207654359Sroberto
207754359Sroberto	if (*cp++ != '-')
207854359Sroberto	    return 0;
2079285612Sdelphij
2080330567Sgordon	if (!isdigit(pgetc(cp)))
208154359Sroberto	    return 0;
2082132451Sroberto	cal.year = (u_short)(*cp++ - '0');
2083330567Sgordon	if (isdigit(pgetc(cp))) {
2084132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2085132451Sroberto		cal.year = (u_short)(*cp++ - '0');
208654359Sroberto	}
2087330567Sgordon	if (isdigit(pgetc(cp))) {
2088132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2089132451Sroberto		cal.year = (u_short)(cal.year + *cp++ - '0');
209054359Sroberto	}
2091330567Sgordon	if (isdigit(pgetc(cp))) {
2092132451Sroberto		cal.year = (u_short)((cal.year << 3) + (cal.year << 1));
2093132451Sroberto		cal.year = (u_short)(cal.year + *cp++ - '0');
209454359Sroberto	}
209554359Sroberto
209654359Sroberto	/*
209754359Sroberto	 * Catch special case.  If cal.year == 0 this is a zero timestamp.
209854359Sroberto	 */
209954359Sroberto	if (cal.year == 0) {
210054359Sroberto		L_CLR(lfp);
210154359Sroberto		return 1;
210254359Sroberto	}
210354359Sroberto
2104330567Sgordon	if (*cp++ != ' ' || !isdigit(pgetc(cp)))
210554359Sroberto	    return 0;
2106132451Sroberto	cal.hour = (u_char)(*cp++ - '0');
2107330567Sgordon	if (isdigit(pgetc(cp))) {
2108132451Sroberto		cal.hour = (u_char)((cal.hour << 3) + (cal.hour << 1));
2109132451Sroberto		cal.hour = (u_char)(cal.hour + *cp++ - '0');
211054359Sroberto	}
211154359Sroberto
2112330567Sgordon	if (*cp++ != ':' || !isdigit(pgetc(cp)))
211354359Sroberto	    return 0;
2114132451Sroberto	cal.minute = (u_char)(*cp++ - '0');
2115330567Sgordon	if (isdigit(pgetc(cp))) {
2116132451Sroberto		cal.minute = (u_char)((cal.minute << 3) + (cal.minute << 1));
2117132451Sroberto		cal.minute = (u_char)(cal.minute + *cp++ - '0');
211854359Sroberto	}
211954359Sroberto
2120330567Sgordon	if (*cp++ != ':' || !isdigit(pgetc(cp)))
212154359Sroberto	    return 0;
2122132451Sroberto	cal.second = (u_char)(*cp++ - '0');
2123330567Sgordon	if (isdigit(pgetc(cp))) {
2124132451Sroberto		cal.second = (u_char)((cal.second << 3) + (cal.second << 1));
2125132451Sroberto		cal.second = (u_char)(cal.second + *cp++ - '0');
212654359Sroberto	}
212754359Sroberto
212854359Sroberto	/*
212954359Sroberto	 * For RT-11, 1972 seems to be the pivot year
213054359Sroberto	 */
213154359Sroberto	if (cal.year < 72)
213254359Sroberto		cal.year += 2000;
213354359Sroberto	if (cal.year < 100)
213454359Sroberto		cal.year += 1900;
213554359Sroberto
213654359Sroberto	lfp->l_ui = caltontp(&cal);
213754359Sroberto	lfp->l_uf = 0;
213854359Sroberto	return 1;
213954359Sroberto}
214054359Sroberto
214154359Sroberto
214254359Sroberto/*
214354359Sroberto * decodets - decode a timestamp into an l_fp format number, with
214454359Sroberto *	      consideration of fuzzball formats.
214554359Sroberto */
214654359Srobertoint
214754359Srobertodecodets(
214854359Sroberto	char *str,
214954359Sroberto	l_fp *lfp
215054359Sroberto	)
215154359Sroberto{
2152285612Sdelphij	char *cp;
2153285612Sdelphij	char buf[30];
2154285612Sdelphij	size_t b;
2155285612Sdelphij
215654359Sroberto	/*
215754359Sroberto	 * If it starts with a 0x, decode as hex.
215854359Sroberto	 */
215954359Sroberto	if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
2160285612Sdelphij		return hextolfp(str+2, lfp);
216154359Sroberto
216254359Sroberto	/*
216354359Sroberto	 * If it starts with a '"', try it as an RT-11 date.
216454359Sroberto	 */
216554359Sroberto	if (*str == '"') {
2166285612Sdelphij		cp = str + 1;
2167285612Sdelphij		b = 0;
2168285612Sdelphij		while ('"' != *cp && '\0' != *cp &&
2169285612Sdelphij		       b < COUNTOF(buf) - 1)
2170285612Sdelphij			buf[b++] = *cp++;
2171285612Sdelphij		buf[b] = '\0';
217254359Sroberto		return rtdatetolfp(buf, lfp);
217354359Sroberto	}
217454359Sroberto
217554359Sroberto	/*
217654359Sroberto	 * Might still be hex.  Check out the first character.  Talk
217754359Sroberto	 * about heuristics!
217854359Sroberto	 */
217954359Sroberto	if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
2180285612Sdelphij		return hextolfp(str, lfp);
218154359Sroberto
218254359Sroberto	/*
218354359Sroberto	 * Try it as a decimal.  If this fails, try as an unquoted
218454359Sroberto	 * RT-11 date.  This code should go away eventually.
218554359Sroberto	 */
218654359Sroberto	if (atolfp(str, lfp))
2187285612Sdelphij		return 1;
2188285612Sdelphij
218954359Sroberto	return rtdatetolfp(str, lfp);
219054359Sroberto}
219154359Sroberto
219254359Sroberto
219354359Sroberto/*
219454359Sroberto * decodetime - decode a time value.  It should be in milliseconds
219554359Sroberto */
219654359Srobertoint
219754359Srobertodecodetime(
219854359Sroberto	char *str,
219954359Sroberto	l_fp *lfp
220054359Sroberto	)
220154359Sroberto{
220254359Sroberto	return mstolfp(str, lfp);
220354359Sroberto}
220454359Sroberto
220554359Sroberto
220654359Sroberto/*
220754359Sroberto * decodeint - decode an integer
220854359Sroberto */
220954359Srobertoint
221054359Srobertodecodeint(
221154359Sroberto	char *str,
221254359Sroberto	long *val
221354359Sroberto	)
221454359Sroberto{
221554359Sroberto	if (*str == '0') {
221654359Sroberto		if (*(str+1) == 'x' || *(str+1) == 'X')
2217285612Sdelphij		    return hextoint(str+2, (u_long *)val);
2218285612Sdelphij		return octtoint(str, (u_long *)val);
221954359Sroberto	}
222054359Sroberto	return atoint(str, val);
222154359Sroberto}
222254359Sroberto
222354359Sroberto
222454359Sroberto/*
222554359Sroberto * decodeuint - decode an unsigned integer
222654359Sroberto */
222754359Srobertoint
222854359Srobertodecodeuint(
222954359Sroberto	char *str,
223054359Sroberto	u_long *val
223154359Sroberto	)
223254359Sroberto{
223354359Sroberto	if (*str == '0') {
223454359Sroberto		if (*(str + 1) == 'x' || *(str + 1) == 'X')
223554359Sroberto			return (hextoint(str + 2, val));
223654359Sroberto		return (octtoint(str, val));
223754359Sroberto	}
223854359Sroberto	return (atouint(str, val));
223954359Sroberto}
224054359Sroberto
224154359Sroberto
224254359Sroberto/*
224354359Sroberto * decodearr - decode an array of time values
224454359Sroberto */
224554359Srobertostatic int
224654359Srobertodecodearr(
2247330567Sgordon	char *cp,
2248330567Sgordon	int  *narr,
2249330567Sgordon	l_fp *lfpa,
2250330567Sgordon	int   amax
225154359Sroberto	)
225254359Sroberto{
2253330567Sgordon	char *bp;
225454359Sroberto	char buf[60];
225554359Sroberto
225654359Sroberto	*narr = 0;
225754359Sroberto
2258330567Sgordon	while (*narr < amax && *cp) {
2259330567Sgordon		if (isspace(pgetc(cp))) {
2260330567Sgordon			do
2261330567Sgordon				++cp;
2262330567Sgordon			while (*cp && isspace(pgetc(cp)));
2263330567Sgordon		} else {
2264330567Sgordon			bp = buf;
2265330567Sgordon			do {
2266330567Sgordon				if (bp != (buf + sizeof(buf) - 1))
2267330567Sgordon					*bp++ = *cp;
2268330567Sgordon				++cp;
2269330567Sgordon			} while (*cp && !isspace(pgetc(cp)));
2270330567Sgordon			*bp = '\0';
227154359Sroberto
2272330567Sgordon			if (!decodetime(buf, lfpa))
2273330567Sgordon				return 0;
2274330567Sgordon			++(*narr);
2275330567Sgordon			++lfpa;
2276330567Sgordon		}
227754359Sroberto	}
227854359Sroberto	return 1;
227954359Sroberto}
228054359Sroberto
228154359Sroberto
228254359Sroberto/*
228354359Sroberto * Finally, the built in command handlers
228454359Sroberto */
228554359Sroberto
228654359Sroberto/*
228754359Sroberto * help - tell about commands, or details of a particular command
228854359Sroberto */
228954359Srobertostatic void
229054359Srobertohelp(
229154359Sroberto	struct parse *pcmd,
229254359Sroberto	FILE *fp
229354359Sroberto	)
229454359Sroberto{
2295285612Sdelphij	struct xcmd *xcp = NULL;	/* quiet warning */
2296285612Sdelphij	const char *cmd;
2297182007Sroberto	const char *list[100];
2298285612Sdelphij	size_t word, words;
2299285612Sdelphij	size_t row, rows;
2300285612Sdelphij	size_t col, cols;
2301285612Sdelphij	size_t length;
230254359Sroberto
230354359Sroberto	if (pcmd->nargs == 0) {
2304182007Sroberto		words = 0;
2305285612Sdelphij		for (xcp = builtins; xcp->keyword != NULL; xcp++) {
2306285612Sdelphij			if (*(xcp->keyword) != '?' &&
2307285612Sdelphij			    words < COUNTOF(list))
2308285612Sdelphij				list[words++] = xcp->keyword;
230954359Sroberto		}
2310285612Sdelphij		for (xcp = opcmds; xcp->keyword != NULL; xcp++)
2311285612Sdelphij			if (words < COUNTOF(list))
2312285612Sdelphij				list[words++] = xcp->keyword;
231354359Sroberto
2314285612Sdelphij		qsort((void *)list, words, sizeof(list[0]), helpsort);
2315182007Sroberto		col = 0;
2316182007Sroberto		for (word = 0; word < words; word++) {
2317285612Sdelphij			length = strlen(list[word]);
2318285612Sdelphij			col = max(col, length);
231954359Sroberto		}
232054359Sroberto
2321182007Sroberto		cols = SCREENWIDTH / ++col;
2322285612Sdelphij		rows = (words + cols - 1) / cols;
2323182007Sroberto
2324285612Sdelphij		fprintf(fp, "ntpq commands:\n");
2325182007Sroberto
2326182007Sroberto		for (row = 0; row < rows; row++) {
2327285612Sdelphij			for (word = row; word < words; word += rows)
2328285612Sdelphij				fprintf(fp, "%-*.*s", (int)col,
2329285612Sdelphij					(int)col - 1, list[word]);
2330285612Sdelphij			fprintf(fp, "\n");
2331285612Sdelphij		}
233254359Sroberto	} else {
233354359Sroberto		cmd = pcmd->argval[0].string;
2334182007Sroberto		words = findcmd(cmd, builtins, opcmds, &xcp);
2335182007Sroberto		if (words == 0) {
2336285612Sdelphij			fprintf(stderr,
2337285612Sdelphij				"Command `%s' is unknown\n", cmd);
233854359Sroberto			return;
2339182007Sroberto		} else if (words >= 2) {
2340285612Sdelphij			fprintf(stderr,
2341285612Sdelphij				"Command `%s' is ambiguous\n", cmd);
234254359Sroberto			return;
234354359Sroberto		}
2344285612Sdelphij		fprintf(fp, "function: %s\n", xcp->comment);
234554359Sroberto		printusage(xcp, fp);
234654359Sroberto	}
234754359Sroberto}
234854359Sroberto
234954359Sroberto
235054359Sroberto/*
235154359Sroberto * helpsort - do hostname qsort comparisons
235254359Sroberto */
235354359Srobertostatic int
235454359Srobertohelpsort(
235554359Sroberto	const void *t1,
235654359Sroberto	const void *t2
235754359Sroberto	)
235854359Sroberto{
2359285612Sdelphij	const char * const *	name1 = t1;
2360285612Sdelphij	const char * const *	name2 = t2;
236154359Sroberto
236254359Sroberto	return strcmp(*name1, *name2);
236354359Sroberto}
236454359Sroberto
236554359Sroberto
236654359Sroberto/*
236754359Sroberto * printusage - print usage information for a command
236854359Sroberto */
236954359Srobertostatic void
237054359Srobertoprintusage(
237154359Sroberto	struct xcmd *xcp,
237254359Sroberto	FILE *fp
237354359Sroberto	)
237454359Sroberto{
237554359Sroberto	register int i;
237654359Sroberto
2377285612Sdelphij	/* XXX: Do we need to warn about extra args here too? */
2378285612Sdelphij
237954359Sroberto	(void) fprintf(fp, "usage: %s", xcp->keyword);
238054359Sroberto	for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
238154359Sroberto		if (xcp->arg[i] & OPT)
238254359Sroberto		    (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
238354359Sroberto		else
238454359Sroberto		    (void) fprintf(fp, " %s", xcp->desc[i]);
238554359Sroberto	}
238654359Sroberto	(void) fprintf(fp, "\n");
238754359Sroberto}
238854359Sroberto
238954359Sroberto
239054359Sroberto/*
239154359Sroberto * timeout - set time out time
239254359Sroberto */
239354359Srobertostatic void
239454359Srobertotimeout(
239554359Sroberto	struct parse *pcmd,
239654359Sroberto	FILE *fp
239754359Sroberto	)
239854359Sroberto{
239954359Sroberto	int val;
240054359Sroberto
240154359Sroberto	if (pcmd->nargs == 0) {
2402285612Sdelphij		val = (int)tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
240354359Sroberto		(void) fprintf(fp, "primary timeout %d ms\n", val);
240454359Sroberto	} else {
240554359Sroberto		tvout.tv_sec = pcmd->argval[0].uval / 1000;
2406285612Sdelphij		tvout.tv_usec = (pcmd->argval[0].uval - ((long)tvout.tv_sec * 1000))
240754359Sroberto			* 1000;
240854359Sroberto	}
240954359Sroberto}
241054359Sroberto
241154359Sroberto
241254359Sroberto/*
241354359Sroberto * auth_delay - set delay for auth requests
241454359Sroberto */
241554359Srobertostatic void
241654359Srobertoauth_delay(
241754359Sroberto	struct parse *pcmd,
241854359Sroberto	FILE *fp
241954359Sroberto	)
242054359Sroberto{
242154359Sroberto	int isneg;
242254359Sroberto	u_long val;
242354359Sroberto
242454359Sroberto	if (pcmd->nargs == 0) {
242554359Sroberto		val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
242654359Sroberto		(void) fprintf(fp, "delay %lu ms\n", val);
242754359Sroberto	} else {
242854359Sroberto		if (pcmd->argval[0].ival < 0) {
242954359Sroberto			isneg = 1;
243054359Sroberto			val = (u_long)(-pcmd->argval[0].ival);
243154359Sroberto		} else {
243254359Sroberto			isneg = 0;
243354359Sroberto			val = (u_long)pcmd->argval[0].ival;
243454359Sroberto		}
243554359Sroberto
243654359Sroberto		delay_time.l_ui = val / 1000;
243754359Sroberto		val %= 1000;
243854359Sroberto		delay_time.l_uf = val * 4294967;	/* 2**32/1000 */
243954359Sroberto
244054359Sroberto		if (isneg)
244154359Sroberto		    L_NEG(&delay_time);
244254359Sroberto	}
244354359Sroberto}
244454359Sroberto
244554359Sroberto
244654359Sroberto/*
244754359Sroberto * host - set the host we are dealing with.
244854359Sroberto */
244954359Srobertostatic void
245054359Srobertohost(
245154359Sroberto	struct parse *pcmd,
245254359Sroberto	FILE *fp
245354359Sroberto	)
245454359Sroberto{
2455132451Sroberto	int i;
2456132451Sroberto
245754359Sroberto	if (pcmd->nargs == 0) {
245854359Sroberto		if (havehost)
2459285612Sdelphij			(void) fprintf(fp, "current host is %s\n",
2460285612Sdelphij					   currenthost);
246154359Sroberto		else
2462285612Sdelphij			(void) fprintf(fp, "no current host\n");
2463132451Sroberto		return;
2464132451Sroberto	}
2465132451Sroberto
2466132451Sroberto	i = 0;
2467132451Sroberto	ai_fam_templ = ai_fam_default;
2468132451Sroberto	if (pcmd->nargs == 2) {
2469132451Sroberto		if (!strcmp("-4", pcmd->argval[i].string))
2470132451Sroberto			ai_fam_templ = AF_INET;
2471132451Sroberto		else if (!strcmp("-6", pcmd->argval[i].string))
2472132451Sroberto			ai_fam_templ = AF_INET6;
2473285612Sdelphij		else
2474285612Sdelphij			goto no_change;
2475132451Sroberto		i = 1;
2476132451Sroberto	}
2477285612Sdelphij	if (openhost(pcmd->argval[i].string, ai_fam_templ)) {
2478285612Sdelphij		fprintf(fp, "current host set to %s\n", currenthost);
247954359Sroberto	} else {
2480285612Sdelphij    no_change:
248154359Sroberto		if (havehost)
2482285612Sdelphij			fprintf(fp, "current host remains %s\n",
2483285612Sdelphij				currenthost);
248454359Sroberto		else
2485285612Sdelphij			fprintf(fp, "still no current host\n");
248654359Sroberto	}
248754359Sroberto}
248854359Sroberto
248954359Sroberto
249054359Sroberto/*
249154359Sroberto * poll - do one (or more) polls of the host via NTP
249254359Sroberto */
249354359Sroberto/*ARGSUSED*/
249454359Srobertostatic void
249554359Srobertontp_poll(
249654359Sroberto	struct parse *pcmd,
249754359Sroberto	FILE *fp
249854359Sroberto	)
249954359Sroberto{
250054359Sroberto	(void) fprintf(fp, "poll not implemented yet\n");
250154359Sroberto}
250254359Sroberto
250354359Sroberto
250454359Sroberto/*
2505298770Sdelphij * showdrefid2str - return a string explanation of the value of drefid
2506298770Sdelphij */
2507298770Sdelphijstatic char *
2508298770Sdelphijshowdrefid2str(void)
2509298770Sdelphij{
2510298770Sdelphij	switch (drefid) {
2511298770Sdelphij	    case REFID_HASH:
2512298770Sdelphij	    	return "hash";
2513298770Sdelphij	    case REFID_IPV4:
2514298770Sdelphij	    	return "ipv4";
2515298770Sdelphij	    default:
2516298770Sdelphij	    	return "Unknown";
2517298770Sdelphij	}
2518298770Sdelphij}
2519298770Sdelphij
2520298770Sdelphij
2521298770Sdelphij/*
2522298770Sdelphij * drefid - display/change "display hash"
2523298770Sdelphij */
2524298770Sdelphijstatic void
2525298770Sdelphijshowdrefid(
2526298770Sdelphij	struct parse *pcmd,
2527298770Sdelphij	FILE *fp
2528298770Sdelphij	)
2529298770Sdelphij{
2530298770Sdelphij	if (pcmd->nargs == 0) {
2531298770Sdelphij		(void) fprintf(fp, "drefid value is %s\n", showdrefid2str());
2532298770Sdelphij		return;
2533298770Sdelphij	} else if (STREQ(pcmd->argval[0].string, "hash")) {
2534298770Sdelphij		drefid = REFID_HASH;
2535298770Sdelphij	} else if (STREQ(pcmd->argval[0].string, "ipv4")) {
2536298770Sdelphij		drefid = REFID_IPV4;
2537298770Sdelphij	} else {
2538298770Sdelphij		(void) fprintf(fp, "What?\n");
2539298770Sdelphij		return;
2540298770Sdelphij	}
2541298770Sdelphij	(void) fprintf(fp, "drefid value set to %s\n", showdrefid2str());
2542298770Sdelphij}
2543298770Sdelphij
2544298770Sdelphij
2545298770Sdelphij/*
254654359Sroberto * keyid - get a keyid to use for authenticating requests
254754359Sroberto */
254854359Srobertostatic void
254954359Srobertokeyid(
255054359Sroberto	struct parse *pcmd,
255154359Sroberto	FILE *fp
255254359Sroberto	)
255354359Sroberto{
255454359Sroberto	if (pcmd->nargs == 0) {
2555132451Sroberto		if (info_auth_keyid == 0)
255654359Sroberto		    (void) fprintf(fp, "no keyid defined\n");
255754359Sroberto		else
255854359Sroberto		    (void) fprintf(fp, "keyid is %lu\n", (u_long)info_auth_keyid);
255954359Sroberto	} else {
2560132451Sroberto		/* allow zero so that keyid can be cleared. */
2561132451Sroberto		if(pcmd->argval[0].uval > NTP_MAXKEY)
2562132451Sroberto		    (void) fprintf(fp, "Invalid key identifier\n");
256354359Sroberto		info_auth_keyid = pcmd->argval[0].uval;
256454359Sroberto	}
256554359Sroberto}
256654359Sroberto
256754359Sroberto/*
256854359Sroberto * keytype - get type of key to use for authenticating requests
256954359Sroberto */
257054359Srobertostatic void
257154359Srobertokeytype(
257254359Sroberto	struct parse *pcmd,
257354359Sroberto	FILE *fp
257454359Sroberto	)
257554359Sroberto{
2576285612Sdelphij	const char *	digest_name;
2577285612Sdelphij	size_t		digest_len;
2578285612Sdelphij	int		key_type;
257954359Sroberto
2580285612Sdelphij	if (!pcmd->nargs) {
2581285612Sdelphij		fprintf(fp, "keytype is %s with %lu octet digests\n",
2582285612Sdelphij			keytype_name(info_auth_keytype),
2583285612Sdelphij			(u_long)info_auth_hashlen);
2584285612Sdelphij		return;
2585285612Sdelphij	}
2586285612Sdelphij
2587285612Sdelphij	digest_name = pcmd->argval[0].string;
2588285612Sdelphij	digest_len = 0;
2589285612Sdelphij	key_type = keytype_from_text(digest_name, &digest_len);
2590285612Sdelphij
2591285612Sdelphij	if (!key_type) {
2592285612Sdelphij		fprintf(fp, "keytype is not valid. "
2593285612Sdelphij#ifdef OPENSSL
2594285612Sdelphij			"Type \"help keytype\" for the available digest types.\n");
2595285612Sdelphij#else
2596285612Sdelphij			"Only \"md5\" is available.\n");
2597285612Sdelphij#endif
2598285612Sdelphij		return;
2599285612Sdelphij	}
2600285612Sdelphij
2601285612Sdelphij	info_auth_keytype = key_type;
2602285612Sdelphij	info_auth_hashlen = digest_len;
260354359Sroberto}
260454359Sroberto
260554359Sroberto
260654359Sroberto/*
260754359Sroberto * passwd - get an authentication key
260854359Sroberto */
260954359Sroberto/*ARGSUSED*/
261054359Srobertostatic void
261154359Srobertopasswd(
261254359Sroberto	struct parse *pcmd,
261354359Sroberto	FILE *fp
261454359Sroberto	)
261554359Sroberto{
2616285612Sdelphij	const char *pass;
261754359Sroberto
2618132451Sroberto	if (info_auth_keyid == 0) {
2619285612Sdelphij		info_auth_keyid = getkeyid("Keyid: ");
2620285612Sdelphij		if (info_auth_keyid == 0) {
2621285612Sdelphij			(void)fprintf(fp, "Keyid must be defined\n");
262254359Sroberto			return;
262354359Sroberto		}
262454359Sroberto	}
2625285612Sdelphij	if (pcmd->nargs >= 1)
2626285612Sdelphij		pass = pcmd->argval[0].string;
2627132451Sroberto	else {
2628285612Sdelphij		pass = getpass_keytype(info_auth_keytype);
2629285612Sdelphij		if ('\0' == pass[0]) {
2630285612Sdelphij			fprintf(fp, "Password unchanged\n");
2631285612Sdelphij			return;
2632285612Sdelphij		}
2633132451Sroberto	}
2634285612Sdelphij	authusekey(info_auth_keyid, info_auth_keytype,
2635285612Sdelphij		   (const u_char *)pass);
2636285612Sdelphij	authtrust(info_auth_keyid, 1);
263754359Sroberto}
263854359Sroberto
263954359Sroberto
264054359Sroberto/*
264154359Sroberto * hostnames - set the showhostnames flag
264254359Sroberto */
264354359Srobertostatic void
264454359Srobertohostnames(
264554359Sroberto	struct parse *pcmd,
264654359Sroberto	FILE *fp
264754359Sroberto	)
264854359Sroberto{
264954359Sroberto	if (pcmd->nargs == 0) {
265054359Sroberto		if (showhostnames)
265154359Sroberto		    (void) fprintf(fp, "hostnames being shown\n");
265254359Sroberto		else
265354359Sroberto		    (void) fprintf(fp, "hostnames not being shown\n");
265454359Sroberto	} else {
265554359Sroberto		if (STREQ(pcmd->argval[0].string, "yes"))
265654359Sroberto		    showhostnames = 1;
265754359Sroberto		else if (STREQ(pcmd->argval[0].string, "no"))
265854359Sroberto		    showhostnames = 0;
265954359Sroberto		else
266054359Sroberto		    (void)fprintf(stderr, "What?\n");
266154359Sroberto	}
266254359Sroberto}
266354359Sroberto
266454359Sroberto
266554359Sroberto
266654359Sroberto/*
266754359Sroberto * setdebug - set/change debugging level
266854359Sroberto */
266954359Srobertostatic void
267054359Srobertosetdebug(
267154359Sroberto	struct parse *pcmd,
267254359Sroberto	FILE *fp
267354359Sroberto	)
267454359Sroberto{
267554359Sroberto	if (pcmd->nargs == 0) {
267654359Sroberto		(void) fprintf(fp, "debug level is %d\n", debug);
267754359Sroberto		return;
267854359Sroberto	} else if (STREQ(pcmd->argval[0].string, "no")) {
267954359Sroberto		debug = 0;
268054359Sroberto	} else if (STREQ(pcmd->argval[0].string, "more")) {
268154359Sroberto		debug++;
268254359Sroberto	} else if (STREQ(pcmd->argval[0].string, "less")) {
268354359Sroberto		debug--;
268454359Sroberto	} else {
268554359Sroberto		(void) fprintf(fp, "What?\n");
268654359Sroberto		return;
268754359Sroberto	}
268854359Sroberto	(void) fprintf(fp, "debug level set to %d\n", debug);
268954359Sroberto}
269054359Sroberto
269154359Sroberto
269254359Sroberto/*
269354359Sroberto * quit - stop this nonsense
269454359Sroberto */
269554359Sroberto/*ARGSUSED*/
269654359Srobertostatic void
269754359Srobertoquit(
269854359Sroberto	struct parse *pcmd,
269954359Sroberto	FILE *fp
270054359Sroberto	)
270154359Sroberto{
270254359Sroberto	if (havehost)
270354359Sroberto	    closesocket(sockfd);	/* cleanliness next to godliness */
270454359Sroberto	exit(0);
270554359Sroberto}
270654359Sroberto
270754359Sroberto
270854359Sroberto/*
270954359Sroberto * version - print the current version number
271054359Sroberto */
271154359Sroberto/*ARGSUSED*/
271254359Srobertostatic void
271354359Srobertoversion(
271454359Sroberto	struct parse *pcmd,
271554359Sroberto	FILE *fp
271654359Sroberto	)
271754359Sroberto{
271854359Sroberto
271954359Sroberto	(void) fprintf(fp, "%s\n", Version);
272054359Sroberto	return;
272154359Sroberto}
272254359Sroberto
272354359Sroberto
272454359Sroberto/*
272554359Sroberto * raw - set raw mode output
272654359Sroberto */
272754359Sroberto/*ARGSUSED*/
272854359Srobertostatic void
272954359Srobertoraw(
273054359Sroberto	struct parse *pcmd,
273154359Sroberto	FILE *fp
273254359Sroberto	)
273354359Sroberto{
273454359Sroberto	rawmode = 1;
273554359Sroberto	(void) fprintf(fp, "Output set to raw\n");
273654359Sroberto}
273754359Sroberto
273854359Sroberto
273954359Sroberto/*
274054359Sroberto * cooked - set cooked mode output
274154359Sroberto */
274254359Sroberto/*ARGSUSED*/
274354359Srobertostatic void
274454359Srobertocooked(
274554359Sroberto	struct parse *pcmd,
274654359Sroberto	FILE *fp
274754359Sroberto	)
274854359Sroberto{
274954359Sroberto	rawmode = 0;
275054359Sroberto	(void) fprintf(fp, "Output set to cooked\n");
275154359Sroberto	return;
275254359Sroberto}
275354359Sroberto
275454359Sroberto
275554359Sroberto/*
275654359Sroberto * authenticate - always authenticate requests to this host
275754359Sroberto */
275854359Srobertostatic void
275954359Srobertoauthenticate(
276054359Sroberto	struct parse *pcmd,
276154359Sroberto	FILE *fp
276254359Sroberto	)
276354359Sroberto{
276454359Sroberto	if (pcmd->nargs == 0) {
276554359Sroberto		if (always_auth) {
276654359Sroberto			(void) fprintf(fp,
276754359Sroberto				       "authenticated requests being sent\n");
276854359Sroberto		} else
276954359Sroberto		    (void) fprintf(fp,
277054359Sroberto				   "unauthenticated requests being sent\n");
277154359Sroberto	} else {
277254359Sroberto		if (STREQ(pcmd->argval[0].string, "yes")) {
277354359Sroberto			always_auth = 1;
277454359Sroberto		} else if (STREQ(pcmd->argval[0].string, "no")) {
277554359Sroberto			always_auth = 0;
277654359Sroberto		} else
277754359Sroberto		    (void)fprintf(stderr, "What?\n");
277854359Sroberto	}
277954359Sroberto}
278054359Sroberto
278154359Sroberto
278254359Sroberto/*
278354359Sroberto * ntpversion - choose the NTP version to use
278454359Sroberto */
278554359Srobertostatic void
278654359Srobertontpversion(
278754359Sroberto	struct parse *pcmd,
278854359Sroberto	FILE *fp
278954359Sroberto	)
279054359Sroberto{
279154359Sroberto	if (pcmd->nargs == 0) {
279254359Sroberto		(void) fprintf(fp,
279354359Sroberto			       "NTP version being claimed is %d\n", pktversion);
279454359Sroberto	} else {
279554359Sroberto		if (pcmd->argval[0].uval < NTP_OLDVERSION
279654359Sroberto		    || pcmd->argval[0].uval > NTP_VERSION) {
279754359Sroberto			(void) fprintf(stderr, "versions %d to %d, please\n",
279854359Sroberto				       NTP_OLDVERSION, NTP_VERSION);
279954359Sroberto		} else {
280054359Sroberto			pktversion = (u_char) pcmd->argval[0].uval;
280154359Sroberto		}
280254359Sroberto	}
280354359Sroberto}
280454359Sroberto
280554359Sroberto
2806285612Sdelphijstatic void __attribute__((__format__(__printf__, 1, 0)))
2807285612Sdelphijvwarning(const char *fmt, va_list ap)
2808285612Sdelphij{
2809285612Sdelphij	int serrno = errno;
2810285612Sdelphij	(void) fprintf(stderr, "%s: ", progname);
2811285612Sdelphij	vfprintf(stderr, fmt, ap);
2812293650Sglebius	(void) fprintf(stderr, ": %s\n", strerror(serrno));
2813285612Sdelphij}
2814285612Sdelphij
281554359Sroberto/*
281654359Sroberto * warning - print a warning message
281754359Sroberto */
2818285612Sdelphijstatic void __attribute__((__format__(__printf__, 1, 2)))
281954359Srobertowarning(
282054359Sroberto	const char *fmt,
2821285612Sdelphij	...
282254359Sroberto	)
282354359Sroberto{
2824285612Sdelphij	va_list ap;
2825285612Sdelphij	va_start(ap, fmt);
2826285612Sdelphij	vwarning(fmt, ap);
2827285612Sdelphij	va_end(ap);
282854359Sroberto}
282954359Sroberto
283054359Sroberto
283154359Sroberto/*
283254359Sroberto * error - print a message and exit
283354359Sroberto */
2834285612Sdelphijstatic void __attribute__((__format__(__printf__, 1, 2)))
283554359Srobertoerror(
283654359Sroberto	const char *fmt,
2837285612Sdelphij	...
283854359Sroberto	)
283954359Sroberto{
2840285612Sdelphij	va_list ap;
2841285612Sdelphij	va_start(ap, fmt);
2842285612Sdelphij	vwarning(fmt, ap);
2843285612Sdelphij	va_end(ap);
284454359Sroberto	exit(1);
284554359Sroberto}
284654359Sroberto/*
284754359Sroberto * getkeyid - prompt the user for a keyid to use
284854359Sroberto */
284954359Srobertostatic u_long
285054359Srobertogetkeyid(
285154359Sroberto	const char *keyprompt
285254359Sroberto	)
285354359Sroberto{
2854285612Sdelphij	int c;
285554359Sroberto	FILE *fi;
285654359Sroberto	char pbuf[20];
2857285612Sdelphij	size_t i;
2858285612Sdelphij	size_t ilim;
285954359Sroberto
286054359Sroberto#ifndef SYS_WINNT
286154359Sroberto	if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
286254359Sroberto#else
2863285612Sdelphij	if ((fi = _fdopen(open("CONIN$", _O_TEXT), "r")) == NULL)
286454359Sroberto#endif /* SYS_WINNT */
286554359Sroberto		fi = stdin;
2866285612Sdelphij	else
286754359Sroberto		setbuf(fi, (char *)NULL);
286854359Sroberto	fprintf(stderr, "%s", keyprompt); fflush(stderr);
2869285612Sdelphij	for (i = 0, ilim = COUNTOF(pbuf) - 1;
2870285612Sdelphij	     i < ilim && (c = getc(fi)) != '\n' && c != EOF;
2871285612Sdelphij	     )
2872285612Sdelphij		pbuf[i++] = (char)c;
2873285612Sdelphij	pbuf[i] = '\0';
287454359Sroberto	if (fi != stdin)
2875285612Sdelphij		fclose(fi);
287654359Sroberto
287754359Sroberto	return (u_long) atoi(pbuf);
287854359Sroberto}
287954359Sroberto
288054359Sroberto
288154359Sroberto/*
288254359Sroberto * atoascii - printable-ize possibly ascii data using the character
288354359Sroberto *	      transformations cat -v uses.
288454359Sroberto */
288554359Srobertostatic void
288654359Srobertoatoascii(
2887285612Sdelphij	const char *in,
2888285612Sdelphij	size_t in_octets,
2889285612Sdelphij	char *out,
2890285612Sdelphij	size_t out_octets
289154359Sroberto	)
289254359Sroberto{
2893285612Sdelphij	const u_char *	pchIn;
2894285612Sdelphij	const u_char *	pchInLimit;
2895285612Sdelphij	u_char *	pchOut;
2896285612Sdelphij	u_char		c;
289754359Sroberto
2898285612Sdelphij	pchIn = (const u_char *)in;
2899285612Sdelphij	pchInLimit = pchIn + in_octets;
2900285612Sdelphij	pchOut = (u_char *)out;
2901285612Sdelphij
2902285612Sdelphij	if (NULL == pchIn) {
2903285612Sdelphij		if (0 < out_octets)
2904285612Sdelphij			*pchOut = '\0';
290554359Sroberto		return;
290654359Sroberto	}
290754359Sroberto
2908285612Sdelphij#define	ONEOUT(c)					\
2909285612Sdelphijdo {							\
2910285612Sdelphij	if (0 == --out_octets) {			\
2911285612Sdelphij		*pchOut = '\0';				\
2912285612Sdelphij		return;					\
2913285612Sdelphij	}						\
2914285612Sdelphij	*pchOut++ = (c);				\
2915285612Sdelphij} while (0)
2916285612Sdelphij
2917285612Sdelphij	for (	; pchIn < pchInLimit; pchIn++) {
2918285612Sdelphij		c = *pchIn;
2919285612Sdelphij		if ('\0' == c)
2920285612Sdelphij			break;
2921285612Sdelphij		if (c & 0x80) {
2922285612Sdelphij			ONEOUT('M');
2923285612Sdelphij			ONEOUT('-');
2924285612Sdelphij			c &= 0x7f;
292554359Sroberto		}
292654359Sroberto		if (c < ' ') {
2927285612Sdelphij			ONEOUT('^');
2928285612Sdelphij			ONEOUT((u_char)(c + '@'));
2929285612Sdelphij		} else if (0x7f == c) {
2930285612Sdelphij			ONEOUT('^');
2931285612Sdelphij			ONEOUT('?');
2932285612Sdelphij		} else
2933285612Sdelphij			ONEOUT(c);
293454359Sroberto	}
2935285612Sdelphij	ONEOUT('\0');
2936285612Sdelphij
2937285612Sdelphij#undef ONEOUT
293854359Sroberto}
293954359Sroberto
294054359Sroberto
294154359Sroberto/*
294254359Sroberto * makeascii - print possibly ascii data using the character
294354359Sroberto *	       transformations that cat -v uses.
294454359Sroberto */
2945285612Sdelphijvoid
294654359Srobertomakeascii(
2947293650Sglebius	size_t length,
2948285612Sdelphij	const char *data,
294954359Sroberto	FILE *fp
295054359Sroberto	)
295154359Sroberto{
2952285612Sdelphij	const u_char *data_u_char;
2953285612Sdelphij	const u_char *cp;
2954285612Sdelphij	int c;
295554359Sroberto
2956285612Sdelphij	data_u_char = (const u_char *)data;
2957285612Sdelphij
2958285612Sdelphij	for (cp = data_u_char; cp < data_u_char + length; cp++) {
295954359Sroberto		c = (int)*cp;
2960285612Sdelphij		if (c & 0x80) {
296154359Sroberto			putc('M', fp);
296254359Sroberto			putc('-', fp);
2963285612Sdelphij			c &= 0x7f;
296454359Sroberto		}
296554359Sroberto
296654359Sroberto		if (c < ' ') {
296754359Sroberto			putc('^', fp);
2968285612Sdelphij			putc(c + '@', fp);
2969285612Sdelphij		} else if (0x7f == c) {
297054359Sroberto			putc('^', fp);
297154359Sroberto			putc('?', fp);
2972285612Sdelphij		} else
297354359Sroberto			putc(c, fp);
297454359Sroberto	}
297554359Sroberto}
297654359Sroberto
297754359Sroberto
297854359Sroberto/*
297954359Sroberto * asciize - same thing as makeascii except add a newline
298054359Sroberto */
298154359Srobertovoid
298254359Srobertoasciize(
298354359Sroberto	int length,
298454359Sroberto	char *data,
298554359Sroberto	FILE *fp
298654359Sroberto	)
298754359Sroberto{
298854359Sroberto	makeascii(length, data, fp);
298954359Sroberto	putc('\n', fp);
299054359Sroberto}
299154359Sroberto
299254359Sroberto
299354359Sroberto/*
2994285612Sdelphij * truncate string to fit clipping excess at end.
2995285612Sdelphij *	"too long"	->	"too l"
2996285612Sdelphij * Used for hostnames.
2997285612Sdelphij */
2998285612Sdelphijconst char *
2999285612Sdelphijtrunc_right(
3000285612Sdelphij	const char *	src,
3001285612Sdelphij	size_t		width
3002285612Sdelphij	)
3003285612Sdelphij{
3004285612Sdelphij	size_t	sl;
3005285612Sdelphij	char *	out;
3006285612Sdelphij
3007285612Sdelphij
3008285612Sdelphij	sl = strlen(src);
3009285612Sdelphij	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 0) {
3010285612Sdelphij		LIB_GETBUF(out);
3011285612Sdelphij		memcpy(out, src, width);
3012285612Sdelphij		out[width] = '\0';
3013285612Sdelphij
3014285612Sdelphij		return out;
3015285612Sdelphij	}
3016285612Sdelphij
3017285612Sdelphij	return src;
3018285612Sdelphij}
3019285612Sdelphij
3020285612Sdelphij
3021285612Sdelphij/*
3022285612Sdelphij * truncate string to fit by preserving right side and using '_' to hint
3023285612Sdelphij *	"too long"	->	"_long"
3024285612Sdelphij * Used for local IPv6 addresses, where low bits differentiate.
3025285612Sdelphij */
3026285612Sdelphijconst char *
3027285612Sdelphijtrunc_left(
3028285612Sdelphij	const char *	src,
3029285612Sdelphij	size_t		width
3030285612Sdelphij	)
3031285612Sdelphij{
3032285612Sdelphij	size_t	sl;
3033285612Sdelphij	char *	out;
3034285612Sdelphij
3035285612Sdelphij
3036285612Sdelphij	sl = strlen(src);
3037285612Sdelphij	if (sl > width && LIB_BUFLENGTH - 1 > width && width > 1) {
3038285612Sdelphij		LIB_GETBUF(out);
3039285612Sdelphij		out[0] = '_';
3040285612Sdelphij		memcpy(&out[1], &src[sl + 1 - width], width);
3041285612Sdelphij
3042285612Sdelphij		return out;
3043285612Sdelphij	}
3044285612Sdelphij
3045285612Sdelphij	return src;
3046285612Sdelphij}
3047285612Sdelphij
3048285612Sdelphij
3049285612Sdelphij/*
305054359Sroberto * Some circular buffer space
305154359Sroberto */
305254359Sroberto#define	CBLEN	80
305354359Sroberto#define	NUMCB	6
305454359Sroberto
305554359Srobertochar circ_buf[NUMCB][CBLEN];
305654359Srobertoint nextcb = 0;
305754359Sroberto
305854359Sroberto/*
305954359Sroberto * nextvar - find the next variable in the buffer
306054359Sroberto */
306154359Srobertoint
306254359Srobertonextvar(
3063293650Sglebius	size_t *datalen,
3064285612Sdelphij	const char **datap,
306554359Sroberto	char **vname,
306654359Sroberto	char **vvalue
306754359Sroberto	)
306854359Sroberto{
3069285612Sdelphij	const char *cp;
3070285612Sdelphij	const char *np;
3071285612Sdelphij	const char *cpend;
3072285612Sdelphij	size_t srclen;
3073285612Sdelphij	size_t len;
307454359Sroberto	static char name[MAXVARLEN];
307554359Sroberto	static char value[MAXVALLEN];
307654359Sroberto
307754359Sroberto	cp = *datap;
307854359Sroberto	cpend = cp + *datalen;
307954359Sroberto
308054359Sroberto	/*
308154359Sroberto	 * Space past commas and white space
308254359Sroberto	 */
3083330567Sgordon	while (cp < cpend && (*cp == ',' || isspace(pgetc(cp))))
3084285612Sdelphij		cp++;
3085285612Sdelphij	if (cp >= cpend)
3086285612Sdelphij		return 0;
3087285612Sdelphij
308854359Sroberto	/*
308954359Sroberto	 * Copy name until we hit a ',', an '=', a '\r' or a '\n'.  Backspace
309054359Sroberto	 * over any white space and terminate it.
309154359Sroberto	 */
3092285612Sdelphij	srclen = strcspn(cp, ",=\r\n");
3093285612Sdelphij	srclen = min(srclen, (size_t)(cpend - cp));
3094285612Sdelphij	len = srclen;
3095330567Sgordon	while (len > 0 && isspace(pgetc(&cp[len - 1])))
3096285612Sdelphij		len--;
3097294569Sdelphij	if (len >= sizeof(name))
3098294569Sdelphij	    return 0;
3099285612Sdelphij	if (len > 0)
3100285612Sdelphij		memcpy(name, cp, len);
3101285612Sdelphij	name[len] = '\0';
310254359Sroberto	*vname = name;
3103285612Sdelphij	cp += srclen;
310454359Sroberto
310554359Sroberto	/*
310654359Sroberto	 * Check if we hit the end of the buffer or a ','.  If so we are done.
310754359Sroberto	 */
3108285612Sdelphij	if (cp >= cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
3109285612Sdelphij		if (cp < cpend)
3110285612Sdelphij			cp++;
311154359Sroberto		*datap = cp;
3112293650Sglebius		*datalen = size2int_sat(cpend - cp);
3113285612Sdelphij		*vvalue = NULL;
311454359Sroberto		return 1;
311554359Sroberto	}
311654359Sroberto
311754359Sroberto	/*
311854359Sroberto	 * So far, so good.  Copy out the value
311954359Sroberto	 */
312054359Sroberto	cp++;	/* past '=' */
3121330567Sgordon	while (cp < cpend && (isspace(pgetc(cp)) && *cp != '\r' && *cp != '\n'))
3122285612Sdelphij		cp++;
3123285612Sdelphij	np = cp;
3124285612Sdelphij	if ('"' == *np) {
3125285612Sdelphij		do {
3126285612Sdelphij			np++;
3127285612Sdelphij		} while (np < cpend && '"' != *np);
3128285612Sdelphij		if (np < cpend && '"' == *np)
3129285612Sdelphij			np++;
3130285612Sdelphij	} else {
3131285612Sdelphij		while (np < cpend && ',' != *np && '\r' != *np)
3132285612Sdelphij			np++;
313354359Sroberto	}
3134285612Sdelphij	len = np - cp;
3135285612Sdelphij	if (np > cpend || len >= sizeof(value) ||
3136285612Sdelphij	    (np < cpend && ',' != *np && '\r' != *np))
3137285612Sdelphij		return 0;
3138285612Sdelphij	memcpy(value, cp, len);
313954359Sroberto	/*
314054359Sroberto	 * Trim off any trailing whitespace
314154359Sroberto	 */
3142330567Sgordon	while (len > 0 && isspace(pgetc(&value[len - 1])))
3143285612Sdelphij		len--;
3144285612Sdelphij	value[len] = '\0';
314554359Sroberto
314654359Sroberto	/*
314754359Sroberto	 * Return this.  All done.
314854359Sroberto	 */
3149285612Sdelphij	if (np < cpend && ',' == *np)
3150285612Sdelphij		np++;
3151285612Sdelphij	*datap = np;
3152293650Sglebius	*datalen = size2int_sat(cpend - np);
315354359Sroberto	*vvalue = value;
315454359Sroberto	return 1;
315554359Sroberto}
315654359Sroberto
315754359Sroberto
3158285612Sdelphiju_short
3159285612Sdelphijvarfmt(const char * varname)
316054359Sroberto{
3161285612Sdelphij	u_int n;
316254359Sroberto
3163285612Sdelphij	for (n = 0; n < COUNTOF(cookedvars); n++)
3164285612Sdelphij		if (!strcmp(varname, cookedvars[n].varname))
3165285612Sdelphij			return cookedvars[n].fmt;
3166285612Sdelphij
3167285612Sdelphij	return PADDING;
316854359Sroberto}
316954359Sroberto
317054359Sroberto
317154359Sroberto/*
317254359Sroberto * printvars - print variables returned in response packet
317354359Sroberto */
317454359Srobertovoid
317554359Srobertoprintvars(
3176293650Sglebius	size_t length,
3177285612Sdelphij	const char *data,
317854359Sroberto	int status,
317954359Sroberto	int sttype,
3180285612Sdelphij	int quiet,
318154359Sroberto	FILE *fp
318254359Sroberto	)
318354359Sroberto{
318454359Sroberto	if (rawmode)
3185285612Sdelphij	    rawprint(sttype, length, data, status, quiet, fp);
318654359Sroberto	else
3187285612Sdelphij	    cookedprint(sttype, length, data, status, quiet, fp);
318854359Sroberto}
318954359Sroberto
319054359Sroberto
319154359Sroberto/*
319254359Sroberto * rawprint - do a printout of the data in raw mode
319354359Sroberto */
319454359Srobertostatic void
319554359Srobertorawprint(
319654359Sroberto	int datatype,
3197293650Sglebius	size_t length,
3198285612Sdelphij	const char *data,
319954359Sroberto	int status,
3200285612Sdelphij	int quiet,
320154359Sroberto	FILE *fp
320254359Sroberto	)
320354359Sroberto{
3204285612Sdelphij	const char *cp;
3205285612Sdelphij	const char *cpend;
320654359Sroberto
320754359Sroberto	/*
320854359Sroberto	 * Essentially print the data as is.  We reformat unprintables, though.
320954359Sroberto	 */
321054359Sroberto	cp = data;
321154359Sroberto	cpend = data + length;
321254359Sroberto
3213285612Sdelphij	if (!quiet)
3214285612Sdelphij		(void) fprintf(fp, "status=0x%04x,\n", status);
321554359Sroberto
321654359Sroberto	while (cp < cpend) {
321754359Sroberto		if (*cp == '\r') {
321854359Sroberto			/*
321954359Sroberto			 * If this is a \r and the next character is a
322054359Sroberto			 * \n, supress this, else pretty print it.  Otherwise
322154359Sroberto			 * just output the character.
322254359Sroberto			 */
3223285612Sdelphij			if (cp == (cpend - 1) || *(cp + 1) != '\n')
322454359Sroberto			    makeascii(1, cp, fp);
3225330567Sgordon		} else if (isspace(pgetc(cp)) || isprint(pgetc(cp)))
322654359Sroberto			putc(*cp, fp);
3227285612Sdelphij		else
322854359Sroberto			makeascii(1, cp, fp);
322954359Sroberto		cp++;
323054359Sroberto	}
323154359Sroberto}
323254359Sroberto
323354359Sroberto
323454359Sroberto/*
323554359Sroberto * Global data used by the cooked output routines
323654359Sroberto */
323754359Srobertoint out_chars;		/* number of characters output */
323854359Srobertoint out_linecount;	/* number of characters output on this line */
323954359Sroberto
324054359Sroberto
324154359Sroberto/*
324254359Sroberto * startoutput - get ready to do cooked output
324354359Sroberto */
324454359Srobertostatic void
324554359Srobertostartoutput(void)
324654359Sroberto{
324754359Sroberto	out_chars = 0;
324854359Sroberto	out_linecount = 0;
324954359Sroberto}
325054359Sroberto
325154359Sroberto
325254359Sroberto/*
325354359Sroberto * output - output a variable=value combination
325454359Sroberto */
325554359Srobertostatic void
325654359Srobertooutput(
325754359Sroberto	FILE *fp,
3258285612Sdelphij	const char *name,
3259285612Sdelphij	const char *value
326054359Sroberto	)
326154359Sroberto{
3262293650Sglebius	int len;
326354359Sroberto
3264285612Sdelphij	/* strlen of "name=value" */
3265293650Sglebius	len = size2int_sat(strlen(name) + 1 + strlen(value));
326654359Sroberto
326754359Sroberto	if (out_chars != 0) {
3268285612Sdelphij		out_chars += 2;
3269285612Sdelphij		if ((out_linecount + len + 2) > MAXOUTLINE) {
3270285612Sdelphij			fputs(",\n", fp);
327154359Sroberto			out_linecount = 0;
327254359Sroberto		} else {
3273285612Sdelphij			fputs(", ", fp);
3274285612Sdelphij			out_linecount += 2;
327554359Sroberto		}
327654359Sroberto	}
327754359Sroberto
327854359Sroberto	fputs(name, fp);
327954359Sroberto	putc('=', fp);
328054359Sroberto	fputs(value, fp);
3281285612Sdelphij	out_chars += len;
3282285612Sdelphij	out_linecount += len;
328354359Sroberto}
328454359Sroberto
328554359Sroberto
328654359Sroberto/*
328754359Sroberto * endoutput - terminate a block of cooked output
328854359Sroberto */
328954359Srobertostatic void
329054359Srobertoendoutput(
329154359Sroberto	FILE *fp
329254359Sroberto	)
329354359Sroberto{
329454359Sroberto	if (out_chars != 0)
3295285612Sdelphij		putc('\n', fp);
329654359Sroberto}
329754359Sroberto
329854359Sroberto
329954359Sroberto/*
330054359Sroberto * outputarr - output an array of values
330154359Sroberto */
330254359Srobertostatic void
330354359Srobertooutputarr(
330454359Sroberto	FILE *fp,
330554359Sroberto	char *name,
330654359Sroberto	int narr,
330754359Sroberto	l_fp *lfp
330854359Sroberto	)
330954359Sroberto{
3310293650Sglebius	char *bp;
3311293650Sglebius	char *cp;
3312293650Sglebius	size_t i;
3313293650Sglebius	size_t len;
331454359Sroberto	char buf[256];
331554359Sroberto
331654359Sroberto	bp = buf;
331754359Sroberto	/*
331854359Sroberto	 * Hack to align delay and offset values
331954359Sroberto	 */
332054359Sroberto	for (i = (int)strlen(name); i < 11; i++)
332154359Sroberto	    *bp++ = ' ';
3322285612Sdelphij
332354359Sroberto	for (i = narr; i > 0; i--) {
332454359Sroberto		if (i != narr)
332554359Sroberto		    *bp++ = ' ';
332654359Sroberto		cp = lfptoms(lfp, 2);
332754359Sroberto		len = strlen(cp);
332854359Sroberto		if (len > 7) {
332954359Sroberto			cp[7] = '\0';
333054359Sroberto			len = 7;
333154359Sroberto		}
333254359Sroberto		while (len < 7) {
333354359Sroberto			*bp++ = ' ';
333454359Sroberto			len++;
333554359Sroberto		}
333654359Sroberto		while (*cp != '\0')
333754359Sroberto		    *bp++ = *cp++;
333854359Sroberto		lfp++;
333954359Sroberto	}
334054359Sroberto	*bp = '\0';
334154359Sroberto	output(fp, name, buf);
334254359Sroberto}
334354359Sroberto
334454359Srobertostatic char *
334554359Srobertotstflags(
334654359Sroberto	u_long val
334754359Sroberto	)
334854359Sroberto{
3349285612Sdelphij	register char *cp, *s;
3350285612Sdelphij	size_t cb;
335154359Sroberto	register int i;
335254359Sroberto	register const char *sep;
335354359Sroberto
335454359Sroberto	sep = "";
3355285612Sdelphij	s = cp = circ_buf[nextcb];
335654359Sroberto	if (++nextcb >= NUMCB)
3357285612Sdelphij		nextcb = 0;
3358285612Sdelphij	cb = sizeof(circ_buf[0]);
335954359Sroberto
3360285612Sdelphij	snprintf(cp, cb, "%02lx", val);
3361285612Sdelphij	cp += strlen(cp);
3362285612Sdelphij	cb -= strlen(cp);
336354359Sroberto	if (!val) {
3364285612Sdelphij		strlcat(cp, " ok", cb);
3365285612Sdelphij		cp += strlen(cp);
3366285612Sdelphij		cb -= strlen(cp);
336754359Sroberto	} else {
3368285612Sdelphij		if (cb) {
3369285612Sdelphij			*cp++ = ' ';
3370285612Sdelphij			cb--;
3371285612Sdelphij		}
3372285612Sdelphij		for (i = 0; i < (int)COUNTOF(tstflagnames); i++) {
337354359Sroberto			if (val & 0x1) {
3374285612Sdelphij				snprintf(cp, cb, "%s%s", sep,
3375285612Sdelphij					 tstflagnames[i]);
337654359Sroberto				sep = ", ";
3377285612Sdelphij				cp += strlen(cp);
3378285612Sdelphij				cb -= strlen(cp);
337954359Sroberto			}
338054359Sroberto			val >>= 1;
338154359Sroberto		}
338254359Sroberto	}
3383285612Sdelphij	if (cb)
3384285612Sdelphij		*cp = '\0';
3385285612Sdelphij
338654359Sroberto	return s;
338754359Sroberto}
338854359Sroberto
338954359Sroberto/*
339054359Sroberto * cookedprint - output variables in cooked mode
339154359Sroberto */
339254359Srobertostatic void
339354359Srobertocookedprint(
339454359Sroberto	int datatype,
3395293650Sglebius	size_t length,
3396285612Sdelphij	const char *data,
339754359Sroberto	int status,
3398285612Sdelphij	int quiet,
339954359Sroberto	FILE *fp
340054359Sroberto	)
340154359Sroberto{
340254359Sroberto	char *name;
340354359Sroberto	char *value;
3404132451Sroberto	char output_raw;
340554359Sroberto	int fmt;
340654359Sroberto	l_fp lfp;
3407285612Sdelphij	sockaddr_u hval;
340854359Sroberto	u_long uval;
3409285612Sdelphij	int narr;
3410285612Sdelphij	size_t len;
341154359Sroberto	l_fp lfparr[8];
3412285612Sdelphij	char b[12];
3413285612Sdelphij	char bn[2 * MAXVARLEN];
3414285612Sdelphij	char bv[2 * MAXVALLEN];
341554359Sroberto
3416285612Sdelphij	UNUSED_ARG(datatype);
341754359Sroberto
3418285612Sdelphij	if (!quiet)
3419285612Sdelphij		fprintf(fp, "status=%04x %s,\n", status,
3420285612Sdelphij			statustoa(datatype, status));
342154359Sroberto
342254359Sroberto	startoutput();
342354359Sroberto	while (nextvar(&length, &data, &name, &value)) {
3424285612Sdelphij		fmt = varfmt(name);
3425285612Sdelphij		output_raw = 0;
3426285612Sdelphij		switch (fmt) {
3427285612Sdelphij
3428285612Sdelphij		case PADDING:
342954359Sroberto			output_raw = '*';
3430285612Sdelphij			break;
343154359Sroberto
3432285612Sdelphij		case TS:
3433330567Sgordon			if (!value || !decodets(value, &lfp))
3434285612Sdelphij				output_raw = '?';
3435285612Sdelphij			else
3436285612Sdelphij				output(fp, name, prettydate(&lfp));
3437285612Sdelphij			break;
343854359Sroberto
3439285612Sdelphij		case HA:	/* fallthru */
3440285612Sdelphij		case NA:
3441330567Sgordon			if (!value || !decodenetnum(value, &hval)) {
3442285612Sdelphij				output_raw = '?';
3443285612Sdelphij			} else if (fmt == HA){
3444285612Sdelphij				output(fp, name, nntohost(&hval));
3445285612Sdelphij			} else {
3446285612Sdelphij				output(fp, name, stoa(&hval));
3447285612Sdelphij			}
3448285612Sdelphij			break;
344954359Sroberto
3450285612Sdelphij		case RF:
3451330567Sgordon			if (!value) {
3452330567Sgordon				output_raw = '?';
3453330567Sgordon			} else if (decodenetnum(value, &hval)) {
3454285612Sdelphij				if (ISREFCLOCKADR(&hval))
3455285612Sdelphij					output(fp, name,
3456285612Sdelphij					       refnumtoa(&hval));
345754359Sroberto				else
3458285612Sdelphij					output(fp, name, stoa(&hval));
3459285612Sdelphij			} else if (strlen(value) <= 4) {
3460285612Sdelphij				output(fp, name, value);
3461285612Sdelphij			} else {
3462285612Sdelphij				output_raw = '?';
3463285612Sdelphij			}
3464285612Sdelphij			break;
346554359Sroberto
3466285612Sdelphij		case LP:
3467330567Sgordon			if (!value || !decodeuint(value, &uval) || uval > 3) {
3468285612Sdelphij				output_raw = '?';
3469285612Sdelphij			} else {
3470285612Sdelphij				b[0] = (0x2 & uval)
3471285612Sdelphij					   ? '1'
3472285612Sdelphij					   : '0';
3473285612Sdelphij				b[1] = (0x1 & uval)
3474285612Sdelphij					   ? '1'
3475285612Sdelphij					   : '0';
3476285612Sdelphij				b[2] = '\0';
3477285612Sdelphij				output(fp, name, b);
347854359Sroberto			}
3479285612Sdelphij			break;
348054359Sroberto
3481285612Sdelphij		case OC:
3482330567Sgordon			if (!value || !decodeuint(value, &uval)) {
3483285612Sdelphij				output_raw = '?';
3484285612Sdelphij			} else {
3485285612Sdelphij				snprintf(b, sizeof(b), "%03lo", uval);
3486285612Sdelphij				output(fp, name, b);
3487285612Sdelphij			}
3488285612Sdelphij			break;
3489285612Sdelphij
3490285612Sdelphij		case AR:
3491330567Sgordon			if (!value || !decodearr(value, &narr, lfparr, 8))
3492285612Sdelphij				output_raw = '?';
3493285612Sdelphij			else
3494285612Sdelphij				outputarr(fp, name, narr, lfparr);
3495285612Sdelphij			break;
3496285612Sdelphij
3497285612Sdelphij		case FX:
3498330567Sgordon			if (!value || !decodeuint(value, &uval))
3499285612Sdelphij				output_raw = '?';
3500285612Sdelphij			else
3501285612Sdelphij				output(fp, name, tstflags(uval));
3502285612Sdelphij			break;
3503285612Sdelphij
3504285612Sdelphij		default:
3505285612Sdelphij			fprintf(stderr, "Internal error in cookedprint, %s=%s, fmt %d\n",
3506285612Sdelphij				name, value, fmt);
3507285612Sdelphij			output_raw = '?';
3508285612Sdelphij			break;
350954359Sroberto		}
3510285612Sdelphij
351154359Sroberto		if (output_raw != 0) {
3512289997Sglebius			/* TALOS-CAN-0063: avoid buffer overrun */
3513285612Sdelphij			atoascii(name, MAXVARLEN, bn, sizeof(bn));
351454359Sroberto			if (output_raw != '*') {
3515289997Sglebius				atoascii(value, MAXVALLEN,
3516289997Sglebius					 bv, sizeof(bv) - 1);
351754359Sroberto				len = strlen(bv);
351854359Sroberto				bv[len] = output_raw;
351954359Sroberto				bv[len+1] = '\0';
3520289997Sglebius			} else {
3521289997Sglebius				atoascii(value, MAXVALLEN,
3522289997Sglebius					 bv, sizeof(bv));
352354359Sroberto			}
352454359Sroberto			output(fp, bn, bv);
352554359Sroberto		}
352654359Sroberto	}
352754359Sroberto	endoutput(fp);
352854359Sroberto}
352954359Sroberto
353054359Sroberto
353154359Sroberto/*
353254359Sroberto * sortassoc - sort associations in the cache into ascending order
353354359Sroberto */
353454359Srobertovoid
353554359Srobertosortassoc(void)
353654359Sroberto{
353754359Sroberto	if (numassoc > 1)
3538285612Sdelphij		qsort(assoc_cache, (size_t)numassoc,
3539285612Sdelphij		      sizeof(assoc_cache[0]), &assoccmp);
354054359Sroberto}
354154359Sroberto
354254359Sroberto
354354359Sroberto/*
354454359Sroberto * assoccmp - compare two associations
354554359Sroberto */
354654359Srobertostatic int
354754359Srobertoassoccmp(
354854359Sroberto	const void *t1,
354954359Sroberto	const void *t2
355054359Sroberto	)
355154359Sroberto{
3552285612Sdelphij	const struct association *ass1 = t1;
3553285612Sdelphij	const struct association *ass2 = t2;
355454359Sroberto
355554359Sroberto	if (ass1->assid < ass2->assid)
3556285612Sdelphij		return -1;
355754359Sroberto	if (ass1->assid > ass2->assid)
3558285612Sdelphij		return 1;
355954359Sroberto	return 0;
356054359Sroberto}
3561285612Sdelphij
3562285612Sdelphij
3563285612Sdelphij/*
3564285612Sdelphij * grow_assoc_cache() - enlarge dynamic assoc_cache array
3565285612Sdelphij *
3566285612Sdelphij * The strategy is to add an assumed 4k page size at a time, leaving
3567285612Sdelphij * room for malloc() bookkeeping overhead equivalent to 4 pointers.
3568285612Sdelphij */
3569285612Sdelphijvoid
3570285612Sdelphijgrow_assoc_cache(void)
3571285612Sdelphij{
3572285612Sdelphij	static size_t	prior_sz;
3573285612Sdelphij	size_t		new_sz;
3574285612Sdelphij
3575285612Sdelphij	new_sz = prior_sz + 4 * 1024;
3576285612Sdelphij	if (0 == prior_sz) {
3577285612Sdelphij		new_sz -= 4 * sizeof(void *);
3578285612Sdelphij	}
3579285612Sdelphij	assoc_cache = erealloc_zero(assoc_cache, new_sz, prior_sz);
3580285612Sdelphij	prior_sz = new_sz;
3581293650Sglebius	assoc_cache_slots = (u_int)(new_sz / sizeof(assoc_cache[0]));
3582285612Sdelphij}
3583285612Sdelphij
3584285612Sdelphij
3585285612Sdelphij/*
3586285612Sdelphij * ntpq_custom_opt_handler - autoopts handler for -c and -p
3587285612Sdelphij *
3588285612Sdelphij * By default, autoopts loses the relative order of -c and -p options
3589285612Sdelphij * on the command line.  This routine replaces the default handler for
3590285612Sdelphij * those routines and builds a list of commands to execute preserving
3591285612Sdelphij * the order.
3592285612Sdelphij */
3593285612Sdelphijvoid
3594285612Sdelphijntpq_custom_opt_handler(
3595285612Sdelphij	tOptions *pOptions,
3596285612Sdelphij	tOptDesc *pOptDesc
359754359Sroberto	)
359854359Sroberto{
3599285612Sdelphij	switch (pOptDesc->optValue) {
3600285612Sdelphij
3601285612Sdelphij	default:
3602285612Sdelphij		fprintf(stderr,
3603285612Sdelphij			"ntpq_custom_opt_handler unexpected option '%c' (%d)\n",
3604285612Sdelphij			pOptDesc->optValue, pOptDesc->optValue);
3605285612Sdelphij		exit(1);
3606285612Sdelphij
3607285612Sdelphij	case 'c':
3608285612Sdelphij		ADDCMD(pOptDesc->pzLastArg);
3609285612Sdelphij		break;
3610285612Sdelphij
3611285612Sdelphij	case 'p':
3612285612Sdelphij		ADDCMD("peers");
3613285612Sdelphij		break;
3614285612Sdelphij	}
361554359Sroberto}
3616285612Sdelphij/*
3617285612Sdelphij * Obtain list of digest names
3618285612Sdelphij */
3619285612Sdelphij
3620330567Sgordon#if defined(OPENSSL) && !defined(HAVE_EVP_MD_DO_ALL_SORTED)
3621330567Sgordon# if defined(_MSC_VER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
3622330567Sgordon#  define HAVE_EVP_MD_DO_ALL_SORTED
3623330567Sgordon# endif
3624330567Sgordon#endif
3625330567Sgordon
3626285612Sdelphij#ifdef OPENSSL
3627285612Sdelphij# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3628330567Sgordon#  define K_PER_LINE	8
3629330567Sgordon#  define K_NL_PFX_STR	"\n    "
3630330567Sgordon#  define K_DELIM_STR	", "
3631330567Sgordon
3632285612Sdelphijstruct hstate {
3633285612Sdelphij   char *list;
3634285612Sdelphij   const char **seen;
3635285612Sdelphij   int idx;
3636285612Sdelphij};
3637330567Sgordon
3638330567Sgordon
3639330567Sgordonstatic void
3640330567Sgordonlist_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg)
3641285612Sdelphij{
3642330567Sgordon    size_t 	  len, n, digest_len;
3643330567Sgordon    const char	  *name, **seen;
3644285612Sdelphij    struct hstate *hstate = arg;
3645330567Sgordon    char	  *cp;
3646285612Sdelphij
3647330567Sgordon    /* m is MD obj, from is name or alias, to is base name for alias */
3648330567Sgordon    if (!m || !from || to) {
3649285612Sdelphij        return; /* Ignore aliases */
3650330567Sgordon    }
3651285612Sdelphij
3652330567Sgordon    /* Discard MACs that NTP won't accept. */
3653330567Sgordon    /* Keep this consistent with keytype_from_text() in ssl_init.c. */
3654330567Sgordon    if (EVP_MD_size(m) > (MAX_MAC_LEN - sizeof(keyid_t))) {
3655330567Sgordon        return;
3656330567Sgordon    }
3657330567Sgordon
3658285612Sdelphij    name = EVP_MD_name(m);
3659285612Sdelphij
3660285612Sdelphij    /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */
3661285612Sdelphij
3662330567Sgordon    for (cp = name; *cp; cp++) {
3663330567Sgordon	if (islower((unsigned char)*cp)) {
3664285612Sdelphij	    return;
3665330567Sgordon	}
3666285612Sdelphij    }
3667330567Sgordon
3668285612Sdelphij    len = (cp - name) + 1;
3669285612Sdelphij
3670285612Sdelphij    /* There are duplicates.  Discard if name has been seen. */
3671285612Sdelphij
3672330567Sgordon    for (seen = hstate->seen; *seen; seen++) {
3673330567Sgordon        if (!strcmp(*seen, name)) {
3674285612Sdelphij	    return;
3675330567Sgordon	}
3676330567Sgordon    }
3677330567Sgordon
3678285612Sdelphij    n = (seen - hstate->seen) + 2;
3679289997Sglebius    hstate->seen = erealloc(hstate->seen, n * sizeof(*seen));
3680285612Sdelphij    hstate->seen[n-2] = name;
3681285612Sdelphij    hstate->seen[n-1] = NULL;
3682285612Sdelphij
3683330567Sgordon    if (hstate->list != NULL) {
3684330567Sgordon	len += strlen(hstate->list);
3685330567Sgordon    }
3686285612Sdelphij
3687330567Sgordon    len += (hstate->idx >= K_PER_LINE)
3688330567Sgordon		? strlen(K_NL_PFX_STR)
3689330567Sgordon		: strlen(K_DELIM_STR);
3690285612Sdelphij
3691285612Sdelphij    if (hstate->list == NULL) {
3692330567Sgordon        hstate->list = (char *)emalloc(len);
3693285612Sdelphij	hstate->list[0] = '\0';
3694330567Sgordon    } else {
3695289997Sglebius	hstate->list = (char *)erealloc(hstate->list, len);
3696330567Sgordon    }
3697285612Sdelphij
3698285612Sdelphij    sprintf(hstate->list + strlen(hstate->list), "%s%s",
3699330567Sgordon	    ((hstate->idx >= K_PER_LINE) ? K_NL_PFX_STR : K_DELIM_STR),
3700285612Sdelphij	    name);
3701330567Sgordon
3702330567Sgordon    if (hstate->idx >= K_PER_LINE) {
3703285612Sdelphij	hstate->idx = 1;
3704330567Sgordon    } else {
3705285612Sdelphij	hstate->idx++;
3706330567Sgordon    }
3707285612Sdelphij}
3708330567Sgordon
3709330567Sgordon
3710330567Sgordon/* Insert CMAC into SSL digests list */
3711330567Sgordonstatic char *
3712330567Sgordoninsert_cmac(char *list)
3713330567Sgordon{
3714330567Sgordon    int insert;
3715330567Sgordon    size_t len;
3716330567Sgordon
3717330567Sgordon
3718330567Sgordon    /* If list empty, we need to insert CMAC on new line */
3719330567Sgordon    insert = (!list || !*list);
3720330567Sgordon
3721330567Sgordon    if (insert) {
3722330567Sgordon	len = strlen(K_NL_PFX_STR) + strlen(CMAC);
3723330567Sgordon	list = (char *)erealloc(list, len + 1);
3724330567Sgordon	sprintf(list, "%s%s", K_NL_PFX_STR, CMAC);
3725330567Sgordon    } else {	/* List not empty */
3726330567Sgordon	/* Check if CMAC already in list - future proofing */
3727330567Sgordon	const char *cmac_sn;
3728330567Sgordon	char *cmac_p;
3729330567Sgordon
3730330567Sgordon	cmac_sn = OBJ_nid2sn(NID_cmac);
3731330567Sgordon	cmac_p = list;
3732330567Sgordon	insert = cmac_sn != NULL && *cmac_sn != '\0';
3733330567Sgordon
3734330567Sgordon	/* CMAC in list if found, followed by nul char or ',' */
3735330567Sgordon	while (insert && NULL != (cmac_p = strstr(cmac_p, cmac_sn))) {
3736330567Sgordon	    cmac_p += strlen(cmac_sn);
3737330567Sgordon	    /* Still need to insert if not nul and not ',' */
3738330567Sgordon	    insert = *cmac_p && ',' != *cmac_p;
3739330567Sgordon	}
3740330567Sgordon
3741330567Sgordon	/* Find proper insertion point */
3742330567Sgordon	if (insert) {
3743330567Sgordon	    char *last_nl;
3744330567Sgordon	    char *point;
3745330567Sgordon	    char *delim;
3746330567Sgordon	    int found;
3747330567Sgordon
3748330567Sgordon	    /* Default to start if list empty */
3749330567Sgordon	    found = 0;
3750330567Sgordon	    delim = list;
3751330567Sgordon	    len = strlen(list);
3752330567Sgordon
3753330567Sgordon	    /* While new lines */
3754330567Sgordon	    while (delim < list + len && *delim &&
3755330567Sgordon			!strncmp(K_NL_PFX_STR, delim, strlen(K_NL_PFX_STR))) {
3756330567Sgordon		point = delim + strlen(K_NL_PFX_STR);
3757330567Sgordon
3758330567Sgordon		/* While digest names on line */
3759330567Sgordon		while (point < list + len && *point) {
3760330567Sgordon		    /* Another digest after on same or next line? */
3761330567Sgordon		    delim = strstr( point, K_DELIM_STR);
3762330567Sgordon		    last_nl = strstr( point, K_NL_PFX_STR);
3763330567Sgordon
3764330567Sgordon		    /* No - end of list */
3765330567Sgordon		    if (!delim && !last_nl) {
3766330567Sgordon			delim = list + len;
3767330567Sgordon		    } else
3768330567Sgordon		    /* New line and no delim or before delim? */
3769330567Sgordon		    if (last_nl && (!delim || last_nl < delim)) {
3770330567Sgordon			delim = last_nl;
3771330567Sgordon		    }
3772330567Sgordon
3773330567Sgordon		    /* Found insertion point where CMAC before entry? */
3774330567Sgordon		    if (strncmp(CMAC, point, delim - point) < 0) {
3775330567Sgordon			found = 1;
3776330567Sgordon			break;
3777330567Sgordon		    }
3778330567Sgordon
3779330567Sgordon		    if (delim < list + len && *delim &&
3780330567Sgordon			    !strncmp(K_DELIM_STR, delim, strlen(K_DELIM_STR))) {
3781330567Sgordon			point += strlen(K_DELIM_STR);
3782330567Sgordon		    } else {
3783330567Sgordon			break;
3784330567Sgordon		    }
3785330567Sgordon		} /* While digest names on line */
3786330567Sgordon	    } /* While new lines */
3787330567Sgordon
3788330567Sgordon	    /* If found in list */
3789330567Sgordon	    if (found) {
3790330567Sgordon		/* insert cmac and delim */
3791330567Sgordon		/* Space for list could move - save offset */
3792330567Sgordon		ptrdiff_t p_offset = point - list;
3793330567Sgordon		len += strlen(CMAC) + strlen(K_DELIM_STR);
3794330567Sgordon		list = (char *)erealloc(list, len + 1);
3795330567Sgordon		point = list + p_offset;
3796330567Sgordon		/* move to handle src/dest overlap */
3797330567Sgordon		memmove(point + strlen(CMAC) + strlen(K_DELIM_STR),
3798330567Sgordon					point, strlen(point) + 1);
3799330567Sgordon		strncpy(point, CMAC, strlen(CMAC));
3800330567Sgordon		strncpy(point + strlen(CMAC), K_DELIM_STR, strlen(K_DELIM_STR));
3801330567Sgordon	    } else {	/* End of list */
3802330567Sgordon		/* append delim and cmac */
3803330567Sgordon		len += strlen(K_DELIM_STR) + strlen(CMAC);
3804330567Sgordon		list = (char *)erealloc(list, len + 1);
3805330567Sgordon		strcpy(list + strlen(list), K_DELIM_STR);
3806330567Sgordon		strcpy(list + strlen(list), CMAC);
3807330567Sgordon	    }
3808330567Sgordon	} /* insert */
3809330567Sgordon    } /* List not empty */
3810330567Sgordon
3811330567Sgordon    return list;
3812330567Sgordon}
3813285612Sdelphij# endif
3814285612Sdelphij#endif
3815285612Sdelphij
3816330567Sgordon
3817330567Sgordonstatic char *
3818330567Sgordonlist_digest_names(void)
3819285612Sdelphij{
3820285612Sdelphij    char *list = NULL;
3821285612Sdelphij
3822285612Sdelphij#ifdef OPENSSL
3823285612Sdelphij# ifdef HAVE_EVP_MD_DO_ALL_SORTED
3824285612Sdelphij    struct hstate hstate = { NULL, NULL, K_PER_LINE+1 };
3825285612Sdelphij
3826330567Sgordon    /* replace calloc(1, sizeof(const char *)) */
3827330567Sgordon    hstate.seen = (const char **)emalloc_zero(sizeof(const char *));
3828285612Sdelphij
3829285612Sdelphij    INIT_SSL();
3830285612Sdelphij    EVP_MD_do_all_sorted(list_md_fn, &hstate);
3831285612Sdelphij    list = hstate.list;
3832285612Sdelphij    free(hstate.seen);
3833330567Sgordon
3834330567Sgordon    list = insert_cmac(list);	/* Insert CMAC into SSL digests list */
3835330567Sgordon
3836285612Sdelphij# else
3837289997Sglebius    list = (char *)emalloc(sizeof("md5, others (upgrade to OpenSSL-1.0 for full list)"));
3838285612Sdelphij    strcpy(list, "md5, others (upgrade to OpenSSL-1.0 for full list)");
3839285612Sdelphij# endif
3840285612Sdelphij#else
3841289997Sglebius    list = (char *)emalloc(sizeof("md5"));
3842285612Sdelphij    strcpy(list, "md5");
3843285612Sdelphij#endif
3844285612Sdelphij
3845285612Sdelphij    return list;
3846285612Sdelphij}
3847293650Sglebius
3848293650Sglebius#define CTRLC_STACK_MAX 4
3849293650Sglebiusstatic volatile size_t		ctrlc_stack_len = 0;
3850293650Sglebiusstatic volatile Ctrl_C_Handler	ctrlc_stack[CTRLC_STACK_MAX];
3851293650Sglebius
3852293650Sglebius
3853293650Sglebius
3854293650Sglebiusint/*BOOL*/
3855293650Sglebiuspush_ctrl_c_handler(
3856293650Sglebius	Ctrl_C_Handler func
3857293650Sglebius	)
3858293650Sglebius{
3859293650Sglebius	size_t size = ctrlc_stack_len;
3860293650Sglebius	if (func && (size < CTRLC_STACK_MAX)) {
3861293650Sglebius		ctrlc_stack[size] = func;
3862293650Sglebius		ctrlc_stack_len = size + 1;
3863293650Sglebius		return TRUE;
3864293650Sglebius	}
3865293650Sglebius	return FALSE;
3866293650Sglebius}
3867293650Sglebius
3868293650Sglebiusint/*BOOL*/
3869293650Sglebiuspop_ctrl_c_handler(
3870293650Sglebius	Ctrl_C_Handler func
3871293650Sglebius	)
3872293650Sglebius{
3873293650Sglebius	size_t size = ctrlc_stack_len;
3874293650Sglebius	if (size) {
3875293650Sglebius		--size;
3876293650Sglebius		if (func == NULL || func == ctrlc_stack[size]) {
3877293650Sglebius			ctrlc_stack_len = size;
3878293650Sglebius			return TRUE;
3879293650Sglebius		}
3880293650Sglebius	}
3881293650Sglebius	return FALSE;
3882293650Sglebius}
3883293650Sglebius
3884293650Sglebiusstatic void
3885293650Sglebiuson_ctrlc(void)
3886293650Sglebius{
3887293650Sglebius	size_t size = ctrlc_stack_len;
3888293650Sglebius	while (size)
3889293650Sglebius		if ((*ctrlc_stack[--size])())
3890293650Sglebius			break;
3891293650Sglebius}
3892294569Sdelphij
3893294569Sdelphijstatic int
3894294569Sdelphijmy_easprintf(
3895294569Sdelphij	char ** 	ppinto,
3896294569Sdelphij	const char *	fmt   ,
3897294569Sdelphij	...
3898294569Sdelphij	)
3899294569Sdelphij{
3900294569Sdelphij	va_list	va;
3901294569Sdelphij	int	prc;
3902294569Sdelphij	size_t	len = 128;
3903294569Sdelphij	char *	buf = emalloc(len);
3904294569Sdelphij
3905294569Sdelphij  again:
3906294569Sdelphij	/* Note: we expect the memory allocation to fail long before the
3907294569Sdelphij	 * increment in buffer size actually overflows.
3908294569Sdelphij	 */
3909294569Sdelphij	buf = (buf) ? erealloc(buf, len) : emalloc(len);
3910294569Sdelphij
3911294569Sdelphij	va_start(va, fmt);
3912294569Sdelphij	prc = vsnprintf(buf, len, fmt, va);
3913294569Sdelphij	va_end(va);
3914294569Sdelphij
3915294569Sdelphij	if (prc < 0) {
3916294569Sdelphij		/* might be very old vsnprintf. Or actually MSVC... */
3917294569Sdelphij		len += len >> 1;
3918294569Sdelphij		goto again;
3919294569Sdelphij	}
3920294569Sdelphij	if ((size_t)prc >= len) {
3921294569Sdelphij		/* at least we have the proper size now... */
3922294569Sdelphij		len = (size_t)prc + 1;
3923294569Sdelphij		goto again;
3924294569Sdelphij	}
3925294569Sdelphij	if ((size_t)prc < (len - 32))
3926294569Sdelphij		buf = erealloc(buf, (size_t)prc + 1);
3927294569Sdelphij	*ppinto = buf;
3928294569Sdelphij	return prc;
3929294569Sdelphij}
3930